N4X2X2YYQCKXNP6DZONO3LBVKHTXETWQDC3OWMNM5DEMJ3KPDBVQC @LLVM_UNITTEST_SOURCE_DIR@
//===-- ContextCompressionTest.cpp -------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "../tools/llvm-profgen/ProfileGenerator.h"#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"#include "gtest/gtest.h"using namespace llvm;using namespace sampleprof;TEST(TestCompression, TestNoSizeLimit1) {SmallVector<std::string, 16> Context = {"a", "b", "c", "a", "b", "c"};SmallVector<std::string, 16> Expect = {"a", "b", "c"};CSProfileGenerator::compressRecursionContext(Context, -1);EXPECT_TRUE(std::equal(Context.begin(), Context.end(), Expect.begin()));}TEST(TestCompression, TestNoSizeLimit2) {SmallVector<std::string, 16> Context = {"m", "a", "a", "b", "c", "a","b", "c", "b", "c", "d"};SmallVector<std::string, 16> Expect = {"m", "a", "b", "c", "d"};CSProfileGenerator::compressRecursionContext(Context, -1);EXPECT_TRUE(std::equal(Context.begin(), Context.end(), Expect.begin()));}TEST(TestCompression, TestMaxDedupSize) {SmallVector<std::string, 16> Context = {"m", "a", "a", "b", "c", "a","b", "c", "b", "c", "d"};SmallVector<std::string, 16> Expect = {"m", "a", "b", "c","a", "b", "c", "d"};CSProfileGenerator::compressRecursionContext(Context, 2);EXPECT_TRUE(std::equal(Context.begin(), Context.end(), Expect.begin()));}
set(LLVM_LINK_COMPONENTSSupport)add_llvm_unittest(LLVMProfgenTestsContextCompressionTest.cpp)target_link_libraries(LLVMProfgenTests PRIVATE LLVMTestingSupport)add_dependencies(LLVMProfgenTests intrinsics_gen)set_property(TARGET LLVMProfgenTests PROPERTY FOLDER "Tests/UnitTests/ToolTests")
//===---- X86TestBase.h -----------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//// Test fixture common to all X86 MCA tests.//===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H#define LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H#include "MCATestBase.h"#include "llvm/ADT/SmallVector.h"namespace llvm {namespace mca {class X86TestBase : public MCATestBase {protected:X86TestBase();void getSimpleInsts(SmallVectorImpl<MCInst> &Insts, unsigned Repeats = 1);};} // end namespace mca} // end namespace llvm#endif
#include "X86TestBase.h"#include "MCTargetDesc/X86MCTargetDesc.h"#include "llvm/MC/MCInstBuilder.h"#include "llvm/Support/TargetSelect.h"using namespace llvm;using namespace mca;X86TestBase::X86TestBase() : MCATestBase("x86_64-unknown-linux", "skylake") {LLVMInitializeX86TargetInfo();LLVMInitializeX86TargetMC();LLVMInitializeX86Target();LLVMInitializeX86AsmPrinter();}void X86TestBase::getSimpleInsts(SmallVectorImpl<MCInst> &Insts,unsigned Repeats) {for (unsigned i = 0U; i < Repeats; ++i) {// vmulps %xmm0, %xmm1, %xmm2Insts.push_back(MCInstBuilder(X86::VMULPSrr).addReg(X86::XMM2).addReg(X86::XMM1).addReg(X86::XMM0));// vhaddps %xmm2, %xmm2, %xmm3Insts.push_back(MCInstBuilder(X86::VHADDPSrr).addReg(X86::XMM3).addReg(X86::XMM2).addReg(X86::XMM2));// vhaddps %xmm3, %xmm3, %xmm4Insts.push_back(MCInstBuilder(X86::VHADDPSrr).addReg(X86::XMM4).addReg(X86::XMM3).addReg(X86::XMM3));}}
#include "Views/SummaryView.h"#include "X86TestBase.h"#include "llvm/ADT/SmallPtrSet.h"#include "llvm/MCA/CustomBehaviour.h"#include "llvm/MCA/IncrementalSourceMgr.h"#include "llvm/MCA/InstrBuilder.h"#include "llvm/MCA/Pipeline.h"#include "llvm/Support/Format.h"#include "llvm/Support/JSON.h"#include "llvm/Support/raw_ostream.h"#include <unordered_map>using namespace llvm;using namespace mca;TEST_F(X86TestBase, TestResumablePipeline) {mca::Context MCA(*MRI, *STI);mca::IncrementalSourceMgr ISM;// Empty CustomBehaviour.auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);auto PO = getDefaultPipelineOptions();auto P = MCA.createDefaultPipeline(PO, ISM, *CB);ASSERT_TRUE(P);SmallVector<MCInst> MCIs;getSimpleInsts(MCIs, /*Repeats=*/100);// Add views.auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,PO.DispatchWidth);P->addEventListener(SV.get());mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());// Tile size = 7for (unsigned i = 0U, E = MCIs.size(); i < E;) {for (unsigned TE = i + 7; i < TE && i < E; ++i) {Expected<std::unique_ptr<mca::Instruction>> InstOrErr =IB.createInstruction(MCIs[i]);ASSERT_TRUE(bool(InstOrErr));ISM.addInst(std::move(InstOrErr.get()));}// Run the pipeline.Expected<unsigned> Cycles = P->run();if (!Cycles) {// Should be a stream pause error.ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());llvm::consumeError(Cycles.takeError());}}ISM.endOfStream();// Has to terminate properly.Expected<unsigned> Cycles = P->run();ASSERT_TRUE(bool(Cycles));json::Value Result = SV->toJSON();auto *ResultObj = Result.getAsObject();ASSERT_TRUE(ResultObj);// Run the baseline.json::Object BaselineResult;auto E = runBaselineMCA(BaselineResult, MCIs);ASSERT_FALSE(bool(E)) << "Failed to run baseline";auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";// Compare the results.constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps","BlockRThroughput"};for (const auto *F : Fields) {auto V = ResultObj->getInteger(F);auto BV = BaselineObj->getInteger(F);ASSERT_TRUE(V && BV);ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";}}TEST_F(X86TestBase, TestInstructionRecycling) {mca::Context MCA(*MRI, *STI);std::unordered_map<const mca::InstrDesc *, SmallPtrSet<mca::Instruction *, 2>>RecycledInsts;auto GetRecycledInst = [&](const mca::InstrDesc &Desc) -> mca::Instruction * {auto It = RecycledInsts.find(&Desc);if (It != RecycledInsts.end()) {auto &Insts = It->second;if (Insts.size()) {mca::Instruction *I = *Insts.begin();Insts.erase(I);return I;}}return nullptr;};auto AddRecycledInst = [&](mca::Instruction *I) {const mca::InstrDesc &D = I->getDesc();RecycledInsts[&D].insert(I);};mca::IncrementalSourceMgr ISM;ISM.setOnInstFreedCallback(AddRecycledInst);// Empty CustomBehaviour.auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);auto PO = getDefaultPipelineOptions();auto P = MCA.createDefaultPipeline(PO, ISM, *CB);ASSERT_TRUE(P);SmallVector<MCInst> MCIs;getSimpleInsts(MCIs, /*Repeats=*/100);// Add views.auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,PO.DispatchWidth);P->addEventListener(SV.get());mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());IB.setInstRecycleCallback(GetRecycledInst);// Tile size = 7for (unsigned i = 0U, E = MCIs.size(); i < E;) {for (unsigned TE = i + 7; i < TE && i < E; ++i) {Expected<std::unique_ptr<mca::Instruction>> InstOrErr =IB.createInstruction(MCIs[i]);if (!InstOrErr) {mca::Instruction *RecycledInst = nullptr;// Check if the returned instruction is a recycled// one.auto RemainingE = handleErrors(InstOrErr.takeError(),[&](const mca::RecycledInstErr &RC) {RecycledInst = RC.getInst();});ASSERT_FALSE(bool(RemainingE));ASSERT_TRUE(RecycledInst);ISM.addRecycledInst(RecycledInst);} else {ISM.addInst(std::move(InstOrErr.get()));}}// Run the pipeline.Expected<unsigned> Cycles = P->run();if (!Cycles) {// Should be a stream pause error.ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());llvm::consumeError(Cycles.takeError());}}ISM.endOfStream();// Has to terminate properly.Expected<unsigned> Cycles = P->run();ASSERT_TRUE(bool(Cycles));json::Value Result = SV->toJSON();auto *ResultObj = Result.getAsObject();ASSERT_TRUE(ResultObj);// Run the baseline.json::Object BaselineResult;auto E = runBaselineMCA(BaselineResult, MCIs);ASSERT_FALSE(bool(E)) << "Failed to run baseline";auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";// Compare the results.constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps","BlockRThroughput"};for (const auto *F : Fields) {auto V = ResultObj->getInteger(F);auto BV = BaselineObj->getInteger(F);ASSERT_TRUE(V && BV);ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";}}
add_llvm_mca_unittest_includes(${LLVM_MAIN_SRC_DIR}/lib/Target/X86${LLVM_BINARY_DIR}/lib/Target/X86)add_llvm_mca_unittest_sources(TestIncrementalMCA.cppX86TestBase.cpp)add_llvm_mca_unittest_link_components(X86)
//===---- MCATestBase.h -----------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//// Test fixture common to all MCA tests.//===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H#define LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H#include "llvm/ADT/StringRef.h"#include "llvm/ADT/Triple.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCInst.h"#include "llvm/MC/MCInstPrinter.h"#include "llvm/MC/MCInstrAnalysis.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCObjectFileInfo.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/SubtargetFeature.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/MCA/Context.h"#include "gtest/gtest.h"namespace llvm {namespace json {class Object;} // end namespace jsonnamespace mca {class View;class MCATestBase : public ::testing::Test {protected:// Note: Subclass ctors are expected to perform target-specific// initializations.MCATestBase(StringRef TripleStr, StringRef CPUName, StringRef MAttr = ""): TheTriple(TripleStr), CPUName(CPUName), MAttr(MAttr) {}/// Factory function to create a Target.virtual const Target *getLLVMTarget() const;/// Factory function to create a MCTargetOptions instance. Returns an/// empty one by default.virtual MCTargetOptions getMCTargetOptions() { return MCTargetOptions(); }const Target *TheTarget;const Triple TheTriple;StringRef CPUName;StringRef MAttr;// MC components.std::unique_ptr<MCSubtargetInfo> STI;std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<MCObjectFileInfo> MOFI;std::unique_ptr<MCContext> Ctx;std::unique_ptr<MCInstrInfo> MCII;std::unique_ptr<MCInstrAnalysis> MCIA;std::unique_ptr<MCInstPrinter> IP;static mca::PipelineOptions getDefaultPipelineOptions();void SetUp() override;/// Utility function to run MCA with (nearly) the same configuration as the/// `llvm-mca` tool to verify result correctness./// This function only displays on SummaryView by default.virtual Error runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,ArrayRef<mca::View *> Views = None,const mca::PipelineOptions *PO = nullptr);};} // end namespace mca} // end namespace llvm#endif
#include "MCATestBase.h"#include "Views/SummaryView.h"#include "llvm/MCA/CustomBehaviour.h"#include "llvm/MCA/InstrBuilder.h"#include "llvm/MCA/Pipeline.h"#include "llvm/MCA/SourceMgr.h"#include "llvm/MCA/View.h"#include "llvm/Support/JSON.h"#include "llvm/Support/WithColor.h"#include <string>using namespace llvm;using namespace mca;const Target *MCATestBase::getLLVMTarget() const {std::string Error;return TargetRegistry::lookupTarget(TheTriple.getTriple(), Error);}mca::PipelineOptions MCATestBase::getDefaultPipelineOptions() {mca::PipelineOptions PO(/*MicroOpQueue=*/0, /*DecoderThroughput=*/0,/*DispatchWidth=*/0,/*RegisterFileSize=*/0,/*LoadQueueSize=*/0, /*StoreQueueSize=*/0,/*AssumeNoAlias=*/true,/*EnableBottleneckAnalysis=*/false);return PO;}void MCATestBase::SetUp() {TheTarget = getLLVMTarget();ASSERT_NE(TheTarget, nullptr);StringRef TripleName = TheTriple.getTriple();STI.reset(TheTarget->createMCSubtargetInfo(TripleName, CPUName, MAttr));ASSERT_TRUE(STI);ASSERT_TRUE(STI->isCPUStringValid(CPUName));MRI.reset(TheTarget->createMCRegInfo(TripleName));ASSERT_TRUE(MRI);auto MCOptions = getMCTargetOptions();MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));ASSERT_TRUE(MAI);Ctx = std::make_unique<MCContext>(TheTriple, MAI.get(), MRI.get(), STI.get());MOFI.reset(TheTarget->createMCObjectFileInfo(*Ctx, /*PIC=*/false));Ctx->setObjectFileInfo(MOFI.get());MCII.reset(TheTarget->createMCInstrInfo());ASSERT_TRUE(MCII);MCIA.reset(TheTarget->createMCInstrAnalysis(MCII.get()));ASSERT_TRUE(MCIA);IP.reset(TheTarget->createMCInstPrinter(TheTriple, /*AssemblerDialect=*/0,*MAI, *MCII, *MRI));ASSERT_TRUE(IP);}Error MCATestBase::runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,ArrayRef<mca::View *> Views,const mca::PipelineOptions *PO) {mca::Context MCA(*MRI, *STI);mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());SmallVector<std::unique_ptr<mca::Instruction>> LoweredInsts;for (const auto &MCI : Insts) {Expected<std::unique_ptr<mca::Instruction>> Inst =IB.createInstruction(MCI);if (!Inst) {if (auto NewE =handleErrors(Inst.takeError(),[this](const mca::InstructionError<MCInst> &IE) {std::string InstructionStr;raw_string_ostream SS(InstructionStr);WithColor::error() << IE.Message << '\n';IP->printInst(&IE.Inst, 0, "", *STI, SS);WithColor::note()<< "instruction: " << InstructionStr << '\n';})) {// Default case.return NewE;}} else {LoweredInsts.emplace_back(std::move(Inst.get()));}}mca::CircularSourceMgr SM(LoweredInsts, /*Iterations=*/1);// Empty CustomBehaviour.auto CB = std::make_unique<mca::CustomBehaviour>(*STI, SM, *MCII);mca::PipelineOptions ThePO = PO ? *PO : getDefaultPipelineOptions();auto P = MCA.createDefaultPipeline(ThePO, SM, *CB);SmallVector<std::unique_ptr<mca::View>, 1> DefaultViews;if (Views.empty()) {// By default, we only add SummaryView.auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), Insts,ThePO.DispatchWidth);P->addEventListener(SV.get());DefaultViews.emplace_back(std::move(SV));} else {for (auto *V : Views)P->addEventListener(V);}// Run the pipeline.Expected<unsigned> Cycles = P->run();if (!Cycles)return Cycles.takeError();for (const auto *V : Views)Result[V->getNameAsString()] = V->toJSON();for (const auto &V : DefaultViews)Result[V->getNameAsString()] = V->toJSON();return Error::success();}
set(LLVM_LINK_COMPONENTSMCMCAObjectSupport)set(mca_root ${LLVM_MAIN_SRC_DIR}/tools/llvm-mca)set(mca_includes${CMAKE_CURRENT_SOURCE_DIR}${mca_root})# Right now we only need SummaryView.set(mca_views_sourcesSummaryView.cpp)list(TRANSFORM mca_views_sources PREPEND "${mca_root}/Views/")set(mca_sourcesMCATestBase.cpp${mca_views_sources})function(add_llvm_mca_unittest_includes)set(mca_includes ${mca_includes} ${ARGV} PARENT_SCOPE)endfunction()function(add_llvm_mca_unittest_sources)set(sources ${ARGV})list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_LIST_DIR}/")set(mca_sources ${mca_sources} ${sources} PARENT_SCOPE)endfunction()function(add_llvm_mca_unittest_link_components comps)set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} ${ARGV} PARENT_SCOPE)endfunction()if(LLVM_TARGETS_TO_BUILD MATCHES "X86")include(X86/CMakeLists.txt)endif()list(REMOVE_DUPLICATES LLVM_LINK_COMPONENTS)include_directories(${mca_includes})add_llvm_target_unittest(LLVMMCATests${mca_sources})set_property(TARGET LLVMMCATests PROPERTY FOLDER "Tests/UnitTests/ToolTests")
//===-- TestBase.h ----------------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//// Test fixture common to all X86 tests.//===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_X86_TESTBASE_H#define LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_X86_TESTBASE_H#include "LlvmState.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {void InitializeX86ExegesisTarget();class X86TestBase : public ::testing::Test {protected:X86TestBase() : State("x86_64-unknown-linux", "haswell") {}static void SetUpTestCase() {LLVMInitializeX86TargetInfo();LLVMInitializeX86TargetMC();LLVMInitializeX86Target();LLVMInitializeX86AsmPrinter();LLVMInitializeX86AsmParser();InitializeX86ExegesisTarget();}const LLVMState State;};} // namespace exegesis} // namespace llvm#endif
//===-- TargetTest.cpp -----------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "Target.h"#include <cassert>#include <memory>#include "MCTargetDesc/X86MCTargetDesc.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include "llvm/MC/MCInstPrinter.h"namespace llvm {bool operator==(const MCOperand &a, const MCOperand &b) {if (a.isImm() && b.isImm())return a.getImm() == b.getImm();if (a.isReg() && b.isReg())return a.getReg() == b.getReg();return false;}bool operator==(const MCInst &a, const MCInst &b) {if (a.getOpcode() != b.getOpcode())return false;if (a.getNumOperands() != b.getNumOperands())return false;for (unsigned I = 0; I < a.getNumOperands(); ++I) {if (!(a.getOperand(I) == b.getOperand(I)))return false;}return true;}} // namespace llvmnamespace llvm {namespace exegesis {void InitializeX86ExegesisTarget();namespace {using testing::AllOf;using testing::ElementsAre;using testing::ElementsAreArray;using testing::Eq;using testing::Gt;using testing::Matcher;using testing::NotNull;using testing::Property;using testing::SizeIs;Matcher<MCOperand> IsImm(int64_t Value) {return AllOf(Property(&MCOperand::isImm, Eq(true)),Property(&MCOperand::getImm, Eq(Value)));}Matcher<MCOperand> IsReg(unsigned Reg) {return AllOf(Property(&MCOperand::isReg, Eq(true)),Property(&MCOperand::getReg, Eq(Reg)));}Matcher<MCInst> OpcodeIs(unsigned Opcode) {return Property(&MCInst::getOpcode, Eq(Opcode));}Matcher<MCInst> IsMovImmediate(unsigned Opcode, int64_t Reg, int64_t Value) {return AllOf(OpcodeIs(Opcode), ElementsAre(IsReg(Reg), IsImm(Value)));}Matcher<MCInst> IsMovValueToStack(unsigned Opcode, int64_t Value,size_t Offset) {return AllOf(OpcodeIs(Opcode),ElementsAre(IsReg(X86::RSP), IsImm(1), IsReg(0), IsImm(Offset),IsReg(0), IsImm(Value)));}Matcher<MCInst> IsMovValueFromStack(unsigned Opcode, unsigned Reg) {return AllOf(OpcodeIs(Opcode),ElementsAre(IsReg(Reg), IsReg(X86::RSP), IsImm(1), IsReg(0),IsImm(0), IsReg(0)));}Matcher<MCInst> IsStackAllocate(unsigned Size) {return AllOf(OpcodeIs(X86::SUB64ri8),ElementsAre(IsReg(X86::RSP), IsReg(X86::RSP), IsImm(Size)));}Matcher<MCInst> IsStackDeallocate(unsigned Size) {return AllOf(OpcodeIs(X86::ADD64ri8),ElementsAre(IsReg(X86::RSP), IsReg(X86::RSP), IsImm(Size)));}constexpr const char kTriple[] = "x86_64-unknown-linux";class X86TargetTest : public ::testing::Test {protected:X86TargetTest(const char *Features) : State(kTriple, "core2", Features) {}static void SetUpTestCase() {LLVMInitializeX86TargetInfo();LLVMInitializeX86Target();LLVMInitializeX86TargetMC();InitializeX86ExegesisTarget();}std::vector<MCInst> setRegTo(unsigned Reg, const APInt &Value) {return State.getExegesisTarget().setRegTo(State.getSubtargetInfo(), Reg,Value);}const Instruction &getInstr(unsigned OpCode) {return State.getIC().getInstr(OpCode);}LLVMState State;};class X86Core2TargetTest : public X86TargetTest {public:X86Core2TargetTest() : X86TargetTest("") {}};class X86Core2AvxTargetTest : public X86TargetTest {public:X86Core2AvxTargetTest() : X86TargetTest("+avx") {}};class X86Core2Avx512TargetTest : public X86TargetTest {public:X86Core2Avx512TargetTest() : X86TargetTest("+avx512vl") {}};TEST_F(X86Core2TargetTest, NoHighByteRegs) {EXPECT_TRUE(State.getRATC().reservedRegisters().test(X86::AH));}TEST_F(X86Core2TargetTest, SetFlags) {const unsigned Reg = X86::EFLAGS;EXPECT_THAT(setRegTo(Reg, APInt(64, 0x1111222233334444ULL)),ElementsAre(IsStackAllocate(8),IsMovValueToStack(X86::MOV32mi, 0x33334444UL, 0),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 4),OpcodeIs(X86::POPF64)));}TEST_F(X86Core2TargetTest, SetRegToGR8Value) {const uint8_t Value = 0xFFU;const unsigned Reg = X86::AL;EXPECT_THAT(setRegTo(Reg, APInt(8, Value)),ElementsAre(IsMovImmediate(X86::MOV8ri, Reg, Value)));}TEST_F(X86Core2TargetTest, SetRegToGR16Value) {const uint16_t Value = 0xFFFFU;const unsigned Reg = X86::BX;EXPECT_THAT(setRegTo(Reg, APInt(16, Value)),ElementsAre(IsMovImmediate(X86::MOV16ri, Reg, Value)));}TEST_F(X86Core2TargetTest, SetRegToGR32Value) {const uint32_t Value = 0x7FFFFU;const unsigned Reg = X86::ECX;EXPECT_THAT(setRegTo(Reg, APInt(32, Value)),ElementsAre(IsMovImmediate(X86::MOV32ri, Reg, Value)));}TEST_F(X86Core2TargetTest, SetRegToGR64Value) {const uint64_t Value = 0x7FFFFFFFFFFFFFFFULL;const unsigned Reg = X86::RDX;EXPECT_THAT(setRegTo(Reg, APInt(64, Value)),ElementsAre(IsMovImmediate(X86::MOV64ri, Reg, Value)));}TEST_F(X86Core2TargetTest, SetRegToVR64Value) {EXPECT_THAT(setRegTo(X86::MM0, APInt(64, 0x1111222233334444ULL)),ElementsAre(IsStackAllocate(8),IsMovValueToStack(X86::MOV32mi, 0x33334444UL, 0),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 4),IsMovValueFromStack(X86::MMX_MOVQ64rm, X86::MM0),IsStackDeallocate(8)));}TEST_F(X86Core2TargetTest, SetRegToVR128Value_Use_MOVDQUrm) {EXPECT_THAT(setRegTo(X86::XMM0, APInt(128, "11112222333344445555666677778888", 16)),ElementsAre(IsStackAllocate(16),IsMovValueToStack(X86::MOV32mi, 0x77778888UL, 0),IsMovValueToStack(X86::MOV32mi, 0x55556666UL, 4),IsMovValueToStack(X86::MOV32mi, 0x33334444UL, 8),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 12),IsMovValueFromStack(X86::MOVDQUrm, X86::XMM0),IsStackDeallocate(16)));}TEST_F(X86Core2AvxTargetTest, SetRegToVR128Value_Use_VMOVDQUrm) {EXPECT_THAT(setRegTo(X86::XMM0, APInt(128, "11112222333344445555666677778888", 16)),ElementsAre(IsStackAllocate(16),IsMovValueToStack(X86::MOV32mi, 0x77778888UL, 0),IsMovValueToStack(X86::MOV32mi, 0x55556666UL, 4),IsMovValueToStack(X86::MOV32mi, 0x33334444UL, 8),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 12),IsMovValueFromStack(X86::VMOVDQUrm, X86::XMM0),IsStackDeallocate(16)));}TEST_F(X86Core2Avx512TargetTest, SetRegToVR128Value_Use_VMOVDQU32Z128rm) {EXPECT_THAT(setRegTo(X86::XMM0, APInt(128, "11112222333344445555666677778888", 16)),ElementsAre(IsStackAllocate(16),IsMovValueToStack(X86::MOV32mi, 0x77778888UL, 0),IsMovValueToStack(X86::MOV32mi, 0x55556666UL, 4),IsMovValueToStack(X86::MOV32mi, 0x33334444UL, 8),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 12),IsMovValueFromStack(X86::VMOVDQU32Z128rm, X86::XMM0),IsStackDeallocate(16)));}TEST_F(X86Core2AvxTargetTest, SetRegToVR256Value_Use_VMOVDQUYrm) {const char ValueStr[] ="1111111122222222333333334444444455555555666666667777777788888888";EXPECT_THAT(setRegTo(X86::YMM0, APInt(256, ValueStr, 16)),ElementsAreArray({IsStackAllocate(32),IsMovValueToStack(X86::MOV32mi, 0x88888888UL, 0),IsMovValueToStack(X86::MOV32mi, 0x77777777UL, 4),IsMovValueToStack(X86::MOV32mi, 0x66666666UL, 8),IsMovValueToStack(X86::MOV32mi, 0x55555555UL, 12),IsMovValueToStack(X86::MOV32mi, 0x44444444UL, 16),IsMovValueToStack(X86::MOV32mi, 0x33333333UL, 20),IsMovValueToStack(X86::MOV32mi, 0x22222222UL, 24),IsMovValueToStack(X86::MOV32mi, 0x11111111UL, 28),IsMovValueFromStack(X86::VMOVDQUYrm, X86::YMM0),IsStackDeallocate(32)}));}TEST_F(X86Core2Avx512TargetTest, SetRegToVR256Value_Use_VMOVDQU32Z256rm) {const char ValueStr[] ="1111111122222222333333334444444455555555666666667777777788888888";EXPECT_THAT(setRegTo(X86::YMM0, APInt(256, ValueStr, 16)),ElementsAreArray({IsStackAllocate(32),IsMovValueToStack(X86::MOV32mi, 0x88888888UL, 0),IsMovValueToStack(X86::MOV32mi, 0x77777777UL, 4),IsMovValueToStack(X86::MOV32mi, 0x66666666UL, 8),IsMovValueToStack(X86::MOV32mi, 0x55555555UL, 12),IsMovValueToStack(X86::MOV32mi, 0x44444444UL, 16),IsMovValueToStack(X86::MOV32mi, 0x33333333UL, 20),IsMovValueToStack(X86::MOV32mi, 0x22222222UL, 24),IsMovValueToStack(X86::MOV32mi, 0x11111111UL, 28),IsMovValueFromStack(X86::VMOVDQU32Z256rm, X86::YMM0),IsStackDeallocate(32)}));}TEST_F(X86Core2Avx512TargetTest, SetRegToVR512Value) {const char ValueStr[] ="1111111122222222333333334444444455555555666666667777777788888888""99999999AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFF00000000";EXPECT_THAT(setRegTo(X86::ZMM0, APInt(512, ValueStr, 16)),ElementsAreArray({IsStackAllocate(64),IsMovValueToStack(X86::MOV32mi, 0x00000000UL, 0),IsMovValueToStack(X86::MOV32mi, 0xFFFFFFFFUL, 4),IsMovValueToStack(X86::MOV32mi, 0xEEEEEEEEUL, 8),IsMovValueToStack(X86::MOV32mi, 0xDDDDDDDDUL, 12),IsMovValueToStack(X86::MOV32mi, 0xCCCCCCCCUL, 16),IsMovValueToStack(X86::MOV32mi, 0xBBBBBBBBUL, 20),IsMovValueToStack(X86::MOV32mi, 0xAAAAAAAAUL, 24),IsMovValueToStack(X86::MOV32mi, 0x99999999UL, 28),IsMovValueToStack(X86::MOV32mi, 0x88888888UL, 32),IsMovValueToStack(X86::MOV32mi, 0x77777777UL, 36),IsMovValueToStack(X86::MOV32mi, 0x66666666UL, 40),IsMovValueToStack(X86::MOV32mi, 0x55555555UL, 44),IsMovValueToStack(X86::MOV32mi, 0x44444444UL, 48),IsMovValueToStack(X86::MOV32mi, 0x33333333UL, 52),IsMovValueToStack(X86::MOV32mi, 0x22222222UL, 56),IsMovValueToStack(X86::MOV32mi, 0x11111111UL, 60),IsMovValueFromStack(X86::VMOVDQU32Zrm, X86::ZMM0),IsStackDeallocate(64)}));}// Note: We always put 80 bits on the stack independently of the size of the// value. This uses a bit more space but makes the code simpler.TEST_F(X86Core2TargetTest, SetRegToST0_32Bits) {EXPECT_THAT(setRegTo(X86::ST0, APInt(32, 0x11112222ULL)),ElementsAre(IsStackAllocate(10),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 0),IsMovValueToStack(X86::MOV32mi, 0x00000000UL, 4),IsMovValueToStack(X86::MOV16mi, 0x0000UL, 8),OpcodeIs(X86::LD_F80m), IsStackDeallocate(10)));}TEST_F(X86Core2TargetTest, SetRegToST1_32Bits) {const MCInst CopySt0ToSt1 = MCInstBuilder(X86::ST_Frr).addReg(X86::ST1);EXPECT_THAT(setRegTo(X86::ST1, APInt(32, 0x11112222ULL)),ElementsAre(IsStackAllocate(10),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 0),IsMovValueToStack(X86::MOV32mi, 0x00000000UL, 4),IsMovValueToStack(X86::MOV16mi, 0x0000UL, 8),OpcodeIs(X86::LD_F80m), CopySt0ToSt1,IsStackDeallocate(10)));}TEST_F(X86Core2TargetTest, SetRegToST0_64Bits) {EXPECT_THAT(setRegTo(X86::ST0, APInt(64, 0x1111222233334444ULL)),ElementsAre(IsStackAllocate(10),IsMovValueToStack(X86::MOV32mi, 0x33334444UL, 0),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 4),IsMovValueToStack(X86::MOV16mi, 0x0000UL, 8),OpcodeIs(X86::LD_F80m), IsStackDeallocate(10)));}TEST_F(X86Core2TargetTest, SetRegToST0_80Bits) {EXPECT_THAT(setRegTo(X86::ST0, APInt(80, "11112222333344445555", 16)),ElementsAre(IsStackAllocate(10),IsMovValueToStack(X86::MOV32mi, 0x44445555UL, 0),IsMovValueToStack(X86::MOV32mi, 0x22223333UL, 4),IsMovValueToStack(X86::MOV16mi, 0x1111UL, 8),OpcodeIs(X86::LD_F80m), IsStackDeallocate(10)));}TEST_F(X86Core2TargetTest, SetRegToFP0_80Bits) {EXPECT_THAT(setRegTo(X86::FP0, APInt(80, "11112222333344445555", 16)),ElementsAre(IsStackAllocate(10),IsMovValueToStack(X86::MOV32mi, 0x44445555UL, 0),IsMovValueToStack(X86::MOV32mi, 0x22223333UL, 4),IsMovValueToStack(X86::MOV16mi, 0x1111UL, 8),OpcodeIs(X86::LD_Fp80m), IsStackDeallocate(10)));}TEST_F(X86Core2TargetTest, SetRegToFP1_32Bits) {EXPECT_THAT(setRegTo(X86::FP1, APInt(32, 0x11112222ULL)),ElementsAre(IsStackAllocate(10),IsMovValueToStack(X86::MOV32mi, 0x11112222UL, 0),IsMovValueToStack(X86::MOV32mi, 0x00000000UL, 4),IsMovValueToStack(X86::MOV16mi, 0x0000UL, 8),OpcodeIs(X86::LD_Fp80m), IsStackDeallocate(10)));}TEST_F(X86Core2TargetTest, SetRegToFP1_4Bits) {EXPECT_THAT(setRegTo(X86::FP1, APInt(4, 0x1ULL)),ElementsAre(IsStackAllocate(10),IsMovValueToStack(X86::MOV32mi, 0x00000001UL, 0),IsMovValueToStack(X86::MOV32mi, 0x00000000UL, 4),IsMovValueToStack(X86::MOV16mi, 0x0000UL, 8),OpcodeIs(X86::LD_Fp80m), IsStackDeallocate(10)));}TEST_F(X86Core2Avx512TargetTest, FillMemoryOperands_ADD64rm) {const Instruction &I = getInstr(X86::ADD64rm);InstructionTemplate IT(&I);constexpr const int kOffset = 42;State.getExegesisTarget().fillMemoryOperands(IT, X86::RDI, kOffset);// Memory is operands 2-6.EXPECT_THAT(IT.getValueFor(I.Operands[2]), IsReg(X86::RDI));EXPECT_THAT(IT.getValueFor(I.Operands[3]), IsImm(1));EXPECT_THAT(IT.getValueFor(I.Operands[4]), IsReg(0));EXPECT_THAT(IT.getValueFor(I.Operands[5]), IsImm(kOffset));EXPECT_THAT(IT.getValueFor(I.Operands[6]), IsReg(0));}TEST_F(X86Core2Avx512TargetTest, FillMemoryOperands_VGATHERDPSZ128rm) {const Instruction &I = getInstr(X86::VGATHERDPSZ128rm);InstructionTemplate IT(&I);constexpr const int kOffset = 42;State.getExegesisTarget().fillMemoryOperands(IT, X86::RDI, kOffset);// Memory is operands 4-8.EXPECT_THAT(IT.getValueFor(I.Operands[4]), IsReg(X86::RDI));EXPECT_THAT(IT.getValueFor(I.Operands[5]), IsImm(1));EXPECT_THAT(IT.getValueFor(I.Operands[6]), IsReg(0));EXPECT_THAT(IT.getValueFor(I.Operands[7]), IsImm(kOffset));EXPECT_THAT(IT.getValueFor(I.Operands[8]), IsReg(0));}TEST_F(X86Core2TargetTest, AllowAsBackToBack) {EXPECT_TRUE(State.getExegesisTarget().allowAsBackToBack(getInstr(X86::ADD64rr)));EXPECT_FALSE(State.getExegesisTarget().allowAsBackToBack(getInstr(X86::LEA64r)));}} // namespace} // namespace exegesis} // namespace llvm
//===-- SnippetRepetitorTest.cpp --------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "../Common/AssemblerUtils.h"#include "LlvmState.h"#include "MCInstrDescView.h"#include "RegisterAliasing.h"#include "TestBase.h"#include "X86InstrInfo.h"#include "llvm/CodeGen/MachineBasicBlock.h"namespace llvm {namespace exegesis {void InitializeX86ExegesisTarget();namespace {using testing::ElementsAre;using testing::Eq;using testing::Field;using testing::Property;using testing::UnorderedElementsAre;class X86SnippetRepetitorTest : public X86TestBase {protected:void SetUp() override {TM = State.createTargetMachine();Context = std::make_unique<LLVMContext>();Mod = std::make_unique<Module>("X86SnippetRepetitorTest", *Context);Mod->setDataLayout(TM->createDataLayout());MMI = std::make_unique<MachineModuleInfo>(TM.get());MF = &createVoidVoidPtrMachineFunction("TestFn", Mod.get(), MMI.get());}void TestCommon(InstructionBenchmark::RepetitionModeE RepetitionMode) {const auto Repetitor = SnippetRepetitor::Create(RepetitionMode, State);const std::vector<MCInst> Instructions = {MCInstBuilder(X86::NOOP)};FunctionFiller Sink(*MF, {X86::EAX});const auto Fill =Repetitor->Repeat(Instructions, kMinInstructions, kLoopBodySize);Fill(Sink);}static constexpr const unsigned kMinInstructions = 3;static constexpr const unsigned kLoopBodySize = 5;std::unique_ptr<LLVMTargetMachine> TM;std::unique_ptr<LLVMContext> Context;std::unique_ptr<Module> Mod;std::unique_ptr<MachineModuleInfo> MMI;MachineFunction *MF = nullptr;};static auto HasOpcode = [](unsigned Opcode) {return Property(&MachineInstr::getOpcode, Eq(Opcode));};static auto LiveReg = [](unsigned Reg) {return Field(&MachineBasicBlock::RegisterMaskPair::PhysReg, Eq(Reg));};TEST_F(X86SnippetRepetitorTest, Duplicate) {TestCommon(InstructionBenchmark::Duplicate);// Duplicating creates a single basic block that repeats the instructions.ASSERT_EQ(MF->getNumBlockIDs(), 1u);EXPECT_THAT(MF->getBlockNumbered(0)->instrs(),ElementsAre(HasOpcode(X86::NOOP), HasOpcode(X86::NOOP),HasOpcode(X86::NOOP), HasOpcode(X86::RET64)));}TEST_F(X86SnippetRepetitorTest, Loop) {TestCommon(InstructionBenchmark::Loop);// Duplicating creates an entry block, a loop body and a ret block.ASSERT_EQ(MF->getNumBlockIDs(), 3u);const auto &LoopBlock = *MF->getBlockNumbered(1);EXPECT_THAT(LoopBlock.instrs(),ElementsAre(HasOpcode(X86::NOOP), HasOpcode(X86::NOOP),HasOpcode(X86::NOOP), HasOpcode(X86::NOOP),HasOpcode(X86::NOOP), HasOpcode(X86::ADD64ri8),HasOpcode(X86::JCC_1)));EXPECT_THAT(LoopBlock.liveins(),UnorderedElementsAre(LiveReg(X86::EAX),LiveReg(State.getExegesisTarget().getLoopCounterRegister(State.getTargetMachine().getTargetTriple()))));EXPECT_THAT(MF->getBlockNumbered(2)->instrs(),ElementsAre(HasOpcode(X86::RET64)));}} // namespace} // namespace exegesis} // namespace llvm
//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "../Common/AssemblerUtils.h"#include "LlvmState.h"#include "MCInstrDescView.h"#include "ParallelSnippetGenerator.h"#include "RegisterAliasing.h"#include "SerialSnippetGenerator.h"#include "TestBase.h"#include "X86InstrInfo.h"#include <unordered_set>namespace llvm {namespace exegesis {void InitializeX86ExegesisTarget();namespace {using testing::AnyOf;using testing::ElementsAre;using testing::Gt;using testing::HasSubstr;using testing::Not;using testing::SizeIs;using testing::UnorderedElementsAre;MATCHER(IsInvalid, "") { return !arg.isValid(); }MATCHER(IsReg, "") { return arg.isReg(); }template <typename SnippetGeneratorT>class X86SnippetGeneratorTest : public X86TestBase {protected:X86SnippetGeneratorTest() : Generator(State, SnippetGenerator::Options()),InstrInfo(State.getInstrInfo()) {}std::vector<CodeTemplate> checkAndGetCodeTemplates(unsigned Opcode) {randomGenerator().seed(0); // Initialize seed.const Instruction &Instr = State.getIC().getInstr(Opcode);auto CodeTemplateOrError = Generator.generateCodeTemplates(&Instr, State.getRATC().emptyRegisters());EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.return std::move(CodeTemplateOrError.get());}SnippetGeneratorT Generator;const MCInstrInfo &InstrInfo;};using X86SerialSnippetGeneratorTest = X86SnippetGeneratorTest<SerialSnippetGenerator>;using X86ParallelSnippetGeneratorTest =X86SnippetGeneratorTest<ParallelSnippetGenerator>;TEST_F(X86SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughImplicitReg) {// - ADC16i16// - Op0 Explicit Use Immediate// - Op1 Implicit Def Reg(AX)// - Op2 Implicit Def Reg(EFLAGS)// - Op3 Implicit Use Reg(AX)// - Op4 Implicit Use Reg(EFLAGS)// - Var0 [Op0]// - hasAliasingImplicitRegisters (execution is always serial)// - hasAliasingRegistersconst unsigned Opcode = X86::ADC16i16;EXPECT_THAT(InstrInfo.get(Opcode).getImplicitDefs()[0], X86::AX);EXPECT_THAT(InstrInfo.get(Opcode).getImplicitDefs()[1], X86::EFLAGS);EXPECT_THAT(InstrInfo.get(Opcode).getImplicitUses()[0], X86::AX);EXPECT_THAT(InstrInfo.get(Opcode).getImplicitUses()[1], X86::EFLAGS);const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(1)); // Imm.EXPECT_THAT(IT.getVariableValues()[0], IsInvalid()) << "Immediate is not set";}TEST_F(X86SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughTiedRegs) {// - ADD16ri// - Op0 Explicit Def RegClass(GR16)// - Op1 Explicit Use RegClass(GR16) TiedToOp0// - Op2 Explicit Use Immediate// - Op3 Implicit Def Reg(EFLAGS)// - Var0 [Op0,Op1]// - Var1 [Op2]// - hasTiedRegisters (execution is always serial)// - hasAliasingRegistersconst unsigned Opcode = X86::ADD16ri;EXPECT_THAT(InstrInfo.get(Opcode).getImplicitDefs()[0], X86::EFLAGS);const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(2));EXPECT_THAT(IT.getVariableValues()[0], IsInvalid()) << "Operand 1 is not set";EXPECT_THAT(IT.getVariableValues()[1], IsInvalid()) << "Operand 2 is not set";}TEST_F(X86SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughExplicitRegs) {// - VXORPSrr// - Op0 Explicit Def RegClass(VR128)// - Op1 Explicit Use RegClass(VR128)// - Op2 Explicit Use RegClass(VR128)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasAliasingRegistersconst unsigned Opcode = X86::VXORPSrr;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(3));EXPECT_THAT(IT.getVariableValues(),AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()),ElementsAre(IsReg(), IsReg(), IsInvalid())))<< "Op0 is either set to Op1 or to Op2";}TEST_F(X86SerialSnippetGeneratorTest,ImplicitSelfDependencyThroughExplicitRegsForbidAll) {// - VXORPSrr// - Op0 Explicit Def RegClass(VR128)// - Op1 Explicit Use RegClass(VR128)// - Op2 Explicit Use RegClass(VR128)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasAliasingRegistersconst unsigned Opcode = X86::VXORPSrr;randomGenerator().seed(0); // Initialize seed.const Instruction &Instr = State.getIC().getInstr(Opcode);auto AllRegisters = State.getRATC().emptyRegisters();AllRegisters.flip();auto Error =Generator.generateCodeTemplates(&Instr, AllRegisters).takeError();EXPECT_TRUE((bool)Error);consumeError(std::move(Error));}TEST_F(X86SerialSnippetGeneratorTest, DependencyThroughOtherOpcode) {// - CMP64rr// - Op0 Explicit Use RegClass(GR64)// - Op1 Explicit Use RegClass(GR64)// - Op2 Implicit Def Reg(EFLAGS)// - Var0 [Op0]// - Var1 [Op1]const unsigned Opcode = X86::CMP64rr;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(Gt(1U))) << "Many templates are available";for (const auto &CT : CodeTemplates) {EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR);ASSERT_THAT(CT.Instructions, SizeIs(2));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(2));EXPECT_THAT(IT.getVariableValues(),AnyOf(ElementsAre(IsReg(), IsInvalid()),ElementsAre(IsInvalid(), IsReg())));EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode));// TODO: check that the two instructions alias each other.}}TEST_F(X86SerialSnippetGeneratorTest, LAHF) {// - LAHF// - Op0 Implicit Def Reg(AH)// - Op1 Implicit Use Reg(EFLAGS)const unsigned Opcode = X86::LAHF;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(Gt(1U))) << "Many templates are available";for (const auto &CT : CodeTemplates) {EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR);ASSERT_THAT(CT.Instructions, SizeIs(2));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(0));}}TEST_F(X86SerialSnippetGeneratorTest, VCVTUSI642SDZrrb_Int) {// - VCVTUSI642SDZrrb_Int// - Op0 Explicit Def RegClass(VR128X)// - Op1 Explicit Use RegClass(VR128X)// - Op2 Explicit Use STATIC_ROUNDING// - Op2 Explicit Use RegClass(GR64)// - Op4 Implicit Use Reg(MXSCR)const unsigned Opcode = X86::VCVTUSI642SDZrrb_Int;const Instruction &Instr = State.getIC().getInstr(Opcode);std::vector<BenchmarkCode> Configs;auto Error = Generator.generateConfigurations(&Instr, Configs, State.getRATC().emptyRegisters());ASSERT_FALSE(Error);ASSERT_THAT(Configs, SizeIs(1));const BenchmarkCode &BC = Configs[0];ASSERT_THAT(BC.Key.Instructions, SizeIs(1));ASSERT_TRUE(BC.Key.Instructions[0].getOperand(3).isImm());}TEST_F(X86ParallelSnippetGeneratorTest, SerialInstruction) {// - CDQ// - Op0 Implicit Def Reg(EAX)// - Op1 Implicit Def Reg(EDX)// - Op2 Implicit Use Reg(EAX)// - hasAliasingImplicitRegisters (execution is always serial)// - hasAliasingRegistersconst unsigned Opcode = X86::CDQ;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Info, HasSubstr("serial"));EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(0));}TEST_F(X86ParallelSnippetGeneratorTest, StaticRenaming) {// CMOV32rr has tied variables, we enumerate the possible values to execute// as many in parallel as possible.// - CMOV32rr// - Op0 Explicit Def RegClass(GR32)// - Op1 Explicit Use RegClass(GR32) TiedToOp0// - Op2 Explicit Use RegClass(GR32)// - Op3 Explicit Use Immediate// - Op3 Implicit Use Reg(EFLAGS)// - Var0 [Op0,Op1]// - Var1 [Op2]// - hasTiedRegisters (execution is always serial)// - hasAliasingRegistersconst unsigned Opcode = X86::CMOV32rr;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Info, HasSubstr("static renaming"));EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);constexpr const unsigned kInstructionCount = 15;ASSERT_THAT(CT.Instructions, SizeIs(kInstructionCount));std::unordered_set<unsigned> AllDefRegisters;for (const auto &IT : CT.Instructions) {ASSERT_THAT(IT.getVariableValues(), SizeIs(3));AllDefRegisters.insert(IT.getVariableValues()[0].getReg());}EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))<< "Each instruction writes to a different register";}TEST_F(X86ParallelSnippetGeneratorTest, NoTiedVariables) {// CMOV_GR32 has no tied variables, we make sure def and use are different// from each other.// - CMOV_GR32// - Op0 Explicit Def RegClass(GR32)// - Op1 Explicit Use RegClass(GR32)// - Op2 Explicit Use RegClass(GR32)// - Op3 Explicit Use Immediate// - Op4 Implicit Use Reg(EFLAGS)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - Var3 [Op3]// - hasAliasingRegistersconst unsigned Opcode = X86::CMOV_GR32;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(4));EXPECT_THAT(IT.getVariableValues()[0].getReg(),Not(IT.getVariableValues()[1].getReg()))<< "Def is different from first Use";EXPECT_THAT(IT.getVariableValues()[0].getReg(),Not(IT.getVariableValues()[2].getReg()))<< "Def is different from second Use";EXPECT_THAT(IT.getVariableValues()[3], IsInvalid());}TEST_F(X86ParallelSnippetGeneratorTest, MemoryUse) {// Mov32rm reads from memory.// - MOV32rm// - Op0 Explicit Def RegClass(GR32)// - Op1 Explicit Use Memory RegClass(GR8)// - Op2 Explicit Use Memory// - Op3 Explicit Use Memory RegClass(GRH8)// - Op4 Explicit Use Memory// - Op5 Explicit Use Memory RegClass(SEGMENT_REG)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - Var3 [Op3]// - Var4 [Op4]// - Var5 [Op5]// - hasMemoryOperands// - hasAliasingRegistersconst unsigned Opcode = X86::MOV32rm;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);ASSERT_THAT(CT.Instructions,SizeIs(ParallelSnippetGenerator::kMinNumDifferentAddresses));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(6));EXPECT_EQ(IT.getVariableValues()[2].getImm(), 1);EXPECT_EQ(IT.getVariableValues()[3].getReg(), 0u);EXPECT_EQ(IT.getVariableValues()[4].getImm(), 0);EXPECT_EQ(IT.getVariableValues()[5].getReg(), 0u);}TEST_F(X86ParallelSnippetGeneratorTest, MOV16ms) {const unsigned Opcode = X86::MOV16ms;const Instruction &Instr = State.getIC().getInstr(Opcode);std::vector<BenchmarkCode> Benchmarks;auto Err = Generator.generateConfigurations(&Instr, Benchmarks,State.getRATC().emptyRegisters());EXPECT_TRUE((bool)Err);EXPECT_THAT(toString(std::move(Err)),testing::HasSubstr("no available registers"));}TEST_F(X86ParallelSnippetGeneratorTest,AvoidSerializingThroughImplicitRegisters) {// MULX32rr implicitly uses EDX. We should not select that register to avoid// serialization.const unsigned Opcode = X86::MULX32rr;randomGenerator().seed(0); // Initialize seed.const Instruction &Instr = State.getIC().getInstr(Opcode);// Forbid all registers but RDX/EDX/DX/DH/DL. The only option would be to// choose that register, but that would serialize the instruction, so we// should be returning an error.auto AllRegisters = State.getRATC().emptyRegisters();AllRegisters.flip();AllRegisters.reset(X86::RDX);AllRegisters.reset(X86::EDX);AllRegisters.reset(X86::DX);AllRegisters.reset(X86::DH);AllRegisters.reset(X86::DL);auto Err = Generator.generateCodeTemplates(&Instr, AllRegisters);EXPECT_FALSE((bool)Err);EXPECT_THAT(toString(Err.takeError()),testing::HasSubstr("no available registers"));}class X86FakeSnippetGenerator : public SnippetGenerator {public:X86FakeSnippetGenerator(const LLVMState &State, const Options &Opts): SnippetGenerator(State, Opts) {}const Instruction &getInstr(unsigned Opcode) {return State.getIC().getInstr(Opcode);}InstructionTemplate getInstructionTemplate(unsigned Opcode) {return {&getInstr(Opcode)};}private:Expected<std::vector<CodeTemplate>>generateCodeTemplates(InstructionTemplate, const BitVector &) const override {return make_error<StringError>("not implemented", inconvertibleErrorCode());}};using X86FakeSnippetGeneratorTest = X86SnippetGeneratorTest<X86FakeSnippetGenerator>;testing::Matcher<const RegisterValue &> IsRegisterValue(unsigned Reg,APInt Value) {return testing::AllOf(testing::Field(&RegisterValue::Register, Reg),testing::Field(&RegisterValue::Value, Value));}TEST_F(X86FakeSnippetGeneratorTest, MemoryUse_Movsb) {// MOVSB writes to scratch memory register.// - MOVSB// - Op0 Explicit Use Memory RegClass(GR8)// - Op1 Explicit Use Memory RegClass(GR8)// - Op2 Explicit Use Memory RegClass(SEGMENT_REG)// - Op3 Implicit Def Reg(EDI)// - Op4 Implicit Def Reg(ESI)// - Op5 Implicit Use Reg(EDI)// - Op6 Implicit Use Reg(ESI)// - Op7 Implicit Use Reg(DF)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasMemoryOperands// - hasAliasingImplicitRegisters (execution is always serial)// - hasAliasingRegistersconst unsigned Opcode = X86::MOVSB;const Instruction &Instr = State.getIC().getInstr(Opcode);std::vector<BenchmarkCode> Benchmarks;auto Error = Generator.generateConfigurations(&Instr, Benchmarks, State.getRATC().emptyRegisters());EXPECT_TRUE((bool)Error);consumeError(std::move(Error));}TEST_F(X86FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd16ri) {// ADD16ri:// explicit def 0 : reg RegClass=GR16// explicit use 1 : reg RegClass=GR16 | TIED_TO:0// explicit use 2 : imm// implicit def : EFLAGSInstructionTemplate IT = Generator.getInstructionTemplate(X86::ADD16ri);IT.getValueFor(IT.getInstr().Variables[0]) = MCOperand::createReg(X86::AX);std::vector<InstructionTemplate> Snippet;Snippet.push_back(std::move(IT));const auto RIV = Generator.computeRegisterInitialValues(Snippet);EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(X86::AX, APInt())));}TEST_F(X86FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd64rr) {// ADD64rr:// mov64ri rax, 42// add64rr rax, rax, rbx// -> only rbx needs defining.std::vector<InstructionTemplate> Snippet;{InstructionTemplate Mov = Generator.getInstructionTemplate(X86::MOV64ri);Mov.getValueFor(Mov.getInstr().Variables[0]) =MCOperand::createReg(X86::RAX);Mov.getValueFor(Mov.getInstr().Variables[1]) = MCOperand::createImm(42);Snippet.push_back(std::move(Mov));}{InstructionTemplate Add = Generator.getInstructionTemplate(X86::ADD64rr);Add.getValueFor(Add.getInstr().Variables[0]) =MCOperand::createReg(X86::RAX);Add.getValueFor(Add.getInstr().Variables[1]) =MCOperand::createReg(X86::RBX);Snippet.push_back(std::move(Add));}const auto RIV = Generator.computeRegisterInitialValues(Snippet);EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(X86::RBX, APInt())));}} // namespace} // namespace exegesis} // namespace llvm
//===-- SnippetFileTest.cpp -------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "SnippetFile.h"#include "LlvmState.h"#include "TestBase.h"#include "X86InstrInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/Error.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {void InitializeX86ExegesisTarget();namespace {using testing::AllOf;using testing::ElementsAre;using testing::Eq;using testing::Field;using testing::Property;using testing::SizeIs;using llvm::unittest::TempDir;class X86SnippetFileTest : public X86TestBase {protected:Expected<std::vector<BenchmarkCode>> TestCommon(StringRef Contents) {TempDir TestDirectory("SnippetFileTestDir", /*Unique*/ true);SmallString<64> Filename(TestDirectory.path());sys::path::append(Filename, "snippet.s");errs() << Filename << "-------\n";{std::error_code EC;raw_fd_ostream FOS(Filename, EC);FOS << Contents;EXPECT_FALSE(EC);}return readSnippets(State, Filename);}};// FIXME: Refactor these to ../Common/Matchers.hstatic auto HasOpcode = [](unsigned Opcode) {return Property(&MCInst::getOpcode, Eq(Opcode));};MATCHER_P2(RegisterInitialValueIs, Reg, Val, "") {if (arg.Register == Reg &&arg.Value.getLimitedValue() == static_cast<uint64_t>(Val))return true;*result_listener << "expected: {" << Reg << ", " << Val << "} ";*result_listener << "actual: {" << arg.Register << ", "<< arg.Value.getLimitedValue() << "}";return false;}TEST_F(X86SnippetFileTest, Works) {auto Snippets = TestCommon(R"(# LLVM-EXEGESIS-DEFREG RAX 0f# LLVM-EXEGESIS-DEFREG SIL 0# LLVM-EXEGESIS-LIVEIN RDI# LLVM-EXEGESIS-LIVEIN DLincq %rax)");EXPECT_FALSE((bool)Snippets.takeError());ASSERT_THAT(*Snippets, SizeIs(1));const auto &Snippet = (*Snippets)[0];ASSERT_THAT(Snippet.Key.Instructions, ElementsAre(HasOpcode(X86::INC64r)));ASSERT_THAT(Snippet.Key.RegisterInitialValues,ElementsAre(RegisterInitialValueIs(X86::RAX, 15),RegisterInitialValueIs(X86::SIL, 0)));ASSERT_THAT(Snippet.LiveIns, ElementsAre(X86::RDI, X86::DL));}TEST_F(X86SnippetFileTest, BadDefregParam) {auto Error = TestCommon(R"(# LLVM-EXEGESIS-DEFREG DOESNOEXIST 0incq %rax)").takeError();EXPECT_TRUE((bool)Error);consumeError(std::move(Error));}TEST_F(X86SnippetFileTest, NoDefregValue) {auto Error = TestCommon(R"(# LLVM-EXEGESIS-DEFREG RAXincq %rax)").takeError();EXPECT_TRUE((bool)Error);consumeError(std::move(Error));}TEST_F(X86SnippetFileTest, MissingParam) {auto Error = TestCommon(R"(# LLVM-EXEGESIS-LIVEINincq %rax)").takeError();EXPECT_TRUE((bool)Error);consumeError(std::move(Error));}TEST_F(X86SnippetFileTest, NoAsmStreamer) {auto Snippets = TestCommon(R"(.cv_fpo_proc foo 4)");EXPECT_FALSE((bool)Snippets.takeError());}} // namespace} // namespace exegesis} // namespace llvm
//===-- SchedClassResolutionTest.cpp ----------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "SchedClassResolution.h"#include <cassert>#include <memory>#include "TestBase.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace {using testing::Pair;using testing::UnorderedElementsAre;class X86SchedClassResolutionTest : public X86TestBase {protected:X86SchedClassResolutionTest() : STI(State.getSubtargetInfo()) {// Compute the ProxResIdx of ports uses in tests.const auto &SM = STI.getSchedModel();for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {const std::string Name = SM.getProcResource(I)->Name;if (Name == "HWPort0") {P0Idx = I;} else if (Name == "HWPort1") {P1Idx = I;} else if (Name == "HWPort5") {P5Idx = I;} else if (Name == "HWPort6") {P6Idx = I;} else if (Name == "HWPort05") {P05Idx = I;} else if (Name == "HWPort0156") {P0156Idx = I;}}EXPECT_NE(P0Idx, 0);EXPECT_NE(P1Idx, 0);EXPECT_NE(P5Idx, 0);EXPECT_NE(P6Idx, 0);EXPECT_NE(P05Idx, 0);EXPECT_NE(P0156Idx, 0);}protected:const MCSubtargetInfo &STI;uint16_t P0Idx = 0;uint16_t P1Idx = 0;uint16_t P5Idx = 0;uint16_t P6Idx = 0;uint16_t P05Idx = 0;uint16_t P0156Idx = 0;};TEST_F(X86SchedClassResolutionTest, ComputeIdealizedProcResPressure_2P0) {const auto Pressure =computeIdealizedProcResPressure(STI.getSchedModel(), {{P0Idx, 2}});EXPECT_THAT(Pressure, UnorderedElementsAre(Pair(P0Idx, 2.0)));}TEST_F(X86SchedClassResolutionTest, ComputeIdealizedProcResPressure_2P05) {const auto Pressure =computeIdealizedProcResPressure(STI.getSchedModel(), {{P05Idx, 2}});EXPECT_THAT(Pressure,UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P5Idx, 1.0)));}TEST_F(X86SchedClassResolutionTest, ComputeIdealizedProcResPressure_2P05_2P0156) {const auto Pressure = computeIdealizedProcResPressure(STI.getSchedModel(), {{P05Idx, 2}, {P0156Idx, 2}});EXPECT_THAT(Pressure,UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));}TEST_F(X86SchedClassResolutionTest,ComputeIdealizedProcResPressure_1P1_1P05_2P0156) {const auto Pressure = computeIdealizedProcResPressure(STI.getSchedModel(), {{P1Idx, 1}, {P05Idx, 1}, {P0156Idx, 2}});EXPECT_THAT(Pressure,UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));}} // namespace} // namespace exegesis} // namespace llvm
//===-- X86RegisterAliasingTest.cpp --------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "RegisterAliasing.h"#include <cassert>#include <memory>#include "TestBase.h"#include "X86InstrInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace {class X86RegisterAliasingTest : public X86TestBase {};TEST_F(X86RegisterAliasingTest, TrackSimpleRegister) {const auto &RegInfo = State.getRegInfo();const RegisterAliasingTracker tracker(RegInfo, X86::EAX);std::set<MCPhysReg> ActualAliasedRegisters;for (unsigned I : tracker.aliasedBits().set_bits())ActualAliasedRegisters.insert(static_cast<MCPhysReg>(I));const std::set<MCPhysReg> ExpectedAliasedRegisters = {X86::AL, X86::AH, X86::AX, X86::EAX, X86::HAX, X86::RAX};ASSERT_THAT(ActualAliasedRegisters, ExpectedAliasedRegisters);for (MCPhysReg aliased : ExpectedAliasedRegisters) {ASSERT_THAT(tracker.getOrigin(aliased), X86::EAX);}}TEST_F(X86RegisterAliasingTest, TrackRegisterClass) {// The alias bits for GR8_ABCD_LRegClassID are the union of the alias bits for// AL, BL, CL and DL.const auto &RegInfo = State.getRegInfo();const BitVector NoReservedReg(RegInfo.getNumRegs());const RegisterAliasingTracker RegClassTracker(RegInfo, NoReservedReg, RegInfo.getRegClass(X86::GR8_ABCD_LRegClassID));BitVector sum(RegInfo.getNumRegs());sum |= RegisterAliasingTracker(RegInfo, X86::AL).aliasedBits();sum |= RegisterAliasingTracker(RegInfo, X86::BL).aliasedBits();sum |= RegisterAliasingTracker(RegInfo, X86::CL).aliasedBits();sum |= RegisterAliasingTracker(RegInfo, X86::DL).aliasedBits();ASSERT_THAT(RegClassTracker.aliasedBits(), sum);}TEST_F(X86RegisterAliasingTest, TrackRegisterClassCache) {// Fetching twice the same tracker yields the same pointers.const auto &RegInfo = State.getRegInfo();const BitVector NoReservedReg(RegInfo.getNumRegs());RegisterAliasingTrackerCache Cache(RegInfo, NoReservedReg);ASSERT_THAT(&Cache.getRegister(X86::AX), &Cache.getRegister(X86::AX));ASSERT_THAT(&Cache.getRegisterClass(X86::GR8_ABCD_LRegClassID),&Cache.getRegisterClass(X86::GR8_ABCD_LRegClassID));}} // namespace} // namespace exegesis} // namespace llvm
add_llvm_exegesis_unittest_includes(${LLVM_MAIN_SRC_DIR}/lib/Target/X86${LLVM_BINARY_DIR}/lib/Target/X86${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib)add_llvm_exegesis_unittest_sources(AssemblerTest.cppBenchmarkResultTest.cppRegisterAliasingTest.cppSchedClassResolutionTest.cppSnippetFileTest.cppSnippetGeneratorTest.cppSnippetRepetitorTest.cppTargetTest.cpp)add_llvm_exegesis_unittest_link_components(CoreCodegenMCMCParserObjectSupportSymbolizeX86)add_llvm_exegesis_unittest_link_libraries(LLVMExegesisX86)
//===-- BenchmarkResultTest.cpp ---------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "BenchmarkResult.h"#include "X86InstrInfo.h"#include "llvm/ADT/SmallString.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/Error.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Support/raw_ostream.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using ::testing::AllOf;using ::testing::Eq;using ::testing::get;using ::testing::Pointwise;using ::testing::Property;namespace llvm {namespace exegesis {static std::string Dump(const MCInst &McInst) {std::string Buffer;raw_string_ostream OS(Buffer);McInst.print(OS);return Buffer;}MATCHER(EqMCInst, "") {const std::string Lhs = Dump(get<0>(arg));const std::string Rhs = Dump(get<1>(arg));if (Lhs != Rhs) {*result_listener << Lhs << " <=> " << Rhs;return false;}return true;}namespace {TEST(BenchmarkResultTest, WriteToAndReadFromDisk) {LLVMInitializeX86TargetInfo();LLVMInitializeX86Target();LLVMInitializeX86TargetMC();// Read benchmarks.const LLVMState State("x86_64-unknown-linux", "haswell");ExitOnError ExitOnErr;InstructionBenchmark ToDisk;ToDisk.Key.Instructions.push_back(MCInstBuilder(X86::XOR32rr).addReg(X86::AL).addReg(X86::AH).addImm(123).addDFPImm(bit_cast<uint64_t>(0.5)));ToDisk.Key.Config = "config";ToDisk.Key.RegisterInitialValues = {RegisterValue{X86::AL, APInt(8, "-1", 10)},RegisterValue{X86::AH, APInt(8, "123", 10)}};ToDisk.Mode = InstructionBenchmark::Latency;ToDisk.CpuName = "cpu_name";ToDisk.LLVMTriple = "llvm_triple";ToDisk.NumRepetitions = 1;ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, 1});ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, 2});ToDisk.Error = "error";ToDisk.Info = "info";SmallString<64> Filename;std::error_code EC;EC = sys::fs::createUniqueDirectory("BenchmarkResultTestDir", Filename);ASSERT_FALSE(EC);sys::path::append(Filename, "data.yaml");errs() << Filename << "-------\n";ExitOnErr(ToDisk.writeYaml(State, Filename));{// One-element version.const auto FromDisk =ExitOnErr(InstructionBenchmark::readYaml(State, Filename));EXPECT_THAT(FromDisk.Key.Instructions,Pointwise(EqMCInst(), ToDisk.Key.Instructions));EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);EXPECT_THAT(FromDisk.Error, ToDisk.Error);EXPECT_EQ(FromDisk.Info, ToDisk.Info);}{// Vector version.const auto FromDiskVector =ExitOnErr(InstructionBenchmark::readYamls(State, Filename));ASSERT_EQ(FromDiskVector.size(), size_t{1});const auto FromDisk = FromDiskVector[0];EXPECT_THAT(FromDisk.Key.Instructions,Pointwise(EqMCInst(), ToDisk.Key.Instructions));EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);EXPECT_THAT(FromDisk.Error, ToDisk.Error);EXPECT_EQ(FromDisk.Info, ToDisk.Info);}}TEST(BenchmarkResultTest, PerInstructionStats) {PerInstructionStats Stats;Stats.push(BenchmarkMeasure{"a", 0.5, 0.0});Stats.push(BenchmarkMeasure{"a", 1.5, 0.0});Stats.push(BenchmarkMeasure{"a", -1.0, 0.0});Stats.push(BenchmarkMeasure{"a", 0.0, 0.0});EXPECT_EQ(Stats.min(), -1.0);EXPECT_EQ(Stats.max(), 1.5);EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4}} // namespace} // namespace exegesis} // namespace llvm
//===-- AssemblerTest.cpp ---------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "../Common/AssemblerUtils.h"#include "X86InstrInfo.h"namespace llvm {namespace exegesis {void InitializeX86ExegesisTarget();namespace {using X86::EAX;using X86::MOV32ri;using X86::MOV64ri32;using X86::RAX;using X86::XOR32rr;class X86MachineFunctionGeneratorTest: public MachineFunctionGeneratorBaseTest {protected:X86MachineFunctionGeneratorTest(): MachineFunctionGeneratorBaseTest("x86_64-unknown-linux", "haswell") {}static void SetUpTestCase() {LLVMInitializeX86TargetInfo();LLVMInitializeX86TargetMC();LLVMInitializeX86Target();LLVMInitializeX86AsmPrinter();InitializeX86ExegesisTarget();}};TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunction) {Check({}, MCInst(), 0xc3);}TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionXOR32rr_X86) {Check({{EAX, APInt(32, 1)}},MCInstBuilder(XOR32rr).addReg(EAX).addReg(EAX).addReg(EAX),// mov eax, 10xb8, 0x01, 0x00, 0x00, 0x00,// xor eax, eax0x31, 0xc0, 0xc3);}TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionMOV64ri) {Check({}, MCInstBuilder(MOV64ri32).addReg(RAX).addImm(42), 0x48, 0xc7, 0xc0,0x2a, 0x00, 0x00, 0x00, 0xc3);}TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionMOV32ri) {Check({}, MCInstBuilder(MOV32ri).addReg(EAX).addImm(42), 0xb8, 0x2a, 0x00,0x00, 0x00, 0xc3);}} // namespace} // namespace exegesis} // namespace llvm
//===-- RegisterValueTest.cpp -----------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "RegisterValue.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace {#define CHECK(EXPECTED, ACTUAL) \EXPECT_EQ(APInt(SizeInBits, EXPECTED, 16), \bitcastFloatValue(Semantic, PredefinedValues::ACTUAL))TEST(RegisterValueTest, Half) {const size_t SizeInBits = 16;const auto &Semantic = APFloatBase::IEEEhalf();CHECK("0000", POS_ZERO);CHECK("8000", NEG_ZERO);CHECK("3C00", ONE);CHECK("4000", TWO);CHECK("7C00", INF);CHECK("7E00", QNAN);CHECK("7BFF", LARGEST);CHECK("0400", SMALLEST_NORM);CHECK("0001", SMALLEST);CHECK("0001", ULP);CHECK("3C01", ONE_PLUS_ULP);}TEST(RegisterValueTest, Single) {const size_t SizeInBits = 32;const auto &Semantic = APFloatBase::IEEEsingle();CHECK("00000000", POS_ZERO);CHECK("80000000", NEG_ZERO);CHECK("3F800000", ONE);CHECK("40000000", TWO);CHECK("7F800000", INF);CHECK("7FC00000", QNAN);CHECK("7F7FFFFF", LARGEST);CHECK("00800000", SMALLEST_NORM);CHECK("00000001", SMALLEST);CHECK("00000001", ULP);CHECK("3F800001", ONE_PLUS_ULP);}TEST(RegisterValueTest, Double) {const size_t SizeInBits = 64;const auto &Semantic = APFloatBase::IEEEdouble();CHECK("0000000000000000", POS_ZERO);CHECK("8000000000000000", NEG_ZERO);CHECK("3FF0000000000000", ONE);CHECK("4000000000000000", TWO);CHECK("7FF0000000000000", INF);CHECK("7FF8000000000000", QNAN);CHECK("7FEFFFFFFFFFFFFF", LARGEST);CHECK("0010000000000000", SMALLEST_NORM);CHECK("0000000000000001", SMALLEST);CHECK("0000000000000001", ULP);CHECK("3FF0000000000001", ONE_PLUS_ULP);}} // namespace} // namespace exegesis} // namespace llvm
//===-- TestBase.h ----------------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//// Test fixture common to all PowerPC tests.//===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_POWERPC_TESTBASE_H#define LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_POWERPC_TESTBASE_H#include "LlvmState.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {void InitializePowerPCExegesisTarget();class PPCTestBase : public ::testing::Test {protected:PPCTestBase() : State("powerpc64le-unknown-linux", "ppc64le") {}static void SetUpTestCase() {LLVMInitializePowerPCTargetInfo();LLVMInitializePowerPCTargetMC();LLVMInitializePowerPCTarget();InitializePowerPCExegesisTarget();}const LLVMState State;};} // namespace exegesis} // namespace llvm#endif
//===-- TargetTest.cpp ---------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "Target.h"#include <cassert>#include <memory>#include "MCTargetDesc/PPCMCTargetDesc.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm{namespace exegesis {void InitializePowerPCExegesisTarget();namespace {using testing::NotNull;using testing::IsEmpty;using testing::Not;constexpr const char kTriple[] = "powerpc64le-unknown-linux";class PowerPCTargetTest : public ::testing::Test {protected:PowerPCTargetTest(): ExegesisTarget_(ExegesisTarget::lookup(Triple(kTriple))) {EXPECT_THAT(ExegesisTarget_, NotNull());std::string error;Target_ = TargetRegistry::lookupTarget(kTriple, error);EXPECT_THAT(Target_, NotNull());}static void SetUpTestCase() {LLVMInitializePowerPCTargetInfo();LLVMInitializePowerPCTarget();LLVMInitializePowerPCTargetMC();InitializePowerPCExegesisTarget();}const Target *Target_;const ExegesisTarget *const ExegesisTarget_;};TEST_F(PowerPCTargetTest, SetRegToConstant) {const std::unique_ptr<MCSubtargetInfo> STI(Target_->createMCSubtargetInfo(kTriple, "generic", ""));const auto Insts = ExegesisTarget_->setRegTo(*STI, PPC::X0, APInt());EXPECT_THAT(Insts, Not(IsEmpty()));}TEST_F(PowerPCTargetTest, DefaultPfmCounters) {const std::string Expected = "CYCLES";EXPECT_EQ(ExegesisTarget_->getPfmCounters("").CycleCounter, Expected);EXPECT_EQ(ExegesisTarget_->getPfmCounters("unknown_cpu").CycleCounter,Expected);}} // namespace} // namespace exegesis} // namespace llvm
//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "../Common/AssemblerUtils.h"#include "LlvmState.h"#include "MCInstrDescView.h"#include "PPCInstrInfo.h"#include "ParallelSnippetGenerator.h"#include "RegisterAliasing.h"#include "SerialSnippetGenerator.h"#include "TestBase.h"#include <unordered_set>namespace llvm {namespace exegesis {namespace {using testing::AnyOf;using testing::ElementsAre;using testing::HasSubstr;using testing::SizeIs;MATCHER(IsInvalid, "") { return !arg.isValid(); }MATCHER(IsReg, "") { return arg.isReg(); }template <typename SnippetGeneratorT>class PPCSnippetGeneratorTest : public PPCTestBase {protected:PPCSnippetGeneratorTest() : Generator(State, SnippetGenerator::Options()) {}std::vector<CodeTemplate> checkAndGetCodeTemplates(unsigned Opcode) {randomGenerator().seed(0); // Initialize seed.const Instruction &Instr = State.getIC().getInstr(Opcode);auto CodeTemplateOrError = Generator.generateCodeTemplates(&Instr, State.getRATC().emptyRegisters());EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.return std::move(CodeTemplateOrError.get());}SnippetGeneratorT Generator;};using PPCSerialSnippetGeneratorTest = PPCSnippetGeneratorTest<SerialSnippetGenerator>;using PPCParallelSnippetGeneratorTest =PPCSnippetGeneratorTest<ParallelSnippetGenerator>;TEST_F(PPCSerialSnippetGeneratorTest, ImplicitSelfDependencyThroughExplicitRegs) {// - ADD8// - Op0 Explicit Def RegClass(G8RC)// - Op1 Explicit Use RegClass(G8RC)// - Op2 Explicit Use RegClass(G8RC)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasAliasingRegistersconst unsigned Opcode = PPC::ADD8;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(3));EXPECT_THAT(IT.getVariableValues(),AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()),ElementsAre(IsReg(), IsReg(), IsInvalid())))<< "Op0 is either set to Op1 or to Op2";}TEST_F(PPCSerialSnippetGeneratorTest, ImplicitSelfDependencyThroughTiedRegs) {// - RLDIMI// - Op0 Explicit Def RegClass(G8RC)// - Op1 Explicit Use RegClass(G8RC) TiedToOp0// - Op2 Explicit Use RegClass(G8RC)// - Op3 Explicit Use Immediate// - Op4 Explicit Use Immediate// - Var0 [Op0,Op1]// - Var1 [Op2]// - Var2 [Op3]// - Var3 [Op4]// - hasTiedRegisters (execution is always serial)// - hasAliasingRegisters// - RLDIMIconst unsigned Opcode = PPC::RLDIMI;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(4));EXPECT_THAT(IT.getVariableValues()[2], IsInvalid()) << "Operand 1 is not set";EXPECT_THAT(IT.getVariableValues()[3], IsInvalid()) << "Operand 2 is not set";}TEST_F(PPCParallelSnippetGeneratorTest, MemoryUse) {// - LDX// - Op0 Explicit Def RegClass(G8RC)// - Op1 Explicit Use Memory RegClass(GPRC)// - Op2 Explicit Use Memory RegClass(VSSRC)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasMemoryOperands// - hasAliasingRegistersconst unsigned Opcode = PPC::LDX;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Info, HasSubstr("instruction has no tied variables picking ""Uses different from defs"));EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);ASSERT_THAT(CT.Instructions,SizeIs(ParallelSnippetGenerator::kMinNumDifferentAddresses));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(3));EXPECT_EQ(IT.getVariableValues()[1].getReg(), PPC::X1);EXPECT_EQ(IT.getVariableValues()[2].getReg(), PPC::X13);}} // namespace} // namespace exegesis} // namespace llvm
add_llvm_exegesis_unittest_includes(${LLVM_MAIN_SRC_DIR}/lib/Target/PowerPC${LLVM_BINARY_DIR}/lib/Target/PowerPC${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib)add_llvm_exegesis_unittest_link_components(MCMCParserObjectSupportSymbolizePowerPC)add_llvm_exegesis_unittest_sources(AnalysisTest.cppSnippetGeneratorTest.cppTargetTest.cpp)add_llvm_exegesis_unittest_link_libraries(LLVMExegesisPowerPC)
//===-- PPCAnalysisTest.cpp ---------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "Analysis.h"#include <cassert>#include <memory>#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm{namespace exegesis {namespace {using testing::Pair;using testing::UnorderedElementsAre;class PPCAnalysisTest : public ::testing::Test {protected:PPCAnalysisTest() {const std::string TT = "powerpc64le-unknown-linux";std::string error;const Target *const TheTarget = TargetRegistry::lookupTarget(TT, error);if (!TheTarget) {errs() << error << "\n";return;}STI.reset(TheTarget->createMCSubtargetInfo(TT, "pwr9", ""));// Compute the ProxResIdx of ports uses in tests.const auto &SM = STI->getSchedModel();for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {const std::string Name = SM.getProcResource(I)->Name;if (Name == "ALU") {ALUIdx = I;} else if (Name == "ALUE") {ALUEIdx = I;} else if (Name == "ALUO") {ALUOIdx = I;} else if (Name == "IP_AGEN") {IPAGENIdx = I;}}EXPECT_NE(ALUIdx, 0);EXPECT_NE(ALUEIdx, 0);EXPECT_NE(ALUOIdx, 0);EXPECT_NE(IPAGENIdx, 0);}static void SetUpTestCase() {LLVMInitializePowerPCTargetInfo();LLVMInitializePowerPCTarget();LLVMInitializePowerPCTargetMC();}protected:std::unique_ptr<const MCSubtargetInfo> STI;uint16_t ALUIdx = 0;uint16_t ALUEIdx = 0;uint16_t ALUOIdx = 0;uint16_t IPAGENIdx = 0;};TEST_F(PPCAnalysisTest, ComputeIdealizedProcResPressure_2ALU) {const auto Pressure =computeIdealizedProcResPressure(STI->getSchedModel(), {{ALUIdx, 2}});EXPECT_THAT(Pressure, UnorderedElementsAre(Pair(ALUIdx, 2.0)));}TEST_F(PPCAnalysisTest, ComputeIdealizedProcResPressure_1ALUE) {const auto Pressure =computeIdealizedProcResPressure(STI->getSchedModel(), {{ALUEIdx, 2}});EXPECT_THAT(Pressure, UnorderedElementsAre(Pair(ALUEIdx, 2.0)));}TEST_F(PPCAnalysisTest, ComputeIdealizedProcResPressure_1ALU1IPAGEN) {const auto Pressure =computeIdealizedProcResPressure(STI->getSchedModel(), {{ALUIdx, 1}, {IPAGENIdx, 1}});EXPECT_THAT(Pressure, UnorderedElementsAre(Pair(ALUIdx, 1.0),Pair(IPAGENIdx, 1)));}} // namespace} // namespace exegesis} // namespace llvm
//===-- PerfHelperTest.cpp --------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "PerfHelper.h"#include "llvm/Config/config.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace pfm {namespace {using ::testing::IsEmpty;using ::testing::Not;TEST(PerfHelperTest, FunctionalTest) {#ifdef HAVE_LIBPFMASSERT_FALSE(pfmInitialize());PerfEvent Event("CYCLES:u");ASSERT_TRUE(Event.valid());EXPECT_EQ(Event.name(), "CYCLES:u");EXPECT_THAT(Event.getPfmEventString(), Not(IsEmpty()));Counter Cnt(std::move(Event));Cnt.start();Cnt.stop();Cnt.read();pfmTerminate();#elseASSERT_TRUE(pfmInitialize());#endif}} // namespace} // namespace pfm} // namespace exegesis} // namespace llvm
//===-- TestBase.h ----------------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//// Test fixture common to all Mips tests.//===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_MIPS_TESTBASE_H#define LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_MIPS_TESTBASE_H#include "LlvmState.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {void InitializeMipsExegesisTarget();class MipsTestBase : public ::testing::Test {protected:MipsTestBase() : State("mips-unknown-linux", "mips32") {}static void SetUpTestCase() {LLVMInitializeMipsTargetInfo();LLVMInitializeMipsTargetMC();LLVMInitializeMipsTarget();InitializeMipsExegesisTarget();}const LLVMState State;};} // namespace exegesis} // namespace llvm#endif
//===-- TargetTest.cpp ------------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "Target.h"#include <cassert>#include <memory>#include "MCTargetDesc/MipsMCTargetDesc.h"#include "TestBase.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace {using testing::AllOf;using testing::ElementsAre;using testing::Eq;using testing::Matcher;using testing::Property;Matcher<MCOperand> IsImm(int64_t Value) {return AllOf(Property(&MCOperand::isImm, Eq(true)),Property(&MCOperand::getImm, Eq(Value)));}Matcher<MCOperand> IsReg(unsigned Reg) {return AllOf(Property(&MCOperand::isReg, Eq(true)),Property(&MCOperand::getReg, Eq(Reg)));}Matcher<MCInst> OpcodeIs(unsigned Opcode) {return Property(&MCInst::getOpcode, Eq(Opcode));}Matcher<MCInst> IsLoadLow16BitImm(unsigned Reg, int64_t Value, bool IsGPR32) {const unsigned ZeroReg = IsGPR32 ? Mips::ZERO : Mips::ZERO_64;const unsigned ORi = IsGPR32 ? Mips::ORi : Mips::ORi64;return AllOf(OpcodeIs(ORi),ElementsAre(IsReg(Reg), IsReg(ZeroReg), IsImm(Value)));}Matcher<MCInst> IsLoadHigh16BitImm(unsigned Reg, int64_t Value, bool IsGPR32) {const unsigned LUi = IsGPR32 ? Mips::LUi : Mips::LUi64;return AllOf(OpcodeIs(LUi), ElementsAre(IsReg(Reg), IsImm(Value)));}Matcher<MCInst> IsShift(unsigned Reg, uint16_t Amount, bool IsGPR32) {const unsigned SLL = IsGPR32 ? Mips::SLL : Mips::SLL64_64;return AllOf(OpcodeIs(SLL),ElementsAre(IsReg(Reg), IsReg(Reg), IsImm(Amount)));}class MipsTargetTest : public MipsTestBase {protected:std::vector<MCInst> setRegTo(unsigned Reg, const APInt &Value) {return State.getExegesisTarget().setRegTo(State.getSubtargetInfo(), Reg,Value);}};TEST_F(MipsTargetTest, SetGPR32RegTo16BitValue) {const uint16_t Value = 0xFFFFU;const unsigned Reg = Mips::T0;EXPECT_THAT(setRegTo(Reg, APInt(16, Value)),ElementsAre(IsLoadLow16BitImm(Reg, Value, true)));}TEST_F(MipsTargetTest, SetGPR64RegTo16BitValue) {const uint16_t Value = 0xFFFFU;const unsigned Reg = Mips::T0_64;EXPECT_THAT(setRegTo(Reg, APInt(16, Value)),ElementsAre(IsLoadLow16BitImm(Reg, Value, false)));}TEST_F(MipsTargetTest, SetGPR32RegTo32BitValue) {const uint32_t Value0 = 0xFFFF0000UL;const unsigned Reg0 = Mips::T0;EXPECT_THAT(setRegTo(Reg0, APInt(32, Value0)),ElementsAre(IsLoadHigh16BitImm(Reg0, 0xFFFFU, true)));const uint32_t Value1 = 0xFFFFFFFFUL;const unsigned Reg1 = Mips::T1;EXPECT_THAT(setRegTo(Reg1, APInt(32, Value1)),ElementsAre(IsLoadHigh16BitImm(Reg1, 0xFFFFU, true),IsLoadLow16BitImm(Reg1, 0xFFFFU, true)));}TEST_F(MipsTargetTest, SetGPR64RegTo32BitValue) {const uint32_t Value0 = 0x7FFF0000UL;const unsigned Reg0 = Mips::T0_64;EXPECT_THAT(setRegTo(Reg0, APInt(32, Value0)),ElementsAre(IsLoadHigh16BitImm(Reg0, 0x7FFFU, false)));const uint32_t Value1 = 0x7FFFFFFFUL;const unsigned Reg1 = Mips::T1_64;EXPECT_THAT(setRegTo(Reg1, APInt(32, Value1)),ElementsAre(IsLoadHigh16BitImm(Reg1, 0x7FFFU, false),IsLoadLow16BitImm(Reg1, 0xFFFFU, false)));const uint32_t Value2 = 0xFFFF0000UL;const unsigned Reg2 = Mips::T2_64;EXPECT_THAT(setRegTo(Reg2, APInt(32, Value2)),ElementsAre(IsLoadLow16BitImm(Reg2, 0xFFFFU, false),IsShift(Reg2, 16, false)));const uint32_t Value3 = 0xFFFFFFFFUL;const unsigned Reg3 = Mips::T3_64;EXPECT_THAT(setRegTo(Reg3, APInt(32, Value3)),ElementsAre(IsLoadLow16BitImm(Reg3, 0xFFFFU, false),IsShift(Reg3, 16, false),IsLoadLow16BitImm(Reg3, 0xFFFFU, false)));}TEST_F(MipsTargetTest, DefaultPfmCounters) {const std::string Expected = "CYCLES";EXPECT_EQ(State.getExegesisTarget().getPfmCounters("").CycleCounter,Expected);EXPECT_EQ(State.getExegesisTarget().getPfmCounters("unknown_cpu").CycleCounter,Expected);}} // namespace} // namespace exegesis} // namespace llvm
//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "../Common/AssemblerUtils.h"#include "LlvmState.h"#include "MCInstrDescView.h"#include "MipsInstrInfo.h"#include "ParallelSnippetGenerator.h"#include "RegisterAliasing.h"#include "SerialSnippetGenerator.h"#include "TestBase.h"#include <unordered_set>namespace llvm {namespace exegesis {namespace {using testing::AnyOf;using testing::ElementsAre;using testing::HasSubstr;using testing::SizeIs;MATCHER(IsInvalid, "") { return !arg.isValid(); }MATCHER(IsReg, "") { return arg.isReg(); }template <typename SnippetGeneratorT>class MipsSnippetGeneratorTest : public MipsTestBase {protected:MipsSnippetGeneratorTest() : Generator(State, SnippetGenerator::Options()) {}std::vector<CodeTemplate> checkAndGetCodeTemplates(unsigned Opcode) {randomGenerator().seed(0); // Initialize seed.const Instruction &Instr = State.getIC().getInstr(Opcode);auto CodeTemplateOrError = Generator.generateCodeTemplates(&Instr, State.getRATC().emptyRegisters());EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.return std::move(CodeTemplateOrError.get());}SnippetGeneratorT Generator;};using MipsSerialSnippetGeneratorTest = MipsSnippetGeneratorTest<SerialSnippetGenerator>;using MipsParallelSnippetGeneratorTest =MipsSnippetGeneratorTest<ParallelSnippetGenerator>;TEST_F(MipsSerialSnippetGeneratorTest, ImplicitSelfDependencyThroughExplicitRegs) {// - ADD// - Op0 Explicit Def RegClass(GPR32)// - Op1 Explicit Use RegClass(GPR32)// - Op2 Explicit Use RegClass(GPR32)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasAliasingRegistersconst unsigned Opcode = Mips::ADD;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS);ASSERT_THAT(CT.Instructions, SizeIs(1));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(3));EXPECT_THAT(IT.getVariableValues(),AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()),ElementsAre(IsReg(), IsReg(), IsInvalid())))<< "Op0 is either set to Op1 or to Op2";}TEST_F(MipsSerialSnippetGeneratorTest,ImplicitSelfDependencyThroughExplicitRegsForbidAll) {// - XOR// - Op0 Explicit Def RegClass(GPR32)// - Op1 Explicit Use RegClass(GPR32)// - Op2 Explicit Use RegClass(GPR32)// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasAliasingRegistersrandomGenerator().seed(0); // Initialize seed.const Instruction &Instr = State.getIC().getInstr(Mips::XOR);auto AllRegisters = State.getRATC().emptyRegisters();AllRegisters.flip();auto Error =Generator.generateCodeTemplates(&Instr, AllRegisters).takeError();EXPECT_TRUE((bool)Error);consumeError(std::move(Error));}TEST_F(MipsParallelSnippetGeneratorTest, MemoryUse) {// LB reads from memory.// - LB// - Op0 Explicit Def RegClass(GPR32)// - Op1 Explicit Use Memory RegClass(MSA128F16)// - Op2 Explicit Use Memory// - Var0 [Op0]// - Var1 [Op1]// - Var2 [Op2]// - hasMemoryOperandsconst unsigned Opcode = Mips::LB;const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);ASSERT_THAT(CodeTemplates, SizeIs(1));const auto &CT = CodeTemplates[0];EXPECT_THAT(CT.Info,HasSubstr("instruction is parallel, repeating a random one."));EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);ASSERT_THAT(CT.Instructions,SizeIs(ParallelSnippetGenerator::kMinNumDifferentAddresses));const InstructionTemplate &IT = CT.Instructions[0];EXPECT_THAT(IT.getOpcode(), Opcode);ASSERT_THAT(IT.getVariableValues(), SizeIs(3));EXPECT_EQ(IT.getVariableValues()[0].getReg(), 0u);EXPECT_EQ(IT.getVariableValues()[2].getImm(), 0);}} // namespace} // namespace exegesis} // namespace llvm
//===-- RegisterAliasingTest.cpp --------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "RegisterAliasing.h"#include <cassert>#include <memory>#include "MipsInstrInfo.h"#include "TestBase.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace {class MipsRegisterAliasingTest : public MipsTestBase {};TEST_F(MipsRegisterAliasingTest, TrackSimpleRegister) {const auto &RegInfo = State.getRegInfo();const RegisterAliasingTracker tracker(RegInfo, Mips::T0_64);std::set<MCPhysReg> ActualAliasedRegisters;for (unsigned I : tracker.aliasedBits().set_bits())ActualAliasedRegisters.insert(static_cast<MCPhysReg>(I));const std::set<MCPhysReg> ExpectedAliasedRegisters = {Mips::T0, Mips::T0_64};ASSERT_THAT(ActualAliasedRegisters, ExpectedAliasedRegisters);for (MCPhysReg aliased : ExpectedAliasedRegisters) {ASSERT_THAT(tracker.getOrigin(aliased), Mips::T0_64);}}TEST_F(MipsRegisterAliasingTest, TrackRegisterClass) {// The alias bits for// GPR64_with_sub_32_in_GPRMM16MoveP_and_GPRMM16ZeroRegClassID// are the union of the alias bits for ZERO_64, V0_64, V1_64 and S1_64.const auto &RegInfo = State.getRegInfo();const BitVector NoReservedReg(RegInfo.getNumRegs());const RegisterAliasingTracker RegClassTracker(RegInfo, NoReservedReg,RegInfo.getRegClass(Mips::GPR64_with_sub_32_in_GPRMM16MoveP_and_GPRMM16ZeroRegClassID));BitVector sum(RegInfo.getNumRegs());sum |= RegisterAliasingTracker(RegInfo, Mips::ZERO_64).aliasedBits();sum |= RegisterAliasingTracker(RegInfo, Mips::V0_64).aliasedBits();sum |= RegisterAliasingTracker(RegInfo, Mips::V1_64).aliasedBits();sum |= RegisterAliasingTracker(RegInfo, Mips::S1_64).aliasedBits();ASSERT_THAT(RegClassTracker.aliasedBits(), sum);}TEST_F(MipsRegisterAliasingTest, TrackRegisterClassCache) {// Fetching the same tracker twice yields the same pointers.const auto &RegInfo = State.getRegInfo();const BitVector NoReservedReg(RegInfo.getNumRegs());RegisterAliasingTrackerCache Cache(RegInfo, NoReservedReg);ASSERT_THAT(&Cache.getRegister(Mips::T0), &Cache.getRegister(Mips::T0));ASSERT_THAT(&Cache.getRegisterClass(Mips::ACC64RegClassID),&Cache.getRegisterClass(Mips::ACC64RegClassID));}} // namespace} // namespace exegesis} // namespace llvm
add_llvm_exegesis_unittest_includes(${LLVM_MAIN_SRC_DIR}/lib/Target/Mips${LLVM_BINARY_DIR}/lib/Target/Mips${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib)add_llvm_exegesis_unittest_link_components(MCMCParserObjectSupportSymbolizeMips)add_llvm_exegesis_unittest_sources(BenchmarkResultTest.cppRegisterAliasingTest.cppSnippetGeneratorTest.cppTargetTest.cpp)add_llvm_exegesis_unittest_link_libraries(LLVMExegesisMips)
//===-- BenchmarkResultTest.cpp ---------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "BenchmarkResult.h"#include "MipsInstrInfo.h"#include "TestBase.h"#include "llvm/ADT/SmallString.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/Error.h"#include "llvm/Support/Path.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using ::testing::AllOf;using ::testing::Eq;using ::testing::get;using ::testing::Pointwise;using ::testing::Property;using llvm::unittest::TempDir;namespace llvm {namespace exegesis {static std::string Dump(const MCInst &McInst) {std::string Buffer;raw_string_ostream OS(Buffer);McInst.print(OS);return Buffer;}MATCHER(EqMCInst, "") {const std::string Lhs = Dump(get<0>(arg));const std::string Rhs = Dump(get<1>(arg));if (Lhs != Rhs) {*result_listener << Lhs << " <=> " << Rhs;return false;}return true;}namespace {class MipsBenchmarkResultTest : public MipsTestBase {};TEST_F(MipsBenchmarkResultTest, WriteToAndReadFromDisk) {ExitOnError ExitOnErr;InstructionBenchmark ToDisk;ToDisk.Key.Instructions.push_back(MCInstBuilder(Mips::XOR).addReg(Mips::T0).addReg(Mips::T1).addReg(Mips::T2));ToDisk.Key.Config = "config";ToDisk.Key.RegisterInitialValues = {RegisterValue{Mips::T1, APInt(8, "123", 10)},RegisterValue{Mips::T2, APInt(8, "456", 10)}};ToDisk.Mode = InstructionBenchmark::Latency;ToDisk.CpuName = "cpu_name";ToDisk.LLVMTriple = "llvm_triple";ToDisk.NumRepetitions = 1;ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, 1});ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, 2});ToDisk.Error = "error";ToDisk.Info = "info";TempDir TestDirectory("BenchmarkResultTestDir", /*Unique*/ true);SmallString<64> Filename(TestDirectory.path());sys::path::append(Filename, "data.yaml");errs() << Filename << "-------\n";ExitOnErr(ToDisk.writeYaml(State, Filename));{// One-element version.const auto FromDisk =ExitOnErr(InstructionBenchmark::readYaml(State, Filename));EXPECT_THAT(FromDisk.Key.Instructions,Pointwise(EqMCInst(), ToDisk.Key.Instructions));EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);EXPECT_THAT(FromDisk.Error, ToDisk.Error);EXPECT_EQ(FromDisk.Info, ToDisk.Info);}{// Vector version.const auto FromDiskVector =ExitOnErr(InstructionBenchmark::readYamls(State, Filename));ASSERT_EQ(FromDiskVector.size(), size_t{1});const auto FromDisk = FromDiskVector[0];EXPECT_THAT(FromDisk.Key.Instructions,Pointwise(EqMCInst(), ToDisk.Key.Instructions));EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);EXPECT_THAT(FromDisk.Error, ToDisk.Error);EXPECT_EQ(FromDisk.Info, ToDisk.Info);}}TEST_F(MipsBenchmarkResultTest, PerInstructionStats) {PerInstructionStats Stats;Stats.push(BenchmarkMeasure{"a", 0.5, 0.0});Stats.push(BenchmarkMeasure{"a", 1.5, 0.0});Stats.push(BenchmarkMeasure{"a", -1.0, 0.0});Stats.push(BenchmarkMeasure{"a", 0.0, 0.0});EXPECT_EQ(Stats.min(), -1.0);EXPECT_EQ(Stats.max(), 1.5);EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4}} // namespace} // namespace exegesis} // namespace llvm
//===-- AssemblerUtils.h ----------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_ASSEMBLERUTILS_H#define LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_ASSEMBLERUTILS_H#include "Assembler.h"#include "BenchmarkRunner.h"#include "Target.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/CodeGen/MachineInstrBuilder.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/MC/MCInstBuilder.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/Host.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {class MachineFunctionGeneratorBaseTest : public ::testing::Test {protected:MachineFunctionGeneratorBaseTest(const std::string &TT,const std::string &CpuName): TT(TT), CpuName(CpuName),CanExecute(Triple(TT).getArch() ==Triple(sys::getProcessTriple()).getArch()),ET(ExegesisTarget::lookup(Triple(TT))) {assert(ET);if (!CanExecute) {outs() << "Skipping execution, host:" << sys::getProcessTriple()<< ", target:" << TT << "\n";}}template <class... Bs>inline void Check(ArrayRef<RegisterValue> RegisterInitialValues, MCInst Inst,Bs... Bytes) {ExecutableFunction Function =(Inst.getOpcode() == 0)? assembleToFunction(RegisterInitialValues, [](FunctionFiller &) {}): assembleToFunction(RegisterInitialValues,[Inst](FunctionFiller &Filler) {Filler.getEntry().addInstruction(Inst);});ASSERT_THAT(Function.getFunctionBytes().str(),testing::ElementsAre(Bytes...));if (CanExecute) {BenchmarkRunner::ScratchSpace Scratch;Function(Scratch.ptr());}}private:std::unique_ptr<LLVMTargetMachine> createTargetMachine() {std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);EXPECT_TRUE(TheTarget) << Error << " " << TT;const TargetOptions Options;TargetMachine *TM = TheTarget->createTargetMachine(TT, CpuName, "", Options,Reloc::Model::Static);EXPECT_TRUE(TM) << TT << " " << CpuName;return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(TM));}ExecutableFunctionassembleToFunction(ArrayRef<RegisterValue> RegisterInitialValues,FillFunction Fill) {SmallString<256> Buffer;raw_svector_ostream AsmStream(Buffer);EXPECT_FALSE(assembleToStream(*ET, createTargetMachine(), /*LiveIns=*/{},RegisterInitialValues, Fill, AsmStream));return ExecutableFunction(createTargetMachine(),getObjectFromBuffer(AsmStream.str()));}const std::string TT;const std::string CpuName;const bool CanExecute;const ExegesisTarget *const ET;};} // namespace exegesis} // namespace llvm#endif
//===-- ClusteringTest.cpp --------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "Clustering.h"#include "BenchmarkResult.h"#include "llvm/Support/Error.h"#include "llvm/Support/raw_ostream.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace {using testing::Field;using testing::UnorderedElementsAre;using testing::UnorderedElementsAreArray;static const auto HasPoints = [](const std::vector<int> &Indices) {return Field(&InstructionBenchmarkClustering::Cluster::PointIndices,UnorderedElementsAreArray(Indices));};TEST(ClusteringTest, Clusters3D) {std::vector<InstructionBenchmark> Points(6);// Cluster around (x=0, y=1, z=2): points {0, 3}.Points[0].Measurements = {{"x", 0.01, 0.0}, {"y", 1.02, 0.0}, {"z", 1.98, 0.0}};Points[3].Measurements = {{"x", -0.01, 0.0}, {"y", 1.02, 0.0}, {"z", 1.98, 0.0}};// Cluster around (x=1, y=1, z=2): points {1, 4}.Points[1].Measurements = {{"x", 1.01, 0.0}, {"y", 1.02, 0.0}, {"z", 1.98, 0.0}};Points[4].Measurements = {{"x", 0.99, 0.0}, {"y", 1.02, 0.0}, {"z", 1.98, 0.0}};// Cluster around (x=0, y=0, z=0): points {5}, marked as noise.Points[5].Measurements = {{"x", 0.0, 0.0}, {"y", 0.01, 0.0}, {"z", -0.02, 0.0}};// Error cluster: points {2}Points[2].Error = "oops";auto Clustering = InstructionBenchmarkClustering::create(Points, InstructionBenchmarkClustering::ModeE::Dbscan, 2, 0.25);ASSERT_TRUE((bool)Clustering);EXPECT_THAT(Clustering.get().getValidClusters(),UnorderedElementsAre(HasPoints({0, 3}), HasPoints({1, 4})));EXPECT_THAT(Clustering.get().getCluster(InstructionBenchmarkClustering::ClusterId::noise()),HasPoints({5}));EXPECT_THAT(Clustering.get().getCluster(InstructionBenchmarkClustering::ClusterId::error()),HasPoints({2}));EXPECT_EQ(Clustering.get().getClusterIdForPoint(2),InstructionBenchmarkClustering::ClusterId::error());EXPECT_EQ(Clustering.get().getClusterIdForPoint(5),InstructionBenchmarkClustering::ClusterId::noise());EXPECT_EQ(Clustering.get().getClusterIdForPoint(0),Clustering.get().getClusterIdForPoint(3));EXPECT_EQ(Clustering.get().getClusterIdForPoint(1),Clustering.get().getClusterIdForPoint(4));}TEST(ClusteringTest, Clusters3D_InvalidSize) {std::vector<InstructionBenchmark> Points(6);Points[0].Measurements = {{"x", 0.01, 0.0}, {"y", 1.02, 0.0}, {"z", 1.98, 0.0}};Points[1].Measurements = {{"y", 1.02, 0.0}, {"z", 1.98, 0.0}};auto Error =InstructionBenchmarkClustering::create(Points, InstructionBenchmarkClustering::ModeE::Dbscan, 2, 0.25).takeError();ASSERT_TRUE((bool)Error);consumeError(std::move(Error));}TEST(ClusteringTest, Clusters3D_InvalidOrder) {std::vector<InstructionBenchmark> Points(6);Points[0].Measurements = {{"x", 0.01, 0.0}, {"y", 1.02, 0.0}};Points[1].Measurements = {{"y", 1.02, 0.0}, {"x", 1.98, 0.0}};auto Error =InstructionBenchmarkClustering::create(Points, InstructionBenchmarkClustering::ModeE::Dbscan, 2, 0.25).takeError();ASSERT_TRUE((bool)Error);consumeError(std::move(Error));}TEST(ClusteringTest, Ordering) {ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(1),InstructionBenchmarkClustering::ClusterId::makeValid(2));ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(2),InstructionBenchmarkClustering::ClusterId::noise());ASSERT_LT(InstructionBenchmarkClustering::ClusterId::makeValid(2),InstructionBenchmarkClustering::ClusterId::error());ASSERT_LT(InstructionBenchmarkClustering::ClusterId::noise(),InstructionBenchmarkClustering::ClusterId::error());}TEST(ClusteringTest, Ordering1) {std::vector<InstructionBenchmark> Points(3);Points[0].Measurements = {{"x", 0.0, 0.0}};Points[1].Measurements = {{"x", 1.0, 0.0}};Points[2].Measurements = {{"x", 2.0, 0.0}};auto Clustering = InstructionBenchmarkClustering::create(Points, InstructionBenchmarkClustering::ModeE::Dbscan, 2, 1.1);ASSERT_TRUE((bool)Clustering);EXPECT_THAT(Clustering.get().getValidClusters(),UnorderedElementsAre(HasPoints({0, 1, 2})));}TEST(ClusteringTest, Ordering2) {std::vector<InstructionBenchmark> Points(3);Points[0].Measurements = {{"x", 0.0, 0.0}};Points[1].Measurements = {{"x", 2.0, 0.0}};Points[2].Measurements = {{"x", 1.0, 0.0}};auto Clustering = InstructionBenchmarkClustering::create(Points, InstructionBenchmarkClustering::ModeE::Dbscan, 2, 1.1);ASSERT_TRUE((bool)Clustering);EXPECT_THAT(Clustering.get().getValidClusters(),UnorderedElementsAre(HasPoints({0, 1, 2})));}} // namespace} // namespace exegesis} // namespace llvm
set(exegesis_includes${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib)set(LLVM_LINK_COMPONENTSMCMCParserObjectSupportSymbolize)set(exegesis_sourcesBenchmarkRunnerTest.cppClusteringTest.cppPerfHelperTest.cppRegisterValueTest.cpp)set(exegesis_link_libraries LLVMExegesis)function(add_llvm_exegesis_unittest_includes)set(exegesis_includes ${exegesis_includes} ${ARGV} PARENT_SCOPE)endfunction()function(add_llvm_exegesis_unittest_sources)set(sources ${ARGV})list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_LIST_DIR}/")set(exegesis_sources ${exegesis_sources} ${sources} PARENT_SCOPE)endfunction()function(add_llvm_exegesis_unittest_link_components comps)set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} ${ARGV} PARENT_SCOPE)endfunction()function(add_llvm_exegesis_unittest_link_libraries libs)set(exegesis_link_libraries ${exegesis_link_libraries} ${ARGV} PARENT_SCOPE)endfunction()if(LLVM_TARGETS_TO_BUILD MATCHES "X86")include(X86/CMakeLists.txt)endif()if(LLVM_TARGETS_TO_BUILD MATCHES "ARM")include(ARM/CMakeLists.txt)endif()if(LLVM_TARGETS_TO_BUILD MATCHES "AArch64")include(AArch64/CMakeLists.txt)endif()if(LLVM_TARGETS_TO_BUILD MATCHES "PowerPC")include(PowerPC/CMakeLists.txt)endif()if(LLVM_TARGETS_TO_BUILD MATCHES "Mips")include(Mips/CMakeLists.txt)endif()include_directories(${exegesis_includes})list(REMOVE_DUPLICATES LLVM_LINK_COMPONENTS)add_llvm_target_unittest(LLVMExegesisTests${exegesis_sources})target_link_libraries(LLVMExegesisTests PRIVATE ${exegesis_link_libraries})set_property(TARGET LLVMExegesisTests PROPERTY FOLDER "Tests/UnitTests/ToolTests")
//===-- BenchmarkRunnerTest.cpp ---------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "BenchmarkRunner.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {namespace {TEST(ScratchSpaceTest, Works) {BenchmarkRunner::ScratchSpace Space;EXPECT_EQ(reinterpret_cast<intptr_t>(Space.ptr()) %BenchmarkRunner::ScratchSpace::kAlignment,0u);Space.ptr()[0] = 42;Space.ptr()[BenchmarkRunner::ScratchSpace::kSize - 1] = 43;Space.clear();EXPECT_EQ(Space.ptr()[0], 0);EXPECT_EQ(Space.ptr()[BenchmarkRunner::ScratchSpace::kSize - 1], 0);}} // namespace} // namespace exegesis} // namespace llvm
add_llvm_exegesis_unittest_includes(${LLVM_MAIN_SRC_DIR}/lib/Target/ARM${LLVM_BINARY_DIR}/lib/Target/ARM${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib)add_llvm_exegesis_unittest_link_components(CoreMCMCParserObjectSupportSymbolizeARM)add_llvm_exegesis_unittest_sources(AssemblerTest.cpp)
//===-- AssemblerTest.cpp ---------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "../Common/AssemblerUtils.h"#include "ARMInstrInfo.h"namespace llvm {namespace exegesis {namespace {class ARMMachineFunctionGeneratorTest: public MachineFunctionGeneratorBaseTest {protected:ARMMachineFunctionGeneratorTest(): MachineFunctionGeneratorBaseTest("armv7-none-linux-gnueabi", "") {}static void SetUpTestCase() {LLVMInitializeARMTargetInfo();LLVMInitializeARMTargetMC();LLVMInitializeARMTarget();LLVMInitializeARMAsmPrinter();}};TEST_F(ARMMachineFunctionGeneratorTest, DISABLED_JitFunction) {Check({}, MCInst(), 0x1e, 0xff, 0x2f, 0xe1);}TEST_F(ARMMachineFunctionGeneratorTest, DISABLED_JitFunctionADDrr) {Check({{ARM::R0, APInt()}},MCInstBuilder(ARM::ADDrr).addReg(ARM::R0).addReg(ARM::R0).addReg(ARM::R0).addImm(ARMCC::AL).addReg(0).addReg(0),0x00, 0x00, 0x80, 0xe0, 0x1e, 0xff, 0x2f, 0xe1);}} // namespace} // namespace exegesis} // namespace llvm
//===-- TargetTest.cpp ------------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "Target.h"#include <cassert>#include <memory>#include "MCTargetDesc/AArch64MCTargetDesc.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace exegesis {void InitializeAArch64ExegesisTarget();namespace {using testing::Gt;using testing::IsEmpty;using testing::Not;using testing::NotNull;constexpr const char kTriple[] = "aarch64-unknown-linux";class AArch64TargetTest : public ::testing::Test {protected:AArch64TargetTest(): ExegesisTarget_(ExegesisTarget::lookup(Triple(kTriple))) {EXPECT_THAT(ExegesisTarget_, NotNull());std::string error;Target_ = TargetRegistry::lookupTarget(kTriple, error);EXPECT_THAT(Target_, NotNull());STI_.reset(Target_->createMCSubtargetInfo(kTriple, "generic", /*no features*/ ""));}static void SetUpTestCase() {LLVMInitializeAArch64TargetInfo();LLVMInitializeAArch64Target();LLVMInitializeAArch64TargetMC();InitializeAArch64ExegesisTarget();}std::vector<MCInst> setRegTo(unsigned Reg, const APInt &Value) {return ExegesisTarget_->setRegTo(*STI_, Reg, Value);}const Target *Target_;const ExegesisTarget *const ExegesisTarget_;std::unique_ptr<MCSubtargetInfo> STI_;};TEST_F(AArch64TargetTest, SetRegToConstant) {// The AArch64 target currently doesn't know how to set register values.const auto Insts = setRegTo(AArch64::X0, APInt());EXPECT_THAT(Insts, Not(IsEmpty()));}TEST_F(AArch64TargetTest, DefaultPfmCounters) {const std::string Expected = "CPU_CYCLES";EXPECT_EQ(ExegesisTarget_->getPfmCounters("").CycleCounter, Expected);EXPECT_EQ(ExegesisTarget_->getPfmCounters("unknown_cpu").CycleCounter,Expected);}} // namespace} // namespace exegesis} // namespace llvm
add_llvm_exegesis_unittest_includes(${LLVM_MAIN_SRC_DIR}/lib/Target/AArch64${LLVM_BINARY_DIR}/lib/Target/AArch64${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib)add_llvm_exegesis_unittest_link_components(MCMCParserObjectSupportSymbolizeAArch64)add_llvm_exegesis_unittest_sources(TargetTest.cpp)add_llvm_exegesis_unittest_link_libraries(LLVMExegesisAArch64)
//===- llvm/unittests/llvm-cfi-verify/GraphBuilder.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "../tools/llvm-cfi-verify/lib/GraphBuilder.h"#include "../tools/llvm-cfi-verify/lib/FileAnalysis.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include "llvm/BinaryFormat/ELF.h"#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCDisassembler/MCDisassembler.h"#include "llvm/MC/MCInst.h"#include "llvm/MC/MCInstPrinter.h"#include "llvm/MC/MCInstrAnalysis.h"#include "llvm/MC/MCInstrDesc.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCObjectFileInfo.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Object/Binary.h"#include "llvm/Object/COFF.h"#include "llvm/Object/ELFObjectFile.h"#include "llvm/Object/ObjectFile.h"#include "llvm/Support/Casting.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/Error.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/raw_ostream.h"#include <cstdlib>#include <sstream>using Instr = ::llvm::cfi_verify::FileAnalysis::Instr;using ::testing::AllOf;using ::testing::Each;using ::testing::ElementsAre;using ::testing::Eq;using ::testing::Field;using ::testing::IsEmpty;using ::testing::Matches;using ::testing::Pair;using ::testing::PrintToString;using ::testing::Property;using ::testing::SizeIs;using ::testing::UnorderedElementsAre;using ::testing::Value;namespace llvm {namespace cfi_verify {// Printing helpers for gtest.std::string HexStringifyContainer(const std::vector<uint64_t> &C) {std::stringstream Stream;if (C.empty()) {return "{ }";}Stream << "{ ";const auto &LastElemIt = std::end(C) - 1;for (auto It = std::begin(C); It != LastElemIt; ++It) {Stream << "0x" << std::hex << *It << ", ";}Stream << "0x" << std::hex << *LastElemIt << " }";return Stream.str();}void PrintTo(const ConditionalBranchNode &BranchNode, ::std::ostream *os) {*os << "ConditionalBranchNode<Address: 0x" << std::hex << BranchNode.Address<< ", Target: 0x" << BranchNode.Target << ", Fallthrough: 0x"<< BranchNode.Fallthrough<< ", CFIProtection: " << BranchNode.CFIProtection << ">";}void PrintTo(const GraphResult &Result, ::std::ostream *os) {*os << "Result BaseAddress: 0x" << std::hex << Result.BaseAddress << "\n";if (Result.ConditionalBranchNodes.empty())*os << " (No conditional branch nodes)\n";for (const auto &Node : Result.ConditionalBranchNodes) {*os << " ";PrintTo(Node, os);*os << "\n Fallthrough Path: " << std::hex<< HexStringifyContainer(Result.flattenAddress(Node.Fallthrough))<< "\n";*os << " Target Path: " << std::hex<< HexStringifyContainer(Result.flattenAddress(Node.Target)) << "\n";}if (Result.OrphanedNodes.empty())*os << " (No orphaned nodes)";for (const auto &Orphan : Result.OrphanedNodes) {*os << " Orphan (0x" << std::hex << Orphan<< ") Path: " << HexStringifyContainer(Result.flattenAddress(Orphan))<< "\n";}}namespace {class ELFx86TestFileAnalysis : public FileAnalysis {public:ELFx86TestFileAnalysis(): FileAnalysis(Triple("x86_64--"), SubtargetFeatures()) {}// Expose this method publicly for testing.void parseSectionContents(ArrayRef<uint8_t> SectionBytes,object::SectionedAddress Address) {FileAnalysis::parseSectionContents(SectionBytes, Address);}Error initialiseDisassemblyMembers() {return FileAnalysis::initialiseDisassemblyMembers();}};class BasicGraphBuilderTest : public ::testing::Test {protected:void SetUp() override {IgnoreDWARFFlag = true;SuccessfullyInitialised = true;if (auto Err = Analysis.initialiseDisassemblyMembers()) {handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) {SuccessfullyInitialised = false;outs()<< "Note: CFIVerifyTests are disabled due to lack of x86 support ""on this build.\n";});}}bool SuccessfullyInitialised;ELFx86TestFileAnalysis Analysis;};MATCHER_P2(HasPath, Result, Matcher, "has path " + PrintToString(Matcher)) {const auto &Path = Result.flattenAddress(arg);*result_listener << "the path is " << PrintToString(Path);return Matches(Matcher)(Path);}TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathFallthroughUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0x0f, 0x0b, // 2: ud20xff, 0x10, // 4: callq *(%rax)},{0xDEADBEEF, 0x0});const auto Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));EXPECT_THAT(Result.ConditionalBranchNodes,Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 4))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))<< PrintToString(Result);}TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathJumpUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0xff, 0x10, // 2: callq *(%rax)0x0f, 0x0b, // 4: ud2},{0xDEADBEEF, 0x0});const auto Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));EXPECT_THAT(Result.ConditionalBranchNodes,Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 4))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))<< PrintToString(Result);}TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathDualUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x03, // 0: jne 5 [+3]0x90, // 2: nop0xff, 0x10, // 3: callq *(%rax)0x0f, 0x0b, // 5: ud20x75, 0xf9, // 7: jne 2 [-7]0x0f, 0x0b, // 9: ud2},{0xDEADBEEF, 0x0});const auto Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));EXPECT_THAT(Result.ConditionalBranchNodes,Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 5))))))<< PrintToString(Result);EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 7)),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 9))),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))<< PrintToString(Result);}TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathSingleUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x05, // 0: jne 7 [+5]0x90, // 2: nop0xff, 0x10, // 3: callq *(%rax)0x75, 0xfb, // 5: jne 2 [-5]0x0f, 0x0b, // 7: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));EXPECT_THAT(Result.ConditionalBranchNodes,Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))<< PrintToString(Result);EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 7))),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))<< PrintToString(Result);}TEST_F(BasicGraphBuilderTest, BuildFlowGraphFailures) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0x75, 0xfe, // 1: jne 1 [-2]},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 1, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADC0DE, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());}TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoXrefs) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0xeb, 0xfe, // 0: jmp 0 [-2]0xff, 0x10, // 2: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 2));EXPECT_THAT(Result.IntermediateNodes, IsEmpty());}TEST_F(BasicGraphBuilderTest, BuildFlowGraphConditionalInfiniteLoop) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0xfe, // 0: jne 0 [-2]0xff, 0x10, // 2: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));EXPECT_THAT(Result.ConditionalBranchNodes,Each(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))<< PrintToString(Result);}TEST_F(BasicGraphBuilderTest, BuildFlowGraphUnconditionalInfiniteLoop) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0xeb, 0xfc, // 2: jmp 0 [-4]0xff, 0x10, // 4: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF))),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 4))))))<< PrintToString(Result);}TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoFlowsToIndirection) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x00, // 0: jne 2 [+0]0xeb, 0xfc, // 2: jmp 0 [-4]0xff, 0x10, // 4: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 4));EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());}TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededUpwards) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x06, // 0: jne 8 [+6]0x90, // 2: nop0x90, // 3: nop0x90, // 4: nop0x90, // 5: nop0xff, 0x10, // 6: callq *(%rax)0x0f, 0x0b, // 8: ud2},{0xDEADBEEF, 0x0});uint64_t PrevSearchLengthForConditionalBranch =SearchLengthForConditionalBranch;SearchLengthForConditionalBranch = 2;GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 6, 0x0});EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));EXPECT_THAT(Result.OrphanedNodes,Each(HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5,0xDEADBEEF + 6))))<< PrintToString(Result);EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;}TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededDownwards) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0xff, 0x10, // 2: callq *(%rax)0x90, // 4: nop0x90, // 5: nop0x90, // 6: nop0x90, // 7: nop0x0f, 0x0b, // 8: ud2},{0xDEADBEEF, 0x0});uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;SearchLengthForUndef = 2;GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes,Each(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))<< PrintToString(Result);SearchLengthForUndef = PrevSearchLengthForUndef;}// This test ensures when avoiding doing repeated work we still generate the// paths correctly. We don't need to recalculate the flow from 0x2 -> 0x3 as it// should only need to be generated once.TEST_F(BasicGraphBuilderTest, BuildFlowGraphWithRepeatedWork) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x05, // 0: jne 7 [+5]0x90, // 2: nop0xff, 0x10, // 3: callq *(%rax)0x75, 0xfb, // 5: jne 2 [-5]0x0f, 0x0b, // 7: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});EXPECT_THAT(Result.OrphanedNodes, IsEmpty());EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(true)),Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 7))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))<< PrintToString(Result);EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(true)),Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))<< PrintToString(Result);EXPECT_THAT(Result.IntermediateNodes, SizeIs(1));EXPECT_THAT(Result.IntermediateNodes,UnorderedElementsAre(Pair(0xDEADBEEF + 2, 0xDEADBEEF + 3)));}TEST_F(BasicGraphBuilderTest, BuildFlowGraphComplexExample) {if (!SuccessfullyInitialised)return;// The following code has this graph:// +----------+ +--------------+// | 20 | <--- | 0 |// +----------+ +--------------+// | |// v v// +----------+ +--------------+// | 21 | | 2 |// +----------+ +--------------+// | |// v v// +----------+ +--------------+// | 22 (ud2) | +-> | 7 |// +----------+ | +--------------+// ^ | |// | | v// +----------+ | +--------------+// | 4 | | | 8 |// +----------+ | +--------------+// | | |// v | v// +----------+ | +--------------+ +------------+// | 6 | -+ | 9 (indirect) | <- | 13 |// +----------+ +--------------+ +------------+// ^ |// | v// +--------------+ +------------+// | 11 | | 15 (error) |// +--------------+ +------------+// Or, in image format: https://i.imgur.com/aX5fCoi.pngAnalysis.parseSectionContents({0x75, 0x12, // 0: jne 20 [+18]0xeb, 0x03, // 2: jmp 7 [+3]0x75, 0x10, // 4: jne 22 [+16]0x90, // 6: nop0x90, // 7: nop0x90, // 8: nop0xff, 0x10, // 9: callq *(%rax)0xeb, 0xfc, // 11: jmp 9 [-4]0x75, 0xfa, // 13: jne 9 [-6]0xe8, 0x78, 0x56, 0x34, 0x12, // 15: callq OUTOFBOUNDS [+0x12345678]0x90, // 20: nop0x90, // 21: nop0x0f, 0x0b, // 22: ud2},{0x1000, 0x0});uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;SearchLengthForUndef = 5;GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0x1000 + 9, 0x0});EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(3));EXPECT_THAT(Result.OrphanedNodes,Each(AllOf(Eq(0x1000u + 11),HasPath(Result, ElementsAre(0x1000 + 11, 0x1000 + 9)))))<< PrintToString(Result);EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(true)),Field(&ConditionalBranchNode::Address, Eq(0x1000u)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0x1000 + 20, 0x1000 + 21,0x1000 + 22))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0x1000 + 2, 0x1000 + 7,0x1000 + 8, 0x1000 + 9))))))<< PrintToString(Result);EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(true)),Field(&ConditionalBranchNode::Address, Eq(0x1000u + 4)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0x1000 + 22))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0x1000 + 6, 0x1000 + 7,0x1000 + 8, 0x1000 + 9))))))<< PrintToString(Result);EXPECT_THAT(Result.ConditionalBranchNodes,Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),Field(&ConditionalBranchNode::Address, Eq(0x1000u + 13)),Field(&ConditionalBranchNode::Target,HasPath(Result, ElementsAre(0x1000 + 9))),Field(&ConditionalBranchNode::Fallthrough,HasPath(Result, ElementsAre(0x1000 + 15))))))<< PrintToString(Result);SearchLengthForUndef = PrevSearchLengthForUndef;}} // anonymous namespace} // end namespace cfi_verify} // end namespace llvm
//===- llvm/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "../tools/llvm-cfi-verify/lib/FileAnalysis.h"#include "../tools/llvm-cfi-verify/lib/GraphBuilder.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include "llvm/BinaryFormat/ELF.h"#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCDisassembler/MCDisassembler.h"#include "llvm/MC/MCInst.h"#include "llvm/MC/MCInstPrinter.h"#include "llvm/MC/MCInstrAnalysis.h"#include "llvm/MC/MCInstrDesc.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCObjectFileInfo.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Object/Binary.h"#include "llvm/Object/COFF.h"#include "llvm/Object/ELFObjectFile.h"#include "llvm/Object/ObjectFile.h"#include "llvm/Support/Casting.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/Error.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/raw_ostream.h"#include <cstdlib>using Instr = ::llvm::cfi_verify::FileAnalysis::Instr;using ::testing::Eq;using ::testing::Field;namespace llvm {namespace cfi_verify {namespace {class ELFTestFileAnalysis : public FileAnalysis {public:ELFTestFileAnalysis(StringRef Trip): FileAnalysis(Triple(Trip), SubtargetFeatures()) {}// Expose this method publicly for testing.void parseSectionContents(ArrayRef<uint8_t> SectionBytes,object::SectionedAddress Address) {FileAnalysis::parseSectionContents(SectionBytes, Address);}Error initialiseDisassemblyMembers() {return FileAnalysis::initialiseDisassemblyMembers();}};class BasicFileAnalysisTest : public ::testing::Test {public:BasicFileAnalysisTest(StringRef Trip): SuccessfullyInitialised(false), Analysis(Trip) {}protected:void SetUp() override {IgnoreDWARFFlag = true;SuccessfullyInitialised = true;if (auto Err = Analysis.initialiseDisassemblyMembers()) {handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) {SuccessfullyInitialised = false;outs()<< "Note: CFIVerifyTests are disabled due to lack of support ""on this build.\n";});}}bool SuccessfullyInitialised;ELFTestFileAnalysis Analysis;};class BasicX86FileAnalysisTest : public BasicFileAnalysisTest {public:BasicX86FileAnalysisTest() : BasicFileAnalysisTest("x86_64--") {}};class BasicAArch64FileAnalysisTest : public BasicFileAnalysisTest {public:BasicAArch64FileAnalysisTest() : BasicFileAnalysisTest("aarch64--") {}};TEST_F(BasicX86FileAnalysisTest, BasicDisassemblyTraversalTest) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0xb0, 0x00, // 1: mov $0x0, %al0x48, 0x89, 0xe5, // 3: mov %rsp, %rbp0x48, 0x83, 0xec, 0x18, // 6: sub $0x18, %rsp0x48, 0xbe, 0xc4, 0x07, 0x40,0x00, 0x00, 0x00, 0x00, 0x00, // 10: movabs $0x4007c4, %rsi0x2f, // 20: (bad)0x41, 0x0e, // 21: rex.B (bad)0x62, 0x72, 0x65, 0x61, 0x6b, // 23: (bad) {%k1}},{0xDEADBEEF, 0x0});EXPECT_EQ(nullptr, Analysis.getInstruction(0x0));EXPECT_EQ(nullptr, Analysis.getInstruction(0x1000));// 0xDEADBEEF: nopconst auto *InstrMeta = Analysis.getInstruction(0xDEADBEEF);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(0xDEADBEEF, InstrMeta->VMAddress);EXPECT_EQ(1u, InstrMeta->InstructionSize);EXPECT_TRUE(InstrMeta->Valid);const auto *NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));const auto *PrevInstrMeta = InstrMeta;// 0xDEADBEEF + 1: mov $0x0, %alInstrMeta = Analysis.getInstruction(0xDEADBEEF + 1);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(NextInstrMeta, InstrMeta);EXPECT_EQ(0xDEADBEEF + 1, InstrMeta->VMAddress);EXPECT_EQ(2u, InstrMeta->InstructionSize);EXPECT_TRUE(InstrMeta->Valid);NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));PrevInstrMeta = InstrMeta;// 0xDEADBEEF + 3: mov %rsp, %rbpInstrMeta = Analysis.getInstruction(0xDEADBEEF + 3);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(NextInstrMeta, InstrMeta);EXPECT_EQ(0xDEADBEEF + 3, InstrMeta->VMAddress);EXPECT_EQ(3u, InstrMeta->InstructionSize);EXPECT_TRUE(InstrMeta->Valid);NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));PrevInstrMeta = InstrMeta;// 0xDEADBEEF + 6: sub $0x18, %rspInstrMeta = Analysis.getInstruction(0xDEADBEEF + 6);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(NextInstrMeta, InstrMeta);EXPECT_EQ(0xDEADBEEF + 6, InstrMeta->VMAddress);EXPECT_EQ(4u, InstrMeta->InstructionSize);EXPECT_TRUE(InstrMeta->Valid);NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));PrevInstrMeta = InstrMeta;// 0xDEADBEEF + 10: movabs $0x4007c4, %rsiInstrMeta = Analysis.getInstruction(0xDEADBEEF + 10);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(NextInstrMeta, InstrMeta);EXPECT_EQ(0xDEADBEEF + 10, InstrMeta->VMAddress);EXPECT_EQ(10u, InstrMeta->InstructionSize);EXPECT_TRUE(InstrMeta->Valid);EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));PrevInstrMeta = InstrMeta;// 0xDEADBEEF + 20: (bad)InstrMeta = Analysis.getInstruction(0xDEADBEEF + 20);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(0xDEADBEEF + 20, InstrMeta->VMAddress);EXPECT_EQ(1u, InstrMeta->InstructionSize);EXPECT_FALSE(InstrMeta->Valid);EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));// 0xDEADBEEF + 21: rex.B (bad)InstrMeta = Analysis.getInstruction(0xDEADBEEF + 21);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(0xDEADBEEF + 21, InstrMeta->VMAddress);EXPECT_EQ(2u, InstrMeta->InstructionSize);EXPECT_FALSE(InstrMeta->Valid);EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));// 0xDEADBEEF + 6: (bad) {%k1}InstrMeta = Analysis.getInstruction(0xDEADBEEF + 23);EXPECT_NE(nullptr, InstrMeta);EXPECT_EQ(0xDEADBEEF + 23, InstrMeta->VMAddress);EXPECT_EQ(5u, InstrMeta->InstructionSize);EXPECT_FALSE(InstrMeta->Valid);EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));}TEST_F(BasicX86FileAnalysisTest, PrevAndNextFromBadInst) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0x2f, // 1: (bad)0x90 // 2: nop},{0xDEADBEEF, 0x0});const auto &BadInstrMeta = Analysis.getInstructionOrDie(0xDEADBEEF + 1);const auto *GoodInstrMeta =Analysis.getPrevInstructionSequential(BadInstrMeta);EXPECT_NE(nullptr, GoodInstrMeta);EXPECT_EQ(0xDEADBEEF, GoodInstrMeta->VMAddress);EXPECT_EQ(1u, GoodInstrMeta->InstructionSize);GoodInstrMeta = Analysis.getNextInstructionSequential(BadInstrMeta);EXPECT_NE(nullptr, GoodInstrMeta);EXPECT_EQ(0xDEADBEEF + 2, GoodInstrMeta->VMAddress);EXPECT_EQ(1u, GoodInstrMeta->InstructionSize);}TEST_F(BasicX86FileAnalysisTest, CFITrapTest) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0xb0, 0x00, // 1: mov $0x0, %al0x48, 0x89, 0xe5, // 3: mov %rsp, %rbp0x48, 0x83, 0xec, 0x18, // 6: sub $0x18, %rsp0x48, 0xbe, 0xc4, 0x07, 0x40,0x00, 0x00, 0x00, 0x00, 0x00, // 10: movabs $0x4007c4, %rsi0x2f, // 20: (bad)0x41, 0x0e, // 21: rex.B (bad)0x62, 0x72, 0x65, 0x61, 0x6b, // 23: (bad) {%k1}0x0f, 0x0b // 28: ud2},{0xDEADBEEF, 0x0});EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF)));EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 3)));EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 6)));EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 10)));EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 20)));EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 21)));EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 23)));EXPECT_TRUE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 28)));}TEST_F(BasicX86FileAnalysisTest, FallThroughTest) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0xb0, 0x00, // 1: mov $0x0, %al0x2f, // 3: (bad)0x0f, 0x0b, // 4: ud20xff, 0x20, // 6: jmpq *(%rax)0xeb, 0x00, // 8: jmp +00xe8, 0x45, 0xfe, 0xff, 0xff, // 10: callq [some loc]0xff, 0x10, // 15: callq *(rax)0x75, 0x00, // 17: jne +00xc3, // 19: retq},{0xDEADBEEF, 0x0});EXPECT_TRUE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF)));EXPECT_TRUE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 1)));EXPECT_FALSE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 3)));EXPECT_FALSE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 4)));EXPECT_FALSE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 6)));EXPECT_FALSE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 8)));EXPECT_FALSE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 10)));EXPECT_FALSE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 15)));EXPECT_TRUE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 17)));EXPECT_FALSE(Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 19)));}TEST_F(BasicX86FileAnalysisTest, DefiniteNextInstructionTest) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0xb0, 0x00, // 1: mov $0x0, %al0x2f, // 3: (bad)0x0f, 0x0b, // 4: ud20xff, 0x20, // 6: jmpq *(%rax)0xeb, 0x00, // 8: jmp 10 [+0]0xeb, 0x05, // 10: jmp 17 [+5]0xe8, 0x00, 0x00, 0x00, 0x00, // 12: callq 17 [+0]0xe8, 0x78, 0x56, 0x34, 0x12, // 17: callq 0x1234569f [+0x12345678]0xe8, 0x04, 0x00, 0x00, 0x00, // 22: callq 31 [+4]0xff, 0x10, // 27: callq *(rax)0x75, 0x00, // 29: jne 31 [+0]0x75, 0xe0, // 31: jne 1 [-32]0xc3, // 33: retq0xeb, 0xdd, // 34: jmp 1 [-35]0xeb, 0xdd, // 36: jmp 3 [-35]0xeb, 0xdc, // 38: jmp 4 [-36]},{0xDEADBEEF, 0x0});const auto *Current = Analysis.getInstruction(0xDEADBEEF);const auto *Next = Analysis.getDefiniteNextInstruction(*Current);EXPECT_NE(nullptr, Next);EXPECT_EQ(0xDEADBEEF + 1, Next->VMAddress);Current = Analysis.getInstruction(0xDEADBEEF + 1);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 3);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 4);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 6);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 8);Next = Analysis.getDefiniteNextInstruction(*Current);EXPECT_NE(nullptr, Next);EXPECT_EQ(0xDEADBEEF + 10, Next->VMAddress);Current = Analysis.getInstruction(0xDEADBEEF + 10);Next = Analysis.getDefiniteNextInstruction(*Current);EXPECT_NE(nullptr, Next);EXPECT_EQ(0xDEADBEEF + 17, Next->VMAddress);Current = Analysis.getInstruction(0xDEADBEEF + 12);Next = Analysis.getDefiniteNextInstruction(*Current);EXPECT_NE(nullptr, Next);EXPECT_EQ(0xDEADBEEF + 17, Next->VMAddress);Current = Analysis.getInstruction(0xDEADBEEF + 17);// Note, definite next instruction address is out of range and should fail.EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Next = Analysis.getDefiniteNextInstruction(*Current);Current = Analysis.getInstruction(0xDEADBEEF + 22);Next = Analysis.getDefiniteNextInstruction(*Current);EXPECT_NE(nullptr, Next);EXPECT_EQ(0xDEADBEEF + 31, Next->VMAddress);Current = Analysis.getInstruction(0xDEADBEEF + 27);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 29);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 31);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 33);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 34);Next = Analysis.getDefiniteNextInstruction(*Current);EXPECT_NE(nullptr, Next);EXPECT_EQ(0xDEADBEEF + 1, Next->VMAddress);Current = Analysis.getInstruction(0xDEADBEEF + 36);EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));Current = Analysis.getInstruction(0xDEADBEEF + 38);Next = Analysis.getDefiniteNextInstruction(*Current);EXPECT_NE(nullptr, Next);EXPECT_EQ(0xDEADBEEF + 4, Next->VMAddress);}TEST_F(BasicX86FileAnalysisTest, ControlFlowXRefsTest) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0xb0, 0x00, // 1: mov $0x0, %al0x2f, // 3: (bad)0x0f, 0x0b, // 4: ud20xff, 0x20, // 6: jmpq *(%rax)0xeb, 0x00, // 8: jmp 10 [+0]0xeb, 0x05, // 10: jmp 17 [+5]0xe8, 0x00, 0x00, 0x00, 0x00, // 12: callq 17 [+0]0xe8, 0x78, 0x56, 0x34, 0x12, // 17: callq 0x1234569f [+0x12345678]0xe8, 0x04, 0x00, 0x00, 0x00, // 22: callq 31 [+4]0xff, 0x10, // 27: callq *(rax)0x75, 0x00, // 29: jne 31 [+0]0x75, 0xe0, // 31: jne 1 [-32]0xc3, // 33: retq0xeb, 0xdd, // 34: jmp 1 [-35]0xeb, 0xdd, // 36: jmp 3 [-35]0xeb, 0xdc, // 38: jmp 4 [-36]},{0xDEADBEEF, 0x0});const auto *InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF);std::set<const Instr *> XRefs =Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(XRefs.empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 1);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_THAT(XRefs, UnorderedElementsAre(Field(&Instr::VMAddress, Eq(0xDEADBEEF)),Field(&Instr::VMAddress, Eq(0xDEADBEEF + 31)),Field(&Instr::VMAddress, Eq(0xDEADBEEF + 34))));InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 3);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_THAT(XRefs, UnorderedElementsAre(Field(&Instr::VMAddress, Eq(0xDEADBEEF + 1)),Field(&Instr::VMAddress, Eq(0xDEADBEEF + 36))));InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 4);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_THAT(XRefs, UnorderedElementsAre(Field(&Instr::VMAddress, Eq(0xDEADBEEF + 38))));InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 6);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 8);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 10);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_THAT(XRefs, UnorderedElementsAre(Field(&Instr::VMAddress, Eq(0xDEADBEEF + 8))));InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 12);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 17);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_THAT(XRefs, UnorderedElementsAre(Field(&Instr::VMAddress, Eq(0xDEADBEEF + 10)),Field(&Instr::VMAddress, Eq(0xDEADBEEF + 12))));InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 22);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 27);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 29);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 31);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_THAT(XRefs, UnorderedElementsAre(Field(&Instr::VMAddress, Eq(0xDEADBEEF + 22)),Field(&Instr::VMAddress, Eq(0xDEADBEEF + 29))));InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 33);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_THAT(XRefs, UnorderedElementsAre(Field(&Instr::VMAddress, Eq(0xDEADBEEF + 31))));InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 34);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 36);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 38);XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());}TEST_F(BasicX86FileAnalysisTest, CFIProtectionInvalidTargets) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x90, // 0: nop0x0f, 0x0b, // 1: ud20x75, 0x00, // 3: jne 5 [+0]},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_NOT_INDIRECT_CF,Analysis.validateCFIProtection(Result));Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 1, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_NOT_INDIRECT_CF,Analysis.validateCFIProtection(Result));Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_NOT_INDIRECT_CF,Analysis.validateCFIProtection(Result));Result = GraphBuilder::buildFlowGraph(Analysis, {0x12345678, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_INVALID_INSTRUCTION,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0x0f, 0x0b, // 2: ud20xff, 0x10, // 4: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicJumpToUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0xff, 0x10, // 2: callq *(%rax)0x0f, 0x0b, // 4: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x03, // 0: jne 5 [+3]0x90, // 2: nop0xff, 0x10, // 3: callq *(%rax)0x0f, 0x0b, // 5: ud20x75, 0xf9, // 7: jne 2 [-7]0x0f, 0x0b, // 9: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathSingleUd2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x05, // 0: jne 7 [+5]0x90, // 2: nop0xff, 0x10, // 3: callq *(%rax)0x75, 0xfb, // 5: jne 2 [-5]0x0f, 0x0b, // 7: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitUpwards) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x06, // 0: jne 8 [+6]0x90, // 2: nop0x90, // 3: nop0x90, // 4: nop0x90, // 5: nop0xff, 0x10, // 6: callq *(%rax)0x0f, 0x0b, // 8: ud2},{0xDEADBEEF, 0x0});uint64_t PrevSearchLengthForConditionalBranch =SearchLengthForConditionalBranch;SearchLengthForConditionalBranch = 2;GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 6, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,Analysis.validateCFIProtection(Result));SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;}TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitDownwards) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0xff, 0x10, // 2: callq *(%rax)0x90, // 4: nop0x90, // 5: nop0x90, // 6: nop0x90, // 7: nop0x0f, 0x0b, // 8: ud2},{0xDEADBEEF, 0x0});uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;SearchLengthForUndef = 2;GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH,Analysis.validateCFIProtection(Result));SearchLengthForUndef = PrevSearchLengthForUndef;}TEST_F(BasicX86FileAnalysisTest, CFIProtectionGoodAndBadPaths) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0xeb, 0x02, // 0: jmp 4 [+2]0x75, 0x02, // 2: jne 6 [+2]0xff, 0x10, // 4: callq *(%rax)0x0f, 0x0b, // 6: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x04, // 0: jne 6 [+4]0xeb, 0x00, // 2: jmp 4 [+0]0xff, 0x10, // 4: callq *(%rax)0x0f, 0x0b, // 6: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionComplexExample) {if (!SuccessfullyInitialised)return;// See unittests/GraphBuilder.cpp::BuildFlowGraphComplexExample for this// graph.Analysis.parseSectionContents({0x75, 0x12, // 0: jne 20 [+18]0xeb, 0x03, // 2: jmp 7 [+3]0x75, 0x10, // 4: jne 22 [+16]0x90, // 6: nop0x90, // 7: nop0x90, // 8: nop0xff, 0x10, // 9: callq *(%rax)0xeb, 0xfc, // 11: jmp 9 [-4]0x75, 0xfa, // 13: jne 9 [-6]0xe8, 0x78, 0x56, 0x34, 0x12, // 15: callq OUTOFBOUNDS [+0x12345678]0x90, // 20: nop0x90, // 21: nop0x0f, 0x0b, // 22: ud2},{0xDEADBEEF, 0x0});uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;SearchLengthForUndef = 5;GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 9, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,Analysis.validateCFIProtection(Result));SearchLengthForUndef = PrevSearchLengthForUndef;}TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTest) {Analysis.parseSectionContents({0x77, 0x0d, // 0x688118: ja 0x688127 [+12]0x48, 0x89, 0xdf, // 0x68811a: mov %rbx, %rdi0xff, 0xd0, // 0x68811d: callq *%rax0x48, 0x89, 0xdf, // 0x68811f: mov %rbx, %rdi0xe8, 0x09, 0x00, 0x00, 0x00, // 0x688122: callq 0x6881300x0f, 0x0b, // 0x688127: ud2},{0x688118, 0x0});uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;SearchLengthForUndef = 1;GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, {0x68811d, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));SearchLengthForUndef = PrevSearchLengthForUndef;}TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTestFarAway) {Analysis.parseSectionContents({0x74, 0x73, // 0x7759eb: je 0x775a600xe9, 0x1c, 0x04, 0x00, 0x00, 0x00, // 0x7759ed: jmpq 0x775e0e},{0x7759eb, 0x0});Analysis.parseSectionContents({0x0f, 0x85, 0xb2, 0x03, 0x00, 0x00, // 0x775a56: jne 0x775e0e0x48, 0x83, 0xc3, 0xf4, // 0x775a5c: add $0xfffffffffffffff4,%rbx0x48, 0x8b, 0x7c, 0x24, 0x10, // 0x775a60: mov 0x10(%rsp),%rdi0x48, 0x89, 0xde, // 0x775a65: mov %rbx,%rsi0xff, 0xd1, // 0x775a68: callq *%rcx},{0x775a56, 0x0});Analysis.parseSectionContents({0x0f, 0x0b, // 0x775e0e: ud2},{0x775e0e, 0x0});uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;SearchLengthForUndef = 1;GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, {0x775a68, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH,Analysis.validateCFIProtection(Result));SearchLengthForUndef = 2;Result = GraphBuilder::buildFlowGraph(Analysis, {0x775a68, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));SearchLengthForUndef = 3;Result = GraphBuilder::buildFlowGraph(Analysis, {0x775a68, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));SearchLengthForUndef = PrevSearchLengthForUndef;}TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0x0f, 0x0b, // 2: ud20x48, 0x05, 0x00, 0x00, 0x00, 0x00, // 4: add $0x0, %rax0xff, 0x10, // 10: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 10, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0x0f, 0x0b, // 2: ud20x48, 0x83, 0xc0, 0x00, // 4: add $0x0, %rax0xff, 0x10, // 8: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 8, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x02, // 0: jne 4 [+2]0x0f, 0x0b, // 2: ud20x05, 0x00, 0x00, 0x00, 0x00, // 4: add $0x0, %eax0xff, 0x10, // 9: callq *(%rax)},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 9, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberDualPathImplicit) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x75, 0x04, // 0: jne 6 [+4]0x0f, 0x31, // 2: rdtsc (note: affects eax)0xff, 0x10, // 4: callq *(%rax)0x0f, 0x0b, // 6: ud20x75, 0xf9, // 8: jne 2 [-7]0x0f, 0x0b, // 10: ud2},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicUnprotected) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x00, 0x01, 0x3f, 0xd6, // 0: blr x8},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicProtected) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x00, 0x01, 0x3f, 0xd6, // 8: blr x8},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 8, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberBasic) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x08, 0x05, 0x00, 0x91, // 8: add x8, x8, #10x00, 0x01, 0x3f, 0xd6, // 12: blr x8},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 12, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberOneLoad) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]0x20, 0x00, 0x1f, 0xd6, // 12: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 12, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddGood) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x21, 0x04, 0x00, 0x91, // 8: add x1, x1, #10x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]0x20, 0x00, 0x1f, 0xd6, // 16: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]0x21, 0x04, 0x00, 0x91, // 12: add x1, x1, #10x20, 0x00, 0x1f, 0xd6, // 16: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x29, 0x04, 0x00, 0x91, // 16: add x9, x1, #10x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]0x20, 0x00, 0x1f, 0xd6, // 16: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberTwoLoads) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]0x21, 0x08, 0x40, 0xf9, // 12: ldr x1, [x1,#16]0x20, 0x00, 0x1f, 0xd6, // 16: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedSecondLoad) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]0x20, 0x00, 0x1f, 0xd6, // 16: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedLoads) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x49, 0x00, 0x00, 0x54, // 0: b.ls 80x20, 0x00, 0x20, 0xd4, // 4: brk #0x10x22, 0x09, 0x40, 0xf9, // 8: ldr x2, [x9,#16]0x22, 0x08, 0x40, 0xf9, // 12: ldr x2, [x1,#16]0x20, 0x00, 0x1f, 0xd6, // 16: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64GoodAndBadPaths) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0x03, 0x00, 0x00, 0x14, // 0: b 120x49, 0x00, 0x00, 0x54, // 4: b.ls 80x20, 0x00, 0x20, 0xd4, // 8: brk #0x10x20, 0x00, 0x1f, 0xd6, // 12: br x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 12, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPaths) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0xc9, 0x00, 0x00, 0x54, // 0: b.ls 240x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]0x03, 0x00, 0x00, 0x14, // 8: b 120x69, 0x00, 0x00, 0x54, // 12: b.ls 120x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16]0x20, 0x00, 0x1f, 0xd6, // 20: br x10x20, 0x00, 0x20, 0xd4, // 24: brk #0x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 20, 0x0});EXPECT_EQ(CFIProtectionStatus::PROTECTED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad1) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0xe9, 0x00, 0x00, 0x54, // 0: b.ls 280x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]0x21, 0x08, 0x40, 0xf9, // 8: ldr x1, [x1,#16]0x03, 0x00, 0x00, 0x14, // 12: b 120x69, 0x00, 0x00, 0x54, // 16: b.ls 120x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16]0x20, 0x00, 0x1f, 0xd6, // 24: br x10x20, 0x00, 0x20, 0xd4, // 28: brk #0x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 24, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad2) {if (!SuccessfullyInitialised)return;Analysis.parseSectionContents({0xe9, 0x00, 0x00, 0x54, // 0: b.ls 280x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]0x03, 0x00, 0x00, 0x14, // 8: b 120x89, 0x00, 0x00, 0x54, // 12: b.ls 160x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16]0x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16]0x20, 0x00, 0x1f, 0xd6, // 24: br x10x20, 0x00, 0x20, 0xd4, // 28: brk #0x1},{0xDEADBEEF, 0x0});GraphResult Result =GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 24, 0x0});EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,Analysis.validateCFIProtection(Result));}} // anonymous namespace} // end namespace cfi_verify} // end namespace llvmint main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);llvm::cl::ParseCommandLineOptions(argc, argv);llvm::InitializeAllTargetInfos();llvm::InitializeAllTargetMCs();llvm::InitializeAllAsmParsers();llvm::InitializeAllDisassemblers();return RUN_ALL_TESTS();}
set(LLVM_LINK_COMPONENTSAllTargetsAsmParsersAllTargetsDescsAllTargetsDisassemblersAllTargetsInfosMCMCParserObjectSupportSymbolize)add_llvm_unittest(CFIVerifyTestsFileAnalysis.cppGraphBuilder.cpp)target_link_libraries(CFIVerifyTests PRIVATE LLVMCFIVerify)set_property(TARGET CFIVerifyTests PROPERTY FOLDER "Tests/UnitTests/ToolTests")
if(LLVM_TARGETS_TO_BUILD MATCHES "X86")add_subdirectory(llvm-cfi-verify)endif()add_subdirectory(llvm-exegesis)add_subdirectory(llvm-profgen)add_subdirectory(llvm-mca)
//===- ProfileTest.cpp - XRay Profile unit tests ----------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/XRay/Profile.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <numeric>namespace llvm {namespace xray {namespace {using ::testing::AllOf;using ::testing::ElementsAre;using ::testing::Eq;using ::testing::Field;using ::testing::Not;using ::testing::Pair;using ::testing::UnorderedElementsAre;TEST(ProfileTest, CreateProfile) { Profile P; }TEST(ProfileTest, InternPath) {Profile P;auto Path0 = P.internPath({3, 2, 1});auto Path1 = P.internPath({3, 2, 1});auto Path2 = P.internPath({2, 1});EXPECT_THAT(Path0, Eq(Path1));EXPECT_THAT(Path0, Not(Eq(Path2)));}TEST(ProfileTest, ExpandPath) {Profile P;auto PathID = P.internPath({3, 2, 1});auto PathOrError = P.expandPath(PathID);if (!PathOrError)FAIL() << "Error: " << PathOrError.takeError();EXPECT_THAT(PathOrError.get(), ElementsAre(3, 2, 1));}TEST(ProfileTest, AddBlocks) {Profile P;// Expect an error on adding empty blocks.EXPECT_TRUE(errorToBool(P.addBlock({})));// Thread blocks may not be empty.EXPECT_TRUE(errorToBool(P.addBlock({1, {}})));// Thread blocks with data must succeed.EXPECT_FALSE(errorToBool(P.addBlock(Profile::Block{Profile::ThreadID{1},{{P.internPath({2, 1}), Profile::Data{1, 1000}},{P.internPath({3, 2, 1}), Profile::Data{10, 100}},}})));}TEST(ProfileTest, CopyProfile) {Profile P0, P1;EXPECT_FALSE(errorToBool(P0.addBlock(Profile::Block{Profile::ThreadID{1},{{P0.internPath({2, 1}), Profile::Data{1, 1000}},{P0.internPath({3, 2, 1}), Profile::Data{10, 100}},}})));P1 = P0;EXPECT_THAT(P1, UnorderedElementsAre(AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),Field(&Profile::Block::PathData,UnorderedElementsAre(Pair(P1.internPath({2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(1u)),Field(&Profile::Data::CumulativeLocalTime,Eq(1000u)))),Pair(P1.internPath({3, 2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(10u)),Field(&Profile::Data::CumulativeLocalTime,Eq(100u)))))))));}TEST(ProfileTest, MoveProfile) {Profile P0, P1;EXPECT_FALSE(errorToBool(P0.addBlock(Profile::Block{Profile::ThreadID{1},{{P0.internPath({2, 1}), Profile::Data{1, 1000}},{P0.internPath({3, 2, 1}), Profile::Data{10, 100}},}})));P1 = std::move(P0);EXPECT_THAT(P1, UnorderedElementsAre(AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),Field(&Profile::Block::PathData,UnorderedElementsAre(Pair(P1.internPath({2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(1u)),Field(&Profile::Data::CumulativeLocalTime,Eq(1000u)))),Pair(P1.internPath({3, 2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(10u)),Field(&Profile::Data::CumulativeLocalTime,Eq(100u)))))))));EXPECT_THAT(P0, UnorderedElementsAre());}TEST(ProfileTest, MergeProfilesByThread) {Profile P0, P1;// Set up the blocks for two different threads in P0.EXPECT_FALSE(errorToBool(P0.addBlock(Profile::Block{Profile::ThreadID{1},{{P0.internPath({2, 1}), Profile::Data{1, 1000}},{P0.internPath({4, 1}), Profile::Data{1, 1000}}}})));EXPECT_FALSE(errorToBool(P0.addBlock(Profile::Block{Profile::ThreadID{2},{{P0.internPath({3, 1}), Profile::Data{1, 1000}}}})));// Set up the blocks for two different threads in P1.EXPECT_FALSE(errorToBool(P1.addBlock(Profile::Block{Profile::ThreadID{1},{{P1.internPath({2, 1}), Profile::Data{1, 1000}}}})));EXPECT_FALSE(errorToBool(P1.addBlock(Profile::Block{Profile::ThreadID{2},{{P1.internPath({3, 1}), Profile::Data{1, 1000}},{P1.internPath({4, 1}), Profile::Data{1, 1000}}}})));Profile Merged = mergeProfilesByThread(P0, P1);EXPECT_THAT(Merged,UnorderedElementsAre(// We want to see two threads after the merge.AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),Field(&Profile::Block::PathData,UnorderedElementsAre(Pair(Merged.internPath({2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(2u)),Field(&Profile::Data::CumulativeLocalTime,Eq(2000u)))),Pair(Merged.internPath({4, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(1u)),Field(&Profile::Data::CumulativeLocalTime,Eq(1000u))))))),AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{2})),Field(&Profile::Block::PathData,UnorderedElementsAre(Pair(Merged.internPath({3, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(2u)),Field(&Profile::Data::CumulativeLocalTime,Eq(2000u)))),Pair(Merged.internPath({4, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(1u)),Field(&Profile::Data::CumulativeLocalTime,Eq(1000u)))))))));}TEST(ProfileTest, MergeProfilesByStack) {Profile P0, P1;EXPECT_FALSE(errorToBool(P0.addBlock(Profile::Block{Profile::ThreadID{1},{{P0.internPath({2, 1}), Profile::Data{1, 1000}}}})));EXPECT_FALSE(errorToBool(P1.addBlock(Profile::Block{Profile::ThreadID{2},{{P1.internPath({2, 1}), Profile::Data{1, 1000}}}})));Profile Merged = mergeProfilesByStack(P0, P1);EXPECT_THAT(Merged,ElementsAre(AllOf(// We expect that we lose the ThreadID dimension in this// algorithm.Field(&Profile::Block::Thread, Eq(Profile::ThreadID{0})),Field(&Profile::Block::PathData,ElementsAre(Pair(Merged.internPath({2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(2u)),Field(&Profile::Data::CumulativeLocalTime,Eq(2000u)))))))));}TEST(ProfileTest, MergeProfilesByStackAccumulate) {std::vector<Profile> Profiles(3);EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{Profile::ThreadID{1},{{Profiles[0].internPath({2, 1}), Profile::Data{1, 1000}}}})));EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{Profile::ThreadID{2},{{Profiles[1].internPath({2, 1}), Profile::Data{1, 1000}}}})));EXPECT_FALSE(errorToBool(Profiles[2].addBlock(Profile::Block{Profile::ThreadID{3},{{Profiles[2].internPath({2, 1}), Profile::Data{1, 1000}}}})));Profile Merged = std::accumulate(Profiles.begin(), Profiles.end(), Profile(),mergeProfilesByStack);EXPECT_THAT(Merged,ElementsAre(AllOf(// We expect that we lose the ThreadID dimension in this// algorithm.Field(&Profile::Block::Thread, Eq(Profile::ThreadID{0})),Field(&Profile::Block::PathData,ElementsAre(Pair(Merged.internPath({2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(3u)),Field(&Profile::Data::CumulativeLocalTime,Eq(3000u)))))))));}TEST(ProfileTest, MergeProfilesByThreadAccumulate) {std::vector<Profile> Profiles(2);// Set up the blocks for two different threads in Profiles[0].EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{Profile::ThreadID{1},{{Profiles[0].internPath({2, 1}), Profile::Data{1, 1000}},{Profiles[0].internPath({4, 1}), Profile::Data{1, 1000}}}})));EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{Profile::ThreadID{2},{{Profiles[0].internPath({3, 1}), Profile::Data{1, 1000}}}})));// Set up the blocks for two different threads in Profiles[1].EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{Profile::ThreadID{1},{{Profiles[1].internPath({2, 1}), Profile::Data{1, 1000}}}})));EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{Profile::ThreadID{2},{{Profiles[1].internPath({3, 1}), Profile::Data{1, 1000}},{Profiles[1].internPath({4, 1}), Profile::Data{1, 1000}}}})));Profile Merged = std::accumulate(Profiles.begin(), Profiles.end(), Profile(),mergeProfilesByThread);EXPECT_THAT(Merged,UnorderedElementsAre(// We want to see two threads after the merge.AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),Field(&Profile::Block::PathData,UnorderedElementsAre(Pair(Merged.internPath({2, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(2u)),Field(&Profile::Data::CumulativeLocalTime,Eq(2000u)))),Pair(Merged.internPath({4, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(1u)),Field(&Profile::Data::CumulativeLocalTime,Eq(1000u))))))),AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{2})),Field(&Profile::Block::PathData,UnorderedElementsAre(Pair(Merged.internPath({3, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(2u)),Field(&Profile::Data::CumulativeLocalTime,Eq(2000u)))),Pair(Merged.internPath({4, 1}),AllOf(Field(&Profile::Data::CallCount, Eq(1u)),Field(&Profile::Data::CumulativeLocalTime,Eq(1000u)))))))));}// FIXME: Add a test creating a Trace and generating a Profile// FIXME: Add tests for ranking/sorting profile blocks by dimension} // namespace} // namespace xray} // namespace llvm
//===- llvm/unittest/XRay/GraphTest.cpp - XRay Graph unit tests -*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/XRay/Graph.h"#include "gtest/gtest.h"#include <iostream>#include <set>#include <type_traits>using namespace llvm;using namespace xray;namespace {struct VAttr {unsigned VA;};struct EAttr {unsigned EA;};typedef Graph<VAttr, EAttr, unsigned> GraphT;typedef typename GraphT::VertexIdentifier VI;typedef typename GraphT::EdgeIdentifier EI;// Test Fixturetemplate <typename T> class GraphTest : public testing::Test {protected:T Graph = getTestGraph();private:static T getTestGraph() {using std::make_pair;std::remove_const_t<T> G;G.insert(make_pair(1u, VAttr({3u})));G.insert(make_pair(2u, VAttr({5u})));G.insert(make_pair(3u, VAttr({7u})));G.insert(make_pair(4u, VAttr({11u})));G.insert(make_pair(5u, VAttr({13u})));G.insert(make_pair(6u, VAttr({17u})));G.insert(std::make_pair(EI(1u, 2u), EAttr({3u * 5u})));G.insert(std::make_pair(EI(2u, 3u), EAttr({5u * 7u})));G.insert(std::make_pair(EI(6u, 3u), EAttr({2u * 7u * 17u})));G.insert(std::make_pair(EI(4u, 6u), EAttr({11u * 17u})));G.insert(std::make_pair(EI(2u, 4u), EAttr({5u * 11u})));G.insert(std::make_pair(EI(2u, 5u), EAttr({5u * 13u})));G.insert(std::make_pair(EI(4u, 5u), EAttr({11u * 13u})));return G;}};typedef ::testing::Types<GraphT, const GraphT> GraphTestTypes;using VVT = typename GraphT::VertexValueType;using EVT = typename GraphT::EdgeValueType;TYPED_TEST_SUITE(GraphTest, GraphTestTypes, );template <typename T> void graphVertexTester(T &G) {std::set<unsigned> V({1u, 2u, 3u, 4u, 5u, 6u});std::vector<unsigned> VA({0u, 3u, 5u, 7u, 11u, 13u, 17u});EXPECT_EQ(V.size(), G.vertices().size());EXPECT_FALSE(G.vertices().empty());for (unsigned u : V) {auto EVV = G.at(u);ASSERT_TRUE(!!EVV);EXPECT_EQ(1u, G.count(u));EXPECT_EQ(VA[u], EVV->VA);EXPECT_NE(G.vertices().end(),std::find_if(G.vertices().begin(), G.vertices().end(),[&](const VVT &VV) { return VV.first == u; }));consumeError(EVV.takeError());}for (auto &VVT : G.vertices()) {EXPECT_EQ(1u, V.count(VVT.first));EXPECT_EQ(VA[VVT.first], VVT.second.VA);}}template <typename T> void graphEdgeTester(T &G) {std::set<unsigned> V({1u, 2u, 3u, 4u, 5u, 6u});std::set<std::pair<unsigned, unsigned>> E({{1u, 2u}, {2u, 3u}, {6u, 3u}, {4u, 6u}, {2u, 4u}, {2u, 5u}, {4u, 5u}});std::vector<unsigned> VA({0u, 3u, 5u, 7u, 11u, 13u, 17u});EXPECT_EQ(E.size(), G.edges().size());EXPECT_FALSE(G.edges().empty());for (std::pair<unsigned, unsigned> u : E) {auto EEV = G.at(u);ASSERT_TRUE(!!EEV);EXPECT_EQ(1u, G.count(u));EXPECT_EQ(VA[u.first] * VA[u.second] * ((u.first > u.second) ? 2 : 1),EEV->EA);auto Pred = [&](const EVT &EV) { return EV.first == u; };EXPECT_NE(G.edges().end(),std::find_if(G.edges().begin(), G.edges().end(), Pred));consumeError(EEV.takeError());}for (auto &EV : G.edges()) {EXPECT_EQ(1u, E.count(EV.first));EXPECT_EQ(VA[EV.first.first] * VA[EV.first.second] *((EV.first.first > EV.first.second) ? 2 : 1),EV.second.EA);const auto &IE = G.inEdges(EV.first.second);const auto &OE = G.outEdges(EV.first.first);EXPECT_NE(IE.size(), 0u);EXPECT_NE(OE.size(), 0u);EXPECT_NE(IE.begin(), IE.end());EXPECT_NE(OE.begin(), OE.end());{auto It = std::find_if(G.inEdges(EV.first.second).begin(), G.inEdges(EV.first.second).end(),[&](const EVT &EVI) { return EVI.first == EV.first; });EXPECT_NE(G.inEdges(EV.first.second).end(), It);}{auto It = std::find_if(G.inEdges(EV.first.first).begin(), G.inEdges(EV.first.first).end(),[&](const EVT &EVI) { return EVI.first == EV.first; });EXPECT_EQ(G.inEdges(EV.first.first).end(), It);}{auto It =std::find_if(G.outEdges(EV.first.second).begin(),G.outEdges(EV.first.second).end(),[&](const EVT &EVI) { return EVI.first == EV.first; });EXPECT_EQ(G.outEdges(EV.first.second).end(), It);}{auto It = std::find_if(G.outEdges(EV.first.first).begin(), G.outEdges(EV.first.first).end(),[&](const EVT &EVI) { return EVI.first == EV.first; });EXPECT_NE(G.outEdges(EV.first.first).end(), It);}}}TYPED_TEST(GraphTest, TestGraphEdge) {auto &G = this->Graph;graphEdgeTester(G);}TYPED_TEST(GraphTest, TestGraphVertex) {auto &G = this->Graph;graphVertexTester(G);}TYPED_TEST(GraphTest, TestCopyConstructor) {TypeParam G(this->Graph);graphEdgeTester(G);graphVertexTester(G);}TYPED_TEST(GraphTest, TestCopyAssign) {TypeParam G = this->Graph;graphEdgeTester(G);graphVertexTester(G);}TYPED_TEST(GraphTest, TestMoveConstructor) {TypeParam G(std::move(this->Graph));graphEdgeTester(G);graphVertexTester(G);}// Tests the incremental Construction of a graphTEST(GraphTest, TestConstruction) {GraphT MG;const GraphT &G = MG;EXPECT_EQ(0u, G.count(0u));EXPECT_EQ(0u, G.count({0u, 1u}));auto VE = G.at(0);auto EE = G.at({0, 0});EXPECT_FALSE(VE); // G.at[0] returns an errorEXPECT_FALSE(EE); // G.at[{0,0}] returns an errorconsumeError(VE.takeError());consumeError(EE.takeError());EXPECT_TRUE(G.vertices().empty());EXPECT_TRUE(G.edges().empty());EXPECT_EQ(G.vertices().begin(), G.vertices().end());EXPECT_EQ(G.edges().begin(), G.edges().end());}TEST(GraphTest, TestiVertexAccessOperator) {GraphT MG;const GraphT &G = MG;MG[0u] = {1u};EXPECT_EQ(1u, MG[0u].VA);EXPECT_EQ(1u, G.count(0u));EXPECT_EQ(0u, G.count(1u));EXPECT_EQ(1u, MG[0u].VA);auto T = G.at(0u);EXPECT_TRUE(!!T);EXPECT_EQ(1u, T->VA);EXPECT_EQ(1u, G.vertices().size());EXPECT_EQ(0u, G.edges().size());EXPECT_FALSE(G.vertices().empty());EXPECT_TRUE(G.edges().empty());EXPECT_NE(G.vertices().begin(), G.vertices().end());EXPECT_EQ(G.edges().begin(), G.edges().end());EXPECT_EQ(1u, G.vertices().begin()->second.VA);EXPECT_EQ(0u, G.vertices().begin()->first);EXPECT_EQ(0u, G.outEdges(0u).size());EXPECT_TRUE(G.outEdges(0u).empty());EXPECT_EQ(G.outEdges(0u).begin(), G.outEdges(0u).end());EXPECT_EQ(0u, G.inEdges(0u).size());EXPECT_TRUE(G.inEdges(0u).empty());EXPECT_EQ(G.inEdges(0u).begin(), G.inEdges(0u).end());}TEST(GraphTest, TestEdgeAccessOperator) {GraphT MG;const GraphT &G = MG;MG[{0u, 0u}] = {2u};EI EdgeIdent({0u, 0u});EXPECT_EQ(2u, MG[EdgeIdent].EA);EXPECT_EQ(1u, G.count({0u, 0u}));EXPECT_EQ(0u, G.count({0u, 1u}));EXPECT_EQ(1u, G.count(0u));EXPECT_NE(1u, G.count(1u));auto T = G.at({0u, 0u});EXPECT_TRUE(T && T->EA == 2u);EXPECT_EQ(1u, G.edges().size());EXPECT_EQ(1u, G.vertices().size());EXPECT_FALSE(G.edges().empty());EXPECT_FALSE(G.vertices().empty());EXPECT_NE(G.edges().begin(), G.edges().end());EXPECT_EQ(EI(0u, 0u), G.edges().begin()->first);EXPECT_EQ(2u, G.edges().begin()->second.EA);EXPECT_EQ(1u, G.outEdges(0u).size());EXPECT_FALSE(G.outEdges(0u).empty());EXPECT_NE(G.outEdges(0u).begin(), G.outEdges(0u).end());EXPECT_EQ(EI(0u, 0u), G.outEdges(0u).begin()->first);EXPECT_EQ(2u, G.outEdges(0u).begin()->second.EA);EXPECT_EQ(++(G.outEdges(0u).begin()), G.outEdges(0u).end());EXPECT_EQ(1u, G.inEdges(0u).size());EXPECT_FALSE(G.inEdges(0u).empty());EXPECT_NE(G.inEdges(0u).begin(), G.inEdges(0u).end());EXPECT_EQ(EI(0u, 0u), G.inEdges(0u).begin()->first);EXPECT_EQ(2u, G.inEdges(0u).begin()->second.EA);EXPECT_EQ(++(G.inEdges(0u).begin()), G.inEdges(0u).end());}}
//===- llvm/unittest/XRay/FDRTraceWriterTest.cpp ----------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// Test a utility that can write out XRay FDR Mode formatted trace files.////===----------------------------------------------------------------------===//#include "llvm/XRay/FDRTraceWriter.h"#include "llvm/Support/raw_ostream.h"#include "llvm/XRay/FDRLogBuilder.h"#include "llvm/XRay/FDRRecords.h"#include "llvm/XRay/Trace.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <string>namespace llvm {namespace xray {namespace {using testing::ElementsAre;using testing::Eq;using testing::Field;using testing::IsEmpty;using testing::Not;// We want to be able to create an instance of an FDRTraceWriter and associate// it with a stream, which could be loaded and turned into a Trace instance.// This test writes out version 3 trace logs.TEST(FDRTraceWriterTest, WriteToStringBufferVersion3) {std::string Data;raw_string_ostream OS(Data);XRayFileHeader H;H.Version = 3;H.Type = 1;H.ConstantTSC = true;H.NonstopTSC = true;H.CycleFrequency = 3e9;FDRTraceWriter Writer(OS, H);auto L = LogBuilder().add<BufferExtents>(80).add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();for (auto &P : L)ASSERT_FALSE(errorToBool(P->apply(Writer)));OS.flush();// Then from here we load the Trace file.DataExtractor DE(Data, sys::IsLittleEndianHost, 8);auto TraceOrErr = loadTrace(DE, true);if (!TraceOrErr)FAIL() << TraceOrErr.takeError();auto &Trace = TraceOrErr.get();ASSERT_THAT(Trace, Not(IsEmpty()));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)),Field(&XRayRecord::FuncId, Eq(1))));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)),Field(&XRayRecord::TId, Eq(1u))));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::PId, Eq(1u)),Field(&XRayRecord::PId, Eq(1u))));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)),Field(&XRayRecord::CPU, Eq(1u))));EXPECT_THAT(Trace,ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)),Field(&XRayRecord::Type, Eq(RecordTypes::EXIT))));}// This version is almost exactly the same as above, except writing version 2// logs, without the PID records.TEST(FDRTraceWriterTest, WriteToStringBufferVersion2) {std::string Data;raw_string_ostream OS(Data);XRayFileHeader H;H.Version = 2;H.Type = 1;H.ConstantTSC = true;H.NonstopTSC = true;H.CycleFrequency = 3e9;FDRTraceWriter Writer(OS, H);auto L = LogBuilder().add<BufferExtents>(64).add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();for (auto &P : L)ASSERT_FALSE(errorToBool(P->apply(Writer)));OS.flush();// Then from here we load the Trace file.DataExtractor DE(Data, sys::IsLittleEndianHost, 8);auto TraceOrErr = loadTrace(DE, true);if (!TraceOrErr)FAIL() << TraceOrErr.takeError();auto &Trace = TraceOrErr.get();ASSERT_THAT(Trace, Not(IsEmpty()));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)),Field(&XRayRecord::FuncId, Eq(1))));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)),Field(&XRayRecord::TId, Eq(1u))));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)),Field(&XRayRecord::CPU, Eq(1u))));EXPECT_THAT(Trace,ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)),Field(&XRayRecord::Type, Eq(RecordTypes::EXIT))));}// This covers version 1 of the log, without a BufferExtents record but has an// explicit EndOfBuffer record.TEST(FDRTraceWriterTest, WriteToStringBufferVersion1) {std::string Data;raw_string_ostream OS(Data);XRayFileHeader H;H.Version = 1;H.Type = 1;H.ConstantTSC = true;H.NonstopTSC = true;H.CycleFrequency = 3e9;// Write the size of buffers out, arbitrarily it's 4k.constexpr uint64_t BufferSize = 4096;std::memcpy(H.FreeFormData, reinterpret_cast<const char *>(&BufferSize),sizeof(BufferSize));FDRTraceWriter Writer(OS, H);OS.flush();// Ensure that at this point the Data buffer has the file header serialized// size.ASSERT_THAT(Data.size(), Eq(32u));auto L = LogBuilder().add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<EndBufferRecord>().consume();for (auto &P : L)ASSERT_FALSE(errorToBool(P->apply(Writer)));// We need to pad the buffer with 4016 (4096 - 80) bytes of zeros.OS.write_zeros(4016);OS.flush();// For version 1 of the log, we need the whole buffer to be the size of the// file header plus 32.ASSERT_THAT(Data.size(), Eq(BufferSize + 32));// Then from here we load the Trace file.DataExtractor DE(Data, sys::IsLittleEndianHost, 8);auto TraceOrErr = loadTrace(DE, true);if (!TraceOrErr)FAIL() << TraceOrErr.takeError();auto &Trace = TraceOrErr.get();ASSERT_THAT(Trace, Not(IsEmpty()));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)),Field(&XRayRecord::FuncId, Eq(1))));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)),Field(&XRayRecord::TId, Eq(1u))));EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)),Field(&XRayRecord::CPU, Eq(1u))));EXPECT_THAT(Trace,ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)),Field(&XRayRecord::Type, Eq(RecordTypes::EXIT))));}} // namespace} // namespace xray} // namespace llvm
//===- FDRRecords.cpp - Unit Tests for XRay FDR Record Loading ------------===////// 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////===----------------------------------------------------------------------===//#include "gmock/gmock.h"#include "gtest/gtest.h"#include "llvm/XRay/BlockIndexer.h"#include "llvm/XRay/BlockPrinter.h"#include "llvm/XRay/BlockVerifier.h"#include "llvm/XRay/FDRLogBuilder.h"#include "llvm/XRay/FDRRecords.h"#include "llvm/XRay/RecordPrinter.h"namespace llvm {namespace xray {namespace {using ::testing::Eq;using ::testing::Not;TEST(XRayFDRTest, BuilderAndBlockIndexer) {// We recreate a single block of valid records, then ensure that we find all// of them belonging in the same index. We do this for three blocks, and// ensure we find the same records in the blocks we deduce.auto Block0 = LogBuilder().add<BufferExtents>(100).add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<PIDRecord>(1).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<CustomEventRecordV5>(1, 4, "XRAY").add<TypedEventRecord>(1, 4, 2, "XRAY").consume();auto Block1 = LogBuilder().add<BufferExtents>(100).add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<PIDRecord>(1).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<CustomEventRecordV5>(1, 4, "XRAY").add<TypedEventRecord>(1, 4, 2, "XRAY").consume();auto Block2 = LogBuilder().add<BufferExtents>(100).add<NewBufferRecord>(2).add<WallclockRecord>(1, 3).add<PIDRecord>(1).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<CustomEventRecordV5>(1, 4, "XRAY").add<TypedEventRecord>(1, 4, 2, "XRAY").consume();BlockIndexer::Index Index;BlockIndexer Indexer(Index);for (auto B : {std::ref(Block0), std::ref(Block1), std::ref(Block2)}) {for (auto &R : B.get())ASSERT_FALSE(errorToBool(R->apply(Indexer)));ASSERT_FALSE(errorToBool(Indexer.flush()));}// We have two threads worth of blocks.ASSERT_THAT(Index.size(), Eq(2u));auto T1Blocks = Index.find({1, 1});ASSERT_THAT(T1Blocks, Not(Eq(Index.end())));ASSERT_THAT(T1Blocks->second.size(), Eq(2u));auto T2Blocks = Index.find({1, 2});ASSERT_THAT(T2Blocks, Not(Eq(Index.end())));ASSERT_THAT(T2Blocks->second.size(), Eq(1u));}TEST(XRayFDRTest, BuilderAndBlockVerifier) {auto Block = LogBuilder().add<BufferExtents>(48).add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).consume();BlockVerifier Verifier;for (auto &R : Block)ASSERT_FALSE(errorToBool(R->apply(Verifier)));ASSERT_FALSE(errorToBool(Verifier.verify()));}TEST(XRayFDRTest, IndexAndVerifyBlocks) {auto Block0 = LogBuilder().add<BufferExtents>(64).add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<CustomEventRecordV5>(1, 4, "XRAY").add<TypedEventRecord>(1, 4, 2, "XRAY").consume();auto Block1 = LogBuilder().add<BufferExtents>(64).add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<CustomEventRecordV5>(1, 4, "XRAY").add<TypedEventRecord>(1, 4, 2, "XRAY").consume();auto Block2 = LogBuilder().add<BufferExtents>(64).add<NewBufferRecord>(1).add<WallclockRecord>(1, 1).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<CustomEventRecordV5>(1, 4, "XRAY").add<TypedEventRecord>(1, 4, 2, "XRAY").consume();// First, index the records in different blocks.BlockIndexer::Index Index;BlockIndexer Indexer(Index);for (auto B : {std::ref(Block0), std::ref(Block1), std::ref(Block2)}) {for (auto &R : B.get())ASSERT_FALSE(errorToBool(R->apply(Indexer)));ASSERT_FALSE(errorToBool(Indexer.flush()));}// Next, verify that each block is consistently defined.BlockVerifier Verifier;for (auto &ProcessThreadBlocks : Index) {auto &Blocks = ProcessThreadBlocks.second;for (auto &B : Blocks) {for (auto *R : B.Records)ASSERT_FALSE(errorToBool(R->apply(Verifier)));ASSERT_FALSE(errorToBool(Verifier.verify()));Verifier.reset();}}// Then set up the printing mechanisms.std::string Output;raw_string_ostream OS(Output);RecordPrinter RP(OS);BlockPrinter BP(OS, RP);for (auto &ProcessThreadBlocks : Index) {auto &Blocks = ProcessThreadBlocks.second;for (auto &B : Blocks) {for (auto *R : B.Records)ASSERT_FALSE(errorToBool(R->apply(BP)));BP.reset();}}OS.flush();EXPECT_THAT(Output, Not(Eq("")));}} // namespace} // namespace xray} // namespace llvm
//===- llvm/unittest/XRay/FDRRecordPrinterTest.cpp --------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/raw_ostream.h"#include "llvm/XRay/FDRRecords.h"#include "llvm/XRay/RecordPrinter.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <string>namespace llvm {namespace xray {namespace {using ::testing::Eq;template <class RecordType> struct Helper {};template <> struct Helper<BufferExtents> {static std::unique_ptr<Record> construct() {return std::make_unique<BufferExtents>(1);}static const char *expected() { return "<Buffer: size = 1 bytes>"; }};template <> struct Helper<WallclockRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<WallclockRecord>(1, 2);}static const char *expected() { return "<Wall Time: seconds = 1.000002>"; }};template <> struct Helper<NewCPUIDRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<NewCPUIDRecord>(1, 2);}static const char *expected() { return "<CPU: id = 1, tsc = 2>"; }};template <> struct Helper<TSCWrapRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<TSCWrapRecord>(1);}static const char *expected() { return "<TSC Wrap: base = 1>"; }};template <> struct Helper<CustomEventRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<CustomEventRecord>(4, 1, 2, "data");}static const char *expected() {return "<Custom Event: tsc = 1, cpu = 2, size = 4, data = 'data'>";}};template <> struct Helper<CallArgRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<CallArgRecord>(1);}static const char *expected() {return "<Call Argument: data = 1 (hex = 0x1)>";}};template <> struct Helper<PIDRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<PIDRecord>(1);}static const char *expected() { return "<PID: 1>"; }};template <> struct Helper<NewBufferRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<NewBufferRecord>(1);}static const char *expected() { return "<Thread ID: 1>"; }};template <> struct Helper<EndBufferRecord> {static std::unique_ptr<Record> construct() {return std::make_unique<EndBufferRecord>();}static const char *expected() { return "<End of Buffer>"; }};template <class T> class PrinterTest : public ::testing::Test {protected:std::string Data;raw_string_ostream OS;RecordPrinter P;std::unique_ptr<Record> R;public:PrinterTest() : Data(), OS(Data), P(OS), R(Helper<T>::construct()) {}};TYPED_TEST_SUITE_P(PrinterTest);TYPED_TEST_P(PrinterTest, PrintsRecord) {ASSERT_NE(nullptr, this->R);ASSERT_FALSE(errorToBool(this->R->apply(this->P)));this->OS.flush();EXPECT_THAT(this->Data, Eq(Helper<TypeParam>::expected()));}REGISTER_TYPED_TEST_SUITE_P(PrinterTest, PrintsRecord);using FDRRecordTypes =::testing::Types<BufferExtents, NewBufferRecord, EndBufferRecord,NewCPUIDRecord, TSCWrapRecord, WallclockRecord,CustomEventRecord, CallArgRecord, BufferExtents,PIDRecord>;INSTANTIATE_TYPED_TEST_SUITE_P(Records, PrinterTest, FDRRecordTypes, );TEST(FDRRecordPrinterTest, WriteFunctionRecordEnter) {std::string Data;raw_string_ostream OS(Data);RecordPrinter P(OS);FunctionRecord R(RecordTypes::ENTER, 1, 2);ASSERT_FALSE(errorToBool(R.apply(P)));OS.flush();EXPECT_THAT(Data, Eq("<Function Enter: #1 delta = +2>"));}TEST(FDRRecordPrinterTest, WriteFunctionRecordExit) {std::string Data;raw_string_ostream OS(Data);RecordPrinter P(OS);FunctionRecord R(RecordTypes::EXIT, 1, 2);ASSERT_FALSE(errorToBool(R.apply(P)));OS.flush();EXPECT_THAT(Data, Eq("<Function Exit: #1 delta = +2>"));}TEST(FDRRecordPrinterTest, WriteFunctionRecordTailExit) {std::string Data;raw_string_ostream OS(Data);RecordPrinter P(OS);FunctionRecord R(RecordTypes::TAIL_EXIT, 1, 2);ASSERT_FALSE(errorToBool(R.apply(P)));OS.flush();EXPECT_THAT(Data, Eq("<Function Tail Exit: #1 delta = +2>"));}TEST(FDRRecordPrinterTest, WriteFunctionRecordEnterArg) {std::string Data;raw_string_ostream OS(Data);RecordPrinter P(OS);FunctionRecord R(RecordTypes::ENTER_ARG, 1, 2);ASSERT_FALSE(errorToBool(R.apply(P)));OS.flush();EXPECT_THAT(Data, Eq("<Function Enter With Arg: #1 delta = +2>"));}} // namespace} // namespace xray} // namespace llvm
//===- llvm/unittest/XRay/FDRProducerConsumerTest.cpp -----------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// Test for round-trip record writing and reading.////===----------------------------------------------------------------------===//#include "llvm/Support/DataExtractor.h"#include "llvm/Support/raw_ostream.h"#include "llvm/XRay/FDRLogBuilder.h"#include "llvm/XRay/FDRRecordConsumer.h"#include "llvm/XRay/FDRRecordProducer.h"#include "llvm/XRay/FDRRecords.h"#include "llvm/XRay/FDRTraceWriter.h"#include "llvm/XRay/FileHeaderReader.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <string>#include <tuple>namespace llvm {namespace xray {namespace {using ::testing::Eq;using ::testing::IsEmpty;using ::testing::Not;using ::testing::SizeIs;template <class RecordType> std::unique_ptr<Record> MakeRecord();template <> std::unique_ptr<Record> MakeRecord<NewBufferRecord>() {return std::make_unique<NewBufferRecord>(1);}template <> std::unique_ptr<Record> MakeRecord<NewCPUIDRecord>() {return std::make_unique<NewCPUIDRecord>(1, 2);}template <> std::unique_ptr<Record> MakeRecord<TSCWrapRecord>() {return std::make_unique<TSCWrapRecord>(1);}template <> std::unique_ptr<Record> MakeRecord<WallclockRecord>() {return std::make_unique<WallclockRecord>(1, 2);}template <> std::unique_ptr<Record> MakeRecord<CustomEventRecord>() {return std::make_unique<CustomEventRecord>(4, 1, 2, "data");}template <> std::unique_ptr<Record> MakeRecord<CallArgRecord>() {return std::make_unique<CallArgRecord>(1);}template <> std::unique_ptr<Record> MakeRecord<PIDRecord>() {return std::make_unique<PIDRecord>(1);}template <> std::unique_ptr<Record> MakeRecord<FunctionRecord>() {return std::make_unique<FunctionRecord>(RecordTypes::ENTER, 1, 2);}template <> std::unique_ptr<Record> MakeRecord<CustomEventRecordV5>() {return std::make_unique<CustomEventRecordV5>(4, 1, "data");}template <> std::unique_ptr<Record> MakeRecord<TypedEventRecord>() {return std::make_unique<TypedEventRecord>(4, 1, 2, "data");}template <class T> class RoundTripTest : public ::testing::Test {public:RoundTripTest() : Data(), OS(Data) {H.Version = 4;H.Type = 1;H.ConstantTSC = true;H.NonstopTSC = true;H.CycleFrequency = 3e9;Writer = std::make_unique<FDRTraceWriter>(OS, H);Rec = MakeRecord<T>();}protected:std::string Data;raw_string_ostream OS;XRayFileHeader H;std::unique_ptr<FDRTraceWriter> Writer;std::unique_ptr<Record> Rec;};TYPED_TEST_SUITE_P(RoundTripTest);template <class T> class RoundTripTestV5 : public ::testing::Test {public:RoundTripTestV5() : Data(), OS(Data) {H.Version = 5;H.Type = 1;H.ConstantTSC = true;H.NonstopTSC = true;H.CycleFrequency = 3e9;Writer = std::make_unique<FDRTraceWriter>(OS, H);Rec = MakeRecord<T>();}protected:std::string Data;raw_string_ostream OS;XRayFileHeader H;std::unique_ptr<FDRTraceWriter> Writer;std::unique_ptr<Record> Rec;};TYPED_TEST_SUITE_P(RoundTripTestV5);// This test ensures that the writing and reading implementations are in sync --// that given write(read(write(R))) == R.TYPED_TEST_P(RoundTripTest, RoundTripsSingleValue) {// Always write a buffer extents record which will cover the correct size of// the record, for version 3 and up.BufferExtents BE(200);ASSERT_FALSE(errorToBool(BE.apply(*this->Writer)));auto &R = this->Rec;ASSERT_FALSE(errorToBool(R->apply(*this->Writer)));this->OS.flush();DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8);uint64_t OffsetPtr = 0;auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr);if (!HeaderOrErr)FAIL() << HeaderOrErr.takeError();FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr);std::vector<std::unique_ptr<Record>> Records;LogBuilderConsumer C(Records);while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) {auto R = P.produce();if (!R)FAIL() << R.takeError();if (auto E = C.consume(std::move(R.get())))FAIL() << E;}ASSERT_THAT(Records, Not(IsEmpty()));std::string Data2;raw_string_ostream OS2(Data2);FDRTraceWriter Writer2(OS2, this->H);for (auto &P : Records)ASSERT_FALSE(errorToBool(P->apply(Writer2)));OS2.flush();EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)),this->Data.substr(sizeof(XRayFileHeader)));ASSERT_THAT(Records, SizeIs(2));EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType()));}REGISTER_TYPED_TEST_SUITE_P(RoundTripTest, RoundTripsSingleValue);// We duplicate the above case for the V5 version using different types and// encodings.TYPED_TEST_P(RoundTripTestV5, RoundTripsSingleValue) {BufferExtents BE(200);ASSERT_FALSE(errorToBool(BE.apply(*this->Writer)));auto &R = this->Rec;ASSERT_FALSE(errorToBool(R->apply(*this->Writer)));this->OS.flush();DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8);uint64_t OffsetPtr = 0;auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr);if (!HeaderOrErr)FAIL() << HeaderOrErr.takeError();FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr);std::vector<std::unique_ptr<Record>> Records;LogBuilderConsumer C(Records);while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) {auto R = P.produce();if (!R)FAIL() << R.takeError();if (auto E = C.consume(std::move(R.get())))FAIL() << E;}ASSERT_THAT(Records, Not(IsEmpty()));std::string Data2;raw_string_ostream OS2(Data2);FDRTraceWriter Writer2(OS2, this->H);for (auto &P : Records)ASSERT_FALSE(errorToBool(P->apply(Writer2)));OS2.flush();EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)),this->Data.substr(sizeof(XRayFileHeader)));ASSERT_THAT(Records, SizeIs(2));EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType()));}REGISTER_TYPED_TEST_SUITE_P(RoundTripTestV5, RoundTripsSingleValue);// These are the record types we support for v4 and below.using RecordTypes =::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord,WallclockRecord, CustomEventRecord, CallArgRecord,PIDRecord, FunctionRecord>;INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTest, RecordTypes, );// For V5, we have two new types we're supporting.using RecordTypesV5 =::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord,WallclockRecord, CustomEventRecordV5, TypedEventRecord,CallArgRecord, PIDRecord, FunctionRecord>;INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTestV5, RecordTypesV5, );} // namespace} // namespace xray} // namespace llvm
//===- llvm/unittest/XRay/FDRBlockVerifierTest.cpp --------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/Testing/Support/Error.h"#include "llvm/XRay/BlockIndexer.h"#include "llvm/XRay/BlockVerifier.h"#include "llvm/XRay/FDRLogBuilder.h"#include "llvm/XRay/FDRRecords.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace xray {namespace {using ::testing::ElementsAre;using ::testing::Not;using ::testing::SizeIs;TEST(FDRBlockVerifierTest, ValidBlocksV3) {auto Block0 = LogBuilder().add<BufferExtents>(80).add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();auto Block1 = LogBuilder().add<BufferExtents>(80).add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();auto Block2 = LogBuilder().add<BufferExtents>(80).add<NewBufferRecord>(2).add<WallclockRecord>(1, 2).add<PIDRecord>(1).add<NewCPUIDRecord>(2, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();BlockIndexer::Index Index;BlockIndexer Indexer(Index);for (auto B : {std::ref(Block0), std::ref(Block1), std::ref(Block2)}) {for (auto &R : B.get())ASSERT_FALSE(errorToBool(R->apply(Indexer)));ASSERT_FALSE(errorToBool(Indexer.flush()));}BlockVerifier Verifier;for (auto &ProcessThreadBlocks : Index) {auto &Blocks = ProcessThreadBlocks.second;for (auto &B : Blocks) {for (auto *R : B.Records)ASSERT_FALSE(errorToBool(R->apply(Verifier)));ASSERT_FALSE(errorToBool(Verifier.verify()));Verifier.reset();}}}TEST(FDRBlockVerifierTest, MissingPIDRecord) {auto Block = LogBuilder().add<BufferExtents>(20).add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();BlockVerifier Verifier;for (auto &R : Block)ASSERT_FALSE(errorToBool(R->apply(Verifier)));ASSERT_FALSE(errorToBool(Verifier.verify()));}TEST(FDRBlockVerifierTest, MissingBufferExtents) {auto Block = LogBuilder().add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();BlockVerifier Verifier;for (auto &R : Block)ASSERT_FALSE(errorToBool(R->apply(Verifier)));ASSERT_FALSE(errorToBool(Verifier.verify()));}TEST(FDRBlockVerifierTest, IgnoreRecordsAfterEOB) {auto Block = LogBuilder().add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<NewCPUIDRecord>(1, 2).add<EndBufferRecord>().add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();BlockVerifier Verifier;for (auto &R : Block)ASSERT_FALSE(errorToBool(R->apply(Verifier)));ASSERT_FALSE(errorToBool(Verifier.verify()));}TEST(FDRBlockVerifierTest, MalformedV2) {auto Block = LogBuilder().add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).add<NewBufferRecord>(2).consume();BlockVerifier Verifier;ASSERT_THAT(Block, SizeIs(6u));EXPECT_THAT_ERROR(Block[0]->apply(Verifier), Succeeded());EXPECT_THAT_ERROR(Block[1]->apply(Verifier), Succeeded());EXPECT_THAT_ERROR(Block[2]->apply(Verifier), Succeeded());EXPECT_THAT_ERROR(Block[3]->apply(Verifier), Succeeded());EXPECT_THAT_ERROR(Block[4]->apply(Verifier), Succeeded());EXPECT_THAT_ERROR(Block[5]->apply(Verifier), Failed());}} // namespace} // namespace xray} // namespace llvm
//===- llvm/unittest/XRay/FDRTraceWriterTest.cpp ----------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/XRay/BlockIndexer.h"#include "llvm/XRay/FDRLogBuilder.h"#include "llvm/XRay/FDRRecords.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace xray {namespace {using ::testing::ElementsAre;using ::testing::Eq;using ::testing::Field;using ::testing::Not;using ::testing::SizeIs;// This test ensures that we can index blocks that follow version 3 of the log// format.TEST(FDRBlockIndexerTest, IndexBlocksV3) {auto Block0 = LogBuilder().add<BufferExtents>(80).add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();auto Block1 = LogBuilder().add<BufferExtents>(80).add<NewBufferRecord>(1).add<WallclockRecord>(1, 2).add<PIDRecord>(1).add<NewCPUIDRecord>(1, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();auto Block2 = LogBuilder().add<BufferExtents>(80).add<NewBufferRecord>(2).add<WallclockRecord>(1, 2).add<PIDRecord>(1).add<NewCPUIDRecord>(2, 2).add<FunctionRecord>(RecordTypes::ENTER, 1, 1).add<FunctionRecord>(RecordTypes::EXIT, 1, 100).consume();BlockIndexer::Index Index;BlockIndexer Indexer(Index);// Iterate through the contrived blocks we have created above.for (auto B : {std::ref(Block0), std::ref(Block1), std::ref(Block2)}) {// For each record in the block, we apply the indexer.for (auto &R : B.get())ASSERT_FALSE(errorToBool(R->apply(Indexer)));ASSERT_FALSE(errorToBool(Indexer.flush()));}ASSERT_THAT(Index.size(), Eq(2u));auto T1Blocks = Index.find({1, 1});ASSERT_THAT(T1Blocks, Not(Eq(Index.end())));// Expect only six records, because we're ignoring the BufferExtents record.EXPECT_THAT(T1Blocks->second,ElementsAre(Field(&BlockIndexer::Block::Records, SizeIs(6u)),Field(&BlockIndexer::Block::Records, SizeIs(6u))));auto T2Blocks = Index.find({1, 2});ASSERT_THAT(T2Blocks, Not(Eq(Index.end())));EXPECT_THAT(T2Blocks->second, ElementsAre(Field(&BlockIndexer::Block::Records,SizeIs(Eq(6u)))));}// FIXME: Support indexing V2 and V1 blocks.} // namespace} // namespace xray} // namespace llvm
set(LLVM_LINK_COMPONENTSSupportXRay)add_llvm_unittest(XRayTestsFDRBlockIndexerTest.cppFDRBlockVerifierTest.cppFDRProducerConsumerTest.cppFDRRecordPrinterTest.cppFDRRecordsTest.cppFDRTraceWriterTest.cppGraphTest.cppProfileTest.cpp)add_dependencies(XRayTests intrinsics_gen)target_link_libraries(XRayTests PRIVATE LLVMTestingSupport)
//===- llvm/unittest/Transforms/Vectorize/VPlanTestBase.h -----------------===////// 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////===----------------------------------------------------------------------===///// \file/// This file defines a VPlanTestBase class, which provides helpers to parse/// a LLVM IR string and create VPlans given a loop entry block.//===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TRANSFORMS_VECTORIZE_VPLANTESTBASE_H#define LLVM_UNITTESTS_TRANSFORMS_VECTORIZE_VPLANTESTBASE_H#include "../lib/Transforms/Vectorize/VPlan.h"#include "../lib/Transforms/Vectorize/VPlanHCFGBuilder.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/BasicAliasAnalysis.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"namespace llvm {/// Helper class to create a module from an assembly string and VPlans for a/// given loop entry block.class VPlanTestBase : public testing::Test {protected:TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;DataLayout DL;std::unique_ptr<LLVMContext> Ctx;std::unique_ptr<Module> M;std::unique_ptr<LoopInfo> LI;std::unique_ptr<DominatorTree> DT;std::unique_ptr<AssumptionCache> AC;std::unique_ptr<ScalarEvolution> SE;VPlanTestBase(): TLII(), TLI(TLII),DL("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-""f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:""16:32:64-S128"),Ctx(new LLVMContext) {}Module &parseModule(const char *ModuleString) {SMDiagnostic Err;M = parseAssemblyString(ModuleString, Err, *Ctx);EXPECT_TRUE(M);return *M;}void doAnalysis(Function &F) {DT.reset(new DominatorTree(F));LI.reset(new LoopInfo(*DT));AC.reset(new AssumptionCache(F));SE.reset(new ScalarEvolution(F, TLI, *AC, *DT, *LI));}VPlanPtr buildHCFG(BasicBlock *LoopHeader) {doAnalysis(*LoopHeader->getParent());auto Plan = std::make_unique<VPlan>();VPlanHCFGBuilder HCFGBuilder(LI->getLoopFor(LoopHeader), LI.get(), *Plan);HCFGBuilder.buildHierarchicalCFG();return Plan;}/// Build the VPlan plain CFG for the loop starting from \p LoopHeader.VPlanPtr buildPlainCFG(BasicBlock *LoopHeader) {doAnalysis(*LoopHeader->getParent());auto Plan = std::make_unique<VPlan>();VPlanHCFGBuilder HCFGBuilder(LI->getLoopFor(LoopHeader), LI.get(), *Plan);VPBasicBlock *EntryVPBB = HCFGBuilder.buildPlainCFG();Plan->setEntry(EntryVPBB);return Plan;}};} // namespace llvm#endif // LLVM_UNITTESTS_TRANSFORMS_VECTORIZE_VPLANTESTBASE_H
//===- llvm/unittests/Transforms/Vectorize/VPlanTest.cpp - VPlan tests ----===//////// 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////===----------------------------------------------------------------------===//#include "../lib/Transforms/Vectorize/VPlan.h"#include "llvm/ADT/DepthFirstIterator.h"#include "llvm/ADT/PostOrderIterator.h"#include "llvm/Analysis/VectorUtils.h"#include "llvm/IR/Instruction.h"#include "llvm/IR/Instructions.h"#include "gtest/gtest.h"#include <string>namespace llvm {namespace {#define CHECK_ITERATOR(Range1, ...) \do { \std::vector<VPInstruction *> Tmp = {__VA_ARGS__}; \EXPECT_EQ((size_t)std::distance(Range1.begin(), Range1.end()), \Tmp.size()); \for (auto Pair : zip(Range1, make_range(Tmp.begin(), Tmp.end()))) \EXPECT_EQ(&std::get<0>(Pair), std::get<1>(Pair)); \} while (0)TEST(VPInstructionTest, insertBefore) {VPInstruction *I1 = new VPInstruction(0, {});VPInstruction *I2 = new VPInstruction(1, {});VPInstruction *I3 = new VPInstruction(2, {});VPBasicBlock VPBB1;VPBB1.appendRecipe(I1);I2->insertBefore(I1);CHECK_ITERATOR(VPBB1, I2, I1);I3->insertBefore(I2);CHECK_ITERATOR(VPBB1, I3, I2, I1);}TEST(VPInstructionTest, eraseFromParent) {VPInstruction *I1 = new VPInstruction(0, {});VPInstruction *I2 = new VPInstruction(1, {});VPInstruction *I3 = new VPInstruction(2, {});VPBasicBlock VPBB1;VPBB1.appendRecipe(I1);VPBB1.appendRecipe(I2);VPBB1.appendRecipe(I3);I2->eraseFromParent();CHECK_ITERATOR(VPBB1, I1, I3);I1->eraseFromParent();CHECK_ITERATOR(VPBB1, I3);I3->eraseFromParent();EXPECT_TRUE(VPBB1.empty());}TEST(VPInstructionTest, moveAfter) {VPInstruction *I1 = new VPInstruction(0, {});VPInstruction *I2 = new VPInstruction(1, {});VPInstruction *I3 = new VPInstruction(2, {});VPBasicBlock VPBB1;VPBB1.appendRecipe(I1);VPBB1.appendRecipe(I2);VPBB1.appendRecipe(I3);I1->moveAfter(I2);CHECK_ITERATOR(VPBB1, I2, I1, I3);VPInstruction *I4 = new VPInstruction(4, {});VPInstruction *I5 = new VPInstruction(5, {});VPBasicBlock VPBB2;VPBB2.appendRecipe(I4);VPBB2.appendRecipe(I5);I3->moveAfter(I4);CHECK_ITERATOR(VPBB1, I2, I1);CHECK_ITERATOR(VPBB2, I4, I3, I5);EXPECT_EQ(I3->getParent(), I4->getParent());}TEST(VPInstructionTest, moveBefore) {VPInstruction *I1 = new VPInstruction(0, {});VPInstruction *I2 = new VPInstruction(1, {});VPInstruction *I3 = new VPInstruction(2, {});VPBasicBlock VPBB1;VPBB1.appendRecipe(I1);VPBB1.appendRecipe(I2);VPBB1.appendRecipe(I3);I1->moveBefore(VPBB1, I3->getIterator());CHECK_ITERATOR(VPBB1, I2, I1, I3);VPInstruction *I4 = new VPInstruction(4, {});VPInstruction *I5 = new VPInstruction(5, {});VPBasicBlock VPBB2;VPBB2.appendRecipe(I4);VPBB2.appendRecipe(I5);I3->moveBefore(VPBB2, I4->getIterator());CHECK_ITERATOR(VPBB1, I2, I1);CHECK_ITERATOR(VPBB2, I3, I4, I5);EXPECT_EQ(I3->getParent(), I4->getParent());VPBasicBlock VPBB3;I4->moveBefore(VPBB3, VPBB3.end());CHECK_ITERATOR(VPBB1, I2, I1);CHECK_ITERATOR(VPBB2, I3, I5);CHECK_ITERATOR(VPBB3, I4);EXPECT_EQ(&VPBB3, I4->getParent());}TEST(VPInstructionTest, setOperand) {VPValue *VPV1 = new VPValue();VPValue *VPV2 = new VPValue();VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2});EXPECT_EQ(1u, VPV1->getNumUsers());EXPECT_EQ(I1, *VPV1->user_begin());EXPECT_EQ(1u, VPV2->getNumUsers());EXPECT_EQ(I1, *VPV2->user_begin());// Replace operand 0 (VPV1) with VPV3.VPValue *VPV3 = new VPValue();I1->setOperand(0, VPV3);EXPECT_EQ(0u, VPV1->getNumUsers());EXPECT_EQ(1u, VPV2->getNumUsers());EXPECT_EQ(I1, *VPV2->user_begin());EXPECT_EQ(1u, VPV3->getNumUsers());EXPECT_EQ(I1, *VPV3->user_begin());// Replace operand 1 (VPV2) with VPV3.I1->setOperand(1, VPV3);EXPECT_EQ(0u, VPV1->getNumUsers());EXPECT_EQ(0u, VPV2->getNumUsers());EXPECT_EQ(2u, VPV3->getNumUsers());EXPECT_EQ(I1, *VPV3->user_begin());EXPECT_EQ(I1, *std::next(VPV3->user_begin()));// Replace operand 0 (VPV3) with VPV4.VPValue *VPV4 = new VPValue();I1->setOperand(0, VPV4);EXPECT_EQ(1u, VPV3->getNumUsers());EXPECT_EQ(I1, *VPV3->user_begin());EXPECT_EQ(I1, *VPV4->user_begin());// Replace operand 1 (VPV3) with VPV4.I1->setOperand(1, VPV4);EXPECT_EQ(0u, VPV3->getNumUsers());EXPECT_EQ(I1, *VPV4->user_begin());EXPECT_EQ(I1, *std::next(VPV4->user_begin()));delete I1;delete VPV1;delete VPV2;delete VPV3;delete VPV4;}TEST(VPInstructionTest, replaceAllUsesWith) {VPValue *VPV1 = new VPValue();VPValue *VPV2 = new VPValue();VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2});// Replace all uses of VPV1 with VPV3.VPValue *VPV3 = new VPValue();VPV1->replaceAllUsesWith(VPV3);EXPECT_EQ(VPV3, I1->getOperand(0));EXPECT_EQ(VPV2, I1->getOperand(1));EXPECT_EQ(0u, VPV1->getNumUsers());EXPECT_EQ(1u, VPV2->getNumUsers());EXPECT_EQ(I1, *VPV2->user_begin());EXPECT_EQ(1u, VPV3->getNumUsers());EXPECT_EQ(I1, *VPV3->user_begin());// Replace all uses of VPV2 with VPV3.VPV2->replaceAllUsesWith(VPV3);EXPECT_EQ(VPV3, I1->getOperand(0));EXPECT_EQ(VPV3, I1->getOperand(1));EXPECT_EQ(0u, VPV1->getNumUsers());EXPECT_EQ(0u, VPV2->getNumUsers());EXPECT_EQ(2u, VPV3->getNumUsers());EXPECT_EQ(I1, *VPV3->user_begin());// Replace all uses of VPV3 with VPV1.VPV3->replaceAllUsesWith(VPV1);EXPECT_EQ(VPV1, I1->getOperand(0));EXPECT_EQ(VPV1, I1->getOperand(1));EXPECT_EQ(2u, VPV1->getNumUsers());EXPECT_EQ(I1, *VPV1->user_begin());EXPECT_EQ(0u, VPV2->getNumUsers());EXPECT_EQ(0u, VPV3->getNumUsers());VPInstruction *I2 = new VPInstruction(0, {VPV1, VPV2});EXPECT_EQ(3u, VPV1->getNumUsers());VPV1->replaceAllUsesWith(VPV3);EXPECT_EQ(3u, VPV3->getNumUsers());delete I1;delete I2;delete VPV1;delete VPV2;delete VPV3;}TEST(VPInstructionTest, releaseOperandsAtDeletion) {VPValue *VPV1 = new VPValue();VPValue *VPV2 = new VPValue();VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2});EXPECT_EQ(1u, VPV1->getNumUsers());EXPECT_EQ(I1, *VPV1->user_begin());EXPECT_EQ(1u, VPV2->getNumUsers());EXPECT_EQ(I1, *VPV2->user_begin());delete I1;EXPECT_EQ(0u, VPV1->getNumUsers());EXPECT_EQ(0u, VPV2->getNumUsers());delete VPV1;delete VPV2;}TEST(VPBasicBlockTest, getPlan) {{VPBasicBlock *VPBB1 = new VPBasicBlock();VPBasicBlock *VPBB2 = new VPBasicBlock();VPBasicBlock *VPBB3 = new VPBasicBlock();VPBasicBlock *VPBB4 = new VPBasicBlock();// VPBB1// / \// VPBB2 VPBB3// \ /// VPBB4VPBlockUtils::connectBlocks(VPBB1, VPBB2);VPBlockUtils::connectBlocks(VPBB1, VPBB3);VPBlockUtils::connectBlocks(VPBB2, VPBB4);VPBlockUtils::connectBlocks(VPBB3, VPBB4);VPlan Plan;Plan.setEntry(VPBB1);EXPECT_EQ(&Plan, VPBB1->getPlan());EXPECT_EQ(&Plan, VPBB2->getPlan());EXPECT_EQ(&Plan, VPBB3->getPlan());EXPECT_EQ(&Plan, VPBB4->getPlan());}{// Region block is entry into VPlan.VPBasicBlock *R1BB1 = new VPBasicBlock();VPBasicBlock *R1BB2 = new VPBasicBlock();VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB2, "R1");VPBlockUtils::connectBlocks(R1BB1, R1BB2);VPlan Plan;Plan.setEntry(R1);EXPECT_EQ(&Plan, R1->getPlan());EXPECT_EQ(&Plan, R1BB1->getPlan());EXPECT_EQ(&Plan, R1BB2->getPlan());}{// VPBasicBlock is the entry into the VPlan, followed by a region.VPBasicBlock *R1BB1 = new VPBasicBlock();VPBasicBlock *R1BB2 = new VPBasicBlock();VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB2, "R1");VPBlockUtils::connectBlocks(R1BB1, R1BB2);VPBasicBlock *VPBB1 = new VPBasicBlock();VPBlockUtils::connectBlocks(VPBB1, R1);VPlan Plan;Plan.setEntry(VPBB1);EXPECT_EQ(&Plan, VPBB1->getPlan());EXPECT_EQ(&Plan, R1->getPlan());EXPECT_EQ(&Plan, R1BB1->getPlan());EXPECT_EQ(&Plan, R1BB2->getPlan());}{VPBasicBlock *R1BB1 = new VPBasicBlock();VPBasicBlock *R1BB2 = new VPBasicBlock();VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB2, "R1");VPBlockUtils::connectBlocks(R1BB1, R1BB2);VPBasicBlock *R2BB1 = new VPBasicBlock();VPBasicBlock *R2BB2 = new VPBasicBlock();VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2");VPBlockUtils::connectBlocks(R2BB1, R2BB2);VPBasicBlock *VPBB1 = new VPBasicBlock();VPBlockUtils::connectBlocks(VPBB1, R1);VPBlockUtils::connectBlocks(VPBB1, R2);VPBasicBlock *VPBB2 = new VPBasicBlock();VPBlockUtils::connectBlocks(R1, VPBB2);VPBlockUtils::connectBlocks(R2, VPBB2);VPlan Plan;Plan.setEntry(VPBB1);EXPECT_EQ(&Plan, VPBB1->getPlan());EXPECT_EQ(&Plan, R1->getPlan());EXPECT_EQ(&Plan, R1BB1->getPlan());EXPECT_EQ(&Plan, R1BB2->getPlan());EXPECT_EQ(&Plan, R2->getPlan());EXPECT_EQ(&Plan, R2BB1->getPlan());EXPECT_EQ(&Plan, R2BB2->getPlan());EXPECT_EQ(&Plan, VPBB2->getPlan());}}TEST(VPBasicBlockTest, TraversingIteratorTest) {{// VPBasicBlocks only// VPBB1// / \// VPBB2 VPBB3// \ /// VPBB4//VPBasicBlock *VPBB1 = new VPBasicBlock();VPBasicBlock *VPBB2 = new VPBasicBlock();VPBasicBlock *VPBB3 = new VPBasicBlock();VPBasicBlock *VPBB4 = new VPBasicBlock();VPBlockUtils::connectBlocks(VPBB1, VPBB2);VPBlockUtils::connectBlocks(VPBB1, VPBB3);VPBlockUtils::connectBlocks(VPBB2, VPBB4);VPBlockUtils::connectBlocks(VPBB3, VPBB4);VPBlockRecursiveTraversalWrapper<const VPBlockBase *> Start(VPBB1);SmallVector<const VPBlockBase *> FromIterator(depth_first(Start));EXPECT_EQ(4u, FromIterator.size());EXPECT_EQ(VPBB1, FromIterator[0]);EXPECT_EQ(VPBB2, FromIterator[1]);// Use Plan to properly clean up created blocks.VPlan Plan;Plan.setEntry(VPBB1);}{// 2 consecutive regions.// R1 {// \// R1BB1// / \ |--|// R1BB2 R1BB3 -|// \ /// R1BB4// }// |// R2 {// \// R2BB1// |// R2BB2//VPBasicBlock *R1BB1 = new VPBasicBlock();VPBasicBlock *R1BB2 = new VPBasicBlock();VPBasicBlock *R1BB3 = new VPBasicBlock();VPBasicBlock *R1BB4 = new VPBasicBlock();VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB4, "R1");R1BB2->setParent(R1);R1BB3->setParent(R1);VPBlockUtils::connectBlocks(R1BB1, R1BB2);VPBlockUtils::connectBlocks(R1BB1, R1BB3);VPBlockUtils::connectBlocks(R1BB2, R1BB4);VPBlockUtils::connectBlocks(R1BB3, R1BB4);// Cycle.VPBlockUtils::connectBlocks(R1BB3, R1BB3);VPBasicBlock *R2BB1 = new VPBasicBlock();VPBasicBlock *R2BB2 = new VPBasicBlock();VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2");VPBlockUtils::connectBlocks(R2BB1, R2BB2);VPBlockUtils::connectBlocks(R1, R2);// Depth-first.VPBlockRecursiveTraversalWrapper<VPBlockBase *> Start(R1);SmallVector<const VPBlockBase *> FromIterator(df_begin(Start),df_end(Start));EXPECT_EQ(8u, FromIterator.size());EXPECT_EQ(R1, FromIterator[0]);EXPECT_EQ(R1BB1, FromIterator[1]);EXPECT_EQ(R1BB2, FromIterator[2]);EXPECT_EQ(R1BB4, FromIterator[3]);EXPECT_EQ(R2, FromIterator[4]);EXPECT_EQ(R2BB1, FromIterator[5]);EXPECT_EQ(R2BB2, FromIterator[6]);EXPECT_EQ(R1BB3, FromIterator[7]);// const VPBasicBlocks only.FromIterator.clear();copy(VPBlockUtils::blocksOnly<const VPBasicBlock>(depth_first(Start)),std::back_inserter(FromIterator));EXPECT_EQ(6u, FromIterator.size());EXPECT_EQ(R1BB1, FromIterator[0]);EXPECT_EQ(R1BB2, FromIterator[1]);EXPECT_EQ(R1BB4, FromIterator[2]);EXPECT_EQ(R2BB1, FromIterator[3]);EXPECT_EQ(R2BB2, FromIterator[4]);EXPECT_EQ(R1BB3, FromIterator[5]);// VPRegionBlocks only.SmallVector<VPRegionBlock *> FromIteratorVPRegion(VPBlockUtils::blocksOnly<VPRegionBlock>(depth_first(Start)));EXPECT_EQ(2u, FromIteratorVPRegion.size());EXPECT_EQ(R1, FromIteratorVPRegion[0]);EXPECT_EQ(R2, FromIteratorVPRegion[1]);// Post-order.FromIterator.clear();copy(post_order(Start), std::back_inserter(FromIterator));EXPECT_EQ(8u, FromIterator.size());EXPECT_EQ(R2BB2, FromIterator[0]);EXPECT_EQ(R2BB1, FromIterator[1]);EXPECT_EQ(R2, FromIterator[2]);EXPECT_EQ(R1BB4, FromIterator[3]);EXPECT_EQ(R1BB2, FromIterator[4]);EXPECT_EQ(R1BB3, FromIterator[5]);EXPECT_EQ(R1BB1, FromIterator[6]);EXPECT_EQ(R1, FromIterator[7]);// Use Plan to properly clean up created blocks.VPlan Plan;Plan.setEntry(R1);}{// 2 nested regions.// VPBB1// |// R1 {// R1BB1// / \// R2 { |// \ |// R2BB1 |// | \ R1BB2// R2BB2-| |// \ |// R2BB3 |// } /// \ /// R1BB3// }// |// VPBB2//VPBasicBlock *R1BB1 = new VPBasicBlock("R1BB1");VPBasicBlock *R1BB2 = new VPBasicBlock("R1BB2");VPBasicBlock *R1BB3 = new VPBasicBlock("R1BB3");VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB3, "R1");VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1");VPBasicBlock *R2BB2 = new VPBasicBlock("R2BB2");VPBasicBlock *R2BB3 = new VPBasicBlock("R2BB3");VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB3, "R2");R2BB2->setParent(R2);VPBlockUtils::connectBlocks(R2BB1, R2BB2);VPBlockUtils::connectBlocks(R2BB2, R2BB1);VPBlockUtils::connectBlocks(R2BB2, R2BB3);R2->setParent(R1);VPBlockUtils::connectBlocks(R1BB1, R2);R1BB2->setParent(R1);VPBlockUtils::connectBlocks(R1BB1, R1BB2);VPBlockUtils::connectBlocks(R1BB2, R1BB3);VPBlockUtils::connectBlocks(R2, R1BB3);VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1");VPBlockUtils::connectBlocks(VPBB1, R1);VPBasicBlock *VPBB2 = new VPBasicBlock("VPBB2");VPBlockUtils::connectBlocks(R1, VPBB2);// Depth-first.VPBlockRecursiveTraversalWrapper<VPBlockBase *> Start(VPBB1);SmallVector<VPBlockBase *> FromIterator(depth_first(Start));EXPECT_EQ(10u, FromIterator.size());EXPECT_EQ(VPBB1, FromIterator[0]);EXPECT_EQ(R1, FromIterator[1]);EXPECT_EQ(R1BB1, FromIterator[2]);EXPECT_EQ(R2, FromIterator[3]);EXPECT_EQ(R2BB1, FromIterator[4]);EXPECT_EQ(R2BB2, FromIterator[5]);EXPECT_EQ(R2BB3, FromIterator[6]);EXPECT_EQ(R1BB3, FromIterator[7]);EXPECT_EQ(VPBB2, FromIterator[8]);EXPECT_EQ(R1BB2, FromIterator[9]);// Post-order.FromIterator.clear();FromIterator.append(po_begin(Start), po_end(Start));EXPECT_EQ(10u, FromIterator.size());EXPECT_EQ(VPBB2, FromIterator[0]);EXPECT_EQ(R1BB3, FromIterator[1]);EXPECT_EQ(R2BB3, FromIterator[2]);EXPECT_EQ(R2BB2, FromIterator[3]);EXPECT_EQ(R2BB1, FromIterator[4]);EXPECT_EQ(R2, FromIterator[5]);EXPECT_EQ(R1BB2, FromIterator[6]);EXPECT_EQ(R1BB1, FromIterator[7]);EXPECT_EQ(R1, FromIterator[8]);EXPECT_EQ(VPBB1, FromIterator[9]);// Use Plan to properly clean up created blocks.VPlan Plan;Plan.setEntry(VPBB1);}{// VPBB1// |// R1 {// \// R2 {// R2BB1// |// R2BB2// }//VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1");VPBasicBlock *R2BB2 = new VPBasicBlock("R2BB2");VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2");VPBlockUtils::connectBlocks(R2BB1, R2BB2);VPRegionBlock *R1 = new VPRegionBlock(R2, R2, "R1");R2->setParent(R1);VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1");VPBlockUtils::connectBlocks(VPBB1, R1);// Depth-first.VPBlockRecursiveTraversalWrapper<VPBlockBase *> Start(VPBB1);SmallVector<VPBlockBase *> FromIterator(depth_first(Start));EXPECT_EQ(5u, FromIterator.size());EXPECT_EQ(VPBB1, FromIterator[0]);EXPECT_EQ(R1, FromIterator[1]);EXPECT_EQ(R2, FromIterator[2]);EXPECT_EQ(R2BB1, FromIterator[3]);EXPECT_EQ(R2BB2, FromIterator[4]);// Post-order.FromIterator.clear();FromIterator.append(po_begin(Start), po_end(Start));EXPECT_EQ(5u, FromIterator.size());EXPECT_EQ(R2BB2, FromIterator[0]);EXPECT_EQ(R2BB1, FromIterator[1]);EXPECT_EQ(R2, FromIterator[2]);EXPECT_EQ(R1, FromIterator[3]);EXPECT_EQ(VPBB1, FromIterator[4]);// Use Plan to properly clean up created blocks.VPlan Plan;Plan.setEntry(VPBB1);}{// Nested regions with both R3 and R2 being exit nodes without successors.// The successors of R1 should be used.//// VPBB1// |// R1 {// \// R2 {// \// R2BB1// |// R3 {// R3BB1// }// }// |// VPBB2//VPBasicBlock *R3BB1 = new VPBasicBlock("R3BB1");VPRegionBlock *R3 = new VPRegionBlock(R3BB1, R3BB1, "R3");VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1");VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R3, "R2");R3->setParent(R2);VPBlockUtils::connectBlocks(R2BB1, R3);VPRegionBlock *R1 = new VPRegionBlock(R2, R2, "R1");R2->setParent(R1);VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1");VPBasicBlock *VPBB2 = new VPBasicBlock("VPBB2");VPBlockUtils::connectBlocks(VPBB1, R1);VPBlockUtils::connectBlocks(R1, VPBB2);// Depth-first.VPBlockRecursiveTraversalWrapper<VPBlockBase *> Start(VPBB1);SmallVector<VPBlockBase *> FromIterator(depth_first(Start));EXPECT_EQ(7u, FromIterator.size());EXPECT_EQ(VPBB1, FromIterator[0]);EXPECT_EQ(R1, FromIterator[1]);EXPECT_EQ(R2, FromIterator[2]);EXPECT_EQ(R2BB1, FromIterator[3]);EXPECT_EQ(R3, FromIterator[4]);EXPECT_EQ(R3BB1, FromIterator[5]);EXPECT_EQ(VPBB2, FromIterator[6]);SmallVector<VPBlockBase *> FromIteratorVPBB;copy(VPBlockUtils::blocksOnly<VPBasicBlock>(depth_first(Start)),std::back_inserter(FromIteratorVPBB));EXPECT_EQ(VPBB1, FromIteratorVPBB[0]);EXPECT_EQ(R2BB1, FromIteratorVPBB[1]);EXPECT_EQ(R3BB1, FromIteratorVPBB[2]);EXPECT_EQ(VPBB2, FromIteratorVPBB[3]);// Post-order.FromIterator.clear();copy(post_order(Start), std::back_inserter(FromIterator));EXPECT_EQ(7u, FromIterator.size());EXPECT_EQ(VPBB2, FromIterator[0]);EXPECT_EQ(R3BB1, FromIterator[1]);EXPECT_EQ(R3, FromIterator[2]);EXPECT_EQ(R2BB1, FromIterator[3]);EXPECT_EQ(R2, FromIterator[4]);EXPECT_EQ(R1, FromIterator[5]);EXPECT_EQ(VPBB1, FromIterator[6]);// Post-order, const VPRegionBlocks only.VPBlockRecursiveTraversalWrapper<const VPBlockBase *> StartConst(VPBB1);SmallVector<const VPRegionBlock *> FromIteratorVPRegion(VPBlockUtils::blocksOnly<const VPRegionBlock>(post_order(StartConst)));EXPECT_EQ(3u, FromIteratorVPRegion.size());EXPECT_EQ(R3, FromIteratorVPRegion[0]);EXPECT_EQ(R2, FromIteratorVPRegion[1]);EXPECT_EQ(R1, FromIteratorVPRegion[2]);// Post-order, VPBasicBlocks only.FromIterator.clear();copy(VPBlockUtils::blocksOnly<VPBasicBlock>(post_order(Start)),std::back_inserter(FromIterator));EXPECT_EQ(FromIterator.size(), 4u);EXPECT_EQ(VPBB2, FromIterator[0]);EXPECT_EQ(R3BB1, FromIterator[1]);EXPECT_EQ(R2BB1, FromIterator[2]);EXPECT_EQ(VPBB1, FromIterator[3]);// Use Plan to properly clean up created blocks.VPlan Plan;Plan.setEntry(VPBB1);}}#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)TEST(VPBasicBlockTest, print) {VPInstruction *I1 = new VPInstruction(Instruction::Add, {});VPInstruction *I2 = new VPInstruction(Instruction::Sub, {I1});VPInstruction *I3 = new VPInstruction(Instruction::Br, {I1, I2});VPBasicBlock *VPBB1 = new VPBasicBlock();VPBB1->appendRecipe(I1);VPBB1->appendRecipe(I2);VPBB1->appendRecipe(I3);VPBB1->setName("bb1");VPInstruction *I4 = new VPInstruction(Instruction::Mul, {I2, I1});VPInstruction *I5 = new VPInstruction(Instruction::Ret, {I4});VPBasicBlock *VPBB2 = new VPBasicBlock();VPBB2->appendRecipe(I4);VPBB2->appendRecipe(I5);VPBB2->setName("bb2");VPBlockUtils::connectBlocks(VPBB1, VPBB2);// Check printing an instruction without associated VPlan.{std::string I3Dump;raw_string_ostream OS(I3Dump);VPSlotTracker SlotTracker;I3->print(OS, "", SlotTracker);OS.flush();EXPECT_EQ("EMIT br <badref> <badref>", I3Dump);}VPlan Plan;Plan.setEntry(VPBB1);std::string FullDump;raw_string_ostream OS(FullDump);Plan.printDOT(OS);const char *ExpectedStr = R"(digraph VPlan {graph [labelloc=t, fontsize=30; label="Vectorization Plan"]node [shape=rect, fontname=Courier, fontsize=30]edge [fontname=Courier, fontsize=30]compound=trueN0 [label ="bb1:\l" +" EMIT vp\<%1\> = add\l" +" EMIT vp\<%2\> = sub vp\<%1\>\l" +" EMIT br vp\<%1\> vp\<%2\>\l" +"Successor(s): bb2\l"]N0 -> N1 [ label=""]N1 [label ="bb2:\l" +" EMIT vp\<%4\> = mul vp\<%2\> vp\<%1\>\l" +" EMIT ret vp\<%4\>\l" +"No successors\l"]})";EXPECT_EQ(ExpectedStr, FullDump);const char *ExpectedBlock1Str = R"(bb1:EMIT vp<%1> = addEMIT vp<%2> = sub vp<%1>EMIT br vp<%1> vp<%2>Successor(s): bb2)";std::string Block1Dump;raw_string_ostream OS1(Block1Dump);VPBB1->print(OS1);EXPECT_EQ(ExpectedBlock1Str, Block1Dump);// Ensure that numbering is good when dumping the second block in isolation.const char *ExpectedBlock2Str = R"(bb2:EMIT vp<%4> = mul vp<%2> vp<%1>EMIT ret vp<%4>No successors)";std::string Block2Dump;raw_string_ostream OS2(Block2Dump);VPBB2->print(OS2);EXPECT_EQ(ExpectedBlock2Str, Block2Dump);{std::string I3Dump;raw_string_ostream OS(I3Dump);VPSlotTracker SlotTracker(&Plan);I3->print(OS, "", SlotTracker);OS.flush();EXPECT_EQ("EMIT br vp<%1> vp<%2>", I3Dump);}{std::string I4Dump;raw_string_ostream OS(I4Dump);OS << *I4;OS.flush();EXPECT_EQ("EMIT vp<%4> = mul vp<%2> vp<%1>", I4Dump);}}#endifTEST(VPRecipeTest, CastVPInstructionToVPUser) {VPValue Op1;VPValue Op2;VPInstruction Recipe(Instruction::Add, {&Op1, &Op2});EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));EXPECT_EQ(&Recipe, BaseR);}TEST(VPRecipeTest, CastVPWidenRecipeToVPUser) {LLVMContext C;IntegerType *Int32 = IntegerType::get(C, 32);auto *AI =BinaryOperator::CreateAdd(UndefValue::get(Int32), UndefValue::get(Int32));VPValue Op1;VPValue Op2;SmallVector<VPValue *, 2> Args;Args.push_back(&Op1);Args.push_back(&Op1);VPWidenRecipe WidenR(*AI, make_range(Args.begin(), Args.end()));EXPECT_TRUE(isa<VPUser>(&WidenR));VPRecipeBase *WidenRBase = &WidenR;EXPECT_TRUE(isa<VPUser>(WidenRBase));EXPECT_EQ(&WidenR, WidenRBase);delete AI;}TEST(VPRecipeTest, CastVPWidenCallRecipeToVPUserAndVPDef) {LLVMContext C;IntegerType *Int32 = IntegerType::get(C, 32);FunctionType *FTy = FunctionType::get(Int32, false);auto *Call = CallInst::Create(FTy, UndefValue::get(FTy));VPValue Op1;VPValue Op2;SmallVector<VPValue *, 2> Args;Args.push_back(&Op1);Args.push_back(&Op2);VPWidenCallRecipe Recipe(*Call, make_range(Args.begin(), Args.end()));EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));EXPECT_EQ(&Recipe, BaseR);VPValue *VPV = &Recipe;EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDef()));EXPECT_EQ(&Recipe, dyn_cast<VPRecipeBase>(VPV->getDef()));delete Call;}TEST(VPRecipeTest, CastVPWidenSelectRecipeToVPUserAndVPDef) {LLVMContext C;IntegerType *Int1 = IntegerType::get(C, 1);IntegerType *Int32 = IntegerType::get(C, 32);auto *SelectI = SelectInst::Create(UndefValue::get(Int1), UndefValue::get(Int32), UndefValue::get(Int32));VPValue Op1;VPValue Op2;VPValue Op3;SmallVector<VPValue *, 4> Args;Args.push_back(&Op1);Args.push_back(&Op2);Args.push_back(&Op3);VPWidenSelectRecipe WidenSelectR(*SelectI,make_range(Args.begin(), Args.end()), false);EXPECT_TRUE(isa<VPUser>(&WidenSelectR));VPRecipeBase *BaseR = &WidenSelectR;EXPECT_TRUE(isa<VPUser>(BaseR));EXPECT_EQ(&WidenSelectR, BaseR);VPValue *VPV = &WidenSelectR;EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDef()));EXPECT_EQ(&WidenSelectR, dyn_cast<VPRecipeBase>(VPV->getDef()));delete SelectI;}TEST(VPRecipeTest, CastVPWidenGEPRecipeToVPUserAndVPDef) {LLVMContext C;IntegerType *Int32 = IntegerType::get(C, 32);PointerType *Int32Ptr = PointerType::get(Int32, 0);auto *GEP = GetElementPtrInst::Create(Int32, UndefValue::get(Int32Ptr),UndefValue::get(Int32));VPValue Op1;VPValue Op2;SmallVector<VPValue *, 4> Args;Args.push_back(&Op1);Args.push_back(&Op2);VPWidenGEPRecipe Recipe(GEP, make_range(Args.begin(), Args.end()));EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));EXPECT_EQ(&Recipe, BaseR);VPValue *VPV = &Recipe;EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDef()));EXPECT_EQ(&Recipe, dyn_cast<VPRecipeBase>(VPV->getDef()));delete GEP;}TEST(VPRecipeTest, CastVPBlendRecipeToVPUser) {LLVMContext C;IntegerType *Int32 = IntegerType::get(C, 32);auto *Phi = PHINode::Create(Int32, 1);VPValue Op1;VPValue Op2;SmallVector<VPValue *, 4> Args;Args.push_back(&Op1);Args.push_back(&Op2);VPBlendRecipe Recipe(Phi, Args);EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));delete Phi;}TEST(VPRecipeTest, CastVPInterleaveRecipeToVPUser) {LLVMContext C;VPValue Addr;VPValue Mask;InterleaveGroup<Instruction> IG(4, false, Align(4));VPInterleaveRecipe Recipe(&IG, &Addr, {}, &Mask);EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));EXPECT_EQ(&Recipe, BaseR);}TEST(VPRecipeTest, CastVPReplicateRecipeToVPUser) {LLVMContext C;VPValue Op1;VPValue Op2;SmallVector<VPValue *, 4> Args;Args.push_back(&Op1);Args.push_back(&Op2);VPReplicateRecipe Recipe(nullptr, make_range(Args.begin(), Args.end()), true,false);EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));}TEST(VPRecipeTest, CastVPBranchOnMaskRecipeToVPUser) {LLVMContext C;VPValue Mask;VPBranchOnMaskRecipe Recipe(&Mask);EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));EXPECT_EQ(&Recipe, BaseR);}TEST(VPRecipeTest, CastVPWidenMemoryInstructionRecipeToVPUserAndVPDef) {LLVMContext C;IntegerType *Int32 = IntegerType::get(C, 32);PointerType *Int32Ptr = PointerType::get(Int32, 0);auto *Load =new LoadInst(Int32, UndefValue::get(Int32Ptr), "", false, Align(1));VPValue Addr;VPValue Mask;VPWidenMemoryInstructionRecipe Recipe(*Load, &Addr, &Mask, true, false);EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));EXPECT_EQ(&Recipe, BaseR);VPValue *VPV = Recipe.getVPSingleValue();EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDef()));EXPECT_EQ(&Recipe, dyn_cast<VPRecipeBase>(VPV->getDef()));delete Load;}TEST(VPRecipeTest, MayHaveSideEffectsAndMayReadWriteMemory) {LLVMContext C;IntegerType *Int1 = IntegerType::get(C, 1);IntegerType *Int32 = IntegerType::get(C, 32);PointerType *Int32Ptr = PointerType::get(Int32, 0);{auto *AI = BinaryOperator::CreateAdd(UndefValue::get(Int32),UndefValue::get(Int32));VPValue Op1;VPValue Op2;SmallVector<VPValue *, 2> Args;Args.push_back(&Op1);Args.push_back(&Op1);VPWidenRecipe Recipe(*AI, make_range(Args.begin(), Args.end()));EXPECT_FALSE(Recipe.mayHaveSideEffects());EXPECT_FALSE(Recipe.mayReadFromMemory());EXPECT_FALSE(Recipe.mayWriteToMemory());EXPECT_FALSE(Recipe.mayReadOrWriteMemory());delete AI;}{auto *SelectI = SelectInst::Create(UndefValue::get(Int1), UndefValue::get(Int32), UndefValue::get(Int32));VPValue Op1;VPValue Op2;VPValue Op3;SmallVector<VPValue *, 4> Args;Args.push_back(&Op1);Args.push_back(&Op2);Args.push_back(&Op3);VPWidenSelectRecipe Recipe(*SelectI, make_range(Args.begin(), Args.end()),false);EXPECT_FALSE(Recipe.mayHaveSideEffects());EXPECT_FALSE(Recipe.mayReadFromMemory());EXPECT_FALSE(Recipe.mayWriteToMemory());EXPECT_FALSE(Recipe.mayReadOrWriteMemory());delete SelectI;}{auto *GEP = GetElementPtrInst::Create(Int32, UndefValue::get(Int32Ptr),UndefValue::get(Int32));VPValue Op1;VPValue Op2;SmallVector<VPValue *, 4> Args;Args.push_back(&Op1);Args.push_back(&Op2);VPWidenGEPRecipe Recipe(GEP, make_range(Args.begin(), Args.end()));EXPECT_FALSE(Recipe.mayHaveSideEffects());EXPECT_FALSE(Recipe.mayReadFromMemory());EXPECT_FALSE(Recipe.mayWriteToMemory());EXPECT_FALSE(Recipe.mayReadOrWriteMemory());delete GEP;}{VPValue Mask;VPBranchOnMaskRecipe Recipe(&Mask);EXPECT_TRUE(Recipe.mayHaveSideEffects());EXPECT_FALSE(Recipe.mayReadFromMemory());EXPECT_FALSE(Recipe.mayWriteToMemory());EXPECT_FALSE(Recipe.mayReadOrWriteMemory());}{VPValue ChainOp;VPValue VecOp;VPValue CondOp;VPReductionRecipe Recipe(nullptr, nullptr, &ChainOp, &CondOp, &VecOp,nullptr);EXPECT_FALSE(Recipe.mayHaveSideEffects());EXPECT_FALSE(Recipe.mayReadFromMemory());EXPECT_FALSE(Recipe.mayWriteToMemory());EXPECT_FALSE(Recipe.mayReadOrWriteMemory());}{auto *Load =new LoadInst(Int32, UndefValue::get(Int32Ptr), "", false, Align(1));VPValue Addr;VPValue Mask;VPWidenMemoryInstructionRecipe Recipe(*Load, &Addr, &Mask, true, false);EXPECT_TRUE(Recipe.mayHaveSideEffects());EXPECT_TRUE(Recipe.mayReadFromMemory());EXPECT_FALSE(Recipe.mayWriteToMemory());EXPECT_TRUE(Recipe.mayReadOrWriteMemory());delete Load;}{auto *Store = new StoreInst(UndefValue::get(Int32),UndefValue::get(Int32Ptr), false, Align(1));VPValue Addr;VPValue Mask;VPValue StoredV;VPWidenMemoryInstructionRecipe Recipe(*Store, &Addr, &StoredV, &Mask, false,false);EXPECT_TRUE(Recipe.mayHaveSideEffects());EXPECT_FALSE(Recipe.mayReadFromMemory());EXPECT_TRUE(Recipe.mayWriteToMemory());EXPECT_TRUE(Recipe.mayReadOrWriteMemory());delete Store;}{FunctionType *FTy = FunctionType::get(Int32, false);auto *Call = CallInst::Create(FTy, UndefValue::get(FTy));VPValue Op1;VPValue Op2;SmallVector<VPValue *, 2> Args;Args.push_back(&Op1);Args.push_back(&Op2);VPWidenCallRecipe Recipe(*Call, make_range(Args.begin(), Args.end()));EXPECT_TRUE(Recipe.mayHaveSideEffects());EXPECT_TRUE(Recipe.mayReadFromMemory());EXPECT_TRUE(Recipe.mayWriteToMemory());EXPECT_TRUE(Recipe.mayReadOrWriteMemory());delete Call;}// The initial implementation is conservative with respect to VPInstructions.{VPValue Op1;VPValue Op2;VPInstruction VPInst(Instruction::Add, {&Op1, &Op2});VPRecipeBase &Recipe = VPInst;EXPECT_TRUE(Recipe.mayHaveSideEffects());EXPECT_TRUE(Recipe.mayReadFromMemory());EXPECT_TRUE(Recipe.mayWriteToMemory());EXPECT_TRUE(Recipe.mayReadOrWriteMemory());}}#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)TEST(VPRecipeTest, dump) {VPlan Plan;VPBasicBlock *VPBB1 = new VPBasicBlock();Plan.setEntry(VPBB1);LLVMContext C;IntegerType *Int32 = IntegerType::get(C, 32);auto *AI =BinaryOperator::CreateAdd(UndefValue::get(Int32), UndefValue::get(Int32));AI->setName("a");SmallVector<VPValue *, 2> Args;VPValue *ExtVPV1 = Plan.getOrAddExternalDef(ConstantInt::get(Int32, 1));VPValue *ExtVPV2 = Plan.getOrAddExternalDef(ConstantInt::get(Int32, 2));Args.push_back(ExtVPV1);Args.push_back(ExtVPV2);VPWidenRecipe *WidenR =new VPWidenRecipe(*AI, make_range(Args.begin(), Args.end()));VPBB1->appendRecipe(WidenR);{// Use EXPECT_EXIT to capture stderr and compare against expected output.//// Test VPValue::dump().VPValue *VPV = WidenR;EXPECT_EXIT({VPV->dump();exit(0);},testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");// Test VPRecipeBase::dump().VPRecipeBase *R = WidenR;EXPECT_EXIT({R->dump();exit(0);},testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");// Test VPDef::dump().VPDef *D = WidenR;EXPECT_EXIT({D->dump();exit(0);},testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");}delete AI;}#endifTEST(VPRecipeTest, CastVPReductionRecipeToVPUser) {LLVMContext C;VPValue ChainOp;VPValue VecOp;VPValue CondOp;VPReductionRecipe Recipe(nullptr, nullptr, &ChainOp, &CondOp, &VecOp,nullptr);EXPECT_TRUE(isa<VPUser>(&Recipe));VPRecipeBase *BaseR = &Recipe;EXPECT_TRUE(isa<VPUser>(BaseR));}struct VPDoubleValueDef : public VPRecipeBase {VPDoubleValueDef(ArrayRef<VPValue *> Operands) : VPRecipeBase(99, Operands) {new VPValue(nullptr, this);new VPValue(nullptr, this);}void execute(struct VPTransformState &State) override{};#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)void print(raw_ostream &O, const Twine &Indent,VPSlotTracker &SlotTracker) const override {}#endif};TEST(VPDoubleValueDefTest, traverseUseLists) {// Check that the def-use chains of a multi-def can be traversed in both// directions.// Create a new VPDef which defines 2 values and has 2 operands.VPInstruction Op0(20, {});VPInstruction Op1(30, {});VPDoubleValueDef DoubleValueDef({&Op0, &Op1});// Create a new users of the defined values.VPInstruction I1(1, {DoubleValueDef.getVPValue(0), DoubleValueDef.getVPValue(1)});VPInstruction I2(2, {DoubleValueDef.getVPValue(0)});VPInstruction I3(3, {DoubleValueDef.getVPValue(1)});// Check operands of the VPDef (traversing upwards).SmallVector<VPValue *, 4> DoubleOperands(DoubleValueDef.op_begin(),DoubleValueDef.op_end());EXPECT_EQ(2u, DoubleOperands.size());EXPECT_EQ(&Op0, DoubleOperands[0]);EXPECT_EQ(&Op1, DoubleOperands[1]);// Check users of the defined values (traversing downwards).SmallVector<VPUser *, 4> DoubleValueDefV0Users(DoubleValueDef.getVPValue(0)->user_begin(),DoubleValueDef.getVPValue(0)->user_end());EXPECT_EQ(2u, DoubleValueDefV0Users.size());EXPECT_EQ(&I1, DoubleValueDefV0Users[0]);EXPECT_EQ(&I2, DoubleValueDefV0Users[1]);SmallVector<VPUser *, 4> DoubleValueDefV1Users(DoubleValueDef.getVPValue(1)->user_begin(),DoubleValueDef.getVPValue(1)->user_end());EXPECT_EQ(2u, DoubleValueDefV1Users.size());EXPECT_EQ(&I1, DoubleValueDefV1Users[0]);EXPECT_EQ(&I3, DoubleValueDefV1Users[1]);// Now check that we can get the right VPDef for each defined value.EXPECT_EQ(&DoubleValueDef, I1.getOperand(0)->getDef());EXPECT_EQ(&DoubleValueDef, I1.getOperand(1)->getDef());EXPECT_EQ(&DoubleValueDef, I2.getOperand(0)->getDef());EXPECT_EQ(&DoubleValueDef, I3.getOperand(0)->getDef());}} // namespace} // namespace llvm
//===- llvm/unittest/Transforms/Vectorize/VPlanSlpTest.cpp ---------------===////// 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////===----------------------------------------------------------------------===//#include "../lib/Transforms/Vectorize/VPlan.h"#include "../lib/Transforms/Vectorize/VPlanHCFGBuilder.h"#include "VPlanTestBase.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/Analysis/VectorUtils.h"#include "gtest/gtest.h"namespace llvm {namespace {class VPlanSlpTest : public VPlanTestBase {protected:TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;DataLayout DL;std::unique_ptr<AssumptionCache> AC;std::unique_ptr<ScalarEvolution> SE;std::unique_ptr<AAResults> AARes;std::unique_ptr<BasicAAResult> BasicAA;std::unique_ptr<LoopAccessInfo> LAI;std::unique_ptr<PredicatedScalarEvolution> PSE;std::unique_ptr<InterleavedAccessInfo> IAI;VPlanSlpTest(): TLII(), TLI(TLII),DL("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-""f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:""16:32:64-S128") {}VPInterleavedAccessInfo getInterleavedAccessInfo(Function &F, Loop *L,VPlan &Plan) {AC.reset(new AssumptionCache(F));SE.reset(new ScalarEvolution(F, TLI, *AC, *DT, *LI));BasicAA.reset(new BasicAAResult(DL, F, TLI, *AC, &*DT));AARes.reset(new AAResults(TLI));AARes->addAAResult(*BasicAA);PSE.reset(new PredicatedScalarEvolution(*SE, *L));LAI.reset(new LoopAccessInfo(L, &*SE, &TLI, &*AARes, &*DT, &*LI));IAI.reset(new InterleavedAccessInfo(*PSE, L, &*DT, &*LI, &*LAI));IAI->analyzeInterleaving(false);return {Plan, *IAI};}};TEST_F(VPlanSlpTest, testSlpSimple_2) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""%struct.Test3 = type { i32, i32, i32 }\n""%struct.Test4xi8 = type { i8, i8, i8 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %add0 = add nsw i32 %vA0, %vB0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %add1 = add nsw i32 %vA1, %vB1\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" store i32 %add0, i32* %C0, align 4\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));VPlanSlp Slp(VPIAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);EXPECT_EQ(64u, Slp.getWidestBundleBits());EXPECT_EQ(VPInstruction::SLPStore, CombinedStore->getOpcode());auto *CombinedAdd = cast<VPInstruction>(CombinedStore->getOperand(0));EXPECT_EQ(Instruction::Add, CombinedAdd->getOpcode());auto *CombinedLoadA = cast<VPInstruction>(CombinedAdd->getOperand(0));auto *CombinedLoadB = cast<VPInstruction>(CombinedAdd->getOperand(1));EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadB->getOpcode());delete CombinedStore;delete CombinedAdd;delete CombinedLoadA;delete CombinedLoadB;}TEST_F(VPlanSlpTest, testSlpSimple_3) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""%struct.Test3 = type { i32, i32, i32 }\n""%struct.Test4xi8 = type { i8, i8, i8 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr %struct.Test, %struct.Test* %A, i64 "" %indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "" %indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %add0 = add nsw i32 %vA0, %vB0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "" %indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "" %indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %add1 = add nsw i32 %vA1, %vB1\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "" %indvars.iv, i32 0\n"" store i32 %add0, i32* %C0, align 4\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "" %indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPlanSlp Slp(VPIAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);EXPECT_EQ(64u, Slp.getWidestBundleBits());EXPECT_EQ(VPInstruction::SLPStore, CombinedStore->getOpcode());auto *CombinedAdd = cast<VPInstruction>(CombinedStore->getOperand(0));EXPECT_EQ(Instruction::Add, CombinedAdd->getOpcode());auto *CombinedLoadA = cast<VPInstruction>(CombinedAdd->getOperand(0));auto *CombinedLoadB = cast<VPInstruction>(CombinedAdd->getOperand(1));EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadB->getOpcode());VPInstruction *GetA = cast<VPInstruction>(&*std::next(Body->begin(), 1));VPInstruction *GetB = cast<VPInstruction>(&*std::next(Body->begin(), 3));EXPECT_EQ(GetA, CombinedLoadA->getOperand(0));EXPECT_EQ(GetB, CombinedLoadB->getOperand(0));delete CombinedStore;delete CombinedAdd;delete CombinedLoadA;delete CombinedLoadB;}TEST_F(VPlanSlpTest, testSlpReuse_1) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %add0 = add nsw i32 %vA0, %vA0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %add1 = add nsw i32 %vA1, %vA1\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" store i32 %add0, i32* %C0, align 4\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 8));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 10));VPlanSlp Slp(VPIAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);EXPECT_EQ(64u, Slp.getWidestBundleBits());EXPECT_EQ(VPInstruction::SLPStore, CombinedStore->getOpcode());auto *CombinedAdd = cast<VPInstruction>(CombinedStore->getOperand(0));EXPECT_EQ(Instruction::Add, CombinedAdd->getOpcode());auto *CombinedLoadA = cast<VPInstruction>(CombinedAdd->getOperand(0));EXPECT_EQ(CombinedLoadA, CombinedAdd->getOperand(1));EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());delete CombinedStore;delete CombinedAdd;delete CombinedLoadA;}TEST_F(VPlanSlpTest, testSlpReuse_2) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %add0 = add nsw i32 %vA0, %vA0\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" store i32 %add0, i32* %C0, align 4\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %add1 = add nsw i32 %vA1, %vA1\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %use = add i32 %vA1, 1\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 5));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 10));VPlanSlp Slp(VPIAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};Slp.buildGraph(StoreRoot);EXPECT_FALSE(Slp.isCompletelySLP());}static void checkReorderExample(VPInstruction *Store1, VPInstruction *Store2,VPBasicBlock *Body,VPInterleavedAccessInfo &&IAI) {VPlanSlp Slp(IAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);EXPECT_TRUE(Slp.isCompletelySLP());EXPECT_EQ(CombinedStore->getOpcode(), VPInstruction::SLPStore);VPInstruction *CombinedAdd =cast<VPInstruction>(CombinedStore->getOperand(0));EXPECT_EQ(CombinedAdd->getOpcode(), Instruction::Add);VPInstruction *CombinedMulAB =cast<VPInstruction>(CombinedAdd->getOperand(0));VPInstruction *CombinedMulCD =cast<VPInstruction>(CombinedAdd->getOperand(1));EXPECT_EQ(CombinedMulAB->getOpcode(), Instruction::Mul);VPInstruction *CombinedLoadA =cast<VPInstruction>(CombinedMulAB->getOperand(0));EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());VPInstruction *LoadvA0 = cast<VPInstruction>(&*std::next(Body->begin(), 2));VPInstruction *LoadvA1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));EXPECT_EQ(LoadvA0->getOperand(0), CombinedLoadA->getOperand(0));EXPECT_EQ(LoadvA1->getOperand(0), CombinedLoadA->getOperand(1));VPInstruction *CombinedLoadB =cast<VPInstruction>(CombinedMulAB->getOperand(1));EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadB->getOpcode());VPInstruction *LoadvB0 = cast<VPInstruction>(&*std::next(Body->begin(), 4));VPInstruction *LoadvB1 = cast<VPInstruction>(&*std::next(Body->begin(), 14));EXPECT_EQ(LoadvB0->getOperand(0), CombinedLoadB->getOperand(0));EXPECT_EQ(LoadvB1->getOperand(0), CombinedLoadB->getOperand(1));EXPECT_EQ(CombinedMulCD->getOpcode(), Instruction::Mul);VPInstruction *CombinedLoadC =cast<VPInstruction>(CombinedMulCD->getOperand(0));EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadC->getOpcode());VPInstruction *LoadvC0 = cast<VPInstruction>(&*std::next(Body->begin(), 7));VPInstruction *LoadvC1 = cast<VPInstruction>(&*std::next(Body->begin(), 17));EXPECT_EQ(LoadvC0->getOperand(0), CombinedLoadC->getOperand(0));EXPECT_EQ(LoadvC1->getOperand(0), CombinedLoadC->getOperand(1));VPInstruction *CombinedLoadD =cast<VPInstruction>(CombinedMulCD->getOperand(1));EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadD->getOpcode());VPInstruction *LoadvD0 = cast<VPInstruction>(&*std::next(Body->begin(), 9));VPInstruction *LoadvD1 = cast<VPInstruction>(&*std::next(Body->begin(), 19));EXPECT_EQ(LoadvD0->getOperand(0), CombinedLoadD->getOperand(0));EXPECT_EQ(LoadvD1->getOperand(0), CombinedLoadD->getOperand(1));delete CombinedStore;delete CombinedAdd;delete CombinedMulAB;delete CombinedMulCD;delete CombinedLoadA;delete CombinedLoadB;delete CombinedLoadC;delete CombinedLoadD;}TEST_F(VPlanSlpTest, testSlpReorder_1) {LLVMContext Ctx;const char *ModuleString ="%struct.Test = type { i32, i32 }\n""define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* ""%C, %struct.Test* %D, %struct.Test* %E) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %mul11 = mul nsw i32 %vA0, %vB0\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" %vC0 = load i32, i32* %C0, align 4\n"" %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 0\n"" %vD0 = load i32, i32* %D0, align 4\n"" %mul12 = mul nsw i32 %vC0, %vD0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %mul21 = mul nsw i32 %vA1, %vB1\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" %vC1 = load i32, i32* %C1, align 4\n"" %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 1\n"" %vD1 = load i32, i32* %D1, align 4\n"" %mul22 = mul nsw i32 %vC1, %vD1\n"" %add1 = add nsw i32 %mul11, %mul12\n"" %add2 = add nsw i32 %mul22, %mul21\n"" %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 0\n"" store i32 %add1, i32* %E0, align 4\n"" %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 1\n"" store i32 %add2, i32* %E1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x3");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));checkReorderExample(Store1, Store2, Body,getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan));}TEST_F(VPlanSlpTest, testSlpReorder_2) {LLVMContext Ctx;const char *ModuleString ="%struct.Test = type { i32, i32 }\n""define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* ""%C, %struct.Test* %D, %struct.Test* %E) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %mul11 = mul nsw i32 %vA0, %vB0\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" %vC0 = load i32, i32* %C0, align 4\n"" %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 0\n"" %vD0 = load i32, i32* %D0, align 4\n"" %mul12 = mul nsw i32 %vC0, %vD0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %mul21 = mul nsw i32 %vB1, %vA1\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" %vC1 = load i32, i32* %C1, align 4\n"" %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 1\n"" %vD1 = load i32, i32* %D1, align 4\n"" %mul22 = mul nsw i32 %vD1, %vC1\n"" %add1 = add nsw i32 %mul11, %mul12\n"" %add2 = add nsw i32 %mul22, %mul21\n"" %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 0\n"" store i32 %add1, i32* %E0, align 4\n"" %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 1\n"" store i32 %add2, i32* %E1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x3");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));checkReorderExample(Store1, Store2, Body,getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan));}TEST_F(VPlanSlpTest, testSlpReorder_3) {LLVMContext Ctx;const char *ModuleString ="%struct.Test = type { i32, i32 }\n""define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* ""%C, %struct.Test* %D, %struct.Test* %E) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %mul11 = mul nsw i32 %vA1, %vB0\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" %vC0 = load i32, i32* %C0, align 4\n"" %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 0\n"" %vD0 = load i32, i32* %D0, align 4\n"" %mul12 = mul nsw i32 %vC0, %vD0\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %mul21 = mul nsw i32 %vB1, %vA0\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" %vC1 = load i32, i32* %C1, align 4\n"" %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 1\n"" %vD1 = load i32, i32* %D1, align 4\n"" %mul22 = mul nsw i32 %vD1, %vC1\n"" %add1 = add nsw i32 %mul11, %mul12\n"" %add2 = add nsw i32 %mul22, %mul21\n"" %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 0\n"" store i32 %add1, i32* %E0, align 4\n"" %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 1\n"" store i32 %add2, i32* %E1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x3");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPlanSlp Slp(VPIAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));// FIXME Need to select better first value for lane0.EXPECT_FALSE(Slp.isCompletelySLP());}TEST_F(VPlanSlpTest, testSlpReorder_4) {LLVMContext Ctx;const char *ModuleString ="%struct.Test = type { i32, i32 }\n""define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* ""%C, %struct.Test* %D, %struct.Test* %E) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %mul11 = mul nsw i32 %vA0, %vB0\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" %vC0 = load i32, i32* %C0, align 4\n"" %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 0\n"" %vD0 = load i32, i32* %D0, align 4\n"" %mul12 = mul nsw i32 %vC0, %vD0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %mul21 = mul nsw i32 %vA1, %vB1\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" %vC1 = load i32, i32* %C1, align 4\n"" %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 ""%indvars.iv, i32 1\n"" %vD1 = load i32, i32* %D1, align 4\n"" %mul22 = mul nsw i32 %vC1, %vD1\n"" %add1 = add nsw i32 %mul11, %mul12\n"" %add2 = add nsw i32 %mul22, %mul21\n"" %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 0\n"" store i32 %add1, i32* %E0, align 4\n"" %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 ""%indvars.iv, i32 1\n"" store i32 %add2, i32* %E1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x3");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));checkReorderExample(Store1, Store2, Body,getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan));}// Make sure we do not combine instructions with operands in different BBs.TEST_F(VPlanSlpTest, testInstrsInDifferentBBs) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""%struct.Test3 = type { i32, i32, i32 }\n""%struct.Test4xi8 = type { i8, i8, i8 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %add0 = add nsw i32 %vA0, %vB0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" br label %bb2\n""bb2:\n"" %vB1 = load i32, i32* %B1, align 4\n"" %add1 = add nsw i32 %vA1, %vB1\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" store i32 %add0, i32* %C0, align 4\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPBasicBlock *BB2 = Body->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(BB2->begin(), 3));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(BB2->begin(), 5));VPlanSlp Slp(VPIAI, *BB2);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));EXPECT_EQ(0u, Slp.getWidestBundleBits());}// Make sure we do not combine instructions with operands in different BBs.TEST_F(VPlanSlpTest, testInstrsInDifferentBBs2) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""%struct.Test3 = type { i32, i32, i32 }\n""%struct.Test4xi8 = type { i8, i8, i8 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %add0 = add nsw i32 %vA0, %vB0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %add1 = add nsw i32 %vA1, %vB1\n"" br label %bb2\n""bb2:\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" store i32 %add0, i32* %C0, align 4\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPBasicBlock *BB2 = Body->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(BB2->begin(), 1));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(BB2->begin(), 3));VPlanSlp Slp(VPIAI, *BB2);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));EXPECT_EQ(0u, Slp.getWidestBundleBits());}TEST_F(VPlanSlpTest, testSlpAtomicLoad) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""%struct.Test3 = type { i32, i32, i32 }\n""%struct.Test4xi8 = type { i8, i8, i8 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load atomic i32, i32* %A0 monotonic, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %add0 = add nsw i32 %vA0, %vB0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %add1 = add nsw i32 %vA1, %vB1\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" store i32 %add0, i32* %C0, align 4\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));VPlanSlp Slp(VPIAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));EXPECT_FALSE(Slp.isCompletelySLP());}TEST_F(VPlanSlpTest, testSlpAtomicStore) {const char *ModuleString ="%struct.Test = type { i32, i32 }\n""%struct.Test3 = type { i32, i32, i32 }\n""%struct.Test4xi8 = type { i8, i8, i8 }\n""define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* ""nocapture readonly %B, %struct.Test* nocapture %C) {\n""entry:\n"" br label %for.body\n""for.body: ; preds = %for.body, ""%entry\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 0\n"" %vA0 = load i32, i32* %A0, align 4\n"" %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 0\n"" %vB0 = load i32, i32* %B0, align 4\n"" %add0 = add nsw i32 %vA0, %vB0\n"" %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 ""%indvars.iv, i32 1\n"" %vA1 = load i32, i32* %A1, align 4\n"" %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 ""%indvars.iv, i32 1\n"" %vB1 = load i32, i32* %B1, align 4\n"" %add1 = add nsw i32 %vA1, %vB1\n"" %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 0\n"" store atomic i32 %add0, i32* %C0 monotonic, align 4\n"" %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 ""%indvars.iv, i32 1\n"" store i32 %add1, i32* %C1, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"" br i1 %exitcond, label %for.cond.cleanup, label %for.body\n""for.cond.cleanup: ; preds = %for.body\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("add_x2");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));VPlanSlp Slp(VPIAI, *Body);SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};Slp.buildGraph(StoreRoot);EXPECT_FALSE(Slp.isCompletelySLP());}} // namespace} // namespace llvm
//===- llvm/unittest/Transforms/Vectorize/VPlanHCFGTest.cpp ---------------===////// 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////===----------------------------------------------------------------------===//#include "../lib/Transforms/Vectorize/VPlan.h"#include "../lib/Transforms/Vectorize/VPlanTransforms.h"#include "VPlanTestBase.h"#include "gtest/gtest.h"#include <string>namespace llvm {namespace {class VPlanHCFGTest : public VPlanTestBase {};TEST_F(VPlanHCFGTest, testBuildHCFGInnerLoop) {const char *ModuleString ="define void @f(i32* %A, i64 %N) {\n""entry:\n"" br label %for.body\n""for.body:\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %arr.idx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv\n"" %l1 = load i32, i32* %arr.idx, align 4\n"" %res = add i32 %l1, 10\n"" store i32 %res, i32* %arr.idx, align 4\n"" %indvars.iv.next = add i64 %indvars.iv, 1\n"" %exitcond = icmp ne i64 %indvars.iv.next, %N\n"" br i1 %exitcond, label %for.body, label %for.end\n""for.end:\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("f");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);VPBasicBlock *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());EXPECT_EQ(0u, Entry->getNumPredecessors());EXPECT_EQ(1u, Entry->getNumSuccessors());// Check that the region following the preheader is a single basic-block// region (loop).VPBasicBlock *VecBB = Entry->getSingleSuccessor()->getEntryBasicBlock();EXPECT_EQ(8u, VecBB->size());EXPECT_EQ(0u, VecBB->getNumPredecessors());EXPECT_EQ(0u, VecBB->getNumSuccessors());EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB);EXPECT_EQ(VecBB->getParent()->getExitingBasicBlock(), VecBB);EXPECT_EQ(&*Plan, VecBB->getPlan());auto Iter = VecBB->begin();VPWidenPHIRecipe *Phi = dyn_cast<VPWidenPHIRecipe>(&*Iter++);EXPECT_NE(nullptr, Phi);VPInstruction *Idx = dyn_cast<VPInstruction>(&*Iter++);EXPECT_EQ(Instruction::GetElementPtr, Idx->getOpcode());EXPECT_EQ(2u, Idx->getNumOperands());EXPECT_EQ(Phi, Idx->getOperand(1));VPInstruction *Load = dyn_cast<VPInstruction>(&*Iter++);EXPECT_EQ(Instruction::Load, Load->getOpcode());EXPECT_EQ(1u, Load->getNumOperands());EXPECT_EQ(Idx, Load->getOperand(0));VPInstruction *Add = dyn_cast<VPInstruction>(&*Iter++);EXPECT_EQ(Instruction::Add, Add->getOpcode());EXPECT_EQ(2u, Add->getNumOperands());EXPECT_EQ(Load, Add->getOperand(0));VPInstruction *Store = dyn_cast<VPInstruction>(&*Iter++);EXPECT_EQ(Instruction::Store, Store->getOpcode());EXPECT_EQ(2u, Store->getNumOperands());EXPECT_EQ(Add, Store->getOperand(0));EXPECT_EQ(Idx, Store->getOperand(1));VPInstruction *IndvarAdd = dyn_cast<VPInstruction>(&*Iter++);EXPECT_EQ(Instruction::Add, IndvarAdd->getOpcode());EXPECT_EQ(2u, IndvarAdd->getNumOperands());EXPECT_EQ(Phi, IndvarAdd->getOperand(0));VPInstruction *ICmp = dyn_cast<VPInstruction>(&*Iter++);EXPECT_EQ(Instruction::ICmp, ICmp->getOpcode());EXPECT_EQ(2u, ICmp->getNumOperands());EXPECT_EQ(IndvarAdd, ICmp->getOperand(0));#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)// Add an external value to check we do not print the list of external values,// as this is not required with the new printing.Plan->addVPValue(&*F->arg_begin());std::string FullDump;raw_string_ostream OS(FullDump);Plan->printDOT(OS);const char *ExpectedStr = R"(digraph VPlan {graph [labelloc=t, fontsize=30; label="Vectorization Plan"]node [shape=rect, fontname=Courier, fontsize=30]edge [fontname=Courier, fontsize=30]compound=trueN0 [label ="vector.ph:\l" +"Successor(s): for.body\l"]N0 -> N1 [ label="" lhead=cluster_N2]subgraph cluster_N2 {fontname=Courierlabel="\<x1\> for.body"N1 [label ="vector.body:\l" +" WIDEN-PHI ir\<%indvars.iv\> = phi ir\<0\>, ir\<%indvars.iv.next\>\l" +" EMIT ir\<%arr.idx\> = getelementptr ir\<%A\> ir\<%indvars.iv\>\l" +" EMIT ir\<%l1\> = load ir\<%arr.idx\>\l" +" EMIT ir\<%res\> = add ir\<%l1\> ir\<10\>\l" +" EMIT store ir\<%res\> ir\<%arr.idx\>\l" +" EMIT ir\<%indvars.iv.next\> = add ir\<%indvars.iv\> ir\<1\>\l" +" EMIT ir\<%exitcond\> = icmp ir\<%indvars.iv.next\> ir\<%N\>\l" +" EMIT branch-on-cond ir\<%exitcond\>\l" +"No successors\l"]}N1 -> N3 [ label="" ltail=cluster_N2]N3 [label ="for.end:\l" +"No successors\l"]})";EXPECT_EQ(ExpectedStr, FullDump);#endifSmallPtrSet<Instruction *, 1> DeadInstructions;VPlanTransforms::VPInstructionsToVPRecipes(LI->getLoopFor(LoopHeader), Plan, [](PHINode *P) { return nullptr; },DeadInstructions, *SE);}TEST_F(VPlanHCFGTest, testVPInstructionToVPRecipesInner) {const char *ModuleString ="define void @f(i32* %A, i64 %N) {\n""entry:\n"" br label %for.body\n""for.body:\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"" %arr.idx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv\n"" %l1 = load i32, i32* %arr.idx, align 4\n"" %res = add i32 %l1, 10\n"" store i32 %res, i32* %arr.idx, align 4\n"" %indvars.iv.next = add i64 %indvars.iv, 1\n"" %exitcond = icmp ne i64 %indvars.iv.next, %N\n"" br i1 %exitcond, label %for.body, label %for.end\n""for.end:\n"" ret void\n""}\n";Module &M = parseModule(ModuleString);Function *F = M.getFunction("f");BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();auto Plan = buildHCFG(LoopHeader);SmallPtrSet<Instruction *, 1> DeadInstructions;VPlanTransforms::VPInstructionsToVPRecipes(LI->getLoopFor(LoopHeader), Plan, [](PHINode *P) { return nullptr; },DeadInstructions, *SE);VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();EXPECT_NE(nullptr, Entry->getSingleSuccessor());EXPECT_EQ(0u, Entry->getNumPredecessors());EXPECT_EQ(1u, Entry->getNumSuccessors());// Check that the region following the preheader is a single basic-block// region (loop).VPBasicBlock *VecBB = Entry->getSingleSuccessor()->getEntryBasicBlock();EXPECT_EQ(8u, VecBB->size());EXPECT_EQ(0u, VecBB->getNumPredecessors());EXPECT_EQ(0u, VecBB->getNumSuccessors());EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB);EXPECT_EQ(VecBB->getParent()->getExitingBasicBlock(), VecBB);auto Iter = VecBB->begin();EXPECT_NE(nullptr, dyn_cast<VPWidenPHIRecipe>(&*Iter++));EXPECT_NE(nullptr, dyn_cast<VPWidenGEPRecipe>(&*Iter++));EXPECT_NE(nullptr, dyn_cast<VPWidenMemoryInstructionRecipe>(&*Iter++));EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++));EXPECT_NE(nullptr, dyn_cast<VPWidenMemoryInstructionRecipe>(&*Iter++));EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++));EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++));EXPECT_NE(nullptr, dyn_cast<VPInstruction>(&*Iter++));EXPECT_EQ(VecBB->end(), Iter);}} // namespace} // namespace llvm
set(LLVM_LINK_COMPONENTSAnalysisCoreVectorizeAsmParser)add_llvm_unittest(VectorizeTestsVPlanTest.cppVPlanHCFGTest.cppVPlanSlpTest.cpp)set_property(TARGET VectorizeTests PROPERTY FOLDER "Tests/UnitTests/TransformsTests")
//===- ValueMapper.cpp - Unit tests for ValueMapper -----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/ValueMapper.h"#include "llvm/IR/Constants.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/Function.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Metadata.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ValueMapperTest, mapMDNode) {LLVMContext Context;auto *U = MDTuple::get(Context, None);// The node should be unchanged.ValueToValueMapTy VM;EXPECT_EQ(U, ValueMapper(VM).mapMDNode(*U));}TEST(ValueMapperTest, mapMDNodeCycle) {LLVMContext Context;MDNode *U0;MDNode *U1;{Metadata *Ops[] = {nullptr};auto T = MDTuple::getTemporary(Context, Ops);Ops[0] = T.get();U0 = MDTuple::get(Context, Ops);T->replaceOperandWith(0, U0);U1 = MDNode::replaceWithUniqued(std::move(T));U0->resolveCycles();}EXPECT_TRUE(U0->isResolved());EXPECT_TRUE(U0->isUniqued());EXPECT_TRUE(U1->isResolved());EXPECT_TRUE(U1->isUniqued());EXPECT_EQ(U1, U0->getOperand(0));EXPECT_EQ(U0, U1->getOperand(0));// Cycles shouldn't be duplicated.{ValueToValueMapTy VM;EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0));EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1));}// Check the other order.{ValueToValueMapTy VM;EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1));EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0));}}TEST(ValueMapperTest, mapMDNodeDuplicatedCycle) {LLVMContext Context;auto *PtrTy = Type::getInt8Ty(Context)->getPointerTo();std::unique_ptr<GlobalVariable> G0 = std::make_unique<GlobalVariable>(PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G0");std::unique_ptr<GlobalVariable> G1 = std::make_unique<GlobalVariable>(PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G1");// Create a cycle that references G0.MDNode *N0; // !0 = !{!1}MDNode *N1; // !1 = !{!0, i8* @G0}{auto T0 = MDTuple::getTemporary(Context, nullptr);Metadata *Ops1[] = {T0.get(), ConstantAsMetadata::get(G0.get())};N1 = MDTuple::get(Context, Ops1);T0->replaceOperandWith(0, N1);N0 = MDNode::replaceWithUniqued(std::move(T0));}// Resolve N0 and N1.ASSERT_FALSE(N0->isResolved());ASSERT_FALSE(N1->isResolved());N0->resolveCycles();ASSERT_TRUE(N0->isResolved());ASSERT_TRUE(N1->isResolved());// Seed the value map to map G0 to G1 and map the nodes. The output should// have new nodes that reference G1 (instead of G0).ValueToValueMapTy VM;VM[G0.get()] = G1.get();MDNode *MappedN0 = ValueMapper(VM).mapMDNode(*N0);MDNode *MappedN1 = ValueMapper(VM).mapMDNode(*N1);EXPECT_NE(N0, MappedN0);EXPECT_NE(N1, MappedN1);EXPECT_EQ(ConstantAsMetadata::get(G1.get()), MappedN1->getOperand(1));// Check that the output nodes are resolved.EXPECT_TRUE(MappedN0->isResolved());EXPECT_TRUE(MappedN1->isResolved());}TEST(ValueMapperTest, mapMDNodeUnresolved) {LLVMContext Context;TempMDTuple T = MDTuple::getTemporary(Context, None);ValueToValueMapTy VM;EXPECT_EQ(T.get(), ValueMapper(VM, RF_NoModuleLevelChanges).mapMDNode(*T));}TEST(ValueMapperTest, mapMDNodeDistinct) {LLVMContext Context;auto *D = MDTuple::getDistinct(Context, None);{// The node should be cloned.ValueToValueMapTy VM;EXPECT_NE(D, ValueMapper(VM).mapMDNode(*D));}{// The node should be moved.ValueToValueMapTy VM;EXPECT_EQ(D, ValueMapper(VM, RF_ReuseAndMutateDistinctMDs).mapMDNode(*D));}}TEST(ValueMapperTest, mapMDNodeDistinctOperands) {LLVMContext Context;Metadata *Old = MDTuple::getDistinct(Context, None);auto *D = MDTuple::getDistinct(Context, Old);ASSERT_EQ(Old, D->getOperand(0));Metadata *New = MDTuple::getDistinct(Context, None);ValueToValueMapTy VM;VM.MD()[Old].reset(New);// Make sure operands are updated.EXPECT_EQ(D, ValueMapper(VM, RF_ReuseAndMutateDistinctMDs).mapMDNode(*D));EXPECT_EQ(New, D->getOperand(0));}TEST(ValueMapperTest, mapMDNodeSeeded) {LLVMContext Context;auto *D = MDTuple::getDistinct(Context, None);// The node should be moved.ValueToValueMapTy VM;EXPECT_EQ(None, VM.getMappedMD(D));VM.MD().insert(std::make_pair(D, TrackingMDRef(D)));EXPECT_EQ(D, *VM.getMappedMD(D));EXPECT_EQ(D, ValueMapper(VM).mapMDNode(*D));}TEST(ValueMapperTest, mapMDNodeSeededWithNull) {LLVMContext Context;auto *D = MDTuple::getDistinct(Context, None);// The node should be moved.ValueToValueMapTy VM;EXPECT_EQ(None, VM.getMappedMD(D));VM.MD().insert(std::make_pair(D, TrackingMDRef()));EXPECT_EQ(nullptr, *VM.getMappedMD(D));EXPECT_EQ(nullptr, ValueMapper(VM).mapMDNode(*D));}TEST(ValueMapperTest, mapMetadataNullMapGlobalWithIgnoreMissingLocals) {LLVMContext C;FunctionType *FTy =FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);std::unique_ptr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));ValueToValueMapTy VM;RemapFlags Flags = RF_IgnoreMissingLocals | RF_NullMapMissingGlobalValues;EXPECT_EQ(nullptr, ValueMapper(VM, Flags).mapValue(*F));}TEST(ValueMapperTest, mapMetadataMDString) {LLVMContext C;auto *S1 = MDString::get(C, "S1");ValueToValueMapTy VM;// Make sure S1 maps to itself, but isn't memoized.EXPECT_EQ(S1, ValueMapper(VM).mapMetadata(*S1));EXPECT_EQ(None, VM.getMappedMD(S1));// We still expect VM.MD() to be respected.auto *S2 = MDString::get(C, "S2");VM.MD()[S1].reset(S2);EXPECT_EQ(S2, ValueMapper(VM).mapMetadata(*S1));}TEST(ValueMapperTest, mapMetadataGetMappedMD) {LLVMContext C;auto *N0 = MDTuple::get(C, None);auto *N1 = MDTuple::get(C, N0);// Make sure hasMD and getMappedMD work correctly.ValueToValueMapTy VM;EXPECT_FALSE(VM.hasMD());EXPECT_EQ(N0, ValueMapper(VM).mapMetadata(*N0));EXPECT_EQ(N1, ValueMapper(VM).mapMetadata(*N1));EXPECT_TRUE(VM.hasMD());ASSERT_NE(None, VM.getMappedMD(N0));ASSERT_NE(None, VM.getMappedMD(N1));EXPECT_EQ(N0, *VM.getMappedMD(N0));EXPECT_EQ(N1, *VM.getMappedMD(N1));}TEST(ValueMapperTest, mapMetadataNoModuleLevelChanges) {LLVMContext C;auto *N0 = MDTuple::get(C, None);auto *N1 = MDTuple::get(C, N0);// Nothing should be memoized when RF_NoModuleLevelChanges.ValueToValueMapTy VM;EXPECT_FALSE(VM.hasMD());EXPECT_EQ(N0, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N0));EXPECT_EQ(N1, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N1));EXPECT_FALSE(VM.hasMD());EXPECT_EQ(None, VM.getMappedMD(N0));EXPECT_EQ(None, VM.getMappedMD(N1));}TEST(ValueMapperTest, mapMetadataConstantAsMetadata) {LLVMContext C;FunctionType *FTy =FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);std::unique_ptr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));auto *CAM = ConstantAsMetadata::get(F.get());{// ConstantAsMetadata shouldn't be memoized.ValueToValueMapTy VM;EXPECT_EQ(CAM, ValueMapper(VM).mapMetadata(*CAM));EXPECT_FALSE(VM.MD().count(CAM));EXPECT_EQ(CAM, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM));EXPECT_FALSE(VM.MD().count(CAM));// But it should respect a mapping that gets seeded.auto *N = MDTuple::get(C, None);VM.MD()[CAM].reset(N);EXPECT_EQ(N, ValueMapper(VM).mapMetadata(*CAM));EXPECT_EQ(N, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM));}std::unique_ptr<Function> F2(Function::Create(FTy, GlobalValue::ExternalLinkage, "F2"));ValueToValueMapTy VM;VM[F.get()] = F2.get();auto *F2MD = ValueMapper(VM).mapMetadata(*CAM);EXPECT_FALSE(VM.MD().count(CAM));EXPECT_TRUE(F2MD);EXPECT_EQ(F2.get(), cast<ConstantAsMetadata>(F2MD)->getValue());}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST(ValueMapperTest, mapMetadataLocalAsMetadata) {LLVMContext C;FunctionType *FTy =FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);std::unique_ptr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));Argument &A = *F->arg_begin();// mapMetadata doesn't support LocalAsMetadata. The only valid container for// LocalAsMetadata is a MetadataAsValue instance, so use it directly.auto *LAM = LocalAsMetadata::get(&A);ValueToValueMapTy VM;EXPECT_DEATH(ValueMapper(VM).mapMetadata(*LAM), "Unexpected local metadata");EXPECT_DEATH(ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*LAM),"Unexpected local metadata");}#endif#endifTEST(ValueMapperTest, mapValueLocalAsMetadata) {LLVMContext C;FunctionType *FTy =FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);std::unique_ptr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));Argument &A = *F->arg_begin();auto *LAM = LocalAsMetadata::get(&A);auto *MAV = MetadataAsValue::get(C, LAM);// The principled answer to a LocalAsMetadata of an unmapped SSA value would// be to return nullptr (regardless of RF_IgnoreMissingLocals).//// However, algorithms that use RemapInstruction assume that each instruction// only references SSA values from previous instructions. Arguments of// such as "metadata i32 %x" don't currently successfully maintain that// property. To keep RemapInstruction from crashing we need a non-null// return here, but we also shouldn't reference the unmapped local. Use// "metadata !{}".auto *N0 = MDTuple::get(C, None);auto *N0AV = MetadataAsValue::get(C, N0);ValueToValueMapTy VM;EXPECT_EQ(N0AV, ValueMapper(VM).mapValue(*MAV));EXPECT_EQ(nullptr, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));EXPECT_FALSE(VM.count(MAV));EXPECT_FALSE(VM.count(&A));EXPECT_EQ(None, VM.getMappedMD(LAM));VM[MAV] = MAV;EXPECT_EQ(MAV, ValueMapper(VM).mapValue(*MAV));EXPECT_EQ(MAV, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));EXPECT_TRUE(VM.count(MAV));EXPECT_FALSE(VM.count(&A));VM[MAV] = &A;EXPECT_EQ(&A, ValueMapper(VM).mapValue(*MAV));EXPECT_EQ(&A, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));EXPECT_TRUE(VM.count(MAV));EXPECT_FALSE(VM.count(&A));}TEST(ValueMapperTest, mapValueLocalInArgList) {LLVMContext C;FunctionType *FTy =FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);std::unique_ptr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));Argument &A = *F->arg_begin();auto *LAM = LocalAsMetadata::get(&A);std::vector<ValueAsMetadata*> Elts;Elts.push_back(LAM);auto *ArgList = DIArgList::get(C, Elts);auto *MAV = MetadataAsValue::get(C, ArgList);// The principled answer to a LocalAsMetadata of an unmapped SSA value would// be to return nullptr (regardless of RF_IgnoreMissingLocals).//// However, algorithms that use RemapInstruction assume that each instruction// only references SSA values from previous instructions. Arguments of// such as "metadata i32 %x" don't currently successfully maintain that// property. To keep RemapInstruction from crashing we need a non-null// return here, but we also shouldn't reference the unmapped local. Use// undef for uses in a DIArgList.auto *N0 = UndefValue::get(Type::getInt8Ty(C));auto *N0AM = ValueAsMetadata::get(N0);std::vector<ValueAsMetadata*> N0Elts;N0Elts.push_back(N0AM);auto *N0ArgList = DIArgList::get(C, N0Elts);auto *N0AV = MetadataAsValue::get(C, N0ArgList);ValueToValueMapTy VM;EXPECT_EQ(N0AV, ValueMapper(VM).mapValue(*MAV));EXPECT_EQ(MAV, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));EXPECT_FALSE(VM.count(MAV));EXPECT_FALSE(VM.count(&A));EXPECT_EQ(None, VM.getMappedMD(LAM));EXPECT_EQ(None, VM.getMappedMD(ArgList));VM[MAV] = MAV;EXPECT_EQ(MAV, ValueMapper(VM).mapValue(*MAV));EXPECT_EQ(MAV, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));EXPECT_TRUE(VM.count(MAV));EXPECT_FALSE(VM.count(&A));VM[MAV] = &A;EXPECT_EQ(&A, ValueMapper(VM).mapValue(*MAV));EXPECT_EQ(&A, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));EXPECT_TRUE(VM.count(MAV));EXPECT_FALSE(VM.count(&A));}TEST(ValueMapperTest, mapValueLocalAsMetadataToConstant) {LLVMContext Context;auto *Int8 = Type::getInt8Ty(Context);FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), Int8, false);std::unique_ptr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));// Map a local value to a constant.Argument &A = *F->arg_begin();Constant &C = *ConstantInt::get(Int8, 42);ValueToValueMapTy VM;VM[&A] = &C;// Look up the metadata-as-value wrapper. Don't crash.auto *MDA = MetadataAsValue::get(Context, ValueAsMetadata::get(&A));auto *MDC = MetadataAsValue::get(Context, ValueAsMetadata::get(&C));EXPECT_TRUE(isa<LocalAsMetadata>(MDA->getMetadata()));EXPECT_TRUE(isa<ConstantAsMetadata>(MDC->getMetadata()));EXPECT_EQ(&C, ValueMapper(VM).mapValue(A));EXPECT_EQ(MDC, ValueMapper(VM).mapValue(*MDA));}} // end namespace
//===------- VFABIUtils.cpp - VFABI Unittests ----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/AsmParser/Parser.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/ModuleUtils.h"#include "gtest/gtest.h"using namespace llvm;class VFABIAttrTest : public testing::Test {protected:void SetUp() override {M = parseAssemblyString(IR, Err, Ctx);// Get the only call instruction in the block, which is the first// instruction.CI = dyn_cast<CallInst>(&*(instructions(M->getFunction("f")).begin()));}const char *IR = "define i32 @f(i32 %a) {\n"" %1 = call i32 @g(i32 %a) #0\n"" ret i32 %1\n""}\n""declare i32 @g(i32)\n""declare <2 x i32> @custom_vg(<2 x i32>)""declare <4 x i32> @_ZGVnN4v_g(<4 x i32>)""declare <8 x i32> @_ZGVnN8v_g(<8 x i32>)""attributes #0 = { ""\"vector-function-abi-variant\"=\"""_ZGVnN2v_g(custom_vg),_ZGVnN4v_g\" }";LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M;CallInst *CI;SmallVector<std::string, 8> Mappings;};TEST_F(VFABIAttrTest, Write) {Mappings.push_back("_ZGVnN8v_g");Mappings.push_back("_ZGVnN2v_g(custom_vg)");VFABI::setVectorVariantNames(CI, Mappings);const StringRef S =CI->getFnAttr("vector-function-abi-variant").getValueAsString();EXPECT_EQ(S, "_ZGVnN8v_g,_ZGVnN2v_g(custom_vg)");}
//===- UnrollLoopTest.cpp - Unit tests for UnrollLoop ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/UnrollLoop.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("UnrollLoopTests", errs());return Mod;}TEST(LoopUnrollRuntime, Latch) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(define i32 @test(i32* %a, i32* %b, i32* %c, i64 %n) {entry:br label %while.condwhile.cond: ; preds = %while.body, %entry%i.0 = phi i64 [ 0, %entry ], [ %inc, %while.body ]%cmp = icmp slt i64 %i.0, %nbr i1 %cmp, label %while.body, label %while.endwhile.body: ; preds = %while.cond%arrayidx = getelementptr inbounds i32, i32* %b, i64 %i.0%0 = load i32, i32* %arrayidx%arrayidx1 = getelementptr inbounds i32, i32* %c, i64 %i.0%1 = load i32, i32* %arrayidx1%mul = mul nsw i32 %0, %1%arrayidx2 = getelementptr inbounds i32, i32* %a, i64 %i.0store i32 %mul, i32* %arrayidx2%inc = add nsw i64 %i.0, 1br label %while.condwhile.end: ; preds = %while.condret i32 0})");auto *F = M->getFunction("test");DominatorTree DT(*F);LoopInfo LI(DT);AssumptionCache AC(*F);TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);ScalarEvolution SE(*F, TLI, AC, DT, LI);Loop *L = *LI.begin();bool PreserveLCSSA = L->isRecursivelyLCSSAForm(DT,LI);bool ret =UnrollRuntimeLoopRemainder(L, 4, true, false, false, false, &LI, &SE, &DT,&AC, /*TTI=*/nullptr, PreserveLCSSA);EXPECT_FALSE(ret);}
//===- SizeOptsTest.cpp - SizeOpts unit tests -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/SizeOpts.h"#include "llvm/Analysis/ProfileSummaryInfo.h"#include "llvm/Analysis/BlockFrequencyInfo.h"#include "llvm/Analysis/BranchProbabilityInfo.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {class SizeOptsTest : public testing::Test {protected:static const char* IRString;LLVMContext C;std::unique_ptr<Module> M;struct BFIData {std::unique_ptr<DominatorTree> DT;std::unique_ptr<LoopInfo> LI;std::unique_ptr<BranchProbabilityInfo> BPI;std::unique_ptr<BlockFrequencyInfo> BFI;BFIData(Function &F) {DT.reset(new DominatorTree(F));LI.reset(new LoopInfo(*DT));BPI.reset(new BranchProbabilityInfo(F, *LI));BFI.reset(new BlockFrequencyInfo(F, *BPI, *LI));}BlockFrequencyInfo *get() { return BFI.get(); }};void SetUp() override {SMDiagnostic Err;M = parseAssemblyString(IRString, Err, C);}};TEST_F(SizeOptsTest, Test) {Function *F = M->getFunction("f");Function *G = M->getFunction("g");Function *H = M->getFunction("h");ProfileSummaryInfo PSI(*M.get());BFIData BFID_F(*F);BFIData BFID_G(*G);BFIData BFID_H(*H);BlockFrequencyInfo *BFI_F = BFID_F.get();BlockFrequencyInfo *BFI_G = BFID_G.get();BlockFrequencyInfo *BFI_H = BFID_H.get();BasicBlock &BB0 = F->getEntryBlock();BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1);BasicBlock *BB3 = BB1->getSingleSuccessor();EXPECT_TRUE(PSI.hasProfileSummary());EXPECT_FALSE(shouldOptimizeForSize(F, &PSI, BFI_F, PGSOQueryType::Test));EXPECT_TRUE(shouldOptimizeForSize(G, &PSI, BFI_G, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(H, &PSI, BFI_H, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(&BB0, &PSI, BFI_F, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(BB1, &PSI, BFI_F, PGSOQueryType::Test));EXPECT_TRUE(shouldOptimizeForSize(BB2, &PSI, BFI_F, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(BB3, &PSI, BFI_F, PGSOQueryType::Test));}const char* SizeOptsTest::IRString = R"IR(define i32 @g(i32 %x) !prof !14 {ret i32 0}define i32 @h(i32 %x) !prof !15 {ret i32 0}define i32 @f(i32 %x) !prof !16 {bb0:%y1 = icmp eq i32 %x, 0br i1 %y1, label %bb1, label %bb2, !prof !17bb1: ; preds = %bb0%z1 = call i32 @g(i32 %x)br label %bb3bb2: ; preds = %bb0%z2 = call i32 @h(i32 %x)br label %bb3bb3: ; preds = %bb2, %bb1%y2 = phi i32 [ 0, %bb1 ], [ 1, %bb2 ]ret i32 %y2}!llvm.module.flags = !{!0}!0 = !{i32 1, !"ProfileSummary", !1}!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}!2 = !{!"ProfileFormat", !"InstrProf"}!3 = !{!"TotalCount", i64 10000}!4 = !{!"MaxCount", i64 10}!5 = !{!"MaxInternalCount", i64 1}!6 = !{!"MaxFunctionCount", i64 1000}!7 = !{!"NumCounts", i64 3}!8 = !{!"NumFunctions", i64 3}!9 = !{!"DetailedSummary", !10}!10 = !{!11, !12, !13}!11 = !{i32 10000, i64 1000, i32 1}!12 = !{i32 999000, i64 300, i32 3}!13 = !{i32 999999, i64 5, i32 10}!14 = !{!"function_entry_count", i64 1}!15 = !{!"function_entry_count", i64 100}!16 = !{!"function_entry_count", i64 400}!17 = !{!"branch_weights", i32 100, i32 1})IR";} // end anonymous namespace
//=== ScalarEvolutionExpanderTest.cpp - ScalarEvolutionExpander unit tests ===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"#include "llvm/ADT/SmallVector.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ScalarEvolutionExpressions.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/PatternMatch.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"namespace llvm {using namespace PatternMatch;// We use this fixture to ensure that we clean up ScalarEvolution before// deleting the PassManager.class ScalarEvolutionExpanderTest : public testing::Test {protected:LLVMContext Context;Module M;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;std::unique_ptr<AssumptionCache> AC;std::unique_ptr<DominatorTree> DT;std::unique_ptr<LoopInfo> LI;ScalarEvolutionExpanderTest() : M("", Context), TLII(), TLI(TLII) {}ScalarEvolution buildSE(Function &F) {AC.reset(new AssumptionCache(F));DT.reset(new DominatorTree(F));LI.reset(new LoopInfo(*DT));return ScalarEvolution(F, TLI, *AC, *DT, *LI);}void runWithSE(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI, ScalarEvolution &SE)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;ScalarEvolution SE = buildSE(*F);Test(*F, *LI, SE);}};static Instruction &GetInstByName(Function &F, StringRef Name) {for (auto &I : instructions(F))if (I.getName() == Name)return I;llvm_unreachable("Could not find instructions!");}TEST_F(ScalarEvolutionExpanderTest, ExpandPtrTypeSCEV) {// It is to test the fix for PR30213. It exercises the branch in scev// expansion when the value in ValueOffsetPair is a ptr and the offset// is not divisible by the elem type size of value.auto *I8Ty = Type::getInt8Ty(Context);auto *I8PtrTy = Type::getInt8PtrTy(Context);auto *I32Ty = Type::getInt32Ty(Context);auto *I32PtrTy = Type::getInt32PtrTy(Context);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), std::vector<Type *>(), false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F);BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F);BranchInst::Create(LoopBB, EntryBB);ReturnInst::Create(Context, nullptr, ExitBB);// loop: ; preds = %loop, %entry// %alloca = alloca i32// %gep0 = getelementptr i32, i32* %alloca, i32 1// %bitcast1 = bitcast i32* %gep0 to i8*// %gep1 = getelementptr i8, i8* %bitcast1, i32 1// %gep2 = getelementptr i8, i8* undef, i32 1// %cmp = icmp ult i8* undef, %bitcast1// %select = select i1 %cmp, i8* %gep1, i8* %gep2// %bitcast2 = bitcast i8* %select to i32*// br i1 undef, label %loop, label %exitconst DataLayout &DL = F->getParent()->getDataLayout();BranchInst *Br = BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB);AllocaInst *Alloca =new AllocaInst(I32Ty, DL.getAllocaAddrSpace(), "alloca", Br);ConstantInt *Ci32 = ConstantInt::get(Context, APInt(32, 1));GetElementPtrInst *Gep0 =GetElementPtrInst::Create(I32Ty, Alloca, Ci32, "gep0", Br);CastInst *CastA =CastInst::CreateBitOrPointerCast(Gep0, I8PtrTy, "bitcast1", Br);GetElementPtrInst *Gep1 =GetElementPtrInst::Create(I8Ty, CastA, Ci32, "gep1", Br);GetElementPtrInst *Gep2 = GetElementPtrInst::Create(I8Ty, UndefValue::get(I8PtrTy), Ci32, "gep2", Br);CmpInst *Cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT,UndefValue::get(I8PtrTy), CastA, "cmp", Br);SelectInst *Sel = SelectInst::Create(Cmp, Gep1, Gep2, "select", Br);CastInst *CastB =CastInst::CreateBitOrPointerCast(Sel, I32PtrTy, "bitcast2", Br);ScalarEvolution SE = buildSE(*F);auto *S = SE.getSCEV(CastB);EXPECT_TRUE(isa<SCEVUnknown>(S));}// Make sure that SCEV doesn't introduce illegal ptrtoint/inttoptr instructionsTEST_F(ScalarEvolutionExpanderTest, SCEVZeroExtendExprNonIntegral) {/** Create the following code:* func(i64 addrspace(10)* %arg)* top:* br label %L.ph* L.ph:* br label %L* L:* %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ]* %add = add i64 %phi2, 1* br i1 undef, label %post, label %L2* post:* %gepbase = getelementptr i64 addrspace(10)* %arg, i64 1* #= %gep = getelementptr i64 addrspace(10)* %gepbase, i64 %add =#* ret void** We will create the appropriate SCEV expression for %gep and expand it,* then check that no inttoptr/ptrtoint instructions got inserted.*/// Create a module with non-integral pointers in it's datalayoutModule NIM("nonintegral", Context);std::string DataLayout = M.getDataLayoutStr();if (!DataLayout.empty())DataLayout += "-";DataLayout += "ni:10";NIM.setDataLayout(DataLayout);Type *T_int1 = Type::getInt1Ty(Context);Type *T_int64 = Type::getInt64Ty(Context);Type *T_pint64 = T_int64->getPointerTo(10);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM);Argument *Arg = &*F->arg_begin();BasicBlock *Top = BasicBlock::Create(Context, "top", F);BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F);BasicBlock *L = BasicBlock::Create(Context, "L", F);BasicBlock *Post = BasicBlock::Create(Context, "post", F);IRBuilder<> Builder(Top);Builder.CreateBr(LPh);Builder.SetInsertPoint(LPh);Builder.CreateBr(L);Builder.SetInsertPoint(L);PHINode *Phi = Builder.CreatePHI(T_int64, 2);Value *Add = Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add");Builder.CreateCondBr(UndefValue::get(T_int1), L, Post);Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh);Phi->addIncoming(Add, L);Builder.SetInsertPoint(Post);Value *GepBase =Builder.CreateGEP(T_int64, Arg, ConstantInt::get(T_int64, 1));Instruction *Ret = Builder.CreateRetVoid();ScalarEvolution SE = buildSE(*F);auto *AddRec =SE.getAddRecExpr(SE.getUnknown(GepBase), SE.getConstant(T_int64, 1),LI->getLoopFor(L), SCEV::FlagNUW);SCEVExpander Exp(SE, NIM.getDataLayout(), "expander");Exp.disableCanonicalMode();Exp.expandCodeFor(AddRec, T_pint64, Ret);// Make sure none of the instructions inserted were inttoptr/ptrtoint.// The verifier will check this.EXPECT_FALSE(verifyFunction(*F, &errs()));}// Check that we can correctly identify the points at which the SCEV of the// AddRec can be expanded.TEST_F(ScalarEvolutionExpanderTest, SCEVExpanderIsSafeToExpandAt) {/** Create the following code:* func(i64 addrspace(10)* %arg)* top:* br label %L.ph* L.ph:* br label %L* L:* %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ]* %add = add i64 %phi2, 1* %cond = icmp slt i64 %add, 1000; then becomes 2000.* br i1 %cond, label %post, label %L2* post:* ret void**/// Create a module with non-integral pointers in it's datalayoutModule NIM("nonintegral", Context);std::string DataLayout = M.getDataLayoutStr();if (!DataLayout.empty())DataLayout += "-";DataLayout += "ni:10";NIM.setDataLayout(DataLayout);Type *T_int64 = Type::getInt64Ty(Context);Type *T_pint64 = T_int64->getPointerTo(10);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM);BasicBlock *Top = BasicBlock::Create(Context, "top", F);BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F);BasicBlock *L = BasicBlock::Create(Context, "L", F);BasicBlock *Post = BasicBlock::Create(Context, "post", F);IRBuilder<> Builder(Top);Builder.CreateBr(LPh);Builder.SetInsertPoint(LPh);Builder.CreateBr(L);Builder.SetInsertPoint(L);PHINode *Phi = Builder.CreatePHI(T_int64, 2);auto *Add = cast<Instruction>(Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add"));auto *Limit = ConstantInt::get(T_int64, 1000);auto *Cond = cast<Instruction>(Builder.CreateICmp(ICmpInst::ICMP_SLT, Add, Limit, "cond"));Builder.CreateCondBr(Cond, L, Post);Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh);Phi->addIncoming(Add, L);Builder.SetInsertPoint(Post);Instruction *Ret = Builder.CreateRetVoid();ScalarEvolution SE = buildSE(*F);SCEVExpander Exp(SE, M.getDataLayout(), "expander");const SCEV *S = SE.getSCEV(Phi);EXPECT_TRUE(isa<SCEVAddRecExpr>(S));const SCEVAddRecExpr *AR = cast<SCEVAddRecExpr>(S);EXPECT_TRUE(AR->isAffine());EXPECT_FALSE(Exp.isSafeToExpandAt(AR, Top->getTerminator()));EXPECT_FALSE(Exp.isSafeToExpandAt(AR, LPh->getTerminator()));EXPECT_TRUE(Exp.isSafeToExpandAt(AR, L->getTerminator()));EXPECT_TRUE(Exp.isSafeToExpandAt(AR, Post->getTerminator()));EXPECT_TRUE(LI->getLoopFor(L)->isLCSSAForm(*DT));Exp.expandCodeFor(SE.getSCEV(Add), nullptr, Ret);EXPECT_TRUE(LI->getLoopFor(L)->isLCSSAForm(*DT));}// Check that SCEV expander does not use the nuw instruction// for expansion.TEST_F(ScalarEvolutionExpanderTest, SCEVExpanderNUW) {/** Create the following code:* func(i64 %a)* entry:* br false, label %exit, label %body* body:* %s1 = add i64 %a, -1* br label %exit* exit:* %s = add nuw i64 %a, -1* ret %s*/// Create a module.Module M("SCEVExpanderNUW", Context);Type *T_int64 = Type::getInt64Ty(Context);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_int64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M);Argument *Arg = &*F->arg_begin();ConstantInt *C = ConstantInt::get(Context, APInt(64, -1));BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);BasicBlock *Body = BasicBlock::Create(Context, "body", F);BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);IRBuilder<> Builder(Entry);ConstantInt *Cond = ConstantInt::get(Context, APInt(1, 0));Builder.CreateCondBr(Cond, Exit, Body);Builder.SetInsertPoint(Body);auto *S1 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));Builder.CreateBr(Exit);Builder.SetInsertPoint(Exit);auto *S2 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));S2->setHasNoUnsignedWrap(true);auto *R = cast<Instruction>(Builder.CreateRetVoid());ScalarEvolution SE = buildSE(*F);const SCEV *S = SE.getSCEV(S1);EXPECT_TRUE(isa<SCEVAddExpr>(S));SCEVExpander Exp(SE, M.getDataLayout(), "expander");auto *I = cast<Instruction>(Exp.expandCodeFor(S, nullptr, R));EXPECT_FALSE(I->hasNoUnsignedWrap());}// Check that SCEV expander does not use the nsw instruction// for expansion.TEST_F(ScalarEvolutionExpanderTest, SCEVExpanderNSW) {/** Create the following code:* func(i64 %a)* entry:* br false, label %exit, label %body* body:* %s1 = add i64 %a, -1* br label %exit* exit:* %s = add nsw i64 %a, -1* ret %s*/// Create a module.Module M("SCEVExpanderNSW", Context);Type *T_int64 = Type::getInt64Ty(Context);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_int64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M);Argument *Arg = &*F->arg_begin();ConstantInt *C = ConstantInt::get(Context, APInt(64, -1));BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);BasicBlock *Body = BasicBlock::Create(Context, "body", F);BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);IRBuilder<> Builder(Entry);ConstantInt *Cond = ConstantInt::get(Context, APInt(1, 0));Builder.CreateCondBr(Cond, Exit, Body);Builder.SetInsertPoint(Body);auto *S1 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));Builder.CreateBr(Exit);Builder.SetInsertPoint(Exit);auto *S2 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));S2->setHasNoSignedWrap(true);auto *R = cast<Instruction>(Builder.CreateRetVoid());ScalarEvolution SE = buildSE(*F);const SCEV *S = SE.getSCEV(S1);EXPECT_TRUE(isa<SCEVAddExpr>(S));SCEVExpander Exp(SE, M.getDataLayout(), "expander");auto *I = cast<Instruction>(Exp.expandCodeFor(S, nullptr, R));EXPECT_FALSE(I->hasNoSignedWrap());}// Check that SCEV does not save the SCEV -> V// mapping of SCEV differ from V in NUW flag.TEST_F(ScalarEvolutionExpanderTest, SCEVCacheNUW) {/** Create the following code:* func(i64 %a)* entry:* %s1 = add i64 %a, -1* %s2 = add nuw i64 %a, -1* br label %exit* exit:* ret %s*/// Create a module.Module M("SCEVCacheNUW", Context);Type *T_int64 = Type::getInt64Ty(Context);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_int64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M);Argument *Arg = &*F->arg_begin();ConstantInt *C = ConstantInt::get(Context, APInt(64, -1));BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);IRBuilder<> Builder(Entry);auto *S1 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));auto *S2 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));S2->setHasNoUnsignedWrap(true);Builder.CreateBr(Exit);Builder.SetInsertPoint(Exit);auto *R = cast<Instruction>(Builder.CreateRetVoid());ScalarEvolution SE = buildSE(*F);// Get S2 first to move it to cache.const SCEV *SC2 = SE.getSCEV(S2);EXPECT_TRUE(isa<SCEVAddExpr>(SC2));// Now get S1.const SCEV *SC1 = SE.getSCEV(S1);EXPECT_TRUE(isa<SCEVAddExpr>(SC1));// Expand for S1, it should use S1 not S2 in spite S2// first in the cache.SCEVExpander Exp(SE, M.getDataLayout(), "expander");auto *I = cast<Instruction>(Exp.expandCodeFor(SC1, nullptr, R));EXPECT_FALSE(I->hasNoUnsignedWrap());}// Check that SCEV does not save the SCEV -> V// mapping of SCEV differ from V in NSW flag.TEST_F(ScalarEvolutionExpanderTest, SCEVCacheNSW) {/** Create the following code:* func(i64 %a)* entry:* %s1 = add i64 %a, -1* %s2 = add nsw i64 %a, -1* br label %exit* exit:* ret %s*/// Create a module.Module M("SCEVCacheNUW", Context);Type *T_int64 = Type::getInt64Ty(Context);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_int64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M);Argument *Arg = &*F->arg_begin();ConstantInt *C = ConstantInt::get(Context, APInt(64, -1));BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);IRBuilder<> Builder(Entry);auto *S1 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));auto *S2 = cast<Instruction>(Builder.CreateAdd(Arg, C, "add"));S2->setHasNoSignedWrap(true);Builder.CreateBr(Exit);Builder.SetInsertPoint(Exit);auto *R = cast<Instruction>(Builder.CreateRetVoid());ScalarEvolution SE = buildSE(*F);// Get S2 first to move it to cache.const SCEV *SC2 = SE.getSCEV(S2);EXPECT_TRUE(isa<SCEVAddExpr>(SC2));// Now get S1.const SCEV *SC1 = SE.getSCEV(S1);EXPECT_TRUE(isa<SCEVAddExpr>(SC1));// Expand for S1, it should use S1 not S2 in spite S2// first in the cache.SCEVExpander Exp(SE, M.getDataLayout(), "expander");auto *I = cast<Instruction>(Exp.expandCodeFor(SC1, nullptr, R));EXPECT_FALSE(I->hasNoSignedWrap());}TEST_F(ScalarEvolutionExpanderTest, SCEVExpandInsertCanonicalIV) {LLVMContext C;SMDiagnostic Err;// Expand the addrec produced by GetAddRec into a loop without a canonical IV.// SCEVExpander will insert one.auto TestNoCanonicalIV =[&](std::function<const SCEV *(ScalarEvolution & SE, Loop * L)>GetAddRec) {std::unique_ptr<Module> M = parseAssemblyString("define i32 @test(i32 %limit) { ""entry: "" br label %loop ""loop: "" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "" %i.inc = add nsw i32 %i, 1 "" %cont = icmp slt i32 %i.inc, %limit "" br i1 %cont, label %loop, label %exit ""exit: "" ret i32 %i.inc ""}",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I = GetInstByName(F, "i");auto *Loop = LI.getLoopFor(I.getParent());EXPECT_FALSE(Loop->getCanonicalInductionVariable());auto *AR = GetAddRec(SE, Loop);unsigned ExpectedCanonicalIVWidth =SE.getTypeSizeInBits(AR->getType());SCEVExpander Exp(SE, M->getDataLayout(), "expander");auto *InsertAt = I.getNextNode();Exp.expandCodeFor(AR, nullptr, InsertAt);PHINode *CanonicalIV = Loop->getCanonicalInductionVariable();unsigned CanonicalIVBitWidth =cast<IntegerType>(CanonicalIV->getType())->getBitWidth();EXPECT_EQ(CanonicalIVBitWidth, ExpectedCanonicalIVWidth);});};// Expand the addrec produced by GetAddRec into a loop with a canonical IV// which is narrower than addrec type.// SCEVExpander will insert a canonical IV of a wider type to expand the// addrec.auto TestNarrowCanonicalIV = [&](std::function<const SCEV *(ScalarEvolution & SE, Loop * L)>GetAddRec) {std::unique_ptr<Module> M = parseAssemblyString("define i32 @test(i32 %limit) { ""entry: "" br label %loop ""loop: "" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "" %canonical.iv = phi i8 [ 0, %entry ], [ %canonical.iv.inc, %loop ] "" %i.inc = add nsw i32 %i, 1 "" %canonical.iv.inc = add i8 %canonical.iv, 1 "" %cont = icmp slt i32 %i.inc, %limit "" br i1 %cont, label %loop, label %exit ""exit: "" ret i32 %i.inc ""}",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I = GetInstByName(F, "i");auto *LoopHeaderBB = I.getParent();auto *Loop = LI.getLoopFor(LoopHeaderBB);PHINode *CanonicalIV = Loop->getCanonicalInductionVariable();EXPECT_EQ(CanonicalIV, &GetInstByName(F, "canonical.iv"));auto *AR = GetAddRec(SE, Loop);unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType());unsigned CanonicalIVBitWidth =cast<IntegerType>(CanonicalIV->getType())->getBitWidth();EXPECT_LT(CanonicalIVBitWidth, ExpectedCanonicalIVWidth);SCEVExpander Exp(SE, M->getDataLayout(), "expander");auto *InsertAt = I.getNextNode();Exp.expandCodeFor(AR, nullptr, InsertAt);// Loop over all of the PHI nodes, looking for the new canonical indvar.PHINode *NewCanonicalIV = nullptr;for (BasicBlock::iterator i = LoopHeaderBB->begin(); isa<PHINode>(i);++i) {PHINode *PN = cast<PHINode>(i);if (PN == &I || PN == CanonicalIV)continue;// We expect that the only PHI added is the new canonical IVEXPECT_FALSE(NewCanonicalIV);NewCanonicalIV = PN;}// Check that NewCanonicalIV is a canonical IV, i.e {0,+,1}BasicBlock *Incoming = nullptr, *Backedge = nullptr;EXPECT_TRUE(Loop->getIncomingAndBackEdge(Incoming, Backedge));auto *Start = NewCanonicalIV->getIncomingValueForBlock(Incoming);EXPECT_TRUE(isa<ConstantInt>(Start));EXPECT_TRUE(dyn_cast<ConstantInt>(Start)->isZero());auto *Next = NewCanonicalIV->getIncomingValueForBlock(Backedge);EXPECT_TRUE(isa<BinaryOperator>(Next));auto *NextBinOp = dyn_cast<BinaryOperator>(Next);EXPECT_EQ(NextBinOp->getOpcode(), Instruction::Add);EXPECT_EQ(NextBinOp->getOperand(0), NewCanonicalIV);auto *Step = NextBinOp->getOperand(1);EXPECT_TRUE(isa<ConstantInt>(Step));EXPECT_TRUE(dyn_cast<ConstantInt>(Step)->isOne());unsigned NewCanonicalIVBitWidth =cast<IntegerType>(NewCanonicalIV->getType())->getBitWidth();EXPECT_EQ(NewCanonicalIVBitWidth, ExpectedCanonicalIVWidth);});};// Expand the addrec produced by GetAddRec into a loop with a canonical IV// of addrec width.// To expand the addrec SCEVExpander should use the existing canonical IV.auto TestMatchingCanonicalIV =[&](std::function<const SCEV *(ScalarEvolution & SE, Loop * L)> GetAddRec,unsigned ARBitWidth) {auto ARBitWidthTypeStr = "i" + std::to_string(ARBitWidth);std::unique_ptr<Module> M = parseAssemblyString("define i32 @test(i32 %limit) { ""entry: "" br label %loop ""loop: "" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "" %canonical.iv = phi " +ARBitWidthTypeStr +" [ 0, %entry ], [ %canonical.iv.inc, %loop ] "" %i.inc = add nsw i32 %i, 1 "" %canonical.iv.inc = add " +ARBitWidthTypeStr +" %canonical.iv, 1 "" %cont = icmp slt i32 %i.inc, %limit "" br i1 %cont, label %loop, label %exit ""exit: "" ret i32 %i.inc ""}",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I = GetInstByName(F, "i");auto &CanonicalIV = GetInstByName(F, "canonical.iv");auto *LoopHeaderBB = I.getParent();auto *Loop = LI.getLoopFor(LoopHeaderBB);EXPECT_EQ(&CanonicalIV, Loop->getCanonicalInductionVariable());unsigned CanonicalIVBitWidth =cast<IntegerType>(CanonicalIV.getType())->getBitWidth();auto *AR = GetAddRec(SE, Loop);EXPECT_EQ(ARBitWidth, SE.getTypeSizeInBits(AR->getType()));EXPECT_EQ(CanonicalIVBitWidth, ARBitWidth);SCEVExpander Exp(SE, M->getDataLayout(), "expander");auto *InsertAt = I.getNextNode();Exp.expandCodeFor(AR, nullptr, InsertAt);// Loop over all of the PHI nodes, looking if a new canonical// indvar was introduced.PHINode *NewCanonicalIV = nullptr;for (BasicBlock::iterator i = LoopHeaderBB->begin();isa<PHINode>(i); ++i) {PHINode *PN = cast<PHINode>(i);if (PN == &I || PN == &CanonicalIV)continue;NewCanonicalIV = PN;}EXPECT_FALSE(NewCanonicalIV);});};unsigned ARBitWidth = 16;Type *ARType = IntegerType::get(C, ARBitWidth);// Expand {5,+,1}auto GetAR2 = [&](ScalarEvolution &SE, Loop *L) -> const SCEV * {return SE.getAddRecExpr(SE.getConstant(APInt(ARBitWidth, 5)),SE.getOne(ARType), L, SCEV::FlagAnyWrap);};TestNoCanonicalIV(GetAR2);TestNarrowCanonicalIV(GetAR2);TestMatchingCanonicalIV(GetAR2, ARBitWidth);}TEST_F(ScalarEvolutionExpanderTest, SCEVExpanderShlNSW) {auto checkOneCase = [this](std::string &&str) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(str, Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");Function *F = M->getFunction("f");ASSERT_NE(F, nullptr) << "Could not find function 'f'";BasicBlock &Entry = F->getEntryBlock();LoadInst *Load = cast<LoadInst>(&Entry.front());BinaryOperator *And = cast<BinaryOperator>(*Load->user_begin());ScalarEvolution SE = buildSE(*F);const SCEV *AndSCEV = SE.getSCEV(And);EXPECT_TRUE(isa<SCEVMulExpr>(AndSCEV));EXPECT_TRUE(cast<SCEVMulExpr>(AndSCEV)->hasNoSignedWrap());SCEVExpander Exp(SE, M->getDataLayout(), "expander");auto *I = cast<Instruction>(Exp.expandCodeFor(AndSCEV, nullptr, And));EXPECT_EQ(I->getOpcode(), Instruction::Shl);EXPECT_FALSE(I->hasNoSignedWrap());};checkOneCase("define void @f(i16* %arrayidx) { "" %1 = load i16, i16* %arrayidx "" %2 = and i16 %1, -32768 "" ret void ""} ");checkOneCase("define void @f(i8* %arrayidx) { "" %1 = load i8, i8* %arrayidx "" %2 = and i8 %1, -128 "" ret void ""} ");}// Test expansion of nested addrecs in CanonicalMode.// Expanding nested addrecs in canonical mode requiers a canonical IV of a// type wider than the type of the addrec itself. Currently, SCEVExpander// just falls back to literal mode for nested addrecs.TEST_F(ScalarEvolutionExpanderTest, SCEVExpandNonAffineAddRec) {LLVMContext C;SMDiagnostic Err;// Expand the addrec produced by GetAddRec into a loop without a canonical IV.auto TestNoCanonicalIV =[&](std::function<const SCEVAddRecExpr *(ScalarEvolution & SE, Loop * L)>GetAddRec) {std::unique_ptr<Module> M = parseAssemblyString("define i32 @test(i32 %limit) { ""entry: "" br label %loop ""loop: "" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "" %i.inc = add nsw i32 %i, 1 "" %cont = icmp slt i32 %i.inc, %limit "" br i1 %cont, label %loop, label %exit ""exit: "" ret i32 %i.inc ""}",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I = GetInstByName(F, "i");auto *Loop = LI.getLoopFor(I.getParent());EXPECT_FALSE(Loop->getCanonicalInductionVariable());auto *AR = GetAddRec(SE, Loop);EXPECT_FALSE(AR->isAffine());SCEVExpander Exp(SE, M->getDataLayout(), "expander");auto *InsertAt = I.getNextNode();Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt);auto *ExpandedAR = SE.getSCEV(V);// Check that the expansion happened literally.EXPECT_EQ(AR, ExpandedAR);});};// Expand the addrec produced by GetAddRec into a loop with a canonical IV// which is narrower than addrec type.auto TestNarrowCanonicalIV = [&](std::function<const SCEVAddRecExpr *(ScalarEvolution & SE, Loop * L)>GetAddRec) {std::unique_ptr<Module> M = parseAssemblyString("define i32 @test(i32 %limit) { ""entry: "" br label %loop ""loop: "" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "" %canonical.iv = phi i8 [ 0, %entry ], [ %canonical.iv.inc, %loop ] "" %i.inc = add nsw i32 %i, 1 "" %canonical.iv.inc = add i8 %canonical.iv, 1 "" %cont = icmp slt i32 %i.inc, %limit "" br i1 %cont, label %loop, label %exit ""exit: "" ret i32 %i.inc ""}",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I = GetInstByName(F, "i");auto *LoopHeaderBB = I.getParent();auto *Loop = LI.getLoopFor(LoopHeaderBB);PHINode *CanonicalIV = Loop->getCanonicalInductionVariable();EXPECT_EQ(CanonicalIV, &GetInstByName(F, "canonical.iv"));auto *AR = GetAddRec(SE, Loop);EXPECT_FALSE(AR->isAffine());unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType());unsigned CanonicalIVBitWidth =cast<IntegerType>(CanonicalIV->getType())->getBitWidth();EXPECT_LT(CanonicalIVBitWidth, ExpectedCanonicalIVWidth);SCEVExpander Exp(SE, M->getDataLayout(), "expander");auto *InsertAt = I.getNextNode();Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt);auto *ExpandedAR = SE.getSCEV(V);// Check that the expansion happened literally.EXPECT_EQ(AR, ExpandedAR);});};// Expand the addrec produced by GetAddRec into a loop with a canonical IV// of addrec width.auto TestMatchingCanonicalIV =[&](std::function<const SCEVAddRecExpr *(ScalarEvolution & SE, Loop * L)>GetAddRec,unsigned ARBitWidth) {auto ARBitWidthTypeStr = "i" + std::to_string(ARBitWidth);std::unique_ptr<Module> M = parseAssemblyString("define i32 @test(i32 %limit) { ""entry: "" br label %loop ""loop: "" %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] "" %canonical.iv = phi " +ARBitWidthTypeStr +" [ 0, %entry ], [ %canonical.iv.inc, %loop ] "" %i.inc = add nsw i32 %i, 1 "" %canonical.iv.inc = add " +ARBitWidthTypeStr +" %canonical.iv, 1 "" %cont = icmp slt i32 %i.inc, %limit "" br i1 %cont, label %loop, label %exit ""exit: "" ret i32 %i.inc ""}",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I = GetInstByName(F, "i");auto &CanonicalIV = GetInstByName(F, "canonical.iv");auto *LoopHeaderBB = I.getParent();auto *Loop = LI.getLoopFor(LoopHeaderBB);EXPECT_EQ(&CanonicalIV, Loop->getCanonicalInductionVariable());unsigned CanonicalIVBitWidth =cast<IntegerType>(CanonicalIV.getType())->getBitWidth();auto *AR = GetAddRec(SE, Loop);EXPECT_FALSE(AR->isAffine());EXPECT_EQ(ARBitWidth, SE.getTypeSizeInBits(AR->getType()));EXPECT_EQ(CanonicalIVBitWidth, ARBitWidth);SCEVExpander Exp(SE, M->getDataLayout(), "expander");auto *InsertAt = I.getNextNode();Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt);auto *ExpandedAR = SE.getSCEV(V);// Check that the expansion happened literally.EXPECT_EQ(AR, ExpandedAR);});};unsigned ARBitWidth = 16;Type *ARType = IntegerType::get(C, ARBitWidth);// Expand {5,+,1,+,1}auto GetAR3 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * {SmallVector<const SCEV *, 3> Ops = {SE.getConstant(APInt(ARBitWidth, 5)),SE.getOne(ARType), SE.getOne(ARType)};return cast<SCEVAddRecExpr>(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap));};TestNoCanonicalIV(GetAR3);TestNarrowCanonicalIV(GetAR3);TestMatchingCanonicalIV(GetAR3, ARBitWidth);// Expand {5,+,1,+,1,+,1}auto GetAR4 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * {SmallVector<const SCEV *, 4> Ops = {SE.getConstant(APInt(ARBitWidth, 5)),SE.getOne(ARType), SE.getOne(ARType),SE.getOne(ARType)};return cast<SCEVAddRecExpr>(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap));};TestNoCanonicalIV(GetAR4);TestNarrowCanonicalIV(GetAR4);TestMatchingCanonicalIV(GetAR4, ARBitWidth);// Expand {5,+,1,+,1,+,1,+,1}auto GetAR5 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * {SmallVector<const SCEV *, 5> Ops = {SE.getConstant(APInt(ARBitWidth, 5)),SE.getOne(ARType), SE.getOne(ARType),SE.getOne(ARType), SE.getOne(ARType)};return cast<SCEVAddRecExpr>(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap));};TestNoCanonicalIV(GetAR5);TestNarrowCanonicalIV(GetAR5);TestMatchingCanonicalIV(GetAR5, ARBitWidth);}TEST_F(ScalarEvolutionExpanderTest, ExpandNonIntegralPtrWithNullBase) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M =parseAssemblyString("target datalayout = ""\"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:""128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2\"""define float addrspace(1)* @test(i64 %offset) { "" %ptr = getelementptr inbounds float, float ""addrspace(1)* null, i64 %offset"" ret float addrspace(1)* %ptr""}",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I = GetInstByName(F, "ptr");auto PtrPlus1 =SE.getAddExpr(SE.getSCEV(&I), SE.getConstant(I.getType(), 1));SCEVExpander Exp(SE, M->getDataLayout(), "expander");Value *V = Exp.expandCodeFor(PtrPlus1, I.getType(), &I);I.replaceAllUsesWith(V);// Check that the expander created:// define float addrspace(1)* @test(i64 %off) {// %scevgep = getelementptr float, float addrspace(1)* null, i64 %off// %scevgep1 = bitcast float addrspace(1)* %scevgep to i8 addrspace(1)*// %uglygep = getelementptr i8, i8 addrspace(1)* %scevgep1, i64 1// %uglygep2 = bitcast i8 addrspace(1)* %uglygep to float addrspace(1)*// %ptr = getelementptr inbounds float, float addrspace(1)* null, i64 %off// ret float addrspace(1)* %uglygep2// }auto *Cast = dyn_cast<BitCastInst>(V);EXPECT_TRUE(Cast);EXPECT_EQ(Cast->getType(), I.getType());auto *GEP = dyn_cast<GetElementPtrInst>(Cast->getOperand(0));EXPECT_TRUE(GEP);EXPECT_TRUE(match(GEP->getOperand(1), m_SpecificInt(1)));auto *Cast1 = dyn_cast<BitCastInst>(GEP->getPointerOperand());EXPECT_TRUE(Cast1);auto *GEP1 = dyn_cast<GetElementPtrInst>(Cast1->getOperand(0));EXPECT_TRUE(GEP1);EXPECT_TRUE(cast<Constant>(GEP1->getPointerOperand())->isNullValue());EXPECT_EQ(GEP1->getOperand(1), &*F.arg_begin());EXPECT_EQ(cast<PointerType>(GEP1->getPointerOperand()->getType())->getAddressSpace(),cast<PointerType>(I.getType())->getAddressSpace());EXPECT_FALSE(verifyFunction(F, &errs()));});}} // end namespace llvm
//===- SSAUpdaterBulk.cpp - Unit tests for SSAUpdaterBulk -----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/SSAUpdaterBulk.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;TEST(SSAUpdaterBulk, SimpleMerge) {SSAUpdaterBulk Updater;LLVMContext C;Module M("SSAUpdaterTest", C);IRBuilder<> B(C);Type *I32Ty = B.getInt32Ty();auto *F = Function::Create(FunctionType::get(B.getVoidTy(), {I32Ty}, false),GlobalValue::ExternalLinkage, "F", &M);// Generate a simple program:// if:// br i1 true, label %true, label %false// true:// %1 = add i32 %0, 1// %2 = sub i32 %0, 2// br label %merge// false:// %3 = add i32 %0, 3// %4 = sub i32 %0, 4// br label %merge// merge:// %5 = add i32 %1, 5// %6 = add i32 %3, 6// %7 = add i32 %2, %4// %8 = sub i32 %2, %4Argument *FirstArg = &*(F->arg_begin());BasicBlock *IfBB = BasicBlock::Create(C, "if", F);BasicBlock *TrueBB = BasicBlock::Create(C, "true", F);BasicBlock *FalseBB = BasicBlock::Create(C, "false", F);BasicBlock *MergeBB = BasicBlock::Create(C, "merge", F);B.SetInsertPoint(IfBB);B.CreateCondBr(B.getTrue(), TrueBB, FalseBB);B.SetInsertPoint(TrueBB);Value *AddOp1 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 1));Value *SubOp1 = B.CreateSub(FirstArg, ConstantInt::get(I32Ty, 2));B.CreateBr(MergeBB);B.SetInsertPoint(FalseBB);Value *AddOp2 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 3));Value *SubOp2 = B.CreateSub(FirstArg, ConstantInt::get(I32Ty, 4));B.CreateBr(MergeBB);B.SetInsertPoint(MergeBB, MergeBB->begin());auto *I1 = cast<Instruction>(B.CreateAdd(AddOp1, ConstantInt::get(I32Ty, 5)));auto *I2 = cast<Instruction>(B.CreateAdd(AddOp2, ConstantInt::get(I32Ty, 6)));auto *I3 = cast<Instruction>(B.CreateAdd(SubOp1, SubOp2));auto *I4 = cast<Instruction>(B.CreateSub(SubOp1, SubOp2));// Now rewrite uses in instructions %5, %6, %7. They need to use a phi, which// SSAUpdater should insert into %merge.// Intentionally don't touch %8 to see that SSAUpdater only changes// instructions that were explicitly specified.unsigned VarNum = Updater.AddVariable("a", I32Ty);Updater.AddAvailableValue(VarNum, TrueBB, AddOp1);Updater.AddAvailableValue(VarNum, FalseBB, AddOp2);Updater.AddUse(VarNum, &I1->getOperandUse(0));Updater.AddUse(VarNum, &I2->getOperandUse(0));VarNum = Updater.AddVariable("b", I32Ty);Updater.AddAvailableValue(VarNum, TrueBB, SubOp1);Updater.AddAvailableValue(VarNum, FalseBB, SubOp2);Updater.AddUse(VarNum, &I3->getOperandUse(0));Updater.AddUse(VarNum, &I3->getOperandUse(1));DominatorTree DT(*F);Updater.RewriteAllUses(&DT);// Check how %5 and %6 were rewritten.PHINode *UpdatePhiA = dyn_cast_or_null<PHINode>(I1->getOperand(0));EXPECT_NE(UpdatePhiA, nullptr);EXPECT_EQ(UpdatePhiA->getIncomingValueForBlock(TrueBB), AddOp1);EXPECT_EQ(UpdatePhiA->getIncomingValueForBlock(FalseBB), AddOp2);EXPECT_EQ(UpdatePhiA, dyn_cast_or_null<PHINode>(I1->getOperand(0)));// Check how %7 was rewritten.PHINode *UpdatePhiB = dyn_cast_or_null<PHINode>(I3->getOperand(0));EXPECT_EQ(UpdatePhiB->getIncomingValueForBlock(TrueBB), SubOp1);EXPECT_EQ(UpdatePhiB->getIncomingValueForBlock(FalseBB), SubOp2);EXPECT_EQ(UpdatePhiB, dyn_cast_or_null<PHINode>(I3->getOperand(1)));// Check that %8 was kept untouched.EXPECT_EQ(I4->getOperand(0), SubOp1);EXPECT_EQ(I4->getOperand(1), SubOp2);}TEST(SSAUpdaterBulk, Irreducible) {SSAUpdaterBulk Updater;LLVMContext C;Module M("SSAUpdaterTest", C);IRBuilder<> B(C);Type *I32Ty = B.getInt32Ty();auto *F = Function::Create(FunctionType::get(B.getVoidTy(), {I32Ty}, false),GlobalValue::ExternalLinkage, "F", &M);// Generate a small program with a multi-entry loop:// if:// %1 = add i32 %0, 1// br i1 true, label %loopmain, label %loopstart//// loopstart:// %2 = add i32 %0, 2// br label %loopmain//// loopmain:// %3 = add i32 %1, 3// br i1 true, label %loopstart, label %afterloop//// afterloop:// %4 = add i32 %2, 4// ret i32 %0Argument *FirstArg = &*F->arg_begin();BasicBlock *IfBB = BasicBlock::Create(C, "if", F);BasicBlock *LoopStartBB = BasicBlock::Create(C, "loopstart", F);BasicBlock *LoopMainBB = BasicBlock::Create(C, "loopmain", F);BasicBlock *AfterLoopBB = BasicBlock::Create(C, "afterloop", F);B.SetInsertPoint(IfBB);Value *AddOp1 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 1));B.CreateCondBr(B.getTrue(), LoopMainBB, LoopStartBB);B.SetInsertPoint(LoopStartBB);Value *AddOp2 = B.CreateAdd(FirstArg, ConstantInt::get(I32Ty, 2));B.CreateBr(LoopMainBB);B.SetInsertPoint(LoopMainBB);auto *I1 = cast<Instruction>(B.CreateAdd(AddOp1, ConstantInt::get(I32Ty, 3)));B.CreateCondBr(B.getTrue(), LoopStartBB, AfterLoopBB);B.SetInsertPoint(AfterLoopBB);auto *I2 = cast<Instruction>(B.CreateAdd(AddOp2, ConstantInt::get(I32Ty, 4)));ReturnInst *Return = B.CreateRet(FirstArg);// Now rewrite uses in instructions %3, %4, and 'ret i32 %0'. Only %4 needs a// new phi, others should be able to work with existing values.// The phi for %4 should be inserted into LoopMainBB and should look like// this:// %b = phi i32 [ %2, %loopstart ], [ undef, %if ]// No other rewrites should be made.// Add use in %3.unsigned VarNum = Updater.AddVariable("c", I32Ty);Updater.AddAvailableValue(VarNum, IfBB, AddOp1);Updater.AddUse(VarNum, &I1->getOperandUse(0));// Add use in %4.VarNum = Updater.AddVariable("b", I32Ty);Updater.AddAvailableValue(VarNum, LoopStartBB, AddOp2);Updater.AddUse(VarNum, &I2->getOperandUse(0));// Add use in the return instruction.VarNum = Updater.AddVariable("a", I32Ty);Updater.AddAvailableValue(VarNum, &F->getEntryBlock(), FirstArg);Updater.AddUse(VarNum, &Return->getOperandUse(0));// Save all inserted phis into a vector.SmallVector<PHINode *, 8> Inserted;DominatorTree DT(*F);Updater.RewriteAllUses(&DT, &Inserted);// Only one phi should have been inserted.EXPECT_EQ(Inserted.size(), 1u);// I1 and Return should use the same values as they used before.EXPECT_EQ(I1->getOperand(0), AddOp1);EXPECT_EQ(Return->getOperand(0), FirstArg);// I2 should use the new phi.PHINode *UpdatePhi = dyn_cast_or_null<PHINode>(I2->getOperand(0));EXPECT_NE(UpdatePhi, nullptr);EXPECT_EQ(UpdatePhi->getIncomingValueForBlock(LoopStartBB), AddOp2);EXPECT_EQ(UpdatePhi->getIncomingValueForBlock(IfBB), UndefValue::get(I32Ty));}
//===- ModuleUtilsTest.cpp - Unit tests for Module 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/ModuleUtils.h"#include "llvm/ADT/StringRef.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("ModuleUtilsTest", errs());return Mod;}static int getUsedListSize(Module &M, StringRef Name) {auto *UsedList = M.getGlobalVariable(Name);if (!UsedList)return 0;auto *UsedListBaseArrayType = cast<ArrayType>(UsedList->getValueType());return UsedListBaseArrayType->getNumElements();}TEST(ModuleUtils, AppendToUsedList1) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(@x = addrspace(4) global [2 x i32] zeroinitializer, align 4)");SmallVector<GlobalValue *, 2> Globals;for (auto &G : M->globals()) {Globals.push_back(&G);}EXPECT_EQ(0, getUsedListSize(*M, "llvm.compiler.used"));appendToCompilerUsed(*M, Globals);EXPECT_EQ(1, getUsedListSize(*M, "llvm.compiler.used"));EXPECT_EQ(0, getUsedListSize(*M, "llvm.used"));appendToUsed(*M, Globals);EXPECT_EQ(1, getUsedListSize(*M, "llvm.used"));}TEST(ModuleUtils, AppendToUsedList2) {LLVMContext C;std::unique_ptr<Module> M =parseIR(C, R"(@x = global [2 x i32] zeroinitializer, align 4)");SmallVector<GlobalValue *, 2> Globals;for (auto &G : M->globals()) {Globals.push_back(&G);}EXPECT_EQ(0, getUsedListSize(*M, "llvm.compiler.used"));appendToCompilerUsed(*M, Globals);EXPECT_EQ(1, getUsedListSize(*M, "llvm.compiler.used"));EXPECT_EQ(0, getUsedListSize(*M, "llvm.used"));appendToUsed(*M, Globals);EXPECT_EQ(1, getUsedListSize(*M, "llvm.used"));}
//=========- MemTransferLowerTest.cpp - MemTransferLower unit tests -=========////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/CGSCCPassManager.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetTransformInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Function.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/PassManager.h"#include "llvm/InitializePasses.h"#include "llvm/Passes/PassBuilder.h"#include "llvm/Support/Debug.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"#include "llvm/Transforms/Vectorize/LoopVectorize.h"#include "gtest/gtest-spi.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct ForwardingPass : public PassInfoMixin<ForwardingPass> {template <typename T> ForwardingPass(T &&Arg) : Func(std::forward<T>(Arg)) {}PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {return Func(F, FAM);}std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func;};struct MemTransferLowerTest : public testing::Test {PassBuilder PB;LoopAnalysisManager LAM;FunctionAnalysisManager FAM;CGSCCAnalysisManager CGAM;ModuleAnalysisManager MAM;ModulePassManager MPM;LLVMContext Context;std::unique_ptr<Module> M;MemTransferLowerTest() {// Register all the basic analyses with the managers.PB.registerModuleAnalyses(MAM);PB.registerCGSCCAnalyses(CGAM);PB.registerFunctionAnalyses(FAM);PB.registerLoopAnalyses(LAM);PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);}BasicBlock *getBasicBlockByName(Function &F, StringRef Name) const {for (BasicBlock &BB : F) {if (BB.getName() == Name)return &BB;}return nullptr;}Instruction *getInstructionByOpcode(BasicBlock &BB, unsigned Opcode,unsigned Number) const {unsigned CurrNumber = 0;for (Instruction &I : BB)if (I.getOpcode() == Opcode) {++CurrNumber;if (CurrNumber == Number)return &I;}return nullptr;}void ParseAssembly(const char *IR) {SMDiagnostic Error;M = parseAssemblyString(IR, Error, Context);std::string errMsg;raw_string_ostream os(errMsg);Error.print("", os);// A failure here means that the test itself is buggy.if (!M)report_fatal_error(os.str().c_str());}};// By semantics source and destination of llvm.memcpy.* intrinsic// are either equal or don't overlap. Once the intrinsic is lowered// to a loop it can be hard or impossible to reason about these facts.// For that reason expandMemCpyAsLoop is expected to explicitly mark// loads from source and stores to destination as not aliasing.TEST_F(MemTransferLowerTest, MemCpyKnownLength) {ParseAssembly("declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8 *, i64, i1)\n""define void @foo(i8* %dst, i8* %src, i64 %n) optsize {\n""entry:\n"" %is_not_equal = icmp ne i8* %dst, %src\n"" br i1 %is_not_equal, label %memcpy, label %exit\n""memcpy:\n"" call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, ""i64 1024, i1 false)\n"" br label %exit\n""exit:\n"" ret void\n""}\n");FunctionPassManager FPM;FPM.addPass(ForwardingPass([=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {TargetTransformInfo TTI(M->getDataLayout());auto *MemCpyBB = getBasicBlockByName(F, "memcpy");Instruction *Inst = &MemCpyBB->front();MemCpyInst *MemCpyI = cast<MemCpyInst>(Inst);auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);expandMemCpyAsLoop(MemCpyI, TTI, &SE);auto *CopyLoopBB = getBasicBlockByName(F, "load-store-loop");Instruction *LoadInst =getInstructionByOpcode(*CopyLoopBB, Instruction::Load, 1);EXPECT_NE(nullptr, LoadInst->getMetadata(LLVMContext::MD_alias_scope));Instruction *StoreInst =getInstructionByOpcode(*CopyLoopBB, Instruction::Store, 1);EXPECT_NE(nullptr, StoreInst->getMetadata(LLVMContext::MD_noalias));return PreservedAnalyses::none();}));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}// This test indirectly checks that loads and stores (generated as a result of// llvm.memcpy lowering) doesn't alias by making sure the loop can be// successfully vectorized without additional runtime checks.TEST_F(MemTransferLowerTest, VecMemCpyKnownLength) {ParseAssembly("declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8 *, i64, i1)\n""define void @foo(i8* %dst, i8* %src, i64 %n) optsize {\n""entry:\n"" %is_not_equal = icmp ne i8* %dst, %src\n"" br i1 %is_not_equal, label %memcpy, label %exit\n""memcpy:\n"" call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, ""i64 1024, i1 false)\n"" br label %exit\n""exit:\n"" ret void\n""}\n");FunctionPassManager FPM;FPM.addPass(ForwardingPass([=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {TargetTransformInfo TTI(M->getDataLayout());auto *MemCpyBB = getBasicBlockByName(F, "memcpy");Instruction *Inst = &MemCpyBB->front();MemCpyInst *MemCpyI = cast<MemCpyInst>(Inst);auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);expandMemCpyAsLoop(MemCpyI, TTI, &SE);return PreservedAnalyses::none();}));FPM.addPass(LoopVectorizePass(LoopVectorizeOptions()));FPM.addPass(ForwardingPass([=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {auto *TargetBB = getBasicBlockByName(F, "vector.body");EXPECT_NE(nullptr, TargetBB);return PreservedAnalyses::all();}));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}TEST_F(MemTransferLowerTest, AtomicMemCpyKnownLength) {ParseAssembly("declare void ""@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32*, ""i32 *, i64, i32)\n""define void @foo(i32* %dst, i32* %src, i64 %n) optsize {\n""entry:\n"" %is_not_equal = icmp ne i32* %dst, %src\n"" br i1 %is_not_equal, label %memcpy, label %exit\n""memcpy:\n"" call void ""@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32* ""%dst, i32* %src, ""i64 1024, i32 4)\n"" br label %exit\n""exit:\n"" ret void\n""}\n");FunctionPassManager FPM;FPM.addPass(ForwardingPass([=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {TargetTransformInfo TTI(M->getDataLayout());auto *MemCpyBB = getBasicBlockByName(F, "memcpy");Instruction *Inst = &MemCpyBB->front();assert(isa<AtomicMemCpyInst>(Inst) &&"Expecting llvm.memcpy.p0i8.i64 instructon");AtomicMemCpyInst *MemCpyI = cast<AtomicMemCpyInst>(Inst);auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);expandAtomicMemCpyAsLoop(MemCpyI, TTI, &SE);auto *CopyLoopBB = getBasicBlockByName(F, "load-store-loop");Instruction *LoadInst =getInstructionByOpcode(*CopyLoopBB, Instruction::Load, 1);EXPECT_TRUE(LoadInst->isAtomic());EXPECT_NE(LoadInst->getMetadata(LLVMContext::MD_alias_scope), nullptr);Instruction *StoreInst =getInstructionByOpcode(*CopyLoopBB, Instruction::Store, 1);EXPECT_TRUE(StoreInst->isAtomic());EXPECT_NE(StoreInst->getMetadata(LLVMContext::MD_noalias), nullptr);return PreservedAnalyses::none();}));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}TEST_F(MemTransferLowerTest, AtomicMemCpyUnKnownLength) {ParseAssembly("declare void ""@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32*, ""i32 *, i64, i32)\n""define void @foo(i32* %dst, i32* %src, i64 %n) optsize {\n""entry:\n"" %is_not_equal = icmp ne i32* %dst, %src\n"" br i1 %is_not_equal, label %memcpy, label %exit\n""memcpy:\n"" call void ""@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32* ""%dst, i32* %src, ""i64 %n, i32 4)\n"" br label %exit\n""exit:\n"" ret void\n""}\n");FunctionPassManager FPM;FPM.addPass(ForwardingPass([=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {TargetTransformInfo TTI(M->getDataLayout());auto *MemCpyBB = getBasicBlockByName(F, "memcpy");Instruction *Inst = &MemCpyBB->front();assert(isa<AtomicMemCpyInst>(Inst) &&"Expecting llvm.memcpy.p0i8.i64 instructon");AtomicMemCpyInst *MemCpyI = cast<AtomicMemCpyInst>(Inst);auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);expandAtomicMemCpyAsLoop(MemCpyI, TTI, &SE);auto *CopyLoopBB = getBasicBlockByName(F, "loop-memcpy-expansion");Instruction *LoadInst =getInstructionByOpcode(*CopyLoopBB, Instruction::Load, 1);EXPECT_TRUE(LoadInst->isAtomic());EXPECT_NE(LoadInst->getMetadata(LLVMContext::MD_alias_scope), nullptr);Instruction *StoreInst =getInstructionByOpcode(*CopyLoopBB, Instruction::Store, 1);EXPECT_TRUE(StoreInst->isAtomic());EXPECT_NE(StoreInst->getMetadata(LLVMContext::MD_noalias), nullptr);return PreservedAnalyses::none();}));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}} // namespace
//===- LoopUtilsTest.cpp - Unit tests for LoopUtils -----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/LoopUtils.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("LoopUtilsTests", errs());return Mod;}static void run(Module &M, StringRef FuncName,function_ref<void(Function &F, DominatorTree &DT,ScalarEvolution &SE, LoopInfo &LI)>Test) {Function *F = M.getFunction(FuncName);DominatorTree DT(*F);TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AssumptionCache AC(*F);LoopInfo LI(DT);ScalarEvolution SE(*F, TLI, AC, DT, LI);Test(*F, DT, SE, LI);}TEST(LoopUtils, DeleteDeadLoopNest) {LLVMContext C;std::unique_ptr<Module> M =parseIR(C, "define void @foo() {\n""entry:\n"" br label %for.i\n""for.i:\n"" %i = phi i64 [ 0, %entry ], [ %inc.i, %for.i.latch ]\n"" br label %for.j\n""for.j:\n"" %j = phi i64 [ 0, %for.i ], [ %inc.j, %for.j ]\n"" %inc.j = add nsw i64 %j, 1\n"" %cmp.j = icmp slt i64 %inc.j, 100\n"" br i1 %cmp.j, label %for.j, label %for.k.preheader\n""for.k.preheader:\n"" br label %for.k\n""for.k:\n"" %k = phi i64 [ %inc.k, %for.k ], [ 0, %for.k.preheader ]\n"" %inc.k = add nsw i64 %k, 1\n"" %cmp.k = icmp slt i64 %inc.k, 100\n"" br i1 %cmp.k, label %for.k, label %for.i.latch\n""for.i.latch:\n"" %inc.i = add nsw i64 %i, 1\n"" %cmp.i = icmp slt i64 %inc.i, 100\n"" br i1 %cmp.i, label %for.i, label %for.end\n""for.end:\n"" ret void\n""}\n");run(*M, "foo",[&](Function &F, DominatorTree &DT, ScalarEvolution &SE, LoopInfo &LI) {assert(LI.begin() != LI.end() && "Expecting loops in function F");Loop *L = *LI.begin();assert(L && L->getName() == "for.i" && "Expecting loop for.i");deleteDeadLoop(L, &DT, &SE, &LI);assert(DT.verify(DominatorTree::VerificationLevel::Fast) &&"Expecting valid dominator tree");LI.verify(DT);assert(LI.begin() == LI.end() &&"Expecting no loops left in function F");SE.verify();Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI++);assert(Entry->getName() == "entry" && "Expecting BasicBlock entry");const BranchInst *BI = dyn_cast<BranchInst>(Entry->getTerminator());assert(BI && "Expecting valid branch instruction");EXPECT_EQ(BI->getNumSuccessors(), (unsigned)1);EXPECT_EQ(BI->getSuccessor(0)->getName(), "for.end");});}
//===- LoopRotationUtilsTest.cpp - Unit tests for LoopRotation 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/LoopRotationUtils.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/InstructionSimplify.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/Analysis/TargetTransformInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("LoopRotationUtilsTest", errs());return Mod;}/// This test contains multi-deopt-exits pattern that might allow loop rotation/// to trigger multiple times if multiple rotations are enabled./// At least one rotation should be performed, no matter what loop rotation settings are.TEST(LoopRotate, MultiDeoptExit) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(declare i32 @llvm.experimental.deoptimize.i32(...)define i32 @test(i32 * nonnull %a, i64 %x) {entry:br label %for.cond1for.cond1:%idx = phi i64 [ 0, %entry ], [ %idx.next, %for.tail ]%sum = phi i32 [ 0, %entry ], [ %sum.next, %for.tail ]%a.idx = getelementptr inbounds i32, i32 *%a, i64 %idx%val.a.idx = load i32, i32* %a.idx, align 4%zero.check = icmp eq i32 %val.a.idx, 0br i1 %zero.check, label %deopt.exit, label %for.cond2for.cond2:%for.check = icmp ult i64 %idx, %xbr i1 %for.check, label %for.body, label %returnfor.body:br label %for.tailfor.tail:%sum.next = add i32 %sum, %val.a.idx%idx.next = add nuw nsw i64 %idx, 1br label %for.cond1return:ret i32 %sumdeopt.exit:%deopt.val = call i32(...) @llvm.experimental.deoptimize.i32() [ "deopt"(i32 %val.a.idx) ]ret i32 %deopt.val})");auto *F = M->getFunction("test");DominatorTree DT(*F);LoopInfo LI(DT);AssumptionCache AC(*F);TargetTransformInfo TTI(M->getDataLayout());TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);ScalarEvolution SE(*F, TLI, AC, DT, LI);SimplifyQuery SQ(M->getDataLayout());Loop *L = *LI.begin();bool ret = LoopRotation(L, &LI, &TTI,&AC, &DT,&SE, nullptr,SQ, true, -1, false);EXPECT_TRUE(ret);}/// Checking a special case of multi-deopt exit loop that can not perform/// required amount of rotations due to the desired header containing/// non-duplicatable code./// Similar to MultiDeoptExit test this one should do at least one rotation and/// pass no matter what loop rotation settings are.TEST(LoopRotate, MultiDeoptExit_Nondup) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(; Rotation should be done once, attempted twice.; Second time fails due to non-duplicatable header.declare i32 @llvm.experimental.deoptimize.i32(...)declare void @nondup()define i32 @test_nondup(i32 * nonnull %a, i64 %x) {entry:br label %for.cond1for.cond1:%idx = phi i64 [ 0, %entry ], [ %idx.next, %for.tail ]%sum = phi i32 [ 0, %entry ], [ %sum.next, %for.tail ]%a.idx = getelementptr inbounds i32, i32 *%a, i64 %idx%val.a.idx = load i32, i32* %a.idx, align 4%zero.check = icmp eq i32 %val.a.idx, 0br i1 %zero.check, label %deopt.exit, label %for.cond2for.cond2:call void @nondup() noduplicate%for.check = icmp ult i64 %idx, %xbr i1 %for.check, label %for.body, label %returnfor.body:br label %for.tailfor.tail:%sum.next = add i32 %sum, %val.a.idx%idx.next = add nuw nsw i64 %idx, 1br label %for.cond1return:ret i32 %sumdeopt.exit:%deopt.val = call i32(...) @llvm.experimental.deoptimize.i32() [ "deopt"(i32 %val.a.idx) ]ret i32 %deopt.val})");auto *F = M->getFunction("test_nondup");DominatorTree DT(*F);LoopInfo LI(DT);AssumptionCache AC(*F);TargetTransformInfo TTI(M->getDataLayout());TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);ScalarEvolution SE(*F, TLI, AC, DT, LI);SimplifyQuery SQ(M->getDataLayout());Loop *L = *LI.begin();bool ret = LoopRotation(L, &LI, &TTI,&AC, &DT,&SE, nullptr,SQ, true, -1, false);/// LoopRotation should properly report "true" as we still perform the first rotation/// so we do change the IR.EXPECT_TRUE(ret);}
//===- Local.cpp - Unit tests for Local -----------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/Local.h"#include "llvm/Analysis/DomTreeUpdater.h"#include "llvm/Analysis/InstructionSimplify.h"#include "llvm/Analysis/PostDominators.h"#include "llvm/Analysis/TargetTransformInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DebugInfo.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;TEST(Local, RecursivelyDeleteDeadPHINodes) {LLVMContext C;IRBuilder<> builder(C);// Make blocksBasicBlock *bb0 = BasicBlock::Create(C);BasicBlock *bb1 = BasicBlock::Create(C);builder.SetInsertPoint(bb0);PHINode *phi = builder.CreatePHI(Type::getInt32Ty(C), 2);BranchInst *br0 = builder.CreateCondBr(builder.getTrue(), bb0, bb1);builder.SetInsertPoint(bb1);BranchInst *br1 = builder.CreateBr(bb0);phi->addIncoming(phi, bb0);phi->addIncoming(phi, bb1);// The PHI will be removedEXPECT_TRUE(RecursivelyDeleteDeadPHINode(phi));// Make sure the blocks only contain the branchesEXPECT_EQ(&bb0->front(), br0);EXPECT_EQ(&bb1->front(), br1);builder.SetInsertPoint(bb0);phi = builder.CreatePHI(Type::getInt32Ty(C), 0);EXPECT_TRUE(RecursivelyDeleteDeadPHINode(phi));builder.SetInsertPoint(bb0);phi = builder.CreatePHI(Type::getInt32Ty(C), 0);builder.CreateAdd(phi, phi);EXPECT_TRUE(RecursivelyDeleteDeadPHINode(phi));bb0->dropAllReferences();bb1->dropAllReferences();delete bb0;delete bb1;}TEST(Local, RemoveDuplicatePHINodes) {LLVMContext C;IRBuilder<> B(C);std::unique_ptr<Function> F(Function::Create(FunctionType::get(B.getVoidTy(), false),GlobalValue::ExternalLinkage, "F"));BasicBlock *Entry(BasicBlock::Create(C, "", F.get()));BasicBlock *BB(BasicBlock::Create(C, "", F.get()));BranchInst::Create(BB, Entry);B.SetInsertPoint(BB);AssertingVH<PHINode> P1 = B.CreatePHI(Type::getInt32Ty(C), 2);P1->addIncoming(B.getInt32(42), Entry);PHINode *P2 = B.CreatePHI(Type::getInt32Ty(C), 2);P2->addIncoming(B.getInt32(42), Entry);AssertingVH<PHINode> P3 = B.CreatePHI(Type::getInt32Ty(C), 2);P3->addIncoming(B.getInt32(42), Entry);P3->addIncoming(B.getInt32(23), BB);PHINode *P4 = B.CreatePHI(Type::getInt32Ty(C), 2);P4->addIncoming(B.getInt32(42), Entry);P4->addIncoming(B.getInt32(23), BB);P1->addIncoming(P3, BB);P2->addIncoming(P4, BB);BranchInst::Create(BB, BB);// Verify that we can eliminate PHIs that become duplicates after chaning PHIs// downstream.EXPECT_TRUE(EliminateDuplicatePHINodes(BB));EXPECT_EQ(3U, BB->size());}static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("UtilsTests", errs());return Mod;}TEST(Local, ReplaceDbgDeclare) {LLVMContext C;// Original C source to get debug info for a local variable:// void f() { int x; }std::unique_ptr<Module> M = parseIR(C,R"(define void @f() !dbg !8 {entry:%x = alloca i32, align 4call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata !DIExpression()), !dbg !13call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata !DIExpression()), !dbg !13ret void, !dbg !14}declare void @llvm.dbg.declare(metadata, metadata, metadata)!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!3, !4}!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t2.c", directory: "foo")!2 = !{}!3 = !{i32 2, !"Dwarf Version", i32 4}!4 = !{i32 2, !"Debug Info Version", i32 3}!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2)!9 = !DISubroutineType(types: !10)!10 = !{null}!11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12)!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)!13 = !DILocation(line: 2, column: 7, scope: !8)!14 = !DILocation(line: 3, column: 1, scope: !8))");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = Inst->getNextNode()->getNextNode();ASSERT_TRUE(Inst);auto *DII = dyn_cast<DbgDeclareInst>(Inst);ASSERT_TRUE(DII);Value *NewBase = Constant::getNullValue(Type::getInt32PtrTy(C));DIBuilder DIB(*M);replaceDbgDeclare(AI, NewBase, DIB, DIExpression::ApplyOffset, 0);// There should be exactly two dbg.declares.int Declares = 0;for (const Instruction &I : F->front())if (isa<DbgDeclareInst>(I))Declares++;EXPECT_EQ(2, Declares);}/// Build the dominator tree for the function and run the Test.static void runWithDomTree(Module &M, StringRef FuncName,function_ref<void(Function &F, DominatorTree *DT)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;// Compute the dominator tree for the function.DominatorTree DT(*F);Test(*F, &DT);}TEST(Local, MergeBasicBlockIntoOnlyPred) {LLVMContext C;std::unique_ptr<Module> M;auto resetIR = [&]() {M = parseIR(C,R"(define i32 @f(i8* %str) {entry:br label %bb2.ibb2.i: ; preds = %bb4.i, %entrybr i1 false, label %bb4.i, label %base2flt.exit204bb4.i: ; preds = %bb2.ibr i1 false, label %base2flt.exit204, label %bb2.ibb10.i196.bb7.i197_crit_edge: ; No predecessors!br label %bb7.i197bb7.i197: ; preds = %bb10.i196.bb7.i197_crit_edge%.reg2mem.0 = phi i32 [ %.reg2mem.0, %bb10.i196.bb7.i197_crit_edge ]br i1 undef, label %base2flt.exit204, label %base2flt.exit204base2flt.exit204: ; preds = %bb7.i197, %bb7.i197, %bb2.i, %bb4.iret i32 0})");};auto resetIRReplaceEntry = [&]() {M = parseIR(C,R"(define i32 @f() {entry:br label %bb2.ibb2.i: ; preds = %entryret i32 0})");};auto Test = [&](Function &F, DomTreeUpdater &DTU) {for (Function::iterator I = F.begin(), E = F.end(); I != E;) {BasicBlock *BB = &*I++;BasicBlock *SinglePred = BB->getSinglePredecessor();if (!SinglePred || SinglePred == BB || BB->hasAddressTaken())continue;BranchInst *Term = dyn_cast<BranchInst>(SinglePred->getTerminator());if (Term && !Term->isConditional())MergeBasicBlockIntoOnlyPred(BB, &DTU);}if (DTU.hasDomTree()) {EXPECT_TRUE(DTU.getDomTree().verify());}if (DTU.hasPostDomTree()) {EXPECT_TRUE(DTU.getPostDomTree().verify());}};// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with// both DT and PDT.resetIR();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with// DT.resetIR();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {DomTreeUpdater DTU(*DT, DomTreeUpdater::UpdateStrategy::Eager);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with// PDT.resetIR();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(PDT, DomTreeUpdater::UpdateStrategy::Eager);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with// both DT and PDT.resetIR();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with// PDT.resetIR();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(PDT, DomTreeUpdater::UpdateStrategy::Lazy);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with DT.resetIR();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {DomTreeUpdater DTU(*DT, DomTreeUpdater::UpdateStrategy::Lazy);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with// both DT and PDT.resetIRReplaceEntry();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with// DT.resetIRReplaceEntry();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {DomTreeUpdater DTU(*DT, DomTreeUpdater::UpdateStrategy::Eager);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with// PDT.resetIRReplaceEntry();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(PDT, DomTreeUpdater::UpdateStrategy::Eager);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with// both DT and PDT.resetIRReplaceEntry();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with// PDT.resetIRReplaceEntry();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(PDT, DomTreeUpdater::UpdateStrategy::Lazy);Test(F, DTU);});// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with DT.resetIRReplaceEntry();runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT) {DomTreeUpdater DTU(*DT, DomTreeUpdater::UpdateStrategy::Lazy);Test(F, DTU);});}TEST(Local, ConstantFoldTerminator) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(define void @br_same_dest() {entry:br i1 false, label %bb0, label %bb0bb0:ret void}define void @br_different_dest() {entry:br i1 true, label %bb0, label %bb1bb0:br label %exitbb1:br label %exitexit:ret void}define void @switch_2_different_dest() {entry:switch i32 0, label %default [ i32 0, label %bb0 ]default:ret voidbb0:ret void}define void @switch_2_different_dest_default() {entry:switch i32 1, label %default [ i32 0, label %bb0 ]default:ret voidbb0:ret void}define void @switch_3_different_dest() {entry:switch i32 0, label %default [ i32 0, label %bb0i32 1, label %bb1 ]default:ret voidbb0:ret voidbb1:ret void}define void @switch_variable_2_default_dest(i32 %arg) {entry:switch i32 %arg, label %default [ i32 0, label %default ]default:ret void}define void @switch_constant_2_default_dest() {entry:switch i32 1, label %default [ i32 0, label %default ]default:ret void}define void @switch_constant_3_repeated_dest() {entry:switch i32 0, label %default [ i32 0, label %bb0i32 1, label %bb0 ]bb0:ret voiddefault:ret void}define void @indirectbr() {entry:indirectbr i8* blockaddress(@indirectbr, %bb0), [label %bb0, label %bb1]bb0:ret voidbb1:ret void}define void @indirectbr_repeated() {entry:indirectbr i8* blockaddress(@indirectbr_repeated, %bb0), [label %bb0, label %bb0]bb0:ret void}define void @indirectbr_unreachable() {entry:indirectbr i8* blockaddress(@indirectbr_unreachable, %bb0), [label %bb1]bb0:ret voidbb1:ret void})");auto CFAllTerminatorsEager = [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);for (Function::iterator I = F.begin(), E = F.end(); I != E;) {BasicBlock *BB = &*I++;ConstantFoldTerminator(BB, true, nullptr, &DTU);}EXPECT_TRUE(DTU.getDomTree().verify());EXPECT_TRUE(DTU.getPostDomTree().verify());};auto CFAllTerminatorsLazy = [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);for (Function::iterator I = F.begin(), E = F.end(); I != E;) {BasicBlock *BB = &*I++;ConstantFoldTerminator(BB, true, nullptr, &DTU);}EXPECT_TRUE(DTU.getDomTree().verify());EXPECT_TRUE(DTU.getPostDomTree().verify());};// Test ConstantFoldTerminator under Eager UpdateStrategy.runWithDomTree(*M, "br_same_dest", CFAllTerminatorsEager);runWithDomTree(*M, "br_different_dest", CFAllTerminatorsEager);runWithDomTree(*M, "switch_2_different_dest", CFAllTerminatorsEager);runWithDomTree(*M, "switch_2_different_dest_default", CFAllTerminatorsEager);runWithDomTree(*M, "switch_3_different_dest", CFAllTerminatorsEager);runWithDomTree(*M, "switch_variable_2_default_dest", CFAllTerminatorsEager);runWithDomTree(*M, "switch_constant_2_default_dest", CFAllTerminatorsEager);runWithDomTree(*M, "switch_constant_3_repeated_dest", CFAllTerminatorsEager);runWithDomTree(*M, "indirectbr", CFAllTerminatorsEager);runWithDomTree(*M, "indirectbr_repeated", CFAllTerminatorsEager);runWithDomTree(*M, "indirectbr_unreachable", CFAllTerminatorsEager);// Test ConstantFoldTerminator under Lazy UpdateStrategy.runWithDomTree(*M, "br_same_dest", CFAllTerminatorsLazy);runWithDomTree(*M, "br_different_dest", CFAllTerminatorsLazy);runWithDomTree(*M, "switch_2_different_dest", CFAllTerminatorsLazy);runWithDomTree(*M, "switch_2_different_dest_default", CFAllTerminatorsLazy);runWithDomTree(*M, "switch_3_different_dest", CFAllTerminatorsLazy);runWithDomTree(*M, "switch_variable_2_default_dest", CFAllTerminatorsLazy);runWithDomTree(*M, "switch_constant_2_default_dest", CFAllTerminatorsLazy);runWithDomTree(*M, "switch_constant_3_repeated_dest", CFAllTerminatorsLazy);runWithDomTree(*M, "indirectbr", CFAllTerminatorsLazy);runWithDomTree(*M, "indirectbr_repeated", CFAllTerminatorsLazy);runWithDomTree(*M, "indirectbr_unreachable", CFAllTerminatorsLazy);}struct SalvageDebugInfoTest : ::testing::Test {LLVMContext C;std::unique_ptr<Module> M;Function *F = nullptr;void SetUp() override {M = parseIR(C,R"(define void @f() !dbg !8 {entry:%x = add i32 0, 1%y = add i32 %x, 2call void @llvm.dbg.value(metadata i32 %x, metadata !11, metadata !DIExpression()), !dbg !13call void @llvm.dbg.value(metadata i32 %y, metadata !11, metadata !DIExpression()), !dbg !13ret void, !dbg !14}declare void @llvm.dbg.value(metadata, metadata, metadata)!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!3, !4}!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t2.c", directory: "foo")!2 = !{}!3 = !{i32 2, !"Dwarf Version", i32 4}!4 = !{i32 2, !"Debug Info Version", i32 3}!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2)!9 = !DISubroutineType(types: !10)!10 = !{null}!11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12)!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)!13 = !DILocation(line: 2, column: 7, scope: !8)!14 = !DILocation(line: 3, column: 1, scope: !8))");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);F = dyn_cast<Function>(GV);ASSERT_TRUE(F);}bool doesDebugValueDescribeX(const DbgValueInst &DI) {if (DI.getNumVariableLocationOps() != 1u)return false;const auto &CI = *cast<ConstantInt>(DI.getValue(0));if (CI.isZero())return DI.getExpression()->getElements().equals({dwarf::DW_OP_plus_uconst, 1, dwarf::DW_OP_stack_value});else if (CI.isOneValue())return DI.getExpression()->getElements().empty();return false;}bool doesDebugValueDescribeY(const DbgValueInst &DI) {if (DI.getNumVariableLocationOps() != 1u)return false;const auto &CI = *cast<ConstantInt>(DI.getVariableLocationOp(0));if (CI.isZero())return DI.getExpression()->getElements().equals({dwarf::DW_OP_plus_uconst, 1, dwarf::DW_OP_plus_uconst, 2,dwarf::DW_OP_stack_value});else if (CI.isOneValue())return DI.getExpression()->getElements().equals({dwarf::DW_OP_plus_uconst, 2, dwarf::DW_OP_stack_value});return false;}void verifyDebugValuesAreSalvaged() {// Check that the debug values for %x and %y are preserved.bool FoundX = false;bool FoundY = false;for (const Instruction &I : F->front()) {auto DI = dyn_cast<DbgValueInst>(&I);if (!DI) {// The function should only contain debug values and a terminator.ASSERT_TRUE(I.isTerminator());continue;}EXPECT_EQ(DI->getVariable()->getName(), "x");FoundX |= doesDebugValueDescribeX(*DI);FoundY |= doesDebugValueDescribeY(*DI);}ASSERT_TRUE(FoundX);ASSERT_TRUE(FoundY);}};TEST_F(SalvageDebugInfoTest, RecursiveInstDeletion) {Instruction *Inst = &F->front().front();Inst = Inst->getNextNode(); // Get %y = add ...ASSERT_TRUE(Inst);bool Deleted = RecursivelyDeleteTriviallyDeadInstructions(Inst);ASSERT_TRUE(Deleted);verifyDebugValuesAreSalvaged();}TEST_F(SalvageDebugInfoTest, RecursiveBlockSimplification) {BasicBlock *BB = &F->front();ASSERT_TRUE(BB);bool Deleted = SimplifyInstructionsInBlock(BB);ASSERT_TRUE(Deleted);verifyDebugValuesAreSalvaged();}TEST(Local, SimplifyVScaleWithRange) {LLVMContext C;Module M("Module", C);IntegerType *Ty = Type::getInt32Ty(C);Function *VScale = Intrinsic::getDeclaration(&M, Intrinsic::vscale, {Ty});auto *CI = CallInst::Create(VScale, {}, "vscale");// Test that simplifyCall won't try to query it's parent function for// vscale_range attributes in order to simplify llvm.vscale -> constant.EXPECT_EQ(simplifyCall(CI, SimplifyQuery(M.getDataLayout())), nullptr);delete CI;}TEST(Local, ChangeToUnreachable) {LLVMContext Ctx;std::unique_ptr<Module> M = parseIR(Ctx,R"(define internal void @foo() !dbg !6 {entry:ret void, !dbg !8}!llvm.dbg.cu = !{!0}!llvm.debugify = !{!3, !4}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "test.ll", directory: "/")!2 = !{}!3 = !{i32 1}!4 = !{i32 0}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, isLocal: true, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !2)!7 = !DISubroutineType(types: !2)!8 = !DILocation(line: 1, column: 1, scope: !6))");bool BrokenDebugInfo = true;verifyModule(*M, &errs(), &BrokenDebugInfo);ASSERT_FALSE(BrokenDebugInfo);Function &F = *cast<Function>(M->getNamedValue("foo"));BasicBlock &BB = F.front();Instruction &A = BB.front();DebugLoc DLA = A.getDebugLoc();ASSERT_TRUE(isa<ReturnInst>(&A));// One instruction should be affected.EXPECT_EQ(changeToUnreachable(&A), 1U);Instruction &B = BB.front();// There should be an uncreachable instruction.ASSERT_TRUE(isa<UnreachableInst>(&B));DebugLoc DLB = B.getDebugLoc();EXPECT_EQ(DLA, DLB);}TEST(Local, ReplaceAllDbgUsesWith) {using namespace llvm::dwarf;LLVMContext Ctx;// Note: The datalayout simulates Darwin/x86_64.std::unique_ptr<Module> M = parseIR(Ctx,R"(target datalayout = "e-m:o-i63:64-f80:128-n8:16:32:64-S128"declare i32 @escape(i32)define void @f() !dbg !6 {entry:%a = add i32 0, 1, !dbg !15call void @llvm.dbg.value(metadata i32 %a, metadata !9, metadata !DIExpression()), !dbg !15%b = add i64 0, 1, !dbg !16call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression()), !dbg !16call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul)), !dbg !16call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul, DW_OP_stack_value)), !dbg !16call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 8)), !dbg !16call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul, DW_OP_LLVM_fragment, 0, 8)), !dbg !16call void @llvm.dbg.value(metadata i64 %b, metadata !11, metadata !DIExpression(DW_OP_lit0, DW_OP_mul, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 8)), !dbg !16%c = inttoptr i64 0 to i64*, !dbg !17call void @llvm.dbg.declare(metadata i64* %c, metadata !13, metadata !DIExpression()), !dbg !17%d = inttoptr i64 0 to i32*, !dbg !18call void @llvm.dbg.addr(metadata i32* %d, metadata !20, metadata !DIExpression()), !dbg !18%e = add <2 x i16> zeroinitializer, zeroinitializercall void @llvm.dbg.value(metadata <2 x i16> %e, metadata !14, metadata !DIExpression()), !dbg !18%f = call i32 @escape(i32 0)call void @llvm.dbg.value(metadata i32 %f, metadata !9, metadata !DIExpression()), !dbg !15%barrier = call i32 @escape(i32 0)%g = call i32 @escape(i32 %f)call void @llvm.dbg.value(metadata i32 %g, metadata !9, metadata !DIExpression()), !dbg !15ret void, !dbg !19}declare void @llvm.dbg.addr(metadata, metadata, metadata)declare void @llvm.dbg.declare(metadata, metadata, metadata)declare void @llvm.dbg.value(metadata, metadata, metadata)!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "/Users/vsk/Desktop/foo.ll", directory: "/")!2 = !{}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !8)!7 = !DISubroutineType(types: !2)!8 = !{!9, !11, !13, !14}!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)!10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_signed)!11 = !DILocalVariable(name: "2", scope: !6, file: !1, line: 2, type: !12)!12 = !DIBasicType(name: "ty64", size: 64, encoding: DW_ATE_signed)!13 = !DILocalVariable(name: "3", scope: !6, file: !1, line: 3, type: !12)!14 = !DILocalVariable(name: "4", scope: !6, file: !1, line: 4, type: !10)!15 = !DILocation(line: 1, column: 1, scope: !6)!16 = !DILocation(line: 2, column: 1, scope: !6)!17 = !DILocation(line: 3, column: 1, scope: !6)!18 = !DILocation(line: 4, column: 1, scope: !6)!19 = !DILocation(line: 5, column: 1, scope: !6)!20 = !DILocalVariable(name: "5", scope: !6, file: !1, line: 5, type: !10))");bool BrokenDebugInfo = true;verifyModule(*M, &errs(), &BrokenDebugInfo);ASSERT_FALSE(BrokenDebugInfo);Function &F = *cast<Function>(M->getNamedValue("f"));DominatorTree DT{F};BasicBlock &BB = F.front();Instruction &A = BB.front();Instruction &B = *A.getNextNonDebugInstruction();Instruction &C = *B.getNextNonDebugInstruction();Instruction &D = *C.getNextNonDebugInstruction();Instruction &E = *D.getNextNonDebugInstruction();Instruction &F_ = *E.getNextNonDebugInstruction();Instruction &Barrier = *F_.getNextNonDebugInstruction();Instruction &G = *Barrier.getNextNonDebugInstruction();// Simulate i32 <-> i64* conversion. Expect no updates: the datalayout says// pointers are 64 bits, so the conversion would be lossy.EXPECT_FALSE(replaceAllDbgUsesWith(A, C, C, DT));EXPECT_FALSE(replaceAllDbgUsesWith(C, A, A, DT));// Simulate i32 <-> <2 x i16> conversion. This is unsupported.EXPECT_FALSE(replaceAllDbgUsesWith(E, A, A, DT));EXPECT_FALSE(replaceAllDbgUsesWith(A, E, E, DT));// Simulate i32* <-> i64* conversion.EXPECT_TRUE(replaceAllDbgUsesWith(D, C, C, DT));SmallVector<DbgVariableIntrinsic *, 2> CDbgVals;findDbgUsers(CDbgVals, &C);EXPECT_EQ(2U, CDbgVals.size());EXPECT_TRUE(any_of(CDbgVals, [](DbgVariableIntrinsic *DII) {return isa<DbgAddrIntrinsic>(DII);}));EXPECT_TRUE(any_of(CDbgVals, [](DbgVariableIntrinsic *DII) {return isa<DbgDeclareInst>(DII);}));EXPECT_TRUE(replaceAllDbgUsesWith(C, D, D, DT));SmallVector<DbgVariableIntrinsic *, 2> DDbgVals;findDbgUsers(DDbgVals, &D);EXPECT_EQ(2U, DDbgVals.size());EXPECT_TRUE(any_of(DDbgVals, [](DbgVariableIntrinsic *DII) {return isa<DbgAddrIntrinsic>(DII);}));EXPECT_TRUE(any_of(DDbgVals, [](DbgVariableIntrinsic *DII) {return isa<DbgDeclareInst>(DII);}));// Introduce a use-before-def. Check that the dbg.value for %a is salvaged.EXPECT_TRUE(replaceAllDbgUsesWith(A, F_, F_, DT));auto *ADbgVal = cast<DbgValueInst>(A.getNextNode());EXPECT_EQ(ADbgVal->getNumVariableLocationOps(), 1u);EXPECT_EQ(ConstantInt::get(A.getType(), 0), ADbgVal->getVariableLocationOp(0));// Introduce a use-before-def. Check that the dbg.values for %f become undef.EXPECT_TRUE(replaceAllDbgUsesWith(F_, G, G, DT));auto *FDbgVal = cast<DbgValueInst>(F_.getNextNode());EXPECT_EQ(FDbgVal->getNumVariableLocationOps(), 1u);EXPECT_TRUE(FDbgVal->isUndef());SmallVector<DbgValueInst *, 1> FDbgVals;findDbgValues(FDbgVals, &F_);EXPECT_EQ(0U, FDbgVals.size());// Simulate i32 -> i64 conversion to test sign-extension. Here are some// interesting cases to handle:// 1) debug user has empty DIExpression// 2) debug user has non-empty, non-stack-value'd DIExpression// 3) debug user has non-empty, stack-value'd DIExpression// 4-6) like (1-3), but with a fragmentEXPECT_TRUE(replaceAllDbgUsesWith(B, A, A, DT));SmallVector<DbgValueInst *, 8> ADbgVals;findDbgValues(ADbgVals, &A);EXPECT_EQ(6U, ADbgVals.size());// Check that %a has a dbg.value with a DIExpression matching \p Ops.auto hasADbgVal = [&](ArrayRef<uint64_t> Ops) {return any_of(ADbgVals, [&](DbgValueInst *DVI) {assert(DVI->getVariable()->getName() == "2");return DVI->getExpression()->getElements() == Ops;});};// Case 1: The original expr is empty, so no deref is needed.EXPECT_TRUE(hasADbgVal({DW_OP_LLVM_convert, 32, DW_ATE_signed,DW_OP_LLVM_convert, 64, DW_ATE_signed,DW_OP_stack_value}));// Case 2: Perform an address calculation with the original expr, deref it,// then sign-extend the result.EXPECT_TRUE(hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_deref,DW_OP_LLVM_convert, 32, DW_ATE_signed,DW_OP_LLVM_convert, 64, DW_ATE_signed,DW_OP_stack_value}));// Case 3: Insert the sign-extension logic before the DW_OP_stack_value.EXPECT_TRUE(hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_LLVM_convert, 32,DW_ATE_signed, DW_OP_LLVM_convert, 64, DW_ATE_signed,DW_OP_stack_value}));// Cases 4-6: Just like cases 1-3, but preserve the fragment at the end.EXPECT_TRUE(hasADbgVal({DW_OP_LLVM_convert, 32, DW_ATE_signed,DW_OP_LLVM_convert, 64, DW_ATE_signed,DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 8}));EXPECT_TRUE(hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_deref,DW_OP_LLVM_convert, 32, DW_ATE_signed,DW_OP_LLVM_convert, 64, DW_ATE_signed,DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 8}));EXPECT_TRUE(hasADbgVal({DW_OP_lit0, DW_OP_mul, DW_OP_LLVM_convert, 32,DW_ATE_signed, DW_OP_LLVM_convert, 64, DW_ATE_signed,DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 8}));verifyModule(*M, &errs(), &BrokenDebugInfo);ASSERT_FALSE(BrokenDebugInfo);}TEST(Local, RemoveUnreachableBlocks) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(define void @br_simple() {entry:br label %bb0bb0:ret voidbb1:ret void}define void @br_self_loop() {entry:br label %bb0bb0:br i1 true, label %bb1, label %bb0bb1:br i1 true, label %bb0, label %bb2bb2:br label %bb2}define void @br_constant() {entry:br label %bb0bb0:br i1 true, label %bb1, label %bb2bb1:br i1 true, label %bb0, label %bb2bb2:br label %bb2}define void @br_loop() {entry:br label %bb0bb0:br label %bb0bb1:br label %bb2bb2:br label %bb1}declare i32 @__gxx_personality_v0(...)define void @invoke_terminator() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {entry:br i1 undef, label %invoke.block, label %exitinvoke.block:%cond = invoke zeroext i1 @invokable()to label %continue.block unwind label %lpad.blockcontinue.block:br i1 %cond, label %if.then, label %if.endif.then:unreachableif.end:unreachablelpad.block:%lp = landingpad { i8*, i32 }catch i8* nullbr label %exitexit:ret void}declare i1 @invokable())");auto runEager = [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);removeUnreachableBlocks(F, &DTU);EXPECT_TRUE(DTU.getDomTree().verify());EXPECT_TRUE(DTU.getPostDomTree().verify());};auto runLazy = [&](Function &F, DominatorTree *DT) {PostDominatorTree PDT = PostDominatorTree(F);DomTreeUpdater DTU(*DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);removeUnreachableBlocks(F, &DTU);EXPECT_TRUE(DTU.getDomTree().verify());EXPECT_TRUE(DTU.getPostDomTree().verify());};// Test removeUnreachableBlocks under Eager UpdateStrategy.runWithDomTree(*M, "br_simple", runEager);runWithDomTree(*M, "br_self_loop", runEager);runWithDomTree(*M, "br_constant", runEager);runWithDomTree(*M, "br_loop", runEager);runWithDomTree(*M, "invoke_terminator", runEager);// Test removeUnreachableBlocks under Lazy UpdateStrategy.runWithDomTree(*M, "br_simple", runLazy);runWithDomTree(*M, "br_self_loop", runLazy);runWithDomTree(*M, "br_constant", runLazy);runWithDomTree(*M, "br_loop", runLazy);runWithDomTree(*M, "invoke_terminator", runLazy);M = parseIR(C,R"(define void @f() {entry:ret voidbb0:ret void})");auto checkRUBlocksRetVal = [&](Function &F, DominatorTree *DT) {DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy);EXPECT_TRUE(removeUnreachableBlocks(F, &DTU));EXPECT_FALSE(removeUnreachableBlocks(F, &DTU));EXPECT_TRUE(DTU.getDomTree().verify());};runWithDomTree(*M, "f", checkRUBlocksRetVal);}TEST(Local, SimplifyCFGWithNullAC) {LLVMContext Ctx;std::unique_ptr<Module> M = parseIR(Ctx, R"(declare void @true_path()declare void @false_path()declare void @llvm.assume(i1 %cond);define i32 @foo(i1, i32) {entry:%cmp = icmp sgt i32 %1, 0br i1 %cmp, label %if.bb1, label %then.bb1if.bb1:call void @true_path()br label %test.bbthen.bb1:call void @false_path()br label %test.bbtest.bb:%phi = phi i1 [1, %if.bb1], [%0, %then.bb1]call void @llvm.assume(i1 %0)br i1 %phi, label %if.bb2, label %then.bb2if.bb2:ret i32 %1then.bb2:ret i32 0})");Function &F = *cast<Function>(M->getNamedValue("foo"));TargetTransformInfo TTI(M->getDataLayout());SimplifyCFGOptions Options{};Options.setAssumptionCache(nullptr);// Obtain BasicBlock of interest to this test, %test.bb.BasicBlock *TestBB = nullptr;for (BasicBlock &BB : F) {if (BB.getName().equals("test.bb")) {TestBB = &BB;break;}}ASSERT_TRUE(TestBB);DominatorTree DT(F);DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);// %test.bb is expected to be simplified by FoldCondBranchOnPHI.EXPECT_TRUE(simplifyCFG(TestBB, TTI,RequireAndPreserveDomTree ? &DTU : nullptr, Options));}TEST(Local, CanReplaceOperandWithVariable) {LLVMContext Ctx;Module M("test_module", Ctx);IRBuilder<> B(Ctx);FunctionType *FnType =FunctionType::get(Type::getVoidTy(Ctx), {}, false);FunctionType *VarArgFnType =FunctionType::get(Type::getVoidTy(Ctx), {B.getInt32Ty()}, true);Function *TestBody = Function::Create(FnType, GlobalValue::ExternalLinkage,0, "", &M);BasicBlock *BB0 = BasicBlock::Create(Ctx, "", TestBody);B.SetInsertPoint(BB0);FunctionCallee Intrin = M.getOrInsertFunction("llvm.foo", FnType);FunctionCallee Func = M.getOrInsertFunction("foo", FnType);FunctionCallee VarArgFunc= M.getOrInsertFunction("foo.vararg", VarArgFnType);FunctionCallee VarArgIntrin= M.getOrInsertFunction("llvm.foo.vararg", VarArgFnType);auto *CallToIntrin = B.CreateCall(Intrin);auto *CallToFunc = B.CreateCall(Func);// Test if it's valid to replace the callee operand.EXPECT_FALSE(canReplaceOperandWithVariable(CallToIntrin, 0));EXPECT_TRUE(canReplaceOperandWithVariable(CallToFunc, 0));// That it's invalid to replace an argument in the variadic argument list for// an intrinsic, but OK for a normal function.auto *CallToVarArgFunc = B.CreateCall(VarArgFunc, {B.getInt32(0), B.getInt32(1), B.getInt32(2)});EXPECT_TRUE(canReplaceOperandWithVariable(CallToVarArgFunc, 0));EXPECT_TRUE(canReplaceOperandWithVariable(CallToVarArgFunc, 1));EXPECT_TRUE(canReplaceOperandWithVariable(CallToVarArgFunc, 2));EXPECT_TRUE(canReplaceOperandWithVariable(CallToVarArgFunc, 3));auto *CallToVarArgIntrin = B.CreateCall(VarArgIntrin, {B.getInt32(0), B.getInt32(1), B.getInt32(2)});EXPECT_TRUE(canReplaceOperandWithVariable(CallToVarArgIntrin, 0));EXPECT_FALSE(canReplaceOperandWithVariable(CallToVarArgIntrin, 1));EXPECT_FALSE(canReplaceOperandWithVariable(CallToVarArgIntrin, 2));EXPECT_FALSE(canReplaceOperandWithVariable(CallToVarArgIntrin, 3));// Test that it's invalid to replace gcroot operands, even though it can't use// immarg.Type *PtrPtr = B.getInt8Ty()->getPointerTo(0);Value *Alloca = B.CreateAlloca(PtrPtr, (unsigned)0);CallInst *GCRoot = B.CreateIntrinsic(Intrinsic::gcroot, {},{Alloca, Constant::getNullValue(PtrPtr)});EXPECT_TRUE(canReplaceOperandWithVariable(GCRoot, 0)); // AllocaEXPECT_FALSE(canReplaceOperandWithVariable(GCRoot, 1));EXPECT_FALSE(canReplaceOperandWithVariable(GCRoot, 2));BB0->dropAllReferences();}
//===- IntegerDivision.cpp - Unit tests for the integer division code -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/IntegerDivision.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Function.h"#include "llvm/IR/GlobalValue.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(IntegerDivision, SDiv) {LLVMContext C;Module M("test division", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt32Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Div = Builder.CreateSDiv(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::SDiv);Value *Ret = Builder.CreateRet(Div);expandDivision(cast<BinaryOperator>(Div));EXPECT_TRUE(BB->front().getOpcode() == Instruction::AShr);Instruction* Quotient = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Quotient && Quotient->getOpcode() == Instruction::Sub);}TEST(IntegerDivision, UDiv) {LLVMContext C;Module M("test division", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt32Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Div = Builder.CreateUDiv(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::UDiv);Value *Ret = Builder.CreateRet(Div);expandDivision(cast<BinaryOperator>(Div));EXPECT_TRUE(BB->front().getOpcode() == Instruction::ICmp);Instruction* Quotient = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Quotient && Quotient->getOpcode() == Instruction::PHI);}TEST(IntegerDivision, SRem) {LLVMContext C;Module M("test remainder", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt32Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Rem = Builder.CreateSRem(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::SRem);Value *Ret = Builder.CreateRet(Rem);expandRemainder(cast<BinaryOperator>(Rem));EXPECT_TRUE(BB->front().getOpcode() == Instruction::AShr);Instruction* Remainder = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Remainder && Remainder->getOpcode() == Instruction::Sub);}TEST(IntegerDivision, URem) {LLVMContext C;Module M("test remainder", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt32Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Rem = Builder.CreateURem(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::URem);Value *Ret = Builder.CreateRet(Rem);expandRemainder(cast<BinaryOperator>(Rem));EXPECT_TRUE(BB->front().getOpcode() == Instruction::ICmp);Instruction* Remainder = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Remainder && Remainder->getOpcode() == Instruction::Sub);}TEST(IntegerDivision, SDiv64) {LLVMContext C;Module M("test division", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt64Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Div = Builder.CreateSDiv(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::SDiv);Value *Ret = Builder.CreateRet(Div);expandDivision(cast<BinaryOperator>(Div));EXPECT_TRUE(BB->front().getOpcode() == Instruction::AShr);Instruction* Quotient = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Quotient && Quotient->getOpcode() == Instruction::Sub);}TEST(IntegerDivision, UDiv64) {LLVMContext C;Module M("test division", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt64Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Div = Builder.CreateUDiv(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::UDiv);Value *Ret = Builder.CreateRet(Div);expandDivision(cast<BinaryOperator>(Div));EXPECT_TRUE(BB->front().getOpcode() == Instruction::ICmp);Instruction* Quotient = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Quotient && Quotient->getOpcode() == Instruction::PHI);}TEST(IntegerDivision, SRem64) {LLVMContext C;Module M("test remainder", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt64Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Rem = Builder.CreateSRem(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::SRem);Value *Ret = Builder.CreateRet(Rem);expandRemainder(cast<BinaryOperator>(Rem));EXPECT_TRUE(BB->front().getOpcode() == Instruction::AShr);Instruction* Remainder = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Remainder && Remainder->getOpcode() == Instruction::Sub);}TEST(IntegerDivision, URem64) {LLVMContext C;Module M("test remainder", C);IRBuilder<> Builder(C);SmallVector<Type*, 2> ArgTys(2, Builder.getInt64Ty());Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(),ArgTys, false),GlobalValue::ExternalLinkage, "F", &M);assert(F->arg_size() == 2);BasicBlock *BB = BasicBlock::Create(C, "", F);Builder.SetInsertPoint(BB);Function::arg_iterator AI = F->arg_begin();Value *A = &*AI++;Value *B = &*AI++;Value *Rem = Builder.CreateURem(A, B);EXPECT_TRUE(BB->front().getOpcode() == Instruction::URem);Value *Ret = Builder.CreateRet(Rem);expandRemainder(cast<BinaryOperator>(Rem));EXPECT_TRUE(BB->front().getOpcode() == Instruction::ICmp);Instruction* Remainder = dyn_cast<Instruction>(cast<User>(Ret)->getOperand(0));EXPECT_TRUE(Remainder && Remainder->getOpcode() == Instruction::Sub);}}
//===- FunctionComparator.cpp - Unit tests for FunctionComparator ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/FunctionComparator.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;/// Generates a simple test function.struct TestFunction {Function *F;BasicBlock *BB;Constant *C;Instruction *I;Type *T;TestFunction(LLVMContext &Ctx, Module &M, int addVal) {IRBuilder<> B(Ctx);T = B.getInt8Ty();F = Function::Create(FunctionType::get(T, {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BB = BasicBlock::Create(Ctx, "", F);B.SetInsertPoint(BB);Argument *PointerArg = &*F->arg_begin();LoadInst *LoadInst = B.CreateLoad(T, PointerArg);C = B.getInt8(addVal);I = cast<Instruction>(B.CreateAdd(LoadInst, C));B.CreateRet(I);}};/// A class for testing the FunctionComparator API.////// The main purpose is to test if the required protected functions are/// accessible from a derived class of FunctionComparator.class TestComparator : public FunctionComparator {public:TestComparator(const Function *F1, const Function *F2,GlobalNumberState *GN): FunctionComparator(F1, F2, GN) {}bool testFunctionAccess(const Function *F1, const Function *F2) {// Test if FnL and FnR are accessible.return F1 == FnL && F2 == FnR;}int testCompare() {return compare();}int testCompareSignature() {beginCompare();return compareSignature();}int testCmpBasicBlocks(BasicBlock *BBL, BasicBlock *BBR) {beginCompare();return cmpBasicBlocks(BBL, BBR);}int testCmpConstants(const Constant *L, const Constant *R) {beginCompare();return cmpConstants(L, R);}int testCmpGlobalValues(GlobalValue *L, GlobalValue *R) {beginCompare();return cmpGlobalValues(L, R);}int testCmpValues(const Value *L, const Value *R) {beginCompare();return cmpValues(L, R);}int testCmpOperations(const Instruction *L, const Instruction *R,bool &needToCmpOperands) {beginCompare();return cmpOperations(L, R, needToCmpOperands);}int testCmpTypes(Type *TyL, Type *TyR) {beginCompare();return cmpTypes(TyL, TyR);}int testCmpPrimitives() {beginCompare();returncmpNumbers(2, 3) +cmpAPInts(APInt(32, 2), APInt(32, 3)) +cmpAPFloats(APFloat(2.0), APFloat(3.0)) +cmpMem("2", "3");}};/// A sanity check for the FunctionComparator API.TEST(FunctionComparatorTest, TestAPI) {LLVMContext C;Module M("test", C);TestFunction F1(C, M, 27);TestFunction F2(C, M, 28);GlobalNumberState GN;TestComparator Cmp(F1.F, F2.F, &GN);EXPECT_TRUE(Cmp.testFunctionAccess(F1.F, F2.F));EXPECT_EQ(Cmp.testCompare(), -1);EXPECT_EQ(Cmp.testCompareSignature(), 0);EXPECT_EQ(Cmp.testCmpBasicBlocks(F1.BB, F2.BB), -1);EXPECT_EQ(Cmp.testCmpConstants(F1.C, F2.C), -1);EXPECT_EQ(Cmp.testCmpGlobalValues(F1.F, F2.F), -1);EXPECT_EQ(Cmp.testCmpValues(F1.I, F2.I), 0);bool needToCmpOperands = false;EXPECT_EQ(Cmp.testCmpOperations(F1.I, F2.I, needToCmpOperands), 0);EXPECT_TRUE(needToCmpOperands);EXPECT_EQ(Cmp.testCmpTypes(F1.T, F2.T), 0);EXPECT_EQ(Cmp.testCmpPrimitives(), -4);}
//===- DebugifyTest.cpp - Debugify unit tests -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallVector.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/Debugify.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("DebugifyTest", errs());return Mod;}namespace llvm {void initializeDebugInfoDropPass(PassRegistry &);void initializeDebugInfoDummyAnalysisPass(PassRegistry &);namespace {struct DebugInfoDrop : public FunctionPass {static char ID;bool runOnFunction(Function &F) override {// Drop DISubprogram.F.setSubprogram(nullptr);for (BasicBlock &BB : F) {// Remove debug locations.for (Instruction &I : BB)I.setDebugLoc(DebugLoc());}return false;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesCFG();}DebugInfoDrop() : FunctionPass(ID) {}};struct DebugValueDrop : public FunctionPass {static char ID;bool runOnFunction(Function &F) override {SmallVector<DbgVariableIntrinsic *, 4> Dbgs;for (BasicBlock &BB : F) {// Remove dbg var intrinsics.for (Instruction &I : BB) {if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))Dbgs.push_back(DVI);}}for (auto &I : Dbgs)I->eraseFromParent();return true;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesCFG();}DebugValueDrop() : FunctionPass(ID) {}};struct DebugInfoDummyAnalysis : public FunctionPass {static char ID;bool runOnFunction(Function &F) override {// Do nothing, so debug info stays untouched.return false;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();}DebugInfoDummyAnalysis() : FunctionPass(ID) {}};}char DebugInfoDrop::ID = 0;char DebugValueDrop::ID = 0;char DebugInfoDummyAnalysis::ID = 0;TEST(DebugInfoDrop, DropOriginalDebugInfo) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define i16 @f(i16 %a) !dbg !6 {%b = add i16 %a, 1, !dbg !11call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11ret i16 0, !dbg !11}declare void @llvm.dbg.value(metadata, metadata, metadata)!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t.ll", directory: "/")!2 = !{}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)!7 = !DISubroutineType(types: !2)!8 = !{!9}!9 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !10)!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)!11 = !DILocation(line: 1, column: 1, scope: !6))");DebugInfoDrop *P = new DebugInfoDrop();DebugInfoPerPass DIBeforePass;DebugifyCustomPassManager Passes;Passes.setDebugInfoBeforePass(DIBeforePass);Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",&(Passes.getDebugInfoPerPass())));Passes.add(P);Passes.add(createCheckDebugifyModulePass(false, "", nullptr,DebugifyMode::OriginalDebugInfo,&(Passes.getDebugInfoPerPass())));testing::internal::CaptureStderr();Passes.run(*M);std::string StdOut = testing::internal::GetCapturedStderr();std::string ErrorForSP = "ERROR: dropped DISubprogram of";std::string WarningForLoc = "WARNING: dropped DILocation of";std::string FinalResult = "CheckModuleDebugify (original debuginfo): FAIL";EXPECT_TRUE(StdOut.find(ErrorForSP) != std::string::npos);EXPECT_TRUE(StdOut.find(WarningForLoc) != std::string::npos);EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);}TEST(DebugValueDrop, DropOriginalDebugValues) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define i16 @f(i16 %a) !dbg !6 {%b = add i16 %a, 1, !dbg !11call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11ret i16 0, !dbg !11}declare void @llvm.dbg.value(metadata, metadata, metadata)!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t.ll", directory: "/")!2 = !{}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)!7 = !DISubroutineType(types: !2)!8 = !{!9}!9 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !10)!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)!11 = !DILocation(line: 1, column: 1, scope: !6))");DebugValueDrop *P = new DebugValueDrop();DebugInfoPerPass DIBeforePass;DebugifyCustomPassManager Passes;Passes.setDebugInfoBeforePass(DIBeforePass);Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",&(Passes.getDebugInfoPerPass())));Passes.add(P);Passes.add(createCheckDebugifyModulePass(false, "", nullptr,DebugifyMode::OriginalDebugInfo,&(Passes.getDebugInfoPerPass())));testing::internal::CaptureStderr();Passes.run(*M);std::string StdOut = testing::internal::GetCapturedStderr();std::string ErrorForSP = "ERROR: dropped DISubprogram of";std::string WarningForLoc = "WARNING: dropped DILocation of";std::string WarningForVars = "WARNING: drops dbg.value()/dbg.declare() for";std::string FinalResult = "CheckModuleDebugify (original debuginfo): FAIL";EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);EXPECT_TRUE(StdOut.find(WarningForVars) != std::string::npos);EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);}TEST(DebugInfoDummyAnalysis, PreserveOriginalDebugInfo) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define i32 @g(i32 %b) !dbg !6 {%c = add i32 %b, 1, !dbg !11call void @llvm.dbg.value(metadata i32 %c, metadata !9, metadata !DIExpression()), !dbg !11ret i32 1, !dbg !11}declare void @llvm.dbg.value(metadata, metadata, metadata)!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "test.ll", directory: "/")!2 = !{}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)!7 = !DISubroutineType(types: !2)!8 = !{!9}!9 = !DILocalVariable(name: "c", scope: !6, file: !1, line: 1, type: !10)!10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)!11 = !DILocation(line: 1, column: 1, scope: !6))");DebugInfoDummyAnalysis *P = new DebugInfoDummyAnalysis();DebugInfoPerPass DIBeforePass;DebugifyCustomPassManager Passes;Passes.setDebugInfoBeforePass(DIBeforePass);Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",&(Passes.getDebugInfoPerPass())));Passes.add(P);Passes.add(createCheckDebugifyModulePass(false, "", nullptr,DebugifyMode::OriginalDebugInfo,&(Passes.getDebugInfoPerPass())));testing::internal::CaptureStderr();Passes.run(*M);std::string StdOut = testing::internal::GetCapturedStderr();std::string ErrorForSP = "ERROR: dropped DISubprogram of";std::string WarningForLoc = "WARNING: dropped DILocation of";std::string WarningForVars = "WARNING: drops dbg.value()/dbg.declare() for";std::string FinalResult = "CheckModuleDebugify (original debuginfo): PASS";EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);EXPECT_TRUE(StdOut.find(WarningForVars) == std::string::npos);EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);}} // end namespace llvmINITIALIZE_PASS_BEGIN(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass",false, false)INITIALIZE_PASS_END(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass", false,false)INITIALIZE_PASS_BEGIN(DebugInfoDummyAnalysis, "debuginfodummyanalysispass","debuginfodummyanalysispass", false, false)INITIALIZE_PASS_END(DebugInfoDummyAnalysis, "debuginfodummyanalysispass","debuginfodummyanalysispass", false, false)
//===- CodeMoverUtils.cpp - Unit tests for CodeMoverUtils ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/CodeMoverUtils.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/DependenceAnalysis.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/PostDominators.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("CodeMoverUtilsTests", errs());return Mod;}static void run(Module &M, StringRef FuncName,function_ref<void(Function &F, DominatorTree &DT,PostDominatorTree &PDT, DependenceInfo &DI)>Test) {auto *F = M.getFunction(FuncName);DominatorTree DT(*F);PostDominatorTree PDT(*F);TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AssumptionCache AC(*F);AliasAnalysis AA(TLI);LoopInfo LI(DT);ScalarEvolution SE(*F, TLI, AC, DT, LI);DependenceInfo DI(F, &AA, &SE, &LI);Test(*F, DT, PDT, DI);}static BasicBlock *getBasicBlockByName(Function &F, StringRef Name) {for (BasicBlock &BB : F)if (BB.getName() == Name)return &BB;llvm_unreachable("Expected to find basic block!");}static Instruction *getInstructionByName(Function &F, StringRef Name) {for (BasicBlock &BB : F)for (Instruction &I : BB)if (I.getName() == Name)return &I;llvm_unreachable("Expected to find instruction!");}TEST(CodeMoverUtils, IsControlFlowEquivalentSimpleTest) {LLVMContext C;// void foo(int &i, bool cond1, bool cond2) {// if (cond1)// i = 1;// if (cond1)// i = 2;// if (cond2)// i = 3;// }std::unique_ptr<Module> M =parseIR(C, R"(define void @foo(i32* %i, i1 %cond1, i1 %cond2) {entry:br i1 %cond1, label %if.first, label %if.first.endif.first:store i32 1, i32* %i, align 4br label %if.first.endif.first.end:br i1 %cond1, label %if.second, label %if.second.endif.second:store i32 2, i32* %i, align 4br label %if.second.endif.second.end:br i1 %cond2, label %if.third, label %if.third.endif.third:store i32 3, i32* %i, align 4br label %if.third.endif.third.end:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.first");EXPECT_TRUE(isControlFlowEquivalent(*FirstIfBody, *FirstIfBody, DT, PDT));BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.second");EXPECT_TRUE(isControlFlowEquivalent(*FirstIfBody, *SecondIfBody, DT, PDT));BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.third");EXPECT_FALSE(isControlFlowEquivalent(*FirstIfBody, *ThirdIfBody, DT, PDT));EXPECT_FALSE(isControlFlowEquivalent(*SecondIfBody, *ThirdIfBody, DT, PDT));});}TEST(CodeMoverUtils, IsControlFlowEquivalentOppositeCondTest) {LLVMContext C;// void foo(int &i, unsigned X, unsigned Y) {// if (X < Y)// i = 1;// if (Y > X)// i = 2;// if (X >= Y)// i = 3;// else// i = 4;// if (X == Y)// i = 5;// if (Y == X)// i = 6;// else// i = 7;// if (X != Y)// i = 8;// else// i = 9;// }std::unique_ptr<Module> M =parseIR(C, R"(define void @foo(i32* %i, i32 %X, i32 %Y) {entry:%cmp1 = icmp ult i32 %X, %Ybr i1 %cmp1, label %if.first, label %if.first.endif.first:store i32 1, i32* %i, align 4br label %if.first.endif.first.end:%cmp2 = icmp ugt i32 %Y, %Xbr i1 %cmp2, label %if.second, label %if.second.endif.second:store i32 2, i32* %i, align 4br label %if.second.endif.second.end:%cmp3 = icmp uge i32 %X, %Ybr i1 %cmp3, label %if.third, label %if.third.elseif.third:store i32 3, i32* %i, align 4br label %if.third.endif.third.else:store i32 4, i32* %i, align 4br label %if.third.endif.third.end:%cmp4 = icmp eq i32 %X, %Ybr i1 %cmp4, label %if.fourth, label %if.fourth.endif.fourth:store i32 5, i32* %i, align 4br label %if.fourth.endif.fourth.end:%cmp5 = icmp eq i32 %Y, %Xbr i1 %cmp5, label %if.fifth, label %if.fifth.elseif.fifth:store i32 6, i32* %i, align 4br label %if.fifth.endif.fifth.else:store i32 7, i32* %i, align 4br label %if.fifth.endif.fifth.end:%cmp6 = icmp ne i32 %X, %Ybr i1 %cmp6, label %if.sixth, label %if.sixth.elseif.sixth:store i32 8, i32* %i, align 4br label %if.sixth.endif.sixth.else:store i32 9, i32* %i, align 4br label %if.sixth.endif.sixth.end:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.first");BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.second");BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.third");BasicBlock *ThirdElseBody = getBasicBlockByName(F, "if.third.else");EXPECT_TRUE(isControlFlowEquivalent(*FirstIfBody, *ThirdElseBody, DT, PDT));EXPECT_TRUE(isControlFlowEquivalent(*SecondIfBody, *ThirdElseBody, DT, PDT));EXPECT_FALSE(isControlFlowEquivalent(*ThirdIfBody, *ThirdElseBody, DT, PDT));BasicBlock *FourthIfBody = getBasicBlockByName(F, "if.fourth");BasicBlock *FifthIfBody = getBasicBlockByName(F, "if.fifth");BasicBlock *FifthElseBody = getBasicBlockByName(F, "if.fifth.else");EXPECT_FALSE(isControlFlowEquivalent(*FifthIfBody, *FifthElseBody, DT, PDT));BasicBlock *SixthIfBody = getBasicBlockByName(F, "if.sixth");EXPECT_TRUE(isControlFlowEquivalent(*FifthElseBody, *SixthIfBody, DT, PDT));BasicBlock *SixthElseBody = getBasicBlockByName(F, "if.sixth.else");EXPECT_TRUE(isControlFlowEquivalent(*FourthIfBody, *SixthElseBody, DT, PDT));EXPECT_TRUE(isControlFlowEquivalent(*FifthIfBody, *SixthElseBody, DT, PDT));});}TEST(CodeMoverUtils, IsControlFlowEquivalentCondNestTest) {LLVMContext C;// void foo(int &i, bool cond1, bool cond2) {// if (cond1)// if (cond2)// i = 1;// if (cond2)// if (cond1)// i = 2;// }std::unique_ptr<Module> M =parseIR(C, R"(define void @foo(i32* %i, i1 %cond1, i1 %cond2) {entry:br i1 %cond1, label %if.outer.first, label %if.first.endif.outer.first:br i1 %cond2, label %if.inner.first, label %if.first.endif.inner.first:store i32 1, i32* %i, align 4br label %if.first.endif.first.end:br i1 %cond2, label %if.outer.second, label %if.second.endif.outer.second:br i1 %cond1, label %if.inner.second, label %if.second.endif.inner.second:store i32 2, i32* %i, align 4br label %if.second.endif.second.end:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock *FirstOuterIfBody = getBasicBlockByName(F, "if.outer.first");BasicBlock *FirstInnerIfBody = getBasicBlockByName(F, "if.inner.first");BasicBlock *SecondOuterIfBody =getBasicBlockByName(F, "if.outer.second");BasicBlock *SecondInnerIfBody =getBasicBlockByName(F, "if.inner.second");EXPECT_TRUE(isControlFlowEquivalent(*FirstInnerIfBody,*SecondInnerIfBody, DT, PDT));EXPECT_FALSE(isControlFlowEquivalent(*FirstOuterIfBody,*SecondOuterIfBody, DT, PDT));EXPECT_FALSE(isControlFlowEquivalent(*FirstOuterIfBody,*SecondInnerIfBody, DT, PDT));EXPECT_FALSE(isControlFlowEquivalent(*FirstInnerIfBody,*SecondOuterIfBody, DT, PDT));});}TEST(CodeMoverUtils, IsControlFlowEquivalentImbalanceTest) {LLVMContext C;// void foo(int &i, bool cond1, bool cond2) {// if (cond1)// if (cond2)// if (cond3)// i = 1;// if (cond2)// if (cond3)// i = 2;// if (cond1)// if (cond1)// i = 3;// if (cond1)// i = 4;// }std::unique_ptr<Module> M = parseIR(C, R"(define void @foo(i32* %i, i1 %cond1, i1 %cond2, i1 %cond3) {entry:br i1 %cond1, label %if.outer.first, label %if.first.endif.outer.first:br i1 %cond2, label %if.middle.first, label %if.first.endif.middle.first:br i1 %cond3, label %if.inner.first, label %if.first.endif.inner.first:store i32 1, i32* %i, align 4br label %if.first.endif.first.end:br i1 %cond2, label %if.outer.second, label %if.second.endif.outer.second:br i1 %cond3, label %if.inner.second, label %if.second.endif.inner.second:store i32 2, i32* %i, align 4br label %if.second.endif.second.end:br i1 %cond1, label %if.outer.third, label %if.third.endif.outer.third:br i1 %cond1, label %if.inner.third, label %if.third.endif.inner.third:store i32 3, i32* %i, align 4br label %if.third.endif.third.end:br i1 %cond1, label %if.fourth, label %if.fourth.endif.fourth:store i32 4, i32* %i, align 4br label %if.fourth.endif.fourth.end:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.inner.first");BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.inner.second");EXPECT_FALSE(isControlFlowEquivalent(*FirstIfBody, *SecondIfBody, DT, PDT));BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.inner.third");BasicBlock *FourthIfBody = getBasicBlockByName(F, "if.fourth");EXPECT_TRUE(isControlFlowEquivalent(*ThirdIfBody, *FourthIfBody, DT, PDT));});}TEST(CodeMoverUtils, IsControlFlowEquivalentPointerTest) {LLVMContext C;// void foo(int &i, int *cond) {// if (*cond)// i = 1;// if (*cond)// i = 2;// *cond = 1;// if (*cond)// i = 3;// }std::unique_ptr<Module> M =parseIR(C, R"(define void @foo(i32* %i, i32* %cond) {entry:%0 = load i32, i32* %cond, align 4%tobool1 = icmp ne i32 %0, 0br i1 %tobool1, label %if.first, label %if.first.endif.first:store i32 1, i32* %i, align 4br label %if.first.endif.first.end:%1 = load i32, i32* %cond, align 4%tobool2 = icmp ne i32 %1, 0br i1 %tobool2, label %if.second, label %if.second.endif.second:store i32 2, i32* %i, align 4br label %if.second.endif.second.end:store i32 1, i32* %cond, align 4%2 = load i32, i32* %cond, align 4%tobool3 = icmp ne i32 %2, 0br i1 %tobool3, label %if.third, label %if.third.endif.third:store i32 3, i32* %i, align 4br label %if.third.endif.third.end:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock *FirstIfBody = getBasicBlockByName(F, "if.first");BasicBlock *SecondIfBody = getBasicBlockByName(F, "if.second");// Limitation: if we can prove cond haven't been modify between %0 and// %1, then we can prove FirstIfBody and SecondIfBody are control flow// equivalent.EXPECT_FALSE(isControlFlowEquivalent(*FirstIfBody, *SecondIfBody, DT, PDT));BasicBlock *ThirdIfBody = getBasicBlockByName(F, "if.third");EXPECT_FALSE(isControlFlowEquivalent(*FirstIfBody, *ThirdIfBody, DT, PDT));EXPECT_FALSE(isControlFlowEquivalent(*SecondIfBody, *ThirdIfBody, DT, PDT));});}TEST(CodeMoverUtils, IsControlFlowEquivalentNotPostdomTest) {LLVMContext C;// void foo(bool cond1, bool cond2) {// if (cond1) {// if (cond2)// return;// } else// if (cond2)// return;// return;// }std::unique_ptr<Module> M =parseIR(C, R"(define void @foo(i1 %cond1, i1 %cond2) {idom:br i1 %cond1, label %succ0, label %succ1succ0:br i1 %cond2, label %succ0ret, label %succ0succ1succ0ret:ret voidsucc0succ1:br label %bbsucc1:br i1 %cond2, label %succ1ret, label %succ1succ1succ1ret:ret voidsucc1succ1:br label %bbbb:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock &Idom = F.front();assert(Idom.getName() == "idom" && "Expecting BasicBlock idom");BasicBlock &BB = F.back();assert(BB.getName() == "bb" && "Expecting BasicBlock bb");EXPECT_FALSE(isControlFlowEquivalent(Idom, BB, DT, PDT));});}TEST(CodeMoverUtils, IsSafeToMoveTest1) {LLVMContext C;// void safecall() noexcept willreturn nosync;// void unsafecall();// void foo(int * __restrict__ A, int * __restrict__ B, int * __restrict__ C,// long N) {// X = N / 1;// safecall();// unsafecall1();// unsafecall2();// for (long i = 0; i < N; ++i) {// A[5] = 5;// A[i] = 0;// B[i] = A[i];// C[i] = A[i];// A[6] = 6;// }// }std::unique_ptr<Module> M = parseIR(C, R"(define void @foo(i32* noalias %A, i32* noalias %B, i32* noalias %C, i64 %N) {entry:%X = sdiv i64 1, %Ncall void @safecall()%cmp1 = icmp slt i64 0, %Ncall void @unsafecall1()call void @unsafecall2()br i1 %cmp1, label %for.body, label %for.endfor.body:%i = phi i64 [ 0, %entry ], [ %inc, %for.body ]%arrayidx_A5 = getelementptr inbounds i32, i32* %A, i64 5store i32 5, i32* %arrayidx_A5, align 4%arrayidx_A = getelementptr inbounds i32, i32* %A, i64 %istore i32 0, i32* %arrayidx_A, align 4%load1 = load i32, i32* %arrayidx_A, align 4%arrayidx_B = getelementptr inbounds i32, i32* %B, i64 %istore i32 %load1, i32* %arrayidx_B, align 4%load2 = load i32, i32* %arrayidx_A, align 4%arrayidx_C = getelementptr inbounds i32, i32* %C, i64 %istore i32 %load2, i32* %arrayidx_C, align 4%arrayidx_A6 = getelementptr inbounds i32, i32* %A, i64 6store i32 6, i32* %arrayidx_A6, align 4%inc = add nsw i64 %i, 1%cmp = icmp slt i64 %inc, %Nbr i1 %cmp, label %for.body, label %for.endfor.end:ret void}declare void @safecall() nounwind nosync willreturndeclare void @unsafecall1()declare void @unsafecall2())");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock *Entry = getBasicBlockByName(F, "entry");Instruction *CI_safecall = Entry->front().getNextNode();assert(isa<CallInst>(CI_safecall) &&"Expecting CI_safecall to be a CallInst");Instruction *CI_unsafecall = CI_safecall->getNextNode()->getNextNode();assert(isa<CallInst>(CI_unsafecall) &&"Expecting CI_unsafecall to be a CallInst");BasicBlock *ForBody = getBasicBlockByName(F, "for.body");Instruction &PN = ForBody->front();assert(isa<PHINode>(PN) && "Expecting PN to be a PHINode");Instruction *SI_A5 =getInstructionByName(F, "arrayidx_A5")->getNextNode();Instruction *SI = getInstructionByName(F, "arrayidx_A")->getNextNode();Instruction *LI1 = getInstructionByName(F, "load1");Instruction *LI2 = getInstructionByName(F, "load2");Instruction *SI_A6 =getInstructionByName(F, "arrayidx_A6")->getNextNode();// Can move after CI_safecall, as it does not throw, not synchronize, or// must return.EXPECT_TRUE(isSafeToMoveBefore(*CI_safecall->getPrevNode(),*CI_safecall->getNextNode(), DT, &PDT,&DI));// Cannot move CI_unsafecall, as it may throw.EXPECT_FALSE(isSafeToMoveBefore(*CI_unsafecall->getNextNode(),*CI_unsafecall, DT, &PDT, &DI));// Moving instruction to non control flow equivalent places are not// supported.EXPECT_FALSE(isSafeToMoveBefore(*SI_A5, *Entry->getTerminator(), DT, &PDT, &DI));// Moving PHINode is not supported.EXPECT_FALSE(isSafeToMoveBefore(PN, *PN.getNextNode()->getNextNode(),DT, &PDT, &DI));// Cannot move non-PHINode before PHINode.EXPECT_FALSE(isSafeToMoveBefore(*PN.getNextNode(), PN, DT, &PDT, &DI));// Moving Terminator is not supported.EXPECT_FALSE(isSafeToMoveBefore(*Entry->getTerminator(),*PN.getNextNode(), DT, &PDT, &DI));// Cannot move %arrayidx_A after SI, as SI is its user.EXPECT_FALSE(isSafeToMoveBefore(*SI->getPrevNode(), *SI->getNextNode(),DT, &PDT, &DI));// Cannot move SI before %arrayidx_A, as %arrayidx_A is its operand.EXPECT_FALSE(isSafeToMoveBefore(*SI, *SI->getPrevNode(), DT, &PDT, &DI));// Cannot move LI2 after SI_A6, as there is a flow dependence.EXPECT_FALSE(isSafeToMoveBefore(*LI2, *SI_A6->getNextNode(), DT, &PDT, &DI));// Cannot move SI after LI1, as there is a anti dependence.EXPECT_FALSE(isSafeToMoveBefore(*SI, *LI1->getNextNode(), DT, &PDT, &DI));// Cannot move SI_A5 after SI, as there is a output dependence.EXPECT_FALSE(isSafeToMoveBefore(*SI_A5, *LI1, DT, &PDT, &DI));// Can move LI2 before LI1, as there is only an input dependence.EXPECT_TRUE(isSafeToMoveBefore(*LI2, *LI1, DT, &PDT, &DI));});}TEST(CodeMoverUtils, IsSafeToMoveTest2) {LLVMContext C;std::unique_ptr<Module> M =parseIR(C, R"(define void @foo(i1 %cond, i32 %op0, i32 %op1) {entry:br i1 %cond, label %if.then.first, label %if.end.firstif.then.first:%add = add i32 %op0, %op1%user = add i32 %add, 1br label %if.end.firstif.end.first:br i1 %cond, label %if.then.second, label %if.end.secondif.then.second:%sub_op0 = add i32 %op0, 1%sub = sub i32 %sub_op0, %op1br label %if.end.secondif.end.second:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {Instruction *AddInst = getInstructionByName(F, "add");Instruction *SubInst = getInstructionByName(F, "sub");// Cannot move as %user uses %add and %sub doesn't dominates %user.EXPECT_FALSE(isSafeToMoveBefore(*AddInst, *SubInst, DT, &PDT, &DI));// Cannot move as %sub_op0 is an operand of %sub and %add doesn't// dominates %sub_op0.EXPECT_FALSE(isSafeToMoveBefore(*SubInst, *AddInst, DT, &PDT, &DI));});}TEST(CodeMoverUtils, IsSafeToMoveTest3) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define void @foo(i64 %N) {entry:br label %for.bodyfor.body:%i = phi i64 [ 0, %entry ], [ %inc, %for.latch ]%inc = add nsw i64 %i, 1br label %for.latchfor.latch:%cmp = icmp slt i64 %inc, %N%add = add i64 100, %N%add2 = add i64 %add, %Nbr i1 %cmp, label %for.body, label %for.endfor.end:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {Instruction *IncInst = getInstructionByName(F, "inc");Instruction *CmpInst = getInstructionByName(F, "cmp");BasicBlock *BB0 = getBasicBlockByName(F, "for.body");BasicBlock *BB1 = getBasicBlockByName(F, "for.latch");// Can move as the incoming block of %inc for %i (%for.latch) dominated// by %cmp.EXPECT_TRUE(isSafeToMoveBefore(*IncInst, *CmpInst, DT, &PDT, &DI));// Can move as the operands of instructions in BB1 either dominate// InsertPoint or appear before that instruction, e.g., %add appears// before %add2 although %add does not dominate InsertPoint.EXPECT_TRUE(isSafeToMoveBefore(*BB1, *BB0->getTerminator(), DT, &PDT, &DI));});}TEST(CodeMoverUtils, IsSafeToMoveTest4) {LLVMContext C;std::unique_ptr<Module> M =parseIR(C, R"(define void @foo(i1 %cond, i32 %op0, i32 %op1) {entry:br i1 %cond, label %if.end.first, label %if.then.firstif.then.first:%add = add i32 %op0, %op1%user = add i32 %add, 1%add2 = add i32 %op0, 1br label %if.end.firstif.end.first:br i1 %cond, label %if.end.second, label %if.then.secondif.then.second:%sub_op0 = add i32 %op0, 1%sub = sub i32 %sub_op0, %op1%sub2 = sub i32 %op0, 1br label %if.end.secondif.end.second:ret void})");run(*M, "foo",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {Instruction *AddInst = getInstructionByName(F, "add");Instruction *AddInst2 = getInstructionByName(F, "add2");Instruction *SubInst = getInstructionByName(F, "sub");Instruction *SubInst2 = getInstructionByName(F, "sub2");// Cannot move as %user uses %add and %sub doesn't dominates %user.EXPECT_FALSE(isSafeToMoveBefore(*AddInst, *SubInst, DT, &PDT, &DI));// Cannot move as %sub_op0 is an operand of %sub and %add doesn't// dominates %sub_op0.EXPECT_FALSE(isSafeToMoveBefore(*SubInst, *AddInst, DT, &PDT, &DI));// Can move as %add2 and %sub2 are control flow equivalent,// although %add2 does not strictly dominate %sub2.EXPECT_TRUE(isSafeToMoveBefore(*AddInst2, *SubInst2, DT, &PDT, &DI));// Can move as %add2 and %sub2 are control flow equivalent,// although %add2 does not strictly dominate %sub2.EXPECT_TRUE(isSafeToMoveBefore(*SubInst2, *AddInst2, DT, &PDT, &DI));});}TEST(CodeMoverUtils, IsSafeToMoveTest5) {LLVMContext C;std::unique_ptr<Module> M =parseIR(C, R"(define void @dependence(i32* noalias %A, i32* noalias %B){entry:store i32 0, i32* %A, align 4 ; storeA0store i32 2, i32* %A, align 4 ; storeA1%tmp0 = load i32, i32* %A, align 4 ; loadA0store i32 1, i32* %B, align 4 ; storeB0%tmp1 = load i32, i32* %A, align 4 ; loadA1store i32 2, i32* %A, align 4 ; storeA2store i32 4, i32* %B, align 4 ; StoreB1%tmp2 = load i32, i32* %A, align 4 ; loadA2%tmp3 = load i32, i32* %A, align 4 ; loadA3%tmp4 = load i32, i32* %B, align 4 ; loadB2%tmp5 = load i32, i32* %B, align 4 ; loadB3ret void})");run(*M, "dependence",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {Instruction *LoadA0 = getInstructionByName(F, "tmp0");Instruction *LoadA1 = getInstructionByName(F, "tmp1");Instruction *LoadA2 = getInstructionByName(F, "tmp2");Instruction *LoadA3 = getInstructionByName(F, "tmp3");Instruction *LoadB2 = getInstructionByName(F, "tmp4");Instruction *LoadB3 = getInstructionByName(F, "tmp5");Instruction *StoreA1 = LoadA0->getPrevNode();Instruction *StoreA0 = StoreA1->getPrevNode();Instruction *StoreB0 = LoadA0->getNextNode();Instruction *StoreB1 = LoadA2->getPrevNode();Instruction *StoreA2 = StoreB1->getPrevNode();// Input forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA2, *LoadB2, DT, &PDT, &DI));// Input backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadA2, DT, &PDT, &DI));// Output forward dependencyEXPECT_FALSE(isSafeToMoveBefore(*StoreA0, *LoadA0, DT, &PDT, &DI));// Output backward dependencyEXPECT_FALSE(isSafeToMoveBefore(*StoreA1, *StoreA0, DT, &PDT, &DI));// Flow forward dependencyEXPECT_FALSE(isSafeToMoveBefore(*StoreA1, *StoreB0, DT, &PDT, &DI));// Flow backward dependencyEXPECT_FALSE(isSafeToMoveBefore(*LoadA0, *StoreA1, DT, &PDT, &DI));// Anti forward dependencyEXPECT_FALSE(isSafeToMoveBefore(*LoadA1, *StoreB1, DT, &PDT, &DI));// Anti backward dependencyEXPECT_FALSE(isSafeToMoveBefore(*StoreA2, *LoadA1, DT, &PDT, &DI));// No input backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadB2, *LoadA3, DT, &PDT, &DI));// No input forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadB3, DT, &PDT, &DI));// No Output forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*StoreA2, *LoadA2, DT, &PDT, &DI));// No Output backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*StoreB1, *StoreA2, DT, &PDT, &DI));// No flow forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*StoreB0, *StoreA2, DT, &PDT, &DI));// No flow backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA1, *StoreB0, DT, &PDT, &DI));// No anti backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*StoreB0, *LoadA0, DT, &PDT, &DI));// No anti forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA0, *LoadA1, DT, &PDT, &DI));});}TEST(CodeMoverUtils, IsSafeToMoveTest6) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define void @dependence(i1 %cond, i32* noalias %A, i32* noalias %B){entry:br i1 %cond, label %bb0, label %bb1bb0:br label %bb1bb1:store i32 0, i32* %A, align 4 ; storeA0br i1 %cond, label %bb2, label %bb3bb2:br label %bb3bb3:store i32 2, i32* %A, align 4 ; storeA1br i1 %cond, label %bb4, label %bb5bb4:br label %bb5bb5:%tmp0 = load i32, i32* %A, align 4 ; loadA0br i1 %cond, label %bb6, label %bb7bb6:br label %bb7bb7:store i32 1, i32* %B, align 4 ; storeB0br i1 %cond, label %bb8, label %bb9bb8:br label %bb9bb9:%tmp1 = load i32, i32* %A, align 4 ; loadA1br i1 %cond, label %bb10, label %bb11bb10:br label %bb11bb11:store i32 2, i32* %A, align 4 ; storeA2br i1 %cond, label %bb12, label %bb13bb12:br label %bb13bb13:store i32 4, i32* %B, align 4 ; StoreB1br i1 %cond, label %bb14, label %bb15bb14:br label %bb15bb15:%tmp2 = load i32, i32* %A, align 4 ; loadA2br i1 %cond, label %bb16, label %bb17bb16:br label %bb17bb17:%tmp3 = load i32, i32* %A, align 4 ; loadA3br i1 %cond, label %bb18, label %bb19bb18:br label %bb19bb19:%tmp4 = load i32, i32* %B, align 4 ; loadB2br i1 %cond, label %bb20, label %bb21bb20:br label %bb21bb21:%tmp5 = load i32, i32* %B, align 4 ; loadB3ret void})");run(*M, "dependence",[&](Function &F, DominatorTree &DT, PostDominatorTree &PDT,DependenceInfo &DI) {BasicBlock *BB1 = getBasicBlockByName(F, "bb1");BasicBlock *BB3 = getBasicBlockByName(F, "bb3");BasicBlock *BB7 = getBasicBlockByName(F, "bb7");BasicBlock *BB11 = getBasicBlockByName(F, "bb11");BasicBlock *BB13 = getBasicBlockByName(F, "bb13");Instruction *LoadA0 = getInstructionByName(F, "tmp0");Instruction *LoadA1 = getInstructionByName(F, "tmp1");Instruction *LoadA2 = getInstructionByName(F, "tmp2");Instruction *LoadA3 = getInstructionByName(F, "tmp3");Instruction *LoadB2 = getInstructionByName(F, "tmp4");Instruction *LoadB3 = getInstructionByName(F, "tmp5");Instruction &StoreA1 = BB3->front();Instruction &StoreA0 = BB1->front();Instruction &StoreB0 = BB7->front();Instruction &StoreB1 = BB13->front();Instruction &StoreA2 = BB11->front();// Input forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA2, *LoadB2, DT, &PDT, &DI));// Input backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadA2, DT, &PDT, &DI));// Output forward dependencyEXPECT_FALSE(isSafeToMoveBefore(StoreA0, *LoadA0, DT, &PDT, &DI));// Output backward dependencyEXPECT_FALSE(isSafeToMoveBefore(StoreA1, StoreA0, DT, &PDT, &DI));// Flow forward dependencyEXPECT_FALSE(isSafeToMoveBefore(StoreA1, StoreB0, DT, &PDT, &DI));// Flow backward dependencyEXPECT_FALSE(isSafeToMoveBefore(*LoadA0, StoreA1, DT, &PDT, &DI));// Anti forward dependencyEXPECT_FALSE(isSafeToMoveBefore(*LoadA1, StoreB1, DT, &PDT, &DI));// Anti backward dependencyEXPECT_FALSE(isSafeToMoveBefore(StoreA2, *LoadA1, DT, &PDT, &DI));// No input backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadB2, *LoadA3, DT, &PDT, &DI));// No input forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA3, *LoadB3, DT, &PDT, &DI));// No Output forward dependencyEXPECT_TRUE(isSafeToMoveBefore(StoreA2, *LoadA2, DT, &PDT, &DI));// No Output backward dependencyEXPECT_TRUE(isSafeToMoveBefore(StoreB1, StoreA2, DT, &PDT, &DI));// No flow forward dependencyEXPECT_TRUE(isSafeToMoveBefore(StoreB0, StoreA2, DT, &PDT, &DI));// No flow backward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA1, StoreB0, DT, &PDT, &DI));// No anti backward dependencyEXPECT_TRUE(isSafeToMoveBefore(StoreB0, *LoadA0, DT, &PDT, &DI));// No anti forward dependencyEXPECT_TRUE(isSafeToMoveBefore(*LoadA0, *LoadA1, DT, &PDT, &DI));});}
//===- CodeExtractor.cpp - Unit tests for CodeExtractor -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/CodeExtractor.h"#include "llvm/AsmParser/Parser.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/IRReader/IRReader.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {BasicBlock *getBlockByName(Function *F, StringRef name) {for (auto &BB : *F)if (BB.getName() == name)return &BB;return nullptr;}TEST(CodeExtractor, ExitStub) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"invalid(define i32 @foo(i32 %x, i32 %y, i32 %z) {header:%0 = icmp ugt i32 %x, %ybr i1 %0, label %body1, label %body2body1:%1 = add i32 %z, 2br label %notExtractedbody2:%2 = mul i32 %z, 7br label %notExtractednotExtracted:%3 = phi i32 [ %1, %body1 ], [ %2, %body2 ]%4 = add i32 %3, %xret i32 %4})invalid",Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 3> Candidates{ getBlockByName(Func, "header"),getBlockByName(Func, "body1"),getBlockByName(Func, "body2") };CodeExtractor CE(Candidates);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);BasicBlock *Exit = getBlockByName(Func, "notExtracted");BasicBlock *ExitSplit = getBlockByName(Outlined, "notExtracted.split");// Ensure that PHI in exit block has only one incoming value (from code// replacer block).EXPECT_TRUE(Exit && cast<PHINode>(Exit->front()).getNumIncomingValues() == 1);// Ensure that there is a PHI in outlined function with 2 incoming values.EXPECT_TRUE(ExitSplit &&cast<PHINode>(ExitSplit->front()).getNumIncomingValues() == 2);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}TEST(CodeExtractor, InputOutputMonitoring) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"invalid(define i32 @foo(i32 %x, i32 %y, i32 %z) {header:%0 = icmp ugt i32 %x, %ybr i1 %0, label %body1, label %body2body1:%1 = add i32 %z, 2br label %notExtractedbody2:%2 = mul i32 %z, 7br label %notExtractednotExtracted:%3 = phi i32 [ %1, %body1 ], [ %2, %body2 ]%4 = add i32 %3, %xret i32 %4})invalid",Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 3> Candidates{getBlockByName(Func, "header"),getBlockByName(Func, "body1"),getBlockByName(Func, "body2")};CodeExtractor CE(Candidates);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);SetVector<Value *> Inputs, Outputs;Function *Outlined = CE.extractCodeRegion(CEAC, Inputs, Outputs);EXPECT_TRUE(Outlined);EXPECT_EQ(Inputs.size(), 3u);EXPECT_EQ(Inputs[0], Func->getArg(2));EXPECT_EQ(Inputs[1], Func->getArg(0));EXPECT_EQ(Inputs[2], Func->getArg(1));EXPECT_EQ(Outputs.size(), 1u);StoreInst *SI = cast<StoreInst>(Outlined->getArg(3)->user_back());Value *OutputVal = SI->getValueOperand();EXPECT_EQ(Outputs[0], OutputVal);BasicBlock *Exit = getBlockByName(Func, "notExtracted");BasicBlock *ExitSplit = getBlockByName(Outlined, "notExtracted.split");// Ensure that PHI in exit block has only one incoming value (from code// replacer block).EXPECT_TRUE(Exit && cast<PHINode>(Exit->front()).getNumIncomingValues() == 1);// Ensure that there is a PHI in outlined function with 2 incoming values.EXPECT_TRUE(ExitSplit &&cast<PHINode>(ExitSplit->front()).getNumIncomingValues() == 2);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}TEST(CodeExtractor, ExitBlockOrderingPhis) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"invalid(define void @foo(i32 %a, i32 %b) {entry:%0 = alloca i32, align 4br label %test0test0:%c = load i32, i32* %0, align 4br label %test1test1:%e = load i32, i32* %0, align 4br i1 true, label %first, label %testtest:%d = load i32, i32* %0, align 4br i1 true, label %first, label %nextfirst:%1 = phi i32 [ %c, %test ], [ %e, %test1 ]ret voidnext:%2 = add i32 %d, 1%3 = add i32 %e, 1ret void})invalid",Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 3> Candidates{ getBlockByName(Func, "test0"),getBlockByName(Func, "test1"),getBlockByName(Func, "test") };CodeExtractor CE(Candidates);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);BasicBlock *FirstExitStub = getBlockByName(Outlined, "first.exitStub");BasicBlock *NextExitStub = getBlockByName(Outlined, "next.exitStub");Instruction *FirstTerm = FirstExitStub->getTerminator();ReturnInst *FirstReturn = dyn_cast<ReturnInst>(FirstTerm);EXPECT_TRUE(FirstReturn);ConstantInt *CIFirst = dyn_cast<ConstantInt>(FirstReturn->getReturnValue());EXPECT_TRUE(CIFirst->getLimitedValue() == 1u);Instruction *NextTerm = NextExitStub->getTerminator();ReturnInst *NextReturn = dyn_cast<ReturnInst>(NextTerm);EXPECT_TRUE(NextReturn);ConstantInt *CINext = dyn_cast<ConstantInt>(NextReturn->getReturnValue());EXPECT_TRUE(CINext->getLimitedValue() == 0u);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}TEST(CodeExtractor, ExitBlockOrdering) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"invalid(define void @foo(i32 %a, i32 %b) {entry:%0 = alloca i32, align 4br label %test0test0:%c = load i32, i32* %0, align 4br label %test1test1:%e = load i32, i32* %0, align 4br i1 true, label %first, label %testtest:%d = load i32, i32* %0, align 4br i1 true, label %first, label %nextfirst:ret voidnext:%1 = add i32 %d, 1%2 = add i32 %e, 1ret void})invalid",Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 3> Candidates{ getBlockByName(Func, "test0"),getBlockByName(Func, "test1"),getBlockByName(Func, "test") };CodeExtractor CE(Candidates);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);BasicBlock *FirstExitStub = getBlockByName(Outlined, "first.exitStub");BasicBlock *NextExitStub = getBlockByName(Outlined, "next.exitStub");Instruction *FirstTerm = FirstExitStub->getTerminator();ReturnInst *FirstReturn = dyn_cast<ReturnInst>(FirstTerm);EXPECT_TRUE(FirstReturn);ConstantInt *CIFirst = dyn_cast<ConstantInt>(FirstReturn->getReturnValue());EXPECT_TRUE(CIFirst->getLimitedValue() == 1u);Instruction *NextTerm = NextExitStub->getTerminator();ReturnInst *NextReturn = dyn_cast<ReturnInst>(NextTerm);EXPECT_TRUE(NextReturn);ConstantInt *CINext = dyn_cast<ConstantInt>(NextReturn->getReturnValue());EXPECT_TRUE(CINext->getLimitedValue() == 0u);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}TEST(CodeExtractor, ExitPHIOnePredFromRegion) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"invalid(define i32 @foo() {header:br i1 undef, label %extracted1, label %predpred:br i1 undef, label %exit1, label %exit2extracted1:br i1 undef, label %extracted2, label %exit1extracted2:br label %exit2exit1:%0 = phi i32 [ 1, %extracted1 ], [ 2, %pred ]ret i32 %0exit2:%1 = phi i32 [ 3, %extracted2 ], [ 4, %pred ]ret i32 %1})invalid", Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 2> ExtractedBlocks{getBlockByName(Func, "extracted1"),getBlockByName(Func, "extracted2")};CodeExtractor CE(ExtractedBlocks);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);BasicBlock *Exit1 = getBlockByName(Func, "exit1");BasicBlock *Exit2 = getBlockByName(Func, "exit2");// Ensure that PHIs in exits are not splitted (since that they have only one// incoming value from extracted region).EXPECT_TRUE(Exit1 &&cast<PHINode>(Exit1->front()).getNumIncomingValues() == 2);EXPECT_TRUE(Exit2 &&cast<PHINode>(Exit2->front()).getNumIncomingValues() == 2);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}TEST(CodeExtractor, StoreOutputInvokeResultAfterEHPad) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"invalid(declare i8 @hoge()define i32 @foo() personality i8* null {entry:%call = invoke i8 @hoge()to label %invoke.cont unwind label %lpadinvoke.cont: ; preds = %entryunreachablelpad: ; preds = %entry%0 = landingpad { i8*, i32 }catch i8* nullbr i1 undef, label %catch, label %finally.catchallcatch: ; preds = %lpad%call2 = invoke i8 @hoge()to label %invoke.cont2 unwind label %lpad2invoke.cont2: ; preds = %catch%call3 = invoke i8 @hoge()to label %invoke.cont3 unwind label %lpad2invoke.cont3: ; preds = %invoke.cont2unreachablelpad2: ; preds = %invoke.cont2, %catch%ex.1 = phi i8* [ undef, %invoke.cont2 ], [ null, %catch ]%1 = landingpad { i8*, i32 }catch i8* nullbr label %finally.catchallfinally.catchall: ; preds = %lpad33, %lpad%ex.2 = phi i8* [ %ex.1, %lpad2 ], [ null, %lpad ]unreachable})invalid", Err, Ctx));if (!M) {Err.print("unit", errs());exit(1);}Function *Func = M->getFunction("foo");EXPECT_FALSE(verifyFunction(*Func, &errs()));SmallVector<BasicBlock *, 2> ExtractedBlocks{getBlockByName(Func, "catch"),getBlockByName(Func, "invoke.cont2"),getBlockByName(Func, "invoke.cont3"),getBlockByName(Func, "lpad2")};CodeExtractor CE(ExtractedBlocks);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);EXPECT_FALSE(verifyFunction(*Outlined, &errs()));EXPECT_FALSE(verifyFunction(*Func, &errs()));}TEST(CodeExtractor, StoreOutputInvokeResultInExitStub) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"invalid(declare i32 @bar()define i32 @foo() personality i8* null {entry:%0 = invoke i32 @bar() to label %exit unwind label %lpadexit:ret i32 %0lpad:%1 = landingpad { i8*, i32 }cleanupresume { i8*, i32 } %1})invalid",Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 1> Blocks{ getBlockByName(Func, "entry"),getBlockByName(Func, "lpad") };CodeExtractor CE(Blocks);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}TEST(CodeExtractor, ExtractAndInvalidateAssumptionCache) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"ir(target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"target triple = "aarch64"%b = type { i64 }declare void @g(i8*)declare void @llvm.assume(i1) #0define void @test() {entry:br label %labellabel:%0 = load %b*, %b** inttoptr (i64 8 to %b**), align 8%1 = getelementptr inbounds %b, %b* %0, i64 undef, i32 0%2 = load i64, i64* %1, align 8%3 = icmp ugt i64 %2, 1br i1 %3, label %if.then, label %if.elseif.then:unreachableif.else:call void @g(i8* undef)store i64 undef, i64* null, align 536870912%4 = icmp eq i64 %2, 0call void @llvm.assume(i1 %4)unreachable}attributes #0 = { nounwind willreturn })ir",Err, Ctx));assert(M && "Could not parse module?");Function *Func = M->getFunction("test");SmallVector<BasicBlock *, 1> Blocks{ getBlockByName(Func, "if.else") };AssumptionCache AC(*Func);CodeExtractor CE(Blocks, nullptr, false, nullptr, nullptr, &AC);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));EXPECT_FALSE(CE.verifyAssumptionCache(*Func, *Outlined, &AC));}TEST(CodeExtractor, RemoveBitcastUsesFromOuterLifetimeMarkers) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"ir(target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-unknown-linux-gnu"declare void @use(i32*)declare void @llvm.lifetime.start.p0i8(i64, i8*)declare void @llvm.lifetime.end.p0i8(i64, i8*)define void @foo() {entry:%0 = alloca i32br label %extractextract:%1 = bitcast i32* %0 to i8*call void @llvm.lifetime.start.p0i8(i64 4, i8* %1)call void @use(i32* %0)br label %exitexit:call void @use(i32* %0)call void @llvm.lifetime.end.p0i8(i64 4, i8* %1)ret void})ir",Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 1> Blocks{getBlockByName(Func, "extract")};CodeExtractor CE(Blocks);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;BasicBlock *CommonExit = nullptr;CE.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);CE.findInputsOutputs(Inputs, Outputs, SinkingCands);EXPECT_EQ(Outputs.size(), 0U);Function *Outlined = CE.extractCodeRegion(CEAC);EXPECT_TRUE(Outlined);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}TEST(CodeExtractor, PartialAggregateArgs) {LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M(parseAssemblyString(R"ir(target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-unknown-linux-gnu"declare void @use(i32)define void @foo(i32 %a, i32 %b, i32 %c) {entry:br label %extractextract:call void @use(i32 %a)call void @use(i32 %b)call void @use(i32 %c)br label %exitexit:ret void})ir",Err, Ctx));Function *Func = M->getFunction("foo");SmallVector<BasicBlock *, 1> Blocks{getBlockByName(Func, "extract")};// Create the CodeExtractor with arguments aggregation enabled.CodeExtractor CE(Blocks, /* DominatorTree */ nullptr,/* AggregateArgs */ true);EXPECT_TRUE(CE.isEligible());CodeExtractorAnalysisCache CEAC(*Func);SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;BasicBlock *CommonExit = nullptr;CE.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);CE.findInputsOutputs(Inputs, Outputs, SinkingCands);// Exclude the first input from the argument aggregate.CE.excludeArgFromAggregate(Inputs[0]);Function *Outlined = CE.extractCodeRegion(CEAC, Inputs, Outputs);EXPECT_TRUE(Outlined);// Expect 2 arguments in the outlined function: the excluded input and the// struct aggregate for the remaining inputs.EXPECT_EQ(Outlined->arg_size(), 2U);EXPECT_FALSE(verifyFunction(*Outlined));EXPECT_FALSE(verifyFunction(*Func));}} // end anonymous namespace
//===- Cloning.cpp - Unit tests for the Cloner ----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/Cloning.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SmallPtrSet.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/DomTreeUpdater.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Argument.h"#include "llvm/IR/Constant.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DebugInfo.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {class CloneInstruction : public ::testing::Test {protected:void SetUp() override { V = nullptr; }template <typename T>T *clone(T *V1) {Value *V2 = V1->clone();Orig.insert(V1);Clones.insert(V2);return cast<T>(V2);}void eraseClones() {for (Value *V : Clones)V->deleteValue();Clones.clear();}void TearDown() override {eraseClones();for (Value *V : Orig)V->deleteValue();Orig.clear();if (V)V->deleteValue();}SmallPtrSet<Value *, 4> Orig; // Erase on exitSmallPtrSet<Value *, 4> Clones; // Erase in eraseClonesLLVMContext context;Value *V;};TEST_F(CloneInstruction, OverflowBits) {V = new Argument(Type::getInt32Ty(context));BinaryOperator *Add = BinaryOperator::Create(Instruction::Add, V, V);BinaryOperator *Sub = BinaryOperator::Create(Instruction::Sub, V, V);BinaryOperator *Mul = BinaryOperator::Create(Instruction::Mul, V, V);BinaryOperator *AddClone = this->clone(Add);BinaryOperator *SubClone = this->clone(Sub);BinaryOperator *MulClone = this->clone(Mul);EXPECT_FALSE(AddClone->hasNoUnsignedWrap());EXPECT_FALSE(AddClone->hasNoSignedWrap());EXPECT_FALSE(SubClone->hasNoUnsignedWrap());EXPECT_FALSE(SubClone->hasNoSignedWrap());EXPECT_FALSE(MulClone->hasNoUnsignedWrap());EXPECT_FALSE(MulClone->hasNoSignedWrap());eraseClones();Add->setHasNoUnsignedWrap();Sub->setHasNoUnsignedWrap();Mul->setHasNoUnsignedWrap();AddClone = this->clone(Add);SubClone = this->clone(Sub);MulClone = this->clone(Mul);EXPECT_TRUE(AddClone->hasNoUnsignedWrap());EXPECT_FALSE(AddClone->hasNoSignedWrap());EXPECT_TRUE(SubClone->hasNoUnsignedWrap());EXPECT_FALSE(SubClone->hasNoSignedWrap());EXPECT_TRUE(MulClone->hasNoUnsignedWrap());EXPECT_FALSE(MulClone->hasNoSignedWrap());eraseClones();Add->setHasNoSignedWrap();Sub->setHasNoSignedWrap();Mul->setHasNoSignedWrap();AddClone = this->clone(Add);SubClone = this->clone(Sub);MulClone = this->clone(Mul);EXPECT_TRUE(AddClone->hasNoUnsignedWrap());EXPECT_TRUE(AddClone->hasNoSignedWrap());EXPECT_TRUE(SubClone->hasNoUnsignedWrap());EXPECT_TRUE(SubClone->hasNoSignedWrap());EXPECT_TRUE(MulClone->hasNoUnsignedWrap());EXPECT_TRUE(MulClone->hasNoSignedWrap());eraseClones();Add->setHasNoUnsignedWrap(false);Sub->setHasNoUnsignedWrap(false);Mul->setHasNoUnsignedWrap(false);AddClone = this->clone(Add);SubClone = this->clone(Sub);MulClone = this->clone(Mul);EXPECT_FALSE(AddClone->hasNoUnsignedWrap());EXPECT_TRUE(AddClone->hasNoSignedWrap());EXPECT_FALSE(SubClone->hasNoUnsignedWrap());EXPECT_TRUE(SubClone->hasNoSignedWrap());EXPECT_FALSE(MulClone->hasNoUnsignedWrap());EXPECT_TRUE(MulClone->hasNoSignedWrap());}TEST_F(CloneInstruction, Inbounds) {V = new Argument(Type::getInt32PtrTy(context));Constant *Z = Constant::getNullValue(Type::getInt32Ty(context));std::vector<Value *> ops;ops.push_back(Z);GetElementPtrInst *GEP =GetElementPtrInst::Create(Type::getInt32Ty(context), V, ops);EXPECT_FALSE(this->clone(GEP)->isInBounds());GEP->setIsInBounds();EXPECT_TRUE(this->clone(GEP)->isInBounds());}TEST_F(CloneInstruction, Exact) {V = new Argument(Type::getInt32Ty(context));BinaryOperator *SDiv = BinaryOperator::Create(Instruction::SDiv, V, V);EXPECT_FALSE(this->clone(SDiv)->isExact());SDiv->setIsExact(true);EXPECT_TRUE(this->clone(SDiv)->isExact());}TEST_F(CloneInstruction, Attributes) {Type *ArgTy1[] = { Type::getInt32PtrTy(context) };FunctionType *FT1 = FunctionType::get(Type::getVoidTy(context), ArgTy1, false);Function *F1 = Function::Create(FT1, Function::ExternalLinkage);BasicBlock *BB = BasicBlock::Create(context, "", F1);IRBuilder<> Builder(BB);Builder.CreateRetVoid();Function *F2 = Function::Create(FT1, Function::ExternalLinkage);Argument *A = &*F1->arg_begin();A->addAttr(Attribute::NoCapture);SmallVector<ReturnInst*, 4> Returns;ValueToValueMapTy VMap;VMap[A] = UndefValue::get(A->getType());CloneFunctionInto(F2, F1, VMap, CloneFunctionChangeType::LocalChangesOnly,Returns);EXPECT_FALSE(F2->arg_begin()->hasNoCaptureAttr());delete F1;delete F2;}TEST_F(CloneInstruction, CallingConvention) {Type *ArgTy1[] = { Type::getInt32PtrTy(context) };FunctionType *FT1 = FunctionType::get(Type::getVoidTy(context), ArgTy1, false);Function *F1 = Function::Create(FT1, Function::ExternalLinkage);F1->setCallingConv(CallingConv::Cold);BasicBlock *BB = BasicBlock::Create(context, "", F1);IRBuilder<> Builder(BB);Builder.CreateRetVoid();Function *F2 = Function::Create(FT1, Function::ExternalLinkage);SmallVector<ReturnInst*, 4> Returns;ValueToValueMapTy VMap;VMap[&*F1->arg_begin()] = &*F2->arg_begin();CloneFunctionInto(F2, F1, VMap, CloneFunctionChangeType::LocalChangesOnly,Returns);EXPECT_EQ(CallingConv::Cold, F2->getCallingConv());delete F1;delete F2;}TEST_F(CloneInstruction, DuplicateInstructionsToSplit) {Type *ArgTy1[] = {Type::getInt32PtrTy(context)};FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false);V = new Argument(Type::getInt32Ty(context));Function *F = Function::Create(FT, Function::ExternalLinkage);BasicBlock *BB1 = BasicBlock::Create(context, "", F);IRBuilder<> Builder1(BB1);BasicBlock *BB2 = BasicBlock::Create(context, "", F);IRBuilder<> Builder2(BB2);Builder1.CreateBr(BB2);Instruction *AddInst = cast<Instruction>(Builder2.CreateAdd(V, V));Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));Instruction *SubInst = cast<Instruction>(Builder2.CreateSub(MulInst, V));Builder2.CreateRetVoid();// Dummy DTU.ValueToValueMapTy Mapping;DomTreeUpdater DTU(DomTreeUpdater::UpdateStrategy::Lazy);auto Split =DuplicateInstructionsInSplitBetween(BB2, BB1, SubInst, Mapping, DTU);EXPECT_TRUE(Split);EXPECT_EQ(Mapping.size(), 2u);EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end());EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end());auto AddSplit = dyn_cast<Instruction>(Mapping[AddInst]);EXPECT_TRUE(AddSplit);EXPECT_EQ(AddSplit->getOperand(0), V);EXPECT_EQ(AddSplit->getOperand(1), V);EXPECT_EQ(AddSplit->getParent(), Split);auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);EXPECT_TRUE(MulSplit);EXPECT_EQ(MulSplit->getOperand(0), AddSplit);EXPECT_EQ(MulSplit->getOperand(1), V);EXPECT_EQ(MulSplit->getParent(), Split);EXPECT_EQ(AddSplit->getNextNode(), MulSplit);EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator());delete F;}TEST_F(CloneInstruction, DuplicateInstructionsToSplitBlocksEq1) {Type *ArgTy1[] = {Type::getInt32PtrTy(context)};FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false);V = new Argument(Type::getInt32Ty(context));Function *F = Function::Create(FT, Function::ExternalLinkage);BasicBlock *BB1 = BasicBlock::Create(context, "", F);IRBuilder<> Builder1(BB1);BasicBlock *BB2 = BasicBlock::Create(context, "", F);IRBuilder<> Builder2(BB2);Builder1.CreateBr(BB2);Instruction *AddInst = cast<Instruction>(Builder2.CreateAdd(V, V));Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));Instruction *SubInst = cast<Instruction>(Builder2.CreateSub(MulInst, V));Builder2.CreateBr(BB2);// Dummy DTU.DomTreeUpdater DTU(DomTreeUpdater::UpdateStrategy::Lazy);ValueToValueMapTy Mapping;auto Split = DuplicateInstructionsInSplitBetween(BB2, BB2, BB2->getTerminator(), Mapping, DTU);EXPECT_TRUE(Split);EXPECT_EQ(Mapping.size(), 3u);EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end());EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end());EXPECT_TRUE(Mapping.find(SubInst) != Mapping.end());auto AddSplit = dyn_cast<Instruction>(Mapping[AddInst]);EXPECT_TRUE(AddSplit);EXPECT_EQ(AddSplit->getOperand(0), V);EXPECT_EQ(AddSplit->getOperand(1), V);EXPECT_EQ(AddSplit->getParent(), Split);auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);EXPECT_TRUE(MulSplit);EXPECT_EQ(MulSplit->getOperand(0), AddSplit);EXPECT_EQ(MulSplit->getOperand(1), V);EXPECT_EQ(MulSplit->getParent(), Split);auto SubSplit = dyn_cast<Instruction>(Mapping[SubInst]);EXPECT_EQ(MulSplit->getNextNode(), SubSplit);EXPECT_EQ(SubSplit->getNextNode(), Split->getTerminator());EXPECT_EQ(Split->getSingleSuccessor(), BB2);EXPECT_EQ(BB2->getSingleSuccessor(), Split);delete F;}TEST_F(CloneInstruction, DuplicateInstructionsToSplitBlocksEq2) {Type *ArgTy1[] = {Type::getInt32PtrTy(context)};FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false);V = new Argument(Type::getInt32Ty(context));Function *F = Function::Create(FT, Function::ExternalLinkage);BasicBlock *BB1 = BasicBlock::Create(context, "", F);IRBuilder<> Builder1(BB1);BasicBlock *BB2 = BasicBlock::Create(context, "", F);IRBuilder<> Builder2(BB2);Builder1.CreateBr(BB2);Instruction *AddInst = cast<Instruction>(Builder2.CreateAdd(V, V));Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));Instruction *SubInst = cast<Instruction>(Builder2.CreateSub(MulInst, V));Builder2.CreateBr(BB2);// Dummy DTU.DomTreeUpdater DTU(DomTreeUpdater::UpdateStrategy::Lazy);ValueToValueMapTy Mapping;auto Split =DuplicateInstructionsInSplitBetween(BB2, BB2, SubInst, Mapping, DTU);EXPECT_TRUE(Split);EXPECT_EQ(Mapping.size(), 2u);EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end());EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end());auto AddSplit = dyn_cast<Instruction>(Mapping[AddInst]);EXPECT_TRUE(AddSplit);EXPECT_EQ(AddSplit->getOperand(0), V);EXPECT_EQ(AddSplit->getOperand(1), V);EXPECT_EQ(AddSplit->getParent(), Split);auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);EXPECT_TRUE(MulSplit);EXPECT_EQ(MulSplit->getOperand(0), AddSplit);EXPECT_EQ(MulSplit->getOperand(1), V);EXPECT_EQ(MulSplit->getParent(), Split);EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator());EXPECT_EQ(Split->getSingleSuccessor(), BB2);EXPECT_EQ(BB2->getSingleSuccessor(), Split);delete F;}static void runWithLoopInfoAndDominatorTree(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI, DominatorTree &DT)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;DominatorTree DT(*F);LoopInfo LI(DT);Test(*F, LI, DT);}static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("CloneLoop", errs());return Mod;}TEST(CloneLoop, CloneLoopNest) {// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = parseIR(Context,R"(define void @foo(i32* %A, i32 %ub) {entry:%guardcmp = icmp slt i32 0, %ubbr i1 %guardcmp, label %for.outer.preheader, label %for.endfor.outer.preheader:br label %for.outerfor.outer:%j = phi i32 [ 0, %for.outer.preheader ], [ %inc.outer, %for.outer.latch ]br i1 %guardcmp, label %for.inner.preheader, label %for.outer.latchfor.inner.preheader:br label %for.innerfor.inner:%i = phi i32 [ 0, %for.inner.preheader ], [ %inc, %for.inner ]%idxprom = sext i32 %i to i64%arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxpromstore i32 %i, i32* %arrayidx, align 4%inc = add nsw i32 %i, 1%cmp = icmp slt i32 %inc, %ubbr i1 %cmp, label %for.inner, label %for.inner.exitfor.inner.exit:br label %for.outer.latchfor.outer.latch:%inc.outer = add nsw i32 %j, 1%cmp.outer = icmp slt i32 %inc.outer, %ubbr i1 %cmp.outer, label %for.outer, label %for.outer.exitfor.outer.exit:br label %for.endfor.end:ret void})");runWithLoopInfoAndDominatorTree(*M, "foo", [&](Function &F, LoopInfo &LI, DominatorTree &DT) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Preheader = &*(++FI);BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.outer");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);EXPECT_EQ(Header, L->getHeader());EXPECT_EQ(Preheader, L->getLoopPreheader());ValueToValueMapTy VMap;SmallVector<BasicBlock *, 4> ClonedLoopBlocks;Loop *NewLoop = cloneLoopWithPreheader(Preheader, Preheader, L, VMap,"", &LI, &DT, ClonedLoopBlocks);EXPECT_NE(NewLoop, nullptr);EXPECT_EQ(NewLoop->getSubLoops().size(), 1u);Loop::block_iterator BI = NewLoop->block_begin();EXPECT_TRUE((*BI)->getName().startswith("for.outer"));EXPECT_TRUE((*(++BI))->getName().startswith("for.inner.preheader"));EXPECT_TRUE((*(++BI))->getName().startswith("for.inner"));EXPECT_TRUE((*(++BI))->getName().startswith("for.inner.exit"));EXPECT_TRUE((*(++BI))->getName().startswith("for.outer.latch"));});}class CloneFunc : public ::testing::Test {protected:void SetUp() override {SetupModule();CreateOldFunc();CreateNewFunc();SetupFinder();}void TearDown() override { delete Finder; }void SetupModule() {M = new Module("", C);}void CreateOldFunc() {FunctionType* FuncType = FunctionType::get(Type::getVoidTy(C), false);OldFunc = Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", M);CreateOldFunctionBodyAndDI();}void CreateOldFunctionBodyAndDI() {DIBuilder DBuilder(*M);IRBuilder<> IBuilder(C);// Function DIauto *File = DBuilder.createFile("filename.c", "/file/dir/");DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None);DISubroutineType *FuncType =DBuilder.createSubroutineType(ParamTypes);auto *CU = DBuilder.createCompileUnit(dwarf::DW_LANG_C99,DBuilder.createFile("filename.c","/file/dir"),"CloneFunc", false, "", 0);auto *Subprogram = DBuilder.createFunction(CU, "f", "f", File, 4, FuncType, 3, DINode::FlagZero,DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition);OldFunc->setSubprogram(Subprogram);// Function bodyBasicBlock* Entry = BasicBlock::Create(C, "", OldFunc);IBuilder.SetInsertPoint(Entry);DebugLoc Loc = DILocation::get(Subprogram->getContext(), 3, 2, Subprogram);IBuilder.SetCurrentDebugLocation(Loc);AllocaInst* Alloca = IBuilder.CreateAlloca(IntegerType::getInt32Ty(C));IBuilder.SetCurrentDebugLocation(DILocation::get(Subprogram->getContext(), 4, 2, Subprogram));Value* AllocaContent = IBuilder.getInt32(1);Instruction* Store = IBuilder.CreateStore(AllocaContent, Alloca);IBuilder.SetCurrentDebugLocation(DILocation::get(Subprogram->getContext(), 5, 2, Subprogram));// Create a local variable around the allocaauto *IntType = DBuilder.createBasicType("int", 32, dwarf::DW_ATE_signed);auto *E = DBuilder.createExpression();auto *Variable =DBuilder.createAutoVariable(Subprogram, "x", File, 5, IntType, true);auto *DL = DILocation::get(Subprogram->getContext(), 5, 0, Subprogram);DBuilder.insertDeclare(Alloca, Variable, E, DL, Store);DBuilder.insertDbgValueIntrinsic(AllocaContent, Variable, E, DL, Entry);// Also create an inlined variable.// Create a distinct struct type that we should not duplicate during// cloning).auto *StructType = DICompositeType::getDistinct(C, dwarf::DW_TAG_structure_type, "some_struct", nullptr, 0, nullptr,nullptr, 32, 32, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr);auto *InlinedSP = DBuilder.createFunction(CU, "inlined", "inlined", File, 8, FuncType, 9, DINode::FlagZero,DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition);auto *InlinedVar =DBuilder.createAutoVariable(InlinedSP, "inlined", File, 5, StructType, true);auto *Scope = DBuilder.createLexicalBlock(DBuilder.createLexicalBlockFile(InlinedSP, File), File, 1, 1);auto InlinedDL = DILocation::get(Subprogram->getContext(), 9, 4, Scope,DILocation::get(Subprogram->getContext(), 5, 2, Subprogram));IBuilder.SetCurrentDebugLocation(InlinedDL);DBuilder.insertDeclare(Alloca, InlinedVar, E, InlinedDL, Store);IBuilder.CreateStore(IBuilder.getInt32(2), Alloca);// Finalize the debug info.DBuilder.finalize();IBuilder.CreateRetVoid();// Create another, empty, compile unit.DIBuilder DBuilder2(*M);DBuilder2.createCompileUnit(dwarf::DW_LANG_C99,DBuilder.createFile("extra.c", "/file/dir"),"CloneFunc", false, "", 0);DBuilder2.finalize();}void CreateNewFunc() {ValueToValueMapTy VMap;NewFunc = CloneFunction(OldFunc, VMap, nullptr);}void SetupFinder() {Finder = new DebugInfoFinder();Finder->processModule(*M);}LLVMContext C;Function* OldFunc;Function* NewFunc;Module* M;DebugInfoFinder* Finder;};// Test that a new, distinct function was created.TEST_F(CloneFunc, NewFunctionCreated) {EXPECT_NE(OldFunc, NewFunc);}// Test that a new subprogram entry was added and is pointing to the new// function, while the original subprogram still points to the old one.TEST_F(CloneFunc, Subprogram) {EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_EQ(3U, Finder->subprogram_count());EXPECT_NE(NewFunc->getSubprogram(), OldFunc->getSubprogram());}// Test that instructions in the old function still belong to it in the// metadata, while instruction in the new function belong to the new one.TEST_F(CloneFunc, InstructionOwnership) {EXPECT_FALSE(verifyModule(*M));inst_iterator OldIter = inst_begin(OldFunc);inst_iterator OldEnd = inst_end(OldFunc);inst_iterator NewIter = inst_begin(NewFunc);inst_iterator NewEnd = inst_end(NewFunc);while (OldIter != OldEnd && NewIter != NewEnd) {Instruction& OldI = *OldIter;Instruction& NewI = *NewIter;EXPECT_NE(&OldI, &NewI);EXPECT_EQ(OldI.hasMetadata(), NewI.hasMetadata());if (OldI.hasMetadata()) {const DebugLoc& OldDL = OldI.getDebugLoc();const DebugLoc& NewDL = NewI.getDebugLoc();// Verify that the debug location data is the sameEXPECT_EQ(OldDL.getLine(), NewDL.getLine());EXPECT_EQ(OldDL.getCol(), NewDL.getCol());// But that they belong to different functionsauto *OldSubprogram = cast<DISubprogram>(OldDL.getInlinedAtScope());auto *NewSubprogram = cast<DISubprogram>(NewDL.getInlinedAtScope());EXPECT_EQ(OldFunc->getSubprogram(), OldSubprogram);EXPECT_EQ(NewFunc->getSubprogram(), NewSubprogram);}++OldIter;++NewIter;}EXPECT_EQ(OldEnd, OldIter);EXPECT_EQ(NewEnd, NewIter);}// Test that the arguments for debug intrinsics in the new function were// properly clonedTEST_F(CloneFunc, DebugIntrinsics) {EXPECT_FALSE(verifyModule(*M));inst_iterator OldIter = inst_begin(OldFunc);inst_iterator OldEnd = inst_end(OldFunc);inst_iterator NewIter = inst_begin(NewFunc);inst_iterator NewEnd = inst_end(NewFunc);while (OldIter != OldEnd && NewIter != NewEnd) {Instruction& OldI = *OldIter;Instruction& NewI = *NewIter;if (DbgDeclareInst* OldIntrin = dyn_cast<DbgDeclareInst>(&OldI)) {DbgDeclareInst* NewIntrin = dyn_cast<DbgDeclareInst>(&NewI);EXPECT_TRUE(NewIntrin);// Old address must belong to the old functionEXPECT_EQ(OldFunc, cast<AllocaInst>(OldIntrin->getAddress())->getParent()->getParent());// New address must belong to the new functionEXPECT_EQ(NewFunc, cast<AllocaInst>(NewIntrin->getAddress())->getParent()->getParent());if (OldIntrin->getDebugLoc()->getInlinedAt()) {// Inlined variable should refer to the same DILocalVariable as in the// Old FunctionEXPECT_EQ(OldIntrin->getVariable(), NewIntrin->getVariable());} else {// Old variable must belong to the old function.EXPECT_EQ(OldFunc->getSubprogram(),cast<DISubprogram>(OldIntrin->getVariable()->getScope()));// New variable must belong to the new function.EXPECT_EQ(NewFunc->getSubprogram(),cast<DISubprogram>(NewIntrin->getVariable()->getScope()));}} else if (DbgValueInst* OldIntrin = dyn_cast<DbgValueInst>(&OldI)) {DbgValueInst* NewIntrin = dyn_cast<DbgValueInst>(&NewI);EXPECT_TRUE(NewIntrin);if (!OldIntrin->getDebugLoc()->getInlinedAt()) {// Old variable must belong to the old function.EXPECT_EQ(OldFunc->getSubprogram(),cast<DISubprogram>(OldIntrin->getVariable()->getScope()));// New variable must belong to the new function.EXPECT_EQ(NewFunc->getSubprogram(),cast<DISubprogram>(NewIntrin->getVariable()->getScope()));}}++OldIter;++NewIter;}}static int GetDICompileUnitCount(const Module& M) {if (const auto* LLVM_DBG_CU = M.getNamedMetadata("llvm.dbg.cu")) {return LLVM_DBG_CU->getNumOperands();}return 0;}static bool haveCompileUnitsInCommon(const Module &LHS, const Module &RHS) {const NamedMDNode *LHSCUs = LHS.getNamedMetadata("llvm.dbg.cu");if (!LHSCUs)return false;const NamedMDNode *RHSCUs = RHS.getNamedMetadata("llvm.dbg.cu");if (!RHSCUs)return false;SmallPtrSet<const MDNode *, 8> Found;for (int I = 0, E = LHSCUs->getNumOperands(); I != E; ++I)if (const MDNode *N = LHSCUs->getOperand(I))Found.insert(N);for (int I = 0, E = RHSCUs->getNumOperands(); I != E; ++I)if (const MDNode *N = RHSCUs->getOperand(I))if (Found.count(N))return true;return false;}TEST(CloneFunction, CloneEmptyFunction) {StringRef ImplAssembly = R"(define void @foo() {ret void}declare void @bar())";LLVMContext Context;SMDiagnostic Error;auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);EXPECT_TRUE(ImplModule != nullptr);auto *ImplFunction = ImplModule->getFunction("foo");EXPECT_TRUE(ImplFunction != nullptr);auto *DeclFunction = ImplModule->getFunction("bar");EXPECT_TRUE(DeclFunction != nullptr);ValueToValueMapTy VMap;SmallVector<ReturnInst *, 8> Returns;ClonedCodeInfo CCI;CloneFunctionInto(ImplFunction, DeclFunction, VMap,CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI);EXPECT_FALSE(verifyModule(*ImplModule, &errs()));EXPECT_FALSE(CCI.ContainsCalls);EXPECT_FALSE(CCI.ContainsDynamicAllocas);}TEST(CloneFunction, CloneFunctionWithInalloca) {StringRef ImplAssembly = R"(declare void @a(i32* inalloca(i32))define void @foo() {%a = alloca inalloca i32call void @a(i32* inalloca(i32) %a)ret void}declare void @bar())";LLVMContext Context;SMDiagnostic Error;auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);EXPECT_TRUE(ImplModule != nullptr);auto *ImplFunction = ImplModule->getFunction("foo");EXPECT_TRUE(ImplFunction != nullptr);auto *DeclFunction = ImplModule->getFunction("bar");EXPECT_TRUE(DeclFunction != nullptr);ValueToValueMapTy VMap;SmallVector<ReturnInst *, 8> Returns;ClonedCodeInfo CCI;CloneFunctionInto(DeclFunction, ImplFunction, VMap,CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI);EXPECT_FALSE(verifyModule(*ImplModule, &errs()));EXPECT_TRUE(CCI.ContainsCalls);EXPECT_TRUE(CCI.ContainsDynamicAllocas);}TEST(CloneFunction, CloneFunctionWithSubprograms) {// Tests that the debug info is duplicated correctly when a DISubprogram// happens to be one of the operands of the DISubprogram that is being cloned.// In general, operands of "test" that are distinct should be duplicated,// but in this case "my_operator" should not be duplicated. If it is// duplicated, the metadata in the llvm.dbg.declare could end up with// different duplicates.StringRef ImplAssembly = R"(declare void @llvm.dbg.declare(metadata, metadata, metadata)define void @test() !dbg !5 {call void @llvm.dbg.declare(metadata i8* undef, metadata !4, metadata !DIExpression()), !dbg !6ret void}declare void @cloned()!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!2}!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1)!1 = !DIFile(filename: "test.cpp", directory: "")!2 = !{i32 1, !"Debug Info Version", i32 3}!3 = distinct !DISubprogram(name: "my_operator", scope: !1, unit: !0, retainedNodes: !{!4})!4 = !DILocalVariable(name: "awaitables", scope: !3)!5 = distinct !DISubprogram(name: "test", scope: !3, unit: !0)!6 = !DILocation(line: 55, column: 15, scope: !3, inlinedAt: !7)!7 = distinct !DILocation(line: 73, column: 14, scope: !5))";LLVMContext Context;SMDiagnostic Error;auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);EXPECT_TRUE(ImplModule != nullptr);auto *OldFunc = ImplModule->getFunction("test");EXPECT_TRUE(OldFunc != nullptr);auto *NewFunc = ImplModule->getFunction("cloned");EXPECT_TRUE(NewFunc != nullptr);ValueToValueMapTy VMap;SmallVector<ReturnInst *, 8> Returns;ClonedCodeInfo CCI;CloneFunctionInto(NewFunc, OldFunc, VMap,CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI);// This fails if the scopes in the llvm.dbg.declare variable and location// aren't the same.EXPECT_FALSE(verifyModule(*ImplModule, &errs()));}TEST(CloneFunction, CloneFunctionWithInlinedSubprograms) {StringRef ImplAssembly = R"(declare void @llvm.dbg.declare(metadata, metadata, metadata)define void @test() !dbg !3 {call void @llvm.dbg.declare(metadata i8* undef, metadata !5, metadata !DIExpression()), !dbg !7ret void}declare void @cloned()!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!2}!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1)!1 = !DIFile(filename: "test.cpp", directory: "")!2 = !{i32 1, !"Debug Info Version", i32 3}!3 = distinct !DISubprogram(name: "test", scope: !0, unit: !0)!4 = distinct !DISubprogram(name: "inlined", scope: !0, unit: !0, retainedNodes: !{!5})!5 = !DILocalVariable(name: "awaitables", scope: !4)!6 = distinct !DILexicalBlock(scope: !4, file: !1, line: 1)!7 = !DILocation(line: 1, scope: !6, inlinedAt: !8)!8 = !DILocation(line: 10, scope: !3))";LLVMContext Context;SMDiagnostic Error;auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);EXPECT_TRUE(ImplModule != nullptr);auto *Func = ImplModule->getFunction("test");EXPECT_TRUE(Func != nullptr);auto *ClonedFunc = ImplModule->getFunction("cloned");EXPECT_TRUE(ClonedFunc != nullptr);ValueToValueMapTy VMap;SmallVector<ReturnInst *, 8> Returns;ClonedCodeInfo CCI;CloneFunctionInto(ClonedFunc, Func, VMap,CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI);EXPECT_FALSE(verifyModule(*ImplModule, &errs()));// Check that DILexicalBlock of inlined function was not cloned.auto DbgDeclareI = Func->begin()->begin();auto ClonedDbgDeclareI = ClonedFunc->begin()->begin();const DebugLoc &DbgLoc = DbgDeclareI->getDebugLoc();const DebugLoc &ClonedDbgLoc = ClonedDbgDeclareI->getDebugLoc();EXPECT_NE(DbgLoc.get(), ClonedDbgLoc.get());EXPECT_EQ(cast<DILexicalBlock>(DbgLoc.getScope()),cast<DILexicalBlock>(ClonedDbgLoc.getScope()));}TEST(CloneFunction, CloneFunctionToDifferentModule) {StringRef ImplAssembly = R"(define void @foo() {ret void, !dbg !5}!llvm.module.flags = !{!0}!llvm.dbg.cu = !{!2, !6}!0 = !{i32 1, !"Debug Info Version", i32 3}!1 = distinct !DISubprogram(unit: !2)!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3)!3 = !DIFile(filename: "foo.c", directory: "/tmp")!4 = distinct !DISubprogram(unit: !2)!5 = !DILocation(line: 4, scope: !1)!6 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3))";StringRef DeclAssembly = R"(declare void @foo())";LLVMContext Context;SMDiagnostic Error;auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);EXPECT_TRUE(ImplModule != nullptr);// DICompileUnits: !2, !6. Only !2 is reachable from @foo().EXPECT_TRUE(GetDICompileUnitCount(*ImplModule) == 2);auto* ImplFunction = ImplModule->getFunction("foo");EXPECT_TRUE(ImplFunction != nullptr);auto DeclModule = parseAssemblyString(DeclAssembly, Error, Context);EXPECT_TRUE(DeclModule != nullptr);// No DICompileUnits defined here.EXPECT_TRUE(GetDICompileUnitCount(*DeclModule) == 0);auto* DeclFunction = DeclModule->getFunction("foo");EXPECT_TRUE(DeclFunction != nullptr);ValueToValueMapTy VMap;VMap[ImplFunction] = DeclFunction;// No args to mapSmallVector<ReturnInst*, 8> Returns;CloneFunctionInto(DeclFunction, ImplFunction, VMap,CloneFunctionChangeType::DifferentModule, Returns);EXPECT_FALSE(verifyModule(*ImplModule, &errs()));EXPECT_FALSE(verifyModule(*DeclModule, &errs()));// DICompileUnit !2 shall be cloned into DeclModule.EXPECT_TRUE(GetDICompileUnitCount(*DeclModule) == 1);EXPECT_FALSE(haveCompileUnitsInCommon(*ImplModule, *DeclModule));}class CloneModule : public ::testing::Test {protected:void SetUp() override {SetupModule();CreateOldModule();CreateNewModule();}void SetupModule() { OldM = new Module("", C); }void CreateOldModule() {auto *CD = OldM->getOrInsertComdat("comdat");CD->setSelectionKind(Comdat::ExactMatch);auto GV = new GlobalVariable(*OldM, Type::getInt32Ty(C), false, GlobalValue::ExternalLinkage,ConstantInt::get(Type::getInt32Ty(C), 1), "gv");GV->addMetadata(LLVMContext::MD_type, *MDNode::get(C, {}));GV->setComdat(CD);{// Add an empty compile unit first that isn't otherwise referenced, to// confirm that compile units get cloned in the correct order.DIBuilder EmptyBuilder(*OldM);auto *File = EmptyBuilder.createFile("empty.c", "/file/dir/");(void)EmptyBuilder.createCompileUnit(dwarf::DW_LANG_C99, File,"EmptyUnit", false, "", 0);EmptyBuilder.finalize();}DIBuilder DBuilder(*OldM);IRBuilder<> IBuilder(C);auto *FuncType = FunctionType::get(Type::getVoidTy(C), false);auto *PersFn = Function::Create(FuncType, GlobalValue::ExternalLinkage,"persfn", OldM);auto *F =Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", OldM);F->setPersonalityFn(PersFn);F->setComdat(CD);// Create debug infoauto *File = DBuilder.createFile("filename.c", "/file/dir/");DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None);DISubroutineType *DFuncType = DBuilder.createSubroutineType(ParamTypes);auto *CU = DBuilder.createCompileUnit(dwarf::DW_LANG_C99,DBuilder.createFile("filename.c","/file/dir"),"CloneModule", false, "", 0);// Function DIauto *Subprogram = DBuilder.createFunction(CU, "f", "f", File, 4, DFuncType, 3, DINode::FlagZero,DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition);F->setSubprogram(Subprogram);// Create and assign DIGlobalVariableExpression to gvauto GVExpression = DBuilder.createGlobalVariableExpression(Subprogram, "gv", "gv", File, 1, DBuilder.createNullPtrType(), false);GV->addDebugInfo(GVExpression);// DIGlobalVariableExpression not attached to any global variableauto Expr = DBuilder.createExpression(ArrayRef<uint64_t>{dwarf::DW_OP_constu, 42U, dwarf::DW_OP_stack_value});DBuilder.createGlobalVariableExpression(Subprogram, "unattached", "unattached", File, 1,DBuilder.createNullPtrType(), false, true, Expr);auto *Entry = BasicBlock::Create(C, "", F);IBuilder.SetInsertPoint(Entry);IBuilder.CreateRetVoid();auto *G =Function::Create(FuncType, GlobalValue::ExternalLinkage, "g", OldM);G->addMetadata(LLVMContext::MD_type, *MDNode::get(C, {}));// Finalize the debug infoDBuilder.finalize();}void CreateNewModule() { NewM = llvm::CloneModule(*OldM).release(); }LLVMContext C;Module *OldM;Module *NewM;};TEST_F(CloneModule, Verify) {// Confirm the old module is (still) valid.EXPECT_FALSE(verifyModule(*OldM, &errs()));// Check the new module.EXPECT_FALSE(verifyModule(*NewM, &errs()));}TEST_F(CloneModule, OldModuleUnchanged) {DebugInfoFinder Finder;Finder.processModule(*OldM);EXPECT_EQ(1U, Finder.subprogram_count());}TEST_F(CloneModule, Subprogram) {Function *NewF = NewM->getFunction("f");DISubprogram *SP = NewF->getSubprogram();EXPECT_TRUE(SP != nullptr);EXPECT_EQ(SP->getName(), "f");EXPECT_EQ(SP->getFile()->getFilename(), "filename.c");EXPECT_EQ(SP->getLine(), (unsigned)4);}TEST_F(CloneModule, FunctionDeclarationMetadata) {Function *NewF = NewM->getFunction("g");EXPECT_NE(nullptr, NewF->getMetadata(LLVMContext::MD_type));}TEST_F(CloneModule, GlobalMetadata) {GlobalVariable *NewGV = NewM->getGlobalVariable("gv");EXPECT_NE(nullptr, NewGV->getMetadata(LLVMContext::MD_type));}TEST_F(CloneModule, GlobalDebugInfo) {GlobalVariable *NewGV = NewM->getGlobalVariable("gv");EXPECT_TRUE(NewGV != nullptr);// Find debug info expression assigned to globalSmallVector<DIGlobalVariableExpression *, 1> GVs;NewGV->getDebugInfo(GVs);EXPECT_EQ(GVs.size(), 1U);DIGlobalVariableExpression *GVExpr = GVs[0];DIGlobalVariable *GV = GVExpr->getVariable();EXPECT_TRUE(GV != nullptr);EXPECT_EQ(GV->getName(), "gv");EXPECT_EQ(GV->getLine(), 1U);// Assert that the scope of the debug info attached to// global variable matches the cloned function.DISubprogram *SP = NewM->getFunction("f")->getSubprogram();EXPECT_TRUE(SP != nullptr);EXPECT_EQ(GV->getScope(), SP);}TEST_F(CloneModule, CompileUnit) {// Find DICompileUnit listed in llvm.dbg.cuauto *NMD = NewM->getNamedMetadata("llvm.dbg.cu");EXPECT_TRUE(NMD != nullptr);EXPECT_EQ(NMD->getNumOperands(), 2U);EXPECT_FALSE(haveCompileUnitsInCommon(*OldM, *NewM));// Check that the empty CU is first, even though it's not referenced except// from named metadata.DICompileUnit *EmptyCU = dyn_cast<llvm::DICompileUnit>(NMD->getOperand(0));EXPECT_TRUE(EmptyCU != nullptr);EXPECT_EQ("EmptyUnit", EmptyCU->getProducer());// Get the interesting CU.DICompileUnit *CU = dyn_cast<llvm::DICompileUnit>(NMD->getOperand(1));EXPECT_TRUE(CU != nullptr);EXPECT_EQ("CloneModule", CU->getProducer());// Assert this CU is consistent with the cloned function debug infoDISubprogram *SP = NewM->getFunction("f")->getSubprogram();EXPECT_TRUE(SP != nullptr);EXPECT_EQ(SP->getUnit(), CU);// Check globals listed in CU have the correct scopeDIGlobalVariableExpressionArray GlobalArray = CU->getGlobalVariables();EXPECT_EQ(GlobalArray.size(), 2U);for (DIGlobalVariableExpression *GVExpr : GlobalArray) {DIGlobalVariable *GV = GVExpr->getVariable();EXPECT_EQ(GV->getScope(), SP);}}TEST_F(CloneModule, Comdat) {GlobalVariable *NewGV = NewM->getGlobalVariable("gv");auto *CD = NewGV->getComdat();ASSERT_NE(nullptr, CD);EXPECT_EQ("comdat", CD->getName());EXPECT_EQ(Comdat::ExactMatch, CD->getSelectionKind());Function *NewF = NewM->getFunction("f");EXPECT_EQ(CD, NewF->getComdat());}}
//===- CallPromotionUtilsTest.cpp - CallPromotionUtils unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/CallPromotionUtils.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("UtilsTests", errs());return Mod;}TEST(CallPromotionUtilsTest, TryPromoteCall) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%class.Impl = type <{ %class.Interface, i32, [4 x i8] }>%class.Interface = type { i32 (...)** }@_ZTV4Impl = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* null, i8* bitcast (void (%class.Impl*)* @_ZN4Impl3RunEv to i8*)] }define void @f() {entry:%o = alloca %class.Impl%base = getelementptr %class.Impl, %class.Impl* %o, i64 0, i32 0, i32 0store i32 (...)** bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTV4Impl, i64 0, inrange i32 0, i64 2) to i32 (...)**), i32 (...)*** %base%f = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 1store i32 3, i32* %f%base.i = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 0%c = bitcast %class.Interface* %base.i to void (%class.Interface*)***%vtable.i = load void (%class.Interface*)**, void (%class.Interface*)*** %c%fp = load void (%class.Interface*)*, void (%class.Interface*)** %vtable.icall void %fp(%class.Interface* nonnull %base.i)ret void}declare void @_ZN4Impl3RunEv(%class.Impl* %this))IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = &*++F->front().rbegin();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted = tryPromoteCall(*CI);EXPECT_TRUE(IsPromoted);GV = M->getNamedValue("_ZN4Impl3RunEv");ASSERT_TRUE(GV);auto *F1 = dyn_cast<Function>(GV);EXPECT_EQ(F1, CI->getCalledFunction());}TEST(CallPromotionUtilsTest, TryPromoteCall_NoFPLoad) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%class.Impl = type <{ %class.Interface, i32, [4 x i8] }>%class.Interface = type { i32 (...)** }define void @f(void (%class.Interface*)* %fp, %class.Interface* nonnull %base.i) {entry:call void %fp(%class.Interface* nonnull %base.i)ret void})IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted = tryPromoteCall(*CI);EXPECT_FALSE(IsPromoted);}TEST(CallPromotionUtilsTest, TryPromoteCall_NoVTablePtrLoad) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%class.Impl = type <{ %class.Interface, i32, [4 x i8] }>%class.Interface = type { i32 (...)** }define void @f(void (%class.Interface*)** %vtable.i, %class.Interface* nonnull %base.i) {entry:%fp = load void (%class.Interface*)*, void (%class.Interface*)** %vtable.icall void %fp(%class.Interface* nonnull %base.i)ret void})IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &*++F->front().rbegin();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted = tryPromoteCall(*CI);EXPECT_FALSE(IsPromoted);}TEST(CallPromotionUtilsTest, TryPromoteCall_NoVTableInitFound) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%class.Impl = type <{ %class.Interface, i32, [4 x i8] }>%class.Interface = type { i32 (...)** }define void @f() {entry:%o = alloca %class.Impl%f = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 1store i32 3, i32* %f%base.i = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 0%c = bitcast %class.Interface* %base.i to void (%class.Interface*)***%vtable.i = load void (%class.Interface*)**, void (%class.Interface*)*** %c%fp = load void (%class.Interface*)*, void (%class.Interface*)** %vtable.icall void %fp(%class.Interface* nonnull %base.i)ret void}declare void @_ZN4Impl3RunEv(%class.Impl* %this))IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &*++F->front().rbegin();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted = tryPromoteCall(*CI);EXPECT_FALSE(IsPromoted);}TEST(CallPromotionUtilsTest, TryPromoteCall_EmptyVTable) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%class.Impl = type <{ %class.Interface, i32, [4 x i8] }>%class.Interface = type { i32 (...)** }@_ZTV4Impl = external global { [3 x i8*] }define void @f() {entry:%o = alloca %class.Impl%base = getelementptr %class.Impl, %class.Impl* %o, i64 0, i32 0, i32 0store i32 (...)** bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTV4Impl, i64 0, inrange i32 0, i64 2) to i32 (...)**), i32 (...)*** %base%f = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 1store i32 3, i32* %f%base.i = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 0%c = bitcast %class.Interface* %base.i to void (%class.Interface*)***%vtable.i = load void (%class.Interface*)**, void (%class.Interface*)*** %c%fp = load void (%class.Interface*)*, void (%class.Interface*)** %vtable.icall void %fp(%class.Interface* nonnull %base.i)ret void}declare void @_ZN4Impl3RunEv(%class.Impl* %this))IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = &*++F->front().rbegin();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted = tryPromoteCall(*CI);EXPECT_FALSE(IsPromoted);}TEST(CallPromotionUtilsTest, TryPromoteCall_NullFP) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%class.Impl = type <{ %class.Interface, i32, [4 x i8] }>%class.Interface = type { i32 (...)** }@_ZTV4Impl = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* null, i8* null] }define void @f() {entry:%o = alloca %class.Impl%base = getelementptr %class.Impl, %class.Impl* %o, i64 0, i32 0, i32 0store i32 (...)** bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTV4Impl, i64 0, inrange i32 0, i64 2) to i32 (...)**), i32 (...)*** %base%f = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 1store i32 3, i32* %f%base.i = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 0%c = bitcast %class.Interface* %base.i to void (%class.Interface*)***%vtable.i = load void (%class.Interface*)**, void (%class.Interface*)*** %c%fp = load void (%class.Interface*)*, void (%class.Interface*)** %vtable.icall void %fp(%class.Interface* nonnull %base.i)ret void}declare void @_ZN4Impl3RunEv(%class.Impl* %this))IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = &*++F->front().rbegin();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted = tryPromoteCall(*CI);EXPECT_FALSE(IsPromoted);}// Based on clang/test/CodeGenCXX/member-function-pointer-calls.cppTEST(CallPromotionUtilsTest, TryPromoteCall_MemberFunctionCalls) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%struct.A = type { i32 (...)** }@_ZTV1A = linkonce_odr unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* null, i8* bitcast (i32 (%struct.A*)* @_ZN1A3vf1Ev to i8*), i8* bitcast (i32 (%struct.A*)* @_ZN1A3vf2Ev to i8*)] }, align 8define i32 @_Z2g1v() {entry:%a = alloca %struct.A, align 8%0 = bitcast %struct.A* %a to i8*%1 = getelementptr %struct.A, %struct.A* %a, i64 0, i32 0store i32 (...)** bitcast (i8** getelementptr inbounds ({ [4 x i8*] }, { [4 x i8*] }* @_ZTV1A, i64 0, inrange i32 0, i64 2) to i32 (...)**), i32 (...)*** %1, align 8%2 = bitcast %struct.A* %a to i8*%3 = bitcast i8* %2 to i8**%vtable.i = load i8*, i8** %3, align 8%4 = bitcast i8* %vtable.i to i32 (%struct.A*)**%memptr.virtualfn.i = load i32 (%struct.A*)*, i32 (%struct.A*)** %4, align 8%call.i = call i32 %memptr.virtualfn.i(%struct.A* %a)ret i32 %call.i}define i32 @_Z2g2v() {entry:%a = alloca %struct.A, align 8%0 = bitcast %struct.A* %a to i8*%1 = getelementptr %struct.A, %struct.A* %a, i64 0, i32 0store i32 (...)** bitcast (i8** getelementptr inbounds ({ [4 x i8*] }, { [4 x i8*] }* @_ZTV1A, i64 0, inrange i32 0, i64 2) to i32 (...)**), i32 (...)*** %1, align 8%2 = bitcast %struct.A* %a to i8*%3 = bitcast i8* %2 to i8**%vtable.i = load i8*, i8** %3, align 8%4 = getelementptr i8, i8* %vtable.i, i64 8%5 = bitcast i8* %4 to i32 (%struct.A*)**%memptr.virtualfn.i = load i32 (%struct.A*)*, i32 (%struct.A*)** %5, align 8%call.i = call i32 %memptr.virtualfn.i(%struct.A* %a)ret i32 %call.i}declare i32 @_ZN1A3vf1Ev(%struct.A* %this)declare i32 @_ZN1A3vf2Ev(%struct.A* %this))IR");auto *GV = M->getNamedValue("_Z2g1v");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = &*++F->front().rbegin();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted1 = tryPromoteCall(*CI);EXPECT_TRUE(IsPromoted1);GV = M->getNamedValue("_ZN1A3vf1Ev");ASSERT_TRUE(GV);F = dyn_cast<Function>(GV);EXPECT_EQ(F, CI->getCalledFunction());GV = M->getNamedValue("_Z2g2v");ASSERT_TRUE(GV);F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Inst = &F->front().front();AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = &*++F->front().rbegin();CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted2 = tryPromoteCall(*CI);EXPECT_TRUE(IsPromoted2);GV = M->getNamedValue("_ZN1A3vf2Ev");ASSERT_TRUE(GV);F = dyn_cast<Function>(GV);EXPECT_EQ(F, CI->getCalledFunction());}// Check that it isn't crashing due to missing promotion legality.TEST(CallPromotionUtilsTest, TryPromoteCall_Legality) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(%struct1 = type <{ i32, i64 }>%struct2 = type <{ i32, i64 }>%class.Impl = type <{ %class.Interface, i32, [4 x i8] }>%class.Interface = type { i32 (...)** }@_ZTV4Impl = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* null, i8* bitcast (%struct2 (%class.Impl*)* @_ZN4Impl3RunEv to i8*)] }define %struct1 @f() {entry:%o = alloca %class.Impl%base = getelementptr %class.Impl, %class.Impl* %o, i64 0, i32 0, i32 0store i32 (...)** bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTV4Impl, i64 0, inrange i32 0, i64 2) to i32 (...)**), i32 (...)*** %base%f = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 1store i32 3, i32* %f%base.i = getelementptr inbounds %class.Impl, %class.Impl* %o, i64 0, i32 0%c = bitcast %class.Interface* %base.i to %struct1 (%class.Interface*)***%vtable.i = load %struct1 (%class.Interface*)**, %struct1 (%class.Interface*)*** %c%fp = load %struct1 (%class.Interface*)*, %struct1 (%class.Interface*)** %vtable.i%rv = call %struct1 %fp(%class.Interface* nonnull %base.i)ret %struct1 %rv}declare %struct2 @_ZN4Impl3RunEv(%class.Impl* %this))IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = &*++F->front().rbegin();auto *CI = dyn_cast<CallInst>(Inst);ASSERT_TRUE(CI);ASSERT_FALSE(CI->getCalledFunction());bool IsPromoted = tryPromoteCall(*CI);EXPECT_FALSE(IsPromoted);}
set(LLVM_LINK_COMPONENTSAnalysisAsmParserBitWriterCoreSupportTransformUtilsPassesVectorize)add_llvm_unittest(UtilsTestsASanStackFrameLayoutTest.cppBasicBlockUtilsTest.cppCallPromotionUtilsTest.cppCloningTest.cppCodeExtractorTest.cppCodeMoverUtilsTest.cppDebugifyTest.cppFunctionComparatorTest.cppIntegerDivisionTest.cppLocalTest.cppLoopRotationUtilsTest.cppLoopUtilsTest.cppMemTransferLowering.cppModuleUtilsTest.cppScalarEvolutionExpanderTest.cppSizeOptsTest.cppSSAUpdaterBulkTest.cppUnrollLoopTest.cppValueMapperTest.cppVFABIUtils.cpp)set_property(TARGET UtilsTests PROPERTY FOLDER "Tests/UnitTests/TransformsTests")
//===- BasicBlockUtils.cpp - Unit tests for BasicBlockUtils ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/BasicBlockUtils.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/BasicAliasAnalysis.h"#include "llvm/Analysis/BlockFrequencyInfo.h"#include "llvm/Analysis/BranchProbabilityInfo.h"#include "llvm/Analysis/CFG.h"#include "llvm/Analysis/DomTreeUpdater.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/MemorySSA.h"#include "llvm/Analysis/MemorySSAUpdater.h"#include "llvm/Analysis/PostDominators.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/BreakCriticalEdges.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("BasicBlockUtilsTests", errs());return Mod;}static BasicBlock *getBasicBlockByName(Function &F, StringRef Name) {for (BasicBlock &BB : F)if (BB.getName() == Name)return &BB;llvm_unreachable("Expected to find basic block!");}TEST(BasicBlockUtils, EliminateUnreachableBlocks) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define i32 @has_unreachable(i1 %cond) {entry:br i1 %cond, label %bb0, label %bb1bb0:br label %bb1bb1:%phi = phi i32 [ 0, %entry ], [ 1, %bb0 ]ret i32 %phibb2:ret i32 42})IR");Function *F = M->getFunction("has_unreachable");DominatorTree DT(*F);DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);EXPECT_EQ(F->size(), (size_t)4);bool Result = EliminateUnreachableBlocks(*F, &DTU);EXPECT_TRUE(Result);EXPECT_EQ(F->size(), (size_t)3);EXPECT_TRUE(DT.verify());}TEST(BasicBlockUtils, SplitEdge_ex1) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @foo(i1 %cond0) {entry:br i1 %cond0, label %bb0, label %bb1bb0:%0 = mul i32 1, 2br label %bb1bb1:br label %bb2bb2:ret void})IR");Function *F = M->getFunction("foo");DominatorTree DT(*F);BasicBlock *SrcBlock;BasicBlock *DestBlock;BasicBlock *NewBB;SrcBlock = getBasicBlockByName(*F, "entry");DestBlock = getBasicBlockByName(*F, "bb0");NewBB = SplitEdge(SrcBlock, DestBlock, &DT, nullptr, nullptr);EXPECT_TRUE(DT.verify());EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);EXPECT_EQ(NewBB->getParent(), F);bool BBFlag = false;for (BasicBlock &BB : *F) {if (BB.getName() == NewBB->getName()) {BBFlag = true;}}EXPECT_TRUE(BBFlag);}TEST(BasicBlockUtils, SplitEdge_ex2) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @foo() {bb0:br label %bb2bb1:br label %bb2bb2:ret void})IR");Function *F = M->getFunction("foo");DominatorTree DT(*F);BasicBlock *SrcBlock;BasicBlock *DestBlock;BasicBlock *NewBB;SrcBlock = getBasicBlockByName(*F, "bb0");DestBlock = getBasicBlockByName(*F, "bb2");NewBB = SplitEdge(SrcBlock, DestBlock, &DT, nullptr, nullptr);EXPECT_TRUE(DT.verify());EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);EXPECT_EQ(NewBB->getParent(), F);bool BBFlag = false;for (BasicBlock &BB : *F) {if (BB.getName() == NewBB->getName()) {BBFlag = true;}}EXPECT_TRUE(BBFlag);}TEST(BasicBlockUtils, SplitEdge_ex3) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define i32 @foo(i32 %n) {entry:br label %headerheader:%sum.02 = phi i32 [ 0, %entry ], [ %sum.1, %bb3 ]%0 = phi i32 [ 0, %entry ], [ %4, %bb3 ]%1 = icmp slt i32 %0, %nbr i1 %1, label %bb0, label %bb1bb0:%2 = add nsw i32 %sum.02, 2br label %bb2bb1:%3 = add nsw i32 %sum.02, 1br label %bb2bb2:%sum.1 = phi i32 [ %2, %bb0 ], [ %3, %bb1 ]br label %bb3bb3:%4 = add nsw i32 %0, 1%5 = icmp slt i32 %4, 100br i1 %5, label %header, label %bb4bb4:%sum.0.lcssa = phi i32 [ %sum.1, %bb3 ]ret i32 %sum.0.lcssa})IR");Function *F = M->getFunction("foo");DominatorTree DT(*F);LoopInfo LI(DT);DataLayout DL("e-i64:64-f80:128-n8:16:32:64-S128");TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AssumptionCache AC(*F);AAResults AA(TLI);BasicAAResult BAA(DL, *F, TLI, AC, &DT);AA.addAAResult(BAA);MemorySSA MSSA(*F, &AA, &DT);MemorySSAUpdater Updater(&MSSA);BasicBlock *SrcBlock;BasicBlock *DestBlock;BasicBlock *NewBB;SrcBlock = getBasicBlockByName(*F, "header");DestBlock = getBasicBlockByName(*F, "bb0");NewBB = SplitEdge(SrcBlock, DestBlock, &DT, &LI, &Updater);Updater.getMemorySSA()->verifyMemorySSA();EXPECT_TRUE(DT.verify());EXPECT_NE(LI.getLoopFor(SrcBlock), nullptr);EXPECT_NE(LI.getLoopFor(DestBlock), nullptr);EXPECT_NE(LI.getLoopFor(NewBB), nullptr);EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);EXPECT_EQ(NewBB->getParent(), F);bool BBFlag = false;for (BasicBlock &BB : *F) {if (BB.getName() == NewBB->getName()) {BBFlag = true;}}EXPECT_TRUE(BBFlag);}TEST(BasicBlockUtils, SplitEdge_ex4) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @bar(i32 %cond) personality i8 0 {entry:switch i32 %cond, label %exit [i32 -1, label %continuei32 0, label %continuei32 1, label %continue_alti32 2, label %continue_alt]exit:ret voidcontinue:invoke void @sink() to label %normal unwind label %exceptioncontinue_alt:invoke void @sink_alt() to label %normal unwind label %exceptionexception:%cleanup = landingpad i8 cleanupbr label %trivial-eh-handlertrivial-eh-handler:call void @sideeffect(i32 1)br label %normalnormal:call void @sideeffect(i32 0)ret void}declare void @sideeffect(i32)declare void @sink() colddeclare void @sink_alt() cold)IR");Function *F = M->getFunction("bar");DominatorTree DT(*F);LoopInfo LI(DT);TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AAResults AA(TLI);MemorySSA MSSA(*F, &AA, &DT);MemorySSAUpdater MSSAU(&MSSA);BasicBlock *SrcBlock;BasicBlock *DestBlock;SrcBlock = getBasicBlockByName(*F, "continue");DestBlock = getBasicBlockByName(*F, "exception");unsigned SuccNum = GetSuccessorNumber(SrcBlock, DestBlock);Instruction *LatchTerm = SrcBlock->getTerminator();const CriticalEdgeSplittingOptions Options =CriticalEdgeSplittingOptions(&DT, &LI, &MSSAU);// Check that the following edge is both critical and the destination block is// an exception block. These must be handled differently by SplitEdgebool CriticalEdge =isCriticalEdge(LatchTerm, SuccNum, Options.MergeIdenticalEdges);EXPECT_TRUE(CriticalEdge);bool Ehpad = DestBlock->isEHPad();EXPECT_TRUE(Ehpad);BasicBlock *NewBB = SplitEdge(SrcBlock, DestBlock, &DT, &LI, &MSSAU, "");MSSA.verifyMemorySSA();EXPECT_TRUE(DT.verify());EXPECT_NE(NewBB, nullptr);EXPECT_EQ(NewBB->getSinglePredecessor(), SrcBlock);EXPECT_EQ(NewBB, SrcBlock->getTerminator()->getSuccessor(SuccNum));EXPECT_EQ(NewBB->getParent(), F);bool BBFlag = false;for (BasicBlock &BB : *F) {if (BB.getName() == NewBB->getName()) {BBFlag = true;break;}}EXPECT_TRUE(BBFlag);}TEST(BasicBlockUtils, splitBasicBlockBefore_ex1) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @foo() {bb0:%0 = mul i32 1, 2br label %bb2bb1:br label %bb3bb2:%1 = phi i32 [ %0, %bb0 ]br label %bb3bb3:ret void})IR");Function *F = M->getFunction("foo");DominatorTree DT(*F);BasicBlock *DestBlock;BasicBlock *NewBB;DestBlock = getBasicBlockByName(*F, "bb2");NewBB = DestBlock->splitBasicBlockBefore(DestBlock->front().getIterator(),"test");PHINode *PN = dyn_cast<PHINode>(&(DestBlock->front()));EXPECT_EQ(PN->getIncomingBlock(0), NewBB);EXPECT_EQ(NewBB->getName(), "test");EXPECT_EQ(NewBB->getSingleSuccessor(), DestBlock);EXPECT_EQ(DestBlock->getSinglePredecessor(), NewBB);}#ifndef NDEBUGTEST(BasicBlockUtils, splitBasicBlockBefore_ex2) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @foo() {bb0:%0 = mul i32 1, 2br label %bb2bb1:br label %bb2bb2:%1 = phi i32 [ %0, %bb0 ], [ 1, %bb1 ]br label %bb3bb3:ret void})IR");Function *F = M->getFunction("foo");DominatorTree DT(*F);BasicBlock *DestBlock = getBasicBlockByName(*F, "bb2");ASSERT_DEATH({DestBlock->splitBasicBlockBefore(DestBlock->front().getIterator(),"test");},"cannot split on multi incoming phis");}#endifTEST(BasicBlockUtils, NoUnreachableBlocksToEliminate) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define i32 @no_unreachable(i1 %cond) {entry:br i1 %cond, label %bb0, label %bb1bb0:br label %bb1bb1:%phi = phi i32 [ 0, %entry ], [ 1, %bb0 ]ret i32 %phi})IR");Function *F = M->getFunction("no_unreachable");DominatorTree DT(*F);DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);EXPECT_EQ(F->size(), (size_t)3);bool Result = EliminateUnreachableBlocks(*F, &DTU);EXPECT_FALSE(Result);EXPECT_EQ(F->size(), (size_t)3);EXPECT_TRUE(DT.verify());}TEST(BasicBlockUtils, SplitBlockPredecessors) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define i32 @basic_func(i1 %cond) {entry:br i1 %cond, label %bb0, label %bb1bb0:br label %bb1bb1:%phi = phi i32 [ 0, %entry ], [ 1, %bb0 ]ret i32 %phi})IR");Function *F = M->getFunction("basic_func");DominatorTree DT(*F);// Make sure the dominator tree is properly updated if calling this on the// entry block.SplitBlockPredecessors(&F->getEntryBlock(), {}, "split.entry", &DT);EXPECT_TRUE(DT.verify());}TEST(BasicBlockUtils, SplitCriticalEdge) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @crit_edge(i1 %cond0, i1 %cond1) {entry:br i1 %cond0, label %bb0, label %bb1bb0:br label %bb1bb1:br label %bb2bb2:ret void})IR");Function *F = M->getFunction("crit_edge");DominatorTree DT(*F);PostDominatorTree PDT(*F);CriticalEdgeSplittingOptions CESO(&DT, nullptr, nullptr, &PDT);EXPECT_EQ(1u, SplitAllCriticalEdges(*F, CESO));EXPECT_TRUE(DT.verify());EXPECT_TRUE(PDT.verify());}TEST(BasicBlockUtils, SplitIndirectBrCriticalEdgesIgnorePHIs) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @crit_edge(i8* %tgt, i1 %cond0, i1 %cond1) {entry:indirectbr i8* %tgt, [label %bb0, label %bb1, label %bb2]bb0:br i1 %cond0, label %bb1, label %bb2bb1:%p = phi i32 [0, %bb0], [0, %entry]br i1 %cond1, label %bb3, label %bb4bb2:ret voidbb3:ret voidbb4:ret void})IR");Function *F = M->getFunction("crit_edge");DominatorTree DT(*F);LoopInfo LI(DT);BranchProbabilityInfo BPI(*F, LI);BlockFrequencyInfo BFI(*F, BPI, LI);ASSERT_TRUE(SplitIndirectBrCriticalEdges(*F, /*IgnoreBlocksWithoutPHI=*/true,&BPI, &BFI));// Check that successors of the split block get their probability correct.BasicBlock *BB1 = getBasicBlockByName(*F, "bb1");BasicBlock *SplitBB = BB1->getTerminator()->getSuccessor(0);ASSERT_EQ(2u, SplitBB->getTerminator()->getNumSuccessors());EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 0u));EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 1u));// bb2 has no PHI, so we shouldn't split bb0 -> bb2BasicBlock *BB0 = getBasicBlockByName(*F, "bb0");ASSERT_EQ(2u, BB0->getTerminator()->getNumSuccessors());EXPECT_EQ(BB0->getTerminator()->getSuccessor(1),getBasicBlockByName(*F, "bb2"));}TEST(BasicBlockUtils, SplitIndirectBrCriticalEdges) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @crit_edge(i8* %tgt, i1 %cond0, i1 %cond1) {entry:indirectbr i8* %tgt, [label %bb0, label %bb1, label %bb2]bb0:br i1 %cond0, label %bb1, label %bb2bb1:%p = phi i32 [0, %bb0], [0, %entry]br i1 %cond1, label %bb3, label %bb4bb2:ret voidbb3:ret voidbb4:ret void})IR");Function *F = M->getFunction("crit_edge");DominatorTree DT(*F);LoopInfo LI(DT);BranchProbabilityInfo BPI(*F, LI);BlockFrequencyInfo BFI(*F, BPI, LI);ASSERT_TRUE(SplitIndirectBrCriticalEdges(*F, /*IgnoreBlocksWithoutPHI=*/false,&BPI, &BFI));// Check that successors of the split block get their probability correct.BasicBlock *BB1 = getBasicBlockByName(*F, "bb1");BasicBlock *SplitBB = BB1->getTerminator()->getSuccessor(0);ASSERT_EQ(2u, SplitBB->getTerminator()->getNumSuccessors());EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 0u));EXPECT_EQ(BranchProbability(1, 2), BPI.getEdgeProbability(SplitBB, 1u));// Should split, resulting in:// bb0 -> bb2.clone; bb2 -> split1; bb2.clone -> split,BasicBlock *BB0 = getBasicBlockByName(*F, "bb0");ASSERT_EQ(2u, BB0->getTerminator()->getNumSuccessors());BasicBlock *BB2Clone = BB0->getTerminator()->getSuccessor(1);BasicBlock *BB2 = getBasicBlockByName(*F, "bb2");EXPECT_NE(BB2Clone, BB2);ASSERT_EQ(1u, BB2->getTerminator()->getNumSuccessors());ASSERT_EQ(1u, BB2Clone->getTerminator()->getNumSuccessors());EXPECT_EQ(BB2->getTerminator()->getSuccessor(0),BB2Clone->getTerminator()->getSuccessor(0));}TEST(BasicBlockUtils, SetEdgeProbability) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @edge_probability(i32 %0) {entry:switch i32 %0, label %LD [i32 700, label %L0i32 701, label %L1i32 702, label %L2i32 703, label %L3i32 704, label %L4i32 705, label %L5i32 706, label %L6i32 707, label %L7i32 708, label %L8i32 709, label %L9i32 710, label %L10i32 711, label %L11i32 712, label %L12i32 713, label %L13i32 714, label %L14i32 715, label %L15i32 716, label %L16i32 717, label %L17i32 718, label %L18i32 719, label %L19], !prof !{!"branch_weights", i32 1, i32 1, i32 1, i32 1, i32 1, i32 451, i32 1,i32 12, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1,i32 1, i32 1, i32 1, i32 1, i32 1}LD:unreachableL0:ret voidL1:ret voidL2:ret voidL3:ret voidL4:ret voidL5:ret voidL6:ret voidL7:ret voidL8:ret voidL9:ret voidL10:ret voidL11:ret voidL12:ret voidL13:ret voidL14:ret voidL15:ret voidL16:ret voidL17:ret voidL18:ret voidL19:ret void})IR");Function *F = M->getFunction("edge_probability");DominatorTree DT(*F);LoopInfo LI(DT);BranchProbabilityInfo BPI(*F, LI);// Check that the unreachable block has the minimal probability.const BasicBlock *EntryBB = getBasicBlockByName(*F, "entry");const BasicBlock *UnreachableBB = getBasicBlockByName(*F, "LD");EXPECT_EQ(BranchProbability::getRaw(1),BPI.getEdgeProbability(EntryBB, UnreachableBB));}
//===- ASanStackFrameLayoutTest.cpp - Tests for ComputeASanStackFrameLayout===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Utils/ASanStackFrameLayout.h"#include "llvm/ADT/ArrayRef.h"#include "gtest/gtest.h"#include <sstream>using namespace llvm;static std::stringShadowBytesToString(ArrayRef<uint8_t> ShadowBytes) {std::ostringstream os;for (size_t i = 0, n = ShadowBytes.size(); i < n; i++) {switch (ShadowBytes[i]) {case kAsanStackLeftRedzoneMagic: os << "L"; break;case kAsanStackRightRedzoneMagic: os << "R"; break;case kAsanStackMidRedzoneMagic: os << "M"; break;case kAsanStackUseAfterScopeMagic:os << "S";break;default: os << (unsigned)ShadowBytes[i];}}return os.str();}// Use macro to preserve line information in EXPECT_EQ output.#define TEST_LAYOUT(V, Granularity, MinHeaderSize, ExpectedDescr, \ExpectedShadow, ExpectedShadowAfterScope) \{ \SmallVector<ASanStackVariableDescription, 10> Vars = V; \ASanStackFrameLayout L = \ComputeASanStackFrameLayout(Vars, Granularity, MinHeaderSize); \EXPECT_STREQ(ExpectedDescr, \ComputeASanStackFrameDescription(Vars).c_str()); \EXPECT_EQ(ExpectedShadow, ShadowBytesToString(GetShadowBytes(Vars, L))); \EXPECT_EQ(ExpectedShadowAfterScope, \ShadowBytesToString(GetShadowBytesAfterScope(Vars, L))); \}TEST(ASanStackFrameLayout, Test) {#define VAR(name, size, lifetime, alignment, line) \ASanStackVariableDescription name##size##_##alignment = { \#name #size "_" #alignment, \size, \lifetime, \alignment, \0, \0, \line, \}VAR(a, 1, 0, 1, 0);VAR(p, 1, 0, 32, 15);VAR(p, 1, 0, 256, 2700);VAR(a, 2, 0, 1, 0);VAR(a, 3, 0, 1, 0);VAR(a, 4, 0, 1, 0);VAR(a, 7, 0, 1, 0);VAR(a, 8, 8, 1, 0);VAR(a, 9, 0, 1, 0);VAR(a, 16, 16, 1, 0);VAR(a, 41, 9, 1, 7);VAR(a, 105, 103, 1, 0);VAR(a, 200, 97, 1, 0);TEST_LAYOUT({a1_1}, 8, 16, "1 16 1 4 a1_1", "LL1R", "LL1R");TEST_LAYOUT({a1_1}, 16, 16, "1 16 1 4 a1_1", "L1R", "L1R");TEST_LAYOUT({a1_1}, 32, 32, "1 32 1 4 a1_1", "L1R", "L1R");TEST_LAYOUT({a1_1}, 64, 64, "1 64 1 4 a1_1", "L1R", "L1R");TEST_LAYOUT({p1_32}, 8, 32, "1 32 1 8 p1_32:15", "LLLL1RRR", "LLLL1RRR");TEST_LAYOUT({p1_32}, 8, 64, "1 64 1 8 p1_32:15", "LLLLLLLL1RRRRRRR","LLLLLLLL1RRRRRRR");TEST_LAYOUT({a1_1}, 8, 32, "1 32 1 4 a1_1", "LLLL1RRR", "LLLL1RRR");TEST_LAYOUT({a2_1}, 8, 32, "1 32 2 4 a2_1", "LLLL2RRR", "LLLL2RRR");TEST_LAYOUT({a3_1}, 8, 32, "1 32 3 4 a3_1", "LLLL3RRR", "LLLL3RRR");TEST_LAYOUT({a4_1}, 8, 32, "1 32 4 4 a4_1", "LLLL4RRR", "LLLL4RRR");TEST_LAYOUT({a7_1}, 8, 32, "1 32 7 4 a7_1", "LLLL7RRR", "LLLL7RRR");TEST_LAYOUT({a8_1}, 8, 32, "1 32 8 4 a8_1", "LLLL0RRR", "LLLLSRRR");TEST_LAYOUT({a9_1}, 8, 32, "1 32 9 4 a9_1", "LLLL01RR", "LLLL01RR");TEST_LAYOUT({a16_1}, 8, 32, "1 32 16 5 a16_1", "LLLL00RR", "LLLLSSRR");TEST_LAYOUT({p1_256}, 8, 32, "1 256 1 11 p1_256:2700","LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1RRR","LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1RRR");TEST_LAYOUT({a41_1}, 8, 32, "1 32 41 7 a41_1:7", "LLLL000001RRRRRR","LLLLSS0001RRRRRR");TEST_LAYOUT({a105_1}, 8, 32, "1 32 105 6 a105_1", "LLLL00000000000001RRRRRR","LLLLSSSSSSSSSSSSS1RRRRRR");{SmallVector<ASanStackVariableDescription, 10> t = {a1_1, p1_256};TEST_LAYOUT(t, 8, 32, "2 256 1 11 p1_256:2700 272 1 4 a1_1","LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1M1R","LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1M1R");}{SmallVector<ASanStackVariableDescription, 10> t = {a1_1, a16_1, a41_1};TEST_LAYOUT(t, 8, 32, "3 32 1 4 a1_1 48 16 5 a16_1 80 41 7 a41_1:7","LLLL1M00MM000001RRRR", "LLLL1MSSMMSS0001RRRR");}TEST_LAYOUT({a2_1}, 32, 32, "1 32 2 4 a2_1", "L2R", "L2R");TEST_LAYOUT({a9_1}, 32, 32, "1 32 9 4 a9_1", "L9R", "L9R");TEST_LAYOUT({a16_1}, 32, 32, "1 32 16 5 a16_1", "L16R", "LSR");TEST_LAYOUT({p1_256}, 32, 32, "1 256 1 11 p1_256:2700","LLLLLLLL1R", "LLLLLLLL1R");TEST_LAYOUT({a41_1}, 32, 32, "1 32 41 7 a41_1:7", "L09R","LS9R");TEST_LAYOUT({a105_1}, 32, 32, "1 32 105 6 a105_1", "L0009R","LSSSSR");TEST_LAYOUT({a200_1}, 32, 32, "1 32 200 6 a200_1", "L0000008RR","LSSSS008RR");{SmallVector<ASanStackVariableDescription, 10> t = {a1_1, p1_256};TEST_LAYOUT(t, 32, 32, "2 256 1 11 p1_256:2700 320 1 4 a1_1","LLLLLLLL1M1R", "LLLLLLLL1M1R");}{SmallVector<ASanStackVariableDescription, 10> t = {a1_1, a16_1, a41_1};TEST_LAYOUT(t, 32, 32, "3 32 1 4 a1_1 96 16 5 a16_1 160 41 7 a41_1:7","L1M16M09R", "L1MSMS9R");}#undef VAR#undef TEST_LAYOUT}
//===- llvm/unittest/Analysis/LoopPassManagerTest.cpp - LPM tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/Scalar/LoopPassManager.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/BlockFrequencyInfo.h"#include "llvm/Analysis/BranchProbabilityInfo.h"#include "llvm/Analysis/MemorySSA.h"#include "llvm/Analysis/PostDominators.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/Analysis/TargetTransformInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/PassManager.h"#include "llvm/Support/SourceMgr.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;namespace {using testing::DoDefault;using testing::Return;using testing::Expectation;using testing::Invoke;using testing::InvokeWithoutArgs;using testing::_;template <typename DerivedT, typename IRUnitT,typename AnalysisManagerT = AnalysisManager<IRUnitT>,typename... ExtraArgTs>class MockAnalysisHandleBase {public:class Analysis : public AnalysisInfoMixin<Analysis> {friend AnalysisInfoMixin<Analysis>;friend MockAnalysisHandleBase;static AnalysisKey Key;DerivedT *Handle;Analysis(DerivedT &Handle) : Handle(&Handle) {static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value,"Must pass the derived type to this template!");}public:class Result {friend MockAnalysisHandleBase;DerivedT *Handle;Result(DerivedT &Handle) : Handle(&Handle) {}public:// Forward invalidation events to the mock handle.bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA,typename AnalysisManagerT::Invalidator &Inv) {return Handle->invalidate(IR, PA, Inv);}};Result run(IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) {return Handle->run(IR, AM, ExtraArgs...);}};Analysis getAnalysis() { return Analysis(static_cast<DerivedT &>(*this)); }typename Analysis::Result getResult() {return typename Analysis::Result(static_cast<DerivedT &>(*this));}protected:// FIXME: MSVC seems unable to handle a lambda argument to Invoke from within// the template, so we use a boring static function.static bool invalidateCallback(IRUnitT &IR, const PreservedAnalyses &PA,typename AnalysisManagerT::Invalidator &Inv) {auto PAC = PA.template getChecker<Analysis>();return !PAC.preserved() &&!PAC.template preservedSet<AllAnalysesOn<IRUnitT>>();}/// Derived classes should call this in their constructor to set up default/// mock actions. (We can't do this in our constructor because this has to/// run after the DerivedT is constructed.)void setDefaults() {ON_CALL(static_cast<DerivedT &>(*this),run(_, _, testing::Matcher<ExtraArgTs>(_)...)).WillByDefault(Return(this->getResult()));ON_CALL(static_cast<DerivedT &>(*this), invalidate(_, _, _)).WillByDefault(Invoke(&invalidateCallback));}};template <typename DerivedT, typename IRUnitT, typename AnalysisManagerT,typename... ExtraArgTs>AnalysisKey MockAnalysisHandleBase<DerivedT, IRUnitT, AnalysisManagerT,ExtraArgTs...>::Analysis::Key;/// Mock handle for loop analyses.////// This is provided as a template accepting an (optional) integer. Because/// analyses are identified and queried by type, this allows constructing/// multiple handles with distinctly typed nested 'Analysis' types that can be/// registered and queried. If you want to register multiple loop analysis/// passes, you'll need to instantiate this type with different values for I./// For example:////// MockLoopAnalysisHandleTemplate<0> h0;/// MockLoopAnalysisHandleTemplate<1> h1;/// typedef decltype(h0)::Analysis Analysis0;/// typedef decltype(h1)::Analysis Analysis1;template <size_t I = static_cast<size_t>(-1)>struct MockLoopAnalysisHandleTemplate: MockAnalysisHandleBase<MockLoopAnalysisHandleTemplate<I>, Loop,LoopAnalysisManager,LoopStandardAnalysisResults &> {typedef typename MockLoopAnalysisHandleTemplate::Analysis Analysis;MOCK_METHOD3_T(run, typename Analysis::Result(Loop &, LoopAnalysisManager &,LoopStandardAnalysisResults &));MOCK_METHOD3_T(invalidate, bool(Loop &, const PreservedAnalyses &,LoopAnalysisManager::Invalidator &));MockLoopAnalysisHandleTemplate() { this->setDefaults(); }};typedef MockLoopAnalysisHandleTemplate<> MockLoopAnalysisHandle;struct MockFunctionAnalysisHandle: MockAnalysisHandleBase<MockFunctionAnalysisHandle, Function> {MOCK_METHOD2(run, Analysis::Result(Function &, FunctionAnalysisManager &));MOCK_METHOD3(invalidate, bool(Function &, const PreservedAnalyses &,FunctionAnalysisManager::Invalidator &));MockFunctionAnalysisHandle() { setDefaults(); }};template <typename DerivedT, typename IRUnitT,typename AnalysisManagerT = AnalysisManager<IRUnitT>,typename... ExtraArgTs>class MockPassHandleBase {public:class Pass : public PassInfoMixin<Pass> {friend MockPassHandleBase;DerivedT *Handle;Pass(DerivedT &Handle) : Handle(&Handle) {static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value,"Must pass the derived type to this template!");}public:PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,ExtraArgTs... ExtraArgs) {return Handle->run(IR, AM, ExtraArgs...);}};Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); }protected:/// Derived classes should call this in their constructor to set up default/// mock actions. (We can't do this in our constructor because this has to/// run after the DerivedT is constructed.)void setDefaults() {ON_CALL(static_cast<DerivedT &>(*this),run(_, _, testing::Matcher<ExtraArgTs>(_)...)).WillByDefault(Return(PreservedAnalyses::all()));}};struct MockLoopPassHandle: MockPassHandleBase<MockLoopPassHandle, Loop, LoopAnalysisManager,LoopStandardAnalysisResults &, LPMUpdater &> {MOCK_METHOD4(run,PreservedAnalyses(Loop &, LoopAnalysisManager &,LoopStandardAnalysisResults &, LPMUpdater &));MockLoopPassHandle() { setDefaults(); }};struct MockLoopNestPassHandle: MockPassHandleBase<MockLoopNestPassHandle, LoopNest, LoopAnalysisManager,LoopStandardAnalysisResults &, LPMUpdater &> {MOCK_METHOD4(run,PreservedAnalyses(LoopNest &, LoopAnalysisManager &,LoopStandardAnalysisResults &, LPMUpdater &));MockLoopNestPassHandle() { setDefaults(); }};struct MockFunctionPassHandle: MockPassHandleBase<MockFunctionPassHandle, Function> {MOCK_METHOD2(run, PreservedAnalyses(Function &, FunctionAnalysisManager &));MockFunctionPassHandle() { setDefaults(); }};struct MockModulePassHandle : MockPassHandleBase<MockModulePassHandle, Module> {MOCK_METHOD2(run, PreservedAnalyses(Module &, ModuleAnalysisManager &));MockModulePassHandle() { setDefaults(); }};/// Define a custom matcher for objects which support a 'getName' method/// returning a StringRef.////// LLVM often has IR objects or analysis objects which expose a StringRef name/// and in tests it is convenient to match these by name for readability. This/// matcher supports any type exposing a getName() method of this form.////// It should be used as:////// HasName("my_function")////// No namespace or other qualification is required.MATCHER_P(HasName, Name, "") {// The matcher's name and argument are printed in the case of failure, but we// also want to print out the name of the argument. This uses an implicitly// avaiable std::ostream, so we have to construct a std::string.*result_listener << "has name '" << arg.getName().str() << "'";return Name == arg.getName();}std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;return parseAssemblyString(IR, Err, C);}class LoopPassManagerTest : public ::testing::Test {protected:LLVMContext Context;std::unique_ptr<Module> M;LoopAnalysisManager LAM;FunctionAnalysisManager FAM;ModuleAnalysisManager MAM;MockLoopAnalysisHandle MLAHandle;MockLoopPassHandle MLPHandle;MockLoopNestPassHandle MLNPHandle;MockFunctionPassHandle MFPHandle;MockModulePassHandle MMPHandle;static PreservedAnalysesgetLoopAnalysisResult(Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &) {(void)AM.getResult<MockLoopAnalysisHandle::Analysis>(L, AR);return PreservedAnalyses::all();};public:LoopPassManagerTest(): M(parseIR(Context,"define void @f(i1* %ptr) {\n""entry:\n"" br label %loop.0\n""loop.0:\n"" %cond.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0, label %loop.0.0.ph, label %end\n""loop.0.0.ph:\n"" br label %loop.0.0\n""loop.0.0:\n"" %cond.0.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n""loop.0.1.ph:\n"" br label %loop.0.1\n""loop.0.1:\n"" %cond.0.1 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.1, label %loop.0.1, label %loop.0.latch\n""loop.0.latch:\n"" br label %loop.0\n""end:\n"" ret void\n""}\n""\n""define void @g(i1* %ptr) {\n""entry:\n"" br label %loop.g.0\n""loop.g.0:\n"" %cond.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0, label %loop.g.0, label %end\n""end:\n"" ret void\n""}\n")),LAM(), FAM(), MAM() {// Register our mock analysis.LAM.registerPass([&] { return MLAHandle.getAnalysis(); });// We need DominatorTreeAnalysis for LoopAnalysis.FAM.registerPass([&] { return DominatorTreeAnalysis(); });FAM.registerPass([&] { return LoopAnalysis(); });// We also allow loop passes to assume a set of other analyses and so need// those.FAM.registerPass([&] { return AAManager(); });FAM.registerPass([&] { return AssumptionAnalysis(); });FAM.registerPass([&] { return BlockFrequencyAnalysis(); });FAM.registerPass([&] { return BranchProbabilityAnalysis(); });FAM.registerPass([&] { return PostDominatorTreeAnalysis(); });FAM.registerPass([&] { return MemorySSAAnalysis(); });FAM.registerPass([&] { return ScalarEvolutionAnalysis(); });FAM.registerPass([&] { return TargetLibraryAnalysis(); });FAM.registerPass([&] { return TargetIRAnalysis(); });// Register required pass instrumentation analysis.LAM.registerPass([&] { return PassInstrumentationAnalysis(); });FAM.registerPass([&] { return PassInstrumentationAnalysis(); });MAM.registerPass([&] { return PassInstrumentationAnalysis(); });// Cross-register proxies.LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); });FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); });FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });}};TEST_F(LoopPassManagerTest, Basic) {ModulePassManager MPM;::testing::InSequence MakeExpectationsSequenced;// First we just visit all the loops in all the functions and get their// analysis results. This will run the analysis a total of four times,// once for each loop.EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));// Wire the loop pass through pass managers into the module pipeline.{LoopPassManager LPM;LPM.addPass(MLPHandle.getPass());FunctionPassManager FPM;FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));}// Next we run two passes over the loops. The first one invalidates the// analyses for one loop, the second ones try to get the analysis results.// This should force only one analysis to re-run within the loop PM, but will// also invalidate everything after the loop pass manager finishes.EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(DoDefault()).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(InvokeWithoutArgs([] { return PreservedAnalyses::none(); })).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(DoDefault()).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)).WillOnce(DoDefault()).WillOnce(Invoke(getLoopAnalysisResult));// Wire two loop pass runs into the module pipeline.{LoopPassManager LPM;LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());FunctionPassManager FPM;FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));}// And now run the pipeline across the module.MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, FunctionPassInvalidationOfLoopAnalyses) {ModulePassManager MPM;FunctionPassManager FPM;// We process each function completely in sequence.::testing::Sequence FSequence, GSequence;// First, force the analysis result to be computed for each loop.EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _)).InSequence(FSequence).WillOnce(DoDefault());EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _)).InSequence(FSequence).WillOnce(DoDefault());EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _)).InSequence(FSequence).WillOnce(DoDefault());EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _)).InSequence(GSequence).WillOnce(DoDefault());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));// No need to re-run if we require again from a fresh loop pass manager.FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));// For 'f', preserve most things but not the specific loop analyses.auto PA = getLoopPassPreservedAnalyses();PA.preserve<MemorySSAAnalysis>();EXPECT_CALL(MFPHandle, run(HasName("f"), _)).InSequence(FSequence).WillOnce(Return(PA));EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.0"), _, _)).InSequence(FSequence).WillOnce(DoDefault());// On one loop, skip the invalidation (as though we did an internal update).EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.1"), _, _)).InSequence(FSequence).WillOnce(Return(false));EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0"), _, _)).InSequence(FSequence).WillOnce(DoDefault());// Now two loops still have to be recomputed.EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _)).InSequence(FSequence).WillOnce(DoDefault());EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _)).InSequence(FSequence).WillOnce(DoDefault());// Preserve things in the second function to ensure invalidation remains// isolated to one function.EXPECT_CALL(MFPHandle, run(HasName("g"), _)).InSequence(GSequence).WillOnce(DoDefault());FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));EXPECT_CALL(MFPHandle, run(HasName("f"), _)).InSequence(FSequence).WillOnce(DoDefault());// For 'g', fail to preserve anything, causing the loops themselves to be// cleared. We don't get an invalidation event here as the loop is gone, but// we should still have to recompute the analysis.EXPECT_CALL(MFPHandle, run(HasName("g"), _)).InSequence(GSequence).WillOnce(Return(PreservedAnalyses::none()));EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _)).InSequence(GSequence).WillOnce(DoDefault());FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));// Verify with a separate function pass run that we didn't mess up 'f's// cache. No analysis runs should be necessary here.MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, ModulePassInvalidationOfLoopAnalyses) {ModulePassManager MPM;::testing::InSequence MakeExpectationsSequenced;// First, force the analysis result to be computed for each loop.EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));// Walking all the way out and all the way back in doesn't re-run the// analysis.MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));// But a module pass that doesn't preserve the actual mock loop analysis// invalidates all the way down and forces recomputing.EXPECT_CALL(MMPHandle, run(_, _)).WillOnce(InvokeWithoutArgs([] {auto PA = getLoopPassPreservedAnalyses();PA.preserve<FunctionAnalysisManagerModuleProxy>();PA.preserve<MemorySSAAnalysis>();return PA;}));// All the loop analyses from both functions get invalidated before we// recompute anything.EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.0"), _, _));// On one loop, again skip the invalidation (as though we did an internal// update).EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.1"), _, _)).WillOnce(Return(false));EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0"), _, _));EXPECT_CALL(MLAHandle, invalidate(HasName("loop.g.0"), _, _));// Now all but one of the loops gets re-analyzed.EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));MPM.addPass(MMPHandle.getPass());MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));// Verify that the cached values persist.MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));// Now we fail to preserve the loop analysis and observe that the loop// analyses are cleared (so no invalidation event) as the loops themselves// are no longer valid.EXPECT_CALL(MMPHandle, run(_, _)).WillOnce(InvokeWithoutArgs([] {auto PA = PreservedAnalyses::none();PA.preserve<FunctionAnalysisManagerModuleProxy>();return PA;}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));MPM.addPass(MMPHandle.getPass());MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));// Verify that the cached values persist.MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));// Next, check that even if we preserve everything within the function itelf,// if the function's module pass proxy isn't preserved and the potential set// of functions changes, the clear reaches the loop analyses as well. This// will again trigger re-runs but not invalidation events.EXPECT_CALL(MMPHandle, run(_, _)).WillOnce(InvokeWithoutArgs([] {auto PA = PreservedAnalyses::none();PA.preserveSet<AllAnalysesOn<Function>>();PA.preserveSet<AllAnalysesOn<Loop>>();return PA;}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));MPM.addPass(MMPHandle.getPass());MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));MPM.run(*M, MAM);}// Test that if any of the bundled analyses provided in the LPM's signature// become invalid, the analysis proxy itself becomes invalid and we clear all// loop analysis results.TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) {ModulePassManager MPM;FunctionPassManager FPM;::testing::InSequence MakeExpectationsSequenced;// First, force the analysis result to be computed for each loop.EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));// No need to re-run if we require again from a fresh loop pass manager.FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));// Preserving everything but the loop analyses themselves results in// invalidation and running.EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(Return(getLoopPassPreservedAnalyses()));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));// The rest don't invalidate analyses, they only trigger re-runs because we// clear the cache completely.EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {auto PA = PreservedAnalyses::none();// Not preserving `AAManager`.PA.preserve<DominatorTreeAnalysis>();PA.preserve<LoopAnalysis>();PA.preserve<LoopAnalysisManagerFunctionProxy>();PA.preserve<ScalarEvolutionAnalysis>();return PA;}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {auto PA = PreservedAnalyses::none();// Not preserving `DominatorTreeAnalysis`.PA.preserve<LoopAnalysis>();PA.preserve<LoopAnalysisManagerFunctionProxy>();PA.preserve<ScalarEvolutionAnalysis>();return PA;}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {auto PA = PreservedAnalyses::none();PA.preserve<DominatorTreeAnalysis>();// Not preserving the `LoopAnalysis`.PA.preserve<LoopAnalysisManagerFunctionProxy>();PA.preserve<ScalarEvolutionAnalysis>();return PA;}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {auto PA = PreservedAnalyses::none();PA.preserve<DominatorTreeAnalysis>();PA.preserve<LoopAnalysis>();// Not preserving the `LoopAnalysisManagerFunctionProxy`.PA.preserve<ScalarEvolutionAnalysis>();return PA;}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {auto PA = PreservedAnalyses::none();PA.preserve<DominatorTreeAnalysis>();PA.preserve<LoopAnalysis>();PA.preserve<LoopAnalysisManagerFunctionProxy>();// Not preserving `ScalarEvolutionAnalysis`.return PA;}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));// After all the churn on 'f', we'll compute the loop analysis results for// 'g' once with a requires pass and then run our mock pass over g a bunch// but just get cached results each time.EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));EXPECT_CALL(MFPHandle, run(HasName("g"), _)).Times(6);MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, IndirectInvalidation) {// We need two distinct analysis types and handles.enum { A, B };MockLoopAnalysisHandleTemplate<A> MLAHandleA;MockLoopAnalysisHandleTemplate<B> MLAHandleB;LAM.registerPass([&] { return MLAHandleA.getAnalysis(); });LAM.registerPass([&] { return MLAHandleB.getAnalysis(); });typedef decltype(MLAHandleA)::Analysis AnalysisA;typedef decltype(MLAHandleB)::Analysis AnalysisB;// Set up AnalysisA to depend on our AnalysisB. For testing purposes we just// need to get the AnalysisB results in AnalysisA's run method and check if// AnalysisB gets invalidated in AnalysisA's invalidate method.ON_CALL(MLAHandleA, run(_, _, _)).WillByDefault(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR) {(void)AM.getResult<AnalysisB>(L, AR);return MLAHandleA.getResult();}));ON_CALL(MLAHandleA, invalidate(_, _, _)).WillByDefault(Invoke([](Loop &L, const PreservedAnalyses &PA,LoopAnalysisManager::Invalidator &Inv) {auto PAC = PA.getChecker<AnalysisA>();return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Loop>>()) ||Inv.invalidate<AnalysisB>(L, PA);}));::testing::InSequence MakeExpectationsSequenced;// Compute the analyses across all of 'f' first.EXPECT_CALL(MLAHandleA, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandleB, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandleA, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandleB, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandleA, run(HasName("loop.0"), _, _));EXPECT_CALL(MLAHandleB, run(HasName("loop.0"), _, _));// Now we invalidate AnalysisB (but not AnalysisA) for one of the loops and// preserve everything for the rest. This in turn triggers that one loop to// recompute both AnalysisB *and* AnalysisA if indirect invalidation is// working.EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(InvokeWithoutArgs([] {auto PA = getLoopPassPreservedAnalyses();// Specifically preserve AnalysisA so that it would survive if it// didn't depend on AnalysisB.PA.preserve<AnalysisA>();return PA;}));// It happens that AnalysisB is invalidated first. That shouldn't matter// though, and we should still call AnalysisA's invalidation.EXPECT_CALL(MLAHandleB, invalidate(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandleA, invalidate(HasName("loop.0.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke([](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &) {(void)AM.getResult<AnalysisA>(L, AR);return PreservedAnalyses::all();}));EXPECT_CALL(MLAHandleA, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandleB, run(HasName("loop.0.0"), _, _));// The rest of the loops should run and get cached results.EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).Times(2).WillRepeatedly(Invoke([](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &) {(void)AM.getResult<AnalysisA>(L, AR);return PreservedAnalyses::all();}));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).Times(2).WillRepeatedly(Invoke([](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &) {(void)AM.getResult<AnalysisA>(L, AR);return PreservedAnalyses::all();}));// The run over 'g' should be boring, with us just computing the analyses once// up front and then running loop passes and getting cached results.EXPECT_CALL(MLAHandleA, run(HasName("loop.g.0"), _, _));EXPECT_CALL(MLAHandleB, run(HasName("loop.g.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)).Times(2).WillRepeatedly(Invoke([](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &) {(void)AM.getResult<AnalysisA>(L, AR);return PreservedAnalyses::all();}));// Build the pipeline and run it.ModulePassManager MPM;FunctionPassManager FPM;FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<AnalysisA>()));LoopPassManager LPM;LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, IndirectOuterPassInvalidation) {typedef decltype(MLAHandle)::Analysis LoopAnalysis;MockFunctionAnalysisHandle MFAHandle;FAM.registerPass([&] { return MFAHandle.getAnalysis(); });typedef decltype(MFAHandle)::Analysis FunctionAnalysis;// Set up the loop analysis to depend on both the function and module// analysis.ON_CALL(MLAHandle, run(_, _, _)).WillByDefault(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR) {auto &FAMP = AM.getResult<FunctionAnalysisManagerLoopProxy>(L, AR);Function &F = *L.getHeader()->getParent();// This call will assert when trying to get the actual analysis if the// FunctionAnalysis can be invalidated. Only check its existence.// Alternatively, use FAM above, for the purposes of this unittest.if (FAMP.cachedResultExists<FunctionAnalysis>(F))FAMP.registerOuterAnalysisInvalidation<FunctionAnalysis,LoopAnalysis>();return MLAHandle.getResult();}));::testing::InSequence MakeExpectationsSequenced;// Compute the analyses across all of 'f' first.EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(Invoke([](Function &F, FunctionAnalysisManager &AM) {// Force the computing of the function analysis so it is available in// this function.(void)AM.getResult<FunctionAnalysis>(F);return PreservedAnalyses::all();}));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));// Now invalidate the function analysis but preserve the loop analyses.// This should trigger immediate invalidation of the loop analyses, despite// the fact that they were preserved.EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {auto PA = getLoopPassPreservedAnalyses();PA.preserve<MemorySSAAnalysis>();PA.preserveSet<AllAnalysesOn<Loop>>();return PA;}));EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0"), _, _));// And re-running a requires pass recomputes them.EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));// When we run over 'g' we don't populate the cache with the function// analysis.EXPECT_CALL(MFPHandle, run(HasName("g"), _)).WillOnce(Return(PreservedAnalyses::all()));EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));// Which means that no extra invalidation occurs and cached values are used.EXPECT_CALL(MFPHandle, run(HasName("g"), _)).WillOnce(InvokeWithoutArgs([] {auto PA = getLoopPassPreservedAnalyses();PA.preserve<MemorySSAAnalysis>();PA.preserveSet<AllAnalysesOn<Loop>>();return PA;}));// Build the pipeline and run it.ModulePassManager MPM;FunctionPassManager FPM;FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<LoopAnalysis>()));FPM.addPass(MFPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<LoopAnalysis>()));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, LoopChildInsertion) {// Super boring module with three loops in a single loop nest.M = parseIR(Context, "define void @f(i1* %ptr) {\n""entry:\n"" br label %loop.0\n""loop.0:\n"" %cond.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0, label %loop.0.0.ph, label %end\n""loop.0.0.ph:\n"" br label %loop.0.0\n""loop.0.0:\n"" %cond.0.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n""loop.0.1.ph:\n"" br label %loop.0.1\n""loop.0.1:\n"" %cond.0.1 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n""loop.0.2.ph:\n"" br label %loop.0.2\n""loop.0.2:\n"" %cond.0.2 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n""loop.0.latch:\n"" br label %loop.0\n""end:\n"" ret void\n""}\n");// Build up variables referring into the IR so we can rewrite it below// easily.Function &F = *M->begin();ASSERT_THAT(F, HasName("f"));Argument &Ptr = *F.arg_begin();auto BBI = F.begin();BasicBlock &EntryBB = *BBI++;ASSERT_THAT(EntryBB, HasName("entry"));BasicBlock &Loop0BB = *BBI++;ASSERT_THAT(Loop0BB, HasName("loop.0"));BasicBlock &Loop00PHBB = *BBI++;ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph"));BasicBlock &Loop00BB = *BBI++;ASSERT_THAT(Loop00BB, HasName("loop.0.0"));BasicBlock &Loop01PHBB = *BBI++;ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph"));BasicBlock &Loop01BB = *BBI++;ASSERT_THAT(Loop01BB, HasName("loop.0.1"));BasicBlock &Loop02PHBB = *BBI++;ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph"));BasicBlock &Loop02BB = *BBI++;ASSERT_THAT(Loop02BB, HasName("loop.0.2"));BasicBlock &Loop0LatchBB = *BBI++;ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch"));BasicBlock &EndBB = *BBI++;ASSERT_THAT(EndBB, HasName("end"));ASSERT_THAT(BBI, F.end());auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB,const char *Name, BasicBlock *BB) {auto *Cond = new LoadInst(Type::getInt1Ty(Context), &Ptr, Name,/*isVolatile*/ true, BB);BranchInst::Create(TrueBB, FalseBB, Cond, BB);};// Build the pass managers and register our pipeline. We build a single loop// pass pipeline consisting of three mock pass runs over each loop. After// this we run both domtree and loop verification passes to make sure that// the IR remained valid during our mutations.ModulePassManager MPM;FunctionPassManager FPM;LoopPassManager LPM;LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));FPM.addPass(DominatorTreeVerifierPass());FPM.addPass(LoopVerifierPass());MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));// All the visit orders are deterministic, so we use simple fully order// expectations.::testing::InSequence MakeExpectationsSequenced;// We run loop passes three times over each of the loops.EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));// When running over the middle loop, the second run inserts two new child// loops, inserting them and itself into the worklist.BasicBlock *NewLoop010BB, *NewLoop01LatchBB;EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR,LPMUpdater &Updater) {auto *NewLoop = AR.LI.AllocateLoop();L.addChildLoop(NewLoop);auto *NewLoop010PHBB =BasicBlock::Create(Context, "loop.0.1.0.ph", &F, &Loop02PHBB);NewLoop010BB =BasicBlock::Create(Context, "loop.0.1.0", &F, &Loop02PHBB);NewLoop01LatchBB =BasicBlock::Create(Context, "loop.0.1.latch", &F, &Loop02PHBB);Loop01BB.getTerminator()->replaceUsesOfWith(&Loop01BB, NewLoop010PHBB);BranchInst::Create(NewLoop010BB, NewLoop010PHBB);CreateCondBr(NewLoop01LatchBB, NewLoop010BB, "cond.0.1.0",NewLoop010BB);BranchInst::Create(&Loop01BB, NewLoop01LatchBB);AR.DT.addNewBlock(NewLoop010PHBB, &Loop01BB);AR.DT.addNewBlock(NewLoop010BB, NewLoop010PHBB);AR.DT.addNewBlock(NewLoop01LatchBB, NewLoop010BB);EXPECT_TRUE(AR.DT.verify());L.addBasicBlockToLoop(NewLoop010PHBB, AR.LI);NewLoop->addBasicBlockToLoop(NewLoop010BB, AR.LI);L.addBasicBlockToLoop(NewLoop01LatchBB, AR.LI);NewLoop->verifyLoop();L.verifyLoop();Updater.addChildLoops({NewLoop});return PreservedAnalyses::all();}));// We should immediately drop down to fully visit the new inner loop.EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.0"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));// After visiting the inner loop, we should re-visit the second loop// reflecting its new loop nest structure.EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));// In the second run over the middle loop after we've visited the new child,// we add another child to check that we can repeatedly add children, and add// children to a loop that already has children.EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR,LPMUpdater &Updater) {auto *NewLoop = AR.LI.AllocateLoop();L.addChildLoop(NewLoop);auto *NewLoop011PHBB = BasicBlock::Create(Context, "loop.0.1.1.ph", &F, NewLoop01LatchBB);auto *NewLoop011BB = BasicBlock::Create(Context, "loop.0.1.1", &F, NewLoop01LatchBB);NewLoop010BB->getTerminator()->replaceUsesOfWith(NewLoop01LatchBB,NewLoop011PHBB);BranchInst::Create(NewLoop011BB, NewLoop011PHBB);CreateCondBr(NewLoop01LatchBB, NewLoop011BB, "cond.0.1.1",NewLoop011BB);AR.DT.addNewBlock(NewLoop011PHBB, NewLoop010BB);auto *NewDTNode = AR.DT.addNewBlock(NewLoop011BB, NewLoop011PHBB);AR.DT.changeImmediateDominator(AR.DT[NewLoop01LatchBB], NewDTNode);EXPECT_TRUE(AR.DT.verify());L.addBasicBlockToLoop(NewLoop011PHBB, AR.LI);NewLoop->addBasicBlockToLoop(NewLoop011BB, AR.LI);NewLoop->verifyLoop();L.verifyLoop();Updater.addChildLoops({NewLoop});return PreservedAnalyses::all();}));// Again, we should immediately drop down to visit the new, unvisited child// loop. We don't need to revisit the other child though.EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.1"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1.1"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.1"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));// And now we should pop back up to the second loop and do a full pipeline of// three passes on its current form.EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).Times(3).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.2"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));// Now that all the expected actions are registered, run the pipeline over// our module. All of our expectations are verified when the test finishes.MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, LoopPeerInsertion) {// Super boring module with two loop nests and loop nest with two child// loops.M = parseIR(Context, "define void @f(i1* %ptr) {\n""entry:\n"" br label %loop.0\n""loop.0:\n"" %cond.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0, label %loop.0.0.ph, label %loop.2.ph\n""loop.0.0.ph:\n"" br label %loop.0.0\n""loop.0.0:\n"" %cond.0.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.0, label %loop.0.0, label %loop.0.2.ph\n""loop.0.2.ph:\n"" br label %loop.0.2\n""loop.0.2:\n"" %cond.0.2 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n""loop.0.latch:\n"" br label %loop.0\n""loop.2.ph:\n"" br label %loop.2\n""loop.2:\n"" %cond.2 = load volatile i1, i1* %ptr\n"" br i1 %cond.2, label %loop.2, label %end\n""end:\n"" ret void\n""}\n");// Build up variables referring into the IR so we can rewrite it below// easily.Function &F = *M->begin();ASSERT_THAT(F, HasName("f"));Argument &Ptr = *F.arg_begin();auto BBI = F.begin();BasicBlock &EntryBB = *BBI++;ASSERT_THAT(EntryBB, HasName("entry"));BasicBlock &Loop0BB = *BBI++;ASSERT_THAT(Loop0BB, HasName("loop.0"));BasicBlock &Loop00PHBB = *BBI++;ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph"));BasicBlock &Loop00BB = *BBI++;ASSERT_THAT(Loop00BB, HasName("loop.0.0"));BasicBlock &Loop02PHBB = *BBI++;ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph"));BasicBlock &Loop02BB = *BBI++;ASSERT_THAT(Loop02BB, HasName("loop.0.2"));BasicBlock &Loop0LatchBB = *BBI++;ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch"));BasicBlock &Loop2PHBB = *BBI++;ASSERT_THAT(Loop2PHBB, HasName("loop.2.ph"));BasicBlock &Loop2BB = *BBI++;ASSERT_THAT(Loop2BB, HasName("loop.2"));BasicBlock &EndBB = *BBI++;ASSERT_THAT(EndBB, HasName("end"));ASSERT_THAT(BBI, F.end());auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB,const char *Name, BasicBlock *BB) {auto *Cond = new LoadInst(Type::getInt1Ty(Context), &Ptr, Name,/*isVolatile*/ true, BB);BranchInst::Create(TrueBB, FalseBB, Cond, BB);};// Build the pass managers and register our pipeline. We build a single loop// pass pipeline consisting of three mock pass runs over each loop. After// this we run both domtree and loop verification passes to make sure that// the IR remained valid during our mutations.ModulePassManager MPM;FunctionPassManager FPM;LoopPassManager LPM;LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));FPM.addPass(DominatorTreeVerifierPass());FPM.addPass(LoopVerifierPass());MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));// All the visit orders are deterministic, so we use simple fully order// expectations.::testing::InSequence MakeExpectationsSequenced;// We run loop passes three times over each of the loops.EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));// On the second run, we insert a sibling loop.EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR,LPMUpdater &Updater) {auto *NewLoop = AR.LI.AllocateLoop();L.getParentLoop()->addChildLoop(NewLoop);auto *NewLoop01PHBB = BasicBlock::Create(Context, "loop.0.1.ph", &F, &Loop02PHBB);auto *NewLoop01BB = BasicBlock::Create(Context, "loop.0.1", &F, &Loop02PHBB);BranchInst::Create(NewLoop01BB, NewLoop01PHBB);CreateCondBr(&Loop02PHBB, NewLoop01BB, "cond.0.1", NewLoop01BB);Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02PHBB, NewLoop01PHBB);AR.DT.addNewBlock(NewLoop01PHBB, &Loop00BB);auto *NewDTNode = AR.DT.addNewBlock(NewLoop01BB, NewLoop01PHBB);AR.DT.changeImmediateDominator(AR.DT[&Loop02PHBB], NewDTNode);EXPECT_TRUE(AR.DT.verify());L.getParentLoop()->addBasicBlockToLoop(NewLoop01PHBB, AR.LI);NewLoop->addBasicBlockToLoop(NewLoop01BB, AR.LI);L.getParentLoop()->verifyLoop();Updater.addSiblingLoops({NewLoop});return PreservedAnalyses::all();}));// We finish processing this loop as sibling loops don't perturb the// postorder walk.EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));// We visit the inserted sibling next.EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.2"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));// Next, on the third pass run on the last inner loop we add more new// siblings, more than one, and one with nested child loops. By doing this at// the end we make sure that edge case works well.EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR,LPMUpdater &Updater) {Loop *NewLoops[] = {AR.LI.AllocateLoop(), AR.LI.AllocateLoop(),AR.LI.AllocateLoop()};L.getParentLoop()->addChildLoop(NewLoops[0]);L.getParentLoop()->addChildLoop(NewLoops[1]);NewLoops[1]->addChildLoop(NewLoops[2]);auto *NewLoop03PHBB =BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB);auto *NewLoop03BB =BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB);auto *NewLoop04PHBB =BasicBlock::Create(Context, "loop.0.4.ph", &F, &Loop0LatchBB);auto *NewLoop04BB =BasicBlock::Create(Context, "loop.0.4", &F, &Loop0LatchBB);auto *NewLoop040PHBB =BasicBlock::Create(Context, "loop.0.4.0.ph", &F, &Loop0LatchBB);auto *NewLoop040BB =BasicBlock::Create(Context, "loop.0.4.0", &F, &Loop0LatchBB);auto *NewLoop04LatchBB =BasicBlock::Create(Context, "loop.0.4.latch", &F, &Loop0LatchBB);Loop02BB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB, NewLoop03PHBB);BranchInst::Create(NewLoop03BB, NewLoop03PHBB);CreateCondBr(NewLoop04PHBB, NewLoop03BB, "cond.0.3", NewLoop03BB);BranchInst::Create(NewLoop04BB, NewLoop04PHBB);CreateCondBr(&Loop0LatchBB, NewLoop040PHBB, "cond.0.4", NewLoop04BB);BranchInst::Create(NewLoop040BB, NewLoop040PHBB);CreateCondBr(NewLoop04LatchBB, NewLoop040BB, "cond.0.4.0", NewLoop040BB);BranchInst::Create(NewLoop04BB, NewLoop04LatchBB);AR.DT.addNewBlock(NewLoop03PHBB, &Loop02BB);AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB);AR.DT.addNewBlock(NewLoop04PHBB, NewLoop03BB);auto *NewDTNode = AR.DT.addNewBlock(NewLoop04BB, NewLoop04PHBB);AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB], NewDTNode);AR.DT.addNewBlock(NewLoop040PHBB, NewLoop04BB);AR.DT.addNewBlock(NewLoop040BB, NewLoop040PHBB);AR.DT.addNewBlock(NewLoop04LatchBB, NewLoop040BB);EXPECT_TRUE(AR.DT.verify());L.getParentLoop()->addBasicBlockToLoop(NewLoop03PHBB, AR.LI);NewLoops[0]->addBasicBlockToLoop(NewLoop03BB, AR.LI);L.getParentLoop()->addBasicBlockToLoop(NewLoop04PHBB, AR.LI);NewLoops[1]->addBasicBlockToLoop(NewLoop04BB, AR.LI);NewLoops[1]->addBasicBlockToLoop(NewLoop040PHBB, AR.LI);NewLoops[2]->addBasicBlockToLoop(NewLoop040BB, AR.LI);NewLoops[1]->addBasicBlockToLoop(NewLoop04LatchBB, AR.LI);L.getParentLoop()->verifyLoop();Updater.addSiblingLoops({NewLoops[0], NewLoops[1]});return PreservedAnalyses::all();}));EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.3"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));// Note that we need to visit the inner loop of this added sibling before the// sibling itself!EXPECT_CALL(MLPHandle, run(HasName("loop.0.4.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.4.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.4.0"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.4"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.4"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.4"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));// And only now do we visit the outermost loop of the nest.EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));// On the second pass, we add sibling loops which become new top-level loops.EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR,LPMUpdater &Updater) {auto *NewLoop = AR.LI.AllocateLoop();AR.LI.addTopLevelLoop(NewLoop);auto *NewLoop1PHBB = BasicBlock::Create(Context, "loop.1.ph", &F, &Loop2BB);auto *NewLoop1BB = BasicBlock::Create(Context, "loop.1", &F, &Loop2BB);BranchInst::Create(NewLoop1BB, NewLoop1PHBB);CreateCondBr(&Loop2PHBB, NewLoop1BB, "cond.1", NewLoop1BB);Loop0BB.getTerminator()->replaceUsesOfWith(&Loop2PHBB, NewLoop1PHBB);AR.DT.addNewBlock(NewLoop1PHBB, &Loop0BB);auto *NewDTNode = AR.DT.addNewBlock(NewLoop1BB, NewLoop1PHBB);AR.DT.changeImmediateDominator(AR.DT[&Loop2PHBB], NewDTNode);EXPECT_TRUE(AR.DT.verify());NewLoop->addBasicBlockToLoop(NewLoop1BB, AR.LI);NewLoop->verifyLoop();Updater.addSiblingLoops({NewLoop});return PreservedAnalyses::all();}));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.1"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.1"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.1"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.2"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.2"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.2"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));// Now that all the expected actions are registered, run the pipeline over// our module. All of our expectations are verified when the test finishes.MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, LoopDeletion) {// Build a module with a single loop nest that contains one outer loop with// three subloops, and one of those with its own subloop. We will// incrementally delete all of these to test different deletion scenarios.M = parseIR(Context, "define void @f(i1* %ptr) {\n""entry:\n"" br label %loop.0\n""loop.0:\n"" %cond.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0, label %loop.0.0.ph, label %end\n""loop.0.0.ph:\n"" br label %loop.0.0\n""loop.0.0:\n"" %cond.0.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n""loop.0.1.ph:\n"" br label %loop.0.1\n""loop.0.1:\n"" %cond.0.1 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n""loop.0.2.ph:\n"" br label %loop.0.2\n""loop.0.2:\n"" %cond.0.2 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.2, label %loop.0.2.0.ph, label %loop.0.latch\n""loop.0.2.0.ph:\n"" br label %loop.0.2.0\n""loop.0.2.0:\n"" %cond.0.2.0 = load volatile i1, i1* %ptr\n"" br i1 %cond.0.2.0, label %loop.0.2.0, label %loop.0.2.latch\n""loop.0.2.latch:\n"" br label %loop.0.2\n""loop.0.latch:\n"" br label %loop.0\n""end:\n"" ret void\n""}\n");// Build up variables referring into the IR so we can rewrite it below// easily.Function &F = *M->begin();ASSERT_THAT(F, HasName("f"));Argument &Ptr = *F.arg_begin();auto BBI = F.begin();BasicBlock &EntryBB = *BBI++;ASSERT_THAT(EntryBB, HasName("entry"));BasicBlock &Loop0BB = *BBI++;ASSERT_THAT(Loop0BB, HasName("loop.0"));BasicBlock &Loop00PHBB = *BBI++;ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph"));BasicBlock &Loop00BB = *BBI++;ASSERT_THAT(Loop00BB, HasName("loop.0.0"));BasicBlock &Loop01PHBB = *BBI++;ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph"));BasicBlock &Loop01BB = *BBI++;ASSERT_THAT(Loop01BB, HasName("loop.0.1"));BasicBlock &Loop02PHBB = *BBI++;ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph"));BasicBlock &Loop02BB = *BBI++;ASSERT_THAT(Loop02BB, HasName("loop.0.2"));BasicBlock &Loop020PHBB = *BBI++;ASSERT_THAT(Loop020PHBB, HasName("loop.0.2.0.ph"));BasicBlock &Loop020BB = *BBI++;ASSERT_THAT(Loop020BB, HasName("loop.0.2.0"));BasicBlock &Loop02LatchBB = *BBI++;ASSERT_THAT(Loop02LatchBB, HasName("loop.0.2.latch"));BasicBlock &Loop0LatchBB = *BBI++;ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch"));BasicBlock &EndBB = *BBI++;ASSERT_THAT(EndBB, HasName("end"));ASSERT_THAT(BBI, F.end());// Helper to do the actual deletion of a loop. We directly encode this here// to isolate ourselves from the rest of LLVM and for simplicity. Here we can// egregiously cheat based on knowledge of the test case. For example, we// have no PHI nodes and there is always a single i-dom.auto EraseLoop = [](Loop &L, BasicBlock &IDomBB,LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {assert(L.isInnermost() && "Can only delete leaf loops with this routine!");SmallVector<BasicBlock *, 4> LoopBBs(L.block_begin(), L.block_end());Updater.markLoopAsDeleted(L, L.getName());IDomBB.getTerminator()->replaceUsesOfWith(L.getHeader(),L.getUniqueExitBlock());for (BasicBlock *LoopBB : LoopBBs) {SmallVector<DomTreeNode *, 4> ChildNodes(AR.DT[LoopBB]->begin(),AR.DT[LoopBB]->end());for (DomTreeNode *ChildNode : ChildNodes)AR.DT.changeImmediateDominator(ChildNode, AR.DT[&IDomBB]);AR.DT.eraseNode(LoopBB);AR.LI.removeBlock(LoopBB);LoopBB->dropAllReferences();}for (BasicBlock *LoopBB : LoopBBs)LoopBB->eraseFromParent();AR.LI.erase(&L);};// Build up the pass managers.ModulePassManager MPM;FunctionPassManager FPM;// We run several loop pass pipelines across the loop nest, but they all take// the same form of three mock pass runs in a loop pipeline followed by// domtree and loop verification. We use a lambda to stamp this out each// time.auto AddLoopPipelineAndVerificationPasses = [&] {LoopPassManager LPM;LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());LPM.addPass(MLPHandle.getPass());FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));FPM.addPass(DominatorTreeVerifierPass());FPM.addPass(LoopVerifierPass());};// All the visit orders are deterministic so we use simple fully order// expectations.::testing::InSequence MakeExpectationsSequenced;// We run the loop pipeline with three passes over each of the loops. When// running over the middle loop, the second pass in the pipeline deletes it.// This should prevent the third pass from visiting it but otherwise leave// the process unimpacted.AddLoopPipelineAndVerificationPasses();EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {Loop *ParentL = L.getParentLoop();AR.SE.forgetLoop(&L);EraseLoop(L, Loop01PHBB, AR, Updater);ParentL->verifyLoop();return PreservedAnalyses::all();}));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.2.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2.0"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.2"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));// Run the loop pipeline again. This time we delete the last loop, which// contains a nested loop within it and insert a new loop into the nest. This// makes sure we can handle nested loop deletion.AddLoopPipelineAndVerificationPasses();EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).Times(3).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2.0"), _, _, _)).Times(3).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));BasicBlock *NewLoop03PHBB;EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {AR.SE.forgetLoop(*L.begin());EraseLoop(**L.begin(), Loop020PHBB, AR, Updater);auto *ParentL = L.getParentLoop();AR.SE.forgetLoop(&L);EraseLoop(L, Loop02PHBB, AR, Updater);// Now insert a new sibling loop.auto *NewSibling = AR.LI.AllocateLoop();ParentL->addChildLoop(NewSibling);NewLoop03PHBB =BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB);auto *NewLoop03BB =BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB);BranchInst::Create(NewLoop03BB, NewLoop03PHBB);auto *Cond =new LoadInst(Type::getInt1Ty(Context), &Ptr, "cond.0.3",/*isVolatile*/ true, NewLoop03BB);BranchInst::Create(&Loop0LatchBB, NewLoop03BB, Cond, NewLoop03BB);Loop02PHBB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB,NewLoop03PHBB);AR.DT.addNewBlock(NewLoop03PHBB, &Loop02PHBB);AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB);AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB],AR.DT[NewLoop03BB]);EXPECT_TRUE(AR.DT.verify());ParentL->addBasicBlockToLoop(NewLoop03PHBB, AR.LI);NewSibling->addBasicBlockToLoop(NewLoop03BB, AR.LI);NewSibling->verifyLoop();ParentL->verifyLoop();Updater.addSiblingLoops({NewSibling});return PreservedAnalyses::all();}));// To respect our inner-to-outer traversal order, we must visit the// newly-inserted sibling of the loop we just deleted before we visit the// outer loop. When we do so, this must compute a fresh analysis result, even// though our new loop has the same pointer value as the loop we deleted.EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLAHandle, run(HasName("loop.0.3"), _, _));EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _)).Times(2).WillRepeatedly(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).Times(3).WillRepeatedly(Invoke(getLoopAnalysisResult));// In the final loop pipeline run we delete every loop, including the last// loop of the nest. We do this again in the second pass in the pipeline, and// as a consequence we never make it to three runs on any loop. We also cover// deleting multiple loops in a single pipeline, deleting the first loop and// deleting the (last) top level loop.AddLoopPipelineAndVerificationPasses();EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {AR.SE.forgetLoop(&L);EraseLoop(L, Loop00PHBB, AR, Updater);return PreservedAnalyses::all();}));EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {AR.SE.forgetLoop(&L);EraseLoop(L, *NewLoop03PHBB, AR, Updater);return PreservedAnalyses::all();}));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke(getLoopAnalysisResult));EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {AR.SE.forgetLoop(&L);EraseLoop(L, EntryBB, AR, Updater);return PreservedAnalyses::all();}));// Add the function pass pipeline now that it is fully built up and run it// over the module's one function.MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}TEST_F(LoopPassManagerTest, HandleLoopNestPass) {::testing::Sequence FSequence, GSequence;EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).Times(2).InSequence(FSequence);EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).Times(2).InSequence(FSequence);EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)).InSequence(GSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)).InSequence(GSequence);EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)).InSequence(GSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)).InSequence(GSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)).InSequence(GSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)).InSequence(GSequence);ModulePassManager MPM;FunctionPassManager FPM;{LoopPassManager LPM;LPM.addPass(MLPHandle.getPass());LPM.addPass(MLNPHandle.getPass());LPM.addPass(MLPHandle.getPass());LPM.addPass(MLNPHandle.getPass());auto Adaptor = createFunctionToLoopPassAdaptor(std::move(LPM));ASSERT_FALSE(Adaptor.isLoopNestMode());FPM.addPass(std::move(Adaptor));}{auto Adaptor = createFunctionToLoopPassAdaptor(MLNPHandle.getPass());ASSERT_TRUE(Adaptor.isLoopNestMode());FPM.addPass(std::move(Adaptor));}{LoopPassManager LPM;LPM.addPass(MLNPHandle.getPass());auto Adaptor = createFunctionToLoopPassAdaptor(MLNPHandle.getPass());ASSERT_TRUE(Adaptor.isLoopNestMode());FPM.addPass(std::move(Adaptor));}MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.run(*M, MAM);}} // namespace
//===- LICMTest.cpp - LICM unit tests -------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Module.h"#include "llvm/Passes/PassBuilder.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Transforms/Scalar/LICM.h"#include "gtest/gtest.h"namespace llvm {TEST(LICMTest, TestSCEVInvalidationOnHoisting) {LLVMContext Ctx;ModulePassManager MPM;PassBuilder PB;LoopAnalysisManager LAM;FunctionAnalysisManager FAM;CGSCCAnalysisManager CGAM;ModuleAnalysisManager MAM;PB.registerModuleAnalyses(MAM);PB.registerCGSCCAnalyses(CGAM);PB.registerFunctionAnalyses(FAM);PB.registerLoopAnalyses(LAM);PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);StringRef PipelineStr = "require<opt-remark-emit>,loop-mssa(licm)";ASSERT_THAT_ERROR(PB.parsePassPipeline(MPM, PipelineStr), Succeeded());SMDiagnostic Error;StringRef Text = R"(define void @foo(i64* %ptr) {entry:br label %looploop:%iv = phi i64 [ 0, %entry ], [ %iv.inc, %loop ]%n = load i64, i64* %ptr, !invariant.load !0%iv.inc = add i64 %iv, 1%cmp = icmp ult i64 %iv.inc, %nbr i1 %cmp, label %loop, label %exitexit:ret void}!0 = !{})";std::unique_ptr<Module> M = parseAssemblyString(Text, Error, Ctx);ASSERT_TRUE(M);Function *F = M->getFunction("foo");ScalarEvolution &SE = FAM.getResult<ScalarEvolutionAnalysis>(*F);BasicBlock &EntryBB = F->getEntryBlock();BasicBlock *LoopBB = EntryBB.getUniqueSuccessor();// Select `load i64, i64* %ptr`.Instruction *IBefore = LoopBB->getFirstNonPHI();// Make sure the right instruction was selected.ASSERT_TRUE(isa<LoadInst>(IBefore));// Upon this query SCEV caches disposition of <load i64, i64* %ptr> SCEV.ASSERT_EQ(SE.getBlockDisposition(SE.getSCEV(IBefore), LoopBB),ScalarEvolution::BlockDisposition::DominatesBlock);MPM.run(*M, MAM);// Select `load i64, i64* %ptr` after it was hoisted.Instruction *IAfter = EntryBB.getFirstNonPHI();// Make sure the right instruction was selected.ASSERT_TRUE(isa<LoadInst>(IAfter));ScalarEvolution::BlockDisposition DispositionBeforeInvalidation =SE.getBlockDisposition(SE.getSCEV(IAfter), LoopBB);SE.forgetValue(IAfter);ScalarEvolution::BlockDisposition DispositionAfterInvalidation =SE.getBlockDisposition(SE.getSCEV(IAfter), LoopBB);// If LICM have properly invalidated SCEV,// 1. SCEV of <load i64, i64* %ptr> should properly dominate the "loop" BB,// 2. extra invalidation shouldn't change result of the query.EXPECT_EQ(DispositionBeforeInvalidation,ScalarEvolution::BlockDisposition::ProperlyDominatesBlock);EXPECT_EQ(DispositionBeforeInvalidation, DispositionAfterInvalidation);}}
set(LLVM_LINK_COMPONENTSAnalysisAsmParserCorePassesSupportScalarOptsTransformUtils)add_llvm_unittest(ScalarTestsLICMTest.cppLoopPassManagerTest.cpp)target_link_libraries(ScalarTests PRIVATE LLVMTestingSupport)# Workaround for the gcc 6.1 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80916.if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)set_source_files_properties(LoopPassManagerTest.cpp PROPERTIES COMPILE_FLAGS -Wno-unused-function)endif()set_property(TARGET ScalarTests PROPERTY FOLDER "Tests/UnitTests/TransformsTests")
//===- WholeProgramDevirt.cpp - Unit tests for whole-program devirt -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/IPO/WholeProgramDevirt.h"#include "llvm/ADT/ArrayRef.h"#include "gtest/gtest.h"using namespace llvm;using namespace wholeprogramdevirt;TEST(WholeProgramDevirt, findLowestOffset) {VTableBits VT1;VT1.ObjectSize = 8;VT1.Before.BytesUsed = {1 << 0};VT1.After.BytesUsed = {1 << 1};VTableBits VT2;VT2.ObjectSize = 8;VT2.Before.BytesUsed = {1 << 1};VT2.After.BytesUsed = {1 << 0};TypeMemberInfo TM1{&VT1, 0};TypeMemberInfo TM2{&VT2, 0};VirtualCallTarget Targets[] = {{&TM1, /*IsBigEndian=*/false},{&TM2, /*IsBigEndian=*/false},};EXPECT_EQ(2ull, findLowestOffset(Targets, /*IsAfter=*/false, 1));EXPECT_EQ(66ull, findLowestOffset(Targets, /*IsAfter=*/true, 1));EXPECT_EQ(8ull, findLowestOffset(Targets, /*IsAfter=*/false, 8));EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/true, 8));TM1.Offset = 4;EXPECT_EQ(33ull, findLowestOffset(Targets, /*IsAfter=*/false, 1));EXPECT_EQ(65ull, findLowestOffset(Targets, /*IsAfter=*/true, 1));EXPECT_EQ(40ull, findLowestOffset(Targets, /*IsAfter=*/false, 8));EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/true, 8));TM1.Offset = 8;TM2.Offset = 8;EXPECT_EQ(66ull, findLowestOffset(Targets, /*IsAfter=*/false, 1));EXPECT_EQ(2ull, findLowestOffset(Targets, /*IsAfter=*/true, 1));EXPECT_EQ(72ull, findLowestOffset(Targets, /*IsAfter=*/false, 8));EXPECT_EQ(8ull, findLowestOffset(Targets, /*IsAfter=*/true, 8));VT1.After.BytesUsed = {0xff, 0, 0, 0, 0xff};VT2.After.BytesUsed = {0xff, 1, 0, 0, 0};EXPECT_EQ(16ull, findLowestOffset(Targets, /*IsAfter=*/true, 16));EXPECT_EQ(40ull, findLowestOffset(Targets, /*IsAfter=*/true, 32));}TEST(WholeProgramDevirt, setReturnValues) {VTableBits VT1;VT1.ObjectSize = 8;VTableBits VT2;VT2.ObjectSize = 8;TypeMemberInfo TM1{&VT1, 0};TypeMemberInfo TM2{&VT2, 0};VirtualCallTarget Targets[] = {{&TM1, /*IsBigEndian=*/false},{&TM2, /*IsBigEndian=*/false},};TM1.Offset = 4;TM2.Offset = 4;int64_t OffsetByte;uint64_t OffsetBit;Targets[0].RetVal = 1;Targets[1].RetVal = 0;setBeforeReturnValues(Targets, 32, 1, OffsetByte, OffsetBit);EXPECT_EQ(-5ll, OffsetByte);EXPECT_EQ(0ull, OffsetBit);EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.Bytes);EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.BytesUsed);EXPECT_EQ(std::vector<uint8_t>{0}, VT2.Before.Bytes);EXPECT_EQ(std::vector<uint8_t>{1}, VT2.Before.BytesUsed);Targets[0].RetVal = 0;Targets[1].RetVal = 1;setBeforeReturnValues(Targets, 39, 1, OffsetByte, OffsetBit);EXPECT_EQ(-5ll, OffsetByte);EXPECT_EQ(7ull, OffsetBit);EXPECT_EQ(std::vector<uint8_t>{1}, VT1.Before.Bytes);EXPECT_EQ(std::vector<uint8_t>{0x81}, VT1.Before.BytesUsed);EXPECT_EQ(std::vector<uint8_t>{0x80}, VT2.Before.Bytes);EXPECT_EQ(std::vector<uint8_t>{0x81}, VT2.Before.BytesUsed);Targets[0].RetVal = 12;Targets[1].RetVal = 34;setBeforeReturnValues(Targets, 40, 8, OffsetByte, OffsetBit);EXPECT_EQ(-6ll, OffsetByte);EXPECT_EQ(0ull, OffsetBit);EXPECT_EQ((std::vector<uint8_t>{1, 12}), VT1.Before.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT1.Before.BytesUsed);EXPECT_EQ((std::vector<uint8_t>{0x80, 34}), VT2.Before.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT2.Before.BytesUsed);Targets[0].RetVal = 56;Targets[1].RetVal = 78;setBeforeReturnValues(Targets, 48, 16, OffsetByte, OffsetBit);EXPECT_EQ(-8ll, OffsetByte);EXPECT_EQ(0ull, OffsetBit);EXPECT_EQ((std::vector<uint8_t>{1, 12, 0, 56}), VT1.Before.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),VT1.Before.BytesUsed);EXPECT_EQ((std::vector<uint8_t>{0x80, 34, 0, 78}), VT2.Before.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),VT2.Before.BytesUsed);Targets[0].RetVal = 1;Targets[1].RetVal = 0;setAfterReturnValues(Targets, 32, 1, OffsetByte, OffsetBit);EXPECT_EQ(4ll, OffsetByte);EXPECT_EQ(0ull, OffsetBit);EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.Bytes);EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.BytesUsed);EXPECT_EQ(std::vector<uint8_t>{0}, VT2.After.Bytes);EXPECT_EQ(std::vector<uint8_t>{1}, VT2.After.BytesUsed);Targets[0].RetVal = 0;Targets[1].RetVal = 1;setAfterReturnValues(Targets, 39, 1, OffsetByte, OffsetBit);EXPECT_EQ(4ll, OffsetByte);EXPECT_EQ(7ull, OffsetBit);EXPECT_EQ(std::vector<uint8_t>{1}, VT1.After.Bytes);EXPECT_EQ(std::vector<uint8_t>{0x81}, VT1.After.BytesUsed);EXPECT_EQ(std::vector<uint8_t>{0x80}, VT2.After.Bytes);EXPECT_EQ(std::vector<uint8_t>{0x81}, VT2.After.BytesUsed);Targets[0].RetVal = 12;Targets[1].RetVal = 34;setAfterReturnValues(Targets, 40, 8, OffsetByte, OffsetBit);EXPECT_EQ(5ll, OffsetByte);EXPECT_EQ(0ull, OffsetBit);EXPECT_EQ((std::vector<uint8_t>{1, 12}), VT1.After.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT1.After.BytesUsed);EXPECT_EQ((std::vector<uint8_t>{0x80, 34}), VT2.After.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff}), VT2.After.BytesUsed);Targets[0].RetVal = 56;Targets[1].RetVal = 78;setAfterReturnValues(Targets, 48, 16, OffsetByte, OffsetBit);EXPECT_EQ(6ll, OffsetByte);EXPECT_EQ(0ull, OffsetBit);EXPECT_EQ((std::vector<uint8_t>{1, 12, 56, 0}), VT1.After.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),VT1.After.BytesUsed);EXPECT_EQ((std::vector<uint8_t>{0x80, 34, 78, 0}), VT2.After.Bytes);EXPECT_EQ((std::vector<uint8_t>{0x81, 0xff, 0xff, 0xff}),VT2.After.BytesUsed);}
//===- LowerTypeTests.cpp - Unit tests for type test lowering -------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Transforms/IPO/LowerTypeTests.h"#include "gtest/gtest.h"using namespace llvm;using namespace lowertypetests;TEST(LowerTypeTests, BitSetBuilder) {struct {std::vector<uint64_t> Offsets;std::set<uint64_t> Bits;uint64_t ByteOffset;uint64_t BitSize;unsigned AlignLog2;bool IsSingleOffset;bool IsAllOnes;} BSBTests[] = {{{}, std::set<uint64_t>{}, 0, 1, 0, false, false},{{0}, {0}, 0, 1, 0, true, true},{{4}, {0}, 4, 1, 0, true, true},{{37}, {0}, 37, 1, 0, true, true},{{0, 1}, {0, 1}, 0, 2, 0, false, true},{{0, 4}, {0, 1}, 0, 2, 2, false, true},{{0, uint64_t(1) << 33}, {0, 1}, 0, 2, 33, false, true},{{3, 7}, {0, 1}, 3, 2, 2, false, true},{{0, 1, 7}, {0, 1, 7}, 0, 8, 0, false, false},{{0, 2, 14}, {0, 1, 7}, 0, 8, 1, false, false},{{0, 1, 8}, {0, 1, 8}, 0, 9, 0, false, false},{{0, 2, 16}, {0, 1, 8}, 0, 9, 1, false, false},{{0, 1, 2, 3, 4, 5, 6, 7},{0, 1, 2, 3, 4, 5, 6, 7},0,8,0,false,true},{{0, 1, 2, 3, 4, 5, 6, 7, 8},{0, 1, 2, 3, 4, 5, 6, 7, 8},0,9,0,false,true},};for (auto &&T : BSBTests) {BitSetBuilder BSB;for (auto Offset : T.Offsets)BSB.addOffset(Offset);BitSetInfo BSI = BSB.build();EXPECT_EQ(T.Bits, BSI.Bits);EXPECT_EQ(T.ByteOffset, BSI.ByteOffset);EXPECT_EQ(T.BitSize, BSI.BitSize);EXPECT_EQ(T.AlignLog2, BSI.AlignLog2);EXPECT_EQ(T.IsSingleOffset, BSI.isSingleOffset());EXPECT_EQ(T.IsAllOnes, BSI.isAllOnes());for (auto Offset : T.Offsets)EXPECT_TRUE(BSI.containsGlobalOffset(Offset));auto I = T.Offsets.begin();for (uint64_t NonOffset = 0; NonOffset != 256; ++NonOffset) {if (I != T.Offsets.end() && *I == NonOffset) {++I;continue;}EXPECT_FALSE(BSI.containsGlobalOffset(NonOffset));}}}TEST(LowerTypeTests, GlobalLayoutBuilder) {struct {uint64_t NumObjects;std::vector<std::set<uint64_t>> Fragments;std::vector<uint64_t> WantLayout;} GLBTests[] = {{0, {}, {}},{4, {{0, 1}, {2, 3}}, {0, 1, 2, 3}},{3, {{0, 1}, {1, 2}}, {0, 1, 2}},{4, {{0, 1}, {1, 2}, {2, 3}}, {0, 1, 2, 3}},{4, {{0, 1}, {2, 3}, {1, 2}}, {0, 1, 2, 3}},{6, {{2, 5}, {0, 1, 2, 3, 4, 5}}, {0, 1, 2, 5, 3, 4}},};for (auto &&T : GLBTests) {GlobalLayoutBuilder GLB(T.NumObjects);for (auto &&F : T.Fragments)GLB.addFragment(F);std::vector<uint64_t> ComputedLayout;for (auto &&F : GLB.Fragments)ComputedLayout.insert(ComputedLayout.end(), F.begin(), F.end());EXPECT_EQ(T.WantLayout, ComputedLayout);}}TEST(LowerTypeTests, ByteArrayBuilder) {struct BABAlloc {std::set<uint64_t> Bits;uint64_t BitSize;uint64_t WantByteOffset;uint8_t WantMask;};struct {std::vector<BABAlloc> Allocs;std::vector<uint8_t> WantBytes;} BABTests[] = {{{{{0}, 1, 0, 1}, {{0}, 1, 0, 2}}, {3}},{{{{0}, 16, 0, 1},{{1}, 15, 0, 2},{{2}, 14, 0, 4},{{3}, 13, 0, 8},{{4}, 12, 0, 0x10},{{5}, 11, 0, 0x20},{{6}, 10, 0, 0x40},{{7}, 9, 0, 0x80},{{0}, 7, 9, 0x80},{{0}, 6, 10, 0x40},{{0}, 5, 11, 0x20},{{0}, 4, 12, 0x10},{{0}, 3, 13, 8},{{0}, 2, 14, 4},{{0}, 1, 15, 2}},{1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0, 0x80, 0x40, 0x20, 0x10, 8, 4,2}},};for (auto &&T : BABTests) {ByteArrayBuilder BABuilder;for (auto &&A : T.Allocs) {uint64_t GotByteOffset;uint8_t GotMask;BABuilder.allocate(A.Bits, A.BitSize, GotByteOffset, GotMask);EXPECT_EQ(A.WantByteOffset, GotByteOffset);EXPECT_EQ(A.WantMask, GotMask);}EXPECT_EQ(T.WantBytes, BABuilder.Bytes);}}
set(LLVM_LINK_COMPONENTSAnalysisAsmParserCoreIPOSupportTransformUtils)add_llvm_unittest(IPOTestsLowerTypeTests.cppWholeProgramDevirt.cppAttributorTest.cpp)set_property(TARGET IPOTests PROPERTY FOLDER "Tests/UnitTests/TransformsTests")
//===- llvm/unittest/Transforms/IPO/AttributorTestBase.h -----------------===////// 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////===----------------------------------------------------------------------===///// \file/// This file defines an AttributorTestBase class, which provides helpers to/// parse a LLVM IR string and create Attributor//===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_TRANSFORMS_ATTRIBUTOR_TESTBASE_H#define LLVM_UNITTESTS_TRANSFORMS_ATTRIBUTOR_TESTBASE_H#include "llvm/Analysis/CGSCCPassManager.h"#include "llvm/Analysis/CallGraphSCCPass.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Module.h"#include "llvm/IR/PassManager.h"#include "llvm/Support/Allocator.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Transforms/IPO/Attributor.h"#include "llvm/Transforms/Utils/CallGraphUpdater.h"#include "gtest/gtest.h"#include <memory>namespace llvm {/// Helper class to create a module from assembly string and an Attributorclass AttributorTestBase : public testing::Test {protected:std::unique_ptr<LLVMContext> Ctx;std::unique_ptr<Module> M;AttributorTestBase() : Ctx(new LLVMContext) {}Module &parseModule(const char *ModuleString) {SMDiagnostic Err;M = parseAssemblyString(ModuleString, Err, *Ctx);EXPECT_TRUE(M);return *M;}};} // namespace llvm#endif
//===- AttributorTest.cpp - Attributor unit tests ------------------------===////// 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////===---------------------------------------------------------------------===//#include "llvm/Transforms/IPO/Attributor.h"#include "AttributorTestBase.h"#include "llvm/ADT/StringRef.h"#include "llvm/Analysis/CGSCCPassManager.h"#include "llvm/Analysis/CallGraphSCCPass.h"#include "llvm/Analysis/LoopAnalysisManager.h"#include "llvm/AsmParser/Parser.h"#include "llvm/Support/Allocator.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Transforms/Utils/CallGraphUpdater.h"#include "gtest/gtest.h"#include <memory>namespace llvm {TEST_F(AttributorTestBase, IRPPositionCallBaseContext) {const char *ModuleString = R"(define i32 @foo(i32 %a) {entry:ret i32 %a})";parseModule(ModuleString);Function *F = M->getFunction("foo");IRPosition Pos =IRPosition::function(*F, (const llvm::CallBase *)(uintptr_t)0xDEADBEEF);EXPECT_TRUE(Pos.hasCallBaseContext());EXPECT_FALSE(Pos.stripCallBaseContext().hasCallBaseContext());}TEST_F(AttributorTestBase, TestCast) {const char *ModuleString = R"(define i32 @foo(i32 %a, i32 %b) {entry:%c = add i32 %a, %bret i32 %c})";Module &M = parseModule(ModuleString);SetVector<Function *> Functions;AnalysisGetter AG;for (Function &F : M)Functions.insert(&F);CallGraphUpdater CGUpdater;BumpPtrAllocator Allocator;InformationCache InfoCache(M, AG, Allocator, nullptr);AttributorConfig AC(CGUpdater);Attributor A(Functions, InfoCache, AC);Function *F = M.getFunction("foo");const AbstractAttribute *AA =&A.getOrCreateAAFor<AAIsDead>(IRPosition::function(*F));EXPECT_TRUE(AA);const auto *SFail = dyn_cast<AAAlign>(AA);const auto *SSucc = dyn_cast<AAIsDead>(AA);ASSERT_EQ(SFail, nullptr);ASSERT_TRUE(SSucc);}TEST_F(AttributorTestBase, AAReachabilityTest) {const char *ModuleString = R"(@x = external global i32define internal void @func4() {store i32 0, i32* @xret void}define internal void @func3() {store i32 0, i32* @xret void}define internal void @func8() {store i32 0, i32* @xret void}define internal void @func2() {entry:call void @func3()ret void}define internal void @func1() {entry:call void @func2()ret void}declare void @unknown()define internal void @func5(void ()* %ptr) {entry:call void %ptr()call void @unknown()ret void}define internal void @func6() {entry:call void @func5(void ()* @func3)ret void}define internal void @func7() {entry:call void @func2()call void @func4()ret void}define internal void @func9() {entry:call void @func2()call void @func8()ret void}define void @func10() {entry:call void @func9()call void @func4()ret void})";Module &M = parseModule(ModuleString);SetVector<Function *> Functions;AnalysisGetter AG;for (Function &F : M)Functions.insert(&F);CallGraphUpdater CGUpdater;BumpPtrAllocator Allocator;InformationCache InfoCache(M, AG, Allocator, nullptr);AttributorConfig AC(CGUpdater);AC.DeleteFns = false;Attributor A(Functions, InfoCache, AC);Function &F1 = *M.getFunction("func1");Function &F3 = *M.getFunction("func3");Function &F4 = *M.getFunction("func4");Function &F6 = *M.getFunction("func6");Function &F7 = *M.getFunction("func7");Function &F9 = *M.getFunction("func9");// call void @func2()CallBase &F7FirstCB = static_cast<CallBase &>(*F7.getEntryBlock().begin());// call void @func2()Instruction &F9FirstInst = *F9.getEntryBlock().begin();// call void @func8Instruction &F9SecondInst = *++(F9.getEntryBlock().begin());const AAFunctionReachability &F1AA =A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F1));const AAFunctionReachability &F6AA =A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F6));const AAFunctionReachability &F7AA =A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F7));const AAFunctionReachability &F9AA =A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F9));F1AA.canReach(A, F3);F1AA.canReach(A, F4);F6AA.canReach(A, F4);F7AA.canReach(A, F7FirstCB, F3);F7AA.canReach(A, F7FirstCB, F4);F9AA.instructionCanReach(A, F9FirstInst, F3);F9AA.instructionCanReach(A, F9FirstInst, F4);A.run();ASSERT_TRUE(F1AA.canReach(A, F3));ASSERT_FALSE(F1AA.canReach(A, F4));ASSERT_TRUE(F7AA.canReach(A, F7FirstCB, F3));ASSERT_FALSE(F7AA.canReach(A, F7FirstCB, F4));// Assumed to be reacahable, since F6 can reach a function with// a unknown callee.ASSERT_TRUE(F6AA.canReach(A, F4));// The second instruction of F9 can't reach the first call.ASSERT_FALSE(F9AA.instructionCanReach(A, F9SecondInst, F3));// The first instruction of F9 can reach the first call.ASSERT_TRUE(F9AA.instructionCanReach(A, F9FirstInst, F3));// Because func10 calls the func4 after the call to func9 it is reachable but// as it requires backwards logic we would need AA::isPotentiallyReachable.ASSERT_FALSE(F9AA.instructionCanReach(A, F9FirstInst, F4));}} // namespace llvm
add_subdirectory(IPO)add_subdirectory(Scalar)add_subdirectory(Utils)add_subdirectory(Vectorize)
//===-- TextStubV4Tests.cpp - TBD V4 File Test ----------------------------===////// 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////===-----------------------------------------------------------------------===/#include "TextStubHelpers.h"#include "llvm/TextAPI/InterfaceFile.h"#include "llvm/TextAPI/TextAPIReader.h"#include "llvm/TextAPI/TextAPIWriter.h"#include "gtest/gtest.h"#include <string>#include <vector>using namespace llvm;using namespace llvm::MachO;namespace TBDv4 {TEST(TBDv4, ReadFile) {static const char TBDv4File[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n""uuids:\n"" - target: i386-macos\n"" value: 00000000-0000-0000-0000-000000000000\n"" - target: x86_64-macos\n"" value: 11111111-1111-1111-1111-111111111111\n"" - target: x86_64-ios\n"" value: 11111111-1111-1111-1111-111111111111\n""flags: [ flat_namespace, installapi ]\n""install-name: Umbrella.framework/Umbrella\n""current-version: 1.2.3\n""compatibility-version: 1.2\n""swift-abi-version: 5\n""parent-umbrella:\n"" - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n"" umbrella: System\n""allowable-clients:\n"" - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n"" clients: [ ClientA ]\n""reexported-libraries:\n"" - targets: [ i386-macos ]\n"" libraries: [ /System/Library/Frameworks/A.framework/A ]\n""exports:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symA ]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n"" - targets: [ x86_64-ios ]\n"" symbols: [_symB]\n"" - targets: [ x86_64-macos, x86_64-ios ]\n"" symbols: [_symAB]\n""reexports:\n"" - targets: [ i386-macos ]\n"" symbols: [_symC]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n""undefineds:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symD ]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4File, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());PlatformSet Platforms;Platforms.insert(getPlatformFromName("macos"));Platforms.insert(getPlatformFromName("ios"));auto Archs = AK_i386 | AK_x86_64;TargetList Targets = {Target(AK_i386, PLATFORM_MACOS),Target(AK_x86_64, PLATFORM_MACOS),Target(AK_x86_64, PLATFORM_IOS),};UUIDs uuids = {{Targets[0], "00000000-0000-0000-0000-000000000000"},{Targets[1], "11111111-1111-1111-1111-111111111111"},{Targets[2], "11111111-1111-1111-1111-111111111111"}};EXPECT_EQ(Archs, File->getArchitectures());EXPECT_EQ(uuids, File->uuids());EXPECT_EQ(Platforms.size(), File->getPlatforms().size());for (auto Platform : File->getPlatforms())EXPECT_EQ(Platforms.count(Platform), 1U);EXPECT_EQ(std::string("Umbrella.framework/Umbrella"), File->getInstallName());EXPECT_EQ(PackedVersion(1, 2, 3), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 2, 0), File->getCompatibilityVersion());EXPECT_EQ(5U, File->getSwiftABIVersion());EXPECT_FALSE(File->isTwoLevelNamespace());EXPECT_TRUE(File->isApplicationExtensionSafe());EXPECT_TRUE(File->isInstallAPI());InterfaceFileRef client("ClientA", Targets);InterfaceFileRef reexport("/System/Library/Frameworks/A.framework/A",{Targets[0]});EXPECT_EQ(1U, File->allowableClients().size());EXPECT_EQ(client, File->allowableClients().front());EXPECT_EQ(1U, File->reexportedLibraries().size());EXPECT_EQ(reexport, File->reexportedLibraries().front());ExportedSymbolSeq Exports, Reexports, Undefineds;ExportedSymbol temp;for (const auto *Sym : File->symbols()) {temp = ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),Sym->isWeakDefined(), Sym->isThreadLocalValue()};EXPECT_FALSE(Sym->isWeakReferenced());if (Sym->isUndefined())Undefineds.emplace_back(std::move(temp));elseSym->isReexported() ? Reexports.emplace_back(std::move(temp)): Exports.emplace_back(std::move(temp));}llvm::sort(Exports);llvm::sort(Reexports);llvm::sort(Undefineds);static ExportedSymbol ExpectedExportedSymbols[] = {{SymbolKind::GlobalSymbol, "_symA", false, false},{SymbolKind::GlobalSymbol, "_symAB", false, false},{SymbolKind::GlobalSymbol, "_symB", false, false},};static ExportedSymbol ExpectedReexportedSymbols[] = {{SymbolKind::GlobalSymbol, "_symC", false, false},};static ExportedSymbol ExpectedUndefinedSymbols[] = {{SymbolKind::GlobalSymbol, "_symD", false, false},};EXPECT_EQ(sizeof(ExpectedExportedSymbols) / sizeof(ExportedSymbol),Exports.size());EXPECT_EQ(sizeof(ExpectedReexportedSymbols) / sizeof(ExportedSymbol),Reexports.size());EXPECT_EQ(sizeof(ExpectedUndefinedSymbols) / sizeof(ExportedSymbol),Undefineds.size());EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(),std::begin(ExpectedExportedSymbols)));EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(),std::begin(ExpectedReexportedSymbols)));EXPECT_TRUE(std::equal(Undefineds.begin(), Undefineds.end(),std::begin(ExpectedUndefinedSymbols)));}TEST(TBDv4, ReadMultipleDocuments) {static const char TBDv4Inlines[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-macos, i386-maccatalyst, x86_64-macos, ""x86_64-maccatalyst ]\n""uuids:\n"" - target: i386-macos\n"" value: 00000000-0000-0000-0000-000000000000\n"" - target: i386-maccatalyst\n"" value: 00000000-0000-0000-0000-000000000002\n"" - target: x86_64-macos\n"" value: 11111111-1111-1111-1111-111111111111\n"" - target: x86_64-maccatalyst\n"" value: 11111111-1111-1111-1111-111111111112\n""install-name: /System/Library/Frameworks/Umbrella.framework/Umbrella\n""parent-umbrella:\n"" - targets: [ i386-macos, x86_64-macos ]\n"" umbrella: System\n""reexported-libraries:\n"" - targets: [ i386-macos, x86_64-macos ]\n"" libraries: [ /System/Library/Frameworks/A.framework/A ]\n""--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-macos, x86_64-macos ]\n""uuids:\n"" - target: i386-macos\n"" value: 20000000-0000-0000-0000-000000000000\n"" - target: x86_64-macos\n"" value: 21111111-1111-1111-1111-111111111111\n""flags: [ flat_namespace ]\n""install-name: /System/Library/Frameworks/A.framework/A\n""current-version: 1.2.3\n""compatibility-version: 1.2\n""swift-abi-version: 5\n""exports:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symA ]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n"" - targets: [ x86_64-macos ]\n"" symbols: [_symAB]\n""reexports:\n"" - targets: [ i386-macos ]\n"" symbols: [_symC]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n""undefineds:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symD ]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n""...\n";PlatformSet Platforms;Platforms.insert(PLATFORM_MACOS);Platforms.insert(PLATFORM_MACCATALYST);ArchitectureSet Archs = AK_i386 | AK_x86_64;TargetList Targets;for (auto &&Arch : Archs)for (auto &&Platform : Platforms)Targets.emplace_back(Target(Arch, Platform));UUIDs Uuids = {{Targets[0], "00000000-0000-0000-0000-000000000000"},{Targets[1], "00000000-0000-0000-0000-000000000002"},{Targets[2], "11111111-1111-1111-1111-111111111111"},{Targets[3], "11111111-1111-1111-1111-111111111112"},};Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4Inlines, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(Archs, File->getArchitectures());EXPECT_EQ(Uuids, File->uuids());EXPECT_EQ(Platforms, File->getPlatforms());EXPECT_EQ(std::string("/System/Library/Frameworks/Umbrella.framework/Umbrella"),File->getInstallName());EXPECT_TRUE(File->isTwoLevelNamespace());EXPECT_TRUE(File->isApplicationExtensionSafe());EXPECT_FALSE(File->isInstallAPI());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());InterfaceFileRef reexport("/System/Library/Frameworks/A.framework/A",{Targets[0], Targets[2]});EXPECT_EQ(1U, File->reexportedLibraries().size());EXPECT_EQ(reexport, File->reexportedLibraries().front());EXPECT_TRUE(File->symbols().empty());// Check Inlined DocumentTargets.clear();Uuids.clear();PlatformType Platform = PLATFORM_MACOS;for (auto &&Arch : Archs)Targets.emplace_back(Target(Arch, Platform));Uuids = {{Targets[0], "20000000-0000-0000-0000-000000000000"},{Targets[1], "21111111-1111-1111-1111-111111111111"},};TBDReexportFile Document = File->documents().front();EXPECT_EQ(FileType::TBD_V4, Document->getFileType());EXPECT_EQ(Archs, Document->getArchitectures());EXPECT_EQ(Uuids, Document->uuids());EXPECT_EQ(1U, Document->getPlatforms().size());EXPECT_EQ(Platform, *(Document->getPlatforms().begin()));EXPECT_EQ(std::string("/System/Library/Frameworks/A.framework/A"),Document->getInstallName());EXPECT_EQ(PackedVersion(1, 2, 3), Document->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 2, 0), Document->getCompatibilityVersion());EXPECT_EQ(5U, Document->getSwiftABIVersion());EXPECT_FALSE(Document->isTwoLevelNamespace());EXPECT_TRUE(Document->isApplicationExtensionSafe());EXPECT_FALSE(Document->isInstallAPI());ExportedSymbolSeq Exports;ExportedSymbolSeq Reexports, Undefineds;for (const auto *Sym : Document->symbols()) {ExportedSymbol Temp =ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),Sym->isWeakDefined(), Sym->isThreadLocalValue()};EXPECT_FALSE(Sym->isWeakReferenced());if (Sym->isUndefined())Undefineds.emplace_back(std::move(Temp));elseSym->isReexported() ? Reexports.emplace_back(std::move(Temp)): Exports.emplace_back(std::move(Temp));}llvm::sort(Exports);llvm::sort(Reexports);llvm::sort(Undefineds);static ExportedSymbol ExpectedExportedSymbols[] = {{SymbolKind::GlobalSymbol, "_symA", false, false},{SymbolKind::GlobalSymbol, "_symAB", false, false},};static ExportedSymbol ExpectedReexportedSymbols[] = {{SymbolKind::GlobalSymbol, "_symC", false, false},};static ExportedSymbol ExpectedUndefinedSymbols[] = {{SymbolKind::GlobalSymbol, "_symD", false, false},};EXPECT_EQ(sizeof(ExpectedExportedSymbols) / sizeof(ExportedSymbol),Exports.size());EXPECT_EQ(sizeof(ExpectedReexportedSymbols) / sizeof(ExportedSymbol),Reexports.size());EXPECT_EQ(sizeof(ExpectedUndefinedSymbols) / sizeof(ExportedSymbol),Undefineds.size());EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(),std::begin(ExpectedExportedSymbols)));EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(),std::begin(ExpectedReexportedSymbols)));EXPECT_TRUE(std::equal(Undefineds.begin(), Undefineds.end(),std::begin(ExpectedUndefinedSymbols)));}TEST(TBDv4, WriteFile) {static const char TBDv4File[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-macos, x86_64-ios-simulator ]\n""uuids:\n"" - target: i386-macos\n"" value: 00000000-0000-0000-0000-000000000000\n"" - target: x86_64-ios-simulator\n"" value: 11111111-1111-1111-1111-111111111111\n""flags: [ installapi ]\n""install-name: 'Umbrella.framework/Umbrella'\n""current-version: 1.2.3\n""compatibility-version: 0\n""swift-abi-version: 5\n""parent-umbrella:\n"" - targets: [ i386-macos, x86_64-ios-simulator ]\n"" umbrella: System\n""allowable-clients:\n"" - targets: [ i386-macos ]\n"" clients: [ ClientA ]\n""exports:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symA ]\n"" objc-classes: [ Class1 ]\n"" weak-symbols: [ _symC ]\n"" - targets: [ x86_64-ios-simulator ]\n"" symbols: [ _symB ]\n""...\n";InterfaceFile File;TargetList Targets = {Target(AK_i386, PLATFORM_MACOS),Target(AK_x86_64, PLATFORM_IOSSIMULATOR),};UUIDs uuids = {{Targets[0], "00000000-0000-0000-0000-000000000000"},{Targets[1], "11111111-1111-1111-1111-111111111111"}};File.setInstallName("Umbrella.framework/Umbrella");File.setFileType(FileType::TBD_V4);File.addTargets(Targets);File.addUUID(uuids[0].first, uuids[0].second);File.addUUID(uuids[1].first, uuids[1].second);File.setCurrentVersion(PackedVersion(1, 2, 3));File.setTwoLevelNamespace();File.setInstallAPI(true);File.setApplicationExtensionSafe(true);File.setSwiftABIVersion(5);File.addAllowableClient("ClientA", Targets[0]);File.addParentUmbrella(Targets[0], "System");File.addParentUmbrella(Targets[1], "System");File.addSymbol(SymbolKind::GlobalSymbol, "_symA", {Targets[0]});File.addSymbol(SymbolKind::GlobalSymbol, "_symB", {Targets[1]});File.addSymbol(SymbolKind::GlobalSymbol, "_symC", {Targets[0]},SymbolFlags::WeakDefined);File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", {Targets[0]});SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error Result = TextAPIWriter::writeToStream(OS, File);EXPECT_FALSE(Result);EXPECT_STREQ(TBDv4File, Buffer.c_str());}TEST(TBDv4, WriteMultipleDocuments) {static const char TBDv4Inlines[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n""uuids:\n"" - target: i386-maccatalyst\n"" value: 00000000-0000-0000-0000-000000000002\n"" - target: x86_64-maccatalyst\n"" value: 11111111-1111-1111-1111-111111111112\n""install-name: ""'/System/Library/Frameworks/Umbrella.framework/Umbrella'\n""reexported-libraries:\n"" - targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n"" libraries: [ '/System/Library/Frameworks/A.framework/A' ]\n""--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n""uuids:\n"" - target: i386-maccatalyst\n"" value: 00000000-0000-0000-0000-000000000000\n"" - target: x86_64-maccatalyst\n"" value: 11111111-1111-1111-1111-111111111111\n""install-name: '/System/Library/Frameworks/A.framework/A'\n""exports:\n"" - targets: [ i386-maccatalyst ]\n"" weak-symbols: [ _symC ]\n"" - targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n"" symbols: [ _symA ]\n"" objc-classes: [ Class1 ]\n"" - targets: [ x86_64-maccatalyst ]\n"" symbols: [ _symAB ]\n""...\n";InterfaceFile File;PlatformType Platform = PLATFORM_MACCATALYST;TargetList Targets = {Target(AK_i386, Platform),Target(AK_x86_64, Platform),};UUIDs Uuids = {{Targets[0], "00000000-0000-0000-0000-000000000002"},{Targets[1], "11111111-1111-1111-1111-111111111112"}};File.setInstallName("/System/Library/Frameworks/Umbrella.framework/Umbrella");File.setFileType(FileType::TBD_V4);File.addTargets(Targets);File.addUUID(Uuids[0].first, Uuids[0].second);File.addUUID(Uuids[1].first, Uuids[1].second);File.setCompatibilityVersion(PackedVersion(1, 0, 0));File.setCurrentVersion(PackedVersion(1, 0, 0));File.setTwoLevelNamespace();File.setApplicationExtensionSafe(true);File.addReexportedLibrary("/System/Library/Frameworks/A.framework/A",Targets[0]);File.addReexportedLibrary("/System/Library/Frameworks/A.framework/A",Targets[1]);// Write Second DocumentUuids = {{Targets[0], "00000000-0000-0000-0000-000000000000"},{Targets[1], "11111111-1111-1111-1111-111111111111"}};InterfaceFile Document;Document.setInstallName("/System/Library/Frameworks/A.framework/A");Document.setFileType(FileType::TBD_V4);Document.addTargets(Targets);Document.addUUID(Uuids[0].first, Uuids[0].second);Document.addUUID(Uuids[1].first, Uuids[1].second);Document.setCompatibilityVersion(PackedVersion(1, 0, 0));Document.setCurrentVersion(PackedVersion(1, 0, 0));Document.setTwoLevelNamespace();Document.setApplicationExtensionSafe(true);Document.addSymbol(SymbolKind::GlobalSymbol, "_symA", Targets);Document.addSymbol(SymbolKind::GlobalSymbol, "_symAB", {Targets[1]});Document.addSymbol(SymbolKind::GlobalSymbol, "_symC", {Targets[0]},SymbolFlags::WeakDefined);Document.addSymbol(SymbolKind::ObjectiveCClass, "Class1", Targets);File.addDocument(std::make_shared<InterfaceFile>(std::move(Document)));SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error Result = TextAPIWriter::writeToStream(OS, File);EXPECT_FALSE(Result);EXPECT_STREQ(TBDv4Inlines, Buffer.c_str());}TEST(TBDv4, MultipleTargets) {static const char TBDv4MultipleTargets[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-maccatalyst, x86_64-tvos, arm64-ios ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4MultipleTargets, "Test.tbd"));EXPECT_TRUE(!!Result);PlatformSet Platforms;Platforms.insert(PLATFORM_MACCATALYST);Platforms.insert(PLATFORM_TVOS);Platforms.insert(PLATFORM_IOS);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(AK_x86_64 | AK_arm64 | AK_i386, File->getArchitectures());EXPECT_EQ(Platforms.size(), File->getPlatforms().size());for (auto Platform : File->getPlatforms())EXPECT_EQ(Platforms.count(Platform), 1U);SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4MultipleTargets),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, MultipleTargetsSameArch) {static const char TBDv4TargetsSameArch[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-tvos , x86_64-maccatalyst ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4TargetsSameArch, "Test.tbd"));EXPECT_TRUE(!!Result);PlatformSet Platforms;Platforms.insert(PLATFORM_TVOS);Platforms.insert(PLATFORM_MACCATALYST);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures());EXPECT_EQ(Platforms.size(), File->getPlatforms().size());for (auto Platform : File->getPlatforms())EXPECT_EQ(Platforms.count(Platform), 1U);SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4TargetsSameArch),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, MultipleTargetsSamePlatform) {static const char TBDv4MultipleTargetsSamePlatform[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ armv7k-ios , arm64-ios]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result = TextAPIReader::get(MemoryBufferRef(TBDv4MultipleTargetsSamePlatform, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(AK_arm64 | AK_armv7k, File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_IOS, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4MultipleTargetsSamePlatform),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_maccatalyst) {static const char TBDv4TargetMacCatalyst[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-maccatalyst ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4TargetMacCatalyst, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_MACCATALYST, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4TargetMacCatalyst),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_x86_ios) {static const char TBDv4Targetx86iOS[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-ios ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4Targetx86iOS, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_IOS, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4Targetx86iOS),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_arm_bridgeOS) {static const char TBDv4PlatformBridgeOS[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ armv7k-bridgeos ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4PlatformBridgeOS, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_BRIDGEOS, *File->getPlatforms().begin());EXPECT_EQ(ArchitectureSet(AK_armv7k), File->getArchitectures());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4PlatformBridgeOS),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_arm_iOS) {static const char TBDv4ArchArm64e[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ arm64e-ios ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4ArchArm64e, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_IOS, *File->getPlatforms().begin());EXPECT_EQ(ArchitectureSet(AK_arm64e), File->getArchitectures());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4ArchArm64e), stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_x86_macos) {static const char TBDv4Targetx86MacOS[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-macos ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4Targetx86MacOS, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_MACOS, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4Targetx86MacOS),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_x86_ios_simulator) {static const char TBDv4Targetx86iOSSim[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-ios-simulator ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4Targetx86iOSSim, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_IOSSIMULATOR, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4Targetx86iOSSim),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_x86_tvos_simulator) {static const char TBDv4x86tvOSSim[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-tvos-simulator ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4x86tvOSSim, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_TVOSSIMULATOR, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4x86tvOSSim), stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_i386_watchos_simulator) {static const char TBDv4i386watchOSSim[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-watchos-simulator ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4i386watchOSSim, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_i386), File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_WATCHOSSIMULATOR, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4i386watchOSSim),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Target_i386_driverkit) {static const char TBDv4i386DriverKit[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-driverkit ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4i386DriverKit, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(ArchitectureSet(AK_i386), File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(PLATFORM_DRIVERKIT, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4i386DriverKit),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Swift_1) {static const char TBDv4SwiftVersion1[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-macos ]\n""install-name: Test.dylib\n""swift-abi-version: 1\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4SwiftVersion1, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(1U, File->getSwiftABIVersion());// No writer test because we emit "swift-abi-version:1.0".}TEST(TBDv4, Swift_2) {static const char TBDv4Swift2[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-macos ]\n""install-name: Test.dylib\n""swift-abi-version: 2\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4Swift2, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(2U, File->getSwiftABIVersion());// No writer test because we emit "swift-abi-version:2.0".}TEST(TBDv4, Swift_5) {static const char TBDv4SwiftVersion5[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-macos ]\n""install-name: Test.dylib\n""swift-abi-version: 5\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4SwiftVersion5, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(5U, File->getSwiftABIVersion());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4SwiftVersion5),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, Swift_99) {static const char TBDv4SwiftVersion99[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-macos ]\n""install-name: Test.dylib\n""swift-abi-version: 99\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4SwiftVersion99, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V4, File->getFileType());EXPECT_EQ(99U, File->getSwiftABIVersion());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv4SwiftVersion99),stripWhitespace(Buffer.c_str()));}TEST(TBDv4, InvalidArchitecture) {static const char TBDv4UnknownArch[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ foo-macos ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4UnknownArch, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:3:12: error: unknown ""architecture\ntargets: [ foo-macos ]\n"" ^~~~~~~~~~\n",ErrorMessage);}TEST(TBDv4, InvalidPlatform) {static const char TBDv4FInvalidPlatform[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-maos ]\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4FInvalidPlatform, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:3:12: error: unknown platform\ntargets: ""[ x86_64-maos ]\n"" ^~~~~~~~~~~~\n",ErrorMessage);}TEST(TBDv4, MalformedFile1) {static const char TBDv4MalformedFile1[] = "--- !tapi-tbd\n""tbd-version: 4\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4MalformedFile1, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key ""'targets'\ntbd-version: 4\n^\n",ErrorMessage);}TEST(TBDv4, MalformedFile2) {static const char TBDv4MalformedFile2[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-macos ]\n""install-name: Test.dylib\n""foobar: \"unsupported key\"\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4MalformedFile2, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:5:1: error: unknown key 'foobar'\nfoobar: ""\"unsupported key\"\n^~~~~~\n",ErrorMessage);}TEST(TBDv4, MalformedFile3) {static const char TBDv4MalformedSwift[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ x86_64-macos ]\n""install-name: Test.dylib\n""swift-abi-version: 1.1\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv4MalformedSwift, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:5:20: error: invalid Swift ABI ""version.\nswift-abi-version: 1.1\n ^~~\n",ErrorMessage);}TEST(TBDv4, InterfaceEquality) {static const char TBDv4File[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n""uuids:\n"" - target: i386-macos\n"" value: 00000000-0000-0000-0000-000000000000\n"" - target: x86_64-macos\n"" value: 11111111-1111-1111-1111-111111111111\n"" - target: x86_64-ios\n"" value: 11111111-1111-1111-1111-111111111111\n""flags: [ flat_namespace, installapi ]\n""install-name: Umbrella.framework/Umbrella\n""current-version: 1.2.3\n""compatibility-version: 1.2\n""swift-abi-version: 5\n""parent-umbrella:\n"" - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n"" umbrella: System\n""allowable-clients:\n"" - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n"" clients: [ ClientA ]\n""reexported-libraries:\n"" - targets: [ i386-macos ]\n"" libraries: [ /System/Library/Frameworks/A.framework/A ]\n""exports:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symA ]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n"" - targets: [ x86_64-ios ]\n"" symbols: [_symB]\n"" - targets: [ x86_64-macos, x86_64-ios ]\n"" symbols: [_symAB]\n""reexports:\n"" - targets: [ i386-macos ]\n"" symbols: [_symC]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n""undefineds:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symD ]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n""tbd-version: 4\n""targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n""uuids:\n"" - target: i386-maccatalyst\n"" value: 00000000-0000-0000-0000-000000000000\n"" - target: x86_64-maccatalyst\n"" value: 11111111-1111-1111-1111-111111111111\n""install-name: '/System/Library/Frameworks/A.framework/A'\n""exports:\n"" - targets: [ i386-maccatalyst ]\n"" weak-symbols: [ _symC ]\n"" - targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n"" symbols: [ _symA ]\n"" objc-classes: [ Class1 ]\n"" - targets: [ x86_64-maccatalyst ]\n"" symbols: [ _symAB ]\n""...\n";Expected<TBDFile> ResultA =TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestA.tbd"));EXPECT_TRUE(!!ResultA);InterfaceFile FileA = std::move(*ResultA.get());Expected<TBDFile> ResultB =TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestB.tbd"));EXPECT_TRUE(!!ResultB);InterfaceFile FileB = std::move(*ResultB.get());EXPECT_TRUE(FileA == FileB);}TEST(TBDv4, InterfaceDiffVersionsEquality) {static const char TBDv4File[] ="--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-macos, x86_64-macos ]\n""uuids:\n"" - target: i386-macos\n"" value: 00000000-0000-0000-0000-000000000000\n"" - target: x86_64-macos\n"" value: 11111111-1111-1111-1111-111111111111\n""flags: [ installapi ]\n""install-name: Umbrella.framework/Umbrella\n""current-version: 1.2.3\n""compatibility-version: 1.0\n""swift-abi-version: 5\n""parent-umbrella:\n"" - targets: [ i386-macos, x86_64-macos ]\n"" umbrella: System\n""allowable-clients:\n"" - targets: [ i386-macos, x86_64-macos ]\n"" clients: [ ClientA ]\n""reexported-libraries:\n"" - targets: [ i386-macos ]\n"" libraries: [ /System/Library/Frameworks/A.framework/A ]\n""exports:\n"" - targets: [ i386-macos ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ class3]\n"" objc-eh-types: []\n"" objc-ivars: [ class1._ivar3 ]\n"" weak-symbols: [ _weak3 ]\n"" - targets: [ x86_64-macos ]\n"" symbols: [_symAB]\n"" - targets: [ i386-macos, x86_64-macos ]\n"" symbols: [_symA]\n"" objc-classes: [ class1, class2 ]\n"" objc-eh-types: [ class1 ]\n"" objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"" weak-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv3 ]\n""undefineds:\n"" - targets: [ i386-macos ]\n"" symbols: [ _symC ]\n"" objc-classes: []\n"" objc-eh-types: []\n"" objc-ivars: []\n"" weak-symbols: []\n"" thread-local-symbols: []\n""...\n";static const char TBDv3File[] ="--- !tapi-tbd-v3\n""archs: [ i386, x86_64 ]\n""uuids: [ 'i386: 00000000-0000-0000-0000-000000000000',\n"" 'x86_64: 22222222-2222-2222-2222-222222222222']\n""platform: macosx\n""flags: [ installapi ]\n""install-name: Umbrella.framework/Umbrella\n""current-version: 1.2.3\n""compatibility-version: 1.0\n""swift-abi-version: 5\n""parent-umbrella: System\n""exports:\n"" - archs: [ i386, x86_64 ]\n"" allowable-clients: [ ClientA ]\n"" symbols: [ _symA ]\n"" objc-classes: [ class1, class2 ]\n"" objc-eh-types: [ class1 ]\n"" objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"" weak-def-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv3 ]\n"" - archs: [ i386 ]\n"" re-exports: [ /System/Library/Frameworks/A.framework/A ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ class3 ]\n"" objc-ivars: [ class1._ivar3 ]\n"" weak-def-symbols: [ _weak3 ]\n"" - archs: [ x86_64 ]\n"" symbols: [ _symAB ]\n""undefineds:\n"" - archs: [ i386 ]\n"" symbols: [ _symC ]\n""...\n";Expected<TBDFile> ResultA =TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestA.tbd"));EXPECT_TRUE(!!ResultA);InterfaceFile FileA = std::move(*ResultA.get());Expected<TBDFile> ResultB =TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));EXPECT_TRUE(!!ResultB);InterfaceFile FileB = std::move(*ResultB.get());EXPECT_NE(FileA.uuids(), FileB.uuids());EXPECT_TRUE(FileA == FileB);}TEST(TBDv4, InterfaceInequality) {static const char TBDv4File[] = "--- !tapi-tbd\n""tbd-version: 4\n""targets: [ i386-macos, x86_64-macos ]\n""install-name: Umbrella.framework/Umbrella\n""...\n";Expected<TBDFile> ResultA =TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestA.tbd"));EXPECT_TRUE(!!ResultA);InterfaceFile FileA = std::move(*ResultA.get());Expected<TBDFile> ResultB =TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestB.tbd"));EXPECT_TRUE(!!ResultB);InterfaceFile FileB = std::move(*ResultB.get());EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addTarget(Target(AK_x86_64, PLATFORM_IOS));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->setCurrentVersion(PackedVersion(1, 2, 3));File->setCompatibilityVersion(PackedVersion(1, 0, 0));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) { File->setSwiftABIVersion(5); }));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->setTwoLevelNamespace(false);}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) { File->setInstallAPI(true); }));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->setApplicationExtensionSafe(false);}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addParentUmbrella(Target(AK_x86_64, PLATFORM_MACOS), "System.dylib");}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addAllowableClient("ClientA", Target(AK_i386, PLATFORM_MACOS));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addReexportedLibrary("/System/Library/Frameworks/A.framework/A",Target(AK_i386, PLATFORM_MACOS));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addSymbol(SymbolKind::GlobalSymbol, "_symA",{Target(AK_x86_64, PLATFORM_MACOS)});}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {InterfaceFile Document;Document.addTargets(TargetList{Target(AK_i386, PLATFORM_MACOS),Target(AK_x86_64, PLATFORM_MACOS)});Document.setInstallName("/System/Library/Frameworks/A.framework/A");File->addDocument(std::make_shared<InterfaceFile>(std::move(Document)));}));}} // end namespace TBDv4
//===-- TextStubV3Tests.cpp - TBD V3 File Test ----------------------------===////// 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////===-----------------------------------------------------------------------===/#include "TextStubHelpers.h"#include "llvm/TextAPI/InterfaceFile.h"#include "llvm/TextAPI/TextAPIReader.h"#include "llvm/TextAPI/TextAPIWriter.h"#include "gtest/gtest.h"#include <string>#include <vector>using namespace llvm;using namespace llvm::MachO;static ExportedSymbol TBDv3Symbols[] = {{SymbolKind::GlobalSymbol, "$ld$hide$os9.0$_sym1", false, false},{SymbolKind::GlobalSymbol, "_sym1", false, false},{SymbolKind::GlobalSymbol, "_sym2", false, false},{SymbolKind::GlobalSymbol, "_sym3", false, false},{SymbolKind::GlobalSymbol, "_sym4", false, false},{SymbolKind::GlobalSymbol, "_sym5", false, false},{SymbolKind::GlobalSymbol, "_tlv1", false, true},{SymbolKind::GlobalSymbol, "_tlv3", false, true},{SymbolKind::GlobalSymbol, "_weak1", true, false},{SymbolKind::GlobalSymbol, "_weak2", true, false},{SymbolKind::GlobalSymbol, "_weak3", true, false},{SymbolKind::ObjectiveCClass, "class1", false, false},{SymbolKind::ObjectiveCClass, "class2", false, false},{SymbolKind::ObjectiveCClass, "class3", false, false},{SymbolKind::ObjectiveCClassEHType, "class1", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar1", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar2", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar3", false, false},};namespace TBDv3 {TEST(TBDv3, ReadFile) {static const char TBDv3File1[] ="--- !tapi-tbd-v3\n""archs: [ armv7, arm64 ]\n""uuids: [ 'armv7: 00000000-0000-0000-0000-000000000000',\n"" 'arm64: 11111111-1111-1111-1111-111111111111']\n""platform: ios\n""flags: [ installapi ]\n""install-name: Test.dylib\n""current-version: 2.3.4\n""compatibility-version: 1.0\n""swift-abi-version: 1.1\n""parent-umbrella: Umbrella.dylib\n""exports:\n"" - archs: [ armv7, arm64 ]\n"" allowable-clients: [ clientA ]\n"" re-exports: [ /usr/lib/libfoo.dylib ]\n"" symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"" objc-classes: [ class1, class2 ]\n"" objc-eh-types: [ class1 ]\n"" objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"" weak-def-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv3 ]\n"" - archs: [ armv7 ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ class3 ]\n"" objc-ivars: [ class1._ivar3 ]\n"" weak-def-symbols: [ _weak3 ]\n"" thread-local-symbols: [ _tlv3 ]\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3File1, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());auto Archs = AK_armv7 | AK_arm64;auto Platform = PLATFORM_IOS;TargetList Targets;for (auto &&arch : Archs)Targets.emplace_back(Target(arch, Platform));EXPECT_EQ(Archs, File->getArchitectures());UUIDs Uuids = {{Target(AK_armv7, PLATFORM_UNKNOWN),"00000000-0000-0000-0000-000000000000"},{Target(AK_arm64, PLATFORM_UNKNOWN),"11111111-1111-1111-1111-111111111111"}};EXPECT_EQ(Uuids, File->uuids());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());EXPECT_EQ(std::string("Test.dylib"), File->getInstallName());EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());EXPECT_EQ(2U, File->getSwiftABIVersion());EXPECT_EQ(ObjCConstraintType::Retain_Release, File->getObjCConstraint());EXPECT_TRUE(File->isTwoLevelNamespace());EXPECT_TRUE(File->isApplicationExtensionSafe());EXPECT_TRUE(File->isInstallAPI());InterfaceFileRef client("clientA", Targets);InterfaceFileRef reexport("/usr/lib/libfoo.dylib", Targets);EXPECT_EQ(1U, File->allowableClients().size());EXPECT_EQ(client, File->allowableClients().front());EXPECT_EQ(1U, File->reexportedLibraries().size());EXPECT_EQ(reexport, File->reexportedLibraries().front());ExportedSymbolSeq Exports;for (const auto *Sym : File->symbols()) {EXPECT_FALSE(Sym->isWeakReferenced());EXPECT_FALSE(Sym->isUndefined());Exports.emplace_back(ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),Sym->isWeakDefined(), Sym->isThreadLocalValue()});}llvm::sort(Exports);EXPECT_EQ(sizeof(TBDv3Symbols) / sizeof(ExportedSymbol), Exports.size());EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), std::begin(TBDv3Symbols)));}TEST(TBDv3, ReadMultipleDocuments) {static const char TBDv3Inlines[] ="--- !tapi-tbd-v3\n""archs: [ armv7, arm64 ]\n""uuids: [ 'armv7: 00000000-0000-0000-0000-000000000000',\n"" 'arm64: 11111111-1111-1111-1111-111111111111']\n""platform: ios\n""install-name: Test.dylib\n""current-version: 2.3.4\n""compatibility-version: 1.0\n""swift-abi-version: 1.1\n""parent-umbrella: Umbrella.dylib\n""exports:\n"" - archs: [ armv7, arm64 ]\n"" allowable-clients: [ clientA ]\n"" re-exports: [ /usr/lib/libfoo.dylib,\n"" TestInline.dylib ]\n"" symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"" objc-classes: [ class1, class2 ]\n"" objc-eh-types: [ class1 ]\n"" objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"" weak-def-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv3 ]\n"" - archs: [ armv7 ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ class3 ]\n"" objc-ivars: [ class1._ivar3 ]\n"" weak-def-symbols: [ _weak3 ]\n"" thread-local-symbols: [ _tlv3 ]\n""--- !tapi-tbd-v3\n""archs: [ armv7, arm64 ]\n""uuids: [ 'armv7: 00000000-0000-0000-0000-000000000000',\n"" 'arm64: 11111111-1111-1111-1111-111111111111']\n""platform: ios\n""install-name: TestInline.dylib\n""swift-abi-version: 1.1\n""exports:\n"" - archs: [ armv7, arm64 ]\n"" symbols: [ _sym5, _sym6 ]\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Inlines, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(File->documents().size(), 1U);EXPECT_EQ(FileType::TBD_V3, File->getFileType());auto Archs = AK_armv7 | AK_arm64;auto Platform = PLATFORM_IOS;TargetList Targets;for (auto &&arch : Archs)Targets.emplace_back(Target(arch, Platform));EXPECT_EQ(Archs, File->getArchitectures());UUIDs Uuids = {{Target(AK_armv7, PLATFORM_UNKNOWN),"00000000-0000-0000-0000-000000000000"},{Target(AK_arm64, PLATFORM_UNKNOWN),"11111111-1111-1111-1111-111111111111"}};EXPECT_EQ(Uuids, File->uuids());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());EXPECT_EQ(std::string("Test.dylib"), File->getInstallName());EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());EXPECT_EQ(2U, File->getSwiftABIVersion());EXPECT_EQ(ObjCConstraintType::Retain_Release, File->getObjCConstraint());EXPECT_TRUE(File->isTwoLevelNamespace());EXPECT_TRUE(File->isApplicationExtensionSafe());EXPECT_FALSE(File->isInstallAPI());InterfaceFileRef Client("clientA", Targets);const std::vector<InterfaceFileRef> Reexports = {InterfaceFileRef("/usr/lib/libfoo.dylib", Targets),InterfaceFileRef("TestInline.dylib", Targets)};EXPECT_EQ(1U, File->allowableClients().size());EXPECT_EQ(Client, File->allowableClients().front());EXPECT_EQ(2U, File->reexportedLibraries().size());EXPECT_EQ(Reexports, File->reexportedLibraries());ExportedSymbolSeq Exports;for (const auto *Sym : File->symbols()) {EXPECT_FALSE(Sym->isWeakReferenced());EXPECT_FALSE(Sym->isUndefined());Exports.emplace_back(ExportedSymbol{Sym->getKind(), Sym->getName().str(),Sym->isWeakDefined(),Sym->isThreadLocalValue()});}llvm::sort(Exports);EXPECT_EQ(sizeof(TBDv3Symbols) / sizeof(ExportedSymbol), Exports.size());EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), std::begin(TBDv3Symbols)));// Check Second DocumentExports.clear();TBDReexportFile Document = File->documents().front();EXPECT_EQ(FileType::TBD_V3, Document->getFileType());EXPECT_EQ(Archs, Document->getArchitectures());EXPECT_EQ(Uuids, Document->uuids());EXPECT_EQ(Platform, *Document->getPlatforms().begin());EXPECT_EQ(std::string("TestInline.dylib"), Document->getInstallName());EXPECT_EQ(PackedVersion(1, 0, 0), Document->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), Document->getCompatibilityVersion());EXPECT_EQ(2U, Document->getSwiftABIVersion());for (const auto *Sym : Document->symbols()) {EXPECT_FALSE(Sym->isWeakReferenced());EXPECT_FALSE(Sym->isUndefined());Exports.emplace_back(ExportedSymbol{Sym->getKind(), Sym->getName().str(),Sym->isWeakDefined(),Sym->isThreadLocalValue()});}llvm::sort(Exports);ExportedSymbolSeq DocumentSymbols{{SymbolKind::GlobalSymbol, "_sym5", false, false},{SymbolKind::GlobalSymbol, "_sym6", false, false},};EXPECT_EQ(DocumentSymbols.size(), Exports.size());EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), DocumentSymbols.begin()));}TEST(TBDv3, WriteFile) {static const char TBDv3File3[] ="--- !tapi-tbd-v3\n""archs: [ i386, x86_64 ]\n""platform: macosx\n""install-name: '/usr/lib/libfoo.dylib'\n""current-version: 1.2.3\n""compatibility-version: 0\n""swift-abi-version: 5\n""exports:\n"" - archs: [ i386 ]\n"" symbols: [ _sym1 ]\n"" weak-def-symbols: [ _sym2 ]\n"" thread-local-symbols: [ _sym3 ]\n"" - archs: [ x86_64 ]\n"" allowable-clients: [ clientA ]\n"" re-exports: [ '/usr/lib/libfoo.dylib' ]\n"" objc-classes: [ Class1 ]\n"" objc-eh-types: [ Class1 ]\n"" objc-ivars: [ Class1._ivar1 ]\n""...\n";InterfaceFile File;TargetList Targets;for (auto &&arch : AK_i386 | AK_x86_64)Targets.emplace_back(Target(arch, PLATFORM_MACOS));File.setPath("libfoo.dylib");File.setInstallName("/usr/lib/libfoo.dylib");File.setFileType(FileType::TBD_V3);File.addTargets(Targets);File.setCurrentVersion(PackedVersion(1, 2, 3));File.setTwoLevelNamespace();File.setApplicationExtensionSafe();File.setSwiftABIVersion(5);File.setObjCConstraint(ObjCConstraintType::Retain_Release);File.addAllowableClient("clientA", Targets[1]);File.addReexportedLibrary("/usr/lib/libfoo.dylib", Targets[1]);File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", {Targets[0]});File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", {Targets[0]},SymbolFlags::WeakDefined);File.addSymbol(SymbolKind::GlobalSymbol, "_sym3", {Targets[0]},SymbolFlags::ThreadLocalValue);File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", {Targets[1]});File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]});File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1",{Targets[1]});SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error Result = TextAPIWriter::writeToStream(OS, File);EXPECT_FALSE(Result);EXPECT_STREQ(TBDv3File3, Buffer.c_str());}TEST(TBDv3, WriteMultipleDocuments) {static const char TBDv3Inlines[] ="--- !tapi-tbd-v3\n""archs: [ i386, x86_64 ]\n""platform: zippered\n""install-name: '/usr/lib/libfoo.dylib'\n""current-version: 1.2.3\n""compatibility-version: 0\n""swift-abi-version: 5\n""exports:\n"" - archs: [ x86_64 ]\n"" allowable-clients: [ clientA ]\n"" re-exports: [ '/usr/lib/libbar.dylib' ]\n"" - archs: [ i386, x86_64 ]\n"" symbols: [ _sym1 ]\n"" objc-classes: [ Class1 ]\n"" objc-eh-types: [ Class1 ]\n"" objc-ivars: [ Class1._ivar1 ]\n"" weak-def-symbols: [ _sym2 ]\n"" thread-local-symbols: [ _symA ]\n""--- !tapi-tbd-v3\n""archs: [ i386 ]\n""platform: macosx\n""install-name: '/usr/lib/libbar.dylib'\n""current-version: 0\n""compatibility-version: 0\n""swift-abi-version: 5\n""objc-constraint: none\n""exports:\n"" - archs: [ i386 ]\n"" symbols: [ _sym3, _sym4 ]\n""...\n";InterfaceFile File;TargetList Targets;for (auto &&arch : AK_i386 | AK_x86_64) {Targets.emplace_back(Target(arch, PLATFORM_MACOS));Targets.emplace_back(Target(arch, PLATFORM_MACCATALYST));}File.addTargets(Targets);File.setPath("libfoo.dylib");File.setInstallName("/usr/lib/libfoo.dylib");File.setFileType(FileType::TBD_V3);File.setCurrentVersion(PackedVersion(1, 2, 3));File.setTwoLevelNamespace();File.setApplicationExtensionSafe();File.setSwiftABIVersion(5);File.setObjCConstraint(ObjCConstraintType::Retain_Release);File.addAllowableClient("clientA", Targets[2]);File.addReexportedLibrary("/usr/lib/libbar.dylib", Targets[2]);File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", Targets);File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", Targets,SymbolFlags::WeakDefined);File.addSymbol(SymbolKind::GlobalSymbol, "_symA", Targets,SymbolFlags::ThreadLocalValue);File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", Targets);File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", Targets);File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1",Targets);// Inline documentInterfaceFile Document;Targets = {Target(AK_i386, PLATFORM_MACOS)};Document.addTargets(Targets);Document.setPath("libbar.dylib");Document.setInstallName("/usr/lib/libbar.dylib");Document.setFileType(FileType::TBD_V3);Document.setTwoLevelNamespace();Document.setApplicationExtensionSafe();Document.setSwiftABIVersion(5);Document.addSymbol(SymbolKind::GlobalSymbol, "_sym3", Targets);Document.addSymbol(SymbolKind::GlobalSymbol, "_sym4", Targets);File.addDocument(std::make_shared<InterfaceFile>(std::move(Document)));SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error Result = TextAPIWriter::writeToStream(OS, File);EXPECT_FALSE(Result);EXPECT_STREQ(TBDv3Inlines, Buffer.c_str());}TEST(TBDv3, Platform_macOS) {static const char TBDv3PlatformMacOS[] = "--- !tapi-tbd-v3\n""archs: [ x86_64 ]\n""platform: macosx\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3PlatformMacOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_MACOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3PlatformMacOS),stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_iOS) {static const char TBDv3PlatformiOS[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3PlatformiOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_IOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3PlatformiOS), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_watchOS) {static const char TBDv3watchOS[] = "--- !tapi-tbd-v3\n""archs: [ armv7k ]\n""platform: watchos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3watchOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_WATCHOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3watchOS), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_tvOS) {static const char TBDv3PlatformtvOS[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: tvos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3PlatformtvOS, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());auto Platform = PLATFORM_TVOS;EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_FALSE(WriteResult);EXPECT_EQ(stripWhitespace(TBDv3PlatformtvOS),stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_bridgeOS) {static const char TBDv3BridgeOS[] = "--- !tapi-tbd-v3\n""archs: [ armv7k ]\n""platform: bridgeos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3BridgeOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_BRIDGEOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3BridgeOS), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_macCatalyst) {static const char TBDv3PlatformiOSmac[] = "--- !tapi-tbd-v3\n""archs: [ armv7k ]\n""platform: iosmac\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3PlatformiOSmac, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_MACCATALYST;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3PlatformiOSmac),stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_zippered) {static const char TBDv3PlatformZippered[] = "--- !tapi-tbd-v3\n""archs: [ armv7k ]\n""platform: zippered\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3PlatformZippered, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());PlatformSet Platforms;Platforms.insert(PLATFORM_MACOS);Platforms.insert(PLATFORM_MACCATALYST);EXPECT_EQ(Platforms.size(), File->getPlatforms().size());for (auto Platform : File->getPlatforms())EXPECT_EQ(Platforms.count(Platform), 1U);SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3PlatformZippered),stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_iOSSim) {static const char TBDv3PlatformiOSsim[] = "--- !tapi-tbd-v3\n""archs: [ x86_64 ]\n""platform: ios\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3PlatformiOSsim, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_IOSSIMULATOR;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3PlatformiOSsim),stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_watchOSSim) {static const char TBDv3watchOSsim[] = "--- !tapi-tbd-v3\n""archs: [ x86_64 ]\n""platform: watchos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3watchOSsim, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_WATCHOSSIMULATOR;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3watchOSsim), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Platform_tvOSSim) {static const char TBDv3PlatformtvOSsim[] = "--- !tapi-tbd-v3\n""archs: [ x86_64 ]\n""platform: tvos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3PlatformtvOSsim, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());auto Platform = PLATFORM_TVOSSIMULATOR;EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3PlatformtvOSsim),stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Arch_arm64e) {static const char TBDv3ArchArm64e[] = "--- !tapi-tbd-v3\n""archs: [ arm64, arm64e ]\n""platform: ios\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3ArchArm64e, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());auto Platform = PLATFORM_IOS;auto Archs = AK_arm64 | AK_arm64e;EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());EXPECT_EQ(Archs, File->getArchitectures());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3ArchArm64e), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Swift_1_0) {static const char TBDv3Swift1[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-abi-version: 1.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Swift1, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(1U, File->getSwiftABIVersion());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3Swift1), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Swift_1_1) {static const char TBDv3Swift1Dot[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-abi-version: 1.1\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Swift1Dot, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(2U, File->getSwiftABIVersion());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3Swift1Dot), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Swift_2_0) {static const char TBDv3Swift2[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-abi-version: 2.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Swift2, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(3U, File->getSwiftABIVersion());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3Swift2), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Swift_3_0) {static const char TBDv3Swift3[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-abi-version: 3.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Swift3, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(4U, File->getSwiftABIVersion());SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error WriteResult = TextAPIWriter::writeToStream(OS, *File);EXPECT_TRUE(!WriteResult);EXPECT_EQ(stripWhitespace(TBDv3Swift3), stripWhitespace(Buffer.c_str()));}TEST(TBDv3, Swift_4_0) {static const char TBDv3Swift4[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-abi-version: 4.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Swift4, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:5:20: error: invalid Swift ABI ""version.\nswift-abi-version: 4.0\n ^~~\n",ErrorMessage);}TEST(TBDv3, Swift_5) {static const char TBDv3Swift5[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-abi-version: 5\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Swift5, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(5U, File->getSwiftABIVersion());}TEST(TBDv3, Swift_99) {static const char TBDv3Swift99[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-abi-version: 99\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3Swift99, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V3, File->getFileType());EXPECT_EQ(99U, File->getSwiftABIVersion());}TEST(TBDv3, UnknownArchitecture) {static const char TBDv3FileUnknownArch[] = "--- !tapi-tbd-v3\n""archs: [ foo ]\n""platform: macosx\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3FileUnknownArch, "Test.tbd"));EXPECT_TRUE(!!Result);}TEST(TBDv3, UnknownPlatform) {static const char TBDv3FileUnknownPlatform[] = "--- !tapi-tbd-v3\n""archs: [ i386 ]\n""platform: newOS\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3FileUnknownPlatform, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: ""newOS\n ^~~~~\n",ErrorMessage);}TEST(TBDv3, MalformedFile1) {static const char TBDv3FileMalformed1[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""foobar: \"Unsupported key\"\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3FileMalformed1, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key ""'platform'\narchs: [ arm64 ]\n^\n",ErrorMessage);}TEST(TBDv3, MalformedFile2) {static const char TBDv3FileMalformed2[] = "--- !tapi-tbd-v3\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""foobar: \"Unsupported key\"\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv3FileMalformed2, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:5:1: error: unknown key 'foobar'\nfoobar: ""\"Unsupported key\"\n^~~~~~\n",ErrorMessage);}TEST(TBDv3, InterfaceEquality) {static const char TBDv3File[] ="--- !tapi-tbd-v3\n""archs: [ armv7, arm64 ]\n""uuids: [ 'armv7: 00000000-0000-0000-0000-000000000000',\n"" 'arm64: 11111111-1111-1111-1111-111111111111']\n""platform: ios\n""flags: [ installapi ]\n""install-name: Test.dylib\n""current-version: 2.3.4\n""compatibility-version: 1.0\n""swift-abi-version: 1.1\n""parent-umbrella: Umbrella.dylib\n""exports:\n"" - archs: [ armv7, arm64 ]\n"" allowable-clients: [ clientA ]\n"" re-exports: [ /usr/lib/libfoo.dylib ]\n"" symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"" objc-classes: [ class1, class2 ]\n"" objc-eh-types: [ class1 ]\n"" objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"" weak-def-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv3 ]\n"" - archs: [ armv7 ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ class3 ]\n"" objc-ivars: [ class1._ivar3 ]\n"" weak-def-symbols: [ _weak3 ]\n"" thread-local-symbols: [ _tlv3 ]\n""--- !tapi-tbd-v3\n""archs: [ i386 ]\n""platform: macosx\n""install-name: '/usr/lib/libbar.dylib'\n""current-version: 0\n""compatibility-version: 0\n""swift-abi-version: 5\n""objc-constraint: none\n""exports:\n"" - archs: [ i386 ]\n"" symbols: [ _sym3, _sym4 ]\n""...\n";Expected<TBDFile> ResultA =TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestA.tbd"));EXPECT_TRUE(!!ResultA);InterfaceFile FileA = std::move(*ResultA.get());Expected<TBDFile> ResultB =TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));EXPECT_TRUE(!!ResultB);InterfaceFile FileB = std::move(*ResultB.get());EXPECT_FALSE(FileA.getPath() == FileB.getPath());EXPECT_TRUE(FileA == FileB);}TEST(TBDv3, InterfaceInequality) {static const char TBDv3File[] = "--- !tapi-tbd-v3\n""archs: [ armv7, arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> ResultA =TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestA.tbd"));EXPECT_TRUE(!!ResultA);InterfaceFile FileA = std::move(*ResultA.get());Expected<TBDFile> ResultB =TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));EXPECT_TRUE(!!ResultB);InterfaceFile FileB = std::move(*ResultB.get());EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addTarget(Target(AK_x86_64, PLATFORM_IOS));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->setCurrentVersion(PackedVersion(1, 2, 3));File->setCompatibilityVersion(PackedVersion(1, 0, 0));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) { File->setSwiftABIVersion(5); }));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->setTwoLevelNamespace(false);}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) { File->setInstallAPI(true); }));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->setApplicationExtensionSafe(false);}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addParentUmbrella(Target(AK_armv7, PLATFORM_IOS), "Umbrella.dylib");}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addAllowableClient("ClientA", Target(AK_armv7, PLATFORM_IOS));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addReexportedLibrary("/System/Library/Frameworks/A.framework/A",Target(AK_armv7, PLATFORM_IOS));}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {File->addSymbol(SymbolKind::GlobalSymbol, "_symA",{Target(AK_arm64, PLATFORM_IOS)});}));EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {InterfaceFile Document;Document.addTargets(TargetList{Target(AK_armv7, PLATFORM_IOS),Target(AK_arm64, PLATFORM_IOS)});Document.setInstallName("/System/Library/Frameworks/A.framework/A");File->addDocument(std::make_shared<InterfaceFile>(std::move(Document)));}));}} // namespace TBDv3
//===-- TextStubV2Tests.cpp - TBD V2 File Test ----------------------------===////// 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////===-----------------------------------------------------------------------===/#include "TextStubHelpers.h"#include "llvm/TextAPI/InterfaceFile.h"#include "llvm/TextAPI/TextAPIReader.h"#include "llvm/TextAPI/TextAPIWriter.h"#include "gtest/gtest.h"#include <string>#include <vector>using namespace llvm;using namespace llvm::MachO;static ExportedSymbol TBDv2Symbols[] = {{SymbolKind::GlobalSymbol, "$ld$hide$os9.0$_sym1", false, false},{SymbolKind::GlobalSymbol, "_sym1", false, false},{SymbolKind::GlobalSymbol, "_sym2", false, false},{SymbolKind::GlobalSymbol, "_sym3", false, false},{SymbolKind::GlobalSymbol, "_sym4", false, false},{SymbolKind::GlobalSymbol, "_sym5", false, false},{SymbolKind::GlobalSymbol, "_tlv1", false, true},{SymbolKind::GlobalSymbol, "_tlv2", false, true},{SymbolKind::GlobalSymbol, "_tlv3", false, true},{SymbolKind::GlobalSymbol, "_weak1", true, false},{SymbolKind::GlobalSymbol, "_weak2", true, false},{SymbolKind::GlobalSymbol, "_weak3", true, false},{SymbolKind::ObjectiveCClass, "class1", false, false},{SymbolKind::ObjectiveCClass, "class2", false, false},{SymbolKind::ObjectiveCClass, "class3", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar1", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar2", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar3", false, false},};namespace TBDv2 {TEST(TBDv2, ReadFile) {static const char TBDv2File1[] ="--- !tapi-tbd-v2\n""archs: [ armv7, armv7s, armv7k, arm64 ]\n""platform: ios\n""flags: [ installapi ]\n""install-name: Test.dylib\n""current-version: 2.3.4\n""compatibility-version: 1.0\n""swift-version: 1.1\n""parent-umbrella: Umbrella.dylib\n""exports:\n"" - archs: [ armv7, armv7s, armv7k, arm64 ]\n"" allowable-clients: [ clientA ]\n"" re-exports: [ /usr/lib/libfoo.dylib ]\n"" symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"" objc-classes: [ _class1, _class2 ]\n"" objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n"" weak-def-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv2 ]\n"" - archs: [ armv7, armv7s, armv7k ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ _class3 ]\n"" objc-ivars: [ _class1._ivar3 ]\n"" weak-def-symbols: [ _weak3 ]\n"" thread-local-symbols: [ _tlv3 ]\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2File1, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64;auto Platform = PLATFORM_IOS;TargetList Targets;for (auto &&arch : Archs)Targets.emplace_back(Target(arch, Platform));EXPECT_EQ(Archs, File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());EXPECT_EQ(std::string("Test.dylib"), File->getInstallName());EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());EXPECT_EQ(2U, File->getSwiftABIVersion());EXPECT_EQ(ObjCConstraintType::Retain_Release, File->getObjCConstraint());EXPECT_TRUE(File->isTwoLevelNamespace());EXPECT_TRUE(File->isApplicationExtensionSafe());EXPECT_TRUE(File->isInstallAPI());InterfaceFileRef client("clientA", Targets);InterfaceFileRef reexport("/usr/lib/libfoo.dylib", Targets);EXPECT_EQ(1U, File->allowableClients().size());EXPECT_EQ(client, File->allowableClients().front());EXPECT_EQ(1U, File->reexportedLibraries().size());EXPECT_EQ(reexport, File->reexportedLibraries().front());ExportedSymbolSeq Exports;for (const auto *Sym : File->symbols()) {EXPECT_FALSE(Sym->isWeakReferenced());EXPECT_FALSE(Sym->isUndefined());Exports.emplace_back(ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),Sym->isWeakDefined(), Sym->isThreadLocalValue()});}llvm::sort(Exports);EXPECT_EQ(sizeof(TBDv2Symbols) / sizeof(ExportedSymbol), Exports.size());EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), std::begin(TBDv2Symbols)));}TEST(TBDv2, ReadFile2) {static const char TBDv2File2[] ="--- !tapi-tbd-v2\n""archs: [ armv7, armv7s, armv7k, arm64 ]\n""platform: ios\n""flags: [ flat_namespace, not_app_extension_safe ]\n""install-name: Test.dylib\n""swift-version: 1.1\n""exports:\n"" - archs: [ armv7, armv7s, armv7k, arm64 ]\n"" symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"" objc-classes: [ _class1, _class2 ]\n"" objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n"" weak-def-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv2 ]\n"" - archs: [ armv7, armv7s, armv7k ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ _class3 ]\n"" objc-ivars: [ _class1._ivar3 ]\n"" weak-def-symbols: [ _weak3 ]\n"" thread-local-symbols: [ _tlv3 ]\n""undefineds:\n"" - archs: [ armv7, armv7s, armv7k, arm64 ]\n"" symbols: [ _undefSym1, _undefSym2, _undefSym3 ]\n"" objc-classes: [ _undefClass1, _undefClass2 ]\n"" objc-ivars: [ _undefClass1._ivar1, _undefClass1._ivar2 ]\n"" weak-ref-symbols: [ _undefWeak1, _undefWeak2 ]\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2File2, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64;auto Platform = PLATFORM_IOS;TargetList Targets;for (auto &&arch : Archs)Targets.emplace_back(Target(arch, Platform));EXPECT_EQ(Archs, File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());EXPECT_EQ(std::string("Test.dylib"), File->getInstallName());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());EXPECT_EQ(2U, File->getSwiftABIVersion());EXPECT_EQ(ObjCConstraintType::Retain_Release, File->getObjCConstraint());EXPECT_FALSE(File->isTwoLevelNamespace());EXPECT_FALSE(File->isApplicationExtensionSafe());EXPECT_FALSE(File->isInstallAPI());EXPECT_EQ(0U, File->allowableClients().size());EXPECT_EQ(0U, File->reexportedLibraries().size());}TEST(TBDv2, WriteFile) {static const char TBDv2File3[] ="--- !tapi-tbd-v2\n""archs: [ i386, x86_64 ]\n""platform: macosx\n""install-name: '/usr/lib/libfoo.dylib'\n""current-version: 1.2.3\n""compatibility-version: 0\n""swift-version: 5\n""exports:\n"" - archs: [ i386 ]\n"" symbols: [ _sym1 ]\n"" weak-def-symbols: [ _sym2 ]\n"" thread-local-symbols: [ _sym3 ]\n"" - archs: [ x86_64 ]\n"" allowable-clients: [ clientA ]\n"" re-exports: [ '/usr/lib/libfoo.dylib' ]\n"" symbols: [ '_OBJC_EHTYPE_$_Class1' ]\n"" objc-classes: [ _Class1 ]\n"" objc-ivars: [ _Class1._ivar1 ]\n""...\n";InterfaceFile File;TargetList Targets;for (auto &&arch : AK_i386 | AK_x86_64)Targets.emplace_back(Target(arch, PLATFORM_MACOS));File.setPath("libfoo.dylib");File.setInstallName("/usr/lib/libfoo.dylib");File.setFileType(FileType::TBD_V2);File.addTargets(Targets);File.setCurrentVersion(PackedVersion(1, 2, 3));File.setTwoLevelNamespace();File.setApplicationExtensionSafe();File.setSwiftABIVersion(5);File.setObjCConstraint(ObjCConstraintType::Retain_Release);File.addAllowableClient("clientA", Targets[1]);File.addReexportedLibrary("/usr/lib/libfoo.dylib", Targets[1]);File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", {Targets[0]});File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", {Targets[0]},SymbolFlags::WeakDefined);File.addSymbol(SymbolKind::GlobalSymbol, "_sym3", {Targets[0]},SymbolFlags::ThreadLocalValue);File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", {Targets[1]});File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]});File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1",{Targets[1]});SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error Result = TextAPIWriter::writeToStream(OS, File);EXPECT_FALSE(Result);EXPECT_STREQ(TBDv2File3, Buffer.c_str());}TEST(TBDv2, Platform_macOS) {static const char TBDv2PlatformMacOS[] = "--- !tapi-tbd-v2\n""archs: [ x86_64 ]\n""platform: macosx\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2PlatformMacOS, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());auto Platform = PLATFORM_MACOS;EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv2, Platform_iOS) {static const char TBDv2PlatformiOS[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2PlatformiOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_IOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv2, Platform_watchOS) {static const char TBDv2PlatformWatchOS[] = "--- !tapi-tbd-v2\n""archs: [ armv7k ]\n""platform: watchos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2PlatformWatchOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_WATCHOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv2, Platform_tvOS) {static const char TBDv2PlatformtvOS[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: tvos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2PlatformtvOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_TVOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv2, Platform_bridgeOS) {static const char TBDv2BridgeOS[] = "--- !tapi-tbd-v2\n""archs: [ armv7k ]\n""platform: bridgeos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2BridgeOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_BRIDGEOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv2, Swift_1_0) {static const char TBDv2Swift1[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 1.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2Swift1, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(1U, File->getSwiftABIVersion());}TEST(TBDv2, Swift_1_1) {static const char TBDv2Swift1dot[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 1.1\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2Swift1dot, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(2U, File->getSwiftABIVersion());}TEST(TBDv2, Swift_2_0) {static const char tbd_v2_swift_2_0[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 2.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(tbd_v2_swift_2_0, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(3U, File->getSwiftABIVersion());}TEST(TBDv2, Swift_3_0) {static const char TBDv2Swift3[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 3.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2Swift3, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(4U, File->getSwiftABIVersion());}TEST(TBDv2, Swift_4_0) {static const char TBDv2Swift4[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 4.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2Swift4, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:5:16: error: invalid Swift ABI ""version.\nswift-version: 4.0\n ^~~\n",ErrorMessage);}TEST(TBDv2, Swift_5) {static const char TBDv2Swift5[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 5\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2Swift5, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(5U, File->getSwiftABIVersion());}TEST(TBDv2, Swift_99) {static const char TBDv2Swift99[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 99\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2Swift99, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V2, File->getFileType());EXPECT_EQ(99U, File->getSwiftABIVersion());}TEST(TBDv2, UnknownArchitecture) {static const char TBDv2FileUnknownArch[] = "--- !tapi-tbd-v2\n""archs: [ foo ]\n""platform: macosx\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2FileUnknownArch, "Test.tbd"));EXPECT_TRUE(!!Result);}TEST(TBDv2, UnknownPlatform) {static const char TBDv2FileUnknownPlatform[] = "--- !tapi-tbd-v2\n""archs: [ i386 ]\n""platform: newOS\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2FileUnknownPlatform, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: ""newOS\n ^~~~~\n",ErrorMessage);}TEST(TBDv2, InvalidPlatform) {static const char TBDv2FileInvalidPlatform[] = "--- !tapi-tbd-v2\n""archs: [ i386 ]\n""platform: iosmac\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2FileInvalidPlatform, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:3:11: error: invalid platform\nplatform: ""iosmac\n ^~~~~~\n",ErrorMessage);}TEST(TBDv2, MalformedFile1) {static const char TBDv2FileMalformed1[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""foobar: \"Unsupported key\"\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2FileMalformed1, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key ""'platform'\narchs: [ arm64 ]\n^\n",ErrorMessage);}TEST(TBDv2, MalformedFile2) {static const char TBDv2FileMalformed2[] = "--- !tapi-tbd-v2\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""foobar: \"Unsupported key\"\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv2FileMalformed2, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:5:1: error: unknown key 'foobar'\nfoobar: ""\"Unsupported key\"\n^~~~~~\n",ErrorMessage);}} // namespace TBDv2
//===-- TextStubV1Tests.cpp - TBD V1 File Test ----------------------------===////// 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////===-----------------------------------------------------------------------===/#include "TextStubHelpers.h"#include "llvm/TextAPI/InterfaceFile.h"#include "llvm/TextAPI/TextAPIReader.h"#include "llvm/TextAPI/TextAPIWriter.h"#include "gtest/gtest.h"#include <string>#include <vector>using namespace llvm;using namespace llvm::MachO;static ExportedSymbol TBDv1Symbols[] = {{SymbolKind::GlobalSymbol, "$ld$hide$os9.0$_sym1", false, false},{SymbolKind::GlobalSymbol, "_sym1", false, false},{SymbolKind::GlobalSymbol, "_sym2", false, false},{SymbolKind::GlobalSymbol, "_sym3", false, false},{SymbolKind::GlobalSymbol, "_sym4", false, false},{SymbolKind::GlobalSymbol, "_sym5", false, false},{SymbolKind::GlobalSymbol, "_tlv1", false, true},{SymbolKind::GlobalSymbol, "_tlv2", false, true},{SymbolKind::GlobalSymbol, "_tlv3", false, true},{SymbolKind::GlobalSymbol, "_weak1", true, false},{SymbolKind::GlobalSymbol, "_weak2", true, false},{SymbolKind::GlobalSymbol, "_weak3", true, false},{SymbolKind::ObjectiveCClass, "class1", false, false},{SymbolKind::ObjectiveCClass, "class2", false, false},{SymbolKind::ObjectiveCClass, "class3", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar1", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar2", false, false},{SymbolKind::ObjectiveCInstanceVariable, "class1._ivar3", false, false},};namespace TBDv1 {TEST(TBDv1, ReadFile) {static const char TBDv1File1[] ="---\n""archs: [ armv7, armv7s, armv7k, arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""current-version: 2.3.4\n""compatibility-version: 1.0\n""swift-version: 1.1\n""exports:\n"" - archs: [ armv7, armv7s, armv7k, arm64 ]\n"" allowed-clients: [ clientA ]\n"" re-exports: [ /usr/lib/libfoo.dylib ]\n"" symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"" objc-classes: [ _class1, _class2 ]\n"" objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n"" weak-def-symbols: [ _weak1, _weak2 ]\n"" thread-local-symbols: [ _tlv1, _tlv2 ]\n"" - archs: [ armv7, armv7s, armv7k ]\n"" symbols: [ _sym5 ]\n"" objc-classes: [ _class3 ]\n"" objc-ivars: [ _class1._ivar3 ]\n"" weak-def-symbols: [ _weak3 ]\n"" thread-local-symbols: [ _tlv3 ]\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1File1, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64;auto Platform = PLATFORM_IOS;TargetList Targets;for (auto &&arch : Archs)Targets.emplace_back(Target(arch, Platform));EXPECT_EQ(Archs, File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());EXPECT_EQ(std::string("Test.dylib"), File->getInstallName());EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());EXPECT_EQ(2U, File->getSwiftABIVersion());EXPECT_EQ(ObjCConstraintType::None, File->getObjCConstraint());EXPECT_TRUE(File->isTwoLevelNamespace());EXPECT_TRUE(File->isApplicationExtensionSafe());EXPECT_FALSE(File->isInstallAPI());InterfaceFileRef client("clientA", Targets);InterfaceFileRef reexport("/usr/lib/libfoo.dylib", Targets);EXPECT_EQ(1U, File->allowableClients().size());EXPECT_EQ(client, File->allowableClients().front());EXPECT_EQ(1U, File->reexportedLibraries().size());EXPECT_EQ(reexport, File->reexportedLibraries().front());ExportedSymbolSeq Exports;for (const auto *Sym : File->symbols()) {EXPECT_FALSE(Sym->isWeakReferenced());EXPECT_FALSE(Sym->isUndefined());Exports.emplace_back(ExportedSymbol{Sym->getKind(), std::string(Sym->getName()),Sym->isWeakDefined(), Sym->isThreadLocalValue()});}llvm::sort(Exports);EXPECT_EQ(sizeof(TBDv1Symbols) / sizeof(ExportedSymbol), Exports.size());EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), std::begin(TBDv1Symbols)));File->addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]});File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1",{Targets[1]});}TEST(TBDv1, ReadFile2) {static const char TBDv1File2[] = "--- !tapi-tbd-v1\n""archs: [ armv7, armv7s, armv7k, arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1File2, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64;auto Platform = PLATFORM_IOS;TargetList Targets;for (auto &&arch : Archs)Targets.emplace_back(Target(arch, Platform));EXPECT_EQ(Archs, File->getArchitectures());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());EXPECT_EQ(std::string("Test.dylib"), File->getInstallName());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion());EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion());EXPECT_EQ(0U, File->getSwiftABIVersion());EXPECT_EQ(ObjCConstraintType::None, File->getObjCConstraint());EXPECT_TRUE(File->isTwoLevelNamespace());EXPECT_TRUE(File->isApplicationExtensionSafe());EXPECT_FALSE(File->isInstallAPI());EXPECT_EQ(0U, File->allowableClients().size());EXPECT_EQ(0U, File->reexportedLibraries().size());}TEST(TBDv1, WriteFile) {static const char TBDv1File3[] ="---\n""archs: [ i386, x86_64 ]\n""platform: macosx\n""install-name: '/usr/lib/libfoo.dylib'\n""current-version: 1.2.3\n""compatibility-version: 0\n""swift-version: 5\n""objc-constraint: retain_release\n""exports:\n"" - archs: [ i386 ]\n"" symbols: [ _sym1 ]\n"" weak-def-symbols: [ _sym2 ]\n"" thread-local-symbols: [ _sym3 ]\n"" - archs: [ x86_64 ]\n"" allowed-clients: [ clientA ]\n"" re-exports: [ '/usr/lib/libfoo.dylib' ]\n"" symbols: [ '_OBJC_EHTYPE_$_Class1' ]\n"" objc-classes: [ _Class1 ]\n"" objc-ivars: [ _Class1._ivar1 ]\n""...\n";InterfaceFile File;TargetList Targets;for (auto &&arch : AK_i386 | AK_x86_64)Targets.emplace_back(Target(arch, PLATFORM_MACOS));File.setPath("libfoo.dylib");File.setInstallName("/usr/lib/libfoo.dylib");File.setFileType(FileType::TBD_V1);File.addTargets(Targets);File.setCurrentVersion(PackedVersion(1, 2, 3));File.setSwiftABIVersion(5);File.setObjCConstraint(ObjCConstraintType::Retain_Release);File.addAllowableClient("clientA", Targets[1]);File.addReexportedLibrary("/usr/lib/libfoo.dylib", Targets[1]);File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", {Targets[0]});File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", {Targets[0]},SymbolFlags::WeakDefined);File.addSymbol(SymbolKind::GlobalSymbol, "_sym3", {Targets[0]},SymbolFlags::ThreadLocalValue);File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", {Targets[1]});File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]});File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1",{Targets[1]});SmallString<4096> Buffer;raw_svector_ostream OS(Buffer);Error Result = TextAPIWriter::writeToStream(OS, File);EXPECT_FALSE(Result);EXPECT_STREQ(TBDv1File3, Buffer.c_str());}TEST(TBDv1, Platform_macOS) {static const char TBDv1PlatformMacOS[] = "---\n""archs: [ x86_64 ]\n""platform: macosx\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1PlatformMacOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_MACOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv1, Platform_iOS) {static const char TBDv1PlatformiOS[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1PlatformiOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_IOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv1, Platform_watchOS) {static const char TBDv1PlatformWatchOS[] = "---\n""archs: [ armv7k ]\n""platform: watchos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1PlatformWatchOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_WATCHOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv1, Platform_tvOS) {static const char TBDv1PlatformtvOS[] = "---\n""archs: [ arm64 ]\n""platform: tvos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1PlatformtvOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_TVOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv1, Platform_bridgeOS) {static const char TBDv1BridgeOS[] = "---\n""archs: [ armv7k ]\n""platform: bridgeos\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1BridgeOS, "Test.tbd"));EXPECT_TRUE(!!Result);auto Platform = PLATFORM_BRIDGEOS;TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(File->getPlatforms().size(), 1U);EXPECT_EQ(Platform, *File->getPlatforms().begin());}TEST(TBDv1, Swift_1_0) {static const char TBDv1Swift1[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 1.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1Swift1, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(1U, File->getSwiftABIVersion());}TEST(TBDv1, Swift_1_1) {static const char TBDv1Swift1dot[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 1.1\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1Swift1dot, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(2U, File->getSwiftABIVersion());}TEST(TBDv1, Swift_2_0) {static const char TBDv1Swift2[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 2.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1Swift2, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(3U, File->getSwiftABIVersion());}TEST(TBDv1, Swift_3_0) {static const char TBDv1Swift3[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 3.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1Swift3, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(4U, File->getSwiftABIVersion());}TEST(TBDv1, Swift_4_0) {static const char TBDv1Swift4[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 4.0\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1Swift4, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:5:16: error: invalid Swift ABI ""version.\nswift-version: 4.0\n ^~~\n",ErrorMessage);}TEST(TBDv1, Swift_5) {static const char TBDv1Swift5[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 5\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1Swift5, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(5U, File->getSwiftABIVersion());}TEST(TBDv1, Swift_99) {static const char TBDv1Swift99[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""swift-version: 99\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1Swift99, "Test.tbd"));EXPECT_TRUE(!!Result);TBDFile File = std::move(Result.get());EXPECT_EQ(FileType::TBD_V1, File->getFileType());EXPECT_EQ(99U, File->getSwiftABIVersion());}TEST(TBDv1, UnknownArchitecture) {static const char TBDv1FileUnknownArch[] = "---\n""archs: [ foo ]\n""platform: macosx\n""install-name: Test.dylib\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1FileUnknownArch, "Test.tbd"));EXPECT_TRUE(!!Result);}TEST(TBDv1, UnknownPlatform) {static const char TBDv1FileUnknownPlatform[] = "---\n""archs: [ i386 ]\n""platform: newOS\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1FileUnknownPlatform, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: ""newOS\n ^~~~~\n",ErrorMessage);}TEST(TBDv1, MalformedFile1) {static const char TBDv1FileMalformed1[] = "---\n""archs: [ arm64 ]\n""foobar: \"Unsupported key\"\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1FileMalformed1, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key ""'platform'\narchs: [ arm64 ]\n^\n",ErrorMessage);}TEST(TBDv1, MalformedFile2) {static const char TBDv1FileMalformed2[] = "---\n""archs: [ arm64 ]\n""platform: ios\n""install-name: Test.dylib\n""foobar: \"Unsupported key\"\n""...\n";Expected<TBDFile> Result =TextAPIReader::get(MemoryBufferRef(TBDv1FileMalformed2, "Test.tbd"));EXPECT_FALSE(!!Result);std::string ErrorMessage = toString(Result.takeError());ASSERT_EQ("malformed file\nTest.tbd:5:1: error: unknown key 'foobar'\nfoobar: ""\"Unsupported key\"\n^~~~~~\n",ErrorMessage);}} // end namespace TBDv1.
//===-- TextStubHelpers.cpp -------------------------------------*- C++ -*-===////// 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////===-----------------------------------------------------------------------===/#include "llvm/Support/MemoryBuffer.h"#include "llvm/TextAPI/InterfaceFile.h"#include <algorithm>#include <string>#ifndef TEXT_STUB_HELPERS_H#define TEXT_STUB_HELPERS_Hnamespace llvm {struct ExportedSymbol {llvm::MachO::SymbolKind Kind;std::string Name;bool WeakDefined;bool ThreadLocalValue;};using ExportedSymbolSeq = std::vector<ExportedSymbol>;using UUIDs = std::vector<std::pair<llvm::MachO::Target, std::string>>;using TBDFile = std::unique_ptr<MachO::InterfaceFile>;using TBDReexportFile = std::shared_ptr<MachO::InterfaceFile>;inline bool operator<(const ExportedSymbol &LHS, const ExportedSymbol &RHS) {return std::tie(LHS.Kind, LHS.Name) < std::tie(RHS.Kind, RHS.Name);}inline bool operator==(const ExportedSymbol &LHS, const ExportedSymbol &RHS) {return std::tie(LHS.Kind, LHS.Name, LHS.WeakDefined, LHS.ThreadLocalValue) ==std::tie(RHS.Kind, RHS.Name, RHS.WeakDefined, RHS.ThreadLocalValue);}inline std::string stripWhitespace(std::string S) {S.erase(std::remove_if(S.begin(), S.end(), ::isspace), S.end());return S;}// This will transform a single InterfaceFile then compare against the other// InterfaceFile then transform the second InterfaceFile in the same way to// regain equality.inline boolcheckEqualityOnTransform(MachO::InterfaceFile &FileA,MachO::InterfaceFile &FileB,void (*Transform)(MachO::InterfaceFile *)) {Transform(&FileA);// Files should not be equal.if (FileA == FileB)return false;Transform(&FileB);// Files should be equal.if (FileA != FileB)return false;return true;}} // namespace llvm#endif
set(LLVM_LINK_COMPONENTSTextAPI)add_llvm_unittest(TextAPITestsTextStubV1Tests.cppTextStubV2Tests.cppTextStubV3Tests.cppTextStubV4Tests.cpp)target_link_libraries(TextAPITests PRIVATE LLVMTestingSupport)
//===- TempPathTest.cpp ---------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"using namespace llvm;using llvm::unittest::TempDir;using llvm::unittest::TempFile;using llvm::unittest::TempLink;namespace {TEST(TempPathTest, TempDir) {Optional<TempDir> Dir1, Dir2;StringRef Prefix = "temp-path-test";Dir1.emplace(Prefix, /*Unique=*/true);EXPECT_EQ(Prefix,sys::path::filename(Dir1->path()).take_front(Prefix.size()));EXPECT_NE(Prefix, sys::path::filename(Dir1->path()));std::string Path = Dir1->path().str();ASSERT_TRUE(sys::fs::exists(Path));Dir2 = std::move(*Dir1);ASSERT_EQ(Path, Dir2->path());ASSERT_TRUE(sys::fs::exists(Path));Dir1 = None;ASSERT_TRUE(sys::fs::exists(Path));Dir2 = None;ASSERT_FALSE(sys::fs::exists(Path));}TEST(TempPathTest, TempFile) {TempDir D("temp-path-test", /*Unique=*/true);ASSERT_TRUE(sys::fs::exists(D.path()));Optional<TempFile> File1, File2;File1.emplace(D.path("file"), "suffix", "content");EXPECT_EQ("file.suffix", sys::path::filename(File1->path()));{ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =MemoryBuffer::getFile(File1->path());ASSERT_TRUE(Buffer);ASSERT_EQ("content", (*Buffer)->getBuffer());}std::string Path = File1->path().str();ASSERT_TRUE(sys::fs::exists(Path));File2 = std::move(*File1);ASSERT_EQ(Path, File2->path());ASSERT_TRUE(sys::fs::exists(Path));File1 = None;ASSERT_TRUE(sys::fs::exists(Path));File2 = None;ASSERT_FALSE(sys::fs::exists(Path));}TEST(TempPathTest, TempLink) {TempDir D("temp-path-test", /*Unique=*/true);ASSERT_TRUE(sys::fs::exists(D.path()));TempFile File(D.path("file"), "suffix", "content");Optional<TempLink> Link1, Link2;Link1.emplace(File.path(), D.path("link"));EXPECT_EQ("link", sys::path::filename(Link1->path()));{ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =MemoryBuffer::getFile(Link1->path());ASSERT_TRUE(Buffer);ASSERT_EQ("content", (*Buffer)->getBuffer());}std::string Path = Link1->path().str();ASSERT_TRUE(sys::fs::exists(Path));Link2 = std::move(*Link1);ASSERT_EQ(Path, Link2->path());ASSERT_TRUE(sys::fs::exists(Path));Link1 = None;ASSERT_TRUE(sys::fs::exists(Path));Link2 = None;ASSERT_FALSE(sys::fs::exists(Path));}} // namespace
set(LLVM_LINK_COMPONENTSSupportTestingSupport)add_llvm_unittest(TestingSupportTestsTempPathTest.cpp)target_link_libraries(TestingSupportTests PRIVATE LLVMTestingSupport)
add_subdirectory(Support)
//===- MachineSizeOptsTest.cpp --------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/MachineSizeOpts.h"#include "llvm/Analysis/BlockFrequencyInfo.h"#include "llvm/Analysis/BranchProbabilityInfo.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ProfileSummaryInfo.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"#include "llvm/CodeGen/MachineBranchProbabilityInfo.h"#include "llvm/CodeGen/MachineDominators.h"#include "llvm/CodeGen/MachineLoopInfo.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"using namespace llvm;namespace {std::unique_ptr<LLVMTargetMachine> createTargetMachine() {auto TT(Triple::normalize("x86_64--"));std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(TheTarget->createTargetMachine(TT, "", "", TargetOptions(), None, None,CodeGenOpt::Default)));}class MachineSizeOptsTest : public testing::Test {protected:static const char* MIRString;LLVMContext Context;std::unique_ptr<LLVMTargetMachine> TM;std::unique_ptr<MachineModuleInfo> MMI;std::unique_ptr<MIRParser> Parser;std::unique_ptr<Module> M;struct BFIData {std::unique_ptr<MachineDominatorTree> MDT;std::unique_ptr<MachineLoopInfo> MLI;std::unique_ptr<MachineBranchProbabilityInfo> MBPI;std::unique_ptr<MachineBlockFrequencyInfo> MBFI;BFIData(MachineFunction &MF) {MDT.reset(new MachineDominatorTree(MF));MLI.reset(new MachineLoopInfo(*MDT));MBPI.reset(new MachineBranchProbabilityInfo());MBFI.reset(new MachineBlockFrequencyInfo(MF, *MBPI, *MLI));}MachineBlockFrequencyInfo *get() { return MBFI.get(); }};static void SetUpTestCase() {LLVMInitializeX86TargetInfo();LLVMInitializeX86Target();LLVMInitializeX86TargetMC();}void SetUp() override {TM = createTargetMachine();std::unique_ptr<MemoryBuffer> MBuffer =MemoryBuffer::getMemBuffer(MIRString);Parser = createMIRParser(std::move(MBuffer), Context);if (!Parser)report_fatal_error("null MIRParser");M = Parser->parseIRModule();if (!M)report_fatal_error("parseIRModule failed");M->setTargetTriple(TM->getTargetTriple().getTriple());M->setDataLayout(TM->createDataLayout());MMI = std::make_unique<MachineModuleInfo>(TM.get());if (Parser->parseMachineFunctions(*M, *MMI.get()))report_fatal_error("parseMachineFunctions failed");}MachineFunction *getMachineFunction(Module *M, StringRef Name) {auto F = M->getFunction(Name);if (!F)report_fatal_error("null Function");auto &MF = MMI->getOrCreateMachineFunction(*F);return &MF;}};TEST_F(MachineSizeOptsTest, Test) {MachineFunction *F = getMachineFunction(M.get(), "f");ASSERT_TRUE(F != nullptr);MachineFunction *G = getMachineFunction(M.get(), "g");ASSERT_TRUE(G != nullptr);MachineFunction *H = getMachineFunction(M.get(), "h");ASSERT_TRUE(H != nullptr);ProfileSummaryInfo PSI = ProfileSummaryInfo(*M.get());ASSERT_TRUE(PSI.hasProfileSummary());BFIData BFID_F(*F);BFIData BFID_G(*G);BFIData BFID_H(*H);MachineBlockFrequencyInfo *MBFI_F = BFID_F.get();MachineBlockFrequencyInfo *MBFI_G = BFID_G.get();MachineBlockFrequencyInfo *MBFI_H = BFID_H.get();MachineBasicBlock &BB0 = F->front();auto iter = BB0.succ_begin();MachineBasicBlock *BB1 = *iter;iter++;MachineBasicBlock *BB2 = *iter;iter++;ASSERT_TRUE(iter == BB0.succ_end());MachineBasicBlock *BB3 = *BB1->succ_begin();ASSERT_TRUE(BB3 == *BB2->succ_begin());EXPECT_FALSE(shouldOptimizeForSize(F, &PSI, MBFI_F, PGSOQueryType::Test));EXPECT_TRUE(shouldOptimizeForSize(G, &PSI, MBFI_G, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(H, &PSI, MBFI_H, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(&BB0, &PSI, MBFI_F, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(BB1, &PSI, MBFI_F, PGSOQueryType::Test));EXPECT_TRUE(shouldOptimizeForSize(BB2, &PSI, MBFI_F, PGSOQueryType::Test));EXPECT_FALSE(shouldOptimizeForSize(BB3, &PSI, MBFI_F, PGSOQueryType::Test));}const char* MachineSizeOptsTest::MIRString = R"MIR(--- |define i32 @g(i32 %x) !prof !14 {ret i32 0}define i32 @h(i32 %x) !prof !15 {ret i32 0}define i32 @f(i32 %x) !prof !16 {bb0:%y1 = icmp eq i32 %x, 0br i1 %y1, label %bb1, label %bb2, !prof !17bb1: ; preds = %bb0%z1 = call i32 @g(i32 %x)br label %bb3bb2: ; preds = %bb0%z2 = call i32 @h(i32 %x)br label %bb3bb3: ; preds = %bb2, %bb1%y2 = phi i32 [ 0, %bb1 ], [ 1, %bb2 ]ret i32 %y2}!llvm.module.flags = !{!0}!0 = !{i32 1, !"ProfileSummary", !1}!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}!2 = !{!"ProfileFormat", !"InstrProf"}!3 = !{!"TotalCount", i64 10000}!4 = !{!"MaxCount", i64 10}!5 = !{!"MaxInternalCount", i64 1}!6 = !{!"MaxFunctionCount", i64 1000}!7 = !{!"NumCounts", i64 3}!8 = !{!"NumFunctions", i64 3}!9 = !{!"DetailedSummary", !10}!10 = !{!11, !12, !13}!11 = !{i32 10000, i64 1000, i32 1}!12 = !{i32 999000, i64 300, i32 3}!13 = !{i32 999999, i64 5, i32 10}!14 = !{!"function_entry_count", i64 1}!15 = !{!"function_entry_count", i64 100}!16 = !{!"function_entry_count", i64 400}!17 = !{!"branch_weights", i32 100, i32 1}...---name: gbody: |bb.0:%1:gr32 = MOV32r0 implicit-def dead $eflags$eax = COPY %1RET 0, $eax...---name: hbody: |bb.0:%1:gr32 = MOV32r0 implicit-def dead $eflags$eax = COPY %1RET 0, $eax...---name: ftracksRegLiveness: truebody: |bb.0:successors: %bb.1(0x7ebb907a), %bb.2(0x01446f86)liveins: $edi%1:gr32 = COPY $ediTEST32rr %1, %1, implicit-def $eflagsJCC_1 %bb.2, 5, implicit $eflagsJMP_1 %bb.1bb.1:successors: %bb.3(0x80000000)ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp$edi = COPY %1CALL64pcrel32 @g, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eaxADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp%5:gr32 = COPY $eax%4:gr32 = MOV32r0 implicit-def dead $eflagsJMP_1 %bb.3bb.2:successors: %bb.3(0x80000000)ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp$edi = COPY %1CALL64pcrel32 @h, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eaxADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp%3:gr32 = COPY $eax%2:gr32 = MOV32ri 1bb.3:%0:gr32 = PHI %2, %bb.2, %4, %bb.1$eax = COPY %0RET 0, $eax...)MIR";} // anonymous namespace
include_directories(${CMAKE_SOURCE_DIR}/lib/Target/X86${CMAKE_BINARY_DIR}/lib/Target/X86)set(LLVM_LINK_COMPONENTSAnalysisCodeGenCoreMCMIRParserSupportTargetX86CodeGenX86DescX86Info)add_llvm_unittest(X86TestsMachineSizeOptsTest.cpp)set_property(TARGET X86Tests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
//=== WebAssemblyExceptionInfoTest.cpp - WebAssemblyExceptionInfo unit tests =////// 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////===----------------------------------------------------------------------===//#include "WebAssemblyExceptionInfo.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineDominanceFrontier.h"#include "llvm/CodeGen/MachineDominators.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"using namespace llvm;namespace {std::unique_ptr<LLVMTargetMachine> createTargetMachine() {auto TT(Triple::normalize("wasm32-unknown-unknown"));std::string CPU("");std::string FS("");LLVMInitializeWebAssemblyTargetInfo();LLVMInitializeWebAssemblyTarget();LLVMInitializeWebAssemblyTargetMC();std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);assert(TheTarget);return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(TheTarget->createTargetMachine(TT, CPU, FS, TargetOptions(), None, None,CodeGenOpt::Default)));}std::unique_ptr<Module> parseMIR(LLVMContext &Context,std::unique_ptr<MIRParser> &MIR,const TargetMachine &TM, StringRef MIRCode,const char *FuncName, MachineModuleInfo &MMI) {SMDiagnostic Diagnostic;std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);MIR = createMIRParser(std::move(MBuffer), Context);if (!MIR)return nullptr;std::unique_ptr<Module> M = MIR->parseIRModule();if (!M)return nullptr;M->setDataLayout(TM.createDataLayout());if (MIR->parseMachineFunctions(*M, MMI))return nullptr;return M;}} // namespaceTEST(WebAssemblyExceptionInfoTest, TEST0) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();ASSERT_TRUE(TM);StringRef MIRString = R"MIR(--- |target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"target triple = "wasm32-unknown-unknown"declare i32 @__gxx_wasm_personality_v0(...)define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {unreachable}...---name: test0liveins:- { reg: '$arguments' }- { reg: '$value_stack' }body: |bb.0:successors: %bb.1, %bb.2liveins: $arguments, $value_stackBR %bb.1, implicit-def dead $argumentsbb.1:; predecessors: %bb.0successors: %bb.7liveins: $value_stackBR %bb.7, implicit-def $argumentsbb.2 (landing-pad):; predecessors: %bb.0successors: %bb.3, %bb.9liveins: $value_stackCATCH_ALL implicit-def $argumentsRETHROW 0, implicit-def dead $argumentsbb.3 (landing-pad):; predecessors: %bb.2successors: %bb.4, %bb.6liveins: $value_stack%1:i32 = CATCH &__cpp_exception, implicit-def $argumentsBR_IF %bb.4, %58:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stackBR %bb.6, implicit-def $argumentsbb.4:; predecessors: %bb.3successors: %bb.5, %bb.8liveins: $value_stackBR %bb.5, implicit-def dead $argumentsbb.5:; predecessors: %bb.4successors: %bb.7liveins: $value_stackBR %bb.7, implicit-def dead $argumentsbb.6:; predecessors: %bb.3successors: %bb.10, %bb.9liveins: $value_stackBR %bb.10, implicit-def dead $argumentsbb.7:; predecessors: %bb.5, %bb.1liveins: $value_stackRETURN implicit-def $argumentsbb.8 (landing-pad):; predecessors: %bb.4successors: %bb.9liveins: $value_stackCATCH_ALL implicit-def $argumentsRETHROW 0, implicit-def dead $argumentsbb.9 (landing-pad):; predecessors: %bb.2, %bb.6, %bb.8liveins: $value_stackCATCH_ALL implicit-def $argumentsRETHROW 0, implicit-def dead $argumentsbb.10:; predecessors: %bb.6liveins: $value_stackUNREACHABLE implicit-def $arguments)MIR";LLVMContext Context;std::unique_ptr<MIRParser> MIR;MachineModuleInfo MMI(TM.get());std::unique_ptr<Module> M =parseMIR(Context, MIR, *TM, MIRString, "test0", MMI);ASSERT_TRUE(M);Function *F = M->getFunction("test0");auto *MF = MMI.getMachineFunction(*F);ASSERT_TRUE(MF);WebAssemblyExceptionInfo WEI;MachineDominatorTree MDT;MachineDominanceFrontier MDF;MDT.runOnMachineFunction(*MF);MDF.getBase().analyze(MDT.getBase());WEI.recalculate(*MF, MDT, MDF);// Exception info structure:// |- bb2 (ehpad), bb3, bb4, bb5, bb6, bb8, bb9, bb10// |- bb3 (ehpad), bb4, bb5, bb6, bb8, bb10// |- bb8 (ehpad)// |- bb9 (ehpad)auto *MBB2 = MF->getBlockNumbered(2);auto *WE0 = WEI.getExceptionFor(MBB2);ASSERT_TRUE(WE0);EXPECT_EQ(WE0->getEHPad(), MBB2);EXPECT_EQ(WE0->getParentException(), nullptr);EXPECT_EQ(WE0->getExceptionDepth(), (unsigned)1);auto *MBB3 = MF->getBlockNumbered(3);auto *WE0_0 = WEI.getExceptionFor(MBB3);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);EXPECT_EQ(WE0_0->getParentException(), WE0);EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned)2);auto *MBB4 = MF->getBlockNumbered(4);WE0_0 = WEI.getExceptionFor(MBB4);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB5 = MF->getBlockNumbered(5);WE0_0 = WEI.getExceptionFor(MBB5);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB6 = MF->getBlockNumbered(6);WE0_0 = WEI.getExceptionFor(MBB6);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB10 = MF->getBlockNumbered(10);WE0_0 = WEI.getExceptionFor(MBB10);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB8 = MF->getBlockNumbered(8);auto *WE0_0_0 = WEI.getExceptionFor(MBB8);ASSERT_TRUE(WE0_0_0);EXPECT_EQ(WE0_0_0->getEHPad(), MBB8);EXPECT_EQ(WE0_0_0->getParentException(), WE0_0);EXPECT_EQ(WE0_0_0->getExceptionDepth(), (unsigned)3);auto *MBB9 = MF->getBlockNumbered(9);auto *WE0_1 = WEI.getExceptionFor(MBB9);ASSERT_TRUE(WE0_1);EXPECT_EQ(WE0_1->getEHPad(), MBB9);EXPECT_EQ(WE0_1->getParentException(), WE0);EXPECT_EQ(WE0_1->getExceptionDepth(), (unsigned)2);}TEST(WebAssemblyExceptionInfoTest, TEST1) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();ASSERT_TRUE(TM);StringRef MIRString = R"MIR(--- |target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"target triple = "wasm32-unknown-unknown"declare i32 @__gxx_wasm_personality_v0(...)define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {unreachable}...---name: test1liveins:- { reg: '$arguments' }- { reg: '$value_stack' }body: |bb.0:successors: %bb.9, %bb.1liveins: $arguments, $value_stackBR %bb.9, implicit-def dead $argumentsbb.1 (landing-pad):; predecessors: %bb.0successors: %bb.2, %bb.8liveins: $value_stack%0:i32 = CATCH &__cpp_exception, implicit-def $argumentsBR_IF %bb.2, %32:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stackBR %bb.8, implicit-def $argumentsbb.2:; predecessors: %bb.1successors: %bb.7, %bb.3, %bb.11liveins: $value_stackBR %bb.7, implicit-def dead $argumentsbb.3 (landing-pad):; predecessors: %bb.2successors: %bb.4, %bb.6liveins: $value_stack%1:i32 = CATCH &__cpp_exception, implicit-def $argumentsBR_IF %bb.4, %43:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stackBR %bb.6, implicit-def $argumentsbb.4:; predecessors: %bb.3successors: %bb.5, %bb.10liveins: $value_stackBR %bb.5, implicit-def dead $argumentsbb.5:; predecessors: %bb.4successors: %bb.7(0x80000000); %bb.7(200.00%)liveins: $value_stackBR %bb.7, implicit-def dead $argumentsbb.6:; predecessors: %bb.3successors: %bb.12, %bb.11liveins: $value_stackBR %bb.12, implicit-def dead $argumentsbb.7:; predecessors: %bb.2, %bb.5successors: %bb.9(0x80000000); %bb.9(200.00%)liveins: $value_stackBR %bb.9, implicit-def dead $argumentsbb.8:; predecessors: %bb.1liveins: $value_stackUNREACHABLE implicit-def $argumentsbb.9:; predecessors: %bb.0, %bb.7liveins: $value_stackRETURN implicit-def $argumentsbb.10 (landing-pad):; predecessors: %bb.4successors: %bb.11liveins: $value_stackCATCH_ALL implicit-def $argumentsRETHROW 0, implicit-def dead $argumentsbb.11 (landing-pad):; predecessors: %bb.2, %bb.6, %bb.10liveins: $value_stackCATCH_ALL implicit-def $argumentsRETHROW 0, implicit-def dead $argumentsbb.12:; predecessors: %bb.6liveins: $value_stackUNREACHABLE implicit-def $arguments)MIR";LLVMContext Context;std::unique_ptr<MIRParser> MIR;MachineModuleInfo MMI(TM.get());std::unique_ptr<Module> M =parseMIR(Context, MIR, *TM, MIRString, "test1", MMI);ASSERT_TRUE(M);Function *F = M->getFunction("test1");auto *MF = MMI.getMachineFunction(*F);ASSERT_TRUE(MF);WebAssemblyExceptionInfo WEI;MachineDominatorTree MDT;MachineDominanceFrontier MDF;MDT.runOnMachineFunction(*MF);MDF.getBase().analyze(MDT.getBase());WEI.recalculate(*MF, MDT, MDF);// Exception info structure:// |- bb1 (ehpad), bb2, bb3, bb4, bb5, bb6, bb7, bb8, bb10, bb11, bb12// |- bb3 (ehpad), bb4, bb5, bb6, bb10, bb12// |- bb10 (ehpad)// |- bb11 (ehpad)auto *MBB1 = MF->getBlockNumbered(1);auto *WE0 = WEI.getExceptionFor(MBB1);ASSERT_TRUE(WE0);EXPECT_EQ(WE0->getEHPad(), MBB1);EXPECT_EQ(WE0->getParentException(), nullptr);EXPECT_EQ(WE0->getExceptionDepth(), (unsigned)1);auto *MBB2 = MF->getBlockNumbered(2);WE0 = WEI.getExceptionFor(MBB2);ASSERT_TRUE(WE0);EXPECT_EQ(WE0->getEHPad(), MBB1);auto *MBB7 = MF->getBlockNumbered(7);WE0 = WEI.getExceptionFor(MBB7);ASSERT_TRUE(WE0);EXPECT_EQ(WE0->getEHPad(), MBB1);auto *MBB8 = MF->getBlockNumbered(8);WE0 = WEI.getExceptionFor(MBB8);ASSERT_TRUE(WE0);EXPECT_EQ(WE0->getEHPad(), MBB1);auto *MBB3 = MF->getBlockNumbered(3);auto *WE0_0 = WEI.getExceptionFor(MBB3);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);EXPECT_EQ(WE0_0->getParentException(), WE0);EXPECT_EQ(WE0_0->getExceptionDepth(), (unsigned)2);auto *MBB4 = MF->getBlockNumbered(4);WE0_0 = WEI.getExceptionFor(MBB4);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB5 = MF->getBlockNumbered(5);WE0_0 = WEI.getExceptionFor(MBB5);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB6 = MF->getBlockNumbered(6);WE0_0 = WEI.getExceptionFor(MBB6);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB12 = MF->getBlockNumbered(12);WE0_0 = WEI.getExceptionFor(MBB12);ASSERT_TRUE(WE0_0);EXPECT_EQ(WE0_0->getEHPad(), MBB3);auto *MBB10 = MF->getBlockNumbered(10);auto *WE0_0_0 = WEI.getExceptionFor(MBB10);ASSERT_TRUE(WE0_0_0);EXPECT_EQ(WE0_0_0->getEHPad(), MBB10);EXPECT_EQ(WE0_0_0->getParentException(), WE0_0);EXPECT_EQ(WE0_0_0->getExceptionDepth(), (unsigned)3);auto *MBB11 = MF->getBlockNumbered(11);auto *WE0_1 = WEI.getExceptionFor(MBB11);ASSERT_TRUE(WE0_1);EXPECT_EQ(WE0_1->getEHPad(), MBB11);EXPECT_EQ(WE0_1->getParentException(), WE0);EXPECT_EQ(WE0_1->getExceptionDepth(), (unsigned)2);}
include_directories(${CMAKE_SOURCE_DIR}/lib/Target/WebAssembly${CMAKE_BINARY_DIR}/lib/Target/WebAssembly)set(LLVM_LINK_COMPONENTSCodeGenCoreMCMIRParserWebAssemblyCodeGenWebAssemblyDescWebAssemblyInfo)add_llvm_target_unittest(WebAssemblyTestsWebAssemblyExceptionInfoTest.cpp)set_property(TARGET WebAssemblyTests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
include_directories(${CMAKE_SOURCE_DIR}/lib/Target/PowerPC${CMAKE_BINARY_DIR}/lib/Target/PowerPC)set(LLVM_LINK_COMPONENTSMCSupportTargetPowerPCCodeGenPowerPCDescPowerPCInfo)add_llvm_unittest(PowerPCTestsAIXRelocModelTest.cpp)set_property(TARGET PowerPCTests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
#include "llvm/ADT/Triple.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"using namespace llvm;namespace {class AIXRelocModelTest : public ::testing::Test {protected:static void SetUpTestCase() {LLVMInitializePowerPCTargetInfo();LLVMInitializePowerPCTarget();LLVMInitializePowerPCTargetMC();}};TEST_F(AIXRelocModelTest, DefalutToPIC) {Triple TheTriple(/*ArchStr*/ "powerpc", /*VendorStr*/ "", /*OSStr*/ "aix");std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, Error);ASSERT_TRUE(TheTarget) << Error;TargetOptions Options;// Create a TargetMachine for powerpc--aix target, and deliberately leave its// relocation model unset.std::unique_ptr<TargetMachine> Target(TheTarget->createTargetMachine(/*TT*/ TheTriple.getTriple(), /*CPU*/ "", /*Features*/ "",/*Options*/ Options, /*RM*/ None, /*CM*/ None,/*OL*/ CodeGenOpt::Default));ASSERT_TRUE(Target) << "Could not allocate target machine!";// The relocation model on AIX should be forced to PIC regardless.EXPECT_TRUE(Target->getRelocationModel() == Reloc::PIC_);}} // end of anonymous namespace
//===- llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp ---------===////// 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////===----------------------------------------------------------------------===//#include "DXILPointerType.h"#include "PointerTypeAnalysis.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Type.h"#include "llvm/Support/SourceMgr.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using ::testing::Contains;using ::testing::Pair;using namespace llvm;using namespace llvm::dxil;template <typename T> struct IsA {friend bool operator==(const Value *V, const IsA &) { return isa<T>(V); }};TEST(DXILPointerType, PrintTest) {std::string Buffer;LLVMContext Context;raw_string_ostream OS(Buffer);Type *I8Ptr = TypedPointerType::get(Type::getInt8Ty(Context), 0);I8Ptr->print(OS);EXPECT_TRUE(StringRef(Buffer).startswith("dxil-ptr ("));}TEST(PointerTypeAnalysis, DigressToi8) {StringRef Assembly = R"(define i64 @test(ptr %p) {store i32 0, ptr %p%v = load i64, ptr %pret i64 %v})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";PointerTypeMap Map = PointerTypeAnalysis::run(*M);ASSERT_EQ(Map.size(), 2u);Type *I8Ptr = TypedPointerType::get(Type::getInt8Ty(Context), 0);Type *FnTy = FunctionType::get(Type::getInt64Ty(Context), {I8Ptr}, false);EXPECT_THAT(Map, Contains(Pair(IsA<Function>(), FnTy)));EXPECT_THAT(Map, Contains(Pair(IsA<Argument>(), I8Ptr)));}TEST(PointerTypeAnalysis, DiscoverStore) {StringRef Assembly = R"(define i32 @test(ptr %p) {store i32 0, ptr %pret i32 0})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";PointerTypeMap Map = PointerTypeAnalysis::run(*M);ASSERT_EQ(Map.size(), 2u);Type *I32Ptr = TypedPointerType::get(Type::getInt32Ty(Context), 0);Type *FnTy = FunctionType::get(Type::getInt32Ty(Context), {I32Ptr}, false);EXPECT_THAT(Map, Contains(Pair(IsA<Function>(), FnTy)));EXPECT_THAT(Map, Contains(Pair(IsA<Argument>(), I32Ptr)));}TEST(PointerTypeAnalysis, DiscoverLoad) {StringRef Assembly = R"(define i32 @test(ptr %p) {%v = load i32, ptr %pret i32 %v})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";PointerTypeMap Map = PointerTypeAnalysis::run(*M);ASSERT_EQ(Map.size(), 2u);Type *I32Ptr = TypedPointerType::get(Type::getInt32Ty(Context), 0);Type *FnTy = FunctionType::get(Type::getInt32Ty(Context), {I32Ptr}, false);EXPECT_THAT(Map, Contains(Pair(IsA<Function>(), FnTy)));EXPECT_THAT(Map, Contains(Pair(IsA<Argument>(), I32Ptr)));}TEST(PointerTypeAnalysis, DiscoverGEP) {StringRef Assembly = R"(define ptr @test(ptr %p) {%p2 = getelementptr i64, ptr %p, i64 1ret ptr %p2})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";PointerTypeMap Map = PointerTypeAnalysis::run(*M);ASSERT_EQ(Map.size(), 3u);Type *I64Ptr = TypedPointerType::get(Type::getInt64Ty(Context), 0);Type *FnTy = FunctionType::get(I64Ptr, {I64Ptr}, false);EXPECT_THAT(Map, Contains(Pair(IsA<Function>(), FnTy)));EXPECT_THAT(Map, Contains(Pair(IsA<Argument>(), I64Ptr)));EXPECT_THAT(Map, Contains(Pair(IsA<GetElementPtrInst>(), I64Ptr)));}TEST(PointerTypeAnalysis, TraceIndirect) {StringRef Assembly = R"(define i64 @test(ptr %p) {%p2 = load ptr, ptr %p%v = load i64, ptr %p2ret i64 %v})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";PointerTypeMap Map = PointerTypeAnalysis::run(*M);ASSERT_EQ(Map.size(), 3u);Type *I64Ptr = TypedPointerType::get(Type::getInt64Ty(Context), 0);Type *I64PtrPtr = TypedPointerType::get(I64Ptr, 0);Type *FnTy = FunctionType::get(Type::getInt64Ty(Context), {I64PtrPtr}, false);EXPECT_THAT(Map, Contains(Pair(IsA<Function>(), FnTy)));EXPECT_THAT(Map, Contains(Pair(IsA<Argument>(), I64PtrPtr)));EXPECT_THAT(Map, Contains(Pair(IsA<LoadInst>(), I64Ptr)));}TEST(PointerTypeAnalysis, WithNoOpCasts) {StringRef Assembly = R"(define i64 @test(ptr %p) {%1 = bitcast ptr %p to ptr%2 = bitcast ptr %p to ptrstore i32 0, ptr %1, align 4%3 = load i64, ptr %2, align 8ret i64 %3})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";PointerTypeMap Map = PointerTypeAnalysis::run(*M);ASSERT_EQ(Map.size(), 4u);Type *I8Ptr = TypedPointerType::get(Type::getInt8Ty(Context), 0);Type *I32Ptr = TypedPointerType::get(Type::getInt32Ty(Context), 0);Type *I64Ptr = TypedPointerType::get(Type::getInt64Ty(Context), 0);Type *FnTy = FunctionType::get(Type::getInt64Ty(Context), {I8Ptr}, false);EXPECT_THAT(Map, Contains(Pair(IsA<Function>(), FnTy)));EXPECT_THAT(Map, Contains(Pair(IsA<Argument>(), I8Ptr)));EXPECT_THAT(Map, Contains(Pair(IsA<BitCastInst>(), I64Ptr)));EXPECT_THAT(Map, Contains(Pair(IsA<BitCastInst>(), I32Ptr)));}
include_directories(${LLVM_MAIN_SRC_DIR}/lib/Target/DirectX${LLVM_BINARY_DIR}/lib/Target/DirectX)set(LLVM_LINK_COMPONENTSAsmParserCoreDirectXCodeGenSupport)add_llvm_target_unittest(DirectXTestsPointerTypeAnalysisTests.cpp)
foreach(t ${LLVM_TARGETS_TO_BUILD})if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${t})add_subdirectory(${t})endif()endforeach()
#include "ARMBaseInstrInfo.h"#include "ARMSubtarget.h"#include "ARMTargetMachine.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;TEST(MachineInstructionDoubleWidthResult, IsCorrect) {using namespace ARM;auto DoubleWidthResult = [](unsigned Opcode) {switch (Opcode) {default:break;case MVE_VMULLBp16:case MVE_VMULLBp8:case MVE_VMULLBs16:case MVE_VMULLBs32:case MVE_VMULLBs8:case MVE_VMULLBu16:case MVE_VMULLBu32:case MVE_VMULLBu8:case MVE_VMULLTp16:case MVE_VMULLTp8:case MVE_VMULLTs16:case MVE_VMULLTs32:case MVE_VMULLTs8:case MVE_VMULLTu16:case MVE_VMULLTu32:case MVE_VMULLTu8:case MVE_VQDMULL_qr_s16bh:case MVE_VQDMULL_qr_s16th:case MVE_VQDMULL_qr_s32bh:case MVE_VQDMULL_qr_s32th:case MVE_VQDMULLs16bh:case MVE_VQDMULLs16th:case MVE_VQDMULLs32bh:case MVE_VQDMULLs32th:case MVE_VMOVLs16bh:case MVE_VMOVLs16th:case MVE_VMOVLs8bh:case MVE_VMOVLs8th:case MVE_VMOVLu16bh:case MVE_VMOVLu16th:case MVE_VMOVLu8bh:case MVE_VMOVLu8th:case MVE_VSHLL_imms16bh:case MVE_VSHLL_imms16th:case MVE_VSHLL_imms8bh:case MVE_VSHLL_imms8th:case MVE_VSHLL_immu16bh:case MVE_VSHLL_immu16th:case MVE_VSHLL_immu8bh:case MVE_VSHLL_immu8th:case MVE_VSHLL_lws16bh:case MVE_VSHLL_lws16th:case MVE_VSHLL_lws8bh:case MVE_VSHLL_lws8th:case MVE_VSHLL_lwu16bh:case MVE_VSHLL_lwu16th:case MVE_VSHLL_lwu8bh:case MVE_VSHLL_lwu8th:return true;}return false;};LLVMInitializeARMTargetInfo();LLVMInitializeARMTarget();LLVMInitializeARMTargetMC();auto TT(Triple::normalize("thumbv8.1m.main-none-none-eabi"));std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T) {dbgs() << Error;return;}TargetOptions Options;auto TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(T->createTargetMachine(TT, "generic", "", Options, None, None,CodeGenOpt::Default)));ARMSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()),*static_cast<const ARMBaseTargetMachine *>(TM.get()), false);const ARMBaseInstrInfo *TII = ST.getInstrInfo();auto MII = TM->getMCInstrInfo();for (unsigned i = 0; i < ARM::INSTRUCTION_LIST_END; ++i) {const MCInstrDesc &Desc = TII->get(i);uint64_t Flags = Desc.TSFlags;if ((Flags & ARMII::DomainMask) != ARMII::DomainMVE)continue;bool Valid = (Flags & ARMII::DoubleWidthResult) != 0;ASSERT_EQ(DoubleWidthResult(i), Valid)<< MII->getName(i)<< ": mismatched expectation for tail-predicated safety\n";}}TEST(MachineInstructionHorizontalReduction, IsCorrect) {using namespace ARM;auto HorizontalReduction = [](unsigned Opcode) {switch (Opcode) {default:break;case MVE_VABAVs16:case MVE_VABAVs32:case MVE_VABAVs8:case MVE_VABAVu16:case MVE_VABAVu32:case MVE_VABAVu8:case MVE_VADDLVs32acc:case MVE_VADDLVs32no_acc:case MVE_VADDLVu32acc:case MVE_VADDLVu32no_acc:case MVE_VADDVs16acc:case MVE_VADDVs16no_acc:case MVE_VADDVs32acc:case MVE_VADDVs32no_acc:case MVE_VADDVs8acc:case MVE_VADDVs8no_acc:case MVE_VADDVu16acc:case MVE_VADDVu16no_acc:case MVE_VADDVu32acc:case MVE_VADDVu32no_acc:case MVE_VADDVu8acc:case MVE_VADDVu8no_acc:case MVE_VMAXAVs16:case MVE_VMAXAVs32:case MVE_VMAXAVs8:case MVE_VMAXNMAVf16:case MVE_VMAXNMAVf32:case MVE_VMAXNMVf16:case MVE_VMAXNMVf32:case MVE_VMAXVs16:case MVE_VMAXVs32:case MVE_VMAXVs8:case MVE_VMAXVu16:case MVE_VMAXVu32:case MVE_VMAXVu8:case MVE_VMINAVs16:case MVE_VMINAVs32:case MVE_VMINAVs8:case MVE_VMINNMAVf16:case MVE_VMINNMAVf32:case MVE_VMINNMVf16:case MVE_VMINNMVf32:case MVE_VMINVs16:case MVE_VMINVs32:case MVE_VMINVs8:case MVE_VMINVu16:case MVE_VMINVu32:case MVE_VMINVu8:case MVE_VMLADAVas16:case MVE_VMLADAVas32:case MVE_VMLADAVas8:case MVE_VMLADAVau16:case MVE_VMLADAVau32:case MVE_VMLADAVau8:case MVE_VMLADAVaxs16:case MVE_VMLADAVaxs32:case MVE_VMLADAVaxs8:case MVE_VMLADAVs16:case MVE_VMLADAVs32:case MVE_VMLADAVs8:case MVE_VMLADAVu16:case MVE_VMLADAVu32:case MVE_VMLADAVu8:case MVE_VMLADAVxs16:case MVE_VMLADAVxs32:case MVE_VMLADAVxs8:case MVE_VMLALDAVas16:case MVE_VMLALDAVas32:case MVE_VMLALDAVau16:case MVE_VMLALDAVau32:case MVE_VMLALDAVaxs16:case MVE_VMLALDAVaxs32:case MVE_VMLALDAVs16:case MVE_VMLALDAVs32:case MVE_VMLALDAVu16:case MVE_VMLALDAVu32:case MVE_VMLALDAVxs16:case MVE_VMLALDAVxs32:case MVE_VMLSDAVas16:case MVE_VMLSDAVas32:case MVE_VMLSDAVas8:case MVE_VMLSDAVaxs16:case MVE_VMLSDAVaxs32:case MVE_VMLSDAVaxs8:case MVE_VMLSDAVs16:case MVE_VMLSDAVs32:case MVE_VMLSDAVs8:case MVE_VMLSDAVxs16:case MVE_VMLSDAVxs32:case MVE_VMLSDAVxs8:case MVE_VMLSLDAVas16:case MVE_VMLSLDAVas32:case MVE_VMLSLDAVaxs16:case MVE_VMLSLDAVaxs32:case MVE_VMLSLDAVs16:case MVE_VMLSLDAVs32:case MVE_VMLSLDAVxs16:case MVE_VMLSLDAVxs32:case MVE_VRMLALDAVHas32:case MVE_VRMLALDAVHau32:case MVE_VRMLALDAVHaxs32:case MVE_VRMLALDAVHs32:case MVE_VRMLALDAVHu32:case MVE_VRMLALDAVHxs32:case MVE_VRMLSLDAVHas32:case MVE_VRMLSLDAVHaxs32:case MVE_VRMLSLDAVHs32:case MVE_VRMLSLDAVHxs32:return true;}return false;};LLVMInitializeARMTargetInfo();LLVMInitializeARMTarget();LLVMInitializeARMTargetMC();auto TT(Triple::normalize("thumbv8.1m.main-none-none-eabi"));std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T) {dbgs() << Error;return;}TargetOptions Options;auto TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(T->createTargetMachine(TT, "generic", "", Options, None, None,CodeGenOpt::Default)));ARMSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()),*static_cast<const ARMBaseTargetMachine *>(TM.get()), false);const ARMBaseInstrInfo *TII = ST.getInstrInfo();auto MII = TM->getMCInstrInfo();for (unsigned i = 0; i < ARM::INSTRUCTION_LIST_END; ++i) {const MCInstrDesc &Desc = TII->get(i);uint64_t Flags = Desc.TSFlags;if ((Flags & ARMII::DomainMask) != ARMII::DomainMVE)continue;bool Valid = (Flags & ARMII::HorizontalReduction) != 0;ASSERT_EQ(HorizontalReduction(i), Valid)<< MII->getName(i)<< ": mismatched expectation for tail-predicated safety\n";}}TEST(MachineInstructionRetainsPreviousHalfElement, IsCorrect) {using namespace ARM;auto RetainsPreviousHalfElement = [](unsigned Opcode) {switch (Opcode) {default:break;case MVE_VMOVNi16bh:case MVE_VMOVNi16th:case MVE_VMOVNi32bh:case MVE_VMOVNi32th:case MVE_VQMOVNs16bh:case MVE_VQMOVNs16th:case MVE_VQMOVNs32bh:case MVE_VQMOVNs32th:case MVE_VQMOVNu16bh:case MVE_VQMOVNu16th:case MVE_VQMOVNu32bh:case MVE_VQMOVNu32th:case MVE_VQMOVUNs16bh:case MVE_VQMOVUNs16th:case MVE_VQMOVUNs32bh:case MVE_VQMOVUNs32th:case MVE_VQRSHRNbhs16:case MVE_VQRSHRNbhs32:case MVE_VQRSHRNbhu16:case MVE_VQRSHRNbhu32:case MVE_VQRSHRNths16:case MVE_VQRSHRNths32:case MVE_VQRSHRNthu16:case MVE_VQRSHRNthu32:case MVE_VQRSHRUNs16bh:case MVE_VQRSHRUNs16th:case MVE_VQRSHRUNs32bh:case MVE_VQRSHRUNs32th:case MVE_VQSHRNbhs16:case MVE_VQSHRNbhs32:case MVE_VQSHRNbhu16:case MVE_VQSHRNbhu32:case MVE_VQSHRNths16:case MVE_VQSHRNths32:case MVE_VQSHRNthu16:case MVE_VQSHRNthu32:case MVE_VQSHRUNs16bh:case MVE_VQSHRUNs16th:case MVE_VQSHRUNs32bh:case MVE_VQSHRUNs32th:case MVE_VRSHRNi16bh:case MVE_VRSHRNi16th:case MVE_VRSHRNi32bh:case MVE_VRSHRNi32th:case MVE_VSHRNi16bh:case MVE_VSHRNi16th:case MVE_VSHRNi32bh:case MVE_VSHRNi32th:case MVE_VCVTf16f32bh:case MVE_VCVTf16f32th:case MVE_VCVTf32f16bh:case MVE_VCVTf32f16th:return true;}return false;};LLVMInitializeARMTargetInfo();LLVMInitializeARMTarget();LLVMInitializeARMTargetMC();auto TT(Triple::normalize("thumbv8.1m.main-none-none-eabi"));std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T) {dbgs() << Error;return;}TargetOptions Options;auto TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(T->createTargetMachine(TT, "generic", "", Options, None, None,CodeGenOpt::Default)));ARMSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()),*static_cast<const ARMBaseTargetMachine *>(TM.get()), false);const ARMBaseInstrInfo *TII = ST.getInstrInfo();auto MII = TM->getMCInstrInfo();for (unsigned i = 0; i < ARM::INSTRUCTION_LIST_END; ++i) {const MCInstrDesc &Desc = TII->get(i);uint64_t Flags = Desc.TSFlags;if ((Flags & ARMII::DomainMask) != ARMII::DomainMVE)continue;bool Valid = (Flags & ARMII::RetainsPreviousHalfElement) != 0;ASSERT_EQ(RetainsPreviousHalfElement(i), Valid)<< MII->getName(i)<< ": mismatched expectation for tail-predicated safety\n";}}// Test for instructions that aren't immediately obviously valid within a// tail-predicated loop. This should be marked up in their tablegen// descriptions. Currently we, conservatively, disallow:// - cross beat carries.// - complex operations.// - horizontal operations with exchange.// - byte swapping.// - interleaved memory instructions.// TODO: Add to this list once we can handle them safely.TEST(MachineInstrValidTailPredication, IsCorrect) {using namespace ARM;auto IsValidTPOpcode = [](unsigned Opcode) {switch (Opcode) {default:return false;case MVE_ASRLi:case MVE_ASRLr:case MVE_LSRL:case MVE_LSLLi:case MVE_LSLLr:case MVE_SQRSHR:case MVE_SQRSHRL:case MVE_SQSHL:case MVE_SQSHLL:case MVE_SRSHR:case MVE_SRSHRL:case MVE_UQRSHL:case MVE_UQRSHLL:case MVE_UQSHL:case MVE_UQSHLL:case MVE_URSHR:case MVE_URSHRL:case MVE_VABDf16:case MVE_VABDf32:case MVE_VABDs16:case MVE_VABDs32:case MVE_VABDs8:case MVE_VABDu16:case MVE_VABDu32:case MVE_VABDu8:case MVE_VABSf16:case MVE_VABSf32:case MVE_VABSs16:case MVE_VABSs32:case MVE_VABSs8:case MVE_VADD_qr_f16:case MVE_VADD_qr_f32:case MVE_VADD_qr_i16:case MVE_VADD_qr_i32:case MVE_VADD_qr_i8:case MVE_VADDVs16acc:case MVE_VADDVs16no_acc:case MVE_VADDVs32acc:case MVE_VADDVs32no_acc:case MVE_VADDVs8acc:case MVE_VADDVs8no_acc:case MVE_VADDVu16acc:case MVE_VADDVu16no_acc:case MVE_VADDVu32acc:case MVE_VADDVu32no_acc:case MVE_VADDVu8acc:case MVE_VADDVu8no_acc:case MVE_VADDf16:case MVE_VADDf32:case MVE_VADDi16:case MVE_VADDi32:case MVE_VADDi8:case MVE_VAND:case MVE_VBIC:case MVE_VBICimmi16:case MVE_VBICimmi32:case MVE_VBRSR16:case MVE_VBRSR32:case MVE_VBRSR8:case MVE_VCLSs16:case MVE_VCLSs32:case MVE_VCLSs8:case MVE_VCLZs16:case MVE_VCLZs32:case MVE_VCLZs8:case MVE_VCMPf16:case MVE_VCMPf16r:case MVE_VCMPf32:case MVE_VCMPf32r:case MVE_VCMPi16:case MVE_VCMPi16r:case MVE_VCMPi32:case MVE_VCMPi32r:case MVE_VCMPi8:case MVE_VCMPi8r:case MVE_VCMPs16:case MVE_VCMPs16r:case MVE_VCMPs32:case MVE_VCMPs32r:case MVE_VCMPs8:case MVE_VCMPs8r:case MVE_VCMPu16:case MVE_VCMPu16r:case MVE_VCMPu32:case MVE_VCMPu32r:case MVE_VCMPu8:case MVE_VCMPu8r:case MVE_VCTP16:case MVE_VCTP32:case MVE_VCTP64:case MVE_VCTP8:case MVE_VCVTf16s16_fix:case MVE_VCVTf16s16n:case MVE_VCVTf16u16_fix:case MVE_VCVTf16u16n:case MVE_VCVTf32s32_fix:case MVE_VCVTf32s32n:case MVE_VCVTf32u32_fix:case MVE_VCVTf32u32n:case MVE_VCVTs16f16_fix:case MVE_VCVTs16f16a:case MVE_VCVTs16f16m:case MVE_VCVTs16f16n:case MVE_VCVTs16f16p:case MVE_VCVTs16f16z:case MVE_VCVTs32f32_fix:case MVE_VCVTs32f32a:case MVE_VCVTs32f32m:case MVE_VCVTs32f32n:case MVE_VCVTs32f32p:case MVE_VCVTs32f32z:case MVE_VCVTu16f16_fix:case MVE_VCVTu16f16a:case MVE_VCVTu16f16m:case MVE_VCVTu16f16n:case MVE_VCVTu16f16p:case MVE_VCVTu16f16z:case MVE_VCVTu32f32_fix:case MVE_VCVTu32f32a:case MVE_VCVTu32f32m:case MVE_VCVTu32f32n:case MVE_VCVTu32f32p:case MVE_VCVTu32f32z:case MVE_VDDUPu16:case MVE_VDDUPu32:case MVE_VDDUPu8:case MVE_VDUP16:case MVE_VDUP32:case MVE_VDUP8:case MVE_VDWDUPu16:case MVE_VDWDUPu32:case MVE_VDWDUPu8:case MVE_VEOR:case MVE_VFMA_qr_Sf16:case MVE_VFMA_qr_Sf32:case MVE_VFMA_qr_f16:case MVE_VFMA_qr_f32:case MVE_VFMAf16:case MVE_VFMAf32:case MVE_VFMSf16:case MVE_VFMSf32:case MVE_VMAXAs16:case MVE_VMAXAs32:case MVE_VMAXAs8:case MVE_VMAXs16:case MVE_VMAXs32:case MVE_VMAXs8:case MVE_VMAXu16:case MVE_VMAXu32:case MVE_VMAXu8:case MVE_VMAXNMf16:case MVE_VMAXNMf32:case MVE_VMAXNMAf16:case MVE_VMAXNMAf32:case MVE_VMINAs16:case MVE_VMINAs32:case MVE_VMINAs8:case MVE_VMINs16:case MVE_VMINs32:case MVE_VMINs8:case MVE_VMINu16:case MVE_VMINu32:case MVE_VMINu8:case MVE_VMINNMf16:case MVE_VMINNMf32:case MVE_VMINNMAf16:case MVE_VMINNMAf32:case MVE_VMLADAVas16:case MVE_VMLADAVas32:case MVE_VMLADAVas8:case MVE_VMLADAVau16:case MVE_VMLADAVau32:case MVE_VMLADAVau8:case MVE_VMLADAVs16:case MVE_VMLADAVs32:case MVE_VMLADAVs8:case MVE_VMLADAVu16:case MVE_VMLADAVu32:case MVE_VMLADAVu8:case MVE_VMLALDAVs16:case MVE_VMLALDAVs32:case MVE_VMLALDAVu16:case MVE_VMLALDAVu32:case MVE_VMLALDAVas16:case MVE_VMLALDAVas32:case MVE_VMLALDAVau16:case MVE_VMLALDAVau32:case MVE_VMLSDAVas16:case MVE_VMLSDAVas32:case MVE_VMLSDAVas8:case MVE_VMLSDAVs16:case MVE_VMLSDAVs32:case MVE_VMLSDAVs8:case MVE_VMLSLDAVas16:case MVE_VMLSLDAVas32:case MVE_VMLSLDAVs16:case MVE_VMLSLDAVs32:case MVE_VRMLALDAVHas32:case MVE_VRMLALDAVHau32:case MVE_VRMLALDAVHs32:case MVE_VRMLALDAVHu32:case MVE_VRMLSLDAVHas32:case MVE_VRMLSLDAVHs32:case MVE_VMLAS_qr_s16:case MVE_VMLAS_qr_s32:case MVE_VMLAS_qr_s8:case MVE_VMLAS_qr_u16:case MVE_VMLAS_qr_u32:case MVE_VMLAS_qr_u8:case MVE_VMLA_qr_s16:case MVE_VMLA_qr_s32:case MVE_VMLA_qr_s8:case MVE_VMLA_qr_u16:case MVE_VMLA_qr_u32:case MVE_VMLA_qr_u8:case MVE_VHADD_qr_s16:case MVE_VHADD_qr_s32:case MVE_VHADD_qr_s8:case MVE_VHADD_qr_u16:case MVE_VHADD_qr_u32:case MVE_VHADD_qr_u8:case MVE_VHADDs16:case MVE_VHADDs32:case MVE_VHADDs8:case MVE_VHADDu16:case MVE_VHADDu32:case MVE_VHADDu8:case MVE_VHSUB_qr_s16:case MVE_VHSUB_qr_s32:case MVE_VHSUB_qr_s8:case MVE_VHSUB_qr_u16:case MVE_VHSUB_qr_u32:case MVE_VHSUB_qr_u8:case MVE_VHSUBs16:case MVE_VHSUBs32:case MVE_VHSUBs8:case MVE_VHSUBu16:case MVE_VHSUBu32:case MVE_VHSUBu8:case MVE_VIDUPu16:case MVE_VIDUPu32:case MVE_VIDUPu8:case MVE_VIWDUPu16:case MVE_VIWDUPu32:case MVE_VIWDUPu8:case MVE_VLD20_8:case MVE_VLD21_8:case MVE_VLD20_16:case MVE_VLD21_16:case MVE_VLD20_32:case MVE_VLD21_32:case MVE_VLD20_8_wb:case MVE_VLD21_8_wb:case MVE_VLD20_16_wb:case MVE_VLD21_16_wb:case MVE_VLD20_32_wb:case MVE_VLD21_32_wb:case MVE_VLD40_8:case MVE_VLD41_8:case MVE_VLD42_8:case MVE_VLD43_8:case MVE_VLD40_16:case MVE_VLD41_16:case MVE_VLD42_16:case MVE_VLD43_16:case MVE_VLD40_32:case MVE_VLD41_32:case MVE_VLD42_32:case MVE_VLD43_32:case MVE_VLD40_8_wb:case MVE_VLD41_8_wb:case MVE_VLD42_8_wb:case MVE_VLD43_8_wb:case MVE_VLD40_16_wb:case MVE_VLD41_16_wb:case MVE_VLD42_16_wb:case MVE_VLD43_16_wb:case MVE_VLD40_32_wb:case MVE_VLD41_32_wb:case MVE_VLD42_32_wb:case MVE_VLD43_32_wb:case MVE_VLDRBS16:case MVE_VLDRBS16_post:case MVE_VLDRBS16_pre:case MVE_VLDRBS16_rq:case MVE_VLDRBS32:case MVE_VLDRBS32_post:case MVE_VLDRBS32_pre:case MVE_VLDRBS32_rq:case MVE_VLDRBU16:case MVE_VLDRBU16_post:case MVE_VLDRBU16_pre:case MVE_VLDRBU16_rq:case MVE_VLDRBU32:case MVE_VLDRBU32_post:case MVE_VLDRBU32_pre:case MVE_VLDRBU32_rq:case MVE_VLDRBU8:case MVE_VLDRBU8_post:case MVE_VLDRBU8_pre:case MVE_VLDRBU8_rq:case MVE_VLDRDU64_qi:case MVE_VLDRDU64_qi_pre:case MVE_VLDRDU64_rq:case MVE_VLDRDU64_rq_u:case MVE_VLDRHS32:case MVE_VLDRHS32_post:case MVE_VLDRHS32_pre:case MVE_VLDRHS32_rq:case MVE_VLDRHS32_rq_u:case MVE_VLDRHU16:case MVE_VLDRHU16_post:case MVE_VLDRHU16_pre:case MVE_VLDRHU16_rq:case MVE_VLDRHU16_rq_u:case MVE_VLDRHU32:case MVE_VLDRHU32_post:case MVE_VLDRHU32_pre:case MVE_VLDRHU32_rq:case MVE_VLDRHU32_rq_u:case MVE_VLDRWU32:case MVE_VLDRWU32_post:case MVE_VLDRWU32_pre:case MVE_VLDRWU32_qi:case MVE_VLDRWU32_qi_pre:case MVE_VLDRWU32_rq:case MVE_VLDRWU32_rq_u:case MVE_VMOVimmf32:case MVE_VMOVimmi16:case MVE_VMOVimmi32:case MVE_VMOVimmi64:case MVE_VMOVimmi8:case MVE_VMOVNi16bh:case MVE_VMOVNi16th:case MVE_VMOVNi32bh:case MVE_VMOVNi32th:case MVE_VMULLBp16:case MVE_VMULLBp8:case MVE_VMULLBs16:case MVE_VMULLBs32:case MVE_VMULLBs8:case MVE_VMULLBu16:case MVE_VMULLBu32:case MVE_VMULLBu8:case MVE_VMULLTp16:case MVE_VMULLTp8:case MVE_VMULLTs16:case MVE_VMULLTs32:case MVE_VMULLTs8:case MVE_VMULLTu16:case MVE_VMULLTu32:case MVE_VMULLTu8:case MVE_VMUL_qr_f16:case MVE_VMUL_qr_f32:case MVE_VMUL_qr_i16:case MVE_VMUL_qr_i32:case MVE_VMUL_qr_i8:case MVE_VMULf16:case MVE_VMULf32:case MVE_VMULi16:case MVE_VMULi8:case MVE_VMULi32:case MVE_VMULHs32:case MVE_VMULHs16:case MVE_VMULHs8:case MVE_VMULHu32:case MVE_VMULHu16:case MVE_VMULHu8:case MVE_VMVN:case MVE_VMVNimmi16:case MVE_VMVNimmi32:case MVE_VNEGf16:case MVE_VNEGf32:case MVE_VNEGs16:case MVE_VNEGs32:case MVE_VNEGs8:case MVE_VORN:case MVE_VORR:case MVE_VORRimmi16:case MVE_VORRimmi32:case MVE_VPST:case MVE_VPTv16i8:case MVE_VPTv8i16:case MVE_VPTv4i32:case MVE_VPTv16i8r:case MVE_VPTv8i16r:case MVE_VPTv4i32r:case MVE_VPTv16s8:case MVE_VPTv8s16:case MVE_VPTv4s32:case MVE_VPTv16s8r:case MVE_VPTv8s16r:case MVE_VPTv4s32r:case MVE_VPTv16u8:case MVE_VPTv8u16:case MVE_VPTv4u32:case MVE_VPTv16u8r:case MVE_VPTv8u16r:case MVE_VPTv4u32r:case MVE_VPTv8f16:case MVE_VPTv4f32:case MVE_VPTv8f16r:case MVE_VPTv4f32r:case MVE_VQABSs16:case MVE_VQABSs32:case MVE_VQABSs8:case MVE_VQADD_qr_s16:case MVE_VQADD_qr_s32:case MVE_VQADD_qr_s8:case MVE_VQADD_qr_u16:case MVE_VQADD_qr_u32:case MVE_VQADD_qr_u8:case MVE_VQADDs16:case MVE_VQADDs32:case MVE_VQADDs8:case MVE_VQADDu16:case MVE_VQADDu32:case MVE_VQADDu8:case MVE_VQDMULH_qr_s16:case MVE_VQDMULH_qr_s32:case MVE_VQDMULH_qr_s8:case MVE_VQDMULHi16:case MVE_VQDMULHi32:case MVE_VQDMULHi8:case MVE_VQDMULL_qr_s16bh:case MVE_VQDMULL_qr_s16th:case MVE_VQDMULL_qr_s32bh:case MVE_VQDMULL_qr_s32th:case MVE_VQDMULLs16bh:case MVE_VQDMULLs16th:case MVE_VQDMULLs32bh:case MVE_VQDMULLs32th:case MVE_VQRDMULH_qr_s16:case MVE_VQRDMULH_qr_s32:case MVE_VQRDMULH_qr_s8:case MVE_VQRDMULHi16:case MVE_VQRDMULHi32:case MVE_VQRDMULHi8:case MVE_VQNEGs16:case MVE_VQNEGs32:case MVE_VQNEGs8:case MVE_VQMOVNs16bh:case MVE_VQMOVNs16th:case MVE_VQMOVNs32bh:case MVE_VQMOVNs32th:case MVE_VQMOVNu16bh:case MVE_VQMOVNu16th:case MVE_VQMOVNu32bh:case MVE_VQMOVNu32th:case MVE_VQMOVUNs16bh:case MVE_VQMOVUNs16th:case MVE_VQMOVUNs32bh:case MVE_VQMOVUNs32th:case MVE_VQRSHL_by_vecs16:case MVE_VQRSHL_by_vecs32:case MVE_VQRSHL_by_vecs8:case MVE_VQRSHL_by_vecu16:case MVE_VQRSHL_by_vecu32:case MVE_VQRSHL_by_vecu8:case MVE_VQRSHL_qrs16:case MVE_VQRSHL_qrs32:case MVE_VQRSHL_qrs8:case MVE_VQRSHL_qru16:case MVE_VQRSHL_qru8:case MVE_VQRSHL_qru32:case MVE_VQSHLU_imms16:case MVE_VQSHLU_imms32:case MVE_VQSHLU_imms8:case MVE_VQSHLimms16:case MVE_VQSHLimms32:case MVE_VQSHLimms8:case MVE_VQSHLimmu16:case MVE_VQSHLimmu32:case MVE_VQSHLimmu8:case MVE_VQSHL_by_vecs16:case MVE_VQSHL_by_vecs32:case MVE_VQSHL_by_vecs8:case MVE_VQSHL_by_vecu16:case MVE_VQSHL_by_vecu32:case MVE_VQSHL_by_vecu8:case MVE_VQSHL_qrs16:case MVE_VQSHL_qrs32:case MVE_VQSHL_qrs8:case MVE_VQSHL_qru16:case MVE_VQSHL_qru32:case MVE_VQSHL_qru8:case MVE_VQRSHRNbhs16:case MVE_VQRSHRNbhs32:case MVE_VQRSHRNbhu16:case MVE_VQRSHRNbhu32:case MVE_VQRSHRNths16:case MVE_VQRSHRNths32:case MVE_VQRSHRNthu16:case MVE_VQRSHRNthu32:case MVE_VQRSHRUNs16bh:case MVE_VQRSHRUNs16th:case MVE_VQRSHRUNs32bh:case MVE_VQRSHRUNs32th:case MVE_VQSHRNbhs16:case MVE_VQSHRNbhs32:case MVE_VQSHRNbhu16:case MVE_VQSHRNbhu32:case MVE_VQSHRNths16:case MVE_VQSHRNths32:case MVE_VQSHRNthu16:case MVE_VQSHRNthu32:case MVE_VQSHRUNs16bh:case MVE_VQSHRUNs16th:case MVE_VQSHRUNs32bh:case MVE_VQSHRUNs32th:case MVE_VQSUB_qr_s16:case MVE_VQSUB_qr_s32:case MVE_VQSUB_qr_s8:case MVE_VQSUB_qr_u16:case MVE_VQSUB_qr_u32:case MVE_VQSUB_qr_u8:case MVE_VQSUBs16:case MVE_VQSUBs32:case MVE_VQSUBs8:case MVE_VQSUBu16:case MVE_VQSUBu32:case MVE_VQSUBu8:case MVE_VRHADDs16:case MVE_VRHADDs32:case MVE_VRHADDs8:case MVE_VRHADDu16:case MVE_VRHADDu32:case MVE_VRHADDu8:case MVE_VRINTf16A:case MVE_VRINTf16M:case MVE_VRINTf16N:case MVE_VRINTf16P:case MVE_VRINTf16X:case MVE_VRINTf16Z:case MVE_VRINTf32A:case MVE_VRINTf32M:case MVE_VRINTf32N:case MVE_VRINTf32P:case MVE_VRINTf32X:case MVE_VRINTf32Z:case MVE_VRMULHs32:case MVE_VRMULHs16:case MVE_VRMULHs8:case MVE_VRMULHu32:case MVE_VRMULHu16:case MVE_VRMULHu8:case MVE_VRSHL_by_vecs16:case MVE_VRSHL_by_vecs32:case MVE_VRSHL_by_vecs8:case MVE_VRSHL_by_vecu16:case MVE_VRSHL_by_vecu32:case MVE_VRSHL_by_vecu8:case MVE_VRSHL_qrs16:case MVE_VRSHL_qrs32:case MVE_VRSHL_qrs8:case MVE_VRSHL_qru16:case MVE_VRSHL_qru32:case MVE_VRSHL_qru8:case MVE_VRSHR_imms16:case MVE_VRSHR_imms32:case MVE_VRSHR_imms8:case MVE_VRSHR_immu16:case MVE_VRSHR_immu32:case MVE_VRSHR_immu8:case MVE_VRSHRNi16bh:case MVE_VRSHRNi16th:case MVE_VRSHRNi32bh:case MVE_VRSHRNi32th:case MVE_VSHL_by_vecs16:case MVE_VSHL_by_vecs32:case MVE_VSHL_by_vecs8:case MVE_VSHL_by_vecu16:case MVE_VSHL_by_vecu32:case MVE_VSHL_by_vecu8:case MVE_VSHL_immi16:case MVE_VSHL_immi32:case MVE_VSHL_immi8:case MVE_VSHL_qrs16:case MVE_VSHL_qrs32:case MVE_VSHL_qrs8:case MVE_VSHL_qru16:case MVE_VSHL_qru32:case MVE_VSHL_qru8:case MVE_VSHR_imms16:case MVE_VSHR_imms32:case MVE_VSHR_imms8:case MVE_VSHR_immu16:case MVE_VSHR_immu32:case MVE_VSHR_immu8:case MVE_VSHRNi16bh:case MVE_VSHRNi16th:case MVE_VSHRNi32bh:case MVE_VSHRNi32th:case MVE_VSLIimm16:case MVE_VSLIimm32:case MVE_VSLIimm8:case MVE_VSRIimm16:case MVE_VSRIimm32:case MVE_VSRIimm8:case MVE_VSTRB16:case MVE_VSTRB16_post:case MVE_VSTRB16_pre:case MVE_VSTRB16_rq:case MVE_VSTRB32:case MVE_VSTRB32_post:case MVE_VSTRB32_pre:case MVE_VSTRB32_rq:case MVE_VSTRB8_rq:case MVE_VSTRBU8:case MVE_VSTRBU8_post:case MVE_VSTRBU8_pre:case MVE_VSTRD64_qi:case MVE_VSTRD64_qi_pre:case MVE_VSTRD64_rq:case MVE_VSTRD64_rq_u:case MVE_VSTRH16_rq:case MVE_VSTRH16_rq_u:case MVE_VSTRH32:case MVE_VSTRH32_post:case MVE_VSTRH32_pre:case MVE_VSTRH32_rq:case MVE_VSTRH32_rq_u:case MVE_VSTRHU16:case MVE_VSTRHU16_post:case MVE_VSTRHU16_pre:case MVE_VSTRW32_qi:case MVE_VSTRW32_qi_pre:case MVE_VSTRW32_rq:case MVE_VSTRW32_rq_u:case MVE_VSTRWU32:case MVE_VSTRWU32_post:case MVE_VSTRWU32_pre:case MVE_VSUB_qr_f16:case MVE_VSUB_qr_f32:case MVE_VSUB_qr_i16:case MVE_VSUB_qr_i32:case MVE_VSUB_qr_i8:case MVE_VSUBf16:case MVE_VSUBf32:case MVE_VSUBi16:case MVE_VSUBi32:case MVE_VSUBi8:case VLDR_P0_off:case VLDR_P0_post:case VLDR_P0_pre:case VLDR_VPR_off:case VLDR_VPR_post:case VLDR_VPR_pre:case VSTR_P0_off:case VSTR_P0_post:case VSTR_P0_pre:case VSTR_VPR_off:case VSTR_VPR_post:case VSTR_VPR_pre:case VMRS_P0:case VMRS_VPR:return true;}};LLVMInitializeARMTargetInfo();LLVMInitializeARMTarget();LLVMInitializeARMTargetMC();auto TT(Triple::normalize("thumbv8.1m.main-none-none-eabi"));std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T) {dbgs() << Error;return;}TargetOptions Options;auto TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(T->createTargetMachine(TT, "generic", "", Options, None, None,CodeGenOpt::Default)));ARMSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()),*static_cast<const ARMBaseTargetMachine *>(TM.get()), false);auto MII = TM->getMCInstrInfo();for (unsigned i = 0; i < ARM::INSTRUCTION_LIST_END; ++i) {uint64_t Flags = MII->get(i).TSFlags;if ((Flags & ARMII::DomainMask) != ARMII::DomainMVE)continue;bool Valid = (Flags & ARMII::ValidForTailPredication) != 0;ASSERT_EQ(IsValidTPOpcode(i), Valid)<< MII->getName(i)<< ": mismatched expectation for tail-predicated safety\n";}}TEST(MachineInstr, HasSideEffects) {using namespace ARM;std::set<unsigned> UnpredictableOpcodes = {// MVE InstructionsMVE_VCTP8,MVE_VCTP16,MVE_VCTP32,MVE_VCTP64,MVE_VPST,MVE_VPTv16i8,MVE_VPTv8i16,MVE_VPTv4i32,MVE_VPTv16i8r,MVE_VPTv8i16r,MVE_VPTv4i32r,MVE_VPTv16s8,MVE_VPTv8s16,MVE_VPTv4s32,MVE_VPTv16s8r,MVE_VPTv8s16r,MVE_VPTv4s32r,MVE_VPTv16u8,MVE_VPTv8u16,MVE_VPTv4u32,MVE_VPTv16u8r,MVE_VPTv8u16r,MVE_VPTv4u32r,MVE_VPTv8f16,MVE_VPTv4f32,MVE_VPTv8f16r,MVE_VPTv4f32r,MVE_VADC,MVE_VADCI,MVE_VSBC,MVE_VSBCI,MVE_VSHLC,// FP InstructionsFLDMXIA,FLDMXDB_UPD,FLDMXIA_UPD,FSTMXDB_UPD,FSTMXIA,FSTMXIA_UPD,VLDR_FPCXTNS_off,VLDR_FPCXTNS_off,VLDR_FPCXTNS_post,VLDR_FPCXTNS_pre,VLDR_FPCXTS_off,VLDR_FPCXTS_post,VLDR_FPCXTS_pre,VLDR_FPSCR_NZCVQC_off,VLDR_FPSCR_NZCVQC_post,VLDR_FPSCR_NZCVQC_pre,VLDR_FPSCR_off,VLDR_FPSCR_post,VLDR_FPSCR_pre,VLDR_P0_off,VLDR_P0_post,VLDR_P0_pre,VLDR_VPR_off,VLDR_VPR_post,VLDR_VPR_pre,VLLDM,VLSTM,VMRS,VMRS_FPCXTNS,VMRS_FPCXTS,VMRS_FPEXC,VMRS_FPINST,VMRS_FPINST2,VMRS_FPSCR_NZCVQC,VMRS_FPSID,VMRS_MVFR0,VMRS_MVFR1,VMRS_MVFR2,VMRS_P0,VMRS_VPR,VMSR,VMSR_FPCXTNS,VMSR_FPCXTS,VMSR_FPEXC,VMSR_FPINST,VMSR_FPINST2,VMSR_FPSCR_NZCVQC,VMSR_FPSID,VMSR_P0,VMSR_VPR,VSCCLRMD,VSCCLRMS,VSTR_FPCXTNS_off,VSTR_FPCXTNS_post,VSTR_FPCXTNS_pre,VSTR_FPCXTS_off,VSTR_FPCXTS_post,VSTR_FPCXTS_pre,VSTR_FPSCR_NZCVQC_off,VSTR_FPSCR_NZCVQC_post,VSTR_FPSCR_NZCVQC_pre,VSTR_FPSCR_off,VSTR_FPSCR_post,VSTR_FPSCR_pre,VSTR_P0_off,VSTR_P0_post,VSTR_P0_pre,VSTR_VPR_off,VSTR_VPR_post,VSTR_VPR_pre,};LLVMInitializeARMTargetInfo();LLVMInitializeARMTarget();LLVMInitializeARMTargetMC();auto TT(Triple::normalize("thumbv8.1m.main-none-none-eabi"));std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T) {dbgs() << Error;return;}TargetOptions Options;auto TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine(TT, "generic", "", Options, None, None, CodeGenOpt::Default)));ARMSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()),*static_cast<const ARMBaseTargetMachine *>(TM.get()), false);const ARMBaseInstrInfo *TII = ST.getInstrInfo();auto MII = TM->getMCInstrInfo();for (unsigned Op = 0; Op < ARM::INSTRUCTION_LIST_END; ++Op) {const MCInstrDesc &Desc = TII->get(Op);if ((Desc.TSFlags &(ARMII::DomainMVE | ARMII::DomainVFP | ARMII::DomainNEONA8)) == 0)continue;if (UnpredictableOpcodes.count(Op))continue;ASSERT_FALSE(Desc.hasUnmodeledSideEffects())<< MII->getName(Op) << " has unexpected side effects";}}TEST(MachineInstr, MVEVecSize) {using namespace ARM;auto MVEVecSize = [](unsigned Opcode) {switch (Opcode) {default:dbgs() << Opcode << "\n";llvm_unreachable("Unexpected MVE instruction!");case MVE_ASRLi:case MVE_ASRLr:case MVE_LSLLi:case MVE_LSLLr:case MVE_LSRL:case MVE_SQRSHR:case MVE_SQRSHRL:case MVE_SQSHL:case MVE_SQSHLL:case MVE_SRSHR:case MVE_SRSHRL:case MVE_UQRSHL:case MVE_UQRSHLL:case MVE_UQSHL:case MVE_UQSHLL:case MVE_URSHR:case MVE_URSHRL:case MVE_VABAVs8:case MVE_VABAVu8:case MVE_VABDs8:case MVE_VABDu8:case MVE_VABSs8:case MVE_VADDVs8acc:case MVE_VADDVs8no_acc:case MVE_VADDVu8acc:case MVE_VADDVu8no_acc:case MVE_VADD_qr_i8:case MVE_VADDi8:case MVE_VBRSR8:case MVE_VCADDi8:case MVE_VCLSs8:case MVE_VCLZs8:case MVE_VCMPi8:case MVE_VCMPi8r:case MVE_VCMPs8:case MVE_VCMPs8r:case MVE_VCMPu8:case MVE_VCMPu8r:case MVE_VCTP8:case MVE_VDDUPu8:case MVE_VDUP8:case MVE_VDWDUPu8:case MVE_VHADD_qr_s8:case MVE_VHADD_qr_u8:case MVE_VHADDs8:case MVE_VHADDu8:case MVE_VHCADDs8:case MVE_VHSUB_qr_s8:case MVE_VHSUB_qr_u8:case MVE_VHSUBs8:case MVE_VHSUBu8:case MVE_VIDUPu8:case MVE_VIWDUPu8:case MVE_VLD20_8:case MVE_VLD20_8_wb:case MVE_VLD21_8:case MVE_VLD21_8_wb:case MVE_VLD40_8:case MVE_VLD40_8_wb:case MVE_VLD41_8:case MVE_VLD41_8_wb:case MVE_VLD42_8:case MVE_VLD42_8_wb:case MVE_VLD43_8:case MVE_VLD43_8_wb:case MVE_VLDRBU8:case MVE_VLDRBU8_post:case MVE_VLDRBU8_pre:case MVE_VLDRBU8_rq:case MVE_VMAXAVs8:case MVE_VMAXAs8:case MVE_VMAXVs8:case MVE_VMAXVu8:case MVE_VMAXs8:case MVE_VMAXu8:case MVE_VMINAVs8:case MVE_VMINAs8:case MVE_VMINVs8:case MVE_VMINVu8:case MVE_VMINs8:case MVE_VMINu8:case MVE_VMLADAVas8:case MVE_VMLADAVau8:case MVE_VMLADAVaxs8:case MVE_VMLADAVs8:case MVE_VMLADAVu8:case MVE_VMLADAVxs8:case MVE_VMLAS_qr_s8:case MVE_VMLAS_qr_u8:case MVE_VMLA_qr_s8:case MVE_VMLA_qr_u8:case MVE_VMLSDAVas8:case MVE_VMLSDAVaxs8:case MVE_VMLSDAVs8:case MVE_VMLSDAVxs8:case MVE_VMOV_from_lane_s8:case MVE_VMOV_from_lane_u8:case MVE_VMOV_to_lane_8:case MVE_VMOVimmi8:case MVE_VMULHs8:case MVE_VMULHu8:case MVE_VMUL_qr_i8:case MVE_VMULi8:case MVE_VNEGs8:case MVE_VPTv16i8:case MVE_VPTv16i8r:case MVE_VPTv16s8:case MVE_VPTv16s8r:case MVE_VPTv16u8:case MVE_VPTv16u8r:case MVE_VQABSs8:case MVE_VQADD_qr_s8:case MVE_VQADD_qr_u8:case MVE_VQADDs8:case MVE_VQADDu8:case MVE_VQDMLADHXs8:case MVE_VQDMLADHs8:case MVE_VQDMLAH_qrs8:case MVE_VQDMLASH_qrs8:case MVE_VQDMLSDHXs8:case MVE_VQDMLSDHs8:case MVE_VQDMULH_qr_s8:case MVE_VQDMULHi8:case MVE_VQNEGs8:case MVE_VQRDMLADHXs8:case MVE_VQRDMLADHs8:case MVE_VQRDMLAH_qrs8:case MVE_VQRDMLASH_qrs8:case MVE_VQRDMLSDHXs8:case MVE_VQRDMLSDHs8:case MVE_VQRDMULH_qr_s8:case MVE_VQRDMULHi8:case MVE_VQRSHL_by_vecs8:case MVE_VQRSHL_by_vecu8:case MVE_VQRSHL_qrs8:case MVE_VQRSHL_qru8:case MVE_VQSHLU_imms8:case MVE_VQSHL_by_vecs8:case MVE_VQSHL_by_vecu8:case MVE_VQSHL_qrs8:case MVE_VQSHL_qru8:case MVE_VQSHLimms8:case MVE_VQSHLimmu8:case MVE_VQSUB_qr_s8:case MVE_VQSUB_qr_u8:case MVE_VQSUBs8:case MVE_VQSUBu8:case MVE_VRHADDs8:case MVE_VRHADDu8:case MVE_VRMULHs8:case MVE_VRMULHu8:case MVE_VRSHL_by_vecs8:case MVE_VRSHL_by_vecu8:case MVE_VRSHL_qrs8:case MVE_VRSHL_qru8:case MVE_VRSHR_imms8:case MVE_VRSHR_immu8:case MVE_VSHL_by_vecs8:case MVE_VSHL_by_vecu8:case MVE_VSHL_immi8:case MVE_VSHL_qru8:case MVE_VSHL_qrs8:case MVE_VSHR_imms8:case MVE_VSHR_immu8:case MVE_VSLIimm8:case MVE_VSRIimm8:case MVE_VST20_8:case MVE_VST20_8_wb:case MVE_VST21_8:case MVE_VST21_8_wb:case MVE_VST40_8:case MVE_VST40_8_wb:case MVE_VST41_8:case MVE_VST41_8_wb:case MVE_VST42_8:case MVE_VST42_8_wb:case MVE_VST43_8:case MVE_VST43_8_wb:case MVE_VSTRB8_rq:case MVE_VSTRBU8:case MVE_VSTRBU8_post:case MVE_VSTRBU8_pre:case MVE_VSUB_qr_i8:case MVE_VSUBi8:case MVE_VAND:case MVE_VBIC:case MVE_VEOR:case MVE_VMVN:case MVE_VORN:case MVE_VORR:case MVE_VPNOT:case MVE_VPSEL:case MVE_VPST:case MQPRCopy:return 0;case MVE_VABAVs16:case MVE_VABAVu16:case MVE_VABDf16:case MVE_VABDs16:case MVE_VABDu16:case MVE_VABSf16:case MVE_VABSs16:case MVE_VADDVs16acc:case MVE_VADDVs16no_acc:case MVE_VADDVu16acc:case MVE_VADDVu16no_acc:case MVE_VADD_qr_f16:case MVE_VADD_qr_i16:case MVE_VADDf16:case MVE_VADDi16:case MVE_VBICimmi16:case MVE_VBRSR16:case MVE_VCADDf16:case MVE_VCADDi16:case MVE_VCLSs16:case MVE_VCLZs16:case MVE_VCMLAf16:case MVE_VCMPf16:case MVE_VCMPf16r:case MVE_VCMPi16:case MVE_VCMPi16r:case MVE_VCMPs16:case MVE_VCMPs16r:case MVE_VCMPu16:case MVE_VCMPu16r:case MVE_VCMULf16:case MVE_VCTP16:case MVE_VCVTf16s16_fix:case MVE_VCVTf16s16n:case MVE_VCVTf16u16_fix:case MVE_VCVTf16u16n:case MVE_VCVTs16f16_fix:case MVE_VCVTs16f16a:case MVE_VCVTs16f16m:case MVE_VCVTs16f16n:case MVE_VCVTs16f16p:case MVE_VCVTs16f16z:case MVE_VCVTu16f16_fix:case MVE_VCVTu16f16a:case MVE_VCVTu16f16m:case MVE_VCVTu16f16n:case MVE_VCVTu16f16p:case MVE_VCVTu16f16z:case MVE_VDDUPu16:case MVE_VDUP16:case MVE_VDWDUPu16:case MVE_VFMA_qr_Sf16:case MVE_VFMA_qr_f16:case MVE_VFMAf16:case MVE_VFMSf16:case MVE_VHADD_qr_s16:case MVE_VHADD_qr_u16:case MVE_VHADDs16:case MVE_VHADDu16:case MVE_VHCADDs16:case MVE_VHSUB_qr_s16:case MVE_VHSUB_qr_u16:case MVE_VHSUBs16:case MVE_VHSUBu16:case MVE_VIDUPu16:case MVE_VIWDUPu16:case MVE_VLD20_16:case MVE_VLD20_16_wb:case MVE_VLD21_16:case MVE_VLD21_16_wb:case MVE_VLD40_16:case MVE_VLD40_16_wb:case MVE_VLD41_16:case MVE_VLD41_16_wb:case MVE_VLD42_16:case MVE_VLD42_16_wb:case MVE_VLD43_16:case MVE_VLD43_16_wb:case MVE_VLDRBS16:case MVE_VLDRBS16_post:case MVE_VLDRBS16_pre:case MVE_VLDRBS16_rq:case MVE_VLDRBU16:case MVE_VLDRBU16_post:case MVE_VLDRBU16_pre:case MVE_VLDRBU16_rq:case MVE_VLDRHU16:case MVE_VLDRHU16_post:case MVE_VLDRHU16_pre:case MVE_VLDRHU16_rq:case MVE_VLDRHU16_rq_u:case MVE_VMAXAVs16:case MVE_VMAXAs16:case MVE_VMAXNMAVf16:case MVE_VMAXNMAf16:case MVE_VMAXNMVf16:case MVE_VMAXNMf16:case MVE_VMAXVs16:case MVE_VMAXVu16:case MVE_VMAXs16:case MVE_VMAXu16:case MVE_VMINAVs16:case MVE_VMINAs16:case MVE_VMINNMAVf16:case MVE_VMINNMAf16:case MVE_VMINNMVf16:case MVE_VMINNMf16:case MVE_VMINVs16:case MVE_VMINVu16:case MVE_VMINs16:case MVE_VMINu16:case MVE_VMLADAVas16:case MVE_VMLADAVau16:case MVE_VMLADAVaxs16:case MVE_VMLADAVs16:case MVE_VMLADAVu16:case MVE_VMLADAVxs16:case MVE_VMLALDAVas16:case MVE_VMLALDAVau16:case MVE_VMLALDAVaxs16:case MVE_VMLALDAVs16:case MVE_VMLALDAVu16:case MVE_VMLALDAVxs16:case MVE_VMLAS_qr_s16:case MVE_VMLAS_qr_u16:case MVE_VMLA_qr_s16:case MVE_VMLA_qr_u16:case MVE_VMLSDAVas16:case MVE_VMLSDAVaxs16:case MVE_VMLSDAVs16:case MVE_VMLSDAVxs16:case MVE_VMLSLDAVas16:case MVE_VMLSLDAVaxs16:case MVE_VMLSLDAVs16:case MVE_VMLSLDAVxs16:case MVE_VMOVNi16bh:case MVE_VMOVNi16th:case MVE_VMOV_from_lane_s16:case MVE_VMOV_from_lane_u16:case MVE_VMOV_to_lane_16:case MVE_VMOVimmi16:case MVE_VMOVLs8bh:case MVE_VMOVLs8th:case MVE_VMOVLu8bh:case MVE_VMOVLu8th:case MVE_VMULLBp8:case MVE_VMULLBs8:case MVE_VMULLBu8:case MVE_VMULLTp8:case MVE_VMULLTs8:case MVE_VMULLTu8:case MVE_VMULHs16:case MVE_VMULHu16:case MVE_VMUL_qr_f16:case MVE_VMUL_qr_i16:case MVE_VMULf16:case MVE_VMULi16:case MVE_VMVNimmi16:case MVE_VNEGf16:case MVE_VNEGs16:case MVE_VORRimmi16:case MVE_VPTv8f16:case MVE_VPTv8f16r:case MVE_VPTv8i16:case MVE_VPTv8i16r:case MVE_VPTv8s16:case MVE_VPTv8s16r:case MVE_VPTv8u16:case MVE_VPTv8u16r:case MVE_VQABSs16:case MVE_VQADD_qr_s16:case MVE_VQADD_qr_u16:case MVE_VQADDs16:case MVE_VQADDu16:case MVE_VQDMLADHXs16:case MVE_VQDMLADHs16:case MVE_VQDMLAH_qrs16:case MVE_VQDMLASH_qrs16:case MVE_VQDMLSDHXs16:case MVE_VQDMLSDHs16:case MVE_VQDMULH_qr_s16:case MVE_VQDMULHi16:case MVE_VQDMULL_qr_s16bh:case MVE_VQDMULL_qr_s16th:case MVE_VQDMULLs16bh:case MVE_VQDMULLs16th:case MVE_VQMOVNs16bh:case MVE_VQMOVNs16th:case MVE_VQMOVNu16bh:case MVE_VQMOVNu16th:case MVE_VQMOVUNs16bh:case MVE_VQMOVUNs16th:case MVE_VQNEGs16:case MVE_VQRDMLADHXs16:case MVE_VQRDMLADHs16:case MVE_VQRDMLAH_qrs16:case MVE_VQRDMLASH_qrs16:case MVE_VQRDMLSDHXs16:case MVE_VQRDMLSDHs16:case MVE_VQRDMULH_qr_s16:case MVE_VQRDMULHi16:case MVE_VQRSHL_by_vecs16:case MVE_VQRSHL_by_vecu16:case MVE_VQRSHL_qrs16:case MVE_VQRSHL_qru16:case MVE_VQRSHRNbhs16:case MVE_VQRSHRNbhu16:case MVE_VQRSHRNths16:case MVE_VQRSHRNthu16:case MVE_VQRSHRUNs16bh:case MVE_VQRSHRUNs16th:case MVE_VQSHLU_imms16:case MVE_VQSHL_by_vecs16:case MVE_VQSHL_by_vecu16:case MVE_VQSHL_qrs16:case MVE_VQSHL_qru16:case MVE_VQSHLimms16:case MVE_VQSHLimmu16:case MVE_VQSHRNbhs16:case MVE_VQSHRNbhu16:case MVE_VQSHRNths16:case MVE_VQSHRNthu16:case MVE_VQSHRUNs16bh:case MVE_VQSHRUNs16th:case MVE_VQSUB_qr_s16:case MVE_VQSUB_qr_u16:case MVE_VQSUBs16:case MVE_VQSUBu16:case MVE_VREV16_8:case MVE_VRHADDs16:case MVE_VRHADDu16:case MVE_VRINTf16A:case MVE_VRINTf16M:case MVE_VRINTf16N:case MVE_VRINTf16P:case MVE_VRINTf16X:case MVE_VRINTf16Z:case MVE_VRMULHs16:case MVE_VRMULHu16:case MVE_VRSHL_by_vecs16:case MVE_VRSHL_by_vecu16:case MVE_VRSHL_qrs16:case MVE_VRSHL_qru16:case MVE_VRSHRNi16bh:case MVE_VRSHRNi16th:case MVE_VRSHR_imms16:case MVE_VRSHR_immu16:case MVE_VSHLL_imms8bh:case MVE_VSHLL_imms8th:case MVE_VSHLL_immu8bh:case MVE_VSHLL_immu8th:case MVE_VSHLL_lws8bh:case MVE_VSHLL_lws8th:case MVE_VSHLL_lwu8bh:case MVE_VSHLL_lwu8th:case MVE_VSHL_by_vecs16:case MVE_VSHL_by_vecu16:case MVE_VSHL_immi16:case MVE_VSHL_qrs16:case MVE_VSHL_qru16:case MVE_VSHRNi16bh:case MVE_VSHRNi16th:case MVE_VSHR_imms16:case MVE_VSHR_immu16:case MVE_VSLIimm16:case MVE_VSRIimm16:case MVE_VST20_16:case MVE_VST20_16_wb:case MVE_VST21_16:case MVE_VST21_16_wb:case MVE_VST40_16:case MVE_VST40_16_wb:case MVE_VST41_16:case MVE_VST41_16_wb:case MVE_VST42_16:case MVE_VST42_16_wb:case MVE_VST43_16:case MVE_VST43_16_wb:case MVE_VSTRB16:case MVE_VSTRB16_post:case MVE_VSTRB16_pre:case MVE_VSTRB16_rq:case MVE_VSTRH16_rq:case MVE_VSTRH16_rq_u:case MVE_VSTRHU16:case MVE_VSTRHU16_post:case MVE_VSTRHU16_pre:case MVE_VSUB_qr_f16:case MVE_VSUB_qr_i16:case MVE_VSUBf16:case MVE_VSUBi16:return 1;case MVE_VABAVs32:case MVE_VABAVu32:case MVE_VABDf32:case MVE_VABDs32:case MVE_VABDu32:case MVE_VABSf32:case MVE_VABSs32:case MVE_VADC:case MVE_VADCI:case MVE_VADDLVs32acc:case MVE_VADDLVs32no_acc:case MVE_VADDLVu32acc:case MVE_VADDLVu32no_acc:case MVE_VADDVs32acc:case MVE_VADDVs32no_acc:case MVE_VADDVu32acc:case MVE_VADDVu32no_acc:case MVE_VADD_qr_f32:case MVE_VADD_qr_i32:case MVE_VADDf32:case MVE_VADDi32:case MVE_VBICimmi32:case MVE_VBRSR32:case MVE_VCADDf32:case MVE_VCADDi32:case MVE_VCLSs32:case MVE_VCLZs32:case MVE_VCMLAf32:case MVE_VCMPf32:case MVE_VCMPf32r:case MVE_VCMPi32:case MVE_VCMPi32r:case MVE_VCMPs32:case MVE_VCMPs32r:case MVE_VCMPu32:case MVE_VCMPu32r:case MVE_VCMULf32:case MVE_VCTP32:case MVE_VCVTf16f32bh:case MVE_VCVTf16f32th:case MVE_VCVTf32f16bh:case MVE_VCVTf32f16th:case MVE_VCVTf32s32_fix:case MVE_VCVTf32s32n:case MVE_VCVTf32u32_fix:case MVE_VCVTf32u32n:case MVE_VCVTs32f32_fix:case MVE_VCVTs32f32a:case MVE_VCVTs32f32m:case MVE_VCVTs32f32n:case MVE_VCVTs32f32p:case MVE_VCVTs32f32z:case MVE_VCVTu32f32_fix:case MVE_VCVTu32f32a:case MVE_VCVTu32f32m:case MVE_VCVTu32f32n:case MVE_VCVTu32f32p:case MVE_VCVTu32f32z:case MVE_VDDUPu32:case MVE_VDUP32:case MVE_VDWDUPu32:case MVE_VFMA_qr_Sf32:case MVE_VFMA_qr_f32:case MVE_VFMAf32:case MVE_VFMSf32:case MVE_VHADD_qr_s32:case MVE_VHADD_qr_u32:case MVE_VHADDs32:case MVE_VHADDu32:case MVE_VHCADDs32:case MVE_VHSUB_qr_s32:case MVE_VHSUB_qr_u32:case MVE_VHSUBs32:case MVE_VHSUBu32:case MVE_VIDUPu32:case MVE_VIWDUPu32:case MVE_VLD20_32:case MVE_VLD20_32_wb:case MVE_VLD21_32:case MVE_VLD21_32_wb:case MVE_VLD40_32:case MVE_VLD40_32_wb:case MVE_VLD41_32:case MVE_VLD41_32_wb:case MVE_VLD42_32:case MVE_VLD42_32_wb:case MVE_VLD43_32:case MVE_VLD43_32_wb:case MVE_VLDRBS32:case MVE_VLDRBS32_post:case MVE_VLDRBS32_pre:case MVE_VLDRBS32_rq:case MVE_VLDRBU32:case MVE_VLDRBU32_post:case MVE_VLDRBU32_pre:case MVE_VLDRBU32_rq:case MVE_VLDRHS32:case MVE_VLDRHS32_post:case MVE_VLDRHS32_pre:case MVE_VLDRHS32_rq:case MVE_VLDRHS32_rq_u:case MVE_VLDRHU32:case MVE_VLDRHU32_post:case MVE_VLDRHU32_pre:case MVE_VLDRHU32_rq:case MVE_VLDRHU32_rq_u:case MVE_VLDRWU32:case MVE_VLDRWU32_post:case MVE_VLDRWU32_pre:case MVE_VLDRWU32_qi:case MVE_VLDRWU32_qi_pre:case MVE_VLDRWU32_rq:case MVE_VLDRWU32_rq_u:case MVE_VMAXAVs32:case MVE_VMAXAs32:case MVE_VMAXNMAVf32:case MVE_VMAXNMAf32:case MVE_VMAXNMVf32:case MVE_VMAXNMf32:case MVE_VMAXVs32:case MVE_VMAXVu32:case MVE_VMAXs32:case MVE_VMAXu32:case MVE_VMINAVs32:case MVE_VMINAs32:case MVE_VMINNMAVf32:case MVE_VMINNMAf32:case MVE_VMINNMVf32:case MVE_VMINNMf32:case MVE_VMINVs32:case MVE_VMINVu32:case MVE_VMINs32:case MVE_VMINu32:case MVE_VMLADAVas32:case MVE_VMLADAVau32:case MVE_VMLADAVaxs32:case MVE_VMLADAVs32:case MVE_VMLADAVu32:case MVE_VMLADAVxs32:case MVE_VMLALDAVas32:case MVE_VMLALDAVau32:case MVE_VMLALDAVaxs32:case MVE_VMLALDAVs32:case MVE_VMLALDAVu32:case MVE_VMLALDAVxs32:case MVE_VMLAS_qr_s32:case MVE_VMLAS_qr_u32:case MVE_VMLA_qr_s32:case MVE_VMLA_qr_u32:case MVE_VMLSDAVas32:case MVE_VMLSDAVaxs32:case MVE_VMLSDAVs32:case MVE_VMLSDAVxs32:case MVE_VMLSLDAVas32:case MVE_VMLSLDAVaxs32:case MVE_VMLSLDAVs32:case MVE_VMLSLDAVxs32:case MVE_VMOVNi32bh:case MVE_VMOVNi32th:case MVE_VMOV_from_lane_32:case MVE_VMOV_q_rr:case MVE_VMOV_rr_q:case MVE_VMOV_to_lane_32:case MVE_VMOVimmf32:case MVE_VMOVimmi32:case MVE_VMOVLs16bh:case MVE_VMOVLs16th:case MVE_VMOVLu16bh:case MVE_VMOVLu16th:case MVE_VMULHs32:case MVE_VMULHu32:case MVE_VMULLBp16:case MVE_VMULLBs16:case MVE_VMULLBu16:case MVE_VMULLTp16:case MVE_VMULLTs16:case MVE_VMULLTu16:case MVE_VMUL_qr_f32:case MVE_VMUL_qr_i32:case MVE_VMULf32:case MVE_VMULi32:case MVE_VMVNimmi32:case MVE_VNEGf32:case MVE_VNEGs32:case MVE_VORRimmi32:case MVE_VPTv4f32:case MVE_VPTv4f32r:case MVE_VPTv4i32:case MVE_VPTv4i32r:case MVE_VPTv4s32:case MVE_VPTv4s32r:case MVE_VPTv4u32:case MVE_VPTv4u32r:case MVE_VQABSs32:case MVE_VQADD_qr_s32:case MVE_VQADD_qr_u32:case MVE_VQADDs32:case MVE_VQADDu32:case MVE_VQDMLADHXs32:case MVE_VQDMLADHs32:case MVE_VQDMLAH_qrs32:case MVE_VQDMLASH_qrs32:case MVE_VQDMLSDHXs32:case MVE_VQDMLSDHs32:case MVE_VQDMULH_qr_s32:case MVE_VQDMULHi32:case MVE_VQDMULL_qr_s32bh:case MVE_VQDMULL_qr_s32th:case MVE_VQDMULLs32bh:case MVE_VQDMULLs32th:case MVE_VQMOVNs32bh:case MVE_VQMOVNs32th:case MVE_VQMOVNu32bh:case MVE_VQMOVNu32th:case MVE_VQMOVUNs32bh:case MVE_VQMOVUNs32th:case MVE_VQNEGs32:case MVE_VQRDMLADHXs32:case MVE_VQRDMLADHs32:case MVE_VQRDMLAH_qrs32:case MVE_VQRDMLASH_qrs32:case MVE_VQRDMLSDHXs32:case MVE_VQRDMLSDHs32:case MVE_VQRDMULH_qr_s32:case MVE_VQRDMULHi32:case MVE_VQRSHL_by_vecs32:case MVE_VQRSHL_by_vecu32:case MVE_VQRSHL_qrs32:case MVE_VQRSHL_qru32:case MVE_VQRSHRNbhs32:case MVE_VQRSHRNbhu32:case MVE_VQRSHRNths32:case MVE_VQRSHRNthu32:case MVE_VQRSHRUNs32bh:case MVE_VQRSHRUNs32th:case MVE_VQSHLU_imms32:case MVE_VQSHL_by_vecs32:case MVE_VQSHL_by_vecu32:case MVE_VQSHL_qrs32:case MVE_VQSHL_qru32:case MVE_VQSHLimms32:case MVE_VQSHLimmu32:case MVE_VQSHRNbhs32:case MVE_VQSHRNbhu32:case MVE_VQSHRNths32:case MVE_VQSHRNthu32:case MVE_VQSHRUNs32bh:case MVE_VQSHRUNs32th:case MVE_VQSUB_qr_s32:case MVE_VQSUB_qr_u32:case MVE_VQSUBs32:case MVE_VQSUBu32:case MVE_VREV32_16:case MVE_VREV32_8:case MVE_VRHADDs32:case MVE_VRHADDu32:case MVE_VRINTf32A:case MVE_VRINTf32M:case MVE_VRINTf32N:case MVE_VRINTf32P:case MVE_VRINTf32X:case MVE_VRINTf32Z:case MVE_VRMLALDAVHas32:case MVE_VRMLALDAVHau32:case MVE_VRMLALDAVHaxs32:case MVE_VRMLALDAVHs32:case MVE_VRMLALDAVHu32:case MVE_VRMLALDAVHxs32:case MVE_VRMLSLDAVHas32:case MVE_VRMLSLDAVHaxs32:case MVE_VRMLSLDAVHs32:case MVE_VRMLSLDAVHxs32:case MVE_VRMULHs32:case MVE_VRMULHu32:case MVE_VRSHL_by_vecs32:case MVE_VRSHL_by_vecu32:case MVE_VRSHL_qrs32:case MVE_VRSHL_qru32:case MVE_VRSHRNi32bh:case MVE_VRSHRNi32th:case MVE_VRSHR_imms32:case MVE_VRSHR_immu32:case MVE_VSBC:case MVE_VSBCI:case MVE_VSHLC:case MVE_VSHLL_imms16bh:case MVE_VSHLL_imms16th:case MVE_VSHLL_immu16bh:case MVE_VSHLL_immu16th:case MVE_VSHLL_lws16bh:case MVE_VSHLL_lws16th:case MVE_VSHLL_lwu16bh:case MVE_VSHLL_lwu16th:case MVE_VSHL_by_vecs32:case MVE_VSHL_by_vecu32:case MVE_VSHL_immi32:case MVE_VSHL_qrs32:case MVE_VSHL_qru32:case MVE_VSHRNi32bh:case MVE_VSHRNi32th:case MVE_VSHR_imms32:case MVE_VSHR_immu32:case MVE_VSLIimm32:case MVE_VSRIimm32:case MVE_VST20_32:case MVE_VST20_32_wb:case MVE_VST21_32:case MVE_VST21_32_wb:case MVE_VST40_32:case MVE_VST40_32_wb:case MVE_VST41_32:case MVE_VST41_32_wb:case MVE_VST42_32:case MVE_VST42_32_wb:case MVE_VST43_32:case MVE_VST43_32_wb:case MVE_VSTRB32:case MVE_VSTRB32_post:case MVE_VSTRB32_pre:case MVE_VSTRB32_rq:case MVE_VSTRH32:case MVE_VSTRH32_post:case MVE_VSTRH32_pre:case MVE_VSTRH32_rq:case MVE_VSTRH32_rq_u:case MVE_VSTRW32_qi:case MVE_VSTRW32_qi_pre:case MVE_VSTRW32_rq:case MVE_VSTRW32_rq_u:case MVE_VSTRWU32:case MVE_VSTRWU32_post:case MVE_VSTRWU32_pre:case MVE_VSUB_qr_f32:case MVE_VSUB_qr_i32:case MVE_VSUBf32:case MVE_VSUBi32:return 2;case MVE_VCTP64:case MVE_VLDRDU64_qi:case MVE_VLDRDU64_qi_pre:case MVE_VLDRDU64_rq:case MVE_VLDRDU64_rq_u:case MVE_VMULLBs32:case MVE_VMULLBu32:case MVE_VMULLTs32:case MVE_VMULLTu32:case MVE_VMOVimmi64:case MVE_VREV64_16:case MVE_VREV64_32:case MVE_VREV64_8:case MVE_VSTRD64_qi:case MVE_VSTRD64_qi_pre:case MVE_VSTRD64_rq:case MVE_VSTRD64_rq_u:return 3;}};LLVMInitializeARMTargetInfo();LLVMInitializeARMTarget();LLVMInitializeARMTargetMC();auto TT(Triple::normalize("thumbv8.1m.main-none-none-eabi"));std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T) {dbgs() << Error;return;}TargetOptions Options;auto TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(T->createTargetMachine(TT, "generic", "", Options, None, None,CodeGenOpt::Default)));ARMSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()),*static_cast<const ARMBaseTargetMachine *>(TM.get()), false);auto MII = TM->getMCInstrInfo();for (unsigned i = 0; i < ARM::INSTRUCTION_LIST_END; ++i) {uint64_t Flags = MII->get(i).TSFlags;if ((Flags & ARMII::DomainMask) != ARMII::DomainMVE)continue;int Size = (Flags & ARMII::VecSize) >> ARMII::VecSizeShift;ASSERT_EQ(MVEVecSize(i), Size)<< MII->getName(i)<< ": mismatched expectation for MVE vec size\n";}}
#include "ARMInstrInfo.h"#include "ARMSubtarget.h"#include "ARMTargetMachine.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;namespace {/// The \p InputIRSnippet is only needed for things that can't be expressed in/// the \p InputMIRSnippet (global variables etc)/// TODO: Some of this might be useful for other architectures as well - extract/// the platform-independent parts somewhere they can be reused.void runChecks(LLVMTargetMachine *TM, const ARMBaseInstrInfo *II,const StringRef InputIRSnippet, const StringRef InputMIRSnippet,unsigned Expected,std::function<void(const ARMBaseInstrInfo &, MachineFunction &, unsigned &)>Checks) {LLVMContext Context;auto MIRString = "--- |\n"" declare void @sizes()\n" +InputIRSnippet.str() +"...\n""---\n""name: sizes\n""constants:\n"" - id: 0\n"" value: i32 12345678\n"" alignment: 4\n""jumpTable:\n"" kind: inline\n"" entries:\n"" - id: 0\n"" blocks: [ '%bb.0' ]\n""body: |\n"" bb.0:\n" +InputMIRSnippet.str();std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRString);std::unique_ptr<MIRParser> MParser =createMIRParser(std::move(MBuffer), Context);ASSERT_TRUE(MParser);std::unique_ptr<Module> M = MParser->parseIRModule();ASSERT_TRUE(M);M->setTargetTriple(TM->getTargetTriple().getTriple());M->setDataLayout(TM->createDataLayout());MachineModuleInfo MMI(TM);bool Res = MParser->parseMachineFunctions(*M, MMI);ASSERT_FALSE(Res);auto F = M->getFunction("sizes");ASSERT_TRUE(F != nullptr);auto &MF = MMI.getOrCreateMachineFunction(*F);Checks(*II, MF, Expected);}} // anonymous namespaceTEST(InstSizes, PseudoInst) {LLVMInitializeARMTargetInfo();LLVMInitializeARMTarget();LLVMInitializeARMTargetMC();auto TT(Triple::normalize("thumbv8.1m.main-none-none-eabi"));std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T) {dbgs() << Error;return;}TargetOptions Options;auto TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine(TT, "generic", "", Options, None, None, CodeGenOpt::Default)));ARMSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()),*static_cast<const ARMBaseTargetMachine *>(TM.get()), false);const ARMBaseInstrInfo *II = ST.getInstrInfo();auto cmpInstSize = [](const ARMBaseInstrInfo &II, MachineFunction &MF,unsigned &Expected) {auto I = MF.begin()->begin();EXPECT_EQ(Expected, II.getInstSizeInBytes(*I));};runChecks(TM.get(), II, ""," $r0 = MOVi16_ga_pcrel"" target-flags(arm-lo16, arm-nonlazy) @sizes, 0\n",4u, cmpInstSize);runChecks(TM.get(), II, ""," $r0 = MOVTi16_ga_pcrel $r0,"" target-flags(arm-hi16, arm-nonlazy) @sizes, 0\n",4u, cmpInstSize);runChecks(TM.get(), II, ""," $r0 = t2MOVi16_ga_pcrel"" target-flags(arm-lo16, arm-nonlazy) @sizes, 0\n",4u, cmpInstSize);runChecks(TM.get(), II, ""," $r0 = t2MOVTi16_ga_pcrel $r0,"" target-flags(arm-hi16, arm-nonlazy) @sizes, 0\n",4u, cmpInstSize);runChecks(TM.get(), II, "", " $r0 = MOVi32imm 2\n", 8u, cmpInstSize);runChecks(TM.get(), II, "", " $r0 = t2MOVi32imm 2\n", 8u, cmpInstSize);runChecks(TM.get(), II, ""," SpeculationBarrierISBDSBEndBB\n"" tBX_RET 14, $noreg, implicit $r0\n",8u, cmpInstSize);runChecks(TM.get(), II, ""," t2SpeculationBarrierISBDSBEndBB\n"" tBX_RET 14, $noreg, implicit $r0\n",8u, cmpInstSize);runChecks(TM.get(), II, ""," SpeculationBarrierSBEndBB\n"" tBX_RET 14, $noreg, implicit $r0\n",4u, cmpInstSize);runChecks(TM.get(), II, ""," t2SpeculationBarrierSBEndBB\n"" tBX_RET 14, $noreg, implicit $r0\n",4u, cmpInstSize);runChecks(TM.get(), II, ""," Int_eh_sjlj_longjmp $r0, $r1, implicit-def $r7,"" implicit-def $lr, implicit-def $sp\n",16u, cmpInstSize);runChecks(TM.get(), II, ""," tInt_eh_sjlj_longjmp $r0, $r1, implicit-def $r7,"" implicit-def $lr, implicit-def $sp\n",10u, cmpInstSize);runChecks(TM.get(), II, ""," tInt_WIN_eh_sjlj_longjmp $r0, $r1, implicit-def $r11,"" implicit-def $lr, implicit-def $sp\n",12u, cmpInstSize);runChecks(TM.get(), II, ""," Int_eh_sjlj_setjmp $r0, $r1, implicit-def $r0,"" implicit-def $r1, implicit-def $r2, implicit-def $r3,"" implicit-def $r4, implicit-def $r5, implicit-def $r6,"" implicit-def $r7, implicit-def $r8, implicit-def $r9,"" implicit-def $r10, implicit-def $r11, implicit-def $r12,"" implicit-def $lr, implicit-def $cpsr, implicit-def $q0,"" implicit-def $q1, implicit-def $q2, implicit-def $q3,"" implicit-def $q4, implicit-def $q5, implicit-def $q6,"" implicit-def $q7, implicit-def $q8, implicit-def $q9,"" implicit-def $q10, implicit-def $q11, implicit-def $q12,"" implicit-def $q13, implicit-def $q14, implicit-def $q15\n"" tBX_RET 14, $noreg, implicit $r0\n",20u, cmpInstSize);runChecks(TM.get(), II, ""," Int_eh_sjlj_setjmp_nofp $r0, $r1, implicit-def $r0,"" implicit-def $r1, implicit-def $r2, implicit-def $r3,"" implicit-def $r4, implicit-def $r5, implicit-def $r6,"" implicit-def $r7, implicit-def $r8, implicit-def $r9,"" implicit-def $r10, implicit-def $r11, implicit-def $r12,"" implicit-def $lr, implicit-def $cpsr\n"" tBX_RET 14, $noreg, implicit $r0\n",20u, cmpInstSize);runChecks(TM.get(), II, ""," tInt_eh_sjlj_setjmp $r0, $r1, implicit-def $r0,"" implicit-def $r1, implicit-def $r2, implicit-def $r3,"" implicit-def $r4, implicit-def $r5, implicit-def $r6,"" implicit-def $r7, implicit-def $r12, implicit-def $cpsr\n"" tBX_RET 14, $noreg, implicit $r0\n",12u, cmpInstSize);runChecks(TM.get(), II, ""," t2Int_eh_sjlj_setjmp $r0, $r1, implicit-def $r0,"" implicit-def $r1, implicit-def $r2, implicit-def $r3,"" implicit-def $r4, implicit-def $r5, implicit-def $r6,"" implicit-def $r7, implicit-def $r8, implicit-def $r9,"" implicit-def $r10, implicit-def $r11, implicit-def $r12,"" implicit-def $lr, implicit-def $cpsr, implicit-def $q0,"" implicit-def $q1, implicit-def $q2, implicit-def $q3,"" implicit-def $q8, implicit-def $q9, implicit-def $q10,"" implicit-def $q11, implicit-def $q12, implicit-def $q13,"" implicit-def $q14, implicit-def $q15\n"" tBX_RET 14, $noreg, implicit $r0\n",12u, cmpInstSize);runChecks(TM.get(), II, ""," t2Int_eh_sjlj_setjmp_nofp $r0, $r1, implicit-def $r0,"" implicit-def $r1, implicit-def $r2, implicit-def $r3,"" implicit-def $r4, implicit-def $r5, implicit-def $r6,"" implicit-def $r7, implicit-def $r8, implicit-def $r9,"" implicit-def $r10, implicit-def $r11, implicit-def $r12,"" implicit-def $lr, implicit-def $cpsr\n"" tBX_RET 14, $noreg, implicit $r0\n",12u, cmpInstSize);runChecks(TM.get(), II, "", " CONSTPOOL_ENTRY 3, %const.0, 8\n", 8u,cmpInstSize);runChecks(TM.get(), II, "", " JUMPTABLE_ADDRS 0, %jump-table.0, 123\n", 123u,cmpInstSize);runChecks(TM.get(), II, "", " JUMPTABLE_INSTS 0, %jump-table.0, 456\n", 456u,cmpInstSize);runChecks(TM.get(), II, "", " JUMPTABLE_TBB 0, %jump-table.0, 789\n", 789u,cmpInstSize);runChecks(TM.get(), II, "", " JUMPTABLE_TBH 0, %jump-table.0, 188\n", 188u,cmpInstSize);runChecks(TM.get(), II, "", " $r0 = SPACE 40, undef $r0\n", 40u,cmpInstSize);runChecks(TM.get(), II, "", " INLINEASM &\"movs r0, #42\", 1\n", 6u,cmpInstSize);runChecks(TM.get(), II," define void @foo() {\n"" entry:\n"" ret void\n"" }\n"," INLINEASM_BR &\"b ${0:l}\", 1, 13, blockaddress(@foo, ""%ir-block.entry)\n",6u, cmpInstSize);}
include_directories(${LLVM_MAIN_SRC_DIR}/lib/Target/ARM${LLVM_BINARY_DIR}/lib/Target/ARM)set(LLVM_LINK_COMPONENTSARMCodeGenARMDescARMInfoCodeGenCoreGlobalISelMCMIRParserSelectionDAGSupportTarget)add_llvm_target_unittest(ARMTestsMachineInstrTest.cppInstSizes.cpp)set_property(TARGET ARMTests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
//===- llvm/unittests/Target/AMDGPU/ExecMayBeModifiedBeforeAnyUse.cpp -----===////// 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////===----------------------------------------------------------------------===//#include "AMDGPUTargetMachine.h"#include "GCNSubtarget.h"#include "MCTargetDesc/AMDGPUMCTargetDesc.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"#include <thread>using namespace llvm;// implementation is in the llvm/unittests/Target/AMDGPU/DwarfRegMappings.cppstd::unique_ptr<const GCNTargetMachine>createTargetMachine(std::string TStr, StringRef CPU, StringRef FS);TEST(AMDGPUExecMayBeModifiedBeforeAnyUse, TheTest) {auto TM = createTargetMachine("amdgcn-amd-", "gfx906", "");if (!TM)return;GCNSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()), *TM);LLVMContext Ctx;Module Mod("Module", Ctx);Mod.setDataLayout(TM->createDataLayout());auto *Type = FunctionType::get(Type::getVoidTy(Ctx), false);auto *F = Function::Create(Type, GlobalValue::ExternalLinkage, "Test", &Mod);MachineModuleInfo MMI(TM.get());auto MF = std::make_unique<MachineFunction>(*F, *TM, ST, 42, MMI);auto *BB = MF->CreateMachineBasicBlock();MF->push_back(BB);auto E = BB->end();DebugLoc DL;const auto &TII = *ST.getInstrInfo();auto &MRI = MF->getRegInfo();// create machine IRRegister R = MRI.createVirtualRegister(&AMDGPU::SReg_32RegClass);MachineInstr *DefMI =BuildMI(*BB, E, DL, TII.get(AMDGPU::S_MOV_B32), R).addImm(42).getInstr();auto First =BuildMI(*BB, E, DL, TII.get(AMDGPU::S_NOP)).addReg(R, RegState::Implicit);BuildMI(*BB, E, DL, TII.get(AMDGPU::S_NOP)).addReg(R, RegState::Implicit);// this violates the continuous sequence of R's uses for the first S_NOPFirst.addReg(R, RegState::Implicit);#ifdef DEBUG_THIS_TESTMF->dump();MRI.dumpUses(R);#endif// make sure execMayBeModifiedBeforeAnyUse doesn't crashASSERT_FALSE(execMayBeModifiedBeforeAnyUse(MRI, R, *DefMI));}
//===- llvm/unittests/Target/AMDGPU/DwarfRegMappings.cpp ------------------===////// 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////===----------------------------------------------------------------------===//#include "AMDGPUSubtarget.h"#include "AMDGPUTargetMachine.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"#include <thread>using namespace llvm;std::once_flag flag;void InitializeAMDGPUTarget() {std::call_once(flag, []() {LLVMInitializeAMDGPUTargetInfo();LLVMInitializeAMDGPUTarget();LLVMInitializeAMDGPUTargetMC();});}std::unique_ptr<const GCNTargetMachine>createTargetMachine(std::string TStr, StringRef CPU, StringRef FS) {InitializeAMDGPUTarget();std::string Error;const Target *T = TargetRegistry::lookupTarget(TStr, Error);if (!T)return nullptr;TargetOptions Options;return std::unique_ptr<GCNTargetMachine>(static_cast<GCNTargetMachine *>(T->createTargetMachine(TStr, CPU, FS, Options, None, None)));}TEST(AMDGPUDwarfRegMappingTests, TestWave64DwarfRegMapping) {for (auto Triple :{"amdgcn-amd-", "amdgcn-amd-amdhsa", "amdgcn-amd-amdpal"}) {auto TM = createTargetMachine(Triple, "gfx1010", "+wavefrontsize64");if (TM) {GCNSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()), *TM);auto MRI = ST.getRegisterInfo();if (MRI) {// Wave64 Dwarf register mapping test numbers// PC_64 => 16, EXEC_MASK_64 => 17, S0 => 32, S63 => 95,// S64 => 1088, S105 => 1129, V0 => 2560, V255 => 2815,// A0 => 3072, A255 => 3327for (int llvmReg :{16, 17, 32, 95, 1088, 1129, 2560, 2815, 3072, 3327}) {MCRegister PCReg(*MRI->getLLVMRegNum(llvmReg, false));EXPECT_EQ(llvmReg, MRI->getDwarfRegNum(PCReg, false));}}}}}TEST(AMDGPUDwarfRegMappingTests, TestWave32DwarfRegMapping) {for (auto Triple :{"amdgcn-amd-", "amdgcn-amd-amdhsa", "amdgcn-amd-amdpal"}) {auto TM = createTargetMachine(Triple, "gfx1010", "+wavefrontsize32");if (TM) {GCNSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()), *TM);auto MRI = ST.getRegisterInfo();if (MRI) {// Wave32 Dwarf register mapping test numbers// PC_64 => 16, EXEC_MASK_32 => 1, S0 => 32, S63 => 95,// S64 => 1088, S105 => 1129, V0 => 1536, V255 => 1791,// A0 => 2048, A255 => 2303for (int llvmReg :{16, 1, 32, 95, 1088, 1129, 1536, 1791, 2048, 2303}) {MCRegister PCReg(*MRI->getLLVMRegNum(llvmReg, false));EXPECT_EQ(llvmReg, MRI->getDwarfRegNum(PCReg, false));}}}}}
include_directories(${PROJECT_SOURCE_DIR}/lib/Target/AMDGPU${PROJECT_BINARY_DIR}/lib/Target/AMDGPU)set(LLVM_LINK_COMPONENTSAMDGPUCodeGenAMDGPUDescAMDGPUInfoCodeGenCoreMCSupport)add_llvm_target_unittest(AMDGPUTestsDwarfRegMappings.cppExecMayBeModifiedBeforeAnyUse.cpp)set_property(TARGET AMDGPUTests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
#include "AArch64Subtarget.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;namespace {std::unique_ptr<LLVMTargetMachine> createTargetMachine() {auto TT(Triple::normalize("aarch64--"));std::string CPU("generic");std::string FS("+sme");LLVMInitializeAArch64TargetInfo();LLVMInitializeAArch64Target();LLVMInitializeAArch64TargetMC();std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(TheTarget->createTargetMachine(TT, CPU, FS, TargetOptions(), None, None, CodeGenOpt::Default)));}std::unique_ptr<AArch64InstrInfo> createInstrInfo(TargetMachine *TM) {AArch64Subtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()), *TM,/* isLittle */ false);return std::make_unique<AArch64InstrInfo>(ST);}TEST(MatrixRegisterAliasing, Aliasing) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();ASSERT_TRUE(TM);std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());const AArch64RegisterInfo &TRI = II->getRegisterInfo();// za overlaps with za.bASSERT_TRUE(TRI.regsOverlap(AArch64::ZA, AArch64::ZAB0));// za0.b overlaps with all tilesASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAQ0));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAQ15));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAD0));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAD7));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAS0));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAS3));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAH0));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAB0, AArch64::ZAH1));// za0.h aliases with za0.q, za2.q, ..ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ0));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ2));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ4));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ6));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ8));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ10));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ12));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ14));// za1.h aliases with za1.q, za3.q, ...ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ1));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ3));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ5));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ7));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ9));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ11));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ13));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ15));// za1.h doesn't alias with za0.q, za2.q, ..ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ0));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ2));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ4));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ6));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ8));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ10));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ12));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH1, AArch64::ZAQ14));// za0.h doesn't alias with za1.q, za3.q, ..ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ1));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ3));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ5));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ7));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ9));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ11));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ13));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAH0, AArch64::ZAQ15));// za0.s aliases with za0.q, za4.q, za8.q, za12.qASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ0));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ4));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ8));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ12));// za1.s aliases with za1.q, za5.q, za9.q, za13.qASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ1));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ5));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ9));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ13));// za0.s doesn't alias with za1.q, za5.q, za9.q, za13.qASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ1));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ5));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ9));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS0, AArch64::ZAQ13));// za1.s doesn't alias with za0.q, za4.q, za8.q, za12.qASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ0));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ4));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ8));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAS1, AArch64::ZAQ12));// za0.d aliases za0.q and za8.qASSERT_TRUE(TRI.regsOverlap(AArch64::ZAD0, AArch64::ZAQ0));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAD0, AArch64::ZAQ8));// za1.d aliases za1.q and za9.qASSERT_TRUE(TRI.regsOverlap(AArch64::ZAD1, AArch64::ZAQ1));ASSERT_TRUE(TRI.regsOverlap(AArch64::ZAD1, AArch64::ZAQ9));// za0.d doesn't alias with za1.q and za9.qASSERT_FALSE(TRI.regsOverlap(AArch64::ZAD0, AArch64::ZAQ1));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAD0, AArch64::ZAQ9));// za1.d doesn't alias with za0.q and za8.qASSERT_FALSE(TRI.regsOverlap(AArch64::ZAD1, AArch64::ZAQ0));ASSERT_FALSE(TRI.regsOverlap(AArch64::ZAD1, AArch64::ZAQ8));}} // end anonymous namespace
#include "AArch64Subtarget.h"#include "AArch64TargetMachine.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;namespace {std::unique_ptr<LLVMTargetMachine> createTargetMachine() {auto TT(Triple::normalize("aarch64--"));std::string CPU("generic");std::string FS("+pauth,+mops,+mte");LLVMInitializeAArch64TargetInfo();LLVMInitializeAArch64Target();LLVMInitializeAArch64TargetMC();std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(TheTarget->createTargetMachine(TT, CPU, FS, TargetOptions(), None, None,CodeGenOpt::Default)));}std::unique_ptr<AArch64InstrInfo> createInstrInfo(TargetMachine *TM) {AArch64Subtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),std::string(TM->getTargetCPU()),std::string(TM->getTargetFeatureString()), *TM,/* isLittle */ false);return std::make_unique<AArch64InstrInfo>(ST);}/// The \p InputIRSnippet is only needed for things that can't be expressed in/// the \p InputMIRSnippet (global variables etc)/// TODO: Some of this might be useful for other architectures as well - extract/// the platform-independent parts somewhere they can be reused.void runChecks(LLVMTargetMachine *TM, AArch64InstrInfo *II, const StringRef InputIRSnippet,const StringRef InputMIRSnippet,std::function<void(AArch64InstrInfo &, MachineFunction &)> Checks) {LLVMContext Context;auto MIRString ="--- |\n"" declare void @sizes()\n"+ InputIRSnippet.str() +"...\n""---\n""name: sizes\n""jumpTable:\n"" kind: block-address\n"" entries:\n"" - id: 0\n"" blocks: [ '%bb.0' ]\n""body: |\n"" bb.0:\n"+ InputMIRSnippet.str();std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRString);std::unique_ptr<MIRParser> MParser =createMIRParser(std::move(MBuffer), Context);ASSERT_TRUE(MParser);std::unique_ptr<Module> M = MParser->parseIRModule();ASSERT_TRUE(M);M->setTargetTriple(TM->getTargetTriple().getTriple());M->setDataLayout(TM->createDataLayout());MachineModuleInfo MMI(TM);bool Res = MParser->parseMachineFunctions(*M, MMI);ASSERT_FALSE(Res);auto F = M->getFunction("sizes");ASSERT_TRUE(F != nullptr);auto &MF = MMI.getOrCreateMachineFunction(*F);Checks(*II, MF);}} // anonymous namespaceTEST(InstSizes, Authenticated) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();ASSERT_TRUE(TM);std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());auto isAuthInst = [](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(4u, II.getInstSizeInBytes(*I));EXPECT_TRUE(I->getDesc().isAuthenticated());};runChecks(TM.get(), II.get(), ""," \n"" BLRAA $x10, $x9\n",isAuthInst);runChecks(TM.get(), II.get(), ""," \n"" RETAB implicit $lr, implicit $sp, implicit killed $x0\n",isAuthInst);runChecks(TM.get(), II.get(), ""," \n"" frame-destroy AUTIASP implicit-def $lr, implicit killed $lr, implicit $sp\n",isAuthInst);runChecks(TM.get(), II.get(), ""," \n"" frame-destroy AUTIBSP implicit-def $lr, implicit killed $lr, implicit $sp\n",isAuthInst);}TEST(InstSizes, STACKMAP) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();ASSERT_TRUE(TM);std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), "", " STACKMAP 0, 16\n"" STACKMAP 1, 32\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(16u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(32u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, PATCHPOINT) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," PATCHPOINT 0, 16, 0, 0, 0, csr_aarch64_aapcs\n"" PATCHPOINT 1, 32, 0, 0, 0, csr_aarch64_aapcs\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(16u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(32u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, STATEPOINT) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," STATEPOINT 0, 0, 0, @sizes, 2, 0, 2, 0, 2, 0, 2, 1, 1, 8,"" $sp, 24, 2, 0, 2, 1, 0, 0\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(4u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, SPACE) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," $xzr = SPACE 1024, undef $xzr\n"" dead $xzr = SPACE 4096, $xzr\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(1024u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(4096u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, TLSDESC_CALLSEQ) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get()," @ThreadLocalGlobal = external thread_local global i32, align 8\n"," TLSDESC_CALLSEQ target-flags(aarch64-tls) @ThreadLocalGlobal\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(16u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, StoreSwiftAsyncContext) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," StoreSwiftAsyncContext $x0, $x1, 12, implicit-def $x16, ""implicit-def $x17\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(20u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, SpeculationBarrierISBDSBEndBB) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," SpeculationBarrierISBDSBEndBB\n"" BR $x8\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(8u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, SpeculationBarrierSBEndBB) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," SpeculationBarrierSBEndBB\n"" BR $x8\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(4u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, JumpTable) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," $x10, $x11 = JumpTableDest32 $x9, $x8, %jump-table.0\n"" $x10, $x11 = JumpTableDest16 $x9, $x8, %jump-table.0\n"" $x10, $x11 = JumpTableDest8 $x9, $x8, %jump-table.0\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(12u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(12u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(12u, II.getInstSizeInBytes(*I));});}TEST(InstSizes, MOPSMemoryPseudos) {std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();std::unique_ptr<AArch64InstrInfo> II = createInstrInfo(TM.get());runChecks(TM.get(), II.get(), ""," $x0, $x1, $x2 = MOPSMemoryMovePseudo $x0, $x1, $x2, ""implicit-def $nzcv\n"" $x0, $x1 = MOPSMemorySetPseudo $x0, $x1, $x2, ""implicit-def $nzcv\n"" $x0, $x1, $x8 = MOPSMemoryCopyPseudo $x0, $x1, $x8, ""implicit-def $nzcv\n"" $x0, $x1 = MOPSMemorySetTaggingPseudo $x0, $x1, $x2, ""implicit-def $nzcv\n",[](AArch64InstrInfo &II, MachineFunction &MF) {auto I = MF.begin()->begin();EXPECT_EQ(12u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(12u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(12u, II.getInstSizeInBytes(*I));++I;EXPECT_EQ(12u, II.getInstSizeInBytes(*I));});}
//===- TestStackOffset.cpp - StackOffset unit tests------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/TypeSize.h"#include "AArch64InstrInfo.h"#include "gtest/gtest.h"using namespace llvm;TEST(StackOffset, decomposeStackOffsetForFrameOffsets) {StackOffset A = StackOffset::getFixed(8);StackOffset B = StackOffset::getFixed(4);StackOffset C = StackOffset::getScalable(16);// If all offsets can be materialized with only ADDVL,// make sure PLSized is 0.int64_t ByteSized, VLSized, PLSized;AArch64InstrInfo::decomposeStackOffsetForFrameOffsets(A + B + C, ByteSized, PLSized,VLSized);EXPECT_EQ(12, ByteSized);EXPECT_EQ(1, VLSized);EXPECT_EQ(0, PLSized);// If we need an ADDPL to materialize the offset, and the number of scalable// bytes fits the ADDPL immediate, fold the scalable bytes to fit in PLSized.StackOffset D = StackOffset::getScalable(2);AArch64InstrInfo::decomposeStackOffsetForFrameOffsets(C + D, ByteSized, PLSized, VLSized);EXPECT_EQ(0, ByteSized);EXPECT_EQ(0, VLSized);EXPECT_EQ(9, PLSized);StackOffset E = StackOffset::getScalable(64);StackOffset F = StackOffset::getScalable(2);AArch64InstrInfo::decomposeStackOffsetForFrameOffsets(E + F, ByteSized, PLSized, VLSized);EXPECT_EQ(0, ByteSized);EXPECT_EQ(0, VLSized);EXPECT_EQ(33, PLSized);// If the offset requires an ADDPL instruction to materialize, and would// require more than two instructions, decompose it into both// ADDVL (n x 16 bytes) and ADDPL (n x 2 bytes) instructions.StackOffset G = StackOffset::getScalable(128);StackOffset H = StackOffset::getScalable(2);AArch64InstrInfo::decomposeStackOffsetForFrameOffsets(G + H, ByteSized, PLSized, VLSized);EXPECT_EQ(0, ByteSized);EXPECT_EQ(8, VLSized);EXPECT_EQ(1, PLSized);}
include_directories(${CMAKE_SOURCE_DIR}/lib/Target/AArch64${CMAKE_BINARY_DIR}/lib/Target/AArch64)set(LLVM_LINK_COMPONENTSAArch64CodeGenAArch64DescAArch64InfoCodeGenCoreGlobalISelMCMIRParserSelectionDAGSupportTarget)add_llvm_target_unittest(AArch64TestsInstSizes.cppDecomposeStackOffsetTest.cppMatrixRegisterAliasing.cpp)set_property(TARGET AArch64Tests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
//===- unittest/TableGen/ParserEntryPointTest.cpp - Parser tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/SourceMgr.h"#include "llvm/TableGen/Parser.h"#include "llvm/TableGen/Record.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;TEST(Parser, SanityTest) {// Simple TableGen source file with a single record.const char *SimpleTdSource = R"td(def Foo {string strField = "value";})td";SourceMgr SrcMgr;SrcMgr.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(SimpleTdSource, "test_buffer"), SMLoc());RecordKeeper Records;bool ProcessResult = TableGenParseFile(SrcMgr, Records);EXPECT_FALSE(ProcessResult);Record *Foo = Records.getDef("Foo");Optional<StringRef> Field = Foo->getValueAsOptionalString("strField");EXPECT_TRUE(Field.has_value());EXPECT_EQ(Field.value(), "value");}
//===- llvm/unittest/TableGen/CodeExpanderTest.cpp - Tests ----------------===////// 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////===----------------------------------------------------------------------===//#include "GlobalISel/CodeExpander.h"#include "GlobalISel/CodeExpansions.h"#include "llvm/Support/raw_ostream.h"#include "llvm/TableGen/Error.h"#include "gtest/gtest.h"using namespace llvm;static StringRef bufferize(StringRef Str) {std::unique_ptr<MemoryBuffer> Buffer =MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");StringRef StrBufferRef = Buffer->getBuffer();SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc());return StrBufferRef;}class RAIIDiagnosticChecker {std::string EmittedDiags;raw_string_ostream OS;std::vector<SMDiagnostic> Expected;std::vector<SMDiagnostic> Received;public:RAIIDiagnosticChecker() : OS(EmittedDiags) {SrcMgr.setDiagHandler(handler, this);}~RAIIDiagnosticChecker() {SrcMgr.setDiagHandler(nullptr);EXPECT_EQ(Received.size(), Expected.size());for (unsigned i = 0; i < Received.size() && i < Expected.size(); ++i) {EXPECT_EQ(Received[i].getLoc(), Expected[i].getLoc());EXPECT_EQ(Received[i].getFilename(), Expected[i].getFilename());EXPECT_EQ(Received[i].getKind(), Expected[i].getKind());EXPECT_EQ(Received[i].getLineNo(), Expected[i].getLineNo());EXPECT_EQ(Received[i].getColumnNo(), Expected[i].getColumnNo());EXPECT_EQ(Received[i].getMessage(), Expected[i].getMessage());EXPECT_EQ(Received[i].getLineContents(), Expected[i].getLineContents());EXPECT_EQ(Received[i].getRanges(), Expected[i].getRanges());}if (testing::Test::HasFailure())errs() << "Emitted diagnostic:\n" << OS.str();}void expect(SMDiagnostic D) { Expected.push_back(D); }void diag(const SMDiagnostic &D) {Received.push_back(D);}static void handler(const SMDiagnostic &D, void *Context) {RAIIDiagnosticChecker *Self = static_cast<RAIIDiagnosticChecker *>(Context);Self->diag(D);SrcMgr.setDiagHandler(nullptr);SrcMgr.PrintMessage(Self->OS, D);SrcMgr.setDiagHandler(handler, Context);};};TEST(CodeExpander, NoExpansions) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;RAIIDiagnosticChecker DiagChecker;CodeExpander("No expansions", Expansions, SMLoc(), false).emit(OS);EXPECT_EQ(OS.str(), "No expansions");}// Indentation is applied to all lines except the firstTEST(CodeExpander, Indentation) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;RAIIDiagnosticChecker DiagChecker;CodeExpander("No expansions\nsecond line\nthird line", Expansions, SMLoc(),false, " ").emit(OS);EXPECT_EQ(OS.str(), "No expansions\n second line\n third line");}// \ is an escape character that removes special meanings from the next// character.TEST(CodeExpander, Escape) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;RAIIDiagnosticChecker DiagChecker;CodeExpander("\\\\\\a\\$", Expansions, SMLoc(), false).emit(OS);EXPECT_EQ(OS.str(), "\\a$");}// $foo is not an expansion. It should warn though.TEST(CodeExpander, NotAnExpansion) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;RAIIDiagnosticChecker DiagChecker;StringRef In = bufferize(" $foo");CodeExpander(" $foo", Expansions, SMLoc::getFromPointer(In.data()), false).emit(OS);EXPECT_EQ(OS.str(), " $foo");DiagChecker.expect(SMDiagnostic(SrcMgr, SMLoc::getFromPointer(In.data()), "TestBuffer", 1, 0,SourceMgr::DK_Warning, "Assuming missing escape character: \\$", " $foo", {}));}// \$foo is not an expansion but shouldn't warn as it's using the escape.TEST(CodeExpander, EscapedNotAnExpansion) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;RAIIDiagnosticChecker DiagChecker;CodeExpander("\\$foo", Expansions, SMLoc(), false).emit(OS);EXPECT_EQ(OS.str(), "$foo");}// \${foo is not an expansion but shouldn't warn as it's using the escape.TEST(CodeExpander, EscapedUnterminatedExpansion) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;RAIIDiagnosticChecker DiagChecker;CodeExpander("\\${foo", Expansions, SMLoc(), false).emit(OS);EXPECT_EQ(OS.str(), "${foo");}// \${foo is not an expansion but shouldn't warn as it's using the escape.TEST(CodeExpander, EscapedExpansion) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;RAIIDiagnosticChecker DiagChecker;CodeExpander("\\${foo}", Expansions, SMLoc(), false).emit(OS);EXPECT_EQ(OS.str(), "${foo}");}// ${foo} is an undefined expansion and should error.TEST(CodeExpander, UndefinedExpansion) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;Expansions.declare("bar", "expansion");RAIIDiagnosticChecker DiagChecker;CodeExpander("${foo}${bar}", Expansions, SMLoc(), false).emit(OS);EXPECT_EQ(OS.str(), "expansion");DiagChecker.expect(SMDiagnostic(SrcMgr, SMLoc(), "<unknown>", 0, -1, SourceMgr::DK_Error,"Attempt to expand an undeclared variable 'foo'", "", {}));}// ${bar is an unterminated expansion. Warn and implicitly terminate it.TEST(CodeExpander, UnterminatedExpansion) {std::string Result;raw_string_ostream OS(Result);CodeExpansions Expansions;Expansions.declare("bar", "expansion");RAIIDiagnosticChecker DiagChecker;StringRef In = bufferize(" ${bar");CodeExpander(In, Expansions, SMLoc::getFromPointer(In.data()), false).emit(OS);EXPECT_EQ(OS.str(), " expansion");DiagChecker.expect(SMDiagnostic(SrcMgr, SMLoc::getFromPointer(In.data()),"TestBuffer", 1, 0, SourceMgr::DK_Warning,"Unterminated expansion '${bar'", " ${bar", {}));}
set(LLVM_LINK_COMPONENTSTableGenSupport)set(LLVM_TARGET_DEFINITIONS Automata.td)tablegen(LLVM AutomataTables.inc -gen-searchable-tables)tablegen(LLVM AutomataAutomata.inc -gen-automata)add_public_tablegen_target(AutomataTestTableGen)add_llvm_unittest(TableGenTests DISABLE_LLVM_LINK_LLVM_DYLIBAutomataTest.cppCodeExpanderTest.cppParserEntryPointTest.cpp)include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../utils/TableGen)target_link_libraries(TableGenTests PRIVATE LLVMTableGenGlobalISel LLVMTableGen)
//===- unittest/TableGen/AutomataTest.cpp - DFA tests ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Support/Debug.h"#include "llvm/Support/Automaton.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;using testing::ContainerEq;using testing::UnorderedElementsAre;// Bring in the enums created by SearchableTables.td.#define GET_SymKind_DECL#define GET_BinRequirementKindEnum_DECL#include "AutomataTables.inc"// And bring in the automata from Automata.td.#define GET_SimpleAutomaton_DECL#define GET_TupleAutomaton_DECL#define GET_NfaAutomaton_DECL#define GET_BinPackerAutomaton_DECL#include "AutomataAutomata.inc"TEST(Automata, SimpleAutomatonAcceptsFromInitialState) {Automaton<SymKind> A(makeArrayRef(SimpleAutomatonTransitions));EXPECT_TRUE(A.add(SK_a));A.reset();EXPECT_TRUE(A.add(SK_b));A.reset();EXPECT_TRUE(A.add(SK_c));A.reset();EXPECT_FALSE(A.add(SK_d));}TEST(Automata, SimpleAutomatonAcceptsSequences) {Automaton<SymKind> A(makeArrayRef(SimpleAutomatonTransitions));// Test sequence <a b>A.reset();EXPECT_TRUE(A.add(SK_a));EXPECT_TRUE(A.add(SK_b));// Test sequence <a c> is rejected (c cannot get bit 0b10);A.reset();EXPECT_TRUE(A.add(SK_a));EXPECT_FALSE(A.add(SK_c));// Symmetric test: sequence <c a> is rejected.A.reset();EXPECT_TRUE(A.add(SK_c));EXPECT_FALSE(A.add(SK_a));}TEST(Automata, TupleAutomatonAccepts) {Automaton<TupleAutomatonAction> A(makeArrayRef(TupleAutomatonTransitions));A.reset();EXPECT_TRUE(A.add(TupleAutomatonAction{SK_a, SK_b, "yeet"}));A.reset();EXPECT_FALSE(A.add(TupleAutomatonAction{SK_a, SK_a, "yeet"}));A.reset();EXPECT_FALSE(A.add(TupleAutomatonAction{SK_a, SK_b, "feet"}));A.reset();EXPECT_TRUE(A.add(TupleAutomatonAction{SK_b, SK_b, "foo"}));}TEST(Automata, NfaAutomatonAccepts) {Automaton<SymKind> A(makeArrayRef(NfaAutomatonTransitions));// Test sequences <a a>, <a b>, <b a>, <b b>. All should be accepted.A.reset();EXPECT_TRUE(A.add(SK_a));EXPECT_TRUE(A.add(SK_a));A.reset();EXPECT_TRUE(A.add(SK_a));EXPECT_TRUE(A.add(SK_b));A.reset();EXPECT_TRUE(A.add(SK_b));EXPECT_TRUE(A.add(SK_a));A.reset();EXPECT_TRUE(A.add(SK_b));EXPECT_TRUE(A.add(SK_b));// Expect that <b b b> is not accepted.A.reset();EXPECT_TRUE(A.add(SK_b));EXPECT_TRUE(A.add(SK_b));EXPECT_FALSE(A.add(SK_b));}TEST(Automata, BinPackerAutomatonAccepts) {Automaton<BinPackerAutomatonAction> A(makeArrayRef(BinPackerAutomatonTransitions));// Expect that we can pack two double-bins in 0-4, then no more in 0-4.A.reset();EXPECT_TRUE(A.add(BRK_0_to_4_dbl));EXPECT_TRUE(A.add(BRK_0_to_4_dbl));EXPECT_FALSE(A.add(BRK_0_to_4));// Expect that we can pack two double-bins in 0-4, two more in 0-6 then no// more.A.reset();EXPECT_TRUE(A.add(BRK_0_to_4_dbl));EXPECT_TRUE(A.add(BRK_0_to_4_dbl));EXPECT_TRUE(A.add(BRK_0_to_6));EXPECT_TRUE(A.add(BRK_0_to_6));EXPECT_FALSE(A.add(BRK_0_to_6));// Expect that we can pack BRK_0_to_6 five times to occupy five bins, then// cannot allocate any double-bins.A.reset();for (unsigned I = 0; I < 5; ++I)EXPECT_TRUE(A.add(BRK_0_to_6));EXPECT_FALSE(A.add(BRK_0_to_6_dbl));}// The state we defined in TableGen uses the least significant 6 bits to represent a bin state.#define BINS(a, b, c, d, e, f) \((a << 5) | (b << 4) | (c << 3) | (d << 2) | (e << 1) | (f << 0))TEST(Automata, BinPackerAutomatonExplains) {Automaton<BinPackerAutomatonAction> A(makeArrayRef(BinPackerAutomatonTransitions),makeArrayRef(BinPackerAutomatonTransitionInfo));// Pack two double-bins in 0-4, then a single bin in 0-6.EXPECT_TRUE(A.add(BRK_0_to_4_dbl));EXPECT_TRUE(A.add(BRK_0_to_4_dbl));EXPECT_TRUE(A.add(BRK_0_to_6));EXPECT_THAT(A.getNfaPaths(),UnorderedElementsAre(// Allocate {0,1} first, then 6.ContainerEq(NfaPath{BINS(0, 0, 0, 0, 1, 1), BINS(0, 0, 1, 1, 1, 1),BINS(1, 0, 1, 1, 1, 1)}),// Allocate {0,1} first, then 5.ContainerEq(NfaPath{BINS(0, 0, 0, 0, 1, 1), BINS(0, 0, 1, 1, 1, 1),BINS(0, 1, 1, 1, 1, 1)}),// Allocate {2,3} first, then 6.ContainerEq(NfaPath{BINS(0, 0, 1, 1, 0, 0), BINS(0, 0, 1, 1, 1, 1),BINS(1, 0, 1, 1, 1, 1)}),// Allocate {2,3} first, then 5.ContainerEq(NfaPath{BINS(0, 0, 1, 1, 0, 0), BINS(0, 0, 1, 1, 1, 1),BINS(0, 1, 1, 1, 1, 1)})));}
include "llvm/TableGen/Automaton.td"include "llvm/TableGen/SearchableTable.td"// Define a set of input token symbols.class SymKindTy;def SK_a : SymKindTy;def SK_b : SymKindTy;def SK_c : SymKindTy;def SK_d : SymKindTy;// Emit those as a C++ enum using SearchableTables.def SymKind : GenericEnum {let FilterClass = "SymKindTy";}// Define a transition implementation.class SimpleTransition<bits<2> State, SymKindTy A> : Transition {let NewState{1...0} = State;SymKindTy ActionSym = A;}// Token SK_a sets bit 0b01.def : SimpleTransition<0b01, SK_a>;// Token SK_b sets bits 0b10.def : SimpleTransition<0b10, SK_b>;// Token SK_c sets both bits 0b11.def : SimpleTransition<0b11, SK_c>;def SimpleAutomaton : GenericAutomaton {let TransitionClass = "SimpleTransition";let SymbolFields = ["ActionSym"];// Override the type of ActionSym from SymKindTy to the C++ type SymKind.string TypeOf_ActionSym = "SymKind";}//===----------------------------------------------------------------------===//// TupleActionAutomaton test implementation// Define a transition implementation.class TupleTransition<bits<2> State, SymKindTy s1, SymKindTy s2, string s3> : Transition {let NewState{1...0} = State;SymKindTy S1 = s1;SymKindTy S2 = s2;string S3 = s3;}def : TupleTransition<0b01, SK_a, SK_b, "yeet">;def : TupleTransition<0b10, SK_b, SK_b, "foo">;def : TupleTransition<0b10, SK_c, SK_a, "foo">;def TupleAutomaton : GenericAutomaton {let TransitionClass = "TupleTransition";let SymbolFields = ["S1", "S2", "S3"];string TypeOf_S1 = "SymKind";string TypeOf_S2 = "SymKind";}//===----------------------------------------------------------------------===//// NfaAutomaton test implementationclass NfaTransition<bits<2> State, SymKindTy S> : Transition {let NewState{1...0} = State;SymKindTy A = S;}// Symbols a and b can transition to 0b01 or 0b11 (sets bit 0).def : NfaTransition<0b01, SK_a>;def : NfaTransition<0b01, SK_b>;// Symbols a and b can also transition to 0b10 or 0b11 (sets bit 1).def : NfaTransition<0b10, SK_a>;def : NfaTransition<0b10, SK_b>;def NfaAutomaton : GenericAutomaton {let TransitionClass = "NfaTransition";let SymbolFields = ["A"];string TypeOf_A = "SymKind";}//===----------------------------------------------------------------------===//// BinPacker test implementation//===----------------------------------------------------------------------===//// This test generates an automaton that can pack values into bins subject to// constraints. There are 6 possible bins, and the input tokens are constraint// types. Some input types span two bins.// The symbol type for a bin constraint. We use lists of ints as a tblgen hack// to conditionally generate defs within multiclasses based on record// information. A bin is nonempty (has a dummy one-element value) if enabled.class BinRequirementKind {list<int> Bin0 = [];list<int> Bin1 = [];list<int> Bin2 = [];list<int> Bin3 = [];list<int> Bin4 = [];list<int> Bin5 = [];}// Can use bins {0-3}def BRK_0_to_4 : BinRequirementKind { let Bin0 = [1]; let Bin1 = [1]; let Bin2 = [1]; let Bin3 = [1]; }// Can use bins {0-3} but only evens (0 and 2).def BRK_0_to_4_lo : BinRequirementKind { let Bin0 = [1]; let Bin2 = [1]; }// Can use bins {0-3} but only odds (1 and 3).def BRK_0_to_4_hi : BinRequirementKind { let Bin1 = [1]; let Bin3 = [1]; }// Can use bins {0-3} but only even-odd pairs (0+1 or 1+2).def BRK_0_to_4_dbl : BinRequirementKind { let Bin0 = [1]; let Bin2 = [1]; }def BRK_0_to_6 : BinRequirementKind { let Bin0 = [1]; let Bin1 = [1]; let Bin2 = [1];let Bin3 = [1]; let Bin4 = [1]; let Bin5 = [1]; }def BRK_0_to_6_lo : BinRequirementKind { let Bin0 = [1]; let Bin2 = [1]; let Bin4 = [1]; }def BRK_0_to_6_hi : BinRequirementKind { let Bin1 = [1]; let Bin3 = [1]; let Bin5 = [1]; }def BRK_0_to_6_dbl : BinRequirementKind { let Bin0 = [1]; let Bin2 = [1]; let Bin4 = [1]; }def BRK_2_to_6 : BinRequirementKind { let Bin2 = [1];let Bin3 = [1]; let Bin4 = [1]; let Bin5 = [1]; }def BRK_2_to_6_lo : BinRequirementKind { let Bin2 = [1]; let Bin4 = [1]; }def BRK_2_to_6_hi : BinRequirementKind { let Bin3 = [1]; let Bin5 = [1];}def BRK_2_to_6_dbl : BinRequirementKind { let Bin2 = [1]; let Bin4 = [1]; }def BRK_2_to_4 : BinRequirementKind { let Bin2 = [1]; let Bin3 = [1]; }def BRK_2_to_4_lo : BinRequirementKind { let Bin2 = [1]; }def BRK_2_to_4_hi : BinRequirementKind { let Bin3 = [1]; }def BRK_2_to_4_dbl : BinRequirementKind { let Bin2 = [1]; }def BinRequirementKindEnum : GenericEnum {let FilterClass = "BinRequirementKind";}// The transition class is trivial; it just contains the constraint symbol.class BinTransition : Transition {BinRequirementKind Sym;}// Mixin that occupies a single bin.class Bin0 : BinTransition { let NewState{0} = 1; }class Bin1 : BinTransition { let NewState{1} = 1; }class Bin2 : BinTransition { let NewState{2} = 1;}class Bin3 : BinTransition { let NewState{3} = 1; }class Bin4 : BinTransition { let NewState{4} = 1;}class Bin5 : BinTransition { let NewState{5} = 1; }// Mixin that occupies a pair of bins (even-odd pairs).class Bin01 : BinTransition { let NewState{0,1} = 0b11; }class Bin23 : BinTransition { let NewState{2,3} = 0b11; }class Bin45 : BinTransition { let NewState{4,5} = 0b11; }// Instantiate all possible bin assignments for E.multiclass BinAssignments<BinRequirementKind E> {let Sym = E in {// Note the tablegen hack to conditionally instantiate a def based on E.foreach x = E.Bin0 in { def : Bin0; }foreach x = E.Bin1 in { def : Bin1; }foreach x = E.Bin2 in { def : Bin2; }foreach x = E.Bin3 in { def : Bin3; }foreach x = E.Bin4 in { def : Bin4; }foreach x = E.Bin5 in { def : Bin5; }}}// Instantiate all possible bin assignments for E, which spans even-odd pairs.multiclass DblBinAssignments<BinRequirementKind E> {let Sym = E in {foreach x = E.Bin0 in { def : Bin01; }foreach x = E.Bin2 in { def : Bin23; }foreach x = E.Bin4 in { def : Bin45; }}}defm : BinAssignments<BRK_0_to_4>;defm : DblBinAssignments<BRK_0_to_4_dbl>;defm : BinAssignments<BRK_0_to_4_lo>;defm : BinAssignments<BRK_0_to_4_hi>;defm : BinAssignments<BRK_0_to_6>;defm : DblBinAssignments<BRK_0_to_6_dbl>;defm : BinAssignments<BRK_0_to_6_lo>;defm : BinAssignments<BRK_0_to_6_hi>;defm : BinAssignments<BRK_2_to_6>;defm : DblBinAssignments<BRK_2_to_6_dbl>;defm : BinAssignments<BRK_2_to_6_lo>;defm : BinAssignments<BRK_2_to_6_hi>;defm : BinAssignments<BRK_2_to_4>;defm : DblBinAssignments<BRK_2_to_4_dbl>;defm : BinAssignments<BRK_2_to_4_lo>;defm : BinAssignments<BRK_2_to_4_hi>;def BinPackerAutomaton : GenericAutomaton {let TransitionClass = "BinTransition";let SymbolFields = ["Sym"];string TypeOf_Sym = "BinRequirementKindEnum";}
//===- llvm/unittest/Support/xxhashTest.cpp -------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/xxhash.h"#include "gtest/gtest.h"using namespace llvm;TEST(xxhashTest, Basic) {EXPECT_EQ(0x33bf00a859c4ba3fU, xxHash64("foo"));EXPECT_EQ(0x48a37c90ad27a659U, xxHash64("bar"));EXPECT_EQ(0x69196c1b3af0bff9U,xxHash64("0123456789abcdefghijklmnopqrstuvwxyz"));}
//===- llvm/unittest/Support/raw_ostream_test.cpp - raw_ostream tests -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Format.h"#include "llvm/Support/raw_sha1_ostream.h"#include "gtest/gtest.h"#include <string>using namespace llvm;static std::string toHex(ArrayRef<uint8_t> Input) {static const char *const LUT = "0123456789ABCDEF";size_t Length = Input.size();std::string Output;Output.reserve(2 * Length);for (size_t i = 0; i < Length; ++i) {const unsigned char c = Input[i];Output.push_back(LUT[c >> 4]);Output.push_back(LUT[c & 15]);}return Output;}TEST(raw_sha1_ostreamTest, Basic) {llvm::raw_sha1_ostream Sha1Stream;Sha1Stream << "Hello World!";auto Hash = toHex(Sha1Stream.sha1());ASSERT_EQ("2EF7BDE608CE5404E97D5F042F95F89F1C232871", Hash);}TEST(sha1_hash_test, Basic) {ArrayRef<uint8_t> Input((const uint8_t *)"Hello World!", 12);std::array<uint8_t, 20> Vec = SHA1::hash(Input);std::string Hash = toHex(Vec);ASSERT_EQ("2EF7BDE608CE5404E97D5F042F95F89F1C232871", Hash);}TEST(sha1_hash_test, Update) {SHA1 sha1;std::string Input = "123456789012345678901234567890";ASSERT_EQ(Input.size(), 30UL);// 3 short updates.sha1.update(Input);sha1.update(Input);sha1.update(Input);// Long update that gets into the optimized loop with prefix/suffix.sha1.update(Input + Input + Input + Input);// 18 bytes buffered now.std::string Hash = toHex(sha1.final());ASSERT_EQ("3E4A614101AD84985AB0FE54DC12A6D71551E5AE", Hash);}// Check that getting the intermediate hash in the middle of the stream does// not invalidate the final result.TEST(raw_sha1_ostreamTest, Intermediate) {llvm::raw_sha1_ostream Sha1Stream;Sha1Stream << "Hello";auto Hash = toHex(Sha1Stream.sha1());ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash);Sha1Stream << " World!";Hash = toHex(Sha1Stream.sha1());// Compute the non-split hash separately as a reference.llvm::raw_sha1_ostream NonSplitSha1Stream;NonSplitSha1Stream << "Hello World!";auto NonSplitHash = toHex(NonSplitSha1Stream.sha1());ASSERT_EQ(NonSplitHash, Hash);}TEST(raw_sha1_ostreamTest, Reset) {llvm::raw_sha1_ostream Sha1Stream;Sha1Stream << "Hello";auto Hash = toHex(Sha1Stream.sha1());ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash);Sha1Stream.resetHash();Sha1Stream << " World!";Hash = toHex(Sha1Stream.sha1());ASSERT_EQ("7447F2A5A42185C8CF91E632789C431830B59067", Hash);}
//===- raw_pwrite_stream_test.cpp - raw_pwrite_stream tests ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "llvm/Config/llvm-config.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/FileUtilities.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}namespace {TEST(raw_pwrite_ostreamTest, TestSVector) {SmallVector<char, 0> Buffer;raw_svector_ostream OS(Buffer);OS << "abcd";StringRef Test = "test";OS.pwrite(Test.data(), Test.size(), 0);EXPECT_EQ(Test, OS.str());#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGEXPECT_DEATH(OS.pwrite("12345", 5, 0),"We don't support extending the stream");#endif#endif}#ifdef _WIN32#define setenv(name, var, ignore) _putenv_s(name, var)#endifTEST(raw_pwrite_ostreamTest, TestFD) {SmallString<64> Path;int FD;// If we want to clean up from a death test, we have to remove the file from// the parent process. Have the parent create the file, pass it via// environment variable to the child, let the child crash, and then remove it// in the parent.const char *ParentPath = getenv("RAW_PWRITE_TEST_FILE");if (ParentPath) {Path = ParentPath;ASSERT_NO_ERROR(sys::fs::openFileForRead(Path, FD));} else {ASSERT_NO_ERROR(sys::fs::createTemporaryFile("foo", "bar", FD, Path));setenv("RAW_PWRITE_TEST_FILE", Path.c_str(), true);}FileRemover Cleanup(Path);raw_fd_ostream OS(FD, true);OS << "abcd";StringRef Test = "test";OS.pwrite(Test.data(), Test.size(), 0);OS.pwrite(Test.data(), Test.size(), 0);#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGEXPECT_DEATH(OS.pwrite("12345", 5, 0),"We don't support extending the stream");#endif#endif}#ifdef LLVM_ON_UNIXTEST(raw_pwrite_ostreamTest, TestDevNull) {int FD;sys::fs::openFileForWrite("/dev/null", FD, sys::fs::CD_OpenExisting);raw_fd_ostream OS(FD, true);OS << "abcd";StringRef Test = "test";OS.pwrite(Test.data(), Test.size(), 0);OS.pwrite(Test.data(), Test.size(), 0);}#endif}
//===- llvm/unittest/Support/raw_ostream_test.cpp - raw_ostream tests -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "llvm/Support/Errc.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/FileUtilities.h"#include "llvm/Support/Format.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {template<typename T> std::string printToString(const T &Value) {std::string res;llvm::raw_string_ostream OS(res);OS.SetBuffered();OS << Value;OS.flush();return res;}/// printToString - Print the given value to a stream which only has \arg/// BytesLeftInBuffer bytes left in the buffer. This is useful for testing edge/// cases in the buffer handling logic.template<typename T> std::string printToString(const T &Value,unsigned BytesLeftInBuffer) {// FIXME: This is relying on internal knowledge of how raw_ostream works to// get the buffer position right.SmallString<256> SVec;assert(BytesLeftInBuffer < 256 && "Invalid buffer count!");llvm::raw_svector_ostream OS(SVec);unsigned StartIndex = 256 - BytesLeftInBuffer;for (unsigned i = 0; i != StartIndex; ++i)OS << '?';OS << Value;return std::string(OS.str().substr(StartIndex));}template<typename T> std::string printToStringUnbuffered(const T &Value) {std::string res;llvm::raw_string_ostream OS(res);OS.SetUnbuffered();OS << Value;return res;}struct X {};raw_ostream &operator<<(raw_ostream &OS, const X &) { return OS << 'X'; }TEST(raw_ostreamTest, Types_Buffered) {// CharEXPECT_EQ("c", printToString('c'));// StringEXPECT_EQ("hello", printToString("hello"));EXPECT_EQ("hello", printToString(std::string("hello")));// IntEXPECT_EQ("0", printToString(0));EXPECT_EQ("2425", printToString(2425));EXPECT_EQ("-2425", printToString(-2425));// Long longEXPECT_EQ("0", printToString(0LL));EXPECT_EQ("257257257235709", printToString(257257257235709LL));EXPECT_EQ("-257257257235709", printToString(-257257257235709LL));// DoubleEXPECT_EQ("1.100000e+00", printToString(1.1));// void*EXPECT_EQ("0x0", printToString((void*) nullptr));EXPECT_EQ("0xbeef", printToString((void*) 0xbeefLL));EXPECT_EQ("0xdeadbeef", printToString((void*) 0xdeadbeefLL));// Min and max.EXPECT_EQ("18446744073709551615", printToString(UINT64_MAX));EXPECT_EQ("-9223372036854775808", printToString(INT64_MIN));// X, checking free operator<<().EXPECT_EQ("X", printToString(X{}));}TEST(raw_ostreamTest, Types_Unbuffered) {// CharEXPECT_EQ("c", printToStringUnbuffered('c'));// StringEXPECT_EQ("hello", printToStringUnbuffered("hello"));EXPECT_EQ("hello", printToStringUnbuffered(std::string("hello")));// IntEXPECT_EQ("0", printToStringUnbuffered(0));EXPECT_EQ("2425", printToStringUnbuffered(2425));EXPECT_EQ("-2425", printToStringUnbuffered(-2425));// Long longEXPECT_EQ("0", printToStringUnbuffered(0LL));EXPECT_EQ("257257257235709", printToStringUnbuffered(257257257235709LL));EXPECT_EQ("-257257257235709", printToStringUnbuffered(-257257257235709LL));// DoubleEXPECT_EQ("1.100000e+00", printToStringUnbuffered(1.1));// void*EXPECT_EQ("0x0", printToStringUnbuffered((void*) nullptr));EXPECT_EQ("0xbeef", printToStringUnbuffered((void*) 0xbeefLL));EXPECT_EQ("0xdeadbeef", printToStringUnbuffered((void*) 0xdeadbeefLL));// Min and max.EXPECT_EQ("18446744073709551615", printToStringUnbuffered(UINT64_MAX));EXPECT_EQ("-9223372036854775808", printToStringUnbuffered(INT64_MIN));// X, checking free operator<<().EXPECT_EQ("X", printToString(X{}));}TEST(raw_ostreamTest, BufferEdge) {EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 1));EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 2));EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 3));EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 4));EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 10));}TEST(raw_ostreamTest, TinyBuffer) {std::string Str;raw_string_ostream OS(Str);OS.SetBufferSize(1);OS << "hello";OS << 1;OS << 'w' << 'o' << 'r' << 'l' << 'd';OS.flush();EXPECT_EQ("hello1world", Str);}TEST(raw_ostreamTest, WriteEscaped) {std::string Str;Str = "";raw_string_ostream(Str).write_escaped("hi");EXPECT_EQ("hi", Str);Str = "";raw_string_ostream(Str).write_escaped("\\\t\n\"");EXPECT_EQ("\\\\\\t\\n\\\"", Str);Str = "";raw_string_ostream(Str).write_escaped("\1\10\200");EXPECT_EQ("\\001\\010\\200", Str);}TEST(raw_ostreamTest, Justify) {EXPECT_EQ("xyz ", printToString(left_justify("xyz", 6), 6));EXPECT_EQ("abc", printToString(left_justify("abc", 3), 3));EXPECT_EQ("big", printToString(left_justify("big", 1), 3));EXPECT_EQ(" xyz", printToString(right_justify("xyz", 6), 6));EXPECT_EQ("abc", printToString(right_justify("abc", 3), 3));EXPECT_EQ("big", printToString(right_justify("big", 1), 3));EXPECT_EQ(" on ", printToString(center_justify("on", 9), 9));EXPECT_EQ(" off ", printToString(center_justify("off", 10), 10));EXPECT_EQ("single ", printToString(center_justify("single", 7), 7));EXPECT_EQ("none", printToString(center_justify("none", 1), 4));EXPECT_EQ("none", printToString(center_justify("none", 1), 1));}TEST(raw_ostreamTest, FormatHex) {EXPECT_EQ("0x1234", printToString(format_hex(0x1234, 6), 6));EXPECT_EQ("0x001234", printToString(format_hex(0x1234, 8), 8));EXPECT_EQ("0x00001234", printToString(format_hex(0x1234, 10), 10));EXPECT_EQ("0x1234", printToString(format_hex(0x1234, 4), 6));EXPECT_EQ("0xff", printToString(format_hex(255, 4), 4));EXPECT_EQ("0xFF", printToString(format_hex(255, 4, true), 4));EXPECT_EQ("0x1", printToString(format_hex(1, 3), 3));EXPECT_EQ("0x12", printToString(format_hex(0x12, 3), 4));EXPECT_EQ("0x123", printToString(format_hex(0x123, 3), 5));EXPECT_EQ("FF", printToString(format_hex_no_prefix(0xFF, 2, true), 4));EXPECT_EQ("ABCD", printToString(format_hex_no_prefix(0xABCD, 2, true), 4));EXPECT_EQ("0xffffffffffffffff",printToString(format_hex(UINT64_MAX, 18), 18));EXPECT_EQ("0x8000000000000000",printToString(format_hex((INT64_MIN), 18), 18));}TEST(raw_ostreamTest, FormatDecimal) {EXPECT_EQ(" 0", printToString(format_decimal(0, 4), 4));EXPECT_EQ(" -1", printToString(format_decimal(-1, 4), 4));EXPECT_EQ(" -1", printToString(format_decimal(-1, 6), 6));EXPECT_EQ("1234567890", printToString(format_decimal(1234567890, 10), 10));EXPECT_EQ(" 9223372036854775807",printToString(format_decimal(INT64_MAX, 21), 21));EXPECT_EQ(" -9223372036854775808",printToString(format_decimal(INT64_MIN, 21), 21));}static std::string formatted_bytes_str(ArrayRef<uint8_t> Bytes,llvm::Optional<uint64_t> Offset = None,uint32_t NumPerLine = 16,uint8_t ByteGroupSize = 4) {std::string S;raw_string_ostream Str(S);Str << format_bytes(Bytes, Offset, NumPerLine, ByteGroupSize);Str.flush();return S;}static std::string format_bytes_with_ascii_str(ArrayRef<uint8_t> Bytes,Optional<uint64_t> Offset = None,uint32_t NumPerLine = 16,uint8_t ByteGroupSize = 4) {std::string S;raw_string_ostream Str(S);Str << format_bytes_with_ascii(Bytes, Offset, NumPerLine, ByteGroupSize);Str.flush();return S;}TEST(raw_ostreamTest, FormattedHexBytes) {std::vector<uint8_t> Buf = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i','j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r','s', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0','1', '2', '3', '4', '5', '6', '7', '8', '9'};ArrayRef<uint8_t> B(Buf);// Test invalid input.EXPECT_EQ("", formatted_bytes_str(ArrayRef<uint8_t>()));EXPECT_EQ("", format_bytes_with_ascii_str(ArrayRef<uint8_t>()));//----------------------------------------------------------------------// Test hex byte output with the default 4 byte groups//----------------------------------------------------------------------EXPECT_EQ("61", formatted_bytes_str(B.take_front()));EXPECT_EQ("61626364 65", formatted_bytes_str(B.take_front(5)));// Test that 16 bytes get written to a line correctly.EXPECT_EQ("61626364 65666768 696a6b6c 6d6e6f70",formatted_bytes_str(B.take_front(16)));// Test raw bytes with default 16 bytes per line wrapping.EXPECT_EQ("61626364 65666768 696a6b6c 6d6e6f70\n71",formatted_bytes_str(B.take_front(17)));// Test raw bytes with 1 bytes per line wrapping.EXPECT_EQ("61\n62\n63\n64\n65\n66",formatted_bytes_str(B.take_front(6), None, 1));// Test raw bytes with 7 bytes per line wrapping.EXPECT_EQ("61626364 656667\n68696a6b 6c6d6e\n6f7071",formatted_bytes_str(B.take_front(17), None, 7));// Test raw bytes with 8 bytes per line wrapping.EXPECT_EQ("61626364 65666768\n696a6b6c 6d6e6f70\n71",formatted_bytes_str(B.take_front(17), None, 8));//----------------------------------------------------------------------// Test hex byte output with the 1 byte groups//----------------------------------------------------------------------EXPECT_EQ("61 62 63 64 65",formatted_bytes_str(B.take_front(5), None, 16, 1));// Test that 16 bytes get written to a line correctly.EXPECT_EQ("61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70",formatted_bytes_str(B.take_front(16), None, 16, 1));// Test raw bytes with default 16 bytes per line wrapping.EXPECT_EQ("61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70\n71",formatted_bytes_str(B.take_front(17), None, 16, 1));// Test raw bytes with 7 bytes per line wrapping.EXPECT_EQ("61 62 63 64 65 66 67\n68 69 6a 6b 6c 6d 6e\n6f 70 71",formatted_bytes_str(B.take_front(17), None, 7, 1));// Test raw bytes with 8 bytes per line wrapping.EXPECT_EQ("61 62 63 64 65 66 67 68\n69 6a 6b 6c 6d 6e 6f 70\n71",formatted_bytes_str(B.take_front(17), None, 8, 1));//----------------------------------------------------------------------// Test hex byte output with the 2 byte groups//----------------------------------------------------------------------EXPECT_EQ("6162 6364 65", formatted_bytes_str(B.take_front(5), None, 16, 2));// Test that 16 bytes get written to a line correctly.EXPECT_EQ("6162 6364 6566 6768 696a 6b6c 6d6e 6f70",formatted_bytes_str(B.take_front(16), None, 16, 2));// Test raw bytes with default 16 bytes per line wrapping.EXPECT_EQ("6162 6364 6566 6768 696a 6b6c 6d6e 6f70\n71",formatted_bytes_str(B.take_front(17), None, 16, 2));// Test raw bytes with 7 bytes per line wrapping.EXPECT_EQ("6162 6364 6566 67\n6869 6a6b 6c6d 6e\n6f70 71",formatted_bytes_str(B.take_front(17), None, 7, 2));// Test raw bytes with 8 bytes per line wrapping.EXPECT_EQ("6162 6364 6566 6768\n696a 6b6c 6d6e 6f70\n71",formatted_bytes_str(B.take_front(17), None, 8, 2));//----------------------------------------------------------------------// Test hex bytes with offset with the default 4 byte groups.//----------------------------------------------------------------------EXPECT_EQ("0000: 61", formatted_bytes_str(B.take_front(), 0x0));EXPECT_EQ("1000: 61", formatted_bytes_str(B.take_front(), 0x1000));EXPECT_EQ("1000: 61\n1001: 62",formatted_bytes_str(B.take_front(2), 0x1000, 1));//----------------------------------------------------------------------// Test hex bytes with ASCII with the default 4 byte groups.//----------------------------------------------------------------------EXPECT_EQ("61626364 65666768 696a6b6c 6d6e6f70 |abcdefghijklmnop|",format_bytes_with_ascii_str(B.take_front(16)));EXPECT_EQ("61626364 65666768 |abcdefgh|\n""696a6b6c 6d6e6f70 |ijklmnop|",format_bytes_with_ascii_str(B.take_front(16), None, 8));EXPECT_EQ("61626364 65666768 |abcdefgh|\n696a6b6c |ijkl|",format_bytes_with_ascii_str(B.take_front(12), None, 8));std::vector<uint8_t> Unprintable = {'a', '\x1e', 'b', '\x1f'};// Make sure the ASCII is still lined up correctly when fewer bytes than 16// bytes per line are available. The ASCII should still be aligned as if 16// bytes of hex might be displayed.EXPECT_EQ("611e621f |a.b.|",format_bytes_with_ascii_str(Unprintable));//----------------------------------------------------------------------// Test hex bytes with ASCII with offsets with the default 4 byte groups.//----------------------------------------------------------------------EXPECT_EQ("0000: 61626364 65666768 ""696a6b6c 6d6e6f70 |abcdefghijklmnop|",format_bytes_with_ascii_str(B.take_front(16), 0));EXPECT_EQ("0000: 61626364 65666768 |abcdefgh|\n""0008: 696a6b6c 6d6e6f70 |ijklmnop|",format_bytes_with_ascii_str(B.take_front(16), 0, 8));EXPECT_EQ("0000: 61626364 656667 |abcdefg|\n""0007: 68696a6b 6c |hijkl|",format_bytes_with_ascii_str(B.take_front(12), 0, 7));//----------------------------------------------------------------------// Test hex bytes with ASCII with offsets with the default 2 byte groups.//----------------------------------------------------------------------EXPECT_EQ("0000: 6162 6364 6566 6768 ""696a 6b6c 6d6e 6f70 |abcdefghijklmnop|",format_bytes_with_ascii_str(B.take_front(16), 0, 16, 2));EXPECT_EQ("0000: 6162 6364 6566 6768 |abcdefgh|\n""0008: 696a 6b6c 6d6e 6f70 |ijklmnop|",format_bytes_with_ascii_str(B.take_front(16), 0, 8, 2));EXPECT_EQ("0000: 6162 6364 6566 67 |abcdefg|\n""0007: 6869 6a6b 6c |hijkl|",format_bytes_with_ascii_str(B.take_front(12), 0, 7, 2));//----------------------------------------------------------------------// Test hex bytes with ASCII with offsets with the default 1 byte groups.//----------------------------------------------------------------------EXPECT_EQ("0000: 61 62 63 64 65 66 67 68 ""69 6a 6b 6c 6d 6e 6f 70 |abcdefghijklmnop|",format_bytes_with_ascii_str(B.take_front(16), 0, 16, 1));EXPECT_EQ("0000: 61 62 63 64 65 66 67 68 |abcdefgh|\n""0008: 69 6a 6b 6c 6d 6e 6f 70 |ijklmnop|",format_bytes_with_ascii_str(B.take_front(16), 0, 8, 1));EXPECT_EQ("0000: 61 62 63 64 65 66 67 |abcdefg|\n""0007: 68 69 6a 6b 6c |hijkl|",format_bytes_with_ascii_str(B.take_front(12), 0, 7, 1));}#ifdef LLVM_ON_UNIXTEST(raw_ostreamTest, Colors) {{std::string S;raw_string_ostream Sos(S);Sos.enable_colors(false);Sos.changeColor(raw_ostream::YELLOW);EXPECT_EQ("", Sos.str());}{std::string S;raw_string_ostream Sos(S);Sos.enable_colors(true);Sos.changeColor(raw_ostream::YELLOW);EXPECT_EQ("\x1B[0;33m", Sos.str());}}#endifTEST(raw_fd_ostreamTest, multiple_raw_fd_ostream_to_stdout) {std::error_code EC;{ raw_fd_ostream("-", EC, sys::fs::OpenFlags::OF_None); }{ raw_fd_ostream("-", EC, sys::fs::OpenFlags::OF_None); }}TEST(raw_ostreamTest, flush_tied_to_stream_on_write) {std::string TiedToBuffer;raw_string_ostream TiedTo(TiedToBuffer);TiedTo.SetBuffered();TiedTo << "a";std::string Buffer;raw_string_ostream TiedStream(Buffer);TiedStream.tie(&TiedTo);// Sanity check that the stream hasn't already been flushed.EXPECT_EQ("", TiedToBuffer);// Empty string doesn't cause a flush of TiedTo.TiedStream << "";EXPECT_EQ("", TiedToBuffer);// Non-empty strings trigger flush of TiedTo.TiedStream << "abc";EXPECT_EQ("a", TiedToBuffer);// Single char write flushes TiedTo.TiedTo << "c";TiedStream << 'd';EXPECT_EQ("ac", TiedToBuffer);// Write to buffered stream without flush does not flush TiedTo.TiedStream.SetBuffered();TiedStream.SetBufferSize(2);TiedTo << "e";TiedStream << "f";EXPECT_EQ("ac", TiedToBuffer);// Explicit flush of buffered stream flushes TiedTo.TiedStream.flush();EXPECT_EQ("ace", TiedToBuffer);// Explicit flush of buffered stream with empty buffer does not flush TiedTo.TiedTo << "g";TiedStream.flush();EXPECT_EQ("ace", TiedToBuffer);// Write of data to empty buffer that is greater than buffer size flushes// TiedTo.TiedStream << "hijklm";EXPECT_EQ("aceg", TiedToBuffer);// Write of data that overflows buffer size also flushes TiedTo.TiedStream.flush();TiedStream << "n";TiedTo << "o";TiedStream << "pq";EXPECT_EQ("acego", TiedToBuffer);// Streams can be tied to each other safely.TiedStream.flush();Buffer = "";TiedTo.tie(&TiedStream);TiedTo.SetBufferSize(2);TiedStream << "r";TiedTo << "s";EXPECT_EQ("", Buffer);EXPECT_EQ("acego", TiedToBuffer);TiedTo << "tuv";EXPECT_EQ("r", Buffer);TiedStream << "wxy";EXPECT_EQ("acegostuv", TiedToBuffer);// The x remains in the buffer, since it was written after the flush of// TiedTo.EXPECT_EQ("rwx", Buffer);TiedTo.tie(nullptr);// Calling tie with nullptr unties stream.TiedStream.SetUnbuffered();TiedStream.tie(nullptr);TiedTo << "y";TiedStream << "0";EXPECT_EQ("acegostuv", TiedToBuffer);TiedTo.flush();TiedStream.flush();}TEST(raw_ostreamTest, reserve_stream) {std::string Str;raw_string_ostream OS(Str);OS << "11111111111111111111";uint64_t CurrentPos = OS.tell();OS.reserveExtraSpace(1000);EXPECT_GE(Str.capacity(), CurrentPos + 1000);OS << "hello";OS << 1;OS << 'w' << 'o' << 'r' << 'l' << 'd';OS.flush();EXPECT_EQ("11111111111111111111hello1world", Str);}static void checkFileData(StringRef FileName, StringRef GoldenData) {ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =MemoryBuffer::getFileOrSTDIN(FileName);EXPECT_FALSE(BufOrErr.getError());EXPECT_EQ((*BufOrErr)->getBufferSize(), GoldenData.size());EXPECT_EQ(memcmp((*BufOrErr)->getBufferStart(), GoldenData.data(),GoldenData.size()),0);}TEST(raw_ostreamTest, writeToOutputFile) {SmallString<64> Path;int FD;ASSERT_FALSE(sys::fs::createTemporaryFile("foo", "bar", FD, Path));FileRemover Cleanup(Path);ASSERT_THAT_ERROR(writeToOutput(Path,[](raw_ostream &Out) -> Error {Out << "HelloWorld";return Error::success();}),Succeeded());checkFileData(Path, "HelloWorld");}TEST(raw_ostreamTest, writeToNonexistingPath) {StringRef FileName = "/_bad/_path";std::string ErrorMessage = toString(createFileError(FileName, make_error_code(errc::no_such_file_or_directory)));EXPECT_THAT_ERROR(writeToOutput(FileName,[](raw_ostream &Out) -> Error {Out << "HelloWorld";return Error::success();}),FailedWithMessage(ErrorMessage));}TEST(raw_ostreamTest, writeToDevNull) {bool DevNullIsUsed = false;EXPECT_THAT_ERROR(writeToOutput("/dev/null",[&](raw_ostream &Out) -> Error {DevNullIsUsed =testing::internal::CheckedDowncastToActualType<raw_null_ostream, raw_ostream>(&Out);return Error::success();}),Succeeded());EXPECT_TRUE(DevNullIsUsed);}TEST(raw_ostreamTest, writeToStdOut) {outs().flush();testing::internal::CaptureStdout();EXPECT_THAT_ERROR(writeToOutput("-",[](raw_ostream &Out) -> Error {Out << "HelloWorld";return Error::success();}),Succeeded());outs().flush();std::string CapturedStdOut = testing::internal::GetCapturedStdout();EXPECT_EQ(CapturedStdOut, "HelloWorld");}} // namespace
//===- llvm/unittest/Support/raw_fd_stream_test.cpp - raw_fd_stream tests -===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "llvm/Config/llvm-config.h"#include "llvm/Support/Casting.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/FileUtilities.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(raw_fd_streamTest, ReadAfterWrite) {SmallString<64> Path;int FD;ASSERT_FALSE(sys::fs::createTemporaryFile("foo", "bar", FD, Path));FileRemover Cleanup(Path);std::error_code EC;raw_fd_stream OS(Path, EC);EXPECT_TRUE(!EC);char Bytes[8];OS.write("01234567", 8);OS.seek(3);EXPECT_EQ(OS.read(Bytes, 2), 2);EXPECT_EQ(Bytes[0], '3');EXPECT_EQ(Bytes[1], '4');OS.seek(4);OS.write("xyz", 3);OS.seek(0);EXPECT_EQ(OS.read(Bytes, 8), 8);EXPECT_EQ(Bytes[0], '0');EXPECT_EQ(Bytes[1], '1');EXPECT_EQ(Bytes[2], '2');EXPECT_EQ(Bytes[3], '3');EXPECT_EQ(Bytes[4], 'x');EXPECT_EQ(Bytes[5], 'y');EXPECT_EQ(Bytes[6], 'z');EXPECT_EQ(Bytes[7], '7');}TEST(raw_fd_streamTest, DynCast) {{std::error_code EC;raw_fd_stream OS("-", EC);EXPECT_TRUE(dyn_cast<raw_fd_stream>(&OS));}{std::error_code EC;raw_fd_ostream OS("-", EC);EXPECT_FALSE(dyn_cast<raw_fd_stream>(&OS));}}} // namespace
//===- llvm/unittest/Support/formatted_raw_ostream_test.cpp ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "llvm/Support/FormattedStream.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(formatted_raw_ostreamTest, Test_Tell) {// Check offset when underlying stream has buffer contents.SmallString<128> A;raw_svector_ostream B(A);formatted_raw_ostream C(B);char tmp[100] = "";for (unsigned i = 0; i != 3; ++i) {C.write(tmp, 100);EXPECT_EQ(100*(i+1), (unsigned) C.tell());}}TEST(formatted_raw_ostreamTest, Test_LineColumn) {// Test tracking of line and column numbers in a stream.SmallString<128> A;raw_svector_ostream B(A);formatted_raw_ostream C(B);EXPECT_EQ(0U, C.getLine());EXPECT_EQ(0U, C.getColumn());C << "a";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(1U, C.getColumn());C << "bcdef";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(6U, C.getColumn());// '\n' increments line number, sets column to zero.C << "\n";EXPECT_EQ(1U, C.getLine());EXPECT_EQ(0U, C.getColumn());// '\r sets column to zero without changing line numberC << "foo\r";EXPECT_EQ(1U, C.getLine());EXPECT_EQ(0U, C.getColumn());// '\t' advances column to the next multiple of 8.// FIXME: If the column number is already a multiple of 8 this will do// nothing, is this behaviour correct?C << "1\t";EXPECT_EQ(8U, C.getColumn());C << "\t";EXPECT_EQ(8U, C.getColumn());C << "1234567\t";EXPECT_EQ(16U, C.getColumn());EXPECT_EQ(1U, C.getLine());}TEST(formatted_raw_ostreamTest, Test_Flush) {// Flushing the buffer causes the characters in the buffer to be scanned// before the buffer is emptied, so line and column numbers will still be// tracked properly.SmallString<128> A;raw_svector_ostream B(A);B.SetBufferSize(32);formatted_raw_ostream C(B);C << "\nabc";EXPECT_EQ(4U, C.GetNumBytesInBuffer());C.flush();EXPECT_EQ(1U, C.getLine());EXPECT_EQ(3U, C.getColumn());EXPECT_EQ(0U, C.GetNumBytesInBuffer());}TEST(formatted_raw_ostreamTest, Test_UTF8) {SmallString<128> A;raw_svector_ostream B(A);B.SetBufferSize(32);formatted_raw_ostream C(B);// U+00A0 Non-breaking space: encoded as two bytes, but only one column wide.C << u8"\u00a0";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(1U, C.getColumn());EXPECT_EQ(2U, C.GetNumBytesInBuffer());// U+2468 CIRCLED DIGIT NINE: encoded as three bytes, but only one column// wide.C << u8"\u2468";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(2U, C.getColumn());EXPECT_EQ(5U, C.GetNumBytesInBuffer());// U+00010000 LINEAR B SYLLABLE B008 A: encoded as four bytes, but only one// column wide.C << u8"\U00010000";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(3U, C.getColumn());EXPECT_EQ(9U, C.GetNumBytesInBuffer());// U+55B5, CJK character, encodes as three bytes, takes up two columns.C << u8"\u55b5";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(5U, C.getColumn());EXPECT_EQ(12U, C.GetNumBytesInBuffer());// U+200B, zero-width space, encoded as three bytes but has no effect on the// column or line number.C << u8"\u200b";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(5U, C.getColumn());EXPECT_EQ(15U, C.GetNumBytesInBuffer());}TEST(formatted_raw_ostreamTest, Test_UTF8Buffered) {SmallString<128> A;raw_svector_ostream B(A);B.SetBufferSize(4);formatted_raw_ostream C(B);// U+2468 encodes as three bytes, so will cause the buffer to be flushed after// the first byte (4 byte buffer, 3 bytes already written). We need to save// the first part of the UTF-8 encoding until after the buffer is cleared and// the remaining two bytes are written, at which point we can check the// display width. In this case the display width is 1, so we end at column 4,// with 6 bytes written into total, 2 of which are in the buffer.C << u8"123\u2468";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(4U, C.getColumn());EXPECT_EQ(2U, C.GetNumBytesInBuffer());C.flush();EXPECT_EQ(6U, A.size());// Same as above, but with a CJK character which displays as two columns.C << u8"123\u55b5";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(9U, C.getColumn());EXPECT_EQ(2U, C.GetNumBytesInBuffer());C.flush();EXPECT_EQ(12U, A.size());}TEST(formatted_raw_ostreamTest, Test_UTF8TinyBuffer) {SmallString<128> A;raw_svector_ostream B(A);B.SetBufferSize(1);formatted_raw_ostream C(B);// The stream has a one-byte buffer, so it gets flushed multiple times while// printing a single Unicode character.C << u8"\u2468";EXPECT_EQ(0U, C.getLine());EXPECT_EQ(1U, C.getColumn());EXPECT_EQ(0U, C.GetNumBytesInBuffer());C.flush();EXPECT_EQ(3U, A.size());}}
//===- buffer_ostream_test.cpp - buffer_ostream tests ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {/// Naive version of raw_svector_ostream that is buffered (by default) and/// doesn't support pwrite.class NaiveSmallVectorStream : public raw_ostream {public:uint64_t current_pos() const override { return Vector.size(); }void write_impl(const char *Ptr, size_t Size) override {Vector.append(Ptr, Ptr + Size);}explicit NaiveSmallVectorStream(SmallVectorImpl<char> &Vector): Vector(Vector) {}~NaiveSmallVectorStream() override { flush(); }SmallVectorImpl<char> &Vector;};TEST(buffer_ostreamTest, Reference) {SmallString<128> Dest;{NaiveSmallVectorStream DestOS(Dest);buffer_ostream BufferOS(DestOS);// Writing and flushing should have no effect on Dest.BufferOS << "abcd";static_cast<raw_ostream &>(BufferOS).flush();EXPECT_EQ("", Dest);DestOS.flush();EXPECT_EQ("", Dest);}// Write should land when constructor is called.EXPECT_EQ("abcd", Dest);}TEST(buffer_ostreamTest, Owned) {SmallString<128> Dest;{auto DestOS = std::make_unique<NaiveSmallVectorStream>(Dest);// Confirm that NaiveSmallVectorStream is buffered by default.EXPECT_NE(0u, DestOS->GetBufferSize());// Confirm that passing ownership to buffer_unique_ostream sets it to// unbuffered. Also steal a reference to DestOS.NaiveSmallVectorStream &DestOSRef = *DestOS;buffer_unique_ostream BufferOS(std::move(DestOS));EXPECT_EQ(0u, DestOSRef.GetBufferSize());// Writing and flushing should have no effect on Dest.BufferOS << "abcd";static_cast<raw_ostream &>(BufferOS).flush();EXPECT_EQ("", Dest);DestOSRef.flush();EXPECT_EQ("", Dest);}// Write should land when constructor is called.EXPECT_EQ("abcd", Dest);}} // end namespace
//===- unittest/Support/YAMLParserTest ------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/YAMLParser.h"#include "llvm/ADT/Twine.h"#include "llvm/Support/Casting.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"namespace llvm {static void SuppressDiagnosticsOutput(const SMDiagnostic &, void *) {// Prevent SourceMgr from writing errors to stderr// to reduce noise in unit test runs.}// Assumes Ctx is an SMDiagnostic where Diag can be stored.static void CollectDiagnosticsOutput(const SMDiagnostic &Diag, void *Ctx) {SMDiagnostic* DiagOut = static_cast<SMDiagnostic*>(Ctx);*DiagOut = Diag;}// Checks that the given input gives a parse error. Makes sure that an error// text is available and the parse fails.static void ExpectParseError(StringRef Message, StringRef Input) {SourceMgr SM;yaml::Stream Stream(Input, SM);SM.setDiagHandler(SuppressDiagnosticsOutput);EXPECT_FALSE(Stream.validate()) << Message << ": " << Input;EXPECT_TRUE(Stream.failed()) << Message << ": " << Input;}// Checks that the given input can be parsed without error.static void ExpectParseSuccess(StringRef Message, StringRef Input) {SourceMgr SM;yaml::Stream Stream(Input, SM);EXPECT_TRUE(Stream.validate()) << Message << ": " << Input;}TEST(YAMLParser, ParsesEmptyArray) {ExpectParseSuccess("Empty array", "[]");}TEST(YAMLParser, FailsIfNotClosingArray) {ExpectParseError("Not closing array", "[");ExpectParseError("Not closing array", " [ ");ExpectParseError("Not closing array", " [x");}TEST(YAMLParser, ParsesEmptyArrayWithWhitespace) {ExpectParseSuccess("Array with spaces", " [ ] ");ExpectParseSuccess("All whitespaces", "\t\r\n[\t\n \t\r ]\t\r \n\n");}TEST(YAMLParser, ParsesEmptyObject) {ExpectParseSuccess("Empty object", "[{}]");}TEST(YAMLParser, ParsesObject) {ExpectParseSuccess("Object with an entry", "[{\"a\":\"/b\"}]");}TEST(YAMLParser, ParsesMultipleKeyValuePairsInObject) {ExpectParseSuccess("Multiple key, value pairs","[{\"a\":\"/b\",\"c\":\"d\",\"e\":\"f\"}]");}TEST(YAMLParser, FailsIfNotClosingObject) {ExpectParseError("Missing close on empty", "[{]");ExpectParseError("Missing close after pair", "[{\"a\":\"b\"]");}TEST(YAMLParser, FailsIfMissingColon) {ExpectParseError("Missing colon between key and value", "[{\"a\"\"/b\"}]");ExpectParseError("Missing colon between key and value", "[{\"a\" \"b\"}]");}TEST(YAMLParser, FailsOnMissingQuote) {ExpectParseError("Missing open quote", "[{a\":\"b\"}]");ExpectParseError("Missing closing quote", "[{\"a\":\"b}]");}TEST(YAMLParser, ParsesEscapedQuotes) {ExpectParseSuccess("Parses escaped string in key and value","[{\"a\":\"\\\"b\\\" \\\" \\\"\"}]");}TEST(YAMLParser, ParsesEmptyString) {ExpectParseSuccess("Parses empty string in value", "[{\"a\":\"\"}]");}TEST(YAMLParser, ParsesMultipleObjects) {ExpectParseSuccess("Multiple objects in array","["" { \"a\" : \"b\" },"" { \"a\" : \"b\" },"" { \"a\" : \"b\" }""]");}TEST(YAMLParser, FailsOnMissingComma) {ExpectParseError("Missing comma","["" { \"a\" : \"b\" }"" { \"a\" : \"b\" }""]");}TEST(YAMLParser, ParsesSpacesInBetweenTokens) {ExpectParseSuccess("Various whitespace between tokens"," \t \n\n \r [ \t \n\n \r"" \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :"" \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r,\t \n\n \r"" \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :"" \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r]\t \n\n \r");}TEST(YAMLParser, ParsesArrayOfArrays) {ExpectParseSuccess("Array of arrays", "[[]]");}TEST(YAMLParser, ParsesBlockLiteralScalars) {ExpectParseSuccess("Block literal scalar", "test: |\n Hello\n World\n");ExpectParseSuccess("Block literal scalar EOF", "test: |\n Hello\n World");ExpectParseSuccess("Empty block literal scalar header EOF", "test: | ");ExpectParseSuccess("Empty block literal scalar", "test: |\ntest2: 20");ExpectParseSuccess("Empty block literal scalar 2", "- | \n \n\n \n- 42");ExpectParseSuccess("Block literal scalar in sequence","- |\n Testing\n Out\n\n- 22");ExpectParseSuccess("Block literal scalar in document","--- |\n Document\n...");ExpectParseSuccess("Empty non indented lines still count","- |\n First line\n \n\n Another line\n\n- 2");ExpectParseSuccess("Comment in block literal scalar header","test: | # Comment \n No Comment\ntest 2: | # Void");ExpectParseSuccess("Chomping indicators in block literal scalar header","test: |- \n Hello\n\ntest 2: |+ \n\n World\n\n\n");ExpectParseSuccess("Indent indicators in block literal scalar header","test: |1 \n \n Hello \n World\n");ExpectParseSuccess("Chomping and indent indicators in block literals","test: |-1\n Hello\ntest 2: |9+\n World");ExpectParseSuccess("Trailing comments in block literals","test: |\n Content\n # Trailing\n #Comment\ntest 2: 3");ExpectParseError("Invalid block scalar header", "test: | failure");ExpectParseError("Invalid line indentation", "test: |\n First line\n Error");ExpectParseError("Long leading space line", "test: |\n \n Test\n");}TEST(YAMLParser, NullTerminatedBlockScalars) {SourceMgr SM;yaml::Stream Stream("test: |\n Hello\n World\n", SM);yaml::Document &Doc = *Stream.begin();yaml::MappingNode *Map = cast<yaml::MappingNode>(Doc.getRoot());StringRef Value =cast<yaml::BlockScalarNode>(Map->begin()->getValue())->getValue();EXPECT_EQ(Value, "Hello\nWorld\n");EXPECT_EQ(Value.data()[Value.size()], '\0');}TEST(YAMLParser, HandlesEndOfFileGracefully) {ExpectParseError("In string starting with EOF", "[\"");ExpectParseError("In string hitting EOF", "[\" ");ExpectParseError("In string escaping EOF", "[\" \\");ExpectParseError("In array starting with EOF", "[");ExpectParseError("In array element starting with EOF", "[[], ");ExpectParseError("In array hitting EOF", "[[] ");ExpectParseError("In array hitting EOF", "[[]");ExpectParseError("In object hitting EOF", "{\"\"");}TEST(YAMLParser, HandlesNullValuesInKeyValueNodesGracefully) {ExpectParseError("KeyValueNode with null key", "? \"\n:");ExpectParseError("KeyValueNode with null value", "test: '");}// Checks that the given string can be parsed into an identical string inside// of an array.static void ExpectCanParseString(StringRef String) {std::string StringInArray = (llvm::Twine("[\"") + String + "\"]").str();SourceMgr SM;yaml::Stream Stream(StringInArray, SM);yaml::SequenceNode *ParsedSequence= dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot());StringRef ParsedString= dyn_cast<yaml::ScalarNode>(static_cast<yaml::Node*>(ParsedSequence->begin()))->getRawValue();ParsedString = ParsedString.substr(1, ParsedString.size() - 2);EXPECT_EQ(String, ParsedString.str());}// Checks that parsing the given string inside an array fails.static void ExpectCannotParseString(StringRef String) {std::string StringInArray = (llvm::Twine("[\"") + String + "\"]").str();ExpectParseError((Twine("When parsing string \"") + String + "\"").str(),StringInArray);}TEST(YAMLParser, ParsesStrings) {ExpectCanParseString("");ExpectCannotParseString("\\");ExpectCannotParseString("\"");ExpectCanParseString(" ");ExpectCanParseString("\\ ");ExpectCanParseString("\\\"");ExpectCannotParseString("\"\\");ExpectCannotParseString(" \\");ExpectCanParseString("\\\\");ExpectCannotParseString("\\\\\\");ExpectCanParseString("\\\\\\\\");ExpectCanParseString("\\\" ");ExpectCannotParseString("\\\\\" ");ExpectCanParseString("\\\\\\\" ");ExpectCanParseString(" \\\\ \\\" \\\\\\\" ");}TEST(YAMLParser, WorksWithIteratorAlgorithms) {SourceMgr SM;yaml::Stream Stream("[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"]", SM);yaml::SequenceNode *Array= dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot());EXPECT_EQ(6, std::distance(Array->begin(), Array->end()));}TEST(YAMLParser, DefaultDiagnosticFilename) {SourceMgr SM;SMDiagnostic GeneratedDiag;SM.setDiagHandler(CollectDiagnosticsOutput, &GeneratedDiag);// When we construct a YAML stream over an unnamed string,// the filename is hard-coded as "YAML".yaml::Stream UnnamedStream("[]", SM);UnnamedStream.printError(UnnamedStream.begin()->getRoot(), "Hello, World!");EXPECT_EQ("YAML", GeneratedDiag.getFilename());}TEST(YAMLParser, DiagnosticFilenameFromBufferID) {SourceMgr SM;SMDiagnostic GeneratedDiag;SM.setDiagHandler(CollectDiagnosticsOutput, &GeneratedDiag);// When we construct a YAML stream over a named buffer,// we get its ID as filename in diagnostics.std::unique_ptr<MemoryBuffer> Buffer =MemoryBuffer::getMemBuffer("[]", "buffername.yaml");yaml::Stream Stream(Buffer->getMemBufferRef(), SM);Stream.printError(Stream.begin()->getRoot(), "Hello, World!");EXPECT_EQ("buffername.yaml", GeneratedDiag.getFilename());}TEST(YAMLParser, SameNodeIteratorOperatorNotEquals) {SourceMgr SM;yaml::Stream Stream("[\"1\", \"2\"]", SM);yaml::SequenceNode *Node = dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot());auto Begin = Node->begin();auto End = Node->end();EXPECT_NE(Begin, End);EXPECT_EQ(Begin, Begin);EXPECT_EQ(End, End);}TEST(YAMLParser, SameNodeIteratorOperatorEquals) {SourceMgr SM;yaml::Stream Stream("[\"1\", \"2\"]", SM);yaml::SequenceNode *Node = dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot());auto Begin = Node->begin();auto End = Node->end();EXPECT_NE(Begin, End);EXPECT_EQ(Begin, Begin);EXPECT_EQ(End, End);}TEST(YAMLParser, DifferentNodesIteratorOperatorNotEquals) {SourceMgr SM;yaml::Stream Stream("[\"1\", \"2\"]", SM);yaml::Stream AnotherStream("[\"1\", \"2\"]", SM);yaml::SequenceNode *Node = dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot());yaml::SequenceNode *AnotherNode = dyn_cast<yaml::SequenceNode>(AnotherStream.begin()->getRoot());auto Begin = Node->begin();auto End = Node->end();auto AnotherBegin = AnotherNode->begin();auto AnotherEnd = AnotherNode->end();EXPECT_NE(Begin, AnotherBegin);EXPECT_NE(Begin, AnotherEnd);EXPECT_EQ(End, AnotherEnd);}TEST(YAMLParser, DifferentNodesIteratorOperatorEquals) {SourceMgr SM;yaml::Stream Stream("[\"1\", \"2\"]", SM);yaml::Stream AnotherStream("[\"1\", \"2\"]", SM);yaml::SequenceNode *Node = dyn_cast<yaml::SequenceNode>(Stream.begin()->getRoot());yaml::SequenceNode *AnotherNode = dyn_cast<yaml::SequenceNode>(AnotherStream.begin()->getRoot());auto Begin = Node->begin();auto End = Node->end();auto AnotherBegin = AnotherNode->begin();auto AnotherEnd = AnotherNode->end();EXPECT_NE(Begin, AnotherBegin);EXPECT_NE(Begin, AnotherEnd);EXPECT_EQ(End, AnotherEnd);}TEST(YAMLParser, FlowSequenceTokensOutsideFlowSequence) {auto FlowSequenceStrs = {",", "]", "}"};SourceMgr SM;for (auto &Str : FlowSequenceStrs) {yaml::Stream Stream(Str, SM);yaml::Document &Doc = *Stream.begin();EXPECT_FALSE(Doc.skip());}}static void expectCanParseBool(StringRef S, bool Expected) {llvm::Optional<bool> Parsed = yaml::parseBool(S);EXPECT_TRUE(Parsed.has_value());EXPECT_EQ(*Parsed, Expected);}static void expectCannotParseBool(StringRef S) {EXPECT_FALSE(yaml::parseBool(S).has_value());}TEST(YAMLParser, ParsesBools) {// Test true values.expectCanParseBool("ON", true);expectCanParseBool("On", true);expectCanParseBool("on", true);expectCanParseBool("TRUE", true);expectCanParseBool("True", true);expectCanParseBool("true", true);expectCanParseBool("Y", true);expectCanParseBool("y", true);expectCanParseBool("YES", true);expectCanParseBool("Yes", true);expectCanParseBool("yes", true);expectCannotParseBool("1");// Test false values.expectCanParseBool("FALSE", false);expectCanParseBool("False", false);expectCanParseBool("false", false);expectCanParseBool("N", false);expectCanParseBool("n", false);expectCanParseBool("NO", false);expectCanParseBool("No", false);expectCanParseBool("no", false);expectCanParseBool("OFF", false);expectCanParseBool("Off", false);expectCanParseBool("off", false);expectCannotParseBool("0");}} // end namespace llvm
//===- unittest/Support/YAMLIOTest.cpp ------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/BitmaskEnum.h"#include "llvm/ADT/StringMap.h"#include "llvm/ADT/StringRef.h"#include "llvm/ADT/StringSwitch.h"#include "llvm/ADT/Twine.h"#include "llvm/Support/Casting.h"#include "llvm/Support/Endian.h"#include "llvm/Support/Format.h"#include "llvm/Support/YAMLTraits.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using llvm::yaml::Hex16;using llvm::yaml::Hex32;using llvm::yaml::Hex64;using llvm::yaml::Hex8;using llvm::yaml::Input;using llvm::yaml::IO;using llvm::yaml::isNumeric;using llvm::yaml::MappingNormalization;using llvm::yaml::MappingTraits;using llvm::yaml::Output;using llvm::yaml::ScalarTraits;using ::testing::StartsWith;static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) {}//===----------------------------------------------------------------------===//// Test MappingTraits//===----------------------------------------------------------------------===//struct FooBar {int foo;int bar;};typedef std::vector<FooBar> FooBarSequence;LLVM_YAML_IS_SEQUENCE_VECTOR(FooBar)struct FooBarContainer {FooBarSequence fbs;};namespace llvm {namespace yaml {template <>struct MappingTraits<FooBar> {static void mapping(IO &io, FooBar& fb) {io.mapRequired("foo", fb.foo);io.mapRequired("bar", fb.bar);}};template <> struct MappingTraits<FooBarContainer> {static void mapping(IO &io, FooBarContainer &fb) {io.mapRequired("fbs", fb.fbs);}};}}//// Test the reading of a yaml mapping//TEST(YAMLIO, TestMapRead) {FooBar doc;{Input yin("---\nfoo: 3\nbar: 5\n...\n");yin >> doc;EXPECT_FALSE(yin.error());EXPECT_EQ(doc.foo, 3);EXPECT_EQ(doc.bar, 5);}{Input yin("{foo: 3, bar: 5}");yin >> doc;EXPECT_FALSE(yin.error());EXPECT_EQ(doc.foo, 3);EXPECT_EQ(doc.bar, 5);}}TEST(YAMLIO, TestMalformedMapRead) {FooBar doc;Input yin("{foo: 3; bar: 5}", nullptr, suppressErrorMessages);yin >> doc;EXPECT_TRUE(!!yin.error());}//// Test the reading of a yaml sequence of mappings//TEST(YAMLIO, TestSequenceMapRead) {FooBarSequence seq;Input yin("---\n - foo: 3\n bar: 5\n - foo: 7\n bar: 9\n...\n");yin >> seq;EXPECT_FALSE(yin.error());EXPECT_EQ(seq.size(), 2UL);FooBar& map1 = seq[0];FooBar& map2 = seq[1];EXPECT_EQ(map1.foo, 3);EXPECT_EQ(map1.bar, 5);EXPECT_EQ(map2.foo, 7);EXPECT_EQ(map2.bar, 9);}//// Test the reading of a map containing a yaml sequence of mappings//TEST(YAMLIO, TestContainerSequenceMapRead) {{FooBarContainer cont;Input yin2("---\nfbs:\n - foo: 3\n bar: 5\n - foo: 7\n bar: 9\n...\n");yin2 >> cont;EXPECT_FALSE(yin2.error());EXPECT_EQ(cont.fbs.size(), 2UL);EXPECT_EQ(cont.fbs[0].foo, 3);EXPECT_EQ(cont.fbs[0].bar, 5);EXPECT_EQ(cont.fbs[1].foo, 7);EXPECT_EQ(cont.fbs[1].bar, 9);}{FooBarContainer cont;Input yin("---\nfbs:\n...\n");yin >> cont;// Okay: Empty node represents an empty array.EXPECT_FALSE(yin.error());EXPECT_EQ(cont.fbs.size(), 0UL);}{FooBarContainer cont;Input yin("---\nfbs: !!null null\n...\n");yin >> cont;// Okay: null represents an empty array.EXPECT_FALSE(yin.error());EXPECT_EQ(cont.fbs.size(), 0UL);}{FooBarContainer cont;Input yin("---\nfbs: ~\n...\n");yin >> cont;// Okay: null represents an empty array.EXPECT_FALSE(yin.error());EXPECT_EQ(cont.fbs.size(), 0UL);}{FooBarContainer cont;Input yin("---\nfbs: null\n...\n");yin >> cont;// Okay: null represents an empty array.EXPECT_FALSE(yin.error());EXPECT_EQ(cont.fbs.size(), 0UL);}}//// Test the reading of a map containing a malformed yaml sequence//TEST(YAMLIO, TestMalformedContainerSequenceMapRead) {{FooBarContainer cont;Input yin("---\nfbs:\n foo: 3\n bar: 5\n...\n", nullptr,suppressErrorMessages);yin >> cont;// Error: fbs is not a sequence.EXPECT_TRUE(!!yin.error());EXPECT_EQ(cont.fbs.size(), 0UL);}{FooBarContainer cont;Input yin("---\nfbs: 'scalar'\n...\n", nullptr, suppressErrorMessages);yin >> cont;// This should be an error.EXPECT_TRUE(!!yin.error());EXPECT_EQ(cont.fbs.size(), 0UL);}}//// Test writing then reading back a sequence of mappings//TEST(YAMLIO, TestSequenceMapWriteAndRead) {std::string intermediate;{FooBar entry1;entry1.foo = 10;entry1.bar = -3;FooBar entry2;entry2.foo = 257;entry2.bar = 0;FooBarSequence seq;seq.push_back(entry1);seq.push_back(entry2);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << seq;}{Input yin(intermediate);FooBarSequence seq2;yin >> seq2;EXPECT_FALSE(yin.error());EXPECT_EQ(seq2.size(), 2UL);FooBar& map1 = seq2[0];FooBar& map2 = seq2[1];EXPECT_EQ(map1.foo, 10);EXPECT_EQ(map1.bar, -3);EXPECT_EQ(map2.foo, 257);EXPECT_EQ(map2.bar, 0);}}//// Test reading the entire struct as an enum.//struct FooBarEnum {int Foo;int Bar;bool operator==(const FooBarEnum &R) const {return Foo == R.Foo && Bar == R.Bar;}};namespace llvm {namespace yaml {template <> struct MappingTraits<FooBarEnum> {static void enumInput(IO &io, FooBarEnum &Val) {io.enumCase(Val, "OnlyFoo", FooBarEnum({1, 0}));io.enumCase(Val, "OnlyBar", FooBarEnum({0, 1}));}static void mapping(IO &io, FooBarEnum &Val) {io.mapOptional("Foo", Val.Foo);io.mapOptional("Bar", Val.Bar);}};} // namespace yaml} // namespace llvmTEST(YAMLIO, TestMapEnumRead) {FooBarEnum Doc;{Input Yin("OnlyFoo");Yin >> Doc;EXPECT_FALSE(Yin.error());EXPECT_EQ(Doc.Foo, 1);EXPECT_EQ(Doc.Bar, 0);}{Input Yin("OnlyBar");Yin >> Doc;EXPECT_FALSE(Yin.error());EXPECT_EQ(Doc.Foo, 0);EXPECT_EQ(Doc.Bar, 1);}{Input Yin("{Foo: 3, Bar: 5}");Yin >> Doc;EXPECT_FALSE(Yin.error());EXPECT_EQ(Doc.Foo, 3);EXPECT_EQ(Doc.Bar, 5);}}//// Test YAML filename handling.//static void testErrorFilename(const llvm::SMDiagnostic &Error, void *) {EXPECT_EQ(Error.getFilename(), "foo.yaml");}TEST(YAMLIO, TestGivenFilename) {auto Buffer = llvm::MemoryBuffer::getMemBuffer("{ x: 42 }", "foo.yaml");Input yin(*Buffer, nullptr, testErrorFilename);FooBar Value;yin >> Value;EXPECT_TRUE(!!yin.error());}struct WithStringField {std::string str1;std::string str2;std::string str3;};namespace llvm {namespace yaml {template <> struct MappingTraits<WithStringField> {static void mapping(IO &io, WithStringField &fb) {io.mapRequired("str1", fb.str1);io.mapRequired("str2", fb.str2);io.mapRequired("str3", fb.str3);}};} // namespace yaml} // namespace llvmTEST(YAMLIO, MultilineStrings) {WithStringField Original;Original.str1 = "a multiline string\nfoobarbaz";Original.str2 = "another one\rfoobarbaz";Original.str3 = "a one-line string";std::string Serialized;{llvm::raw_string_ostream OS(Serialized);Output YOut(OS);YOut << Original;}auto Expected = "---\n""str1: \"a multiline string\\nfoobarbaz\"\n""str2: \"another one\\rfoobarbaz\"\n""str3: a one-line string\n""...\n";ASSERT_EQ(Serialized, Expected);// Also check it parses back without the errors.WithStringField Deserialized;{Input YIn(Serialized);YIn >> Deserialized;ASSERT_FALSE(YIn.error())<< "Parsing error occurred during deserialization. Serialized string:\n"<< Serialized;}EXPECT_EQ(Original.str1, Deserialized.str1);EXPECT_EQ(Original.str2, Deserialized.str2);EXPECT_EQ(Original.str3, Deserialized.str3);}TEST(YAMLIO, NoQuotesForTab) {WithStringField WithTab;WithTab.str1 = "aba\tcaba";std::string Serialized;{llvm::raw_string_ostream OS(Serialized);Output YOut(OS);YOut << WithTab;}auto ExpectedPrefix = "---\n""str1: aba\tcaba\n";EXPECT_THAT(Serialized, StartsWith(ExpectedPrefix));}//===----------------------------------------------------------------------===//// Test built-in types//===----------------------------------------------------------------------===//struct BuiltInTypes {llvm::StringRef str;std::string stdstr;uint64_t u64;uint32_t u32;uint16_t u16;uint8_t u8;bool b;int64_t s64;int32_t s32;int16_t s16;int8_t s8;float f;double d;Hex8 h8;Hex16 h16;Hex32 h32;Hex64 h64;};namespace llvm {namespace yaml {template <>struct MappingTraits<BuiltInTypes> {static void mapping(IO &io, BuiltInTypes& bt) {io.mapRequired("str", bt.str);io.mapRequired("stdstr", bt.stdstr);io.mapRequired("u64", bt.u64);io.mapRequired("u32", bt.u32);io.mapRequired("u16", bt.u16);io.mapRequired("u8", bt.u8);io.mapRequired("b", bt.b);io.mapRequired("s64", bt.s64);io.mapRequired("s32", bt.s32);io.mapRequired("s16", bt.s16);io.mapRequired("s8", bt.s8);io.mapRequired("f", bt.f);io.mapRequired("d", bt.d);io.mapRequired("h8", bt.h8);io.mapRequired("h16", bt.h16);io.mapRequired("h32", bt.h32);io.mapRequired("h64", bt.h64);}};}}//// Test the reading of all built-in scalar conversions//TEST(YAMLIO, TestReadBuiltInTypes) {BuiltInTypes map;Input yin("---\n""str: hello there\n""stdstr: hello where?\n""u64: 5000000000\n""u32: 4000000000\n""u16: 65000\n""u8: 255\n""b: false\n""s64: -5000000000\n""s32: -2000000000\n""s16: -32000\n""s8: -127\n""f: 137.125\n""d: -2.8625\n""h8: 0xFF\n""h16: 0x8765\n""h32: 0xFEDCBA98\n""h64: 0xFEDCBA9876543210\n""...\n");yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(map.str, "hello there");EXPECT_EQ(map.stdstr, "hello where?");EXPECT_EQ(map.u64, 5000000000ULL);EXPECT_EQ(map.u32, 4000000000U);EXPECT_EQ(map.u16, 65000);EXPECT_EQ(map.u8, 255);EXPECT_EQ(map.b, false);EXPECT_EQ(map.s64, -5000000000LL);EXPECT_EQ(map.s32, -2000000000L);EXPECT_EQ(map.s16, -32000);EXPECT_EQ(map.s8, -127);EXPECT_EQ(map.f, 137.125);EXPECT_EQ(map.d, -2.8625);EXPECT_EQ(map.h8, Hex8(255));EXPECT_EQ(map.h16, Hex16(0x8765));EXPECT_EQ(map.h32, Hex32(0xFEDCBA98));EXPECT_EQ(map.h64, Hex64(0xFEDCBA9876543210LL));}//// Test writing then reading back all built-in scalar types//TEST(YAMLIO, TestReadWriteBuiltInTypes) {std::string intermediate;{BuiltInTypes map;map.str = "one two";map.stdstr = "three four";map.u64 = 6000000000ULL;map.u32 = 3000000000U;map.u16 = 50000;map.u8 = 254;map.b = true;map.s64 = -6000000000LL;map.s32 = -2000000000;map.s16 = -32000;map.s8 = -128;map.f = 3.25;map.d = -2.8625;map.h8 = 254;map.h16 = 50000;map.h32 = 3000000000U;map.h64 = 6000000000LL;llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;}{Input yin(intermediate);BuiltInTypes map;yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(map.str, "one two");EXPECT_EQ(map.stdstr, "three four");EXPECT_EQ(map.u64, 6000000000ULL);EXPECT_EQ(map.u32, 3000000000U);EXPECT_EQ(map.u16, 50000);EXPECT_EQ(map.u8, 254);EXPECT_EQ(map.b, true);EXPECT_EQ(map.s64, -6000000000LL);EXPECT_EQ(map.s32, -2000000000L);EXPECT_EQ(map.s16, -32000);EXPECT_EQ(map.s8, -128);EXPECT_EQ(map.f, 3.25);EXPECT_EQ(map.d, -2.8625);EXPECT_EQ(map.h8, Hex8(254));EXPECT_EQ(map.h16, Hex16(50000));EXPECT_EQ(map.h32, Hex32(3000000000U));EXPECT_EQ(map.h64, Hex64(6000000000LL));}}//===----------------------------------------------------------------------===//// Test endian-aware types//===----------------------------------------------------------------------===//struct EndianTypes {typedef llvm::support::detail::packed_endian_specific_integral<float, llvm::support::little, llvm::support::unaligned>ulittle_float;typedef llvm::support::detail::packed_endian_specific_integral<double, llvm::support::little, llvm::support::unaligned>ulittle_double;llvm::support::ulittle64_t u64;llvm::support::ulittle32_t u32;llvm::support::ulittle16_t u16;llvm::support::little64_t s64;llvm::support::little32_t s32;llvm::support::little16_t s16;ulittle_float f;ulittle_double d;};namespace llvm {namespace yaml {template <> struct MappingTraits<EndianTypes> {static void mapping(IO &io, EndianTypes &et) {io.mapRequired("u64", et.u64);io.mapRequired("u32", et.u32);io.mapRequired("u16", et.u16);io.mapRequired("s64", et.s64);io.mapRequired("s32", et.s32);io.mapRequired("s16", et.s16);io.mapRequired("f", et.f);io.mapRequired("d", et.d);}};}}//// Test the reading of all endian scalar conversions//TEST(YAMLIO, TestReadEndianTypes) {EndianTypes map;Input yin("---\n""u64: 5000000000\n""u32: 4000000000\n""u16: 65000\n""s64: -5000000000\n""s32: -2000000000\n""s16: -32000\n""f: 3.25\n""d: -2.8625\n""...\n");yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(map.u64, 5000000000ULL);EXPECT_EQ(map.u32, 4000000000U);EXPECT_EQ(map.u16, 65000);EXPECT_EQ(map.s64, -5000000000LL);EXPECT_EQ(map.s32, -2000000000L);EXPECT_EQ(map.s16, -32000);EXPECT_EQ(map.f, 3.25f);EXPECT_EQ(map.d, -2.8625);}//// Test writing then reading back all endian-aware scalar types//TEST(YAMLIO, TestReadWriteEndianTypes) {std::string intermediate;{EndianTypes map;map.u64 = 6000000000ULL;map.u32 = 3000000000U;map.u16 = 50000;map.s64 = -6000000000LL;map.s32 = -2000000000;map.s16 = -32000;map.f = 3.25f;map.d = -2.8625;llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;}{Input yin(intermediate);EndianTypes map;yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(map.u64, 6000000000ULL);EXPECT_EQ(map.u32, 3000000000U);EXPECT_EQ(map.u16, 50000);EXPECT_EQ(map.s64, -6000000000LL);EXPECT_EQ(map.s32, -2000000000L);EXPECT_EQ(map.s16, -32000);EXPECT_EQ(map.f, 3.25f);EXPECT_EQ(map.d, -2.8625);}}enum class Enum : uint16_t { One, Two };enum class BitsetEnum : uint16_t {ZeroOne = 0x01,OneZero = 0x10,LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ OneZero),};LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();struct EndianEnums {llvm::support::little_t<Enum> LittleEnum;llvm::support::big_t<Enum> BigEnum;llvm::support::little_t<BitsetEnum> LittleBitset;llvm::support::big_t<BitsetEnum> BigBitset;};namespace llvm {namespace yaml {template <> struct ScalarEnumerationTraits<Enum> {static void enumeration(IO &io, Enum &E) {io.enumCase(E, "One", Enum::One);io.enumCase(E, "Two", Enum::Two);}};template <> struct ScalarBitSetTraits<BitsetEnum> {static void bitset(IO &io, BitsetEnum &E) {io.bitSetCase(E, "ZeroOne", BitsetEnum::ZeroOne);io.bitSetCase(E, "OneZero", BitsetEnum::OneZero);}};template <> struct MappingTraits<EndianEnums> {static void mapping(IO &io, EndianEnums &EE) {io.mapRequired("LittleEnum", EE.LittleEnum);io.mapRequired("BigEnum", EE.BigEnum);io.mapRequired("LittleBitset", EE.LittleBitset);io.mapRequired("BigBitset", EE.BigBitset);}};} // namespace yaml} // namespace llvmTEST(YAMLIO, TestReadEndianEnums) {EndianEnums map;Input yin("---\n""LittleEnum: One\n""BigEnum: Two\n""LittleBitset: [ ZeroOne ]\n""BigBitset: [ ZeroOne, OneZero ]\n""...\n");yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(Enum::One, map.LittleEnum);EXPECT_EQ(Enum::Two, map.BigEnum);EXPECT_EQ(BitsetEnum::ZeroOne, map.LittleBitset);EXPECT_EQ(BitsetEnum::ZeroOne | BitsetEnum::OneZero, map.BigBitset);}TEST(YAMLIO, TestReadWriteEndianEnums) {std::string intermediate;{EndianEnums map;map.LittleEnum = Enum::Two;map.BigEnum = Enum::One;map.LittleBitset = BitsetEnum::OneZero | BitsetEnum::ZeroOne;map.BigBitset = BitsetEnum::OneZero;llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;}{Input yin(intermediate);EndianEnums map;yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(Enum::Two, map.LittleEnum);EXPECT_EQ(Enum::One, map.BigEnum);EXPECT_EQ(BitsetEnum::OneZero | BitsetEnum::ZeroOne, map.LittleBitset);EXPECT_EQ(BitsetEnum::OneZero, map.BigBitset);}}struct StringTypes {llvm::StringRef str1;llvm::StringRef str2;llvm::StringRef str3;llvm::StringRef str4;llvm::StringRef str5;llvm::StringRef str6;llvm::StringRef str7;llvm::StringRef str8;llvm::StringRef str9;llvm::StringRef str10;llvm::StringRef str11;std::string stdstr1;std::string stdstr2;std::string stdstr3;std::string stdstr4;std::string stdstr5;std::string stdstr6;std::string stdstr7;std::string stdstr8;std::string stdstr9;std::string stdstr10;std::string stdstr11;std::string stdstr12;std::string stdstr13;};namespace llvm {namespace yaml {template <>struct MappingTraits<StringTypes> {static void mapping(IO &io, StringTypes& st) {io.mapRequired("str1", st.str1);io.mapRequired("str2", st.str2);io.mapRequired("str3", st.str3);io.mapRequired("str4", st.str4);io.mapRequired("str5", st.str5);io.mapRequired("str6", st.str6);io.mapRequired("str7", st.str7);io.mapRequired("str8", st.str8);io.mapRequired("str9", st.str9);io.mapRequired("str10", st.str10);io.mapRequired("str11", st.str11);io.mapRequired("stdstr1", st.stdstr1);io.mapRequired("stdstr2", st.stdstr2);io.mapRequired("stdstr3", st.stdstr3);io.mapRequired("stdstr4", st.stdstr4);io.mapRequired("stdstr5", st.stdstr5);io.mapRequired("stdstr6", st.stdstr6);io.mapRequired("stdstr7", st.stdstr7);io.mapRequired("stdstr8", st.stdstr8);io.mapRequired("stdstr9", st.stdstr9);io.mapRequired("stdstr10", st.stdstr10);io.mapRequired("stdstr11", st.stdstr11);io.mapRequired("stdstr12", st.stdstr12);io.mapRequired("stdstr13", st.stdstr13);}};}}TEST(YAMLIO, TestReadWriteStringTypes) {std::string intermediate;{StringTypes map;map.str1 = "'aaa";map.str2 = "\"bbb";map.str3 = "`ccc";map.str4 = "@ddd";map.str5 = "";map.str6 = "0000000004000000";map.str7 = "true";map.str8 = "FALSE";map.str9 = "~";map.str10 = "0.2e20";map.str11 = "0x30";map.stdstr1 = "'eee";map.stdstr2 = "\"fff";map.stdstr3 = "`ggg";map.stdstr4 = "@hhh";map.stdstr5 = "";map.stdstr6 = "0000000004000000";map.stdstr7 = "true";map.stdstr8 = "FALSE";map.stdstr9 = "~";map.stdstr10 = "0.2e20";map.stdstr11 = "0x30";map.stdstr12 = "- match";map.stdstr13.assign("\0a\0b\0", 5);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;}llvm::StringRef flowOut(intermediate);EXPECT_NE(llvm::StringRef::npos, flowOut.find("'''aaa"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'\"bbb'"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'`ccc'"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'@ddd'"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("''\n"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0000000004000000'\n"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'true'\n"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'FALSE'\n"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'~'\n"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0.2e20'\n"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0x30'\n"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("'- match'\n"));EXPECT_NE(std::string::npos, flowOut.find("'''eee"));EXPECT_NE(std::string::npos, flowOut.find("'\"fff'"));EXPECT_NE(std::string::npos, flowOut.find("'`ggg'"));EXPECT_NE(std::string::npos, flowOut.find("'@hhh'"));EXPECT_NE(std::string::npos, flowOut.find("''\n"));EXPECT_NE(std::string::npos, flowOut.find("'0000000004000000'\n"));EXPECT_NE(std::string::npos, flowOut.find("\"\\0a\\0b\\0\""));{Input yin(intermediate);StringTypes map;yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(map.str1, "'aaa");EXPECT_EQ(map.str2, "\"bbb");EXPECT_EQ(map.str3, "`ccc");EXPECT_EQ(map.str4, "@ddd");EXPECT_EQ(map.str5, "");EXPECT_EQ(map.str6, "0000000004000000");EXPECT_EQ(map.stdstr1, "'eee");EXPECT_EQ(map.stdstr2, "\"fff");EXPECT_EQ(map.stdstr3, "`ggg");EXPECT_EQ(map.stdstr4, "@hhh");EXPECT_EQ(map.stdstr5, "");EXPECT_EQ(map.stdstr6, "0000000004000000");EXPECT_EQ(std::string("\0a\0b\0", 5), map.stdstr13);}}//===----------------------------------------------------------------------===//// Test ScalarEnumerationTraits//===----------------------------------------------------------------------===//enum Colors {cRed,cBlue,cGreen,cYellow};struct ColorMap {Colors c1;Colors c2;Colors c3;Colors c4;Colors c5;Colors c6;};namespace llvm {namespace yaml {template <>struct ScalarEnumerationTraits<Colors> {static void enumeration(IO &io, Colors &value) {io.enumCase(value, "red", cRed);io.enumCase(value, "blue", cBlue);io.enumCase(value, "green", cGreen);io.enumCase(value, "yellow",cYellow);}};template <>struct MappingTraits<ColorMap> {static void mapping(IO &io, ColorMap& c) {io.mapRequired("c1", c.c1);io.mapRequired("c2", c.c2);io.mapRequired("c3", c.c3);io.mapOptional("c4", c.c4, cBlue); // supplies defaultio.mapOptional("c5", c.c5, cYellow); // supplies defaultio.mapOptional("c6", c.c6, cRed); // supplies default}};}}//// Test reading enumerated scalars//TEST(YAMLIO, TestEnumRead) {ColorMap map;Input yin("---\n""c1: blue\n""c2: red\n""c3: green\n""c5: yellow\n""...\n");yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(cBlue, map.c1);EXPECT_EQ(cRed, map.c2);EXPECT_EQ(cGreen, map.c3);EXPECT_EQ(cBlue, map.c4); // tests defaultEXPECT_EQ(cYellow,map.c5); // tests overriddenEXPECT_EQ(cRed, map.c6); // tests default}//===----------------------------------------------------------------------===//// Test ScalarBitSetTraits//===----------------------------------------------------------------------===//enum MyFlags {flagNone = 0,flagBig = 1 << 0,flagFlat = 1 << 1,flagRound = 1 << 2,flagPointy = 1 << 3};inline MyFlags operator|(MyFlags a, MyFlags b) {return static_cast<MyFlags>(static_cast<uint32_t>(a) | static_cast<uint32_t>(b));}struct FlagsMap {MyFlags f1;MyFlags f2;MyFlags f3;MyFlags f4;};namespace llvm {namespace yaml {template <>struct ScalarBitSetTraits<MyFlags> {static void bitset(IO &io, MyFlags &value) {io.bitSetCase(value, "big", flagBig);io.bitSetCase(value, "flat", flagFlat);io.bitSetCase(value, "round", flagRound);io.bitSetCase(value, "pointy",flagPointy);}};template <>struct MappingTraits<FlagsMap> {static void mapping(IO &io, FlagsMap& c) {io.mapRequired("f1", c.f1);io.mapRequired("f2", c.f2);io.mapRequired("f3", c.f3);io.mapOptional("f4", c.f4, flagRound);}};}}//// Test reading flow sequence representing bit-mask values//TEST(YAMLIO, TestFlagsRead) {FlagsMap map;Input yin("---\n""f1: [ big ]\n""f2: [ round, flat ]\n""f3: []\n""...\n");yin >> map;EXPECT_FALSE(yin.error());EXPECT_EQ(flagBig, map.f1);EXPECT_EQ(flagRound|flagFlat, map.f2);EXPECT_EQ(flagNone, map.f3); // check empty setEXPECT_EQ(flagRound, map.f4); // check optional key}//// Test writing then reading back bit-mask values//TEST(YAMLIO, TestReadWriteFlags) {std::string intermediate;{FlagsMap map;map.f1 = flagBig;map.f2 = flagRound | flagFlat;map.f3 = flagNone;map.f4 = flagNone;llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;}{Input yin(intermediate);FlagsMap map2;yin >> map2;EXPECT_FALSE(yin.error());EXPECT_EQ(flagBig, map2.f1);EXPECT_EQ(flagRound|flagFlat, map2.f2);EXPECT_EQ(flagNone, map2.f3);//EXPECT_EQ(flagRound, map2.f4); // check optional key}}//===----------------------------------------------------------------------===//// Test ScalarTraits//===----------------------------------------------------------------------===//struct MyCustomType {int length;int width;};struct MyCustomTypeMap {MyCustomType f1;MyCustomType f2;int f3;};namespace llvm {namespace yaml {template <>struct MappingTraits<MyCustomTypeMap> {static void mapping(IO &io, MyCustomTypeMap& s) {io.mapRequired("f1", s.f1);io.mapRequired("f2", s.f2);io.mapRequired("f3", s.f3);}};// MyCustomType is formatted as a yaml scalar. A value of// {length=3, width=4} would be represented in yaml as "3 by 4".template<>struct ScalarTraits<MyCustomType> {static void output(const MyCustomType &value, void* ctxt, llvm::raw_ostream &out) {out << llvm::format("%d by %d", value.length, value.width);}static StringRef input(StringRef scalar, void* ctxt, MyCustomType &value) {size_t byStart = scalar.find("by");if ( byStart != StringRef::npos ) {StringRef lenStr = scalar.slice(0, byStart);lenStr = lenStr.rtrim();if ( lenStr.getAsInteger(0, value.length) ) {return "malformed length";}StringRef widthStr = scalar.drop_front(byStart+2);widthStr = widthStr.ltrim();if ( widthStr.getAsInteger(0, value.width) ) {return "malformed width";}return StringRef();}else {return "malformed by";}}static QuotingType mustQuote(StringRef) { return QuotingType::Single; }};}}//// Test writing then reading back custom values//TEST(YAMLIO, TestReadWriteMyCustomType) {std::string intermediate;{MyCustomTypeMap map;map.f1.length = 1;map.f1.width = 4;map.f2.length = 100;map.f2.width = 400;map.f3 = 10;llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;}{Input yin(intermediate);MyCustomTypeMap map2;yin >> map2;EXPECT_FALSE(yin.error());EXPECT_EQ(1, map2.f1.length);EXPECT_EQ(4, map2.f1.width);EXPECT_EQ(100, map2.f2.length);EXPECT_EQ(400, map2.f2.width);EXPECT_EQ(10, map2.f3);}}//===----------------------------------------------------------------------===//// Test BlockScalarTraits//===----------------------------------------------------------------------===//struct MultilineStringType {std::string str;};struct MultilineStringTypeMap {MultilineStringType name;MultilineStringType description;MultilineStringType ingredients;MultilineStringType recipes;MultilineStringType warningLabels;MultilineStringType documentation;int price;};namespace llvm {namespace yaml {template <>struct MappingTraits<MultilineStringTypeMap> {static void mapping(IO &io, MultilineStringTypeMap& s) {io.mapRequired("name", s.name);io.mapRequired("description", s.description);io.mapRequired("ingredients", s.ingredients);io.mapRequired("recipes", s.recipes);io.mapRequired("warningLabels", s.warningLabels);io.mapRequired("documentation", s.documentation);io.mapRequired("price", s.price);}};// MultilineStringType is formatted as a yaml block literal scalar. A value of// "Hello\nWorld" would be represented in yaml as// |// Hello// Worldtemplate <>struct BlockScalarTraits<MultilineStringType> {static void output(const MultilineStringType &value, void *ctxt,llvm::raw_ostream &out) {out << value.str;}static StringRef input(StringRef scalar, void *ctxt,MultilineStringType &value) {value.str = scalar.str();return StringRef();}};}}LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MultilineStringType)//// Test writing then reading back custom values//TEST(YAMLIO, TestReadWriteMultilineStringType) {std::string intermediate;{MultilineStringTypeMap map;map.name.str = "An Item";map.description.str = "Hello\nWorld";map.ingredients.str = "SubItem 1\nSub Item 2\n\nSub Item 3\n";map.recipes.str = "\n\nTest 1\n\n\n";map.warningLabels.str = "";map.documentation.str = "\n\n";map.price = 350;llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;}{Input yin(intermediate);MultilineStringTypeMap map2;yin >> map2;EXPECT_FALSE(yin.error());EXPECT_EQ(map2.name.str, "An Item\n");EXPECT_EQ(map2.description.str, "Hello\nWorld\n");EXPECT_EQ(map2.ingredients.str, "SubItem 1\nSub Item 2\n\nSub Item 3\n");EXPECT_EQ(map2.recipes.str, "\n\nTest 1\n");EXPECT_TRUE(map2.warningLabels.str.empty());EXPECT_TRUE(map2.documentation.str.empty());EXPECT_EQ(map2.price, 350);}}//// Test writing then reading back custom values//TEST(YAMLIO, TestReadWriteBlockScalarDocuments) {std::string intermediate;{std::vector<MultilineStringType> documents;MultilineStringType doc;doc.str = "Hello\nWorld";documents.push_back(doc);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << documents;// Verify that the block scalar header was written out on the same line// as the document marker.EXPECT_NE(llvm::StringRef::npos, llvm::StringRef(ostr.str()).find("--- |"));}{Input yin(intermediate);std::vector<MultilineStringType> documents2;yin >> documents2;EXPECT_FALSE(yin.error());EXPECT_EQ(documents2.size(), size_t(1));EXPECT_EQ(documents2[0].str, "Hello\nWorld\n");}}TEST(YAMLIO, TestReadWriteBlockScalarValue) {std::string intermediate;{MultilineStringType doc;doc.str = "Just a block\nscalar doc";llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << doc;}{Input yin(intermediate);MultilineStringType doc;yin >> doc;EXPECT_FALSE(yin.error());EXPECT_EQ(doc.str, "Just a block\nscalar doc\n");}}//===----------------------------------------------------------------------===//// Test flow sequences//===----------------------------------------------------------------------===//LLVM_YAML_STRONG_TYPEDEF(int, MyNumber)LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(MyNumber)LLVM_YAML_STRONG_TYPEDEF(llvm::StringRef, MyString)LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(MyString)namespace llvm {namespace yaml {template<>struct ScalarTraits<MyNumber> {static void output(const MyNumber &value, void *, llvm::raw_ostream &out) {out << value;}static StringRef input(StringRef scalar, void *, MyNumber &value) {long long n;if ( getAsSignedInteger(scalar, 0, n) )return "invalid number";value = n;return StringRef();}static QuotingType mustQuote(StringRef) { return QuotingType::None; }};template <> struct ScalarTraits<MyString> {using Impl = ScalarTraits<StringRef>;static void output(const MyString &V, void *Ctx, raw_ostream &OS) {Impl::output(V, Ctx, OS);}static StringRef input(StringRef S, void *Ctx, MyString &V) {return Impl::input(S, Ctx, V.value);}static QuotingType mustQuote(StringRef S) {return Impl::mustQuote(S);}};}}struct NameAndNumbers {llvm::StringRef name;std::vector<MyString> strings;std::vector<MyNumber> single;std::vector<MyNumber> numbers;};namespace llvm {namespace yaml {template <>struct MappingTraits<NameAndNumbers> {static void mapping(IO &io, NameAndNumbers& nn) {io.mapRequired("name", nn.name);io.mapRequired("strings", nn.strings);io.mapRequired("single", nn.single);io.mapRequired("numbers", nn.numbers);}};}}typedef std::vector<MyNumber> MyNumberFlowSequence;LLVM_YAML_IS_SEQUENCE_VECTOR(MyNumberFlowSequence)struct NameAndNumbersFlow {llvm::StringRef name;std::vector<MyNumberFlowSequence> sequenceOfNumbers;};namespace llvm {namespace yaml {template <>struct MappingTraits<NameAndNumbersFlow> {static void mapping(IO &io, NameAndNumbersFlow& nn) {io.mapRequired("name", nn.name);io.mapRequired("sequenceOfNumbers", nn.sequenceOfNumbers);}};}}//// Test writing then reading back custom values//TEST(YAMLIO, TestReadWriteMyFlowSequence) {std::string intermediate;{NameAndNumbers map;map.name = "hello";map.strings.push_back(llvm::StringRef("one"));map.strings.push_back(llvm::StringRef("two"));map.single.push_back(1);map.numbers.push_back(10);map.numbers.push_back(-30);map.numbers.push_back(1024);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;// Verify sequences were written in flow styleostr.flush();llvm::StringRef flowOut(intermediate);EXPECT_NE(llvm::StringRef::npos, flowOut.find("one, two"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("10, -30, 1024"));}{Input yin(intermediate);NameAndNumbers map2;yin >> map2;EXPECT_FALSE(yin.error());EXPECT_TRUE(map2.name.equals("hello"));EXPECT_EQ(map2.strings.size(), 2UL);EXPECT_TRUE(map2.strings[0].value.equals("one"));EXPECT_TRUE(map2.strings[1].value.equals("two"));EXPECT_EQ(map2.single.size(), 1UL);EXPECT_EQ(1, map2.single[0]);EXPECT_EQ(map2.numbers.size(), 3UL);EXPECT_EQ(10, map2.numbers[0]);EXPECT_EQ(-30, map2.numbers[1]);EXPECT_EQ(1024, map2.numbers[2]);}}//// Test writing then reading back a sequence of flow sequences.//TEST(YAMLIO, TestReadWriteSequenceOfMyFlowSequence) {std::string intermediate;{NameAndNumbersFlow map;map.name = "hello";MyNumberFlowSequence single = { 0 };MyNumberFlowSequence numbers = { 12, 1, -512 };map.sequenceOfNumbers.push_back(single);map.sequenceOfNumbers.push_back(numbers);map.sequenceOfNumbers.push_back(MyNumberFlowSequence());llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << map;// Verify sequences were written in flow style// and that the parent sequence used '-'.ostr.flush();llvm::StringRef flowOut(intermediate);EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ 0 ]"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ 12, 1, -512 ]"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ ]"));}{Input yin(intermediate);NameAndNumbersFlow map2;yin >> map2;EXPECT_FALSE(yin.error());EXPECT_TRUE(map2.name.equals("hello"));EXPECT_EQ(map2.sequenceOfNumbers.size(), 3UL);EXPECT_EQ(map2.sequenceOfNumbers[0].size(), 1UL);EXPECT_EQ(0, map2.sequenceOfNumbers[0][0]);EXPECT_EQ(map2.sequenceOfNumbers[1].size(), 3UL);EXPECT_EQ(12, map2.sequenceOfNumbers[1][0]);EXPECT_EQ(1, map2.sequenceOfNumbers[1][1]);EXPECT_EQ(-512, map2.sequenceOfNumbers[1][2]);EXPECT_TRUE(map2.sequenceOfNumbers[2].empty());}}//===----------------------------------------------------------------------===//// Test normalizing/denormalizing//===----------------------------------------------------------------------===//LLVM_YAML_STRONG_TYPEDEF(uint32_t, TotalSeconds)typedef std::vector<TotalSeconds> SecondsSequence;LLVM_YAML_IS_SEQUENCE_VECTOR(TotalSeconds)namespace llvm {namespace yaml {template <>struct MappingTraits<TotalSeconds> {class NormalizedSeconds {public:NormalizedSeconds(IO &io): hours(0), minutes(0), seconds(0) {}NormalizedSeconds(IO &, TotalSeconds &secs): hours(secs/3600),minutes((secs - (hours*3600))/60),seconds(secs % 60) {}TotalSeconds denormalize(IO &) {return TotalSeconds(hours*3600 + minutes*60 + seconds);}uint32_t hours;uint8_t minutes;uint8_t seconds;};static void mapping(IO &io, TotalSeconds &secs) {MappingNormalization<NormalizedSeconds, TotalSeconds> keys(io, secs);io.mapOptional("hours", keys->hours, 0);io.mapOptional("minutes", keys->minutes, 0);io.mapRequired("seconds", keys->seconds);}};}}//// Test the reading of a yaml sequence of mappings//TEST(YAMLIO, TestReadMySecondsSequence) {SecondsSequence seq;Input yin("---\n - hours: 1\n seconds: 5\n - seconds: 59\n...\n");yin >> seq;EXPECT_FALSE(yin.error());EXPECT_EQ(seq.size(), 2UL);EXPECT_EQ(seq[0], 3605U);EXPECT_EQ(seq[1], 59U);}//// Test writing then reading back custom values//TEST(YAMLIO, TestReadWriteMySecondsSequence) {std::string intermediate;{SecondsSequence seq;seq.push_back(4000);seq.push_back(500);seq.push_back(59);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << seq;}{Input yin(intermediate);SecondsSequence seq2;yin >> seq2;EXPECT_FALSE(yin.error());EXPECT_EQ(seq2.size(), 3UL);EXPECT_EQ(seq2[0], 4000U);EXPECT_EQ(seq2[1], 500U);EXPECT_EQ(seq2[2], 59U);}}//===----------------------------------------------------------------------===//// Test dynamic typing//===----------------------------------------------------------------------===//enum AFlags {a1,a2,a3};enum BFlags {b1,b2,b3};enum Kind {kindA,kindB};struct KindAndFlags {KindAndFlags() : kind(kindA), flags(0) { }KindAndFlags(Kind k, uint32_t f) : kind(k), flags(f) { }Kind kind;uint32_t flags;};typedef std::vector<KindAndFlags> KindAndFlagsSequence;LLVM_YAML_IS_SEQUENCE_VECTOR(KindAndFlags)namespace llvm {namespace yaml {template <>struct ScalarEnumerationTraits<AFlags> {static void enumeration(IO &io, AFlags &value) {io.enumCase(value, "a1", a1);io.enumCase(value, "a2", a2);io.enumCase(value, "a3", a3);}};template <>struct ScalarEnumerationTraits<BFlags> {static void enumeration(IO &io, BFlags &value) {io.enumCase(value, "b1", b1);io.enumCase(value, "b2", b2);io.enumCase(value, "b3", b3);}};template <>struct ScalarEnumerationTraits<Kind> {static void enumeration(IO &io, Kind &value) {io.enumCase(value, "A", kindA);io.enumCase(value, "B", kindB);}};template <>struct MappingTraits<KindAndFlags> {static void mapping(IO &io, KindAndFlags& kf) {io.mapRequired("kind", kf.kind);// Type of "flags" field varies depending on "kind" field.// Use memcpy here to avoid breaking strict aliasing rules.if (kf.kind == kindA) {AFlags aflags = static_cast<AFlags>(kf.flags);io.mapRequired("flags", aflags);kf.flags = aflags;} else {BFlags bflags = static_cast<BFlags>(kf.flags);io.mapRequired("flags", bflags);kf.flags = bflags;}}};}}//// Test the reading of a yaml sequence dynamic types//TEST(YAMLIO, TestReadKindAndFlagsSequence) {KindAndFlagsSequence seq;Input yin("---\n - kind: A\n flags: a2\n - kind: B\n flags: b1\n...\n");yin >> seq;EXPECT_FALSE(yin.error());EXPECT_EQ(seq.size(), 2UL);EXPECT_EQ(seq[0].kind, kindA);EXPECT_EQ(seq[0].flags, (uint32_t)a2);EXPECT_EQ(seq[1].kind, kindB);EXPECT_EQ(seq[1].flags, (uint32_t)b1);}//// Test writing then reading back dynamic types//TEST(YAMLIO, TestReadWriteKindAndFlagsSequence) {std::string intermediate;{KindAndFlagsSequence seq;seq.push_back(KindAndFlags(kindA,a1));seq.push_back(KindAndFlags(kindB,b1));seq.push_back(KindAndFlags(kindA,a2));seq.push_back(KindAndFlags(kindB,b2));seq.push_back(KindAndFlags(kindA,a3));llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << seq;}{Input yin(intermediate);KindAndFlagsSequence seq2;yin >> seq2;EXPECT_FALSE(yin.error());EXPECT_EQ(seq2.size(), 5UL);EXPECT_EQ(seq2[0].kind, kindA);EXPECT_EQ(seq2[0].flags, (uint32_t)a1);EXPECT_EQ(seq2[1].kind, kindB);EXPECT_EQ(seq2[1].flags, (uint32_t)b1);EXPECT_EQ(seq2[2].kind, kindA);EXPECT_EQ(seq2[2].flags, (uint32_t)a2);EXPECT_EQ(seq2[3].kind, kindB);EXPECT_EQ(seq2[3].flags, (uint32_t)b2);EXPECT_EQ(seq2[4].kind, kindA);EXPECT_EQ(seq2[4].flags, (uint32_t)a3);}}//===----------------------------------------------------------------------===//// Test document list//===----------------------------------------------------------------------===//struct FooBarMap {int foo;int bar;};typedef std::vector<FooBarMap> FooBarMapDocumentList;LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(FooBarMap)namespace llvm {namespace yaml {template <>struct MappingTraits<FooBarMap> {static void mapping(IO &io, FooBarMap& fb) {io.mapRequired("foo", fb.foo);io.mapRequired("bar", fb.bar);}};}}//// Test the reading of a yaml mapping//TEST(YAMLIO, TestDocRead) {FooBarMap doc;Input yin("---\nfoo: 3\nbar: 5\n...\n");yin >> doc;EXPECT_FALSE(yin.error());EXPECT_EQ(doc.foo, 3);EXPECT_EQ(doc.bar,5);}//// Test writing then reading back a sequence of mappings//TEST(YAMLIO, TestSequenceDocListWriteAndRead) {std::string intermediate;{FooBarMap doc1;doc1.foo = 10;doc1.bar = -3;FooBarMap doc2;doc2.foo = 257;doc2.bar = 0;std::vector<FooBarMap> docList;docList.push_back(doc1);docList.push_back(doc2);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << docList;}{Input yin(intermediate);std::vector<FooBarMap> docList2;yin >> docList2;EXPECT_FALSE(yin.error());EXPECT_EQ(docList2.size(), 2UL);FooBarMap& map1 = docList2[0];FooBarMap& map2 = docList2[1];EXPECT_EQ(map1.foo, 10);EXPECT_EQ(map1.bar, -3);EXPECT_EQ(map2.foo, 257);EXPECT_EQ(map2.bar, 0);}}//===----------------------------------------------------------------------===//// Test document tags//===----------------------------------------------------------------------===//struct MyDouble {MyDouble() : value(0.0) { }MyDouble(double x) : value(x) { }double value;};LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyDouble)namespace llvm {namespace yaml {template <>struct MappingTraits<MyDouble> {static void mapping(IO &io, MyDouble &d) {if (io.mapTag("!decimal", true)) {mappingDecimal(io, d);} else if (io.mapTag("!fraction")) {mappingFraction(io, d);}}static void mappingDecimal(IO &io, MyDouble &d) {io.mapRequired("value", d.value);}static void mappingFraction(IO &io, MyDouble &d) {double num, denom;io.mapRequired("numerator", num);io.mapRequired("denominator", denom);// convert fraction to doubled.value = num/denom;}};}}//// Test the reading of two different tagged yaml documents.//TEST(YAMLIO, TestTaggedDocuments) {std::vector<MyDouble> docList;Input yin("--- !decimal\nvalue: 3.0\n""--- !fraction\nnumerator: 9.0\ndenominator: 2\n...\n");yin >> docList;EXPECT_FALSE(yin.error());EXPECT_EQ(docList.size(), 2UL);EXPECT_EQ(docList[0].value, 3.0);EXPECT_EQ(docList[1].value, 4.5);}//// Test writing then reading back tagged documents//TEST(YAMLIO, TestTaggedDocumentsWriteAndRead) {std::string intermediate;{MyDouble a(10.25);MyDouble b(-3.75);std::vector<MyDouble> docList;docList.push_back(a);docList.push_back(b);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << docList;}{Input yin(intermediate);std::vector<MyDouble> docList2;yin >> docList2;EXPECT_FALSE(yin.error());EXPECT_EQ(docList2.size(), 2UL);EXPECT_EQ(docList2[0].value, 10.25);EXPECT_EQ(docList2[1].value, -3.75);}}//===----------------------------------------------------------------------===//// Test mapping validation//===----------------------------------------------------------------------===//struct MyValidation {double value;};LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyValidation)namespace llvm {namespace yaml {template <>struct MappingTraits<MyValidation> {static void mapping(IO &io, MyValidation &d) {io.mapRequired("value", d.value);}static std::string validate(IO &io, MyValidation &d) {if (d.value < 0)return "negative value";return {};}};}}//// Test that validate() is called and complains about the negative value.//TEST(YAMLIO, TestValidatingInput) {std::vector<MyValidation> docList;Input yin("--- \nvalue: 3.0\n""--- \nvalue: -1.0\n...\n",nullptr, suppressErrorMessages);yin >> docList;EXPECT_TRUE(!!yin.error());}//===----------------------------------------------------------------------===//// Test flow mapping//===----------------------------------------------------------------------===//struct FlowFooBar {int foo;int bar;FlowFooBar() : foo(0), bar(0) {}FlowFooBar(int foo, int bar) : foo(foo), bar(bar) {}};typedef std::vector<FlowFooBar> FlowFooBarSequence;LLVM_YAML_IS_SEQUENCE_VECTOR(FlowFooBar)struct FlowFooBarDoc {FlowFooBar attribute;FlowFooBarSequence seq;};namespace llvm {namespace yaml {template <>struct MappingTraits<FlowFooBar> {static void mapping(IO &io, FlowFooBar &fb) {io.mapRequired("foo", fb.foo);io.mapRequired("bar", fb.bar);}static const bool flow = true;};template <>struct MappingTraits<FlowFooBarDoc> {static void mapping(IO &io, FlowFooBarDoc &fb) {io.mapRequired("attribute", fb.attribute);io.mapRequired("seq", fb.seq);}};}}//// Test writing then reading back custom mappings//TEST(YAMLIO, TestReadWriteMyFlowMapping) {std::string intermediate;{FlowFooBarDoc doc;doc.attribute = FlowFooBar(42, 907);doc.seq.push_back(FlowFooBar(1, 2));doc.seq.push_back(FlowFooBar(0, 0));doc.seq.push_back(FlowFooBar(-1, 1024));llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << doc;// Verify that mappings were written in flow styleostr.flush();llvm::StringRef flowOut(intermediate);EXPECT_NE(llvm::StringRef::npos, flowOut.find("{ foo: 42, bar: 907 }"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 1, bar: 2 }"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 0, bar: 0 }"));EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: -1, bar: 1024 }"));}{Input yin(intermediate);FlowFooBarDoc doc2;yin >> doc2;EXPECT_FALSE(yin.error());EXPECT_EQ(doc2.attribute.foo, 42);EXPECT_EQ(doc2.attribute.bar, 907);EXPECT_EQ(doc2.seq.size(), 3UL);EXPECT_EQ(doc2.seq[0].foo, 1);EXPECT_EQ(doc2.seq[0].bar, 2);EXPECT_EQ(doc2.seq[1].foo, 0);EXPECT_EQ(doc2.seq[1].bar, 0);EXPECT_EQ(doc2.seq[2].foo, -1);EXPECT_EQ(doc2.seq[2].bar, 1024);}}//===----------------------------------------------------------------------===//// Test error handling//===----------------------------------------------------------------------===////// Test error handling of unknown enumerated scalar//TEST(YAMLIO, TestColorsReadError) {ColorMap map;Input yin("---\n""c1: blue\n""c2: purple\n""c3: green\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> map;EXPECT_TRUE(!!yin.error());}//// Test error handling of flow sequence with unknown value//TEST(YAMLIO, TestFlagsReadError) {FlagsMap map;Input yin("---\n""f1: [ big ]\n""f2: [ round, hollow ]\n""f3: []\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> map;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in uint8_t type//TEST(YAMLIO, TestReadBuiltInTypesUint8Error) {std::vector<uint8_t> seq;Input yin("---\n""- 255\n""- 0\n""- 257\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in uint16_t type//TEST(YAMLIO, TestReadBuiltInTypesUint16Error) {std::vector<uint16_t> seq;Input yin("---\n""- 65535\n""- 0\n""- 66000\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in uint32_t type//TEST(YAMLIO, TestReadBuiltInTypesUint32Error) {std::vector<uint32_t> seq;Input yin("---\n""- 4000000000\n""- 0\n""- 5000000000\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in uint64_t type//TEST(YAMLIO, TestReadBuiltInTypesUint64Error) {std::vector<uint64_t> seq;Input yin("---\n""- 18446744073709551615\n""- 0\n""- 19446744073709551615\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int8_t type//TEST(YAMLIO, TestReadBuiltInTypesint8OverError) {std::vector<int8_t> seq;Input yin("---\n""- -128\n""- 0\n""- 127\n""- 128\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int8_t type//TEST(YAMLIO, TestReadBuiltInTypesint8UnderError) {std::vector<int8_t> seq;Input yin("---\n""- -128\n""- 0\n""- 127\n""- -129\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int16_t type//TEST(YAMLIO, TestReadBuiltInTypesint16UnderError) {std::vector<int16_t> seq;Input yin("---\n""- 32767\n""- 0\n""- -32768\n""- -32769\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int16_t type//TEST(YAMLIO, TestReadBuiltInTypesint16OverError) {std::vector<int16_t> seq;Input yin("---\n""- 32767\n""- 0\n""- -32768\n""- 32768\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int32_t type//TEST(YAMLIO, TestReadBuiltInTypesint32UnderError) {std::vector<int32_t> seq;Input yin("---\n""- 2147483647\n""- 0\n""- -2147483648\n""- -2147483649\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int32_t type//TEST(YAMLIO, TestReadBuiltInTypesint32OverError) {std::vector<int32_t> seq;Input yin("---\n""- 2147483647\n""- 0\n""- -2147483648\n""- 2147483649\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int64_t type//TEST(YAMLIO, TestReadBuiltInTypesint64UnderError) {std::vector<int64_t> seq;Input yin("---\n""- -9223372036854775808\n""- 0\n""- 9223372036854775807\n""- -9223372036854775809\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in int64_t type//TEST(YAMLIO, TestReadBuiltInTypesint64OverError) {std::vector<int64_t> seq;Input yin("---\n""- -9223372036854775808\n""- 0\n""- 9223372036854775807\n""- 9223372036854775809\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in float type//TEST(YAMLIO, TestReadBuiltInTypesFloatError) {std::vector<float> seq;Input yin("---\n""- 0.0\n""- 1000.1\n""- -123.456\n""- 1.2.3\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in float type//TEST(YAMLIO, TestReadBuiltInTypesDoubleError) {std::vector<double> seq;Input yin("---\n""- 0.0\n""- 1000.1\n""- -123.456\n""- 1.2.3\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());}//// Test error handling reading built-in Hex8 type//TEST(YAMLIO, TestReadBuiltInTypesHex8Error) {std::vector<Hex8> seq;Input yin("---\n""- 0x12\n""- 0xFE\n""- 0x123\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());std::vector<Hex8> seq2;Input yin2("---\n""[ 0x12, 0xFE, 0x123 ]\n""...\n",/*Ctxt=*/nullptr, suppressErrorMessages);yin2 >> seq2;EXPECT_TRUE(!!yin2.error());EXPECT_EQ(seq.size(), 3u);EXPECT_EQ(seq.size(), seq2.size());for (size_t i = 0; i < seq.size(); ++i)EXPECT_EQ(seq[i], seq2[i]);}//// Test error handling reading built-in Hex16 type//TEST(YAMLIO, TestReadBuiltInTypesHex16Error) {std::vector<Hex16> seq;Input yin("---\n""- 0x0012\n""- 0xFEFF\n""- 0x12345\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());std::vector<Hex16> seq2;Input yin2("---\n""[ 0x0012, 0xFEFF, 0x12345 ]\n""...\n",/*Ctxt=*/nullptr, suppressErrorMessages);yin2 >> seq2;EXPECT_TRUE(!!yin2.error());EXPECT_EQ(seq.size(), 3u);EXPECT_EQ(seq.size(), seq2.size());for (size_t i = 0; i < seq.size(); ++i)EXPECT_EQ(seq[i], seq2[i]);}//// Test error handling reading built-in Hex32 type//TEST(YAMLIO, TestReadBuiltInTypesHex32Error) {std::vector<Hex32> seq;Input yin("---\n""- 0x0012\n""- 0xFEFF0000\n""- 0x1234556789\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());std::vector<Hex32> seq2;Input yin2("---\n""[ 0x0012, 0xFEFF0000, 0x1234556789 ]\n""...\n",/*Ctxt=*/nullptr, suppressErrorMessages);yin2 >> seq2;EXPECT_TRUE(!!yin2.error());EXPECT_EQ(seq.size(), 3u);EXPECT_EQ(seq.size(), seq2.size());for (size_t i = 0; i < seq.size(); ++i)EXPECT_EQ(seq[i], seq2[i]);}//// Test error handling reading built-in Hex64 type//TEST(YAMLIO, TestReadBuiltInTypesHex64Error) {std::vector<Hex64> seq;Input yin("---\n""- 0x0012\n""- 0xFFEEDDCCBBAA9988\n""- 0x12345567890ABCDEF0\n""...\n",/*Ctxt=*/nullptr,suppressErrorMessages);yin >> seq;EXPECT_TRUE(!!yin.error());std::vector<Hex64> seq2;Input yin2("---\n""[ 0x0012, 0xFFEEDDCCBBAA9988, 0x12345567890ABCDEF0 ]\n""...\n",/*Ctxt=*/nullptr, suppressErrorMessages);yin2 >> seq2;EXPECT_TRUE(!!yin2.error());EXPECT_EQ(seq.size(), 3u);EXPECT_EQ(seq.size(), seq2.size());for (size_t i = 0; i < seq.size(); ++i)EXPECT_EQ(seq[i], seq2[i]);}TEST(YAMLIO, TestMalformedMapFailsGracefully) {FooBar doc;{// We pass the suppressErrorMessages handler to handle the error// message generated in the constructor of Input.Input yin("{foo:3, bar: 5}", /*Ctxt=*/nullptr, suppressErrorMessages);yin >> doc;EXPECT_TRUE(!!yin.error());}{Input yin("---\nfoo:3\nbar: 5\n...\n", /*Ctxt=*/nullptr, suppressErrorMessages);yin >> doc;EXPECT_TRUE(!!yin.error());}}struct OptionalTest {std::vector<int> Numbers;};struct OptionalTestSeq {std::vector<OptionalTest> Tests;};LLVM_YAML_IS_SEQUENCE_VECTOR(OptionalTest)namespace llvm {namespace yaml {template <>struct MappingTraits<OptionalTest> {static void mapping(IO& IO, OptionalTest &OT) {IO.mapOptional("Numbers", OT.Numbers);}};template <>struct MappingTraits<OptionalTestSeq> {static void mapping(IO &IO, OptionalTestSeq &OTS) {IO.mapOptional("Tests", OTS.Tests);}};}}TEST(YAMLIO, SequenceElideTest) {// Test that writing out a purely optional structure with its fields set to// default followed by other data is properly read back in.OptionalTestSeq Seq;OptionalTest One, Two, Three, Four;int N[] = {1, 2, 3};Three.Numbers.assign(N, N + 3);Seq.Tests.push_back(One);Seq.Tests.push_back(Two);Seq.Tests.push_back(Three);Seq.Tests.push_back(Four);std::string intermediate;{llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << Seq;}Input yin(intermediate);OptionalTestSeq Seq2;yin >> Seq2;EXPECT_FALSE(yin.error());EXPECT_EQ(4UL, Seq2.Tests.size());EXPECT_TRUE(Seq2.Tests[0].Numbers.empty());EXPECT_TRUE(Seq2.Tests[1].Numbers.empty());EXPECT_EQ(1, Seq2.Tests[2].Numbers[0]);EXPECT_EQ(2, Seq2.Tests[2].Numbers[1]);EXPECT_EQ(3, Seq2.Tests[2].Numbers[2]);EXPECT_TRUE(Seq2.Tests[3].Numbers.empty());}TEST(YAMLIO, TestEmptyStringFailsForMapWithRequiredFields) {FooBar doc;Input yin("");yin >> doc;EXPECT_TRUE(!!yin.error());}TEST(YAMLIO, TestEmptyStringSucceedsForMapWithOptionalFields) {OptionalTest doc;Input yin("");yin >> doc;EXPECT_FALSE(yin.error());}TEST(YAMLIO, TestEmptyStringSucceedsForSequence) {std::vector<uint8_t> seq;Input yin("", /*Ctxt=*/nullptr, suppressErrorMessages);yin >> seq;EXPECT_FALSE(yin.error());EXPECT_TRUE(seq.empty());}struct FlowMap {llvm::StringRef str1, str2, str3;FlowMap(llvm::StringRef str1, llvm::StringRef str2, llvm::StringRef str3): str1(str1), str2(str2), str3(str3) {}};struct FlowSeq {llvm::StringRef str;FlowSeq(llvm::StringRef S) : str(S) {}FlowSeq() = default;};namespace llvm {namespace yaml {template <>struct MappingTraits<FlowMap> {static void mapping(IO &io, FlowMap &fm) {io.mapRequired("str1", fm.str1);io.mapRequired("str2", fm.str2);io.mapRequired("str3", fm.str3);}static const bool flow = true;};template <>struct ScalarTraits<FlowSeq> {static void output(const FlowSeq &value, void*, llvm::raw_ostream &out) {out << value.str;}static StringRef input(StringRef scalar, void*, FlowSeq &value) {value.str = scalar;return "";}static QuotingType mustQuote(StringRef S) { return QuotingType::None; }};}}LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FlowSeq)TEST(YAMLIO, TestWrapFlow) {std::string out;llvm::raw_string_ostream ostr(out);FlowMap Map("This is str1", "This is str2", "This is str3");std::vector<FlowSeq> Seq;Seq.emplace_back("This is str1");Seq.emplace_back("This is str2");Seq.emplace_back("This is str3");{// 20 is just bellow the total length of the first mapping field.// We should wreap at every element.Output yout(ostr, nullptr, 15);yout << Map;ostr.flush();EXPECT_EQ(out,"---\n""{ str1: This is str1, \n"" str2: This is str2, \n"" str3: This is str3 }\n""...\n");out.clear();yout << Seq;ostr.flush();EXPECT_EQ(out,"---\n""[ This is str1, \n"" This is str2, \n"" This is str3 ]\n""...\n");out.clear();}{// 25 will allow the second field to be output on the first line.Output yout(ostr, nullptr, 25);yout << Map;ostr.flush();EXPECT_EQ(out,"---\n""{ str1: This is str1, str2: This is str2, \n"" str3: This is str3 }\n""...\n");out.clear();yout << Seq;ostr.flush();EXPECT_EQ(out,"---\n""[ This is str1, This is str2, \n"" This is str3 ]\n""...\n");out.clear();}{// 0 means no wrapping.Output yout(ostr, nullptr, 0);yout << Map;ostr.flush();EXPECT_EQ(out,"---\n""{ str1: This is str1, str2: This is str2, str3: This is str3 }\n""...\n");out.clear();yout << Seq;ostr.flush();EXPECT_EQ(out,"---\n""[ This is str1, This is str2, This is str3 ]\n""...\n");out.clear();}}struct MappingContext {int A = 0;};struct SimpleMap {int B = 0;int C = 0;};struct NestedMap {NestedMap(MappingContext &Context) : Context(Context) {}SimpleMap Simple;MappingContext &Context;};namespace llvm {namespace yaml {template <> struct MappingContextTraits<SimpleMap, MappingContext> {static void mapping(IO &io, SimpleMap &sm, MappingContext &Context) {io.mapRequired("B", sm.B);io.mapRequired("C", sm.C);++Context.A;io.mapRequired("Context", Context.A);}};template <> struct MappingTraits<NestedMap> {static void mapping(IO &io, NestedMap &nm) {io.mapRequired("Simple", nm.Simple, nm.Context);}};}}TEST(YAMLIO, TestMapWithContext) {MappingContext Context;NestedMap Nested(Context);std::string out;llvm::raw_string_ostream ostr(out);Output yout(ostr, nullptr, 15);yout << Nested;ostr.flush();EXPECT_EQ(1, Context.A);EXPECT_EQ("---\n""Simple:\n"" B: 0\n"" C: 0\n"" Context: 1\n""...\n",out);out.clear();Nested.Simple.B = 2;Nested.Simple.C = 3;yout << Nested;ostr.flush();EXPECT_EQ(2, Context.A);EXPECT_EQ("---\n""Simple:\n"" B: 2\n"" C: 3\n"" Context: 2\n""...\n",out);out.clear();}LLVM_YAML_IS_STRING_MAP(int)TEST(YAMLIO, TestCustomMapping) {std::map<std::string, int> x;std::string out;llvm::raw_string_ostream ostr(out);Output xout(ostr, nullptr, 0);xout << x;ostr.flush();EXPECT_EQ("---\n""{}\n""...\n",out);x["foo"] = 1;x["bar"] = 2;out.clear();xout << x;ostr.flush();EXPECT_EQ("---\n""bar: 2\n""foo: 1\n""...\n",out);Input yin(out);std::map<std::string, int> y;yin >> y;EXPECT_EQ(2ul, y.size());EXPECT_EQ(1, y["foo"]);EXPECT_EQ(2, y["bar"]);}LLVM_YAML_IS_STRING_MAP(FooBar)TEST(YAMLIO, TestCustomMappingStruct) {std::map<std::string, FooBar> x;x["foo"].foo = 1;x["foo"].bar = 2;x["bar"].foo = 3;x["bar"].bar = 4;std::string out;llvm::raw_string_ostream ostr(out);Output xout(ostr, nullptr, 0);xout << x;ostr.flush();EXPECT_EQ("---\n""bar:\n"" foo: 3\n"" bar: 4\n""foo:\n"" foo: 1\n"" bar: 2\n""...\n",out);Input yin(out);std::map<std::string, FooBar> y;yin >> y;EXPECT_EQ(2ul, y.size());EXPECT_EQ(1, y["foo"].foo);EXPECT_EQ(2, y["foo"].bar);EXPECT_EQ(3, y["bar"].foo);EXPECT_EQ(4, y["bar"].bar);}struct FooBarMapMap {std::map<std::string, FooBar> fbm;};namespace llvm {namespace yaml {template <> struct MappingTraits<FooBarMapMap> {static void mapping(IO &io, FooBarMapMap &x) {io.mapRequired("fbm", x.fbm);}};}}TEST(YAMLIO, TestEmptyMapWrite) {FooBarMapMap cont;std::string str;llvm::raw_string_ostream OS(str);Output yout(OS);yout << cont;EXPECT_EQ(OS.str(), "---\nfbm: {}\n...\n");}TEST(YAMLIO, TestEmptySequenceWrite) {{FooBarContainer cont;std::string str;llvm::raw_string_ostream OS(str);Output yout(OS);yout << cont;EXPECT_EQ(OS.str(), "---\nfbs: []\n...\n");}{FooBarSequence seq;std::string str;llvm::raw_string_ostream OS(str);Output yout(OS);yout << seq;EXPECT_EQ(OS.str(), "---\n[]\n...\n");}}static void TestEscaped(llvm::StringRef Input, llvm::StringRef Expected) {std::string out;llvm::raw_string_ostream ostr(out);Output xout(ostr, nullptr, 0);llvm::yaml::EmptyContext Ctx;yamlize(xout, Input, true, Ctx);ostr.flush();// Make a separate StringRef so we get nice byte-by-byte output.llvm::StringRef Got(out);EXPECT_EQ(Expected, Got);}TEST(YAMLIO, TestEscaped) {// Single quoteTestEscaped("@abc@", "'@abc@'");// No quoteTestEscaped("abc", "abc");// Forward slash quotedTestEscaped("abc/", "'abc/'");// Double quote non-printableTestEscaped("\01@abc@", "\"\\x01@abc@\"");// Double quote inside single quoteTestEscaped("abc\"fdf", "'abc\"fdf'");// Double quote inside double quoteTestEscaped("\01bc\"fdf", "\"\\x01bc\\\"fdf\"");// Single quote inside single quoteTestEscaped("abc'fdf", "'abc''fdf'");// UTF8TestEscaped("/*параметр*/", "\"/*параметр*/\"");// UTF8 with single quote inside double quoteTestEscaped("parameter 'параметр' is unused","\"parameter 'параметр' is unused\"");// String with embedded non-printable multibyte UTF-8 sequence (U+200B// zero-width space). The thing to test here is that we emit a// unicode-scalar level escape like \uNNNN (at the YAML level), and don't// just pass the UTF-8 byte sequence through as with quoted printables.{const unsigned char foobar[10] = {'f', 'o', 'o',0xE2, 0x80, 0x8B, // UTF-8 of U+200B'b', 'a', 'r',0x0};TestEscaped((char const *)foobar, "\"foo\\u200Bbar\"");}}TEST(YAMLIO, Numeric) {EXPECT_TRUE(isNumeric(".inf"));EXPECT_TRUE(isNumeric(".INF"));EXPECT_TRUE(isNumeric(".Inf"));EXPECT_TRUE(isNumeric("-.inf"));EXPECT_TRUE(isNumeric("+.inf"));EXPECT_TRUE(isNumeric(".nan"));EXPECT_TRUE(isNumeric(".NaN"));EXPECT_TRUE(isNumeric(".NAN"));EXPECT_TRUE(isNumeric("0"));EXPECT_TRUE(isNumeric("0."));EXPECT_TRUE(isNumeric("0.0"));EXPECT_TRUE(isNumeric("-0.0"));EXPECT_TRUE(isNumeric("+0.0"));EXPECT_TRUE(isNumeric("12345"));EXPECT_TRUE(isNumeric("012345"));EXPECT_TRUE(isNumeric("+12.0"));EXPECT_TRUE(isNumeric(".5"));EXPECT_TRUE(isNumeric("+.5"));EXPECT_TRUE(isNumeric("-1.0"));EXPECT_TRUE(isNumeric("2.3e4"));EXPECT_TRUE(isNumeric("-2E+05"));EXPECT_TRUE(isNumeric("+12e03"));EXPECT_TRUE(isNumeric("6.8523015e+5"));EXPECT_TRUE(isNumeric("1.e+1"));EXPECT_TRUE(isNumeric(".0e+1"));EXPECT_TRUE(isNumeric("0x2aF3"));EXPECT_TRUE(isNumeric("0o01234567"));EXPECT_FALSE(isNumeric("not a number"));EXPECT_FALSE(isNumeric("."));EXPECT_FALSE(isNumeric(".e+1"));EXPECT_FALSE(isNumeric(".1e"));EXPECT_FALSE(isNumeric(".1e+"));EXPECT_FALSE(isNumeric(".1e++1"));EXPECT_FALSE(isNumeric("ABCD"));EXPECT_FALSE(isNumeric("+0x2AF3"));EXPECT_FALSE(isNumeric("-0x2AF3"));EXPECT_FALSE(isNumeric("0x2AF3Z"));EXPECT_FALSE(isNumeric("0o012345678"));EXPECT_FALSE(isNumeric("0xZ"));EXPECT_FALSE(isNumeric("-0o012345678"));EXPECT_FALSE(isNumeric("000003A8229434B839616A25C16B0291F77A438B"));EXPECT_FALSE(isNumeric(""));EXPECT_FALSE(isNumeric("."));EXPECT_FALSE(isNumeric(".e+1"));EXPECT_FALSE(isNumeric(".e+"));EXPECT_FALSE(isNumeric(".e"));EXPECT_FALSE(isNumeric("e1"));// Deprecated formats: as for YAML 1.2 specification, the following are not// valid numbers anymore://// * Sexagecimal numbers// * Decimal numbers with comma s the delimiter// * "inf", "nan" without '.' prefixEXPECT_FALSE(isNumeric("3:25:45"));EXPECT_FALSE(isNumeric("+12,345"));EXPECT_FALSE(isNumeric("-inf"));EXPECT_FALSE(isNumeric("1,230.15"));}//===----------------------------------------------------------------------===//// Test PolymorphicTraits and TaggedScalarTraits//===----------------------------------------------------------------------===//struct Poly {enum NodeKind {NK_Scalar,NK_Seq,NK_Map,} Kind;Poly(NodeKind Kind) : Kind(Kind) {}virtual ~Poly() = default;NodeKind getKind() const { return Kind; }};struct Scalar : Poly {enum ScalarKind {SK_Unknown,SK_Double,SK_Bool,} SKind;union {double DoubleValue;bool BoolValue;};Scalar() : Poly(NK_Scalar), SKind(SK_Unknown) {}Scalar(double DoubleValue): Poly(NK_Scalar), SKind(SK_Double), DoubleValue(DoubleValue) {}Scalar(bool BoolValue): Poly(NK_Scalar), SKind(SK_Bool), BoolValue(BoolValue) {}static bool classof(const Poly *N) { return N->getKind() == NK_Scalar; }};struct Seq : Poly, std::vector<std::unique_ptr<Poly>> {Seq() : Poly(NK_Seq) {}static bool classof(const Poly *N) { return N->getKind() == NK_Seq; }};struct Map : Poly, llvm::StringMap<std::unique_ptr<Poly>> {Map() : Poly(NK_Map) {}static bool classof(const Poly *N) { return N->getKind() == NK_Map; }};namespace llvm {namespace yaml {template <> struct PolymorphicTraits<std::unique_ptr<Poly>> {static NodeKind getKind(const std::unique_ptr<Poly> &N) {if (isa<Scalar>(*N))return NodeKind::Scalar;if (isa<Seq>(*N))return NodeKind::Sequence;if (isa<Map>(*N))return NodeKind::Map;llvm_unreachable("unsupported node type");}static Scalar &getAsScalar(std::unique_ptr<Poly> &N) {if (!N || !isa<Scalar>(*N))N = std::make_unique<Scalar>();return *cast<Scalar>(N.get());}static Seq &getAsSequence(std::unique_ptr<Poly> &N) {if (!N || !isa<Seq>(*N))N = std::make_unique<Seq>();return *cast<Seq>(N.get());}static Map &getAsMap(std::unique_ptr<Poly> &N) {if (!N || !isa<Map>(*N))N = std::make_unique<Map>();return *cast<Map>(N.get());}};template <> struct TaggedScalarTraits<Scalar> {static void output(const Scalar &S, void *Ctxt, raw_ostream &ScalarOS,raw_ostream &TagOS) {switch (S.SKind) {case Scalar::SK_Unknown:report_fatal_error("output unknown scalar");break;case Scalar::SK_Double:TagOS << "!double";ScalarTraits<double>::output(S.DoubleValue, Ctxt, ScalarOS);break;case Scalar::SK_Bool:TagOS << "!bool";ScalarTraits<bool>::output(S.BoolValue, Ctxt, ScalarOS);break;}}static StringRef input(StringRef ScalarStr, StringRef Tag, void *Ctxt,Scalar &S) {S.SKind = StringSwitch<Scalar::ScalarKind>(Tag).Case("!double", Scalar::SK_Double).Case("!bool", Scalar::SK_Bool).Default(Scalar::SK_Unknown);switch (S.SKind) {case Scalar::SK_Unknown:return StringRef("unknown scalar tag");case Scalar::SK_Double:return ScalarTraits<double>::input(ScalarStr, Ctxt, S.DoubleValue);case Scalar::SK_Bool:return ScalarTraits<bool>::input(ScalarStr, Ctxt, S.BoolValue);}llvm_unreachable("unknown scalar kind");}static QuotingType mustQuote(const Scalar &S, StringRef Str) {switch (S.SKind) {case Scalar::SK_Unknown:report_fatal_error("quote unknown scalar");case Scalar::SK_Double:return ScalarTraits<double>::mustQuote(Str);case Scalar::SK_Bool:return ScalarTraits<bool>::mustQuote(Str);}llvm_unreachable("unknown scalar kind");}};template <> struct CustomMappingTraits<Map> {static void inputOne(IO &IO, StringRef Key, Map &M) {IO.mapRequired(Key.str().c_str(), M[Key]);}static void output(IO &IO, Map &M) {for (auto &N : M)IO.mapRequired(N.getKey().str().c_str(), N.getValue());}};template <> struct SequenceTraits<Seq> {static size_t size(IO &IO, Seq &A) { return A.size(); }static std::unique_ptr<Poly> &element(IO &IO, Seq &A, size_t Index) {if (Index >= A.size())A.resize(Index + 1);return A[Index];}};} // namespace yaml} // namespace llvmTEST(YAMLIO, TestReadWritePolymorphicScalar) {std::string intermediate;std::unique_ptr<Poly> node = std::make_unique<Scalar>(true);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGEXPECT_DEATH(yout << node, "plain scalar documents are not supported");#endif#endif}TEST(YAMLIO, TestReadWritePolymorphicSeq) {std::string intermediate;{auto seq = std::make_unique<Seq>();seq->push_back(std::make_unique<Scalar>(true));seq->push_back(std::make_unique<Scalar>(1.0));auto node = llvm::unique_dyn_cast<Poly>(seq);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << node;}{Input yin(intermediate);std::unique_ptr<Poly> node;yin >> node;EXPECT_FALSE(yin.error());auto seq = llvm::dyn_cast<Seq>(node.get());ASSERT_TRUE(seq);ASSERT_EQ(seq->size(), 2u);auto first = llvm::dyn_cast<Scalar>((*seq)[0].get());ASSERT_TRUE(first);EXPECT_EQ(first->SKind, Scalar::SK_Bool);EXPECT_TRUE(first->BoolValue);auto second = llvm::dyn_cast<Scalar>((*seq)[1].get());ASSERT_TRUE(second);EXPECT_EQ(second->SKind, Scalar::SK_Double);EXPECT_EQ(second->DoubleValue, 1.0);}}TEST(YAMLIO, TestReadWritePolymorphicMap) {std::string intermediate;{auto map = std::make_unique<Map>();(*map)["foo"] = std::make_unique<Scalar>(false);(*map)["bar"] = std::make_unique<Scalar>(2.0);std::unique_ptr<Poly> node = llvm::unique_dyn_cast<Poly>(map);llvm::raw_string_ostream ostr(intermediate);Output yout(ostr);yout << node;}{Input yin(intermediate);std::unique_ptr<Poly> node;yin >> node;EXPECT_FALSE(yin.error());auto map = llvm::dyn_cast<Map>(node.get());ASSERT_TRUE(map);auto foo = llvm::dyn_cast<Scalar>((*map)["foo"].get());ASSERT_TRUE(foo);EXPECT_EQ(foo->SKind, Scalar::SK_Bool);EXPECT_FALSE(foo->BoolValue);auto bar = llvm::dyn_cast<Scalar>((*map)["bar"].get());ASSERT_TRUE(bar);EXPECT_EQ(bar->SKind, Scalar::SK_Double);EXPECT_EQ(bar->DoubleValue, 2.0);}}TEST(YAMLIO, TestAnchorMapError) {Input yin("& & &: ");yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestFlowSequenceTokenErrors) {Input yin(",");EXPECT_FALSE(yin.setCurrentDocument());EXPECT_TRUE(yin.error());Input yin2("]");EXPECT_FALSE(yin2.setCurrentDocument());EXPECT_TRUE(yin2.error());Input yin3("}");EXPECT_FALSE(yin3.setCurrentDocument());EXPECT_TRUE(yin3.error());}TEST(YAMLIO, TestDirectiveMappingNoValue) {Input yin("%YAML\n{5:");EXPECT_FALSE(yin.setCurrentDocument());EXPECT_TRUE(yin.error());Input yin2("%TAG\n'\x98!< :\n");yin2.setCurrentDocument();EXPECT_TRUE(yin2.error());}TEST(YAMLIO, TestUnescapeInfiniteLoop) {Input yin("\"\\u\\^#\\\\\"");yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerUnexpectedCharacter) {Input yin("!<$\x9F.");EXPECT_FALSE(yin.setCurrentDocument());EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestUnknownDirective) {Input yin("%");EXPECT_FALSE(yin.setCurrentDocument());EXPECT_TRUE(yin.error());Input yin2("%)");EXPECT_FALSE(yin2.setCurrentDocument());EXPECT_TRUE(yin2.error());}TEST(YAMLIO, TestEmptyAlias) {Input yin("&");EXPECT_FALSE(yin.setCurrentDocument());EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestEmptyAnchor) {Input yin("*");EXPECT_FALSE(yin.setCurrentDocument());}TEST(YAMLIO, TestScannerNoNullEmpty) {std::vector<char> str{};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_FALSE(yin.error());}TEST(YAMLIO, TestScannerNoNullSequenceOfNull) {std::vector<char> str{'-'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_FALSE(yin.error());}TEST(YAMLIO, TestScannerNoNullSimpleSequence) {std::vector<char> str{'-', ' ', 'a'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_FALSE(yin.error());}TEST(YAMLIO, TestScannerNoNullUnbalancedMap) {std::vector<char> str{'{'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerNoNullEmptyMap) {std::vector<char> str{'{', '}'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_FALSE(yin.error());}TEST(YAMLIO, TestScannerNoNullUnbalancedSequence) {std::vector<char> str{'['};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerNoNullEmptySequence) {std::vector<char> str{'[', ']'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_FALSE(yin.error());}TEST(YAMLIO, TestScannerNoNullScalarUnbalancedDoubleQuote) {std::vector<char> str{'"'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerNoNullScalarUnbalancedSingleQuote) {std::vector<char> str{'\''};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerNoNullEmptyAlias) {std::vector<char> str{'&'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerNoNullEmptyAnchor) {std::vector<char> str{'*'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerNoNullDecodeInvalidUTF8) {std::vector<char> str{'\xef'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}TEST(YAMLIO, TestScannerNoNullScanPlainScalarInFlow) {std::vector<char> str{'{', 'a', ':'};Input yin(llvm::StringRef(str.data(), str.size()));yin.setCurrentDocument();EXPECT_TRUE(yin.error());}
//===- WithColorTest.cpp --------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/WithColor.h"#include "gtest/gtest.h"using namespace llvm;TEST(WithColorTest, ColorMode) {{std::string S;llvm::raw_string_ostream OS(S);OS.enable_colors(true);WithColor(OS, HighlightColor::Error, ColorMode::Disable) << "test";EXPECT_EQ("test", OS.str());}{std::string S;llvm::raw_string_ostream OS(S);OS.enable_colors(true);WithColor(OS, HighlightColor::Error, ColorMode::Auto) << "test";EXPECT_EQ("test", OS.str());}#ifdef LLVM_ON_UNIX{std::string S;llvm::raw_string_ostream OS(S);OS.enable_colors(true);WithColor(OS, HighlightColor::Error, ColorMode::Enable) << "test";EXPECT_EQ("\x1B[0;1;31mtest\x1B[0m", OS.str());}#endif}
//===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/VirtualFileSystem.h"#include "llvm/ADT/Triple.h"#include "llvm/Config/llvm-config.h"#include "llvm/Support/Errc.h"#include "llvm/Support/Host.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/Path.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <map>#include <string>using namespace llvm;using llvm::sys::fs::UniqueID;using llvm::unittest::TempDir;using llvm::unittest::TempFile;using llvm::unittest::TempLink;using testing::ElementsAre;using testing::Pair;using testing::UnorderedElementsAre;namespace {struct DummyFile : public vfs::File {vfs::Status S;explicit DummyFile(vfs::Status S) : S(S) {}llvm::ErrorOr<vfs::Status> status() override { return S; }llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,bool IsVolatile) override {llvm_unreachable("unimplemented");}std::error_code close() override { return std::error_code(); }};class DummyFileSystem : public vfs::FileSystem {int FSID; // used to produce UniqueIDsint FileID; // used to produce UniqueIDsstd::string WorkingDirectory;std::map<std::string, vfs::Status> FilesAndDirs;typedef std::map<std::string, vfs::Status>::const_iterator const_iterator;static int getNextFSID() {static int Count = 0;return Count++;}public:DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}ErrorOr<vfs::Status> status(const Twine &Path) override {auto I = findEntry(Path);if (I == FilesAndDirs.end())return make_error_code(llvm::errc::no_such_file_or_directory);return I->second;}ErrorOr<std::unique_ptr<vfs::File>>openFileForRead(const Twine &Path) override {auto S = status(Path);if (S)return std::unique_ptr<vfs::File>(new DummyFile{*S});return S.getError();}llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {return WorkingDirectory;}std::error_code setCurrentWorkingDirectory(const Twine &Path) override {WorkingDirectory = Path.str();return std::error_code();}// Map any symlink to "/symlink".std::error_code getRealPath(const Twine &Path,SmallVectorImpl<char> &Output) const override {auto I = findEntry(Path);if (I == FilesAndDirs.end())return make_error_code(llvm::errc::no_such_file_or_directory);if (I->second.isSymlink()) {Output.clear();Twine("/symlink").toVector(Output);return std::error_code();}Output.clear();Path.toVector(Output);return std::error_code();}struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {std::map<std::string, vfs::Status> &FilesAndDirs;std::map<std::string, vfs::Status>::iterator I;std::string Path;bool isInPath(StringRef S) {if (Path.size() < S.size() && S.find(Path) == 0) {auto LastSep = S.find_last_of('/');if (LastSep == Path.size() || LastSep == Path.size() - 1)return true;}return false;}DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,const Twine &_Path): FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),Path(_Path.str()) {for (; I != FilesAndDirs.end(); ++I) {if (isInPath(I->first)) {CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),I->second.getType());break;}}}std::error_code increment() override {++I;for (; I != FilesAndDirs.end(); ++I) {if (isInPath(I->first)) {CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),I->second.getType());break;}}if (I == FilesAndDirs.end())CurrentEntry = vfs::directory_entry();return std::error_code();}};vfs::directory_iterator dir_begin(const Twine &Dir,std::error_code &EC) override {return vfs::directory_iterator(std::make_shared<DirIterImpl>(FilesAndDirs, Dir));}void addEntry(StringRef Path, const vfs::Status &Status) {FilesAndDirs[std::string(Path)] = Status;}const_iterator findEntry(const Twine &Path) const {SmallString<128> P;Path.toVector(P);std::error_code EC = makeAbsolute(P);assert(!EC);(void)EC;return FilesAndDirs.find(std::string(P.str()));}void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {vfs::Status S(Path, UniqueID(FSID, FileID++),std::chrono::system_clock::now(), 0, 0, 1024,sys::fs::file_type::regular_file, Perms);addEntry(Path, S);}void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {vfs::Status S(Path, UniqueID(FSID, FileID++),std::chrono::system_clock::now(), 0, 0, 0,sys::fs::file_type::directory_file, Perms);addEntry(Path, S);}void addSymlink(StringRef Path) {vfs::Status S(Path, UniqueID(FSID, FileID++),std::chrono::system_clock::now(), 0, 0, 0,sys::fs::file_type::symlink_file, sys::fs::all_all);addEntry(Path, S);}protected:void printImpl(raw_ostream &OS, PrintType Type,unsigned IndentLevel) const override {printIndent(OS, IndentLevel);OS << "DummyFileSystem (";switch (Type) {case vfs::FileSystem::PrintType::Summary:OS << "Summary";break;case vfs::FileSystem::PrintType::Contents:OS << "Contents";break;case vfs::FileSystem::PrintType::RecursiveContents:OS << "RecursiveContents";break;}OS << ")\n";}};class ErrorDummyFileSystem : public DummyFileSystem {std::error_code setCurrentWorkingDirectory(const Twine &Path) override {return llvm::errc::no_such_file_or_directory;}};/// Replace back-slashes by front-slashes.std::string getPosixPath(const Twine &S) {SmallString<128> Result;llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix);return std::string(Result.str());}} // end anonymous namespaceTEST(VirtualFileSystemTest, StatusQueries) {IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());ErrorOr<vfs::Status> Status((std::error_code()));D->addRegularFile("/foo");Status = D->status("/foo");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->isStatusKnown());EXPECT_FALSE(Status->isDirectory());EXPECT_TRUE(Status->isRegularFile());EXPECT_FALSE(Status->isSymlink());EXPECT_FALSE(Status->isOther());EXPECT_TRUE(Status->exists());D->addDirectory("/bar");Status = D->status("/bar");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->isStatusKnown());EXPECT_TRUE(Status->isDirectory());EXPECT_FALSE(Status->isRegularFile());EXPECT_FALSE(Status->isSymlink());EXPECT_FALSE(Status->isOther());EXPECT_TRUE(Status->exists());D->addSymlink("/baz");Status = D->status("/baz");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->isStatusKnown());EXPECT_FALSE(Status->isDirectory());EXPECT_FALSE(Status->isRegularFile());EXPECT_TRUE(Status->isSymlink());EXPECT_FALSE(Status->isOther());EXPECT_TRUE(Status->exists());EXPECT_TRUE(Status->equivalent(*Status));ErrorOr<vfs::Status> Status2 = D->status("/foo");ASSERT_FALSE(Status2.getError());EXPECT_FALSE(Status->equivalent(*Status2));}TEST(VirtualFileSystemTest, BaseOnlyOverlay) {IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());ErrorOr<vfs::Status> Status((std::error_code()));EXPECT_FALSE(Status = D->status("/foo"));IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));EXPECT_FALSE(Status = O->status("/foo"));D->addRegularFile("/foo");Status = D->status("/foo");EXPECT_FALSE(Status.getError());ErrorOr<vfs::Status> Status2((std::error_code()));Status2 = O->status("/foo");EXPECT_FALSE(Status2.getError());EXPECT_TRUE(Status->equivalent(*Status2));}TEST(VirtualFileSystemTest, GetRealPathInOverlay) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addRegularFile("/foo");Lower->addSymlink("/lower_link");IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Upper);// Regular file.SmallString<16> RealPath;EXPECT_FALSE(O->getRealPath("/foo", RealPath));EXPECT_EQ(RealPath.str(), "/foo");// Expect no error getting real path for symlink in lower overlay.EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));EXPECT_EQ(RealPath.str(), "/symlink");// Try a non-existing link.EXPECT_EQ(O->getRealPath("/upper_link", RealPath),errc::no_such_file_or_directory);// Add a new symlink in upper.Upper->addSymlink("/upper_link");EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));EXPECT_EQ(RealPath.str(), "/symlink");}TEST(VirtualFileSystemTest, OverlayFiles) {IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Base));O->pushOverlay(Middle);O->pushOverlay(Top);ErrorOr<vfs::Status> Status1((std::error_code())),Status2((std::error_code())), Status3((std::error_code())),StatusB((std::error_code())), StatusM((std::error_code())),StatusT((std::error_code()));Base->addRegularFile("/foo");StatusB = Base->status("/foo");ASSERT_FALSE(StatusB.getError());Status1 = O->status("/foo");ASSERT_FALSE(Status1.getError());Middle->addRegularFile("/foo");StatusM = Middle->status("/foo");ASSERT_FALSE(StatusM.getError());Status2 = O->status("/foo");ASSERT_FALSE(Status2.getError());Top->addRegularFile("/foo");StatusT = Top->status("/foo");ASSERT_FALSE(StatusT.getError());Status3 = O->status("/foo");ASSERT_FALSE(Status3.getError());EXPECT_TRUE(Status1->equivalent(*StatusB));EXPECT_TRUE(Status2->equivalent(*StatusM));EXPECT_TRUE(Status3->equivalent(*StatusT));EXPECT_FALSE(Status1->equivalent(*Status2));EXPECT_FALSE(Status2->equivalent(*Status3));EXPECT_FALSE(Status1->equivalent(*Status3));}TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Upper);Lower->addDirectory("/lower-only");Upper->addDirectory("/upper-only");// non-merged paths should be the sameErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");ASSERT_FALSE(Status1.getError());ErrorOr<vfs::Status> Status2 = O->status("/lower-only");ASSERT_FALSE(Status2.getError());EXPECT_TRUE(Status1->equivalent(*Status2));Status1 = Upper->status("/upper-only");ASSERT_FALSE(Status1.getError());Status2 = O->status("/upper-only");ASSERT_FALSE(Status2.getError());EXPECT_TRUE(Status1->equivalent(*Status2));}TEST(VirtualFileSystemTest, MergedDirPermissions) {// merged directories get the permissions of the upper dirIntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Upper);ErrorOr<vfs::Status> Status((std::error_code()));Lower->addDirectory("/both", sys::fs::owner_read);Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);Status = O->status("/both");ASSERT_FALSE(Status.getError());EXPECT_EQ(0740, Status->getPermissions());// permissions (as usual) are not recursively appliedLower->addRegularFile("/both/foo", sys::fs::owner_read);Upper->addRegularFile("/both/bar", sys::fs::owner_write);Status = O->status("/both/foo");ASSERT_FALSE(Status.getError());EXPECT_EQ(0400, Status->getPermissions());Status = O->status("/both/bar");ASSERT_FALSE(Status.getError());EXPECT_EQ(0200, Status->getPermissions());}TEST(VirtualFileSystemTest, OverlayIterator) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addRegularFile("/foo");IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Upper);ErrorOr<vfs::Status> Status((std::error_code()));{auto it = O->overlays_begin();auto end = O->overlays_end();EXPECT_NE(it, end);Status = (*it)->status("/foo");ASSERT_TRUE(Status.getError());it++;EXPECT_NE(it, end);Status = (*it)->status("/foo");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());it++;EXPECT_EQ(it, end);}{auto it = O->overlays_rbegin();auto end = O->overlays_rend();EXPECT_NE(it, end);Status = (*it)->status("/foo");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());it++;EXPECT_NE(it, end);Status = (*it)->status("/foo");ASSERT_TRUE(Status.getError());it++;EXPECT_EQ(it, end);}}TEST(VirtualFileSystemTest, BasicRealFSIteration) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();std::error_code EC;vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory.path()), EC);ASSERT_FALSE(EC);EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is emptyTempDir _a(TestDirectory.path("a"));TempDir _ab(TestDirectory.path("a/b"));TempDir _c(TestDirectory.path("c"));TempDir _cd(TestDirectory.path("c/d"));I = FS->dir_begin(Twine(TestDirectory.path()), EC);ASSERT_FALSE(EC);ASSERT_NE(vfs::directory_iterator(), I);// Check either a or c, since we can't rely on the iteration order.EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));I.increment(EC);ASSERT_FALSE(EC);ASSERT_NE(vfs::directory_iterator(), I);EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));I.increment(EC);EXPECT_EQ(vfs::directory_iterator(), I);}#ifdef LLVM_ON_UNIXTEST(VirtualFileSystemTest, MultipleWorkingDirs) {// Our root contains a/aa, b/bb, c, where c is a link to a/.// Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).// Interleave operations to show the working directories are independent.TempDir Root("r", /*Unique*/ true);TempDir ADir(Root.path("a"));TempDir BDir(Root.path("b"));TempLink C(ADir.path(), Root.path("c"));TempFile AA(ADir.path("aa"), "", "aaaa");TempFile BB(BDir.path("bb"), "", "bbbb");std::unique_ptr<vfs::FileSystem> BFS = vfs::createPhysicalFileSystem(),CFS = vfs::createPhysicalFileSystem();ASSERT_FALSE(BFS->setCurrentWorkingDirectory(BDir.path()));ASSERT_FALSE(CFS->setCurrentWorkingDirectory(C.path()));EXPECT_EQ(BDir.path(), *BFS->getCurrentWorkingDirectory());EXPECT_EQ(C.path(), *CFS->getCurrentWorkingDirectory());// openFileForRead(), indirectly.auto BBuf = BFS->getBufferForFile("bb");ASSERT_TRUE(BBuf);EXPECT_EQ("bbbb", (*BBuf)->getBuffer());auto ABuf = CFS->getBufferForFile("aa");ASSERT_TRUE(ABuf);EXPECT_EQ("aaaa", (*ABuf)->getBuffer());// status()auto BStat = BFS->status("bb");ASSERT_TRUE(BStat);EXPECT_EQ("bb", BStat->getName());auto AStat = CFS->status("aa");ASSERT_TRUE(AStat);EXPECT_EQ("aa", AStat->getName()); // unresolved name// getRealPath()SmallString<128> BPath;ASSERT_FALSE(BFS->getRealPath("bb", BPath));EXPECT_EQ(BB.path(), BPath);SmallString<128> APath;ASSERT_FALSE(CFS->getRealPath("aa", APath));EXPECT_EQ(AA.path(), APath); // Reports resolved name.// dir_beginstd::error_code EC;auto BIt = BFS->dir_begin(".", EC);ASSERT_FALSE(EC);ASSERT_NE(BIt, vfs::directory_iterator());EXPECT_EQ((BDir.path() + "/./bb").str(), BIt->path());BIt.increment(EC);ASSERT_FALSE(EC);ASSERT_EQ(BIt, vfs::directory_iterator());auto CIt = CFS->dir_begin(".", EC);ASSERT_FALSE(EC);ASSERT_NE(CIt, vfs::directory_iterator());EXPECT_EQ((ADir.path() + "/./aa").str(),CIt->path()); // Partly resolved name!CIt.increment(EC); // Because likely to read through this path.ASSERT_FALSE(EC);ASSERT_EQ(CIt, vfs::directory_iterator());}TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();TempLink _a("no_such_file", TestDirectory.path("a"));TempDir _b(TestDirectory.path("b"));TempLink _c("no_such_file", TestDirectory.path("c"));// Should get no iteration error, but a stat error for the broken symlinks.std::map<std::string, std::error_code> StatResults;std::error_code EC;for (vfs::directory_iteratorI = FS->dir_begin(Twine(TestDirectory.path()), EC),E;I != E; I.increment(EC)) {EXPECT_FALSE(EC);StatResults[std::string(sys::path::filename(I->path()))] =FS->status(I->path()).getError();}EXPECT_THAT(StatResults,ElementsAre(Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),Pair("b", std::error_code()),Pair("c",std::make_error_code(std::errc::no_such_file_or_directory))));}#endifTEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();std::error_code EC;auto I =vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);ASSERT_FALSE(EC);EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is emptyTempDir _a(TestDirectory.path("a"));TempDir _ab(TestDirectory.path("a/b"));TempDir _c(TestDirectory.path("c"));TempDir _cd(TestDirectory.path("c/d"));I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);ASSERT_FALSE(EC);ASSERT_NE(vfs::recursive_directory_iterator(), I);std::vector<std::string> Contents;for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;I.increment(EC)) {Contents.push_back(std::string(I->path()));}// Check contents, which may be in any orderEXPECT_EQ(4U, Contents.size());int Counts[4] = {0, 0, 0, 0};for (const std::string &Name : Contents) {ASSERT_FALSE(Name.empty());int Index = Name[Name.size() - 1] - 'a';ASSERT_GE(Index, 0);ASSERT_LT(Index, 4);Counts[Index]++;}EXPECT_EQ(1, Counts[0]); // aEXPECT_EQ(1, Counts[1]); // bEXPECT_EQ(1, Counts[2]); // cEXPECT_EQ(1, Counts[3]); // d}TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);TempDir _a(TestDirectory.path("a"));TempDir _ab(TestDirectory.path("a/b"));TempDir _c(TestDirectory.path("c"));TempDir _cd(TestDirectory.path("c/d"));TempDir _e(TestDirectory.path("e"));TempDir _ef(TestDirectory.path("e/f"));TempDir _g(TestDirectory.path("g"));IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();// Test that calling no_push on entries without subdirectories has no effect.{std::error_code EC;auto I =vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);ASSERT_FALSE(EC);std::vector<std::string> Contents;for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;I.increment(EC)) {Contents.push_back(std::string(I->path()));char last = I->path().back();switch (last) {case 'b':case 'd':case 'f':case 'g':I.no_push();break;default:break;}}EXPECT_EQ(7U, Contents.size());}// Test that calling no_push skips subdirectories.{std::error_code EC;auto I =vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);ASSERT_FALSE(EC);std::vector<std::string> Contents;for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;I.increment(EC)) {Contents.push_back(std::string(I->path()));char last = I->path().back();switch (last) {case 'a':case 'c':case 'e':I.no_push();break;default:break;}}// Check contents, which may be in any orderEXPECT_EQ(4U, Contents.size());int Counts[7] = {0, 0, 0, 0, 0, 0, 0};for (const std::string &Name : Contents) {ASSERT_FALSE(Name.empty());int Index = Name[Name.size() - 1] - 'a';ASSERT_GE(Index, 0);ASSERT_LT(Index, 7);Counts[Index]++;}EXPECT_EQ(1, Counts[0]); // aEXPECT_EQ(0, Counts[1]); // bEXPECT_EQ(1, Counts[2]); // cEXPECT_EQ(0, Counts[3]); // dEXPECT_EQ(1, Counts[4]); // eEXPECT_EQ(0, Counts[5]); // fEXPECT_EQ(1, Counts[6]); // g}}#ifdef LLVM_ON_UNIXTEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();TempLink _a("no_such_file", TestDirectory.path("a"));TempDir _b(TestDirectory.path("b"));TempLink _ba("no_such_file", TestDirectory.path("b/a"));TempDir _bb(TestDirectory.path("b/b"));TempLink _bc("no_such_file", TestDirectory.path("b/c"));TempLink _c("no_such_file", TestDirectory.path("c"));TempDir _d(TestDirectory.path("d"));TempDir _dd(TestDirectory.path("d/d"));TempDir _ddd(TestDirectory.path("d/d/d"));TempLink _e("no_such_file", TestDirectory.path("e"));std::vector<std::string> VisitedBrokenSymlinks;std::vector<std::string> VisitedNonBrokenSymlinks;std::error_code EC;for (vfs::recursive_directory_iteratorI(*FS, Twine(TestDirectory.path()), EC),E;I != E; I.increment(EC)) {EXPECT_FALSE(EC);(FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks).push_back(std::string(I->path()));}// Check visited file names.EXPECT_THAT(VisitedBrokenSymlinks,UnorderedElementsAre(_a.path().str(), _ba.path().str(),_bc.path().str(), _c.path().str(),_e.path().str()));EXPECT_THAT(VisitedNonBrokenSymlinks,UnorderedElementsAre(_b.path().str(), _bb.path().str(),_d.path().str(), _dd.path().str(),_ddd.path().str()));}#endiftemplate <typename DirIter>static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {std::error_code EC;SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());SmallVector<std::string, 4> InputToCheck;// Do not rely on iteration order to check for contents, sort both// content vectors before comparison.for (DirIter E; !EC && I != E; I.increment(EC))InputToCheck.push_back(std::string(I->path()));llvm::sort(InputToCheck);llvm::sort(Expected);EXPECT_EQ(InputToCheck.size(), Expected.size());unsigned LastElt = std::min(InputToCheck.size(), Expected.size());for (unsigned Idx = 0; Idx != LastElt; ++Idx)EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);}TEST(VirtualFileSystemTest, OverlayIteration) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Upper);std::error_code EC;checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());Lower->addRegularFile("/file1");checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));Upper->addRegularFile("/file2");checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});Lower->addDirectory("/dir1");Lower->addRegularFile("/dir1/foo");Upper->addDirectory("/dir2");Upper->addRegularFile("/dir2/foo");checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});}TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Middle);O->pushOverlay(Upper);std::error_code EC;checkContents(vfs::recursive_directory_iterator(*O, "/", EC),ArrayRef<StringRef>());Lower->addRegularFile("/file1");checkContents(vfs::recursive_directory_iterator(*O, "/", EC),ArrayRef<StringRef>("/file1"));Upper->addDirectory("/dir");Upper->addRegularFile("/dir/file2");checkContents(vfs::recursive_directory_iterator(*O, "/", EC),{"/dir", "/dir/file2", "/file1"});Lower->addDirectory("/dir1");Lower->addRegularFile("/dir1/foo");Lower->addDirectory("/dir1/a");Lower->addRegularFile("/dir1/a/b");Middle->addDirectory("/a");Middle->addDirectory("/a/b");Middle->addDirectory("/a/b/c");Middle->addRegularFile("/a/b/c/d");Middle->addRegularFile("/hiddenByUp");Upper->addDirectory("/dir2");Upper->addRegularFile("/dir2/foo");Upper->addRegularFile("/hiddenByUp");checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),ArrayRef<StringRef>("/dir2/foo"));checkContents(vfs::recursive_directory_iterator(*O, "/", EC),{"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp","/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a","/dir1/a/b", "/dir1/foo", "/file1"});}TEST(VirtualFileSystemTest, ThreeLevelIteration) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Middle);O->pushOverlay(Upper);std::error_code EC;checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());Middle->addRegularFile("/file2");checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));Lower->addRegularFile("/file1");Upper->addRegularFile("/file3");checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});}TEST(VirtualFileSystemTest, HiddenInIteration) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(Middle);O->pushOverlay(Upper);std::error_code EC;Lower->addRegularFile("/onlyInLow");Lower->addDirectory("/hiddenByMid");Lower->addDirectory("/hiddenByUp");Middle->addRegularFile("/onlyInMid");Middle->addRegularFile("/hiddenByMid");Middle->addDirectory("/hiddenByUp");Upper->addRegularFile("/onlyInUp");Upper->addRegularFile("/hiddenByUp");checkContents(O->dir_begin("/", EC),{"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});// Make sure we get the top-most entry{std::error_code EC;vfs::directory_iterator I = O->dir_begin("/", EC), E;for (; !EC && I != E; I.increment(EC))if (I->path() == "/hiddenByUp")break;ASSERT_NE(E, I);EXPECT_EQ(sys::fs::file_type::regular_file, I->type());}{std::error_code EC;vfs::directory_iterator I = O->dir_begin("/", EC), E;for (; !EC && I != E; I.increment(EC))if (I->path() == "/hiddenByMid")break;ASSERT_NE(E, I);EXPECT_EQ(sys::fs::file_type::regular_file, I->type());}}TEST(OverlayFileSystemTest, PrintOutput) {auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>();auto Overlay1 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Dummy);Overlay1->pushOverlay(Dummy);auto Overlay2 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Overlay1);Overlay2->pushOverlay(Dummy);SmallString<0> Output;raw_svector_ostream OuputStream{Output};Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Summary);ASSERT_EQ("OverlayFileSystem\n", Output);Output.clear();Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Contents);ASSERT_EQ("OverlayFileSystem\n"" DummyFileSystem (Summary)\n"" OverlayFileSystem\n",Output);Output.clear();Overlay2->print(OuputStream, vfs::FileSystem::PrintType::RecursiveContents);ASSERT_EQ("OverlayFileSystem\n"" DummyFileSystem (RecursiveContents)\n"" OverlayFileSystem\n"" DummyFileSystem (RecursiveContents)\n"" DummyFileSystem (RecursiveContents)\n",Output);}TEST(ProxyFileSystemTest, Basic) {IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base(new vfs::InMemoryFileSystem());vfs::ProxyFileSystem PFS(Base);Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));auto Stat = PFS.status("/a");ASSERT_FALSE(Stat.getError());auto File = PFS.openFileForRead("/a");ASSERT_FALSE(File.getError());EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer());std::error_code EC;vfs::directory_iterator I = PFS.dir_begin("/", EC);ASSERT_FALSE(EC);ASSERT_EQ("/a", I->path());I.increment(EC);ASSERT_FALSE(EC);ASSERT_EQ(vfs::directory_iterator(), I);ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/"));auto PWD = PFS.getCurrentWorkingDirectory();ASSERT_FALSE(PWD.getError());ASSERT_EQ("/", getPosixPath(*PWD));SmallString<16> Path;ASSERT_FALSE(PFS.getRealPath("a", Path));ASSERT_EQ("/a", getPosixPath(Path));bool Local = true;ASSERT_FALSE(PFS.isLocal("/a", Local));EXPECT_FALSE(Local);}class InMemoryFileSystemTest : public ::testing::Test {protected:llvm::vfs::InMemoryFileSystem FS;llvm::vfs::InMemoryFileSystem NormalizedFS;InMemoryFileSystemTest(): FS(/*UseNormalizedPaths=*/false),NormalizedFS(/*UseNormalizedPaths=*/true) {}};MATCHER_P2(IsHardLinkTo, FS, Target, "") {StringRef From = arg;StringRef To = Target;auto OpenedFrom = FS->openFileForRead(From);auto OpenedTo = FS->openFileForRead(To);return !OpenedFrom.getError() && !OpenedTo.getError() &&(*OpenedFrom)->status()->getUniqueID() ==(*OpenedTo)->status()->getUniqueID();}TEST_F(InMemoryFileSystemTest, IsEmpty) {auto Stat = FS.status("/a");ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();Stat = FS.status("/");ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();}TEST_F(InMemoryFileSystemTest, WindowsPath) {FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));auto Stat = FS.status("c:");#if !defined(_WIN32)ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();#endifStat = FS.status("c:/windows/system128/foo.cpp");ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));Stat = FS.status("d:/windows/foo.cpp");ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();}TEST_F(InMemoryFileSystemTest, OverlayFile) {FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));auto Stat = FS.status("/");ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();Stat = FS.status("/.");ASSERT_FALSE(Stat);Stat = NormalizedFS.status("/.");ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();Stat = FS.status("/a");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_EQ("/a", Stat->getName());}TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {auto Buf = MemoryBuffer::getMemBuffer("a");FS.addFileNoOwn("/a", 0, *Buf);auto Stat = FS.status("/a");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_EQ("/a", Stat->getName());}TEST_F(InMemoryFileSystemTest, OpenFileForRead) {FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));auto File = FS.openFileForRead("/a");ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());File = FS.openFileForRead("/a"); // Open again.ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());File = NormalizedFS.openFileForRead("/././a"); // Open again.ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());File = FS.openFileForRead("/");ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();File = FS.openFileForRead("/b");ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();File = FS.openFileForRead("./c");ASSERT_FALSE(File);File = FS.openFileForRead("e/../d");ASSERT_FALSE(File);File = NormalizedFS.openFileForRead("./c");ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());File = NormalizedFS.openFileForRead("e/../d");ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());}TEST_F(InMemoryFileSystemTest, DuplicatedFile) {ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));}TEST_F(InMemoryFileSystemTest, DirectoryIteration) {FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));std::error_code EC;vfs::directory_iterator I = FS.dir_begin("/", EC);ASSERT_FALSE(EC);ASSERT_EQ("/a", I->path());I.increment(EC);ASSERT_FALSE(EC);ASSERT_EQ("/b", I->path());I.increment(EC);ASSERT_FALSE(EC);ASSERT_EQ(vfs::directory_iterator(), I);I = FS.dir_begin("/b", EC);ASSERT_FALSE(EC);// When on Windows, we end up with "/b\\c" as the name. Convert to Posix// path for the sake of the comparison.ASSERT_EQ("/b/c", getPosixPath(std::string(I->path())));I.increment(EC);ASSERT_FALSE(EC);ASSERT_EQ(vfs::directory_iterator(), I);}TEST_F(InMemoryFileSystemTest, WorkingDirectory) {FS.setCurrentWorkingDirectory("/b");FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));auto Stat = FS.status("/b/c");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_EQ("/b/c", Stat->getName());ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());Stat = FS.status("c");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();NormalizedFS.setCurrentWorkingDirectory("/b/c");NormalizedFS.setCurrentWorkingDirectory(".");ASSERT_EQ("/b/c",getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));NormalizedFS.setCurrentWorkingDirectory("..");ASSERT_EQ("/b",getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));}TEST_F(InMemoryFileSystemTest, IsLocal) {FS.setCurrentWorkingDirectory("/b");FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));std::error_code EC;bool IsLocal = true;EC = FS.isLocal("c", IsLocal);ASSERT_FALSE(EC);ASSERT_FALSE(IsLocal);}#if !defined(_WIN32)TEST_F(InMemoryFileSystemTest, GetRealPath) {SmallString<16> Path;EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);auto GetRealPath = [this](StringRef P) {SmallString<16> Output;auto EC = FS.getRealPath(P, Output);EXPECT_FALSE(EC);return std::string(Output);};FS.setCurrentWorkingDirectory("a");EXPECT_EQ(GetRealPath("b"), "a/b");EXPECT_EQ(GetRealPath("../b"), "b");EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");FS.setCurrentWorkingDirectory("/a");EXPECT_EQ(GetRealPath("b"), "/a/b");EXPECT_EQ(GetRealPath("../b"), "/b");EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");}#endif // _WIN32TEST_F(InMemoryFileSystemTest, AddFileWithUser) {FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);auto Stat = FS.status("/a");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());ASSERT_EQ(0xFEEDFACE, Stat->getUser());Stat = FS.status("/a/b");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());ASSERT_EQ(0xFEEDFACE, Stat->getUser());Stat = FS.status("/a/b/c");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isRegularFile());ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());ASSERT_EQ(0xFEEDFACE, Stat->getUser());}TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);auto Stat = FS.status("/a");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());ASSERT_EQ(0xDABBAD00, Stat->getGroup());Stat = FS.status("/a/b");ASSERT_TRUE(Stat->isDirectory());ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_EQ(0xDABBAD00, Stat->getGroup());Stat = FS.status("/a/b/c");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isRegularFile());ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());ASSERT_EQ(0xDABBAD00, Stat->getGroup());}TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,sys::fs::file_type::socket_file);auto Stat = FS.status("/a");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());Stat = FS.status("/a/b");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());Stat = FS.status("/a/b/c");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());}TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None,sys::fs::perms::owner_read | sys::fs::perms::owner_write);auto Stat = FS.status("/a");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |sys::fs::perms::owner_exe,Stat->getPermissions());Stat = FS.status("/a/b");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |sys::fs::perms::owner_exe,Stat->getPermissions());Stat = FS.status("/a/b/c");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isRegularFile());ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,Stat->getPermissions());}TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,/*Group=*/None, sys::fs::file_type::directory_file);FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,/*Group=*/None, sys::fs::file_type::regular_file);auto Stat = FS.status("/a");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isDirectory());Stat = FS.status("/a/b");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();ASSERT_TRUE(Stat->isRegularFile());}// Test that the name returned by status() is in the same form as the path that// was requested (to match the behavior of RealFileSystem).TEST_F(InMemoryFileSystemTest, StatusName) {NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),/*User=*/None,/*Group=*/None, sys::fs::file_type::regular_file);NormalizedFS.setCurrentWorkingDirectory("/a/b");// Access using InMemoryFileSystem::status.auto Stat = NormalizedFS.status("../b/c");ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"<< NormalizedFS.toString();ASSERT_TRUE(Stat->isRegularFile());ASSERT_EQ("../b/c", Stat->getName());// Access using InMemoryFileAdaptor::status.auto File = NormalizedFS.openFileForRead("../b/c");ASSERT_FALSE(File.getError()) << File.getError() << "\n"<< NormalizedFS.toString();Stat = (*File)->status();ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"<< NormalizedFS.toString();ASSERT_TRUE(Stat->isRegularFile());ASSERT_EQ("../b/c", Stat->getName());// Access using a directory iterator.std::error_code EC;llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);// When on Windows, we end up with "../b\\c" as the name. Convert to Posix// path for the sake of the comparison.ASSERT_EQ("../b/c", getPosixPath(std::string(It->path())));}TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {StringRef FromLink = "/path/to/FROM/link";StringRef Target = "/path/to/TO/file";FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));EXPECT_TRUE(FS.addHardLink(FromLink, Target));EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));EXPECT_EQ(FS.status(FromLink)->getSize(), FS.status(Target)->getSize());EXPECT_EQ(FS.getBufferForFile(FromLink)->get()->getBuffer(),FS.getBufferForFile(Target)->get()->getBuffer());}TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {StringRef Link0 = "/path/to/0/link";StringRef Link1 = "/path/to/1/link";StringRef Link2 = "/path/to/2/link";StringRef Target = "/path/to/target";FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));EXPECT_TRUE(FS.addHardLink(Link2, Target));EXPECT_TRUE(FS.addHardLink(Link1, Link2));EXPECT_TRUE(FS.addHardLink(Link0, Link1));EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));}TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));}TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {StringRef Link = "/path/to/link";StringRef Target = "/path/to/target";FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));EXPECT_FALSE(FS.addHardLink(Link, Target));}TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {StringRef Link = "/path/to/link";StringRef Target = "/path/to/target";FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));EXPECT_TRUE(FS.addHardLink(Link, Target));EXPECT_FALSE(FS.addHardLink(Link, Target));}TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {StringRef Link = "/path/to/link";StringRef Target = "/path/to/target";StringRef Content = "content of target";EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));EXPECT_TRUE(FS.addHardLink(Link, Target));EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));}TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {StringRef Link = "/path/to/link";StringRef Target = "/path/to/target";StringRef Content = "content of target";StringRef LinkContent = "different content of link";EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));EXPECT_TRUE(FS.addHardLink(Link, Target));EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));}TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {StringRef Dir = "path/to/dummy/dir";StringRef Link = "/path/to/link";StringRef File = "path/to/dummy/dir/target";StringRef Content = "content of target";EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));EXPECT_FALSE(FS.addHardLink(Link, Dir));}TEST_F(InMemoryFileSystemTest, AddHardLinkToASymlink) {EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer("content")));EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0));EXPECT_TRUE(FS.addHardLink("/hardlink", "/symlink"));EXPECT_EQ((*FS.getBufferForFile("/hardlink"))->getBuffer(), "content");}TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {StringRef Dir = "path/to/dummy/dir";StringRef Target = "path/to/dummy/dir/target";StringRef Content = "content of target";EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));EXPECT_FALSE(FS.addHardLink(Dir, Target));}TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {StringRef CommonContent = "content string";FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));}TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {std::error_code EC;FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));auto I = vfs::recursive_directory_iterator(FS, "/", EC);ASSERT_FALSE(EC);std::vector<std::string> Nodes;for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;I.increment(EC)) {Nodes.push_back(getPosixPath(std::string(I->path())));}EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));}TEST_F(InMemoryFileSystemTest, UniqueID) {ASSERT_TRUE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));ASSERT_TRUE(FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text")));ASSERT_TRUE(FS.addHardLink("/e/f", "/a/b"));EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/a/b")->getUniqueID());EXPECT_NE(FS.status("/a/b")->getUniqueID(), FS.status("/c/d")->getUniqueID());EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/e/f")->getUniqueID());EXPECT_EQ(FS.status("/a")->getUniqueID(), FS.status("/a")->getUniqueID());EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/c")->getUniqueID());EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID());// Recreating the "same" FS yields the same UniqueIDs.// Note: FS2 should match FS with respect to path normalization.vfs::InMemoryFileSystem FS2(/*UseNormalizedPath=*/false);ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));EXPECT_EQ(FS.status("/a/b")->getUniqueID(),FS2.status("/a/b")->getUniqueID());EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID());}TEST_F(InMemoryFileSystemTest, AddSymlinkToAFile) {EXPECT_TRUE(FS.addFile("/some/file", 0, MemoryBuffer::getMemBuffer("contents")));EXPECT_TRUE(FS.addSymbolicLink("/other/file/link", "/some/file", 0));ErrorOr<vfs::Status> Stat = FS.status("/some/file");EXPECT_TRUE(Stat->isRegularFile());}TEST_F(InMemoryFileSystemTest, AddSymlinkToADirectory) {EXPECT_TRUE(FS.addSymbolicLink("/link", "/target", 0));EXPECT_TRUE(FS.addFile("/target/foo.h", 0, MemoryBuffer::getMemBuffer("foo")));ErrorOr<vfs::Status> Stat = FS.status("/link/foo.h");EXPECT_TRUE(Stat);EXPECT_EQ((*Stat).getName(), "/link/foo.h");EXPECT_TRUE(Stat->isRegularFile());}TEST_F(InMemoryFileSystemTest, AddSymlinkToASymlink) {EXPECT_TRUE(FS.addSymbolicLink("/first", "/second", 0));EXPECT_TRUE(FS.addSymbolicLink("/second", "/third", 0));EXPECT_TRUE(FS.addFile("/third", 0, MemoryBuffer::getMemBuffer("")));ErrorOr<vfs::Status> Stat = FS.status("/first");EXPECT_TRUE(Stat);EXPECT_EQ((*Stat).getName(), "/first");// Follow-through symlinks by default. This matches RealFileSystem's// semantics.EXPECT_TRUE(Stat->isRegularFile());Stat = FS.status("/second");EXPECT_TRUE(Stat);EXPECT_EQ((*Stat).getName(), "/second");EXPECT_TRUE(Stat->isRegularFile());Stat = FS.status("/third");EXPECT_TRUE(Stat);EXPECT_EQ((*Stat).getName(), "/third");EXPECT_TRUE(Stat->isRegularFile());}TEST_F(InMemoryFileSystemTest, AddRecursiveSymlink) {EXPECT_TRUE(FS.addSymbolicLink("/link-a", "/link-b", 0));EXPECT_TRUE(FS.addSymbolicLink("/link-b", "/link-a", 0));ErrorOr<vfs::Status> Stat = FS.status("/link-a/foo");EXPECT_FALSE(Stat);EXPECT_EQ(Stat.getError(), errc::no_such_file_or_directory);}TEST_F(InMemoryFileSystemTest, DirectoryIteratorWithSymlinkToAFile) {std::error_code EC;EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer("")));EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0));vfs::directory_iterator I = FS.dir_begin("/", EC), E;ASSERT_FALSE(EC);std::vector<std::string> Nodes;for (; !EC && I != E; I.increment(EC))Nodes.push_back(getPosixPath(std::string(I->path())));EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/file", "/file"));}TEST_F(InMemoryFileSystemTest, RecursiveDirectoryIteratorWithSymlinkToADir) {std::error_code EC;EXPECT_TRUE(FS.addFile("/dir/file", 0, MemoryBuffer::getMemBuffer("")));EXPECT_TRUE(FS.addSymbolicLink("/dir_symlink", "/dir", 0));vfs::recursive_directory_iterator I(FS, "/", EC), E;ASSERT_FALSE(EC);std::vector<std::string> Nodes;for (; !EC && I != E; I.increment(EC))Nodes.push_back(getPosixPath(std::string(I->path())));EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/dir", "/dir/file", "/dir","/dir/file"));}// NOTE: in the tests below, we use '//root/' as our root directory, since it is// a legal *absolute* path on Windows as well as *nix.class VFSFromYAMLTest : public ::testing::Test {public:int NumDiagnostics;void SetUp() override { NumDiagnostics = 0; }static void CountingDiagHandler(const SMDiagnostic &, void *Context) {VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);++Test->NumDiagnostics;}std::unique_ptr<vfs::FileSystem>getFromYAMLRawString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,ExternalFS);}std::unique_ptr<vfs::FileSystem> getFromYAMLString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {std::string VersionPlusContent("{\n 'version':0,\n");VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);return getFromYAMLRawString(VersionPlusContent, ExternalFS);}// This is intended as a "XFAIL" for windows hosts.bool supportsSameDirMultipleYAMLEntries() {Triple Host(Triple::normalize(sys::getProcessTriple()));return !Host.isOSWindows();}};TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {IntrusiveRefCntPtr<vfs::FileSystem> FS;FS = getFromYAMLString("");EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("[]");EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("'string'");EXPECT_EQ(nullptr, FS.get());EXPECT_EQ(3, NumDiagnostics);}TEST_F(VFSFromYAMLTest, MappedFiles) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/foo/bar");Lower->addRegularFile("//root/foo/bar/a");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'file1',\n"" 'external-contents': '//root/foo/bar/a'\n"" },\n"" {\n"" 'type': 'file',\n"" 'name': 'file2',\n"" 'external-contents': '//root/foo/b'\n"" },\n"" {\n"" 'type': 'directory-remap',\n"" 'name': 'mappeddir',\n"" 'external-contents': '//root/foo/bar'\n"" },\n"" {\n"" 'type': 'directory-remap',\n"" 'name': 'mappeddir2',\n"" 'use-external-name': false,\n"" 'external-contents': '//root/foo/bar'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);// fileErrorOr<vfs::Status> S = O->status("//root/file1");ASSERT_FALSE(S.getError());EXPECT_EQ("//root/foo/bar/a", S->getName());EXPECT_TRUE(S->IsVFSMapped);EXPECT_TRUE(S->ExposesExternalVFSPath);ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");EXPECT_EQ("//root/foo/bar/a", SLower->getName());EXPECT_TRUE(S->equivalent(*SLower));EXPECT_FALSE(SLower->IsVFSMapped);EXPECT_FALSE(SLower->ExposesExternalVFSPath);// file after openingauto OpenedF = O->openFileForRead("//root/file1");ASSERT_FALSE(OpenedF.getError());auto OpenedS = (*OpenedF)->status();ASSERT_FALSE(OpenedS.getError());EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());EXPECT_TRUE(OpenedS->IsVFSMapped);EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);// directoryS = O->status("//root/");ASSERT_FALSE(S.getError());EXPECT_TRUE(S->isDirectory());EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID// remapped directoryS = O->status("//root/mappeddir");ASSERT_FALSE(S.getError());EXPECT_TRUE(S->isDirectory());EXPECT_TRUE(S->IsVFSMapped);EXPECT_TRUE(S->ExposesExternalVFSPath);EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar")));SLower = O->status("//root/foo/bar");EXPECT_EQ("//root/foo/bar", SLower->getName());EXPECT_TRUE(S->equivalent(*SLower));EXPECT_FALSE(SLower->IsVFSMapped);EXPECT_FALSE(SLower->ExposesExternalVFSPath);// file in remapped directoryS = O->status("//root/mappeddir/a");ASSERT_FALSE(S.getError());EXPECT_FALSE(S->isDirectory());EXPECT_TRUE(S->IsVFSMapped);EXPECT_TRUE(S->ExposesExternalVFSPath);EXPECT_EQ("//root/foo/bar/a", S->getName());// file in remapped directory, with use-external-name=falseS = O->status("//root/mappeddir2/a");ASSERT_FALSE(S.getError());EXPECT_FALSE(S->isDirectory());EXPECT_TRUE(S->IsVFSMapped);EXPECT_FALSE(S->ExposesExternalVFSPath);EXPECT_EQ("//root/mappeddir2/a", S->getName());// file contents in remapped directoryOpenedF = O->openFileForRead("//root/mappeddir/a");ASSERT_FALSE(OpenedF.getError());OpenedS = (*OpenedF)->status();ASSERT_FALSE(OpenedS.getError());EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());EXPECT_TRUE(OpenedS->IsVFSMapped);EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);// file contents in remapped directory, with use-external-name=falseOpenedF = O->openFileForRead("//root/mappeddir2/a");ASSERT_FALSE(OpenedF.getError());OpenedS = (*OpenedF)->status();ASSERT_FALSE(OpenedS.getError());EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName());EXPECT_TRUE(OpenedS->IsVFSMapped);EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);// broken mappingEXPECT_EQ(O->status("//root/file2").getError(),llvm::errc::no_such_file_or_directory);EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, MappedRoot) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/foo/bar");Lower->addRegularFile("//root/foo/bar/a");IntrusiveRefCntPtr<vfs::FileSystem> FS =getFromYAMLString("{ 'roots': [\n""{\n"" 'type': 'directory-remap',\n"" 'name': '//mappedroot/',\n"" 'external-contents': '//root/foo/bar'\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);// fileErrorOr<vfs::Status> S = O->status("//mappedroot/a");ASSERT_FALSE(S.getError());EXPECT_EQ("//root/foo/bar/a", S->getName());EXPECT_TRUE(S->IsVFSMapped);EXPECT_TRUE(S->ExposesExternalVFSPath);ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");EXPECT_EQ("//root/foo/bar/a", SLower->getName());EXPECT_TRUE(S->equivalent(*SLower));EXPECT_FALSE(SLower->IsVFSMapped);EXPECT_FALSE(SLower->ExposesExternalVFSPath);// file after openingauto OpenedF = O->openFileForRead("//mappedroot/a");ASSERT_FALSE(OpenedF.getError());auto OpenedS = (*OpenedF)->status();ASSERT_FALSE(OpenedS.getError());EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());EXPECT_TRUE(OpenedS->IsVFSMapped);EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlay) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addDirectory("//root/bar");Lower->addRegularFile("//root/bar/b");Lower->addRegularFile("//root/bar/c");IntrusiveRefCntPtr<vfs::FileSystem> FS =getFromYAMLString("{ 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'directory-remap',\n"" 'name': 'bar',\n"" 'external-contents': '//root/foo'\n"" }\n"" ]\n""}]}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);ErrorOr<vfs::Status> S = O->status("//root/foo");ASSERT_FALSE(S.getError());ErrorOr<vfs::Status> SS = O->status("//root/bar");ASSERT_FALSE(SS.getError());EXPECT_TRUE(S->equivalent(*SS));std::error_code EC;checkContents(O->dir_begin("//root/bar", EC),{"//root/foo/a", "//root/bar/b", "//root/bar/c"});Lower->addRegularFile("//root/foo/b");checkContents(O->dir_begin("//root/bar", EC),{"//root/foo/a", "//root/foo/b", "//root/bar/c"});EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addDirectory("//root/bar");Lower->addRegularFile("//root/bar/b");Lower->addRegularFile("//root/bar/c");IntrusiveRefCntPtr<vfs::FileSystem> FS =getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'directory-remap',\n"" 'name': 'bar',\n"" 'external-contents': '//root/foo'\n"" }\n"" ]\n""}]}",Lower);ASSERT_NE(FS.get(), nullptr);ErrorOr<vfs::Status> S = FS->status("//root/foo");ASSERT_FALSE(S.getError());ErrorOr<vfs::Status> SS = FS->status("//root/bar");ASSERT_FALSE(SS.getError());EXPECT_TRUE(S->equivalent(*SS));std::error_code EC;checkContents(FS->dir_begin("//root/bar", EC),{"//root/bar/a", "//root/bar/b", "//root/bar/c"});Lower->addRegularFile("//root/foo/b");checkContents(FS->dir_begin("//root/bar", EC),{"//root/bar/a", "//root/bar/b", "//root/bar/c"});EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addDirectory("//root/bar");Lower->addRegularFile("//root/bar/b");Lower->addRegularFile("//root/bar/c");IntrusiveRefCntPtr<vfs::FileSystem> FS =getFromYAMLString("{ 'fallthrough': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'directory-remap',\n"" 'name': 'bar',\n"" 'external-contents': '//root/foo'\n"" }\n"" ]\n""}]}",Lower);ASSERT_NE(FS.get(), nullptr);ErrorOr<vfs::Status> S = Lower->status("//root/foo");ASSERT_FALSE(S.getError());ErrorOr<vfs::Status> SS = FS->status("//root/bar");ASSERT_FALSE(SS.getError());EXPECT_TRUE(S->equivalent(*SS));std::error_code EC;checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"});Lower->addRegularFile("//root/foo/b");checkContents(FS->dir_begin("//root/bar", EC),{"//root/foo/a", "//root/foo/b"});EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) {IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(new vfs::InMemoryFileSystem);BaseFS->addFile("//root/foo/a", 0,MemoryBuffer::getMemBuffer("contents of a"));ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo"));auto RemappedFS = vfs::RedirectingFileSystem::create({}, /*UseExternalNames=*/false, *BaseFS);auto OpenedF = RemappedFS->openFileForRead("a");ASSERT_FALSE(OpenedF.getError());llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();ASSERT_FALSE(Name.getError());EXPECT_EQ("a", Name.get());auto OpenedS = (*OpenedF)->status();ASSERT_FALSE(OpenedS.getError());EXPECT_EQ("a", OpenedS->getName());EXPECT_FALSE(OpenedS->IsVFSMapped);EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);auto DirectS = RemappedFS->status("a");ASSERT_FALSE(DirectS.getError());EXPECT_EQ("a", DirectS->getName());EXPECT_FALSE(DirectS->IsVFSMapped);EXPECT_FALSE(DirectS->ExposesExternalVFSPath);EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) {IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(new vfs::InMemoryFileSystem);BaseFS->addFile("//root/foo/realname", 0,MemoryBuffer::getMemBuffer("contents of a"));auto FS =getFromYAMLString("{ 'use-external-names': true,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/foo',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'vfsname',\n"" 'external-contents': 'realname'\n"" }\n"" ]\n""}]}",BaseFS);ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));auto OpenedF = FS->openFileForRead("vfsname");ASSERT_FALSE(OpenedF.getError());llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();ASSERT_FALSE(Name.getError());EXPECT_EQ("realname", Name.get());auto OpenedS = (*OpenedF)->status();ASSERT_FALSE(OpenedS.getError());EXPECT_EQ("realname", OpenedS->getName());EXPECT_TRUE(OpenedS->IsVFSMapped);EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);auto DirectS = FS->status("vfsname");ASSERT_FALSE(DirectS.getError());EXPECT_EQ("realname", DirectS->getName());EXPECT_TRUE(DirectS->IsVFSMapped);EXPECT_TRUE(DirectS->ExposesExternalVFSPath);EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) {IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(new vfs::InMemoryFileSystem);BaseFS->addFile("//root/foo/realname", 0,MemoryBuffer::getMemBuffer("contents of a"));auto FS =getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/foo',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'vfsname',\n"" 'external-contents': 'realname'\n"" }\n"" ]\n""}]}",BaseFS);ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));auto OpenedF = FS->openFileForRead("vfsname");ASSERT_FALSE(OpenedF.getError());llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();ASSERT_FALSE(Name.getError());EXPECT_EQ("vfsname", Name.get());auto OpenedS = (*OpenedF)->status();ASSERT_FALSE(OpenedS.getError());EXPECT_EQ("vfsname", OpenedS->getName());EXPECT_TRUE(OpenedS->IsVFSMapped);EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);auto DirectS = FS->status("vfsname");ASSERT_FALSE(DirectS.getError());EXPECT_EQ("vfsname", DirectS->getName());EXPECT_TRUE(DirectS->IsVFSMapped);EXPECT_FALSE(DirectS->ExposesExternalVFSPath);EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, CaseInsensitive) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addRegularFile("//root/foo/bar/a");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'case-sensitive': 'false',\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'XX',\n"" 'external-contents': '//root/foo/bar/a'\n"" }\n"" ]\n""}]}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);ErrorOr<vfs::Status> S = O->status("//root/XX");ASSERT_FALSE(S.getError());ErrorOr<vfs::Status> SS = O->status("//root/xx");ASSERT_FALSE(SS.getError());EXPECT_TRUE(S->equivalent(*SS));SS = O->status("//root/xX");EXPECT_TRUE(S->equivalent(*SS));SS = O->status("//root/Xx");EXPECT_TRUE(S->equivalent(*SS));EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, CaseSensitive) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addRegularFile("//root/foo/bar/a");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'case-sensitive': 'true',\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'XX',\n"" 'external-contents': '//root/foo/bar/a'\n"" }\n"" ]\n""}]}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);ErrorOr<vfs::Status> SS = O->status("//root/xx");EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);SS = O->status("//root/xX");EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);SS = O->status("//root/Xx");EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, IllegalVFSFile) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());// invalid YAML at top-levelIntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);EXPECT_EQ(nullptr, FS.get());// invalid YAML in rootsFS = getFromYAMLString("{ 'roots':[}", Lower);// invalid YAML in directoryFS = getFromYAMLString("{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",Lower);EXPECT_EQ(nullptr, FS.get());// invalid configurationFS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);EXPECT_EQ(nullptr, FS.get());// invalid rootsFS = getFromYAMLString("{ 'roots':'' }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':{} }", Lower);EXPECT_EQ(nullptr, FS.get());// invalid entriesFS = getFromYAMLString("{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], ""'external-contents': 'other' }",Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",Lower);EXPECT_EQ(nullptr, FS.get());// missing mandatory fieldsFS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);EXPECT_EQ(nullptr, FS.get());// duplicate keysFS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLString("{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",Lower);EXPECT_EQ(nullptr, FS.get());FS =getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', ""'external-contents':'blah' } ] }",Lower);EXPECT_EQ(nullptr, FS.get());// missing versionFS = getFromYAMLRawString("{ 'roots':[] }", Lower);EXPECT_EQ(nullptr, FS.get());// bad version numberFS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);EXPECT_EQ(nullptr, FS.get());FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);EXPECT_EQ(nullptr, FS.get());// both 'external-contents' and 'contents' specifiedLower->addDirectory("//root/external/dir");FS = getFromYAMLString("{ 'roots':[ \n""{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"" 'external-contents': '//root/external/dir'}]}",Lower);EXPECT_EQ(nullptr, FS.get());// 'directory-remap' with 'contents'FS = getFromYAMLString("{ 'roots':[ \n""{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",Lower);EXPECT_EQ(nullptr, FS.get());// invalid redirect kindFS = getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n"" 'type': 'directory-remap',\n"" 'name': '//root/A',\n"" 'external-contents': '//root/B' }]}",Lower);EXPECT_EQ(nullptr, FS.get());// redirect and fallthrough passedFS = getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n"" 'fallthrough': true,\n"" 'roots': [{\n"" 'type': 'directory-remap',\n"" 'name': '//root/A',\n"" 'external-contents': '//root/B' }]}",Lower);EXPECT_EQ(nullptr, FS.get());EXPECT_EQ(28, NumDiagnostics);}TEST_F(VFSFromYAMLTest, UseExternalName) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addRegularFile("//root/external/file");IntrusiveRefCntPtr<vfs::FileSystem> FS =getFromYAMLString("{ 'roots': [\n"" { 'type': 'file', 'name': '//root/A',\n"" 'external-contents': '//root/external/file'\n"" },\n"" { 'type': 'file', 'name': '//root/B',\n"" 'use-external-name': true,\n"" 'external-contents': '//root/external/file'\n"" },\n"" { 'type': 'file', 'name': '//root/C',\n"" 'use-external-name': false,\n"" 'external-contents': '//root/external/file'\n"" }\n""] }",Lower);ASSERT_NE(nullptr, FS.get());// default trueEXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());// explicitEXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());EXPECT_EQ("//root/C", FS->status("//root/C")->getName());// global configurationFS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n"" { 'type': 'file', 'name': '//root/A',\n"" 'external-contents': '//root/external/file'\n"" },\n"" { 'type': 'file', 'name': '//root/B',\n"" 'use-external-name': true,\n"" 'external-contents': '//root/external/file'\n"" },\n"" { 'type': 'file', 'name': '//root/C',\n"" 'use-external-name': false,\n"" 'external-contents': '//root/external/file'\n"" }\n""] }",Lower);ASSERT_NE(nullptr, FS.get());// defaultEXPECT_EQ("//root/A", FS->status("//root/A")->getName());// explicitEXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());EXPECT_EQ("//root/C", FS->status("//root/C")->getName());}TEST_F(VFSFromYAMLTest, MultiComponentPath) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addRegularFile("//root/other");// file in rootsIntrusiveRefCntPtr<vfs::FileSystem> FS =getFromYAMLString("{ 'roots': [\n"" { 'type': 'file', 'name': '//root/path/to/file',\n"" 'external-contents': '//root/other' }]\n""}",Lower);ASSERT_NE(nullptr, FS.get());EXPECT_FALSE(FS->status("//root/path/to/file").getError());EXPECT_FALSE(FS->status("//root/path/to").getError());EXPECT_FALSE(FS->status("//root/path").getError());EXPECT_FALSE(FS->status("//root/").getError());// at the startFS = getFromYAMLString("{ 'roots': [\n"" { 'type': 'directory', 'name': '//root/path/to',\n"" 'contents': [ { 'type': 'file', 'name': 'file',\n"" 'external-contents': '//root/other' }]}]\n""}",Lower);ASSERT_NE(nullptr, FS.get());EXPECT_FALSE(FS->status("//root/path/to/file").getError());EXPECT_FALSE(FS->status("//root/path/to").getError());EXPECT_FALSE(FS->status("//root/path").getError());EXPECT_FALSE(FS->status("//root/").getError());// at the endFS = getFromYAMLString("{ 'roots': [\n"" { 'type': 'directory', 'name': '//root/',\n"" 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"" 'external-contents': '//root/other' }]}]\n""}",Lower);ASSERT_NE(nullptr, FS.get());EXPECT_FALSE(FS->status("//root/path/to/file").getError());EXPECT_FALSE(FS->status("//root/path/to").getError());EXPECT_FALSE(FS->status("//root/path").getError());EXPECT_FALSE(FS->status("//root/").getError());}TEST_F(VFSFromYAMLTest, TrailingSlashes) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addRegularFile("//root/other");// file in rootsIntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'roots': [\n"" { 'type': 'directory', 'name': '//root/path/to////',\n"" 'contents': [ { 'type': 'file', 'name': 'file',\n"" 'external-contents': '//root/other' }]}]\n""}",Lower);ASSERT_NE(nullptr, FS.get());EXPECT_FALSE(FS->status("//root/path/to/file").getError());EXPECT_FALSE(FS->status("//root/path/to").getError());EXPECT_FALSE(FS->status("//root/path").getError());EXPECT_FALSE(FS->status("//root/").getError());}TEST_F(VFSFromYAMLTest, DirectoryIteration) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/");Lower->addDirectory("//root/foo");Lower->addDirectory("//root/foo/bar");Lower->addRegularFile("//root/foo/bar/a");Lower->addRegularFile("//root/foo/bar/b");Lower->addRegularFile("//root/file3");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'file1',\n"" 'external-contents': '//root/foo/bar/a'\n"" },\n"" {\n"" 'type': 'file',\n"" 'name': 'file2',\n"" 'external-contents': '//root/foo/bar/b'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);std::error_code EC;checkContents(O->dir_begin("//root/", EC),{"//root/file1", "//root/file2", "//root/file3", "//root/foo"});checkContents(O->dir_begin("//root/foo/bar", EC),{"//root/foo/bar/a", "//root/foo/bar/b"});}TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {// https://llvm.org/bugs/show_bug.cgi?id=27725if (!supportsSameDirMultipleYAMLEntries())GTEST_SKIP();IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/zab");Lower->addDirectory("//root/baz");Lower->addRegularFile("//root/zab/a");Lower->addRegularFile("//root/zab/b");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/baz/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'x',\n"" 'external-contents': '//root/zab/a'\n"" }\n"" ]\n""},\n""{\n"" 'type': 'directory',\n"" 'name': '//root/baz/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'y',\n"" 'external-contents': '//root/zab/b'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);std::error_code EC;checkContents(O->dir_begin("//root/baz/", EC),{"//root/baz/x", "//root/baz/y"});}TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/a");Lower->addDirectory("//root/a/b");Lower->addDirectory("//root/a/b/c");Lower->addRegularFile("//root/a/b/c/file");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/a/b/c/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'file',\n"" 'external-contents': '//root/a/b/c/file'\n"" }\n"" ]\n""},\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Lower));O->pushOverlay(FS);std::error_code EC;// Test recursive_directory_iterator level()vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(*O, "//root", EC),E;ASSERT_FALSE(EC);for (int l = 0; I != E; I.increment(EC), ++l) {ASSERT_FALSE(EC);EXPECT_EQ(I.level(), l);}EXPECT_EQ(I, E);}TEST_F(VFSFromYAMLTest, RelativePaths) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());std::error_code EC;SmallString<128> CWD;EC = llvm::sys::fs::current_path(CWD);ASSERT_FALSE(EC);// Filename at root level without a parent directory.IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'roots': [\n"" { 'type': 'file', 'name': 'file-not-in-directory.h',\n"" 'external-contents': '//root/external/file'\n"" }\n""] }",Lower);ASSERT_TRUE(FS.get() != nullptr);SmallString<128> ExpectedPathNotInDir("file-not-in-directory.h");llvm::sys::fs::make_absolute(ExpectedPathNotInDir);checkContents(FS->dir_begin(CWD, EC), {ExpectedPathNotInDir});// Relative file path.FS = getFromYAMLString("{ 'roots': [\n"" { 'type': 'file', 'name': 'relative/path.h',\n"" 'external-contents': '//root/external/file'\n"" }\n""] }",Lower);ASSERT_TRUE(FS.get() != nullptr);SmallString<128> Parent("relative");llvm::sys::fs::make_absolute(Parent);auto I = FS->dir_begin(Parent, EC);ASSERT_FALSE(EC);// Convert to POSIX path for comparison of windows pathsASSERT_EQ("relative/path.h",getPosixPath(std::string(I->path().substr(CWD.size() + 1))));// Relative directory path.FS = getFromYAMLString("{ 'roots': [\n"" { 'type': 'directory', 'name': 'relative/directory/path.h',\n"" 'contents': []\n"" }\n""] }",Lower);ASSERT_TRUE(FS.get() != nullptr);SmallString<128> Root("relative/directory");llvm::sys::fs::make_absolute(Root);I = FS->dir_begin(Root, EC);ASSERT_FALSE(EC);ASSERT_EQ("path.h", std::string(I->path().substr(Root.size() + 1)));EXPECT_EQ(0, NumDiagnostics);}TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/");Lower->addRegularFile("//root/a");Lower->addRegularFile("//root/b");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'fallthrough': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'c',\n"" 'external-contents': '//root/a'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);std::error_code EC;checkContents(FS->dir_begin("//root/", EC),{"//root/c"});}TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/");Lower->addRegularFile("//root/a");Lower->addRegularFile("//root/b");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'a',\n"" 'external-contents': '//root/a'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);std::error_code EC;checkContents(FS->dir_begin("//root/", EC),{"//root/a", "//root/b"});}TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/");Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addRegularFile("//root/foo/b");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'bar/a',\n"" 'external-contents': '//root/foo/a'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);std::error_code EC;checkContents(FS->dir_begin("//root/foo", EC),{"//root/foo/a", "//root/foo/b"});}TEST_F(VFSFromYAMLTest, GetRealPath) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//dir/");Lower->addRegularFile("/foo");Lower->addSymlink("/link");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'bar',\n"" 'external-contents': '/link'\n"" }\n"" ]\n""},\n""{\n"" 'type': 'directory',\n"" 'name': '//dir/',\n"" 'contents': []\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);// Regular file present in underlying file system.SmallString<16> RealPath;EXPECT_FALSE(FS->getRealPath("/foo", RealPath));EXPECT_EQ(RealPath.str(), "/foo");// File present in YAML pointing to symlink in underlying file system.EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath));EXPECT_EQ(RealPath.str(), "/symlink");// Directories should fall back to the underlying file system is possible.EXPECT_FALSE(FS->getRealPath("//dir/", RealPath));EXPECT_EQ(RealPath.str(), "//dir/");// Try a non-existing file.EXPECT_EQ(FS->getRealPath("/non_existing", RealPath),errc::no_such_file_or_directory);}TEST_F(VFSFromYAMLTest, WorkingDirectory) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/");Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addRegularFile("//root/foo/b");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/bar',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'a',\n"" 'external-contents': '//root/foo/a'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");ASSERT_FALSE(EC);llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory();ASSERT_TRUE(WorkingDir);EXPECT_EQ(*WorkingDir, "//root/bar");llvm::ErrorOr<vfs::Status> Status = FS->status("./a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->isStatusKnown());EXPECT_FALSE(Status->isDirectory());EXPECT_TRUE(Status->isRegularFile());EXPECT_FALSE(Status->isSymlink());EXPECT_FALSE(Status->isOther());EXPECT_TRUE(Status->exists());EC = FS->setCurrentWorkingDirectory("bogus");ASSERT_TRUE(EC);WorkingDir = FS->getCurrentWorkingDirectory();ASSERT_TRUE(WorkingDir);EXPECT_EQ(*WorkingDir, "//root/bar");EC = FS->setCurrentWorkingDirectory("//root/");ASSERT_FALSE(EC);WorkingDir = FS->getCurrentWorkingDirectory();ASSERT_TRUE(WorkingDir);EXPECT_EQ(*WorkingDir, "//root/");EC = FS->setCurrentWorkingDirectory("bar");ASSERT_FALSE(EC);WorkingDir = FS->getCurrentWorkingDirectory();ASSERT_TRUE(WorkingDir);EXPECT_EQ(*WorkingDir, "//root/bar");}TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) {IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());Lower->addDirectory("//root/");Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addRegularFile("//root/foo/b");Lower->addRegularFile("//root/c");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/bar',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'a',\n"" 'external-contents': '//root/foo/a'\n"" }\n"" ]\n""},\n""{\n"" 'type': 'directory',\n"" 'name': '//root/bar/baz',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'a',\n"" 'external-contents': '//root/foo/a'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);std::error_code EC = FS->setCurrentWorkingDirectory("//root/");ASSERT_FALSE(EC);ASSERT_NE(FS.get(), nullptr);llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());Status = FS->status("foo/a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());EC = FS->setCurrentWorkingDirectory("//root/bar");ASSERT_FALSE(EC);Status = FS->status("./a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());Status = FS->status("./b");ASSERT_TRUE(Status.getError());Status = FS->status("./c");ASSERT_TRUE(Status.getError());EC = FS->setCurrentWorkingDirectory("//root/");ASSERT_FALSE(EC);Status = FS->status("c");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());Status = FS->status("./bar/baz/a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());EC = FS->setCurrentWorkingDirectory("//root/bar");ASSERT_FALSE(EC);Status = FS->status("./baz/a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());Status = FS->status("../bar/baz/a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());}TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) {IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());Lower->addDirectory("//root/");Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addRegularFile("//root/foo/b");Lower->addRegularFile("//root/c");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/bar',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'a',\n"" 'external-contents': '//root/foo/a'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);std::error_code EC = FS->setCurrentWorkingDirectory("//root/");ASSERT_FALSE(EC);ASSERT_NE(FS.get(), nullptr);llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());Status = FS->status("foo/a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());}TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) {IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());Lower->addDirectory("//root/");Lower->addDirectory("//root/foo");Lower->addRegularFile("//root/foo/a");Lower->addRegularFile("//root/foo/b");Lower->addRegularFile("//root/c");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n"" 'roots': [\n""{\n"" 'type': 'directory',\n"" 'name': '//root/bar',\n"" 'contents': [ {\n"" 'type': 'file',\n"" 'name': 'a',\n"" 'external-contents': '//root/foo/a'\n"" }\n"" ]\n""}\n""]\n""}",Lower);ASSERT_NE(FS.get(), nullptr);std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");ASSERT_FALSE(EC);ASSERT_NE(FS.get(), nullptr);llvm::ErrorOr<vfs::Status> Status = FS->status("a");ASSERT_FALSE(Status.getError());EXPECT_TRUE(Status->exists());}TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);TempDir _a(TestDirectory.path("a"));TempFile _ab(TestDirectory.path("a, b"));TempDir _c(TestDirectory.path("c"));TempFile _cd(TestDirectory.path("c/d"));TempDir _e(TestDirectory.path("e"));TempDir _ef(TestDirectory.path("e/f"));TempFile _g(TestDirectory.path("g"));TempDir _h(TestDirectory.path("h"));vfs::YAMLVFSWriter VFSWriter;VFSWriter.addDirectoryMapping(_a.path(), "//root/a");VFSWriter.addFileMapping(_ab.path(), "//root/a/b");VFSWriter.addFileMapping(_cd.path(), "//root/c/d");VFSWriter.addDirectoryMapping(_e.path(), "//root/e");VFSWriter.addDirectoryMapping(_ef.path(), "//root/e/f");VFSWriter.addFileMapping(_g.path(), "//root/g");VFSWriter.addDirectoryMapping(_h.path(), "//root/h");std::string Buffer;raw_string_ostream OS(Buffer);VFSWriter.write(OS);OS.flush();IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());Lower->addDirectory("//root/");Lower->addDirectory("//root/a");Lower->addRegularFile("//root/a/b");Lower->addDirectory("//root/b");Lower->addDirectory("//root/c");Lower->addRegularFile("//root/c/d");Lower->addDirectory("//root/e");Lower->addDirectory("//root/e/f");Lower->addRegularFile("//root/g");Lower->addDirectory("//root/h");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);ASSERT_NE(FS.get(), nullptr);EXPECT_TRUE(FS->exists(_a.path()));EXPECT_TRUE(FS->exists(_ab.path()));EXPECT_TRUE(FS->exists(_c.path()));EXPECT_TRUE(FS->exists(_cd.path()));EXPECT_TRUE(FS->exists(_e.path()));EXPECT_TRUE(FS->exists(_ef.path()));EXPECT_TRUE(FS->exists(_g.path()));EXPECT_TRUE(FS->exists(_h.path()));}TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);TempDir _a(TestDirectory.path("a"));TempFile _ab(TestDirectory.path("a/b"));TempDir _ac(TestDirectory.path("a/c"));TempFile _acd(TestDirectory.path("a/c/d"));TempFile _ace(TestDirectory.path("a/c/e"));TempFile _acf(TestDirectory.path("a/c/f"));TempDir _ag(TestDirectory.path("a/g"));TempFile _agh(TestDirectory.path("a/g/h"));vfs::YAMLVFSWriter VFSWriter;VFSWriter.addDirectoryMapping(_a.path(), "//root/a");VFSWriter.addFileMapping(_ab.path(), "//root/a/b");VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d");VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e");VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f");VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g");VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h");std::string Buffer;raw_string_ostream OS(Buffer);VFSWriter.write(OS);OS.flush();IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);EXPECT_NE(FS.get(), nullptr);}TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);TempDir _a(TestDirectory.path("a"));TempFile _ab(TestDirectory.path("a/b"));TempDir _ac(TestDirectory.path("a/c"));TempDir _acd(TestDirectory.path("a/c/d"));TempDir _acde(TestDirectory.path("a/c/d/e"));TempFile _acdef(TestDirectory.path("a/c/d/e/f"));TempFile _acdeg(TestDirectory.path("a/c/d/e/g"));TempDir _ah(TestDirectory.path("a/h"));TempFile _ahi(TestDirectory.path("a/h/i"));vfs::YAMLVFSWriter VFSWriter;VFSWriter.addDirectoryMapping(_a.path(), "//root/a");VFSWriter.addFileMapping(_ab.path(), "//root/a/b");VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d");VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e");VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f");VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g");VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h");VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i");std::string Buffer;raw_string_ostream OS(Buffer);VFSWriter.write(OS);OS.flush();IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);EXPECT_NE(FS.get(), nullptr);}TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) {TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);TempDir _a(TestDirectory.path("a"));TempDir _b(TestDirectory.path("b"));TempDir _c(TestDirectory.path("c"));vfs::YAMLVFSWriter VFSWriter;VFSWriter.addDirectoryMapping(_a.path(), "//root/a");VFSWriter.addDirectoryMapping(_b.path(), "//root/b");VFSWriter.addDirectoryMapping(_c.path(), "//root/c");std::string Buffer;raw_string_ostream OS(Buffer);VFSWriter.write(OS);OS.flush();// We didn't add a single file - only directories.EXPECT_EQ(Buffer.find("'type': 'file'"), std::string::npos);IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());Lower->addDirectory("//root/a");Lower->addDirectory("//root/b");Lower->addDirectory("//root/c");// canariesLower->addRegularFile("//root/a/a");Lower->addRegularFile("//root/b/b");Lower->addRegularFile("//root/c/c");IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);ASSERT_NE(FS.get(), nullptr);EXPECT_FALSE(FS->exists(_a.path("a")));EXPECT_FALSE(FS->exists(_b.path("b")));EXPECT_FALSE(FS->exists(_c.path("c")));}TEST_F(VFSFromYAMLTest, RedirectingWith) {IntrusiveRefCntPtr<DummyFileSystem> Both(new DummyFileSystem());Both->addDirectory("//root/a");Both->addRegularFile("//root/a/f");Both->addDirectory("//root/b");Both->addRegularFile("//root/b/f");IntrusiveRefCntPtr<DummyFileSystem> AOnly(new DummyFileSystem());AOnly->addDirectory("//root/a");AOnly->addRegularFile("//root/a/f");IntrusiveRefCntPtr<DummyFileSystem> BOnly(new DummyFileSystem());BOnly->addDirectory("//root/b");BOnly->addRegularFile("//root/b/f");auto BaseStr = std::string(" 'roots': [\n"" {\n"" 'type': 'directory-remap',\n"" 'name': '//root/a',\n"" 'external-contents': '//root/b'\n"" }\n"" ]\n""}");auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr;auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr;auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr;auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected,StringRef Message) {auto AF = FS.openFileForRead("//root/a/f");ASSERT_FALSE(AF.getError()) << Message;auto AFName = (*AF)->getName();ASSERT_FALSE(AFName.getError()) << Message;EXPECT_EQ(Expected.str(), AFName.get()) << Message;auto AS = FS.status("//root/a/f");ASSERT_FALSE(AS.getError()) << Message;EXPECT_EQ(Expected.str(), AS->getName()) << Message;};auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) {EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message;EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message;};{// `f` in both `a` and `b`// `fallthrough` tries `external-name` first, so should be `b`IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =getFromYAMLString(FallthroughStr, Both);ASSERT_TRUE(Fallthrough.get() != nullptr);ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist");// `fallback` tries the original name first, so should be `a`IntrusiveRefCntPtr<vfs::FileSystem> Fallback =getFromYAMLString(FallbackStr, Both);ASSERT_TRUE(Fallback.get() != nullptr);ExpectPath(*Fallback, "//root/a/f", "fallback, both exist");// `redirect-only` is the same as `fallthrough` but doesn't try the// original on failure, so no change here (ie. `b`)IntrusiveRefCntPtr<vfs::FileSystem> Redirect =getFromYAMLString(RedirectOnlyStr, Both);ASSERT_TRUE(Redirect.get() != nullptr);ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist");}{// `f` in `a` only// Fallthrough to the original path, `a`IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =getFromYAMLString(FallthroughStr, AOnly);ASSERT_TRUE(Fallthrough.get() != nullptr);ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only");// Original first, so still `a`IntrusiveRefCntPtr<vfs::FileSystem> Fallback =getFromYAMLString(FallbackStr, AOnly);ASSERT_TRUE(Fallback.get() != nullptr);ExpectPath(*Fallback, "//root/a/f", "fallback, a only");// Fails since no fallthroughIntrusiveRefCntPtr<vfs::FileSystem> Redirect =getFromYAMLString(RedirectOnlyStr, AOnly);ASSERT_TRUE(Redirect.get() != nullptr);ExpectFailure(*Redirect, "redirect-only, a only");}{// `f` in `b` only// Tries `b` first (no fallthrough)IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =getFromYAMLString(FallthroughStr, BOnly);ASSERT_TRUE(Fallthrough.get() != nullptr);ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only");// Tries original first but then fallsback to `b`IntrusiveRefCntPtr<vfs::FileSystem> Fallback =getFromYAMLString(FallbackStr, BOnly);ASSERT_TRUE(Fallback.get() != nullptr);ExpectPath(*Fallback, "//root/b/f", "fallback, b only");// Redirect exists, so uses it (`b`)IntrusiveRefCntPtr<vfs::FileSystem> Redirect =getFromYAMLString(RedirectOnlyStr, BOnly);ASSERT_TRUE(Redirect.get() != nullptr);ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only");}EXPECT_EQ(0, NumDiagnostics);}TEST(VFSFromRemappedFilesTest, Basic) {IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =new vfs::InMemoryFileSystem;BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));std::vector<std::pair<std::string, std::string>> RemappedFiles = {{"//root/a/a", "//root/b"},{"//root/a/b/c", "//root/c"},};auto RemappedFS = vfs::RedirectingFileSystem::create(RemappedFiles, /*UseExternalNames=*/false, *BaseFS);auto StatA = RemappedFS->status("//root/a/a");auto StatB = RemappedFS->status("//root/a/b/c");ASSERT_TRUE(StatA);ASSERT_TRUE(StatB);EXPECT_EQ("//root/a/a", StatA->getName());EXPECT_EQ("//root/a/b/c", StatB->getName());auto BufferA = RemappedFS->getBufferForFile("//root/a/a");auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");ASSERT_TRUE(BufferA);ASSERT_TRUE(BufferB);EXPECT_EQ("contents of b", (*BufferA)->getBuffer());EXPECT_EQ("contents of c", (*BufferB)->getBuffer());}TEST(VFSFromRemappedFilesTest, UseExternalNames) {IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =new vfs::InMemoryFileSystem;BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));std::vector<std::pair<std::string, std::string>> RemappedFiles = {{"//root/a/a", "//root/b"},{"//root/a/b/c", "//root/c"},};auto RemappedFS = vfs::RedirectingFileSystem::create(RemappedFiles, /*UseExternalNames=*/true, *BaseFS);auto StatA = RemappedFS->status("//root/a/a");auto StatB = RemappedFS->status("//root/a/b/c");ASSERT_TRUE(StatA);ASSERT_TRUE(StatB);EXPECT_EQ("//root/b", StatA->getName());EXPECT_EQ("//root/c", StatB->getName());auto BufferA = RemappedFS->getBufferForFile("//root/a/a");auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");ASSERT_TRUE(BufferA);ASSERT_TRUE(BufferB);EXPECT_EQ("contents of b", (*BufferA)->getBuffer());EXPECT_EQ("contents of c", (*BufferB)->getBuffer());}TEST(VFSFromRemappedFilesTest, LastMappingWins) {IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =new vfs::InMemoryFileSystem;BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));std::vector<std::pair<std::string, std::string>> RemappedFiles = {{"//root/a", "//root/b"},{"//root/a", "//root/c"},};auto RemappedFSKeepName = vfs::RedirectingFileSystem::create(RemappedFiles, /*UseExternalNames=*/false, *BaseFS);auto RemappedFSExternalName = vfs::RedirectingFileSystem::create(RemappedFiles, /*UseExternalNames=*/true, *BaseFS);auto StatKeepA = RemappedFSKeepName->status("//root/a");auto StatExternalA = RemappedFSExternalName->status("//root/a");ASSERT_TRUE(StatKeepA);ASSERT_TRUE(StatExternalA);EXPECT_EQ("//root/a", StatKeepA->getName());EXPECT_EQ("//root/c", StatExternalA->getName());auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a");auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a");ASSERT_TRUE(BufferKeepA);ASSERT_TRUE(BufferExternalA);EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer());EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer());}TEST(RedirectingFileSystemTest, PrintOutput) {auto Buffer =MemoryBuffer::getMemBuffer("{\n"" 'version': 0,\n"" 'roots': [\n"" {\n"" 'type': 'directory-remap',\n"" 'name': '/dremap',\n"" 'external-contents': '/a',\n"" },"" {\n"" 'type': 'directory',\n"" 'name': '/vdir',\n"" 'contents': ["" {\n"" 'type': 'directory-remap',\n"" 'name': 'dremap',\n"" 'external-contents': '/b'\n"" 'use-external-name': 'true'\n"" },\n"" {\n"" 'type': 'file',\n"" 'name': 'vfile',\n"" 'external-contents': '/c'\n"" 'use-external-name': 'false'\n"" }]\n"" }]\n""}");auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>();auto Redirecting = vfs::RedirectingFileSystem::create(std::move(Buffer), nullptr, "", nullptr, Dummy);SmallString<0> Output;raw_svector_ostream OuputStream{Output};Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Summary);ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output);Output.clear();Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents);ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n""'/'\n"" 'dremap' -> '/a'\n"" 'vdir'\n"" 'dremap' -> '/b' (UseExternalName: true)\n"" 'vfile' -> '/c' (UseExternalName: false)\n""ExternalFS:\n"" DummyFileSystem (Summary)\n",Output);Output.clear();Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents, 1);ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n"" '/'\n"" 'dremap' -> '/a'\n"" 'vdir'\n"" 'dremap' -> '/b' (UseExternalName: true)\n"" 'vfile' -> '/c' (UseExternalName: false)\n"" ExternalFS:\n"" DummyFileSystem (Summary)\n",Output);Output.clear();Redirecting->print(OuputStream,vfs::FileSystem::PrintType::RecursiveContents);ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n""'/'\n"" 'dremap' -> '/a'\n"" 'vdir'\n"" 'dremap' -> '/b' (UseExternalName: true)\n"" 'vfile' -> '/c' (UseExternalName: false)\n""ExternalFS:\n"" DummyFileSystem (RecursiveContents)\n",Output);}
//===- VersionTupleTests.cpp - Version Number Handling Tests --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/VersionTuple.h"#include "gtest/gtest.h"using namespace llvm;TEST(VersionTuple, getAsString) {EXPECT_EQ("0", VersionTuple().getAsString());EXPECT_EQ("1", VersionTuple(1).getAsString());EXPECT_EQ("1.2", VersionTuple(1, 2).getAsString());EXPECT_EQ("1.2.3", VersionTuple(1, 2, 3).getAsString());EXPECT_EQ("1.2.3.4", VersionTuple(1, 2, 3, 4).getAsString());}TEST(VersionTuple, tryParse) {VersionTuple VT;EXPECT_FALSE(VT.tryParse("1"));EXPECT_EQ("1", VT.getAsString());EXPECT_FALSE(VT.tryParse("1.2"));EXPECT_EQ("1.2", VT.getAsString());EXPECT_FALSE(VT.tryParse("1.2.3"));EXPECT_EQ("1.2.3", VT.getAsString());EXPECT_FALSE(VT.tryParse("1.2.3.4"));EXPECT_EQ("1.2.3.4", VT.getAsString());EXPECT_TRUE(VT.tryParse(""));EXPECT_TRUE(VT.tryParse("1."));EXPECT_TRUE(VT.tryParse("1.2."));EXPECT_TRUE(VT.tryParse("1.2.3."));EXPECT_TRUE(VT.tryParse("1.2.3.4."));EXPECT_TRUE(VT.tryParse("1.2.3.4.5"));EXPECT_TRUE(VT.tryParse("1-2"));EXPECT_TRUE(VT.tryParse("1+2"));EXPECT_TRUE(VT.tryParse(".1"));EXPECT_TRUE(VT.tryParse(" 1"));EXPECT_TRUE(VT.tryParse("1 "));EXPECT_TRUE(VT.tryParse("."));}
//===- unittests/Support/UnicodeTest.cpp - Unicode.h tests ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Unicode.h"#include "llvm/ADT/StringExtras.h"#include "llvm/ADT/edit_distance.h"#include "llvm/Support/ConvertUTF.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace sys {namespace unicode {namespace {TEST(Unicode, columnWidthUTF8) {EXPECT_EQ(0, columnWidthUTF8(""));EXPECT_EQ(1, columnWidthUTF8(" "));EXPECT_EQ(1, columnWidthUTF8("a"));EXPECT_EQ(1, columnWidthUTF8("~"));EXPECT_EQ(6, columnWidthUTF8("abcdef"));EXPECT_EQ(-1, columnWidthUTF8("\x01"));EXPECT_EQ(-1, columnWidthUTF8("\t"));EXPECT_EQ(-1, columnWidthUTF8("aaaaaaaaaa\x01"));EXPECT_EQ(-1, columnWidthUTF8("\342\200\213")); // 200B ZERO WIDTH SPACE// 00AD SOFT HYPHEN is displayed on most terminals as a space or a dash. Some// text editors display it only when a line is broken at it, some use it as a// line-break hint, but don't display. We choose terminal-oriented// interpretation.EXPECT_EQ(1, columnWidthUTF8("\302\255"));EXPECT_EQ(0, columnWidthUTF8("\314\200")); // 0300 COMBINING GRAVE ACCENTEXPECT_EQ(1, columnWidthUTF8("\340\270\201")); // 0E01 THAI CHARACTER KO KAIEXPECT_EQ(2, columnWidthUTF8("\344\270\200")); // CJK UNIFIED IDEOGRAPH-4E00EXPECT_EQ(4, columnWidthUTF8("\344\270\200\344\270\200"));EXPECT_EQ(3, columnWidthUTF8("q\344\270\200"));EXPECT_EQ(3, columnWidthUTF8("\314\200\340\270\201\344\270\200"));// Invalid UTF-8 strings, columnWidthUTF8 should error out.EXPECT_EQ(-2, columnWidthUTF8("\344"));EXPECT_EQ(-2, columnWidthUTF8("\344\270"));EXPECT_EQ(-2, columnWidthUTF8("\344\270\033"));EXPECT_EQ(-2, columnWidthUTF8("\344\270\300"));EXPECT_EQ(-2, columnWidthUTF8("\377\366\355"));EXPECT_EQ(-2, columnWidthUTF8("qwer\344"));EXPECT_EQ(-2, columnWidthUTF8("qwer\344\270"));EXPECT_EQ(-2, columnWidthUTF8("qwer\344\270\033"));EXPECT_EQ(-2, columnWidthUTF8("qwer\344\270\300"));EXPECT_EQ(-2, columnWidthUTF8("qwer\377\366\355"));// UTF-8 sequences longer than 4 bytes correspond to unallocated Unicode// characters.EXPECT_EQ(-2, columnWidthUTF8("\370\200\200\200\200")); // U+200000EXPECT_EQ(-2, columnWidthUTF8("\374\200\200\200\200\200")); // U+4000000}TEST(Unicode, isPrintable) {EXPECT_FALSE(isPrintable(0)); // <control-0000>-<control-001F>EXPECT_FALSE(isPrintable(0x01));EXPECT_FALSE(isPrintable(0x1F));EXPECT_TRUE(isPrintable(' '));EXPECT_TRUE(isPrintable('A'));EXPECT_TRUE(isPrintable('~'));EXPECT_FALSE(isPrintable(0x7F)); // <control-007F>..<control-009F>EXPECT_FALSE(isPrintable(0x90));EXPECT_FALSE(isPrintable(0x9F));EXPECT_TRUE(isPrintable(0xAC));EXPECT_TRUE(isPrintable(0xAD)); // SOFT HYPHEN is displayed on most terminals// as either a space or a dash.EXPECT_TRUE(isPrintable(0xAE));EXPECT_TRUE(isPrintable(0x0377)); // GREEK SMALL LETTER PAMPHYLIAN DIGAMMAEXPECT_FALSE(isPrintable(0x0378)); // <reserved-0378>..<reserved-0379>EXPECT_FALSE(isPrintable(0x0600)); // ARABIC NUMBER SIGNEXPECT_FALSE(isPrintable(0x1FFFF)); // <reserved-1F774>..<noncharacter-1FFFF>EXPECT_TRUE(isPrintable(0x20000)); // CJK UNIFIED IDEOGRAPH-20000EXPECT_FALSE(isPrintable(0x10FFFF)); // noncharacter// test the validity of a fast path in columnWidthUTF8for (unsigned char c = 0; c < 128; ++c) {const UTF8 buf8[2] = {c, 0};const UTF8 *Target8 = &buf8[0];UTF32 buf32[1];UTF32 *Target32 = &buf32[0];auto status = ConvertUTF8toUTF32(&Target8, Target8 + 1, &Target32,Target32 + 1, strictConversion);EXPECT_EQ(status, conversionOK);EXPECT_EQ((columnWidthUTF8(reinterpret_cast<const char *>(buf8)) == 1),(bool)isPrintable(buf32[0]));}}TEST(Unicode, nameToCodepointStrict) {auto map = [](StringRef Str) {return nameToCodepointStrict(Str).value_or(0xFFFF'FFFF);};// generated codepointsEXPECT_EQ(0x03400u, map("CJK UNIFIED IDEOGRAPH-3400"));EXPECT_EQ(0x04DBFu, map("CJK UNIFIED IDEOGRAPH-4DBF"));EXPECT_EQ(0x04E00u, map("CJK UNIFIED IDEOGRAPH-4E00"));EXPECT_EQ(0x09FFCu, map("CJK UNIFIED IDEOGRAPH-9FFC"));EXPECT_EQ(0x20000u, map("CJK UNIFIED IDEOGRAPH-20000"));EXPECT_EQ(0x2A6DDu, map("CJK UNIFIED IDEOGRAPH-2A6DD"));EXPECT_EQ(0x2A700u, map("CJK UNIFIED IDEOGRAPH-2A700"));EXPECT_EQ(0x2B740u, map("CJK UNIFIED IDEOGRAPH-2B740"));EXPECT_EQ(0x2B81Du, map("CJK UNIFIED IDEOGRAPH-2B81D"));EXPECT_EQ(0x2B820u, map("CJK UNIFIED IDEOGRAPH-2B820"));EXPECT_EQ(0x2CEA1u, map("CJK UNIFIED IDEOGRAPH-2CEA1"));EXPECT_EQ(0x2CEB0u, map("CJK UNIFIED IDEOGRAPH-2CEB0"));EXPECT_EQ(0x2EBE0u, map("CJK UNIFIED IDEOGRAPH-2EBE0"));EXPECT_EQ(0x30000u, map("CJK UNIFIED IDEOGRAPH-30000"));EXPECT_EQ(0x3134Au, map("CJK UNIFIED IDEOGRAPH-3134A"));EXPECT_EQ(0x17000u, map("TANGUT IDEOGRAPH-17000"));EXPECT_EQ(0x187F7u, map("TANGUT IDEOGRAPH-187F7"));EXPECT_EQ(0x18D00u, map("TANGUT IDEOGRAPH-18D00"));EXPECT_EQ(0x18D08u, map("TANGUT IDEOGRAPH-18D08"));EXPECT_EQ(0x18B00u, map("KHITAN SMALL SCRIPT CHARACTER-18B00"));EXPECT_EQ(0x18CD5u, map("KHITAN SMALL SCRIPT CHARACTER-18CD5"));EXPECT_EQ(0x1B170u, map("NUSHU CHARACTER-1B170"));EXPECT_EQ(0x1B2FBu, map("NUSHU CHARACTER-1B2FB"));EXPECT_EQ(0x0F900u, map("CJK COMPATIBILITY IDEOGRAPH-F900"));EXPECT_EQ(0x0FA6Du, map("CJK COMPATIBILITY IDEOGRAPH-FA6D"));EXPECT_EQ(0x0FA70u, map("CJK COMPATIBILITY IDEOGRAPH-FA70"));EXPECT_EQ(0x0FAD9u, map("CJK COMPATIBILITY IDEOGRAPH-FAD9"));EXPECT_EQ(0x2F800u, map("CJK COMPATIBILITY IDEOGRAPH-2F800"));EXPECT_EQ(0x2FA1Du, map("CJK COMPATIBILITY IDEOGRAPH-2FA1D"));EXPECT_EQ(0xAC00u, map("HANGUL SYLLABLE GA"));EXPECT_EQ(0xAC14u, map("HANGUL SYLLABLE GASS"));EXPECT_EQ(0xAC2Bu, map("HANGUL SYLLABLE GAELH"));EXPECT_EQ(0xAC7Bu, map("HANGUL SYLLABLE GEOLB"));EXPECT_EQ(0xC640u, map("HANGUL SYLLABLE WA"));EXPECT_EQ(0xC544u, map("HANGUL SYLLABLE A"));EXPECT_EQ(0xC5D0u, map("HANGUL SYLLABLE E"));EXPECT_EQ(0xC774u, map("HANGUL SYLLABLE I"));EXPECT_EQ(0x1F984u, map("UNICORN FACE"));EXPECT_EQ(0x00640u, map("ARABIC TATWEEL"));EXPECT_EQ(0x02C05u, map("GLAGOLITIC CAPITAL LETTER YESTU"));EXPECT_EQ(0x13000u, map("EGYPTIAN HIEROGLYPH A001"));EXPECT_EQ(0x02235u, map("BECAUSE"));EXPECT_EQ(0x1F514u, map("BELL"));EXPECT_EQ(0x1F9A9u, map("FLAMINGO"));EXPECT_EQ(0x1F402u, map("OX")); // 2 charactersEXPECT_EQ(0x0FBF9u, map("ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA ISOLATED FORM"));// AliasesEXPECT_EQ(0x0000u, map("NULL"));EXPECT_EQ(0x0007u, map("ALERT"));EXPECT_EQ(0x0009u, map("HORIZONTAL TABULATION"));EXPECT_EQ(0x0009u, map("CHARACTER TABULATION"));EXPECT_EQ(0x000Au, map("LINE FEED"));EXPECT_EQ(0x000Au, map("NEW LINE"));EXPECT_EQ(0x0089u, map("CHARACTER TABULATION WITH JUSTIFICATION"));EXPECT_EQ(0x0089u, map("HORIZONTAL TABULATION WITH JUSTIFICATION"));EXPECT_EQ(0x2118u,map("WEIERSTRASS ELLIPTIC FUNCTION")); // correctionEXPECT_EQ(0x2118u, map("SCRIPT CAPITAL P")); // correctionEXPECT_EQ(0xFEFFu, map("BYTE ORDER MARK")); // alternateEXPECT_EQ(0xFEFFu, map("ZERO WIDTH NO-BREAK SPACE")); // alternate// Should perform exact case matchEXPECT_EQ(0xFFFFFFFFu, map(""));EXPECT_EQ(0xFFFFFFFFu, map("NOT A UNICODE CHARACTER"));EXPECT_EQ(0xFFFFFFFFu, map("unicorn face"));EXPECT_EQ(0xFFFFFFFFu, map("UNICORN FaCE"));EXPECT_EQ(0xFFFFFFFFu, map("UNICORNFaCE"));EXPECT_EQ(0xFFFFFFFFu, map("UNICORN"));EXPECT_EQ(0xFFFFFFFFu, map("HANGUL SYLLABLE i"));EXPECT_EQ(0xFFFFFFFFu, map("hANGUL SYLLABLE i"));EXPECT_EQ(0xFFFFFFFFu, map("HANGULSYLLABLEI"));EXPECT_EQ(0xFFFFFFFFu, map("HANGUL SYLLABLE"));EXPECT_EQ(0xFFFFFFFFu, map("cJK COMPATIBILITY IDEOGRAPH-2FA1D"));EXPECT_EQ(0xFFFFFFFFu, map("CJK COMPATIBILITY IDEOGRAPH-2FA1d"));EXPECT_EQ(0xFFFFFFFFu, map("CJK COMPATIBILITY IDEOGRAPH 2FA1D"));EXPECT_EQ(0xFFFFFFFF, map("CJK COMPATIBILITY IDEOGRAPH-NOTANUMBER"));EXPECT_EQ(0xFFFFFFFFu, map("CJK COMPATIBILITY IDEOGRAPH-1"));EXPECT_EQ(0xFFFFFFFFu, map("ZERO WIDTH NO BREAK SPACE"));// Should not support abbreviations or figmentsEXPECT_EQ(0xFFFFFFFFu, map("FVS1"));EXPECT_EQ(0xFFFFFFFFu, map("HIGH OCTET PRESET"));EXPECT_EQ(0xFFFFFFFFu, map("BEL"));}TEST(Unicode, nameToCodepointLoose) {auto map = [](StringRef Str) {auto Opt = nameToCodepointLooseMatching(Str);if (!Opt)return char32_t(0xFFFF'FFFF);return Opt->CodePoint;};// generated codepointsEXPECT_EQ(0x04DBFu, map("CJK UNIFIED IDEOGRAPH-4DBF"));EXPECT_EQ(0x04E00u, map("CJK UNIFIED IDEOGRAPH-4E00"));EXPECT_EQ(0x09FFCu, map("CJK UNIFIED IDEOGRAPH-9FFC"));EXPECT_EQ(0x20000u, map("CJK UNIFIED IDEOGRAPH-20000"));EXPECT_EQ(0x2A6DDu, map("CJK UNIFIED IDEOGRAPH-2A6DD"));EXPECT_EQ(0x2A700u, map("CJK UNIFIED IDEOGRAPH-2A700"));EXPECT_EQ(0x2B740u, map("CJK UNIFIED IDEOGRAPH-2B740"));EXPECT_EQ(0x03400u, map("CJK UNIFIED IDEOGRAPH-3400"));EXPECT_EQ(0x2B81Du, map("CJK UNIFIED IDEOGRAPH-2B81D"));EXPECT_EQ(0x2B820u, map("CJK UNIFIED IDEOGRAPH-2B820"));EXPECT_EQ(0x2CEA1u, map("CJK UNIFIED IDEOGRAPH-2CEA1"));EXPECT_EQ(0x2CEB0u, map("CJK UNIFIED IDEOGRAPH-2CEB0"));EXPECT_EQ(0x2EBE0u, map("CJK UNIFIED IDEOGRAPH-2EBE0"));EXPECT_EQ(0x30000u, map("CJK UNIFIED IDEOGRAPH-30000"));EXPECT_EQ(0x3134Au, map("CJK UNIFIED IDEOGRAPH-3134A"));EXPECT_EQ(0x17000u, map("TANGUT IDEOGRAPH-17000"));EXPECT_EQ(0x187F7u, map("TANGUT IDEOGRAPH-187F7"));EXPECT_EQ(0x18D00u, map("TANGUT IDEOGRAPH-18D00"));EXPECT_EQ(0x18D08u, map("TANGUT IDEOGRAPH-18D08"));EXPECT_EQ(0x18B00u, map("KHITAN SMALL SCRIPT CHARACTER-18B00"));EXPECT_EQ(0x18CD5u, map("KHITAN SMALL SCRIPT CHARACTER-18CD5"));EXPECT_EQ(0x1B170u, map("NUSHU CHARACTER-1B170"));EXPECT_EQ(0x1B2FBu, map("NUSHU CHARACTER-1B2FB"));EXPECT_EQ(0x0F900u, map("CJK COMPATIBILITY IDEOGRAPH-F900"));EXPECT_EQ(0x0FA6Du, map("CJK COMPATIBILITY IDEOGRAPH-FA6D"));EXPECT_EQ(0x0FA70u, map("CJK COMPATIBILITY IDEOGRAPH-FA70"));EXPECT_EQ(0x0FAD9u, map("CJK COMPATIBILITY IDEOGRAPH-FAD9"));EXPECT_EQ(0x2F800u, map("CJK COMPATIBILITY IDEOGRAPH-2F800"));EXPECT_EQ(0x2FA1Du, map("CJK COMPATIBILITY IDEOGRAPH-2FA1D"));EXPECT_EQ(0xAC00u, map("HANGUL SYLLABLE GA"));EXPECT_EQ(0xAC14u, map("HANGUL SYLLABLE GASS"));EXPECT_EQ(0xAC2Bu, map("HANGUL SYLLABLE GAELH"));EXPECT_EQ(0xAC7Bu, map("HANGUL SYLLABLE GEOLB"));EXPECT_EQ(0xC640u, map("HANGUL SYLLABLE WA"));EXPECT_EQ(0xC544u, map("HANGUL SYLLABLE A"));EXPECT_EQ(0xC5D0u, map("HANGUL SYLLABLE E"));EXPECT_EQ(0xC774u, map("HANGUL SYLLABLE I"));EXPECT_EQ(0x1F984u, map("UNICORN FACE"));EXPECT_EQ(0x00640u, map("ARABIC TATWEEL"));EXPECT_EQ(0x02C05u, map("GLAGOLITIC CAPITAL LETTER YESTU"));EXPECT_EQ(0x13000u, map("EGYPTIAN HIEROGLYPH A001"));EXPECT_EQ(0x02235u, map("BECAUSE"));EXPECT_EQ(0x1F514u, map("BELL"));EXPECT_EQ(0x1F9A9u, map("FLAMINGO"));EXPECT_EQ(0x1F402u, map("OX")); // 2 charactersEXPECT_EQ(0x0FBF9u, map("ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA ISOLATED FORM"));// AliasesEXPECT_EQ(0x0000u, map("NULL"));EXPECT_EQ(0x0007u, map("ALERT"));EXPECT_EQ(0x0009u, map("HORIZONTAL TABULATION"));EXPECT_EQ(0x0009u, map("CHARACTER TABULATION"));EXPECT_EQ(0x000Au, map("LINE FEED"));EXPECT_EQ(0x000Au, map("NEW LINE"));EXPECT_EQ(0x0089u, map("CHARACTER TABULATION WITH JUSTIFICATION"));EXPECT_EQ(0x0089u, map("HORIZONTAL TABULATION WITH JUSTIFICATION"));EXPECT_EQ(0x2118u,map("WEIERSTRASS ELLIPTIC FUNCTION")); // correctionEXPECT_EQ(0x2118u, map("SCRIPT CAPITAL P")); // correctionEXPECT_EQ(0xFEFFu, map("BYTE ORDER MARK")); // alternateEXPECT_EQ(0xFEFFu, map("ZERO WIDTH NO-BREAK SPACE")); // alternateEXPECT_EQ(0xFEFFu, map("ZERO WIDTH NO BREAK SPACE")); // alternate// Should perform loose matchingEXPECT_EQ(0xFFFFFFFFu, map(""));EXPECT_EQ(0xFFFFFFFFu, map("NOT A UNICODE CHARACTER"));EXPECT_EQ(0x0001F984u, map("unicorn face"));EXPECT_EQ(0x0001F984u, map("UNICORN FaCE"));EXPECT_EQ(0x0001F984u, map("UNICORNFaCE"));EXPECT_EQ(0xFFFFFFFFu, map("UNICORN"));EXPECT_EQ(0xC774u, map("HANGUL SYLLABLE i"));EXPECT_EQ(0xC774u, map("hANGUL SYLLABLE i"));EXPECT_EQ(0xC774u, map("HANGULSYLLABLEI"));EXPECT_EQ(0xFFFFFFFFu, map("HANGUL SYLLABLE"));EXPECT_EQ(0x2FA1Du, map("cJK COMPATIBILITY IDEOGRAPH-2FA1D"));EXPECT_EQ(0x2FA1Du, map("CJK COMPATIBILITY IDEOGRAPH-2FA1d"));EXPECT_EQ(0x2FA1Du, map("CJK COMPATIBILITY IDEOGRAPH 2FA1D"));EXPECT_EQ(0xFFFFFFFFu, map("CJK COMPATIBILITY IDEOGRAPH-NOTANUMBER"));EXPECT_EQ(0xFFFFFFFFu, map("CJK COMPATIBILITY IDEOGRAPH-1"));// https://unicode.org/reports/tr44/#Matching_Names// UAX44-LM2: Medial hypens are ignored, non medial hyphens are notEXPECT_EQ(0x1FBC5u, map("S-T-I-C-K-F-I-G-U-R-E"));EXPECT_EQ(0xFFFFFFFFu, map("-STICK FIGURE"));EXPECT_EQ(0xFFFFFFFFu, map("STICK FIGURE-"));EXPECT_EQ(0xFFFFFFFFu, map("STICK FIGURE -"));EXPECT_EQ(0xFFFFFFFFu, map("STICK FIGURE --"));EXPECT_EQ(0xFFFFFFFFu, map("STICK--FIGURE"));EXPECT_EQ(0x0F68u, map("TIBETAN LETTER A"));EXPECT_EQ(0x0F68u, map("TIBETAN LETTERA"));EXPECT_EQ(0x0F68u, map("TIBETAN LETTER-A"));EXPECT_EQ(0x0F60u, map("TIBETAN LETTER -A"));EXPECT_EQ(0x0F60u, map("TIBETAN LETTER -A"));;// special caseEXPECT_EQ(0x1180u, map("HANGUL JUNGSEONG O-E"));EXPECT_EQ(0x116Cu, map("HANGUL JUNGSEONG OE"));// names that are prefix to existing characters should not matchEXPECT_FALSE(nameToCodepointLooseMatching("B"));EXPECT_FALSE(nameToCodepointLooseMatching("BE"));EXPECT_FALSE(nameToCodepointLooseMatching("BEE"));EXPECT_FALSE(nameToCodepointLooseMatching("BEET"));EXPECT_FALSE(nameToCodepointLooseMatching("BEETL"));EXPECT_TRUE(nameToCodepointLooseMatching("BEETLE"));}} // namespacebool operator==(MatchForCodepointName a, MatchForCodepointName b) {return a.Name == b.Name && a.Distance == b.Distance && a.Value == b.Value;}namespace {TEST(Unicode, nearestMatchesForCodepointName) {auto Normalize = [](StringRef Name) {std::string Out;Out.reserve(Name.size());for (char C : Name) {if (isAlnum(C))Out.push_back(toUpper(C));}return Out;};auto L = [&](StringRef name) {auto v = nearestMatchesForCodepointName(name, 3);for (auto &r : v) {auto A = Normalize(r.Name);auto B = Normalize(name);EXPECT_EQ(StringRef(A).edit_distance(B, true), r.Distance);}return v;};using ::testing::ElementsAre;using M = MatchForCodepointName;ASSERT_THAT(L(""), ElementsAre(M{"OX", 2, 0x1F402}, M{"ANT", 3, 0x1F41C},M{"ARC", 3, 0x2312}));// shortest nameASSERT_THAT(L("OX"), ElementsAre(M{"OX", 0, 0x1F402}, M{"AXE", 2, 0x1FA93},M{"BOY", 2, 0x1F466}));// longest nameASSERT_THAT(L("ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF ""MAKSURA INITIAL FORM"),ElementsAre(M{"ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA INITIAL FORM",0, 0xFBFB},M{"ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA FINAL FORM",4, 0xFBFA},M{"ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA ISOLATED FORM",7, 0xFBF9}));// same result with underscore, spaces, etcASSERT_THAT(L("______ARABICLIGATUREUIGHUR KIRGHIZ YEH with HAMZA ABOVE WITH ""ALEF MAKsURAINITIAL form_"),ElementsAre(M{"ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA INITIAL FORM",0, 0xFBFB},M{"ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA FINAL FORM",4, 0xFBFA},M{"ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ""ABOVE WITH ALEF MAKSURA ISOLATED FORM",7, 0xFBF9}));ASSERT_THAT(L("GREEK CAPITAL LETTER LAMBDA"),ElementsAre(M{"GREEK CAPITAL LETTER LAMDA", 1, 0x39B},M{"GREEK CAPITAL LETTER GAMMA", 3, 0x0393},M{"GREEK CAPITAL LETTER ALPHA", 4, 0x0391}));ASSERT_THAT(L("greekcapitalletter-lambda"),ElementsAre(M{"GREEK CAPITAL LETTER LAMDA", 1, 0x39B},M{"GREEK CAPITAL LETTER GAMMA", 3, 0x0393},M{"GREEK CAPITAL LETTER ALPHA", 4, 0x0391}));// typo http://www.unicode.org/notes/tn27/tn27-5.htmlASSERT_THAT(L("PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET"),ElementsAre(M{"PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET", 0,0xFE18}, // typoM{"PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRACKET", 2,0xFE18}, // correctionM{"PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET", 6,0xFE17}));// typo http://www.unicode.org/notes/tn27/tn27-5.htmlASSERT_THAT(L("BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS"),ElementsAre(M{"BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS", 0, 0x1D0C5},M{"BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA VASIS", 2, 0x1D0C5},M{"BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI", 7,0x1D0C6}));}} // namespace} // namespace unicode} // namespace sys} // namespace llvm
//===- TypeTraitsTest.cpp -------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/FunctionExtras.h"#include "llvm/ADT/Optional.h"#include "llvm/ADT/PointerIntPair.h"#include "llvm/ADT/SmallString.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/StringRef.h"#include "llvm/Support/type_traits.h"#include "gtest/gtest.h"namespace {// Compile-time tests using static assert.namespace triviality {// Helper for compile time checking trivially copy constructible and trivially// move constructible type traits.template <typename T, bool IsTriviallyCopyConstructible,bool IsTriviallyMoveConstructible>void TrivialityTester() {static_assert(llvm::is_trivially_copy_constructible<T>::value ==IsTriviallyCopyConstructible,"Mismatch in expected trivial copy construction!");static_assert(llvm::is_trivially_move_constructible<T>::value ==IsTriviallyMoveConstructible,"Mismatch in expected trivial move construction!");#if defined(_LIBCPP_VERSION) || defined(_MSC_VER)// On compilers with support for the standard traits, make sure they agree.static_assert(std::is_trivially_copy_constructible<T>::value ==IsTriviallyCopyConstructible,"Mismatch in expected trivial copy construction!");static_assert(std::is_trivially_move_constructible<T>::value ==IsTriviallyMoveConstructible,"Mismatch in expected trivial move construction!");#endif}template void TrivialityTester<int, true, true>();template void TrivialityTester<void *, true, true>();template void TrivialityTester<int &, true, true>();template void TrivialityTester<int &&, false, true>();struct X {};struct Y {Y(const Y &);};struct Z {Z(const Z &);Z(Z &&);};struct A {A(const A &) = default;A(A &&);};struct B {B(const B &);B(B &&) = default;};template void TrivialityTester<X, true, true>();template void TrivialityTester<Y, false, false>();template void TrivialityTester<Z, false, false>();template void TrivialityTester<A, true, false>();template void TrivialityTester<B, false, true>();template void TrivialityTester<Z &, true, true>();template void TrivialityTester<A &, true, true>();template void TrivialityTester<B &, true, true>();template void TrivialityTester<Z &&, false, true>();template void TrivialityTester<A &&, false, true>();template void TrivialityTester<B &&, false, true>();TEST(Triviality, Tester) {TrivialityTester<int, true, true>();TrivialityTester<void *, true, true>();TrivialityTester<int &, true, true>();TrivialityTester<int &&, false, true>();TrivialityTester<X, true, true>();TrivialityTester<Y, false, false>();TrivialityTester<Z, false, false>();TrivialityTester<A, true, false>();TrivialityTester<B, false, true>();TrivialityTester<Z &, true, true>();TrivialityTester<A &, true, true>();TrivialityTester<B &, true, true>();TrivialityTester<Z &&, false, true>();TrivialityTester<A &&, false, true>();TrivialityTester<B &&, false, true>();}// Test that the following ADT behave as expected wrt. trivially copyable trait//// NB: It is important that this trait behaves the same for (at least) these// types for all supported compilers to prevent ABI issue when llvm is compiled// with compiler A and an other project using llvm is compiled with compiler B.TEST(Triviality, ADT) {TrivialityTester<llvm::SmallVector<int>, false, false>();TrivialityTester<llvm::SmallString<8>, false, false>();TrivialityTester<std::function<int()>, false, false>();#if !defined(__FreeBSD__)TrivialityTester<std::pair<int, bool>, true, true>();#endifTrivialityTester<llvm::unique_function<int()>, false, false>();TrivialityTester<llvm::StringRef, true, true>();TrivialityTester<llvm::ArrayRef<int>, true, true>();TrivialityTester<llvm::PointerIntPair<int *, 2>, true, true>();TrivialityTester<llvm::Optional<int>, true, true>();}} // namespace triviality} // end anonymous namespace
//===- TypeNameTest.cpp ---------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/TypeName.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {namespace N1 {struct S1 {};class C1 {};union U1 {};}TEST(TypeNameTest, Names) {struct S2 {};StringRef S1Name = getTypeName<N1::S1>();StringRef C1Name = getTypeName<N1::C1>();StringRef U1Name = getTypeName<N1::U1>();StringRef S2Name = getTypeName<S2>();#if defined(__clang__) || defined(__GNUC__) || defined(__INTEL_COMPILER) || \defined(_MSC_VER)EXPECT_TRUE(S1Name.endswith("::N1::S1")) << S1Name.str();EXPECT_TRUE(C1Name.endswith("::N1::C1")) << C1Name.str();EXPECT_TRUE(U1Name.endswith("::N1::U1")) << U1Name.str();#ifdef __clang__EXPECT_TRUE(S2Name.endswith("S2")) << S2Name.str();#elseEXPECT_TRUE(S2Name.endswith("::S2")) << S2Name.str();#endif#elseEXPECT_EQ("UNKNOWN_TYPE", S1Name);EXPECT_EQ("UNKNOWN_TYPE", C1Name);EXPECT_EQ("UNKNOWN_TYPE", U1Name);EXPECT_EQ("UNKNOWN_TYPE", S2Name);#endif}} // end anonymous namespace
//===- TrigramIndexTest.cpp - Unit tests for TrigramIndex -----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/TrigramIndex.h"#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"#include <string>#include <vector>using namespace llvm;namespace {class TrigramIndexTest : public ::testing::Test {protected:std::unique_ptr<TrigramIndex> makeTrigramIndex(std::vector<std::string> Rules) {std::unique_ptr<TrigramIndex> TI =std::make_unique<TrigramIndex>();for (auto &Rule : Rules)TI->insert(Rule);return TI;}};TEST_F(TrigramIndexTest, Empty) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({});EXPECT_FALSE(TI->isDefeated());EXPECT_TRUE(TI->isDefinitelyOut("foo"));}TEST_F(TrigramIndexTest, Basic) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"*hello*", "*wor.d*"});EXPECT_FALSE(TI->isDefeated());EXPECT_TRUE(TI->isDefinitelyOut("foo"));}TEST_F(TrigramIndexTest, NoTrigramsInRules) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"b.r", "za*az"});EXPECT_TRUE(TI->isDefeated());EXPECT_FALSE(TI->isDefinitelyOut("foo"));EXPECT_FALSE(TI->isDefinitelyOut("bar"));EXPECT_FALSE(TI->isDefinitelyOut("zakaz"));}TEST_F(TrigramIndexTest, NoTrigramsInARule) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"*hello*", "*wo.ld*"});EXPECT_TRUE(TI->isDefeated());EXPECT_FALSE(TI->isDefinitelyOut("foo"));}TEST_F(TrigramIndexTest, RepetitiveRule) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"*bar*bar*bar*bar*bar", "bar*bar"});EXPECT_FALSE(TI->isDefeated());EXPECT_TRUE(TI->isDefinitelyOut("foo"));EXPECT_TRUE(TI->isDefinitelyOut("bar"));EXPECT_FALSE(TI->isDefinitelyOut("barbara"));EXPECT_FALSE(TI->isDefinitelyOut("bar+bar"));}TEST_F(TrigramIndexTest, PopularTrigram) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"*aaa*", "*aaaa*", "*aaaaa*", "*aaaaa*", "*aaaaaa*"});EXPECT_TRUE(TI->isDefeated());}TEST_F(TrigramIndexTest, PopularTrigram2) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"class1.h", "class2.h", "class3.h", "class4.h", "class.h"});EXPECT_TRUE(TI->isDefeated());}TEST_F(TrigramIndexTest, TooComplicatedRegex) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"[0-9]+"});EXPECT_TRUE(TI->isDefeated());}TEST_F(TrigramIndexTest, TooComplicatedRegex2) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"foo|bar"});EXPECT_TRUE(TI->isDefeated());}TEST_F(TrigramIndexTest, EscapedSymbols) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"*c\\+\\+*", "*hello\\\\world*", "a\\tb", "a\\0b"});EXPECT_FALSE(TI->isDefeated());EXPECT_FALSE(TI->isDefinitelyOut("c++"));EXPECT_TRUE(TI->isDefinitelyOut("c\\+\\+"));EXPECT_FALSE(TI->isDefinitelyOut("hello\\world"));EXPECT_TRUE(TI->isDefinitelyOut("hello\\\\world"));EXPECT_FALSE(TI->isDefinitelyOut("atb"));EXPECT_TRUE(TI->isDefinitelyOut("a\\tb"));EXPECT_TRUE(TI->isDefinitelyOut("a\tb"));EXPECT_FALSE(TI->isDefinitelyOut("a0b"));}TEST_F(TrigramIndexTest, Backreference1) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"*foo\\1*"});EXPECT_TRUE(TI->isDefeated());}TEST_F(TrigramIndexTest, Backreference2) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"*foo\\2*"});EXPECT_TRUE(TI->isDefeated());}TEST_F(TrigramIndexTest, Sequence) {std::unique_ptr<TrigramIndex> TI =makeTrigramIndex({"class1.h", "class2.h", "class3.h", "class4.h"});EXPECT_FALSE(TI->isDefeated());EXPECT_FALSE(TI->isDefinitelyOut("class1"));EXPECT_TRUE(TI->isDefinitelyOut("class.h"));EXPECT_TRUE(TI->isDefinitelyOut("class"));}} // namespace
//=== - llvm/unittest/Support/TrailingObjectsTest.cpp ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/TrailingObjects.h"#include "gtest/gtest.h"using namespace llvm;namespace {// This class, beyond being used by the test case, a nice// demonstration of the intended usage of TrailingObjects, with a// single trailing array.class Class1 final : protected TrailingObjects<Class1, short> {friend TrailingObjects;unsigned NumShorts;protected:size_t numTrailingObjects(OverloadToken<short>) const { return NumShorts; }Class1(int *ShortArray, unsigned NumShorts) : NumShorts(NumShorts) {std::uninitialized_copy(ShortArray, ShortArray + NumShorts,getTrailingObjects<short>());}public:static Class1 *create(int *ShortArray, unsigned NumShorts) {void *Mem = ::operator new(totalSizeToAlloc<short>(NumShorts));return new (Mem) Class1(ShortArray, NumShorts);}void operator delete(void *p) { ::operator delete(p); }short get(unsigned Num) const { return getTrailingObjects<short>()[Num]; }unsigned numShorts() const { return NumShorts; }// Pull some protected members in as public, for testability.template <typename... Ty>using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>;using TrailingObjects::totalSizeToAlloc;using TrailingObjects::additionalSizeToAlloc;using TrailingObjects::getTrailingObjects;};// Here, there are two singular optional object types appended. Note// that the alignment of Class2 is automatically increased to account// for the alignment requirements of the trailing objects.class Class2 final : protected TrailingObjects<Class2, double, short> {friend TrailingObjects;bool HasShort, HasDouble;protected:size_t numTrailingObjects(OverloadToken<short>) const {return HasShort ? 1 : 0;}size_t numTrailingObjects(OverloadToken<double>) const {return HasDouble ? 1 : 0;}Class2(bool HasShort, bool HasDouble): HasShort(HasShort), HasDouble(HasDouble) {}public:static Class2 *create(short S = 0, double D = 0.0) {bool HasShort = S != 0;bool HasDouble = D != 0.0;void *Mem =::operator new(totalSizeToAlloc<double, short>(HasDouble, HasShort));Class2 *C = new (Mem) Class2(HasShort, HasDouble);if (HasShort)*C->getTrailingObjects<short>() = S;if (HasDouble)*C->getTrailingObjects<double>() = D;return C;}void operator delete(void *p) { ::operator delete(p); }short getShort() const {if (!HasShort)return 0;return *getTrailingObjects<short>();}double getDouble() const {if (!HasDouble)return 0.0;return *getTrailingObjects<double>();}// Pull some protected members in as public, for testability.template <typename... Ty>using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>;using TrailingObjects::totalSizeToAlloc;using TrailingObjects::additionalSizeToAlloc;using TrailingObjects::getTrailingObjects;};TEST(TrailingObjects, OneArg) {int arr[] = {1, 2, 3};Class1 *C = Class1::create(arr, 3);EXPECT_EQ(sizeof(Class1), sizeof(unsigned));EXPECT_EQ(Class1::additionalSizeToAlloc<short>(1), sizeof(short));EXPECT_EQ(Class1::additionalSizeToAlloc<short>(3), sizeof(short) * 3);EXPECT_EQ(alignof(Class1),alignof(Class1::FixedSizeStorage<short>::with_counts<1>::type));EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<1>::type),llvm::alignTo(Class1::totalSizeToAlloc<short>(1), alignof(Class1)));EXPECT_EQ(Class1::totalSizeToAlloc<short>(1), sizeof(Class1) + sizeof(short));EXPECT_EQ(alignof(Class1),alignof(Class1::FixedSizeStorage<short>::with_counts<3>::type));EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<3>::type),llvm::alignTo(Class1::totalSizeToAlloc<short>(3), alignof(Class1)));EXPECT_EQ(Class1::totalSizeToAlloc<short>(3),sizeof(Class1) + sizeof(short) * 3);EXPECT_EQ(C->getTrailingObjects<short>(), reinterpret_cast<short *>(C + 1));EXPECT_EQ(C->get(0), 1);EXPECT_EQ(C->get(2), 3);delete C;}TEST(TrailingObjects, TwoArg) {Class2 *C1 = Class2::create(4);Class2 *C2 = Class2::create(0, 4.2);EXPECT_EQ(sizeof(Class2), llvm::alignTo(sizeof(bool) * 2, alignof(double)));EXPECT_EQ(alignof(Class2), alignof(double));EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(1, 0)),sizeof(double));EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(0, 1)),sizeof(short));EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(3, 1)),sizeof(double) * 3 + sizeof(short));EXPECT_EQ(alignof(Class2),(alignof(Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type)));EXPECT_EQ(sizeof(Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type),llvm::alignTo(Class2::totalSizeToAlloc<double, short>(1, 1),alignof(Class2)));EXPECT_EQ((Class2::totalSizeToAlloc<double, short>(1, 1)),sizeof(Class2) + sizeof(double) + sizeof(short));EXPECT_EQ(C1->getDouble(), 0);EXPECT_EQ(C1->getShort(), 4);EXPECT_EQ(C1->getTrailingObjects<double>(),reinterpret_cast<double *>(C1 + 1));EXPECT_EQ(C1->getTrailingObjects<short>(), reinterpret_cast<short *>(C1 + 1));EXPECT_EQ(C2->getDouble(), 4.2);EXPECT_EQ(C2->getShort(), 0);EXPECT_EQ(C2->getTrailingObjects<double>(),reinterpret_cast<double *>(C2 + 1));EXPECT_EQ(C2->getTrailingObjects<short>(),reinterpret_cast<short *>(reinterpret_cast<double *>(C2 + 1) + 1));delete C1;delete C2;}// This test class is not trying to be a usage demo, just asserting// that three args does actually work too (it's the same code as// handles the second arg, so it's basically covered by the above, but// just in case..)class Class3 final : public TrailingObjects<Class3, double, short, bool> {friend TrailingObjects;size_t numTrailingObjects(OverloadToken<double>) const { return 1; }size_t numTrailingObjects(OverloadToken<short>) const { return 1; }};TEST(TrailingObjects, ThreeArg) {EXPECT_EQ((Class3::additionalSizeToAlloc<double, short, bool>(1, 1, 3)),sizeof(double) + sizeof(short) + 3 * sizeof(bool));EXPECT_EQ(sizeof(Class3), llvm::alignTo(1, alignof(double)));EXPECT_EQ(alignof(Class3),(alignof(Class3::FixedSizeStorage<double, short,bool>::with_counts<1, 1, 3>::type)));EXPECT_EQ(sizeof(Class3::FixedSizeStorage<double, short,bool>::with_counts<1, 1, 3>::type),llvm::alignTo(Class3::totalSizeToAlloc<double, short, bool>(1, 1, 3),alignof(Class3)));std::unique_ptr<char[]> P(new char[1000]);Class3 *C = reinterpret_cast<Class3 *>(P.get());EXPECT_EQ(C->getTrailingObjects<double>(), reinterpret_cast<double *>(C + 1));EXPECT_EQ(C->getTrailingObjects<short>(),reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1));EXPECT_EQ(C->getTrailingObjects<bool>(),reinterpret_cast<bool *>(reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1) +1));}class Class4 final : public TrailingObjects<Class4, char, long> {friend TrailingObjects;size_t numTrailingObjects(OverloadToken<char>) const { return 1; }};TEST(TrailingObjects, Realignment) {EXPECT_EQ((Class4::additionalSizeToAlloc<char, long>(1, 1)),llvm::alignTo(sizeof(long) + 1, alignof(long)));EXPECT_EQ(sizeof(Class4), llvm::alignTo(1, alignof(long)));EXPECT_EQ(alignof(Class4),(alignof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type)));EXPECT_EQ(sizeof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type),llvm::alignTo(Class4::totalSizeToAlloc<char, long>(1, 1),alignof(Class4)));std::unique_ptr<char[]> P(new char[1000]);Class4 *C = reinterpret_cast<Class4 *>(P.get());EXPECT_EQ(C->getTrailingObjects<char>(), reinterpret_cast<char *>(C + 1));EXPECT_EQ(C->getTrailingObjects<long>(),reinterpret_cast<long *>(llvm::alignAddr(reinterpret_cast<char *>(C + 1) + 1, Align::Of<long>())));}}// Test the use of TrailingObjects with a template class. This// previously failed to compile due to a bug in MSVC's member access// control/lookup handling for OverloadToken.template <typename Derived>class Class5Tmpl : private llvm::TrailingObjects<Derived, float, int> {using TrailingObjects = typename llvm::TrailingObjects<Derived, float>;friend TrailingObjects;size_t numTrailingObjects(typename TrailingObjects::template OverloadToken<float>) const {return 1;}size_t numTrailingObjects(typename TrailingObjects::template OverloadToken<int>) const {return 2;}};class Class5 : public Class5Tmpl<Class5> {};
//===- ToolOutputFileTest.cpp - ToolOutputFile tests ----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ToolOutputFile.h"#include "llvm/Support/FileSystem.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ToolOutputFileTest, DashOpensOuts) {std::error_code EC;EXPECT_EQ(&ToolOutputFile("-", EC, sys::fs::OF_None).os(), &outs());}} // namespace
//===- unittests/TimerTest.cpp - Timer tests ------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Timer.h"#include "gtest/gtest.h"#if _WIN32#include <windows.h>#else#include <time.h>#endifusing namespace llvm;namespace {// FIXME: Put this somewhere in Support, it's also used in LockFileManager.void SleepMS() {#if _WIN32Sleep(1);#elsestruct timespec Interval;Interval.tv_sec = 0;Interval.tv_nsec = 1000000;nanosleep(&Interval, nullptr);#endif}TEST(Timer, Additivity) {Timer T1("T1", "T1");EXPECT_TRUE(T1.isInitialized());T1.startTimer();T1.stopTimer();auto TR1 = T1.getTotalTime();T1.startTimer();SleepMS();T1.stopTimer();auto TR2 = T1.getTotalTime();EXPECT_LT(TR1, TR2);}TEST(Timer, CheckIfTriggered) {Timer T1("T1", "T1");EXPECT_FALSE(T1.hasTriggered());T1.startTimer();EXPECT_TRUE(T1.hasTriggered());T1.stopTimer();EXPECT_TRUE(T1.hasTriggered());T1.clear();EXPECT_FALSE(T1.hasTriggered());}} // end anon namespace
//===- unittests/Threading.cpp - Thread tests -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Threading.h"#include "llvm/Support/thread.h"#include "gtest/gtest.h"#include <atomic>#include <condition_variable>using namespace llvm;namespace {TEST(Threading, PhysicalConcurrency) {auto Num = heavyweight_hardware_concurrency();// Since Num is unsigned this will also catch us trying to// return -1.ASSERT_LE(Num.compute_thread_count(),hardware_concurrency().compute_thread_count());}#if LLVM_ENABLE_THREADSclass Notification {public:void notify() {{std::lock_guard<std::mutex> Lock(M);Notified = true;// Broadcast with the lock held, so it's safe to destroy the Notification// after wait() returns.CV.notify_all();}}bool wait() {std::unique_lock<std::mutex> Lock(M);using steady_clock = std::chrono::steady_clock;auto Deadline = steady_clock::now() +std::chrono::duration_cast<steady_clock::duration>(std::chrono::duration<double>(5));return CV.wait_until(Lock, Deadline, [this] { return Notified; });}private:bool Notified = false;mutable std::condition_variable CV;mutable std::mutex M;};TEST(Threading, RunOnThreadSyncAsync) {Notification ThreadStarted, ThreadAdvanced, ThreadFinished;auto ThreadFunc = [&] {ThreadStarted.notify();ASSERT_TRUE(ThreadAdvanced.wait());ThreadFinished.notify();};llvm::thread Thread(ThreadFunc);Thread.detach();ASSERT_TRUE(ThreadStarted.wait());ThreadAdvanced.notify();ASSERT_TRUE(ThreadFinished.wait());}TEST(Threading, RunOnThreadSync) {std::atomic_bool Executed(false);llvm::thread Thread([](void *Arg) { *static_cast<std::atomic_bool *>(Arg) = true; },&Executed);Thread.join();ASSERT_EQ(Executed, true);}#if defined(__APPLE__)TEST(Threading, AppleStackSize) {llvm::thread Thread([] {volatile unsigned char Var[8 * 1024 * 1024 - 10240];Var[0] = 0xff;ASSERT_EQ(Var[0], 0xff);});Thread.join();}#endif#endif} // end anon namespace
//========- unittests/Support/ThreadPools.cpp - ThreadPools.h tests --========////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ThreadPool.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SetVector.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/Triple.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/Host.h"#include "llvm/Support/Program.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/Threading.h"#include <chrono>#include <thread>#include "gtest/gtest.h"using namespace llvm;// Fixture for the unittests, allowing to *temporarily* disable the unittests// on a particular platformclass ThreadPoolTest : public testing::Test {Triple Host;SmallVector<Triple::ArchType, 4> UnsupportedArchs;SmallVector<Triple::OSType, 4> UnsupportedOSs;SmallVector<Triple::EnvironmentType, 1> UnsupportedEnvironments;protected:// This is intended for platform as a temporary "XFAIL"bool isUnsupportedOSOrEnvironment() {Triple Host(Triple::normalize(sys::getProcessTriple()));if (find(UnsupportedEnvironments, Host.getEnvironment()) !=UnsupportedEnvironments.end())return true;if (is_contained(UnsupportedOSs, Host.getOS()))return true;if (is_contained(UnsupportedArchs, Host.getArch()))return true;return false;}ThreadPoolTest() {// Add unsupported configuration here, example:// UnsupportedArchs.push_back(Triple::x86_64);// See https://llvm.org/bugs/show_bug.cgi?id=25829UnsupportedArchs.push_back(Triple::ppc64le);UnsupportedArchs.push_back(Triple::ppc64);}/// Make sure this thread not progress faster than the main thread.void waitForMainThread() { waitForPhase(1); }/// Set the readiness of the main thread.void setMainThreadReady() { setPhase(1); }/// Wait until given phase is set using setPhase(); first "main" phase is 1./// See also PhaseResetHelper below.void waitForPhase(int Phase) {std::unique_lock<std::mutex> LockGuard(CurrentPhaseMutex);CurrentPhaseCondition.wait(LockGuard, [&] { return CurrentPhase == Phase || CurrentPhase < 0; });}/// If a thread waits on another phase, the test could bail out on a failed/// assertion and ThreadPool destructor would wait() on all threads, which/// would deadlock on the task waiting. Create this helper to automatically/// reset the phase and unblock such threads.struct PhaseResetHelper {PhaseResetHelper(ThreadPoolTest *test) : test(test) {}~PhaseResetHelper() { test->setPhase(-1); }ThreadPoolTest *test;};/// Advance to the given phase.void setPhase(int Phase) {{std::unique_lock<std::mutex> LockGuard(CurrentPhaseMutex);assert(Phase == CurrentPhase + 1 || Phase < 0);CurrentPhase = Phase;}CurrentPhaseCondition.notify_all();}void SetUp() override { CurrentPhase = 0; }std::vector<llvm::BitVector> RunOnAllSockets(ThreadPoolStrategy S);std::condition_variable CurrentPhaseCondition;std::mutex CurrentPhaseMutex;int CurrentPhase; // -1 = error, 0 = setup, 1 = ready, 2+ = custom};#define CHECK_UNSUPPORTED() \do { \if (isUnsupportedOSOrEnvironment()) \GTEST_SKIP(); \} while (0);TEST_F(ThreadPoolTest, AsyncBarrier) {CHECK_UNSUPPORTED();// test that async & barrier work together properly.std::atomic_int checked_in{0};ThreadPool Pool;for (size_t i = 0; i < 5; ++i) {Pool.async([this, &checked_in] {waitForMainThread();++checked_in;});}ASSERT_EQ(0, checked_in);setMainThreadReady();Pool.wait();ASSERT_EQ(5, checked_in);}static void TestFunc(std::atomic_int &checked_in, int i) { checked_in += i; }TEST_F(ThreadPoolTest, AsyncBarrierArgs) {CHECK_UNSUPPORTED();// Test that async works with a function requiring multiple parameters.std::atomic_int checked_in{0};ThreadPool Pool;for (size_t i = 0; i < 5; ++i) {Pool.async(TestFunc, std::ref(checked_in), i);}Pool.wait();ASSERT_EQ(10, checked_in);}TEST_F(ThreadPoolTest, Async) {CHECK_UNSUPPORTED();ThreadPool Pool;std::atomic_int i{0};Pool.async([this, &i] {waitForMainThread();++i;});Pool.async([&i] { ++i; });ASSERT_NE(2, i.load());setMainThreadReady();Pool.wait();ASSERT_EQ(2, i.load());}TEST_F(ThreadPoolTest, GetFuture) {CHECK_UNSUPPORTED();ThreadPool Pool(hardware_concurrency(2));std::atomic_int i{0};Pool.async([this, &i] {waitForMainThread();++i;});// Force the future using get()Pool.async([&i] { ++i; }).get();ASSERT_NE(2, i.load());setMainThreadReady();Pool.wait();ASSERT_EQ(2, i.load());}TEST_F(ThreadPoolTest, GetFutureWithResult) {CHECK_UNSUPPORTED();ThreadPool Pool(hardware_concurrency(2));auto F1 = Pool.async([] { return 1; });auto F2 = Pool.async([] { return 2; });setMainThreadReady();Pool.wait();ASSERT_EQ(1, F1.get());ASSERT_EQ(2, F2.get());}TEST_F(ThreadPoolTest, GetFutureWithResultAndArgs) {CHECK_UNSUPPORTED();ThreadPool Pool(hardware_concurrency(2));auto Fn = [](int x) { return x; };auto F1 = Pool.async(Fn, 1);auto F2 = Pool.async(Fn, 2);setMainThreadReady();Pool.wait();ASSERT_EQ(1, F1.get());ASSERT_EQ(2, F2.get());}TEST_F(ThreadPoolTest, PoolDestruction) {CHECK_UNSUPPORTED();// Test that we are waiting on destructionstd::atomic_int checked_in{0};{ThreadPool Pool;for (size_t i = 0; i < 5; ++i) {Pool.async([this, &checked_in] {waitForMainThread();++checked_in;});}ASSERT_EQ(0, checked_in);setMainThreadReady();}ASSERT_EQ(5, checked_in);}// Check running tasks in different groups.TEST_F(ThreadPoolTest, Groups) {CHECK_UNSUPPORTED();// Need at least two threads, as the task in group2// might block a thread until all tasks in group1 finish.ThreadPoolStrategy S = hardware_concurrency(2);if (S.compute_thread_count() < 2)return;ThreadPool Pool(S);PhaseResetHelper Helper(this);ThreadPoolTaskGroup Group1(Pool);ThreadPoolTaskGroup Group2(Pool);// Check that waiting for an empty group is a no-op.Group1.wait();std::atomic_int checked_in1{0};std::atomic_int checked_in2{0};for (size_t i = 0; i < 5; ++i) {Group1.async([this, &checked_in1] {waitForMainThread();++checked_in1;});}Group2.async([this, &checked_in2] {waitForPhase(2);++checked_in2;});ASSERT_EQ(0, checked_in1);ASSERT_EQ(0, checked_in2);// Start first group and wait for it.setMainThreadReady();Group1.wait();ASSERT_EQ(5, checked_in1);// Second group has not yet finished, start it and wait for it.ASSERT_EQ(0, checked_in2);setPhase(2);Group2.wait();ASSERT_EQ(5, checked_in1);ASSERT_EQ(1, checked_in2);}// Check recursive tasks.TEST_F(ThreadPoolTest, RecursiveGroups) {CHECK_UNSUPPORTED();ThreadPool Pool;ThreadPoolTaskGroup Group(Pool);std::atomic_int checked_in1{0};for (size_t i = 0; i < 5; ++i) {Group.async([this, &Pool, &checked_in1] {waitForMainThread();ThreadPoolTaskGroup LocalGroup(Pool);// Check that waiting for an empty group is a no-op.LocalGroup.wait();std::atomic_int checked_in2{0};for (size_t i = 0; i < 5; ++i) {LocalGroup.async([&checked_in2] { ++checked_in2; });}LocalGroup.wait();ASSERT_EQ(5, checked_in2);++checked_in1;});}ASSERT_EQ(0, checked_in1);setMainThreadReady();Group.wait();ASSERT_EQ(5, checked_in1);}TEST_F(ThreadPoolTest, RecursiveWaitDeadlock) {CHECK_UNSUPPORTED();ThreadPoolStrategy S = hardware_concurrency(2);if (S.compute_thread_count() < 2)return;ThreadPool Pool(S);PhaseResetHelper Helper(this);ThreadPoolTaskGroup Group(Pool);// Test that a thread calling wait() for a group and is waiting for more tasks// returns when the last task finishes in a different thread while the waiting// thread was waiting for more tasks to process while waiting.// Task A runs in the first thread. It finishes and leaves// the background thread waiting for more tasks.Group.async([this] {waitForMainThread();setPhase(2);});// Task B is run in a second thread, it launches yet another// task C in a different group, which will be handled by the waiting// thread started above.Group.async([this, &Pool] {waitForPhase(2);ThreadPoolTaskGroup LocalGroup(Pool);LocalGroup.async([this] {waitForPhase(3);// Give the other thread enough time to check that there's no task// to process and suspend waiting for a notification. This is indeed racy,// but probably the best that can be done.std::this_thread::sleep_for(std::chrono::milliseconds(10));});// And task B only now will wait for the tasks in the group (=task C)// to finish. This test checks that it does not deadlock. If the// `NotifyGroup` handling in ThreadPool::processTasks() didn't take place,// this task B would be stuck waiting for tasks to arrive.setPhase(3);LocalGroup.wait();});setMainThreadReady();Group.wait();}#if LLVM_ENABLE_THREADS == 1// FIXME: Skip some tests below on non-Windows because multi-socket systems// were not fully tested on Unix yet, and llvm::get_thread_affinity_mask()// isn't implemented for Unix (need AffinityMask in Support/Unix/Program.inc).#ifdef _WIN32std::vector<llvm::BitVector>ThreadPoolTest::RunOnAllSockets(ThreadPoolStrategy S) {llvm::SetVector<llvm::BitVector> ThreadsUsed;std::mutex Lock;{std::condition_variable AllThreads;std::mutex AllThreadsLock;unsigned Active = 0;ThreadPool Pool(S);for (size_t I = 0; I < S.compute_thread_count(); ++I) {Pool.async([&] {{std::lock_guard<std::mutex> Guard(AllThreadsLock);++Active;AllThreads.notify_one();}waitForMainThread();std::lock_guard<std::mutex> Guard(Lock);auto Mask = llvm::get_thread_affinity_mask();ThreadsUsed.insert(Mask);});}EXPECT_EQ(true, ThreadsUsed.empty());{std::unique_lock<std::mutex> Guard(AllThreadsLock);AllThreads.wait(Guard,[&]() { return Active == S.compute_thread_count(); });}setMainThreadReady();}return ThreadsUsed.takeVector();}TEST_F(ThreadPoolTest, AllThreads_UseAllRessources) {CHECK_UNSUPPORTED();std::vector<llvm::BitVector> ThreadsUsed = RunOnAllSockets({});ASSERT_EQ(llvm::get_cpus(), ThreadsUsed.size());}TEST_F(ThreadPoolTest, AllThreads_OneThreadPerCore) {CHECK_UNSUPPORTED();std::vector<llvm::BitVector> ThreadsUsed =RunOnAllSockets(llvm::heavyweight_hardware_concurrency());ASSERT_EQ(llvm::get_cpus(), ThreadsUsed.size());}// From TestMain.cpp.extern const char *TestMainArgv0;// Just a reachable symbol to ease resolving of the executable's path.static cl::opt<std::string> ThreadPoolTestStringArg1("thread-pool-string-arg1");#ifdef _WIN32#define setenv(name, var, ignore) _putenv_s(name, var)#endifTEST_F(ThreadPoolTest, AffinityMask) {CHECK_UNSUPPORTED();// Skip this test if less than 4 threads are available.if (llvm::hardware_concurrency().compute_thread_count() < 4)GTEST_SKIP();using namespace llvm::sys;if (getenv("LLVM_THREADPOOL_AFFINITYMASK")) {std::vector<llvm::BitVector> ThreadsUsed = RunOnAllSockets({});// Ensure the threads only ran on CPUs 0-3.// NOTE: Don't use ASSERT* here because this runs in a subprocess,// and will show up as un-executed in the parent.assert(llvm::all_of(ThreadsUsed,[](auto &T) { return T.getData().front() < 16UL; }) &&"Threads ran on more CPUs than expected! The affinity mask does not ""seem to work.");GTEST_SKIP();}std::string Executable =sys::fs::getMainExecutable(TestMainArgv0, &ThreadPoolTestStringArg1);StringRef argv[] = {Executable, "--gtest_filter=ThreadPoolTest.AffinityMask"};// Add environment variable to the environment of the child process.int Res = setenv("LLVM_THREADPOOL_AFFINITYMASK", "1", false);ASSERT_EQ(Res, 0);std::string Error;bool ExecutionFailed;BitVector Affinity;Affinity.resize(4);Affinity.set(0, 4); // Use CPUs 0,1,2,3.int Ret = sys::ExecuteAndWait(Executable, argv, {}, {}, 0, 0, &Error,&ExecutionFailed, nullptr, &Affinity);ASSERT_EQ(0, Ret);}#endif // #ifdef _WIN32#endif // #if LLVM_ENABLE_THREADS == 1
//===- llvm/unittest/Support/ThreadLocalTest.cpp - ThreadLocal tests ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ThreadLocal.h"#include "gtest/gtest.h"#include <type_traits>using namespace llvm;using namespace sys;namespace {class ThreadLocalTest : public ::testing::Test {};struct S {int i;};TEST_F(ThreadLocalTest, Basics) {ThreadLocal<const S> x;static_assert(std::is_const<std::remove_pointer<decltype(x.get())>::type>::value,"ThreadLocal::get didn't return a pointer to const object");EXPECT_EQ(nullptr, x.get());S s;x.set(&s);EXPECT_EQ(&s, x.get());x.erase();EXPECT_EQ(nullptr, x.get());ThreadLocal<S> y;static_assert(!std::is_const<std::remove_pointer<decltype(y.get())>::type>::value,"ThreadLocal::get returned a pointer to const object");EXPECT_EQ(nullptr, y.get());y.set(&s);EXPECT_EQ(&s, y.get());y.erase();EXPECT_EQ(nullptr, y.get());}}
//========- unittests/Support/TaskQueue.cpp - TaskQueue.h tests ------========////// 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////===----------------------------------------------------------------------===//#include "llvm/Config/llvm-config.h"#if LLVM_ENABLE_THREADS#include "llvm/Support/TaskQueue.h"#include "gtest/gtest.h"using namespace llvm;class TaskQueueTest : public testing::Test {protected:TaskQueueTest() {}};TEST_F(TaskQueueTest, OrderedFutures) {ThreadPool TP(hardware_concurrency(1));TaskQueue TQ(TP);std::atomic<int> X{ 0 };std::atomic<int> Y{ 0 };std::atomic<int> Z{ 0 };std::mutex M1, M2, M3;std::unique_lock<std::mutex> L1(M1);std::unique_lock<std::mutex> L2(M2);std::unique_lock<std::mutex> L3(M3);std::future<void> F1 = TQ.async([&] {std::unique_lock<std::mutex> Lock(M1);++X;});std::future<void> F2 = TQ.async([&] {std::unique_lock<std::mutex> Lock(M2);++Y;});std::future<void> F3 = TQ.async([&] {std::unique_lock<std::mutex> Lock(M3);++Z;});L1.unlock();F1.wait();ASSERT_EQ(1, X);ASSERT_EQ(0, Y);ASSERT_EQ(0, Z);L2.unlock();F2.wait();ASSERT_EQ(1, X);ASSERT_EQ(1, Y);ASSERT_EQ(0, Z);L3.unlock();F3.wait();ASSERT_EQ(1, X);ASSERT_EQ(1, Y);ASSERT_EQ(1, Z);}TEST_F(TaskQueueTest, UnOrderedFutures) {ThreadPool TP(hardware_concurrency(1));TaskQueue TQ(TP);std::atomic<int> X{ 0 };std::atomic<int> Y{ 0 };std::atomic<int> Z{ 0 };std::mutex M;std::unique_lock<std::mutex> Lock(M);std::future<void> F1 = TQ.async([&] { ++X; });std::future<void> F2 = TQ.async([&] { ++Y; });std::future<void> F3 = TQ.async([&M, &Z] {std::unique_lock<std::mutex> Lock(M);++Z;});F2.wait();ASSERT_EQ(1, X);ASSERT_EQ(1, Y);ASSERT_EQ(0, Z);Lock.unlock();F3.wait();ASSERT_EQ(1, X);ASSERT_EQ(1, Y);ASSERT_EQ(1, Z);}TEST_F(TaskQueueTest, FutureWithReturnValue) {ThreadPool TP(hardware_concurrency(1));TaskQueue TQ(TP);std::future<std::string> F1 = TQ.async([&] { return std::string("Hello"); });std::future<int> F2 = TQ.async([&] { return 42; });ASSERT_EQ(42, F2.get());ASSERT_EQ("Hello", F1.get());}#endif
//===----------- TargetParser.cpp - Target Parser -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/StringExtras.h"#include "llvm/Support/AArch64TargetParser.h"#include "llvm/Support/ARMBuildAttributes.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/TargetParser.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <string>using namespace llvm;namespace {const char *ARMArch[] = {"armv2", "armv2a", "armv3", "armv3m", "armv4","armv4t", "armv5", "armv5t", "armv5e", "armv5te","armv5tej", "armv6", "armv6j", "armv6k", "armv6hl","armv6t2", "armv6kz", "armv6z", "armv6zk", "armv6-m","armv6m", "armv6sm", "armv6s-m", "armv7-a", "armv7","armv7a", "armv7ve", "armv7hl", "armv7l", "armv7-r","armv7r", "armv7-m", "armv7m", "armv7k", "armv7s","armv7e-m", "armv7em", "armv8-a", "armv8", "armv8a","armv8l", "armv8.1-a", "armv8.1a", "armv8.2-a", "armv8.2a","armv8.3-a", "armv8.3a", "armv8.4-a", "armv8.4a", "armv8.5-a","armv8.5a", "armv8.6-a", "armv8.6a", "armv8.7-a", "armv8.7a","armv8.8-a", "armv8.8a", "armv8-r", "armv8r", "armv8-m.base","armv8m.base", "armv8-m.main", "armv8m.main", "iwmmxt", "iwmmxt2","xscale", "armv8.1-m.main", "armv9-a", "armv9", "armv9a","armv9.1-a", "armv9.1a", "armv9.2-a", "armv9.2a",};template <ARM::ISAKind ISAKind>std::string FormatExtensionFlags(uint64_t Flags) {std::vector<StringRef> Features;if (ISAKind == ARM::ISAKind::AARCH64) {// AEK_NONE is not meant to be shown to the user so the target parser// does not recognise it. It is relevant here though.if (Flags & AArch64::AEK_NONE)Features.push_back("none");AArch64::getExtensionFeatures(Flags, Features);} else {if (Flags & ARM::AEK_NONE)Features.push_back("none");ARM::getExtensionFeatures(Flags, Features);}// The target parser also includes every extension you don't have.// E.g. if AEK_CRC is not set then it adds "-crc". Not useful here.Features.erase(std::remove_if(Features.begin(), Features.end(),[](StringRef extension) {return extension.startswith("-");}),Features.end());return llvm::join(Features, ", ");}template <ARM::ISAKind ISAKind>testing::AssertionResultAssertSameExtensionFlags(const char *m_expr, const char *n_expr,uint64_t ExpectedFlags, uint64_t GotFlags) {if (ExpectedFlags == GotFlags)return testing::AssertionSuccess();return testing::AssertionFailure() << llvm::formatv("Expected extension flags: {0} ({1:x})\n"" Got extension flags: {2} ({3:x})\n",FormatExtensionFlags<ISAKind>(ExpectedFlags), ExpectedFlags,FormatExtensionFlags<ISAKind>(GotFlags), GotFlags);}struct ARMCPUTestParams {ARMCPUTestParams(StringRef CPUName, StringRef ExpectedArch,StringRef ExpectedFPU, uint64_t ExpectedFlags,StringRef CPUAttr): CPUName(CPUName), ExpectedArch(ExpectedArch), ExpectedFPU(ExpectedFPU),ExpectedFlags(ExpectedFlags), CPUAttr(CPUAttr) {}friend std::ostream &operator<<(std::ostream &os,const ARMCPUTestParams ¶ms) {return os << "\"" << params.CPUName.str() << "\", \""<< params.ExpectedArch.str() << "\", \""<< params.ExpectedFPU.str() << "\", 0x" << std::hex<< params.ExpectedFlags << ", \"" << params.CPUAttr.str() << "\"";}StringRef CPUName;StringRef ExpectedArch;StringRef ExpectedFPU;uint64_t ExpectedFlags;StringRef CPUAttr;};class ARMCPUTestFixture : public ::testing::TestWithParam<ARMCPUTestParams> {};TEST_P(ARMCPUTestFixture, ARMCPUTests) {auto params = GetParam();ARM::ArchKind AK = ARM::parseCPUArch(params.CPUName);EXPECT_EQ(params.ExpectedArch, ARM::getArchName(AK));unsigned FPUKind = ARM::getDefaultFPU(params.CPUName, AK);EXPECT_EQ(params.ExpectedFPU, ARM::getFPUName(FPUKind));uint64_t default_extensions = ARM::getDefaultExtensions(params.CPUName, AK);EXPECT_PRED_FORMAT2(AssertSameExtensionFlags<ARM::ISAKind::ARM>,params.ExpectedFlags, default_extensions);EXPECT_EQ(params.CPUAttr, ARM::getCPUAttr(AK));}// Note that we include ARM::AEK_NONE even when there are other extensions// we expect. This is because the default extensions for a CPU are the sum// of the default extensions for its architecture and for the CPU.// So if a CPU has no extra extensions, it adds AEK_NONE.INSTANTIATE_TEST_SUITE_P(ARMCPUTestsPart1, ARMCPUTestFixture,::testing::Values(ARMCPUTestParams("invalid", "invalid", "invalid", ARM::AEK_NONE, ""),ARMCPUTestParams("generic", "invalid", "none", ARM::AEK_NONE, ""),ARMCPUTestParams("arm8", "armv4", "none", ARM::AEK_NONE, "4"),ARMCPUTestParams("arm810", "armv4", "none", ARM::AEK_NONE, "4"),ARMCPUTestParams("strongarm", "armv4", "none", ARM::AEK_NONE, "4"),ARMCPUTestParams("strongarm110", "armv4", "none", ARM::AEK_NONE, "4"),ARMCPUTestParams("strongarm1100", "armv4", "none", ARM::AEK_NONE, "4"),ARMCPUTestParams("strongarm1110", "armv4", "none", ARM::AEK_NONE, "4"),ARMCPUTestParams("arm7tdmi", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm7tdmi-s", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm710t", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm720t", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm9", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm9tdmi", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm920", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm920t", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm922t", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm940t", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("ep9312", "armv4t", "none", ARM::AEK_NONE, "4T"),ARMCPUTestParams("arm10tdmi", "armv5t", "none", ARM::AEK_NONE, "5T"),ARMCPUTestParams("arm1020t", "armv5t", "none", ARM::AEK_NONE, "5T"),ARMCPUTestParams("arm9e", "armv5te", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TE"),ARMCPUTestParams("arm946e-s", "armv5te", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TE"),ARMCPUTestParams("arm966e-s", "armv5te", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TE"),ARMCPUTestParams("arm968e-s", "armv5te", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TE"),ARMCPUTestParams("arm10e", "armv5te", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TE"),ARMCPUTestParams("arm1020e", "armv5te", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TE"),ARMCPUTestParams("arm1022e", "armv5te", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TE"),ARMCPUTestParams("arm926ej-s", "armv5tej", "none",ARM::AEK_NONE | ARM::AEK_DSP, "5TEJ"),ARMCPUTestParams("arm1136j-s", "armv6", "none",ARM::AEK_NONE | ARM::AEK_DSP, "6"),ARMCPUTestParams("arm1136jf-s", "armv6", "vfpv2",ARM::AEK_NONE | ARM::AEK_DSP, "6"),ARMCPUTestParams("arm1176jz-s", "armv6kz", "none",ARM::AEK_NONE | ARM::AEK_SEC | ARM::AEK_DSP, "6KZ"),ARMCPUTestParams("mpcore", "armv6k", "vfpv2",ARM::AEK_NONE | ARM::AEK_DSP, "6K"),ARMCPUTestParams("mpcorenovfp", "armv6k", "none",ARM::AEK_NONE | ARM::AEK_DSP, "6K"),ARMCPUTestParams("arm1176jzf-s", "armv6kz", "vfpv2",ARM::AEK_NONE | ARM::AEK_SEC | ARM::AEK_DSP, "6KZ"),ARMCPUTestParams("arm1156t2-s", "armv6t2", "none",ARM::AEK_NONE | ARM::AEK_DSP, "6T2"),ARMCPUTestParams("arm1156t2f-s", "armv6t2", "vfpv2",ARM::AEK_NONE | ARM::AEK_DSP, "6T2"),ARMCPUTestParams("cortex-m0", "armv6-m", "none", ARM::AEK_NONE, "6-M"),ARMCPUTestParams("cortex-m0plus", "armv6-m", "none", ARM::AEK_NONE,"6-M"),ARMCPUTestParams("cortex-m1", "armv6-m", "none", ARM::AEK_NONE, "6-M"),ARMCPUTestParams("sc000", "armv6-m", "none", ARM::AEK_NONE, "6-M"),ARMCPUTestParams("cortex-a5", "armv7-a", "neon-vfpv4",ARM::AEK_MP | ARM::AEK_SEC | ARM::AEK_DSP, "7-A"),ARMCPUTestParams("cortex-a7", "armv7-a", "neon-vfpv4",ARM::AEK_HWDIVTHUMB | ARM::AEK_HWDIVARM | ARM::AEK_MP |ARM::AEK_SEC | ARM::AEK_VIRT | ARM::AEK_DSP,"7-A"),ARMCPUTestParams("cortex-a8", "armv7-a", "neon",ARM::AEK_SEC | ARM::AEK_DSP, "7-A")));// gtest in llvm has a limit of 50 test cases when using ::Values so we split// them into 2 blocksINSTANTIATE_TEST_SUITE_P(ARMCPUTestsPart2, ARMCPUTestFixture,::testing::Values(ARMCPUTestParams("cortex-a9", "armv7-a", "neon-fp16",ARM::AEK_MP | ARM::AEK_SEC | ARM::AEK_DSP, "7-A"),ARMCPUTestParams("cortex-a12", "armv7-a", "neon-vfpv4",ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP,"7-A"),ARMCPUTestParams("cortex-a15", "armv7-a", "neon-vfpv4",ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP,"7-A"),ARMCPUTestParams("cortex-a17", "armv7-a", "neon-vfpv4",ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP,"7-A"),ARMCPUTestParams("krait", "armv7-a", "neon-vfpv4",ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"7-A"),ARMCPUTestParams("cortex-r4", "armv7-r", "none",ARM::AEK_NONE | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"7-R"),ARMCPUTestParams("cortex-r4f", "armv7-r", "vfpv3-d16",ARM::AEK_NONE | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"7-R"),ARMCPUTestParams("cortex-r5", "armv7-r", "vfpv3-d16",ARM::AEK_MP | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP,"7-R"),ARMCPUTestParams("cortex-r7", "armv7-r", "vfpv3-d16-fp16",ARM::AEK_MP | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP,"7-R"),ARMCPUTestParams("cortex-r8", "armv7-r", "vfpv3-d16-fp16",ARM::AEK_MP | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP,"7-R"),ARMCPUTestParams("cortex-r52", "armv8-r", "neon-fp-armv8",ARM::AEK_NONE | ARM::AEK_CRC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-R"),ARMCPUTestParams("sc300", "armv7-m", "none",ARM::AEK_NONE | ARM::AEK_HWDIVTHUMB, "7-M"),ARMCPUTestParams("cortex-m3", "armv7-m", "none",ARM::AEK_NONE | ARM::AEK_HWDIVTHUMB, "7-M"),ARMCPUTestParams("cortex-m4", "armv7e-m", "fpv4-sp-d16",ARM::AEK_NONE | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"7E-M"),ARMCPUTestParams("cortex-m7", "armv7e-m", "fpv5-d16",ARM::AEK_NONE | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"7E-M"),ARMCPUTestParams("cortex-a32", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("cortex-a35", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("cortex-a53", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("cortex-a55", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_FP16 | ARM::AEK_RAS | ARM::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("cortex-a57", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("cortex-a72", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("cortex-a73", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("cortex-a75", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_FP16 | ARM::AEK_RAS | ARM::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("cortex-a76", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_FP16 | ARM::AEK_RAS | ARM::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("cortex-a76ae", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_FP16 | ARM::AEK_RAS | ARM::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("cortex-a78c", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP | ARM::AEK_CRC | ARM::AEK_RAS |ARM::AEK_FP16 | ARM::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("cortex-a710", "armv9-a", "neon-fp-armv8",ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP | ARM::AEK_CRC | ARM::AEK_RAS |ARM::AEK_DOTPROD | ARM::AEK_FP16FML |ARM::AEK_BF16 | ARM::AEK_I8MM | ARM::AEK_SB,"9-A"),ARMCPUTestParams("cortex-a77", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_FP16 | ARM::AEK_RAS | ARM::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("cortex-a78", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_DOTPROD | ARM::AEK_FP16 | ARM::AEK_SEC |ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_CRC |ARM::AEK_RAS,"8.2-A"),ARMCPUTestParams("cortex-x1", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_RAS | ARM::AEK_FP16 | ARM::AEK_DOTPROD |ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP | ARM::AEK_CRC,"8.2-A"),ARMCPUTestParams("cortex-x1c", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_RAS | ARM::AEK_FP16 | ARM::AEK_DOTPROD |ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP | ARM::AEK_CRC,"8.2-A"),ARMCPUTestParams("neoverse-n1", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_FP16 | ARM::AEK_RAS | ARM::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("neoverse-n2", "armv8.5-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_HWDIVTHUMB |ARM::AEK_HWDIVARM | ARM::AEK_MP | ARM::AEK_SEC |ARM::AEK_VIRT | ARM::AEK_DSP | ARM::AEK_BF16 |ARM::AEK_DOTPROD | ARM::AEK_RAS | ARM::AEK_I8MM |ARM::AEK_SB,"8.5-A"),ARMCPUTestParams("neoverse-v1", "armv8.4-a", "crypto-neon-fp-armv8",ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT |ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB |ARM::AEK_DSP | ARM::AEK_CRC | ARM::AEK_RAS |ARM::AEK_FP16 | ARM::AEK_BF16 | ARM::AEK_DOTPROD,"8.4-A"),ARMCPUTestParams("cyclone", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("exynos-m3", "armv8-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"8-A"),ARMCPUTestParams("exynos-m4", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_DOTPROD | ARM::AEK_FP16 | ARM::AEK_RAS,"8.2-A"),ARMCPUTestParams("exynos-m5", "armv8.2-a", "crypto-neon-fp-armv8",ARM::AEK_CRC | ARM::AEK_SEC | ARM::AEK_MP |ARM::AEK_VIRT | ARM::AEK_HWDIVARM |ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP |ARM::AEK_DOTPROD | ARM::AEK_FP16 | ARM::AEK_RAS,"8.2-A"),ARMCPUTestParams("cortex-m23", "armv8-m.base", "none",ARM::AEK_NONE | ARM::AEK_HWDIVTHUMB, "8-M.Baseline"),ARMCPUTestParams("cortex-m33", "armv8-m.main", "fpv5-sp-d16",ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP, "8-M.Mainline"),ARMCPUTestParams("cortex-m35p", "armv8-m.main", "fpv5-sp-d16",ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP, "8-M.Mainline"),ARMCPUTestParams("cortex-m55", "armv8.1-m.main","fp-armv8-fullfp16-d16",ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_SIMD |ARM::AEK_FP | ARM::AEK_RAS | ARM::AEK_LOB |ARM::AEK_FP16,"8.1-M.Mainline"),ARMCPUTestParams("cortex-m85", "armv8.1-m.main","fp-armv8-fullfp16-d16",ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_SIMD |ARM::AEK_FP | ARM::AEK_RAS | ARM::AEK_LOB |ARM::AEK_FP16 | ARM::AEK_PACBTI,"8.1-M.Mainline"),ARMCPUTestParams("iwmmxt", "iwmmxt", "none", ARM::AEK_NONE, "iwmmxt"),ARMCPUTestParams("xscale", "xscale", "none", ARM::AEK_NONE, "xscale"),ARMCPUTestParams("swift", "armv7s", "neon-vfpv4",ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP,"7-S")));static constexpr unsigned NumARMCPUArchs = 89;TEST(TargetParserTest, testARMCPUArchList) {SmallVector<StringRef, NumARMCPUArchs> List;ARM::fillValidCPUArchList(List);// No list exists for these in this test suite, so ensure all are// valid, and match the expected 'magic' count.EXPECT_EQ(List.size(), NumARMCPUArchs);for(StringRef CPU : List) {EXPECT_NE(ARM::parseCPUArch(CPU), ARM::ArchKind::INVALID);}}TEST(TargetParserTest, testInvalidARMArch) {auto InvalidArchStrings = {"armv", "armv99", "noarm"};for (const char* InvalidArch : InvalidArchStrings)EXPECT_EQ(ARM::parseArch(InvalidArch), ARM::ArchKind::INVALID);}bool testARMArch(StringRef Arch, StringRef DefaultCPU, StringRef SubArch,unsigned ArchAttr) {ARM::ArchKind AK = ARM::parseArch(Arch);bool Result = (AK != ARM::ArchKind::INVALID);Result &= ARM::getDefaultCPU(Arch).equals(DefaultCPU);Result &= ARM::getSubArch(AK).equals(SubArch);Result &= (ARM::getArchAttr(AK) == ArchAttr);return Result;}TEST(TargetParserTest, testARMArch) {EXPECT_TRUE(testARMArch("armv2", "generic", "v2", ARMBuildAttrs::CPUArch::Pre_v4));EXPECT_TRUE(testARMArch("armv2a", "generic", "v2a", ARMBuildAttrs::CPUArch::Pre_v4));EXPECT_TRUE(testARMArch("armv3", "generic", "v3", ARMBuildAttrs::CPUArch::Pre_v4));EXPECT_TRUE(testARMArch("armv3m", "generic", "v3m", ARMBuildAttrs::CPUArch::Pre_v4));EXPECT_TRUE(testARMArch("armv4", "strongarm", "v4",ARMBuildAttrs::CPUArch::v4));EXPECT_TRUE(testARMArch("armv4t", "arm7tdmi", "v4t",ARMBuildAttrs::CPUArch::v4T));EXPECT_TRUE(testARMArch("armv5t", "arm10tdmi", "v5",ARMBuildAttrs::CPUArch::v5T));EXPECT_TRUE(testARMArch("armv5te", "arm1022e", "v5e",ARMBuildAttrs::CPUArch::v5TE));EXPECT_TRUE(testARMArch("armv5tej", "arm926ej-s", "v5e",ARMBuildAttrs::CPUArch::v5TEJ));EXPECT_TRUE(testARMArch("armv6", "arm1136jf-s", "v6",ARMBuildAttrs::CPUArch::v6));EXPECT_TRUE(testARMArch("armv6k", "mpcore", "v6k",ARMBuildAttrs::CPUArch::v6K));EXPECT_TRUE(testARMArch("armv6t2", "arm1156t2-s", "v6t2",ARMBuildAttrs::CPUArch::v6T2));EXPECT_TRUE(testARMArch("armv6kz", "arm1176jzf-s", "v6kz",ARMBuildAttrs::CPUArch::v6KZ));EXPECT_TRUE(testARMArch("armv6-m", "cortex-m0", "v6m",ARMBuildAttrs::CPUArch::v6_M));EXPECT_TRUE(testARMArch("armv7-a", "generic", "v7",ARMBuildAttrs::CPUArch::v7));EXPECT_TRUE(testARMArch("armv7ve", "generic", "v7ve",ARMBuildAttrs::CPUArch::v7));EXPECT_TRUE(testARMArch("armv7-r", "cortex-r4", "v7r",ARMBuildAttrs::CPUArch::v7));EXPECT_TRUE(testARMArch("armv7-m", "cortex-m3", "v7m",ARMBuildAttrs::CPUArch::v7));EXPECT_TRUE(testARMArch("armv7e-m", "cortex-m4", "v7em",ARMBuildAttrs::CPUArch::v7E_M));EXPECT_TRUE(testARMArch("armv8-a", "generic", "v8",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.1-a", "generic", "v8.1a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.2-a", "generic", "v8.2a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.3-a", "generic", "v8.3a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.4-a", "generic", "v8.4a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.5-a", "generic", "v8.5a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.6-a", "generic", "v8.6a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.7-a", "generic", "v8.7a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv8.8-a", "generic", "v8.8a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testARMArch("armv9-a", "generic", "v9a",ARMBuildAttrs::CPUArch::v9_A));EXPECT_TRUE(testARMArch("armv9.1-a", "generic", "v9.1a",ARMBuildAttrs::CPUArch::v9_A));EXPECT_TRUE(testARMArch("armv9.2-a", "generic", "v9.2a",ARMBuildAttrs::CPUArch::v9_A));EXPECT_TRUE(testARMArch("armv9.3-a", "generic", "v9.3a",ARMBuildAttrs::CPUArch::v9_A));EXPECT_TRUE(testARMArch("armv8-r", "cortex-r52", "v8r",ARMBuildAttrs::CPUArch::v8_R));EXPECT_TRUE(testARMArch("armv8-m.base", "generic", "v8m.base",ARMBuildAttrs::CPUArch::v8_M_Base));EXPECT_TRUE(testARMArch("armv8-m.main", "generic", "v8m.main",ARMBuildAttrs::CPUArch::v8_M_Main));EXPECT_TRUE(testARMArch("armv8.1-m.main", "generic", "v8.1m.main",ARMBuildAttrs::CPUArch::v8_1_M_Main));EXPECT_TRUE(testARMArch("iwmmxt", "iwmmxt", "",ARMBuildAttrs::CPUArch::v5TE));EXPECT_TRUE(testARMArch("iwmmxt2", "generic", "",ARMBuildAttrs::CPUArch::v5TE));EXPECT_TRUE(testARMArch("xscale", "xscale", "v5e",ARMBuildAttrs::CPUArch::v5TE));EXPECT_TRUE(testARMArch("armv7s", "swift", "v7s",ARMBuildAttrs::CPUArch::v7));EXPECT_TRUE(testARMArch("armv7k", "generic", "v7k",ARMBuildAttrs::CPUArch::v7));}bool testARMExtension(StringRef CPUName,ARM::ArchKind ArchKind, StringRef ArchExt) {return ARM::getDefaultExtensions(CPUName, ArchKind) &ARM::parseArchExt(ArchExt);}TEST(TargetParserTest, testARMExtension) {EXPECT_FALSE(testARMExtension("strongarm", ARM::ArchKind::INVALID, "dsp"));EXPECT_FALSE(testARMExtension("arm7tdmi", ARM::ArchKind::INVALID, "dsp"));EXPECT_FALSE(testARMExtension("arm10tdmi",ARM::ArchKind::INVALID, "simd"));EXPECT_FALSE(testARMExtension("arm1022e", ARM::ArchKind::INVALID, "simd"));EXPECT_FALSE(testARMExtension("arm926ej-s",ARM::ArchKind::INVALID, "simd"));EXPECT_FALSE(testARMExtension("arm1136jf-s",ARM::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testARMExtension("arm1156t2-s",ARM::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testARMExtension("arm1176jzf-s",ARM::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testARMExtension("cortex-m0",ARM::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testARMExtension("cortex-a8",ARM::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testARMExtension("cortex-r4",ARM::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testARMExtension("cortex-m3",ARM::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testARMExtension("cortex-a53",ARM::ArchKind::INVALID, "ras"));EXPECT_FALSE(testARMExtension("cortex-a53",ARM::ArchKind::INVALID, "fp16"));EXPECT_TRUE(testARMExtension("cortex-a55",ARM::ArchKind::INVALID, "fp16"));EXPECT_FALSE(testARMExtension("cortex-a55",ARM::ArchKind::INVALID, "fp16fml"));EXPECT_TRUE(testARMExtension("cortex-a75",ARM::ArchKind::INVALID, "fp16"));EXPECT_FALSE(testARMExtension("cortex-a75",ARM::ArchKind::INVALID, "fp16fml"));EXPECT_FALSE(testARMExtension("cortex-r52",ARM::ArchKind::INVALID, "ras"));EXPECT_FALSE(testARMExtension("iwmmxt", ARM::ArchKind::INVALID, "crc"));EXPECT_FALSE(testARMExtension("xscale", ARM::ArchKind::INVALID, "crc"));EXPECT_FALSE(testARMExtension("swift", ARM::ArchKind::INVALID, "crc"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV2, "thumb"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV2A, "thumb"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV3, "thumb"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV3M, "thumb"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV4, "dsp"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV4T, "dsp"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV5T, "simd"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV5TE, "simd"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV5TEJ, "simd"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV6, "crypto"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV6K, "crypto"));EXPECT_FALSE(testARMExtension("generic",ARM::ArchKind::ARMV6T2, "crypto"));EXPECT_FALSE(testARMExtension("generic",ARM::ArchKind::ARMV6KZ, "crypto"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV6M, "crypto"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV7A, "crypto"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV7R, "crypto"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV7M, "crypto"));EXPECT_FALSE(testARMExtension("generic",ARM::ArchKind::ARMV7EM, "crypto"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8A, "ras"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_1A, "ras"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_2A, "profile"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_2A, "fp16"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_2A, "fp16fml"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_3A, "fp16"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_3A, "fp16fml"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_4A, "fp16"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8_4A, "fp16fml"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV8R, "ras"));EXPECT_FALSE(testARMExtension("generic",ARM::ArchKind::ARMV8MBaseline, "crc"));EXPECT_FALSE(testARMExtension("generic",ARM::ArchKind::ARMV8MMainline, "crc"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::IWMMXT, "crc"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::IWMMXT2, "crc"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::XSCALE, "crc"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV7S, "crypto"));EXPECT_FALSE(testARMExtension("generic", ARM::ArchKind::ARMV7K, "crypto"));}TEST(TargetParserTest, ARMFPUVersion) {for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0);FK <= ARM::FPUKind::FK_LAST;FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1))if (FK == ARM::FK_LAST || ARM::getFPUName(FK) == "invalid" ||ARM::getFPUName(FK) == "none" || ARM::getFPUName(FK) == "softvfp")EXPECT_EQ(ARM::FPUVersion::NONE, ARM::getFPUVersion(FK));elseEXPECT_NE(ARM::FPUVersion::NONE, ARM::getFPUVersion(FK));}TEST(TargetParserTest, ARMFPUNeonSupportLevel) {for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0);FK <= ARM::FPUKind::FK_LAST;FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1))if (FK == ARM::FK_LAST ||ARM::getFPUName(FK).find("neon") == std::string::npos)EXPECT_EQ(ARM::NeonSupportLevel::None,ARM::getFPUNeonSupportLevel(FK));elseEXPECT_NE(ARM::NeonSupportLevel::None,ARM::getFPUNeonSupportLevel(FK));}TEST(TargetParserTest, ARMFPURestriction) {for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0);FK <= ARM::FPUKind::FK_LAST;FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1)) {if (FK == ARM::FK_LAST ||(ARM::getFPUName(FK).find("d16") == std::string::npos &&ARM::getFPUName(FK).find("vfpv3xd") == std::string::npos))EXPECT_EQ(ARM::FPURestriction::None, ARM::getFPURestriction(FK));elseEXPECT_NE(ARM::FPURestriction::None, ARM::getFPURestriction(FK));}}TEST(TargetParserTest, ARMExtensionFeatures) {std::map<uint64_t, std::vector<StringRef>> Extensions;for (auto &Ext : ARM::ARCHExtNames) {if (Ext.Feature && Ext.NegFeature)Extensions[Ext.ID] = { StringRef(Ext.Feature),StringRef(Ext.NegFeature) };}Extensions[ARM::AEK_HWDIVARM] = { "+hwdiv-arm", "-hwdiv-arm" };Extensions[ARM::AEK_HWDIVTHUMB] = { "+hwdiv", "-hwdiv" };std::vector<StringRef> Features;EXPECT_FALSE(ARM::getExtensionFeatures(ARM::AEK_INVALID, Features));for (auto &E : Extensions) {// test +extensionFeatures.clear();ARM::getExtensionFeatures(E.first, Features);EXPECT_TRUE(llvm::is_contained(Features, E.second.at(0)));EXPECT_EQ(Extensions.size(), Features.size());// test -extensionFeatures.clear();ARM::getExtensionFeatures(~E.first, Features);EXPECT_TRUE(llvm::is_contained(Features, E.second.at(1)));EXPECT_EQ(Extensions.size(), Features.size());}}TEST(TargetParserTest, ARMFPUFeatures) {std::vector<StringRef> Features;for (ARM::FPUKind FK = static_cast<ARM::FPUKind>(0);FK <= ARM::FPUKind::FK_LAST;FK = static_cast<ARM::FPUKind>(static_cast<unsigned>(FK) + 1)) {if (FK == ARM::FK_INVALID || FK >= ARM::FK_LAST)EXPECT_FALSE(ARM::getFPUFeatures(FK, Features));elseEXPECT_TRUE(ARM::getFPUFeatures(FK, Features));}}TEST(TargetParserTest, ARMArchExtFeature) {const char *ArchExt[][4] = {{"crc", "nocrc", "+crc", "-crc"},{"crypto", "nocrypto", "+crypto", "-crypto"},{"dsp", "nodsp", "+dsp", "-dsp"},{"fp", "nofp", nullptr, nullptr},{"idiv", "noidiv", nullptr, nullptr},{"mp", "nomp", nullptr, nullptr},{"simd", "nosimd", nullptr, nullptr},{"sec", "nosec", nullptr, nullptr},{"virt", "novirt", nullptr, nullptr},{"fp16", "nofp16", "+fullfp16", "-fullfp16"},{"fp16fml", "nofp16fml", "+fp16fml", "-fp16fml"},{"ras", "noras", "+ras", "-ras"},{"dotprod", "nodotprod", "+dotprod", "-dotprod"},{"os", "noos", nullptr, nullptr},{"iwmmxt", "noiwmmxt", nullptr, nullptr},{"iwmmxt2", "noiwmmxt2", nullptr, nullptr},{"maverick", "maverick", nullptr, nullptr},{"xscale", "noxscale", nullptr, nullptr},{"sb", "nosb", "+sb", "-sb"},{"i8mm", "noi8mm", "+i8mm", "-i8mm"},{"mve", "nomve", "+mve", "-mve"},{"mve.fp", "nomve.fp", "+mve.fp", "-mve.fp"}};for (unsigned i = 0; i < array_lengthof(ArchExt); i++) {EXPECT_EQ(StringRef(ArchExt[i][2]), ARM::getArchExtFeature(ArchExt[i][0]));EXPECT_EQ(StringRef(ArchExt[i][3]), ARM::getArchExtFeature(ArchExt[i][1]));}}static booltestArchExtDependency(const char *ArchExt,const std::initializer_list<const char *> &Expected) {std::vector<StringRef> Features;unsigned FPUID;if (!ARM::appendArchExtFeatures("", ARM::ArchKind::ARMV8_1MMainline, ArchExt,Features, FPUID))return false;return llvm::all_of(Expected, [&](StringRef Ext) {return llvm::is_contained(Features, Ext);});}TEST(TargetParserTest, ARMArchExtDependencies) {EXPECT_TRUE(testArchExtDependency("mve", {"+mve", "+dsp"}));EXPECT_TRUE(testArchExtDependency("mve.fp", {"+mve.fp", "+mve", "+dsp"}));EXPECT_TRUE(testArchExtDependency("nodsp", {"-dsp", "-mve", "-mve.fp"}));EXPECT_TRUE(testArchExtDependency("nomve", {"-mve", "-mve.fp"}));}TEST(TargetParserTest, ARMparseHWDiv) {const char *hwdiv[] = {"thumb", "arm", "arm,thumb", "thumb,arm"};for (unsigned i = 0; i < array_lengthof(hwdiv); i++)EXPECT_NE(ARM::AEK_INVALID, ARM::parseHWDiv((StringRef)hwdiv[i]));}TEST(TargetParserTest, ARMparseArchEndianAndISA) {const char *Arch[] = {"v2", "v2a", "v3", "v3m", "v4", "v4t","v5", "v5t", "v5e", "v5te", "v5tej", "v6","v6j", "v6k", "v6hl", "v6t2", "v6kz", "v6z","v6zk", "v6-m", "v6m", "v6sm", "v6s-m", "v7-a","v7", "v7a", "v7ve", "v7hl", "v7l", "v7-r","v7r", "v7-m", "v7m", "v7k", "v7s", "v7e-m","v7em", "v8-a", "v8", "v8a", "v8l", "v8.1-a","v8.1a", "v8.2-a", "v8.2a", "v8.3-a", "v8.3a", "v8.4-a","v8.4a", "v8.5-a", "v8.5a", "v8.6-a", "v8.6a", "v8.7-a","v8.7a", "v8.8-a", "v8.8a", "v8-r", "v8m.base", "v8m.main","v8.1m.main"};for (unsigned i = 0; i < array_lengthof(Arch); i++) {std::string arm_1 = "armeb" + (std::string)(Arch[i]);std::string arm_2 = "arm" + (std::string)(Arch[i]) + "eb";std::string arm_3 = "arm" + (std::string)(Arch[i]);std::string thumb_1 = "thumbeb" + (std::string)(Arch[i]);std::string thumb_2 = "thumb" + (std::string)(Arch[i]) + "eb";std::string thumb_3 = "thumb" + (std::string)(Arch[i]);EXPECT_EQ(ARM::EndianKind::BIG, ARM::parseArchEndian(arm_1));EXPECT_EQ(ARM::EndianKind::BIG, ARM::parseArchEndian(arm_2));EXPECT_EQ(ARM::EndianKind::LITTLE, ARM::parseArchEndian(arm_3));EXPECT_EQ(ARM::ISAKind::ARM, ARM::parseArchISA(arm_1));EXPECT_EQ(ARM::ISAKind::ARM, ARM::parseArchISA(arm_2));EXPECT_EQ(ARM::ISAKind::ARM, ARM::parseArchISA(arm_3));if (i >= 4) {EXPECT_EQ(ARM::EndianKind::BIG, ARM::parseArchEndian(thumb_1));EXPECT_EQ(ARM::EndianKind::BIG, ARM::parseArchEndian(thumb_2));EXPECT_EQ(ARM::EndianKind::LITTLE, ARM::parseArchEndian(thumb_3));EXPECT_EQ(ARM::ISAKind::THUMB, ARM::parseArchISA(thumb_1));EXPECT_EQ(ARM::ISAKind::THUMB, ARM::parseArchISA(thumb_2));EXPECT_EQ(ARM::ISAKind::THUMB, ARM::parseArchISA(thumb_3));}}EXPECT_EQ(ARM::EndianKind::LITTLE, ARM::parseArchEndian("aarch64"));EXPECT_EQ(ARM::EndianKind::LITTLE, ARM::parseArchEndian("arm64_32"));EXPECT_EQ(ARM::EndianKind::BIG, ARM::parseArchEndian("aarch64_be"));EXPECT_EQ(ARM::ISAKind::AARCH64, ARM::parseArchISA("aarch64"));EXPECT_EQ(ARM::ISAKind::AARCH64, ARM::parseArchISA("aarch64_be"));EXPECT_EQ(ARM::ISAKind::AARCH64, ARM::parseArchISA("arm64"));EXPECT_EQ(ARM::ISAKind::AARCH64, ARM::parseArchISA("arm64_be"));EXPECT_EQ(ARM::ISAKind::AARCH64, ARM::parseArchISA("arm64_32"));EXPECT_EQ(ARM::ISAKind::AARCH64, ARM::parseArchISA("aarch64_32"));}TEST(TargetParserTest, ARMparseArchProfile) {for (unsigned i = 0; i < array_lengthof(ARMArch); i++) {switch (ARM::parseArch(ARMArch[i])) {case ARM::ArchKind::ARMV6M:case ARM::ArchKind::ARMV7M:case ARM::ArchKind::ARMV7EM:case ARM::ArchKind::ARMV8MMainline:case ARM::ArchKind::ARMV8MBaseline:case ARM::ArchKind::ARMV8_1MMainline:EXPECT_EQ(ARM::ProfileKind::M, ARM::parseArchProfile(ARMArch[i]));break;case ARM::ArchKind::ARMV7R:case ARM::ArchKind::ARMV8R:EXPECT_EQ(ARM::ProfileKind::R, ARM::parseArchProfile(ARMArch[i]));break;case ARM::ArchKind::ARMV7A:case ARM::ArchKind::ARMV7VE:case ARM::ArchKind::ARMV7K:case ARM::ArchKind::ARMV8A:case ARM::ArchKind::ARMV8_1A:case ARM::ArchKind::ARMV8_2A:case ARM::ArchKind::ARMV8_3A:case ARM::ArchKind::ARMV8_4A:case ARM::ArchKind::ARMV8_5A:case ARM::ArchKind::ARMV8_6A:case ARM::ArchKind::ARMV8_7A:case ARM::ArchKind::ARMV8_8A:case ARM::ArchKind::ARMV9A:case ARM::ArchKind::ARMV9_1A:case ARM::ArchKind::ARMV9_2A:case ARM::ArchKind::ARMV9_3A:EXPECT_EQ(ARM::ProfileKind::A, ARM::parseArchProfile(ARMArch[i]));break;default:EXPECT_EQ(ARM::ProfileKind::INVALID, ARM::parseArchProfile(ARMArch[i]));break;}}}TEST(TargetParserTest, ARMparseArchVersion) {for (unsigned i = 0; i < array_lengthof(ARMArch); i++)if (((std::string)ARMArch[i]).substr(0, 4) == "armv")EXPECT_EQ((ARMArch[i][4] - 48u), ARM::parseArchVersion(ARMArch[i]));elseEXPECT_EQ(5u, ARM::parseArchVersion(ARMArch[i]));}class AArch64CPUTestFixture: public ::testing::TestWithParam<ARMCPUTestParams> {};TEST_P(AArch64CPUTestFixture, testAArch64CPU) {ARMCPUTestParams params = GetParam();AArch64::ArchKind AK = AArch64::parseCPUArch(params.CPUName);EXPECT_EQ(params.ExpectedArch, AArch64::getArchName(AK));uint64_t default_extensions =AArch64::getDefaultExtensions(params.CPUName, AK);EXPECT_PRED_FORMAT2(AssertSameExtensionFlags<ARM::ISAKind::AARCH64>,params.ExpectedFlags, default_extensions);unsigned FPUKind = AArch64::getDefaultFPU(params.CPUName, AK);EXPECT_EQ(params.ExpectedFPU, ARM::getFPUName(FPUKind));EXPECT_EQ(params.CPUAttr, AArch64::getCPUAttr(AK));}INSTANTIATE_TEST_SUITE_P(AArch64CPUTests, AArch64CPUTestFixture,::testing::Values(ARMCPUTestParams("invalid", "invalid", "invalid", AArch64::AEK_NONE,""),ARMCPUTestParams("generic", "invalid", "none", AArch64::AEK_NONE, ""),ARMCPUTestParams("cortex-a34", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("cortex-a35", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("cortex-a53", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("cortex-a55", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_RAS | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC,"8.2-A"),ARMCPUTestParams("cortex-a510", "armv9-a", "neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_FP |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_RDM |AArch64::AEK_RCPC | AArch64::AEK_DOTPROD |AArch64::AEK_BF16 | AArch64::AEK_I8MM |AArch64::AEK_SVE | AArch64::AEK_SVE2 |AArch64::AEK_SVE2BITPERM | AArch64::AEK_PAUTH |AArch64::AEK_MTE | AArch64::AEK_SSBS |AArch64::AEK_FP16FML | AArch64::AEK_SB,"9-A"),ARMCPUTestParams("cortex-a57", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("cortex-a65", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_DOTPROD | AArch64::AEK_FP |AArch64::AEK_FP16 | AArch64::AEK_LSE |AArch64::AEK_RAS | AArch64::AEK_RCPC |AArch64::AEK_RDM | AArch64::AEK_SIMD |AArch64::AEK_SSBS,"8.2-A"),ARMCPUTestParams("cortex-a65ae", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_DOTPROD | AArch64::AEK_FP |AArch64::AEK_FP16 | AArch64::AEK_LSE |AArch64::AEK_RAS | AArch64::AEK_RCPC |AArch64::AEK_RDM | AArch64::AEK_SIMD |AArch64::AEK_SSBS,"8.2-A"),ARMCPUTestParams("cortex-a72", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("cortex-a73", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("cortex-a75", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_RAS | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC,"8.2-A"),ARMCPUTestParams("cortex-a76", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC |AArch64::AEK_SSBS,"8.2-A"),ARMCPUTestParams("cortex-a76ae", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC |AArch64::AEK_SSBS,"8.2-A"),ARMCPUTestParams("cortex-a77", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC |AArch64::AEK_SSBS,"8.2-A"),ARMCPUTestParams("cortex-a78", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC |AArch64::AEK_SSBS | AArch64::AEK_PROFILE,"8.2-A"),ARMCPUTestParams("cortex-a78c", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_RAS | AArch64::AEK_CRC |AArch64::AEK_CRYPTO | AArch64::AEK_FP |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_RDM |AArch64::AEK_FP16 | AArch64::AEK_DOTPROD |AArch64::AEK_RCPC | AArch64::AEK_SSBS |AArch64::AEK_PROFILE | AArch64::AEK_FLAGM |AArch64::AEK_PAUTH | AArch64::AEK_FP16FML,"8.2-A"),ARMCPUTestParams("cortex-a710", "armv9-a", "neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_FP |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_RDM |AArch64::AEK_RCPC | AArch64::AEK_DOTPROD |AArch64::AEK_MTE | AArch64::AEK_FP16FML |AArch64::AEK_SVE | AArch64::AEK_SVE2 |AArch64::AEK_SVE2BITPERM | AArch64::AEK_PAUTH |AArch64::AEK_FLAGM | AArch64::AEK_SB |AArch64::AEK_I8MM | AArch64::AEK_BF16,"9-A"),ARMCPUTestParams("neoverse-v1", "armv8.4-a", "crypto-neon-fp-armv8",AArch64::AEK_RAS | AArch64::AEK_SVE | AArch64::AEK_SSBS |AArch64::AEK_RCPC | AArch64::AEK_CRC | AArch64::AEK_FP |AArch64::AEK_SIMD | AArch64::AEK_RAS | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_RCPC | AArch64::AEK_DOTPROD |AArch64::AEK_CRYPTO | AArch64::AEK_FP16 | AArch64::AEK_BF16 |AArch64::AEK_PROFILE | AArch64::AEK_RAND |AArch64::AEK_FP16FML | AArch64::AEK_I8MM,"8.4-A"),ARMCPUTestParams("cortex-r82", "armv8-r", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_RDM |AArch64::AEK_SSBS | AArch64::AEK_DOTPROD |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_FP16 | AArch64::AEK_FP16FML |AArch64::AEK_RAS | AArch64::AEK_RCPC |AArch64::AEK_LSE | AArch64::AEK_SB,"8-R"),ARMCPUTestParams("cortex-x1", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC |AArch64::AEK_SSBS | AArch64::AEK_PROFILE,"8.2-A"),ARMCPUTestParams("cortex-x1c", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_FP16 |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC |AArch64::AEK_SSBS | AArch64::AEK_PAUTH |AArch64::AEK_PROFILE,"8.2-A"),ARMCPUTestParams("cortex-x2", "armv9-a", "neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_FP |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_RDM |AArch64::AEK_RCPC | AArch64::AEK_DOTPROD |AArch64::AEK_MTE | AArch64::AEK_PAUTH |AArch64::AEK_I8MM | AArch64::AEK_BF16 |AArch64::AEK_SVE | AArch64::AEK_SVE2 |AArch64::AEK_SVE2BITPERM | AArch64::AEK_SSBS |AArch64::AEK_SB | AArch64::AEK_FP16FML,"9-A"),ARMCPUTestParams("cyclone", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_NONE | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("apple-a7", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_NONE | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("apple-a8", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_NONE | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("apple-a9", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_NONE | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("apple-a10", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_RDM |AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("apple-a11", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_LSE |AArch64::AEK_RAS | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_FP16,"8.2-A"),ARMCPUTestParams("apple-a12", "armv8.3-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_LSE | AArch64::AEK_RAS |AArch64::AEK_RDM | AArch64::AEK_RCPC |AArch64::AEK_FP16,"8.3-A"),ARMCPUTestParams("apple-a13", "armv8.4-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_LSE | AArch64::AEK_RAS |AArch64::AEK_RDM | AArch64::AEK_RCPC |AArch64::AEK_DOTPROD | AArch64::AEK_FP16 |AArch64::AEK_FP16FML | AArch64::AEK_SHA3,"8.4-A"),ARMCPUTestParams("apple-a14", "armv8.5-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_LSE | AArch64::AEK_RAS |AArch64::AEK_RDM | AArch64::AEK_RCPC |AArch64::AEK_DOTPROD | AArch64::AEK_FP16 |AArch64::AEK_FP16FML | AArch64::AEK_SHA3,"8.5-A"),ARMCPUTestParams("apple-m1", "armv8.5-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_LSE | AArch64::AEK_RAS |AArch64::AEK_RDM | AArch64::AEK_RCPC |AArch64::AEK_DOTPROD | AArch64::AEK_FP16 |AArch64::AEK_FP16FML | AArch64::AEK_SHA3,"8.5-A"),ARMCPUTestParams("apple-s4", "armv8.3-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_LSE | AArch64::AEK_RAS |AArch64::AEK_RDM | AArch64::AEK_RCPC |AArch64::AEK_FP16,"8.3-A"),ARMCPUTestParams("apple-s5", "armv8.3-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_LSE | AArch64::AEK_RAS |AArch64::AEK_RDM | AArch64::AEK_RCPC |AArch64::AEK_FP16,"8.3-A"),ARMCPUTestParams("exynos-m3", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("exynos-m4", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_DOTPROD | AArch64::AEK_FP |AArch64::AEK_FP16 | AArch64::AEK_LSE |AArch64::AEK_RAS | AArch64::AEK_RDM |AArch64::AEK_SIMD,"8.2-A"),ARMCPUTestParams("exynos-m5", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_DOTPROD | AArch64::AEK_FP |AArch64::AEK_FP16 | AArch64::AEK_LSE |AArch64::AEK_RAS | AArch64::AEK_RDM |AArch64::AEK_SIMD,"8.2-A"),ARMCPUTestParams("falkor", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_RDM,"8-A"),ARMCPUTestParams("kryo", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD,"8-A"),ARMCPUTestParams("neoverse-e1", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_DOTPROD | AArch64::AEK_FP |AArch64::AEK_FP16 | AArch64::AEK_LSE |AArch64::AEK_RAS | AArch64::AEK_RCPC |AArch64::AEK_RDM | AArch64::AEK_SIMD |AArch64::AEK_SSBS,"8.2-A"),ARMCPUTestParams("neoverse-n1", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_DOTPROD | AArch64::AEK_FP |AArch64::AEK_FP16 | AArch64::AEK_LSE |AArch64::AEK_PROFILE | AArch64::AEK_RAS |AArch64::AEK_RCPC | AArch64::AEK_RDM |AArch64::AEK_SIMD | AArch64::AEK_SSBS,"8.2-A"),ARMCPUTestParams("neoverse-n2", "armv8.5-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_FP16 | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_SVE |AArch64::AEK_DOTPROD | AArch64::AEK_RCPC |AArch64::AEK_RDM | AArch64::AEK_MTE |AArch64::AEK_SSBS | AArch64::AEK_SB |AArch64::AEK_SVE2 | AArch64::AEK_SVE2BITPERM |AArch64::AEK_BF16 | AArch64::AEK_I8MM,"8.5-A"),ARMCPUTestParams("ampere1", "armv8.6-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_FP | AArch64::AEK_FP16 |AArch64::AEK_SIMD | AArch64::AEK_RAS | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_RCPC | AArch64::AEK_DOTPROD |AArch64::AEK_SM4 | AArch64::AEK_SHA3 | AArch64::AEK_BF16 |AArch64::AEK_SHA2 | AArch64::AEK_AES | AArch64::AEK_I8MM |AArch64::AEK_SSBS | AArch64::AEK_SB,"8.6-A"),ARMCPUTestParams("neoverse-512tvb", "armv8.4-a", "crypto-neon-fp-armv8",AArch64::AEK_RAS | AArch64::AEK_SVE | AArch64::AEK_SSBS |AArch64::AEK_RCPC | AArch64::AEK_CRC | AArch64::AEK_FP |AArch64::AEK_SIMD | AArch64::AEK_RAS | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_RCPC | AArch64::AEK_DOTPROD |AArch64::AEK_CRYPTO | AArch64::AEK_FP16 | AArch64::AEK_BF16 |AArch64::AEK_PROFILE | AArch64::AEK_RAND |AArch64::AEK_FP16FML | AArch64::AEK_I8MM,"8.4-A"),ARMCPUTestParams("thunderx2t99", "armv8.1-a", "crypto-neon-fp-armv8",AArch64::AEK_NONE | AArch64::AEK_CRC |AArch64::AEK_CRYPTO | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_FP |AArch64::AEK_SIMD,"8.1-A"),ARMCPUTestParams("thunderx3t110", "armv8.3-a", "crypto-neon-fp-armv8",AArch64::AEK_NONE | AArch64::AEK_CRC |AArch64::AEK_CRYPTO | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_FP |AArch64::AEK_SIMD | AArch64::AEK_RAS |AArch64::AEK_RCPC,"8.3-A"),ARMCPUTestParams("thunderx", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_SIMD | AArch64::AEK_FP,"8-A"),ARMCPUTestParams("thunderxt81", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_SIMD | AArch64::AEK_FP,"8-A"),ARMCPUTestParams("thunderxt83", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_SIMD | AArch64::AEK_FP,"8-A"),ARMCPUTestParams("thunderxt88", "armv8-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_SIMD | AArch64::AEK_FP,"8-A"),ARMCPUTestParams("tsv110", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_RAS | AArch64::AEK_LSE |AArch64::AEK_RDM | AArch64::AEK_PROFILE |AArch64::AEK_FP16 | AArch64::AEK_FP16FML |AArch64::AEK_DOTPROD,"8.2-A"),ARMCPUTestParams("a64fx", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_FP16 | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_SVE |AArch64::AEK_RDM,"8.2-A"),ARMCPUTestParams("carmel", "armv8.2-a", "crypto-neon-fp-armv8",AArch64::AEK_CRC | AArch64::AEK_CRYPTO |AArch64::AEK_FP | AArch64::AEK_SIMD |AArch64::AEK_FP16 | AArch64::AEK_RAS |AArch64::AEK_LSE | AArch64::AEK_RDM,"8.2-A")));static constexpr unsigned NumAArch64CPUArchs = 54;TEST(TargetParserTest, testAArch64CPUArchList) {SmallVector<StringRef, NumAArch64CPUArchs> List;AArch64::fillValidCPUArchList(List);// No list exists for these in this test suite, so ensure all are// valid, and match the expected 'magic' count.EXPECT_EQ(List.size(), NumAArch64CPUArchs);for(StringRef CPU : List) {EXPECT_NE(AArch64::parseCPUArch(CPU), AArch64::ArchKind::INVALID);}}bool testAArch64Arch(StringRef Arch, StringRef DefaultCPU, StringRef SubArch,unsigned ArchAttr) {AArch64::ArchKind AK = AArch64::parseArch(Arch);return (AK != AArch64::ArchKind::INVALID) &&AArch64::getDefaultCPU(Arch).equals(DefaultCPU) &&AArch64::getSubArch(AK).equals(SubArch) &&(AArch64::getArchAttr(AK) == ArchAttr);}TEST(TargetParserTest, testAArch64Arch) {EXPECT_TRUE(testAArch64Arch("armv8-a", "cortex-a53", "v8",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.1-a", "generic", "v8.1a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.2-a", "generic", "v8.2a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.3-a", "generic", "v8.3a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.4-a", "generic", "v8.4a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.5-a", "generic", "v8.5a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.6-a", "generic", "v8.6a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.7-a", "generic", "v8.7a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv8.8-a", "generic", "v8.8a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv9-a", "generic", "v9a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv9.1-a", "generic", "v9.1a",ARMBuildAttrs::CPUArch::v8_A));EXPECT_TRUE(testAArch64Arch("armv9.2-a", "generic", "v9.2a",ARMBuildAttrs::CPUArch::v8_A));}bool testAArch64Extension(StringRef CPUName, AArch64::ArchKind AK,StringRef ArchExt) {return AArch64::getDefaultExtensions(CPUName, AK) &AArch64::parseArchExt(ArchExt);}TEST(TargetParserTest, testAArch64Extension) {EXPECT_FALSE(testAArch64Extension("cortex-a34",AArch64::ArchKind::INVALID, "ras"));EXPECT_FALSE(testAArch64Extension("cortex-a35",AArch64::ArchKind::INVALID, "ras"));EXPECT_FALSE(testAArch64Extension("cortex-a53",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("cortex-a55",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("cortex-a55",AArch64::ArchKind::INVALID, "fp16"));EXPECT_FALSE(testAArch64Extension("cortex-a55",AArch64::ArchKind::INVALID, "fp16fml"));EXPECT_TRUE(testAArch64Extension("cortex-a55",AArch64::ArchKind::INVALID, "dotprod"));EXPECT_FALSE(testAArch64Extension("cortex-a57",AArch64::ArchKind::INVALID, "ras"));EXPECT_FALSE(testAArch64Extension("cortex-a72",AArch64::ArchKind::INVALID, "ras"));EXPECT_FALSE(testAArch64Extension("cortex-a73",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("cortex-a75",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("cortex-a75",AArch64::ArchKind::INVALID, "fp16"));EXPECT_FALSE(testAArch64Extension("cortex-a75",AArch64::ArchKind::INVALID, "fp16fml"));EXPECT_TRUE(testAArch64Extension("cortex-a75",AArch64::ArchKind::INVALID, "dotprod"));EXPECT_TRUE(testAArch64Extension("cortex-r82",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("cortex-r82",AArch64::ArchKind::INVALID, "fp16"));EXPECT_TRUE(testAArch64Extension("cortex-r82",AArch64::ArchKind::INVALID, "fp16fml"));EXPECT_TRUE(testAArch64Extension("cortex-r82",AArch64::ArchKind::INVALID, "dotprod"));EXPECT_TRUE(testAArch64Extension("cortex-r82",AArch64::ArchKind::INVALID, "lse"));EXPECT_FALSE(testAArch64Extension("cyclone",AArch64::ArchKind::INVALID, "ras"));EXPECT_FALSE(testAArch64Extension("exynos-m3",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("exynos-m4",AArch64::ArchKind::INVALID, "dotprod"));EXPECT_TRUE(testAArch64Extension("exynos-m4",AArch64::ArchKind::INVALID, "fp16"));EXPECT_TRUE(testAArch64Extension("exynos-m4",AArch64::ArchKind::INVALID, "lse"));EXPECT_TRUE(testAArch64Extension("exynos-m4",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("exynos-m4",AArch64::ArchKind::INVALID, "rdm"));EXPECT_TRUE(testAArch64Extension("exynos-m5",AArch64::ArchKind::INVALID, "dotprod"));EXPECT_TRUE(testAArch64Extension("exynos-m5",AArch64::ArchKind::INVALID, "fp16"));EXPECT_TRUE(testAArch64Extension("exynos-m5",AArch64::ArchKind::INVALID, "lse"));EXPECT_TRUE(testAArch64Extension("exynos-m5",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("exynos-m5",AArch64::ArchKind::INVALID, "rdm"));EXPECT_TRUE(testAArch64Extension("falkor",AArch64::ArchKind::INVALID, "rdm"));EXPECT_FALSE(testAArch64Extension("kryo",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("saphira",AArch64::ArchKind::INVALID, "crc"));EXPECT_TRUE(testAArch64Extension("saphira",AArch64::ArchKind::INVALID, "lse"));EXPECT_TRUE(testAArch64Extension("saphira",AArch64::ArchKind::INVALID, "rdm"));EXPECT_TRUE(testAArch64Extension("saphira",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("saphira",AArch64::ArchKind::INVALID, "rcpc"));EXPECT_TRUE(testAArch64Extension("saphira",AArch64::ArchKind::INVALID, "profile"));EXPECT_FALSE(testAArch64Extension("saphira",AArch64::ArchKind::INVALID, "fp16"));EXPECT_FALSE(testAArch64Extension("thunderx2t99",AArch64::ArchKind::INVALID, "ras"));EXPECT_FALSE(testAArch64Extension("thunderx",AArch64::ArchKind::INVALID, "lse"));EXPECT_FALSE(testAArch64Extension("thunderxt81",AArch64::ArchKind::INVALID, "lse"));EXPECT_FALSE(testAArch64Extension("thunderxt83",AArch64::ArchKind::INVALID, "lse"));EXPECT_FALSE(testAArch64Extension("thunderxt88",AArch64::ArchKind::INVALID, "lse"));EXPECT_TRUE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "crypto"));EXPECT_FALSE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "sha3"));EXPECT_FALSE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "sm4"));EXPECT_TRUE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "ras"));EXPECT_TRUE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "profile"));EXPECT_TRUE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "fp16"));EXPECT_TRUE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "fp16fml"));EXPECT_TRUE(testAArch64Extension("tsv110",AArch64::ArchKind::INVALID, "dotprod"));EXPECT_TRUE(testAArch64Extension("a64fx",AArch64::ArchKind::INVALID, "fp16"));EXPECT_TRUE(testAArch64Extension("a64fx",AArch64::ArchKind::INVALID, "sve"));EXPECT_FALSE(testAArch64Extension("a64fx",AArch64::ArchKind::INVALID, "sve2"));EXPECT_TRUE(testAArch64Extension("carmel", AArch64::ArchKind::INVALID, "crypto"));EXPECT_TRUE(testAArch64Extension("carmel", AArch64::ArchKind::INVALID, "fp16"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8A, "ras"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_1A, "ras"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_2A, "profile"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_2A, "fp16"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_2A, "fp16fml"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_3A, "fp16"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_3A, "fp16fml"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_4A, "fp16"));EXPECT_FALSE(testAArch64Extension("generic", AArch64::ArchKind::ARMV8_4A, "fp16fml"));}TEST(TargetParserTest, AArch64ExtensionFeatures) {std::vector<uint64_t> Extensions = {AArch64::AEK_CRC, AArch64::AEK_LSE, AArch64::AEK_RDM,AArch64::AEK_CRYPTO, AArch64::AEK_SM4, AArch64::AEK_SHA3,AArch64::AEK_SHA2, AArch64::AEK_AES, AArch64::AEK_DOTPROD,AArch64::AEK_FP, AArch64::AEK_SIMD, AArch64::AEK_FP16,AArch64::AEK_FP16FML, AArch64::AEK_PROFILE, AArch64::AEK_RAS,AArch64::AEK_SVE, AArch64::AEK_SVE2, AArch64::AEK_SVE2AES,AArch64::AEK_SVE2SM4, AArch64::AEK_SVE2SHA3, AArch64::AEK_SVE2BITPERM,AArch64::AEK_RCPC, AArch64::AEK_RAND, AArch64::AEK_MTE,AArch64::AEK_SSBS, AArch64::AEK_SB, AArch64::AEK_PREDRES,AArch64::AEK_BF16, AArch64::AEK_I8MM, AArch64::AEK_F32MM,AArch64::AEK_F64MM, AArch64::AEK_TME, AArch64::AEK_LS64,AArch64::AEK_BRBE, AArch64::AEK_PAUTH, AArch64::AEK_FLAGM,AArch64::AEK_SME, AArch64::AEK_SMEF64, AArch64::AEK_SMEI64,AArch64::AEK_HBC, AArch64::AEK_MOPS, AArch64::AEK_PERFMON};std::vector<StringRef> Features;uint64_t ExtVal = 0;for (auto Ext : Extensions)ExtVal |= Ext;// INVALID and NONE have no feature names.EXPECT_FALSE(AArch64::getExtensionFeatures(AArch64::AEK_INVALID, Features));EXPECT_TRUE(!Features.size());// We return True here because NONE is a valid choice.EXPECT_TRUE(AArch64::getExtensionFeatures(AArch64::AEK_NONE, Features));EXPECT_TRUE(!Features.size());AArch64::getExtensionFeatures(ExtVal, Features);EXPECT_EQ(Extensions.size(), Features.size());EXPECT_TRUE(llvm::is_contained(Features, "+crc"));EXPECT_TRUE(llvm::is_contained(Features, "+lse"));EXPECT_TRUE(llvm::is_contained(Features, "+rdm"));EXPECT_TRUE(llvm::is_contained(Features, "+crypto"));EXPECT_TRUE(llvm::is_contained(Features, "+sm4"));EXPECT_TRUE(llvm::is_contained(Features, "+sha3"));EXPECT_TRUE(llvm::is_contained(Features, "+sha2"));EXPECT_TRUE(llvm::is_contained(Features, "+aes"));EXPECT_TRUE(llvm::is_contained(Features, "+dotprod"));EXPECT_TRUE(llvm::is_contained(Features, "+fp-armv8"));EXPECT_TRUE(llvm::is_contained(Features, "+neon"));EXPECT_TRUE(llvm::is_contained(Features, "+fullfp16"));EXPECT_TRUE(llvm::is_contained(Features, "+fp16fml"));EXPECT_TRUE(llvm::is_contained(Features, "+spe"));EXPECT_TRUE(llvm::is_contained(Features, "+ras"));EXPECT_TRUE(llvm::is_contained(Features, "+sve"));EXPECT_TRUE(llvm::is_contained(Features, "+sve2"));EXPECT_TRUE(llvm::is_contained(Features, "+sve2-aes"));EXPECT_TRUE(llvm::is_contained(Features, "+sve2-sm4"));EXPECT_TRUE(llvm::is_contained(Features, "+sve2-sha3"));EXPECT_TRUE(llvm::is_contained(Features, "+sve2-bitperm"));EXPECT_TRUE(llvm::is_contained(Features, "+rcpc"));EXPECT_TRUE(llvm::is_contained(Features, "+rand"));EXPECT_TRUE(llvm::is_contained(Features, "+mte"));EXPECT_TRUE(llvm::is_contained(Features, "+ssbs"));EXPECT_TRUE(llvm::is_contained(Features, "+sb"));EXPECT_TRUE(llvm::is_contained(Features, "+predres"));EXPECT_TRUE(llvm::is_contained(Features, "+bf16"));EXPECT_TRUE(llvm::is_contained(Features, "+i8mm"));EXPECT_TRUE(llvm::is_contained(Features, "+f32mm"));EXPECT_TRUE(llvm::is_contained(Features, "+f64mm"));EXPECT_TRUE(llvm::is_contained(Features, "+tme"));EXPECT_TRUE(llvm::is_contained(Features, "+ls64"));EXPECT_TRUE(llvm::is_contained(Features, "+brbe"));EXPECT_TRUE(llvm::is_contained(Features, "+pauth"));EXPECT_TRUE(llvm::is_contained(Features, "+flagm"));EXPECT_TRUE(llvm::is_contained(Features, "+sme"));EXPECT_TRUE(llvm::is_contained(Features, "+sme-f64"));EXPECT_TRUE(llvm::is_contained(Features, "+sme-i64"));EXPECT_TRUE(llvm::is_contained(Features, "+hbc"));EXPECT_TRUE(llvm::is_contained(Features, "+mops"));EXPECT_TRUE(llvm::is_contained(Features, "+perfmon"));// Assuming we listed every extension above, this should produce the same// result. (note that AEK_NONE doesn't have a name so it won't be in the// result despite its bit being set)std::vector<StringRef> AllFeatures;EXPECT_TRUE(AArch64::getExtensionFeatures(-1, AllFeatures));EXPECT_THAT(Features, ::testing::ContainerEq(AllFeatures));}TEST(TargetParserTest, AArch64ArchFeatures) {std::vector<StringRef> Features;for (auto AK : AArch64::ArchKinds) {if (AK == AArch64::ArchKind::INVALID)EXPECT_FALSE(AArch64::getArchFeatures(AK, Features));elseEXPECT_TRUE(AArch64::getArchFeatures(AK, Features));}}TEST(TargetParserTest, AArch64ArchExtFeature) {const char *ArchExt[][4] = {{"crc", "nocrc", "+crc", "-crc"},{"crypto", "nocrypto", "+crypto", "-crypto"},{"flagm", "noflagm", "+flagm", "-flagm"},{"fp", "nofp", "+fp-armv8", "-fp-armv8"},{"simd", "nosimd", "+neon", "-neon"},{"fp16", "nofp16", "+fullfp16", "-fullfp16"},{"fp16fml", "nofp16fml", "+fp16fml", "-fp16fml"},{"profile", "noprofile", "+spe", "-spe"},{"ras", "noras", "+ras", "-ras"},{"lse", "nolse", "+lse", "-lse"},{"rdm", "nordm", "+rdm", "-rdm"},{"sve", "nosve", "+sve", "-sve"},{"sve2", "nosve2", "+sve2", "-sve2"},{"sve2-aes", "nosve2-aes", "+sve2-aes", "-sve2-aes"},{"sve2-sm4", "nosve2-sm4", "+sve2-sm4", "-sve2-sm4"},{"sve2-sha3", "nosve2-sha3", "+sve2-sha3", "-sve2-sha3"},{"sve2-bitperm", "nosve2-bitperm", "+sve2-bitperm", "-sve2-bitperm"},{"dotprod", "nodotprod", "+dotprod", "-dotprod"},{"rcpc", "norcpc", "+rcpc", "-rcpc"},{"rng", "norng", "+rand", "-rand"},{"memtag", "nomemtag", "+mte", "-mte"},{"tme", "notme", "+tme", "-tme"},{"pauth", "nopauth", "+pauth", "-pauth"},{"ssbs", "nossbs", "+ssbs", "-ssbs"},{"sb", "nosb", "+sb", "-sb"},{"predres", "nopredres", "+predres", "-predres"},{"i8mm", "noi8mm", "+i8mm", "-i8mm"},{"f32mm", "nof32mm", "+f32mm", "-f32mm"},{"f64mm", "nof64mm", "+f64mm", "-f64mm"},{"sme", "nosme", "+sme", "-sme"},{"sme-f64", "nosme-f64", "+sme-f64", "-sme-f64"},{"sme-i64", "nosme-i64", "+sme-i64", "-sme-i64"},{"hbc", "nohbc", "+hbc", "-hbc"},{"mops", "nomops", "+mops", "-mops"},{"pmuv3", "nopmuv3", "+perfmon", "-perfmon"},};for (unsigned i = 0; i < array_lengthof(ArchExt); i++) {EXPECT_EQ(StringRef(ArchExt[i][2]),AArch64::getArchExtFeature(ArchExt[i][0]));EXPECT_EQ(StringRef(ArchExt[i][3]),AArch64::getArchExtFeature(ArchExt[i][1]));}}} // namespace
//===- llvm/unittest/Support/TarWriterTest.cpp ----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/TarWriter.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"#include <vector>using namespace llvm;using llvm::unittest::TempFile;namespace {struct UstarHeader {char Name[100];char Mode[8];char Uid[8];char Gid[8];char Size[12];char Mtime[12];char Checksum[8];char TypeFlag;char Linkname[100];char Magic[6];char Version[2];char Uname[32];char Gname[32];char DevMajor[8];char DevMinor[8];char Prefix[155];char Pad[12];};class TarWriterTest : public ::testing::Test {};static std::vector<uint8_t> createTar(StringRef Base, StringRef Filename) {TempFile TarWriterTest("TarWriterTest", "tar", "", /*Unique*/ true);// Create a tar file.Expected<std::unique_ptr<TarWriter>> TarOrErr =TarWriter::create(TarWriterTest.path(), Base);EXPECT_TRUE((bool)TarOrErr);std::unique_ptr<TarWriter> Tar = std::move(*TarOrErr);Tar->append(Filename, "contents");Tar.reset();// Read the tar file.ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =MemoryBuffer::getFile(TarWriterTest.path());EXPECT_TRUE((bool)MBOrErr);std::unique_ptr<MemoryBuffer> MB = std::move(*MBOrErr);std::vector<uint8_t> Buf((const uint8_t *)MB->getBufferStart(),(const uint8_t *)MB->getBufferEnd());// Windows does not allow us to remove a mmap'ed files, so// unmap first and then remove the temporary file.MB = nullptr;return Buf;}static UstarHeader createUstar(StringRef Base, StringRef Filename) {std::vector<uint8_t> Buf = createTar(Base, Filename);EXPECT_GE(Buf.size(), sizeof(UstarHeader));return *reinterpret_cast<const UstarHeader *>(Buf.data());}TEST_F(TarWriterTest, Basics) {UstarHeader Hdr = createUstar("base", "file");EXPECT_EQ("ustar", StringRef(Hdr.Magic));EXPECT_EQ("00", StringRef(Hdr.Version, 2));EXPECT_EQ("base/file", StringRef(Hdr.Name));EXPECT_EQ("00000000010", StringRef(Hdr.Size));}TEST_F(TarWriterTest, LongFilename) {// The prefix is prefixed by an additional '/' so it's one longer than the// number of x's here.std::string x136(136, 'x');std::string x137(137, 'x');std::string y99(99, 'y');std::string y100(100, 'y');UstarHeader Hdr1 = createUstar("", x136 + "/" + y99);EXPECT_EQ("/" + x136, StringRef(Hdr1.Prefix));EXPECT_EQ(y99, StringRef(Hdr1.Name));UstarHeader Hdr2 = createUstar("", x137 + "/" + y99);EXPECT_EQ("", StringRef(Hdr2.Prefix));EXPECT_EQ("", StringRef(Hdr2.Name));UstarHeader Hdr3 = createUstar("", x136 + "/" + y100);EXPECT_EQ("", StringRef(Hdr3.Prefix));EXPECT_EQ("", StringRef(Hdr3.Name));UstarHeader Hdr4 = createUstar("", x137 + "/" + y100);EXPECT_EQ("", StringRef(Hdr4.Prefix));EXPECT_EQ("", StringRef(Hdr4.Name));std::string yz = "yyyyyyyyyyyyyyyyyyyy/zzzzzzzzzzzzzzzzzzzz";UstarHeader Hdr5 = createUstar("", x136 + "/" + yz);EXPECT_EQ("/" + x136, StringRef(Hdr5.Prefix));EXPECT_EQ(yz, StringRef(Hdr5.Name));}TEST_F(TarWriterTest, Pax) {std::vector<uint8_t> Buf = createTar("", std::string(200, 'x'));EXPECT_GE(Buf.size(), 1024u);auto *Hdr = reinterpret_cast<const UstarHeader *>(Buf.data());EXPECT_EQ("", StringRef(Hdr->Prefix));EXPECT_EQ("", StringRef(Hdr->Name));StringRef Pax = StringRef((char *)(Buf.data() + 512), 512);EXPECT_TRUE(Pax.startswith("211 path=/" + std::string(200, 'x')));}TEST_F(TarWriterTest, SingleFile) {TempFile TarWriterTest("TarWriterTest", "tar", "", /*Unique*/ true);Expected<std::unique_ptr<TarWriter>> TarOrErr =TarWriter::create(TarWriterTest.path(), "");EXPECT_TRUE((bool)TarOrErr);std::unique_ptr<TarWriter> Tar = std::move(*TarOrErr);Tar->append("FooPath", "foo");Tar.reset();uint64_t TarSize;std::error_code EC = sys::fs::file_size(TarWriterTest.path(), TarSize);EXPECT_FALSE((bool)EC);EXPECT_EQ(TarSize, 2048ULL);}TEST_F(TarWriterTest, NoDuplicate) {TempFile TarWriterTest("TarWriterTest", "tar", "", /*Unique*/ true);Expected<std::unique_ptr<TarWriter>> TarOrErr =TarWriter::create(TarWriterTest.path(), "");EXPECT_TRUE((bool)TarOrErr);std::unique_ptr<TarWriter> Tar = std::move(*TarOrErr);Tar->append("FooPath", "foo");Tar->append("BarPath", "bar");Tar.reset();uint64_t TarSize;std::error_code EC = sys::fs::file_size(TarWriterTest.path(), TarSize);EXPECT_FALSE((bool)EC);EXPECT_EQ(TarSize, 3072ULL);}TEST_F(TarWriterTest, Duplicate) {TempFile TarWriterTest("TarWriterTest", "tar", "", /*Unique*/ true);Expected<std::unique_ptr<TarWriter>> TarOrErr =TarWriter::create(TarWriterTest.path(), "");EXPECT_TRUE((bool)TarOrErr);std::unique_ptr<TarWriter> Tar = std::move(*TarOrErr);Tar->append("FooPath", "foo");Tar->append("FooPath", "bar");Tar.reset();uint64_t TarSize;std::error_code EC = sys::fs::file_size(TarWriterTest.path(), TarSize);EXPECT_FALSE((bool)EC);EXPECT_EQ(TarSize, 2048ULL);}} // namespace
//===- unittests/Support/SymbolRemappingReaderTest.cpp --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/SymbolRemappingReader.h"#include "llvm/Support/MemoryBuffer.h"#include "gtest/gtest.h"using namespace llvm;namespace {class SymbolRemappingReaderTest : public testing::Test {public:std::unique_ptr<MemoryBuffer> Buffer;SymbolRemappingReader Reader;std::string readWithErrors(StringRef Text, StringRef BufferName) {Buffer = MemoryBuffer::getMemBuffer(Text, BufferName);Error E = Reader.read(*Buffer);EXPECT_TRUE((bool)E);return toString(std::move(E));}void read(StringRef Text, StringRef BufferName) {Buffer = MemoryBuffer::getMemBuffer(Text, BufferName);Error E = Reader.read(*Buffer);EXPECT_FALSE((bool)E);}};} // unnamed namespaceTEST_F(SymbolRemappingReaderTest, ParseErrors) {EXPECT_EQ(readWithErrors("error", "foo.map"),"foo.map:1: Expected 'kind mangled_name mangled_name', ""found 'error'");EXPECT_EQ(readWithErrors("error m1 m2", "foo.map"),"foo.map:1: Invalid kind, expected 'name', 'type', or 'encoding', ""found 'error'");}TEST_F(SymbolRemappingReaderTest, DemanglingErrors) {EXPECT_EQ(readWithErrors("type i banana", "foo.map"),"foo.map:1: Could not demangle 'banana' as a <type>; ""invalid mangling?");EXPECT_EQ(readWithErrors("name i 1X", "foo.map"),"foo.map:1: Could not demangle 'i' as a <name>; ""invalid mangling?");EXPECT_EQ(readWithErrors("name 1X 1fv", "foo.map"),"foo.map:1: Could not demangle '1fv' as a <name>; ""invalid mangling?");EXPECT_EQ(readWithErrors("encoding 1fv 1f1gE", "foo.map"),"foo.map:1: Could not demangle '1f1gE' as a <encoding>; ""invalid mangling?");}TEST_F(SymbolRemappingReaderTest, BadMappingOrder) {StringRef Map = R"(# N::foo == M::barname N1N3fooE N1M3barE# N:: == M::name 1N 1M)";EXPECT_EQ(readWithErrors(Map, "foo.map"),"foo.map:6: Manglings '1N' and '1M' have both been used in prior ""remappings. Move this remapping earlier in the file.");}TEST_F(SymbolRemappingReaderTest, RemappingsAdded) {StringRef Map = R"(# A::foo == B::barname N1A3fooE N1B3barE# int == longtype i l# void f<int>() = void g<int>()encoding 1fIiEvv 1gIiEvv)";read(Map, "foo.map");auto Key = Reader.insert("_ZN1B3bar3bazIiEEvv");EXPECT_NE(Key, SymbolRemappingReader::Key());EXPECT_EQ(Key, Reader.lookup("_ZN1A3foo3bazIlEEvv"));EXPECT_NE(Key, Reader.lookup("_ZN1C3foo3bazIlEEvv"));Key = Reader.insert("_Z1fIiEvv");EXPECT_NE(Key, SymbolRemappingReader::Key());EXPECT_EQ(Key, Reader.lookup("_Z1gIlEvv"));}
//===- unittests/Support/SwapByteOrderTest.cpp - swap byte order test -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/SwapByteOrder.h"#include "gtest/gtest.h"#include <cstdlib>#include <ctime>using namespace llvm;#undef maxnamespace {TEST(ByteSwap, Swap_32) {EXPECT_EQ(0x44332211u, ByteSwap_32(0x11223344));EXPECT_EQ(0xDDCCBBAAu, ByteSwap_32(0xAABBCCDD));}TEST(ByteSwap, Swap_64) {EXPECT_EQ(0x8877665544332211ULL, ByteSwap_64(0x1122334455667788LL));EXPECT_EQ(0x1100FFEEDDCCBBAAULL, ByteSwap_64(0xAABBCCDDEEFF0011LL));}// In these first two tests all of the original_uintx values are truncated// except for 64. We could avoid this, but there's really no point.TEST(getSwappedBytes, UnsignedRoundTrip) {// The point of the bit twiddling of magic is to test with and without bits// in every byte.uint64_t value = 1;for (std::size_t i = 0; i <= sizeof(value); ++i) {uint8_t original_uint8 = static_cast<uint8_t>(value);EXPECT_EQ(original_uint8,sys::getSwappedBytes(sys::getSwappedBytes(original_uint8)));uint16_t original_uint16 = static_cast<uint16_t>(value);EXPECT_EQ(original_uint16,sys::getSwappedBytes(sys::getSwappedBytes(original_uint16)));uint32_t original_uint32 = static_cast<uint32_t>(value);EXPECT_EQ(original_uint32,sys::getSwappedBytes(sys::getSwappedBytes(original_uint32)));uint64_t original_uint64 = static_cast<uint64_t>(value);EXPECT_EQ(original_uint64,sys::getSwappedBytes(sys::getSwappedBytes(original_uint64)));value = (value << 8) | 0x55; // binary 0101 0101.}}TEST(getSwappedBytes, SignedRoundTrip) {// The point of the bit twiddling of magic is to test with and without bits// in every byte.uint64_t value = 1;for (std::size_t i = 0; i <= sizeof(value); ++i) {int8_t original_int8 = static_cast<int8_t>(value);EXPECT_EQ(original_int8,sys::getSwappedBytes(sys::getSwappedBytes(original_int8)));int16_t original_int16 = static_cast<int16_t>(value);EXPECT_EQ(original_int16,sys::getSwappedBytes(sys::getSwappedBytes(original_int16)));int32_t original_int32 = static_cast<int32_t>(value);EXPECT_EQ(original_int32,sys::getSwappedBytes(sys::getSwappedBytes(original_int32)));int64_t original_int64 = static_cast<int64_t>(value);EXPECT_EQ(original_int64,sys::getSwappedBytes(sys::getSwappedBytes(original_int64)));// Test other sign.value *= -1;original_int8 = static_cast<int8_t>(value);EXPECT_EQ(original_int8,sys::getSwappedBytes(sys::getSwappedBytes(original_int8)));original_int16 = static_cast<int16_t>(value);EXPECT_EQ(original_int16,sys::getSwappedBytes(sys::getSwappedBytes(original_int16)));original_int32 = static_cast<int32_t>(value);EXPECT_EQ(original_int32,sys::getSwappedBytes(sys::getSwappedBytes(original_int32)));original_int64 = static_cast<int64_t>(value);EXPECT_EQ(original_int64,sys::getSwappedBytes(sys::getSwappedBytes(original_int64)));// Return to normal sign and twiddle.value *= -1;value = (value << 8) | 0x55; // binary 0101 0101.}}TEST(getSwappedBytes, uint8_t) {EXPECT_EQ(uint8_t(0x11), sys::getSwappedBytes(uint8_t(0x11)));}TEST(getSwappedBytes, uint16_t) {EXPECT_EQ(uint16_t(0x1122), sys::getSwappedBytes(uint16_t(0x2211)));}TEST(getSwappedBytes, uint32_t) {EXPECT_EQ(uint32_t(0x11223344), sys::getSwappedBytes(uint32_t(0x44332211)));}TEST(getSwappedBytes, uint64_t) {EXPECT_EQ(uint64_t(0x1122334455667788ULL),sys::getSwappedBytes(uint64_t(0x8877665544332211ULL)));}TEST(getSwappedBytes, int8_t) {EXPECT_EQ(int8_t(0x11), sys::getSwappedBytes(int8_t(0x11)));}TEST(getSwappedBytes, int16_t) {EXPECT_EQ(int16_t(0x1122), sys::getSwappedBytes(int16_t(0x2211)));}TEST(getSwappedBytes, int32_t) {EXPECT_EQ(int32_t(0x11223344), sys::getSwappedBytes(int32_t(0x44332211)));}TEST(getSwappedBytes, int64_t) {EXPECT_EQ(int64_t(0x1122334455667788LL),sys::getSwappedBytes(int64_t(0x8877665544332211LL)));}TEST(getSwappedBytes, float) {EXPECT_EQ(1.79366203433576585078237386661e-43f, sys::getSwappedBytes(-0.0f));// 0x11223344EXPECT_EQ(7.1653228759765625e2f, sys::getSwappedBytes(1.2795344e-28f));}TEST(getSwappedBytes, double) {EXPECT_EQ(6.32404026676795576546008054871e-322, sys::getSwappedBytes(-0.0));// 0x1122334455667788EXPECT_EQ(-7.08687663657301358331704585496e-268,sys::getSwappedBytes(3.84141202447173065923064450234e-226));}TEST(swapByteOrder, uint8_t) {uint8_t value = 0x11;sys::swapByteOrder(value);EXPECT_EQ(uint8_t(0x11), value);}TEST(swapByteOrder, uint16_t) {uint16_t value = 0x2211;sys::swapByteOrder(value);EXPECT_EQ(uint16_t(0x1122), value);}TEST(swapByteOrder, uint32_t) {uint32_t value = 0x44332211;sys::swapByteOrder(value);EXPECT_EQ(uint32_t(0x11223344), value);}TEST(swapByteOrder, uint64_t) {uint64_t value = 0x8877665544332211ULL;sys::swapByteOrder(value);EXPECT_EQ(uint64_t(0x1122334455667788ULL), value);}TEST(swapByteOrder, int8_t) {int8_t value = 0x11;sys::swapByteOrder(value);EXPECT_EQ(int8_t(0x11), value);}TEST(swapByteOrder, int16_t) {int16_t value = 0x2211;sys::swapByteOrder(value);EXPECT_EQ(int16_t(0x1122), value);}TEST(swapByteOrder, int32_t) {int32_t value = 0x44332211;sys::swapByteOrder(value);EXPECT_EQ(int32_t(0x11223344), value);}TEST(swapByteOrder, int64_t) {int64_t value = 0x8877665544332211LL;sys::swapByteOrder(value);EXPECT_EQ(int64_t(0x1122334455667788LL), value);}TEST(swapByteOrder, float) {float value = 7.1653228759765625e2f; // 0x44332211sys::swapByteOrder(value);EXPECT_EQ(1.2795344e-28f, value);}TEST(swapByteOrder, double) {double value = -7.08687663657301358331704585496e-268; // 0x8877665544332211sys::swapByteOrder(value);EXPECT_EQ(3.84141202447173065923064450234e-226, value);}}
//===- unittests/Support/SuffixTreeTest.cpp - suffix tree tests -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/SuffixTree.h"#include "gtest/gtest.h"#include <vector>using namespace llvm;namespace {// Each example vector has a unique element at the end to represent the end of// the string// Tests that The SuffixTree finds a simple repetition of the substring {1, 2}// {1, 2} twice in the provided string.TEST(SuffixTreeTest, TestSingleRepetition) {std::vector<unsigned> SimpleRepetitionData = {1, 2, 1, 2, 3};SuffixTree ST(SimpleRepetitionData);std::vector<SuffixTree::RepeatedSubstring> SubStrings;for (auto It = ST.begin(); It != ST.end(); It++)SubStrings.push_back(*It);ASSERT_EQ(SubStrings.size(), 1u);EXPECT_EQ(SubStrings[0].Length, 2u);EXPECT_EQ(SubStrings[0].StartIndices.size(), 2u);for (unsigned StartIdx : SubStrings[0].StartIndices) {EXPECT_EQ(SimpleRepetitionData[StartIdx], 1u);EXPECT_EQ(SimpleRepetitionData[StartIdx + 1], 2u);}}// Tests that the SuffixTree is able to find the substrings {1, 2, 3} at// at indices 0 and 3 as well as the substrings {2, 3} at indices 1 and 4.// This test also serves as a flag for improvements to the suffix tree.//// FIXME: Right now, the longest repeated substring from a specific index is// returned, this could be improved to return the longest repeated substring, as// well as those that are smaller.TEST(SuffixTreeTest, TestLongerRepetition) {std::vector<unsigned> RepeatedRepetitionData = {1, 2, 3, 1, 2, 3, 4};SuffixTree ST(RepeatedRepetitionData);std::vector<SuffixTree::RepeatedSubstring> SubStrings;for (auto It = ST.begin(); It != ST.end(); It++)SubStrings.push_back(*It);EXPECT_EQ(SubStrings.size(), 2u);unsigned Len;for (SuffixTree::RepeatedSubstring &RS : SubStrings) {Len = RS.Length;bool IsExpectedLen = (Len == 3u || Len == 2u);bool IsExpectedIndex;ASSERT_TRUE(IsExpectedLen);if (Len == 3u) {for (unsigned StartIdx : RS.StartIndices) {IsExpectedIndex = (StartIdx == 0u || StartIdx == 3u);EXPECT_TRUE(IsExpectedIndex);EXPECT_EQ(RepeatedRepetitionData[StartIdx], 1u);EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 2u);EXPECT_EQ(RepeatedRepetitionData[StartIdx + 2], 3u);}} else {for (unsigned StartIdx : RS.StartIndices) {IsExpectedIndex = (StartIdx == 1u || StartIdx == 4u);EXPECT_TRUE(IsExpectedIndex);EXPECT_EQ(RepeatedRepetitionData[StartIdx], 2u);EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 3u);}}}}// Tests that the SuffixTree is able to find substring {1, 1, 1, 1, 1} at// indices 0 and 1.//// FIXME: Add support for detecting {1, 1} and {1, 1, 1}TEST(SuffixTreeTest, TestSingleCharacterRepeat) {std::vector<unsigned> RepeatedRepetitionData = {1, 1, 1, 1, 1, 1, 2};std::vector<unsigned>::iterator RRDIt, RRDIt2;SuffixTree ST(RepeatedRepetitionData);std::vector<SuffixTree::RepeatedSubstring> SubStrings;for (auto It = ST.begin(); It != ST.end(); It++)SubStrings.push_back(*It);EXPECT_EQ(SubStrings.size(), 1u);for (SuffixTree::RepeatedSubstring &RS : SubStrings) {EXPECT_EQ(RS.StartIndices.size(),RepeatedRepetitionData.size() - RS.Length);for (unsigned StartIdx : SubStrings[0].StartIndices) {RRDIt = RRDIt2 = RepeatedRepetitionData.begin();std::advance(RRDIt, StartIdx);std::advance(RRDIt2, StartIdx + SubStrings[0].Length);ASSERT_TRUE(all_of(make_range<std::vector<unsigned>::iterator>(RRDIt, RRDIt2),[](unsigned Elt) { return Elt == 1; }));}}}// The suffix tree cannot currently find repeated substrings in strings of the// form {1, 2, 3, 1, 2, 3}, because the two {1, 2, 3}s are adjacent ("tandem// repeats")//// FIXME: Teach the SuffixTree to recognize these cases.TEST(SuffixTreeTest, TestTandemRepeat) {std::vector<unsigned> RepeatedRepetitionData = {1, 2, 3, 1, 2, 3};SuffixTree ST(RepeatedRepetitionData);std::vector<SuffixTree::RepeatedSubstring> SubStrings;for (auto It = ST.begin(); It != ST.end(); It++)SubStrings.push_back(*It);EXPECT_EQ(SubStrings.size(), 0u);}// Tests that the SuffixTree does not erroneously include values that are not// in repeated substrings. That is, only finds {1, 1} at indices 0 and 3 and// does not include 2 and 3.TEST(SuffixTreeTest, TestExclusion) {std::vector<unsigned> RepeatedRepetitionData = {1, 1, 2, 1, 1, 3};std::vector<unsigned>::iterator RRDIt, RRDIt2;SuffixTree ST(RepeatedRepetitionData);std::vector<SuffixTree::RepeatedSubstring> SubStrings;for (auto It = ST.begin(); It != ST.end(); It++)SubStrings.push_back(*It);EXPECT_EQ(SubStrings.size(), 1u);bool IsExpectedIndex;for (SuffixTree::RepeatedSubstring &RS : SubStrings) {for (unsigned StartIdx : RS.StartIndices) {IsExpectedIndex = (StartIdx == 0u || StartIdx == 3u);EXPECT_TRUE(IsExpectedIndex);RRDIt = RRDIt2 = RepeatedRepetitionData.begin();std::advance(RRDIt, StartIdx);std::advance(RRDIt2, StartIdx + RS.Length);EXPECT_TRUE(all_of(make_range<std::vector<unsigned>::iterator>(RRDIt, RRDIt2),[](unsigned Elt) { return Elt == 1; }));}}}} // namespace
//===- SpecialCaseListTest.cpp - Unit tests for SpecialCaseList -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/SpecialCaseList.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/VirtualFileSystem.h"#include "gtest/gtest.h"using namespace llvm;namespace {class SpecialCaseListTest : public ::testing::Test {protected:std::unique_ptr<SpecialCaseList> makeSpecialCaseList(StringRef List,std::string &Error) {std::unique_ptr<MemoryBuffer> MB = MemoryBuffer::getMemBuffer(List);return SpecialCaseList::create(MB.get(), Error);}std::unique_ptr<SpecialCaseList> makeSpecialCaseList(StringRef List) {std::string Error;auto SCL = makeSpecialCaseList(List, Error);assert(SCL);assert(Error == "");return SCL;}std::string makeSpecialCaseListFile(StringRef Contents) {int FD;SmallString<64> Path;sys::fs::createTemporaryFile("SpecialCaseListTest", "temp", FD, Path);raw_fd_ostream OF(FD, true, true);OF << Contents;OF.close();return std::string(Path.str());}};TEST_F(SpecialCaseListTest, Basic) {std::unique_ptr<SpecialCaseList> SCL =makeSpecialCaseList("# This is a comment.\n""\n""src:hello\n""src:bye\n""src:hi=category\n""src:z*=category\n");EXPECT_TRUE(SCL->inSection("", "src", "hello"));EXPECT_TRUE(SCL->inSection("", "src", "bye"));EXPECT_TRUE(SCL->inSection("", "src", "hi", "category"));EXPECT_TRUE(SCL->inSection("", "src", "zzzz", "category"));EXPECT_FALSE(SCL->inSection("", "src", "hi"));EXPECT_FALSE(SCL->inSection("", "fun", "hello"));EXPECT_FALSE(SCL->inSection("", "src", "hello", "category"));EXPECT_EQ(3u, SCL->inSectionBlame("", "src", "hello"));EXPECT_EQ(4u, SCL->inSectionBlame("", "src", "bye"));EXPECT_EQ(5u, SCL->inSectionBlame("", "src", "hi", "category"));EXPECT_EQ(6u, SCL->inSectionBlame("", "src", "zzzz", "category"));EXPECT_EQ(0u, SCL->inSectionBlame("", "src", "hi"));EXPECT_EQ(0u, SCL->inSectionBlame("", "fun", "hello"));EXPECT_EQ(0u, SCL->inSectionBlame("", "src", "hello", "category"));}TEST_F(SpecialCaseListTest, CorrectErrorLineNumberWithBlankLine) {std::string Error;EXPECT_EQ(nullptr, makeSpecialCaseList("# This is a comment.\n""\n""[not valid\n",Error));EXPECT_TRUE(((StringRef)Error).startswith("malformed section header on line 3:"));EXPECT_EQ(nullptr, makeSpecialCaseList("\n\n\n""[not valid\n",Error));EXPECT_TRUE(((StringRef)Error).startswith("malformed section header on line 4:"));}TEST_F(SpecialCaseListTest, SectionRegexErrorHandling) {std::string Error;EXPECT_EQ(makeSpecialCaseList("[address", Error), nullptr);EXPECT_TRUE(((StringRef)Error).startswith("malformed section header "));EXPECT_EQ(makeSpecialCaseList("[[]", Error), nullptr);EXPECT_TRUE(((StringRef)Error).startswith("malformed regex for section [: "));EXPECT_EQ(makeSpecialCaseList("src:=", Error), nullptr);EXPECT_TRUE(((StringRef)Error).endswith("Supplied regexp was blank"));}TEST_F(SpecialCaseListTest, Section) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("src:global\n""[sect1|sect2]\n""src:test1\n""[sect3*]\n""src:test2\n");EXPECT_TRUE(SCL->inSection("arbitrary", "src", "global"));EXPECT_TRUE(SCL->inSection("", "src", "global"));EXPECT_TRUE(SCL->inSection("sect1", "src", "test1"));EXPECT_FALSE(SCL->inSection("sect1-arbitrary", "src", "test1"));EXPECT_FALSE(SCL->inSection("sect", "src", "test1"));EXPECT_FALSE(SCL->inSection("sect1", "src", "test2"));EXPECT_TRUE(SCL->inSection("sect2", "src", "test1"));EXPECT_TRUE(SCL->inSection("sect3", "src", "test2"));EXPECT_TRUE(SCL->inSection("sect3-arbitrary", "src", "test2"));EXPECT_FALSE(SCL->inSection("", "src", "test1"));EXPECT_FALSE(SCL->inSection("", "src", "test2"));}TEST_F(SpecialCaseListTest, GlobalInit) {std::unique_ptr<SpecialCaseList> SCL =makeSpecialCaseList("global:foo=init\n");EXPECT_FALSE(SCL->inSection("", "global", "foo"));EXPECT_FALSE(SCL->inSection("", "global", "bar"));EXPECT_TRUE(SCL->inSection("", "global", "foo", "init"));EXPECT_FALSE(SCL->inSection("", "global", "bar", "init"));SCL = makeSpecialCaseList("type:t2=init\n");EXPECT_FALSE(SCL->inSection("", "type", "t1"));EXPECT_FALSE(SCL->inSection("", "type", "t2"));EXPECT_FALSE(SCL->inSection("", "type", "t1", "init"));EXPECT_TRUE(SCL->inSection("", "type", "t2", "init"));SCL = makeSpecialCaseList("src:hello=init\n");EXPECT_FALSE(SCL->inSection("", "src", "hello"));EXPECT_FALSE(SCL->inSection("", "src", "bye"));EXPECT_TRUE(SCL->inSection("", "src", "hello", "init"));EXPECT_FALSE(SCL->inSection("", "src", "bye", "init"));}TEST_F(SpecialCaseListTest, Substring) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("src:hello\n""fun:foo\n""global:bar\n");EXPECT_FALSE(SCL->inSection("", "src", "othello"));EXPECT_FALSE(SCL->inSection("", "fun", "tomfoolery"));EXPECT_FALSE(SCL->inSection("", "global", "bartender"));SCL = makeSpecialCaseList("fun:*foo*\n");EXPECT_TRUE(SCL->inSection("", "fun", "tomfoolery"));EXPECT_TRUE(SCL->inSection("", "fun", "foobar"));}TEST_F(SpecialCaseListTest, InvalidSpecialCaseList) {std::string Error;EXPECT_EQ(nullptr, makeSpecialCaseList("badline", Error));EXPECT_EQ("malformed line 1: 'badline'", Error);EXPECT_EQ(nullptr, makeSpecialCaseList("src:bad[a-", Error));EXPECT_EQ("malformed regex in line 1: 'bad[a-': invalid character range",Error);EXPECT_EQ(nullptr, makeSpecialCaseList("src:a.c\n""fun:fun(a\n",Error));EXPECT_EQ("malformed regex in line 2: 'fun(a': parentheses not balanced",Error);std::vector<std::string> Files(1, "unexisting");EXPECT_EQ(nullptr,SpecialCaseList::create(Files, *vfs::getRealFileSystem(), Error));EXPECT_EQ(0U, Error.find("can't open file 'unexisting':"));}TEST_F(SpecialCaseListTest, EmptySpecialCaseList) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("");EXPECT_FALSE(SCL->inSection("", "foo", "bar"));}TEST_F(SpecialCaseListTest, MultipleExclusions) {std::vector<std::string> Files;Files.push_back(makeSpecialCaseListFile("src:bar\n""src:*foo*\n""src:ban=init\n"));Files.push_back(makeSpecialCaseListFile("src:baz\n""src:*fog*\n"));auto SCL = SpecialCaseList::createOrDie(Files, *vfs::getRealFileSystem());EXPECT_TRUE(SCL->inSection("", "src", "bar"));EXPECT_TRUE(SCL->inSection("", "src", "baz"));EXPECT_FALSE(SCL->inSection("", "src", "ban"));EXPECT_TRUE(SCL->inSection("", "src", "ban", "init"));EXPECT_TRUE(SCL->inSection("", "src", "tomfoolery"));EXPECT_TRUE(SCL->inSection("", "src", "tomfoglery"));for (auto &Path : Files)sys::fs::remove(Path);}TEST_F(SpecialCaseListTest, NoTrigramsInRules) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:b.r\n""fun:za*az\n");EXPECT_TRUE(SCL->inSection("", "fun", "bar"));EXPECT_FALSE(SCL->inSection("", "fun", "baz"));EXPECT_TRUE(SCL->inSection("", "fun", "zakaz"));EXPECT_FALSE(SCL->inSection("", "fun", "zaraza"));}TEST_F(SpecialCaseListTest, NoTrigramsInARule) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:*bar*\n""fun:za*az\n");EXPECT_TRUE(SCL->inSection("", "fun", "abara"));EXPECT_FALSE(SCL->inSection("", "fun", "bor"));EXPECT_TRUE(SCL->inSection("", "fun", "zakaz"));EXPECT_FALSE(SCL->inSection("", "fun", "zaraza"));}TEST_F(SpecialCaseListTest, RepetitiveRule) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:*bar*bar*bar*bar*\n""fun:bar*\n");EXPECT_TRUE(SCL->inSection("", "fun", "bara"));EXPECT_FALSE(SCL->inSection("", "fun", "abara"));EXPECT_TRUE(SCL->inSection("", "fun", "barbarbarbar"));EXPECT_TRUE(SCL->inSection("", "fun", "abarbarbarbar"));EXPECT_FALSE(SCL->inSection("", "fun", "abarbarbar"));}TEST_F(SpecialCaseListTest, SpecialSymbolRule) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("src:*c\\+\\+abi*\n");EXPECT_TRUE(SCL->inSection("", "src", "c++abi"));EXPECT_FALSE(SCL->inSection("", "src", "c\\+\\+abi"));}TEST_F(SpecialCaseListTest, PopularTrigram) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("fun:*aaaaaa*\n""fun:*aaaaa*\n""fun:*aaaa*\n""fun:*aaa*\n");EXPECT_TRUE(SCL->inSection("", "fun", "aaa"));EXPECT_TRUE(SCL->inSection("", "fun", "aaaa"));EXPECT_TRUE(SCL->inSection("", "fun", "aaaabbbaaa"));}TEST_F(SpecialCaseListTest, EscapedSymbols) {std::unique_ptr<SpecialCaseList> SCL = makeSpecialCaseList("src:*c\\+\\+abi*\n""src:*hello\\\\world*\n");EXPECT_TRUE(SCL->inSection("", "src", "dir/c++abi"));EXPECT_FALSE(SCL->inSection("", "src", "dir/c\\+\\+abi"));EXPECT_FALSE(SCL->inSection("", "src", "c\\+\\+abi"));EXPECT_TRUE(SCL->inSection("", "src", "C:\\hello\\world"));EXPECT_TRUE(SCL->inSection("", "src", "hello\\world"));EXPECT_FALSE(SCL->inSection("", "src", "hello\\\\world"));}}
//===- unittests/Support/SourceMgrTest.cpp - SourceMgr tests --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/SourceMgr.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {class SourceMgrTest : public testing::Test {public:SourceMgr SM;unsigned MainBufferID;std::string Output;void setMainBuffer(StringRef Text, StringRef BufferName) {std::unique_ptr<MemoryBuffer> MainBuffer =MemoryBuffer::getMemBuffer(Text, BufferName);MainBufferID = SM.AddNewSourceBuffer(std::move(MainBuffer), llvm::SMLoc());}SMLoc getLoc(unsigned Offset) {return SMLoc::getFromPointer(SM.getMemoryBuffer(MainBufferID)->getBufferStart() + Offset);}SMRange getRange(unsigned Offset, unsigned Length) {return SMRange(getLoc(Offset), getLoc(Offset + Length));}void printMessage(SMLoc Loc, SourceMgr::DiagKind Kind,const Twine &Msg, ArrayRef<SMRange> Ranges,ArrayRef<SMFixIt> FixIts) {raw_string_ostream OS(Output);SM.PrintMessage(OS, Loc, Kind, Msg, Ranges, FixIts);}};} // unnamed namespaceTEST_F(SourceMgrTest, BasicError) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:5: error: message\n""aaa bbb\n"" ^\n",Output);}TEST_F(SourceMgrTest, BasicWarning) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Warning, "message", None, None);EXPECT_EQ("file.in:1:5: warning: message\n""aaa bbb\n"" ^\n",Output);}TEST_F(SourceMgrTest, BasicRemark) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Remark, "message", None, None);EXPECT_EQ("file.in:1:5: remark: message\n""aaa bbb\n"" ^\n",Output);}TEST_F(SourceMgrTest, BasicNote) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Note, "message", None, None);EXPECT_EQ("file.in:1:5: note: message\n""aaa bbb\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationAtEndOfLine) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(6), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:7: error: message\n""aaa bbb\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationAtNewline) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(7), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:8: error: message\n""aaa bbb\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationAtEmptyBuffer) {setMainBuffer("", "file.in");printMessage(getLoc(0), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:1: error: message\n""\n""^\n",Output);}TEST_F(SourceMgrTest, LocationJustOnSoleNewline) {setMainBuffer("\n", "file.in");printMessage(getLoc(0), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:1: error: message\n""\n""^\n",Output);}TEST_F(SourceMgrTest, LocationJustAfterSoleNewline) {setMainBuffer("\n", "file.in");printMessage(getLoc(1), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:2:1: error: message\n""\n""^\n",Output);}TEST_F(SourceMgrTest, LocationJustAfterNonNewline) {setMainBuffer("123", "file.in");printMessage(getLoc(3), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:4: error: message\n""123\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationOnFirstLineOfMultiline) {setMainBuffer("1234\n6789\n", "file.in");printMessage(getLoc(3), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:4: error: message\n""1234\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationOnEOLOfFirstLineOfMultiline) {setMainBuffer("1234\n6789\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:1:5: error: message\n""1234\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationOnSecondLineOfMultiline) {setMainBuffer("1234\n6789\n", "file.in");printMessage(getLoc(5), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:2:1: error: message\n""6789\n""^\n",Output);}TEST_F(SourceMgrTest, LocationOnSecondLineOfMultilineNoSecondEOL) {setMainBuffer("1234\n6789", "file.in");printMessage(getLoc(5), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:2:1: error: message\n""6789\n""^\n",Output);}TEST_F(SourceMgrTest, LocationOnEOLOfSecondSecondLineOfMultiline) {setMainBuffer("1234\n6789\n", "file.in");printMessage(getLoc(9), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:2:5: error: message\n""6789\n"" ^\n",Output);}#define STRING_LITERAL_253_BYTES \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n1234567890\n" \"1234567890\n"//===----------------------------------------------------------------------===//// 255-byte buffer tests//===----------------------------------------------------------------------===//TEST_F(SourceMgrTest, LocationBeforeEndOf255ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"12" // + 2 = 255 bytes, "file.in");printMessage(getLoc(253), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:1: error: message\n""12\n""^\n",Output);}TEST_F(SourceMgrTest, LocationAtEndOf255ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"12" // + 2 = 255 bytes, "file.in");printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:2: error: message\n""12\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationPastEndOf255ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"12" // + 2 = 255 bytes, "file.in");printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:3: error: message\n""12\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationBeforeEndOf255ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"1\n" // + 2 = 255 bytes, "file.in");printMessage(getLoc(253), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:1: error: message\n""1\n""^\n",Output);}TEST_F(SourceMgrTest, LocationAtEndOf255ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"1\n" // + 2 = 255 bytes, "file.in");printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:2: error: message\n""1\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationPastEndOf255ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"1\n" // + 2 = 255 bytes, "file.in");printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:25:1: error: message\n""\n""^\n",Output);}//===----------------------------------------------------------------------===//// 256-byte buffer tests//===----------------------------------------------------------------------===//TEST_F(SourceMgrTest, LocationBeforeEndOf256ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"123" // + 3 = 256 bytes, "file.in");printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:2: error: message\n""123\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationAtEndOf256ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"123" // + 3 = 256 bytes, "file.in");printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:3: error: message\n""123\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationPastEndOf256ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"123" // + 3 = 256 bytes, "file.in");printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:4: error: message\n""123\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationBeforeEndOf256ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"12\n" // + 3 = 256 bytes, "file.in");printMessage(getLoc(254), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:2: error: message\n""12\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationAtEndOf256ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"12\n" // + 3 = 256 bytes, "file.in");printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:3: error: message\n""12\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationPastEndOf256ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"12\n" // + 3 = 256 bytes, "file.in");printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:25:1: error: message\n""\n""^\n",Output);}//===----------------------------------------------------------------------===//// 257-byte buffer tests//===----------------------------------------------------------------------===//TEST_F(SourceMgrTest, LocationBeforeEndOf257ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"1234" // + 4 = 257 bytes, "file.in");printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:3: error: message\n""1234\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationAtEndOf257ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"1234" // + 4 = 257 bytes, "file.in");printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:4: error: message\n""1234\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationPastEndOf257ByteBuffer) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"1234" // + 4 = 257 bytes, "file.in");printMessage(getLoc(257), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:5: error: message\n""1234\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationBeforeEndOf257ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"123\n" // + 4 = 257 bytes, "file.in");printMessage(getLoc(255), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:3: error: message\n""123\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationAtEndOf257ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"123\n" // + 4 = 257 bytes, "file.in");printMessage(getLoc(256), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:24:4: error: message\n""123\n"" ^\n",Output);}TEST_F(SourceMgrTest, LocationPastEndOf257ByteBufferEndingInNewline) {setMainBuffer(STRING_LITERAL_253_BYTES // first 253 bytes"123\n" // + 4 = 257 bytes, "file.in");printMessage(getLoc(257), SourceMgr::DK_Error, "message", None, None);EXPECT_EQ("file.in:25:1: error: message\n""\n""^\n",Output);}TEST_F(SourceMgrTest, BasicRange) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(4, 3), None);EXPECT_EQ("file.in:1:5: error: message\n""aaa bbb\n"" ^~~\n",Output);}TEST_F(SourceMgrTest, RangeWithTab) {setMainBuffer("aaa\tbbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(3, 3), None);EXPECT_EQ("file.in:1:5: error: message\n""aaa bbb\n"" ~~~~~^~\n",Output);}TEST_F(SourceMgrTest, MultiLineRange) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(4, 7), None);EXPECT_EQ("file.in:1:5: error: message\n""aaa bbb\n"" ^~~\n",Output);}TEST_F(SourceMgrTest, MultipleRanges) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");SMRange Ranges[] = { getRange(0, 3), getRange(4, 3) };printMessage(getLoc(4), SourceMgr::DK_Error, "message", Ranges, None);EXPECT_EQ("file.in:1:5: error: message\n""aaa bbb\n""~~~ ^~~\n",Output);}TEST_F(SourceMgrTest, OverlappingRanges) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");SMRange Ranges[] = { getRange(0, 3), getRange(2, 4) };printMessage(getLoc(4), SourceMgr::DK_Error, "message", Ranges, None);EXPECT_EQ("file.in:1:5: error: message\n""aaa bbb\n""~~~~^~\n",Output);}TEST_F(SourceMgrTest, BasicFixit) {setMainBuffer("aaa bbb\nccc ddd\n", "file.in");printMessage(getLoc(4), SourceMgr::DK_Error, "message", None,makeArrayRef(SMFixIt(getRange(4, 3), "zzz")));EXPECT_EQ("file.in:1:5: error: message\n""aaa bbb\n"" ^~~\n"" zzz\n",Output);}TEST_F(SourceMgrTest, FixitForTab) {setMainBuffer("aaa\tbbb\nccc ddd\n", "file.in");printMessage(getLoc(3), SourceMgr::DK_Error, "message", None,makeArrayRef(SMFixIt(getRange(3, 1), "zzz")));EXPECT_EQ("file.in:1:4: error: message\n""aaa bbb\n"" ^^^^^\n"" zzz\n",Output);}
//===- llvm/unittest/Support/ScopedPrinterTest.cpp - ScopedPrinter tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ScopedPrinter.h"#include "llvm/ADT/APSInt.h"#include "gtest/gtest.h"#include <vector>using namespace llvm;TEST(JSONScopedPrinterTest, PrettyPrintCtor) {auto PrintFunc = [](ScopedPrinter &W) {DictScope D(W);W.printString("Key", "Value");};std::string StreamBuffer;raw_string_ostream OS(StreamBuffer);JSONScopedPrinter PrettyPrintWriter(OS, /*PrettyPrint=*/true);JSONScopedPrinter NoPrettyPrintWriter(OS, /*PrettyPrint=*/false);const char *PrettyPrintOut = R"({"Key": "Value"})";const char *NoPrettyPrintOut = R"({"Key":"Value"})";PrintFunc(PrettyPrintWriter);EXPECT_EQ(PrettyPrintOut, OS.str());StreamBuffer.clear();PrintFunc(NoPrettyPrintWriter);EXPECT_EQ(NoPrettyPrintOut, OS.str());}TEST(JSONScopedPrinterTest, DelimitedScopeCtor) {std::string StreamBuffer;raw_string_ostream OS(StreamBuffer);{JSONScopedPrinter DictScopeWriter(OS, /*PrettyPrint=*/false,std::make_unique<DictScope>());DictScopeWriter.printString("Label", "DictScope");}EXPECT_EQ(R"({"Label":"DictScope"})", OS.str());StreamBuffer.clear();{JSONScopedPrinter ListScopeWriter(OS, /*PrettyPrint=*/false,std::make_unique<ListScope>());ListScopeWriter.printString("ListScope");}EXPECT_EQ(R"(["ListScope"])", OS.str());StreamBuffer.clear();{JSONScopedPrinter NoScopeWriter(OS, /*PrettyPrint=*/false);NoScopeWriter.printString("NoScope");}EXPECT_EQ(R"("NoScope")", OS.str());}class ScopedPrinterTest : public ::testing::Test {protected:std::string StreamBuffer;raw_string_ostream OS;ScopedPrinter Writer;JSONScopedPrinter JSONWriter;bool HasPrintedToJSON;ScopedPrinterTest(): OS(StreamBuffer), Writer(OS), JSONWriter(OS, /*PrettyPrint=*/true),HasPrintedToJSON(false) {}using PrintFunc = function_ref<void(ScopedPrinter &)>;void verifyScopedPrinter(StringRef Expected, PrintFunc Func) {Func(Writer);Writer.flush();EXPECT_EQ(Expected.str(), OS.str());StreamBuffer.clear();}void verifyJSONScopedPrinter(StringRef Expected, PrintFunc Func) {{DictScope D(JSONWriter);Func(JSONWriter);}JSONWriter.flush();EXPECT_EQ(Expected.str(), OS.str());StreamBuffer.clear();HasPrintedToJSON = true;}void verifyAll(StringRef ExpectedOut, StringRef JSONExpectedOut,PrintFunc Func) {verifyScopedPrinter(ExpectedOut, Func);verifyJSONScopedPrinter(JSONExpectedOut, Func);}void TearDown() {// JSONScopedPrinter fails an assert if nothing's been printed.if (!HasPrintedToJSON)JSONWriter.printString("");}};TEST_F(ScopedPrinterTest, GetKind) {EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::Base, Writer.getKind());EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::JSON, JSONWriter.getKind());}TEST_F(ScopedPrinterTest, ClassOf) {EXPECT_TRUE(ScopedPrinter::classof(&Writer));EXPECT_TRUE(JSONScopedPrinter::classof(&JSONWriter));EXPECT_FALSE(ScopedPrinter::classof(&JSONWriter));EXPECT_FALSE(JSONScopedPrinter::classof(&Writer));}TEST_F(ScopedPrinterTest, Indent) {auto PrintFunc = [](ScopedPrinter &W) {W.printString("|");W.indent();W.printString("|");W.indent(2);W.printString("|");};const char *ExpectedOut = R"(|||)";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, Unindent) {auto PrintFunc = [](ScopedPrinter &W) {W.indent(3);W.printString("|");W.unindent(2);W.printString("|");W.unindent();W.printString("|");W.unindent();W.printString("|");};const char *ExpectedOut = R"( ||||)";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, ResetIndent) {auto PrintFunc = [](ScopedPrinter &W) {W.indent(4);W.printString("|");W.resetIndent();W.printString("|");};const char *ExpectedOut = R"( ||)";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintIndent) {auto PrintFunc = [](ScopedPrinter &W) {W.printIndent();W.printString("|");W.indent();W.printIndent();W.printString("|");};const char *ExpectedOut = R"(||)";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, GetIndentLevel) {EXPECT_EQ(Writer.getIndentLevel(), 0);Writer.indent();EXPECT_EQ(Writer.getIndentLevel(), 1);Writer.indent();EXPECT_EQ(Writer.getIndentLevel(), 2);Writer.unindent();EXPECT_EQ(Writer.getIndentLevel(), 1);Writer.indent();Writer.resetIndent();EXPECT_EQ(Writer.getIndentLevel(), 0);Writer.unindent();EXPECT_EQ(Writer.getIndentLevel(), 0);Writer.indent();EXPECT_EQ(Writer.getIndentLevel(), 1);}TEST_F(ScopedPrinterTest, SetPrefix) {auto PrintFunc = [](ScopedPrinter &W) {W.setPrefix("Prefix1");W.indent();W.printIndent();W.printString("|");W.unindent();W.printIndent();W.printString("|");W.setPrefix("Prefix2");W.printIndent();W.printString("|");};const char *ExpectedOut = R"(Prefix1 Prefix1 |Prefix1Prefix1|Prefix2Prefix2|)";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintEnum) {auto PrintFunc = [](ScopedPrinter &W) {const EnumEntry<int> EnumList[] = {{"Name1", "AltName1", 1},{"Name2", "AltName2", 2},{"Name3", "AltName3", 3},{"Name4", "AltName4", 2}};EnumEntry<int> OtherEnum{"Name5", "AltName5", 5};W.printEnum("Exists", EnumList[1].Value, makeArrayRef(EnumList));W.printEnum("DoesNotExist", OtherEnum.Value, makeArrayRef(EnumList));};const char *ExpectedOut = R"(Exists: Name2 (0x2)DoesNotExist: 0x5)";const char *JSONExpectedOut = R"({"Exists": {"Value": "Name2","RawValue": 2},"DoesNotExist": 5})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintFlag) {auto PrintFunc = [](ScopedPrinter &W) {const EnumEntry<uint16_t> SingleBitFlags[] = {{"Name0", "AltName0", 0},{"Name1", "AltName1", 1},{"Name2", "AltName2", 1 << 1},{"Name3", "AltName3", 1 << 2}};const EnumEntry<uint16_t> UnsortedFlags[] = {{"C", "c", 1}, {"B", "b", 1 << 1}, {"A", "a", 1 << 2}};const EnumEntry<uint16_t> EnumFlags[] = {{"FirstByte1", "First1", 0x1u}, {"FirstByte2", "First2", 0x2u},{"FirstByte3", "First3", 0x3u}, {"SecondByte1", "Second1", 0x10u},{"SecondByte2", "Second2", 0x20u}, {"SecondByte3", "Second3", 0x30u},{"ThirdByte1", "Third1", 0x100u}, {"ThirdByte2", "Third2", 0x200u},{"ThirdByte3", "Third3", 0x300u}};W.printFlags("ZeroFlag", 0, makeArrayRef(SingleBitFlags));W.printFlags("NoFlag", 1 << 3, makeArrayRef(SingleBitFlags));W.printFlags("Flag1", SingleBitFlags[1].Value,makeArrayRef(SingleBitFlags));W.printFlags("Flag1&3", (1 << 2) + 1, makeArrayRef(SingleBitFlags));W.printFlags("ZeroFlagRaw", 0);W.printFlags("NoFlagRaw", 1 << 3);W.printFlags("Flag1Raw", SingleBitFlags[1].Value);W.printFlags("Flag1&3Raw", (1 << 2) + 1);W.printFlags("FlagSorted", (1 << 2) + (1 << 1) + 1,makeArrayRef(UnsortedFlags));uint16_t NoBitMask = 0;uint16_t FirstByteMask = 0xFu;uint16_t SecondByteMask = 0xF0u;uint16_t ThirdByteMask = 0xF00u;W.printFlags("NoBitMask", 0xFFFu, makeArrayRef(EnumFlags), NoBitMask);W.printFlags("FirstByteMask", 0x3u, makeArrayRef(EnumFlags), FirstByteMask);W.printFlags("SecondByteMask", 0x30u, makeArrayRef(EnumFlags),SecondByteMask);W.printFlags("ValueOutsideMask", 0x1u, makeArrayRef(EnumFlags),SecondByteMask);W.printFlags("FirstSecondByteMask", 0xFFu, makeArrayRef(EnumFlags),FirstByteMask, SecondByteMask);W.printFlags("FirstSecondThirdByteMask", 0x333u, makeArrayRef(EnumFlags),FirstByteMask, SecondByteMask, ThirdByteMask);};const char *ExpectedOut = R"(ZeroFlag [ (0x0)]NoFlag [ (0x8)]Flag1 [ (0x1)Name1 (0x1)]Flag1&3 [ (0x5)Name1 (0x1)Name3 (0x4)]ZeroFlagRaw [ (0x0)]NoFlagRaw [ (0x8)0x8]Flag1Raw [ (0x1)0x1]Flag1&3Raw [ (0x5)0x10x4]FlagSorted [ (0x7)A (0x4)B (0x2)C (0x1)]NoBitMask [ (0xFFF)FirstByte1 (0x1)FirstByte2 (0x2)FirstByte3 (0x3)SecondByte1 (0x10)SecondByte2 (0x20)SecondByte3 (0x30)ThirdByte1 (0x100)ThirdByte2 (0x200)ThirdByte3 (0x300)]FirstByteMask [ (0x3)FirstByte3 (0x3)]SecondByteMask [ (0x30)SecondByte3 (0x30)]ValueOutsideMask [ (0x1)FirstByte1 (0x1)]FirstSecondByteMask [ (0xFF)]FirstSecondThirdByteMask [ (0x333)FirstByte3 (0x3)SecondByte3 (0x30)ThirdByte3 (0x300)])";const char *JSONExpectedOut = R"({"ZeroFlag": {"RawFlags": 0,"Flags": []},"NoFlag": {"RawFlags": 8,"Flags": []},"Flag1": {"RawFlags": 1,"Flags": [{"Name": "Name1","Value": 1}]},"Flag1&3": {"RawFlags": 5,"Flags": [{"Name": "Name1","Value": 1},{"Name": "Name3","Value": 4}]},"ZeroFlagRaw": {"RawFlags": 0,"Flags": []},"NoFlagRaw": {"RawFlags": 8,"Flags": [8]},"Flag1Raw": {"RawFlags": 1,"Flags": [1]},"Flag1&3Raw": {"RawFlags": 5,"Flags": [1,4]},"FlagSorted": {"RawFlags": 7,"Flags": [{"Name": "A","Value": 4},{"Name": "B","Value": 2},{"Name": "C","Value": 1}]},"NoBitMask": {"RawFlags": 4095,"Flags": [{"Name": "FirstByte1","Value": 1},{"Name": "FirstByte2","Value": 2},{"Name": "FirstByte3","Value": 3},{"Name": "SecondByte1","Value": 16},{"Name": "SecondByte2","Value": 32},{"Name": "SecondByte3","Value": 48},{"Name": "ThirdByte1","Value": 256},{"Name": "ThirdByte2","Value": 512},{"Name": "ThirdByte3","Value": 768}]},"FirstByteMask": {"RawFlags": 3,"Flags": [{"Name": "FirstByte3","Value": 3}]},"SecondByteMask": {"RawFlags": 48,"Flags": [{"Name": "SecondByte3","Value": 48}]},"ValueOutsideMask": {"RawFlags": 1,"Flags": [{"Name": "FirstByte1","Value": 1}]},"FirstSecondByteMask": {"RawFlags": 255,"Flags": []},"FirstSecondThirdByteMask": {"RawFlags": 819,"Flags": [{"Name": "FirstByte3","Value": 3},{"Name": "SecondByte3","Value": 48},{"Name": "ThirdByte3","Value": 768}]}})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintNumber) {auto PrintFunc = [](ScopedPrinter &W) {uint64_t Unsigned64Max = std::numeric_limits<uint64_t>::max();uint64_t Unsigned64Min = std::numeric_limits<uint64_t>::min();W.printNumber("uint64_t-max", Unsigned64Max);W.printNumber("uint64_t-min", Unsigned64Min);uint32_t Unsigned32Max = std::numeric_limits<uint32_t>::max();uint32_t Unsigned32Min = std::numeric_limits<uint32_t>::min();W.printNumber("uint32_t-max", Unsigned32Max);W.printNumber("uint32_t-min", Unsigned32Min);uint16_t Unsigned16Max = std::numeric_limits<uint16_t>::max();uint16_t Unsigned16Min = std::numeric_limits<uint16_t>::min();W.printNumber("uint16_t-max", Unsigned16Max);W.printNumber("uint16_t-min", Unsigned16Min);uint8_t Unsigned8Max = std::numeric_limits<uint8_t>::max();uint8_t Unsigned8Min = std::numeric_limits<uint8_t>::min();W.printNumber("uint8_t-max", Unsigned8Max);W.printNumber("uint8_t-min", Unsigned8Min);int64_t Signed64Max = std::numeric_limits<int64_t>::max();int64_t Signed64Min = std::numeric_limits<int64_t>::min();W.printNumber("int64_t-max", Signed64Max);W.printNumber("int64_t-min", Signed64Min);int32_t Signed32Max = std::numeric_limits<int32_t>::max();int32_t Signed32Min = std::numeric_limits<int32_t>::min();W.printNumber("int32_t-max", Signed32Max);W.printNumber("int32_t-min", Signed32Min);int16_t Signed16Max = std::numeric_limits<int16_t>::max();int16_t Signed16Min = std::numeric_limits<int16_t>::min();W.printNumber("int16_t-max", Signed16Max);W.printNumber("int16_t-min", Signed16Min);int8_t Signed8Max = std::numeric_limits<int8_t>::max();int8_t Signed8Min = std::numeric_limits<int8_t>::min();W.printNumber("int8_t-max", Signed8Max);W.printNumber("int8_t-min", Signed8Min);APSInt LargeNum("9999999999999999999999");W.printNumber("apsint", LargeNum);W.printNumber("label", "value", 0);};const char *ExpectedOut = R"(uint64_t-max: 18446744073709551615uint64_t-min: 0uint32_t-max: 4294967295uint32_t-min: 0uint16_t-max: 65535uint16_t-min: 0uint8_t-max: 255uint8_t-min: 0int64_t-max: 9223372036854775807int64_t-min: -9223372036854775808int32_t-max: 2147483647int32_t-min: -2147483648int16_t-max: 32767int16_t-min: -32768int8_t-max: 127int8_t-min: -128apsint: 9999999999999999999999label: value (0))";const char *JSONExpectedOut = R"({"uint64_t-max": 18446744073709551615,"uint64_t-min": 0,"uint32_t-max": 4294967295,"uint32_t-min": 0,"uint16_t-max": 65535,"uint16_t-min": 0,"uint8_t-max": 255,"uint8_t-min": 0,"int64_t-max": 9223372036854775807,"int64_t-min": -9223372036854775808,"int32_t-max": 2147483647,"int32_t-min": -2147483648,"int16_t-max": 32767,"int16_t-min": -32768,"int8_t-max": 127,"int8_t-min": -128,"apsint": 9999999999999999999999,"label": {"Value": "value","RawValue": 0}})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintBoolean) {auto PrintFunc = [](ScopedPrinter &W) {W.printBoolean("True", true);W.printBoolean("False", false);};const char *ExpectedOut = R"(True: YesFalse: No)";const char *JSONExpectedOut = R"({"True": true,"False": false})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintVersion) {auto PrintFunc = [](ScopedPrinter &W) {W.printVersion("Version", "123", "456", "789");};const char *ExpectedOut = R"(Version: 123.456.789)";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintList) {auto PrintFunc = [](ScopedPrinter &W) {const std::vector<uint64_t> EmptyList;const std::vector<std::string> StringList = {"foo", "bar", "baz"};const bool BoolList[] = {true, false};const std::vector<uint64_t> Unsigned64List = {std::numeric_limits<uint64_t>::max(),std::numeric_limits<uint64_t>::min()};const std::vector<uint32_t> Unsigned32List = {std::numeric_limits<uint32_t>::max(),std::numeric_limits<uint32_t>::min()};const std::vector<uint16_t> Unsigned16List = {std::numeric_limits<uint16_t>::max(),std::numeric_limits<uint16_t>::min()};const std::vector<uint8_t> Unsigned8List = {std::numeric_limits<uint8_t>::max(),std::numeric_limits<uint8_t>::min()};const std::vector<int64_t> Signed64List = {std::numeric_limits<int64_t>::max(),std::numeric_limits<int64_t>::min()};const std::vector<int32_t> Signed32List = {std::numeric_limits<int32_t>::max(),std::numeric_limits<int32_t>::min()};const std::vector<int16_t> Signed16List = {std::numeric_limits<int16_t>::max(),std::numeric_limits<int16_t>::min()};const std::vector<int8_t> Signed8List = {std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::min()};const std::vector<APSInt> APSIntList = {APSInt("9999999999999999999999"),APSInt("-9999999999999999999999")};W.printList("EmptyList", EmptyList);W.printList("StringList", StringList);W.printList("BoolList", makeArrayRef(BoolList));W.printList("uint64List", Unsigned64List);W.printList("uint32List", Unsigned32List);W.printList("uint16List", Unsigned16List);W.printList("uint8List", Unsigned8List);W.printList("int64List", Signed64List);W.printList("int32List", Signed32List);W.printList("int16List", Signed16List);W.printList("int8List", Signed8List);W.printList("APSIntList", APSIntList);};const char *ExpectedOut = R"(EmptyList: []StringList: [foo, bar, baz]BoolList: [1, 0]uint64List: [18446744073709551615, 0]uint32List: [4294967295, 0]uint16List: [65535, 0]uint8List: [255, 0]int64List: [9223372036854775807, -9223372036854775808]int32List: [2147483647, -2147483648]int16List: [32767, -32768]int8List: [127, -128]APSIntList: [9999999999999999999999, -9999999999999999999999])";const char *JSONExpectedOut = R"({"EmptyList": [],"StringList": ["foo","bar","baz"],"BoolList": [true,false],"uint64List": [18446744073709551615,0],"uint32List": [4294967295,0],"uint16List": [65535,0],"uint8List": [255,0],"int64List": [9223372036854775807,-9223372036854775808],"int32List": [2147483647,-2147483648],"int16List": [32767,-32768],"int8List": [127,-128],"APSIntList": [9999999999999999999999,-9999999999999999999999]})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintListPrinter) {auto PrintFunc = [](ScopedPrinter &W) {const std::string StringList[] = {"a", "ab", "abc"};W.printList("StringSizeList", StringList,[](raw_ostream &OS, StringRef Item) { OS << Item.size(); });};const char *ExpectedOut = R"(StringSizeList: [1, 2, 3])";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintHex) {auto PrintFunc = [](ScopedPrinter &W) {W.printHex("HexNumber", 0x10);W.printHex("HexLabel", "Name", 0x10);};const char *ExpectedOut = R"(HexNumber: 0x10HexLabel: Name (0x10))";const char *JSONExpectedOut = R"({"HexNumber": 16,"HexLabel": {"Value": "Name","RawValue": 16}})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintHexList) {auto PrintFunc = [](ScopedPrinter &W) {const uint64_t HexList[] = {0x1, 0x10, 0x100};W.printHexList("HexList", HexList);};const char *ExpectedOut = R"(HexList: [0x1, 0x10, 0x100])";const char *JSONExpectedOut = R"({"HexList": [1,16,256]})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintSymbolOffset) {auto PrintFunc = [](ScopedPrinter &W) {W.printSymbolOffset("SymbolOffset", "SymbolName", 0x10);W.printSymbolOffset("NoSymbolOffset", "SymbolName", 0);};const char *ExpectedOut = R"(SymbolOffset: SymbolName+0x10NoSymbolOffset: SymbolName+0x0)";const char *JSONExpectedOut = R"({"SymbolOffset": {"SymName": "SymbolName","Offset": 16},"NoSymbolOffset": {"SymName": "SymbolName","Offset": 0}})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintString) {auto PrintFunc = [](ScopedPrinter &W) {const StringRef StringRefValue("Value");const std::string StringValue = "Value";const char *CharArrayValue = "Value";W.printString("StringRef", StringRefValue);W.printString("String", StringValue);W.printString("CharArray", CharArrayValue);ListScope L(W, "StringList");W.printString(StringRefValue);};const char *ExpectedOut = R"(StringRef: ValueString: ValueCharArray: ValueStringList [Value])";const char *JSONExpectedOut = R"({"StringRef": "Value","String": "Value","CharArray": "Value","StringList": ["Value"]})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintBinary) {auto PrintFunc = [](ScopedPrinter &W) {std::vector<uint8_t> IntArray = {70, 111, 111, 66, 97, 114};std::vector<char> CharArray = {'F', 'o', 'o', 'B', 'a', 'r'};std::vector<uint8_t> InvalidChars = {255, 255};W.printBinary("Binary1", "FooBar", IntArray);W.printBinary("Binary2", "FooBar", CharArray);W.printBinary("Binary3", IntArray);W.printBinary("Binary4", CharArray);W.printBinary("Binary5", StringRef("FooBar"));W.printBinary("Binary6", StringRef("Multiple Line FooBar"));W.printBinaryBlock("Binary7", IntArray, 20);W.printBinaryBlock("Binary8", IntArray);W.printBinaryBlock("Binary9", "FooBar");W.printBinaryBlock("Binary10", "Multiple Line FooBar");W.printBinaryBlock("Binary11", InvalidChars);};const char *ExpectedOut = R"(Binary1: FooBar (46 6F 6F 42 61 72)Binary2: FooBar (46 6F 6F 42 61 72)Binary3: (46 6F 6F 42 61 72)Binary4: (46 6F 6F 42 61 72)Binary5: (46 6F 6F 42 61 72)Binary6 (0000: 4D756C74 69706C65 204C696E 6520466F |Multiple Line Fo|0010: 6F426172 |oBar|)Binary7 (0014: 466F6F42 6172 |FooBar|)Binary8 (0000: 466F6F42 6172 |FooBar|)Binary9 (0000: 466F6F42 6172 |FooBar|)Binary10 (0000: 4D756C74 69706C65 204C696E 6520466F |Multiple Line Fo|0010: 6F426172 |oBar|)Binary11 (0000: FFFF |..|))";const char *JSONExpectedOut = R"({"Binary1": {"Value": "FooBar","Offset": 0,"Bytes": [70,111,111,66,97,114]},"Binary2": {"Value": "FooBar","Offset": 0,"Bytes": [70,111,111,66,97,114]},"Binary3": {"Offset": 0,"Bytes": [70,111,111,66,97,114]},"Binary4": {"Offset": 0,"Bytes": [70,111,111,66,97,114]},"Binary5": {"Offset": 0,"Bytes": [70,111,111,66,97,114]},"Binary6": {"Offset": 0,"Bytes": [77,117,108,116,105,112,108,101,32,76,105,110,101,32,70,111,111,66,97,114]},"Binary7": {"Offset": 20,"Bytes": [70,111,111,66,97,114]},"Binary8": {"Offset": 0,"Bytes": [70,111,111,66,97,114]},"Binary9": {"Offset": 0,"Bytes": [70,111,111,66,97,114]},"Binary10": {"Offset": 0,"Bytes": [77,117,108,116,105,112,108,101,32,76,105,110,101,32,70,111,111,66,97,114]},"Binary11": {"Offset": 0,"Bytes": [255,255]}})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintObject) {auto PrintFunc = [](ScopedPrinter &W) { W.printObject("Object", "Value"); };const char *ExpectedOut = R"(Object: Value)";const char *JSONExpectedOut = R"({"Object": "Value"})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, StartLine) {auto PrintFunc = [](ScopedPrinter &W) {W.startLine() << "|";W.indent(2);W.startLine() << "|";W.unindent();W.startLine() << "|";};const char *ExpectedOut = "| | |";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, GetOStream) {auto PrintFunc = [](ScopedPrinter &W) { W.getOStream() << "Test"; };const char *ExpectedOut = "Test";verifyScopedPrinter(ExpectedOut, PrintFunc);}TEST_F(ScopedPrinterTest, PrintScope) {auto PrintFunc = [](ScopedPrinter &W) {{DictScope O(W, "Object");{ DictScope OO(W, "ObjectInObject"); }{ ListScope LO(W, "ListInObject"); }}{ListScope L(W, "List");{ DictScope OL(W, "ObjectInList"); }{ ListScope LL(W, "ListInList"); }}};const char *ExpectedOut = R"(Object {ObjectInObject {}ListInObject []}List [ObjectInList {}ListInList []])";const char *JSONExpectedOut = R"({"Object": {"ObjectInObject": {},"ListInObject": []},"List": [{"ObjectInList": {}},{"ListInList": []}]})";verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);}
//===- llvm/unittest/Support/ScaledNumberTest.cpp - ScaledPair tests -----==////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ScaledNumber.h"#include "llvm/Support/DataTypes.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::ScaledNumbers;namespace {template <class UIntT> struct ScaledPair {UIntT D;int S;ScaledPair(const std::pair<UIntT, int16_t> &F) : D(F.first), S(F.second) {}ScaledPair(UIntT D, int S) : D(D), S(S) {}bool operator==(const ScaledPair<UIntT> &X) const {return D == X.D && S == X.S;}};template <class UIntT>bool operator==(const std::pair<UIntT, int16_t> &L,const ScaledPair<UIntT> &R) {return ScaledPair<UIntT>(L) == R;}template <class UIntT>void PrintTo(const ScaledPair<UIntT> &F, ::std::ostream *os) {*os << F.D << "*2^" << F.S;}typedef ScaledPair<uint32_t> SP32;typedef ScaledPair<uint64_t> SP64;TEST(ScaledNumberHelpersTest, getRounded) {EXPECT_EQ(getRounded32(0, 0, false), SP32(0, 0));EXPECT_EQ(getRounded32(0, 0, true), SP32(1, 0));EXPECT_EQ(getRounded32(20, 21, true), SP32(21, 21));EXPECT_EQ(getRounded32(UINT32_MAX, 0, false), SP32(UINT32_MAX, 0));EXPECT_EQ(getRounded32(UINT32_MAX, 0, true), SP32(1 << 31, 1));EXPECT_EQ(getRounded64(0, 0, false), SP64(0, 0));EXPECT_EQ(getRounded64(0, 0, true), SP64(1, 0));EXPECT_EQ(getRounded64(20, 21, true), SP64(21, 21));EXPECT_EQ(getRounded64(UINT32_MAX, 0, false), SP64(UINT32_MAX, 0));EXPECT_EQ(getRounded64(UINT32_MAX, 0, true), SP64(UINT64_C(1) << 32, 0));EXPECT_EQ(getRounded64(UINT64_MAX, 0, false), SP64(UINT64_MAX, 0));EXPECT_EQ(getRounded64(UINT64_MAX, 0, true), SP64(UINT64_C(1) << 63, 1));}TEST(ScaledNumberHelpersTest, getAdjusted) {const uint64_t Max32In64 = UINT32_MAX;EXPECT_EQ(getAdjusted32(0), SP32(0, 0));EXPECT_EQ(getAdjusted32(0, 5), SP32(0, 5));EXPECT_EQ(getAdjusted32(UINT32_MAX), SP32(UINT32_MAX, 0));EXPECT_EQ(getAdjusted32(Max32In64 << 1), SP32(UINT32_MAX, 1));EXPECT_EQ(getAdjusted32(Max32In64 << 1, 1), SP32(UINT32_MAX, 2));EXPECT_EQ(getAdjusted32(Max32In64 << 31), SP32(UINT32_MAX, 31));EXPECT_EQ(getAdjusted32(Max32In64 << 32), SP32(UINT32_MAX, 32));EXPECT_EQ(getAdjusted32(Max32In64 + 1), SP32(1u << 31, 1));EXPECT_EQ(getAdjusted32(UINT64_MAX), SP32(1u << 31, 33));EXPECT_EQ(getAdjusted64(0), SP64(0, 0));EXPECT_EQ(getAdjusted64(0, 5), SP64(0, 5));EXPECT_EQ(getAdjusted64(UINT32_MAX), SP64(UINT32_MAX, 0));EXPECT_EQ(getAdjusted64(Max32In64 << 1), SP64(Max32In64 << 1, 0));EXPECT_EQ(getAdjusted64(Max32In64 << 1, 1), SP64(Max32In64 << 1, 1));EXPECT_EQ(getAdjusted64(Max32In64 << 31), SP64(Max32In64 << 31, 0));EXPECT_EQ(getAdjusted64(Max32In64 << 32), SP64(Max32In64 << 32, 0));EXPECT_EQ(getAdjusted64(Max32In64 + 1), SP64(Max32In64 + 1, 0));EXPECT_EQ(getAdjusted64(UINT64_MAX), SP64(UINT64_MAX, 0));}TEST(ScaledNumberHelpersTest, getProduct) {// Zero.EXPECT_EQ(SP32(0, 0), getProduct32(0, 0));EXPECT_EQ(SP32(0, 0), getProduct32(0, 1));EXPECT_EQ(SP32(0, 0), getProduct32(0, 33));// Basic.EXPECT_EQ(SP32(6, 0), getProduct32(2, 3));EXPECT_EQ(SP32(UINT16_MAX / 3 * UINT16_MAX / 5 * 2, 0),getProduct32(UINT16_MAX / 3, UINT16_MAX / 5 * 2));// Overflow, no loss of precision.// ==> 0xf00010 * 0x1001// ==> 0xf00f00000 + 0x10010// ==> 0xf00f10010// ==> 0xf00f1001 * 2^4EXPECT_EQ(SP32(0xf00f1001, 4), getProduct32(0xf00010, 0x1001));// Overflow, loss of precision, rounds down.// ==> 0xf000070 * 0x1001// ==> 0xf00f000000 + 0x70070// ==> 0xf00f070070// ==> 0xf00f0700 * 2^8EXPECT_EQ(SP32(0xf00f0700, 8), getProduct32(0xf000070, 0x1001));// Overflow, loss of precision, rounds up.// ==> 0xf000080 * 0x1001// ==> 0xf00f000000 + 0x80080// ==> 0xf00f080080// ==> 0xf00f0801 * 2^8EXPECT_EQ(SP32(0xf00f0801, 8), getProduct32(0xf000080, 0x1001));// Reverse operand order.EXPECT_EQ(SP32(0, 0), getProduct32(1, 0));EXPECT_EQ(SP32(0, 0), getProduct32(33, 0));EXPECT_EQ(SP32(6, 0), getProduct32(3, 2));EXPECT_EQ(SP32(UINT16_MAX / 3 * UINT16_MAX / 5 * 2, 0),getProduct32(UINT16_MAX / 5 * 2, UINT16_MAX / 3));EXPECT_EQ(SP32(0xf00f1001, 4), getProduct32(0x1001, 0xf00010));EXPECT_EQ(SP32(0xf00f0700, 8), getProduct32(0x1001, 0xf000070));EXPECT_EQ(SP32(0xf00f0801, 8), getProduct32(0x1001, 0xf000080));// Round to overflow.EXPECT_EQ(SP64(UINT64_C(1) << 63, 64),getProduct64(UINT64_C(10376293541461622786),UINT64_C(16397105843297379211)));// Big number with rounding.EXPECT_EQ(SP64(UINT64_C(9223372036854775810), 64),getProduct64(UINT64_C(18446744073709551556),UINT64_C(9223372036854775840)));}TEST(ScaledNumberHelpersTest, getQuotient) {// Zero.EXPECT_EQ(SP32(0, 0), getQuotient32(0, 0));EXPECT_EQ(SP32(0, 0), getQuotient32(0, 1));EXPECT_EQ(SP32(0, 0), getQuotient32(0, 73));EXPECT_EQ(SP32(UINT32_MAX, MaxScale), getQuotient32(1, 0));EXPECT_EQ(SP32(UINT32_MAX, MaxScale), getQuotient32(6, 0));// Powers of two.EXPECT_EQ(SP32(1u << 31, -31), getQuotient32(1, 1));EXPECT_EQ(SP32(1u << 31, -30), getQuotient32(2, 1));EXPECT_EQ(SP32(1u << 31, -33), getQuotient32(4, 16));EXPECT_EQ(SP32(7u << 29, -29), getQuotient32(7, 1));EXPECT_EQ(SP32(7u << 29, -30), getQuotient32(7, 2));EXPECT_EQ(SP32(7u << 29, -33), getQuotient32(7, 16));// Divide evenly.EXPECT_EQ(SP32(3u << 30, -30), getQuotient32(9, 3));EXPECT_EQ(SP32(9u << 28, -28), getQuotient32(63, 7));// Divide unevenly.EXPECT_EQ(SP32(0xaaaaaaab, -33), getQuotient32(1, 3));EXPECT_EQ(SP32(0xd5555555, -31), getQuotient32(5, 3));// 64-bit division is hard to test, since divide64 doesn't canonicalize its// output. However, this is the algorithm the implementation uses://// - Shift divisor right.// - If we have 1 (power of 2), return early -- not canonicalized.// - Shift dividend left.// - 64-bit integer divide.// - If there's a remainder, continue with long division.//// TODO: require less knowledge about the implementation in the test.// Zero.EXPECT_EQ(SP64(0, 0), getQuotient64(0, 0));EXPECT_EQ(SP64(0, 0), getQuotient64(0, 1));EXPECT_EQ(SP64(0, 0), getQuotient64(0, 73));EXPECT_EQ(SP64(UINT64_MAX, MaxScale), getQuotient64(1, 0));EXPECT_EQ(SP64(UINT64_MAX, MaxScale), getQuotient64(6, 0));// Powers of two.EXPECT_EQ(SP64(1, 0), getQuotient64(1, 1));EXPECT_EQ(SP64(2, 0), getQuotient64(2, 1));EXPECT_EQ(SP64(4, -4), getQuotient64(4, 16));EXPECT_EQ(SP64(7, 0), getQuotient64(7, 1));EXPECT_EQ(SP64(7, -1), getQuotient64(7, 2));EXPECT_EQ(SP64(7, -4), getQuotient64(7, 16));// Divide evenly.EXPECT_EQ(SP64(UINT64_C(3) << 60, -60), getQuotient64(9, 3));EXPECT_EQ(SP64(UINT64_C(9) << 58, -58), getQuotient64(63, 7));// Divide unevenly.EXPECT_EQ(SP64(0xaaaaaaaaaaaaaaab, -65), getQuotient64(1, 3));EXPECT_EQ(SP64(0xd555555555555555, -63), getQuotient64(5, 3));}TEST(ScaledNumberHelpersTest, getLg) {EXPECT_EQ(0, getLg(UINT32_C(1), 0));EXPECT_EQ(1, getLg(UINT32_C(1), 1));EXPECT_EQ(1, getLg(UINT32_C(2), 0));EXPECT_EQ(3, getLg(UINT32_C(1), 3));EXPECT_EQ(3, getLg(UINT32_C(7), 0));EXPECT_EQ(3, getLg(UINT32_C(8), 0));EXPECT_EQ(3, getLg(UINT32_C(9), 0));EXPECT_EQ(3, getLg(UINT32_C(64), -3));EXPECT_EQ(31, getLg((UINT32_MAX >> 1) + 2, 0));EXPECT_EQ(32, getLg(UINT32_MAX, 0));EXPECT_EQ(-1, getLg(UINT32_C(1), -1));EXPECT_EQ(-1, getLg(UINT32_C(2), -2));EXPECT_EQ(INT32_MIN, getLg(UINT32_C(0), -1));EXPECT_EQ(INT32_MIN, getLg(UINT32_C(0), 0));EXPECT_EQ(INT32_MIN, getLg(UINT32_C(0), 1));EXPECT_EQ(0, getLg(UINT64_C(1), 0));EXPECT_EQ(1, getLg(UINT64_C(1), 1));EXPECT_EQ(1, getLg(UINT64_C(2), 0));EXPECT_EQ(3, getLg(UINT64_C(1), 3));EXPECT_EQ(3, getLg(UINT64_C(7), 0));EXPECT_EQ(3, getLg(UINT64_C(8), 0));EXPECT_EQ(3, getLg(UINT64_C(9), 0));EXPECT_EQ(3, getLg(UINT64_C(64), -3));EXPECT_EQ(63, getLg((UINT64_MAX >> 1) + 2, 0));EXPECT_EQ(64, getLg(UINT64_MAX, 0));EXPECT_EQ(-1, getLg(UINT64_C(1), -1));EXPECT_EQ(-1, getLg(UINT64_C(2), -2));EXPECT_EQ(INT32_MIN, getLg(UINT64_C(0), -1));EXPECT_EQ(INT32_MIN, getLg(UINT64_C(0), 0));EXPECT_EQ(INT32_MIN, getLg(UINT64_C(0), 1));}TEST(ScaledNumberHelpersTest, getLgFloor) {EXPECT_EQ(0, getLgFloor(UINT32_C(1), 0));EXPECT_EQ(1, getLgFloor(UINT32_C(1), 1));EXPECT_EQ(1, getLgFloor(UINT32_C(2), 0));EXPECT_EQ(2, getLgFloor(UINT32_C(7), 0));EXPECT_EQ(3, getLgFloor(UINT32_C(1), 3));EXPECT_EQ(3, getLgFloor(UINT32_C(8), 0));EXPECT_EQ(3, getLgFloor(UINT32_C(9), 0));EXPECT_EQ(3, getLgFloor(UINT32_C(64), -3));EXPECT_EQ(31, getLgFloor((UINT32_MAX >> 1) + 2, 0));EXPECT_EQ(31, getLgFloor(UINT32_MAX, 0));EXPECT_EQ(INT32_MIN, getLgFloor(UINT32_C(0), -1));EXPECT_EQ(INT32_MIN, getLgFloor(UINT32_C(0), 0));EXPECT_EQ(INT32_MIN, getLgFloor(UINT32_C(0), 1));EXPECT_EQ(0, getLgFloor(UINT64_C(1), 0));EXPECT_EQ(1, getLgFloor(UINT64_C(1), 1));EXPECT_EQ(1, getLgFloor(UINT64_C(2), 0));EXPECT_EQ(2, getLgFloor(UINT64_C(7), 0));EXPECT_EQ(3, getLgFloor(UINT64_C(1), 3));EXPECT_EQ(3, getLgFloor(UINT64_C(8), 0));EXPECT_EQ(3, getLgFloor(UINT64_C(9), 0));EXPECT_EQ(3, getLgFloor(UINT64_C(64), -3));EXPECT_EQ(63, getLgFloor((UINT64_MAX >> 1) + 2, 0));EXPECT_EQ(63, getLgFloor(UINT64_MAX, 0));EXPECT_EQ(INT32_MIN, getLgFloor(UINT64_C(0), -1));EXPECT_EQ(INT32_MIN, getLgFloor(UINT64_C(0), 0));EXPECT_EQ(INT32_MIN, getLgFloor(UINT64_C(0), 1));}TEST(ScaledNumberHelpersTest, getLgCeiling) {EXPECT_EQ(0, getLgCeiling(UINT32_C(1), 0));EXPECT_EQ(1, getLgCeiling(UINT32_C(1), 1));EXPECT_EQ(1, getLgCeiling(UINT32_C(2), 0));EXPECT_EQ(3, getLgCeiling(UINT32_C(1), 3));EXPECT_EQ(3, getLgCeiling(UINT32_C(7), 0));EXPECT_EQ(3, getLgCeiling(UINT32_C(8), 0));EXPECT_EQ(3, getLgCeiling(UINT32_C(64), -3));EXPECT_EQ(4, getLgCeiling(UINT32_C(9), 0));EXPECT_EQ(32, getLgCeiling(UINT32_MAX, 0));EXPECT_EQ(32, getLgCeiling((UINT32_MAX >> 1) + 2, 0));EXPECT_EQ(INT32_MIN, getLgCeiling(UINT32_C(0), -1));EXPECT_EQ(INT32_MIN, getLgCeiling(UINT32_C(0), 0));EXPECT_EQ(INT32_MIN, getLgCeiling(UINT32_C(0), 1));EXPECT_EQ(0, getLgCeiling(UINT64_C(1), 0));EXPECT_EQ(1, getLgCeiling(UINT64_C(1), 1));EXPECT_EQ(1, getLgCeiling(UINT64_C(2), 0));EXPECT_EQ(3, getLgCeiling(UINT64_C(1), 3));EXPECT_EQ(3, getLgCeiling(UINT64_C(7), 0));EXPECT_EQ(3, getLgCeiling(UINT64_C(8), 0));EXPECT_EQ(3, getLgCeiling(UINT64_C(64), -3));EXPECT_EQ(4, getLgCeiling(UINT64_C(9), 0));EXPECT_EQ(64, getLgCeiling((UINT64_MAX >> 1) + 2, 0));EXPECT_EQ(64, getLgCeiling(UINT64_MAX, 0));EXPECT_EQ(INT32_MIN, getLgCeiling(UINT64_C(0), -1));EXPECT_EQ(INT32_MIN, getLgCeiling(UINT64_C(0), 0));EXPECT_EQ(INT32_MIN, getLgCeiling(UINT64_C(0), 1));}TEST(ScaledNumberHelpersTest, compare) {EXPECT_EQ(0, compare(UINT32_C(0), 0, UINT32_C(0), 1));EXPECT_EQ(0, compare(UINT32_C(0), 0, UINT32_C(0), -10));EXPECT_EQ(0, compare(UINT32_C(0), 0, UINT32_C(0), 20));EXPECT_EQ(0, compare(UINT32_C(8), 0, UINT32_C(64), -3));EXPECT_EQ(0, compare(UINT32_C(8), 0, UINT32_C(32), -2));EXPECT_EQ(0, compare(UINT32_C(8), 0, UINT32_C(16), -1));EXPECT_EQ(0, compare(UINT32_C(8), 0, UINT32_C(8), 0));EXPECT_EQ(0, compare(UINT32_C(8), 0, UINT32_C(4), 1));EXPECT_EQ(0, compare(UINT32_C(8), 0, UINT32_C(2), 2));EXPECT_EQ(0, compare(UINT32_C(8), 0, UINT32_C(1), 3));EXPECT_EQ(-1, compare(UINT32_C(0), 0, UINT32_C(1), 3));EXPECT_EQ(-1, compare(UINT32_C(7), 0, UINT32_C(1), 3));EXPECT_EQ(-1, compare(UINT32_C(7), 0, UINT32_C(64), -3));EXPECT_EQ(1, compare(UINT32_C(9), 0, UINT32_C(1), 3));EXPECT_EQ(1, compare(UINT32_C(9), 0, UINT32_C(64), -3));EXPECT_EQ(1, compare(UINT32_C(9), 0, UINT32_C(0), 0));EXPECT_EQ(0, compare(UINT64_C(0), 0, UINT64_C(0), 1));EXPECT_EQ(0, compare(UINT64_C(0), 0, UINT64_C(0), -10));EXPECT_EQ(0, compare(UINT64_C(0), 0, UINT64_C(0), 20));EXPECT_EQ(0, compare(UINT64_C(8), 0, UINT64_C(64), -3));EXPECT_EQ(0, compare(UINT64_C(8), 0, UINT64_C(32), -2));EXPECT_EQ(0, compare(UINT64_C(8), 0, UINT64_C(16), -1));EXPECT_EQ(0, compare(UINT64_C(8), 0, UINT64_C(8), 0));EXPECT_EQ(0, compare(UINT64_C(8), 0, UINT64_C(4), 1));EXPECT_EQ(0, compare(UINT64_C(8), 0, UINT64_C(2), 2));EXPECT_EQ(0, compare(UINT64_C(8), 0, UINT64_C(1), 3));EXPECT_EQ(-1, compare(UINT64_C(0), 0, UINT64_C(1), 3));EXPECT_EQ(-1, compare(UINT64_C(7), 0, UINT64_C(1), 3));EXPECT_EQ(-1, compare(UINT64_C(7), 0, UINT64_C(64), -3));EXPECT_EQ(1, compare(UINT64_C(9), 0, UINT64_C(1), 3));EXPECT_EQ(1, compare(UINT64_C(9), 0, UINT64_C(64), -3));EXPECT_EQ(1, compare(UINT64_C(9), 0, UINT64_C(0), 0));EXPECT_EQ(-1, compare(UINT64_MAX, 0, UINT64_C(1), 64));}TEST(ScaledNumberHelpersTest, matchScales) {#define MATCH_SCALES(T, LDIn, LSIn, RDIn, RSIn, LDOut, RDOut, SOut) \do { \T LDx = LDIn; \T RDx = RDIn; \T LDy = LDOut; \T RDy = RDOut; \int16_t LSx = LSIn; \int16_t RSx = RSIn; \int16_t Sy = SOut; \\EXPECT_EQ(SOut, matchScales(LDx, LSx, RDx, RSx)); \EXPECT_EQ(LDy, LDx); \EXPECT_EQ(RDy, RDx); \if (LDy) { \EXPECT_EQ(Sy, LSx); \} \if (RDy) { \EXPECT_EQ(Sy, RSx); \} \} while (false)MATCH_SCALES(uint32_t, 0, 0, 0, 0, 0, 0, 0);MATCH_SCALES(uint32_t, 0, 50, 7, 1, 0, 7, 1);MATCH_SCALES(uint32_t, UINT32_C(1) << 31, 1, 9, 0, UINT32_C(1) << 31, 4, 1);MATCH_SCALES(uint32_t, UINT32_C(1) << 31, 2, 9, 0, UINT32_C(1) << 31, 2, 2);MATCH_SCALES(uint32_t, UINT32_C(1) << 31, 3, 9, 0, UINT32_C(1) << 31, 1, 3);MATCH_SCALES(uint32_t, UINT32_C(1) << 31, 4, 9, 0, UINT32_C(1) << 31, 0, 4);MATCH_SCALES(uint32_t, UINT32_C(1) << 30, 4, 9, 0, UINT32_C(1) << 31, 1, 3);MATCH_SCALES(uint32_t, UINT32_C(1) << 29, 4, 9, 0, UINT32_C(1) << 31, 2, 2);MATCH_SCALES(uint32_t, UINT32_C(1) << 28, 4, 9, 0, UINT32_C(1) << 31, 4, 1);MATCH_SCALES(uint32_t, UINT32_C(1) << 27, 4, 9, 0, UINT32_C(1) << 31, 9, 0);MATCH_SCALES(uint32_t, 7, 1, 0, 50, 7, 0, 1);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 31, 1, 4, UINT32_C(1) << 31, 1);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 31, 2, 2, UINT32_C(1) << 31, 2);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 31, 3, 1, UINT32_C(1) << 31, 3);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 31, 4, 0, UINT32_C(1) << 31, 4);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 30, 4, 1, UINT32_C(1) << 31, 3);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 29, 4, 2, UINT32_C(1) << 31, 2);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 28, 4, 4, UINT32_C(1) << 31, 1);MATCH_SCALES(uint32_t, 9, 0, UINT32_C(1) << 27, 4, 9, UINT32_C(1) << 31, 0);MATCH_SCALES(uint64_t, 0, 0, 0, 0, 0, 0, 0);MATCH_SCALES(uint64_t, 0, 100, 7, 1, 0, 7, 1);MATCH_SCALES(uint64_t, UINT64_C(1) << 63, 1, 9, 0, UINT64_C(1) << 63, 4, 1);MATCH_SCALES(uint64_t, UINT64_C(1) << 63, 2, 9, 0, UINT64_C(1) << 63, 2, 2);MATCH_SCALES(uint64_t, UINT64_C(1) << 63, 3, 9, 0, UINT64_C(1) << 63, 1, 3);MATCH_SCALES(uint64_t, UINT64_C(1) << 63, 4, 9, 0, UINT64_C(1) << 63, 0, 4);MATCH_SCALES(uint64_t, UINT64_C(1) << 62, 4, 9, 0, UINT64_C(1) << 63, 1, 3);MATCH_SCALES(uint64_t, UINT64_C(1) << 61, 4, 9, 0, UINT64_C(1) << 63, 2, 2);MATCH_SCALES(uint64_t, UINT64_C(1) << 60, 4, 9, 0, UINT64_C(1) << 63, 4, 1);MATCH_SCALES(uint64_t, UINT64_C(1) << 59, 4, 9, 0, UINT64_C(1) << 63, 9, 0);MATCH_SCALES(uint64_t, 7, 1, 0, 100, 7, 0, 1);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 63, 1, 4, UINT64_C(1) << 63, 1);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 63, 2, 2, UINT64_C(1) << 63, 2);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 63, 3, 1, UINT64_C(1) << 63, 3);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 63, 4, 0, UINT64_C(1) << 63, 4);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 62, 4, 1, UINT64_C(1) << 63, 3);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 61, 4, 2, UINT64_C(1) << 63, 2);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 60, 4, 4, UINT64_C(1) << 63, 1);MATCH_SCALES(uint64_t, 9, 0, UINT64_C(1) << 59, 4, 9, UINT64_C(1) << 63, 0);}TEST(ScaledNumberHelpersTest, getSum) {// Zero.EXPECT_EQ(SP32(1, 0), getSum32(0, 0, 1, 0));EXPECT_EQ(SP32(8, -3), getSum32(0, 0, 8, -3));EXPECT_EQ(SP32(UINT32_MAX, 0), getSum32(0, 0, UINT32_MAX, 0));// Basic.EXPECT_EQ(SP32(2, 0), getSum32(1, 0, 1, 0));EXPECT_EQ(SP32(3, 0), getSum32(1, 0, 2, 0));EXPECT_EQ(SP32(67, 0), getSum32(7, 0, 60, 0));// Different scales.EXPECT_EQ(SP32(3, 0), getSum32(1, 0, 1, 1));EXPECT_EQ(SP32(4, 0), getSum32(2, 0, 1, 1));// Loss of precision.EXPECT_EQ(SP32(UINT32_C(1) << 31, 1), getSum32(1, 32, 1, 0));EXPECT_EQ(SP32(UINT32_C(1) << 31, -31), getSum32(1, -32, 1, 0));// Not quite loss of precision.EXPECT_EQ(SP32((UINT32_C(1) << 31) + 1, 1), getSum32(1, 32, 1, 1));EXPECT_EQ(SP32((UINT32_C(1) << 31) + 1, -32), getSum32(1, -32, 1, -1));// Overflow.EXPECT_EQ(SP32(UINT32_C(1) << 31, 1), getSum32(1, 0, UINT32_MAX, 0));// Reverse operand order.EXPECT_EQ(SP32(1, 0), getSum32(1, 0, 0, 0));EXPECT_EQ(SP32(8, -3), getSum32(8, -3, 0, 0));EXPECT_EQ(SP32(UINT32_MAX, 0), getSum32(UINT32_MAX, 0, 0, 0));EXPECT_EQ(SP32(3, 0), getSum32(2, 0, 1, 0));EXPECT_EQ(SP32(67, 0), getSum32(60, 0, 7, 0));EXPECT_EQ(SP32(3, 0), getSum32(1, 1, 1, 0));EXPECT_EQ(SP32(4, 0), getSum32(1, 1, 2, 0));EXPECT_EQ(SP32(UINT32_C(1) << 31, 1), getSum32(1, 0, 1, 32));EXPECT_EQ(SP32(UINT32_C(1) << 31, -31), getSum32(1, 0, 1, -32));EXPECT_EQ(SP32((UINT32_C(1) << 31) + 1, 1), getSum32(1, 1, 1, 32));EXPECT_EQ(SP32((UINT32_C(1) << 31) + 1, -32), getSum32(1, -1, 1, -32));EXPECT_EQ(SP32(UINT32_C(1) << 31, 1), getSum32(UINT32_MAX, 0, 1, 0));// Zero.EXPECT_EQ(SP64(1, 0), getSum64(0, 0, 1, 0));EXPECT_EQ(SP64(8, -3), getSum64(0, 0, 8, -3));EXPECT_EQ(SP64(UINT64_MAX, 0), getSum64(0, 0, UINT64_MAX, 0));// Basic.EXPECT_EQ(SP64(2, 0), getSum64(1, 0, 1, 0));EXPECT_EQ(SP64(3, 0), getSum64(1, 0, 2, 0));EXPECT_EQ(SP64(67, 0), getSum64(7, 0, 60, 0));// Different scales.EXPECT_EQ(SP64(3, 0), getSum64(1, 0, 1, 1));EXPECT_EQ(SP64(4, 0), getSum64(2, 0, 1, 1));// Loss of precision.EXPECT_EQ(SP64(UINT64_C(1) << 63, 1), getSum64(1, 64, 1, 0));EXPECT_EQ(SP64(UINT64_C(1) << 63, -63), getSum64(1, -64, 1, 0));// Not quite loss of precision.EXPECT_EQ(SP64((UINT64_C(1) << 63) + 1, 1), getSum64(1, 64, 1, 1));EXPECT_EQ(SP64((UINT64_C(1) << 63) + 1, -64), getSum64(1, -64, 1, -1));// Overflow.EXPECT_EQ(SP64(UINT64_C(1) << 63, 1), getSum64(1, 0, UINT64_MAX, 0));// Reverse operand order.EXPECT_EQ(SP64(1, 0), getSum64(1, 0, 0, 0));EXPECT_EQ(SP64(8, -3), getSum64(8, -3, 0, 0));EXPECT_EQ(SP64(UINT64_MAX, 0), getSum64(UINT64_MAX, 0, 0, 0));EXPECT_EQ(SP64(3, 0), getSum64(2, 0, 1, 0));EXPECT_EQ(SP64(67, 0), getSum64(60, 0, 7, 0));EXPECT_EQ(SP64(3, 0), getSum64(1, 1, 1, 0));EXPECT_EQ(SP64(4, 0), getSum64(1, 1, 2, 0));EXPECT_EQ(SP64(UINT64_C(1) << 63, 1), getSum64(1, 0, 1, 64));EXPECT_EQ(SP64(UINT64_C(1) << 63, -63), getSum64(1, 0, 1, -64));EXPECT_EQ(SP64((UINT64_C(1) << 63) + 1, 1), getSum64(1, 1, 1, 64));EXPECT_EQ(SP64((UINT64_C(1) << 63) + 1, -64), getSum64(1, -1, 1, -64));EXPECT_EQ(SP64(UINT64_C(1) << 63, 1), getSum64(UINT64_MAX, 0, 1, 0));}TEST(ScaledNumberHelpersTest, getDifference) {// Basic.EXPECT_EQ(SP32(0, 0), getDifference32(1, 0, 1, 0));EXPECT_EQ(SP32(1, 0), getDifference32(2, 0, 1, 0));EXPECT_EQ(SP32(53, 0), getDifference32(60, 0, 7, 0));// Equals "0", different scales.EXPECT_EQ(SP32(0, 0), getDifference32(2, 0, 1, 1));// Subtract "0".EXPECT_EQ(SP32(1, 0), getDifference32(1, 0, 0, 0));EXPECT_EQ(SP32(8, -3), getDifference32(8, -3, 0, 0));EXPECT_EQ(SP32(UINT32_MAX, 0), getDifference32(UINT32_MAX, 0, 0, 0));// Loss of precision.EXPECT_EQ(SP32((UINT32_C(1) << 31) + 1, 1),getDifference32((UINT32_C(1) << 31) + 1, 1, 1, 0));EXPECT_EQ(SP32((UINT32_C(1) << 31) + 1, -31),getDifference32((UINT32_C(1) << 31) + 1, -31, 1, -32));// Not quite loss of precision.EXPECT_EQ(SP32(UINT32_MAX, 0), getDifference32(1, 32, 1, 0));EXPECT_EQ(SP32(UINT32_MAX, -32), getDifference32(1, 0, 1, -32));// Saturate to "0".EXPECT_EQ(SP32(0, 0), getDifference32(0, 0, 1, 0));EXPECT_EQ(SP32(0, 0), getDifference32(0, 0, 8, -3));EXPECT_EQ(SP32(0, 0), getDifference32(0, 0, UINT32_MAX, 0));EXPECT_EQ(SP32(0, 0), getDifference32(7, 0, 60, 0));EXPECT_EQ(SP32(0, 0), getDifference32(1, 0, 1, 1));EXPECT_EQ(SP32(0, 0), getDifference32(1, -32, 1, 0));EXPECT_EQ(SP32(0, 0), getDifference32(1, -32, 1, -1));// Regression tests for cases that failed during bringup.EXPECT_EQ(SP32(UINT32_C(1) << 26, -31),getDifference32(1, 0, UINT32_C(31) << 27, -32));// Basic.EXPECT_EQ(SP64(0, 0), getDifference64(1, 0, 1, 0));EXPECT_EQ(SP64(1, 0), getDifference64(2, 0, 1, 0));EXPECT_EQ(SP64(53, 0), getDifference64(60, 0, 7, 0));// Equals "0", different scales.EXPECT_EQ(SP64(0, 0), getDifference64(2, 0, 1, 1));// Subtract "0".EXPECT_EQ(SP64(1, 0), getDifference64(1, 0, 0, 0));EXPECT_EQ(SP64(8, -3), getDifference64(8, -3, 0, 0));EXPECT_EQ(SP64(UINT64_MAX, 0), getDifference64(UINT64_MAX, 0, 0, 0));// Loss of precision.EXPECT_EQ(SP64((UINT64_C(1) << 63) + 1, 1),getDifference64((UINT64_C(1) << 63) + 1, 1, 1, 0));EXPECT_EQ(SP64((UINT64_C(1) << 63) + 1, -63),getDifference64((UINT64_C(1) << 63) + 1, -63, 1, -64));// Not quite loss of precision.EXPECT_EQ(SP64(UINT64_MAX, 0), getDifference64(1, 64, 1, 0));EXPECT_EQ(SP64(UINT64_MAX, -64), getDifference64(1, 0, 1, -64));// Saturate to "0".EXPECT_EQ(SP64(0, 0), getDifference64(0, 0, 1, 0));EXPECT_EQ(SP64(0, 0), getDifference64(0, 0, 8, -3));EXPECT_EQ(SP64(0, 0), getDifference64(0, 0, UINT64_MAX, 0));EXPECT_EQ(SP64(0, 0), getDifference64(7, 0, 60, 0));EXPECT_EQ(SP64(0, 0), getDifference64(1, 0, 1, 1));EXPECT_EQ(SP64(0, 0), getDifference64(1, -64, 1, 0));EXPECT_EQ(SP64(0, 0), getDifference64(1, -64, 1, -1));}TEST(ScaledNumberHelpersTest, arithmeticOperators) {EXPECT_EQ(ScaledNumber<uint32_t>(10, 0),ScaledNumber<uint32_t>(1, 3) + ScaledNumber<uint32_t>(1, 1));EXPECT_EQ(ScaledNumber<uint32_t>(6, 0),ScaledNumber<uint32_t>(1, 3) - ScaledNumber<uint32_t>(1, 1));EXPECT_EQ(ScaledNumber<uint32_t>(2, 3),ScaledNumber<uint32_t>(1, 3) * ScaledNumber<uint32_t>(1, 1));EXPECT_EQ(ScaledNumber<uint32_t>(1, 2),ScaledNumber<uint32_t>(1, 3) / ScaledNumber<uint32_t>(1, 1));EXPECT_EQ(ScaledNumber<uint32_t>(1, 2), ScaledNumber<uint32_t>(1, 3) >> 1);EXPECT_EQ(ScaledNumber<uint32_t>(1, 4), ScaledNumber<uint32_t>(1, 3) << 1);EXPECT_EQ(ScaledNumber<uint64_t>(10, 0),ScaledNumber<uint64_t>(1, 3) + ScaledNumber<uint64_t>(1, 1));EXPECT_EQ(ScaledNumber<uint64_t>(6, 0),ScaledNumber<uint64_t>(1, 3) - ScaledNumber<uint64_t>(1, 1));EXPECT_EQ(ScaledNumber<uint64_t>(2, 3),ScaledNumber<uint64_t>(1, 3) * ScaledNumber<uint64_t>(1, 1));EXPECT_EQ(ScaledNumber<uint64_t>(1, 2),ScaledNumber<uint64_t>(1, 3) / ScaledNumber<uint64_t>(1, 1));EXPECT_EQ(ScaledNumber<uint64_t>(1, 2), ScaledNumber<uint64_t>(1, 3) >> 1);EXPECT_EQ(ScaledNumber<uint64_t>(1, 4), ScaledNumber<uint64_t>(1, 3) << 1);}TEST(ScaledNumberHelpersTest, toIntBug) {ScaledNumber<uint32_t> n(1, 0);EXPECT_EQ(1u, (n * n).toInt<uint32_t>());}static_assert(std::is_trivially_copyable<ScaledNumber<uint32_t>>::value,"trivially copyable");} // end namespace
//===- llvm/unittest/Support/SHA256Test.cpp - SHA256 tests//----------------------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for the SHA256 functions.////===----------------------------------------------------------------------===//#include "llvm/Support/SHA256.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/SmallString.h"#include "gtest/gtest.h"using namespace llvm;namespace {static std::string toHex(ArrayRef<uint8_t> Input) {static const char *const LUT = "0123456789abcdef";size_t Length = Input.size();std::string Output;Output.reserve(2 * Length);for (size_t i = 0; i < Length; ++i) {const unsigned char c = Input[i];Output.push_back(LUT[c >> 4]);Output.push_back(LUT[c & 15]);}return Output;}/// Tests an arbitrary set of bytes passed as \p Input.void TestSHA256Sum(ArrayRef<uint8_t> Input, StringRef Final) {SHA256 Hash;Hash.update(Input);auto hash = Hash.final();auto hashStr = toHex(hash);EXPECT_EQ(hashStr, Final);}using KV = std::pair<const char *, const char *>;TEST(SHA256Test, SHA256) {std::array<KV, 5> testvectors{KV{"","e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},KV{"a","ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"},KV{"abc","ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"},KV{"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq","248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"},KV{"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklm""nopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu","cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"}};for (auto input_expected : testvectors) {auto str = std::get<0>(input_expected);auto expected = std::get<1>(input_expected);TestSHA256Sum({reinterpret_cast<const uint8_t *>(str), strlen(str)},expected);}std::string rep(1000, 'a');SHA256 Hash;for (int i = 0; i < 1000; ++i) {Hash.update({reinterpret_cast<const uint8_t *>(rep.data()), rep.size()});}auto hash = Hash.final();auto hashStr = toHex(hash);EXPECT_EQ(hashStr,"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");}} // namespace
//===- llvm/unittest/Support/ReverseIterationTest.cpp ---------------------===////// 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////===---------------------------------------------------------------------===////// Reverse Iteration unit tests.////===---------------------------------------------------------------------===//#include "llvm/Support/ReverseIteration.h"#include "llvm/ADT/DenseMap.h"#include "llvm/ADT/DenseMapInfo.h"#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"using namespace llvm;TEST(ReverseIterationTest, DenseMapTest1) {static_assert(detail::IsPointerLike<int *>::value,"int * is pointer-like");static_assert(detail::IsPointerLike<uintptr_t>::value,"uintptr_t is pointer-like");static_assert(!detail::IsPointerLike<int>::value,"int is not pointer-like");static_assert(detail::IsPointerLike<void *>::value,"void * is pointer-like");struct IncompleteType;static_assert(detail::IsPointerLike<IncompleteType *>::value,"incomplete * is pointer-like");// For a DenseMap with non-pointer-like keys, forward iteration equals// reverse iteration.DenseMap<int, int> Map;int Keys[] = { 1, 2, 3, 4 };// Insert keys into the DenseMap.for (auto Key: Keys)Map[Key] = 0;// Note: This is the observed order of keys in the DenseMap.// If there is any change in the behavior of the DenseMap, this order// would need to be adjusted accordingly.int IterKeys[] = { 2, 4, 1, 3 };// Check that the DenseMap is iterated in the expected order.for (auto Tuple : zip(Map, IterKeys))ASSERT_EQ(std::get<0>(Tuple).first, std::get<1>(Tuple));// Check operator++ (post-increment).int i = 0;for (auto iter = Map.begin(), end = Map.end(); iter != end; iter++, ++i)ASSERT_EQ(iter->first, IterKeys[i]);}// Define a pointer-like int.struct PtrLikeInt { int value; };namespace llvm {template<> struct DenseMapInfo<PtrLikeInt *> {static PtrLikeInt *getEmptyKey() {static PtrLikeInt EmptyKey;return &EmptyKey;}static PtrLikeInt *getTombstoneKey() {static PtrLikeInt TombstoneKey;return &TombstoneKey;}static int getHashValue(const PtrLikeInt *P) {return P->value;}static bool isEqual(const PtrLikeInt *LHS, const PtrLikeInt *RHS) {return LHS == RHS;}};} // end namespace llvmTEST(ReverseIterationTest, DenseMapTest2) {static_assert(detail::IsPointerLike<PtrLikeInt *>::value,"PtrLikeInt * is pointer-like");PtrLikeInt a = {4}, b = {8}, c = {12}, d = {16};PtrLikeInt *Keys[] = { &a, &b, &c, &d };// Insert keys into the DenseMap.DenseMap<PtrLikeInt *, int> Map;for (auto *Key : Keys)Map[Key] = Key->value;// Note: If there is any change in the behavior of the DenseMap,// the observed order of keys would need to be adjusted accordingly.if (shouldReverseIterate<PtrLikeInt *>())std::reverse(&Keys[0], &Keys[4]);// Check that the DenseMap is iterated in the expected order.for (auto Tuple : zip(Map, Keys))ASSERT_EQ(std::get<0>(Tuple).second, std::get<1>(Tuple)->value);// Check operator++ (post-increment).int i = 0;for (auto iter = Map.begin(), end = Map.end(); iter != end; iter++, ++i)ASSERT_EQ(iter->second, Keys[i]->value);}
//===- llvm/unittest/Support/ReplaceFileTest.cpp - unit tests -------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Errc.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/Path.h"#include "llvm/Support/Process.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::sys;#define ASSERT_NO_ERROR(x) \do { \if (std::error_code ASSERT_NO_ERROR_ec = x) { \errs() << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \} \} while (false)namespace {std::error_code CreateFileWithContent(const SmallString<128> &FilePath,const StringRef &content) {int FD = 0;if (std::error_code ec = fs::openFileForWrite(FilePath, FD))return ec;const bool ShouldClose = true;raw_fd_ostream OS(FD, ShouldClose);OS << content;return std::error_code();}class ScopedFD {int FD;ScopedFD(const ScopedFD &) = delete;ScopedFD &operator=(const ScopedFD &) = delete;public:explicit ScopedFD(int Descriptor) : FD(Descriptor) {}~ScopedFD() { Process::SafelyCloseFileDescriptor(FD); }};bool FDHasContent(int FD, StringRef Content) {auto Buffer =MemoryBuffer::getOpenFile(sys::fs::convertFDToNativeFile(FD), "", -1);assert(Buffer);return Buffer.get()->getBuffer() == Content;}bool FileHasContent(StringRef File, StringRef Content) {int FD = 0;auto EC = fs::openFileForRead(File, FD);(void)EC;assert(!EC);ScopedFD EventuallyCloseIt(FD);return FDHasContent(FD, Content);}TEST(rename, FileOpenedForReadingCanBeReplaced) {// Create unique temporary directory for this test.SmallString<128> TestDirectory;ASSERT_NO_ERROR(fs::createUniqueDirectory("FileOpenedForReadingCanBeReplaced-test", TestDirectory));// Add a couple of files to the test directory.SmallString<128> SourceFileName(TestDirectory);path::append(SourceFileName, "source");SmallString<128> TargetFileName(TestDirectory);path::append(TargetFileName, "target");ASSERT_NO_ERROR(CreateFileWithContent(SourceFileName, "!!source!!"));ASSERT_NO_ERROR(CreateFileWithContent(TargetFileName, "!!target!!"));{// Open the target file for reading.int ReadFD = 0;ASSERT_NO_ERROR(fs::openFileForRead(TargetFileName, ReadFD));ScopedFD EventuallyCloseIt(ReadFD);// Confirm we can replace the file while it is open.EXPECT_TRUE(!fs::rename(SourceFileName, TargetFileName));// We should still be able to read the old data through the existing// descriptor.EXPECT_TRUE(FDHasContent(ReadFD, "!!target!!"));// The source file should no longer existEXPECT_FALSE(fs::exists(SourceFileName));}// If we obtain a new descriptor for the target file, we should find that it// contains the content that was in the source file.EXPECT_TRUE(FileHasContent(TargetFileName, "!!source!!"));// Rename the target file back to the source file name to confirm that rename// still works if the destination does not already exist.EXPECT_TRUE(!fs::rename(TargetFileName, SourceFileName));EXPECT_FALSE(fs::exists(TargetFileName));ASSERT_TRUE(fs::exists(SourceFileName));// Clean up.ASSERT_NO_ERROR(fs::remove(SourceFileName));ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));}TEST(rename, ExistingTemp) {// Test that existing .tmpN files don't get deleted by the Windows// sys::fs::rename implementation.SmallString<128> TestDirectory;ASSERT_NO_ERROR(fs::createUniqueDirectory("ExistingTemp-test", TestDirectory));SmallString<128> SourceFileName(TestDirectory);path::append(SourceFileName, "source");SmallString<128> TargetFileName(TestDirectory);path::append(TargetFileName, "target");SmallString<128> TargetTmp0FileName(TestDirectory);path::append(TargetTmp0FileName, "target.tmp0");SmallString<128> TargetTmp1FileName(TestDirectory);path::append(TargetTmp1FileName, "target.tmp1");ASSERT_NO_ERROR(CreateFileWithContent(SourceFileName, "!!source!!"));ASSERT_NO_ERROR(CreateFileWithContent(TargetFileName, "!!target!!"));ASSERT_NO_ERROR(CreateFileWithContent(TargetTmp0FileName, "!!target.tmp0!!"));{// Use mapped_file_region to make sure that the destination file is mmap'ed.// This will cause SetInformationByHandle to fail when renaming to the// destination, and we will follow the code path that tries to give target// a temporary name.int TargetFD;std::error_code EC;ASSERT_NO_ERROR(fs::openFileForRead(TargetFileName, TargetFD));ScopedFD X(TargetFD);sys::fs::mapped_file_region MFR(sys::fs::convertFDToNativeFile(TargetFD),sys::fs::mapped_file_region::readonly, 10,0, EC);ASSERT_FALSE(EC);ASSERT_NO_ERROR(fs::rename(SourceFileName, TargetFileName));#ifdef _WIN32// Make sure that target was temporarily renamed to target.tmp1 on Windows.// This is signified by a permission denied error as opposed to no such file// or directory when trying to open it.int Tmp1FD;EXPECT_EQ(errc::permission_denied,fs::openFileForRead(TargetTmp1FileName, Tmp1FD));#endif}EXPECT_TRUE(FileHasContent(TargetTmp0FileName, "!!target.tmp0!!"));ASSERT_NO_ERROR(fs::remove(TargetFileName));ASSERT_NO_ERROR(fs::remove(TargetTmp0FileName));ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));}} // anonymous namespace
//===- llvm/unittest/Support/RegexTest.cpp - Regex tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Regex.h"#include "llvm/ADT/SmallVector.h"#include "gtest/gtest.h"#include <cstring>using namespace llvm;namespace {class RegexTest : public ::testing::Test {};TEST_F(RegexTest, Basics) {Regex r1("^[0-9]+$");EXPECT_TRUE(r1.match("916"));EXPECT_TRUE(r1.match("9"));EXPECT_FALSE(r1.match("9a"));SmallVector<StringRef, 1> Matches;Regex r2("[0-9]+");EXPECT_TRUE(r2.match("aa216b", &Matches));EXPECT_EQ(1u, Matches.size());EXPECT_EQ("216", Matches[0].str());Regex r3("[0-9]+([a-f])?:([0-9]+)");EXPECT_TRUE(r3.match("9a:513b", &Matches));EXPECT_EQ(3u, Matches.size());EXPECT_EQ("9a:513", Matches[0].str());EXPECT_EQ("a", Matches[1].str());EXPECT_EQ("513", Matches[2].str());EXPECT_TRUE(r3.match("9:513b", &Matches));EXPECT_EQ(3u, Matches.size());EXPECT_EQ("9:513", Matches[0].str());EXPECT_EQ("", Matches[1].str());EXPECT_EQ("513", Matches[2].str());Regex r4("a[^b]+b");std::string String="axxb";String[2] = '\0';EXPECT_FALSE(r4.match("abb"));EXPECT_TRUE(r4.match(String, &Matches));EXPECT_EQ(1u, Matches.size());EXPECT_EQ(String, Matches[0].str());std::string NulPattern="X[0-9]+X([a-f])?:([0-9]+)";String="YX99a:513b";NulPattern[7] = '\0';Regex r5(NulPattern);EXPECT_FALSE(r5.match(String));EXPECT_FALSE(r5.match("X9"));String[3]='\0';EXPECT_TRUE(r5.match(String));}TEST_F(RegexTest, Backreferences) {Regex r1("([a-z]+)_\\1");SmallVector<StringRef, 4> Matches;EXPECT_TRUE(r1.match("abc_abc", &Matches));EXPECT_EQ(2u, Matches.size());EXPECT_FALSE(r1.match("abc_ab", &Matches));Regex r2("a([0-9])b\\1c\\1");EXPECT_TRUE(r2.match("a4b4c4", &Matches));EXPECT_EQ(2u, Matches.size());EXPECT_EQ("4", Matches[1].str());EXPECT_FALSE(r2.match("a2b2c3"));Regex r3("a([0-9])([a-z])b\\1\\2");EXPECT_TRUE(r3.match("a6zb6z", &Matches));EXPECT_EQ(3u, Matches.size());EXPECT_EQ("6", Matches[1].str());EXPECT_EQ("z", Matches[2].str());EXPECT_FALSE(r3.match("a6zb6y"));EXPECT_FALSE(r3.match("a6zb7z"));}TEST_F(RegexTest, Substitution) {std::string Error;EXPECT_EQ("aNUMber", Regex("[0-9]+").sub("NUM", "a1234ber"));// Standard EscapesEXPECT_EQ("a\\ber", Regex("[0-9]+").sub("\\\\", "a1234ber", &Error));EXPECT_EQ("", Error);EXPECT_EQ("a\nber", Regex("[0-9]+").sub("\\n", "a1234ber", &Error));EXPECT_EQ("", Error);EXPECT_EQ("a\tber", Regex("[0-9]+").sub("\\t", "a1234ber", &Error));EXPECT_EQ("", Error);EXPECT_EQ("ajber", Regex("[0-9]+").sub("\\j", "a1234ber", &Error));EXPECT_EQ("", Error);EXPECT_EQ("aber", Regex("[0-9]+").sub("\\", "a1234ber", &Error));EXPECT_EQ(Error, "replacement string contained trailing backslash");// BackreferencesEXPECT_EQ("aa1234bber", Regex("a[0-9]+b").sub("a\\0b", "a1234ber", &Error));EXPECT_EQ("", Error);EXPECT_EQ("a1234ber", Regex("a([0-9]+)b").sub("a\\1b", "a1234ber", &Error));EXPECT_EQ("", Error);EXPECT_EQ("aber", Regex("a[0-9]+b").sub("a\\100b", "a1234ber", &Error));EXPECT_EQ(Error, "invalid backreference string '100'");}TEST_F(RegexTest, IsLiteralERE) {EXPECT_TRUE(Regex::isLiteralERE("abc"));EXPECT_FALSE(Regex::isLiteralERE("a(bc)"));EXPECT_FALSE(Regex::isLiteralERE("^abc"));EXPECT_FALSE(Regex::isLiteralERE("abc$"));EXPECT_FALSE(Regex::isLiteralERE("a|bc"));EXPECT_FALSE(Regex::isLiteralERE("abc*"));EXPECT_FALSE(Regex::isLiteralERE("abc+"));EXPECT_FALSE(Regex::isLiteralERE("abc?"));EXPECT_FALSE(Regex::isLiteralERE("abc."));EXPECT_FALSE(Regex::isLiteralERE("a[bc]"));EXPECT_FALSE(Regex::isLiteralERE("abc\\1"));EXPECT_FALSE(Regex::isLiteralERE("abc{1,2}"));}TEST_F(RegexTest, Escape) {EXPECT_EQ("a\\[bc\\]", Regex::escape("a[bc]"));EXPECT_EQ("abc\\{1\\\\,2\\}", Regex::escape("abc{1\\,2}"));}TEST_F(RegexTest, IsValid) {std::string Error;EXPECT_FALSE(Regex("(foo").isValid(Error));EXPECT_EQ("parentheses not balanced", Error);EXPECT_FALSE(Regex("a[b-").isValid(Error));EXPECT_EQ("invalid character range", Error);}TEST_F(RegexTest, MoveConstruct) {Regex r1("^[0-9]+$");Regex r2(std::move(r1));EXPECT_TRUE(r2.match("916"));}TEST_F(RegexTest, MoveAssign) {Regex r1("^[0-9]+$");Regex r2("abc");r2 = std::move(r1);EXPECT_TRUE(r2.match("916"));std::string Error;EXPECT_FALSE(r1.isValid(Error));}TEST_F(RegexTest, NoArgConstructor) {std::string Error;Regex r1;EXPECT_FALSE(r1.isValid(Error));EXPECT_EQ("invalid regular expression", Error);r1 = Regex("abc");EXPECT_TRUE(r1.isValid(Error));}TEST_F(RegexTest, MatchInvalid) {Regex r1;std::string Error;EXPECT_FALSE(r1.isValid(Error));EXPECT_FALSE(r1.match("X"));}// https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=3727TEST_F(RegexTest, OssFuzz3727Regression) {// Wrap in a StringRef so the NUL byte doesn't terminate the stringRegex r(StringRef("[[[=GS\x00[=][", 10));std::string Error;EXPECT_FALSE(r.isValid(Error));}}
//===----- unittests/RISCVAttributeParserTest.cpp -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/RISCVAttributeParser.h"#include "llvm/Support/ARMBuildAttributes.h"#include "llvm/Support/ELFAttributes.h"#include "gtest/gtest.h"#include <string>using namespace llvm;struct RISCVAttributeSection {unsigned Tag;unsigned Value;RISCVAttributeSection(unsigned tag, unsigned value): Tag(tag), Value(value) {}void write(raw_ostream &OS) {OS.flush();// length = length + "riscv\0" + TagFile + ByteSize + Tag + Value;// length = 17 bytesOS << 'A' << (uint8_t)17 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0;OS << "riscv" << '\0';OS << (uint8_t)1 << (uint8_t)7 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0;OS << (uint8_t)Tag << (uint8_t)Value;}};static bool testAttribute(unsigned Tag, unsigned Value, unsigned ExpectedTag,unsigned ExpectedValue) {std::string buffer;raw_string_ostream OS(buffer);RISCVAttributeSection Section(Tag, Value);Section.write(OS);ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(OS.str().c_str()),OS.str().size());RISCVAttributeParser Parser;cantFail(Parser.parse(Bytes, support::little));Optional<unsigned> Attr = Parser.getAttributeValue(ExpectedTag);return Attr && *Attr == ExpectedValue;}static bool testTagString(unsigned Tag, const char *name) {return ELFAttrs::attrTypeAsString(Tag, RISCVAttrs::getRISCVAttributeTags()).str() == name;}TEST(StackAlign, testAttribute) {EXPECT_TRUE(testTagString(4, "Tag_stack_align"));EXPECT_TRUE(testAttribute(4, 4, RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_4));EXPECT_TRUE(testAttribute(4, 16, RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_16));}TEST(UnalignedAccess, testAttribute) {EXPECT_TRUE(testTagString(6, "Tag_unaligned_access"));EXPECT_TRUE(testAttribute(6, 0, RISCVAttrs::UNALIGNED_ACCESS,RISCVAttrs::NOT_ALLOWED));EXPECT_TRUE(testAttribute(6, 1, RISCVAttrs::UNALIGNED_ACCESS, RISCVAttrs::ALLOWED));}
//===- unittest/Support/ProgramTest.cpp -----------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Program.h"#include "llvm/Config/llvm-config.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/ConvertUTF.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "llvm/Support/Signals.h"#include "gtest/gtest.h"#include <stdlib.h>#include <thread>#if defined(__APPLE__)# include <crt_externs.h>#elif !defined(_MSC_VER)// Forward declare environ in case it's not provided by stdlib.h.extern char **environ;#endif#if defined(LLVM_ON_UNIX)#include <unistd.h>void sleep_for(unsigned int seconds) {sleep(seconds);}#elif defined(_WIN32)#include <windows.h>void sleep_for(unsigned int seconds) {Sleep(seconds * 1000);}#else#error sleep_for is not implemented on your platform.#endif#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}// From TestMain.cpp.extern const char *TestMainArgv0;namespace {using namespace llvm;using namespace sys;static cl::opt<std::string>ProgramTestStringArg1("program-test-string-arg1");static cl::opt<std::string>ProgramTestStringArg2("program-test-string-arg2");class ProgramEnvTest : public testing::Test {std::vector<StringRef> EnvTable;std::vector<std::string> EnvStorage;protected:void SetUp() override {auto EnvP = [] {#if defined(_WIN32)_wgetenv(L"TMP"); // Populate _wenviron, initially is nullreturn _wenviron;#elif defined(__APPLE__)return *_NSGetEnviron();#elsereturn environ;#endif}();ASSERT_TRUE(EnvP);auto prepareEnvVar = [this](decltype(*EnvP) Var) -> StringRef {#if defined(_WIN32)// On Windows convert UTF16 encoded variable to UTF8auto Len = wcslen(Var);ArrayRef<char> Ref{reinterpret_cast<char const *>(Var),Len * sizeof(*Var)};EnvStorage.emplace_back();auto convStatus = convertUTF16ToUTF8String(Ref, EnvStorage.back());EXPECT_TRUE(convStatus);return EnvStorage.back();#else(void)this;return StringRef(Var);#endif};while (*EnvP != nullptr) {auto S = prepareEnvVar(*EnvP);if (!StringRef(S).startswith("GTEST_"))EnvTable.emplace_back(S);++EnvP;}}void TearDown() override {EnvTable.clear();EnvStorage.clear();}void addEnvVar(StringRef Var) { EnvTable.emplace_back(Var); }ArrayRef<StringRef> getEnviron() const { return EnvTable; }};#ifdef _WIN32void checkSeparators(StringRef Path) {char UndesiredSeparator = sys::path::get_separator()[0] == '/' ? '\\' : '/';ASSERT_EQ(Path.find(UndesiredSeparator), StringRef::npos);}TEST_F(ProgramEnvTest, CreateProcessLongPath) {if (getenv("LLVM_PROGRAM_TEST_LONG_PATH"))exit(0);// getMainExecutable returns an absolute path; prepend the long-path prefix.SmallString<128> MyAbsExe(sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1));checkSeparators(MyAbsExe);// Force a path with backslashes, when we are going to prepend the \\?\// prefix.sys::path::native(MyAbsExe, sys::path::Style::windows_backslash);std::string MyExe;if (!StringRef(MyAbsExe).startswith("\\\\?\\"))MyExe.append("\\\\?\\");MyExe.append(std::string(MyAbsExe.begin(), MyAbsExe.end()));StringRef ArgV[] = {MyExe,"--gtest_filter=ProgramEnvTest.CreateProcessLongPath"};// Add LLVM_PROGRAM_TEST_LONG_PATH to the environment of the child.addEnvVar("LLVM_PROGRAM_TEST_LONG_PATH=1");// Redirect stdout to a long path.SmallString<128> TestDirectory;ASSERT_NO_ERROR(fs::createUniqueDirectory("program-redirect-test", TestDirectory));SmallString<256> LongPath(TestDirectory);LongPath.push_back('\\');// MAX_PATH = 260LongPath.append(260 - TestDirectory.size(), 'a');std::string Error;bool ExecutionFailed;Optional<StringRef> Redirects[] = {None, LongPath.str(), None};int RC = ExecuteAndWait(MyExe, ArgV, getEnviron(), Redirects,/*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &Error,&ExecutionFailed);EXPECT_FALSE(ExecutionFailed) << Error;EXPECT_EQ(0, RC);// Remove the long stdout.ASSERT_NO_ERROR(fs::remove(Twine(LongPath)));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory)));}#endifTEST_F(ProgramEnvTest, CreateProcessTrailingSlash) {if (getenv("LLVM_PROGRAM_TEST_CHILD")) {if (ProgramTestStringArg1 == "has\\\\ trailing\\" &&ProgramTestStringArg2 == "has\\\\ trailing\\") {exit(0); // Success! The arguments were passed and parsed.}exit(1);}std::string my_exe =sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);StringRef argv[] = {my_exe,"--gtest_filter=ProgramEnvTest.CreateProcessTrailingSlash","-program-test-string-arg1","has\\\\ trailing\\","-program-test-string-arg2","has\\\\ trailing\\"};// Add LLVM_PROGRAM_TEST_CHILD to the environment of the child.addEnvVar("LLVM_PROGRAM_TEST_CHILD=1");std::string error;bool ExecutionFailed;// Redirect stdout and stdin to NUL, but let stderr through.#ifdef _WIN32StringRef nul("NUL");#elseStringRef nul("/dev/null");#endifOptional<StringRef> redirects[] = { nul, nul, None };int rc = ExecuteAndWait(my_exe, argv, getEnviron(), redirects,/*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &error,&ExecutionFailed);EXPECT_FALSE(ExecutionFailed) << error;EXPECT_EQ(0, rc);}TEST_F(ProgramEnvTest, TestExecuteNoWait) {using namespace llvm::sys;if (getenv("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT")) {sleep_for(/*seconds*/ 1);exit(0);}std::string Executable =sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);StringRef argv[] = {Executable,"--gtest_filter=ProgramEnvTest.TestExecuteNoWait"};// Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child.addEnvVar("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1");std::string Error;bool ExecutionFailed;ProcessInfo PI1 = ExecuteNoWait(Executable, argv, getEnviron(), {}, 0, &Error,&ExecutionFailed);ASSERT_FALSE(ExecutionFailed) << Error;ASSERT_NE(PI1.Pid, ProcessInfo::InvalidPid) << "Invalid process id";unsigned LoopCount = 0;// Test that Wait() with WaitUntilTerminates=true works. In this case,// LoopCount should only be incremented once.while (true) {++LoopCount;ProcessInfo WaitResult = llvm::sys::Wait(PI1, 0, true, &Error);ASSERT_TRUE(Error.empty());if (WaitResult.Pid == PI1.Pid)break;}EXPECT_EQ(LoopCount, 1u) << "LoopCount should be 1";ProcessInfo PI2 = ExecuteNoWait(Executable, argv, getEnviron(), {}, 0, &Error,&ExecutionFailed);ASSERT_FALSE(ExecutionFailed) << Error;ASSERT_NE(PI2.Pid, ProcessInfo::InvalidPid) << "Invalid process id";// Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this// cse, LoopCount should be greater than 1 (more than one increment occurs).while (true) {++LoopCount;ProcessInfo WaitResult = llvm::sys::Wait(PI2, 0, false, &Error);ASSERT_TRUE(Error.empty());if (WaitResult.Pid == PI2.Pid)break;}ASSERT_GT(LoopCount, 1u) << "LoopCount should be >1";}TEST_F(ProgramEnvTest, TestExecuteAndWaitTimeout) {using namespace llvm::sys;if (getenv("LLVM_PROGRAM_TEST_TIMEOUT")) {sleep_for(/*seconds*/ 10);exit(0);}std::string Executable =sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);StringRef argv[] = {Executable, "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitTimeout"};// Add LLVM_PROGRAM_TEST_TIMEOUT to the environment of the child.addEnvVar("LLVM_PROGRAM_TEST_TIMEOUT=1");std::string Error;bool ExecutionFailed;int RetCode =ExecuteAndWait(Executable, argv, getEnviron(), {}, /*secondsToWait=*/1, 0,&Error, &ExecutionFailed);ASSERT_EQ(-2, RetCode);}TEST(ProgramTest, TestExecuteNegative) {std::string Executable = "i_dont_exist";StringRef argv[] = {Executable};{std::string Error;bool ExecutionFailed;int RetCode = ExecuteAndWait(Executable, argv, llvm::None, {}, 0, 0, &Error,&ExecutionFailed);ASSERT_LT(RetCode, 0) << "On error ExecuteAndWait should return 0 or ""positive value indicating the result code";ASSERT_TRUE(ExecutionFailed);ASSERT_FALSE(Error.empty());}{std::string Error;bool ExecutionFailed;ProcessInfo PI = ExecuteNoWait(Executable, argv, llvm::None, {}, 0, &Error,&ExecutionFailed);ASSERT_EQ(PI.Pid, ProcessInfo::InvalidPid)<< "On error ExecuteNoWait should return an invalid ProcessInfo";ASSERT_TRUE(ExecutionFailed);ASSERT_FALSE(Error.empty());}}#ifdef _WIN32const char utf16le_text[] ="\x6c\x00\x69\x00\x6e\x00\x67\x00\xfc\x00\x69\x00\xe7\x00\x61\x00";const char utf16be_text[] ="\x00\x6c\x00\x69\x00\x6e\x00\x67\x00\xfc\x00\x69\x00\xe7\x00\x61";#endifconst char utf8_text[] = "\x6c\x69\x6e\x67\xc3\xbc\x69\xc3\xa7\x61";TEST(ProgramTest, TestWriteWithSystemEncoding) {SmallString<128> TestDirectory;ASSERT_NO_ERROR(fs::createUniqueDirectory("program-test", TestDirectory));errs() << "Test Directory: " << TestDirectory << '\n';errs().flush();SmallString<128> file_pathname(TestDirectory);path::append(file_pathname, "international-file.txt");// Only on Windows we should encode in UTF16. For other systems, use UTF8ASSERT_NO_ERROR(sys::writeFileWithEncoding(file_pathname.c_str(), utf8_text,sys::WEM_UTF16));int fd = 0;ASSERT_NO_ERROR(fs::openFileForRead(file_pathname.c_str(), fd));#if defined(_WIN32)char buf[18];ASSERT_EQ(::read(fd, buf, 18), 18);const char *utf16_text;if (strncmp(buf, "\xfe\xff", 2) == 0) { // UTF16-BEutf16_text = utf16be_text;} else if (strncmp(buf, "\xff\xfe", 2) == 0) { // UTF16-LEutf16_text = utf16le_text;} else {FAIL() << "Invalid BOM in UTF-16 file";}ASSERT_EQ(strncmp(&buf[2], utf16_text, 16), 0);#elsechar buf[10];ASSERT_EQ(::read(fd, buf, 10), 10);ASSERT_EQ(strncmp(buf, utf8_text, 10), 0);#endif::close(fd);ASSERT_NO_ERROR(fs::remove(file_pathname.str()));ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));}TEST_F(ProgramEnvTest, TestExecuteAndWaitStatistics) {using namespace llvm::sys;if (getenv("LLVM_PROGRAM_TEST_STATISTICS"))exit(0);std::string Executable =sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);StringRef argv[] = {Executable, "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitStatistics"};// Add LLVM_PROGRAM_TEST_STATISTICS to the environment of the child.addEnvVar("LLVM_PROGRAM_TEST_STATISTICS=1");std::string Error;bool ExecutionFailed;Optional<ProcessStatistics> ProcStat;int RetCode = ExecuteAndWait(Executable, argv, getEnviron(), {}, 0, 0, &Error,&ExecutionFailed, &ProcStat);ASSERT_EQ(0, RetCode);ASSERT_TRUE(ProcStat);ASSERT_GE(ProcStat->UserTime, std::chrono::microseconds(0));ASSERT_GE(ProcStat->TotalTime, ProcStat->UserTime);}TEST_F(ProgramEnvTest, TestLockFile) {using namespace llvm::sys;if (const char *LockedFile = getenv("LLVM_PROGRAM_TEST_LOCKED_FILE")) {// Child process.int FD2;ASSERT_NO_ERROR(fs::openFileForReadWrite(LockedFile, FD2,fs::CD_OpenExisting, fs::OF_None));std::error_code ErrC = fs::tryLockFile(FD2, std::chrono::seconds(5));ASSERT_NO_ERROR(ErrC);ASSERT_NO_ERROR(fs::unlockFile(FD2));close(FD2);exit(0);}// Create file that will be locked.SmallString<64> LockedFile;int FD1;ASSERT_NO_ERROR(fs::createTemporaryFile("TestLockFile", "temp", FD1, LockedFile));std::string Executable =sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);StringRef argv[] = {Executable, "--gtest_filter=ProgramEnvTest.TestLockFile"};// Add LLVM_PROGRAM_TEST_LOCKED_FILE to the environment of the child.std::string EnvVar = "LLVM_PROGRAM_TEST_LOCKED_FILE=";EnvVar += LockedFile.str();addEnvVar(EnvVar);// Lock the file.ASSERT_NO_ERROR(fs::tryLockFile(FD1));std::string Error;bool ExecutionFailed;ProcessInfo PI2 = ExecuteNoWait(Executable, argv, getEnviron(), {}, 0, &Error,&ExecutionFailed);ASSERT_FALSE(ExecutionFailed) << Error;ASSERT_TRUE(Error.empty());ASSERT_NE(PI2.Pid, ProcessInfo::InvalidPid) << "Invalid process id";// Wait some time to give the child process a chance to start.std::this_thread::sleep_for(std::chrono::milliseconds(100));ASSERT_NO_ERROR(fs::unlockFile(FD1));ProcessInfo WaitResult = llvm::sys::Wait(PI2, 5 /* seconds */, true, &Error);ASSERT_TRUE(Error.empty());ASSERT_EQ(0, WaitResult.ReturnCode);ASSERT_EQ(WaitResult.Pid, PI2.Pid);sys::fs::remove(LockedFile);}TEST_F(ProgramEnvTest, TestExecuteWithNoStacktraceHandler) {using namespace llvm::sys;if (getenv("LLVM_PROGRAM_TEST_NO_STACKTRACE_HANDLER")) {sys::PrintStackTrace(errs());exit(0);}std::string Executable =sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);StringRef argv[] = {Executable,"--gtest_filter=ProgramEnvTest.TestExecuteWithNoStacktraceHandler"};addEnvVar("LLVM_PROGRAM_TEST_NO_STACKTRACE_HANDLER=1");std::string Error;bool ExecutionFailed;int RetCode = ExecuteAndWait(Executable, argv, getEnviron(), {}, 0, 0, &Error,&ExecutionFailed);EXPECT_FALSE(ExecutionFailed) << Error;ASSERT_EQ(0, RetCode);}} // end anonymous namespace
//===- unittest/Support/ProcessTest.cpp -----------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Triple.h"#include "llvm/Support/Error.h"#include "llvm/Support/Host.h"#include "llvm/Support/Process.h"#include "gtest/gtest.h"#ifdef _WIN32#include <windows.h>#endifnamespace {using namespace llvm;using namespace sys;TEST(ProcessTest, GetProcessIdTest) {const Process::Pid pid = Process::getProcessId();#ifdef _WIN32EXPECT_EQ((DWORD)pid, ::GetCurrentProcessId());#elseEXPECT_EQ(pid, ::getpid());#endif}TEST(ProcessTest, GetRandomNumberTest) {const unsigned r1 = Process::GetRandomNumber();const unsigned r2 = Process::GetRandomNumber();// It should be extremely unlikely that both r1 and r2 are 0.EXPECT_NE((r1 | r2), 0u);}#ifdef _MSC_VER#define setenv(name, var, ignore) _putenv_s(name, var)#endif#if HAVE_SETENV || _MSC_VERTEST(ProcessTest, Basic) {setenv("__LLVM_TEST_ENVIRON_VAR__", "abc", true);Optional<std::string> val(Process::GetEnv("__LLVM_TEST_ENVIRON_VAR__"));EXPECT_TRUE(val.hasValue());EXPECT_STREQ("abc", val->c_str());}TEST(ProcessTest, None) {Optional<std::string> val(Process::GetEnv("__LLVM_TEST_ENVIRON_NO_SUCH_VAR__"));EXPECT_FALSE(val.hasValue());}#endif#ifdef _WIN32TEST(ProcessTest, EmptyVal) {SetEnvironmentVariableA("__LLVM_TEST_ENVIRON_VAR__", "");Optional<std::string> val(Process::GetEnv("__LLVM_TEST_ENVIRON_VAR__"));EXPECT_TRUE(val.hasValue());EXPECT_STREQ("", val->c_str());}TEST(ProcessTest, Wchar) {SetEnvironmentVariableW(L"__LLVM_TEST_ENVIRON_VAR__", L"abcdefghijklmnopqrs");Optional<std::string> val(Process::GetEnv("__LLVM_TEST_ENVIRON_VAR__"));EXPECT_TRUE(val.hasValue());EXPECT_STREQ("abcdefghijklmnopqrs", val->c_str());}#endifclass PageSizeTest : public testing::Test {Triple Host;protected:PageSizeTest() : Host(Triple::normalize(sys::getProcessTriple())) {}bool isSupported() const {// For now just on X86-64 and Aarch64. This can be expanded in the future.return (Host.getArch() == Triple::x86_64 ||Host.getArch() == Triple::aarch64) &&Host.getOS() == Triple::Linux;}bool pageSizeAsExpected(unsigned PageSize) const {switch (Host.getArch()) {case Triple::x86_64:return PageSize == 4096;case Triple::aarch64:// supported granule sizes are 4k, 16k and 64kreturn PageSize == 4096 || PageSize == 16384 || PageSize == 65536;default:llvm_unreachable("unexpected arch!");}}};TEST_F(PageSizeTest, PageSize) {if (!isSupported())GTEST_SKIP();llvm::Expected<unsigned> Result = llvm::sys::Process::getPageSize();ASSERT_FALSE(!Result);ASSERT_TRUE(pageSizeAsExpected(*Result));}} // end anonymous namespace
//===- llvm/unittest/Support/Path.cpp - Path tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Path.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/ScopeExit.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/Triple.h"#include "llvm/BinaryFormat/Magic.h"#include "llvm/Config/llvm-config.h"#include "llvm/Support/Compiler.h"#include "llvm/Support/ConvertUTF.h"#include "llvm/Support/Duration.h"#include "llvm/Support/Errc.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/FileUtilities.h"#include "llvm/Support/Host.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#ifdef _WIN32#include "llvm/ADT/ArrayRef.h"#include "llvm/Support/Chrono.h"#include "llvm/Support/Windows/WindowsSupport.h"#include <windows.h>#include <winerror.h>#endif#ifdef LLVM_ON_UNIX#include <pwd.h>#include <sys/stat.h>#endifusing namespace llvm;using namespace llvm::sys;#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}#define ASSERT_ERROR(x) \if (!x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return a failure error code.\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \}namespace {void checkSeparators(StringRef Path) {#ifdef _WIN32char UndesiredSeparator = sys::path::get_separator()[0] == '/' ? '\\' : '/';ASSERT_EQ(Path.find(UndesiredSeparator), StringRef::npos);#endif}struct FileDescriptorCloser {explicit FileDescriptorCloser(int FD) : FD(FD) {}~FileDescriptorCloser() { ::close(FD); }int FD;};TEST(is_style_Style, Works) {using namespace llvm::sys::path;// Check platform-independent results.EXPECT_TRUE(is_style_posix(Style::posix));EXPECT_TRUE(is_style_windows(Style::windows));EXPECT_TRUE(is_style_windows(Style::windows_slash));EXPECT_FALSE(is_style_posix(Style::windows));EXPECT_FALSE(is_style_posix(Style::windows_slash));EXPECT_FALSE(is_style_windows(Style::posix));// Check platform-dependent results.#if defined(_WIN32)EXPECT_FALSE(is_style_posix(Style::native));EXPECT_TRUE(is_style_windows(Style::native));#elseEXPECT_TRUE(is_style_posix(Style::native));EXPECT_FALSE(is_style_windows(Style::native));#endif}TEST(is_separator, Works) {EXPECT_TRUE(path::is_separator('/'));EXPECT_FALSE(path::is_separator('\0'));EXPECT_FALSE(path::is_separator('-'));EXPECT_FALSE(path::is_separator(' '));EXPECT_TRUE(path::is_separator('\\', path::Style::windows));EXPECT_TRUE(path::is_separator('\\', path::Style::windows_slash));EXPECT_FALSE(path::is_separator('\\', path::Style::posix));EXPECT_EQ(path::is_style_windows(path::Style::native),path::is_separator('\\'));}TEST(get_separator, Works) {EXPECT_EQ(path::get_separator(path::Style::posix), "/");EXPECT_EQ(path::get_separator(path::Style::windows_backslash), "\\");EXPECT_EQ(path::get_separator(path::Style::windows_slash), "/");}TEST(is_absolute_gnu, Works) {// Test tuple <Path, ExpectedPosixValue, ExpectedWindowsValue>.const std::tuple<StringRef, bool, bool> Paths[] = {std::make_tuple("", false, false),std::make_tuple("/", true, true),std::make_tuple("/foo", true, true),std::make_tuple("\\", false, true),std::make_tuple("\\foo", false, true),std::make_tuple("foo", false, false),std::make_tuple("c", false, false),std::make_tuple("c:", false, true),std::make_tuple("c:\\", false, true),std::make_tuple("!:", false, true),std::make_tuple("xx:", false, false),std::make_tuple("c:abc\\", false, true),std::make_tuple(":", false, false)};for (const auto &Path : Paths) {EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::posix),std::get<1>(Path));EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::windows),std::get<2>(Path));constexpr int Native = is_style_posix(path::Style::native) ? 1 : 2;EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::native),std::get<Native>(Path));}}TEST(Support, Path) {SmallVector<StringRef, 40> paths;paths.push_back("");paths.push_back(".");paths.push_back("..");paths.push_back("foo");paths.push_back("/");paths.push_back("/foo");paths.push_back("foo/");paths.push_back("/foo/");paths.push_back("foo/bar");paths.push_back("/foo/bar");paths.push_back("//net");paths.push_back("//net/");paths.push_back("//net/foo");paths.push_back("///foo///");paths.push_back("///foo///bar");paths.push_back("/.");paths.push_back("./");paths.push_back("/..");paths.push_back("../");paths.push_back("foo/.");paths.push_back("foo/..");paths.push_back("foo/./");paths.push_back("foo/./bar");paths.push_back("foo/..");paths.push_back("foo/../");paths.push_back("foo/../bar");paths.push_back("c:");paths.push_back("c:/");paths.push_back("c:foo");paths.push_back("c:/foo");paths.push_back("c:foo/");paths.push_back("c:/foo/");paths.push_back("c:/foo/bar");paths.push_back("prn:");paths.push_back("c:\\");paths.push_back("c:foo");paths.push_back("c:\\foo");paths.push_back("c:foo\\");paths.push_back("c:\\foo\\");paths.push_back("c:\\foo/");paths.push_back("c:/foo\\bar");for (SmallVector<StringRef, 40>::const_iterator i = paths.begin(),e = paths.end();i != e;++i) {SCOPED_TRACE(*i);SmallVector<StringRef, 5> ComponentStack;for (sys::path::const_iterator ci = sys::path::begin(*i),ce = sys::path::end(*i);ci != ce;++ci) {EXPECT_FALSE(ci->empty());ComponentStack.push_back(*ci);}SmallVector<StringRef, 5> ReverseComponentStack;for (sys::path::reverse_iterator ci = sys::path::rbegin(*i),ce = sys::path::rend(*i);ci != ce;++ci) {EXPECT_FALSE(ci->empty());ReverseComponentStack.push_back(*ci);}std::reverse(ReverseComponentStack.begin(), ReverseComponentStack.end());EXPECT_THAT(ComponentStack, testing::ContainerEq(ReverseComponentStack));// Crash test most of the API - since we're iterating over all of our paths// here there isn't really anything reasonable to assert on in the results.(void)path::has_root_path(*i);(void)path::root_path(*i);(void)path::has_root_name(*i);(void)path::root_name(*i);(void)path::has_root_directory(*i);(void)path::root_directory(*i);(void)path::has_parent_path(*i);(void)path::parent_path(*i);(void)path::has_filename(*i);(void)path::filename(*i);(void)path::has_stem(*i);(void)path::stem(*i);(void)path::has_extension(*i);(void)path::extension(*i);(void)path::is_absolute(*i);(void)path::is_absolute_gnu(*i);(void)path::is_relative(*i);SmallString<128> temp_store;temp_store = *i;ASSERT_NO_ERROR(fs::make_absolute(temp_store));temp_store = *i;path::remove_filename(temp_store);temp_store = *i;path::replace_extension(temp_store, "ext");StringRef filename(temp_store.begin(), temp_store.size()), stem, ext;stem = path::stem(filename);ext = path::extension(filename);EXPECT_EQ(*sys::path::rbegin(filename), (stem + ext).str());path::native(*i, temp_store);}{SmallString<32> Relative("foo.cpp");sys::fs::make_absolute("/root", Relative);Relative[5] = '/'; // Fix up windows paths.ASSERT_EQ("/root/foo.cpp", Relative);}{SmallString<32> Relative("foo.cpp");sys::fs::make_absolute("//root", Relative);Relative[6] = '/'; // Fix up windows paths.ASSERT_EQ("//root/foo.cpp", Relative);}}TEST(Support, PathRoot) {ASSERT_EQ(path::root_name("//net/hello", path::Style::posix).str(), "//net");ASSERT_EQ(path::root_name("c:/hello", path::Style::posix).str(), "");ASSERT_EQ(path::root_name("c:/hello", path::Style::windows).str(), "c:");ASSERT_EQ(path::root_name("/hello", path::Style::posix).str(), "");ASSERT_EQ(path::root_directory("/goo/hello", path::Style::posix).str(), "/");ASSERT_EQ(path::root_directory("c:/hello", path::Style::windows).str(), "/");ASSERT_EQ(path::root_directory("d/file.txt", path::Style::posix).str(), "");ASSERT_EQ(path::root_directory("d/file.txt", path::Style::windows).str(), "");SmallVector<StringRef, 40> paths;paths.push_back("");paths.push_back(".");paths.push_back("..");paths.push_back("foo");paths.push_back("/");paths.push_back("/foo");paths.push_back("foo/");paths.push_back("/foo/");paths.push_back("foo/bar");paths.push_back("/foo/bar");paths.push_back("//net");paths.push_back("//net/");paths.push_back("//net/foo");paths.push_back("///foo///");paths.push_back("///foo///bar");paths.push_back("/.");paths.push_back("./");paths.push_back("/..");paths.push_back("../");paths.push_back("foo/.");paths.push_back("foo/..");paths.push_back("foo/./");paths.push_back("foo/./bar");paths.push_back("foo/..");paths.push_back("foo/../");paths.push_back("foo/../bar");paths.push_back("c:");paths.push_back("c:/");paths.push_back("c:foo");paths.push_back("c:/foo");paths.push_back("c:foo/");paths.push_back("c:/foo/");paths.push_back("c:/foo/bar");paths.push_back("prn:");paths.push_back("c:\\");paths.push_back("c:foo");paths.push_back("c:\\foo");paths.push_back("c:foo\\");paths.push_back("c:\\foo\\");paths.push_back("c:\\foo/");paths.push_back("c:/foo\\bar");for (StringRef p : paths) {ASSERT_EQ(path::root_name(p, path::Style::posix).str() + path::root_directory(p, path::Style::posix).str(),path::root_path(p, path::Style::posix).str());ASSERT_EQ(path::root_name(p, path::Style::windows).str() + path::root_directory(p, path::Style::windows).str(),path::root_path(p, path::Style::windows).str());}}TEST(Support, FilenameParent) {EXPECT_EQ("/", path::filename("/"));EXPECT_EQ("", path::parent_path("/"));EXPECT_EQ("\\", path::filename("c:\\", path::Style::windows));EXPECT_EQ("c:", path::parent_path("c:\\", path::Style::windows));EXPECT_EQ("/", path::filename("///"));EXPECT_EQ("", path::parent_path("///"));EXPECT_EQ("\\", path::filename("c:\\\\", path::Style::windows));EXPECT_EQ("c:", path::parent_path("c:\\\\", path::Style::windows));EXPECT_EQ("bar", path::filename("/foo/bar"));EXPECT_EQ("/foo", path::parent_path("/foo/bar"));EXPECT_EQ("foo", path::filename("/foo"));EXPECT_EQ("/", path::parent_path("/foo"));EXPECT_EQ("foo", path::filename("foo"));EXPECT_EQ("", path::parent_path("foo"));EXPECT_EQ(".", path::filename("foo/"));EXPECT_EQ("foo", path::parent_path("foo/"));EXPECT_EQ("//net", path::filename("//net"));EXPECT_EQ("", path::parent_path("//net"));EXPECT_EQ("/", path::filename("//net/"));EXPECT_EQ("//net", path::parent_path("//net/"));EXPECT_EQ("foo", path::filename("//net/foo"));EXPECT_EQ("//net/", path::parent_path("//net/foo"));// These checks are just to make sure we do something reasonable with the// paths below. They are not meant to prescribe the one true interpretation of// these paths. Other decompositions (e.g. "//" -> "" + "//") are also// possible.EXPECT_EQ("/", path::filename("//"));EXPECT_EQ("", path::parent_path("//"));EXPECT_EQ("\\", path::filename("\\\\", path::Style::windows));EXPECT_EQ("", path::parent_path("\\\\", path::Style::windows));EXPECT_EQ("\\", path::filename("\\\\\\", path::Style::windows));EXPECT_EQ("", path::parent_path("\\\\\\", path::Style::windows));}static std::vector<StringRef>GetComponents(StringRef Path, path::Style S = path::Style::native) {return {path::begin(Path, S), path::end(Path)};}TEST(Support, PathIterator) {EXPECT_THAT(GetComponents("/foo"), testing::ElementsAre("/", "foo"));EXPECT_THAT(GetComponents("/"), testing::ElementsAre("/"));EXPECT_THAT(GetComponents("//"), testing::ElementsAre("/"));EXPECT_THAT(GetComponents("///"), testing::ElementsAre("/"));EXPECT_THAT(GetComponents("c/d/e/foo.txt"),testing::ElementsAre("c", "d", "e", "foo.txt"));EXPECT_THAT(GetComponents(".c/.d/../."),testing::ElementsAre(".c", ".d", "..", "."));EXPECT_THAT(GetComponents("/c/d/e/foo.txt"),testing::ElementsAre("/", "c", "d", "e", "foo.txt"));EXPECT_THAT(GetComponents("/.c/.d/../."),testing::ElementsAre("/", ".c", ".d", "..", "."));EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows),testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows_slash),testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/"));EXPECT_THAT(GetComponents("//net/c/foo.txt"),testing::ElementsAre("//net", "/", "c", "foo.txt"));}TEST(Support, AbsolutePathIteratorEnd) {// Trailing slashes are converted to '.' unless they are part of the root path.SmallVector<std::pair<StringRef, path::Style>, 4> Paths;Paths.emplace_back("/foo/", path::Style::native);Paths.emplace_back("/foo//", path::Style::native);Paths.emplace_back("//net/foo/", path::Style::native);Paths.emplace_back("c:\\foo\\", path::Style::windows);for (auto &Path : Paths) {SCOPED_TRACE(Path.first);StringRef LastComponent = *path::rbegin(Path.first, Path.second);EXPECT_EQ(".", LastComponent);}SmallVector<std::pair<StringRef, path::Style>, 3> RootPaths;RootPaths.emplace_back("/", path::Style::native);RootPaths.emplace_back("//net/", path::Style::native);RootPaths.emplace_back("c:\\", path::Style::windows);RootPaths.emplace_back("//net//", path::Style::native);RootPaths.emplace_back("c:\\\\", path::Style::windows);for (auto &Path : RootPaths) {SCOPED_TRACE(Path.first);StringRef LastComponent = *path::rbegin(Path.first, Path.second);EXPECT_EQ(1u, LastComponent.size());EXPECT_TRUE(path::is_separator(LastComponent[0], Path.second));}}#ifdef _WIN32std::string getEnvWin(const wchar_t *Var) {std::string expected;if (wchar_t const *path = ::_wgetenv(Var)) {auto pathLen = ::wcslen(path);ArrayRef<char> ref{reinterpret_cast<char const *>(path),pathLen * sizeof(wchar_t)};convertUTF16ToUTF8String(ref, expected);SmallString<32> Buf(expected);path::make_preferred(Buf);expected.assign(Buf.begin(), Buf.end());}return expected;}#else// RAII helper to set and restore an environment variable.class WithEnv {const char *Var;llvm::Optional<std::string> OriginalValue;public:WithEnv(const char *Var, const char *Value) : Var(Var) {if (const char *V = ::getenv(Var))OriginalValue.emplace(V);if (Value)::setenv(Var, Value, 1);else::unsetenv(Var);}~WithEnv() {if (OriginalValue)::setenv(Var, OriginalValue->c_str(), 1);else::unsetenv(Var);}};#endifTEST(Support, HomeDirectory) {std::string expected;#ifdef _WIN32expected = getEnvWin(L"USERPROFILE");#elseif (char const *path = ::getenv("HOME"))expected = path;#endif// Do not try to test it if we don't know what to expect.// On Windows we use something better than env vars.if (expected.empty())GTEST_SKIP();SmallString<128> HomeDir;auto status = path::home_directory(HomeDir);EXPECT_TRUE(status);EXPECT_EQ(expected, HomeDir);}// Apple has their own solution for this.#if defined(LLVM_ON_UNIX) && !defined(__APPLE__)TEST(Support, HomeDirectoryWithNoEnv) {WithEnv Env("HOME", nullptr);// Don't run the test if we have nothing to compare against.struct passwd *pw = getpwuid(getuid());if (!pw || !pw->pw_dir) return;std::string PwDir = pw->pw_dir;SmallString<128> HomeDir;EXPECT_TRUE(path::home_directory(HomeDir));EXPECT_EQ(PwDir, HomeDir);}TEST(Support, ConfigDirectoryWithEnv) {WithEnv Env("XDG_CONFIG_HOME", "/xdg/config");SmallString<128> ConfigDir;EXPECT_TRUE(path::user_config_directory(ConfigDir));EXPECT_EQ("/xdg/config", ConfigDir);}TEST(Support, ConfigDirectoryNoEnv) {WithEnv Env("XDG_CONFIG_HOME", nullptr);SmallString<128> Fallback;ASSERT_TRUE(path::home_directory(Fallback));path::append(Fallback, ".config");SmallString<128> CacheDir;EXPECT_TRUE(path::user_config_directory(CacheDir));EXPECT_EQ(Fallback, CacheDir);}TEST(Support, CacheDirectoryWithEnv) {WithEnv Env("XDG_CACHE_HOME", "/xdg/cache");SmallString<128> CacheDir;EXPECT_TRUE(path::cache_directory(CacheDir));EXPECT_EQ("/xdg/cache", CacheDir);}TEST(Support, CacheDirectoryNoEnv) {WithEnv Env("XDG_CACHE_HOME", nullptr);SmallString<128> Fallback;ASSERT_TRUE(path::home_directory(Fallback));path::append(Fallback, ".cache");SmallString<128> CacheDir;EXPECT_TRUE(path::cache_directory(CacheDir));EXPECT_EQ(Fallback, CacheDir);}#endif#ifdef __APPLE__TEST(Support, ConfigDirectory) {SmallString<128> Fallback;ASSERT_TRUE(path::home_directory(Fallback));path::append(Fallback, "Library/Preferences");SmallString<128> ConfigDir;EXPECT_TRUE(path::user_config_directory(ConfigDir));EXPECT_EQ(Fallback, ConfigDir);}#endif#ifdef _WIN32TEST(Support, ConfigDirectory) {std::string Expected = getEnvWin(L"LOCALAPPDATA");// Do not try to test it if we don't know what to expect.if (Expected.empty())GTEST_SKIP();SmallString<128> CacheDir;EXPECT_TRUE(path::user_config_directory(CacheDir));EXPECT_EQ(Expected, CacheDir);}TEST(Support, CacheDirectory) {std::string Expected = getEnvWin(L"LOCALAPPDATA");// Do not try to test it if we don't know what to expect.if (Expected.empty())GTEST_SKIP();SmallString<128> CacheDir;EXPECT_TRUE(path::cache_directory(CacheDir));EXPECT_EQ(Expected, CacheDir);}#endifTEST(Support, TempDirectory) {SmallString<32> TempDir;path::system_temp_directory(false, TempDir);EXPECT_TRUE(!TempDir.empty());TempDir.clear();path::system_temp_directory(true, TempDir);EXPECT_TRUE(!TempDir.empty());}#ifdef _WIN32static std::string path2regex(std::string Path) {size_t Pos = 0;bool Forward = path::get_separator()[0] == '/';while ((Pos = Path.find('\\', Pos)) != std::string::npos) {if (Forward) {Path.replace(Pos, 1, "/");Pos += 1;} else {Path.replace(Pos, 1, "\\\\");Pos += 2;}}return Path;}/// Helper for running temp dir test in separated process. See below.#define EXPECT_TEMP_DIR(prepare, expected) \EXPECT_EXIT( \{ \prepare; \SmallString<300> TempDir; \path::system_temp_directory(true, TempDir); \raw_os_ostream(std::cerr) << TempDir; \std::exit(0); \}, \::testing::ExitedWithCode(0), path2regex(expected))TEST(SupportDeathTest, TempDirectoryOnWindows) {// In this test we want to check how system_temp_directory responds to// different values of specific env vars. To prevent corrupting env vars of// the current process all checks are done in separated processes.EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:\\OtherFolder"), "C:\\OtherFolder");EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:/Unix/Path/Seperators"),"C:\\Unix\\Path\\Seperators");EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"Local Path"), ".+\\Local Path$");EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"F:\\TrailingSep\\"), "F:\\TrailingSep");EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:\\2\x03C0r-\x00B5\x00B3\\\x2135\x2080"),"C:\\2\xCF\x80r-\xC2\xB5\xC2\xB3\\\xE2\x84\xB5\xE2\x82\x80");// Test $TMP empty, $TEMP set.EXPECT_TEMP_DIR({_wputenv_s(L"TMP", L"");_wputenv_s(L"TEMP", L"C:\\Valid\\Path");},"C:\\Valid\\Path");// All related env vars emptyEXPECT_TEMP_DIR({_wputenv_s(L"TMP", L"");_wputenv_s(L"TEMP", L"");_wputenv_s(L"USERPROFILE", L"");},"C:\\Temp");// Test evn var / path with 260 chars.SmallString<270> Expected{"C:\\Temp\\AB\\123456789"};while (Expected.size() < 260)Expected.append("\\DirNameWith19Charss");ASSERT_EQ(260U, Expected.size());EXPECT_TEMP_DIR(_putenv_s("TMP", Expected.c_str()), Expected.c_str());}#endifclass FileSystemTest : public testing::Test {protected:/// Unique temporary directory in which all created filesystem entities must/// be placed. It is removed at the end of each test (must be empty).SmallString<128> TestDirectory;SmallString<128> NonExistantFile;void SetUp() override {ASSERT_NO_ERROR(fs::createUniqueDirectory("file-system-test", TestDirectory));// We don't care about this specific file.errs() << "Test Directory: " << TestDirectory << '\n';errs().flush();NonExistantFile = TestDirectory;// Even though this value is hardcoded, is a 128-bit GUID, so we should be// guaranteed that this file will never exist.sys::path::append(NonExistantFile, "1B28B495C16344CB9822E588CD4C3EF0");}void TearDown() override { ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); }};TEST_F(FileSystemTest, Unique) {// Create a temp file.int FileDescriptor;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));// The same file should return an identical unique id.fs::UniqueID F1, F2;ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F1));ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F2));ASSERT_EQ(F1, F2);// Different files should return different unique ids.int FileDescriptor2;SmallString<64> TempPath2;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FileDescriptor2, TempPath2));fs::UniqueID D;ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D));ASSERT_NE(D, F1);::close(FileDescriptor2);ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));// Two paths representing the same file on disk should still provide the// same unique id. We can test this by making a hard link.ASSERT_NO_ERROR(fs::create_link(Twine(TempPath), Twine(TempPath2)));fs::UniqueID D2;ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D2));ASSERT_EQ(D2, F1);::close(FileDescriptor);SmallString<128> Dir1;ASSERT_NO_ERROR(fs::createUniqueDirectory("dir1", Dir1));ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F1));ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F2));ASSERT_EQ(F1, F2);SmallString<128> Dir2;ASSERT_NO_ERROR(fs::createUniqueDirectory("dir2", Dir2));ASSERT_NO_ERROR(fs::getUniqueID(Dir2.c_str(), F2));ASSERT_NE(F1, F2);ASSERT_NO_ERROR(fs::remove(Dir1));ASSERT_NO_ERROR(fs::remove(Dir2));ASSERT_NO_ERROR(fs::remove(TempPath2));ASSERT_NO_ERROR(fs::remove(TempPath));}TEST_F(FileSystemTest, RealPath) {ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/test1/test2/test3"));ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/test1/test2/test3"));SmallString<64> RealBase;SmallString<64> Expected;SmallString<64> Actual;// TestDirectory itself might be under a symlink or have been specified with// a different case than the existing temp directory. In such cases real_path// on the concatenated path will differ in the TestDirectory portion from// how we specified it. Make sure to compare against the real_path of the// TestDirectory, and not just the value of TestDirectory.ASSERT_NO_ERROR(fs::real_path(TestDirectory, RealBase));checkSeparators(RealBase);path::native(Twine(RealBase) + "/test1/test2", Expected);ASSERT_NO_ERROR(fs::real_path(Twine(TestDirectory) + "/././test1/../test1/test2/./test3/..", Actual));checkSeparators(Actual);EXPECT_EQ(Expected, Actual);SmallString<64> HomeDir;// This can fail if $HOME is not set and getpwuid fails.bool Result = llvm::sys::path::home_directory(HomeDir);if (Result) {checkSeparators(HomeDir);ASSERT_NO_ERROR(fs::real_path(HomeDir, Expected));checkSeparators(Expected);ASSERT_NO_ERROR(fs::real_path("~", Actual, true));EXPECT_EQ(Expected, Actual);ASSERT_NO_ERROR(fs::real_path("~/", Actual, true));EXPECT_EQ(Expected, Actual);}ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/test1"));}TEST_F(FileSystemTest, ExpandTilde) {SmallString<64> Expected;SmallString<64> Actual;SmallString<64> HomeDir;// This can fail if $HOME is not set and getpwuid fails.bool Result = llvm::sys::path::home_directory(HomeDir);if (Result) {fs::expand_tilde(HomeDir, Expected);fs::expand_tilde("~", Actual);EXPECT_EQ(Expected, Actual);#ifdef _WIN32Expected += "\\foo";fs::expand_tilde("~\\foo", Actual);#elseExpected += "/foo";fs::expand_tilde("~/foo", Actual);#endifEXPECT_EQ(Expected, Actual);}}#ifdef LLVM_ON_UNIXTEST_F(FileSystemTest, RealPathNoReadPerm) {SmallString<64> Expanded;ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/noreadperm"));ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/noreadperm"));fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::no_perms);fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::all_exe);ASSERT_NO_ERROR(fs::real_path(Twine(TestDirectory) + "/noreadperm", Expanded,false));ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/noreadperm"));}#endifTEST_F(FileSystemTest, TempFileKeepDiscard) {// We can keep then discard.auto TempFileOrError = fs::TempFile::create(TestDirectory + "/test-%%%%");ASSERT_TRUE((bool)TempFileOrError);fs::TempFile File = std::move(*TempFileOrError);ASSERT_EQ(-1, TempFileOrError->FD);ASSERT_FALSE((bool)File.keep(TestDirectory + "/keep"));ASSERT_FALSE((bool)File.discard());ASSERT_TRUE(fs::exists(TestDirectory + "/keep"));ASSERT_NO_ERROR(fs::remove(TestDirectory + "/keep"));}TEST_F(FileSystemTest, TempFileDiscardDiscard) {// We can discard twice.auto TempFileOrError = fs::TempFile::create(TestDirectory + "/test-%%%%");ASSERT_TRUE((bool)TempFileOrError);fs::TempFile File = std::move(*TempFileOrError);ASSERT_EQ(-1, TempFileOrError->FD);ASSERT_FALSE((bool)File.discard());ASSERT_FALSE((bool)File.discard());ASSERT_FALSE(fs::exists(TestDirectory + "/keep"));}TEST_F(FileSystemTest, TempFiles) {// Create a temp file.int FileDescriptor;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));// Make sure it exists.ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));// Create another temp tile.int FD2;SmallString<64> TempPath2;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD2, TempPath2));ASSERT_TRUE(TempPath2.endswith(".temp"));ASSERT_NE(TempPath.str(), TempPath2.str());fs::file_status A, B;ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));EXPECT_FALSE(fs::equivalent(A, B));::close(FD2);// Remove Temp2.ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));ASSERT_EQ(fs::remove(Twine(TempPath2), false),errc::no_such_file_or_directory);std::error_code EC = fs::status(TempPath2.c_str(), B);EXPECT_EQ(EC, errc::no_such_file_or_directory);EXPECT_EQ(B.type(), fs::file_type::file_not_found);// Make sure Temp2 doesn't exist.ASSERT_EQ(fs::access(Twine(TempPath2), sys::fs::AccessMode::Exist),errc::no_such_file_or_directory);SmallString<64> TempPath3;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "", TempPath3));ASSERT_FALSE(TempPath3.endswith("."));FileRemover Cleanup3(TempPath3);// Create a hard link to Temp1.ASSERT_NO_ERROR(fs::create_link(Twine(TempPath), Twine(TempPath2)));bool equal;ASSERT_NO_ERROR(fs::equivalent(Twine(TempPath), Twine(TempPath2), equal));EXPECT_TRUE(equal);ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));EXPECT_TRUE(fs::equivalent(A, B));// Remove Temp1.::close(FileDescriptor);ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));// Remove the hard link.ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));// Make sure Temp1 doesn't exist.ASSERT_EQ(fs::access(Twine(TempPath), sys::fs::AccessMode::Exist),errc::no_such_file_or_directory);#ifdef _WIN32// Path name > 260 chars should get an error.const char *Path270 ="abcdefghijklmnopqrstuvwxyz9abcdefghijklmnopqrstuvwxyz8""abcdefghijklmnopqrstuvwxyz7abcdefghijklmnopqrstuvwxyz6""abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4""abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2""abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";EXPECT_EQ(fs::createUniqueFile(Path270, FileDescriptor, TempPath),errc::invalid_argument);// Relative path < 247 chars, no problem.const char *Path216 ="abcdefghijklmnopqrstuvwxyz7abcdefghijklmnopqrstuvwxyz6""abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4""abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2""abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";ASSERT_NO_ERROR(fs::createTemporaryFile(Path216, "", TempPath));ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));#endif}TEST_F(FileSystemTest, TempFileCollisions) {SmallString<128> TestDirectory;ASSERT_NO_ERROR(fs::createUniqueDirectory("CreateUniqueFileTest", TestDirectory));FileRemover Cleanup(TestDirectory);SmallString<128> Model = TestDirectory;path::append(Model, "%.tmp");SmallString<128> Path;std::vector<fs::TempFile> TempFiles;auto TryCreateTempFile = [&]() {Expected<fs::TempFile> T = fs::TempFile::create(Model);if (T) {TempFiles.push_back(std::move(*T));return true;} else {logAllUnhandledErrors(T.takeError(), errs(),"Failed to create temporary file: ");return false;}};// Our single-character template allows for 16 unique names. Check that// calling TryCreateTempFile repeatedly results in 16 successes.// Because the test depends on random numbers, it could theoretically fail.// However, the probability of this happening is tiny: with 32 calls, each// of which will retry up to 128 times, to not get a given digit we would// have to fail at least 15 + 17 * 128 = 2191 attempts. The probability of// 2191 attempts not producing a given hexadecimal digit is// (1 - 1/16) ** 2191 or 3.88e-62.int Successes = 0;for (int i = 0; i < 32; ++i)if (TryCreateTempFile()) ++Successes;EXPECT_EQ(Successes, 16);for (fs::TempFile &T : TempFiles)cantFail(T.discard());}TEST_F(FileSystemTest, CreateDir) {ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "foo"));ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "foo"));ASSERT_EQ(fs::create_directory(Twine(TestDirectory) + "foo", false),errc::file_exists);ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "foo"));#ifdef LLVM_ON_UNIX// Set a 0000 umask so that we can test our directory permissions.mode_t OldUmask = ::umask(0000);fs::file_status Status;ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "baz500", false,fs::perms::owner_read | fs::perms::owner_exe));ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz500", Status));ASSERT_EQ(Status.permissions() & fs::perms::all_all,fs::perms::owner_read | fs::perms::owner_exe);ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "baz777", false,fs::perms::all_all));ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz777", Status));ASSERT_EQ(Status.permissions() & fs::perms::all_all, fs::perms::all_all);// Restore umask to be safe.::umask(OldUmask);#endif#ifdef _WIN32// Prove that create_directories() can handle a pathname > 248 characters,// which is the documented limit for CreateDirectory().// (248 is MAX_PATH subtracting room for an 8.3 filename.)// Generate a directory path guaranteed to fall into that range.size_t TmpLen = TestDirectory.size();const char *OneDir = "\\123456789";size_t OneDirLen = strlen(OneDir);ASSERT_LT(OneDirLen, 12U);size_t NLevels = ((248 - TmpLen) / OneDirLen) + 1;SmallString<260> LongDir(TestDirectory);for (size_t I = 0; I < NLevels; ++I)LongDir.append(OneDir);ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));ASSERT_EQ(fs::create_directories(Twine(LongDir), false),errc::file_exists);// Tidy up, "recursively" removing the directories.StringRef ThisDir(LongDir);for (size_t J = 0; J < NLevels; ++J) {ASSERT_NO_ERROR(fs::remove(ThisDir));ThisDir = path::parent_path(ThisDir);}// Also verify that paths with Unix separators are handled correctly.std::string LongPathWithUnixSeparators(TestDirectory.str());// Add at least one subdirectory to TestDirectory, and replace slashes with// backslashesdo {LongPathWithUnixSeparators.append("/DirNameWith19Charss");} while (LongPathWithUnixSeparators.size() < 260);std::replace(LongPathWithUnixSeparators.begin(),LongPathWithUnixSeparators.end(),'\\', '/');ASSERT_NO_ERROR(fs::create_directories(Twine(LongPathWithUnixSeparators)));// cleanupASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) +"/DirNameWith19Charss"));// Similarly for a relative pathname. Need to set the current directory to// TestDirectory so that the one we create ends up in the right place.char PreviousDir[260];size_t PreviousDirLen = ::GetCurrentDirectoryA(260, PreviousDir);ASSERT_GT(PreviousDirLen, 0U);ASSERT_LT(PreviousDirLen, 260U);ASSERT_NE(::SetCurrentDirectoryA(TestDirectory.c_str()), 0);LongDir.clear();// Generate a relative directory name with absolute length > 248.size_t LongDirLen = 249 - TestDirectory.size();LongDir.assign(LongDirLen, 'a');ASSERT_NO_ERROR(fs::create_directory(Twine(LongDir)));// While we're here, prove that .. and . handling works in these long paths.const char *DotDotDirs = "\\..\\.\\b";LongDir.append(DotDotDirs);ASSERT_NO_ERROR(fs::create_directory("b"));ASSERT_EQ(fs::create_directory(Twine(LongDir), false), errc::file_exists);// And clean up.ASSERT_NO_ERROR(fs::remove("b"));ASSERT_NO_ERROR(fs::remove(Twine(LongDir.substr(0, LongDir.size() - strlen(DotDotDirs)))));ASSERT_NE(::SetCurrentDirectoryA(PreviousDir), 0);#endif}TEST_F(FileSystemTest, DirectoryIteration) {std::error_code ec;for (fs::directory_iterator i(".", ec), e; i != e; i.increment(ec))ASSERT_NO_ERROR(ec);// Create a known hierarchy to recurse over.ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/recursive/a0/aa1"));ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/recursive/a0/ab1"));ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) +"/recursive/dontlookhere/da1"));ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/recursive/z0/za1"));ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/recursive/pop/p1"));typedef std::vector<std::string> v_t;v_t visited;for (fs::recursive_directory_iterator i(Twine(TestDirectory)+ "/recursive", ec), e; i != e; i.increment(ec)){ASSERT_NO_ERROR(ec);if (path::filename(i->path()) == "p1") {i.pop();// FIXME: recursive_directory_iterator should be more robust.if (i == e) break;}if (path::filename(i->path()) == "dontlookhere")i.no_push();visited.push_back(std::string(path::filename(i->path())));}v_t::const_iterator a0 = find(visited, "a0");v_t::const_iterator aa1 = find(visited, "aa1");v_t::const_iterator ab1 = find(visited, "ab1");v_t::const_iterator dontlookhere = find(visited, "dontlookhere");v_t::const_iterator da1 = find(visited, "da1");v_t::const_iterator z0 = find(visited, "z0");v_t::const_iterator za1 = find(visited, "za1");v_t::const_iterator pop = find(visited, "pop");v_t::const_iterator p1 = find(visited, "p1");// Make sure that each path was visited correctly.ASSERT_NE(a0, visited.end());ASSERT_NE(aa1, visited.end());ASSERT_NE(ab1, visited.end());ASSERT_NE(dontlookhere, visited.end());ASSERT_EQ(da1, visited.end()); // Not visited.ASSERT_NE(z0, visited.end());ASSERT_NE(za1, visited.end());ASSERT_NE(pop, visited.end());ASSERT_EQ(p1, visited.end()); // Not visited.// Make sure that parents were visited before children. No other ordering// guarantees can be made across siblings.ASSERT_LT(a0, aa1);ASSERT_LT(a0, ab1);ASSERT_LT(z0, za1);ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0/aa1"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0/ab1"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/dontlookhere/da1"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/dontlookhere"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/pop/p1"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/pop"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0/za1"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive"));// Test recursive_directory_iterator level()ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/reclevel/a/b/c"));fs::recursive_directory_iterator I(Twine(TestDirectory) + "/reclevel", ec), E;for (int l = 0; I != E; I.increment(ec), ++l) {ASSERT_NO_ERROR(ec);EXPECT_EQ(I.level(), l);}EXPECT_EQ(I, E);ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b/c"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a"));ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel"));}TEST_F(FileSystemTest, DirectoryNotExecutable) {ASSERT_EQ(fs::access(TestDirectory, sys::fs::AccessMode::Execute),errc::permission_denied);}#ifdef LLVM_ON_UNIXTEST_F(FileSystemTest, BrokenSymlinkDirectoryIteration) {// Create a known hierarchy to recurse over.ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/symlink"));ASSERT_NO_ERROR(fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/a"));ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/symlink/b/bb"));ASSERT_NO_ERROR(fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/ba"));ASSERT_NO_ERROR(fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/bc"));ASSERT_NO_ERROR(fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/c"));ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/symlink/d/dd/ddd"));ASSERT_NO_ERROR(fs::create_link(Twine(TestDirectory) + "/symlink/d/dd",Twine(TestDirectory) + "/symlink/d/da"));ASSERT_NO_ERROR(fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/e"));typedef std::vector<std::string> v_t;v_t VisitedNonBrokenSymlinks;v_t VisitedBrokenSymlinks;std::error_code ec;using testing::UnorderedElementsAre;using testing::UnorderedElementsAreArray;// Broken symbol links are expected to throw an error.for (fs::directory_iterator i(Twine(TestDirectory) + "/symlink", ec), e;i != e; i.increment(ec)) {ASSERT_NO_ERROR(ec);if (i->status().getError() ==std::make_error_code(std::errc::no_such_file_or_directory)) {VisitedBrokenSymlinks.push_back(std::string(path::filename(i->path())));continue;}VisitedNonBrokenSymlinks.push_back(std::string(path::filename(i->path())));}EXPECT_THAT(VisitedNonBrokenSymlinks, UnorderedElementsAre("b", "d"));VisitedNonBrokenSymlinks.clear();EXPECT_THAT(VisitedBrokenSymlinks, UnorderedElementsAre("a", "c", "e"));VisitedBrokenSymlinks.clear();// Broken symbol links are expected to throw an error.for (fs::recursive_directory_iterator i(Twine(TestDirectory) + "/symlink", ec), e; i != e; i.increment(ec)) {ASSERT_NO_ERROR(ec);if (i->status().getError() ==std::make_error_code(std::errc::no_such_file_or_directory)) {VisitedBrokenSymlinks.push_back(std::string(path::filename(i->path())));continue;}VisitedNonBrokenSymlinks.push_back(std::string(path::filename(i->path())));}EXPECT_THAT(VisitedNonBrokenSymlinks,UnorderedElementsAre("b", "bb", "d", "da", "dd", "ddd", "ddd"));VisitedNonBrokenSymlinks.clear();EXPECT_THAT(VisitedBrokenSymlinks,UnorderedElementsAre("a", "ba", "bc", "c", "e"));VisitedBrokenSymlinks.clear();for (fs::recursive_directory_iterator i(Twine(TestDirectory) + "/symlink", ec, /*follow_symlinks=*/false), e;i != e; i.increment(ec)) {ASSERT_NO_ERROR(ec);if (i->status().getError() ==std::make_error_code(std::errc::no_such_file_or_directory)) {VisitedBrokenSymlinks.push_back(std::string(path::filename(i->path())));continue;}VisitedNonBrokenSymlinks.push_back(std::string(path::filename(i->path())));}EXPECT_THAT(VisitedNonBrokenSymlinks,UnorderedElementsAreArray({"a", "b", "ba", "bb", "bc", "c", "d","da", "dd", "ddd", "e"}));VisitedNonBrokenSymlinks.clear();EXPECT_THAT(VisitedBrokenSymlinks, UnorderedElementsAre());VisitedBrokenSymlinks.clear();ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/symlink"));}#endif#ifdef _WIN32TEST_F(FileSystemTest, UTF8ToUTF16DirectoryIteration) {// The Windows filesystem support uses UTF-16 and converts paths from the// input UTF-8. The UTF-16 equivalent of the input path can be shorter in// length.// This test relies on TestDirectory not being so long such that MAX_PATH// would be exceeded (see widenPath). If that were the case, the UTF-16// path is likely to be longer than the input.const char *Pi = "\xcf\x80"; // UTF-8 lower case pi.std::string RootDir = (TestDirectory + "/" + Pi).str();// Create test directories.ASSERT_NO_ERROR(fs::create_directories(Twine(RootDir) + "/a"));ASSERT_NO_ERROR(fs::create_directories(Twine(RootDir) + "/b"));std::error_code EC;unsigned Count = 0;for (fs::directory_iterator I(Twine(RootDir), EC), E; I != E;I.increment(EC)) {ASSERT_NO_ERROR(EC);StringRef DirName = path::filename(I->path());EXPECT_TRUE(DirName == "a" || DirName == "b");++Count;}EXPECT_EQ(Count, 2U);ASSERT_NO_ERROR(fs::remove(Twine(RootDir) + "/a"));ASSERT_NO_ERROR(fs::remove(Twine(RootDir) + "/b"));ASSERT_NO_ERROR(fs::remove(Twine(RootDir)));}#endifTEST_F(FileSystemTest, Remove) {SmallString<64> BaseDir;SmallString<64> Paths[4];int fds[4];ASSERT_NO_ERROR(fs::createUniqueDirectory("fs_remove", BaseDir));ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/baz"));ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/buzz"));ASSERT_NO_ERROR(fs::createUniqueFile(Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[0], Paths[0]));ASSERT_NO_ERROR(fs::createUniqueFile(Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[1], Paths[1]));ASSERT_NO_ERROR(fs::createUniqueFile(Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[2], Paths[2]));ASSERT_NO_ERROR(fs::createUniqueFile(Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[3], Paths[3]));for (int fd : fds)::close(fd);EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/baz"));EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/buzz"));EXPECT_TRUE(fs::exists(Paths[0]));EXPECT_TRUE(fs::exists(Paths[1]));EXPECT_TRUE(fs::exists(Paths[2]));EXPECT_TRUE(fs::exists(Paths[3]));ASSERT_NO_ERROR(fs::remove_directories("D:/footest"));ASSERT_NO_ERROR(fs::remove_directories(BaseDir));ASSERT_FALSE(fs::exists(BaseDir));}#ifdef _WIN32TEST_F(FileSystemTest, CarriageReturn) {SmallString<128> FilePathname(TestDirectory);std::error_code EC;path::append(FilePathname, "test");{raw_fd_ostream File(FilePathname, EC, sys::fs::OF_TextWithCRLF);ASSERT_NO_ERROR(EC);File << '\n';}{auto Buf = MemoryBuffer::getFile(FilePathname.str());EXPECT_TRUE((bool)Buf);EXPECT_EQ(Buf.get()->getBuffer(), "\r\n");}{raw_fd_ostream File(FilePathname, EC, sys::fs::OF_None);ASSERT_NO_ERROR(EC);File << '\n';}{auto Buf = MemoryBuffer::getFile(FilePathname.str());EXPECT_TRUE((bool)Buf);EXPECT_EQ(Buf.get()->getBuffer(), "\n");}ASSERT_NO_ERROR(fs::remove(Twine(FilePathname)));}#endifTEST_F(FileSystemTest, Resize) {int FD;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));ASSERT_NO_ERROR(fs::resize_file(FD, 123));fs::file_status Status;ASSERT_NO_ERROR(fs::status(FD, Status));ASSERT_EQ(Status.getSize(), 123U);::close(FD);ASSERT_NO_ERROR(fs::remove(TempPath));}TEST_F(FileSystemTest, ResizeBeforeMapping) {// Create a temp file.int FD;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));ASSERT_NO_ERROR(fs::resize_file_before_mapping_readwrite(FD, 123));// Map in temp file. On Windows, fs::resize_file_before_mapping_readwrite is// a no-op and the mapping itself will resize the file.std::error_code EC;{fs::mapped_file_region mfr(fs::convertFDToNativeFile(FD),fs::mapped_file_region::readwrite, 123, 0, EC);ASSERT_NO_ERROR(EC);// Unmap temp file}// Check the size.fs::file_status Status;ASSERT_NO_ERROR(fs::status(FD, Status));ASSERT_EQ(Status.getSize(), 123U);::close(FD);ASSERT_NO_ERROR(fs::remove(TempPath));}TEST_F(FileSystemTest, MD5) {int FD;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));StringRef Data("abcdefghijklmnopqrstuvwxyz");ASSERT_EQ(write(FD, Data.data(), Data.size()), static_cast<ssize_t>(Data.size()));lseek(FD, 0, SEEK_SET);auto Hash = fs::md5_contents(FD);::close(FD);ASSERT_NO_ERROR(Hash.getError());EXPECT_STREQ("c3fcd3d76192e4007dfb496cca67e13b", Hash->digest().c_str());}TEST_F(FileSystemTest, FileMapping) {// Create a temp file.int FileDescriptor;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));unsigned Size = 4096;ASSERT_NO_ERROR(fs::resize_file_before_mapping_readwrite(FileDescriptor, Size));// Map in temp file and add some contentstd::error_code EC;StringRef Val("hello there");fs::mapped_file_region MaybeMFR;EXPECT_FALSE(MaybeMFR);{fs::mapped_file_region mfr(fs::convertFDToNativeFile(FileDescriptor),fs::mapped_file_region::readwrite, Size, 0, EC);ASSERT_NO_ERROR(EC);std::copy(Val.begin(), Val.end(), mfr.data());// Explicitly add a 0.mfr.data()[Val.size()] = 0;// Move it out of the scope and confirm mfr is reset.MaybeMFR = std::move(mfr);EXPECT_FALSE(mfr);#if !defined(NDEBUG) && GTEST_HAS_DEATH_TESTEXPECT_DEATH(mfr.data(), "Mapping failed but used anyway!");EXPECT_DEATH(mfr.size(), "Mapping failed but used anyway!");#endif}// Check that the moved-to region is still valid.EXPECT_EQ(Val, StringRef(MaybeMFR.data()));EXPECT_EQ(Size, MaybeMFR.size());// Unmap temp file.MaybeMFR.unmap();ASSERT_EQ(close(FileDescriptor), 0);// Map it back in read-only{int FD;EC = fs::openFileForRead(Twine(TempPath), FD);ASSERT_NO_ERROR(EC);fs::mapped_file_region mfr(fs::convertFDToNativeFile(FD),fs::mapped_file_region::readonly, Size, 0, EC);ASSERT_NO_ERROR(EC);// Verify contentEXPECT_EQ(StringRef(mfr.const_data()), Val);// Unmap temp filefs::mapped_file_region m(fs::convertFDToNativeFile(FD),fs::mapped_file_region::readonly, Size, 0, EC);ASSERT_NO_ERROR(EC);ASSERT_EQ(close(FD), 0);}ASSERT_NO_ERROR(fs::remove(TempPath));}TEST(Support, NormalizePath) {// Input, Expected Win, Expected Posixusing TestTuple = std::tuple<const char *, const char *, const char *>;std::vector<TestTuple> Tests;Tests.emplace_back("a", "a", "a");Tests.emplace_back("a/b", "a\\b", "a/b");Tests.emplace_back("a\\b", "a\\b", "a/b");Tests.emplace_back("a\\\\b", "a\\\\b", "a//b");Tests.emplace_back("\\a", "\\a", "/a");Tests.emplace_back("a\\", "a\\", "a/");Tests.emplace_back("a\\t", "a\\t", "a/t");for (auto &T : Tests) {SmallString<64> Win(std::get<0>(T));SmallString<64> Posix(Win);SmallString<64> WinSlash(Win);path::native(Win, path::Style::windows);path::native(Posix, path::Style::posix);path::native(WinSlash, path::Style::windows_slash);EXPECT_EQ(std::get<1>(T), Win);EXPECT_EQ(std::get<2>(T), Posix);EXPECT_EQ(std::get<2>(T), WinSlash);}for (auto &T : Tests) {SmallString<64> WinBackslash(std::get<0>(T));SmallString<64> Posix(WinBackslash);SmallString<64> WinSlash(WinBackslash);path::make_preferred(WinBackslash, path::Style::windows_backslash);path::make_preferred(Posix, path::Style::posix);path::make_preferred(WinSlash, path::Style::windows_slash);EXPECT_EQ(std::get<1>(T), WinBackslash);EXPECT_EQ(std::get<0>(T), Posix); // Posix remains unchanged hereEXPECT_EQ(std::get<2>(T), WinSlash);}#if defined(_WIN32)SmallString<64> PathHome;path::home_directory(PathHome);const char *Path7a = "~/aaa";SmallString<64> Path7(Path7a);path::native(Path7, path::Style::windows_backslash);EXPECT_TRUE(Path7.endswith("\\aaa"));EXPECT_TRUE(Path7.startswith(PathHome));EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));Path7 = Path7a;path::native(Path7, path::Style::windows_slash);EXPECT_TRUE(Path7.endswith("/aaa"));EXPECT_TRUE(Path7.startswith(PathHome));EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));const char *Path8a = "~";SmallString<64> Path8(Path8a);path::native(Path8);EXPECT_EQ(Path8, PathHome);const char *Path9a = "~aaa";SmallString<64> Path9(Path9a);path::native(Path9);EXPECT_EQ(Path9, "~aaa");const char *Path10a = "aaa/~/b";SmallString<64> Path10(Path10a);path::native(Path10, path::Style::windows_backslash);EXPECT_EQ(Path10, "aaa\\~\\b");#endif}TEST(Support, RemoveLeadingDotSlash) {StringRef Path1("././/foolz/wat");StringRef Path2("./////");Path1 = path::remove_leading_dotslash(Path1);EXPECT_EQ(Path1, "foolz/wat");Path2 = path::remove_leading_dotslash(Path2);EXPECT_EQ(Path2, "");}static std::string remove_dots(StringRef path, bool remove_dot_dot,path::Style style) {SmallString<256> buffer(path);path::remove_dots(buffer, remove_dot_dot, style);return std::string(buffer.str());}TEST(Support, RemoveDots) {EXPECT_EQ("foolz\\wat",remove_dots(".\\.\\\\foolz\\wat", false, path::Style::windows));EXPECT_EQ("", remove_dots(".\\\\\\\\\\", false, path::Style::windows));EXPECT_EQ("a\\..\\b\\c",remove_dots(".\\a\\..\\b\\c", false, path::Style::windows));EXPECT_EQ("b\\c", remove_dots(".\\a\\..\\b\\c", true, path::Style::windows));EXPECT_EQ("c", remove_dots(".\\.\\c", true, path::Style::windows));EXPECT_EQ("..\\a\\c",remove_dots("..\\a\\b\\..\\c", true, path::Style::windows));EXPECT_EQ("..\\..\\a\\c",remove_dots("..\\..\\a\\b\\..\\c", true, path::Style::windows));EXPECT_EQ("C:\\a\\c", remove_dots("C:\\foo\\bar//..\\..\\a\\c", true,path::Style::windows));EXPECT_EQ("C:\\bar",remove_dots("C:/foo/../bar", true, path::Style::windows));EXPECT_EQ("C:\\foo\\bar",remove_dots("C:/foo/bar", true, path::Style::windows));EXPECT_EQ("C:\\foo\\bar",remove_dots("C:/foo\\bar", true, path::Style::windows));EXPECT_EQ("\\", remove_dots("/", true, path::Style::windows));EXPECT_EQ("C:\\", remove_dots("C:/", true, path::Style::windows));// Some clients of remove_dots expect it to remove trailing slashes. Again,// this is emergent behavior that VFS relies on, and not inherently part of// the specification.EXPECT_EQ("C:\\foo\\bar",remove_dots("C:\\foo\\bar\\", true, path::Style::windows));EXPECT_EQ("/foo/bar",remove_dots("/foo/bar/", true, path::Style::posix));// A double separator is rewritten.EXPECT_EQ("C:\\foo\\bar",remove_dots("C:/foo//bar", true, path::Style::windows));SmallString<64> Path1(".\\.\\c");EXPECT_TRUE(path::remove_dots(Path1, true, path::Style::windows));EXPECT_EQ("c", Path1);EXPECT_EQ("foolz/wat",remove_dots("././/foolz/wat", false, path::Style::posix));EXPECT_EQ("", remove_dots("./////", false, path::Style::posix));EXPECT_EQ("a/../b/c", remove_dots("./a/../b/c", false, path::Style::posix));EXPECT_EQ("b/c", remove_dots("./a/../b/c", true, path::Style::posix));EXPECT_EQ("c", remove_dots("././c", true, path::Style::posix));EXPECT_EQ("../a/c", remove_dots("../a/b/../c", true, path::Style::posix));EXPECT_EQ("../../a/c",remove_dots("../../a/b/../c", true, path::Style::posix));EXPECT_EQ("/a/c", remove_dots("/../../a/c", true, path::Style::posix));EXPECT_EQ("/a/c",remove_dots("/../a/b//../././/c", true, path::Style::posix));EXPECT_EQ("/", remove_dots("/", true, path::Style::posix));// FIXME: Leaving behind this double leading slash seems like a bug.EXPECT_EQ("//foo/bar",remove_dots("//foo/bar/", true, path::Style::posix));SmallString<64> Path2("././c");EXPECT_TRUE(path::remove_dots(Path2, true, path::Style::posix));EXPECT_EQ("c", Path2);}TEST(Support, ReplacePathPrefix) {SmallString<64> Path1("/foo");SmallString<64> Path2("/old/foo");SmallString<64> Path3("/oldnew/foo");SmallString<64> Path4("C:\\old/foo\\bar");SmallString<64> OldPrefix("/old");SmallString<64> OldPrefixSep("/old/");SmallString<64> OldPrefixWin("c:/oLD/F");SmallString<64> NewPrefix("/new");SmallString<64> NewPrefix2("/longernew");SmallString<64> EmptyPrefix("");bool Found;SmallString<64> Path = Path1;Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);EXPECT_FALSE(Found);EXPECT_EQ(Path, "/foo");Path = Path2;Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/new/foo");Path = Path2;Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix2);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/longernew/foo");Path = Path1;Found = path::replace_path_prefix(Path, EmptyPrefix, NewPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/new/foo");Path = Path2;Found = path::replace_path_prefix(Path, OldPrefix, EmptyPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/foo");Path = Path2;Found = path::replace_path_prefix(Path, OldPrefixSep, EmptyPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "foo");Path = Path3;Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/newnew/foo");Path = Path3;Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix2);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/longernewnew/foo");Path = Path1;Found = path::replace_path_prefix(Path, EmptyPrefix, NewPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/new/foo");Path = OldPrefix;Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/new");Path = OldPrefixSep;Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/new/");Path = OldPrefix;Found = path::replace_path_prefix(Path, OldPrefixSep, NewPrefix);EXPECT_FALSE(Found);EXPECT_EQ(Path, "/old");Path = Path4;Found = path::replace_path_prefix(Path, OldPrefixWin, NewPrefix,path::Style::windows);EXPECT_TRUE(Found);EXPECT_EQ(Path, "/newoo\\bar");Path = Path4;Found = path::replace_path_prefix(Path, OldPrefixWin, NewPrefix,path::Style::posix);EXPECT_FALSE(Found);EXPECT_EQ(Path, "C:\\old/foo\\bar");}TEST_F(FileSystemTest, OpenFileForRead) {// Create a temp file.int FileDescriptor;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));FileRemover Cleanup(TempPath);// Make sure it exists.ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));// Open the file for readint FileDescriptor2;SmallString<64> ResultPath;ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor2,fs::OF_None, &ResultPath))// If we succeeded, check that the paths are the same (modulo case):if (!ResultPath.empty()) {// The paths returned by createTemporaryFile and getPathFromOpenFD// should reference the same file on disk.fs::UniqueID D1, D2;ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));ASSERT_EQ(D1, D2);}::close(FileDescriptor);::close(FileDescriptor2);#ifdef _WIN32// Since Windows Vista, file access time is not updated by default.// This is instead updated manually by openFileForRead.// https://blogs.technet.microsoft.com/filecab/2006/11/07/disabling-last-access-time-in-windows-vista-to-improve-ntfs-performance/// This part of the unit test is Windows specific as the updating of// access times can be disabled on Linux using /etc/fstab.// Set access time to UNIX epoch.ASSERT_NO_ERROR(sys::fs::openFileForWrite(Twine(TempPath), FileDescriptor,fs::CD_OpenExisting));TimePoint<> Epoch(std::chrono::milliseconds(0));ASSERT_NO_ERROR(fs::setLastAccessAndModificationTime(FileDescriptor, Epoch));::close(FileDescriptor);// Open the file and ensure access time is updated, when forced.ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor,fs::OF_UpdateAtime, &ResultPath));sys::fs::file_status Status;ASSERT_NO_ERROR(sys::fs::status(FileDescriptor, Status));auto FileAccessTime = Status.getLastAccessedTime();ASSERT_NE(Epoch, FileAccessTime);::close(FileDescriptor);// Ideally this test would include a case when ATime is not forced to update,// however the expected behaviour will differ depending on the configuration// of the Windows file system.#endif}static void createFileWithData(const Twine &Path, bool ShouldExistBefore,fs::CreationDisposition Disp, StringRef Data) {int FD;ASSERT_EQ(ShouldExistBefore, fs::exists(Path));ASSERT_NO_ERROR(fs::openFileForWrite(Path, FD, Disp));FileDescriptorCloser Closer(FD);ASSERT_TRUE(fs::exists(Path));ASSERT_EQ(Data.size(), (size_t)write(FD, Data.data(), Data.size()));}static void verifyFileContents(const Twine &Path, StringRef Contents) {auto Buffer = MemoryBuffer::getFile(Path);ASSERT_TRUE((bool)Buffer);StringRef Data = Buffer.get()->getBuffer();ASSERT_EQ(Data, Contents);}TEST_F(FileSystemTest, CreateNew) {int FD;Optional<FileDescriptorCloser> Closer;// Succeeds if the file does not exist.ASSERT_FALSE(fs::exists(NonExistantFile));ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));ASSERT_TRUE(fs::exists(NonExistantFile));FileRemover Cleanup(NonExistantFile);Closer.emplace(FD);// And creates a file of size 0.sys::fs::file_status Status;ASSERT_NO_ERROR(sys::fs::status(FD, Status));EXPECT_EQ(0ULL, Status.getSize());// Close this first, before trying to re-open the file.Closer.reset();// But fails if the file does exist.ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));}TEST_F(FileSystemTest, CreateAlways) {int FD;Optional<FileDescriptorCloser> Closer;// Succeeds if the file does not exist.ASSERT_FALSE(fs::exists(NonExistantFile));ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));Closer.emplace(FD);ASSERT_TRUE(fs::exists(NonExistantFile));FileRemover Cleanup(NonExistantFile);// And creates a file of size 0.uint64_t FileSize;ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));ASSERT_EQ(0ULL, FileSize);// If we write some data to it re-create it with CreateAlways, it succeeds and// truncates to 0 bytes.ASSERT_EQ(4, write(FD, "Test", 4));Closer.reset();ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));ASSERT_EQ(4ULL, FileSize);ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));Closer.emplace(FD);ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));ASSERT_EQ(0ULL, FileSize);}TEST_F(FileSystemTest, OpenExisting) {int FD;// Fails if the file does not exist.ASSERT_FALSE(fs::exists(NonExistantFile));ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));ASSERT_FALSE(fs::exists(NonExistantFile));// Make a dummy file now so that we can try again when the file does exist.createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");FileRemover Cleanup(NonExistantFile);uint64_t FileSize;ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));ASSERT_EQ(4ULL, FileSize);// If we re-create it with different data, it overwrites rather than// appending.createFileWithData(NonExistantFile, true, fs::CD_OpenExisting, "Buzz");verifyFileContents(NonExistantFile, "Buzz");}TEST_F(FileSystemTest, OpenAlways) {// Succeeds if the file does not exist.createFileWithData(NonExistantFile, false, fs::CD_OpenAlways, "Fizz");FileRemover Cleanup(NonExistantFile);uint64_t FileSize;ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));ASSERT_EQ(4ULL, FileSize);// Now re-open it and write again, verifying the contents get over-written.createFileWithData(NonExistantFile, true, fs::CD_OpenAlways, "Bu");verifyFileContents(NonExistantFile, "Buzz");}TEST_F(FileSystemTest, AppendSetsCorrectFileOffset) {fs::CreationDisposition Disps[] = {fs::CD_CreateAlways, fs::CD_OpenAlways,fs::CD_OpenExisting};// Write some data and re-open it with every possible disposition (this is a// hack that shouldn't work, but is left for compatibility. OF_Append// overrides// the specified disposition.for (fs::CreationDisposition Disp : Disps) {int FD;Optional<FileDescriptorCloser> Closer;createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");FileRemover Cleanup(NonExistantFile);uint64_t FileSize;ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));ASSERT_EQ(4ULL, FileSize);ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, Disp, fs::OF_Append));Closer.emplace(FD);ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));ASSERT_EQ(4ULL, FileSize);ASSERT_EQ(4, write(FD, "Buzz", 4));Closer.reset();verifyFileContents(NonExistantFile, "FizzBuzz");}}static void verifyRead(int FD, StringRef Data, bool ShouldSucceed) {std::vector<char> Buffer;Buffer.resize(Data.size());int Result = ::read(FD, Buffer.data(), Buffer.size());if (ShouldSucceed) {ASSERT_EQ((size_t)Result, Data.size());ASSERT_EQ(Data, StringRef(Buffer.data(), Buffer.size()));} else {ASSERT_EQ(-1, Result);ASSERT_EQ(EBADF, errno);}}static void verifyWrite(int FD, StringRef Data, bool ShouldSucceed) {int Result = ::write(FD, Data.data(), Data.size());if (ShouldSucceed)ASSERT_EQ((size_t)Result, Data.size());else {ASSERT_EQ(-1, Result);ASSERT_EQ(EBADF, errno);}}TEST_F(FileSystemTest, ReadOnlyFileCantWrite) {createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");FileRemover Cleanup(NonExistantFile);int FD;ASSERT_NO_ERROR(fs::openFileForRead(NonExistantFile, FD));FileDescriptorCloser Closer(FD);verifyWrite(FD, "Buzz", false);verifyRead(FD, "Fizz", true);}TEST_F(FileSystemTest, WriteOnlyFileCantRead) {createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");FileRemover Cleanup(NonExistantFile);int FD;ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));FileDescriptorCloser Closer(FD);verifyRead(FD, "Fizz", false);verifyWrite(FD, "Buzz", true);}TEST_F(FileSystemTest, ReadWriteFileCanReadOrWrite) {createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");FileRemover Cleanup(NonExistantFile);int FD;ASSERT_NO_ERROR(fs::openFileForReadWrite(NonExistantFile, FD,fs::CD_OpenExisting, fs::OF_None));FileDescriptorCloser Closer(FD);verifyRead(FD, "Fizz", true);verifyWrite(FD, "Buzz", true);}TEST_F(FileSystemTest, readNativeFile) {createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "01234");FileRemover Cleanup(NonExistantFile);const auto &Read = [&](size_t ToRead) -> Expected<std::string> {std::string Buf(ToRead, '?');Expected<fs::file_t> FD = fs::openNativeFileForRead(NonExistantFile);if (!FD)return FD.takeError();auto Close = make_scope_exit([&] { fs::closeFile(*FD); });if (Expected<size_t> BytesRead = fs::readNativeFile(*FD, makeMutableArrayRef(&*Buf.begin(), Buf.size())))return Buf.substr(0, *BytesRead);elsereturn BytesRead.takeError();};EXPECT_THAT_EXPECTED(Read(5), HasValue("01234"));EXPECT_THAT_EXPECTED(Read(3), HasValue("012"));EXPECT_THAT_EXPECTED(Read(6), HasValue("01234"));}TEST_F(FileSystemTest, readNativeFileToEOF) {constexpr StringLiteral Content = "0123456789";createFileWithData(NonExistantFile, false, fs::CD_CreateNew, Content);FileRemover Cleanup(NonExistantFile);const auto &Read = [&](SmallVectorImpl<char> &V,Optional<ssize_t> ChunkSize) {Expected<fs::file_t> FD = fs::openNativeFileForRead(NonExistantFile);if (!FD)return FD.takeError();auto Close = make_scope_exit([&] { fs::closeFile(*FD); });if (ChunkSize)return fs::readNativeFileToEOF(*FD, V, *ChunkSize);return fs::readNativeFileToEOF(*FD, V);};// Check basic operation.{SmallString<0> NoSmall;SmallString<fs::DefaultReadChunkSize + Content.size()> StaysSmall;SmallVectorImpl<char> *Vectors[] = {static_cast<SmallVectorImpl<char> *>(&NoSmall),static_cast<SmallVectorImpl<char> *>(&StaysSmall),};for (SmallVectorImpl<char> *V : Vectors) {ASSERT_THAT_ERROR(Read(*V, None), Succeeded());ASSERT_EQ(Content, StringRef(V->begin(), V->size()));}ASSERT_EQ(fs::DefaultReadChunkSize + Content.size(), StaysSmall.capacity());// Check appending.{constexpr StringLiteral Prefix = "prefix-";for (SmallVectorImpl<char> *V : Vectors) {V->assign(Prefix.begin(), Prefix.end());ASSERT_THAT_ERROR(Read(*V, None), Succeeded());ASSERT_EQ((Prefix + Content).str(), StringRef(V->begin(), V->size()));}}}// Check that the chunk size (if specified) is respected.SmallString<Content.size() + 5> SmallChunks;ASSERT_THAT_ERROR(Read(SmallChunks, 5), Succeeded());ASSERT_EQ(SmallChunks, Content);ASSERT_EQ(Content.size() + 5, SmallChunks.capacity());}TEST_F(FileSystemTest, readNativeFileSlice) {createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "01234");FileRemover Cleanup(NonExistantFile);Expected<fs::file_t> FD = fs::openNativeFileForRead(NonExistantFile);ASSERT_THAT_EXPECTED(FD, Succeeded());auto Close = make_scope_exit([&] { fs::closeFile(*FD); });const auto &Read = [&](size_t Offset,size_t ToRead) -> Expected<std::string> {std::string Buf(ToRead, '?');if (Expected<size_t> BytesRead = fs::readNativeFileSlice(*FD, makeMutableArrayRef(&*Buf.begin(), Buf.size()), Offset))return Buf.substr(0, *BytesRead);elsereturn BytesRead.takeError();};EXPECT_THAT_EXPECTED(Read(0, 5), HasValue("01234"));EXPECT_THAT_EXPECTED(Read(0, 3), HasValue("012"));EXPECT_THAT_EXPECTED(Read(2, 3), HasValue("234"));EXPECT_THAT_EXPECTED(Read(0, 6), HasValue("01234"));EXPECT_THAT_EXPECTED(Read(2, 6), HasValue("234"));EXPECT_THAT_EXPECTED(Read(5, 5), HasValue(""));}TEST_F(FileSystemTest, is_local) {bool TestDirectoryIsLocal;ASSERT_NO_ERROR(fs::is_local(TestDirectory, TestDirectoryIsLocal));EXPECT_EQ(TestDirectoryIsLocal, fs::is_local(TestDirectory));int FD;SmallString<128> TempPath;ASSERT_NO_ERROR(fs::createUniqueFile(Twine(TestDirectory) + "/temp", FD, TempPath));FileRemover Cleanup(TempPath);// Make sure it exists.ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));bool TempFileIsLocal;ASSERT_NO_ERROR(fs::is_local(FD, TempFileIsLocal));EXPECT_EQ(TempFileIsLocal, fs::is_local(FD));::close(FD);// Expect that the file and its parent directory are equally local or equally// remote.EXPECT_EQ(TestDirectoryIsLocal, TempFileIsLocal);}TEST_F(FileSystemTest, getUmask) {#ifdef _WIN32EXPECT_EQ(fs::getUmask(), 0U) << "Should always be 0 on Windows.";#elseunsigned OldMask = ::umask(0022);unsigned CurrentMask = fs::getUmask();EXPECT_EQ(CurrentMask, 0022U)<< "getUmask() didn't return previously set umask()";EXPECT_EQ(::umask(OldMask), mode_t(0022U))<< "getUmask() may have changed umask()";#endif}TEST_F(FileSystemTest, RespectUmask) {#ifndef _WIN32unsigned OldMask = ::umask(0022);int FD;SmallString<128> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));fs::perms AllRWE = static_cast<fs::perms>(0777);ASSERT_NO_ERROR(fs::setPermissions(TempPath, AllRWE));ErrorOr<fs::perms> Perms = fs::getPermissions(TempPath);ASSERT_TRUE(!!Perms);EXPECT_EQ(Perms.get(), AllRWE) << "Should have ignored umask by default";ASSERT_NO_ERROR(fs::setPermissions(TempPath, AllRWE));Perms = fs::getPermissions(TempPath);ASSERT_TRUE(!!Perms);EXPECT_EQ(Perms.get(), AllRWE) << "Should have ignored umask";ASSERT_NO_ERROR(fs::setPermissions(FD, static_cast<fs::perms>(AllRWE & ~fs::getUmask())));Perms = fs::getPermissions(TempPath);ASSERT_TRUE(!!Perms);EXPECT_EQ(Perms.get(), static_cast<fs::perms>(0755))<< "Did not respect umask";(void)::umask(0057);ASSERT_NO_ERROR(fs::setPermissions(FD, static_cast<fs::perms>(AllRWE & ~fs::getUmask())));Perms = fs::getPermissions(TempPath);ASSERT_TRUE(!!Perms);EXPECT_EQ(Perms.get(), static_cast<fs::perms>(0720))<< "Did not respect umask";(void)::umask(OldMask);(void)::close(FD);#endif}TEST_F(FileSystemTest, set_current_path) {SmallString<128> path;ASSERT_NO_ERROR(fs::current_path(path));ASSERT_NE(TestDirectory, path);struct RestorePath {SmallString<128> path;RestorePath(const SmallString<128> &path) : path(path) {}~RestorePath() { fs::set_current_path(path); }} restore_path(path);ASSERT_NO_ERROR(fs::set_current_path(TestDirectory));ASSERT_NO_ERROR(fs::current_path(path));fs::UniqueID D1, D2;ASSERT_NO_ERROR(fs::getUniqueID(TestDirectory, D1));ASSERT_NO_ERROR(fs::getUniqueID(path, D2));ASSERT_EQ(D1, D2) << "D1: " << TestDirectory << "\nD2: " << path;}TEST_F(FileSystemTest, permissions) {int FD;SmallString<64> TempPath;ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));FileRemover Cleanup(TempPath);// Make sure it exists.ASSERT_TRUE(fs::exists(Twine(TempPath)));auto CheckPermissions = [&](fs::perms Expected) {ErrorOr<fs::perms> Actual = fs::getPermissions(TempPath);return Actual && *Actual == Expected;};std::error_code NoError;EXPECT_EQ(fs::setPermissions(TempPath, fs::all_all), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::all_exe), NoError);EXPECT_TRUE(CheckPermissions(fs::all_read | fs::all_exe));#if defined(_WIN32)fs::perms ReadOnly = fs::all_read | fs::all_exe;EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe |fs::set_gid_on_exe |fs::sticky_bit),NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, ReadOnly | fs::set_uid_on_exe |fs::set_gid_on_exe |fs::sticky_bit),NoError);EXPECT_TRUE(CheckPermissions(ReadOnly));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError);EXPECT_TRUE(CheckPermissions(fs::all_all));#elseEXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError);EXPECT_TRUE(CheckPermissions(fs::no_perms));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError);EXPECT_TRUE(CheckPermissions(fs::owner_read));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError);EXPECT_TRUE(CheckPermissions(fs::owner_write));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError);EXPECT_TRUE(CheckPermissions(fs::owner_exe));EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError);EXPECT_TRUE(CheckPermissions(fs::owner_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError);EXPECT_TRUE(CheckPermissions(fs::group_read));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError);EXPECT_TRUE(CheckPermissions(fs::group_write));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError);EXPECT_TRUE(CheckPermissions(fs::group_exe));EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError);EXPECT_TRUE(CheckPermissions(fs::group_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError);EXPECT_TRUE(CheckPermissions(fs::others_read));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError);EXPECT_TRUE(CheckPermissions(fs::others_write));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError);EXPECT_TRUE(CheckPermissions(fs::others_exe));EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError);EXPECT_TRUE(CheckPermissions(fs::others_all));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError);EXPECT_TRUE(CheckPermissions(fs::all_read));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError);EXPECT_TRUE(CheckPermissions(fs::all_write));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError);EXPECT_TRUE(CheckPermissions(fs::all_exe));EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError);EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe));EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError);EXPECT_TRUE(CheckPermissions(fs::set_gid_on_exe));// Modern BSDs require root to set the sticky bit on files.// AIX and Solaris without root will mask off (i.e., lose) the sticky bit// on files.#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \!defined(_AIX) && !(defined(__sun__) && defined(__svr4__))EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError);EXPECT_TRUE(CheckPermissions(fs::sticky_bit));EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe |fs::set_gid_on_exe |fs::sticky_bit),NoError);EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe | fs::set_gid_on_exe |fs::sticky_bit));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::set_uid_on_exe |fs::set_gid_on_exe |fs::sticky_bit),NoError);EXPECT_TRUE(CheckPermissions(fs::all_read | fs::set_uid_on_exe |fs::set_gid_on_exe | fs::sticky_bit));EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError);EXPECT_TRUE(CheckPermissions(fs::all_perms));#endif // !FreeBSD && !NetBSD && !OpenBSD && !AIXEXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms & ~fs::sticky_bit),NoError);EXPECT_TRUE(CheckPermissions(fs::all_perms & ~fs::sticky_bit));#endif}#ifdef _WIN32TEST_F(FileSystemTest, widenPath) {const std::wstring LongPathPrefix(L"\\\\?\\");// Test that the length limit is checked against the UTF-16 length and not the// UTF-8 length.std::string Input("C:\\foldername\\");const std::string Pi("\xcf\x80"); // UTF-8 lower case pi.// Add Pi up to the MAX_PATH limit.const size_t NumChars = MAX_PATH - Input.size() - 1;for (size_t i = 0; i < NumChars; ++i)Input += Pi;// Check that UTF-8 length already exceeds MAX_PATH.EXPECT_GT(Input.size(), (size_t)MAX_PATH);SmallVector<wchar_t, MAX_PATH + 16> Result;ASSERT_NO_ERROR(windows::widenPath(Input, Result));// Result should not start with the long path prefix.EXPECT_TRUE(std::wmemcmp(Result.data(), LongPathPrefix.c_str(),LongPathPrefix.size()) != 0);EXPECT_EQ(Result.size(), (size_t)MAX_PATH - 1);// Add another Pi to exceed the MAX_PATH limit.Input += Pi;// Construct the expected result.SmallVector<wchar_t, MAX_PATH + 16> Expected;ASSERT_NO_ERROR(windows::UTF8ToUTF16(Input, Expected));Expected.insert(Expected.begin(), LongPathPrefix.begin(),LongPathPrefix.end());ASSERT_NO_ERROR(windows::widenPath(Input, Result));EXPECT_EQ(Result, Expected);// Pass a path with forward slashes, check that it ends up with// backslashes when widened with the long path prefix.SmallString<MAX_PATH + 16> InputForward(Input);path::make_preferred(InputForward, path::Style::windows_slash);ASSERT_NO_ERROR(windows::widenPath(InputForward, Result));EXPECT_EQ(Result, Expected);// Pass a path which has the long path prefix prepended originally, but// which is short enough to not require the long path prefix. If such a// path is passed with forward slashes, make sure it gets normalized to// backslashes.SmallString<MAX_PATH + 16> PrefixedPath("\\\\?\\C:\\foldername");ASSERT_NO_ERROR(windows::UTF8ToUTF16(PrefixedPath, Expected));// Mangle the input to forward slashes.path::make_preferred(PrefixedPath, path::Style::windows_slash);ASSERT_NO_ERROR(windows::widenPath(PrefixedPath, Result));EXPECT_EQ(Result, Expected);// A short path with an inconsistent prefix is passed through as-is; this// is a degenerate case that we currently don't care about handling.PrefixedPath.assign("/\\?/C:/foldername");ASSERT_NO_ERROR(windows::UTF8ToUTF16(PrefixedPath, Expected));ASSERT_NO_ERROR(windows::widenPath(PrefixedPath, Result));EXPECT_EQ(Result, Expected);// Test that UNC paths are handled correctly.const std::string ShareName("\\\\sharename\\");const std::string FileName("\\filename");// Initialize directory name so that the input is within the MAX_PATH limit.const char DirChar = 'x';std::string DirName(MAX_PATH - ShareName.size() - FileName.size() - 1,DirChar);Input = ShareName + DirName + FileName;ASSERT_NO_ERROR(windows::widenPath(Input, Result));// Result should not start with the long path prefix.EXPECT_TRUE(std::wmemcmp(Result.data(), LongPathPrefix.c_str(),LongPathPrefix.size()) != 0);EXPECT_EQ(Result.size(), (size_t)MAX_PATH - 1);// Extend the directory name so the input exceeds the MAX_PATH limit.DirName += DirChar;Input = ShareName + DirName + FileName;// Construct the expected result.ASSERT_NO_ERROR(windows::UTF8ToUTF16(StringRef(Input).substr(2), Expected));const std::wstring UNCPrefix(LongPathPrefix + L"UNC\\");Expected.insert(Expected.begin(), UNCPrefix.begin(), UNCPrefix.end());ASSERT_NO_ERROR(windows::widenPath(Input, Result));EXPECT_EQ(Result, Expected);// Check that Unix separators are handled correctly.std::replace(Input.begin(), Input.end(), '\\', '/');ASSERT_NO_ERROR(windows::widenPath(Input, Result));EXPECT_EQ(Result, Expected);// Check the removal of "dots".Input = ShareName + DirName + "\\.\\foo\\.\\.." + FileName;ASSERT_NO_ERROR(windows::widenPath(Input, Result));EXPECT_EQ(Result, Expected);}#endif#ifdef _WIN32// Windows refuses lock request if file region is already locked by the same// process. POSIX system in this case updates the existing lock.TEST_F(FileSystemTest, FileLocker) {using namespace std::chrono;int FD;std::error_code EC;SmallString<64> TempPath;EC = fs::createTemporaryFile("test", "temp", FD, TempPath);ASSERT_NO_ERROR(EC);FileRemover Cleanup(TempPath);raw_fd_ostream Stream(TempPath, EC);EC = fs::tryLockFile(FD);ASSERT_NO_ERROR(EC);EC = fs::unlockFile(FD);ASSERT_NO_ERROR(EC);if (auto L = Stream.lock()) {ASSERT_ERROR(fs::tryLockFile(FD));ASSERT_NO_ERROR(L->unlock());ASSERT_NO_ERROR(fs::tryLockFile(FD));ASSERT_NO_ERROR(fs::unlockFile(FD));} else {ADD_FAILURE();handleAllErrors(L.takeError(), [&](ErrorInfoBase &EIB) {});}ASSERT_NO_ERROR(fs::tryLockFile(FD));ASSERT_NO_ERROR(fs::unlockFile(FD));{Expected<fs::FileLocker> L1 = Stream.lock();ASSERT_THAT_EXPECTED(L1, Succeeded());raw_fd_ostream Stream2(FD, false);Expected<fs::FileLocker> L2 = Stream2.tryLockFor(250ms);ASSERT_THAT_EXPECTED(L2, Failed());ASSERT_NO_ERROR(L1->unlock());Expected<fs::FileLocker> L3 = Stream.tryLockFor(0ms);ASSERT_THAT_EXPECTED(L3, Succeeded());}ASSERT_NO_ERROR(fs::tryLockFile(FD));ASSERT_NO_ERROR(fs::unlockFile(FD));}#endifTEST_F(FileSystemTest, CopyFile) {unittest::TempDir RootTestDirectory("CopyFileTest", /*Unique=*/true);SmallVector<std::string> Data;SmallVector<SmallString<128>> Sources;for (int I = 0, E = 3; I != E; ++I) {Data.push_back(Twine(I).str());Sources.emplace_back(RootTestDirectory.path());path::append(Sources.back(), "source" + Data.back() + ".txt");createFileWithData(Sources.back(), /*ShouldExistBefore=*/false,fs::CD_CreateNew, Data.back());}// Copy the first file to a non-existing file.SmallString<128> Destination(RootTestDirectory.path());path::append(Destination, "destination");ASSERT_FALSE(fs::exists(Destination));fs::copy_file(Sources[0], Destination);verifyFileContents(Destination, Data[0]);// Copy the second file to an existing file.fs::copy_file(Sources[1], Destination);verifyFileContents(Destination, Data[1]);// Note: The remaining logic is targeted at a potential failure case related// to file cloning and symlinks on Darwin. On Windows, fs::create_link() does// not return success here so the test is skipped.#if !defined(_WIN32)// Set up a symlink to the third file.SmallString<128> Symlink(RootTestDirectory.path());path::append(Symlink, "symlink");ASSERT_NO_ERROR(fs::create_link(path::filename(Sources[2]), Symlink));verifyFileContents(Symlink, Data[2]);// fs::getUniqueID() should follow symlinks. Otherwise, this isn't good test// coverage.fs::UniqueID SymlinkID;fs::UniqueID Data2ID;ASSERT_NO_ERROR(fs::getUniqueID(Symlink, SymlinkID));ASSERT_NO_ERROR(fs::getUniqueID(Sources[2], Data2ID));ASSERT_EQ(SymlinkID, Data2ID);// Copy the third file through the symlink.fs::copy_file(Symlink, Destination);verifyFileContents(Destination, Data[2]);// Confirm the destination is not a link to the original file, and not a// symlink.bool IsDestinationSymlink;ASSERT_NO_ERROR(fs::is_symlink_file(Destination, IsDestinationSymlink));ASSERT_FALSE(IsDestinationSymlink);fs::UniqueID DestinationID;ASSERT_NO_ERROR(fs::getUniqueID(Destination, DestinationID));ASSERT_NE(SymlinkID, DestinationID);#endif}} // anonymous namespace
//===- llvm/unittest/Support/ParallelTest.cpp -----------------------------===////// 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////===----------------------------------------------------------------------===//////// \file/// Parallel.h unit tests./////===----------------------------------------------------------------------===//#include "llvm/Support/Parallel.h"#include "gtest/gtest.h"#include <array>#include <random>uint32_t array[1024 * 1024];using namespace llvm;// Tests below are hanging up on mingw. Investigating.#if !defined(__MINGW32__)TEST(Parallel, sort) {std::mt19937 randEngine;std::uniform_int_distribution<uint32_t> dist;for (auto &i : array)i = dist(randEngine);parallelSort(std::begin(array), std::end(array));ASSERT_TRUE(llvm::is_sorted(array));}TEST(Parallel, parallel_for) {// We need to test the case with a TaskSize > 1. We are white-box testing// here. The TaskSize is calculated as (End - Begin) / 1024 at the time of// writing.uint32_t range[2050];std::fill(range, range + 2050, 1);parallelFor(0, 2049, [&range](size_t I) { ++range[I]; });uint32_t expected[2049];std::fill(expected, expected + 2049, 2);ASSERT_TRUE(std::equal(range, range + 2049, expected));// Check that we don't write past the end of the requested range.ASSERT_EQ(range[2049], 1u);}TEST(Parallel, TransformReduce) {// Sum an empty list, check that it works.auto identity = [](uint32_t v) { return v; };uint32_t sum = parallelTransformReduce(ArrayRef<uint32_t>(), 0U,std::plus<uint32_t>(), identity);EXPECT_EQ(sum, 0U);// Sum the lengths of these strings in parallel.const char *strs[] = {"a", "ab", "abc", "abcd", "abcde", "abcdef"};size_t lenSum =parallelTransformReduce(strs, static_cast<size_t>(0), std::plus<size_t>(),[](const char *s) { return strlen(s); });EXPECT_EQ(lenSum, static_cast<size_t>(21));// Check that we handle non-divisible task sizes as above.uint32_t range[2050];std::fill(std::begin(range), std::end(range), 1);sum = parallelTransformReduce(range, 0U, std::plus<uint32_t>(), identity);EXPECT_EQ(sum, 2050U);std::fill(std::begin(range), std::end(range), 2);sum = parallelTransformReduce(range, 0U, std::plus<uint32_t>(), identity);EXPECT_EQ(sum, 4100U);// Avoid one large task.uint32_t range2[3060];std::fill(std::begin(range2), std::end(range2), 1);sum = parallelTransformReduce(range2, 0U, std::plus<uint32_t>(), identity);EXPECT_EQ(sum, 3060U);}TEST(Parallel, ForEachError) {int nums[] = {1, 2, 3, 4, 5, 6};Error e = parallelForEachError(nums, [](int v) -> Error {if ((v & 1) == 0)return createStringError(std::errc::invalid_argument, "asdf");return Error::success();});EXPECT_TRUE(e.isA<ErrorList>());std::string errText = toString(std::move(e));EXPECT_EQ(errText, std::string("asdf\nasdf\nasdf"));}#endif
//=== - unittest/Support/OptimizedStructLayoutTest.cpp - Layout tests -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/OptimizedStructLayout.h"#include "gtest/gtest.h"using namespace llvm;namespace {class LayoutTest {struct Field {uint64_t Size;Align Alignment;uint64_t ForcedOffset;uint64_t ExpectedOffset;};SmallVector<Field, 16> Fields;bool Verified = false;public:LayoutTest() {}LayoutTest(const LayoutTest &) = delete;LayoutTest &operator=(const LayoutTest &) = delete;~LayoutTest() { assert(Verified); }LayoutTest &flexible(uint64_t Size, uint64_t Alignment,uint64_t ExpectedOffset) {Fields.push_back({Size, Align(Alignment),OptimizedStructLayoutField::FlexibleOffset, ExpectedOffset});return *this;}LayoutTest &fixed(uint64_t Size, uint64_t Alignment, uint64_t Offset) {Fields.push_back({Size, Align(Alignment), Offset, Offset});return *this;}void verify(uint64_t ExpectedSize, uint64_t ExpectedAlignment) {SmallVector<OptimizedStructLayoutField, 8> LayoutFields;LayoutFields.reserve(Fields.size());for (auto &F : Fields)LayoutFields.emplace_back(&F, F.Size, F.Alignment, F.ForcedOffset);auto SizeAndAlign = performOptimizedStructLayout(LayoutFields);EXPECT_EQ(SizeAndAlign.first, ExpectedSize);EXPECT_EQ(SizeAndAlign.second, Align(ExpectedAlignment));for (auto &LF : LayoutFields) {auto &F = *static_cast<const Field *>(LF.Id);EXPECT_EQ(LF.Offset, F.ExpectedOffset);}Verified = true;}};}TEST(OptimizedStructLayoutTest, Basic) {LayoutTest().flexible(12, 4, 8).flexible(8, 8, 0).flexible(4, 4, 20).verify(24, 8);}TEST(OptimizedStructLayoutTest, OddSize) {LayoutTest().flexible(8, 8, 16).flexible(4, 4, 12).flexible(1, 1, 10).flexible(10, 8, 0).verify(24, 8);}TEST(OptimizedStructLayoutTest, Gaps) {LayoutTest().fixed(4, 4, 8).fixed(4, 4, 16).flexible(4, 4, 0).flexible(4, 4, 4).flexible(4, 4, 12).flexible(4, 4, 20).verify(24, 4);}TEST(OptimizedStructLayoutTest, Greed) {// The greedy algorithm doesn't find the optimal layout here, which// would be to put the 5-byte field at the end.LayoutTest().fixed(4, 4, 8).flexible(5, 4, 0).flexible(4, 4, 12).flexible(4, 4, 16).flexible(4, 4, 20).verify(24, 4);}TEST(OptimizedStructLayoutTest, Jagged) {LayoutTest().flexible(1, 2, 18).flexible(13, 8, 0).flexible(3, 2, 14).verify(19, 8);}TEST(OptimizedStructLayoutTest, GardenPath) {// The 4-byte-aligned field is our highest priority, but the less-aligned// fields keep leaving the end offset mis-aligned.LayoutTest().fixed(7, 4, 0).flexible(4, 4, 44).flexible(6, 1, 7).flexible(5, 1, 13).flexible(7, 2, 18).flexible(4, 1, 25).flexible(4, 1, 29).flexible(1, 1, 33).flexible(4, 2, 34).flexible(4, 2, 38).flexible(2, 2, 42).flexible(2, 2, 48).verify(50, 4);}// PR 51131TEST(OptimizedStructLayoutTest, HighAlignment) {// Handle the case where a flexible field has such a high alignment// requirement that aligning LastEnd to it gives an offset past the// end of the gap before the next fixed-alignment field.LayoutTest().fixed(8, 8, 0).fixed(8, 8, 8).fixed(64, 64, 64).flexible(1, 1, 16).flexible(1, 1, 17).flexible(4, 128, 128).flexible(1, 1, 18).flexible(1, 1, 19).verify(132, 128);}
//===- llvm/unittest/Support/NativeFormatTests.cpp - formatting tests -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "llvm/Support/NativeFormatting.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <type_traits>using namespace llvm;namespace {template <typename T> std::string format_number(T N, IntegerStyle Style) {std::string S;llvm::raw_string_ostream Str(S);write_integer(Str, N, 0, Style);Str.flush();return S;}std::string format_number(uint64_t N, HexPrintStyle Style,Optional<size_t> Width = None) {std::string S;llvm::raw_string_ostream Str(S);write_hex(Str, N, Style, Width);Str.flush();return S;}std::string format_number(double D, FloatStyle Style,Optional<size_t> Precision = None) {std::string S;llvm::raw_string_ostream Str(S);write_double(Str, D, Style, Precision);Str.flush();return S;}// Test basic number formatting with various styles and default width and// precision.TEST(NativeFormatTest, BasicIntegerTests) {// Simple integers with no decimal.EXPECT_EQ("0", format_number(0, IntegerStyle::Integer));EXPECT_EQ("2425", format_number(2425, IntegerStyle::Integer));EXPECT_EQ("-2425", format_number(-2425, IntegerStyle::Integer));EXPECT_EQ("0", format_number(0LL, IntegerStyle::Integer));EXPECT_EQ("257257257235709",format_number(257257257235709LL, IntegerStyle::Integer));EXPECT_EQ("-257257257235709",format_number(-257257257235709LL, IntegerStyle::Integer));// Number formatting.EXPECT_EQ("0", format_number(0, IntegerStyle::Number));EXPECT_EQ("2,425", format_number(2425, IntegerStyle::Number));EXPECT_EQ("-2,425", format_number(-2425, IntegerStyle::Number));EXPECT_EQ("257,257,257,235,709",format_number(257257257235709LL, IntegerStyle::Number));EXPECT_EQ("-257,257,257,235,709",format_number(-257257257235709LL, IntegerStyle::Number));// Hex formatting.// lower case, prefix.EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixLower));EXPECT_EQ("0xbeef", format_number(0xbeefLL, HexPrintStyle::PrefixLower));EXPECT_EQ("0xdeadbeef",format_number(0xdeadbeefLL, HexPrintStyle::PrefixLower));// upper-case, prefix.EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixUpper));EXPECT_EQ("0xBEEF", format_number(0xbeefLL, HexPrintStyle::PrefixUpper));EXPECT_EQ("0xDEADBEEF",format_number(0xdeadbeefLL, HexPrintStyle::PrefixUpper));// lower-case, no prefixEXPECT_EQ("0", format_number(0, HexPrintStyle::Lower));EXPECT_EQ("beef", format_number(0xbeefLL, HexPrintStyle::Lower));EXPECT_EQ("deadbeef", format_number(0xdeadbeefLL, HexPrintStyle::Lower));// upper-case, no prefix.EXPECT_EQ("0", format_number(0, HexPrintStyle::Upper));EXPECT_EQ("BEEF", format_number(0xbeef, HexPrintStyle::Upper));EXPECT_EQ("DEADBEEF", format_number(0xdeadbeef, HexPrintStyle::Upper));}// Test basic floating point formatting with various styles and default width// and precision.TEST(NativeFormatTest, BasicFloatingPointTests) {// DoubleEXPECT_EQ("0.000000e+00", format_number(0.0, FloatStyle::Exponent));EXPECT_EQ("-0.000000e+00", format_number(-0.0, FloatStyle::Exponent));EXPECT_EQ("1.100000e+00", format_number(1.1, FloatStyle::Exponent));EXPECT_EQ("1.100000E+00", format_number(1.1, FloatStyle::ExponentUpper));// Default precision is 2 for floating points.EXPECT_EQ("1.10", format_number(1.1, FloatStyle::Fixed));EXPECT_EQ("1.34", format_number(1.34, FloatStyle::Fixed));EXPECT_EQ("1.34", format_number(1.344, FloatStyle::Fixed));EXPECT_EQ("1.35", format_number(1.346, FloatStyle::Fixed));}// Test common boundary cases and min/max conditions.TEST(NativeFormatTest, BoundaryTests) {// Min and max.EXPECT_EQ("18446744073709551615",format_number(UINT64_MAX, IntegerStyle::Integer));EXPECT_EQ("9223372036854775807",format_number(INT64_MAX, IntegerStyle::Integer));EXPECT_EQ("-9223372036854775808",format_number(INT64_MIN, IntegerStyle::Integer));EXPECT_EQ("4294967295", format_number(UINT32_MAX, IntegerStyle::Integer));EXPECT_EQ("2147483647", format_number(INT32_MAX, IntegerStyle::Integer));EXPECT_EQ("-2147483648", format_number(INT32_MIN, IntegerStyle::Integer));EXPECT_EQ("nan", format_number(std::numeric_limits<double>::quiet_NaN(),FloatStyle::Fixed));EXPECT_EQ("INF", format_number(std::numeric_limits<double>::infinity(),FloatStyle::Fixed));EXPECT_EQ("-INF", format_number(-std::numeric_limits<double>::infinity(),FloatStyle::Fixed));}TEST(NativeFormatTest, HexTests) {// Test hex formatting with different widths and precisions.// Width less than the value should print the full value anyway.EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixLower, 0));EXPECT_EQ("0xabcde", format_number(0xABCDE, HexPrintStyle::PrefixLower, 3));// Precision greater than the value should pad with 0s.// TODO: The prefix should not be counted in the precision. But unfortunately// it is and we have to live with it unless we fix all existing users of// prefixed hex formatting.EXPECT_EQ("0x000", format_number(0, HexPrintStyle::PrefixLower, 5));EXPECT_EQ("0x0abcde", format_number(0xABCDE, HexPrintStyle::PrefixLower, 8));EXPECT_EQ("00000", format_number(0, HexPrintStyle::Lower, 5));EXPECT_EQ("000abcde", format_number(0xABCDE, HexPrintStyle::Lower, 8));// Try printing more digits than can fit in a uint64.EXPECT_EQ("0x00000000000000abcde",format_number(0xABCDE, HexPrintStyle::PrefixLower, 21));}TEST(NativeFormatTest, IntegerTests) {EXPECT_EQ("-10", format_number(-10, IntegerStyle::Integer));EXPECT_EQ("-100", format_number(-100, IntegerStyle::Integer));EXPECT_EQ("-1000", format_number(-1000, IntegerStyle::Integer));EXPECT_EQ("-1234567890", format_number(-1234567890, IntegerStyle::Integer));EXPECT_EQ("10", format_number(10, IntegerStyle::Integer));EXPECT_EQ("100", format_number(100, IntegerStyle::Integer));EXPECT_EQ("1000", format_number(1000, IntegerStyle::Integer));EXPECT_EQ("1234567890", format_number(1234567890, IntegerStyle::Integer));}TEST(NativeFormatTest, CommaTests) {EXPECT_EQ("0", format_number(0, IntegerStyle::Number));EXPECT_EQ("10", format_number(10, IntegerStyle::Number));EXPECT_EQ("100", format_number(100, IntegerStyle::Number));EXPECT_EQ("1,000", format_number(1000, IntegerStyle::Number));EXPECT_EQ("1,234,567,890", format_number(1234567890, IntegerStyle::Number));EXPECT_EQ("-10", format_number(-10, IntegerStyle::Number));EXPECT_EQ("-100", format_number(-100, IntegerStyle::Number));EXPECT_EQ("-1,000", format_number(-1000, IntegerStyle::Number));EXPECT_EQ("-1,234,567,890", format_number(-1234567890, IntegerStyle::Number));}}
//===- llvm/unittest/Support/AllocatorTest.cpp - BumpPtrAllocator tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Memory.h"#include "llvm/Support/Process.h"#include "gtest/gtest.h"#include <cassert>#include <cstdlib>#if defined(__NetBSD__)// clang-format off#include <sys/param.h>#include <sys/types.h>#include <sys/sysctl.h>#include <err.h>#include <unistd.h>// clang-format on#endifusing namespace llvm;using namespace sys;namespace {bool IsMPROTECT() {#if defined(__NetBSD__)int mib[3];int paxflags;size_t len = sizeof(paxflags);mib[0] = CTL_PROC;mib[1] = getpid();mib[2] = PROC_PID_PAXFLAGS;if (sysctl(mib, 3, &paxflags, &len, NULL, 0) != 0)err(EXIT_FAILURE, "sysctl");return !!(paxflags & CTL_PROC_PAXFLAGS_MPROTECT);#elif defined(__APPLE__) && defined(__aarch64__)return true;#elsereturn false;#endif}class MappedMemoryTest : public ::testing::TestWithParam<unsigned> {public:MappedMemoryTest() {Flags = GetParam();PageSize = sys::Process::getPageSizeEstimate();}protected:// Adds RW flags to permit testing of the resulting memoryunsigned getTestableEquivalent(unsigned RequestedFlags) {switch (RequestedFlags) {case Memory::MF_READ:case Memory::MF_WRITE:case Memory::MF_READ|Memory::MF_WRITE:return Memory::MF_READ|Memory::MF_WRITE;case Memory::MF_READ|Memory::MF_EXEC:case Memory::MF_READ|Memory::MF_WRITE|Memory::MF_EXEC:case Memory::MF_EXEC:return Memory::MF_READ|Memory::MF_WRITE|Memory::MF_EXEC;}// Default in case values are added to the enum, as required by some compilersreturn Memory::MF_READ|Memory::MF_WRITE;}// Returns true if the memory blocks overlapbool doesOverlap(MemoryBlock M1, MemoryBlock M2) {if (M1.base() == M2.base())return true;if (M1.base() > M2.base())return (unsigned char *)M2.base() + M2.allocatedSize() > M1.base();return (unsigned char *)M1.base() + M1.allocatedSize() > M2.base();}unsigned Flags;size_t PageSize;};// MPROTECT prevents W+X mmaps#define CHECK_UNSUPPORTED() \do { \if ((Flags & Memory::MF_WRITE) && (Flags & Memory::MF_EXEC) && \IsMPROTECT()) \return; \} while (0)TEST_P(MappedMemoryTest, AllocAndRelease) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock M1 = Memory::allocateMappedMemory(sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(sizeof(int), M1.allocatedSize());EXPECT_FALSE(Memory::releaseMappedMemory(M1));}TEST_P(MappedMemoryTest, AllocAndReleaseHuge) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock M1 = Memory::allocateMappedMemory(sizeof(int), nullptr, Flags | Memory::MF_HUGE_HINT, EC);EXPECT_EQ(std::error_code(), EC);// Test large/huge memory pages. In the worst case, 4kb pages should be// returned, if large pages aren't available.EXPECT_NE((void *)nullptr, M1.base());EXPECT_LE(sizeof(int), M1.allocatedSize());EXPECT_FALSE(Memory::releaseMappedMemory(M1));}TEST_P(MappedMemoryTest, MultipleAllocAndRelease) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock M1 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M2 = Memory::allocateMappedMemory(64, nullptr, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M3 = Memory::allocateMappedMemory(32, nullptr, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(16U, M1.allocatedSize());EXPECT_NE((void*)nullptr, M2.base());EXPECT_LE(64U, M2.allocatedSize());EXPECT_NE((void*)nullptr, M3.base());EXPECT_LE(32U, M3.allocatedSize());EXPECT_FALSE(doesOverlap(M1, M2));EXPECT_FALSE(doesOverlap(M2, M3));EXPECT_FALSE(doesOverlap(M1, M3));EXPECT_FALSE(Memory::releaseMappedMemory(M1));EXPECT_FALSE(Memory::releaseMappedMemory(M3));MemoryBlock M4 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M4.base());EXPECT_LE(16U, M4.allocatedSize());EXPECT_FALSE(Memory::releaseMappedMemory(M4));EXPECT_FALSE(Memory::releaseMappedMemory(M2));}TEST_P(MappedMemoryTest, BasicWrite) {// This test applies only to readable and writeable combinationsif (Flags &&!((Flags & Memory::MF_READ) && (Flags & Memory::MF_WRITE)))return;CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock M1 = Memory::allocateMappedMemory(sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(sizeof(int), M1.allocatedSize());int *a = (int*)M1.base();*a = 1;EXPECT_EQ(1, *a);EXPECT_FALSE(Memory::releaseMappedMemory(M1));}TEST_P(MappedMemoryTest, MultipleWrite) {// This test applies only to readable and writeable combinationsif (Flags &&!((Flags & Memory::MF_READ) && (Flags & Memory::MF_WRITE)))return;CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock M1 = Memory::allocateMappedMemory(sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M2 = Memory::allocateMappedMemory(8 * sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M3 = Memory::allocateMappedMemory(4 * sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);EXPECT_FALSE(doesOverlap(M1, M2));EXPECT_FALSE(doesOverlap(M2, M3));EXPECT_FALSE(doesOverlap(M1, M3));EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(1U * sizeof(int), M1.allocatedSize());EXPECT_NE((void*)nullptr, M2.base());EXPECT_LE(8U * sizeof(int), M2.allocatedSize());EXPECT_NE((void*)nullptr, M3.base());EXPECT_LE(4U * sizeof(int), M3.allocatedSize());int *x = (int*)M1.base();*x = 1;int *y = (int*)M2.base();for (int i = 0; i < 8; i++) {y[i] = i;}int *z = (int*)M3.base();*z = 42;EXPECT_EQ(1, *x);EXPECT_EQ(7, y[7]);EXPECT_EQ(42, *z);EXPECT_FALSE(Memory::releaseMappedMemory(M1));EXPECT_FALSE(Memory::releaseMappedMemory(M3));MemoryBlock M4 = Memory::allocateMappedMemory(64 * sizeof(int), nullptr,Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M4.base());EXPECT_LE(64U * sizeof(int), M4.allocatedSize());x = (int*)M4.base();*x = 4;EXPECT_EQ(4, *x);EXPECT_FALSE(Memory::releaseMappedMemory(M4));// Verify that M2 remains unaffected by other activityfor (int i = 0; i < 8; i++) {EXPECT_EQ(i, y[i]);}EXPECT_FALSE(Memory::releaseMappedMemory(M2));}TEST_P(MappedMemoryTest, EnabledWrite) {// MPROTECT prevents W+X, and since this test always adds W we need// to block any variant with X.if ((Flags & Memory::MF_EXEC) && IsMPROTECT())return;std::error_code EC;MemoryBlock M1 = Memory::allocateMappedMemory(2 * sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M2 = Memory::allocateMappedMemory(8 * sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M3 = Memory::allocateMappedMemory(4 * sizeof(int), nullptr, Flags,EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(2U * sizeof(int), M1.allocatedSize());EXPECT_NE((void*)nullptr, M2.base());EXPECT_LE(8U * sizeof(int), M2.allocatedSize());EXPECT_NE((void*)nullptr, M3.base());EXPECT_LE(4U * sizeof(int), M3.allocatedSize());EXPECT_FALSE(Memory::protectMappedMemory(M1, getTestableEquivalent(Flags)));EXPECT_FALSE(Memory::protectMappedMemory(M2, getTestableEquivalent(Flags)));EXPECT_FALSE(Memory::protectMappedMemory(M3, getTestableEquivalent(Flags)));EXPECT_FALSE(doesOverlap(M1, M2));EXPECT_FALSE(doesOverlap(M2, M3));EXPECT_FALSE(doesOverlap(M1, M3));int *x = (int*)M1.base();*x = 1;int *y = (int*)M2.base();for (unsigned int i = 0; i < 8; i++) {y[i] = i;}int *z = (int*)M3.base();*z = 42;EXPECT_EQ(1, *x);EXPECT_EQ(7, y[7]);EXPECT_EQ(42, *z);EXPECT_FALSE(Memory::releaseMappedMemory(M1));EXPECT_FALSE(Memory::releaseMappedMemory(M3));EXPECT_EQ(6, y[6]);MemoryBlock M4 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M4.base());EXPECT_LE(16U, M4.allocatedSize());EXPECT_EQ(std::error_code(),Memory::protectMappedMemory(M4, getTestableEquivalent(Flags)));x = (int*)M4.base();*x = 4;EXPECT_EQ(4, *x);EXPECT_FALSE(Memory::releaseMappedMemory(M4));EXPECT_FALSE(Memory::releaseMappedMemory(M2));}TEST_P(MappedMemoryTest, SuccessiveNear) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock M1 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M2 = Memory::allocateMappedMemory(64, &M1, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M3 = Memory::allocateMappedMemory(32, &M2, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(16U, M1.allocatedSize());EXPECT_NE((void*)nullptr, M2.base());EXPECT_LE(64U, M2.allocatedSize());EXPECT_NE((void*)nullptr, M3.base());EXPECT_LE(32U, M3.allocatedSize());EXPECT_FALSE(doesOverlap(M1, M2));EXPECT_FALSE(doesOverlap(M2, M3));EXPECT_FALSE(doesOverlap(M1, M3));EXPECT_FALSE(Memory::releaseMappedMemory(M1));EXPECT_FALSE(Memory::releaseMappedMemory(M3));EXPECT_FALSE(Memory::releaseMappedMemory(M2));}TEST_P(MappedMemoryTest, DuplicateNear) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock Near((void*)(3*PageSize), 16);MemoryBlock M1 = Memory::allocateMappedMemory(16, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M2 = Memory::allocateMappedMemory(64, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M3 = Memory::allocateMappedMemory(32, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(16U, M1.allocatedSize());EXPECT_NE((void*)nullptr, M2.base());EXPECT_LE(64U, M2.allocatedSize());EXPECT_NE((void*)nullptr, M3.base());EXPECT_LE(32U, M3.allocatedSize());EXPECT_FALSE(Memory::releaseMappedMemory(M1));EXPECT_FALSE(Memory::releaseMappedMemory(M3));EXPECT_FALSE(Memory::releaseMappedMemory(M2));}TEST_P(MappedMemoryTest, ZeroNear) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock Near(nullptr, 0);MemoryBlock M1 = Memory::allocateMappedMemory(16, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M2 = Memory::allocateMappedMemory(64, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M3 = Memory::allocateMappedMemory(32, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(16U, M1.allocatedSize());EXPECT_NE((void*)nullptr, M2.base());EXPECT_LE(64U, M2.allocatedSize());EXPECT_NE((void*)nullptr, M3.base());EXPECT_LE(32U, M3.allocatedSize());EXPECT_FALSE(doesOverlap(M1, M2));EXPECT_FALSE(doesOverlap(M2, M3));EXPECT_FALSE(doesOverlap(M1, M3));EXPECT_FALSE(Memory::releaseMappedMemory(M1));EXPECT_FALSE(Memory::releaseMappedMemory(M3));EXPECT_FALSE(Memory::releaseMappedMemory(M2));}TEST_P(MappedMemoryTest, ZeroSizeNear) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock Near((void*)(4*PageSize), 0);MemoryBlock M1 = Memory::allocateMappedMemory(16, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M2 = Memory::allocateMappedMemory(64, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);MemoryBlock M3 = Memory::allocateMappedMemory(32, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(16U, M1.allocatedSize());EXPECT_NE((void*)nullptr, M2.base());EXPECT_LE(64U, M2.allocatedSize());EXPECT_NE((void*)nullptr, M3.base());EXPECT_LE(32U, M3.allocatedSize());EXPECT_FALSE(doesOverlap(M1, M2));EXPECT_FALSE(doesOverlap(M2, M3));EXPECT_FALSE(doesOverlap(M1, M3));EXPECT_FALSE(Memory::releaseMappedMemory(M1));EXPECT_FALSE(Memory::releaseMappedMemory(M3));EXPECT_FALSE(Memory::releaseMappedMemory(M2));}TEST_P(MappedMemoryTest, UnalignedNear) {CHECK_UNSUPPORTED();std::error_code EC;MemoryBlock Near((void*)(2*PageSize+5), 0);MemoryBlock M1 = Memory::allocateMappedMemory(15, &Near, Flags, EC);EXPECT_EQ(std::error_code(), EC);EXPECT_NE((void*)nullptr, M1.base());EXPECT_LE(sizeof(int), M1.allocatedSize());EXPECT_FALSE(Memory::releaseMappedMemory(M1));}// Note that Memory::MF_WRITE is not supported exclusively across// operating systems and architectures and can imply MF_READ|MF_WRITEunsigned MemoryFlags[] = {Memory::MF_READ,Memory::MF_WRITE,Memory::MF_READ|Memory::MF_WRITE,Memory::MF_EXEC,Memory::MF_READ|Memory::MF_EXEC,Memory::MF_READ|Memory::MF_WRITE|Memory::MF_EXEC};INSTANTIATE_TEST_SUITE_P(AllocationTests, MappedMemoryTest,::testing::ValuesIn(MemoryFlags));} // anonymous namespace
//===- llvm/unittest/Support/MemoryBufferTest.cpp - MemoryBuffer tests ----===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for the MemoryBuffer support class.////===----------------------------------------------------------------------===//#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/SmallVectorMemoryBuffer.h"#include "llvm/ADT/ScopeExit.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/FileUtilities.h"#include "llvm/Support/Process.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#if LLVM_ENABLE_THREADS#include <thread>#endif#if LLVM_ON_UNIX#include <unistd.h>#endif#if _WIN32#include <windows.h>#endifusing namespace llvm;#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}#define ASSERT_ERROR(x) \if (!x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return a failure error code.\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \}namespace {class MemoryBufferTest : public testing::Test {protected:MemoryBufferTest(): data("this is some data"){ }void SetUp() override {}/// Common testing for different modes of getOpenFileSlice./// Creates a temporary file with known contents, and uses/// MemoryBuffer::getOpenFileSlice to map it./// If \p Reopen is true, the file is closed after creating and reopened/// anew before using MemoryBuffer.void testGetOpenFileSlice(bool Reopen);typedef std::unique_ptr<MemoryBuffer> OwningBuffer;std::string data;};TEST_F(MemoryBufferTest, get) {// Default name and null-terminator flagOwningBuffer MB1(MemoryBuffer::getMemBuffer(data));EXPECT_NE(nullptr, MB1.get());// RequiresNullTerminator = falseOwningBuffer MB2(MemoryBuffer::getMemBuffer(data, "one", false));EXPECT_NE(nullptr, MB2.get());// RequiresNullTerminator = trueOwningBuffer MB3(MemoryBuffer::getMemBuffer(data, "two", true));EXPECT_NE(nullptr, MB3.get());// verify all 3 buffers point to the same addressEXPECT_EQ(MB1->getBufferStart(), MB2->getBufferStart());EXPECT_EQ(MB2->getBufferStart(), MB3->getBufferStart());// verify the original data is unmodified after deleting the buffersMB1.reset();MB2.reset();MB3.reset();EXPECT_EQ("this is some data", data);}TEST_F(MemoryBufferTest, getOpenFile) {int FD;SmallString<64> TestPath;ASSERT_EQ(sys::fs::createTemporaryFile("MemoryBufferTest_getOpenFile", "temp",FD, TestPath),std::error_code());FileRemover Cleanup(TestPath);raw_fd_ostream OF(FD, /*shouldClose*/ true);OF << "12345678";OF.close();{Expected<sys::fs::file_t> File = sys::fs::openNativeFileForRead(TestPath);ASSERT_THAT_EXPECTED(File, Succeeded());auto OnExit =make_scope_exit([&] { ASSERT_NO_ERROR(sys::fs::closeFile(*File)); });ErrorOr<OwningBuffer> MB = MemoryBuffer::getOpenFile(*File, TestPath, 6);ASSERT_NO_ERROR(MB.getError());EXPECT_EQ("123456", MB.get()->getBuffer());}{Expected<sys::fs::file_t> File = sys::fs::openNativeFileForWrite(TestPath, sys::fs::CD_OpenExisting, sys::fs::OF_None);ASSERT_THAT_EXPECTED(File, Succeeded());auto OnExit =make_scope_exit([&] { ASSERT_NO_ERROR(sys::fs::closeFile(*File)); });ASSERT_ERROR(MemoryBuffer::getOpenFile(*File, TestPath, 6).getError());}}TEST_F(MemoryBufferTest, NullTerminator4K) {// Test that a file with size that is a multiple of the page size can be null// terminated correctly by MemoryBuffer.int TestFD;SmallString<64> TestPath;sys::fs::createTemporaryFile("MemoryBufferTest_NullTerminator4K", "temp",TestFD, TestPath);FileRemover Cleanup(TestPath);raw_fd_ostream OF(TestFD, true, /*unbuffered=*/true);for (unsigned i = 0; i < 4096 / 16; ++i) {OF << "0123456789abcdef";}OF.close();ErrorOr<OwningBuffer> MB = MemoryBuffer::getFile(TestPath.c_str());std::error_code EC = MB.getError();ASSERT_FALSE(EC);const char *BufData = MB.get()->getBufferStart();EXPECT_EQ('f', BufData[4095]);EXPECT_EQ('\0', BufData[4096]);}TEST_F(MemoryBufferTest, copy) {// copy with no nameOwningBuffer MBC1(MemoryBuffer::getMemBufferCopy(data));EXPECT_NE(nullptr, MBC1.get());// copy with a nameOwningBuffer MBC2(MemoryBuffer::getMemBufferCopy(data, "copy"));EXPECT_NE(nullptr, MBC2.get());// verify the two copies do not point to the same placeEXPECT_NE(MBC1->getBufferStart(), MBC2->getBufferStart());}#if LLVM_ENABLE_THREADSTEST_F(MemoryBufferTest, createFromPipe) {sys::fs::file_t pipes[2];#if LLVM_ON_UNIXASSERT_EQ(::pipe(pipes), 0) << strerror(errno);#elseASSERT_TRUE(::CreatePipe(&pipes[0], &pipes[1], nullptr, 0))<< ::GetLastError();#endifauto ReadCloser = make_scope_exit([&] { sys::fs::closeFile(pipes[0]); });std::thread Writer([&] {auto WriteCloser = make_scope_exit([&] { sys::fs::closeFile(pipes[1]); });for (unsigned i = 0; i < 5; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(10));#if LLVM_ON_UNIXASSERT_EQ(::write(pipes[1], "foo", 3), 3) << strerror(errno);#elseDWORD Written;ASSERT_TRUE(::WriteFile(pipes[1], "foo", 3, &Written, nullptr))<< ::GetLastError();ASSERT_EQ(Written, 3u);#endif}});ErrorOr<OwningBuffer> MB =MemoryBuffer::getOpenFile(pipes[0], "pipe", /*FileSize*/ -1);Writer.join();ASSERT_NO_ERROR(MB.getError());EXPECT_EQ(MB.get()->getBuffer(), "foofoofoofoofoo");}#endifTEST_F(MemoryBufferTest, make_new) {// 0-sized bufferOwningBuffer Zero(WritableMemoryBuffer::getNewUninitMemBuffer(0));EXPECT_NE(nullptr, Zero.get());// uninitialized buffer with no nameOwningBuffer One(WritableMemoryBuffer::getNewUninitMemBuffer(321));EXPECT_NE(nullptr, One.get());// uninitialized buffer with nameOwningBuffer Two(WritableMemoryBuffer::getNewUninitMemBuffer(123, "bla"));EXPECT_NE(nullptr, Two.get());// 0-initialized buffer with no nameOwningBuffer Three(WritableMemoryBuffer::getNewMemBuffer(321, data));EXPECT_NE(nullptr, Three.get());for (size_t i = 0; i < 321; ++i)EXPECT_EQ(0, Three->getBufferStart()[0]);// 0-initialized buffer with nameOwningBuffer Four(WritableMemoryBuffer::getNewMemBuffer(123, "zeros"));EXPECT_NE(nullptr, Four.get());for (size_t i = 0; i < 123; ++i)EXPECT_EQ(0, Four->getBufferStart()[0]);// uninitialized buffer with rollover sizeOwningBuffer Five(WritableMemoryBuffer::getNewUninitMemBuffer(SIZE_MAX, "huge"));EXPECT_EQ(nullptr, Five.get());}void MemoryBufferTest::testGetOpenFileSlice(bool Reopen) {// Test that MemoryBuffer::getOpenFile works properly when no null// terminator is requested and the size is large enough to trigger// the usage of memory mapping.int TestFD;SmallString<64> TestPath;// Create a temporary file and write data into it.sys::fs::createTemporaryFile("prefix", "temp", TestFD, TestPath);FileRemover Cleanup(TestPath);// OF is responsible for closing the file; If the file is not// reopened, it will be unbuffered so that the results are// immediately visible through the fd.raw_fd_ostream OF(TestFD, true, !Reopen);for (int i = 0; i < 60000; ++i) {OF << "0123456789";}if (Reopen) {OF.close();EXPECT_FALSE(sys::fs::openFileForRead(TestPath.c_str(), TestFD));}ErrorOr<OwningBuffer> Buf = MemoryBuffer::getOpenFileSlice(sys::fs::convertFDToNativeFile(TestFD), TestPath.c_str(),40000, // Size80000 // Offset);std::error_code EC = Buf.getError();EXPECT_FALSE(EC);StringRef BufData = Buf.get()->getBuffer();EXPECT_EQ(BufData.size(), 40000U);EXPECT_EQ(BufData[0], '0');EXPECT_EQ(BufData[9], '9');}TEST_F(MemoryBufferTest, getOpenFileNoReopen) {testGetOpenFileSlice(false);}TEST_F(MemoryBufferTest, getOpenFileReopened) {testGetOpenFileSlice(true);}TEST_F(MemoryBufferTest, slice) {// Create a file that is six pages long with different data on each page.int FD;SmallString<64> TestPath;sys::fs::createTemporaryFile("MemoryBufferTest_Slice", "temp", FD, TestPath);FileRemover Cleanup(TestPath);raw_fd_ostream OF(FD, true, /*unbuffered=*/true);for (unsigned i = 0; i < 0x2000 / 8; ++i) {OF << "12345678";}for (unsigned i = 0; i < 0x2000 / 8; ++i) {OF << "abcdefgh";}for (unsigned i = 0; i < 0x2000 / 8; ++i) {OF << "ABCDEFGH";}OF.close();// Try offset of one page.ErrorOr<OwningBuffer> MB = MemoryBuffer::getFileSlice(TestPath.str(),0x4000, 0x1000);std::error_code EC = MB.getError();ASSERT_FALSE(EC);EXPECT_EQ(0x4000UL, MB.get()->getBufferSize());StringRef BufData = MB.get()->getBuffer();EXPECT_TRUE(BufData.substr(0x0000,8).equals("12345678"));EXPECT_TRUE(BufData.substr(0x0FF8,8).equals("12345678"));EXPECT_TRUE(BufData.substr(0x1000,8).equals("abcdefgh"));EXPECT_TRUE(BufData.substr(0x2FF8,8).equals("abcdefgh"));EXPECT_TRUE(BufData.substr(0x3000,8).equals("ABCDEFGH"));EXPECT_TRUE(BufData.substr(0x3FF8,8).equals("ABCDEFGH"));// Try non-page aligned.ErrorOr<OwningBuffer> MB2 = MemoryBuffer::getFileSlice(TestPath.str(),0x3000, 0x0800);EC = MB2.getError();ASSERT_FALSE(EC);EXPECT_EQ(0x3000UL, MB2.get()->getBufferSize());StringRef BufData2 = MB2.get()->getBuffer();EXPECT_TRUE(BufData2.substr(0x0000,8).equals("12345678"));EXPECT_TRUE(BufData2.substr(0x17F8,8).equals("12345678"));EXPECT_TRUE(BufData2.substr(0x1800,8).equals("abcdefgh"));EXPECT_TRUE(BufData2.substr(0x2FF8,8).equals("abcdefgh"));}TEST_F(MemoryBufferTest, writableSlice) {// Create a file initialized with some dataint FD;SmallString<64> TestPath;sys::fs::createTemporaryFile("MemoryBufferTest_WritableSlice", "temp", FD,TestPath);FileRemover Cleanup(TestPath);raw_fd_ostream OF(FD, true);for (unsigned i = 0; i < 0x1000; ++i)OF << "0123456789abcdef";OF.close();{auto MBOrError =WritableMemoryBuffer::getFileSlice(TestPath.str(), 0x6000, 0x2000);ASSERT_FALSE(MBOrError.getError());// Write some data. It should be mapped private, so that upon completion// the original file contents are not modified.WritableMemoryBuffer &MB = **MBOrError;ASSERT_EQ(0x6000u, MB.getBufferSize());char *Start = MB.getBufferStart();ASSERT_EQ(MB.getBufferEnd(), MB.getBufferStart() + MB.getBufferSize());::memset(Start, 'x', MB.getBufferSize());}auto MBOrError = MemoryBuffer::getFile(TestPath);ASSERT_FALSE(MBOrError.getError());auto &MB = **MBOrError;ASSERT_EQ(0x10000u, MB.getBufferSize());for (size_t i = 0; i < MB.getBufferSize(); i += 0x10)EXPECT_EQ("0123456789abcdef", MB.getBuffer().substr(i, 0x10)) << "i: " << i;}TEST_F(MemoryBufferTest, writeThroughFile) {// Create a file initialized with some dataint FD;SmallString<64> TestPath;sys::fs::createTemporaryFile("MemoryBufferTest_WriteThrough", "temp", FD,TestPath);FileRemover Cleanup(TestPath);raw_fd_ostream OF(FD, true);OF << "0123456789abcdef";OF.close();{auto MBOrError = WriteThroughMemoryBuffer::getFile(TestPath);ASSERT_FALSE(MBOrError.getError());// Write some data. It should be mapped readwrite, so that upon completion// the original file contents are modified.WriteThroughMemoryBuffer &MB = **MBOrError;ASSERT_EQ(16u, MB.getBufferSize());char *Start = MB.getBufferStart();ASSERT_EQ(MB.getBufferEnd(), MB.getBufferStart() + MB.getBufferSize());::memset(Start, 'x', MB.getBufferSize());}auto MBOrError = MemoryBuffer::getFile(TestPath);ASSERT_FALSE(MBOrError.getError());auto &MB = **MBOrError;ASSERT_EQ(16u, MB.getBufferSize());EXPECT_EQ("xxxxxxxxxxxxxxxx", MB.getBuffer());}TEST_F(MemoryBufferTest, mmapVolatileNoNull) {// Verify that `MemoryBuffer::getOpenFile` will use mmap when// `RequiresNullTerminator = false`, `IsVolatile = true`, and the file is// large enough to use mmap.//// This is done because Clang should use this mode to open module files, and// falling back to malloc for them causes a huge memory usage increase.int FD;SmallString<64> TestPath;ASSERT_NO_ERROR(sys::fs::createTemporaryFile("MemoryBufferTest_mmapVolatileNoNull", "temp", FD, TestPath));FileRemover Cleanup(TestPath);raw_fd_ostream OF(FD, true);// Create a file large enough to mmap. 4 pages should be enough.unsigned PageSize = sys::Process::getPageSizeEstimate();unsigned FileWrites = (PageSize * 4) / 8;for (unsigned i = 0; i < FileWrites; ++i)OF << "01234567";OF.close();Expected<sys::fs::file_t> File = sys::fs::openNativeFileForRead(TestPath);ASSERT_THAT_EXPECTED(File, Succeeded());auto OnExit =make_scope_exit([&] { ASSERT_NO_ERROR(sys::fs::closeFile(*File)); });auto MBOrError = MemoryBuffer::getOpenFile(*File, TestPath,/*FileSize=*/-1, /*RequiresNullTerminator=*/false, /*IsVolatile=*/true);ASSERT_NO_ERROR(MBOrError.getError())OwningBuffer MB = std::move(*MBOrError);EXPECT_EQ(MB->getBufferKind(), MemoryBuffer::MemoryBuffer_MMap);EXPECT_EQ(MB->getBufferSize(), std::size_t(FileWrites * 8));EXPECT_TRUE(MB->getBuffer().startswith("01234567"));}// Test that SmallVector without a null terminator gets one.TEST(SmallVectorMemoryBufferTest, WithoutNullTerminatorRequiresNullTerminator) {SmallString<0> Data("some data");SmallVectorMemoryBuffer MB(std::move(Data),/*RequiresNullTerminator=*/true);EXPECT_EQ(MB.getBufferSize(), 9u);EXPECT_EQ(MB.getBufferEnd()[0], '\0');}// Test that SmallVector with a null terminator keeps it.TEST(SmallVectorMemoryBufferTest, WithNullTerminatorRequiresNullTerminator) {SmallString<0> Data("some data");Data.push_back('\0');Data.pop_back();SmallVectorMemoryBuffer MB(std::move(Data),/*RequiresNullTerminator=*/true);EXPECT_EQ(MB.getBufferSize(), 9u);EXPECT_EQ(MB.getBufferEnd()[0], '\0');}} // namespace
//===- MemoryBufferRefTest.cpp - MemoryBufferRef tests --------------------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for the MemoryBufferRef support class.////===----------------------------------------------------------------------===//#include "llvm/Support/MemoryBufferRef.h"#include "llvm/Support/MemoryBuffer.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(MemoryBufferRefTest, construct) {std::unique_ptr<MemoryBuffer> MB(MemoryBuffer::getMemBuffer("data", "id"));MemoryBufferRef MBR(*MB);EXPECT_EQ(MB->getBufferStart(), MBR.getBufferStart());EXPECT_EQ(MB->getBufferIdentifier(), MBR.getBufferIdentifier());}TEST(MemoryBufferRefTest, compareEquals) {std::string Data = "data";std::unique_ptr<MemoryBuffer> MB(MemoryBuffer::getMemBuffer(Data, "id"));MemoryBufferRef Ref(*MB);MemoryBufferRef Empty;MemoryBufferRef NoIdentifier(MB->getBuffer(), "");MemoryBufferRef NoData("", MB->getBufferIdentifier());MemoryBufferRef Same(MB->getBuffer(), MB->getBufferIdentifier());EXPECT_NE(Empty, Ref);EXPECT_NE(NoIdentifier, Ref);EXPECT_NE(NoData, Ref);EXPECT_EQ(Same, Ref);// Confirm NE when content matches but pointer identity does not.std::unique_ptr<MemoryBuffer> Copy(MemoryBuffer::getMemBufferCopy(Data, "id"));MemoryBufferRef CopyRef(*Copy);EXPECT_EQ(Ref.getBuffer(), CopyRef.getBuffer());EXPECT_NE(Ref, CopyRef);}} // end namespace
//===- unittests/Support/MathExtrasTest.cpp - math utils tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/MathExtras.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(MathExtras, countTrailingZeros) {uint8_t Z8 = 0;uint16_t Z16 = 0;uint32_t Z32 = 0;uint64_t Z64 = 0;EXPECT_EQ(8u, countTrailingZeros(Z8));EXPECT_EQ(16u, countTrailingZeros(Z16));EXPECT_EQ(32u, countTrailingZeros(Z32));EXPECT_EQ(64u, countTrailingZeros(Z64));uint8_t NZ8 = 42;uint16_t NZ16 = 42;uint32_t NZ32 = 42;uint64_t NZ64 = 42;EXPECT_EQ(1u, countTrailingZeros(NZ8));EXPECT_EQ(1u, countTrailingZeros(NZ16));EXPECT_EQ(1u, countTrailingZeros(NZ32));EXPECT_EQ(1u, countTrailingZeros(NZ64));}TEST(MathExtras, countLeadingZeros) {uint8_t Z8 = 0;uint16_t Z16 = 0;uint32_t Z32 = 0;uint64_t Z64 = 0;EXPECT_EQ(8u, countLeadingZeros(Z8));EXPECT_EQ(16u, countLeadingZeros(Z16));EXPECT_EQ(32u, countLeadingZeros(Z32));EXPECT_EQ(64u, countLeadingZeros(Z64));uint8_t NZ8 = 42;uint16_t NZ16 = 42;uint32_t NZ32 = 42;uint64_t NZ64 = 42;EXPECT_EQ(2u, countLeadingZeros(NZ8));EXPECT_EQ(10u, countLeadingZeros(NZ16));EXPECT_EQ(26u, countLeadingZeros(NZ32));EXPECT_EQ(58u, countLeadingZeros(NZ64));EXPECT_EQ(8u, countLeadingZeros(0x00F000FFu));EXPECT_EQ(8u, countLeadingZeros(0x00F12345u));for (unsigned i = 0; i <= 30; ++i) {EXPECT_EQ(31 - i, countLeadingZeros(1u << i));}EXPECT_EQ(8u, countLeadingZeros(0x00F1234500F12345ULL));EXPECT_EQ(1u, countLeadingZeros(1ULL << 62));for (unsigned i = 0; i <= 62; ++i) {EXPECT_EQ(63 - i, countLeadingZeros(1ULL << i));}}TEST(MathExtras, onesMask) {EXPECT_EQ(0U, maskLeadingOnes<uint8_t>(0));EXPECT_EQ(0U, maskTrailingOnes<uint8_t>(0));EXPECT_EQ(0U, maskLeadingOnes<uint16_t>(0));EXPECT_EQ(0U, maskTrailingOnes<uint16_t>(0));EXPECT_EQ(0U, maskLeadingOnes<uint32_t>(0));EXPECT_EQ(0U, maskTrailingOnes<uint32_t>(0));EXPECT_EQ(0U, maskLeadingOnes<uint64_t>(0));EXPECT_EQ(0U, maskTrailingOnes<uint64_t>(0));EXPECT_EQ(0x00000003U, maskTrailingOnes<uint32_t>(2U));EXPECT_EQ(0xC0000000U, maskLeadingOnes<uint32_t>(2U));EXPECT_EQ(0x000007FFU, maskTrailingOnes<uint32_t>(11U));EXPECT_EQ(0xFFE00000U, maskLeadingOnes<uint32_t>(11U));EXPECT_EQ(0xFFFFFFFFU, maskTrailingOnes<uint32_t>(32U));EXPECT_EQ(0xFFFFFFFFU, maskLeadingOnes<uint32_t>(32U));EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, maskTrailingOnes<uint64_t>(64U));EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, maskLeadingOnes<uint64_t>(64U));EXPECT_EQ(0x0000FFFFFFFFFFFFULL, maskTrailingOnes<uint64_t>(48U));EXPECT_EQ(0xFFFFFFFFFFFF0000ULL, maskLeadingOnes<uint64_t>(48U));}TEST(MathExtras, findFirstSet) {uint8_t Z8 = 0;uint16_t Z16 = 0;uint32_t Z32 = 0;uint64_t Z64 = 0;EXPECT_EQ(0xFFULL, findFirstSet(Z8));EXPECT_EQ(0xFFFFULL, findFirstSet(Z16));EXPECT_EQ(0xFFFFFFFFULL, findFirstSet(Z32));EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findFirstSet(Z64));uint8_t NZ8 = 42;uint16_t NZ16 = 42;uint32_t NZ32 = 42;uint64_t NZ64 = 42;EXPECT_EQ(1u, findFirstSet(NZ8));EXPECT_EQ(1u, findFirstSet(NZ16));EXPECT_EQ(1u, findFirstSet(NZ32));EXPECT_EQ(1u, findFirstSet(NZ64));}TEST(MathExtras, findLastSet) {uint8_t Z8 = 0;uint16_t Z16 = 0;uint32_t Z32 = 0;uint64_t Z64 = 0;EXPECT_EQ(0xFFULL, findLastSet(Z8));EXPECT_EQ(0xFFFFULL, findLastSet(Z16));EXPECT_EQ(0xFFFFFFFFULL, findLastSet(Z32));EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findLastSet(Z64));uint8_t NZ8 = 42;uint16_t NZ16 = 42;uint32_t NZ32 = 42;uint64_t NZ64 = 42;EXPECT_EQ(5u, findLastSet(NZ8));EXPECT_EQ(5u, findLastSet(NZ16));EXPECT_EQ(5u, findLastSet(NZ32));EXPECT_EQ(5u, findLastSet(NZ64));}TEST(MathExtras, isIntN) {EXPECT_TRUE(isIntN(16, 32767));EXPECT_FALSE(isIntN(16, 32768));}TEST(MathExtras, isUIntN) {EXPECT_TRUE(isUIntN(16, 65535));EXPECT_FALSE(isUIntN(16, 65536));EXPECT_TRUE(isUIntN(1, 0));EXPECT_TRUE(isUIntN(6, 63));}TEST(MathExtras, maxIntN) {EXPECT_EQ(32767, maxIntN(16));EXPECT_EQ(2147483647, maxIntN(32));EXPECT_EQ(std::numeric_limits<int32_t>::max(), maxIntN(32));EXPECT_EQ(std::numeric_limits<int64_t>::max(), maxIntN(64));}TEST(MathExtras, minIntN) {EXPECT_EQ(-32768LL, minIntN(16));EXPECT_EQ(-64LL, minIntN(7));EXPECT_EQ(std::numeric_limits<int32_t>::min(), minIntN(32));EXPECT_EQ(std::numeric_limits<int64_t>::min(), minIntN(64));}TEST(MathExtras, maxUIntN) {EXPECT_EQ(0xffffULL, maxUIntN(16));EXPECT_EQ(0xffffffffULL, maxUIntN(32));EXPECT_EQ(0xffffffffffffffffULL, maxUIntN(64));EXPECT_EQ(1ULL, maxUIntN(1));EXPECT_EQ(0x0fULL, maxUIntN(4));}TEST(MathExtras, reverseBits) {uint8_t NZ8 = 42;uint16_t NZ16 = 42;uint32_t NZ32 = 42;uint64_t NZ64 = 42;EXPECT_EQ(0x54ULL, reverseBits(NZ8));EXPECT_EQ(0x5400ULL, reverseBits(NZ16));EXPECT_EQ(0x54000000ULL, reverseBits(NZ32));EXPECT_EQ(0x5400000000000000ULL, reverseBits(NZ64));}TEST(MathExtras, isShiftedMask_32) {EXPECT_FALSE(isShiftedMask_32(0x01010101));EXPECT_TRUE(isShiftedMask_32(0xf0000000));EXPECT_TRUE(isShiftedMask_32(0xffff0000));EXPECT_TRUE(isShiftedMask_32(0xff << 1));unsigned MaskIdx, MaskLen;EXPECT_FALSE(isShiftedMask_32(0x01010101, MaskIdx, MaskLen));EXPECT_TRUE(isShiftedMask_32(0xf0000000, MaskIdx, MaskLen));EXPECT_EQ(28, (int)MaskIdx);EXPECT_EQ(4, (int)MaskLen);EXPECT_TRUE(isShiftedMask_32(0xffff0000, MaskIdx, MaskLen));EXPECT_EQ(16, (int)MaskIdx);EXPECT_EQ(16, (int)MaskLen);EXPECT_TRUE(isShiftedMask_32(0xff << 1, MaskIdx, MaskLen));EXPECT_EQ(1, (int)MaskIdx);EXPECT_EQ(8, (int)MaskLen);}TEST(MathExtras, isShiftedMask_64) {EXPECT_FALSE(isShiftedMask_64(0x0101010101010101ull));EXPECT_TRUE(isShiftedMask_64(0xf000000000000000ull));EXPECT_TRUE(isShiftedMask_64(0xffff000000000000ull));EXPECT_TRUE(isShiftedMask_64(0xffull << 55));unsigned MaskIdx, MaskLen;EXPECT_FALSE(isShiftedMask_64(0x0101010101010101ull, MaskIdx, MaskLen));EXPECT_TRUE(isShiftedMask_64(0xf000000000000000ull, MaskIdx, MaskLen));EXPECT_EQ(60, (int)MaskIdx);EXPECT_EQ(4, (int)MaskLen);EXPECT_TRUE(isShiftedMask_64(0xffff000000000000ull, MaskIdx, MaskLen));EXPECT_EQ(48, (int)MaskIdx);EXPECT_EQ(16, (int)MaskLen);EXPECT_TRUE(isShiftedMask_64(0xffull << 55, MaskIdx, MaskLen));EXPECT_EQ(55, (int)MaskIdx);EXPECT_EQ(8, (int)MaskLen);}TEST(MathExtras, isPowerOf2_32) {EXPECT_FALSE(isPowerOf2_32(0));EXPECT_TRUE(isPowerOf2_32(1 << 6));EXPECT_TRUE(isPowerOf2_32(1 << 12));EXPECT_FALSE(isPowerOf2_32((1 << 19) + 3));EXPECT_FALSE(isPowerOf2_32(0xABCDEF0));}TEST(MathExtras, isPowerOf2_64) {EXPECT_FALSE(isPowerOf2_64(0));EXPECT_TRUE(isPowerOf2_64(1LL << 46));EXPECT_TRUE(isPowerOf2_64(1LL << 12));EXPECT_FALSE(isPowerOf2_64((1LL << 53) + 3));EXPECT_FALSE(isPowerOf2_64(0xABCDEF0ABCDEF0LL));}TEST(MathExtras, PowerOf2Ceil) {EXPECT_EQ(0U, PowerOf2Ceil(0U));EXPECT_EQ(8U, PowerOf2Ceil(8U));EXPECT_EQ(8U, PowerOf2Ceil(7U));}TEST(MathExtras, PowerOf2Floor) {EXPECT_EQ(0U, PowerOf2Floor(0U));EXPECT_EQ(8U, PowerOf2Floor(8U));EXPECT_EQ(4U, PowerOf2Floor(7U));}TEST(MathExtras, CTLog2) {EXPECT_EQ(CTLog2<1ULL << 0>(), 0U);EXPECT_EQ(CTLog2<1ULL << 1>(), 1U);EXPECT_EQ(CTLog2<1ULL << 2>(), 2U);EXPECT_EQ(CTLog2<1ULL << 3>(), 3U);EXPECT_EQ(CTLog2<1ULL << 4>(), 4U);EXPECT_EQ(CTLog2<1ULL << 5>(), 5U);EXPECT_EQ(CTLog2<1ULL << 6>(), 6U);EXPECT_EQ(CTLog2<1ULL << 7>(), 7U);EXPECT_EQ(CTLog2<1ULL << 8>(), 8U);EXPECT_EQ(CTLog2<1ULL << 9>(), 9U);EXPECT_EQ(CTLog2<1ULL << 10>(), 10U);EXPECT_EQ(CTLog2<1ULL << 11>(), 11U);EXPECT_EQ(CTLog2<1ULL << 12>(), 12U);EXPECT_EQ(CTLog2<1ULL << 13>(), 13U);EXPECT_EQ(CTLog2<1ULL << 14>(), 14U);EXPECT_EQ(CTLog2<1ULL << 15>(), 15U);}TEST(MathExtras, countLeadingOnes) {for (int i = 30; i >= 0; --i) {// Start with all ones and unset some bit.EXPECT_EQ(31u - i, countLeadingOnes(0xFFFFFFFF ^ (1 << i)));}for (int i = 62; i >= 0; --i) {// Start with all ones and unset some bit.EXPECT_EQ(63u - i, countLeadingOnes(0xFFFFFFFFFFFFFFFFULL ^ (1LL << i)));}for (int i = 30; i >= 0; --i) {// Start with all ones and unset some bit.EXPECT_EQ(31u - i, countLeadingOnes(0xFFFFFFFF ^ (1 << i)));}}TEST(MathExtras, FloatBits) {static const float kValue = 5632.34f;EXPECT_FLOAT_EQ(kValue, BitsToFloat(FloatToBits(kValue)));}TEST(MathExtras, DoubleBits) {static const double kValue = 87987234.983498;EXPECT_DOUBLE_EQ(kValue, BitsToDouble(DoubleToBits(kValue)));}TEST(MathExtras, MinAlign) {EXPECT_EQ(1u, MinAlign(2, 3));EXPECT_EQ(2u, MinAlign(2, 4));EXPECT_EQ(1u, MinAlign(17, 64));EXPECT_EQ(256u, MinAlign(256, 512));}TEST(MathExtras, NextPowerOf2) {EXPECT_EQ(4u, NextPowerOf2(3));EXPECT_EQ(16u, NextPowerOf2(15));EXPECT_EQ(256u, NextPowerOf2(128));}TEST(MathExtras, alignTo) {EXPECT_EQ(8u, alignTo(5, 8));EXPECT_EQ(24u, alignTo(17, 8));EXPECT_EQ(0u, alignTo(~0LL, 8));EXPECT_EQ(7u, alignTo(5, 8, 7));EXPECT_EQ(17u, alignTo(17, 8, 1));EXPECT_EQ(3u, alignTo(~0LL, 8, 3));EXPECT_EQ(552u, alignTo(321, 255, 42));}template<typename T>void SaturatingAddTestHelper(){const T Max = std::numeric_limits<T>::max();bool ResultOverflowed;EXPECT_EQ(T(3), SaturatingAdd(T(1), T(2)));EXPECT_EQ(T(3), SaturatingAdd(T(1), T(2), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(Max, SaturatingAdd(Max, T(1)));EXPECT_EQ(Max, SaturatingAdd(Max, T(1), &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 1)));EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 1), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(Max, SaturatingAdd(T(1), Max));EXPECT_EQ(Max, SaturatingAdd(T(1), Max, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);EXPECT_EQ(Max, SaturatingAdd(Max, Max));EXPECT_EQ(Max, SaturatingAdd(Max, Max, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);}TEST(MathExtras, SaturatingAdd) {SaturatingAddTestHelper<uint8_t>();SaturatingAddTestHelper<uint16_t>();SaturatingAddTestHelper<uint32_t>();SaturatingAddTestHelper<uint64_t>();}template<typename T>void SaturatingMultiplyTestHelper(){const T Max = std::numeric_limits<T>::max();bool ResultOverflowed;// Test basic multiplication.EXPECT_EQ(T(6), SaturatingMultiply(T(2), T(3)));EXPECT_EQ(T(6), SaturatingMultiply(T(2), T(3), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(6), SaturatingMultiply(T(3), T(2)));EXPECT_EQ(T(6), SaturatingMultiply(T(3), T(2), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);// Test multiplication by zero.EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(0)));EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(0), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(0), SaturatingMultiply(T(1), T(0)));EXPECT_EQ(T(0), SaturatingMultiply(T(1), T(0), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(1)));EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(1), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(0), SaturatingMultiply(Max, T(0)));EXPECT_EQ(T(0), SaturatingMultiply(Max, T(0), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(0), SaturatingMultiply(T(0), Max));EXPECT_EQ(T(0), SaturatingMultiply(T(0), Max, &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);// Test multiplication by maximum value.EXPECT_EQ(Max, SaturatingMultiply(Max, T(2)));EXPECT_EQ(Max, SaturatingMultiply(Max, T(2), &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);EXPECT_EQ(Max, SaturatingMultiply(T(2), Max));EXPECT_EQ(Max, SaturatingMultiply(T(2), Max, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);EXPECT_EQ(Max, SaturatingMultiply(Max, Max));EXPECT_EQ(Max, SaturatingMultiply(Max, Max, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);// Test interesting boundary conditions for algorithm -// ((1 << A) - 1) * ((1 << B) + K) for K in [-1, 0, 1]// and A + B == std::numeric_limits<T>::digits.// We expect overflow iff A > B and K = 1.const int Digits = std::numeric_limits<T>::digits;for (int A = 1, B = Digits - 1; B >= 1; ++A, --B) {for (int K = -1; K <= 1; ++K) {T X = (T(1) << A) - T(1);T Y = (T(1) << B) + K;bool OverflowExpected = A > B && K == 1;if(OverflowExpected) {EXPECT_EQ(Max, SaturatingMultiply(X, Y));EXPECT_EQ(Max, SaturatingMultiply(X, Y, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);} else {EXPECT_EQ(X * Y, SaturatingMultiply(X, Y));EXPECT_EQ(X * Y, SaturatingMultiply(X, Y, &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);}}}}TEST(MathExtras, SaturatingMultiply) {SaturatingMultiplyTestHelper<uint8_t>();SaturatingMultiplyTestHelper<uint16_t>();SaturatingMultiplyTestHelper<uint32_t>();SaturatingMultiplyTestHelper<uint64_t>();}template<typename T>void SaturatingMultiplyAddTestHelper(){const T Max = std::numeric_limits<T>::max();bool ResultOverflowed;// Test basic multiply-add.EXPECT_EQ(T(16), SaturatingMultiplyAdd(T(2), T(3), T(10)));EXPECT_EQ(T(16), SaturatingMultiplyAdd(T(2), T(3), T(10), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);// Test multiply overflows, add doesn't overflowEXPECT_EQ(Max, SaturatingMultiplyAdd(Max, Max, T(0), &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);// Test multiply doesn't overflow, add overflowsEXPECT_EQ(Max, SaturatingMultiplyAdd(T(1), T(1), Max, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);// Test multiply-add with Max as operandEXPECT_EQ(Max, SaturatingMultiplyAdd(T(1), T(1), Max, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);EXPECT_EQ(Max, SaturatingMultiplyAdd(T(1), Max, T(1), &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);EXPECT_EQ(Max, SaturatingMultiplyAdd(Max, Max, T(1), &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);EXPECT_EQ(Max, SaturatingMultiplyAdd(Max, Max, Max, &ResultOverflowed));EXPECT_TRUE(ResultOverflowed);// Test multiply-add with 0 as operandEXPECT_EQ(T(1), SaturatingMultiplyAdd(T(1), T(1), T(0), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(1), SaturatingMultiplyAdd(T(1), T(0), T(1), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(1), SaturatingMultiplyAdd(T(0), T(0), T(1), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);EXPECT_EQ(T(0), SaturatingMultiplyAdd(T(0), T(0), T(0), &ResultOverflowed));EXPECT_FALSE(ResultOverflowed);}TEST(MathExtras, SaturatingMultiplyAdd) {SaturatingMultiplyAddTestHelper<uint8_t>();SaturatingMultiplyAddTestHelper<uint16_t>();SaturatingMultiplyAddTestHelper<uint32_t>();SaturatingMultiplyAddTestHelper<uint64_t>();}TEST(MathExtras, IsShiftedUInt) {EXPECT_TRUE((isShiftedUInt<1, 0>(0)));EXPECT_TRUE((isShiftedUInt<1, 0>(1)));EXPECT_FALSE((isShiftedUInt<1, 0>(2)));EXPECT_FALSE((isShiftedUInt<1, 0>(3)));EXPECT_FALSE((isShiftedUInt<1, 0>(0x8000000000000000)));EXPECT_TRUE((isShiftedUInt<1, 63>(0x8000000000000000)));EXPECT_TRUE((isShiftedUInt<2, 62>(0xC000000000000000)));EXPECT_FALSE((isShiftedUInt<2, 62>(0xE000000000000000)));// 0x201 is ten bits long and has a 1 in the MSB and LSB.EXPECT_TRUE((isShiftedUInt<10, 5>(uint64_t(0x201) << 5)));EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 4)));EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 6)));}TEST(MathExtras, IsShiftedInt) {EXPECT_TRUE((isShiftedInt<1, 0>(0)));EXPECT_TRUE((isShiftedInt<1, 0>(-1)));EXPECT_FALSE((isShiftedInt<1, 0>(2)));EXPECT_FALSE((isShiftedInt<1, 0>(3)));EXPECT_FALSE((isShiftedInt<1, 0>(0x8000000000000000)));EXPECT_TRUE((isShiftedInt<1, 63>(0x8000000000000000)));EXPECT_TRUE((isShiftedInt<2, 62>(0xC000000000000000)));EXPECT_FALSE((isShiftedInt<2, 62>(0xE000000000000000)));// 0x201 is ten bits long and has a 1 in the MSB and LSB.EXPECT_TRUE((isShiftedInt<11, 5>(int64_t(0x201) << 5)));EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 3)));EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 6)));EXPECT_TRUE((isShiftedInt<11, 5>(-(int64_t(0x201) << 5))));EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 3))));EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 6))));EXPECT_TRUE((isShiftedInt<6, 10>(-(int64_t(1) << 15))));EXPECT_FALSE((isShiftedInt<6, 10>(int64_t(1) << 15)));}template <typename T>class OverflowTest : public ::testing::Test { };using OverflowTestTypes = ::testing::Types<signed char, short, int, long,long long>;TYPED_TEST_SUITE(OverflowTest, OverflowTestTypes, );TYPED_TEST(OverflowTest, AddNoOverflow) {TypeParam Result;EXPECT_FALSE(AddOverflow<TypeParam>(1, 2, Result));EXPECT_EQ(Result, TypeParam(3));}TYPED_TEST(OverflowTest, AddOverflowToNegative) {TypeParam Result;auto MaxValue = std::numeric_limits<TypeParam>::max();EXPECT_TRUE(AddOverflow<TypeParam>(MaxValue, MaxValue, Result));EXPECT_EQ(Result, TypeParam(-2));}TYPED_TEST(OverflowTest, AddOverflowToMin) {TypeParam Result;auto MaxValue = std::numeric_limits<TypeParam>::max();EXPECT_TRUE(AddOverflow<TypeParam>(MaxValue, TypeParam(1), Result));EXPECT_EQ(Result, std::numeric_limits<TypeParam>::min());}TYPED_TEST(OverflowTest, AddOverflowToZero) {TypeParam Result;auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_TRUE(AddOverflow<TypeParam>(MinValue, MinValue, Result));EXPECT_EQ(Result, TypeParam(0));}TYPED_TEST(OverflowTest, AddOverflowToMax) {TypeParam Result;auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_TRUE(AddOverflow<TypeParam>(MinValue, TypeParam(-1), Result));EXPECT_EQ(Result, std::numeric_limits<TypeParam>::max());}TYPED_TEST(OverflowTest, SubNoOverflow) {TypeParam Result;EXPECT_FALSE(SubOverflow<TypeParam>(1, 2, Result));EXPECT_EQ(Result, TypeParam(-1));}TYPED_TEST(OverflowTest, SubOverflowToMax) {TypeParam Result;auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_TRUE(SubOverflow<TypeParam>(0, MinValue, Result));EXPECT_EQ(Result, MinValue);}TYPED_TEST(OverflowTest, SubOverflowToMin) {TypeParam Result;auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_TRUE(SubOverflow<TypeParam>(0, MinValue, Result));EXPECT_EQ(Result, MinValue);}TYPED_TEST(OverflowTest, SubOverflowToNegative) {TypeParam Result;auto MaxValue = std::numeric_limits<TypeParam>::max();auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_TRUE(SubOverflow<TypeParam>(MaxValue, MinValue, Result));EXPECT_EQ(Result, TypeParam(-1));}TYPED_TEST(OverflowTest, SubOverflowToPositive) {TypeParam Result;auto MaxValue = std::numeric_limits<TypeParam>::max();auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_TRUE(SubOverflow<TypeParam>(MinValue, MaxValue, Result));EXPECT_EQ(Result, TypeParam(1));}TYPED_TEST(OverflowTest, MulNoOverflow) {TypeParam Result;EXPECT_FALSE(MulOverflow<TypeParam>(1, 2, Result));EXPECT_EQ(Result, 2);EXPECT_FALSE(MulOverflow<TypeParam>(-1, 3, Result));EXPECT_EQ(Result, -3);EXPECT_FALSE(MulOverflow<TypeParam>(4, -2, Result));EXPECT_EQ(Result, -8);EXPECT_FALSE(MulOverflow<TypeParam>(-6, -5, Result));EXPECT_EQ(Result, 30);}TYPED_TEST(OverflowTest, MulNoOverflowToMax) {TypeParam Result;auto MaxValue = std::numeric_limits<TypeParam>::max();auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_FALSE(MulOverflow<TypeParam>(MinValue + 1, -1, Result));EXPECT_EQ(Result, MaxValue);}TYPED_TEST(OverflowTest, MulOverflowToMin) {TypeParam Result;auto MinValue = std::numeric_limits<TypeParam>::min();EXPECT_TRUE(MulOverflow<TypeParam>(MinValue, -1, Result));EXPECT_EQ(Result, MinValue);}TYPED_TEST(OverflowTest, MulOverflowMax) {TypeParam Result;auto MinValue = std::numeric_limits<TypeParam>::min();auto MaxValue = std::numeric_limits<TypeParam>::max();EXPECT_TRUE(MulOverflow<TypeParam>(MinValue, MinValue, Result));EXPECT_EQ(Result, 0);EXPECT_TRUE(MulOverflow<TypeParam>(MaxValue, MaxValue, Result));EXPECT_EQ(Result, 1);}TYPED_TEST(OverflowTest, MulResultZero) {TypeParam Result;EXPECT_FALSE(MulOverflow<TypeParam>(4, 0, Result));EXPECT_EQ(Result, TypeParam(0));EXPECT_FALSE(MulOverflow<TypeParam>(-5, 0, Result));EXPECT_EQ(Result, TypeParam(0));EXPECT_FALSE(MulOverflow<TypeParam>(0, 5, Result));EXPECT_EQ(Result, TypeParam(0));EXPECT_FALSE(MulOverflow<TypeParam>(0, -5, Result));EXPECT_EQ(Result, TypeParam(0));}} // namespace
//===----- unittests/MatchersTest.cpp -------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Optional.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gmock/gmock-matchers.h"using ::testing::_;using ::testing::AllOf;using ::testing::Gt;using ::testing::Lt;using ::testing::Not;namespace {TEST(MatchersTest, Optional) {EXPECT_THAT(llvm::Optional<int>(llvm::None), Not(llvm::ValueIs(_)));EXPECT_THAT(llvm::Optional<int>(10), llvm::ValueIs(10));EXPECT_THAT(llvm::Optional<int>(10), llvm::ValueIs(AllOf(Lt(11), Gt(9))));}} // namespace
//===- llvm/unittest/Support/ManagedStatic.cpp - ManagedStatic tests ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Allocator.h"#include "llvm/Support/ManagedStatic.h"#include "llvm/Config/config.h"#ifdef HAVE_PTHREAD_H#include <pthread.h>#endif#include "gtest/gtest.h"using namespace llvm;namespace {#if LLVM_ENABLE_THREADS != 0 && defined(HAVE_PTHREAD_H) && \!__has_feature(memory_sanitizer)namespace test1 {llvm::ManagedStatic<int> ms;void *helper(void*) {*ms;return nullptr;}// Valgrind's leak checker complains glibc's stack allocation.// To appease valgrind, we provide our own stack for each thread.void *allocate_stack(pthread_attr_t &a, size_t n = 65536) {void *stack = safe_malloc(n);pthread_attr_init(&a);#if defined(__linux__)pthread_attr_setstack(&a, stack, n);#endifreturn stack;}}TEST(Initialize, MultipleThreads) {// Run this test under tsan: http://code.google.com/p/data-race-test/pthread_attr_t a1, a2;void *p1 = test1::allocate_stack(a1);void *p2 = test1::allocate_stack(a2);pthread_t t1, t2;pthread_create(&t1, &a1, test1::helper, nullptr);pthread_create(&t2, &a2, test1::helper, nullptr);pthread_join(t1, nullptr);pthread_join(t2, nullptr);free(p1);free(p2);}#endifnamespace NestedStatics {static ManagedStatic<int> Ms1;struct Nest {Nest() {++(*Ms1);}~Nest() {assert(Ms1.isConstructed());++(*Ms1);}};static ManagedStatic<Nest> Ms2;TEST(ManagedStaticTest, NestedStatics) {EXPECT_FALSE(Ms1.isConstructed());EXPECT_FALSE(Ms2.isConstructed());*Ms2;EXPECT_TRUE(Ms1.isConstructed());EXPECT_TRUE(Ms2.isConstructed());}} // namespace NestedStaticsnamespace CustomCreatorDeletor {struct CustomCreate {static void *call() {void *Mem = safe_malloc(sizeof(int));*((int *)Mem) = 42;return Mem;}};struct CustomDelete {static void call(void *P) { std::free(P); }};static ManagedStatic<int, CustomCreate, CustomDelete> Custom;TEST(ManagedStaticTest, CustomCreatorDeletor) {EXPECT_EQ(42, *Custom);}} // namespace CustomCreatorDeletor} // anonymous namespace
//===- llvm/unittest/Support/MD5Test.cpp - MD5 tests ----------------------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for the MD5 functions.////===----------------------------------------------------------------------===//#include "llvm/Support/MD5.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/SmallString.h"#include "gtest/gtest.h"using namespace llvm;namespace {/// Tests an arbitrary set of bytes passed as \p Input.void TestMD5Sum(ArrayRef<uint8_t> Input, StringRef Final) {MD5 Hash;Hash.update(Input);MD5::MD5Result MD5Res;Hash.final(MD5Res);SmallString<32> Res;MD5::stringifyResult(MD5Res, Res);EXPECT_EQ(Res, Final);}void TestMD5Sum(StringRef Input, StringRef Final) {MD5 Hash;Hash.update(Input);MD5::MD5Result MD5Res;Hash.final(MD5Res);SmallString<32> Res;MD5::stringifyResult(MD5Res, Res);EXPECT_EQ(Res, Final);}TEST(MD5Test, MD5) {TestMD5Sum(makeArrayRef((const uint8_t *)"", (size_t) 0),"d41d8cd98f00b204e9800998ecf8427e");TestMD5Sum(makeArrayRef((const uint8_t *)"a", (size_t) 1),"0cc175b9c0f1b6a831c399e269772661");TestMD5Sum(makeArrayRef((const uint8_t *)"abcdefghijklmnopqrstuvwxyz",(size_t) 26),"c3fcd3d76192e4007dfb496cca67e13b");TestMD5Sum(makeArrayRef((const uint8_t *)"\0", (size_t) 1),"93b885adfe0da089cdf634904fd59f71");TestMD5Sum(makeArrayRef((const uint8_t *)"a\0", (size_t) 2),"4144e195f46de78a3623da7364d04f11");TestMD5Sum(makeArrayRef((const uint8_t *)"abcdefghijklmnopqrstuvwxyz\0",(size_t) 27),"81948d1f1554f58cd1a56ebb01f808cb");TestMD5Sum("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b");}TEST(MD5HashTest, MD5) {ArrayRef<uint8_t> Input((const uint8_t *)"abcdefghijklmnopqrstuvwxyz", 26);std::array<uint8_t, 16> Vec = MD5::hash(Input);MD5::MD5Result MD5Res;SmallString<32> Res;memcpy(MD5Res.data(), Vec.data(), Vec.size());MD5::stringifyResult(MD5Res, Res);EXPECT_EQ(Res, "c3fcd3d76192e4007dfb496cca67e13b");EXPECT_EQ(0x3be167ca6c49fb7dULL, MD5Res.high());EXPECT_EQ(0x00e49261d7d3fcc3ULL, MD5Res.low());}TEST(MD5Test, FinalAndResultHelpers) {MD5 Hash;Hash.update("abcd");{MD5 ReferenceHash;ReferenceHash.update("abcd");MD5::MD5Result ReferenceResult;ReferenceHash.final(ReferenceResult);EXPECT_EQ(Hash.result(), ReferenceResult);}Hash.update("xyz");{MD5 ReferenceHash;ReferenceHash.update("abcd");ReferenceHash.update("xyz");MD5::MD5Result ReferenceResult;ReferenceHash.final(ReferenceResult);EXPECT_EQ(Hash.final(), ReferenceResult);}}} // namespace
//===- unittests/LockFileManagerTest.cpp - LockFileManager tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/LockFileManager.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"#include <memory>using namespace llvm;using llvm::unittest::TempDir;namespace {TEST(LockFileManagerTest, Basic) {TempDir TmpDir("LockFileManagerTestDir", /*Unique*/ true);SmallString<64> LockedFile(TmpDir.path());sys::path::append(LockedFile, "file.lock");{// The lock file should not exist, so we should successfully acquire it.LockFileManager Locked1(LockedFile);EXPECT_EQ(LockFileManager::LFS_Owned, Locked1.getState());// Attempting to reacquire the lock should fail. Waiting on it would cause// deadlock, so don't try that.LockFileManager Locked2(LockedFile);EXPECT_NE(LockFileManager::LFS_Owned, Locked2.getState());}// Now that the lock is out of scope, the file should be gone.EXPECT_FALSE(sys::fs::exists(LockedFile.str()));}TEST(LockFileManagerTest, LinkLockExists) {TempDir LockFileManagerTestDir("LockFileManagerTestDir", /*Unique*/ true);SmallString<64> LockedFile(LockFileManagerTestDir.path());sys::path::append(LockedFile, "file");SmallString<64> FileLocK(LockFileManagerTestDir.path());sys::path::append(FileLocK, "file.lock");SmallString<64> TmpFileLock(LockFileManagerTestDir.path());sys::path::append(TmpFileLock, "file.lock-000");int FD;std::error_code EC = sys::fs::openFileForWrite(TmpFileLock.str(), FD);ASSERT_FALSE(EC);int Ret = close(FD);ASSERT_EQ(Ret, 0);EC = sys::fs::create_link(TmpFileLock.str(), FileLocK.str());ASSERT_FALSE(EC);EC = sys::fs::remove(TmpFileLock.str());ASSERT_FALSE(EC);{// The lock file doesn't point to a real file, so we should successfully// acquire it.LockFileManager Locked(LockedFile);EXPECT_EQ(LockFileManager::LFS_Owned, Locked.getState());}// Now that the lock is out of scope, the file should be gone.EXPECT_FALSE(sys::fs::exists(LockedFile.str()));}TEST(LockFileManagerTest, RelativePath) {TempDir LockFileManagerTestDir("LockFileManagerTestDir", /*Unique*/ true);char PathBuf[1024];const char *OrigPath = getcwd(PathBuf, 1024);ASSERT_FALSE(chdir(LockFileManagerTestDir.c_str()));TempDir inner("inner");SmallString<64> LockedFile(inner.path());sys::path::append(LockedFile, "file");SmallString<64> FileLock(LockedFile);FileLock += ".lock";{// The lock file should not exist, so we should successfully acquire it.LockFileManager Locked(LockedFile);EXPECT_EQ(LockFileManager::LFS_Owned, Locked.getState());EXPECT_TRUE(sys::fs::exists(FileLock.str()));}// Now that the lock is out of scope, the file should be gone.EXPECT_FALSE(sys::fs::exists(LockedFile.str()));EXPECT_FALSE(sys::fs::exists(FileLock.str()));ASSERT_FALSE(chdir(OrigPath));}} // end anonymous namespace
//===- TestPoly3D.cpp - Poly3D unit tests------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/TypeSize.h"#include "gtest/gtest.h"using namespace llvm;class Poly3D;namespace llvm {template <> struct LinearPolyBaseTypeTraits<Poly3D> {using ScalarTy = int64_t;static const unsigned Dimensions = 3;};}using Poly3DBase = LinearPolyBase<Poly3D>;class Poly3D : public Poly3DBase {public:using ScalarTy = Poly3DBase::ScalarTy;Poly3D(ScalarTy x, ScalarTy y, ScalarTy z) : Poly3DBase({x, y, z}) {}Poly3D(const Poly3DBase &Convert) : Poly3DBase(Convert) {}};TEST(LinearPolyBase, Poly3D_isZero) {EXPECT_TRUE(Poly3D(0, 0, 0).isZero());EXPECT_TRUE(Poly3D(0, 0, 1).isNonZero());EXPECT_TRUE(Poly3D(0, 0, 1));}TEST(LinearPolyBase, Poly3D_Equality) {EXPECT_EQ(Poly3D(1, 2, 3), Poly3D(1, 2, 3));EXPECT_NE(Poly3D(1, 2, 3), Poly3D(1, 2, 4));}TEST(LinearPolyBase, Poly3D_GetValue) {EXPECT_EQ(Poly3D(1, 2, 3).getValue(0), 1);EXPECT_EQ(Poly3D(1, 2, 3).getValue(1), 2);EXPECT_EQ(Poly3D(1, 2, 3).getValue(2), 3);}TEST(LinearPolyBase, Poly3D_Add) {// Test operator+EXPECT_EQ(Poly3D(42, 0, 0) + Poly3D(0, 42, 0) + Poly3D(0, 0, 42),Poly3D(42, 42, 42));// Test operator+=Poly3D X(42, 0, 0);X += Poly3D(0, 42, 0);X += Poly3D(0, 0, 42);EXPECT_EQ(X, Poly3D(42, 42, 42));}TEST(LinearPolyBase, Poly3D_Sub) {// Test operator-EXPECT_EQ(Poly3D(42, 42, 42) - Poly3D(42, 0, 0) - Poly3D(0, 42, 0) -Poly3D(0, 0, 42),Poly3D(0, 0, 0));// Test operator-=Poly3D X(42, 42, 42);X -= Poly3D(42, 0, 0);X -= Poly3D(0, 42, 0);X -= Poly3D(0, 0, 42);EXPECT_EQ(X, Poly3D(0, 0, 0));}TEST(LinearPolyBase, Poly3D_Scale) {// Test operator*EXPECT_EQ(Poly3D(1, 2, 4) * 2, Poly3D(2, 4, 8));EXPECT_EQ(Poly3D(1, 2, 4) * -2, Poly3D(-2, -4, -8));}TEST(LinearPolyBase, Poly3D_Invert) {// Test operator-EXPECT_EQ(-Poly3D(2, 4, 8), Poly3D(-2, -4, -8));}class Univariate3D;namespace llvm {template <> struct LinearPolyBaseTypeTraits<Univariate3D> {using ScalarTy = int64_t;static const unsigned Dimensions = 3;};}using Univariate3DBase = UnivariateLinearPolyBase<Univariate3D>;class Univariate3D : public Univariate3DBase {public:using ScalarTy = Univariate3DBase::ScalarTy;Univariate3D(ScalarTy x, unsigned Dim) : Univariate3DBase(x, Dim) {}Univariate3D(const Univariate3DBase &Convert) : Univariate3DBase(Convert) {}};TEST(UnivariateLinearPolyBase, Univariate3D_isZero) {EXPECT_TRUE(Univariate3D(0, 0).isZero());EXPECT_TRUE(Univariate3D(0, 1).isZero());EXPECT_TRUE(Univariate3D(0, 2).isZero());EXPECT_TRUE(Univariate3D(1, 0).isNonZero());EXPECT_TRUE(Univariate3D(1, 1).isNonZero());EXPECT_TRUE(Univariate3D(1, 2).isNonZero());EXPECT_TRUE(Univariate3D(1, 0));}TEST(UnivariateLinearPolyBase, Univariate3D_Equality) {EXPECT_EQ(Univariate3D(1, 0), Univariate3D(1, 0));EXPECT_NE(Univariate3D(1, 0), Univariate3D(1, 2));EXPECT_NE(Univariate3D(1, 0), Univariate3D(1, 1));EXPECT_NE(Univariate3D(1, 0), Univariate3D(2, 0));EXPECT_NE(Univariate3D(1, 0), Univariate3D(0, 0));}TEST(UnivariateLinearPolyBase, Univariate3D_GetValue) {EXPECT_EQ(Univariate3D(42, 0).getValue(0), 42);EXPECT_EQ(Univariate3D(42, 0).getValue(1), 0);EXPECT_EQ(Univariate3D(42, 0).getValue(2), 0);EXPECT_EQ(Univariate3D(42, 1).getValue(0), 0);EXPECT_EQ(Univariate3D(42, 1).getValue(1), 42);EXPECT_EQ(Univariate3D(42, 1).getValue(2), 0);}TEST(UnivariateLinearPolyBase, Univariate3D_Add) {// Test operator+EXPECT_EQ(Univariate3D(42, 0) + Univariate3D(42, 0), Univariate3D(84, 0));EXPECT_EQ(Univariate3D(42, 1) + Univariate3D(42, 1), Univariate3D(84, 1));EXPECT_DEBUG_DEATH(Univariate3D(42, 0) + Univariate3D(42, 1),"Invalid dimensions");// Test operator+=Univariate3D X(42, 0);X += Univariate3D(42, 0);EXPECT_EQ(X, Univariate3D(84, 0));// Test 'getWithIncrement' methodEXPECT_EQ(Univariate3D(42, 0).getWithIncrement(1), Univariate3D(43, 0));EXPECT_EQ(Univariate3D(42, 1).getWithIncrement(2), Univariate3D(44, 1));EXPECT_EQ(Univariate3D(42, 2).getWithIncrement(3), Univariate3D(45, 2));}TEST(UnivariateLinearPolyBase, Univariate3D_Sub) {// Test operator+EXPECT_EQ(Univariate3D(84, 0) - Univariate3D(42, 0), Univariate3D(42, 0));EXPECT_EQ(Univariate3D(84, 1) - Univariate3D(42, 1), Univariate3D(42, 1));EXPECT_DEBUG_DEATH(Univariate3D(84, 0) - Univariate3D(42, 1),"Invalid dimensions");// Test operator+=Univariate3D X(84, 0);X -= Univariate3D(42, 0);EXPECT_EQ(X, Univariate3D(42, 0));// Test 'getWithDecrement' methodEXPECT_EQ(Univariate3D(43, 0).getWithDecrement(1), Univariate3D(42, 0));EXPECT_EQ(Univariate3D(44, 1).getWithDecrement(2), Univariate3D(42, 1));EXPECT_EQ(Univariate3D(45, 2).getWithDecrement(3), Univariate3D(42, 2));}TEST(UnivariateLinearPolyBase, Univariate3D_Scale) {// Test operator*EXPECT_EQ(Univariate3D(4, 0) * 2, Univariate3D(8, 0));EXPECT_EQ(Univariate3D(4, 1) * -2, Univariate3D(-8, 1));}TEST(UnivariateLinearPolyBase, Univariate3D_Invert) {// Test operator-EXPECT_EQ(-Univariate3D(4, 0), Univariate3D(-4, 0));EXPECT_EQ(-Univariate3D(4, 1), Univariate3D(-4, 1));}
//===- LineIterator.cpp - Unit tests --------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/LineIterator.h"#include "llvm/Support/MemoryBuffer.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::sys;namespace {TEST(LineIteratorTest, Basic) {std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer("line 1\n""line 2\n""line 3");line_iterator I = line_iterator(*Buffer), E;EXPECT_FALSE(I.is_at_eof());EXPECT_NE(E, I);EXPECT_EQ("line 1", *I);EXPECT_EQ(1, I.line_number());++I;EXPECT_EQ("line 2", *I);EXPECT_EQ(2, I.line_number());++I;EXPECT_EQ("line 3", *I);EXPECT_EQ(3, I.line_number());++I;EXPECT_TRUE(I.is_at_eof());EXPECT_EQ(E, I);}TEST(LineIteratorTest, Ref) {std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer("line 1\n""line 2\n""line 3");line_iterator I = line_iterator(Buffer->getMemBufferRef()), E;EXPECT_FALSE(I.is_at_eof());EXPECT_NE(E, I);EXPECT_EQ("line 1", *I);EXPECT_EQ(1, I.line_number());++I;EXPECT_EQ("line 2", *I);EXPECT_EQ(2, I.line_number());++I;EXPECT_EQ("line 3", *I);EXPECT_EQ(3, I.line_number());++I;EXPECT_TRUE(I.is_at_eof());EXPECT_EQ(E, I);}TEST(LineIteratorTest, CommentAndBlankSkipping) {std::unique_ptr<MemoryBuffer> Buffer(MemoryBuffer::getMemBuffer("line 1\n""line 2\n""# Comment 1\n""\n""line 5\n""\n""# Comment 2"));line_iterator I = line_iterator(*Buffer, true, '#'), E;EXPECT_FALSE(I.is_at_eof());EXPECT_NE(E, I);EXPECT_EQ("line 1", *I);EXPECT_EQ(1, I.line_number());++I;EXPECT_EQ("line 2", *I);EXPECT_EQ(2, I.line_number());++I;EXPECT_EQ("line 5", *I);EXPECT_EQ(5, I.line_number());++I;EXPECT_TRUE(I.is_at_eof());EXPECT_EQ(E, I);}TEST(LineIteratorTest, CommentSkippingKeepBlanks) {std::unique_ptr<MemoryBuffer> Buffer(MemoryBuffer::getMemBuffer("line 1\n""line 2\n""# Comment 1\n""# Comment 2\n""\n""line 6\n""\n""# Comment 3"));line_iterator I = line_iterator(*Buffer, false, '#'), E;EXPECT_FALSE(I.is_at_eof());EXPECT_NE(E, I);EXPECT_EQ("line 1", *I);EXPECT_EQ(1, I.line_number());++I;EXPECT_EQ("line 2", *I);EXPECT_EQ(2, I.line_number());++I;EXPECT_EQ("", *I);EXPECT_EQ(5, I.line_number());++I;EXPECT_EQ("line 6", *I);EXPECT_EQ(6, I.line_number());++I;EXPECT_EQ("", *I);EXPECT_EQ(7, I.line_number());++I;EXPECT_TRUE(I.is_at_eof());EXPECT_EQ(E, I);}TEST(LineIteratorTest, BlankSkipping) {std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer("\n\n\n""line 1\n""\n\n\n""line 2\n""\n\n\n");line_iterator I = line_iterator(*Buffer), E;EXPECT_FALSE(I.is_at_eof());EXPECT_NE(E, I);EXPECT_EQ("line 1", *I);EXPECT_EQ(4, I.line_number());++I;EXPECT_EQ("line 2", *I);EXPECT_EQ(8, I.line_number());++I;EXPECT_TRUE(I.is_at_eof());EXPECT_EQ(E, I);}TEST(LineIteratorTest, BlankKeeping) {std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer("\n\n""line 3\n""\n""line 5\n""\n\n");line_iterator I = line_iterator(*Buffer, false), E;EXPECT_FALSE(I.is_at_eof());EXPECT_NE(E, I);EXPECT_EQ("", *I);EXPECT_EQ(1, I.line_number());++I;EXPECT_EQ("", *I);EXPECT_EQ(2, I.line_number());++I;EXPECT_EQ("line 3", *I);EXPECT_EQ(3, I.line_number());++I;EXPECT_EQ("", *I);EXPECT_EQ(4, I.line_number());++I;EXPECT_EQ("line 5", *I);EXPECT_EQ(5, I.line_number());++I;EXPECT_EQ("", *I);EXPECT_EQ(6, I.line_number());++I;EXPECT_EQ("", *I);EXPECT_EQ(7, I.line_number());++I;EXPECT_TRUE(I.is_at_eof());EXPECT_EQ(E, I);}TEST(LineIteratorTest, EmptyBuffers) {std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer("");EXPECT_TRUE(line_iterator(*Buffer).is_at_eof());EXPECT_EQ(line_iterator(), line_iterator(*Buffer));EXPECT_TRUE(line_iterator(*Buffer, false).is_at_eof());EXPECT_EQ(line_iterator(), line_iterator(*Buffer, false));Buffer = MemoryBuffer::getMemBuffer("\n\n\n");EXPECT_TRUE(line_iterator(*Buffer).is_at_eof());EXPECT_EQ(line_iterator(), line_iterator(*Buffer));Buffer = MemoryBuffer::getMemBuffer("# foo\n""\n""# bar");EXPECT_TRUE(line_iterator(*Buffer, true, '#').is_at_eof());EXPECT_EQ(line_iterator(), line_iterator(*Buffer, true, '#'));Buffer = MemoryBuffer::getMemBuffer("\n""# baz\n""\n");EXPECT_TRUE(line_iterator(*Buffer, true, '#').is_at_eof());EXPECT_EQ(line_iterator(), line_iterator(*Buffer, true, '#'));}} // anonymous namespace
//===- llvm/unittest/Support/LEB128Test.cpp - LEB128 function tests -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/LEB128.h"#include "llvm/Support/DataTypes.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <string>using namespace llvm;namespace {TEST(LEB128Test, EncodeSLEB128) {#define EXPECT_SLEB128_EQ(EXPECTED, VALUE, PAD) \do { \std::string Expected(EXPECTED, sizeof(EXPECTED) - 1); \\/* encodeSLEB128(uint64_t, raw_ostream &, unsigned) */ \std::string Actual1; \raw_string_ostream Stream(Actual1); \encodeSLEB128(VALUE, Stream, PAD); \Stream.flush(); \EXPECT_EQ(Expected, Actual1); \\/* encodeSLEB128(uint64_t, uint8_t *, unsigned) */ \uint8_t Buffer[32]; \unsigned Size = encodeSLEB128(VALUE, Buffer, PAD); \std::string Actual2(reinterpret_cast<const char *>(Buffer), Size); \EXPECT_EQ(Expected, Actual2); \} while (0)// Encode SLEB128EXPECT_SLEB128_EQ("\x00", 0, 0);EXPECT_SLEB128_EQ("\x01", 1, 0);EXPECT_SLEB128_EQ("\x7f", -1, 0);EXPECT_SLEB128_EQ("\x3f", 63, 0);EXPECT_SLEB128_EQ("\x41", -63, 0);EXPECT_SLEB128_EQ("\x40", -64, 0);EXPECT_SLEB128_EQ("\xbf\x7f", -65, 0);EXPECT_SLEB128_EQ("\xc0\x00", 64, 0);// Encode SLEB128 with some extra padding bytesEXPECT_SLEB128_EQ("\x80\x00", 0, 2);EXPECT_SLEB128_EQ("\x80\x80\x00", 0, 3);EXPECT_SLEB128_EQ("\xff\x80\x00", 0x7f, 3);EXPECT_SLEB128_EQ("\xff\x80\x80\x00", 0x7f, 4);EXPECT_SLEB128_EQ("\x80\x81\x00", 0x80, 3);EXPECT_SLEB128_EQ("\x80\x81\x80\x00", 0x80, 4);EXPECT_SLEB128_EQ("\xc0\x7f", -0x40, 2);EXPECT_SLEB128_EQ("\xc0\xff\x7f", -0x40, 3);EXPECT_SLEB128_EQ("\x80\xff\x7f", -0x80, 3);EXPECT_SLEB128_EQ("\x80\xff\xff\x7f", -0x80, 4);#undef EXPECT_SLEB128_EQ}TEST(LEB128Test, EncodeULEB128) {#define EXPECT_ULEB128_EQ(EXPECTED, VALUE, PAD) \do { \std::string Expected(EXPECTED, sizeof(EXPECTED) - 1); \\/* encodeULEB128(uint64_t, raw_ostream &, unsigned) */ \std::string Actual1; \raw_string_ostream Stream(Actual1); \encodeULEB128(VALUE, Stream, PAD); \Stream.flush(); \EXPECT_EQ(Expected, Actual1); \\/* encodeULEB128(uint64_t, uint8_t *, unsigned) */ \uint8_t Buffer[32]; \unsigned Size = encodeULEB128(VALUE, Buffer, PAD); \std::string Actual2(reinterpret_cast<const char *>(Buffer), Size); \EXPECT_EQ(Expected, Actual2); \} while (0)// Encode ULEB128EXPECT_ULEB128_EQ("\x00", 0, 0);EXPECT_ULEB128_EQ("\x01", 1, 0);EXPECT_ULEB128_EQ("\x3f", 63, 0);EXPECT_ULEB128_EQ("\x40", 64, 0);EXPECT_ULEB128_EQ("\x7f", 0x7f, 0);EXPECT_ULEB128_EQ("\x80\x01", 0x80, 0);EXPECT_ULEB128_EQ("\x81\x01", 0x81, 0);EXPECT_ULEB128_EQ("\x90\x01", 0x90, 0);EXPECT_ULEB128_EQ("\xff\x01", 0xff, 0);EXPECT_ULEB128_EQ("\x80\x02", 0x100, 0);EXPECT_ULEB128_EQ("\x81\x02", 0x101, 0);// Encode ULEB128 with some extra padding bytesEXPECT_ULEB128_EQ("\x80\x00", 0, 2);EXPECT_ULEB128_EQ("\x80\x80\x00", 0, 3);EXPECT_ULEB128_EQ("\xff\x00", 0x7f, 2);EXPECT_ULEB128_EQ("\xff\x80\x00", 0x7f, 3);EXPECT_ULEB128_EQ("\x80\x81\x00", 0x80, 3);EXPECT_ULEB128_EQ("\x80\x81\x80\x00", 0x80, 4);#undef EXPECT_ULEB128_EQ}TEST(LEB128Test, DecodeULEB128) {#define EXPECT_DECODE_ULEB128_EQ(EXPECTED, VALUE) \do { \unsigned ActualSize = 0; \uint64_t Actual = decodeULEB128(reinterpret_cast<const uint8_t *>(VALUE), \&ActualSize); \EXPECT_EQ(sizeof(VALUE) - 1, ActualSize); \EXPECT_EQ(EXPECTED, Actual); \} while (0)// Don't crashEXPECT_EQ(0u, decodeULEB128(nullptr, nullptr, nullptr));// Decode ULEB128EXPECT_DECODE_ULEB128_EQ(0u, "\x00");EXPECT_DECODE_ULEB128_EQ(1u, "\x01");EXPECT_DECODE_ULEB128_EQ(63u, "\x3f");EXPECT_DECODE_ULEB128_EQ(64u, "\x40");EXPECT_DECODE_ULEB128_EQ(0x7fu, "\x7f");EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x01");EXPECT_DECODE_ULEB128_EQ(0x81u, "\x81\x01");EXPECT_DECODE_ULEB128_EQ(0x90u, "\x90\x01");EXPECT_DECODE_ULEB128_EQ(0xffu, "\xff\x01");EXPECT_DECODE_ULEB128_EQ(0x100u, "\x80\x02");EXPECT_DECODE_ULEB128_EQ(0x101u, "\x81\x02");EXPECT_DECODE_ULEB128_EQ(4294975616ULL, "\x80\xc1\x80\x80\x10");// Decode ULEB128 with extra padding bytesEXPECT_DECODE_ULEB128_EQ(0u, "\x80\x00");EXPECT_DECODE_ULEB128_EQ(0u, "\x80\x80\x00");EXPECT_DECODE_ULEB128_EQ(0x7fu, "\xff\x00");EXPECT_DECODE_ULEB128_EQ(0x7fu, "\xff\x80\x00");EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x81\x00");EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x81\x80\x00");EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x81\x80\x80\x80\x80\x80\x80\x80\x00");EXPECT_DECODE_ULEB128_EQ(0x80000000'00000000ul,"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01");#undef EXPECT_DECODE_ULEB128_EQ}TEST(LEB128Test, DecodeInvalidULEB128) {#define EXPECT_INVALID_ULEB128(VALUE, ERROR_OFFSET) \do { \const uint8_t *Value = reinterpret_cast<const uint8_t *>(VALUE); \const char *Error = nullptr; \unsigned ErrorOffset = 0; \uint64_t Actual = \decodeULEB128(Value, &ErrorOffset, Value + strlen(VALUE), &Error); \EXPECT_NE(Error, nullptr); \EXPECT_EQ(0ul, Actual); \EXPECT_EQ(ERROR_OFFSET, ErrorOffset); \} while (0)// Buffer overflow.EXPECT_INVALID_ULEB128("", 0u);EXPECT_INVALID_ULEB128("\x80", 1u);// Does not fit in 64 bits.EXPECT_INVALID_ULEB128("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02", 9u);EXPECT_INVALID_ULEB128("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02", 10u);#undef EXPECT_INVALID_ULEB128}TEST(LEB128Test, DecodeSLEB128) {#define EXPECT_DECODE_SLEB128_EQ(EXPECTED, VALUE) \do { \unsigned ActualSize = 0; \int64_t Actual = decodeSLEB128(reinterpret_cast<const uint8_t *>(VALUE), \&ActualSize); \EXPECT_EQ(sizeof(VALUE) - 1, ActualSize); \EXPECT_EQ(EXPECTED, Actual); \} while (0)// Don't crashEXPECT_EQ(0, decodeSLEB128(nullptr, nullptr, nullptr));// Decode SLEB128EXPECT_DECODE_SLEB128_EQ(0L, "\x00");EXPECT_DECODE_SLEB128_EQ(1L, "\x01");EXPECT_DECODE_SLEB128_EQ(63L, "\x3f");EXPECT_DECODE_SLEB128_EQ(-64L, "\x40");EXPECT_DECODE_SLEB128_EQ(-63L, "\x41");EXPECT_DECODE_SLEB128_EQ(-1L, "\x7f");EXPECT_DECODE_SLEB128_EQ(128L, "\x80\x01");EXPECT_DECODE_SLEB128_EQ(129L, "\x81\x01");EXPECT_DECODE_SLEB128_EQ(-129L, "\xff\x7e");EXPECT_DECODE_SLEB128_EQ(-128L, "\x80\x7f");EXPECT_DECODE_SLEB128_EQ(-127L, "\x81\x7f");EXPECT_DECODE_SLEB128_EQ(64L, "\xc0\x00");EXPECT_DECODE_SLEB128_EQ(-12345L, "\xc7\x9f\x7f");// Decode unnormalized SLEB128 with extra padding bytes.EXPECT_DECODE_SLEB128_EQ(0L, "\x80\x00");EXPECT_DECODE_SLEB128_EQ(0L, "\x80\x80\x00");EXPECT_DECODE_SLEB128_EQ(0x7fL, "\xff\x00");EXPECT_DECODE_SLEB128_EQ(0x7fL, "\xff\x80\x00");EXPECT_DECODE_SLEB128_EQ(0x80L, "\x80\x81\x00");EXPECT_DECODE_SLEB128_EQ(0x80L, "\x80\x81\x80\x00");EXPECT_DECODE_SLEB128_EQ(0x80L, "\x80\x81\x80\x80\x80\x80\x80\x80\x80\x00");EXPECT_DECODE_SLEB128_EQ(-2L, "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F");EXPECT_DECODE_SLEB128_EQ(INT64_MIN,"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7F");EXPECT_DECODE_SLEB128_EQ(INT64_MAX,"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00");#undef EXPECT_DECODE_SLEB128_EQ}TEST(LEB128Test, DecodeInvalidSLEB128) {#define EXPECT_INVALID_SLEB128(VALUE, ERROR_OFFSET) \do { \const uint8_t *Value = reinterpret_cast<const uint8_t *>(VALUE); \const char *Error = nullptr; \unsigned ErrorOffset = 0; \uint64_t Actual = \decodeSLEB128(Value, &ErrorOffset, Value + strlen(VALUE), &Error); \EXPECT_NE(Error, nullptr); \EXPECT_EQ(0ul, Actual); \EXPECT_EQ(ERROR_OFFSET, ErrorOffset); \} while (0)// Buffer overflow.EXPECT_INVALID_SLEB128("", 0u);EXPECT_INVALID_SLEB128("\x80", 1u);// Does not fit in 64 bits.EXPECT_INVALID_SLEB128("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01", 9u);EXPECT_INVALID_SLEB128("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7E", 9u);EXPECT_INVALID_SLEB128("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02", 10u);EXPECT_INVALID_SLEB128("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7E", 9u);EXPECT_INVALID_SLEB128("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01", 9u);EXPECT_INVALID_SLEB128("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7E", 10u);EXPECT_INVALID_SLEB128("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", 10u);#undef EXPECT_INVALID_SLEB128}TEST(LEB128Test, SLEB128Size) {// Positive Value Testing Plan:// (1) 128 ^ n - 1 ........ need (n+1) bytes// (2) 128 ^ n ............ need (n+1) bytes// (3) 128 ^ n * 63 ....... need (n+1) bytes// (4) 128 ^ n * 64 - 1 ... need (n+1) bytes// (5) 128 ^ n * 64 ....... need (n+2) bytesEXPECT_EQ(1u, getSLEB128Size(0x0LL));EXPECT_EQ(1u, getSLEB128Size(0x1LL));EXPECT_EQ(1u, getSLEB128Size(0x3fLL));EXPECT_EQ(1u, getSLEB128Size(0x3fLL));EXPECT_EQ(2u, getSLEB128Size(0x40LL));EXPECT_EQ(2u, getSLEB128Size(0x7fLL));EXPECT_EQ(2u, getSLEB128Size(0x80LL));EXPECT_EQ(2u, getSLEB128Size(0x1f80LL));EXPECT_EQ(2u, getSLEB128Size(0x1fffLL));EXPECT_EQ(3u, getSLEB128Size(0x2000LL));EXPECT_EQ(3u, getSLEB128Size(0x3fffLL));EXPECT_EQ(3u, getSLEB128Size(0x4000LL));EXPECT_EQ(3u, getSLEB128Size(0xfc000LL));EXPECT_EQ(3u, getSLEB128Size(0xfffffLL));EXPECT_EQ(4u, getSLEB128Size(0x100000LL));EXPECT_EQ(4u, getSLEB128Size(0x1fffffLL));EXPECT_EQ(4u, getSLEB128Size(0x200000LL));EXPECT_EQ(4u, getSLEB128Size(0x7e00000LL));EXPECT_EQ(4u, getSLEB128Size(0x7ffffffLL));EXPECT_EQ(5u, getSLEB128Size(0x8000000LL));EXPECT_EQ(5u, getSLEB128Size(0xfffffffLL));EXPECT_EQ(5u, getSLEB128Size(0x10000000LL));EXPECT_EQ(5u, getSLEB128Size(0x3f0000000LL));EXPECT_EQ(5u, getSLEB128Size(0x3ffffffffLL));EXPECT_EQ(6u, getSLEB128Size(0x400000000LL));EXPECT_EQ(6u, getSLEB128Size(0x7ffffffffLL));EXPECT_EQ(6u, getSLEB128Size(0x800000000LL));EXPECT_EQ(6u, getSLEB128Size(0x1f800000000LL));EXPECT_EQ(6u, getSLEB128Size(0x1ffffffffffLL));EXPECT_EQ(7u, getSLEB128Size(0x20000000000LL));EXPECT_EQ(7u, getSLEB128Size(0x3ffffffffffLL));EXPECT_EQ(7u, getSLEB128Size(0x40000000000LL));EXPECT_EQ(7u, getSLEB128Size(0xfc0000000000LL));EXPECT_EQ(7u, getSLEB128Size(0xffffffffffffLL));EXPECT_EQ(8u, getSLEB128Size(0x1000000000000LL));EXPECT_EQ(8u, getSLEB128Size(0x1ffffffffffffLL));EXPECT_EQ(8u, getSLEB128Size(0x2000000000000LL));EXPECT_EQ(8u, getSLEB128Size(0x7e000000000000LL));EXPECT_EQ(8u, getSLEB128Size(0x7fffffffffffffLL));EXPECT_EQ(9u, getSLEB128Size(0x80000000000000LL));EXPECT_EQ(9u, getSLEB128Size(0xffffffffffffffLL));EXPECT_EQ(9u, getSLEB128Size(0x100000000000000LL));EXPECT_EQ(9u, getSLEB128Size(0x3f00000000000000LL));EXPECT_EQ(9u, getSLEB128Size(0x3fffffffffffffffLL));EXPECT_EQ(10u, getSLEB128Size(0x4000000000000000LL));EXPECT_EQ(10u, getSLEB128Size(0x7fffffffffffffffLL));EXPECT_EQ(10u, getSLEB128Size(INT64_MAX));// Negative Value Testing Plan:// (1) - 128 ^ n - 1 ........ need (n+1) bytes// (2) - 128 ^ n ............ need (n+1) bytes// (3) - 128 ^ n * 63 ....... need (n+1) bytes// (4) - 128 ^ n * 64 ....... need (n+1) bytes (different from positive one)// (5) - 128 ^ n * 65 - 1 ... need (n+2) bytes (if n > 0)// (6) - 128 ^ n * 65 ....... need (n+2) bytesEXPECT_EQ(1u, getSLEB128Size(0x0LL));EXPECT_EQ(1u, getSLEB128Size(-0x1LL));EXPECT_EQ(1u, getSLEB128Size(-0x3fLL));EXPECT_EQ(1u, getSLEB128Size(-0x40LL));EXPECT_EQ(1u, getSLEB128Size(-0x40LL)); // special caseEXPECT_EQ(2u, getSLEB128Size(-0x41LL));EXPECT_EQ(2u, getSLEB128Size(-0x7fLL));EXPECT_EQ(2u, getSLEB128Size(-0x80LL));EXPECT_EQ(2u, getSLEB128Size(-0x1f80LL));EXPECT_EQ(2u, getSLEB128Size(-0x2000LL));EXPECT_EQ(3u, getSLEB128Size(-0x207fLL));EXPECT_EQ(3u, getSLEB128Size(-0x2080LL));EXPECT_EQ(3u, getSLEB128Size(-0x3fffLL));EXPECT_EQ(3u, getSLEB128Size(-0x4000LL));EXPECT_EQ(3u, getSLEB128Size(-0xfc000LL));EXPECT_EQ(3u, getSLEB128Size(-0x100000LL));EXPECT_EQ(4u, getSLEB128Size(-0x103fffLL));EXPECT_EQ(4u, getSLEB128Size(-0x104000LL));EXPECT_EQ(4u, getSLEB128Size(-0x1fffffLL));EXPECT_EQ(4u, getSLEB128Size(-0x200000LL));EXPECT_EQ(4u, getSLEB128Size(-0x7e00000LL));EXPECT_EQ(4u, getSLEB128Size(-0x8000000LL));EXPECT_EQ(5u, getSLEB128Size(-0x81fffffLL));EXPECT_EQ(5u, getSLEB128Size(-0x8200000LL));EXPECT_EQ(5u, getSLEB128Size(-0xfffffffLL));EXPECT_EQ(5u, getSLEB128Size(-0x10000000LL));EXPECT_EQ(5u, getSLEB128Size(-0x3f0000000LL));EXPECT_EQ(5u, getSLEB128Size(-0x400000000LL));EXPECT_EQ(6u, getSLEB128Size(-0x40fffffffLL));EXPECT_EQ(6u, getSLEB128Size(-0x410000000LL));EXPECT_EQ(6u, getSLEB128Size(-0x7ffffffffLL));EXPECT_EQ(6u, getSLEB128Size(-0x800000000LL));EXPECT_EQ(6u, getSLEB128Size(-0x1f800000000LL));EXPECT_EQ(6u, getSLEB128Size(-0x20000000000LL));EXPECT_EQ(7u, getSLEB128Size(-0x207ffffffffLL));EXPECT_EQ(7u, getSLEB128Size(-0x20800000000LL));EXPECT_EQ(7u, getSLEB128Size(-0x3ffffffffffLL));EXPECT_EQ(7u, getSLEB128Size(-0x40000000000LL));EXPECT_EQ(7u, getSLEB128Size(-0xfc0000000000LL));EXPECT_EQ(7u, getSLEB128Size(-0x1000000000000LL));EXPECT_EQ(8u, getSLEB128Size(-0x103ffffffffffLL));EXPECT_EQ(8u, getSLEB128Size(-0x1040000000000LL));EXPECT_EQ(8u, getSLEB128Size(-0x1ffffffffffffLL));EXPECT_EQ(8u, getSLEB128Size(-0x2000000000000LL));EXPECT_EQ(8u, getSLEB128Size(-0x7e000000000000LL));EXPECT_EQ(8u, getSLEB128Size(-0x80000000000000LL));EXPECT_EQ(9u, getSLEB128Size(-0x81ffffffffffffLL));EXPECT_EQ(9u, getSLEB128Size(-0x82000000000000LL));EXPECT_EQ(9u, getSLEB128Size(-0xffffffffffffffLL));EXPECT_EQ(9u, getSLEB128Size(-0x100000000000000LL));EXPECT_EQ(9u, getSLEB128Size(-0x3f00000000000000LL));EXPECT_EQ(9u, getSLEB128Size(-0x4000000000000000LL));EXPECT_EQ(10u, getSLEB128Size(-0x40ffffffffffffffLL));EXPECT_EQ(10u, getSLEB128Size(-0x4100000000000000LL));EXPECT_EQ(10u, getSLEB128Size(-0x7fffffffffffffffLL));EXPECT_EQ(10u, getSLEB128Size(-0x8000000000000000LL));EXPECT_EQ(10u, getSLEB128Size(INT64_MIN));}TEST(LEB128Test, ULEB128Size) {// Testing Plan:// (1) 128 ^ n ............ need (n+1) bytes// (2) 128 ^ n * 64 ....... need (n+1) bytes// (3) 128 ^ (n+1) - 1 .... need (n+1) bytesEXPECT_EQ(1u, getULEB128Size(0)); // special caseEXPECT_EQ(1u, getULEB128Size(0x1ULL));EXPECT_EQ(1u, getULEB128Size(0x40ULL));EXPECT_EQ(1u, getULEB128Size(0x7fULL));EXPECT_EQ(2u, getULEB128Size(0x80ULL));EXPECT_EQ(2u, getULEB128Size(0x2000ULL));EXPECT_EQ(2u, getULEB128Size(0x3fffULL));EXPECT_EQ(3u, getULEB128Size(0x4000ULL));EXPECT_EQ(3u, getULEB128Size(0x100000ULL));EXPECT_EQ(3u, getULEB128Size(0x1fffffULL));EXPECT_EQ(4u, getULEB128Size(0x200000ULL));EXPECT_EQ(4u, getULEB128Size(0x8000000ULL));EXPECT_EQ(4u, getULEB128Size(0xfffffffULL));EXPECT_EQ(5u, getULEB128Size(0x10000000ULL));EXPECT_EQ(5u, getULEB128Size(0x400000000ULL));EXPECT_EQ(5u, getULEB128Size(0x7ffffffffULL));EXPECT_EQ(6u, getULEB128Size(0x800000000ULL));EXPECT_EQ(6u, getULEB128Size(0x20000000000ULL));EXPECT_EQ(6u, getULEB128Size(0x3ffffffffffULL));EXPECT_EQ(7u, getULEB128Size(0x40000000000ULL));EXPECT_EQ(7u, getULEB128Size(0x1000000000000ULL));EXPECT_EQ(7u, getULEB128Size(0x1ffffffffffffULL));EXPECT_EQ(8u, getULEB128Size(0x2000000000000ULL));EXPECT_EQ(8u, getULEB128Size(0x80000000000000ULL));EXPECT_EQ(8u, getULEB128Size(0xffffffffffffffULL));EXPECT_EQ(9u, getULEB128Size(0x100000000000000ULL));EXPECT_EQ(9u, getULEB128Size(0x4000000000000000ULL));EXPECT_EQ(9u, getULEB128Size(0x7fffffffffffffffULL));EXPECT_EQ(10u, getULEB128Size(0x8000000000000000ULL));EXPECT_EQ(10u, getULEB128Size(UINT64_MAX));}} // anonymous namespace
//===- llvm/unittest/Support/KnownBitsTest.h - KnownBits tests ------------===////// 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////===----------------------------------------------------------------------===////// This file implements helpers for KnownBits and DemandedBits unit tests.////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_SUPPORT_KNOWNBITSTEST_H#define LLVM_UNITTESTS_SUPPORT_KNOWNBITSTEST_H#include "llvm/Support/KnownBits.h"namespace {using namespace llvm;template <typename FnTy> void ForeachKnownBits(unsigned Bits, FnTy Fn) {unsigned Max = 1 << Bits;KnownBits Known(Bits);for (unsigned Zero = 0; Zero < Max; ++Zero) {for (unsigned One = 0; One < Max; ++One) {Known.Zero = Zero;Known.One = One;if (Known.hasConflict())continue;Fn(Known);}}}template <typename FnTy>void ForeachNumInKnownBits(const KnownBits &Known, FnTy Fn) {unsigned Bits = Known.getBitWidth();unsigned Max = 1 << Bits;for (unsigned N = 0; N < Max; ++N) {APInt Num(Bits, N);if ((Num & Known.Zero) != 0 || (~Num & Known.One) != 0)continue;Fn(Num);}}} // end anonymous namespace#endif
//===- llvm/unittest/Support/KnownBitsTest.cpp - KnownBits tests ----------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for KnownBits functions.////===----------------------------------------------------------------------===//#include "llvm/Support/KnownBits.h"#include "KnownBitsTest.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(KnownBitsTest, AddCarryExhaustive) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known1) {ForeachKnownBits(Bits, [&](const KnownBits &Known2) {ForeachKnownBits(1, [&](const KnownBits &KnownCarry) {// Explicitly compute known bits of the addition by trying all// possibilities.KnownBits Known(Bits);Known.Zero.setAllBits();Known.One.setAllBits();ForeachNumInKnownBits(Known1, [&](const APInt &N1) {ForeachNumInKnownBits(Known2, [&](const APInt &N2) {ForeachNumInKnownBits(KnownCarry, [&](const APInt &Carry) {APInt Add = N1 + N2;if (Carry.getBoolValue())++Add;Known.One &= Add;Known.Zero &= ~Add;});});});KnownBits KnownComputed =KnownBits::computeForAddCarry(Known1, Known2, KnownCarry);EXPECT_EQ(Known, KnownComputed);});});});}static void TestAddSubExhaustive(bool IsAdd) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known1) {ForeachKnownBits(Bits, [&](const KnownBits &Known2) {KnownBits Known(Bits), KnownNSW(Bits);Known.Zero.setAllBits();Known.One.setAllBits();KnownNSW.Zero.setAllBits();KnownNSW.One.setAllBits();ForeachNumInKnownBits(Known1, [&](const APInt &N1) {ForeachNumInKnownBits(Known2, [&](const APInt &N2) {bool Overflow;APInt Res;if (IsAdd)Res = N1.sadd_ov(N2, Overflow);elseRes = N1.ssub_ov(N2, Overflow);Known.One &= Res;Known.Zero &= ~Res;if (!Overflow) {KnownNSW.One &= Res;KnownNSW.Zero &= ~Res;}});});KnownBits KnownComputed =KnownBits::computeForAddSub(IsAdd, /*NSW*/ false, Known1, Known2);EXPECT_EQ(Known, KnownComputed);// The NSW calculation is not precise, only check that it's// conservatively correct.KnownBits KnownNSWComputed = KnownBits::computeForAddSub(IsAdd, /*NSW*/true, Known1, Known2);EXPECT_TRUE(KnownNSWComputed.Zero.isSubsetOf(KnownNSW.Zero));EXPECT_TRUE(KnownNSWComputed.One.isSubsetOf(KnownNSW.One));});});}TEST(KnownBitsTest, AddSubExhaustive) {TestAddSubExhaustive(true);TestAddSubExhaustive(false);}TEST(KnownBitsTest, BinaryExhaustive) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known1) {ForeachKnownBits(Bits, [&](const KnownBits &Known2) {KnownBits KnownAnd(Bits);KnownAnd.Zero.setAllBits();KnownAnd.One.setAllBits();KnownBits KnownOr(KnownAnd);KnownBits KnownXor(KnownAnd);KnownBits KnownUMax(KnownAnd);KnownBits KnownUMin(KnownAnd);KnownBits KnownSMax(KnownAnd);KnownBits KnownSMin(KnownAnd);KnownBits KnownMul(KnownAnd);KnownBits KnownMulHS(KnownAnd);KnownBits KnownMulHU(KnownAnd);KnownBits KnownUDiv(KnownAnd);KnownBits KnownURem(KnownAnd);KnownBits KnownSRem(KnownAnd);KnownBits KnownShl(KnownAnd);KnownBits KnownLShr(KnownAnd);KnownBits KnownAShr(KnownAnd);ForeachNumInKnownBits(Known1, [&](const APInt &N1) {ForeachNumInKnownBits(Known2, [&](const APInt &N2) {APInt Res;Res = N1 & N2;KnownAnd.One &= Res;KnownAnd.Zero &= ~Res;Res = N1 | N2;KnownOr.One &= Res;KnownOr.Zero &= ~Res;Res = N1 ^ N2;KnownXor.One &= Res;KnownXor.Zero &= ~Res;Res = APIntOps::umax(N1, N2);KnownUMax.One &= Res;KnownUMax.Zero &= ~Res;Res = APIntOps::umin(N1, N2);KnownUMin.One &= Res;KnownUMin.Zero &= ~Res;Res = APIntOps::smax(N1, N2);KnownSMax.One &= Res;KnownSMax.Zero &= ~Res;Res = APIntOps::smin(N1, N2);KnownSMin.One &= Res;KnownSMin.Zero &= ~Res;Res = N1 * N2;KnownMul.One &= Res;KnownMul.Zero &= ~Res;Res = (N1.sext(2 * Bits) * N2.sext(2 * Bits)).extractBits(Bits, Bits);KnownMulHS.One &= Res;KnownMulHS.Zero &= ~Res;Res = (N1.zext(2 * Bits) * N2.zext(2 * Bits)).extractBits(Bits, Bits);KnownMulHU.One &= Res;KnownMulHU.Zero &= ~Res;if (!N2.isZero()) {Res = N1.udiv(N2);KnownUDiv.One &= Res;KnownUDiv.Zero &= ~Res;Res = N1.urem(N2);KnownURem.One &= Res;KnownURem.Zero &= ~Res;Res = N1.srem(N2);KnownSRem.One &= Res;KnownSRem.Zero &= ~Res;}if (N2.ult(1ULL << N1.getBitWidth())) {Res = N1.shl(N2);KnownShl.One &= Res;KnownShl.Zero &= ~Res;Res = N1.lshr(N2);KnownLShr.One &= Res;KnownLShr.Zero &= ~Res;Res = N1.ashr(N2);KnownAShr.One &= Res;KnownAShr.Zero &= ~Res;} else {KnownShl.resetAll();KnownLShr.resetAll();KnownAShr.resetAll();}});});KnownBits ComputedAnd = Known1 & Known2;EXPECT_EQ(KnownAnd, ComputedAnd);KnownBits ComputedOr = Known1 | Known2;EXPECT_EQ(KnownOr, ComputedOr);KnownBits ComputedXor = Known1 ^ Known2;EXPECT_EQ(KnownXor, ComputedXor);KnownBits ComputedUMax = KnownBits::umax(Known1, Known2);EXPECT_EQ(KnownUMax, ComputedUMax);KnownBits ComputedUMin = KnownBits::umin(Known1, Known2);EXPECT_EQ(KnownUMin, ComputedUMin);KnownBits ComputedSMax = KnownBits::smax(Known1, Known2);EXPECT_EQ(KnownSMax, ComputedSMax);KnownBits ComputedSMin = KnownBits::smin(Known1, Known2);EXPECT_EQ(KnownSMin, ComputedSMin);// The following are conservatively correct, but not guaranteed to be// precise.KnownBits ComputedMul = KnownBits::mul(Known1, Known2);EXPECT_TRUE(ComputedMul.Zero.isSubsetOf(KnownMul.Zero));EXPECT_TRUE(ComputedMul.One.isSubsetOf(KnownMul.One));KnownBits ComputedMulHS = KnownBits::mulhs(Known1, Known2);EXPECT_TRUE(ComputedMulHS.Zero.isSubsetOf(KnownMulHS.Zero));EXPECT_TRUE(ComputedMulHS.One.isSubsetOf(KnownMulHS.One));KnownBits ComputedMulHU = KnownBits::mulhu(Known1, Known2);EXPECT_TRUE(ComputedMulHU.Zero.isSubsetOf(KnownMulHU.Zero));EXPECT_TRUE(ComputedMulHU.One.isSubsetOf(KnownMulHU.One));KnownBits ComputedUDiv = KnownBits::udiv(Known1, Known2);EXPECT_TRUE(ComputedUDiv.Zero.isSubsetOf(KnownUDiv.Zero));EXPECT_TRUE(ComputedUDiv.One.isSubsetOf(KnownUDiv.One));KnownBits ComputedURem = KnownBits::urem(Known1, Known2);EXPECT_TRUE(ComputedURem.Zero.isSubsetOf(KnownURem.Zero));EXPECT_TRUE(ComputedURem.One.isSubsetOf(KnownURem.One));KnownBits ComputedSRem = KnownBits::srem(Known1, Known2);EXPECT_TRUE(ComputedSRem.Zero.isSubsetOf(KnownSRem.Zero));EXPECT_TRUE(ComputedSRem.One.isSubsetOf(KnownSRem.One));KnownBits ComputedShl = KnownBits::shl(Known1, Known2);EXPECT_TRUE(ComputedShl.Zero.isSubsetOf(KnownShl.Zero));EXPECT_TRUE(ComputedShl.One.isSubsetOf(KnownShl.One));KnownBits ComputedLShr = KnownBits::lshr(Known1, Known2);EXPECT_TRUE(ComputedLShr.Zero.isSubsetOf(KnownLShr.Zero));EXPECT_TRUE(ComputedLShr.One.isSubsetOf(KnownLShr.One));KnownBits ComputedAShr = KnownBits::ashr(Known1, Known2);EXPECT_TRUE(ComputedAShr.Zero.isSubsetOf(KnownAShr.Zero));EXPECT_TRUE(ComputedAShr.One.isSubsetOf(KnownAShr.One));});});// Also test 'unary' binary cases where the same argument is repeated.ForeachKnownBits(Bits, [&](const KnownBits &Known) {KnownBits KnownMul(Bits);KnownMul.Zero.setAllBits();KnownMul.One.setAllBits();ForeachNumInKnownBits(Known, [&](const APInt &N) {APInt Res = N * N;KnownMul.One &= Res;KnownMul.Zero &= ~Res;});KnownBits ComputedMul = KnownBits::mul(Known, Known, /*SelfMultiply*/ true);EXPECT_TRUE(ComputedMul.Zero.isSubsetOf(KnownMul.Zero));EXPECT_TRUE(ComputedMul.One.isSubsetOf(KnownMul.One));});}TEST(KnownBitsTest, UnaryExhaustive) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known) {KnownBits KnownAbs(Bits);KnownAbs.Zero.setAllBits();KnownAbs.One.setAllBits();KnownBits KnownAbsPoison(KnownAbs);ForeachNumInKnownBits(Known, [&](const APInt &N) {APInt Res = N.abs();KnownAbs.One &= Res;KnownAbs.Zero &= ~Res;if (!N.isMinSignedValue()) {KnownAbsPoison.One &= Res;KnownAbsPoison.Zero &= ~Res;}});// abs() is conservatively correct, but not guaranteed to be precise.KnownBits ComputedAbs = Known.abs();EXPECT_TRUE(ComputedAbs.Zero.isSubsetOf(KnownAbs.Zero));EXPECT_TRUE(ComputedAbs.One.isSubsetOf(KnownAbs.One));KnownBits ComputedAbsPoison = Known.abs(true);EXPECT_TRUE(ComputedAbsPoison.Zero.isSubsetOf(KnownAbsPoison.Zero));EXPECT_TRUE(ComputedAbsPoison.One.isSubsetOf(KnownAbsPoison.One));});}TEST(KnownBitsTest, ICmpExhaustive) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known1) {ForeachKnownBits(Bits, [&](const KnownBits &Known2) {bool AllEQ = true, NoneEQ = true;bool AllNE = true, NoneNE = true;bool AllUGT = true, NoneUGT = true;bool AllUGE = true, NoneUGE = true;bool AllULT = true, NoneULT = true;bool AllULE = true, NoneULE = true;bool AllSGT = true, NoneSGT = true;bool AllSGE = true, NoneSGE = true;bool AllSLT = true, NoneSLT = true;bool AllSLE = true, NoneSLE = true;ForeachNumInKnownBits(Known1, [&](const APInt &N1) {ForeachNumInKnownBits(Known2, [&](const APInt &N2) {AllEQ &= N1.eq(N2);AllNE &= N1.ne(N2);AllUGT &= N1.ugt(N2);AllUGE &= N1.uge(N2);AllULT &= N1.ult(N2);AllULE &= N1.ule(N2);AllSGT &= N1.sgt(N2);AllSGE &= N1.sge(N2);AllSLT &= N1.slt(N2);AllSLE &= N1.sle(N2);NoneEQ &= !N1.eq(N2);NoneNE &= !N1.ne(N2);NoneUGT &= !N1.ugt(N2);NoneUGE &= !N1.uge(N2);NoneULT &= !N1.ult(N2);NoneULE &= !N1.ule(N2);NoneSGT &= !N1.sgt(N2);NoneSGE &= !N1.sge(N2);NoneSLT &= !N1.slt(N2);NoneSLE &= !N1.sle(N2);});});Optional<bool> KnownEQ = KnownBits::eq(Known1, Known2);Optional<bool> KnownNE = KnownBits::ne(Known1, Known2);Optional<bool> KnownUGT = KnownBits::ugt(Known1, Known2);Optional<bool> KnownUGE = KnownBits::uge(Known1, Known2);Optional<bool> KnownULT = KnownBits::ult(Known1, Known2);Optional<bool> KnownULE = KnownBits::ule(Known1, Known2);Optional<bool> KnownSGT = KnownBits::sgt(Known1, Known2);Optional<bool> KnownSGE = KnownBits::sge(Known1, Known2);Optional<bool> KnownSLT = KnownBits::slt(Known1, Known2);Optional<bool> KnownSLE = KnownBits::sle(Known1, Known2);EXPECT_EQ(AllEQ || NoneEQ, KnownEQ.has_value());EXPECT_EQ(AllNE || NoneNE, KnownNE.has_value());EXPECT_EQ(AllUGT || NoneUGT, KnownUGT.has_value());EXPECT_EQ(AllUGE || NoneUGE, KnownUGE.has_value());EXPECT_EQ(AllULT || NoneULT, KnownULT.has_value());EXPECT_EQ(AllULE || NoneULE, KnownULE.has_value());EXPECT_EQ(AllSGT || NoneSGT, KnownSGT.has_value());EXPECT_EQ(AllSGE || NoneSGE, KnownSGE.has_value());EXPECT_EQ(AllSLT || NoneSLT, KnownSLT.has_value());EXPECT_EQ(AllSLE || NoneSLE, KnownSLE.has_value());EXPECT_EQ(AllEQ, KnownEQ.has_value() && KnownEQ.value());EXPECT_EQ(AllNE, KnownNE.has_value() && KnownNE.value());EXPECT_EQ(AllUGT, KnownUGT.has_value() && KnownUGT.value());EXPECT_EQ(AllUGE, KnownUGE.has_value() && KnownUGE.value());EXPECT_EQ(AllULT, KnownULT.has_value() && KnownULT.value());EXPECT_EQ(AllULE, KnownULE.has_value() && KnownULE.value());EXPECT_EQ(AllSGT, KnownSGT.has_value() && KnownSGT.value());EXPECT_EQ(AllSGE, KnownSGE.has_value() && KnownSGE.value());EXPECT_EQ(AllSLT, KnownSLT.has_value() && KnownSLT.value());EXPECT_EQ(AllSLE, KnownSLE.has_value() && KnownSLE.value());EXPECT_EQ(NoneEQ, KnownEQ.has_value() && !KnownEQ.value());EXPECT_EQ(NoneNE, KnownNE.has_value() && !KnownNE.value());EXPECT_EQ(NoneUGT, KnownUGT.has_value() && !KnownUGT.value());EXPECT_EQ(NoneUGE, KnownUGE.has_value() && !KnownUGE.value());EXPECT_EQ(NoneULT, KnownULT.has_value() && !KnownULT.value());EXPECT_EQ(NoneULE, KnownULE.has_value() && !KnownULE.value());EXPECT_EQ(NoneSGT, KnownSGT.has_value() && !KnownSGT.value());EXPECT_EQ(NoneSGE, KnownSGE.has_value() && !KnownSGE.value());EXPECT_EQ(NoneSLT, KnownSLT.has_value() && !KnownSLT.value());EXPECT_EQ(NoneSLE, KnownSLE.has_value() && !KnownSLE.value());});});}TEST(KnownBitsTest, GetMinMaxVal) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known) {APInt Min = APInt::getMaxValue(Bits);APInt Max = APInt::getMinValue(Bits);ForeachNumInKnownBits(Known, [&](const APInt &N) {Min = APIntOps::umin(Min, N);Max = APIntOps::umax(Max, N);});EXPECT_EQ(Min, Known.getMinValue());EXPECT_EQ(Max, Known.getMaxValue());});}TEST(KnownBitsTest, GetSignedMinMaxVal) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known) {APInt Min = APInt::getSignedMaxValue(Bits);APInt Max = APInt::getSignedMinValue(Bits);ForeachNumInKnownBits(Known, [&](const APInt &N) {Min = APIntOps::smin(Min, N);Max = APIntOps::smax(Max, N);});EXPECT_EQ(Min, Known.getSignedMinValue());EXPECT_EQ(Max, Known.getSignedMaxValue());});}TEST(KnownBitsTest, CountMaxActiveBits) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known) {unsigned Expected = 0;ForeachNumInKnownBits(Known, [&](const APInt &N) {Expected = std::max(Expected, N.getActiveBits());});EXPECT_EQ(Expected, Known.countMaxActiveBits());});}TEST(KnownBitsTest, CountMaxSignificantBits) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known) {unsigned Expected = 0;ForeachNumInKnownBits(Known, [&](const APInt &N) {Expected = std::max(Expected, N.getSignificantBits());});EXPECT_EQ(Expected, Known.countMaxSignificantBits());});}TEST(KnownBitsTest, SExtOrTrunc) {const unsigned NarrowerSize = 4;const unsigned BaseSize = 6;const unsigned WiderSize = 8;APInt NegativeFitsNarrower(BaseSize, -4, /*isSigned*/ true);APInt NegativeDoesntFitNarrower(BaseSize, -28, /*isSigned*/ true);APInt PositiveFitsNarrower(BaseSize, 14);APInt PositiveDoesntFitNarrower(BaseSize, 36);auto InitKnownBits = [&](KnownBits &Res, const APInt &Input) {Res = KnownBits(Input.getBitWidth());Res.One = Input;Res.Zero = ~Input;};for (unsigned Size : {NarrowerSize, BaseSize, WiderSize}) {for (const APInt &Input :{NegativeFitsNarrower, NegativeDoesntFitNarrower, PositiveFitsNarrower,PositiveDoesntFitNarrower}) {KnownBits Test;InitKnownBits(Test, Input);KnownBits Baseline;InitKnownBits(Baseline, Input.sextOrTrunc(Size));Test = Test.sextOrTrunc(Size);EXPECT_EQ(Test, Baseline);}}}TEST(KnownBitsTest, SExtInReg) {unsigned Bits = 4;for (unsigned FromBits = 1; FromBits <= Bits; ++FromBits) {ForeachKnownBits(Bits, [&](const KnownBits &Known) {APInt CommonOne = APInt::getAllOnes(Bits);APInt CommonZero = APInt::getAllOnes(Bits);unsigned ExtBits = Bits - FromBits;ForeachNumInKnownBits(Known, [&](const APInt &N) {APInt Ext = N << ExtBits;Ext.ashrInPlace(ExtBits);CommonOne &= Ext;CommonZero &= ~Ext;});KnownBits KnownSExtInReg = Known.sextInReg(FromBits);EXPECT_EQ(CommonOne, KnownSExtInReg.One);EXPECT_EQ(CommonZero, KnownSExtInReg.Zero);});}}TEST(KnownBitsTest, CommonBitsSet) {unsigned Bits = 4;ForeachKnownBits(Bits, [&](const KnownBits &Known1) {ForeachKnownBits(Bits, [&](const KnownBits &Known2) {bool HasCommonBitsSet = false;ForeachNumInKnownBits(Known1, [&](const APInt &N1) {ForeachNumInKnownBits(Known2, [&](const APInt &N2) {HasCommonBitsSet |= N1.intersects(N2);});});EXPECT_EQ(!HasCommonBitsSet,KnownBits::haveNoCommonBitsSet(Known1, Known2));});});}} // end anonymous namespace
//===-- JSONTest.cpp - JSON unit tests --------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/JSON.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/Error.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace llvm {namespace json {namespace {std::string s(const Value &E) { return llvm::formatv("{0}", E).str(); }std::string sp(const Value &E) { return llvm::formatv("{0:2}", E).str(); }TEST(JSONTest, Types) {EXPECT_EQ("true", s(true));EXPECT_EQ("null", s(nullptr));EXPECT_EQ("2.5", s(2.5));EXPECT_EQ(R"("foo")", s("foo"));EXPECT_EQ("[1,2,3]", s({1, 2, 3}));EXPECT_EQ(R"({"x":10,"y":20})", s(Object{{"x", 10}, {"y", 20}}));#ifdef NDEBUGEXPECT_EQ(R"("��")", s("\xC0\x80"));EXPECT_EQ(R"({"��":0})", s(Object{{"\xC0\x80", 0}}));#elseEXPECT_DEATH(s("\xC0\x80"), "Invalid UTF-8");EXPECT_DEATH(s(Object{{"\xC0\x80", 0}}), "Invalid UTF-8");#endif}TEST(JSONTest, Constructors) {// Lots of edge cases around empty and singleton init lists.EXPECT_EQ("[[[3]]]", s({{{3}}}));EXPECT_EQ("[[[]]]", s({{{}}}));EXPECT_EQ("[[{}]]", s({{Object{}}}));EXPECT_EQ(R"({"A":{"B":{}}})", s(Object{{"A", Object{{"B", Object{}}}}}));EXPECT_EQ(R"({"A":{"B":{"X":"Y"}}})",s(Object{{"A", Object{{"B", Object{{"X", "Y"}}}}}}));EXPECT_EQ("null", s(llvm::Optional<double>()));EXPECT_EQ("2.5", s(llvm::Optional<double>(2.5)));EXPECT_EQ("[[2.5,null]]", s(std::vector<std::vector<llvm::Optional<double>>>{{2.5, llvm::None}}));}TEST(JSONTest, StringOwnership) {char X[] = "Hello";Value Alias = static_cast<const char *>(X);X[1] = 'a';EXPECT_EQ(R"("Hallo")", s(Alias));std::string Y = "Hello";Value Copy = Y;Y[1] = 'a';EXPECT_EQ(R"("Hello")", s(Copy));}TEST(JSONTest, CanonicalOutput) {// Objects are sorted (but arrays aren't)!EXPECT_EQ(R"({"a":1,"b":2,"c":3})", s(Object{{"a", 1}, {"c", 3}, {"b", 2}}));EXPECT_EQ(R"(["a","c","b"])", s({"a", "c", "b"}));EXPECT_EQ("3", s(3.0));}TEST(JSONTest, Escaping) {std::string Test = {0, // Strings may contain nulls.'\b', '\f', // Have mnemonics, but we escape numerically.'\r', '\n', '\t', // Escaped with mnemonics.'S', '\"', '\\', // Printable ASCII characters.'\x7f', // Delete is not escaped.'\xce', '\x94', // Non-ASCII UTF-8 is not escaped.};std::string TestString = R"("\u0000\u0008\u000c\r\n\tS\"\\)""\x7f\xCE\x94\"";EXPECT_EQ(TestString, s(Test));EXPECT_EQ(R"({"object keys are\nescaped":true})",s(Object{{"object keys are\nescaped", true}}));}TEST(JSONTest, PrettyPrinting) {const char Str[] = R"({"empty_array": [],"empty_object": {},"full_array": [1,null],"full_object": {"nested_array": [{"property": "value"}]}})";EXPECT_EQ(Str, sp(Object{{"empty_object", Object{}},{"empty_array", {}},{"full_array", {1, nullptr}},{"full_object",Object{{"nested_array",{Object{{"property", "value"},}}},}},}));}TEST(JSONTest, Array) {Array A{1, 2};A.emplace_back(3);A.emplace(++A.begin(), 0);A.push_back(4);A.insert(++++A.begin(), 99);EXPECT_EQ(A.size(), 6u);EXPECT_EQ(R"([1,0,99,2,3,4])", s(std::move(A)));}TEST(JSONTest, Object) {Object O{{"a", 1}, {"b", 2}, {"c", 3}};EXPECT_TRUE(O.try_emplace("d", 4).second);EXPECT_FALSE(O.try_emplace("a", 4).second);auto D = O.find("d");EXPECT_NE(D, O.end());auto E = O.find("e");EXPECT_EQ(E, O.end());O.erase("b");O.erase(D);EXPECT_EQ(O.size(), 2u);EXPECT_EQ(R"({"a":1,"c":3})", s(std::move(O)));}TEST(JSONTest, Parse) {auto Compare = [](llvm::StringRef S, Value Expected) {if (auto E = parse(S)) {// Compare both string forms and with operator==, in case we have bugs.EXPECT_EQ(*E, Expected);EXPECT_EQ(sp(*E), sp(Expected));} else {handleAllErrors(E.takeError(), [S](const llvm::ErrorInfoBase &E) {FAIL() << "Failed to parse JSON >>> " << S << " <<<: " << E.message();});}};Compare(R"(true)", true);Compare(R"(false)", false);Compare(R"(null)", nullptr);Compare(R"(42)", 42);Compare(R"(2.5)", 2.5);Compare(R"(2e50)", 2e50);Compare(R"(1.2e3456789)", std::numeric_limits<double>::infinity());Compare(R"("foo")", "foo");Compare(R"("\"\\\b\f\n\r\t")", "\"\\\b\f\n\r\t");Compare(R"("\u0000")", llvm::StringRef("\0", 1));Compare("\"\x7f\"", "\x7f");Compare(R"("\ud801\udc37")", u8"\U00010437"); // UTF16 surrogate pair escape.Compare("\"\xE2\x82\xAC\xF0\x9D\x84\x9E\"", u8"\u20ac\U0001d11e"); // UTF8Compare(R"("LoneLeading=\ud801, LoneTrailing=\udc01, LeadingLeadingTrailing=\ud801\ud801\udc37")",u8"LoneLeading=\ufffd, LoneTrailing=\ufffd, "u8"LeadingLeadingTrailing=\ufffd\U00010437"); // Invalid unicode.Compare(R"({"":0,"":0})", Object{{"", 0}});Compare(R"({"obj":{},"arr":[]})", Object{{"obj", Object{}}, {"arr", {}}});Compare(R"({"\n":{"\u0000":[[[[]]]]}})",Object{{"\n", Object{{llvm::StringRef("\0", 1), {{{{}}}}},}}});Compare("\r[\n\t] ", {});}TEST(JSONTest, ParseErrors) {auto ExpectErr = [](llvm::StringRef Msg, llvm::StringRef S) {if (auto E = parse(S)) {// Compare both string forms and with operator==, in case we have bugs.FAIL() << "Parsed JSON >>> " << S << " <<< but wanted error: " << Msg;} else {handleAllErrors(E.takeError(), [S, Msg](const llvm::ErrorInfoBase &E) {EXPECT_THAT(E.message(), testing::HasSubstr(std::string(Msg))) << S;});}};ExpectErr("Unexpected EOF", "");ExpectErr("Unexpected EOF", "[");ExpectErr("Text after end of document", "[][]");ExpectErr("Invalid JSON value (false?)", "fuzzy");ExpectErr("Expected , or ]", "[2?]");ExpectErr("Expected object key", "{a:2}");ExpectErr("Expected : after object key", R"({"a",2})");ExpectErr("Expected , or } after object property", R"({"a":2 "b":3})");ExpectErr("Invalid JSON value", R"([&%!])");ExpectErr("Invalid JSON value (number?)", "1e1.0");ExpectErr("Unterminated string", R"("abc\"def)");ExpectErr("Control character in string", "\"abc\ndef\"");ExpectErr("Invalid escape sequence", R"("\030")");ExpectErr("Invalid \\u escape sequence", R"("\usuck")");ExpectErr("[3:3, byte=19]", R"({"valid": 1,invalid: 2})");ExpectErr("Invalid UTF-8 sequence", "\"\xC0\x80\""); // WTF-8 null}// Direct tests of isUTF8 and fixUTF8. Internal uses are also tested elsewhere.TEST(JSONTest, UTF8) {for (const char *Valid : {"this is ASCII text","thïs tëxt häs BMP chäräctërs","𐌶𐌰L𐌾𐍈 C𐍈𐌼𐌴𐍃",}) {EXPECT_TRUE(isUTF8(Valid)) << Valid;EXPECT_EQ(fixUTF8(Valid), Valid);}for (auto Invalid : std::vector<std::pair<const char *, const char *>>{{"lone trailing \x81\x82 bytes", "lone trailing �� bytes"},{"missing trailing \xD0 bytes", "missing trailing � bytes"},{"truncated character \xD0", "truncated character �"},{"not \xC1\x80 the \xE0\x9f\xBF shortest \xF0\x83\x83\x83 encoding","not �� the ��� shortest ���� encoding"},{"too \xF9\x80\x80\x80\x80 long", "too ����� long"},{"surrogate \xED\xA0\x80 invalid \xF4\x90\x80\x80","surrogate ��� invalid ����"}}) {EXPECT_FALSE(isUTF8(Invalid.first)) << Invalid.first;EXPECT_EQ(fixUTF8(Invalid.first), Invalid.second);}}TEST(JSONTest, Inspection) {llvm::Expected<Value> Doc = parse(R"({"null": null,"boolean": false,"number": 2.78,"string": "json","array": [null, true, 3.14, "hello", [1,2,3], {"time": "arrow"}],"object": {"fruit": "banana"}})");EXPECT_TRUE(!!Doc);Object *O = Doc->getAsObject();ASSERT_TRUE(O);EXPECT_FALSE(O->getNull("missing"));EXPECT_FALSE(O->getNull("boolean"));EXPECT_TRUE(O->getNull("null"));EXPECT_EQ(O->getNumber("number"), llvm::Optional<double>(2.78));EXPECT_FALSE(O->getInteger("number"));EXPECT_EQ(O->getString("string"), llvm::Optional<llvm::StringRef>("json"));ASSERT_FALSE(O->getObject("missing"));ASSERT_FALSE(O->getObject("array"));ASSERT_TRUE(O->getObject("object"));EXPECT_EQ(*O->getObject("object"), (Object{{"fruit", "banana"}}));Array *A = O->getArray("array");ASSERT_TRUE(A);EXPECT_EQ((*A)[1].getAsBoolean(), llvm::Optional<bool>(true));ASSERT_TRUE((*A)[4].getAsArray());EXPECT_EQ(*(*A)[4].getAsArray(), (Array{1, 2, 3}));EXPECT_EQ((*(*A)[4].getAsArray())[1].getAsInteger(),llvm::Optional<int64_t>(2));int I = 0;for (Value &E : *A) {if (I++ == 5) {ASSERT_TRUE(E.getAsObject());EXPECT_EQ(E.getAsObject()->getString("time"),llvm::Optional<llvm::StringRef>("arrow"));} elseEXPECT_FALSE(E.getAsObject());}}// Verify special integer handling - we try to preserve exact int64 values.TEST(JSONTest, Integers) {struct {const char *Desc;Value Val;const char *Str;llvm::Optional<int64_t> AsInt;llvm::Optional<double> AsNumber;} TestCases[] = {{"Non-integer. Stored as double, not convertible.",double{1.5},"1.5",llvm::None,1.5,},{"Integer, not exact double. Stored as int64, convertible.",int64_t{0x4000000000000001},"4611686018427387905",int64_t{0x4000000000000001},double{0x4000000000000000},},{"Negative integer, not exact double. Stored as int64, convertible.",int64_t{-0x4000000000000001},"-4611686018427387905",int64_t{-0x4000000000000001},double{-0x4000000000000000},},// PR46470,// https://developercommunity.visualstudio.com/content/problem/1093399/incorrect-result-when-printing-6917529027641081856.html#if !defined(_MSC_VER) || _MSC_VER < 1926{"Dynamically exact integer. Stored as double, convertible.",double{0x6000000000000000},"6.9175290276410819e+18",int64_t{0x6000000000000000},double{0x6000000000000000},},#endif{"Dynamically integer, >64 bits. Stored as double, not convertible.",1.5 * double{0x8000000000000000},"1.3835058055282164e+19",llvm::None,1.5 * double{0x8000000000000000},},};for (const auto &T : TestCases) {EXPECT_EQ(T.Str, s(T.Val)) << T.Desc;llvm::Expected<Value> Doc = parse(T.Str);EXPECT_TRUE(!!Doc) << T.Desc;EXPECT_EQ(Doc->getAsInteger(), T.AsInt) << T.Desc;EXPECT_EQ(Doc->getAsNumber(), T.AsNumber) << T.Desc;EXPECT_EQ(T.Val, *Doc) << T.Desc;EXPECT_EQ(T.Str, s(*Doc)) << T.Desc;}}// Verify uint64_t type.TEST(JSONTest, U64Integers) {Value Val = uint64_t{3100100100};uint64_t Var = 3100100100;EXPECT_EQ(Val, Var);Val = uint64_t{std::numeric_limits<uint64_t>::max()};Var = std::numeric_limits<uint64_t>::max();EXPECT_EQ(Val, Var);// Test the parse() part.{const char *Str = "4611686018427387905";llvm::Expected<Value> Doc = parse(Str);EXPECT_TRUE(!!Doc);EXPECT_EQ(Doc->getAsInteger(), int64_t{4611686018427387905});EXPECT_EQ(Doc->getAsUINT64(), uint64_t{4611686018427387905});}{const char *Str = "-78278238238328222";llvm::Expected<Value> Doc = parse(Str);EXPECT_TRUE(!!Doc);EXPECT_EQ(Doc->getAsInteger(), int64_t{-78278238238328222});EXPECT_EQ(Doc->getAsUINT64(), llvm::None);}// Test with the largest 64 signed int.{const char *Str = "9223372036854775807";llvm::Expected<Value> Doc = parse(Str);EXPECT_TRUE(!!Doc);EXPECT_EQ(Doc->getAsInteger(), int64_t{9223372036854775807});EXPECT_EQ(Doc->getAsUINT64(), uint64_t{9223372036854775807});}// Test with the largest 64 unsigned int.{const char *Str = "18446744073709551615";llvm::Expected<Value> Doc = parse(Str);EXPECT_TRUE(!!Doc);EXPECT_EQ(Doc->getAsInteger(), None);EXPECT_EQ(Doc->getAsUINT64(), uint64_t{18446744073709551615u});}// Test with a number that is too big for 64 bits.{const char *Str = "184467440737095516150";llvm::Expected<Value> Doc = parse(Str);EXPECT_TRUE(!!Doc);EXPECT_EQ(Doc->getAsInteger(), None);EXPECT_EQ(Doc->getAsUINT64(), None);// The number was parsed as a double.EXPECT_TRUE(!!Doc->getAsNumber());}// Test with a negative number that is too small for 64 bits.{const char *Str = "-18446744073709551615";llvm::Expected<Value> Doc = parse(Str);EXPECT_TRUE(!!Doc);EXPECT_EQ(Doc->getAsInteger(), None);EXPECT_EQ(Doc->getAsUINT64(), None);// The number was parsed as a double.EXPECT_TRUE(!!Doc->getAsNumber());}// Test with a large number that is malformed.{const char *Str = "184467440737095516150.12.12";llvm::Expected<Value> Doc = parse(Str);EXPECT_EQ("[1:27, byte=27]: Invalid JSON value (number?)",llvm::toString(Doc.takeError()));}}// Sample struct with typical JSON-mapping rules.struct CustomStruct {CustomStruct() : B(false) {}CustomStruct(std::string S, llvm::Optional<int> I, bool B): S(S), I(I), B(B) {}std::string S;llvm::Optional<int> I;bool B;};inline bool operator==(const CustomStruct &L, const CustomStruct &R) {return L.S == R.S && L.I == R.I && L.B == R.B;}inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,const CustomStruct &S) {return OS << "(" << S.S << ", " << (S.I ? std::to_string(*S.I) : "None")<< ", " << S.B << ")";}bool fromJSON(const Value &E, CustomStruct &R, Path P) {ObjectMapper O(E, P);return O && O.map("str", R.S) && O.map("int", R.I) &&O.mapOptional("bool", R.B);}static std::string errorContext(const Value &V, const Path::Root &R) {std::string Context;llvm::raw_string_ostream OS(Context);R.printErrorContext(V, OS);return OS.str();}TEST(JSONTest, Deserialize) {std::map<std::string, std::vector<CustomStruct>> R;CustomStruct ExpectedStruct = {"foo", 42, true};std::map<std::string, std::vector<CustomStruct>> Expected;Value J = Object{{"foo", Array{Object{{"str", "foo"},{"int", 42},{"bool", true},{"unknown", "ignored"},},Object{{"str", "bar"}},}}};Expected["foo"] = {CustomStruct("foo", 42, true),CustomStruct("bar", llvm::None, false),};Path::Root Root("CustomStruct");ASSERT_TRUE(fromJSON(J, R, Root));EXPECT_EQ(R, Expected);(*J.getAsObject()->getArray("foo"))[0] = 123;ASSERT_FALSE(fromJSON(J, R, Root));EXPECT_EQ("expected object at CustomStruct.foo[0]",toString(Root.getError()));const char *ExpectedDump = R"({"foo": [/* error: expected object */123,{ ... }]})";EXPECT_EQ(ExpectedDump, errorContext(J, Root));CustomStruct V;EXPECT_FALSE(fromJSON(nullptr, V, Root));EXPECT_EQ("expected object when parsing CustomStruct",toString(Root.getError()));EXPECT_FALSE(fromJSON(Object{}, V, Root));EXPECT_EQ("missing value at CustomStruct.str", toString(Root.getError()));EXPECT_FALSE(fromJSON(Object{{"str", 1}}, V, Root));EXPECT_EQ("expected string at CustomStruct.str", toString(Root.getError()));// Optional<T> must parse as the correct type if present.EXPECT_FALSE(fromJSON(Object{{"str", "1"}, {"int", "string"}}, V, Root));EXPECT_EQ("expected integer at CustomStruct.int", toString(Root.getError()));// mapOptional must parse as the correct type if present.EXPECT_FALSE(fromJSON(Object{{"str", "1"}, {"bool", "string"}}, V, Root));EXPECT_EQ("expected boolean at CustomStruct.bool", toString(Root.getError()));}TEST(JSONTest, ParseDeserialize) {auto E = parse<std::vector<CustomStruct>>(R"json([{"str": "foo", "int": 42}, {"int": 42}])json");EXPECT_THAT_EXPECTED(E, FailedWithMessage("missing value at (root)[1].str"));E = parse<std::vector<CustomStruct>>(R"json([{"str": "foo", "int": 42}, {"str": "bar"})json");EXPECT_THAT_EXPECTED(E,FailedWithMessage("[3:2, byte=50]: Expected , or ] after array element"));E = parse<std::vector<CustomStruct>>(R"json([{"str": "foo", "int": 42}])json");EXPECT_THAT_EXPECTED(E, Succeeded());EXPECT_THAT(*E, testing::SizeIs(1));}TEST(JSONTest, Stream) {auto StreamStuff = [](unsigned Indent) {std::string S;llvm::raw_string_ostream OS(S);OStream J(OS, Indent);J.comment("top*/level");J.object([&] {J.attributeArray("foo", [&] {J.value(nullptr);J.comment("element");J.value(42.5);J.arrayBegin();J.value(43);J.arrayEnd();J.rawValue([](raw_ostream &OS) { OS << "'unverified\nraw value'"; });});J.comment("attribute");J.attributeBegin("bar");J.comment("attribute value");J.objectBegin();J.objectEnd();J.attributeEnd();J.attribute("baz", "xyz");});return OS.str();};const char *Plain =R"(/*top* /level*/{"foo":[null,/*element*/42.5,[43],'unverifiedraw value'],/*attribute*/"bar":/*attribute value*/{},"baz":"xyz"})";EXPECT_EQ(Plain, StreamStuff(0));const char *Pretty = R"(/* top* /level */{"foo": [null,/* element */42.5,[43],'unverifiedraw value'],/* attribute */"bar": /* attribute value */ {},"baz": "xyz"})";EXPECT_EQ(Pretty, StreamStuff(2));}TEST(JSONTest, Path) {Path::Root R("foo");Path P = R, A = P.field("a"), B = P.field("b");P.report("oh no");EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("oh no when parsing foo"));A.index(1).field("c").index(2).report("boom");EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("boom at foo.a[1].c[2]"));B.field("d").field("e").report("bam");EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("bam at foo.b.d.e"));Value V = Object{{"a", Array{42}},{"b",Object{{"d",Object{{"e", Array{1, Object{{"x", "y"}}}},{"f", "a moderately long string: 48 characters in total"},}}}},};const char *Expected = R"({"a": [ ... ],"b": {"d": {"e": /* error: bam */ [1,{ ... }],"f": "a moderately long string: 48 characte..."}}})";EXPECT_EQ(Expected, errorContext(V, R));}} // namespace} // namespace json} // namespace llvm
//===-------------- ItaniumManglingCanonicalizerTest.cpp ------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ItaniumManglingCanonicalizer.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/StringRef.h"#include "gtest/gtest.h"#include <cstdlib>#include <map>#include <vector>using namespace llvm;namespace {using EquivalenceError = llvm::ItaniumManglingCanonicalizer::EquivalenceError;using FragmentKind = llvm::ItaniumManglingCanonicalizer::FragmentKind;struct Equivalence {FragmentKind Kind;llvm::StringRef First;llvm::StringRef Second;};// A set of manglings that should all be considered equivalent.using EquivalenceClass = std::vector<llvm::StringRef>;struct Testcase {// A set of equivalences to register.std::vector<Equivalence> Equivalences;// A set of distinct equivalence classes created by registering the// equivalences.std::vector<EquivalenceClass> Classes;};// A function that returns a set of test cases.static std::vector<Testcase> getTestcases() {return {// Three different manglings for std::string (old libstdc++, new libstdc++,// libc++).{{{FragmentKind::Type, "Ss","NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE"},{FragmentKind::Type, "Ss","NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},},{{"_Z1fv"},{"_Z1fSs","_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE","_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},{"_ZNKSs4sizeEv","_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4sizeEv","_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4sizeEv"},}},// Check that substitutions are properly handled.{{// ::X <-> ::N::X<int>{FragmentKind::Type, "1X", "N1N1XIiEE"},// ::T<T<int, int>, T<int, int>> <-> T<int>{FragmentKind::Type, "1TIS_IiiES0_E", "1TIiE"},// A::B::foo <-> AB::foo{FragmentKind::Name, "N1A1B3fooE", "N2AB3fooE"},},{{"_Z1f1XPS_RS_", "_Z1fN1N1XIiEEPS1_RS1_"},{"_ZN1A1B3fooE1TIS1_IiiES2_EPS3_RS3_", "_ZN2AB3fooE1TIiEPS1_RS1_"},}},// Check that nested equivalences are properly handled.{{// std::__1::char_traits == std::__cxx11::char_traits// (Note that this is unused and should make no difference,// but it should not cause us to fail to match up the cases// below.){FragmentKind::Name,"NSt3__111char_traitsE","NSt7__cxx1111char_traitsE"},// std::__1::allocator == std::allocator{FragmentKind::Name,"NSt3__19allocatorE","Sa"}, // "Sa" is not strictly a <name> but we accept it as one.// std::__1::vector == std::vector{FragmentKind::Name,"St6vector","NSt3__16vectorE"},// std::__1::basic_string<// char// std::__1::char_traits<char>,// std::__1::allocator<char>> ==// std::__cxx11::basic_string<// char,// std::char_traits<char>,// std::allocator<char>>{FragmentKind::Type,"NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE","NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},// X<A> <-> X<B>{FragmentKind::Type, "1XI1AE", "1XI1BE"},// X <-> Y{FragmentKind::Name, "1X", "1Y"},},{// f(std::string){"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE","_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},// f(std::vector<int>){"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},// f(X<A>), f(X<B>), f(Y<A>), f(Y<B>){"_Z1f1XI1AE", "_Z1f1XI1BE", "_Z1f1YI1AE", "_Z1f1YI1BE"},// f(X<C>), f(Y<C>){"_Z1f1XI1CE", "_Z1f1YI1CE"},}},// Check namespace equivalences.{{// std::__1 == std::__cxx11{FragmentKind::Name, "St3__1", "St7__cxx11"},// std::__1::allocator == std::allocator{FragmentKind::Name, "NSt3__19allocatorE", "Sa"},// std::vector == std::__1::vector{FragmentKind::Name, "St6vector", "NSt3__16vectorE"},// std::__cxx11::char_traits == std::char_traits// (This indirectly means that std::__1::char_traits == std::char_traits,// due to the std::__cxx11 == std::__1 equivalence, which is what we rely// on below.){FragmentKind::Name, "NSt7__cxx1111char_traitsE", "St11char_traits"},},{// f(std::foo){"_Z1fNSt7__cxx113fooE","_Z1fNSt3__13fooE"},// f(std::string){"_Z1fNSt7__cxx1111char_traitsIcEE","_Z1fNSt3__111char_traitsIcEE","_Z1fSt11char_traitsIcE"},// f(std::string){"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE","_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},// f(std::vector<int>){"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},}},// Check namespace equivalences for namespace 'std'. We support using 'St'// for this, despite it not technically being a <name>.{{// std::__1 == std{FragmentKind::Name, "St3__1", "St"},// std::__1 == std::__cxx11{FragmentKind::Name, "St3__1", "St7__cxx11"},// FIXME: Should a 'std' equivalence also cover the predefined// substitutions?// std::__1::allocator == std::allocator{FragmentKind::Name, "NSt3__19allocatorE", "Sa"},},{{"_Z1fSt3foo", "_Z1fNSt3__13fooE", "_Z1fNSt7__cxx113fooE"},{"_Z1fNSt3bar3bazE", "_Z1fNSt3__13bar3bazE"},// f(std::string){"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE","_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},// f(std::vector<int>){"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},}},// Check mutually-recursive equivalences.{{{FragmentKind::Type, "1A", "1B"},{FragmentKind::Type, "1A", "1C"},{FragmentKind::Type, "1D", "1B"},{FragmentKind::Type, "1C", "1E"},},{{"_Z1f1A", "_Z1f1B", "_Z1f1C", "_Z1f1D", "_Z1f1E"},{"_Z1f1F"},}},// Check <encoding>s.{{{FragmentKind::Encoding, "1fv", "1gv"},},{// f(void) -> g(void){"_Z1fv", "_Z1gv"},// static local 'n' in f(void) -> static local 'n' in g(void){"_ZZ1fvE1n", "_ZZ1gvE1n"},}},// Corner case: the substitution can appear within its own expansion.{{// X <-> Y<X>{FragmentKind::Type, "1X", "1YI1XE"},// A<B> <-> B{FragmentKind::Type, "1AI1BE", "1B"},},{// f(X) == f(Y<X>) == f(Y<Y<X>>) == f(Y<Y<Y<X>>>){"_Z1f1X", "_Z1f1YI1XE", "_Z1f1YIS_I1XEE", "_Z1f1YIS_IS_I1XEEE"},// f(B) == f(A<B>) == f(A<A<B>>) == f(A<A<A<B>>>){"_Z1f1B", "_Z1f1AI1BE", "_Z1f1AIS_I1BEE", "_Z1f1AIS_IS_I1BEEE"},}},// Redundant equivalences are accepted (and have no effect).{{{FragmentKind::Name, "3std", "St"},{FragmentKind::Name, "1X", "1Y"},{FragmentKind::Name, "N1X1ZE", "N1Y1ZE"},},{}},// Check that ctor and dtor variants are considered distinct.{{},{{"_ZN1XC1Ev"}, {"_ZN1XC2Ev"}, {"_ZN1XD1Ev"}, {"_ZN1XD2Ev"}}},// Ensure array types with and without bounds are handled properly.{{{FragmentKind::Type, "A_i", "A1_f"},},{{"_Z1fRA_i", "_Z1fRA_i", "_Z1fRA1_f"},{"_Z1fRA1_i"}, {"_Z1fRA_f"},}},// Unmangled names can be remapped as complete encodings.{{{FragmentKind::Encoding, "3foo", "3bar"},},{// foo == bar{"foo", "bar"},// void f<foo>() == void f<bar>(){"_Z1fIL_Z3fooEEvv", "_Z1fIL_Z3barEEvv"},}},};}// A function to get a set of test cases for forward template references.static std::vector<Testcase> getForwardTemplateReferenceTestcases() {return {// ForwardTemplateReference does not support canonicalization.// FIXME: We should consider ways of fixing this, perhaps by eliminating// the ForwardTemplateReference node with a tree transformation.{{// X::operator T() <with T = A> == Y::operator T() <with T = A>{FragmentKind::Encoding, "N1XcvT_I1AEEv", "N1YcvT_I1AEEv"},// A == B{FragmentKind::Name, "1A", "1B"},},{// All combinations result in unique equivalence classes.{"_ZN1XcvT_I1AEEv"},{"_ZN1XcvT_I1BEEv"},{"_ZN1YcvT_I1AEEv"},{"_ZN1YcvT_I1BEEv"},// Even giving the same string twice gives a new class.{"_ZN1XcvT_I1AEEv"},}},};}template<bool CanonicalizeFirst>static void testTestcases(ArrayRef<Testcase> Testcases) {for (const auto &Testcase : Testcases) {llvm::ItaniumManglingCanonicalizer Canonicalizer;for (const auto &Equiv : Testcase.Equivalences) {auto Result =Canonicalizer.addEquivalence(Equiv.Kind, Equiv.First, Equiv.Second);EXPECT_EQ(Result, EquivalenceError::Success)<< "couldn't add equivalence between " << Equiv.First << " and "<< Equiv.Second;}using CanonKey = llvm::ItaniumManglingCanonicalizer::Key;std::map<const EquivalenceClass*, CanonKey> Keys;if (CanonicalizeFirst)for (const auto &Class : Testcase.Classes)Keys.insert({&Class, Canonicalizer.canonicalize(*Class.begin())});std::map<CanonKey, llvm::StringRef> Found;for (const auto &Class : Testcase.Classes) {CanonKey ClassKey = Keys[&Class];for (llvm::StringRef Str : Class) {// Force a copy to be made when calling lookup to test that it doesn't// retain any part of the provided string.CanonKey ThisKey = CanonicalizeFirst? Canonicalizer.lookup(std::string(Str)): Canonicalizer.canonicalize(Str);EXPECT_NE(ThisKey, CanonKey()) << "couldn't canonicalize " << Str;if (ClassKey) {EXPECT_EQ(ThisKey, ClassKey)<< Str << " not in the same class as " << *Class.begin();} else {ClassKey = ThisKey;}}EXPECT_TRUE(Found.insert({ClassKey, *Class.begin()}).second)<< *Class.begin() << " is in the same class as " << Found[ClassKey];}}}TEST(ItaniumManglingCanonicalizerTest, TestCanonicalize) {testTestcases<false>(getTestcases());}TEST(ItaniumManglingCanonicalizerTest, TestLookup) {testTestcases<true>(getTestcases());}TEST(ItaniumManglingCanonicalizerTest, TestForwardTemplateReference) {// lookup(...) after canonicalization (intentionally) returns different// values for this testcase.testTestcases<false>(getForwardTemplateReferenceTestcases());}TEST(ItaniumManglingCanonicalizerTest, TestInvalidManglings) {llvm::ItaniumManglingCanonicalizer Canonicalizer;EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "", "1X"),EquivalenceError::InvalidFirstMangling);EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1X", "1ab"),EquivalenceError::InvalidSecondMangling);EXPECT_EQ(Canonicalizer.canonicalize("_Z3fooE"),llvm::ItaniumManglingCanonicalizer::Key());EXPECT_EQ(Canonicalizer.canonicalize("_Zfoo"),llvm::ItaniumManglingCanonicalizer::Key());// A reference to a template parameter ('T_' etc) cannot appear in a <name>,// because we don't have template arguments to bind to it. (The arguments in// an 'I ... E' construct in the <name> aren't registered as// backreferenceable arguments in this sense, because they're not part of// the template argument list of an <encoding>.EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Name, "N1XcvT_I1AEE","1f"),EquivalenceError::InvalidFirstMangling);}TEST(ItaniumManglingCanonicalizerTest, TestBadEquivalenceOrder) {llvm::ItaniumManglingCanonicalizer Canonicalizer;EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "N1P1XE", "N1Q1XE"),EquivalenceError::Success);EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1P", "1Q"),EquivalenceError::ManglingAlreadyUsed);EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "N1C1XE", "N1A1YE"),EquivalenceError::Success);EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1A", "1B"),EquivalenceError::Success);EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1C", "1D"),EquivalenceError::Success);EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1B", "1D"),EquivalenceError::ManglingAlreadyUsed);}} // end anonymous namespace
//===- InstructionCostTest.cpp - InstructionCost tests --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/InstructionCost.h"#include "gtest/gtest.h"#include <limits>using namespace llvm;namespace {struct CostTest : public testing::Test {CostTest() {}};} // namespaceTEST_F(CostTest, DefaultCtor) {InstructionCost DefaultCost;ASSERT_TRUE(DefaultCost.isValid());EXPECT_EQ(*(DefaultCost.getValue()), 0);}TEST_F(CostTest, Operators) {InstructionCost VThree = 3;InstructionCost VNegTwo = -2;InstructionCost VSix = 6;InstructionCost IThreeA = InstructionCost::getInvalid(3);InstructionCost IThreeB = InstructionCost::getInvalid(3);InstructionCost ITwo = InstructionCost::getInvalid(2);InstructionCost TmpCost;EXPECT_NE(VThree, VNegTwo);EXPECT_GT(VThree, VNegTwo);EXPECT_NE(VThree, IThreeA);EXPECT_EQ(IThreeA, IThreeB);EXPECT_GE(IThreeA, VNegTwo);EXPECT_LT(VSix, IThreeA);EXPECT_LT(VThree, ITwo);EXPECT_GE(ITwo, VThree);EXPECT_EQ(VSix - IThreeA, IThreeB);EXPECT_EQ(VThree - VNegTwo, 5);EXPECT_EQ(VThree * VNegTwo, -6);EXPECT_EQ(VSix / VThree, 2);EXPECT_NE(IThreeA, ITwo);EXPECT_LT(ITwo, IThreeA);EXPECT_GT(IThreeA, ITwo);EXPECT_FALSE(IThreeA.isValid());EXPECT_EQ(IThreeA.getState(), InstructionCost::Invalid);TmpCost = VThree + IThreeA;EXPECT_FALSE(TmpCost.isValid());// Test increments, decrementsEXPECT_EQ(++VThree, 4);EXPECT_EQ(VThree++, 4);EXPECT_EQ(VThree, 5);EXPECT_EQ(--VThree, 4);EXPECT_EQ(VThree--, 4);EXPECT_EQ(VThree, 3);TmpCost = VThree * IThreeA;EXPECT_FALSE(TmpCost.isValid());// Test value extractionEXPECT_EQ(*(VThree.getValue()), 3);EXPECT_EQ(IThreeA.getValue(), None);EXPECT_EQ(std::min(VThree, VNegTwo), -2);EXPECT_EQ(std::max(VThree, VSix), 6);// Test saturationauto Max = InstructionCost::getMax();auto Min = InstructionCost::getMin();auto MinusOne = InstructionCost(-1);auto MinusTwo = InstructionCost(-2);auto One = InstructionCost(1);auto Two = InstructionCost(2);EXPECT_EQ(Max + One, Max);EXPECT_EQ(Min + MinusOne, Min);EXPECT_EQ(Min - One, Min);EXPECT_EQ(Max - MinusOne, Max);EXPECT_EQ(Max * Two, Max);EXPECT_EQ(Min * Two, Min);EXPECT_EQ(Max * MinusTwo, Min);EXPECT_EQ(Min * MinusTwo, Max);}
//===- IndexedAccessorTest.cpp - Indexed Accessor Tests -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/STLExtras.h"#include "gmock/gmock.h"using namespace llvm;using namespace llvm::detail;namespace {/// Simple indexed accessor range that wraps an array.template <typename T>struct ArrayIndexedAccessorRange: public indexed_accessor_range<ArrayIndexedAccessorRange<T>, T *, T> {ArrayIndexedAccessorRange(T *data, ptrdiff_t start, ptrdiff_t numElements): indexed_accessor_range<ArrayIndexedAccessorRange<T>, T *, T>(data, start, numElements) {}using indexed_accessor_range<ArrayIndexedAccessorRange<T>, T *,T>::indexed_accessor_range;/// See `llvm::indexed_accessor_range` for details.static T &dereference(T *data, ptrdiff_t index) { return data[index]; }};} // end anonymous namespacetemplate <typename T>static void compareData(ArrayIndexedAccessorRange<T> range,ArrayRef<T> referenceData) {ASSERT_EQ(referenceData.size(), range.size());ASSERT_TRUE(std::equal(range.begin(), range.end(), referenceData.begin()));}namespace {TEST(AccessorRange, SliceTest) {int rawData[] = {0, 1, 2, 3, 4};ArrayRef<int> data = llvm::makeArrayRef(rawData);ArrayIndexedAccessorRange<int> range(rawData, /*start=*/0, /*numElements=*/5);compareData(range, data);compareData(range.slice(2, 3), data.slice(2, 3));compareData(range.slice(0, 5), data.slice(0, 5));}TEST(AccessorRange, EqualTest) {int32_t rawData1[] = {0, 1, 2, 3, 4};uint64_t rawData2[] = {0, 1, 2, 3, 4};ArrayIndexedAccessorRange<int32_t> range1(rawData1, /*start=*/0,/*numElements=*/5);ArrayIndexedAccessorRange<uint64_t> range2(rawData2, /*start=*/0,/*numElements=*/5);EXPECT_TRUE(range1 == range2);EXPECT_FALSE(range1 != range2);EXPECT_TRUE(range2 == range1);EXPECT_FALSE(range2 != range1);}} // end anonymous namespace
//========- unittests/Support/Host.cpp - Host.cpp tests --------------========////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Host.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/Triple.h"#include "llvm/Config/config.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "llvm/Support/Program.h"#include "llvm/Support/Threading.h"#include "gtest/gtest.h"#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}using namespace llvm;class HostTest : public testing::Test {Triple Host;protected:bool isSupportedArchAndOS() {// Initially this is only testing detection of the number of// physical cores, which is currently only supported/tested on// some systems.return (Host.isOSWindows() && llvm_is_multithreaded()) ||Host.isOSDarwin() || (Host.isX86() && Host.isOSLinux()) ||(Host.isPPC64() && Host.isOSLinux()) ||(Host.isSystemZ() && (Host.isOSLinux() || Host.isOSzOS()));}HostTest() : Host(Triple::normalize(sys::getProcessTriple())) {}};TEST_F(HostTest, NumPhysicalCoresSupported) {if (!isSupportedArchAndOS())GTEST_SKIP();int Num = sys::getHostNumPhysicalCores();ASSERT_GT(Num, 0);}TEST_F(HostTest, NumPhysicalCoresUnsupported) {if (isSupportedArchAndOS())GTEST_SKIP();int Num = sys::getHostNumPhysicalCores();ASSERT_EQ(Num, -1);}TEST(getLinuxHostCPUName, ARM) {StringRef CortexA9ProcCpuinfo = R"(processor : 0model name : ARMv7 Processor rev 10 (v7l)BogoMIPS : 1393.66Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32CPU implementer : 0x41CPU architecture: 7CPU variant : 0x2CPU part : 0xc09CPU revision : 10processor : 1model name : ARMv7 Processor rev 10 (v7l)BogoMIPS : 1393.66Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32CPU implementer : 0x41CPU architecture: 7CPU variant : 0x2CPU part : 0xc09CPU revision : 10Hardware : Generic OMAP4 (Flattened Device Tree)Revision : 0000Serial : 0000000000000000)";EXPECT_EQ(sys::detail::getHostCPUNameForARM(CortexA9ProcCpuinfo),"cortex-a9");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x41\n""CPU part : 0xc0f"),"cortex-a15");// Verify that both CPU implementer and CPU part are checked:EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x40\n""CPU part : 0xc0f"),"generic");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n""CPU part : 0x06f"),"krait");}TEST(getLinuxHostCPUName, AArch64) {EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x41\n""CPU part : 0xd03"),"cortex-a53");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x41\n""CPU part : 0xd40"),"neoverse-v1");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x41\n""CPU part : 0xd0c"),"neoverse-n1");// Verify that both CPU implementer and CPU part are checked:EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x40\n""CPU part : 0xd03"),"generic");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n""CPU part : 0x201"),"kryo");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n""CPU part : 0x800"),"cortex-a73");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n""CPU part : 0x801"),"cortex-a73");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n""CPU part : 0xc00"),"falkor");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x51\n""CPU part : 0xc01"),"saphira");EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0xc0\n""CPU part : 0xac3"),"ampere1");// MSM8992/4 weirdnessStringRef MSM8992ProcCpuInfo = R"(Processor : AArch64 Processor rev 3 (aarch64)processor : 0processor : 1processor : 2processor : 3processor : 4processor : 5Features : fp asimd evtstrm aes pmull sha1 sha2 crc32CPU implementer : 0x41CPU architecture: 8CPU variant : 0x0CPU part : 0xd03CPU revision : 3Hardware : Qualcomm Technologies, Inc MSM8992)";EXPECT_EQ(sys::detail::getHostCPUNameForARM(MSM8992ProcCpuInfo),"cortex-a53");// Exynos big.LITTLE weirdnessconst std::string ExynosProcCpuInfo = R"(processor : 0Features : fp asimd evtstrm aes pmull sha1 sha2 crc32CPU implementer : 0x41CPU architecture: 8CPU variant : 0x0CPU part : 0xd05processor : 1Features : fp asimd evtstrm aes pmull sha1 sha2 crc32CPU implementer : 0x53CPU architecture: 8)";// Verify default for Exynos.EXPECT_EQ(sys::detail::getHostCPUNameForARM(ExynosProcCpuInfo +"CPU variant : 0xc\n""CPU part : 0xafe"),"exynos-m3");// Verify Exynos M3.EXPECT_EQ(sys::detail::getHostCPUNameForARM(ExynosProcCpuInfo +"CPU variant : 0x1\n""CPU part : 0x002"),"exynos-m3");// Verify Exynos M4.EXPECT_EQ(sys::detail::getHostCPUNameForARM(ExynosProcCpuInfo +"CPU variant : 0x1\n""CPU part : 0x003"),"exynos-m4");const std::string ThunderX2T99ProcCpuInfo = R"(processor : 0BogoMIPS : 400.00Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomicsCPU implementer : 0x43CPU architecture: 8CPU variant : 0x1CPU part : 0x0af)";// Verify different versions of ThunderX2T99.EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x42\n""CPU part : 0x516"),"thunderx2t99");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x42\n""CPU part : 0x0516"),"thunderx2t99");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x43\n""CPU part : 0x516"),"thunderx2t99");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x43\n""CPU part : 0x0516"),"thunderx2t99");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x42\n""CPU part : 0xaf"),"thunderx2t99");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x42\n""CPU part : 0x0af"),"thunderx2t99");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x43\n""CPU part : 0xaf"),"thunderx2t99");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderX2T99ProcCpuInfo +"CPU implementer : 0x43\n""CPU part : 0x0af"),"thunderx2t99");// Verify ThunderXT88.const std::string ThunderXT88ProcCpuInfo = R"(processor : 0BogoMIPS : 200.00Features : fp asimd evtstrm aes pmull sha1 sha2 crc32CPU implementer : 0x43CPU architecture: 8CPU variant : 0x1CPU part : 0x0a1)";EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderXT88ProcCpuInfo +"CPU implementer : 0x43\n""CPU part : 0x0a1"),"thunderxt88");EXPECT_EQ(sys::detail::getHostCPUNameForARM(ThunderXT88ProcCpuInfo +"CPU implementer : 0x43\n""CPU part : 0xa1"),"thunderxt88");// Verify HiSilicon processors.EXPECT_EQ(sys::detail::getHostCPUNameForARM("CPU implementer : 0x48\n""CPU part : 0xd01"),"tsv110");// Verify A64FX.const std::string A64FXProcCpuInfo = R"(processor : 0BogoMIPS : 200.00Features : fp asimd evtstrm sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm fcma dcpop sveCPU implementer : 0x46CPU architecture: 8CPU variant : 0x1CPU part : 0x001)";EXPECT_EQ(sys::detail::getHostCPUNameForARM(A64FXProcCpuInfo), "a64fx");// Verify Nvidia Carmel.const std::string CarmelProcCpuInfo = R"(processor : 0model name : ARMv8 Processor rev 0 (v8l)BogoMIPS : 62.50Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm dcpopCPU implementer : 0x4eCPU architecture: 8CPU variant : 0x0CPU part : 0x004CPU revision : 0)";EXPECT_EQ(sys::detail::getHostCPUNameForARM(CarmelProcCpuInfo), "carmel");// Snapdragon mixed implementer quirkconst std::string Snapdragon865ProcCPUInfo = R"(processor : 0BogoMIPS : 38.40Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddpCPU implementer : 0x51CPU architecture: 8CPU variant : 0xdCPU part : 0x805CPU revision : 14processor : 1processor : 2processor : 3processor : 4processor : 5processor : 6BogoMIPS : 38.40Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddpCPU implementer : 0x41CPU architecture: 8CPU variant : 0x1CPU part : 0xd0dCPU revision : 0)";EXPECT_EQ(sys::detail::getHostCPUNameForARM(Snapdragon865ProcCPUInfo), "cortex-a77");}TEST(getLinuxHostCPUName, s390x) {SmallVector<std::string> ModelIDs({"3931", "8561", "3906", "2964", "2827", "2817", "2097", "2064"});SmallVector<std::string> VectorSupport({"", "vx"});SmallVector<StringRef> ExpectedCPUs;// Model Id: 3931ExpectedCPUs.push_back("zEC12");ExpectedCPUs.push_back("z16");// Model Id: 8561ExpectedCPUs.push_back("zEC12");ExpectedCPUs.push_back("z15");// Model Id: 3906ExpectedCPUs.push_back("zEC12");ExpectedCPUs.push_back("z14");// Model Id: 2964ExpectedCPUs.push_back("zEC12");ExpectedCPUs.push_back("z13");// Model Id: 2827ExpectedCPUs.push_back("zEC12");ExpectedCPUs.push_back("zEC12");// Model Id: 2817ExpectedCPUs.push_back("z196");ExpectedCPUs.push_back("z196");// Model Id: 2097ExpectedCPUs.push_back("z10");ExpectedCPUs.push_back("z10");// Model Id: 2064ExpectedCPUs.push_back("generic");ExpectedCPUs.push_back("generic");const std::string DummyBaseVectorInfo ="features : esan3 zarch stfle msa ldisp eimm dfp edat etf3eh highgprs ""te ";const std::string DummyBaseMachineInfo ="processor 0: version = FF, identification = 059C88, machine = ";int CheckIndex = 0;for (size_t I = 0; I < ModelIDs.size(); I++) {for (size_t J = 0; J < VectorSupport.size(); J++) {const std::string DummyCPUInfo = DummyBaseVectorInfo + VectorSupport[J] +"\n" + DummyBaseMachineInfo +ModelIDs[I];EXPECT_EQ(sys::detail::getHostCPUNameForS390x(DummyCPUInfo),ExpectedCPUs[CheckIndex++]);}}}TEST(getLinuxHostCPUName, RISCV) {const StringRef SifiveU74MCProcCPUInfo = R"(processor : 0hart : 2isa : rv64imafdcmmu : sv39uarch : sifive,u74-mc)";EXPECT_EQ(sys::detail::getHostCPUNameForRISCV(SifiveU74MCProcCPUInfo),"sifive-u74");EXPECT_EQ(sys::detail::getHostCPUNameForRISCV("uarch : sifive,bullet0\n"),"sifive-u74");}static bool runAndGetCommandOutput(const char *ExePath, ArrayRef<llvm::StringRef> argv,std::unique_ptr<char[]> &Buffer, off_t &Size) {bool Success = false;[ExePath, argv, &Buffer, &Size, &Success] {using namespace llvm::sys;SmallString<128> TestDirectory;ASSERT_NO_ERROR(fs::createUniqueDirectory("host_test", TestDirectory));SmallString<128> OutputFile(TestDirectory);path::append(OutputFile, "out");StringRef OutputPath = OutputFile.str();const Optional<StringRef> Redirects[] = {/*STDIN=*/None, /*STDOUT=*/OutputPath, /*STDERR=*/None};int RetCode = ExecuteAndWait(ExePath, argv, /*env=*/llvm::None, Redirects);ASSERT_EQ(0, RetCode);int FD = 0;ASSERT_NO_ERROR(fs::openFileForRead(OutputPath, FD));Size = ::lseek(FD, 0, SEEK_END);ASSERT_NE(-1, Size);::lseek(FD, 0, SEEK_SET);Buffer = std::make_unique<char[]>(Size);ASSERT_EQ(::read(FD, Buffer.get(), Size), Size);::close(FD);ASSERT_NO_ERROR(fs::remove(OutputPath));ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));Success = true;}();return Success;}TEST_F(HostTest, DummyRunAndGetCommandOutputUse) {// Suppress defined-but-not-used warnings when the tests using the helper are// disabled.(void)&runAndGetCommandOutput;}TEST_F(HostTest, getMacOSHostVersion) {llvm::Triple HostTriple(llvm::sys::getProcessTriple());if (!HostTriple.isMacOSX())GTEST_SKIP();const char *SwVersPath = "/usr/bin/sw_vers";StringRef argv[] = {SwVersPath, "-productVersion"};std::unique_ptr<char[]> Buffer;off_t Size;ASSERT_EQ(runAndGetCommandOutput(SwVersPath, argv, Buffer, Size), true);StringRef SystemVersionStr = StringRef(Buffer.get(), Size).rtrim();// Ensure that the two versions match.VersionTuple SystemVersion;ASSERT_EQ(llvm::Triple((Twine("x86_64-apple-macos") + SystemVersionStr)).getMacOSXVersion(SystemVersion),true);VersionTuple HostVersion;ASSERT_EQ(HostTriple.getMacOSXVersion(HostVersion), true);if (SystemVersion.getMajor() > 10) {// Don't compare the 'Minor' and 'Micro' versions, as they're always '0' for// the 'Darwin' triples on 11.x.ASSERT_EQ(SystemVersion.getMajor(), HostVersion.getMajor());} else {// Don't compare the 'Micro' version, as it's always '0' for the 'Darwin'// triples.ASSERT_EQ(SystemVersion.getMajor(), HostVersion.getMajor());ASSERT_EQ(SystemVersion.getMinor(), HostVersion.getMinor());}}// Helper to return AIX system version. Must return void to use ASSERT_*.static void getAIXSystemVersion(VersionTuple &SystemVersion) {const char *ExePath = "/usr/bin/oslevel";StringRef argv[] = {ExePath};std::unique_ptr<char[]> Buffer;off_t Size;ASSERT_EQ(runAndGetCommandOutput(ExePath, argv, Buffer, Size), true);StringRef SystemVersionStr = StringRef(Buffer.get(), Size).rtrim();SystemVersion =llvm::Triple((Twine("powerpc-ibm-aix") + SystemVersionStr)).getOSVersion();}TEST_F(HostTest, AIXHostVersionDetect) {llvm::Triple HostTriple(llvm::sys::getProcessTriple());if (HostTriple.getOS() != Triple::AIX)GTEST_SKIP();llvm::Triple ConfiguredHostTriple(LLVM_HOST_TRIPLE);ASSERT_EQ(ConfiguredHostTriple.getOS(), Triple::AIX);VersionTuple SystemVersion;getAIXSystemVersion(SystemVersion);// Ensure that the host triple version (major) and release (minor) numbers,// unless explicitly configured, match with those of the current system.auto SysMajor = SystemVersion.getMajor();auto SysMinor = SystemVersion.getMinor();VersionTuple HostVersion = HostTriple.getOSVersion();if (ConfiguredHostTriple.getOSMajorVersion()) {// Explicitly configured, force a match. We do it this way so the// asserts are always executed.SysMajor = HostVersion.getMajor();SysMinor = HostVersion.getMinor();}ASSERT_EQ(SysMajor, HostVersion.getMajor());ASSERT_EQ(SysMinor, HostVersion.getMinor());}TEST_F(HostTest, AIXTargetVersionDetect) {llvm::Triple TargetTriple(llvm::sys::getDefaultTargetTriple());if (TargetTriple.getOS() != Triple::AIX)GTEST_SKIP();// Ensure that the target triple version (major) and release (minor) numbers// match with those of the current system.llvm::Triple ConfiguredTargetTriple(LLVM_DEFAULT_TARGET_TRIPLE);if (ConfiguredTargetTriple.getOSMajorVersion())GTEST_SKIP(); // The version was configured explicitly; skip.VersionTuple SystemVersion;getAIXSystemVersion(SystemVersion);VersionTuple TargetVersion = TargetTriple.getOSVersion();ASSERT_EQ(SystemVersion.getMajor(), TargetVersion.getMajor());ASSERT_EQ(SystemVersion.getMinor(), TargetVersion.getMinor());}TEST_F(HostTest, AIXHostCPUDetect) {llvm::Triple HostTriple(llvm::sys::getProcessTriple());if (HostTriple.getOS() != Triple::AIX)GTEST_SKIP();// Return a value based on the current processor implementation mode.const char *ExePath = "/usr/sbin/getsystype";StringRef argv[] = {ExePath, "-i"};std::unique_ptr<char[]> Buffer;off_t Size;ASSERT_EQ(runAndGetCommandOutput(ExePath, argv, Buffer, Size), true);StringRef CPU(Buffer.get(), Size);StringRef MCPU = StringSwitch<const char *>(CPU).Case("POWER 4\n", "pwr4").Case("POWER 5\n", "pwr5").Case("POWER 6\n", "pwr6").Case("POWER 7\n", "pwr7").Case("POWER 8\n", "pwr8").Case("POWER 9\n", "pwr9").Case("POWER 10\n", "pwr10").Default("unknown");StringRef HostCPU = sys::getHostCPUName();// Just do the comparison on the base implementation mode.if (HostCPU == "970")HostCPU = StringRef("pwr4");elseHostCPU = HostCPU.rtrim('x');EXPECT_EQ(HostCPU, MCPU);}
//===- llvm/unittest/Support/HashBuilderTest.cpp - HashBuilder unit tests -===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/HashBuilder.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/Support/MD5.h"#include "llvm/Support/SHA1.h"#include "llvm/Support/SHA256.h"#include "gtest/gtest.h"#include <list>#include <string>#include <type_traits>#include <utility>#include <vector>// gtest utilities and macros rely on using a single type. So wrap both the// hasher type and endianness.template <typename _HasherT, llvm::support::endianness _Endianness>struct HasherTAndEndianness {using HasherT = _HasherT;static constexpr llvm::support::endianness Endianness = _Endianness;};using HasherTAndEndiannessToTest =::testing::Types<HasherTAndEndianness<llvm::MD5, llvm::support::big>,HasherTAndEndianness<llvm::MD5, llvm::support::little>,HasherTAndEndianness<llvm::MD5, llvm::support::native>,HasherTAndEndianness<llvm::SHA1, llvm::support::big>,HasherTAndEndianness<llvm::SHA1, llvm::support::little>,HasherTAndEndianness<llvm::SHA1, llvm::support::native>,HasherTAndEndianness<llvm::SHA256, llvm::support::big>,HasherTAndEndianness<llvm::SHA256, llvm::support::little>,HasherTAndEndianness<llvm::SHA256, llvm::support::native>>;template <typename HasherT> class HashBuilderTest : public testing::Test {};TYPED_TEST_SUITE(HashBuilderTest, HasherTAndEndiannessToTest, );template <typename HasherTAndEndianness>using HashBuilder = llvm::HashBuilder<typename HasherTAndEndianness::HasherT,HasherTAndEndianness::Endianness>;template <typename HasherTAndEndianness, typename... Ts>static typename HashBuilder<HasherTAndEndianness>::template HashResultTy<>hashWithBuilder(const Ts &...Args) {return HashBuilder<HasherTAndEndianness>().add(Args...).final();}template <typename HasherTAndEndianness, typename... Ts>static typename HashBuilder<HasherTAndEndianness>::template HashResultTy<>hashRangeWithBuilder(const Ts &...Args) {return HashBuilder<HasherTAndEndianness>().addRange(Args...).final();}// All the test infrastructure relies on the variadic helpers. Test them first.TYPED_TEST(HashBuilderTest, VariadicHelpers) {{HashBuilder<TypeParam> HBuilder;HBuilder.add(100);HBuilder.add('c');HBuilder.add("string");EXPECT_EQ(HBuilder.final(), hashWithBuilder<TypeParam>(100, 'c', "string"));}{HashBuilder<TypeParam> HBuilder;std::vector<int> Vec{100, 101, 102};HBuilder.addRange(Vec);EXPECT_EQ(HBuilder.final(), hashRangeWithBuilder<TypeParam>(Vec));}{HashBuilder<TypeParam> HBuilder;std::vector<int> Vec{200, 201, 202};HBuilder.addRange(Vec.begin(), Vec.end());EXPECT_EQ(HBuilder.final(),hashRangeWithBuilder<TypeParam>(Vec.begin(), Vec.end()));}}TYPED_TEST(HashBuilderTest, AddRangeElements) {HashBuilder<TypeParam> HBuilder;int Values[] = {1, 2, 3};HBuilder.addRangeElements(llvm::ArrayRef<int>(Values));EXPECT_EQ(HBuilder.final(), hashWithBuilder<TypeParam>(1, 2, 3));}TYPED_TEST(HashBuilderTest, AddHashableData) {using HE = TypeParam;auto ByteSwapAndHashWithHasher = [](auto Data) {using H = typename HE::HasherT;constexpr auto E = HE::Endianness;H Hasher;auto SwappedData = llvm::support::endian::byte_swap(Data, E);Hasher.update(llvm::makeArrayRef(reinterpret_cast<const uint8_t *>(&SwappedData), sizeof(Data)));return Hasher.final();};char C = 'c';int32_t I = 0x12345678;uint64_t UI64 = static_cast<uint64_t>(1) << 50;enum TestEnumeration : uint16_t { TE_One = 1, TE_Two = 2 };TestEnumeration Enum = TE_Two;EXPECT_EQ(ByteSwapAndHashWithHasher(C), hashWithBuilder<HE>(C));EXPECT_EQ(ByteSwapAndHashWithHasher(I), hashWithBuilder<HE>(I));EXPECT_EQ(ByteSwapAndHashWithHasher(UI64), hashWithBuilder<HE>(UI64));EXPECT_EQ(ByteSwapAndHashWithHasher(Enum), hashWithBuilder<HE>(Enum));}struct SimpleStruct {char C;int I;};template <typename HasherT, llvm::support::endianness Endianness>void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder,const SimpleStruct &Value) {HBuilder.add(Value.C);HBuilder.add(Value.I);}struct StructWithoutCopyOrMove {int I;StructWithoutCopyOrMove() = default;StructWithoutCopyOrMove(const StructWithoutCopyOrMove &) = delete;StructWithoutCopyOrMove &operator=(const StructWithoutCopyOrMove &) = delete;template <typename HasherT, llvm::support::endianness Endianness>friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder,const StructWithoutCopyOrMove &Value) {HBuilder.add(Value.I);}};// The struct and associated tests are simplified to avoid failures caused by// different alignments on different platforms.struct /* __attribute__((packed)) */ StructWithFastHash {int I;// char C;// If possible, we want to hash both `I` and `C` in a single `update`// call for performance concerns.template <typename HasherT, llvm::support::endianness Endianness>friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder,const StructWithFastHash &Value) {if (Endianness == llvm::support::endian::system_endianness()) {HBuilder.update(llvm::makeArrayRef(reinterpret_cast<const uint8_t *>(&Value), sizeof(Value)));} else {// Rely on existing `add` methods to handle endianness.HBuilder.add(Value.I);// HBuilder.add(Value.C);}}};struct CustomContainer {private:size_t Size;int Elements[100];public:CustomContainer(size_t Size) : Size(Size) {for (size_t I = 0; I != Size; ++I)Elements[I] = I;}template <typename HasherT, llvm::support::endianness Endianness>friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder,const CustomContainer &Value) {if (Endianness == llvm::support::endian::system_endianness()) {HBuilder.update(llvm::makeArrayRef(reinterpret_cast<const uint8_t *>(&Value.Size),sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0])));} else {HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] + Value.Size);}}};TYPED_TEST(HashBuilderTest, HashUserDefinedStruct) {using HE = TypeParam;EXPECT_EQ(hashWithBuilder<HE>(SimpleStruct{'c', 123}),hashWithBuilder<HE>('c', 123));EXPECT_EQ(hashWithBuilder<HE>(StructWithoutCopyOrMove{1}),hashWithBuilder<HE>(1));EXPECT_EQ(hashWithBuilder<HE>(StructWithFastHash{123}),hashWithBuilder<HE>(123));EXPECT_EQ(hashWithBuilder<HE>(CustomContainer(3)),hashWithBuilder<HE>(static_cast<size_t>(3), 0, 1, 2));}TYPED_TEST(HashBuilderTest, HashArrayRefHashableDataTypes) {using HE = TypeParam;int Values[] = {1, 20, 0x12345678};llvm::ArrayRef<int> Array(Values);EXPECT_NE(hashWithBuilder<HE>(Array), hashWithBuilder<HE>(1, 20, 0x12345678));EXPECT_EQ(hashWithBuilder<HE>(Array),hashRangeWithBuilder<HE>(Array.begin(), Array.end()));EXPECT_EQ(hashWithBuilder<HE>(Array),hashRangeWithBuilder<HE>(Array.data(), Array.data() + Array.size()));}TYPED_TEST(HashBuilderTest, HashArrayRef) {using HE = TypeParam;int Values[] = {1, 2, 3};llvm::ArrayRef<int> Array123(&Values[0], 3);llvm::ArrayRef<int> Array12(&Values[0], 2);llvm::ArrayRef<int> Array1(&Values[0], 1);llvm::ArrayRef<int> Array23(&Values[1], 2);llvm::ArrayRef<int> Array3(&Values[2], 1);llvm::ArrayRef<int> ArrayEmpty(&Values[0], static_cast<size_t>(0));auto Hash123andEmpty = hashWithBuilder<HE>(Array123, ArrayEmpty);auto Hash12And3 = hashWithBuilder<HE>(Array12, Array3);auto Hash1And23 = hashWithBuilder<HE>(Array1, Array23);auto HashEmptyAnd123 = hashWithBuilder<HE>(ArrayEmpty, Array123);EXPECT_NE(Hash123andEmpty, Hash12And3);EXPECT_NE(Hash123andEmpty, Hash1And23);EXPECT_NE(Hash123andEmpty, HashEmptyAnd123);EXPECT_NE(Hash12And3, Hash1And23);EXPECT_NE(Hash12And3, HashEmptyAnd123);EXPECT_NE(Hash1And23, HashEmptyAnd123);}TYPED_TEST(HashBuilderTest, HashArrayRefNonHashableDataTypes) {using HE = TypeParam;SimpleStruct Values[] = {{'a', 100}, {'b', 200}};llvm::ArrayRef<SimpleStruct> Array(Values);EXPECT_NE(hashWithBuilder<HE>(Array),hashWithBuilder<HE>(SimpleStruct{'a', 100}, SimpleStruct{'b', 200}));}TYPED_TEST(HashBuilderTest, HashStringRef) {using HE = TypeParam;llvm::StringRef SEmpty("");llvm::StringRef S1("1");llvm::StringRef S12("12");llvm::StringRef S123("123");llvm::StringRef S23("23");llvm::StringRef S3("3");auto Hash123andEmpty = hashWithBuilder<HE>(S123, SEmpty);auto Hash12And3 = hashWithBuilder<HE>(S12, S3);auto Hash1And23 = hashWithBuilder<HE>(S1, S23);auto HashEmptyAnd123 = hashWithBuilder<HE>(SEmpty, S123);EXPECT_NE(Hash123andEmpty, Hash12And3);EXPECT_NE(Hash123andEmpty, Hash1And23);EXPECT_NE(Hash123andEmpty, HashEmptyAnd123);EXPECT_NE(Hash12And3, Hash1And23);EXPECT_NE(Hash12And3, HashEmptyAnd123);EXPECT_NE(Hash1And23, HashEmptyAnd123);}TYPED_TEST(HashBuilderTest, HashStdString) {using HE = TypeParam;EXPECT_EQ(hashWithBuilder<HE>(std::string("123")),hashWithBuilder<HE>(llvm::StringRef("123")));}TYPED_TEST(HashBuilderTest, HashStdPair) {using HE = TypeParam;EXPECT_EQ(hashWithBuilder<HE>(std::make_pair(1, "string")),hashWithBuilder<HE>(1, "string"));std::pair<StructWithoutCopyOrMove, std::string> Pair;Pair.first.I = 1;Pair.second = "string";EXPECT_EQ(hashWithBuilder<HE>(Pair), hashWithBuilder<HE>(1, "string"));}TYPED_TEST(HashBuilderTest, HashStdTuple) {using HE = TypeParam;EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(1)), hashWithBuilder<HE>(1));EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(2ULL)),hashWithBuilder<HE>(2ULL));EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple("three")),hashWithBuilder<HE>("three"));EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(1, 2ULL)),hashWithBuilder<HE>(1, 2ULL));EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(1, 2ULL, "three")),hashWithBuilder<HE>(1, 2ULL, "three"));std::tuple<StructWithoutCopyOrMove, std::string> Tuple;std::get<0>(Tuple).I = 1;std::get<1>(Tuple) = "two";EXPECT_EQ(hashWithBuilder<HE>(Tuple), hashWithBuilder<HE>(1, "two"));}TYPED_TEST(HashBuilderTest, HashRangeWithForwardIterator) {using HE = TypeParam;std::list<int> List;List.push_back(1);List.push_back(2);List.push_back(3);EXPECT_NE(hashRangeWithBuilder<HE>(List), hashWithBuilder<HE>(1, 2, 3));}TEST(CustomHasher, CustomHasher) {struct SumHash {explicit SumHash(uint8_t Seed1, uint8_t Seed2) : Hash(Seed1 + Seed2) {}void update(llvm::ArrayRef<uint8_t> Data) {for (uint8_t C : Data)Hash += C;}uint8_t Hash;};{llvm::HashBuilder<SumHash, llvm::support::endianness::little> HBuilder(0,1);EXPECT_EQ(HBuilder.add(0x02, 0x03, 0x400).getHasher().Hash, 0xa);}{llvm::HashBuilder<SumHash, llvm::support::endianness::little> HBuilder(2,3);EXPECT_EQ(HBuilder.add("ab", 'c').getHasher().Hash,static_cast<uint8_t>(/*seeds*/ 2 + 3 + /*range size*/ 2 +/*characters*/ 'a' + 'b' + 'c'));}}
//===- llvm/unittest/Support/GlobPatternTest.cpp --------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/GlobPattern.h"#include "gtest/gtest.h"using namespace llvm;namespace {class GlobPatternTest : public ::testing::Test {};TEST_F(GlobPatternTest, Empty) {Expected<GlobPattern> Pat1 = GlobPattern::create("");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match(""));EXPECT_FALSE(Pat1->match("a"));}TEST_F(GlobPatternTest, Glob) {Expected<GlobPattern> Pat1 = GlobPattern::create("ab*c*def");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("abcdef"));EXPECT_TRUE(Pat1->match("abxcxdef"));EXPECT_FALSE(Pat1->match(""));EXPECT_FALSE(Pat1->match("xabcdef"));EXPECT_FALSE(Pat1->match("abcdefx"));}TEST_F(GlobPatternTest, Wildcard) {Expected<GlobPattern> Pat1 = GlobPattern::create("a??c");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("axxc"));EXPECT_FALSE(Pat1->match("axxx"));EXPECT_FALSE(Pat1->match(""));}TEST_F(GlobPatternTest, Escape) {Expected<GlobPattern> Pat1 = GlobPattern::create("\\*");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("*"));EXPECT_FALSE(Pat1->match("\\*"));EXPECT_FALSE(Pat1->match("a"));Expected<GlobPattern> Pat2 = GlobPattern::create("a?\\?c");EXPECT_TRUE((bool)Pat2);EXPECT_TRUE(Pat2->match("ax?c"));EXPECT_FALSE(Pat2->match("axxc"));EXPECT_FALSE(Pat2->match(""));}TEST_F(GlobPatternTest, BasicCharacterClass) {Expected<GlobPattern> Pat1 = GlobPattern::create("[abc-fy-z]");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("a"));EXPECT_TRUE(Pat1->match("b"));EXPECT_TRUE(Pat1->match("c"));EXPECT_TRUE(Pat1->match("d"));EXPECT_TRUE(Pat1->match("e"));EXPECT_TRUE(Pat1->match("f"));EXPECT_TRUE(Pat1->match("y"));EXPECT_TRUE(Pat1->match("z"));EXPECT_FALSE(Pat1->match("g"));EXPECT_FALSE(Pat1->match(""));}TEST_F(GlobPatternTest, NegatedCharacterClass) {Expected<GlobPattern> Pat1 = GlobPattern::create("[^abc-fy-z]");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("g"));EXPECT_FALSE(Pat1->match("a"));EXPECT_FALSE(Pat1->match("b"));EXPECT_FALSE(Pat1->match("c"));EXPECT_FALSE(Pat1->match("d"));EXPECT_FALSE(Pat1->match("e"));EXPECT_FALSE(Pat1->match("f"));EXPECT_FALSE(Pat1->match("y"));EXPECT_FALSE(Pat1->match("z"));EXPECT_FALSE(Pat1->match(""));Expected<GlobPattern> Pat2 = GlobPattern::create("[!abc-fy-z]");EXPECT_TRUE((bool)Pat2);EXPECT_TRUE(Pat2->match("g"));EXPECT_FALSE(Pat2->match("a"));EXPECT_FALSE(Pat2->match("b"));EXPECT_FALSE(Pat2->match("c"));EXPECT_FALSE(Pat2->match("d"));EXPECT_FALSE(Pat2->match("e"));EXPECT_FALSE(Pat2->match("f"));EXPECT_FALSE(Pat2->match("y"));EXPECT_FALSE(Pat2->match("z"));EXPECT_FALSE(Pat2->match(""));}TEST_F(GlobPatternTest, BracketFrontOfCharacterClass) {Expected<GlobPattern> Pat1 = GlobPattern::create("[]a]x");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("]x"));EXPECT_TRUE(Pat1->match("ax"));EXPECT_FALSE(Pat1->match("a]x"));EXPECT_FALSE(Pat1->match(""));}TEST_F(GlobPatternTest, SpecialCharsInCharacterClass) {Expected<GlobPattern> Pat1 = GlobPattern::create("[*?^]");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("*"));EXPECT_TRUE(Pat1->match("?"));EXPECT_TRUE(Pat1->match("^"));EXPECT_FALSE(Pat1->match("*?^"));EXPECT_FALSE(Pat1->match(""));}TEST_F(GlobPatternTest, Invalid) {Expected<GlobPattern> Pat1 = GlobPattern::create("[");EXPECT_FALSE((bool)Pat1);handleAllErrors(Pat1.takeError(), [&](ErrorInfoBase &EIB) {});Expected<GlobPattern> Pat2 = GlobPattern::create("[]");EXPECT_FALSE((bool)Pat2);handleAllErrors(Pat2.takeError(), [&](ErrorInfoBase &EIB) {});}TEST_F(GlobPatternTest, ExtSym) {Expected<GlobPattern> Pat1 = GlobPattern::create("a*\xFF");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->match("axxx\xFF"));Expected<GlobPattern> Pat2 = GlobPattern::create("[\xFF-\xFF]");EXPECT_TRUE((bool)Pat2);EXPECT_TRUE(Pat2->match("\xFF"));}TEST_F(GlobPatternTest, IsTrivialMatchAll) {Expected<GlobPattern> Pat1 = GlobPattern::create("*");EXPECT_TRUE((bool)Pat1);EXPECT_TRUE(Pat1->isTrivialMatchAll());const char *NegativeCases[] = {"a*", "*a", "?*", "*?", "**", "\\*"};for (auto *P : NegativeCases) {Expected<GlobPattern> Pat2 = GlobPattern::create(P);EXPECT_TRUE((bool)Pat2);EXPECT_FALSE(Pat2->isTrivialMatchAll());}}}
//===- FormatVariadicTest.cpp - Unit tests for string formatting ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/Error.h"#include "llvm/Support/FormatAdapters.h"#include "gtest/gtest.h"using namespace llvm;// Compile-time tests templates in the detail namespace.namespace {struct Format : public FormatAdapter<int> {Format(int N) : FormatAdapter<int>(std::move(N)) {}void format(raw_ostream &OS, StringRef Opt) override { OS << "Format"; }};using detail::uses_format_member;using detail::uses_missing_provider;static_assert(uses_format_member<Format>::value, "");static_assert(uses_format_member<Format &>::value, "");static_assert(uses_format_member<Format &&>::value, "");static_assert(uses_format_member<const Format>::value, "");static_assert(uses_format_member<const Format &>::value, "");static_assert(uses_format_member<const volatile Format>::value, "");static_assert(uses_format_member<const volatile Format &>::value, "");struct NoFormat {};static_assert(uses_missing_provider<NoFormat>::value, "");}TEST(FormatVariadicTest, EmptyFormatString) {auto Replacements = formatv_object_base::parseFormatString("");EXPECT_EQ(0U, Replacements.size());}TEST(FormatVariadicTest, NoReplacements) {const StringRef kFormatString = "This is a test";auto Replacements = formatv_object_base::parseFormatString(kFormatString);ASSERT_EQ(1U, Replacements.size());EXPECT_EQ(kFormatString, Replacements[0].Spec);EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);}TEST(FormatVariadicTest, EscapedBrace) {// {{ should be replaced with {auto Replacements = formatv_object_base::parseFormatString("{{");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("{", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);// An even number N of braces should be replaced with N/2 braces.Replacements = formatv_object_base::parseFormatString("{{{{{{");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("{{{", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);// } does not require doubling up.Replacements = formatv_object_base::parseFormatString("}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("}", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);// } does not require doubling up.Replacements = formatv_object_base::parseFormatString("}}}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("}}}", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);}TEST(FormatVariadicTest, ValidReplacementSequence) {// 1. Simple replacement - parameter index onlyauto Replacements = formatv_object_base::parseFormatString("{0}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(0u, Replacements[0].Align);EXPECT_EQ("", Replacements[0].Options);Replacements = formatv_object_base::parseFormatString("{1}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(1u, Replacements[0].Index);EXPECT_EQ(0u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ("", Replacements[0].Options);// 2. Parameter index with right alignmentReplacements = formatv_object_base::parseFormatString("{0,3}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(3u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ("", Replacements[0].Options);// 3. And left alignmentReplacements = formatv_object_base::parseFormatString("{0,-3}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(3u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);EXPECT_EQ("", Replacements[0].Options);// 4. And center alignmentReplacements = formatv_object_base::parseFormatString("{0,=3}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(3u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Center, Replacements[0].Where);EXPECT_EQ("", Replacements[0].Options);// 4. Parameter index with option stringReplacements = formatv_object_base::parseFormatString("{0:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(0u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ("foo", Replacements[0].Options);// 5. Parameter index with alignment before option stringReplacements = formatv_object_base::parseFormatString("{0,-3:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(3u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);EXPECT_EQ("foo", Replacements[0].Options);// 7. Parameter indices, options, and alignment can all have whitespace.Replacements = formatv_object_base::parseFormatString("{ 0, -3 : foo }");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(3u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);EXPECT_EQ("foo", Replacements[0].Options);// 8. Everything after the first option specifier is part of the style, even// if it contains another option specifier.Replacements = formatv_object_base::parseFormatString("{0:0:1}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("0:0:1", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(0u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ("0:1", Replacements[0].Options);// 9. Custom padding characterReplacements = formatv_object_base::parseFormatString("{0,p+4:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("0,p+4:foo", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(4u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ('p', Replacements[0].Pad);EXPECT_EQ("foo", Replacements[0].Options);// Format string special characters are allowed as padding characterReplacements = formatv_object_base::parseFormatString("{0,-+4:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("0,-+4:foo", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(4u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ('-', Replacements[0].Pad);EXPECT_EQ("foo", Replacements[0].Options);Replacements = formatv_object_base::parseFormatString("{0,+-4:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("0,+-4:foo", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(4u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);EXPECT_EQ('+', Replacements[0].Pad);EXPECT_EQ("foo", Replacements[0].Options);Replacements = formatv_object_base::parseFormatString("{0,==4:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("0,==4:foo", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(4u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Center, Replacements[0].Where);EXPECT_EQ('=', Replacements[0].Pad);EXPECT_EQ("foo", Replacements[0].Options);Replacements = formatv_object_base::parseFormatString("{0,:=4:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ("0,:=4:foo", Replacements[0].Spec);EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(4u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Center, Replacements[0].Where);EXPECT_EQ(':', Replacements[0].Pad);EXPECT_EQ("foo", Replacements[0].Options);}TEST(FormatVariadicTest, DefaultReplacementValues) {// 2. If options string is missing, it defaults to empty.auto Replacements = formatv_object_base::parseFormatString("{0,3}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(3u, Replacements[0].Align);EXPECT_EQ("", Replacements[0].Options);// Including if the colon is present but contains no text.Replacements = formatv_object_base::parseFormatString("{0,3:}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(3u, Replacements[0].Align);EXPECT_EQ("", Replacements[0].Options);// 3. If alignment is missing, it defaults to 0, right, spaceReplacements = formatv_object_base::parseFormatString("{0:foo}");ASSERT_EQ(1u, Replacements.size());EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ(' ', Replacements[0].Pad);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(0u, Replacements[0].Align);EXPECT_EQ("foo", Replacements[0].Options);}TEST(FormatVariadicTest, MultipleReplacements) {auto Replacements =formatv_object_base::parseFormatString("{0} {1:foo}-{2,-3:bar}");ASSERT_EQ(5u, Replacements.size());// {0}EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);EXPECT_EQ(0u, Replacements[0].Index);EXPECT_EQ(0u, Replacements[0].Align);EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);EXPECT_EQ("", Replacements[0].Options);// " "EXPECT_EQ(ReplacementType::Literal, Replacements[1].Type);EXPECT_EQ(" ", Replacements[1].Spec);// {1:foo} - Options=fooEXPECT_EQ(ReplacementType::Format, Replacements[2].Type);EXPECT_EQ(1u, Replacements[2].Index);EXPECT_EQ(0u, Replacements[2].Align);EXPECT_EQ(AlignStyle::Right, Replacements[2].Where);EXPECT_EQ("foo", Replacements[2].Options);// "-"EXPECT_EQ(ReplacementType::Literal, Replacements[3].Type);EXPECT_EQ("-", Replacements[3].Spec);// {2:bar,-3} - Options=bar, Align=-3EXPECT_EQ(ReplacementType::Format, Replacements[4].Type);EXPECT_EQ(2u, Replacements[4].Index);EXPECT_EQ(3u, Replacements[4].Align);EXPECT_EQ(AlignStyle::Left, Replacements[4].Where);EXPECT_EQ("bar", Replacements[4].Options);}TEST(FormatVariadicTest, FormatNoReplacements) {EXPECT_EQ("", formatv("").str());EXPECT_EQ("Test", formatv("Test").str());}TEST(FormatVariadicTest, FormatBasicTypesOneReplacement) {EXPECT_EQ("1", formatv("{0}", 1).str());EXPECT_EQ("c", formatv("{0}", 'c').str());EXPECT_EQ("-3", formatv("{0}", -3).str());EXPECT_EQ("Test", formatv("{0}", "Test").str());EXPECT_EQ("Test2", formatv("{0}", StringRef("Test2")).str());EXPECT_EQ("Test3", formatv("{0}", std::string("Test3")).str());}TEST(FormatVariadicTest, IntegralHexFormatting) {// 1. Trivial cases. Make sure hex is not the default.EXPECT_EQ("0", formatv("{0}", 0).str());EXPECT_EQ("2748", formatv("{0}", 0xABC).str());EXPECT_EQ("-2748", formatv("{0}", -0xABC).str());// 3. various hex prefixes.EXPECT_EQ("0xFF", formatv("{0:X}", 255).str());EXPECT_EQ("0xFF", formatv("{0:X+}", 255).str());EXPECT_EQ("0xff", formatv("{0:x}", 255).str());EXPECT_EQ("0xff", formatv("{0:x+}", 255).str());EXPECT_EQ("FF", formatv("{0:X-}", 255).str());EXPECT_EQ("ff", formatv("{0:x-}", 255).str());// 5. Precision pads left of the most significant digit but right of the// prefix (if one exists).EXPECT_EQ("0xFF", formatv("{0:X2}", 255).str());EXPECT_EQ("0xFF", formatv("{0:X+2}", 255).str());EXPECT_EQ("0x0ff", formatv("{0:x3}", 255).str());EXPECT_EQ("0x0ff", formatv("{0:x+3}", 255).str());EXPECT_EQ("00FF", formatv("{0:X-4}", 255).str());EXPECT_EQ("00ff", formatv("{0:x-4}", 255).str());// 6. Try some larger types.EXPECT_EQ("0xDEADBEEFDEADBEEF",formatv("{0:X16}", -2401053088876216593LL).str());EXPECT_EQ("0xFEEBDAEDFEEBDAED",formatv("{0:X16}", 0xFEEBDAEDFEEBDAEDULL).str());EXPECT_EQ("0x00000000DEADBEEF", formatv("{0:X16}", 0xDEADBEEF).str());// 7. Padding should take into account the prefixEXPECT_EQ("0xff", formatv("{0,4:x}", 255).str());EXPECT_EQ(" 0xff", formatv("{0,5:x+}", 255).str());EXPECT_EQ(" FF", formatv("{0,4:X-}", 255).str());EXPECT_EQ(" ff", formatv("{0,5:x-}", 255).str());// 8. Including when it's been zero-paddedEXPECT_EQ(" 0x0ff", formatv("{0,7:x3}", 255).str());EXPECT_EQ(" 0x00ff", formatv("{0,7:x+4}", 255).str());EXPECT_EQ(" 000FF", formatv("{0,7:X-5}", 255).str());EXPECT_EQ(" 0000ff", formatv("{0,7:x-6}", 255).str());// 9. Precision with default format specifier should work tooEXPECT_EQ(" 255", formatv("{0,7:3}", 255).str());EXPECT_EQ(" 0255", formatv("{0,7:4}", 255).str());EXPECT_EQ(" 00255", formatv("{0,7:5}", 255).str());EXPECT_EQ(" 000255", formatv("{0,7:6}", 255).str());}TEST(FormatVariadicTest, PointerFormatting) {// 1. Trivial cases. Hex is default. Default Precision is pointer width.if (sizeof(void *) == 4) {EXPECT_EQ("0x00000000", formatv("{0}", (void *)0).str());EXPECT_EQ("0x00000ABC", formatv("{0}", (void *)0xABC).str());} else {EXPECT_EQ("0x0000000000000000", formatv("{0}", (void *)0).str());EXPECT_EQ("0x0000000000000ABC", formatv("{0}", (void *)0xABC).str());}// 2. But we can reduce the precision explicitly.EXPECT_EQ("0x0", formatv("{0:0}", (void *)0).str());EXPECT_EQ("0xABC", formatv("{0:0}", (void *)0xABC).str());EXPECT_EQ("0x0000", formatv("{0:4}", (void *)0).str());EXPECT_EQ("0x0ABC", formatv("{0:4}", (void *)0xABC).str());// 3. various hex prefixes.EXPECT_EQ("0x0ABC", formatv("{0:X4}", (void *)0xABC).str());EXPECT_EQ("0x0abc", formatv("{0:x4}", (void *)0xABC).str());EXPECT_EQ("0ABC", formatv("{0:X-4}", (void *)0xABC).str());EXPECT_EQ("0abc", formatv("{0:x-4}", (void *)0xABC).str());}TEST(FormatVariadicTest, IntegralNumberFormatting) {// 1. Test comma grouping with default widths and precisions.EXPECT_EQ("0", formatv("{0:N}", 0).str());EXPECT_EQ("10", formatv("{0:N}", 10).str());EXPECT_EQ("100", formatv("{0:N}", 100).str());EXPECT_EQ("1,000", formatv("{0:N}", 1000).str());EXPECT_EQ("1,234,567,890", formatv("{0:N}", 1234567890).str());EXPECT_EQ("-10", formatv("{0:N}", -10).str());EXPECT_EQ("-100", formatv("{0:N}", -100).str());EXPECT_EQ("-1,000", formatv("{0:N}", -1000).str());EXPECT_EQ("-1,234,567,890", formatv("{0:N}", -1234567890).str());// 2. If there is no comma, width and precision pad to the same absolute// size.EXPECT_EQ(" 1", formatv("{0,2:N}", 1).str());// 3. But if there is a comma or negative sign, width factors them in but// precision doesn't.EXPECT_EQ(" 1,000", formatv("{0,6:N}", 1000).str());EXPECT_EQ(" -1,000", formatv("{0,7:N}", -1000).str());// 4. Large widths all line up.EXPECT_EQ(" 1,000", formatv("{0,11:N}", 1000).str());EXPECT_EQ(" -1,000", formatv("{0,11:N}", -1000).str());EXPECT_EQ(" -100,000", formatv("{0,11:N}", -100000).str());}TEST(FormatVariadicTest, StringFormatting) {const char FooArray[] = "FooArray";const char *FooPtr = "FooPtr";llvm::StringRef FooRef("FooRef");constexpr StringLiteral FooLiteral("FooLiteral");std::string FooString("FooString");// 1. Test that we can print various types of strings.EXPECT_EQ(FooArray, formatv("{0}", FooArray).str());EXPECT_EQ(FooPtr, formatv("{0}", FooPtr).str());EXPECT_EQ(FooRef, formatv("{0}", FooRef).str());EXPECT_EQ(FooLiteral, formatv("{0}", FooLiteral).str());EXPECT_EQ(FooString, formatv("{0}", FooString).str());// 2. Test that the precision specifier prints the correct number of// characters.EXPECT_EQ("FooA", formatv("{0:4}", FooArray).str());EXPECT_EQ("FooP", formatv("{0:4}", FooPtr).str());EXPECT_EQ("FooR", formatv("{0:4}", FooRef).str());EXPECT_EQ("FooS", formatv("{0:4}", FooString).str());// 3. And that padding works.EXPECT_EQ(" FooA", formatv("{0,6:4}", FooArray).str());EXPECT_EQ(" FooP", formatv("{0,6:4}", FooPtr).str());EXPECT_EQ(" FooR", formatv("{0,6:4}", FooRef).str());EXPECT_EQ(" FooS", formatv("{0,6:4}", FooString).str());}TEST(FormatVariadicTest, CharFormatting) {// 1. Not much to see here. Just print a char with and without padding.EXPECT_EQ("C", formatv("{0}", 'C').str());EXPECT_EQ(" C", formatv("{0,3}", 'C').str());// 2. char is really an integral type though, where the only difference is// that the "default" is to print the ASCII. So if a non-default presentation// specifier exists, it should print as an integer.EXPECT_EQ("37", formatv("{0:D}", (char)37).str());EXPECT_EQ(" 037", formatv("{0,5:D3}", (char)37).str());}TEST(FormatVariadicTest, BoolTest) {// 1. Default style is lowercase text (same as 't')EXPECT_EQ("true", formatv("{0}", true).str());EXPECT_EQ("false", formatv("{0}", false).str());EXPECT_EQ("true", formatv("{0:t}", true).str());EXPECT_EQ("false", formatv("{0:t}", false).str());// 2. T - uppercase textEXPECT_EQ("TRUE", formatv("{0:T}", true).str());EXPECT_EQ("FALSE", formatv("{0:T}", false).str());// 3. D / d - integralEXPECT_EQ("1", formatv("{0:D}", true).str());EXPECT_EQ("0", formatv("{0:D}", false).str());EXPECT_EQ("1", formatv("{0:d}", true).str());EXPECT_EQ("0", formatv("{0:d}", false).str());// 4. Y - uppercase yes/noEXPECT_EQ("YES", formatv("{0:Y}", true).str());EXPECT_EQ("NO", formatv("{0:Y}", false).str());// 5. y - lowercase yes/noEXPECT_EQ("yes", formatv("{0:y}", true).str());EXPECT_EQ("no", formatv("{0:y}", false).str());}TEST(FormatVariadicTest, DoubleFormatting) {// Test exponents, fixed point, and percent formatting.// 1. Signed, unsigned, and zero exponent format.EXPECT_EQ("0.000000E+00", formatv("{0:E}", 0.0).str());EXPECT_EQ("-0.000000E+00", formatv("{0:E}", -0.0).str());EXPECT_EQ("1.100000E+00", formatv("{0:E}", 1.1).str());EXPECT_EQ("-1.100000E+00", formatv("{0:E}", -1.1).str());EXPECT_EQ("1.234568E+03", formatv("{0:E}", 1234.5678).str());EXPECT_EQ("-1.234568E+03", formatv("{0:E}", -1234.5678).str());EXPECT_EQ("1.234568E-03", formatv("{0:E}", .0012345678).str());EXPECT_EQ("-1.234568E-03", formatv("{0:E}", -.0012345678).str());// 2. With padding and precision.EXPECT_EQ(" 0.000E+00", formatv("{0,11:E3}", 0.0).str());EXPECT_EQ(" -1.100E+00", formatv("{0,11:E3}", -1.1).str());EXPECT_EQ(" 1.235E+03", formatv("{0,11:E3}", 1234.5678).str());EXPECT_EQ(" -1.235E-03", formatv("{0,11:E3}", -.0012345678).str());// 3. Signed, unsigned, and zero fixed point format.EXPECT_EQ("0.00", formatv("{0:F}", 0.0).str());EXPECT_EQ("-0.00", formatv("{0:F}", -0.0).str());EXPECT_EQ("1.10", formatv("{0:F}", 1.1).str());EXPECT_EQ("-1.10", formatv("{0:F}", -1.1).str());EXPECT_EQ("1234.57", formatv("{0:F}", 1234.5678).str());EXPECT_EQ("-1234.57", formatv("{0:F}", -1234.5678).str());EXPECT_EQ("0.00", formatv("{0:F}", .0012345678).str());EXPECT_EQ("-0.00", formatv("{0:F}", -.0012345678).str());// 2. With padding and precision.EXPECT_EQ(" 0.000", formatv("{0,8:F3}", 0.0).str());EXPECT_EQ(" -1.100", formatv("{0,8:F3}", -1.1).str());EXPECT_EQ("1234.568", formatv("{0,8:F3}", 1234.5678).str());EXPECT_EQ(" -0.001", formatv("{0,8:F3}", -.0012345678).str());}TEST(FormatVariadicTest, CustomPaddingCharacter) {// 1. Padding with custom characterEXPECT_EQ("==123", formatv("{0,=+5}", 123).str());EXPECT_EQ("=123=", formatv("{0,==5}", 123).str());EXPECT_EQ("123==", formatv("{0,=-5}", 123).str());// 2. Combined with zero paddingEXPECT_EQ("=00123=", formatv("{0,==7:5}", 123).str());}struct format_tuple {const char *Fmt;explicit format_tuple(const char *Fmt) : Fmt(Fmt) {}template <typename... Ts> auto operator()(Ts &&... Values) const {return formatv(Fmt, std::forward<Ts>(Values)...);}};TEST(FormatVariadicTest, BigTest) {using Tuple =std::tuple<char, int, const char *, StringRef, std::string, double, float,void *, int, double, int64_t, uint64_t, double, uint8_t>;Tuple Ts[] = {Tuple('a', 1, "Str", StringRef(), std::string(), 3.14159, -.17532f,(void *)nullptr, 123456, 6.02E23, -908234908423, 908234908422234,std::numeric_limits<double>::quiet_NaN(), 0xAB),Tuple('x', 0xDDB5B, "LongerStr", "StringRef", "std::string", -2.7,.08215f, (void *)nullptr, 0, 6.62E-34, -908234908423,908234908422234, std::numeric_limits<double>::infinity(), 0x0)};// Test long string formatting with many edge cases combined.const char *Intro ="There are {{{0}} items in the tuple, and {{{1}} tuple(s) in the array.";const char *Header ="{0,6}|{1,8}|{2,=10}|{3,=10}|{4,=13}|{5,7}|{6,7}|{7,10}|{8,""-7}|{9,10}|{10,16}|{11,17}|{12,6}|{13,4}";const char *Line ="{0,6}|{1,8:X}|{2,=10}|{3,=10:5}|{4,=13}|{5,7:3}|{6,7:P2}|{7,""10:X8}|{8,-7:N}|{9,10:E4}|{10,16:N}|{11,17:D}|{12,6}|{13,""4:X}";std::string S;llvm::raw_string_ostream Stream(S);Stream << formatv(Intro, std::tuple_size<Tuple>::value,llvm::array_lengthof(Ts))<< "\n";Stream << formatv(Header, "Char", "HexInt", "Str", "Ref", "std::str","double", "float", "pointer", "comma", "exp", "bigint","bigint2", "limit", "byte")<< "\n";for (auto &Item : Ts) {Stream << llvm::apply_tuple(format_tuple(Line), Item) << "\n";}Stream.flush();const char *Expected =R"foo(There are {14} items in the tuple, and {2} tuple(s) in the array.Char| HexInt| Str | Ref | std::str | double| float| pointer|comma | exp| bigint| bigint2| limit|bytea| 0x1| Str | | | 3.142|-17.53%|0x00000000|123,456|6.0200E+23|-908,234,908,423| 908234908422234| nan|0xABx| 0xDDB5B|LongerStr | Strin | std::string | -2.700| 8.21%|0x00000000|0 |6.6200E-34|-908,234,908,423| 908234908422234| INF| 0x0)foo";EXPECT_EQ(Expected, S);}TEST(FormatVariadicTest, Range) {std::vector<int> IntRange = {1, 1, 2, 3, 5, 8, 13};// 1. Simple range with default separator and element style.EXPECT_EQ("1, 1, 2, 3, 5, 8, 13",formatv("{0}", make_range(IntRange.begin(), IntRange.end())).str());EXPECT_EQ("1, 2, 3, 5, 8",formatv("{0}", make_range(IntRange.begin() + 1, IntRange.end() - 1)).str());// 2. Non-default separatorEXPECT_EQ("1/1/2/3/5/8/13",formatv("{0:$[/]}", make_range(IntRange.begin(), IntRange.end())).str());// 3. Default separator, non-default element style.EXPECT_EQ("0x1, 0x1, 0x2, 0x3, 0x5, 0x8, 0xd",formatv("{0:@[x]}", make_range(IntRange.begin(), IntRange.end())).str());// 4. Non-default separator and element style.EXPECT_EQ("0x1 + 0x1 + 0x2 + 0x3 + 0x5 + 0x8 + 0xd",formatv("{0:$[ + ]@[x]}", make_range(IntRange.begin(), IntRange.end())).str());// 5. Element style and/or separator using alternate delimeters to allow using// delimeter characters as part of the separator.EXPECT_EQ("<0x1><0x1><0x2><0x3><0x5><0x8><0xd>",formatv("<{0:$[><]@(x)}>", make_range(IntRange.begin(), IntRange.end())).str());EXPECT_EQ("[0x1][0x1][0x2][0x3][0x5][0x8][0xd]",formatv("[{0:$(][)@[x]}]", make_range(IntRange.begin(), IntRange.end())).str());EXPECT_EQ("(0x1)(0x1)(0x2)(0x3)(0x5)(0x8)(0xd)",formatv("({0:$<)(>@<x>})", make_range(IntRange.begin(), IntRange.end())).str());// 5. Empty range.EXPECT_EQ("", formatv("{0:$[+]@[x]}",make_range(IntRange.begin(), IntRange.begin())).str());// 6. Empty separator and style.EXPECT_EQ("11235813",formatv("{0:$[]@<>}", make_range(IntRange.begin(), IntRange.end())).str());}TEST(FormatVariadicTest, Adapter) {class Negative : public FormatAdapter<int> {public:explicit Negative(int N) : FormatAdapter<int>(std::move(N)) {}void format(raw_ostream &S, StringRef Options) override { S << -Item; }};EXPECT_EQ("-7", formatv("{0}", Negative(7)).str());int N = 171;EXPECT_EQ(" 171 ",formatv("{0}", fmt_align(N, AlignStyle::Center, 7)).str());EXPECT_EQ("--171--",formatv("{0}", fmt_align(N, AlignStyle::Center, 7, '-')).str());EXPECT_EQ(" 171 ", formatv("{0}", fmt_pad(N, 1, 3)).str());EXPECT_EQ("171171171171171", formatv("{0}", fmt_repeat(N, 5)).str());EXPECT_EQ(" ABABABABAB ",formatv("{0:X-}", fmt_pad(fmt_repeat(N, 5), 1, 3)).str());EXPECT_EQ(" AB AB AB AB AB ",formatv("{0,=34:X-}", fmt_repeat(fmt_pad(N, 1, 3), 5)).str());}TEST(FormatVariadicTest, MoveConstructor) {auto fmt = formatv("{0} {1}", 1, 2);auto fmt2 = std::move(fmt);std::string S = std::string(fmt2);EXPECT_EQ("1 2", S);}TEST(FormatVariadicTest, ImplicitConversions) {std::string S = std::string(formatv("{0} {1}", 1, 2));EXPECT_EQ("1 2", S);SmallString<4> S2 = formatv("{0} {1}", 1, 2);EXPECT_EQ("1 2", S2);}TEST(FormatVariadicTest, FormatAdapter) {EXPECT_EQ("Format", formatv("{0}", Format(1)).str());Format var(1);EXPECT_EQ("Format", formatv("{0}", var).str());EXPECT_EQ("Format", formatv("{0}", std::move(var)).str());// Not supposed to compile// const Format cvar(1);// EXPECT_EQ("Format", formatv("{0}", cvar).str());}TEST(FormatVariadicTest, FormatFormatvObject) {EXPECT_EQ("Format", formatv("F{0}t", formatv("o{0}a", "rm")).str());EXPECT_EQ("[ ! ]", formatv("[{0,+5}]", formatv("{0,-2}", "!")).str());}namespace {struct Recorder {int Copied = 0, Moved = 0;Recorder() = default;Recorder(const Recorder &Copy) : Copied(1 + Copy.Copied), Moved(Copy.Moved) {}Recorder(const Recorder &&Move): Copied(Move.Copied), Moved(1 + Move.Moved) {}};} // namespacenamespace llvm {template <> struct format_provider<Recorder> {static void format(const Recorder &R, raw_ostream &OS, StringRef style) {OS << R.Copied << "C " << R.Moved << "M";}};} // namespaceTEST(FormatVariadicTest, CopiesAndMoves) {Recorder R;EXPECT_EQ("0C 0M", formatv("{0}", R).str());EXPECT_EQ("0C 3M", formatv("{0}", std::move(R)).str());EXPECT_EQ("0C 3M", formatv("{0}", Recorder()).str());EXPECT_EQ(0, R.Copied);EXPECT_EQ(0, R.Moved);}namespace adl {struct X {};raw_ostream &operator<<(raw_ostream &OS, const X &) { return OS << "X"; }} // namespace adlTEST(FormatVariadicTest, FormatStreamable) {adl::X X;EXPECT_EQ("X", formatv("{0}", X).str());}TEST(FormatVariadicTest, FormatError) {auto E1 = make_error<StringError>("X", inconvertibleErrorCode());EXPECT_EQ("X", formatv("{0}", E1).str());EXPECT_TRUE(E1.isA<StringError>()); // not consumedEXPECT_EQ("X", formatv("{0}", fmt_consume(std::move(E1))).str());EXPECT_FALSE(E1.isA<StringError>()); // consumed}
//===- llvm/unittest/Support/FileUtilitiesTest.cpp - unit tests -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/FileUtilities.h"#include "llvm/Support/Errc.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/Path.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"#include <fstream>using namespace llvm;using namespace llvm::sys;using llvm::unittest::TempDir;#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}namespace {TEST(writeFileAtomicallyTest, Test) {// Create unique temporary directory for these testsTempDir RootTestDirectory("writeFileAtomicallyTest", /*Unique*/ true);SmallString<128> FinalTestfilePath(RootTestDirectory.path());sys::path::append(FinalTestfilePath, "foo.txt");const std::string TempUniqTestFileModel =std::string(FinalTestfilePath) + "-%%%%%%%%";const std::string TestfileContent = "fooFOOfoo";llvm::Error Err = llvm::writeFileAtomically(TempUniqTestFileModel, FinalTestfilePath, TestfileContent);ASSERT_FALSE(static_cast<bool>(Err));std::ifstream FinalFileStream(std::string(FinalTestfilePath.str()));std::string FinalFileContent;FinalFileStream >> FinalFileContent;ASSERT_EQ(FinalFileContent, TestfileContent);}} // anonymous namespace
//===- llvm/unittest/Support/FileOutputBuffer.cpp - unit tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/FileOutputBuffer.h"#include "llvm/Support/Errc.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/Path.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::sys;#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}namespace {TEST(FileOutputBuffer, Test) {// Create unique temporary directory for these testsSmallString<128> TestDirectory;{ASSERT_NO_ERROR(fs::createUniqueDirectory("FileOutputBuffer-test", TestDirectory));}// TEST 1: Verify commit case.SmallString<128> File1(TestDirectory);File1.append("/file1");{Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =FileOutputBuffer::create(File1, 8192);ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError()));std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr;// Start buffer with special header.memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);// Write to end of buffer to verify it is writable.memcpy(Buffer->getBufferEnd() - 20, "AABBCCDDEEFFGGHHIIJJ", 20);// Commit buffer.ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit()));}// Verify file is correct size.uint64_t File1Size;ASSERT_NO_ERROR(fs::file_size(Twine(File1), File1Size));ASSERT_EQ(File1Size, 8192ULL);ASSERT_NO_ERROR(fs::remove(File1.str()));// TEST 2: Verify abort case.SmallString<128> File2(TestDirectory);File2.append("/file2");{Expected<std::unique_ptr<FileOutputBuffer>> Buffer2OrErr =FileOutputBuffer::create(File2, 8192);ASSERT_NO_ERROR(errorToErrorCode(Buffer2OrErr.takeError()));std::unique_ptr<FileOutputBuffer> &Buffer2 = *Buffer2OrErr;// Fill buffer with special header.memcpy(Buffer2->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);// Do *not* commit buffer.}// Verify file does not exist (because buffer not committed).ASSERT_EQ(fs::access(Twine(File2), fs::AccessMode::Exist),errc::no_such_file_or_directory);ASSERT_NO_ERROR(fs::remove(File2.str()));// TEST 3: Verify sizing down case.SmallString<128> File3(TestDirectory);File3.append("/file3");{Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =FileOutputBuffer::create(File3, 8192000);ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError()));std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr;// Start buffer with special header.memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);// Write to end of buffer to verify it is writable.memcpy(Buffer->getBufferEnd() - 20, "AABBCCDDEEFFGGHHIIJJ", 20);ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit()));}// Verify file is correct size.uint64_t File3Size;ASSERT_NO_ERROR(fs::file_size(Twine(File3), File3Size));ASSERT_EQ(File3Size, 8192000ULL);ASSERT_NO_ERROR(fs::remove(File3.str()));// TEST 4: Verify file can be made executable.SmallString<128> File4(TestDirectory);File4.append("/file4");{Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =FileOutputBuffer::create(File4, 8192, FileOutputBuffer::F_executable);ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError()));std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr;// Start buffer with special header.memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);// Commit buffer.ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit()));}// Verify file exists and is executable.fs::file_status Status;ASSERT_NO_ERROR(fs::status(Twine(File4), Status));bool IsExecutable = (Status.permissions() & fs::owner_exe);EXPECT_TRUE(IsExecutable);ASSERT_NO_ERROR(fs::remove(File4.str()));// TEST 5: In-memory buffer works as expected.SmallString<128> File5(TestDirectory);File5.append("/file5");{Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =FileOutputBuffer::create(File5, 8000, FileOutputBuffer::F_no_mmap);ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError()));std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr;// Start buffer with special header.memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit()));// Write to end of buffer to verify it is writable.memcpy(Buffer->getBufferEnd() - 20, "AABBCCDDEEFFGGHHIIJJ", 20);// Commit buffer.ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit()));}// Verify file is correct size.uint64_t File5Size;ASSERT_NO_ERROR(fs::file_size(Twine(File5), File5Size));ASSERT_EQ(File5Size, 8000ULL);ASSERT_NO_ERROR(fs::remove(File5.str()));// TEST 6: Create an empty file.SmallString<128> File6(TestDirectory);File6.append("/file6");{Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =FileOutputBuffer::create(File6, 0);ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError()));ASSERT_NO_ERROR(errorToErrorCode((*BufferOrErr)->commit()));}uint64_t File6Size;ASSERT_NO_ERROR(fs::file_size(Twine(File6), File6Size));ASSERT_EQ(File6Size, 0ULL);ASSERT_NO_ERROR(fs::remove(File6.str()));// Clean up.ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));}} // anonymous namespace
//===-- FileCollectorTest.cpp -----------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "gmock/gmock.h"#include "gtest/gtest.h"#include "llvm/Support/FileCollector.h"#include "llvm/Support/FileSystem.h"#include "llvm/Testing/Support/SupportHelpers.h"using namespace llvm;using llvm::unittest::TempDir;using llvm::unittest::TempFile;using llvm::unittest::TempLink;namespace llvm {namespace vfs {inline bool operator==(const llvm::vfs::YAMLVFSEntry &LHS,const llvm::vfs::YAMLVFSEntry &RHS) {return LHS.VPath == RHS.VPath && LHS.RPath == RHS.RPath;}} // namespace vfs} // namespace llvmnamespace {class TestingFileCollector : public FileCollector {public:using FileCollector::FileCollector;using FileCollector::Root;using FileCollector::Seen;using FileCollector::VFSWriter;bool hasSeen(StringRef fs) {return Seen.find(fs) != Seen.end();}};} // end anonymous namespaceTEST(FileCollectorTest, addFile) {TempDir root("add_file_root", /*Unique*/ true);std::string root_fs(root.path());TestingFileCollector FileCollector(root_fs, root_fs);FileCollector.addFile("/path/to/a");FileCollector.addFile("/path/to/b");FileCollector.addFile("/path/to/c");// Make sure the root is correct.EXPECT_EQ(FileCollector.Root, root_fs);// Make sure we've seen all the added files.EXPECT_TRUE(FileCollector.hasSeen("/path/to/a"));EXPECT_TRUE(FileCollector.hasSeen("/path/to/b"));EXPECT_TRUE(FileCollector.hasSeen("/path/to/c"));// Make sure we've only seen the added files.EXPECT_FALSE(FileCollector.hasSeen("/path/to/d"));}TEST(FileCollectorTest, addDirectory) {TempDir file_root("file_root", /*Unique*/ true);llvm::SmallString<128> aaa(file_root.path());llvm::sys::path::append(aaa, "aaa");TempFile a(aaa.str());llvm::SmallString<128> bbb(file_root.path());llvm::sys::path::append(bbb, "bbb");TempFile b(bbb.str());llvm::SmallString<128> ccc(file_root.path());llvm::sys::path::append(ccc, "ccc");TempFile c(ccc.str());std::string root_fs(file_root.path());TestingFileCollector FileCollector(root_fs, root_fs);FileCollector.addDirectory(file_root.path());// Make sure the root is correct.EXPECT_EQ(FileCollector.Root, root_fs);// Make sure we've seen all the added files.EXPECT_TRUE(FileCollector.hasSeen(a.path()));EXPECT_TRUE(FileCollector.hasSeen(b.path()));EXPECT_TRUE(FileCollector.hasSeen(c.path()));// Make sure we've only seen the added files.llvm::SmallString<128> ddd(file_root.path());llvm::sys::path::append(ddd, "ddd");TempFile d(ddd);EXPECT_FALSE(FileCollector.hasSeen(d.path()));}TEST(FileCollectorTest, copyFiles) {TempDir file_root("file_root", /*Unique*/ true);TempFile a(file_root.path("aaa"));TempFile b(file_root.path("bbb"));TempFile c(file_root.path("ccc"));// Create file collector and add files.TempDir root("copy_files_root", /*Unique*/ true);std::string root_fs(root.path());TestingFileCollector FileCollector(root_fs, root_fs);FileCollector.addFile(a.path());FileCollector.addFile(b.path());FileCollector.addFile(c.path());// Make sure we can copy the files.std::error_code ec = FileCollector.copyFiles(true);EXPECT_FALSE(ec);// Now add a bogus file and make sure we error out.FileCollector.addFile("/some/bogus/file");ec = FileCollector.copyFiles(true);EXPECT_TRUE(ec);// However, if stop_on_error is true the copy should still succeed.ec = FileCollector.copyFiles(false);EXPECT_FALSE(ec);}TEST(FileCollectorTest, recordAndConstructDirectory) {TempDir file_root("dir_root", /*Unique*/ true);TempDir subdir(file_root.path("subdir"));TempDir subdir2(file_root.path("subdir2"));TempFile a(subdir2.path("a"));// Create file collector and add files.TempDir root("copy_files_root", /*Unique*/ true);std::string root_fs(root.path());TestingFileCollector FileCollector(root_fs, root_fs);FileCollector.addFile(a.path());// The empty directory isn't seen until we add it.EXPECT_TRUE(FileCollector.hasSeen(a.path()));EXPECT_FALSE(FileCollector.hasSeen(subdir.path()));FileCollector.addFile(subdir.path());EXPECT_TRUE(FileCollector.hasSeen(subdir.path()));// Make sure we can construct the directory.std::error_code ec = FileCollector.copyFiles(true);EXPECT_FALSE(ec);bool IsDirectory = false;llvm::SmallString<128> SubdirInRoot = root.path();llvm::sys::path::append(SubdirInRoot,llvm::sys::path::relative_path(subdir.path()));ec = sys::fs::is_directory(SubdirInRoot, IsDirectory);EXPECT_FALSE(ec);ASSERT_TRUE(IsDirectory);}TEST(FileCollectorTest, recordVFSAccesses) {TempDir file_root("dir_root", /*Unique*/ true);TempDir subdir(file_root.path("subdir"));TempDir subdir2(file_root.path("subdir2"));TempFile a(subdir2.path("a"));TempFile b(file_root.path("b"));TempDir subdir3(file_root.path("subdir3"));TempFile subdir3a(subdir3.path("aa"));TempDir subdir3b(subdir3.path("subdirb"));{ TempFile subdir3fileremoved(subdir3.path("removed")); }// Create file collector and add files.TempDir root("copy_files_root", /*Unique*/ true);std::string root_fs(root.path());auto Collector = std::make_shared<TestingFileCollector>(root_fs, root_fs);auto VFS =FileCollector::createCollectorVFS(vfs::getRealFileSystem(), Collector);VFS->status(a.path());EXPECT_TRUE(Collector->hasSeen(a.path()));VFS->openFileForRead(b.path());EXPECT_TRUE(Collector->hasSeen(b.path()));VFS->status(subdir.path());EXPECT_TRUE(Collector->hasSeen(subdir.path()));#ifndef _WIN32std::error_code EC;auto It = VFS->dir_begin(subdir3.path(), EC);EXPECT_FALSE(EC);EXPECT_TRUE(Collector->hasSeen(subdir3.path()));EXPECT_TRUE(Collector->hasSeen(subdir3a.path()));EXPECT_TRUE(Collector->hasSeen(subdir3b.path()));std::string RemovedFileName((Twine(subdir3.path("removed"))).str());EXPECT_FALSE(Collector->hasSeen(RemovedFileName));#endif}#ifndef _WIN32TEST(FileCollectorTest, Symlinks) {// Root where the original files live.TempDir file_root("file_root", /*Unique*/ true);// Create some files in the file root.TempFile a(file_root.path("aaa"));TempFile b(file_root.path("bbb"));TempFile c(file_root.path("ccc"));// Create a directory foo with file ddd.TempDir foo(file_root.path("foo"));TempFile d(foo.path("ddd"));// Create a file eee in the foo's parent directory.TempFile e(foo.path("../eee"));// Create a symlink bar pointing to foo.TempLink symlink(file_root.path("foo"), file_root.path("bar"));// Root where files are copied to.TempDir reproducer_root("reproducer_root", /*Unique*/ true);std::string root_fs(reproducer_root.path());TestingFileCollector FileCollector(root_fs, root_fs);// Add all the files to the collector.FileCollector.addFile(a.path());FileCollector.addFile(b.path());FileCollector.addFile(c.path());FileCollector.addFile(d.path());FileCollector.addFile(e.path());FileCollector.addFile(file_root.path() + "/bar/ddd");auto mapping = FileCollector.VFSWriter.getMappings();{// Make sure the common case works.std::string vpath = (file_root.path() + "/aaa").str();std::string rpath =(reproducer_root.path() + file_root.path() + "/aaa").str();printf("%s -> %s\n", vpath.c_str(), rpath.c_str());EXPECT_THAT(mapping, testing::Contains(vfs::YAMLVFSEntry(vpath, rpath)));}{// Make sure the virtual path points to the real source path.std::string vpath = (file_root.path() + "/bar/ddd").str();std::string rpath =(reproducer_root.path() + file_root.path() + "/foo/ddd").str();printf("%s -> %s\n", vpath.c_str(), rpath.c_str());EXPECT_THAT(mapping, testing::Contains(vfs::YAMLVFSEntry(vpath, rpath)));}{// Make sure that .. is removed from the source path.std::string vpath = (file_root.path() + "/eee").str();std::string rpath =(reproducer_root.path() + file_root.path() + "/eee").str();printf("%s -> %s\n", vpath.c_str(), rpath.c_str());EXPECT_THAT(mapping, testing::Contains(vfs::YAMLVFSEntry(vpath, rpath)));}}TEST(FileCollectorTest, recordVFSSymlinkAccesses) {TempDir file_root("dir_root", /*Unique*/ true);TempFile a(file_root.path("a"));TempLink symlink(file_root.path("a"), file_root.path("b"));// Create file collector and add files.TempDir root("copy_files_root", true);std::string root_fs(root.path());auto Collector = std::make_shared<TestingFileCollector>(root_fs, root_fs);auto VFS =FileCollector::createCollectorVFS(vfs::getRealFileSystem(), Collector);SmallString<256> Output;VFS->getRealPath(symlink.path(), Output);EXPECT_TRUE(Collector->hasSeen(a.path()));EXPECT_TRUE(Collector->hasSeen(symlink.path()));}#endif
//===- llvm/unittest/Support/FSUniqueIDTest.cpp - Test sys::fs::UniqueID --===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/FileSystem/UniqueID.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::sys::fs;namespace {TEST(FSUniqueIDTest, construct) {EXPECT_EQ(20u, UniqueID(20, 10).getDevice());EXPECT_EQ(10u, UniqueID(20, 10).getFile());}TEST(FSUniqueIDTest, equals) {EXPECT_EQ(UniqueID(20, 10), UniqueID(20, 10));EXPECT_NE(UniqueID(20, 20), UniqueID(20, 10));EXPECT_NE(UniqueID(10, 10), UniqueID(20, 10));}TEST(FSUniqueIDTest, less) {EXPECT_FALSE(UniqueID(20, 2) < UniqueID(20, 2));EXPECT_FALSE(UniqueID(20, 3) < UniqueID(20, 2));EXPECT_FALSE(UniqueID(30, 2) < UniqueID(20, 2));EXPECT_FALSE(UniqueID(30, 2) < UniqueID(20, 40));EXPECT_TRUE(UniqueID(20, 2) < UniqueID(20, 3));EXPECT_TRUE(UniqueID(20, 2) < UniqueID(30, 2));EXPECT_TRUE(UniqueID(20, 40) < UniqueID(30, 2));}} // anonymous namespace
//===------ unittests/ExtensibleRTTITest.cpp - Extensible RTTI Tests ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ExtensibleRTTI.h"#include "llvm/Support/Casting.h"#include "gtest/gtest.h"using namespace llvm;namespace {class MyBaseType : public RTTIExtends<MyBaseType, RTTIRoot> {public:static char ID;};class MyDerivedType : public RTTIExtends<MyDerivedType, MyBaseType> {public:static char ID;};class MyOtherDerivedType : public RTTIExtends<MyOtherDerivedType, MyBaseType> {public:static char ID;};class MyDeeperDerivedType: public RTTIExtends<MyDeeperDerivedType, MyDerivedType> {public:static char ID;};char MyBaseType::ID = 0;char MyDerivedType::ID = 0;char MyOtherDerivedType::ID = 0;char MyDeeperDerivedType::ID = 0;TEST(ExtensibleRTTI, isa) {MyBaseType B;MyDerivedType D;MyDeeperDerivedType DD;EXPECT_TRUE(isa<MyBaseType>(B));EXPECT_FALSE(isa<MyDerivedType>(B));EXPECT_FALSE(isa<MyOtherDerivedType>(B));EXPECT_FALSE(isa<MyDeeperDerivedType>(B));EXPECT_TRUE(isa<MyBaseType>(D));EXPECT_TRUE(isa<MyDerivedType>(D));EXPECT_FALSE(isa<MyOtherDerivedType>(D));EXPECT_FALSE(isa<MyDeeperDerivedType>(D));EXPECT_TRUE(isa<MyBaseType>(DD));EXPECT_TRUE(isa<MyDerivedType>(DD));EXPECT_FALSE(isa<MyOtherDerivedType>(DD));EXPECT_TRUE(isa<MyDeeperDerivedType>(DD));}TEST(ExtensibleRTTI, cast) {MyDerivedType D;MyBaseType &BD = D;(void)cast<MyBaseType>(D);(void)cast<MyBaseType>(BD);(void)cast<MyDerivedType>(BD);}TEST(ExtensibleRTTI, dyn_cast) {MyBaseType B;MyDerivedType D;MyBaseType &BD = D;EXPECT_EQ(dyn_cast<MyDerivedType>(&B), nullptr);EXPECT_EQ(dyn_cast<MyDerivedType>(&D), &D);EXPECT_EQ(dyn_cast<MyBaseType>(&BD), &BD);EXPECT_EQ(dyn_cast<MyDerivedType>(&BD), &D);}} // namespace
//===----- unittests/ErrorTest.cpp - Error.h tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Error.h"#include "llvm-c/Error.h"#include "llvm/ADT/Twine.h"#include "llvm/Support/Errc.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest-spi.h"#include "gtest/gtest.h"#include <memory>using namespace llvm;namespace {// Custom error class with a default base class and some random 'info' attached.class CustomError : public ErrorInfo<CustomError> {public:// Create an error with some info attached.CustomError(int Info) : Info(Info) {}// Get the info attached to this error.int getInfo() const { return Info; }// Log this error to a stream.void log(raw_ostream &OS) const override {OS << "CustomError {" << getInfo() << "}";}std::error_code convertToErrorCode() const override {llvm_unreachable("CustomError doesn't support ECError conversion");}// Used by ErrorInfo::classID.static char ID;protected:// This error is subclassed below, but we can't use inheriting constructors// yet, so we can't propagate the constructors through ErrorInfo. Instead// we have to have a default constructor and have the subclass initialize all// fields.CustomError() : Info(0) {}int Info;};char CustomError::ID = 0;// Custom error class with a custom base class and some additional random// 'info'.class CustomSubError : public ErrorInfo<CustomSubError, CustomError> {public:// Create a sub-error with some info attached.CustomSubError(int Info, int ExtraInfo) : ExtraInfo(ExtraInfo) {this->Info = Info;}// Get the extra info attached to this error.int getExtraInfo() const { return ExtraInfo; }// Log this error to a stream.void log(raw_ostream &OS) const override {OS << "CustomSubError { " << getInfo() << ", " << getExtraInfo() << "}";}std::error_code convertToErrorCode() const override {llvm_unreachable("CustomSubError doesn't support ECError conversion");}// Used by ErrorInfo::classID.static char ID;protected:int ExtraInfo;};char CustomSubError::ID = 0;static Error handleCustomError(const CustomError &CE) {return Error::success();}static void handleCustomErrorVoid(const CustomError &CE) {}static Error handleCustomErrorUP(std::unique_ptr<CustomError> CE) {return Error::success();}static void handleCustomErrorUPVoid(std::unique_ptr<CustomError> CE) {}// Test that success values implicitly convert to false, and don't cause crashes// once they've been implicitly converted.TEST(Error, CheckedSuccess) {Error E = Error::success();EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'";}// Test that unchecked success values cause an abort.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, UncheckedSuccess) {EXPECT_DEATH({ Error E = Error::success(); },"Program aborted due to an unhandled Error:")<< "Unchecked Error Succes value did not cause abort()";}#endif// ErrorAsOutParameter tester.void errAsOutParamHelper(Error &Err) {ErrorAsOutParameter ErrAsOutParam(&Err);// Verify that checked flag is raised - assignment should not crash.Err = Error::success();// Raise the checked bit manually - caller should still have to test the// error.(void)!!Err;}// Test that ErrorAsOutParameter sets the checked flag on construction.TEST(Error, ErrorAsOutParameterChecked) {Error E = Error::success();errAsOutParamHelper(E);(void)!!E;}// Test that ErrorAsOutParameter clears the checked flag on destruction.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, ErrorAsOutParameterUnchecked) {EXPECT_DEATH({ Error E = Error::success(); errAsOutParamHelper(E); },"Program aborted due to an unhandled Error:")<< "ErrorAsOutParameter did not clear the checked flag on destruction.";}#endif// Check that we abort on unhandled failure cases. (Force conversion to bool// to make sure that we don't accidentally treat checked errors as handled).// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, UncheckedError) {auto DropUnhandledError = []() {Error E = make_error<CustomError>(42);(void)!E;};EXPECT_DEATH(DropUnhandledError(),"Program aborted due to an unhandled Error:")<< "Unhandled Error failure value did not cause abort()";}#endif// Check 'Error::isA<T>' method handling.TEST(Error, IsAHandling) {// Check 'isA' handling.Error E = make_error<CustomError>(1);Error F = make_error<CustomSubError>(1, 2);Error G = Error::success();EXPECT_TRUE(E.isA<CustomError>());EXPECT_FALSE(E.isA<CustomSubError>());EXPECT_TRUE(F.isA<CustomError>());EXPECT_TRUE(F.isA<CustomSubError>());EXPECT_FALSE(G.isA<CustomError>());consumeError(std::move(E));consumeError(std::move(F));consumeError(std::move(G));}// Check that we can handle a custom error.TEST(Error, HandleCustomError) {int CaughtErrorInfo = 0;handleAllErrors(make_error<CustomError>(42), [&](const CustomError &CE) {CaughtErrorInfo = CE.getInfo();});EXPECT_EQ(CaughtErrorInfo, 42) << "Wrong result from CustomError handler";}// Check that handler type deduction also works for handlers// of the following types:// void (const Err&)// Error (const Err&) mutable// void (const Err&) mutable// Error (Err&)// void (Err&)// Error (Err&) mutable// void (Err&) mutable// Error (unique_ptr<Err>)// void (unique_ptr<Err>)// Error (unique_ptr<Err>) mutable// void (unique_ptr<Err>) mutableTEST(Error, HandlerTypeDeduction) {handleAllErrors(make_error<CustomError>(42), [](const CustomError &CE) {});handleAllErrors(make_error<CustomError>(42),[](const CustomError &CE) mutable -> Error { return Error::success(); });handleAllErrors(make_error<CustomError>(42),[](const CustomError &CE) mutable {});handleAllErrors(make_error<CustomError>(42),[](CustomError &CE) -> Error { return Error::success(); });handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) {});handleAllErrors(make_error<CustomError>(42),[](CustomError &CE) mutable -> Error { return Error::success(); });handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) mutable {});handleAllErrors(make_error<CustomError>(42),[](std::unique_ptr<CustomError> CE) -> Error { return Error::success(); });handleAllErrors(make_error<CustomError>(42),[](std::unique_ptr<CustomError> CE) {});handleAllErrors(make_error<CustomError>(42),[](std::unique_ptr<CustomError> CE) mutable -> Error { return Error::success(); });handleAllErrors(make_error<CustomError>(42),[](std::unique_ptr<CustomError> CE) mutable {});// Check that named handlers of type 'Error (const Err&)' work.handleAllErrors(make_error<CustomError>(42), handleCustomError);// Check that named handlers of type 'void (const Err&)' work.handleAllErrors(make_error<CustomError>(42), handleCustomErrorVoid);// Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.handleAllErrors(make_error<CustomError>(42), handleCustomErrorUP);// Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.handleAllErrors(make_error<CustomError>(42), handleCustomErrorUPVoid);}// Test that we can handle errors with custom base classes.TEST(Error, HandleCustomErrorWithCustomBaseClass) {int CaughtErrorInfo = 0;int CaughtErrorExtraInfo = 0;handleAllErrors(make_error<CustomSubError>(42, 7),[&](const CustomSubError &SE) {CaughtErrorInfo = SE.getInfo();CaughtErrorExtraInfo = SE.getExtraInfo();});EXPECT_EQ(CaughtErrorInfo, 42) << "Wrong result from CustomSubError handler";EXPECT_EQ(CaughtErrorExtraInfo, 7)<< "Wrong result from CustomSubError handler";}// Check that we trigger only the first handler that applies.TEST(Error, FirstHandlerOnly) {int DummyInfo = 0;int CaughtErrorInfo = 0;int CaughtErrorExtraInfo = 0;handleAllErrors(make_error<CustomSubError>(42, 7),[&](const CustomSubError &SE) {CaughtErrorInfo = SE.getInfo();CaughtErrorExtraInfo = SE.getExtraInfo();},[&](const CustomError &CE) { DummyInfo = CE.getInfo(); });EXPECT_EQ(CaughtErrorInfo, 42) << "Activated the wrong Error handler(s)";EXPECT_EQ(CaughtErrorExtraInfo, 7) << "Activated the wrong Error handler(s)";EXPECT_EQ(DummyInfo, 0) << "Activated the wrong Error handler(s)";}// Check that general handlers shadow specific ones.TEST(Error, HandlerShadowing) {int CaughtErrorInfo = 0;int DummyInfo = 0;int DummyExtraInfo = 0;handleAllErrors(make_error<CustomSubError>(42, 7),[&](const CustomError &CE) { CaughtErrorInfo = CE.getInfo(); },[&](const CustomSubError &SE) {DummyInfo = SE.getInfo();DummyExtraInfo = SE.getExtraInfo();});EXPECT_EQ(CaughtErrorInfo, 42)<< "General Error handler did not shadow specific handler";EXPECT_EQ(DummyInfo, 0)<< "General Error handler did not shadow specific handler";EXPECT_EQ(DummyExtraInfo, 0)<< "General Error handler did not shadow specific handler";}// Test joinErrors.TEST(Error, CheckJoinErrors) {int CustomErrorInfo1 = 0;int CustomErrorInfo2 = 0;int CustomErrorExtraInfo = 0;Error E =joinErrors(make_error<CustomError>(7), make_error<CustomSubError>(42, 7));handleAllErrors(std::move(E),[&](const CustomSubError &SE) {CustomErrorInfo2 = SE.getInfo();CustomErrorExtraInfo = SE.getExtraInfo();},[&](const CustomError &CE) {// Assert that the CustomError instance above is handled// before the// CustomSubError - joinErrors should preserve error// ordering.EXPECT_EQ(CustomErrorInfo2, 0)<< "CustomErrorInfo2 should be 0 here. ""joinErrors failed to preserve ordering.\n";CustomErrorInfo1 = CE.getInfo();});EXPECT_EQ(CustomErrorInfo1, 7) << "Failed handling compound Error.";EXPECT_EQ(CustomErrorInfo2, 42) << "Failed handling compound Error.";EXPECT_EQ(CustomErrorExtraInfo, 7) << "Failed handling compound Error.";// Test appending a single item to a list.{int Sum = 0;handleAllErrors(joinErrors(joinErrors(make_error<CustomError>(7),make_error<CustomError>(7)),make_error<CustomError>(7)),[&](const CustomError &CE) {Sum += CE.getInfo();});EXPECT_EQ(Sum, 21) << "Failed to correctly append error to error list.";}// Test prepending a single item to a list.{int Sum = 0;handleAllErrors(joinErrors(make_error<CustomError>(7),joinErrors(make_error<CustomError>(7),make_error<CustomError>(7))),[&](const CustomError &CE) {Sum += CE.getInfo();});EXPECT_EQ(Sum, 21) << "Failed to correctly prepend error to error list.";}// Test concatenating two error lists.{int Sum = 0;handleAllErrors(joinErrors(joinErrors(make_error<CustomError>(7),make_error<CustomError>(7)),joinErrors(make_error<CustomError>(7),make_error<CustomError>(7))),[&](const CustomError &CE) {Sum += CE.getInfo();});EXPECT_EQ(Sum, 28) << "Failed to correctly concatenate error lists.";}}// Test that we can consume success values.TEST(Error, ConsumeSuccess) {Error E = Error::success();consumeError(std::move(E));}TEST(Error, ConsumeError) {Error E = make_error<CustomError>(7);consumeError(std::move(E));}// Test that handleAllUnhandledErrors crashes if an error is not caught.// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, FailureToHandle) {auto FailToHandle = []() {handleAllErrors(make_error<CustomError>(7), [&](const CustomSubError &SE) {errs() << "This should never be called";exit(1);});};EXPECT_DEATH(FailToHandle(),"Failure value returned from cantFail wrapped call\n""CustomError \\{7\\}")<< "Unhandled Error in handleAllErrors call did not cause an ""abort()";}#endif// Test that handleAllUnhandledErrors crashes if an error is returned from a// handler.// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, FailureFromHandler) {auto ReturnErrorFromHandler = []() {handleAllErrors(make_error<CustomError>(7),[&](std::unique_ptr<CustomSubError> SE) {return Error(std::move(SE));});};EXPECT_DEATH(ReturnErrorFromHandler(),"Failure value returned from cantFail wrapped call\n""CustomError \\{7\\}")<< " Error returned from handler in handleAllErrors call did not ""cause abort()";}#endif// Test that we can return values from handleErrors.TEST(Error, CatchErrorFromHandler) {int ErrorInfo = 0;Error E = handleErrors(make_error<CustomError>(7),[&](std::unique_ptr<CustomError> CE) { return Error(std::move(CE)); });handleAllErrors(std::move(E),[&](const CustomError &CE) { ErrorInfo = CE.getInfo(); });EXPECT_EQ(ErrorInfo, 7)<< "Failed to handle Error returned from handleErrors.";}TEST(Error, StringError) {std::string Msg;raw_string_ostream S(Msg);logAllUnhandledErrors(make_error<StringError>("foo" + Twine(42), inconvertibleErrorCode()), S);EXPECT_EQ(S.str(), "foo42\n") << "Unexpected StringError log result";auto EC =errorToErrorCode(make_error<StringError>("", errc::invalid_argument));EXPECT_EQ(EC, errc::invalid_argument)<< "Failed to convert StringError to error_code.";}TEST(Error, createStringError) {static const char *Bar = "bar";static const std::error_code EC = errc::invalid_argument;std::string Msg;raw_string_ostream S(Msg);logAllUnhandledErrors(createStringError(EC, "foo%s%d0x%" PRIx8, Bar, 1, 0xff),S);EXPECT_EQ(S.str(), "foobar10xff\n")<< "Unexpected createStringError() log result";S.flush();Msg.clear();logAllUnhandledErrors(createStringError(EC, Bar), S);EXPECT_EQ(S.str(), "bar\n")<< "Unexpected createStringError() (overloaded) log result";S.flush();Msg.clear();auto Res = errorToErrorCode(createStringError(EC, "foo%s", Bar));EXPECT_EQ(Res, EC)<< "Failed to convert createStringError() result to error_code.";}// Test that the ExitOnError utility works as expected.TEST(ErrorDeathTest, ExitOnError) {ExitOnError ExitOnErr;ExitOnErr.setBanner("Error in tool:");ExitOnErr.setExitCodeMapper([](const Error &E) {if (E.isA<CustomSubError>())return 2;return 1;});// Make sure we don't bail on success.ExitOnErr(Error::success());EXPECT_EQ(ExitOnErr(Expected<int>(7)), 7)<< "exitOnError returned an invalid value for Expected";int A = 7;int &B = ExitOnErr(Expected<int&>(A));EXPECT_EQ(&A, &B) << "ExitOnError failed to propagate reference";// Exit tests.EXPECT_EXIT(ExitOnErr(make_error<CustomError>(7)),::testing::ExitedWithCode(1), "Error in tool:")<< "exitOnError returned an unexpected error result";EXPECT_EXIT(ExitOnErr(Expected<int>(make_error<CustomSubError>(0, 0))),::testing::ExitedWithCode(2), "Error in tool:")<< "exitOnError returned an unexpected error result";}// Test that the ExitOnError utility works as expected.TEST(Error, CantFailSuccess) {cantFail(Error::success());int X = cantFail(Expected<int>(42));EXPECT_EQ(X, 42) << "Expected value modified by cantFail";int Dummy = 42;int &Y = cantFail(Expected<int&>(Dummy));EXPECT_EQ(&Dummy, &Y) << "Reference mangled by cantFail";}// Test that cantFail results in a crash if you pass it a failure value.#if LLVM_ENABLE_ABI_BREAKING_CHECKS && !defined(NDEBUG)TEST(Error, CantFailDeath) {EXPECT_DEATH(cantFail(make_error<StringError>("Original error message",inconvertibleErrorCode()),"Cantfail call failed"),"Cantfail call failed\n""Original error message")<< "cantFail(Error) did not cause an abort for failure value";EXPECT_DEATH({auto IEC = inconvertibleErrorCode();int X = cantFail(Expected<int>(make_error<StringError>("foo", IEC)));(void)X;},"Failure value returned from cantFail wrapped call")<< "cantFail(Expected<int>) did not cause an abort for failure value";}#endif// Test Checked Expected<T> in success mode.TEST(Error, CheckedExpectedInSuccessMode) {Expected<int> A = 7;EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'";// Access is safe in second test, since we checked the error in the first.EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value";}// Test Expected with reference type.TEST(Error, ExpectedWithReferenceType) {int A = 7;Expected<int&> B = A;// 'Check' B.(void)!!B;int &C = *B;EXPECT_EQ(&A, &C) << "Expected failed to propagate reference";}// Test Unchecked Expected<T> in success mode.// We expect this to blow up the same way Error would.// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, UncheckedExpectedInSuccessModeDestruction) {EXPECT_DEATH({ Expected<int> A = 7; },"Expected<T> must be checked before access or destruction.")<< "Unchecked Expected<T> success value did not cause an abort().";}#endif// Test Unchecked Expected<T> in success mode.// We expect this to blow up the same way Error would.// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, UncheckedExpectedInSuccessModeAccess) {EXPECT_DEATH({const Expected<int> A = 7;*A;},"Expected<T> must be checked before access or destruction.")<< "Unchecked Expected<T> success value did not cause an abort().";}#endif// Test Unchecked Expected<T> in success mode.// We expect this to blow up the same way Error would.// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, UncheckedExpectedInSuccessModeAssignment) {EXPECT_DEATH({Expected<int> A = 7;A = 7;},"Expected<T> must be checked before access or destruction.")<< "Unchecked Expected<T> success value did not cause an abort().";}#endif// Test Expected<T> in failure mode.TEST(Error, ExpectedInFailureMode) {Expected<int> A = make_error<CustomError>(42);EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'";Error E = A.takeError();EXPECT_TRUE(E.isA<CustomError>()) << "Incorrect Expected error value";consumeError(std::move(E));}// Check that an Expected instance with an error value doesn't allow access to// operator*.// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, AccessExpectedInFailureMode) {Expected<int> A = make_error<CustomError>(42);EXPECT_DEATH(*A, "Expected<T> must be checked before access or destruction.")<< "Incorrect Expected error value";consumeError(A.takeError());}#endif// Check that an Expected instance with an error triggers an abort if// unhandled.// Test runs in debug mode only.#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(Error, UnhandledExpectedInFailureMode) {EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); },"Expected<T> must be checked before access or destruction.")<< "Unchecked Expected<T> failure value did not cause an abort()";}#endif// Test covariance of Expected.TEST(Error, ExpectedCovariance) {class B {};class D : public B {};Expected<B *> A1(Expected<D *>(nullptr));// Check A1 by converting to bool before assigning to it.(void)!!A1;A1 = Expected<D *>(nullptr);// Check A1 again before destruction.(void)!!A1;Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr));// Check A2 by converting to bool before assigning to it.(void)!!A2;A2 = Expected<std::unique_ptr<D>>(nullptr);// Check A2 again before destruction.(void)!!A2;}// Test that handleExpected just returns success values.TEST(Error, HandleExpectedSuccess) {auto ValOrErr =handleExpected(Expected<int>(42),[]() { return Expected<int>(43); });EXPECT_TRUE(!!ValOrErr)<< "handleExpected should have returned a success value here";EXPECT_EQ(*ValOrErr, 42)<< "handleExpected should have returned the original success value here";}enum FooStrategy { Aggressive, Conservative };static Expected<int> foo(FooStrategy S) {if (S == Aggressive)return make_error<CustomError>(7);return 42;}// Test that handleExpected invokes the error path if errors are not handled.TEST(Error, HandleExpectedUnhandledError) {// foo(Aggressive) should return a CustomError which should pass through as// there is no handler for CustomError.auto ValOrErr =handleExpected(foo(Aggressive),[]() { return foo(Conservative); });EXPECT_FALSE(!!ValOrErr)<< "handleExpected should have returned an error here";auto Err = ValOrErr.takeError();EXPECT_TRUE(Err.isA<CustomError>())<< "handleExpected should have returned the CustomError generated by ""foo(Aggressive) here";consumeError(std::move(Err));}// Test that handleExpected invokes the fallback path if errors are handled.TEST(Error, HandleExpectedHandledError) {// foo(Aggressive) should return a CustomError which should handle triggering// the fallback path.auto ValOrErr =handleExpected(foo(Aggressive),[]() { return foo(Conservative); },[](const CustomError&) { /* do nothing */ });EXPECT_TRUE(!!ValOrErr)<< "handleExpected should have returned a success value here";EXPECT_EQ(*ValOrErr, 42)<< "handleExpected returned the wrong success value";}TEST(Error, ErrorCodeConversions) {// Round-trip a success value to check that it converts correctly.EXPECT_EQ(errorToErrorCode(errorCodeToError(std::error_code())),std::error_code())<< "std::error_code() should round-trip via Error conversions";// Round-trip an error value to check that it converts correctly.EXPECT_EQ(errorToErrorCode(errorCodeToError(errc::invalid_argument)),errc::invalid_argument)<< "std::error_code error value should round-trip via Error ""conversions";// Round-trip a success value through ErrorOr/Expected to check that it// converts correctly.{auto Orig = ErrorOr<int>(42);auto RoundTripped =expectedToErrorOr(errorOrToExpected(ErrorOr<int>(42)));EXPECT_EQ(*Orig, *RoundTripped)<< "ErrorOr<T> success value should round-trip via Expected<T> ""conversions.";}// Round-trip a failure value through ErrorOr/Expected to check that it// converts correctly.{auto Orig = ErrorOr<int>(errc::invalid_argument);auto RoundTripped =expectedToErrorOr(errorOrToExpected(ErrorOr<int>(errc::invalid_argument)));EXPECT_EQ(Orig.getError(), RoundTripped.getError())<< "ErrorOr<T> failure value should round-trip via Expected<T> ""conversions.";}}// Test that error messages work.TEST(Error, ErrorMessage) {EXPECT_EQ(toString(Error::success()), "");Error E1 = make_error<CustomError>(0);EXPECT_EQ(toString(std::move(E1)), "CustomError {0}");Error E2 = make_error<CustomError>(0);handleAllErrors(std::move(E2), [](const CustomError &CE) {EXPECT_EQ(CE.message(), "CustomError {0}");});Error E3 = joinErrors(make_error<CustomError>(0), make_error<CustomError>(1));EXPECT_EQ(toString(std::move(E3)), "CustomError {0}\n""CustomError {1}");}TEST(Error, Stream) {{Error OK = Error::success();std::string Buf;llvm::raw_string_ostream S(Buf);S << OK;EXPECT_EQ("success", S.str());consumeError(std::move(OK));}{Error E1 = make_error<CustomError>(0);std::string Buf;llvm::raw_string_ostream S(Buf);S << E1;EXPECT_EQ("CustomError {0}", S.str());consumeError(std::move(E1));}}TEST(Error, SucceededMatcher) {EXPECT_THAT_ERROR(Error::success(), Succeeded());EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(make_error<CustomError>(0), Succeeded()),"Expected: succeeded\n Actual: failed (CustomError {0})");EXPECT_THAT_EXPECTED(Expected<int>(0), Succeeded());EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),Succeeded()),"Expected: succeeded\n Actual: failed (CustomError {0})");int a = 1;EXPECT_THAT_EXPECTED(Expected<int &>(a), Succeeded());}TEST(Error, FailedMatcher) {EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed());EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(Error::success(), Failed()),"Expected: failed\n Actual: succeeded");EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed<CustomError>());EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(Error::success(), Failed<CustomError>()),"Expected: failed with Error of given type\n Actual: succeeded");EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed<CustomSubError>()),"Error was not of given type");EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(joinErrors(make_error<CustomError>(0), make_error<CustomError>(1)),Failed<CustomError>()),"multiple errors");EXPECT_THAT_ERROR(make_error<CustomError>(0),Failed<CustomError>(testing::Property(&CustomError::getInfo, 0)));EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(make_error<CustomError>(0),Failed<CustomError>(testing::Property(&CustomError::getInfo, 1))),"Expected: failed with Error of given type and the error is an object ""whose given property is equal to 1\n"" Actual: failed (CustomError {0})");EXPECT_THAT_ERROR(make_error<CustomError>(0), Failed<ErrorInfoBase>());EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)), Failed());EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(0), Failed()),"Expected: failed\n Actual: succeeded with value 0");EXPECT_THAT_EXPECTED(Expected<int &>(make_error<CustomError>(0)), Failed());}TEST(Error, HasValueMatcher) {EXPECT_THAT_EXPECTED(Expected<int>(0), HasValue(0));EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),HasValue(0)),"Expected: succeeded with value (is equal to 0)\n"" Actual: failed (CustomError {0})");EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(1), HasValue(0)),"Expected: succeeded with value (is equal to 0)\n"" Actual: succeeded with value 1, (isn't equal to 0)");int a = 1;EXPECT_THAT_EXPECTED(Expected<int &>(a), HasValue(testing::Eq(1)));EXPECT_THAT_EXPECTED(Expected<int>(1), HasValue(testing::Gt(0)));EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(0), HasValue(testing::Gt(1))),"Expected: succeeded with value (is > 1)\n"" Actual: succeeded with value 0, (isn't > 1)");EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),HasValue(testing::Gt(1))),"Expected: succeeded with value (is > 1)\n"" Actual: failed (CustomError {0})");}TEST(Error, FailedWithMessageMatcher) {EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),FailedWithMessage("CustomError {0}"));EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(1)),FailedWithMessage("CustomError {0}")),"Expected: failed with Error whose message has 1 element that is equal ""to \"CustomError {0}\"\n"" Actual: failed (CustomError {1})");EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(0),FailedWithMessage("CustomError {0}")),"Expected: failed with Error whose message has 1 element that is equal ""to \"CustomError {0}\"\n"" Actual: succeeded with value 0");EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(make_error<CustomError>(0)),FailedWithMessage("CustomError {0}", "CustomError {0}")),"Expected: failed with Error whose message has 2 elements where\n""element #0 is equal to \"CustomError {0}\",\n""element #1 is equal to \"CustomError {0}\"\n"" Actual: failed (CustomError {0}), which has 1 element");EXPECT_NONFATAL_FAILURE(EXPECT_THAT_EXPECTED(Expected<int>(joinErrors(make_error<CustomError>(0),make_error<CustomError>(0))),FailedWithMessage("CustomError {0}")),"Expected: failed with Error whose message has 1 element that is equal ""to \"CustomError {0}\"\n"" Actual: failed (CustomError {0}; CustomError {0}), which has 2 elements");EXPECT_THAT_ERROR(joinErrors(make_error<CustomError>(0), make_error<CustomError>(0)),FailedWithMessageArray(testing::SizeIs(2)));}TEST(Error, C_API) {EXPECT_THAT_ERROR(unwrap(wrap(Error::success())), Succeeded())<< "Failed to round-trip Error success value via C API";EXPECT_THAT_ERROR(unwrap(wrap(make_error<CustomError>(0))),Failed<CustomError>())<< "Failed to round-trip Error failure value via C API";auto Err =wrap(make_error<StringError>("test message", inconvertibleErrorCode()));EXPECT_EQ(LLVMGetErrorTypeId(Err), LLVMGetStringErrorTypeId())<< "Failed to match error type ids via C API";char *ErrMsg = LLVMGetErrorMessage(Err);EXPECT_STREQ(ErrMsg, "test message")<< "Failed to roundtrip StringError error message via C API";LLVMDisposeErrorMessage(ErrMsg);bool GotCSE = false;bool GotCE = false;handleAllErrors(unwrap(wrap(joinErrors(make_error<CustomSubError>(42, 7),make_error<CustomError>(42)))),[&](CustomSubError &CSE) {GotCSE = true;},[&](CustomError &CE) {GotCE = true;});EXPECT_TRUE(GotCSE) << "Failed to round-trip ErrorList via C API";EXPECT_TRUE(GotCE) << "Failed to round-trip ErrorList via C API";}TEST(Error, FileErrorTest) {#if !defined(NDEBUG) && GTEST_HAS_DEATH_TESTEXPECT_DEATH({Error S = Error::success();consumeError(createFileError("file.bin", std::move(S)));},"");#endif// Not allowed, would fail at compile-time//consumeError(createFileError("file.bin", ErrorSuccess()));Error E1 = make_error<CustomError>(1);Error FE1 = createFileError("file.bin", std::move(E1));EXPECT_EQ(toString(std::move(FE1)), "'file.bin': CustomError {1}");Error E2 = make_error<CustomError>(2);Error FE2 = createFileError("file.bin", std::move(E2));handleAllErrors(std::move(FE2), [](const FileError &F) {EXPECT_EQ(F.message(), "'file.bin': CustomError {2}");});Error E3 = make_error<CustomError>(3);Error FE3 = createFileError("file.bin", std::move(E3));auto E31 = handleErrors(std::move(FE3), [](std::unique_ptr<FileError> F) {return F->takeError();});handleAllErrors(std::move(E31), [](const CustomError &C) {EXPECT_EQ(C.message(), "CustomError {3}");});Error FE4 =joinErrors(createFileError("file.bin", make_error<CustomError>(41)),createFileError("file2.bin", make_error<CustomError>(42)));EXPECT_EQ(toString(std::move(FE4)), "'file.bin': CustomError {41}\n""'file2.bin': CustomError {42}");Error FE5 = createFileError("", make_error<CustomError>(5));EXPECT_EQ(toString(std::move(FE5)), "'': CustomError {5}");Error FE6 = createFileError("unused", make_error<CustomError>(6));handleAllErrors(std::move(FE6), [](std::unique_ptr<FileError> F) {EXPECT_EQ(F->messageWithoutFileInfo(), "CustomError {6}");});}TEST(Error, FileErrorErrorCode) {for (std::error_code EC : {make_error_code(std::errc::not_supported),make_error_code(std::errc::invalid_argument),make_error_code(std::errc::no_such_file_or_directory),}) {EXPECT_EQ(EC, errorToErrorCode(createFileError("file.bin", EC)));EXPECT_EQ(EC, errorToErrorCode(createFileError("file.bin", /*Line=*/5, EC)));EXPECT_EQ(EC, errorToErrorCode(createFileError("file.bin", errorCodeToError(EC))));EXPECT_EQ(EC, errorToErrorCode(createFileError("file.bin", /*Line=*/5, errorCodeToError(EC))));}// inconvertibleErrorCode() should be wrapped to avoid a fatal error.EXPECT_EQ("A file error occurred.",errorToErrorCode(createFileError("file.bin", inconvertibleErrorCode())).message());EXPECT_EQ("A file error occurred.",errorToErrorCode(createFileError("file.bin", /*Line=*/5, inconvertibleErrorCode())).message());}enum class test_error_code {unspecified = 1,error_1,error_2,};} // end anon namespacenamespace std {template <>struct is_error_code_enum<test_error_code> : std::true_type {};} // namespace stdnamespace {const std::error_category &TErrorCategory();inline std::error_code make_error_code(test_error_code E) {return std::error_code(static_cast<int>(E), TErrorCategory());}class TestDebugError : public ErrorInfo<TestDebugError, StringError> {public:using ErrorInfo<TestDebugError, StringError >::ErrorInfo; // inherit constructorsTestDebugError(const Twine &S) : ErrorInfo(S, test_error_code::unspecified) {}static char ID;};class TestErrorCategory : public std::error_category {public:const char *name() const noexcept override { return "error"; }std::string message(int Condition) const override {switch (static_cast<test_error_code>(Condition)) {case test_error_code::unspecified:return "An unknown error has occurred.";case test_error_code::error_1:return "Error 1.";case test_error_code::error_2:return "Error 2.";}llvm_unreachable("Unrecognized test_error_code");}};const std::error_category &TErrorCategory() {static TestErrorCategory TestErrCategory;return TestErrCategory;}char TestDebugError::ID;TEST(Error, SubtypeStringErrorTest) {auto E1 = make_error<TestDebugError>(test_error_code::error_1);EXPECT_EQ(toString(std::move(E1)), "Error 1.");auto E2 = make_error<TestDebugError>(test_error_code::error_1,"Detailed information");EXPECT_EQ(toString(std::move(E2)), "Error 1. Detailed information");auto E3 = make_error<TestDebugError>(test_error_code::error_2);handleAllErrors(std::move(E3), [](const TestDebugError &F) {EXPECT_EQ(F.message(), "Error 2.");});auto E4 = joinErrors(make_error<TestDebugError>(test_error_code::error_1,"Detailed information"),make_error<TestDebugError>(test_error_code::error_2));EXPECT_EQ(toString(std::move(E4)), "Error 1. Detailed information\n""Error 2.");}static Error createAnyError() {return errorCodeToError(test_error_code::unspecified);}struct MoveOnlyBox {Optional<int> Box;explicit MoveOnlyBox(int I) : Box(I) {}MoveOnlyBox() = default;MoveOnlyBox(MoveOnlyBox &&) = default;MoveOnlyBox &operator=(MoveOnlyBox &&) = default;MoveOnlyBox(const MoveOnlyBox &) = delete;MoveOnlyBox &operator=(const MoveOnlyBox &) = delete;bool operator==(const MoveOnlyBox &RHS) const {if (bool(Box) != bool(RHS.Box))return false;return Box ? *Box == *RHS.Box : false;}};TEST(Error, moveInto) {// Use MoveOnlyBox as the T in Expected<T>.auto make = [](int I) -> Expected<MoveOnlyBox> { return MoveOnlyBox(I); };auto makeFailure = []() -> Expected<MoveOnlyBox> { return createAnyError(); };{MoveOnlyBox V;// Failure with no prior value.EXPECT_THAT_ERROR(makeFailure().moveInto(V), Failed());EXPECT_EQ(None, V.Box);// Success with no prior value.EXPECT_THAT_ERROR(make(5).moveInto(V), Succeeded());EXPECT_EQ(5, V.Box);// Success with an existing value.EXPECT_THAT_ERROR(make(7).moveInto(V), Succeeded());EXPECT_EQ(7, V.Box);// Failure with an existing value. Might be nice to assign a// default-constructed value in this case, but for now it's being left// alone.EXPECT_THAT_ERROR(makeFailure().moveInto(V), Failed());EXPECT_EQ(7, V.Box);}// Check that this works with optionals too.{// Same cases as above.Optional<MoveOnlyBox> MaybeV;EXPECT_THAT_ERROR(makeFailure().moveInto(MaybeV), Failed());EXPECT_EQ(None, MaybeV);EXPECT_THAT_ERROR(make(5).moveInto(MaybeV), Succeeded());EXPECT_EQ(MoveOnlyBox(5), MaybeV);EXPECT_THAT_ERROR(make(7).moveInto(MaybeV), Succeeded());EXPECT_EQ(MoveOnlyBox(7), MaybeV);EXPECT_THAT_ERROR(makeFailure().moveInto(MaybeV), Failed());EXPECT_EQ(MoveOnlyBox(7), MaybeV);}}} // namespace
//===- unittests/ErrorOrTest.cpp - ErrorOr.h tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ErrorOr.h"#include "llvm/Support/Errc.h"#include "gtest/gtest.h"#include <memory>using namespace llvm;namespace {ErrorOr<int> t1() { return 1; }ErrorOr<int> t2() { return errc::invalid_argument; }TEST(ErrorOr, SimpleValue) {ErrorOr<int> a = t1();// FIXME: This is probably a bug in gtest. EXPECT_TRUE should expand to// include the !! to make it friendly to explicit bool operators.EXPECT_TRUE(!!a);EXPECT_EQ(1, *a);ErrorOr<int> b = a;EXPECT_EQ(1, *b);a = t2();EXPECT_FALSE(a);EXPECT_EQ(a.getError(), errc::invalid_argument);#ifdef EXPECT_DEBUG_DEATHEXPECT_DEBUG_DEATH(*a, "Cannot get value when an error exists");#endif}ErrorOr<std::unique_ptr<int> > t3() {return std::unique_ptr<int>(new int(3));}TEST(ErrorOr, Types) {int x;ErrorOr<int&> a(x);*a = 42;EXPECT_EQ(42, x);// Move only types.EXPECT_EQ(3, **t3());}struct B {};struct D : B {};TEST(ErrorOr, Covariant) {ErrorOr<B*> b(ErrorOr<D*>(nullptr));b = ErrorOr<D*>(nullptr);ErrorOr<std::unique_ptr<B> > b1(ErrorOr<std::unique_ptr<D> >(nullptr));b1 = ErrorOr<std::unique_ptr<D> >(nullptr);ErrorOr<std::unique_ptr<int>> b2(ErrorOr<int *>(nullptr));ErrorOr<int *> b3(nullptr);ErrorOr<std::unique_ptr<int>> b4(b3);}TEST(ErrorOr, Comparison) {ErrorOr<int> x(errc::no_such_file_or_directory);EXPECT_EQ(x, errc::no_such_file_or_directory);}TEST(ErrorOr, ImplicitConversion) {ErrorOr<std::string> x("string literal");EXPECT_TRUE(!!x);}TEST(ErrorOr, ImplicitConversionCausesMove) {struct Source {};struct Destination {Destination(const Source&) {}Destination(Source&&) = delete;};Source s;ErrorOr<Destination> x = s;EXPECT_TRUE(!!x);}TEST(ErrorOr, ImplicitConversionNoAmbiguity) {struct CastsToErrorCode {CastsToErrorCode() = default;CastsToErrorCode(std::error_code) {}operator std::error_code() { return errc::invalid_argument; }} casts_to_error_code;ErrorOr<CastsToErrorCode> x1(casts_to_error_code);ErrorOr<CastsToErrorCode> x2 = casts_to_error_code;ErrorOr<CastsToErrorCode> x3 = {casts_to_error_code};ErrorOr<CastsToErrorCode> x4{casts_to_error_code};ErrorOr<CastsToErrorCode> x5(errc::no_such_file_or_directory);ErrorOr<CastsToErrorCode> x6 = errc::no_such_file_or_directory;ErrorOr<CastsToErrorCode> x7 = {errc::no_such_file_or_directory};ErrorOr<CastsToErrorCode> x8{errc::no_such_file_or_directory};EXPECT_TRUE(!!x1);EXPECT_TRUE(!!x2);EXPECT_TRUE(!!x3);EXPECT_TRUE(!!x4);EXPECT_FALSE(x5);EXPECT_FALSE(x6);EXPECT_FALSE(x7);EXPECT_FALSE(x8);}// ErrorOr<int*> x(nullptr);// ErrorOr<std::unique_ptr<int>> y = x; // invalid conversionstatic_assert(!std::is_convertible<const ErrorOr<int *> &,ErrorOr<std::unique_ptr<int>>>::value,"do not invoke explicit ctors in implicit conversion from lvalue");// ErrorOr<std::unique_ptr<int>> y = ErrorOr<int*>(nullptr); // invalid// // conversionstatic_assert(!std::is_convertible<ErrorOr<int *> &&,ErrorOr<std::unique_ptr<int>>>::value,"do not invoke explicit ctors in implicit conversion from rvalue");// ErrorOr<int*> x(nullptr);// ErrorOr<std::unique_ptr<int>> y;// y = x; // invalid conversionstatic_assert(!std::is_assignable<ErrorOr<std::unique_ptr<int>>&,const ErrorOr<int *> &>::value,"do not invoke explicit ctors in assignment");// ErrorOr<std::unique_ptr<int>> x;// x = ErrorOr<int*>(nullptr); // invalid conversionstatic_assert(!std::is_assignable<ErrorOr<std::unique_ptr<int>>&,ErrorOr<int *> &&>::value,"do not invoke explicit ctors in assignment");} // end anon namespace
//===- ErrnoTest.cpp - Error handling unit tests --------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Errno.h"#include "gtest/gtest.h"using namespace llvm::sys;TEST(ErrnoTest, RetryAfterSignal) {EXPECT_EQ(1, RetryAfterSignal(-1, [] { return 1; }));EXPECT_EQ(-1, RetryAfterSignal(-1, [] {errno = EAGAIN;return -1;}));EXPECT_EQ(EAGAIN, errno);unsigned calls = 0;EXPECT_EQ(1, RetryAfterSignal(-1, [&calls] {errno = EINTR;++calls;return calls == 1 ? -1 : 1;}));EXPECT_EQ(2u, calls);EXPECT_EQ(1, RetryAfterSignal(-1, [](int x) { return x; }, 1));std::unique_ptr<int> P(RetryAfterSignal(nullptr, [] { return new int(47); }));EXPECT_EQ(47, *P);errno = EINTR;EXPECT_EQ(-1, RetryAfterSignal(-1, [] { return -1; }));}
//===- unittests/Support/EndianTest.cpp - Endian.h tests ------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Endian.h"#include "llvm/Support/DataTypes.h"#include "gtest/gtest.h"#include <cstdlib>#include <ctime>using namespace llvm;using namespace support;#undef maxnamespace {TEST(Endian, Read) {// These are 5 bytes so we can be sure at least one of the reads is unaligned.unsigned char bigval[] = {0x00, 0x01, 0x02, 0x03, 0x04};unsigned char littleval[] = {0x00, 0x04, 0x03, 0x02, 0x01};int32_t BigAsHost = 0x00010203;EXPECT_EQ(BigAsHost, (endian::read<int32_t, big, unaligned>(bigval)));int32_t LittleAsHost = 0x02030400;EXPECT_EQ(LittleAsHost,(endian::read<int32_t, little, unaligned>(littleval)));EXPECT_EQ((endian::read<int32_t, big, unaligned>(bigval + 1)),(endian::read<int32_t, little, unaligned>(littleval + 1)));}TEST(Endian, ReadBitAligned) {// Simple test to make sure we properly pull out the 0x0 word.unsigned char littleval[] = {0x3f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff};unsigned char bigval[] = {0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0};EXPECT_EQ((endian::readAtBitAlignment<int, little, unaligned>(&littleval[0], 6)),0x0);EXPECT_EQ((endian::readAtBitAlignment<int, big, unaligned>(&bigval[0], 6)),0x0);// Test to make sure that signed right shift of 0xf0000000 is masked// properly.unsigned char littleval2[] = {0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00};unsigned char bigval2[] = {0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};EXPECT_EQ((endian::readAtBitAlignment<int, little, unaligned>(&littleval2[0], 4)),0x0f000000);EXPECT_EQ((endian::readAtBitAlignment<int, big, unaligned>(&bigval2[0], 4)),0x0f000000);// Test to make sure left shift of start bit doesn't overflow.EXPECT_EQ((endian::readAtBitAlignment<int, little, unaligned>(&littleval2[0], 1)),0x78000000);EXPECT_EQ((endian::readAtBitAlignment<int, big, unaligned>(&bigval2[0], 1)),0x78000000);// Test to make sure 64-bit int doesn't overflow.unsigned char littleval3[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};unsigned char bigval3[] = {0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};EXPECT_EQ((endian::readAtBitAlignment<int64_t, little, unaligned>(&littleval3[0], 4)),0x0f00000000000000);EXPECT_EQ((endian::readAtBitAlignment<int64_t, big, unaligned>(&bigval3[0], 4)),0x0f00000000000000);}TEST(Endian, WriteBitAligned) {// This test ensures that signed right shift of 0xffffaa is masked// properly.unsigned char bigval[8] = {0x00};endian::writeAtBitAlignment<int32_t, big, unaligned>(bigval, (int)0xffffaaaa,4);EXPECT_EQ(bigval[0], 0xff);EXPECT_EQ(bigval[1], 0xfa);EXPECT_EQ(bigval[2], 0xaa);EXPECT_EQ(bigval[3], 0xa0);EXPECT_EQ(bigval[4], 0x00);EXPECT_EQ(bigval[5], 0x00);EXPECT_EQ(bigval[6], 0x00);EXPECT_EQ(bigval[7], 0x0f);unsigned char littleval[8] = {0x00};endian::writeAtBitAlignment<int32_t, little, unaligned>(littleval,(int)0xffffaaaa, 4);EXPECT_EQ(littleval[0], 0xa0);EXPECT_EQ(littleval[1], 0xaa);EXPECT_EQ(littleval[2], 0xfa);EXPECT_EQ(littleval[3], 0xff);EXPECT_EQ(littleval[4], 0x0f);EXPECT_EQ(littleval[5], 0x00);EXPECT_EQ(littleval[6], 0x00);EXPECT_EQ(littleval[7], 0x00);// This test makes sure 1<<31 doesn't overflow.// Test to make sure left shift of start bit doesn't overflow.unsigned char bigval2[8] = {0x00};endian::writeAtBitAlignment<int32_t, big, unaligned>(bigval2, (int)0xffffffff,1);EXPECT_EQ(bigval2[0], 0xff);EXPECT_EQ(bigval2[1], 0xff);EXPECT_EQ(bigval2[2], 0xff);EXPECT_EQ(bigval2[3], 0xfe);EXPECT_EQ(bigval2[4], 0x00);EXPECT_EQ(bigval2[5], 0x00);EXPECT_EQ(bigval2[6], 0x00);EXPECT_EQ(bigval2[7], 0x01);unsigned char littleval2[8] = {0x00};endian::writeAtBitAlignment<int32_t, little, unaligned>(littleval2,(int)0xffffffff, 1);EXPECT_EQ(littleval2[0], 0xfe);EXPECT_EQ(littleval2[1], 0xff);EXPECT_EQ(littleval2[2], 0xff);EXPECT_EQ(littleval2[3], 0xff);EXPECT_EQ(littleval2[4], 0x01);EXPECT_EQ(littleval2[5], 0x00);EXPECT_EQ(littleval2[6], 0x00);EXPECT_EQ(littleval2[7], 0x00);// Test to make sure 64-bit int doesn't overflow.unsigned char bigval64[16] = {0x00};endian::writeAtBitAlignment<int64_t, big, unaligned>(bigval64, (int64_t)0xffffffffffffffff, 1);EXPECT_EQ(bigval64[0], 0xff);EXPECT_EQ(bigval64[1], 0xff);EXPECT_EQ(bigval64[2], 0xff);EXPECT_EQ(bigval64[3], 0xff);EXPECT_EQ(bigval64[4], 0xff);EXPECT_EQ(bigval64[5], 0xff);EXPECT_EQ(bigval64[6], 0xff);EXPECT_EQ(bigval64[7], 0xfe);EXPECT_EQ(bigval64[8], 0x00);EXPECT_EQ(bigval64[9], 0x00);EXPECT_EQ(bigval64[10], 0x00);EXPECT_EQ(bigval64[11], 0x00);EXPECT_EQ(bigval64[12], 0x00);EXPECT_EQ(bigval64[13], 0x00);EXPECT_EQ(bigval64[14], 0x00);EXPECT_EQ(bigval64[15], 0x01);unsigned char littleval64[16] = {0x00};endian::writeAtBitAlignment<int64_t, little, unaligned>(littleval64, (int64_t)0xffffffffffffffff, 1);EXPECT_EQ(littleval64[0], 0xfe);EXPECT_EQ(littleval64[1], 0xff);EXPECT_EQ(littleval64[2], 0xff);EXPECT_EQ(littleval64[3], 0xff);EXPECT_EQ(littleval64[4], 0xff);EXPECT_EQ(littleval64[5], 0xff);EXPECT_EQ(littleval64[6], 0xff);EXPECT_EQ(littleval64[7], 0xff);EXPECT_EQ(littleval64[8], 0x01);EXPECT_EQ(littleval64[9], 0x00);EXPECT_EQ(littleval64[10], 0x00);EXPECT_EQ(littleval64[11], 0x00);EXPECT_EQ(littleval64[12], 0x00);EXPECT_EQ(littleval64[13], 0x00);EXPECT_EQ(littleval64[14], 0x00);EXPECT_EQ(littleval64[15], 0x00);}TEST(Endian, Write) {unsigned char data[5];endian::write<int32_t, big, unaligned>(data, -1362446643);EXPECT_EQ(data[0], 0xAE);EXPECT_EQ(data[1], 0xCA);EXPECT_EQ(data[2], 0xB6);EXPECT_EQ(data[3], 0xCD);endian::write<int32_t, big, unaligned>(data + 1, -1362446643);EXPECT_EQ(data[1], 0xAE);EXPECT_EQ(data[2], 0xCA);EXPECT_EQ(data[3], 0xB6);EXPECT_EQ(data[4], 0xCD);endian::write<int32_t, little, unaligned>(data, -1362446643);EXPECT_EQ(data[0], 0xCD);EXPECT_EQ(data[1], 0xB6);EXPECT_EQ(data[2], 0xCA);EXPECT_EQ(data[3], 0xAE);endian::write<int32_t, little, unaligned>(data + 1, -1362446643);EXPECT_EQ(data[1], 0xCD);EXPECT_EQ(data[2], 0xB6);EXPECT_EQ(data[3], 0xCA);EXPECT_EQ(data[4], 0xAE);}TEST(Endian, PackedEndianSpecificIntegral) {// These are 5 bytes so we can be sure at least one of the reads is unaligned.unsigned char big[] = {0x00, 0x01, 0x02, 0x03, 0x04};unsigned char little[] = {0x00, 0x04, 0x03, 0x02, 0x01};big32_t *big_val =reinterpret_cast<big32_t *>(big + 1);little32_t *little_val =reinterpret_cast<little32_t *>(little + 1);EXPECT_EQ(*big_val, *little_val);}TEST(Endian, PacketEndianSpecificIntegralAsEnum) {enum class Test : uint16_t { ONETWO = 0x0102, TWOONE = 0x0201 };unsigned char bytes[] = {0x01, 0x02};using LittleTest = little_t<Test>;using BigTest = big_t<Test>;EXPECT_EQ(Test::TWOONE, *reinterpret_cast<LittleTest *>(bytes));EXPECT_EQ(Test::ONETWO, *reinterpret_cast<BigTest *>(bytes));}} // end anon namespace
//===- unittests/Support/EndianStreamTest.cpp - EndianStream.h tests ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/EndianStream.h"#include "llvm/ADT/SmallString.h"#include "llvm/Support/DataTypes.h"#include "gtest/gtest.h"using namespace llvm;using namespace support;namespace {TEST(EndianStream, WriteInt32LE) {SmallString<16> data;{raw_svector_ostream OS(data);endian::Writer LE(OS, little);LE.write(static_cast<int32_t>(-1362446643));}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0xCD);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0xB6);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0xCA);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0xAE);}TEST(EndianStream, WriteInt32BE) {SmallVector<char, 16> data;{raw_svector_ostream OS(data);endian::Writer BE(OS, big);BE.write(static_cast<int32_t>(-1362446643));}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0xAE);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0xCA);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0xB6);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0xCD);}TEST(EndianStream, WriteFloatLE) {SmallString<16> data;{raw_svector_ostream OS(data);endian::Writer LE(OS, little);LE.write(12345.0f);}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0x00);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0xE4);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0x40);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0x46);}TEST(EndianStream, WriteFloatBE) {SmallVector<char, 16> data;{raw_svector_ostream OS(data);endian::Writer BE(OS, big);BE.write(12345.0f);}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0x46);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0x40);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0xE4);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0x00);}TEST(EndianStream, WriteInt64LE) {SmallString<16> data;{raw_svector_ostream OS(data);endian::Writer LE(OS, little);LE.write(static_cast<int64_t>(-136244664332342323));}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0xCD);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0xAB);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0xED);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0x1B);EXPECT_EQ(static_cast<uint8_t>(data[4]), 0x33);EXPECT_EQ(static_cast<uint8_t>(data[5]), 0xF6);EXPECT_EQ(static_cast<uint8_t>(data[6]), 0x1B);EXPECT_EQ(static_cast<uint8_t>(data[7]), 0xFE);}TEST(EndianStream, WriteInt64BE) {SmallVector<char, 16> data;{raw_svector_ostream OS(data);endian::Writer BE(OS, big);BE.write(static_cast<int64_t>(-136244664332342323));}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0xFE);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0x1B);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0xF6);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0x33);EXPECT_EQ(static_cast<uint8_t>(data[4]), 0x1B);EXPECT_EQ(static_cast<uint8_t>(data[5]), 0xED);EXPECT_EQ(static_cast<uint8_t>(data[6]), 0xAB);EXPECT_EQ(static_cast<uint8_t>(data[7]), 0xCD);}TEST(EndianStream, WriteDoubleLE) {SmallString<16> data;{raw_svector_ostream OS(data);endian::Writer LE(OS, little);LE.write(-2349214918.58107);}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0x20);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0x98);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0xD2);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0x98);EXPECT_EQ(static_cast<uint8_t>(data[4]), 0xC5);EXPECT_EQ(static_cast<uint8_t>(data[5]), 0x80);EXPECT_EQ(static_cast<uint8_t>(data[6]), 0xE1);EXPECT_EQ(static_cast<uint8_t>(data[7]), 0xC1);}TEST(EndianStream, WriteDoubleBE) {SmallVector<char, 16> data;{raw_svector_ostream OS(data);endian::Writer BE(OS, big);BE.write(-2349214918.58107);}EXPECT_EQ(static_cast<uint8_t>(data[0]), 0xC1);EXPECT_EQ(static_cast<uint8_t>(data[1]), 0xE1);EXPECT_EQ(static_cast<uint8_t>(data[2]), 0x80);EXPECT_EQ(static_cast<uint8_t>(data[3]), 0xC5);EXPECT_EQ(static_cast<uint8_t>(data[4]), 0x98);EXPECT_EQ(static_cast<uint8_t>(data[5]), 0xD2);EXPECT_EQ(static_cast<uint8_t>(data[6]), 0x98);EXPECT_EQ(static_cast<uint8_t>(data[7]), 0x20);}TEST(EndianStream, WriteArrayLE) {SmallString<16> Data;{raw_svector_ostream OS(Data);endian::Writer LE(OS, little);LE.write<uint16_t>({0x1234, 0x5678});}EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x34);EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0x12);EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x78);EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x56);}TEST(EndianStream, WriteVectorLE) {SmallString<16> Data;{raw_svector_ostream OS(Data);endian::Writer LE(OS, little);std::vector<uint16_t> Vec{0x1234, 0x5678};LE.write<uint16_t>(Vec);}EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x34);EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0x12);EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x78);EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x56);}TEST(EndianStream, WriteFloatArrayLE) {SmallString<16> Data;{raw_svector_ostream OS(Data);endian::Writer LE(OS, little);LE.write<float>({12345.0f, 12346.0f});}EXPECT_EQ(static_cast<uint8_t>(Data[0]), 0x00);EXPECT_EQ(static_cast<uint8_t>(Data[1]), 0xE4);EXPECT_EQ(static_cast<uint8_t>(Data[2]), 0x40);EXPECT_EQ(static_cast<uint8_t>(Data[3]), 0x46);EXPECT_EQ(static_cast<uint8_t>(Data[4]), 0x00);EXPECT_EQ(static_cast<uint8_t>(Data[5]), 0xE8);EXPECT_EQ(static_cast<uint8_t>(Data[6]), 0x40);EXPECT_EQ(static_cast<uint8_t>(Data[7]), 0x46);}} // end anon namespace
//===----- unittests/ELFAttributeParserTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ELFAttributeParser.h"#include "llvm/Support/ELFAttributes.h"#include "gtest/gtest.h"#include <string>using namespace llvm;static const TagNameMap emptyTagNameMap;// This class is used to test the common part of the ELF attribute section.class AttributeHeaderParser : public ELFAttributeParser {Error handler(uint64_t tag, bool &handled) override {// Treat all attributes as handled.handled = true;return Error::success();}public:AttributeHeaderParser(ScopedPrinter *printer): ELFAttributeParser(printer, emptyTagNameMap, "test") {}AttributeHeaderParser() : ELFAttributeParser(emptyTagNameMap, "test") {}};static void testParseError(ArrayRef<uint8_t> bytes, const char *msg) {AttributeHeaderParser parser;Error e = parser.parse(bytes, support::little);EXPECT_STREQ(toString(std::move(e)).c_str(), msg);}TEST(AttributeHeaderParser, UnrecognizedFormatVersion) {static const uint8_t bytes[] = {1};testParseError(bytes, "unrecognized format-version: 0x1");}TEST(AttributeHeaderParser, InvalidSectionLength) {static const uint8_t bytes[] = {'A', 3, 0, 0, 0};testParseError(bytes, "invalid section length 3 at offset 0x1");}TEST(AttributeHeaderParser, UnrecognizedVendorName) {static const uint8_t bytes[] = {'A', 7, 0, 0, 0, 'x', 'y', 0};testParseError(bytes, "unrecognized vendor-name: xy");}TEST(AttributeHeaderParser, UnrecognizedTag) {static const uint8_t bytes[] = {'A', 14, 0, 0, 0, 't', 'e', 's','t', 0, 4, 5, 0, 0, 0};testParseError(bytes, "unrecognized tag 0x4 at offset 0xa");}TEST(AttributeHeaderParser, InvalidAttributeSize) {static const uint8_t bytes[] = {'A', 14, 0, 0, 0, 't', 'e', 's','t', 0, 1, 4, 0, 0, 0};testParseError(bytes, "invalid attribute size 4 at offset 0xa");}
//===- llvm/unittest/Support/DynamicLibrary/PipSqueak.h -------------------===////// 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////===----------------------------------------------------------------------===//#ifndef LLVM_PIPSQUEAK_H#define LLVM_PIPSQUEAK_H#if defined(_WIN32) && !defined(__GNUC__)// Disable warnings from inclusion of xlocale & exception#pragma warning(push)#pragma warning(disable: 4530)#pragma warning(disable: 4577)#include <string>#include <vector>#pragma warning(pop)#else#include <string>#include <vector>#endif#ifdef _WIN32#define PIPSQUEAK_EXPORT __declspec(dllexport)#elif defined(__MVS__)#define PIPSQUEAK_EXPORT __attribute__((__visibility__("default")))#else#define PIPSQUEAK_EXPORT#endifextern "C" PIPSQUEAK_EXPORT const char *TestA();#endif
//===- llvm/unittest/Support/DynamicLibrary/PipSqueak.cpp -----------------===////// 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////===----------------------------------------------------------------------===//#include "PipSqueak.h"struct Global {std::string *Str;std::vector<std::string> *Vec;Global() : Str(nullptr), Vec(nullptr) {}~Global() {if (Str) {if (Vec)Vec->push_back(*Str);*Str = "Global::~Global";}}};static Global Glb;struct Local {std::string &Str;Local(std::string &S) : Str(S) {Str = "Local::Local";if (Glb.Str && !Glb.Str->empty())Str += std::string("(") + *Glb.Str + std::string(")");}~Local() { Str = "Local::~Local"; }};extern "C" PIPSQUEAK_EXPORT void SetStrings(std::string &GStr,std::string &LStr) {Glb.Str = &GStr;static Local Lcl(LStr);}extern "C" PIPSQUEAK_EXPORT void TestOrder(std::vector<std::string> &V) {Glb.Vec = &V;}#define PIPSQUEAK_TESTA_RETURN "LibCall"#include "ExportedFuncs.cpp"
//===- llvm/unittest/Support/DynamicLibrary/ExportedFuncs.cpp -------------===////// 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////===----------------------------------------------------------------------===//#include "PipSqueak.h"#ifndef PIPSQUEAK_TESTA_RETURN#define PIPSQUEAK_TESTA_RETURN "ProcessCall"#endifextern "C" PIPSQUEAK_EXPORT const char *TestA() { return PIPSQUEAK_TESTA_RETURN; }
//===- llvm/unittest/Support/DynamicLibrary/DynamicLibraryTest.cpp --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/DynamicLibrary.h"#include "llvm/Config/config.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/ManagedStatic.h"#include "llvm/Support/Path.h"#include "gtest/gtest.h"#include "PipSqueak.h"using namespace llvm;using namespace llvm::sys;std::string LibPath(const std::string Name = "PipSqueak") {const auto &Argvs = testing::internal::GetArgvs();const char *Argv0 =Argvs.size() > 0 ? Argvs[0].c_str() : "DynamicLibraryTests";void *Ptr = (void*)(intptr_t)TestA;std::string Path = fs::getMainExecutable(Argv0, Ptr);llvm::SmallString<256> Buf(path::parent_path(Path));path::append(Buf, (Name + LLVM_PLUGIN_EXT).c_str());return std::string(Buf.str());}#if defined(_WIN32) || (defined(HAVE_DLFCN_H) && defined(HAVE_DLOPEN))typedef void (*SetStrings)(std::string &GStr, std::string &LStr);typedef void (*TestOrder)(std::vector<std::string> &V);typedef const char *(*GetString)();template <class T> static T FuncPtr(void *Ptr) {union {T F;void *P;} Tmp;Tmp.P = Ptr;return Tmp.F;}template <class T> static void* PtrFunc(T *Func) {union {T *F;void *P;} Tmp;Tmp.F = Func;return Tmp.P;}static const char *OverloadTestA() { return "OverloadCall"; }std::string StdString(const char *Ptr) { return Ptr ? Ptr : ""; }TEST(DynamicLibrary, Overload) {{std::string Err;llvm_shutdown_obj Shutdown;DynamicLibrary DL =DynamicLibrary::getPermanentLibrary(LibPath().c_str(), &Err);EXPECT_TRUE(DL.isValid());EXPECT_TRUE(Err.empty());GetString GS = FuncPtr<GetString>(DL.getAddressOfSymbol("TestA"));EXPECT_NE(GS, nullptr);EXPECT_NE(GS, &TestA);EXPECT_EQ(StdString(GS()), "LibCall");GS = FuncPtr<GetString>(DynamicLibrary::SearchForAddressOfSymbol("TestA"));EXPECT_NE(GS, nullptr);EXPECT_NE(GS, &TestA);EXPECT_EQ(StdString(GS()), "LibCall");DL = DynamicLibrary::getPermanentLibrary(nullptr, &Err);EXPECT_TRUE(DL.isValid());EXPECT_TRUE(Err.empty());// Test overloading local symbols does not occur by defaultGS = FuncPtr<GetString>(DynamicLibrary::SearchForAddressOfSymbol("TestA"));EXPECT_NE(GS, nullptr);EXPECT_EQ(GS, &TestA);EXPECT_EQ(StdString(GS()), "ProcessCall");GS = FuncPtr<GetString>(DL.getAddressOfSymbol("TestA"));EXPECT_NE(GS, nullptr);EXPECT_EQ(GS, &TestA);EXPECT_EQ(StdString(GS()), "ProcessCall");// Test overloading by forcing library priority when searching for a symbolDynamicLibrary::SearchOrder = DynamicLibrary::SO_LoadedFirst;GS = FuncPtr<GetString>(DynamicLibrary::SearchForAddressOfSymbol("TestA"));EXPECT_NE(GS, nullptr);EXPECT_NE(GS, &TestA);EXPECT_EQ(StdString(GS()), "LibCall");DynamicLibrary::AddSymbol("TestA", PtrFunc(&OverloadTestA));GS = FuncPtr<GetString>(DL.getAddressOfSymbol("TestA"));EXPECT_NE(GS, nullptr);EXPECT_NE(GS, &OverloadTestA);GS = FuncPtr<GetString>(DynamicLibrary::SearchForAddressOfSymbol("TestA"));EXPECT_NE(GS, nullptr);EXPECT_EQ(GS, &OverloadTestA);EXPECT_EQ(StdString(GS()), "OverloadCall");}EXPECT_TRUE(FuncPtr<GetString>(DynamicLibrary::SearchForAddressOfSymbol("TestA")) == nullptr);// Check serach ordering is reset to default after call to llvm_shutdownEXPECT_EQ(DynamicLibrary::SearchOrder, DynamicLibrary::SO_Linker);}TEST(DynamicLibrary, Shutdown) {std::string A("PipSqueak"), B, C("SecondLib");std::vector<std::string> Order;{std::string Err;llvm_shutdown_obj Shutdown;DynamicLibrary DL =DynamicLibrary::getPermanentLibrary(LibPath(A).c_str(), &Err);EXPECT_TRUE(DL.isValid());EXPECT_TRUE(Err.empty());SetStrings SS_0 = FuncPtr<SetStrings>(DynamicLibrary::SearchForAddressOfSymbol("SetStrings"));EXPECT_NE(SS_0, nullptr);SS_0(A, B);EXPECT_EQ(B, "Local::Local(PipSqueak)");TestOrder TO_0 = FuncPtr<TestOrder>(DynamicLibrary::SearchForAddressOfSymbol("TestOrder"));EXPECT_NE(TO_0, nullptr);DynamicLibrary DL2 =DynamicLibrary::getPermanentLibrary(LibPath(C).c_str(), &Err);EXPECT_TRUE(DL2.isValid());EXPECT_TRUE(Err.empty());// Should find latest version of symbols in SecondLibSetStrings SS_1 = FuncPtr<SetStrings>(DynamicLibrary::SearchForAddressOfSymbol("SetStrings"));EXPECT_NE(SS_1, nullptr);EXPECT_NE(SS_0, SS_1);TestOrder TO_1 = FuncPtr<TestOrder>(DynamicLibrary::SearchForAddressOfSymbol("TestOrder"));EXPECT_NE(TO_1, nullptr);EXPECT_NE(TO_0, TO_1);B.clear();SS_1(C, B);EXPECT_EQ(B, "Local::Local(SecondLib)");TO_0(Order);TO_1(Order);}EXPECT_EQ(A, "Global::~Global");EXPECT_EQ(B, "Local::~Local");EXPECT_EQ(FuncPtr<SetStrings>(DynamicLibrary::SearchForAddressOfSymbol("SetStrings")),nullptr);// Test unload/destruction orderingEXPECT_EQ(Order.size(), 2UL);EXPECT_EQ(Order.front(), "SecondLib");EXPECT_EQ(Order.back(), "PipSqueak");}#elseTEST(DynamicLibrary, Unsupported) {std::string Err;DynamicLibrary DL =DynamicLibrary::getPermanentLibrary(LibPath().c_str(), &Err);EXPECT_FALSE(DL.isValid());EXPECT_EQ(Err, "dlopen() not supported on this platform");}#endif
# Needed by LLVM's CMake checks because this file defines multiple targets.set(LLVM_OPTIONAL_SOURCES ExportedFuncs.cpp PipSqueak.cpp)set(LLVM_LINK_COMPONENTS Support)add_library(DynamicLibraryLib STATICExportedFuncs.cpp)set_target_properties(DynamicLibraryLib PROPERTIES FOLDER "Tests")# extract_symbols.py relies on all its library arguments being in the same# directory, so we must set the output directory in the same way as if# add_llvm_library was used.set_output_directory(DynamicLibraryLibLIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR})add_llvm_unittest(DynamicLibraryTestsDynamicLibraryTest.cpp)target_link_libraries(DynamicLibraryTests PRIVATE DynamicLibraryLib)export_executable_symbols(DynamicLibraryTests)function(dynlib_add_module NAME)add_library(${NAME} MODULEPipSqueak.cpp)set_target_properties(${NAME} PROPERTIES FOLDER "Tests")set_output_directory(${NAME}BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})set_target_properties(${NAME}PROPERTIES PREFIX ""SUFFIX ${LLVM_PLUGIN_EXT})add_dependencies(DynamicLibraryTests ${NAME})if(LLVM_INTEGRATED_CRT_ALLOC)# We need to link in the Support lib for the Memory allocator override,# otherwise the DynamicLibrary.Shutdown test will fail, because it would# allocate memory with the CRT allocator, and release it with our custom# allocator (see llvm/lib/Support/Windows/Memory.inc).# /INCLUDE:malloc is there to force searching into LLVMSupport before libucrtllvm_map_components_to_libnames(llvm_libs Support)target_link_libraries(${NAME} ${llvm_libs} "-INCLUDE:malloc")endif()endfunction(dynlib_add_module)# Revert -Wl,-z,nodelete on this test since it relies on the file# being unloaded.if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")string(REPLACE "-Wl,-z,nodelete" "" CMAKE_MODULE_LINKER_FLAGS${CMAKE_MODULE_LINKER_FLAGS})endif()dynlib_add_module(PipSqueak)dynlib_add_module(SecondLib)
//===- llvm/unittest/Support/DebugTest.cpp --------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Debug.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <string>using namespace llvm;#ifndef NDEBUGTEST(DebugTest, Basic) {std::string s1, s2;raw_string_ostream os1(s1), os2(s2);static const char *DT[] = {"A", "B"};llvm::DebugFlag = true;setCurrentDebugTypes(DT, 2);DEBUG_WITH_TYPE("A", os1 << "A");DEBUG_WITH_TYPE("B", os1 << "B");EXPECT_EQ("AB", os1.str());setCurrentDebugType("A");DEBUG_WITH_TYPE("A", os2 << "A");DEBUG_WITH_TYPE("B", os2 << "B");EXPECT_EQ("A", os2.str());}#endif
//===- llvm/unittest/Support/DebugCounterTest.cpp -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/DebugCounter.h"#include "gtest/gtest.h"#include <string>using namespace llvm;#ifndef NDEBUGTEST(DebugCounterTest, CounterCheck) {DEBUG_COUNTER(TestCounter, "test-counter", "Counter used for unit test");EXPECT_FALSE(DebugCounter::isCounterSet(TestCounter));auto DC = &DebugCounter::instance();DC->push_back("test-counter-skip=1");DC->push_back("test-counter-count=3");EXPECT_TRUE(DebugCounter::isCounterSet(TestCounter));EXPECT_EQ(0, DebugCounter::getCounterValue(TestCounter));EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter));EXPECT_EQ(1, DebugCounter::getCounterValue(TestCounter));EXPECT_TRUE(DebugCounter::shouldExecute(TestCounter));DebugCounter::setCounterValue(TestCounter, 3);EXPECT_TRUE(DebugCounter::shouldExecute(TestCounter));EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter));DebugCounter::setCounterValue(TestCounter, 100);EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter));}#endif
//===- llvm/unittest/Support/DataExtractorTest.cpp - DataExtractor tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/DataExtractor.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {const char numberData[] = "\x80\x90\xFF\xFF\x80\x00\x00\x00";const char leb128data[] = "\xA6\x49";const char bigleb128data[] = "\xAA\xA9\xFF\xAA\xFF\xAA\xFF\x4A";TEST(DataExtractorTest, OffsetOverflow) {DataExtractor DE(StringRef(numberData, sizeof(numberData)-1), false, 8);EXPECT_FALSE(DE.isValidOffsetForDataOfSize(-2U, 5));}TEST(DataExtractorTest, UnsignedNumbers) {DataExtractor DE(StringRef(numberData, sizeof(numberData)-1), false, 8);uint64_t offset = 0;EXPECT_EQ(0x80U, DE.getU8(&offset));EXPECT_EQ(1U, offset);offset = 0;EXPECT_EQ(0x8090U, DE.getU16(&offset));EXPECT_EQ(2U, offset);offset = 0;EXPECT_EQ(0x8090FFFFU, DE.getU32(&offset));EXPECT_EQ(4U, offset);offset = 0;EXPECT_EQ(0x8090FFFF80000000ULL, DE.getU64(&offset));EXPECT_EQ(8U, offset);offset = 0;EXPECT_EQ(0x8090FFFF80000000ULL, DE.getAddress(&offset));EXPECT_EQ(8U, offset);offset = 0;uint32_t data[2];EXPECT_EQ(data, DE.getU32(&offset, data, 2));EXPECT_EQ(0x8090FFFFU, data[0]);EXPECT_EQ(0x80000000U, data[1]);EXPECT_EQ(8U, offset);offset = 0;// Now for little endian.DE = DataExtractor(StringRef(numberData, sizeof(numberData)-1), true, 4);EXPECT_EQ(0x9080U, DE.getU16(&offset));EXPECT_EQ(2U, offset);offset = 0;EXPECT_EQ(0xFFFF9080U, DE.getU32(&offset));EXPECT_EQ(4U, offset);offset = 0;EXPECT_EQ(0x80FFFF9080ULL, DE.getU64(&offset));EXPECT_EQ(8U, offset);offset = 0;EXPECT_EQ(0xFFFF9080U, DE.getAddress(&offset));EXPECT_EQ(4U, offset);offset = 0;EXPECT_EQ(data, DE.getU32(&offset, data, 2));EXPECT_EQ(0xFFFF9080U, data[0]);EXPECT_EQ(0x80U, data[1]);EXPECT_EQ(8U, offset);}TEST(DataExtractorTest, SignedNumbers) {DataExtractor DE(StringRef(numberData, sizeof(numberData)-1), false, 8);uint64_t offset = 0;EXPECT_EQ(-128, DE.getSigned(&offset, 1));EXPECT_EQ(1U, offset);offset = 0;EXPECT_EQ(-32624, DE.getSigned(&offset, 2));EXPECT_EQ(2U, offset);offset = 0;EXPECT_EQ(-2137980929, DE.getSigned(&offset, 4));EXPECT_EQ(4U, offset);offset = 0;EXPECT_EQ(-9182558167379214336LL, DE.getSigned(&offset, 8));EXPECT_EQ(8U, offset);}TEST(DataExtractorTest, Strings) {const char stringData[] = "hellohello\0hello";DataExtractor DE(StringRef(stringData, sizeof(stringData)-1), false, 8);uint64_t offset = 0;EXPECT_EQ(stringData, DE.getCStr(&offset));EXPECT_EQ(11U, offset);EXPECT_EQ(nullptr, DE.getCStr(&offset));EXPECT_EQ(11U, offset);DataExtractor::Cursor C(0);EXPECT_EQ(stringData, DE.getCStr(C));EXPECT_EQ(11U, C.tell());EXPECT_EQ(nullptr, DE.getCStr(C));EXPECT_EQ(11U, C.tell());EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("no null terminated string at offset 0xb"));}TEST(DataExtractorTest, LEB128) {DataExtractor DE(StringRef(leb128data, sizeof(leb128data)-1), false, 8);uint64_t offset = 0;EXPECT_EQ(9382ULL, DE.getULEB128(&offset));EXPECT_EQ(2U, offset);offset = 0;EXPECT_EQ(-7002LL, DE.getSLEB128(&offset));EXPECT_EQ(2U, offset);DataExtractor BDE(StringRef(bigleb128data, sizeof(bigleb128data)-1), false,8);offset = 0;EXPECT_EQ(42218325750568106ULL, BDE.getULEB128(&offset));EXPECT_EQ(8U, offset);offset = 0;EXPECT_EQ(-29839268287359830LL, BDE.getSLEB128(&offset));EXPECT_EQ(8U, offset);}TEST(DataExtractorTest, LEB128_error) {DataExtractor DE(StringRef("\x81"), false, 8);uint64_t Offset = 0;EXPECT_EQ(0U, DE.getULEB128(&Offset));EXPECT_EQ(0U, Offset);Offset = 0;EXPECT_EQ(0U, DE.getSLEB128(&Offset));EXPECT_EQ(0U, Offset);DataExtractor::Cursor C(0);EXPECT_EQ(0U, DE.getULEB128(C));EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("unable to decode LEB128 at offset 0x00000000: ""malformed uleb128, extends past end"));C = DataExtractor::Cursor(0);EXPECT_EQ(0U, DE.getSLEB128(C));EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("unable to decode LEB128 at offset 0x00000000: ""malformed sleb128, extends past end"));// Show non-zero offsets are reported appropriately.C = DataExtractor::Cursor(1);EXPECT_EQ(0U, DE.getULEB128(C));EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed uleb128, extends past end"));}TEST(DataExtractorTest, Cursor_tell) {DataExtractor DE(StringRef("AB"), false, 8);DataExtractor::Cursor C(0);// A successful read operation advances the cursorEXPECT_EQ('A', DE.getU8(C));EXPECT_EQ(1u, C.tell());// An unsuccessful one doesn't.EXPECT_EQ(0u, DE.getU16(C));EXPECT_EQ(1u, C.tell());// And neither do any subsequent operations.EXPECT_EQ(0, DE.getU8(C));EXPECT_EQ(1u, C.tell());consumeError(C.takeError());}TEST(DataExtractorTest, Cursor_seek) {DataExtractor::Cursor C(5);C.seek(3);EXPECT_EQ(3u, C.tell());C.seek(8);EXPECT_EQ(8u, C.tell());EXPECT_THAT_ERROR(C.takeError(), Succeeded());}TEST(DataExtractorTest, Cursor_takeError) {DataExtractor DE(StringRef("AB"), false, 8);DataExtractor::Cursor C(0);// Initially, the cursor is in the "success" state.EXPECT_THAT_ERROR(C.takeError(), Succeeded());// It remains "success" after a successful read.EXPECT_EQ('A', DE.getU8(C));EXPECT_THAT_ERROR(C.takeError(), Succeeded());// An unsuccessful read sets the error state.EXPECT_EQ(0u, DE.getU32(C));EXPECT_THAT_ERROR(C.takeError(), Failed());// Once set the error sticks until explicitly cleared.EXPECT_EQ(0u, DE.getU32(C));EXPECT_EQ(0, DE.getU8(C));EXPECT_THAT_ERROR(C.takeError(), Failed());// At which point reads can be succeed again.EXPECT_EQ('B', DE.getU8(C));EXPECT_THAT_ERROR(C.takeError(), Succeeded());}TEST(DataExtractorTest, Cursor_chaining) {DataExtractor DE(StringRef("ABCD"), false, 8);DataExtractor::Cursor C(0);// Multiple reads can be chained without trigerring any assertions.EXPECT_EQ('A', DE.getU8(C));EXPECT_EQ('B', DE.getU8(C));EXPECT_EQ('C', DE.getU8(C));EXPECT_EQ('D', DE.getU8(C));// And the error checked at the end.EXPECT_THAT_ERROR(C.takeError(), Succeeded());}#if defined(GTEST_HAS_DEATH_TEST) && defined(_DEBUG) && \LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(DataExtractorDeathTest, Cursor) {DataExtractor DE(StringRef("AB"), false, 8);// Even an unused cursor must be checked for errors:EXPECT_DEATH(DataExtractor::Cursor(0),"Success values must still be checked prior to being destroyed");{auto C = std::make_unique<DataExtractor::Cursor>(0);EXPECT_EQ(0u, DE.getU32(*C));// It must also be checked after an unsuccessful operation.// destruction.EXPECT_DEATH(C.reset(), "unexpected end of data");EXPECT_THAT_ERROR(C->takeError(), Failed());}{auto C = std::make_unique<DataExtractor::Cursor>(0);EXPECT_EQ('A', DE.getU8(*C));// Same goes for a successful one.EXPECT_DEATH(C.reset(),"Success values must still be checked prior to being destroyed");EXPECT_THAT_ERROR(C->takeError(), Succeeded());}{auto C = std::make_unique<DataExtractor::Cursor>(0);EXPECT_EQ('A', DE.getU8(*C));EXPECT_EQ(0u, DE.getU32(*C));// Even if a successful operation is followed by an unsuccessful one.EXPECT_DEATH(C.reset(), "unexpected end of data");EXPECT_THAT_ERROR(C->takeError(), Failed());}{auto C = std::make_unique<DataExtractor::Cursor>(0);EXPECT_EQ(0u, DE.getU32(*C));EXPECT_EQ(0, DE.getU8(*C));// Even if an unsuccessful operation is followed by one that would normally// succeed.EXPECT_DEATH(C.reset(), "unexpected end of data");EXPECT_THAT_ERROR(C->takeError(), Failed());}}#endifTEST(DataExtractorTest, getU8_vector) {DataExtractor DE(StringRef("AB"), false, 8);DataExtractor::Cursor C(0);SmallVector<uint8_t, 2> S;DE.getU8(C, S, 4);EXPECT_THAT_ERROR(C.takeError(), Failed());EXPECT_EQ("", toStringRef(S));DE.getU8(C, S, 2);EXPECT_THAT_ERROR(C.takeError(), Succeeded());EXPECT_EQ("AB", toStringRef(S));C = DataExtractor::Cursor(0x47);DE.getU8(C, S, 2);EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("offset 0x47 is beyond the end of data at 0x2"));}TEST(DataExtractorTest, getU24) {DataExtractor DE(StringRef("ABCD"), false, 8);DataExtractor::Cursor C(0);EXPECT_EQ(0x414243u, DE.getU24(C));EXPECT_EQ(0u, DE.getU24(C));EXPECT_EQ(3u, C.tell());EXPECT_THAT_ERROR(C.takeError(), Failed());}TEST(DataExtractorTest, skip) {DataExtractor DE(StringRef("AB"), false, 8);DataExtractor::Cursor C(0);DE.skip(C, 4);EXPECT_THAT_ERROR(C.takeError(), Failed());EXPECT_EQ(0u, C.tell());DE.skip(C, 2);EXPECT_THAT_ERROR(C.takeError(), Succeeded());EXPECT_EQ(2u, C.tell());}TEST(DataExtractorTest, eof) {DataExtractor DE(StringRef("A"), false, 8);DataExtractor::Cursor C(0);EXPECT_FALSE(DE.eof(C));EXPECT_EQ(0, DE.getU16(C));EXPECT_FALSE(DE.eof(C));EXPECT_THAT_ERROR(C.takeError(), Failed());EXPECT_EQ('A', DE.getU8(C));EXPECT_TRUE(DE.eof(C));EXPECT_THAT_ERROR(C.takeError(), Succeeded());}TEST(DataExtractorTest, size) {uint8_t Data[] = {'A', 'B', 'C', 'D'};DataExtractor DE1(StringRef(reinterpret_cast<char *>(Data), sizeof(Data)),false, 8);EXPECT_EQ(DE1.size(), sizeof(Data));DataExtractor DE2(ArrayRef<uint8_t>(Data), false, 8);EXPECT_EQ(DE2.size(), sizeof(Data));}TEST(DataExtractorTest, FixedLengthString) {const char Data[] = "hello\x00\x00\x00world \thola\x00";DataExtractor DE(StringRef(Data, sizeof(Data)-1), false, 8);uint64_t Offset = 0;StringRef Str;// Test extracting too many bytes doesn't modify Offset and returns None.Str = DE.getFixedLengthString(&Offset, sizeof(Data));EXPECT_TRUE(Str.empty());EXPECT_EQ(Offset, 0u);// Test extracting a fixed width C string with trailing NULL characters.Str = DE.getFixedLengthString(&Offset, 8);EXPECT_EQ(Offset, 8u);EXPECT_EQ(Str.size(), 5u);EXPECT_EQ(Str, "hello");// Test extracting a fixed width C string with trailing space and tab// characters.Str = DE.getFixedLengthString(&Offset, 8, " \t");EXPECT_EQ(Offset, 16u);EXPECT_EQ(Str.size(), 5u);EXPECT_EQ(Str, "world");// Now extract a normal C string.Str = DE.getCStrRef(&Offset);EXPECT_EQ(Str.size(), 4u);EXPECT_EQ(Str, "hola");}TEST(DataExtractorTest, GetBytes) {// Use data with an embedded NULL character for good measure.const char Data[] = "\x01\x02\x00\x04";StringRef Bytes(Data, sizeof(Data)-1);DataExtractor DE(Bytes, false, 8);uint64_t Offset = 0;StringRef Str;// Test extracting too many bytes doesn't modify Offset and returns None.Str = DE.getBytes(&Offset, sizeof(Data));EXPECT_TRUE(Str.empty());EXPECT_EQ(Offset, 0u);// Test extracting 4 bytes from the stream.Str = DE.getBytes(&Offset, 4);EXPECT_EQ(Offset, 4u);EXPECT_EQ(Str.size(), 4u);EXPECT_EQ(Str, Bytes);DataExtractor::Cursor C(0);EXPECT_EQ(StringRef("\x01\x02"), DE.getBytes(C, 2));EXPECT_EQ(StringRef("\x00\x04", 2), DE.getBytes(C, 2));EXPECT_EQ(StringRef(), DE.getBytes(C, 2));EXPECT_EQ(StringRef(), DE.getBytes(C, 2));EXPECT_EQ(4u, C.tell());EXPECT_THAT_ERROR(C.takeError(), Failed());}}
//===---------- llvm/unittest/Support/DJBTest.cpp -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/DJB.h"#include "llvm/ADT/Twine.h"#include "gtest/gtest.h"using namespace llvm;TEST(DJBTest, caseFolding) {struct TestCase {StringLiteral One;StringLiteral Two;};static constexpr TestCase Tests[] = {{{"ASDF"}, {"asdf"}},{{"qWeR"}, {"QwEr"}},{{"qqqqqqqqqqqqqqqqqqqq"}, {"QQQQQQQQQQQQQQQQQQQQ"}},{{"I"}, {"i"}},// Latin Small Letter Dotless I{{u8"\u0130"}, {"i"}},// Latin Capital Letter I With Dot Above{{u8"\u0131"}, {"i"}},// Latin Capital Letter A With Grave{{u8"\u00c0"}, {u8"\u00e0"}},// Latin Capital Letter A With Macron{{u8"\u0100"}, {u8"\u0101"}},// Latin Capital Letter L With Acute{{u8"\u0139"}, {u8"\u013a"}},// Cyrillic Capital Letter Ie{{u8"\u0415"}, {u8"\u0435"}},// Latin Capital Letter A With Circumflex And Grave{{u8"\u1ea6"}, {u8"\u1ea7"}},// Kelvin Sign{{u8"\u212a"}, {u8"\u006b"}},// Glagolitic Capital Letter Chrivi{{u8"\u2c1d"}, {u8"\u2c4d"}},// Fullwidth Latin Capital Letter M{{u8"\uff2d"}, {u8"\uff4d"}},// Old Hungarian Capital Letter Ej{{u8"\U00010c92"}, {u8"\U00010cd2"}},};for (const TestCase &T : Tests) {SCOPED_TRACE("Comparing '" + T.One + "' and '" + T.Two + "'");EXPECT_EQ(caseFoldingDjbHash(T.One), caseFoldingDjbHash(T.Two));}}TEST(DJBTest, knownValuesLowerCase) {struct TestCase {StringLiteral Text;uint32_t Hash;};static constexpr TestCase Tests[] = {{{""}, 5381u},{{"f"}, 177675u},{{"fo"}, 5863386u},{{"foo"}, 193491849u},{{"foob"}, 2090263819u},{{"fooba"}, 259229388u},{{"foobar"}, 4259602622u},{{"pneumonoultramicroscopicsilicovolcanoconiosis"}, 3999417781u},};for (const TestCase &T : Tests) {SCOPED_TRACE("Text: '" + T.Text + "'");EXPECT_EQ(T.Hash, djbHash(T.Text));EXPECT_EQ(T.Hash, caseFoldingDjbHash(T.Text));EXPECT_EQ(T.Hash, caseFoldingDjbHash(T.Text.upper()));}}TEST(DJBTest, knownValuesUnicode) {EXPECT_EQ(5866553u, djbHash(u8"\u0130"));EXPECT_EQ(177678u, caseFoldingDjbHash(u8"\u0130"));EXPECT_EQ(1302161417u,djbHash(u8"\u0130\u0131\u00c0\u00e0\u0100\u0101\u0139\u013a\u0415\u0435\u1ea6"u8"\u1ea7\u212a\u006b\u2c1d\u2c4d\uff2d\uff4d\U00010c92\U00010cd2"));EXPECT_EQ(1145571043u,caseFoldingDjbHash(u8"\u0130\u0131\u00c0\u00e0\u0100\u0101\u0139\u013a\u0415\u0435\u1ea6"u8"\u1ea7\u212a\u006b\u2c1d\u2c4d\uff2d\uff4d\U00010c92\U00010cd2"));}
//===- llvm/unittest/Support/CrashRecoveryTest.cpp ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Triple.h"#include "llvm/Config/config.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/Compiler.h"#include "llvm/Support/CrashRecoveryContext.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Host.h"#include "llvm/Support/Program.h"#include "llvm/Support/Signals.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#ifdef _WIN32#define WIN32_LEAN_AND_MEAN#define NOGDI#include <windows.h>#endif#ifdef LLVM_ON_UNIX#ifdef HAVE_SIGNAL_H#include <signal.h>#endif#endifusing namespace llvm;using namespace llvm::sys;static int GlobalInt = 0;static void nullDeref() { *(volatile int *)0x10 = 0; }static void incrementGlobal() { ++GlobalInt; }static void llvmTrap() { LLVM_BUILTIN_TRAP; }static void incrementGlobalWithParam(void *) { ++GlobalInt; }TEST(CrashRecoveryTest, Basic) {llvm::CrashRecoveryContext::Enable();GlobalInt = 0;EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal));EXPECT_EQ(1, GlobalInt);EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref));EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap));}struct IncrementGlobalCleanup : CrashRecoveryContextCleanup {IncrementGlobalCleanup(CrashRecoveryContext *CRC): CrashRecoveryContextCleanup(CRC) {}void recoverResources() override { ++GlobalInt; }};static void noop() {}TEST(CrashRecoveryTest, Cleanup) {llvm::CrashRecoveryContext::Enable();GlobalInt = 0;{CrashRecoveryContext CRC;CRC.registerCleanup(new IncrementGlobalCleanup(&CRC));EXPECT_TRUE(CRC.RunSafely(noop));} // run cleanupsEXPECT_EQ(1, GlobalInt);GlobalInt = 0;{CrashRecoveryContext CRC;CRC.registerCleanup(new IncrementGlobalCleanup(&CRC));EXPECT_FALSE(CRC.RunSafely(nullDeref));} // run cleanupsEXPECT_EQ(1, GlobalInt);llvm::CrashRecoveryContext::Disable();}TEST(CrashRecoveryTest, DumpStackCleanup) {SmallString<128> Filename;std::error_code EC = sys::fs::createTemporaryFile("crash", "test", Filename);EXPECT_FALSE(EC);sys::RemoveFileOnSignal(Filename);llvm::sys::AddSignalHandler(incrementGlobalWithParam, nullptr);GlobalInt = 0;llvm::CrashRecoveryContext::Enable();{CrashRecoveryContext CRC;CRC.DumpStackAndCleanupOnFailure = true;EXPECT_TRUE(CRC.RunSafely(noop));}EXPECT_TRUE(sys::fs::exists(Filename));EXPECT_EQ(GlobalInt, 0);{CrashRecoveryContext CRC;CRC.DumpStackAndCleanupOnFailure = true;EXPECT_FALSE(CRC.RunSafely(nullDeref));EXPECT_NE(CRC.RetCode, 0);}EXPECT_FALSE(sys::fs::exists(Filename));EXPECT_EQ(GlobalInt, 1);llvm::CrashRecoveryContext::Disable();}TEST(CrashRecoveryTest, LimitedStackTrace) {// FIXME: Handle "Depth" parameter in PrintStackTrace() function// to print stack trace upto a specified Depth.if (Triple(sys::getProcessTriple()).isOSWindows())GTEST_SKIP();std::string Res;llvm::raw_string_ostream RawStream(Res);PrintStackTrace(RawStream, 1);std::string Str = RawStream.str();EXPECT_EQ(std::string::npos, Str.find("#1"));}#ifdef _WIN32static void raiseIt() {RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL);}TEST(CrashRecoveryTest, RaiseException) {llvm::CrashRecoveryContext::Enable();EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt));}static void outputString() {OutputDebugStringA("output for debugger\n");}TEST(CrashRecoveryTest, CallOutputDebugString) {llvm::CrashRecoveryContext::Enable();EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString));}TEST(CrashRecoveryTest, Abort) {llvm::CrashRecoveryContext::Enable();auto A = []() { abort(); };EXPECT_FALSE(CrashRecoveryContext().RunSafely(A));// Test a second time to ensure we reinstall the abort signal handler.EXPECT_FALSE(CrashRecoveryContext().RunSafely(A));}#endif// Specifically ensure that programs that signal() or abort() through the// CrashRecoveryContext can re-throw again their signal, so that `not --crash`// succeeds.#ifdef LLVM_ON_UNIX// See llvm/utils/unittest/UnitTestMain/TestMain.cppextern const char *TestMainArgv0;// Just a reachable symbol to ease resolving of the executable's path.static cl::opt<std::string> CrashTestStringArg1("crash-test-string-arg1");TEST(CrashRecoveryTest, UnixCRCReturnCode) {using namespace llvm::sys;if (getenv("LLVM_CRC_UNIXCRCRETURNCODE")) {llvm::CrashRecoveryContext::Enable();CrashRecoveryContext CRC;// This path runs in a subprocess that exits by signalling, so don't use// the googletest macros to verify things as they won't report properly.if (CRC.RunSafely(abort))llvm_unreachable("RunSafely returned true!");if (CRC.RetCode != 128 + SIGABRT)llvm_unreachable("Unexpected RetCode!");// re-throw signalllvm::sys::unregisterHandlers();raise(CRC.RetCode - 128);llvm_unreachable("Should have exited already!");}std::string Executable =sys::fs::getMainExecutable(TestMainArgv0, &CrashTestStringArg1);StringRef argv[] = {Executable, "--gtest_filter=CrashRecoveryTest.UnixCRCReturnCode"};// Add LLVM_CRC_UNIXCRCRETURNCODE to the environment of the child process.int Res = setenv("LLVM_CRC_UNIXCRCRETURNCODE", "1", 0);ASSERT_EQ(Res, 0);Res = unsetenv("GTEST_SHARD_INDEX");ASSERT_EQ(Res, 0);Res = unsetenv("GTEST_TOTAL_SHARDS");ASSERT_EQ(Res, 0);std::string Error;bool ExecutionFailed;int RetCode = ExecuteAndWait(Executable, argv, {}, {}, 0, 0, &Error,&ExecutionFailed);ASSERT_EQ(-2, RetCode);}#endif
//===- llvm/unittest/Support/ConvertUTFTest.cpp - ConvertUTF tests --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ConvertUTF.h"#include "llvm/ADT/ArrayRef.h"#include "gtest/gtest.h"#include <string>#include <vector>using namespace llvm;TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) {// Src is the look of disapproval.alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";ArrayRef<char> Ref(Src, sizeof(Src) - 1);std::string Result;bool Success = convertUTF16ToUTF8String(Ref, Result);EXPECT_TRUE(Success);std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");EXPECT_EQ(Expected, Result);}TEST(ConvertUTFTest, ConvertUTF32LittleEndianToUTF8String) {// Src is the look of disapproval.alignas(UTF32) static const char Src[] ="\xFF\xFE\x00\x00\xA0\x0C\x00\x00\x5F\x00\x00\x00\xA0\x0C\x00\x00";ArrayRef<char> Ref(Src, sizeof(Src) - 1);std::string Result;bool Success = convertUTF32ToUTF8String(Ref, Result);EXPECT_TRUE(Success);std::string Expected("\xE0\xB2\xA0_\xE0\xB2\xA0");EXPECT_EQ(Expected, Result);}TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) {// Src is the look of disapproval.alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0";ArrayRef<char> Ref(Src, sizeof(Src) - 1);std::string Result;bool Success = convertUTF16ToUTF8String(Ref, Result);EXPECT_TRUE(Success);std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");EXPECT_EQ(Expected, Result);}TEST(ConvertUTFTest, ConvertUTF32BigEndianToUTF8String) {// Src is the look of disapproval.alignas(UTF32) static const char Src[] ="\x00\x00\xFE\xFF\x00\x00\x0C\xA0\x00\x00\x00\x5F\x00\x00\x0C\xA0";ArrayRef<char> Ref(Src, sizeof(Src) - 1);std::string Result;bool Success = convertUTF32ToUTF8String(Ref, Result);EXPECT_TRUE(Success);std::string Expected("\xE0\xB2\xA0_\xE0\xB2\xA0");EXPECT_EQ(Expected, Result);}TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) {// Src is the look of disapproval.static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0";StringRef Ref(Src, sizeof(Src) - 1);SmallVector<UTF16, 5> Result;bool Success = convertUTF8ToUTF16String(Ref, Result);EXPECT_TRUE(Success);static const UTF16 Expected[] = {0x0CA0, 0x005f, 0x0CA0, 0};ASSERT_EQ(3u, Result.size());for (int I = 0, E = 3; I != E; ++I)EXPECT_EQ(Expected[I], Result[I]);}TEST(ConvertUTFTest, OddLengthInput) {std::string Result;bool Success = convertUTF16ToUTF8String(makeArrayRef("xxxxx", 5), Result);EXPECT_FALSE(Success);}TEST(ConvertUTFTest, Empty) {std::string Result;bool Success = convertUTF16ToUTF8String(llvm::ArrayRef<char>(None), Result);EXPECT_TRUE(Success);EXPECT_TRUE(Result.empty());}TEST(ConvertUTFTest, HasUTF16BOM) {bool HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xff\xfe", 2));EXPECT_TRUE(HasBOM);HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff", 2));EXPECT_TRUE(HasBOM);HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff ", 3));EXPECT_TRUE(HasBOM); // Don't care about odd lengths.HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff\x00asdf", 6));EXPECT_TRUE(HasBOM);HasBOM = hasUTF16ByteOrderMark(None);EXPECT_FALSE(HasBOM);HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe", 1));EXPECT_FALSE(HasBOM);}TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) {// Src is the look of disapproval.alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c";ArrayRef<UTF16> SrcRef = makeArrayRef((const UTF16 *)Src, 4);std::string Result;bool Success = convertUTF16ToUTF8String(SrcRef, Result);EXPECT_TRUE(Success);std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");EXPECT_EQ(Expected, Result);}TEST(ConvertUTFTest, ConvertUTF8toWide) {// Src is the look of disapproval.static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0";std::wstring Result;bool Success = ConvertUTF8toWide((const char*)Src, Result);EXPECT_TRUE(Success);std::wstring Expected(L"\x0ca0_\x0ca0");EXPECT_EQ(Expected, Result);Result.clear();Success = ConvertUTF8toWide(StringRef(Src, 7), Result);EXPECT_TRUE(Success);EXPECT_EQ(Expected, Result);}TEST(ConvertUTFTest, convertWideToUTF8) {// Src is the look of disapproval.static const wchar_t Src[] = L"\x0ca0_\x0ca0";std::string Result;bool Success = convertWideToUTF8(Src, Result);EXPECT_TRUE(Success);std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0");EXPECT_EQ(Expected, Result);}struct ConvertUTFResultContainer {ConversionResult ErrorCode;std::vector<unsigned> UnicodeScalars;ConvertUTFResultContainer(ConversionResult ErrorCode): ErrorCode(ErrorCode) {}ConvertUTFResultContainerwithScalars(unsigned US0 = 0x110000, unsigned US1 = 0x110000,unsigned US2 = 0x110000, unsigned US3 = 0x110000,unsigned US4 = 0x110000, unsigned US5 = 0x110000,unsigned US6 = 0x110000, unsigned US7 = 0x110000) {ConvertUTFResultContainer Result(*this);if (US0 != 0x110000)Result.UnicodeScalars.push_back(US0);if (US1 != 0x110000)Result.UnicodeScalars.push_back(US1);if (US2 != 0x110000)Result.UnicodeScalars.push_back(US2);if (US3 != 0x110000)Result.UnicodeScalars.push_back(US3);if (US4 != 0x110000)Result.UnicodeScalars.push_back(US4);if (US5 != 0x110000)Result.UnicodeScalars.push_back(US5);if (US6 != 0x110000)Result.UnicodeScalars.push_back(US6);if (US7 != 0x110000)Result.UnicodeScalars.push_back(US7);return Result;}};std::pair<ConversionResult, std::vector<unsigned>>ConvertUTF8ToUnicodeScalarsLenient(StringRef S) {const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(S.data());const UTF8 *SourceNext = SourceStart;std::vector<UTF32> Decoded(S.size(), 0);UTF32 *TargetStart = Decoded.data();auto ErrorCode =ConvertUTF8toUTF32(&SourceNext, SourceStart + S.size(), &TargetStart,Decoded.data() + Decoded.size(), lenientConversion);Decoded.resize(TargetStart - Decoded.data());return std::make_pair(ErrorCode, Decoded);}std::pair<ConversionResult, std::vector<unsigned>>ConvertUTF8ToUnicodeScalarsPartialLenient(StringRef S) {const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(S.data());const UTF8 *SourceNext = SourceStart;std::vector<UTF32> Decoded(S.size(), 0);UTF32 *TargetStart = Decoded.data();auto ErrorCode = ConvertUTF8toUTF32Partial(&SourceNext, SourceStart + S.size(), &TargetStart,Decoded.data() + Decoded.size(), lenientConversion);Decoded.resize(TargetStart - Decoded.data());return std::make_pair(ErrorCode, Decoded);}::testing::AssertionResultCheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer Expected,StringRef S, bool Partial = false) {ConversionResult ErrorCode;std::vector<unsigned> Decoded;if (!Partial)std::tie(ErrorCode, Decoded) = ConvertUTF8ToUnicodeScalarsLenient(S);elsestd::tie(ErrorCode, Decoded) = ConvertUTF8ToUnicodeScalarsPartialLenient(S);if (Expected.ErrorCode != ErrorCode)return ::testing::AssertionFailure() << "Expected error code "<< Expected.ErrorCode << ", actual "<< ErrorCode;if (Expected.UnicodeScalars != Decoded)return ::testing::AssertionFailure()<< "Expected lenient decoded result:\n"<< ::testing::PrintToString(Expected.UnicodeScalars) << "\n"<< "Actual result:\n" << ::testing::PrintToString(Decoded);return ::testing::AssertionSuccess();}TEST(ConvertUTFTest, UTF8ToUTF32Lenient) {//// 1-byte sequences//// U+0041 LATIN CAPITAL LETTER AEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x0041), "\x41"));//// 2-byte sequences//// U+0283 LATIN SMALL LETTER ESHEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x0283),"\xca\x83"));// U+03BA GREEK SMALL LETTER KAPPA// U+1F79 GREEK SMALL LETTER OMICRON WITH OXIA// U+03C3 GREEK SMALL LETTER SIGMA// U+03BC GREEK SMALL LETTER MU// U+03B5 GREEK SMALL LETTER EPSILONEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x03ba, 0x1f79, 0x03c3, 0x03bc, 0x03b5),"\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5"));//// 3-byte sequences//// U+4F8B CJK UNIFIED IDEOGRAPH-4F8B// U+6587 CJK UNIFIED IDEOGRAPH-6587EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x4f8b, 0x6587),"\xe4\xbe\x8b\xe6\x96\x87"));// U+D55C HANGUL SYLLABLE HAN// U+AE00 HANGUL SYLLABLE GEULEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xd55c, 0xae00),"\xed\x95\x9c\xea\xb8\x80"));// U+1112 HANGUL CHOSEONG HIEUH// U+1161 HANGUL JUNGSEONG A// U+11AB HANGUL JONGSEONG NIEUN// U+1100 HANGUL CHOSEONG KIYEOK// U+1173 HANGUL JUNGSEONG EU// U+11AF HANGUL JONGSEONG RIEULEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x1112, 0x1161, 0x11ab, 0x1100, 0x1173, 0x11af),"\xe1\x84\x92\xe1\x85\xa1\xe1\x86\xab\xe1\x84\x80\xe1\x85\xb3""\xe1\x86\xaf"));//// 4-byte sequences//// U+E0100 VARIATION SELECTOR-17EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x000E0100),"\xf3\xa0\x84\x80"));//// First possible sequence of a certain length//// U+0000 NULLEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x0000),StringRef("\x00", 1)));// U+0080 PADDING CHARACTEREXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x0080),"\xc2\x80"));// U+0800 SAMARITAN LETTER ALAFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x0800),"\xe0\xa0\x80"));// U+10000 LINEAR B SYLLABLE B008 AEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x10000),"\xf0\x90\x80\x80"));// U+200000 (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\x88\x80\x80\x80"));// U+4000000 (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x84\x80\x80\x80\x80"));//// Last possible sequence of a certain length//// U+007F DELETEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x007f), "\x7f"));// U+07FF (unassigned)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x07ff),"\xdf\xbf"));// U+FFFF (noncharacter)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xffff),"\xef\xbf\xbf"));// U+1FFFFF (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf7\xbf\xbf\xbf"));// U+3FFFFFF (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfb\xbf\xbf\xbf\xbf"));// U+7FFFFFFF (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfd\xbf\xbf\xbf\xbf\xbf"));//// Other boundary conditions//// U+D7FF (unassigned)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xd7ff),"\xed\x9f\xbf"));// U+E000 (private use)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xe000),"\xee\x80\x80"));// U+FFFD REPLACEMENT CHARACTEREXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfffd),"\xef\xbf\xbd"));// U+10FFFF (noncharacter)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x10ffff),"\xf4\x8f\xbf\xbf"));// U+110000 (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf4\x90\x80\x80"));//// Unexpected continuation bytes//// A sequence of unexpected continuation bytes that don't follow a first// byte, every byte is a maximal subpart.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\x80\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xbf\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\x80\xbf\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\x80\xbf\x80\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\x80\xbf\x82\xbf\xaa"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xaa\xb0\xbb\xbf\xaa\xa0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xaa\xb0\xbb\xbf\xaa\xa0\x8f"));// All continuation bytes (0x80--0xbf).EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd),"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f""\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f""\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf""\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"));//// Lonely start bytes//// Start bytes of 2-byte sequences (0xc0--0xdf).EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf""\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020),"\xc0\x20\xc1\x20\xc2\x20\xc3\x20\xc4\x20\xc5\x20\xc6\x20\xc7\x20""\xc8\x20\xc9\x20\xca\x20\xcb\x20\xcc\x20\xcd\x20\xce\x20\xcf\x20""\xd0\x20\xd1\x20\xd2\x20\xd3\x20\xd4\x20\xd5\x20\xd6\x20\xd7\x20""\xd8\x20\xd9\x20\xda\x20\xdb\x20\xdc\x20\xdd\x20\xde\x20\xdf\x20"));// Start bytes of 3-byte sequences (0xe0--0xef).EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020),"\xe0\x20\xe1\x20\xe2\x20\xe3\x20\xe4\x20\xe5\x20\xe6\x20\xe7\x20""\xe8\x20\xe9\x20\xea\x20\xeb\x20\xec\x20\xed\x20\xee\x20\xef\x20"));// Start bytes of 4-byte sequences (0xf0--0xf7).EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd,0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020),"\xf0\x20\xf1\x20\xf2\x20\xf3\x20\xf4\x20\xf5\x20\xf6\x20\xf7\x20"));// Start bytes of 5-byte sequences (0xf8--0xfb).EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\xf9\xfa\xfb"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020),"\xf8\x20\xf9\x20\xfa\x20\xfb\x20"));// Start bytes of 6-byte sequences (0xfc--0xfd).EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfc\xfd"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020),"\xfc\x20\xfd\x20"));//// Other bytes (0xc0--0xc1, 0xfe--0xff).//EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xc0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xc1"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfe"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xff"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xc0\xc1\xfe\xff"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfe\xfe\xff\xff"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfe\x80\x80\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xff\x80\x80\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0x0020, 0xfffd, 0x0020,0xfffd, 0x0020, 0xfffd, 0x0020),"\xc0\x20\xc1\x20\xfe\x20\xff\x20"));//// Sequences with one continuation byte missing//EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xc2"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xdf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xe0\xa0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xe0\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xe1\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xec\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xed\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xed\x9f"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xee\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xef\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf0\x90\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf0\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf1\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf3\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf4\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf4\x8f\xbf"));// Overlong sequences with one trailing byte missing.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xc0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xc1"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xe0\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xe0\x9f"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf0\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf0\x8f\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x80\x80\x80\x80"));// Sequences that represent surrogates with one trailing byte missing.// High surrogatesEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xed\xa0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xed\xac"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xed\xaf"));// Low surrogatesEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xed\xb0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xed\xb4"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xed\xbf"));// Ill-formed 4-byte sequences.// 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx// U+1100xx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf4\x90\x80"));// U+13FBxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf4\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf5\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf6\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf7\x80\x80"));// U+1FFBxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf7\xbf\xbf"));// Ill-formed 5-byte sequences.// 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+2000xx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\x88\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\xbf\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf9\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfa\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfb\x80\x80\x80"));// U+3FFFFxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfb\xbf\xbf\xbf"));// Ill-formed 6-byte sequences.// 1111110u 10uuuuuu 10uzzzzz 10zzzyyyy 10yyyyxx 10xxxxxx// U+40000xx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x84\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\xbf\xbf\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfd\x80\x80\x80\x80"));// U+7FFFFFxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfd\xbf\xbf\xbf\xbf"));//// Sequences with two continuation bytes missing//EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf0\x90"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf0\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf1\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf3\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf4\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd),"\xf4\x8f"));// Overlong sequences with two trailing byte missing.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xe0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf0\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf0\x8f"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf8\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x80\x80\x80"));// Sequences that represent surrogates with two trailing bytes missing.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xed"));// Ill-formed 4-byte sequences.// 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx// U+110yxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf4\x90"));// U+13Fyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf4\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf5\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf6\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf7\x80"));// U+1FFyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf7\xbf"));// Ill-formed 5-byte sequences.// 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+200yxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf8\x88\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf8\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xf9\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfa\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfb\x80\x80"));// U+3FFFyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfb\xbf\xbf"));// Ill-formed 6-byte sequences.// 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+4000yxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x84\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\xbf\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfd\x80\x80\x80"));// U+7FFFFyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfd\xbf\xbf\xbf"));//// Sequences with three continuation bytes missing//EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf1"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf2"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf3"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf4"));// Broken overlong sequences.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf0"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf8\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfc\x80\x80"));// Ill-formed 4-byte sequences.// 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx// U+14yyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf5"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf6"));// U+1Cyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf7"));// Ill-formed 5-byte sequences.// 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+20yyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf8\x88"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf8\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xf9\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfa\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfb\x80"));// U+3FCyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfb\xbf"));// Ill-formed 6-byte sequences.// 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+400yyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfc\x84\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfc\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfd\x80\x80"));// U+7FFCyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xfd\xbf\xbf"));//// Sequences with four continuation bytes missing//// Ill-formed 5-byte sequences.// 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+uzyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf8"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf9"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfa"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfb"));// U+3zyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfb"));// Broken overlong sequences.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf8"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfc\x80"));// Ill-formed 6-byte sequences.// 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+uzzyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfc\x84"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfc\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfd\x80"));// U+7Fzzyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xfd\xbf"));//// Sequences with five continuation bytes missing//// Ill-formed 6-byte sequences.// 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx// U+uzzyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfc"));// U+uuzzyyxx (invalid)EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfd"));//// Consecutive sequences with trailing bytes missing//EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, /**/ 0xfffd, 0xfffd, /**/ 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, /**/ 0xfffd, /**/ 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xc0" "\xe0\x80" "\xf0\x80\x80""\xf8\x80\x80\x80""\xfc\x80\x80\x80\x80""\xdf" "\xef\xbf" "\xf7\xbf\xbf""\xfb\xbf\xbf\xbf""\xfd\xbf\xbf\xbf\xbf"));//// Overlong UTF-8 sequences//// U+002F SOLIDUSEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x002f), "\x2f"));// Overlong sequences of the above.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xc0\xaf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xe0\x80\xaf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf0\x80\x80\xaf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\x80\x80\x80\xaf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x80\x80\x80\x80\xaf"));// U+0000 NULLEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x0000),StringRef("\x00", 1)));// Overlong sequences of the above.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xc0\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xe0\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf0\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\x80\x80\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x80\x80\x80\x80\x80"));// Other overlong sequences.EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xc0\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xc1\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd),"\xc1\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xe0\x9f\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xa0\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf0\x8f\x80\x80"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf0\x8f\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xf8\x87\xbf\xbf\xbf"));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xfc\x83\xbf\xbf\xbf\xbf"));//// Isolated surrogates//// Unicode 6.3.0://// D71. High-surrogate code point: A Unicode code point in the range// U+D800 to U+DBFF.//// D73. Low-surrogate code point: A Unicode code point in the range// U+DC00 to U+DFFF.// Note: U+E0100 is <DB40 DD00> in UTF16.// High surrogates// U+D800EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xa0\x80"));// U+DB40EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xac\xa0"));// U+DBFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xaf\xbf"));// Low surrogates// U+DC00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xb0\x80"));// U+DD00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xb4\x80"));// U+DFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd),"\xed\xbf\xbf"));// Surrogate pairs// U+D800 U+DC00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xa0\x80\xed\xb0\x80"));// U+D800 U+DD00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xa0\x80\xed\xb4\x80"));// U+D800 U+DFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xa0\x80\xed\xbf\xbf"));// U+DB40 U+DC00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xac\xa0\xed\xb0\x80"));// U+DB40 U+DD00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xac\xa0\xed\xb4\x80"));// U+DB40 U+DFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xac\xa0\xed\xbf\xbf"));// U+DBFF U+DC00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xaf\xbf\xed\xb0\x80"));// U+DBFF U+DD00EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xaf\xbf\xed\xb4\x80"));// U+DBFF U+DFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd),"\xed\xaf\xbf\xed\xbf\xbf"));//// Noncharacters//// Unicode 6.3.0://// D14. Noncharacter: A code point that is permanently reserved for// internal use and that should never be interchanged. Noncharacters// consist of the values U+nFFFE and U+nFFFF (where n is from 0 to 1016)// and the values U+FDD0..U+FDEF.// U+FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfffe),"\xef\xbf\xbe"));// U+FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xffff),"\xef\xbf\xbf"));// U+1FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x1fffe),"\xf0\x9f\xbf\xbe"));// U+1FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x1ffff),"\xf0\x9f\xbf\xbf"));// U+2FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x2fffe),"\xf0\xaf\xbf\xbe"));// U+2FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x2ffff),"\xf0\xaf\xbf\xbf"));// U+3FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x3fffe),"\xf0\xbf\xbf\xbe"));// U+3FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x3ffff),"\xf0\xbf\xbf\xbf"));// U+4FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x4fffe),"\xf1\x8f\xbf\xbe"));// U+4FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x4ffff),"\xf1\x8f\xbf\xbf"));// U+5FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x5fffe),"\xf1\x9f\xbf\xbe"));// U+5FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x5ffff),"\xf1\x9f\xbf\xbf"));// U+6FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x6fffe),"\xf1\xaf\xbf\xbe"));// U+6FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x6ffff),"\xf1\xaf\xbf\xbf"));// U+7FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x7fffe),"\xf1\xbf\xbf\xbe"));// U+7FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x7ffff),"\xf1\xbf\xbf\xbf"));// U+8FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x8fffe),"\xf2\x8f\xbf\xbe"));// U+8FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x8ffff),"\xf2\x8f\xbf\xbf"));// U+9FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x9fffe),"\xf2\x9f\xbf\xbe"));// U+9FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x9ffff),"\xf2\x9f\xbf\xbf"));// U+AFFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xafffe),"\xf2\xaf\xbf\xbe"));// U+AFFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xaffff),"\xf2\xaf\xbf\xbf"));// U+BFFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xbfffe),"\xf2\xbf\xbf\xbe"));// U+BFFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xbffff),"\xf2\xbf\xbf\xbf"));// U+CFFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xcfffe),"\xf3\x8f\xbf\xbe"));// U+CFFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xcfffF),"\xf3\x8f\xbf\xbf"));// U+DFFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xdfffe),"\xf3\x9f\xbf\xbe"));// U+DFFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xdffff),"\xf3\x9f\xbf\xbf"));// U+EFFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xefffe),"\xf3\xaf\xbf\xbe"));// U+EFFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xeffff),"\xf3\xaf\xbf\xbf"));// U+FFFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xffffe),"\xf3\xbf\xbf\xbe"));// U+FFFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfffff),"\xf3\xbf\xbf\xbf"));// U+10FFFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x10fffe),"\xf4\x8f\xbf\xbe"));// U+10FFFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x10ffff),"\xf4\x8f\xbf\xbf"));// U+FDD0EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd0),"\xef\xb7\x90"));// U+FDD1EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd1),"\xef\xb7\x91"));// U+FDD2EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd2),"\xef\xb7\x92"));// U+FDD3EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd3),"\xef\xb7\x93"));// U+FDD4EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd4),"\xef\xb7\x94"));// U+FDD5EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd5),"\xef\xb7\x95"));// U+FDD6EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd6),"\xef\xb7\x96"));// U+FDD7EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd7),"\xef\xb7\x97"));// U+FDD8EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd8),"\xef\xb7\x98"));// U+FDD9EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdd9),"\xef\xb7\x99"));// U+FDDAEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdda),"\xef\xb7\x9a"));// U+FDDBEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfddb),"\xef\xb7\x9b"));// U+FDDCEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfddc),"\xef\xb7\x9c"));// U+FDDDEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfddd),"\xef\xb7\x9d"));// U+FDDEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdde),"\xef\xb7\x9e"));// U+FDDFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfddf),"\xef\xb7\x9f"));// U+FDE0EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde0),"\xef\xb7\xa0"));// U+FDE1EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde1),"\xef\xb7\xa1"));// U+FDE2EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde2),"\xef\xb7\xa2"));// U+FDE3EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde3),"\xef\xb7\xa3"));// U+FDE4EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde4),"\xef\xb7\xa4"));// U+FDE5EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde5),"\xef\xb7\xa5"));// U+FDE6EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde6),"\xef\xb7\xa6"));// U+FDE7EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde7),"\xef\xb7\xa7"));// U+FDE8EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde8),"\xef\xb7\xa8"));// U+FDE9EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfde9),"\xef\xb7\xa9"));// U+FDEAEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdea),"\xef\xb7\xaa"));// U+FDEBEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdeb),"\xef\xb7\xab"));// U+FDECEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdec),"\xef\xb7\xac"));// U+FDEDEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfded),"\xef\xb7\xad"));// U+FDEEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdee),"\xef\xb7\xae"));// U+FDEFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdef),"\xef\xb7\xaf"));// U+FDF0EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf0),"\xef\xb7\xb0"));// U+FDF1EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf1),"\xef\xb7\xb1"));// U+FDF2EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf2),"\xef\xb7\xb2"));// U+FDF3EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf3),"\xef\xb7\xb3"));// U+FDF4EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf4),"\xef\xb7\xb4"));// U+FDF5EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf5),"\xef\xb7\xb5"));// U+FDF6EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf6),"\xef\xb7\xb6"));// U+FDF7EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf7),"\xef\xb7\xb7"));// U+FDF8EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf8),"\xef\xb7\xb8"));// U+FDF9EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdf9),"\xef\xb7\xb9"));// U+FDFAEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdfa),"\xef\xb7\xba"));// U+FDFBEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdfb),"\xef\xb7\xbb"));// U+FDFCEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdfc),"\xef\xb7\xbc"));// U+FDFDEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdfd),"\xef\xb7\xbd"));// U+FDFEEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdfe),"\xef\xb7\xbe"));// U+FDFFEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0xfdff),"\xef\xb7\xbf"));}TEST(ConvertUTFTest, UTF8ToUTF32PartialLenient) {// U+0041 LATIN CAPITAL LETTER AEXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(conversionOK).withScalars(0x0041),"\x41", true));//// Sequences with one continuation byte missing//EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xc2", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xdf", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xe0\xa0", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xe0\xbf", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xe1\x80", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xec\xbf", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xed\x80", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xed\x9f", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xee\x80", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xef\xbf", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xf0\x90\x80", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xf0\xbf\xbf", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xf1\x80\x80", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xf3\xbf\xbf", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xf4\x80\x80", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted),"\xf4\x8f\xbf", true));EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer(sourceExhausted).withScalars(0x0041),"\x41\xc2", true));}
//===- llvm/unittest/Support/CompressionTest.cpp - Compression tests ------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for the Compression functions.////===----------------------------------------------------------------------===//#include "llvm/Support/Compression.h"#include "llvm/ADT/SmallString.h"#include "llvm/ADT/StringRef.h"#include "llvm/Config/config.h"#include "llvm/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::compression;namespace {#if LLVM_ENABLE_ZLIBstatic void testZlibCompression(StringRef Input, int Level) {SmallVector<uint8_t, 0> Compressed;SmallVector<uint8_t, 0> Uncompressed;zlib::compress(arrayRefFromStringRef(Input), Compressed, Level);// Check that uncompressed buffer is the same as original.Error E = zlib::uncompress(Compressed, Uncompressed, Input.size());consumeError(std::move(E));EXPECT_EQ(Input, toStringRef(Uncompressed));if (Input.size() > 0) {// Uncompression fails if expected length is too short.E = zlib::uncompress(Compressed, Uncompressed, Input.size() - 1);EXPECT_EQ("zlib error: Z_BUF_ERROR", llvm::toString(std::move(E)));}}TEST(CompressionTest, Zlib) {testZlibCompression("", zlib::DefaultCompression);testZlibCompression("hello, world!", zlib::NoCompression);testZlibCompression("hello, world!", zlib::BestSizeCompression);testZlibCompression("hello, world!", zlib::BestSpeedCompression);testZlibCompression("hello, world!", zlib::DefaultCompression);const size_t kSize = 1024;char BinaryData[kSize];for (size_t i = 0; i < kSize; ++i)BinaryData[i] = i & 255;StringRef BinaryDataStr(BinaryData, kSize);testZlibCompression(BinaryDataStr, zlib::NoCompression);testZlibCompression(BinaryDataStr, zlib::BestSizeCompression);testZlibCompression(BinaryDataStr, zlib::BestSpeedCompression);testZlibCompression(BinaryDataStr, zlib::DefaultCompression);}#endif#if LLVM_ENABLE_ZSTDstatic void testZstdCompression(StringRef Input, int Level) {SmallVector<uint8_t, 0> Compressed;SmallVector<uint8_t, 0> Uncompressed;zstd::compress(arrayRefFromStringRef(Input), Compressed, Level);// Check that uncompressed buffer is the same as original.Error E = zstd::uncompress(Compressed, Uncompressed, Input.size());consumeError(std::move(E));EXPECT_EQ(Input, toStringRef(Uncompressed));if (Input.size() > 0) {// Uncompression fails if expected length is too short.E = zstd::uncompress(Compressed, Uncompressed, Input.size() - 1);EXPECT_EQ("Destination buffer is too small", llvm::toString(std::move(E)));}}TEST(CompressionTest, Zstd) {testZstdCompression("", zstd::DefaultCompression);testZstdCompression("hello, world!", zstd::NoCompression);testZstdCompression("hello, world!", zstd::BestSizeCompression);testZstdCompression("hello, world!", zstd::BestSpeedCompression);testZstdCompression("hello, world!", zstd::DefaultCompression);const size_t kSize = 1024;char BinaryData[kSize];for (size_t i = 0; i < kSize; ++i)BinaryData[i] = i & 255;StringRef BinaryDataStr(BinaryData, kSize);testZstdCompression(BinaryDataStr, zstd::NoCompression);testZstdCompression(BinaryDataStr, zstd::BestSizeCompression);testZstdCompression(BinaryDataStr, zstd::BestSpeedCompression);testZstdCompression(BinaryDataStr, zstd::DefaultCompression);}#endif}
//===- llvm/unittest/Support/CommandLineTest.cpp - CommandLine tests ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/CommandLine.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SmallString.h"#include "llvm/ADT/StringRef.h"#include "llvm/ADT/Triple.h"#include "llvm/Config/config.h"#include "llvm/Support/Allocator.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Host.h"#include "llvm/Support/InitLLVM.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/Path.h"#include "llvm/Support/Program.h"#include "llvm/Support/StringSaver.h"#include "llvm/Support/VirtualFileSystem.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <fstream>#include <stdlib.h>#include <string>#include <tuple>using namespace llvm;using llvm::unittest::TempDir;using llvm::unittest::TempFile;namespace {MATCHER(StringEquality, "Checks if two char* are equal as strings") {return std::string(std::get<0>(arg)) == std::string(std::get<1>(arg));}class TempEnvVar {public:TempEnvVar(const char *name, const char *value): name(name) {const char *old_value = getenv(name);EXPECT_EQ(nullptr, old_value) << old_value;#if HAVE_SETENVsetenv(name, value, true);#endif}~TempEnvVar() {#if HAVE_SETENV// Assume setenv and unsetenv come together.unsetenv(name);#else(void)name; // Suppress -Wunused-private-field.#endif}private:const char *const name;};template <typename T, typename Base = cl::opt<T>>class StackOption : public Base {public:template <class... Ts>explicit StackOption(Ts &&... Ms) : Base(std::forward<Ts>(Ms)...) {}~StackOption() override { this->removeArgument(); }template <class DT> StackOption<T> &operator=(const DT &V) {Base::operator=(V);return *this;}};class StackSubCommand : public cl::SubCommand {public:StackSubCommand(StringRef Name,StringRef Description = StringRef()): SubCommand(Name, Description) {}StackSubCommand() : SubCommand() {}~StackSubCommand() { unregisterSubCommand(); }};cl::OptionCategory TestCategory("Test Options", "Description");TEST(CommandLineTest, ModifyExisitingOption) {StackOption<int> TestOption("test-option", cl::desc("old description"));static const char Description[] = "New description";static const char ArgString[] = "new-test-option";static const char ValueString[] = "Integer";StringMap<cl::Option *> &Map =cl::getRegisteredOptions(*cl::TopLevelSubCommand);ASSERT_EQ(Map.count("test-option"), 1u) << "Could not find option in map.";cl::Option *Retrieved = Map["test-option"];ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";ASSERT_NE(Retrieved->Categories.end(),find_if(Retrieved->Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &cl::getGeneralCategory();}))<< "Incorrect default option category.";Retrieved->addCategory(TestCategory);ASSERT_NE(Retrieved->Categories.end(),find_if(Retrieved->Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &TestCategory;}))<< "Failed to modify option's option category.";Retrieved->setDescription(Description);ASSERT_STREQ(Retrieved->HelpStr.data(), Description)<< "Changing option description failed.";Retrieved->setArgStr(ArgString);ASSERT_STREQ(ArgString, Retrieved->ArgStr.data())<< "Failed to modify option's Argument string.";Retrieved->setValueStr(ValueString);ASSERT_STREQ(Retrieved->ValueStr.data(), ValueString)<< "Failed to modify option's Value string.";Retrieved->setHiddenFlag(cl::Hidden);ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) <<"Failed to modify option's hidden flag.";}TEST(CommandLineTest, UseOptionCategory) {StackOption<int> TestOption2("test-option", cl::cat(TestCategory));ASSERT_NE(TestOption2.Categories.end(),find_if(TestOption2.Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &TestCategory;}))<< "Failed to assign Option Category.";}TEST(CommandLineTest, UseMultipleCategories) {StackOption<int> TestOption2("test-option2", cl::cat(TestCategory),cl::cat(cl::getGeneralCategory()),cl::cat(cl::getGeneralCategory()));// Make sure cl::getGeneralCategory() wasn't added twice.ASSERT_EQ(TestOption2.Categories.size(), 2U);ASSERT_NE(TestOption2.Categories.end(),find_if(TestOption2.Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &TestCategory;}))<< "Failed to assign Option Category.";ASSERT_NE(TestOption2.Categories.end(),find_if(TestOption2.Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &cl::getGeneralCategory();}))<< "Failed to assign General Category.";cl::OptionCategory AnotherCategory("Additional test Options", "Description");StackOption<int> TestOption("test-option", cl::cat(TestCategory),cl::cat(AnotherCategory));ASSERT_EQ(TestOption.Categories.end(),find_if(TestOption.Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &cl::getGeneralCategory();}))<< "Failed to remove General Category.";ASSERT_NE(TestOption.Categories.end(),find_if(TestOption.Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &TestCategory;}))<< "Failed to assign Option Category.";ASSERT_NE(TestOption.Categories.end(),find_if(TestOption.Categories,[&](const llvm::cl::OptionCategory *Cat) {return Cat == &AnotherCategory;}))<< "Failed to assign Another Category.";}typedef void ParserFunction(StringRef Source, StringSaver &Saver,SmallVectorImpl<const char *> &NewArgv,bool MarkEOLs);void testCommandLineTokenizer(ParserFunction *parse, StringRef Input,ArrayRef<const char *> Output,bool MarkEOLs = false) {SmallVector<const char *, 0> Actual;BumpPtrAllocator A;StringSaver Saver(A);parse(Input, Saver, Actual, MarkEOLs);EXPECT_EQ(Output.size(), Actual.size());for (unsigned I = 0, E = Actual.size(); I != E; ++I) {if (I < Output.size()) {EXPECT_STREQ(Output[I], Actual[I]);}}}TEST(CommandLineTest, TokenizeGNUCommandLine) {const char Input[] ="foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' -DFOO=bar\\(\\) ""foo\"bar\"baz C:\\\\src\\\\foo.cpp \"C:\\src\\foo.cpp\"";const char *const Output[] = {"foo bar", "foo bar", "foo bar", "foo\\bar","-DFOO=bar()", "foobarbaz", "C:\\src\\foo.cpp", "C:srcfoo.cpp"};testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output);}TEST(CommandLineTest, TokenizeWindowsCommandLine1) {const char Input[] =R"(a\b c\\d e\\"f g" h\"i j\\\"k "lmn" o pqr "st \"u" \v)";const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k","lmn", "o", "pqr", "st \"u", "\\v" };testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output);}TEST(CommandLineTest, TokenizeWindowsCommandLine2) {const char Input[] = "clang -c -DFOO=\"\"\"ABC\"\"\" x.cpp";const char *const Output[] = { "clang", "-c", "-DFOO=\"ABC\"", "x.cpp"};testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output);}TEST(CommandLineTest, TokenizeWindowsCommandLineQuotedLastArgument) {// Whitespace at the end of the command line doesn't cause an empty last wordconst char Input0[] = R"(a b c d )";const char *const Output0[] = {"a", "b", "c", "d"};testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input0, Output0);// But an explicit "" doesconst char Input1[] = R"(a b c d "")";const char *const Output1[] = {"a", "b", "c", "d", ""};testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input1, Output1);// An unterminated quoted string is also emitted as an argument word, empty// or notconst char Input2[] = R"(a b c d ")";const char *const Output2[] = {"a", "b", "c", "d", ""};testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input2, Output2);const char Input3[] = R"(a b c d "text)";const char *const Output3[] = {"a", "b", "c", "d", "text"};testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input3, Output3);}TEST(CommandLineTest, TokenizeWindowsCommandLineExeName) {const char Input1[] =R"("C:\Program Files\Whatever\"clang.exe z.c -DY=\"x\")";const char *const Output1[] = {"C:\\Program Files\\Whatever\\clang.exe","z.c", "-DY=\"x\""};testCommandLineTokenizer(cl::TokenizeWindowsCommandLineFull, Input1, Output1);const char Input2[] = "\"a\\\"b c\\\"d\n\"e\\\"f g\\\"h\n";const char *const Output2[] = {"a\\b", "c\"d", nullptr,"e\\f", "g\"h", nullptr};testCommandLineTokenizer(cl::TokenizeWindowsCommandLineFull, Input2, Output2,/*MarkEOLs=*/true);const char Input3[] = R"(\\server\share\subdir\clang.exe)";const char *const Output3[] = {"\\\\server\\share\\subdir\\clang.exe"};testCommandLineTokenizer(cl::TokenizeWindowsCommandLineFull, Input3, Output3);}TEST(CommandLineTest, TokenizeAndMarkEOLs) {// Clang uses EOL marking in response files to support options that consume// the rest of the arguments on the current line, but do not consume arguments// from subsequent lines. For example, given these rsp files contents:// /c /Zi /O2// /Oy- /link /debug /opt:ref// /Zc:ThreadsafeStatics-//// clang-cl needs to treat "/debug /opt:ref" as linker flags, and everything// else as compiler flags. The tokenizer inserts nullptr sentinels into the// output so that clang-cl can find the end of the current line.const char Input[] = "clang -Xclang foo\n\nfoo\"bar\"baz\n x.cpp\n";const char *const Output[] = {"clang", "-Xclang", "foo",nullptr, nullptr, "foobarbaz",nullptr, "x.cpp", nullptr};testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output,/*MarkEOLs=*/true);testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output,/*MarkEOLs=*/true);}TEST(CommandLineTest, TokenizeConfigFile1) {const char *Input = "\\";const char *const Output[] = { "\\" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile2) {const char *Input = "\\abc";const char *const Output[] = { "abc" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile3) {const char *Input = "abc\\";const char *const Output[] = { "abc\\" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile4) {const char *Input = "abc\\\n123";const char *const Output[] = { "abc123" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile5) {const char *Input = "abc\\\r\n123";const char *const Output[] = { "abc123" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile6) {const char *Input = "abc\\\n";const char *const Output[] = { "abc" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile7) {const char *Input = "abc\\\r\n";const char *const Output[] = { "abc" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile8) {SmallVector<const char *, 0> Actual;BumpPtrAllocator A;StringSaver Saver(A);cl::tokenizeConfigFile("\\\n", Saver, Actual, /*MarkEOLs=*/false);EXPECT_TRUE(Actual.empty());}TEST(CommandLineTest, TokenizeConfigFile9) {SmallVector<const char *, 0> Actual;BumpPtrAllocator A;StringSaver Saver(A);cl::tokenizeConfigFile("\\\r\n", Saver, Actual, /*MarkEOLs=*/false);EXPECT_TRUE(Actual.empty());}TEST(CommandLineTest, TokenizeConfigFile10) {const char *Input = "\\\nabc";const char *const Output[] = { "abc" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, TokenizeConfigFile11) {const char *Input = "\\\r\nabc";const char *const Output[] = { "abc" };testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);}TEST(CommandLineTest, AliasesWithArguments) {static const size_t ARGC = 3;const char *const Inputs[][ARGC] = {{ "-tool", "-actual=x", "-extra" },{ "-tool", "-actual", "x" },{ "-tool", "-alias=x", "-extra" },{ "-tool", "-alias", "x" }};for (size_t i = 0, e = array_lengthof(Inputs); i < e; ++i) {StackOption<std::string> Actual("actual");StackOption<bool> Extra("extra");StackOption<std::string> Input(cl::Positional);cl::alias Alias("alias", llvm::cl::aliasopt(Actual));cl::ParseCommandLineOptions(ARGC, Inputs[i]);EXPECT_EQ("x", Actual);EXPECT_EQ(0, Input.getNumOccurrences());Alias.removeArgument();}}void testAliasRequired(int argc, const char *const *argv) {StackOption<std::string> Option("option", cl::Required);cl::alias Alias("o", llvm::cl::aliasopt(Option));cl::ParseCommandLineOptions(argc, argv);EXPECT_EQ("x", Option);EXPECT_EQ(1, Option.getNumOccurrences());Alias.removeArgument();}TEST(CommandLineTest, AliasRequired) {const char *opts1[] = { "-tool", "-option=x" };const char *opts2[] = { "-tool", "-o", "x" };testAliasRequired(array_lengthof(opts1), opts1);testAliasRequired(array_lengthof(opts2), opts2);}TEST(CommandLineTest, HideUnrelatedOptions) {StackOption<int> TestOption1("hide-option-1");StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));cl::HideUnrelatedOptions(TestCategory);ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())<< "Failed to hide extra option.";ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())<< "Hid extra option that should be visable.";StringMap<cl::Option *> &Map =cl::getRegisteredOptions(*cl::TopLevelSubCommand);ASSERT_TRUE(Map.count("help") == (size_t)0 ||cl::NotHidden == Map["help"]->getOptionHiddenFlag())<< "Hid default option that should be visable.";}cl::OptionCategory TestCategory2("Test Options set 2", "Description");TEST(CommandLineTest, HideUnrelatedOptionsMulti) {StackOption<int> TestOption1("multi-hide-option-1");StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory));StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2));const cl::OptionCategory *VisibleCategories[] = {&TestCategory,&TestCategory2};cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())<< "Failed to hide extra option.";ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())<< "Hid extra option that should be visable.";ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())<< "Hid extra option that should be visable.";StringMap<cl::Option *> &Map =cl::getRegisteredOptions(*cl::TopLevelSubCommand);ASSERT_TRUE(Map.count("help") == (size_t)0 ||cl::NotHidden == Map["help"]->getOptionHiddenFlag())<< "Hid default option that should be visable.";}TEST(CommandLineTest, SetMultiValues) {StackOption<int> Option("option");const char *args[] = {"prog", "-option=1", "-option=2"};EXPECT_TRUE(cl::ParseCommandLineOptions(array_lengthof(args), args,StringRef(), &llvm::nulls()));EXPECT_EQ(Option, 2);}TEST(CommandLineTest, SetValueInSubcategories) {cl::ResetCommandLineParser();StackSubCommand SC1("sc1", "First subcommand");StackSubCommand SC2("sc2", "Second subcommand");StackOption<bool> TopLevelOpt("top-level", cl::init(false));StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));EXPECT_FALSE(TopLevelOpt);EXPECT_FALSE(SC1Opt);EXPECT_FALSE(SC2Opt);const char *args[] = {"prog", "-top-level"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));EXPECT_TRUE(TopLevelOpt);EXPECT_FALSE(SC1Opt);EXPECT_FALSE(SC2Opt);TopLevelOpt = false;cl::ResetAllOptionOccurrences();EXPECT_FALSE(TopLevelOpt);EXPECT_FALSE(SC1Opt);EXPECT_FALSE(SC2Opt);const char *args2[] = {"prog", "sc1", "-sc1"};EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));EXPECT_FALSE(TopLevelOpt);EXPECT_TRUE(SC1Opt);EXPECT_FALSE(SC2Opt);SC1Opt = false;cl::ResetAllOptionOccurrences();EXPECT_FALSE(TopLevelOpt);EXPECT_FALSE(SC1Opt);EXPECT_FALSE(SC2Opt);const char *args3[] = {"prog", "sc2", "-sc2"};EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &llvm::nulls()));EXPECT_FALSE(TopLevelOpt);EXPECT_FALSE(SC1Opt);EXPECT_TRUE(SC2Opt);}TEST(CommandLineTest, LookupFailsInWrongSubCommand) {cl::ResetCommandLineParser();StackSubCommand SC1("sc1", "First subcommand");StackSubCommand SC2("sc2", "Second subcommand");StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));std::string Errs;raw_string_ostream OS(Errs);const char *args[] = {"prog", "sc1", "-sc2"};EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));OS.flush();EXPECT_FALSE(Errs.empty());}TEST(CommandLineTest, AddToAllSubCommands) {cl::ResetCommandLineParser();StackSubCommand SC1("sc1", "First subcommand");StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands),cl::init(false));StackSubCommand SC2("sc2", "Second subcommand");const char *args[] = {"prog", "-everywhere"};const char *args2[] = {"prog", "sc1", "-everywhere"};const char *args3[] = {"prog", "sc2", "-everywhere"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_FALSE(AllOpt);EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));EXPECT_TRUE(AllOpt);AllOpt = false;cl::ResetAllOptionOccurrences();EXPECT_FALSE(AllOpt);EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS));EXPECT_TRUE(AllOpt);AllOpt = false;cl::ResetAllOptionOccurrences();EXPECT_FALSE(AllOpt);EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS));EXPECT_TRUE(AllOpt);// Since all parsing succeeded, the error message should be empty.OS.flush();EXPECT_TRUE(Errs.empty());}TEST(CommandLineTest, ReparseCommandLineOptions) {cl::ResetCommandLineParser();StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand),cl::init(false));const char *args[] = {"prog", "-top-level"};EXPECT_FALSE(TopLevelOpt);EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));EXPECT_TRUE(TopLevelOpt);TopLevelOpt = false;cl::ResetAllOptionOccurrences();EXPECT_FALSE(TopLevelOpt);EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));EXPECT_TRUE(TopLevelOpt);}TEST(CommandLineTest, RemoveFromRegularSubCommand) {cl::ResetCommandLineParser();StackSubCommand SC("sc", "Subcommand");StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false));StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false));const char *args[] = {"prog", "sc", "-remove-option"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_FALSE(RemoveOption);EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));EXPECT_TRUE(RemoveOption);OS.flush();EXPECT_TRUE(Errs.empty());RemoveOption.removeArgument();cl::ResetAllOptionOccurrences();EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));OS.flush();EXPECT_FALSE(Errs.empty());}TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {cl::ResetCommandLineParser();StackOption<bool> TopLevelRemove("top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false));StackOption<bool> TopLevelKeep("top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false));const char *args[] = {"prog", "-top-level-remove"};EXPECT_FALSE(TopLevelRemove);EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));EXPECT_TRUE(TopLevelRemove);TopLevelRemove.removeArgument();cl::ResetAllOptionOccurrences();EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));}TEST(CommandLineTest, RemoveFromAllSubCommands) {cl::ResetCommandLineParser();StackSubCommand SC1("sc1", "First Subcommand");StackSubCommand SC2("sc2", "Second Subcommand");StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands),cl::init(false));StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands),cl::init(false));const char *args0[] = {"prog", "-remove-option"};const char *args1[] = {"prog", "sc1", "-remove-option"};const char *args2[] = {"prog", "sc2", "-remove-option"};// It should work for all subcommands including the top-level.EXPECT_FALSE(RemoveOption);EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));EXPECT_TRUE(RemoveOption);RemoveOption = false;cl::ResetAllOptionOccurrences();EXPECT_FALSE(RemoveOption);EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));EXPECT_TRUE(RemoveOption);RemoveOption = false;cl::ResetAllOptionOccurrences();EXPECT_FALSE(RemoveOption);EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));EXPECT_TRUE(RemoveOption);RemoveOption.removeArgument();// It should not work for any subcommands including the top-level.cl::ResetAllOptionOccurrences();EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));cl::ResetAllOptionOccurrences();EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));cl::ResetAllOptionOccurrences();EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));}TEST(CommandLineTest, GetRegisteredSubcommands) {cl::ResetCommandLineParser();StackSubCommand SC1("sc1", "First Subcommand");StackOption<bool> Opt1("opt1", cl::sub(SC1), cl::init(false));StackSubCommand SC2("sc2", "Second subcommand");StackOption<bool> Opt2("opt2", cl::sub(SC2), cl::init(false));const char *args0[] = {"prog", "sc1"};const char *args1[] = {"prog", "sc2"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));EXPECT_FALSE(Opt1);EXPECT_FALSE(Opt2);for (auto *S : cl::getRegisteredSubcommands()) {if (*S) {EXPECT_EQ("sc1", S->getName());}}cl::ResetAllOptionOccurrences();EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls()));EXPECT_FALSE(Opt1);EXPECT_FALSE(Opt2);for (auto *S : cl::getRegisteredSubcommands()) {if (*S) {EXPECT_EQ("sc2", S->getName());}}}TEST(CommandLineTest, DefaultOptions) {cl::ResetCommandLineParser();StackOption<std::string> Bar("bar", cl::sub(*cl::AllSubCommands),cl::DefaultOption);StackOption<std::string, cl::alias> Bar_Alias("b", cl::desc("Alias for -bar"), cl::aliasopt(Bar), cl::DefaultOption);StackOption<bool> Foo("foo", cl::init(false), cl::sub(*cl::AllSubCommands),cl::DefaultOption);StackOption<bool, cl::alias> Foo_Alias("f", cl::desc("Alias for -foo"),cl::aliasopt(Foo), cl::DefaultOption);StackSubCommand SC1("sc1", "First Subcommand");// Override "-b" and change type in sc1 SubCommand.StackOption<bool> SC1_B("b", cl::sub(SC1), cl::init(false));StackSubCommand SC2("sc2", "Second subcommand");// Override "-foo" and change type in sc2 SubCommand. Note that this does not// affect "-f" alias, which continues to work correctly.StackOption<std::string> SC2_Foo("foo", cl::sub(SC2));const char *args0[] = {"prog", "-b", "args0 bar string", "-f"};EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args0) / sizeof(char *), args0,StringRef(), &llvm::nulls()));EXPECT_EQ(Bar, "args0 bar string");EXPECT_TRUE(Foo);EXPECT_FALSE(SC1_B);EXPECT_TRUE(SC2_Foo.empty());cl::ResetAllOptionOccurrences();const char *args1[] = {"prog", "sc1", "-b", "-bar", "args1 bar string", "-f"};EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args1) / sizeof(char *), args1,StringRef(), &llvm::nulls()));EXPECT_EQ(Bar, "args1 bar string");EXPECT_TRUE(Foo);EXPECT_TRUE(SC1_B);EXPECT_TRUE(SC2_Foo.empty());for (auto *S : cl::getRegisteredSubcommands()) {if (*S) {EXPECT_EQ("sc1", S->getName());}}cl::ResetAllOptionOccurrences();const char *args2[] = {"prog", "sc2", "-b", "args2 bar string","-f", "-foo", "foo string"};EXPECT_TRUE(cl::ParseCommandLineOptions(sizeof(args2) / sizeof(char *), args2,StringRef(), &llvm::nulls()));EXPECT_EQ(Bar, "args2 bar string");EXPECT_TRUE(Foo);EXPECT_FALSE(SC1_B);EXPECT_EQ(SC2_Foo, "foo string");for (auto *S : cl::getRegisteredSubcommands()) {if (*S) {EXPECT_EQ("sc2", S->getName());}}cl::ResetCommandLineParser();}TEST(CommandLineTest, ArgumentLimit) {std::string args(32 * 4096, 'a');EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data()));std::string args2(256, 'a');EXPECT_TRUE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args2.data()));}TEST(CommandLineTest, ArgumentLimitWindows) {if (!Triple(sys::getProcessTriple()).isOSWindows())GTEST_SKIP();// We use 32000 as a limit for command line length. Program name ('cl'),// separating spaces and termination null character occupy 5 symbols.std::string long_arg(32000 - 5, 'b');EXPECT_TRUE(llvm::sys::commandLineFitsWithinSystemLimits("cl", long_arg.data()));long_arg += 'b';EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", long_arg.data()));}TEST(CommandLineTest, ResponseFileWindows) {if (!Triple(sys::getProcessTriple()).isOSWindows())GTEST_SKIP();StackOption<std::string, cl::list<std::string>> InputFilenames(cl::Positional, cl::desc("<input files>"));StackOption<bool> TopLevelOpt("top-level", cl::init(false));// Create response file.TempFile ResponseFile("resp-", ".txt","-top-level\npath\\dir\\file1\npath/dir/file2",/*Unique*/ true);llvm::SmallString<128> RspOpt;RspOpt.append(1, '@');RspOpt.append(ResponseFile.path());const char *args[] = {"prog", RspOpt.c_str()};EXPECT_FALSE(TopLevelOpt);EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));EXPECT_TRUE(TopLevelOpt);EXPECT_EQ(InputFilenames[0], "path\\dir\\file1");EXPECT_EQ(InputFilenames[1], "path/dir/file2");}TEST(CommandLineTest, ResponseFiles) {vfs::InMemoryFileSystem FS;#ifdef _WIN32const char *TestRoot = "C:\\";#elseconst char *TestRoot = "/";#endifFS.setCurrentWorkingDirectory(TestRoot);// Create included response file of first level.llvm::StringRef IncludedFileName = "resp1";FS.addFile(IncludedFileName, 0,llvm::MemoryBuffer::getMemBuffer("-option_1 -option_2\n""@incdir/resp2\n""-option_3=abcd\n""@incdir/resp3\n""-option_4=efjk\n"));// Directory for included file.llvm::StringRef IncDir = "incdir";// Create included response file of second level.llvm::SmallString<128> IncludedFileName2;llvm::sys::path::append(IncludedFileName2, IncDir, "resp2");FS.addFile(IncludedFileName2, 0,MemoryBuffer::getMemBuffer("-option_21 -option_22\n""-option_23=abcd\n"));// Create second included response file of second level.llvm::SmallString<128> IncludedFileName3;llvm::sys::path::append(IncludedFileName3, IncDir, "resp3");FS.addFile(IncludedFileName3, 0,MemoryBuffer::getMemBuffer("-option_31 -option_32\n""-option_33=abcd\n"));// Prepare 'file' with reference to response file.SmallString<128> IncRef;IncRef.append(1, '@');IncRef.append(IncludedFileName);llvm::SmallVector<const char *, 4> Argv = {"test/test", "-flag_1",IncRef.c_str(), "-flag_2"};// Expand response files.llvm::BumpPtrAllocator A;llvm::StringSaver Saver(A);ASSERT_TRUE(llvm::cl::ExpandResponseFiles(Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, true, false,/*CurrentDir=*/StringRef(TestRoot), FS));EXPECT_THAT(Argv, testing::Pointwise(StringEquality(),{"test/test", "-flag_1", "-option_1", "-option_2","-option_21", "-option_22", "-option_23=abcd","-option_3=abcd", "-option_31", "-option_32","-option_33=abcd", "-option_4=efjk", "-flag_2"}));}TEST(CommandLineTest, RecursiveResponseFiles) {vfs::InMemoryFileSystem FS;#ifdef _WIN32const char *TestRoot = "C:\\";#elseconst char *TestRoot = "/";#endifFS.setCurrentWorkingDirectory(TestRoot);StringRef SelfFilePath = "self.rsp";std::string SelfFileRef = ("@" + SelfFilePath).str();StringRef NestedFilePath = "nested.rsp";std::string NestedFileRef = ("@" + NestedFilePath).str();StringRef FlagFilePath = "flag.rsp";std::string FlagFileRef = ("@" + FlagFilePath).str();std::string SelfFileContents;raw_string_ostream SelfFile(SelfFileContents);SelfFile << "-option_1\n";SelfFile << FlagFileRef << "\n";SelfFile << NestedFileRef << "\n";SelfFile << SelfFileRef << "\n";FS.addFile(SelfFilePath, 0, MemoryBuffer::getMemBuffer(SelfFile.str()));std::string NestedFileContents;raw_string_ostream NestedFile(NestedFileContents);NestedFile << "-option_2\n";NestedFile << FlagFileRef << "\n";NestedFile << SelfFileRef << "\n";NestedFile << NestedFileRef << "\n";FS.addFile(NestedFilePath, 0, MemoryBuffer::getMemBuffer(NestedFile.str()));std::string FlagFileContents;raw_string_ostream FlagFile(FlagFileContents);FlagFile << "-option_x\n";FS.addFile(FlagFilePath, 0, MemoryBuffer::getMemBuffer(FlagFile.str()));// Ensure:// Recursive expansion terminates// Recursive files never expand// Non-recursive repeats are allowedSmallVector<const char *, 4> Argv = {"test/test", SelfFileRef.c_str(),"-option_3"};BumpPtrAllocator A;StringSaver Saver(A);#ifdef _WIN32cl::TokenizerCallback Tokenizer = cl::TokenizeWindowsCommandLine;#elsecl::TokenizerCallback Tokenizer = cl::TokenizeGNUCommandLine;#endifASSERT_FALSE(cl::ExpandResponseFiles(Saver, Tokenizer, Argv, false, false, false,/*CurrentDir=*/llvm::StringRef(TestRoot), FS));EXPECT_THAT(Argv,testing::Pointwise(StringEquality(),{"test/test", "-option_1", "-option_x","-option_2", "-option_x", SelfFileRef.c_str(),NestedFileRef.c_str(), SelfFileRef.c_str(),"-option_3"}));}TEST(CommandLineTest, ResponseFilesAtArguments) {vfs::InMemoryFileSystem FS;#ifdef _WIN32const char *TestRoot = "C:\\";#elseconst char *TestRoot = "/";#endifFS.setCurrentWorkingDirectory(TestRoot);StringRef ResponseFilePath = "test.rsp";std::string ResponseFileContents;raw_string_ostream ResponseFile(ResponseFileContents);ResponseFile << "-foo" << "\n";ResponseFile << "-bar" << "\n";FS.addFile(ResponseFilePath, 0,MemoryBuffer::getMemBuffer(ResponseFile.str()));// Ensure we expand rsp files after lots of non-rsp arguments starting with @.constexpr size_t NON_RSP_AT_ARGS = 64;SmallVector<const char *, 4> Argv = {"test/test"};Argv.append(NON_RSP_AT_ARGS, "@non_rsp_at_arg");std::string ResponseFileRef = ("@" + ResponseFilePath).str();Argv.push_back(ResponseFileRef.c_str());BumpPtrAllocator A;StringSaver Saver(A);ASSERT_FALSE(cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv,false, false, false,/*CurrentDir=*/StringRef(TestRoot), FS));// ASSERT instead of EXPECT to prevent potential out-of-bounds access.ASSERT_EQ(Argv.size(), 1 + NON_RSP_AT_ARGS + 2);size_t i = 0;EXPECT_STREQ(Argv[i++], "test/test");for (; i < 1 + NON_RSP_AT_ARGS; ++i)EXPECT_STREQ(Argv[i], "@non_rsp_at_arg");EXPECT_STREQ(Argv[i++], "-foo");EXPECT_STREQ(Argv[i++], "-bar");}TEST(CommandLineTest, ResponseFileRelativePath) {vfs::InMemoryFileSystem FS;#ifdef _WIN32const char *TestRoot = "C:\\";#elseconst char *TestRoot = "//net";#endifFS.setCurrentWorkingDirectory(TestRoot);StringRef OuterFile = "dir/outer.rsp";StringRef OuterFileContents = "@inner.rsp";FS.addFile(OuterFile, 0, MemoryBuffer::getMemBuffer(OuterFileContents));StringRef InnerFile = "dir/inner.rsp";StringRef InnerFileContents = "-flag";FS.addFile(InnerFile, 0, MemoryBuffer::getMemBuffer(InnerFileContents));SmallVector<const char *, 2> Argv = {"test/test", "@dir/outer.rsp"};BumpPtrAllocator A;StringSaver Saver(A);ASSERT_TRUE(cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv,false, true, false,/*CurrentDir=*/StringRef(TestRoot), FS));EXPECT_THAT(Argv,testing::Pointwise(StringEquality(), {"test/test", "-flag"}));}TEST(CommandLineTest, ResponseFileEOLs) {vfs::InMemoryFileSystem FS;#ifdef _WIN32const char *TestRoot = "C:\\";#elseconst char *TestRoot = "//net";#endifFS.setCurrentWorkingDirectory(TestRoot);FS.addFile("eols.rsp", 0,MemoryBuffer::getMemBuffer("-Xclang -Wno-whatever\n input.cpp"));SmallVector<const char *, 2> Argv = {"clang", "@eols.rsp"};BumpPtrAllocator A;StringSaver Saver(A);ASSERT_TRUE(cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine,Argv, true, true, false,/*CurrentDir=*/StringRef(TestRoot), FS));const char *Expected[] = {"clang", "-Xclang", "-Wno-whatever", nullptr,"input.cpp"};ASSERT_EQ(array_lengthof(Expected), Argv.size());for (size_t I = 0, E = array_lengthof(Expected); I < E; ++I) {if (Expected[I] == nullptr) {ASSERT_EQ(Argv[I], nullptr);} else {ASSERT_STREQ(Expected[I], Argv[I]);}}}TEST(CommandLineTest, SetDefautValue) {cl::ResetCommandLineParser();StackOption<std::string> Opt1("opt1", cl::init("true"));StackOption<bool> Opt2("opt2", cl::init(true));cl::alias Alias("alias", llvm::cl::aliasopt(Opt2));StackOption<int> Opt3("opt3", cl::init(3));const char *args[] = {"prog", "-opt1=false", "-opt2", "-opt3"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));EXPECT_EQ(Opt1, "false");EXPECT_TRUE(Opt2);EXPECT_EQ(Opt3, 3);Opt2 = false;Opt3 = 1;cl::ResetAllOptionOccurrences();for (auto &OM : cl::getRegisteredOptions(*cl::TopLevelSubCommand)) {cl::Option *O = OM.second;if (O->ArgStr == "opt2") {continue;}O->setDefault();}EXPECT_EQ(Opt1, "true");EXPECT_TRUE(Opt2);EXPECT_EQ(Opt3, 3);Alias.removeArgument();}TEST(CommandLineTest, ReadConfigFile) {llvm::SmallVector<const char *, 1> Argv;TempDir TestDir("unittest", /*Unique*/ true);TempDir TestSubDir(TestDir.path("subdir"), /*Unique*/ false);llvm::SmallString<128> TestCfg = TestDir.path("foo");TempFile ConfigFile(TestCfg, "","# Comment\n""-option_1\n""-option_2=<CFGDIR>/dir1\n""-option_3=<CFGDIR>\n""-option_4 <CFGDIR>\n""-option_5=<CFG\\\n""DIR>\n""-option_6=<CFGDIR>/dir1,<CFGDIR>/dir2\n""@subconfig\n""-option_11=abcd\n""-option_12=\\\n""cdef\n");llvm::SmallString<128> TestCfg2 = TestDir.path("subconfig");TempFile ConfigFile2(TestCfg2, "","-option_7\n""-option_8=<CFGDIR>/dir2\n""@subdir/subfoo\n""\n"" # comment\n");llvm::SmallString<128> TestCfg3 = TestSubDir.path("subfoo");TempFile ConfigFile3(TestCfg3, "","-option_9=<CFGDIR>/dir3\n""@<CFGDIR>/subfoo2\n");llvm::SmallString<128> TestCfg4 = TestSubDir.path("subfoo2");TempFile ConfigFile4(TestCfg4, "", "-option_10\n");// Make sure the current directory is not the directory where config files// resides. In this case the code that expands response files will not find// 'subconfig' unless it resolves nested inclusions relative to the including// file.llvm::SmallString<128> CurrDir;std::error_code EC = llvm::sys::fs::current_path(CurrDir);EXPECT_TRUE(!EC);EXPECT_NE(CurrDir.str(), TestDir.path());llvm::BumpPtrAllocator A;llvm::StringSaver Saver(A);bool Result = llvm::cl::readConfigFile(ConfigFile.path(), Saver, Argv);EXPECT_TRUE(Result);EXPECT_EQ(Argv.size(), 13U);EXPECT_STREQ(Argv[0], "-option_1");EXPECT_STREQ(Argv[1],("-option_2=" + TestDir.path() + "/dir1").str().c_str());EXPECT_STREQ(Argv[2], ("-option_3=" + TestDir.path()).str().c_str());EXPECT_STREQ(Argv[3], "-option_4");EXPECT_STREQ(Argv[4], TestDir.path().str().c_str());EXPECT_STREQ(Argv[5], ("-option_5=" + TestDir.path()).str().c_str());EXPECT_STREQ(Argv[6], ("-option_6=" + TestDir.path() + "/dir1," +TestDir.path() + "/dir2").str().c_str());EXPECT_STREQ(Argv[7], "-option_7");EXPECT_STREQ(Argv[8],("-option_8=" + TestDir.path() + "/dir2").str().c_str());EXPECT_STREQ(Argv[9],("-option_9=" + TestSubDir.path() + "/dir3").str().c_str());EXPECT_STREQ(Argv[10], "-option_10");EXPECT_STREQ(Argv[11], "-option_11=abcd");EXPECT_STREQ(Argv[12], "-option_12=cdef");}TEST(CommandLineTest, PositionalEatArgsError) {cl::ResetCommandLineParser();StackOption<std::string, cl::list<std::string>> PosEatArgs("positional-eat-args", cl::Positional, cl::desc("<arguments>..."),cl::PositionalEatsArgs);StackOption<std::string, cl::list<std::string>> PosEatArgs2("positional-eat-args2", cl::Positional, cl::desc("Some strings"),cl::PositionalEatsArgs);const char *args[] = {"prog", "-positional-eat-args=XXXX"};const char *args2[] = {"prog", "-positional-eat-args=XXXX", "-foo"};const char *args3[] = {"prog", "-positional-eat-args", "-foo"};const char *args4[] = {"prog", "-positional-eat-args","-foo", "-positional-eat-args2","-bar", "foo"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); OS.flush();EXPECT_FALSE(Errs.empty()); Errs.clear();EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); OS.flush();EXPECT_FALSE(Errs.empty()); Errs.clear();EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); OS.flush();EXPECT_TRUE(Errs.empty()); Errs.clear();cl::ResetAllOptionOccurrences();EXPECT_TRUE(cl::ParseCommandLineOptions(6, args4, StringRef(), &OS)); OS.flush();EXPECT_EQ(PosEatArgs.size(), 1u);EXPECT_EQ(PosEatArgs2.size(), 2u);EXPECT_TRUE(Errs.empty());}#ifdef _WIN32void checkSeparators(StringRef Path) {char UndesiredSeparator = sys::path::get_separator()[0] == '/' ? '\\' : '/';ASSERT_EQ(Path.find(UndesiredSeparator), StringRef::npos);}TEST(CommandLineTest, GetCommandLineArguments) {int argc = __argc;char **argv = __argv;// GetCommandLineArguments is called in InitLLVM.llvm::InitLLVM X(argc, argv);EXPECT_EQ(llvm::sys::path::is_absolute(argv[0]),llvm::sys::path::is_absolute(__argv[0]));checkSeparators(argv[0]);EXPECT_TRUE(llvm::sys::path::filename(argv[0]).equals_insensitive("supporttests.exe"))<< "Filename of test executable is "<< llvm::sys::path::filename(argv[0]);}#endifclass OutputRedirector {public:OutputRedirector(int RedirectFD): RedirectFD(RedirectFD), OldFD(dup(RedirectFD)) {if (OldFD == -1 ||sys::fs::createTemporaryFile("unittest-redirect", "", NewFD,FilePath) ||dup2(NewFD, RedirectFD) == -1)Valid = false;}~OutputRedirector() {dup2(OldFD, RedirectFD);close(OldFD);close(NewFD);}SmallVector<char, 128> FilePath;bool Valid = true;private:int RedirectFD;int OldFD;int NewFD;};struct AutoDeleteFile {SmallVector<char, 128> FilePath;~AutoDeleteFile() {if (!FilePath.empty())sys::fs::remove(std::string(FilePath.data(), FilePath.size()));}};class PrintOptionInfoTest : public ::testing::Test {public:// Return std::string because the output of a failing EXPECT check is// unreadable for StringRef. It also avoids any lifetime issues.template <typename... Ts> std::string runTest(Ts... OptionAttributes) {outs().flush(); // flush any output from previous testsAutoDeleteFile File;{OutputRedirector Stdout(fileno(stdout));if (!Stdout.Valid)return "";File.FilePath = Stdout.FilePath;StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText),OptionAttributes...);printOptionInfo(TestOption, 26);outs().flush();}auto Buffer = MemoryBuffer::getFile(File.FilePath);if (!Buffer)return "";return Buffer->get()->getBuffer().str();}enum class OptionValue { Val };const StringRef Opt = "some-option";const StringRef HelpText = "some help";private:// This is a workaround for cl::Option sub-classes having their// printOptionInfo functions private.void printOptionInfo(const cl::Option &O, size_t Width) {O.printOptionInfo(Width);}};TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithoutSentinel) {std::string Output =runTest(cl::ValueOptional,cl::values(clEnumValN(OptionValue::Val, "v1", "desc1")));// clang-format offEXPECT_EQ(Output, (" --" + Opt + "=<value> - " + HelpText + "\n"" =v1 - desc1\n").str());// clang-format on}TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinel) {std::string Output = runTest(cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"),clEnumValN(OptionValue::Val, "", "")));// clang-format offEXPECT_EQ(Output,(" --" + Opt + " - " + HelpText + "\n"" --" + Opt + "=<value> - " + HelpText + "\n"" =v1 - desc1\n").str());// clang-format on}TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinelWithHelp) {std::string Output = runTest(cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"),clEnumValN(OptionValue::Val, "", "desc2")));// clang-format offEXPECT_EQ(Output, (" --" + Opt + " - " + HelpText + "\n"" --" + Opt + "=<value> - " + HelpText + "\n"" =v1 - desc1\n"" =<empty> - desc2\n").str());// clang-format on}TEST_F(PrintOptionInfoTest, PrintOptionInfoValueRequiredWithEmptyValueName) {std::string Output = runTest(cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"),clEnumValN(OptionValue::Val, "", "")));// clang-format offEXPECT_EQ(Output, (" --" + Opt + "=<value> - " + HelpText + "\n"" =v1 - desc1\n"" =<empty>\n").str());// clang-format on}TEST_F(PrintOptionInfoTest, PrintOptionInfoEmptyValueDescription) {std::string Output = runTest(cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", "")));// clang-format offEXPECT_EQ(Output,(" --" + Opt + "=<value> - " + HelpText + "\n"" =v1\n").str());// clang-format on}TEST_F(PrintOptionInfoTest, PrintOptionInfoMultilineValueDescription) {std::string Output =runTest(cl::ValueRequired,cl::values(clEnumValN(OptionValue::Val, "v1","This is the first enum value\n""which has a really long description\n""thus it is multi-line."),clEnumValN(OptionValue::Val, "","This is an unnamed enum value option\n""Should be indented as well")));// clang-format offEXPECT_EQ(Output,(" --" + Opt + "=<value> - " + HelpText + "\n"" =v1 - This is the first enum value\n"" which has a really long description\n"" thus it is multi-line.\n"" =<empty> - This is an unnamed enum value option\n"" Should be indented as well\n").str());// clang-format on}class GetOptionWidthTest : public ::testing::Test {public:enum class OptionValue { Val };template <typename... Ts>size_t runTest(StringRef ArgName, Ts... OptionAttributes) {StackOption<OptionValue> TestOption(ArgName, cl::desc("some help"),OptionAttributes...);return getOptionWidth(TestOption);}private:// This is a workaround for cl::Option sub-classes having their// printOptionInfo// functions private.size_t getOptionWidth(const cl::Option &O) { return O.getOptionWidth(); }};TEST_F(GetOptionWidthTest, GetOptionWidthArgNameLonger) {StringRef ArgName("a-long-argument-name");size_t ExpectedStrSize = (" --" + ArgName + "=<value> - ").str().size();EXPECT_EQ(runTest(ArgName, cl::values(clEnumValN(OptionValue::Val, "v", "help"))),ExpectedStrSize);}TEST_F(GetOptionWidthTest, GetOptionWidthFirstOptionNameLonger) {StringRef OptName("a-long-option-name");size_t ExpectedStrSize = (" =" + OptName + " - ").str().size();EXPECT_EQ(runTest("a", cl::values(clEnumValN(OptionValue::Val, OptName, "help"),clEnumValN(OptionValue::Val, "b", "help"))),ExpectedStrSize);}TEST_F(GetOptionWidthTest, GetOptionWidthSecondOptionNameLonger) {StringRef OptName("a-long-option-name");size_t ExpectedStrSize = (" =" + OptName + " - ").str().size();EXPECT_EQ(runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"),clEnumValN(OptionValue::Val, OptName, "help"))),ExpectedStrSize);}TEST_F(GetOptionWidthTest, GetOptionWidthEmptyOptionNameLonger) {size_t ExpectedStrSize = StringRef(" =<empty> - ").size();// The length of a=<value> (including indentation) is actually the same as the// =<empty> string, so it is impossible to distinguish via testing the case// where the empty string is picked from where the option name is picked.EXPECT_EQ(runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"),clEnumValN(OptionValue::Val, "", "help"))),ExpectedStrSize);}TEST_F(GetOptionWidthTest,GetOptionWidthValueOptionalEmptyOptionWithNoDescription) {StringRef ArgName("a");// The length of a=<value> (including indentation) is actually the same as the// =<empty> string, so it is impossible to distinguish via testing the case// where the empty string is ignored from where it is not ignored.// The dash will not actually be printed, but the space it would take up is// included to ensure a consistent column width.size_t ExpectedStrSize = (" -" + ArgName + "=<value> - ").str().size();EXPECT_EQ(runTest(ArgName, cl::ValueOptional,cl::values(clEnumValN(OptionValue::Val, "value", "help"),clEnumValN(OptionValue::Val, "", ""))),ExpectedStrSize);}TEST_F(GetOptionWidthTest,GetOptionWidthValueRequiredEmptyOptionWithNoDescription) {// The length of a=<value> (including indentation) is actually the same as the// =<empty> string, so it is impossible to distinguish via testing the case// where the empty string is picked from where the option name is pickedsize_t ExpectedStrSize = StringRef(" =<empty> - ").size();EXPECT_EQ(runTest("a", cl::ValueRequired,cl::values(clEnumValN(OptionValue::Val, "value", "help"),clEnumValN(OptionValue::Val, "", ""))),ExpectedStrSize);}TEST(CommandLineTest, PrefixOptions) {cl::ResetCommandLineParser();StackOption<std::string, cl::list<std::string>> IncludeDirs("I", cl::Prefix, cl::desc("Declare an include directory"));// Test non-prefixed variant works with cl::Prefix options.EXPECT_TRUE(IncludeDirs.empty());const char *args[] = {"prog", "-I=/usr/include"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));EXPECT_EQ(IncludeDirs.size(), 1u);EXPECT_EQ(IncludeDirs.front().compare("/usr/include"), 0);IncludeDirs.erase(IncludeDirs.begin());cl::ResetAllOptionOccurrences();// Test non-prefixed variant works with cl::Prefix options when value is// passed in following argument.EXPECT_TRUE(IncludeDirs.empty());const char *args2[] = {"prog", "-I", "/usr/include"};EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));EXPECT_EQ(IncludeDirs.size(), 1u);EXPECT_EQ(IncludeDirs.front().compare("/usr/include"), 0);IncludeDirs.erase(IncludeDirs.begin());cl::ResetAllOptionOccurrences();// Test prefixed variant works with cl::Prefix options.EXPECT_TRUE(IncludeDirs.empty());const char *args3[] = {"prog", "-I/usr/include"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));EXPECT_EQ(IncludeDirs.size(), 1u);EXPECT_EQ(IncludeDirs.front().compare("/usr/include"), 0);StackOption<std::string, cl::list<std::string>> MacroDefs("D", cl::AlwaysPrefix, cl::desc("Define a macro"),cl::value_desc("MACRO[=VALUE]"));cl::ResetAllOptionOccurrences();// Test non-prefixed variant does not work with cl::AlwaysPrefix options:// equal sign is part of the value.EXPECT_TRUE(MacroDefs.empty());const char *args4[] = {"prog", "-D=HAVE_FOO"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));EXPECT_EQ(MacroDefs.size(), 1u);EXPECT_EQ(MacroDefs.front().compare("=HAVE_FOO"), 0);MacroDefs.erase(MacroDefs.begin());cl::ResetAllOptionOccurrences();// Test non-prefixed variant does not allow value to be passed in following// argument with cl::AlwaysPrefix options.EXPECT_TRUE(MacroDefs.empty());const char *args5[] = {"prog", "-D", "HAVE_FOO"};EXPECT_FALSE(cl::ParseCommandLineOptions(3, args5, StringRef(), &llvm::nulls()));EXPECT_TRUE(MacroDefs.empty());cl::ResetAllOptionOccurrences();// Test prefixed variant works with cl::AlwaysPrefix options.EXPECT_TRUE(MacroDefs.empty());const char *args6[] = {"prog", "-DHAVE_FOO"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls()));EXPECT_EQ(MacroDefs.size(), 1u);EXPECT_EQ(MacroDefs.front().compare("HAVE_FOO"), 0);}TEST(CommandLineTest, GroupingWithValue) {cl::ResetCommandLineParser();StackOption<bool> OptF("f", cl::Grouping, cl::desc("Some flag"));StackOption<bool> OptB("b", cl::Grouping, cl::desc("Another flag"));StackOption<bool> OptD("d", cl::Grouping, cl::ValueDisallowed,cl::desc("ValueDisallowed option"));StackOption<std::string> OptV("v", cl::Grouping,cl::desc("ValueRequired option"));StackOption<std::string> OptO("o", cl::Grouping, cl::ValueOptional,cl::desc("ValueOptional option"));// Should be possible to use an option which requires a value// at the end of a group.const char *args1[] = {"prog", "-fv", "val1"};EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("val1", OptV.c_str());OptV.clear();cl::ResetAllOptionOccurrences();// Should not crash if it is accidentally used elsewhere in the group.const char *args2[] = {"prog", "-vf", "val2"};EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));OptV.clear();cl::ResetAllOptionOccurrences();// Should allow the "opt=value" form at the end of the groupconst char *args3[] = {"prog", "-fv=val3"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("val3", OptV.c_str());OptV.clear();cl::ResetAllOptionOccurrences();// Should allow assigning a value for a ValueOptional option// at the end of the groupconst char *args4[] = {"prog", "-fo=val4"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("val4", OptO.c_str());OptO.clear();cl::ResetAllOptionOccurrences();// Should assign an empty value if a ValueOptional option is used elsewhere// in the group.const char *args5[] = {"prog", "-fob"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args5, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_EQ(1, OptO.getNumOccurrences());EXPECT_EQ(1, OptB.getNumOccurrences());EXPECT_TRUE(OptO.empty());cl::ResetAllOptionOccurrences();// Should not allow an assignment for a ValueDisallowed option.const char *args6[] = {"prog", "-fd=false"};EXPECT_FALSE(cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls()));}TEST(CommandLineTest, GroupingAndPrefix) {cl::ResetCommandLineParser();StackOption<bool> OptF("f", cl::Grouping, cl::desc("Some flag"));StackOption<bool> OptB("b", cl::Grouping, cl::desc("Another flag"));StackOption<std::string> OptP("p", cl::Prefix, cl::Grouping,cl::desc("Prefix and Grouping"));StackOption<std::string> OptA("a", cl::AlwaysPrefix, cl::Grouping,cl::desc("AlwaysPrefix and Grouping"));// Should be possible to use a cl::Prefix option without grouping.const char *args1[] = {"prog", "-pval1"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls()));EXPECT_STREQ("val1", OptP.c_str());OptP.clear();cl::ResetAllOptionOccurrences();// Should be possible to pass a value in a separate argument.const char *args2[] = {"prog", "-p", "val2"};EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));EXPECT_STREQ("val2", OptP.c_str());OptP.clear();cl::ResetAllOptionOccurrences();// The "-opt=value" form should work, too.const char *args3[] = {"prog", "-p=val3"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));EXPECT_STREQ("val3", OptP.c_str());OptP.clear();cl::ResetAllOptionOccurrences();// All three previous cases should work the same way if an option with both// cl::Prefix and cl::Grouping modifiers is used at the end of a group.const char *args4[] = {"prog", "-fpval4"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("val4", OptP.c_str());OptP.clear();cl::ResetAllOptionOccurrences();const char *args5[] = {"prog", "-fp", "val5"};EXPECT_TRUE(cl::ParseCommandLineOptions(3, args5, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("val5", OptP.c_str());OptP.clear();cl::ResetAllOptionOccurrences();const char *args6[] = {"prog", "-fp=val6"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("val6", OptP.c_str());OptP.clear();cl::ResetAllOptionOccurrences();// Should assign a value even if the part after a cl::Prefix option is equal// to the name of another option.const char *args7[] = {"prog", "-fpb"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args7, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("b", OptP.c_str());EXPECT_FALSE(OptB);OptP.clear();cl::ResetAllOptionOccurrences();// Should be possible to use a cl::AlwaysPrefix option without grouping.const char *args8[] = {"prog", "-aval8"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args8, StringRef(), &llvm::nulls()));EXPECT_STREQ("val8", OptA.c_str());OptA.clear();cl::ResetAllOptionOccurrences();// Should not be possible to pass a value in a separate argument.const char *args9[] = {"prog", "-a", "val9"};EXPECT_FALSE(cl::ParseCommandLineOptions(3, args9, StringRef(), &llvm::nulls()));cl::ResetAllOptionOccurrences();// With the "-opt=value" form, the "=" symbol should be preserved.const char *args10[] = {"prog", "-a=val10"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args10, StringRef(), &llvm::nulls()));EXPECT_STREQ("=val10", OptA.c_str());OptA.clear();cl::ResetAllOptionOccurrences();// All three previous cases should work the same way if an option with both// cl::AlwaysPrefix and cl::Grouping modifiers is used at the end of a group.const char *args11[] = {"prog", "-faval11"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args11, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("val11", OptA.c_str());OptA.clear();cl::ResetAllOptionOccurrences();const char *args12[] = {"prog", "-fa", "val12"};EXPECT_FALSE(cl::ParseCommandLineOptions(3, args12, StringRef(), &llvm::nulls()));cl::ResetAllOptionOccurrences();const char *args13[] = {"prog", "-fa=val13"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args13, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("=val13", OptA.c_str());OptA.clear();cl::ResetAllOptionOccurrences();// Should assign a value even if the part after a cl::AlwaysPrefix option// is equal to the name of another option.const char *args14[] = {"prog", "-fab"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args14, StringRef(), &llvm::nulls()));EXPECT_TRUE(OptF);EXPECT_STREQ("b", OptA.c_str());EXPECT_FALSE(OptB);OptA.clear();cl::ResetAllOptionOccurrences();}TEST(CommandLineTest, LongOptions) {cl::ResetCommandLineParser();StackOption<bool> OptA("a", cl::desc("Some flag"));StackOption<bool> OptBLong("long-flag", cl::desc("Some long flag"));StackOption<bool, cl::alias> OptB("b", cl::desc("Alias to --long-flag"),cl::aliasopt(OptBLong));StackOption<std::string> OptAB("ab", cl::desc("Another long option"));std::string Errs;raw_string_ostream OS(Errs);const char *args1[] = {"prog", "-a", "-ab", "val1"};const char *args2[] = {"prog", "-a", "--ab", "val1"};const char *args3[] = {"prog", "-ab", "--ab", "val1"};//// The following tests treat `-` and `--` the same, and always match the// longest string.//EXPECT_TRUE(cl::ParseCommandLineOptions(4, args1, StringRef(), &OS)); OS.flush();EXPECT_TRUE(OptA);EXPECT_FALSE(OptBLong);EXPECT_STREQ("val1", OptAB.c_str());EXPECT_TRUE(Errs.empty()); Errs.clear();cl::ResetAllOptionOccurrences();EXPECT_TRUE(cl::ParseCommandLineOptions(4, args2, StringRef(), &OS)); OS.flush();EXPECT_TRUE(OptA);EXPECT_FALSE(OptBLong);EXPECT_STREQ("val1", OptAB.c_str());EXPECT_TRUE(Errs.empty()); Errs.clear();cl::ResetAllOptionOccurrences();// Fails because `-ab` and `--ab` are treated the same and appear more than// once. Also, `val1` is unexpected.EXPECT_FALSE(cl::ParseCommandLineOptions(4, args3, StringRef(), &OS)); OS.flush();outs()<< Errs << "\n";EXPECT_FALSE(Errs.empty()); Errs.clear();cl::ResetAllOptionOccurrences();//// The following tests treat `-` and `--` differently, with `-` for short, and// `--` for long options.//// Fails because `-ab` is treated as `-a -b`, so `-a` is seen twice, and// `val1` is unexpected.EXPECT_FALSE(cl::ParseCommandLineOptions(4, args1, StringRef(),&OS, nullptr, true)); OS.flush();EXPECT_FALSE(Errs.empty()); Errs.clear();cl::ResetAllOptionOccurrences();// Works because `-a` is treated differently than `--ab`.EXPECT_TRUE(cl::ParseCommandLineOptions(4, args2, StringRef(),&OS, nullptr, true)); OS.flush();EXPECT_TRUE(Errs.empty()); Errs.clear();cl::ResetAllOptionOccurrences();// Works because `-ab` is treated as `-a -b`, and `--ab` is a long option.EXPECT_TRUE(cl::ParseCommandLineOptions(4, args3, StringRef(),&OS, nullptr, true));EXPECT_TRUE(OptA);EXPECT_TRUE(OptBLong);EXPECT_STREQ("val1", OptAB.c_str());OS.flush();EXPECT_TRUE(Errs.empty()); Errs.clear();cl::ResetAllOptionOccurrences();}TEST(CommandLineTest, OptionErrorMessage) {// When there is an error, we expect some error message like:// prog: for the -a option: [...]//// Test whether the "for the -a option"-part is correctly formatted.cl::ResetCommandLineParser();StackOption<bool> OptA("a", cl::desc("Some option"));StackOption<bool> OptLong("long", cl::desc("Some long option"));std::string Errs;raw_string_ostream OS(Errs);OptA.error("custom error", OS);OS.flush();EXPECT_NE(Errs.find("for the -a option:"), std::string::npos);Errs.clear();OptLong.error("custom error", OS);OS.flush();EXPECT_NE(Errs.find("for the --long option:"), std::string::npos);Errs.clear();cl::ResetAllOptionOccurrences();}TEST(CommandLineTest, OptionErrorMessageSuggest) {// When there is an error, and the edit-distance is not very large,// we expect some error message like:// prog: did you mean '--option'?//// Test whether this message is well-formatted.cl::ResetCommandLineParser();StackOption<bool> OptLong("aluminium", cl::desc("Some long option"));const char *args[] = {"prog", "--aluminum"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));OS.flush();EXPECT_NE(Errs.find("prog: Did you mean '--aluminium'?\n"),std::string::npos);Errs.clear();cl::ResetAllOptionOccurrences();}TEST(CommandLineTest, OptionErrorMessageSuggestNoHidden) {// We expect that 'really hidden' option do not show up in option// suggestions.cl::ResetCommandLineParser();StackOption<bool> OptLong("aluminium", cl::desc("Some long option"));StackOption<bool> OptLong2("aluminum", cl::desc("Bad option"),cl::ReallyHidden);const char *args[] = {"prog", "--alumnum"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));OS.flush();EXPECT_NE(Errs.find("prog: Did you mean '--aluminium'?\n"),std::string::npos);Errs.clear();cl::ResetAllOptionOccurrences();}TEST(CommandLineTest, Callback) {cl::ResetCommandLineParser();StackOption<bool> OptA("a", cl::desc("option a"));StackOption<bool> OptB("b", cl::desc("option b -- This option turns on option a"),cl::callback([&](const bool &) { OptA = true; }));StackOption<bool> OptC("c", cl::desc("option c -- This option turns on options a and b"),cl::callback([&](const bool &) { OptB = true; }));StackOption<std::string, cl::list<std::string>> List("list",cl::desc("option list -- This option turns on options a, b, and c when ""'foo' is included in list"),cl::CommaSeparated,cl::callback([&](const std::string &Str) {if (Str == "foo")OptC = true;}));const char *args1[] = {"prog", "-a"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1));EXPECT_TRUE(OptA);EXPECT_FALSE(OptB);EXPECT_FALSE(OptC);EXPECT_EQ(List.size(), 0u);cl::ResetAllOptionOccurrences();const char *args2[] = {"prog", "-b"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args2));EXPECT_TRUE(OptA);EXPECT_TRUE(OptB);EXPECT_FALSE(OptC);EXPECT_EQ(List.size(), 0u);cl::ResetAllOptionOccurrences();const char *args3[] = {"prog", "-c"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3));EXPECT_TRUE(OptA);EXPECT_TRUE(OptB);EXPECT_TRUE(OptC);EXPECT_EQ(List.size(), 0u);cl::ResetAllOptionOccurrences();const char *args4[] = {"prog", "--list=foo,bar"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4));EXPECT_TRUE(OptA);EXPECT_TRUE(OptB);EXPECT_TRUE(OptC);EXPECT_EQ(List.size(), 2u);cl::ResetAllOptionOccurrences();const char *args5[] = {"prog", "--list=bar"};EXPECT_TRUE(cl::ParseCommandLineOptions(2, args5));EXPECT_FALSE(OptA);EXPECT_FALSE(OptB);EXPECT_FALSE(OptC);EXPECT_EQ(List.size(), 1u);cl::ResetAllOptionOccurrences();}enum Enum { Val1, Val2 };static cl::bits<Enum> ExampleBits(cl::desc("An example cl::bits to ensure it compiles"),cl::values(clEnumValN(Val1, "bits-val1", "The Val1 value"),clEnumValN(Val1, "bits-val2", "The Val2 value")));TEST(CommandLineTest, ConsumeAfterOnePositional) {cl::ResetCommandLineParser();// input [args]StackOption<std::string, cl::opt<std::string>> Input(cl::Positional,cl::Required);StackOption<std::string, cl::list<std::string>> ExtraArgs(cl::ConsumeAfter);const char *Args[] = {"prog", "input", "arg1", "arg2"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_TRUE(cl::ParseCommandLineOptions(4, Args, StringRef(), &OS));OS.flush();EXPECT_EQ("input", Input);EXPECT_EQ(ExtraArgs.size(), 2u);EXPECT_EQ(ExtraArgs[0], "arg1");EXPECT_EQ(ExtraArgs[1], "arg2");EXPECT_TRUE(Errs.empty());}TEST(CommandLineTest, ConsumeAfterTwoPositionals) {cl::ResetCommandLineParser();// input1 input2 [args]StackOption<std::string, cl::opt<std::string>> Input1(cl::Positional,cl::Required);StackOption<std::string, cl::opt<std::string>> Input2(cl::Positional,cl::Required);StackOption<std::string, cl::list<std::string>> ExtraArgs(cl::ConsumeAfter);const char *Args[] = {"prog", "input1", "input2", "arg1", "arg2"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_TRUE(cl::ParseCommandLineOptions(5, Args, StringRef(), &OS));OS.flush();EXPECT_EQ("input1", Input1);EXPECT_EQ("input2", Input2);EXPECT_EQ(ExtraArgs.size(), 2u);EXPECT_EQ(ExtraArgs[0], "arg1");EXPECT_EQ(ExtraArgs[1], "arg2");EXPECT_TRUE(Errs.empty());}TEST(CommandLineTest, ResetAllOptionOccurrences) {cl::ResetCommandLineParser();// -option -str -enableA -enableC [sink] input [args]StackOption<bool> Option("option");StackOption<std::string> Str("str");enum Vals { ValA, ValB, ValC };StackOption<Vals, cl::bits<Vals>> Bits(cl::values(clEnumValN(ValA, "enableA", "Enable A"),clEnumValN(ValB, "enableB", "Enable B"),clEnumValN(ValC, "enableC", "Enable C")));StackOption<std::string, cl::list<std::string>> Sink(cl::Sink);StackOption<std::string> Input(cl::Positional);StackOption<std::string, cl::list<std::string>> ExtraArgs(cl::ConsumeAfter);const char *Args[] = {"prog", "-option", "-str=STR", "-enableA","-enableC", "-unknown", "input", "-arg"};std::string Errs;raw_string_ostream OS(Errs);EXPECT_TRUE(cl::ParseCommandLineOptions(8, Args, StringRef(), &OS));EXPECT_TRUE(OS.str().empty());EXPECT_TRUE(Option);EXPECT_EQ("STR", Str);EXPECT_EQ((1u << ValA) | (1u << ValC), Bits.getBits());EXPECT_EQ(1u, Sink.size());EXPECT_EQ("-unknown", Sink[0]);EXPECT_EQ("input", Input);EXPECT_EQ(1u, ExtraArgs.size());EXPECT_EQ("-arg", ExtraArgs[0]);cl::ResetAllOptionOccurrences();EXPECT_FALSE(Option);EXPECT_EQ("", Str);EXPECT_EQ(0u, Bits.getBits());EXPECT_EQ(0u, Sink.size());EXPECT_EQ(0, Input.getNumOccurrences());EXPECT_EQ(0u, ExtraArgs.size());}TEST(CommandLineTest, DefaultValue) {cl::ResetCommandLineParser();StackOption<bool> BoolOption("bool-option");StackOption<std::string> StrOption("str-option");StackOption<bool> BoolInitOption("bool-init-option", cl::init(true));StackOption<std::string> StrInitOption("str-init-option",cl::init("str-default-value"));const char *Args[] = {"prog"}; // no optionsstd::string Errs;raw_string_ostream OS(Errs);EXPECT_TRUE(cl::ParseCommandLineOptions(1, Args, StringRef(), &OS));EXPECT_TRUE(OS.str().empty());EXPECT_TRUE(!BoolOption);EXPECT_FALSE(BoolOption.Default.hasValue());EXPECT_EQ(0, BoolOption.getNumOccurrences());EXPECT_EQ("", StrOption);EXPECT_FALSE(StrOption.Default.hasValue());EXPECT_EQ(0, StrOption.getNumOccurrences());EXPECT_TRUE(BoolInitOption);EXPECT_TRUE(BoolInitOption.Default.hasValue());EXPECT_EQ(0, BoolInitOption.getNumOccurrences());EXPECT_EQ("str-default-value", StrInitOption);EXPECT_TRUE(StrInitOption.Default.hasValue());EXPECT_EQ(0, StrInitOption.getNumOccurrences());const char *Args2[] = {"prog", "-bool-option", "-str-option=str-value","-bool-init-option=0","-str-init-option=str-init-value"};EXPECT_TRUE(cl::ParseCommandLineOptions(5, Args2, StringRef(), &OS));EXPECT_TRUE(OS.str().empty());EXPECT_TRUE(BoolOption);EXPECT_FALSE(BoolOption.Default.hasValue());EXPECT_EQ(1, BoolOption.getNumOccurrences());EXPECT_EQ("str-value", StrOption);EXPECT_FALSE(StrOption.Default.hasValue());EXPECT_EQ(1, StrOption.getNumOccurrences());EXPECT_FALSE(BoolInitOption);EXPECT_TRUE(BoolInitOption.Default.hasValue());EXPECT_EQ(1, BoolInitOption.getNumOccurrences());EXPECT_EQ("str-init-value", StrInitOption);EXPECT_TRUE(StrInitOption.Default.hasValue());EXPECT_EQ(1, StrInitOption.getNumOccurrences());}} // anonymous namespace
//===- llvm/unittest/Support/CommandLineInit/CommandLineInitTest.cpp ------===////// 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////===----------------------------------------------------------------------===/////// \file/// Check if preset options in libSupport -- e.g., "help", "version", etc. --/// are correctly initialized and registered before getRegisteredOptions is/// invoked.////// Most code here comes from llvm/utils/unittest/UnitTestMain/TestMain.cpp,/// except that llvm::cl::ParseCommandLineOptions() call is removed.////===----------------------------------------------------------------------===//#include "llvm/Support/CommandLine.h"#include "gtest/gtest.h"#if defined(_WIN32)#include <windows.h>#if defined(_MSC_VER)#include <crtdbg.h>#endif#endifusing namespace llvm;int main(int argc, char **argv) {testing::InitGoogleTest(&argc, argv);#if defined(_WIN32)// Disable all of the possible ways Windows conspires to make automated// testing impossible.::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);#if defined(_MSC_VER)::_set_error_mode(_OUT_TO_STDERR);_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);#endif#endifreturn RUN_ALL_TESTS();}TEST(CommandLineInitTest, GetPresetOptions) {StringMap<cl::Option *> &Map =cl::getRegisteredOptions(*cl::TopLevelSubCommand);for (auto *Str :{"help", "help-hidden", "help-list", "help-list-hidden", "version"})EXPECT_EQ(Map.count(Str), (size_t)1)<< "Could not get preset option `" << Str << '`';}
set(test_name CommandLineInitTests)set(test_suite UnitTests)# We don't call add_llvm_unittest() here, because the function automatically# links the test against TestMain.cpp, in which main() function calls# llvm::cl::ParseCommandLineOptions, and it makes the test always pass.# The following code mainly comes from `add_unittest` in# llvm/cmake/modules/AddLLVM.cmake, except that gtest_main is excluded from# target_link_libraries to prevent the test linking against TestMain.cpp.if (NOT LLVM_BUILD_TESTS)set(EXCLUDE_FROM_ALL ON)endif()if (SUPPORTS_VARIADIC_MACROS_FLAG)list(APPEND LLVM_COMPILE_FLAGS "-Wno-variadic-macros")endif ()# Some parts of gtest rely on this GNU extension, don't warn on it.if(SUPPORTS_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS_FLAG)list(APPEND LLVM_COMPILE_FLAGS "-Wno-gnu-zero-variadic-macro-arguments")endif()list(APPEND LLVM_LINK_COMPONENTS Support)add_llvm_executable(${test_name}IGNORE_EXTERNALIZE_DEBUGINFO NO_INSTALL_RPATHCommandLineInitTest.cpp)target_link_libraries(${test_name} PRIVATE llvm_gtest)add_dependencies(${test_suite} ${test_name})set(outdir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})set_output_directory(${test_name} BINARY_DIR ${outdir} LIBRARY_DIR ${outdir})get_target_property(test_suite_folder ${test_suite} FOLDER)if (test_suite_folder)set_property(TARGET ${test_name} PROPERTY FOLDER "${test_suite_folder}")endif ()
//===- llvm/unittest/Support/Chrono.cpp - Time utilities tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Chrono.h"#include "llvm/ADT/SmallVector.h"#include "llvm/Support/FormatVariadic.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::sys;using namespace std::chrono;namespace {TEST(Chrono, TimeTConversion) {EXPECT_EQ(time_t(0), toTimeT(toTimePoint(time_t(0))));EXPECT_EQ(time_t(1), toTimeT(toTimePoint(time_t(1))));EXPECT_EQ(time_t(47), toTimeT(toTimePoint(time_t(47))));TimePoint<> TP;EXPECT_EQ(TP, toTimePoint(toTimeT(TP)));TP += seconds(1);EXPECT_EQ(TP, toTimePoint(toTimeT(TP)));TP += hours(47);EXPECT_EQ(TP, toTimePoint(toTimeT(TP)));}TEST(Chrono, TimePointFormat) {using namespace std::chrono;struct tm TM {};TM.tm_year = 106;TM.tm_mon = 0;TM.tm_mday = 2;TM.tm_hour = 15;TM.tm_min = 4;TM.tm_sec = 5;TM.tm_isdst = -1;TimePoint<> T =system_clock::from_time_t(mktime(&TM)) + nanoseconds(123456789);// operator<< uses the format YYYY-MM-DD HH:MM:SS.NNNNNNNNNstd::string S;raw_string_ostream OS(S);OS << T;EXPECT_EQ("2006-01-02 15:04:05.123456789", OS.str());// formatv default style matches operator<<.EXPECT_EQ("2006-01-02 15:04:05.123456789", formatv("{0}", T).str());// formatv supports strftime-style format strings.EXPECT_EQ("15:04:05", formatv("{0:%H:%M:%S}", T).str());// formatv supports our strftime extensions for sub-second precision.EXPECT_EQ("123", formatv("{0:%L}", T).str());EXPECT_EQ("123456", formatv("{0:%f}", T).str());EXPECT_EQ("123456789", formatv("{0:%N}", T).str());// our extensions don't interfere with %% escaping.EXPECT_EQ("%foo", formatv("{0:%%foo}", T).str());}// Test that toTimePoint and toTimeT can be called with a arguments with varying// precisions.TEST(Chrono, ImplicitConversions) {std::time_t TimeT = 47;TimePoint<seconds> Sec = toTimePoint(TimeT);TimePoint<milliseconds> Milli = toTimePoint(TimeT);TimePoint<microseconds> Micro = toTimePoint(TimeT);TimePoint<nanoseconds> Nano = toTimePoint(TimeT);EXPECT_EQ(Sec, Milli);EXPECT_EQ(Sec, Micro);EXPECT_EQ(Sec, Nano);EXPECT_EQ(TimeT, toTimeT(Sec));EXPECT_EQ(TimeT, toTimeT(Milli));EXPECT_EQ(TimeT, toTimeT(Micro));EXPECT_EQ(TimeT, toTimeT(Nano));}TEST(Chrono, DurationFormat) {EXPECT_EQ("1 h", formatv("{0}", hours(1)).str());EXPECT_EQ("1 m", formatv("{0}", minutes(1)).str());EXPECT_EQ("1 s", formatv("{0}", seconds(1)).str());EXPECT_EQ("1 ms", formatv("{0}", milliseconds(1)).str());EXPECT_EQ("1 us", formatv("{0}", microseconds(1)).str());EXPECT_EQ("1 ns", formatv("{0}", nanoseconds(1)).str());EXPECT_EQ("1 s", formatv("{0:+}", seconds(1)).str());EXPECT_EQ("1", formatv("{0:-}", seconds(1)).str());EXPECT_EQ("1000 ms", formatv("{0:ms}", seconds(1)).str());EXPECT_EQ("1000000 us", formatv("{0:us}", seconds(1)).str());EXPECT_EQ("1000", formatv("{0:ms-}", seconds(1)).str());EXPECT_EQ("1,000 ms", formatv("{0:+n}", milliseconds(1000)).str());EXPECT_EQ("0x3e8", formatv("{0:-x}", milliseconds(1000)).str());EXPECT_EQ("010", formatv("{0:-3}", milliseconds(10)).str());EXPECT_EQ("10,000", formatv("{0:ms-n}", seconds(10)).str());EXPECT_EQ("1.00 s", formatv("{0}", duration<float>(1)).str());EXPECT_EQ("0.123 s", formatv("{0:+3}", duration<float>(0.123f)).str());EXPECT_EQ("1.230e-01 s", formatv("{0:+e3}", duration<float>(0.123f)).str());typedef duration<float, std::ratio<60 * 60 * 24 * 14, 1000000>>microfortnights;EXPECT_EQ("1.00", formatv("{0:-}", microfortnights(1)).str());EXPECT_EQ("1209.60 ms", formatv("{0:ms}", microfortnights(1)).str());}} // anonymous namespace
#include "llvm/Support/CheckedArithmetic.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(CheckedArithmetic, CheckedAdd) {const int64_t Max = std::numeric_limits<int64_t>::max();const int64_t Min = std::numeric_limits<int64_t>::min();EXPECT_EQ(checkedAdd<int64_t>(Max, Max), None);EXPECT_EQ(checkedAdd<int64_t>(Min, -1), None);EXPECT_EQ(checkedAdd<int64_t>(Max, 1), None);EXPECT_EQ(checkedAdd<int64_t>(10, 1), Optional<int64_t>(11));}TEST(CheckedArithmetic, CheckedAddSmall) {const int16_t Max = std::numeric_limits<int16_t>::max();const int16_t Min = std::numeric_limits<int16_t>::min();EXPECT_EQ(checkedAdd<int16_t>(Max, Max), None);EXPECT_EQ(checkedAdd<int16_t>(Min, -1), None);EXPECT_EQ(checkedAdd<int16_t>(Max, 1), None);EXPECT_EQ(checkedAdd<int16_t>(10, 1), Optional<int64_t>(11));}TEST(CheckedArithmetic, CheckedMul) {const int64_t Max = std::numeric_limits<int64_t>::max();const int64_t Min = std::numeric_limits<int64_t>::min();EXPECT_EQ(checkedMul<int64_t>(Max, 2), None);EXPECT_EQ(checkedMul<int64_t>(Max, Max), None);EXPECT_EQ(checkedMul<int64_t>(Min, 2), None);EXPECT_EQ(checkedMul<int64_t>(10, 2), Optional<int64_t>(20));}TEST(CheckedArithmetic, CheckedMulAdd) {const int64_t Max = std::numeric_limits<int64_t>::max();const int64_t Min = std::numeric_limits<int64_t>::min();EXPECT_EQ(checkedMulAdd<int64_t>(Max, 1, 2), None);EXPECT_EQ(checkedMulAdd<int64_t>(1, 1, Max), None);EXPECT_EQ(checkedMulAdd<int64_t>(1, -1, Min), None);EXPECT_EQ(checkedMulAdd<int64_t>(10, 2, 3), Optional<int64_t>(23));}TEST(CheckedArithmetic, CheckedMulSmall) {const int16_t Max = std::numeric_limits<int16_t>::max();const int16_t Min = std::numeric_limits<int16_t>::min();EXPECT_EQ(checkedMul<int16_t>(Max, 2), None);EXPECT_EQ(checkedMul<int16_t>(Max, Max), None);EXPECT_EQ(checkedMul<int16_t>(Min, 2), None);EXPECT_EQ(checkedMul<int16_t>(10, 2), Optional<int16_t>(20));}TEST(CheckedArithmetic, CheckedMulAddSmall) {const int16_t Max = std::numeric_limits<int16_t>::max();const int16_t Min = std::numeric_limits<int16_t>::min();EXPECT_EQ(checkedMulAdd<int16_t>(Max, 1, 2), None);EXPECT_EQ(checkedMulAdd<int16_t>(1, 1, Max), None);EXPECT_EQ(checkedMulAdd<int16_t>(1, -1, Min), None);EXPECT_EQ(checkedMulAdd<int16_t>(10, 2, 3), Optional<int16_t>(23));}TEST(CheckedArithmetic, CheckedAddUnsigned) {const uint64_t Max = std::numeric_limits<uint64_t>::max();EXPECT_EQ(checkedAddUnsigned<uint64_t>(Max, Max), None);EXPECT_EQ(checkedAddUnsigned<uint64_t>(Max, 1), None);EXPECT_EQ(checkedAddUnsigned<uint64_t>(10, 1), Optional<uint64_t>(11));}TEST(CheckedArithmetic, CheckedMulUnsigned) {const uint64_t Max = std::numeric_limits<uint64_t>::max();EXPECT_EQ(checkedMulUnsigned<uint64_t>(Max, 2), llvm::None);EXPECT_EQ(checkedMulUnsigned<uint64_t>(Max, Max), llvm::None);EXPECT_EQ(checkedMulUnsigned<uint64_t>(10, 2), Optional<uint64_t>(20));}TEST(CheckedArithmetic, CheckedMulAddUnsigned) {const uint64_t Max = std::numeric_limits<uint64_t>::max();EXPECT_EQ(checkedMulAddUnsigned<uint64_t>(Max, 1, 2), None);EXPECT_EQ(checkedMulAddUnsigned<uint64_t>(1, 1, Max), None);EXPECT_EQ(checkedMulAddUnsigned<uint64_t>(10, 2, 3), Optional<uint64_t>(23));}} // namespace
//===---------- llvm/unittest/Support/Casting.cpp - Casting tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Casting.h"#include "llvm/IR/User.h"#include "llvm/Support/Debug.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <cstdlib>namespace llvm {// Used to test illegal cast. If a cast doesn't match any of the "real" ones,// it will match this one.struct IllegalCast;template <typename T> IllegalCast *cast(...) { return nullptr; }// set up two example classes// with conversion facility//struct bar {bar() {}struct foo *baz();struct foo *caz();struct foo *daz();struct foo *naz();private:bar(const bar &);};struct foo {foo(const bar &) {}void ext() const;};struct base {virtual ~base() {}};struct derived : public base {static bool classof(const base *B) { return true; }};template <> struct isa_impl<foo, bar> {static inline bool doit(const bar &Val) {dbgs() << "Classof: " << &Val << "\n";return true;}};// Note for the future - please don't do this. isa_impl is an internal template// for the implementation of `isa` and should not be exposed this way.// Completely unrelated types *should* result in compiler errors if you try to// cast between them.template <typename T> struct isa_impl<foo, T> {static inline bool doit(const T &Val) { return false; }};foo *bar::baz() { return cast<foo>(this); }foo *bar::caz() { return cast_or_null<foo>(this); }foo *bar::daz() { return dyn_cast<foo>(this); }foo *bar::naz() { return dyn_cast_or_null<foo>(this); }bar *fub();template <> struct simplify_type<foo> {typedef int SimpleType;static SimpleType getSimplifiedValue(foo &Val) { return 0; }};struct T1 {};struct T2 {T2(const T1 &x) {}static bool classof(const T1 *x) { return true; }};template <> struct CastInfo<T2, T1> : public OptionalValueCast<T2, T1> {};struct T3 {T3(const T1 *x) : hasValue(x != nullptr) {}static bool classof(const T1 *x) { return true; }bool hasValue = false;};// T3 is convertible from a pointer to T1.template <> struct CastInfo<T3, T1 *> : public ValueFromPointerCast<T3, T1> {};struct T4 {T4() : hasValue(false) {}T4(const T3 &x) : hasValue(true) {}static bool classof(const T3 *x) { return true; }bool hasValue = false;};template <> struct ValueIsPresent<T3> {using UnwrappedType = T3;static inline bool isPresent(const T3 &t) { return t.hasValue; }static inline const T3 &unwrapValue(const T3 &t) { return t; }};template <> struct CastInfo<T4, T3> {using CastResultType = T4;static inline CastResultType doCast(const T3 &t) { return T4(t); }static inline CastResultType castFailed() { return CastResultType(); }static inline CastResultType doCastIfPossible(const T3 &f) {return doCast(f);}};} // namespace llvmusing namespace llvm;// Test the peculiar behavior of Use in simplify_type.static_assert(std::is_same<simplify_type<Use>::SimpleType, Value *>::value,"Use doesn't simplify correctly!");static_assert(std::is_same<simplify_type<Use *>::SimpleType, Value *>::value,"Use doesn't simplify correctly!");// Test that a regular class behaves as expected.static_assert(std::is_same<simplify_type<foo>::SimpleType, int>::value,"Unexpected simplify_type result!");static_assert(std::is_same<simplify_type<foo *>::SimpleType, foo *>::value,"Unexpected simplify_type result!");namespace {const foo *null_foo = nullptr;bar B;extern bar &B1;bar &B1 = B;extern const bar *B2;// test various configurations of constconst bar &B3 = B1;const bar *const B4 = B2;TEST(CastingTest, isa) {EXPECT_TRUE(isa<foo>(B1));EXPECT_TRUE(isa<foo>(B2));EXPECT_TRUE(isa<foo>(B3));EXPECT_TRUE(isa<foo>(B4));}TEST(CastingTest, isa_and_nonnull) {EXPECT_TRUE(isa_and_nonnull<foo>(B2));EXPECT_TRUE(isa_and_nonnull<foo>(B4));EXPECT_FALSE(isa_and_nonnull<foo>(fub()));}TEST(CastingTest, cast) {foo &F1 = cast<foo>(B1);EXPECT_NE(&F1, null_foo);const foo *F3 = cast<foo>(B2);EXPECT_NE(F3, null_foo);const foo *F4 = cast<foo>(B2);EXPECT_NE(F4, null_foo);const foo &F5 = cast<foo>(B3);EXPECT_NE(&F5, null_foo);const foo *F6 = cast<foo>(B4);EXPECT_NE(F6, null_foo);// Can't pass null pointer to cast<>.// foo *F7 = cast<foo>(fub());// EXPECT_EQ(F7, null_foo);foo *F8 = B1.baz();EXPECT_NE(F8, null_foo);std::unique_ptr<const bar> BP(B2);auto FP = cast<foo>(std::move(BP));static_assert(std::is_same<std::unique_ptr<const foo>, decltype(FP)>::value,"Incorrect deduced return type!");EXPECT_NE(FP.get(), null_foo);FP.release();}TEST(CastingTest, cast_or_null) {const foo *F11 = cast_or_null<foo>(B2);EXPECT_NE(F11, null_foo);const foo *F12 = cast_or_null<foo>(B2);EXPECT_NE(F12, null_foo);const foo *F13 = cast_or_null<foo>(B4);EXPECT_NE(F13, null_foo);const foo *F14 = cast_or_null<foo>(fub()); // Shouldn't print.EXPECT_EQ(F14, null_foo);foo *F15 = B1.caz();EXPECT_NE(F15, null_foo);std::unique_ptr<const bar> BP(fub());auto FP = cast_or_null<foo>(std::move(BP));EXPECT_EQ(FP.get(), null_foo);}TEST(CastingTest, dyn_cast) {const foo *F1 = dyn_cast<foo>(B2);EXPECT_NE(F1, null_foo);const foo *F2 = dyn_cast<foo>(B2);EXPECT_NE(F2, null_foo);const foo *F3 = dyn_cast<foo>(B4);EXPECT_NE(F3, null_foo);// Can't pass null pointer to dyn_cast<>.// foo *F4 = dyn_cast<foo>(fub());// EXPECT_EQ(F4, null_foo);foo *F5 = B1.daz();EXPECT_NE(F5, null_foo);}// All these tests forward to dyn_cast_if_present, so they also provde an// effective test for its use cases.TEST(CastingTest, dyn_cast_or_null) {const foo *F1 = dyn_cast_or_null<foo>(B2);EXPECT_NE(F1, null_foo);const foo *F2 = dyn_cast_or_null<foo>(B2);EXPECT_NE(F2, null_foo);const foo *F3 = dyn_cast_or_null<foo>(B4);EXPECT_NE(F3, null_foo);foo *F4 = dyn_cast_or_null<foo>(fub());EXPECT_EQ(F4, null_foo);foo *F5 = B1.naz();EXPECT_NE(F5, null_foo);// dyn_cast_if_present should have exactly the same behavior as// dyn_cast_or_null.const foo *F6 = dyn_cast_if_present<foo>(B2);EXPECT_EQ(F6, F2);}TEST(CastingTest, dyn_cast_value_types) {T1 t1;Optional<T2> t2 = dyn_cast<T2>(t1);EXPECT_TRUE(t2);T2 *t2ptr = dyn_cast<T2>(&t1);EXPECT_TRUE(t2ptr != nullptr);T3 t3 = dyn_cast<T3>(&t1);EXPECT_TRUE(t3.hasValue);}TEST(CastingTest, dyn_cast_if_present) {Optional<T1> empty{};Optional<T2> F1 = dyn_cast_if_present<T2>(empty);EXPECT_FALSE(F1.has_value());T1 t1;Optional<T2> F2 = dyn_cast_if_present<T2>(t1);EXPECT_TRUE(F2.has_value());T1 *t1Null = nullptr;// T3 should have hasValue == false because t1Null is nullptr.T3 t3 = dyn_cast_if_present<T3>(t1Null);EXPECT_FALSE(t3.hasValue);// Now because of that, T4 should receive the castFailed implementation of its// FallibleCastTraits, which default-constructs a T4, which has no value.T4 t4 = dyn_cast_if_present<T4>(t3);EXPECT_FALSE(t4.hasValue);}std::unique_ptr<derived> newd() { return std::make_unique<derived>(); }std::unique_ptr<base> newb() { return std::make_unique<derived>(); }TEST(CastingTest, unique_dyn_cast) {derived *OrigD = nullptr;auto D = std::make_unique<derived>();OrigD = D.get();// Converting from D to itself is valid, it should return a new unique_ptr// and the old one should become nullptr.auto NewD = unique_dyn_cast<derived>(D);ASSERT_EQ(OrigD, NewD.get());ASSERT_EQ(nullptr, D);// Converting from D to B is valid, B should have a value and D should be// nullptr.auto B = unique_dyn_cast<base>(NewD);ASSERT_EQ(OrigD, B.get());ASSERT_EQ(nullptr, NewD);// Converting from B to itself is valid, it should return a new unique_ptr// and the old one should become nullptr.auto NewB = unique_dyn_cast<base>(B);ASSERT_EQ(OrigD, NewB.get());ASSERT_EQ(nullptr, B);// Converting from B to D is valid, D should have a value and B should be// nullptr;D = unique_dyn_cast<derived>(NewB);ASSERT_EQ(OrigD, D.get());ASSERT_EQ(nullptr, NewB);// This is a very contrived test, casting between completely unrelated types// should generally fail to compile. See the classof shenanigans we have in// the definition of `foo` above.auto F = unique_dyn_cast<foo>(D);ASSERT_EQ(nullptr, F);ASSERT_EQ(OrigD, D.get());// All of the above should also hold for temporaries.auto D2 = unique_dyn_cast<derived>(newd());EXPECT_NE(nullptr, D2);auto B2 = unique_dyn_cast<derived>(newb());EXPECT_NE(nullptr, B2);auto B3 = unique_dyn_cast<base>(newb());EXPECT_NE(nullptr, B3);// This is a very contrived test, casting between completely unrelated types// should generally fail to compile. See the classof shenanigans we have in// the definition of `foo` above.auto F2 = unique_dyn_cast<foo>(newb());EXPECT_EQ(nullptr, F2);}// These lines are errors...// foo *F20 = cast<foo>(B2); // Yields const foo*// foo &F21 = cast<foo>(B3); // Yields const foo&// foo *F22 = cast<foo>(B4); // Yields const foo*// foo &F23 = cast_or_null<foo>(B1);// const foo &F24 = cast_or_null<foo>(B3);const bar *B2 = &B;} // anonymous namespacebar *llvm::fub() { return nullptr; }namespace {namespace inferred_upcasting {// This test case verifies correct behavior of inferred upcasts when the// types are statically known to be OK to upcast. This is the case when,// for example, Derived inherits from Base, and we do `isa<Base>(Derived)`.// Note: This test will actually fail to compile without inferred// upcasting.class Base {public:// No classof. We are testing that the upcast is inferred.Base() {}};class Derived : public Base {public:Derived() {}};// Even with no explicit classof() in Base, we should still be able to cast// Derived to its base class.TEST(CastingTest, UpcastIsInferred) {Derived D;EXPECT_TRUE(isa<Base>(D));Base *BP = dyn_cast<Base>(&D);EXPECT_NE(BP, nullptr);}// This test verifies that the inferred upcast takes precedence over an// explicitly written one. This is important because it verifies that the// dynamic check gets optimized away.class UseInferredUpcast {public:int Dummy;static bool classof(const UseInferredUpcast *) { return false; }};TEST(CastingTest, InferredUpcastTakesPrecedence) {UseInferredUpcast UIU;// Since the explicit classof() returns false, this will fail if the// explicit one is used.EXPECT_TRUE(isa<UseInferredUpcast>(&UIU));}} // end namespace inferred_upcasting} // end anonymous namespacenamespace {namespace pointer_wrappers {struct Base {bool IsDerived;Base(bool IsDerived = false) : IsDerived(IsDerived) {}};struct Derived : Base {Derived() : Base(true) {}static bool classof(const Base *B) { return B->IsDerived; }};class PTy {Base *B;public:PTy(Base *B) : B(B) {}explicit operator bool() const { return get(); }Base *get() const { return B; }};} // end namespace pointer_wrappers} // end namespacenamespace llvm {template <> struct ValueIsPresent<pointer_wrappers::PTy> {using UnwrappedType = pointer_wrappers::PTy;static inline bool isPresent(const pointer_wrappers::PTy &P) {return P.get() != nullptr;}static UnwrappedType &unwrapValue(pointer_wrappers::PTy &P) { return P; }};template <> struct ValueIsPresent<const pointer_wrappers::PTy> {using UnwrappedType = pointer_wrappers::PTy;static inline bool isPresent(const pointer_wrappers::PTy &P) {return P.get() != nullptr;}static UnwrappedType &unwrapValue(const pointer_wrappers::PTy &P) {return const_cast<UnwrappedType &>(P);}};template <> struct simplify_type<pointer_wrappers::PTy> {typedef pointer_wrappers::Base *SimpleType;static SimpleType getSimplifiedValue(pointer_wrappers::PTy &P) {return P.get();}};template <> struct simplify_type<const pointer_wrappers::PTy> {typedef pointer_wrappers::Base *SimpleType;static SimpleType getSimplifiedValue(const pointer_wrappers::PTy &P) {return P.get();}};} // end namespace llvmnamespace {namespace pointer_wrappers {// Some objects.pointer_wrappers::Base B;pointer_wrappers::Derived D;// Mutable "smart" pointers.pointer_wrappers::PTy MN(nullptr);pointer_wrappers::PTy MB(&B);pointer_wrappers::PTy MD(&D);// Const "smart" pointers.const pointer_wrappers::PTy CN(nullptr);const pointer_wrappers::PTy CB(&B);const pointer_wrappers::PTy CD(&D);TEST(CastingTest, smart_isa) {EXPECT_TRUE(!isa<pointer_wrappers::Derived>(MB));EXPECT_TRUE(!isa<pointer_wrappers::Derived>(CB));EXPECT_TRUE(isa<pointer_wrappers::Derived>(MD));EXPECT_TRUE(isa<pointer_wrappers::Derived>(CD));}TEST(CastingTest, smart_cast) {EXPECT_EQ(cast<pointer_wrappers::Derived>(MD), &D);EXPECT_EQ(cast<pointer_wrappers::Derived>(CD), &D);}TEST(CastingTest, smart_cast_or_null) {EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(MN), nullptr);EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(CN), nullptr);EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(MD), &D);EXPECT_EQ(cast_or_null<pointer_wrappers::Derived>(CD), &D);}TEST(CastingTest, smart_dyn_cast) {EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(MB), nullptr);EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(CB), nullptr);EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(MD), &D);EXPECT_EQ(dyn_cast<pointer_wrappers::Derived>(CD), &D);}TEST(CastingTest, smart_dyn_cast_or_null) {EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(MN), nullptr);EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(CN), nullptr);EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(MB), nullptr);EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(CB), nullptr);EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(MD), &D);EXPECT_EQ(dyn_cast_or_null<pointer_wrappers::Derived>(CD), &D);}} // end namespace pointer_wrappers#ifndef NDEBUGnamespace assertion_checks {struct Base {virtual ~Base() {}};struct Derived : public Base {static bool classof(const Base *B) { return false; }};TEST(CastingTest, assertion_check_const_ref) {const Base B;EXPECT_DEATH((void)cast<Derived>(B), "argument of incompatible type")<< "Invalid cast of const ref did not cause an abort()";}TEST(CastingTest, assertion_check_ref) {Base B;EXPECT_DEATH((void)cast<Derived>(B), "argument of incompatible type")<< "Invalid cast of const ref did not cause an abort()";}TEST(CastingTest, assertion_check_ptr) {Base B;EXPECT_DEATH((void)cast<Derived>(&B), "argument of incompatible type")<< "Invalid cast of const ref did not cause an abort()";}TEST(CastingTest, assertion_check_unique_ptr) {auto B = std::make_unique<Base>();EXPECT_DEATH((void)cast<Derived>(std::move(B)),"argument of incompatible type")<< "Invalid cast of const ref did not cause an abort()";}} // end namespace assertion_checks#endif} // end namespace
//===- CachePruningTest.cpp -----------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/CachePruning.h"#include "llvm/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;TEST(CachePruningPolicyParser, Empty) {auto P = parseCachePruningPolicy("");ASSERT_TRUE(bool(P));EXPECT_EQ(std::chrono::seconds(1200), P->Interval);EXPECT_EQ(std::chrono::hours(7 * 24), P->Expiration);EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);}TEST(CachePruningPolicyParser, Interval) {auto P = parseCachePruningPolicy("prune_interval=1s");ASSERT_TRUE(bool(P));EXPECT_EQ(std::chrono::seconds(1), P->Interval);P = parseCachePruningPolicy("prune_interval=2m");ASSERT_TRUE(bool(P));EXPECT_EQ(std::chrono::minutes(2), *P->Interval);P = parseCachePruningPolicy("prune_interval=3h");ASSERT_TRUE(bool(P));EXPECT_EQ(std::chrono::hours(3), *P->Interval);}TEST(CachePruningPolicyParser, Expiration) {auto P = parseCachePruningPolicy("prune_after=1s");ASSERT_TRUE(bool(P));EXPECT_EQ(std::chrono::seconds(1), P->Expiration);}TEST(CachePruningPolicyParser, MaxSizePercentageOfAvailableSpace) {auto P = parseCachePruningPolicy("cache_size=100%");ASSERT_TRUE(bool(P));EXPECT_EQ(100u, P->MaxSizePercentageOfAvailableSpace);EXPECT_EQ(0u, P->MaxSizeBytes);}TEST(CachePruningPolicyParser, MaxSizeBytes) {auto P = parseCachePruningPolicy("cache_size_bytes=1");ASSERT_TRUE(bool(P));EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);EXPECT_EQ(1u, P->MaxSizeBytes);P = parseCachePruningPolicy("cache_size_bytes=2k");ASSERT_TRUE(bool(P));EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);EXPECT_EQ(2u * 1024u, P->MaxSizeBytes);P = parseCachePruningPolicy("cache_size_bytes=3m");ASSERT_TRUE(bool(P));EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);EXPECT_EQ(3u * 1024u * 1024u, P->MaxSizeBytes);P = parseCachePruningPolicy("cache_size_bytes=4G");ASSERT_TRUE(bool(P));EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);EXPECT_EQ(4ull * 1024ull * 1024ull * 1024ull, P->MaxSizeBytes);}TEST(CachePruningPolicyParser, Multiple) {auto P = parseCachePruningPolicy("prune_after=1s:cache_size=50%");ASSERT_TRUE(bool(P));EXPECT_EQ(std::chrono::seconds(1200), P->Interval);EXPECT_EQ(std::chrono::seconds(1), P->Expiration);EXPECT_EQ(50u, P->MaxSizePercentageOfAvailableSpace);}TEST(CachePruningPolicyParser, Errors) {EXPECT_EQ("Duration must not be empty",toString(parseCachePruningPolicy("prune_interval=").takeError()));EXPECT_EQ("'foo' not an integer",toString(parseCachePruningPolicy("prune_interval=foos").takeError()));EXPECT_EQ("'24x' must end with one of 's', 'm' or 'h'",toString(parseCachePruningPolicy("prune_interval=24x").takeError()));EXPECT_EQ("'foo' must be a percentage",toString(parseCachePruningPolicy("cache_size=foo").takeError()));EXPECT_EQ("'foo' not an integer",toString(parseCachePruningPolicy("cache_size=foo%").takeError()));EXPECT_EQ("'101' must be between 0 and 100",toString(parseCachePruningPolicy("cache_size=101%").takeError()));EXPECT_EQ("'foo' not an integer",toString(parseCachePruningPolicy("cache_size_bytes=foo").takeError()));EXPECT_EQ("'foo' not an integer",toString(parseCachePruningPolicy("cache_size_bytes=foom").takeError()));EXPECT_EQ("Unknown key: 'foo'",toString(parseCachePruningPolicy("foo=bar").takeError()));}
//===-------- CSKYTargetParser.cpp - CSKY Target Parser -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/CSKYTargetParser.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/StringExtras.h"#include "llvm/Support/CSKYAttributes.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/TargetParser.h"#include "gtest/gtest.h"#include <string>using namespace llvm;namespace {std::string FormatExtensionFlags(uint64_t Flags) {std::vector<StringRef> Features;if (Flags & CSKY::AEK_NONE)Features.push_back("none");CSKY::getExtensionFeatures(Flags, Features);Features.erase(std::remove_if(Features.begin(), Features.end(),[](StringRef extension) {return extension.startswith("-");}),Features.end());return llvm::join(Features, ", ");}testing::AssertionResult AssertSameExtensionFlags(const char *m_expr,const char *n_expr,uint64_t ExpectedFlags,uint64_t GotFlags) {if (ExpectedFlags == GotFlags)return testing::AssertionSuccess();return testing::AssertionFailure()<< llvm::formatv("Expected extension flags: {0} ({1:x})\n"" Got extension flags: {2} ({3:x})\n",FormatExtensionFlags(ExpectedFlags), ExpectedFlags,FormatExtensionFlags(GotFlags), GotFlags);}struct CSKYCPUTestParams {CSKYCPUTestParams(StringRef CPUName, StringRef ExpectedArch,uint64_t ExpectedFlags): CPUName(CPUName), ExpectedArch(ExpectedArch),ExpectedFlags(ExpectedFlags) {}friend std::ostream &operator<<(std::ostream &os,const CSKYCPUTestParams ¶ms) {return os << "\"" << params.CPUName.str() << "\", \""<< params.ExpectedArch.str() << "\", \"" << params.ExpectedFlags<< "\"";}StringRef CPUName;StringRef ExpectedArch;uint64_t ExpectedFlags;};class CSKYCPUTestFixture : public ::testing::TestWithParam<CSKYCPUTestParams> {};TEST_P(CSKYCPUTestFixture, CSKYCPUTests) {auto params = GetParam();CSKY::ArchKind AK = CSKY::parseCPUArch(params.CPUName);EXPECT_EQ(params.ExpectedArch, CSKY::getArchName(AK));uint64_t default_extensions = CSKY::getDefaultExtensions(params.CPUName);EXPECT_PRED_FORMAT2(AssertSameExtensionFlags, params.ExpectedFlags,default_extensions);}// Note that we include CSKY::AEK_NONE even when there are other extensions// we expect. This is because the default extensions for a CPU are the sum// of the default extensions for its architecture and for the CPU.// So if a CPU has no extra extensions, it adds AEK_NONE.INSTANTIATE_TEST_SUITE_P(CSKYCPUTests, CSKYCPUTestFixture,::testing::Values(CSKYCPUTestParams("ck801", "ck801",CSKY::AEK_NONE | CSKY::MAEK_E1 | CSKY::AEK_TRUST),CSKYCPUTestParams("ck801t", "ck801",CSKY::AEK_NONE | CSKY::MAEK_E1 | CSKY::AEK_TRUST),CSKYCPUTestParams("e801", "ck801",CSKY::AEK_NONE | CSKY::MAEK_E1 | CSKY::AEK_TRUST),CSKYCPUTestParams("ck802", "ck802",CSKY::AEK_NONE | CSKY::MAEK_E2 | CSKY::AEK_TRUST |CSKY::AEK_NVIC),CSKYCPUTestParams("ck802t", "ck802",CSKY::AEK_NONE | CSKY::MAEK_E2 | CSKY::AEK_TRUST |CSKY::AEK_NVIC),CSKYCPUTestParams("ck802j", "ck802",CSKY::AEK_JAVA | CSKY::MAEK_E2 | CSKY::AEK_TRUST |CSKY::AEK_NVIC),CSKYCPUTestParams("e802", "ck802",CSKY::AEK_NONE | CSKY::MAEK_E2 | CSKY::AEK_TRUST |CSKY::AEK_NVIC),CSKYCPUTestParams("e802t", "ck802",CSKY::AEK_NONE | CSKY::MAEK_E2 | CSKY::AEK_TRUST |CSKY::AEK_NVIC),CSKYCPUTestParams("s802", "ck802",CSKY::AEK_NONE | CSKY::MAEK_E2 | CSKY::AEK_TRUST |CSKY::AEK_NVIC),CSKYCPUTestParams("s802t", "ck802",CSKY::AEK_NONE | CSKY::MAEK_E2 | CSKY::AEK_TRUST |CSKY::AEK_NVIC),CSKYCPUTestParams("ck803", "ck803",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803h", "ck803",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803t", "ck803",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ht", "ck803",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803f", "ck803",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803fh", "ck803",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803e", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803eh", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803et", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803eht", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ef", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efh", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ft", "ck803",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803eft", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efht", "ck803",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803r1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803r2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803r3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803hr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803hr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803hr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803tr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803tr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803tr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803htr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803htr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803htr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803fr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803fr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803fr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803fhr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803fhr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803fhr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803er1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803er2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803er3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ehr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ehr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ehr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803etr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803etr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803etr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ehtr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ehtr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ehtr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efhr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efhr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efhr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ftr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ftr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803ftr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803eftr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803eftr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803eftr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efhtr1", "ck803",CSKY::MAEK_3E3R1 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efhtr2", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803efhtr3", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_DSPV2 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("s803", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("s803t", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("e803", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("e803t", "ck803",CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803s", "ck803s",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803st", "ck803s",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803se", "ck803s",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803sf", "ck803s",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803sef", "ck803s",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck803seft", "ck803s",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV),CSKYCPUTestParams("ck804", "ck804",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804h", "ck804",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804t", "ck804",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804ht", "ck804",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804f", "ck804",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804fh", "ck804",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804e", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804eh", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804et", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804eht", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804ef", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804efh", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804ft", "ck804",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804eft", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("ck804efht", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("e804d", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("e804dt", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_HIGHREG | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("e804f", "ck804",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("e804ft", "ck804",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3),CSKYCPUTestParams("e804df", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("e804dft", "ck804",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_HIGHREG |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("ck805", "ck805",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::AEK_HIGHREG |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck805e", "ck805",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::AEK_HIGHREG | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3 | CSKY::AEK_VDSPV2 |CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck805f", "ck805",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::AEK_HIGHREG |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck805t", "ck805",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::AEK_HIGHREG |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck805ef", "ck805",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::AEK_HIGHREG |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck805et", "ck805",CSKY::AEK_DSPV2 | CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::AEK_HIGHREG | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3 | CSKY::AEK_VDSPV2 |CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck805ft", "ck805",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::AEK_HIGHREG |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck805eft", "ck805",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::AEK_DSPV2 |CSKY::AEK_3E3R1 | CSKY::AEK_3E3R3 |CSKY::MAEK_2E3 | CSKY::AEK_MP | CSKY::AEK_TRUST |CSKY::AEK_NVIC | CSKY::AEK_HWDIV |CSKY::AEK_HIGHREG | CSKY::MAEK_3E3R2 |CSKY::AEK_3E3R3 | CSKY::AEK_VDSPV2 |CSKY::AEK_VDSP2E3),CSKYCPUTestParams("i805", "ck805",CSKY::AEK_NONE | CSKY::MAEK_2E3 | CSKY::AEK_MP |CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::AEK_HIGHREG |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E3),CSKYCPUTestParams("i805f", "ck805",CSKY::AEK_FPUV2SF | CSKY::AEK_FLOATE1 |CSKY::AEK_FLOAT1E3 | CSKY::MAEK_2E3 |CSKY::AEK_MP | CSKY::AEK_TRUST | CSKY::AEK_NVIC |CSKY::AEK_HWDIV | CSKY::AEK_HIGHREG |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E3),CSKYCPUTestParams("ck807", "ck807",CSKY::AEK_NONE | CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck807e", "ck807",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck807f", "ck807",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::AEK_FLOAT1E3 |CSKY::AEK_FLOAT3E4 | CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST | CSKY::AEK_HWDIV |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP | CSKY::AEK_NVIC |CSKY::AEK_CACHE),CSKYCPUTestParams("ck807ef", "ck807",CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::AEK_FLOAT1E3 |CSKY::AEK_FLOAT3E4 | CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST | CSKY::AEK_HWDIV |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP | CSKY::AEK_NVIC |CSKY::AEK_CACHE),CSKYCPUTestParams("c807", "ck807",CSKY::AEK_NONE | CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("c807f", "ck807",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::AEK_FLOAT1E3 |CSKY::AEK_FLOAT3E4 | CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST | CSKY::AEK_HWDIV |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP | CSKY::AEK_NVIC |CSKY::AEK_CACHE),CSKYCPUTestParams("r807", "ck807",CSKY::AEK_NONE | CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("r807f", "ck807",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::AEK_FLOAT1E3 |CSKY::AEK_FLOAT3E4 | CSKY::MAEK_3E7 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST | CSKY::AEK_HWDIV |CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP | CSKY::AEK_NVIC |CSKY::AEK_CACHE),CSKYCPUTestParams("ck810e", "ck810",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810et", "ck810",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810ef", "ck810",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810eft", "ck810",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810", "ck810",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810f", "ck810",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810t", "ck810",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810ft", "ck810",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("c810", "ck810",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("c810t", "ck810",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE),CSKYCPUTestParams("ck810v", "ck810v",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::AEK_VDSPV1),CSKYCPUTestParams("ck810ev", "ck810v",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::AEK_VDSPV1),CSKYCPUTestParams("ck810tv", "ck810v",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::AEK_VDSPV1),CSKYCPUTestParams("ck810etv", "ck810v",CSKY::AEK_NONE | CSKY::MAEK_7E10 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP |CSKY::AEK_DSP1E2 | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::AEK_VDSPV1),CSKYCPUTestParams("c810v", "ck810v",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE | CSKY::AEK_VDSPV1),CSKYCPUTestParams("ck810fv", "ck810v",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE | CSKY::AEK_VDSPV1),CSKYCPUTestParams("ck810efv", "ck810v",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE | CSKY::AEK_VDSPV1),CSKYCPUTestParams("ck810ftv", "ck810v",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE | CSKY::AEK_VDSPV1),CSKYCPUTestParams("c810tv", "ck810v",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE | CSKY::AEK_VDSPV1),CSKYCPUTestParams("c810eftv", "ck810v",CSKY::AEK_FPUV2SF | CSKY::AEK_FPUV2DF | CSKY::AEK_FDIVDU |CSKY::AEK_FLOATE1 | CSKY::AEK_FLOAT1E2 | CSKY::MAEK_7E10 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_EDSP | CSKY::AEK_DSP1E2 |CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE | CSKY::AEK_VDSPV1),CSKYCPUTestParams("ck860", "ck860",CSKY::AEK_NONE | CSKY::MAEK_10E60 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("ck860f", "ck860",CSKY::AEK_FPUV3HI | CSKY::AEK_FPUV3HF | CSKY::AEK_FPUV3SF |CSKY::AEK_FPUV3DF | CSKY::AEK_FLOAT7E60 | CSKY::MAEK_10E60 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::AEK_HARDTP | CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("c860", "ck860",CSKY::AEK_FPUV3HI | CSKY::AEK_FPUV3HF | CSKY::AEK_FPUV3SF |CSKY::AEK_FPUV3DF | CSKY::AEK_FLOAT7E60 | CSKY::MAEK_10E60 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::AEK_HARDTP | CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3),CSKYCPUTestParams("ck860v", "ck860v",CSKY::AEK_NONE | CSKY::MAEK_10E60 | CSKY::MAEK_MP |CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_DSPE60 |CSKY::AEK_HIGHREG | CSKY::AEK_HARDTP |CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 |CSKY::AEK_VDSPV2 | CSKY::AEK_VDSP2E60F),CSKYCPUTestParams("ck860fv", "ck860v",CSKY::AEK_FPUV3HI | CSKY::AEK_FPUV3HF | CSKY::AEK_FPUV3SF |CSKY::AEK_FPUV3DF | CSKY::AEK_FLOAT7E60 | CSKY::MAEK_10E60 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::AEK_HARDTP | CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_VDSPV2 |CSKY::AEK_VDSP2E60F),CSKYCPUTestParams("c860v", "ck860v",CSKY::AEK_FPUV3HI | CSKY::AEK_FPUV3HF | CSKY::AEK_FPUV3SF |CSKY::AEK_FPUV3DF | CSKY::AEK_FLOAT7E60 | CSKY::MAEK_10E60 |CSKY::MAEK_MP | CSKY::MAEK_MP1E2 | CSKY::AEK_TRUST |CSKY::AEK_HWDIV | CSKY::AEK_DSPE60 | CSKY::AEK_HIGHREG |CSKY::AEK_HARDTP | CSKY::AEK_NVIC | CSKY::AEK_CACHE |CSKY::MAEK_3E3R2 | CSKY::AEK_3E3R3 | CSKY::AEK_VDSPV2 |CSKY::AEK_VDSP2E60F)));static constexpr unsigned NumCSKYCPUArchs = 145;TEST(TargetParserTest, testCSKYCPUArchList) {SmallVector<StringRef, NumCSKYCPUArchs> List;CSKY::fillValidCPUArchList(List);// No list exists for these in this test suite, so ensure all are// valid, and match the expected 'magic' count.EXPECT_EQ(List.size(), NumCSKYCPUArchs);for (StringRef CPU : List) {EXPECT_NE(CSKY::parseCPUArch(CPU), CSKY::ArchKind::INVALID);}}TEST(TargetParserTest, testInvalidCSKYArch) {auto InvalidArchStrings = {"ckv", "ckv99", "nock"};for (const char *InvalidArch : InvalidArchStrings)EXPECT_EQ(CSKY::parseArch(InvalidArch), CSKY::ArchKind::INVALID);}bool testCSKYArch(StringRef Arch, StringRef DefaultCPU) {CSKY::ArchKind AK = CSKY::parseArch(Arch);bool Result = (AK != CSKY::ArchKind::INVALID);Result &= CSKY::getDefaultCPU(Arch).equals(DefaultCPU);return Result;}TEST(TargetParserTest, testCSKYArch) {EXPECT_TRUE(testCSKYArch("ck801", "ck801"));EXPECT_TRUE(testCSKYArch("ck802", "ck802"));EXPECT_TRUE(testCSKYArch("ck803", "ck803"));EXPECT_TRUE(testCSKYArch("ck803s", "ck803s"));EXPECT_TRUE(testCSKYArch("ck804", "ck804"));EXPECT_TRUE(testCSKYArch("ck805", "ck805"));EXPECT_TRUE(testCSKYArch("ck807", "ck807"));EXPECT_TRUE(testCSKYArch("ck810", "ck810"));EXPECT_TRUE(testCSKYArch("ck810v", "ck810v"));EXPECT_TRUE(testCSKYArch("ck860", "ck860"));EXPECT_TRUE(testCSKYArch("ck860v", "ck860v"));}TEST(TargetParserTest, CSKYFPUVersion) {for (CSKY::CSKYFPUKind FK = static_cast<CSKY::CSKYFPUKind>(0);FK <= CSKY::CSKYFPUKind::FK_LAST;FK = static_cast<CSKY::CSKYFPUKind>(static_cast<unsigned>(FK) + 1))if (FK == CSKY::FK_LAST || CSKY::getFPUName(FK) == "invalid")EXPECT_EQ(CSKY::FPUVersion::NONE, CSKY::getFPUVersion(FK));elseEXPECT_NE(CSKY::FPUVersion::NONE, CSKY::getFPUVersion(FK));}TEST(TargetParserTest, CSKYExtensionFeatures) {std::map<uint64_t, std::vector<StringRef>> Extensions;for (auto &Ext : CSKY::CSKYARCHExtNames) {if (Ext.Feature && Ext.NegFeature)Extensions[Ext.ID] = {StringRef(Ext.Feature), StringRef(Ext.NegFeature)};}std::vector<StringRef> Features;EXPECT_FALSE(CSKY::getExtensionFeatures(CSKY::AEK_INVALID, Features));for (auto &E : Extensions) {CSKY::getExtensionFeatures(E.first, Features);EXPECT_TRUE(llvm::is_contained(Features, E.second.at(0)));}EXPECT_EQ(Extensions.size(), Features.size());}TEST(TargetParserTest, CSKYFPUFeatures) {std::vector<StringRef> Features;for (CSKY::CSKYFPUKind FK = static_cast<CSKY::CSKYFPUKind>(0);FK <= CSKY::CSKYFPUKind::FK_LAST;FK = static_cast<CSKY::CSKYFPUKind>(static_cast<unsigned>(FK) + 1)) {if (FK == CSKY::FK_INVALID || FK >= CSKY::FK_LAST)EXPECT_FALSE(CSKY::getFPUFeatures(FK, Features));elseEXPECT_TRUE(CSKY::getFPUFeatures(FK, Features));}}TEST(TargetParserTest, CSKYArchExtFeature) {const char *ArchExt[][4] = {{"fpuv2_sf", "nofpuv2_sf", "+fpuv2_sf", "-fpuv2_sf"},{"fpuv2_df", "nofpuv2_df", "+fpuv2_df", "-fpuv2_df"},{"fdivdu", "nofdivdu", "+fdivdu", "-fdivdu"},{"fpuv3_hi", "nofpuv3_hi", "+fpuv3_hi", "-fpuv3_hi"},{"fpuv3_hf", "nofpuv3_hf", "+fpuv3_hf", "-fpuv3_hf"},{"fpuv2_df", "nofpuv2_df", "+fpuv2_df", "-fpuv2_df"},{"fpuv3_sf", "nofpuv3_sf", "+fpuv3_sf", "-fpuv3_sf"},{"fpuv3_df", "nofpuv3_df", "+fpuv3_df", "-fpuv3_df"},{"floate1", "nofloate1", "+floate1", "-floate1"},{"float1e2", "nofloat1e2", "+float1e2", "-float1e2"},{"float1e3", "nofloat1e3", "+float1e3", "-float1e3"},{"float3e4", "nofloat3e4", "+float3e4", "-float3e4"},{"float7e60", "nofloat7e60", "+float7e60", "-float7e60"},{"hwdiv", "nohwdiv", "+hwdiv", "-hwdiv"},{"multiple_stld", "nomultiple_stld", "+multiple_stld", "-multiple_stld"},{"pushpop", "nopushpop", "+pushpop", "-pushpop"},{"edsp", "noedsp", "+edsp", "-edsp"},{"dsp1e2", "nodsp1e2", "+dsp1e2", "-dsp1e2"},{"dspe60", "nodspe60", "+dspe60", "-dspe60"},{"dspv2", "nodspv2", "+dspv2", "-dspv2"},{"dsp_silan", "nodsp_silan", "+dsp_silan", "-dsp_silan"},{"elrw", "noelrw", "+elrw", "-elrw"},{"trust", "notrust", "+trust", "-trust"},{"java", "nojava", "+java", "-java"},{"cache", "nocache", "+cache", "-cache"},{"nvic", "nonvic", "+nvic", "-nvic"},{"doloop", "nodoloop", "+doloop", "-doloop"},{"high-registers", "nohigh-registers", "+high-registers","-high-registers"},{"smart", "nosmart", "+smart", "-smart"},{"vdsp2e3", "novdsp2e3", "+vdsp2e3", "-vdsp2e3"},{"vdsp2e60f", "novdsp2e60f", "+vdsp2e60f", "-vdsp2e60f"},{"vdspv2", "novdspv2", "+vdspv2", "-vdspv2"},{"hard-tp", "nohard-tp", "+hard-tp", "-hard-tp"},{"soft-tp", "nosoft-tp", "+soft-tp", "-soft-tp"},{"istack", "noistack", "+istack", "-istack"},{"constpool", "noconstpool", "+constpool", "-constpool"},{"stack-size", "nostack-size", "+stack-size", "-stack-size"},{"ccrt", "noccrt", "+ccrt", "-ccrt"},{"vdspv1", "novdspv1", "+vdspv1", "-vdspv1"},{"e1", "noe1", "+e1", "-e1"},{"e2", "noe2", "+e2", "-e2"},{"2e3", "no2e3", "+2e3", "-2e3"},{"mp", "nomp", "+mp", "-mp"},{"3e3r1", "no3e3r1", "+3e3r1", "-3e3r1"},{"3e3r2", "no3e3r2", "+3e3r2", "-3e3r2"},{"3e3r3", "no3e3r3", "+3e3r3", "-3e3r3"},{"3e7", "no3e7", "+3e7", "-3e7"},{"mp1e2", "nomp1e2", "+mp1e2", "-mp1e2"},{"7e10", "no7e10", "+7e10", "-7e10"},{"10e60", "no10e60", "+10e60", "-10e60"},};for (unsigned i = 0; i < array_lengthof(ArchExt); i++) {EXPECT_EQ(StringRef(ArchExt[i][2]), CSKY::getArchExtFeature(ArchExt[i][0]));EXPECT_EQ(StringRef(ArchExt[i][3]), CSKY::getArchExtFeature(ArchExt[i][1]));}}} // namespace
//===----- unittests/CSKYAttributeParserTest.cpp --------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/CSKYAttributeParser.h"#include "llvm/Support/ARMBuildAttributes.h"#include "llvm/Support/ELFAttributes.h"#include "gtest/gtest.h"#include <string>using namespace llvm;struct CSKYAttributeSection {unsigned Tag;struct {unsigned IntValue;const char *StringValue;} Value;CSKYAttributeSection(unsigned tag, unsigned value) : Tag(tag) {Value.IntValue = value;}CSKYAttributeSection(unsigned tag, const char *value) : Tag(tag) {Value.StringValue = value;}void writeInt(raw_ostream &OS) {OS.flush();// Format version.OS << 'A'// uint32_t = VendorHeaderSize + TagHeaderSize + ContentsSize.<< (uint8_t)16 << (uint8_t)0 << (uint8_t)0<< (uint8_t)0// CurrentVendor.<< "csky"<< '\0'// ELFAttrs::File.<< (uint8_t)1// uint32_t = TagHeaderSize + ContentsSize.<< (uint8_t)6 << (uint8_t)0 << (uint8_t)0<< (uint8_t)0// Tag<< (uint8_t)Tag// IntValue<< (uint8_t)Value.IntValue;}void writeString(raw_ostream &OS) {OS.flush();// Format version.OS << 'A'// uint32_t = VendorHeaderSize + TagHeaderSize + ContentsSize.<< (uint8_t)(16 + strlen(Value.StringValue)) << (uint8_t)0 << (uint8_t)0<< (uint8_t)0// CurrentVendor.<< "csky"<< '\0'// ELFAttrs::File.<< (uint8_t)1// uint32_t = TagHeaderSize + ContentsSize.<< (uint8_t)(6 + strlen(Value.StringValue)) << (uint8_t)0 << (uint8_t)0<< (uint8_t)0// Tag<< (uint8_t)Tag// StringValue<< Value.StringValue << '\0';}};static bool testAttributeInt(unsigned Tag, unsigned Value, unsigned ExpectedTag,unsigned ExpectedValue) {std::string buffer;raw_string_ostream OS(buffer);CSKYAttributeSection Section(Tag, Value);Section.writeInt(OS);ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(OS.str().c_str()),OS.str().size());CSKYAttributeParser Parser;cantFail(Parser.parse(Bytes, support::little));Optional<unsigned> Attr = Parser.getAttributeValue(ExpectedTag);return Attr && *Attr == ExpectedValue;}static bool testAttributeString(unsigned Tag, const char *Value,unsigned ExpectedTag,const char *ExpectedValue) {std::string buffer;raw_string_ostream OS(buffer);CSKYAttributeSection Section(Tag, Value);Section.writeString(OS);ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(OS.str().c_str()),OS.str().size());CSKYAttributeParser Parser;cantFail(Parser.parse(Bytes, support::little));Optional<StringRef> Attr = Parser.getAttributeString(ExpectedTag);return Attr && *Attr == ExpectedValue;}static void testParseError(unsigned Tag, unsigned Value, const char *msg) {std::string buffer;raw_string_ostream OS(buffer);CSKYAttributeSection Section(Tag, Value);Section.writeInt(OS);ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(OS.str().c_str()),OS.str().size());CSKYAttributeParser Parser;Error e = Parser.parse(Bytes, support::little);EXPECT_STREQ(toString(std::move(e)).c_str(), msg);}static bool testTagString(unsigned Tag, const char *name) {return ELFAttrs::attrTypeAsString(Tag, CSKYAttrs::getCSKYAttributeTags()).str() == name;}TEST(ArchName, testAttribute) {EXPECT_TRUE(testTagString(4, "Tag_CSKY_ARCH_NAME"));EXPECT_TRUE(testAttributeString(4, "ck860", CSKYAttrs::CSKY_ARCH_NAME, "ck860"));EXPECT_FALSE(testAttributeString(4, "ck86", CSKYAttrs::CSKY_ARCH_NAME, "ck60"));}TEST(CPUName, testAttribute) {EXPECT_TRUE(testTagString(5, "Tag_CSKY_CPU_NAME"));EXPECT_TRUE(testAttributeString(5, "ck860fv", CSKYAttrs::CSKY_CPU_NAME, "ck860fv"));EXPECT_FALSE(testAttributeString(5, "ck860", CSKYAttrs::CSKY_CPU_NAME, "ck860fv"));}TEST(DSPVersion, testAttribute) {EXPECT_TRUE(testTagString(8, "Tag_CSKY_DSP_VERSION"));EXPECT_TRUE(testAttributeInt(8, 1, CSKYAttrs::CSKY_DSP_VERSION,CSKYAttrs::DSP_VERSION_EXTENSION));EXPECT_TRUE(testAttributeInt(8, 2, CSKYAttrs::CSKY_DSP_VERSION,CSKYAttrs::DSP_VERSION_2));EXPECT_FALSE(testAttributeInt(8, 0, CSKYAttrs::CSKY_DSP_VERSION,CSKYAttrs::DSP_VERSION_EXTENSION));testParseError(8, 3, "unknown Tag_CSKY_DSP_VERSION value: 3");}TEST(VDSPVersion, testAttribute) {EXPECT_TRUE(testTagString(9, "Tag_CSKY_VDSP_VERSION"));EXPECT_TRUE(testAttributeInt(9, 1, CSKYAttrs::CSKY_VDSP_VERSION,CSKYAttrs::VDSP_VERSION_1));EXPECT_TRUE(testAttributeInt(9, 2, CSKYAttrs::CSKY_VDSP_VERSION,CSKYAttrs::VDSP_VERSION_2));EXPECT_FALSE(testAttributeInt(9, 0, CSKYAttrs::CSKY_VDSP_VERSION,CSKYAttrs::VDSP_VERSION_2));testParseError(9, 3, "unknown Tag_CSKY_VDSP_VERSION value: 3");}TEST(FPUVersion, testAttribute) {EXPECT_TRUE(testTagString(16, "Tag_CSKY_FPU_VERSION"));EXPECT_TRUE(testAttributeInt(16, 1, CSKYAttrs::CSKY_FPU_VERSION,CSKYAttrs::FPU_VERSION_1));EXPECT_TRUE(testAttributeInt(16, 2, CSKYAttrs::CSKY_FPU_VERSION,CSKYAttrs::FPU_VERSION_2));EXPECT_TRUE(testAttributeInt(16, 3, CSKYAttrs::CSKY_FPU_VERSION,CSKYAttrs::FPU_VERSION_3));EXPECT_FALSE(testAttributeInt(16, 0, CSKYAttrs::CSKY_FPU_VERSION,CSKYAttrs::FPU_VERSION_3));testParseError(16, 4, "unknown Tag_CSKY_FPU_VERSION value: 4");}TEST(FPUABI, testAttribute) {EXPECT_TRUE(testTagString(17, "Tag_CSKY_FPU_ABI"));EXPECT_TRUE(testAttributeInt(17, 1, CSKYAttrs::CSKY_FPU_ABI,CSKYAttrs::FPU_ABI_SOFT));EXPECT_TRUE(testAttributeInt(17, 2, CSKYAttrs::CSKY_FPU_ABI,CSKYAttrs::FPU_ABI_SOFTFP));EXPECT_TRUE(testAttributeInt(17, 3, CSKYAttrs::CSKY_FPU_ABI,CSKYAttrs::FPU_ABI_HARD));EXPECT_FALSE(testAttributeInt(17, 0, CSKYAttrs::CSKY_FPU_ABI,CSKYAttrs::FPU_ABI_HARD));testParseError(17, 4, "unknown Tag_CSKY_FPU_ABI value: 4");}TEST(FPURounding, testAttribute) {EXPECT_TRUE(testTagString(18, "Tag_CSKY_FPU_ROUNDING"));EXPECT_TRUE(testAttributeInt(18, 0, CSKYAttrs::CSKY_FPU_ROUNDING, CSKYAttrs::NONE));EXPECT_TRUE(testAttributeInt(18, 1, CSKYAttrs::CSKY_FPU_ROUNDING, CSKYAttrs::NEEDED));testParseError(18, 2, "unknown Tag_CSKY_FPU_ROUNDING value: 2");}TEST(FPUDenormal, testAttribute) {EXPECT_TRUE(testTagString(19, "Tag_CSKY_FPU_DENORMAL"));EXPECT_TRUE(testAttributeInt(19, 0, CSKYAttrs::CSKY_FPU_DENORMAL, CSKYAttrs::NONE));EXPECT_TRUE(testAttributeInt(19, 1, CSKYAttrs::CSKY_FPU_DENORMAL, CSKYAttrs::NEEDED));testParseError(19, 2, "unknown Tag_CSKY_FPU_DENORMAL value: 2");}TEST(FPUException, testAttribute) {EXPECT_TRUE(testTagString(20, "Tag_CSKY_FPU_EXCEPTION"));EXPECT_TRUE(testAttributeInt(20, 0, CSKYAttrs::CSKY_FPU_EXCEPTION, CSKYAttrs::NONE));EXPECT_TRUE(testAttributeInt(20, 1, CSKYAttrs::CSKY_FPU_EXCEPTION,CSKYAttrs::NEEDED));testParseError(20, 2, "unknown Tag_CSKY_FPU_EXCEPTION value: 2");}TEST(FPUNumberModule, testAttribute) {EXPECT_TRUE(testTagString(21, "Tag_CSKY_FPU_NUMBER_MODULE"));EXPECT_TRUE(testAttributeString(21, "IEEE 754", CSKYAttrs::CSKY_FPU_NUMBER_MODULE, "IEEE 754"));EXPECT_FALSE(testAttributeString(21, "IEEE 755", CSKYAttrs::CSKY_FPU_NUMBER_MODULE, "IEEE 754"));}TEST(FPUHardFP, testAttribute) {EXPECT_TRUE(testTagString(22, "Tag_CSKY_FPU_HARDFP"));EXPECT_TRUE(testAttributeInt(22, 1, CSKYAttrs::CSKY_FPU_HARDFP,CSKYAttrs::FPU_HARDFP_HALF));EXPECT_TRUE(testAttributeInt(22, 2, CSKYAttrs::CSKY_FPU_HARDFP,CSKYAttrs::FPU_HARDFP_SINGLE));EXPECT_TRUE(testAttributeInt(22, 4, CSKYAttrs::CSKY_FPU_HARDFP,CSKYAttrs::FPU_HARDFP_DOUBLE));EXPECT_FALSE(testAttributeInt(22, 3, CSKYAttrs::CSKY_FPU_HARDFP,CSKYAttrs::FPU_HARDFP_DOUBLE));testParseError(22, 0, "unknown Tag_CSKY_FPU_HARDFP value: 0");testParseError(22, 8, "unknown Tag_CSKY_FPU_HARDFP value: 8");}
//===- llvm/unittest/Support/CRCTest.cpp - CRC tests ----------------------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for CRC calculation functions.////===----------------------------------------------------------------------===//#include "llvm/Support/CRC.h"#include "llvm/ADT/StringExtras.h"#include "gtest/gtest.h"#include <stdlib.h>using namespace llvm;namespace {TEST(CRCTest, CRC32) {EXPECT_EQ(0x414FA339U, llvm::crc32(arrayRefFromStringRef("The quick brown fox jumps over the lazy dog")));// CRC-32/ISO-HDLC test vector// http://reveng.sourceforge.net/crc-catalogue/17plus.htm#crc.cat.crc-32cEXPECT_EQ(0xCBF43926U, llvm::crc32(arrayRefFromStringRef("123456789")));// Check the CRC-32 of each byte value, exercising all of CRCTable.for (int i = 0; i < 256; i++) {// Compute CRCTable[i] using Hacker's Delight (2nd ed.) Figure 14-7.uint32_t crc = i;for (int j = 7; j >= 0; j--) {uint32_t mask = -(crc & 1);crc = (crc >> 1) ^ (0xEDB88320 & mask);}// CRCTable[i] is the CRC-32 of i without the initial and final bit flips.uint8_t byte = i;EXPECT_EQ(crc, ~llvm::crc32(0xFFFFFFFFU, byte));}EXPECT_EQ(0x00000000U, llvm::crc32(arrayRefFromStringRef("")));}#if (SIZE_MAX > UINT32_MAX) && defined(EXPENSIVE_CHECKS)TEST(CRCTest, LargeCRC32) {// Check that crc32 can handle inputs with sizes larger than 32 bits.size_t TestSize = (size_t)UINT32_MAX + 42;uint8_t *TestData = (uint8_t*)calloc(TestSize, 1);if (!TestData)return;// Test expectation generated with:// $ truncate --size=`echo 2^32-1+42 | bc` /tmp/foo// $ crc32 /tmp/fooEXPECT_EQ(0xE46F28FBU, llvm::crc32(makeArrayRef(TestData, TestSize)));free(TestData);}#endif} // end anonymous namespace
set(LLVM_LINK_COMPONENTSSupport)add_llvm_unittest(SupportTestsAddressRangeTest.cppAlignmentTest.cppAlignOfTest.cppAllocatorTest.cppAnnotationsTest.cppARMAttributeParser.cppArrayRecyclerTest.cppBase64Test.cppBinaryStreamTest.cppBLAKE3Test.cppBlockFrequencyTest.cppBranchProbabilityTest.cppCachePruningTest.cppCrashRecoveryTest.cppCasting.cppCheckedArithmeticTest.cppChrono.cppCommandLineTest.cppCompressionTest.cppConvertUTFTest.cppCRCTest.cppCSKYAttributeParserTest.cppCSKYTargetParserTest.cppDataExtractorTest.cppDebugTest.cppDebugCounterTest.cppDJBTest.cppEndianStreamTest.cppEndianTest.cppELFAttributeParserTest.cppErrnoTest.cppErrorOrTest.cppErrorTest.cppExtensibleRTTITest.cppFileCollectorTest.cppFileOutputBufferTest.cppFileUtilitiesTest.cppFormatVariadicTest.cppFSUniqueIDTest.cppGlobPatternTest.cppHashBuilderTest.cppHost.cppIndexedAccessorTest.cppInstructionCostTest.cppItaniumManglingCanonicalizerTest.cppJSONTest.cppKnownBitsTest.cppLEB128Test.cppLinearPolyBaseTest.cppLineIteratorTest.cppLockFileManagerTest.cppMatchersTest.cppMD5Test.cppManagedStatic.cppMathExtrasTest.cppMemoryBufferRefTest.cppMemoryBufferTest.cppMemoryTest.cppNativeFormatTests.cppOptimizedStructLayoutTest.cppParallelTest.cppPath.cppProcessTest.cppProgramTest.cppRegexTest.cppReverseIterationTest.cppReplaceFileTest.cppRISCVAttributeParserTest.cppScaledNumberTest.cppScopedPrinterTest.cppSHA256.cppSourceMgrTest.cppSpecialCaseListTest.cppSuffixTreeTest.cppSwapByteOrderTest.cppSymbolRemappingReaderTest.cppTarWriterTest.cppTargetParserTest.cppTaskQueueTest.cppThreadLocalTest.cppThreadPool.cppThreading.cppTimerTest.cppToolOutputFileTest.cppTypeNameTest.cppTypeTraitsTest.cppTrailingObjectsTest.cppTrigramIndexTest.cppUnicodeTest.cppVersionTupleTest.cppVirtualFileSystemTest.cppWithColorTest.cppYAMLIOTest.cppYAMLParserTest.cppbuffer_ostream_test.cppformatted_raw_ostream_test.cppraw_fd_stream_test.cppraw_ostream_test.cppraw_pwrite_stream_test.cppraw_sha1_ostream_test.cppxxhashTest.cpp)target_link_libraries(SupportTests PRIVATE LLVMTestingSupport)# Disable all warning for AlignOfTest.cpp,# as it does things intentionally, and there is no reliable way of# disabling all warnings for all the compilers by using pragmas.# Don't disable on MSVC, because all incriminated warnings are already disabled# in source; and because we would otherwise see this warning:# cl : Command line warning D9025: overriding '/W4' with '/w'if(NOT MSVC)set_source_files_properties(AlignOfTest.cpp PROPERTIES COMPILE_FLAGS -w)endif()if(MSVC)if( CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.14 )# Since VS2017 15.8, the following snippet: Failed<CustomSubError>()# generates a warning:# \svn\llvm\utils\unittest\googlemock\include\gmock\gmock-matchers.h(186):# warning C5046: 'testing::MatcherInterface<T>::~MatcherInterface': Symbol involving type with internal linkage not definedset_source_files_properties(ErrorTest.cpp PROPERTIES COMPILE_FLAGS -wd5046)endif()endif()# ManagedStatic.cpp uses <pthread>.target_link_libraries(SupportTests PRIVATE LLVMTestingSupport ${LLVM_PTHREAD_LIB})if(NOT LLVM_INTEGRATED_CRT_ALLOC)# The test doesn't pass when using a custom allocator, PR47881.add_subdirectory(DynamicLibrary)endif()add_subdirectory(CommandLineInit)
//===- unittest/Support/BranchProbabilityTest.cpp - BranchProbability tests -=////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/BranchProbability.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {void PrintTo(BranchProbability P, ::std::ostream *os) {*os << P.getNumerator() << "/" << P.getDenominator();}}namespace {typedef BranchProbability BP;TEST(BranchProbabilityTest, Accessors) {EXPECT_EQ(306783378u, BP(1, 7).getNumerator());EXPECT_EQ(1u << 31, BP(1, 7).getDenominator());EXPECT_EQ(0u, BP::getZero().getNumerator());EXPECT_EQ(1u << 31, BP::getZero().getDenominator());EXPECT_EQ(1u << 31, BP::getOne().getNumerator());EXPECT_EQ(1u << 31, BP::getOne().getDenominator());}TEST(BranchProbabilityTest, Operators) {EXPECT_TRUE(BP(1, 7) < BP(2, 7));EXPECT_TRUE(BP(1, 7) < BP(1, 4));EXPECT_TRUE(BP(5, 7) < BP(3, 4));EXPECT_FALSE(BP(1, 7) < BP(1, 7));EXPECT_FALSE(BP(1, 7) < BP(2, 14));EXPECT_FALSE(BP(4, 7) < BP(1, 2));EXPECT_FALSE(BP(4, 7) < BP(3, 7));EXPECT_FALSE(BP(1, 7) > BP(2, 7));EXPECT_FALSE(BP(1, 7) > BP(1, 4));EXPECT_FALSE(BP(5, 7) > BP(3, 4));EXPECT_FALSE(BP(1, 7) > BP(1, 7));EXPECT_FALSE(BP(1, 7) > BP(2, 14));EXPECT_TRUE(BP(4, 7) > BP(1, 2));EXPECT_TRUE(BP(4, 7) > BP(3, 7));EXPECT_TRUE(BP(1, 7) <= BP(2, 7));EXPECT_TRUE(BP(1, 7) <= BP(1, 4));EXPECT_TRUE(BP(5, 7) <= BP(3, 4));EXPECT_TRUE(BP(1, 7) <= BP(1, 7));EXPECT_TRUE(BP(1, 7) <= BP(2, 14));EXPECT_FALSE(BP(4, 7) <= BP(1, 2));EXPECT_FALSE(BP(4, 7) <= BP(3, 7));EXPECT_FALSE(BP(1, 7) >= BP(2, 7));EXPECT_FALSE(BP(1, 7) >= BP(1, 4));EXPECT_FALSE(BP(5, 7) >= BP(3, 4));EXPECT_TRUE(BP(1, 7) >= BP(1, 7));EXPECT_TRUE(BP(1, 7) >= BP(2, 14));EXPECT_TRUE(BP(4, 7) >= BP(1, 2));EXPECT_TRUE(BP(4, 7) >= BP(3, 7));EXPECT_FALSE(BP(1, 7) == BP(2, 7));EXPECT_FALSE(BP(1, 7) == BP(1, 4));EXPECT_FALSE(BP(5, 7) == BP(3, 4));EXPECT_TRUE(BP(1, 7) == BP(1, 7));EXPECT_TRUE(BP(1, 7) == BP(2, 14));EXPECT_FALSE(BP(4, 7) == BP(1, 2));EXPECT_FALSE(BP(4, 7) == BP(3, 7));EXPECT_TRUE(BP(1, 7) != BP(2, 7));EXPECT_TRUE(BP(1, 7) != BP(1, 4));EXPECT_TRUE(BP(5, 7) != BP(3, 4));EXPECT_FALSE(BP(1, 7) != BP(1, 7));EXPECT_FALSE(BP(1, 7) != BP(2, 14));EXPECT_TRUE(BP(4, 7) != BP(1, 2));EXPECT_TRUE(BP(4, 7) != BP(3, 7));EXPECT_TRUE(BP(1, 7) == BP(2, 14));EXPECT_TRUE(BP(1, 7) == BP(3, 21));EXPECT_TRUE(BP(5, 7) == BP(25, 35));EXPECT_TRUE(BP(99999998, 100000000) < BP(99999999, 100000000));EXPECT_TRUE(BP(4, 8) == BP(400000000, 800000000));}TEST(BranchProbabilityTest, MoreOperators) {BP A(4, 5);BP B(4U << 29, 5U << 29);BP C(3, 4);EXPECT_TRUE(A == B);EXPECT_FALSE(A != B);EXPECT_FALSE(A < B);EXPECT_FALSE(A > B);EXPECT_TRUE(A <= B);EXPECT_TRUE(A >= B);EXPECT_FALSE(B == C);EXPECT_TRUE(B != C);EXPECT_FALSE(B < C);EXPECT_TRUE(B > C);EXPECT_FALSE(B <= C);EXPECT_TRUE(B >= C);BP BigZero(0, UINT32_MAX);BP BigOne(UINT32_MAX, UINT32_MAX);EXPECT_FALSE(BigZero == BigOne);EXPECT_TRUE(BigZero != BigOne);EXPECT_TRUE(BigZero < BigOne);EXPECT_FALSE(BigZero > BigOne);EXPECT_TRUE(BigZero <= BigOne);EXPECT_FALSE(BigZero >= BigOne);}TEST(BranchProbabilityTest, ArithmeticOperators) {BP Z(0, 1);BP O(1, 1);BP H(1, 2);BP Q(1, 4);BP Q3(3, 4);EXPECT_EQ(Z + O, O);EXPECT_EQ(H + Z, H);EXPECT_EQ(H + H, O);EXPECT_EQ(Q + H, Q3);EXPECT_EQ(Q + Q3, O);EXPECT_EQ(H + Q3, O);EXPECT_EQ(Q3 + Q3, O);EXPECT_EQ(Z - O, Z);EXPECT_EQ(O - Z, O);EXPECT_EQ(O - H, H);EXPECT_EQ(O - Q, Q3);EXPECT_EQ(Q3 - H, Q);EXPECT_EQ(Q - H, Z);EXPECT_EQ(Q - Q3, Z);EXPECT_EQ(Z * O, Z);EXPECT_EQ(H * H, Q);EXPECT_EQ(Q * O, Q);EXPECT_EQ(O * O, O);EXPECT_EQ(Z * Z, Z);EXPECT_EQ(Z * 3, Z);EXPECT_EQ(Q * 3, Q3);EXPECT_EQ(H * 3, O);EXPECT_EQ(Q3 * 2, O);EXPECT_EQ(O * UINT32_MAX, O);EXPECT_EQ(Z / 4, Z);EXPECT_EQ(O / 4, Q);EXPECT_EQ(Q3 / 3, Q);EXPECT_EQ(H / 2, Q);EXPECT_EQ(O / 2, H);EXPECT_EQ(H / UINT32_MAX, Z);BP Min(1, 1u << 31);EXPECT_EQ(O / UINT32_MAX, Z);EXPECT_EQ(Min * UINT32_MAX, O);}TEST(BranchProbabilityTest, getCompl) {EXPECT_EQ(BP(5, 7), BP(2, 7).getCompl());EXPECT_EQ(BP(2, 7), BP(5, 7).getCompl());EXPECT_EQ(BP::getZero(), BP(7, 7).getCompl());EXPECT_EQ(BP::getOne(), BP(0, 7).getCompl());}TEST(BranchProbabilityTest, scale) {// Multiply by 1.0.EXPECT_EQ(UINT64_MAX, BP(1, 1).scale(UINT64_MAX));EXPECT_EQ(UINT64_MAX, BP(7, 7).scale(UINT64_MAX));EXPECT_EQ(UINT32_MAX, BP(1, 1).scale(UINT32_MAX));EXPECT_EQ(UINT32_MAX, BP(7, 7).scale(UINT32_MAX));EXPECT_EQ(0u, BP(1, 1).scale(0));EXPECT_EQ(0u, BP(7, 7).scale(0));// Multiply by 0.0.EXPECT_EQ(0u, BP(0, 1).scale(UINT64_MAX));EXPECT_EQ(0u, BP(0, 1).scale(UINT64_MAX));EXPECT_EQ(0u, BP(0, 1).scale(0));auto Two63 = UINT64_C(1) << 63;auto Two31 = UINT64_C(1) << 31;// Multiply by 0.5.EXPECT_EQ(Two63 - 1, BP(1, 2).scale(UINT64_MAX));// Big fractions.EXPECT_EQ(1u, BP(Two31, UINT32_MAX).scale(2));EXPECT_EQ(Two31, BP(Two31, UINT32_MAX).scale(Two31 * 2));EXPECT_EQ(9223372036854775807ULL, BP(Two31, UINT32_MAX).scale(UINT64_MAX));// High precision.EXPECT_EQ(UINT64_C(9223372045444710399),BP(Two31 + 1, UINT32_MAX - 2).scale(UINT64_MAX));}TEST(BranchProbabilityTest, scaleByInverse) {// Divide by 1.0.EXPECT_EQ(UINT64_MAX, BP(1, 1).scaleByInverse(UINT64_MAX));EXPECT_EQ(UINT64_MAX, BP(7, 7).scaleByInverse(UINT64_MAX));EXPECT_EQ(UINT32_MAX, BP(1, 1).scaleByInverse(UINT32_MAX));EXPECT_EQ(UINT32_MAX, BP(7, 7).scaleByInverse(UINT32_MAX));EXPECT_EQ(0u, BP(1, 1).scaleByInverse(0));EXPECT_EQ(0u, BP(7, 7).scaleByInverse(0));auto MAX_DENOMINATOR = BP::getDenominator();// Divide by something very small.EXPECT_EQ(UINT64_MAX, BP(1, UINT32_MAX).scaleByInverse(UINT64_MAX));EXPECT_EQ(uint64_t(UINT32_MAX) * MAX_DENOMINATOR,BP(1, MAX_DENOMINATOR).scaleByInverse(UINT32_MAX));EXPECT_EQ(MAX_DENOMINATOR, BP(1, MAX_DENOMINATOR).scaleByInverse(1));auto Two63 = UINT64_C(1) << 63;auto Two31 = UINT64_C(1) << 31;// Divide by 0.5.EXPECT_EQ(UINT64_MAX - 1, BP(1, 2).scaleByInverse(Two63 - 1));EXPECT_EQ(UINT64_MAX, BP(1, 2).scaleByInverse(Two63));// Big fractions.EXPECT_EQ(2u, BP(Two31, UINT32_MAX).scaleByInverse(1));EXPECT_EQ(2u, BP(Two31 - 1, UINT32_MAX).scaleByInverse(1));EXPECT_EQ(Two31 * 2, BP(Two31, UINT32_MAX).scaleByInverse(Two31));EXPECT_EQ(Two31 * 2, BP(Two31 - 1, UINT32_MAX).scaleByInverse(Two31));EXPECT_EQ(UINT64_MAX, BP(Two31, UINT32_MAX).scaleByInverse(Two63 + Two31));// High precision. The exact answers to these are close to the successors of// the floor. If we were rounding, these would round up.EXPECT_EQ(UINT64_C(18446744060824649767),BP(Two31 + 2, UINT32_MAX - 2).scaleByInverse(UINT64_C(9223372047592194056)));EXPECT_EQ(UINT64_C(18446744060824649739),BP(Two31 + 1, UINT32_MAX).scaleByInverse(Two63 + Two31));}TEST(BranchProbabilityTest, scaleBruteForce) {struct {uint64_t Num;uint32_t Prob[2];uint64_t Result;} Tests[] = {// Data for scaling that results in <= 64 bit division.{ 0x1423e2a50ULL, { 0x64819521, 0x7765dd13 }, 0x10f418888ULL },{ 0x35ef14ceULL, { 0x28ade3c7, 0x304532ae }, 0x2d73c33bULL },{ 0xd03dbfbe24ULL, { 0x790079, 0xe419f3 }, 0x6e776fc2c4ULL },{ 0x21d67410bULL, { 0x302a9dc2, 0x3ddb4442 }, 0x1a5948fd4ULL },{ 0x8664aeadULL, { 0x3d523513, 0x403523b1 }, 0x805a04cfULL },{ 0x201db0cf4ULL, { 0x35112a7b, 0x79fc0c74 }, 0xdf8b07f8ULL },{ 0x13f1e4430aULL, { 0x21c92bf, 0x21e63aae }, 0x13e0cba26ULL },{ 0x16c83229ULL, { 0x3793f66f, 0x53180dea }, 0xf3ce7b6ULL },{ 0xc62415be8ULL, { 0x9cc4a63, 0x4327ae9b }, 0x1ce8b71c1ULL },{ 0x6fac5e434ULL, { 0xe5f9170, 0x1115e10b }, 0x5df23dd4cULL },{ 0x1929375f2ULL, { 0x3a851375, 0x76c08456 }, 0xc662b083ULL },{ 0x243c89db6ULL, { 0x354ebfc0, 0x450ef197 }, 0x1bf8c1663ULL },{ 0x310e9b31aULL, { 0x1b1b8acf, 0x2d3629f0 }, 0x1d69c93f9ULL },{ 0xa1fae921dULL, { 0xa7a098c, 0x10469f44 }, 0x684413d6eULL },{ 0xc1582d957ULL, { 0x498e061, 0x59856bc }, 0x9edc5f4ecULL },{ 0x57cfee75ULL, { 0x1d061dc3, 0x7c8bfc17 }, 0x1476a220ULL },{ 0x139220080ULL, { 0x294a6c71, 0x2a2b07c9 }, 0x1329e1c75ULL },{ 0x1665d353cULL, { 0x7080db5, 0xde0d75c }, 0xb590d9faULL },{ 0xe8f14541ULL, { 0x5188e8b2, 0x736527ef }, 0xa4971be5ULL },{ 0x2f4775f29ULL, { 0x254ef0fe, 0x435fcf50 }, 0x1a2e449c1ULL },{ 0x27b85d8d7ULL, { 0x304c8220, 0x5de678f2 }, 0x146e3befbULL },{ 0x1d362e36bULL, { 0x36c85b12, 0x37a66f55 }, 0x1cc19b8e7ULL },{ 0x155fd48c7ULL, { 0xf5894d, 0x1256108 }, 0x11e383604ULL },{ 0xb5db2d15ULL, { 0x39bb26c5, 0x5bdcda3e }, 0x72499259ULL },{ 0x153990298ULL, { 0x48921c09, 0x706eb817 }, 0xdb3268e7ULL },{ 0x28a7c3ed7ULL, { 0x1f776fd7, 0x349f7a70 }, 0x184f73ae2ULL },{ 0x724dbeabULL, { 0x1bd149f5, 0x253a085e }, 0x5569c0b3ULL },{ 0xd8f0c513ULL, { 0x18c8cc4c, 0x1b72bad0 }, 0xc3e30642ULL },{ 0x17ce3dcbULL, { 0x1e4c6260, 0x233b359e }, 0x1478f4afULL },{ 0x1ce036ce0ULL, { 0x29e3c8af, 0x5318dd4a }, 0xe8e76195ULL },{ 0x1473ae2aULL, { 0x29b897ba, 0x2be29378 }, 0x13718185ULL },{ 0x1dd41aa68ULL, { 0x3d0a4441, 0x5a0e8f12 }, 0x1437b6bbfULL },{ 0x1b49e4a53ULL, { 0x3430c1fe, 0x5a204aed }, 0xfcd6852fULL },{ 0x217941b19ULL, { 0x12ced2bd, 0x21b68310 }, 0x12aca65b1ULL },{ 0xac6a4dc8ULL, { 0x3ed68da8, 0x6fdca34c }, 0x60da926dULL },{ 0x1c503a4e7ULL, { 0xfcbbd32, 0x11e48d17 }, 0x18fec7d37ULL },{ 0x1c885855ULL, { 0x213e919d, 0x25941897 }, 0x193de742ULL },{ 0x29b9c168eULL, { 0x2b644aea, 0x45725ee7 }, 0x1a122e5d4ULL },{ 0x806a33f2ULL, { 0x30a80a23, 0x5063733a }, 0x4db9a264ULL },{ 0x282afc96bULL, { 0x143ae554, 0x1a9863ff }, 0x1e8de5204ULL },// Data for scaling that results in > 64 bit division.{ 0x23ca5f2f672ca41cULL, { 0xecbc641, 0x111373f7 }, 0x1f0301e5c76869c6ULL },{ 0x5e4f2468142265e3ULL, { 0x1ddf5837, 0x32189233 }, 0x383ca7bad6053ac9ULL },{ 0x277a1a6f6b266bf6ULL, { 0x415d81a8, 0x61eb5e1e }, 0x1a5a3e1d1c9e8540ULL },{ 0x1bdbb49a237035cbULL, { 0xea5bf17, 0x1d25ffb3 }, 0xdffc51c5cb51cf1ULL },{ 0x2bce6d29b64fb8ULL, { 0x3bfd5631, 0x7525c9bb }, 0x166ebedd9581fdULL },{ 0x3a02116103df5013ULL, { 0x2ee18a83, 0x3299aea8 }, 0x35be89227276f105ULL },{ 0x7b5762390799b18cULL, { 0x12f8e5b9, 0x2563bcd4 }, 0x3e960077695655a3ULL },{ 0x69cfd72537021579ULL, { 0x4c35f468, 0x6a40feee }, 0x4be4cb38695a4f30ULL },{ 0x49dfdf835120f1c1ULL, { 0x8cb3759, 0x559eb891 }, 0x79663f6e3c8d8f6ULL },{ 0x74b5be5c27676381ULL, { 0x47e4c5e0, 0x7c7b19ff }, 0x4367d2dfb22b3265ULL },{ 0x4f50f97075e7f431ULL, { 0x9a50a17, 0x11cd1185 }, 0x2af952b30374f382ULL },{ 0x2f8b0d712e393be4ULL, { 0x1487e386, 0x15aa356e }, 0x2d0df3649b2b19fcULL },{ 0x224c1c75999d3deULL, { 0x3b2df0ea, 0x4523b100 }, 0x1d5b481d160dd8bULL },{ 0x2bcbcea22a399a76ULL, { 0x28b58212, 0x48dd013e }, 0x187814d0610c8a56ULL },{ 0x1dbfca91257cb2d1ULL, { 0x1a8c04d9, 0x5e92502c }, 0x859cf7d19e83ad0ULL },{ 0x7f20039b57cda935ULL, { 0xeccf651, 0x323f476e }, 0x25720cd9054634bdULL },{ 0x40512c6a586aa087ULL, { 0x113b0423, 0x398c9eab }, 0x1341c03dbb662054ULL },{ 0x63d802693f050a11ULL, { 0xf50cdd6, 0xfce2a44 }, 0x60c0177b667a4feaULL },{ 0x2d956b422838de77ULL, { 0xb2d345b, 0x1321e557 }, 0x1aa0ed16b094575cULL },{ 0x5a1cdf0c1657bc91ULL, { 0x1d77bb0c, 0x1f991ff1 }, 0x54097ee9907290eaULL },{ 0x3801b26d7e00176bULL, { 0xeed25da, 0x1a819d8b }, 0x1f89e96a616b9abeULL },{ 0x37655e74338e1e45ULL, { 0x300e170a, 0x5a1595fe }, 0x1d8cfb55ff6a6dbcULL },{ 0x7b38703f2a84e6ULL, { 0x66d9053, 0xc79b6b9 }, 0x3f7d4c91b9afb9ULL },{ 0x2245063c0acb3215ULL, { 0x30ce2f5b, 0x610e7271 }, 0x113b916455fe2560ULL },{ 0x6bc195877b7b8a7eULL, { 0x392004aa, 0x4a24e60c }, 0x530594fabfc81cc3ULL },{ 0x40a3fde23c7b43dbULL, { 0x4e712195, 0x6553e56e }, 0x320a799bc205c78dULL },{ 0x1d3dfc2866fbccbaULL, { 0x5075b517, 0x5fc42245 }, 0x18917f00745cb781ULL },{ 0x19aeb14045a61121ULL, { 0x1bf6edec, 0x707e2f4b }, 0x6626672aa2ba10aULL },{ 0x44ff90486c531e9fULL, { 0x66598a, 0x8a90dc }, 0x32f6f2b097001598ULL },{ 0x3f3e7121092c5bcbULL, { 0x1c754df7, 0x5951a1b9 }, 0x14267f50d4971583ULL },{ 0x60e2dafb7e50a67eULL, { 0x4d96c66e, 0x65bd878d }, 0x49e317155d75e883ULL },{ 0x656286667e0e6e29ULL, { 0x9d971a2, 0xacda23b }, 0x5c6ee3159e1deac3ULL },{ 0x1114e0974255d507ULL, { 0x1c693, 0x2d6ff }, 0xaae42e4be5f9f8dULL },{ 0x508c8baf3a70ff5aULL, { 0x3b26b779, 0x6ad78745 }, 0x2c983876178ed5b1ULL },{ 0x5b47bc666bf1f9cfULL, { 0x10a87ed6, 0x187d358a }, 0x3e1767153bea720aULL },{ 0x50954e3744460395ULL, { 0x7a42263, 0xcdaa048 }, 0x2fe739f0944a023cULL },{ 0x20020b406550dd8fULL, { 0x3318539, 0x42eead0 }, 0x186f326307c0d985ULL },{ 0x5bcb0b872439ffd5ULL, { 0x6f61fb2, 0x9af7344 }, 0x41fa1e3c47f0f80dULL },{ 0x7a670f365db87a53ULL, { 0x417e102, 0x3bb54c67 }, 0x8642a551d0f41b0ULL },{ 0x1ef0db1e7bab1cd0ULL, { 0x2b60cf38, 0x4188f78f }, 0x147ae0d63fc0575aULL }};for (const auto &T : Tests) {EXPECT_EQ(T.Result, BP(T.Prob[0], T.Prob[1]).scale(T.Num));}}TEST(BranchProbabilityTest, NormalizeProbabilities) {const auto UnknownProb = BranchProbability::getUnknown();{SmallVector<BranchProbability, 2> Probs{{0, 1}, {0, 1}};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[1].getNumerator());}{SmallVector<BranchProbability, 2> Probs{{0, 1}, {1, 1}};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(0u, Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator(), Probs[1].getNumerator());}{SmallVector<BranchProbability, 2> Probs{{1, 100}, {1, 100}};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[1].getNumerator());}{SmallVector<BranchProbability, 2> Probs{{1, 1}, {1, 1}};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[1].getNumerator());}{SmallVector<BranchProbability, 3> Probs{{1, 1}, {1, 1}, {1, 1}};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(BranchProbability::getDenominator() / 3 + 1,Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 3 + 1,Probs[1].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 3 + 1,Probs[2].getNumerator());}{SmallVector<BranchProbability, 2> Probs{{0, 1}, UnknownProb};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(0U, Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator(), Probs[1].getNumerator());}{SmallVector<BranchProbability, 2> Probs{{1, 1}, UnknownProb};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(BranchProbability::getDenominator(), Probs[0].getNumerator());EXPECT_EQ(0U, Probs[1].getNumerator());}{SmallVector<BranchProbability, 2> Probs{{1, 2}, UnknownProb};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 2, Probs[1].getNumerator());}{SmallVector<BranchProbability, 4> Probs{{1, 2}, {1, 2}, {1, 2}, UnknownProb};BranchProbability::normalizeProbabilities(Probs.begin(), Probs.end());EXPECT_EQ(BranchProbability::getDenominator() / 3 + 1,Probs[0].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 3 + 1,Probs[1].getNumerator());EXPECT_EQ(BranchProbability::getDenominator() / 3 + 1,Probs[2].getNumerator());EXPECT_EQ(0U, Probs[3].getNumerator());}}}
//===- unittests/Support/BlockFrequencyTest.cpp - BlockFrequency tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/BlockFrequency.h"#include "llvm/Support/BranchProbability.h"#include "llvm/Support/DataTypes.h"#include "gtest/gtest.h"#include <climits>using namespace llvm;namespace {TEST(BlockFrequencyTest, OneToZero) {BlockFrequency Freq(1);BranchProbability Prob(UINT32_MAX / 3, UINT32_MAX);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 0u);Freq = BlockFrequency(1);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 0u);}TEST(BlockFrequencyTest, OneToOne) {BlockFrequency Freq(1);BranchProbability Prob(UINT32_MAX, UINT32_MAX);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 1u);Freq = BlockFrequency(1);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 1u);}TEST(BlockFrequencyTest, ThreeToOne) {BlockFrequency Freq(3);BranchProbability Prob(3000000, 9000000);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 1u);Freq = BlockFrequency(3);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 1u);}TEST(BlockFrequencyTest, MaxToHalfMax) {BlockFrequency Freq(UINT64_MAX);BranchProbability Prob(UINT32_MAX / 2, UINT32_MAX);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 9223372036854775807ULL);Freq = BlockFrequency(UINT64_MAX);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), 9223372036854775807ULL);}TEST(BlockFrequencyTest, BigToBig) {const uint64_t Big = 387246523487234346LL;const uint32_t P = 123456789;BlockFrequency Freq(Big);BranchProbability Prob(P, P);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), Big);Freq = BlockFrequency(Big);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), Big);}TEST(BlockFrequencyTest, MaxToMax) {BlockFrequency Freq(UINT64_MAX);BranchProbability Prob(UINT32_MAX, UINT32_MAX);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), UINT64_MAX);// This additionally makes sure if we have a value equal to our saturating// value, we do not signal saturation if the result equals said value, but// saturating does not occur.Freq = BlockFrequency(UINT64_MAX);Freq *= Prob;EXPECT_EQ(Freq.getFrequency(), UINT64_MAX);}TEST(BlockFrequencyTest, Subtract) {BlockFrequency Freq1(0), Freq2(1);EXPECT_EQ((Freq1 - Freq2).getFrequency(), 0u);EXPECT_EQ((Freq2 - Freq1).getFrequency(), 1u);}TEST(BlockFrequency, Divide) {BlockFrequency Freq(0x3333333333333333ULL);Freq /= BranchProbability(1, 2);EXPECT_EQ(Freq.getFrequency(), 0x6666666666666666ULL);}TEST(BlockFrequencyTest, Saturate) {BlockFrequency Freq(0x3333333333333333ULL);Freq /= BranchProbability(100, 300);EXPECT_EQ(Freq.getFrequency(), 0x9999999866666668ULL);Freq /= BranchProbability(1, 2);EXPECT_EQ(Freq.getFrequency(), UINT64_MAX);Freq = 0x1000000000000000ULL;Freq /= BranchProbability(10000, 170000);EXPECT_EQ(Freq.getFrequency(), UINT64_MAX);// Try to cheat the multiplication overflow check.Freq = 0x00000001f0000001ull;Freq /= BranchProbability(1000, 0xf000000f);EXPECT_EQ(33527736066704712ULL, Freq.getFrequency());}TEST(BlockFrequencyTest, SaturatingRightShift) {BlockFrequency Freq(0x10080ULL);Freq >>= 2;EXPECT_EQ(Freq.getFrequency(), 0x4020ULL);Freq >>= 20;EXPECT_EQ(Freq.getFrequency(), 0x1ULL);}}
//===- llvm/unittest/Support/BinaryStreamTest.cpp -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Allocator.h"#include "llvm/Support/BinaryByteStream.h"#include "llvm/Support/BinaryItemStream.h"#include "llvm/Support/BinaryStreamArray.h"#include "llvm/Support/BinaryStreamReader.h"#include "llvm/Support/BinaryStreamRef.h"#include "llvm/Support/BinaryStreamWriter.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::support;namespace {class BrokenStream : public WritableBinaryStream {public:BrokenStream(MutableArrayRef<uint8_t> Data, endianness Endian, uint32_t Align): Data(Data), PartitionIndex(alignDown(Data.size() / 2, Align)),Endian(Endian) {}endianness getEndian() const override { return Endian; }Error readBytes(uint64_t Offset, uint64_t Size,ArrayRef<uint8_t> &Buffer) override {if (auto EC = checkOffsetForRead(Offset, Size))return EC;uint64_t S = startIndex(Offset);auto Ref = Data.drop_front(S);if (Ref.size() >= Size) {Buffer = Ref.take_front(Size);return Error::success();}uint64_t BytesLeft = Size - Ref.size();uint8_t *Ptr = Allocator.Allocate<uint8_t>(Size);::memcpy(Ptr, Ref.data(), Ref.size());::memcpy(Ptr + Ref.size(), Data.data(), BytesLeft);Buffer = makeArrayRef<uint8_t>(Ptr, Size);return Error::success();}Error readLongestContiguousChunk(uint64_t Offset,ArrayRef<uint8_t> &Buffer) override {if (auto EC = checkOffsetForRead(Offset, 1))return EC;uint64_t S = startIndex(Offset);Buffer = Data.drop_front(S);return Error::success();}uint64_t getLength() override { return Data.size(); }Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> SrcData) override {if (auto EC = checkOffsetForWrite(Offset, SrcData.size()))return EC;if (SrcData.empty())return Error::success();uint64_t S = startIndex(Offset);MutableArrayRef<uint8_t> Ref(Data);Ref = Ref.drop_front(S);if (Ref.size() >= SrcData.size()) {::memcpy(Ref.data(), SrcData.data(), SrcData.size());return Error::success();}uint64_t BytesLeft = SrcData.size() - Ref.size();::memcpy(Ref.data(), SrcData.data(), Ref.size());::memcpy(&Data[0], SrcData.data() + Ref.size(), BytesLeft);return Error::success();}Error commit() override { return Error::success(); }private:uint64_t startIndex(uint64_t Offset) const {return (Offset + PartitionIndex) % Data.size();}uint64_t endIndex(uint64_t Offset, uint64_t Size) const {return (startIndex(Offset) + Size - 1) % Data.size();}// Buffer is organized like this:// -------------------------------------------------// | N/2 | N/2+1 | ... | N-1 | 0 | 1 | ... | N/2-1 |// -------------------------------------------------// So reads from the beginning actually come from the middle.MutableArrayRef<uint8_t> Data;uint32_t PartitionIndex = 0;endianness Endian;BumpPtrAllocator Allocator;};constexpr endianness Endians[] = {big, little, native};constexpr uint32_t NumEndians = llvm::array_lengthof(Endians);constexpr uint32_t NumStreams = 2 * NumEndians;class BinaryStreamTest : public testing::Test {public:BinaryStreamTest() {}void SetUp() override {Streams.clear();Streams.resize(NumStreams);for (uint32_t I = 0; I < NumStreams; ++I)Streams[I].IsContiguous = (I % 2 == 0);InputData.clear();OutputData.clear();}protected:struct StreamPair {bool IsContiguous;std::unique_ptr<BinaryStream> Input;std::unique_ptr<WritableBinaryStream> Output;};void initializeInput(ArrayRef<uint8_t> Input, uint32_t Align) {InputData = Input;BrokenInputData.resize(InputData.size());if (!Input.empty()) {uint64_t PartitionIndex = alignDown(InputData.size() / 2, Align);uint64_t RightBytes = InputData.size() - PartitionIndex;uint64_t LeftBytes = PartitionIndex;if (RightBytes > 0)::memcpy(&BrokenInputData[PartitionIndex], Input.data(), RightBytes);if (LeftBytes > 0)::memcpy(&BrokenInputData[0], Input.data() + RightBytes, LeftBytes);}for (uint32_t I = 0; I < NumEndians; ++I) {auto InByteStream =std::make_unique<BinaryByteStream>(InputData, Endians[I]);auto InBrokenStream = std::make_unique<BrokenStream>(BrokenInputData, Endians[I], Align);Streams[I * 2].Input = std::move(InByteStream);Streams[I * 2 + 1].Input = std::move(InBrokenStream);}}void initializeOutput(uint64_t Size, uint32_t Align) {OutputData.resize(Size);BrokenOutputData.resize(Size);for (uint32_t I = 0; I < NumEndians; ++I) {Streams[I * 2].Output =std::make_unique<MutableBinaryByteStream>(OutputData, Endians[I]);Streams[I * 2 + 1].Output = std::make_unique<BrokenStream>(BrokenOutputData, Endians[I], Align);}}void initializeOutputFromInput(uint32_t Align) {for (uint32_t I = 0; I < NumEndians; ++I) {Streams[I * 2].Output =std::make_unique<MutableBinaryByteStream>(InputData, Endians[I]);Streams[I * 2 + 1].Output = std::make_unique<BrokenStream>(BrokenInputData, Endians[I], Align);}}void initializeInputFromOutput(uint32_t Align) {for (uint32_t I = 0; I < NumEndians; ++I) {Streams[I * 2].Input =std::make_unique<BinaryByteStream>(OutputData, Endians[I]);Streams[I * 2 + 1].Input = std::make_unique<BrokenStream>(BrokenOutputData, Endians[I], Align);}}std::vector<uint8_t> InputData;std::vector<uint8_t> BrokenInputData;std::vector<uint8_t> OutputData;std::vector<uint8_t> BrokenOutputData;std::vector<StreamPair> Streams;};// Tests that a we can read from a BinaryByteStream without a StreamReader.TEST_F(BinaryStreamTest, BinaryByteStreamBounds) {std::vector<uint8_t> InputData = {1, 2, 3, 4, 5};initializeInput(InputData, 1);for (auto &Stream : Streams) {ArrayRef<uint8_t> Buffer;// 1. If the read fits it should work.ASSERT_EQ(InputData.size(), Stream.Input->getLength());ASSERT_THAT_ERROR(Stream.Input->readBytes(2, 1, Buffer), Succeeded());EXPECT_EQ(makeArrayRef(InputData).slice(2, 1), Buffer);ASSERT_THAT_ERROR(Stream.Input->readBytes(0, 4, Buffer), Succeeded());EXPECT_EQ(makeArrayRef(InputData).slice(0, 4), Buffer);// 2. Reading past the bounds of the input should fail.EXPECT_THAT_ERROR(Stream.Input->readBytes(4, 2, Buffer), Failed());}}TEST_F(BinaryStreamTest, StreamRefBounds) {std::vector<uint8_t> InputData = {1, 2, 3, 4, 5};initializeInput(InputData, 1);for (const auto &Stream : Streams) {ArrayRef<uint8_t> Buffer;BinaryStreamRef Ref(*Stream.Input);// Read 1 byte from offset 2 should workASSERT_EQ(InputData.size(), Ref.getLength());ASSERT_THAT_ERROR(Ref.readBytes(2, 1, Buffer), Succeeded());EXPECT_EQ(makeArrayRef(InputData).slice(2, 1), Buffer);// Reading everything from offset 2 on.ASSERT_THAT_ERROR(Ref.readLongestContiguousChunk(2, Buffer), Succeeded());if (Stream.IsContiguous)EXPECT_EQ(makeArrayRef(InputData).slice(2), Buffer);elseEXPECT_FALSE(Buffer.empty());// Reading 6 bytes from offset 0 is too big.EXPECT_THAT_ERROR(Ref.readBytes(0, 6, Buffer), Failed());EXPECT_THAT_ERROR(Ref.readLongestContiguousChunk(6, Buffer), Failed());// Reading 1 byte from offset 2 after dropping 1 byte is the same as reading// 1 byte from offset 3.Ref = Ref.drop_front(1);ASSERT_THAT_ERROR(Ref.readBytes(2, 1, Buffer), Succeeded());if (Stream.IsContiguous)EXPECT_EQ(makeArrayRef(InputData).slice(3, 1), Buffer);elseEXPECT_FALSE(Buffer.empty());// Reading everything from offset 2 on after dropping 1 byte.ASSERT_THAT_ERROR(Ref.readLongestContiguousChunk(2, Buffer), Succeeded());if (Stream.IsContiguous)EXPECT_EQ(makeArrayRef(InputData).slice(3), Buffer);elseEXPECT_FALSE(Buffer.empty());// Reading 2 bytes from offset 2 after dropping 2 bytes is the same as// reading 2 bytes from offset 4, and should fail.Ref = Ref.drop_front(1);EXPECT_THAT_ERROR(Ref.readBytes(2, 2, Buffer), Failed());// But if we read the longest contiguous chunk instead, we should still// get the 1 byte at the end.ASSERT_THAT_ERROR(Ref.readLongestContiguousChunk(2, Buffer), Succeeded());EXPECT_EQ(makeArrayRef(InputData).take_back(), Buffer);}}TEST_F(BinaryStreamTest, StreamRefDynamicSize) {StringRef Strings[] = {"1", "2", "3", "4"};AppendingBinaryByteStream Stream(support::little);BinaryStreamWriter Writer(Stream);BinaryStreamReader Reader(Stream);const uint8_t *Byte;StringRef Str;// When the stream is empty, it should report a 0 length and we should get an// error trying to read even 1 byte from it.BinaryStreamRef ConstRef(Stream);EXPECT_EQ(0U, ConstRef.getLength());EXPECT_THAT_ERROR(Reader.readObject(Byte), Failed());// But if we write to it, its size should increase and we should be able to// read not just a byte, but the string that was written.EXPECT_THAT_ERROR(Writer.writeCString(Strings[0]), Succeeded());EXPECT_EQ(2U, ConstRef.getLength());EXPECT_THAT_ERROR(Reader.readObject(Byte), Succeeded());Reader.setOffset(0);EXPECT_THAT_ERROR(Reader.readCString(Str), Succeeded());EXPECT_EQ(Str, Strings[0]);// If we drop some bytes from the front, we should still track the length as// the// underlying stream grows.BinaryStreamRef Dropped = ConstRef.drop_front(1);EXPECT_EQ(1U, Dropped.getLength());EXPECT_THAT_ERROR(Writer.writeCString(Strings[1]), Succeeded());EXPECT_EQ(4U, ConstRef.getLength());EXPECT_EQ(3U, Dropped.getLength());// If we drop zero bytes from the back, we should continue tracking the// length.Dropped = Dropped.drop_back(0);EXPECT_THAT_ERROR(Writer.writeCString(Strings[2]), Succeeded());EXPECT_EQ(6U, ConstRef.getLength());EXPECT_EQ(5U, Dropped.getLength());// If we drop non-zero bytes from the back, we should stop tracking the// length.Dropped = Dropped.drop_back(1);EXPECT_THAT_ERROR(Writer.writeCString(Strings[3]), Succeeded());EXPECT_EQ(8U, ConstRef.getLength());EXPECT_EQ(4U, Dropped.getLength());}TEST_F(BinaryStreamTest, DropOperations) {std::vector<uint8_t> InputData = {1, 2, 3, 4, 5, 4, 3, 2, 1};auto RefData = makeArrayRef(InputData);initializeInput(InputData, 1);ArrayRef<uint8_t> Result;BinaryStreamRef Original(InputData, support::little);ASSERT_EQ(InputData.size(), Original.getLength());EXPECT_THAT_ERROR(Original.readBytes(0, InputData.size(), Result),Succeeded());EXPECT_EQ(RefData, Result);auto Dropped = Original.drop_front(2);EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result),Succeeded());EXPECT_EQ(RefData.drop_front(2), Result);Dropped = Original.drop_back(2);EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result),Succeeded());EXPECT_EQ(RefData.drop_back(2), Result);Dropped = Original.keep_front(2);EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result),Succeeded());EXPECT_EQ(RefData.take_front(2), Result);Dropped = Original.keep_back(2);EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result),Succeeded());EXPECT_EQ(RefData.take_back(2), Result);Dropped = Original.drop_symmetric(2);EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result),Succeeded());EXPECT_EQ(RefData.drop_front(2).drop_back(2), Result);}// Test that we can write to a BinaryStream without a StreamWriter.TEST_F(BinaryStreamTest, MutableBinaryByteStreamBounds) {std::vector<uint8_t> InputData = {'T', 'e', 's', 't', '\0'};initializeInput(InputData, 1);initializeOutput(InputData.size(), 1);// For every combination of input stream and output stream.for (auto &Stream : Streams) {ASSERT_EQ(InputData.size(), Stream.Input->getLength());// 1. Try two reads that are supposed to work. One from offset 0, and one// from the middle.uint32_t Offsets[] = {0, 3};for (auto Offset : Offsets) {uint64_t ExpectedSize = Stream.Input->getLength() - Offset;// Read everything from Offset until the end of the input data.ArrayRef<uint8_t> Data;ASSERT_THAT_ERROR(Stream.Input->readBytes(Offset, ExpectedSize, Data),Succeeded());ASSERT_EQ(ExpectedSize, Data.size());// Then write it to the destination.ASSERT_THAT_ERROR(Stream.Output->writeBytes(0, Data), Succeeded());// Then we read back what we wrote, it should match the corresponding// slice of the original input data.ArrayRef<uint8_t> Data2;ASSERT_THAT_ERROR(Stream.Output->readBytes(Offset, ExpectedSize, Data2),Succeeded());EXPECT_EQ(makeArrayRef(InputData).drop_front(Offset), Data2);}std::vector<uint8_t> BigData = {0, 1, 2, 3, 4};// 2. If the write is too big, it should fail.EXPECT_THAT_ERROR(Stream.Output->writeBytes(3, BigData), Failed());}}TEST_F(BinaryStreamTest, AppendingStream) {AppendingBinaryByteStream Stream(llvm::support::little);EXPECT_EQ(0U, Stream.getLength());std::vector<uint8_t> InputData = {'T', 'e', 's', 't', 'T', 'e', 's', 't'};auto Test = makeArrayRef(InputData).take_front(4);// Writing past the end of the stream is an error.EXPECT_THAT_ERROR(Stream.writeBytes(4, Test), Failed());// Writing exactly at the end of the stream is ok.EXPECT_THAT_ERROR(Stream.writeBytes(0, Test), Succeeded());EXPECT_EQ(Test, Stream.data());// And now that the end of the stream is where we couldn't write before, now// we can write.EXPECT_THAT_ERROR(Stream.writeBytes(4, Test), Succeeded());EXPECT_EQ(MutableArrayRef<uint8_t>(InputData), Stream.data());}// Test that FixedStreamArray works correctly.TEST_F(BinaryStreamTest, FixedStreamArray) {std::vector<uint32_t> Ints = {90823, 12908, 109823, 209823};ArrayRef<uint8_t> IntBytes(reinterpret_cast<uint8_t *>(Ints.data()),Ints.size() * sizeof(uint32_t));initializeInput(IntBytes, alignof(uint32_t));for (auto &Stream : Streams) {ASSERT_EQ(InputData.size(), Stream.Input->getLength());FixedStreamArray<uint32_t> Array(*Stream.Input);auto Iter = Array.begin();ASSERT_EQ(Ints[0], *Iter++);ASSERT_EQ(Ints[1], *Iter++);ASSERT_EQ(Ints[2], *Iter++);ASSERT_EQ(Ints[3], *Iter++);ASSERT_EQ(Array.end(), Iter);}}// Ensure FixedStreamArrayIterator::operator-> works.// Added for coverage of r302257.TEST_F(BinaryStreamTest, FixedStreamArrayIteratorArrow) {std::vector<std::pair<uint32_t, uint32_t>> Pairs = {{867, 5309}, {555, 1212}};ArrayRef<uint8_t> PairBytes(reinterpret_cast<uint8_t *>(Pairs.data()),Pairs.size() * sizeof(Pairs[0]));initializeInput(PairBytes, alignof(uint32_t));for (auto &Stream : Streams) {ASSERT_EQ(InputData.size(), Stream.Input->getLength());const FixedStreamArray<std::pair<uint32_t, uint32_t>> Array(*Stream.Input);auto Iter = Array.begin();ASSERT_EQ(Pairs[0].first, Iter->first);ASSERT_EQ(Pairs[0].second, Iter->second);++Iter;ASSERT_EQ(Pairs[1].first, Iter->first);ASSERT_EQ(Pairs[1].second, Iter->second);++Iter;ASSERT_EQ(Array.end(), Iter);}}// Test that VarStreamArray works correctly.TEST_F(BinaryStreamTest, VarStreamArray) {StringLiteral Strings("1. Test2. Longer Test3. Really Long Test4. Super ""Extra Longest Test Of All");ArrayRef<uint8_t> StringBytes(reinterpret_cast<const uint8_t *>(Strings.data()), Strings.size());initializeInput(StringBytes, 1);struct StringExtractor {public:Error operator()(BinaryStreamRef Stream, uint32_t &Len, StringRef &Item) {if (Index == 0)Len = strlen("1. Test");else if (Index == 1)Len = strlen("2. Longer Test");else if (Index == 2)Len = strlen("3. Really Long Test");elseLen = strlen("4. Super Extra Longest Test Of All");ArrayRef<uint8_t> Bytes;if (auto EC = Stream.readBytes(0, Len, Bytes))return EC;Item =StringRef(reinterpret_cast<const char *>(Bytes.data()), Bytes.size());++Index;return Error::success();}uint32_t Index = 0;};for (auto &Stream : Streams) {VarStreamArray<StringRef, StringExtractor> Array(*Stream.Input);auto Iter = Array.begin();ASSERT_EQ("1. Test", *Iter++);ASSERT_EQ("2. Longer Test", *Iter++);ASSERT_EQ("3. Really Long Test", *Iter++);ASSERT_EQ("4. Super Extra Longest Test Of All", *Iter++);ASSERT_EQ(Array.end(), Iter);}}TEST_F(BinaryStreamTest, StreamReaderBounds) {std::vector<uint8_t> Bytes;initializeInput(Bytes, 1);for (auto &Stream : Streams) {StringRef S;BinaryStreamReader Reader(*Stream.Input);EXPECT_EQ(0U, Reader.bytesRemaining());EXPECT_THAT_ERROR(Reader.readFixedString(S, 1), Failed());}Bytes.resize(5);initializeInput(Bytes, 1);for (auto &Stream : Streams) {StringRef S;BinaryStreamReader Reader(*Stream.Input);EXPECT_EQ(Bytes.size(), Reader.bytesRemaining());EXPECT_THAT_ERROR(Reader.readFixedString(S, 5), Succeeded());EXPECT_THAT_ERROR(Reader.readFixedString(S, 6), Failed());}}TEST_F(BinaryStreamTest, StreamReaderIntegers) {support::ulittle64_t Little{908234};support::ubig32_t Big{28907823};short NS = 2897;int NI = -89723;unsigned long NUL = 902309023UL;constexpr uint32_t Size =sizeof(Little) + sizeof(Big) + sizeof(NS) + sizeof(NI) + sizeof(NUL);initializeOutput(Size, alignof(support::ulittle64_t));initializeInputFromOutput(alignof(support::ulittle64_t));for (auto &Stream : Streams) {BinaryStreamWriter Writer(*Stream.Output);ASSERT_THAT_ERROR(Writer.writeObject(Little), Succeeded());ASSERT_THAT_ERROR(Writer.writeObject(Big), Succeeded());ASSERT_THAT_ERROR(Writer.writeInteger(NS), Succeeded());ASSERT_THAT_ERROR(Writer.writeInteger(NI), Succeeded());ASSERT_THAT_ERROR(Writer.writeInteger(NUL), Succeeded());const support::ulittle64_t *Little2;const support::ubig32_t *Big2;short NS2;int NI2;unsigned long NUL2;// 1. Reading fields individually.BinaryStreamReader Reader(*Stream.Input);ASSERT_THAT_ERROR(Reader.readObject(Little2), Succeeded());ASSERT_THAT_ERROR(Reader.readObject(Big2), Succeeded());ASSERT_THAT_ERROR(Reader.readInteger(NS2), Succeeded());ASSERT_THAT_ERROR(Reader.readInteger(NI2), Succeeded());ASSERT_THAT_ERROR(Reader.readInteger(NUL2), Succeeded());ASSERT_EQ(0U, Reader.bytesRemaining());EXPECT_EQ(Little, *Little2);EXPECT_EQ(Big, *Big2);EXPECT_EQ(NS, NS2);EXPECT_EQ(NI, NI2);EXPECT_EQ(NUL, NUL2);}}TEST_F(BinaryStreamTest, StreamReaderIntegerArray) {// 1. Arrays of integersstd::vector<int> Ints = {1, 2, 3, 4, 5};ArrayRef<uint8_t> IntBytes(reinterpret_cast<uint8_t *>(&Ints[0]),Ints.size() * sizeof(int));initializeInput(IntBytes, alignof(int));for (auto &Stream : Streams) {BinaryStreamReader Reader(*Stream.Input);ArrayRef<int> IntsRef;ASSERT_THAT_ERROR(Reader.readArray(IntsRef, Ints.size()), Succeeded());ASSERT_EQ(0U, Reader.bytesRemaining());EXPECT_EQ(makeArrayRef(Ints), IntsRef);Reader.setOffset(0);FixedStreamArray<int> FixedIntsRef;ASSERT_THAT_ERROR(Reader.readArray(FixedIntsRef, Ints.size()), Succeeded());ASSERT_EQ(0U, Reader.bytesRemaining());ASSERT_EQ(Ints, std::vector<int>(FixedIntsRef.begin(), FixedIntsRef.end()));}}TEST_F(BinaryStreamTest, StreamReaderEnum) {enum class MyEnum : int64_t { Foo = -10, Bar = 0, Baz = 10 };std::vector<MyEnum> Enums = {MyEnum::Bar, MyEnum::Baz, MyEnum::Foo};initializeOutput(Enums.size() * sizeof(MyEnum), alignof(MyEnum));initializeInputFromOutput(alignof(MyEnum));for (auto &Stream : Streams) {BinaryStreamWriter Writer(*Stream.Output);for (auto Value : Enums)ASSERT_THAT_ERROR(Writer.writeEnum(Value), Succeeded());BinaryStreamReader Reader(*Stream.Input);FixedStreamArray<MyEnum> FSA;for (size_t I = 0; I < Enums.size(); ++I) {MyEnum Value;ASSERT_THAT_ERROR(Reader.readEnum(Value), Succeeded());EXPECT_EQ(Enums[I], Value);}ASSERT_EQ(0U, Reader.bytesRemaining());}}TEST_F(BinaryStreamTest, StreamReaderULEB128) {std::vector<uint64_t> TestValues = {0, // Zero0x7F, // One byte0xFF, // One byte, all-ones0xAAAA, // Two bytes0xAAAAAAAA, // Four bytes0xAAAAAAAAAAAAAAAA, // Eight bytes0xffffffffffffffff // Eight bytess, all-ones};// Conservatively assume a 10-byte encoding for each of our LEB128s, with no// alignment requirement.initializeOutput(10 * TestValues.size(), 1);initializeInputFromOutput(1);for (auto &Stream : Streams) {// Write fields.BinaryStreamWriter Writer(*Stream.Output);for (const auto &Value : TestValues)ASSERT_THAT_ERROR(Writer.writeULEB128(Value), Succeeded());// Read fields.BinaryStreamReader Reader(*Stream.Input);std::vector<uint64_t> Results;Results.resize(TestValues.size());for (unsigned I = 0; I != TestValues.size(); ++I)ASSERT_THAT_ERROR(Reader.readULEB128(Results[I]), Succeeded());for (unsigned I = 0; I != TestValues.size(); ++I)EXPECT_EQ(TestValues[I], Results[I]);}}TEST_F(BinaryStreamTest, StreamReaderSLEB128) {std::vector<int64_t> TestValues = {0, // Zero0x7F, // One byte-0x7F, // One byte, negative0xFF, // One byte, all-ones0xAAAA, // Two bytes-0xAAAA, // Two bytes, negative0xAAAAAAAA, // Four bytes-0xAAAAAAAA, // Four bytes, negative0x2AAAAAAAAAAAAAAA, // Eight bytes-0x7ffffffffffffff // Eight bytess, negative};// Conservatively assume a 10-byte encoding for each of our LEB128s, with no// alignment requirement.initializeOutput(10 * TestValues.size(), 1);initializeInputFromOutput(1);for (auto &Stream : Streams) {// Write fields.BinaryStreamWriter Writer(*Stream.Output);for (const auto &Value : TestValues)ASSERT_THAT_ERROR(Writer.writeSLEB128(Value), Succeeded());// Read fields.BinaryStreamReader Reader(*Stream.Input);std::vector<int64_t> Results;Results.resize(TestValues.size());for (unsigned I = 0; I != TestValues.size(); ++I)ASSERT_THAT_ERROR(Reader.readSLEB128(Results[I]), Succeeded());for (unsigned I = 0; I != TestValues.size(); ++I)EXPECT_EQ(TestValues[I], Results[I]);}}TEST_F(BinaryStreamTest, StreamReaderObject) {struct Foo {int X;double Y;char Z;bool operator==(const Foo &Other) const {return X == Other.X && Y == Other.Y && Z == Other.Z;}};std::vector<Foo> Foos;Foos.push_back({-42, 42.42, 42});Foos.push_back({100, 3.1415, static_cast<char>(-89)});Foos.push_back({200, 2.718, static_cast<char>(-12) });const uint8_t *Bytes = reinterpret_cast<const uint8_t *>(&Foos[0]);initializeInput(makeArrayRef(Bytes, 3 * sizeof(Foo)), alignof(Foo));for (auto &Stream : Streams) {// 1. Reading object pointers.BinaryStreamReader Reader(*Stream.Input);const Foo *FPtrOut = nullptr;const Foo *GPtrOut = nullptr;const Foo *HPtrOut = nullptr;ASSERT_THAT_ERROR(Reader.readObject(FPtrOut), Succeeded());ASSERT_THAT_ERROR(Reader.readObject(GPtrOut), Succeeded());ASSERT_THAT_ERROR(Reader.readObject(HPtrOut), Succeeded());EXPECT_EQ(0U, Reader.bytesRemaining());EXPECT_EQ(Foos[0], *FPtrOut);EXPECT_EQ(Foos[1], *GPtrOut);EXPECT_EQ(Foos[2], *HPtrOut);}}TEST_F(BinaryStreamTest, StreamReaderStrings) {std::vector<uint8_t> Bytes = {'O', 'n', 'e', '\0', 'T', 'w', 'o','\0', 'T', 'h', 'r', 'e', 'e', '\0','F', 'o', 'u', 'r', '\0'};initializeInput(Bytes, 1);for (auto &Stream : Streams) {BinaryStreamReader Reader(*Stream.Input);StringRef S1;StringRef S2;StringRef S3;StringRef S4;ASSERT_THAT_ERROR(Reader.readCString(S1), Succeeded());ASSERT_THAT_ERROR(Reader.readCString(S2), Succeeded());ASSERT_THAT_ERROR(Reader.readCString(S3), Succeeded());ASSERT_THAT_ERROR(Reader.readCString(S4), Succeeded());ASSERT_EQ(0U, Reader.bytesRemaining());EXPECT_EQ("One", S1);EXPECT_EQ("Two", S2);EXPECT_EQ("Three", S3);EXPECT_EQ("Four", S4);S1 = S2 = S3 = S4 = "";Reader.setOffset(0);ASSERT_THAT_ERROR(Reader.readFixedString(S1, 3), Succeeded());ASSERT_THAT_ERROR(Reader.skip(1), Succeeded());ASSERT_THAT_ERROR(Reader.readFixedString(S2, 3), Succeeded());ASSERT_THAT_ERROR(Reader.skip(1), Succeeded());ASSERT_THAT_ERROR(Reader.readFixedString(S3, 5), Succeeded());ASSERT_THAT_ERROR(Reader.skip(1), Succeeded());ASSERT_THAT_ERROR(Reader.readFixedString(S4, 4), Succeeded());ASSERT_THAT_ERROR(Reader.skip(1), Succeeded());ASSERT_EQ(0U, Reader.bytesRemaining());EXPECT_EQ("One", S1);EXPECT_EQ("Two", S2);EXPECT_EQ("Three", S3);EXPECT_EQ("Four", S4);}}TEST_F(BinaryStreamTest, StreamWriterBounds) {initializeOutput(5, 1);for (auto &Stream : Streams) {BinaryStreamWriter Writer(*Stream.Output);// 1. Can write a string that exactly fills the buffer.EXPECT_EQ(5U, Writer.bytesRemaining());EXPECT_THAT_ERROR(Writer.writeFixedString("abcde"), Succeeded());EXPECT_EQ(0U, Writer.bytesRemaining());// 2. Can write an empty string even when you're fullEXPECT_THAT_ERROR(Writer.writeFixedString(""), Succeeded());EXPECT_THAT_ERROR(Writer.writeFixedString("a"), Failed());// 3. Can't write a string that is one character too long.Writer.setOffset(0);EXPECT_THAT_ERROR(Writer.writeFixedString("abcdef"), Failed());}}TEST_F(BinaryStreamTest, StreamWriterIntegerArrays) {// 3. Arrays of integersstd::vector<int> SourceInts = {1, 2, 3, 4, 5};ArrayRef<uint8_t> SourceBytes(reinterpret_cast<uint8_t *>(&SourceInts[0]),SourceInts.size() * sizeof(int));initializeInput(SourceBytes, alignof(int));initializeOutputFromInput(alignof(int));for (auto &Stream : Streams) {BinaryStreamReader Reader(*Stream.Input);BinaryStreamWriter Writer(*Stream.Output);ArrayRef<int> Ints;ArrayRef<int> Ints2;// First read them, then write them, then read them back.ASSERT_THAT_ERROR(Reader.readArray(Ints, SourceInts.size()), Succeeded());ASSERT_THAT_ERROR(Writer.writeArray(Ints), Succeeded());BinaryStreamReader ReaderBacker(*Stream.Output);ASSERT_THAT_ERROR(ReaderBacker.readArray(Ints2, SourceInts.size()),Succeeded());EXPECT_EQ(makeArrayRef(SourceInts), Ints2);}}TEST_F(BinaryStreamTest, StreamWriterStrings) {StringRef Strings[] = {"First", "Second", "Third", "Fourth"};size_t Length = 0;for (auto S : Strings)Length += S.size() + 1;initializeOutput(Length, 1);initializeInputFromOutput(1);for (auto &Stream : Streams) {BinaryStreamWriter Writer(*Stream.Output);for (auto S : Strings)ASSERT_THAT_ERROR(Writer.writeCString(S), Succeeded());std::vector<StringRef> InStrings;BinaryStreamReader Reader(*Stream.Input);while (!Reader.empty()) {StringRef S;ASSERT_THAT_ERROR(Reader.readCString(S), Succeeded());InStrings.push_back(S);}EXPECT_EQ(makeArrayRef(Strings), makeArrayRef(InStrings));}}TEST_F(BinaryStreamTest, StreamWriterPadToAlignment) {// This test may seem excessive but it is checking for past bugs and corner// cases by making sure that the stream is allowed to grow and that// both multiple pad chunks and single chunk extensions work.AppendingBinaryByteStream Stream(support::little);BinaryStreamWriter Writer(Stream);// Offset 0: '0'EXPECT_THAT_ERROR(Writer.writeInteger('0'), Succeeded());// Offset 1..110: 0EXPECT_THAT_ERROR(Writer.padToAlignment(111), Succeeded());// Offset 111: '*'EXPECT_THAT_ERROR(Writer.writeInteger('*'), Succeeded());// Offset 112..120: 0EXPECT_THAT_ERROR(Writer.padToAlignment(11), Succeeded());BinaryStreamReader Reader(Stream);char c;// Offset 0EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded());EXPECT_EQ('0', c);// Offset 1..110for (int i = 0; i < 110; ++i) {char c;EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded());EXPECT_EQ('\0', c);}// Offset 111EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded());EXPECT_EQ('*', c);// Offset 112..120for (int i = 0; i < 9; ++i) {char c;EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded());EXPECT_EQ('\0', c);}// EOF.EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Failed());}TEST_F(BinaryStreamTest, StreamWriterAppend) {StringRef Strings[] = {"First", "Second", "Third", "Fourth"};AppendingBinaryByteStream Stream(support::little);BinaryStreamWriter Writer(Stream);for (auto &Str : Strings) {EXPECT_THAT_ERROR(Writer.writeCString(Str), Succeeded());}BinaryStreamReader Reader(Stream);for (auto &Str : Strings) {StringRef S;EXPECT_THAT_ERROR(Reader.readCString(S), Succeeded());EXPECT_EQ(Str, S);}}}namespace {struct BinaryItemStreamObject {explicit BinaryItemStreamObject(ArrayRef<uint8_t> Bytes) : Bytes(Bytes) {}ArrayRef<uint8_t> Bytes;};}namespace llvm {template <> struct BinaryItemTraits<BinaryItemStreamObject> {static size_t length(const BinaryItemStreamObject &Item) {return Item.Bytes.size();}static ArrayRef<uint8_t> bytes(const BinaryItemStreamObject &Item) {return Item.Bytes;}};}namespace {TEST_F(BinaryStreamTest, BinaryItemStream) {std::vector<BinaryItemStreamObject> Objects;struct Foo {int X;double Y;};std::vector<Foo> Foos = {{1, 1.0}, {2, 2.0}, {3, 3.0}};BumpPtrAllocator Allocator;for (const auto &F : Foos) {uint8_t *Ptr = static_cast<uint8_t *>(Allocator.Allocate(sizeof(Foo),alignof(Foo)));MutableArrayRef<uint8_t> Buffer(Ptr, sizeof(Foo));MutableBinaryByteStream Stream(Buffer, llvm::support::big);BinaryStreamWriter Writer(Stream);ASSERT_THAT_ERROR(Writer.writeObject(F), Succeeded());Objects.push_back(BinaryItemStreamObject(Buffer));}BinaryItemStream<BinaryItemStreamObject> ItemStream(big);ItemStream.setItems(Objects);BinaryStreamReader Reader(ItemStream);for (const auto &F : Foos) {const Foo *F2;ASSERT_THAT_ERROR(Reader.readObject(F2), Succeeded());EXPECT_EQ(F.X, F2->X);EXPECT_DOUBLE_EQ(F.Y, F2->Y);}}} // end anonymous namespace
//===- llvm/unittest/Support/Base64Test.cpp - Base64 tests//--------------------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for the Base64 functions.////===----------------------------------------------------------------------===//#include "llvm/Support/Base64.h"#include "llvm/ADT/StringRef.h"#include "gtest/gtest.h"using namespace llvm;namespace {/// Tests an arbitrary set of bytes passed as \p Input.void TestBase64(StringRef Input, StringRef Final) {auto Res = encodeBase64(Input);EXPECT_EQ(Res, Final);}} // namespaceTEST(Base64Test, Base64) {// from: https://tools.ietf.org/html/rfc4648#section-10TestBase64("", "");TestBase64("f", "Zg==");TestBase64("fo", "Zm8=");TestBase64("foo", "Zm9v");TestBase64("foob", "Zm9vYg==");TestBase64("fooba", "Zm9vYmE=");TestBase64("foobar", "Zm9vYmFy");// With non-printable values.char NonPrintableVector[] = {0x00, 0x00, 0x00, 0x46,0x00, 0x08, (char)0xff, (char)0xee};TestBase64({NonPrintableVector, sizeof(NonPrintableVector)}, "AAAARgAI/+4=");// Large test casechar LargeVector[] = {0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b,0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f,0x78, 0x20, 0x6a, 0x75, 0x6d, 0x70, 0x73, 0x20, 0x6f,0x76, 0x65, 0x72, 0x20, 0x31, 0x33, 0x20, 0x6c, 0x61,0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67, 0x73, 0x2e};TestBase64({LargeVector, sizeof(LargeVector)},"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4=");}
//===- llvm/unittest/Support/BLAKE3Test.cpp - BLAKE3 tests ----------------===////// 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////===----------------------------------------------------------------------===////// This file implements unit tests for the BLAKE3 functions.////===----------------------------------------------------------------------===//#include "llvm/Support/BLAKE3.h"#include "llvm/ADT/StringExtras.h"#include "llvm/Support/HashBuilder.h"#include "gtest/gtest.h"using namespace llvm;namespace {/// Tests an arbitrary set of bytes passed as \p Input.void TestBLAKE3Sum(ArrayRef<uint8_t> Input, StringRef Final) {BLAKE3 Hash;Hash.update(Input);auto hash = Hash.final();auto hashStr = toHex(hash);EXPECT_EQ(hashStr, Final);}using KV = std::pair<const char *, const char *>;TEST(BLAKE3Test, BLAKE3) {std::array<KV, 5> testvectors{KV{"","AF1349B9F5F9A1A6A0404DEA36DCC9499BCB25C9ADC112B7CC9A93CAE41F3262"},KV{"a","17762FDDD969A453925D65717AC3EEA21320B66B54342FDE15128D6CAF21215F"},KV{"abc","6437B3AC38465133FFB63B75273A8DB548C558465D79DB03FD359C6CD5BD9D85"},KV{"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq","C19012CC2AAF0DC3D8E5C45A1B79114D2DF42ABB2A410BF54BE09E891AF06FF8"},KV{"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklm""nopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu","553E1AA2A477CB3166E6AB38C12D59F6C5017F0885AAF079F217DA00CFCA363F"}};for (auto input_expected : testvectors) {auto str = std::get<0>(input_expected);auto expected = std::get<1>(input_expected);TestBLAKE3Sum({reinterpret_cast<const uint8_t *>(str), strlen(str)},expected);}std::string rep(1000, 'a');BLAKE3 Hash;for (int i = 0; i < 1000; ++i) {Hash.update({reinterpret_cast<const uint8_t *>(rep.data()), rep.size()});}auto hash = Hash.final();auto hashStr = toHex(hash);EXPECT_EQ(hashStr,"616F575A1B58D4C9797D4217B9730AE5E6EB319D76EDEF6549B46F4EFE31FF8B");// Using generic HashBuilder.HashBuilder<BLAKE3, support::endianness::native> HashBuilder;HashBuilder.update(std::get<0>(testvectors[2]));BLAKE3Result<> HBHash1 = HashBuilder.final();BLAKE3Result<> HBHash2 = HashBuilder.result();EXPECT_EQ(std::get<1>(testvectors[2]), toHex(HBHash1));EXPECT_EQ(std::get<1>(testvectors[2]), toHex(HBHash2));}TEST(BLAKE3Test, SmallerHashSize) {const char *InputStr = "abc";ArrayRef<uint8_t> Input(reinterpret_cast<const uint8_t *>(InputStr),strlen(InputStr));BLAKE3 Hash;Hash.update(Input);auto hash1 = Hash.final<16>();auto hash2 = BLAKE3::hash<16>(Input);auto hashStr1 = toHex(hash1);auto hashStr2 = toHex(hash2);EXPECT_EQ(hashStr1, hashStr2);EXPECT_EQ(hashStr1, "6437B3AC38465133FFB63B75273A8DB5");// Using generic HashBuilder.HashBuilder<TruncatedBLAKE3<16>, support::endianness::native> HashBuilder;HashBuilder.update(Input);BLAKE3Result<16> hash3 = HashBuilder.final();BLAKE3Result<16> hash4 = HashBuilder.result();EXPECT_EQ(hashStr1, toHex(hash3));EXPECT_EQ(hashStr1, toHex(hash4));}} // namespace
//===--- unittest/Support/ArrayRecyclerTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/ArrayRecycler.h"#include "llvm/Support/Allocator.h"#include "gtest/gtest.h"#include <cstdlib>using namespace llvm;namespace {struct Object {int Num;Object *Other;};typedef ArrayRecycler<Object> ARO;TEST(ArrayRecyclerTest, Capacity) {// Capacity size should never be 0.ARO::Capacity Cap = ARO::Capacity::get(0);EXPECT_LT(0u, Cap.getSize());size_t PrevSize = Cap.getSize();for (unsigned N = 1; N != 100; ++N) {Cap = ARO::Capacity::get(N);EXPECT_LE(N, Cap.getSize());if (PrevSize >= N)EXPECT_EQ(PrevSize, Cap.getSize());elseEXPECT_LT(PrevSize, Cap.getSize());PrevSize = Cap.getSize();}// Check that the buckets are monotonically increasing.Cap = ARO::Capacity::get(0);PrevSize = Cap.getSize();for (unsigned N = 0; N != 20; ++N) {Cap = Cap.getNext();EXPECT_LT(PrevSize, Cap.getSize());PrevSize = Cap.getSize();}}TEST(ArrayRecyclerTest, Basics) {BumpPtrAllocator Allocator;ArrayRecycler<Object> DUT;ARO::Capacity Cap = ARO::Capacity::get(8);Object *A1 = DUT.allocate(Cap, Allocator);A1[0].Num = 21;A1[7].Num = 17;Object *A2 = DUT.allocate(Cap, Allocator);A2[0].Num = 121;A2[7].Num = 117;Object *A3 = DUT.allocate(Cap, Allocator);A3[0].Num = 221;A3[7].Num = 217;EXPECT_EQ(21, A1[0].Num);EXPECT_EQ(17, A1[7].Num);EXPECT_EQ(121, A2[0].Num);EXPECT_EQ(117, A2[7].Num);EXPECT_EQ(221, A3[0].Num);EXPECT_EQ(217, A3[7].Num);DUT.deallocate(Cap, A2);// Check that deallocation didn't clobber anything.EXPECT_EQ(21, A1[0].Num);EXPECT_EQ(17, A1[7].Num);EXPECT_EQ(221, A3[0].Num);EXPECT_EQ(217, A3[7].Num);// Verify recycling.Object *A2x = DUT.allocate(Cap, Allocator);EXPECT_EQ(A2, A2x);DUT.deallocate(Cap, A2x);DUT.deallocate(Cap, A1);DUT.deallocate(Cap, A3);// Objects are not required to be recycled in reverse deallocation order, but// that is what the current implementation does.Object *A3x = DUT.allocate(Cap, Allocator);EXPECT_EQ(A3, A3x);Object *A1x = DUT.allocate(Cap, Allocator);EXPECT_EQ(A1, A1x);Object *A2y = DUT.allocate(Cap, Allocator);EXPECT_EQ(A2, A2y);// Back to allocation from the BumpPtrAllocator.Object *A4 = DUT.allocate(Cap, Allocator);EXPECT_NE(A1, A4);EXPECT_NE(A2, A4);EXPECT_NE(A3, A4);DUT.clear(Allocator);}} // end anonymous namespace
//===----- unittests/AnnotationsTest.cpp ----------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Testing/Support/Annotations.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using ::testing::ElementsAre;using ::testing::IsEmpty;namespace {llvm::Annotations::Range range(size_t Begin, size_t End) {llvm::Annotations::Range R;R.Begin = Begin;R.End = End;return R;}TEST(AnnotationsTest, CleanedCode) {EXPECT_EQ(llvm::Annotations("foo^bar$nnn[[baz$^[[qux]]]]").code(),"foobarbazqux");}TEST(AnnotationsTest, Points) {// A single point.EXPECT_EQ(llvm::Annotations("^ab").point(), 0u);EXPECT_EQ(llvm::Annotations("a^b").point(), 1u);EXPECT_EQ(llvm::Annotations("ab^").point(), 2u);// Multiple points.EXPECT_THAT(llvm::Annotations("^a^bc^d^").points(),ElementsAre(0u, 1u, 3u, 4u));// No points.EXPECT_THAT(llvm::Annotations("ab[[cd]]").points(), IsEmpty());// Consecutive points.EXPECT_THAT(llvm::Annotations("ab^^^cd").points(), ElementsAre(2u, 2u, 2u));}TEST(AnnotationsTest, Ranges) {// A single range.EXPECT_EQ(llvm::Annotations("[[a]]bc").range(), range(0, 1));EXPECT_EQ(llvm::Annotations("a[[bc]]d").range(), range(1, 3));EXPECT_EQ(llvm::Annotations("ab[[cd]]").range(), range(2, 4));// Empty range.EXPECT_EQ(llvm::Annotations("[[]]ab").range(), range(0, 0));EXPECT_EQ(llvm::Annotations("a[[]]b").range(), range(1, 1));EXPECT_EQ(llvm::Annotations("ab[[]]").range(), range(2, 2));// Multiple ranges.EXPECT_THAT(llvm::Annotations("[[a]][[b]]cd[[ef]]ef").ranges(),ElementsAre(range(0, 1), range(1, 2), range(4, 6)));// No ranges.EXPECT_THAT(llvm::Annotations("ab^c^defef").ranges(), IsEmpty());}TEST(AnnotationsTest, Nested) {llvm::Annotations Annotated("a[[f^oo^bar[[b[[a]]z]]]]bcdef");EXPECT_THAT(Annotated.points(), ElementsAre(2u, 4u));EXPECT_THAT(Annotated.ranges(),ElementsAre(range(8, 9), range(7, 10), range(1, 10)));}TEST(AnnotationsTest, Named) {// A single named point or range.EXPECT_EQ(llvm::Annotations("a$foo^b").point("foo"), 1u);EXPECT_EQ(llvm::Annotations("a$foo[[b]]cdef").range("foo"), range(1, 2));// Empty names should also work.EXPECT_EQ(llvm::Annotations("a$^b").point(""), 1u);EXPECT_EQ(llvm::Annotations("a$[[b]]cdef").range(""), range(1, 2));// Multiple named points.llvm::Annotations Annotated("a$p1^bcd$p2^123$p1^345");EXPECT_THAT(Annotated.points(), IsEmpty());EXPECT_THAT(Annotated.points("p1"), ElementsAre(1u, 7u));EXPECT_EQ(Annotated.point("p2"), 4u);}TEST(AnnotationsTest, Errors) {// Annotations use llvm_unreachable, it will only crash in debug mode.#ifndef NDEBUG// point() and range() crash on zero or multiple ranges.EXPECT_DEATH(llvm::Annotations("ab[[c]]def").point(),"expected exactly one point");EXPECT_DEATH(llvm::Annotations("a^b^cdef").point(),"expected exactly one point");EXPECT_DEATH(llvm::Annotations("a^bcdef").range(),"expected exactly one range");EXPECT_DEATH(llvm::Annotations("a[[b]]c[[d]]ef").range(),"expected exactly one range");EXPECT_DEATH(llvm::Annotations("$foo^a$foo^a").point("foo"),"expected exactly one point");EXPECT_DEATH(llvm::Annotations("$foo[[a]]bc$foo[[a]]").range("foo"),"expected exactly one range");// Parsing failures.EXPECT_DEATH(llvm::Annotations("ff[[fdfd"), "unmatched \\[\\[");EXPECT_DEATH(llvm::Annotations("ff[[fdjsfjd]]xxx]]"), "unmatched \\]\\]");EXPECT_DEATH(llvm::Annotations("ff$fdsfd"), "unterminated \\$name");#endif}} // namespace
//===- llvm/unittest/Support/AllocatorTest.cpp - BumpPtrAllocator tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Allocator.h"#include "gtest/gtest.h"#include <cstdlib>using namespace llvm;namespace {TEST(AllocatorTest, Basics) {BumpPtrAllocator Alloc;int *a = (int*)Alloc.Allocate(sizeof(int), alignof(int));int *b = (int*)Alloc.Allocate(sizeof(int) * 10, alignof(int));int *c = (int*)Alloc.Allocate(sizeof(int), alignof(int));*a = 1;b[0] = 2;b[9] = 2;*c = 3;EXPECT_EQ(1, *a);EXPECT_EQ(2, b[0]);EXPECT_EQ(2, b[9]);EXPECT_EQ(3, *c);EXPECT_EQ(1U, Alloc.GetNumSlabs());BumpPtrAllocator Alloc2 = std::move(Alloc);EXPECT_EQ(0U, Alloc.GetNumSlabs());EXPECT_EQ(1U, Alloc2.GetNumSlabs());// Make sure the old pointers still work. These are especially interesting// under ASan or Valgrind.EXPECT_EQ(1, *a);EXPECT_EQ(2, b[0]);EXPECT_EQ(2, b[9]);EXPECT_EQ(3, *c);Alloc = std::move(Alloc2);EXPECT_EQ(0U, Alloc2.GetNumSlabs());EXPECT_EQ(1U, Alloc.GetNumSlabs());}// Allocate enough bytes to create three slabs.TEST(AllocatorTest, ThreeSlabs) {BumpPtrAllocator Alloc;Alloc.Allocate(3000, 1);EXPECT_EQ(1U, Alloc.GetNumSlabs());Alloc.Allocate(3000, 1);EXPECT_EQ(2U, Alloc.GetNumSlabs());Alloc.Allocate(3000, 1);EXPECT_EQ(3U, Alloc.GetNumSlabs());}// Allocate enough bytes to create two slabs, reset the allocator, and do it// again.TEST(AllocatorTest, TestReset) {BumpPtrAllocator Alloc;// Allocate something larger than the SizeThreshold=4096.(void)Alloc.Allocate(5000, 1);Alloc.Reset();// Calling Reset should free all CustomSizedSlabs.EXPECT_EQ(0u, Alloc.GetNumSlabs());Alloc.Allocate(3000, 1);EXPECT_EQ(1U, Alloc.GetNumSlabs());Alloc.Allocate(3000, 1);EXPECT_EQ(2U, Alloc.GetNumSlabs());Alloc.Reset();EXPECT_EQ(1U, Alloc.GetNumSlabs());Alloc.Allocate(3000, 1);EXPECT_EQ(1U, Alloc.GetNumSlabs());Alloc.Allocate(3000, 1);EXPECT_EQ(2U, Alloc.GetNumSlabs());}// Test some allocations at varying alignments.TEST(AllocatorTest, TestAlignment) {BumpPtrAllocator Alloc;uintptr_t a;a = (uintptr_t)Alloc.Allocate(1, 2);EXPECT_EQ(0U, a & 1);a = (uintptr_t)Alloc.Allocate(1, 4);EXPECT_EQ(0U, a & 3);a = (uintptr_t)Alloc.Allocate(1, 8);EXPECT_EQ(0U, a & 7);a = (uintptr_t)Alloc.Allocate(1, 16);EXPECT_EQ(0U, a & 15);a = (uintptr_t)Alloc.Allocate(1, 32);EXPECT_EQ(0U, a & 31);a = (uintptr_t)Alloc.Allocate(1, 64);EXPECT_EQ(0U, a & 63);a = (uintptr_t)Alloc.Allocate(1, 128);EXPECT_EQ(0U, a & 127);}// Test zero-sized allocations.// In general we don't need to allocate memory for these.// However Allocate never returns null, so if the first allocation is zero-sized// we end up creating a slab for it.TEST(AllocatorTest, TestZero) {BumpPtrAllocator Alloc;Alloc.setRedZoneSize(0); // else our arithmetic is all offEXPECT_EQ(0u, Alloc.GetNumSlabs());EXPECT_EQ(0u, Alloc.getBytesAllocated());void *Empty = Alloc.Allocate(0, 1);EXPECT_NE(Empty, nullptr) << "Allocate is __attribute__((returns_nonnull))";EXPECT_EQ(1u, Alloc.GetNumSlabs()) << "Allocated a slab to point to";EXPECT_EQ(0u, Alloc.getBytesAllocated());void *Large = Alloc.Allocate(4096, 1);EXPECT_EQ(1u, Alloc.GetNumSlabs());EXPECT_EQ(4096u, Alloc.getBytesAllocated());EXPECT_EQ(Empty, Large);void *Empty2 = Alloc.Allocate(0, 1);EXPECT_NE(Empty2, nullptr);EXPECT_EQ(1u, Alloc.GetNumSlabs());EXPECT_EQ(4096u, Alloc.getBytesAllocated());}// Test allocating just over the slab size. This tests a bug where before the// allocator incorrectly calculated the buffer end pointer.TEST(AllocatorTest, TestOverflow) {BumpPtrAllocator Alloc;// Fill the slab right up until the end pointer.Alloc.Allocate(4096, 1);EXPECT_EQ(1U, Alloc.GetNumSlabs());// If we don't allocate a new slab, then we will have overflowed.Alloc.Allocate(1, 1);EXPECT_EQ(2U, Alloc.GetNumSlabs());}// Test allocating with a size larger than the initial slab size.TEST(AllocatorTest, TestSmallSlabSize) {BumpPtrAllocator Alloc;Alloc.Allocate(8000, 1);EXPECT_EQ(1U, Alloc.GetNumSlabs());}// Test requesting alignment that goes past the end of the current slab.TEST(AllocatorTest, TestAlignmentPastSlab) {BumpPtrAllocator Alloc;Alloc.Allocate(4095, 1);// Aligning the current slab pointer is likely to move it past the end of the// slab, which would confuse any unsigned comparisons with the difference of// the end pointer and the aligned pointer.Alloc.Allocate(1024, 8192);EXPECT_EQ(2U, Alloc.GetNumSlabs());}// Test allocating with a decreased growth delay.TEST(AllocatorTest, TestFasterSlabGrowthDelay) {const size_t SlabSize = 4096;// Decrease the growth delay to double the slab size every slab.const size_t GrowthDelay = 1;BumpPtrAllocatorImpl<MallocAllocator, SlabSize, SlabSize, GrowthDelay> Alloc;// Disable the red zone for this test. The additional bytes allocated for the// red zone would change the allocation numbers we check below.Alloc.setRedZoneSize(0);Alloc.Allocate(SlabSize, 1);EXPECT_EQ(SlabSize, Alloc.getTotalMemory());// We hit our growth delay with the previous allocation so the next// allocation should get a twice as large slab.Alloc.Allocate(SlabSize, 1);EXPECT_EQ(SlabSize * 3, Alloc.getTotalMemory());Alloc.Allocate(SlabSize, 1);EXPECT_EQ(SlabSize * 3, Alloc.getTotalMemory());// Both slabs are full again and hit the growth delay again, so the// next allocation should again get a slab with four times the size of the// original slab size. In total we now should have a memory size of:// 1 + 2 + 4 * SlabSize.Alloc.Allocate(SlabSize, 1);EXPECT_EQ(SlabSize * 7, Alloc.getTotalMemory());}// Test allocating with a increased growth delay.TEST(AllocatorTest, TestSlowerSlabGrowthDelay) {const size_t SlabSize = 16;// Increase the growth delay to only double the slab size every 256 slabs.const size_t GrowthDelay = 256;BumpPtrAllocatorImpl<MallocAllocator, SlabSize, SlabSize, GrowthDelay> Alloc;// Disable the red zone for this test. The additional bytes allocated for the// red zone would change the allocation numbers we check below.Alloc.setRedZoneSize(0);// Allocate 256 slabs. We should keep getting slabs with the original size// as we haven't hit our growth delay on the last allocation.for (std::size_t i = 0; i < GrowthDelay; ++i)Alloc.Allocate(SlabSize, 1);EXPECT_EQ(SlabSize * GrowthDelay, Alloc.getTotalMemory());// Allocate another slab. This time we should get another slab allocated// that is twice as large as the normal slab size.Alloc.Allocate(SlabSize, 1);EXPECT_EQ(SlabSize * GrowthDelay + SlabSize * 2, Alloc.getTotalMemory());}// Mock slab allocator that returns slabs aligned on 4096 bytes. There is no// easy portable way to do this, so this is kind of a hack.class MockSlabAllocator {static size_t LastSlabSize;public:~MockSlabAllocator() { }void *Allocate(size_t Size, size_t /*Alignment*/) {// Allocate space for the alignment, the slab, and a void* that goes right// before the slab.Align Alignment(4096);void *MemBase = safe_malloc(Size + Alignment.value() - 1 + sizeof(void *));// Find the slab start.void *Slab = (void *)alignAddr((char*)MemBase + sizeof(void *), Alignment);// Hold a pointer to the base so we can free the whole malloced block.((void**)Slab)[-1] = MemBase;LastSlabSize = Size;return Slab;}void Deallocate(void *Slab, size_t /*Size*/, size_t /*Alignment*/) {free(((void**)Slab)[-1]);}static size_t GetLastSlabSize() { return LastSlabSize; }};size_t MockSlabAllocator::LastSlabSize = 0;// Allocate a large-ish block with a really large alignment so that the// allocator will think that it has space, but after it does the alignment it// will not.TEST(AllocatorTest, TestBigAlignment) {BumpPtrAllocatorImpl<MockSlabAllocator> Alloc;// First allocate a tiny bit to ensure we have to re-align things.(void)Alloc.Allocate(1, 1);// Now the big chunk with a big alignment.(void)Alloc.Allocate(3000, 2048);// We test that the last slab size is not the default 4096 byte slab, but// rather a custom sized slab that is larger.EXPECT_GT(MockSlabAllocator::GetLastSlabSize(), 4096u);}} // anonymous namespace
//=== - llvm/unittest/Support/Alignment.cpp - Alignment utility tests -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/Support/Alignment.h"#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"#include <vector>#ifdef _MSC_VER// Disable warnings about potential divide by 0.#pragma warning(push)#pragma warning(disable : 4723)#endifusing namespace llvm;namespace {TEST(AlignmentTest, AlignOfConstant) {EXPECT_EQ(Align::Of<uint8_t>(), Align(alignof(uint8_t)));EXPECT_EQ(Align::Of<uint16_t>(), Align(alignof(uint16_t)));EXPECT_EQ(Align::Of<uint32_t>(), Align(alignof(uint32_t)));EXPECT_EQ(Align::Of<uint64_t>(), Align(alignof(uint64_t)));}TEST(AlignmentTest, AlignConstant) {EXPECT_EQ(Align::Constant<1>(), Align(1));EXPECT_EQ(Align::Constant<2>(), Align(2));EXPECT_EQ(Align::Constant<4>(), Align(4));EXPECT_EQ(Align::Constant<8>(), Align(8));EXPECT_EQ(Align::Constant<16>(), Align(16));EXPECT_EQ(Align::Constant<32>(), Align(32));EXPECT_EQ(Align::Constant<64>(), Align(64));}TEST(AlignmentTest, AlignConstexprConstant) {constexpr Align kConstantAlign = Align::Of<uint64_t>();EXPECT_EQ(Align(alignof(uint64_t)), kConstantAlign);}std::vector<uint64_t> getValidAlignments() {std::vector<uint64_t> Out;for (size_t Shift = 0; Shift < 64; ++Shift)Out.push_back(1ULL << Shift);return Out;}TEST(AlignmentTest, AlignDefaultCTor) { EXPECT_EQ(Align().value(), 1ULL); }TEST(AlignmentTest, MaybeAlignDefaultCTor) { EXPECT_FALSE(MaybeAlign()); }TEST(AlignmentTest, ValidCTors) {for (uint64_t Value : getValidAlignments()) {EXPECT_EQ(Align(Value).value(), Value);EXPECT_EQ((*MaybeAlign(Value)).value(), Value);}}TEST(AlignmentTest, CheckMaybeAlignHasValue) {EXPECT_TRUE(MaybeAlign(1));EXPECT_TRUE(MaybeAlign(1).has_value());EXPECT_FALSE(MaybeAlign(0));EXPECT_FALSE(MaybeAlign(0).has_value());EXPECT_FALSE(MaybeAlign());EXPECT_FALSE(MaybeAlign().has_value());}TEST(AlignmentTest, Division) {for (uint64_t Value : getValidAlignments()) {if (Value > 1) {EXPECT_EQ(Align(Value).previous(), Value / 2);}}}TEST(AlignmentTest, AlignTo) {struct {uint64_t alignment;uint64_t offset;uint64_t rounded;const void *forgedAddr() const {// A value of any integral or enumeration type can be converted to a// pointer type.return reinterpret_cast<const void *>(offset);}} kTests[] = {// Align{1, 0, 0}, {1, 1, 1}, {1, 5, 5}, {2, 0, 0}, {2, 1, 2}, {2, 2, 2},{2, 7, 8}, {2, 16, 16}, {4, 0, 0}, {4, 1, 4}, {4, 4, 4}, {4, 6, 8},};for (const auto &T : kTests) {Align A = Align(T.alignment);EXPECT_EQ(alignTo(T.offset, A), T.rounded);EXPECT_EQ(alignAddr(T.forgedAddr(), A), T.rounded);}}TEST(AlignmentTest, AlignToWithSkew) {EXPECT_EQ(alignTo(5, Align(8), 0), alignTo(5, Align(8)));EXPECT_EQ(alignTo(5, Align(8), 7), 7U);EXPECT_EQ(alignTo(17, Align(8), 1), 17U);EXPECT_EQ(alignTo(~0LL, Align(8), 3), 3U);}TEST(AlignmentTest, Log2) {for (uint64_t Value : getValidAlignments()) {EXPECT_EQ(Log2(Align(Value)), Log2_64(Value));}}TEST(AlignmentTest, Encode_Decode) {for (uint64_t Value : getValidAlignments()) {{Align Actual(Value);Align Expected = *decodeMaybeAlign(encode(Actual));EXPECT_EQ(Expected, Actual);}{MaybeAlign Actual(Value);MaybeAlign Expected = decodeMaybeAlign(encode(Actual));EXPECT_EQ(Expected, Actual);}}MaybeAlign Actual(0);MaybeAlign Expected = decodeMaybeAlign(encode(Actual));EXPECT_EQ(Expected, Actual);}TEST(AlignmentTest, isAligned_isAddrAligned) {struct {uint64_t alignment;uint64_t offset;bool isAligned;const void *forgedAddr() const {// A value of any integral or enumeration type can be converted to a// pointer type.return reinterpret_cast<const void *>(offset);}} kTests[] = {{1, 0, true}, {1, 1, true}, {1, 5, true}, {2, 0, true},{2, 1, false}, {2, 2, true}, {2, 7, false}, {2, 16, true},{4, 0, true}, {4, 1, false}, {4, 4, true}, {4, 6, false},};for (const auto &T : kTests) {MaybeAlign A(T.alignment);// Test Alignif (A) {EXPECT_EQ(isAligned(A.value(), T.offset), T.isAligned);EXPECT_EQ(isAddrAligned(A.value(), T.forgedAddr()), T.isAligned);}}}TEST(AlignmentTest, offsetToAlignment) {struct {uint64_t alignment;uint64_t offset;uint64_t alignedOffset;const void *forgedAddr() const {// A value of any integral or enumeration type can be converted to a// pointer type.return reinterpret_cast<const void *>(offset);}} kTests[] = {{1, 0, 0}, {1, 1, 0}, {1, 5, 0}, {2, 0, 0}, {2, 1, 1}, {2, 2, 0},{2, 7, 1}, {2, 16, 0}, {4, 0, 0}, {4, 1, 3}, {4, 4, 0}, {4, 6, 2},};for (const auto &T : kTests) {const Align A(T.alignment);EXPECT_EQ(offsetToAlignment(T.offset, A), T.alignedOffset);EXPECT_EQ(offsetToAlignedAddr(T.forgedAddr(), A), T.alignedOffset);}}TEST(AlignmentTest, AlignComparisons) {std::vector<uint64_t> ValidAlignments = getValidAlignments();llvm::sort(ValidAlignments);for (size_t I = 1; I < ValidAlignments.size(); ++I) {assert(I >= 1);const Align A(ValidAlignments[I - 1]);const Align B(ValidAlignments[I]);EXPECT_EQ(A, A);EXPECT_NE(A, B);EXPECT_LT(A, B);EXPECT_GT(B, A);EXPECT_LE(A, B);EXPECT_GE(B, A);EXPECT_LE(A, A);EXPECT_GE(A, A);EXPECT_EQ(A, A.value());EXPECT_NE(A, B.value());EXPECT_LT(A, B.value());EXPECT_GT(B, A.value());EXPECT_LE(A, B.value());EXPECT_GE(B, A.value());EXPECT_LE(A, A.value());EXPECT_GE(A, A.value());EXPECT_EQ(std::max(A, B), B);EXPECT_EQ(std::min(A, B), A);const MaybeAlign MA(ValidAlignments[I - 1]);const MaybeAlign MB(ValidAlignments[I]);EXPECT_EQ(MA, MA);EXPECT_NE(MA, MB);EXPECT_EQ(std::max(A, B), B);EXPECT_EQ(std::min(A, B), A);}}TEST(AlignmentTest, AssumeAligned) {EXPECT_EQ(assumeAligned(0), Align(1));EXPECT_EQ(assumeAligned(0), Align());EXPECT_EQ(assumeAligned(1), Align(1));EXPECT_EQ(assumeAligned(1), Align());}// Death tests reply on assert which is disabled in release mode.#ifndef NDEBUG// We use a subset of valid alignments for DEATH_TESTs as they are particularly// slow.std::vector<uint64_t> getValidAlignmentsForDeathTest() {return {1, 1ULL << 31, 1ULL << 63};}std::vector<uint64_t> getNonPowerOfTwo() { return {3, 10, 15}; }TEST(AlignmentDeathTest, CantConvertUnsetMaybe) {EXPECT_DEATH((*MaybeAlign(0)), ".*");}TEST(AlignmentDeathTest, InvalidCTors) {EXPECT_DEATH((Align(0)), "Value must not be 0");for (uint64_t Value : getNonPowerOfTwo()) {EXPECT_DEATH((Align(Value)), "Alignment is not a power of 2");EXPECT_DEATH((MaybeAlign(Value)),"Alignment is neither 0 nor a power of 2");}}TEST(AlignmentDeathTest, ComparisonsWithZero) {for (uint64_t Value : getValidAlignmentsForDeathTest()) {EXPECT_DEATH((void)(Align(Value) == 0), ".* should be defined");EXPECT_DEATH((void)(Align(Value) != 0), ".* should be defined");EXPECT_DEATH((void)(Align(Value) >= 0), ".* should be defined");EXPECT_DEATH((void)(Align(Value) <= 0), ".* should be defined");EXPECT_DEATH((void)(Align(Value) > 0), ".* should be defined");EXPECT_DEATH((void)(Align(Value) < 0), ".* should be defined");}}TEST(AlignmentDeathTest, AlignAddr) {const void *const unaligned_high_ptr =reinterpret_cast<const void *>(std::numeric_limits<uintptr_t>::max() - 1);EXPECT_DEATH(alignAddr(unaligned_high_ptr, Align(16)), "Overflow");}#endif // NDEBUG} // end anonymous namespace#ifdef _MSC_VER#pragma warning(pop)#endif
//=== - llvm/unittest/Support/AlignOfTest.cpp - Alignment utility tests ---===////// 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////===----------------------------------------------------------------------===//#ifdef _MSC_VER// Disable warnings about alignment-based structure padding.// This must be above the includes to suppress warnings in included templates.#pragma warning(disable:4324)#endif#include "llvm/Support/AlignOf.h"#include "llvm/Support/Compiler.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Disable warnings about questionable type definitions.// We're testing that even questionable types work with the alignment utilities.#ifdef _MSC_VER#pragma warning(disable:4584)#endif// Suppress direct base '{anonymous}::S1' inaccessible in '{anonymous}::D9'// due to ambiguity warning.#ifdef __clang__#pragma clang diagnostic ignored "-Wunknown-pragmas"#pragma clang diagnostic ignored "-Winaccessible-base"#elif ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402// Pragma based warning suppression was introduced in GGC 4.2. Additionally// this warning is "enabled by default". The warning still appears if -Wall is// suppressed. Apparently GCC suppresses it when -w is specifed, which is odd.#pragma GCC diagnostic warning "-w"#endif// Define some fixed alignment types to use in these tests.struct alignas(1) A1 {};struct alignas(2) A2 {};struct alignas(4) A4 {};struct alignas(8) A8 {};struct S1 {};struct S2 { char a; };struct S3 { int x; };struct S4 { double y; };struct S5 { A1 a1; A2 a2; A4 a4; A8 a8; };struct S6 { double f(); };struct D1 : S1 {};struct D2 : S6 { float g(); };struct D3 : S2 {};struct D4 : S2 { int x; };struct D5 : S3 { char c; };struct D6 : S2, S3 {};struct D7 : S1, S3 {};struct D8 : S1, D4, D5 { double x[2]; };struct D9 : S1, D1 { S1 s1; };struct V1 { virtual ~V1(); };struct V2 { int x; virtual ~V2(); };struct V3 : V1 {~V3() override;};struct V4 : virtual V2 { int y;~V4() override;};struct V5 : V4, V3 { double z;~V5() override;};struct V6 : S1 { virtual ~V6(); };struct V7 : virtual V2, virtual V6 {~V7() override;};struct V8 : V5, virtual V6, V7 { double zz;~V8() override;};double S6::f() { return 0.0; }float D2::g() { return 0.0f; }V1::~V1() {}V2::~V2() {}V3::~V3() {}V4::~V4() {}V5::~V5() {}V6::~V6() {}V7::~V7() {}V8::~V8() {}template <typename M> struct T { M m; };TEST(AlignOfTest, BasicAlignedArray) {EXPECT_LE(1u, alignof(AlignedCharArrayUnion<A1>));EXPECT_LE(2u, alignof(AlignedCharArrayUnion<A2>));EXPECT_LE(4u, alignof(AlignedCharArrayUnion<A4>));EXPECT_LE(8u, alignof(AlignedCharArrayUnion<A8>));EXPECT_LE(1u, sizeof(AlignedCharArrayUnion<A1>));EXPECT_LE(2u, sizeof(AlignedCharArrayUnion<A2>));EXPECT_LE(4u, sizeof(AlignedCharArrayUnion<A4>));EXPECT_LE(8u, sizeof(AlignedCharArrayUnion<A8>));EXPECT_EQ(1u, (alignof(AlignedCharArrayUnion<A1>)));EXPECT_EQ(2u, (alignof(AlignedCharArrayUnion<A1, A2>)));EXPECT_EQ(4u, (alignof(AlignedCharArrayUnion<A1, A2, A4>)));EXPECT_EQ(8u, (alignof(AlignedCharArrayUnion<A1, A2, A4, A8>)));EXPECT_EQ(1u, sizeof(AlignedCharArrayUnion<A1>));EXPECT_EQ(2u, sizeof(AlignedCharArrayUnion<A1, A2>));EXPECT_EQ(4u, sizeof(AlignedCharArrayUnion<A1, A2, A4>));EXPECT_EQ(8u, sizeof(AlignedCharArrayUnion<A1, A2, A4, A8>));EXPECT_EQ(1u, (alignof(AlignedCharArrayUnion<A1[1]>)));EXPECT_EQ(2u, (alignof(AlignedCharArrayUnion<A1[2], A2[1]>)));EXPECT_EQ(4u, (alignof(AlignedCharArrayUnion<A1[42], A2[55], A4[13]>)));EXPECT_EQ(8u, (alignof(AlignedCharArrayUnion<A1[2], A2[1], A4, A8>)));EXPECT_EQ(1u, sizeof(AlignedCharArrayUnion<A1[1]>));EXPECT_EQ(2u, sizeof(AlignedCharArrayUnion<A1[2], A2[1]>));EXPECT_EQ(4u, sizeof(AlignedCharArrayUnion<A1[3], A2[2], A4>));EXPECT_EQ(16u, sizeof(AlignedCharArrayUnion<A1, A2[3],A4[3], A8>));// For other tests we simply assert that the alignment of the union mathes// that of the fundamental type and hope that we have any weird type// productions that would trigger bugs.EXPECT_EQ(alignof(T<char>), alignof(AlignedCharArrayUnion<char>));EXPECT_EQ(alignof(T<short>), alignof(AlignedCharArrayUnion<short>));EXPECT_EQ(alignof(T<int>), alignof(AlignedCharArrayUnion<int>));EXPECT_EQ(alignof(T<long>), alignof(AlignedCharArrayUnion<long>));EXPECT_EQ(alignof(T<long long>), alignof(AlignedCharArrayUnion<long long>));EXPECT_EQ(alignof(T<float>), alignof(AlignedCharArrayUnion<float>));#ifdef _AIXEXPECT_LE(alignof(T<double>), alignof(AlignedCharArrayUnion<double>));EXPECT_LE(alignof(T<long double>),alignof(AlignedCharArrayUnion<long double>));EXPECT_LE(alignof(S4), alignof(AlignedCharArrayUnion<S4>));#elseEXPECT_EQ(alignof(T<double>), alignof(AlignedCharArrayUnion<double>));EXPECT_EQ(alignof(T<long double>),alignof(AlignedCharArrayUnion<long double>));EXPECT_EQ(alignof(S4), alignof(AlignedCharArrayUnion<S4>));#endifEXPECT_EQ(alignof(T<void *>), alignof(AlignedCharArrayUnion<void *>));EXPECT_EQ(alignof(T<int *>), alignof(AlignedCharArrayUnion<int *>));EXPECT_EQ(alignof(T<double (*)(double)>),alignof(AlignedCharArrayUnion<double (*)(double)>));EXPECT_EQ(alignof(T<double (S6::*)()>),alignof(AlignedCharArrayUnion<double (S6::*)()>));EXPECT_EQ(alignof(S1), alignof(AlignedCharArrayUnion<S1>));EXPECT_EQ(alignof(S2), alignof(AlignedCharArrayUnion<S2>));EXPECT_EQ(alignof(S3), alignof(AlignedCharArrayUnion<S3>));EXPECT_EQ(alignof(S5), alignof(AlignedCharArrayUnion<S5>));EXPECT_EQ(alignof(S6), alignof(AlignedCharArrayUnion<S6>));EXPECT_EQ(alignof(D1), alignof(AlignedCharArrayUnion<D1>));EXPECT_EQ(alignof(D2), alignof(AlignedCharArrayUnion<D2>));EXPECT_EQ(alignof(D3), alignof(AlignedCharArrayUnion<D3>));EXPECT_EQ(alignof(D4), alignof(AlignedCharArrayUnion<D4>));EXPECT_EQ(alignof(D5), alignof(AlignedCharArrayUnion<D5>));EXPECT_EQ(alignof(D6), alignof(AlignedCharArrayUnion<D6>));EXPECT_EQ(alignof(D7), alignof(AlignedCharArrayUnion<D7>));EXPECT_EQ(alignof(D8), alignof(AlignedCharArrayUnion<D8>));EXPECT_EQ(alignof(D9), alignof(AlignedCharArrayUnion<D9>));EXPECT_EQ(alignof(V1), alignof(AlignedCharArrayUnion<V1>));EXPECT_EQ(alignof(V2), alignof(AlignedCharArrayUnion<V2>));EXPECT_EQ(alignof(V3), alignof(AlignedCharArrayUnion<V3>));EXPECT_EQ(alignof(V4), alignof(AlignedCharArrayUnion<V4>));EXPECT_EQ(alignof(V5), alignof(AlignedCharArrayUnion<V5>));EXPECT_EQ(alignof(V6), alignof(AlignedCharArrayUnion<V6>));EXPECT_EQ(alignof(V7), alignof(AlignedCharArrayUnion<V7>));// Some versions of MSVC get this wrong somewhat disturbingly. The failure// appears to be benign: alignof(V8) produces a preposterous value: 12#ifndef _MSC_VEREXPECT_EQ(alignof(V8), alignof(AlignedCharArrayUnion<V8>));#endifEXPECT_EQ(sizeof(char), sizeof(AlignedCharArrayUnion<char>));EXPECT_EQ(sizeof(char[1]), sizeof(AlignedCharArrayUnion<char[1]>));EXPECT_EQ(sizeof(char[2]), sizeof(AlignedCharArrayUnion<char[2]>));EXPECT_EQ(sizeof(char[3]), sizeof(AlignedCharArrayUnion<char[3]>));EXPECT_EQ(sizeof(char[4]), sizeof(AlignedCharArrayUnion<char[4]>));EXPECT_EQ(sizeof(char[5]), sizeof(AlignedCharArrayUnion<char[5]>));EXPECT_EQ(sizeof(char[8]), sizeof(AlignedCharArrayUnion<char[8]>));EXPECT_EQ(sizeof(char[13]), sizeof(AlignedCharArrayUnion<char[13]>));EXPECT_EQ(sizeof(char[16]), sizeof(AlignedCharArrayUnion<char[16]>));EXPECT_EQ(sizeof(char[21]), sizeof(AlignedCharArrayUnion<char[21]>));EXPECT_EQ(sizeof(char[32]), sizeof(AlignedCharArrayUnion<char[32]>));EXPECT_EQ(sizeof(short), sizeof(AlignedCharArrayUnion<short>));EXPECT_EQ(sizeof(int), sizeof(AlignedCharArrayUnion<int>));EXPECT_EQ(sizeof(long), sizeof(AlignedCharArrayUnion<long>));EXPECT_EQ(sizeof(long long),sizeof(AlignedCharArrayUnion<long long>));EXPECT_EQ(sizeof(float), sizeof(AlignedCharArrayUnion<float>));EXPECT_EQ(sizeof(double), sizeof(AlignedCharArrayUnion<double>));EXPECT_EQ(sizeof(long double),sizeof(AlignedCharArrayUnion<long double>));EXPECT_EQ(sizeof(void *), sizeof(AlignedCharArrayUnion<void *>));EXPECT_EQ(sizeof(int *), sizeof(AlignedCharArrayUnion<int *>));EXPECT_EQ(sizeof(double (*)(double)),sizeof(AlignedCharArrayUnion<double (*)(double)>));EXPECT_EQ(sizeof(double (S6::*)()),sizeof(AlignedCharArrayUnion<double (S6::*)()>));EXPECT_EQ(sizeof(S1), sizeof(AlignedCharArrayUnion<S1>));EXPECT_EQ(sizeof(S2), sizeof(AlignedCharArrayUnion<S2>));EXPECT_EQ(sizeof(S3), sizeof(AlignedCharArrayUnion<S3>));EXPECT_EQ(sizeof(S4), sizeof(AlignedCharArrayUnion<S4>));EXPECT_EQ(sizeof(S5), sizeof(AlignedCharArrayUnion<S5>));EXPECT_EQ(sizeof(S6), sizeof(AlignedCharArrayUnion<S6>));EXPECT_EQ(sizeof(D1), sizeof(AlignedCharArrayUnion<D1>));EXPECT_EQ(sizeof(D2), sizeof(AlignedCharArrayUnion<D2>));EXPECT_EQ(sizeof(D3), sizeof(AlignedCharArrayUnion<D3>));EXPECT_EQ(sizeof(D4), sizeof(AlignedCharArrayUnion<D4>));EXPECT_EQ(sizeof(D5), sizeof(AlignedCharArrayUnion<D5>));EXPECT_EQ(sizeof(D6), sizeof(AlignedCharArrayUnion<D6>));EXPECT_EQ(sizeof(D7), sizeof(AlignedCharArrayUnion<D7>));EXPECT_EQ(sizeof(D8), sizeof(AlignedCharArrayUnion<D8>));EXPECT_EQ(sizeof(D9), sizeof(AlignedCharArrayUnion<D9>));EXPECT_EQ(sizeof(D9[1]), sizeof(AlignedCharArrayUnion<D9[1]>));EXPECT_EQ(sizeof(D9[2]), sizeof(AlignedCharArrayUnion<D9[2]>));EXPECT_EQ(sizeof(D9[3]), sizeof(AlignedCharArrayUnion<D9[3]>));EXPECT_EQ(sizeof(D9[4]), sizeof(AlignedCharArrayUnion<D9[4]>));EXPECT_EQ(sizeof(D9[5]), sizeof(AlignedCharArrayUnion<D9[5]>));EXPECT_EQ(sizeof(D9[8]), sizeof(AlignedCharArrayUnion<D9[8]>));EXPECT_EQ(sizeof(D9[13]), sizeof(AlignedCharArrayUnion<D9[13]>));EXPECT_EQ(sizeof(D9[16]), sizeof(AlignedCharArrayUnion<D9[16]>));EXPECT_EQ(sizeof(D9[21]), sizeof(AlignedCharArrayUnion<D9[21]>));EXPECT_EQ(sizeof(D9[32]), sizeof(AlignedCharArrayUnion<D9[32]>));EXPECT_EQ(sizeof(V1), sizeof(AlignedCharArrayUnion<V1>));EXPECT_EQ(sizeof(V2), sizeof(AlignedCharArrayUnion<V2>));EXPECT_EQ(sizeof(V3), sizeof(AlignedCharArrayUnion<V3>));EXPECT_EQ(sizeof(V4), sizeof(AlignedCharArrayUnion<V4>));EXPECT_EQ(sizeof(V5), sizeof(AlignedCharArrayUnion<V5>));EXPECT_EQ(sizeof(V6), sizeof(AlignedCharArrayUnion<V6>));EXPECT_EQ(sizeof(V7), sizeof(AlignedCharArrayUnion<V7>));// Some versions of MSVC also get this wrong. The failure again appears to be// benign: sizeof(V8) is only 52 bytes, but our array reserves 56.#ifndef _MSC_VEREXPECT_EQ(sizeof(V8), sizeof(AlignedCharArrayUnion<V8>));#endif}} // end anonymous namespace
//===- llvm/unittest/Support/AddresRangeTest.cpp --------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/AddressRanges.h"#include "llvm/Testing/Support/Error.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <string>using namespace llvm;TEST(AddressRangeTest, TestRanges) {// test llvm::AddressRange.const uint64_t StartAddr = 0x1000;const uint64_t EndAddr = 0x2000;// Verify constructor and API to ensure it takes start and end address.const AddressRange Range(StartAddr, EndAddr);EXPECT_EQ(Range.size(), EndAddr - StartAddr);// Verify llvm::AddressRange::contains().EXPECT_FALSE(Range.contains(0));EXPECT_FALSE(Range.contains(StartAddr - 1));EXPECT_TRUE(Range.contains(StartAddr));EXPECT_TRUE(Range.contains(EndAddr - 1));EXPECT_FALSE(Range.contains(EndAddr));EXPECT_FALSE(Range.contains(UINT64_MAX));const AddressRange RangeSame(StartAddr, EndAddr);const AddressRange RangeDifferentStart(StartAddr + 1, EndAddr);const AddressRange RangeDifferentEnd(StartAddr, EndAddr + 1);const AddressRange RangeDifferentStartEnd(StartAddr + 1, EndAddr + 1);// Test == and != with values that are the sameEXPECT_EQ(Range, RangeSame);EXPECT_FALSE(Range != RangeSame);// Test == and != with values that are the differentEXPECT_NE(Range, RangeDifferentStart);EXPECT_NE(Range, RangeDifferentEnd);EXPECT_NE(Range, RangeDifferentStartEnd);EXPECT_FALSE(Range == RangeDifferentStart);EXPECT_FALSE(Range == RangeDifferentEnd);EXPECT_FALSE(Range == RangeDifferentStartEnd);// Test "bool operator<(const AddressRange &, const AddressRange &)".EXPECT_FALSE(Range < RangeSame);EXPECT_FALSE(RangeSame < Range);EXPECT_LT(Range, RangeDifferentStart);EXPECT_LT(Range, RangeDifferentEnd);EXPECT_LT(Range, RangeDifferentStartEnd);// Test "bool operator<(const AddressRange &, uint64_t)"EXPECT_LT(Range.start(), StartAddr + 1);// Test "bool operator<(uint64_t, const AddressRange &)"EXPECT_LT(StartAddr - 1, Range.start());// Verify llvm::AddressRange::isContiguousWith() and// llvm::AddressRange::intersects().const AddressRange EndsBeforeRangeStart(0, StartAddr - 1);const AddressRange EndsAtRangeStart(0, StartAddr);const AddressRange OverlapsRangeStart(StartAddr - 1, StartAddr + 1);const AddressRange InsideRange(StartAddr + 1, EndAddr - 1);const AddressRange OverlapsRangeEnd(EndAddr - 1, EndAddr + 1);const AddressRange StartsAtRangeEnd(EndAddr, EndAddr + 0x100);const AddressRange StartsAfterRangeEnd(EndAddr + 1, EndAddr + 0x100);EXPECT_FALSE(Range.intersects(EndsBeforeRangeStart));EXPECT_FALSE(Range.intersects(EndsAtRangeStart));EXPECT_TRUE(Range.intersects(OverlapsRangeStart));EXPECT_TRUE(Range.intersects(InsideRange));EXPECT_TRUE(Range.intersects(OverlapsRangeEnd));EXPECT_FALSE(Range.intersects(StartsAtRangeEnd));EXPECT_FALSE(Range.intersects(StartsAfterRangeEnd));// Test the functions that maintain address ranges:// "bool AddressRange::contains(uint64_t Addr) const;"// "void AddressRanges::insert(const AddressRange &R);"AddressRanges Ranges;Ranges.insert(AddressRange(0x1000, 0x2000));Ranges.insert(AddressRange(0x2000, 0x3000));Ranges.insert(AddressRange(0x4000, 0x5000));EXPECT_FALSE(Ranges.contains(0));EXPECT_FALSE(Ranges.contains(0x1000 - 1));EXPECT_TRUE(Ranges.contains(0x1000));EXPECT_TRUE(Ranges.contains(0x2000));EXPECT_TRUE(Ranges.contains(0x4000));EXPECT_TRUE(Ranges.contains(0x2000 - 1));EXPECT_TRUE(Ranges.contains(0x3000 - 1));EXPECT_FALSE(Ranges.contains(0x3000 + 1));EXPECT_TRUE(Ranges.contains(0x5000 - 1));EXPECT_FALSE(Ranges.contains(0x5000 + 1));EXPECT_FALSE(Ranges.contains(UINT64_MAX));EXPECT_FALSE(Ranges.contains(AddressRange()));EXPECT_FALSE(Ranges.contains(AddressRange(0x1000 - 1, 0x1000)));EXPECT_FALSE(Ranges.contains(AddressRange(0x1000, 0x1000)));EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x1000 + 1)));EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x2000)));EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x2001)));EXPECT_TRUE(Ranges.contains(AddressRange(0x2000, 0x3000)));EXPECT_FALSE(Ranges.contains(AddressRange(0x2000, 0x3001)));EXPECT_FALSE(Ranges.contains(AddressRange(0x3000, 0x3001)));EXPECT_FALSE(Ranges.contains(AddressRange(0x1500, 0x4500)));EXPECT_FALSE(Ranges.contains(AddressRange(0x5000, 0x5001)));// Verify that intersecting ranges get combinedRanges.clear();Ranges.insert(AddressRange(0x1100, 0x1F00));// Verify a wholy contained range that is added doesn't do anything.Ranges.insert(AddressRange(0x1500, 0x1F00));EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges[0], AddressRange(0x1100, 0x1F00));// Verify a range that starts before and intersects gets combined.Ranges.insert(AddressRange(0x1000, Ranges[0].start() + 1));EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x1F00));// Verify a range that starts inside and extends ranges gets combined.Ranges.insert(AddressRange(Ranges[0].end() - 1, 0x2000));EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x2000));// Verify that adjacent ranges get combinedRanges.insert(AddressRange(0x2000, 0x2fff));EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x2fff));// Verify that ranges having 1 byte gap do not get combinedRanges.insert(AddressRange(0x3000, 0x4000));EXPECT_EQ(Ranges.size(), 2u);EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x2fff));EXPECT_EQ(Ranges[1], AddressRange(0x3000, 0x4000));// Verify if we add an address range that intersects two ranges// that they get combinedRanges.insert(AddressRange(Ranges[0].end() - 1, Ranges[1].start() + 1));EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x4000));Ranges.insert(AddressRange(0x3000, 0x4000));Ranges.insert(AddressRange(0x4000, 0x5000));Ranges.insert(AddressRange(0x2000, 0x4500));EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x5000));}TEST(AddressRangeTest, TestRangesMap) {AddressRangesMap<int> Ranges;EXPECT_EQ(Ranges.size(), 0u);EXPECT_TRUE(Ranges.empty());// Add single range.Ranges.insert(AddressRange(0x1000, 0x2000), 0xfe);EXPECT_EQ(Ranges.size(), 1u);EXPECT_FALSE(Ranges.empty());EXPECT_TRUE(Ranges.contains(0x1500));EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x2000)));// Clear ranges.Ranges.clear();EXPECT_EQ(Ranges.size(), 0u);EXPECT_TRUE(Ranges.empty());// Add range and check value.Ranges.insert(AddressRange(0x1000, 0x2000), 0xfe);EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xfe);// Add adjacent range and check 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));// 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 second range and check 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);// Add intersecting range and check value.Ranges.insert(AddressRange(0x0, 0x6000), 0x1);EXPECT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0x1);// Check that 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(0x600, 0x6ff), 0x7);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);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);}
#include "llvm/Support/ARMAttributeParser.h"#include "llvm/Support/ARMBuildAttributes.h"#include "llvm/Support/ELFAttributes.h"#include "gtest/gtest.h"#include <string>using namespace llvm;struct AttributeSection {unsigned Tag;unsigned Value;AttributeSection(unsigned tag, unsigned value) : Tag(tag), Value(value) { }void write(raw_ostream &OS) {OS.flush();// length = length + "aeabi\0" + TagFile + ByteSize + Tag + Value;// length = 17 bytesOS << 'A' << (uint8_t)17 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0;OS << "aeabi" << '\0';OS << (uint8_t)1 << (uint8_t)7 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0;OS << (uint8_t)Tag << (uint8_t)Value;}};bool testBuildAttr(unsigned Tag, unsigned Value,unsigned ExpectedTag, unsigned ExpectedValue) {std::string buffer;raw_string_ostream OS(buffer);AttributeSection Section(Tag, Value);Section.write(OS);ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t*>(OS.str().c_str()), OS.str().size());ARMAttributeParser Parser;cantFail(Parser.parse(Bytes, support::little));Optional<unsigned> Attr = Parser.getAttributeValue(ExpectedTag);return Attr && *Attr == ExpectedValue;}void testParseError(ArrayRef<uint8_t> bytes, const char *msg) {ARMAttributeParser parser;Error e = parser.parse(bytes, support::little);EXPECT_STREQ(toString(std::move(e)).c_str(), msg);}bool testTagString(unsigned Tag, const char *name) {return ELFAttrs::attrTypeAsString(Tag, ARMBuildAttrs::getARMAttributeTags()).str() == name;}TEST(ARMAttributeParser, UnknownCPU_arch) {static const uint8_t bytes[] = {'A', 15, 0, 0, 0, 'a', 'e', 'a', 'b','i', 0, 1, 7, 0, 0, 0, 6, 23};testParseError(bytes, "unknown CPU_arch value: 23");}TEST(CPUArchBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(6, "Tag_CPU_arch"));EXPECT_TRUE(testBuildAttr(6, 0, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::Pre_v4));EXPECT_TRUE(testBuildAttr(6, 1, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v4));EXPECT_TRUE(testBuildAttr(6, 2, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v4T));EXPECT_TRUE(testBuildAttr(6, 3, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v5T));EXPECT_TRUE(testBuildAttr(6, 4, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v5TE));EXPECT_TRUE(testBuildAttr(6, 5, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v5TEJ));EXPECT_TRUE(testBuildAttr(6, 6, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v6));EXPECT_TRUE(testBuildAttr(6, 7, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v6KZ));EXPECT_TRUE(testBuildAttr(6, 8, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v6T2));EXPECT_TRUE(testBuildAttr(6, 9, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v6K));EXPECT_TRUE(testBuildAttr(6, 10, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v7));EXPECT_TRUE(testBuildAttr(6, 11, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v6_M));EXPECT_TRUE(testBuildAttr(6, 12, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v6S_M));EXPECT_TRUE(testBuildAttr(6, 13, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v7E_M));EXPECT_TRUE(testBuildAttr(6, 14, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v8_A));EXPECT_TRUE(testBuildAttr(6, 15, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v8_R));EXPECT_TRUE(testBuildAttr(6, 16, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v8_M_Base));EXPECT_TRUE(testBuildAttr(6, 17, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v8_M_Main));EXPECT_TRUE(testBuildAttr(6, 21, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v8_1_M_Main));EXPECT_TRUE(testBuildAttr(6, 22, ARMBuildAttrs::CPU_arch,ARMBuildAttrs::v9_A));}TEST(CPUArchProfileBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(7, "Tag_CPU_arch_profile"));EXPECT_TRUE(testBuildAttr(7, 'A', ARMBuildAttrs::CPU_arch_profile,ARMBuildAttrs::ApplicationProfile));EXPECT_TRUE(testBuildAttr(7, 'R', ARMBuildAttrs::CPU_arch_profile,ARMBuildAttrs::RealTimeProfile));EXPECT_TRUE(testBuildAttr(7, 'M', ARMBuildAttrs::CPU_arch_profile,ARMBuildAttrs::MicroControllerProfile));EXPECT_TRUE(testBuildAttr(7, 'S', ARMBuildAttrs::CPU_arch_profile,ARMBuildAttrs::SystemProfile));}TEST(ARMISABuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(8, "Tag_ARM_ISA_use"));EXPECT_TRUE(testBuildAttr(8, 0, ARMBuildAttrs::ARM_ISA_use,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(8, 1, ARMBuildAttrs::ARM_ISA_use,ARMBuildAttrs::Allowed));}TEST(ThumbISABuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(9, "Tag_THUMB_ISA_use"));EXPECT_TRUE(testBuildAttr(9, 0, ARMBuildAttrs::THUMB_ISA_use,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(9, 1, ARMBuildAttrs::THUMB_ISA_use,ARMBuildAttrs::Allowed));EXPECT_TRUE(testBuildAttr(9, 2, ARMBuildAttrs::THUMB_ISA_use,ARMBuildAttrs::AllowThumb32));EXPECT_TRUE(testBuildAttr(9, 3, ARMBuildAttrs::THUMB_ISA_use,ARMBuildAttrs::AllowThumbDerived));}TEST(FPArchBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(10, "Tag_FP_arch"));EXPECT_TRUE(testBuildAttr(10, 0, ARMBuildAttrs::FP_arch,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(10, 1, ARMBuildAttrs::FP_arch,ARMBuildAttrs::Allowed));EXPECT_TRUE(testBuildAttr(10, 2, ARMBuildAttrs::FP_arch,ARMBuildAttrs::AllowFPv2));EXPECT_TRUE(testBuildAttr(10, 3, ARMBuildAttrs::FP_arch,ARMBuildAttrs::AllowFPv3A));EXPECT_TRUE(testBuildAttr(10, 4, ARMBuildAttrs::FP_arch,ARMBuildAttrs::AllowFPv3B));EXPECT_TRUE(testBuildAttr(10, 5, ARMBuildAttrs::FP_arch,ARMBuildAttrs::AllowFPv4A));EXPECT_TRUE(testBuildAttr(10, 6, ARMBuildAttrs::FP_arch,ARMBuildAttrs::AllowFPv4B));EXPECT_TRUE(testBuildAttr(10, 7, ARMBuildAttrs::FP_arch,ARMBuildAttrs::AllowFPARMv8A));EXPECT_TRUE(testBuildAttr(10, 8, ARMBuildAttrs::FP_arch,ARMBuildAttrs::AllowFPARMv8B));}TEST(WMMXBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(11, "Tag_WMMX_arch"));EXPECT_TRUE(testBuildAttr(11, 0, ARMBuildAttrs::WMMX_arch,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(11, 1, ARMBuildAttrs::WMMX_arch,ARMBuildAttrs::AllowWMMXv1));EXPECT_TRUE(testBuildAttr(11, 2, ARMBuildAttrs::WMMX_arch,ARMBuildAttrs::AllowWMMXv2));}TEST(SIMDBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(12, "Tag_Advanced_SIMD_arch"));EXPECT_TRUE(testBuildAttr(12, 0, ARMBuildAttrs::Advanced_SIMD_arch,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(12, 1, ARMBuildAttrs::Advanced_SIMD_arch,ARMBuildAttrs::AllowNeon));EXPECT_TRUE(testBuildAttr(12, 2, ARMBuildAttrs::Advanced_SIMD_arch,ARMBuildAttrs::AllowNeon2));EXPECT_TRUE(testBuildAttr(12, 3, ARMBuildAttrs::Advanced_SIMD_arch,ARMBuildAttrs::AllowNeonARMv8));EXPECT_TRUE(testBuildAttr(12, 4, ARMBuildAttrs::Advanced_SIMD_arch,ARMBuildAttrs::AllowNeonARMv8_1a));}TEST(FPHPBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(36, "Tag_FP_HP_extension"));EXPECT_TRUE(testBuildAttr(36, 0, ARMBuildAttrs::FP_HP_extension,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(36, 1, ARMBuildAttrs::FP_HP_extension,ARMBuildAttrs::AllowHPFP));}TEST(MVEBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(48, "Tag_MVE_arch"));EXPECT_TRUE(testBuildAttr(48, 0, ARMBuildAttrs::MVE_arch,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(48, 1, ARMBuildAttrs::MVE_arch,ARMBuildAttrs::AllowMVEInteger));EXPECT_TRUE(testBuildAttr(48, 2, ARMBuildAttrs::MVE_arch,ARMBuildAttrs::AllowMVEIntegerAndFloat));}TEST(CPUAlignBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(34, "Tag_CPU_unaligned_access"));EXPECT_TRUE(testBuildAttr(34, 0, ARMBuildAttrs::CPU_unaligned_access,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(34, 1, ARMBuildAttrs::CPU_unaligned_access,ARMBuildAttrs::Allowed));}TEST(T2EEBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(66, "Tag_T2EE_use"));EXPECT_TRUE(testBuildAttr(66, 0, ARMBuildAttrs::T2EE_use,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(66, 1, ARMBuildAttrs::T2EE_use,ARMBuildAttrs::Allowed));}TEST(VirtualizationBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(68, "Tag_Virtualization_use"));EXPECT_TRUE(testBuildAttr(68, 0, ARMBuildAttrs::Virtualization_use,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(68, 1, ARMBuildAttrs::Virtualization_use,ARMBuildAttrs::AllowTZ));EXPECT_TRUE(testBuildAttr(68, 2, ARMBuildAttrs::Virtualization_use,ARMBuildAttrs::AllowVirtualization));EXPECT_TRUE(testBuildAttr(68, 3, ARMBuildAttrs::Virtualization_use,ARMBuildAttrs::AllowTZVirtualization));}TEST(MPBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(42, "Tag_MPextension_use"));EXPECT_TRUE(testBuildAttr(42, 0, ARMBuildAttrs::MPextension_use,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(42, 1, ARMBuildAttrs::MPextension_use,ARMBuildAttrs::AllowMP));}TEST(DivBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(44, "Tag_DIV_use"));EXPECT_TRUE(testBuildAttr(44, 0, ARMBuildAttrs::DIV_use,ARMBuildAttrs::AllowDIVIfExists));EXPECT_TRUE(testBuildAttr(44, 1, ARMBuildAttrs::DIV_use,ARMBuildAttrs::DisallowDIV));EXPECT_TRUE(testBuildAttr(44, 2, ARMBuildAttrs::DIV_use,ARMBuildAttrs::AllowDIVExt));}TEST(PCS_ConfigBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(13, "Tag_PCS_config"));EXPECT_TRUE(testBuildAttr(13, 0, ARMBuildAttrs::PCS_config, 0));EXPECT_TRUE(testBuildAttr(13, 1, ARMBuildAttrs::PCS_config, 1));EXPECT_TRUE(testBuildAttr(13, 2, ARMBuildAttrs::PCS_config, 2));EXPECT_TRUE(testBuildAttr(13, 3, ARMBuildAttrs::PCS_config, 3));EXPECT_TRUE(testBuildAttr(13, 4, ARMBuildAttrs::PCS_config, 4));EXPECT_TRUE(testBuildAttr(13, 5, ARMBuildAttrs::PCS_config, 5));EXPECT_TRUE(testBuildAttr(13, 6, ARMBuildAttrs::PCS_config, 6));EXPECT_TRUE(testBuildAttr(13, 7, ARMBuildAttrs::PCS_config, 7));}TEST(PCS_R9BuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(14, "Tag_ABI_PCS_R9_use"));EXPECT_TRUE(testBuildAttr(14, 0, ARMBuildAttrs::ABI_PCS_R9_use,ARMBuildAttrs::R9IsGPR));EXPECT_TRUE(testBuildAttr(14, 1, ARMBuildAttrs::ABI_PCS_R9_use,ARMBuildAttrs::R9IsSB));EXPECT_TRUE(testBuildAttr(14, 2, ARMBuildAttrs::ABI_PCS_R9_use,ARMBuildAttrs::R9IsTLSPointer));EXPECT_TRUE(testBuildAttr(14, 3, ARMBuildAttrs::ABI_PCS_R9_use,ARMBuildAttrs::R9Reserved));}TEST(PCS_RWBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(15, "Tag_ABI_PCS_RW_data"));EXPECT_TRUE(testBuildAttr(15, 0, ARMBuildAttrs::ABI_PCS_RW_data,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(15, 1, ARMBuildAttrs::ABI_PCS_RW_data,ARMBuildAttrs::AddressRWPCRel));EXPECT_TRUE(testBuildAttr(15, 2, ARMBuildAttrs::ABI_PCS_RW_data,ARMBuildAttrs::AddressRWSBRel));EXPECT_TRUE(testBuildAttr(15, 3, ARMBuildAttrs::ABI_PCS_RW_data,ARMBuildAttrs::AddressRWNone));}TEST(PCS_ROBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(16, "Tag_ABI_PCS_RO_data"));EXPECT_TRUE(testBuildAttr(16, 0, ARMBuildAttrs::ABI_PCS_RO_data,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(16, 1, ARMBuildAttrs::ABI_PCS_RO_data,ARMBuildAttrs::AddressROPCRel));EXPECT_TRUE(testBuildAttr(16, 2, ARMBuildAttrs::ABI_PCS_RO_data,ARMBuildAttrs::AddressRONone));}TEST(PCS_GOTBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(17, "Tag_ABI_PCS_GOT_use"));EXPECT_TRUE(testBuildAttr(17, 0, ARMBuildAttrs::ABI_PCS_GOT_use,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(17, 1, ARMBuildAttrs::ABI_PCS_GOT_use,ARMBuildAttrs::AddressDirect));EXPECT_TRUE(testBuildAttr(17, 2, ARMBuildAttrs::ABI_PCS_GOT_use,ARMBuildAttrs::AddressGOT));}TEST(PCS_WCharBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(18, "Tag_ABI_PCS_wchar_t"));EXPECT_TRUE(testBuildAttr(18, 0, ARMBuildAttrs::ABI_PCS_wchar_t,ARMBuildAttrs::WCharProhibited));EXPECT_TRUE(testBuildAttr(18, 2, ARMBuildAttrs::ABI_PCS_wchar_t,ARMBuildAttrs::WCharWidth2Bytes));EXPECT_TRUE(testBuildAttr(18, 4, ARMBuildAttrs::ABI_PCS_wchar_t,ARMBuildAttrs::WCharWidth4Bytes));}TEST(EnumSizeBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(26, "Tag_ABI_enum_size"));EXPECT_TRUE(testBuildAttr(26, 0, ARMBuildAttrs::ABI_enum_size,ARMBuildAttrs::EnumProhibited));EXPECT_TRUE(testBuildAttr(26, 1, ARMBuildAttrs::ABI_enum_size,ARMBuildAttrs::EnumSmallest));EXPECT_TRUE(testBuildAttr(26, 2, ARMBuildAttrs::ABI_enum_size,ARMBuildAttrs::Enum32Bit));EXPECT_TRUE(testBuildAttr(26, 3, ARMBuildAttrs::ABI_enum_size,ARMBuildAttrs::Enum32BitABI));}TEST(AlignNeededBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(24, "Tag_ABI_align_needed"));EXPECT_TRUE(testBuildAttr(24, 0, ARMBuildAttrs::ABI_align_needed,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(24, 1, ARMBuildAttrs::ABI_align_needed,ARMBuildAttrs::Align8Byte));EXPECT_TRUE(testBuildAttr(24, 2, ARMBuildAttrs::ABI_align_needed,ARMBuildAttrs::Align4Byte));EXPECT_TRUE(testBuildAttr(24, 3, ARMBuildAttrs::ABI_align_needed,ARMBuildAttrs::AlignReserved));}TEST(AlignPreservedBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(25, "Tag_ABI_align_preserved"));EXPECT_TRUE(testBuildAttr(25, 0, ARMBuildAttrs::ABI_align_preserved,ARMBuildAttrs::AlignNotPreserved));EXPECT_TRUE(testBuildAttr(25, 1, ARMBuildAttrs::ABI_align_preserved,ARMBuildAttrs::AlignPreserve8Byte));EXPECT_TRUE(testBuildAttr(25, 2, ARMBuildAttrs::ABI_align_preserved,ARMBuildAttrs::AlignPreserveAll));EXPECT_TRUE(testBuildAttr(25, 3, ARMBuildAttrs::ABI_align_preserved,ARMBuildAttrs::AlignReserved));}TEST(FPRoundingBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(19, "Tag_ABI_FP_rounding"));EXPECT_TRUE(testBuildAttr(19, 0, ARMBuildAttrs::ABI_FP_rounding, 0));EXPECT_TRUE(testBuildAttr(19, 1, ARMBuildAttrs::ABI_FP_rounding, 1));}TEST(FPDenormalBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(20, "Tag_ABI_FP_denormal"));EXPECT_TRUE(testBuildAttr(20, 0, ARMBuildAttrs::ABI_FP_denormal,ARMBuildAttrs::PositiveZero));EXPECT_TRUE(testBuildAttr(20, 1, ARMBuildAttrs::ABI_FP_denormal,ARMBuildAttrs::IEEEDenormals));EXPECT_TRUE(testBuildAttr(20, 2, ARMBuildAttrs::ABI_FP_denormal,ARMBuildAttrs::PreserveFPSign));}TEST(FPExceptionsBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(21, "Tag_ABI_FP_exceptions"));EXPECT_TRUE(testBuildAttr(21, 0, ARMBuildAttrs::ABI_FP_exceptions, 0));EXPECT_TRUE(testBuildAttr(21, 1, ARMBuildAttrs::ABI_FP_exceptions, 1));}TEST(FPUserExceptionsBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(22, "Tag_ABI_FP_user_exceptions"));EXPECT_TRUE(testBuildAttr(22, 0, ARMBuildAttrs::ABI_FP_user_exceptions, 0));EXPECT_TRUE(testBuildAttr(22, 1, ARMBuildAttrs::ABI_FP_user_exceptions, 1));}TEST(FPNumberModelBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(23, "Tag_ABI_FP_number_model"));EXPECT_TRUE(testBuildAttr(23, 0, ARMBuildAttrs::ABI_FP_number_model,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(23, 1, ARMBuildAttrs::ABI_FP_number_model,ARMBuildAttrs::AllowIEEENormal));EXPECT_TRUE(testBuildAttr(23, 2, ARMBuildAttrs::ABI_FP_number_model,ARMBuildAttrs::AllowRTABI));EXPECT_TRUE(testBuildAttr(23, 3, ARMBuildAttrs::ABI_FP_number_model,ARMBuildAttrs::AllowIEEE754));}TEST(FP16BuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(38, "Tag_ABI_FP_16bit_format"));EXPECT_TRUE(testBuildAttr(38, 0, ARMBuildAttrs::ABI_FP_16bit_format,ARMBuildAttrs::Not_Allowed));EXPECT_TRUE(testBuildAttr(38, 1, ARMBuildAttrs::ABI_FP_16bit_format,ARMBuildAttrs::FP16FormatIEEE));EXPECT_TRUE(testBuildAttr(38, 2, ARMBuildAttrs::ABI_FP_16bit_format,ARMBuildAttrs::FP16VFP3));}TEST(HardFPBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(27, "Tag_ABI_HardFP_use"));EXPECT_TRUE(testBuildAttr(27, 0, ARMBuildAttrs::ABI_HardFP_use,ARMBuildAttrs::HardFPImplied));EXPECT_TRUE(testBuildAttr(27, 1, ARMBuildAttrs::ABI_HardFP_use,ARMBuildAttrs::HardFPSinglePrecision));EXPECT_TRUE(testBuildAttr(27, 2, ARMBuildAttrs::ABI_HardFP_use, 2));}TEST(VFPArgsBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(28, "Tag_ABI_VFP_args"));EXPECT_TRUE(testBuildAttr(28, 0, ARMBuildAttrs::ABI_VFP_args,ARMBuildAttrs::BaseAAPCS));EXPECT_TRUE(testBuildAttr(28, 1, ARMBuildAttrs::ABI_VFP_args,ARMBuildAttrs::HardFPAAPCS));EXPECT_TRUE(testBuildAttr(28, 2, ARMBuildAttrs::ABI_VFP_args, 2));EXPECT_TRUE(testBuildAttr(28, 3, ARMBuildAttrs::ABI_VFP_args, 3));}TEST(WMMXArgsBuildAttr, testBuildAttr) {EXPECT_TRUE(testTagString(29, "Tag_ABI_WMMX_args"));EXPECT_TRUE(testBuildAttr(29, 0, ARMBuildAttrs::ABI_WMMX_args, 0));EXPECT_TRUE(testBuildAttr(29, 1, ARMBuildAttrs::ABI_WMMX_args, 1));EXPECT_TRUE(testBuildAttr(29, 2, ARMBuildAttrs::ABI_WMMX_args, 2));}
//===- unittest/Support/YAMLRemarksSerializerTest.cpp --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Remarks/Remark.h"#include "llvm/Remarks/RemarkParser.h"#include "llvm/Remarks/YAMLRemarkSerializer.h"#include "llvm/Support/Error.h"#include "gtest/gtest.h"// We need to supprt Windows paths as well. In order to have paths with the same// length, use a different path according to the platform.#ifdef _WIN32#define EXTERNALFILETESTPATH "C:/externalfi"#else#define EXTERNALFILETESTPATH "/externalfile"#endifusing namespace llvm;static void check(remarks::Format SerializerFormat,remarks::SerializerMode Mode, ArrayRef<remarks::Remark> Rs,StringRef ExpectedR, Optional<StringRef> ExpectedMeta,Optional<remarks::StringTable> StrTab = None) {std::string Buf;raw_string_ostream OS(Buf);Expected<std::unique_ptr<remarks::RemarkSerializer>> MaybeS = [&] {if (StrTab)return createRemarkSerializer(SerializerFormat, Mode, OS,std::move(*StrTab));elsereturn createRemarkSerializer(SerializerFormat, Mode, OS);}();EXPECT_FALSE(errorToBool(MaybeS.takeError()));std::unique_ptr<remarks::RemarkSerializer> S = std::move(*MaybeS);for (const remarks::Remark &R : Rs)S->emit(R);EXPECT_EQ(OS.str(), ExpectedR);if (ExpectedMeta) {Buf.clear();std::unique_ptr<remarks::MetaSerializer> MS =S->metaSerializer(OS, StringRef(EXTERNALFILETESTPATH));MS->emit();EXPECT_EQ(OS.str(), *ExpectedMeta);}}static void check(remarks::Format SerializerFormat, const remarks::Remark &R,StringRef ExpectedR, StringRef ExpectedMeta,Optional<remarks::StringTable> StrTab = None) {return check(SerializerFormat, remarks::SerializerMode::Separate,makeArrayRef(&R, &R + 1), ExpectedR, ExpectedMeta,std::move(StrTab));}static void checkStandalone(remarks::Format SerializerFormat,const remarks::Remark &R, StringRef ExpectedR,Optional<remarks::StringTable> StrTab = None) {return check(SerializerFormat, remarks::SerializerMode::Standalone,makeArrayRef(&R, &R + 1), ExpectedR,/*ExpectedMeta=*/None, std::move(StrTab));}TEST(YAMLRemarks, SerializerRemark) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};check(remarks::Format::YAML, R,"--- !Missed\n""Pass: pass\n""Name: name\n""DebugLoc: { File: path, Line: 3, Column: 4 }\n""Function: func\n""Hotness: 5\n""Args:\n"" - key: value\n"" - keydebug: valuedebug\n"" DebugLoc: { File: argpath, Line: 6, Column: 7 }\n""...\n",StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\0\0\0\0\0\0\0\0" EXTERNALFILETESTPATH "\0",38));}TEST(YAMLRemarks, SerializerRemarkStandalone) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};checkStandalone(remarks::Format::YAML, R,StringRef("--- !Missed\n""Pass: pass\n""Name: name\n""DebugLoc: { File: path, Line: 3, Column: 4 }\n""Function: func\n""Hotness: 5\n""Args:\n"" - key: value\n"" - keydebug: valuedebug\n"" DebugLoc: { File: argpath, Line: 6, Column: 7 }\n""...\n"));}TEST(YAMLRemarks, SerializerRemarkStrTab) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};check(remarks::Format::YAMLStrTab, R,"--- !Missed\n""Pass: 0\n""Name: 1\n""DebugLoc: { File: 3, Line: 3, Column: 4 }\n""Function: 2\n""Hotness: 5\n""Args:\n"" - key: 4\n"" - keydebug: 5\n"" DebugLoc: { File: 6, Line: 6, Column: 7 }\n""...\n",StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\x2d\0\0\0\0\0\0\0""pass\0name\0func\0path\0value\0valuedebug\0argpath""\0" EXTERNALFILETESTPATH "\0",83));}TEST(YAMLRemarks, SerializerRemarkParsedStrTab) {StringRef StrTab("pass\0name\0func\0path\0value\0valuedebug\0argpath\0", 45);remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};check(remarks::Format::YAMLStrTab, R,"--- !Missed\n""Pass: 0\n""Name: 1\n""DebugLoc: { File: 3, Line: 3, Column: 4 }\n""Function: 2\n""Hotness: 5\n""Args:\n"" - key: 4\n"" - keydebug: 5\n"" DebugLoc: { File: 6, Line: 6, Column: 7 }\n""...\n",StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\x2d\0\0\0\0\0\0\0""pass\0name\0func\0path\0value\0valuedebug\0argpath""\0" EXTERNALFILETESTPATH "\0",83),remarks::StringTable(remarks::ParsedStringTable(StrTab)));}TEST(YAMLRemarks, SerializerRemarkParsedStrTabStandaloneNoStrTab) {// Check that we don't use the string table even if it was provided.StringRef StrTab("pass\0name\0func\0path\0value\0valuedebug\0argpath\0", 45);remarks::ParsedStringTable ParsedStrTab(StrTab);remarks::StringTable PreFilledStrTab(ParsedStrTab);remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};checkStandalone(remarks::Format::YAML, R,StringRef("--- !Missed\n""Pass: pass\n""Name: name\n""DebugLoc: { File: path, Line: 3, Column: 4 }\n""Function: func\n""Hotness: 5\n""Args:\n"" - key: value\n"" - keydebug: valuedebug\n"" DebugLoc: { File: argpath, Line: 6, Column: 7 }\n""...\n"),std::move(PreFilledStrTab));}TEST(YAMLRemarks, SerializerRemarkParsedStrTabStandalone) {StringRef StrTab("pass\0name\0func\0path\0value\0valuedebug\0argpath\0", 45);remarks::ParsedStringTable ParsedStrTab(StrTab);remarks::StringTable PreFilledStrTab(ParsedStrTab);remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};checkStandalone(remarks::Format::YAMLStrTab, R,StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\x2d\0\0\0\0\0\0\0""pass\0name\0func\0path\0value\0valuedebug\0argpath\0""--- !Missed\n""Pass: 0\n""Name: 1\n""DebugLoc: { File: 3, Line: 3, Column: 4 }\n""Function: 2\n""Hotness: 5\n""Args:\n"" - key: 4\n"" - keydebug: 5\n"" DebugLoc: { File: 6, Line: 6, Column: 7 }\n""...\n",315),std::move(PreFilledStrTab));}TEST(YAMLRemarks, SerializerRemarkParsedStrTabStandaloneMultipleRemarks) {StringRef StrTab("pass\0name\0func\0path\0value\0valuedebug\0argpath\0", 45);remarks::ParsedStringTable ParsedStrTab(StrTab);remarks::StringTable PreFilledStrTab(ParsedStrTab);SmallVector<remarks::Remark, 2> Rs;remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};Rs.emplace_back(R.clone());Rs.emplace_back(std::move(R));check(remarks::Format::YAMLStrTab, remarks::SerializerMode::Standalone, Rs,StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\x2d\0\0\0\0\0\0\0""pass\0name\0func\0path\0value\0valuedebug\0argpath\0""--- !Missed\n""Pass: 0\n""Name: 1\n""DebugLoc: { File: 3, Line: 3, Column: 4 }\n""Function: 2\n""Hotness: 5\n""Args:\n"" - key: 4\n"" - keydebug: 5\n"" DebugLoc: { File: 6, Line: 6, Column: 7 }\n""...\n""--- !Missed\n""Pass: 0\n""Name: 1\n""DebugLoc: { File: 3, Line: 3, Column: 4 }\n""Function: 2\n""Hotness: 5\n""Args:\n"" - key: 4\n"" - keydebug: 5\n"" DebugLoc: { File: 6, Line: 6, Column: 7 }\n""...\n",561),/*ExpectedMeta=*/None, std::move(PreFilledStrTab));}
//===- unittest/Support/YAMLRemarksParsingTest.cpp - OptTable tests -------===////// 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////===----------------------------------------------------------------------===//#include "llvm-c/Remarks.h"#include "llvm/Remarks/Remark.h"#include "llvm/Remarks/RemarkParser.h"#include "gtest/gtest.h"using namespace llvm;template <size_t N> void parseGood(const char (&Buf)[N]) {Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.EXPECT_TRUE(*Remark != nullptr); // At least one remark.Remark = Parser.next();Error E = Remark.takeError();EXPECT_TRUE(E.isA<remarks::EndOfFileError>());EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.}void parseGoodMeta(StringRef Buf) {Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParserFromMeta(remarks::Format::YAML, Buf);EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.EXPECT_TRUE(*Remark != nullptr); // At least one remark.Remark = Parser.next();Error E = Remark.takeError();EXPECT_TRUE(E.isA<remarks::EndOfFileError>());EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.}template <size_t N>bool parseExpectError(const char (&Buf)[N], const char *Error) {Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();EXPECT_FALSE(Remark); // Check for parsing errors.std::string ErrorStr;raw_string_ostream Stream(ErrorStr);handleAllErrors(Remark.takeError(),[&](const ErrorInfoBase &EIB) { EIB.log(Stream); });return StringRef(Stream.str()).contains(Error);}enum class CmpType {Equal,Contains};void parseExpectErrorMeta(StringRef Buf, const char *Error, CmpType Cmp,Optional<StringRef> ExternalFilePrependPath = None) {std::string ErrorStr;raw_string_ostream Stream(ErrorStr);Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParserFromMeta(remarks::Format::YAML, Buf,/*StrTab=*/None,std::move(ExternalFilePrependPath));handleAllErrors(MaybeParser.takeError(),[&](const ErrorInfoBase &EIB) { EIB.log(Stream); });// Use a case insensitive comparision due to case differences in error strings// for different OSs.if (Cmp == CmpType::Equal) {EXPECT_EQ(StringRef(Stream.str()).lower(), StringRef(Error).lower());}if (Cmp == CmpType::Contains) {EXPECT_TRUE(StringRef(Stream.str()).contains(StringRef(Error)));}}TEST(YAMLRemarks, ParsingEmpty) {EXPECT_TRUE(parseExpectError("\n\n", "document root is not of mapping type."));}TEST(YAMLRemarks, ParsingNotYAML) {EXPECT_TRUE(parseExpectError("\x01\x02\x03\x04\x05\x06", "Got empty plain scalar"));}TEST(YAMLRemarks, ParsingGood) {parseGood("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""");// No debug loc should also pass.parseGood("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""");// No args is also ok.parseGood("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""");// Different order.parseGood("\n""--- !Missed\n""DebugLoc: { Line: 3, Column: 12, File: file.c }\n""Function: foo\n""Name: NoDefinition\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""Pass: inline\n""");}// Mandatory common part of a remark.#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"// Test all the types.TEST(YAMLRemarks, ParsingTypes) {// Type: PassedparseGood("--- !Passed" COMMON_REMARK);// Type: MissedparseGood("--- !Missed" COMMON_REMARK);// Type: AnalysisparseGood("--- !Analysis" COMMON_REMARK);// Type: AnalysisFPCommuteparseGood("--- !AnalysisFPCommute" COMMON_REMARK);// Type: AnalysisAliasingparseGood("--- !AnalysisAliasing" COMMON_REMARK);// Type: FailureparseGood("--- !Failure" COMMON_REMARK);}#undef COMMON_REMARKTEST(YAMLRemarks, ParsingMissingFields) {// No type.EXPECT_TRUE(parseExpectError("\n""---\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""","expected a remark tag."));// No pass.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Name: NoDefinition\n""Function: foo\n""","Type, Pass, Name or Function missing."));// No name.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Function: foo\n""","Type, Pass, Name or Function missing."));// No function.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""","Type, Pass, Name or Function missing."));// Debug loc but no file.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { Line: 3, Column: 12 }\n""","DebugLoc node incomplete."));// Debug loc but no line.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Column: 12 }\n""","DebugLoc node incomplete."));// Debug loc but no column.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Line: 3 }\n""","DebugLoc node incomplete."));}TEST(YAMLRemarks, ParsingWrongTypes) {// Wrong debug loc type.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: foo\n""","expected a value of mapping type."));// Wrong line type.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Line: b, Column: 12 }\n""","expected a value of integer type."));// Wrong column type.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Line: 3, Column: c }\n""","expected a value of integer type."));// Wrong args type.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""Args: foo\n""","wrong value type for key."));// Wrong key type.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""{ A: a }: inline\n""Name: NoDefinition\n""Function: foo\n""","key is not a string."));// Debug loc with unknown entry.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Column: 12, Unknown: 12 }\n""","unknown entry in DebugLoc map."));// Unknown entry.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Unknown: inline\n""","unknown key."));// Not a scalar.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: { File: a, Line: 1, Column: 2 }\n""Name: NoDefinition\n""Function: foo\n""","expected a value of scalar type."));// Not a string file in debug loc.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: { a: b }, Column: 12, Line: 12 }\n""","expected a value of scalar type."));// Not a integer column in debug loc.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Column: { a: b }, Line: 12 }\n""","expected a value of scalar type."));// Not a integer line in debug loc.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n""","expected a value of scalar type."));// Not a mapping type value for args.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n""","expected a value of scalar type."));}TEST(YAMLRemarks, ParsingWrongArgs) {// Multiple debug locs per arg.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""Args:\n"" - Str: string\n"" DebugLoc: { File: a, Line: 1, Column: 2 }\n"" DebugLoc: { File: a, Line: 1, Column: 2 }\n""","only one DebugLoc entry is allowed per argument."));// Multiple strings per arg.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""Args:\n"" - Str: string\n"" Str2: string\n"" DebugLoc: { File: a, Line: 1, Column: 2 }\n""","only one string entry is allowed per argument."));// No arg value.EXPECT_TRUE(parseExpectError("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""Args:\n"" - DebugLoc: { File: a, Line: 1, Column: 2 }\n""","argument key is missing."));}static inline StringRef checkStr(StringRef Str, unsigned ExpectedLen) {const char *StrData = Str.data();unsigned StrLen = Str.size();EXPECT_EQ(StrLen, ExpectedLen);return StringRef(StrData, StrLen);}TEST(YAMLRemarks, Contents) {StringRef Buf = "\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""Hotness: 4\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""\n";Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParser(remarks::Format::YAML, Buf);EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();EXPECT_FALSE(errorToBool(MaybeRemark.takeError())); // Check for parsing errors.EXPECT_TRUE(*MaybeRemark != nullptr); // At least one remark.const remarks::Remark &Remark = **MaybeRemark;EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");EXPECT_TRUE(Remark.Loc);const remarks::RemarkLocation &RL = *Remark.Loc;EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");EXPECT_EQ(RL.SourceLine, 3U);EXPECT_EQ(RL.SourceColumn, 12U);EXPECT_TRUE(Remark.Hotness);EXPECT_EQ(*Remark.Hotness, 4U);EXPECT_EQ(Remark.Args.size(), 4U);unsigned ArgID = 0;for (const remarks::Argument &Arg : Remark.Args) {switch (ArgID) {case 0:EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");EXPECT_EQ(checkStr(Arg.Val, 3), "bar");EXPECT_FALSE(Arg.Loc);break;case 1:EXPECT_EQ(checkStr(Arg.Key, 6), "String");EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");EXPECT_FALSE(Arg.Loc);break;case 2: {EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");EXPECT_EQ(checkStr(Arg.Val, 3), "foo");EXPECT_TRUE(Arg.Loc);const remarks::RemarkLocation &RL = *Arg.Loc;EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");EXPECT_EQ(RL.SourceLine, 2U);EXPECT_EQ(RL.SourceColumn, 0U);break;}case 3:EXPECT_EQ(checkStr(Arg.Key, 6), "String");EXPECT_EQ(checkStr(Arg.Val, 38)," because its definition is unavailable");EXPECT_FALSE(Arg.Loc);break;default:break;}++ArgID;}MaybeRemark = Parser.next();Error E = MaybeRemark.takeError();EXPECT_TRUE(E.isA<remarks::EndOfFileError>());EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.}static inline StringRef checkStr(LLVMRemarkStringRef Str,unsigned ExpectedLen) {const char *StrData = LLVMRemarkStringGetData(Str);unsigned StrLen = LLVMRemarkStringGetLen(Str);EXPECT_EQ(StrLen, ExpectedLen);return StringRef(StrData, StrLen);}TEST(YAMLRemarks, ContentsCAPI) {StringRef Buf = "\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""\n";LLVMRemarkParserRef Parser =LLVMRemarkParserCreateYAML(Buf.data(), Buf.size());LLVMRemarkEntryRef Remark = LLVMRemarkParserGetNext(Parser);EXPECT_FALSE(Remark == nullptr);EXPECT_EQ(LLVMRemarkEntryGetType(Remark), LLVMRemarkTypeMissed);EXPECT_EQ(checkStr(LLVMRemarkEntryGetPassName(Remark), 6), "inline");EXPECT_EQ(checkStr(LLVMRemarkEntryGetRemarkName(Remark), 12), "NoDefinition");EXPECT_EQ(checkStr(LLVMRemarkEntryGetFunctionName(Remark), 3), "foo");LLVMRemarkDebugLocRef DL = LLVMRemarkEntryGetDebugLoc(Remark);EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 3U);EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 12U);EXPECT_EQ(LLVMRemarkEntryGetHotness(Remark), 0U);EXPECT_EQ(LLVMRemarkEntryGetNumArgs(Remark), 4U);unsigned ArgID = 0;LLVMRemarkArgRef Arg = LLVMRemarkEntryGetFirstArg(Remark);do {switch (ArgID) {case 0:EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Callee");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "bar");EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);break;case 1:EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 26)," will not be inlined into ");EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);break;case 2: {EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Caller");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "foo");LLVMRemarkDebugLocRef DL = LLVMRemarkArgGetDebugLoc(Arg);EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 2U);EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 0U);break;}case 3:EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 38)," because its definition is unavailable");EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);break;default:break;}++ArgID;} while ((Arg = LLVMRemarkEntryGetNextArg(Arg, Remark)));LLVMRemarkEntryDispose(Remark);EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr);EXPECT_FALSE(LLVMRemarkParserHasError(Parser));LLVMRemarkParserDispose(Parser);}TEST(YAMLRemarks, ContentsStrTab) {StringRef Buf = "\n""--- !Missed\n""Pass: 0\n""Name: 1\n""DebugLoc: { File: 2, Line: 3, Column: 12 }\n""Function: 3\n""Hotness: 4\n""Args:\n"" - Callee: 5\n"" - String: 7\n"" - Caller: 3\n"" DebugLoc: { File: 2, Line: 2, Column: 0 }\n"" - String: 8\n""\n";StringRef StrTabBuf =StringRef("inline\0NoDefinition\0file.c\0foo\0Callee\0bar\0String\0 ""will not be inlined into \0 because its definition is ""unavailable",115);remarks::ParsedStringTable StrTab(StrTabBuf);Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParser(remarks::Format::YAMLStrTab, Buf,std::move(StrTab));EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();EXPECT_FALSE(errorToBool(MaybeRemark.takeError())); // Check for parsing errors.EXPECT_TRUE(*MaybeRemark != nullptr); // At least one remark.const remarks::Remark &Remark = **MaybeRemark;EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");EXPECT_TRUE(Remark.Loc);const remarks::RemarkLocation &RL = *Remark.Loc;EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");EXPECT_EQ(RL.SourceLine, 3U);EXPECT_EQ(RL.SourceColumn, 12U);EXPECT_TRUE(Remark.Hotness);EXPECT_EQ(*Remark.Hotness, 4U);EXPECT_EQ(Remark.Args.size(), 4U);unsigned ArgID = 0;for (const remarks::Argument &Arg : Remark.Args) {switch (ArgID) {case 0:EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");EXPECT_EQ(checkStr(Arg.Val, 3), "bar");EXPECT_FALSE(Arg.Loc);break;case 1:EXPECT_EQ(checkStr(Arg.Key, 6), "String");EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");EXPECT_FALSE(Arg.Loc);break;case 2: {EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");EXPECT_EQ(checkStr(Arg.Val, 3), "foo");EXPECT_TRUE(Arg.Loc);const remarks::RemarkLocation &RL = *Arg.Loc;EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");EXPECT_EQ(RL.SourceLine, 2U);EXPECT_EQ(RL.SourceColumn, 0U);break;}case 3:EXPECT_EQ(checkStr(Arg.Key, 6), "String");EXPECT_EQ(checkStr(Arg.Val, 38)," because its definition is unavailable");EXPECT_FALSE(Arg.Loc);break;default:break;}++ArgID;}MaybeRemark = Parser.next();Error E = MaybeRemark.takeError();EXPECT_TRUE(E.isA<remarks::EndOfFileError>());EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.}TEST(YAMLRemarks, ParsingBadStringTableIndex) {StringRef Buf = "\n""--- !Missed\n""Pass: 50\n""\n";StringRef StrTabBuf = StringRef("inline");remarks::ParsedStringTable StrTab(StrTabBuf);Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParser(remarks::Format::YAMLStrTab, Buf,std::move(StrTab));EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();EXPECT_FALSE(MaybeRemark); // Expect an error here.std::string ErrorStr;raw_string_ostream Stream(ErrorStr);handleAllErrors(MaybeRemark.takeError(),[&](const ErrorInfoBase &EIB) { EIB.log(Stream); });EXPECT_TRUE(StringRef(Stream.str()).contains("String with index 50 is out of bounds (size = 1)."));}TEST(YAMLRemarks, ParsingGoodMeta) {// No metadata should also work.parseGoodMeta("--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n");// No string table.parseGoodMeta(StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\0\0\0\0\0\0\0\0""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n",82));// Use the string table from the metadata.parseGoodMeta(StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\x02\0\0\0\0\0\0\0""a\0""--- !Missed\n""Pass: 0\n""Name: 0\n""Function: 0\n",66));}TEST(YAMLRemarks, ParsingBadMeta) {parseExpectErrorMeta(StringRef("REMARKSS", 9),"Expecting \\0 after magic number.", CmpType::Equal);parseExpectErrorMeta(StringRef("REMARKS\0", 8), "Expecting version number.",CmpType::Equal);parseExpectErrorMeta(StringRef("REMARKS\0""\x09\0\0\0\0\0\0\0",16),"Mismatching remark version. Got 9, expected 0.",CmpType::Equal);parseExpectErrorMeta(StringRef("REMARKS\0""\0\0\0\0\0\0\0\0",16),"Expecting string table size.", CmpType::Equal);parseExpectErrorMeta(StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\x01\0\0\0\0\0\0\0",24),"Expecting string table.", CmpType::Equal);parseExpectErrorMeta(StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\0\0\0\0\0\0\0\0""/path/",30),"'/path/'", CmpType::Contains);parseExpectErrorMeta(StringRef("REMARKS\0""\0\0\0\0\0\0\0\0""\0\0\0\0\0\0\0\0""/path/",30),"'/baddir/path/'", CmpType::Contains,StringRef("/baddir/"));}
//===- unittest/Support/RemarksStrTabParsingTest.cpp - StrTab tests -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Remarks/Remark.h"#include "llvm/Remarks/RemarkParser.h"#include "gtest/gtest.h"using namespace llvm;TEST(RemarksStrTab, ParsingEmpty) {StringRef Empty("", 0);remarks::ParsedStringTable StrTab(Empty);Expected<StringRef> Nothing = StrTab[0];EXPECT_FALSE(static_cast<bool>(Nothing));EXPECT_EQ(toString(Nothing.takeError()),"String with index 0 is out of bounds (size = 0).");}TEST(RemarksStrTab, ParsingGood) {StringRef Strings("str1\0str2\0str3\0str4", 20);remarks::ParsedStringTable StrTab(Strings);Expected<StringRef> Result = StrTab[0];EXPECT_TRUE(static_cast<bool>(Result));EXPECT_EQ(*Result, "str1");Result = StrTab[1];EXPECT_TRUE(static_cast<bool>(Result));EXPECT_EQ(*Result, "str2");Result = StrTab[2];EXPECT_TRUE(static_cast<bool>(Result));EXPECT_EQ(*Result, "str3");Result = StrTab[3];EXPECT_TRUE(static_cast<bool>(Result));EXPECT_EQ(*Result, "str4");}
//===- unittest/Support/RemarksLinkingTest.cpp - Linking tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Bitcode/BitcodeAnalyzer.h"#include "llvm/Remarks/RemarkLinker.h"#include "llvm/Remarks/RemarkSerializer.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <string>using namespace llvm;static void serializeAndCheck(remarks::RemarkLinker &RL,remarks::Format OutputFormat,StringRef ExpectedOutput) {// 1. Create a serializer.// 2. Serialize all the remarks from the linker.// 3. Check that it matches the output.std::string Buf;raw_string_ostream OS(Buf);Error E = RL.serialize(OS, OutputFormat);EXPECT_FALSE(static_cast<bool>(E));// For bitstream, run it through the analyzer.if (OutputFormat == remarks::Format::Bitstream) {std::string AnalyzeBuf;raw_string_ostream AnalyzeOS(AnalyzeBuf);BCDumpOptions O(AnalyzeOS);O.ShowBinaryBlobs = true;BitcodeAnalyzer BA(OS.str());EXPECT_FALSE(BA.analyze(O)); // Expect no errors.EXPECT_EQ(AnalyzeOS.str(), ExpectedOutput);} else {EXPECT_EQ(OS.str(), ExpectedOutput);}}static void check(remarks::Format InputFormat, StringRef Input,remarks::Format OutputFormat, StringRef ExpectedOutput) {remarks::RemarkLinker RL;EXPECT_FALSE(RL.link(Input, InputFormat));serializeAndCheck(RL, OutputFormat, ExpectedOutput);}static void check(remarks::Format InputFormat, StringRef Input,remarks::Format InputFormat2, StringRef Input2,remarks::Format OutputFormat, StringRef ExpectedOutput) {remarks::RemarkLinker RL;EXPECT_FALSE(RL.link(Input, InputFormat));EXPECT_FALSE(RL.link(Input2, InputFormat2));serializeAndCheck(RL, OutputFormat, ExpectedOutput);}TEST(Remarks, LinkingGoodYAML) {// One YAML remark.check(remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n",remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n");// Check that we don't keep remarks without debug locations.check(remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""...\n",remarks::Format::YAML, "");// Check that we deduplicate remarks.check(remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n",remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n");}TEST(Remarks, LinkingGoodBitstream) {// One YAML remark.check(remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n",remarks::Format::Bitstream,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n"" <String table codeid=3 abbrevid=6/> blob data = ""'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n""</Meta>\n""<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n""</Remark>\n");// Check that we deduplicate remarks.check(remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n",remarks::Format::Bitstream,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n"" <String table codeid=3 abbrevid=6/> blob data = ""'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n""</Meta>\n""<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n""</Remark>\n");}TEST(Remarks, LinkingGoodStrTab) {// Check that remarks from different entries use the same strtab.check(remarks::Format::YAML,"--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n",remarks::Format::YAML,"--- !Passed\n""Pass: inline\n""Name: Ok\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""...\n",remarks::Format::YAMLStrTab,StringRef("REMARKS\0\0\0\0\0\0\0\0\0\x22\0\0\0\0\0\0\0""inline\0NoDefinition\0foo\0file.c\0Ok\0""--- !Passed\n""Pass: 0\n""Name: 4\n""DebugLoc: { File: 3, Line: 3, Column: 12 }\n""Function: 2\n""...\n""--- !Missed\n""Pass: 0\n""Name: 1\n""DebugLoc: { File: 3, Line: 3, Column: 12 }\n""Function: 2\n""...\n",304));}// Check that we propagate parsing errors.TEST(Remarks, LinkingError) {remarks::RemarkLinker RL;{Error E = RL.link("badyaml", remarks::Format::YAML);EXPECT_TRUE(static_cast<bool>(E));EXPECT_EQ(toString(std::move(E)),"YAML:1:1: error: document root is not of mapping type.\n""\n""badyaml\n""^~~~~~~\n""\n");}{// Check that the prepend path is propagated and fails with the full path.RL.setExternalFilePrependPath("/baddir/");Error E = RL.link(StringRef("REMARKS\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0badfile.opt.yaml",40),remarks::Format::YAMLStrTab);EXPECT_TRUE(static_cast<bool>(E));std::string ErrorMessage = toString(std::move(E));EXPECT_EQ(StringRef(ErrorMessage).lower(),StringRef("'/baddir/badfile.opt.yaml': No such file or directory").lower());}}
//===- unittest/Support/RemarksAPITest.cpp - C++ API tests ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Remarks/Remark.h"#include "llvm/Remarks/RemarkStringTable.h"#include "gtest/gtest.h"using namespace llvm;TEST(RemarksAPI, Comparison) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};// Check that == works.EXPECT_EQ(R, R);// Check that != works.remarks::Remark R2 = R.clone();R2.FunctionName = "func0";EXPECT_NE(R, R2);// Check that we iterate through all the arguments.remarks::Remark R3 = R.clone();R3.Args.back().Val = "not";EXPECT_NE(R, R3);}TEST(RemarksAPI, Clone) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Hotness = 5;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};// Check that clone works.remarks::Remark R2 = R.clone();EXPECT_EQ(R, R2);}TEST(RemarksAPI, ArgsAsMsg) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "can not do this ";R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "because of that.";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};EXPECT_EQ(R.getArgsAsMsg(), "can not do this because of that.");}TEST(RemarksAPI, StringTableInternalize) {remarks::StringTable StrTab;// Empty table.EXPECT_EQ(StrTab.SerializedSize, 0UL);remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "name";R.FunctionName = "func";R.Loc = remarks::RemarkLocation{"path", 3, 4};R.Args.emplace_back();R.Args.back().Key = "keydebug";R.Args.back().Val = "valuedebug";R.Args.back().Loc = remarks::RemarkLocation{"argpath", 6, 7};// Check that internalize starts using the strings from the string table.remarks::Remark R2 = R.clone();StrTab.internalize(R2);// Check that the pointers in the remarks are different.EXPECT_NE(R.PassName.data(), R2.PassName.data());EXPECT_NE(R.RemarkName.data(), R2.RemarkName.data());EXPECT_NE(R.FunctionName.data(), R2.FunctionName.data());EXPECT_NE(R.Loc->SourceFilePath.data(), R2.Loc->SourceFilePath.data());EXPECT_NE(R.Args.back().Key.data(), R2.Args.back().Key.data());EXPECT_NE(R.Args.back().Val.data(), R2.Args.back().Val.data());EXPECT_NE(R.Args.back().Loc->SourceFilePath.data(),R2.Args.back().Loc->SourceFilePath.data());// Check that the internalized remark is using the pointers from the string table.EXPECT_EQ(StrTab.add(R.PassName).second.data(), R2.PassName.data());EXPECT_EQ(StrTab.add(R.RemarkName).second.data(), R2.RemarkName.data());EXPECT_EQ(StrTab.add(R.FunctionName).second.data(), R2.FunctionName.data());EXPECT_EQ(StrTab.add(R.Loc->SourceFilePath).second.data(),R2.Loc->SourceFilePath.data());EXPECT_EQ(StrTab.add(R.Args.back().Key).second.data(),R2.Args.back().Key.data());EXPECT_EQ(StrTab.add(R.Args.back().Val).second.data(),R2.Args.back().Val.data());EXPECT_EQ(StrTab.add(R.Args.back().Loc->SourceFilePath).second.data(),R2.Args.back().Loc->SourceFilePath.data());}
set(LLVM_LINK_COMPONENTSBitReaderRemarksSupport)add_llvm_unittest(RemarksTestsBitstreamRemarksFormatTest.cppBitstreamRemarksParsingTest.cppBitstreamRemarksSerializerTest.cppRemarksAPITest.cppRemarksLinkingTest.cppRemarksStrTabParsingTest.cppYAMLRemarksParsingTest.cppYAMLRemarksSerializerTest.cpp)
//===- unittest/Support/BitstreamRemarksSerializerTest.cpp ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Bitcode/BitcodeAnalyzer.h"#include "llvm/Remarks/BitstreamRemarkSerializer.h"#include "llvm/Remarks/Remark.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <string>// We need to supprt Windows paths as well. In order to have paths with the same// length, use a different path according to the platform.#ifdef _WIN32#define EXTERNALFILETESTPATH "C:/externalfi"#else#define EXTERNALFILETESTPATH "/externalfile"#endifusing namespace llvm;static void checkAnalyze(StringRef Input, StringRef Expected) {std::string OutputBuf;raw_string_ostream OutputOS(OutputBuf);BCDumpOptions O(OutputOS);O.ShowBinaryBlobs = true;BitcodeAnalyzer BA(Input);EXPECT_FALSE(BA.analyze(O)); // Expect no errors.EXPECT_EQ(OutputOS.str(), Expected);}static void check(remarks::SerializerMode Mode, const remarks::Remark &R,StringRef ExpectedR, Optional<StringRef> ExpectedMeta,Optional<remarks::StringTable> StrTab) {// Emit the remark.std::string InputBuf;raw_string_ostream InputOS(InputBuf);Expected<std::unique_ptr<remarks::RemarkSerializer>> MaybeSerializer = [&] {if (StrTab)return createRemarkSerializer(remarks::Format::Bitstream, Mode, InputOS,std::move(*StrTab));elsereturn createRemarkSerializer(remarks::Format::Bitstream, Mode, InputOS);}();EXPECT_FALSE(errorToBool(MaybeSerializer.takeError()));std::unique_ptr<remarks::RemarkSerializer> Serializer =std::move(*MaybeSerializer);Serializer->emit(R);// Analyze the serialized remark.checkAnalyze(InputOS.str(), ExpectedR);// Analyze the serialized metadata if it's not in standalone mode.if (ExpectedMeta) {std::string MetaBuf;raw_string_ostream MetaOS(MetaBuf);std::unique_ptr<remarks::MetaSerializer> MetaSerializer =Serializer->metaSerializer(MetaOS, StringRef(EXTERNALFILETESTPATH));MetaSerializer->emit();checkAnalyze(MetaOS.str(), *ExpectedMeta);}}static void check(const remarks::Remark &R, StringRef ExpectedR,StringRef ExpectedMeta,Optional<remarks::StringTable> StrTab = None) {return check(remarks::SerializerMode::Separate, R, ExpectedR, ExpectedMeta,std::move(StrTab));}static void checkStandalone(const remarks::Remark &R, StringRef ExpectedR,Optional<remarks::StringTable> StrTab = None) {return check(remarks::SerializerMode::Standalone, R, ExpectedR,/*ExpectedMeta=*/None, std::move(StrTab));}TEST(BitstreamRemarkSerializer, SeparateRemarkFileNoOptionals) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";check(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n""</Meta>\n""<Remark BlockID=9 NumWords=1 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n""</Remark>\n","<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=14 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"" <String table codeid=3 abbrevid=5/> blob data = ""'remark\\x00pass\\x00function\\x00'\n"" <External File codeid=4 abbrevid=6/> blob data = ""'" EXTERNALFILETESTPATH"'\n""</Meta>\n");}TEST(BitstreamRemarkSerializer, SeparateRemarkFileNoOptionalsSeparateStrTab) {remarks::StringTable StrTab;StrTab.add("function");StrTab.add("pass");StrTab.add("remark");remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";check(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n""</Meta>\n""<Remark BlockID=9 NumWords=1 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=2 op2=1 op3=0/>\n""</Remark>\n","<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=14 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"" <String table codeid=3 abbrevid=5/> blob data = ""'function\\x00pass\\x00remark\\x00'\n"" <External File codeid=4 abbrevid=6/> blob data = ""'" EXTERNALFILETESTPATH"'\n""</Meta>\n",std::move(StrTab));}TEST(BitstreamRemarkSerializer, SeparateRemarkFileDebugLoc) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";R.Loc.emplace();R.Loc->SourceFilePath = "path";R.Loc->SourceLine = 99;R.Loc->SourceColumn = 55;check(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n""</Meta>\n""<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>\n""</Remark>\n","<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=15 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"" <String table codeid=3 abbrevid=5/> blob data = ""'remark\\x00pass\\x00function\\x00path\\x00'\n"" <External File codeid=4 abbrevid=6/> blob data = ""'" EXTERNALFILETESTPATH"'\n""</Meta>\n");}TEST(BitstreamRemarkSerializer, SeparateRemarkFileHotness) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";R.Hotness.emplace(999999999);check(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n""</Meta>\n""<Remark BlockID=9 NumWords=3 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"" <Remark hotness codeid=7 abbrevid=6 op0=999999999/>\n""</Remark>\n","<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=14 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"" <String table codeid=3 abbrevid=5/> blob data = ""'remark\\x00pass\\x00function\\x00'\n"" <External File codeid=4 abbrevid=6/> blob data = ""'" EXTERNALFILETESTPATH"'\n""</Meta>\n");}TEST(BitstreamRemarkSerializer, SeparateRemarkFileArgNoDebugLoc) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";check(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n""</Meta>\n""<Remark BlockID=9 NumWords=2 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"" <Argument codeid=9 abbrevid=8 op0=3 op1=4/>\n""</Remark>\n","<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=16 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"" <String table codeid=3 abbrevid=5/> blob data = ""'remark\\x00pass\\x00function\\x00key\\x00value\\x00'\n"" <External File codeid=4 abbrevid=6/> blob data = ""'" EXTERNALFILETESTPATH"'\n""</Meta>\n");}TEST(BitstreamRemarkSerializer, SeparateRemarkFileArgDebugLoc) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.back().Loc.emplace();R.Args.back().Loc->SourceFilePath = "path";R.Args.back().Loc->SourceLine = 99;R.Args.back().Loc->SourceColumn = 55;check(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n""</Meta>\n""<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"" <Argument with debug location codeid=8 abbrevid=7 op0=3 op1=4 op2=5 ""op3=99 op4=55/>\n""</Remark>\n","<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=17 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"" <String table codeid=3 abbrevid=5/> blob data = ""'remark\\x00pass\\x00function\\x00key\\x00value\\x00path\\x00'\n"" <External File codeid=4 abbrevid=6/> blob data = ""'" EXTERNALFILETESTPATH"'\n""</Meta>\n");}TEST(BitstreamRemarkSerializer, SeparateRemarkFileAll) {remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";R.Loc.emplace();R.Loc->SourceFilePath = "path";R.Loc->SourceLine = 99;R.Loc->SourceColumn = 55;R.Hotness.emplace(999999999);R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.back().Loc.emplace();R.Args.back().Loc->SourceFilePath = "argpath";R.Args.back().Loc->SourceLine = 11;R.Args.back().Loc->SourceColumn = 66;check(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=3 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=1/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n""</Meta>\n""<Remark BlockID=9 NumWords=8 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=0 op2=1 op3=2/>\n"" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>\n"" <Remark hotness codeid=7 abbrevid=6 op0=999999999/>\n"" <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 ""op3=11 op4=66/>\n""</Remark>\n","<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=19 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=0/>\n"" <String table codeid=3 abbrevid=5/> blob data = ""'remark\\x00pass\\x00function\\x00path\\x00key\\x00value\\x00argpa""th\\x00'\n <External File codeid=4 abbrevid=6/> blob data = ""'" EXTERNALFILETESTPATH"'\n""</Meta>\n");}TEST(BitstreamRemarkSerializer, Standalone) {// Pre-populate the string table.remarks::StringTable StrTab;StrTab.add("pass");StrTab.add("remark");StrTab.add("function");StrTab.add("path");StrTab.add("key");StrTab.add("value");StrTab.add("argpath");remarks::Remark R;R.RemarkType = remarks::Type::Missed;R.PassName = "pass";R.RemarkName = "remark";R.FunctionName = "function";R.Loc.emplace();R.Loc->SourceFilePath = "path";R.Loc->SourceLine = 99;R.Loc->SourceColumn = 55;R.Hotness.emplace(999999999);R.Args.emplace_back();R.Args.back().Key = "key";R.Args.back().Val = "value";R.Args.back().Loc.emplace();R.Args.back().Loc->SourceFilePath = "argpath";R.Args.back().Loc->SourceLine = 11;R.Args.back().Loc->SourceColumn = 66;checkStandalone(R,"<BLOCKINFO_BLOCK/>\n""<Meta BlockID=8 NumWords=15 BlockCodeSize=3>\n"" <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"" <Remark version codeid=2 abbrevid=5 op0=0/>\n"" <String table codeid=3 abbrevid=6/> blob data = ""'pass\\x00remark\\x00function\\x00path\\x00key\\x00value\\x00argpath\\x0""0'\n""</Meta>\n""<Remark BlockID=9 NumWords=8 BlockCodeSize=4>\n"" <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=99 op2=55/>\n"" <Remark hotness codeid=7 abbrevid=6 op0=999999999/>\n"" <Argument with debug location codeid=8 abbrevid=7 op0=4 op1=5 op2=6 ""op3=11 op4=66/>\n""</Remark>\n",std::move(StrTab));}
//===- unittests/Support/BitstreamRemarksParsingTest.cpp - Parsing tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm-c/Remarks.h"#include "llvm/Remarks/Remark.h"#include "llvm/Remarks/RemarkParser.h"#include "llvm/Remarks/RemarkSerializer.h"#include "gtest/gtest.h"using namespace llvm;template <size_t N> void parseGood(const char (&Buf)[N]) {// 1. Parse the YAML remark -> FromYAMLRemark// 2. Serialize it to bitstream -> BSStream// 3. Parse it back -> FromBSRemark// 4. Compare the remark objects//// This testing methodology has the drawback of relying on both the YAML// remark parser and the bitstream remark serializer. It does simplify// testing a lot, since working directly with bitstream is not that easy.// 1.Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);std::unique_ptr<remarks::Remark> FromYAMLRemark = nullptr;remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.EXPECT_TRUE(*Remark != nullptr); // At least one remark.// Keep the previous remark around.FromYAMLRemark = std::move(*Remark);Remark = Parser.next();Error E = Remark.takeError();EXPECT_TRUE(E.isA<remarks::EndOfFileError>());EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.// 2.remarks::StringTable BSStrTab;BSStrTab.internalize(*FromYAMLRemark);std::string BSBuf;raw_string_ostream BSStream(BSBuf);Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =remarks::createRemarkSerializer(remarks::Format::Bitstream,remarks::SerializerMode::Standalone,BSStream, std::move(BSStrTab));EXPECT_FALSE(errorToBool(BSSerializer.takeError()));(*BSSerializer)->emit(*FromYAMLRemark);// 3.Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =remarks::createRemarkParser(remarks::Format::Bitstream, BSStream.str());EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));EXPECT_TRUE(*MaybeBSParser != nullptr);std::unique_ptr<remarks::Remark> FromBSRemark = nullptr;remarks::RemarkParser &BSParser = **MaybeBSParser;Expected<std::unique_ptr<remarks::Remark>> BSRemark = BSParser.next();EXPECT_FALSE(errorToBool(BSRemark.takeError())); // Check for parsing errors.EXPECT_TRUE(*BSRemark != nullptr); // At least one remark.// Keep the previous remark around.FromBSRemark = std::move(*BSRemark);BSRemark = BSParser.next();Error BSE = BSRemark.takeError();EXPECT_TRUE(BSE.isA<remarks::EndOfFileError>());EXPECT_TRUE(errorToBool(std::move(BSE))); // Check for parsing errors.EXPECT_EQ(*FromYAMLRemark, *FromBSRemark);}TEST(BitstreamRemarks, ParsingGood) {parseGood("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""");// No debug loc should also pass.parseGood("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""Function: foo\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""");// No args is also ok.parseGood("\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""");}// Mandatory common part of a remark.#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"// Test all the types.TEST(BitstreamRemarks, ParsingTypes) {// Type: PassedparseGood("--- !Passed" COMMON_REMARK);// Type: MissedparseGood("--- !Missed" COMMON_REMARK);// Type: AnalysisparseGood("--- !Analysis" COMMON_REMARK);// Type: AnalysisFPCommuteparseGood("--- !AnalysisFPCommute" COMMON_REMARK);// Type: AnalysisAliasingparseGood("--- !AnalysisAliasing" COMMON_REMARK);// Type: FailureparseGood("--- !Failure" COMMON_REMARK);}#undef COMMON_REMARKstatic inline StringRef checkStr(StringRef Str, unsigned ExpectedLen) {const char *StrData = Str.data();unsigned StrLen = Str.size();EXPECT_EQ(StrLen, ExpectedLen);return StringRef(StrData, StrLen);}TEST(BitstreamRemarks, Contents) {StringRef Buf = "\n""--- !Missed\n""Pass: inline\n""Name: NoDefinition\n""DebugLoc: { File: file.c, Line: 3, Column: 12 }\n""Function: foo\n""Hotness: 4\n""Args:\n"" - Callee: bar\n"" - String: ' will not be inlined into '\n"" - Caller: foo\n"" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"" - String: ' because its definition is unavailable'\n""\n";Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =remarks::createRemarkParser(remarks::Format::YAML, Buf);EXPECT_FALSE(errorToBool(MaybeParser.takeError()));EXPECT_TRUE(*MaybeParser != nullptr);remarks::RemarkParser &Parser = **MaybeParser;Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();EXPECT_FALSE(errorToBool(MaybeRemark.takeError())); // Check for parsing errors.EXPECT_TRUE(*MaybeRemark != nullptr); // At least one remark.const remarks::Remark &Remark = **MaybeRemark;EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");EXPECT_TRUE(Remark.Loc);const remarks::RemarkLocation &RL = *Remark.Loc;EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");EXPECT_EQ(RL.SourceLine, 3U);EXPECT_EQ(RL.SourceColumn, 12U);EXPECT_TRUE(Remark.Hotness);EXPECT_EQ(*Remark.Hotness, 4U);EXPECT_EQ(Remark.Args.size(), 4U);unsigned ArgID = 0;for (const remarks::Argument &Arg : Remark.Args) {switch (ArgID) {case 0:EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");EXPECT_EQ(checkStr(Arg.Val, 3), "bar");EXPECT_FALSE(Arg.Loc);break;case 1:EXPECT_EQ(checkStr(Arg.Key, 6), "String");EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");EXPECT_FALSE(Arg.Loc);break;case 2: {EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");EXPECT_EQ(checkStr(Arg.Val, 3), "foo");EXPECT_TRUE(Arg.Loc);const remarks::RemarkLocation &RL = *Arg.Loc;EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");EXPECT_EQ(RL.SourceLine, 2U);EXPECT_EQ(RL.SourceColumn, 0U);break;}case 3:EXPECT_EQ(checkStr(Arg.Key, 6), "String");EXPECT_EQ(checkStr(Arg.Val, 38)," because its definition is unavailable");EXPECT_FALSE(Arg.Loc);break;default:break;}++ArgID;}MaybeRemark = Parser.next();Error E = MaybeRemark.takeError();EXPECT_TRUE(E.isA<remarks::EndOfFileError>());EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.}static inline StringRef checkStr(LLVMRemarkStringRef Str,unsigned ExpectedLen) {const char *StrData = LLVMRemarkStringGetData(Str);unsigned StrLen = LLVMRemarkStringGetLen(Str);EXPECT_EQ(StrLen, ExpectedLen);return StringRef(StrData, StrLen);}TEST(BitstreamRemarks, ContentsCAPI) {remarks::StringTable BSStrTab;remarks::Remark ToSerializeRemark;ToSerializeRemark.RemarkType = remarks::Type::Missed;ToSerializeRemark.PassName = "inline";ToSerializeRemark.RemarkName = "NoDefinition";ToSerializeRemark.FunctionName = "foo";ToSerializeRemark.Loc = remarks::RemarkLocation{"file.c", 3, 12};ToSerializeRemark.Hotness = 0;ToSerializeRemark.Args.emplace_back();ToSerializeRemark.Args.back().Key = "Callee";ToSerializeRemark.Args.back().Val = "bar";ToSerializeRemark.Args.emplace_back();ToSerializeRemark.Args.back().Key = "String";ToSerializeRemark.Args.back().Val = " will not be inlined into ";ToSerializeRemark.Args.emplace_back();ToSerializeRemark.Args.back().Key = "Caller";ToSerializeRemark.Args.back().Val = "foo";ToSerializeRemark.Args.back().Loc = remarks::RemarkLocation{"file.c", 2, 0};ToSerializeRemark.Args.emplace_back();ToSerializeRemark.Args.back().Key = "String";ToSerializeRemark.Args.back().Val = " because its definition is unavailable";BSStrTab.internalize(ToSerializeRemark);std::string BSBuf;raw_string_ostream BSStream(BSBuf);Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =remarks::createRemarkSerializer(remarks::Format::Bitstream,remarks::SerializerMode::Standalone,BSStream, std::move(BSStrTab));EXPECT_FALSE(errorToBool(BSSerializer.takeError()));(*BSSerializer)->emit(ToSerializeRemark);StringRef Buf = BSStream.str();LLVMRemarkParserRef Parser =LLVMRemarkParserCreateBitstream(Buf.data(), Buf.size());LLVMRemarkEntryRef Remark = LLVMRemarkParserGetNext(Parser);EXPECT_FALSE(Remark == nullptr);EXPECT_EQ(LLVMRemarkEntryGetType(Remark), LLVMRemarkTypeMissed);EXPECT_EQ(checkStr(LLVMRemarkEntryGetPassName(Remark), 6), "inline");EXPECT_EQ(checkStr(LLVMRemarkEntryGetRemarkName(Remark), 12), "NoDefinition");EXPECT_EQ(checkStr(LLVMRemarkEntryGetFunctionName(Remark), 3), "foo");LLVMRemarkDebugLocRef DL = LLVMRemarkEntryGetDebugLoc(Remark);EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 3U);EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 12U);EXPECT_EQ(LLVMRemarkEntryGetHotness(Remark), 0U);EXPECT_EQ(LLVMRemarkEntryGetNumArgs(Remark), 4U);unsigned ArgID = 0;LLVMRemarkArgRef Arg = LLVMRemarkEntryGetFirstArg(Remark);do {switch (ArgID) {case 0:EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Callee");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "bar");EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);break;case 1:EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 26)," will not be inlined into ");EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);break;case 2: {EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Caller");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "foo");LLVMRemarkDebugLocRef DL = LLVMRemarkArgGetDebugLoc(Arg);EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 2U);EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 0U);break;}case 3:EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 38)," because its definition is unavailable");EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);break;default:break;}++ArgID;} while ((Arg = LLVMRemarkEntryGetNextArg(Arg, Remark)));LLVMRemarkEntryDispose(Remark);EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr);EXPECT_FALSE(LLVMRemarkParserHasError(Parser));LLVMRemarkParserDispose(Parser);}static void parseBad(StringRef Input, const char *ErrorMsg) {Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =remarks::createRemarkParser(remarks::Format::Bitstream, Input);EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));EXPECT_TRUE(*MaybeBSParser != nullptr);remarks::RemarkParser &BSParser = **MaybeBSParser;Expected<std::unique_ptr<remarks::Remark>> BSRemark = BSParser.next();EXPECT_EQ(ErrorMsg, toString(BSRemark.takeError())); // Expect an error.}TEST(BitstreamRemarks, ParsingEmpty) {parseBad(StringRef(), "End of file reached.");}TEST(BitstreamRemarks, ParsingBadMagic) {parseBad("KRMR", "Unknown magic number: expecting RMRK, got KRMR.");}// Testing malformed bitstream is not easy. We would need to replace bytes in// the stream to create malformed and unknown records and blocks. There is no// textual format for bitstream that can be decoded, modified and encoded// back.// FIXME: Add tests for the following error messages:// * Error while parsing META_BLOCK: malformed record entry// (RECORD_META_CONTAINER_INFO).// * Error while parsing META_BLOCK: malformed record entry// (RECORD_META_REMARK_VERSION).// * Error while parsing META_BLOCK: malformed record entry// (RECORD_META_STRTAB).// * Error while parsing META_BLOCK: malformed record entry// (RECORD_META_EXTERNAL_FILE).// * Error while parsing META_BLOCK: unknown record entry (NUM).// * Error while parsing REMARK_BLOCK: malformed record entry// (RECORD_REMARK_HEADER).// * Error while parsing REMARK_BLOCK: malformed record entry// (RECORD_REMARK_DEBUG_LOC).// * Error while parsing REMARK_BLOCK: malformed record entry// (RECORD_REMARK_HOTNESS).// * Error while parsing REMARK_BLOCK: malformed record entry// (RECORD_REMARK_ARG_WITH_DEBUGLOC).// * Error while parsing REMARK_BLOCK: malformed record entry// (RECORD_REMARK_ARG_WITHOUT_DEBUGLOC).// * Error while parsing REMARK_BLOCK: unknown record entry (NUM).// * Error while parsing META_BLOCK: expecting [ENTER_SUBBLOCO, META_BLOCK,// ...].// * Error while entering META_BLOCK.// * Error while parsing META_BLOCK: expecting records.// * Error while parsing META_BLOCK: unterminated block.// * Error while parsing REMARK_BLOCK: expecting [ENTER_SUBBLOCO, REMARK_BLOCK,// ...].// * Error while entering REMARK_BLOCK.// * Error while parsing REMARK_BLOCK: expecting records.// * Error while parsing REMARK_BLOCK: unterminated block.// * Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK,// BLOCKINFO_BLOCK, ...].// * Error while parsing BLOCKINFO_BLOCK.// * Unexpected error while parsing bitstream.// * Expecting META_BLOCK after the BLOCKINFO_BLOCK.// * Error while parsing BLOCK_META: missing container version.// * Error while parsing BLOCK_META: invalid container type.// * Error while parsing BLOCK_META: missing container type.// * Error while parsing BLOCK_META: missing string table.// * Error while parsing BLOCK_META: missing remark version.// * Error while parsing BLOCK_META: missing external file path.// * Error while parsing external file's BLOCK_META: wrong container type.// * Error while parsing external file's BLOCK_META: mismatching versions:// original meta: NUM, external file meta: NUM.// * Error while parsing BLOCK_REMARK: missing string table.// * Error while parsing BLOCK_REMARK: missing remark type.// * Error while parsing BLOCK_REMARK: unknown remark type.// * Error while parsing BLOCK_REMARK: missing remark name.// * Error while parsing BLOCK_REMARK: missing remark pass.// * Error while parsing BLOCK_REMARK: missing remark function name.// * Error while parsing BLOCK_REMARK: missing key in remark argument.// * Error while parsing BLOCK_REMARK: missing value in remark argument.
//===- unittest/Support/BitstreamRemarksFormatTest.cpp - BitCodes tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Remarks/BitstreamRemarkContainer.h"#include "gtest/gtest.h"using namespace llvm;// The goal for this test is to observe test failures and carefully update the// constants when they change.// This should not change over time.TEST(BitstreamRemarksFormat, Magic) {EXPECT_EQ(remarks::ContainerMagic, "RMRK");}// This should be updated whenever any of the tests below are modified.TEST(BitstreamRemarksFormat, ContainerVersion) {EXPECT_EQ(remarks::CurrentContainerVersion, 0UL);}// The values of the current blocks should not change over time.// When adding new blocks, make sure to append them to the enum.TEST(BitstreamRemarksFormat, BlockIDs) {EXPECT_EQ(remarks::META_BLOCK_ID, 8);EXPECT_EQ(remarks::REMARK_BLOCK_ID, 9);}// The values of the current records should not change over time.// When adding new records, make sure to append them to the enum.TEST(BitstreamRemarksFormat, RecordIDs) {EXPECT_EQ(remarks::RECORD_FIRST, 1);EXPECT_EQ(remarks::RECORD_META_CONTAINER_INFO, 1);EXPECT_EQ(remarks::RECORD_META_REMARK_VERSION, 2);EXPECT_EQ(remarks::RECORD_META_STRTAB, 3);EXPECT_EQ(remarks::RECORD_META_EXTERNAL_FILE, 4);EXPECT_EQ(remarks::RECORD_REMARK_HEADER, 5);EXPECT_EQ(remarks::RECORD_REMARK_DEBUG_LOC, 6);EXPECT_EQ(remarks::RECORD_REMARK_HOTNESS, 7);EXPECT_EQ(remarks::RECORD_REMARK_ARG_WITH_DEBUGLOC, 8);EXPECT_EQ(remarks::RECORD_REMARK_ARG_WITHOUT_DEBUGLOC, 9);EXPECT_EQ(remarks::RECORD_LAST, 9);}
//===- unittest/ProfileData/SampleProfTest.cpp ------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ProfileData/SampleProf.h"#include "llvm/ADT/StringMap.h"#include "llvm/ADT/StringRef.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Metadata.h"#include "llvm/IR/Module.h"#include "llvm/ProfileData/SampleProfReader.h"#include "llvm/ProfileData/SampleProfWriter.h"#include "llvm/Support/Casting.h"#include "llvm/Support/ErrorOr.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"#include <string>#include <vector>using namespace llvm;using namespace sampleprof;using llvm::unittest::TempFile;static ::testing::AssertionResult NoError(std::error_code EC) {if (!EC)return ::testing::AssertionSuccess();return ::testing::AssertionFailure() << "error " << EC.value() << ": "<< EC.message();}namespace {struct SampleProfTest : ::testing::Test {LLVMContext Context;std::unique_ptr<SampleProfileWriter> Writer;std::unique_ptr<SampleProfileReader> Reader;SampleProfTest() : Writer(), Reader() {}void createWriter(SampleProfileFormat Format, StringRef Profile) {std::error_code EC;std::unique_ptr<raw_ostream> OS(new raw_fd_ostream(Profile, EC, sys::fs::OF_None));auto WriterOrErr = SampleProfileWriter::create(OS, Format);ASSERT_TRUE(NoError(WriterOrErr.getError()));Writer = std::move(WriterOrErr.get());}void readProfile(const Module &M, StringRef Profile,StringRef RemapFile = "") {auto ReaderOrErr = SampleProfileReader::create(std::string(Profile), Context, FSDiscriminatorPass::Base,std::string(RemapFile));ASSERT_TRUE(NoError(ReaderOrErr.getError()));Reader = std::move(ReaderOrErr.get());Reader->setModule(&M);}TempFile createRemapFile() {return TempFile("remapfile", "", R"(# Types 'int' and 'long' are equivalenttype i l# Function names 'foo' and 'faux' are equivalentname 3foo 4faux)",/*Unique*/ true);}// Verify profile summary is consistent in the roundtrip to and from// Metadata. \p AddPartialField is to choose whether the Metadata// contains the IsPartialProfile field which is optional.void verifyProfileSummary(ProfileSummary &Summary, Module &M,const bool AddPartialField,const bool AddPartialProfileRatioField) {LLVMContext &Context = M.getContext();const bool IsPartialProfile = Summary.isPartialProfile();const double PartialProfileRatio = Summary.getPartialProfileRatio();auto VerifySummary = [IsPartialProfile, PartialProfileRatio](ProfileSummary &Summary) mutable {ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());ASSERT_EQ(138211u, Summary.getTotalCount());ASSERT_EQ(10u, Summary.getNumCounts());ASSERT_EQ(4u, Summary.getNumFunctions());ASSERT_EQ(1437u, Summary.getMaxFunctionCount());ASSERT_EQ(60351u, Summary.getMaxCount());ASSERT_EQ(IsPartialProfile, Summary.isPartialProfile());ASSERT_EQ(PartialProfileRatio, Summary.getPartialProfileRatio());uint32_t Cutoff = 800000;auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {return PE.Cutoff == Cutoff;};const std::vector<ProfileSummaryEntry> &Details =Summary.getDetailedSummary();auto EightyPerc = find_if(Details, Predicate);Cutoff = 900000;auto NinetyPerc = find_if(Details, Predicate);Cutoff = 950000;auto NinetyFivePerc = find_if(Details, Predicate);Cutoff = 990000;auto NinetyNinePerc = find_if(Details, Predicate);ASSERT_EQ(60000u, EightyPerc->MinCount);ASSERT_EQ(12557u, NinetyPerc->MinCount);ASSERT_EQ(12557u, NinetyFivePerc->MinCount);ASSERT_EQ(600u, NinetyNinePerc->MinCount);};VerifySummary(Summary);// Test that conversion of summary to and from Metadata works.Metadata *MD =Summary.getMD(Context, AddPartialField, AddPartialProfileRatioField);ASSERT_TRUE(MD);ProfileSummary *PS = ProfileSummary::getFromMD(MD);ASSERT_TRUE(PS);VerifySummary(*PS);delete PS;// Test that summary can be attached to and read back from module.M.eraseNamedMetadata(M.getOrInsertModuleFlagsMetadata());M.setProfileSummary(MD, ProfileSummary::PSK_Sample);MD = M.getProfileSummary(/* IsCS */ false);ASSERT_TRUE(MD);PS = ProfileSummary::getFromMD(MD);ASSERT_TRUE(PS);VerifySummary(*PS);delete PS;}void testRoundTrip(SampleProfileFormat Format, bool Remap, bool UseMD5) {TempFile ProfileFile("profile", "", "", /*Unique*/ true);createWriter(Format, ProfileFile.path());if (Format == SampleProfileFormat::SPF_Ext_Binary && UseMD5)static_cast<SampleProfileWriterExtBinary *>(Writer.get())->setUseMD5();StringRef FooName("_Z3fooi");FunctionSamples FooSamples;FooSamples.setName(FooName);FooSamples.addTotalSamples(7711);FooSamples.addHeadSamples(610);FooSamples.addBodySamples(1, 0, 610);FooSamples.addBodySamples(2, 0, 600);FooSamples.addBodySamples(4, 0, 60000);FooSamples.addBodySamples(8, 0, 60351);FooSamples.addBodySamples(10, 0, 605);// Add inline instance with name "_Z3gooi".StringRef GooName("_Z3gooi");auto &GooSamples =FooSamples.functionSamplesAt(LineLocation(7, 0))[GooName.str()];GooSamples.setName(GooName);GooSamples.addTotalSamples(502);GooSamples.addBodySamples(3, 0, 502);// Add inline instance with name "_Z3hooi".StringRef HooName("_Z3hooi");auto &HooSamples =GooSamples.functionSamplesAt(LineLocation(9, 0))[HooName.str()];HooSamples.setName(HooName);HooSamples.addTotalSamples(317);HooSamples.addBodySamples(4, 0, 317);StringRef BarName("_Z3bari");FunctionSamples BarSamples;BarSamples.setName(BarName);BarSamples.addTotalSamples(20301);BarSamples.addHeadSamples(1437);BarSamples.addBodySamples(1, 0, 1437);// Test how reader/writer handles unmangled names.StringRef MconstructName("_M_construct<char *>");StringRef StringviewName("string_view<std::allocator<char> >");BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);StringRef BazName("_Z3bazi");FunctionSamples BazSamples;BazSamples.setName(BazName);BazSamples.addTotalSamples(12557);BazSamples.addHeadSamples(1257);BazSamples.addBodySamples(1, 0, 12557);StringRef BooName("_Z3booi");FunctionSamples BooSamples;BooSamples.setName(BooName);BooSamples.addTotalSamples(1232);BooSamples.addHeadSamples(1);BooSamples.addBodySamples(1, 0, 1232);SampleProfileMap Profiles;Profiles[FooName] = std::move(FooSamples);Profiles[BarName] = std::move(BarSamples);Profiles[BazName] = std::move(BazSamples);Profiles[BooName] = std::move(BooSamples);Module M("my_module", Context);FunctionType *fn_type =FunctionType::get(Type::getVoidTy(Context), {}, false);TempFile RemapFile(createRemapFile());if (Remap) {FooName = "_Z4fauxi";BarName = "_Z3barl";GooName = "_Z3gool";HooName = "_Z3hool";}M.getOrInsertFunction(FooName, fn_type);M.getOrInsertFunction(BarName, fn_type);M.getOrInsertFunction(BooName, fn_type);ProfileSymbolList List;if (Format == SampleProfileFormat::SPF_Ext_Binary) {List.add("zoo", true);List.add("moo", true);}Writer->setProfileSymbolList(&List);std::error_code EC;EC = Writer->write(Profiles);ASSERT_TRUE(NoError(EC));Writer->getOutputStream().flush();readProfile(M, ProfileFile.path(), RemapFile.path());EC = Reader->read();ASSERT_TRUE(NoError(EC));if (Format == SampleProfileFormat::SPF_Ext_Binary) {std::unique_ptr<ProfileSymbolList> ReaderList =Reader->getProfileSymbolList();ReaderList->contains("zoo");ReaderList->contains("moo");}FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);ASSERT_TRUE(ReadFooSamples != nullptr);if (!UseMD5) {ASSERT_EQ("_Z3fooi", ReadFooSamples->getName());}ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());// Try to find a FunctionSamples with GooName at given callsites containing// inline instance for GooName. Test the correct FunctionSamples can be// found with Remapper support.const FunctionSamples *ReadGooSamples =ReadFooSamples->findFunctionSamplesAt(LineLocation(7, 0), GooName,Reader->getRemapper());ASSERT_TRUE(ReadGooSamples != nullptr);ASSERT_EQ(502u, ReadGooSamples->getTotalSamples());// Try to find a FunctionSamples with GooName at given callsites containing// no inline instance for GooName. Test no FunctionSamples will be// found with Remapper support.const FunctionSamples *ReadGooSamplesAgain =ReadFooSamples->findFunctionSamplesAt(LineLocation(9, 0), GooName,Reader->getRemapper());ASSERT_TRUE(ReadGooSamplesAgain == nullptr);// The inline instance of Hoo is inside of the inline instance of Goo.// Try to find a FunctionSamples with HooName at given callsites containing// inline instance for HooName. Test the correct FunctionSamples can be// found with Remapper support.const FunctionSamples *ReadHooSamples =ReadGooSamples->findFunctionSamplesAt(LineLocation(9, 0), HooName,Reader->getRemapper());ASSERT_TRUE(ReadHooSamples != nullptr);ASSERT_EQ(317u, ReadHooSamples->getTotalSamples());FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);ASSERT_TRUE(ReadBarSamples != nullptr);if (!UseMD5) {ASSERT_EQ("_Z3bari", ReadBarSamples->getName());}ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());ErrorOr<SampleRecord::CallTargetMap> CTMap =ReadBarSamples->findCallTargetMapAt(1, 0);ASSERT_FALSE(CTMap.getError());// Because _Z3bazi is not defined in module M, expect _Z3bazi's profile// is not loaded when the profile is ExtBinary or Compact format because// these formats support loading function profiles on demand.FunctionSamples *ReadBazSamples = Reader->getSamplesFor(BazName);if (Format == SampleProfileFormat::SPF_Ext_Binary ||Format == SampleProfileFormat::SPF_Compact_Binary) {ASSERT_TRUE(ReadBazSamples == nullptr);ASSERT_EQ(3u, Reader->getProfiles().size());} else {ASSERT_TRUE(ReadBazSamples != nullptr);ASSERT_EQ(12557u, ReadBazSamples->getTotalSamples());ASSERT_EQ(4u, Reader->getProfiles().size());}FunctionSamples *ReadBooSamples = Reader->getSamplesFor(BooName);ASSERT_TRUE(ReadBooSamples != nullptr);ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples());std::string MconstructGUID;StringRef MconstructRep =getRepInFormat(MconstructName, UseMD5, MconstructGUID);std::string StringviewGUID;StringRef StringviewRep =getRepInFormat(StringviewName, UseMD5, StringviewGUID);ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);ASSERT_EQ(437u, CTMap.get()[StringviewRep]);ProfileSummary &Summary = Reader->getSummary();Summary.setPartialProfile(true);verifyProfileSummary(Summary, M, true, false);Summary.setPartialProfile(false);verifyProfileSummary(Summary, M, true, false);verifyProfileSummary(Summary, M, false, false);Summary.setPartialProfile(true);Summary.setPartialProfileRatio(0.5);verifyProfileSummary(Summary, M, true, true);}void addFunctionSamples(SampleProfileMap *Smap, const char *Fname,uint64_t TotalSamples, uint64_t HeadSamples) {StringRef Name(Fname);FunctionSamples FcnSamples;FcnSamples.setName(Name);FcnSamples.addTotalSamples(TotalSamples);FcnSamples.addHeadSamples(HeadSamples);FcnSamples.addBodySamples(1, 0, HeadSamples);(*Smap)[Name] = FcnSamples;}SampleProfileMap setupFcnSamplesForElisionTest(StringRef Policy) {SampleProfileMap Smap;addFunctionSamples(&Smap, "foo", uint64_t(20301), uint64_t(1437));if (Policy == "" || Policy == "all")return Smap;addFunctionSamples(&Smap, "foo.bar", uint64_t(20303), uint64_t(1439));if (Policy == "selected")return Smap;addFunctionSamples(&Smap, "foo.llvm.2465", uint64_t(20305), uint64_t(1441));return Smap;}void createFunctionWithSampleProfileElisionPolicy(Module *M,const char *Fname,StringRef Policy) {FunctionType *FnType =FunctionType::get(Type::getVoidTy(Context), {}, false);auto Inserted = M->getOrInsertFunction(Fname, FnType);auto Fcn = cast<Function>(Inserted.getCallee());if (Policy != "")Fcn->addFnAttr("sample-profile-suffix-elision-policy", Policy);}void setupModuleForElisionTest(Module *M, StringRef Policy) {createFunctionWithSampleProfileElisionPolicy(M, "foo", Policy);createFunctionWithSampleProfileElisionPolicy(M, "foo.bar", Policy);createFunctionWithSampleProfileElisionPolicy(M, "foo.llvm.2465", Policy);}void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy,const StringMap<uint64_t> &Expected) {TempFile ProfileFile("profile", "", "", /*Unique*/ true);Module M("my_module", Context);setupModuleForElisionTest(&M, Policy);SampleProfileMap ProfMap = setupFcnSamplesForElisionTest(Policy);// write profilecreateWriter(Format, ProfileFile.path());std::error_code EC;EC = Writer->write(ProfMap);ASSERT_TRUE(NoError(EC));Writer->getOutputStream().flush();// read profilereadProfile(M, ProfileFile.path());EC = Reader->read();ASSERT_TRUE(NoError(EC));for (auto I = Expected.begin(); I != Expected.end(); ++I) {uint64_t Esamples = uint64_t(-1);FunctionSamples *Samples = Reader->getSamplesFor(I->getKey());if (Samples != nullptr)Esamples = Samples->getTotalSamples();ASSERT_EQ(I->getValue(), Esamples);}}};TEST_F(SampleProfTest, roundtrip_text_profile) {testRoundTrip(SampleProfileFormat::SPF_Text, false, false);}TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {testRoundTrip(SampleProfileFormat::SPF_Binary, false, false);}TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false, true);}TEST_F(SampleProfTest, roundtrip_ext_binary_profile) {testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false, false);}TEST_F(SampleProfTest, roundtrip_md5_ext_binary_profile) {testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false, true);}TEST_F(SampleProfTest, remap_text_profile) {testRoundTrip(SampleProfileFormat::SPF_Text, true, false);}TEST_F(SampleProfTest, remap_raw_binary_profile) {testRoundTrip(SampleProfileFormat::SPF_Binary, true, false);}TEST_F(SampleProfTest, remap_ext_binary_profile) {testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, true, false);}TEST_F(SampleProfTest, sample_overflow_saturation) {const uint64_t Max = std::numeric_limits<uint64_t>::max();sampleprof_error Result;FunctionSamples FooSamples;Result = FooSamples.addTotalSamples(1);ASSERT_EQ(Result, sampleprof_error::success);Result = FooSamples.addHeadSamples(1);ASSERT_EQ(Result, sampleprof_error::success);Result = FooSamples.addBodySamples(10, 0, 1);ASSERT_EQ(Result, sampleprof_error::success);Result = FooSamples.addTotalSamples(Max);ASSERT_EQ(Result, sampleprof_error::counter_overflow);ASSERT_EQ(FooSamples.getTotalSamples(), Max);Result = FooSamples.addHeadSamples(Max);ASSERT_EQ(Result, sampleprof_error::counter_overflow);ASSERT_EQ(FooSamples.getHeadSamples(), Max);Result = FooSamples.addBodySamples(10, 0, Max);ASSERT_EQ(Result, sampleprof_error::counter_overflow);ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(10, 0);ASSERT_FALSE(BodySamples.getError());ASSERT_EQ(BodySamples.get(), Max);}TEST_F(SampleProfTest, default_suffix_elision_text) {// Default suffix elision policy: strip everything after first dot.// This implies that all suffix variants will map to "foo", so// we don't expect to see any entries for them in the sample// profile.StringMap<uint64_t> Expected;Expected["foo"] = uint64_t(20301);Expected["foo.bar"] = uint64_t(-1);Expected["foo.llvm.2465"] = uint64_t(-1);testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected);}TEST_F(SampleProfTest, default_suffix_elision_compact_binary) {// Default suffix elision policy: strip everything after first dot.// This implies that all suffix variants will map to "foo", so// we don't expect to see any entries for them in the sample// profile.StringMap<uint64_t> Expected;Expected["foo"] = uint64_t(20301);Expected["foo.bar"] = uint64_t(-1);Expected["foo.llvm.2465"] = uint64_t(-1);testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "",Expected);}TEST_F(SampleProfTest, selected_suffix_elision_text) {// Profile is created and searched using the "selected"// suffix elision policy: we only strip a .XXX suffix if// it matches a pattern known to be generated by the compiler// (e.g. ".llvm.<digits>").StringMap<uint64_t> Expected;Expected["foo"] = uint64_t(20301);Expected["foo.bar"] = uint64_t(20303);Expected["foo.llvm.2465"] = uint64_t(-1);testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected);}TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) {// Profile is created and searched using the "selected"// suffix elision policy: we only strip a .XXX suffix if// it matches a pattern known to be generated by the compiler// (e.g. ".llvm.<digits>").StringMap<uint64_t> Expected;Expected["foo"] = uint64_t(20301);Expected["foo.bar"] = uint64_t(20303);Expected["foo.llvm.2465"] = uint64_t(-1);testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected",Expected);}TEST_F(SampleProfTest, none_suffix_elision_text) {// Profile is created and searched using the "none"// suffix elision policy: no stripping of suffixes at all.// Here we expect to see all variants in the profile.StringMap<uint64_t> Expected;Expected["foo"] = uint64_t(20301);Expected["foo.bar"] = uint64_t(20303);Expected["foo.llvm.2465"] = uint64_t(20305);testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);}TEST_F(SampleProfTest, none_suffix_elision_compact_binary) {// Profile is created and searched using the "none"// suffix elision policy: no stripping of suffixes at all.// Here we expect to see all variants in the profile.StringMap<uint64_t> Expected;Expected["foo"] = uint64_t(20301);Expected["foo.bar"] = uint64_t(20303);Expected["foo.llvm.2465"] = uint64_t(20305);testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none",Expected);}} // end anonymous namespace
#include "llvm/ProfileData/MemProf.h"#include "llvm/ADT/DenseMap.h"#include "llvm/ADT/MapVector.h"#include "llvm/DebugInfo/DIContext.h"#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"#include "llvm/IR/Function.h"#include "llvm/IR/Value.h"#include "llvm/Object/ObjectFile.h"#include "llvm/ProfileData/InstrProf.h"#include "llvm/ProfileData/MemProfData.inc"#include "llvm/ProfileData/RawMemProfReader.h"#include "llvm/Support/Error.h"#include "llvm/Support/MD5.h"#include "llvm/Support/raw_ostream.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <initializer_list>namespace {using ::llvm::DIGlobal;using ::llvm::DIInliningInfo;using ::llvm::DILineInfo;using ::llvm::DILineInfoSpecifier;using ::llvm::DILocal;using ::llvm::memprof::CallStackMap;using ::llvm::memprof::Frame;using ::llvm::memprof::FrameId;using ::llvm::memprof::IndexedMemProfRecord;using ::llvm::memprof::MemInfoBlock;using ::llvm::memprof::MemProfRecord;using ::llvm::memprof::MemProfSchema;using ::llvm::memprof::Meta;using ::llvm::memprof::PortableMemInfoBlock;using ::llvm::memprof::RawMemProfReader;using ::llvm::memprof::SegmentEntry;using ::llvm::object::SectionedAddress;using ::llvm::symbolize::SymbolizableModule;using ::testing::Return;class MockSymbolizer : public SymbolizableModule {public:MOCK_CONST_METHOD3(symbolizeInlinedCode,DIInliningInfo(SectionedAddress, DILineInfoSpecifier,bool));// Most of the methods in the interface are unused. We only mock the// method that we expect to be called from the memprof reader.virtual DILineInfo symbolizeCode(SectionedAddress, DILineInfoSpecifier,bool) const {llvm_unreachable("unused");}virtual DIGlobal symbolizeData(SectionedAddress) const {llvm_unreachable("unused");}virtual std::vector<DILocal> symbolizeFrame(SectionedAddress) const {llvm_unreachable("unused");}virtual bool isWin32Module() const { llvm_unreachable("unused"); }virtual uint64_t getModulePreferredBase() const {llvm_unreachable("unused");}};struct MockInfo {std::string FunctionName;uint32_t Line;uint32_t StartLine;uint32_t Column;std::string FileName = "valid/path.cc";};DIInliningInfo makeInliningInfo(std::initializer_list<MockInfo> MockFrames) {DIInliningInfo Result;for (const auto &Item : MockFrames) {DILineInfo Frame;Frame.FunctionName = Item.FunctionName;Frame.Line = Item.Line;Frame.StartLine = Item.StartLine;Frame.Column = Item.Column;Frame.FileName = Item.FileName;Result.addFrame(Frame);}return Result;}llvm::SmallVector<SegmentEntry, 4> makeSegments() {llvm::SmallVector<SegmentEntry, 4> Result;// Mimic an entry for a non position independent executable.Result.emplace_back(0x0, 0x40000, 0x0);return Result;}const DILineInfoSpecifier specifier() {return DILineInfoSpecifier(DILineInfoSpecifier::FileLineInfoKind::RawValue,DILineInfoSpecifier::FunctionNameKind::LinkageName);}MATCHER_P4(FrameContains, FunctionName, LineOffset, Column, Inline, "") {const Frame &F = arg;const uint64_t ExpectedHash = llvm::Function::getGUID(FunctionName);if (F.Function != ExpectedHash) {*result_listener << "Hash mismatch";return false;}if (F.SymbolName && F.SymbolName.value() != FunctionName) {*result_listener << "SymbolName mismatch\nWant: " << FunctionName<< "\nGot: " << F.SymbolName.value();return false;}if (F.LineOffset == LineOffset && F.Column == Column &&F.IsInlineFrame == Inline) {return true;}*result_listener << "LineOffset, Column or Inline mismatch";return false;}MemProfSchema getFullSchema() {MemProfSchema Schema;#define MIBEntryDef(NameTag, Name, Type) Schema.push_back(Meta::Name);#include "llvm/ProfileData/MIBEntryDef.inc"#undef MIBEntryDefreturn Schema;}TEST(MemProf, FillsValue) {std::unique_ptr<MockSymbolizer> Symbolizer(new MockSymbolizer());EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x1000},specifier(), false)).Times(1) // Only once since we remember invalid PCs..WillRepeatedly(Return(makeInliningInfo({{"new", 70, 57, 3, "memprof/memprof_new_delete.cpp"},})));EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},specifier(), false)).Times(1) // Only once since we cache the result for future lookups..WillRepeatedly(Return(makeInliningInfo({{"foo", 10, 5, 30},{"bar", 201, 150, 20},})));EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x3000},specifier(), false)).Times(1).WillRepeatedly(Return(makeInliningInfo({{"xyz", 10, 5, 30},{"abc", 10, 5, 30},})));CallStackMap CSM;CSM[0x1] = {0x1000, 0x2000, 0x3000};llvm::MapVector<uint64_t, MemInfoBlock> Prof;Prof[0x1].AllocCount = 1;auto Seg = makeSegments();RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM,/*KeepName=*/true);llvm::DenseMap<llvm::GlobalValue::GUID, MemProfRecord> Records;for (const auto &Pair : Reader) {Records.insert({Pair.first, Pair.second});}// Mock program psuedocode and expected memprof record contents.//// AllocSite CallSite// inline foo() { new(); } Y N// bar() { foo(); } Y Y// inline xyz() { bar(); } N Y// abc() { xyz(); } N Y// We expect 4 records. We attach alloc site data to foo and bar, i.e.// all frames bottom up until we find a non-inline frame. We attach call site// data to bar, xyz and abc.ASSERT_EQ(Records.size(), 4U);// Check the memprof record for foo.const llvm::GlobalValue::GUID FooId = IndexedMemProfRecord::getGUID("foo");ASSERT_EQ(Records.count(FooId), 1U);const MemProfRecord &Foo = Records[FooId];ASSERT_EQ(Foo.AllocSites.size(), 1U);EXPECT_EQ(Foo.AllocSites[0].Info.getAllocCount(), 1U);EXPECT_THAT(Foo.AllocSites[0].CallStack[0],FrameContains("foo", 5U, 30U, true));EXPECT_THAT(Foo.AllocSites[0].CallStack[1],FrameContains("bar", 51U, 20U, false));EXPECT_THAT(Foo.AllocSites[0].CallStack[2],FrameContains("xyz", 5U, 30U, true));EXPECT_THAT(Foo.AllocSites[0].CallStack[3],FrameContains("abc", 5U, 30U, false));EXPECT_TRUE(Foo.CallSites.empty());// Check the memprof record for bar.const llvm::GlobalValue::GUID BarId = IndexedMemProfRecord::getGUID("bar");ASSERT_EQ(Records.count(BarId), 1U);const MemProfRecord &Bar = Records[BarId];ASSERT_EQ(Bar.AllocSites.size(), 1U);EXPECT_EQ(Bar.AllocSites[0].Info.getAllocCount(), 1U);EXPECT_THAT(Bar.AllocSites[0].CallStack[0],FrameContains("foo", 5U, 30U, true));EXPECT_THAT(Bar.AllocSites[0].CallStack[1],FrameContains("bar", 51U, 20U, false));EXPECT_THAT(Bar.AllocSites[0].CallStack[2],FrameContains("xyz", 5U, 30U, true));EXPECT_THAT(Bar.AllocSites[0].CallStack[3],FrameContains("abc", 5U, 30U, false));ASSERT_EQ(Bar.CallSites.size(), 1U);ASSERT_EQ(Bar.CallSites[0].size(), 2U);EXPECT_THAT(Bar.CallSites[0][0], FrameContains("foo", 5U, 30U, true));EXPECT_THAT(Bar.CallSites[0][1], FrameContains("bar", 51U, 20U, false));// Check the memprof record for xyz.const llvm::GlobalValue::GUID XyzId = IndexedMemProfRecord::getGUID("xyz");ASSERT_EQ(Records.count(XyzId), 1U);const MemProfRecord &Xyz = Records[XyzId];ASSERT_EQ(Xyz.CallSites.size(), 1U);ASSERT_EQ(Xyz.CallSites[0].size(), 2U);// Expect the entire frame even though in practice we only need the first// entry here.EXPECT_THAT(Xyz.CallSites[0][0], FrameContains("xyz", 5U, 30U, true));EXPECT_THAT(Xyz.CallSites[0][1], FrameContains("abc", 5U, 30U, false));// Check the memprof record for abc.const llvm::GlobalValue::GUID AbcId = IndexedMemProfRecord::getGUID("abc");ASSERT_EQ(Records.count(AbcId), 1U);const MemProfRecord &Abc = Records[AbcId];EXPECT_TRUE(Abc.AllocSites.empty());ASSERT_EQ(Abc.CallSites.size(), 1U);ASSERT_EQ(Abc.CallSites[0].size(), 2U);EXPECT_THAT(Abc.CallSites[0][0], FrameContains("xyz", 5U, 30U, true));EXPECT_THAT(Abc.CallSites[0][1], FrameContains("abc", 5U, 30U, false));}TEST(MemProf, PortableWrapper) {MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,/*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,/*dealloc_cpu=*/4);const auto Schema = getFullSchema();PortableMemInfoBlock WriteBlock(Info);std::string Buffer;llvm::raw_string_ostream OS(Buffer);WriteBlock.serialize(Schema, OS);OS.flush();PortableMemInfoBlock ReadBlock(Schema, reinterpret_cast<const unsigned char *>(Buffer.data()));EXPECT_EQ(ReadBlock, WriteBlock);// Here we compare directly with the actual counts instead of MemInfoBlock// members. Since the MemInfoBlock struct is packed and the EXPECT_EQ macros// take a reference to the params, this results in unaligned accesses.EXPECT_EQ(1UL, ReadBlock.getAllocCount());EXPECT_EQ(7ULL, ReadBlock.getTotalAccessCount());EXPECT_EQ(3UL, ReadBlock.getAllocCpuId());}TEST(MemProf, RecordSerializationRoundTrip) {const MemProfSchema Schema = getFullSchema();MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,/*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,/*dealloc_cpu=*/4);llvm::SmallVector<llvm::SmallVector<FrameId>> AllocCallStacks = {{0x123, 0x345}, {0x123, 0x567}};llvm::SmallVector<llvm::SmallVector<FrameId>> CallSites = {{0x333, 0x777}};IndexedMemProfRecord Record;for (const auto &ACS : AllocCallStacks) {// Use the same info block for both allocation sites.Record.AllocSites.emplace_back(ACS, Info);}Record.CallSites.assign(CallSites);std::string Buffer;llvm::raw_string_ostream OS(Buffer);Record.serialize(Schema, OS);OS.flush();const IndexedMemProfRecord GotRecord = IndexedMemProfRecord::deserialize(Schema, reinterpret_cast<const unsigned char *>(Buffer.data()));EXPECT_EQ(Record, GotRecord);}TEST(MemProf, SymbolizationFilter) {std::unique_ptr<MockSymbolizer> Symbolizer(new MockSymbolizer());EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x1000},specifier(), false)).Times(1) // once since we don't lookup invalid PCs repeatedly..WillRepeatedly(Return(makeInliningInfo({{"malloc", 70, 57, 3, "memprof/memprof_malloc_linux.cpp"},})));EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},specifier(), false)).Times(1) // once since we don't lookup invalid PCs repeatedly..WillRepeatedly(Return(makeInliningInfo({{"new", 70, 57, 3, "memprof/memprof_new_delete.cpp"},})));EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x3000},specifier(), false)).Times(1) // once since we don't lookup invalid PCs repeatedly..WillRepeatedly(Return(makeInliningInfo({{DILineInfo::BadString, 0, 0, 0},})));EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x4000},specifier(), false)).Times(1).WillRepeatedly(Return(makeInliningInfo({{"foo", 10, 5, 30},})));CallStackMap CSM;CSM[0x1] = {0x1000, 0x2000, 0x3000, 0x4000};// This entry should be dropped since all PCs are either not// symbolizable or belong to the runtime.CSM[0x2] = {0x1000, 0x2000};llvm::MapVector<uint64_t, MemInfoBlock> Prof;Prof[0x1].AllocCount = 1;Prof[0x2].AllocCount = 1;auto Seg = makeSegments();RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM);llvm::SmallVector<MemProfRecord, 1> Records;for (const auto &KeyRecordPair : Reader) {Records.push_back(KeyRecordPair.second);}ASSERT_EQ(Records.size(), 1U);ASSERT_EQ(Records[0].AllocSites.size(), 1U);ASSERT_EQ(Records[0].AllocSites[0].CallStack.size(), 1U);EXPECT_THAT(Records[0].AllocSites[0].CallStack[0],FrameContains("foo", 5U, 30U, false));}} // namespace
//===- unittest/ProfileData/InstrProfTest.cpp -------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/ProfileData/InstrProfReader.h"#include "llvm/ProfileData/InstrProfWriter.h"#include "llvm/ProfileData/MemProf.h"#include "llvm/ProfileData/MemProfData.inc"#include "llvm/Support/Compression.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"#include <cstdarg>using namespace llvm;LLVM_NODISCARD static ::testing::AssertionResultErrorEquals(instrprof_error Expected, Error E) {instrprof_error Found;std::string FoundMsg;handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {Found = IPE.get();FoundMsg = IPE.message();});if (Expected == Found)return ::testing::AssertionSuccess();return ::testing::AssertionFailure() << "error: " << FoundMsg << "\n";}namespace {struct InstrProfTest : ::testing::Test {InstrProfWriter Writer;std::unique_ptr<IndexedInstrProfReader> Reader;void SetUp() override { Writer.setOutputSparse(false); }void readProfile(std::unique_ptr<MemoryBuffer> Profile,std::unique_ptr<MemoryBuffer> Remapping = nullptr) {auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile),std::move(Remapping));EXPECT_THAT_ERROR(ReaderOrErr.takeError(), Succeeded());Reader = std::move(ReaderOrErr.get());}};struct SparseInstrProfTest : public InstrProfTest {void SetUp() override { Writer.setOutputSparse(true); }};struct MaybeSparseInstrProfTest : public InstrProfTest,public ::testing::WithParamInterface<bool> {void SetUp() override { Writer.setOutputSparse(GetParam()); }};TEST_P(MaybeSparseInstrProfTest, write_and_read_empty_profile) {auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));ASSERT_TRUE(Reader->begin() == Reader->end());}static const auto Err = [](Error E) {consumeError(std::move(E));FAIL();};TEST_P(MaybeSparseInstrProfTest, write_and_read_one_function) {Writer.addRecord({"foo", 0x1234, {1, 2, 3, 4}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));auto I = Reader->begin(), E = Reader->end();ASSERT_TRUE(I != E);ASSERT_EQ(StringRef("foo"), I->Name);ASSERT_EQ(0x1234U, I->Hash);ASSERT_EQ(4U, I->Counts.size());ASSERT_EQ(1U, I->Counts[0]);ASSERT_EQ(2U, I->Counts[1]);ASSERT_EQ(3U, I->Counts[2]);ASSERT_EQ(4U, I->Counts[3]);ASSERT_TRUE(++I == E);}TEST_P(MaybeSparseInstrProfTest, get_instr_prof_record) {Writer.addRecord({"foo", 0x1234, {1, 2}}, Err);Writer.addRecord({"foo", 0x1235, {3, 4}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("foo", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(2U, R->Counts.size());ASSERT_EQ(1U, R->Counts[0]);ASSERT_EQ(2U, R->Counts[1]);R = Reader->getInstrProfRecord("foo", 0x1235);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(2U, R->Counts.size());ASSERT_EQ(3U, R->Counts[0]);ASSERT_EQ(4U, R->Counts[1]);R = Reader->getInstrProfRecord("foo", 0x5678);ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, R.takeError()));R = Reader->getInstrProfRecord("bar", 0x1234);ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, R.takeError()));}TEST_P(MaybeSparseInstrProfTest, get_function_counts) {Writer.addRecord({"foo", 0x1234, {1, 2}}, Err);Writer.addRecord({"foo", 0x1235, {3, 4}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));std::vector<uint64_t> Counts;EXPECT_THAT_ERROR(Reader->getFunctionCounts("foo", 0x1234, Counts),Succeeded());ASSERT_EQ(2U, Counts.size());ASSERT_EQ(1U, Counts[0]);ASSERT_EQ(2U, Counts[1]);EXPECT_THAT_ERROR(Reader->getFunctionCounts("foo", 0x1235, Counts),Succeeded());ASSERT_EQ(2U, Counts.size());ASSERT_EQ(3U, Counts[0]);ASSERT_EQ(4U, Counts[1]);Error E1 = Reader->getFunctionCounts("foo", 0x5678, Counts);ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, std::move(E1)));Error E2 = Reader->getFunctionCounts("bar", 0x1234, Counts);ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, std::move(E2)));}// Profile data is copied from general.proftextTEST_F(InstrProfTest, get_profile_summary) {Writer.addRecord({"func1", 0x1234, {97531}}, Err);Writer.addRecord({"func2", 0x1234, {0, 0}}, Err);Writer.addRecord({"func3",0x1234,{2305843009213693952, 1152921504606846976, 576460752303423488,288230376151711744, 144115188075855872, 72057594037927936}},Err);Writer.addRecord({"func4", 0x1234, {0}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));auto VerifySummary = [](ProfileSummary &IPS) mutable {ASSERT_EQ(ProfileSummary::PSK_Instr, IPS.getKind());ASSERT_EQ(2305843009213693952U, IPS.getMaxFunctionCount());ASSERT_EQ(2305843009213693952U, IPS.getMaxCount());ASSERT_EQ(10U, IPS.getNumCounts());ASSERT_EQ(4539628424389557499U, IPS.getTotalCount());const std::vector<ProfileSummaryEntry> &Details = IPS.getDetailedSummary();uint32_t Cutoff = 800000;auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {return PE.Cutoff == Cutoff;};auto EightyPerc = find_if(Details, Predicate);Cutoff = 900000;auto NinetyPerc = find_if(Details, Predicate);Cutoff = 950000;auto NinetyFivePerc = find_if(Details, Predicate);Cutoff = 990000;auto NinetyNinePerc = find_if(Details, Predicate);ASSERT_EQ(576460752303423488U, EightyPerc->MinCount);ASSERT_EQ(288230376151711744U, NinetyPerc->MinCount);ASSERT_EQ(288230376151711744U, NinetyFivePerc->MinCount);ASSERT_EQ(72057594037927936U, NinetyNinePerc->MinCount);};ProfileSummary &PS = Reader->getSummary(/* IsCS */ false);VerifySummary(PS);// Test that conversion of summary to and from Metadata works.LLVMContext Context;Metadata *MD = PS.getMD(Context);ASSERT_TRUE(MD);ProfileSummary *PSFromMD = ProfileSummary::getFromMD(MD);ASSERT_TRUE(PSFromMD);VerifySummary(*PSFromMD);delete PSFromMD;// Test that summary can be attached to and read back from module.Module M("my_module", Context);M.setProfileSummary(MD, ProfileSummary::PSK_Instr);MD = M.getProfileSummary(/* IsCS */ false);ASSERT_TRUE(MD);PSFromMD = ProfileSummary::getFromMD(MD);ASSERT_TRUE(PSFromMD);VerifySummary(*PSFromMD);delete PSFromMD;}TEST_F(InstrProfTest, test_writer_merge) {Writer.addRecord({"func1", 0x1234, {42}}, Err);InstrProfWriter Writer2;Writer2.addRecord({"func2", 0x1234, {0, 0}}, Err);Writer.mergeRecordsFromWriter(std::move(Writer2), Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("func1", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(1U, R->Counts.size());ASSERT_EQ(42U, R->Counts[0]);R = Reader->getInstrProfRecord("func2", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(2U, R->Counts.size());ASSERT_EQ(0U, R->Counts[0]);ASSERT_EQ(0U, R->Counts[1]);}using ::llvm::memprof::IndexedMemProfRecord;using ::llvm::memprof::MemInfoBlock;using FrameIdMapTy =llvm::DenseMap<::llvm::memprof::FrameId, ::llvm::memprof::Frame>;static FrameIdMapTy getFrameMapping() {FrameIdMapTy Mapping;Mapping.insert({0, {0x123, 1, 2, false}});Mapping.insert({1, {0x345, 3, 4, true}});Mapping.insert({2, {0x125, 5, 6, false}});Mapping.insert({3, {0x567, 7, 8, true}});Mapping.insert({4, {0x124, 5, 6, false}});Mapping.insert({5, {0x789, 8, 9, true}});return Mapping;}IndexedMemProfRecord makeRecord(std::initializer_list<std::initializer_list<::llvm::memprof::FrameId>>AllocFrames,std::initializer_list<std::initializer_list<::llvm::memprof::FrameId>>CallSiteFrames,const MemInfoBlock &Block = MemInfoBlock()) {llvm::memprof::IndexedMemProfRecord MR;for (const auto &Frames : AllocFrames)MR.AllocSites.emplace_back(Frames, Block);for (const auto &Frames : CallSiteFrames)MR.CallSites.push_back(Frames);return MR;}MATCHER_P(EqualsRecord, Want, "") {const memprof::MemProfRecord &Got = arg;auto PrintAndFail = [&]() {std::string Buffer;llvm::raw_string_ostream OS(Buffer);OS << "Want:\n";Want.print(OS);OS << "Got:\n";Got.print(OS);OS.flush();*result_listener << "MemProf Record differs!\n" << Buffer;return false;};if (Want.AllocSites.size() != Got.AllocSites.size())return PrintAndFail();if (Want.CallSites.size() != Got.CallSites.size())return PrintAndFail();for (size_t I = 0; I < Got.AllocSites.size(); I++) {if (Want.AllocSites[I].Info != Got.AllocSites[I].Info)return PrintAndFail();if (Want.AllocSites[I].CallStack != Got.AllocSites[I].CallStack)return PrintAndFail();}for (size_t I = 0; I < Got.CallSites.size(); I++) {if (Want.CallSites[I] != Got.CallSites[I])return PrintAndFail();}return true;}TEST_F(InstrProfTest, test_memprof) {ASSERT_THAT_ERROR(Writer.mergeProfileKind(InstrProfKind::MemProf),Succeeded());const IndexedMemProfRecord IndexedMR = makeRecord(/*AllocFrames=*/{{0, 1},{2, 3},},/*CallSiteFrames=*/{{4, 5},});const FrameIdMapTy IdToFrameMap = getFrameMapping();for (const auto &I : IdToFrameMap) {Writer.addMemProfFrame(I.first, I.getSecond(), Err);}Writer.addMemProfRecord(/*Id=*/0x9999, IndexedMR);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));auto RecordOr = Reader->getMemProfRecord(0x9999);ASSERT_THAT_ERROR(RecordOr.takeError(), Succeeded());const memprof::MemProfRecord &Record = RecordOr.get();memprof::FrameId LastUnmappedFrameId = 0;bool HasFrameMappingError = false;auto IdToFrameCallback = [&](const memprof::FrameId Id) {auto Iter = IdToFrameMap.find(Id);if (Iter == IdToFrameMap.end()) {LastUnmappedFrameId = Id;HasFrameMappingError = true;return memprof::Frame(0, 0, 0, false);}return Iter->second;};const memprof::MemProfRecord WantRecord(IndexedMR, IdToFrameCallback);ASSERT_FALSE(HasFrameMappingError)<< "could not map frame id: " << LastUnmappedFrameId;EXPECT_THAT(WantRecord, EqualsRecord(Record));}TEST_F(InstrProfTest, test_memprof_getrecord_error) {ASSERT_THAT_ERROR(Writer.mergeProfileKind(InstrProfKind::MemProf),Succeeded());const IndexedMemProfRecord IndexedMR = makeRecord(/*AllocFrames=*/{{0, 1},{2, 3},},/*CallSiteFrames=*/{{4, 5},});// We skip adding the frame mappings here unlike the test_memprof unit test// above to exercise the failure path when getMemProfRecord is invoked.Writer.addMemProfRecord(/*Id=*/0x9999, IndexedMR);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));// Missing frames give a hash_mismatch error.auto RecordOr = Reader->getMemProfRecord(0x9999);ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, RecordOr.takeError()));// Missing functions give a unknown_function error.RecordOr = Reader->getMemProfRecord(0x1111);ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, RecordOr.takeError()));}TEST_F(InstrProfTest, test_memprof_merge) {Writer.addRecord({"func1", 0x1234, {42}}, Err);InstrProfWriter Writer2;ASSERT_THAT_ERROR(Writer2.mergeProfileKind(InstrProfKind::MemProf),Succeeded());const IndexedMemProfRecord IndexedMR = makeRecord(/*AllocFrames=*/{{0, 1},{2, 3},},/*CallSiteFrames=*/{{4, 5},});const FrameIdMapTy IdToFrameMap = getFrameMapping();for (const auto &I : IdToFrameMap) {Writer.addMemProfFrame(I.first, I.getSecond(), Err);}Writer2.addMemProfRecord(/*Id=*/0x9999, IndexedMR);ASSERT_THAT_ERROR(Writer.mergeProfileKind(Writer2.getProfileKind()),Succeeded());Writer.mergeRecordsFromWriter(std::move(Writer2), Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("func1", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(1U, R->Counts.size());ASSERT_EQ(42U, R->Counts[0]);auto RecordOr = Reader->getMemProfRecord(0x9999);ASSERT_THAT_ERROR(RecordOr.takeError(), Succeeded());const memprof::MemProfRecord &Record = RecordOr.get();memprof::FrameId LastUnmappedFrameId = 0;bool HasFrameMappingError = false;auto IdToFrameCallback = [&](const memprof::FrameId Id) {auto Iter = IdToFrameMap.find(Id);if (Iter == IdToFrameMap.end()) {LastUnmappedFrameId = Id;HasFrameMappingError = true;return memprof::Frame(0, 0, 0, false);}return Iter->second;};const memprof::MemProfRecord WantRecord(IndexedMR, IdToFrameCallback);ASSERT_FALSE(HasFrameMappingError)<< "could not map frame id: " << LastUnmappedFrameId;EXPECT_THAT(WantRecord, EqualsRecord(Record));}static const char callee1[] = "callee1";static const char callee2[] = "callee2";static const char callee3[] = "callee3";static const char callee4[] = "callee4";static const char callee5[] = "callee5";static const char callee6[] = "callee6";TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write) {NamedInstrProfRecord Record1("caller", 0x1234, {1, 2});// 4 value sites.Record1.reserveSites(IPVK_IndirectCallTarget, 4);InstrProfValueData VD0[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}};Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr);// No value profile data at the second site.Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}};Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr);InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}};Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr);Writer.addRecord(std::move(Record1), Err);Writer.addRecord({"callee1", 0x1235, {3, 4}}, Err);Writer.addRecord({"callee2", 0x1235, {3, 4}}, Err);Writer.addRecord({"callee3", 0x1235, {3, 4}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget));ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));uint64_t TotalC;std::unique_ptr<InstrProfValueData[]> VD =R->getValueForSite(IPVK_IndirectCallTarget, 0, &TotalC);ASSERT_EQ(3U, VD[0].Count);ASSERT_EQ(2U, VD[1].Count);ASSERT_EQ(1U, VD[2].Count);ASSERT_EQ(6U, TotalC);ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3"));ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2"));ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));}TEST_P(MaybeSparseInstrProfTest, annotate_vp_data) {NamedInstrProfRecord Record("caller", 0x1234, {1, 2});Record.reserveSites(IPVK_IndirectCallTarget, 1);InstrProfValueData VD0[] = {{1000, 1}, {2000, 2}, {3000, 3}, {5000, 5},{4000, 4}, {6000, 6}};Record.addValueData(IPVK_IndirectCallTarget, 0, VD0, 6, nullptr);Writer.addRecord(std::move(Record), Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());LLVMContext Ctx;std::unique_ptr<Module> M(new Module("MyModule", Ctx));FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),/*isVarArg=*/false);Function *F =Function::Create(FTy, Function::ExternalLinkage, "caller", M.get());BasicBlock *BB = BasicBlock::Create(Ctx, "", F);IRBuilder<> Builder(BB);BasicBlock *TBB = BasicBlock::Create(Ctx, "", F);BasicBlock *FBB = BasicBlock::Create(Ctx, "", F);// Use branch instruction to annotate with value profile data for simplicityInstruction *Inst = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB);Instruction *Inst2 = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB);annotateValueSite(*M, *Inst, R.get(), IPVK_IndirectCallTarget, 0);InstrProfValueData ValueData[5];uint32_t N;uint64_t T;bool Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5,ValueData, N, T);ASSERT_TRUE(Res);ASSERT_EQ(3U, N);ASSERT_EQ(21U, T);// The result should be sorted already:ASSERT_EQ(6000U, ValueData[0].Value);ASSERT_EQ(6U, ValueData[0].Count);ASSERT_EQ(5000U, ValueData[1].Value);ASSERT_EQ(5U, ValueData[1].Count);ASSERT_EQ(4000U, ValueData[2].Value);ASSERT_EQ(4U, ValueData[2].Count);Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 1, ValueData,N, T);ASSERT_TRUE(Res);ASSERT_EQ(1U, N);ASSERT_EQ(21U, T);Res = getValueProfDataFromInst(*Inst2, IPVK_IndirectCallTarget, 5, ValueData,N, T);ASSERT_FALSE(Res);// Remove the MD_prof metadataInst->setMetadata(LLVMContext::MD_prof, 0);// Annotate 5 records this time.annotateValueSite(*M, *Inst, R.get(), IPVK_IndirectCallTarget, 0, 5);Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5,ValueData, N, T);ASSERT_TRUE(Res);ASSERT_EQ(5U, N);ASSERT_EQ(21U, T);ASSERT_EQ(6000U, ValueData[0].Value);ASSERT_EQ(6U, ValueData[0].Count);ASSERT_EQ(5000U, ValueData[1].Value);ASSERT_EQ(5U, ValueData[1].Count);ASSERT_EQ(4000U, ValueData[2].Value);ASSERT_EQ(4U, ValueData[2].Count);ASSERT_EQ(3000U, ValueData[3].Value);ASSERT_EQ(3U, ValueData[3].Count);ASSERT_EQ(2000U, ValueData[4].Value);ASSERT_EQ(2U, ValueData[4].Count);// Remove the MD_prof metadataInst->setMetadata(LLVMContext::MD_prof, 0);// Annotate with 4 records.InstrProfValueData VD0Sorted[] = {{1000, 6}, {2000, 5}, {3000, 4}, {4000, 3},{5000, 2}, {6000, 1}};annotateValueSite(*M, *Inst, makeArrayRef(VD0Sorted).slice(2), 10,IPVK_IndirectCallTarget, 5);Res = getValueProfDataFromInst(*Inst, IPVK_IndirectCallTarget, 5,ValueData, N, T);ASSERT_TRUE(Res);ASSERT_EQ(4U, N);ASSERT_EQ(10U, T);ASSERT_EQ(3000U, ValueData[0].Value);ASSERT_EQ(4U, ValueData[0].Count);ASSERT_EQ(4000U, ValueData[1].Value);ASSERT_EQ(3U, ValueData[1].Count);ASSERT_EQ(5000U, ValueData[2].Value);ASSERT_EQ(2U, ValueData[2].Count);ASSERT_EQ(6000U, ValueData[3].Value);ASSERT_EQ(1U, ValueData[3].Count);}TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_with_weight) {NamedInstrProfRecord Record1("caller", 0x1234, {1, 2});// 4 value sites.Record1.reserveSites(IPVK_IndirectCallTarget, 4);InstrProfValueData VD0[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}};Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr);// No value profile data at the second site.Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}};Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr);InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}};Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr);Writer.addRecord(std::move(Record1), 10, Err);Writer.addRecord({"callee1", 0x1235, {3, 4}}, Err);Writer.addRecord({"callee2", 0x1235, {3, 4}}, Err);Writer.addRecord({"callee3", 0x1235, {3, 4}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget));ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));uint64_t TotalC;std::unique_ptr<InstrProfValueData[]> VD =R->getValueForSite(IPVK_IndirectCallTarget, 0, &TotalC);ASSERT_EQ(30U, VD[0].Count);ASSERT_EQ(20U, VD[1].Count);ASSERT_EQ(10U, VD[2].Count);ASSERT_EQ(60U, TotalC);ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3"));ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2"));ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));}TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_big_endian) {NamedInstrProfRecord Record1("caller", 0x1234, {1, 2});// 4 value sites.Record1.reserveSites(IPVK_IndirectCallTarget, 4);InstrProfValueData VD0[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}, {(uint64_t)callee3, 3}};Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr);// No value profile data at the second site.Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);InstrProfValueData VD2[] = {{(uint64_t)callee1, 1}, {(uint64_t)callee2, 2}};Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr);InstrProfValueData VD3[] = {{(uint64_t)callee1, 1}};Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr);Writer.addRecord(std::move(Record1), Err);Writer.addRecord({"callee1", 0x1235, {3, 4}}, Err);Writer.addRecord({"callee2", 0x1235, {3, 4}}, Err);Writer.addRecord({"callee3", 0x1235, {3, 4}}, Err);// Set big endian output.Writer.setValueProfDataEndianness(support::big);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));// Set big endian input.Reader->setValueProfDataEndianness(support::big);Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(4U, R->getNumValueSites(IPVK_IndirectCallTarget));ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));ASSERT_EQ(2U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));std::unique_ptr<InstrProfValueData[]> VD =R->getValueForSite(IPVK_IndirectCallTarget, 0);ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3"));ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2"));ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));// Restore little endian default:Writer.setValueProfDataEndianness(support::little);}TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1) {static const char caller[] = "caller";NamedInstrProfRecord Record11(caller, 0x1234, {1, 2});NamedInstrProfRecord Record12(caller, 0x1234, {1, 2});// 5 value sites.Record11.reserveSites(IPVK_IndirectCallTarget, 5);InstrProfValueData VD0[] = {{uint64_t(callee1), 1},{uint64_t(callee2), 2},{uint64_t(callee3), 3},{uint64_t(callee4), 4}};Record11.addValueData(IPVK_IndirectCallTarget, 0, VD0, 4, nullptr);// No value profile data at the second site.Record11.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);InstrProfValueData VD2[] = {{uint64_t(callee1), 1}, {uint64_t(callee2), 2}, {uint64_t(callee3), 3}};Record11.addValueData(IPVK_IndirectCallTarget, 2, VD2, 3, nullptr);InstrProfValueData VD3[] = {{uint64_t(callee1), 1}};Record11.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr);InstrProfValueData VD4[] = {{uint64_t(callee1), 1},{uint64_t(callee2), 2},{uint64_t(callee3), 3}};Record11.addValueData(IPVK_IndirectCallTarget, 4, VD4, 3, nullptr);// A different record for the same caller.Record12.reserveSites(IPVK_IndirectCallTarget, 5);InstrProfValueData VD02[] = {{uint64_t(callee2), 5}, {uint64_t(callee3), 3}};Record12.addValueData(IPVK_IndirectCallTarget, 0, VD02, 2, nullptr);// No value profile data at the second site.Record12.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);InstrProfValueData VD22[] = {{uint64_t(callee2), 1}, {uint64_t(callee3), 3}, {uint64_t(callee4), 4}};Record12.addValueData(IPVK_IndirectCallTarget, 2, VD22, 3, nullptr);Record12.addValueData(IPVK_IndirectCallTarget, 3, nullptr, 0, nullptr);InstrProfValueData VD42[] = {{uint64_t(callee1), 1},{uint64_t(callee2), 2},{uint64_t(callee3), 3}};Record12.addValueData(IPVK_IndirectCallTarget, 4, VD42, 3, nullptr);Writer.addRecord(std::move(Record11), Err);// Merge profile data.Writer.addRecord(std::move(Record12), Err);Writer.addRecord({callee1, 0x1235, {3, 4}}, Err);Writer.addRecord({callee2, 0x1235, {3, 4}}, Err);Writer.addRecord({callee3, 0x1235, {3, 4}}, Err);Writer.addRecord({callee3, 0x1235, {3, 4}}, Err);Writer.addRecord({callee4, 0x1235, {3, 5}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());ASSERT_EQ(5U, R->getNumValueSites(IPVK_IndirectCallTarget));ASSERT_EQ(4U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));ASSERT_EQ(0U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 1));ASSERT_EQ(4U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 2));ASSERT_EQ(1U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 3));ASSERT_EQ(3U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 4));std::unique_ptr<InstrProfValueData[]> VD =R->getValueForSite(IPVK_IndirectCallTarget, 0);ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee2"));ASSERT_EQ(7U, VD[0].Count);ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee3"));ASSERT_EQ(6U, VD[1].Count);ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee4"));ASSERT_EQ(4U, VD[2].Count);ASSERT_EQ(StringRef((const char *)VD[3].Value, 7), StringRef("callee1"));ASSERT_EQ(1U, VD[3].Count);std::unique_ptr<InstrProfValueData[]> VD_2(R->getValueForSite(IPVK_IndirectCallTarget, 2));ASSERT_EQ(StringRef((const char *)VD_2[0].Value, 7), StringRef("callee3"));ASSERT_EQ(6U, VD_2[0].Count);ASSERT_EQ(StringRef((const char *)VD_2[1].Value, 7), StringRef("callee4"));ASSERT_EQ(4U, VD_2[1].Count);ASSERT_EQ(StringRef((const char *)VD_2[2].Value, 7), StringRef("callee2"));ASSERT_EQ(3U, VD_2[2].Count);ASSERT_EQ(StringRef((const char *)VD_2[3].Value, 7), StringRef("callee1"));ASSERT_EQ(1U, VD_2[3].Count);std::unique_ptr<InstrProfValueData[]> VD_3(R->getValueForSite(IPVK_IndirectCallTarget, 3));ASSERT_EQ(StringRef((const char *)VD_3[0].Value, 7), StringRef("callee1"));ASSERT_EQ(1U, VD_3[0].Count);std::unique_ptr<InstrProfValueData[]> VD_4(R->getValueForSite(IPVK_IndirectCallTarget, 4));ASSERT_EQ(StringRef((const char *)VD_4[0].Value, 7), StringRef("callee3"));ASSERT_EQ(6U, VD_4[0].Count);ASSERT_EQ(StringRef((const char *)VD_4[1].Value, 7), StringRef("callee2"));ASSERT_EQ(4U, VD_4[1].Count);ASSERT_EQ(StringRef((const char *)VD_4[2].Value, 7), StringRef("callee1"));ASSERT_EQ(2U, VD_4[2].Count);}TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1_saturation) {static const char bar[] = "bar";const uint64_t Max = std::numeric_limits<uint64_t>::max();instrprof_error Result;auto Err = [&](Error E) { Result = InstrProfError::take(std::move(E)); };Result = instrprof_error::success;Writer.addRecord({"foo", 0x1234, {1}}, Err);ASSERT_EQ(Result, instrprof_error::success);// Verify counter overflow.Result = instrprof_error::success;Writer.addRecord({"foo", 0x1234, {Max}}, Err);ASSERT_EQ(Result, instrprof_error::counter_overflow);Result = instrprof_error::success;Writer.addRecord({bar, 0x9012, {8}}, Err);ASSERT_EQ(Result, instrprof_error::success);NamedInstrProfRecord Record4("baz", 0x5678, {3, 4});Record4.reserveSites(IPVK_IndirectCallTarget, 1);InstrProfValueData VD4[] = {{uint64_t(bar), 1}};Record4.addValueData(IPVK_IndirectCallTarget, 0, VD4, 1, nullptr);Result = instrprof_error::success;Writer.addRecord(std::move(Record4), Err);ASSERT_EQ(Result, instrprof_error::success);// Verify value data counter overflow.NamedInstrProfRecord Record5("baz", 0x5678, {5, 6});Record5.reserveSites(IPVK_IndirectCallTarget, 1);InstrProfValueData VD5[] = {{uint64_t(bar), Max}};Record5.addValueData(IPVK_IndirectCallTarget, 0, VD5, 1, nullptr);Result = instrprof_error::success;Writer.addRecord(std::move(Record5), Err);ASSERT_EQ(Result, instrprof_error::counter_overflow);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));// Verify saturation of counts.Expected<InstrProfRecord> ReadRecord1 =Reader->getInstrProfRecord("foo", 0x1234);EXPECT_THAT_ERROR(ReadRecord1.takeError(), Succeeded());ASSERT_EQ(Max, ReadRecord1->Counts[0]);Expected<InstrProfRecord> ReadRecord2 =Reader->getInstrProfRecord("baz", 0x5678);ASSERT_TRUE(bool(ReadRecord2));ASSERT_EQ(1U, ReadRecord2->getNumValueSites(IPVK_IndirectCallTarget));std::unique_ptr<InstrProfValueData[]> VD =ReadRecord2->getValueForSite(IPVK_IndirectCallTarget, 0);ASSERT_EQ(StringRef("bar"), StringRef((const char *)VD[0].Value, 3));ASSERT_EQ(Max, VD[0].Count);}// This test tests that when there are too many values// for a given site, the merged results are properly// truncated.TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge_site_trunc) {static const char caller[] = "caller";NamedInstrProfRecord Record11(caller, 0x1234, {1, 2});NamedInstrProfRecord Record12(caller, 0x1234, {1, 2});// 2 value sites.Record11.reserveSites(IPVK_IndirectCallTarget, 2);InstrProfValueData VD0[255];for (int I = 0; I < 255; I++) {VD0[I].Value = 2 * I;VD0[I].Count = 2 * I + 1000;}Record11.addValueData(IPVK_IndirectCallTarget, 0, VD0, 255, nullptr);Record11.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);Record12.reserveSites(IPVK_IndirectCallTarget, 2);InstrProfValueData VD1[255];for (int I = 0; I < 255; I++) {VD1[I].Value = 2 * I + 1;VD1[I].Count = 2 * I + 1001;}Record12.addValueData(IPVK_IndirectCallTarget, 0, VD1, 255, nullptr);Record12.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr);Writer.addRecord(std::move(Record11), Err);// Merge profile data.Writer.addRecord(std::move(Record12), Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));Expected<InstrProfRecord> R = Reader->getInstrProfRecord("caller", 0x1234);EXPECT_THAT_ERROR(R.takeError(), Succeeded());std::unique_ptr<InstrProfValueData[]> VD(R->getValueForSite(IPVK_IndirectCallTarget, 0));ASSERT_EQ(2U, R->getNumValueSites(IPVK_IndirectCallTarget));ASSERT_EQ(255U, R->getNumValueDataForSite(IPVK_IndirectCallTarget, 0));for (unsigned I = 0; I < 255; I++) {ASSERT_EQ(VD[I].Value, 509 - I);ASSERT_EQ(VD[I].Count, 1509 - I);}}static void addValueProfData(InstrProfRecord &Record) {Record.reserveSites(IPVK_IndirectCallTarget, 5);InstrProfValueData VD0[] = {{uint64_t(callee1), 400},{uint64_t(callee2), 1000},{uint64_t(callee3), 500},{uint64_t(callee4), 300},{uint64_t(callee5), 100}};Record.addValueData(IPVK_IndirectCallTarget, 0, VD0, 5, nullptr);InstrProfValueData VD1[] = {{uint64_t(callee5), 800},{uint64_t(callee3), 1000},{uint64_t(callee2), 2500},{uint64_t(callee1), 1300}};Record.addValueData(IPVK_IndirectCallTarget, 1, VD1, 4, nullptr);InstrProfValueData VD2[] = {{uint64_t(callee6), 800},{uint64_t(callee3), 1000},{uint64_t(callee4), 5500}};Record.addValueData(IPVK_IndirectCallTarget, 2, VD2, 3, nullptr);InstrProfValueData VD3[] = {{uint64_t(callee2), 1800},{uint64_t(callee3), 2000}};Record.addValueData(IPVK_IndirectCallTarget, 3, VD3, 2, nullptr);Record.addValueData(IPVK_IndirectCallTarget, 4, nullptr, 0, nullptr);}TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write) {InstrProfRecord SrcRecord({1ULL << 31, 2});addValueProfData(SrcRecord);std::unique_ptr<ValueProfData> VPData =ValueProfData::serializeFrom(SrcRecord);InstrProfRecord Record({1ULL << 31, 2});VPData->deserializeTo(Record, nullptr);// Now read data from Record and sanity check the dataASSERT_EQ(5U, Record.getNumValueSites(IPVK_IndirectCallTarget));ASSERT_EQ(5U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 0));ASSERT_EQ(4U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 1));ASSERT_EQ(3U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 2));ASSERT_EQ(2U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 3));ASSERT_EQ(0U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 4));auto Cmp = [](const InstrProfValueData &VD1, const InstrProfValueData &VD2) {return VD1.Count > VD2.Count;};std::unique_ptr<InstrProfValueData[]> VD_0(Record.getValueForSite(IPVK_IndirectCallTarget, 0));llvm::sort(&VD_0[0], &VD_0[5], Cmp);ASSERT_EQ(StringRef((const char *)VD_0[0].Value, 7), StringRef("callee2"));ASSERT_EQ(1000U, VD_0[0].Count);ASSERT_EQ(StringRef((const char *)VD_0[1].Value, 7), StringRef("callee3"));ASSERT_EQ(500U, VD_0[1].Count);ASSERT_EQ(StringRef((const char *)VD_0[2].Value, 7), StringRef("callee1"));ASSERT_EQ(400U, VD_0[2].Count);ASSERT_EQ(StringRef((const char *)VD_0[3].Value, 7), StringRef("callee4"));ASSERT_EQ(300U, VD_0[3].Count);ASSERT_EQ(StringRef((const char *)VD_0[4].Value, 7), StringRef("callee5"));ASSERT_EQ(100U, VD_0[4].Count);std::unique_ptr<InstrProfValueData[]> VD_1(Record.getValueForSite(IPVK_IndirectCallTarget, 1));llvm::sort(&VD_1[0], &VD_1[4], Cmp);ASSERT_EQ(StringRef((const char *)VD_1[0].Value, 7), StringRef("callee2"));ASSERT_EQ(2500U, VD_1[0].Count);ASSERT_EQ(StringRef((const char *)VD_1[1].Value, 7), StringRef("callee1"));ASSERT_EQ(1300U, VD_1[1].Count);ASSERT_EQ(StringRef((const char *)VD_1[2].Value, 7), StringRef("callee3"));ASSERT_EQ(1000U, VD_1[2].Count);ASSERT_EQ(StringRef((const char *)VD_1[3].Value, 7), StringRef("callee5"));ASSERT_EQ(800U, VD_1[3].Count);std::unique_ptr<InstrProfValueData[]> VD_2(Record.getValueForSite(IPVK_IndirectCallTarget, 2));llvm::sort(&VD_2[0], &VD_2[3], Cmp);ASSERT_EQ(StringRef((const char *)VD_2[0].Value, 7), StringRef("callee4"));ASSERT_EQ(5500U, VD_2[0].Count);ASSERT_EQ(StringRef((const char *)VD_2[1].Value, 7), StringRef("callee3"));ASSERT_EQ(1000U, VD_2[1].Count);ASSERT_EQ(StringRef((const char *)VD_2[2].Value, 7), StringRef("callee6"));ASSERT_EQ(800U, VD_2[2].Count);std::unique_ptr<InstrProfValueData[]> VD_3(Record.getValueForSite(IPVK_IndirectCallTarget, 3));llvm::sort(&VD_3[0], &VD_3[2], Cmp);ASSERT_EQ(StringRef((const char *)VD_3[0].Value, 7), StringRef("callee3"));ASSERT_EQ(2000U, VD_3[0].Count);ASSERT_EQ(StringRef((const char *)VD_3[1].Value, 7), StringRef("callee2"));ASSERT_EQ(1800U, VD_3[1].Count);}TEST_P(MaybeSparseInstrProfTest, value_prof_data_read_write_mapping) {NamedInstrProfRecord SrcRecord("caller", 0x1234, {1ULL << 31, 2});addValueProfData(SrcRecord);std::unique_ptr<ValueProfData> VPData =ValueProfData::serializeFrom(SrcRecord);NamedInstrProfRecord Record("caller", 0x1234, {1ULL << 31, 2});InstrProfSymtab Symtab;Symtab.mapAddress(uint64_t(callee1), 0x1000ULL);Symtab.mapAddress(uint64_t(callee2), 0x2000ULL);Symtab.mapAddress(uint64_t(callee3), 0x3000ULL);Symtab.mapAddress(uint64_t(callee4), 0x4000ULL);// Missing mapping for callee5VPData->deserializeTo(Record, &Symtab);// Now read data from Record and sanity check the dataASSERT_EQ(5U, Record.getNumValueSites(IPVK_IndirectCallTarget));ASSERT_EQ(5U, Record.getNumValueDataForSite(IPVK_IndirectCallTarget, 0));auto Cmp = [](const InstrProfValueData &VD1, const InstrProfValueData &VD2) {return VD1.Count > VD2.Count;};std::unique_ptr<InstrProfValueData[]> VD_0(Record.getValueForSite(IPVK_IndirectCallTarget, 0));llvm::sort(&VD_0[0], &VD_0[5], Cmp);ASSERT_EQ(VD_0[0].Value, 0x2000ULL);ASSERT_EQ(1000U, VD_0[0].Count);ASSERT_EQ(VD_0[1].Value, 0x3000ULL);ASSERT_EQ(500U, VD_0[1].Count);ASSERT_EQ(VD_0[2].Value, 0x1000ULL);ASSERT_EQ(400U, VD_0[2].Count);// callee5 does not have a mapped value -- default to 0.ASSERT_EQ(VD_0[4].Value, 0ULL);}TEST_P(MaybeSparseInstrProfTest, get_max_function_count) {Writer.addRecord({"foo", 0x1234, {1ULL << 31, 2}}, Err);Writer.addRecord({"bar", 0, {1ULL << 63}}, Err);Writer.addRecord({"baz", 0x5678, {0, 0, 0, 0}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount(/* IsCS */ false));}TEST_P(MaybeSparseInstrProfTest, get_weighted_function_counts) {Writer.addRecord({"foo", 0x1234, {1, 2}}, 3, Err);Writer.addRecord({"foo", 0x1235, {3, 4}}, 5, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));std::vector<uint64_t> Counts;EXPECT_THAT_ERROR(Reader->getFunctionCounts("foo", 0x1234, Counts),Succeeded());ASSERT_EQ(2U, Counts.size());ASSERT_EQ(3U, Counts[0]);ASSERT_EQ(6U, Counts[1]);EXPECT_THAT_ERROR(Reader->getFunctionCounts("foo", 0x1235, Counts),Succeeded());ASSERT_EQ(2U, Counts.size());ASSERT_EQ(15U, Counts[0]);ASSERT_EQ(20U, Counts[1]);}// Testing symtab creator interface used by indexed profile reader.TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_test) {std::vector<StringRef> FuncNames;FuncNames.push_back("func1");FuncNames.push_back("func2");FuncNames.push_back("func3");FuncNames.push_back("bar1");FuncNames.push_back("bar2");FuncNames.push_back("bar3");InstrProfSymtab Symtab;EXPECT_THAT_ERROR(Symtab.create(FuncNames), Succeeded());StringRef R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("func1"));ASSERT_EQ(StringRef("func1"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("func2"));ASSERT_EQ(StringRef("func2"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("func3"));ASSERT_EQ(StringRef("func3"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar1"));ASSERT_EQ(StringRef("bar1"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar2"));ASSERT_EQ(StringRef("bar2"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar3"));ASSERT_EQ(StringRef("bar3"), R);// negative testsR = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar4"));ASSERT_EQ(StringRef(), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("foo4"));ASSERT_EQ(StringRef(), R);// Now incrementally update the symtabEXPECT_THAT_ERROR(Symtab.addFuncName("blah_1"), Succeeded());EXPECT_THAT_ERROR(Symtab.addFuncName("blah_2"), Succeeded());EXPECT_THAT_ERROR(Symtab.addFuncName("blah_3"), Succeeded());// Check againR = Symtab.getFuncName(IndexedInstrProf::ComputeHash("blah_1"));ASSERT_EQ(StringRef("blah_1"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("blah_2"));ASSERT_EQ(StringRef("blah_2"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("blah_3"));ASSERT_EQ(StringRef("blah_3"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("func1"));ASSERT_EQ(StringRef("func1"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("func2"));ASSERT_EQ(StringRef("func2"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("func3"));ASSERT_EQ(StringRef("func3"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar1"));ASSERT_EQ(StringRef("bar1"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar2"));ASSERT_EQ(StringRef("bar2"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash("bar3"));ASSERT_EQ(StringRef("bar3"), R);}// Test that we get an error when creating a bogus symtab.TEST_P(MaybeSparseInstrProfTest, instr_prof_bogus_symtab_empty_func_name) {InstrProfSymtab Symtab;EXPECT_TRUE(ErrorEquals(instrprof_error::malformed, Symtab.addFuncName("")));}// Testing symtab creator interface used by value profile transformer.TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_module_test) {LLVMContext Ctx;std::unique_ptr<Module> M = std::make_unique<Module>("MyModule.cpp", Ctx);FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),/*isVarArg=*/false);Function::Create(FTy, Function::ExternalLinkage, "Gfoo", M.get());Function::Create(FTy, Function::ExternalLinkage, "Gblah", M.get());Function::Create(FTy, Function::ExternalLinkage, "Gbar", M.get());Function::Create(FTy, Function::InternalLinkage, "Ifoo", M.get());Function::Create(FTy, Function::InternalLinkage, "Iblah", M.get());Function::Create(FTy, Function::InternalLinkage, "Ibar", M.get());Function::Create(FTy, Function::PrivateLinkage, "Pfoo", M.get());Function::Create(FTy, Function::PrivateLinkage, "Pblah", M.get());Function::Create(FTy, Function::PrivateLinkage, "Pbar", M.get());Function::Create(FTy, Function::WeakODRLinkage, "Wfoo", M.get());Function::Create(FTy, Function::WeakODRLinkage, "Wblah", M.get());Function::Create(FTy, Function::WeakODRLinkage, "Wbar", M.get());InstrProfSymtab ProfSymtab;EXPECT_THAT_ERROR(ProfSymtab.create(*M), Succeeded());StringRef Funcs[] = {"Gfoo", "Gblah", "Gbar", "Ifoo", "Iblah", "Ibar","Pfoo", "Pblah", "Pbar", "Wfoo", "Wblah", "Wbar"};for (unsigned I = 0; I < sizeof(Funcs) / sizeof(*Funcs); I++) {Function *F = M->getFunction(Funcs[I]);ASSERT_TRUE(F != nullptr);std::string PGOName = getPGOFuncName(*F);uint64_t Key = IndexedInstrProf::ComputeHash(PGOName);ASSERT_EQ(StringRef(PGOName),ProfSymtab.getFuncName(Key));ASSERT_EQ(StringRef(Funcs[I]), ProfSymtab.getOrigFuncName(Key));}}// Testing symtab serialization and creator/deserialization interface// used by coverage map reader, and raw profile reader.TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_compression_test) {std::vector<std::string> FuncNames1;std::vector<std::string> FuncNames2;for (int I = 0; I < 3; I++) {std::string str;raw_string_ostream OS(str);OS << "func_" << I;FuncNames1.push_back(OS.str());str.clear();OS << "f oooooooooooooo_" << I;FuncNames1.push_back(OS.str());str.clear();OS << "BAR_" << I;FuncNames2.push_back(OS.str());str.clear();OS << "BlahblahBlahblahBar_" << I;FuncNames2.push_back(OS.str());}for (bool DoCompression : {false, true}) {// Compressing:std::string FuncNameStrings1;EXPECT_THAT_ERROR(collectPGOFuncNameStrings(FuncNames1,(DoCompression && compression::zlib::isAvailable()),FuncNameStrings1),Succeeded());// Compressing:std::string FuncNameStrings2;EXPECT_THAT_ERROR(collectPGOFuncNameStrings(FuncNames2,(DoCompression && compression::zlib::isAvailable()),FuncNameStrings2),Succeeded());for (int Padding = 0; Padding < 2; Padding++) {// Join with paddings :std::string FuncNameStrings = FuncNameStrings1;for (int P = 0; P < Padding; P++) {FuncNameStrings.push_back('\0');}FuncNameStrings += FuncNameStrings2;// Now decompress:InstrProfSymtab Symtab;EXPECT_THAT_ERROR(Symtab.create(StringRef(FuncNameStrings)), Succeeded());// Now do the checks:// First sampling some data points:StringRef R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[0]));ASSERT_EQ(StringRef("func_0"), R);R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(FuncNames1[1]));ASSERT_EQ(StringRef("f oooooooooooooo_0"), R);for (int I = 0; I < 3; I++) {std::string N[4];N[0] = FuncNames1[2 * I];N[1] = FuncNames1[2 * I + 1];N[2] = FuncNames2[2 * I];N[3] = FuncNames2[2 * I + 1];for (int J = 0; J < 4; J++) {StringRef R = Symtab.getFuncName(IndexedInstrProf::ComputeHash(N[J]));ASSERT_EQ(StringRef(N[J]), R);}}}}}TEST_P(MaybeSparseInstrProfTest, remapping_test) {Writer.addRecord({"_Z3fooi", 0x1234, {1, 2, 3, 4}}, Err);Writer.addRecord({"file:_Z3barf", 0x567, {5, 6, 7}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile), llvm::MemoryBuffer::getMemBuffer(R"(type i lname 3bar 4quux)"));std::vector<uint64_t> Counts;for (StringRef FooName : {"_Z3fooi", "_Z3fool"}) {EXPECT_THAT_ERROR(Reader->getFunctionCounts(FooName, 0x1234, Counts),Succeeded());ASSERT_EQ(4u, Counts.size());EXPECT_EQ(1u, Counts[0]);EXPECT_EQ(2u, Counts[1]);EXPECT_EQ(3u, Counts[2]);EXPECT_EQ(4u, Counts[3]);}for (StringRef BarName : {"file:_Z3barf", "file:_Z4quuxf"}) {EXPECT_THAT_ERROR(Reader->getFunctionCounts(BarName, 0x567, Counts),Succeeded());ASSERT_EQ(3u, Counts.size());EXPECT_EQ(5u, Counts[0]);EXPECT_EQ(6u, Counts[1]);EXPECT_EQ(7u, Counts[2]);}for (StringRef BadName : {"_Z3foof", "_Z4quuxi", "_Z3barl", "", "_ZZZ","_Z3barf", "otherfile:_Z4quuxf"}) {EXPECT_THAT_ERROR(Reader->getFunctionCounts(BadName, 0x1234, Counts),Failed());EXPECT_THAT_ERROR(Reader->getFunctionCounts(BadName, 0x567, Counts),Failed());}}TEST_F(SparseInstrProfTest, preserve_no_records) {Writer.addRecord({"foo", 0x1234, {0}}, Err);Writer.addRecord({"bar", 0x4321, {0, 0}}, Err);Writer.addRecord({"baz", 0x4321, {0, 0, 0}}, Err);auto Profile = Writer.writeBuffer();readProfile(std::move(Profile));auto I = Reader->begin(), E = Reader->end();ASSERT_TRUE(I == E);}INSTANTIATE_TEST_SUITE_P(MaybeSparse, MaybeSparseInstrProfTest,::testing::Bool());#if defined(_LP64) && defined(EXPENSIVE_CHECKS)TEST(ProfileReaderTest, ReadsLargeFiles) {const size_t LargeSize = 1ULL << 32; // 4GBauto RawProfile = WritableMemoryBuffer::getNewUninitMemBuffer(LargeSize);if (!RawProfile)return;auto RawProfileReaderOrErr = InstrProfReader::create(std::move(RawProfile));ASSERT_TRUE(InstrProfError::take(RawProfileReaderOrErr.takeError()) ==instrprof_error::unrecognized_format);auto IndexedProfile = WritableMemoryBuffer::getNewUninitMemBuffer(LargeSize);if (!IndexedProfile)return;auto IndexedReaderOrErr =IndexedInstrProfReader::create(std::move(IndexedProfile), nullptr);ASSERT_TRUE(InstrProfError::take(IndexedReaderOrErr.takeError()) ==instrprof_error::bad_magic);}#endif} // end anonymous namespace
//===- unittest/ProfileData/InstProfDataTest.cpp ----------------------------=////// 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////===----------------------------------------------------------------------===//#include "gtest/gtest.h"#include <cstdint>#define INSTR_PROF_VALUE_PROF_MEMOP_API#include "llvm/ProfileData/InstrProfData.inc"namespace {TEST(InstrProfDataTest, MapValueToRangeRepValue) {EXPECT_EQ(0ULL, InstrProfGetRangeRepValue(0));EXPECT_EQ(1ULL, InstrProfGetRangeRepValue(1));EXPECT_EQ(2ULL, InstrProfGetRangeRepValue(2));EXPECT_EQ(3ULL, InstrProfGetRangeRepValue(3));EXPECT_EQ(4ULL, InstrProfGetRangeRepValue(4));EXPECT_EQ(5ULL, InstrProfGetRangeRepValue(5));EXPECT_EQ(6ULL, InstrProfGetRangeRepValue(6));EXPECT_EQ(7ULL, InstrProfGetRangeRepValue(7));EXPECT_EQ(8ULL, InstrProfGetRangeRepValue(8));EXPECT_EQ(9ULL, InstrProfGetRangeRepValue(9));EXPECT_EQ(16ULL, InstrProfGetRangeRepValue(16));EXPECT_EQ(17ULL, InstrProfGetRangeRepValue(30));EXPECT_EQ(32ULL, InstrProfGetRangeRepValue(32));EXPECT_EQ(33ULL, InstrProfGetRangeRepValue(54));EXPECT_EQ(64ULL, InstrProfGetRangeRepValue(64));EXPECT_EQ(65ULL, InstrProfGetRangeRepValue(127));EXPECT_EQ(128ULL, InstrProfGetRangeRepValue(128));EXPECT_EQ(129ULL, InstrProfGetRangeRepValue(200));EXPECT_EQ(256ULL, InstrProfGetRangeRepValue(256));EXPECT_EQ(257ULL, InstrProfGetRangeRepValue(397));EXPECT_EQ(512ULL, InstrProfGetRangeRepValue(512));EXPECT_EQ(513ULL, InstrProfGetRangeRepValue(2832048023));}TEST(InstrProfDataTest, IsInOneValueRange) {EXPECT_EQ(true, InstrProfIsSingleValRange(0));EXPECT_EQ(true, InstrProfIsSingleValRange(1));EXPECT_EQ(true, InstrProfIsSingleValRange(2));EXPECT_EQ(true, InstrProfIsSingleValRange(3));EXPECT_EQ(true, InstrProfIsSingleValRange(4));EXPECT_EQ(true, InstrProfIsSingleValRange(5));EXPECT_EQ(true, InstrProfIsSingleValRange(6));EXPECT_EQ(true, InstrProfIsSingleValRange(7));EXPECT_EQ(true, InstrProfIsSingleValRange(8));EXPECT_EQ(false, InstrProfIsSingleValRange(9));EXPECT_EQ(true, InstrProfIsSingleValRange(16));EXPECT_EQ(false, InstrProfIsSingleValRange(30));EXPECT_EQ(true, InstrProfIsSingleValRange(32));EXPECT_EQ(false, InstrProfIsSingleValRange(54));EXPECT_EQ(true, InstrProfIsSingleValRange(64));EXPECT_EQ(false, InstrProfIsSingleValRange(127));EXPECT_EQ(true, InstrProfIsSingleValRange(128));EXPECT_EQ(false, InstrProfIsSingleValRange(200));EXPECT_EQ(true, InstrProfIsSingleValRange(256));EXPECT_EQ(false, InstrProfIsSingleValRange(397));EXPECT_EQ(true, InstrProfIsSingleValRange(512));EXPECT_EQ(false, InstrProfIsSingleValRange(2832048023344));}} // end anonymous namespace
//===- unittest/ProfileData/CoverageMappingTest.cpp -------------------------=////// 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////===----------------------------------------------------------------------===//#include "llvm/ProfileData/Coverage/CoverageMapping.h"#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"#include "llvm/ProfileData/InstrProfReader.h"#include "llvm/ProfileData/InstrProfWriter.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"#include <ostream>#include <utility>using namespace llvm;using namespace coverage;LLVM_NODISCARD static ::testing::AssertionResultErrorEquals(coveragemap_error Expected, Error E) {coveragemap_error Found;std::string FoundMsg;handleAllErrors(std::move(E), [&](const CoverageMapError &CME) {Found = CME.get();FoundMsg = CME.message();});if (Expected == Found)return ::testing::AssertionSuccess();return ::testing::AssertionFailure() << "error: " << FoundMsg << "\n";}namespace llvm {namespace coverage {void PrintTo(const Counter &C, ::std::ostream *os) {if (C.isZero())*os << "Zero";else if (C.isExpression())*os << "Expression " << C.getExpressionID();else*os << "Counter " << C.getCounterID();}void PrintTo(const CoverageSegment &S, ::std::ostream *os) {*os << "CoverageSegment(" << S.Line << ", " << S.Col << ", ";if (S.HasCount)*os << S.Count << ", ";*os << (S.IsRegionEntry ? "true" : "false") << ")";}}}namespace {struct OutputFunctionCoverageData {StringRef Name;uint64_t Hash;std::vector<StringRef> Filenames;std::vector<CounterMappingRegion> Regions;std::vector<CounterExpression> Expressions;OutputFunctionCoverageData() : Hash(0) {}OutputFunctionCoverageData(OutputFunctionCoverageData &&OFCD): Name(OFCD.Name), Hash(OFCD.Hash), Filenames(std::move(OFCD.Filenames)),Regions(std::move(OFCD.Regions)) {}OutputFunctionCoverageData(const OutputFunctionCoverageData &) = delete;OutputFunctionCoverageData &operator=(const OutputFunctionCoverageData &) = delete;OutputFunctionCoverageData &operator=(OutputFunctionCoverageData &&) = delete;void fillCoverageMappingRecord(CoverageMappingRecord &Record) const {Record.FunctionName = Name;Record.FunctionHash = Hash;Record.Filenames = Filenames;Record.Expressions = Expressions;Record.MappingRegions = Regions;}};struct CoverageMappingReaderMock : CoverageMappingReader {ArrayRef<OutputFunctionCoverageData> Functions;CoverageMappingReaderMock(ArrayRef<OutputFunctionCoverageData> Functions): Functions(Functions) {}Error readNextRecord(CoverageMappingRecord &Record) override {if (Functions.empty())return make_error<CoverageMapError>(coveragemap_error::eof);Functions.front().fillCoverageMappingRecord(Record);Functions = Functions.slice(1);return Error::success();}};struct InputFunctionCoverageData {// Maps the global file index from CoverageMappingTest.Files// to the index of that file within this function. We can't just use// global file indexes here because local indexes have to be dense.// This map is used during serialization to create the virtual file mapping// (from local fileId to global Index) in the head of the per-function// coverage mapping data.SmallDenseMap<unsigned, unsigned> ReverseVirtualFileMapping;std::string Name;uint64_t Hash;std::vector<CounterMappingRegion> Regions;std::vector<CounterExpression> Expressions;InputFunctionCoverageData(std::string Name, uint64_t Hash): Name(std::move(Name)), Hash(Hash) {}InputFunctionCoverageData(InputFunctionCoverageData &&IFCD): ReverseVirtualFileMapping(std::move(IFCD.ReverseVirtualFileMapping)),Name(std::move(IFCD.Name)), Hash(IFCD.Hash),Regions(std::move(IFCD.Regions)) {}InputFunctionCoverageData(const InputFunctionCoverageData &) = delete;InputFunctionCoverageData &operator=(const InputFunctionCoverageData &) = delete;InputFunctionCoverageData &operator=(InputFunctionCoverageData &&) = delete;};struct CoverageMappingTest : ::testing::TestWithParam<std::tuple<bool, bool>> {bool UseMultipleReaders;StringMap<unsigned> Files;std::vector<std::string> Filenames;std::vector<InputFunctionCoverageData> InputFunctions;std::vector<OutputFunctionCoverageData> OutputFunctions;InstrProfWriter ProfileWriter;std::unique_ptr<IndexedInstrProfReader> ProfileReader;std::unique_ptr<CoverageMapping> LoadedCoverage;void SetUp() override {ProfileWriter.setOutputSparse(std::get<0>(GetParam()));UseMultipleReaders = std::get<1>(GetParam());}unsigned getGlobalFileIndex(StringRef Name) {auto R = Files.find(Name);if (R != Files.end())return R->second;unsigned Index = Files.size() + 1;Files.try_emplace(Name, Index);return Index;}// Return the file index of file 'Name' for the current function.// Add the file into the global map if necessary.// See also InputFunctionCoverageData::ReverseVirtualFileMapping// for additional comments.unsigned getFileIndexForFunction(StringRef Name) {unsigned GlobalIndex = getGlobalFileIndex(Name);auto &CurrentFunctionFileMapping =InputFunctions.back().ReverseVirtualFileMapping;auto R = CurrentFunctionFileMapping.find(GlobalIndex);if (R != CurrentFunctionFileMapping.end())return R->second;unsigned IndexInFunction = CurrentFunctionFileMapping.size();CurrentFunctionFileMapping.insert(std::make_pair(GlobalIndex, IndexInFunction));return IndexInFunction;}void startFunction(StringRef FuncName, uint64_t Hash) {InputFunctions.emplace_back(FuncName.str(), Hash);}void addCMR(Counter C, StringRef File, unsigned LS, unsigned CS, unsigned LE,unsigned CE, bool Skipped = false) {auto &Regions = InputFunctions.back().Regions;unsigned FileID = getFileIndexForFunction(File);Regions.push_back(Skipped ? CounterMappingRegion::makeSkipped(FileID, LS, CS, LE, CE): CounterMappingRegion::makeRegion(C, FileID, LS, CS, LE, CE));}void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS,unsigned CS, unsigned LE, unsigned CE) {InputFunctions.back().Regions.push_back(CounterMappingRegion::makeExpansion(getFileIndexForFunction(File), getFileIndexForFunction(ExpandedFile),LS, CS, LE, CE));}void addExpression(CounterExpression CE) {InputFunctions.back().Expressions.push_back(CE);}std::string writeCoverageRegions(InputFunctionCoverageData &Data) {SmallVector<unsigned, 8> FileIDs(Data.ReverseVirtualFileMapping.size());for (const auto &E : Data.ReverseVirtualFileMapping)FileIDs[E.second] = E.first;std::string Coverage;llvm::raw_string_ostream OS(Coverage);CoverageMappingWriter(FileIDs, Data.Expressions, Data.Regions).write(OS);return OS.str();}void readCoverageRegions(const std::string &Coverage,OutputFunctionCoverageData &Data) {// We will re-use the StringRef in duplicate tests, clear it to avoid// clobber previous ones.Filenames.clear();Filenames.resize(Files.size() + 1);for (const auto &E : Files)Filenames[E.getValue()] = E.getKey().str();ArrayRef<std::string> FilenameRefs = llvm::makeArrayRef(Filenames);RawCoverageMappingReader Reader(Coverage, FilenameRefs, Data.Filenames,Data.Expressions, Data.Regions);EXPECT_THAT_ERROR(Reader.read(), Succeeded());}void writeAndReadCoverageRegions(bool EmitFilenames = true) {OutputFunctions.resize(InputFunctions.size());for (unsigned I = 0; I < InputFunctions.size(); ++I) {std::string Regions = writeCoverageRegions(InputFunctions[I]);readCoverageRegions(Regions, OutputFunctions[I]);OutputFunctions[I].Name = InputFunctions[I].Name;OutputFunctions[I].Hash = InputFunctions[I].Hash;if (!EmitFilenames)OutputFunctions[I].Filenames.clear();}}void readProfCounts() {auto Profile = ProfileWriter.writeBuffer();auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile));EXPECT_THAT_ERROR(ReaderOrErr.takeError(), Succeeded());ProfileReader = std::move(ReaderOrErr.get());}Expected<std::unique_ptr<CoverageMapping>> readOutputFunctions() {std::vector<std::unique_ptr<CoverageMappingReader>> CoverageReaders;if (UseMultipleReaders) {for (const auto &OF : OutputFunctions) {ArrayRef<OutputFunctionCoverageData> Funcs(OF);CoverageReaders.push_back(std::make_unique<CoverageMappingReaderMock>(Funcs));}} else {ArrayRef<OutputFunctionCoverageData> Funcs(OutputFunctions);CoverageReaders.push_back(std::make_unique<CoverageMappingReaderMock>(Funcs));}return CoverageMapping::load(CoverageReaders, *ProfileReader);}Error loadCoverageMapping(bool EmitFilenames = true) {readProfCounts();writeAndReadCoverageRegions(EmitFilenames);auto CoverageOrErr = readOutputFunctions();if (!CoverageOrErr)return CoverageOrErr.takeError();LoadedCoverage = std::move(CoverageOrErr.get());return Error::success();}};TEST_P(CoverageMappingTest, basic_write_read) {startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1);addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2);addCMR(Counter::getZero(), "foo", 3, 1, 3, 4);addCMR(Counter::getCounter(2), "foo", 4, 1, 4, 8);addCMR(Counter::getCounter(3), "bar", 1, 2, 3, 4);writeAndReadCoverageRegions();ASSERT_EQ(1u, InputFunctions.size());ASSERT_EQ(1u, OutputFunctions.size());InputFunctionCoverageData &Input = InputFunctions.back();OutputFunctionCoverageData &Output = OutputFunctions.back();size_t N = makeArrayRef(Input.Regions).size();ASSERT_EQ(N, Output.Regions.size());for (size_t I = 0; I < N; ++I) {ASSERT_EQ(Input.Regions[I].Count, Output.Regions[I].Count);ASSERT_EQ(Input.Regions[I].FileID, Output.Regions[I].FileID);ASSERT_EQ(Input.Regions[I].startLoc(), Output.Regions[I].startLoc());ASSERT_EQ(Input.Regions[I].endLoc(), Output.Regions[I].endLoc());ASSERT_EQ(Input.Regions[I].Kind, Output.Regions[I].Kind);}}TEST_P(CoverageMappingTest, correct_deserialize_for_more_than_two_files) {const char *FileNames[] = {"bar", "baz", "foo"};static const unsigned N = array_lengthof(FileNames);startFunction("func", 0x1234);for (unsigned I = 0; I < N; ++I)// Use LineStart to hold the index of the file name// in order to preserve that information during possible sorting of CMRs.addCMR(Counter::getCounter(0), FileNames[I], I, 1, I, 1);writeAndReadCoverageRegions();ASSERT_EQ(1u, OutputFunctions.size());OutputFunctionCoverageData &Output = OutputFunctions.back();ASSERT_EQ(N, Output.Regions.size());ASSERT_EQ(N, Output.Filenames.size());for (unsigned I = 0; I < N; ++I) {ASSERT_GT(N, Output.Regions[I].FileID);ASSERT_GT(N, Output.Regions[I].LineStart);EXPECT_EQ(FileNames[Output.Regions[I].LineStart],Output.Filenames[Output.Regions[I].FileID]);}}static const auto Err = [](Error E) { FAIL(); };TEST_P(CoverageMappingTest, load_coverage_for_more_than_two_files) {ProfileWriter.addRecord({"func", 0x1234, {0}}, Err);const char *FileNames[] = {"bar", "baz", "foo"};static const unsigned N = array_lengthof(FileNames);startFunction("func", 0x1234);for (unsigned I = 0; I < N; ++I)// Use LineStart to hold the index of the file name// in order to preserve that information during possible sorting of CMRs.addCMR(Counter::getCounter(0), FileNames[I], I, 1, I, 1);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());for (unsigned I = 0; I < N; ++I) {CoverageData Data = LoadedCoverage->getCoverageForFile(FileNames[I]);ASSERT_TRUE(!Data.empty());EXPECT_EQ(I, Data.begin()->Line);}}TEST_P(CoverageMappingTest, load_coverage_with_bogus_function_name) {ProfileWriter.addRecord({"", 0x1234, {10}}, Err);startFunction("", 0x1234);addCMR(Counter::getCounter(0), "foo", 1, 1, 5, 5);EXPECT_TRUE(ErrorEquals(coveragemap_error::malformed, loadCoverageMapping()));}TEST_P(CoverageMappingTest, load_coverage_for_several_functions) {ProfileWriter.addRecord({"func1", 0x1234, {10}}, Err);ProfileWriter.addRecord({"func2", 0x2345, {20}}, Err);startFunction("func1", 0x1234);addCMR(Counter::getCounter(0), "foo", 1, 1, 5, 5);startFunction("func2", 0x2345);addCMR(Counter::getCounter(0), "bar", 2, 2, 6, 6);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();EXPECT_EQ(2, std::distance(FunctionRecords.begin(), FunctionRecords.end()));for (const auto &FunctionRecord : FunctionRecords) {CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(2U, Segments.size());if (FunctionRecord.Name == "func1") {EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);EXPECT_EQ(CoverageSegment(5, 5, false), Segments[1]);} else {ASSERT_EQ("func2", FunctionRecord.Name);EXPECT_EQ(CoverageSegment(2, 2, 20, true), Segments[0]);EXPECT_EQ(CoverageSegment(6, 6, false), Segments[1]);}}}TEST_P(CoverageMappingTest, create_combined_regions) {ProfileWriter.addRecord({"func1", 0x1234, {1, 2, 3}}, Err);startFunction("func1", 0x1234);// Given regions which start at the same location, emit a segment for the// last region.addCMR(Counter::getCounter(0), "file1", 1, 1, 2, 2);addCMR(Counter::getCounter(1), "file1", 1, 1, 2, 2);addCMR(Counter::getCounter(2), "file1", 1, 1, 2, 2);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(2U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 6, true), Segments[0]);EXPECT_EQ(CoverageSegment(2, 2, false), Segments[1]);}TEST_P(CoverageMappingTest, skipped_segments_have_no_count) {ProfileWriter.addRecord({"func1", 0x1234, {1}}, Err);startFunction("func1", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 5, 5);addCMR(Counter::getCounter(0), "file1", 5, 1, 5, 5, /*Skipped=*/true);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(3U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);EXPECT_EQ(CoverageSegment(5, 1, true), Segments[1]);EXPECT_EQ(CoverageSegment(5, 5, false), Segments[2]);}TEST_P(CoverageMappingTest, multiple_regions_end_after_parent_ends) {ProfileWriter.addRecord({"func1", 0x1234, {1, 0}}, Err);startFunction("func1", 0x1234);// 1| F{ a{// 2|// 3| a} b{ c{// 4|// 5| b}// 6|// 7| c} d{ e{// 8|// 9| d} e} F}addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); // < FaddCMR(Counter::getCounter(0), "file1", 1, 1, 3, 5); // < aaddCMR(Counter::getCounter(0), "file1", 3, 5, 5, 4); // < baddCMR(Counter::getCounter(1), "file1", 3, 5, 7, 3); // < caddCMR(Counter::getCounter(1), "file1", 7, 3, 9, 2); // < daddCMR(Counter::getCounter(1), "file1", 7, 7, 9, 7); // < eEXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());// Old output (not sorted or unique):// Segment at 1:1 with count 1// Segment at 1:1 with count 1// Segment at 3:5 with count 1// Segment at 3:5 with count 0// Segment at 3:5 with count 1// Segment at 5:4 with count 0// Segment at 7:3 with count 1// Segment at 7:3 with count 0// Segment at 7:7 with count 0// Segment at 9:7 with count 0// Segment at 9:2 with count 1// Top level segment at 9:9// New output (sorted and unique):// Segment at 1:1 (count = 1), RegionEntry// Segment at 3:5 (count = 1), RegionEntry// Segment at 5:4 (count = 0)// Segment at 7:3 (count = 0), RegionEntry// Segment at 7:7 (count = 0), RegionEntry// Segment at 9:2 (count = 0)// Segment at 9:7 (count = 1)// Segment at 9:9 (count = 0), SkippedASSERT_EQ(8U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[1]);EXPECT_EQ(CoverageSegment(5, 4, 0, false), Segments[2]);EXPECT_EQ(CoverageSegment(7, 3, 0, true), Segments[3]);EXPECT_EQ(CoverageSegment(7, 7, 0, true), Segments[4]);EXPECT_EQ(CoverageSegment(9, 2, 0, false), Segments[5]);EXPECT_EQ(CoverageSegment(9, 7, 1, false), Segments[6]);EXPECT_EQ(CoverageSegment(9, 9, false), Segments[7]);}TEST_P(CoverageMappingTest, multiple_completed_segments_at_same_loc) {ProfileWriter.addRecord({"func1", 0x1234, {0, 1, 2}}, Err);startFunction("func1", 0x1234);// PR35495addCMR(Counter::getCounter(1), "file1", 2, 1, 18, 2);addCMR(Counter::getCounter(0), "file1", 8, 10, 14, 6);addCMR(Counter::getCounter(0), "file1", 8, 12, 14, 6);addCMR(Counter::getCounter(1), "file1", 9, 1, 14, 6);addCMR(Counter::getCounter(2), "file1", 11, 13, 11, 14);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(7U, Segments.size());EXPECT_EQ(CoverageSegment(2, 1, 1, true), Segments[0]);EXPECT_EQ(CoverageSegment(8, 10, 0, true), Segments[1]);EXPECT_EQ(CoverageSegment(8, 12, 0, true), Segments[2]);EXPECT_EQ(CoverageSegment(9, 1, 1, true), Segments[3]);EXPECT_EQ(CoverageSegment(11, 13, 2, true), Segments[4]);// Use count=1 (from 9:1 -> 14:6), not count=0 (from 8:12 -> 14:6).EXPECT_EQ(CoverageSegment(11, 14, 1, false), Segments[5]);EXPECT_EQ(CoverageSegment(18, 2, false), Segments[6]);}TEST_P(CoverageMappingTest, dont_emit_redundant_segments) {ProfileWriter.addRecord({"func1", 0x1234, {1, 1}}, Err);startFunction("func1", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 4, 4);addCMR(Counter::getCounter(1), "file1", 2, 2, 5, 5);addCMR(Counter::getCounter(0), "file1", 3, 3, 6, 6);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(5U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]);EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]);EXPECT_EQ(CoverageSegment(4, 4, 1, false), Segments[3]);// A closing segment starting at 5:5 would be redundant: it would have the// same count as the segment starting at 4:4, and has all the same metadata.EXPECT_EQ(CoverageSegment(6, 6, false), Segments[4]);}TEST_P(CoverageMappingTest, dont_emit_closing_segment_at_new_region_start) {ProfileWriter.addRecord({"func1", 0x1234, {1}}, Err);startFunction("func1", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 6, 5);addCMR(Counter::getCounter(0), "file1", 2, 2, 6, 5);addCMR(Counter::getCounter(0), "file1", 3, 3, 6, 5);addCMR(Counter::getCounter(0), "file1", 6, 5, 7, 7);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(5U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]);EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]);EXPECT_EQ(CoverageSegment(6, 5, 1, true), Segments[3]);// The old segment builder would get this wrong by emitting multiple segments// which start at 6:5 (a few of which were skipped segments). We should just// get a segment for the region entry.EXPECT_EQ(CoverageSegment(7, 7, false), Segments[4]);}TEST_P(CoverageMappingTest, handle_consecutive_regions_with_zero_length) {ProfileWriter.addRecord({"func1", 0x1234, {1, 2}}, Err);startFunction("func1", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);addCMR(Counter::getCounter(1), "file1", 1, 1, 1, 1);addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);addCMR(Counter::getCounter(1), "file1", 1, 1, 1, 1);addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(1U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, true), Segments[0]);// We need to get a skipped segment starting at 1:1. In this case there is// also a region entry at 1:1.}TEST_P(CoverageMappingTest, handle_sandwiched_zero_length_region) {ProfileWriter.addRecord({"func1", 0x1234, {2, 1}}, Err);startFunction("func1", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 5, 4, 4);addCMR(Counter::getCounter(1), "file1", 1, 9, 1, 50);addCMR(Counter::getCounter(1), "file1", 2, 7, 2, 34);addCMR(Counter::getCounter(1), "file1", 3, 5, 3, 21);addCMR(Counter::getCounter(1), "file1", 3, 21, 3, 21);addCMR(Counter::getCounter(1), "file1", 4, 12, 4, 17);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(10U, Segments.size());EXPECT_EQ(CoverageSegment(1, 5, 2, true), Segments[0]);EXPECT_EQ(CoverageSegment(1, 9, 1, true), Segments[1]);EXPECT_EQ(CoverageSegment(1, 50, 2, false), Segments[2]);EXPECT_EQ(CoverageSegment(2, 7, 1, true), Segments[3]);EXPECT_EQ(CoverageSegment(2, 34, 2, false), Segments[4]);EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[5]);EXPECT_EQ(CoverageSegment(3, 21, 2, true), Segments[6]);// Handle the zero-length region by creating a segment with its predecessor's// count (i.e the count from 1:5 -> 4:4).EXPECT_EQ(CoverageSegment(4, 4, false), Segments[7]);// The area between 4:4 and 4:12 is skipped.EXPECT_EQ(CoverageSegment(4, 12, 1, true), Segments[8]);EXPECT_EQ(CoverageSegment(4, 17, false), Segments[9]);}TEST_P(CoverageMappingTest, handle_last_completed_region) {ProfileWriter.addRecord({"func1", 0x1234, {1, 2, 3, 4}}, Err);startFunction("func1", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 8, 8);addCMR(Counter::getCounter(1), "file1", 2, 2, 5, 5);addCMR(Counter::getCounter(2), "file1", 3, 3, 4, 4);addCMR(Counter::getCounter(3), "file1", 6, 6, 7, 7);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();const auto &FunctionRecord = *FunctionRecords.begin();CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(8U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);EXPECT_EQ(CoverageSegment(2, 2, 2, true), Segments[1]);EXPECT_EQ(CoverageSegment(3, 3, 3, true), Segments[2]);EXPECT_EQ(CoverageSegment(4, 4, 2, false), Segments[3]);EXPECT_EQ(CoverageSegment(5, 5, 1, false), Segments[4]);EXPECT_EQ(CoverageSegment(6, 6, 4, true), Segments[5]);EXPECT_EQ(CoverageSegment(7, 7, 1, false), Segments[6]);EXPECT_EQ(CoverageSegment(8, 8, false), Segments[7]);}TEST_P(CoverageMappingTest, expansion_gets_first_counter) {startFunction("func", 0x1234);addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2);// This starts earlier in "foo", so the expansion should get its counter.addCMR(Counter::getCounter(2), "foo", 1, 1, 20, 1);addExpansionCMR("bar", "foo", 3, 3, 3, 3);writeAndReadCoverageRegions();ASSERT_EQ(1u, OutputFunctions.size());OutputFunctionCoverageData &Output = OutputFunctions.back();ASSERT_EQ(CounterMappingRegion::ExpansionRegion, Output.Regions[2].Kind);ASSERT_EQ(Counter::getCounter(2), Output.Regions[2].Count);ASSERT_EQ(3U, Output.Regions[2].LineStart);}TEST_P(CoverageMappingTest, basic_coverage_iteration) {ProfileWriter.addRecord({"func", 0x1234, {30, 20, 10, 0}}, Err);startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);addCMR(Counter::getCounter(2), "file1", 5, 8, 9, 1);addCMR(Counter::getCounter(3), "file1", 10, 10, 11, 11);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file1");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(7U, Segments.size());ASSERT_EQ(CoverageSegment(1, 1, 20, true), Segments[0]);ASSERT_EQ(CoverageSegment(4, 7, 30, false), Segments[1]);ASSERT_EQ(CoverageSegment(5, 8, 10, true), Segments[2]);ASSERT_EQ(CoverageSegment(9, 1, 30, false), Segments[3]);ASSERT_EQ(CoverageSegment(9, 9, false), Segments[4]);ASSERT_EQ(CoverageSegment(10, 10, 0, true), Segments[5]);ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]);}TEST_P(CoverageMappingTest, test_line_coverage_iterator) {ProfileWriter.addRecord({"func", 0x1234, {30, 20, 10, 0}}, Err);startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);addCMR(Counter::getCounter(2), "file1", 5, 8, 9, 1);addCMR(Counter::getCounter(3), "file1", 10, 10, 11, 11);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file1");unsigned Line = 0;unsigned LineCounts[] = {20, 20, 20, 20, 30, 10, 10, 10, 10, 0, 0};for (const auto &LCS : getLineCoverageStats(Data)) {ASSERT_EQ(Line + 1, LCS.getLine());errs() << "Line: " << Line + 1 << ", count = " << LCS.getExecutionCount() << "\n";ASSERT_EQ(LineCounts[Line], LCS.getExecutionCount());++Line;}ASSERT_EQ(11U, Line);// Check that operator->() works / compiles.ASSERT_EQ(1U, LineCoverageIterator(Data)->getLine());}TEST_P(CoverageMappingTest, uncovered_function) {startFunction("func", 0x1234);addCMR(Counter::getZero(), "file1", 1, 2, 3, 4);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file1");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(2U, Segments.size());ASSERT_EQ(CoverageSegment(1, 2, 0, true), Segments[0]);ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]);}TEST_P(CoverageMappingTest, uncovered_function_with_mapping) {startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file1");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(3U, Segments.size());ASSERT_EQ(CoverageSegment(1, 1, 0, true), Segments[0]);ASSERT_EQ(CoverageSegment(4, 7, 0, false), Segments[1]);ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]);}TEST_P(CoverageMappingTest, combine_regions) {ProfileWriter.addRecord({"func", 0x1234, {10, 20, 30}}, Err);startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4);addCMR(Counter::getCounter(2), "file1", 3, 3, 4, 4);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file1");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(4U, Segments.size());ASSERT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);ASSERT_EQ(CoverageSegment(3, 3, 50, true), Segments[1]);ASSERT_EQ(CoverageSegment(4, 4, 10, false), Segments[2]);ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);}TEST_P(CoverageMappingTest, restore_combined_counter_after_nested_region) {ProfileWriter.addRecord({"func", 0x1234, {10, 20, 40}}, Err);startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(1), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(2), "file1", 3, 3, 5, 5);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file1");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(4U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 30, true), Segments[0]);EXPECT_EQ(CoverageSegment(3, 3, 40, true), Segments[1]);EXPECT_EQ(CoverageSegment(5, 5, 30, false), Segments[2]);EXPECT_EQ(CoverageSegment(9, 9, false), Segments[3]);}// If CodeRegions and ExpansionRegions cover the same area,// only counts of CodeRegions should be used.TEST_P(CoverageMappingTest, dont_combine_expansions) {ProfileWriter.addRecord({"func", 0x1234, {10, 20}}, Err);ProfileWriter.addRecord({"func", 0x1234, {0, 0}}, Err);startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(1), "file1", 3, 3, 4, 4);addCMR(Counter::getCounter(1), "include1", 6, 6, 7, 7);addExpansionCMR("file1", "include1", 3, 3, 4, 4);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file1");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(4U, Segments.size());ASSERT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);ASSERT_EQ(CoverageSegment(3, 3, 20, true), Segments[1]);ASSERT_EQ(CoverageSegment(4, 4, 10, false), Segments[2]);ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);}// If an area is covered only by ExpansionRegions, they should be combinated.TEST_P(CoverageMappingTest, combine_expansions) {ProfileWriter.addRecord({"func", 0x1234, {2, 3, 7}}, Err);startFunction("func", 0x1234);addCMR(Counter::getCounter(1), "include1", 1, 1, 1, 10);addCMR(Counter::getCounter(2), "include2", 1, 1, 1, 10);addCMR(Counter::getCounter(0), "file", 1, 1, 5, 5);addExpansionCMR("file", "include1", 3, 1, 3, 5);addExpansionCMR("file", "include2", 3, 1, 3, 5);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("file");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(4U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 2, true), Segments[0]);EXPECT_EQ(CoverageSegment(3, 1, 10, true), Segments[1]);EXPECT_EQ(CoverageSegment(3, 5, 2, false), Segments[2]);EXPECT_EQ(CoverageSegment(5, 5, false), Segments[3]);}// Test that counters not associated with any code regions are allowed.TEST_P(CoverageMappingTest, non_code_region_counters) {// No records in profdatastartFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file", 1, 1, 5, 5);addCMR(Counter::getExpression(0), "file", 6, 1, 6, 5);addExpression(CounterExpression(CounterExpression::Add, Counter::getCounter(1), Counter::getCounter(2)));EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());std::vector<std::string> Names;for (const auto &Func : LoadedCoverage->getCoveredFunctions()) {Names.push_back(Func.Name);ASSERT_EQ(2U, Func.CountedRegions.size());}ASSERT_EQ(1U, Names.size());}TEST_P(CoverageMappingTest, strip_filename_prefix) {ProfileWriter.addRecord({"file1:func", 0x1234, {0}}, Err);startFunction("file1:func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());std::vector<std::string> Names;for (const auto &Func : LoadedCoverage->getCoveredFunctions())Names.push_back(Func.Name);ASSERT_EQ(1U, Names.size());ASSERT_EQ("func", Names[0]);}TEST_P(CoverageMappingTest, strip_unknown_filename_prefix) {ProfileWriter.addRecord({"<unknown>:func", 0x1234, {0}}, Err);startFunction("<unknown>:func", 0x1234);addCMR(Counter::getCounter(0), "", 1, 1, 9, 9);EXPECT_THAT_ERROR(loadCoverageMapping(/*EmitFilenames=*/false), Succeeded());std::vector<std::string> Names;for (const auto &Func : LoadedCoverage->getCoveredFunctions())Names.push_back(Func.Name);ASSERT_EQ(1U, Names.size());ASSERT_EQ("func", Names[0]);}TEST_P(CoverageMappingTest, dont_detect_false_instantiations) {ProfileWriter.addRecord({"foo", 0x1234, {10}}, Err);ProfileWriter.addRecord({"bar", 0x2345, {20}}, Err);startFunction("foo", 0x1234);addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10);addExpansionCMR("main", "expanded", 4, 1, 4, 5);startFunction("bar", 0x2345);addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10);addExpansionCMR("main", "expanded", 9, 1, 9, 5);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());std::vector<InstantiationGroup> InstantiationGroups =LoadedCoverage->getInstantiationGroups("expanded");ASSERT_TRUE(InstantiationGroups.empty());}TEST_P(CoverageMappingTest, load_coverage_for_expanded_file) {ProfileWriter.addRecord({"func", 0x1234, {10}}, Err);startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "expanded", 1, 1, 1, 10);addExpansionCMR("main", "expanded", 4, 1, 4, 5);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());CoverageData Data = LoadedCoverage->getCoverageForFile("expanded");std::vector<CoverageSegment> Segments(Data.begin(), Data.end());ASSERT_EQ(2U, Segments.size());EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]);EXPECT_EQ(CoverageSegment(1, 10, false), Segments[1]);}TEST_P(CoverageMappingTest, skip_duplicate_function_record) {ProfileWriter.addRecord({"func", 0x1234, {1}}, Err);// This record should be loaded.startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);// This record should be loaded.startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(0), "file2", 1, 1, 9, 9);// This record should be skipped.startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);// This record should be loaded.startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file2", 1, 1, 9, 9);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);// This record should be skipped.startFunction("func", 0x1234);addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);addCMR(Counter::getCounter(0), "file2", 1, 1, 9, 9);EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());auto Funcs = LoadedCoverage->getCoveredFunctions();unsigned NumFuncs = std::distance(Funcs.begin(), Funcs.end());ASSERT_EQ(3U, NumFuncs);}INSTANTIATE_TEST_SUITE_P(ParameterizedCovMapTest, CoverageMappingTest,::testing::Combine(::testing::Bool(),::testing::Bool()));TEST(CoverageMappingTest, filename_roundtrip) {std::vector<std::string> Paths({"dir", "a", "b", "c", "d", "e"});for (bool Compress : {false, true}) {std::string EncodedFilenames;{raw_string_ostream OS(EncodedFilenames);CoverageFilenamesSectionWriter Writer(Paths);Writer.write(OS, Compress);}std::vector<std::string> ReadFilenames;RawCoverageFilenamesReader Reader(EncodedFilenames, ReadFilenames);EXPECT_THAT_ERROR(Reader.read(CovMapVersion::CurrentVersion), Succeeded());ASSERT_EQ(ReadFilenames.size(), Paths.size());for (unsigned I = 1; I < Paths.size(); ++I) {SmallString<256> P(Paths[0]);llvm::sys::path::append(P, Paths[I]);ASSERT_EQ(ReadFilenames[I], P);}}}TEST(CoverageMappingTest, filename_compilation_dir) {std::vector<std::string> Paths({"dir", "a", "b", "c", "d", "e"});for (bool Compress : {false, true}) {std::string EncodedFilenames;{raw_string_ostream OS(EncodedFilenames);CoverageFilenamesSectionWriter Writer(Paths);Writer.write(OS, Compress);}StringRef CompilationDir = "out";std::vector<std::string> ReadFilenames;RawCoverageFilenamesReader Reader(EncodedFilenames, ReadFilenames,CompilationDir);EXPECT_THAT_ERROR(Reader.read(CovMapVersion::CurrentVersion), Succeeded());ASSERT_EQ(ReadFilenames.size(), Paths.size());for (unsigned I = 1; I < Paths.size(); ++I) {SmallString<256> P(CompilationDir);llvm::sys::path::append(P, Paths[I]);ASSERT_EQ(ReadFilenames[I], P);}}}} // end anonymous namespace
set(LLVM_LINK_COMPONENTSCoreCoverageProfileDataSupportObject)add_llvm_unittest(ProfileDataTestsCoverageMappingTest.cppInstrProfDataTest.cppInstrProfTest.cppSampleProfTest.cppMemProfTest.cpp)target_link_libraries(ProfileDataTests PRIVATE LLVMTestingSupport)
#define TEST_PLUGIN_NAME "TestPlugin"#define TEST_PLUGIN_VERSION "0.1-unit"
//===- unittests/Passes/TestPlugin.cpp --------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Passes/PassBuilder.h"#include "llvm/Passes/PassPlugin.h"#include "TestPlugin.h"using namespace llvm;struct TestModulePass : public PassInfoMixin<TestModulePass> {PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {return PreservedAnalyses::all();}static void registerCallbacks(PassBuilder &PB) {PB.registerPipelineParsingCallback([](StringRef Name, ModulePassManager &PM,ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {if (Name == "plugin-pass") {PM.addPass(TestModulePass());return true;}return false;});}};extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAKllvmGetPassPluginInfo() {return {LLVM_PLUGIN_API_VERSION, TEST_PLUGIN_NAME, TEST_PLUGIN_VERSION,TestModulePass::registerCallbacks};}
//===- unittests/Passes/Plugins/PluginsTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/CGSCCPassManager.h"#include "llvm/AsmParser/Parser.h"#include "llvm/Config/config.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/PassManager.h"#include "llvm/Passes/PassBuilder.h"#include "llvm/Passes/PassPlugin.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Transforms/Scalar/LoopPassManager.h"#include "gtest/gtest.h"#include "TestPlugin.h"#include <cstdint>using namespace llvm;void anchor() {}static std::string LibPath(const std::string Name = "TestPlugin") {const auto &Argvs = testing::internal::GetArgvs();const char *Argv0 = Argvs.size() > 0 ? Argvs[0].c_str() : "PluginsTests";void *Ptr = (void *)(intptr_t)anchor;std::string Path = sys::fs::getMainExecutable(Argv0, Ptr);llvm::SmallString<256> Buf{sys::path::parent_path(Path)};sys::path::append(Buf, (Name + LLVM_PLUGIN_EXT).c_str());return std::string(Buf.str());}TEST(PluginsTests, LoadPlugin) {#if !defined(LLVM_ENABLE_PLUGINS)// Disable the test if plugins are disabled.return;#endifauto PluginPath = LibPath();ASSERT_NE("", PluginPath);Expected<PassPlugin> Plugin = PassPlugin::Load(PluginPath);ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath;ASSERT_EQ(TEST_PLUGIN_NAME, Plugin->getPluginName());ASSERT_EQ(TEST_PLUGIN_VERSION, Plugin->getPluginVersion());PassBuilder PB;ModulePassManager PM;ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, "plugin-pass"), Failed());Plugin->registerPassBuilderCallbacks(PB);ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, "plugin-pass"), Succeeded());}// Test that llvmGetPassPluginInfo from DoublerPlugin is called twice with// -fpass-plugin=DoublerPlugin -fpass-plugin=TestPlugin// -fpass-plugin=DoublerPlugin.TEST(PluginsTests, LoadMultiplePlugins) {#if !defined(LLVM_ENABLE_PLUGINS)// Disable the test if plugins are disabled.return;#endifauto DoublerPluginPath = LibPath("DoublerPlugin");auto TestPluginPath = LibPath("TestPlugin");ASSERT_NE("", DoublerPluginPath);ASSERT_NE("", TestPluginPath);Expected<PassPlugin> DoublerPlugin1 = PassPlugin::Load(DoublerPluginPath);ASSERT_TRUE(!!DoublerPlugin1)<< "Plugin path: " << DoublerPlugin1->getFilename();Expected<PassPlugin> TestPlugin = PassPlugin::Load(TestPluginPath);ASSERT_TRUE(!!TestPlugin) << "Plugin path: " << TestPlugin->getFilename();// If llvmGetPassPluginInfo is resolved as a weak symbol taking into account// all loaded symbols, the second call to PassPlugin::Load will actually// return the llvmGetPassPluginInfo from the most recently loaded plugin, in// this case TestPlugin.Expected<PassPlugin> DoublerPlugin2 = PassPlugin::Load(DoublerPluginPath);ASSERT_TRUE(!!DoublerPlugin2)<< "Plugin path: " << DoublerPlugin2->getFilename();ASSERT_EQ("DoublerPlugin", DoublerPlugin1->getPluginName());ASSERT_EQ("2.2-unit", DoublerPlugin1->getPluginVersion());ASSERT_EQ(TEST_PLUGIN_NAME, TestPlugin->getPluginName());ASSERT_EQ(TEST_PLUGIN_VERSION, TestPlugin->getPluginVersion());// Check that the plugin name/version is set correctly when loaded a second// timeASSERT_EQ("DoublerPlugin", DoublerPlugin2->getPluginName());ASSERT_EQ("2.2-unit", DoublerPlugin2->getPluginVersion());PassBuilder PB;ModulePassManager PM;const char *PipelineText = "module(doubler-pass,plugin-pass,doubler-pass)";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Failed());TestPlugin->registerPassBuilderCallbacks(PB);DoublerPlugin1->registerPassBuilderCallbacks(PB);DoublerPlugin2->registerPassBuilderCallbacks(PB);ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded());LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M =parseAssemblyString(R"IR(@doubleme = constant i32 7)IR", Err, C);// Check that the initial value is 7{auto *GV = M->getNamedValue("doubleme");auto *Init = cast<GlobalVariable>(GV)->getInitializer();auto *CI = cast<ConstantInt>(Init);ASSERT_EQ(CI->getSExtValue(), 7);}ModuleAnalysisManager MAM;// Register required pass instrumentation analysis.MAM.registerPass([&] { return PassInstrumentationAnalysis(); });PM.run(*M, MAM);// Check that the final value is 28 because DoublerPlugin::run was called// twice, indicating that the llvmGetPassPluginInfo and registerCallbacks// were correctly called.{// Check the value was doubled twiceauto *GV = M->getNamedValue("doubleme");auto *Init = cast<GlobalVariable>(GV)->getInitializer();auto *CI = cast<ConstantInt>(Init);ASSERT_EQ(CI->getSExtValue(), 28);}}
//===- unittests/Passes/PassBuilderBindingsTest.cpp -----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm-c/Core.h"#include "llvm-c/Transforms/PassBuilder.h"#include "llvm-c/Types.h"#include "gtest/gtest.h"#include <string.h>using namespace llvm;class PassBuilderCTest : public testing::Test {void SetUp() override {char *Triple = LLVMGetDefaultTargetTriple();if (strlen(Triple) == 0) {GTEST_SKIP();LLVMDisposeMessage(Triple);return;}LLVMInitializeAllTargetInfos();char *Err;LLVMTargetRef Target;if (LLVMGetTargetFromTriple(Triple, &Target, &Err)) {FAIL() << "Failed to create target from default triple (" << Triple<< "): " << Err;}TM = LLVMCreateTargetMachine(Target, Triple, "generic", "",LLVMCodeGenLevelDefault, LLVMRelocDefault,LLVMCodeModelDefault);LLVMDisposeMessage(Triple);Context = LLVMContextCreate();Module = LLVMModuleCreateWithNameInContext("test", Context);}void TearDown() override {char *Triple = LLVMGetDefaultTargetTriple();if (strlen(Triple) == 0) {LLVMDisposeMessage(Triple);return; // Skipped, so nothing to tear down}LLVMDisposeMessage(Triple);LLVMDisposeTargetMachine(TM);LLVMDisposeModule(Module);LLVMContextDispose(Context);}public:LLVMTargetMachineRef TM;LLVMModuleRef Module;LLVMContextRef Context;};TEST_F(PassBuilderCTest, Basic) {LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions();LLVMPassBuilderOptionsSetLoopUnrolling(Options, 1);LLVMPassBuilderOptionsSetVerifyEach(Options, 1);LLVMPassBuilderOptionsSetDebugLogging(Options, 0);if (LLVMErrorRef E = LLVMRunPasses(Module, "default<O2>", TM, Options)) {char *Msg = LLVMGetErrorMessage(E);LLVMConsumeError(E);LLVMDisposePassBuilderOptions(Options);FAIL() << "Failed to run passes: " << Msg;}LLVMDisposePassBuilderOptions(Options);}TEST_F(PassBuilderCTest, InvalidPassIsError) {LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions();LLVMErrorRef E1 = LLVMRunPasses(Module, "", TM, Options);LLVMErrorRef E2 = LLVMRunPasses(Module, "does-not-exist-pass", TM, Options);ASSERT_TRUE(E1);ASSERT_TRUE(E2);LLVMConsumeError(E1);LLVMConsumeError(E2);LLVMDisposePassBuilderOptions(Options);}
//===- unittests/Passes/DoublerPlugin.cpp//--------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Passes/PassBuilder.h"#include "llvm/Passes/PassPlugin.h"using namespace llvm;struct DoublerModulePass : public PassInfoMixin<DoublerModulePass> {// Double the value of the initializerPreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {auto *GV = cast<GlobalVariable>(M.getNamedValue("doubleme"));auto *Init = GV->getInitializer();auto *Init2 = ConstantExpr::getAdd(Init, Init);GV->setInitializer(Init2);return PreservedAnalyses::none();}static void registerCallbacks(PassBuilder &PB) {PB.registerPipelineParsingCallback([](StringRef Name, ModulePassManager &PM,ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {if (Name == "doubler-pass") {PM.addPass(DoublerModulePass());return true;}return false;});}};extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAKllvmGetPassPluginInfo() {return {LLVM_PLUGIN_API_VERSION, "DoublerPlugin", "2.2-unit",DoublerModulePass::registerCallbacks};}
# Needed by LLVM's CMake checks because this file defines multiple targets.set(LLVM_OPTIONAL_SOURCES PluginsTest.cpp TestPlugin.cpp DoublerPlugin.cpp PassBuilderBindingsTest.cpp)# If plugins are disabled, this test will disable itself at runtime. Otherwise,# reconfiguring with plugins disabled will leave behind a stale executable.if (LLVM_ENABLE_PLUGINS)add_definitions(-DLLVM_ENABLE_PLUGINS)endif()# The plugin expects to not link against the Support and Core libraries,# but expects them to exist in the process loading the plugin. This doesn't# work with DLLs on Windows (where a shared library can't have undefined# references), so just skip this testcase on Windows.if (NOT WIN32)# On AIX, enable run-time linking to allow symbols from the plugins shared# objects to be properly bound.if(CMAKE_SYSTEM_NAME STREQUAL "AIX")set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-brtl")endif()set(LLVM_LINK_COMPONENTS Support Passes Core AsmParser)add_llvm_unittest(PluginsTestsPluginsTest.cpp)export_executable_symbols_for_plugins(PluginsTests)target_link_libraries(PluginsTests PRIVATE LLVMTestingSupport)set(LLVM_LINK_COMPONENTS)foreach(PLUGIN TestPlugin DoublerPlugin)add_llvm_library(${PLUGIN} MODULE BUILDTREE_ONLY ${PLUGIN}.cpp)# Put PLUGIN next to the unit test executable.set_output_directory(${PLUGIN}BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})set_target_properties(${PLUGIN} PROPERTIES FOLDER "Tests")add_dependencies(${PLUGIN} intrinsics_gen)add_dependencies(PluginsTests ${PLUGIN})endforeach()endif()set(LLVM_LINK_COMPONENTS Support Passes Core Target native AllTargetsInfos)add_llvm_unittest(PassesBindingsTestsPassBuilderBindingsTest.cpp)target_link_libraries(PassesBindingsTests PRIVATE LLVMTestingSupport)
include "llvm/Option/OptParser.td"def OptFlag1 : OptionFlag;def OptFlag2 : OptionFlag;def OptFlag3 : OptionFlag;def A : Flag<["-"], "A">, HelpText<"The A option">, Flags<[OptFlag1]>;def AB : Flag<["-"], "AB">;def B : Joined<["-"], "B">, HelpText<"The B option">, MetaVarName<"B">, Flags<[OptFlag2]>;def C : Separate<["-"], "C">, HelpText<"The C option">, MetaVarName<"C">, Flags<[OptFlag1]>;def SLASH_C : Separate<["/", "-"], "C">, HelpText<"The C option">, MetaVarName<"C">, Flags<[OptFlag3]>;def D : CommaJoined<["-"], "D">, HelpText<"The D option">, MetaVarName<"D">;def E : MultiArg<["-"], "E", 2>, Flags<[OptFlag1, OptFlag2]>;def F : JoinedOrSeparate<["-"], "F">, HelpText<"The F option">, MetaVarName<"F">;def G : JoinedAndSeparate<["-"], "G">, HelpText<"The G option">, MetaVarName<"G">;def Ceq : Joined<["-", "--"], "C=">, Alias<C>, Flags<[OptFlag1]>;def H : Flag<["-"], "H">, Flags<[HelpHidden]>;def my_group : OptionGroup<"my group">;def I : Flag<["-"], "I">, Alias<H>, Group<my_group>;def J : Flag<["-"], "J">, Alias<B>, AliasArgs<["foo"]>;def Joo : Flag<["-"], "Joo">, Alias<B>, AliasArgs<["bar"]>;def K : Flag<["-"], "K">, Alias<B>;def Slurp : Option<["-"], "slurp", KIND_REMAINING_ARGS>;def SlurpJoined : Option<["-"], "slurpjoined", KIND_REMAINING_ARGS_JOINED>;def Blorp : Flag<["-", "--"], "blorp">, HelpText<"The blorp option">, Flags<[OptFlag1]>;def Blarn : Flag<["--", "-"], "blarn">, HelpText<"The blarn option">, Flags<[OptFlag1]>;def Cramb : Joined<["/"], "cramb:">, HelpText<"The cramb option">, MetaVarName<"CRAMB">, Flags<[OptFlag1]>;def Doopf1 : Flag<["-"], "doopf1">, HelpText<"The doopf1 option">, Flags<[OptFlag1]>;def Doopf2 : Flag<["-"], "doopf2">, HelpText<"The doopf2 option">, Flags<[OptFlag2]>;def Ermgh : Joined<["--"], "ermgh">, HelpText<"The ermgh option">, MetaVarName<"ERMGH">, Flags<[OptFlag1]>;def Fjormp : Flag<["--"], "fjormp">, HelpText<"The fjormp option">, Flags<[OptFlag1]>;def Glorrmp_eq : Flag<["--"], "glorrmp=">;def Blurmpq : Flag<["--"], "blurmp">;def Blurmpq_eq : Flag<["--"], "blurmp=">;def DashDash : Option<["--"], "", KIND_REMAINING_ARGS>;class XOpts<string base> : KeyPathAndMacro<"X->", base> {}def marshalled_flag_d : Flag<["-"], "marshalled-flag-d">,MarshallingInfoFlag<XOpts<"MarshalledFlagD">>;def marshalled_flag_c : Flag<["-"], "marshalled-flag-c">,MarshallingInfoFlag<XOpts<"MarshalledFlagC">>,ImpliedByAnyOf<[marshalled_flag_d.KeyPath], "true">;def marshalled_flag_b : Flag<["-"], "marshalled-flag-b">,MarshallingInfoFlag<XOpts<"MarshalledFlagB">>,ImpliedByAnyOf<[marshalled_flag_d.KeyPath], "true">;def marshalled_flag_a : Flag<["-"], "marshalled-flag-a">,MarshallingInfoFlag<XOpts<"MarshalledFlagA">>,ImpliedByAnyOf<[marshalled_flag_c.KeyPath, marshalled_flag_b.KeyPath]>;
//===- unittest/Support/OptionParsingTest.cpp - OptTable tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/Option/Arg.h"#include "llvm/Option/ArgList.h"#include "llvm/Option/Option.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::opt;enum ID {OPT_INVALID = 0, // This is not an option ID.#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \HELPTEXT, METAVAR, VALUES) \OPT_##ID,#include "Opts.inc"LastOption#undef OPTION};#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;#include "Opts.inc"#undef PREFIXenum OptionFlags {OptFlag1 = (1 << 4),OptFlag2 = (1 << 5),OptFlag3 = (1 << 6)};static const OptTable::Info InfoTable[] = {#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \HELPTEXT, METAVAR, VALUES) \{PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES},#include "Opts.inc"#undef OPTION};namespace {class TestOptTable : public OptTable {public:TestOptTable(bool IgnoreCase = false): OptTable(InfoTable, IgnoreCase) {}};}const char *Args[] = {"-A","-Bhi","--C=desu","-C", "bye","-D,adena","-E", "apple", "bloom","-Fblarg","-F", "42","-Gchuu", "2"};TEST(Option, OptionParsing) {TestOptTable T;unsigned MAI, MAC;InputArgList AL = T.ParseArgs(Args, MAI, MAC);// Check they all exist.EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_B));EXPECT_TRUE(AL.hasArg(OPT_C));EXPECT_TRUE(AL.hasArg(OPT_D));EXPECT_TRUE(AL.hasArg(OPT_E));EXPECT_TRUE(AL.hasArg(OPT_F));EXPECT_TRUE(AL.hasArg(OPT_G));// Check the values.EXPECT_EQ("hi", AL.getLastArgValue(OPT_B));EXPECT_EQ("bye", AL.getLastArgValue(OPT_C));EXPECT_EQ("adena", AL.getLastArgValue(OPT_D));std::vector<std::string> Es = AL.getAllArgValues(OPT_E);EXPECT_EQ("apple", Es[0]);EXPECT_EQ("bloom", Es[1]);EXPECT_EQ("42", AL.getLastArgValue(OPT_F));std::vector<std::string> Gs = AL.getAllArgValues(OPT_G);EXPECT_EQ("chuu", Gs[0]);EXPECT_EQ("2", Gs[1]);// Check the help text.std::string Help;raw_string_ostream RSO(Help);T.printHelp(RSO, "test", "title!");EXPECT_NE(std::string::npos, Help.find("-A"));// Check usage line.T.printHelp(RSO, "name [options] file...", "title!");EXPECT_NE(std::string::npos, Help.find("USAGE: name [options] file...\n"));// Test aliases.auto Cs = AL.filtered(OPT_C);ASSERT_NE(Cs.begin(), Cs.end());EXPECT_EQ("desu", StringRef((*Cs.begin())->getValue()));ArgStringList ASL;(*Cs.begin())->render(AL, ASL);ASSERT_EQ(2u, ASL.size());EXPECT_EQ("-C", StringRef(ASL[0]));EXPECT_EQ("desu", StringRef(ASL[1]));}TEST(Option, ParseWithFlagExclusions) {TestOptTable T;unsigned MAI, MAC;// Exclude flag3 to avoid parsing as OPT_SLASH_C.InputArgList AL = T.ParseArgs(Args, MAI, MAC,/*FlagsToInclude=*/0,/*FlagsToExclude=*/OptFlag3);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_C));EXPECT_FALSE(AL.hasArg(OPT_SLASH_C));// Exclude flag1 to avoid parsing as OPT_C.AL = T.ParseArgs(Args, MAI, MAC,/*FlagsToInclude=*/0,/*FlagsToExclude=*/OptFlag1);EXPECT_TRUE(AL.hasArg(OPT_B));EXPECT_FALSE(AL.hasArg(OPT_C));EXPECT_TRUE(AL.hasArg(OPT_SLASH_C));const char *NewArgs[] = { "/C", "foo", "--C=bar" };AL = T.ParseArgs(NewArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_SLASH_C));EXPECT_TRUE(AL.hasArg(OPT_C));EXPECT_EQ("foo", AL.getLastArgValue(OPT_SLASH_C));EXPECT_EQ("bar", AL.getLastArgValue(OPT_C));}TEST(Option, ParseAliasInGroup) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-I" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_H));}TEST(Option, AliasArgs) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-J", "-Joo" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_B));EXPECT_EQ("foo", AL.getAllArgValues(OPT_B)[0]);EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]);}TEST(Option, IgnoreCase) {TestOptTable T(true);unsigned MAI, MAC;const char *MyArgs[] = { "-a", "-joo" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_B));}TEST(Option, DoNotIgnoreCase) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-a", "-joo" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_FALSE(AL.hasArg(OPT_A));EXPECT_FALSE(AL.hasArg(OPT_B));}TEST(Option, SlurpEmpty) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-A", "-slurp" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_Slurp));EXPECT_EQ(0U, AL.getAllArgValues(OPT_Slurp).size());}TEST(Option, Slurp) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-A", "-slurp", "-B", "--", "foo" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_EQ(AL.size(), 2U);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_FALSE(AL.hasArg(OPT_B));EXPECT_TRUE(AL.hasArg(OPT_Slurp));EXPECT_EQ(3U, AL.getAllArgValues(OPT_Slurp).size());EXPECT_EQ("-B", AL.getAllArgValues(OPT_Slurp)[0]);EXPECT_EQ("--", AL.getAllArgValues(OPT_Slurp)[1]);EXPECT_EQ("foo", AL.getAllArgValues(OPT_Slurp)[2]);}TEST(Option, SlurpJoinedEmpty) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-A", "-slurpjoined" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 0U);}TEST(Option, SlurpJoinedOneJoined) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-A", "-slurpjoinedfoo" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 1U);EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined)[0], "foo");}TEST(Option, SlurpJoinedAndSeparate) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-A", "-slurpjoinedfoo", "bar", "baz" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size());EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]);EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]);EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]);}TEST(Option, SlurpJoinedButSeparate) {TestOptTable T;unsigned MAI, MAC;const char *MyArgs[] = { "-A", "-slurpjoined", "foo", "bar", "baz" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size());EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]);EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]);EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]);}TEST(Option, FlagAliasToJoined) {TestOptTable T;unsigned MAI, MAC;// Check that a flag alias provides an empty argument to a joined option.const char *MyArgs[] = { "-K" };InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);EXPECT_EQ(AL.size(), 1U);EXPECT_TRUE(AL.hasArg(OPT_B));EXPECT_EQ(1U, AL.getAllArgValues(OPT_B).size());EXPECT_EQ("", AL.getAllArgValues(OPT_B)[0]);}TEST(Option, FindNearest) {TestOptTable T;std::string Nearest;// Options that are too short should not be considered// "near" other short options.EXPECT_GT(T.findNearest("-A", Nearest), 4U);EXPECT_GT(T.findNearest("/C", Nearest), 4U);EXPECT_GT(T.findNearest("--C=foo", Nearest), 4U);// The nearest candidate should mirror the amount of prefix// characters used in the original string.EXPECT_EQ(1U, T.findNearest("-blorb", Nearest));EXPECT_EQ(Nearest, "-blorp");EXPECT_EQ(1U, T.findNearest("--blorm", Nearest));EXPECT_EQ(Nearest, "--blorp");EXPECT_EQ(1U, T.findNearest("-blarg", Nearest));EXPECT_EQ(Nearest, "-blarn");EXPECT_EQ(1U, T.findNearest("--blarm", Nearest));EXPECT_EQ(Nearest, "--blarn");EXPECT_EQ(1U, T.findNearest("-fjormp", Nearest));EXPECT_EQ(Nearest, "--fjormp");// The nearest candidate respects the prefix and value delimiter// of the original string.EXPECT_EQ(1U, T.findNearest("/framb:foo", Nearest));EXPECT_EQ(Nearest, "/cramb:foo");// `--glormp` should have an editing distance > 0 from `--glormp=`.EXPECT_GT(T.findNearest("--glorrmp", Nearest), 0U);EXPECT_EQ(Nearest, "--glorrmp=");EXPECT_EQ(0U, T.findNearest("--glorrmp=foo", Nearest));// `--blurmps` should correct to `--blurmp`, not `--blurmp=`, even though// both naively have an editing distance of 1.EXPECT_EQ(1U, T.findNearest("--blurmps", Nearest));EXPECT_EQ(Nearest, "--blurmp");// ...but `--blurmps=foo` should correct to `--blurmp=foo`.EXPECT_EQ(1U, T.findNearest("--blurmps=foo", Nearest));EXPECT_EQ(Nearest, "--blurmp=foo");// Flags should be included and excluded as specified.EXPECT_EQ(1U, T.findNearest("-doopf", Nearest, /*FlagsToInclude=*/OptFlag2));EXPECT_EQ(Nearest, "-doopf2");EXPECT_EQ(1U, T.findNearest("-doopf", Nearest,/*FlagsToInclude=*/0,/*FlagsToExclude=*/OptFlag2));EXPECT_EQ(Nearest, "-doopf1");}TEST(DISABLED_Option, FindNearestFIXME) {TestOptTable T;std::string Nearest;// FIXME: Options with joined values should not have those values considered// when calculating distance. The test below would fail if run, but it should// succeed.EXPECT_EQ(1U, T.findNearest("--erbghFoo", Nearest));EXPECT_EQ(Nearest, "--ermghFoo");}TEST(Option, ParseGroupedShortOptions) {TestOptTable T;T.setGroupedShortOptions(true);unsigned MAI, MAC;// Grouped short options can be followed by a long Flag (-Joo), or a non-Flag// option (-C=1).const char *Args1[] = {"-AIJ", "-AIJoo", "-AC=1"};InputArgList AL = T.ParseArgs(Args1, MAI, MAC);EXPECT_TRUE(AL.hasArg(OPT_A));EXPECT_TRUE(AL.hasArg(OPT_H));ASSERT_EQ((size_t)2, AL.getAllArgValues(OPT_B).size());EXPECT_EQ("foo", AL.getAllArgValues(OPT_B)[0]);EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]);ASSERT_TRUE(AL.hasArg(OPT_C));EXPECT_EQ("1", AL.getAllArgValues(OPT_C)[0]);// Prefer a long option to a short option.const char *Args2[] = {"-AB"};InputArgList AL2 = T.ParseArgs(Args2, MAI, MAC);EXPECT_TRUE(!AL2.hasArg(OPT_A));EXPECT_TRUE(AL2.hasArg(OPT_AB));// Short options followed by a long option. We probably should disallow this.const char *Args3[] = {"-AIblorp"};InputArgList AL3 = T.ParseArgs(Args3, MAI, MAC);EXPECT_TRUE(AL3.hasArg(OPT_A));EXPECT_TRUE(AL3.hasArg(OPT_Blorp));}TEST(Option, UnknownOptions) {TestOptTable T;unsigned MAI, MAC;const char *Args[] = {"-u", "--long", "0"};for (int I = 0; I < 2; ++I) {T.setGroupedShortOptions(I != 0);InputArgList AL = T.ParseArgs(Args, MAI, MAC);const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN);ASSERT_EQ((size_t)2, Unknown.size());EXPECT_EQ("-u", Unknown[0]);EXPECT_EQ("--long", Unknown[1]);}}TEST(Option, FlagsWithoutValues) {TestOptTable T;T.setGroupedShortOptions(true);unsigned MAI, MAC;const char *Args[] = {"-A=1", "-A="};InputArgList AL = T.ParseArgs(Args, MAI, MAC);const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN);ASSERT_EQ((size_t)2, Unknown.size());EXPECT_EQ("-A=1", Unknown[0]);EXPECT_EQ("-A=", Unknown[1]);}TEST(Option, UnknownGroupedShortOptions) {TestOptTable T;T.setGroupedShortOptions(true);unsigned MAI, MAC;const char *Args[] = {"-AuzK", "-AuzK"};InputArgList AL = T.ParseArgs(Args, MAI, MAC);const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN);ASSERT_EQ((size_t)4, Unknown.size());EXPECT_EQ("-u", Unknown[0]);EXPECT_EQ("-z", Unknown[1]);EXPECT_EQ("-u", Unknown[2]);EXPECT_EQ("-z", Unknown[3]);}
//===- unittest/Support/OptionMarshallingTest.cpp - OptParserEmitter tests ===////// 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////===----------------------------------------------------------------------===//#include "gtest/gtest.h"struct OptionWithMarshallingInfo {const char *Name;const char *KeyPath;const char *ImpliedCheck;const char *ImpliedValue;};static const OptionWithMarshallingInfo MarshallingTable[] = {#define OPTION_WITH_MARSHALLING( \PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \MERGER, EXTRACTOR, TABLE_INDEX) \{NAME, #KEYPATH, #IMPLIED_CHECK, #IMPLIED_VALUE},#include "Opts.inc"#undef OPTION_WITH_MARSHALLING};TEST(OptionMarshalling, EmittedOrderSameAsDefinitionOrder) {ASSERT_STREQ(MarshallingTable[0].Name, "marshalled-flag-d");ASSERT_STREQ(MarshallingTable[1].Name, "marshalled-flag-c");ASSERT_STREQ(MarshallingTable[2].Name, "marshalled-flag-b");ASSERT_STREQ(MarshallingTable[3].Name, "marshalled-flag-a");}TEST(OptionMarshalling, EmittedSpecifiedKeyPath) {ASSERT_STREQ(MarshallingTable[0].KeyPath, "X->MarshalledFlagD");ASSERT_STREQ(MarshallingTable[1].KeyPath, "X->MarshalledFlagC");ASSERT_STREQ(MarshallingTable[2].KeyPath, "X->MarshalledFlagB");ASSERT_STREQ(MarshallingTable[3].KeyPath, "X->MarshalledFlagA");}TEST(OptionMarshalling, ImpliedCheckContainsDisjunctionOfKeypaths) {ASSERT_STREQ(MarshallingTable[0].ImpliedCheck, "false");ASSERT_STREQ(MarshallingTable[1].ImpliedCheck, "false || X->MarshalledFlagD");ASSERT_STREQ(MarshallingTable[1].ImpliedValue, "true");ASSERT_STREQ(MarshallingTable[2].ImpliedCheck, "false || X->MarshalledFlagD");ASSERT_STREQ(MarshallingTable[2].ImpliedValue, "true");ASSERT_STREQ(MarshallingTable[3].ImpliedCheck,"false || X->MarshalledFlagC || X->MarshalledFlagB");ASSERT_STREQ(MarshallingTable[3].ImpliedValue, "true");}
set(LLVM_LINK_COMPONENTSOptionSupport)set(LLVM_TARGET_DEFINITIONS Opts.td)tablegen(LLVM Opts.inc -gen-opt-parser-defs)add_public_tablegen_target(OptsTestTableGen)add_llvm_unittest(OptionTestsOptionParsingTest.cppOptionMarshallingTest.cpp)
//===- YAMLTest.cpp - Tests for Object YAML -------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ObjectYAML/YAML.h"#include "llvm/Support/YAMLTraits.h"#include "gtest/gtest.h"using namespace llvm;struct BinaryHolder {yaml::BinaryRef Binary;};namespace llvm {namespace yaml {template <>struct MappingTraits<BinaryHolder> {static void mapping(IO &IO, BinaryHolder &BH) {IO.mapRequired("Binary", BH.Binary);}};} // end namespace yaml} // end namespace llvmTEST(ObjectYAML, BinaryRef) {BinaryHolder BH;SmallVector<char, 32> Buf;llvm::raw_svector_ostream OS(Buf);yaml::Output YOut(OS);YOut << BH;EXPECT_NE(OS.str().find("''"), StringRef::npos);}TEST(ObjectYAML, UnknownOption) {StringRef InputYAML = "InvalidKey: InvalidValue\n""Binary: AAAA\n";BinaryHolder BH;yaml::Input Input(InputYAML);// test 1: default in trying to parse invalid key is an error case.Input >> BH;EXPECT_EQ(Input.error().value(), 22);// test 2: only warn about invalid key if actively set.yaml::Input Input2(InputYAML);BinaryHolder BH2;Input2.setAllowUnknownKeys(true);Input2 >> BH2;EXPECT_EQ(BH2.Binary, yaml::BinaryRef("AAAA"));EXPECT_EQ(Input2.error().value(), 0);}
//===- YAML2ObjTest.cpp --------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ObjectYAML/yaml2obj.h"#include "llvm/ADT/SmallString.h"#include "llvm/Object/ObjectFile.h"#include "llvm/Support/Error.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace object;using namespace yaml;TEST(yaml2ObjectFile, ELF) {bool ErrorReported = false;auto ErrHandler = [&](const Twine &Msg) { ErrorReported = true; };SmallString<0> Storage;std::unique_ptr<ObjectFile> Obj = yaml2ObjectFile(Storage, R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_RELMachine: EM_X86_64)", ErrHandler);ASSERT_FALSE(ErrorReported);ASSERT_TRUE(Obj);ASSERT_TRUE(Obj->isELF());ASSERT_TRUE(Obj->isRelocatableObject());}TEST(yaml2ObjectFile, Errors) {std::vector<std::string> Errors;auto ErrHandler = [&](const Twine &Msg) {Errors.push_back("ObjectYAML: " + Msg.str());};SmallString<0> Storage;StringRef Yaml = R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_RELMachine: EM_X86_64Symbols:- Name: foo- Name: foo- Name: foo)";// 1. Test yaml2ObjectFile().std::unique_ptr<ObjectFile> Obj = yaml2ObjectFile(Storage, Yaml, ErrHandler);ASSERT_FALSE(Obj);ASSERT_TRUE(Errors.size() == 2);ASSERT_TRUE(Errors[0] == "ObjectYAML: repeated symbol name: 'foo'");ASSERT_TRUE(Errors[1] == Errors[0]);// 2. Test convertYAML().Errors.clear();Storage.clear();raw_svector_ostream OS(Storage);yaml::Input YIn(Yaml);bool Res = convertYAML(YIn, OS, ErrHandler);ASSERT_FALSE(Res);ASSERT_TRUE(Errors.size() == 2);ASSERT_TRUE(Errors[0] == "ObjectYAML: repeated symbol name: 'foo'");ASSERT_TRUE(Errors[1] == Errors[0]);}
//===- MinidumpYAMLTest.cpp - Tests for Minidump<->YAML code --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/Minidump.h"#include "llvm/ObjectYAML/yaml2obj.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::minidump;static Expected<std::unique_ptr<object::MinidumpFile>>toBinary(SmallVectorImpl<char> &Storage, StringRef Yaml) {Storage.clear();raw_svector_ostream OS(Storage);yaml::Input YIn(Yaml);if (!yaml::convertYAML(YIn, OS, [](const Twine &Msg) {}))return createStringError(std::errc::invalid_argument,"unable to convert YAML");return object::MinidumpFile::create(MemoryBufferRef(OS.str(), "Binary"));}TEST(MinidumpYAML, Basic) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: SystemInfoProcessor Arch: ARM64Platform ID: LinuxCPU:CPUID: 0x05060708- Type: LinuxMapsText: |400d9000-400db000 r-xp 00000000 b3:04 227 /system/bin/app_process400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process- Type: LinuxAuxvContent: DEADBEEFBAADF00D)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;ASSERT_EQ(3u, File.streams().size());EXPECT_EQ(StreamType::SystemInfo, File.streams()[0].Type);auto ExpectedSysInfo = File.getSystemInfo();ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded());const SystemInfo &SysInfo = *ExpectedSysInfo;EXPECT_EQ(ProcessorArchitecture::ARM64, SysInfo.ProcessorArch);EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId);EXPECT_EQ(0x05060708u, SysInfo.CPU.Arm.CPUID);EXPECT_EQ(StreamType::LinuxMaps, File.streams()[1].Type);EXPECT_EQ("400d9000-400db000 r-xp 00000000 b3:04 227 ""/system/bin/app_process\n""400db000-400dc000 r--p 00001000 b3:04 227 ""/system/bin/app_process\n",toStringRef(*File.getRawStream(StreamType::LinuxMaps)));EXPECT_EQ(StreamType::LinuxAuxv, File.streams()[2].Type);EXPECT_EQ((ArrayRef<uint8_t>{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D}),File.getRawStream(StreamType::LinuxAuxv));}TEST(MinidumpYAML, RawContent) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: LinuxAuxvSize: 9Content: DEADBEEFBAADF00D)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;EXPECT_EQ((ArrayRef<uint8_t>{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D, 0x00}),File.getRawStream(StreamType::LinuxAuxv));}TEST(MinidumpYAML, X86SystemInfo) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: SystemInfoProcessor Arch: X86Platform ID: LinuxCPU:Vendor ID: LLVMLLVMLLVMVersion Info: 0x01020304Feature Info: 0x05060708AMD Extended Features: 0x09000102)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;ASSERT_EQ(1u, File.streams().size());auto ExpectedSysInfo = File.getSystemInfo();ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded());const SystemInfo &SysInfo = *ExpectedSysInfo;EXPECT_EQ(ProcessorArchitecture::X86, SysInfo.ProcessorArch);EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId);EXPECT_EQ("LLVMLLVMLLVM", StringRef(SysInfo.CPU.X86.VendorID,sizeof(SysInfo.CPU.X86.VendorID)));EXPECT_EQ(0x01020304u, SysInfo.CPU.X86.VersionInfo);EXPECT_EQ(0x05060708u, SysInfo.CPU.X86.FeatureInfo);EXPECT_EQ(0x09000102u, SysInfo.CPU.X86.AMDExtendedFeatures);}TEST(MinidumpYAML, OtherSystemInfo) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: SystemInfoProcessor Arch: PPCPlatform ID: LinuxCPU:Features: 000102030405060708090a0b0c0d0e0f)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;ASSERT_EQ(1u, File.streams().size());auto ExpectedSysInfo = File.getSystemInfo();ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded());const SystemInfo &SysInfo = *ExpectedSysInfo;EXPECT_EQ(ProcessorArchitecture::PPC, SysInfo.ProcessorArch);EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId);EXPECT_EQ((ArrayRef<uint8_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),makeArrayRef(SysInfo.CPU.Other.ProcessorFeatures));}// Test that we can parse a normal-looking ExceptionStream.TEST(MinidumpYAML, ExceptionStream) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: ExceptionThread ID: 0x7Exception Record:Exception Code: 0x23Exception Flags: 0x5Exception Record: 0x0102030405060708Exception Address: 0x0a0b0c0d0e0f1011Number of Parameters: 2Parameter 0: 0x22Parameter 1: 0x24Thread Context: 3DeadBeefDefacedABadCafe)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;ASSERT_EQ(1u, File.streams().size());Expected<const minidump::ExceptionStream &> ExpectedStream =File.getExceptionStream();ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());const minidump::ExceptionStream &Stream = *ExpectedStream;EXPECT_EQ(0x7u, Stream.ThreadId);const minidump::Exception &Exception = Stream.ExceptionRecord;EXPECT_EQ(0x23u, Exception.ExceptionCode);EXPECT_EQ(0x5u, Exception.ExceptionFlags);EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);EXPECT_EQ(2u, Exception.NumberParameters);EXPECT_EQ(0x22u, Exception.ExceptionInformation[0]);EXPECT_EQ(0x24u, Exception.ExceptionInformation[1]);Expected<ArrayRef<uint8_t>> ExpectedContext =File.getRawData(Stream.ThreadContext);ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,0xab, 0xad, 0xca, 0xfe}),*ExpectedContext);}// Test that we can parse an exception stream with no ExceptionInformation.TEST(MinidumpYAML, ExceptionStream_NoParameters) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: ExceptionThread ID: 0x7Exception Record:Exception Code: 0x23Exception Flags: 0x5Exception Record: 0x0102030405060708Exception Address: 0x0a0b0c0d0e0f1011Thread Context: 3DeadBeefDefacedABadCafe)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;ASSERT_EQ(1u, File.streams().size());Expected<const minidump::ExceptionStream &> ExpectedStream =File.getExceptionStream();ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());const minidump::ExceptionStream &Stream = *ExpectedStream;EXPECT_EQ(0x7u, Stream.ThreadId);const minidump::Exception &Exception = Stream.ExceptionRecord;EXPECT_EQ(0x23u, Exception.ExceptionCode);EXPECT_EQ(0x5u, Exception.ExceptionFlags);EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);EXPECT_EQ(0u, Exception.NumberParameters);Expected<ArrayRef<uint8_t>> ExpectedContext =File.getRawData(Stream.ThreadContext);ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,0xab, 0xad, 0xca, 0xfe}),*ExpectedContext);}// Test that we can parse an ExceptionStream where the stated number of// parameters is greater than the actual size of the ExceptionInformation// array.TEST(MinidumpYAML, ExceptionStream_TooManyParameters) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: ExceptionThread ID: 0x8Exception Record:Exception Code: 0Number of Parameters: 16Parameter 0: 0x0Parameter 1: 0xffParameter 2: 0xeeParameter 3: 0xddParameter 4: 0xccParameter 5: 0xbbParameter 6: 0xaaParameter 7: 0x99Parameter 8: 0x88Parameter 9: 0x77Parameter 10: 0x66Parameter 11: 0x55Parameter 12: 0x44Parameter 13: 0x33Parameter 14: 0x22Thread Context: 3DeadBeefDefacedABadCafe)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;ASSERT_EQ(1u, File.streams().size());Expected<const minidump::ExceptionStream &> ExpectedStream =File.getExceptionStream();ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());const minidump::ExceptionStream &Stream = *ExpectedStream;EXPECT_EQ(0x8u, Stream.ThreadId);const minidump::Exception &Exception = Stream.ExceptionRecord;EXPECT_EQ(0x0u, Exception.ExceptionCode);EXPECT_EQ(0x0u, Exception.ExceptionFlags);EXPECT_EQ(0x00u, Exception.ExceptionRecord);EXPECT_EQ(0x0u, Exception.ExceptionAddress);EXPECT_EQ(16u, Exception.NumberParameters);EXPECT_EQ(0x0u, Exception.ExceptionInformation[0]);for (int Index = 1; Index < 15; ++Index) {EXPECT_EQ(0x110u - Index * 0x11, Exception.ExceptionInformation[Index]);}Expected<ArrayRef<uint8_t>> ExpectedContext =File.getRawData(Stream.ThreadContext);ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,0xab, 0xad, 0xca, 0xfe}),*ExpectedContext);}// Test that we can parse an ExceptionStream where the number of// ExceptionInformation parameters provided is greater than the// specified Number of Parameters.TEST(MinidumpYAML, ExceptionStream_ExtraParameter) {SmallString<0> Storage;auto ExpectedFile = toBinary(Storage, R"(--- !minidumpStreams:- Type: ExceptionThread ID: 0x7Exception Record:Exception Code: 0x23Exception Flags: 0x5Exception Record: 0x0102030405060708Exception Address: 0x0a0b0c0d0e0f1011Number of Parameters: 2Parameter 0: 0x99Parameter 1: 0x23Parameter 2: 0x42Thread Context: 3DeadBeefDefacedABadCafe)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());object::MinidumpFile &File = **ExpectedFile;ASSERT_EQ(1u, File.streams().size());Expected<const minidump::ExceptionStream &> ExpectedStream =File.getExceptionStream();ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());const minidump::ExceptionStream &Stream = *ExpectedStream;EXPECT_EQ(0x7u, Stream.ThreadId);const minidump::Exception &Exception = Stream.ExceptionRecord;EXPECT_EQ(0x23u, Exception.ExceptionCode);EXPECT_EQ(0x5u, Exception.ExceptionFlags);EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);EXPECT_EQ(2u, Exception.NumberParameters);EXPECT_EQ(0x99u, Exception.ExceptionInformation[0]);EXPECT_EQ(0x23u, Exception.ExceptionInformation[1]);EXPECT_EQ(0x42u, Exception.ExceptionInformation[2]);Expected<ArrayRef<uint8_t>> ExpectedContext =File.getRawData(Stream.ThreadContext);ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,0xab, 0xad, 0xca, 0xfe}),*ExpectedContext);}
//===- ELFYAMLTest.cpp - Tests for ELFYAML.cpp ----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/ELF.h"#include "llvm/Object/ELFObjectFile.h"#include "llvm/Object/ELFTypes.h"#include "llvm/ObjectYAML/yaml2obj.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;template <class ELFT>static Expected<ELFObjectFile<ELFT>> toBinary(SmallVectorImpl<char> &Storage,StringRef Yaml) {Storage.clear();raw_svector_ostream OS(Storage);yaml::Input YIn(Yaml);if (!yaml::convertYAML(YIn, OS, [](const Twine &Msg) {}))return createStringError(std::errc::invalid_argument,"unable to convert YAML");return ELFObjectFile<ELFT>::create(MemoryBufferRef(OS.str(), "Binary"));}TEST(ELFRelocationTypeTest, RelocationTestForVE) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ExpectedFile = toBinary<ELF64LE>(Storage, R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_RELMachine: EM_VESections:- Name: .rela.textType: SHT_RELARelocations:- Type: R_VE_NONE- Type: R_VE_REFLONG- Type: R_VE_REFQUAD- Type: R_VE_SREL32- Type: R_VE_HI32- Type: R_VE_LO32- Type: R_VE_PC_HI32- Type: R_VE_PC_LO32- Type: R_VE_GOT32- Type: R_VE_GOT_HI32- Type: R_VE_GOT_LO32- Type: R_VE_GOTOFF32- Type: R_VE_GOTOFF_HI32- Type: R_VE_GOTOFF_LO32- Type: R_VE_PLT32- Type: R_VE_PLT_HI32- Type: R_VE_PLT_LO32- Type: R_VE_RELATIVE- Type: R_VE_GLOB_DAT- Type: R_VE_JUMP_SLOT- Type: R_VE_COPY- Type: R_VE_DTPMOD64- Type: R_VE_DTPOFF64- Type: R_VE_TLS_GD_HI32- Type: R_VE_TLS_GD_LO32- Type: R_VE_TPOFF_HI32- Type: R_VE_TPOFF_LO32- Type: R_VE_CALL_HI32- Type: R_VE_CALL_LO32)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const ELFObjectFile<ELF64LE> &File = *ExpectedFile;EXPECT_EQ("elf64-ve", File.getFileFormatName());EXPECT_EQ(Triple::ve, File.getArch());// Test relocation types.for (SectionRef Sec : File.sections()) {Expected<StringRef> NameOrErr = Sec.getName();ASSERT_THAT_EXPECTED(NameOrErr, Succeeded());StringRef SectionName = *NameOrErr;if (SectionName != ".rela.text")continue;for (RelocationRef R : Sec.relocations()) {SmallString<32> RelTypeName;using namespace llvm::ELF;#define NAME_CHECK(ID) \case ID: \R.getTypeName(RelTypeName); \EXPECT_EQ(#ID, RelTypeName); \breakswitch (R.getType()) {NAME_CHECK(R_VE_NONE);NAME_CHECK(R_VE_REFLONG);NAME_CHECK(R_VE_REFQUAD);NAME_CHECK(R_VE_SREL32);NAME_CHECK(R_VE_HI32);NAME_CHECK(R_VE_LO32);NAME_CHECK(R_VE_PC_HI32);NAME_CHECK(R_VE_PC_LO32);NAME_CHECK(R_VE_GOT32);NAME_CHECK(R_VE_GOT_HI32);NAME_CHECK(R_VE_GOT_LO32);NAME_CHECK(R_VE_GOTOFF32);NAME_CHECK(R_VE_GOTOFF_HI32);NAME_CHECK(R_VE_GOTOFF_LO32);NAME_CHECK(R_VE_PLT32);NAME_CHECK(R_VE_PLT_HI32);NAME_CHECK(R_VE_PLT_LO32);NAME_CHECK(R_VE_RELATIVE);NAME_CHECK(R_VE_GLOB_DAT);NAME_CHECK(R_VE_JUMP_SLOT);NAME_CHECK(R_VE_COPY);NAME_CHECK(R_VE_DTPMOD64);NAME_CHECK(R_VE_DTPOFF64);NAME_CHECK(R_VE_TLS_GD_HI32);NAME_CHECK(R_VE_TLS_GD_LO32);NAME_CHECK(R_VE_TPOFF_HI32);NAME_CHECK(R_VE_TPOFF_LO32);NAME_CHECK(R_VE_CALL_HI32);NAME_CHECK(R_VE_CALL_LO32);default:FAIL() << "Found unexpected relocation type: " + Twine(R.getType());break;}}}}
//===- DXContainerTest.cpp - Tests for DXContainerFile --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringRef.h"#include "llvm/ADT/Twine.h"#include "llvm/ObjectYAML/ObjectYAML.h"#include "llvm/ObjectYAML/yaml2obj.h"#include "llvm/Support/MemoryBufferRef.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;static bool convert(SmallVectorImpl<char> &Output, const char *YAML) {raw_svector_ostream OS(Output);yaml::Input YIn(YAML);return convertYAML(YIn, OS, [](const Twine &Err) { errs() << Err; });}TEST(DXCFile, ParseEmptyParts) {SmallString<128> Storage;// First read a fully explicit yaml with all sizes and offsets providedASSERT_TRUE(convert(Storage, R"(--- !dxcontainerHeader:Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]Version:Major: 1Minor: 0FileSize: 116PartCount: 7PartOffsets: [ 60, 68, 76, 84, 92, 100, 108 ]Parts:- Name: SFI0Size: 0- Name: ISG1Size: 0- Name: OSG1Size: 0- Name: PSV0Size: 0- Name: STATSize: 0- Name: DXILSize: 0- Name: DEADSize: 0...)"));// Resultchar Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,0x44, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,0x5C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,0x53, 0x46, 0x49, 0x30, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31,0x00, 0x00, 0x00, 0x00, 0x4F, 0x53, 0x47, 0x31, 0x00, 0x00, 0x00, 0x00,0x50, 0x53, 0x56, 0x30, 0x00, 0x00, 0x00, 0x00, 0x53, 0x54, 0x41, 0x54,0x00, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x00, 0x00, 0x00, 0x00,0x44, 0x45, 0x41, 0x44, 0x00, 0x00, 0x00, 0x00,};EXPECT_EQ(Storage.size(), 116u);EXPECT_TRUE(memcmp(Buffer, Storage.data(), 116) == 0);Storage.clear();// Next, read the same file without the part offsets or file size. Both cases// should result in the same final output.ASSERT_TRUE(convert(Storage, R"(--- !dxcontainerHeader:Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]Version:Major: 1Minor: 0PartCount: 7Parts:- Name: SFI0Size: 0- Name: ISG1Size: 0- Name: OSG1Size: 0- Name: PSV0Size: 0- Name: STATSize: 0- Name: DXILSize: 0- Name: DEADSize: 0...)"));EXPECT_EQ(Storage.size(), 116u);EXPECT_TRUE(memcmp(Buffer, Storage.data(), 116) == 0);}
//===- DWARFYAMLTest.cpp - Tests for DWARFYAML.cpp ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ObjectYAML/DWARFYAML.h"#include "llvm/ObjectYAML/DWARFEmitter.h"#include "llvm/Support/Error.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;template <class T> static Error parseDWARFYAML(StringRef Yaml, T &Data) {SMDiagnostic GenerateDiag;yaml::Input YIn(Yaml, /*Ctxt=*/nullptr,[](const SMDiagnostic &Diag, void *DiagContext) {*static_cast<SMDiagnostic *>(DiagContext) = Diag;},&GenerateDiag);YIn >> Data;if (YIn.error())return createStringError(YIn.error(), GenerateDiag.getMessage());return Error::success();}TEST(DebugAddrSection, TestParseDebugAddrYAML) {StringRef Yaml = R"(debug_addr:- Format: DWARF64Length: 0x1234Version: 5)";DWARFYAML::Data Data;EXPECT_THAT_ERROR(parseDWARFYAML(Yaml, Data), Succeeded());}TEST(DebugAddrSection, TestMissingVersion) {StringRef Yaml = R"(Format: DWARF64Length: 0x1234)";DWARFYAML::AddrTableEntry AddrTableEntry;EXPECT_THAT_ERROR(parseDWARFYAML(Yaml, AddrTableEntry),FailedWithMessage("missing required key 'Version'"));}TEST(DebugAddrSection, TestUnexpectedKey) {StringRef Yaml = R"(Format: DWARF64Length: 0x1234Version: 5Blah: unexpected)";DWARFYAML::AddrTableEntry AddrTableEntry;EXPECT_THAT_ERROR(parseDWARFYAML(Yaml, AddrTableEntry),FailedWithMessage("unknown key 'Blah'"));}TEST(DebugPubSection, TestDebugPubSection) {StringRef Yaml = R"(debug_pubnames:Length: 0x1234Version: 2UnitOffset: 0x4321UnitSize: 0x00Entries:- DieOffset: 0x1234Name: abc- DieOffset: 0x4321Name: defdebug_pubtypes:Length: 0x1234Version: 2UnitOffset: 0x4321UnitSize: 0x00Entries:- DieOffset: 0x1234Name: abc- DieOffset: 0x4321Name: def)";DWARFYAML::Data Data;ASSERT_THAT_ERROR(parseDWARFYAML(Yaml, Data), Succeeded());ASSERT_TRUE(Data.PubNames.has_value());DWARFYAML::PubSection PubNames = Data.PubNames.value();ASSERT_EQ(PubNames.Entries.size(), 2u);EXPECT_EQ((uint32_t)PubNames.Entries[0].DieOffset, 0x1234u);EXPECT_EQ(PubNames.Entries[0].Name, "abc");EXPECT_EQ((uint32_t)PubNames.Entries[1].DieOffset, 0x4321u);EXPECT_EQ(PubNames.Entries[1].Name, "def");ASSERT_TRUE(Data.PubTypes.has_value());DWARFYAML::PubSection PubTypes = Data.PubTypes.value();ASSERT_EQ(PubTypes.Entries.size(), 2u);EXPECT_EQ((uint32_t)PubTypes.Entries[0].DieOffset, 0x1234u);EXPECT_EQ(PubTypes.Entries[0].Name, "abc");EXPECT_EQ((uint32_t)PubTypes.Entries[1].DieOffset, 0x4321u);EXPECT_EQ(PubTypes.Entries[1].Name, "def");}TEST(DebugPubSection, TestUnexpectedDescriptor) {StringRef Yaml = R"(debug_pubnames:Length: 0x1234Version: 2UnitOffset: 0x4321UnitSize: 0x00Entries:- DieOffset: 0x1234Descriptor: 0x12Name: abcd)";DWARFYAML::Data Data;EXPECT_THAT_ERROR(parseDWARFYAML(Yaml, Data),FailedWithMessage("unknown key 'Descriptor'"));}TEST(DebugGNUPubSection, TestDebugGNUPubSections) {StringRef Yaml = R"(debug_gnu_pubnames:Length: 0x1234Version: 2UnitOffset: 0x4321UnitSize: 0x00Entries:- DieOffset: 0x1234Descriptor: 0x12Name: abc- DieOffset: 0x4321Descriptor: 0x34Name: defdebug_gnu_pubtypes:Length: 0x1234Version: 2UnitOffset: 0x4321UnitSize: 0x00Entries:- DieOffset: 0x1234Descriptor: 0x12Name: abc- DieOffset: 0x4321Descriptor: 0x34Name: def)";DWARFYAML::Data Data;ASSERT_THAT_ERROR(parseDWARFYAML(Yaml, Data), Succeeded());ASSERT_TRUE(Data.GNUPubNames.has_value());DWARFYAML::PubSection GNUPubNames = Data.GNUPubNames.value();ASSERT_EQ(GNUPubNames.Entries.size(), 2u);EXPECT_EQ((uint32_t)GNUPubNames.Entries[0].DieOffset, 0x1234u);EXPECT_EQ((uint8_t)GNUPubNames.Entries[0].Descriptor, 0x12);EXPECT_EQ(GNUPubNames.Entries[0].Name, "abc");EXPECT_EQ((uint32_t)GNUPubNames.Entries[1].DieOffset, 0x4321u);EXPECT_EQ((uint8_t)GNUPubNames.Entries[1].Descriptor, 0x34);EXPECT_EQ(GNUPubNames.Entries[1].Name, "def");ASSERT_TRUE(Data.GNUPubTypes.has_value());DWARFYAML::PubSection GNUPubTypes = Data.GNUPubTypes.value();ASSERT_EQ(GNUPubTypes.Entries.size(), 2u);EXPECT_EQ((uint32_t)GNUPubTypes.Entries[0].DieOffset, 0x1234u);EXPECT_EQ((uint8_t)GNUPubTypes.Entries[0].Descriptor, 0x12);EXPECT_EQ(GNUPubTypes.Entries[0].Name, "abc");EXPECT_EQ((uint32_t)GNUPubTypes.Entries[1].DieOffset, 0x4321u);EXPECT_EQ((uint8_t)GNUPubTypes.Entries[1].Descriptor, 0x34);EXPECT_EQ(GNUPubTypes.Entries[1].Name, "def");}TEST(DebugGNUPubSection, TestMissingDescriptor) {StringRef Yaml = R"(debug_gnu_pubnames:Length: 0x1234Version: 2UnitOffset: 0x4321UnitSize: 0x00Entries:- DieOffset: 0x1234Name: abcd)";DWARFYAML::Data Data;EXPECT_THAT_ERROR(parseDWARFYAML(Yaml, Data),FailedWithMessage("missing required key 'Descriptor'"));}
set(LLVM_LINK_COMPONENTSObjectObjectYAML)add_llvm_unittest(ObjectYAMLTestsDWARFYAMLTest.cppDXContainerYAMLTest.cppELFYAMLTest.cppMinidumpYAMLTest.cppYAML2ObjTest.cppYAMLTest.cpp)target_link_libraries(ObjectYAMLTests PRIVATE LLVMTestingSupport)
//===- XCOFFObjectFileTest.cpp - Tests for XCOFFObjectFile ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/ELFObjectFile.h"#include "llvm/Object/XCOFFObjectFile.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;using namespace llvm::XCOFF;TEST(XCOFFObjectFileTest, XCOFFObjectType) {// Create an arbitrary object of a non-XCOFF type and test that// dyn_cast<XCOFFObjectFile> returns null for it.char Buf[sizeof(typename ELF64LE::Ehdr)] = {};memcpy(Buf, "\177ELF", 4);auto *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(Buf);EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;MemoryBufferRef Source(StringRef(Buf, sizeof(Buf)), "non-XCOFF");Expected<std::unique_ptr<ObjectFile>> ObjOrErr =ObjectFile::createObjectFile(Source);ASSERT_THAT_EXPECTED(ObjOrErr, Succeeded());EXPECT_TRUE(dyn_cast<XCOFFObjectFile>((*ObjOrErr).get()) == nullptr);}TEST(XCOFFObjectFileTest, doesXCOFFTracebackTableBegin) {EXPECT_TRUE(doesXCOFFTracebackTableBegin({0, 0, 0, 0}));EXPECT_TRUE(doesXCOFFTracebackTableBegin({0, 0, 0, 0, 1}));EXPECT_FALSE(doesXCOFFTracebackTableBegin({0, 0, 0, 1}));EXPECT_FALSE(doesXCOFFTracebackTableBegin({0, 0, 0}));}TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIGeneral) {uint8_t V[] = {0x00, 0x00, 0x22, 0x40, 0x80, 0x00, 0x01, 0x05, 0x58, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x07, 0x61, 0x64,0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x00};uint64_t Size = sizeof(V);Expected<XCOFFTracebackTable> TTOrErr = XCOFFTracebackTable::create(V, Size);ASSERT_THAT_EXPECTED(TTOrErr, Succeeded());XCOFFTracebackTable TT = *TTOrErr;EXPECT_EQ(TT.getVersion(), 0);EXPECT_EQ(TT.getLanguageID(), 0);EXPECT_FALSE(TT.isGlobalLinkage());EXPECT_FALSE(TT.isOutOfLineEpilogOrPrologue());EXPECT_TRUE(TT.hasTraceBackTableOffset());EXPECT_FALSE(TT.isInternalProcedure());EXPECT_FALSE(TT.hasControlledStorage());EXPECT_FALSE(TT.isTOCless());EXPECT_TRUE(TT.isFloatingPointPresent());EXPECT_FALSE(TT.isFloatingPointOperationLogOrAbortEnabled());EXPECT_FALSE(TT.isInterruptHandler());EXPECT_TRUE(TT.isFuncNamePresent());EXPECT_FALSE(TT.isAllocaUsed());EXPECT_EQ(TT.getOnConditionDirective(), 0);EXPECT_FALSE(TT.isCRSaved());EXPECT_FALSE(TT.isLRSaved());EXPECT_TRUE(TT.isBackChainStored());EXPECT_FALSE(TT.isFixup());EXPECT_EQ(TT.getNumOfFPRsSaved(), 0);EXPECT_FALSE(TT.hasExtensionTable());EXPECT_FALSE(TT.hasVectorInfo());EXPECT_EQ(TT.getNumOfGPRsSaved(), 0);EXPECT_EQ(TT.getNumberOfFixedParms(), 1);EXPECT_EQ(TT.getNumberOfFPParms(), 2);EXPECT_TRUE(TT.hasParmsOnStack());ASSERT_TRUE(TT.getParmsType());EXPECT_EQ(TT.getParmsType().value(), "i, f, d");ASSERT_TRUE(TT.getTraceBackTableOffset());EXPECT_EQ(TT.getTraceBackTableOffset().value(), 64u);EXPECT_FALSE(TT.getHandlerMask());ASSERT_TRUE(TT.getFunctionName());EXPECT_EQ(TT.getFunctionName().value(), "add_all");EXPECT_EQ(TT.getFunctionName().value().size(), 7u);EXPECT_FALSE(TT.getAllocaRegister());EXPECT_EQ(Size, 25u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIParmsType) {uint8_t V[] = {0x01, 0x02, 0xA2, 0x40, 0x80, 0x00, 0x02, 0x07, 0x2B, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x07, 0x61, 0x64,0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x00};uint64_t Size = sizeof(V);Expected<XCOFFTracebackTable> TTOrErr = XCOFFTracebackTable::create(V, Size);ASSERT_THAT_EXPECTED(TTOrErr, Succeeded());XCOFFTracebackTable TT = *TTOrErr;EXPECT_EQ(TT.getVersion(), 1);EXPECT_EQ(TT.getLanguageID(), 2);EXPECT_TRUE(TT.isGlobalLinkage());EXPECT_EQ(TT.getNumberOfFixedParms(), 2);EXPECT_EQ(TT.getNumberOfFPParms(), 3);ASSERT_TRUE(TT.getParmsType());EXPECT_EQ(*TT.getParmsType(), "i, i, f, f, d");V[8] = 0xAC;Size = sizeof(V);Expected<XCOFFTracebackTable> TTOrErr1 = XCOFFTracebackTable::create(V, Size);ASSERT_THAT_EXPECTED(TTOrErr1, Succeeded());XCOFFTracebackTable TT1 = *TTOrErr1;ASSERT_TRUE(TT1.getParmsType());EXPECT_EQ(*TT1.getParmsType(), "f, f, d, i, i");V[8] = 0xD4;Size = sizeof(V);Expected<XCOFFTracebackTable> TTOrErr2 = XCOFFTracebackTable::create(V, Size);ASSERT_THAT_EXPECTED(TTOrErr2, Succeeded());XCOFFTracebackTable TT2 = *TTOrErr2;ASSERT_TRUE(TT2.getParmsType());EXPECT_EQ(*TT2.getParmsType(), "d, i, f, f, i");V[6] = 0x01;Size = sizeof(V);Expected<XCOFFTracebackTable> TTOrErr3 = XCOFFTracebackTable::create(V, Size);ASSERT_THAT_EXPECTED(TTOrErr3, Succeeded());XCOFFTracebackTable TT3 = *TTOrErr3;ASSERT_TRUE(TT3.getParmsType());EXPECT_EQ(*TT3.getParmsType(), "d, i, f, f");V[6] = 0x04;V[7] = 0x1E;V[8] = 0xAC;V[9] = 0xAA;V[10] = 0xAA;V[11] = 0xAA;Size = sizeof(V);Expected<XCOFFTracebackTable> TTOrErr4 = XCOFFTracebackTable::create(V, Size);ASSERT_THAT_EXPECTED(TTOrErr4, Succeeded());XCOFFTracebackTable TT4 = *TTOrErr4;ASSERT_TRUE(TT4.getParmsType());EXPECT_EQ(*TT4.getParmsType(),"f, f, d, i, i, f, f, f, f, f, f, f, f, f, f, f, f, ...");}const uint8_t TBTableData[] = {0x00, 0x00, 0x2A, 0x60, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc4, 0x00, 0x00,0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00,0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c,0x6c, 0x1f, 0x02, 0x05, 0xf0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00};TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIControlledStorageInfoDisp) {uint64_t Size = sizeof(TBTableData);Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);ASSERT_THAT_EXPECTED(TTOrErr, Succeeded());XCOFFTracebackTable TT = *TTOrErr;EXPECT_TRUE(TT.hasControlledStorage());ASSERT_TRUE(TT.getNumOfCtlAnchors());EXPECT_EQ(TT.getNumOfCtlAnchors().value(), 2u);ASSERT_TRUE(TT.getControlledStorageInfoDisp());SmallVector<uint32_t, 8> Disp = TT.getControlledStorageInfoDisp().value();ASSERT_EQ(Disp.size(), 2UL);EXPECT_EQ(Disp[0], 0x05050000u);EXPECT_EQ(Disp[1], 0x06060000u);EXPECT_EQ(Size, 45u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIAllocaRegister) {uint64_t Size = sizeof(TBTableData);Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);ASSERT_THAT_EXPECTED(TTOrErr, Succeeded());XCOFFTracebackTable TT = *TTOrErr;ASSERT_TRUE(TT.getAllocaRegister());EXPECT_EQ(*TT.getAllocaRegister(), 31u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIHasVectorInfo) {uint64_t Size = sizeof(TBTableData);Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);ASSERT_THAT_EXPECTED(TTOrErr, Succeeded());XCOFFTracebackTable TT = *TTOrErr;EXPECT_EQ(TT.getNumberOfFixedParms(), 3);EXPECT_EQ(TT.getNumberOfFPParms(), 2);EXPECT_TRUE(TT.hasVectorInfo());EXPECT_TRUE(TT.hasExtensionTable());ASSERT_TRUE(TT.getParmsType());EXPECT_EQ(TT.getParmsType().value(), "v, i, f, i, d, i, v");ASSERT_TRUE(TT.getVectorExt());TBVectorExt VecExt = TT.getVectorExt().value();EXPECT_EQ(VecExt.getNumberOfVRSaved(), 0);EXPECT_TRUE(VecExt.isVRSavedOnStack());EXPECT_FALSE(VecExt.hasVarArgs());EXPECT_EQ(VecExt.getNumberOfVectorParms(), 2u);EXPECT_TRUE(VecExt.hasVMXInstruction());EXPECT_EQ(VecExt.getVectorParmsInfo(), "vf, vf");ASSERT_TRUE(TT.getExtensionTable());EXPECT_EQ(*TT.getExtensionTable(), ExtendedTBTableFlag::TB_SSP_CANARY);EXPECT_EQ(Size, 45u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIHasVectorInfo1) {const uint8_t TBTableData[] = {0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc5, 0x00, 0x00,0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00,0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c,0x6c, 0x11, 0x07, 0x90, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00};uint64_t Size = sizeof(TBTableData);Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);ASSERT_THAT_EXPECTED(TTOrErr, Succeeded());XCOFFTracebackTable TT = *TTOrErr;ASSERT_TRUE(TT.getParmsType());EXPECT_EQ(TT.getParmsType().value(), "v, i, f, i, d, i, v, v");ASSERT_TRUE(TT.getVectorExt());TBVectorExt VecExt = TT.getVectorExt().value();EXPECT_EQ(VecExt.getNumberOfVRSaved(), 4);EXPECT_FALSE(VecExt.isVRSavedOnStack());EXPECT_TRUE(VecExt.hasVarArgs());EXPECT_EQ(VecExt.getNumberOfVectorParms(), 3u);EXPECT_TRUE(VecExt.hasVMXInstruction());EXPECT_EQ(VecExt.getVectorParmsInfo(), "vi, vs, vc");ASSERT_TRUE(TT.getExtensionTable());EXPECT_EQ(*TT.getExtensionTable(), ExtendedTBTableFlag::TB_SSP_CANARY);EXPECT_EQ(Size, 44u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtMandatory) {uint64_t Size = 6;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x6 while reading [0x0, 0x8)"));EXPECT_EQ(Size, 0u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtParmsType) {uint64_t Size = 9;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x9 while reading [0x8, 0xc)"));EXPECT_EQ(Size, 8u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtTBOffset) {uint64_t Size = 14;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0xe while reading [0xc, 0x10)"));EXPECT_EQ(Size, 12u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtHandlerMask) {uint8_t V[] = {0x00, 0x00, 0x22, 0xC0, 0x80, 0x00, 0x01, 0x05, 0x58,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x07};uint64_t Size = sizeof(V);Expected<XCOFFTracebackTable> TTOrErr = XCOFFTracebackTable::create(V, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x12 while reading [0x10, 0x14)"));EXPECT_EQ(Size, 16u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtNumOfCtlAnchors) {uint64_t Size = 19;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x13 while reading [0x10, 0x14)"));EXPECT_EQ(Size, 16u);}TEST(XCOFFObjectFileTest,XCOFFTracebackTableTruncatedAtControlledStorageInfoDisp) {uint64_t Size = 21;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x15 while reading [0x14, 0x18)"));EXPECT_EQ(Size, 20u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtNameLen) {uint64_t Size = 29;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x1d while reading [0x1c, 0x1e)"));EXPECT_EQ(Size, 28u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtFunctionName) {uint64_t Size = 36;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x24 while reading [0x1e, 0x25)"));EXPECT_EQ(Size, 30u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtAllocaUsed) {uint64_t Size = 37;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x25 while reading [0x25, 0x26)"));EXPECT_EQ(Size, 37u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtVectorInfoData) {uint64_t Size = 39;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x27 while reading [0x26, 0x2c)"));EXPECT_EQ(Size, 38u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtVectorInfoParmsInfo) {uint64_t Size = 43;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x2b while reading [0x26, 0x2c)"));EXPECT_EQ(Size, 38u);}TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtExtLongTBTable) {uint64_t Size = 44;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("unexpected end of data at offset 0x2c while reading [0x2c, 0x2d)"));EXPECT_EQ(Size, 44u);}TEST(XCOFFObjectFileTest, XCOFFGetCsectAuxRef32) {uint8_t XCOFF32Binary[] = {// File header.0x01, 0xdf, 0x00, 0x01, 0x5f, 0x58, 0xf8, 0x95, 0x00, 0x00, 0x00, 0x3c,0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,// Section header for empty .data section.0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x40,// Start of symbol table.// C_File symbol.0x2e, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0xff, 0xfe, 0x00, 0x03, 0x67, 0x01,// File Auxiliary Entry.0x61, 0x2e, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// Csect symbol.0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x01, 0x00, 0x00, 0x6b, 0x01,// Csect auxiliary entry.0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x05,0x00, 0x00, 0x00, 0x00, 0x00, 0x00};ArrayRef<uint8_t> XCOFF32Ref(XCOFF32Binary, sizeof(XCOFF32Binary));Expected<std::unique_ptr<ObjectFile>> XCOFFObjOrErr =object::ObjectFile::createObjectFile(MemoryBufferRef(toStringRef(XCOFF32Ref), "dummyXCOFF"),file_magic::xcoff_object_32);ASSERT_THAT_EXPECTED(XCOFFObjOrErr, Succeeded());const XCOFFObjectFile &File = *cast<XCOFFObjectFile>((*XCOFFObjOrErr).get());DataRefImpl Ref;Ref.p = File.getSymbolEntryAddressByIndex(2);XCOFFSymbolRef SymRef = File.toSymbolRef(Ref);Expected<XCOFFCsectAuxRef> CsectRefOrErr = SymRef.getXCOFFCsectAuxRef();ASSERT_THAT_EXPECTED(CsectRefOrErr, Succeeded());// Set csect symbol's auxiliary entry count to 0.XCOFF32Binary[113] = 0;Expected<XCOFFCsectAuxRef> ExpectErr = SymRef.getXCOFFCsectAuxRef();EXPECT_THAT_ERROR(ExpectErr.takeError(),FailedWithMessage("csect symbol \".data\" with index 2 contains no auxiliary entry"));}TEST(XCOFFObjectFileTest, XCOFFGetCsectAuxRef64) {uint8_t XCOFF64Binary[] = {// File header.0x01, 0xf7, 0x00, 0x01, 0x5f, 0x59, 0x25, 0xeb, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,// Section header for empty .data section.0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,// Start of symbol table.// C_File symbol.0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,0xff, 0xfe, 0x00, 0x02, 0x67, 0x01,// File Auxiliary Entry.0x61, 0x2e, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,// Csect symbol.0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,0x00, 0x01, 0x00, 0x00, 0x6b, 0x01,// Csect auxiliary entry.0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x05,0x00, 0x00, 0x00, 0x00, 0x00, 0xfb,// String table.0x00, 0x00, 0x00, 0x10, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x2e, 0x64,0x61, 0x74, 0x61, 0x00};ArrayRef<uint8_t> XCOFF64Ref(XCOFF64Binary, sizeof(XCOFF64Binary));Expected<std::unique_ptr<ObjectFile>> XCOFFObjOrErr =object::ObjectFile::createObjectFile(MemoryBufferRef(toStringRef(XCOFF64Ref), "dummyXCOFF"),file_magic::xcoff_object_64);ASSERT_THAT_EXPECTED(XCOFFObjOrErr, Succeeded());const XCOFFObjectFile &File = *cast<XCOFFObjectFile>((*XCOFFObjOrErr).get());DataRefImpl Ref;Ref.p = File.getSymbolEntryAddressByIndex(2);XCOFFSymbolRef SymRef = File.toSymbolRef(Ref);Expected<XCOFFCsectAuxRef> CsectRefOrErr = SymRef.getXCOFFCsectAuxRef();ASSERT_THAT_EXPECTED(CsectRefOrErr, Succeeded());// Inject incorrect auxiliary type value.XCOFF64Binary[167] = static_cast<uint8_t>(XCOFF::AUX_SYM);Expected<XCOFFCsectAuxRef> NotFoundErr = SymRef.getXCOFFCsectAuxRef();EXPECT_THAT_ERROR(NotFoundErr.takeError(),FailedWithMessage("a csect auxiliary entry has not been found for symbol ""\".data\" with index 2"));// Set csect symbol's auxiliary entry count to 0.XCOFF64Binary[149] = 0;Expected<XCOFFCsectAuxRef> ExpectErr = SymRef.getXCOFFCsectAuxRef();EXPECT_THAT_ERROR(ExpectErr.takeError(),FailedWithMessage("csect symbol \".data\" with index 2 contains no auxiliary entry"));}TEST(XCOFFObjectFileTest, XCOFFTracebackTableErrorAtParameterType) {const uint8_t TBTableData[] = {0x00, 0x00, 0x22, 0x40, 0x80, 0x00, 0x01,0x05, 0x58, 0x00, 0x10, 0x00, 0x00, 0x00,0x00, 0x40, 0x00, 0x07, 0x61, 0x64, 0x64,0x5f, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x00};uint64_t Size = 28;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("ParmsType encodes can not map to ParmsNum parameters ""in parseParmsType."));}TEST(XCOFFObjectFileTest, XCOFFTracebackTableErrorAtParameterTypeWithVecInfo) {const uint8_t TBTableData[] = {0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc0, 0x00, 0x10,0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00,0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c,0x6c, 0x11, 0x07, 0x90, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00};uint64_t Size = 44;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("ParmsType encodes can not map to ParmsNum parameters ""in parseParmsTypeWithVecInfo."));}TEST(XCOFFObjectFileTest, XCOFFTracebackTableErrorAtVecParameterType) {const uint8_t TBTableData[] = {0x00, 0x00, 0x2A, 0x40, 0x80, 0xc0, 0x03, 0x05, 0x48, 0xc0, 0x00, 0x00,0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00,0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c,0x6c, 0x11, 0x07, 0x90, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00};uint64_t Size = 44;Expected<XCOFFTracebackTable> TTOrErr =XCOFFTracebackTable::create(TBTableData, Size);EXPECT_THAT_ERROR(TTOrErr.takeError(),FailedWithMessage("ParmsType encodes more than ParmsNum ""parameters in parseVectorParmsType."));}
//===- SymbolicFileTest.cpp - Tests for SymbolicFile.cpp ------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/SymbolicFile.h"#include "llvm/Support/Host.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <sstream>TEST(Object, DataRefImplOstream) {std::string s;llvm::raw_string_ostream OS(s);llvm::object::DataRefImpl Data;Data.d.a = 0xeeee0000;Data.d.b = 0x0000ffff;static_assert(sizeof Data.p == sizeof(uint64_t) ||sizeof Data.p == sizeof(uint32_t),"Test expected pointer type to be 32 or 64-bit.");char const *Expected;if (sizeof Data.p == sizeof(uint64_t)) {Expected = llvm::sys::IsLittleEndianHost? "(0xffffeeee0000 (0xeeee0000, 0x0000ffff))": "(0xeeee00000000ffff (0xeeee0000, 0x0000ffff))";}else {Expected = "(0xeeee0000 (0xeeee0000, 0x0000ffff))";}OS << Data;OS.flush();EXPECT_EQ(Expected, s);}
//===- SymbolSizeTest.cpp - Tests for SymbolSize.cpp ----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/SymbolSize.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;TEST(Object, SymbolSizeSort) {auto it = symbol_iterator(SymbolRef());std::vector<SymEntry> Syms{SymEntry{it, 0xffffffff00000000ull, 1, 0},SymEntry{it, 0x00ffffff00000000ull, 2, 0},SymEntry{it, 0x00ffffff000000ffull, 3, 0},SymEntry{it, 0x0000000100000000ull, 4, 0},SymEntry{it, 0x00000000000000ffull, 5, 0},SymEntry{it, 0x00000001000000ffull, 6, 0},SymEntry{it, 0x000000010000ffffull, 7, 0},};array_pod_sort(Syms.begin(), Syms.end(), compareAddress);for (unsigned I = 0, N = Syms.size(); I < N - 1; ++I) {EXPECT_LE(Syms[I].Address, Syms[I + 1].Address);}}
#include "llvm/Object/OffloadBinary.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <random>using namespace llvm;using namespace llvm::object;TEST(OffloadingTest, checkOffloadingBinary) {// Create random data to fill the image.std::mt19937 Rng(std::random_device{}());std::uniform_int_distribution<uint64_t> SizeDist(0, 256);std::uniform_int_distribution<uint16_t> KindDist(0);std::uniform_int_distribution<uint16_t> BinaryDist(std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max());std::uniform_int_distribution<int16_t> StringDist('!', '~');std::vector<uint8_t> Image(SizeDist(Rng));std::generate(Image.begin(), Image.end(), [&]() { return BinaryDist(Rng); });std::vector<std::pair<std::string, std::string>> Strings(SizeDist(Rng));for (auto &KeyAndValue : Strings) {std::string Key(SizeDist(Rng), '\0');std::string Value(SizeDist(Rng), '\0');std::generate(Key.begin(), Key.end(), [&]() { return StringDist(Rng); });std::generate(Value.begin(), Value.end(),[&]() { return StringDist(Rng); });KeyAndValue = std::make_pair(Key, Value);}// Create the image.StringMap<StringRef> StringData;for (auto &KeyAndValue : Strings)StringData[KeyAndValue.first] = KeyAndValue.second;std::unique_ptr<MemoryBuffer> ImageData = MemoryBuffer::getMemBuffer({reinterpret_cast<char *>(Image.data()), Image.size()}, "", false);OffloadBinary::OffloadingImage Data;Data.TheImageKind = static_cast<ImageKind>(KindDist(Rng));Data.TheOffloadKind = static_cast<OffloadKind>(KindDist(Rng));Data.Flags = KindDist(Rng);Data.StringData = StringData;Data.Image = std::move(ImageData);auto BinaryBuffer = OffloadBinary::write(Data);auto BinaryOrErr = OffloadBinary::create(*BinaryBuffer);if (!BinaryOrErr)FAIL();// Make sure we get the same data out.auto &Binary = **BinaryOrErr;ASSERT_EQ(Data.TheImageKind, Binary.getImageKind());ASSERT_EQ(Data.TheOffloadKind, Binary.getOffloadKind());ASSERT_EQ(Data.Flags, Binary.getFlags());for (auto &KeyAndValue : Strings)ASSERT_TRUE(StringData[KeyAndValue.first] ==Binary.getString(KeyAndValue.first));EXPECT_TRUE(Data.Image->getBuffer() == Binary.getImage());// Ensure the size and alignment of the data is correct.EXPECT_TRUE(Binary.getSize() % OffloadBinary::getAlignment() == 0);EXPECT_TRUE(Binary.getSize() == BinaryBuffer->getBuffer().size());}
//===- ObjectFileTest.cpp - Tests for ObjectFile.cpp ----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/ObjectFile.h"#include "llvm/Support/ScopedPrinter.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;TEST(SectionedAddress, StreamingOperator) {EXPECT_EQ("SectionedAddress{0x00000047}", to_string(SectionedAddress{0x47}));EXPECT_EQ("SectionedAddress{0x00000047, 42}",to_string(SectionedAddress{0x47, 42}));}
//===- MinidumpTest.cpp - Tests for Minidump.cpp --------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/Minidump.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;using namespace minidump;static Expected<std::unique_ptr<MinidumpFile>> create(ArrayRef<uint8_t> Data) {return MinidumpFile::create(MemoryBufferRef(toStringRef(Data), "Test buffer"));}TEST(MinidumpFile, BasicInterface) {std::vector<uint8_t> Data{ // Header'M', 'D', 'M', 'P', // Signature0x93, 0xa7, 0, 0, // Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};// A very simple minidump file which contains just a single stream.auto ExpectedFile = create(Data);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;const Header &H = File.header();EXPECT_EQ(Header::MagicSignature, H.Signature);EXPECT_EQ(Header::MagicVersion, H.Version);EXPECT_EQ(1u, H.NumberOfStreams);EXPECT_EQ(0x20u, H.StreamDirectoryRVA);EXPECT_EQ(0x03020100u, H.Checksum);EXPECT_EQ(0x07060504u, H.TimeDateStamp);EXPECT_EQ(uint64_t(0x0504030201000908), H.Flags);ASSERT_EQ(1u, File.streams().size());const Directory &Stream0 = File.streams()[0];EXPECT_EQ(StreamType::LinuxCPUInfo, Stream0.Type);EXPECT_EQ(7u, Stream0.Location.DataSize);EXPECT_EQ(0x2cu, Stream0.Location.RVA);EXPECT_EQ("CPUINFO", toStringRef(File.getRawStream(Stream0)));EXPECT_EQ("CPUINFO",toStringRef(*File.getRawStream(StreamType::LinuxCPUInfo)));EXPECT_THAT_EXPECTED(File.getSystemInfo(), Failed<BinaryError>());}// Use the input from the previous test, but corrupt it in various waysTEST(MinidumpFile, create_ErrorCases) {std::vector<uint8_t> FileTooShort{'M', 'D', 'M', 'P'};EXPECT_THAT_EXPECTED(create(FileTooShort), Failed<BinaryError>());std::vector<uint8_t> WrongSignature{// Header'!', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(WrongSignature), Failed<BinaryError>());std::vector<uint8_t> WrongVersion{// Header'M', 'D', 'M', 'P', 0x39, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(WrongVersion), Failed<BinaryError>());std::vector<uint8_t> DirectoryAfterEOF{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 1, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(DirectoryAfterEOF), Failed<BinaryError>());std::vector<uint8_t> TruncatedDirectory{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 1, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(TruncatedDirectory), Failed<BinaryError>());std::vector<uint8_t> Stream0AfterEOF{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x2c, 1, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(Stream0AfterEOF), Failed<BinaryError>());std::vector<uint8_t> Stream0Truncated{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 8, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(Stream0Truncated), Failed<BinaryError>());std::vector<uint8_t> DuplicateStream{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version2, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x40, 0, 0, 0, // RVA// Stream3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,0x40, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(DuplicateStream), Failed<BinaryError>());std::vector<uint8_t> DenseMapInfoConflict{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory0xff, 0xff, 0xff, 0xff, 7, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Stream'C', 'P', 'U', 'I', 'N', 'F', 'O'};EXPECT_THAT_EXPECTED(create(DenseMapInfoConflict), Failed<BinaryError>());}TEST(MinidumpFile, IngoresDummyStreams) {std::vector<uint8_t> TwoDummyStreams{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version2, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,0x20, 0, 0, 0, // RVA0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,0x20, 0, 0, 0, // RVA};auto ExpectedFile = create(TwoDummyStreams);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;ASSERT_EQ(2u, File.streams().size());EXPECT_EQ(StreamType::Unused, File.streams()[0].Type);EXPECT_EQ(StreamType::Unused, File.streams()[1].Type);EXPECT_EQ(None, File.getRawStream(StreamType::Unused));}TEST(MinidumpFile, getSystemInfo) {std::vector<uint8_t> Data{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory7, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// SystemInfo0, 0, 1, 2, // ProcessorArch, ProcessorLevel3, 4, 5, 6, // ProcessorRevision, NumberOfProcessors, ProductType7, 8, 9, 0, 1, 2, 3, 4, // MajorVersion, MinorVersion5, 6, 7, 8, 2, 0, 0, 0, // BuildNumber, PlatformId1, 2, 3, 4, 5, 6, 7, 8, // CSDVersionRVA, SuiteMask, Reserved'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', // VendorID1, 2, 3, 4, 5, 6, 7, 8, // VersionInfo, FeatureInfo9, 0, 1, 2, // AMDExtendedFeatures};auto ExpectedFile = create(Data);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;auto ExpectedInfo = File.getSystemInfo();ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded());const SystemInfo &Info = *ExpectedInfo;EXPECT_EQ(ProcessorArchitecture::X86, Info.ProcessorArch);EXPECT_EQ(0x0201, Info.ProcessorLevel);EXPECT_EQ(0x0403, Info.ProcessorRevision);EXPECT_EQ(5, Info.NumberOfProcessors);EXPECT_EQ(6, Info.ProductType);EXPECT_EQ(0x00090807u, Info.MajorVersion);EXPECT_EQ(0x04030201u, Info.MinorVersion);EXPECT_EQ(0x08070605u, Info.BuildNumber);EXPECT_EQ(OSPlatform::Win32NT, Info.PlatformId);EXPECT_EQ(0x04030201u, Info.CSDVersionRVA);EXPECT_EQ(0x0605u, Info.SuiteMask);EXPECT_EQ(0x0807u, Info.Reserved);EXPECT_EQ("LLVMLLVMLLVM", llvm::StringRef(Info.CPU.X86.VendorID,sizeof(Info.CPU.X86.VendorID)));EXPECT_EQ(0x04030201u, Info.CPU.X86.VersionInfo);EXPECT_EQ(0x08070605u, Info.CPU.X86.FeatureInfo);EXPECT_EQ(0x02010009u, Info.CPU.X86.AMDExtendedFeatures);}TEST(MinidumpFile, getString) {std::vector<uint8_t> ManyStrings{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version2, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,0x20, 0, 0, 0, // RVA1, 0, 0, 0, 0, 0, // String1 - odd length0, 0, 1, 0, 0, 0, // String2 - too long2, 0, 0, 0, 0, 0xd8, // String3 - invalid utf160, 0, 0, 0, 0, 0, // String4 - ""2, 0, 0, 0, 'a', 0, // String5 - "a"0, // Mis-align next string2, 0, 0, 0, 'a', 0, // String6 - "a"};auto ExpectedFile = create(ManyStrings);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;EXPECT_THAT_EXPECTED(File.getString(44), Failed<BinaryError>());EXPECT_THAT_EXPECTED(File.getString(50), Failed<BinaryError>());EXPECT_THAT_EXPECTED(File.getString(56), Failed<BinaryError>());EXPECT_THAT_EXPECTED(File.getString(62), HasValue(""));EXPECT_THAT_EXPECTED(File.getString(68), HasValue("a"));EXPECT_THAT_EXPECTED(File.getString(75), HasValue("a"));// Check the case when the size field does not fit into the remaining data.EXPECT_THAT_EXPECTED(File.getString(ManyStrings.size() - 2),Failed<BinaryError>());}TEST(MinidumpFile, getModuleList) {std::vector<uint8_t> OneModule{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory4, 0, 0, 0, 112, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// ModuleList1, 0, 0, 0, // NumberOfModules1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion0, 0, 0, 0, 0, 0, 0, 0, // FileVersion0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags0, 0, 0, 0, // FileOS0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType0, 0, 0, 0, 0, 0, 0, 0, // FileDate1, 2, 3, 4, 5, 6, 7, 8, // CvRecord9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord7, 8, 9, 0, 1, 2, 3, 4, // Reserved05, 6, 7, 8, 9, 0, 1, 2, // Reserved1};// Same as before, but with a padded module list.std::vector<uint8_t> PaddedModule{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory4, 0, 0, 0, 116, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// ModuleList1, 0, 0, 0, // NumberOfModules0, 0, 0, 0, // Padding1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion0, 0, 0, 0, 0, 0, 0, 0, // FileVersion0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags0, 0, 0, 0, // FileOS0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType0, 0, 0, 0, 0, 0, 0, 0, // FileDate1, 2, 3, 4, 5, 6, 7, 8, // CvRecord9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord7, 8, 9, 0, 1, 2, 3, 4, // Reserved05, 6, 7, 8, 9, 0, 1, 2, // Reserved1};for (ArrayRef<uint8_t> Data : {OneModule, PaddedModule}) {auto ExpectedFile = create(Data);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;Expected<ArrayRef<Module>> ExpectedModule = File.getModuleList();ASSERT_THAT_EXPECTED(ExpectedModule, Succeeded());ASSERT_EQ(1u, ExpectedModule->size());const Module &M = ExpectedModule.get()[0];EXPECT_EQ(0x0807060504030201u, M.BaseOfImage);EXPECT_EQ(0x02010009u, M.SizeOfImage);EXPECT_EQ(0x06050403u, M.Checksum);EXPECT_EQ(0x00090807u, M.TimeDateStamp);EXPECT_EQ(0x04030201u, M.ModuleNameRVA);EXPECT_EQ(0x04030201u, M.CvRecord.DataSize);EXPECT_EQ(0x08070605u, M.CvRecord.RVA);EXPECT_EQ(0x02010009u, M.MiscRecord.DataSize);EXPECT_EQ(0x06050403u, M.MiscRecord.RVA);EXPECT_EQ(0x0403020100090807u, M.Reserved0);EXPECT_EQ(0x0201000908070605u, M.Reserved1);}std::vector<uint8_t> StreamTooShort{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory4, 0, 0, 0, 111, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// ModuleList1, 0, 0, 0, // NumberOfModules1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion0, 0, 0, 0, 0, 0, 0, 0, // FileVersion0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags0, 0, 0, 0, // FileOS0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType0, 0, 0, 0, 0, 0, 0, 0, // FileDate1, 2, 3, 4, 5, 6, 7, 8, // CvRecord9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord7, 8, 9, 0, 1, 2, 3, 4, // Reserved05, 6, 7, 8, 9, 0, 1, 2, // Reserved1};auto ExpectedFile = create(StreamTooShort);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;EXPECT_THAT_EXPECTED(File.getModuleList(), Failed<BinaryError>());}TEST(MinidumpFile, getThreadList) {std::vector<uint8_t> OneThread{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory3, 0, 0, 0, 52, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// ThreadList1, 0, 0, 0, // NumberOfThreads1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock// Stack5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA// Context1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA};// Same as before, but with a padded thread list.std::vector<uint8_t> PaddedThread{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory3, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// ThreadList1, 0, 0, 0, // NumberOfThreads0, 0, 0, 0, // Padding1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock// Stack5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA// Context1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA};for (ArrayRef<uint8_t> Data : {OneThread, PaddedThread}) {auto ExpectedFile = create(Data);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;Expected<ArrayRef<Thread>> ExpectedThread = File.getThreadList();ASSERT_THAT_EXPECTED(ExpectedThread, Succeeded());ASSERT_EQ(1u, ExpectedThread->size());const Thread &T = ExpectedThread.get()[0];EXPECT_EQ(0x04030201u, T.ThreadId);EXPECT_EQ(0x08070605u, T.SuspendCount);EXPECT_EQ(0x02010009u, T.PriorityClass);EXPECT_EQ(0x06050403u, T.Priority);EXPECT_EQ(0x0403020100090807u, T.EnvironmentBlock);EXPECT_EQ(0x0201000908070605u, T.Stack.StartOfMemoryRange);EXPECT_EQ(0x06050403u, T.Stack.Memory.DataSize);EXPECT_EQ(0x00090807u, T.Stack.Memory.RVA);EXPECT_EQ(0x04030201u, T.Context.DataSize);EXPECT_EQ(0x08070605u, T.Context.RVA);}}TEST(MinidumpFile, getMemoryList) {std::vector<uint8_t> OneRange{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory5, 0, 0, 0, 20, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryDescriptor1, 0, 0, 0, // NumberOfMemoryRanges5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA};// Same as before, but with a padded memory list.std::vector<uint8_t> PaddedRange{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory5, 0, 0, 0, 24, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryDescriptor1, 0, 0, 0, // NumberOfMemoryRanges0, 0, 0, 0, // Padding5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA};for (ArrayRef<uint8_t> Data : {OneRange, PaddedRange}) {auto ExpectedFile = create(Data);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;Expected<ArrayRef<MemoryDescriptor>> ExpectedRanges = File.getMemoryList();ASSERT_THAT_EXPECTED(ExpectedRanges, Succeeded());ASSERT_EQ(1u, ExpectedRanges->size());const MemoryDescriptor &MD = ExpectedRanges.get()[0];EXPECT_EQ(0x0201000908070605u, MD.StartOfMemoryRange);EXPECT_EQ(0x06050403u, MD.Memory.DataSize);EXPECT_EQ(0x00090807u, MD.Memory.RVA);}}TEST(MinidumpFile, getMemoryInfoList) {std::vector<uint8_t> OneEntry{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryInfoListHeader16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries// MemoryInfo0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved00, 1, 2, 3, 4, 5, 6, 7, // RegionSize0, 16, 0, 0, 32, 0, 0, 0, // State, Protect0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1};// Same as before, but the list header is larger.std::vector<uint8_t> BiggerHeader{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryInfoListHeader20, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries0, 0, 0, 0, // ???// MemoryInfo0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved00, 1, 2, 3, 4, 5, 6, 7, // RegionSize0, 16, 0, 0, 32, 0, 0, 0, // State, Protect0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1};// Same as before, but the entry is larger.std::vector<uint8_t> BiggerEntry{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryInfoListHeader16, 0, 0, 0, 52, 0, 0, 0, // SizeOfHeader, SizeOfEntry1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries// MemoryInfo0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved00, 1, 2, 3, 4, 5, 6, 7, // RegionSize0, 16, 0, 0, 32, 0, 0, 0, // State, Protect0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved10, 0, 0, 0, // ???};for (ArrayRef<uint8_t> Data : {OneEntry, BiggerHeader, BiggerEntry}) {auto ExpectedFile = create(Data);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;auto ExpectedInfo = File.getMemoryInfoList();ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded());ASSERT_EQ(1, std::distance(ExpectedInfo->begin(), ExpectedInfo->end()));const MemoryInfo &Info = *ExpectedInfo.get().begin();EXPECT_EQ(0x0706050403020100u, Info.BaseAddress);EXPECT_EQ(0x0504030201000908u, Info.AllocationBase);EXPECT_EQ(MemoryProtection::Execute, Info.AllocationProtect);EXPECT_EQ(0x09080706u, Info.Reserved0);EXPECT_EQ(0x0706050403020100u, Info.RegionSize);EXPECT_EQ(MemoryState::Commit, Info.State);EXPECT_EQ(MemoryProtection::ExecuteRead, Info.Protect);EXPECT_EQ(MemoryType::Private, Info.Type);EXPECT_EQ(0x01000908u, Info.Reserved1);}// Header does not fit into the stream.std::vector<uint8_t> HeaderTooBig{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryInfoListHeader16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry1, 0, 0, 0, 0, 0, 0, // ???};Expected<std::unique_ptr<MinidumpFile>> File = create(HeaderTooBig);ASSERT_THAT_EXPECTED(File, Succeeded());EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>());// Header fits into the stream, but it is too small to contain the required// entries.std::vector<uint8_t> HeaderTooSmall{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryInfoListHeader15, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry1, 0, 0, 0, 0, 0, 0, // ???};File = create(HeaderTooSmall);ASSERT_THAT_EXPECTED(File, Succeeded());EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>());std::vector<uint8_t> EntryTooBig{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryInfoListHeader16, 0, 0, 0, 49, 0, 0, 0, // SizeOfHeader, SizeOfEntry1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries// MemoryInfo0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved00, 1, 2, 3, 4, 5, 6, 7, // RegionSize0, 16, 0, 0, 32, 0, 0, 0, // State, Protect0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1};File = create(EntryTooBig);ASSERT_THAT_EXPECTED(File, Succeeded());EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>());std::vector<uint8_t> ThreeEntries{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,32, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp0, 0, 0, 0, 0, 0, 0, 0, // Flags// Stream Directory16, 0, 0, 0, 160, 0, 0, 0, // Type, DataSize,44, 0, 0, 0, // RVA// MemoryInfoListHeader16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry3, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries// MemoryInfo0, 1, 2, 3, 0, 0, 0, 0, // BaseAddress0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved00, 0, 0, 0, 0, 0, 0, 0, // RegionSize0, 0, 0, 0, 0, 0, 0, 0, // State, Protect0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved10, 0, 4, 5, 6, 7, 0, 0, // BaseAddress0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved00, 0, 0, 0, 0, 0, 0, 0, // RegionSize0, 0, 0, 0, 0, 0, 0, 0, // State, Protect0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved10, 0, 0, 8, 9, 0, 1, 0, // BaseAddress0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved00, 0, 0, 0, 0, 0, 0, 0, // RegionSize0, 0, 0, 0, 0, 0, 0, 0, // State, Protect0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1};File = create(ThreeEntries);ASSERT_THAT_EXPECTED(File, Succeeded());auto ExpectedInfo = File.get()->getMemoryInfoList();ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded());EXPECT_THAT(to_vector<3>(map_range(*ExpectedInfo,[](const MemoryInfo &Info) -> uint64_t {return Info.BaseAddress;})),testing::ElementsAre(0x0000000003020100u, 0x0000070605040000u,0x0001000908000000u));}TEST(MinidumpFile, getExceptionStream) {std::vector<uint8_t> Data{// Header'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version1, 0, 0, 0, // NumberOfStreams,0x20, 0, 0, 0, // StreamDirectoryRVA0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp8, 9, 0, 1, 2, 3, 4, 5, // Flags// Stream Directory6, 0, 0, 0, 168, 0, 0, 0, // Type, DataSize,0x2c, 0, 0, 0, // RVA// Exception Stream1, 2, 3, 4, // Thread ID0, 0, 0, 0, // Padding// Exception Record2, 3, 4, 2, 7, 8, 8, 9, // Code, Flags3, 4, 5, 6, 7, 8, 9, 10, // Inner exception record address8, 7, 6, 5, 4, 3, 2, 1, // Exception address4, 0, 0, 0, 0, 0, 0, 0, // Parameter count, padding0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // Parameter 00x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // Parameter 10x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Parameter 20x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // Parameter 30x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // Parameter 40x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // Parameter 50x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // Parameter 60x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // Parameter 70x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // Parameter 80xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, // Parameter 90xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, // Parameter 100xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // Parameter 110xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, // Parameter 120xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, // Parameter 130xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, // Parameter 14// Thread Context0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, // DataSize, RVA};auto ExpectedFile = create(Data);ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());const MinidumpFile &File = **ExpectedFile;Expected<const minidump::ExceptionStream &> ExpectedStream =File.getExceptionStream();ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());EXPECT_EQ(0x04030201u, ExpectedStream->ThreadId);const minidump::Exception &Exception = ExpectedStream->ExceptionRecord;EXPECT_EQ(0x02040302u, Exception.ExceptionCode);EXPECT_EQ(0x09080807u, Exception.ExceptionFlags);EXPECT_EQ(0x0a09080706050403u, Exception.ExceptionRecord);EXPECT_EQ(0x0102030405060708u, Exception.ExceptionAddress);EXPECT_EQ(4u, Exception.NumberParameters);for (uint64_t index = 0; index < Exception.MaxParameters; ++index) {EXPECT_EQ(0x1716151413121110u + index * 0x1010101010101010u,Exception.ExceptionInformation[index]);}EXPECT_EQ(0x84838281, ExpectedStream->ThreadContext.DataSize);EXPECT_EQ(0x88878685, ExpectedStream->ThreadContext.RVA);}
//===----------------------- ELFTypesTest.cpp -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/ELFTypes.h"#include "gtest/gtest.h"#include <iostream>using namespace llvm;using namespace llvm::object;template <typename ELFT> using Elf_Note = typename ELFT::Note;template <class ELFT> struct NoteTestData {std::vector<uint8_t> Data;const Elf_Note_Impl<ELFT> getElfNote(StringRef Name, uint32_t Type,ArrayRef<uint8_t> Desc) {Data.resize(sizeof(Elf_Nhdr_Impl<ELFT>) +alignTo<Elf_Nhdr_Impl<ELFT>::Align>(Name.size()) +alignTo<Elf_Nhdr_Impl<ELFT>::Align>(Desc.size()),0);Elf_Nhdr_Impl<ELFT> *Nhdr =reinterpret_cast<Elf_Nhdr_Impl<ELFT> *>(Data.data());Nhdr->n_namesz = (Name == "") ? 0 : Name.size() + 1;Nhdr->n_descsz = Desc.size();Nhdr->n_type = Type;auto NameOffset = Data.begin() + sizeof(*Nhdr);std::copy(Name.begin(), Name.end(), NameOffset);auto DescOffset =NameOffset + alignTo<Elf_Nhdr_Impl<ELFT>::Align>(Nhdr->n_namesz);std::copy(Desc.begin(), Desc.end(), DescOffset);return Elf_Note_Impl<ELFT>(*Nhdr);}};TEST(ELFTypesTest, NoteTest) {static const uint8_t Random[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};ArrayRef<uint8_t> RandomData = makeArrayRef(Random);NoteTestData<ELF64LE> TestData;auto Note1 = TestData.getElfNote(StringRef("AMD"), ELF::NT_AMDGPU_METADATA,RandomData);EXPECT_EQ(Note1.getName(), "AMD");EXPECT_EQ(Note1.getType(), ELF::NT_AMDGPU_METADATA);EXPECT_EQ(Note1.getDesc(), RandomData);EXPECT_EQ(Note1.getDescAsStringRef(),StringRef(reinterpret_cast<const char *>(Random), sizeof(Random)));auto Note2 = TestData.getElfNote("", ELF::NT_AMDGPU_METADATA, RandomData);EXPECT_EQ(Note2.getName(), "");auto Note3 =TestData.getElfNote("AMD", ELF::NT_AMDGPU_METADATA, ArrayRef<uint8_t>());EXPECT_EQ(Note3.getDescAsStringRef(), StringRef(""));}
//===- ELFTest.cpp - Tests for ELF.cpp ------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/ELF.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;using namespace llvm::ELF;TEST(ELFTest, getELFRelocationTypeNameForVE) {EXPECT_EQ("R_VE_NONE", getELFRelocationTypeName(EM_VE, R_VE_NONE));EXPECT_EQ("R_VE_REFLONG", getELFRelocationTypeName(EM_VE, R_VE_REFLONG));EXPECT_EQ("R_VE_REFQUAD", getELFRelocationTypeName(EM_VE, R_VE_REFQUAD));EXPECT_EQ("R_VE_SREL32", getELFRelocationTypeName(EM_VE, R_VE_SREL32));EXPECT_EQ("R_VE_HI32", getELFRelocationTypeName(EM_VE, R_VE_HI32));EXPECT_EQ("R_VE_LO32", getELFRelocationTypeName(EM_VE, R_VE_LO32));EXPECT_EQ("R_VE_PC_HI32", getELFRelocationTypeName(EM_VE, R_VE_PC_HI32));EXPECT_EQ("R_VE_PC_LO32", getELFRelocationTypeName(EM_VE, R_VE_PC_LO32));EXPECT_EQ("R_VE_GOT32", getELFRelocationTypeName(EM_VE, R_VE_GOT32));EXPECT_EQ("R_VE_GOT_HI32", getELFRelocationTypeName(EM_VE, R_VE_GOT_HI32));EXPECT_EQ("R_VE_GOT_LO32", getELFRelocationTypeName(EM_VE, R_VE_GOT_LO32));EXPECT_EQ("R_VE_GOTOFF32", getELFRelocationTypeName(EM_VE, R_VE_GOTOFF32));EXPECT_EQ("R_VE_GOTOFF_HI32",getELFRelocationTypeName(EM_VE, R_VE_GOTOFF_HI32));EXPECT_EQ("R_VE_GOTOFF_LO32",getELFRelocationTypeName(EM_VE, R_VE_GOTOFF_LO32));EXPECT_EQ("R_VE_PLT32", getELFRelocationTypeName(EM_VE, R_VE_PLT32));EXPECT_EQ("R_VE_PLT_HI32", getELFRelocationTypeName(EM_VE, R_VE_PLT_HI32));EXPECT_EQ("R_VE_PLT_LO32", getELFRelocationTypeName(EM_VE, R_VE_PLT_LO32));EXPECT_EQ("R_VE_RELATIVE", getELFRelocationTypeName(EM_VE, R_VE_RELATIVE));EXPECT_EQ("R_VE_GLOB_DAT", getELFRelocationTypeName(EM_VE, R_VE_GLOB_DAT));EXPECT_EQ("R_VE_JUMP_SLOT", getELFRelocationTypeName(EM_VE, R_VE_JUMP_SLOT));EXPECT_EQ("R_VE_COPY", getELFRelocationTypeName(EM_VE, R_VE_COPY));EXPECT_EQ("R_VE_DTPMOD64", getELFRelocationTypeName(EM_VE, R_VE_DTPMOD64));EXPECT_EQ("R_VE_DTPOFF64", getELFRelocationTypeName(EM_VE, R_VE_DTPOFF64));EXPECT_EQ("R_VE_TLS_GD_HI32",getELFRelocationTypeName(EM_VE, R_VE_TLS_GD_HI32));EXPECT_EQ("R_VE_TLS_GD_LO32",getELFRelocationTypeName(EM_VE, R_VE_TLS_GD_LO32));EXPECT_EQ("R_VE_TPOFF_HI32",getELFRelocationTypeName(EM_VE, R_VE_TPOFF_HI32));EXPECT_EQ("R_VE_TPOFF_LO32",getELFRelocationTypeName(EM_VE, R_VE_TPOFF_LO32));EXPECT_EQ("R_VE_CALL_HI32", getELFRelocationTypeName(EM_VE, R_VE_CALL_HI32));EXPECT_EQ("R_VE_CALL_LO32", getELFRelocationTypeName(EM_VE, R_VE_CALL_LO32));}TEST(ELFTest, getELFRelocationTypeNameForLoongArch) {EXPECT_EQ("R_LARCH_NONE",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_NONE));EXPECT_EQ("R_LARCH_32", getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_32));EXPECT_EQ("R_LARCH_64", getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_64));EXPECT_EQ("R_LARCH_RELATIVE",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_RELATIVE));EXPECT_EQ("R_LARCH_COPY",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_COPY));EXPECT_EQ("R_LARCH_JUMP_SLOT",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_JUMP_SLOT));EXPECT_EQ("R_LARCH_TLS_DTPMOD32",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_TLS_DTPMOD32));EXPECT_EQ("R_LARCH_TLS_DTPMOD64",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_TLS_DTPMOD64));EXPECT_EQ("R_LARCH_TLS_DTPREL32",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_TLS_DTPREL32));EXPECT_EQ("R_LARCH_TLS_DTPREL64",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_TLS_DTPREL64));EXPECT_EQ("R_LARCH_TLS_TPREL32",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_TLS_TPREL32));EXPECT_EQ("R_LARCH_TLS_TPREL64",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_TLS_TPREL64));EXPECT_EQ("R_LARCH_IRELATIVE",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_IRELATIVE));EXPECT_EQ("R_LARCH_MARK_LA",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_MARK_LA));EXPECT_EQ("R_LARCH_MARK_PCREL",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_MARK_PCREL));EXPECT_EQ("R_LARCH_SOP_PUSH_PCREL",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_PCREL));EXPECT_EQ("R_LARCH_SOP_PUSH_ABSOLUTE",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_ABSOLUTE));EXPECT_EQ("R_LARCH_SOP_PUSH_DUP",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_DUP));EXPECT_EQ("R_LARCH_SOP_PUSH_GPREL",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_GPREL));EXPECT_EQ("R_LARCH_SOP_PUSH_TLS_TPREL",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_TLS_TPREL));EXPECT_EQ("R_LARCH_SOP_PUSH_TLS_GOT",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_TLS_GOT));EXPECT_EQ("R_LARCH_SOP_PUSH_TLS_GD",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_TLS_GD));EXPECT_EQ("R_LARCH_SOP_PUSH_PLT_PCREL",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_PUSH_PLT_PCREL));EXPECT_EQ("R_LARCH_SOP_ASSERT",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_ASSERT));EXPECT_EQ("R_LARCH_SOP_NOT",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_NOT));EXPECT_EQ("R_LARCH_SOP_SUB",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_SUB));EXPECT_EQ("R_LARCH_SOP_SL",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_SL));EXPECT_EQ("R_LARCH_SOP_SR",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_SR));EXPECT_EQ("R_LARCH_SOP_ADD",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_ADD));EXPECT_EQ("R_LARCH_SOP_AND",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_AND));EXPECT_EQ("R_LARCH_SOP_IF_ELSE",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_IF_ELSE));EXPECT_EQ("R_LARCH_SOP_POP_32_S_10_5",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_POP_32_S_10_5));EXPECT_EQ("R_LARCH_SOP_POP_32_U_10_12",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_POP_32_U_10_12));EXPECT_EQ("R_LARCH_SOP_POP_32_S_10_12",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_POP_32_S_10_12));EXPECT_EQ("R_LARCH_SOP_POP_32_S_10_16",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_POP_32_S_10_16));EXPECT_EQ("R_LARCH_SOP_POP_32_S_10_16_S2",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_POP_32_S_10_16_S2));EXPECT_EQ("R_LARCH_SOP_POP_32_S_5_20",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_POP_32_S_5_20));EXPECT_EQ("R_LARCH_SOP_POP_32_S_0_5_10_16_S2",getELFRelocationTypeName(EM_LOONGARCH,R_LARCH_SOP_POP_32_S_0_5_10_16_S2));EXPECT_EQ("R_LARCH_SOP_POP_32_S_0_10_10_16_S2",getELFRelocationTypeName(EM_LOONGARCH,R_LARCH_SOP_POP_32_S_0_10_10_16_S2));EXPECT_EQ("R_LARCH_SOP_POP_32_U",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SOP_POP_32_U));EXPECT_EQ("R_LARCH_ADD8",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_ADD8));EXPECT_EQ("R_LARCH_ADD16",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_ADD16));EXPECT_EQ("R_LARCH_ADD24",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_ADD24));EXPECT_EQ("R_LARCH_ADD32",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_ADD32));EXPECT_EQ("R_LARCH_ADD64",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_ADD64));EXPECT_EQ("R_LARCH_SUB8",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SUB8));EXPECT_EQ("R_LARCH_SUB16",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SUB16));EXPECT_EQ("R_LARCH_SUB24",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SUB24));EXPECT_EQ("R_LARCH_SUB32",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SUB32));EXPECT_EQ("R_LARCH_SUB64",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_SUB64));EXPECT_EQ("R_LARCH_GNU_VTINHERIT",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_GNU_VTINHERIT));EXPECT_EQ("R_LARCH_GNU_VTENTRY",getELFRelocationTypeName(EM_LOONGARCH, R_LARCH_GNU_VTENTRY));}TEST(ELFTest, getELFRelativeRelocationType) {EXPECT_EQ(ELF::R_VE_RELATIVE, getELFRelativeRelocationType(EM_VE));}// This is a test for the DataRegion helper struct, defined in ELF.h header.TEST(ELFTest, DataRegionTest) {std::vector<uint8_t> Data = {0, 1, 2};// Used to check that the operator[] works properly.auto CheckOperator = [&](DataRegion<uint8_t> &R) {for (size_t I = 0, E = Data.size(); I != E; ++I) {Expected<uint8_t> ValOrErr = R[I];ASSERT_THAT_EXPECTED(ValOrErr, Succeeded());EXPECT_EQ(*ValOrErr, I);}};// Check we can use the constructor that takes an ArrayRef<T>.DataRegion<uint8_t> Region(Data);CheckOperator(Region);const char *ErrMsg1 ="the index is greater than or equal to the number of entries (3)";EXPECT_THAT_ERROR(Region[3].takeError(), FailedWithMessage(ErrMsg1));EXPECT_THAT_ERROR(Region[4].takeError(), FailedWithMessage(ErrMsg1));// Check we can use the constructor that takes the data begin and the// data end pointers.Region = {Data.data(), Data.data() + Data.size()};CheckOperator(Region);const char *ErrMsg2 = "can't read past the end of the file";EXPECT_THAT_ERROR(Region[3].takeError(), FailedWithMessage(ErrMsg2));EXPECT_THAT_ERROR(Region[4].takeError(), FailedWithMessage(ErrMsg2));}
//===- ELFObjectFileTest.cpp - Tests for ELFObjectFile --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/ELFObjectFile.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/ObjectYAML/yaml2obj.h"#include "llvm/Support/YAMLTraits.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;namespace {// A struct to initialize a buffer to represent an ELF object file.struct DataForTest {std::vector<uint8_t> Data;template <typename T>std::vector<uint8_t> makeElfData(uint8_t Class, uint8_t Encoding,uint16_t Machine) {T Ehdr{}; // Zero-initialise the header.Ehdr.e_ident[ELF::EI_MAG0] = 0x7f;Ehdr.e_ident[ELF::EI_MAG1] = 'E';Ehdr.e_ident[ELF::EI_MAG2] = 'L';Ehdr.e_ident[ELF::EI_MAG3] = 'F';Ehdr.e_ident[ELF::EI_CLASS] = Class;Ehdr.e_ident[ELF::EI_DATA] = Encoding;Ehdr.e_ident[ELF::EI_VERSION] = 1;Ehdr.e_type = ELF::ET_REL;Ehdr.e_machine = Machine;Ehdr.e_version = 1;Ehdr.e_ehsize = sizeof(T);bool IsLittleEndian = Encoding == ELF::ELFDATA2LSB;if (sys::IsLittleEndianHost != IsLittleEndian) {sys::swapByteOrder(Ehdr.e_type);sys::swapByteOrder(Ehdr.e_machine);sys::swapByteOrder(Ehdr.e_version);sys::swapByteOrder(Ehdr.e_ehsize);}uint8_t *EhdrBytes = reinterpret_cast<uint8_t *>(&Ehdr);std::vector<uint8_t> Bytes;std::copy(EhdrBytes, EhdrBytes + sizeof(Ehdr), std::back_inserter(Bytes));return Bytes;}DataForTest(uint8_t Class, uint8_t Encoding, uint16_t Machine) {if (Class == ELF::ELFCLASS64)Data = makeElfData<ELF::Elf64_Ehdr>(Class, Encoding, Machine);else {assert(Class == ELF::ELFCLASS32);Data = makeElfData<ELF::Elf32_Ehdr>(Class, Encoding, Machine);}}};void checkFormatAndArch(const DataForTest &D, StringRef Fmt,Triple::ArchType Arch) {Expected<std::unique_ptr<ObjectFile>> ELFObjOrErr =object::ObjectFile::createELFObjectFile(MemoryBufferRef(toStringRef(D.Data), "dummyELF"));ASSERT_THAT_EXPECTED(ELFObjOrErr, Succeeded());const ObjectFile &File = *(*ELFObjOrErr).get();EXPECT_EQ(Fmt, File.getFileFormatName());EXPECT_EQ(Arch, File.getArch());}std::array<DataForTest, 4> generateData(uint16_t Machine) {return {DataForTest(ELF::ELFCLASS32, ELF::ELFDATA2LSB, Machine),DataForTest(ELF::ELFCLASS32, ELF::ELFDATA2MSB, Machine),DataForTest(ELF::ELFCLASS64, ELF::ELFDATA2LSB, Machine),DataForTest(ELF::ELFCLASS64, ELF::ELFDATA2MSB, Machine)};}} // namespaceTEST(ELFObjectFileTest, MachineTestForNoneOrUnused) {std::array<StringRef, 4> Formats = {"elf32-unknown", "elf32-unknown","elf64-unknown", "elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_NONE))checkFormatAndArch(D, Formats[I++], Triple::UnknownArch);// Test an arbitrary unused EM_* value (255).I = 0;for (const DataForTest &D : generateData(255))checkFormatAndArch(D, Formats[I++], Triple::UnknownArch);}TEST(ELFObjectFileTest, MachineTestForVE) {std::array<StringRef, 4> Formats = {"elf32-unknown", "elf32-unknown","elf64-ve", "elf64-ve"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_VE))checkFormatAndArch(D, Formats[I++], Triple::ve);}TEST(ELFObjectFileTest, MachineTestForX86_64) {std::array<StringRef, 4> Formats = {"elf32-x86-64", "elf32-x86-64","elf64-x86-64", "elf64-x86-64"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_X86_64))checkFormatAndArch(D, Formats[I++], Triple::x86_64);}TEST(ELFObjectFileTest, MachineTestFor386) {std::array<StringRef, 4> Formats = {"elf32-i386", "elf32-i386", "elf64-i386","elf64-i386"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_386))checkFormatAndArch(D, Formats[I++], Triple::x86);}TEST(ELFObjectFileTest, MachineTestForMIPS) {std::array<StringRef, 4> Formats = {"elf32-mips", "elf32-mips", "elf64-mips","elf64-mips"};std::array<Triple::ArchType, 4> Archs = {Triple::mipsel, Triple::mips,Triple::mips64el, Triple::mips64};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_MIPS)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForAMDGPU) {std::array<StringRef, 4> Formats = {"elf32-amdgpu", "elf32-amdgpu","elf64-amdgpu", "elf64-amdgpu"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_AMDGPU))checkFormatAndArch(D, Formats[I++], Triple::UnknownArch);}TEST(ELFObjectFileTest, MachineTestForIAMCU) {std::array<StringRef, 4> Formats = {"elf32-iamcu", "elf32-iamcu","elf64-unknown", "elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_IAMCU))checkFormatAndArch(D, Formats[I++], Triple::x86);}TEST(ELFObjectFileTest, MachineTestForAARCH64) {std::array<StringRef, 4> Formats = {"elf32-unknown", "elf32-unknown","elf64-littleaarch64","elf64-bigaarch64"};std::array<Triple::ArchType, 4> Archs = {Triple::aarch64, Triple::aarch64_be,Triple::aarch64, Triple::aarch64_be};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_AARCH64)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForPPC64) {std::array<StringRef, 4> Formats = {"elf32-unknown", "elf32-unknown","elf64-powerpcle", "elf64-powerpc"};std::array<Triple::ArchType, 4> Archs = {Triple::ppc64le, Triple::ppc64,Triple::ppc64le, Triple::ppc64};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_PPC64)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForPPC) {std::array<StringRef, 4> Formats = {"elf32-powerpcle", "elf32-powerpc","elf64-unknown", "elf64-unknown"};std::array<Triple::ArchType, 4> Archs = {Triple::ppcle, Triple::ppc,Triple::ppcle, Triple::ppc};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_PPC)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForRISCV) {std::array<StringRef, 4> Formats = {"elf32-littleriscv", "elf32-littleriscv","elf64-littleriscv", "elf64-littleriscv"};std::array<Triple::ArchType, 4> Archs = {Triple::riscv32, Triple::riscv32,Triple::riscv64, Triple::riscv64};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_RISCV)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForARM) {std::array<StringRef, 4> Formats = {"elf32-littlearm", "elf32-bigarm","elf64-unknown", "elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_ARM))checkFormatAndArch(D, Formats[I++], Triple::arm);}TEST(ELFObjectFileTest, MachineTestForS390) {std::array<StringRef, 4> Formats = {"elf32-unknown", "elf32-unknown","elf64-s390", "elf64-s390"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_S390))checkFormatAndArch(D, Formats[I++], Triple::systemz);}TEST(ELFObjectFileTest, MachineTestForSPARCV9) {std::array<StringRef, 4> Formats = {"elf32-unknown", "elf32-unknown","elf64-sparc", "elf64-sparc"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_SPARCV9))checkFormatAndArch(D, Formats[I++], Triple::sparcv9);}TEST(ELFObjectFileTest, MachineTestForSPARC) {std::array<StringRef, 4> Formats = {"elf32-sparc", "elf32-sparc","elf64-unknown", "elf64-unknown"};std::array<Triple::ArchType, 4> Archs = {Triple::sparcel, Triple::sparc,Triple::sparcel, Triple::sparc};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_SPARC)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForSPARC32PLUS) {std::array<StringRef, 4> Formats = {"elf32-sparc", "elf32-sparc","elf64-unknown", "elf64-unknown"};std::array<Triple::ArchType, 4> Archs = {Triple::sparcel, Triple::sparc,Triple::sparcel, Triple::sparc};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_SPARC32PLUS)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForBPF) {std::array<StringRef, 4> Formats = {"elf32-unknown", "elf32-unknown","elf64-bpf", "elf64-bpf"};std::array<Triple::ArchType, 4> Archs = {Triple::bpfel, Triple::bpfeb,Triple::bpfel, Triple::bpfeb};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_BPF)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForAVR) {std::array<StringRef, 4> Formats = {"elf32-avr", "elf32-avr", "elf64-unknown","elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_AVR))checkFormatAndArch(D, Formats[I++], Triple::avr);}TEST(ELFObjectFileTest, MachineTestForHEXAGON) {std::array<StringRef, 4> Formats = {"elf32-hexagon", "elf32-hexagon","elf64-unknown", "elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_HEXAGON))checkFormatAndArch(D, Formats[I++], Triple::hexagon);}TEST(ELFObjectFileTest, MachineTestForLANAI) {std::array<StringRef, 4> Formats = {"elf32-lanai", "elf32-lanai","elf64-unknown", "elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_LANAI))checkFormatAndArch(D, Formats[I++], Triple::lanai);}TEST(ELFObjectFileTest, MachineTestForMSP430) {std::array<StringRef, 4> Formats = {"elf32-msp430", "elf32-msp430","elf64-unknown", "elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_MSP430))checkFormatAndArch(D, Formats[I++], Triple::msp430);}TEST(ELFObjectFileTest, MachineTestForLoongArch) {std::array<StringRef, 4> Formats = {"elf32-loongarch", "elf32-loongarch","elf64-loongarch", "elf64-loongarch"};std::array<Triple::ArchType, 4> Archs = {Triple::loongarch32, Triple::loongarch32, Triple::loongarch64,Triple::loongarch64};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_LOONGARCH)) {checkFormatAndArch(D, Formats[I], Archs[I]);++I;}}TEST(ELFObjectFileTest, MachineTestForCSKY) {std::array<StringRef, 4> Formats = {"elf32-csky", "elf32-csky","elf64-unknown", "elf64-unknown"};size_t I = 0;for (const DataForTest &D : generateData(ELF::EM_CSKY))checkFormatAndArch(D, Formats[I++], Triple::csky);}// ELF relative relocation type test.TEST(ELFObjectFileTest, RelativeRelocationTypeTest) {EXPECT_EQ(ELF::R_CKCORE_RELATIVE, getELFRelativeRelocationType(ELF::EM_CSKY));}template <class ELFT>static Expected<ELFObjectFile<ELFT>> toBinary(SmallVectorImpl<char> &Storage,StringRef Yaml) {raw_svector_ostream OS(Storage);yaml::Input YIn(Yaml);if (!yaml::convertYAML(YIn, OS, [](const Twine &Msg) {}))return createStringError(std::errc::invalid_argument,"unable to convert YAML");return ELFObjectFile<ELFT>::create(MemoryBufferRef(OS.str(), "dummyELF"));}// Check we are able to create an ELFObjectFile even when the content of the// SHT_SYMTAB_SHNDX section can't be read properly.TEST(ELFObjectFileTest, InvalidSymtabShndxTest) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ExpectedFile = toBinary<ELF64LE>(Storage, R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_RELSections:- Name: .symtab_shndxType: SHT_SYMTAB_SHNDXEntries: [ 0 ]ShSize: 0xFFFFFFFF)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());}// Test that we are able to create an ELFObjectFile even when loadable segments// are unsorted by virtual address.// Test that ELFFile<ELFT>::toMappedAddr works properly in this case.TEST(ELFObjectFileTest, InvalidLoadSegmentsOrderTest) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ExpectedFile = toBinary<ELF64LE>(Storage, R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_EXECSections:- Name: .fooType: SHT_PROGBITSAddress: 0x1000Offset: 0x3000ContentArray: [ 0x11 ]- Name: .barType: SHT_PROGBITSAddress: 0x2000Offset: 0x4000ContentArray: [ 0x99 ]ProgramHeaders:- Type: PT_LOADVAddr: 0x2000FirstSec: .barLastSec: .bar- Type: PT_LOADVAddr: 0x1000FirstSec: .fooLastSec: .foo)");ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());std::string WarnString;auto ToMappedAddr = [&](uint64_t Addr) -> const uint8_t * {Expected<const uint8_t *> DataOrErr =ExpectedFile->getELFFile().toMappedAddr(Addr, [&](const Twine &Msg) {EXPECT_TRUE(WarnString.empty());WarnString = Msg.str();return Error::success();});if (!DataOrErr) {ADD_FAILURE() << toString(DataOrErr.takeError());return nullptr;}EXPECT_TRUE(WarnString =="loadable segments are unsorted by virtual address");WarnString = "";return *DataOrErr;};const uint8_t *Data = ToMappedAddr(0x1000);ASSERT_TRUE(Data);MemoryBufferRef Buf = ExpectedFile->getMemoryBufferRef();EXPECT_EQ((const char *)Data - Buf.getBufferStart(), 0x3000);EXPECT_EQ(Data[0], 0x11);Data = ToMappedAddr(0x2000);ASSERT_TRUE(Data);Buf = ExpectedFile->getMemoryBufferRef();EXPECT_EQ((const char *)Data - Buf.getBufferStart(), 0x4000);EXPECT_EQ(Data[0], 0x99);}// This is a test for API that is related to symbols.// We check that errors are properly reported here.TEST(ELFObjectFileTest, InvalidSymbolTest) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ElfOrErr = toBinary<ELF64LE>(Storage, R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_DYNMachine: EM_X86_64Sections:- Name: .symtabType: SHT_SYMTAB)");ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded());const ELFFile<ELF64LE> &Elf = ElfOrErr->getELFFile();const ELFObjectFile<ELF64LE> &Obj = *ElfOrErr;Expected<const typename ELF64LE::Shdr *> SymtabSecOrErr = Elf.getSection(1);ASSERT_THAT_EXPECTED(SymtabSecOrErr, Succeeded());ASSERT_EQ((*SymtabSecOrErr)->sh_type, ELF::SHT_SYMTAB);auto DoCheck = [&](unsigned BrokenSymIndex, const char *ErrMsg) {ELFSymbolRef BrokenSym = Obj.toSymbolRef(*SymtabSecOrErr, BrokenSymIndex);// 1) Check the behavior of ELFObjectFile<ELFT>::getSymbolName().// SymbolRef::getName() calls it internally. We can't test it directly,// because it is protected.EXPECT_THAT_ERROR(BrokenSym.getName().takeError(),FailedWithMessage(ErrMsg));// 2) Check the behavior of ELFObjectFile<ELFT>::getSymbol().EXPECT_THAT_ERROR(Obj.getSymbol(BrokenSym.getRawDataRefImpl()).takeError(),FailedWithMessage(ErrMsg));// 3) Check the behavior of ELFObjectFile<ELFT>::getSymbolSection().// SymbolRef::getSection() calls it internally. We can't test it// directly, because it is protected.EXPECT_THAT_ERROR(BrokenSym.getSection().takeError(),FailedWithMessage(ErrMsg));// 4) Check the behavior of ELFObjectFile<ELFT>::getSymbolFlags().// SymbolRef::getFlags() calls it internally. We can't test it directly,// because it is protected.EXPECT_THAT_ERROR(BrokenSym.getFlags().takeError(),FailedWithMessage(ErrMsg));// 5) Check the behavior of ELFObjectFile<ELFT>::getSymbolType().// SymbolRef::getType() calls it internally. We can't test it directly,// because it is protected.EXPECT_THAT_ERROR(BrokenSym.getType().takeError(),FailedWithMessage(ErrMsg));// 6) Check the behavior of ELFObjectFile<ELFT>::getSymbolAddress().// SymbolRef::getAddress() calls it internally. We can't test it// directly, because it is protected.EXPECT_THAT_ERROR(BrokenSym.getAddress().takeError(),FailedWithMessage(ErrMsg));// Finally, check the `ELFFile<ELFT>::getEntry` API. This is an underlying// method that generates errors for all cases above.EXPECT_THAT_EXPECTED(Elf.getEntry<typename ELF64LE::Sym>(**SymtabSecOrErr, 0), Succeeded());EXPECT_THAT_ERROR(Elf.getEntry<typename ELF64LE::Sym>(**SymtabSecOrErr, BrokenSymIndex).takeError(),FailedWithMessage(ErrMsg));};// We create a symbol with an index that is too large to exist in the symbol// table.DoCheck(0x1, "can't read an entry at 0x18: it goes past the end of the ""section (0x18)");// We create a symbol with an index that is too large to exist in the object.DoCheck(0xFFFFFFFF, "can't read an entry at 0x17ffffffe8: it goes past the ""end of the section (0x18)");}// Tests for error paths of the ELFFile::decodeBBAddrMap API.TEST(ELFObjectFileTest, InvalidDecodeBBAddrMap) {StringRef CommonYamlString(R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_EXECSections:- Type: SHT_LLVM_BB_ADDR_MAPName: .llvm_bb_addr_mapEntries:- Address: 0x11111)");auto DoCheck = [&](StringRef YamlString, const char *ErrMsg) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ElfOrErr =toBinary<ELF64LE>(Storage, YamlString);ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded());const ELFFile<ELF64LE> &Elf = ElfOrErr->getELFFile();Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr =Elf.getSection(1);ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded());EXPECT_THAT_ERROR(Elf.decodeBBAddrMap(**BBAddrMapSecOrErr).takeError(),FailedWithMessage(ErrMsg));};// Check that we can detect unsupported versions.SmallString<128> UnsupportedVersionYamlString(CommonYamlString);UnsupportedVersionYamlString += R"(Version: 2BBEntries:- AddressOffset: 0x0Size: 0x1Metadata: 0x2)";DoCheck(UnsupportedVersionYamlString,"unsupported SHT_LLVM_BB_ADDR_MAP version: 2");SmallString<128> CommonVersionedYamlString(CommonYamlString);CommonVersionedYamlString += R"(Version: 1BBEntries:- AddressOffset: 0x0Size: 0x1Metadata: 0x2)";// Check that we can detect the malformed encoding when the section is// truncated.SmallString<128> TruncatedYamlString(CommonVersionedYamlString);TruncatedYamlString += R"(ShSize: 0xa)";DoCheck(TruncatedYamlString, "unable to decode LEB128 at offset 0x0000000a: ""malformed uleb128, extends past end");// Check that we can detect when the encoded BB entry fields exceed the UINT32// limit.SmallVector<SmallString<128>, 3> OverInt32LimitYamlStrings(3, CommonVersionedYamlString);OverInt32LimitYamlStrings[0] += R"(- AddressOffset: 0x100000000Size: 0xFFFFFFFFMetadata: 0xFFFFFFFF)";OverInt32LimitYamlStrings[1] += R"(- AddressOffset: 0xFFFFFFFFSize: 0x100000000Metadata: 0xFFFFFFFF)";OverInt32LimitYamlStrings[2] += R"(- AddressOffset: 0xFFFFFFFFSize: 0xFFFFFFFFMetadata: 0x100000000)";DoCheck(OverInt32LimitYamlStrings[0],"ULEB128 value at offset 0xe exceeds UINT32_MAX (0x100000000)");DoCheck(OverInt32LimitYamlStrings[1],"ULEB128 value at offset 0x13 exceeds UINT32_MAX (0x100000000)");DoCheck(OverInt32LimitYamlStrings[2],"ULEB128 value at offset 0x18 exceeds UINT32_MAX (0x100000000)");// Check the proper error handling when the section has fields exceeding// UINT32 and is also truncated. This is for checking that we don't generate// unhandled errors.SmallVector<SmallString<128>, 3> OverInt32LimitAndTruncated(3, OverInt32LimitYamlStrings[1]);// Truncate before the end of the 5-byte field.OverInt32LimitAndTruncated[0] += R"(ShSize: 0x17)";// Truncate at the end of the 5-byte field.OverInt32LimitAndTruncated[1] += R"(ShSize: 0x18)";// Truncate after the end of the 5-byte field.OverInt32LimitAndTruncated[2] += R"(ShSize: 0x19)";DoCheck(OverInt32LimitAndTruncated[0],"unable to decode LEB128 at offset 0x00000013: malformed uleb128, ""extends past end");DoCheck(OverInt32LimitAndTruncated[1],"ULEB128 value at offset 0x13 exceeds UINT32_MAX (0x100000000)");DoCheck(OverInt32LimitAndTruncated[2],"ULEB128 value at offset 0x13 exceeds UINT32_MAX (0x100000000)");// Check for proper error handling when the 'NumBlocks' field is overridden// with an out-of-range value.SmallString<128> OverLimitNumBlocks(CommonVersionedYamlString);OverLimitNumBlocks += R"(NumBlocks: 0x100000000)";DoCheck(OverLimitNumBlocks,"ULEB128 value at offset 0xa exceeds UINT32_MAX (0x100000000)");}// Test for the ELFObjectFile::readBBAddrMap API.TEST(ELFObjectFileTest, ReadBBAddrMap) {StringRef CommonYamlString(R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_EXECSections:- Name: .llvm_bb_addr_map_1Type: SHT_LLVM_BB_ADDR_MAPLink: 1Entries:- Version: 1Address: 0x11111BBEntries:- AddressOffset: 0x0Size: 0x1Metadata: 0x2- Name: .llvm_bb_addr_map_2Type: SHT_LLVM_BB_ADDR_MAPLink: 1Entries:- Version: 1Address: 0x22222BBEntries:- AddressOffset: 0x0Size: 0x2Metadata: 0x4- Name: .llvm_bb_addr_mapType: SHT_LLVM_BB_ADDR_MAP_V0# Link: 0 (by default)Entries:- Version: 0Address: 0x33333BBEntries:- AddressOffset: 0x0Size: 0x3Metadata: 0x6)");BBAddrMap E1 = {0x11111, {{0x0, 0x1, 0x2}}};BBAddrMap E2 = {0x22222, {{0x0, 0x2, 0x4}}};BBAddrMap E3 = {0x33333, {{0x0, 0x3, 0x6}}};std::vector<BBAddrMap> Section0BBAddrMaps = {E3};std::vector<BBAddrMap> Section1BBAddrMaps = {E1, E2};std::vector<BBAddrMap> AllBBAddrMaps = {E1, E2, E3};auto DoCheckSucceeds = [&](StringRef YamlString,Optional<unsigned> TextSectionIndex,std::vector<BBAddrMap> ExpectedResult) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ElfOrErr =toBinary<ELF64LE>(Storage, YamlString);ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded());Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr =ElfOrErr->getELFFile().getSection(1);ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded());auto BBAddrMaps = ElfOrErr->readBBAddrMap(TextSectionIndex);EXPECT_THAT_EXPECTED(BBAddrMaps, Succeeded());EXPECT_EQ(*BBAddrMaps, ExpectedResult);};auto DoCheckFails = [&](StringRef YamlString,Optional<unsigned> TextSectionIndex,const char *ErrMsg) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ElfOrErr =toBinary<ELF64LE>(Storage, YamlString);ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded());Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr =ElfOrErr->getELFFile().getSection(1);ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded());EXPECT_THAT_ERROR(ElfOrErr->readBBAddrMap(TextSectionIndex).takeError(),FailedWithMessage(ErrMsg));};// Check that we can retrieve the data in the normal case.DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/None, AllBBAddrMaps);DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/0, Section0BBAddrMaps);DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/1, Section1BBAddrMaps);// Check that when no bb-address-map section is found for a text section,// we return an empty result.DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/2, {});// Check that we detect when a bb-addr-map section is linked to an invalid// (not present) section.SmallString<128> InvalidLinkedYamlString(CommonYamlString);InvalidLinkedYamlString += R"(Link: 10)";DoCheckFails(InvalidLinkedYamlString, /*TextSectionIndex=*/1,"unable to get the linked-to section for ""SHT_LLVM_BB_ADDR_MAP_V0 section with index 3: invalid section ""index: 10");// Linked sections are not checked when we don't target a specific text// section.DoCheckSucceeds(InvalidLinkedYamlString, /*TextSectionIndex=*/None,AllBBAddrMaps);// Check that we can detect when bb-address-map decoding fails.SmallString<128> TruncatedYamlString(CommonYamlString);TruncatedYamlString += R"(ShSize: 0x8)";DoCheckFails(TruncatedYamlString, /*TextSectionIndex=*/None,"unable to read SHT_LLVM_BB_ADDR_MAP_V0 section with index 3: ""unable to decode LEB128 at offset 0x00000008: malformed ""uleb128, extends past end");// Check that we can read the other section's bb-address-maps which are// valid.DoCheckSucceeds(TruncatedYamlString, /*TextSectionIndex=*/1,Section1BBAddrMaps);}// Test for ObjectFile::getRelocatedSection: check that it returns a relocated// section for executable and relocatable files.TEST(ELFObjectFileTest, ExecutableWithRelocs) {StringRef HeaderString(R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSB)");StringRef ContentsString(R"(Sections:- Name: .textType: SHT_PROGBITSFlags: [ SHF_ALLOC, SHF_EXECINSTR ]- Name: .rela.textType: SHT_RELAFlags: [ SHF_INFO_LINK ]Info: .text)");auto DoCheck = [&](StringRef YamlString) {SmallString<0> Storage;Expected<ELFObjectFile<ELF64LE>> ElfOrErr =toBinary<ELF64LE>(Storage, YamlString);ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded());const ELFObjectFile<ELF64LE> &Obj = *ElfOrErr;bool FoundRela;for (SectionRef Sec : Obj.sections()) {Expected<StringRef> SecNameOrErr = Sec.getName();ASSERT_THAT_EXPECTED(SecNameOrErr, Succeeded());StringRef SecName = *SecNameOrErr;if (SecName != ".rela.text")continue;FoundRela = true;Expected<section_iterator> RelSecOrErr = Sec.getRelocatedSection();ASSERT_THAT_EXPECTED(RelSecOrErr, Succeeded());section_iterator RelSec = *RelSecOrErr;ASSERT_NE(RelSec, Obj.section_end());Expected<StringRef> TextSecNameOrErr = RelSec->getName();ASSERT_THAT_EXPECTED(TextSecNameOrErr, Succeeded());StringRef TextSecName = *TextSecNameOrErr;EXPECT_EQ(TextSecName, ".text");}ASSERT_TRUE(FoundRela);};// Check ET_EXEC file (`ld --emit-relocs` use-case).SmallString<128> ExecFileYamlString(HeaderString);ExecFileYamlString += R"(Type: ET_EXEC)";ExecFileYamlString += ContentsString;DoCheck(ExecFileYamlString);// Check ET_REL file.SmallString<128> RelocatableFileYamlString(HeaderString);RelocatableFileYamlString += R"(Type: ET_REL)";RelocatableFileYamlString += ContentsString;DoCheck(RelocatableFileYamlString);}
//===- DXContainerTest.cpp - Tests for DXContainerFile --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/DXContainer.h"#include "llvm/ADT/StringRef.h"#include "llvm/BinaryFormat/Magic.h"#include "llvm/Support/MemoryBufferRef.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::object;template <std::size_t X> MemoryBufferRef getMemoryBuffer(uint8_t Data[X]) {StringRef Obj(reinterpret_cast<char *>(&Data[0]), X);return MemoryBufferRef(Obj, "");}TEST(DXCFile, IdentifyMagic) {{StringRef Buffer("DXBC");EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object);}{StringRef Buffer("DXBCBlahBlahBlah");EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object);}}TEST(DXCFile, ParseHeaderErrors) {uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43};EXPECT_THAT_EXPECTED(DXContainer::create(getMemoryBuffer<4>(Buffer)),FailedWithMessage("Reading structure out of file bounds"));}TEST(DXCFile, EmptyFile) {EXPECT_THAT_EXPECTED(DXContainer::create(MemoryBufferRef(StringRef("", 0), "")),FailedWithMessage("Reading structure out of file bounds"));}TEST(DXCFile, ParseHeader) {uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,0x70, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};DXContainer C =llvm::cantFail(DXContainer::create(getMemoryBuffer<32>(Buffer)));EXPECT_TRUE(memcmp(C.getHeader().Magic, "DXBC", 4) == 0);EXPECT_TRUE(memcmp(C.getHeader().FileHash.Digest,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0);EXPECT_EQ(C.getHeader().Version.Major, 1u);EXPECT_EQ(C.getHeader().Version.Minor, 0u);}TEST(DXCFile, ParsePartMissingOffsets) {uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,0x00, 0x00, 0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,};EXPECT_THAT_EXPECTED(DXContainer::create(getMemoryBuffer<32>(Buffer)),FailedWithMessage("Reading structure out of file bounds"));}TEST(DXCFile, ParsePartInvalidOffsets) {uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,};EXPECT_THAT_EXPECTED(DXContainer::create(getMemoryBuffer<36>(Buffer)),FailedWithMessage("Part offset points beyond boundary of the file"));}TEST(DXCFile, ParseEmptyParts) {uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,0x70, 0x0D, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,0x44, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,0x5C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,0x53, 0x46, 0x49, 0x30, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31,0x00, 0x00, 0x00, 0x00, 0x4F, 0x53, 0x47, 0x31, 0x00, 0x00, 0x00, 0x00,0x50, 0x53, 0x56, 0x30, 0x00, 0x00, 0x00, 0x00, 0x53, 0x54, 0x41, 0x54,0x00, 0x00, 0x00, 0x00, 0x43, 0x58, 0x49, 0x4C, 0x00, 0x00, 0x00, 0x00,0x44, 0x45, 0x41, 0x44, 0x00, 0x00, 0x00, 0x00,};DXContainer C =llvm::cantFail(DXContainer::create(getMemoryBuffer<116>(Buffer)));EXPECT_EQ(C.getHeader().PartCount, 7u);// All the part sizes are 0, which makes a nice test of the range based forint ElementsVisited = 0;for (auto Part : C) {EXPECT_EQ(Part.Part.Size, 0u);EXPECT_EQ(Part.Data.size(), 0u);++ElementsVisited;}EXPECT_EQ(ElementsVisited, 7);{auto It = C.begin();EXPECT_TRUE(memcmp(It->Part.Name, "SFI0", 4) == 0);++It;EXPECT_TRUE(memcmp(It->Part.Name, "ISG1", 4) == 0);++It;EXPECT_TRUE(memcmp(It->Part.Name, "OSG1", 4) == 0);++It;EXPECT_TRUE(memcmp(It->Part.Name, "PSV0", 4) == 0);++It;EXPECT_TRUE(memcmp(It->Part.Name, "STAT", 4) == 0);++It;EXPECT_TRUE(memcmp(It->Part.Name, "CXIL", 4) == 0);++It;EXPECT_TRUE(memcmp(It->Part.Name, "DEAD", 4) == 0);++It; // Don't increment past the endEXPECT_TRUE(memcmp(It->Part.Name, "DEAD", 4) == 0);}}
set(LLVM_LINK_COMPONENTSBinaryFormatObjectObjectYAML)add_llvm_unittest(ObjectTestsArchiveTest.cppDXContainerTest.cppELFObjectFileTest.cppELFTypesTest.cppELFTest.cppMinidumpTest.cppObjectFileTest.cppOffloadingTest.cppSymbolSizeTest.cppSymbolicFileTest.cppXCOFFObjectFileTest.cpp)target_link_libraries(ObjectTests PRIVATE LLVMTestingSupport)
//===- ArchiveTest.cpp ----------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Object/Archive.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace object;using namespace testing;static const char ArchiveWithMember[] = "!<arch>\n" // Global header"test/ " // Member name"0 " // Timestamp"0 " // Owner ID"0 " // Group ID"0 " // File mode"9999999999" // Size"`\n";static const char ThinArchiveWithMember[] = "!<thin>\n" // Global header"test/ " // Member name"0 " // Timestamp"0 " // Owner ID"0 " // Group ID"0 " // File mode"9999999999" // Size"`\n";static const uint64_t MemberSize = 9999999999ull;struct ArchiveTestsFixture : Test {Expected<Archive::child_iterator> createChild(StringRef Src) {MemoryBufferRef Source(Src, "archive");Expected<std::unique_ptr<Archive>> AOrErr = Archive::create(Source);if (!AOrErr)return AOrErr.takeError();A = std::move(*AOrErr);Error ChildErr = Error::success();auto Child = A->child_begin(ChildErr);if (ChildErr)return std::move(ChildErr);return Child;}std::unique_ptr<Archive> A;};TEST_F(ArchiveTestsFixture, ArchiveChildGetSizeRegularArchive) {// This test relies on a StringRef being able to hold the appropriate amount// of data.if (std::numeric_limits<StringRef::size_type>::max() < MemberSize)return;auto Child = createChild(ArchiveWithMember);ASSERT_THAT_EXPECTED(Child, Succeeded());Expected<uint64_t> Size = (*Child)->getSize();ASSERT_THAT_EXPECTED(Size, Succeeded());EXPECT_EQ(MemberSize, *Size);}TEST_F(ArchiveTestsFixture, ArchiveChildGetSizeThinArchive) {auto Child = createChild(ThinArchiveWithMember);ASSERT_THAT_EXPECTED(Child, Succeeded());Expected<uint64_t> Size = (*Child)->getSize();ASSERT_THAT_EXPECTED(Size, Succeeded());EXPECT_EQ(MemberSize, *Size);}TEST_F(ArchiveTestsFixture, ArchiveChildGetBuffer) {// This test relies on a StringRef being able to hold the appropriate amount// of data.if (std::numeric_limits<StringRef::size_type>::max() < MemberSize)return;auto Child = createChild(ArchiveWithMember);ASSERT_THAT_EXPECTED(Child, Succeeded());Expected<StringRef> Buffer = (*Child)->getBuffer();// Cannot use ASSERT_THAT_EXPECTED, as that will attempt to print the// StringRef (which has an invalid size).ASSERT_TRUE((bool)Buffer);EXPECT_EQ(MemberSize, Buffer->size());EXPECT_EQ(ArchiveWithMember + sizeof(ArchiveWithMember) - 1, Buffer->data());}
//===- ObjCopyTest.cpp ----------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ObjCopy/ObjCopy.h"#include "llvm/ADT/SmallString.h"#include "llvm/ObjCopy/ConfigManager.h"#include "llvm/Object/ObjectFile.h"#include "llvm/ObjectYAML/yaml2obj.h"#include "llvm/Support/Error.h"#include "llvm/Support/FileUtilities.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace object;using namespace objcopy;using namespace yaml;const char *SimpleFileCOFFYAML = R"(--- !COFFheader:Machine: IMAGE_FILE_MACHINE_AMD64Characteristics: [ ]sections:- Name: .textCharacteristics: [ ]Alignment: 4SectionData: E800000000C3C3C3symbols:...)";const char *SimpleFileELFYAML = R"(--- !ELFFileHeader:Class: ELFCLASS64Data: ELFDATA2LSBType: ET_RELSections:- Name: .textType: SHT_PROGBITSFlags: [ SHF_ALLOC ]Content: "12345678")";const char *SimpleFileMachOYAML = R"(--- !mach-oFileHeader:magic: 0xFEEDFACFcputype: 0x01000007cpusubtype: 0x80000003filetype: 0x00000001ncmds: 1sizeofcmds: 152flags: 0x00002000reserved: 0x00000000LoadCommands:- cmd: LC_SEGMENT_64cmdsize: 152segname: __TEXTvmaddr: 0vmsize: 4fileoff: 184filesize: 4maxprot: 7initprot: 7nsects: 1flags: 0Sections:- sectname: __textsegname: __TEXTaddr: 0x0000000000000000content: 'AABBCCDD'size: 4offset: 184align: 0reloff: 0x00000000nreloc: 0flags: 0x80000400reserved1: 0x00000000reserved2: 0x00000000reserved3: 0x00000000...)";const char *SimpleFileWasmYAML = R"(--- !WASMFileHeader:Version: 0x00000001Sections:- Type: CUSTOMName: textPayload: ABC123...)";// Create ObjectFile from \p YamlCreationString and do validation using \p// IsValidFormat checker. \p Storage is a storage for data. \returns created// ObjectFile.Expected<std::unique_ptr<ObjectFile>> createObjectFileFromYamlDescription(const char *YamlCreationString, SmallVector<char> &Storage,function_ref<bool(const Binary &File)> IsValidFormat) {auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; };std::unique_ptr<ObjectFile> Obj =yaml2ObjectFile(Storage, YamlCreationString, ErrHandler);if (!Obj)return createError("could not create ObjectFile from yaml description");if (!IsValidFormat(*Obj))return createError("wrong file format");return std::move(Obj);}// Call objcopy::executeObjcopyOnBinary for \p Config and \p In. \p DataVector// is a holder for data. \returns Binary for copied data.Expected<std::unique_ptr<Binary>>callObjCopy(ConfigManager &Config, object::Binary &In,SmallVector<char> &DataVector,function_ref<bool(const Binary &File)> IsValidFormat) {raw_svector_ostream OutStream(DataVector);if (Error Err = objcopy::executeObjcopyOnBinary(Config, In, OutStream))return std::move(Err);MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()),Config.Common.OutputFilename);Expected<std::unique_ptr<Binary>> Result = createBinary(Buffer);// Check copied file.if (!Result)return Result;if (!IsValidFormat(**Result))return createError("wrong file format");if (!(*Result)->isObject())return createError("binary is not object file");return Result;}// \returns true if specified \p File has a section named \p SectionName.bool hasSection(ObjectFile &File, StringRef SectionName) {for (const object::SectionRef &Sec : File.sections()) {Expected<StringRef> CurSecNameOrErr = Sec.getName();if (!CurSecNameOrErr)continue;if (*CurSecNameOrErr == SectionName)return true;}return false;}// Check that specified \p File has a section \p SectionName and its data// matches \p SectionData.void checkSectionData(ObjectFile &File, StringRef SectionName,StringRef SectionData) {for (const object::SectionRef &Sec : File.sections()) {Expected<StringRef> CurSecNameOrErr = Sec.getName();ASSERT_THAT_EXPECTED(CurSecNameOrErr, Succeeded());if (*CurSecNameOrErr == SectionName) {Expected<StringRef> CurSectionData = Sec.getContents();ASSERT_THAT_EXPECTED(CurSectionData, Succeeded());EXPECT_TRUE(Sec.getSize() == SectionData.size());EXPECT_TRUE(memcmp(CurSectionData->data(), SectionData.data(),SectionData.size()) == 0);return;}}// Section SectionName must be presented.EXPECT_TRUE(false);}void copySimpleInMemoryFileImpl(const char *YamlCreationString,function_ref<bool(const Binary &File)> IsValidFormat) {SCOPED_TRACE("copySimpleInMemoryFileImpl");// Create Object file from YAML description.SmallVector<char> Storage;Expected<std::unique_ptr<ObjectFile>> Obj =createObjectFileFromYamlDescription(YamlCreationString, Storage,IsValidFormat);ASSERT_THAT_EXPECTED(Obj, Succeeded());ConfigManager Config;Config.Common.OutputFilename = "a.out";// Call executeObjcopyOnBinary()SmallVector<char> DataVector;Expected<std::unique_ptr<Binary>> Result =callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);ASSERT_THAT_EXPECTED(Result, Succeeded());}TEST(CopySimpleInMemoryFile, COFF) {SCOPED_TRACE("CopySimpleInMemoryFileCOFF");copySimpleInMemoryFileImpl(SimpleFileCOFFYAML,[](const Binary &File) { return File.isCOFF(); });}TEST(CopySimpleInMemoryFile, ELF) {SCOPED_TRACE("CopySimpleInMemoryFileELF");copySimpleInMemoryFileImpl(SimpleFileELFYAML,[](const Binary &File) { return File.isELF(); });}TEST(CopySimpleInMemoryFile, MachO) {SCOPED_TRACE("CopySimpleInMemoryFileMachO");copySimpleInMemoryFileImpl(SimpleFileMachOYAML,[](const Binary &File) { return File.isMachO(); });}TEST(CopySimpleInMemoryFile, Wasm) {SCOPED_TRACE("CopySimpleInMemoryFileWasm");copySimpleInMemoryFileImpl(SimpleFileWasmYAML,[](const Binary &File) { return File.isWasm(); });}enum Action : uint8_t { AddSection, UpdateSection };void addOrUpdateSectionToFileImpl(const char *YamlCreationString,function_ref<bool(const Binary &File)> IsValidFormat,StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) {SCOPED_TRACE("addOrUpdateSectionToFileImpl");// Create Object file from YAML description.SmallVector<char> Storage;Expected<std::unique_ptr<ObjectFile>> Obj =createObjectFileFromYamlDescription(YamlCreationString, Storage,IsValidFormat);ASSERT_THAT_EXPECTED(Obj, Succeeded());std::unique_ptr<MemoryBuffer> NewSectionBuffer =MemoryBuffer::getMemBuffer(NewSectionData, NewSectionName, false);std::string Name;if ((*Obj)->isMachO())Name = "__TEXT," + NewSectionName.str();elseName = NewSectionName.str();ConfigManager Config;Config.Common.OutputFilename = "a.out";if (SectionAction == AddSection)Config.Common.AddSection.push_back({Name, std::move(NewSectionBuffer)});elseConfig.Common.UpdateSection.push_back({Name, std::move(NewSectionBuffer)});// Call executeObjcopyOnBinary()SmallVector<char> DataVector;Expected<std::unique_ptr<Binary>> Result =callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);ASSERT_THAT_EXPECTED(Result, Succeeded());// Check that copied file has the new section.checkSectionData(*static_cast<ObjectFile *>((*Result).get()), NewSectionName,NewSectionData);}TEST(AddSection, COFF) {SCOPED_TRACE("addSectionToFileCOFF");addOrUpdateSectionToFileImpl(SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); },".foo", "1234", AddSection);}TEST(AddSection, ELF) {SCOPED_TRACE("addSectionToFileELF");addOrUpdateSectionToFileImpl(SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); },".foo", "1234", AddSection);}TEST(AddSection, MachO) {SCOPED_TRACE("addSectionToFileMachO");addOrUpdateSectionToFileImpl(SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); },"__foo", "1234", AddSection);}TEST(AddSection, Wasm) {SCOPED_TRACE("addSectionToFileWasm");addOrUpdateSectionToFileImpl(SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); },".foo", "1234", AddSection);}TEST(UpdateSection, COFF) {SCOPED_TRACE("updateSectionToFileCOFF");addOrUpdateSectionToFileImpl(SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); },".text", "1234", UpdateSection);}TEST(UpdateSection, ELF) {SCOPED_TRACE("updateSectionToFileELF");addOrUpdateSectionToFileImpl(SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); },".text", "1234", UpdateSection);}TEST(UpdateSection, MachO) {SCOPED_TRACE("updateSectionToFileMachO");addOrUpdateSectionToFileImpl(SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); },"__text", "1234", UpdateSection);}void removeSectionByPatternImpl(const char *YamlCreationString,function_ref<bool(const Binary &File)> IsValidFormat,StringRef SectionWildcard, StringRef SectionName) {SCOPED_TRACE("removeSectionByPatternImpl");// Create Object file from YAML description.SmallVector<char> Storage;Expected<std::unique_ptr<ObjectFile>> Obj =createObjectFileFromYamlDescription(YamlCreationString, Storage,IsValidFormat);ASSERT_THAT_EXPECTED(Obj, Succeeded());// Check that section is present.EXPECT_TRUE(hasSection(**Obj, SectionName));Expected<NameOrPattern> Pattern = objcopy::NameOrPattern::create(SectionWildcard, objcopy::MatchStyle::Wildcard,[](Error Err) { return Err; });ConfigManager Config;Config.Common.OutputFilename = "a.out";EXPECT_THAT_ERROR(Config.Common.ToRemove.addMatcher(std::move(Pattern)),Succeeded());SmallVector<char> DataVector;Expected<std::unique_ptr<Binary>> Result =callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);ASSERT_THAT_EXPECTED(Result, Succeeded());// Check that section was removed.EXPECT_FALSE(hasSection(*static_cast<ObjectFile *>((*Result).get()), SectionName));}TEST(RemoveSectionByPattern, COFF) {SCOPED_TRACE("removeSectionByPatternCOFF");removeSectionByPatternImpl(SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); },"\\.text*", ".text");}TEST(RemoveSectionByPattern, ELF) {SCOPED_TRACE("removeSectionByPatternELF");removeSectionByPatternImpl(SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); },"\\.text*", ".text");}TEST(RemoveSectionByPattern, MachO) {SCOPED_TRACE("removeSectionByPatternMachO");removeSectionByPatternImpl(SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); },"__TEXT,__text*", "__text");}TEST(RemoveSectionByPattern, Wasm) {SCOPED_TRACE("removeSectionByPatternWasm");removeSectionByPatternImpl(SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); },"text*", "text");}
set(LLVM_LINK_COMPONENTSObjectObjCopyObjectYAML)add_llvm_unittest(ObjCopyTestsObjCopyTest.cpp)target_link_libraries(ObjCopyTests PRIVATE LLVMTestingSupport)
//===- MachineInstrBundleIteratorTest.cpp ---------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MIRPrinter.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineMemOperand.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/MachineModuleSlotTracker.h"#include "llvm/CodeGen/MachineOperand.h"#include "llvm/CodeGen/TargetFrameLowering.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/FileCheck/FileCheck.h"#include "llvm/IR/MDBuilder.h"#include "llvm/IR/ModuleSlotTracker.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"using namespace llvm;class MachineMetadataTest : public testing::Test {public:MachineMetadataTest() {}protected:LLVMContext Context;std::unique_ptr<Module> M;std::unique_ptr<MIRParser> MIR;static void SetUpTestCase() {InitializeAllTargetInfos();InitializeAllTargets();InitializeAllTargetMCs();}void SetUp() override { M = std::make_unique<Module>("Dummy", Context); }void addHooks(ModuleSlotTracker &MST, const MachineOperand &MO) {// Setup hooks to assign slot numbers for the specified machine metadata.MST.setProcessHook([&MO](AbstractSlotTrackerStorage *AST, const Module *M,bool ShouldInitializeAllMetadata) {if (ShouldInitializeAllMetadata) {if (MO.isMetadata())AST->createMetadataSlot(MO.getMetadata());}});MST.setProcessHook([&MO](AbstractSlotTrackerStorage *AST, const Function *F,bool ShouldInitializeAllMetadata) {if (!ShouldInitializeAllMetadata) {if (MO.isMetadata())AST->createMetadataSlot(MO.getMetadata());}});}std::unique_ptr<LLVMTargetMachine>createTargetMachine(std::string TT, StringRef CPU, StringRef FS) {std::string Error;const Target *T = TargetRegistry::lookupTarget(TT, Error);if (!T)return nullptr;TargetOptions Options;return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine(TT, CPU, FS, Options, None, None)));}std::unique_ptr<Module> parseMIR(const TargetMachine &TM, StringRef MIRCode,const char *FnName, MachineModuleInfo &MMI) {SMDiagnostic Diagnostic;std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);MIR = createMIRParser(std::move(MBuffer), Context);if (!MIR)return nullptr;std::unique_ptr<Module> Mod = MIR->parseIRModule();if (!Mod)return nullptr;Mod->setDataLayout(TM.createDataLayout());if (MIR->parseMachineFunctions(*Mod, MMI)) {M.reset();return nullptr;}return Mod;}};// Helper to dump the printer output into a string.static std::string print(std::function<void(raw_ostream &OS)> PrintFn) {std::string Str;raw_string_ostream OS(Str);PrintFn(OS);OS.flush();return Str;}TEST_F(MachineMetadataTest, TrivialHook) {// Verify that post-process hook is invoked to assign slot numbers for// machine metadata.ASSERT_TRUE(M);// Create a MachineOperand with a metadata and print it.Metadata *MDS = MDString::get(Context, "foo");MDNode *Node = MDNode::get(Context, MDS);MachineOperand MO = MachineOperand::CreateMetadata(Node);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isMetadata());ASSERT_EQ(MO.getMetadata(), Node);ModuleSlotTracker MST(M.get());addHooks(MST, MO);// Print a MachineOperand containing a metadata node.EXPECT_EQ("!0", print([&](raw_ostream &OS) {MO.print(OS, MST, LLT{}, /*OpIdx*/ ~0U, /*PrintDef=*/false,/*IsStandalone=*/false,/*ShouldPrintRegisterTies=*/false, /*TiedOperandIdx=*/0,/*TRI=*/nullptr,/*IntrinsicInfo=*/nullptr);}));// Print the definition of that metadata node.EXPECT_EQ("!0 = !{!\"foo\"}",print([&](raw_ostream &OS) { Node->print(OS, MST); }));}TEST_F(MachineMetadataTest, BasicHook) {// Verify that post-process hook is invoked to assign slot numbers for// machine metadata. When both LLVM IR and machine IR contain metadata,// ensure that machine metadata is always assigned after LLVM IR.ASSERT_TRUE(M);// Create a MachineOperand with a metadata and print it.Metadata *MachineMDS = MDString::get(Context, "foo");MDNode *MachineNode = MDNode::get(Context, MachineMDS);MachineOperand MO = MachineOperand::CreateMetadata(MachineNode);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isMetadata());ASSERT_EQ(MO.getMetadata(), MachineNode);// Create metadata in LLVM IR.NamedMDNode *MD = M->getOrInsertNamedMetadata("namedmd");Metadata *MDS = MDString::get(Context, "bar");MDNode *Node = MDNode::get(Context, MDS);MD->addOperand(Node);ModuleSlotTracker MST(M.get());addHooks(MST, MO);// Print a MachineOperand containing a metadata node.EXPECT_EQ("!1", print([&](raw_ostream &OS) {MO.print(OS, MST, LLT{}, /*OpIdx*/ ~0U, /*PrintDef=*/false,/*IsStandalone=*/false,/*ShouldPrintRegisterTies=*/false, /*TiedOperandIdx=*/0,/*TRI=*/nullptr,/*IntrinsicInfo=*/nullptr);}));// Print the definition of these unnamed metadata nodes.EXPECT_EQ("!0 = !{!\"bar\"}",print([&](raw_ostream &OS) { Node->print(OS, MST); }));EXPECT_EQ("!1 = !{!\"foo\"}",print([&](raw_ostream &OS) { MachineNode->print(OS, MST); }));}static bool checkOutput(std::string CheckString, std::string Output) {auto CheckBuffer = MemoryBuffer::getMemBuffer(CheckString, "");auto OutputBuffer = MemoryBuffer::getMemBuffer(Output, "Output", false);SmallString<4096> CheckFileBuffer;FileCheckRequest Req;FileCheck FC(Req);StringRef CheckFileText =FC.CanonicalizeFile(*CheckBuffer.get(), CheckFileBuffer);SourceMgr SM;SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(CheckFileText, "CheckFile"),SMLoc());Regex PrefixRE = FC.buildCheckPrefixRegex();if (FC.readCheckFile(SM, CheckFileText, PrefixRE))return false;auto OutBuffer = OutputBuffer->getBuffer();SM.AddNewSourceBuffer(std::move(OutputBuffer), SMLoc());return FC.checkInput(SM, OutBuffer);}TEST_F(MachineMetadataTest, MMSlotTrackerAArch64) {auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");if (!TM)GTEST_SKIP();StringRef MIRString = R"MIR(--- |define i32 @test0(i32* %p) {%r = load i32, i32* %p, align 4ret i32 %r}...---name: test0liveins:- { reg: '$x0', virtual-reg: '%0' }body: |bb.0 (%ir-block.0):liveins: $x0%0:gpr64common = COPY $x0%1:gpr32 = LDRWui %0, 0 :: (load (s32) from %ir.p)...)MIR";MachineModuleInfo MMI(TM.get());M = parseMIR(*TM, MIRString, "test0", MMI);ASSERT_TRUE(M);auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));auto *MBB = MF->getBlockNumbered(0);auto &MI = MBB->back();ASSERT_TRUE(MI.hasOneMemOperand());// Create and attached scoped AA metadata on that instruction with one MMO.MDBuilder MDB(Context);MDNode *Domain = MDB.createAnonymousAliasScopeDomain("domain");MDNode *Scope0 = MDB.createAnonymousAliasScope(Domain, "scope0");MDNode *Scope1 = MDB.createAnonymousAliasScope(Domain, "scope1");MDNode *Set0 = MDNode::get(Context, {Scope0});MDNode *Set1 = MDNode::get(Context, {Scope1});AAMDNodes AAInfo;AAInfo.TBAA = AAInfo.TBAAStruct = nullptr;AAInfo.Scope = Set0;AAInfo.NoAlias = Set1;auto *OldMMO = MI.memoperands().front();auto *NewMMO = MF->getMachineMemOperand(OldMMO, AAInfo);MI.setMemRefs(*MF, NewMMO);MachineModuleSlotTracker MST(MF);// Print that MI with new machine metadata, which slot numbers should be// assigned.EXPECT_EQ("%1:gpr32 = LDRWui %0, 0 :: (load (s32) from %ir.p, ""!alias.scope !0, !noalias !3)",print([&](raw_ostream &OS) {MI.print(OS, MST, /*IsStandalone=*/false, /*SkipOpers=*/false,/*SkipDebugLoc=*/false, /*AddNewLine=*/false);}));std::vector<const MDNode *> Generated{Domain, Scope0, Scope1, Set0, Set1};// Examine machine metadata collected. They should match ones// afore-generated.std::vector<const MDNode *> Collected;MachineModuleSlotTracker::MachineMDNodeListType MDList;MST.collectMachineMDNodes(MDList);for (auto &MD : MDList)Collected.push_back(MD.second);llvm::sort(Generated);llvm::sort(Collected);EXPECT_EQ(Collected, Generated);// FileCheck the output from MIR printer.std::string Output = print([&](raw_ostream &OS) { printMIR(OS, *MF); });std::string CheckString = R"(CHECK: machineMetadataNodes:CHECK-DAG: ![[MMDOMAIN:[0-9]+]] = distinct !{!{{[0-9]+}}, !"domain"}CHECK-DAG: ![[MMSCOPE0:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope0"}CHECK-DAG: ![[MMSCOPE1:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope1"}CHECK-DAG: ![[MMSET0:[0-9]+]] = !{![[MMSCOPE0]]}CHECK-DAG: ![[MMSET1:[0-9]+]] = !{![[MMSCOPE1]]}CHECK: body:CHECK: %1:gpr32 = LDRWui %0, 0 :: (load (s32) from %ir.p, !alias.scope ![[MMSET0]], !noalias ![[MMSET1]]))";EXPECT_TRUE(checkOutput(CheckString, Output));}TEST_F(MachineMetadataTest, isMetaInstruction) {auto TM = createTargetMachine(Triple::normalize("x86_64--"), "", "");if (!TM)GTEST_SKIP();StringRef MIRString = R"MIR(--- |define void @test0(i32 %b) {ret void}!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)!1 = !DIFile(filename: "a.c", directory: "/tmp")!2 = !{i32 7, !"Dwarf Version", i32 4}!3 = !{i32 2, !"Debug Info Version", i32 3}!4 = !{i32 1, !"wchar_size", i32 4}!5 = !{i32 7, !"uwtable", i32 1}!6 = !{i32 7, !"frame-pointer", i32 2}!7 = !{!""}!8 = distinct !DISubprogram(name: "test0", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !12)!9 = !DISubroutineType(types: !10)!10 = !{null, !11}!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)!12 = !{}!13 = !DILocalVariable(name: "b", arg: 1, scope: !8, file: !1, line: 1, type: !11)!14 = !DILocation(line: 1, column: 16, scope: !8)...---name: test0machineFunctionInfobody: |bb.0:$rdi = IMPLICIT_DEFKILL $rsiCFI_INSTRUCTION undefined $raxEH_LABEL 0GC_LABEL 0DBG_VALUE $rax, $noreg, !13, !DIExpression(), debug-location !14DBG_LABEL 0LIFETIME_START 0LIFETIME_END 0PSEUDO_PROBE 6699318081062747564, 1, 0, 0$xmm0 = ARITH_FENCE $xmm0Int_MemBarrier...)MIR";MachineModuleInfo MMI(TM.get());M = parseMIR(*TM, MIRString, "test0", MMI);ASSERT_TRUE(M);auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));auto *MBB = MF->getBlockNumbered(0);for (auto It = MBB->begin(); It != MBB->end(); ++It) {MachineInstr &MI = *It;ASSERT_TRUE(MI.isMetaInstruction());}}TEST_F(MachineMetadataTest, MMSlotTrackerX64) {auto TM = createTargetMachine(Triple::normalize("x86_64--"), "", "");if (!TM)GTEST_SKIP();StringRef MIRString = R"MIR(--- |define i32 @test0(i32* %p) {%r = load i32, i32* %p, align 4ret i32 %r}...---name: test0liveins:- { reg: '$rdi', virtual-reg: '%0' }body: |bb.0 (%ir-block.0):liveins: $rdi%0:gr64 = COPY $rdi%1:gr32 = MOV32rm %0, 1, $noreg, 0, $noreg :: (load (s32) from %ir.p)...)MIR";MachineModuleInfo MMI(TM.get());M = parseMIR(*TM, MIRString, "test0", MMI);ASSERT_TRUE(M);auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));auto *MBB = MF->getBlockNumbered(0);auto &MI = MBB->back();ASSERT_FALSE(MI.memoperands_empty());ASSERT_TRUE(MI.hasOneMemOperand());// Create and attached scoped AA metadata on that instruction with one MMO.MDBuilder MDB(Context);MDNode *Domain = MDB.createAnonymousAliasScopeDomain("domain");MDNode *Scope0 = MDB.createAnonymousAliasScope(Domain, "scope0");MDNode *Scope1 = MDB.createAnonymousAliasScope(Domain, "scope1");MDNode *Set0 = MDNode::get(Context, {Scope0});MDNode *Set1 = MDNode::get(Context, {Scope1});AAMDNodes AAInfo;AAInfo.TBAA = AAInfo.TBAAStruct = nullptr;AAInfo.Scope = Set0;AAInfo.NoAlias = Set1;auto *OldMMO = MI.memoperands().front();auto *NewMMO = MF->getMachineMemOperand(OldMMO, AAInfo);MI.setMemRefs(*MF, NewMMO);MachineModuleSlotTracker MST(MF);// Print that MI with new machine metadata, which slot numbers should be// assigned.EXPECT_EQ("%1:gr32 = MOV32rm %0, 1, $noreg, 0, $noreg :: (load (s32) from %ir.p, ""!alias.scope !0, !noalias !3)",print([&](raw_ostream &OS) {MI.print(OS, MST, /*IsStandalone=*/false, /*SkipOpers=*/false,/*SkipDebugLoc=*/false, /*AddNewLine=*/false);}));std::vector<const MDNode *> Generated{Domain, Scope0, Scope1, Set0, Set1};// Examine machine metadata collected. They should match ones// afore-generated.std::vector<const MDNode *> Collected;MachineModuleSlotTracker::MachineMDNodeListType MDList;MST.collectMachineMDNodes(MDList);for (auto &MD : MDList)Collected.push_back(MD.second);llvm::sort(Generated);llvm::sort(Collected);EXPECT_EQ(Collected, Generated);// FileCheck the output from MIR printer.std::string Output = print([&](raw_ostream &OS) { printMIR(OS, *MF); });std::string CheckString = R"(CHECK: machineMetadataNodes:CHECK-DAG: ![[MMDOMAIN:[0-9]+]] = distinct !{!{{[0-9]+}}, !"domain"}CHECK-DAG: ![[MMSCOPE0:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope0"}CHECK-DAG: ![[MMSCOPE1:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope1"}CHECK-DAG: ![[MMSET0:[0-9]+]] = !{![[MMSCOPE0]]}CHECK-DAG: ![[MMSET1:[0-9]+]] = !{![[MMSCOPE1]]}CHECK: body:CHECK: %1:gr32 = MOV32rm %0, 1, $noreg, 0, $noreg :: (load (s32) from %ir.p, !alias.scope ![[MMSET0]], !noalias ![[MMSET1]]))";EXPECT_TRUE(checkOutput(CheckString, Output));}TEST_F(MachineMetadataTest, MMSlotTrackerAMDGPU) {auto TM = createTargetMachine(Triple::normalize("amdgcn-amd-amdhsa"),"gfx1010", "");if (!TM)GTEST_SKIP();StringRef MIRString = R"MIR(--- |define i32 @test0(i32* %p) {%r = load i32, i32* %p, align 4ret i32 %r}...---name: test0liveins:- { reg: '$vgpr0', virtual-reg: '%0' }- { reg: '$vgpr1', virtual-reg: '%1' }- { reg: '$sgpr30_sgpr31', virtual-reg: '%2' }body: |bb.0 (%ir-block.0):liveins: $vgpr0, $vgpr1, $sgpr30_sgpr31%2:sreg_64 = COPY $sgpr30_sgpr31%1:vgpr_32 = COPY $vgpr1%0:vgpr_32 = COPY $vgpr0%8:vreg_64 = REG_SEQUENCE %0, %subreg.sub0, %1, %subreg.sub1%6:vreg_64 = COPY %8%5:vgpr_32 = FLAT_LOAD_DWORD killed %6, 0, 0, implicit $exec, implicit $flat_scr :: (load (s32) from %ir.p)...)MIR";MachineModuleInfo MMI(TM.get());M = parseMIR(*TM, MIRString, "test0", MMI);ASSERT_TRUE(M);auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));auto *MBB = MF->getBlockNumbered(0);auto &MI = MBB->back();ASSERT_FALSE(MI.memoperands_empty());ASSERT_TRUE(MI.hasOneMemOperand());// Create and attached scoped AA metadata on that instruction with one MMO.MDBuilder MDB(Context);MDNode *Domain = MDB.createAnonymousAliasScopeDomain("domain");MDNode *Scope0 = MDB.createAnonymousAliasScope(Domain, "scope0");MDNode *Scope1 = MDB.createAnonymousAliasScope(Domain, "scope1");MDNode *Set0 = MDNode::get(Context, {Scope0});MDNode *Set1 = MDNode::get(Context, {Scope1});AAMDNodes AAInfo;AAInfo.TBAA = AAInfo.TBAAStruct = nullptr;AAInfo.Scope = Set0;AAInfo.NoAlias = Set1;auto *OldMMO = MI.memoperands().front();auto *NewMMO = MF->getMachineMemOperand(OldMMO, AAInfo);MI.setMemRefs(*MF, NewMMO);MachineModuleSlotTracker MST(MF);// Print that MI with new machine metadata, which slot numbers should be// assigned.EXPECT_EQ("%5:vgpr_32 = FLAT_LOAD_DWORD killed %4, 0, 0, implicit $exec, implicit ""$flat_scr :: (load (s32) from %ir.p, !alias.scope !0, !noalias !3)",print([&](raw_ostream &OS) {MI.print(OS, MST, /*IsStandalone=*/false, /*SkipOpers=*/false,/*SkipDebugLoc=*/false, /*AddNewLine=*/false);}));std::vector<const MDNode *> Generated{Domain, Scope0, Scope1, Set0, Set1};// Examine machine metadata collected. They should match ones// afore-generated.std::vector<const MDNode *> Collected;MachineModuleSlotTracker::MachineMDNodeListType MDList;MST.collectMachineMDNodes(MDList);for (auto &MD : MDList)Collected.push_back(MD.second);llvm::sort(Generated);llvm::sort(Collected);EXPECT_EQ(Collected, Generated);// FileCheck the output from MIR printer.std::string Output = print([&](raw_ostream &OS) { printMIR(OS, *MF); });std::string CheckString = R"(CHECK: machineMetadataNodes:CHECK-DAG: ![[MMDOMAIN:[0-9]+]] = distinct !{!{{[0-9]+}}, !"domain"}CHECK-DAG: ![[MMSCOPE0:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope0"}CHECK-DAG: ![[MMSCOPE1:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope1"}CHECK-DAG: ![[MMSET0:[0-9]+]] = !{![[MMSCOPE0]]}CHECK-DAG: ![[MMSET1:[0-9]+]] = !{![[MMSCOPE1]]}CHECK: body:CHECK: %5:vgpr_32 = FLAT_LOAD_DWORD killed %4, 0, 0, implicit $exec, implicit $flat_scr :: (load (s32) from %ir.p, !alias.scope ![[MMSET0]], !noalias ![[MMSET1]]))";EXPECT_TRUE(checkOutput(CheckString, Output));}TEST_F(MachineMetadataTest, TiedOpsRewritten) {auto TM = createTargetMachine(Triple::normalize("powerpc64--"), "", "");if (!TM)GTEST_SKIP();StringRef MIRString = R"MIR(---name: fooalignment: 16tracksRegLiveness: trueframeInfo:maxAlignment: 16machineFunctionInfo: {}body: |bb.0:liveins: $r3%0:gprc = COPY $r3%0 = RLWIMI killed %0, $r3, 1, 0, 30$r3 = COPY %0BLR8 implicit $r3, implicit $lr8, implicit $rm...)MIR";MachineModuleInfo MMI(TM.get());M = parseMIR(*TM, MIRString, "foo", MMI);ASSERT_TRUE(M);auto *MF = MMI.getMachineFunction(*M->getFunction("foo"));MachineFunctionProperties &Properties = MF->getProperties();ASSERT_TRUE(Properties.hasProperty(MachineFunctionProperties::Property::TiedOpsRewritten));}TEST_F(MachineMetadataTest, NoTiedOpsRewritten) {auto TM = createTargetMachine(Triple::normalize("powerpc64--"), "", "");if (!TM)GTEST_SKIP();StringRef MIRString = R"MIR(---name: fooalignment: 16tracksRegLiveness: trueframeInfo:maxAlignment: 16machineFunctionInfo: {}body: |bb.0:liveins: $r3%0:gprc = COPY $r3%1:gprc = RLWIMI killed %0, $r3, 1, 0, 30$r3 = COPY %1BLR8 implicit $r3, implicit $lr8, implicit $rm...)MIR";MachineModuleInfo MMI(TM.get());M = parseMIR(*TM, MIRString, "foo", MMI);ASSERT_TRUE(M);auto *MF = MMI.getMachineFunction(*M->getFunction("foo"));MachineFunctionProperties &Properties = MF->getProperties();ASSERT_FALSE(Properties.hasProperty(MachineFunctionProperties::Property::TiedOpsRewritten));}
set(LLVM_LINK_COMPONENTS${LLVM_TARGETS_TO_BUILD}CodeGenCoreFileCheckMCMIRParserSupportTarget)add_llvm_unittest(MIRTestsMachineMetadata.cpp)target_link_libraries(MIRTests PRIVATE LLVMTestingSupport)
#include "llvm/ADT/STLExtras.h"#include "llvm/CodeGen/LiveIntervals.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetRegisterInfo.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/InitializePasses.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {void initializeTestPassPass(PassRegistry &);}namespace {void initLLVM() {InitializeAllTargets();InitializeAllTargetMCs();InitializeAllAsmPrinters();InitializeAllAsmParsers();PassRegistry *Registry = PassRegistry::getPassRegistry();initializeCore(*Registry);initializeCodeGen(*Registry);}/// Create a TargetMachine. As we lack a dedicated always available target for/// unittests, we go for "AMDGPU" to be able to test normal and subregister/// liveranges.std::unique_ptr<LLVMTargetMachine> createTargetMachine() {Triple TargetTriple("amdgcn--");std::string Error;const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);if (!T)return nullptr;TargetOptions Options;return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(T->createTargetMachine("AMDGPU", "gfx900", "", Options, None, None,CodeGenOpt::Aggressive)));}std::unique_ptr<Module> parseMIR(LLVMContext &Context,legacy::PassManagerBase &PM, std::unique_ptr<MIRParser> &MIR,const LLVMTargetMachine &TM, StringRef MIRCode, const char *FuncName) {SMDiagnostic Diagnostic;std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);MIR = createMIRParser(std::move(MBuffer), Context);if (!MIR)return nullptr;std::unique_ptr<Module> M = MIR->parseIRModule();if (!M)return nullptr;M->setDataLayout(TM.createDataLayout());MachineModuleInfoWrapperPass *MMIWP = new MachineModuleInfoWrapperPass(&TM);if (MIR->parseMachineFunctions(*M, MMIWP->getMMI()))return nullptr;PM.add(MMIWP);return M;}typedef std::function<void(MachineFunction&,LiveIntervals&)> LiveIntervalTest;struct TestPass : public MachineFunctionPass {static char ID;TestPass() : MachineFunctionPass(ID) {// We should never call this but always use PM.add(new TestPass(...))abort();}TestPass(LiveIntervalTest T) : MachineFunctionPass(ID), T(T) {initializeTestPassPass(*PassRegistry::getPassRegistry());}bool runOnMachineFunction(MachineFunction &MF) override {LiveIntervals &LIS = getAnalysis<LiveIntervals>();T(MF, LIS);EXPECT_TRUE(MF.verify(this));return true;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();AU.addRequired<LiveIntervals>();AU.addPreserved<LiveIntervals>();MachineFunctionPass::getAnalysisUsage(AU);}private:LiveIntervalTest T;};static MachineInstr &getMI(MachineFunction &MF, unsigned At,unsigned BlockNum) {MachineBasicBlock &MBB = *MF.getBlockNumbered(BlockNum);unsigned I = 0;for (MachineInstr &MI : MBB) {if (I == At)return MI;++I;}llvm_unreachable("Instruction not found");}/*** Move instruction number \p From in front of instruction number \p To and* update affected liveness intervals with LiveIntervalAnalysis::handleMove().*/static void testHandleMove(MachineFunction &MF, LiveIntervals &LIS,unsigned From, unsigned To, unsigned BlockNum = 0) {MachineInstr &FromInstr = getMI(MF, From, BlockNum);MachineInstr &ToInstr = getMI(MF, To, BlockNum);MachineBasicBlock &MBB = *FromInstr.getParent();MBB.splice(ToInstr.getIterator(), &MBB, FromInstr.getIterator());LIS.handleMove(FromInstr, true);}/*** Move instructions numbered \p From inclusive through instruction number* \p To into a newly formed bundle and update affected liveness intervals* with LiveIntervalAnalysis::handleMoveIntoNewBundle().*/static void testHandleMoveIntoNewBundle(MachineFunction &MF, LiveIntervals &LIS,unsigned From, unsigned To,unsigned BlockNum = 0) {MachineInstr &FromInstr = getMI(MF, From, BlockNum);MachineInstr &ToInstr = getMI(MF, To, BlockNum);MachineBasicBlock &MBB = *FromInstr.getParent();MachineBasicBlock::instr_iterator I = FromInstr.getIterator();// Build bundlefinalizeBundle(MBB, I, std::next(ToInstr.getIterator()));// Update LiveIntervalsMachineBasicBlock::instr_iterator BundleStart = std::prev(I);LIS.handleMoveIntoNewBundle(*BundleStart, true);}/*** Split block numbered \p BlockNum at instruction \p SplitAt using* MachineBasicBlock::splitAt updating liveness intervals.*/static void testSplitAt(MachineFunction &MF, LiveIntervals &LIS,unsigned SplitAt, unsigned BlockNum) {MachineInstr &SplitInstr = getMI(MF, SplitAt, BlockNum);MachineBasicBlock &MBB = *SplitInstr.getParent();// Split block and update live intervalsMBB.splitAt(SplitInstr, false, &LIS);}static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) {LLVMContext Context;std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();// This test is designed for the X86 backend; stop if it is not available.if (!TM)return;legacy::PassManager PM;SmallString<160> S;StringRef MIRString = (Twine(R"MIR(---...name: funcregisters:- { id: 0, class: sreg_64 }body: |bb.0:)MIR") + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S);std::unique_ptr<MIRParser> MIR;std::unique_ptr<Module> M = parseMIR(Context, PM, MIR, *TM, MIRString,"func");ASSERT_TRUE(M);PM.add(new TestPass(T));PM.run(*M);}} // End of anonymous namespace.char TestPass::ID = 0;INITIALIZE_PASS(TestPass, "testpass", "testpass", false, false)TEST(LiveIntervalTest, MoveUpDef) {// Value defined.liveIntervalTest(R"MIR(S_NOP 0S_NOP 0early-clobber %0 = IMPLICIT_DEFS_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 2, 1);});}TEST(LiveIntervalTest, MoveUpRedef) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0%0 = IMPLICIT_DEF implicit %0(tied-def 0)S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 2, 1);});}TEST(LiveIntervalTest, MoveUpEarlyDef) {liveIntervalTest(R"MIR(S_NOP 0S_NOP 0early-clobber %0 = IMPLICIT_DEFS_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 2, 1);});}TEST(LiveIntervalTest, MoveUpEarlyRedef) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 2, 1);});}TEST(LiveIntervalTest, MoveUpKill) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 2, 1);});}TEST(LiveIntervalTest, MoveUpKillFollowing) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 2, 1);});}// TODO: Construct a situation where we have intervals following a hole// while still having connected components.TEST(LiveIntervalTest, MoveDownDef) {// Value defined.liveIntervalTest(R"MIR(S_NOP 0early-clobber %0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, MoveDownRedef) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEF%0 = IMPLICIT_DEF implicit %0(tied-def 0)S_NOP 0S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, MoveDownEarlyDef) {liveIntervalTest(R"MIR(S_NOP 0early-clobber %0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, MoveDownEarlyRedef) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFearly-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)S_NOP 0S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, MoveDownKill) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0, implicit %0S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, MoveDownKillFollowing) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, MoveUndefUse) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0, implicit undef %0S_NOP 0, implicit %0S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 3);});}TEST(LiveIntervalTest, MoveUpValNos) {// handleMoveUp() had a bug where it would reuse the value number of the// destination segment, even though we have no guarantee that this valno// wasn't used in other segments.liveIntervalTest(R"MIR(successors: %bb.1, %bb.2%0 = IMPLICIT_DEFS_CBRANCH_VCCNZ %bb.2, implicit undef $vccS_BRANCH %bb.1bb.2:S_NOP 0, implicit %0bb.1:successors: %bb.2%0 = IMPLICIT_DEF implicit %0(tied-def 0)%0 = IMPLICIT_DEF implicit %0(tied-def 0)%0 = IMPLICIT_DEF implicit %0(tied-def 0)S_BRANCH %bb.2)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 2, 0, 2);});}TEST(LiveIntervalTest, MoveOverUndefUse0) {// findLastUseBefore() used by handleMoveUp() must ignore undef operands.liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit undef %0%0 = IMPLICIT_DEF implicit %0(tied-def 0))MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 3, 1);});}TEST(LiveIntervalTest, MoveOverUndefUse1) {// findLastUseBefore() used by handleMoveUp() must ignore undef operands.liveIntervalTest(R"MIR($sgpr0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit undef $sgpr0$sgpr0 = IMPLICIT_DEF implicit $sgpr0(tied-def 0))MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 3, 1);});}TEST(LiveIntervalTest, SubRegMoveDown) {// Subregister ranges can have holes inside a basic block. Check for a// movement of the form 32->150 in a liverange [16, 32) [100,200).liveIntervalTest(R"MIR(successors: %bb.1, %bb.2%0 = IMPLICIT_DEFS_CBRANCH_VCCNZ %bb.2, implicit undef $vccS_BRANCH %bb.1bb.2:successors: %bb.1S_NOP 0, implicit %0.sub0S_NOP 0, implicit %0.sub1S_NOP 0undef %0.sub0 = IMPLICIT_DEF%0.sub1 = IMPLICIT_DEFbb.1:S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {// Scheduler behaviour: Clear def,read-undef flag and move.MachineInstr &MI = getMI(MF, 3, /*BlockNum=*/1);MI.getOperand(0).setIsUndef(false);testHandleMove(MF, LIS, 1, 4, /*BlockNum=*/1);});}TEST(LiveIntervalTest, SubRegMoveUp) {// handleMoveUp had a bug not updating valno of segment incoming to bb.2// after swapping subreg definitions.liveIntervalTest(R"MIR(successors: %bb.1, %bb.2undef %0.sub0 = IMPLICIT_DEF%0.sub1 = IMPLICIT_DEFS_CBRANCH_VCCNZ %bb.2, implicit undef $vccS_BRANCH %bb.1bb.1:S_NOP 0, implicit %0.sub1bb.2:S_NOP 0, implicit %0.sub1)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 0);});}TEST(LiveIntervalTest, DeadSubRegMoveUp) {// handleMoveUp had a bug where moving a dead subreg def into the middle of// an earlier segment resulted in an invalid live range.liveIntervalTest(R"MIR(undef %125.sub0:vreg_128 = V_MOV_B32_e32 0, implicit $exec%125.sub1:vreg_128 = COPY %125.sub0%125.sub2:vreg_128 = COPY %125.sub0undef %51.sub0:vreg_128 = V_MOV_B32_e32 898625526, implicit $exec%51.sub1:vreg_128 = COPY %51.sub0%51.sub2:vreg_128 = COPY %51.sub0%52:vgpr_32 = V_MOV_B32_e32 986714345, implicit $exec%54:vgpr_32 = V_MOV_B32_e32 1742342378, implicit $exec%57:vgpr_32 = V_MOV_B32_e32 3168768712, implicit $exec%59:vgpr_32 = V_MOV_B32_e32 1039972644, implicit $exec%60:vgpr_32 = nofpexcept V_MAD_F32_e64 0, %52, 0, undef %61:vgpr_32, 0, %59, 0, 0, implicit $mode, implicit $exec%63:vgpr_32 = nofpexcept V_ADD_F32_e32 %51.sub3, undef %64:vgpr_32, implicit $mode, implicit $execdead %66:vgpr_32 = nofpexcept V_MAD_F32_e64 0, %60, 0, undef %67:vgpr_32, 0, %125.sub2, 0, 0, implicit $mode, implicit $execundef %124.sub1:vreg_128 = nofpexcept V_MAD_F32_e64 0, %57, 0, undef %70:vgpr_32, 0, %125.sub1, 0, 0, implicit $mode, implicit $exec%124.sub0:vreg_128 = nofpexcept V_MAD_F32_e64 0, %54, 0, undef %73:vgpr_32, 0, %125.sub0, 0, 0, implicit $mode, implicit $execdead undef %125.sub3:vreg_128 = nofpexcept V_MAC_F32_e32 %63, undef %76:vgpr_32, %125.sub3, implicit $mode, implicit $exec)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 15, 12);});}TEST(LiveIntervalTest, EarlyClobberSubRegMoveUp) {// handleMoveUp had a bug where moving an early-clobber subreg def into the// middle of an earlier segment resulted in an invalid live range.liveIntervalTest(R"MIR(%4:sreg_32 = IMPLICIT_DEF%6:sreg_32 = IMPLICIT_DEFundef early-clobber %9.sub0:sreg_64 = STRICT_WWM %4:sreg_32, implicit $exec%5:sreg_32 = S_FLBIT_I32_B32 %9.sub0:sreg_64early-clobber %9.sub1:sreg_64 = STRICT_WWM %6:sreg_32, implicit $exec%7:sreg_32 = S_FLBIT_I32_B32 %9.sub1:sreg_64)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 4, 3);});}TEST(LiveIntervalTest, TestMoveSubRegDefAcrossUseDef) {liveIntervalTest(R"MIR(%1:vreg_64 = IMPLICIT_DEFbb.1:%2:vgpr_32 = V_MOV_B32_e32 2, implicit $exec%3:vgpr_32 = V_ADD_U32_e32 %2, %1.sub0, implicit $execundef %1.sub0:vreg_64 = V_ADD_U32_e32 %2, %2, implicit $exec%1.sub1:vreg_64 = COPY %2S_NOP 0, implicit %1.sub1S_BRANCH %bb.1)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {MachineInstr &UndefSubregDef = getMI(MF, 2, 1);// The scheduler clears undef from subregister defs before movingUndefSubregDef.getOperand(0).setIsUndef(false);testHandleMove(MF, LIS, 3, 1, 1);});}TEST(LiveIntervalTest, TestMoveSubRegDefAcrossUseDefMulti) {liveIntervalTest(R"MIR(%1:vreg_96 = IMPLICIT_DEFbb.1:%2:vgpr_32 = V_MOV_B32_e32 2, implicit $exec%3:vgpr_32 = V_ADD_U32_e32 %2, %1.sub0, implicit $execundef %1.sub0:vreg_96 = V_ADD_U32_e32 %2, %2, implicit $exec%1.sub1:vreg_96 = COPY %2%1.sub2:vreg_96 = COPY %2S_NOP 0, implicit %1.sub1, implicit %1.sub2S_BRANCH %bb.1)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {MachineInstr &UndefSubregDef = getMI(MF, 2, 1);// The scheduler clears undef from subregister defs before movingUndefSubregDef.getOperand(0).setIsUndef(false);testHandleMove(MF, LIS, 4, 1, 1);});}TEST(LiveIntervalTest, TestMoveSubRegUseAcrossMainRangeHole) {liveIntervalTest(R"MIR(%1:sgpr_128 = IMPLICIT_DEFbb.1:%2:sgpr_32 = COPY %1.sub2%3:sgpr_32 = COPY %1.sub1%1.sub2 = COPY %2undef %1.sub0 = IMPLICIT_DEF%1.sub2 = IMPLICIT_DEFS_CBRANCH_SCC1 %bb.1, implicit undef $sccS_BRANCH %bb.2bb.2:)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {MachineInstr &MI = getMI(MF, 3, /*BlockNum=*/1);MI.getOperand(0).setIsUndef(false);testHandleMove(MF, LIS, 4, 3, 1);testHandleMove(MF, LIS, 1, 4, 1);});}TEST(LiveIntervalTest, TestMoveSubRegsOfOneReg) {liveIntervalTest(R"MIR(INLINEASM &"", 0, 1835018, def undef %4.sub0:vreg_64, 1835018, def undef %4.sub1:vreg_64%1:vreg_64 = COPY %4undef %2.sub0:vreg_64 = V_MOV_B32_e32 0, implicit $exec%2.sub1:vreg_64 = COPY %2.sub0%3:vreg_64 = COPY %2)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMove(MF, LIS, 1, 4);testHandleMove(MF, LIS, 0, 3);});}TEST(LiveIntervalTest, BundleUse) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMoveIntoNewBundle(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, BundleDef) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMoveIntoNewBundle(MF, LIS, 0, 1);});}TEST(LiveIntervalTest, BundleRedef) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0%0 = IMPLICIT_DEF implicit %0(tied-def 0)S_NOP 0, implicit %0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMoveIntoNewBundle(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, BundleInternalUse) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit %0S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMoveIntoNewBundle(MF, LIS, 0, 2);});}TEST(LiveIntervalTest, BundleUndefUse) {liveIntervalTest(R"MIR(%0 = IMPLICIT_DEFS_NOP 0S_NOP 0, implicit undef %0S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMoveIntoNewBundle(MF, LIS, 1, 2);});}TEST(LiveIntervalTest, BundleSubRegUse) {liveIntervalTest(R"MIR(successors: %bb.1, %bb.2undef %0.sub0 = IMPLICIT_DEF%0.sub1 = IMPLICIT_DEFS_CBRANCH_VCCNZ %bb.2, implicit undef $vccS_BRANCH %bb.1bb.1:S_NOP 0S_NOP 0, implicit %0.sub1bb.2:S_NOP 0, implicit %0.sub1)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMoveIntoNewBundle(MF, LIS, 0, 1, 1);});}TEST(LiveIntervalTest, BundleSubRegDef) {liveIntervalTest(R"MIR(successors: %bb.1, %bb.2undef %0.sub0 = IMPLICIT_DEF%0.sub1 = IMPLICIT_DEFS_CBRANCH_VCCNZ %bb.2, implicit undef $vccS_BRANCH %bb.1bb.1:S_NOP 0S_NOP 0, implicit %0.sub1bb.2:S_NOP 0, implicit %0.sub1)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testHandleMoveIntoNewBundle(MF, LIS, 0, 1, 0);});}TEST(LiveIntervalTest, SplitAtOneInstruction) {liveIntervalTest(R"MIR(successors: %bb.1%0 = IMPLICIT_DEFS_BRANCH %bb.1bb.1:S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testSplitAt(MF, LIS, 1, 0);});}TEST(LiveIntervalTest, SplitAtMultiInstruction) {liveIntervalTest(R"MIR(successors: %bb.1%0 = IMPLICIT_DEFS_NOP 0S_NOP 0S_NOP 0S_NOP 0S_BRANCH %bb.1bb.1:S_NOP 0)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {testSplitAt(MF, LIS, 0, 0);});}TEST(LiveIntervalTest, RepairIntervals) {liveIntervalTest(R"MIR(%1:sgpr_32 = IMPLICIT_DEFdead %2:sgpr_32 = COPY undef %3.sub0:sgpr_128undef %4.sub2:sgpr_128 = COPY %1:sgpr_32%5:sgpr_32 = COPY %4.sub2:sgpr_128)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {MachineInstr &Instr1 = getMI(MF, 1, 0);MachineInstr &Instr2 = getMI(MF, 2, 0);MachineInstr &Instr3 = getMI(MF, 3, 0);LIS.RemoveMachineInstrFromMaps(Instr2);MachineBasicBlock *MBB = Instr1.getParent();SmallVector<Register> OrigRegs{Instr1.getOperand(0).getReg(),Instr2.getOperand(0).getReg(),Instr2.getOperand(1).getReg(),};LIS.repairIntervalsInRange(MBB, Instr2, Instr3, OrigRegs);});}int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);initLLVM();return RUN_ALL_TESTS();}
set(LLVM_LINK_COMPONENTS${LLVM_TARGETS_TO_BUILD}CodeGenCoreMCMIRParserSupportTarget)add_llvm_unittest(MITestsLiveIntervalTest.cpp)
//===- X86MCDisassemblerTest.cpp - Tests for X86 MCDisassembler -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCDisassembler/MCDisassembler.h"#include "llvm/MC/MCDisassembler/MCSymbolizer.h"#include "llvm/MC/MCInst.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct Context {const char *TripleName = "x86_64-unknown-elf";std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<MCContext> Ctx;std::unique_ptr<MCSubtargetInfo> STI;std::unique_ptr<MCDisassembler> DisAsm;Context() {LLVMInitializeX86TargetInfo();LLVMInitializeX86TargetMC();LLVMInitializeX86Disassembler();// If we didn't build x86, do not run the test.std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);if (!TheTarget)return;MRI.reset(TheTarget->createMCRegInfo(TripleName));MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCTargetOptions()));STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),STI.get());DisAsm.reset(TheTarget->createMCDisassembler(*STI, *Ctx));}operator MCContext &() { return *Ctx; };};Context &getContext() {static Context Ctxt;return Ctxt;}class X86MCSymbolizerTest : public MCSymbolizer {public:X86MCSymbolizerTest(MCContext &MC) : MCSymbolizer(MC, nullptr) {}~X86MCSymbolizerTest() {}struct OpInfo {int64_t Value = 0;uint64_t Offset = 0;uint64_t Size;};std::vector<OpInfo> Operands;uint64_t InstructionSize = 0;void reset() {Operands.clear();InstructionSize = 0;}bool tryAddingSymbolicOperand(MCInst &Inst, raw_ostream &CStream,int64_t Value, uint64_t Address, bool IsBranch,uint64_t Offset, uint64_t OpSize,uint64_t InstSize) override {Operands.push_back({Value, Offset, OpSize});InstructionSize = InstSize;return false;}void tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value,uint64_t Address) override {}};} // namespaceTEST(X86Disassembler, X86MCSymbolizerTest) {X86MCSymbolizerTest *TestSymbolizer = new X86MCSymbolizerTest(getContext());getContext().DisAsm->setSymbolizer(std::unique_ptr<MCSymbolizer>(TestSymbolizer));MCDisassembler::DecodeStatus Status;MCInst Inst;uint64_t InstSize;auto checkBytes = [&](ArrayRef<uint8_t> Bytes) {TestSymbolizer->reset();Status =getContext().DisAsm->getInstruction(Inst, InstSize, Bytes, 0, nulls());ASSERT_TRUE(Status == MCDisassembler::Success);EXPECT_EQ(TestSymbolizer->InstructionSize, InstSize);};auto checkOperand = [&](size_t OpNo, int64_t Value, uint64_t Offset,uint64_t Size) {ASSERT_TRUE(TestSymbolizer->Operands.size() > OpNo);EXPECT_EQ(TestSymbolizer->Operands[OpNo].Value, Value);EXPECT_EQ(TestSymbolizer->Operands[OpNo].Offset, Offset);EXPECT_EQ(TestSymbolizer->Operands[OpNo].Size, Size);};// movq $0x80000, 0x80000checkBytes({0x48, 0xc7, 0x04, 0x25, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00});checkOperand(0, 0x80000, 4, 4);checkOperand(1, 0x80000, 8, 4);// movq $0x2a, 0x123(%rax,%r14,8)checkBytes({0x4a, 0xc7, 0x84, 0xf0, 0x23, 0x01, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00});checkOperand(0, 291, 4, 4);checkOperand(1, 42, 8, 4);// movq $0xffffffffffffefe8, -0x1(%rip)// Test that the value of the rip-relative operand is set correctly.// The instruction address is 0 and the size is 12 bytes.checkBytes({0x48, 0xc7, 0x05, 0xff, 0xff, 0xff, 0xff, 0xe8, 0xef, 0xff, 0xff});checkOperand(0, /*next instr address*/ 11 - /*disp*/ 1, 3, 4);checkOperand(1, 0xffffffffffffefe8, 7, 4);// movq $0xfffffffffffffef5, (%r12)// Test that the displacement operand has a size of 0, since it is not// explicitly specified in the instruction.checkBytes({0x49, 0xc7, 0x04, 0x24, 0xf5, 0xfe, 0xff, 0xff});checkOperand(0, 0, 4, 0);checkOperand(1, 0xfffffffffffffef5, 4, 4);// mov %ax, 0x1568179(%rbx)// Test that the displacement operand size is not affected by the operand// size override prefix.checkBytes({0x66, 0x89, 0x83, 0x79, 0x81, 0x56, 0x01});checkOperand(0, 0x1568179, 3, 4);}
set(LLVM_LINK_COMPONENTSMCMCDisassemblerTargetX86DescX86DisassemblerX86Info)add_llvm_unittest(X86MCTestsX86MCDisassemblerTest.cpp)
//===- unittests/MC/TargetRegistry.cpp ------------------------------------===////// 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////===----------------------------------------------------------------------===//// The target registry code lives in Support, but it relies on linking in all// LLVM targets. We keep this test with the MC tests, which already do that, to// keep the SupportTests target small.#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(TargetRegistry, TargetHasArchType) {// Presence of at least one target will be asserted when done with the loop,// else this would pass by accident if InitializeAllTargetInfos were omitted.int Count = 0;llvm::InitializeAllTargetInfos();for (const Target &T : TargetRegistry::targets()) {StringRef Name = T.getName();// There is really no way (at present) to ask a Target whether it targets// a specific architecture, because the logic for that is buried in a// predicate.// We can't ask the predicate "Are you a function that always returns// false?"// So given that the cpp backend truly has no target arch, it is skipped.if (Name != "cpp") {Triple::ArchType Arch = Triple::getArchTypeForLLVMName(Name);EXPECT_NE(Arch, Triple::UnknownArch);++Count;}}ASSERT_NE(Count, 0);}} // end namespace
//===- llvm/unittests/MC/SystemZ/SystemZAsmLexerTest.cpp ----------------===////// 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////===--------------------------------------------------------------------===//#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCObjectFileInfo.h"#include "llvm/MC/MCParser/MCAsmLexer.h"#include "llvm/MC/MCParser/MCTargetAsmParser.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCStreamer.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/MCSymbol.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Setup a testing class that the GTest framework can call.class SystemZAsmLexerTest : public ::testing::Test {protected:static void SetUpTestCase() {LLVMInitializeSystemZTargetInfo();LLVMInitializeSystemZTargetMC();LLVMInitializeSystemZAsmParser();}std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<const MCInstrInfo> MII;std::unique_ptr<MCObjectFileInfo> MOFI;std::unique_ptr<MCStreamer> Str;std::unique_ptr<MCAsmParser> Parser;std::unique_ptr<MCContext> Ctx;std::unique_ptr<MCSubtargetInfo> STI;std::unique_ptr<MCTargetAsmParser> TargetAsmParser;SourceMgr SrcMgr;std::string TripleName;llvm::Triple Triple;const Target *TheTarget;const MCTargetOptions MCOptions;SystemZAsmLexerTest() = delete;SystemZAsmLexerTest(std::string SystemZTriple) {// We will use the SystemZ triple, because of missing// Object File and Streamer support for the z/OS target.TripleName = SystemZTriple;Triple = llvm::Triple(TripleName);std::string Error;TheTarget = TargetRegistry::lookupTarget(TripleName, Error);EXPECT_NE(TheTarget, nullptr);MRI.reset(TheTarget->createMCRegInfo(TripleName));EXPECT_NE(MRI, nullptr);MII.reset(TheTarget->createMCInstrInfo());EXPECT_NE(MII, nullptr);STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "z10", ""));EXPECT_NE(STI, nullptr);MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));EXPECT_NE(MAI, nullptr);}void setupCallToAsmParser(StringRef AsmStr) {std::unique_ptr<MemoryBuffer> Buffer(MemoryBuffer::getMemBuffer(AsmStr));SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc());EXPECT_EQ(Buffer, nullptr);Ctx.reset(new MCContext(Triple, MAI.get(), MRI.get(), STI.get(), &SrcMgr,&MCOptions));MOFI.reset(TheTarget->createMCObjectFileInfo(*Ctx, /*PIC=*/false,/*LargeCodeModel=*/false));Ctx->setObjectFileInfo(MOFI.get());Str.reset(TheTarget->createNullStreamer(*Ctx));Parser.reset(createMCAsmParser(SrcMgr, *Ctx, *Str, *MAI));TargetAsmParser.reset(TheTarget->createMCAsmParser(*STI, *Parser, *MII, MCOptions));Parser->setTargetParser(*TargetAsmParser);}void lexAndCheckTokens(StringRef AsmStr,SmallVector<AsmToken::TokenKind> ExpectedTokens) {// Get reference to AsmLexer.MCAsmLexer &Lexer = Parser->getLexer();// Loop through all expected tokens checking one by one.for (size_t I = 0; I < ExpectedTokens.size(); ++I) {EXPECT_EQ(Lexer.getTok().getKind(), ExpectedTokens[I]);Lexer.Lex();}}void lexAndCheckIntegerTokensAndValues(StringRef AsmStr,SmallVector<int64_t> ExpectedValues) {// Get reference to AsmLexer.MCAsmLexer &Lexer = Parser->getLexer();// Loop through all expected tokens and expected values.for (size_t I = 0; I < ExpectedValues.size(); ++I) {// Skip any EndOfStatement tokens, we're not concerned with them.if (Lexer.getTok().getKind() == AsmToken::EndOfStatement)continue;EXPECT_EQ(Lexer.getTok().getKind(), AsmToken::Integer);EXPECT_EQ(Lexer.getTok().getIntVal(), ExpectedValues[I]);Lexer.Lex();}}};class SystemZAsmLexerLinux : public SystemZAsmLexerTest {protected:SystemZAsmLexerLinux() : SystemZAsmLexerTest("s390x-ibm-linux") {}};class SystemZAsmLexerZOS : public SystemZAsmLexerTest {protected:SystemZAsmLexerZOS() : SystemZAsmLexerTest("s390x-ibm-zos") {}};TEST_F(SystemZAsmLexerLinux, CheckDontRestrictCommentStringToStartOfStatement) {StringRef AsmStr = "jne #-4";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement});lexAndCheckTokens(AsmStr /* "jne #-4" */, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckRestrictCommentStringToStartOfStatement) {StringRef AsmStr = "jne #-4";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();// When we are restricting the comment string to only the start of the// statement, The sequence of tokens we are expecting are: Identifier - "jne"// Hash - '#'// Minus - '-'// Integer - '4'SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::Space, AsmToken::Identifier});lexAndCheckTokens(AsmStr /* "jne #-4" */, ExpectedTokens);}// Test HLASM Comment Syntax ('*')TEST_F(SystemZAsmLexerZOS, CheckHLASMComment) {StringRef AsmStr = "* lhi 1,10";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr /* "* lhi 1,10" */, ExpectedTokens);}TEST_F(SystemZAsmLexerLinux, CheckHashDefault) {StringRef AsmStr = "lh#123";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();// "lh" -> Identifier// "#123" -> EndOfStatement (Lexed as a comment since CommentString is "#")SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}// Test if "#" is accepted as an IdentifierTEST_F(SystemZAsmLexerZOS, CheckAllowHashInIdentifier) {StringRef AsmStr = "lh#123";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();// "lh123" -> IdentifierSmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckAllowHashInIdentifier2) {StringRef AsmStr = "lh#12*3";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();// "lh#12" -> Identifier// "*" -> Star// "3" -> IntegerSmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::Star, AsmToken::Integer,AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerLinux, DontCheckStrictCommentString) {StringRef AsmStr = "# abc\n/* def */// xyz";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::EndOfStatement, AsmToken::Comment, AsmToken::EndOfStatement,AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckStrictCommentString) {StringRef AsmStr = "# abc\n/* def */// xyz";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens;ExpectedTokens.push_back(AsmToken::Identifier); // "#"ExpectedTokens.push_back(AsmToken::Space); // " "ExpectedTokens.push_back(AsmToken::Identifier); // "abc"ExpectedTokens.push_back(AsmToken::EndOfStatement); // "\n"ExpectedTokens.push_back(AsmToken::Slash); // "/"ExpectedTokens.push_back(AsmToken::Star); // "*"ExpectedTokens.push_back(AsmToken::Space); // " "ExpectedTokens.push_back(AsmToken::Identifier); // "def"ExpectedTokens.push_back(AsmToken::Space); // " "ExpectedTokens.push_back(AsmToken::Star); // "*"ExpectedTokens.push_back(AsmToken::Slash); // "/"ExpectedTokens.push_back(AsmToken::Slash); // "/"ExpectedTokens.push_back(AsmToken::Slash); // "/"ExpectedTokens.push_back(AsmToken::Space); // " "ExpectedTokens.push_back(AsmToken::Identifier); // "xyz"ExpectedTokens.push_back(AsmToken::EndOfStatement);ExpectedTokens.push_back(AsmToken::Eof);lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckValidHLASMIntegers) {StringRef AsmStr = "123\n000123\n1999\n007\n12300\n12021\n";// StringRef AsmStr = "123";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();// SmallVector<int64_t> ExpectedValues({123});SmallVector<int64_t> ExpectedValues({123, 123, 1999, 7, 12300, 12021});lexAndCheckIntegerTokensAndValues(AsmStr, ExpectedValues);}TEST_F(SystemZAsmLexerZOS, CheckInvalidHLASMIntegers) {StringRef AsmStr = "0b0101\n0xDEADBEEF\nfffh\n.133\n";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens;ExpectedTokens.push_back(AsmToken::Integer); // "0"ExpectedTokens.push_back(AsmToken::Identifier); // "b0101"ExpectedTokens.push_back(AsmToken::EndOfStatement); // "\n"ExpectedTokens.push_back(AsmToken::Integer); // "0"ExpectedTokens.push_back(AsmToken::Identifier); // "xDEADBEEF"ExpectedTokens.push_back(AsmToken::EndOfStatement); // "\n"ExpectedTokens.push_back(AsmToken::Identifier); // "fffh"ExpectedTokens.push_back(AsmToken::EndOfStatement); // "\n"ExpectedTokens.push_back(AsmToken::Real); // ".133"ExpectedTokens.push_back(AsmToken::EndOfStatement); // "\n"ExpectedTokens.push_back(AsmToken::Eof);lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerLinux, CheckDefaultIntegers) {StringRef AsmStr = "0b0101\n0xDEADBEEF\nfffh\n";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<int64_t> ExpectedValues({5, 0xDEADBEEF, 0xFFF});lexAndCheckIntegerTokensAndValues(AsmStr, ExpectedValues);}TEST_F(SystemZAsmLexerLinux, CheckDefaultFloats) {StringRef AsmStr = "0.333\n1.3\n2.5\n3.0\n";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens;for (int I = 0; I < 4; ++I)ExpectedTokens.insert(ExpectedTokens.begin(),{AsmToken::Real, AsmToken::EndOfStatement});ExpectedTokens.push_back(AsmToken::Eof);lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerLinux, CheckDefaultQuestionAtStartOfIdentifier) {StringRef AsmStr = "?lh1?23";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Error, AsmToken::Identifier, AsmToken::EndOfStatement,AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerLinux, CheckDefaultAtAtStartOfIdentifier) {StringRef AsmStr = "@@lh1?23";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::At, AsmToken::At, AsmToken::Identifier,AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckAcceptAtAtStartOfIdentifier) {StringRef AsmStr = "@@lh1?23";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerLinux, CheckDefaultDollarAtStartOfIdentifier) {StringRef AsmStr = "$$ac$c";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Dollar, AsmToken::Dollar, AsmToken::Identifier,AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckAcceptDollarAtStartOfIdentifier) {StringRef AsmStr = "$$ab$c";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckAcceptHashAtStartOfIdentifier) {StringRef AsmStr = "##a#b$c";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerLinux, CheckAcceptHashAtStartOfIdentifier2) {StringRef AsmStr = "##a#b$c";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();// By default, the CommentString attribute is set to "#".// Hence, "##a#b$c" is lexed as a line comment irrespective// of whether the AllowHashAtStartOfIdentifier attribute is set to true.SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckAcceptHashAtStartOfIdentifier3) {StringRef AsmStr = "##a#b$c";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckAcceptHashAtStartOfIdentifier4) {StringRef AsmStr = "##a#b$c";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();// Since, the AllowAdditionalComments attribute is set to false,// only strings starting with the CommentString attribute are// lexed as possible comments.// Hence, "##a$b$c" is lexed as an Identifier because the// AllowHashAtStartOfIdentifier attribute is set to true.SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckRejectDotAsCurrentPC) {StringRef AsmStr = ".-4";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();const MCExpr *Expr;bool ParsePrimaryExpr = Parser->parseExpression(Expr);EXPECT_EQ(ParsePrimaryExpr, true);EXPECT_EQ(Parser->hasPendingError(), true);}TEST_F(SystemZAsmLexerLinux, CheckRejectStarAsCurrentPC) {StringRef AsmStr = "*-4";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();const MCExpr *Expr;bool ParsePrimaryExpr = Parser->parseExpression(Expr);EXPECT_EQ(ParsePrimaryExpr, true);EXPECT_EQ(Parser->hasPendingError(), true);}TEST_F(SystemZAsmLexerZOS, CheckRejectCharLiterals) {StringRef AsmStr = "abc 'd'";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::Space, AsmToken::Error, AsmToken::Error,AsmToken::EndOfStatement, AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckRejectStringLiterals) {StringRef AsmStr = "abc \"ef\"";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();SmallVector<AsmToken::TokenKind> ExpectedTokens({AsmToken::Identifier, AsmToken::Space, AsmToken::Error,AsmToken::Identifier, AsmToken::Error, AsmToken::EndOfStatement,AsmToken::Eof});lexAndCheckTokens(AsmStr, ExpectedTokens);}TEST_F(SystemZAsmLexerZOS, CheckPrintAcceptableSymbol) {std::string AsmStr = "ab13_$.@";EXPECT_EQ(true, MAI->isValidUnquotedName(AsmStr));AsmStr += "#";EXPECT_EQ(true, MAI->isValidUnquotedName(AsmStr));}TEST_F(SystemZAsmLexerLinux, CheckPrintAcceptableSymbol) {std::string AsmStr = "ab13_$.";EXPECT_EQ(true, MAI->isValidUnquotedName(AsmStr));AsmStr = "ab13_$.@";EXPECT_EQ(false, MAI->isValidUnquotedName(AsmStr));AsmStr = "ab13_$.#";EXPECT_EQ(false, MAI->isValidUnquotedName(AsmStr));}TEST_F(SystemZAsmLexerZOS, CheckLabelCaseUpperCase) {StringRef AsmStr = "label";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();const MCExpr *Expr;bool ParsePrimaryExpr = Parser->parseExpression(Expr);EXPECT_EQ(ParsePrimaryExpr, false);const MCSymbolRefExpr *SymbolExpr = dyn_cast<MCSymbolRefExpr>(Expr);EXPECT_NE(SymbolExpr, nullptr);EXPECT_NE(&SymbolExpr->getSymbol(), nullptr);EXPECT_EQ((&SymbolExpr->getSymbol())->getName(), StringRef("LABEL"));}TEST_F(SystemZAsmLexerLinux, CheckLabelUpperCase2) {StringRef AsmStr = "label";// Setup.setupCallToAsmParser(AsmStr);// Lex initially to get the string.Parser->getLexer().Lex();const MCExpr *Expr;bool ParsePrimaryExpr = Parser->parseExpression(Expr);EXPECT_EQ(ParsePrimaryExpr, false);const MCSymbolRefExpr *SymbolExpr = dyn_cast<MCSymbolRefExpr>(Expr);EXPECT_NE(SymbolExpr, nullptr);EXPECT_NE(&SymbolExpr->getSymbol(), nullptr);EXPECT_EQ((&SymbolExpr->getSymbol())->getName(), StringRef("label"));}} // end anonymous namespace
include_directories(${LLVM_MAIN_SRC_DIR}/lib/Target/SystemZ)set(LLVM_LINK_COMPONENTSSystemZMCParserMCSupport)add_llvm_unittest(SystemZAsmLexerTestsSystemZAsmLexerTest.cpp)
//===----------- StringTableBuilderTest.cpp -------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/MC/StringTableBuilder.h"#include "llvm/ADT/SmallString.h"#include "llvm/Support/Endian.h"#include "gtest/gtest.h"#include <string>using namespace llvm;namespace {TEST(StringTableBuilderTest, BasicELF) {StringTableBuilder B(StringTableBuilder::ELF);B.add("foo");B.add("bar");B.add("foobar");B.finalize();std::string Expected;Expected += '\x00';Expected += "foobar";Expected += '\x00';Expected += "foo";Expected += '\x00';SmallString<64> Data;raw_svector_ostream OS(Data);B.write(OS);EXPECT_EQ(Expected, Data);EXPECT_EQ(1U, B.getOffset("foobar"));EXPECT_EQ(4U, B.getOffset("bar"));EXPECT_EQ(8U, B.getOffset("foo"));}TEST(StringTableBuilderTest, BasicWinCOFF) {StringTableBuilder B(StringTableBuilder::WinCOFF);// Strings must be 9 chars or longer to go in the table.B.add("hippopotamus");B.add("pygmy hippopotamus");B.add("river horse");B.finalize();// size_field + "pygmy hippopotamus\0" + "river horse\0"uint32_t ExpectedSize = 4 + 19 + 12;EXPECT_EQ(ExpectedSize, B.getSize());std::string Expected;ExpectedSize =support::endian::byte_swap<uint32_t, support::little>(ExpectedSize);Expected.append((const char*)&ExpectedSize, 4);Expected += "pygmy hippopotamus";Expected += '\x00';Expected += "river horse";Expected += '\x00';SmallString<64> Data;raw_svector_ostream OS(Data);B.write(OS);EXPECT_EQ(Expected, Data);EXPECT_EQ(4U, B.getOffset("pygmy hippopotamus"));EXPECT_EQ(10U, B.getOffset("hippopotamus"));EXPECT_EQ(23U, B.getOffset("river horse"));}TEST(StringTableBuilderTest, ELFInOrder) {StringTableBuilder B(StringTableBuilder::ELF);EXPECT_EQ(1U, B.add("foo"));EXPECT_EQ(5U, B.add("bar"));EXPECT_EQ(9U, B.add("foobar"));B.finalizeInOrder();std::string Expected;Expected += '\x00';Expected += "foo";Expected += '\x00';Expected += "bar";Expected += '\x00';Expected += "foobar";Expected += '\x00';SmallString<64> Data;raw_svector_ostream OS(Data);B.write(OS);EXPECT_EQ(Expected, Data);EXPECT_EQ(1U, B.getOffset("foo"));EXPECT_EQ(5U, B.getOffset("bar"));EXPECT_EQ(9U, B.getOffset("foobar"));}TEST(StringTableBuilderTest, MachOInOrder) {StringTableBuilder B(StringTableBuilder::MachO);B.add("foo");B.add("bar");B.add("fooba");B.finalizeInOrder();std::string Expected;Expected += '\x00';Expected += "foo";Expected += '\x00';Expected += "bar";Expected += '\x00';Expected += "fooba";Expected += '\x00';// Mach-O pads to 4 bytesExpected += '\x00';SmallString<64> Data;raw_svector_ostream OS(Data);B.write(OS);EXPECT_EQ(Expected, Data);EXPECT_EQ(1U, B.getOffset("foo"));EXPECT_EQ(5U, B.getOffset("bar"));EXPECT_EQ(9U, B.getOffset("fooba"));}TEST(StringTableBuilderTest, MachO64InOrder) {StringTableBuilder B(StringTableBuilder::MachO64);B.add("foo");B.add("bar");B.add("f");B.finalizeInOrder();std::string Expected;Expected += '\x00';Expected += "foo";Expected += '\x00';Expected += "bar";Expected += '\x00';Expected += "f";Expected += '\x00';// 64 bit Mach-O pads to 8 bytesExpected += '\x00';Expected += '\x00';Expected += '\x00';Expected += '\x00';Expected += '\x00';SmallString<64> Data;raw_svector_ostream OS(Data);B.write(OS);EXPECT_EQ(Expected, Data);EXPECT_EQ(1U, B.getOffset("foo"));EXPECT_EQ(5U, B.getOffset("bar"));EXPECT_EQ(9U, B.getOffset("f"));}TEST(StringTableBuilderTest, MachOLinkedInOrder) {StringTableBuilder B(StringTableBuilder::MachOLinked);B.add("foo");B.add("bar");B.add("foob");B.finalizeInOrder();std::string Expected;Expected += ' ';Expected += '\x00';Expected += "foo";Expected += '\x00';Expected += "bar";Expected += '\x00';Expected += "foob";Expected += '\x00';// Mach-O pads to 4 bytesExpected += '\x00';SmallString<64> Data;raw_svector_ostream OS(Data);B.write(OS);EXPECT_EQ(Expected, Data);EXPECT_EQ(2U, B.getOffset("foo"));EXPECT_EQ(6U, B.getOffset("bar"));EXPECT_EQ(10U, B.getOffset("foob"));}TEST(StringTableBuilderTest, MachO64LinkedInOrder) {StringTableBuilder B(StringTableBuilder::MachO64Linked);B.add("foo");B.add("ba");B.add("f");B.finalizeInOrder();std::string Expected;Expected += ' ';Expected += '\x00';Expected += "foo";Expected += '\x00';Expected += "ba";Expected += '\x00';Expected += "f";Expected += '\x00';// 64 bit Mach-O pads to 8 bytesExpected += '\x00';Expected += '\x00';Expected += '\x00';Expected += '\x00';Expected += '\x00';SmallString<64> Data;raw_svector_ostream OS(Data);B.write(OS);EXPECT_EQ(Expected, Data);EXPECT_EQ(2U, B.getOffset("foo"));EXPECT_EQ(6U, B.getOffset("ba"));EXPECT_EQ(9U, B.getOffset("f"));}}
//===- llvm/unittest/MC/MCInstPrinter.cpp ---------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/MC/MCInstPrinter.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;namespace {class MCInstPrinterTest : public ::testing::Test {public:std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<const MCInstrInfo> MII;std::unique_ptr<MCInstPrinter> Printer;MCInstPrinterTest() {llvm::InitializeAllTargetInfos();llvm::InitializeAllTargetMCs();std::string TripleName = "x86_64-pc-linux";std::string ErrorStr;const Target *TheTarget =TargetRegistry::lookupTarget(TripleName, ErrorStr);// If we didn't build x86, do not run the test.if (!TheTarget)return;MRI.reset(TheTarget->createMCRegInfo(TripleName));MCTargetOptions MCOptions;MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));MII.reset(TheTarget->createMCInstrInfo());Printer.reset(TheTarget->createMCInstPrinter(Triple(TripleName), MAI->getAssemblerDialect(), *MAI, *MII, *MRI));}template <typename T> std::string formatHex(T i) {std::string Buffer;raw_string_ostream OS(Buffer);OS << Printer->formatHex(i);OS.flush();return Buffer;}};} // namespaceTEST_F(MCInstPrinterTest, formatHex) {if (!Printer)return;EXPECT_EQ("0x1", formatHex<int64_t>(1));EXPECT_EQ("0x7fffffffffffffff",formatHex(std::numeric_limits<int64_t>::max()));EXPECT_EQ("-0x8000000000000000",formatHex(std::numeric_limits<int64_t>::min()));}
//===- MCDisassemblerTest.cpp - Tests for MCDisassembler.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/MC/MCDisassembler/MCDisassembler.h"#include "gtest/gtest.h"using namespace llvm;TEST(MCDisassembler, XCOFFSymbolPriorityTest) {SymbolInfoTy SIT1(0x100000, "sym1", None, 1, false);SymbolInfoTy SIT2(0x110000, "sym2", None, 2, false);SymbolInfoTy SIT3(0x120000, ".func", XCOFF::XMC_PR, 3, true);SymbolInfoTy SIT4(0x120000, ".text", XCOFF::XMC_PR, 4, false);SymbolInfoTy SIT5(0x130000, "TOC", XCOFF::XMC_TC0, 5, false);SymbolInfoTy SIT6(0x130000, "func", XCOFF::XMC_TC, 6, false);// Test that higher addresses would appear later than lower ones when symbols// are sorted in ascending order.EXPECT_TRUE(SIT1 < SIT2);EXPECT_FALSE(SIT2 < SIT1);// Test that symbols with a StorageMappingClass have higher priority than those// without.EXPECT_TRUE(SIT2 < SIT5);EXPECT_FALSE(SIT5 < SIT2);// Test that symbols with a TC0 StorageMappingClass have lower priority than those// with some other StorageMappingClass.EXPECT_TRUE(SIT5 < SIT6);EXPECT_FALSE(SIT6 < SIT5);// Test label symbols have higher priorty than non-label symbols.EXPECT_TRUE(SIT4 < SIT3);EXPECT_FALSE(SIT3 < SIT4);// Test symbols comparing with themselves.EXPECT_FALSE(SIT1 < SIT1);EXPECT_FALSE(SIT2 < SIT2);EXPECT_FALSE(SIT3 < SIT3);EXPECT_FALSE(SIT4 < SIT4);EXPECT_FALSE(SIT5 < SIT5);EXPECT_FALSE(SIT6 < SIT6);}
//===- llvm/unittest/MC/DwarfLineTables.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCDwarf.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct Context {const char *TripleName = "x86_64-pc-linux";std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<MCContext> Ctx;Context() {llvm::InitializeAllTargetInfos();llvm::InitializeAllTargetMCs();llvm::InitializeAllDisassemblers();// If we didn't build x86, do not run the test.std::string Error;const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);if (!TheTarget)return;MRI.reset(TheTarget->createMCRegInfo(TripleName));MCTargetOptions MCOptions;MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),/*MSTI=*/nullptr);}operator bool() { return Ctx.get(); }operator MCContext &() { return *Ctx; };};Context &getContext() {static Context Ctxt;return Ctxt;}}void verifyEncoding(MCDwarfLineTableParams Params, int LineDelta, int AddrDelta,ArrayRef<uint8_t> ExpectedEncoding) {SmallString<16> Buffer;raw_svector_ostream EncodingOS(Buffer);MCDwarfLineAddr::Encode(getContext(), Params, LineDelta, AddrDelta,EncodingOS);EXPECT_EQ(ExpectedEncoding, arrayRefFromStringRef(Buffer));}TEST(DwarfLineTables, TestDefaultParams) {if (!getContext())return;MCDwarfLineTableParams Params;// Minimal line offset expressible through extended opcode, 0 addr deltaconst uint8_t Encoding0[] = {13}; // Special opcode Addr += 0, Line += -5verifyEncoding(Params, -5, 0, Encoding0);// Maximal line offset expressible through extended opcode,const uint8_t Encoding1[] = {26}; // Special opcode Addr += 0, Line += +8verifyEncoding(Params, 8, 0, Encoding1);// Random value in the middle of the special ocode rangeconst uint8_t Encoding2[] = {146}; // Special opcode Addr += 9, Line += 2verifyEncoding(Params, 2, 9, Encoding2);// Minimal line offset expressible through extended opcode, max addr deltaconst uint8_t Encoding3[] = {251}; // Special opcode Addr += 17, Line += -5verifyEncoding(Params, -5, 17, Encoding3);// Biggest special opcodeconst uint8_t Encoding4[] = {255}; // Special opcode Addr += 17, Line += -1verifyEncoding(Params, -1, 17, Encoding4);// Line delta outside of the special opcode range, address delta in rangeconst uint8_t Encoding5[] = {dwarf::DW_LNS_advance_line, 9,158}; // Special opcode Addr += 10, Line += 0verifyEncoding(Params, 9, 10, Encoding5);// Address delta outside of the special opcode range, but small// enough to do DW_LNS_const_add_pc + special opcode.const uint8_t Encoding6[] = {dwarf::DW_LNS_const_add_pc, // pc += 1762}; // Special opcode Addr += 3, Line += 2verifyEncoding(Params, 2, 20, Encoding6);// Address delta big enough to require the use of DW_LNS_advance_pc// Line delta in special opcode rangeconst uint8_t Encoding7[] = {dwarf::DW_LNS_advance_pc, 100,20}; // Special opcode Addr += 0, Line += 2verifyEncoding(Params, 2, 100, Encoding7);// No special opcode possible.const uint8_t Encoding8[] = {dwarf::DW_LNS_advance_line, 20,dwarf::DW_LNS_advance_pc, 100,dwarf::DW_LNS_copy};verifyEncoding(Params, 20, 100, Encoding8);}TEST(DwarfLineTables, TestCustomParams) {if (!getContext())return;// Some tests against the example values given in the standard.MCDwarfLineTableParams Params;Params.DWARF2LineOpcodeBase = 13;Params.DWARF2LineBase = -3;Params.DWARF2LineRange = 12;// Minimal line offset expressible through extended opcode, 0 addr deltaconst uint8_t Encoding0[] = {13}; // Special opcode Addr += 0, Line += -5verifyEncoding(Params, -3, 0, Encoding0);// Maximal line offset expressible through extended opcode,const uint8_t Encoding1[] = {24}; // Special opcode Addr += 0, Line += +8verifyEncoding(Params, 8, 0, Encoding1);// Random value in the middle of the special ocode rangeconst uint8_t Encoding2[] = {126}; // Special opcode Addr += 9, Line += 2verifyEncoding(Params, 2, 9, Encoding2);// Minimal line offset expressible through extended opcode, max addr deltaconst uint8_t Encoding3[] = {253}; // Special opcode Addr += 20, Line += -3verifyEncoding(Params, -3, 20, Encoding3);// Biggest special opcodeconst uint8_t Encoding4[] = {255}; // Special opcode Addr += 17, Line += -1verifyEncoding(Params, -1, 20, Encoding4);// Line delta outside of the special opcode range, address delta in rangeconst uint8_t Encoding5[] = {dwarf::DW_LNS_advance_line, 9,136}; // Special opcode Addr += 10, Line += 0verifyEncoding(Params, 9, 10, Encoding5);// Address delta outside of the special opcode range, but small// enough to do DW_LNS_const_add_pc + special opcode.const uint8_t Encoding6[] = {dwarf::DW_LNS_const_add_pc, // pc += 20138}; // Special opcode Addr += 10, Line += 2verifyEncoding(Params, 2, 30, Encoding6);// Address delta big enough to require the use of DW_LNS_advance_pc// Line delta in special opcode rangeconst uint8_t Encoding7[] = {dwarf::DW_LNS_advance_pc, 100,18}; // Special opcode Addr += 0, Line += 2verifyEncoding(Params, 2, 100, Encoding7);// No special opcode possible.const uint8_t Encoding8[] = {dwarf::DW_LNS_advance_line, 20,dwarf::DW_LNS_advance_pc, 100,dwarf::DW_LNS_copy};verifyEncoding(Params, 20, 100, Encoding8);}TEST(DwarfLineTables, TestCustomParams2) {if (!getContext())return;// Corner case param values.MCDwarfLineTableParams Params;Params.DWARF2LineOpcodeBase = 13;Params.DWARF2LineBase = 1;Params.DWARF2LineRange = 255;const uint8_t Encoding0[] = {dwarf::DW_LNS_advance_line, 248, 1,dwarf::DW_LNS_copy};verifyEncoding(Params, 248, 0, Encoding0);}
//===- llvm/unittest/MC/DwarfLineTableHeaders.cpp -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/MC/MCAsmBackend.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCAssembler.h"#include "llvm/MC/MCCodeEmitter.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCDwarf.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCObjectStreamer.h"#include "llvm/MC/MCObjectWriter.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCStreamer.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Object/Binary.h"#include "llvm/Object/ELFObjectFile.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Support/ToolOutputFile.h"#include "gtest/gtest.h"using namespace llvm;namespace {class DwarfLineTableHeaders : public ::testing::Test {public:const char *TripleName = "x86_64-pc-linux";std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<const MCSubtargetInfo> STI;const Target *TheTarget;struct StreamerContext {std::unique_ptr<MCObjectFileInfo> MOFI;std::unique_ptr<MCContext> Ctx;std::unique_ptr<const MCInstrInfo> MII;std::unique_ptr<MCStreamer> Streamer;};DwarfLineTableHeaders() {llvm::InitializeAllTargetInfos();llvm::InitializeAllTargetMCs();llvm::InitializeAllDisassemblers();// If we didn't build x86, do not run the test.std::string Error;TheTarget = TargetRegistry::lookupTarget(TripleName, Error);if (!TheTarget)return;MRI.reset(TheTarget->createMCRegInfo(TripleName));MCTargetOptions MCOptions;MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));}/// Create all data structures necessary to operate an assemblerStreamerContext createStreamer(raw_pwrite_stream &OS) {StreamerContext Res;Res.Ctx =std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),/*MSTI=*/nullptr);Res.MOFI.reset(TheTarget->createMCObjectFileInfo(*Res.Ctx.get(),/*PIC=*/false));Res.Ctx->setObjectFileInfo(Res.MOFI.get());Res.MII.reset(TheTarget->createMCInstrInfo());MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*Res.MII, *Res.Ctx);MCAsmBackend *MAB =TheTarget->createMCAsmBackend(*STI, *MRI, MCTargetOptions());std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);Res.Streamer.reset(TheTarget->createMCObjectStreamer(Triple(TripleName), *Res.Ctx, std::unique_ptr<MCAsmBackend>(MAB),std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *STI,/* RelaxAll */ false,/* IncrementalLinkerCompatible */ false,/* DWARFMustBeAtTheEnd */ false));return Res;}/// Emit a .debug_line section with the given context parametersvoid emitDebugLineSection(StreamerContext &C) {MCContext &Ctx = *C.Ctx;MCStreamer *TheStreamer = C.Streamer.get();MCAssembler &Assembler =static_cast<MCObjectStreamer *>(TheStreamer)->getAssembler();TheStreamer->initSections(false, *STI);// Create a mock functionMCSection *Section = C.MOFI->getTextSection();Section->setHasInstructions(true);TheStreamer->switchSection(Section);TheStreamer->emitCFIStartProc(true);// Create a mock dwarflocCtx.setCurrentDwarfLoc(/*FileNo=*/0, /*Line=*/1, /*Column=*/1, /*Flags=*/0,/*Isa=*/0, /*Discriminator=*/0);MCDwarfLoc Loc = Ctx.getCurrentDwarfLoc();MCSymbol *LineSym = Ctx.createTempSymbol();// Set the value of the symbol to use for the MCDwarfLineEntry.TheStreamer->emitLabel(LineSym);TheStreamer->emitNops(4, 1, SMLoc(), *STI);TheStreamer->emitCFIEndProc();// Start emission of .debug_lineTheStreamer->switchSection(C.MOFI->getDwarfLineSection());MCDwarfLineTableHeader Header;MCDwarfLineTableParams Params = Assembler.getDWARFLinetableParams();Optional<MCDwarfLineStr> LineStr(None);if (Ctx.getDwarfVersion() >= 5) {LineStr.emplace(Ctx);Header.setRootFile("dir", "file", None, None);}MCSymbol *LineEndSym = Header.Emit(TheStreamer, Params, LineStr).second;// Put out the line tables.MCLineSection::MCDwarfLineEntryCollection LineEntries;MCDwarfLineEntry LineEntry(LineSym, Loc);LineEntries.push_back(LineEntry);MCDwarfLineTable::emitOne(TheStreamer, Section, LineEntries);TheStreamer->emitLabel(LineEndSym);if (LineStr) {SmallString<0> Data = LineStr->getFinalizedData();TheStreamer->switchSection(TheStreamer->getContext().getObjectFileInfo()->getDwarfLineStrSection());TheStreamer->emitBinaryData(Data.str());}}/// Check contents of .debug_line sectionvoid verifyDebugLineContents(const llvm::object::ObjectFile &E,ArrayRef<uint8_t> ExpectedEncoding) {for (const llvm::object::SectionRef &Section : E.sections()) {Expected<StringRef> SectionNameOrErr = Section.getName();ASSERT_TRUE(static_cast<bool>(SectionNameOrErr));StringRef SectionName = *SectionNameOrErr;if (SectionName.empty() || SectionName != ".debug_line")continue;Expected<StringRef> ContentsOrErr = Section.getContents();ASSERT_TRUE(static_cast<bool>(ContentsOrErr));StringRef Contents = *ContentsOrErr;ASSERT_TRUE(Contents.size() > ExpectedEncoding.size());EXPECT_EQ(arrayRefFromStringRef(Contents.slice(0, ExpectedEncoding.size())),ExpectedEncoding);return;}llvm_unreachable(".debug_line not found");}/// Check contents of .debug_line_str sectionvoid verifyDebugLineStrContents(const llvm::object::ObjectFile &E) {for (const llvm::object::SectionRef &Section : E.sections()) {Expected<StringRef> SectionNameOrErr = Section.getName();ASSERT_TRUE(static_cast<bool>(SectionNameOrErr));StringRef SectionName = *SectionNameOrErr;if (SectionName.empty() || SectionName != ".debug_line_str")continue;Expected<StringRef> ContentsOrErr = Section.getContents();ASSERT_TRUE(static_cast<bool>(ContentsOrErr));StringRef Contents = *ContentsOrErr;ASSERT_TRUE(Contents.find("dir") != StringRef::npos);ASSERT_TRUE(Contents.find("file") != StringRef::npos);ASSERT_TRUE(Contents.size() == 9);return;}llvm_unreachable(".debug_line_str not found");}/// Open ObjFileData as an object file and read its .debug_line sectionvoid readAndCheckDebugContents(StringRef ObjFileData,ArrayRef<uint8_t> Expected, uint8_t DwarfVersion) {std::unique_ptr<MemoryBuffer> MB =MemoryBuffer::getMemBuffer(ObjFileData, "", false);std::unique_ptr<object::Binary> Bin =cantFail(llvm::object::createBinary(MB->getMemBufferRef()));if (auto *E = dyn_cast<llvm::object::ELFObjectFileBase>(&*Bin)) {verifyDebugLineContents(*E, Expected);if (DwarfVersion >= 5)verifyDebugLineStrContents(*E);return;}llvm_unreachable("ELF object file not found");}};} // namespaceTEST_F(DwarfLineTableHeaders, TestDWARF4HeaderEmission) {if (!MRI)return;SmallString<0> EmittedBinContents;raw_svector_ostream VecOS(EmittedBinContents);StreamerContext C = createStreamer(VecOS);constexpr uint8_t DwarfVersion = 4;C.Ctx->setDwarfVersion(DwarfVersion);emitDebugLineSection(C);C.Streamer->finish();readAndCheckDebugContents(EmittedBinContents.str(),{/* Total length=*/0x30, 0, 0, 0,/* DWARF version=*/DwarfVersion, 0,/* Prologue length=*/0x14, 0, 0, 0,/* min_inst_length=*/1,/*max_ops_per_inst=*/1,/* default_is_stmt=*/DWARF2_LINE_DEFAULT_IS_STMT,/* line_base=*/static_cast<uint8_t>(-5),/* line_range=*/14,/* opcode_base=*/13}, DwarfVersion);}TEST_F(DwarfLineTableHeaders, TestDWARF5HeaderEmission) {if (!MRI)return;SmallString<0> EmittedBinContents;raw_svector_ostream VecOS(EmittedBinContents);StreamerContext C = createStreamer(VecOS);constexpr uint8_t DwarfVersion = 5;C.Ctx->setDwarfVersion(DwarfVersion);emitDebugLineSection(C);C.Streamer->finish();readAndCheckDebugContents(EmittedBinContents.str(),{/* Total length=*/0x43, 0, 0, 0,/* DWARF version=*/DwarfVersion, 0,/* ptr size=*/8,/* segment=*/0,/* Prologue length=*/0x25, 0, 0, 0,/* min_inst_length=*/1,/*max_ops_per_inst=*/1,/* default_is_stmt=*/DWARF2_LINE_DEFAULT_IS_STMT,/* line_base=*/static_cast<uint8_t>(-5),/* line_range=*/14,/* opcode_base=*/13}, DwarfVersion);}
//===- llvm/unittest/Object/Disassembler.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm-c/Disassembler.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"using namespace llvm;static const char *symbolLookupCallback(void *DisInfo, uint64_t ReferenceValue,uint64_t *ReferenceType,uint64_t ReferencePC,const char **ReferenceName) {*ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;return nullptr;}TEST(Disassembler, X86Test) {llvm::InitializeAllTargetInfos();llvm::InitializeAllTargetMCs();llvm::InitializeAllDisassemblers();uint8_t Bytes[] = {0x90, 0x90, 0xeb, 0xfd};uint8_t *BytesP = Bytes;const char OutStringSize = 100;char OutString[OutStringSize];LLVMDisasmContextRef DCR = LLVMCreateDisasm("x86_64-pc-linux", nullptr, 0,nullptr, symbolLookupCallback);if (!DCR)return;size_t InstSize;unsigned NumBytes = sizeof(Bytes);unsigned PC = 0;InstSize =LLVMDisasmInstruction(DCR, BytesP, 0, PC, OutString, OutStringSize);EXPECT_EQ(InstSize, 0U);InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,OutStringSize);EXPECT_EQ(InstSize, 1U);EXPECT_EQ(StringRef(OutString), "\tnop");PC += InstSize;BytesP += InstSize;NumBytes -= InstSize;InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,OutStringSize);EXPECT_EQ(InstSize, 1U);EXPECT_EQ(StringRef(OutString), "\tnop");PC += InstSize;BytesP += InstSize;NumBytes -= InstSize;InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,OutStringSize);EXPECT_EQ(InstSize, 2U);EXPECT_EQ(StringRef(OutString), "\tjmp\t0x1");LLVMDisasmDispose(DCR);}TEST(Disassembler, WebAssemblyTest) {llvm::InitializeAllTargetInfos();llvm::InitializeAllTargetMCs();llvm::InitializeAllDisassemblers();uint8_t Bytes[] = {0x6a, 0x42, 0x7F, 0x35, 0x01, 0x10};uint8_t *BytesP = Bytes;const char OutStringSize = 100;char OutString[OutStringSize];LLVMDisasmContextRef DCR = LLVMCreateDisasm("wasm32-unknown-unknown", nullptr,0, nullptr, symbolLookupCallback);if (!DCR)return;size_t InstSize;unsigned NumBytes = sizeof(Bytes);unsigned PC = 0;InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,OutStringSize);EXPECT_EQ(InstSize, 1U);EXPECT_EQ(StringRef(OutString), "\ti32.add ");PC += InstSize;BytesP += InstSize;NumBytes -= InstSize;InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,OutStringSize);EXPECT_EQ(InstSize, 2U);EXPECT_EQ(StringRef(OutString), "\ti64.const\t-1");PC += InstSize;BytesP += InstSize;NumBytes -= InstSize;InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString,OutStringSize);EXPECT_EQ(InstSize, 3U);EXPECT_EQ(StringRef(OutString), "\ti64.load32_u\t16:p2align=1");LLVMDisasmDispose(DCR);}
foreach(t ${LLVM_TARGETS_TO_BUILD})if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${t})add_subdirectory(${t})endif()endforeach()set(LLVM_LINK_COMPONENTS${LLVM_TARGETS_TO_BUILD}MCMCDisassemblerObjectSupport)add_llvm_unittest(MCTestsDisassembler.cppDwarfLineTables.cppDwarfLineTableHeaders.cppMCInstPrinter.cppStringTableBuilderTest.cppTargetRegistry.cppMCDisassemblerTest.cpp)
//===- llvm/unittests/MC/AMDGPU/DwarfRegMappings.cpp ----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"#include <mutex>#include <thread>using namespace llvm;std::once_flag flag;void InitializeAMDGPUTarget() {std::call_once(flag, []() {LLVMInitializeAMDGPUTargetInfo();LLVMInitializeAMDGPUTarget();LLVMInitializeAMDGPUTargetMC();});}std::unique_ptr<LLVMTargetMachine>createTargetMachine(std::string TStr, StringRef CPU, StringRef FS) {InitializeAMDGPUTarget();std::string Error;const Target *T = TargetRegistry::lookupTarget(TStr, Error);if (!T)return nullptr;TargetOptions Options;return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine(TStr, CPU, FS, Options, None, None)));}TEST(AMDGPUDwarfRegMappingTests, TestWave64DwarfRegMapping) {for (auto Triple :{"amdgcn-amd-", "amdgcn-amd-amdhsa", "amdgcn-amd-amdpal"}) {auto TM = createTargetMachine(Triple, "gfx1010", "+wavefrontsize64");if (TM && TM->getMCRegisterInfo()) {auto MRI = TM->getMCRegisterInfo();// Wave64 Dwarf register mapping test numbers// PC_64 => 16, EXEC_MASK_64 => 17, S0 => 32, S63 => 95,// S64 => 1088, S105 => 1129, V0 => 2560, V255 => 2815,// A0 => 3072, A255 => 3327for (int llvmReg : {16, 17, 32, 95, 1088, 1129, 2560, 2815, 3072, 3327}) {MCRegister PCReg(*MRI->getLLVMRegNum(llvmReg, false));EXPECT_EQ(llvmReg, MRI->getDwarfRegNum(PCReg, false));}}}}TEST(AMDGPUDwarfRegMappingTests, TestWave32DwarfRegMapping) {for (auto Triple :{"amdgcn-amd-", "amdgcn-amd-amdhsa", "amdgcn-amd-amdpal"}) {auto TM = createTargetMachine(Triple, "gfx1010", "+wavefrontsize32");if (TM && TM->getMCRegisterInfo()) {auto MRI = TM->getMCRegisterInfo();// Wave32 Dwarf register mapping test numbers// PC_64 => 16, EXEC_MASK_32 => 1, S0 => 32, S63 => 95,// S64 => 1088, S105 => 1129, V0 => 1536, V255 => 1791,// A0 => 2048, A255 => 2303for (int llvmReg : {16, 1, 32, 95, 1088, 1129, 1536, 1791, 2048, 2303}) {MCRegister PCReg(*MRI->getLLVMRegNum(llvmReg, false));EXPECT_EQ(llvmReg, MRI->getDwarfRegNum(PCReg, false));}}}}
set(LLVM_LINK_COMPONENTSAMDGPUCodeGenAMDGPUDescAMDGPUInfoMCSupport)add_llvm_unittest(AMDGPUDwarfTestsDwarfRegMappings.cpp)
//===- llvm/unittest/Linker/LinkModulesTest.cpp - IRBuilder tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm-c/Core.h"#include "llvm-c/Linker.h"#include "llvm/ADT/STLExtras.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Module.h"#include "llvm/Linker/Linker.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {class LinkModuleTest : public testing::Test {protected:void SetUp() override {M.reset(new Module("MyModule", Ctx));FunctionType *FTy = FunctionType::get(Type::getInt8PtrTy(Ctx), Type::getInt32Ty(Ctx), false /*=isVarArg*/);F = Function::Create(FTy, Function::ExternalLinkage, "ba_func", M.get());F->setCallingConv(CallingConv::C);EntryBB = BasicBlock::Create(Ctx, "entry", F);SwitchCase1BB = BasicBlock::Create(Ctx, "switch.case.1", F);SwitchCase2BB = BasicBlock::Create(Ctx, "switch.case.2", F);ExitBB = BasicBlock::Create(Ctx, "exit", F);AT = ArrayType::get(Type::getInt8PtrTy(Ctx), 3);GV = new GlobalVariable(*M.get(), AT, false /*=isConstant*/,GlobalValue::InternalLinkage, nullptr,"switch.bas");// Global Initializerstd::vector<Constant *> Init;Constant *SwitchCase1BA = BlockAddress::get(SwitchCase1BB);Init.push_back(SwitchCase1BA);Constant *SwitchCase2BA = BlockAddress::get(SwitchCase2BB);Init.push_back(SwitchCase2BA);ConstantInt *One = ConstantInt::get(Type::getInt32Ty(Ctx), 1);Constant *OnePtr = ConstantExpr::getCast(Instruction::IntToPtr, One,Type::getInt8PtrTy(Ctx));Init.push_back(OnePtr);GV->setInitializer(ConstantArray::get(AT, Init));}void TearDown() override { M.reset(); }LLVMContext Ctx;std::unique_ptr<Module> M;Function *F;ArrayType *AT;GlobalVariable *GV;BasicBlock *EntryBB;BasicBlock *SwitchCase1BB;BasicBlock *SwitchCase2BB;BasicBlock *ExitBB;};static void expectNoDiags(const DiagnosticInfo &DI, void *C) {llvm_unreachable("expectNoDiags called!");}TEST_F(LinkModuleTest, BlockAddress) {IRBuilder<> Builder(EntryBB);std::vector<Value *> GEPIndices;GEPIndices.push_back(ConstantInt::get(Type::getInt32Ty(Ctx), 0));GEPIndices.push_back(&*F->arg_begin());Value *GEP = Builder.CreateGEP(AT, GV, GEPIndices, "switch.gep");Value *Load = Builder.CreateLoad(AT->getElementType(), GEP, "switch.load");Builder.CreateRet(Load);Builder.SetInsertPoint(SwitchCase1BB);Builder.CreateBr(ExitBB);Builder.SetInsertPoint(SwitchCase2BB);Builder.CreateBr(ExitBB);Builder.SetInsertPoint(ExitBB);Builder.CreateRet(ConstantPointerNull::get(Type::getInt8PtrTy(Ctx)));Module *LinkedModule = new Module("MyModuleLinked", Ctx);Ctx.setDiagnosticHandlerCallBack(expectNoDiags);Linker::linkModules(*LinkedModule, std::move(M));// Check that the global "@switch.bas" is well-formed.const GlobalVariable *LinkedGV = LinkedModule->getNamedGlobal("switch.bas");const Constant *Init = LinkedGV->getInitializer();// @switch.bas = internal global [3 x i8*]// [i8* blockaddress(@ba_func, %switch.case.1),// i8* blockaddress(@ba_func, %switch.case.2),// i8* inttoptr (i32 1 to i8*)]ArrayType *AT = ArrayType::get(Type::getInt8PtrTy(Ctx), 3);EXPECT_EQ(AT, Init->getType());Value *Elem = Init->getOperand(0);ASSERT_TRUE(isa<BlockAddress>(Elem));EXPECT_EQ(cast<BlockAddress>(Elem)->getFunction(),LinkedModule->getFunction("ba_func"));EXPECT_EQ(cast<BlockAddress>(Elem)->getBasicBlock()->getParent(),LinkedModule->getFunction("ba_func"));Elem = Init->getOperand(1);ASSERT_TRUE(isa<BlockAddress>(Elem));EXPECT_EQ(cast<BlockAddress>(Elem)->getFunction(),LinkedModule->getFunction("ba_func"));EXPECT_EQ(cast<BlockAddress>(Elem)->getBasicBlock()->getParent(),LinkedModule->getFunction("ba_func"));delete LinkedModule;}static Module *getExternal(LLVMContext &Ctx, StringRef FuncName) {// Create a module with an empty externally-linked functionModule *M = new Module("ExternalModule", Ctx);FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), Type::getInt8PtrTy(Ctx), false /*=isVarArgs*/);Function *F =Function::Create(FTy, Function::ExternalLinkage, FuncName, M);F->setCallingConv(CallingConv::C);BasicBlock *BB = BasicBlock::Create(Ctx, "", F);IRBuilder<> Builder(BB);Builder.CreateRetVoid();return M;}static Module *getInternal(LLVMContext &Ctx) {Module *InternalM = new Module("InternalModule", Ctx);FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), Type::getInt8PtrTy(Ctx), false /*=isVarArgs*/);Function *F =Function::Create(FTy, Function::InternalLinkage, "bar", InternalM);F->setCallingConv(CallingConv::C);BasicBlock *BB = BasicBlock::Create(Ctx, "", F);IRBuilder<> Builder(BB);Builder.CreateRetVoid();StructType *STy = StructType::create(Ctx, PointerType::get(FTy, 0));GlobalVariable *GV =new GlobalVariable(*InternalM, STy, false /*=isConstant*/,GlobalValue::InternalLinkage, nullptr, "g");GV->setInitializer(ConstantStruct::get(STy, F));return InternalM;}TEST_F(LinkModuleTest, EmptyModule) {std::unique_ptr<Module> InternalM(getInternal(Ctx));std::unique_ptr<Module> EmptyM(new Module("EmptyModule1", Ctx));Ctx.setDiagnosticHandlerCallBack(expectNoDiags);Linker::linkModules(*EmptyM, std::move(InternalM));}TEST_F(LinkModuleTest, EmptyModule2) {std::unique_ptr<Module> InternalM(getInternal(Ctx));std::unique_ptr<Module> EmptyM(new Module("EmptyModule1", Ctx));Ctx.setDiagnosticHandlerCallBack(expectNoDiags);Linker::linkModules(*InternalM, std::move(EmptyM));}TEST_F(LinkModuleTest, TypeMerge) {LLVMContext C;SMDiagnostic Err;const char *M1Str = "%t = type {i32}\n""@t1 = weak global %t zeroinitializer\n";std::unique_ptr<Module> M1 = parseAssemblyString(M1Str, Err, C);const char *M2Str = "%t = type {i32}\n""@t2 = weak global %t zeroinitializer\n";std::unique_ptr<Module> M2 = parseAssemblyString(M2Str, Err, C);Ctx.setDiagnosticHandlerCallBack(expectNoDiags);Linker::linkModules(*M1, std::move(M2));EXPECT_EQ(M1->getNamedGlobal("t1")->getType(),M1->getNamedGlobal("t2")->getType());}TEST_F(LinkModuleTest, NewCAPISuccess) {std::unique_ptr<Module> DestM(getExternal(Ctx, "foo"));std::unique_ptr<Module> SourceM(getExternal(Ctx, "bar"));LLVMBool Result =LLVMLinkModules2(wrap(DestM.get()), wrap(SourceM.release()));EXPECT_EQ(0, Result);// "bar" is present in destination moduleEXPECT_NE(nullptr, DestM->getFunction("bar"));}static void diagnosticHandler(LLVMDiagnosticInfoRef DI, void *C) {auto *Err = reinterpret_cast<std::string *>(C);char *CErr = LLVMGetDiagInfoDescription(DI);*Err = CErr;LLVMDisposeMessage(CErr);}TEST_F(LinkModuleTest, NewCAPIFailure) {// Symbol clash between two modulesLLVMContext Ctx;std::string Err;LLVMContextSetDiagnosticHandler(wrap(&Ctx), diagnosticHandler, &Err);std::unique_ptr<Module> DestM(getExternal(Ctx, "foo"));std::unique_ptr<Module> SourceM(getExternal(Ctx, "foo"));LLVMBool Result =LLVMLinkModules2(wrap(DestM.get()), wrap(SourceM.release()));EXPECT_EQ(1, Result);EXPECT_EQ("Linking globals named 'foo': symbol multiply defined!", Err);}TEST_F(LinkModuleTest, MoveDistinctMDs) {LLVMContext C;SMDiagnostic Err;const char *SrcStr = "define void @foo() !attach !0 {\n""entry:\n"" call void @llvm.md(metadata !1)\n"" ret void, !attach !2\n""}\n""declare void @llvm.md(metadata)\n""!named = !{!3, !4}\n""!0 = distinct !{}\n""!1 = distinct !{}\n""!2 = distinct !{}\n""!3 = distinct !{}\n""!4 = !{!3}\n";std::unique_ptr<Module> Src = parseAssemblyString(SrcStr, Err, C);assert(Src);ASSERT_TRUE(Src.get());// Get the addresses of the Metadata before merging.Function *F = &*Src->begin();ASSERT_EQ("foo", F->getName());BasicBlock *BB = &F->getEntryBlock();auto *CI = cast<CallInst>(&BB->front());auto *RI = cast<ReturnInst>(BB->getTerminator());NamedMDNode *NMD = &*Src->named_metadata_begin();MDNode *M0 = F->getMetadata("attach");MDNode *M1 =cast<MDNode>(cast<MetadataAsValue>(CI->getArgOperand(0))->getMetadata());MDNode *M2 = RI->getMetadata("attach");MDNode *M3 = NMD->getOperand(0);MDNode *M4 = NMD->getOperand(1);// Confirm a few things about the IR.EXPECT_TRUE(M0->isDistinct());EXPECT_TRUE(M1->isDistinct());EXPECT_TRUE(M2->isDistinct());EXPECT_TRUE(M3->isDistinct());EXPECT_TRUE(M4->isUniqued());EXPECT_EQ(M3, M4->getOperand(0));// Link into destination module.auto Dst = std::make_unique<Module>("Linked", C);ASSERT_TRUE(Dst.get());Ctx.setDiagnosticHandlerCallBack(expectNoDiags);Linker::linkModules(*Dst, std::move(Src));// Check that distinct metadata was moved, not cloned. Even !4, the uniqued// node, should effectively be moved, since its only operand hasn't changed.F = &*Dst->begin();BB = &F->getEntryBlock();CI = cast<CallInst>(&BB->front());RI = cast<ReturnInst>(BB->getTerminator());NMD = &*Dst->named_metadata_begin();EXPECT_EQ(M0, F->getMetadata("attach"));EXPECT_EQ(M1, cast<MetadataAsValue>(CI->getArgOperand(0))->getMetadata());EXPECT_EQ(M2, RI->getMetadata("attach"));EXPECT_EQ(M3, NMD->getOperand(0));EXPECT_EQ(M4, NMD->getOperand(1));// Confirm a few things about the IR. This shouldn't have changed.EXPECT_TRUE(M0->isDistinct());EXPECT_TRUE(M1->isDistinct());EXPECT_TRUE(M2->isDistinct());EXPECT_TRUE(M3->isDistinct());EXPECT_TRUE(M4->isUniqued());EXPECT_EQ(M3, M4->getOperand(0));}TEST_F(LinkModuleTest, RemangleIntrinsics) {LLVMContext C;SMDiagnostic Err;// We load two modules inside the same context C. In both modules there is a// "struct.rtx_def" type. In the module loaded the second (Bar) this type will// be renamed to "struct.rtx_def.0". Check that the intrinsics which have this// type in the signature are properly remangled.const char *FooStr ="%struct.rtx_def = type { i16 }\n""define void @foo(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"" call void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"" ret void\n""}\n""declare void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";const char *BarStr ="%struct.rtx_def = type { i16 }\n""define void @bar(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"" call void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"" ret void\n""}\n""declare void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";std::unique_ptr<Module> Foo = parseAssemblyString(FooStr, Err, C);assert(Foo);ASSERT_TRUE(Foo.get());// Foo is loaded first, so the type and the intrinsic have theis original// names.ASSERT_TRUE(Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.i32"));ASSERT_FALSE(Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.0.i32"));std::unique_ptr<Module> Bar = parseAssemblyString(BarStr, Err, C);assert(Bar);ASSERT_TRUE(Bar.get());// Bar is loaded after Foo, so the type is renamed to struct.rtx_def.0. Check// that the intrinsic is also renamed.ASSERT_FALSE(Bar->getFunction("llvm.memset.p0s_struct.rtx_defs.i32"));ASSERT_TRUE(Bar->getFunction("llvm.memset.p0s_struct.rtx_def.0s.i32"));// Link two modules together.auto Dst = std::make_unique<Module>("Linked", C);ASSERT_TRUE(Dst.get());Ctx.setDiagnosticHandlerCallBack(expectNoDiags);bool Failed = Linker::linkModules(*Foo, std::move(Bar));ASSERT_FALSE(Failed);// "struct.rtx_def" from Foo and "struct.rtx_def.0" from Bar are isomorphic// types, so they must be uniquified by linker. Check that they use the same// intrinsic definition.Function *F = Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.i32");ASSERT_EQ(F->getNumUses(), (unsigned)2);}} // end anonymous namespace
set(LLVM_LINK_COMPONENTSAsmParsercorelinker)add_llvm_unittest(LinkerTestsLinkModulesTest.cpp)
//===-- LineEditor.cpp ----------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/LineEditor/LineEditor.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "gtest/gtest.h"using namespace llvm;class LineEditorTest : public testing::Test {public:SmallString<64> HistPath;LineEditor *LE;LineEditorTest() {init();}void init() {sys::fs::createTemporaryFile("temp", "history", HistPath);ASSERT_FALSE(HistPath.empty());LE = new LineEditor("test", HistPath);}~LineEditorTest() override {delete LE;sys::fs::remove(HistPath.str());}};TEST_F(LineEditorTest, HistorySaveLoad) {LE->saveHistory();LE->loadHistory();}struct TestListCompleter {std::vector<LineEditor::Completion> Completions;TestListCompleter(const std::vector<LineEditor::Completion> &Completions): Completions(Completions) {}std::vector<LineEditor::Completion> operator()(StringRef Buffer,size_t Pos) const {EXPECT_TRUE(Buffer.empty());EXPECT_EQ(0u, Pos);return Completions;}};TEST_F(LineEditorTest, ListCompleters) {std::vector<LineEditor::Completion> Comps;Comps.push_back(LineEditor::Completion("foo", "int foo()"));LE->setListCompleter(TestListCompleter(Comps));LineEditor::CompletionAction CA = LE->getCompletionAction("", 0);EXPECT_EQ(LineEditor::CompletionAction::AK_Insert, CA.Kind);EXPECT_EQ("foo", CA.Text);Comps.push_back(LineEditor::Completion("bar", "int bar()"));LE->setListCompleter(TestListCompleter(Comps));CA = LE->getCompletionAction("", 0);EXPECT_EQ(LineEditor::CompletionAction::AK_ShowCompletions, CA.Kind);ASSERT_EQ(2u, CA.Completions.size());ASSERT_EQ("int foo()", CA.Completions[0]);ASSERT_EQ("int bar()", CA.Completions[1]);Comps.clear();Comps.push_back(LineEditor::Completion("fee", "int fee()"));Comps.push_back(LineEditor::Completion("fi", "int fi()"));Comps.push_back(LineEditor::Completion("foe", "int foe()"));Comps.push_back(LineEditor::Completion("fum", "int fum()"));LE->setListCompleter(TestListCompleter(Comps));CA = LE->getCompletionAction("", 0);EXPECT_EQ(LineEditor::CompletionAction::AK_Insert, CA.Kind);EXPECT_EQ("f", CA.Text);}
set(LLVM_LINK_COMPONENTSLineEditorSupport)add_llvm_unittest(LineEditorTestsLineEditor.cpp)
//===- llvm/unittests/TextAPI/YAMLTest.cpp --------------------------------===////// 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////===-----------------------------------------------------------------------===/#include "llvm/ADT/StringRef.h"#include "llvm/BinaryFormat/ELF.h"#include "llvm/InterfaceStub/IFSHandler.h"#include "llvm/InterfaceStub/IFSStub.h"#include "llvm/Support/Error.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <string>using namespace llvm;using namespace llvm::ELF;using namespace llvm::ifs;void compareByLine(StringRef LHS, StringRef RHS) {StringRef Line1;StringRef Line2;while (LHS.size() > 0 && RHS.size() > 0) {std::tie(Line1, LHS) = LHS.split('\n');std::tie(Line2, RHS) = RHS.split('\n');// Comparing StringRef objects works, but has messy output when not equal.// Using STREQ on StringRef.data() doesn't work since these substrings are// not null terminated.// This is inefficient, but forces null terminated strings that can be// cleanly compared.EXPECT_STREQ(Line1.str().data(), Line2.str().data());}}TEST(ElfYamlTextAPI, YAMLReadableTBE) {const char Data[] = "--- !ifs-v1\n""IfsVersion: 1.0\n""Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: ""little, BitWidth: 64 }\n""NeededLibs: [libc.so, libfoo.so, libbar.so]\n""Symbols:\n"" - { Name: foo, Type: Func, Undefined: true }\n""...\n";Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data);ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded());std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get());EXPECT_NE(Stub.get(), nullptr);EXPECT_FALSE(Stub->SoName.has_value());EXPECT_TRUE(Stub->Target.Arch.has_value());EXPECT_EQ(Stub->Target.Arch.value(), (uint16_t)llvm::ELF::EM_X86_64);EXPECT_EQ(Stub->NeededLibs.size(), 3u);EXPECT_STREQ(Stub->NeededLibs[0].c_str(), "libc.so");EXPECT_STREQ(Stub->NeededLibs[1].c_str(), "libfoo.so");EXPECT_STREQ(Stub->NeededLibs[2].c_str(), "libbar.so");}TEST(ElfYamlTextAPI, YAMLReadsTBESymbols) {const char Data[] ="--- !ifs-v1\n""IfsVersion: 1.0\n""SoName: test.so\n""Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: little, ""BitWidth: 64 }\n""Symbols:\n"" - { Name: bar, Type: Object, Size: 42 }\n"" - { Name: baz, Type: TLS, Size: 3 }\n"" - { Name: foo, Type: Func, Warning: \"Deprecated!\" }\n"" - { Name: nor, Type: NoType, Undefined: true }\n"" - { Name: not, Type: File, Undefined: true, Size: 111, ""Weak: true, Warning: \'All fields populated!\' }\n""...\n";Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data);ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded());std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get());EXPECT_NE(Stub.get(), nullptr);EXPECT_TRUE(Stub->SoName);EXPECT_STREQ(Stub->SoName->c_str(), "test.so");EXPECT_EQ(Stub->Symbols.size(), 5u);auto Iterator = Stub->Symbols.begin();IFSSymbol const &SymBar = *Iterator++;EXPECT_STREQ(SymBar.Name.c_str(), "bar");EXPECT_EQ(*SymBar.Size, 42u);EXPECT_EQ(SymBar.Type, IFSSymbolType::Object);EXPECT_FALSE(SymBar.Undefined);EXPECT_FALSE(SymBar.Weak);EXPECT_FALSE(SymBar.Warning);IFSSymbol const &SymBaz = *Iterator++;EXPECT_STREQ(SymBaz.Name.c_str(), "baz");EXPECT_EQ(*SymBaz.Size, 3u);EXPECT_EQ(SymBaz.Type, IFSSymbolType::TLS);EXPECT_FALSE(SymBaz.Undefined);EXPECT_FALSE(SymBaz.Weak);EXPECT_FALSE(SymBaz.Warning.has_value());IFSSymbol const &SymFoo = *Iterator++;EXPECT_STREQ(SymFoo.Name.c_str(), "foo");EXPECT_FALSE(SymFoo.Size.has_value());EXPECT_EQ(SymFoo.Type, IFSSymbolType::Func);EXPECT_FALSE(SymFoo.Undefined);EXPECT_FALSE(SymFoo.Weak);EXPECT_TRUE(SymFoo.Warning.has_value());EXPECT_STREQ(SymFoo.Warning->c_str(), "Deprecated!");IFSSymbol const &SymNor = *Iterator++;EXPECT_STREQ(SymNor.Name.c_str(), "nor");EXPECT_FALSE(SymNor.Size.has_value());EXPECT_EQ(SymNor.Type, IFSSymbolType::NoType);EXPECT_TRUE(SymNor.Undefined);EXPECT_FALSE(SymNor.Weak);EXPECT_FALSE(SymNor.Warning.has_value());IFSSymbol const &SymNot = *Iterator++;EXPECT_STREQ(SymNot.Name.c_str(), "not");EXPECT_EQ(*SymNot.Size, 111u);EXPECT_EQ(SymNot.Type, IFSSymbolType::Unknown);EXPECT_TRUE(SymNot.Undefined);EXPECT_TRUE(SymNot.Weak);EXPECT_TRUE(SymNot.Warning);EXPECT_STREQ(SymNot.Warning->c_str(), "All fields populated!");}TEST(ElfYamlTextAPI, YAMLReadsNoTBESyms) {const char Data[] = "--- !ifs-v1\n""IfsVersion: 1.0\n""SoName: test.so\n""Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: ""little, BitWidth: 64 }\n""Symbols: []\n""...\n";Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data);ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded());std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get());EXPECT_NE(Stub.get(), nullptr);EXPECT_EQ(0u, Stub->Symbols.size());}TEST(ElfYamlTextAPI, YAMLUnreadableTBE) {// Can't read: wrong format/version.const char Data[] = "--- !tapi-tbz\n""IfsVersion: z.3\n""SoName: test.so\n""Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: ""little, BitWidth: 64 }\n""Symbols:\n"" foo: { Type: Func, Undefined: true }\n";Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data);ASSERT_THAT_ERROR(StubOrErr.takeError(), Failed());}TEST(ElfYamlTextAPI, YAMLUnsupportedVersion) {const char Data[] = "--- !ifs-v1\n""IfsVersion: 9.9.9\n""SoName: test.so\n""Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: ""little, BitWidth: 64 }\n""Symbols: []\n""...\n";Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data);std::string ErrorMessage = toString(StubOrErr.takeError());EXPECT_EQ("IFS version 9.9.9 is unsupported.", ErrorMessage);}TEST(ElfYamlTextAPI, YAMLWritesTBESymbols) {const char Expected[] ="--- !ifs-v1\n""IfsVersion: 1.0\n""Target: { ObjectFormat: ELF, Arch: AArch64, Endianness: ""little, BitWidth: 64 }\n""Symbols:\n"" - { Name: bar, Type: Func, Weak: true }\n"" - { Name: foo, Type: NoType, Size: 99, Warning: Does nothing }\n"" - { Name: nor, Type: Func, Undefined: true }\n"" - { Name: not, Type: Unknown, Size: 12345678901234 }\n""...\n";IFSStub Stub;Stub.IfsVersion = VersionTuple(1, 0);Stub.Target.Arch = ELF::EM_AARCH64;Stub.Target.BitWidth = IFSBitWidthType::IFS64;Stub.Target.Endianness = IFSEndiannessType::Little;Stub.Target.ObjectFormat = "ELF";IFSSymbol SymBar("bar");SymBar.Size = 128u;SymBar.Type = IFSSymbolType::Func;SymBar.Undefined = false;SymBar.Weak = true;IFSSymbol SymFoo("foo");SymFoo.Size = 99u;SymFoo.Type = IFSSymbolType::NoType;SymFoo.Undefined = false;SymFoo.Weak = false;SymFoo.Warning = "Does nothing";IFSSymbol SymNor("nor");SymNor.Size = 1234u;SymNor.Type = IFSSymbolType::Func;SymNor.Undefined = true;SymNor.Weak = false;IFSSymbol SymNot("not");SymNot.Size = 12345678901234u;SymNot.Type = IFSSymbolType::Unknown;SymNot.Undefined = false;SymNot.Weak = false;// Symbol order is preserved instead of being sorted.Stub.Symbols.push_back(SymBar);Stub.Symbols.push_back(SymFoo);Stub.Symbols.push_back(SymNor);Stub.Symbols.push_back(SymNot);// Ensure move constructor works as expected.IFSStub Moved = std::move(Stub);std::string Result;raw_string_ostream OS(Result);ASSERT_THAT_ERROR(writeIFSToOutputStream(OS, Moved), Succeeded());Result = OS.str();compareByLine(Result.c_str(), Expected);}TEST(ElfYamlTextAPI, YAMLWritesNoTBESyms) {const char Expected[] = "--- !ifs-v1\n""IfsVersion: 1.0\n""SoName: nosyms.so\n""Target: { ObjectFormat: ELF, Arch: x86_64, ""Endianness: little, BitWidth: 64 }\n""NeededLibs:\n"" - libc.so\n"" - libfoo.so\n"" - libbar.so\n""Symbols: []\n""...\n";IFSStub Stub;Stub.IfsVersion = VersionTuple(1, 0);Stub.SoName = "nosyms.so";Stub.Target.Arch = ELF::EM_X86_64;Stub.Target.BitWidth = IFSBitWidthType::IFS64;Stub.Target.Endianness = IFSEndiannessType::Little;Stub.Target.ObjectFormat = "ELF";Stub.NeededLibs.push_back("libc.so");Stub.NeededLibs.push_back("libfoo.so");Stub.NeededLibs.push_back("libbar.so");std::string Result;raw_string_ostream OS(Result);ASSERT_THAT_ERROR(writeIFSToOutputStream(OS, Stub), Succeeded());Result = OS.str();compareByLine(Result.c_str(), Expected);}
set(LLVM_LINK_COMPONENTSInterfaceStub)add_llvm_unittest(InterfaceStubTestsELFYAMLTest.cpp)target_link_libraries(InterfaceStubTests PRIVATE LLVMTestingSupport)
//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests --*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Verifier.h"#include "llvm/IR/Constants.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/Function.h"#include "llvm/IR/GlobalAlias.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"namespace llvm {namespace {TEST(VerifierTest, Branch_i1) {LLVMContext C;Module M("M", C);FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);BasicBlock *Entry = BasicBlock::Create(C, "entry", F);BasicBlock *Exit = BasicBlock::Create(C, "exit", F);ReturnInst::Create(C, Exit);// To avoid triggering an assertion in BranchInst::Create, we first create// a branch with an 'i1' condition ...Constant *False = ConstantInt::getFalse(C);BranchInst *BI = BranchInst::Create(Exit, Exit, False, Entry);// ... then use setOperand to redirect it to a value of different type.Constant *Zero32 = ConstantInt::get(IntegerType::get(C, 32), 0);BI->setOperand(0, Zero32);EXPECT_TRUE(verifyFunction(*F));}TEST(VerifierTest, Freeze) {LLVMContext C;Module M("M", C);FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);BasicBlock *Entry = BasicBlock::Create(C, "entry", F);ReturnInst *RI = ReturnInst::Create(C, Entry);IntegerType *ITy = IntegerType::get(C, 32);ConstantInt *CI = ConstantInt::get(ITy, 0);// Valid type : freeze(<2 x i32>)Constant *CV = ConstantVector::getSplat(ElementCount::getFixed(2), CI);FreezeInst *FI_vec = new FreezeInst(CV);FI_vec->insertBefore(RI);EXPECT_FALSE(verifyFunction(*F));FI_vec->eraseFromParent();// Valid type : freeze(float)Constant *CFP = ConstantFP::get(Type::getDoubleTy(C), 0.0);FreezeInst *FI_dbl = new FreezeInst(CFP);FI_dbl->insertBefore(RI);EXPECT_FALSE(verifyFunction(*F));FI_dbl->eraseFromParent();// Valid type : freeze(i32*)PointerType *PT = PointerType::get(ITy, 0);ConstantPointerNull *CPN = ConstantPointerNull::get(PT);FreezeInst *FI_ptr = new FreezeInst(CPN);FI_ptr->insertBefore(RI);EXPECT_FALSE(verifyFunction(*F));FI_ptr->eraseFromParent();// Valid type : freeze(int)FreezeInst *FI = new FreezeInst(CI);FI->insertBefore(RI);EXPECT_FALSE(verifyFunction(*F));FI->eraseFromParent();}TEST(VerifierTest, InvalidRetAttribute) {LLVMContext C;Module M("M", C);FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);AttributeList AS = F->getAttributes();F->setAttributes(AS.addRetAttribute(C, Attribute::getWithUWTableKind(C, UWTableKind::Default)));std::string Error;raw_string_ostream ErrorOS(Error);EXPECT_TRUE(verifyModule(M, &ErrorOS));EXPECT_TRUE(StringRef(ErrorOS.str()).startswith("Attribute 'uwtable' does not apply to function return values"));}TEST(VerifierTest, CrossModuleRef) {LLVMContext C;Module M1("M1", C);Module M2("M2", C);Module M3("M3", C);FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);Function *F1 = Function::Create(FTy, Function::ExternalLinkage, "foo1", M1);Function *F2 = Function::Create(FTy, Function::ExternalLinkage, "foo2", M2);Function *F3 = Function::Create(FTy, Function::ExternalLinkage, "foo3", M3);BasicBlock *Entry1 = BasicBlock::Create(C, "entry", F1);BasicBlock *Entry3 = BasicBlock::Create(C, "entry", F3);// BAD: Referencing function in another moduleCallInst::Create(F2,"call",Entry1);// BAD: Referencing personality routine in another moduleF3->setPersonalityFn(F2);// Fill in the bodyConstant *ConstZero = ConstantInt::get(Type::getInt32Ty(C), 0);ReturnInst::Create(C, ConstZero, Entry1);ReturnInst::Create(C, ConstZero, Entry3);std::string Error;raw_string_ostream ErrorOS(Error);EXPECT_TRUE(verifyModule(M2, &ErrorOS));EXPECT_TRUE(StringRef(ErrorOS.str()).equals("Global is referenced in a different module!\n""ptr @foo2\n""; ModuleID = 'M2'\n"" %call = call i32 @foo2()\n""ptr @foo1\n""; ModuleID = 'M1'\n""Global is used by function in a different module\n""ptr @foo2\n""; ModuleID = 'M2'\n""ptr @foo3\n""; ModuleID = 'M3'\n"));Error.clear();EXPECT_TRUE(verifyModule(M1, &ErrorOS));EXPECT_TRUE(StringRef(ErrorOS.str()).equals("Referencing function in another module!\n"" %call = call i32 @foo2()\n""; ModuleID = 'M1'\n""ptr @foo2\n""; ModuleID = 'M2'\n"));Error.clear();EXPECT_TRUE(verifyModule(M3, &ErrorOS));EXPECT_TRUE(StringRef(ErrorOS.str()).startswith("Referencing personality function in another module!"));// Erase bad methods to avoid triggering an assertion failure on destructionF1->eraseFromParent();F3->eraseFromParent();}TEST(VerifierTest, InvalidVariableLinkage) {LLVMContext C;Module M("M", C);new GlobalVariable(M, Type::getInt8Ty(C), false,GlobalValue::LinkOnceODRLinkage, nullptr, "Some Global");std::string Error;raw_string_ostream ErrorOS(Error);EXPECT_TRUE(verifyModule(M, &ErrorOS));EXPECT_TRUE(StringRef(ErrorOS.str()).startswith("Global is external, but doesn't ""have external or weak linkage!"));}TEST(VerifierTest, InvalidFunctionLinkage) {LLVMContext C;Module M("M", C);FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);Function::Create(FTy, GlobalValue::LinkOnceODRLinkage, "foo", &M);std::string Error;raw_string_ostream ErrorOS(Error);EXPECT_TRUE(verifyModule(M, &ErrorOS));EXPECT_TRUE(StringRef(ErrorOS.str()).startswith("Global is external, but doesn't ""have external or weak linkage!"));}TEST(VerifierTest, DetectInvalidDebugInfo) {{LLVMContext C;Module M("M", C);DIBuilder DIB(M);DIB.createCompileUnit(dwarf::DW_LANG_C89, DIB.createFile("broken.c", "/"),"unittest", false, "", 0);DIB.finalize();EXPECT_FALSE(verifyModule(M));// Now break it by inserting non-CU node to the list of CUs.auto *File = DIB.createFile("not-a-CU.f", ".");NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");NMD->addOperand(File);EXPECT_TRUE(verifyModule(M));}{LLVMContext C;Module M("M", C);DIBuilder DIB(M);auto *CU = DIB.createCompileUnit(dwarf::DW_LANG_C89,DIB.createFile("broken.c", "/"),"unittest", false, "", 0);new GlobalVariable(M, Type::getInt8Ty(C), false,GlobalValue::ExternalLinkage, nullptr, "g");auto *F = Function::Create(FunctionType::get(Type::getVoidTy(C), false),Function::ExternalLinkage, "f", M);IRBuilder<> Builder(BasicBlock::Create(C, "", F));Builder.CreateUnreachable();F->setSubprogram(DIB.createFunction(CU, "f", "f", DIB.createFile("broken.c", "/"), 1, nullptr, 1,DINode::FlagZero,DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition));DIB.finalize();EXPECT_FALSE(verifyModule(M));// Now break it by not listing the CU at all.M.eraseNamedMetadata(M.getOrInsertNamedMetadata("llvm.dbg.cu"));EXPECT_TRUE(verifyModule(M));}}TEST(VerifierTest, MDNodeWrongContext) {LLVMContext C1, C2;auto *Node = MDNode::get(C1, None);Module M("M", C2);auto *NamedNode = M.getOrInsertNamedMetadata("test");NamedNode->addOperand(Node);std::string Error;raw_string_ostream ErrorOS(Error);EXPECT_TRUE(verifyModule(M, &ErrorOS));EXPECT_TRUE(StringRef(ErrorOS.str()).startswith("MDNode context does not match Module context!"));}TEST(VerifierTest, AttributesWrongContext) {LLVMContext C1, C2;Module M1("M", C1);FunctionType *FTy1 =FunctionType::get(Type::getVoidTy(C1), /*isVarArg=*/false);Function *F1 = Function::Create(FTy1, Function::ExternalLinkage, "foo", M1);F1->setDoesNotReturn();Module M2("M", C2);FunctionType *FTy2 =FunctionType::get(Type::getVoidTy(C2), /*isVarArg=*/false);Function *F2 = Function::Create(FTy2, Function::ExternalLinkage, "foo", M2);F2->copyAttributesFrom(F1);EXPECT_TRUE(verifyFunction(*F2));}} // end anonymous namespace} // end namespace llvm
//===--- llvm/unittest/IR/VectorTypesTest.cpp - vector types unit tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/DataLayout.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/TypeSize.h"#include "gtest/gtest.h"using namespace llvm;namespace {#define EXPECT_VTY_EQ(LHS, RHS) \do { \ASSERT_NE(LHS, nullptr) << #LHS << " must not be null"; \ASSERT_NE(RHS, nullptr) << #RHS << " must not be null"; \EXPECT_EQ(LHS, RHS) << "Expect that " << #LHS << " == " << #RHS \<< " where " << #LHS << " = " << *LHS << " and " \<< #RHS << " = " << *RHS; \} while (false)#define EXPECT_VTY_NE(LHS, RHS) \do { \ASSERT_NE(LHS, nullptr) << #LHS << " must not be null"; \ASSERT_NE(RHS, nullptr) << #RHS << " must not be null"; \EXPECT_NE(LHS, RHS) << "Expect that " << #LHS << " != " << #RHS \<< " where " << #LHS << " = " << *LHS << " and " \<< #RHS << " = " << *RHS; \} while (false)TEST(VectorTypesTest, FixedLength) {LLVMContext Ctx;Type *Int8Ty = Type::getInt8Ty(Ctx);Type *Int16Ty = Type::getInt16Ty(Ctx);Type *Int32Ty = Type::getInt32Ty(Ctx);Type *Int64Ty = Type::getInt64Ty(Ctx);Type *Float64Ty = Type::getDoubleTy(Ctx);auto *V16Int8Ty = FixedVectorType::get(Int8Ty, 16);ASSERT_NE(nullptr, V16Int8Ty);EXPECT_EQ(V16Int8Ty->getNumElements(), 16U);EXPECT_EQ(V16Int8Ty->getElementType()->getScalarSizeInBits(), 8U);auto *V8Int32Ty =dyn_cast<FixedVectorType>(VectorType::get(Int32Ty, 8, false));ASSERT_NE(nullptr, V8Int32Ty);EXPECT_EQ(V8Int32Ty->getNumElements(), 8U);EXPECT_EQ(V8Int32Ty->getElementType()->getScalarSizeInBits(), 32U);auto *V8Int8Ty =dyn_cast<FixedVectorType>(VectorType::get(Int8Ty, V8Int32Ty));EXPECT_VTY_NE(V8Int32Ty, V8Int8Ty);EXPECT_EQ(V8Int8Ty->getElementCount(), V8Int32Ty->getElementCount());EXPECT_EQ(V8Int8Ty->getElementType()->getScalarSizeInBits(), 8U);auto *V8Int32Ty2 =dyn_cast<FixedVectorType>(VectorType::get(Int32Ty, V8Int32Ty));EXPECT_VTY_EQ(V8Int32Ty, V8Int32Ty2);auto *V8Int16Ty = dyn_cast<FixedVectorType>(VectorType::get(Int16Ty, ElementCount::getFixed(8)));ASSERT_NE(nullptr, V8Int16Ty);EXPECT_EQ(V8Int16Ty->getNumElements(), 8U);EXPECT_EQ(V8Int16Ty->getElementType()->getScalarSizeInBits(), 16U);auto EltCnt = ElementCount::getFixed(4);auto *V4Int64Ty = dyn_cast<FixedVectorType>(VectorType::get(Int64Ty, EltCnt));ASSERT_NE(nullptr, V4Int64Ty);EXPECT_EQ(V4Int64Ty->getNumElements(), 4U);EXPECT_EQ(V4Int64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *V2Int64Ty = dyn_cast<FixedVectorType>(VectorType::get(Int64Ty, EltCnt.divideCoefficientBy(2)));ASSERT_NE(nullptr, V2Int64Ty);EXPECT_EQ(V2Int64Ty->getNumElements(), 2U);EXPECT_EQ(V2Int64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *V8Int64Ty =dyn_cast<FixedVectorType>(VectorType::get(Int64Ty, EltCnt * 2));ASSERT_NE(nullptr, V8Int64Ty);EXPECT_EQ(V8Int64Ty->getNumElements(), 8U);EXPECT_EQ(V8Int64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *V4Float64Ty =dyn_cast<FixedVectorType>(VectorType::get(Float64Ty, EltCnt));ASSERT_NE(nullptr, V4Float64Ty);EXPECT_EQ(V4Float64Ty->getNumElements(), 4U);EXPECT_EQ(V4Float64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *ExtTy = dyn_cast<FixedVectorType>(VectorType::getExtendedElementVectorType(V8Int16Ty));EXPECT_VTY_EQ(ExtTy, V8Int32Ty);EXPECT_EQ(ExtTy->getNumElements(), 8U);EXPECT_EQ(ExtTy->getElementType()->getScalarSizeInBits(), 32U);auto *TruncTy = dyn_cast<FixedVectorType>(VectorType::getTruncatedElementVectorType(V8Int32Ty));EXPECT_VTY_EQ(TruncTy, V8Int16Ty);EXPECT_EQ(TruncTy->getNumElements(), 8U);EXPECT_EQ(TruncTy->getElementType()->getScalarSizeInBits(), 16U);auto *HalvedTy = dyn_cast<FixedVectorType>(VectorType::getHalfElementsVectorType(V4Int64Ty));EXPECT_VTY_EQ(HalvedTy, V2Int64Ty);EXPECT_EQ(HalvedTy->getNumElements(), 2U);EXPECT_EQ(HalvedTy->getElementType()->getScalarSizeInBits(), 64U);auto *DoubledTy = dyn_cast<FixedVectorType>(VectorType::getDoubleElementsVectorType(V4Int64Ty));EXPECT_VTY_EQ(DoubledTy, V8Int64Ty);EXPECT_EQ(DoubledTy->getNumElements(), 8U);EXPECT_EQ(DoubledTy->getElementType()->getScalarSizeInBits(), 64U);auto *ConvTy = dyn_cast<FixedVectorType>(VectorType::getInteger(V4Float64Ty));EXPECT_VTY_EQ(ConvTy, V4Int64Ty);EXPECT_EQ(ConvTy->getNumElements(), 4U);EXPECT_EQ(ConvTy->getElementType()->getScalarSizeInBits(), 64U);EltCnt = V8Int64Ty->getElementCount();EXPECT_EQ(EltCnt.getKnownMinValue(), 8U);ASSERT_FALSE(EltCnt.isScalable());}TEST(VectorTypesTest, Scalable) {LLVMContext Ctx;Type *Int8Ty = Type::getInt8Ty(Ctx);Type *Int16Ty = Type::getInt16Ty(Ctx);Type *Int32Ty = Type::getInt32Ty(Ctx);Type *Int64Ty = Type::getInt64Ty(Ctx);Type *Float64Ty = Type::getDoubleTy(Ctx);auto *ScV16Int8Ty = ScalableVectorType::get(Int8Ty, 16);ASSERT_NE(nullptr, ScV16Int8Ty);EXPECT_EQ(ScV16Int8Ty->getMinNumElements(), 16U);EXPECT_EQ(ScV16Int8Ty->getScalarSizeInBits(), 8U);auto *ScV8Int32Ty =dyn_cast<ScalableVectorType>(VectorType::get(Int32Ty, 8, true));ASSERT_NE(nullptr, ScV8Int32Ty);EXPECT_EQ(ScV8Int32Ty->getMinNumElements(), 8U);EXPECT_EQ(ScV8Int32Ty->getElementType()->getScalarSizeInBits(), 32U);auto *ScV8Int8Ty =dyn_cast<ScalableVectorType>(VectorType::get(Int8Ty, ScV8Int32Ty));EXPECT_VTY_NE(ScV8Int32Ty, ScV8Int8Ty);EXPECT_EQ(ScV8Int8Ty->getElementCount(), ScV8Int32Ty->getElementCount());EXPECT_EQ(ScV8Int8Ty->getElementType()->getScalarSizeInBits(), 8U);auto *ScV8Int32Ty2 =dyn_cast<ScalableVectorType>(VectorType::get(Int32Ty, ScV8Int32Ty));EXPECT_VTY_EQ(ScV8Int32Ty, ScV8Int32Ty2);auto *ScV8Int16Ty = dyn_cast<ScalableVectorType>(VectorType::get(Int16Ty, ElementCount::getScalable(8)));ASSERT_NE(nullptr, ScV8Int16Ty);EXPECT_EQ(ScV8Int16Ty->getMinNumElements(), 8U);EXPECT_EQ(ScV8Int16Ty->getElementType()->getScalarSizeInBits(), 16U);auto EltCnt = ElementCount::getScalable(4);auto *ScV4Int64Ty =dyn_cast<ScalableVectorType>(VectorType::get(Int64Ty, EltCnt));ASSERT_NE(nullptr, ScV4Int64Ty);EXPECT_EQ(ScV4Int64Ty->getMinNumElements(), 4U);EXPECT_EQ(ScV4Int64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *ScV2Int64Ty = dyn_cast<ScalableVectorType>(VectorType::get(Int64Ty, EltCnt.divideCoefficientBy(2)));ASSERT_NE(nullptr, ScV2Int64Ty);EXPECT_EQ(ScV2Int64Ty->getMinNumElements(), 2U);EXPECT_EQ(ScV2Int64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *ScV8Int64Ty =dyn_cast<ScalableVectorType>(VectorType::get(Int64Ty, EltCnt * 2));ASSERT_NE(nullptr, ScV8Int64Ty);EXPECT_EQ(ScV8Int64Ty->getMinNumElements(), 8U);EXPECT_EQ(ScV8Int64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *ScV4Float64Ty =dyn_cast<ScalableVectorType>(VectorType::get(Float64Ty, EltCnt));ASSERT_NE(nullptr, ScV4Float64Ty);EXPECT_EQ(ScV4Float64Ty->getMinNumElements(), 4U);EXPECT_EQ(ScV4Float64Ty->getElementType()->getScalarSizeInBits(), 64U);auto *ExtTy = dyn_cast<ScalableVectorType>(VectorType::getExtendedElementVectorType(ScV8Int16Ty));EXPECT_VTY_EQ(ExtTy, ScV8Int32Ty);EXPECT_EQ(ExtTy->getMinNumElements(), 8U);EXPECT_EQ(ExtTy->getElementType()->getScalarSizeInBits(), 32U);auto *TruncTy = dyn_cast<ScalableVectorType>(VectorType::getTruncatedElementVectorType(ScV8Int32Ty));EXPECT_VTY_EQ(TruncTy, ScV8Int16Ty);EXPECT_EQ(TruncTy->getMinNumElements(), 8U);EXPECT_EQ(TruncTy->getElementType()->getScalarSizeInBits(), 16U);auto *HalvedTy = dyn_cast<ScalableVectorType>(VectorType::getHalfElementsVectorType(ScV4Int64Ty));EXPECT_VTY_EQ(HalvedTy, ScV2Int64Ty);EXPECT_EQ(HalvedTy->getMinNumElements(), 2U);EXPECT_EQ(HalvedTy->getElementType()->getScalarSizeInBits(), 64U);auto *DoubledTy = dyn_cast<ScalableVectorType>(VectorType::getDoubleElementsVectorType(ScV4Int64Ty));EXPECT_VTY_EQ(DoubledTy, ScV8Int64Ty);EXPECT_EQ(DoubledTy->getMinNumElements(), 8U);EXPECT_EQ(DoubledTy->getElementType()->getScalarSizeInBits(), 64U);auto *ConvTy =dyn_cast<ScalableVectorType>(VectorType::getInteger(ScV4Float64Ty));EXPECT_VTY_EQ(ConvTy, ScV4Int64Ty);EXPECT_EQ(ConvTy->getMinNumElements(), 4U);EXPECT_EQ(ConvTy->getElementType()->getScalarSizeInBits(), 64U);EltCnt = ScV8Int64Ty->getElementCount();EXPECT_EQ(EltCnt.getKnownMinValue(), 8U);ASSERT_TRUE(EltCnt.isScalable());}TEST(VectorTypesTest, BaseVectorType) {LLVMContext Ctx;Type *Int16Ty = Type::getInt16Ty(Ctx);Type *Int32Ty = Type::getInt32Ty(Ctx);std::array<VectorType *, 8> VTys = {VectorType::get(Int16Ty, ElementCount::getScalable(4)),VectorType::get(Int16Ty, ElementCount::getFixed(4)),VectorType::get(Int16Ty, ElementCount::getScalable(2)),VectorType::get(Int16Ty, ElementCount::getFixed(2)),VectorType::get(Int32Ty, ElementCount::getScalable(4)),VectorType::get(Int32Ty, ElementCount::getFixed(4)),VectorType::get(Int32Ty, ElementCount::getScalable(2)),VectorType::get(Int32Ty, ElementCount::getFixed(2))};/*The comparison matrix is symmetric, so we only check the upper triangle:(0,0) (0,1) (0,2) ... (0,7)(1,0) (1,1) (1,2) .(2,0) (2,1) (2,2) .. . .. .. .(7,0) ... (7,7)*/for (size_t I = 0, IEnd = VTys.size(); I < IEnd; ++I) {// test I == JVectorType *VI = VTys[I];ElementCount ECI = VI->getElementCount();EXPECT_EQ(isa<ScalableVectorType>(VI), ECI.isScalable());for (size_t J = I + 1, JEnd = VTys.size(); J < JEnd; ++J) {// test I < JVectorType *VJ = VTys[J];EXPECT_VTY_NE(VI, VJ);VectorType *VJPrime = VectorType::get(VI->getElementType(), VJ);if (VI->getElementType() == VJ->getElementType()) {EXPECT_VTY_EQ(VJ, VJPrime);} else {EXPECT_VTY_NE(VJ, VJPrime);}EXPECT_EQ(VJ->getTypeID(), VJPrime->getTypeID())<< "VJ and VJPrime are the same sort of vector";}}}TEST(VectorTypesTest, FixedLenComparisons) {LLVMContext Ctx;DataLayout DL("");Type *Int32Ty = Type::getInt32Ty(Ctx);Type *Int64Ty = Type::getInt64Ty(Ctx);auto *V2Int32Ty = FixedVectorType::get(Int32Ty, 2);auto *V4Int32Ty = FixedVectorType::get(Int32Ty, 4);auto *V2Int64Ty = FixedVectorType::get(Int64Ty, 2);TypeSize V2I32Len = V2Int32Ty->getPrimitiveSizeInBits();EXPECT_EQ(V2I32Len.getKnownMinSize(), 64U);EXPECT_FALSE(V2I32Len.isScalable());EXPECT_LT(V2Int32Ty->getPrimitiveSizeInBits().getFixedSize(),V4Int32Ty->getPrimitiveSizeInBits().getFixedSize());EXPECT_GT(V2Int64Ty->getPrimitiveSizeInBits().getFixedSize(),V2Int32Ty->getPrimitiveSizeInBits().getFixedSize());EXPECT_EQ(V4Int32Ty->getPrimitiveSizeInBits(),V2Int64Ty->getPrimitiveSizeInBits());EXPECT_NE(V2Int32Ty->getPrimitiveSizeInBits(),V2Int64Ty->getPrimitiveSizeInBits());// Check that a fixed-only comparison works for fixed size vectors.EXPECT_EQ(V2Int64Ty->getPrimitiveSizeInBits().getFixedSize(),V4Int32Ty->getPrimitiveSizeInBits().getFixedSize());// Check the DataLayout interfaces.EXPECT_EQ(DL.getTypeSizeInBits(V2Int64Ty), DL.getTypeSizeInBits(V4Int32Ty));EXPECT_EQ(DL.getTypeSizeInBits(V2Int32Ty), 64U);EXPECT_EQ(DL.getTypeSizeInBits(V2Int64Ty), 128U);EXPECT_EQ(DL.getTypeStoreSize(V2Int64Ty), DL.getTypeStoreSize(V4Int32Ty));EXPECT_NE(DL.getTypeStoreSizeInBits(V2Int32Ty),DL.getTypeStoreSizeInBits(V2Int64Ty));EXPECT_EQ(DL.getTypeStoreSizeInBits(V2Int32Ty), 64U);EXPECT_EQ(DL.getTypeStoreSize(V2Int64Ty), 16U);EXPECT_EQ(DL.getTypeAllocSize(V4Int32Ty), DL.getTypeAllocSize(V2Int64Ty));EXPECT_NE(DL.getTypeAllocSizeInBits(V2Int32Ty),DL.getTypeAllocSizeInBits(V2Int64Ty));EXPECT_EQ(DL.getTypeAllocSizeInBits(V4Int32Ty), 128U);EXPECT_EQ(DL.getTypeAllocSize(V2Int32Ty), 8U);ASSERT_TRUE(DL.typeSizeEqualsStoreSize(V4Int32Ty));}TEST(VectorTypesTest, ScalableComparisons) {LLVMContext Ctx;DataLayout DL("");Type *Int32Ty = Type::getInt32Ty(Ctx);Type *Int64Ty = Type::getInt64Ty(Ctx);auto *ScV2Int32Ty = ScalableVectorType::get(Int32Ty, 2);auto *ScV4Int32Ty = ScalableVectorType::get(Int32Ty, 4);auto *ScV2Int64Ty = ScalableVectorType::get(Int64Ty, 2);TypeSize ScV2I32Len = ScV2Int32Ty->getPrimitiveSizeInBits();EXPECT_EQ(ScV2I32Len.getKnownMinSize(), 64U);EXPECT_TRUE(ScV2I32Len.isScalable());EXPECT_LT(ScV2Int32Ty->getPrimitiveSizeInBits().getKnownMinSize(),ScV4Int32Ty->getPrimitiveSizeInBits().getKnownMinSize());EXPECT_GT(ScV2Int64Ty->getPrimitiveSizeInBits().getKnownMinSize(),ScV2Int32Ty->getPrimitiveSizeInBits().getKnownMinSize());EXPECT_EQ(ScV4Int32Ty->getPrimitiveSizeInBits().getKnownMinSize(),ScV2Int64Ty->getPrimitiveSizeInBits().getKnownMinSize());EXPECT_NE(ScV2Int32Ty->getPrimitiveSizeInBits().getKnownMinSize(),ScV2Int64Ty->getPrimitiveSizeInBits().getKnownMinSize());// Check the DataLayout interfaces.EXPECT_EQ(DL.getTypeSizeInBits(ScV2Int64Ty),DL.getTypeSizeInBits(ScV4Int32Ty));EXPECT_EQ(DL.getTypeSizeInBits(ScV2Int32Ty).getKnownMinSize(), 64U);EXPECT_EQ(DL.getTypeStoreSize(ScV2Int64Ty), DL.getTypeStoreSize(ScV4Int32Ty));EXPECT_NE(DL.getTypeStoreSizeInBits(ScV2Int32Ty),DL.getTypeStoreSizeInBits(ScV2Int64Ty));EXPECT_EQ(DL.getTypeStoreSizeInBits(ScV2Int32Ty).getKnownMinSize(), 64U);EXPECT_EQ(DL.getTypeStoreSize(ScV2Int64Ty).getKnownMinSize(), 16U);EXPECT_EQ(DL.getTypeAllocSize(ScV4Int32Ty), DL.getTypeAllocSize(ScV2Int64Ty));EXPECT_NE(DL.getTypeAllocSizeInBits(ScV2Int32Ty),DL.getTypeAllocSizeInBits(ScV2Int64Ty));EXPECT_EQ(DL.getTypeAllocSizeInBits(ScV4Int32Ty).getKnownMinSize(), 128U);EXPECT_EQ(DL.getTypeAllocSize(ScV2Int32Ty).getKnownMinSize(), 8U);ASSERT_TRUE(DL.typeSizeEqualsStoreSize(ScV4Int32Ty));}TEST(VectorTypesTest, CrossComparisons) {LLVMContext Ctx;Type *Int32Ty = Type::getInt32Ty(Ctx);auto *V4Int32Ty = FixedVectorType::get(Int32Ty, 4);auto *ScV4Int32Ty = ScalableVectorType::get(Int32Ty, 4);// Even though the minimum size is the same, a scalable vector could be// larger so we don't consider them to be the same size.EXPECT_NE(V4Int32Ty->getPrimitiveSizeInBits(),ScV4Int32Ty->getPrimitiveSizeInBits());// If we are only checking the minimum, then they are the same size.EXPECT_EQ(V4Int32Ty->getPrimitiveSizeInBits().getKnownMinSize(),ScV4Int32Ty->getPrimitiveSizeInBits().getKnownMinSize());// We can't use ordering comparisons (<,<=,>,>=) between scalable and// non-scalable vector sizes.}} // end anonymous namespace
//===--------- VectorBuilderTest.cpp - VectorBuilder unit tests -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/VectorBuilder.h"#include "llvm/IR/Constants.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;namespace {static unsigned VectorNumElements = 8;class VectorBuilderTest : public testing::Test {protected:LLVMContext Context;VectorBuilderTest() : Context() {}std::unique_ptr<Module> createBuilderModule(Function *&Func, BasicBlock *&BB,Value *&Mask, Value *&EVL) {auto Mod = std::make_unique<Module>("TestModule", Context);auto *Int32Ty = Type::getInt32Ty(Context);auto *Mask8Ty =FixedVectorType::get(Type::getInt1Ty(Context), VectorNumElements);auto *VoidFuncTy =FunctionType::get(Type::getVoidTy(Context), {Mask8Ty, Int32Ty}, false);Func =Function::Create(VoidFuncTy, GlobalValue::ExternalLinkage, "bla", *Mod);Mask = Func->getArg(0);EVL = Func->getArg(1);BB = BasicBlock::Create(Context, "entry", Func);return Mod;}};/// Check that creating binary arithmetic VP intrinsics works.TEST_F(VectorBuilderTest, TestCreateBinaryInstructions) {Function *F;BasicBlock *BB;Value *Mask, *EVL;auto Mod = createBuilderModule(F, BB, Mask, EVL);IRBuilder<> Builder(BB);VectorBuilder VBuild(Builder);VBuild.setMask(Mask).setEVL(EVL);auto *FloatVecTy =FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);auto *IntVecTy =FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \{ \auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \bool IsFP = (#INSTCLASS)[0] == 'F'; \auto *ValueTy = IsFP ? FloatVecTy : IntVecTy; \Value *Op = UndefValue::get(ValueTy); \auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \{Op, Op}); \ASSERT_TRUE(isa<VPIntrinsic>(I)); \auto *VPIntrin = cast<VPIntrinsic>(I); \ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \}#include "llvm/IR/Instruction.def"}static bool isAllTrueMask(Value *Val, unsigned NumElements) {auto *ConstMask = dyn_cast<Constant>(Val);if (!ConstMask)return false;// Structure check.if (!ConstMask->isAllOnesValue())return false;// Type check.auto *MaskVecTy = cast<FixedVectorType>(ConstMask->getType());if (MaskVecTy->getNumElements() != NumElements)return false;return MaskVecTy->getElementType()->isIntegerTy(1);}/// Check that creating binary arithmetic VP intrinsics works.TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoMask) {Function *F;BasicBlock *BB;Value *Mask, *EVL;auto Mod = createBuilderModule(F, BB, Mask, EVL);IRBuilder<> Builder(BB);VectorBuilder VBuild(Builder);VBuild.setEVL(EVL).setStaticVL(VectorNumElements);auto *FloatVecTy =FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);auto *IntVecTy =FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \{ \auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \bool IsFP = (#INSTCLASS)[0] == 'F'; \Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \Value *Op = UndefValue::get(ValueTy); \auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \{Op, Op}); \ASSERT_TRUE(isa<VPIntrinsic>(I)); \auto *VPIntrin = cast<VPIntrinsic>(I); \ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \}#include "llvm/IR/Instruction.def"}static bool isLegalConstEVL(Value *Val, unsigned ExpectedEVL) {auto *ConstEVL = dyn_cast<ConstantInt>(Val);if (!ConstEVL)return false;// Value check.if (ConstEVL->getZExtValue() != ExpectedEVL)return false;// Type check.return ConstEVL->getType()->isIntegerTy(32);}/// Check that creating binary arithmetic VP intrinsics works.TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoEVL) {Function *F;BasicBlock *BB;Value *Mask, *EVL;auto Mod = createBuilderModule(F, BB, Mask, EVL);IRBuilder<> Builder(BB);VectorBuilder VBuild(Builder);VBuild.setMask(Mask).setStaticVL(VectorNumElements);auto *FloatVecTy =FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);auto *IntVecTy =FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \{ \auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \bool IsFP = (#INSTCLASS)[0] == 'F'; \Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \Value *Op = UndefValue::get(ValueTy); \auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \{Op, Op}); \ASSERT_TRUE(isa<VPIntrinsic>(I)); \auto *VPIntrin = cast<VPIntrinsic>(I); \ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \ASSERT_TRUE( \isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \}#include "llvm/IR/Instruction.def"}/// Check that creating binary arithmetic VP intrinsics works.TEST_F(VectorBuilderTest,TestCreateBinaryInstructions_FixedVector_NoMask_NoEVL) {Function *F;BasicBlock *BB;Value *Mask, *EVL;auto Mod = createBuilderModule(F, BB, Mask, EVL);IRBuilder<> Builder(BB);VectorBuilder VBuild(Builder);VBuild.setStaticVL(VectorNumElements);auto *FloatVecTy =FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);auto *IntVecTy =FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \{ \auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \bool IsFP = (#INSTCLASS)[0] == 'F'; \Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \Value *Op = UndefValue::get(ValueTy); \auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \{Op, Op}); \ASSERT_TRUE(isa<VPIntrinsic>(I)); \auto *VPIntrin = cast<VPIntrinsic>(I); \ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \ASSERT_TRUE( \isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \}#include "llvm/IR/Instruction.def"}/// Check that creating vp.load/vp.store works.TEST_F(VectorBuilderTest, TestCreateLoadStore) {Function *F;BasicBlock *BB;Value *Mask, *EVL;auto Mod = createBuilderModule(F, BB, Mask, EVL);IRBuilder<> Builder(BB);VectorBuilder VBuild(Builder);VBuild.setMask(Mask).setEVL(EVL);auto *FloatVecTy =FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);auto *FloatVecPtrTy = FloatVecTy->getPointerTo();Value *FloatVecPtr = UndefValue::get(FloatVecPtrTy);Value *FloatVec = UndefValue::get(FloatVecTy);// vp.loadauto LoadVPID = VPIntrinsic::getForOpcode(Instruction::Load);auto *LoadIntrin = VBuild.createVectorInstruction(Instruction::Load,FloatVecTy, {FloatVecPtr});ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));auto *VPLoad = cast<VPIntrinsic>(LoadIntrin);ASSERT_EQ(VPLoad->getIntrinsicID(), LoadVPID);ASSERT_EQ(VPLoad->getMemoryPointerParam(), FloatVecPtr);// vp.storeauto *VoidTy = Builder.getVoidTy();auto StoreVPID = VPIntrinsic::getForOpcode(Instruction::Store);auto *StoreIntrin = VBuild.createVectorInstruction(Instruction::Store, VoidTy,{FloatVec, FloatVecPtr});ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));auto *VPStore = cast<VPIntrinsic>(StoreIntrin);ASSERT_EQ(VPStore->getIntrinsicID(), StoreVPID);ASSERT_EQ(VPStore->getMemoryPointerParam(), FloatVecPtr);ASSERT_EQ(VPStore->getMemoryDataParam(), FloatVec);}/// Check that the SilentlyReturnNone error handling mode works.TEST_F(VectorBuilderTest, TestFail_SilentlyReturnNone) {Function *F;BasicBlock *BB;Value *Mask, *EVL;auto Mod = createBuilderModule(F, BB, Mask, EVL);IRBuilder<> Builder(BB);auto *VoidTy = Builder.getVoidTy();VectorBuilder VBuild(Builder, VectorBuilder::Behavior::SilentlyReturnNone);VBuild.setMask(Mask).setEVL(EVL);auto *Val = VBuild.createVectorInstruction(Instruction::Br, VoidTy, {});ASSERT_EQ(Val, nullptr);}/// Check that the ReportAndFail error handling mode aborts as advertised.TEST_F(VectorBuilderTest, TestFail_ReportAndAbort) {Function *F;BasicBlock *BB;Value *Mask, *EVL;auto Mod = createBuilderModule(F, BB, Mask, EVL);IRBuilder<> Builder(BB);auto *VoidTy = Builder.getVoidTy();VectorBuilder VBuild(Builder, VectorBuilder::Behavior::ReportAndAbort);VBuild.setMask(Mask).setEVL(EVL);ASSERT_DEATH({ VBuild.createVectorInstruction(Instruction::Br, VoidTy, {}); },"No VPIntrinsic for this opcode");}} // end anonymous namespace
//===- llvm/unittest/IR/ValueTest.cpp - Value unit tests ------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Value.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Function.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/ModuleSlotTracker.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ValueTest, UsedInBasicBlock) {LLVMContext C;const char *ModuleString = "define void @f(i32 %x, i32 %y) {\n""bb0:\n"" %y1 = add i32 %y, 1\n"" %y2 = add i32 %y, 1\n"" %y3 = add i32 %y, 1\n"" %y4 = add i32 %y, 1\n"" %y5 = add i32 %y, 1\n"" %y6 = add i32 %y, 1\n"" %y7 = add i32 %y, 1\n"" %y8 = add i32 %x, 1\n"" ret void\n""}\n";SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");EXPECT_FALSE(F->isUsedInBasicBlock(&F->front()));EXPECT_TRUE(std::next(F->arg_begin())->isUsedInBasicBlock(&F->front()));EXPECT_TRUE(F->arg_begin()->isUsedInBasicBlock(&F->front()));}TEST(GlobalTest, CreateAddressSpace) {LLVMContext Ctx;std::unique_ptr<Module> M(new Module("TestModule", Ctx));Type *Int8Ty = Type::getInt8Ty(Ctx);Type *Int32Ty = Type::getInt32Ty(Ctx);GlobalVariable *Dummy0= new GlobalVariable(*M,Int32Ty,true,GlobalValue::ExternalLinkage,Constant::getAllOnesValue(Int32Ty),"dummy",nullptr,GlobalVariable::NotThreadLocal,1);EXPECT_TRUE(Value::MaximumAlignment == 4294967296ULL);Dummy0->setAlignment(Align(4294967296ULL));EXPECT_EQ(Dummy0->getAlignment(), 4294967296ULL);// Make sure the address space isn't dropped when returning this.Constant *Dummy1 = M->getOrInsertGlobal("dummy", Int32Ty);EXPECT_EQ(Dummy0, Dummy1);EXPECT_EQ(1u, Dummy1->getType()->getPointerAddressSpace());// This one requires a bitcast, but the address space must also stay the same.GlobalVariable *DummyCast0= new GlobalVariable(*M,Int32Ty,true,GlobalValue::ExternalLinkage,Constant::getAllOnesValue(Int32Ty),"dummy_cast",nullptr,GlobalVariable::NotThreadLocal,1);// Make sure the address space isn't dropped when returning this.Constant *DummyCast1 = M->getOrInsertGlobal("dummy_cast", Int8Ty);EXPECT_EQ(DummyCast0, DummyCast1);EXPECT_EQ(1u, DummyCast1->getType()->getPointerAddressSpace());}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST(GlobalTest, AlignDeath) {LLVMContext Ctx;std::unique_ptr<Module> M(new Module("TestModule", Ctx));Type *Int32Ty = Type::getInt32Ty(Ctx);GlobalVariable *Var =new GlobalVariable(*M, Int32Ty, true, GlobalValue::ExternalLinkage,Constant::getAllOnesValue(Int32Ty), "var", nullptr,GlobalVariable::NotThreadLocal, 1);EXPECT_DEATH(Var->setAlignment(Align(8589934592ULL)),"Alignment is greater than MaximumAlignment");}#endif#endifTEST(ValueTest, printSlots) {// Check that Value::print() and Value::printAsOperand() work with and// without a slot tracker.LLVMContext C;const char *ModuleString = "@g0 = external global %500\n""@g1 = external global %900\n""\n""%900 = type { i32, i32 }\n""%500 = type { i32 }\n""\n""define void @f(i32 %x, i32 %y) {\n""entry:\n"" %0 = add i32 %y, 1\n"" %1 = add i32 %y, 1\n"" ret void\n""}\n";SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");ASSERT_TRUE(F);ASSERT_FALSE(F->empty());BasicBlock &BB = F->getEntryBlock();ASSERT_EQ(3u, BB.size());Instruction *I0 = &*BB.begin();ASSERT_TRUE(I0);Instruction *I1 = &*++BB.begin();ASSERT_TRUE(I1);GlobalVariable *G0 = M->getGlobalVariable("g0");ASSERT_TRUE(G0);GlobalVariable *G1 = M->getGlobalVariable("g1");ASSERT_TRUE(G1);ModuleSlotTracker MST(M.get());#define CHECK_PRINT(INST, STR) \do { \{ \std::string S; \raw_string_ostream OS(S); \INST->print(OS); \EXPECT_EQ(STR, OS.str()); \} \{ \std::string S; \raw_string_ostream OS(S); \INST->print(OS, MST); \EXPECT_EQ(STR, OS.str()); \} \} while (false)CHECK_PRINT(I0, " %0 = add i32 %y, 1");CHECK_PRINT(I1, " %1 = add i32 %y, 1");#undef CHECK_PRINT#define CHECK_PRINT_AS_OPERAND(INST, TYPE, STR) \do { \{ \std::string S; \raw_string_ostream OS(S); \INST->printAsOperand(OS, TYPE); \EXPECT_EQ(StringRef(STR), StringRef(OS.str())); \} \{ \std::string S; \raw_string_ostream OS(S); \INST->printAsOperand(OS, TYPE, MST); \EXPECT_EQ(StringRef(STR), StringRef(OS.str())); \} \} while (false)CHECK_PRINT_AS_OPERAND(I0, false, "%0");CHECK_PRINT_AS_OPERAND(I1, false, "%1");CHECK_PRINT_AS_OPERAND(I0, true, "i32 %0");CHECK_PRINT_AS_OPERAND(I1, true, "i32 %1");CHECK_PRINT_AS_OPERAND(G0, true, "ptr @g0");CHECK_PRINT_AS_OPERAND(G1, true, "ptr @g1");#undef CHECK_PRINT_AS_OPERAND}TEST(ValueTest, getLocalSlots) {// Verify that the getLocalSlot method returns the correct slot numbers.LLVMContext C;const char *ModuleString = "define void @f(i32 %x, i32 %y) {\n""entry:\n"" %0 = add i32 %y, 1\n"" %1 = add i32 %y, 1\n"" br label %2\n""\n"" ret void\n""}\n";SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");ASSERT_TRUE(F);ASSERT_FALSE(F->empty());BasicBlock &EntryBB = F->getEntryBlock();ASSERT_EQ(3u, EntryBB.size());BasicBlock *BB2 = &*++F->begin();ASSERT_TRUE(BB2);Instruction *I0 = &*EntryBB.begin();ASSERT_TRUE(I0);Instruction *I1 = &*++EntryBB.begin();ASSERT_TRUE(I1);ModuleSlotTracker MST(M.get());MST.incorporateFunction(*F);EXPECT_EQ(MST.getLocalSlot(I0), 0);EXPECT_EQ(MST.getLocalSlot(I1), 1);EXPECT_EQ(MST.getLocalSlot(&EntryBB), -1);EXPECT_EQ(MST.getLocalSlot(BB2), 2);}#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)TEST(ValueTest, getLocalSlotDeath) {LLVMContext C;const char *ModuleString = "define void @f(i32 %x, i32 %y) {\n""entry:\n"" %0 = add i32 %y, 1\n"" %1 = add i32 %y, 1\n"" br label %2\n""\n"" ret void\n""}\n";SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");ASSERT_TRUE(F);ASSERT_FALSE(F->empty());BasicBlock *BB2 = &*++F->begin();ASSERT_TRUE(BB2);ModuleSlotTracker MST(M.get());EXPECT_DEATH(MST.getLocalSlot(BB2), "No function incorporated");}#endifTEST(ValueTest, replaceUsesOutsideBlock) {// Check that Value::replaceUsesOutsideBlock(New, BB) replaces uses outside// BB, including dbg.* uses of MetadataAsValue(ValueAsMetadata(this)).const auto *IR = R"(define i32 @f() !dbg !6 {entry:%a = add i32 0, 1, !dbg !15%b = add i32 0, 2, !dbg !15%c = add i32 %a, 2, !dbg !15call void @llvm.dbg.value(metadata i32 %a, metadata !9, metadata !DIExpression()), !dbg !15br label %exit, !dbg !15exit:call void @llvm.dbg.value(metadata i32 %a, metadata !11, metadata !DIExpression()), !dbg !16ret i32 %a, !dbg !16}declare void @llvm.dbg.value(metadata, metadata, metadata)!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "test.ll", directory: "/")!2 = !{}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !8)!7 = !DISubroutineType(types: !2)!8 = !{!9, !11}!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)!10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_signed)!11 = !DILocalVariable(name: "2", scope: !6, file: !1, line: 2, type: !12)!12 = !DIBasicType(name: "ty64", size: 64, encoding: DW_ATE_signed)!15 = !DILocation(line: 1, column: 1, scope: !6)!16 = !DILocation(line: 5, column: 1, scope: !6))";LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx);if (!M)Err.print("ValueTest", errs());auto GetNext = [](auto *I) { return &*++I->getIterator(); };Function *F = M->getFunction("f");// Entry.BasicBlock *Entry = &F->front();Instruction *A = &Entry->front();Instruction *B = GetNext(A);Instruction *C = GetNext(B);auto *EntryDbg = cast<DbgValueInst>(GetNext(C));// Exit.BasicBlock *Exit = GetNext(Entry);auto *ExitDbg = cast<DbgValueInst>(&Exit->front());Instruction *Ret = GetNext(ExitDbg);A->replaceUsesOutsideBlock(B, Entry);// These users are in Entry so shouldn't be changed.ASSERT_TRUE(C->getOperand(0) == cast<Value>(A));ASSERT_TRUE(EntryDbg->getValue(0) == cast<Value>(A));// These users are outside Entry so should be changed.ASSERT_TRUE(ExitDbg->getValue(0) == cast<Value>(B));ASSERT_TRUE(Ret->getOperand(0) == cast<Value>(B));}} // end anonymous namespace
//===- llvm/unittest/ADT/ValueMapTest.cpp - ValueMap unit tests -*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/ValueMap.h"#include "llvm/Config/llvm-config.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Test fixturetemplate<typename T>class ValueMapTest : public testing::Test {protected:LLVMContext Context;Constant *ConstantV;std::unique_ptr<BitCastInst> BitcastV;std::unique_ptr<BinaryOperator> AddV;ValueMapTest(): ConstantV(ConstantInt::get(Type::getInt32Ty(Context), 0)),BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(Context))),AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) {}};// Run everything on Value*, a subtype to make sure that casting works as// expected, and a const subtype to make sure we cast const correctly.typedef ::testing::Types<Value, Instruction, const Instruction> KeyTypes;TYPED_TEST_SUITE(ValueMapTest, KeyTypes, );TYPED_TEST(ValueMapTest, Null) {ValueMap<TypeParam*, int> VM1;VM1[nullptr] = 7;EXPECT_EQ(7, VM1.lookup(nullptr));}TYPED_TEST(ValueMapTest, FollowsValue) {ValueMap<TypeParam*, int> VM;VM[this->BitcastV.get()] = 7;EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));EXPECT_EQ(0u, VM.count(this->AddV.get()));this->BitcastV->replaceAllUsesWith(this->AddV.get());EXPECT_EQ(7, VM.lookup(this->AddV.get()));EXPECT_EQ(0u, VM.count(this->BitcastV.get()));this->AddV.reset();EXPECT_EQ(0u, VM.count(this->AddV.get()));EXPECT_EQ(0u, VM.count(this->BitcastV.get()));EXPECT_EQ(0U, VM.size());}TYPED_TEST(ValueMapTest, OperationsWork) {ValueMap<TypeParam*, int> VM;ValueMap<TypeParam*, int> VM2(16); (void)VM2;typename ValueMapConfig<TypeParam*>::ExtraData Data;ValueMap<TypeParam*, int> VM3(Data, 16); (void)VM3;EXPECT_TRUE(VM.empty());VM[this->BitcastV.get()] = 7;// Find:typename ValueMap<TypeParam*, int>::iterator I =VM.find(this->BitcastV.get());ASSERT_TRUE(I != VM.end());EXPECT_EQ(this->BitcastV.get(), I->first);EXPECT_EQ(7, I->second);EXPECT_TRUE(VM.find(this->AddV.get()) == VM.end());// Const find:const ValueMap<TypeParam*, int> &CVM = VM;typename ValueMap<TypeParam*, int>::const_iterator CI =CVM.find(this->BitcastV.get());ASSERT_TRUE(CI != CVM.end());EXPECT_EQ(this->BitcastV.get(), CI->first);EXPECT_EQ(7, CI->second);EXPECT_TRUE(CVM.find(this->AddV.get()) == CVM.end());// Insert:std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult1 =VM.insert(std::make_pair(this->AddV.get(), 3));EXPECT_EQ(this->AddV.get(), InsertResult1.first->first);EXPECT_EQ(3, InsertResult1.first->second);EXPECT_TRUE(InsertResult1.second);EXPECT_EQ(1u, VM.count(this->AddV.get()));std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult2 =VM.insert(std::make_pair(this->AddV.get(), 5));EXPECT_EQ(this->AddV.get(), InsertResult2.first->first);EXPECT_EQ(3, InsertResult2.first->second);EXPECT_FALSE(InsertResult2.second);// Erase:VM.erase(InsertResult2.first);EXPECT_EQ(0U, VM.count(this->AddV.get()));EXPECT_EQ(1U, VM.count(this->BitcastV.get()));VM.erase(this->BitcastV.get());EXPECT_EQ(0U, VM.count(this->BitcastV.get()));EXPECT_EQ(0U, VM.size());// Range insert:SmallVector<std::pair<Instruction*, int>, 2> Elems;Elems.push_back(std::make_pair(this->AddV.get(), 1));Elems.push_back(std::make_pair(this->BitcastV.get(), 2));VM.insert(Elems.begin(), Elems.end());EXPECT_EQ(1, VM.lookup(this->AddV.get()));EXPECT_EQ(2, VM.lookup(this->BitcastV.get()));}template<typename ExpectedType, typename VarType>void CompileAssertHasType(VarType) {static_assert(std::is_same<ExpectedType, VarType>::value,"Not the same type");}TYPED_TEST(ValueMapTest, Iteration) {ValueMap<TypeParam*, int> VM;VM[this->BitcastV.get()] = 2;VM[this->AddV.get()] = 3;size_t size = 0;for (typename ValueMap<TypeParam*, int>::iterator I = VM.begin(), E = VM.end();I != E; ++I) {++size;std::pair<TypeParam*, int> value = *I; (void)value;CompileAssertHasType<TypeParam*>(I->first);if (I->second == 2) {EXPECT_EQ(this->BitcastV.get(), I->first);I->second = 5;} else if (I->second == 3) {EXPECT_EQ(this->AddV.get(), I->first);I->second = 6;} else {ADD_FAILURE() << "Iterated through an extra value.";}}EXPECT_EQ(2U, size);EXPECT_EQ(5, VM[this->BitcastV.get()]);EXPECT_EQ(6, VM[this->AddV.get()]);size = 0;// Cast to const ValueMap to avoid a bug in DenseMap's iterators.const ValueMap<TypeParam*, int>& CVM = VM;for (typename ValueMap<TypeParam*, int>::const_iterator I = CVM.begin(),E = CVM.end(); I != E; ++I) {++size;std::pair<TypeParam*, int> value = *I; (void)value;CompileAssertHasType<TypeParam*>(I->first);if (I->second == 5) {EXPECT_EQ(this->BitcastV.get(), I->first);} else if (I->second == 6) {EXPECT_EQ(this->AddV.get(), I->first);} else {ADD_FAILURE() << "Iterated through an extra value.";}}EXPECT_EQ(2U, size);}TYPED_TEST(ValueMapTest, DefaultCollisionBehavior) {// By default, we overwrite the old value with the replaced value.ValueMap<TypeParam*, int> VM;VM[this->BitcastV.get()] = 7;VM[this->AddV.get()] = 9;this->BitcastV->replaceAllUsesWith(this->AddV.get());EXPECT_EQ(0u, VM.count(this->BitcastV.get()));EXPECT_EQ(9, VM.lookup(this->AddV.get()));}TYPED_TEST(ValueMapTest, ConfiguredCollisionBehavior) {// TODO: Implement this when someone needs it.}template<typename KeyT, typename MutexT>struct LockMutex : ValueMapConfig<KeyT, MutexT> {struct ExtraData {MutexT *M;bool *CalledRAUW;bool *CalledDeleted;};static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) {*Data.CalledRAUW = true;EXPECT_FALSE(Data.M->try_lock()) << "Mutex should already be locked.";}static void onDelete(const ExtraData &Data, KeyT Old) {*Data.CalledDeleted = true;EXPECT_FALSE(Data.M->try_lock()) << "Mutex should already be locked.";}static MutexT *getMutex(const ExtraData &Data) { return Data.M; }};// FIXME: These tests started failing on Windows.#if LLVM_ENABLE_THREADS && !defined(_WIN32)TYPED_TEST(ValueMapTest, LocksMutex) {std::mutex M;bool CalledRAUW = false, CalledDeleted = false;typedef LockMutex<TypeParam*, std::mutex> ConfigType;typename ConfigType::ExtraData Data = {&M, &CalledRAUW, &CalledDeleted};ValueMap<TypeParam*, int, ConfigType> VM(Data);VM[this->BitcastV.get()] = 7;this->BitcastV->replaceAllUsesWith(this->AddV.get());this->AddV.reset();EXPECT_TRUE(CalledRAUW);EXPECT_TRUE(CalledDeleted);}#endiftemplate<typename KeyT>struct NoFollow : ValueMapConfig<KeyT> {enum { FollowRAUW = false };};TYPED_TEST(ValueMapTest, NoFollowRAUW) {ValueMap<TypeParam*, int, NoFollow<TypeParam*> > VM;VM[this->BitcastV.get()] = 7;EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));EXPECT_EQ(0u, VM.count(this->AddV.get()));this->BitcastV->replaceAllUsesWith(this->AddV.get());EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));EXPECT_EQ(0, VM.lookup(this->AddV.get()));this->AddV.reset();EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));EXPECT_EQ(0, VM.lookup(this->AddV.get()));this->BitcastV.reset();EXPECT_EQ(0, VM.lookup(this->BitcastV.get()));EXPECT_EQ(0, VM.lookup(this->AddV.get()));EXPECT_EQ(0U, VM.size());}template<typename KeyT>struct CountOps : ValueMapConfig<KeyT> {struct ExtraData {int *Deletions;int *RAUWs;};static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) {++*Data.RAUWs;}static void onDelete(const ExtraData &Data, KeyT Old) {++*Data.Deletions;}};TYPED_TEST(ValueMapTest, CallsConfig) {int Deletions = 0, RAUWs = 0;typename CountOps<TypeParam*>::ExtraData Data = {&Deletions, &RAUWs};ValueMap<TypeParam*, int, CountOps<TypeParam*> > VM(Data);VM[this->BitcastV.get()] = 7;this->BitcastV->replaceAllUsesWith(this->AddV.get());EXPECT_EQ(0, Deletions);EXPECT_EQ(1, RAUWs);this->AddV.reset();EXPECT_EQ(1, Deletions);EXPECT_EQ(1, RAUWs);this->BitcastV.reset();EXPECT_EQ(1, Deletions);EXPECT_EQ(1, RAUWs);}template<typename KeyT>struct ModifyingConfig : ValueMapConfig<KeyT> {// We'll put a pointer here back to the ValueMap this key is in, so// that we can modify it (and clobber *this) before the ValueMap// tries to do the same modification. In previous versions of// ValueMap, that exploded.typedef ValueMap<KeyT, int, ModifyingConfig<KeyT> > **ExtraData;static void onRAUW(ExtraData Map, KeyT Old, KeyT New) {(*Map)->erase(Old);}static void onDelete(ExtraData Map, KeyT Old) {(*Map)->erase(Old);}};TYPED_TEST(ValueMapTest, SurvivesModificationByConfig) {ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > *MapAddress;ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > VM(&MapAddress);MapAddress = &VM;// Now the ModifyingConfig can modify the Map inside a callback.VM[this->BitcastV.get()] = 7;this->BitcastV->replaceAllUsesWith(this->AddV.get());EXPECT_EQ(0u, VM.count(this->BitcastV.get()));EXPECT_EQ(0u, VM.count(this->AddV.get()));VM[this->AddV.get()] = 7;this->AddV.reset();EXPECT_EQ(0u, VM.count(this->AddV.get()));}} // end namespace
//===- ValueHandleTest.cpp - ValueHandle tests ----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/ValueHandle.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "gtest/gtest.h"#include <memory>using namespace llvm;namespace {class ValueHandle : public testing::Test {protected:LLVMContext Context;Constant *ConstantV;std::unique_ptr<BitCastInst> BitcastV;ValueHandle(): ConstantV(ConstantInt::get(Type::getInt32Ty(Context), 0)),BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(Context))) {}};class ConcreteCallbackVH final : public CallbackVH {public:ConcreteCallbackVH(Value *V) : CallbackVH(V) {}};TEST_F(ValueHandle, WeakVH_BasicOperation) {WeakVH WVH(BitcastV.get());EXPECT_EQ(BitcastV.get(), WVH);WVH = ConstantV;EXPECT_EQ(ConstantV, WVH);// Make sure I can call a method on the underlying Value. It// doesn't matter which method.EXPECT_EQ(Type::getInt32Ty(Context), WVH->getType());EXPECT_EQ(Type::getInt32Ty(Context), (*WVH).getType());WVH = BitcastV.get();BitcastV->replaceAllUsesWith(ConstantV);EXPECT_EQ(WVH, BitcastV.get());BitcastV.reset();EXPECT_EQ(WVH, nullptr);}TEST_F(ValueHandle, WeakTrackingVH_BasicOperation) {WeakTrackingVH WVH(BitcastV.get());EXPECT_EQ(BitcastV.get(), WVH);WVH = ConstantV;EXPECT_EQ(ConstantV, WVH);// Make sure I can call a method on the underlying Value. It// doesn't matter which method.EXPECT_EQ(Type::getInt32Ty(Context), WVH->getType());EXPECT_EQ(Type::getInt32Ty(Context), (*WVH).getType());}TEST_F(ValueHandle, WeakTrackingVH_Comparisons) {WeakTrackingVH BitcastWVH(BitcastV.get());WeakTrackingVH ConstantWVH(ConstantV);EXPECT_TRUE(BitcastWVH == BitcastWVH);EXPECT_TRUE(BitcastV.get() == BitcastWVH);EXPECT_TRUE(BitcastWVH == BitcastV.get());EXPECT_FALSE(BitcastWVH == ConstantWVH);EXPECT_TRUE(BitcastWVH != ConstantWVH);EXPECT_TRUE(BitcastV.get() != ConstantWVH);EXPECT_TRUE(BitcastWVH != ConstantV);EXPECT_FALSE(BitcastWVH != BitcastWVH);// Cast to Value* so comparisons work.Value *BV = BitcastV.get();Value *CV = ConstantV;EXPECT_EQ(BV < CV, BitcastWVH < ConstantWVH);EXPECT_EQ(BV <= CV, BitcastWVH <= ConstantWVH);EXPECT_EQ(BV > CV, BitcastWVH > ConstantWVH);EXPECT_EQ(BV >= CV, BitcastWVH >= ConstantWVH);EXPECT_EQ(BV < CV, BitcastV.get() < ConstantWVH);EXPECT_EQ(BV <= CV, BitcastV.get() <= ConstantWVH);EXPECT_EQ(BV > CV, BitcastV.get() > ConstantWVH);EXPECT_EQ(BV >= CV, BitcastV.get() >= ConstantWVH);EXPECT_EQ(BV < CV, BitcastWVH < ConstantV);EXPECT_EQ(BV <= CV, BitcastWVH <= ConstantV);EXPECT_EQ(BV > CV, BitcastWVH > ConstantV);EXPECT_EQ(BV >= CV, BitcastWVH >= ConstantV);}TEST_F(ValueHandle, WeakTrackingVH_FollowsRAUW) {WeakTrackingVH WVH(BitcastV.get());WeakTrackingVH WVH_Copy(WVH);WeakTrackingVH WVH_Recreated(BitcastV.get());BitcastV->replaceAllUsesWith(ConstantV);EXPECT_EQ(ConstantV, WVH);EXPECT_EQ(ConstantV, WVH_Copy);EXPECT_EQ(ConstantV, WVH_Recreated);}TEST_F(ValueHandle, WeakTrackingVH_NullOnDeletion) {WeakTrackingVH WVH(BitcastV.get());WeakTrackingVH WVH_Copy(WVH);WeakTrackingVH WVH_Recreated(BitcastV.get());BitcastV.reset();Value *null_value = nullptr;EXPECT_EQ(null_value, WVH);EXPECT_EQ(null_value, WVH_Copy);EXPECT_EQ(null_value, WVH_Recreated);}TEST_F(ValueHandle, AssertingVH_BasicOperation) {AssertingVH<CastInst> AVH(BitcastV.get());CastInst *implicit_to_exact_type = AVH;(void)implicit_to_exact_type; // Avoid warning.AssertingVH<Value> GenericAVH(BitcastV.get());EXPECT_EQ(BitcastV.get(), GenericAVH);GenericAVH = ConstantV;EXPECT_EQ(ConstantV, GenericAVH);// Make sure I can call a method on the underlying CastInst. It// doesn't matter which method.EXPECT_FALSE(AVH->mayWriteToMemory());EXPECT_FALSE((*AVH).mayWriteToMemory());}TEST_F(ValueHandle, AssertingVH_Const) {const CastInst *ConstBitcast = BitcastV.get();AssertingVH<const CastInst> AVH(ConstBitcast);const CastInst *implicit_to_exact_type = AVH;(void)implicit_to_exact_type; // Avoid warning.}TEST_F(ValueHandle, AssertingVH_Comparisons) {AssertingVH<Value> BitcastAVH(BitcastV.get());AssertingVH<Value> ConstantAVH(ConstantV);EXPECT_TRUE(BitcastAVH == BitcastAVH);EXPECT_TRUE(BitcastV.get() == BitcastAVH);EXPECT_TRUE(BitcastAVH == BitcastV.get());EXPECT_FALSE(BitcastAVH == ConstantAVH);EXPECT_TRUE(BitcastAVH != ConstantAVH);EXPECT_TRUE(BitcastV.get() != ConstantAVH);EXPECT_TRUE(BitcastAVH != ConstantV);EXPECT_FALSE(BitcastAVH != BitcastAVH);// Cast to Value* so comparisons work.Value *BV = BitcastV.get();Value *CV = ConstantV;EXPECT_EQ(BV < CV, BitcastAVH < ConstantAVH);EXPECT_EQ(BV <= CV, BitcastAVH <= ConstantAVH);EXPECT_EQ(BV > CV, BitcastAVH > ConstantAVH);EXPECT_EQ(BV >= CV, BitcastAVH >= ConstantAVH);EXPECT_EQ(BV < CV, BitcastV.get() < ConstantAVH);EXPECT_EQ(BV <= CV, BitcastV.get() <= ConstantAVH);EXPECT_EQ(BV > CV, BitcastV.get() > ConstantAVH);EXPECT_EQ(BV >= CV, BitcastV.get() >= ConstantAVH);EXPECT_EQ(BV < CV, BitcastAVH < ConstantV);EXPECT_EQ(BV <= CV, BitcastAVH <= ConstantV);EXPECT_EQ(BV > CV, BitcastAVH > ConstantV);EXPECT_EQ(BV >= CV, BitcastAVH >= ConstantV);}TEST_F(ValueHandle, AssertingVH_DoesNotFollowRAUW) {AssertingVH<Value> AVH(BitcastV.get());BitcastV->replaceAllUsesWith(ConstantV);EXPECT_EQ(BitcastV.get(), AVH);}#ifdef NDEBUGTEST_F(ValueHandle, AssertingVH_ReducesToPointer) {EXPECT_EQ(sizeof(CastInst *), sizeof(AssertingVH<CastInst>));}#elif LLVM_ENABLE_ABI_BREAKING_CHECKS // && !NDEBUG#ifdef GTEST_HAS_DEATH_TESTTEST_F(ValueHandle, AssertingVH_Asserts) {AssertingVH<Value> AVH(BitcastV.get());EXPECT_DEATH({BitcastV.reset();},"An asserting value handle still pointed to this value!");AssertingVH<Value> Copy(AVH);AVH = nullptr;EXPECT_DEATH({BitcastV.reset();},"An asserting value handle still pointed to this value!");Copy = nullptr;BitcastV.reset();}#endif // GTEST_HAS_DEATH_TEST#endif // NDEBUGTEST_F(ValueHandle, CallbackVH_BasicOperation) {ConcreteCallbackVH CVH(BitcastV.get());EXPECT_EQ(BitcastV.get(), CVH);CVH = ConstantV;EXPECT_EQ(ConstantV, CVH);// Make sure I can call a method on the underlying Value. It// doesn't matter which method.EXPECT_EQ(Type::getInt32Ty(Context), CVH->getType());EXPECT_EQ(Type::getInt32Ty(Context), (*CVH).getType());}TEST_F(ValueHandle, CallbackVH_Comparisons) {ConcreteCallbackVH BitcastCVH(BitcastV.get());ConcreteCallbackVH ConstantCVH(ConstantV);EXPECT_TRUE(BitcastCVH == BitcastCVH);EXPECT_TRUE(BitcastV.get() == BitcastCVH);EXPECT_TRUE(BitcastCVH == BitcastV.get());EXPECT_FALSE(BitcastCVH == ConstantCVH);EXPECT_TRUE(BitcastCVH != ConstantCVH);EXPECT_TRUE(BitcastV.get() != ConstantCVH);EXPECT_TRUE(BitcastCVH != ConstantV);EXPECT_FALSE(BitcastCVH != BitcastCVH);// Cast to Value* so comparisons work.Value *BV = BitcastV.get();Value *CV = ConstantV;EXPECT_EQ(BV < CV, BitcastCVH < ConstantCVH);EXPECT_EQ(BV <= CV, BitcastCVH <= ConstantCVH);EXPECT_EQ(BV > CV, BitcastCVH > ConstantCVH);EXPECT_EQ(BV >= CV, BitcastCVH >= ConstantCVH);EXPECT_EQ(BV < CV, BitcastV.get() < ConstantCVH);EXPECT_EQ(BV <= CV, BitcastV.get() <= ConstantCVH);EXPECT_EQ(BV > CV, BitcastV.get() > ConstantCVH);EXPECT_EQ(BV >= CV, BitcastV.get() >= ConstantCVH);EXPECT_EQ(BV < CV, BitcastCVH < ConstantV);EXPECT_EQ(BV <= CV, BitcastCVH <= ConstantV);EXPECT_EQ(BV > CV, BitcastCVH > ConstantV);EXPECT_EQ(BV >= CV, BitcastCVH >= ConstantV);}TEST_F(ValueHandle, CallbackVH_CallbackOnDeletion) {class RecordingVH final : public CallbackVH {public:int DeletedCalls;int AURWCalls;RecordingVH() : DeletedCalls(0), AURWCalls(0) {}RecordingVH(Value *V) : CallbackVH(V), DeletedCalls(0), AURWCalls(0) {}private:void deleted() override {DeletedCalls++;CallbackVH::deleted();}void allUsesReplacedWith(Value *) override { AURWCalls++; }};RecordingVH RVH;RVH = BitcastV.get();EXPECT_EQ(0, RVH.DeletedCalls);EXPECT_EQ(0, RVH.AURWCalls);BitcastV.reset();EXPECT_EQ(1, RVH.DeletedCalls);EXPECT_EQ(0, RVH.AURWCalls);}TEST_F(ValueHandle, CallbackVH_CallbackOnRAUW) {class RecordingVH final : public CallbackVH {public:int DeletedCalls;Value *AURWArgument;RecordingVH() : DeletedCalls(0), AURWArgument(nullptr) {}RecordingVH(Value *V): CallbackVH(V), DeletedCalls(0), AURWArgument(nullptr) {}private:void deleted() override {DeletedCalls++;CallbackVH::deleted();}void allUsesReplacedWith(Value *new_value) override {EXPECT_EQ(nullptr, AURWArgument);AURWArgument = new_value;}};RecordingVH RVH;RVH = BitcastV.get();EXPECT_EQ(0, RVH.DeletedCalls);EXPECT_EQ(nullptr, RVH.AURWArgument);BitcastV->replaceAllUsesWith(ConstantV);EXPECT_EQ(0, RVH.DeletedCalls);EXPECT_EQ(ConstantV, RVH.AURWArgument);}TEST_F(ValueHandle, CallbackVH_DeletionCanRAUW) {class RecoveringVH final : public CallbackVH {public:int DeletedCalls;Value *AURWArgument;LLVMContext *Context;RecoveringVH(LLVMContext &TheContext): DeletedCalls(0), AURWArgument(nullptr), Context(&TheContext) {}RecoveringVH(LLVMContext &TheContext, Value *V): CallbackVH(V), DeletedCalls(0), AURWArgument(nullptr),Context(&TheContext) {}private:void deleted() override {getValPtr()->replaceAllUsesWith(Constant::getNullValue(Type::getInt32Ty(*Context)));setValPtr(nullptr);}void allUsesReplacedWith(Value *new_value) override {ASSERT_TRUE(nullptr != getValPtr());EXPECT_EQ(1U, getValPtr()->getNumUses());EXPECT_EQ(nullptr, AURWArgument);AURWArgument = new_value;}};// Normally, if a value has uses, deleting it will crash. However, we can use// a CallbackVH to remove the uses before the check for no uses.RecoveringVH RVH(Context);RVH = RecoveringVH(Context, BitcastV.get());std::unique_ptr<BinaryOperator> BitcastUser(BinaryOperator::CreateAdd(RVH, Constant::getNullValue(Type::getInt32Ty(Context))));EXPECT_EQ(BitcastV.get(), BitcastUser->getOperand(0));BitcastV.reset(); // Would crash without the ValueHandler.EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(Context)),RVH.AURWArgument);EXPECT_EQ(Constant::getNullValue(Type::getInt32Ty(Context)),BitcastUser->getOperand(0));}TEST_F(ValueHandle, DestroyingOtherVHOnSameValueDoesntBreakIteration) {// When a CallbackVH modifies other ValueHandles in its callbacks,// that shouldn't interfere with non-modified ValueHandles receiving// their appropriate callbacks.//// We create the active CallbackVH in the middle of a palindromic// arrangement of other VHs so that the bad behavior would be// triggered in whichever order callbacks run.class DestroyingVH final : public CallbackVH {public:std::unique_ptr<WeakTrackingVH> ToClear[2];DestroyingVH(Value *V) {ToClear[0].reset(new WeakTrackingVH(V));setValPtr(V);ToClear[1].reset(new WeakTrackingVH(V));}void deleted() override {ToClear[0].reset();ToClear[1].reset();CallbackVH::deleted();}void allUsesReplacedWith(Value *) override {ToClear[0].reset();ToClear[1].reset();}};{WeakTrackingVH ShouldBeVisited1(BitcastV.get());DestroyingVH C(BitcastV.get());WeakTrackingVH ShouldBeVisited2(BitcastV.get());BitcastV->replaceAllUsesWith(ConstantV);EXPECT_EQ(ConstantV, static_cast<Value*>(ShouldBeVisited1));EXPECT_EQ(ConstantV, static_cast<Value*>(ShouldBeVisited2));}{WeakTrackingVH ShouldBeVisited1(BitcastV.get());DestroyingVH C(BitcastV.get());WeakTrackingVH ShouldBeVisited2(BitcastV.get());BitcastV.reset();EXPECT_EQ(nullptr, static_cast<Value*>(ShouldBeVisited1));EXPECT_EQ(nullptr, static_cast<Value*>(ShouldBeVisited2));}}TEST_F(ValueHandle, AssertingVHCheckedLast) {// If a CallbackVH exists to clear out a group of AssertingVHs on// Value deletion, the CallbackVH should get a chance to do so// before the AssertingVHs assert.class ClearingVH final : public CallbackVH {public:AssertingVH<Value> *ToClear[2];ClearingVH(Value *V,AssertingVH<Value> &A0, AssertingVH<Value> &A1): CallbackVH(V) {ToClear[0] = &A0;ToClear[1] = &A1;}void deleted() override {*ToClear[0] = nullptr;*ToClear[1] = nullptr;CallbackVH::deleted();}};AssertingVH<Value> A1, A2;A1 = BitcastV.get();ClearingVH C(BitcastV.get(), A1, A2);A2 = BitcastV.get();// C.deleted() should run first, clearing the two AssertingVHs,// which should prevent them from asserting.BitcastV.reset();}TEST_F(ValueHandle, PoisoningVH_BasicOperation) {PoisoningVH<CastInst> VH(BitcastV.get());CastInst *implicit_to_exact_type = VH;(void)implicit_to_exact_type; // Avoid warning.PoisoningVH<Value> GenericVH(BitcastV.get());EXPECT_EQ(BitcastV.get(), GenericVH);GenericVH = ConstantV;EXPECT_EQ(ConstantV, GenericVH);// Make sure I can call a method on the underlying CastInst. It// doesn't matter which method.EXPECT_FALSE(VH->mayWriteToMemory());EXPECT_FALSE((*VH).mayWriteToMemory());}TEST_F(ValueHandle, PoisoningVH_Const) {const CastInst *ConstBitcast = BitcastV.get();PoisoningVH<const CastInst> VH(ConstBitcast);const CastInst *implicit_to_exact_type = VH;(void)implicit_to_exact_type; // Avoid warning.}TEST_F(ValueHandle, PoisoningVH_Comparisons) {PoisoningVH<Value> BitcastVH(BitcastV.get());PoisoningVH<Value> ConstantVH(ConstantV);EXPECT_TRUE(BitcastVH == BitcastVH);EXPECT_TRUE(BitcastV.get() == BitcastVH);EXPECT_TRUE(BitcastVH == BitcastV.get());EXPECT_FALSE(BitcastVH == ConstantVH);EXPECT_TRUE(BitcastVH != ConstantVH);EXPECT_TRUE(BitcastV.get() != ConstantVH);EXPECT_TRUE(BitcastVH != ConstantV);EXPECT_FALSE(BitcastVH != BitcastVH);// Cast to Value* so comparisons work.Value *BV = BitcastV.get();Value *CV = ConstantV;EXPECT_EQ(BV < CV, BitcastVH < ConstantVH);EXPECT_EQ(BV <= CV, BitcastVH <= ConstantVH);EXPECT_EQ(BV > CV, BitcastVH > ConstantVH);EXPECT_EQ(BV >= CV, BitcastVH >= ConstantVH);EXPECT_EQ(BV < CV, BitcastV.get() < ConstantVH);EXPECT_EQ(BV <= CV, BitcastV.get() <= ConstantVH);EXPECT_EQ(BV > CV, BitcastV.get() > ConstantVH);EXPECT_EQ(BV >= CV, BitcastV.get() >= ConstantVH);EXPECT_EQ(BV < CV, BitcastVH < ConstantV);EXPECT_EQ(BV <= CV, BitcastVH <= ConstantV);EXPECT_EQ(BV > CV, BitcastVH > ConstantV);EXPECT_EQ(BV >= CV, BitcastVH >= ConstantV);}TEST_F(ValueHandle, PoisoningVH_DoesNotFollowRAUW) {PoisoningVH<Value> VH(BitcastV.get());BitcastV->replaceAllUsesWith(ConstantV);EXPECT_TRUE(DenseMapInfo<PoisoningVH<Value>>::isEqual(VH, BitcastV.get()));}TEST_F(ValueHandle, AssertingVH_DenseMap) {DenseMap<AssertingVH<Value>, int> Map;Map.insert({BitcastV.get(), 1});Map.insert({ConstantV, 2});// These will create a temporary AssertingVH during lookup.EXPECT_TRUE(Map.find(BitcastV.get()) != Map.end());EXPECT_TRUE(Map.find(ConstantV) != Map.end());// These will not create a temporary AssertingVH.EXPECT_TRUE(Map.find_as(BitcastV.get()) != Map.end());EXPECT_TRUE(Map.find_as(ConstantV) != Map.end());}TEST_F(ValueHandle, PoisoningVH_DenseMap) {DenseMap<PoisoningVH<Value>, int> Map;Map.insert({BitcastV.get(), 1});Map.insert({ConstantV, 2});// These will create a temporary PoisoningVH during lookup.EXPECT_TRUE(Map.find(BitcastV.get()) != Map.end());EXPECT_TRUE(Map.find(ConstantV) != Map.end());// These will not create a temporary PoisoningVH.EXPECT_TRUE(Map.find_as(BitcastV.get()) != Map.end());EXPECT_TRUE(Map.find_as(ConstantV) != Map.end());}#ifdef NDEBUGTEST_F(ValueHandle, PoisoningVH_ReducesToPointer) {EXPECT_EQ(sizeof(CastInst *), sizeof(PoisoningVH<CastInst>));}#else // !NDEBUGTEST_F(ValueHandle, TrackingVH_Tracks) {TrackingVH<Value> VH(BitcastV.get());BitcastV->replaceAllUsesWith(ConstantV);EXPECT_EQ(VH, ConstantV);}#ifdef GTEST_HAS_DEATH_TEST#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST_F(ValueHandle, PoisoningVH_Asserts) {PoisoningVH<Value> VH(BitcastV.get());// The poisoned handle shouldn't assert when the value is deleted.BitcastV.reset(new BitCastInst(ConstantV, Type::getInt32Ty(Context)));// But should when we access the handle.EXPECT_DEATH((void)*VH, "Accessed a poisoned value handle!");// Now check that poison catches RAUW.VH = BitcastV.get();// The replace doesn't trigger anything immediately.BitcastV->replaceAllUsesWith(ConstantV);// But a use does.EXPECT_DEATH((void)*VH, "Accessed a poisoned value handle!");// Don't clear anything out here as destroying the handles should be fine.}#endif // LLVM_ENABLE_ABI_BREAKING_CHECKSTEST_F(ValueHandle, TrackingVH_Asserts) {{TrackingVH<Value> VH(BitcastV.get());// The tracking handle shouldn't assert when the value is deleted.BitcastV.reset(new BitCastInst(ConstantV, Type::getInt32Ty(Context)));// But should when we access the handle.EXPECT_DEATH((void)*VH,"TrackingVH must be non-null and valid on dereference!");}{TrackingVH<Instruction> VH(BitcastV.get());BitcastV->replaceAllUsesWith(ConstantV);EXPECT_DEATH((void)*VH,"Tracked Value was replaced by one with an invalid type!");}}#endif // GTEST_HAS_DEATH_TEST#endif // NDEBUG}
//===- VPIntrinsicTest.cpp - VPIntrinsic unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallVector.h"#include "llvm/AsmParser/Parser.h"#include "llvm/CodeGen/ISDOpcodes.h"#include "llvm/IR/Constants.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"#include <sstream>using namespace llvm;namespace {static const char *ReductionIntOpcodes[] = {"add", "mul", "and", "or", "xor", "smin", "smax", "umin", "umax"};static const char *ReductionFPOpcodes[] = {"fadd", "fmul", "fmin", "fmax"};class VPIntrinsicTest : public testing::Test {protected:LLVMContext Context;VPIntrinsicTest() : Context() {}LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> createVPDeclarationModule() {const char *BinaryIntOpcodes[] = {"add", "sub", "mul", "sdiv", "srem","udiv", "urem", "and", "xor", "or","ashr", "lshr", "shl"};std::stringstream Str;for (const char *BinaryIntOpcode : BinaryIntOpcodes)Str << " declare <8 x i32> @llvm.vp." << BinaryIntOpcode<< ".v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) ";const char *BinaryFPOpcodes[] = {"fadd", "fsub", "fmul", "fdiv", "frem"};for (const char *BinaryFPOpcode : BinaryFPOpcodes)Str << " declare <8 x float> @llvm.vp." << BinaryFPOpcode<< ".v8f32(<8 x float>, <8 x float>, <8 x i1>, i32) ";Str << " declare <8 x float> @llvm.vp.fneg.v8f32(<8 x float>, <8 x i1>, ""i32)";Str << " declare <8 x float> @llvm.vp.fma.v8f32(<8 x float>, <8 x float>, ""<8 x float>, <8 x i1>, i32) ";Str << " declare void @llvm.vp.store.v8i32.p0v8i32(<8 x i32>, <8 x i32>*, ""<8 x i1>, i32) ";Str << "declare void ""@llvm.experimental.vp.strided.store.v8i32.i32(<8 x i32>, ""i32*, i32, <8 x i1>, i32) ";Str << "declare void ""@llvm.experimental.vp.strided.store.v8i32.p1i32.i32(<8 x i32>, ""i32 addrspace(1)*, i32, <8 x i1>, i32) ";Str << " declare void @llvm.vp.scatter.v8i32.v8p0i32(<8 x i32>, <8 x ""i32*>, <8 x i1>, i32) ";Str << " declare <8 x i32> @llvm.vp.load.v8i32.p0v8i32(<8 x i32>*, <8 x ""i1>, i32) ";Str << "declare <8 x i32> ""@llvm.experimental.vp.strided.load.v8i32.i32(i32*, i32, <8 ""x i1>, i32) ";Str << "declare <8 x i32> ""@llvm.experimental.vp.strided.load.v8i32.p1i32.i32(i32 ""addrspace(1)*, i32, <8 x i1>, i32) ";Str << " declare <8 x i32> @llvm.vp.gather.v8i32.v8p0i32(<8 x i32*>, <8 x ""i1>, i32) ";for (const char *ReductionOpcode : ReductionIntOpcodes)Str << " declare i32 @llvm.vp.reduce." << ReductionOpcode<< ".v8i32(i32, <8 x i32>, <8 x i1>, i32) ";for (const char *ReductionOpcode : ReductionFPOpcodes)Str << " declare float @llvm.vp.reduce." << ReductionOpcode<< ".v8f32(float, <8 x float>, <8 x i1>, i32) ";Str << " declare <8 x i32> @llvm.vp.merge.v8i32(<8 x i1>, <8 x i32>, <8 x ""i32>, i32)";Str << " declare <8 x i32> @llvm.vp.select.v8i32(<8 x i1>, <8 x i32>, <8 x ""i32>, i32)";Str << " declare <8 x i32> @llvm.experimental.vp.splice.v8i32(<8 x ""i32>, <8 x i32>, i32, <8 x i1>, i32, i32) ";Str << " declare <8 x i32> @llvm.vp.fptoui.v8i32"<< ".v8f32(<8 x float>, <8 x i1>, i32) ";Str << " declare <8 x i32> @llvm.vp.fptosi.v8i32"<< ".v8f32(<8 x float>, <8 x i1>, i32) ";Str << " declare <8 x float> @llvm.vp.uitofp.v8f32"<< ".v8i32(<8 x i32>, <8 x i1>, i32) ";Str << " declare <8 x float> @llvm.vp.sitofp.v8f32"<< ".v8i32(<8 x i32>, <8 x i1>, i32) ";Str << " declare <8 x float> @llvm.vp.fptrunc.v8f32"<< ".v8f64(<8 x double>, <8 x i1>, i32) ";Str << " declare <8 x double> @llvm.vp.fpext.v8f64"<< ".v8f32(<8 x float>, <8 x i1>, i32) ";Str << " declare <8 x i32> @llvm.vp.trunc.v8i32"<< ".v8i64(<8 x i64>, <8 x i1>, i32) ";Str << " declare <8 x i64> @llvm.vp.zext.v8i64"<< ".v8i32(<8 x i32>, <8 x i1>, i32) ";Str << " declare <8 x i64> @llvm.vp.sext.v8i64"<< ".v8i32(<8 x i32>, <8 x i1>, i32) ";Str << " declare <8 x i32> @llvm.vp.ptrtoint.v8i32"<< ".v8p0i32(<8 x i32*>, <8 x i1>, i32) ";Str << " declare <8 x i32*> @llvm.vp.inttoptr.v8p0i32"<< ".v8i32(<8 x i32>, <8 x i1>, i32) ";Str << " declare <8 x i1> @llvm.vp.fcmp.v8f32"<< "(<8 x float>, <8 x float>, metadata, <8 x i1>, i32) ";Str << " declare <8 x i1> @llvm.vp.icmp.v8i16"<< "(<8 x i16>, <8 x i16>, metadata, <8 x i1>, i32) ";return parseAssemblyString(Str.str(), Err, C);}};/// Check that the property scopes include/llvm/IR/VPIntrinsics.def are closed.TEST_F(VPIntrinsicTest, VPIntrinsicsDefScopes) {Optional<Intrinsic::ID> ScopeVPID;#define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) \ASSERT_FALSE(ScopeVPID.has_value()); \ScopeVPID = Intrinsic::VPID;#define END_REGISTER_VP_INTRINSIC(VPID) \ASSERT_TRUE(ScopeVPID.has_value()); \ASSERT_EQ(ScopeVPID.value(), Intrinsic::VPID); \ScopeVPID = None;Optional<ISD::NodeType> ScopeOPC;#define BEGIN_REGISTER_VP_SDNODE(SDOPC, ...) \ASSERT_FALSE(ScopeOPC.has_value()); \ScopeOPC = ISD::SDOPC;#define END_REGISTER_VP_SDNODE(SDOPC) \ASSERT_TRUE(ScopeOPC.has_value()); \ASSERT_EQ(ScopeOPC.value(), ISD::SDOPC); \ScopeOPC = None;#include "llvm/IR/VPIntrinsics.def"ASSERT_FALSE(ScopeVPID.has_value());ASSERT_FALSE(ScopeOPC.has_value());}/// Check that every VP intrinsic in the test module is recognized as a VP/// intrinsic.TEST_F(VPIntrinsicTest, VPModuleComplete) {std::unique_ptr<Module> M = createVPDeclarationModule();assert(M);// Check that all @llvm.vp.* functions in the module are recognized vp// intrinsics.std::set<Intrinsic::ID> SeenIDs;for (const auto &VPDecl : *M) {ASSERT_TRUE(VPDecl.isIntrinsic());ASSERT_TRUE(VPIntrinsic::isVPIntrinsic(VPDecl.getIntrinsicID()));SeenIDs.insert(VPDecl.getIntrinsicID());}// Check that every registered VP intrinsic has an instance in the test// module.#define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) \ASSERT_TRUE(SeenIDs.count(Intrinsic::VPID));#include "llvm/IR/VPIntrinsics.def"}/// Check that VPIntrinsic:canIgnoreVectorLengthParam() returns true/// if the vector length parameter does not mask off any lanes.TEST_F(VPIntrinsicTest, CanIgnoreVectorLength) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M =parseAssemblyString("declare <256 x i64> @llvm.vp.mul.v256i64(<256 x i64>, <256 x i64>, <256 x i1>, i32)""declare <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64>, <vscale x 2 x i64>, <vscale x 2 x i1>, i32)""declare <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64>, <vscale x 1 x i64>, <vscale x 1 x i1>, i32)""declare i32 @llvm.vscale.i32()""define void @test_static_vlen( "" <256 x i64> %i0, <vscale x 2 x i64> %si0x2, <vscale x 1 x i64> %si0x1,"" <256 x i64> %i1, <vscale x 2 x i64> %si1x2, <vscale x 1 x i64> %si1x1,"" <256 x i1> %m, <vscale x 2 x i1> %smx2, <vscale x 1 x i1> %smx1, i32 %vl) { "" %r0 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 %vl)"" %r1 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 256)"" %r2 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 0)"" %r3 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 7)"" %r4 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 123)"" %vs = call i32 @llvm.vscale.i32()"" %vs.x2 = mul i32 %vs, 2"" %r5 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 %vs.x2)"" %r6 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 %vs)"" %r7 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 99999)"" %r8 = call <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64> %si0x1, <vscale x 1 x i64> %si1x1, <vscale x 1 x i1> %smx1, i32 %vs)"" %r9 = call <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64> %si0x1, <vscale x 1 x i64> %si1x1, <vscale x 1 x i1> %smx1, i32 1)"" %r10 = call <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64> %si0x1, <vscale x 1 x i64> %si1x1, <vscale x 1 x i1> %smx1, i32 %vs.x2)"" %vs.wat = add i32 %vs, 2"" %r11 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 %vs.wat)"" ret void ""}",Err, C);auto *F = M->getFunction("test_static_vlen");assert(F);const bool Expected[] = {false, true, false, false, false, true,false, false, true, false, true, false};const auto *ExpectedIt = std::begin(Expected);for (auto &I : F->getEntryBlock()) {VPIntrinsic *VPI = dyn_cast<VPIntrinsic>(&I);if (!VPI)continue;ASSERT_NE(ExpectedIt, std::end(Expected));ASSERT_EQ(*ExpectedIt, VPI->canIgnoreVectorLengthParam());++ExpectedIt;}}/// Check that the argument returned by/// VPIntrinsic::get<X>ParamPos(Intrinsic::ID) has the expected type.TEST_F(VPIntrinsicTest, GetParamPos) {std::unique_ptr<Module> M = createVPDeclarationModule();assert(M);for (Function &F : *M) {ASSERT_TRUE(F.isIntrinsic());Optional<unsigned> MaskParamPos =VPIntrinsic::getMaskParamPos(F.getIntrinsicID());if (MaskParamPos) {Type *MaskParamType = F.getArg(MaskParamPos.value())->getType();ASSERT_TRUE(MaskParamType->isVectorTy());ASSERT_TRUE(cast<VectorType>(MaskParamType)->getElementType()->isIntegerTy(1));}Optional<unsigned> VecLenParamPos =VPIntrinsic::getVectorLengthParamPos(F.getIntrinsicID());if (VecLenParamPos) {Type *VecLenParamType = F.getArg(VecLenParamPos.value())->getType();ASSERT_TRUE(VecLenParamType->isIntegerTy(32));}}}/// Check that going from Opcode to VP intrinsic and back results in the same/// Opcode.TEST_F(VPIntrinsicTest, OpcodeRoundTrip) {std::vector<unsigned> Opcodes;Opcodes.reserve(100);{#define HANDLE_INST(OCNum, OCName, Class) Opcodes.push_back(OCNum);#include "llvm/IR/Instruction.def"}unsigned FullTripCounts = 0;for (unsigned OC : Opcodes) {Intrinsic::ID VPID = VPIntrinsic::getForOpcode(OC);// No equivalent VP intrinsic available.if (VPID == Intrinsic::not_intrinsic)continue;Optional<unsigned> RoundTripOC =VPIntrinsic::getFunctionalOpcodeForVP(VPID);// No equivalent Opcode available.if (!RoundTripOC)continue;ASSERT_EQ(*RoundTripOC, OC);++FullTripCounts;}ASSERT_NE(FullTripCounts, 0u);}/// Check that going from VP intrinsic to Opcode and back results in the same/// intrinsic id.TEST_F(VPIntrinsicTest, IntrinsicIDRoundTrip) {std::unique_ptr<Module> M = createVPDeclarationModule();assert(M);unsigned FullTripCounts = 0;for (const auto &VPDecl : *M) {auto VPID = VPDecl.getIntrinsicID();Optional<unsigned> OC = VPIntrinsic::getFunctionalOpcodeForVP(VPID);// no equivalent Opcode availableif (!OC)continue;Intrinsic::ID RoundTripVPID = VPIntrinsic::getForOpcode(*OC);ASSERT_EQ(RoundTripVPID, VPID);++FullTripCounts;}ASSERT_NE(FullTripCounts, 0u);}/// Check that VPIntrinsic::getDeclarationForParams works.TEST_F(VPIntrinsicTest, VPIntrinsicDeclarationForParams) {std::unique_ptr<Module> M = createVPDeclarationModule();assert(M);auto OutM = std::make_unique<Module>("", M->getContext());for (auto &F : *M) {auto *FuncTy = F.getFunctionType();// Declare intrinsic anew with explicit types.std::vector<Value *> Values;for (auto *ParamTy : FuncTy->params())Values.push_back(UndefValue::get(ParamTy));ASSERT_NE(F.getIntrinsicID(), Intrinsic::not_intrinsic);auto *NewDecl = VPIntrinsic::getDeclarationForParams(OutM.get(), F.getIntrinsicID(), FuncTy->getReturnType(), Values);ASSERT_TRUE(NewDecl);// Check that 'old decl' == 'new decl'.ASSERT_EQ(F.getIntrinsicID(), NewDecl->getIntrinsicID());FunctionType::param_iterator ItNewParams =NewDecl->getFunctionType()->param_begin();FunctionType::param_iterator EndItNewParams =NewDecl->getFunctionType()->param_end();for (auto *ParamTy : FuncTy->params()) {ASSERT_NE(ItNewParams, EndItNewParams);ASSERT_EQ(*ItNewParams, ParamTy);++ItNewParams;}}}/// Check that the HANDLE_VP_TO_CONSTRAINEDFP maps to an existing intrinsic with/// the right amount of constrained-fp metadata args.TEST_F(VPIntrinsicTest, HandleToConstrainedFP) {#define VP_PROPERTY_CONSTRAINEDFP(HASROUND, HASEXCEPT, CFPID) \{ \SmallVector<Intrinsic::IITDescriptor, 5> T; \Intrinsic::getIntrinsicInfoTableEntries(Intrinsic::CFPID, T); \unsigned NumMetadataArgs = 0; \for (auto TD : T) \NumMetadataArgs += (TD.Kind == Intrinsic::IITDescriptor::Metadata); \bool IsCmp = Intrinsic::CFPID == Intrinsic::experimental_constrained_fcmp; \ASSERT_EQ(NumMetadataArgs, (unsigned)(IsCmp + HASROUND + HASEXCEPT)); \}#include "llvm/IR/VPIntrinsics.def"}} // end anonymous namespace/// Check various properties of VPReductionIntrinsicsTEST_F(VPIntrinsicTest, VPReductions) {LLVMContext C;SMDiagnostic Err;std::stringstream Str;Str << "declare <8 x i32> @llvm.vp.mul.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, ""i32)";for (const char *ReductionOpcode : ReductionIntOpcodes)Str << " declare i32 @llvm.vp.reduce." << ReductionOpcode<< ".v8i32(i32, <8 x i32>, <8 x i1>, i32) ";for (const char *ReductionOpcode : ReductionFPOpcodes)Str << " declare float @llvm.vp.reduce." << ReductionOpcode<< ".v8f32(float, <8 x float>, <8 x i1>, i32) ";Str << "define void @test_reductions(i32 %start, <8 x i32> %val, float ""%fpstart, <8 x float> %fpval, <8 x i1> %m, i32 %vl) {";// Mix in a regular non-reduction intrinsic to check that the// VPReductionIntrinsic subclass works as intended.Str << " %r0 = call <8 x i32> @llvm.vp.mul.v8i32(<8 x i32> %val, <8 x i32> ""%val, <8 x i1> %m, i32 %vl)";unsigned Idx = 1;for (const char *ReductionOpcode : ReductionIntOpcodes)Str << " %r" << Idx++ << " = call i32 @llvm.vp.reduce." << ReductionOpcode<< ".v8i32(i32 %start, <8 x i32> %val, <8 x i1> %m, i32 %vl)";for (const char *ReductionOpcode : ReductionFPOpcodes)Str << " %r" << Idx++ << " = call float @llvm.vp.reduce."<< ReductionOpcode<< ".v8f32(float %fpstart, <8 x float> %fpval, <8 x i1> %m, i32 %vl)";Str << " ret void""}";std::unique_ptr<Module> M = parseAssemblyString(Str.str(), Err, C);assert(M);auto *F = M->getFunction("test_reductions");assert(F);for (const auto &I : F->getEntryBlock()) {const VPIntrinsic *VPI = dyn_cast<VPIntrinsic>(&I);if (!VPI)continue;Intrinsic::ID ID = VPI->getIntrinsicID();const auto *VPRedI = dyn_cast<VPReductionIntrinsic>(&I);if (!VPReductionIntrinsic::isVPReduction(ID)) {EXPECT_EQ(VPRedI, nullptr);EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID).has_value(), false);EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID).has_value(), false);continue;}EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID).has_value(), true);EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID).has_value(), true);ASSERT_NE(VPRedI, nullptr);EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID),VPRedI->getStartParamPos());EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID),VPRedI->getVectorParamPos());EXPECT_EQ(VPRedI->getStartParamPos(), 0u);EXPECT_EQ(VPRedI->getVectorParamPos(), 1u);}}
//===- llvm/unittest/IR/UserTest.cpp - User unit tests --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/User.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Function.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(UserTest, ValueOpIteration) {LLVMContext C;const char *ModuleString = "define void @f(i32 %x, i32 %y) {\n""entry:\n"" switch i32 undef, label %s0\n"" [ i32 1, label %s1\n"" i32 2, label %s2\n"" i32 3, label %s3\n"" i32 4, label %s4\n"" i32 5, label %s5\n"" i32 6, label %s6\n"" i32 7, label %s7\n"" i32 8, label %s8\n"" i32 9, label %s9 ]\n""\n""s0:\n"" br label %exit\n""s1:\n"" br label %exit\n""s2:\n"" br label %exit\n""s3:\n"" br label %exit\n""s4:\n"" br label %exit\n""s5:\n"" br label %exit\n""s6:\n"" br label %exit\n""s7:\n"" br label %exit\n""s8:\n"" br label %exit\n""s9:\n"" br label %exit\n""\n""exit:\n"" %phi = phi i32 [ 0, %s0 ], [ 1, %s1 ],\n"" [ 2, %s2 ], [ 3, %s3 ],\n"" [ 4, %s4 ], [ 5, %s5 ],\n"" [ 6, %s6 ], [ 7, %s7 ],\n"" [ 8, %s8 ], [ 9, %s9 ]\n"" ret void\n""}\n";SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");BasicBlock &ExitBB = F->back();PHINode &P = cast<PHINode>(ExitBB.front());EXPECT_TRUE(P.value_op_begin() == P.value_op_begin());EXPECT_FALSE(P.value_op_begin() == P.value_op_end());EXPECT_TRUE(P.value_op_begin() != P.value_op_end());EXPECT_FALSE(P.value_op_end() != P.value_op_end());EXPECT_TRUE(P.value_op_begin() < P.value_op_end());EXPECT_FALSE(P.value_op_begin() < P.value_op_begin());EXPECT_TRUE(P.value_op_end() > P.value_op_begin());EXPECT_FALSE(P.value_op_begin() > P.value_op_begin());EXPECT_TRUE(P.value_op_begin() <= P.value_op_begin());EXPECT_FALSE(P.value_op_end() <= P.value_op_begin());EXPECT_TRUE(P.value_op_begin() >= P.value_op_begin());EXPECT_FALSE(P.value_op_begin() >= P.value_op_end());EXPECT_EQ(10, std::distance(P.value_op_begin(), P.value_op_end()));// const value op iterationconst PHINode *IP = &P;EXPECT_TRUE(IP->value_op_begin() == IP->value_op_begin());EXPECT_FALSE(IP->value_op_begin() == IP->value_op_end());EXPECT_TRUE(IP->value_op_begin() != IP->value_op_end());EXPECT_FALSE(IP->value_op_end() != IP->value_op_end());EXPECT_TRUE(IP->value_op_begin() < IP->value_op_end());EXPECT_FALSE(IP->value_op_begin() < IP->value_op_begin());EXPECT_TRUE(IP->value_op_end() > IP->value_op_begin());EXPECT_FALSE(IP->value_op_begin() > IP->value_op_begin());EXPECT_TRUE(IP->value_op_begin() <= IP->value_op_begin());EXPECT_FALSE(IP->value_op_end() <= IP->value_op_begin());EXPECT_TRUE(IP->value_op_begin() >= IP->value_op_begin());EXPECT_FALSE(IP->value_op_begin() >= IP->value_op_end());EXPECT_EQ(10, std::distance(IP->value_op_begin(), IP->value_op_end()));User::value_op_iterator I = P.value_op_begin();I += 3;EXPECT_EQ(std::next(P.value_op_begin(), 3), I);EXPECT_EQ(P.getOperand(3), *I);I++;EXPECT_EQ(P.getOperand(6), I[2]);EXPECT_EQ(P.value_op_end(), (I - 2) + 8);// const value opUser::const_value_op_iterator CI = IP->value_op_begin();CI += 3;EXPECT_EQ(std::next(IP->value_op_begin(), 3), CI);EXPECT_EQ(IP->getOperand(3), *CI);CI++;EXPECT_EQ(IP->getOperand(6), CI[2]);EXPECT_EQ(IP->value_op_end(), (CI - 2) + 8);}TEST(UserTest, replaceUseOfWith) {LLVMContext C;const char *ModuleString = "define void @f(i32 %x) {\n""entry:\n"" %v0 = add i32 1, 1\n"" %v1 = add i32 %x, 2\n"" ret void\n""}\n";SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");EXPECT_TRUE(F);EXPECT_TRUE(F->arg_begin() != F->arg_end());BasicBlock& entryBB = F->front();Instruction& I0 = *(entryBB.begin());Instruction& I1 = *(++(entryBB.begin()));Argument &X = *F->arg_begin();EXPECT_EQ("x", X.getName());EXPECT_NE(X.user_begin() ,X.user_end());EXPECT_EQ(I0.user_begin() ,I0.user_end());auto XUser = find(X.users(), &(I1));EXPECT_NE(XUser, X.user_end());EXPECT_TRUE(XUser->replaceUsesOfWith(&X, &I0));EXPECT_EQ(X.user_begin(), X.user_end());EXPECT_NE(I0.user_begin(), I0.user_end());// All uses have already been replaced, nothing more to do.EXPECT_FALSE(XUser->replaceUsesOfWith(&X, &I0));}TEST(UserTest, PersonalityUser) {LLVMContext Context;Module M("", Context);FunctionType *RetVoidTy = FunctionType::get(Type::getVoidTy(Context), false);Function *PersonalityF = Function::Create(RetVoidTy, GlobalValue::ExternalLinkage, "PersonalityFn", &M);Function *TestF =Function::Create(RetVoidTy, GlobalValue::ExternalLinkage, "TestFn", &M);// Set up the personality functionTestF->setPersonalityFn(PersonalityF);auto PersonalityUsers = PersonalityF->user_begin();// One user and that user is the Test functionEXPECT_EQ(*PersonalityUsers, TestF);EXPECT_EQ(++PersonalityUsers, PersonalityF->user_end());// Reset the personality functionTestF->setPersonalityFn(nullptr);// No users should remainEXPECT_TRUE(TestF->user_empty());}} // end anonymous namespace
//===- llvm/unittest/IR/UseTest.cpp - Use unit tests ----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Function.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/User.h"#include "llvm/Support/Format.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(UseTest, sort) {LLVMContext C;const char *ModuleString = "define void @f(i32 %x) {\n""entry:\n"" %v0 = add i32 %x, 0\n"" %v2 = add i32 %x, 2\n"" %v5 = add i32 %x, 5\n"" %v1 = add i32 %x, 1\n"" %v3 = add i32 %x, 3\n"" %v7 = add i32 %x, 7\n"" %v6 = add i32 %x, 6\n"" %v4 = add i32 %x, 4\n"" ret void\n""}\n";SMDiagnostic Err;char vnbuf[8];std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");ASSERT_TRUE(F);ASSERT_TRUE(F->arg_begin() != F->arg_end());Argument &X = *F->arg_begin();ASSERT_EQ("x", X.getName());X.sortUseList([](const Use &L, const Use &R) {return L.getUser()->getName() < R.getUser()->getName();});unsigned I = 0;for (User *U : X.users()) {format("v%u", I++).snprint(vnbuf, sizeof(vnbuf));EXPECT_EQ(vnbuf, U->getName());}ASSERT_EQ(8u, I);X.sortUseList([](const Use &L, const Use &R) {return L.getUser()->getName() > R.getUser()->getName();});I = 0;for (User *U : X.users()) {format("v%u", (7 - I++)).snprint(vnbuf, sizeof(vnbuf));EXPECT_EQ(vnbuf, U->getName());}ASSERT_EQ(8u, I);}TEST(UseTest, reverse) {LLVMContext C;const char *ModuleString = "define void @f(i32 %x) {\n""entry:\n"" %v0 = add i32 %x, 0\n"" %v2 = add i32 %x, 2\n"" %v5 = add i32 %x, 5\n"" %v1 = add i32 %x, 1\n"" %v3 = add i32 %x, 3\n"" %v7 = add i32 %x, 7\n"" %v6 = add i32 %x, 6\n"" %v4 = add i32 %x, 4\n"" ret void\n""}\n";SMDiagnostic Err;char vnbuf[8];std::unique_ptr<Module> M = parseAssemblyString(ModuleString, Err, C);Function *F = M->getFunction("f");ASSERT_TRUE(F);ASSERT_TRUE(F->arg_begin() != F->arg_end());Argument &X = *F->arg_begin();ASSERT_EQ("x", X.getName());X.sortUseList([](const Use &L, const Use &R) {return L.getUser()->getName() < R.getUser()->getName();});unsigned I = 0;for (User *U : X.users()) {format("v%u", I++).snprint(vnbuf, sizeof(vnbuf));EXPECT_EQ(vnbuf, U->getName());}ASSERT_EQ(8u, I);X.reverseUseList();I = 0;for (User *U : X.users()) {format("v%u", (7 - I++)).snprint(vnbuf, sizeof(vnbuf));EXPECT_EQ(vnbuf, U->getName());}ASSERT_EQ(8u, I);}} // end anonymous namespace
//===- llvm/unittest/IR/TypesTest.cpp - Type unit tests -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/LLVMContext.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(TypesTest, StructType) {LLVMContext C;// PR13522StructType *Struct = StructType::create(C, "FooBar");EXPECT_EQ("FooBar", Struct->getName());Struct->setName(Struct->getName().substr(0, 3));EXPECT_EQ("Foo", Struct->getName());Struct->setName("");EXPECT_TRUE(Struct->getName().empty());EXPECT_FALSE(Struct->hasName());}TEST(TypesTest, LayoutIdenticalEmptyStructs) {LLVMContext C;StructType *Foo = StructType::create(C, "Foo");StructType *Bar = StructType::create(C, "Bar");EXPECT_TRUE(Foo->isLayoutIdentical(Bar));}TEST(TypesTest, CopyPointerType) {LLVMContext COpaquePointers;COpaquePointers.setOpaquePointers(true);PointerType *P1 = PointerType::get(COpaquePointers, 1);EXPECT_TRUE(P1->isOpaque());PointerType *P1C = PointerType::getWithSamePointeeType(P1, 1);EXPECT_EQ(P1, P1C);EXPECT_TRUE(P1C->isOpaque());PointerType *P1C0 = PointerType::getWithSamePointeeType(P1, 0);EXPECT_NE(P1, P1C0);EXPECT_TRUE(P1C0->isOpaque());LLVMContext CTypedPointers;CTypedPointers.setOpaquePointers(false);Type *Int8 = Type::getInt8Ty(CTypedPointers);PointerType *P2 = PointerType::get(Int8, 1);EXPECT_FALSE(P2->isOpaque());PointerType *P2C = PointerType::getWithSamePointeeType(P2, 1);EXPECT_EQ(P2, P2C);EXPECT_FALSE(P2C->isOpaque());PointerType *P2C0 = PointerType::getWithSamePointeeType(P2, 0);EXPECT_NE(P2, P2C0);EXPECT_FALSE(P2C0->isOpaque());}} // end anonymous namespace
//===- unittests/IR/TimePassesTest.cpp - TimePassesHandler tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/LegacyPassManager.h"#include "llvm/Pass.h"#include "llvm/PassRegistry.h"#include <gtest/gtest.h>#include <llvm/ADT/SmallString.h>#include <llvm/IR/LLVMContext.h>#include <llvm/IR/Module.h>#include <llvm/IR/PassInstrumentation.h>#include <llvm/IR/PassManager.h>#include <llvm/IR/PassTimingInfo.h>#include <llvm/Support/raw_ostream.h>using namespace llvm;//===----------------------------------------------------------------------===//// Define dummy passes for legacy pass manager run.namespace llvm {void initializePass1Pass(PassRegistry &);void initializePass2Pass(PassRegistry &);namespace {struct Pass1 : public ModulePass {static char ID;public:Pass1() : ModulePass(ID) {}bool runOnModule(Module &M) override { return false; }void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();}StringRef getPassName() const override { return "Pass1"; }};char Pass1::ID;struct Pass2 : public ModulePass {static char ID;public:Pass2() : ModulePass(ID) {}bool runOnModule(Module &M) override { return false; }void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();}StringRef getPassName() const override { return "Pass2"; }};char Pass2::ID;} // namespace} // namespace llvmINITIALIZE_PASS(Pass1, "Pass1", "Pass1", false, false)INITIALIZE_PASS(Pass2, "Pass2", "Pass2", false, false)namespace {TEST(TimePassesTest, LegacyCustomOut) {PassInstrumentationCallbacks PIC;PassInstrumentation PI(&PIC);LLVMContext Context;Module M("TestModule", Context);SmallString<0> TimePassesStr;raw_svector_ostream ReportStream(TimePassesStr);// Setup pass managerlegacy::PassManager PM1;PM1.add(new llvm::Pass1());PM1.add(new llvm::Pass2());// Enable time-passes and run passes.TimePassesIsEnabled = true;PM1.run(M);// Generating report.reportAndResetTimings(&ReportStream);// There should be Pass1 and Pass2 in the reportEXPECT_FALSE(TimePassesStr.empty());EXPECT_TRUE(TimePassesStr.str().contains("report"));EXPECT_TRUE(TimePassesStr.str().contains("Pass1"));EXPECT_TRUE(TimePassesStr.str().contains("Pass2"));// Clear and generate report again.TimePassesStr.clear();reportAndResetTimings(&ReportStream);// Since we did not run any passes since last print, report should be empty.EXPECT_TRUE(TimePassesStr.empty());// Now run just a single pass to populate timers again.legacy::PassManager PM2;PM2.add(new llvm::Pass2());PM2.run(M);// Generate report again.reportAndResetTimings(&ReportStream);// There should be Pass2 in this report and no Pass1.EXPECT_FALSE(TimePassesStr.str().empty());EXPECT_TRUE(TimePassesStr.str().contains("report"));EXPECT_FALSE(TimePassesStr.str().contains("Pass1"));EXPECT_TRUE(TimePassesStr.str().contains("Pass2"));}class MyPass1 : public PassInfoMixin<MyPass1> {};class MyPass2 : public PassInfoMixin<MyPass2> {};TEST(TimePassesTest, CustomOut) {PassInstrumentationCallbacks PIC;PassInstrumentation PI(&PIC);LLVMContext Context;Module M("TestModule", Context);MyPass1 Pass1;MyPass2 Pass2;SmallString<0> TimePassesStr;raw_svector_ostream ReportStream(TimePassesStr);// Setup time-passes handler and redirect output to the stream.std::unique_ptr<TimePassesHandler> TimePasses =std::make_unique<TimePassesHandler>(true);TimePasses->setOutStream(ReportStream);TimePasses->registerCallbacks(PIC);// Pretending that passes are running to trigger the timers.PI.runBeforePass(Pass1, M);PI.runBeforePass(Pass2, M);PI.runAfterPass(Pass2, M, PreservedAnalyses::all());PI.runAfterPass(Pass1, M, PreservedAnalyses::all());// Generating report.TimePasses->print();// There should be Pass1 and Pass2 in the reportEXPECT_FALSE(TimePassesStr.empty());EXPECT_TRUE(TimePassesStr.str().contains("report"));EXPECT_TRUE(TimePassesStr.str().contains("Pass1"));EXPECT_TRUE(TimePassesStr.str().contains("Pass2"));// Clear and generate report again.TimePassesStr.clear();TimePasses->print();// Since we did not run any passes since last print, report should be empty.EXPECT_TRUE(TimePassesStr.empty());// Now trigger just a single pass to populate timers again.PI.runBeforePass(Pass2, M);PI.runAfterPass(Pass2, M, PreservedAnalyses::all());// Generate report by deleting the handler.TimePasses.reset();// There should be Pass2 in this report and no Pass1.EXPECT_FALSE(TimePassesStr.str().empty());EXPECT_TRUE(TimePassesStr.str().contains("report"));EXPECT_FALSE(TimePassesStr.str().contains("Pass1"));EXPECT_TRUE(TimePassesStr.str().contains("Pass2"));}} // end anonymous namespace
//===---- llvm/unittest/IR/PatternMatch.cpp - PatternMatch unit tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/PatternMatch.h"#include "llvm/ADT/APSInt.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Analysis/ValueTracking.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Constants.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/MDBuilder.h"#include "llvm/IR/Module.h"#include "llvm/IR/NoFolder.h"#include "llvm/IR/Operator.h"#include "llvm/IR/Type.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::PatternMatch;namespace {struct PatternMatchTest : ::testing::Test {LLVMContext Ctx;std::unique_ptr<Module> M;Function *F;BasicBlock *BB;IRBuilder<NoFolder> IRB;PatternMatchTest(): M(new Module("PatternMatchTestModule", Ctx)),F(Function::Create(FunctionType::get(Type::getVoidTy(Ctx), /* IsVarArg */ false),Function::ExternalLinkage, "f", M.get())),BB(BasicBlock::Create(Ctx, "entry", F)), IRB(BB) {}};TEST_F(PatternMatchTest, OneUse) {// Build up a little tree of values://// One = (1 + 2) + 42// Two = One + 42// Leaf = (Two + 8) + (Two + 13)Value *One = IRB.CreateAdd(IRB.CreateAdd(IRB.getInt32(1), IRB.getInt32(2)),IRB.getInt32(42));Value *Two = IRB.CreateAdd(One, IRB.getInt32(42));Value *Leaf = IRB.CreateAdd(IRB.CreateAdd(Two, IRB.getInt32(8)),IRB.CreateAdd(Two, IRB.getInt32(13)));Value *V;EXPECT_TRUE(m_OneUse(m_Value(V)).match(One));EXPECT_EQ(One, V);EXPECT_FALSE(m_OneUse(m_Value()).match(Two));EXPECT_FALSE(m_OneUse(m_Value()).match(Leaf));}TEST_F(PatternMatchTest, SpecificIntEQ) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 0)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 0)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 0)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 1)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntNE) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 0)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 0)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 0)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 1)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntUGT) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 0)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 0)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 0)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 1)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SignbitZeroChecks) {Type *IntTy = IRB.getInt32Ty();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_TRUE(m_Negative().match(NegOne));EXPECT_FALSE(m_NonNegative().match(NegOne));EXPECT_FALSE(m_StrictlyPositive().match(NegOne));EXPECT_TRUE(m_NonPositive().match(NegOne));EXPECT_FALSE(m_Negative().match(Zero));EXPECT_TRUE(m_NonNegative().match(Zero));EXPECT_FALSE(m_StrictlyPositive().match(Zero));EXPECT_TRUE(m_NonPositive().match(Zero));EXPECT_FALSE(m_Negative().match(One));EXPECT_TRUE(m_NonNegative().match(One));EXPECT_TRUE(m_StrictlyPositive().match(One));EXPECT_FALSE(m_NonPositive().match(One));}TEST_F(PatternMatchTest, SpecificIntUGE) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 0)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 0)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 0)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 1)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntULT) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 0)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 0)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 0)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 1)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntULE) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 0)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 0)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 0)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 1)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntSGT) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 0)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 0)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 0)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 1)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntSGE) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 0)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 0)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 0)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 1)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntSLT) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 0)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 0)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 0)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 1)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)).match(One));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, SpecificIntSLE) {Type *IntTy = IRB.getInt32Ty();unsigned BitWidth = IntTy->getScalarSizeInBits();Value *Zero = ConstantInt::get(IntTy, 0);Value *One = ConstantInt::get(IntTy, 1);Value *NegOne = ConstantInt::get(IntTy, -1);EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 0)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 0)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 0)).match(NegOne));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 1)).match(Zero));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 1)).match(NegOne));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)).match(Zero));EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)).match(One));EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)).match(NegOne));}TEST_F(PatternMatchTest, Unless) {Value *X = IRB.CreateAdd(IRB.getInt32(1), IRB.getInt32(0));EXPECT_TRUE(m_Add(m_One(), m_Zero()).match(X));EXPECT_FALSE(m_Add(m_Zero(), m_One()).match(X));EXPECT_FALSE(m_Unless(m_Add(m_One(), m_Zero())).match(X));EXPECT_TRUE(m_Unless(m_Add(m_Zero(), m_One())).match(X));EXPECT_TRUE(m_c_Add(m_One(), m_Zero()).match(X));EXPECT_TRUE(m_c_Add(m_Zero(), m_One()).match(X));EXPECT_FALSE(m_Unless(m_c_Add(m_One(), m_Zero())).match(X));EXPECT_FALSE(m_Unless(m_c_Add(m_Zero(), m_One())).match(X));}TEST_F(PatternMatchTest, ZExtSExtSelf) {LLVMContext &Ctx = IRB.getContext();Value *One32 = IRB.getInt32(1);Value *One64Z = IRB.CreateZExt(One32, IntegerType::getInt64Ty(Ctx));Value *One64S = IRB.CreateSExt(One32, IntegerType::getInt64Ty(Ctx));EXPECT_TRUE(m_One().match(One32));EXPECT_FALSE(m_One().match(One64Z));EXPECT_FALSE(m_One().match(One64S));EXPECT_FALSE(m_ZExt(m_One()).match(One32));EXPECT_TRUE(m_ZExt(m_One()).match(One64Z));EXPECT_FALSE(m_ZExt(m_One()).match(One64S));EXPECT_FALSE(m_SExt(m_One()).match(One32));EXPECT_FALSE(m_SExt(m_One()).match(One64Z));EXPECT_TRUE(m_SExt(m_One()).match(One64S));EXPECT_TRUE(m_ZExtOrSelf(m_One()).match(One32));EXPECT_TRUE(m_ZExtOrSelf(m_One()).match(One64Z));EXPECT_FALSE(m_ZExtOrSelf(m_One()).match(One64S));EXPECT_TRUE(m_SExtOrSelf(m_One()).match(One32));EXPECT_FALSE(m_SExtOrSelf(m_One()).match(One64Z));EXPECT_TRUE(m_SExtOrSelf(m_One()).match(One64S));EXPECT_FALSE(m_ZExtOrSExt(m_One()).match(One32));EXPECT_TRUE(m_ZExtOrSExt(m_One()).match(One64Z));EXPECT_TRUE(m_ZExtOrSExt(m_One()).match(One64S));EXPECT_TRUE(m_ZExtOrSExtOrSelf(m_One()).match(One32));EXPECT_TRUE(m_ZExtOrSExtOrSelf(m_One()).match(One64Z));EXPECT_TRUE(m_ZExtOrSExtOrSelf(m_One()).match(One64S));}TEST_F(PatternMatchTest, Power2) {Value *C128 = IRB.getInt32(128);Value *CNeg128 = ConstantExpr::getNeg(cast<Constant>(C128));EXPECT_TRUE(m_Power2().match(C128));EXPECT_FALSE(m_Power2().match(CNeg128));EXPECT_FALSE(m_NegatedPower2().match(C128));EXPECT_TRUE(m_NegatedPower2().match(CNeg128));Value *CIntMin = IRB.getInt64(APSInt::getSignedMinValue(64).getSExtValue());Value *CNegIntMin = ConstantExpr::getNeg(cast<Constant>(CIntMin));EXPECT_TRUE(m_Power2().match(CIntMin));EXPECT_TRUE(m_Power2().match(CNegIntMin));EXPECT_TRUE(m_NegatedPower2().match(CIntMin));EXPECT_TRUE(m_NegatedPower2().match(CNegIntMin));}TEST_F(PatternMatchTest, CommutativeDeferredValue) {Value *X = IRB.getInt32(1);Value *Y = IRB.getInt32(2);{Value *tX = X;EXPECT_TRUE(match(X, m_Deferred(tX)));EXPECT_FALSE(match(Y, m_Deferred(tX)));}{const Value *tX = X;EXPECT_TRUE(match(X, m_Deferred(tX)));EXPECT_FALSE(match(Y, m_Deferred(tX)));}{Value *const tX = X;EXPECT_TRUE(match(X, m_Deferred(tX)));EXPECT_FALSE(match(Y, m_Deferred(tX)));}{const Value *const tX = X;EXPECT_TRUE(match(X, m_Deferred(tX)));EXPECT_FALSE(match(Y, m_Deferred(tX)));}{Value *tX = nullptr;EXPECT_TRUE(match(IRB.CreateAnd(X, X), m_And(m_Value(tX), m_Deferred(tX))));EXPECT_EQ(tX, X);}{Value *tX = nullptr;EXPECT_FALSE(match(IRB.CreateAnd(X, Y), m_c_And(m_Value(tX), m_Deferred(tX))));}auto checkMatch = [X, Y](Value *Pattern) {Value *tX = nullptr, *tY = nullptr;EXPECT_TRUE(match(Pattern, m_c_And(m_Value(tX), m_c_And(m_Deferred(tX), m_Value(tY)))));EXPECT_EQ(tX, X);EXPECT_EQ(tY, Y);};checkMatch(IRB.CreateAnd(X, IRB.CreateAnd(X, Y)));checkMatch(IRB.CreateAnd(X, IRB.CreateAnd(Y, X)));checkMatch(IRB.CreateAnd(IRB.CreateAnd(X, Y), X));checkMatch(IRB.CreateAnd(IRB.CreateAnd(Y, X), X));}TEST_F(PatternMatchTest, FloatingPointOrderedMin) {Type *FltTy = IRB.getFloatTy();Value *L = ConstantFP::get(FltTy, 1.0);Value *R = ConstantFP::get(FltTy, 2.0);Value *MatchL, *MatchR;// Test OLT.EXPECT_TRUE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLT(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test OLE.EXPECT_TRUE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLE(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test no match on OGE.EXPECT_FALSE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGE(L, R), L, R)));// Test no match on OGT.EXPECT_FALSE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGT(L, R), L, R)));// Test inverted selects. Note, that this "inverts" the ordering, e.g.:// %cmp = fcmp oge L, R// %min = select %cmp R, L// Given L == NaN// the above is expanded to %cmp == false ==> %min = L// which is true for UnordFMin, not OrdFMin, so test that:// [OU]GE with inverted select.EXPECT_FALSE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGE(L, R), R, L)));EXPECT_TRUE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGE(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// [OU]GT with inverted select.EXPECT_FALSE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGT(L, R), R, L)));EXPECT_TRUE(m_OrdFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGT(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);}TEST_F(PatternMatchTest, FloatingPointOrderedMax) {Type *FltTy = IRB.getFloatTy();Value *L = ConstantFP::get(FltTy, 1.0);Value *R = ConstantFP::get(FltTy, 2.0);Value *MatchL, *MatchR;// Test OGT.EXPECT_TRUE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGT(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test OGE.EXPECT_TRUE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGE(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test no match on OLE.EXPECT_FALSE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLE(L, R), L, R)));// Test no match on OLT.EXPECT_FALSE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLT(L, R), L, R)));// Test inverted selects. Note, that this "inverts" the ordering, e.g.:// %cmp = fcmp ole L, R// %max = select %cmp, R, L// Given L == NaN,// the above is expanded to %cmp == false ==> %max == L// which is true for UnordFMax, not OrdFMax, so test that:// [OU]LE with inverted select.EXPECT_FALSE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLE(L, R), R, L)));EXPECT_TRUE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULE(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// [OUT]LT with inverted select.EXPECT_FALSE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLT(L, R), R, L)));EXPECT_TRUE(m_OrdFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULT(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);}TEST_F(PatternMatchTest, FloatingPointUnorderedMin) {Type *FltTy = IRB.getFloatTy();Value *L = ConstantFP::get(FltTy, 1.0);Value *R = ConstantFP::get(FltTy, 2.0);Value *MatchL, *MatchR;// Test ULT.EXPECT_TRUE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULT(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test ULE.EXPECT_TRUE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULE(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test no match on UGE.EXPECT_FALSE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGE(L, R), L, R)));// Test no match on UGT.EXPECT_FALSE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGT(L, R), L, R)));// Test inverted selects. Note, that this "inverts" the ordering, e.g.:// %cmp = fcmp uge L, R// %min = select %cmp R, L// Given L == NaN// the above is expanded to %cmp == true ==> %min = R// which is true for OrdFMin, not UnordFMin, so test that:// [UO]GE with inverted select.EXPECT_FALSE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGE(L, R), R, L)));EXPECT_TRUE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGE(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// [UO]GT with inverted select.EXPECT_FALSE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGT(L, R), R, L)));EXPECT_TRUE(m_UnordFMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOGT(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);}TEST_F(PatternMatchTest, FloatingPointUnorderedMax) {Type *FltTy = IRB.getFloatTy();Value *L = ConstantFP::get(FltTy, 1.0);Value *R = ConstantFP::get(FltTy, 2.0);Value *MatchL, *MatchR;// Test UGT.EXPECT_TRUE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGT(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test UGE.EXPECT_TRUE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpUGE(L, R), L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Test no match on ULE.EXPECT_FALSE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULE(L, R), L, R)));// Test no match on ULT.EXPECT_FALSE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULT(L, R), L, R)));// Test inverted selects. Note, that this "inverts" the ordering, e.g.:// %cmp = fcmp ule L, R// %max = select %cmp R, L// Given L == NaN// the above is expanded to %cmp == true ==> %max = R// which is true for OrdFMax, not UnordFMax, so test that:// [UO]LE with inverted select.EXPECT_FALSE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULE(L, R), R, L)));EXPECT_TRUE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLE(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// [UO]LT with inverted select.EXPECT_FALSE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpULT(L, R), R, L)));EXPECT_TRUE(m_UnordFMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateSelect(IRB.CreateFCmpOLT(L, R), R, L)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);}TEST_F(PatternMatchTest, OverflowingBinOps) {Value *L = IRB.getInt32(1);Value *R = IRB.getInt32(2);Value *MatchL, *MatchR;EXPECT_TRUE(m_NSWAdd(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateNSWAdd(L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);MatchL = MatchR = nullptr;EXPECT_TRUE(m_NSWSub(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateNSWSub(L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);MatchL = MatchR = nullptr;EXPECT_TRUE(m_NSWMul(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateNSWMul(L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);MatchL = MatchR = nullptr;EXPECT_TRUE(m_NSWShl(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateShl(L, R, "", /* NUW */ false, /* NSW */ true)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);EXPECT_TRUE(m_NUWAdd(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateNUWAdd(L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);MatchL = MatchR = nullptr;EXPECT_TRUE(m_NUWSub(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateNUWSub(L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);MatchL = MatchR = nullptr;EXPECT_TRUE(m_NUWMul(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateNUWMul(L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);MatchL = MatchR = nullptr;EXPECT_TRUE(m_NUWShl(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateShl(L, R, "", /* NUW */ true, /* NSW */ false)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);EXPECT_FALSE(m_NSWAdd(m_Value(), m_Value()).match(IRB.CreateAdd(L, R)));EXPECT_FALSE(m_NSWAdd(m_Value(), m_Value()).match(IRB.CreateNUWAdd(L, R)));EXPECT_FALSE(m_NSWAdd(m_Value(), m_Value()).match(IRB.CreateNSWSub(L, R)));EXPECT_FALSE(m_NSWSub(m_Value(), m_Value()).match(IRB.CreateSub(L, R)));EXPECT_FALSE(m_NSWSub(m_Value(), m_Value()).match(IRB.CreateNUWSub(L, R)));EXPECT_FALSE(m_NSWSub(m_Value(), m_Value()).match(IRB.CreateNSWAdd(L, R)));EXPECT_FALSE(m_NSWMul(m_Value(), m_Value()).match(IRB.CreateMul(L, R)));EXPECT_FALSE(m_NSWMul(m_Value(), m_Value()).match(IRB.CreateNUWMul(L, R)));EXPECT_FALSE(m_NSWMul(m_Value(), m_Value()).match(IRB.CreateNSWAdd(L, R)));EXPECT_FALSE(m_NSWShl(m_Value(), m_Value()).match(IRB.CreateShl(L, R)));EXPECT_FALSE(m_NSWShl(m_Value(), m_Value()).match(IRB.CreateShl(L, R, "", /* NUW */ true, /* NSW */ false)));EXPECT_FALSE(m_NSWShl(m_Value(), m_Value()).match(IRB.CreateNSWAdd(L, R)));EXPECT_FALSE(m_NUWAdd(m_Value(), m_Value()).match(IRB.CreateAdd(L, R)));EXPECT_FALSE(m_NUWAdd(m_Value(), m_Value()).match(IRB.CreateNSWAdd(L, R)));EXPECT_FALSE(m_NUWAdd(m_Value(), m_Value()).match(IRB.CreateNUWSub(L, R)));EXPECT_FALSE(m_NUWSub(m_Value(), m_Value()).match(IRB.CreateSub(L, R)));EXPECT_FALSE(m_NUWSub(m_Value(), m_Value()).match(IRB.CreateNSWSub(L, R)));EXPECT_FALSE(m_NUWSub(m_Value(), m_Value()).match(IRB.CreateNUWAdd(L, R)));EXPECT_FALSE(m_NUWMul(m_Value(), m_Value()).match(IRB.CreateMul(L, R)));EXPECT_FALSE(m_NUWMul(m_Value(), m_Value()).match(IRB.CreateNSWMul(L, R)));EXPECT_FALSE(m_NUWMul(m_Value(), m_Value()).match(IRB.CreateNUWAdd(L, R)));EXPECT_FALSE(m_NUWShl(m_Value(), m_Value()).match(IRB.CreateShl(L, R)));EXPECT_FALSE(m_NUWShl(m_Value(), m_Value()).match(IRB.CreateShl(L, R, "", /* NUW */ false, /* NSW */ true)));EXPECT_FALSE(m_NUWShl(m_Value(), m_Value()).match(IRB.CreateNUWAdd(L, R)));}TEST_F(PatternMatchTest, LoadStoreOps) {// Create this load/store sequence://// %p = alloca i32*// %0 = load i32*, i32** %p// store i32 42, i32* %0Value *Alloca = IRB.CreateAlloca(IRB.getInt32Ty());Value *LoadInst = IRB.CreateLoad(IRB.getInt32Ty(), Alloca);Value *FourtyTwo = IRB.getInt32(42);Value *StoreInst = IRB.CreateStore(FourtyTwo, Alloca);Value *MatchLoad, *MatchStoreVal, *MatchStorePointer;EXPECT_TRUE(m_Load(m_Value(MatchLoad)).match(LoadInst));EXPECT_EQ(Alloca, MatchLoad);EXPECT_TRUE(m_Load(m_Specific(Alloca)).match(LoadInst));EXPECT_FALSE(m_Load(m_Value(MatchLoad)).match(Alloca));EXPECT_TRUE(m_Store(m_Value(MatchStoreVal), m_Value(MatchStorePointer)).match(StoreInst));EXPECT_EQ(FourtyTwo, MatchStoreVal);EXPECT_EQ(Alloca, MatchStorePointer);EXPECT_FALSE(m_Store(m_Value(MatchStoreVal), m_Value(MatchStorePointer)).match(Alloca));EXPECT_TRUE(m_Store(m_SpecificInt(42), m_Specific(Alloca)).match(StoreInst));EXPECT_FALSE(m_Store(m_SpecificInt(42), m_Specific(FourtyTwo)).match(StoreInst));EXPECT_FALSE(m_Store(m_SpecificInt(43), m_Specific(Alloca)).match(StoreInst));}TEST_F(PatternMatchTest, VectorOps) {// Build up small tree of vector operations//// Val = 0 + 1// Val2 = Val + 3// VI1 = insertelement <2 x i8> undef, i8 1, i32 0 = <1, undef>// VI2 = insertelement <2 x i8> %VI1, i8 %Val2, i8 %Val = <1, 4>// VI3 = insertelement <2 x i8> %VI1, i8 %Val2, i32 1 = <1, 4>// VI4 = insertelement <2 x i8> %VI1, i8 2, i8 %Val = <1, 2>//// SI1 = shufflevector <2 x i8> %VI1, <2 x i8> undef, zeroinitializer// SI2 = shufflevector <2 x i8> %VI3, <2 x i8> %VI4, <2 x i8> <i8 0, i8 2>// SI3 = shufflevector <2 x i8> %VI3, <2 x i8> undef, zeroinitializer// SI4 = shufflevector <2 x i8> %VI4, <2 x i8> undef, zeroinitializer//// SP1 = VectorSplat(2, i8 2)// SP2 = VectorSplat(2, i8 %Val)Type *VecTy = FixedVectorType::get(IRB.getInt8Ty(), 2);Type *i32 = IRB.getInt32Ty();Type *i32VecTy = FixedVectorType::get(i32, 2);Value *Val = IRB.CreateAdd(IRB.getInt8(0), IRB.getInt8(1));Value *Val2 = IRB.CreateAdd(Val, IRB.getInt8(3));SmallVector<Constant *, 2> VecElemIdxs;VecElemIdxs.push_back(ConstantInt::get(i32, 0));VecElemIdxs.push_back(ConstantInt::get(i32, 2));auto *IdxVec = ConstantVector::get(VecElemIdxs);Value *VI1 = IRB.CreateInsertElement(VecTy, IRB.getInt8(1), (uint64_t)0);Value *VI2 = IRB.CreateInsertElement(VI1, Val2, Val);Value *VI3 = IRB.CreateInsertElement(VI1, Val2, (uint64_t)1);Value *VI4 = IRB.CreateInsertElement(VI1, IRB.getInt8(2), Val);Value *EX1 = IRB.CreateExtractElement(VI4, Val);Value *EX2 = IRB.CreateExtractElement(VI4, (uint64_t)0);Value *EX3 = IRB.CreateExtractElement(IdxVec, (uint64_t)1);Constant *Zero = ConstantAggregateZero::get(i32VecTy);SmallVector<int, 16> ZeroMask;ShuffleVectorInst::getShuffleMask(Zero, ZeroMask);Value *SI1 = IRB.CreateShuffleVector(VI1, ZeroMask);Value *SI2 = IRB.CreateShuffleVector(VI3, VI4, IdxVec);Value *SI3 = IRB.CreateShuffleVector(VI3, ZeroMask);Value *SI4 = IRB.CreateShuffleVector(VI4, ZeroMask);Value *SP1 = IRB.CreateVectorSplat(2, IRB.getInt8(2));Value *SP2 = IRB.CreateVectorSplat(2, Val);Value *A = nullptr, *B = nullptr, *C = nullptr;// Test matching insertelementEXPECT_TRUE(match(VI1, m_InsertElt(m_Value(), m_Value(), m_Value())));EXPECT_TRUE(match(VI1, m_InsertElt(m_Undef(), m_ConstantInt(), m_ConstantInt())));EXPECT_TRUE(match(VI1, m_InsertElt(m_Undef(), m_ConstantInt(), m_Zero())));EXPECT_TRUE(match(VI1, m_InsertElt(m_Undef(), m_SpecificInt(1), m_Zero())));EXPECT_TRUE(match(VI2, m_InsertElt(m_Value(), m_Value(), m_Value())));EXPECT_FALSE(match(VI2, m_InsertElt(m_Value(), m_Value(), m_ConstantInt())));EXPECT_FALSE(match(VI2, m_InsertElt(m_Value(), m_ConstantInt(), m_Value())));EXPECT_FALSE(match(VI2, m_InsertElt(m_Constant(), m_Value(), m_Value())));EXPECT_TRUE(match(VI3, m_InsertElt(m_Value(A), m_Value(B), m_Value(C))));EXPECT_TRUE(A == VI1);EXPECT_TRUE(B == Val2);EXPECT_TRUE(isa<ConstantInt>(C));A = B = C = nullptr; // reset// Test matching extractelementEXPECT_TRUE(match(EX1, m_ExtractElt(m_Value(A), m_Value(B))));EXPECT_TRUE(A == VI4);EXPECT_TRUE(B == Val);A = B = C = nullptr; // resetEXPECT_FALSE(match(EX1, m_ExtractElt(m_Value(), m_ConstantInt())));EXPECT_TRUE(match(EX2, m_ExtractElt(m_Value(), m_ConstantInt())));EXPECT_TRUE(match(EX3, m_ExtractElt(m_Constant(), m_ConstantInt())));// Test matching shufflevectorArrayRef<int> Mask;EXPECT_TRUE(match(SI1, m_Shuffle(m_Value(), m_Undef(), m_ZeroMask())));EXPECT_TRUE(match(SI2, m_Shuffle(m_Value(A), m_Value(B), m_Mask(Mask))));EXPECT_TRUE(A == VI3);EXPECT_TRUE(B == VI4);A = B = C = nullptr; // reset// Test matching the vector splat patternEXPECT_TRUE(match(SI1,m_Shuffle(m_InsertElt(m_Undef(), m_SpecificInt(1), m_Zero()),m_Undef(), m_ZeroMask())));EXPECT_FALSE(match(SI3, m_Shuffle(m_InsertElt(m_Undef(), m_Value(), m_Zero()),m_Undef(), m_ZeroMask())));EXPECT_FALSE(match(SI4, m_Shuffle(m_InsertElt(m_Undef(), m_Value(), m_Zero()),m_Undef(), m_ZeroMask())));EXPECT_TRUE(match(SP1,m_Shuffle(m_InsertElt(m_Undef(), m_SpecificInt(2), m_Zero()),m_Undef(), m_ZeroMask())));EXPECT_TRUE(match(SP2, m_Shuffle(m_InsertElt(m_Undef(), m_Value(A), m_Zero()),m_Undef(), m_ZeroMask())));EXPECT_TRUE(A == Val);}TEST_F(PatternMatchTest, UndefPoisonMix) {Type *ScalarTy = IRB.getInt8Ty();ArrayType *ArrTy = ArrayType::get(ScalarTy, 2);StructType *StTy = StructType::get(ScalarTy, ScalarTy);StructType *StTy2 = StructType::get(ScalarTy, StTy);StructType *StTy3 = StructType::get(StTy, ScalarTy);Constant *Zero = ConstantInt::getNullValue(ScalarTy);UndefValue *U = UndefValue::get(ScalarTy);UndefValue *P = PoisonValue::get(ScalarTy);EXPECT_TRUE(match(ConstantVector::get({U, P}), m_Undef()));EXPECT_TRUE(match(ConstantVector::get({P, U}), m_Undef()));EXPECT_TRUE(match(ConstantArray::get(ArrTy, {U, P}), m_Undef()));EXPECT_TRUE(match(ConstantArray::get(ArrTy, {P, U}), m_Undef()));auto *UP = ConstantStruct::get(StTy, {U, P});EXPECT_TRUE(match(ConstantStruct::get(StTy2, {U, UP}), m_Undef()));EXPECT_TRUE(match(ConstantStruct::get(StTy2, {P, UP}), m_Undef()));EXPECT_TRUE(match(ConstantStruct::get(StTy3, {UP, U}), m_Undef()));EXPECT_TRUE(match(ConstantStruct::get(StTy3, {UP, P}), m_Undef()));EXPECT_FALSE(match(ConstantStruct::get(StTy, {U, Zero}), m_Undef()));EXPECT_FALSE(match(ConstantStruct::get(StTy, {Zero, U}), m_Undef()));EXPECT_FALSE(match(ConstantStruct::get(StTy, {P, Zero}), m_Undef()));EXPECT_FALSE(match(ConstantStruct::get(StTy, {Zero, P}), m_Undef()));EXPECT_FALSE(match(ConstantStruct::get(StTy2, {Zero, UP}), m_Undef()));EXPECT_FALSE(match(ConstantStruct::get(StTy3, {UP, Zero}), m_Undef()));}TEST_F(PatternMatchTest, VectorUndefInt) {Type *ScalarTy = IRB.getInt8Ty();Type *VectorTy = FixedVectorType::get(ScalarTy, 4);Constant *ScalarUndef = UndefValue::get(ScalarTy);Constant *VectorUndef = UndefValue::get(VectorTy);Constant *ScalarZero = Constant::getNullValue(ScalarTy);Constant *VectorZero = Constant::getNullValue(VectorTy);SmallVector<Constant *, 4> Elems;Elems.push_back(ScalarUndef);Elems.push_back(ScalarZero);Elems.push_back(ScalarUndef);Elems.push_back(ScalarZero);Constant *VectorZeroUndef = ConstantVector::get(Elems);EXPECT_TRUE(match(ScalarUndef, m_Undef()));EXPECT_TRUE(match(VectorUndef, m_Undef()));EXPECT_FALSE(match(ScalarZero, m_Undef()));EXPECT_FALSE(match(VectorZero, m_Undef()));EXPECT_FALSE(match(VectorZeroUndef, m_Undef()));EXPECT_FALSE(match(ScalarUndef, m_Zero()));EXPECT_FALSE(match(VectorUndef, m_Zero()));EXPECT_TRUE(match(ScalarZero, m_Zero()));EXPECT_TRUE(match(VectorZero, m_Zero()));EXPECT_TRUE(match(VectorZeroUndef, m_Zero()));const APInt *C;// Regardless of whether undefs are allowed,// a fully undef constant does not match.EXPECT_FALSE(match(ScalarUndef, m_APInt(C)));EXPECT_FALSE(match(ScalarUndef, m_APIntForbidUndef(C)));EXPECT_FALSE(match(ScalarUndef, m_APIntAllowUndef(C)));EXPECT_FALSE(match(VectorUndef, m_APInt(C)));EXPECT_FALSE(match(VectorUndef, m_APIntForbidUndef(C)));EXPECT_FALSE(match(VectorUndef, m_APIntAllowUndef(C)));// We can always match simple constants and simple splats.C = nullptr;EXPECT_TRUE(match(ScalarZero, m_APInt(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(ScalarZero, m_APIntForbidUndef(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(ScalarZero, m_APIntAllowUndef(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(VectorZero, m_APInt(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(VectorZero, m_APIntForbidUndef(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(VectorZero, m_APIntAllowUndef(C)));EXPECT_TRUE(C->isZero());// Whether splats with undef can be matched depends on the matcher.EXPECT_FALSE(match(VectorZeroUndef, m_APInt(C)));EXPECT_FALSE(match(VectorZeroUndef, m_APIntForbidUndef(C)));C = nullptr;EXPECT_TRUE(match(VectorZeroUndef, m_APIntAllowUndef(C)));EXPECT_TRUE(C->isZero());}TEST_F(PatternMatchTest, VectorUndefFloat) {Type *ScalarTy = IRB.getFloatTy();Type *VectorTy = FixedVectorType::get(ScalarTy, 4);Constant *ScalarUndef = UndefValue::get(ScalarTy);Constant *VectorUndef = UndefValue::get(VectorTy);Constant *ScalarZero = Constant::getNullValue(ScalarTy);Constant *VectorZero = Constant::getNullValue(VectorTy);Constant *ScalarPosInf = ConstantFP::getInfinity(ScalarTy, false);Constant *ScalarNegInf = ConstantFP::getInfinity(ScalarTy, true);Constant *ScalarNaN = ConstantFP::getNaN(ScalarTy, true);Constant *VectorZeroUndef =ConstantVector::get({ScalarUndef, ScalarZero, ScalarUndef, ScalarZero});Constant *VectorInfUndef = ConstantVector::get({ScalarPosInf, ScalarNegInf, ScalarUndef, ScalarPosInf});Constant *VectorNaNUndef =ConstantVector::get({ScalarUndef, ScalarNaN, ScalarNaN, ScalarNaN});EXPECT_TRUE(match(ScalarUndef, m_Undef()));EXPECT_TRUE(match(VectorUndef, m_Undef()));EXPECT_FALSE(match(ScalarZero, m_Undef()));EXPECT_FALSE(match(VectorZero, m_Undef()));EXPECT_FALSE(match(VectorZeroUndef, m_Undef()));EXPECT_FALSE(match(VectorInfUndef, m_Undef()));EXPECT_FALSE(match(VectorNaNUndef, m_Undef()));EXPECT_FALSE(match(ScalarUndef, m_AnyZeroFP()));EXPECT_FALSE(match(VectorUndef, m_AnyZeroFP()));EXPECT_TRUE(match(ScalarZero, m_AnyZeroFP()));EXPECT_TRUE(match(VectorZero, m_AnyZeroFP()));EXPECT_TRUE(match(VectorZeroUndef, m_AnyZeroFP()));EXPECT_FALSE(match(VectorInfUndef, m_AnyZeroFP()));EXPECT_FALSE(match(VectorNaNUndef, m_AnyZeroFP()));EXPECT_FALSE(match(ScalarUndef, m_NaN()));EXPECT_FALSE(match(VectorUndef, m_NaN()));EXPECT_FALSE(match(VectorZeroUndef, m_NaN()));EXPECT_FALSE(match(ScalarPosInf, m_NaN()));EXPECT_FALSE(match(ScalarNegInf, m_NaN()));EXPECT_TRUE(match(ScalarNaN, m_NaN()));EXPECT_FALSE(match(VectorInfUndef, m_NaN()));EXPECT_TRUE(match(VectorNaNUndef, m_NaN()));EXPECT_FALSE(match(ScalarUndef, m_NonNaN()));EXPECT_FALSE(match(VectorUndef, m_NonNaN()));EXPECT_TRUE(match(VectorZeroUndef, m_NonNaN()));EXPECT_TRUE(match(ScalarPosInf, m_NonNaN()));EXPECT_TRUE(match(ScalarNegInf, m_NonNaN()));EXPECT_FALSE(match(ScalarNaN, m_NonNaN()));EXPECT_TRUE(match(VectorInfUndef, m_NonNaN()));EXPECT_FALSE(match(VectorNaNUndef, m_NonNaN()));EXPECT_FALSE(match(ScalarUndef, m_Inf()));EXPECT_FALSE(match(VectorUndef, m_Inf()));EXPECT_FALSE(match(VectorZeroUndef, m_Inf()));EXPECT_TRUE(match(ScalarPosInf, m_Inf()));EXPECT_TRUE(match(ScalarNegInf, m_Inf()));EXPECT_FALSE(match(ScalarNaN, m_Inf()));EXPECT_TRUE(match(VectorInfUndef, m_Inf()));EXPECT_FALSE(match(VectorNaNUndef, m_Inf()));EXPECT_FALSE(match(ScalarUndef, m_NonInf()));EXPECT_FALSE(match(VectorUndef, m_NonInf()));EXPECT_TRUE(match(VectorZeroUndef, m_NonInf()));EXPECT_FALSE(match(ScalarPosInf, m_NonInf()));EXPECT_FALSE(match(ScalarNegInf, m_NonInf()));EXPECT_TRUE(match(ScalarNaN, m_NonInf()));EXPECT_FALSE(match(VectorInfUndef, m_NonInf()));EXPECT_TRUE(match(VectorNaNUndef, m_NonInf()));EXPECT_FALSE(match(ScalarUndef, m_Finite()));EXPECT_FALSE(match(VectorUndef, m_Finite()));EXPECT_TRUE(match(VectorZeroUndef, m_Finite()));EXPECT_FALSE(match(ScalarPosInf, m_Finite()));EXPECT_FALSE(match(ScalarNegInf, m_Finite()));EXPECT_FALSE(match(ScalarNaN, m_Finite()));EXPECT_FALSE(match(VectorInfUndef, m_Finite()));EXPECT_FALSE(match(VectorNaNUndef, m_Finite()));const APFloat *C;// Regardless of whether undefs are allowed,// a fully undef constant does not match.EXPECT_FALSE(match(ScalarUndef, m_APFloat(C)));EXPECT_FALSE(match(ScalarUndef, m_APFloatForbidUndef(C)));EXPECT_FALSE(match(ScalarUndef, m_APFloatAllowUndef(C)));EXPECT_FALSE(match(VectorUndef, m_APFloat(C)));EXPECT_FALSE(match(VectorUndef, m_APFloatForbidUndef(C)));EXPECT_FALSE(match(VectorUndef, m_APFloatAllowUndef(C)));// We can always match simple constants and simple splats.C = nullptr;EXPECT_TRUE(match(ScalarZero, m_APFloat(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(ScalarZero, m_APFloatForbidUndef(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(ScalarZero, m_APFloatAllowUndef(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(VectorZero, m_APFloat(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(VectorZero, m_APFloatForbidUndef(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(VectorZero, m_APFloatAllowUndef(C)));EXPECT_TRUE(C->isZero());// Whether splats with undef can be matched depends on the matcher.EXPECT_FALSE(match(VectorZeroUndef, m_APFloat(C)));EXPECT_FALSE(match(VectorZeroUndef, m_APFloatForbidUndef(C)));C = nullptr;EXPECT_TRUE(match(VectorZeroUndef, m_APFloatAllowUndef(C)));EXPECT_TRUE(C->isZero());C = nullptr;EXPECT_TRUE(match(VectorZeroUndef, m_Finite(C)));EXPECT_TRUE(C->isZero());}TEST_F(PatternMatchTest, FloatingPointFNeg) {Type *FltTy = IRB.getFloatTy();Value *One = ConstantFP::get(FltTy, 1.0);Value *Z = ConstantFP::get(FltTy, 0.0);Value *NZ = ConstantFP::get(FltTy, -0.0);Value *V = IRB.CreateFNeg(One);Value *V1 = IRB.CreateFSub(NZ, One);Value *V2 = IRB.CreateFSub(Z, One);Value *V3 = IRB.CreateFAdd(NZ, One);Value *Match;// Test FNeg(1.0)EXPECT_TRUE(match(V, m_FNeg(m_Value(Match))));EXPECT_EQ(One, Match);// Test FSub(-0.0, 1.0)EXPECT_TRUE(match(V1, m_FNeg(m_Value(Match))));EXPECT_EQ(One, Match);// Test FSub(0.0, 1.0)EXPECT_FALSE(match(V2, m_FNeg(m_Value(Match))));cast<Instruction>(V2)->setHasNoSignedZeros(true);EXPECT_TRUE(match(V2, m_FNeg(m_Value(Match))));EXPECT_EQ(One, Match);// Test FAdd(-0.0, 1.0)EXPECT_FALSE(match(V3, m_FNeg(m_Value(Match))));}TEST_F(PatternMatchTest, CondBranchTest) {BasicBlock *TrueBB = BasicBlock::Create(Ctx, "TrueBB", F);BasicBlock *FalseBB = BasicBlock::Create(Ctx, "FalseBB", F);Value *Br1 = IRB.CreateCondBr(IRB.getTrue(), TrueBB, FalseBB);EXPECT_TRUE(match(Br1, m_Br(m_Value(), m_BasicBlock(), m_BasicBlock())));BasicBlock *A, *B;EXPECT_TRUE(match(Br1, m_Br(m_Value(), m_BasicBlock(A), m_BasicBlock(B))));EXPECT_EQ(TrueBB, A);EXPECT_EQ(FalseBB, B);EXPECT_FALSE(match(Br1, m_Br(m_Value(), m_SpecificBB(FalseBB), m_BasicBlock())));EXPECT_FALSE(match(Br1, m_Br(m_Value(), m_BasicBlock(), m_SpecificBB(TrueBB))));EXPECT_FALSE(match(Br1, m_Br(m_Value(), m_SpecificBB(FalseBB), m_BasicBlock(TrueBB))));EXPECT_TRUE(match(Br1, m_Br(m_Value(), m_SpecificBB(TrueBB), m_BasicBlock(FalseBB))));// Check we can use m_Deferred with branches.EXPECT_FALSE(match(Br1, m_Br(m_Value(), m_BasicBlock(A), m_Deferred(A))));Value *Br2 = IRB.CreateCondBr(IRB.getTrue(), TrueBB, TrueBB);A = nullptr;EXPECT_TRUE(match(Br2, m_Br(m_Value(), m_BasicBlock(A), m_Deferred(A))));}TEST_F(PatternMatchTest, WithOverflowInst) {Value *Add = IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow,IRB.getInt32(0), IRB.getInt32(0));Value *Add0 = IRB.CreateExtractValue(Add, 0);Value *Add1 = IRB.CreateExtractValue(Add, 1);EXPECT_TRUE(match(Add0, m_ExtractValue<0>(m_Value())));EXPECT_FALSE(match(Add0, m_ExtractValue<1>(m_Value())));EXPECT_FALSE(match(Add1, m_ExtractValue<0>(m_Value())));EXPECT_TRUE(match(Add1, m_ExtractValue<1>(m_Value())));EXPECT_FALSE(match(Add, m_ExtractValue<1>(m_Value())));EXPECT_FALSE(match(Add, m_ExtractValue<1>(m_Value())));WithOverflowInst *WOI;EXPECT_FALSE(match(Add0, m_WithOverflowInst(WOI)));EXPECT_FALSE(match(Add1, m_WithOverflowInst(WOI)));EXPECT_TRUE(match(Add, m_WithOverflowInst(WOI)));EXPECT_TRUE(match(Add0, m_ExtractValue<0>(m_WithOverflowInst(WOI))));EXPECT_EQ(Add, WOI);EXPECT_TRUE(match(Add1, m_ExtractValue<1>(m_WithOverflowInst(WOI))));EXPECT_EQ(Add, WOI);}TEST_F(PatternMatchTest, MinMaxIntrinsics) {Type *Ty = IRB.getInt32Ty();Value *L = ConstantInt::get(Ty, 1);Value *R = ConstantInt::get(Ty, 2);Value *MatchL, *MatchR;// Check for intrinsic ID match and capture of operands.EXPECT_TRUE(m_SMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::smax, L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);EXPECT_TRUE(m_SMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::smin, L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);EXPECT_TRUE(m_UMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::umax, L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);EXPECT_TRUE(m_UMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::umin, L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);// Check for intrinsic ID mismatch.EXPECT_FALSE(m_SMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::smin, L, R)));EXPECT_FALSE(m_SMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::umax, L, R)));EXPECT_FALSE(m_UMax(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::umin, L, R)));EXPECT_FALSE(m_UMin(m_Value(MatchL), m_Value(MatchR)).match(IRB.CreateBinaryIntrinsic(Intrinsic::smax, L, R)));}TEST_F(PatternMatchTest, IntrinsicMatcher) {Value *Name = IRB.CreateAlloca(IRB.getInt8Ty());Value *Hash = IRB.getInt64(0);Value *Num = IRB.getInt32(1);Value *Index = IRB.getInt32(2);Value *Step = IRB.getInt64(3);Value *Ops[] = {Name, Hash, Num, Index, Step};Module *M = BB->getParent()->getParent();Function *TheFn =Intrinsic::getDeclaration(M, Intrinsic::instrprof_increment_step);Value *Intrinsic5 = CallInst::Create(TheFn, Ops, "", BB);// Match without capturing.EXPECT_TRUE(match(Intrinsic5, m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_Value(), m_Value(), m_Value(), m_Value())));EXPECT_FALSE(match(Intrinsic5, m_Intrinsic<Intrinsic::memmove>(m_Value(), m_Value(), m_Value(), m_Value(), m_Value())));// Match with capturing.Value *Arg1 = nullptr;Value *Arg2 = nullptr;Value *Arg3 = nullptr;Value *Arg4 = nullptr;Value *Arg5 = nullptr;EXPECT_TRUE(match(Intrinsic5, m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(Arg1), m_Value(Arg2), m_Value(Arg3),m_Value(Arg4), m_Value(Arg5))));EXPECT_EQ(Arg1, Name);EXPECT_EQ(Arg2, Hash);EXPECT_EQ(Arg3, Num);EXPECT_EQ(Arg4, Index);EXPECT_EQ(Arg5, Step);// Match specific second argument.EXPECT_TRUE(match(Intrinsic5,m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_SpecificInt(0), m_Value(), m_Value(), m_Value())));EXPECT_FALSE(match(Intrinsic5, m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_SpecificInt(10), m_Value(), m_Value(),m_Value())));// Match specific third argument.EXPECT_TRUE(match(Intrinsic5,m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_Value(), m_SpecificInt(1), m_Value(), m_Value())));EXPECT_FALSE(match(Intrinsic5, m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_Value(), m_SpecificInt(10), m_Value(),m_Value())));// Match specific fourth argument.EXPECT_TRUE(match(Intrinsic5,m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_Value(), m_Value(), m_SpecificInt(2), m_Value())));EXPECT_FALSE(match(Intrinsic5, m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_Value(), m_Value(), m_SpecificInt(10),m_Value())));// Match specific fifth argument.EXPECT_TRUE(match(Intrinsic5,m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_Value(), m_Value(), m_Value(), m_SpecificInt(3))));EXPECT_FALSE(match(Intrinsic5, m_Intrinsic<Intrinsic::instrprof_increment_step>(m_Value(), m_Value(), m_Value(), m_Value(),m_SpecificInt(10))));}namespace {struct is_unsigned_zero_pred {bool isValue(const APInt &C) { return C.isZero(); }};struct is_float_zero_pred {bool isValue(const APFloat &C) { return C.isZero(); }};template <typename T> struct always_true_pred {bool isValue(const T &) { return true; }};template <typename T> struct always_false_pred {bool isValue(const T &) { return false; }};struct is_unsigned_max_pred {bool isValue(const APInt &C) { return C.isMaxValue(); }};struct is_float_nan_pred {bool isValue(const APFloat &C) { return C.isNaN(); }};} // namespaceTEST_F(PatternMatchTest, ConstantPredicateType) {// Scalar integerAPInt U32Max = APInt::getAllOnes(32);APInt U32Zero = APInt::getZero(32);APInt U32DeadBeef(32, 0xDEADBEEF);Type *U32Ty = Type::getInt32Ty(Ctx);Constant *CU32Max = Constant::getIntegerValue(U32Ty, U32Max);Constant *CU32Zero = Constant::getIntegerValue(U32Ty, U32Zero);Constant *CU32DeadBeef = Constant::getIntegerValue(U32Ty, U32DeadBeef);EXPECT_TRUE(match(CU32Max, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_FALSE(match(CU32Max, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CU32Max, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CU32Max, cst_pred_ty<always_false_pred<APInt>>()));EXPECT_FALSE(match(CU32Zero, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_TRUE(match(CU32Zero, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CU32Zero, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CU32Zero, cst_pred_ty<always_false_pred<APInt>>()));EXPECT_FALSE(match(CU32DeadBeef, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_FALSE(match(CU32DeadBeef, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CU32DeadBeef, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CU32DeadBeef, cst_pred_ty<always_false_pred<APInt>>()));// Scalar floatAPFloat F32NaN = APFloat::getNaN(APFloat::IEEEsingle());APFloat F32Zero = APFloat::getZero(APFloat::IEEEsingle());APFloat F32Pi(3.14f);Type *F32Ty = Type::getFloatTy(Ctx);Constant *CF32NaN = ConstantFP::get(F32Ty, F32NaN);Constant *CF32Zero = ConstantFP::get(F32Ty, F32Zero);Constant *CF32Pi = ConstantFP::get(F32Ty, F32Pi);EXPECT_TRUE(match(CF32NaN, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_FALSE(match(CF32NaN, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CF32NaN, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CF32NaN, cstfp_pred_ty<always_false_pred<APFloat>>()));EXPECT_FALSE(match(CF32Zero, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_TRUE(match(CF32Zero, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CF32Zero, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CF32Zero, cstfp_pred_ty<always_false_pred<APFloat>>()));EXPECT_FALSE(match(CF32Pi, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_FALSE(match(CF32Pi, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CF32Pi, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CF32Pi, cstfp_pred_ty<always_false_pred<APFloat>>()));auto FixedEC = ElementCount::getFixed(4);auto ScalableEC = ElementCount::getScalable(4);// Vector splatfor (auto EC : {FixedEC, ScalableEC}) {// integerConstant *CSplatU32Max = ConstantVector::getSplat(EC, CU32Max);Constant *CSplatU32Zero = ConstantVector::getSplat(EC, CU32Zero);Constant *CSplatU32DeadBeef = ConstantVector::getSplat(EC, CU32DeadBeef);EXPECT_TRUE(match(CSplatU32Max, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_FALSE(match(CSplatU32Max, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CSplatU32Max, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CSplatU32Max, cst_pred_ty<always_false_pred<APInt>>()));EXPECT_FALSE(match(CSplatU32Zero, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_TRUE(match(CSplatU32Zero, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CSplatU32Zero, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CSplatU32Zero, cst_pred_ty<always_false_pred<APInt>>()));EXPECT_FALSE(match(CSplatU32DeadBeef, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_FALSE(match(CSplatU32DeadBeef, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CSplatU32DeadBeef, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CSplatU32DeadBeef, cst_pred_ty<always_false_pred<APInt>>()));// floatConstant *CSplatF32NaN = ConstantVector::getSplat(EC, CF32NaN);Constant *CSplatF32Zero = ConstantVector::getSplat(EC, CF32Zero);Constant *CSplatF32Pi = ConstantVector::getSplat(EC, CF32Pi);EXPECT_TRUE(match(CSplatF32NaN, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_FALSE(match(CSplatF32NaN, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CSplatF32NaN, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CSplatF32NaN, cstfp_pred_ty<always_false_pred<APFloat>>()));EXPECT_FALSE(match(CSplatF32Zero, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_TRUE(match(CSplatF32Zero, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CSplatF32Zero, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CSplatF32Zero, cstfp_pred_ty<always_false_pred<APFloat>>()));EXPECT_FALSE(match(CSplatF32Pi, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_FALSE(match(CSplatF32Pi, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CSplatF32Pi, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CSplatF32Pi, cstfp_pred_ty<always_false_pred<APFloat>>()));}// Int arbitrary vectorConstant *CMixedU32 = ConstantVector::get({CU32Max, CU32Zero, CU32DeadBeef});Constant *CU32Undef = UndefValue::get(U32Ty);Constant *CU32MaxWithUndef =ConstantVector::get({CU32Undef, CU32Max, CU32Undef});EXPECT_FALSE(match(CMixedU32, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_FALSE(match(CMixedU32, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CMixedU32, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CMixedU32, cst_pred_ty<always_false_pred<APInt>>()));EXPECT_TRUE(match(CU32MaxWithUndef, cst_pred_ty<is_unsigned_max_pred>()));EXPECT_FALSE(match(CU32MaxWithUndef, cst_pred_ty<is_unsigned_zero_pred>()));EXPECT_TRUE(match(CU32MaxWithUndef, cst_pred_ty<always_true_pred<APInt>>()));EXPECT_FALSE(match(CU32MaxWithUndef, cst_pred_ty<always_false_pred<APInt>>()));// Float arbitrary vectorConstant *CMixedF32 = ConstantVector::get({CF32NaN, CF32Zero, CF32Pi});Constant *CF32Undef = UndefValue::get(F32Ty);Constant *CF32NaNWithUndef =ConstantVector::get({CF32Undef, CF32NaN, CF32Undef});EXPECT_FALSE(match(CMixedF32, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_FALSE(match(CMixedF32, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CMixedF32, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CMixedF32, cstfp_pred_ty<always_false_pred<APFloat>>()));EXPECT_TRUE(match(CF32NaNWithUndef, cstfp_pred_ty<is_float_nan_pred>()));EXPECT_FALSE(match(CF32NaNWithUndef, cstfp_pred_ty<is_float_zero_pred>()));EXPECT_TRUE(match(CF32NaNWithUndef, cstfp_pred_ty<always_true_pred<APFloat>>()));EXPECT_FALSE(match(CF32NaNWithUndef, cstfp_pred_ty<always_false_pred<APFloat>>()));}TEST_F(PatternMatchTest, InsertValue) {Type *StructTy = StructType::create(IRB.getContext(),{IRB.getInt32Ty(), IRB.getInt64Ty()});Value *Ins0 =IRB.CreateInsertValue(UndefValue::get(StructTy), IRB.getInt32(20), 0);Value *Ins1 = IRB.CreateInsertValue(Ins0, IRB.getInt64(90), 1);EXPECT_TRUE(match(Ins0, m_InsertValue<0>(m_Value(), m_Value())));EXPECT_FALSE(match(Ins0, m_InsertValue<1>(m_Value(), m_Value())));EXPECT_FALSE(match(Ins1, m_InsertValue<0>(m_Value(), m_Value())));EXPECT_TRUE(match(Ins1, m_InsertValue<1>(m_Value(), m_Value())));EXPECT_TRUE(match(Ins0, m_InsertValue<0>(m_Undef(), m_SpecificInt(20))));EXPECT_FALSE(match(Ins0, m_InsertValue<0>(m_Undef(), m_SpecificInt(0))));EXPECT_TRUE(match(Ins1, m_InsertValue<1>(m_InsertValue<0>(m_Value(), m_Value()),m_SpecificInt(90))));EXPECT_FALSE(match(IRB.getInt64(99), m_InsertValue<0>(m_Value(), m_Value())));}TEST_F(PatternMatchTest, LogicalSelects) {Value *Alloca = IRB.CreateAlloca(IRB.getInt1Ty());Value *X = IRB.CreateLoad(IRB.getInt1Ty(), Alloca);Value *Y = IRB.CreateLoad(IRB.getInt1Ty(), Alloca);Constant *T = IRB.getInt1(true);Constant *F = IRB.getInt1(false);Value *And = IRB.CreateSelect(X, Y, F);Value *Or = IRB.CreateSelect(X, T, Y);// Logical and:// Check basic no-capture logic - opcode and constant must match.EXPECT_TRUE(match(And, m_LogicalAnd(m_Value(), m_Value())));EXPECT_TRUE(match(And, m_c_LogicalAnd(m_Value(), m_Value())));EXPECT_FALSE(match(And, m_LogicalOr(m_Value(), m_Value())));EXPECT_FALSE(match(And, m_c_LogicalOr(m_Value(), m_Value())));// Check with captures.EXPECT_TRUE(match(And, m_LogicalAnd(m_Specific(X), m_Value())));EXPECT_TRUE(match(And, m_LogicalAnd(m_Value(), m_Specific(Y))));EXPECT_TRUE(match(And, m_LogicalAnd(m_Specific(X), m_Specific(Y))));EXPECT_FALSE(match(And, m_LogicalAnd(m_Specific(Y), m_Value())));EXPECT_FALSE(match(And, m_LogicalAnd(m_Value(), m_Specific(X))));EXPECT_FALSE(match(And, m_LogicalAnd(m_Specific(Y), m_Specific(X))));EXPECT_FALSE(match(And, m_LogicalAnd(m_Specific(X), m_Specific(X))));EXPECT_FALSE(match(And, m_LogicalAnd(m_Specific(Y), m_Specific(Y))));// Check captures for commutative match.EXPECT_TRUE(match(And, m_c_LogicalAnd(m_Specific(X), m_Value())));EXPECT_TRUE(match(And, m_c_LogicalAnd(m_Value(), m_Specific(Y))));EXPECT_TRUE(match(And, m_c_LogicalAnd(m_Specific(X), m_Specific(Y))));EXPECT_TRUE(match(And, m_c_LogicalAnd(m_Specific(Y), m_Value())));EXPECT_TRUE(match(And, m_c_LogicalAnd(m_Value(), m_Specific(X))));EXPECT_TRUE(match(And, m_c_LogicalAnd(m_Specific(Y), m_Specific(X))));EXPECT_FALSE(match(And, m_c_LogicalAnd(m_Specific(X), m_Specific(X))));EXPECT_FALSE(match(And, m_c_LogicalAnd(m_Specific(Y), m_Specific(Y))));// Logical or:// Check basic no-capture logic - opcode and constant must match.EXPECT_TRUE(match(Or, m_LogicalOr(m_Value(), m_Value())));EXPECT_TRUE(match(Or, m_c_LogicalOr(m_Value(), m_Value())));EXPECT_FALSE(match(Or, m_LogicalAnd(m_Value(), m_Value())));EXPECT_FALSE(match(Or, m_c_LogicalAnd(m_Value(), m_Value())));// Check with captures.EXPECT_TRUE(match(Or, m_LogicalOr(m_Specific(X), m_Value())));EXPECT_TRUE(match(Or, m_LogicalOr(m_Value(), m_Specific(Y))));EXPECT_TRUE(match(Or, m_LogicalOr(m_Specific(X), m_Specific(Y))));EXPECT_FALSE(match(Or, m_LogicalOr(m_Specific(Y), m_Value())));EXPECT_FALSE(match(Or, m_LogicalOr(m_Value(), m_Specific(X))));EXPECT_FALSE(match(Or, m_LogicalOr(m_Specific(Y), m_Specific(X))));EXPECT_FALSE(match(Or, m_LogicalOr(m_Specific(X), m_Specific(X))));EXPECT_FALSE(match(Or, m_LogicalOr(m_Specific(Y), m_Specific(Y))));// Check captures for commutative match.EXPECT_TRUE(match(Or, m_c_LogicalOr(m_Specific(X), m_Value())));EXPECT_TRUE(match(Or, m_c_LogicalOr(m_Value(), m_Specific(Y))));EXPECT_TRUE(match(Or, m_c_LogicalOr(m_Specific(X), m_Specific(Y))));EXPECT_TRUE(match(Or, m_c_LogicalOr(m_Specific(Y), m_Value())));EXPECT_TRUE(match(Or, m_c_LogicalOr(m_Value(), m_Specific(X))));EXPECT_TRUE(match(Or, m_c_LogicalOr(m_Specific(Y), m_Specific(X))));EXPECT_FALSE(match(Or, m_c_LogicalOr(m_Specific(X), m_Specific(X))));EXPECT_FALSE(match(Or, m_c_LogicalOr(m_Specific(Y), m_Specific(Y))));}TEST_F(PatternMatchTest, VScale) {DataLayout DL = M->getDataLayout();Type *VecTy = ScalableVectorType::get(IRB.getInt8Ty(), 1);Type *VecPtrTy = VecTy->getPointerTo();Value *NullPtrVec = Constant::getNullValue(VecPtrTy);Value *GEP = IRB.CreateGEP(VecTy, NullPtrVec, IRB.getInt64(1));Value *PtrToInt = IRB.CreatePtrToInt(GEP, DL.getIntPtrType(GEP->getType()));EXPECT_TRUE(match(PtrToInt, m_VScale(DL)));// This used to cause assertion failures when attempting to match m_VScale.// With opaque pointers the bitcast is no longer present.Type *VecTy2 = ScalableVectorType::get(IRB.getInt8Ty(), 2);Value *NullPtrVec2 = Constant::getNullValue(VecTy2->getPointerTo());Value *BitCast = IRB.CreateBitCast(NullPtrVec2, VecPtrTy);Value *GEP2 = IRB.CreateGEP(VecTy, BitCast, IRB.getInt64(1));Value *PtrToInt2 =IRB.CreatePtrToInt(GEP2, DL.getIntPtrType(GEP2->getType()));EXPECT_TRUE(match(PtrToInt2, m_VScale(DL)));}TEST_F(PatternMatchTest, NotForbidUndef) {Type *ScalarTy = IRB.getInt8Ty();Type *VectorTy = FixedVectorType::get(ScalarTy, 3);Constant *ScalarUndef = UndefValue::get(ScalarTy);Constant *ScalarOnes = Constant::getAllOnesValue(ScalarTy);Constant *VectorZero = Constant::getNullValue(VectorTy);Constant *VectorOnes = Constant::getAllOnesValue(VectorTy);SmallVector<Constant *, 3> MixedElems;MixedElems.push_back(ScalarOnes);MixedElems.push_back(ScalarOnes);MixedElems.push_back(ScalarUndef);Constant *VectorMixed = ConstantVector::get(MixedElems);Value *Not = IRB.CreateXor(VectorZero, VectorOnes);Value *X;EXPECT_TRUE(match(Not, m_Not(m_Value())));EXPECT_TRUE(match(Not, m_NotForbidUndef(m_Value(X))));EXPECT_TRUE(match(X, m_Zero()));Value *NotCommute = IRB.CreateXor(VectorOnes, VectorZero);Value *Y;EXPECT_TRUE(match(NotCommute, m_Not(m_Value())));EXPECT_TRUE(match(NotCommute, m_NotForbidUndef(m_Value(Y))));EXPECT_TRUE(match(Y, m_Zero()));Value *NotWithUndefs = IRB.CreateXor(VectorZero, VectorMixed);EXPECT_TRUE(match(NotWithUndefs, m_Not(m_Value())));EXPECT_FALSE(match(NotWithUndefs, m_NotForbidUndef(m_Value())));Value *NotWithUndefsCommute = IRB.CreateXor(VectorMixed, VectorZero);EXPECT_TRUE(match(NotWithUndefsCommute, m_Not(m_Value())));EXPECT_FALSE(match(NotWithUndefsCommute, m_NotForbidUndef(m_Value(X))));}template <typename T> struct MutableConstTest : PatternMatchTest { };typedef ::testing::Types<std::tuple<Value*, Instruction*>,std::tuple<const Value*, const Instruction *>>MutableConstTestTypes;TYPED_TEST_SUITE(MutableConstTest, MutableConstTestTypes, );TYPED_TEST(MutableConstTest, ICmp) {auto &IRB = PatternMatchTest::IRB;typedef std::tuple_element_t<0, TypeParam> ValueType;typedef std::tuple_element_t<1, TypeParam> InstructionType;Value *L = IRB.getInt32(1);Value *R = IRB.getInt32(2);ICmpInst::Predicate Pred = ICmpInst::ICMP_UGT;ValueType MatchL;ValueType MatchR;ICmpInst::Predicate MatchPred;EXPECT_TRUE(m_ICmp(MatchPred, m_Value(MatchL), m_Value(MatchR)).match((InstructionType)IRB.CreateICmp(Pred, L, R)));EXPECT_EQ(L, MatchL);EXPECT_EQ(R, MatchR);}TEST_F(PatternMatchTest, ConstExpr) {Constant *G =M->getOrInsertGlobal("dummy", PointerType::getUnqual(IRB.getInt32Ty()));Constant *S = ConstantExpr::getPtrToInt(G, IRB.getInt32Ty());Type *VecTy = FixedVectorType::get(IRB.getInt32Ty(), 2);PoisonValue *P = PoisonValue::get(VecTy);Constant *V = ConstantExpr::getInsertElement(P, S, IRB.getInt32(0));// The match succeeds on a constant that is a constant expression itself// or a constant that contains a constant expression.EXPECT_TRUE(match(S, m_ConstantExpr()));EXPECT_TRUE(match(V, m_ConstantExpr()));}} // anonymous namespace.
//===- llvm/unittest/IR/PassManager.cpp - PassManager tests ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/PassManager.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/TargetTransformInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/PassManagerImpl.h"#include "llvm/Passes/StandardInstrumentations.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Scalar/SimplifyCFG.h"#include "gtest/gtest.h"using namespace llvm;namespace {class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> {public:struct Result {Result(int Count) : InstructionCount(Count) {}int InstructionCount;bool invalidate(Function &, const PreservedAnalyses &PA,FunctionAnalysisManager::Invalidator &) {// Check whether the analysis or all analyses on functions have been// preserved.auto PAC = PA.getChecker<TestFunctionAnalysis>();return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>());}};TestFunctionAnalysis(int &Runs) : Runs(Runs) {}/// Run the analysis pass over the function and return a result.Result run(Function &F, FunctionAnalysisManager &AM) {++Runs;int Count = 0;for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI)for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE;++II)++Count;return Result(Count);}private:friend AnalysisInfoMixin<TestFunctionAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestFunctionAnalysis::Key;class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> {public:struct Result {Result(int Count) : FunctionCount(Count) {}int FunctionCount;bool invalidate(Module &, const PreservedAnalyses &PA,ModuleAnalysisManager::Invalidator &) {// Check whether the analysis or all analyses on modules have been// preserved.auto PAC = PA.getChecker<TestModuleAnalysis>();return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Module>>());}};TestModuleAnalysis(int &Runs) : Runs(Runs) {}Result run(Module &M, ModuleAnalysisManager &AM) {++Runs;int Count = 0;for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)++Count;return Result(Count);}private:friend AnalysisInfoMixin<TestModuleAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestModuleAnalysis::Key;struct TestModulePass : PassInfoMixin<TestModulePass> {TestModulePass(int &RunCount) : RunCount(RunCount) {}PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {++RunCount;return PreservedAnalyses::none();}int &RunCount;};struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> {PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {return PreservedAnalyses::all();}};struct TestFunctionPass : PassInfoMixin<TestFunctionPass> {TestFunctionPass(int &RunCount, int &AnalyzedInstrCount,int &AnalyzedFunctionCount, ModuleAnalysisManager &MAM,bool OnlyUseCachedResults = false): RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount),AnalyzedFunctionCount(AnalyzedFunctionCount), MAM(MAM),OnlyUseCachedResults(OnlyUseCachedResults) {}PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {++RunCount;// Getting a cached result that isn't stateless through the proxy will// trigger an assert:// auto &ModuleProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);// Use MAM, for the purposes of this unittest.if (TestModuleAnalysis::Result *TMA =MAM.getCachedResult<TestModuleAnalysis>(*F.getParent())) {AnalyzedFunctionCount += TMA->FunctionCount;}if (OnlyUseCachedResults) {// Hack to force the use of the cached interface.if (TestFunctionAnalysis::Result *AR =AM.getCachedResult<TestFunctionAnalysis>(F))AnalyzedInstrCount += AR->InstructionCount;} else {// Typical path just runs the analysis as needed.TestFunctionAnalysis::Result &AR = AM.getResult<TestFunctionAnalysis>(F);AnalyzedInstrCount += AR.InstructionCount;}return PreservedAnalyses::all();}int &RunCount;int &AnalyzedInstrCount;int &AnalyzedFunctionCount;ModuleAnalysisManager &MAM;bool OnlyUseCachedResults;};// A test function pass that invalidates all function analyses for a function// with a specific name.struct TestInvalidationFunctionPass: PassInfoMixin<TestInvalidationFunctionPass> {TestInvalidationFunctionPass(StringRef FunctionName) : Name(FunctionName) {}PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {return F.getName() == Name ? PreservedAnalyses::none(): PreservedAnalyses::all();}StringRef Name;};std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) {SMDiagnostic Err;return parseAssemblyString(IR, Err, Context);}class PassManagerTest : public ::testing::Test {protected:LLVMContext Context;std::unique_ptr<Module> M;public:PassManagerTest(): M(parseIR(Context, "define void @f() {\n""entry:\n"" call void @g()\n"" call void @h()\n"" ret void\n""}\n""define void @g() {\n"" ret void\n""}\n""define void @h() {\n"" ret void\n""}\n")) {}};TEST(PreservedAnalysesTest, Basic) {PreservedAnalyses PA1 = PreservedAnalyses();{auto PAC = PA1.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}{auto PAC = PA1.getChecker<TestModuleAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>());}auto PA2 = PreservedAnalyses::none();{auto PAC = PA2.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}auto PA3 = PreservedAnalyses::all();{auto PAC = PA3.getChecker<TestFunctionAnalysis>();EXPECT_TRUE(PAC.preserved());EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());}PreservedAnalyses PA4 = PA1;{auto PAC = PA4.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}PA4 = PA3;{auto PAC = PA4.getChecker<TestFunctionAnalysis>();EXPECT_TRUE(PAC.preserved());EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());}PA4 = std::move(PA2);{auto PAC = PA4.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}auto PA5 = PreservedAnalyses::allInSet<AllAnalysesOn<Function>>();{auto PAC = PA5.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>());}}TEST(PreservedAnalysesTest, Preserve) {auto PA = PreservedAnalyses::none();PA.preserve<TestFunctionAnalysis>();EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved());PA.preserve<TestModuleAnalysis>();EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved());EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved());// Redundant calls are fine.PA.preserve<TestFunctionAnalysis>();EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved());EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved());}TEST(PreservedAnalysesTest, PreserveSets) {auto PA = PreservedAnalyses::none();PA.preserveSet<AllAnalysesOn<Function>>();EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());PA.preserveSet<AllAnalysesOn<Module>>();EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Mixing is fine.PA.preserve<TestFunctionAnalysis>();EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Redundant calls are fine.PA.preserveSet<AllAnalysesOn<Module>>();EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());}TEST(PreservedAnalysisTest, Intersect) {// Setup the initial sets.auto PA1 = PreservedAnalyses::none();PA1.preserve<TestFunctionAnalysis>();PA1.preserveSet<AllAnalysesOn<Module>>();auto PA2 = PreservedAnalyses::none();PA2.preserve<TestFunctionAnalysis>();PA2.preserveSet<AllAnalysesOn<Function>>();PA2.preserve<TestModuleAnalysis>();PA2.preserveSet<AllAnalysesOn<Module>>();auto PA3 = PreservedAnalyses::none();PA3.preserve<TestModuleAnalysis>();PA3.preserveSet<AllAnalysesOn<Function>>();// Self intersection is a no-op.auto Intersected = PA1;Intersected.intersect(PA1);EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Intersecting with all is a no-op.Intersected.intersect(PreservedAnalyses::all());EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Intersecting a narrow set with a more broad set is the narrow set.Intersected.intersect(PA2);EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Intersecting a broad set with a more narrow set is the narrow set.Intersected = PA2;Intersected.intersect(PA1);EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Intersecting with empty clears.Intersected.intersect(PreservedAnalyses::none());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Intersecting non-overlapping clears.Intersected = PA1;Intersected.intersect(PA3);EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Intersecting with moves works in when there is storage on both sides.Intersected = PA1;auto Tmp = PA2;Intersected.intersect(std::move(Tmp));EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// Intersecting with move works for incoming all and existing all.auto Tmp2 = PreservedAnalyses::all();Intersected.intersect(std::move(Tmp2));EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());Intersected = PreservedAnalyses::all();auto Tmp3 = PA1;Intersected.intersect(std::move(Tmp3));EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());}TEST(PreservedAnalysisTest, Abandon) {auto PA = PreservedAnalyses::none();// We can abandon things after they are preserved.PA.preserve<TestFunctionAnalysis>();PA.abandon<TestFunctionAnalysis>();EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved());// Repeated is fine, and abandoning if they were never preserved is fine.PA.abandon<TestFunctionAnalysis>();EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved());PA.abandon<TestModuleAnalysis>();EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved());// Even if the sets are preserved, the abandoned analyses' checker won't// return true for those sets.PA.preserveSet<AllAnalysesOn<Function>>();PA.preserveSet<AllAnalysesOn<Module>>();EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preservedSet<AllAnalysesOn<Module>>());// But an arbitrary (opaque) analysis will still observe the sets as// preserved. This also checks that we can use an explicit ID rather than// a type.AnalysisKey FakeKey, *FakeID = &FakeKey;EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Function>>());EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Module>>());}TEST_F(PassManagerTest, Basic) {FunctionAnalysisManager FAM;int FunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });ModuleAnalysisManager MAM;int ModuleAnalysisRuns = 0;MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });MAM.registerPass([&] { return PassInstrumentationAnalysis(); });FAM.registerPass([&] { return PassInstrumentationAnalysis(); });ModulePassManager MPM;// Count the runs over a Function.int FunctionPassRunCount1 = 0;int AnalyzedInstrCount1 = 0;int AnalyzedFunctionCount1 = 0;{// Pointless scoped copy to test move assignment.ModulePassManager NestedMPM;FunctionPassManager FPM;{// Pointless scope to test move assignment.FunctionPassManager NestedFPM;NestedFPM.addPass(TestFunctionPass(FunctionPassRunCount1,AnalyzedInstrCount1,AnalyzedFunctionCount1, MAM));FPM = std::move(NestedFPM);}NestedMPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM = std::move(NestedMPM);}// Count the runs over a module.int ModulePassRunCount = 0;MPM.addPass(TestModulePass(ModulePassRunCount));// Count the runs over a Function in a separate manager.int FunctionPassRunCount2 = 0;int AnalyzedInstrCount2 = 0;int AnalyzedFunctionCount2 = 0;{FunctionPassManager FPM;FPM.addPass(TestFunctionPass(FunctionPassRunCount2, AnalyzedInstrCount2,AnalyzedFunctionCount2, MAM));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));}// A third function pass manager but with only preserving intervening passes// and with a function pass that invalidates exactly one analysis.MPM.addPass(TestPreservingModulePass());int FunctionPassRunCount3 = 0;int AnalyzedInstrCount3 = 0;int AnalyzedFunctionCount3 = 0;{FunctionPassManager FPM;FPM.addPass(TestFunctionPass(FunctionPassRunCount3, AnalyzedInstrCount3,AnalyzedFunctionCount3, MAM));FPM.addPass(TestInvalidationFunctionPass("f"));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));}// A fourth function pass manager but with only preserving intervening// passes but triggering the module analysis.MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());int FunctionPassRunCount4 = 0;int AnalyzedInstrCount4 = 0;int AnalyzedFunctionCount4 = 0;{FunctionPassManager FPM;FPM.addPass(TestFunctionPass(FunctionPassRunCount4, AnalyzedInstrCount4,AnalyzedFunctionCount4, MAM));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));}// A fifth function pass manager which invalidates one function first but// uses only cached results.int FunctionPassRunCount5 = 0;int AnalyzedInstrCount5 = 0;int AnalyzedFunctionCount5 = 0;{FunctionPassManager FPM;FPM.addPass(TestInvalidationFunctionPass("f"));FPM.addPass(TestFunctionPass(FunctionPassRunCount5, AnalyzedInstrCount5,AnalyzedFunctionCount5, MAM,/*OnlyUseCachedResults=*/true));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));}MPM.run(*M, MAM);// Validate module pass counters.EXPECT_EQ(1, ModulePassRunCount);// Validate all function pass counter sets are the same.EXPECT_EQ(3, FunctionPassRunCount1);EXPECT_EQ(5, AnalyzedInstrCount1);EXPECT_EQ(0, AnalyzedFunctionCount1);EXPECT_EQ(3, FunctionPassRunCount2);EXPECT_EQ(5, AnalyzedInstrCount2);EXPECT_EQ(0, AnalyzedFunctionCount2);EXPECT_EQ(3, FunctionPassRunCount3);EXPECT_EQ(5, AnalyzedInstrCount3);EXPECT_EQ(0, AnalyzedFunctionCount3);EXPECT_EQ(3, FunctionPassRunCount4);EXPECT_EQ(5, AnalyzedInstrCount4);EXPECT_EQ(9, AnalyzedFunctionCount4);EXPECT_EQ(3, FunctionPassRunCount5);EXPECT_EQ(2, AnalyzedInstrCount5); // Only 'g' and 'h' were cached.EXPECT_EQ(9, AnalyzedFunctionCount5);// Validate the analysis counters:// first run over 3 functions, then module pass invalidates// second run over 3 functions, nothing invalidates// third run over 0 functions, but 1 function invalidated// fourth run over 1 function// fifth run invalidates 1 function first, but runs over 0 functionsEXPECT_EQ(7, FunctionAnalysisRuns);EXPECT_EQ(1, ModuleAnalysisRuns);}// A customized pass manager that passes extra arguments through the// infrastructure.typedef AnalysisManager<Function, int> CustomizedAnalysisManager;typedef PassManager<Function, CustomizedAnalysisManager, int, int &>CustomizedPassManager;class CustomizedAnalysis : public AnalysisInfoMixin<CustomizedAnalysis> {public:struct Result {Result(int I) : I(I) {}int I;};Result run(Function &F, CustomizedAnalysisManager &AM, int I) {return Result(I);}private:friend AnalysisInfoMixin<CustomizedAnalysis>;static AnalysisKey Key;};AnalysisKey CustomizedAnalysis::Key;struct CustomizedPass : PassInfoMixin<CustomizedPass> {std::function<void(CustomizedAnalysis::Result &, int &)> Callback;template <typename CallbackT>CustomizedPass(CallbackT Callback) : Callback(Callback) {}PreservedAnalyses run(Function &F, CustomizedAnalysisManager &AM, int I,int &O) {Callback(AM.getResult<CustomizedAnalysis>(F, I), O);return PreservedAnalyses::none();}};TEST_F(PassManagerTest, CustomizedPassManagerArgs) {CustomizedAnalysisManager AM;AM.registerPass([&] { return CustomizedAnalysis(); });PassInstrumentationCallbacks PIC;AM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });CustomizedPassManager PM;// Add an instance of the customized pass that just accumulates the input// after it is round-tripped through the analysis.int Result = 0;PM.addPass(CustomizedPass([](CustomizedAnalysis::Result &R, int &O) { O += R.I; }));// Run this over every function with the input of 42.for (Function &F : *M)PM.run(F, AM, 42, Result);// And ensure that we accumulated the correct result.EXPECT_EQ(42 * (int)M->size(), Result);}/// A test analysis pass which caches in its result another analysis pass and/// uses it to serve queries. This requires the result to invalidate itself/// when its dependency is invalidated.struct TestIndirectFunctionAnalysis: public AnalysisInfoMixin<TestIndirectFunctionAnalysis> {struct Result {Result(TestFunctionAnalysis::Result &FDep, TestModuleAnalysis::Result &MDep): FDep(FDep), MDep(MDep) {}TestFunctionAnalysis::Result &FDep;TestModuleAnalysis::Result &MDep;bool invalidate(Function &F, const PreservedAnalyses &PA,FunctionAnalysisManager::Invalidator &Inv) {auto PAC = PA.getChecker<TestIndirectFunctionAnalysis>();return !(PAC.preserved() ||PAC.preservedSet<AllAnalysesOn<Function>>()) ||Inv.invalidate<TestFunctionAnalysis>(F, PA);}};TestIndirectFunctionAnalysis(int &Runs, ModuleAnalysisManager &MAM): Runs(Runs), MAM(MAM) {}/// Run the analysis pass over the function and return a result.Result run(Function &F, FunctionAnalysisManager &AM) {++Runs;auto &FDep = AM.getResult<TestFunctionAnalysis>(F);auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);// For the test, we insist that the module analysis starts off in the// cache. Getting a cached result that isn't stateless trigger an assert.// Use MAM, for the purposes of this unittest.auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*F.getParent());// And register the dependency as module analysis dependencies have to be// pre-registered on the proxy.MAMProxy.registerOuterAnalysisInvalidation<TestModuleAnalysis,TestIndirectFunctionAnalysis>();return Result(FDep, MDep);}private:friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>;static AnalysisKey Key;int &Runs;ModuleAnalysisManager &MAM;};AnalysisKey TestIndirectFunctionAnalysis::Key;/// A test analysis pass which chaches in its result the result from the above/// indirect analysis pass.////// This allows us to ensure that whenever an analysis pass is invalidated due/// to dependencies (especially dependencies across IR units that trigger/// asynchronous invalidation) we correctly detect that this may in turn cause/// other analysis to be invalidated.struct TestDoublyIndirectFunctionAnalysis: public AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis> {struct Result {Result(TestIndirectFunctionAnalysis::Result &IDep) : IDep(IDep) {}TestIndirectFunctionAnalysis::Result &IDep;bool invalidate(Function &F, const PreservedAnalyses &PA,FunctionAnalysisManager::Invalidator &Inv) {auto PAC = PA.getChecker<TestDoublyIndirectFunctionAnalysis>();return !(PAC.preserved() ||PAC.preservedSet<AllAnalysesOn<Function>>()) ||Inv.invalidate<TestIndirectFunctionAnalysis>(F, PA);}};TestDoublyIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {}/// Run the analysis pass over the function and return a result.Result run(Function &F, FunctionAnalysisManager &AM) {++Runs;auto &IDep = AM.getResult<TestIndirectFunctionAnalysis>(F);return Result(IDep);}private:friend AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestDoublyIndirectFunctionAnalysis::Key;struct LambdaPass : public PassInfoMixin<LambdaPass> {using FuncT = std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)>;LambdaPass(FuncT Func) : Func(std::move(Func)) {}PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {return Func(F, AM);}FuncT Func;};TEST_F(PassManagerTest, IndirectAnalysisInvalidation) {FunctionAnalysisManager FAM;ModuleAnalysisManager MAM;int FunctionAnalysisRuns = 0, ModuleAnalysisRuns = 0,IndirectAnalysisRuns = 0, DoublyIndirectAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });FAM.registerPass([&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns, MAM); });FAM.registerPass([&] {return TestDoublyIndirectFunctionAnalysis(DoublyIndirectAnalysisRuns);});MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });PassInstrumentationCallbacks PIC;MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });int InstrCount = 0, FunctionCount = 0;ModulePassManager MPM;FunctionPassManager FPM;// First just use the analysis to get the instruction count, and preserve// everything.FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);auto &IndirectResult = DoublyIndirectResult.IDep;InstrCount += IndirectResult.FDep.InstructionCount;FunctionCount += IndirectResult.MDep.FunctionCount;return PreservedAnalyses::all();}));// Next, invalidate// - both analyses for "f",// - just the underlying (indirect) analysis for "g", and// - just the direct analysis for "h".FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);auto &IndirectResult = DoublyIndirectResult.IDep;InstrCount += IndirectResult.FDep.InstructionCount;FunctionCount += IndirectResult.MDep.FunctionCount;auto PA = PreservedAnalyses::none();if (F.getName() == "g")PA.preserve<TestFunctionAnalysis>();else if (F.getName() == "h")PA.preserve<TestIndirectFunctionAnalysis>();return PA;}));// Finally, use the analysis again on each function, forcing re-computation// for all of them.FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);auto &IndirectResult = DoublyIndirectResult.IDep;InstrCount += IndirectResult.FDep.InstructionCount;FunctionCount += IndirectResult.MDep.FunctionCount;return PreservedAnalyses::all();}));// Create a second function pass manager. This will cause the module-level// invalidation to occur, which will force yet another invalidation of the// indirect function-level analysis as the module analysis it depends on gets// invalidated.FunctionPassManager FPM2;FPM2.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);auto &IndirectResult = DoublyIndirectResult.IDep;InstrCount += IndirectResult.FDep.InstructionCount;FunctionCount += IndirectResult.MDep.FunctionCount;return PreservedAnalyses::all();}));// Add a requires pass to populate the module analysis and then our function// pass pipeline.MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));// Now require the module analysis again (it will have been invalidated once)// and then use it again from a function pass manager.MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM2)));MPM.run(*M, MAM);// There are generally two possible runs for each of the three functions. But// for one function, we only invalidate the indirect analysis so the base one// only gets run five times.EXPECT_EQ(5, FunctionAnalysisRuns);// The module analysis pass should be run twice here.EXPECT_EQ(2, ModuleAnalysisRuns);// The indirect analysis is invalidated for each function (either directly or// indirectly) and run twice for each.EXPECT_EQ(9, IndirectAnalysisRuns);EXPECT_EQ(9, DoublyIndirectAnalysisRuns);// There are five instructions in the module and we add the count four// times.EXPECT_EQ(5 * 4, InstrCount);// There are three functions and we count them four times for each of the// three functions.EXPECT_EQ(3 * 4 * 3, FunctionCount);}// Run SimplifyCFGPass that makes CFG changes and reports PreservedAnalyses// without CFGAnalyses. So the CFGChecker does not complain.TEST_F(PassManagerTest, FunctionPassCFGChecker) {LLVMContext Context;// SimplifyCFG changes this function to// define void @foo {next: ret void}auto M = parseIR(Context, "define void @foo() {\n"" br label %next\n""next:\n"" br label %exit\n""exit:\n"" ret void\n""}\n");auto *F = M->getFunction("foo");FunctionAnalysisManager FAM;FunctionPassManager FPM;PassInstrumentationCallbacks PIC;StandardInstrumentations SI(/*DebugLogging*/ true);SI.registerCallbacks(PIC, &FAM);FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });FAM.registerPass([&] { return DominatorTreeAnalysis(); });FAM.registerPass([&] { return AssumptionAnalysis(); });FAM.registerPass([&] { return TargetIRAnalysis(); });FPM.addPass(SimplifyCFGPass());FPM.run(*F, FAM);}// FunctionPass that manually invalidates analyses and always returns// PreservedAnalyses::all().struct TestSimplifyCFGInvalidatingAnalysisPass: PassInfoMixin<TestSimplifyCFGInvalidatingAnalysisPass> {PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {// Run SimplifyCFG and if it changes CFG then invalidate the CFG analysis.// This allows to return PreserveAnalysis::all().PreservedAnalyses PA = CFGSimplifier.run(F, FAM);FAM.invalidate(F, PA);return PreservedAnalyses::all();}SimplifyCFGPass CFGSimplifier;};// Run TestSimplifyCFGInvalidatingAnalysisPass which changes CFG by running// SimplifyCFGPass then manually invalidates analyses and always returns// PreservedAnalyses::all(). CFGChecker does not complain because it resets// its saved CFG snapshot when the analyses are invalidated manually.TEST_F(PassManagerTest, FunctionPassCFGCheckerInvalidateAnalysis) {LLVMContext Context;// SimplifyCFG changes this function to// define void @foo {next: ret void}auto M = parseIR(Context, "define void @foo() {\n"" br label %next\n""next:\n"" br label %exit\n""exit:\n"" ret void\n""}\n");auto *F = M->getFunction("foo");FunctionAnalysisManager FAM;FunctionPassManager FPM;PassInstrumentationCallbacks PIC;StandardInstrumentations SI(/*DebugLogging*/ true);SI.registerCallbacks(PIC, &FAM);FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });FAM.registerPass([&] { return DominatorTreeAnalysis(); });FAM.registerPass([&] { return AssumptionAnalysis(); });FAM.registerPass([&] { return TargetIRAnalysis(); });FPM.addPass(TestSimplifyCFGInvalidatingAnalysisPass());FPM.run(*F, FAM);}// Wrap a FunctionPassManager running SimplifyCFG pass with another// FunctionPassManager.struct TestSimplifyCFGWrapperPass : PassInfoMixin<TestSimplifyCFGWrapperPass> {TestSimplifyCFGWrapperPass(FunctionPassManager &InnerPM) : InnerPM(InnerPM) {}PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {// Here we simulate exactly what FunctionPassManager::run() does but// instead of running all passes from InnerPM.Passes we run them in bulk// by calling InnerPM.run().PreservedAnalyses PA = PreservedAnalyses::all();PassInstrumentation PI = FAM.getResult<PassInstrumentationAnalysis>(F);if (!PI.runBeforePass<Function>(InnerPM, F))return PreservedAnalyses::all();PreservedAnalyses PassPA = InnerPM.run(F, FAM);PI.runAfterPass(InnerPM, F, PassPA);FAM.invalidate(F, PassPA);PA.intersect(PassPA);PA.preserveSet<AllAnalysesOn<Function>>();return PA;}FunctionPassManager &InnerPM;};// Run TestSimplifyCFGWrapperPass which simulates behavior of// FunctionPassManager::run() except that it runs all passes at once by calling// an inner pass manager's passes with PassManager::run(). This is how one pass// manager is expected to wrap another pass manager.// SimplifyCFGPass, which is called by the inner pass manager, changes the CFG.// The CFGChecker's AfterPassCallback, run right after SimplifyCFGPass, does not// complain because CFGAnalyses is not in the PreservedAnalises set returned by// SimplifyCFGPass. Then the CFG analysis is invalidated by the analysis manager// according to the PreservedAnalises set. Further calls to CFGChecker's// AfterPassCallback see that all analyses for the current function are// preserved but there is no CFG snapshot available (i.e.// AM.getCachedResult<PreservedCFGCheckerAnalysis>(F) returns nullptr).TEST_F(PassManagerTest, FunctionPassCFGCheckerWrapped) {LLVMContext Context;// SimplifyCFG changes this function to// define void @foo {next: ret void}auto M = parseIR(Context, "define void @foo() {\n"" br label %next\n""next:\n"" br label %exit\n""exit:\n"" ret void\n""}\n");auto *F = M->getFunction("foo");FunctionAnalysisManager FAM;FunctionPassManager FPM;PassInstrumentationCallbacks PIC;StandardInstrumentations SI(/*DebugLogging*/ true);SI.registerCallbacks(PIC, &FAM);FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });FAM.registerPass([&] { return DominatorTreeAnalysis(); });FAM.registerPass([&] { return AssumptionAnalysis(); });FAM.registerPass([&] { return TargetIRAnalysis(); });FunctionPassManager InnerFPM;InnerFPM.addPass(SimplifyCFGPass());FPM.addPass(TestSimplifyCFGWrapperPass(InnerFPM));FPM.run(*F, FAM);}}
//===- unittests/IR/PassBuilderCallbacksTest.cpp - PB Callback Tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm/Testing/Support/Error.h"#include <functional>#include <gmock/gmock.h>#include <gtest/gtest.h>#include <llvm/ADT/Any.h>#include <llvm/Analysis/CGSCCPassManager.h>#include <llvm/Analysis/LoopAnalysisManager.h>#include <llvm/AsmParser/Parser.h>#include <llvm/IR/LLVMContext.h>#include <llvm/IR/PassInstrumentation.h>#include <llvm/IR/PassManager.h>#include <llvm/Passes/PassBuilder.h>#include <llvm/Support/Regex.h>#include <llvm/Support/SourceMgr.h>#include <llvm/Transforms/Scalar/LoopPassManager.h>using namespace llvm;namespace {using testing::AnyNumber;using testing::DoAll;using testing::Not;using testing::Return;using testing::WithArgs;using testing::_;/// A CRTP base for analysis mock handles////// This class reconciles mocking with the value semantics implementation of the/// AnalysisManager. Analysis mock handles should derive from this class and/// call \c setDefault() in their constroctur for wiring up the defaults defined/// by this base with their mock run() and invalidate() implementations.template <typename DerivedT, typename IRUnitT,typename AnalysisManagerT = AnalysisManager<IRUnitT>,typename... ExtraArgTs>class MockAnalysisHandleBase {public:class Analysis : public AnalysisInfoMixin<Analysis> {friend AnalysisInfoMixin<Analysis>;friend MockAnalysisHandleBase;static AnalysisKey Key;DerivedT *Handle;Analysis(DerivedT &Handle) : Handle(&Handle) {static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value,"Must pass the derived type to this template!");}public:class Result {friend MockAnalysisHandleBase;DerivedT *Handle;Result(DerivedT &Handle) : Handle(&Handle) {}public:// Forward invalidation events to the mock handle.bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA,typename AnalysisManagerT::Invalidator &Inv) {return Handle->invalidate(IR, PA, Inv);}};Result run(IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) {return Handle->run(IR, AM, ExtraArgs...);}};Analysis getAnalysis() { return Analysis(static_cast<DerivedT &>(*this)); }typename Analysis::Result getResult() {return typename Analysis::Result(static_cast<DerivedT &>(*this));}static StringRef getName() { return llvm::getTypeName<DerivedT>(); }protected:// FIXME: MSVC seems unable to handle a lambda argument to Invoke from within// the template, so we use a boring static function.static bool invalidateCallback(IRUnitT &IR, const PreservedAnalyses &PA,typename AnalysisManagerT::Invalidator &Inv) {auto PAC = PA.template getChecker<Analysis>();return !PAC.preserved() &&!PAC.template preservedSet<AllAnalysesOn<IRUnitT>>();}/// Derived classes should call this in their constructor to set up default/// mock actions. (We can't do this in our constructor because this has to/// run after the DerivedT is constructed.)void setDefaults() {ON_CALL(static_cast<DerivedT &>(*this),run(_, _, testing::Matcher<ExtraArgTs>(_)...)).WillByDefault(Return(this->getResult()));ON_CALL(static_cast<DerivedT &>(*this), invalidate(_, _, _)).WillByDefault(&invalidateCallback);}};/// A CRTP base for pass mock handles////// This class reconciles mocking with the value semantics implementation of the/// PassManager. Pass mock handles should derive from this class and/// call \c setDefault() in their constroctur for wiring up the defaults defined/// by this base with their mock run() and invalidate() implementations.template <typename DerivedT, typename IRUnitT, typename AnalysisManagerT,typename... ExtraArgTs>AnalysisKey MockAnalysisHandleBase<DerivedT, IRUnitT, AnalysisManagerT,ExtraArgTs...>::Analysis::Key;template <typename DerivedT, typename IRUnitT,typename AnalysisManagerT = AnalysisManager<IRUnitT>,typename... ExtraArgTs>class MockPassHandleBase {public:class Pass : public PassInfoMixin<Pass> {friend MockPassHandleBase;DerivedT *Handle;Pass(DerivedT &Handle) : Handle(&Handle) {static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value,"Must pass the derived type to this template!");}public:PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,ExtraArgTs... ExtraArgs) {return Handle->run(IR, AM, ExtraArgs...);}};static StringRef getName() { return llvm::getTypeName<DerivedT>(); }Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); }protected:/// Derived classes should call this in their constructor to set up default/// mock actions. (We can't do this in our constructor because this has to/// run after the DerivedT is constructed.)void setDefaults() {ON_CALL(static_cast<DerivedT &>(*this),run(_, _, testing::Matcher<ExtraArgTs>(_)...)).WillByDefault(Return(PreservedAnalyses::all()));}};/// Mock handles for passes for the IRUnits Module, CGSCC, Function, Loop./// These handles define the appropriate run() mock interface for the respective/// IRUnit type.template <typename IRUnitT> struct MockPassHandle;template <>struct MockPassHandle<Loop>: MockPassHandleBase<MockPassHandle<Loop>, Loop, LoopAnalysisManager,LoopStandardAnalysisResults &, LPMUpdater &> {MOCK_METHOD4(run,PreservedAnalyses(Loop &, LoopAnalysisManager &,LoopStandardAnalysisResults &, LPMUpdater &));static void invalidateLoop(Loop &L, LoopAnalysisManager &,LoopStandardAnalysisResults &,LPMUpdater &Updater) {Updater.markLoopAsDeleted(L, L.getName());}MockPassHandle() { setDefaults(); }};template <>struct MockPassHandle<LoopNest>: MockPassHandleBase<MockPassHandle<LoopNest>, LoopNest,LoopAnalysisManager, LoopStandardAnalysisResults &,LPMUpdater &> {MOCK_METHOD4(run,PreservedAnalyses(LoopNest &, LoopAnalysisManager &,LoopStandardAnalysisResults &, LPMUpdater &));static void invalidateLoopNest(LoopNest &L, LoopAnalysisManager &,LoopStandardAnalysisResults &,LPMUpdater &Updater) {Updater.markLoopAsDeleted(L.getOutermostLoop(), L.getName());}MockPassHandle() { setDefaults(); }};template <>struct MockPassHandle<Function>: MockPassHandleBase<MockPassHandle<Function>, Function> {MOCK_METHOD2(run, PreservedAnalyses(Function &, FunctionAnalysisManager &));MockPassHandle() { setDefaults(); }};template <>struct MockPassHandle<LazyCallGraph::SCC>: MockPassHandleBase<MockPassHandle<LazyCallGraph::SCC>, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &> {MOCK_METHOD4(run,PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &,LazyCallGraph &G, CGSCCUpdateResult &UR));static void invalidateSCC(LazyCallGraph::SCC &C, CGSCCAnalysisManager &,LazyCallGraph &, CGSCCUpdateResult &UR) {UR.InvalidatedSCCs.insert(&C);}MockPassHandle() { setDefaults(); }};template <>struct MockPassHandle<Module>: MockPassHandleBase<MockPassHandle<Module>, Module> {MOCK_METHOD2(run, PreservedAnalyses(Module &, ModuleAnalysisManager &));MockPassHandle() { setDefaults(); }};/// Mock handles for analyses for the IRUnits Module, CGSCC, Function, Loop./// These handles define the appropriate run() and invalidate() mock interfaces/// for the respective IRUnit type.template <typename IRUnitT> struct MockAnalysisHandle;template <>struct MockAnalysisHandle<Loop>: MockAnalysisHandleBase<MockAnalysisHandle<Loop>, Loop,LoopAnalysisManager,LoopStandardAnalysisResults &> {MOCK_METHOD3_T(run, typename Analysis::Result(Loop &, LoopAnalysisManager &,LoopStandardAnalysisResults &));MOCK_METHOD3_T(invalidate, bool(Loop &, const PreservedAnalyses &,LoopAnalysisManager::Invalidator &));MockAnalysisHandle<Loop>() { this->setDefaults(); }};template <>struct MockAnalysisHandle<Function>: MockAnalysisHandleBase<MockAnalysisHandle<Function>, Function> {MOCK_METHOD2(run, Analysis::Result(Function &, FunctionAnalysisManager &));MOCK_METHOD3(invalidate, bool(Function &, const PreservedAnalyses &,FunctionAnalysisManager::Invalidator &));MockAnalysisHandle<Function>() { setDefaults(); }};template <>struct MockAnalysisHandle<LazyCallGraph::SCC>: MockAnalysisHandleBase<MockAnalysisHandle<LazyCallGraph::SCC>,LazyCallGraph::SCC, CGSCCAnalysisManager,LazyCallGraph &> {MOCK_METHOD3(run, Analysis::Result(LazyCallGraph::SCC &,CGSCCAnalysisManager &, LazyCallGraph &));MOCK_METHOD3(invalidate, bool(LazyCallGraph::SCC &, const PreservedAnalyses &,CGSCCAnalysisManager::Invalidator &));MockAnalysisHandle<LazyCallGraph::SCC>() { setDefaults(); }};template <>struct MockAnalysisHandle<Module>: MockAnalysisHandleBase<MockAnalysisHandle<Module>, Module> {MOCK_METHOD2(run, Analysis::Result(Module &, ModuleAnalysisManager &));MOCK_METHOD3(invalidate, bool(Module &, const PreservedAnalyses &,ModuleAnalysisManager::Invalidator &));MockAnalysisHandle<Module>() { setDefaults(); }};static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;return parseAssemblyString(IR, Err, C);}/// Helper for HasName matcher that returns getName both for IRUnit and/// for IRUnit pointer wrapper into llvm::Any (wrapped by PassInstrumentation).template <typename IRUnitT> std::string getName(const IRUnitT &IR) {return std::string(IR.getName());}template <> std::string getName(const StringRef &name) {return std::string(name);}template <> std::string getName(const llvm::Any &WrappedIR) {if (any_isa<const Module *>(WrappedIR))return any_cast<const Module *>(WrappedIR)->getName().str();if (any_isa<const Function *>(WrappedIR))return any_cast<const Function *>(WrappedIR)->getName().str();if (any_isa<const Loop *>(WrappedIR))return any_cast<const Loop *>(WrappedIR)->getName().str();if (any_isa<const LoopNest *>(WrappedIR))return any_cast<const LoopNest *>(WrappedIR)->getName().str();if (any_isa<const LazyCallGraph::SCC *>(WrappedIR))return any_cast<const LazyCallGraph::SCC *>(WrappedIR)->getName();return "<UNKNOWN>";}/// Define a custom matcher for objects which support a 'getName' method.////// LLVM often has IR objects or analysis objects which expose a name/// and in tests it is convenient to match these by name for readability./// Usually, this name is either a StringRef or a plain std::string. This/// matcher supports any type exposing a getName() method of this form whose/// return value is compatible with an std::ostream. For StringRef, this uses/// the shift operator defined above.////// It should be used as:////// HasName("my_function")////// No namespace or other qualification is required.MATCHER_P(HasName, Name, "") {*result_listener << "has name '" << getName(arg) << "'";return Name == getName(arg);}MATCHER_P(HasNameRegex, Name, "") {*result_listener << "has name '" << getName(arg) << "'";llvm::Regex r(Name);return r.match(getName(arg));}struct MockPassInstrumentationCallbacks {PassInstrumentationCallbacks Callbacks;MockPassInstrumentationCallbacks() {ON_CALL(*this, runBeforePass(_, _)).WillByDefault(Return(true));}MOCK_METHOD2(runBeforePass, bool(StringRef PassID, llvm::Any));MOCK_METHOD2(runBeforeSkippedPass, void(StringRef PassID, llvm::Any));MOCK_METHOD2(runBeforeNonSkippedPass, void(StringRef PassID, llvm::Any));MOCK_METHOD3(runAfterPass,void(StringRef PassID, llvm::Any, const PreservedAnalyses &PA));MOCK_METHOD2(runAfterPassInvalidated,void(StringRef PassID, const PreservedAnalyses &PA));MOCK_METHOD2(runBeforeAnalysis, void(StringRef PassID, llvm::Any));MOCK_METHOD2(runAfterAnalysis, void(StringRef PassID, llvm::Any));void registerPassInstrumentation() {Callbacks.registerShouldRunOptionalPassCallback([this](StringRef P, llvm::Any IR) {return this->runBeforePass(P, IR);});Callbacks.registerBeforeSkippedPassCallback([this](StringRef P, llvm::Any IR) {this->runBeforeSkippedPass(P, IR);});Callbacks.registerBeforeNonSkippedPassCallback([this](StringRef P, llvm::Any IR) {this->runBeforeNonSkippedPass(P, IR);});Callbacks.registerAfterPassCallback([this](StringRef P, llvm::Any IR, const PreservedAnalyses &PA) {this->runAfterPass(P, IR, PA);});Callbacks.registerAfterPassInvalidatedCallback([this](StringRef P, const PreservedAnalyses &PA) {this->runAfterPassInvalidated(P, PA);});Callbacks.registerBeforeAnalysisCallback([this](StringRef P, llvm::Any IR) {return this->runBeforeAnalysis(P, IR);});Callbacks.registerAfterAnalysisCallback([this](StringRef P, llvm::Any IR) { this->runAfterAnalysis(P, IR); });}void ignoreNonMockPassInstrumentation(StringRef IRName) {// Generic EXPECT_CALLs are needed to match instrumentation on unimportant// parts of a pipeline that we do not care about (e.g. various passes added// by default by PassBuilder - Verifier pass etc).// Make sure to avoid ignoring Mock passes/analysis, we definitely want// to check these explicitly.EXPECT_CALL(*this,runBeforePass(Not(HasNameRegex("Mock")), HasName(IRName))).Times(AnyNumber());EXPECT_CALL(*this, runBeforeSkippedPass(Not(HasNameRegex("Mock")), HasName(IRName))).Times(AnyNumber());EXPECT_CALL(*this, runBeforeNonSkippedPass(Not(HasNameRegex("Mock")),HasName(IRName))).Times(AnyNumber());EXPECT_CALL(*this,runAfterPass(Not(HasNameRegex("Mock")), HasName(IRName), _)).Times(AnyNumber());EXPECT_CALL(*this,runBeforeAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))).Times(AnyNumber());EXPECT_CALL(*this,runAfterAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))).Times(AnyNumber());}};template <typename IRUnitT>using ExtraMockPassHandle =std::conditional_t<std::is_same<IRUnitT, Loop>::value,MockPassHandle<LoopNest>, MockPassHandle<IRUnitT>>;template <typename PassManagerT> class PassBuilderCallbacksTest;/// This test fixture is shared between all the actual tests below and/// takes care of setting up appropriate defaults.////// The template specialization serves to extract the IRUnit and AM types from/// the given PassManagerT.template <typename TestIRUnitT, typename... ExtraPassArgTs,typename... ExtraAnalysisArgTs>class PassBuilderCallbacksTest<PassManager<TestIRUnitT, AnalysisManager<TestIRUnitT, ExtraAnalysisArgTs...>,ExtraPassArgTs...>> : public testing::Test {protected:using IRUnitT = TestIRUnitT;using AnalysisManagerT = AnalysisManager<TestIRUnitT, ExtraAnalysisArgTs...>;using PassManagerT =PassManager<TestIRUnitT, AnalysisManagerT, ExtraPassArgTs...>;using AnalysisT = typename MockAnalysisHandle<IRUnitT>::Analysis;LLVMContext Context;std::unique_ptr<Module> M;MockPassInstrumentationCallbacks CallbacksHandle;PassBuilder PB;ModulePassManager PM;LoopAnalysisManager LAM;FunctionAnalysisManager FAM;CGSCCAnalysisManager CGAM;ModuleAnalysisManager AM;MockPassHandle<IRUnitT> PassHandle;ExtraMockPassHandle<IRUnitT> ExtraPassHandle;MockAnalysisHandle<IRUnitT> AnalysisHandle;static PreservedAnalyses getAnalysisResult(IRUnitT &U, AnalysisManagerT &AM,ExtraAnalysisArgTs &&... Args) {(void)AM.template getResult<AnalysisT>(U, std::forward<ExtraAnalysisArgTs>(Args)...);return PreservedAnalyses::all();}PassBuilderCallbacksTest(): M(parseIR(Context,"declare void @bar()\n""define void @foo(i32 %n) {\n""entry:\n"" br label %loop\n""loop:\n"" %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]\n"" %iv.next = add i32 %iv, 1\n"" tail call void @bar()\n"" %cmp = icmp eq i32 %iv, %n\n"" br i1 %cmp, label %exit, label %loop\n""exit:\n"" ret void\n""}\n")),CallbacksHandle(),PB(nullptr, PipelineTuningOptions(), None, &CallbacksHandle.Callbacks),PM(), LAM(), FAM(), CGAM(), AM() {EXPECT_TRUE(&CallbacksHandle.Callbacks ==PB.getPassInstrumentationCallbacks());/// Register a callback for analysis registration.////// The callback is a function taking a reference to an AnalyisManager/// object. When called, the callee gets to register its own analyses with/// this PassBuilder instance.PB.registerAnalysisRegistrationCallback([this](AnalysisManagerT &AM) {// Register our mock analysisAM.registerPass([this] { return AnalysisHandle.getAnalysis(); });});/// Register a callback for pipeline parsing.////// During parsing of a textual pipeline, the PassBuilder will call these/// callbacks for each encountered pass name that it does not know. This/// includes both simple pass names as well as names of sub-pipelines. In/// the latter case, the InnerPipeline is not empty.PB.registerPipelineParsingCallback([this](StringRef Name, PassManagerT &PM,ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {/// Handle parsing of the names of analysis utilities such as/// require<test-analysis> and invalidate<test-analysis> for our/// analysis mock handleif (parseAnalysisUtilityPasses<AnalysisT>("test-analysis", Name, PM))return true;/// Parse the name of our pass mock handleif (Name == "test-transform") {PM.addPass(PassHandle.getPass());if (std::is_same<IRUnitT, Loop>::value)PM.addPass(ExtraPassHandle.getPass());return true;}return false;});/// Register builtin analyses and cross-register the analysis proxiesPB.registerModuleAnalyses(AM);PB.registerCGSCCAnalyses(CGAM);PB.registerFunctionAnalyses(FAM);PB.registerLoopAnalyses(LAM);PB.crossRegisterProxies(LAM, FAM, CGAM, AM);}};using ModuleCallbacksTest = PassBuilderCallbacksTest<ModulePassManager>;using CGSCCCallbacksTest = PassBuilderCallbacksTest<CGSCCPassManager>;using FunctionCallbacksTest = PassBuilderCallbacksTest<FunctionPassManager>;using LoopCallbacksTest = PassBuilderCallbacksTest<LoopPassManager>;/// Test parsing of the name of our mock pass for all IRUnits.////// The pass should by default run our mock analysis and then preserve it.TEST_F(ModuleCallbacksTest, Passes) {EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _));EXPECT_CALL(PassHandle, run(HasName("<string>"), _)).WillOnce(&getAnalysisResult);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(ModuleCallbacksTest, InstrumentedPasses) {EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _));EXPECT_CALL(PassHandle, run(HasName("<string>"), _)).WillOnce(&getAnalysisResult);CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation not specifically mentioned below can be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");// PassInstrumentation calls should happen in-sequence, in the same order// as passes/analyses are scheduled.::testing::Sequence PISequence;EXPECT_CALL(CallbacksHandle, runBeforePass(HasNameRegex("MockPassHandle"),HasName("<string>"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"),HasName("<string>"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"),HasName("<string>"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("<string>"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"),HasName("<string>"), _)).InSequence(PISequence);// No passes are skipped, so there should be no calls to// runBeforeSkippedPass().EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("<string>"))).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(ModuleCallbacksTest, InstrumentedSkippedPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation run here can safely be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");// Skip all passes by returning false. Pass managers and adaptor passes are// also passes that observed by the callbacks.EXPECT_CALL(CallbacksHandle, runBeforePass(_, _)).WillRepeatedly(Return(false));EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle"), _)).Times(3);EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _)).Times(0);EXPECT_CALL(PassHandle, run(HasName("<string>"), _)).Times(0);// As the pass is skipped there is no nonskippedpass/afterPass,// beforeAnalysis/afterAnalysis as well.EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), _, _)).Times(0);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);// Order is important here. `Adaptor` expectations should be checked first// because the its argument contains 'PassManager' (for example:// ModuleToFunctionPassAdaptor{{.*}}PassManager{{.*}}). Check// `runBeforeNonSkippedPass` and `runAfterPass` to show that they are not// skipped.//// Pass managers are not ignored.// 5 = (1) ModulePassManager + (2) FunctionPassMangers + (1) LoopPassManager +// (1) CGSCCPassManagerEXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("PassManager"), _)).Times(5);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("ModuleToFunctionPassAdaptor"), _)).Times(1);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor"), _)).Times(1);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("CGSCCToFunctionPassAdaptor"), _)).Times(1);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("FunctionToLoopPassAdaptor"), _)).Times(1);// The `runAfterPass` checks are the same as these of// `runBeforeNonSkippedPass`.EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("PassManager"), _, _)).Times(5);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("ModuleToFunctionPassAdaptor"), _, _)).Times(1);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor"), _, _)).Times(1);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("CGSCCToFunctionPassAdaptor"), _, _)).Times(1);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("FunctionToLoopPassAdaptor"), _, _)).Times(1);// Ignore analyses introduced by adaptor passes.EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(Not(HasNameRegex("MockAnalysisHandle")), _)).Times(AnyNumber());EXPECT_CALL(CallbacksHandle,runAfterAnalysis(Not(HasNameRegex("MockAnalysisHandle")), _)).Times(AnyNumber());// Register Funtion and Loop version of "test-transform" for testingPB.registerPipelineParsingCallback([](StringRef Name, FunctionPassManager &FPM,ArrayRef<PassBuilder::PipelineElement>) {if (Name == "test-transform") {FPM.addPass(MockPassHandle<Function>().getPass());return true;}return false;});PB.registerPipelineParsingCallback([](StringRef Name, LoopPassManager &LPM,ArrayRef<PassBuilder::PipelineElement>) {if (Name == "test-transform") {LPM.addPass(MockPassHandle<Loop>().getPass());return true;}return false;});StringRef PipelineText = "test-transform,function(test-transform),cgscc(""function(loop(test-transform)))";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(FunctionCallbacksTest, Passes) {EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _));EXPECT_CALL(PassHandle, run(HasName("foo"), _)).WillOnce(&getAnalysisResult);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(FunctionCallbacksTest, InstrumentedPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation not specifically mentioned below can be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _));EXPECT_CALL(PassHandle, run(HasName("foo"), _)).WillOnce(&getAnalysisResult);// PassInstrumentation calls should happen in-sequence, in the same order// as passes/analyses are scheduled.::testing::Sequence PISequence;EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("foo"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("foo"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("foo"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("foo"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), HasName("foo"), _)).InSequence(PISequence);// No passes are skipped, so there should be no calls to// runBeforeSkippedPass().EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("foo"))).Times(0);// Our mock pass does not invalidate IR.EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(FunctionCallbacksTest, InstrumentedSkippedPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation run here can safely be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");// Skip the pass by returning false.EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("foo"))).WillOnce(Return(false));EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("foo"))).Times(1);EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)).Times(0);EXPECT_CALL(PassHandle, run(HasName("foo"), _)).Times(0);// As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis// as well.EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), _, _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), _, _)).Times(0);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(LoopCallbacksTest, Passes) {EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _));EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult));EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _));StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(LoopCallbacksTest, InstrumentedPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation not specifically mentioned below can be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");CallbacksHandle.ignoreNonMockPassInstrumentation("loop");EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _));EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult));EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _));// PassInstrumentation calls should happen in-sequence, in the same order// as passes/analyses are scheduled.::testing::Sequence PISequence;EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"),HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"),HasName("loop"), _)).InSequence(PISequence);// Our mock pass does not invalidate IR.EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)).Times(0);// No passes are skipped, so there should be no calls to// runBeforeSkippedPass().EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(LoopCallbacksTest, InstrumentedInvalidatingPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation not specifically mentioned below can be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");CallbacksHandle.ignoreNonMockPassInstrumentation("loop");EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _));EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).WillOnce(DoAll(WithArgs<0, 1, 2, 3>(&PassHandle.invalidateLoop),WithArgs<0, 1, 2>(&getAnalysisResult)));// PassInstrumentation calls should happen in-sequence, in the same order// as passes/analyses are scheduled.::testing::Sequence PISequence;EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("^PassManager"), _)).InSequence(PISequence);// Our mock pass invalidates IR, thus normal runAfterPass is never called.EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(LoopCallbacksTest, InstrumentedInvalidatingLoopNestPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation not specifically mentioned below can be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");CallbacksHandle.ignoreNonMockPassInstrumentation("loop");EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _));EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult));EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)).WillOnce(DoAll(&ExtraPassHandle.invalidateLoopNest,[&](LoopNest &, LoopAnalysisManager &,LoopStandardAnalysisResults &,LPMUpdater &) { return PreservedAnalyses::all(); }));// PassInstrumentation calls should happen in-sequence, in the same order// as passes/analyses are scheduled.::testing::Sequence PISequence;EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"),HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"), HasName("loop"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>"), _)).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("^PassManager"), _)).InSequence(PISequence);// Our mock pass invalidates IR, thus normal runAfterPass is never called.EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*Loop>"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"),HasName("loop"), _)).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(LoopCallbacksTest, InstrumentedSkippedPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation run here can safely be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");CallbacksHandle.ignoreNonMockPassInstrumentation("loop");// Skip the pass by returning false.EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle<.*Loop>"), HasName("loop"))).WillOnce(Return(false));EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*Loop>"),HasName("loop"))).Times(1);EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"),HasName("loop"))).WillOnce(Return(false));EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"),HasName("loop"))).Times(1);EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)).Times(0);EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).Times(0);EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)).Times(0);// As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis// as well.EXPECT_CALL(CallbacksHandle, runBeforeNonSkippedPass(HasNameRegex("MockPassHandle<.*Loop>"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle<.*Loop>"), _, _)).Times(0);EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*Loop>"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"), _, _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(CGSCCCallbacksTest, Passes) {EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _));EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)).WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult));StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(CGSCCCallbacksTest, InstrumentedPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation not specifically mentioned below can be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)");EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _));EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)).WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult));// PassInstrumentation calls should happen in-sequence, in the same order// as passes/analyses are scheduled.::testing::Sequence PISequence;EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"), _)).InSequence(PISequence);// Our mock pass does not invalidate IR.EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)).Times(0);// No passes are skipped, so there should be no calls to// runBeforeSkippedPass().EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(CGSCCCallbacksTest, InstrumentedInvalidatingPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation not specifically mentioned below can be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)");EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _));EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)).WillOnce(DoAll(WithArgs<0, 1, 2, 3>(&PassHandle.invalidateSCC),WithArgs<0, 1, 2>(&getAnalysisResult)));// PassInstrumentation calls should happen in-sequence, in the same order// as passes/analyses are scheduled.::testing::Sequence PISequence;EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)).InSequence(PISequence);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("^PassManager"), _)).InSequence(PISequence);// Our mock pass does invalidate IR, thus normal runAfterPass is never called.EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"), _)).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(CGSCCCallbacksTest, InstrumentedSkippedPasses) {CallbacksHandle.registerPassInstrumentation();// Non-mock instrumentation run here can safely be ignored.CallbacksHandle.ignoreNonMockPassInstrumentation("<string>");CallbacksHandle.ignoreNonMockPassInstrumentation("foo");CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)");// Skip the pass by returning false.EXPECT_CALL(CallbacksHandle,runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))).WillOnce(Return(false));EXPECT_CALL(CallbacksHandle,runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))).Times(1);// neither Analysis nor Pass are called.EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)).Times(0);EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)).Times(0);// As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis// as well.EXPECT_CALL(CallbacksHandle,runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPass(HasNameRegex("MockPassHandle"), _, _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);EXPECT_CALL(CallbacksHandle,runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)).Times(0);StringRef PipelineText = "test-transform";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}/// Test parsing of the names of analysis utilities for our mock analysis/// for all IRUnits.////// We first require<>, then invalidate<> it, expecting the analysis to be run/// once and subsequently invalidated.TEST_F(ModuleCallbacksTest, AnalysisUtilities) {EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _));EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>"), _, _));StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(CGSCCCallbacksTest, PassUtilities) {EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _));EXPECT_CALL(AnalysisHandle, invalidate(HasName("(foo)"), _, _));StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(FunctionCallbacksTest, AnalysisUtilities) {EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _));EXPECT_CALL(AnalysisHandle, invalidate(HasName("foo"), _, _));StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}TEST_F(LoopCallbacksTest, PassUtilities) {EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _));EXPECT_CALL(AnalysisHandle, invalidate(HasName("loop"), _, _));StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);}/// Test parsing of the top-level pipeline.////// The ParseTopLevelPipeline callback takes over parsing of the entire pipeline/// from PassBuilder if it encounters an unknown pipeline entry at the top level/// (i.e., the first entry on the pipeline)./// This test parses a pipeline named 'another-pipeline', whose only elements/// may be the test-transform pass or the analysis utilitiesTEST_F(ModuleCallbacksTest, ParseTopLevelPipeline) {PB.registerParseTopLevelPipelineCallback([this](ModulePassManager &MPM,ArrayRef<PassBuilder::PipelineElement> Pipeline) {auto &FirstName = Pipeline.front().Name;auto &InnerPipeline = Pipeline.front().InnerPipeline;if (FirstName == "another-pipeline") {for (auto &E : InnerPipeline) {if (parseAnalysisUtilityPasses<AnalysisT>("test-analysis", E.Name,PM))continue;if (E.Name == "test-transform") {PM.addPass(PassHandle.getPass());continue;}return false;}}return true;});EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _));EXPECT_CALL(PassHandle, run(HasName("<string>"), _)).WillOnce(&getAnalysisResult);EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>"), _, _));StringRef PipelineText ="another-pipeline(test-transform,invalidate<test-analysis>)";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded())<< "Pipeline was: " << PipelineText;PM.run(*M, AM);/// Test the negative casePipelineText = "another-pipeline(instcombine)";ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Failed())<< "Pipeline was: " << PipelineText;}} // end anonymous namespace
//===- unittests/IR/ModuleTest.cpp - Module unit tests --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Module.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/ModuleSummaryIndex.h"#include "llvm/Pass.h"#include "llvm/Support/RandomNumberGenerator.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"#include <random>using namespace llvm;namespace {bool sortByName(const GlobalVariable &L, const GlobalVariable &R) {return L.getName() < R.getName();}bool sortByNameReverse(const GlobalVariable &L, const GlobalVariable &R) {return sortByName(R, L);}TEST(ModuleTest, sortGlobalsByName) {LLVMContext Context;for (auto compare : {&sortByName, &sortByNameReverse}) {Module M("M", Context);Type *T = Type::getInt8Ty(Context);GlobalValue::LinkageTypes L = GlobalValue::ExternalLinkage;(void)new GlobalVariable(M, T, false, L, nullptr, "A");(void)new GlobalVariable(M, T, false, L, nullptr, "F");(void)new GlobalVariable(M, T, false, L, nullptr, "G");(void)new GlobalVariable(M, T, false, L, nullptr, "E");(void)new GlobalVariable(M, T, false, L, nullptr, "B");(void)new GlobalVariable(M, T, false, L, nullptr, "H");(void)new GlobalVariable(M, T, false, L, nullptr, "C");(void)new GlobalVariable(M, T, false, L, nullptr, "D");// Sort the globals by name.EXPECT_FALSE(std::is_sorted(M.global_begin(), M.global_end(), compare));M.getGlobalList().sort(compare);EXPECT_TRUE(std::is_sorted(M.global_begin(), M.global_end(), compare));}}TEST(ModuleTest, randomNumberGenerator) {LLVMContext Context;static char ID;struct DummyPass : ModulePass {DummyPass() : ModulePass(ID) {}bool runOnModule(Module &) override { return true; }} DP;Module M("R", Context);std::uniform_int_distribution<int> dist;const size_t NBCheck = 10;std::array<int, NBCheck> RandomStreams[2];for (auto &RandomStream : RandomStreams) {std::unique_ptr<RandomNumberGenerator> RNG = M.createRNG(DP.getPassName());std::generate(RandomStream.begin(), RandomStream.end(),[&]() { return dist(*RNG); });}EXPECT_TRUE(std::equal(RandomStreams[0].begin(), RandomStreams[0].end(),RandomStreams[1].begin()));}TEST(ModuleTest, setModuleFlag) {LLVMContext Context;Module M("M", Context);StringRef Key = "Key";Metadata *Val1 = MDString::get(Context, "Val1");Metadata *Val2 = MDString::get(Context, "Val2");EXPECT_EQ(nullptr, M.getModuleFlag(Key));M.setModuleFlag(Module::ModFlagBehavior::Error, Key, Val1);EXPECT_EQ(Val1, M.getModuleFlag(Key));M.setModuleFlag(Module::ModFlagBehavior::Error, Key, Val2);EXPECT_EQ(Val2, M.getModuleFlag(Key));}const char *IRString = R"IR(!llvm.module.flags = !{!0}!0 = !{i32 1, !"ProfileSummary", !1}!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}!2 = !{!"ProfileFormat", !"SampleProfile"}!3 = !{!"TotalCount", i64 10000}!4 = !{!"MaxCount", i64 10}!5 = !{!"MaxInternalCount", i64 1}!6 = !{!"MaxFunctionCount", i64 1000}!7 = !{!"NumCounts", i64 200}!8 = !{!"NumFunctions", i64 3}!9 = !{!"DetailedSummary", !10}!10 = !{!11, !12, !13}!11 = !{i32 10000, i64 1000, i32 1}!12 = !{i32 990000, i64 300, i32 10}!13 = !{i32 999999, i64 5, i32 100})IR";TEST(ModuleTest, setProfileSummary) {SMDiagnostic Err;LLVMContext Context;std::unique_ptr<Module> M = parseAssemblyString(IRString, Err, Context);auto *PS = ProfileSummary::getFromMD(M->getProfileSummary(/*IsCS*/ false));EXPECT_NE(nullptr, PS);EXPECT_FALSE(PS->isPartialProfile());PS->setPartialProfile(true);M->setProfileSummary(PS->getMD(Context), ProfileSummary::PSK_Sample);delete PS;PS = ProfileSummary::getFromMD(M->getProfileSummary(/*IsCS*/ false));EXPECT_NE(nullptr, PS);EXPECT_EQ(true, PS->isPartialProfile());delete PS;}TEST(ModuleTest, setPartialSampleProfileRatio) {const char *IRString = R"IR(!llvm.module.flags = !{!0}!0 = !{i32 1, !"ProfileSummary", !1}!1 = !{!2, !3, !4, !5, !6, !7, !8, !9, !10, !11}!2 = !{!"ProfileFormat", !"SampleProfile"}!3 = !{!"TotalCount", i64 10000}!4 = !{!"MaxCount", i64 10}!5 = !{!"MaxInternalCount", i64 1}!6 = !{!"MaxFunctionCount", i64 1000}!7 = !{!"NumCounts", i64 200}!8 = !{!"NumFunctions", i64 3}!9 = !{!"IsPartialProfile", i64 1}!10 = !{!"PartialProfileRatio", double 0.0}!11 = !{!"DetailedSummary", !12}!12 = !{!13, !14, !15}!13 = !{i32 10000, i64 1000, i32 1}!14 = !{i32 990000, i64 300, i32 10}!15 = !{i32 999999, i64 5, i32 100})IR";SMDiagnostic Err;LLVMContext Context;std::unique_ptr<Module> M = parseAssemblyString(IRString, Err, Context);ModuleSummaryIndex Index(/*HaveGVs*/ false);const unsigned BlockCount = 100;const unsigned NumCounts = 200;Index.setBlockCount(BlockCount);M->setPartialSampleProfileRatio(Index);double Ratio = (double)BlockCount / NumCounts;std::unique_ptr<ProfileSummary> ProfileSummary(ProfileSummary::getFromMD(M->getProfileSummary(/*IsCS*/ false)));EXPECT_EQ(Ratio, ProfileSummary->getPartialProfileRatio());}} // end namespace
//===- unittests/IR/MetadataTest.cpp - Metadata unit tests ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Metadata.h"#include "llvm/ADT/DenseMap.h"#include "llvm/ADT/STLExtras.h"#include "llvm/IR/Constants.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DebugInfo.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/Function.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/ModuleSlotTracker.h"#include "llvm/IR/Type.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ContextAndReplaceableUsesTest, FromContext) {LLVMContext Context;ContextAndReplaceableUses CRU(Context);EXPECT_EQ(&Context, &CRU.getContext());EXPECT_FALSE(CRU.hasReplaceableUses());EXPECT_FALSE(CRU.getReplaceableUses());}TEST(ContextAndReplaceableUsesTest, FromReplaceableUses) {LLVMContext Context;ContextAndReplaceableUses CRU(std::make_unique<ReplaceableMetadataImpl>(Context));EXPECT_EQ(&Context, &CRU.getContext());EXPECT_TRUE(CRU.hasReplaceableUses());EXPECT_TRUE(CRU.getReplaceableUses());}TEST(ContextAndReplaceableUsesTest, makeReplaceable) {LLVMContext Context;ContextAndReplaceableUses CRU(Context);CRU.makeReplaceable(std::make_unique<ReplaceableMetadataImpl>(Context));EXPECT_EQ(&Context, &CRU.getContext());EXPECT_TRUE(CRU.hasReplaceableUses());EXPECT_TRUE(CRU.getReplaceableUses());}TEST(ContextAndReplaceableUsesTest, takeReplaceableUses) {LLVMContext Context;auto ReplaceableUses = std::make_unique<ReplaceableMetadataImpl>(Context);auto *Ptr = ReplaceableUses.get();ContextAndReplaceableUses CRU(std::move(ReplaceableUses));ReplaceableUses = CRU.takeReplaceableUses();EXPECT_EQ(&Context, &CRU.getContext());EXPECT_FALSE(CRU.hasReplaceableUses());EXPECT_FALSE(CRU.getReplaceableUses());EXPECT_EQ(Ptr, ReplaceableUses.get());}class MetadataTest : public testing::Test {public:MetadataTest() : M("test", Context), Counter(0) {}protected:LLVMContext Context;Module M;int Counter;MDNode *getNode() { return MDNode::get(Context, None); }MDNode *getNode(Metadata *MD) { return MDNode::get(Context, MD); }MDNode *getNode(Metadata *MD1, Metadata *MD2) {Metadata *MDs[] = {MD1, MD2};return MDNode::get(Context, MDs);}MDTuple *getTuple() { return MDTuple::getDistinct(Context, None); }DISubroutineType *getSubroutineType() {return DISubroutineType::getDistinct(Context, DINode::FlagZero, 0,getNode(nullptr));}DISubprogram *getSubprogram() {return DISubprogram::getDistinct(Context, nullptr, "", "", nullptr, 0, nullptr, 0, nullptr, 0, 0,DINode::FlagZero, DISubprogram::SPFlagZero, nullptr);}DIFile *getFile() {return DIFile::getDistinct(Context, "file.c", "/path/to/dir");}DICompileUnit *getUnit() {return DICompileUnit::getDistinct(Context, 1, getFile(), "clang", false, "-g", 2, "",DICompileUnit::FullDebug, getTuple(), getTuple(), getTuple(),getTuple(), getTuple(), 0, true, false,DICompileUnit::DebugNameTableKind::Default, false, "/", "");}DIType *getBasicType(StringRef Name) {return DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type, Name);}DIType *getDerivedType() {return DIDerivedType::getDistinct(Context, dwarf::DW_TAG_pointer_type, "", nullptr, 0, nullptr,getBasicType("basictype"), 1, 2, 0, None, DINode::FlagZero);}Constant *getConstant() {return ConstantInt::get(Type::getInt32Ty(Context), Counter++);}ConstantAsMetadata *getConstantAsMetadata() {return ConstantAsMetadata::get(getConstant());}DIType *getCompositeType() {return DICompositeType::getDistinct(Context, dwarf::DW_TAG_structure_type, "", nullptr, 0, nullptr, nullptr,32, 32, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, "");}Function *getFunction(StringRef Name) {return Function::Create(FunctionType::get(Type::getVoidTy(Context), None, false),Function::ExternalLinkage, Name, M);}};typedef MetadataTest MDStringTest;// Test that construction of MDString with different value produces different// MDString objects, even with the same string pointer and nulls in the string.TEST_F(MDStringTest, CreateDifferent) {char x[3] = { 'f', 0, 'A' };MDString *s1 = MDString::get(Context, StringRef(&x[0], 3));x[2] = 'B';MDString *s2 = MDString::get(Context, StringRef(&x[0], 3));EXPECT_NE(s1, s2);}// Test that creation of MDStrings with the same string contents produces the// same MDString object, even with different pointers.TEST_F(MDStringTest, CreateSame) {char x[4] = { 'a', 'b', 'c', 'X' };char y[4] = { 'a', 'b', 'c', 'Y' };MDString *s1 = MDString::get(Context, StringRef(&x[0], 3));MDString *s2 = MDString::get(Context, StringRef(&y[0], 3));EXPECT_EQ(s1, s2);}// Test that MDString prints out the string we fed it.TEST_F(MDStringTest, PrintingSimple) {char str[14] = "testing 1 2 3";MDString *s = MDString::get(Context, StringRef(&str[0], 13));strncpy(str, "aaaaaaaaaaaaa", 14);std::string Str;raw_string_ostream oss(Str);s->print(oss);EXPECT_STREQ("!\"testing 1 2 3\"", oss.str().c_str());}// Test printing of MDString with non-printable characters.TEST_F(MDStringTest, PrintingComplex) {char str[5] = {0, '\n', '"', '\\', (char)-1};MDString *s = MDString::get(Context, StringRef(str+0, 5));std::string Str;raw_string_ostream oss(Str);s->print(oss);EXPECT_STREQ("!\"\\00\\0A\\22\\\\\\FF\"", oss.str().c_str());}typedef MetadataTest MDNodeTest;// Test the two constructors, and containing other Constants.TEST_F(MDNodeTest, Simple) {char x[3] = { 'a', 'b', 'c' };char y[3] = { '1', '2', '3' };MDString *s1 = MDString::get(Context, StringRef(&x[0], 3));MDString *s2 = MDString::get(Context, StringRef(&y[0], 3));ConstantAsMetadata *CI =ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));std::vector<Metadata *> V;V.push_back(s1);V.push_back(CI);V.push_back(s2);MDNode *n1 = MDNode::get(Context, V);Metadata *const c1 = n1;MDNode *n2 = MDNode::get(Context, c1);Metadata *const c2 = n2;MDNode *n3 = MDNode::get(Context, V);MDNode *n4 = MDNode::getIfExists(Context, V);MDNode *n5 = MDNode::getIfExists(Context, c1);MDNode *n6 = MDNode::getIfExists(Context, c2);EXPECT_NE(n1, n2);EXPECT_EQ(n1, n3);EXPECT_EQ(n4, n1);EXPECT_EQ(n5, n2);EXPECT_EQ(n6, (Metadata *)nullptr);EXPECT_EQ(3u, n1->getNumOperands());EXPECT_EQ(s1, n1->getOperand(0));EXPECT_EQ(CI, n1->getOperand(1));EXPECT_EQ(s2, n1->getOperand(2));EXPECT_EQ(1u, n2->getNumOperands());EXPECT_EQ(n1, n2->getOperand(0));}TEST_F(MDNodeTest, Delete) {Constant *C = ConstantInt::get(Type::getInt32Ty(Context), 1);Instruction *I = new BitCastInst(C, Type::getInt32Ty(Context));Metadata *const V = LocalAsMetadata::get(I);MDNode *n = MDNode::get(Context, V);TrackingMDRef wvh(n);EXPECT_EQ(n, wvh);I->deleteValue();}TEST_F(MDNodeTest, SelfReference) {// !0 = !{!0}// !1 = !{!0}{auto Temp = MDNode::getTemporary(Context, None);Metadata *Args[] = {Temp.get()};MDNode *Self = MDNode::get(Context, Args);Self->replaceOperandWith(0, Self);ASSERT_EQ(Self, Self->getOperand(0));// Self-references should be distinct, so MDNode::get() should grab a// uniqued node that references Self, not Self.Args[0] = Self;MDNode *Ref1 = MDNode::get(Context, Args);MDNode *Ref2 = MDNode::get(Context, Args);EXPECT_NE(Self, Ref1);EXPECT_EQ(Ref1, Ref2);}// !0 = !{!0, !{}}// !1 = !{!0, !{}}{auto Temp = MDNode::getTemporary(Context, None);Metadata *Args[] = {Temp.get(), MDNode::get(Context, None)};MDNode *Self = MDNode::get(Context, Args);Self->replaceOperandWith(0, Self);ASSERT_EQ(Self, Self->getOperand(0));// Self-references should be distinct, so MDNode::get() should grab a// uniqued node that references Self, not Self itself.Args[0] = Self;MDNode *Ref1 = MDNode::get(Context, Args);MDNode *Ref2 = MDNode::get(Context, Args);EXPECT_NE(Self, Ref1);EXPECT_EQ(Ref1, Ref2);}}TEST_F(MDNodeTest, Print) {Constant *C = ConstantInt::get(Type::getInt32Ty(Context), 7);MDString *S = MDString::get(Context, "foo");MDNode *N0 = getNode();MDNode *N1 = getNode(N0);MDNode *N2 = getNode(N0, N1);Metadata *Args[] = {ConstantAsMetadata::get(C), S, nullptr, N0, N1, N2};MDNode *N = MDNode::get(Context, Args);std::string Expected;{raw_string_ostream OS(Expected);OS << "<" << (void *)N << "> = !{";C->printAsOperand(OS);OS << ", ";S->printAsOperand(OS);OS << ", null";MDNode *Nodes[] = {N0, N1, N2};for (auto *Node : Nodes)OS << ", <" << (void *)Node << ">";OS << "}";}std::string Actual;{raw_string_ostream OS(Actual);N->print(OS);}EXPECT_EQ(Expected, Actual);}#define EXPECT_PRINTER_EQ(EXPECTED, PRINT) \do { \std::string Actual_; \raw_string_ostream OS(Actual_); \PRINT; \OS.flush(); \std::string Expected_(EXPECTED); \EXPECT_EQ(Expected_, Actual_); \} while (false)TEST_F(MDNodeTest, PrintTemporary) {MDNode *Arg = getNode();TempMDNode Temp = MDNode::getTemporary(Context, Arg);MDNode *N = getNode(Temp.get());Module M("test", Context);NamedMDNode *NMD = M.getOrInsertNamedMetadata("named");NMD->addOperand(N);EXPECT_PRINTER_EQ("!0 = !{!1}", N->print(OS, &M));EXPECT_PRINTER_EQ("!1 = <temporary!> !{!2}", Temp->print(OS, &M));EXPECT_PRINTER_EQ("!2 = !{}", Arg->print(OS, &M));// Cleanup.Temp->replaceAllUsesWith(Arg);}TEST_F(MDNodeTest, PrintFromModule) {Constant *C = ConstantInt::get(Type::getInt32Ty(Context), 7);MDString *S = MDString::get(Context, "foo");MDNode *N0 = getNode();MDNode *N1 = getNode(N0);MDNode *N2 = getNode(N0, N1);Metadata *Args[] = {ConstantAsMetadata::get(C), S, nullptr, N0, N1, N2};MDNode *N = MDNode::get(Context, Args);Module M("test", Context);NamedMDNode *NMD = M.getOrInsertNamedMetadata("named");NMD->addOperand(N);std::string Expected;{raw_string_ostream OS(Expected);OS << "!0 = !{";C->printAsOperand(OS);OS << ", ";S->printAsOperand(OS);OS << ", null, !1, !2, !3}";}EXPECT_PRINTER_EQ(Expected, N->print(OS, &M));}TEST_F(MDNodeTest, PrintFromFunction) {Module M("test", Context);auto *FTy = FunctionType::get(Type::getVoidTy(Context), false);auto *F0 = Function::Create(FTy, GlobalValue::ExternalLinkage, "F0", &M);auto *F1 = Function::Create(FTy, GlobalValue::ExternalLinkage, "F1", &M);auto *BB0 = BasicBlock::Create(Context, "entry", F0);auto *BB1 = BasicBlock::Create(Context, "entry", F1);auto *R0 = ReturnInst::Create(Context, BB0);auto *R1 = ReturnInst::Create(Context, BB1);auto *N0 = MDNode::getDistinct(Context, None);auto *N1 = MDNode::getDistinct(Context, None);R0->setMetadata("md", N0);R1->setMetadata("md", N1);EXPECT_PRINTER_EQ("!0 = distinct !{}", N0->print(OS, &M));EXPECT_PRINTER_EQ("!1 = distinct !{}", N1->print(OS, &M));ModuleSlotTracker MST(&M);EXPECT_PRINTER_EQ("!0 = distinct !{}", N0->print(OS, MST));EXPECT_PRINTER_EQ("!1 = distinct !{}", N1->print(OS, MST));}TEST_F(MDNodeTest, PrintFromMetadataAsValue) {Module M("test", Context);auto *Intrinsic =Function::Create(FunctionType::get(Type::getVoidTy(Context),Type::getMetadataTy(Context), false),GlobalValue::ExternalLinkage, "llvm.intrinsic", &M);auto *FTy = FunctionType::get(Type::getVoidTy(Context), false);auto *F0 = Function::Create(FTy, GlobalValue::ExternalLinkage, "F0", &M);auto *F1 = Function::Create(FTy, GlobalValue::ExternalLinkage, "F1", &M);auto *BB0 = BasicBlock::Create(Context, "entry", F0);auto *BB1 = BasicBlock::Create(Context, "entry", F1);auto *N0 = MDNode::getDistinct(Context, None);auto *N1 = MDNode::getDistinct(Context, None);auto *MAV0 = MetadataAsValue::get(Context, N0);auto *MAV1 = MetadataAsValue::get(Context, N1);CallInst::Create(Intrinsic, MAV0, "", BB0);CallInst::Create(Intrinsic, MAV1, "", BB1);EXPECT_PRINTER_EQ("!0 = distinct !{}", MAV0->print(OS));EXPECT_PRINTER_EQ("!1 = distinct !{}", MAV1->print(OS));EXPECT_PRINTER_EQ("!0", MAV0->printAsOperand(OS, false));EXPECT_PRINTER_EQ("!1", MAV1->printAsOperand(OS, false));EXPECT_PRINTER_EQ("metadata !0", MAV0->printAsOperand(OS, true));EXPECT_PRINTER_EQ("metadata !1", MAV1->printAsOperand(OS, true));ModuleSlotTracker MST(&M);EXPECT_PRINTER_EQ("!0 = distinct !{}", MAV0->print(OS, MST));EXPECT_PRINTER_EQ("!1 = distinct !{}", MAV1->print(OS, MST));EXPECT_PRINTER_EQ("!0", MAV0->printAsOperand(OS, false, MST));EXPECT_PRINTER_EQ("!1", MAV1->printAsOperand(OS, false, MST));EXPECT_PRINTER_EQ("metadata !0", MAV0->printAsOperand(OS, true, MST));EXPECT_PRINTER_EQ("metadata !1", MAV1->printAsOperand(OS, true, MST));}TEST_F(MDNodeTest, PrintWithDroppedCallOperand) {Module M("test", Context);auto *FTy = FunctionType::get(Type::getVoidTy(Context), false);auto *F0 = Function::Create(FTy, GlobalValue::ExternalLinkage, "F0", &M);auto *F1 = Function::Create(FTy, GlobalValue::ExternalLinkage, "F1", &M);auto *BB0 = BasicBlock::Create(Context, "entry", F0);CallInst *CI0 = CallInst::Create(F1, "", BB0);CI0->dropAllReferences();auto *R0 = ReturnInst::Create(Context, BB0);auto *N0 = MDNode::getDistinct(Context, None);R0->setMetadata("md", N0);// Printing the metadata node would previously result in a failed assertion// due to the call instruction's dropped function operand.ModuleSlotTracker MST(&M);EXPECT_PRINTER_EQ("!0 = distinct !{}", N0->print(OS, MST));}TEST_F(MDNodeTest, PrintTree) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);{DIType *Type = getDerivedType();auto *Var = DILocalVariable::get(Context, Scope, "foo", File,/*LineNo=*/8, Type, /*ArgNo=*/2, Flags,/*Align=*/8, nullptr);std::string Expected;{raw_string_ostream SS(Expected);Var->print(SS);// indent level 1Scope->print((SS << "\n").indent(2));File->print((SS << "\n").indent(2));Type->print((SS << "\n").indent(2));// indent level 2auto *BaseType = cast<DIDerivedType>(Type)->getBaseType();BaseType->print((SS << "\n").indent(4));}EXPECT_PRINTER_EQ(Expected, Var->printTree(OS));}{// Test if printTree works correctly when there is// a cycle in the MDNode and its dependencies.//// We're trying to create type like this:// struct LinkedList {// LinkedList *Head;// };auto *StructTy = cast<DICompositeType>(getCompositeType());DIType *PointerTy = DIDerivedType::getDistinct(Context, dwarf::DW_TAG_pointer_type, "", nullptr, 0, nullptr, StructTy,1, 2, 0, None, DINode::FlagZero);StructTy->replaceElements(MDTuple::get(Context, PointerTy));auto *Var = DILocalVariable::get(Context, Scope, "foo", File,/*LineNo=*/8, StructTy, /*ArgNo=*/2, Flags,/*Align=*/8, nullptr);std::string Expected;{raw_string_ostream SS(Expected);Var->print(SS);// indent level 1Scope->print((SS << "\n").indent(2));File->print((SS << "\n").indent(2));StructTy->print((SS << "\n").indent(2));// indent level 2StructTy->getRawElements()->print((SS << "\n").indent(4));// indent level 3auto Elements = StructTy->getElements();Elements[0]->print((SS << "\n").indent(6));}EXPECT_PRINTER_EQ(Expected, Var->printTree(OS));}}#undef EXPECT_PRINTER_EQTEST_F(MDNodeTest, NullOperand) {// metadata !{}MDNode *Empty = MDNode::get(Context, None);// metadata !{metadata !{}}Metadata *Ops[] = {Empty};MDNode *N = MDNode::get(Context, Ops);ASSERT_EQ(Empty, N->getOperand(0));// metadata !{metadata !{}} => metadata !{null}N->replaceOperandWith(0, nullptr);ASSERT_EQ(nullptr, N->getOperand(0));// metadata !{null}Ops[0] = nullptr;MDNode *NullOp = MDNode::get(Context, Ops);ASSERT_EQ(nullptr, NullOp->getOperand(0));EXPECT_EQ(N, NullOp);}TEST_F(MDNodeTest, DistinctOnUniquingCollision) {// !{}MDNode *Empty = MDNode::get(Context, None);ASSERT_TRUE(Empty->isResolved());EXPECT_FALSE(Empty->isDistinct());// !{!{}}Metadata *Wrapped1Ops[] = {Empty};MDNode *Wrapped1 = MDNode::get(Context, Wrapped1Ops);ASSERT_EQ(Empty, Wrapped1->getOperand(0));ASSERT_TRUE(Wrapped1->isResolved());EXPECT_FALSE(Wrapped1->isDistinct());// !{!{!{}}}Metadata *Wrapped2Ops[] = {Wrapped1};MDNode *Wrapped2 = MDNode::get(Context, Wrapped2Ops);ASSERT_EQ(Wrapped1, Wrapped2->getOperand(0));ASSERT_TRUE(Wrapped2->isResolved());EXPECT_FALSE(Wrapped2->isDistinct());// !{!{!{}}} => !{!{}}Wrapped2->replaceOperandWith(0, Empty);ASSERT_EQ(Empty, Wrapped2->getOperand(0));EXPECT_TRUE(Wrapped2->isDistinct());EXPECT_FALSE(Wrapped1->isDistinct());}TEST_F(MDNodeTest, UniquedOnDeletedOperand) {// temp !{}TempMDTuple T = MDTuple::getTemporary(Context, None);// !{temp !{}}Metadata *Ops[] = {T.get()};MDTuple *N = MDTuple::get(Context, Ops);// !{temp !{}} => !{null}T.reset();ASSERT_TRUE(N->isUniqued());Metadata *NullOps[] = {nullptr};ASSERT_EQ(N, MDTuple::get(Context, NullOps));}TEST_F(MDNodeTest, DistinctOnDeletedValueOperand) {// i1* @GVType *Ty = Type::getInt1PtrTy(Context);std::unique_ptr<GlobalVariable> GV(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));ConstantAsMetadata *Op = ConstantAsMetadata::get(GV.get());// !{i1* @GV}Metadata *Ops[] = {Op};MDTuple *N = MDTuple::get(Context, Ops);// !{i1* @GV} => !{null}GV.reset();ASSERT_TRUE(N->isDistinct());ASSERT_EQ(nullptr, N->getOperand(0));Metadata *NullOps[] = {nullptr};ASSERT_NE(N, MDTuple::get(Context, NullOps));}TEST_F(MDNodeTest, getDistinct) {// !{}MDNode *Empty = MDNode::get(Context, None);ASSERT_TRUE(Empty->isResolved());ASSERT_FALSE(Empty->isDistinct());ASSERT_EQ(Empty, MDNode::get(Context, None));// distinct !{}MDNode *Distinct1 = MDNode::getDistinct(Context, None);MDNode *Distinct2 = MDNode::getDistinct(Context, None);EXPECT_TRUE(Distinct1->isResolved());EXPECT_TRUE(Distinct2->isDistinct());EXPECT_NE(Empty, Distinct1);EXPECT_NE(Empty, Distinct2);EXPECT_NE(Distinct1, Distinct2);// !{}ASSERT_EQ(Empty, MDNode::get(Context, None));}TEST_F(MDNodeTest, isUniqued) {MDNode *U = MDTuple::get(Context, None);MDNode *D = MDTuple::getDistinct(Context, None);auto T = MDTuple::getTemporary(Context, None);EXPECT_TRUE(U->isUniqued());EXPECT_FALSE(D->isUniqued());EXPECT_FALSE(T->isUniqued());}TEST_F(MDNodeTest, isDistinct) {MDNode *U = MDTuple::get(Context, None);MDNode *D = MDTuple::getDistinct(Context, None);auto T = MDTuple::getTemporary(Context, None);EXPECT_FALSE(U->isDistinct());EXPECT_TRUE(D->isDistinct());EXPECT_FALSE(T->isDistinct());}TEST_F(MDNodeTest, isTemporary) {MDNode *U = MDTuple::get(Context, None);MDNode *D = MDTuple::getDistinct(Context, None);auto T = MDTuple::getTemporary(Context, None);EXPECT_FALSE(U->isTemporary());EXPECT_FALSE(D->isTemporary());EXPECT_TRUE(T->isTemporary());}TEST_F(MDNodeTest, getDistinctWithUnresolvedOperands) {// temporary !{}auto Temp = MDTuple::getTemporary(Context, None);ASSERT_FALSE(Temp->isResolved());// distinct !{temporary !{}}Metadata *Ops[] = {Temp.get()};MDNode *Distinct = MDNode::getDistinct(Context, Ops);EXPECT_TRUE(Distinct->isResolved());EXPECT_EQ(Temp.get(), Distinct->getOperand(0));// temporary !{} => !{}MDNode *Empty = MDNode::get(Context, None);Temp->replaceAllUsesWith(Empty);EXPECT_EQ(Empty, Distinct->getOperand(0));}TEST_F(MDNodeTest, handleChangedOperandRecursion) {// !0 = !{}MDNode *N0 = MDNode::get(Context, None);// !1 = !{!3, null}auto Temp3 = MDTuple::getTemporary(Context, None);Metadata *Ops1[] = {Temp3.get(), nullptr};MDNode *N1 = MDNode::get(Context, Ops1);// !2 = !{!3, !0}Metadata *Ops2[] = {Temp3.get(), N0};MDNode *N2 = MDNode::get(Context, Ops2);// !3 = !{!2}Metadata *Ops3[] = {N2};MDNode *N3 = MDNode::get(Context, Ops3);Temp3->replaceAllUsesWith(N3);// !4 = !{!1}Metadata *Ops4[] = {N1};MDNode *N4 = MDNode::get(Context, Ops4);// Confirm that the cycle prevented RAUW from getting dropped.EXPECT_TRUE(N0->isResolved());EXPECT_FALSE(N1->isResolved());EXPECT_FALSE(N2->isResolved());EXPECT_FALSE(N3->isResolved());EXPECT_FALSE(N4->isResolved());// Create a couple of distinct nodes to observe what's going on.//// !5 = distinct !{!2}// !6 = distinct !{!3}Metadata *Ops5[] = {N2};MDNode *N5 = MDNode::getDistinct(Context, Ops5);Metadata *Ops6[] = {N3};MDNode *N6 = MDNode::getDistinct(Context, Ops6);// Mutate !2 to look like !1, causing a uniquing collision (and an RAUW).// This will ripple up, with !3 colliding with !4, and RAUWing. Since !2// references !3, this can cause a re-entry of handleChangedOperand() when !3// is not ready for it.//// !2->replaceOperandWith(1, nullptr)// !2: !{!3, !0} => !{!3, null}// !2->replaceAllUsesWith(!1)// !3: !{!2] => !{!1}// !3->replaceAllUsesWith(!4)N2->replaceOperandWith(1, nullptr);// If all has gone well, N2 and N3 will have been RAUW'ed and deleted from// under us. Just check that the other nodes are sane.//// !1 = !{!4, null}// !4 = !{!1}// !5 = distinct !{!1}// !6 = distinct !{!4}EXPECT_EQ(N4, N1->getOperand(0));EXPECT_EQ(N1, N4->getOperand(0));EXPECT_EQ(N1, N5->getOperand(0));EXPECT_EQ(N4, N6->getOperand(0));}TEST_F(MDNodeTest, replaceResolvedOperand) {// Check code for replacing one resolved operand with another. If doing this// directly (via replaceOperandWith()) becomes illegal, change the operand to// a global value that gets RAUW'ed.//// Use a temporary node to keep N from being resolved.auto Temp = MDTuple::getTemporary(Context, None);Metadata *Ops[] = {nullptr, Temp.get()};MDNode *Empty = MDTuple::get(Context, ArrayRef<Metadata *>());MDNode *N = MDTuple::get(Context, Ops);EXPECT_EQ(nullptr, N->getOperand(0));ASSERT_FALSE(N->isResolved());// Check code for replacing resolved nodes.N->replaceOperandWith(0, Empty);EXPECT_EQ(Empty, N->getOperand(0));// Check code for adding another unresolved operand.N->replaceOperandWith(0, Temp.get());EXPECT_EQ(Temp.get(), N->getOperand(0));// Remove the references to Temp; required for teardown.Temp->replaceAllUsesWith(nullptr);}TEST_F(MDNodeTest, replaceWithUniqued) {auto *Empty = MDTuple::get(Context, None);MDTuple *FirstUniqued;{Metadata *Ops[] = {Empty};auto Temp = MDTuple::getTemporary(Context, Ops);EXPECT_TRUE(Temp->isTemporary());// Don't expect a collision.auto *Current = Temp.get();FirstUniqued = MDNode::replaceWithUniqued(std::move(Temp));EXPECT_TRUE(FirstUniqued->isUniqued());EXPECT_TRUE(FirstUniqued->isResolved());EXPECT_EQ(Current, FirstUniqued);}{Metadata *Ops[] = {Empty};auto Temp = MDTuple::getTemporary(Context, Ops);EXPECT_TRUE(Temp->isTemporary());// Should collide with Uniqued above this time.auto *Uniqued = MDNode::replaceWithUniqued(std::move(Temp));EXPECT_TRUE(Uniqued->isUniqued());EXPECT_TRUE(Uniqued->isResolved());EXPECT_EQ(FirstUniqued, Uniqued);}{auto Unresolved = MDTuple::getTemporary(Context, None);Metadata *Ops[] = {Unresolved.get()};auto Temp = MDTuple::getTemporary(Context, Ops);EXPECT_TRUE(Temp->isTemporary());// Shouldn't be resolved.auto *Uniqued = MDNode::replaceWithUniqued(std::move(Temp));EXPECT_TRUE(Uniqued->isUniqued());EXPECT_FALSE(Uniqued->isResolved());// Should be a different node.EXPECT_NE(FirstUniqued, Uniqued);// Should resolve when we update its node (note: be careful to avoid a// collision with any other nodes above).Uniqued->replaceOperandWith(0, nullptr);EXPECT_TRUE(Uniqued->isResolved());}}TEST_F(MDNodeTest, replaceWithUniquedResolvingOperand) {// temp !{}MDTuple *Op = MDTuple::getTemporary(Context, None).release();EXPECT_FALSE(Op->isResolved());// temp !{temp !{}}Metadata *Ops[] = {Op};MDTuple *N = MDTuple::getTemporary(Context, Ops).release();EXPECT_FALSE(N->isResolved());// temp !{temp !{}} => !{temp !{}}ASSERT_EQ(N, MDNode::replaceWithUniqued(TempMDTuple(N)));EXPECT_FALSE(N->isResolved());// !{temp !{}} => !{!{}}ASSERT_EQ(Op, MDNode::replaceWithUniqued(TempMDTuple(Op)));EXPECT_TRUE(Op->isResolved());EXPECT_TRUE(N->isResolved());}TEST_F(MDNodeTest, replaceWithUniquedDeletedOperand) {// i1* @GVType *Ty = Type::getInt1PtrTy(Context);std::unique_ptr<GlobalVariable> GV(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));ConstantAsMetadata *Op = ConstantAsMetadata::get(GV.get());// temp !{i1* @GV}Metadata *Ops[] = {Op};MDTuple *N = MDTuple::getTemporary(Context, Ops).release();// temp !{i1* @GV} => !{i1* @GV}ASSERT_EQ(N, MDNode::replaceWithUniqued(TempMDTuple(N)));ASSERT_TRUE(N->isUniqued());// !{i1* @GV} => !{null}GV.reset();ASSERT_TRUE(N->isDistinct());ASSERT_EQ(nullptr, N->getOperand(0));Metadata *NullOps[] = {nullptr};ASSERT_NE(N, MDTuple::get(Context, NullOps));}TEST_F(MDNodeTest, replaceWithUniquedChangedOperand) {// i1* @GVType *Ty = Type::getInt1PtrTy(Context);std::unique_ptr<GlobalVariable> GV(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));ConstantAsMetadata *Op = ConstantAsMetadata::get(GV.get());// temp !{i1* @GV}Metadata *Ops[] = {Op};MDTuple *N = MDTuple::getTemporary(Context, Ops).release();// temp !{i1* @GV} => !{i1* @GV}ASSERT_EQ(N, MDNode::replaceWithUniqued(TempMDTuple(N)));ASSERT_TRUE(N->isUniqued());// !{i1* @GV} => !{i1* @GV2}std::unique_ptr<GlobalVariable> GV2(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));GV->replaceAllUsesWith(GV2.get());ASSERT_TRUE(N->isUniqued());Metadata *NullOps[] = {ConstantAsMetadata::get(GV2.get())};ASSERT_EQ(N, MDTuple::get(Context, NullOps));}TEST_F(MDNodeTest, replaceWithDistinct) {{auto *Empty = MDTuple::get(Context, None);Metadata *Ops[] = {Empty};auto Temp = MDTuple::getTemporary(Context, Ops);EXPECT_TRUE(Temp->isTemporary());// Don't expect a collision.auto *Current = Temp.get();auto *Distinct = MDNode::replaceWithDistinct(std::move(Temp));EXPECT_TRUE(Distinct->isDistinct());EXPECT_TRUE(Distinct->isResolved());EXPECT_EQ(Current, Distinct);}{auto Unresolved = MDTuple::getTemporary(Context, None);Metadata *Ops[] = {Unresolved.get()};auto Temp = MDTuple::getTemporary(Context, Ops);EXPECT_TRUE(Temp->isTemporary());// Don't expect a collision.auto *Current = Temp.get();auto *Distinct = MDNode::replaceWithDistinct(std::move(Temp));EXPECT_TRUE(Distinct->isDistinct());EXPECT_TRUE(Distinct->isResolved());EXPECT_EQ(Current, Distinct);// Cleanup; required for teardown.Unresolved->replaceAllUsesWith(nullptr);}}TEST_F(MDNodeTest, replaceWithPermanent) {Metadata *Ops[] = {nullptr};auto Temp = MDTuple::getTemporary(Context, Ops);auto *T = Temp.get();// U is a normal, uniqued node that references T.auto *U = MDTuple::get(Context, T);EXPECT_TRUE(U->isUniqued());// Make Temp self-referencing.Temp->replaceOperandWith(0, T);// Try to uniquify Temp. This should, despite the name in the API, give a// 'distinct' node, since self-references aren't allowed to be uniqued.//// Since it's distinct, N should have the same address as when it was a// temporary (i.e., be equal to T not U).auto *N = MDNode::replaceWithPermanent(std::move(Temp));EXPECT_EQ(N, T);EXPECT_TRUE(N->isDistinct());// U should be the canonical unique node with N as the argument.EXPECT_EQ(U, MDTuple::get(Context, N));EXPECT_TRUE(U->isUniqued());// This temporary should collide with U when replaced, but it should still be// uniqued.EXPECT_EQ(U, MDNode::replaceWithPermanent(MDTuple::getTemporary(Context, N)));EXPECT_TRUE(U->isUniqued());// This temporary should become a new uniqued node.auto Temp2 = MDTuple::getTemporary(Context, U);auto *V = Temp2.get();EXPECT_EQ(V, MDNode::replaceWithPermanent(std::move(Temp2)));EXPECT_TRUE(V->isUniqued());EXPECT_EQ(U, V->getOperand(0));}TEST_F(MDNodeTest, deleteTemporaryWithTrackingRef) {TrackingMDRef Ref;EXPECT_EQ(nullptr, Ref.get());{auto Temp = MDTuple::getTemporary(Context, None);Ref.reset(Temp.get());EXPECT_EQ(Temp.get(), Ref.get());}EXPECT_EQ(nullptr, Ref.get());}typedef MetadataTest DILocationTest;TEST_F(DILocationTest, Overflow) {DISubprogram *N = getSubprogram();{DILocation *L = DILocation::get(Context, 2, 7, N);EXPECT_EQ(2u, L->getLine());EXPECT_EQ(7u, L->getColumn());}unsigned U16 = 1u << 16;{DILocation *L = DILocation::get(Context, UINT32_MAX, U16 - 1, N);EXPECT_EQ(UINT32_MAX, L->getLine());EXPECT_EQ(U16 - 1, L->getColumn());}{DILocation *L = DILocation::get(Context, UINT32_MAX, U16, N);EXPECT_EQ(UINT32_MAX, L->getLine());EXPECT_EQ(0u, L->getColumn());}{DILocation *L = DILocation::get(Context, UINT32_MAX, U16 + 1, N);EXPECT_EQ(UINT32_MAX, L->getLine());EXPECT_EQ(0u, L->getColumn());}}TEST_F(DILocationTest, Merge) {DISubprogram *N = getSubprogram();DIScope *S = DILexicalBlock::get(Context, N, getFile(), 3, 4);{// Identical.auto *A = DILocation::get(Context, 2, 7, N);auto *B = DILocation::get(Context, 2, 7, N);auto *M = DILocation::getMergedLocation(A, B);EXPECT_EQ(2u, M->getLine());EXPECT_EQ(7u, M->getColumn());EXPECT_EQ(N, M->getScope());}{// Identical, different scopes.auto *A = DILocation::get(Context, 2, 7, N);auto *B = DILocation::get(Context, 2, 7, S);auto *M = DILocation::getMergedLocation(A, B);EXPECT_EQ(0u, M->getLine()); // FIXME: Should this be 2?EXPECT_EQ(0u, M->getColumn()); // FIXME: Should this be 7?EXPECT_EQ(N, M->getScope());}{// Different lines, same scopes.auto *A = DILocation::get(Context, 1, 6, N);auto *B = DILocation::get(Context, 2, 7, N);auto *M = DILocation::getMergedLocation(A, B);EXPECT_EQ(0u, M->getLine());EXPECT_EQ(0u, M->getColumn());EXPECT_EQ(N, M->getScope());}{// Twisty locations, all different, same function.auto *A = DILocation::get(Context, 1, 6, N);auto *B = DILocation::get(Context, 2, 7, S);auto *M = DILocation::getMergedLocation(A, B);EXPECT_EQ(0u, M->getLine());EXPECT_EQ(0u, M->getColumn());EXPECT_EQ(N, M->getScope());}{// Different function, same inlined-at.auto *F = getFile();auto *SP1 = DISubprogram::getDistinct(Context, F, "a", "a", F, 0, nullptr,0, nullptr, 0, 0, DINode::FlagZero,DISubprogram::SPFlagZero, nullptr);auto *SP2 = DISubprogram::getDistinct(Context, F, "b", "b", F, 0, nullptr,0, nullptr, 0, 0, DINode::FlagZero,DISubprogram::SPFlagZero, nullptr);auto *I = DILocation::get(Context, 2, 7, N);auto *A = DILocation::get(Context, 1, 6, SP1, I);auto *B = DILocation::get(Context, 2, 7, SP2, I);auto *M = DILocation::getMergedLocation(A, B);EXPECT_EQ(0u, M->getLine());EXPECT_EQ(0u, M->getColumn());EXPECT_TRUE(isa<DILocalScope>(M->getScope()));EXPECT_EQ(I, M->getInlinedAt());}{// Completely different.auto *I = DILocation::get(Context, 2, 7, N);auto *A = DILocation::get(Context, 1, 6, S, I);auto *B = DILocation::get(Context, 2, 7, getSubprogram());auto *M = DILocation::getMergedLocation(A, B);EXPECT_EQ(0u, M->getLine());EXPECT_EQ(0u, M->getColumn());EXPECT_TRUE(isa<DILocalScope>(M->getScope()));EXPECT_EQ(S, M->getScope());EXPECT_EQ(nullptr, M->getInlinedAt());}}TEST_F(DILocationTest, getDistinct) {MDNode *N = getSubprogram();DILocation *L0 = DILocation::getDistinct(Context, 2, 7, N);EXPECT_TRUE(L0->isDistinct());DILocation *L1 = DILocation::get(Context, 2, 7, N);EXPECT_FALSE(L1->isDistinct());EXPECT_EQ(L1, DILocation::get(Context, 2, 7, N));}TEST_F(DILocationTest, getTemporary) {MDNode *N = MDNode::get(Context, None);auto L = DILocation::getTemporary(Context, 2, 7, N);EXPECT_TRUE(L->isTemporary());EXPECT_FALSE(L->isResolved());}TEST_F(DILocationTest, cloneTemporary) {MDNode *N = MDNode::get(Context, None);auto L = DILocation::getTemporary(Context, 2, 7, N);EXPECT_TRUE(L->isTemporary());auto L2 = L->clone();EXPECT_TRUE(L2->isTemporary());}TEST_F(DILocationTest, discriminatorEncoding) {EXPECT_EQ(0U, DILocation::encodeDiscriminator(0, 0, 0).value());// Encode base discriminator as a component: lsb is 0, then the value.// The other components are all absent, so we leave all the other bits 0.EXPECT_EQ(2U, DILocation::encodeDiscriminator(1, 0, 0).value());// Base discriminator component is empty, so lsb is 1. Next component is not// empty, so its lsb is 0, then its value (1). Next component is empty.// So the bit pattern is 101.EXPECT_EQ(5U, DILocation::encodeDiscriminator(0, 1, 0).value());// First 2 components are empty, so the bit pattern is 11. Then the// next component - ending up with 1011.EXPECT_EQ(0xbU, DILocation::encodeDiscriminator(0, 0, 1).value());// The bit pattern for the first 2 components is 11. The next bit is 0,// because the last component is not empty. We have 29 bits usable for// encoding, but we cap it at 12 bits uniformously for all components. We// encode the last component over 14 bits.EXPECT_EQ(0xfffbU, DILocation::encodeDiscriminator(0, 0, 0xfff).value());EXPECT_EQ(0x102U, DILocation::encodeDiscriminator(1, 1, 0).value());EXPECT_EQ(0x13eU, DILocation::encodeDiscriminator(0x1f, 1, 0).value());EXPECT_EQ(0x87feU, DILocation::encodeDiscriminator(0x1ff, 1, 0).value());EXPECT_EQ(0x1f3eU, DILocation::encodeDiscriminator(0x1f, 0x1f, 0).value());EXPECT_EQ(0x3ff3eU, DILocation::encodeDiscriminator(0x1f, 0x1ff, 0).value());EXPECT_EQ(0x1ff87feU,DILocation::encodeDiscriminator(0x1ff, 0x1ff, 0).value());EXPECT_EQ(0xfff9f3eU,DILocation::encodeDiscriminator(0x1f, 0x1f, 0xfff).value());EXPECT_EQ(0xffc3ff3eU,DILocation::encodeDiscriminator(0x1f, 0x1ff, 0x1ff).value());EXPECT_EQ(0xffcf87feU,DILocation::encodeDiscriminator(0x1ff, 0x1f, 0x1ff).value());EXPECT_EQ(0xe1ff87feU,DILocation::encodeDiscriminator(0x1ff, 0x1ff, 7).value());}TEST_F(DILocationTest, discriminatorEncodingNegativeTests) {EXPECT_EQ(None, DILocation::encodeDiscriminator(0, 0, 0x1000));EXPECT_EQ(None, DILocation::encodeDiscriminator(0x1000, 0, 0));EXPECT_EQ(None, DILocation::encodeDiscriminator(0, 0x1000, 0));EXPECT_EQ(None, DILocation::encodeDiscriminator(0, 0, 0x1000));EXPECT_EQ(None, DILocation::encodeDiscriminator(0x1ff, 0x1ff, 8));EXPECT_EQ(None,DILocation::encodeDiscriminator(std::numeric_limits<uint32_t>::max(),std::numeric_limits<uint32_t>::max(),0));}TEST_F(DILocationTest, discriminatorSpecialCases) {// We don't test getCopyIdentifier here because the only way// to set it is by constructing an encoded discriminator using// encodeDiscriminator, which is already tested.auto L1 = DILocation::get(Context, 1, 2, getSubprogram());EXPECT_EQ(0U, L1->getBaseDiscriminator());EXPECT_EQ(1U, L1->getDuplicationFactor());EXPECT_EQ(L1, L1->cloneWithBaseDiscriminator(0).value());EXPECT_EQ(L1, L1->cloneByMultiplyingDuplicationFactor(0).value());EXPECT_EQ(L1, L1->cloneByMultiplyingDuplicationFactor(1).value());auto L2 = L1->cloneWithBaseDiscriminator(1).value();EXPECT_EQ(0U, L1->getBaseDiscriminator());EXPECT_EQ(1U, L1->getDuplicationFactor());EXPECT_EQ(1U, L2->getBaseDiscriminator());EXPECT_EQ(1U, L2->getDuplicationFactor());auto L3 = L2->cloneByMultiplyingDuplicationFactor(2).value();EXPECT_EQ(1U, L3->getBaseDiscriminator());EXPECT_EQ(2U, L3->getDuplicationFactor());EXPECT_EQ(L2, L2->cloneByMultiplyingDuplicationFactor(1).value());auto L4 = L3->cloneByMultiplyingDuplicationFactor(4).value();EXPECT_EQ(1U, L4->getBaseDiscriminator());EXPECT_EQ(8U, L4->getDuplicationFactor());auto L5 = L4->cloneWithBaseDiscriminator(2).value();EXPECT_EQ(2U, L5->getBaseDiscriminator());EXPECT_EQ(8U, L5->getDuplicationFactor());// Check extreme casesauto L6 = L1->cloneWithBaseDiscriminator(0xfff).value();EXPECT_EQ(0xfffU, L6->getBaseDiscriminator());EXPECT_EQ(0xfffU, L6->cloneByMultiplyingDuplicationFactor(0xfff).value()->getDuplicationFactor());// Check we return None for unencodable cases.EXPECT_EQ(None, L4->cloneWithBaseDiscriminator(0x1000));EXPECT_EQ(None, L4->cloneByMultiplyingDuplicationFactor(0x1000));}typedef MetadataTest GenericDINodeTest;TEST_F(GenericDINodeTest, get) {StringRef Header = "header";auto *Empty = MDNode::get(Context, None);Metadata *Ops1[] = {Empty};auto *N = GenericDINode::get(Context, 15, Header, Ops1);EXPECT_EQ(15u, N->getTag());EXPECT_EQ(2u, N->getNumOperands());EXPECT_EQ(Header, N->getHeader());EXPECT_EQ(MDString::get(Context, Header), N->getOperand(0));EXPECT_EQ(1u, N->getNumDwarfOperands());EXPECT_EQ(Empty, N->getDwarfOperand(0));EXPECT_EQ(Empty, N->getOperand(1));ASSERT_TRUE(N->isUniqued());EXPECT_EQ(N, GenericDINode::get(Context, 15, Header, Ops1));N->replaceOperandWith(1, nullptr);EXPECT_EQ(15u, N->getTag());EXPECT_EQ(Header, N->getHeader());EXPECT_EQ(nullptr, N->getDwarfOperand(0));ASSERT_TRUE(N->isUniqued());Metadata *Ops2[] = {nullptr};EXPECT_EQ(N, GenericDINode::get(Context, 15, Header, Ops2));N->replaceDwarfOperandWith(0, Empty);EXPECT_EQ(15u, N->getTag());EXPECT_EQ(Header, N->getHeader());EXPECT_EQ(Empty, N->getDwarfOperand(0));ASSERT_TRUE(N->isUniqued());EXPECT_EQ(N, GenericDINode::get(Context, 15, Header, Ops1));TempGenericDINode Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(GenericDINodeTest, getEmptyHeader) {// Canonicalize !"" to null.auto *N = GenericDINode::get(Context, 15, StringRef(), None);EXPECT_EQ(StringRef(), N->getHeader());EXPECT_EQ(nullptr, N->getOperand(0));}typedef MetadataTest DISubrangeTest;TEST_F(DISubrangeTest, get) {auto *N = DISubrange::get(Context, 5, 7);auto Count = N->getCount();auto Lower = N->getLowerBound();EXPECT_EQ(dwarf::DW_TAG_subrange_type, N->getTag());ASSERT_TRUE(Count);ASSERT_TRUE(Count.is<ConstantInt*>());EXPECT_EQ(5, Count.get<ConstantInt*>()->getSExtValue());EXPECT_EQ(7, Lower.get<ConstantInt *>()->getSExtValue());EXPECT_EQ(N, DISubrange::get(Context, 5, 7));EXPECT_EQ(DISubrange::get(Context, 5, 0), DISubrange::get(Context, 5));TempDISubrange Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DISubrangeTest, getEmptyArray) {auto *N = DISubrange::get(Context, -1, 0);auto Count = N->getCount();auto Lower = N->getLowerBound();EXPECT_EQ(dwarf::DW_TAG_subrange_type, N->getTag());ASSERT_TRUE(Count);ASSERT_TRUE(Count.is<ConstantInt*>());EXPECT_EQ(-1, Count.get<ConstantInt*>()->getSExtValue());EXPECT_EQ(0, Lower.get<ConstantInt *>()->getSExtValue());EXPECT_EQ(N, DISubrange::get(Context, -1, 0));}TEST_F(DISubrangeTest, getVariableCount) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);auto *VlaExpr = DILocalVariable::get(Context, Scope, "vla_expr", File, 8,Type, 2, Flags, 8, nullptr);auto *N = DISubrange::get(Context, VlaExpr, 0);auto Count = N->getCount();auto Lower = N->getLowerBound();ASSERT_TRUE(Count);ASSERT_TRUE(Count.is<DIVariable*>());EXPECT_EQ(VlaExpr, Count.get<DIVariable*>());ASSERT_TRUE(isa<DIVariable>(N->getRawCountNode()));EXPECT_EQ(0, Lower.get<ConstantInt *>()->getSExtValue());EXPECT_EQ("vla_expr", Count.get<DIVariable*>()->getName());EXPECT_EQ(N, DISubrange::get(Context, VlaExpr, 0));}TEST_F(DISubrangeTest, fortranAllocatableInt) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);auto *LI = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), -10));auto *UI = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), 10));auto *SI = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), 4));auto *UIother = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), 20));auto *UVother = DILocalVariable::get(Context, Scope, "ubother", File, 8, Type,2, Flags, 8, nullptr);auto *UEother = DIExpression::get(Context, {5, 6});auto *LIZero = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), 0));auto *UIZero = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), 0));auto *N = DISubrange::get(Context, nullptr, LI, UI, SI);auto Lower = N->getLowerBound();ASSERT_TRUE(Lower);ASSERT_TRUE(Lower.is<ConstantInt *>());EXPECT_EQ(cast<ConstantInt>(LI->getValue()), Lower.get<ConstantInt *>());auto Upper = N->getUpperBound();ASSERT_TRUE(Upper);ASSERT_TRUE(Upper.is<ConstantInt *>());EXPECT_EQ(cast<ConstantInt>(UI->getValue()), Upper.get<ConstantInt *>());auto Stride = N->getStride();ASSERT_TRUE(Stride);ASSERT_TRUE(Stride.is<ConstantInt *>());EXPECT_EQ(cast<ConstantInt>(SI->getValue()), Stride.get<ConstantInt *>());EXPECT_EQ(N, DISubrange::get(Context, nullptr, LI, UI, SI));EXPECT_NE(N, DISubrange::get(Context, nullptr, LI, UIother, SI));EXPECT_NE(N, DISubrange::get(Context, nullptr, LI, UEother, SI));EXPECT_NE(N, DISubrange::get(Context, nullptr, LI, UVother, SI));auto *NZeroLower = DISubrange::get(Context, nullptr, LIZero, UI, SI);EXPECT_NE(NZeroLower, DISubrange::get(Context, nullptr, nullptr, UI, SI));auto *NZeroUpper = DISubrange::get(Context, nullptr, LI, UIZero, SI);EXPECT_NE(NZeroUpper, DISubrange::get(Context, nullptr, LI, nullptr, SI));}TEST_F(DISubrangeTest, fortranAllocatableVar) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);auto *LV =DILocalVariable::get(Context, Scope, "lb", File, 8, Type, 2, Flags, 8,nullptr);auto *UV =DILocalVariable::get(Context, Scope, "ub", File, 8, Type, 2, Flags, 8,nullptr);auto *SV =DILocalVariable::get(Context, Scope, "st", File, 8, Type, 2, Flags, 8,nullptr);auto *SVother = DILocalVariable::get(Context, Scope, "stother", File, 8, Type,2, Flags, 8, nullptr);auto *SIother = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), 20));auto *SEother = DIExpression::get(Context, {5, 6});auto *N = DISubrange::get(Context, nullptr, LV, UV, SV);auto Lower = N->getLowerBound();ASSERT_TRUE(Lower);ASSERT_TRUE(Lower.is<DIVariable *>());EXPECT_EQ(LV, Lower.get<DIVariable *>());auto Upper = N->getUpperBound();ASSERT_TRUE(Upper);ASSERT_TRUE(Upper.is<DIVariable *>());EXPECT_EQ(UV, Upper.get<DIVariable *>());auto Stride = N->getStride();ASSERT_TRUE(Stride);ASSERT_TRUE(Stride.is<DIVariable *>());EXPECT_EQ(SV, Stride.get<DIVariable *>());EXPECT_EQ(N, DISubrange::get(Context, nullptr, LV, UV, SV));EXPECT_NE(N, DISubrange::get(Context, nullptr, LV, UV, SVother));EXPECT_NE(N, DISubrange::get(Context, nullptr, LV, UV, SEother));EXPECT_NE(N, DISubrange::get(Context, nullptr, LV, UV, SIother));}TEST_F(DISubrangeTest, fortranAllocatableExpr) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);auto *LE = DIExpression::get(Context, {1, 2});auto *UE = DIExpression::get(Context, {2, 3});auto *SE = DIExpression::get(Context, {3, 4});auto *LEother = DIExpression::get(Context, {5, 6});auto *LIother = ConstantAsMetadata::get(ConstantInt::getSigned(Type::getInt64Ty(Context), 20));auto *LVother = DILocalVariable::get(Context, Scope, "lbother", File, 8, Type,2, Flags, 8, nullptr);auto *N = DISubrange::get(Context, nullptr, LE, UE, SE);auto Lower = N->getLowerBound();ASSERT_TRUE(Lower);ASSERT_TRUE(Lower.is<DIExpression *>());EXPECT_EQ(LE, Lower.get<DIExpression *>());auto Upper = N->getUpperBound();ASSERT_TRUE(Upper);ASSERT_TRUE(Upper.is<DIExpression *>());EXPECT_EQ(UE, Upper.get<DIExpression *>());auto Stride = N->getStride();ASSERT_TRUE(Stride);ASSERT_TRUE(Stride.is<DIExpression *>());EXPECT_EQ(SE, Stride.get<DIExpression *>());EXPECT_EQ(N, DISubrange::get(Context, nullptr, LE, UE, SE));EXPECT_NE(N, DISubrange::get(Context, nullptr, LEother, UE, SE));EXPECT_NE(N, DISubrange::get(Context, nullptr, LIother, UE, SE));EXPECT_NE(N, DISubrange::get(Context, nullptr, LVother, UE, SE));}typedef MetadataTest DIGenericSubrangeTest;TEST_F(DIGenericSubrangeTest, fortranAssumedRankInt) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);auto *LI = DIExpression::get(Context, {dwarf::DW_OP_consts, static_cast<uint64_t>(-10)});auto *UI = DIExpression::get(Context, {dwarf::DW_OP_consts, 10});auto *SI = DIExpression::get(Context, {dwarf::DW_OP_consts, 4});auto *UIother = DIExpression::get(Context, {dwarf::DW_OP_consts, 20});auto *UVother = DILocalVariable::get(Context, Scope, "ubother", File, 8, Type,2, Flags, 8, nullptr);auto *UEother = DIExpression::get(Context, {5, 6});auto *LIZero = DIExpression::get(Context, {dwarf::DW_OP_consts, 0});auto *UIZero = DIExpression::get(Context, {dwarf::DW_OP_consts, 0});auto *N = DIGenericSubrange::get(Context, nullptr, LI, UI, SI);auto Lower = N->getLowerBound();ASSERT_TRUE(Lower);ASSERT_TRUE(Lower.is<DIExpression *>());EXPECT_EQ(dyn_cast_or_null<DIExpression>(LI), Lower.get<DIExpression *>());auto Upper = N->getUpperBound();ASSERT_TRUE(Upper);ASSERT_TRUE(Upper.is<DIExpression *>());EXPECT_EQ(dyn_cast_or_null<DIExpression>(UI), Upper.get<DIExpression *>());auto Stride = N->getStride();ASSERT_TRUE(Stride);ASSERT_TRUE(Stride.is<DIExpression *>());EXPECT_EQ(dyn_cast_or_null<DIExpression>(SI), Stride.get<DIExpression *>());EXPECT_EQ(N, DIGenericSubrange::get(Context, nullptr, LI, UI, SI));EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LI, UIother, SI));EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LI, UEother, SI));EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LI, UVother, SI));auto *NZeroLower = DIGenericSubrange::get(Context, nullptr, LIZero, UI, SI);EXPECT_NE(NZeroLower,DIGenericSubrange::get(Context, nullptr, nullptr, UI, SI));auto *NZeroUpper = DIGenericSubrange::get(Context, nullptr, LI, UIZero, SI);EXPECT_NE(NZeroUpper,DIGenericSubrange::get(Context, nullptr, LI, nullptr, SI));}TEST_F(DIGenericSubrangeTest, fortranAssumedRankVar) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);auto *LV =DILocalVariable::get(Context, Scope, "lb", File, 8, Type, 2, Flags, 8,nullptr);auto *UV =DILocalVariable::get(Context, Scope, "ub", File, 8, Type, 2, Flags, 8,nullptr);auto *SV =DILocalVariable::get(Context, Scope, "st", File, 8, Type, 2, Flags, 8,nullptr);auto *SVother = DILocalVariable::get(Context, Scope, "stother", File, 8, Type,2, Flags, 8, nullptr);auto *SIother = DIExpression::get(Context, {dwarf::DW_OP_consts, static_cast<uint64_t>(-1)});auto *SEother = DIExpression::get(Context, {5, 6});auto *N = DIGenericSubrange::get(Context, nullptr, LV, UV, SV);auto Lower = N->getLowerBound();ASSERT_TRUE(Lower);ASSERT_TRUE(Lower.is<DIVariable *>());EXPECT_EQ(LV, Lower.get<DIVariable *>());auto Upper = N->getUpperBound();ASSERT_TRUE(Upper);ASSERT_TRUE(Upper.is<DIVariable *>());EXPECT_EQ(UV, Upper.get<DIVariable *>());auto Stride = N->getStride();ASSERT_TRUE(Stride);ASSERT_TRUE(Stride.is<DIVariable *>());EXPECT_EQ(SV, Stride.get<DIVariable *>());EXPECT_EQ(N, DIGenericSubrange::get(Context, nullptr, LV, UV, SV));EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LV, UV, SVother));EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LV, UV, SEother));EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LV, UV, SIother));}TEST_F(DIGenericSubrangeTest, useDIBuilder) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);auto *LV =DILocalVariable::get(Context, Scope, "lb", File, 8, Type, 2, Flags, 8, nullptr);auto *UE = DIExpression::get(Context, {2, 3});auto *SE = DIExpression::get(Context, {3, 4});auto *LVother = DILocalVariable::get(Context, Scope, "lbother", File, 8, Type,2, Flags, 8, nullptr);auto *LIother = DIExpression::get(Context, {dwarf::DW_OP_consts, static_cast<uint64_t>(-1)});Module M("M", Context);DIBuilder DIB(M);auto *N = DIB.getOrCreateGenericSubrange(DIGenericSubrange::BoundType(nullptr), DIGenericSubrange::BoundType(LV),DIGenericSubrange::BoundType(UE), DIGenericSubrange::BoundType(SE));auto Lower = N->getLowerBound();ASSERT_TRUE(Lower);ASSERT_TRUE(Lower.is<DIVariable *>());EXPECT_EQ(LV, Lower.get<DIVariable *>());auto Upper = N->getUpperBound();ASSERT_TRUE(Upper);ASSERT_TRUE(Upper.is<DIExpression *>());EXPECT_EQ(UE, Upper.get<DIExpression *>());auto Stride = N->getStride();ASSERT_TRUE(Stride);ASSERT_TRUE(Stride.is<DIExpression *>());EXPECT_EQ(SE, Stride.get<DIExpression *>());EXPECT_EQ(N, DIB.getOrCreateGenericSubrange(DIGenericSubrange::BoundType(nullptr),DIGenericSubrange::BoundType(LV),DIGenericSubrange::BoundType(UE),DIGenericSubrange::BoundType(SE)));EXPECT_NE(N, DIB.getOrCreateGenericSubrange(DIGenericSubrange::BoundType(nullptr),DIGenericSubrange::BoundType(LVother),DIGenericSubrange::BoundType(UE),DIGenericSubrange::BoundType(SE)));EXPECT_NE(N, DIB.getOrCreateGenericSubrange(DIGenericSubrange::BoundType(nullptr),DIGenericSubrange::BoundType(LIother),DIGenericSubrange::BoundType(UE),DIGenericSubrange::BoundType(SE)));}typedef MetadataTest DIEnumeratorTest;TEST_F(DIEnumeratorTest, get) {auto *N = DIEnumerator::get(Context, 7, false, "name");EXPECT_EQ(dwarf::DW_TAG_enumerator, N->getTag());EXPECT_EQ(7, N->getValue().getSExtValue());EXPECT_FALSE(N->isUnsigned());EXPECT_EQ("name", N->getName());EXPECT_EQ(N, DIEnumerator::get(Context, 7, false, "name"));EXPECT_NE(N, DIEnumerator::get(Context, 7, true, "name"));EXPECT_NE(N, DIEnumerator::get(Context, 8, false, "name"));EXPECT_NE(N, DIEnumerator::get(Context, 7, false, "nam"));TempDIEnumerator Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DIEnumeratorTest, getWithLargeValues) {auto *N = DIEnumerator::get(Context, APInt::getMaxValue(128), false, "val");EXPECT_EQ(128U, N->getValue().countPopulation());EXPECT_EQ(N,DIEnumerator::get(Context, APInt::getMaxValue(128), false, "val"));EXPECT_NE(N,DIEnumerator::get(Context, APInt::getMinValue(128), false, "val"));}typedef MetadataTest DIBasicTypeTest;TEST_F(DIBasicTypeTest, get) {auto *N =DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", 33, 26, 7,DINode::FlagZero);EXPECT_EQ(dwarf::DW_TAG_base_type, N->getTag());EXPECT_EQ("special", N->getName());EXPECT_EQ(33u, N->getSizeInBits());EXPECT_EQ(26u, N->getAlignInBits());EXPECT_EQ(7u, N->getEncoding());EXPECT_EQ(0u, N->getLine());EXPECT_EQ(DINode::FlagZero, N->getFlags());EXPECT_EQ(N, DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", 33,26, 7, DINode::FlagZero));EXPECT_NE(N, DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type,"special", 33, 26, 7, DINode::FlagZero));EXPECT_NE(N,DIBasicType::get(Context, dwarf::DW_TAG_base_type, "s", 33, 26, 7,DINode::FlagZero));EXPECT_NE(N, DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", 32,26, 7, DINode::FlagZero));EXPECT_NE(N, DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", 33,25, 7, DINode::FlagZero));EXPECT_NE(N, DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", 33,26, 6, DINode::FlagZero));EXPECT_NE(N, DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", 33,26, 7, DINode::FlagBigEndian));EXPECT_NE(N, DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special", 33,26, 7, DINode::FlagLittleEndian));TempDIBasicType Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DIBasicTypeTest, getWithLargeValues) {auto *N = DIBasicType::get(Context, dwarf::DW_TAG_base_type, "special",UINT64_MAX, UINT32_MAX - 1, 7, DINode::FlagZero);EXPECT_EQ(UINT64_MAX, N->getSizeInBits());EXPECT_EQ(UINT32_MAX - 1, N->getAlignInBits());}TEST_F(DIBasicTypeTest, getUnspecified) {auto *N =DIBasicType::get(Context, dwarf::DW_TAG_unspecified_type, "unspecified");EXPECT_EQ(dwarf::DW_TAG_unspecified_type, N->getTag());EXPECT_EQ("unspecified", N->getName());EXPECT_EQ(0u, N->getSizeInBits());EXPECT_EQ(0u, N->getAlignInBits());EXPECT_EQ(0u, N->getEncoding());EXPECT_EQ(0u, N->getLine());EXPECT_EQ(DINode::FlagZero, N->getFlags());}typedef MetadataTest DITypeTest;TEST_F(DITypeTest, clone) {// Check that DIType has a specialized clone that returns TempDIType.DIType *N = DIBasicType::get(Context, dwarf::DW_TAG_base_type, "int", 32, 32,dwarf::DW_ATE_signed, DINode::FlagZero);TempDIType Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DITypeTest, cloneWithFlags) {// void (void)Metadata *TypesOps[] = {nullptr};Metadata *Types = MDTuple::get(Context, TypesOps);DIType *D =DISubroutineType::getDistinct(Context, DINode::FlagZero, 0, Types);EXPECT_EQ(DINode::FlagZero, D->getFlags());TempDIType D2 = D->cloneWithFlags(DINode::FlagRValueReference);EXPECT_EQ(DINode::FlagRValueReference, D2->getFlags());EXPECT_EQ(DINode::FlagZero, D->getFlags());TempDIType T =DISubroutineType::getTemporary(Context, DINode::FlagZero, 0, Types);EXPECT_EQ(DINode::FlagZero, T->getFlags());TempDIType T2 = T->cloneWithFlags(DINode::FlagRValueReference);EXPECT_EQ(DINode::FlagRValueReference, T2->getFlags());EXPECT_EQ(DINode::FlagZero, T->getFlags());}typedef MetadataTest DIDerivedTypeTest;TEST_F(DIDerivedTypeTest, get) {DIFile *File = getFile();DIScope *Scope = getSubprogram();DIType *BaseType = getBasicType("basic");MDTuple *ExtraData = getTuple();unsigned DWARFAddressSpace = 8;DINode::DIFlags Flags5 = static_cast<DINode::DIFlags>(5);DINode::DIFlags Flags4 = static_cast<DINode::DIFlags>(4);auto *N =DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File,1, Scope, BaseType, 2, 3, 4, DWARFAddressSpace, Flags5,ExtraData);EXPECT_EQ(dwarf::DW_TAG_pointer_type, N->getTag());EXPECT_EQ("something", N->getName());EXPECT_EQ(File, N->getFile());EXPECT_EQ(1u, N->getLine());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(BaseType, N->getBaseType());EXPECT_EQ(2u, N->getSizeInBits());EXPECT_EQ(3u, N->getAlignInBits());EXPECT_EQ(4u, N->getOffsetInBits());EXPECT_EQ(DWARFAddressSpace, *N->getDWARFAddressSpace());EXPECT_EQ(5u, N->getFlags());EXPECT_EQ(ExtraData, N->getExtraData());EXPECT_EQ(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, Scope, BaseType, 2, 3,4, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_reference_type,"something", File, 1, Scope, BaseType, 2, 3,4, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "else",File, 1, Scope, BaseType, 2, 3,4, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", getFile(), 1, Scope, BaseType, 2,3, 4, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 2, Scope, BaseType, 2, 3,4, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, getSubprogram(),BaseType, 2, 3, 4, DWARFAddressSpace, Flags5,ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1,Scope, getBasicType("basic2"), 2, 3, 4, DWARFAddressSpace,Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, Scope, BaseType, 3, 3,4, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, Scope, BaseType, 2, 2,4, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, Scope, BaseType, 2, 3,5, DWARFAddressSpace, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, Scope, BaseType, 2, 3,4, DWARFAddressSpace + 1, Flags5, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, Scope, BaseType, 2, 3,4, DWARFAddressSpace, Flags4, ExtraData));EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type,"something", File, 1, Scope, BaseType, 2, 3,4, DWARFAddressSpace, Flags5, getTuple()));TempDIDerivedType Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DIDerivedTypeTest, getWithLargeValues) {DIFile *File = getFile();DIScope *Scope = getSubprogram();DIType *BaseType = getBasicType("basic");MDTuple *ExtraData = getTuple();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5);auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope,BaseType, UINT64_MAX, UINT32_MAX - 1, UINT64_MAX - 2, UINT32_MAX - 3,Flags, ExtraData);EXPECT_EQ(UINT64_MAX, N->getSizeInBits());EXPECT_EQ(UINT32_MAX - 1, N->getAlignInBits());EXPECT_EQ(UINT64_MAX - 2, N->getOffsetInBits());EXPECT_EQ(UINT32_MAX - 3, *N->getDWARFAddressSpace());}typedef MetadataTest DICompositeTypeTest;TEST_F(DICompositeTypeTest, get) {unsigned Tag = dwarf::DW_TAG_structure_type;StringRef Name = "some name";DIFile *File = getFile();unsigned Line = 1;DIScope *Scope = getSubprogram();DIType *BaseType = getCompositeType();uint64_t SizeInBits = 2;uint32_t AlignInBits = 3;uint64_t OffsetInBits = 4;DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5);MDTuple *Elements = getTuple();unsigned RuntimeLang = 6;DIType *VTableHolder = getCompositeType();MDTuple *TemplateParams = getTuple();StringRef Identifier = "some id";auto *N = DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier);EXPECT_EQ(Tag, N->getTag());EXPECT_EQ(Name, N->getName());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Line, N->getLine());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(BaseType, N->getBaseType());EXPECT_EQ(SizeInBits, N->getSizeInBits());EXPECT_EQ(AlignInBits, N->getAlignInBits());EXPECT_EQ(OffsetInBits, N->getOffsetInBits());EXPECT_EQ(Flags, N->getFlags());EXPECT_EQ(Elements, N->getElements().get());EXPECT_EQ(RuntimeLang, N->getRuntimeLang());EXPECT_EQ(VTableHolder, N->getVTableHolder());EXPECT_EQ(TemplateParams, N->getTemplateParams().get());EXPECT_EQ(Identifier, N->getIdentifier());EXPECT_EQ(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag + 1, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, "abc", File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, getFile(), Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line + 1, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, getSubprogram(), BaseType,SizeInBits, AlignInBits, OffsetInBits, Flags, Elements,RuntimeLang, VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope, getBasicType("other"),SizeInBits, AlignInBits, OffsetInBits, Flags, Elements,RuntimeLang, VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits + 1, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits + 1,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits,AlignInBits, OffsetInBits + 1, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));DINode::DIFlags FlagsPOne = static_cast<DINode::DIFlags>(Flags + 1);EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits,AlignInBits, OffsetInBits, FlagsPOne, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits,AlignInBits, OffsetInBits, Flags, getTuple(), RuntimeLang,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits,AlignInBits, OffsetInBits, Flags, Elements, RuntimeLang + 1,VTableHolder, TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits,AlignInBits, OffsetInBits, Flags, Elements, RuntimeLang,getCompositeType(), TemplateParams, Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, getTuple(), Identifier));EXPECT_NE(N, DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, "other"));// Be sure that missing identifiers get null pointers.EXPECT_FALSE(DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, "")->getRawIdentifier());EXPECT_FALSE(DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams)->getRawIdentifier());TempDICompositeType Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DICompositeTypeTest, getWithLargeValues) {unsigned Tag = dwarf::DW_TAG_structure_type;StringRef Name = "some name";DIFile *File = getFile();unsigned Line = 1;DIScope *Scope = getSubprogram();DIType *BaseType = getCompositeType();uint64_t SizeInBits = UINT64_MAX;uint32_t AlignInBits = UINT32_MAX - 1;uint64_t OffsetInBits = UINT64_MAX - 2;DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5);MDTuple *Elements = getTuple();unsigned RuntimeLang = 6;DIType *VTableHolder = getCompositeType();MDTuple *TemplateParams = getTuple();StringRef Identifier = "some id";auto *N = DICompositeType::get(Context, Tag, Name, File, Line, Scope,BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, Elements, RuntimeLang,VTableHolder, TemplateParams, Identifier);EXPECT_EQ(SizeInBits, N->getSizeInBits());EXPECT_EQ(AlignInBits, N->getAlignInBits());EXPECT_EQ(OffsetInBits, N->getOffsetInBits());}TEST_F(DICompositeTypeTest, replaceOperands) {unsigned Tag = dwarf::DW_TAG_structure_type;StringRef Name = "some name";DIFile *File = getFile();unsigned Line = 1;DIScope *Scope = getSubprogram();DIType *BaseType = getCompositeType();uint64_t SizeInBits = 2;uint32_t AlignInBits = 3;uint64_t OffsetInBits = 4;DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5);unsigned RuntimeLang = 6;StringRef Identifier = "some id";auto *N = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier);auto *Elements = MDTuple::getDistinct(Context, None);EXPECT_EQ(nullptr, N->getElements().get());N->replaceElements(Elements);EXPECT_EQ(Elements, N->getElements().get());N->replaceElements(nullptr);EXPECT_EQ(nullptr, N->getElements().get());DIType *VTableHolder = getCompositeType();EXPECT_EQ(nullptr, N->getVTableHolder());N->replaceVTableHolder(VTableHolder);EXPECT_EQ(VTableHolder, N->getVTableHolder());// As an extension, the containing type can be anything. This is// used by Rust to associate vtables with their concrete type.DIType *BasicType = getBasicType("basic");N->replaceVTableHolder(BasicType);EXPECT_EQ(BasicType, N->getVTableHolder());N->replaceVTableHolder(nullptr);EXPECT_EQ(nullptr, N->getVTableHolder());auto *TemplateParams = MDTuple::getDistinct(Context, None);EXPECT_EQ(nullptr, N->getTemplateParams().get());N->replaceTemplateParams(TemplateParams);EXPECT_EQ(TemplateParams, N->getTemplateParams().get());N->replaceTemplateParams(nullptr);EXPECT_EQ(nullptr, N->getTemplateParams().get());}TEST_F(DICompositeTypeTest, variant_part) {unsigned Tag = dwarf::DW_TAG_variant_part;StringRef Name = "some name";DIFile *File = getFile();unsigned Line = 1;DIScope *Scope = getSubprogram();DIType *BaseType = getCompositeType();uint64_t SizeInBits = 2;uint32_t AlignInBits = 3;uint64_t OffsetInBits = 4;DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5);unsigned RuntimeLang = 6;StringRef Identifier = "some id";DIDerivedType *Discriminator = cast<DIDerivedType>(getDerivedType());DIDerivedType *Discriminator2 = cast<DIDerivedType>(getDerivedType());EXPECT_NE(Discriminator, Discriminator2);auto *N = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,Discriminator);// Test the hashing.auto *Same = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,Discriminator);auto *Other = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,Discriminator2);auto *NoDisc = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr);EXPECT_EQ(N, Same);EXPECT_NE(Same, Other);EXPECT_NE(Same, NoDisc);EXPECT_NE(Other, NoDisc);EXPECT_EQ(N->getDiscriminator(), Discriminator);}TEST_F(DICompositeTypeTest, dynamicArray) {unsigned Tag = dwarf::DW_TAG_array_type;StringRef Name = "some name";DIFile *File = getFile();unsigned Line = 1;DILocalScope *Scope = getSubprogram();DIType *BaseType = getCompositeType();uint64_t SizeInBits = 32;uint32_t AlignInBits = 32;uint64_t OffsetInBits = 4;DINode::DIFlags Flags = static_cast<DINode::DIFlags>(3);unsigned RuntimeLang = 6;StringRef Identifier = "some id";DIType *Type = getDerivedType();Metadata *DlVar1 = DILocalVariable::get(Context, Scope, "dl_var1", File, 8,Type, 2, Flags, 8, nullptr);Metadata *DlVar2 = DILocalVariable::get(Context, Scope, "dl_var2", File, 8,Type, 2, Flags, 8, nullptr);uint64_t Elements1[] = {dwarf::DW_OP_push_object_address, dwarf::DW_OP_deref};Metadata *DataLocation1 = DIExpression::get(Context, Elements1);uint64_t Elements2[] = {dwarf::DW_OP_constu, 0};Metadata *DataLocation2 = DIExpression::get(Context, Elements2);uint64_t Elements3[] = {dwarf::DW_OP_constu, 3};Metadata *Rank1 = DIExpression::get(Context, Elements3);uint64_t Elements4[] = {dwarf::DW_OP_constu, 4};Metadata *Rank2 = DIExpression::get(Context, Elements4);ConstantInt *RankInt1 = ConstantInt::get(Context, APInt(7, 0));ConstantAsMetadata *RankConst1 = ConstantAsMetadata::get(RankInt1);ConstantInt *RankInt2 = ConstantInt::get(Context, APInt(6, 0));ConstantAsMetadata *RankConst2 = ConstantAsMetadata::get(RankInt2);auto *N1 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DlVar1);auto *Same1 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DlVar1);auto *Other1 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DlVar2);EXPECT_EQ(N1, Same1);EXPECT_NE(Same1, Other1);EXPECT_EQ(N1->getDataLocation(), DlVar1);auto *N2 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1);auto *Same2 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1);auto *Other2 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation2);EXPECT_EQ(N2, Same2);EXPECT_NE(Same2, Other2);EXPECT_EQ(N2->getDataLocationExp(), DataLocation1);auto *N3 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1, nullptr, nullptr, Rank1);auto *Same3 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1, nullptr, nullptr, Rank1);auto *Other3 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1, nullptr, nullptr, Rank2);EXPECT_EQ(N3, Same3);EXPECT_NE(Same3, Other3);EXPECT_EQ(N3->getRankExp(), Rank1);auto *N4 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1, nullptr, nullptr, RankConst1);auto *Same4 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1, nullptr, nullptr, RankConst1);auto *Other4 = DICompositeType::get(Context, Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits,OffsetInBits, Flags, nullptr, RuntimeLang, nullptr, nullptr, Identifier,nullptr, DataLocation1, nullptr, nullptr, RankConst2);EXPECT_EQ(N4, Same4);EXPECT_NE(Same4, Other4);EXPECT_EQ(N4->getRankConst(), RankInt1);}typedef MetadataTest DISubroutineTypeTest;TEST_F(DISubroutineTypeTest, get) {DINode::DIFlags Flags = static_cast<DINode::DIFlags>(1);DINode::DIFlags FlagsPOne = static_cast<DINode::DIFlags>(Flags + 1);MDTuple *TypeArray = getTuple();auto *N = DISubroutineType::get(Context, Flags, 0, TypeArray);EXPECT_EQ(dwarf::DW_TAG_subroutine_type, N->getTag());EXPECT_EQ(Flags, N->getFlags());EXPECT_EQ(TypeArray, N->getTypeArray().get());EXPECT_EQ(N, DISubroutineType::get(Context, Flags, 0, TypeArray));EXPECT_NE(N, DISubroutineType::get(Context, FlagsPOne, 0, TypeArray));EXPECT_NE(N, DISubroutineType::get(Context, Flags, 0, getTuple()));// Test the hashing of calling conventions.auto *Fast = DISubroutineType::get(Context, Flags, dwarf::DW_CC_BORLAND_msfastcall, TypeArray);auto *Std = DISubroutineType::get(Context, Flags,dwarf::DW_CC_BORLAND_stdcall, TypeArray);EXPECT_EQ(Fast,DISubroutineType::get(Context, Flags,dwarf::DW_CC_BORLAND_msfastcall, TypeArray));EXPECT_EQ(Std, DISubroutineType::get(Context, Flags, dwarf::DW_CC_BORLAND_stdcall, TypeArray));EXPECT_NE(N, Fast);EXPECT_NE(N, Std);EXPECT_NE(Fast, Std);TempDISubroutineType Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));// Test always-empty operands.EXPECT_EQ(nullptr, N->getScope());EXPECT_EQ(nullptr, N->getFile());EXPECT_EQ("", N->getName());}typedef MetadataTest DIFileTest;TEST_F(DIFileTest, get) {StringRef Filename = "file";StringRef Directory = "dir";DIFile::ChecksumKind CSKind = DIFile::ChecksumKind::CSK_MD5;StringRef ChecksumString = "000102030405060708090a0b0c0d0e0f";DIFile::ChecksumInfo<StringRef> Checksum(CSKind, ChecksumString);StringRef Source = "source";auto *N = DIFile::get(Context, Filename, Directory, Checksum, Source);EXPECT_EQ(dwarf::DW_TAG_file_type, N->getTag());EXPECT_EQ(Filename, N->getFilename());EXPECT_EQ(Directory, N->getDirectory());EXPECT_EQ(Checksum, N->getChecksum());EXPECT_EQ(Source, N->getSource());EXPECT_EQ(N, DIFile::get(Context, Filename, Directory, Checksum, Source));EXPECT_NE(N, DIFile::get(Context, "other", Directory, Checksum, Source));EXPECT_NE(N, DIFile::get(Context, Filename, "other", Checksum, Source));DIFile::ChecksumInfo<StringRef> OtherChecksum(DIFile::ChecksumKind::CSK_SHA1, ChecksumString);EXPECT_NE(N, DIFile::get(Context, Filename, Directory, OtherChecksum));StringRef OtherSource = "other";EXPECT_NE(N, DIFile::get(Context, Filename, Directory, Checksum, OtherSource));EXPECT_NE(N, DIFile::get(Context, Filename, Directory, Checksum));EXPECT_NE(N, DIFile::get(Context, Filename, Directory));TempDIFile Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DIFileTest, ScopeGetFile) {// Ensure that DIScope::getFile() returns itself.DIScope *N = DIFile::get(Context, "file", "dir");EXPECT_EQ(N, N->getFile());}typedef MetadataTest DICompileUnitTest;TEST_F(DICompileUnitTest, get) {unsigned SourceLanguage = 1;DIFile *File = getFile();StringRef Producer = "some producer";bool IsOptimized = false;StringRef Flags = "flag after flag";unsigned RuntimeVersion = 2;StringRef SplitDebugFilename = "another/file";auto EmissionKind = DICompileUnit::FullDebug;MDTuple *EnumTypes = getTuple();MDTuple *RetainedTypes = getTuple();MDTuple *GlobalVariables = getTuple();MDTuple *ImportedEntities = getTuple();uint64_t DWOId = 0x10000000c0ffee;MDTuple *Macros = getTuple();StringRef SysRoot = "/";StringRef SDK = "MacOSX.sdk";auto *N = DICompileUnit::getDistinct(Context, SourceLanguage, File, Producer, IsOptimized, Flags,RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes,RetainedTypes, GlobalVariables, ImportedEntities, Macros, DWOId, true,false, DICompileUnit::DebugNameTableKind::Default, false, SysRoot, SDK);EXPECT_EQ(dwarf::DW_TAG_compile_unit, N->getTag());EXPECT_EQ(SourceLanguage, N->getSourceLanguage());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Producer, N->getProducer());EXPECT_EQ(IsOptimized, N->isOptimized());EXPECT_EQ(Flags, N->getFlags());EXPECT_EQ(RuntimeVersion, N->getRuntimeVersion());EXPECT_EQ(SplitDebugFilename, N->getSplitDebugFilename());EXPECT_EQ(EmissionKind, N->getEmissionKind());EXPECT_EQ(EnumTypes, N->getEnumTypes().get());EXPECT_EQ(RetainedTypes, N->getRetainedTypes().get());EXPECT_EQ(GlobalVariables, N->getGlobalVariables().get());EXPECT_EQ(ImportedEntities, N->getImportedEntities().get());EXPECT_EQ(Macros, N->getMacros().get());EXPECT_EQ(DWOId, N->getDWOId());EXPECT_EQ(SysRoot, N->getSysRoot());EXPECT_EQ(SDK, N->getSDK());TempDICompileUnit Temp = N->clone();EXPECT_EQ(dwarf::DW_TAG_compile_unit, Temp->getTag());EXPECT_EQ(SourceLanguage, Temp->getSourceLanguage());EXPECT_EQ(File, Temp->getFile());EXPECT_EQ(Producer, Temp->getProducer());EXPECT_EQ(IsOptimized, Temp->isOptimized());EXPECT_EQ(Flags, Temp->getFlags());EXPECT_EQ(RuntimeVersion, Temp->getRuntimeVersion());EXPECT_EQ(SplitDebugFilename, Temp->getSplitDebugFilename());EXPECT_EQ(EmissionKind, Temp->getEmissionKind());EXPECT_EQ(EnumTypes, Temp->getEnumTypes().get());EXPECT_EQ(RetainedTypes, Temp->getRetainedTypes().get());EXPECT_EQ(GlobalVariables, Temp->getGlobalVariables().get());EXPECT_EQ(ImportedEntities, Temp->getImportedEntities().get());EXPECT_EQ(Macros, Temp->getMacros().get());EXPECT_EQ(SysRoot, Temp->getSysRoot());EXPECT_EQ(SDK, Temp->getSDK());auto *TempAddress = Temp.get();auto *Clone = MDNode::replaceWithPermanent(std::move(Temp));EXPECT_TRUE(Clone->isDistinct());EXPECT_EQ(TempAddress, Clone);}TEST_F(DICompileUnitTest, replaceArrays) {unsigned SourceLanguage = 1;DIFile *File = getFile();StringRef Producer = "some producer";bool IsOptimized = false;StringRef Flags = "flag after flag";unsigned RuntimeVersion = 2;StringRef SplitDebugFilename = "another/file";auto EmissionKind = DICompileUnit::FullDebug;MDTuple *EnumTypes = MDTuple::getDistinct(Context, None);MDTuple *RetainedTypes = MDTuple::getDistinct(Context, None);MDTuple *ImportedEntities = MDTuple::getDistinct(Context, None);uint64_t DWOId = 0xc0ffee;StringRef SysRoot = "/";StringRef SDK = "MacOSX.sdk";auto *N = DICompileUnit::getDistinct(Context, SourceLanguage, File, Producer, IsOptimized, Flags,RuntimeVersion, SplitDebugFilename, EmissionKind, EnumTypes,RetainedTypes, nullptr, ImportedEntities, nullptr, DWOId, true, false,DICompileUnit::DebugNameTableKind::Default, false, SysRoot, SDK);auto *GlobalVariables = MDTuple::getDistinct(Context, None);EXPECT_EQ(nullptr, N->getGlobalVariables().get());N->replaceGlobalVariables(GlobalVariables);EXPECT_EQ(GlobalVariables, N->getGlobalVariables().get());N->replaceGlobalVariables(nullptr);EXPECT_EQ(nullptr, N->getGlobalVariables().get());auto *Macros = MDTuple::getDistinct(Context, None);EXPECT_EQ(nullptr, N->getMacros().get());N->replaceMacros(Macros);EXPECT_EQ(Macros, N->getMacros().get());N->replaceMacros(nullptr);EXPECT_EQ(nullptr, N->getMacros().get());}typedef MetadataTest DISubprogramTest;TEST_F(DISubprogramTest, get) {DIScope *Scope = getCompositeType();StringRef Name = "name";StringRef LinkageName = "linkage";DIFile *File = getFile();unsigned Line = 2;DISubroutineType *Type = getSubroutineType();bool IsLocalToUnit = false;bool IsDefinition = true;unsigned ScopeLine = 3;DIType *ContainingType = getCompositeType();unsigned Virtuality = 2;unsigned VirtualIndex = 5;int ThisAdjustment = -3;DINode::DIFlags Flags = static_cast<DINode::DIFlags>(6);bool IsOptimized = false;MDTuple *TemplateParams = getTuple();DISubprogram *Declaration = getSubprogram();MDTuple *RetainedNodes = getTuple();MDTuple *ThrownTypes = getTuple();MDTuple *Annotations = getTuple();StringRef TargetFuncName = "target";DICompileUnit *Unit = getUnit();DISubprogram::DISPFlags SPFlags =static_cast<DISubprogram::DISPFlags>(Virtuality);assert(!IsLocalToUnit && IsDefinition && !IsOptimized &&"bools and SPFlags have to match");SPFlags |= DISubprogram::SPFlagDefinition;auto *N = DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type, ScopeLine,ContainingType, VirtualIndex, ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, Declaration, RetainedNodes, ThrownTypes, Annotations,TargetFuncName);EXPECT_EQ(dwarf::DW_TAG_subprogram, N->getTag());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(Name, N->getName());EXPECT_EQ(LinkageName, N->getLinkageName());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Line, N->getLine());EXPECT_EQ(Type, N->getType());EXPECT_EQ(IsLocalToUnit, N->isLocalToUnit());EXPECT_EQ(IsDefinition, N->isDefinition());EXPECT_EQ(ScopeLine, N->getScopeLine());EXPECT_EQ(ContainingType, N->getContainingType());EXPECT_EQ(Virtuality, N->getVirtuality());EXPECT_EQ(VirtualIndex, N->getVirtualIndex());EXPECT_EQ(ThisAdjustment, N->getThisAdjustment());EXPECT_EQ(Flags, N->getFlags());EXPECT_EQ(IsOptimized, N->isOptimized());EXPECT_EQ(Unit, N->getUnit());EXPECT_EQ(TemplateParams, N->getTemplateParams().get());EXPECT_EQ(Declaration, N->getDeclaration());EXPECT_EQ(RetainedNodes, N->getRetainedNodes().get());EXPECT_EQ(ThrownTypes, N->getThrownTypes().get());EXPECT_EQ(Annotations, N->getAnnotations().get());EXPECT_EQ(TargetFuncName, N->getTargetFuncName());EXPECT_EQ(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, Declaration, RetainedNodes,ThrownTypes, Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, getCompositeType(), Name, LinkageName,File, Line, Type, ScopeLine, ContainingType,VirtualIndex, ThisAdjustment, Flags, SPFlags,Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, "other", LinkageName, File,Line, Type, ScopeLine, ContainingType,VirtualIndex, ThisAdjustment, Flags, SPFlags,Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, "other", File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, Declaration, RetainedNodes,ThrownTypes, Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, getFile(),Line, Type, ScopeLine, ContainingType,VirtualIndex, ThisAdjustment, Flags, SPFlags,Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File,Line + 1, Type, ScopeLine, ContainingType,VirtualIndex, ThisAdjustment, Flags, SPFlags,Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,getSubroutineType(), ScopeLine, ContainingType,VirtualIndex, ThisAdjustment, Flags, SPFlags,Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type,ScopeLine, ContainingType, VirtualIndex, ThisAdjustment,Flags, SPFlags ^ DISubprogram::SPFlagLocalToUnit, Unit,TemplateParams, Declaration, RetainedNodes, ThrownTypes,Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type,ScopeLine, ContainingType, VirtualIndex, ThisAdjustment,Flags, SPFlags ^ DISubprogram::SPFlagDefinition, Unit,TemplateParams, Declaration, RetainedNodes, ThrownTypes,Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine + 1, ContainingType,VirtualIndex, ThisAdjustment, Flags, SPFlags,Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, getCompositeType(),VirtualIndex, ThisAdjustment, Flags, SPFlags,Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type,ScopeLine, ContainingType, VirtualIndex, ThisAdjustment,Flags, SPFlags ^ DISubprogram::SPFlagVirtual, Unit,TemplateParams, Declaration, RetainedNodes, ThrownTypes,Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType,VirtualIndex + 1, ThisAdjustment, Flags,SPFlags, Unit, TemplateParams, Declaration,RetainedNodes, ThrownTypes, Annotations,TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line, Type,ScopeLine, ContainingType, VirtualIndex, ThisAdjustment,Flags, SPFlags ^ DISubprogram::SPFlagOptimized, Unit,TemplateParams, Declaration, RetainedNodes, ThrownTypes,Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, nullptr,TemplateParams, Declaration, RetainedNodes,ThrownTypes, Annotations, TargetFuncName));EXPECT_NE(N,DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit, getTuple(),Declaration, RetainedNodes, ThrownTypes,Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, getSubprogram(), RetainedNodes,ThrownTypes, Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, Declaration, getTuple(),ThrownTypes, Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, Declaration, RetainedNodes,getTuple(), Annotations, TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, Declaration, RetainedNodes,ThrownTypes, getTuple(), TargetFuncName));EXPECT_NE(N, DISubprogram::get(Context, Scope, Name, LinkageName, File, Line,Type, ScopeLine, ContainingType, VirtualIndex,ThisAdjustment, Flags, SPFlags, Unit,TemplateParams, Declaration, RetainedNodes,ThrownTypes, Annotations, "other"));TempDISubprogram Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DILexicalBlockTest;TEST_F(DILexicalBlockTest, get) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();unsigned Line = 5;unsigned Column = 8;auto *N = DILexicalBlock::get(Context, Scope, File, Line, Column);EXPECT_EQ(dwarf::DW_TAG_lexical_block, N->getTag());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Line, N->getLine());EXPECT_EQ(Column, N->getColumn());EXPECT_EQ(N, DILexicalBlock::get(Context, Scope, File, Line, Column));EXPECT_NE(N,DILexicalBlock::get(Context, getSubprogram(), File, Line, Column));EXPECT_NE(N, DILexicalBlock::get(Context, Scope, getFile(), Line, Column));EXPECT_NE(N, DILexicalBlock::get(Context, Scope, File, Line + 1, Column));EXPECT_NE(N, DILexicalBlock::get(Context, Scope, File, Line, Column + 1));TempDILexicalBlock Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DILexicalBlockTest, Overflow) {DISubprogram *SP = getSubprogram();DIFile *F = getFile();{auto *LB = DILexicalBlock::get(Context, SP, F, 2, 7);EXPECT_EQ(2u, LB->getLine());EXPECT_EQ(7u, LB->getColumn());}unsigned U16 = 1u << 16;{auto *LB = DILexicalBlock::get(Context, SP, F, UINT32_MAX, U16 - 1);EXPECT_EQ(UINT32_MAX, LB->getLine());EXPECT_EQ(U16 - 1, LB->getColumn());}{auto *LB = DILexicalBlock::get(Context, SP, F, UINT32_MAX, U16);EXPECT_EQ(UINT32_MAX, LB->getLine());EXPECT_EQ(0u, LB->getColumn());}{auto *LB = DILexicalBlock::get(Context, SP, F, UINT32_MAX, U16 + 1);EXPECT_EQ(UINT32_MAX, LB->getLine());EXPECT_EQ(0u, LB->getColumn());}}typedef MetadataTest DILexicalBlockFileTest;TEST_F(DILexicalBlockFileTest, get) {DILocalScope *Scope = getSubprogram();DIFile *File = getFile();unsigned Discriminator = 5;auto *N = DILexicalBlockFile::get(Context, Scope, File, Discriminator);EXPECT_EQ(dwarf::DW_TAG_lexical_block, N->getTag());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Discriminator, N->getDiscriminator());EXPECT_EQ(N, DILexicalBlockFile::get(Context, Scope, File, Discriminator));EXPECT_NE(N, DILexicalBlockFile::get(Context, getSubprogram(), File,Discriminator));EXPECT_NE(N,DILexicalBlockFile::get(Context, Scope, getFile(), Discriminator));EXPECT_NE(N,DILexicalBlockFile::get(Context, Scope, File, Discriminator + 1));TempDILexicalBlockFile Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DINamespaceTest;TEST_F(DINamespaceTest, get) {DIScope *Scope = getFile();StringRef Name = "namespace";bool ExportSymbols = true;auto *N = DINamespace::get(Context, Scope, Name, ExportSymbols);EXPECT_EQ(dwarf::DW_TAG_namespace, N->getTag());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(Name, N->getName());EXPECT_EQ(N, DINamespace::get(Context, Scope, Name, ExportSymbols));EXPECT_NE(N, DINamespace::get(Context, getFile(), Name, ExportSymbols));EXPECT_NE(N, DINamespace::get(Context, Scope, "other", ExportSymbols));EXPECT_NE(N, DINamespace::get(Context, Scope, Name, !ExportSymbols));TempDINamespace Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DIModuleTest;TEST_F(DIModuleTest, get) {DIFile *File = getFile();DIScope *Scope = getFile();StringRef Name = "module";StringRef ConfigMacro = "-DNDEBUG";StringRef Includes = "-I.";StringRef APINotes = "/tmp/m.apinotes";unsigned LineNo = 4;bool IsDecl = true;auto *N = DIModule::get(Context, File, Scope, Name, ConfigMacro, Includes,APINotes, LineNo, IsDecl);EXPECT_EQ(dwarf::DW_TAG_module, N->getTag());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(Name, N->getName());EXPECT_EQ(ConfigMacro, N->getConfigurationMacros());EXPECT_EQ(Includes, N->getIncludePath());EXPECT_EQ(APINotes, N->getAPINotesFile());EXPECT_EQ(LineNo, N->getLineNo());EXPECT_EQ(IsDecl, N->getIsDecl());EXPECT_EQ(N, DIModule::get(Context, File, Scope, Name, ConfigMacro, Includes,APINotes, LineNo, IsDecl));EXPECT_NE(N, DIModule::get(Context, getFile(), getFile(), Name, ConfigMacro,Includes, APINotes, LineNo, IsDecl));EXPECT_NE(N, DIModule::get(Context, File, Scope, "other", ConfigMacro,Includes, APINotes, LineNo, IsDecl));EXPECT_NE(N, DIModule::get(Context, File, Scope, Name, "other", Includes,APINotes, LineNo, IsDecl));EXPECT_NE(N, DIModule::get(Context, File, Scope, Name, ConfigMacro, "other",APINotes, LineNo, IsDecl));EXPECT_NE(N, DIModule::get(Context, File, Scope, Name, ConfigMacro, Includes,"other", LineNo, IsDecl));EXPECT_NE(N, DIModule::get(Context, getFile(), Scope, Name, ConfigMacro,Includes, APINotes, LineNo, IsDecl));EXPECT_NE(N, DIModule::get(Context, File, Scope, Name, ConfigMacro, Includes,APINotes, 5, IsDecl));EXPECT_NE(N, DIModule::get(Context, File, Scope, Name, ConfigMacro, Includes,APINotes, LineNo, false));TempDIModule Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DITemplateTypeParameterTest;TEST_F(DITemplateTypeParameterTest, get) {StringRef Name = "template";DIType *Type = getBasicType("basic");bool defaulted = false;auto *N = DITemplateTypeParameter::get(Context, Name, Type, defaulted);EXPECT_EQ(dwarf::DW_TAG_template_type_parameter, N->getTag());EXPECT_EQ(Name, N->getName());EXPECT_EQ(Type, N->getType());EXPECT_EQ(N, DITemplateTypeParameter::get(Context, Name, Type, defaulted));EXPECT_NE(N, DITemplateTypeParameter::get(Context, "other", Type, defaulted));EXPECT_NE(N, DITemplateTypeParameter::get(Context, Name,getBasicType("other"), defaulted));EXPECT_NE(N, DITemplateTypeParameter::get(Context, Name, Type, true));TempDITemplateTypeParameter Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DITemplateValueParameterTest;TEST_F(DITemplateValueParameterTest, get) {unsigned Tag = dwarf::DW_TAG_template_value_parameter;StringRef Name = "template";DIType *Type = getBasicType("basic");bool defaulted = false;Metadata *Value = getConstantAsMetadata();auto *N =DITemplateValueParameter::get(Context, Tag, Name, Type, defaulted, Value);EXPECT_EQ(Tag, N->getTag());EXPECT_EQ(Name, N->getName());EXPECT_EQ(Type, N->getType());EXPECT_EQ(Value, N->getValue());EXPECT_EQ(N, DITemplateValueParameter::get(Context, Tag, Name, Type,defaulted, Value));EXPECT_NE(N, DITemplateValueParameter::get(Context, dwarf::DW_TAG_GNU_template_template_param, Name,Type, defaulted, Value));EXPECT_NE(N, DITemplateValueParameter::get(Context, Tag, "other", Type,defaulted, Value));EXPECT_NE(N, DITemplateValueParameter::get(Context, Tag, Name,getBasicType("other"), defaulted,Value));EXPECT_NE(N,DITemplateValueParameter::get(Context, Tag, Name, Type, defaulted,getConstantAsMetadata()));EXPECT_NE(N, DITemplateValueParameter::get(Context, Tag, Name, Type, true, Value));TempDITemplateValueParameter Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DIGlobalVariableTest;TEST_F(DIGlobalVariableTest, get) {DIScope *Scope = getSubprogram();StringRef Name = "name";StringRef LinkageName = "linkage";DIFile *File = getFile();unsigned Line = 5;DIType *Type = getDerivedType();bool IsLocalToUnit = false;bool IsDefinition = true;MDTuple *templateParams = getTuple();DIDerivedType *StaticDataMemberDeclaration =cast<DIDerivedType>(getDerivedType());uint32_t AlignInBits = 8;auto *N = DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit,IsDefinition, StaticDataMemberDeclaration, templateParams, AlignInBits,nullptr);EXPECT_EQ(dwarf::DW_TAG_variable, N->getTag());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(Name, N->getName());EXPECT_EQ(LinkageName, N->getLinkageName());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Line, N->getLine());EXPECT_EQ(Type, N->getType());EXPECT_EQ(IsLocalToUnit, N->isLocalToUnit());EXPECT_EQ(IsDefinition, N->isDefinition());EXPECT_EQ(StaticDataMemberDeclaration, N->getStaticDataMemberDeclaration());EXPECT_EQ(templateParams, N->getTemplateParams());EXPECT_EQ(AlignInBits, N->getAlignInBits());EXPECT_EQ(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line, Type, IsLocalToUnit, IsDefinition,StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, getSubprogram(), Name, LinkageName, File, Line,Type, IsLocalToUnit, IsDefinition,StaticDataMemberDeclaration, templateParams, AlignInBits,nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, "other", LinkageName, File,Line, Type, IsLocalToUnit, IsDefinition,StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, "other", File, Line,Type, IsLocalToUnit, IsDefinition,StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName,getFile(), Line, Type, IsLocalToUnit,IsDefinition, StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line + 1, Type, IsLocalToUnit,IsDefinition, StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line, getDerivedType(), IsLocalToUnit,IsDefinition, StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line, Type, !IsLocalToUnit, IsDefinition,StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line, Type, IsLocalToUnit, !IsDefinition,StaticDataMemberDeclaration,templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line, Type, IsLocalToUnit, IsDefinition,cast<DIDerivedType>(getDerivedType()),templateParams, AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line, Type, IsLocalToUnit, IsDefinition,StaticDataMemberDeclaration, nullptr,AlignInBits, nullptr));EXPECT_NE(N, DIGlobalVariable::get(Context, Scope, Name, LinkageName, File,Line, Type, IsLocalToUnit, IsDefinition,StaticDataMemberDeclaration,templateParams, (AlignInBits << 1),nullptr));TempDIGlobalVariable Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DIGlobalVariableExpressionTest;TEST_F(DIGlobalVariableExpressionTest, get) {DIScope *Scope = getSubprogram();StringRef Name = "name";StringRef LinkageName = "linkage";DIFile *File = getFile();unsigned Line = 5;DIType *Type = getDerivedType();bool IsLocalToUnit = false;bool IsDefinition = true;MDTuple *templateParams = getTuple();auto *Expr = DIExpression::get(Context, {1, 2});auto *Expr2 = DIExpression::get(Context, {1, 2, 3});DIDerivedType *StaticDataMemberDeclaration =cast<DIDerivedType>(getDerivedType());uint32_t AlignInBits = 8;auto *Var = DIGlobalVariable::get(Context, Scope, Name, LinkageName, File, Line, Type, IsLocalToUnit,IsDefinition, StaticDataMemberDeclaration, templateParams, AlignInBits,nullptr);auto *Var2 = DIGlobalVariable::get(Context, Scope, "other", LinkageName, File, Line, Type, IsLocalToUnit,IsDefinition, StaticDataMemberDeclaration, templateParams, AlignInBits,nullptr);auto *N = DIGlobalVariableExpression::get(Context, Var, Expr);EXPECT_EQ(Var, N->getVariable());EXPECT_EQ(Expr, N->getExpression());EXPECT_EQ(N, DIGlobalVariableExpression::get(Context, Var, Expr));EXPECT_NE(N, DIGlobalVariableExpression::get(Context, Var2, Expr));EXPECT_NE(N, DIGlobalVariableExpression::get(Context, Var, Expr2));TempDIGlobalVariableExpression Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DILocalVariableTest;TEST_F(DILocalVariableTest, get) {DILocalScope *Scope = getSubprogram();StringRef Name = "name";DIFile *File = getFile();unsigned Line = 5;DIType *Type = getDerivedType();unsigned Arg = 6;DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);uint32_t AlignInBits = 8;auto *N =DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg, Flags,AlignInBits, nullptr);EXPECT_TRUE(N->isParameter());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(Name, N->getName());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Line, N->getLine());EXPECT_EQ(Type, N->getType());EXPECT_EQ(Arg, N->getArg());EXPECT_EQ(Flags, N->getFlags());EXPECT_EQ(AlignInBits, N->getAlignInBits());EXPECT_EQ(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type, Arg,Flags, AlignInBits, nullptr));EXPECT_FALSE(DILocalVariable::get(Context, Scope, Name, File, Line, Type, 0, Flags,AlignInBits, nullptr)->isParameter());EXPECT_NE(N, DILocalVariable::get(Context, getSubprogram(), Name, File, Line,Type, Arg, Flags, AlignInBits, nullptr));EXPECT_NE(N, DILocalVariable::get(Context, Scope, "other", File, Line, Type,Arg, Flags, AlignInBits, nullptr));EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, getFile(), Line, Type,Arg, Flags, AlignInBits, nullptr));EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line + 1, Type,Arg, Flags, AlignInBits, nullptr));EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line,getDerivedType(), Arg, Flags, AlignInBits,nullptr));EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type,Arg + 1, Flags, AlignInBits, nullptr));EXPECT_NE(N, DILocalVariable::get(Context, Scope, Name, File, Line, Type,Arg, Flags, (AlignInBits << 1), nullptr));TempDILocalVariable Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}TEST_F(DILocalVariableTest, getArg256) {EXPECT_EQ(255u, DILocalVariable::get(Context, getSubprogram(), "", getFile(),0, nullptr, 255, DINode::FlagZero, 0,nullptr)->getArg());EXPECT_EQ(256u, DILocalVariable::get(Context, getSubprogram(), "", getFile(),0, nullptr, 256, DINode::FlagZero, 0,nullptr)->getArg());EXPECT_EQ(257u, DILocalVariable::get(Context, getSubprogram(), "", getFile(),0, nullptr, 257, DINode::FlagZero, 0,nullptr)->getArg());unsigned Max = UINT16_MAX;EXPECT_EQ(Max, DILocalVariable::get(Context, getSubprogram(), "", getFile(),0, nullptr, Max, DINode::FlagZero, 0,nullptr)->getArg());}typedef MetadataTest DIExpressionTest;TEST_F(DIExpressionTest, get) {uint64_t Elements[] = {2, 6, 9, 78, 0};auto *N = DIExpression::get(Context, Elements);EXPECT_EQ(makeArrayRef(Elements), N->getElements());EXPECT_EQ(N, DIExpression::get(Context, Elements));EXPECT_EQ(5u, N->getNumElements());EXPECT_EQ(2u, N->getElement(0));EXPECT_EQ(6u, N->getElement(1));EXPECT_EQ(9u, N->getElement(2));EXPECT_EQ(78u, N->getElement(3));EXPECT_EQ(0u, N->getElement(4));TempDIExpression Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));// Test DIExpression::prepend().uint64_t Elts0[] = {dwarf::DW_OP_LLVM_fragment, 0, 32};auto *N0 = DIExpression::get(Context, Elts0);uint8_t DIExprFlags = DIExpression::ApplyOffset;DIExprFlags |= DIExpression::DerefBefore;DIExprFlags |= DIExpression::DerefAfter;DIExprFlags |= DIExpression::StackValue;auto *N0WithPrependedOps = DIExpression::prepend(N0, DIExprFlags, 64);uint64_t Elts1[] = {dwarf::DW_OP_deref,dwarf::DW_OP_plus_uconst, 64,dwarf::DW_OP_deref,dwarf::DW_OP_stack_value,dwarf::DW_OP_LLVM_fragment, 0, 32};auto *N1 = DIExpression::get(Context, Elts1);EXPECT_EQ(N0WithPrependedOps, N1);// Test DIExpression::append().uint64_t Elts2[] = {dwarf::DW_OP_deref, dwarf::DW_OP_plus_uconst, 64,dwarf::DW_OP_deref, dwarf::DW_OP_stack_value};auto *N2 = DIExpression::append(N0, Elts2);EXPECT_EQ(N0WithPrependedOps, N2);}TEST_F(DIExpressionTest, isValid) {#define EXPECT_VALID(...) \do { \uint64_t Elements[] = {__VA_ARGS__}; \EXPECT_TRUE(DIExpression::get(Context, Elements)->isValid()); \} while (false)#define EXPECT_INVALID(...) \do { \uint64_t Elements[] = {__VA_ARGS__}; \EXPECT_FALSE(DIExpression::get(Context, Elements)->isValid()); \} while (false)// Empty expression should be valid.EXPECT_TRUE(DIExpression::get(Context, None));// Valid constructions.EXPECT_VALID(dwarf::DW_OP_plus_uconst, 6);EXPECT_VALID(dwarf::DW_OP_constu, 6, dwarf::DW_OP_plus);EXPECT_VALID(dwarf::DW_OP_deref);EXPECT_VALID(dwarf::DW_OP_LLVM_fragment, 3, 7);EXPECT_VALID(dwarf::DW_OP_plus_uconst, 6, dwarf::DW_OP_deref);EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_plus_uconst, 6);EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_LLVM_fragment, 3, 7);EXPECT_VALID(dwarf::DW_OP_deref, dwarf::DW_OP_plus_uconst, 6,dwarf::DW_OP_LLVM_fragment, 3, 7);// Invalid constructions.EXPECT_INVALID(~0u);EXPECT_INVALID(dwarf::DW_OP_plus, 0);EXPECT_INVALID(dwarf::DW_OP_plus_uconst);EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment);EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment, 3);EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment, 3, 7, dwarf::DW_OP_plus_uconst, 3);EXPECT_INVALID(dwarf::DW_OP_LLVM_fragment, 3, 7, dwarf::DW_OP_deref);#undef EXPECT_VALID#undef EXPECT_INVALID}TEST_F(DIExpressionTest, createFragmentExpression) {#define EXPECT_VALID_FRAGMENT(Offset, Size, ...) \do { \uint64_t Elements[] = {__VA_ARGS__}; \DIExpression *Expression = DIExpression::get(Context, Elements); \EXPECT_TRUE( \DIExpression::createFragmentExpression(Expression, Offset, Size) \.has_value()); \} while (false)#define EXPECT_INVALID_FRAGMENT(Offset, Size, ...) \do { \uint64_t Elements[] = {__VA_ARGS__}; \DIExpression *Expression = DIExpression::get(Context, Elements); \EXPECT_FALSE( \DIExpression::createFragmentExpression(Expression, Offset, Size) \.has_value()); \} while (false)// createFragmentExpression adds correct ops.Optional<DIExpression*> R = DIExpression::createFragmentExpression(DIExpression::get(Context, {}), 0, 32);EXPECT_EQ(R.has_value(), true);EXPECT_EQ(3u, (*R)->getNumElements());EXPECT_EQ(dwarf::DW_OP_LLVM_fragment, (*R)->getElement(0));EXPECT_EQ(0u, (*R)->getElement(1));EXPECT_EQ(32u, (*R)->getElement(2));// Valid fragment expressions.EXPECT_VALID_FRAGMENT(0, 32, {});EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_deref);EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_LLVM_fragment, 0, 32);EXPECT_VALID_FRAGMENT(16, 16, dwarf::DW_OP_LLVM_fragment, 0, 32);// Invalid fragment expressions (incompatible ops).EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 6, dwarf::DW_OP_plus);EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 14, dwarf::DW_OP_minus);EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shr);EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shl);EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shra);EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6);#undef EXPECT_VALID_FRAGMENT#undef EXPECT_INVALID_FRAGMENT}TEST_F(DIExpressionTest, replaceArg) {#define EXPECT_REPLACE_ARG_EQ(Expr, OldArg, NewArg, ...) \do { \uint64_t Elements[] = {__VA_ARGS__}; \ArrayRef<uint64_t> Expected = Elements; \DIExpression *Expression = DIExpression::replaceArg(Expr, OldArg, NewArg); \EXPECT_EQ(Expression->getElements(), Expected); \} while (false)auto N = DIExpression::get(Context, {dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_LLVM_arg, 1,dwarf::DW_OP_plus, dwarf::DW_OP_LLVM_arg, 2, dwarf::DW_OP_mul});EXPECT_REPLACE_ARG_EQ(N, 0, 1, dwarf::DW_OP_LLVM_arg, 0,dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_plus,dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul);EXPECT_REPLACE_ARG_EQ(N, 0, 2, dwarf::DW_OP_LLVM_arg, 1,dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_plus,dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul);EXPECT_REPLACE_ARG_EQ(N, 2, 0, dwarf::DW_OP_LLVM_arg, 0,dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_plus,dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_mul);EXPECT_REPLACE_ARG_EQ(N, 2, 1, dwarf::DW_OP_LLVM_arg, 0,dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_plus,dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul);#undef EXPECT_REPLACE_ARG_EQ}TEST_F(DIExpressionTest, foldConstant) {const ConstantInt *Int;const ConstantInt *NewInt;DIExpression *Expr;DIExpression *NewExpr;#define EXPECT_FOLD_CONST(StartWidth, StartValue, EndWidth, EndValue, NumElts) \Int = ConstantInt::get(Context, APInt(StartWidth, StartValue)); \std::tie(NewExpr, NewInt) = Expr->constantFold(Int); \ASSERT_EQ(NewInt->getBitWidth(), EndWidth##u); \EXPECT_EQ(NewInt->getValue(), APInt(EndWidth, EndValue)); \EXPECT_EQ(NewExpr->getNumElements(), NumElts##u)// Unfoldable expression should return the original unmodified Int/Expr.Expr = DIExpression::get(Context, {dwarf::DW_OP_deref});EXPECT_FOLD_CONST(32, 117, 32, 117, 1);EXPECT_EQ(NewExpr, Expr);EXPECT_EQ(NewInt, Int);EXPECT_TRUE(NewExpr->startsWithDeref());// One unsigned bit-width conversion.Expr = DIExpression::get(Context, {dwarf::DW_OP_LLVM_convert, 72, dwarf::DW_ATE_unsigned});EXPECT_FOLD_CONST(8, 12, 72, 12, 0);// Two unsigned bit-width conversions (mask truncation).Expr = DIExpression::get(Context, {dwarf::DW_OP_LLVM_convert, 8, dwarf::DW_ATE_unsigned,dwarf::DW_OP_LLVM_convert, 16, dwarf::DW_ATE_unsigned});EXPECT_FOLD_CONST(32, -1, 16, 0xff, 0);// Sign extension.Expr = DIExpression::get(Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed});EXPECT_FOLD_CONST(16, -1, 32, -1, 0);// Get non-foldable operations back in the new Expr.uint64_t Elements[] = {dwarf::DW_OP_deref, dwarf::DW_OP_stack_value};ArrayRef<uint64_t> Expected = Elements;Expr = DIExpression::get(Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed});Expr = DIExpression::append(Expr, Expected);ASSERT_EQ(Expr->getNumElements(), 5u);EXPECT_FOLD_CONST(16, -1, 32, -1, 2);EXPECT_EQ(NewExpr->getElements(), Expected);#undef EXPECT_FOLD_CONST}typedef MetadataTest DIObjCPropertyTest;TEST_F(DIObjCPropertyTest, get) {StringRef Name = "name";DIFile *File = getFile();unsigned Line = 5;StringRef GetterName = "getter";StringRef SetterName = "setter";unsigned Attributes = 7;DIType *Type = getBasicType("basic");auto *N = DIObjCProperty::get(Context, Name, File, Line, GetterName,SetterName, Attributes, Type);EXPECT_EQ(dwarf::DW_TAG_APPLE_property, N->getTag());EXPECT_EQ(Name, N->getName());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Line, N->getLine());EXPECT_EQ(GetterName, N->getGetterName());EXPECT_EQ(SetterName, N->getSetterName());EXPECT_EQ(Attributes, N->getAttributes());EXPECT_EQ(Type, N->getType());EXPECT_EQ(N, DIObjCProperty::get(Context, Name, File, Line, GetterName,SetterName, Attributes, Type));EXPECT_NE(N, DIObjCProperty::get(Context, "other", File, Line, GetterName,SetterName, Attributes, Type));EXPECT_NE(N, DIObjCProperty::get(Context, Name, getFile(), Line, GetterName,SetterName, Attributes, Type));EXPECT_NE(N, DIObjCProperty::get(Context, Name, File, Line + 1, GetterName,SetterName, Attributes, Type));EXPECT_NE(N, DIObjCProperty::get(Context, Name, File, Line, "other",SetterName, Attributes, Type));EXPECT_NE(N, DIObjCProperty::get(Context, Name, File, Line, GetterName,"other", Attributes, Type));EXPECT_NE(N, DIObjCProperty::get(Context, Name, File, Line, GetterName,SetterName, Attributes + 1, Type));EXPECT_NE(N, DIObjCProperty::get(Context, Name, File, Line, GetterName,SetterName, Attributes,getBasicType("other")));TempDIObjCProperty Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));}typedef MetadataTest DIImportedEntityTest;TEST_F(DIImportedEntityTest, get) {unsigned Tag = dwarf::DW_TAG_imported_module;DIScope *Scope = getSubprogram();DINode *Entity = getCompositeType();DIFile *File = getFile();unsigned Line = 5;StringRef Name = "name";auto *N =DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line, Name);EXPECT_EQ(Tag, N->getTag());EXPECT_EQ(Scope, N->getScope());EXPECT_EQ(Entity, N->getEntity());EXPECT_EQ(File, N->getFile());EXPECT_EQ(Line, N->getLine());EXPECT_EQ(Name, N->getName());EXPECT_EQ(N, DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line, Name));EXPECT_NE(N,DIImportedEntity::get(Context, dwarf::DW_TAG_imported_declaration,Scope, Entity, File, Line, Name));EXPECT_NE(N, DIImportedEntity::get(Context, Tag, getSubprogram(), Entity,File, Line, Name));EXPECT_NE(N, DIImportedEntity::get(Context, Tag, Scope, getCompositeType(),File, Line, Name));EXPECT_NE(N, DIImportedEntity::get(Context, Tag, Scope, Entity, nullptr, Line,Name));EXPECT_NE(N, DIImportedEntity::get(Context, Tag, Scope, Entity, File,Line + 1, Name));EXPECT_NE(N, DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line,"other"));TempDIImportedEntity Temp = N->clone();EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));MDTuple *Elements1 = getTuple();MDTuple *Elements2 = getTuple();auto *Ne = DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line,Name, Elements1);EXPECT_EQ(Elements1, Ne->getElements().get());EXPECT_EQ(Ne, DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line,Name, Elements1));EXPECT_NE(Ne, DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line,"ModOther", Elements1));EXPECT_NE(Ne, DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line,Name, Elements2));EXPECT_NE(Ne, DIImportedEntity::get(Context, Tag, Scope, Entity, File, Line, Name));TempDIImportedEntity Tempe = Ne->clone();EXPECT_EQ(Ne, MDNode::replaceWithUniqued(std::move(Tempe)));}typedef MetadataTest MetadataAsValueTest;TEST_F(MetadataAsValueTest, MDNode) {MDNode *N = MDNode::get(Context, None);auto *V = MetadataAsValue::get(Context, N);EXPECT_TRUE(V->getType()->isMetadataTy());EXPECT_EQ(N, V->getMetadata());auto *V2 = MetadataAsValue::get(Context, N);EXPECT_EQ(V, V2);}TEST_F(MetadataAsValueTest, MDNodeMDNode) {MDNode *N = MDNode::get(Context, None);Metadata *Ops[] = {N};MDNode *N2 = MDNode::get(Context, Ops);auto *V = MetadataAsValue::get(Context, N2);EXPECT_TRUE(V->getType()->isMetadataTy());EXPECT_EQ(N2, V->getMetadata());auto *V2 = MetadataAsValue::get(Context, N2);EXPECT_EQ(V, V2);auto *V3 = MetadataAsValue::get(Context, N);EXPECT_TRUE(V3->getType()->isMetadataTy());EXPECT_NE(V, V3);EXPECT_EQ(N, V3->getMetadata());}TEST_F(MetadataAsValueTest, MDNodeConstant) {auto *C = ConstantInt::getTrue(Context);auto *MD = ConstantAsMetadata::get(C);Metadata *Ops[] = {MD};auto *N = MDNode::get(Context, Ops);auto *V = MetadataAsValue::get(Context, MD);EXPECT_TRUE(V->getType()->isMetadataTy());EXPECT_EQ(MD, V->getMetadata());auto *V2 = MetadataAsValue::get(Context, N);EXPECT_EQ(MD, V2->getMetadata());EXPECT_EQ(V, V2);}typedef MetadataTest ValueAsMetadataTest;TEST_F(ValueAsMetadataTest, UpdatesOnRAUW) {Type *Ty = Type::getInt1PtrTy(Context);std::unique_ptr<GlobalVariable> GV0(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));auto *MD = ValueAsMetadata::get(GV0.get());EXPECT_TRUE(MD->getValue() == GV0.get());ASSERT_TRUE(GV0->use_empty());std::unique_ptr<GlobalVariable> GV1(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));GV0->replaceAllUsesWith(GV1.get());EXPECT_TRUE(MD->getValue() == GV1.get());}TEST_F(ValueAsMetadataTest, TempTempReplacement) {// Create a constant.ConstantAsMetadata *CI =ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));auto Temp1 = MDTuple::getTemporary(Context, None);auto Temp2 = MDTuple::getTemporary(Context, {CI});auto *N = MDTuple::get(Context, {Temp1.get()});// Test replacing a temporary node with another temporary node.Temp1->replaceAllUsesWith(Temp2.get());EXPECT_EQ(N->getOperand(0), Temp2.get());// Clean up Temp2 for teardown.Temp2->replaceAllUsesWith(nullptr);}TEST_F(ValueAsMetadataTest, CollidingDoubleUpdates) {// Create a constant.ConstantAsMetadata *CI =ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));// Create a temporary to prevent nodes from resolving.auto Temp = MDTuple::getTemporary(Context, None);// When the first operand of N1 gets reset to nullptr, it'll collide with N2.Metadata *Ops1[] = {CI, CI, Temp.get()};Metadata *Ops2[] = {nullptr, CI, Temp.get()};auto *N1 = MDTuple::get(Context, Ops1);auto *N2 = MDTuple::get(Context, Ops2);ASSERT_NE(N1, N2);// Tell metadata that the constant is getting deleted.//// After this, N1 will be invalid, so don't touch it.ValueAsMetadata::handleDeletion(CI->getValue());EXPECT_EQ(nullptr, N2->getOperand(0));EXPECT_EQ(nullptr, N2->getOperand(1));EXPECT_EQ(Temp.get(), N2->getOperand(2));// Clean up Temp for teardown.Temp->replaceAllUsesWith(nullptr);}typedef MetadataTest DIArgListTest;TEST_F(DIArgListTest, get) {SmallVector<ValueAsMetadata *, 2> VMs;VMs.push_back(ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0))));VMs.push_back(ConstantAsMetadata::get(ConstantInt::get(Context, APInt(2, 0))));DIArgList *DV0 = DIArgList::get(Context, VMs);DIArgList *DV1 = DIArgList::get(Context, VMs);EXPECT_EQ(DV0, DV1);}TEST_F(DIArgListTest, UpdatesOnRAUW) {Type *Ty = Type::getInt1PtrTy(Context);ConstantAsMetadata *CI =ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));std::unique_ptr<GlobalVariable> GV0(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));auto *MD0 = ValueAsMetadata::get(GV0.get());SmallVector<ValueAsMetadata *, 2> VMs;VMs.push_back(CI);VMs.push_back(MD0);auto *AL = DIArgList::get(Context, VMs);EXPECT_EQ(AL->getArgs()[0], CI);EXPECT_EQ(AL->getArgs()[1], MD0);std::unique_ptr<GlobalVariable> GV1(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));auto *MD1 = ValueAsMetadata::get(GV1.get());GV0->replaceAllUsesWith(GV1.get());EXPECT_EQ(AL->getArgs()[0], CI);EXPECT_EQ(AL->getArgs()[1], MD1);}typedef MetadataTest TrackingMDRefTest;TEST_F(TrackingMDRefTest, UpdatesOnRAUW) {Type *Ty = Type::getInt1PtrTy(Context);std::unique_ptr<GlobalVariable> GV0(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));TypedTrackingMDRef<ValueAsMetadata> MD(ValueAsMetadata::get(GV0.get()));EXPECT_TRUE(MD->getValue() == GV0.get());ASSERT_TRUE(GV0->use_empty());std::unique_ptr<GlobalVariable> GV1(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));GV0->replaceAllUsesWith(GV1.get());EXPECT_TRUE(MD->getValue() == GV1.get());// Reset it, so we don't inadvertently test deletion.MD.reset();}TEST_F(TrackingMDRefTest, UpdatesOnDeletion) {Type *Ty = Type::getInt1PtrTy(Context);std::unique_ptr<GlobalVariable> GV(new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));TypedTrackingMDRef<ValueAsMetadata> MD(ValueAsMetadata::get(GV.get()));EXPECT_TRUE(MD->getValue() == GV.get());ASSERT_TRUE(GV->use_empty());GV.reset();EXPECT_TRUE(!MD);}TEST(NamedMDNodeTest, Search) {LLVMContext Context;ConstantAsMetadata *C =ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(Context), 1));ConstantAsMetadata *C2 =ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(Context), 2));Metadata *const V = C;Metadata *const V2 = C2;MDNode *n = MDNode::get(Context, V);MDNode *n2 = MDNode::get(Context, V2);Module M("MyModule", Context);const char *Name = "llvm.NMD1";NamedMDNode *NMD = M.getOrInsertNamedMetadata(Name);NMD->addOperand(n);NMD->addOperand(n2);std::string Str;raw_string_ostream oss(Str);NMD->print(oss);EXPECT_STREQ("!llvm.NMD1 = !{!0, !1}\n",oss.str().c_str());}typedef MetadataTest FunctionAttachmentTest;TEST_F(FunctionAttachmentTest, setMetadata) {Function *F = getFunction("foo");ASSERT_FALSE(F->hasMetadata());EXPECT_EQ(nullptr, F->getMetadata(LLVMContext::MD_dbg));EXPECT_EQ(nullptr, F->getMetadata("dbg"));EXPECT_EQ(nullptr, F->getMetadata("other"));DISubprogram *SP1 = getSubprogram();DISubprogram *SP2 = getSubprogram();ASSERT_NE(SP1, SP2);F->setMetadata("dbg", SP1);EXPECT_TRUE(F->hasMetadata());EXPECT_EQ(SP1, F->getMetadata(LLVMContext::MD_dbg));EXPECT_EQ(SP1, F->getMetadata("dbg"));EXPECT_EQ(nullptr, F->getMetadata("other"));F->setMetadata(LLVMContext::MD_dbg, SP2);EXPECT_TRUE(F->hasMetadata());EXPECT_EQ(SP2, F->getMetadata(LLVMContext::MD_dbg));EXPECT_EQ(SP2, F->getMetadata("dbg"));EXPECT_EQ(nullptr, F->getMetadata("other"));F->setMetadata("dbg", nullptr);EXPECT_FALSE(F->hasMetadata());EXPECT_EQ(nullptr, F->getMetadata(LLVMContext::MD_dbg));EXPECT_EQ(nullptr, F->getMetadata("dbg"));EXPECT_EQ(nullptr, F->getMetadata("other"));MDTuple *T1 = getTuple();MDTuple *T2 = getTuple();ASSERT_NE(T1, T2);F->setMetadata("other1", T1);F->setMetadata("other2", T2);EXPECT_TRUE(F->hasMetadata());EXPECT_EQ(T1, F->getMetadata("other1"));EXPECT_EQ(T2, F->getMetadata("other2"));EXPECT_EQ(nullptr, F->getMetadata("dbg"));F->setMetadata("other1", T2);F->setMetadata("other2", T1);EXPECT_EQ(T2, F->getMetadata("other1"));EXPECT_EQ(T1, F->getMetadata("other2"));F->setMetadata("other1", nullptr);F->setMetadata("other2", nullptr);EXPECT_FALSE(F->hasMetadata());EXPECT_EQ(nullptr, F->getMetadata("other1"));EXPECT_EQ(nullptr, F->getMetadata("other2"));}TEST_F(FunctionAttachmentTest, getAll) {Function *F = getFunction("foo");MDTuple *T1 = getTuple();MDTuple *T2 = getTuple();MDTuple *P = getTuple();DISubprogram *SP = getSubprogram();F->setMetadata("other1", T2);F->setMetadata(LLVMContext::MD_dbg, SP);F->setMetadata("other2", T1);F->setMetadata(LLVMContext::MD_prof, P);F->setMetadata("other2", T2);F->setMetadata("other1", T1);SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;F->getAllMetadata(MDs);ASSERT_EQ(4u, MDs.size());EXPECT_EQ(LLVMContext::MD_dbg, MDs[0].first);EXPECT_EQ(LLVMContext::MD_prof, MDs[1].first);EXPECT_EQ(Context.getMDKindID("other1"), MDs[2].first);EXPECT_EQ(Context.getMDKindID("other2"), MDs[3].first);EXPECT_EQ(SP, MDs[0].second);EXPECT_EQ(P, MDs[1].second);EXPECT_EQ(T1, MDs[2].second);EXPECT_EQ(T2, MDs[3].second);}TEST_F(FunctionAttachmentTest, Verifier) {Function *F = getFunction("foo");F->setMetadata("attach", getTuple());F->setIsMaterializable(true);// Confirm this is materializable.ASSERT_TRUE(F->isMaterializable());// Materializable functions cannot have metadata attachments.EXPECT_TRUE(verifyFunction(*F));// Function declarations can.F->setIsMaterializable(false);EXPECT_FALSE(verifyModule(*F->getParent()));EXPECT_FALSE(verifyFunction(*F));// So can definitions.(void)new UnreachableInst(Context, BasicBlock::Create(Context, "bb", F));EXPECT_FALSE(verifyModule(*F->getParent()));EXPECT_FALSE(verifyFunction(*F));}TEST_F(FunctionAttachmentTest, RealEntryCount) {Function *F = getFunction("foo");EXPECT_FALSE(F->getEntryCount().has_value());F->setEntryCount(12304, Function::PCT_Real);auto Count = F->getEntryCount();EXPECT_TRUE(Count.has_value());EXPECT_EQ(12304u, Count->getCount());EXPECT_EQ(Function::PCT_Real, Count->getType());}TEST_F(FunctionAttachmentTest, SyntheticEntryCount) {Function *F = getFunction("bar");EXPECT_FALSE(F->getEntryCount().has_value());F->setEntryCount(123, Function::PCT_Synthetic);auto Count = F->getEntryCount(true /*allow synthetic*/);EXPECT_TRUE(Count.has_value());EXPECT_EQ(123u, Count->getCount());EXPECT_EQ(Function::PCT_Synthetic, Count->getType());}TEST_F(FunctionAttachmentTest, SubprogramAttachment) {Function *F = getFunction("foo");DISubprogram *SP = getSubprogram();F->setSubprogram(SP);// Note that the static_cast confirms that F->getSubprogram() actually// returns an DISubprogram.EXPECT_EQ(SP, static_cast<DISubprogram *>(F->getSubprogram()));EXPECT_EQ(SP, F->getMetadata("dbg"));EXPECT_EQ(SP, F->getMetadata(LLVMContext::MD_dbg));}typedef MetadataTest DistinctMDOperandPlaceholderTest;TEST_F(DistinctMDOperandPlaceholderTest, getID) {EXPECT_EQ(7u, DistinctMDOperandPlaceholder(7).getID());}TEST_F(DistinctMDOperandPlaceholderTest, replaceUseWith) {// Set up some placeholders.DistinctMDOperandPlaceholder PH0(7);DistinctMDOperandPlaceholder PH1(3);DistinctMDOperandPlaceholder PH2(0);Metadata *Ops[] = {&PH0, &PH1, &PH2};auto *D = MDTuple::getDistinct(Context, Ops);ASSERT_EQ(&PH0, D->getOperand(0));ASSERT_EQ(&PH1, D->getOperand(1));ASSERT_EQ(&PH2, D->getOperand(2));// Replace them.auto *N0 = MDTuple::get(Context, None);auto *N1 = MDTuple::get(Context, N0);PH0.replaceUseWith(N0);PH1.replaceUseWith(N1);PH2.replaceUseWith(nullptr);EXPECT_EQ(N0, D->getOperand(0));EXPECT_EQ(N1, D->getOperand(1));EXPECT_EQ(nullptr, D->getOperand(2));}TEST_F(DistinctMDOperandPlaceholderTest, replaceUseWithNoUser) {// There is no user, but we can still call replace.DistinctMDOperandPlaceholder(7).replaceUseWith(MDTuple::get(Context, None));}// Test various assertions in metadata tracking. Don't run these tests if gtest// will use SEH to recover from them. Two of these tests get halfway through// inserting metadata into DenseMaps for tracking purposes, and then they// assert, and we attempt to destroy an LLVMContext with broken invariants,// leading to infinite loops.#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) && !defined(GTEST_HAS_SEH)TEST_F(DistinctMDOperandPlaceholderTest, MetadataAsValue) {// This shouldn't crash.DistinctMDOperandPlaceholder PH(7);EXPECT_DEATH(MetadataAsValue::get(Context, &PH),"Unexpected callback to owner");}TEST_F(DistinctMDOperandPlaceholderTest, UniquedMDNode) {// This shouldn't crash.DistinctMDOperandPlaceholder PH(7);EXPECT_DEATH(MDTuple::get(Context, &PH), "Unexpected callback to owner");}TEST_F(DistinctMDOperandPlaceholderTest, SecondDistinctMDNode) {// This shouldn't crash.DistinctMDOperandPlaceholder PH(7);MDTuple::getDistinct(Context, &PH);EXPECT_DEATH(MDTuple::getDistinct(Context, &PH),"Placeholders can only be used once");}TEST_F(DistinctMDOperandPlaceholderTest, TrackingMDRefAndDistinctMDNode) {// TrackingMDRef doesn't install an owner callback, so it can't be detected// as an invalid use. However, using a placeholder in a TrackingMDRef *and*// a distinct node isn't possible and we should assert.//// (There's no positive test for using TrackingMDRef because it's not a// useful thing to do.){DistinctMDOperandPlaceholder PH(7);MDTuple::getDistinct(Context, &PH);EXPECT_DEATH(TrackingMDRef Ref(&PH), "Placeholders can only be used once");}{DistinctMDOperandPlaceholder PH(7);TrackingMDRef Ref(&PH);EXPECT_DEATH(MDTuple::getDistinct(Context, &PH),"Placeholders can only be used once");}}#endiftypedef MetadataTest DebugVariableTest;TEST_F(DebugVariableTest, DenseMap) {DenseMap<DebugVariable, uint64_t> DebugVariableMap;DILocalScope *Scope = getSubprogram();DIFile *File = getFile();DIType *Type = getDerivedType();DINode::DIFlags Flags = static_cast<DINode::DIFlags>(7);DILocation *InlinedLoc = DILocation::get(Context, 2, 7, Scope);DILocalVariable *VarA =DILocalVariable::get(Context, Scope, "A", File, 5, Type, 2, Flags, 8, nullptr);DILocalVariable *VarB =DILocalVariable::get(Context, Scope, "B", File, 7, Type, 3, Flags, 8, nullptr);DebugVariable DebugVariableA(VarA, NoneType(), nullptr);DebugVariable DebugVariableInlineA(VarA, NoneType(), InlinedLoc);DebugVariable DebugVariableB(VarB, NoneType(), nullptr);DebugVariable DebugVariableFragB(VarB, {{16, 16}}, nullptr);DebugVariableMap.insert({DebugVariableA, 2});DebugVariableMap.insert({DebugVariableInlineA, 3});DebugVariableMap.insert({DebugVariableB, 6});DebugVariableMap.insert({DebugVariableFragB, 12});EXPECT_EQ(DebugVariableMap.count(DebugVariableA), 1u);EXPECT_EQ(DebugVariableMap.count(DebugVariableInlineA), 1u);EXPECT_EQ(DebugVariableMap.count(DebugVariableB), 1u);EXPECT_EQ(DebugVariableMap.count(DebugVariableFragB), 1u);EXPECT_EQ(DebugVariableMap.find(DebugVariableA)->second, 2u);EXPECT_EQ(DebugVariableMap.find(DebugVariableInlineA)->second, 3u);EXPECT_EQ(DebugVariableMap.find(DebugVariableB)->second, 6u);EXPECT_EQ(DebugVariableMap.find(DebugVariableFragB)->second, 12u);}typedef MetadataTest MDTupleAllocationTest;TEST_F(MDTupleAllocationTest, Tracking) {// Make sure that the move constructor and move assignment op// for MDOperand correctly adjust tracking information.auto *Value1 = getConstantAsMetadata();MDTuple *A = MDTuple::getDistinct(Context, {Value1, Value1});EXPECT_EQ(A->getOperand(0), Value1);EXPECT_EQ(A->getOperand(1), Value1);MDNode::op_range Ops = A->operands();MDOperand NewOps1;// Move assignment operator.NewOps1 = std::move(*const_cast<MDOperand *>(Ops.begin()));// Move constructor.MDOperand NewOps2(std::move(*const_cast<MDOperand *>(Ops.begin() + 1)));EXPECT_EQ(NewOps1.get(), static_cast<Metadata *>(Value1));EXPECT_EQ(NewOps2.get(), static_cast<Metadata *>(Value1));auto *Value2 = getConstantAsMetadata();Value *V1 = Value1->getValue();Value *V2 = Value2->getValue();ValueAsMetadata::handleRAUW(V1, V2);EXPECT_EQ(NewOps1.get(), static_cast<Metadata *>(Value2));EXPECT_EQ(NewOps2.get(), static_cast<Metadata *>(Value2));}TEST_F(MDTupleAllocationTest, Resize) {MDTuple *A = getTuple();Metadata *Value1 = getConstantAsMetadata();Metadata *Value2 = getConstantAsMetadata();Metadata *Value3 = getConstantAsMetadata();EXPECT_EQ(A->getNumOperands(), 0u);// Add a couple of elements to it, which resizes the node.A->push_back(Value1);EXPECT_EQ(A->getNumOperands(), 1u);EXPECT_EQ(A->getOperand(0), Value1);A->push_back(Value2);EXPECT_EQ(A->getNumOperands(), 2u);EXPECT_EQ(A->getOperand(0), Value1);EXPECT_EQ(A->getOperand(1), Value2);// Append another element, which should resize the node// to a "large" node, though not detectable by the user.A->push_back(Value3);EXPECT_EQ(A->getNumOperands(), 3u);EXPECT_EQ(A->getOperand(0), Value1);EXPECT_EQ(A->getOperand(1), Value2);EXPECT_EQ(A->getOperand(2), Value3);// Remove the last elementA->pop_back();EXPECT_EQ(A->getNumOperands(), 2u);EXPECT_EQ(A->getOperand(1), Value2);// Allocate a node with 4 operands.Metadata *Value4 = getConstantAsMetadata();Metadata *Value5 = getConstantAsMetadata();Metadata *Ops[] = {Value1, Value2, Value3, Value4};MDTuple *B = MDTuple::getDistinct(Context, Ops);EXPECT_EQ(B->getNumOperands(), 4u);B->pop_back();EXPECT_EQ(B->getNumOperands(), 3u);B->push_back(Value5);EXPECT_EQ(B->getNumOperands(), 4u);EXPECT_EQ(B->getOperand(0), Value1);EXPECT_EQ(B->getOperand(1), Value2);EXPECT_EQ(B->getOperand(2), Value3);EXPECT_EQ(B->getOperand(3), Value5);// Check that we can resize temporary nodes as well.auto Temp1 = MDTuple::getTemporary(Context, None);EXPECT_EQ(Temp1->getNumOperands(), 0u);Temp1->push_back(Value1);EXPECT_EQ(Temp1->getNumOperands(), 1u);EXPECT_EQ(Temp1->getOperand(0), Value1);for (int i = 0; i < 11; i++)Temp1->push_back(Value2);EXPECT_EQ(Temp1->getNumOperands(), 12u);EXPECT_EQ(Temp1->getOperand(2), Value2);EXPECT_EQ(Temp1->getOperand(11), Value2);// Allocate a node that starts off as a large one.Metadata *OpsLarge[] = {Value1, Value2, Value3, Value4,Value1, Value2, Value3, Value4,Value1, Value2, Value3, Value4,Value1, Value2, Value3, Value4,Value1, Value2, Value3, Value4};MDTuple *C = MDTuple::getDistinct(Context, OpsLarge);EXPECT_EQ(C->getNumOperands(), 20u);EXPECT_EQ(C->getOperand(7), Value4);EXPECT_EQ(C->getOperand(13), Value2);C->push_back(Value1);C->push_back(Value2);EXPECT_EQ(C->getNumOperands(), 22u);EXPECT_EQ(C->getOperand(21), Value2);C->pop_back();EXPECT_EQ(C->getNumOperands(), 21u);EXPECT_EQ(C->getOperand(20), Value1);}TEST_F(MDTupleAllocationTest, Tracking2) {// Resize a tuple and check that we can still RAUW one of its operands.auto *Value1 = getConstantAsMetadata();MDTuple *A = getTuple();A->push_back(Value1);A->push_back(Value1);A->push_back(Value1); // Causes a resize to large.EXPECT_EQ(A->getOperand(0), Value1);EXPECT_EQ(A->getOperand(1), Value1);EXPECT_EQ(A->getOperand(2), Value1);auto *Value2 = getConstantAsMetadata();Value *V1 = Value1->getValue();Value *V2 = Value2->getValue();ValueAsMetadata::handleRAUW(V1, V2);EXPECT_EQ(A->getOperand(0), Value2);EXPECT_EQ(A->getOperand(1), Value2);EXPECT_EQ(A->getOperand(2), Value2);}#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) && !defined(GTEST_HAS_SEH)typedef MetadataTest MDTupleAllocationDeathTest;TEST_F(MDTupleAllocationDeathTest, ResizeRejected) {MDTuple *A = MDTuple::get(Context, None);auto *Value1 = getConstantAsMetadata();EXPECT_DEATH(A->push_back(Value1),"Resizing is not supported for uniqued nodes");// Check that a node, which has been allocated as a temporary,// cannot be resized after it has been uniqued.auto *Value2 = getConstantAsMetadata();auto B = MDTuple::getTemporary(Context, {Value2});B->push_back(Value2);MDTuple *BUniqued = MDNode::replaceWithUniqued(std::move(B));EXPECT_EQ(BUniqued->getNumOperands(), 2u);EXPECT_EQ(BUniqued->getOperand(1), Value2);EXPECT_DEATH(BUniqued->push_back(Value2),"Resizing is not supported for uniqued nodes");}#endif} // end namespace
//===- llvm/unittest/IR/ManglerTest.cpp - Mangler unit tests --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Mangler.h"#include "llvm/IR/CallingConv.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/GlobalValue.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;static std::string mangleStr(StringRef IRName, Mangler &Mang,const DataLayout &DL) {std::string Mangled;raw_string_ostream SS(Mangled);Mang.getNameWithPrefix(SS, IRName, DL);SS.flush();return Mangled;}static std::string mangleFunc(StringRef IRName,GlobalValue::LinkageTypes Linkage,llvm::CallingConv::ID CC, Module &Mod,Mangler &Mang) {Type *VoidTy = Type::getVoidTy(Mod.getContext());Type *I32Ty = Type::getInt32Ty(Mod.getContext());FunctionType *FTy =FunctionType::get(VoidTy, {I32Ty, I32Ty, I32Ty}, /*isVarArg=*/false);Function *F = Function::Create(FTy, Linkage, IRName, &Mod);F->setCallingConv(CC);std::string Mangled;raw_string_ostream SS(Mangled);Mang.getNameWithPrefix(SS, F, false);SS.flush();F->eraseFromParent();return Mangled;}namespace {TEST(ManglerTest, MachO) {LLVMContext Ctx;DataLayout DL("m:o"); // machoModule Mod("test", Ctx);Mod.setDataLayout(DL);Mangler Mang;EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("?foo", Mang, DL), "_?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"_foo");EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"_?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,llvm::CallingConv::C, Mod, Mang),"L_foo");}TEST(ManglerTest, WindowsX86) {LLVMContext Ctx;DataLayout DL("m:x-p:32:32"); // 32-bit windowsModule Mod("test", Ctx);Mod.setDataLayout(DL);Mangler Mang;EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"_foo");EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,llvm::CallingConv::C, Mod, Mang),"L_foo");// Test calling conv mangling.EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_StdCall, Mod, Mang),"_stdcall@12");EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_FastCall, Mod, Mang),"@fastcall@12");EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_VectorCall, Mod, Mang),"vectorcall@@12");// Adding a '?' prefix blocks calling convention mangling.EXPECT_EQ(mangleFunc("?fastcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_FastCall, Mod, Mang),"?fastcall");}TEST(ManglerTest, WindowsX64) {LLVMContext Ctx;DataLayout DL("m:w-p:64:64"); // windowsModule Mod("test", Ctx);Mod.setDataLayout(DL);Mangler Mang;EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"foo");EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,llvm::CallingConv::C, Mod, Mang),".Lfoo");// Test calling conv mangling.EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_StdCall, Mod, Mang),"stdcall");EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_FastCall, Mod, Mang),"fastcall");EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_VectorCall, Mod, Mang),"vectorcall@@24");// Adding a '?' prefix blocks calling convention mangling.EXPECT_EQ(mangleFunc("?vectorcall", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::X86_VectorCall, Mod, Mang),"?vectorcall");}TEST(ManglerTest, XCOFF) {LLVMContext Ctx;DataLayout DL("m:a"); // XCOFF/AIXModule Mod("test", Ctx);Mod.setDataLayout(DL);Mangler Mang;EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"foo");EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,llvm::CallingConv::C, Mod, Mang),"L..foo");}TEST(ManglerTest, GOFF) {LLVMContext Ctx;DataLayout DL("m:l"); // GOFFModule Mod("test", Ctx);Mod.setDataLayout(DL);Mangler Mang;EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,llvm::CallingConv::C, Mod, Mang),"foo");EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,llvm::CallingConv::C, Mod, Mang),"@foo");}} // end anonymous namespace
//===- llvm/unittests/MDBuilderTest.cpp - MDBuilder unit tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/MDBuilder.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Metadata.h"#include "llvm/IR/Operator.h"#include "gtest/gtest.h"using namespace llvm;namespace {class MDBuilderTest : public testing::Test {protected:LLVMContext Context;};TEST_F(MDBuilderTest, createString) {MDBuilder MDHelper(Context);MDString *Str0 = MDHelper.createString("");MDString *Str1 = MDHelper.createString("string");EXPECT_EQ(Str0->getString(), StringRef(""));EXPECT_EQ(Str1->getString(), StringRef("string"));}TEST_F(MDBuilderTest, createFPMath) {MDBuilder MDHelper(Context);MDNode *MD0 = MDHelper.createFPMath(0.0);MDNode *MD1 = MDHelper.createFPMath(1.0);EXPECT_EQ(MD0, (MDNode *)nullptr);EXPECT_NE(MD1, (MDNode *)nullptr);EXPECT_EQ(MD1->getNumOperands(), 1U);Metadata *Op = MD1->getOperand(0);EXPECT_TRUE(mdconst::hasa<ConstantFP>(Op));ConstantFP *Val = mdconst::extract<ConstantFP>(Op);EXPECT_TRUE(Val->getType()->isFloatingPointTy());EXPECT_TRUE(Val->isExactlyValue(1.0));}TEST_F(MDBuilderTest, createRangeMetadata) {MDBuilder MDHelper(Context);APInt A(8, 1), B(8, 2);MDNode *R0 = MDHelper.createRange(A, A);MDNode *R1 = MDHelper.createRange(A, B);EXPECT_EQ(R0, (MDNode *)nullptr);EXPECT_NE(R1, (MDNode *)nullptr);EXPECT_EQ(R1->getNumOperands(), 2U);EXPECT_TRUE(mdconst::hasa<ConstantInt>(R1->getOperand(0)));EXPECT_TRUE(mdconst::hasa<ConstantInt>(R1->getOperand(1)));ConstantInt *C0 = mdconst::extract<ConstantInt>(R1->getOperand(0));ConstantInt *C1 = mdconst::extract<ConstantInt>(R1->getOperand(1));EXPECT_EQ(C0->getValue(), A);EXPECT_EQ(C1->getValue(), B);}TEST_F(MDBuilderTest, createAnonymousTBAARoot) {MDBuilder MDHelper(Context);MDNode *R0 = MDHelper.createAnonymousTBAARoot();MDNode *R1 = MDHelper.createAnonymousTBAARoot();EXPECT_NE(R0, R1);EXPECT_GE(R0->getNumOperands(), 1U);EXPECT_GE(R1->getNumOperands(), 1U);EXPECT_EQ(R0->getOperand(0), R0);EXPECT_EQ(R1->getOperand(0), R1);EXPECT_TRUE(R0->getNumOperands() == 1 || R0->getOperand(1) == nullptr);EXPECT_TRUE(R1->getNumOperands() == 1 || R1->getOperand(1) == nullptr);}TEST_F(MDBuilderTest, createTBAARoot) {MDBuilder MDHelper(Context);MDNode *R0 = MDHelper.createTBAARoot("Root");MDNode *R1 = MDHelper.createTBAARoot("Root");EXPECT_EQ(R0, R1);EXPECT_GE(R0->getNumOperands(), 1U);EXPECT_TRUE(isa<MDString>(R0->getOperand(0)));EXPECT_EQ(cast<MDString>(R0->getOperand(0))->getString(), "Root");EXPECT_TRUE(R0->getNumOperands() == 1 || R0->getOperand(1) == nullptr);}TEST_F(MDBuilderTest, createTBAANode) {MDBuilder MDHelper(Context);MDNode *R = MDHelper.createTBAARoot("Root");MDNode *N0 = MDHelper.createTBAANode("Node", R);MDNode *N1 = MDHelper.createTBAANode("edoN", R);MDNode *N2 = MDHelper.createTBAANode("Node", R, true);MDNode *N3 = MDHelper.createTBAANode("Node", R);EXPECT_EQ(N0, N3);EXPECT_NE(N0, N1);EXPECT_NE(N0, N2);EXPECT_GE(N0->getNumOperands(), 2U);EXPECT_GE(N1->getNumOperands(), 2U);EXPECT_GE(N2->getNumOperands(), 3U);EXPECT_TRUE(isa<MDString>(N0->getOperand(0)));EXPECT_TRUE(isa<MDString>(N1->getOperand(0)));EXPECT_TRUE(isa<MDString>(N2->getOperand(0)));EXPECT_EQ(cast<MDString>(N0->getOperand(0))->getString(), "Node");EXPECT_EQ(cast<MDString>(N1->getOperand(0))->getString(), "edoN");EXPECT_EQ(cast<MDString>(N2->getOperand(0))->getString(), "Node");EXPECT_EQ(N0->getOperand(1), R);EXPECT_EQ(N1->getOperand(1), R);EXPECT_EQ(N2->getOperand(1), R);EXPECT_TRUE(mdconst::hasa<ConstantInt>(N2->getOperand(2)));EXPECT_EQ(mdconst::extract<ConstantInt>(N2->getOperand(2))->getZExtValue(),1U);}}
//===- llvm/unittest/IR/LegacyPassManager.cpp - Legacy PassManager tests --===////// 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////===----------------------------------------------------------------------===////// This unit test exercises the legacy pass manager infrastructure. We use the// old names as well to ensure that the source-level compatibility is preserved// where possible.////===----------------------------------------------------------------------===//#include "llvm/IR/LegacyPassManager.h"#include "llvm/Analysis/CallGraph.h"#include "llvm/Analysis/CallGraphSCCPass.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/LoopPass.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/AbstractCallSite.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/CallingConv.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/Function.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/OptBisect.h"#include "llvm/InitializePasses.h"#include "llvm/Support/MathExtras.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Transforms/Utils/CallGraphUpdater.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {void initializeModuleNDMPass(PassRegistry&);void initializeFPassPass(PassRegistry&);void initializeCGPassPass(PassRegistry&);void initializeLPassPass(PassRegistry&);namespace {// ND = no deps// NM = no modificationsstruct ModuleNDNM: public ModulePass {public:static char run;static char ID;ModuleNDNM() : ModulePass(ID) { }bool runOnModule(Module &M) override {run++;return false;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();}};char ModuleNDNM::ID=0;char ModuleNDNM::run=0;struct ModuleNDM : public ModulePass {public:static char run;static char ID;ModuleNDM() : ModulePass(ID) {}bool runOnModule(Module &M) override {run++;return true;}};char ModuleNDM::ID=0;char ModuleNDM::run=0;struct ModuleNDM2 : public ModulePass {public:static char run;static char ID;ModuleNDM2() : ModulePass(ID) {}bool runOnModule(Module &M) override {run++;return true;}};char ModuleNDM2::ID=0;char ModuleNDM2::run=0;struct ModuleDNM : public ModulePass {public:static char run;static char ID;ModuleDNM() : ModulePass(ID) {initializeModuleNDMPass(*PassRegistry::getPassRegistry());}bool runOnModule(Module &M) override {run++;return false;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.addRequired<ModuleNDM>();AU.setPreservesAll();}};char ModuleDNM::ID=0;char ModuleDNM::run=0;template<typename P>struct PassTestBase : public P {protected:static int runc;static bool initialized;static bool finalized;int allocated;void run() {EXPECT_TRUE(initialized);EXPECT_FALSE(finalized);EXPECT_EQ(0, allocated);allocated++;runc++;}public:static char ID;static void finishedOK(int run) {EXPECT_GT(runc, 0);EXPECT_TRUE(initialized);EXPECT_TRUE(finalized);EXPECT_EQ(run, runc);}PassTestBase() : P(ID), allocated(0) {initialized = false;finalized = false;runc = 0;}void releaseMemory() override {EXPECT_GT(runc, 0);EXPECT_GT(allocated, 0);allocated--;}};template<typename P> char PassTestBase<P>::ID;template<typename P> int PassTestBase<P>::runc;template<typename P> bool PassTestBase<P>::initialized;template<typename P> bool PassTestBase<P>::finalized;template<typename T, typename P>struct PassTest : public PassTestBase<P> {public:#ifndef _MSC_VER // MSVC complains that Pass is not base class.using llvm::Pass::doInitialization;using llvm::Pass::doFinalization;#endifbool doInitialization(T &t) override {EXPECT_FALSE(PassTestBase<P>::initialized);PassTestBase<P>::initialized = true;return false;}bool doFinalization(T &t) override {EXPECT_FALSE(PassTestBase<P>::finalized);PassTestBase<P>::finalized = true;EXPECT_EQ(0, PassTestBase<P>::allocated);return false;}};struct CGPass : public PassTest<CallGraph, CallGraphSCCPass> {public:CGPass() {initializeCGPassPass(*PassRegistry::getPassRegistry());}bool runOnSCC(CallGraphSCC &SCMM) override {run();return false;}};struct FPass : public PassTest<Module, FunctionPass> {public:bool runOnFunction(Function &F) override {// FIXME: PR4112// EXPECT_TRUE(getAnalysisIfAvailable<DataLayout>());run();return false;}};struct LPass : public PassTestBase<LoopPass> {private:static int initcount;static int fincount;public:LPass() {initializeLPassPass(*PassRegistry::getPassRegistry());initcount = 0; fincount=0;EXPECT_FALSE(initialized);}static void finishedOK(int run, int finalized) {PassTestBase<LoopPass>::finishedOK(run);EXPECT_EQ(run, initcount);EXPECT_EQ(finalized, fincount);}using llvm::Pass::doInitialization;using llvm::Pass::doFinalization;bool doInitialization(Loop* L, LPPassManager &LPM) override {initialized = true;initcount++;return false;}bool runOnLoop(Loop *L, LPPassManager &LPM) override {run();return false;}bool doFinalization() override {fincount++;finalized = true;return false;}};int LPass::initcount=0;int LPass::fincount=0;struct OnTheFlyTest: public ModulePass {public:static char ID;OnTheFlyTest() : ModulePass(ID) {initializeFPassPass(*PassRegistry::getPassRegistry());}bool runOnModule(Module &M) override {for (Module::iterator I=M.begin(),E=M.end(); I != E; ++I) {Function &F = *I;{SCOPED_TRACE("Running on the fly function pass");getAnalysis<FPass>(F);}}return false;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.addRequired<FPass>();}};char OnTheFlyTest::ID=0;TEST(PassManager, RunOnce) {LLVMContext Context;Module M("test-once", Context);struct ModuleNDNM *mNDNM = new ModuleNDNM();struct ModuleDNM *mDNM = new ModuleDNM();struct ModuleNDM *mNDM = new ModuleNDM();struct ModuleNDM2 *mNDM2 = new ModuleNDM2();mNDM->run = mNDNM->run = mDNM->run = mNDM2->run = 0;legacy::PassManager Passes;Passes.add(mNDM2);Passes.add(mNDM);Passes.add(mNDNM);Passes.add(mDNM);Passes.run(M);// each pass must be run exactly once, since nothing invalidates themEXPECT_EQ(1, mNDM->run);EXPECT_EQ(1, mNDNM->run);EXPECT_EQ(1, mDNM->run);EXPECT_EQ(1, mNDM2->run);}TEST(PassManager, ReRun) {LLVMContext Context;Module M("test-rerun", Context);struct ModuleNDNM *mNDNM = new ModuleNDNM();struct ModuleDNM *mDNM = new ModuleDNM();struct ModuleNDM *mNDM = new ModuleNDM();struct ModuleNDM2 *mNDM2 = new ModuleNDM2();mNDM->run = mNDNM->run = mDNM->run = mNDM2->run = 0;legacy::PassManager Passes;Passes.add(mNDM);Passes.add(mNDNM);Passes.add(mNDM2);// invalidates mNDM needed by mDNMPasses.add(mDNM);Passes.run(M);// Some passes must be rerun because a pass that modified the// module/function was run in betweenEXPECT_EQ(2, mNDM->run);EXPECT_EQ(1, mNDNM->run);EXPECT_EQ(1, mNDM2->run);EXPECT_EQ(1, mDNM->run);}Module *makeLLVMModule(LLVMContext &Context);template<typename T>void MemoryTestHelper(int run) {LLVMContext Context;std::unique_ptr<Module> M(makeLLVMModule(Context));T *P = new T();legacy::PassManager Passes;Passes.add(P);Passes.run(*M);T::finishedOK(run);}template<typename T>void MemoryTestHelper(int run, int N) {LLVMContext Context;Module *M = makeLLVMModule(Context);T *P = new T();legacy::PassManager Passes;Passes.add(P);Passes.run(*M);T::finishedOK(run, N);delete M;}TEST(PassManager, Memory) {// SCC#1: test1->test2->test3->test1// SCC#2: test4// SCC#3: indirect call node{SCOPED_TRACE("Callgraph pass");MemoryTestHelper<CGPass>(3);}{SCOPED_TRACE("Function pass");MemoryTestHelper<FPass>(4);// 4 functions}{SCOPED_TRACE("Loop pass");MemoryTestHelper<LPass>(2, 1); //2 loops, 1 function}}TEST(PassManager, MemoryOnTheFly) {LLVMContext Context;Module *M = makeLLVMModule(Context);{SCOPED_TRACE("Running OnTheFlyTest");struct OnTheFlyTest *O = new OnTheFlyTest();legacy::PassManager Passes;Passes.add(O);Passes.run(*M);FPass::finishedOK(4);}delete M;}// Skips or runs optional passes.struct CustomOptPassGate : public OptPassGate {bool Skip;CustomOptPassGate(bool Skip) : Skip(Skip) { }bool shouldRunPass(const Pass *P, StringRef IRDescription) override {if (P->getPassKind() == PT_Module)return !Skip;return OptPassGate::shouldRunPass(P, IRDescription);}bool isEnabled() const override { return true; }};// Optional module pass.struct ModuleOpt: public ModulePass {char run = 0;static char ID;ModuleOpt() : ModulePass(ID) { }bool runOnModule(Module &M) override {if (!skipModule(M))run++;return false;}};char ModuleOpt::ID=0;TEST(PassManager, CustomOptPassGate) {LLVMContext Context0;LLVMContext Context1;LLVMContext Context2;CustomOptPassGate SkipOptionalPasses(true);CustomOptPassGate RunOptionalPasses(false);Module M0("custom-opt-bisect", Context0);Module M1("custom-opt-bisect", Context1);Module M2("custom-opt-bisect2", Context2);struct ModuleOpt *mOpt0 = new ModuleOpt();struct ModuleOpt *mOpt1 = new ModuleOpt();struct ModuleOpt *mOpt2 = new ModuleOpt();mOpt0->run = mOpt1->run = mOpt2->run = 0;legacy::PassManager Passes0;legacy::PassManager Passes1;legacy::PassManager Passes2;Passes0.add(mOpt0);Passes1.add(mOpt1);Passes2.add(mOpt2);Context1.setOptPassGate(SkipOptionalPasses);Context2.setOptPassGate(RunOptionalPasses);Passes0.run(M0);Passes1.run(M1);Passes2.run(M2);// By default optional passes are run.EXPECT_EQ(1, mOpt0->run);// The first context skips optional passes.EXPECT_EQ(0, mOpt1->run);// The second context runs optional passes.EXPECT_EQ(1, mOpt2->run);}Module *makeLLVMModule(LLVMContext &Context) {// Module ConstructionModule *mod = new Module("test-mem", Context);mod->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-""i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-""a:0:64-s:64:64-f80:128:128");mod->setTargetTriple("x86_64-unknown-linux-gnu");// Type Definitionsstd::vector<Type*>FuncTy_0_args;FunctionType *FuncTy_0 = FunctionType::get(/*Result=*/IntegerType::get(Context, 32),/*Params=*/FuncTy_0_args,/*isVarArg=*/false);std::vector<Type*>FuncTy_2_args;FuncTy_2_args.push_back(IntegerType::get(Context, 1));FunctionType *FuncTy_2 = FunctionType::get(/*Result=*/Type::getVoidTy(Context),/*Params=*/FuncTy_2_args,/*isVarArg=*/false);// Function DeclarationsFunction* func_test1 = Function::Create(/*Type=*/FuncTy_0,/*Linkage=*/GlobalValue::ExternalLinkage,/*Name=*/"test1", mod);func_test1->setCallingConv(CallingConv::C);AttributeList func_test1_PAL;func_test1->setAttributes(func_test1_PAL);Function* func_test2 = Function::Create(/*Type=*/FuncTy_0,/*Linkage=*/GlobalValue::ExternalLinkage,/*Name=*/"test2", mod);func_test2->setCallingConv(CallingConv::C);AttributeList func_test2_PAL;func_test2->setAttributes(func_test2_PAL);Function* func_test3 = Function::Create(/*Type=*/FuncTy_0,/*Linkage=*/GlobalValue::InternalLinkage,/*Name=*/"test3", mod);func_test3->setCallingConv(CallingConv::C);AttributeList func_test3_PAL;func_test3->setAttributes(func_test3_PAL);Function* func_test4 = Function::Create(/*Type=*/FuncTy_2,/*Linkage=*/GlobalValue::ExternalLinkage,/*Name=*/"test4", mod);func_test4->setCallingConv(CallingConv::C);AttributeList func_test4_PAL;func_test4->setAttributes(func_test4_PAL);// Global Variable Declarations// Constant Definitions// Global Variable Definitions// Function Definitions// Function: test1 (func_test1){BasicBlock *label_entry =BasicBlock::Create(Context, "entry", func_test1, nullptr);// Block entry (label_entry)CallInst* int32_3 = CallInst::Create(func_test2, "", label_entry);int32_3->setCallingConv(CallingConv::C);int32_3->setTailCall(false);AttributeList int32_3_PAL;int32_3->setAttributes(int32_3_PAL);ReturnInst::Create(Context, int32_3, label_entry);}// Function: test2 (func_test2){BasicBlock *label_entry_5 =BasicBlock::Create(Context, "entry", func_test2, nullptr);// Block entry (label_entry_5)CallInst* int32_6 = CallInst::Create(func_test3, "", label_entry_5);int32_6->setCallingConv(CallingConv::C);int32_6->setTailCall(false);AttributeList int32_6_PAL;int32_6->setAttributes(int32_6_PAL);ReturnInst::Create(Context, int32_6, label_entry_5);}// Function: test3 (func_test3){BasicBlock *label_entry_8 =BasicBlock::Create(Context, "entry", func_test3, nullptr);// Block entry (label_entry_8)CallInst* int32_9 = CallInst::Create(func_test1, "", label_entry_8);int32_9->setCallingConv(CallingConv::C);int32_9->setTailCall(false);AttributeList int32_9_PAL;int32_9->setAttributes(int32_9_PAL);ReturnInst::Create(Context, int32_9, label_entry_8);}// Function: test4 (func_test4){Function::arg_iterator args = func_test4->arg_begin();Value *int1_f = &*args++;int1_f->setName("f");BasicBlock *label_entry_11 =BasicBlock::Create(Context, "entry", func_test4, nullptr);BasicBlock *label_bb =BasicBlock::Create(Context, "bb", func_test4, nullptr);BasicBlock *label_bb1 =BasicBlock::Create(Context, "bb1", func_test4, nullptr);BasicBlock *label_return =BasicBlock::Create(Context, "return", func_test4, nullptr);// Block entry (label_entry_11)auto *AI = new AllocaInst(func_test3->getType(), 0, "func3ptr",label_entry_11);new StoreInst(func_test3, AI, label_entry_11);BranchInst::Create(label_bb, label_entry_11);// Block bb (label_bb)BranchInst::Create(label_bb, label_bb1, int1_f, label_bb);// Block bb1 (label_bb1)BranchInst::Create(label_bb1, label_return, int1_f, label_bb1);// Block return (label_return)ReturnInst::Create(Context, label_return);}return mod;}/// Split a simple function which contains only a call and a return into two/// such that the first calls the second and the second whoever was called/// initially.Function *splitSimpleFunction(Function &F) {LLVMContext &Context = F.getContext();Function *SF = Function::Create(F.getFunctionType(), F.getLinkage(),F.getName() + "b", F.getParent());F.setName(F.getName() + "a");BasicBlock *Entry = BasicBlock::Create(Context, "entry", SF, nullptr);CallInst &CI = cast<CallInst>(F.getEntryBlock().front());CI.clone()->insertBefore(ReturnInst::Create(Context, Entry));CI.setCalledFunction(SF);return SF;}struct CGModifierPass : public CGPass {unsigned NumSCCs = 0;unsigned NumFns = 0;unsigned NumFnDecls = 0;unsigned SetupWorked = 0;unsigned NumExtCalledBefore = 0;unsigned NumExtCalledAfter = 0;CallGraphUpdater CGU;bool runOnSCC(CallGraphSCC &SCMM) override {++NumSCCs;for (CallGraphNode *N : SCMM) {if (N->getFunction()){++NumFns;NumFnDecls += N->getFunction()->isDeclaration();}}CGPass::run();CallGraph &CG = const_cast<CallGraph &>(SCMM.getCallGraph());CallGraphNode *ExtCallingNode = CG.getExternalCallingNode();NumExtCalledBefore = ExtCallingNode->size();if (SCMM.size() <= 1)return false;CallGraphNode *N = *(SCMM.begin());Function *F = N->getFunction();Module *M = F->getParent();Function *Test1F = M->getFunction("test1");Function *Test2aF = M->getFunction("test2a");Function *Test2bF = M->getFunction("test2b");Function *Test3F = M->getFunction("test3");auto InSCC = [&](Function *Fn) {return llvm::any_of(SCMM, [Fn](CallGraphNode *CGN) {return CGN->getFunction() == Fn;});};if (!Test1F || !Test2aF || !Test2bF || !Test3F || !InSCC(Test1F) ||!InSCC(Test2aF) || !InSCC(Test2bF) || !InSCC(Test3F))return false;CallInst *CI = dyn_cast<CallInst>(&Test1F->getEntryBlock().front());if (!CI || CI->getCalledFunction() != Test2aF)return false;SetupWorked += 1;// Create a replica of test3 and just move the blocks there.Function *Test3FRepl = Function::Create(/*Type=*/Test3F->getFunctionType(),/*Linkage=*/GlobalValue::InternalLinkage,/*Name=*/"test3repl", Test3F->getParent());while (!Test3F->empty()) {BasicBlock &BB = Test3F->front();BB.removeFromParent();BB.insertInto(Test3FRepl);}CGU.initialize(CG, SCMM);// Replace test3 with the replica. This is legal as it is actually// internal and the "capturing use" is not really capturing anything.CGU.replaceFunctionWith(*Test3F, *Test3FRepl);Test3F->replaceAllUsesWith(Test3FRepl);// Rewrite the call in test1 to point to the replica of 3 not test2.CI->setCalledFunction(Test3FRepl);// Delete test2a and test2b and reanalyze 1 as we changed calls inside.CGU.removeFunction(*Test2aF);CGU.removeFunction(*Test2bF);CGU.reanalyzeFunction(*Test1F);return true;}bool doFinalization(CallGraph &CG) override {CGU.finalize();// We removed test2 and replaced the internal test3.NumExtCalledAfter = CG.getExternalCallingNode()->size();return true;}};TEST(PassManager, CallGraphUpdater0) {// SCC#1: test1->test2a->test2b->test3->test1// SCC#2: test4// SCC#3: test3 (the empty function declaration as we replaced it with// test3repl when we visited SCC#1)// SCC#4: test2a->test2b (the empty function declarations as we deleted// these functions when we visited SCC#1)// SCC#5: indirect call nodeLLVMContext Context;std::unique_ptr<Module> M(makeLLVMModule(Context));ASSERT_EQ(M->getFunctionList().size(), 4U);Function *F = M->getFunction("test2");Function *SF = splitSimpleFunction(*F);CallInst::Create(F, "", &*SF->getEntryBlock().getFirstInsertionPt());ASSERT_EQ(M->getFunctionList().size(), 5U);CGModifierPass *P = new CGModifierPass();legacy::PassManager Passes;Passes.add(P);Passes.run(*M);ASSERT_EQ(P->SetupWorked, 1U);ASSERT_EQ(P->NumSCCs, 4U);ASSERT_EQ(P->NumFns, 6U);ASSERT_EQ(P->NumFnDecls, 1U);ASSERT_EQ(M->getFunctionList().size(), 3U);ASSERT_EQ(P->NumExtCalledBefore, /* test1, 2a, 2b, 3, 4 */ 5U);ASSERT_EQ(P->NumExtCalledAfter, /* test1, 3repl, 4 */ 3U);}// Test for call graph SCC pass that replaces all callback call instructions// with clones and updates CallGraph by calling CallGraph::replaceCallEdge()// method. Test is expected to complete successfully after running pass on// all SCCs in the test module.struct CallbackCallsModifierPass : public CGPass {bool runOnSCC(CallGraphSCC &SCC) override {CGPass::run();CallGraph &CG = const_cast<CallGraph &>(SCC.getCallGraph());bool Changed = false;for (CallGraphNode *CGN : SCC) {Function *F = CGN->getFunction();if (!F || F->isDeclaration())continue;SmallVector<CallBase *, 4u> Calls;for (Use &U : F->uses()) {AbstractCallSite ACS(&U);if (!ACS || !ACS.isCallbackCall() || !ACS.isCallee(&U))continue;Calls.push_back(cast<CallBase>(ACS.getInstruction()));}if (Calls.empty())continue;for (CallBase *OldCB : Calls) {CallGraphNode *CallerCGN = CG[OldCB->getParent()->getParent()];assert(any_of(*CallerCGN,[CGN](const CallGraphNode::CallRecord &CallRecord) {return CallRecord.second == CGN;}) &&"function is not a callee");CallBase *NewCB = cast<CallBase>(OldCB->clone());NewCB->insertBefore(OldCB);NewCB->takeName(OldCB);CallerCGN->replaceCallEdge(*OldCB, *NewCB, CG[F]);OldCB->replaceAllUsesWith(NewCB);OldCB->eraseFromParent();}Changed = true;}return Changed;}};TEST(PassManager, CallbackCallsModifier0) {LLVMContext Context;const char *IR = "define void @foo() {\n"" call void @broker(void (i8*)* @callback0, i8* null)\n"" call void @broker(void (i8*)* @callback1, i8* null)\n"" ret void\n""}\n""\n""declare !callback !0 void @broker(void (i8*)*, i8*)\n""\n""define internal void @callback0(i8* %arg) {\n"" ret void\n""}\n""\n""define internal void @callback1(i8* %arg) {\n"" ret void\n""}\n""\n""!0 = !{!1}\n""!1 = !{i64 0, i64 1, i1 false}";SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Context);if (!M)Err.print("LegacyPassManagerTest", errs());CallbackCallsModifierPass *P = new CallbackCallsModifierPass();legacy::PassManager Passes;Passes.add(P);Passes.run(*M);}}}INITIALIZE_PASS(ModuleNDM, "mndm", "mndm", false, false)INITIALIZE_PASS_BEGIN(CGPass, "cgp","cgp", false, false)INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)INITIALIZE_PASS_END(CGPass, "cgp","cgp", false, false)INITIALIZE_PASS(FPass, "fp","fp", false, false)INITIALIZE_PASS_BEGIN(LPass, "lp","lp", false, false)INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)INITIALIZE_PASS_END(LPass, "lp","lp", false, false)
//===- llvm/unittest/IR/IntrinsicsTest.cpp - ------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/IntrinsicInst.h"#include "gtest/gtest.h"using namespace llvm;namespace {static const char *const NameTable1[] = {"llvm.foo","llvm.foo.a","llvm.foo.b","llvm.foo.b.a","llvm.foo.c",};TEST(IntrinNameLookup, Basic) {int I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo");EXPECT_EQ(0, I);I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.f64");EXPECT_EQ(0, I);I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.b");EXPECT_EQ(2, I);I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.b.a");EXPECT_EQ(3, I);I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.c");EXPECT_EQ(4, I);I = Intrinsic::lookupLLVMIntrinsicByName(NameTable1, "llvm.foo.c.f64");EXPECT_EQ(4, I);}} // end namespace
//===- llvm/unittest/IR/InstructionsTest.cpp - Instructions unit tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Instructions.h"#include "llvm/ADT/CombinationGenerator.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Analysis/ValueTracking.h"#include "llvm/Analysis/VectorUtils.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Constants.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/FPEnv.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/MDBuilder.h"#include "llvm/IR/Module.h"#include "llvm/IR/NoFolder.h"#include "llvm/IR/Operator.h"#include "llvm/Support/SourceMgr.h"#include "llvm-c/Core.h"#include "gmock/gmock-matchers.h"#include "gtest/gtest.h"#include <memory>namespace llvm {namespace {static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("InstructionsTests", errs());return Mod;}TEST(InstructionsTest, ReturnInst) {LLVMContext C;// test for PR6589const ReturnInst* r0 = ReturnInst::Create(C);EXPECT_EQ(r0->getNumOperands(), 0U);EXPECT_EQ(r0->op_begin(), r0->op_end());IntegerType* Int1 = IntegerType::get(C, 1);Constant* One = ConstantInt::get(Int1, 1, true);const ReturnInst* r1 = ReturnInst::Create(C, One);EXPECT_EQ(1U, r1->getNumOperands());User::const_op_iterator b(r1->op_begin());EXPECT_NE(r1->op_end(), b);EXPECT_EQ(One, *b);EXPECT_EQ(One, r1->getOperand(0));++b;EXPECT_EQ(r1->op_end(), b);// clean updelete r0;delete r1;}// Test fixture that provides a module and a single function within it. Useful// for tests that need to refer to the function in some way.class ModuleWithFunctionTest : public testing::Test {protected:ModuleWithFunctionTest() : M(new Module("MyModule", Ctx)) {FArgTypes.push_back(Type::getInt8Ty(Ctx));FArgTypes.push_back(Type::getInt32Ty(Ctx));FArgTypes.push_back(Type::getInt64Ty(Ctx));FunctionType *FTy =FunctionType::get(Type::getVoidTy(Ctx), FArgTypes, false);F = Function::Create(FTy, Function::ExternalLinkage, "", M.get());}LLVMContext Ctx;std::unique_ptr<Module> M;SmallVector<Type *, 3> FArgTypes;Function *F;};TEST_F(ModuleWithFunctionTest, CallInst) {Value *Args[] = {ConstantInt::get(Type::getInt8Ty(Ctx), 20),ConstantInt::get(Type::getInt32Ty(Ctx), 9999),ConstantInt::get(Type::getInt64Ty(Ctx), 42)};std::unique_ptr<CallInst> Call(CallInst::Create(F, Args));// Make sure iteration over a call's arguments works as expected.unsigned Idx = 0;for (Value *Arg : Call->args()) {EXPECT_EQ(FArgTypes[Idx], Arg->getType());EXPECT_EQ(Call->getArgOperand(Idx)->getType(), Arg->getType());Idx++;}Call->addRetAttr(Attribute::get(Call->getContext(), "test-str-attr"));EXPECT_TRUE(Call->hasRetAttr("test-str-attr"));EXPECT_FALSE(Call->hasRetAttr("not-on-call"));}TEST_F(ModuleWithFunctionTest, InvokeInst) {BasicBlock *BB1 = BasicBlock::Create(Ctx, "", F);BasicBlock *BB2 = BasicBlock::Create(Ctx, "", F);Value *Args[] = {ConstantInt::get(Type::getInt8Ty(Ctx), 20),ConstantInt::get(Type::getInt32Ty(Ctx), 9999),ConstantInt::get(Type::getInt64Ty(Ctx), 42)};std::unique_ptr<InvokeInst> Invoke(InvokeInst::Create(F, BB1, BB2, Args));// Make sure iteration over invoke's arguments works as expected.unsigned Idx = 0;for (Value *Arg : Invoke->args()) {EXPECT_EQ(FArgTypes[Idx], Arg->getType());EXPECT_EQ(Invoke->getArgOperand(Idx)->getType(), Arg->getType());Idx++;}}TEST(InstructionsTest, BranchInst) {LLVMContext C;// Make a BasicBlocksBasicBlock* bb0 = BasicBlock::Create(C);BasicBlock* bb1 = BasicBlock::Create(C);// Mandatory BranchInstconst BranchInst* b0 = BranchInst::Create(bb0);EXPECT_TRUE(b0->isUnconditional());EXPECT_FALSE(b0->isConditional());EXPECT_EQ(1U, b0->getNumSuccessors());// check num operandsEXPECT_EQ(1U, b0->getNumOperands());EXPECT_NE(b0->op_begin(), b0->op_end());EXPECT_EQ(b0->op_end(), std::next(b0->op_begin()));EXPECT_EQ(b0->op_end(), std::next(b0->op_begin()));IntegerType* Int1 = IntegerType::get(C, 1);Constant* One = ConstantInt::get(Int1, 1, true);// Conditional BranchInstBranchInst* b1 = BranchInst::Create(bb0, bb1, One);EXPECT_FALSE(b1->isUnconditional());EXPECT_TRUE(b1->isConditional());EXPECT_EQ(2U, b1->getNumSuccessors());// check num operandsEXPECT_EQ(3U, b1->getNumOperands());User::const_op_iterator b(b1->op_begin());// check CONDEXPECT_NE(b, b1->op_end());EXPECT_EQ(One, *b);EXPECT_EQ(One, b1->getOperand(0));EXPECT_EQ(One, b1->getCondition());++b;// check ELSEEXPECT_EQ(bb1, *b);EXPECT_EQ(bb1, b1->getOperand(1));EXPECT_EQ(bb1, b1->getSuccessor(1));++b;// check THENEXPECT_EQ(bb0, *b);EXPECT_EQ(bb0, b1->getOperand(2));EXPECT_EQ(bb0, b1->getSuccessor(0));++b;EXPECT_EQ(b1->op_end(), b);// clean updelete b0;delete b1;delete bb0;delete bb1;}TEST(InstructionsTest, CastInst) {LLVMContext C;Type *Int8Ty = Type::getInt8Ty(C);Type *Int16Ty = Type::getInt16Ty(C);Type *Int32Ty = Type::getInt32Ty(C);Type *Int64Ty = Type::getInt64Ty(C);Type *V8x8Ty = FixedVectorType::get(Int8Ty, 8);Type *V8x64Ty = FixedVectorType::get(Int64Ty, 8);Type *X86MMXTy = Type::getX86_MMXTy(C);Type *HalfTy = Type::getHalfTy(C);Type *FloatTy = Type::getFloatTy(C);Type *DoubleTy = Type::getDoubleTy(C);Type *V2Int32Ty = FixedVectorType::get(Int32Ty, 2);Type *V2Int64Ty = FixedVectorType::get(Int64Ty, 2);Type *V4Int16Ty = FixedVectorType::get(Int16Ty, 4);Type *V1Int16Ty = FixedVectorType::get(Int16Ty, 1);Type *VScaleV2Int32Ty = ScalableVectorType::get(Int32Ty, 2);Type *VScaleV2Int64Ty = ScalableVectorType::get(Int64Ty, 2);Type *VScaleV4Int16Ty = ScalableVectorType::get(Int16Ty, 4);Type *VScaleV1Int16Ty = ScalableVectorType::get(Int16Ty, 1);Type *Int32PtrTy = PointerType::get(Int32Ty, 0);Type *Int64PtrTy = PointerType::get(Int64Ty, 0);Type *Int32PtrAS1Ty = PointerType::get(Int32Ty, 1);Type *Int64PtrAS1Ty = PointerType::get(Int64Ty, 1);Type *V2Int32PtrAS1Ty = FixedVectorType::get(Int32PtrAS1Ty, 2);Type *V2Int64PtrAS1Ty = FixedVectorType::get(Int64PtrAS1Ty, 2);Type *V4Int32PtrAS1Ty = FixedVectorType::get(Int32PtrAS1Ty, 4);Type *VScaleV4Int32PtrAS1Ty = ScalableVectorType::get(Int32PtrAS1Ty, 4);Type *V4Int64PtrAS1Ty = FixedVectorType::get(Int64PtrAS1Ty, 4);Type *V2Int64PtrTy = FixedVectorType::get(Int64PtrTy, 2);Type *V2Int32PtrTy = FixedVectorType::get(Int32PtrTy, 2);Type *VScaleV2Int32PtrTy = ScalableVectorType::get(Int32PtrTy, 2);Type *V4Int32PtrTy = FixedVectorType::get(Int32PtrTy, 4);Type *VScaleV4Int32PtrTy = ScalableVectorType::get(Int32PtrTy, 4);Type *VScaleV4Int64PtrTy = ScalableVectorType::get(Int64PtrTy, 4);const Constant* c8 = Constant::getNullValue(V8x8Ty);const Constant* c64 = Constant::getNullValue(V8x64Ty);const Constant *v2ptr32 = Constant::getNullValue(V2Int32PtrTy);EXPECT_EQ(CastInst::Trunc, CastInst::getCastOpcode(c64, true, V8x8Ty, true));EXPECT_EQ(CastInst::SExt, CastInst::getCastOpcode(c8, true, V8x64Ty, true));EXPECT_FALSE(CastInst::isBitCastable(V8x8Ty, X86MMXTy));EXPECT_FALSE(CastInst::isBitCastable(X86MMXTy, V8x8Ty));EXPECT_FALSE(CastInst::isBitCastable(Int64Ty, X86MMXTy));EXPECT_FALSE(CastInst::isBitCastable(V8x64Ty, V8x8Ty));EXPECT_FALSE(CastInst::isBitCastable(V8x8Ty, V8x64Ty));// Check address space casts are rejected since we don't know the sizes hereEXPECT_FALSE(CastInst::isBitCastable(Int32PtrTy, Int32PtrAS1Ty));EXPECT_FALSE(CastInst::isBitCastable(Int32PtrAS1Ty, Int32PtrTy));EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrTy, V2Int32PtrAS1Ty));EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V2Int32PtrTy));EXPECT_TRUE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V2Int64PtrAS1Ty));EXPECT_EQ(CastInst::AddrSpaceCast, CastInst::getCastOpcode(v2ptr32, true,V2Int32PtrAS1Ty,true));// Test mismatched number of elements for pointersEXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V4Int64PtrAS1Ty));EXPECT_FALSE(CastInst::isBitCastable(V4Int64PtrAS1Ty, V2Int32PtrAS1Ty));EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrAS1Ty, V4Int32PtrAS1Ty));EXPECT_FALSE(CastInst::isBitCastable(Int32PtrTy, V2Int32PtrTy));EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrTy, Int32PtrTy));EXPECT_TRUE(CastInst::isBitCastable(Int32PtrTy, Int64PtrTy));EXPECT_FALSE(CastInst::isBitCastable(DoubleTy, FloatTy));EXPECT_FALSE(CastInst::isBitCastable(FloatTy, DoubleTy));EXPECT_TRUE(CastInst::isBitCastable(FloatTy, FloatTy));EXPECT_TRUE(CastInst::isBitCastable(FloatTy, FloatTy));EXPECT_TRUE(CastInst::isBitCastable(FloatTy, Int32Ty));EXPECT_TRUE(CastInst::isBitCastable(Int16Ty, HalfTy));EXPECT_TRUE(CastInst::isBitCastable(Int32Ty, FloatTy));EXPECT_TRUE(CastInst::isBitCastable(V2Int32Ty, Int64Ty));EXPECT_TRUE(CastInst::isBitCastable(V2Int32Ty, V4Int16Ty));EXPECT_FALSE(CastInst::isBitCastable(Int32Ty, Int64Ty));EXPECT_FALSE(CastInst::isBitCastable(Int64Ty, Int32Ty));EXPECT_FALSE(CastInst::isBitCastable(V2Int32PtrTy, Int64Ty));EXPECT_FALSE(CastInst::isBitCastable(Int64Ty, V2Int32PtrTy));EXPECT_TRUE(CastInst::isBitCastable(V2Int64PtrTy, V2Int32PtrTy));EXPECT_TRUE(CastInst::isBitCastable(V2Int32PtrTy, V2Int64PtrTy));EXPECT_FALSE(CastInst::isBitCastable(V2Int32Ty, V2Int64Ty));EXPECT_FALSE(CastInst::isBitCastable(V2Int64Ty, V2Int32Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(V4Int32PtrTy),V2Int32PtrTy));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(V2Int32PtrTy),V4Int32PtrTy));EXPECT_FALSE(CastInst::castIsValid(Instruction::AddrSpaceCast,Constant::getNullValue(V4Int32PtrAS1Ty),V2Int32PtrTy));EXPECT_FALSE(CastInst::castIsValid(Instruction::AddrSpaceCast,Constant::getNullValue(V2Int32PtrTy),V4Int32PtrAS1Ty));// Address space cast of fixed/scalable vectors of pointers to scalable/fixed// vector of pointers.EXPECT_FALSE(CastInst::castIsValid(Instruction::AddrSpaceCast, Constant::getNullValue(VScaleV4Int32PtrAS1Ty),V4Int32PtrTy));EXPECT_FALSE(CastInst::castIsValid(Instruction::AddrSpaceCast,Constant::getNullValue(V4Int32PtrTy),VScaleV4Int32PtrAS1Ty));// Address space cast of scalable vectors of pointers to scalable vector of// pointers.EXPECT_FALSE(CastInst::castIsValid(Instruction::AddrSpaceCast, Constant::getNullValue(VScaleV4Int32PtrAS1Ty),VScaleV2Int32PtrTy));EXPECT_FALSE(CastInst::castIsValid(Instruction::AddrSpaceCast,Constant::getNullValue(VScaleV2Int32PtrTy),VScaleV4Int32PtrAS1Ty));EXPECT_TRUE(CastInst::castIsValid(Instruction::AddrSpaceCast,Constant::getNullValue(VScaleV4Int64PtrTy),VScaleV4Int32PtrAS1Ty));// Same number of lanes, different address space.EXPECT_TRUE(CastInst::castIsValid(Instruction::AddrSpaceCast, Constant::getNullValue(VScaleV4Int32PtrAS1Ty),VScaleV4Int32PtrTy));// Same number of lanes, same address space.EXPECT_FALSE(CastInst::castIsValid(Instruction::AddrSpaceCast,Constant::getNullValue(VScaleV4Int64PtrTy),VScaleV4Int32PtrTy));// Bit casting fixed/scalable vector to scalable/fixed vectors.EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(V2Int32Ty),VScaleV2Int32Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(V2Int64Ty),VScaleV2Int64Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(V4Int16Ty),VScaleV4Int16Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV2Int32Ty),V2Int32Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV2Int64Ty),V2Int64Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV4Int16Ty),V4Int16Ty));// Bit casting scalable vectors to scalable vectors.EXPECT_TRUE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV4Int16Ty),VScaleV2Int32Ty));EXPECT_TRUE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV2Int32Ty),VScaleV4Int16Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV2Int64Ty),VScaleV2Int32Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV2Int32Ty),VScaleV2Int64Ty));// Bitcasting to/from <vscale x 1 x Ty>EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(VScaleV1Int16Ty),V1Int16Ty));EXPECT_FALSE(CastInst::castIsValid(Instruction::BitCast,Constant::getNullValue(V1Int16Ty),VScaleV1Int16Ty));// Check that assertion is not hit when creating a cast with a vector of// pointers// First formBasicBlock *BB = BasicBlock::Create(C);Constant *NullV2I32Ptr = Constant::getNullValue(V2Int32PtrTy);auto Inst1 = CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty, "foo", BB);Constant *NullVScaleV2I32Ptr = Constant::getNullValue(VScaleV2Int32PtrTy);auto Inst1VScale = CastInst::CreatePointerCast(NullVScaleV2I32Ptr, VScaleV2Int32Ty, "foo.vscale", BB);// Second formauto Inst2 = CastInst::CreatePointerCast(NullV2I32Ptr, V2Int32Ty);auto Inst2VScale =CastInst::CreatePointerCast(NullVScaleV2I32Ptr, VScaleV2Int32Ty);delete Inst2;delete Inst2VScale;Inst1->eraseFromParent();Inst1VScale->eraseFromParent();delete BB;}TEST(InstructionsTest, CastCAPI) {LLVMContext C;Type *Int8Ty = Type::getInt8Ty(C);Type *Int32Ty = Type::getInt32Ty(C);Type *Int64Ty = Type::getInt64Ty(C);Type *FloatTy = Type::getFloatTy(C);Type *DoubleTy = Type::getDoubleTy(C);Type *Int8PtrTy = PointerType::get(Int8Ty, 0);Type *Int32PtrTy = PointerType::get(Int32Ty, 0);const Constant *C8 = Constant::getNullValue(Int8Ty);const Constant *C64 = Constant::getNullValue(Int64Ty);EXPECT_EQ(LLVMBitCast,LLVMGetCastOpcode(wrap(C64), true, wrap(Int64Ty), true));EXPECT_EQ(LLVMTrunc, LLVMGetCastOpcode(wrap(C64), true, wrap(Int8Ty), true));EXPECT_EQ(LLVMSExt, LLVMGetCastOpcode(wrap(C8), true, wrap(Int64Ty), true));EXPECT_EQ(LLVMZExt, LLVMGetCastOpcode(wrap(C8), false, wrap(Int64Ty), true));const Constant *CF32 = Constant::getNullValue(FloatTy);const Constant *CF64 = Constant::getNullValue(DoubleTy);EXPECT_EQ(LLVMFPToUI,LLVMGetCastOpcode(wrap(CF32), true, wrap(Int8Ty), false));EXPECT_EQ(LLVMFPToSI,LLVMGetCastOpcode(wrap(CF32), true, wrap(Int8Ty), true));EXPECT_EQ(LLVMUIToFP,LLVMGetCastOpcode(wrap(C8), false, wrap(FloatTy), true));EXPECT_EQ(LLVMSIToFP, LLVMGetCastOpcode(wrap(C8), true, wrap(FloatTy), true));EXPECT_EQ(LLVMFPTrunc,LLVMGetCastOpcode(wrap(CF64), true, wrap(FloatTy), true));EXPECT_EQ(LLVMFPExt,LLVMGetCastOpcode(wrap(CF32), true, wrap(DoubleTy), true));const Constant *CPtr8 = Constant::getNullValue(Int8PtrTy);EXPECT_EQ(LLVMPtrToInt,LLVMGetCastOpcode(wrap(CPtr8), true, wrap(Int8Ty), true));EXPECT_EQ(LLVMIntToPtr,LLVMGetCastOpcode(wrap(C8), true, wrap(Int8PtrTy), true));Type *V8x8Ty = FixedVectorType::get(Int8Ty, 8);Type *V8x64Ty = FixedVectorType::get(Int64Ty, 8);const Constant *CV8 = Constant::getNullValue(V8x8Ty);const Constant *CV64 = Constant::getNullValue(V8x64Ty);EXPECT_EQ(LLVMTrunc, LLVMGetCastOpcode(wrap(CV64), true, wrap(V8x8Ty), true));EXPECT_EQ(LLVMSExt, LLVMGetCastOpcode(wrap(CV8), true, wrap(V8x64Ty), true));Type *Int32PtrAS1Ty = PointerType::get(Int32Ty, 1);Type *V2Int32PtrAS1Ty = FixedVectorType::get(Int32PtrAS1Ty, 2);Type *V2Int32PtrTy = FixedVectorType::get(Int32PtrTy, 2);const Constant *CV2ptr32 = Constant::getNullValue(V2Int32PtrTy);EXPECT_EQ(LLVMAddrSpaceCast, LLVMGetCastOpcode(wrap(CV2ptr32), true,wrap(V2Int32PtrAS1Ty), true));}TEST(InstructionsTest, VectorGep) {LLVMContext C;// Type DefinitionsType *I8Ty = IntegerType::get(C, 8);Type *I32Ty = IntegerType::get(C, 32);PointerType *Ptri8Ty = PointerType::get(I8Ty, 0);PointerType *Ptri32Ty = PointerType::get(I32Ty, 0);VectorType *V2xi8PTy = FixedVectorType::get(Ptri8Ty, 2);VectorType *V2xi32PTy = FixedVectorType::get(Ptri32Ty, 2);// Test different aspects of the vector-of-pointers type// and GEPs which use this type.ConstantInt *Ci32a = ConstantInt::get(C, APInt(32, 1492));ConstantInt *Ci32b = ConstantInt::get(C, APInt(32, 1948));std::vector<Constant*> ConstVa(2, Ci32a);std::vector<Constant*> ConstVb(2, Ci32b);Constant *C2xi32a = ConstantVector::get(ConstVa);Constant *C2xi32b = ConstantVector::get(ConstVb);CastInst *PtrVecA = new IntToPtrInst(C2xi32a, V2xi32PTy);CastInst *PtrVecB = new IntToPtrInst(C2xi32b, V2xi32PTy);ICmpInst *ICmp0 = new ICmpInst(ICmpInst::ICMP_SGT, PtrVecA, PtrVecB);ICmpInst *ICmp1 = new ICmpInst(ICmpInst::ICMP_ULT, PtrVecA, PtrVecB);EXPECT_NE(ICmp0, ICmp1); // suppress warning.BasicBlock* BB0 = BasicBlock::Create(C);// Test InsertAtEnd ICmpInst constructor.ICmpInst *ICmp2 = new ICmpInst(*BB0, ICmpInst::ICMP_SGE, PtrVecA, PtrVecB);EXPECT_NE(ICmp0, ICmp2); // suppress warning.GetElementPtrInst *Gep0 = GetElementPtrInst::Create(I32Ty, PtrVecA, C2xi32a);GetElementPtrInst *Gep1 = GetElementPtrInst::Create(I32Ty, PtrVecA, C2xi32b);GetElementPtrInst *Gep2 = GetElementPtrInst::Create(I32Ty, PtrVecB, C2xi32a);GetElementPtrInst *Gep3 = GetElementPtrInst::Create(I32Ty, PtrVecB, C2xi32b);CastInst *BTC0 = new BitCastInst(Gep0, V2xi8PTy);CastInst *BTC1 = new BitCastInst(Gep1, V2xi8PTy);CastInst *BTC2 = new BitCastInst(Gep2, V2xi8PTy);CastInst *BTC3 = new BitCastInst(Gep3, V2xi8PTy);Value *S0 = BTC0->stripPointerCasts();Value *S1 = BTC1->stripPointerCasts();Value *S2 = BTC2->stripPointerCasts();Value *S3 = BTC3->stripPointerCasts();EXPECT_NE(S0, Gep0);EXPECT_NE(S1, Gep1);EXPECT_NE(S2, Gep2);EXPECT_NE(S3, Gep3);int64_t Offset;DataLayout TD("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3""2:32:32-f64:64:64-v64:64:64-v128:128:128-a:0:64-s:64:64-f80"":128:128-n8:16:32:64-S128");// Make sure we don't crashGetPointerBaseWithConstantOffset(Gep0, Offset, TD);GetPointerBaseWithConstantOffset(Gep1, Offset, TD);GetPointerBaseWithConstantOffset(Gep2, Offset, TD);GetPointerBaseWithConstantOffset(Gep3, Offset, TD);// Gep of GepsGetElementPtrInst *GepII0 = GetElementPtrInst::Create(I32Ty, Gep0, C2xi32b);GetElementPtrInst *GepII1 = GetElementPtrInst::Create(I32Ty, Gep1, C2xi32a);GetElementPtrInst *GepII2 = GetElementPtrInst::Create(I32Ty, Gep2, C2xi32b);GetElementPtrInst *GepII3 = GetElementPtrInst::Create(I32Ty, Gep3, C2xi32a);EXPECT_EQ(GepII0->getNumIndices(), 1u);EXPECT_EQ(GepII1->getNumIndices(), 1u);EXPECT_EQ(GepII2->getNumIndices(), 1u);EXPECT_EQ(GepII3->getNumIndices(), 1u);EXPECT_FALSE(GepII0->hasAllZeroIndices());EXPECT_FALSE(GepII1->hasAllZeroIndices());EXPECT_FALSE(GepII2->hasAllZeroIndices());EXPECT_FALSE(GepII3->hasAllZeroIndices());delete GepII0;delete GepII1;delete GepII2;delete GepII3;delete BTC0;delete BTC1;delete BTC2;delete BTC3;delete Gep0;delete Gep1;delete Gep2;delete Gep3;ICmp2->eraseFromParent();delete BB0;delete ICmp0;delete ICmp1;delete PtrVecA;delete PtrVecB;}TEST(InstructionsTest, FPMathOperator) {LLVMContext Context;IRBuilder<> Builder(Context);MDBuilder MDHelper(Context);Instruction *I = Builder.CreatePHI(Builder.getDoubleTy(), 0);MDNode *MD1 = MDHelper.createFPMath(1.0);Value *V1 = Builder.CreateFAdd(I, I, "", MD1);EXPECT_TRUE(isa<FPMathOperator>(V1));FPMathOperator *O1 = cast<FPMathOperator>(V1);EXPECT_EQ(O1->getFPAccuracy(), 1.0);V1->deleteValue();I->deleteValue();}TEST(InstructionTest, ConstrainedTrans) {LLVMContext Context;std::unique_ptr<Module> M(new Module("MyModule", Context));FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context),{Type::getFloatTy(Context), Type::getFloatTy(Context),Type::getInt32Ty(Context)},false);auto *F = Function::Create(FTy, Function::ExternalLinkage, "", M.get());auto *BB = BasicBlock::Create(Context, "bb", F);IRBuilder<> Builder(Context);Builder.SetInsertPoint(BB);auto *Arg0 = F->arg_begin();auto *Arg1 = F->arg_begin() + 1;{auto *I = cast<Instruction>(Builder.CreateFAdd(Arg0, Arg1));EXPECT_EQ(Intrinsic::experimental_constrained_fadd,getConstrainedIntrinsicID(*I));}{auto *I = cast<Instruction>(Builder.CreateFPToSI(Arg0, Type::getInt32Ty(Context)));EXPECT_EQ(Intrinsic::experimental_constrained_fptosi,getConstrainedIntrinsicID(*I));}{auto *I = cast<Instruction>(Builder.CreateIntrinsic(Intrinsic::ceil, {Type::getFloatTy(Context)}, {Arg0}));EXPECT_EQ(Intrinsic::experimental_constrained_ceil,getConstrainedIntrinsicID(*I));}{auto *I = cast<Instruction>(Builder.CreateFCmpOEQ(Arg0, Arg1));EXPECT_EQ(Intrinsic::experimental_constrained_fcmp,getConstrainedIntrinsicID(*I));}{auto *Arg2 = F->arg_begin() + 2;auto *I = cast<Instruction>(Builder.CreateAdd(Arg2, Arg2));EXPECT_EQ(Intrinsic::not_intrinsic, getConstrainedIntrinsicID(*I));}{auto *I = cast<Instruction>(Builder.CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fadd, Arg0, Arg0));EXPECT_EQ(Intrinsic::not_intrinsic, getConstrainedIntrinsicID(*I));}}TEST(InstructionsTest, isEliminableCastPair) {LLVMContext C;Type* Int16Ty = Type::getInt16Ty(C);Type* Int32Ty = Type::getInt32Ty(C);Type* Int64Ty = Type::getInt64Ty(C);Type* Int64PtrTy = Type::getInt64PtrTy(C);// Source and destination pointers have same size -> bitcast.EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::PtrToInt,CastInst::IntToPtr,Int64PtrTy, Int64Ty, Int64PtrTy,Int32Ty, nullptr, Int32Ty),CastInst::BitCast);// Source and destination have unknown sizes, but the same address space and// the intermediate int is the maximum pointer size -> bitcastEXPECT_EQ(CastInst::isEliminableCastPair(CastInst::PtrToInt,CastInst::IntToPtr,Int64PtrTy, Int64Ty, Int64PtrTy,nullptr, nullptr, nullptr),CastInst::BitCast);// Source and destination have unknown sizes, but the same address space and// the intermediate int is not the maximum pointer size -> nothingEXPECT_EQ(CastInst::isEliminableCastPair(CastInst::PtrToInt,CastInst::IntToPtr,Int64PtrTy, Int32Ty, Int64PtrTy,nullptr, nullptr, nullptr),0U);// Middle pointer big enough -> bitcast.EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::IntToPtr,CastInst::PtrToInt,Int64Ty, Int64PtrTy, Int64Ty,nullptr, Int64Ty, nullptr),CastInst::BitCast);// Middle pointer too small -> fail.EXPECT_EQ(CastInst::isEliminableCastPair(CastInst::IntToPtr,CastInst::PtrToInt,Int64Ty, Int64PtrTy, Int64Ty,nullptr, Int32Ty, nullptr),0U);// Test that we don't eliminate bitcasts between different address spaces,// or if we don't have available pointer size information.DataLayout DL("e-p:32:32:32-p1:16:16:16-p2:64:64:64-i1:8:8-i8:8:8-i16:16:16""-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64""-v128:128:128-a:0:64-s:64:64-f80:128:128-n8:16:32:64-S128");Type* Int64PtrTyAS1 = Type::getInt64PtrTy(C, 1);Type* Int64PtrTyAS2 = Type::getInt64PtrTy(C, 2);IntegerType *Int16SizePtr = DL.getIntPtrType(C, 1);IntegerType *Int64SizePtr = DL.getIntPtrType(C, 2);// Cannot simplify inttoptr, addrspacecastEXPECT_EQ(CastInst::isEliminableCastPair(CastInst::IntToPtr,CastInst::AddrSpaceCast,Int16Ty, Int64PtrTyAS1, Int64PtrTyAS2,nullptr, Int16SizePtr, Int64SizePtr),0U);// Cannot simplify addrspacecast, ptrtointEXPECT_EQ(CastInst::isEliminableCastPair(CastInst::AddrSpaceCast,CastInst::PtrToInt,Int64PtrTyAS1, Int64PtrTyAS2, Int16Ty,Int64SizePtr, Int16SizePtr, nullptr),0U);// Pass since the bitcast address spaces are the sameEXPECT_EQ(CastInst::isEliminableCastPair(CastInst::IntToPtr,CastInst::BitCast,Int16Ty, Int64PtrTyAS1, Int64PtrTyAS1,nullptr, nullptr, nullptr),CastInst::IntToPtr);}TEST(InstructionsTest, CloneCall) {LLVMContext C;Type *Int32Ty = Type::getInt32Ty(C);Type *ArgTys[] = {Int32Ty, Int32Ty, Int32Ty};FunctionType *FnTy = FunctionType::get(Int32Ty, ArgTys, /*isVarArg=*/false);Value *Callee = Constant::getNullValue(FnTy->getPointerTo());Value *Args[] = {ConstantInt::get(Int32Ty, 1),ConstantInt::get(Int32Ty, 2),ConstantInt::get(Int32Ty, 3)};std::unique_ptr<CallInst> Call(CallInst::Create(FnTy, Callee, Args, "result"));// Test cloning the tail call kind.CallInst::TailCallKind Kinds[] = {CallInst::TCK_None, CallInst::TCK_Tail,CallInst::TCK_MustTail};for (CallInst::TailCallKind TCK : Kinds) {Call->setTailCallKind(TCK);std::unique_ptr<CallInst> Clone(cast<CallInst>(Call->clone()));EXPECT_EQ(Call->getTailCallKind(), Clone->getTailCallKind());}Call->setTailCallKind(CallInst::TCK_None);// Test cloning an attribute.{AttrBuilder AB(C);AB.addAttribute(Attribute::ReadOnly);Call->setAttributes(AttributeList::get(C, AttributeList::FunctionIndex, AB));std::unique_ptr<CallInst> Clone(cast<CallInst>(Call->clone()));EXPECT_TRUE(Clone->onlyReadsMemory());}}TEST(InstructionsTest, AlterCallBundles) {LLVMContext C;Type *Int32Ty = Type::getInt32Ty(C);FunctionType *FnTy = FunctionType::get(Int32Ty, Int32Ty, /*isVarArg=*/false);Value *Callee = Constant::getNullValue(FnTy->getPointerTo());Value *Args[] = {ConstantInt::get(Int32Ty, 42)};OperandBundleDef OldBundle("before", UndefValue::get(Int32Ty));std::unique_ptr<CallInst> Call(CallInst::Create(FnTy, Callee, Args, OldBundle, "result"));Call->setTailCallKind(CallInst::TailCallKind::TCK_NoTail);AttrBuilder AB(C);AB.addAttribute(Attribute::Cold);Call->setAttributes(AttributeList::get(C, AttributeList::FunctionIndex, AB));Call->setDebugLoc(DebugLoc(MDNode::get(C, None)));OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7));std::unique_ptr<CallInst> Clone(CallInst::Create(Call.get(), NewBundle));EXPECT_EQ(Call->arg_size(), Clone->arg_size());EXPECT_EQ(Call->getArgOperand(0), Clone->getArgOperand(0));EXPECT_EQ(Call->getCallingConv(), Clone->getCallingConv());EXPECT_EQ(Call->getTailCallKind(), Clone->getTailCallKind());EXPECT_TRUE(Clone->hasFnAttr(Attribute::AttrKind::Cold));EXPECT_EQ(Call->getDebugLoc(), Clone->getDebugLoc());EXPECT_EQ(Clone->getNumOperandBundles(), 1U);EXPECT_TRUE(Clone->getOperandBundle("after"));}TEST(InstructionsTest, AlterInvokeBundles) {LLVMContext C;Type *Int32Ty = Type::getInt32Ty(C);FunctionType *FnTy = FunctionType::get(Int32Ty, Int32Ty, /*isVarArg=*/false);Value *Callee = Constant::getNullValue(FnTy->getPointerTo());Value *Args[] = {ConstantInt::get(Int32Ty, 42)};std::unique_ptr<BasicBlock> NormalDest(BasicBlock::Create(C));std::unique_ptr<BasicBlock> UnwindDest(BasicBlock::Create(C));OperandBundleDef OldBundle("before", UndefValue::get(Int32Ty));std::unique_ptr<InvokeInst> Invoke(InvokeInst::Create(FnTy, Callee, NormalDest.get(), UnwindDest.get(), Args,OldBundle, "result"));AttrBuilder AB(C);AB.addAttribute(Attribute::Cold);Invoke->setAttributes(AttributeList::get(C, AttributeList::FunctionIndex, AB));Invoke->setDebugLoc(DebugLoc(MDNode::get(C, None)));OperandBundleDef NewBundle("after", ConstantInt::get(Int32Ty, 7));std::unique_ptr<InvokeInst> Clone(InvokeInst::Create(Invoke.get(), NewBundle));EXPECT_EQ(Invoke->getNormalDest(), Clone->getNormalDest());EXPECT_EQ(Invoke->getUnwindDest(), Clone->getUnwindDest());EXPECT_EQ(Invoke->arg_size(), Clone->arg_size());EXPECT_EQ(Invoke->getArgOperand(0), Clone->getArgOperand(0));EXPECT_EQ(Invoke->getCallingConv(), Clone->getCallingConv());EXPECT_TRUE(Clone->hasFnAttr(Attribute::AttrKind::Cold));EXPECT_EQ(Invoke->getDebugLoc(), Clone->getDebugLoc());EXPECT_EQ(Clone->getNumOperandBundles(), 1U);EXPECT_TRUE(Clone->getOperandBundle("after"));}TEST_F(ModuleWithFunctionTest, DropPoisonGeneratingFlags) {auto *OnlyBB = BasicBlock::Create(Ctx, "bb", F);auto *Arg0 = &*F->arg_begin();IRBuilder<NoFolder> B(Ctx);B.SetInsertPoint(OnlyBB);{auto *UI =cast<Instruction>(B.CreateUDiv(Arg0, Arg0, "", /*isExact*/ true));ASSERT_TRUE(UI->isExact());UI->dropPoisonGeneratingFlags();ASSERT_FALSE(UI->isExact());}{auto *ShrI =cast<Instruction>(B.CreateLShr(Arg0, Arg0, "", /*isExact*/ true));ASSERT_TRUE(ShrI->isExact());ShrI->dropPoisonGeneratingFlags();ASSERT_FALSE(ShrI->isExact());}{auto *AI = cast<Instruction>(B.CreateAdd(Arg0, Arg0, "", /*HasNUW*/ true, /*HasNSW*/ false));ASSERT_TRUE(AI->hasNoUnsignedWrap());AI->dropPoisonGeneratingFlags();ASSERT_FALSE(AI->hasNoUnsignedWrap());ASSERT_FALSE(AI->hasNoSignedWrap());}{auto *SI = cast<Instruction>(B.CreateAdd(Arg0, Arg0, "", /*HasNUW*/ false, /*HasNSW*/ true));ASSERT_TRUE(SI->hasNoSignedWrap());SI->dropPoisonGeneratingFlags();ASSERT_FALSE(SI->hasNoUnsignedWrap());ASSERT_FALSE(SI->hasNoSignedWrap());}{auto *ShlI = cast<Instruction>(B.CreateShl(Arg0, Arg0, "", /*HasNUW*/ true, /*HasNSW*/ true));ASSERT_TRUE(ShlI->hasNoSignedWrap());ASSERT_TRUE(ShlI->hasNoUnsignedWrap());ShlI->dropPoisonGeneratingFlags();ASSERT_FALSE(ShlI->hasNoUnsignedWrap());ASSERT_FALSE(ShlI->hasNoSignedWrap());}{Value *GEPBase = Constant::getNullValue(B.getInt8PtrTy());auto *GI = cast<GetElementPtrInst>(B.CreateInBoundsGEP(B.getInt8Ty(), GEPBase, Arg0));ASSERT_TRUE(GI->isInBounds());GI->dropPoisonGeneratingFlags();ASSERT_FALSE(GI->isInBounds());}}TEST(InstructionsTest, GEPIndices) {LLVMContext Context;IRBuilder<NoFolder> Builder(Context);Type *ElementTy = Builder.getInt8Ty();Type *ArrTy = ArrayType::get(ArrayType::get(ElementTy, 64), 64);Value *Indices[] = {Builder.getInt32(0),Builder.getInt32(13),Builder.getInt32(42) };Value *V = Builder.CreateGEP(ArrTy, UndefValue::get(PointerType::getUnqual(ArrTy)),Indices);ASSERT_TRUE(isa<GetElementPtrInst>(V));auto *GEPI = cast<GetElementPtrInst>(V);ASSERT_NE(GEPI->idx_begin(), GEPI->idx_end());ASSERT_EQ(GEPI->idx_end(), std::next(GEPI->idx_begin(), 3));EXPECT_EQ(Indices[0], GEPI->idx_begin()[0]);EXPECT_EQ(Indices[1], GEPI->idx_begin()[1]);EXPECT_EQ(Indices[2], GEPI->idx_begin()[2]);EXPECT_EQ(GEPI->idx_begin(), GEPI->indices().begin());EXPECT_EQ(GEPI->idx_end(), GEPI->indices().end());const auto *CGEPI = GEPI;ASSERT_NE(CGEPI->idx_begin(), CGEPI->idx_end());ASSERT_EQ(CGEPI->idx_end(), std::next(CGEPI->idx_begin(), 3));EXPECT_EQ(Indices[0], CGEPI->idx_begin()[0]);EXPECT_EQ(Indices[1], CGEPI->idx_begin()[1]);EXPECT_EQ(Indices[2], CGEPI->idx_begin()[2]);EXPECT_EQ(CGEPI->idx_begin(), CGEPI->indices().begin());EXPECT_EQ(CGEPI->idx_end(), CGEPI->indices().end());delete GEPI;}TEST(InstructionsTest, SwitchInst) {LLVMContext C;std::unique_ptr<BasicBlock> BB1, BB2, BB3;BB1.reset(BasicBlock::Create(C));BB2.reset(BasicBlock::Create(C));BB3.reset(BasicBlock::Create(C));// We create block 0 after the others so that it gets destroyed first and// clears the uses of the other basic blocks.std::unique_ptr<BasicBlock> BB0(BasicBlock::Create(C));auto *Int32Ty = Type::getInt32Ty(C);SwitchInst *SI =SwitchInst::Create(UndefValue::get(Int32Ty), BB0.get(), 3, BB0.get());SI->addCase(ConstantInt::get(Int32Ty, 1), BB1.get());SI->addCase(ConstantInt::get(Int32Ty, 2), BB2.get());SI->addCase(ConstantInt::get(Int32Ty, 3), BB3.get());auto CI = SI->case_begin();ASSERT_NE(CI, SI->case_end());EXPECT_EQ(1, CI->getCaseValue()->getSExtValue());EXPECT_EQ(BB1.get(), CI->getCaseSuccessor());EXPECT_EQ(2, (CI + 1)->getCaseValue()->getSExtValue());EXPECT_EQ(BB2.get(), (CI + 1)->getCaseSuccessor());EXPECT_EQ(3, (CI + 2)->getCaseValue()->getSExtValue());EXPECT_EQ(BB3.get(), (CI + 2)->getCaseSuccessor());EXPECT_EQ(CI + 1, std::next(CI));EXPECT_EQ(CI + 2, std::next(CI, 2));EXPECT_EQ(CI + 3, std::next(CI, 3));EXPECT_EQ(SI->case_end(), CI + 3);EXPECT_EQ(0, CI - CI);EXPECT_EQ(1, (CI + 1) - CI);EXPECT_EQ(2, (CI + 2) - CI);EXPECT_EQ(3, SI->case_end() - CI);EXPECT_EQ(3, std::distance(CI, SI->case_end()));auto CCI = const_cast<const SwitchInst *>(SI)->case_begin();SwitchInst::ConstCaseIt CCE = SI->case_end();ASSERT_NE(CCI, SI->case_end());EXPECT_EQ(1, CCI->getCaseValue()->getSExtValue());EXPECT_EQ(BB1.get(), CCI->getCaseSuccessor());EXPECT_EQ(2, (CCI + 1)->getCaseValue()->getSExtValue());EXPECT_EQ(BB2.get(), (CCI + 1)->getCaseSuccessor());EXPECT_EQ(3, (CCI + 2)->getCaseValue()->getSExtValue());EXPECT_EQ(BB3.get(), (CCI + 2)->getCaseSuccessor());EXPECT_EQ(CCI + 1, std::next(CCI));EXPECT_EQ(CCI + 2, std::next(CCI, 2));EXPECT_EQ(CCI + 3, std::next(CCI, 3));EXPECT_EQ(CCE, CCI + 3);EXPECT_EQ(0, CCI - CCI);EXPECT_EQ(1, (CCI + 1) - CCI);EXPECT_EQ(2, (CCI + 2) - CCI);EXPECT_EQ(3, CCE - CCI);EXPECT_EQ(3, std::distance(CCI, CCE));// Make sure that the const iterator is compatible with a const auto ref.const auto &Handle = *CCI;EXPECT_EQ(1, Handle.getCaseValue()->getSExtValue());EXPECT_EQ(BB1.get(), Handle.getCaseSuccessor());}TEST(InstructionsTest, SwitchInstProfUpdateWrapper) {LLVMContext C;std::unique_ptr<BasicBlock> BB1, BB2, BB3;BB1.reset(BasicBlock::Create(C));BB2.reset(BasicBlock::Create(C));BB3.reset(BasicBlock::Create(C));// We create block 0 after the others so that it gets destroyed first and// clears the uses of the other basic blocks.std::unique_ptr<BasicBlock> BB0(BasicBlock::Create(C));auto *Int32Ty = Type::getInt32Ty(C);SwitchInst *SI =SwitchInst::Create(UndefValue::get(Int32Ty), BB0.get(), 4, BB0.get());SI->addCase(ConstantInt::get(Int32Ty, 1), BB1.get());SI->addCase(ConstantInt::get(Int32Ty, 2), BB2.get());SI->setMetadata(LLVMContext::MD_prof,MDBuilder(C).createBranchWeights({ 9, 1, 22 }));{SwitchInstProfUpdateWrapper SIW(*SI);EXPECT_EQ(*SIW.getSuccessorWeight(0), 9u);EXPECT_EQ(*SIW.getSuccessorWeight(1), 1u);EXPECT_EQ(*SIW.getSuccessorWeight(2), 22u);SIW.setSuccessorWeight(0, 99u);SIW.setSuccessorWeight(1, 11u);EXPECT_EQ(*SIW.getSuccessorWeight(0), 99u);EXPECT_EQ(*SIW.getSuccessorWeight(1), 11u);EXPECT_EQ(*SIW.getSuccessorWeight(2), 22u);}{ // Create another wrapper and check that the data persist.SwitchInstProfUpdateWrapper SIW(*SI);EXPECT_EQ(*SIW.getSuccessorWeight(0), 99u);EXPECT_EQ(*SIW.getSuccessorWeight(1), 11u);EXPECT_EQ(*SIW.getSuccessorWeight(2), 22u);}}TEST(InstructionsTest, CommuteShuffleMask) {SmallVector<int, 16> Indices({-1, 0, 7});ShuffleVectorInst::commuteShuffleMask(Indices, 4);EXPECT_THAT(Indices, testing::ContainerEq(ArrayRef<int>({-1, 4, 3})));}TEST(InstructionsTest, ShuffleMaskQueries) {// Create the elements for various constant vectors.LLVMContext Ctx;Type *Int32Ty = Type::getInt32Ty(Ctx);Constant *CU = UndefValue::get(Int32Ty);Constant *C0 = ConstantInt::get(Int32Ty, 0);Constant *C1 = ConstantInt::get(Int32Ty, 1);Constant *C2 = ConstantInt::get(Int32Ty, 2);Constant *C3 = ConstantInt::get(Int32Ty, 3);Constant *C4 = ConstantInt::get(Int32Ty, 4);Constant *C5 = ConstantInt::get(Int32Ty, 5);Constant *C6 = ConstantInt::get(Int32Ty, 6);Constant *C7 = ConstantInt::get(Int32Ty, 7);Constant *Identity = ConstantVector::get({C0, CU, C2, C3, C4});EXPECT_TRUE(ShuffleVectorInst::isIdentityMask(Identity));EXPECT_FALSE(ShuffleVectorInst::isSelectMask(Identity)); // identity is distinguished from selectEXPECT_FALSE(ShuffleVectorInst::isReverseMask(Identity));EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(Identity)); // identity is always single sourceEXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Identity));EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(Identity));Constant *Select = ConstantVector::get({CU, C1, C5});EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(Select));EXPECT_TRUE(ShuffleVectorInst::isSelectMask(Select));EXPECT_FALSE(ShuffleVectorInst::isReverseMask(Select));EXPECT_FALSE(ShuffleVectorInst::isSingleSourceMask(Select));EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Select));EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(Select));Constant *Reverse = ConstantVector::get({C3, C2, C1, CU});EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(Reverse));EXPECT_FALSE(ShuffleVectorInst::isSelectMask(Reverse));EXPECT_TRUE(ShuffleVectorInst::isReverseMask(Reverse));EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(Reverse)); // reverse is always single sourceEXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Reverse));EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(Reverse));Constant *SingleSource = ConstantVector::get({C2, C2, C0, CU});EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(SingleSource));EXPECT_FALSE(ShuffleVectorInst::isSelectMask(SingleSource));EXPECT_FALSE(ShuffleVectorInst::isReverseMask(SingleSource));EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(SingleSource));EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(SingleSource));EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(SingleSource));Constant *ZeroEltSplat = ConstantVector::get({C0, C0, CU, C0});EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(ZeroEltSplat));EXPECT_FALSE(ShuffleVectorInst::isSelectMask(ZeroEltSplat));EXPECT_FALSE(ShuffleVectorInst::isReverseMask(ZeroEltSplat));EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(ZeroEltSplat)); // 0-splat is always single sourceEXPECT_TRUE(ShuffleVectorInst::isZeroEltSplatMask(ZeroEltSplat));EXPECT_FALSE(ShuffleVectorInst::isTransposeMask(ZeroEltSplat));Constant *Transpose = ConstantVector::get({C0, C4, C2, C6});EXPECT_FALSE(ShuffleVectorInst::isIdentityMask(Transpose));EXPECT_FALSE(ShuffleVectorInst::isSelectMask(Transpose));EXPECT_FALSE(ShuffleVectorInst::isReverseMask(Transpose));EXPECT_FALSE(ShuffleVectorInst::isSingleSourceMask(Transpose));EXPECT_FALSE(ShuffleVectorInst::isZeroEltSplatMask(Transpose));EXPECT_TRUE(ShuffleVectorInst::isTransposeMask(Transpose));// More tests to make sure the logic is/stays correct...EXPECT_TRUE(ShuffleVectorInst::isIdentityMask(ConstantVector::get({CU, C1, CU, C3})));EXPECT_TRUE(ShuffleVectorInst::isIdentityMask(ConstantVector::get({C4, CU, C6, CU})));EXPECT_TRUE(ShuffleVectorInst::isSelectMask(ConstantVector::get({C4, C1, C6, CU})));EXPECT_TRUE(ShuffleVectorInst::isSelectMask(ConstantVector::get({CU, C1, C6, C3})));EXPECT_TRUE(ShuffleVectorInst::isReverseMask(ConstantVector::get({C7, C6, CU, C4})));EXPECT_TRUE(ShuffleVectorInst::isReverseMask(ConstantVector::get({C3, CU, C1, CU})));EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(ConstantVector::get({C7, C5, CU, C7})));EXPECT_TRUE(ShuffleVectorInst::isSingleSourceMask(ConstantVector::get({C3, C0, CU, C3})));EXPECT_TRUE(ShuffleVectorInst::isZeroEltSplatMask(ConstantVector::get({C4, CU, CU, C4})));EXPECT_TRUE(ShuffleVectorInst::isZeroEltSplatMask(ConstantVector::get({CU, C0, CU, C0})));EXPECT_TRUE(ShuffleVectorInst::isTransposeMask(ConstantVector::get({C1, C5, C3, C7})));EXPECT_TRUE(ShuffleVectorInst::isTransposeMask(ConstantVector::get({C1, C3})));// Nothing special about the values here - just re-using inputs to reduce code.Constant *V0 = ConstantVector::get({C0, C1, C2, C3});Constant *V1 = ConstantVector::get({C3, C2, C1, C0});// Identity with undef elts.ShuffleVectorInst *Id1 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C0, C1, CU, CU}));EXPECT_TRUE(Id1->isIdentity());EXPECT_FALSE(Id1->isIdentityWithPadding());EXPECT_FALSE(Id1->isIdentityWithExtract());EXPECT_FALSE(Id1->isConcat());delete Id1;// Result has less elements than operands.ShuffleVectorInst *Id2 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C0, C1, C2}));EXPECT_FALSE(Id2->isIdentity());EXPECT_FALSE(Id2->isIdentityWithPadding());EXPECT_TRUE(Id2->isIdentityWithExtract());EXPECT_FALSE(Id2->isConcat());delete Id2;// Result has less elements than operands; choose from Op1.ShuffleVectorInst *Id3 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C4, CU, C6}));EXPECT_FALSE(Id3->isIdentity());EXPECT_FALSE(Id3->isIdentityWithPadding());EXPECT_TRUE(Id3->isIdentityWithExtract());EXPECT_FALSE(Id3->isConcat());delete Id3;// Result has less elements than operands; choose from Op0 and Op1 is not identity.ShuffleVectorInst *Id4 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C4, C1, C6}));EXPECT_FALSE(Id4->isIdentity());EXPECT_FALSE(Id4->isIdentityWithPadding());EXPECT_FALSE(Id4->isIdentityWithExtract());EXPECT_FALSE(Id4->isConcat());delete Id4;// Result has more elements than operands, and extra elements are undef.ShuffleVectorInst *Id5 = new ShuffleVectorInst(V0, V1,ConstantVector::get({CU, C1, C2, C3, CU, CU}));EXPECT_FALSE(Id5->isIdentity());EXPECT_TRUE(Id5->isIdentityWithPadding());EXPECT_FALSE(Id5->isIdentityWithExtract());EXPECT_FALSE(Id5->isConcat());delete Id5;// Result has more elements than operands, and extra elements are undef; choose from Op1.ShuffleVectorInst *Id6 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C4, C5, C6, CU, CU, CU}));EXPECT_FALSE(Id6->isIdentity());EXPECT_TRUE(Id6->isIdentityWithPadding());EXPECT_FALSE(Id6->isIdentityWithExtract());EXPECT_FALSE(Id6->isConcat());delete Id6;// Result has more elements than operands, but extra elements are not undef.ShuffleVectorInst *Id7 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C0, C1, C2, C3, CU, C1}));EXPECT_FALSE(Id7->isIdentity());EXPECT_FALSE(Id7->isIdentityWithPadding());EXPECT_FALSE(Id7->isIdentityWithExtract());EXPECT_FALSE(Id7->isConcat());delete Id7;// Result has more elements than operands; choose from Op0 and Op1 is not identity.ShuffleVectorInst *Id8 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C4, CU, C2, C3, CU, CU}));EXPECT_FALSE(Id8->isIdentity());EXPECT_FALSE(Id8->isIdentityWithPadding());EXPECT_FALSE(Id8->isIdentityWithExtract());EXPECT_FALSE(Id8->isConcat());delete Id8;// Result has twice as many elements as operands; choose consecutively from Op0 and Op1 is concat.ShuffleVectorInst *Id9 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C0, CU, C2, C3, CU, CU, C6, C7}));EXPECT_FALSE(Id9->isIdentity());EXPECT_FALSE(Id9->isIdentityWithPadding());EXPECT_FALSE(Id9->isIdentityWithExtract());EXPECT_TRUE(Id9->isConcat());delete Id9;// Result has less than twice as many elements as operands, so not a concat.ShuffleVectorInst *Id10 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C0, CU, C2, C3, CU, CU, C6}));EXPECT_FALSE(Id10->isIdentity());EXPECT_FALSE(Id10->isIdentityWithPadding());EXPECT_FALSE(Id10->isIdentityWithExtract());EXPECT_FALSE(Id10->isConcat());delete Id10;// Result has more than twice as many elements as operands, so not a concat.ShuffleVectorInst *Id11 = new ShuffleVectorInst(V0, V1,ConstantVector::get({C0, CU, C2, C3, CU, CU, C6, C7, CU}));EXPECT_FALSE(Id11->isIdentity());EXPECT_FALSE(Id11->isIdentityWithPadding());EXPECT_FALSE(Id11->isIdentityWithExtract());EXPECT_FALSE(Id11->isConcat());delete Id11;// If an input is undef, it's not a concat.// TODO: IdentityWithPadding should be true here even though the high mask values are not undef.ShuffleVectorInst *Id12 = new ShuffleVectorInst(V0, ConstantVector::get({CU, CU, CU, CU}),ConstantVector::get({C0, CU, C2, C3, CU, CU, C6, C7}));EXPECT_FALSE(Id12->isIdentity());EXPECT_FALSE(Id12->isIdentityWithPadding());EXPECT_FALSE(Id12->isIdentityWithExtract());EXPECT_FALSE(Id12->isConcat());delete Id12;// Not possible to express shuffle mask for scalable vector for extract// subvector.Type *VScaleV4Int32Ty = ScalableVectorType::get(Int32Ty, 4);ShuffleVectorInst *Id13 =new ShuffleVectorInst(Constant::getAllOnesValue(VScaleV4Int32Ty),UndefValue::get(VScaleV4Int32Ty),Constant::getNullValue(VScaleV4Int32Ty));int Index = 0;EXPECT_FALSE(Id13->isExtractSubvectorMask(Index));EXPECT_FALSE(Id13->changesLength());EXPECT_FALSE(Id13->increasesLength());delete Id13;// Result has twice as many operands.Type *VScaleV2Int32Ty = ScalableVectorType::get(Int32Ty, 2);ShuffleVectorInst *Id14 =new ShuffleVectorInst(Constant::getAllOnesValue(VScaleV2Int32Ty),UndefValue::get(VScaleV2Int32Ty),Constant::getNullValue(VScaleV4Int32Ty));EXPECT_TRUE(Id14->changesLength());EXPECT_TRUE(Id14->increasesLength());delete Id14;// Not possible to express these masks for scalable vectors, make sure we// don't crash.ShuffleVectorInst *Id15 =new ShuffleVectorInst(Constant::getAllOnesValue(VScaleV2Int32Ty),Constant::getNullValue(VScaleV2Int32Ty),Constant::getNullValue(VScaleV2Int32Ty));EXPECT_FALSE(Id15->isIdentityWithPadding());EXPECT_FALSE(Id15->isIdentityWithExtract());EXPECT_FALSE(Id15->isConcat());delete Id15;}TEST(InstructionsTest, ShuffleMaskIsReplicationMask) {for (int ReplicationFactor : seq_inclusive(1, 8)) {for (int VF : seq_inclusive(1, 8)) {const auto ReplicatedMask = createReplicatedMask(ReplicationFactor, VF);int GuessedReplicationFactor = -1, GuessedVF = -1;EXPECT_TRUE(ShuffleVectorInst::isReplicationMask(ReplicatedMask, GuessedReplicationFactor, GuessedVF));EXPECT_EQ(GuessedReplicationFactor, ReplicationFactor);EXPECT_EQ(GuessedVF, VF);for (int OpVF : seq_inclusive(VF, 2 * VF + 1)) {LLVMContext Ctx;Type *OpVFTy = FixedVectorType::get(IntegerType::getInt1Ty(Ctx), OpVF);Value *Op = ConstantVector::getNullValue(OpVFTy);ShuffleVectorInst *SVI = new ShuffleVectorInst(Op, Op, ReplicatedMask);EXPECT_EQ(SVI->isReplicationMask(GuessedReplicationFactor, GuessedVF),OpVF == VF);delete SVI;}}}}TEST(InstructionsTest, ShuffleMaskIsReplicationMask_undef) {for (int ReplicationFactor : seq_inclusive(1, 4)) {for (int VF : seq_inclusive(1, 4)) {const auto ReplicatedMask = createReplicatedMask(ReplicationFactor, VF);int GuessedReplicationFactor = -1, GuessedVF = -1;// If we change some mask elements to undef, we should still match.SmallVector<SmallVector<bool>> ElementChoices(ReplicatedMask.size(),{false, true});CombinationGenerator<bool, decltype(ElementChoices)::value_type,/*variable_smallsize=*/4>G(ElementChoices);G.generate([&](ArrayRef<bool> UndefOverrides) -> bool {SmallVector<int> AdjustedMask;AdjustedMask.reserve(ReplicatedMask.size());for (auto I : zip(ReplicatedMask, UndefOverrides))AdjustedMask.emplace_back(std::get<1>(I) ? -1 : std::get<0>(I));assert(AdjustedMask.size() == ReplicatedMask.size() &&"Size misprediction");EXPECT_TRUE(ShuffleVectorInst::isReplicationMask(AdjustedMask, GuessedReplicationFactor, GuessedVF));// Do not check GuessedReplicationFactor and GuessedVF,// with enough undef's we may deduce a different tuple.return /*Abort=*/false;});}}}TEST(InstructionsTest, ShuffleMaskIsReplicationMask_Exhaustive_Correctness) {for (int ShufMaskNumElts : seq_inclusive(1, 6)) {SmallVector<int> PossibleShufMaskElts;PossibleShufMaskElts.reserve(ShufMaskNumElts + 2);for (int PossibleShufMaskElt : seq_inclusive(-1, ShufMaskNumElts))PossibleShufMaskElts.emplace_back(PossibleShufMaskElt);assert(PossibleShufMaskElts.size() == ShufMaskNumElts + 2U &&"Size misprediction");SmallVector<SmallVector<int>> ElementChoices(ShufMaskNumElts,PossibleShufMaskElts);CombinationGenerator<int, decltype(ElementChoices)::value_type,/*variable_smallsize=*/4>G(ElementChoices);G.generate([&](ArrayRef<int> Mask) -> bool {int GuessedReplicationFactor = -1, GuessedVF = -1;bool Match = ShuffleVectorInst::isReplicationMask(Mask, GuessedReplicationFactor, GuessedVF);if (!Match)return /*Abort=*/false;const auto ActualMask =createReplicatedMask(GuessedReplicationFactor, GuessedVF);EXPECT_EQ(Mask.size(), ActualMask.size());for (auto I : zip(Mask, ActualMask)) {int Elt = std::get<0>(I);int ActualElt = std::get<0>(I);if (Elt != -1) {EXPECT_EQ(Elt, ActualElt);}}return /*Abort=*/false;});}}TEST(InstructionsTest, GetSplat) {// Create the elements for various constant vectors.LLVMContext Ctx;Type *Int32Ty = Type::getInt32Ty(Ctx);Constant *CU = UndefValue::get(Int32Ty);Constant *C0 = ConstantInt::get(Int32Ty, 0);Constant *C1 = ConstantInt::get(Int32Ty, 1);Constant *Splat0 = ConstantVector::get({C0, C0, C0, C0});Constant *Splat1 = ConstantVector::get({C1, C1, C1, C1 ,C1});Constant *Splat0Undef = ConstantVector::get({C0, CU, C0, CU});Constant *Splat1Undef = ConstantVector::get({CU, CU, C1, CU});Constant *NotSplat = ConstantVector::get({C1, C1, C0, C1 ,C1});Constant *NotSplatUndef = ConstantVector::get({CU, C1, CU, CU ,C0});// Default - undefs are not allowed.EXPECT_EQ(Splat0->getSplatValue(), C0);EXPECT_EQ(Splat1->getSplatValue(), C1);EXPECT_EQ(Splat0Undef->getSplatValue(), nullptr);EXPECT_EQ(Splat1Undef->getSplatValue(), nullptr);EXPECT_EQ(NotSplat->getSplatValue(), nullptr);EXPECT_EQ(NotSplatUndef->getSplatValue(), nullptr);// Disallow undefs explicitly.EXPECT_EQ(Splat0->getSplatValue(false), C0);EXPECT_EQ(Splat1->getSplatValue(false), C1);EXPECT_EQ(Splat0Undef->getSplatValue(false), nullptr);EXPECT_EQ(Splat1Undef->getSplatValue(false), nullptr);EXPECT_EQ(NotSplat->getSplatValue(false), nullptr);EXPECT_EQ(NotSplatUndef->getSplatValue(false), nullptr);// Allow undefs.EXPECT_EQ(Splat0->getSplatValue(true), C0);EXPECT_EQ(Splat1->getSplatValue(true), C1);EXPECT_EQ(Splat0Undef->getSplatValue(true), C0);EXPECT_EQ(Splat1Undef->getSplatValue(true), C1);EXPECT_EQ(NotSplat->getSplatValue(true), nullptr);EXPECT_EQ(NotSplatUndef->getSplatValue(true), nullptr);}TEST(InstructionsTest, SkipDebug) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(declare void @llvm.dbg.value(metadata, metadata, metadata)define void @f() {entry:call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !13ret void}!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!3, !4}!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t2.c", directory: "foo")!2 = !{}!3 = !{i32 2, !"Dwarf Version", i32 4}!4 = !{i32 2, !"Debug Info Version", i32 3}!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2)!9 = !DISubroutineType(types: !10)!10 = !{null}!11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12)!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)!13 = !DILocation(line: 2, column: 7, scope: !8))");ASSERT_TRUE(M);Function *F = cast<Function>(M->getNamedValue("f"));BasicBlock &BB = F->front();// The first non-debug instruction is the terminator.auto *Term = BB.getTerminator();EXPECT_EQ(Term, BB.begin()->getNextNonDebugInstruction());EXPECT_EQ(Term->getIterator(), skipDebugIntrinsics(BB.begin()));// After the terminator, there are no non-debug instructions.EXPECT_EQ(nullptr, Term->getNextNonDebugInstruction());}TEST(InstructionsTest, PhiMightNotBeFPMathOperator) {LLVMContext Context;IRBuilder<> Builder(Context);MDBuilder MDHelper(Context);Instruction *I = Builder.CreatePHI(Builder.getInt32Ty(), 0);EXPECT_FALSE(isa<FPMathOperator>(I));I->deleteValue();Instruction *FP = Builder.CreatePHI(Builder.getDoubleTy(), 0);EXPECT_TRUE(isa<FPMathOperator>(FP));FP->deleteValue();}TEST(InstructionsTest, FPCallIsFPMathOperator) {LLVMContext C;Type *ITy = Type::getInt32Ty(C);FunctionType *IFnTy = FunctionType::get(ITy, {});Value *ICallee = Constant::getNullValue(IFnTy->getPointerTo());std::unique_ptr<CallInst> ICall(CallInst::Create(IFnTy, ICallee, {}, ""));EXPECT_FALSE(isa<FPMathOperator>(ICall));Type *VITy = FixedVectorType::get(ITy, 2);FunctionType *VIFnTy = FunctionType::get(VITy, {});Value *VICallee = Constant::getNullValue(VIFnTy->getPointerTo());std::unique_ptr<CallInst> VICall(CallInst::Create(VIFnTy, VICallee, {}, ""));EXPECT_FALSE(isa<FPMathOperator>(VICall));Type *AITy = ArrayType::get(ITy, 2);FunctionType *AIFnTy = FunctionType::get(AITy, {});Value *AICallee = Constant::getNullValue(AIFnTy->getPointerTo());std::unique_ptr<CallInst> AICall(CallInst::Create(AIFnTy, AICallee, {}, ""));EXPECT_FALSE(isa<FPMathOperator>(AICall));Type *FTy = Type::getFloatTy(C);FunctionType *FFnTy = FunctionType::get(FTy, {});Value *FCallee = Constant::getNullValue(FFnTy->getPointerTo());std::unique_ptr<CallInst> FCall(CallInst::Create(FFnTy, FCallee, {}, ""));EXPECT_TRUE(isa<FPMathOperator>(FCall));Type *VFTy = FixedVectorType::get(FTy, 2);FunctionType *VFFnTy = FunctionType::get(VFTy, {});Value *VFCallee = Constant::getNullValue(VFFnTy->getPointerTo());std::unique_ptr<CallInst> VFCall(CallInst::Create(VFFnTy, VFCallee, {}, ""));EXPECT_TRUE(isa<FPMathOperator>(VFCall));Type *AFTy = ArrayType::get(FTy, 2);FunctionType *AFFnTy = FunctionType::get(AFTy, {});Value *AFCallee = Constant::getNullValue(AFFnTy->getPointerTo());std::unique_ptr<CallInst> AFCall(CallInst::Create(AFFnTy, AFCallee, {}, ""));EXPECT_TRUE(isa<FPMathOperator>(AFCall));Type *AVFTy = ArrayType::get(VFTy, 2);FunctionType *AVFFnTy = FunctionType::get(AVFTy, {});Value *AVFCallee = Constant::getNullValue(AVFFnTy->getPointerTo());std::unique_ptr<CallInst> AVFCall(CallInst::Create(AVFFnTy, AVFCallee, {}, ""));EXPECT_TRUE(isa<FPMathOperator>(AVFCall));Type *AAVFTy = ArrayType::get(AVFTy, 2);FunctionType *AAVFFnTy = FunctionType::get(AAVFTy, {});Value *AAVFCallee = Constant::getNullValue(AAVFFnTy->getPointerTo());std::unique_ptr<CallInst> AAVFCall(CallInst::Create(AAVFFnTy, AAVFCallee, {}, ""));EXPECT_TRUE(isa<FPMathOperator>(AAVFCall));}TEST(InstructionsTest, FNegInstruction) {LLVMContext Context;Type *FltTy = Type::getFloatTy(Context);Constant *One = ConstantFP::get(FltTy, 1.0);BinaryOperator *FAdd = BinaryOperator::CreateFAdd(One, One);FAdd->setHasNoNaNs(true);UnaryOperator *FNeg = UnaryOperator::CreateFNegFMF(One, FAdd);EXPECT_TRUE(FNeg->hasNoNaNs());EXPECT_FALSE(FNeg->hasNoInfs());EXPECT_FALSE(FNeg->hasNoSignedZeros());EXPECT_FALSE(FNeg->hasAllowReciprocal());EXPECT_FALSE(FNeg->hasAllowContract());EXPECT_FALSE(FNeg->hasAllowReassoc());EXPECT_FALSE(FNeg->hasApproxFunc());FAdd->deleteValue();FNeg->deleteValue();}TEST(InstructionsTest, CallBrInstruction) {LLVMContext Context;std::unique_ptr<Module> M = parseIR(Context, R"(define void @foo() {entry:callbr void asm sideeffect "// XXX: ${0:l}", "!i"()to label %land.rhs.i [label %branch_test.exit]land.rhs.i:br label %branch_test.exitbranch_test.exit:%0 = phi i1 [ true, %entry ], [ false, %land.rhs.i ]br i1 %0, label %if.end, label %if.thenif.then:ret voidif.end:ret void})");Function *Foo = M->getFunction("foo");auto BBs = Foo->getBasicBlockList().begin();CallBrInst &CBI = cast<CallBrInst>(BBs->front());++BBs;++BBs;BasicBlock &BranchTestExit = *BBs;++BBs;BasicBlock &IfThen = *BBs;// Test that setting the first indirect destination of callbr updates the destEXPECT_EQ(&BranchTestExit, CBI.getIndirectDest(0));CBI.setIndirectDest(0, &IfThen);EXPECT_EQ(&IfThen, CBI.getIndirectDest(0));}TEST(InstructionsTest, UnaryOperator) {LLVMContext Context;IRBuilder<> Builder(Context);Instruction *I = Builder.CreatePHI(Builder.getDoubleTy(), 0);Value *F = Builder.CreateFNeg(I);EXPECT_TRUE(isa<Value>(F));EXPECT_TRUE(isa<Instruction>(F));EXPECT_TRUE(isa<UnaryInstruction>(F));EXPECT_TRUE(isa<UnaryOperator>(F));EXPECT_FALSE(isa<BinaryOperator>(F));F->deleteValue();I->deleteValue();}TEST(InstructionsTest, DropLocation) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(declare void @callee()define void @no_parent_scope() {call void @callee() ; I1: Call with no location.call void @callee(), !dbg !11 ; I2: Call with location.ret void, !dbg !11 ; I3: Non-call with location.}define void @with_parent_scope() !dbg !8 {call void @callee() ; I1: Call with no location.call void @callee(), !dbg !11 ; I2: Call with location.ret void, !dbg !11 ; I3: Non-call with location.}!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!3, !4}!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t2.c", directory: "foo")!2 = !{}!3 = !{i32 2, !"Dwarf Version", i32 4}!4 = !{i32 2, !"Debug Info Version", i32 3}!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2)!9 = !DISubroutineType(types: !10)!10 = !{null}!11 = !DILocation(line: 2, column: 7, scope: !8, inlinedAt: !12)!12 = !DILocation(line: 3, column: 8, scope: !8))");ASSERT_TRUE(M);{Function *NoParentScopeF =cast<Function>(M->getNamedValue("no_parent_scope"));BasicBlock &BB = NoParentScopeF->front();auto *I1 = BB.getFirstNonPHI();auto *I2 = I1->getNextNode();auto *I3 = BB.getTerminator();EXPECT_EQ(I1->getDebugLoc(), DebugLoc());I1->dropLocation();EXPECT_EQ(I1->getDebugLoc(), DebugLoc());EXPECT_EQ(I2->getDebugLoc().getLine(), 2U);I2->dropLocation();EXPECT_EQ(I1->getDebugLoc(), DebugLoc());EXPECT_EQ(I3->getDebugLoc().getLine(), 2U);I3->dropLocation();EXPECT_EQ(I3->getDebugLoc(), DebugLoc());}{Function *WithParentScopeF =cast<Function>(M->getNamedValue("with_parent_scope"));BasicBlock &BB = WithParentScopeF->front();auto *I2 = BB.getFirstNonPHI()->getNextNode();MDNode *Scope = cast<MDNode>(WithParentScopeF->getSubprogram());EXPECT_EQ(I2->getDebugLoc().getLine(), 2U);I2->dropLocation();EXPECT_EQ(I2->getDebugLoc().getLine(), 0U);EXPECT_EQ(I2->getDebugLoc().getScope(), Scope);EXPECT_EQ(I2->getDebugLoc().getInlinedAt(), nullptr);}}TEST(InstructionsTest, BranchWeightOverflow) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"(declare void @callee()define void @caller() {call void @callee(), !prof !1ret void}!1 = !{!"branch_weights", i32 20000})");ASSERT_TRUE(M);CallInst *CI =cast<CallInst>(&M->getFunction("caller")->getEntryBlock().front());uint64_t ProfWeight;CI->extractProfTotalWeight(ProfWeight);ASSERT_EQ(ProfWeight, 20000U);CI->updateProfWeight(10000000, 1);CI->extractProfTotalWeight(ProfWeight);ASSERT_EQ(ProfWeight, UINT32_MAX);}TEST(InstructionsTest, AllocaInst) {LLVMContext Ctx;std::unique_ptr<Module> M = parseIR(Ctx, R"(%T = type { i64, [3 x i32]}define void @f(i32 %n) {entry:%A = alloca i32, i32 1%B = alloca i32, i32 4%C = alloca i32, i32 %n%D = alloca <8 x double>%E = alloca <vscale x 8 x double>%F = alloca [2 x half]%G = alloca [2 x [3 x i128]]%H = alloca %Tret void})");const DataLayout &DL = M->getDataLayout();ASSERT_TRUE(M);Function *Fun = cast<Function>(M->getNamedValue("f"));BasicBlock &BB = Fun->front();auto It = BB.begin();AllocaInst &A = cast<AllocaInst>(*It++);AllocaInst &B = cast<AllocaInst>(*It++);AllocaInst &C = cast<AllocaInst>(*It++);AllocaInst &D = cast<AllocaInst>(*It++);AllocaInst &E = cast<AllocaInst>(*It++);AllocaInst &F = cast<AllocaInst>(*It++);AllocaInst &G = cast<AllocaInst>(*It++);AllocaInst &H = cast<AllocaInst>(*It++);EXPECT_EQ(A.getAllocationSizeInBits(DL), TypeSize::getFixed(32));EXPECT_EQ(B.getAllocationSizeInBits(DL), TypeSize::getFixed(128));EXPECT_FALSE(C.getAllocationSizeInBits(DL));EXPECT_EQ(D.getAllocationSizeInBits(DL), TypeSize::getFixed(512));EXPECT_EQ(E.getAllocationSizeInBits(DL), TypeSize::getScalable(512));EXPECT_EQ(F.getAllocationSizeInBits(DL), TypeSize::getFixed(32));EXPECT_EQ(G.getAllocationSizeInBits(DL), TypeSize::getFixed(768));EXPECT_EQ(H.getAllocationSizeInBits(DL), TypeSize::getFixed(160));}} // end anonymous namespace} // end namespace llvm
//===- llvm/unittest/IR/IRBuilderTest.cpp - IRBuilder tests ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/IRBuilder.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/Function.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/IntrinsicsAArch64.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/MDBuilder.h"#include "llvm/IR/Module.h"#include "llvm/IR/NoFolder.h"#include "llvm/IR/Verifier.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;using ::testing::UnorderedElementsAre;namespace {class IRBuilderTest : public testing::Test {protected:void SetUp() override {M.reset(new Module("MyModule", Ctx));FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),/*isVarArg=*/false);F = Function::Create(FTy, Function::ExternalLinkage, "", M.get());BB = BasicBlock::Create(Ctx, "", F);GV = new GlobalVariable(*M, Type::getFloatTy(Ctx), true,GlobalValue::ExternalLinkage, nullptr);}void TearDown() override {BB = nullptr;M.reset();}LLVMContext Ctx;std::unique_ptr<Module> M;Function *F;BasicBlock *BB;GlobalVariable *GV;};TEST_F(IRBuilderTest, Intrinsics) {IRBuilder<> Builder(BB);Value *V;Instruction *I;CallInst *Call;IntrinsicInst *II;V = Builder.CreateLoad(GV->getValueType(), GV);I = cast<Instruction>(Builder.CreateFAdd(V, V));I->setHasNoInfs(true);I->setHasNoNaNs(false);Call = Builder.CreateMinNum(V, V);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::minnum);Call = Builder.CreateMaxNum(V, V);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maxnum);Call = Builder.CreateMinimum(V, V);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::minimum);Call = Builder.CreateMaximum(V, V);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::maximum);Call = Builder.CreateIntrinsic(Intrinsic::readcyclecounter, {}, {});II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::readcyclecounter);Call = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, V);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::fabs);EXPECT_FALSE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, V, I);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::fabs);EXPECT_TRUE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateBinaryIntrinsic(Intrinsic::pow, V, V);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::pow);EXPECT_FALSE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateBinaryIntrinsic(Intrinsic::pow, V, V, I);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::pow);EXPECT_TRUE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateIntrinsic(Intrinsic::fma, {V->getType()}, {V, V, V});II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::fma);EXPECT_FALSE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateIntrinsic(Intrinsic::fma, {V->getType()}, {V, V, V}, I);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::fma);EXPECT_TRUE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateIntrinsic(Intrinsic::fma, {V->getType()}, {V, V, V}, I);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::fma);EXPECT_TRUE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateUnaryIntrinsic(Intrinsic::roundeven, V);II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::roundeven);EXPECT_FALSE(II->hasNoInfs());EXPECT_FALSE(II->hasNoNaNs());Call = Builder.CreateIntrinsic(Intrinsic::set_rounding, {},{Builder.getInt32(static_cast<uint32_t>(RoundingMode::TowardZero))});II = cast<IntrinsicInst>(Call);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::set_rounding);}TEST_F(IRBuilderTest, IntrinsicsWithScalableVectors) {IRBuilder<> Builder(BB);CallInst *Call;FunctionType *FTy;// Test scalable flag isn't dropped for intrinsic that is explicitly defined// with scalable vectors, e.g. LLVMType<nxv4i32>.Type *SrcVecTy = VectorType::get(Builder.getHalfTy(), 8, true);Type *DstVecTy = VectorType::get(Builder.getInt32Ty(), 4, true);Type *PredTy = VectorType::get(Builder.getInt1Ty(), 4, true);SmallVector<Value*, 3> ArgTys;ArgTys.push_back(UndefValue::get(DstVecTy));ArgTys.push_back(UndefValue::get(PredTy));ArgTys.push_back(UndefValue::get(SrcVecTy));Call = Builder.CreateIntrinsic(Intrinsic::aarch64_sve_fcvtzs_i32f16, {},ArgTys, nullptr, "aarch64.sve.fcvtzs.i32f16");FTy = Call->getFunctionType();EXPECT_EQ(FTy->getReturnType(), DstVecTy);for (unsigned i = 0; i != ArgTys.size(); ++i)EXPECT_EQ(FTy->getParamType(i), ArgTys[i]->getType());// Test scalable flag isn't dropped for intrinsic defined with// LLVMScalarOrSameVectorWidth.Type *VecTy = VectorType::get(Builder.getInt32Ty(), 4, true);Type *PtrToVecTy = VecTy->getPointerTo();PredTy = VectorType::get(Builder.getInt1Ty(), 4, true);ArgTys.clear();ArgTys.push_back(UndefValue::get(PtrToVecTy));ArgTys.push_back(UndefValue::get(Builder.getInt32Ty()));ArgTys.push_back(UndefValue::get(PredTy));ArgTys.push_back(UndefValue::get(VecTy));Call = Builder.CreateIntrinsic(Intrinsic::masked_load,{VecTy, PtrToVecTy}, ArgTys,nullptr, "masked.load");FTy = Call->getFunctionType();EXPECT_EQ(FTy->getReturnType(), VecTy);for (unsigned i = 0; i != ArgTys.size(); ++i)EXPECT_EQ(FTy->getParamType(i), ArgTys[i]->getType());}TEST_F(IRBuilderTest, CreateVScale) {IRBuilder<> Builder(BB);Constant *Zero = Builder.getInt32(0);Value *VScale = Builder.CreateVScale(Zero);EXPECT_TRUE(isa<ConstantInt>(VScale) && cast<ConstantInt>(VScale)->isZero());}TEST_F(IRBuilderTest, CreateStepVector) {IRBuilder<> Builder(BB);// Fixed width vectorsType *DstVecTy = VectorType::get(Builder.getInt32Ty(), 4, false);Value *StepVec = Builder.CreateStepVector(DstVecTy);EXPECT_TRUE(isa<Constant>(StepVec));EXPECT_EQ(StepVec->getType(), DstVecTy);const auto *VectorValue = cast<Constant>(StepVec);for (unsigned i = 0; i < 4; i++) {EXPECT_TRUE(isa<ConstantInt>(VectorValue->getAggregateElement(i)));ConstantInt *El = cast<ConstantInt>(VectorValue->getAggregateElement(i));EXPECT_EQ(El->getValue(), i);}// Scalable vectorsDstVecTy = VectorType::get(Builder.getInt32Ty(), 4, true);StepVec = Builder.CreateStepVector(DstVecTy);EXPECT_TRUE(isa<CallInst>(StepVec));CallInst *Call = cast<CallInst>(StepVec);FunctionType *FTy = Call->getFunctionType();EXPECT_EQ(FTy->getReturnType(), DstVecTy);EXPECT_EQ(Call->getIntrinsicID(), Intrinsic::experimental_stepvector);}TEST_F(IRBuilderTest, CreateStepVectorI3) {IRBuilder<> Builder(BB);// Scalable vectorsType *DstVecTy = VectorType::get(IntegerType::get(Ctx, 3), 2, true);Type *VecI8Ty = VectorType::get(Builder.getInt8Ty(), 2, true);Value *StepVec = Builder.CreateStepVector(DstVecTy);EXPECT_TRUE(isa<TruncInst>(StepVec));TruncInst *Trunc = cast<TruncInst>(StepVec);EXPECT_EQ(Trunc->getDestTy(), DstVecTy);EXPECT_EQ(Trunc->getSrcTy(), VecI8Ty);EXPECT_TRUE(isa<CallInst>(Trunc->getOperand(0)));CallInst *Call = cast<CallInst>(Trunc->getOperand(0));FunctionType *FTy = Call->getFunctionType();EXPECT_EQ(FTy->getReturnType(), VecI8Ty);EXPECT_EQ(Call->getIntrinsicID(), Intrinsic::experimental_stepvector);}TEST_F(IRBuilderTest, ConstrainedFP) {IRBuilder<> Builder(BB);Value *V;Value *VDouble;Value *VInt;CallInst *Call;IntrinsicInst *II;GlobalVariable *GVDouble = new GlobalVariable(*M, Type::getDoubleTy(Ctx),true, GlobalValue::ExternalLinkage, nullptr);V = Builder.CreateLoad(GV->getValueType(), GV);VDouble = Builder.CreateLoad(GVDouble->getValueType(), GVDouble);// See if we get constrained intrinsics instead of non-constrained// instructions.Builder.setIsFPConstrained(true);auto Parent = BB->getParent();Parent->addFnAttr(Attribute::StrictFP);V = Builder.CreateFAdd(V, V);ASSERT_TRUE(isa<IntrinsicInst>(V));II = cast<IntrinsicInst>(V);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fadd);V = Builder.CreateFSub(V, V);ASSERT_TRUE(isa<IntrinsicInst>(V));II = cast<IntrinsicInst>(V);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fsub);V = Builder.CreateFMul(V, V);ASSERT_TRUE(isa<IntrinsicInst>(V));II = cast<IntrinsicInst>(V);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fmul);V = Builder.CreateFDiv(V, V);ASSERT_TRUE(isa<IntrinsicInst>(V));II = cast<IntrinsicInst>(V);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fdiv);V = Builder.CreateFRem(V, V);ASSERT_TRUE(isa<IntrinsicInst>(V));II = cast<IntrinsicInst>(V);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_frem);VInt = Builder.CreateFPToUI(VDouble, Builder.getInt32Ty());ASSERT_TRUE(isa<IntrinsicInst>(VInt));II = cast<IntrinsicInst>(VInt);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fptoui);VInt = Builder.CreateFPToSI(VDouble, Builder.getInt32Ty());ASSERT_TRUE(isa<IntrinsicInst>(VInt));II = cast<IntrinsicInst>(VInt);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fptosi);VDouble = Builder.CreateUIToFP(VInt, Builder.getDoubleTy());ASSERT_TRUE(isa<IntrinsicInst>(VDouble));II = cast<IntrinsicInst>(VDouble);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_uitofp);VDouble = Builder.CreateSIToFP(VInt, Builder.getDoubleTy());ASSERT_TRUE(isa<IntrinsicInst>(VDouble));II = cast<IntrinsicInst>(VDouble);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_sitofp);V = Builder.CreateFPTrunc(VDouble, Type::getFloatTy(Ctx));ASSERT_TRUE(isa<IntrinsicInst>(V));II = cast<IntrinsicInst>(V);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fptrunc);VDouble = Builder.CreateFPExt(V, Type::getDoubleTy(Ctx));ASSERT_TRUE(isa<IntrinsicInst>(VDouble));II = cast<IntrinsicInst>(VDouble);EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fpext);// Verify attributes on the call are created automatically.AttributeSet CallAttrs = II->getAttributes().getFnAttrs();EXPECT_EQ(CallAttrs.hasAttribute(Attribute::StrictFP), true);// Verify attributes on the containing function are created when requested.Builder.setConstrainedFPFunctionAttr();AttributeList Attrs = BB->getParent()->getAttributes();AttributeSet FnAttrs = Attrs.getFnAttrs();EXPECT_EQ(FnAttrs.hasAttribute(Attribute::StrictFP), true);// Verify the codepaths for setting and overriding the default metadata.V = Builder.CreateFAdd(V, V);ASSERT_TRUE(isa<ConstrainedFPIntrinsic>(V));auto *CII = cast<ConstrainedFPIntrinsic>(V);EXPECT_EQ(fp::ebStrict, CII->getExceptionBehavior());EXPECT_EQ(RoundingMode::Dynamic, CII->getRoundingMode());Builder.setDefaultConstrainedExcept(fp::ebIgnore);Builder.setDefaultConstrainedRounding(RoundingMode::TowardPositive);V = Builder.CreateFAdd(V, V);CII = cast<ConstrainedFPIntrinsic>(V);EXPECT_EQ(fp::ebIgnore, CII->getExceptionBehavior());EXPECT_EQ(CII->getRoundingMode(), RoundingMode::TowardPositive);Builder.setDefaultConstrainedExcept(fp::ebIgnore);Builder.setDefaultConstrainedRounding(RoundingMode::NearestTiesToEven);V = Builder.CreateFAdd(V, V);CII = cast<ConstrainedFPIntrinsic>(V);EXPECT_EQ(fp::ebIgnore, CII->getExceptionBehavior());EXPECT_EQ(RoundingMode::NearestTiesToEven, CII->getRoundingMode());Builder.setDefaultConstrainedExcept(fp::ebMayTrap);Builder.setDefaultConstrainedRounding(RoundingMode::TowardNegative);V = Builder.CreateFAdd(V, V);CII = cast<ConstrainedFPIntrinsic>(V);EXPECT_EQ(fp::ebMayTrap, CII->getExceptionBehavior());EXPECT_EQ(RoundingMode::TowardNegative, CII->getRoundingMode());Builder.setDefaultConstrainedExcept(fp::ebStrict);Builder.setDefaultConstrainedRounding(RoundingMode::TowardZero);V = Builder.CreateFAdd(V, V);CII = cast<ConstrainedFPIntrinsic>(V);EXPECT_EQ(fp::ebStrict, CII->getExceptionBehavior());EXPECT_EQ(RoundingMode::TowardZero, CII->getRoundingMode());Builder.setDefaultConstrainedExcept(fp::ebIgnore);Builder.setDefaultConstrainedRounding(RoundingMode::Dynamic);V = Builder.CreateFAdd(V, V);CII = cast<ConstrainedFPIntrinsic>(V);EXPECT_EQ(fp::ebIgnore, CII->getExceptionBehavior());EXPECT_EQ(RoundingMode::Dynamic, CII->getRoundingMode());// Now override the defaults.Call = Builder.CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fadd, V, V, nullptr, "", nullptr,RoundingMode::TowardNegative, fp::ebMayTrap);CII = cast<ConstrainedFPIntrinsic>(Call);EXPECT_EQ(CII->getIntrinsicID(), Intrinsic::experimental_constrained_fadd);EXPECT_EQ(fp::ebMayTrap, CII->getExceptionBehavior());EXPECT_EQ(RoundingMode::TowardNegative, CII->getRoundingMode());Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M));}TEST_F(IRBuilderTest, ConstrainedFPIntrinsics) {IRBuilder<> Builder(BB);Value *V;Value *VDouble;ConstrainedFPIntrinsic *CII;GlobalVariable *GVDouble = new GlobalVariable(*M, Type::getDoubleTy(Ctx), true, GlobalValue::ExternalLinkage, nullptr);VDouble = Builder.CreateLoad(GVDouble->getValueType(), GVDouble);Builder.setDefaultConstrainedExcept(fp::ebStrict);Builder.setDefaultConstrainedRounding(RoundingMode::TowardZero);Function *Fn = Intrinsic::getDeclaration(M.get(),Intrinsic::experimental_constrained_roundeven, { Type::getDoubleTy(Ctx) });V = Builder.CreateConstrainedFPCall(Fn, { VDouble });CII = cast<ConstrainedFPIntrinsic>(V);EXPECT_EQ(Intrinsic::experimental_constrained_roundeven, CII->getIntrinsicID());EXPECT_EQ(fp::ebStrict, CII->getExceptionBehavior());}TEST_F(IRBuilderTest, ConstrainedFPFunctionCall) {IRBuilder<> Builder(BB);// Create an empty constrained FP function.FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),/*isVarArg=*/false);Function *Callee =Function::Create(FTy, Function::ExternalLinkage, "", M.get());BasicBlock *CalleeBB = BasicBlock::Create(Ctx, "", Callee);IRBuilder<> CalleeBuilder(CalleeBB);CalleeBuilder.setIsFPConstrained(true);CalleeBuilder.setConstrainedFPFunctionAttr();CalleeBuilder.CreateRetVoid();// Now call the empty constrained FP function.Builder.setIsFPConstrained(true);Builder.setConstrainedFPFunctionAttr();CallInst *FCall = Builder.CreateCall(Callee, None);// Check the attributes to verify the strictfp attribute is on the call.EXPECT_TRUE(FCall->getAttributes().getFnAttrs().hasAttribute(Attribute::StrictFP));Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M));}TEST_F(IRBuilderTest, Lifetime) {IRBuilder<> Builder(BB);AllocaInst *Var1 = Builder.CreateAlloca(Builder.getInt8Ty());AllocaInst *Var2 = Builder.CreateAlloca(Builder.getInt32Ty());AllocaInst *Var3 = Builder.CreateAlloca(Builder.getInt8Ty(),Builder.getInt32(123));CallInst *Start1 = Builder.CreateLifetimeStart(Var1);CallInst *Start2 = Builder.CreateLifetimeStart(Var2);CallInst *Start3 = Builder.CreateLifetimeStart(Var3, Builder.getInt64(100));EXPECT_EQ(Start1->getArgOperand(0), Builder.getInt64(-1));EXPECT_EQ(Start2->getArgOperand(0), Builder.getInt64(-1));EXPECT_EQ(Start3->getArgOperand(0), Builder.getInt64(100));EXPECT_EQ(Start1->getArgOperand(1), Var1);EXPECT_EQ(Start2->getArgOperand(1)->stripPointerCasts(), Var2);EXPECT_EQ(Start3->getArgOperand(1), Var3);Value *End1 = Builder.CreateLifetimeEnd(Var1);Builder.CreateLifetimeEnd(Var2);Builder.CreateLifetimeEnd(Var3);IntrinsicInst *II_Start1 = dyn_cast<IntrinsicInst>(Start1);IntrinsicInst *II_End1 = dyn_cast<IntrinsicInst>(End1);ASSERT_TRUE(II_Start1 != nullptr);EXPECT_EQ(II_Start1->getIntrinsicID(), Intrinsic::lifetime_start);ASSERT_TRUE(II_End1 != nullptr);EXPECT_EQ(II_End1->getIntrinsicID(), Intrinsic::lifetime_end);}TEST_F(IRBuilderTest, CreateCondBr) {IRBuilder<> Builder(BB);BasicBlock *TBB = BasicBlock::Create(Ctx, "", F);BasicBlock *FBB = BasicBlock::Create(Ctx, "", F);BranchInst *BI = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB);Instruction *TI = BB->getTerminator();EXPECT_EQ(BI, TI);EXPECT_EQ(2u, TI->getNumSuccessors());EXPECT_EQ(TBB, TI->getSuccessor(0));EXPECT_EQ(FBB, TI->getSuccessor(1));BI->eraseFromParent();MDNode *Weights = MDBuilder(Ctx).createBranchWeights(42, 13);BI = Builder.CreateCondBr(Builder.getTrue(), TBB, FBB, Weights);TI = BB->getTerminator();EXPECT_EQ(BI, TI);EXPECT_EQ(2u, TI->getNumSuccessors());EXPECT_EQ(TBB, TI->getSuccessor(0));EXPECT_EQ(FBB, TI->getSuccessor(1));EXPECT_EQ(Weights, TI->getMetadata(LLVMContext::MD_prof));}TEST_F(IRBuilderTest, LandingPadName) {IRBuilder<> Builder(BB);LandingPadInst *LP = Builder.CreateLandingPad(Builder.getInt32Ty(), 0, "LP");EXPECT_EQ(LP->getName(), "LP");}TEST_F(IRBuilderTest, DataLayout) {std::unique_ptr<Module> M(new Module("test", Ctx));M->setDataLayout("e-n32");EXPECT_TRUE(M->getDataLayout().isLegalInteger(32));M->setDataLayout("e");EXPECT_FALSE(M->getDataLayout().isLegalInteger(32));}TEST_F(IRBuilderTest, GetIntTy) {IRBuilder<> Builder(BB);IntegerType *Ty1 = Builder.getInt1Ty();EXPECT_EQ(Ty1, IntegerType::get(Ctx, 1));DataLayout* DL = new DataLayout(M.get());IntegerType *IntPtrTy = Builder.getIntPtrTy(*DL);unsigned IntPtrBitSize = DL->getPointerSizeInBits(0);EXPECT_EQ(IntPtrTy, IntegerType::get(Ctx, IntPtrBitSize));delete DL;}TEST_F(IRBuilderTest, UnaryOperators) {IRBuilder<NoFolder> Builder(BB);Value *V = Builder.CreateLoad(GV->getValueType(), GV);// Test CreateUnOp(X)Value *U = Builder.CreateUnOp(Instruction::FNeg, V);ASSERT_TRUE(isa<Instruction>(U));ASSERT_TRUE(isa<FPMathOperator>(U));ASSERT_TRUE(isa<UnaryOperator>(U));ASSERT_FALSE(isa<BinaryOperator>(U));// Test CreateFNegFMF(X)Instruction *I = cast<Instruction>(U);I->setHasNoSignedZeros(true);I->setHasNoNaNs(true);Value *VFMF = Builder.CreateFNegFMF(V, I);Instruction *IFMF = cast<Instruction>(VFMF);EXPECT_TRUE(IFMF->hasNoSignedZeros());EXPECT_TRUE(IFMF->hasNoNaNs());EXPECT_FALSE(IFMF->hasAllowReassoc());}TEST_F(IRBuilderTest, FastMathFlags) {IRBuilder<> Builder(BB);Value *F, *FC;Instruction *FDiv, *FAdd, *FCmp, *FCall;F = Builder.CreateLoad(GV->getValueType(), GV);F = Builder.CreateFAdd(F, F);EXPECT_FALSE(Builder.getFastMathFlags().any());ASSERT_TRUE(isa<Instruction>(F));FAdd = cast<Instruction>(F);EXPECT_FALSE(FAdd->hasNoNaNs());FastMathFlags FMF;Builder.setFastMathFlags(FMF);// By default, no flags are set.F = Builder.CreateFAdd(F, F);EXPECT_FALSE(Builder.getFastMathFlags().any());ASSERT_TRUE(isa<Instruction>(F));FAdd = cast<Instruction>(F);EXPECT_FALSE(FAdd->hasNoNaNs());EXPECT_FALSE(FAdd->hasNoInfs());EXPECT_FALSE(FAdd->hasNoSignedZeros());EXPECT_FALSE(FAdd->hasAllowReciprocal());EXPECT_FALSE(FAdd->hasAllowContract());EXPECT_FALSE(FAdd->hasAllowReassoc());EXPECT_FALSE(FAdd->hasApproxFunc());// Set all flags in the instruction.FAdd->setFast(true);EXPECT_TRUE(FAdd->hasNoNaNs());EXPECT_TRUE(FAdd->hasNoInfs());EXPECT_TRUE(FAdd->hasNoSignedZeros());EXPECT_TRUE(FAdd->hasAllowReciprocal());EXPECT_TRUE(FAdd->hasAllowContract());EXPECT_TRUE(FAdd->hasAllowReassoc());EXPECT_TRUE(FAdd->hasApproxFunc());// All flags are set in the builder.FMF.setFast();Builder.setFastMathFlags(FMF);F = Builder.CreateFAdd(F, F);EXPECT_TRUE(Builder.getFastMathFlags().any());EXPECT_TRUE(Builder.getFastMathFlags().all());ASSERT_TRUE(isa<Instruction>(F));FAdd = cast<Instruction>(F);EXPECT_TRUE(FAdd->hasNoNaNs());EXPECT_TRUE(FAdd->isFast());// Now, try it with CreateBinOpF = Builder.CreateBinOp(Instruction::FAdd, F, F);EXPECT_TRUE(Builder.getFastMathFlags().any());ASSERT_TRUE(isa<Instruction>(F));FAdd = cast<Instruction>(F);EXPECT_TRUE(FAdd->hasNoNaNs());EXPECT_TRUE(FAdd->isFast());F = Builder.CreateFDiv(F, F);EXPECT_TRUE(Builder.getFastMathFlags().all());ASSERT_TRUE(isa<Instruction>(F));FDiv = cast<Instruction>(F);EXPECT_TRUE(FDiv->hasAllowReciprocal());// Clear all FMF in the builder.Builder.clearFastMathFlags();F = Builder.CreateFDiv(F, F);ASSERT_TRUE(isa<Instruction>(F));FDiv = cast<Instruction>(F);EXPECT_FALSE(FDiv->hasAllowReciprocal());// Try individual flags.FMF.clear();FMF.setAllowReciprocal();Builder.setFastMathFlags(FMF);F = Builder.CreateFDiv(F, F);EXPECT_TRUE(Builder.getFastMathFlags().any());EXPECT_TRUE(Builder.getFastMathFlags().AllowReciprocal);ASSERT_TRUE(isa<Instruction>(F));FDiv = cast<Instruction>(F);EXPECT_TRUE(FDiv->hasAllowReciprocal());Builder.clearFastMathFlags();FC = Builder.CreateFCmpOEQ(F, F);ASSERT_TRUE(isa<Instruction>(FC));FCmp = cast<Instruction>(FC);EXPECT_FALSE(FCmp->hasAllowReciprocal());FMF.clear();FMF.setAllowReciprocal();Builder.setFastMathFlags(FMF);FC = Builder.CreateFCmpOEQ(F, F);EXPECT_TRUE(Builder.getFastMathFlags().any());EXPECT_TRUE(Builder.getFastMathFlags().AllowReciprocal);ASSERT_TRUE(isa<Instruction>(FC));FCmp = cast<Instruction>(FC);EXPECT_TRUE(FCmp->hasAllowReciprocal());Builder.clearFastMathFlags();// Test FP-contractFC = Builder.CreateFAdd(F, F);ASSERT_TRUE(isa<Instruction>(FC));FAdd = cast<Instruction>(FC);EXPECT_FALSE(FAdd->hasAllowContract());FMF.clear();FMF.setAllowContract(true);Builder.setFastMathFlags(FMF);FC = Builder.CreateFAdd(F, F);EXPECT_TRUE(Builder.getFastMathFlags().any());EXPECT_TRUE(Builder.getFastMathFlags().AllowContract);ASSERT_TRUE(isa<Instruction>(FC));FAdd = cast<Instruction>(FC);EXPECT_TRUE(FAdd->hasAllowContract());FMF.setApproxFunc();Builder.clearFastMathFlags();Builder.setFastMathFlags(FMF);// Now 'aml' and 'contract' are set.F = Builder.CreateFMul(F, F);FAdd = cast<Instruction>(F);EXPECT_TRUE(FAdd->hasApproxFunc());EXPECT_TRUE(FAdd->hasAllowContract());EXPECT_FALSE(FAdd->hasAllowReassoc());FMF.setAllowReassoc();Builder.clearFastMathFlags();Builder.setFastMathFlags(FMF);// Now 'aml' and 'contract' and 'reassoc' are set.F = Builder.CreateFMul(F, F);FAdd = cast<Instruction>(F);EXPECT_TRUE(FAdd->hasApproxFunc());EXPECT_TRUE(FAdd->hasAllowContract());EXPECT_TRUE(FAdd->hasAllowReassoc());// Test a call with FMF.auto CalleeTy = FunctionType::get(Type::getFloatTy(Ctx),/*isVarArg=*/false);auto Callee =Function::Create(CalleeTy, Function::ExternalLinkage, "", M.get());FCall = Builder.CreateCall(Callee, None);EXPECT_FALSE(FCall->hasNoNaNs());Function *V =Function::Create(CalleeTy, Function::ExternalLinkage, "", M.get());FCall = Builder.CreateCall(V, None);EXPECT_FALSE(FCall->hasNoNaNs());FMF.clear();FMF.setNoNaNs();Builder.setFastMathFlags(FMF);FCall = Builder.CreateCall(Callee, None);EXPECT_TRUE(Builder.getFastMathFlags().any());EXPECT_TRUE(Builder.getFastMathFlags().NoNaNs);EXPECT_TRUE(FCall->hasNoNaNs());FCall = Builder.CreateCall(V, None);EXPECT_TRUE(Builder.getFastMathFlags().any());EXPECT_TRUE(Builder.getFastMathFlags().NoNaNs);EXPECT_TRUE(FCall->hasNoNaNs());Builder.clearFastMathFlags();// To test a copy, make sure that a '0' and a '1' change state.F = Builder.CreateFDiv(F, F);ASSERT_TRUE(isa<Instruction>(F));FDiv = cast<Instruction>(F);EXPECT_FALSE(FDiv->getFastMathFlags().any());FDiv->setHasAllowReciprocal(true);FAdd->setHasAllowReciprocal(false);FAdd->setHasNoNaNs(true);FDiv->copyFastMathFlags(FAdd);EXPECT_TRUE(FDiv->hasNoNaNs());EXPECT_FALSE(FDiv->hasAllowReciprocal());}TEST_F(IRBuilderTest, WrapFlags) {IRBuilder<NoFolder> Builder(BB);// Test instructions.GlobalVariable *G = new GlobalVariable(*M, Builder.getInt32Ty(), true,GlobalValue::ExternalLinkage, nullptr);Value *V = Builder.CreateLoad(G->getValueType(), G);EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateNSWAdd(V, V))->hasNoSignedWrap());EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateNSWMul(V, V))->hasNoSignedWrap());EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateNSWSub(V, V))->hasNoSignedWrap());EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateShl(V, V, "", /* NUW */ false, /* NSW */ true))->hasNoSignedWrap());EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateNUWAdd(V, V))->hasNoUnsignedWrap());EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateNUWMul(V, V))->hasNoUnsignedWrap());EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateNUWSub(V, V))->hasNoUnsignedWrap());EXPECT_TRUE(cast<BinaryOperator>(Builder.CreateShl(V, V, "", /* NUW */ true, /* NSW */ false))->hasNoUnsignedWrap());// Test operators created with constants.Constant *C = Builder.getInt32(42);EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateNSWAdd(C, C))->hasNoSignedWrap());EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateNSWSub(C, C))->hasNoSignedWrap());EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateNSWMul(C, C))->hasNoSignedWrap());EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateShl(C, C, "", /* NUW */ false, /* NSW */ true))->hasNoSignedWrap());EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateNUWAdd(C, C))->hasNoUnsignedWrap());EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateNUWSub(C, C))->hasNoUnsignedWrap());EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateNUWMul(C, C))->hasNoUnsignedWrap());EXPECT_TRUE(cast<OverflowingBinaryOperator>(Builder.CreateShl(C, C, "", /* NUW */ true, /* NSW */ false))->hasNoUnsignedWrap());}TEST_F(IRBuilderTest, RAIIHelpersTest) {IRBuilder<> Builder(BB);EXPECT_FALSE(Builder.getFastMathFlags().allowReciprocal());MDBuilder MDB(M->getContext());MDNode *FPMathA = MDB.createFPMath(0.01f);MDNode *FPMathB = MDB.createFPMath(0.1f);Builder.setDefaultFPMathTag(FPMathA);{IRBuilder<>::FastMathFlagGuard Guard(Builder);FastMathFlags FMF;FMF.setAllowReciprocal();Builder.setFastMathFlags(FMF);Builder.setDefaultFPMathTag(FPMathB);EXPECT_TRUE(Builder.getFastMathFlags().allowReciprocal());EXPECT_EQ(FPMathB, Builder.getDefaultFPMathTag());}EXPECT_FALSE(Builder.getFastMathFlags().allowReciprocal());EXPECT_EQ(FPMathA, Builder.getDefaultFPMathTag());Value *F = Builder.CreateLoad(GV->getValueType(), GV);{IRBuilder<>::InsertPointGuard Guard(Builder);Builder.SetInsertPoint(cast<Instruction>(F));EXPECT_EQ(F, &*Builder.GetInsertPoint());}EXPECT_EQ(BB->end(), Builder.GetInsertPoint());EXPECT_EQ(BB, Builder.GetInsertBlock());}TEST_F(IRBuilderTest, createFunction) {IRBuilder<> Builder(BB);DIBuilder DIB(*M);auto File = DIB.createFile("error.swift", "/");auto CU =DIB.createCompileUnit(dwarf::DW_LANG_Swift, File, "swiftc", true, "", 0);auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));auto NoErr = DIB.createFunction(CU, "noerr", "", File, 1, Type, 1, DINode::FlagZero,DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized);EXPECT_TRUE(!NoErr->getThrownTypes());auto Int = DIB.createBasicType("Int", 64, dwarf::DW_ATE_signed);auto Error = DIB.getOrCreateArray({Int});auto Err = DIB.createFunction(CU, "err", "", File, 1, Type, 1, DINode::FlagZero,DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized, nullptr,nullptr, Error.get());EXPECT_TRUE(Err->getThrownTypes().get() == Error.get());DIB.finalize();}TEST_F(IRBuilderTest, DIBuilder) {IRBuilder<> Builder(BB);DIBuilder DIB(*M);auto File = DIB.createFile("F.CBL", "/");auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74,DIB.createFile("F.CBL", "/"), "llvm-cobol74",true, "", 0);auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));auto SP = DIB.createFunction(CU, "foo", "", File, 1, Type, 1, DINode::FlagZero,DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized);F->setSubprogram(SP);AllocaInst *I = Builder.CreateAlloca(Builder.getInt8Ty());auto BarSP = DIB.createFunction(CU, "bar", "", File, 1, Type, 1, DINode::FlagZero,DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized);auto BadScope = DIB.createLexicalBlockFile(BarSP, File, 0);I->setDebugLoc(DILocation::get(Ctx, 2, 0, BadScope));DIB.finalize();EXPECT_TRUE(verifyModule(*M));}TEST_F(IRBuilderTest, createArtificialSubprogram) {IRBuilder<> Builder(BB);DIBuilder DIB(*M);auto File = DIB.createFile("main.c", "/");auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File, "clang",/*isOptimized=*/true, /*Flags=*/"",/*Runtime Version=*/0);auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));auto SP = DIB.createFunction(CU, "foo", /*LinkageName=*/"", File,/*LineNo=*/1, Type, /*ScopeLine=*/2, DINode::FlagZero,DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized);EXPECT_TRUE(SP->isDistinct());F->setSubprogram(SP);AllocaInst *I = Builder.CreateAlloca(Builder.getInt8Ty());ReturnInst *R = Builder.CreateRetVoid();I->setDebugLoc(DILocation::get(Ctx, 3, 2, SP));R->setDebugLoc(DILocation::get(Ctx, 4, 2, SP));DIB.finalize();EXPECT_FALSE(verifyModule(*M));Function *G = Function::Create(F->getFunctionType(),Function::ExternalLinkage, "", M.get());BasicBlock *GBB = BasicBlock::Create(Ctx, "", G);Builder.SetInsertPoint(GBB);I->removeFromParent();Builder.Insert(I);Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M));DISubprogram *GSP = DIBuilder::createArtificialSubprogram(F->getSubprogram());EXPECT_EQ(SP->getFile(), GSP->getFile());EXPECT_EQ(SP->getType(), GSP->getType());EXPECT_EQ(SP->getLine(), GSP->getLine());EXPECT_EQ(SP->getScopeLine(), GSP->getScopeLine());EXPECT_TRUE(GSP->isDistinct());G->setSubprogram(GSP);EXPECT_TRUE(verifyModule(*M));auto *InlinedAtNode =DILocation::getDistinct(Ctx, GSP->getScopeLine(), 0, GSP);DebugLoc DL = I->getDebugLoc();DenseMap<const MDNode *, MDNode *> IANodes;auto IA = DebugLoc::appendInlinedAt(DL, InlinedAtNode, Ctx, IANodes);auto NewDL =DILocation::get(Ctx, DL.getLine(), DL.getCol(), DL.getScope(), IA);I->setDebugLoc(NewDL);EXPECT_FALSE(verifyModule(*M));EXPECT_EQ("foo", SP->getName());EXPECT_EQ("foo", GSP->getName());EXPECT_FALSE(SP->isArtificial());EXPECT_TRUE(GSP->isArtificial());}// Check that we can add debug info to an existing DICompileUnit.TEST_F(IRBuilderTest, appendDebugInfo) {IRBuilder<> Builder(BB);Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M));auto GetNames = [](DICompileUnit *CU) {SmallVector<StringRef> Names;for (auto *ET : CU->getEnumTypes())Names.push_back(ET->getName());for (auto *RT : CU->getRetainedTypes())Names.push_back(RT->getName());for (auto *GV : CU->getGlobalVariables())Names.push_back(GV->getVariable()->getName());for (auto *IE : CU->getImportedEntities())Names.push_back(IE->getName());for (auto *Node : CU->getMacros())if (auto *MN = dyn_cast_or_null<DIMacro>(Node))Names.push_back(MN->getName());return Names;};DICompileUnit *CU;{DIBuilder DIB(*M);auto *File = DIB.createFile("main.c", "/");CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File, "clang",/*isOptimized=*/true, /*Flags=*/"",/*Runtime Version=*/0);auto *ByteTy = DIB.createBasicType("byte0", 8, dwarf::DW_ATE_signed);DIB.createEnumerationType(CU, "ET0", File, /*LineNo=*/0, /*SizeInBits=*/8,/*AlignInBits=*/8, /*Elements=*/{}, ByteTy);DIB.retainType(ByteTy);DIB.createGlobalVariableExpression(CU, "GV0", /*LinkageName=*/"", File,/*LineNo=*/1, ByteTy,/*IsLocalToUnit=*/true);DIB.createImportedDeclaration(CU, nullptr, File, /*LineNo=*/2, "IM0");DIB.createMacro(nullptr, /*LineNo=*/0, dwarf::DW_MACINFO_define, "M0");DIB.finalize();}EXPECT_FALSE(verifyModule(*M));EXPECT_THAT(GetNames(CU),UnorderedElementsAre("ET0", "byte0", "GV0", "IM0", "M0"));{DIBuilder DIB(*M, true, CU);auto *File = CU->getFile();auto *ByteTy = DIB.createBasicType("byte1", 8, dwarf::DW_ATE_signed);DIB.createEnumerationType(CU, "ET1", File, /*LineNo=*/0,/*SizeInBits=*/8, /*AlignInBits=*/8,/*Elements=*/{}, ByteTy);DIB.retainType(ByteTy);DIB.createGlobalVariableExpression(CU, "GV1", /*LinkageName=*/"", File,/*LineNo=*/1, ByteTy,/*IsLocalToUnit=*/true);DIB.createImportedDeclaration(CU, nullptr, File, /*LineNo=*/2, "IM1");DIB.createMacro(nullptr, /*LineNo=*/0, dwarf::DW_MACINFO_define, "M1");DIB.finalize();}EXPECT_FALSE(verifyModule(*M));EXPECT_THAT(GetNames(CU),UnorderedElementsAre("ET0", "byte0", "GV0", "IM0", "M0", "ET1","byte1", "GV1", "IM1", "M1"));}TEST_F(IRBuilderTest, InsertExtractElement) {IRBuilder<> Builder(BB);auto VecTy = FixedVectorType::get(Builder.getInt64Ty(), 4);auto Elt1 = Builder.getInt64(-1);auto Elt2 = Builder.getInt64(-2);Value *Vec = Builder.CreateInsertElement(VecTy, Elt1, Builder.getInt8(1));Vec = Builder.CreateInsertElement(Vec, Elt2, 2);auto X1 = Builder.CreateExtractElement(Vec, 1);auto X2 = Builder.CreateExtractElement(Vec, Builder.getInt32(2));EXPECT_EQ(Elt1, X1);EXPECT_EQ(Elt2, X2);}TEST_F(IRBuilderTest, CreateGlobalStringPtr) {IRBuilder<> Builder(BB);auto String1a = Builder.CreateGlobalStringPtr("TestString", "String1a");auto String1b = Builder.CreateGlobalStringPtr("TestString", "String1b", 0);auto String2 = Builder.CreateGlobalStringPtr("TestString", "String2", 1);auto String3 = Builder.CreateGlobalString("TestString", "String3", 2);EXPECT_TRUE(String1a->getType()->getPointerAddressSpace() == 0);EXPECT_TRUE(String1b->getType()->getPointerAddressSpace() == 0);EXPECT_TRUE(String2->getType()->getPointerAddressSpace() == 1);EXPECT_TRUE(String3->getType()->getPointerAddressSpace() == 2);}TEST_F(IRBuilderTest, DebugLoc) {auto CalleeTy = FunctionType::get(Type::getVoidTy(Ctx),/*isVarArg=*/false);auto Callee =Function::Create(CalleeTy, Function::ExternalLinkage, "", M.get());DIBuilder DIB(*M);auto File = DIB.createFile("tmp.cpp", "/");auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C_plus_plus_11,DIB.createFile("tmp.cpp", "/"), "", true, "",0);auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));auto SP =DIB.createFunction(CU, "foo", "foo", File, 1, SPType, 1, DINode::FlagZero,DISubprogram::SPFlagDefinition);DebugLoc DL1 = DILocation::get(Ctx, 2, 0, SP);DebugLoc DL2 = DILocation::get(Ctx, 3, 0, SP);auto BB2 = BasicBlock::Create(Ctx, "bb2", F);auto Br = BranchInst::Create(BB2, BB);Br->setDebugLoc(DL1);IRBuilder<> Builder(Ctx);Builder.SetInsertPoint(Br);EXPECT_EQ(DL1, Builder.getCurrentDebugLocation());auto Call1 = Builder.CreateCall(Callee, None);EXPECT_EQ(DL1, Call1->getDebugLoc());Call1->setDebugLoc(DL2);Builder.SetInsertPoint(Call1->getParent(), Call1->getIterator());EXPECT_EQ(DL2, Builder.getCurrentDebugLocation());auto Call2 = Builder.CreateCall(Callee, None);EXPECT_EQ(DL2, Call2->getDebugLoc());DIB.finalize();}TEST_F(IRBuilderTest, DIImportedEntity) {IRBuilder<> Builder(BB);DIBuilder DIB(*M);auto F = DIB.createFile("F.CBL", "/");auto CU = DIB.createCompileUnit(dwarf::DW_LANG_Cobol74,F, "llvm-cobol74",true, "", 0);MDTuple *Elements = MDTuple::getDistinct(Ctx, None);DIB.createImportedDeclaration(CU, nullptr, F, 1);DIB.createImportedDeclaration(CU, nullptr, F, 1);DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, F, 2);DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, F, 2);DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, F, 2, Elements);DIB.createImportedModule(CU, (DIImportedEntity *)nullptr, F, 2, Elements);DIB.finalize();EXPECT_TRUE(verifyModule(*M));EXPECT_TRUE(CU->getImportedEntities().size() == 3);}// 0: #define M0 V0 <-- command line definition// 0: main.c <-- main file// 3: #define M1 V1 <-- M1 definition in main.c// 5: #include "file.h" <-- inclusion of file.h from main.c// 1: #define M2 <-- M2 definition in file.h with no value// 7: #undef M1 V1 <-- M1 un-definition in main.cTEST_F(IRBuilderTest, DIBuilderMacro) {IRBuilder<> Builder(BB);DIBuilder DIB(*M);auto File1 = DIB.createFile("main.c", "/");auto File2 = DIB.createFile("file.h", "/");auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile("main.c", "/"), "llvm-c", true, "", 0);auto MDef0 =DIB.createMacro(nullptr, 0, dwarf::DW_MACINFO_define, "M0", "V0");auto TMF1 = DIB.createTempMacroFile(nullptr, 0, File1);auto MDef1 = DIB.createMacro(TMF1, 3, dwarf::DW_MACINFO_define, "M1", "V1");auto TMF2 = DIB.createTempMacroFile(TMF1, 5, File2);auto MDef2 = DIB.createMacro(TMF2, 1, dwarf::DW_MACINFO_define, "M2");auto MUndef1 = DIB.createMacro(TMF1, 7, dwarf::DW_MACINFO_undef, "M1");EXPECT_EQ(dwarf::DW_MACINFO_define, MDef1->getMacinfoType());EXPECT_EQ(3u, MDef1->getLine());EXPECT_EQ("M1", MDef1->getName());EXPECT_EQ("V1", MDef1->getValue());EXPECT_EQ(dwarf::DW_MACINFO_undef, MUndef1->getMacinfoType());EXPECT_EQ(7u, MUndef1->getLine());EXPECT_EQ("M1", MUndef1->getName());EXPECT_EQ("", MUndef1->getValue());EXPECT_EQ(dwarf::DW_MACINFO_start_file, TMF2->getMacinfoType());EXPECT_EQ(5u, TMF2->getLine());EXPECT_EQ(File2, TMF2->getFile());DIB.finalize();SmallVector<Metadata *, 4> Elements;Elements.push_back(MDef2);auto MF2 = DIMacroFile::get(Ctx, dwarf::DW_MACINFO_start_file, 5, File2,DIB.getOrCreateMacroArray(Elements));Elements.clear();Elements.push_back(MDef1);Elements.push_back(MF2);Elements.push_back(MUndef1);auto MF1 = DIMacroFile::get(Ctx, dwarf::DW_MACINFO_start_file, 0, File1,DIB.getOrCreateMacroArray(Elements));Elements.clear();Elements.push_back(MDef0);Elements.push_back(MF1);auto MN0 = MDTuple::get(Ctx, Elements);EXPECT_EQ(MN0, CU->getRawMacros());Elements.clear();Elements.push_back(MDef1);Elements.push_back(MF2);Elements.push_back(MUndef1);auto MN1 = MDTuple::get(Ctx, Elements);EXPECT_EQ(MN1, MF1->getRawElements());Elements.clear();Elements.push_back(MDef2);auto MN2 = MDTuple::get(Ctx, Elements);EXPECT_EQ(MN2, MF2->getRawElements());EXPECT_TRUE(verifyModule(*M));}TEST_F(IRBuilderTest, NoFolderNames) {IRBuilder<NoFolder> Builder(BB);auto *Add =Builder.CreateAdd(Builder.getInt32(1), Builder.getInt32(2), "add");EXPECT_EQ(Add->getName(), "add");}}
//===- FunctionTest.cpp - Function unit tests -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Function.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(FunctionTest, hasLazyArguments) {LLVMContext C;Type *ArgTypes[] = {Type::getInt8Ty(C), Type::getInt32Ty(C)};FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), ArgTypes, false);// Functions start out with lazy arguments.std::unique_ptr<Function> F(Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));EXPECT_TRUE(F->hasLazyArguments());// Checking for empty or size shouldn't force arguments to be instantiated.EXPECT_FALSE(F->arg_empty());EXPECT_TRUE(F->hasLazyArguments());EXPECT_EQ(2u, F->arg_size());EXPECT_TRUE(F->hasLazyArguments());// The argument list should be populated at first access.(void)F->arg_begin();EXPECT_FALSE(F->hasLazyArguments());// Checking that getArg gets the arguments from F1 in the correct order.unsigned i = 0;for (Argument &A : F->args()) {EXPECT_EQ(&A, F->getArg(i));++i;}EXPECT_FALSE(F->hasLazyArguments());}TEST(FunctionTest, stealArgumentListFrom) {LLVMContext C;Type *ArgTypes[] = {Type::getInt8Ty(C), Type::getInt32Ty(C)};FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), ArgTypes, false);std::unique_ptr<Function> F1(Function::Create(FTy, GlobalValue::ExternalLinkage, "F1"));std::unique_ptr<Function> F2(Function::Create(FTy, GlobalValue::ExternalLinkage, "F1"));EXPECT_TRUE(F1->hasLazyArguments());EXPECT_TRUE(F2->hasLazyArguments());// Steal arguments before they've been accessed. Nothing should change; both// functions should still have lazy arguments.//// steal(empty); drop (empty)F1->stealArgumentListFrom(*F2);EXPECT_TRUE(F1->hasLazyArguments());EXPECT_TRUE(F2->hasLazyArguments());// Save arguments from F1 for later assertions. F1 won't have lazy arguments// anymore.SmallVector<Argument *, 4> Args;for (Argument &A : F1->args())Args.push_back(&A);EXPECT_EQ(2u, Args.size());EXPECT_FALSE(F1->hasLazyArguments());// Steal arguments from F1 to F2. F1's arguments should be lazy again.//// steal(real); drop (empty)F2->stealArgumentListFrom(*F1);EXPECT_TRUE(F1->hasLazyArguments());EXPECT_FALSE(F2->hasLazyArguments());unsigned I = 0;for (Argument &A : F2->args()) {EXPECT_EQ(Args[I], &A);I++;}EXPECT_EQ(2u, I);// Check that arguments in F1 don't have pointer equality with the saved ones.// This also instantiates F1's arguments.I = 0;for (Argument &A : F1->args()) {EXPECT_NE(Args[I], &A);I++;}EXPECT_EQ(2u, I);EXPECT_FALSE(F1->hasLazyArguments());EXPECT_FALSE(F2->hasLazyArguments());// Steal back from F2. F2's arguments should be lazy again.//// steal(real); drop (real)F1->stealArgumentListFrom(*F2);EXPECT_FALSE(F1->hasLazyArguments());EXPECT_TRUE(F2->hasLazyArguments());I = 0;for (Argument &A : F1->args()) {EXPECT_EQ(Args[I], &A);I++;}EXPECT_EQ(2u, I);// Steal from F2 a second time. Now both functions should have lazy// arguments.//// steal(empty); drop (real)F1->stealArgumentListFrom(*F2);EXPECT_TRUE(F1->hasLazyArguments());EXPECT_TRUE(F2->hasLazyArguments());}// Test setting and removing section informationTEST(FunctionTest, setSection) {LLVMContext C;Module M("test", C);llvm::Function *F =Function::Create(llvm::FunctionType::get(llvm::Type::getVoidTy(C), false),llvm::GlobalValue::ExternalLinkage, "F", &M);F->setSection(".text.test");EXPECT_TRUE(F->getSection() == ".text.test");EXPECT_TRUE(F->hasSection());F->setSection("");EXPECT_FALSE(F->hasSection());F->setSection(".text.test");F->setSection(".text.test2");EXPECT_TRUE(F->getSection() == ".text.test2");EXPECT_TRUE(F->hasSection());}TEST(FunctionTest, GetPointerAlignment) {LLVMContext Context;Type *VoidType(Type::getVoidTy(Context));FunctionType *FuncType(FunctionType::get(VoidType, false));std::unique_ptr<Function> Func(Function::Create(FuncType, GlobalValue::ExternalLinkage));EXPECT_EQ(Align(1), Func->getPointerAlignment(DataLayout("")));EXPECT_EQ(Align(1), Func->getPointerAlignment(DataLayout("Fi8")));EXPECT_EQ(Align(1), Func->getPointerAlignment(DataLayout("Fn8")));EXPECT_EQ(Align(2), Func->getPointerAlignment(DataLayout("Fi16")));EXPECT_EQ(Align(2), Func->getPointerAlignment(DataLayout("Fn16")));EXPECT_EQ(Align(4), Func->getPointerAlignment(DataLayout("Fi32")));EXPECT_EQ(Align(4), Func->getPointerAlignment(DataLayout("Fn32")));Func->setAlignment(Align(4));EXPECT_EQ(Align(1), Func->getPointerAlignment(DataLayout("")));EXPECT_EQ(Align(1), Func->getPointerAlignment(DataLayout("Fi8")));EXPECT_EQ(Align(4), Func->getPointerAlignment(DataLayout("Fn8")));EXPECT_EQ(Align(2), Func->getPointerAlignment(DataLayout("Fi16")));EXPECT_EQ(Align(4), Func->getPointerAlignment(DataLayout("Fn16")));EXPECT_EQ(Align(4), Func->getPointerAlignment(DataLayout("Fi32")));EXPECT_EQ(Align(4), Func->getPointerAlignment(DataLayout("Fn32")));}} // end namespace
//===- llvm/unittests/IR/DominatorTreeTest.cpp - Constants unit tests -----===////// 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////===----------------------------------------------------------------------===//#include <random>#include "llvm/Analysis/PostDominators.h"#include "llvm/Analysis/IteratedDominanceFrontier.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "CFGBuilder.h"#include "gtest/gtest.h"using namespace llvm;/// Build the dominator tree for the function and run the Test.static void runWithDomTree(Module &M, StringRef FuncName,function_ref<void(Function &F, DominatorTree *DT, PostDominatorTree *PDT)>Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;// Compute the dominator tree for the function.DominatorTree DT(*F);PostDominatorTree PDT(*F);Test(*F, &DT, &PDT);}static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,StringRef ModuleStr) {SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleStr, Err, Context);assert(M && "Bad assembly?");return M;}TEST(DominatorTree, PHIs) {StringRef ModuleString = R"(define void @f() {bb1:br label %bb1bb2:%a = phi i32 [0, %bb1], [1, %bb2]%b = phi i32 [2, %bb1], [%a, %bb2]br label %bb2};)";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f",[&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {auto FI = F.begin();++FI;BasicBlock *BB2 = &*FI;auto BI = BB2->begin();Instruction *PhiA = &*BI++;Instruction *PhiB = &*BI;// Phis are thought to execute "instantly, together".EXPECT_TRUE(DT->dominates(PhiA, PhiB));EXPECT_TRUE(DT->dominates(PhiB, PhiA));});}TEST(DominatorTree, Unreachable) {StringRef ModuleString ="declare i32 @g()\n""define void @f(i32 %x) personality i32 ()* @g {\n""bb0:\n"" %y1 = add i32 %x, 1\n"" %y2 = add i32 %x, 1\n"" %y3 = invoke i32 @g() to label %bb1 unwind label %bb2\n""bb1:\n"" %y4 = add i32 %x, 1\n"" br label %bb4\n""bb2:\n"" %y5 = landingpad i32\n"" cleanup\n"" br label %bb4\n""bb3:\n"" %y6 = add i32 %x, 1\n"" %y7 = add i32 %x, 1\n"" ret void\n""bb4:\n"" %y8 = phi i32 [0, %bb2], [%y4, %bb1]\n"" %y9 = phi i32 [0, %bb2], [%y4, %bb1]\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Function::iterator FI = F.begin();BasicBlock *BB0 = &*FI++;BasicBlock::iterator BBI = BB0->begin();Instruction *Y1 = &*BBI++;Instruction *Y2 = &*BBI++;Instruction *Y3 = &*BBI++;BasicBlock *BB1 = &*FI++;BBI = BB1->begin();Instruction *Y4 = &*BBI++;BasicBlock *BB2 = &*FI++;BBI = BB2->begin();Instruction *Y5 = &*BBI++;BasicBlock *BB3 = &*FI++;BBI = BB3->begin();Instruction *Y6 = &*BBI++;Instruction *Y7 = &*BBI++;BasicBlock *BB4 = &*FI++;BBI = BB4->begin();Instruction *Y8 = &*BBI++;Instruction *Y9 = &*BBI++;// ReachabilityEXPECT_TRUE(DT->isReachableFromEntry(BB0));EXPECT_TRUE(DT->isReachableFromEntry(BB1));EXPECT_TRUE(DT->isReachableFromEntry(BB2));EXPECT_FALSE(DT->isReachableFromEntry(BB3));EXPECT_TRUE(DT->isReachableFromEntry(BB4));// BB dominanceEXPECT_TRUE(DT->dominates(BB0, BB0));EXPECT_TRUE(DT->dominates(BB0, BB1));EXPECT_TRUE(DT->dominates(BB0, BB2));EXPECT_TRUE(DT->dominates(BB0, BB3));EXPECT_TRUE(DT->dominates(BB0, BB4));EXPECT_FALSE(DT->dominates(BB1, BB0));EXPECT_TRUE(DT->dominates(BB1, BB1));EXPECT_FALSE(DT->dominates(BB1, BB2));EXPECT_TRUE(DT->dominates(BB1, BB3));EXPECT_FALSE(DT->dominates(BB1, BB4));EXPECT_FALSE(DT->dominates(BB2, BB0));EXPECT_FALSE(DT->dominates(BB2, BB1));EXPECT_TRUE(DT->dominates(BB2, BB2));EXPECT_TRUE(DT->dominates(BB2, BB3));EXPECT_FALSE(DT->dominates(BB2, BB4));EXPECT_FALSE(DT->dominates(BB3, BB0));EXPECT_FALSE(DT->dominates(BB3, BB1));EXPECT_FALSE(DT->dominates(BB3, BB2));EXPECT_TRUE(DT->dominates(BB3, BB3));EXPECT_FALSE(DT->dominates(BB3, BB4));// BB proper dominanceEXPECT_FALSE(DT->properlyDominates(BB0, BB0));EXPECT_TRUE(DT->properlyDominates(BB0, BB1));EXPECT_TRUE(DT->properlyDominates(BB0, BB2));EXPECT_TRUE(DT->properlyDominates(BB0, BB3));EXPECT_FALSE(DT->properlyDominates(BB1, BB0));EXPECT_FALSE(DT->properlyDominates(BB1, BB1));EXPECT_FALSE(DT->properlyDominates(BB1, BB2));EXPECT_TRUE(DT->properlyDominates(BB1, BB3));EXPECT_FALSE(DT->properlyDominates(BB2, BB0));EXPECT_FALSE(DT->properlyDominates(BB2, BB1));EXPECT_FALSE(DT->properlyDominates(BB2, BB2));EXPECT_TRUE(DT->properlyDominates(BB2, BB3));EXPECT_FALSE(DT->properlyDominates(BB3, BB0));EXPECT_FALSE(DT->properlyDominates(BB3, BB1));EXPECT_FALSE(DT->properlyDominates(BB3, BB2));EXPECT_FALSE(DT->properlyDominates(BB3, BB3));// Instruction dominance in the same reachable BBEXPECT_FALSE(DT->dominates(Y1, Y1));EXPECT_TRUE(DT->dominates(Y1, Y2));EXPECT_FALSE(DT->dominates(Y2, Y1));EXPECT_FALSE(DT->dominates(Y2, Y2));// Instruction dominance in the same unreachable BBEXPECT_TRUE(DT->dominates(Y6, Y6));EXPECT_TRUE(DT->dominates(Y6, Y7));EXPECT_TRUE(DT->dominates(Y7, Y6));EXPECT_TRUE(DT->dominates(Y7, Y7));// InvokeEXPECT_TRUE(DT->dominates(Y3, Y4));EXPECT_FALSE(DT->dominates(Y3, Y5));// PhiEXPECT_TRUE(DT->dominates(Y2, Y9));EXPECT_FALSE(DT->dominates(Y3, Y9));EXPECT_FALSE(DT->dominates(Y8, Y9));// Anything dominates unreachableEXPECT_TRUE(DT->dominates(Y1, Y6));EXPECT_TRUE(DT->dominates(Y3, Y6));// Unreachable doesn't dominate reachableEXPECT_FALSE(DT->dominates(Y6, Y1));// Instruction, BB dominanceEXPECT_FALSE(DT->dominates(Y1, BB0));EXPECT_TRUE(DT->dominates(Y1, BB1));EXPECT_TRUE(DT->dominates(Y1, BB2));EXPECT_TRUE(DT->dominates(Y1, BB3));EXPECT_TRUE(DT->dominates(Y1, BB4));EXPECT_FALSE(DT->dominates(Y3, BB0));EXPECT_TRUE(DT->dominates(Y3, BB1));EXPECT_FALSE(DT->dominates(Y3, BB2));EXPECT_TRUE(DT->dominates(Y3, BB3));EXPECT_FALSE(DT->dominates(Y3, BB4));EXPECT_TRUE(DT->dominates(Y6, BB3));// Post dominance.EXPECT_TRUE(PDT->dominates(BB0, BB0));EXPECT_FALSE(PDT->dominates(BB1, BB0));EXPECT_FALSE(PDT->dominates(BB2, BB0));EXPECT_FALSE(PDT->dominates(BB3, BB0));EXPECT_TRUE(PDT->dominates(BB4, BB1));// Dominance descendants.SmallVector<BasicBlock *, 8> DominatedBBs, PostDominatedBBs;DT->getDescendants(BB0, DominatedBBs);PDT->getDescendants(BB0, PostDominatedBBs);EXPECT_EQ(DominatedBBs.size(), 4UL);EXPECT_EQ(PostDominatedBBs.size(), 1UL);// BB3 is unreachable. It should have no dominators nor postdominators.DominatedBBs.clear();PostDominatedBBs.clear();DT->getDescendants(BB3, DominatedBBs);DT->getDescendants(BB3, PostDominatedBBs);EXPECT_EQ(DominatedBBs.size(), 0UL);EXPECT_EQ(PostDominatedBBs.size(), 0UL);// Check DFS Numbers beforeDT->updateDFSNumbers();EXPECT_EQ(DT->getNode(BB0)->getDFSNumIn(), 0UL);EXPECT_EQ(DT->getNode(BB0)->getDFSNumOut(), 7UL);EXPECT_EQ(DT->getNode(BB1)->getDFSNumIn(), 1UL);EXPECT_EQ(DT->getNode(BB1)->getDFSNumOut(), 2UL);EXPECT_EQ(DT->getNode(BB2)->getDFSNumIn(), 5UL);EXPECT_EQ(DT->getNode(BB2)->getDFSNumOut(), 6UL);EXPECT_EQ(DT->getNode(BB4)->getDFSNumIn(), 3UL);EXPECT_EQ(DT->getNode(BB4)->getDFSNumOut(), 4UL);// Check levels beforeEXPECT_EQ(DT->getNode(BB0)->getLevel(), 0U);EXPECT_EQ(DT->getNode(BB1)->getLevel(), 1U);EXPECT_EQ(DT->getNode(BB2)->getLevel(), 1U);EXPECT_EQ(DT->getNode(BB4)->getLevel(), 1U);// Reattach block 3 to block 1 and recalculateBB1->getTerminator()->eraseFromParent();BranchInst::Create(BB4, BB3, ConstantInt::getTrue(F.getContext()), BB1);DT->recalculate(F);// Check DFS Numbers afterDT->updateDFSNumbers();EXPECT_EQ(DT->getNode(BB0)->getDFSNumIn(), 0UL);EXPECT_EQ(DT->getNode(BB0)->getDFSNumOut(), 9UL);EXPECT_EQ(DT->getNode(BB1)->getDFSNumIn(), 1UL);EXPECT_EQ(DT->getNode(BB1)->getDFSNumOut(), 4UL);EXPECT_EQ(DT->getNode(BB2)->getDFSNumIn(), 7UL);EXPECT_EQ(DT->getNode(BB2)->getDFSNumOut(), 8UL);EXPECT_EQ(DT->getNode(BB3)->getDFSNumIn(), 2UL);EXPECT_EQ(DT->getNode(BB3)->getDFSNumOut(), 3UL);EXPECT_EQ(DT->getNode(BB4)->getDFSNumIn(), 5UL);EXPECT_EQ(DT->getNode(BB4)->getDFSNumOut(), 6UL);// Check levels afterEXPECT_EQ(DT->getNode(BB0)->getLevel(), 0U);EXPECT_EQ(DT->getNode(BB1)->getLevel(), 1U);EXPECT_EQ(DT->getNode(BB2)->getLevel(), 1U);EXPECT_EQ(DT->getNode(BB3)->getLevel(), 2U);EXPECT_EQ(DT->getNode(BB4)->getLevel(), 1U);// Change root nodeEXPECT_TRUE(DT->verify());BasicBlock *NewEntry =BasicBlock::Create(F.getContext(), "new_entry", &F, BB0);BranchInst::Create(BB0, NewEntry);EXPECT_EQ(F.begin()->getName(), NewEntry->getName());EXPECT_TRUE(&F.getEntryBlock() == NewEntry);DT->setNewRoot(NewEntry);EXPECT_TRUE(DT->verify());});}TEST(DominatorTree, NonUniqueEdges) {StringRef ModuleString ="define i32 @f(i32 %i, i32 *%p) {\n""bb0:\n"" store i32 %i, i32 *%p\n"" switch i32 %i, label %bb2 [\n"" i32 0, label %bb1\n"" i32 1, label %bb1\n"" ]\n"" bb1:\n"" ret i32 1\n"" bb2:\n"" ret i32 4\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Function::iterator FI = F.begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;BasicBlock *BB2 = &*FI++;const Instruction *TI = BB0->getTerminator();assert(TI->getNumSuccessors() == 3 && "Switch has three successors");BasicBlockEdge Edge_BB0_BB2(BB0, TI->getSuccessor(0));assert(Edge_BB0_BB2.getEnd() == BB2 &&"Default label is the 1st successor");BasicBlockEdge Edge_BB0_BB1_a(BB0, TI->getSuccessor(1));assert(Edge_BB0_BB1_a.getEnd() == BB1 && "BB1 is the 2nd successor");BasicBlockEdge Edge_BB0_BB1_b(BB0, TI->getSuccessor(2));assert(Edge_BB0_BB1_b.getEnd() == BB1 && "BB1 is the 3rd successor");EXPECT_TRUE(DT->dominates(Edge_BB0_BB2, BB2));EXPECT_FALSE(DT->dominates(Edge_BB0_BB2, BB1));EXPECT_FALSE(DT->dominates(Edge_BB0_BB1_a, BB1));EXPECT_FALSE(DT->dominates(Edge_BB0_BB1_b, BB1));EXPECT_FALSE(DT->dominates(Edge_BB0_BB1_a, BB2));EXPECT_FALSE(DT->dominates(Edge_BB0_BB1_b, BB2));});}// Verify that the PDT is correctly updated in case an edge removal results// in a new unreachable CFG node. Also make sure that the updated PDT is the// same as a freshly recalculated one.//// For the following input code and initial PDT://// CFG PDT//// A Exit// | |// _B D// / | \ |// ^ v \ B// \ / D / \// C \ C A// v// Exit//// we verify that CFG' and PDT-updated is obtained after removal of edge C -> B.//// CFG' PDT-updated//// A Exit// | / | \// B C B D// | \ |// v \ A// / D// C \// | \// unreachable Exit//// Both the blocks that end with ret and with unreachable become trivial// PostDominatorTree roots, as they have no successors.//TEST(DominatorTree, DeletingEdgesIntroducesUnreachables) {StringRef ModuleString ="define void @f() {\n""A:\n"" br label %B\n""B:\n"" br i1 undef, label %D, label %C\n""C:\n"" br label %B\n""D:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Function::iterator FI = F.begin();FI++;BasicBlock *B = &*FI++;BasicBlock *C = &*FI++;BasicBlock *D = &*FI++;ASSERT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));EXPECT_TRUE(DT->verify());EXPECT_TRUE(PDT->verify());C->getTerminator()->eraseFromParent();new UnreachableInst(C->getContext(), C);DT->deleteEdge(C, B);PDT->deleteEdge(C, B);EXPECT_TRUE(DT->verify());EXPECT_TRUE(PDT->verify());EXPECT_FALSE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));EXPECT_NE(PDT->getNode(C), nullptr);DominatorTree NDT(F);EXPECT_EQ(DT->compare(NDT), 0);PostDominatorTree NPDT(F);EXPECT_EQ(PDT->compare(NPDT), 0);});}// Verify that the PDT is correctly updated in case an edge removal results// in an infinite loop. Also make sure that the updated PDT is the// same as a freshly recalculated one.//// Test case://// CFG PDT//// A Exit// | |// _B D// / | \ |// ^ v \ B// \ / D / \// C \ C A// / \ v// ^ v Exit// \_///// After deleting the edge C->B, C is part of an infinite reverse-unreachable// loop://// CFG' PDT'//// A Exit// | / | \// B C B D// | \ |// v \ A// / D// C \// / \ v// ^ v Exit// \_///// As C now becomes reverse-unreachable, it forms a new non-trivial root and// gets connected to the virtual exit.// D does not postdominate B anymore, because there are two forward paths from// B to the virtual exit:// - B -> C -> VirtualExit// - B -> D -> VirtualExit.//TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop) {StringRef ModuleString ="define void @f() {\n""A:\n"" br label %B\n""B:\n"" br i1 undef, label %D, label %C\n""C:\n"" switch i32 undef, label %C [\n"" i32 0, label %B\n"" ]\n""D:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Function::iterator FI = F.begin();FI++;BasicBlock *B = &*FI++;BasicBlock *C = &*FI++;BasicBlock *D = &*FI++;ASSERT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));EXPECT_TRUE(DT->verify());EXPECT_TRUE(PDT->verify());auto SwitchC = cast<SwitchInst>(C->getTerminator());SwitchC->removeCase(SwitchC->case_begin());DT->deleteEdge(C, B);EXPECT_TRUE(DT->verify());PDT->deleteEdge(C, B);EXPECT_TRUE(PDT->verify());EXPECT_FALSE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));EXPECT_NE(PDT->getNode(C), nullptr);DominatorTree NDT(F);EXPECT_EQ(DT->compare(NDT), 0);PostDominatorTree NPDT(F);EXPECT_EQ(PDT->compare(NPDT), 0);});}// Verify that the PDT is correctly updated in case an edge removal results// in an infinite loop.//// Test case://// CFG PDT//// A Exit// | / | \// B-- C2 B D// | \ / |// v \ C A// / D// C--C2 \// / \ \ v// ^ v --Exit// \_///// After deleting the edge C->E, C is part of an infinite reverse-unreachable// loop://// CFG' PDT'//// A Exit// | / | \// B C B D// | \ |// v \ A// / D// C \// / \ v// ^ v Exit// \_///// In PDT, D does not post-dominate B. After the edge C -> C2 is removed,// C becomes a new nontrivial PDT root.//TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop2) {StringRef ModuleString ="define void @f() {\n""A:\n"" br label %B\n""B:\n"" br i1 undef, label %D, label %C\n""C:\n"" switch i32 undef, label %C [\n"" i32 0, label %C2\n"" ]\n""C2:\n"" ret void\n""D:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Function::iterator FI = F.begin();FI++;BasicBlock *B = &*FI++;BasicBlock *C = &*FI++;BasicBlock *C2 = &*FI++;BasicBlock *D = &*FI++;EXPECT_TRUE(DT->verify());EXPECT_TRUE(PDT->verify());auto SwitchC = cast<SwitchInst>(C->getTerminator());SwitchC->removeCase(SwitchC->case_begin());DT->deleteEdge(C, C2);PDT->deleteEdge(C, C2);C2->removeFromParent();EXPECT_EQ(DT->getNode(C2), nullptr);PDT->eraseNode(C2);delete C2;EXPECT_TRUE(DT->verify());EXPECT_TRUE(PDT->verify());EXPECT_FALSE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));EXPECT_NE(PDT->getNode(C), nullptr);DominatorTree NDT(F);EXPECT_EQ(DT->compare(NDT), 0);PostDominatorTree NPDT(F);EXPECT_EQ(PDT->compare(NPDT), 0);});}// Verify that the IDF returns blocks in a deterministic way.//// Test case://// CFG//// (A)// / \// / \// (B) (C)// |\ /|// | X |// |/ \|// (D) (E)//// IDF for block B is {D, E}, and the order of blocks in this list is defined by// their 1) level in dom-tree and 2) DFSIn number if the level is the same.//TEST(DominatorTree, IDFDeterminismTest) {StringRef ModuleString ="define void @f() {\n""A:\n"" br i1 undef, label %B, label %C\n""B:\n"" br i1 undef, label %D, label %E\n""C:\n"" br i1 undef, label %D, label %E\n""D:\n"" ret void\n""E:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f", [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Function::iterator FI = F.begin();BasicBlock *A = &*FI++;BasicBlock *B = &*FI++;BasicBlock *C = &*FI++;BasicBlock *D = &*FI++;BasicBlock *E = &*FI++;(void)C;DT->updateDFSNumbers();ForwardIDFCalculator IDF(*DT);SmallPtrSet<BasicBlock *, 1> DefBlocks;DefBlocks.insert(B);IDF.setDefiningBlocks(DefBlocks);SmallVector<BasicBlock *, 32> IDFBlocks;SmallPtrSet<BasicBlock *, 32> LiveInBlocks;IDF.resetLiveInBlocks();IDF.calculate(IDFBlocks);EXPECT_EQ(IDFBlocks.size(), 2UL);EXPECT_EQ(DT->getNode(A)->getDFSNumIn(), 0UL);EXPECT_EQ(IDFBlocks[0], D);EXPECT_EQ(IDFBlocks[1], E);EXPECT_TRUE(DT->getNode(IDFBlocks[0])->getDFSNumIn() <DT->getNode(IDFBlocks[1])->getDFSNumIn());});}namespace {const auto Insert = CFGBuilder::ActionKind::Insert;const auto Delete = CFGBuilder::ActionKind::Delete;bool CompUpdates(const CFGBuilder::Update &A, const CFGBuilder::Update &B) {return std::tie(A.Action, A.Edge.From, A.Edge.To) <std::tie(B.Action, B.Edge.From, B.Edge.To);}} // namespaceTEST(DominatorTree, InsertReachable) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},{"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"12", "10"}},{Insert, {"10", "9"}},{Insert, {"7", "6"}},{Insert, {"7", "5"}}};CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {EXPECT_EQ(LastUpdate->Action, Insert);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);DT.insertEdge(From, To);EXPECT_TRUE(DT.verify());PDT.insertEdge(From, To);EXPECT_TRUE(PDT.verify());}}TEST(DominatorTree, InsertReachable2) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},{"7", "5"}, {"2", "8"}, {"8", "11"}, {"11", "12"}, {"12", "10"},{"10", "9"}, {"9", "10"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"10", "7"}}};CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate = B.applyUpdate();EXPECT_TRUE(LastUpdate);EXPECT_EQ(LastUpdate->Action, Insert);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);DT.insertEdge(From, To);EXPECT_TRUE(DT.verify());PDT.insertEdge(From, To);EXPECT_TRUE(PDT.verify());}TEST(DominatorTree, InsertUnreachable) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"},{"5", "6"}, {"5", "7"}, {"3", "8"},{"9", "10"}, {"11", "12"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"4", "5"}},{Insert, {"8", "9"}},{Insert, {"10", "12"}},{Insert, {"10", "11"}}};CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {EXPECT_EQ(LastUpdate->Action, Insert);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);DT.insertEdge(From, To);EXPECT_TRUE(DT.verify());PDT.insertEdge(From, To);EXPECT_TRUE(PDT.verify());}}TEST(DominatorTree, InsertFromUnreachable) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"3", "5"}}};CFGBuilder B(Holder.F, Arcs, Updates);PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate = B.applyUpdate();EXPECT_TRUE(LastUpdate);EXPECT_EQ(LastUpdate->Action, Insert);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);PDT.insertEdge(From, To);EXPECT_TRUE(PDT.verify());EXPECT_EQ(PDT.root_size(), 2UL);// Make sure we can use a const pointer with getNode.const BasicBlock *BB5 = B.getOrAddBlock("5");EXPECT_NE(PDT.getNode(BB5), nullptr);}TEST(DominatorTree, InsertMixed) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"5", "6"}, {"5", "7"},{"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}, {"7", "3"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"4", "5"}}, {Insert, {"2", "5"}}, {Insert, {"10", "9"}},{Insert, {"12", "10"}}, {Insert, {"12", "10"}}, {Insert, {"7", "8"}},{Insert, {"7", "5"}}};CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {EXPECT_EQ(LastUpdate->Action, Insert);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);DT.insertEdge(From, To);EXPECT_TRUE(DT.verify());PDT.insertEdge(From, To);EXPECT_TRUE(PDT.verify());}}TEST(DominatorTree, InsertPermut) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"5", "6"}, {"5", "7"},{"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}, {"7", "3"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"4", "5"}},{Insert, {"2", "5"}},{Insert, {"10", "9"}},{Insert, {"12", "10"}}};while (std::next_permutation(Updates.begin(), Updates.end(), CompUpdates)) {CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {EXPECT_EQ(LastUpdate->Action, Insert);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);DT.insertEdge(From, To);EXPECT_TRUE(DT.verify());PDT.insertEdge(From, To);EXPECT_TRUE(PDT.verify());}}}TEST(DominatorTree, DeleteReachable) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"2", "4"}, {"3", "4"}, {"4", "5"}, {"5", "6"},{"5", "7"}, {"7", "8"}, {"3", "8"}, {"8", "9"}, {"9", "10"}, {"10", "2"}};std::vector<CFGBuilder::Update> Updates = {{Delete, {"2", "4"}}, {Delete, {"7", "8"}}, {Delete, {"10", "2"}}};CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {EXPECT_EQ(LastUpdate->Action, Delete);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);DT.deleteEdge(From, To);EXPECT_TRUE(DT.verify());PDT.deleteEdge(From, To);EXPECT_TRUE(PDT.verify());}}TEST(DominatorTree, DeleteUnreachable) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},{"7", "8"}, {"3", "8"}, {"8", "9"}, {"9", "10"}, {"10", "2"}};std::vector<CFGBuilder::Update> Updates = {{Delete, {"8", "9"}}, {Delete, {"7", "8"}}, {Delete, {"3", "4"}}};CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {EXPECT_EQ(LastUpdate->Action, Delete);BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);DT.deleteEdge(From, To);EXPECT_TRUE(DT.verify());PDT.deleteEdge(From, To);EXPECT_TRUE(PDT.verify());}}TEST(DominatorTree, InsertDelete) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},{"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"2", "4"}}, {Insert, {"12", "10"}}, {Insert, {"10", "9"}},{Insert, {"7", "6"}}, {Insert, {"7", "5"}}, {Delete, {"3", "8"}},{Insert, {"10", "7"}}, {Insert, {"2", "8"}}, {Delete, {"3", "4"}},{Delete, {"8", "9"}}, {Delete, {"11", "12"}}};CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);if (LastUpdate->Action == Insert) {DT.insertEdge(From, To);PDT.insertEdge(From, To);} else {DT.deleteEdge(From, To);PDT.deleteEdge(From, To);}EXPECT_TRUE(DT.verify());EXPECT_TRUE(PDT.verify());}}TEST(DominatorTree, InsertDeleteExhaustive) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},{"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};std::vector<CFGBuilder::Update> Updates = {{Insert, {"2", "4"}}, {Insert, {"12", "10"}}, {Insert, {"10", "9"}},{Insert, {"7", "6"}}, {Insert, {"7", "5"}}, {Delete, {"3", "8"}},{Insert, {"10", "7"}}, {Insert, {"2", "8"}}, {Delete, {"3", "4"}},{Delete, {"8", "9"}}, {Delete, {"11", "12"}}};std::mt19937 Generator(0);for (unsigned i = 0; i < 16; ++i) {std::shuffle(Updates.begin(), Updates.end(), Generator);CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());Optional<CFGBuilder::Update> LastUpdate;while ((LastUpdate = B.applyUpdate())) {BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);if (LastUpdate->Action == Insert) {DT.insertEdge(From, To);PDT.insertEdge(From, To);} else {DT.deleteEdge(From, To);PDT.deleteEdge(From, To);}EXPECT_TRUE(DT.verify());EXPECT_TRUE(PDT.verify());}}}TEST(DominatorTree, InsertIntoIrreducible) {std::vector<CFGBuilder::Arc> Arcs = {{"0", "1"},{"1", "27"}, {"1", "7"},{"10", "18"},{"13", "10"},{"18", "13"}, {"18", "23"},{"23", "13"}, {"23", "24"},{"24", "1"}, {"24", "18"},{"27", "24"}};CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, {{Insert, {"7", "23"}}});DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());B.applyUpdate();BasicBlock *From = B.getOrAddBlock("7");BasicBlock *To = B.getOrAddBlock("23");DT.insertEdge(From, To);EXPECT_TRUE(DT.verify());}TEST(DominatorTree, EdgeDomination) {StringRef ModuleString = "define i32 @f(i1 %cond) {\n"" bb0:\n"" br i1 %cond, label %bb1, label %bb2\n"" bb1:\n"" br label %bb3\n"" bb2:\n"" br label %bb3\n"" bb3:\n"" ret i32 4""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f",[&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Function::iterator FI = F.begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;BasicBlock *BB2 = &*FI++;BasicBlock *BB3 = &*FI++;BasicBlockEdge E01(BB0, BB1);BasicBlockEdge E02(BB0, BB2);BasicBlockEdge E13(BB1, BB3);BasicBlockEdge E23(BB2, BB3);EXPECT_TRUE(DT->dominates(E01, E01));EXPECT_FALSE(DT->dominates(E01, E02));EXPECT_TRUE(DT->dominates(E01, E13));EXPECT_FALSE(DT->dominates(E01, E23));EXPECT_FALSE(DT->dominates(E02, E01));EXPECT_TRUE(DT->dominates(E02, E02));EXPECT_FALSE(DT->dominates(E02, E13));EXPECT_TRUE(DT->dominates(E02, E23));EXPECT_FALSE(DT->dominates(E13, E01));EXPECT_FALSE(DT->dominates(E13, E02));EXPECT_TRUE(DT->dominates(E13, E13));EXPECT_FALSE(DT->dominates(E13, E23));EXPECT_FALSE(DT->dominates(E23, E01));EXPECT_FALSE(DT->dominates(E23, E02));EXPECT_FALSE(DT->dominates(E23, E13));EXPECT_TRUE(DT->dominates(E23, E23));});}TEST(DominatorTree, ValueDomination) {StringRef ModuleString = R"(@foo = global i8 0define i8 @f(i8 %arg) {ret i8 %arg})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);runWithDomTree(*M, "f",[&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) {Argument *A = F.getArg(0);GlobalValue *G = M->getNamedValue("foo");Constant *C = ConstantInt::getNullValue(Type::getInt8Ty(Context));Instruction *I = F.getEntryBlock().getTerminator();EXPECT_TRUE(DT->dominates(A, I));EXPECT_TRUE(DT->dominates(G, I));EXPECT_TRUE(DT->dominates(C, I));const Use &U = I->getOperandUse(0);EXPECT_TRUE(DT->dominates(A, U));EXPECT_TRUE(DT->dominates(G, U));EXPECT_TRUE(DT->dominates(C, U));});}
//===- llvm/unittests/IR/DominatorTreeBatchUpdatesTest.cpp ----------------===////// 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////===----------------------------------------------------------------------===//#include "CFGBuilder.h"#include "llvm/Analysis/PostDominators.h"#include "llvm/IR/Dominators.h"#include "llvm/Support/GenericDomTreeConstruction.h"#include "gtest/gtest.h"#include <random>#define DEBUG_TYPE "batch-update-tests"using namespace llvm;namespace {const auto CFGInsert = CFGBuilder::ActionKind::Insert;const auto CFGDelete = CFGBuilder::ActionKind::Delete;using DomUpdate = DominatorTree::UpdateType;static_assert(std::is_same<DomUpdate, PostDominatorTree::UpdateType>::value,"Trees differing only in IsPostDom should have the same update types");using DomSNCA = DomTreeBuilder::SemiNCAInfo<DomTreeBuilder::BBDomTree>;using PostDomSNCA = DomTreeBuilder::SemiNCAInfo<DomTreeBuilder::BBPostDomTree>;const auto Insert = DominatorTree::Insert;const auto Delete = DominatorTree::Delete;std::vector<DomUpdate> ToDomUpdates(CFGBuilder &B,std::vector<CFGBuilder::Update> &In) {std::vector<DomUpdate> Res;Res.reserve(In.size());for (const auto &CFGU : In)Res.push_back({CFGU.Action == CFGInsert ? Insert : Delete,B.getOrAddBlock(CFGU.Edge.From),B.getOrAddBlock(CFGU.Edge.To)});return Res;}} // namespaceTEST(DominatorTreeBatchUpdates, LegalizeDomUpdates) {CFGHolder Holder;CFGBuilder Builder(Holder.F, {{"A", "B"}}, {});BasicBlock *A = Builder.getOrAddBlock("A");BasicBlock *B = Builder.getOrAddBlock("B");BasicBlock *C = Builder.getOrAddBlock("C");BasicBlock *D = Builder.getOrAddBlock("D");std::vector<DomUpdate> Updates = {{Insert, B, C}, {Insert, C, D}, {Delete, B, C}, {Insert, B, C},{Insert, B, D}, {Delete, C, D}, {Delete, A, B}};SmallVector<DomUpdate, 4> Legalized;cfg::LegalizeUpdates<BasicBlock *>(Updates, Legalized, false);LLVM_DEBUG(dbgs() << "Legalized updates:\t");LLVM_DEBUG(for (auto &U : Legalized) { U.dump(); dbgs() << ", "; });LLVM_DEBUG(dbgs() << "\n");EXPECT_EQ(Legalized.size(), 3UL);EXPECT_TRUE(llvm::is_contained(Legalized, DomUpdate{Insert, B, C}));EXPECT_TRUE(llvm::is_contained(Legalized, DomUpdate{Insert, B, D}));EXPECT_TRUE(llvm::is_contained(Legalized, DomUpdate{Delete, A, B}));}TEST(DominatorTreeBatchUpdates, LegalizePostDomUpdates) {CFGHolder Holder;CFGBuilder Builder(Holder.F, {{"A", "B"}}, {});BasicBlock *A = Builder.getOrAddBlock("A");BasicBlock *B = Builder.getOrAddBlock("B");BasicBlock *C = Builder.getOrAddBlock("C");BasicBlock *D = Builder.getOrAddBlock("D");std::vector<DomUpdate> Updates = {{Insert, B, C}, {Insert, C, D}, {Delete, B, C}, {Insert, B, C},{Insert, B, D}, {Delete, C, D}, {Delete, A, B}};SmallVector<DomUpdate, 4> Legalized;cfg::LegalizeUpdates<BasicBlock *>(Updates, Legalized, true);LLVM_DEBUG(dbgs() << "Legalized postdom updates:\t");LLVM_DEBUG(for (auto &U : Legalized) { U.dump(); dbgs() << ", "; });LLVM_DEBUG(dbgs() << "\n");EXPECT_EQ(Legalized.size(), 3UL);EXPECT_TRUE(llvm::is_contained(Legalized, DomUpdate{Insert, C, B}));EXPECT_TRUE(llvm::is_contained(Legalized, DomUpdate{Insert, D, B}));EXPECT_TRUE(llvm::is_contained(Legalized, DomUpdate{Delete, B, A}));}TEST(DominatorTreeBatchUpdates, SingleInsertion) {CFGHolder Holder;CFGBuilder Builder(Holder.F, {{"A", "B"}}, {{CFGInsert, {"B", "C"}}});DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());BasicBlock *B = Builder.getOrAddBlock("B");BasicBlock *C = Builder.getOrAddBlock("C");std::vector<DomUpdate> Updates = {{Insert, B, C}};ASSERT_TRUE(Builder.applyUpdate());DT.applyUpdates(Updates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(Updates);EXPECT_TRUE(PDT.verify());}TEST(DominatorTreeBatchUpdates, SingleDeletion) {CFGHolder Holder;CFGBuilder Builder(Holder.F, {{"A", "B"}, {"B", "C"}},{{CFGDelete, {"B", "C"}}});DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());BasicBlock *B = Builder.getOrAddBlock("B");BasicBlock *C = Builder.getOrAddBlock("C");std::vector<DomUpdate> Updates = {{Delete, B, C}};ASSERT_TRUE(Builder.applyUpdate());DT.applyUpdates(Updates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(Updates);EXPECT_TRUE(PDT.verify());}TEST(DominatorTreeBatchUpdates, FewInsertion) {std::vector<CFGBuilder::Update> CFGUpdates = {{CFGInsert, {"B", "C"}},{CFGInsert, {"C", "B"}},{CFGInsert, {"C", "D"}},{CFGInsert, {"D", "E"}}};CFGHolder Holder;CFGBuilder Builder(Holder.F, {{"A", "B"}}, CFGUpdates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());BasicBlock *B = Builder.getOrAddBlock("B");BasicBlock *C = Builder.getOrAddBlock("C");BasicBlock *D = Builder.getOrAddBlock("D");BasicBlock *E = Builder.getOrAddBlock("E");std::vector<DomUpdate> Updates = {{Insert, B, C}, {Insert, C, B}, {Insert, C, D}, {Insert, D, E}};while (Builder.applyUpdate());DT.applyUpdates(Updates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(Updates);EXPECT_TRUE(PDT.verify());}TEST(DominatorTreeBatchUpdates, FewDeletions) {std::vector<CFGBuilder::Update> CFGUpdates = {{CFGDelete, {"B", "C"}},{CFGDelete, {"C", "B"}},{CFGDelete, {"B", "D"}},{CFGDelete, {"D", "E"}}};CFGHolder Holder;CFGBuilder Builder(Holder.F, {{"A", "B"}, {"B", "C"}, {"B", "D"}, {"D", "E"}, {"C", "B"}},CFGUpdates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());auto Updates = ToDomUpdates(Builder, CFGUpdates);while (Builder.applyUpdate());DT.applyUpdates(Updates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(Updates);EXPECT_TRUE(PDT.verify());}TEST(DominatorTreeBatchUpdates, InsertDelete) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},{"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};std::vector<CFGBuilder::Update> Updates = {{CFGInsert, {"2", "4"}}, {CFGInsert, {"12", "10"}},{CFGInsert, {"10", "9"}}, {CFGInsert, {"7", "6"}},{CFGInsert, {"7", "5"}}, {CFGDelete, {"3", "8"}},{CFGInsert, {"10", "7"}}, {CFGInsert, {"2", "8"}},{CFGDelete, {"3", "4"}}, {CFGDelete, {"8", "9"}},{CFGDelete, {"11", "12"}}};CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());while (B.applyUpdate());auto DomUpdates = ToDomUpdates(B, Updates);DT.applyUpdates(DomUpdates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(DomUpdates);EXPECT_TRUE(PDT.verify());}TEST(DominatorTreeBatchUpdates, InsertDeleteExhaustive) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}, {"4", "5"}, {"5", "6"}, {"5", "7"},{"3", "8"}, {"8", "9"}, {"9", "10"}, {"8", "11"}, {"11", "12"}};std::vector<CFGBuilder::Update> Updates = {{CFGInsert, {"2", "4"}}, {CFGInsert, {"12", "10"}},{CFGInsert, {"10", "9"}}, {CFGInsert, {"7", "6"}},{CFGInsert, {"7", "5"}}, {CFGDelete, {"3", "8"}},{CFGInsert, {"10", "7"}}, {CFGInsert, {"2", "8"}},{CFGDelete, {"3", "4"}}, {CFGDelete, {"8", "9"}},{CFGDelete, {"11", "12"}}};std::mt19937 Generator(0);for (unsigned i = 0; i < 16; ++i) {std::shuffle(Updates.begin(), Updates.end(), Generator);CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());while (B.applyUpdate());auto DomUpdates = ToDomUpdates(B, Updates);DT.applyUpdates(DomUpdates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(DomUpdates);EXPECT_TRUE(PDT.verify());}}// These are some odd flowgraphs, usually generated from csmith cases,// which are difficult on post dom trees.TEST(DominatorTreeBatchUpdates, InfiniteLoop) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"},{"2", "3"},{"3", "6"}, {"3", "5"},{"4", "5"},{"5", "2"},{"6", "3"}, {"6", "4"}};// SplitBlock on 3 -> 5std::vector<CFGBuilder::Update> Updates = {{CFGInsert, {"N", "5"}}, {CFGInsert, {"3", "N"}}, {CFGDelete, {"3", "5"}}};CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());while (B.applyUpdate());auto DomUpdates = ToDomUpdates(B, Updates);DT.applyUpdates(DomUpdates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(DomUpdates);EXPECT_TRUE(PDT.verify());}TEST(DominatorTreeBatchUpdates, DeadBlocks) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"},{"2", "3"},{"3", "4"}, {"3", "7"},{"4", "4"},{"5", "6"}, {"5", "7"},{"6", "7"},{"7", "2"}, {"7", "8"}};// Remove dead 5 and 7,// plus SplitBlock on 7 -> 8std::vector<CFGBuilder::Update> Updates = {{CFGDelete, {"6", "7"}}, {CFGDelete, {"5", "7"}}, {CFGDelete, {"5", "6"}},{CFGInsert, {"N", "8"}}, {CFGInsert, {"7", "N"}}, {CFGDelete, {"7", "8"}}};CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());while (B.applyUpdate());auto DomUpdates = ToDomUpdates(B, Updates);DT.applyUpdates(DomUpdates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(DomUpdates);EXPECT_TRUE(PDT.verify());}TEST(DominatorTreeBatchUpdates, InfiniteLoop2) {std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"},{"2", "6"}, {"2", "3"},{"3", "4"},{"4", "5"}, {"4", "6"},{"5", "4"},{"6", "2"}};// SplitBlock on 4 -> 6std::vector<CFGBuilder::Update> Updates = {{CFGInsert, {"N", "6"}}, {CFGInsert, {"4", "N"}}, {CFGDelete, {"4", "6"}}};CFGHolder Holder;CFGBuilder B(Holder.F, Arcs, Updates);DominatorTree DT(*Holder.F);EXPECT_TRUE(DT.verify());PostDominatorTree PDT(*Holder.F);EXPECT_TRUE(PDT.verify());while (B.applyUpdate());auto DomUpdates = ToDomUpdates(B, Updates);DT.applyUpdates(DomUpdates);EXPECT_TRUE(DT.verify());PDT.applyUpdates(DomUpdates);EXPECT_TRUE(PDT.verify());}
//===- DemandedBitsTest.cpp - DemandedBits tests --------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/DemandedBits.h"#include "../Support/KnownBitsTest.h"#include "llvm/Support/KnownBits.h"#include "gtest/gtest.h"using namespace llvm;namespace {template <typename Fn1, typename Fn2>static void TestBinOpExhaustive(Fn1 PropagateFn, Fn2 EvalFn) {unsigned Bits = 4;unsigned Max = 1 << Bits;ForeachKnownBits(Bits, [&](const KnownBits &Known1) {ForeachKnownBits(Bits, [&](const KnownBits &Known2) {for (unsigned AOut_ = 0; AOut_ < Max; AOut_++) {APInt AOut(Bits, AOut_);APInt AB1 = PropagateFn(0, AOut, Known1, Known2);APInt AB2 = PropagateFn(1, AOut, Known1, Known2);{// If the propagator claims that certain known bits// didn't matter, check it doesn't change its mind// when they become unknown.KnownBits Known1Redacted;KnownBits Known2Redacted;Known1Redacted.Zero = Known1.Zero & AB1;Known1Redacted.One = Known1.One & AB1;Known2Redacted.Zero = Known2.Zero & AB2;Known2Redacted.One = Known2.One & AB2;APInt AB1R = PropagateFn(0, AOut, Known1Redacted, Known2Redacted);APInt AB2R = PropagateFn(1, AOut, Known1Redacted, Known2Redacted);EXPECT_EQ(AB1, AB1R);EXPECT_EQ(AB2, AB2R);}ForeachNumInKnownBits(Known1, [&](APInt Value1) {ForeachNumInKnownBits(Known2, [&](APInt Value2) {APInt ReferenceResult = EvalFn((Value1 & AB1), (Value2 & AB2));APInt Result = EvalFn(Value1, Value2);EXPECT_EQ(Result & AOut, ReferenceResult & AOut);});});}});});}TEST(DemandedBitsTest, Add) {TestBinOpExhaustive(DemandedBits::determineLiveOperandBitsAdd,[](APInt N1, APInt N2) -> APInt { return N1 + N2; });}TEST(DemandedBitsTest, Sub) {TestBinOpExhaustive(DemandedBits::determineLiveOperandBitsSub,[](APInt N1, APInt N2) -> APInt { return N1 - N2; });}} // anonymous namespace
//===- DebugTypeODRUniquingTest.cpp - Debug type ODR uniquing tests -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/LLVMContext.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(DebugTypeODRUniquingTest, enableDebugTypeODRUniquing) {LLVMContext Context;EXPECT_FALSE(Context.isODRUniquingDebugTypes());Context.enableDebugTypeODRUniquing();EXPECT_TRUE(Context.isODRUniquingDebugTypes());Context.disableDebugTypeODRUniquing();EXPECT_FALSE(Context.isODRUniquingDebugTypes());}TEST(DebugTypeODRUniquingTest, getODRType) {LLVMContext Context;MDString &UUID = *MDString::get(Context, "string");// Without a type map, this should return null.EXPECT_FALSE(DICompositeType::getODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, nullptr,nullptr, nullptr, nullptr, nullptr, nullptr));// Enable the mapping. There still shouldn't be a type.Context.enableDebugTypeODRUniquing();EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID));// Create some ODR-uniqued type.auto &CT = *DICompositeType::getODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, nullptr,nullptr, nullptr, nullptr, nullptr, nullptr);EXPECT_EQ(UUID.getString(), CT.getIdentifier());// Check that we get it back, even if we change a field.EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID));EXPECT_EQ(&CT,DICompositeType::getODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0,nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0,nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr));EXPECT_EQ(&CT, DICompositeType::getODRType(Context, UUID, dwarf::DW_TAG_class_type,MDString::get(Context, "name"), nullptr, 0, nullptr,nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr,nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr));// Check that it's discarded with the type map.Context.disableDebugTypeODRUniquing();EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID));// And it shouldn't magically reappear...Context.enableDebugTypeODRUniquing();EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID));}TEST(DebugTypeODRUniquingTest, buildODRType) {LLVMContext Context;Context.enableDebugTypeODRUniquing();// Build an ODR type that's a forward decl.MDString &UUID = *MDString::get(Context, "Type");auto &CT = *DICompositeType::buildODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr,nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr,nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID));EXPECT_EQ(dwarf::DW_TAG_class_type, CT.getTag());// Update with another forward decl. This should be a no-op.EXPECT_EQ(&CT, DICompositeType::buildODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr,0, nullptr, nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr,0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr, nullptr));EXPECT_FALSE(DICompositeType::buildODRType(Context, UUID, dwarf::DW_TAG_structure_type, nullptr, nullptr, 0, nullptr,nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr,nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));// Update with a definition. This time we should see a change.EXPECT_EQ(&CT, DICompositeType::buildODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr,0, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0,nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr, nullptr));EXPECT_FALSE(CT.isForwardDecl());// Further updates should be ignored.EXPECT_EQ(&CT, DICompositeType::buildODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr,0, nullptr, nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr,0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr, nullptr));EXPECT_FALSE(CT.isForwardDecl());EXPECT_EQ(&CT, DICompositeType::buildODRType(Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr,111u, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr,0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr, nullptr));EXPECT_NE(111u, CT.getLine());}TEST(DebugTypeODRUniquingTest, buildODRTypeFields) {LLVMContext Context;Context.enableDebugTypeODRUniquing();// Build an ODR type that's a forward decl with no other fields set.MDString &UUID = *MDString::get(Context, "UUID");auto &CT = *DICompositeType::buildODRType(Context, UUID, 0, nullptr, nullptr, 0, nullptr, nullptr, 0, 0, 0,DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr, nullptr, nullptr,nullptr, nullptr, nullptr, nullptr);// Create macros for running through all the fields except Identifier and Flags.#define FOR_EACH_MDFIELD() \DO_FOR_FIELD(Name) \DO_FOR_FIELD(File) \DO_FOR_FIELD(Scope) \DO_FOR_FIELD(BaseType) \DO_FOR_FIELD(Elements) \DO_FOR_FIELD(VTableHolder) \DO_FOR_FIELD(TemplateParams)#define FOR_EACH_INLINEFIELD() \DO_FOR_FIELD(Line) \DO_FOR_FIELD(SizeInBits) \DO_FOR_FIELD(AlignInBits) \DO_FOR_FIELD(OffsetInBits) \DO_FOR_FIELD(RuntimeLang)// Create all the fields.#define DO_FOR_FIELD(X) auto *X = MDString::get(Context, #X);FOR_EACH_MDFIELD();#undef DO_FOR_FIELDunsigned NonZeroInit = 0;#define DO_FOR_FIELD(X) auto X = ++NonZeroInit;FOR_EACH_INLINEFIELD();#undef DO_FOR_FIELD// Replace all the fields with new values that are distinct from each other.EXPECT_EQ(&CT,DICompositeType::buildODRType(Context, UUID, 0, Name, File, Line, Scope, BaseType, SizeInBits,AlignInBits, OffsetInBits, DINode::FlagArtificial, Elements,RuntimeLang, VTableHolder, TemplateParams, nullptr, nullptr,nullptr, nullptr, nullptr, nullptr));// Confirm that all the right fields got updated.#define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.getRaw##X());FOR_EACH_MDFIELD();#undef DO_FOR_FIELD#undef FOR_EACH_MDFIELD#define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.get##X());FOR_EACH_INLINEFIELD();#undef DO_FOR_FIELD#undef FOR_EACH_INLINEFIELDEXPECT_EQ(DINode::FlagArtificial, CT.getFlags());EXPECT_EQ(&UUID, CT.getRawIdentifier());}} // end namespace
//===- llvm/unittest/IR/DebugInfo.cpp - DebugInfo tests -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/DebugInfo.h"#include "llvm/ADT/APSInt.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Metadata.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/Local.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("DebugInfoTest", errs());return Mod;}namespace {TEST(DINodeTest, getFlag) {// Some valid flags.EXPECT_EQ(DINode::FlagPublic, DINode::getFlag("DIFlagPublic"));EXPECT_EQ(DINode::FlagProtected, DINode::getFlag("DIFlagProtected"));EXPECT_EQ(DINode::FlagPrivate, DINode::getFlag("DIFlagPrivate"));EXPECT_EQ(DINode::FlagVector, DINode::getFlag("DIFlagVector"));EXPECT_EQ(DINode::FlagRValueReference,DINode::getFlag("DIFlagRValueReference"));// FlagAccessibility shouldn't work.EXPECT_EQ(0u, DINode::getFlag("DIFlagAccessibility"));// Some other invalid strings.EXPECT_EQ(0u, DINode::getFlag("FlagVector"));EXPECT_EQ(0u, DINode::getFlag("Vector"));EXPECT_EQ(0u, DINode::getFlag("other things"));EXPECT_EQ(0u, DINode::getFlag("DIFlagOther"));}TEST(DINodeTest, getFlagString) {// Some valid flags.EXPECT_EQ(StringRef("DIFlagPublic"),DINode::getFlagString(DINode::FlagPublic));EXPECT_EQ(StringRef("DIFlagProtected"),DINode::getFlagString(DINode::FlagProtected));EXPECT_EQ(StringRef("DIFlagPrivate"),DINode::getFlagString(DINode::FlagPrivate));EXPECT_EQ(StringRef("DIFlagVector"),DINode::getFlagString(DINode::FlagVector));EXPECT_EQ(StringRef("DIFlagRValueReference"),DINode::getFlagString(DINode::FlagRValueReference));// FlagAccessibility actually equals FlagPublic.EXPECT_EQ(StringRef("DIFlagPublic"),DINode::getFlagString(DINode::FlagAccessibility));// Some other invalid flags.EXPECT_EQ(StringRef(),DINode::getFlagString(DINode::FlagPublic | DINode::FlagVector));EXPECT_EQ(StringRef(), DINode::getFlagString(DINode::FlagFwdDecl |DINode::FlagArtificial));EXPECT_EQ(StringRef(),DINode::getFlagString(static_cast<DINode::DIFlags>(0xffff)));}TEST(DINodeTest, splitFlags) {// Some valid flags.#define CHECK_SPLIT(FLAGS, VECTOR, REMAINDER) \{ \SmallVector<DINode::DIFlags, 8> V; \EXPECT_EQ(REMAINDER, DINode::splitFlags(FLAGS, V)); \EXPECT_TRUE(makeArrayRef(V).equals(VECTOR)); \}CHECK_SPLIT(DINode::FlagPublic, {DINode::FlagPublic}, DINode::FlagZero);CHECK_SPLIT(DINode::FlagProtected, {DINode::FlagProtected}, DINode::FlagZero);CHECK_SPLIT(DINode::FlagPrivate, {DINode::FlagPrivate}, DINode::FlagZero);CHECK_SPLIT(DINode::FlagVector, {DINode::FlagVector}, DINode::FlagZero);CHECK_SPLIT(DINode::FlagRValueReference, {DINode::FlagRValueReference},DINode::FlagZero);DINode::DIFlags Flags[] = {DINode::FlagFwdDecl, DINode::FlagVector};CHECK_SPLIT(DINode::FlagFwdDecl | DINode::FlagVector, Flags,DINode::FlagZero);CHECK_SPLIT(DINode::FlagZero, {}, DINode::FlagZero);#undef CHECK_SPLIT}TEST(StripTest, LoopMetadata) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define void @f() !dbg !5 {ret void, !dbg !10, !llvm.loop !11}!llvm.dbg.cu = !{!0}!llvm.debugify = !{!3, !3}!llvm.module.flags = !{!4}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "loop.ll", directory: "/")!2 = !{}!3 = !{i32 1}!4 = !{i32 2, !"Debug Info Version", i32 3}!5 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !7)!6 = !DISubroutineType(types: !2)!7 = !{!8}!8 = !DILocalVariable(name: "1", scope: !5, file: !1, line: 1, type: !9)!9 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)!10 = !DILocation(line: 1, column: 1, scope: !5)!11 = distinct !{!11, !10, !10})");// Look up the debug info emission kind for the CU via the loop metadata// attached to the terminator. If, when stripping non-line table debug info,// we update the terminator's metadata correctly, we should be able to// observe the change in emission kind for the CU.auto getEmissionKind = [&]() {Instruction &I = *M->getFunction("f")->getEntryBlock().getFirstNonPHI();MDNode *LoopMD = I.getMetadata(LLVMContext::MD_loop);return cast<DILocation>(LoopMD->getOperand(1))->getScope()->getSubprogram()->getUnit()->getEmissionKind();};EXPECT_EQ(getEmissionKind(), DICompileUnit::FullDebug);bool Changed = stripNonLineTableDebugInfo(*M);EXPECT_TRUE(Changed);EXPECT_EQ(getEmissionKind(), DICompileUnit::LineTablesOnly);bool BrokenDebugInfo = false;bool HardError = verifyModule(*M, &errs(), &BrokenDebugInfo);EXPECT_FALSE(HardError);EXPECT_FALSE(BrokenDebugInfo);}TEST(MetadataTest, DeleteInstUsedByDbgValue) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define i16 @f(i16 %a) !dbg !6 {%b = add i16 %a, 1, !dbg !11call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11ret i16 0, !dbg !11}declare void @llvm.dbg.value(metadata, metadata, metadata) #0attributes #0 = { nounwind readnone speculatable willreturn }!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t.ll", directory: "/")!2 = !{}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)!7 = !DISubroutineType(types: !2)!8 = !{!9}!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)!11 = !DILocation(line: 1, column: 1, scope: !6))");// Find %b = add ...Instruction &I = *M->getFunction("f")->getEntryBlock().getFirstNonPHI();// Find the dbg.value using %b.SmallVector<DbgValueInst *, 1> DVIs;findDbgValues(DVIs, &I);// Delete %b. The dbg.value should now point to undef.I.eraseFromParent();EXPECT_EQ(DVIs[0]->getNumVariableLocationOps(), 1u);EXPECT_TRUE(isa<UndefValue>(DVIs[0]->getValue(0)));}TEST(DIBuilder, CreateFortranArrayTypeWithAttributes) {LLVMContext Ctx;std::unique_ptr<Module> M(new Module("MyModule", Ctx));DIBuilder DIB(*M);DISubrange *Subrange = DIB.getOrCreateSubrange(1,1);SmallVector<Metadata*, 4> Subranges;Subranges.push_back(Subrange);DINodeArray Subscripts = DIB.getOrCreateArray(Subranges);auto getDIExpression = [&DIB](int offset) {SmallVector<uint64_t, 4> ops;ops.push_back(llvm::dwarf::DW_OP_push_object_address);DIExpression::appendOffset(ops, offset);ops.push_back(llvm::dwarf::DW_OP_deref);return DIB.createExpression(ops);};DIFile *F = DIB.createFile("main.c", "/");DICompileUnit *CU = DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile("main.c", "/"), "llvm-c", true, "", 0);DIVariable *DataLocation =DIB.createTempGlobalVariableFwdDecl(CU, "dl", "_dl", F, 1, nullptr, true);DIExpression *Associated = getDIExpression(1);DIExpression *Allocated = getDIExpression(2);DIExpression *Rank = DIB.createConstantValueExpression(3);DICompositeType *ArrayType = DIB.createArrayType(0, 0, nullptr, Subscripts,DataLocation, Associated,Allocated, Rank);EXPECT_TRUE(isa_and_nonnull<DICompositeType>(ArrayType));EXPECT_EQ(ArrayType->getRawDataLocation(), DataLocation);EXPECT_EQ(ArrayType->getRawAssociated(), Associated);EXPECT_EQ(ArrayType->getRawAllocated(), Allocated);EXPECT_EQ(ArrayType->getRawRank(), Rank);// Avoid memory leak.DIVariable::deleteTemporary(DataLocation);}TEST(DIBuilder, CreateSetType) {LLVMContext Ctx;std::unique_ptr<Module> M(new Module("MyModule", Ctx));DIBuilder DIB(*M);DIScope *Scope = DISubprogram::getDistinct(Ctx, nullptr, "", "", nullptr, 0, nullptr, 0, nullptr, 0, 0,DINode::FlagZero, DISubprogram::SPFlagZero, nullptr);DIType *Type = DIB.createBasicType("Int", 64, dwarf::DW_ATE_signed);DIFile *F = DIB.createFile("main.c", "/");DIDerivedType *SetType = DIB.createSetType(Scope, "set1", F, 1, 64, 64, Type);EXPECT_TRUE(isa_and_nonnull<DIDerivedType>(SetType));}TEST(DIBuilder, CreateStringType) {LLVMContext Ctx;std::unique_ptr<Module> M(new Module("MyModule", Ctx));DIBuilder DIB(*M);DIScope *Scope = DISubprogram::getDistinct(Ctx, nullptr, "", "", nullptr, 0, nullptr, 0, nullptr, 0, 0,DINode::FlagZero, DISubprogram::SPFlagZero, nullptr);DIFile *F = DIB.createFile("main.c", "/");StringRef StrName = "string";DIVariable *StringLen = DIB.createAutoVariable(Scope, StrName, F, 0, nullptr,false, DINode::FlagZero, 0);auto getDIExpression = [&DIB](int offset) {SmallVector<uint64_t, 4> ops;ops.push_back(llvm::dwarf::DW_OP_push_object_address);DIExpression::appendOffset(ops, offset);ops.push_back(llvm::dwarf::DW_OP_deref);return DIB.createExpression(ops);};DIExpression *StringLocationExp = getDIExpression(1);DIStringType *StringType =DIB.createStringType(StrName, StringLen, StringLocationExp);EXPECT_TRUE(isa_and_nonnull<DIStringType>(StringType));EXPECT_EQ(StringType->getName(), StrName);EXPECT_EQ(StringType->getStringLength(), StringLen);EXPECT_EQ(StringType->getStringLocationExp(), StringLocationExp);StringRef StrNameExp = "stringexp";DIExpression *StringLengthExp = getDIExpression(2);DIStringType *StringTypeExp =DIB.createStringType(StrNameExp, StringLengthExp, StringLocationExp);EXPECT_TRUE(isa_and_nonnull<DIStringType>(StringTypeExp));EXPECT_EQ(StringTypeExp->getName(), StrNameExp);EXPECT_EQ(StringTypeExp->getStringLocationExp(), StringLocationExp);EXPECT_EQ(StringTypeExp->getStringLengthExp(), StringLengthExp);}TEST(DIBuilder, DIEnumerator) {LLVMContext Ctx;std::unique_ptr<Module> M(new Module("MyModule", Ctx));DIBuilder DIB(*M);APSInt I1(APInt(32, 1));APSInt I2(APInt(33, 1));auto *E = DIEnumerator::get(Ctx, I1, I1.isSigned(), "name");EXPECT_TRUE(E);auto *E1 = DIEnumerator::getIfExists(Ctx, I1, I1.isSigned(), "name");EXPECT_TRUE(E1);auto *E2 = DIEnumerator::getIfExists(Ctx, I2, I1.isSigned(), "name");EXPECT_FALSE(E2);}TEST(DIBuilder, createDbgAddr) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"(define void @f() !dbg !6 {%a = alloca i16, align 8;; It is important that we put the debug marker on the return.;; We take advantage of that to conjure up a debug loc without;; having to synthesize one programatically.ret void, !dbg !11}declare void @llvm.dbg.value(metadata, metadata, metadata) #0attributes #0 = { nounwind readnone speculatable willreturn }!llvm.dbg.cu = !{!0}!llvm.module.flags = !{!5}!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)!1 = !DIFile(filename: "t.ll", directory: "/")!2 = !{}!5 = !{i32 2, !"Debug Info Version", i32 3}!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)!7 = !DISubroutineType(types: !2)!8 = !{!9}!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)!11 = !DILocation(line: 1, column: 1, scope: !6))");auto *F = M->getFunction("f");auto *EntryBlock = &F->getEntryBlock();auto *CU =cast<DICompileUnit>(M->getNamedMetadata("llvm.dbg.cu")->getOperand(0));auto *Alloca = &*EntryBlock->begin();auto *Ret = EntryBlock->getTerminator();auto *SP = cast<DISubprogram>(F->getMetadata(LLVMContext::MD_dbg));auto *File = SP->getFile();std::string Name = "myName";const auto *Loc = Ret->getDebugLoc().get();IRBuilder<> Builder(EntryBlock);DIBuilder DIB(*M, true, CU);DIType *DT = DIB.createBasicType("ty16", 16, dwarf::DW_ATE_unsigned);DILocalVariable *LocalVar =DIB.createAutoVariable(SP, Name, File, 5 /*line*/, DT,/*AlwaysPreserve=*/true);auto *Inst = DIB.insertDbgAddrIntrinsic(Alloca, LocalVar,DIB.createExpression(), Loc, Ret);DIB.finalize();EXPECT_EQ(Inst->getDebugLoc().get(), Loc);auto *MD0 = cast<MetadataAsValue>(Inst->getOperand(0))->getMetadata();auto *MD0Local = cast<LocalAsMetadata>(MD0);EXPECT_EQ(MD0Local->getValue(), Alloca);auto *MD1 = cast<MetadataAsValue>(Inst->getOperand(1))->getMetadata();EXPECT_EQ(MD1->getMetadataID(), Metadata::MetadataKind::DILocalVariableKind);auto *MD2 = cast<MetadataAsValue>(Inst->getOperand(2))->getMetadata();auto *MDExp = cast<DIExpression>(MD2);EXPECT_EQ(MDExp->getNumElements(), 0u);}} // end namespace
//===- ConstantRangeTest.cpp - ConstantRange tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/DataLayout.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Type.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(DataLayoutTest, FunctionPtrAlign) {EXPECT_EQ(MaybeAlign(0), DataLayout("").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(1), DataLayout("Fi8").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(2), DataLayout("Fi16").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(4), DataLayout("Fi32").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(8), DataLayout("Fi64").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(1), DataLayout("Fn8").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(2), DataLayout("Fn16").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(4), DataLayout("Fn32").getFunctionPtrAlign());EXPECT_EQ(MaybeAlign(8), DataLayout("Fn64").getFunctionPtrAlign());EXPECT_EQ(DataLayout::FunctionPtrAlignType::Independent, \DataLayout("").getFunctionPtrAlignType());EXPECT_EQ(DataLayout::FunctionPtrAlignType::Independent, \DataLayout("Fi8").getFunctionPtrAlignType());EXPECT_EQ(DataLayout::FunctionPtrAlignType::MultipleOfFunctionAlign, \DataLayout("Fn8").getFunctionPtrAlignType());EXPECT_EQ(DataLayout("Fi8"), DataLayout("Fi8"));EXPECT_NE(DataLayout("Fi8"), DataLayout("Fi16"));EXPECT_NE(DataLayout("Fi8"), DataLayout("Fn8"));DataLayout a(""), b("Fi8"), c("Fn8");EXPECT_NE(a, b);EXPECT_NE(a, c);EXPECT_NE(b, c);a = b;EXPECT_EQ(a, b);a = c;EXPECT_EQ(a, c);}TEST(DataLayoutTest, ValueOrABITypeAlignment) {const DataLayout DL("Fi8");LLVMContext Context;Type *const FourByteAlignType = Type::getInt32Ty(Context);EXPECT_EQ(Align(16),DL.getValueOrABITypeAlignment(MaybeAlign(16), FourByteAlignType));EXPECT_EQ(Align(4),DL.getValueOrABITypeAlignment(MaybeAlign(), FourByteAlignType));}TEST(DataLayoutTest, GlobalsAddressSpace) {// When not explicitly defined the globals address space should be zero:EXPECT_EQ(DataLayout("").getDefaultGlobalsAddressSpace(), 0u);EXPECT_EQ(DataLayout("P1-A2").getDefaultGlobalsAddressSpace(), 0u);EXPECT_EQ(DataLayout("G2").getDefaultGlobalsAddressSpace(), 2u);// Check that creating a GlobalVariable without an explicit address space// in a module with a default globals address space respects that default:LLVMContext Context;std::unique_ptr<Module> M(new Module("MyModule", Context));// Default is globals in address space zero:auto *Int32 = Type::getInt32Ty(Context);auto *DefaultGlobal1 = new GlobalVariable(*M, Int32, false, GlobalValue::ExternalLinkage, nullptr);EXPECT_EQ(DefaultGlobal1->getAddressSpace(), 0u);auto *ExplicitGlobal1 = new GlobalVariable(*M, Int32, false, GlobalValue::ExternalLinkage, nullptr, "", nullptr,GlobalValue::NotThreadLocal, 123);EXPECT_EQ(ExplicitGlobal1->getAddressSpace(), 123u);// When using a datalayout with the global address space set to 200, global// variables should default to 200M->setDataLayout("G200");auto *DefaultGlobal2 = new GlobalVariable(*M, Int32, false, GlobalValue::ExternalLinkage, nullptr);EXPECT_EQ(DefaultGlobal2->getAddressSpace(), 200u);auto *ExplicitGlobal2 = new GlobalVariable(*M, Int32, false, GlobalValue::ExternalLinkage, nullptr, "", nullptr,GlobalValue::NotThreadLocal, 123);EXPECT_EQ(ExplicitGlobal2->getAddressSpace(), 123u);}TEST(DataLayoutTest, VectorAlign) {Expected<DataLayout> DL = DataLayout::parse("v64:64");EXPECT_THAT_EXPECTED(DL, Succeeded());LLVMContext Context;Type *const FloatTy = Type::getFloatTy(Context);Type *const V8F32Ty = FixedVectorType::get(FloatTy, 8);// The alignment for a vector type larger than any specified vector type uses// the natural alignment as a fallback.EXPECT_EQ(Align(4 * 8), DL->getABITypeAlign(V8F32Ty));EXPECT_EQ(Align(4 * 8), DL->getPrefTypeAlign(V8F32Ty));}} // anonymous namespace
//===- llvm/unittest/IR/ConstantsTest.cpp - Constants unit tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Constants.h"#include "llvm-c/Core.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/ConstantFold.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/InstrTypes.h"#include "llvm/IR/Instruction.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"namespace llvm {namespace {TEST(ConstantsTest, Integer_i1) {LLVMContext Context;IntegerType *Int1 = IntegerType::get(Context, 1);Constant *One = ConstantInt::get(Int1, 1, true);Constant *Zero = ConstantInt::get(Int1, 0);Constant *NegOne = ConstantInt::get(Int1, static_cast<uint64_t>(-1), true);EXPECT_EQ(NegOne, ConstantInt::getSigned(Int1, -1));Constant *Poison = PoisonValue::get(Int1);// Input: @b = constant i1 add(i1 1 , i1 1)// Output: @b = constant i1 falseEXPECT_EQ(Zero, ConstantExpr::getAdd(One, One));// @c = constant i1 add(i1 -1, i1 1)// @c = constant i1 falseEXPECT_EQ(Zero, ConstantExpr::getAdd(NegOne, One));// @d = constant i1 add(i1 -1, i1 -1)// @d = constant i1 falseEXPECT_EQ(Zero, ConstantExpr::getAdd(NegOne, NegOne));// @e = constant i1 sub(i1 -1, i1 1)// @e = constant i1 falseEXPECT_EQ(Zero, ConstantExpr::getSub(NegOne, One));// @f = constant i1 sub(i1 1 , i1 -1)// @f = constant i1 falseEXPECT_EQ(Zero, ConstantExpr::getSub(One, NegOne));// @g = constant i1 sub(i1 1 , i1 1)// @g = constant i1 falseEXPECT_EQ(Zero, ConstantExpr::getSub(One, One));// @h = constant i1 shl(i1 1 , i1 1) ; poison// @h = constant i1 poisonEXPECT_EQ(Poison, ConstantExpr::getShl(One, One));// @i = constant i1 shl(i1 1 , i1 0)// @i = constant i1 trueEXPECT_EQ(One, ConstantExpr::getShl(One, Zero));// @j = constant i1 lshr(i1 1, i1 1) ; poison// @j = constant i1 poisonEXPECT_EQ(Poison, ConstantExpr::getLShr(One, One));// @m = constant i1 ashr(i1 1, i1 1) ; poison// @m = constant i1 poisonEXPECT_EQ(Poison, ConstantExpr::getAShr(One, One));// @n = constant i1 mul(i1 -1, i1 1)// @n = constant i1 trueEXPECT_EQ(One, ConstantExpr::getMul(NegOne, One));// @o = constant i1 sdiv(i1 -1, i1 1) ; overflow// @o = constant i1 trueEXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::SDiv, NegOne, One));// @p = constant i1 sdiv(i1 1 , i1 -1); overflow// @p = constant i1 trueEXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::SDiv, One, NegOne));// @q = constant i1 udiv(i1 -1, i1 1)// @q = constant i1 trueEXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::UDiv, NegOne, One));// @r = constant i1 udiv(i1 1, i1 -1)// @r = constant i1 trueEXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::UDiv, One, NegOne));// @s = constant i1 srem(i1 -1, i1 1) ; overflow// @s = constant i1 falseEXPECT_EQ(Zero,ConstantFoldBinaryInstruction(Instruction::SRem, NegOne, One));// @u = constant i1 srem(i1 1, i1 -1) ; overflow// @u = constant i1 falseEXPECT_EQ(Zero,ConstantFoldBinaryInstruction(Instruction::SRem, One, NegOne));}TEST(ConstantsTest, IntSigns) {LLVMContext Context;IntegerType *Int8Ty = Type::getInt8Ty(Context);EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, false)->getSExtValue());EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, true)->getSExtValue());EXPECT_EQ(100, ConstantInt::getSigned(Int8Ty, 100)->getSExtValue());EXPECT_EQ(-50, ConstantInt::get(Int8Ty, 206)->getSExtValue());EXPECT_EQ(-50, ConstantInt::getSigned(Int8Ty, -50)->getSExtValue());EXPECT_EQ(206U, ConstantInt::getSigned(Int8Ty, -50)->getZExtValue());// Overflow is handled by truncation.EXPECT_EQ(0x3b, ConstantInt::get(Int8Ty, 0x13b)->getSExtValue());}TEST(ConstantsTest, FP128Test) {LLVMContext Context;Type *FP128Ty = Type::getFP128Ty(Context);IntegerType *Int128Ty = Type::getIntNTy(Context, 128);Constant *Zero128 = Constant::getNullValue(Int128Ty);Constant *X = ConstantExpr::getUIToFP(Zero128, FP128Ty);EXPECT_TRUE(isa<ConstantFP>(X));}TEST(ConstantsTest, PointerCast) {LLVMContext C;Type *Int8PtrTy = Type::getInt8PtrTy(C);Type *Int32PtrTy = Type::getInt32PtrTy(C);Type *Int64Ty = Type::getInt64Ty(C);VectorType *Int8PtrVecTy = FixedVectorType::get(Int8PtrTy, 4);VectorType *Int32PtrVecTy = FixedVectorType::get(Int32PtrTy, 4);VectorType *Int64VecTy = FixedVectorType::get(Int64Ty, 4);VectorType *Int8PtrScalableVecTy = ScalableVectorType::get(Int8PtrTy, 4);VectorType *Int32PtrScalableVecTy = ScalableVectorType::get(Int32PtrTy, 4);VectorType *Int64ScalableVecTy = ScalableVectorType::get(Int64Ty, 4);// ptrtoint i8* to i64EXPECT_EQ(Constant::getNullValue(Int64Ty),ConstantExpr::getPointerCast(Constant::getNullValue(Int8PtrTy), Int64Ty));// bitcast i8* to i32*EXPECT_EQ(Constant::getNullValue(Int32PtrTy),ConstantExpr::getPointerCast(Constant::getNullValue(Int8PtrTy),Int32PtrTy));// ptrtoint <4 x i8*> to <4 x i64>EXPECT_EQ(Constant::getNullValue(Int64VecTy),ConstantExpr::getPointerCast(Constant::getNullValue(Int8PtrVecTy),Int64VecTy));// ptrtoint <vscale x 4 x i8*> to <vscale x 4 x i64>EXPECT_EQ(Constant::getNullValue(Int64ScalableVecTy),ConstantExpr::getPointerCast(Constant::getNullValue(Int8PtrScalableVecTy),Int64ScalableVecTy));// bitcast <4 x i8*> to <4 x i32*>EXPECT_EQ(Constant::getNullValue(Int32PtrVecTy),ConstantExpr::getPointerCast(Constant::getNullValue(Int8PtrVecTy),Int32PtrVecTy));// bitcast <vscale x 4 x i8*> to <vscale x 4 x i32*>EXPECT_EQ(Constant::getNullValue(Int32PtrScalableVecTy),ConstantExpr::getPointerCast(Constant::getNullValue(Int8PtrScalableVecTy),Int32PtrScalableVecTy));Type *Int32Ptr1Ty = Type::getInt32PtrTy(C, 1);ConstantInt *K = ConstantInt::get(Type::getInt64Ty(C), 1234);// Make sure that addrspacecast of inttoptr is not folded away.EXPECT_NE(K, ConstantExpr::getAddrSpaceCast(ConstantExpr::getIntToPtr(K, Int32PtrTy), Int32Ptr1Ty));EXPECT_NE(K, ConstantExpr::getAddrSpaceCast(ConstantExpr::getIntToPtr(K, Int32Ptr1Ty), Int32PtrTy));Constant *NullInt32Ptr0 = Constant::getNullValue(Int32PtrTy);Constant *NullInt32Ptr1 = Constant::getNullValue(Int32Ptr1Ty);// Make sure that addrspacecast of null is not folded away.EXPECT_NE(Constant::getNullValue(Int32PtrTy),ConstantExpr::getAddrSpaceCast(NullInt32Ptr0, Int32Ptr1Ty));EXPECT_NE(Constant::getNullValue(Int32Ptr1Ty),ConstantExpr::getAddrSpaceCast(NullInt32Ptr1, Int32PtrTy));}#define CHECK(x, y) \{ \std::string __s; \raw_string_ostream __o(__s); \Instruction *__I = cast<ConstantExpr>(x)->getAsInstruction(); \__I->print(__o); \__I->deleteValue(); \__o.flush(); \EXPECT_EQ(std::string(" <badref> = " y), __s); \}TEST(ConstantsTest, AsInstructionsTest) {LLVMContext Context;std::unique_ptr<Module> M(new Module("MyModule", Context));Type *Int64Ty = Type::getInt64Ty(Context);Type *Int32Ty = Type::getInt32Ty(Context);Type *Int16Ty = Type::getInt16Ty(Context);Type *Int1Ty = Type::getInt1Ty(Context);Type *FloatTy = Type::getFloatTy(Context);Type *DoubleTy = Type::getDoubleTy(Context);Constant *Global =M->getOrInsertGlobal("dummy", PointerType::getUnqual(Int32Ty));Constant *Global2 =M->getOrInsertGlobal("dummy2", PointerType::getUnqual(Int32Ty));Constant *P0 = ConstantExpr::getPtrToInt(Global, Int32Ty);Constant *P1 = ConstantExpr::getUIToFP(P0, FloatTy);Constant *P2 = ConstantExpr::getUIToFP(P0, DoubleTy);Constant *P3 = ConstantExpr::getTrunc(P0, Int1Ty);Constant *P4 = ConstantExpr::getPtrToInt(Global2, Int32Ty);Constant *P5 = ConstantExpr::getUIToFP(P4, FloatTy);Constant *P6 = ConstantExpr::getBitCast(P4, FixedVectorType::get(Int16Ty, 2));Constant *One = ConstantInt::get(Int32Ty, 1);Constant *Two = ConstantInt::get(Int64Ty, 2);Constant *Big = ConstantInt::get(Context, APInt{256, uint64_t(-1), true});Constant *Elt = ConstantInt::get(Int16Ty, 2015);Constant *Poison16 = PoisonValue::get(Int16Ty);Constant *Undef64 = UndefValue::get(Int64Ty);Constant *PoisonV16 = PoisonValue::get(P6->getType());#define P0STR "ptrtoint (ptr @dummy to i32)"#define P1STR "uitofp (i32 ptrtoint (ptr @dummy to i32) to float)"#define P2STR "uitofp (i32 ptrtoint (ptr @dummy to i32) to double)"#define P3STR "ptrtoint (ptr @dummy to i1)"#define P4STR "ptrtoint (ptr @dummy2 to i32)"#define P5STR "uitofp (i32 ptrtoint (ptr @dummy2 to i32) to float)"#define P6STR "bitcast (i32 ptrtoint (ptr @dummy2 to i32) to <2 x i16>)"CHECK(ConstantExpr::getNeg(P0), "sub i32 0, " P0STR);CHECK(ConstantExpr::getFNeg(P1), "fneg float " P1STR);CHECK(ConstantExpr::getNot(P0), "xor i32 " P0STR ", -1");CHECK(ConstantExpr::getAdd(P0, P0), "add i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getAdd(P0, P0, false, true),"add nsw i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getAdd(P0, P0, true, true),"add nuw nsw i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getSub(P0, P0), "sub i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getMul(P0, P0), "mul i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getAnd(P0, P0), "and i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getOr(P0, P0), "or i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getXor(P0, P0), "xor i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getShl(P0, P0), "shl i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getShl(P0, P0, true), "shl nuw i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getShl(P0, P0, false, true),"shl nsw i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getLShr(P0, P0, false), "lshr i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getLShr(P0, P0, true),"lshr exact i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getAShr(P0, P0, false), "ashr i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getAShr(P0, P0, true),"ashr exact i32 " P0STR ", " P0STR);CHECK(ConstantExpr::getSExt(P0, Int64Ty), "sext i32 " P0STR " to i64");CHECK(ConstantExpr::getZExt(P0, Int64Ty), "zext i32 " P0STR " to i64");CHECK(ConstantExpr::getFPTrunc(P2, FloatTy),"fptrunc double " P2STR " to float");CHECK(ConstantExpr::getFPExtend(P1, DoubleTy),"fpext float " P1STR " to double");CHECK(ConstantExpr::getSelect(P3, P0, P4),"select i1 " P3STR ", i32 " P0STR ", i32 " P4STR);CHECK(ConstantExpr::getICmp(CmpInst::ICMP_EQ, P0, P4),"icmp eq i32 " P0STR ", " P4STR);CHECK(ConstantExpr::getFCmp(CmpInst::FCMP_ULT, P1, P5),"fcmp ult float " P1STR ", " P5STR);std::vector<Constant *> V;V.push_back(One);// FIXME: getGetElementPtr() actually creates an inbounds ConstantGEP,// not a normal one!// CHECK(ConstantExpr::getGetElementPtr(Global, V, false),// "getelementptr i32*, i32** @dummy, i32 1");CHECK(ConstantExpr::getInBoundsGetElementPtr(PointerType::getUnqual(Int32Ty),Global, V),"getelementptr inbounds ptr, ptr @dummy, i32 1");CHECK(ConstantExpr::getExtractElement(P6, One),"extractelement <2 x i16> " P6STR ", i32 1");EXPECT_EQ(Poison16, ConstantExpr::getExtractElement(P6, Two));EXPECT_EQ(Poison16, ConstantExpr::getExtractElement(P6, Big));EXPECT_EQ(Poison16, ConstantExpr::getExtractElement(P6, Undef64));EXPECT_EQ(Elt, ConstantExpr::getExtractElement(ConstantExpr::getInsertElement(P6, Elt, One), One));EXPECT_EQ(PoisonV16, ConstantExpr::getInsertElement(P6, Elt, Two));EXPECT_EQ(PoisonV16, ConstantExpr::getInsertElement(P6, Elt, Big));EXPECT_EQ(PoisonV16, ConstantExpr::getInsertElement(P6, Elt, Undef64));}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST(ConstantsTest, ReplaceWithConstantTest) {LLVMContext Context;std::unique_ptr<Module> M(new Module("MyModule", Context));Type *Int32Ty = Type::getInt32Ty(Context);Constant *One = ConstantInt::get(Int32Ty, 1);Constant *Global =M->getOrInsertGlobal("dummy", PointerType::getUnqual(Int32Ty));Constant *GEP = ConstantExpr::getGetElementPtr(PointerType::getUnqual(Int32Ty), Global, One);EXPECT_DEATH(Global->replaceAllUsesWith(GEP),"this->replaceAllUsesWith\\(expr\\(this\\)\\) is NOT valid!");}#endif#endif#undef CHECKTEST(ConstantsTest, ConstantArrayReplaceWithConstant) {LLVMContext Context;std::unique_ptr<Module> M(new Module("MyModule", Context));Type *IntTy = Type::getInt8Ty(Context);ArrayType *ArrayTy = ArrayType::get(IntTy, 2);Constant *A01Vals[2] = {ConstantInt::get(IntTy, 0),ConstantInt::get(IntTy, 1)};Constant *A01 = ConstantArray::get(ArrayTy, A01Vals);Constant *Global = new GlobalVariable(*M, IntTy, false,GlobalValue::ExternalLinkage, nullptr);Constant *GlobalInt = ConstantExpr::getPtrToInt(Global, IntTy);Constant *A0GVals[2] = {ConstantInt::get(IntTy, 0), GlobalInt};Constant *A0G = ConstantArray::get(ArrayTy, A0GVals);ASSERT_NE(A01, A0G);GlobalVariable *RefArray =new GlobalVariable(*M, ArrayTy, false, GlobalValue::ExternalLinkage, A0G);ASSERT_EQ(A0G, RefArray->getInitializer());GlobalInt->replaceAllUsesWith(ConstantInt::get(IntTy, 1));ASSERT_EQ(A01, RefArray->getInitializer());}TEST(ConstantsTest, ConstantExprReplaceWithConstant) {LLVMContext Context;std::unique_ptr<Module> M(new Module("MyModule", Context));Type *IntTy = Type::getInt8Ty(Context);Constant *G1 = new GlobalVariable(*M, IntTy, false,GlobalValue::ExternalLinkage, nullptr);Constant *G2 = new GlobalVariable(*M, IntTy, false,GlobalValue::ExternalLinkage, nullptr);ASSERT_NE(G1, G2);Constant *Int1 = ConstantExpr::getPtrToInt(G1, IntTy);Constant *Int2 = ConstantExpr::getPtrToInt(G2, IntTy);ASSERT_NE(Int1, Int2);GlobalVariable *Ref =new GlobalVariable(*M, IntTy, false, GlobalValue::ExternalLinkage, Int1);ASSERT_EQ(Int1, Ref->getInitializer());G1->replaceAllUsesWith(G2);ASSERT_EQ(Int2, Ref->getInitializer());}TEST(ConstantsTest, GEPReplaceWithConstant) {LLVMContext Context;std::unique_ptr<Module> M(new Module("MyModule", Context));Type *IntTy = Type::getInt32Ty(Context);Type *PtrTy = PointerType::get(IntTy, 0);auto *C1 = ConstantInt::get(IntTy, 1);auto *Placeholder = new GlobalVariable(*M, IntTy, false, GlobalValue::ExternalWeakLinkage, nullptr);auto *GEP = ConstantExpr::getGetElementPtr(IntTy, Placeholder, C1);ASSERT_EQ(GEP->getOperand(0), Placeholder);auto *Ref =new GlobalVariable(*M, PtrTy, false, GlobalValue::ExternalLinkage, GEP);ASSERT_EQ(GEP, Ref->getInitializer());auto *Global = new GlobalVariable(*M, IntTy, false,GlobalValue::ExternalLinkage, nullptr);auto *Alias = GlobalAlias::create(IntTy, 0, GlobalValue::ExternalLinkage,"alias", Global, M.get());Placeholder->replaceAllUsesWith(Alias);ASSERT_EQ(GEP, Ref->getInitializer());ASSERT_EQ(GEP->getOperand(0), Alias);}TEST(ConstantsTest, AliasCAPI) {LLVMContext Context;SMDiagnostic Error;std::unique_ptr<Module> M =parseAssemblyString("@g = global i32 42", Error, Context);GlobalVariable *G = M->getGlobalVariable("g");Type *I16Ty = Type::getInt16Ty(Context);Type *I16PTy = PointerType::get(I16Ty, 0);Constant *Aliasee = ConstantExpr::getBitCast(G, I16PTy);LLVMValueRef AliasRef =LLVMAddAlias2(wrap(M.get()), wrap(I16Ty), 0, wrap(Aliasee), "a");ASSERT_EQ(unwrap<GlobalAlias>(AliasRef)->getAliasee(), Aliasee);}static std::string getNameOfType(Type *T) {std::string S;raw_string_ostream RSOS(S);T->print(RSOS);return S;}TEST(ConstantsTest, BuildConstantDataArrays) {LLVMContext Context;for (Type *T : {Type::getInt8Ty(Context), Type::getInt16Ty(Context),Type::getInt32Ty(Context), Type::getInt64Ty(Context)}) {ArrayType *ArrayTy = ArrayType::get(T, 2);Constant *Vals[] = {ConstantInt::get(T, 0), ConstantInt::get(T, 1)};Constant *CA = ConstantArray::get(ArrayTy, Vals);ASSERT_TRUE(isa<ConstantDataArray>(CA)) << " T = " << getNameOfType(T);auto *CDA = cast<ConstantDataArray>(CA);Constant *CA2 = ConstantDataArray::getRaw(CDA->getRawDataValues(), CDA->getNumElements(), CDA->getElementType());ASSERT_TRUE(CA == CA2) << " T = " << getNameOfType(T);}for (Type *T : {Type::getHalfTy(Context), Type::getBFloatTy(Context),Type::getFloatTy(Context), Type::getDoubleTy(Context)}) {ArrayType *ArrayTy = ArrayType::get(T, 2);Constant *Vals[] = {ConstantFP::get(T, 0), ConstantFP::get(T, 1)};Constant *CA = ConstantArray::get(ArrayTy, Vals);ASSERT_TRUE(isa<ConstantDataArray>(CA)) << " T = " << getNameOfType(T);auto *CDA = cast<ConstantDataArray>(CA);Constant *CA2 = ConstantDataArray::getRaw(CDA->getRawDataValues(), CDA->getNumElements(), CDA->getElementType());ASSERT_TRUE(CA == CA2) << " T = " << getNameOfType(T);}}TEST(ConstantsTest, BuildConstantDataVectors) {LLVMContext Context;for (Type *T : {Type::getInt8Ty(Context), Type::getInt16Ty(Context),Type::getInt32Ty(Context), Type::getInt64Ty(Context)}) {Constant *Vals[] = {ConstantInt::get(T, 0), ConstantInt::get(T, 1)};Constant *CV = ConstantVector::get(Vals);ASSERT_TRUE(isa<ConstantDataVector>(CV)) << " T = " << getNameOfType(T);auto *CDV = cast<ConstantDataVector>(CV);Constant *CV2 = ConstantDataVector::getRaw(CDV->getRawDataValues(), CDV->getNumElements(), CDV->getElementType());ASSERT_TRUE(CV == CV2) << " T = " << getNameOfType(T);}for (Type *T : {Type::getHalfTy(Context), Type::getBFloatTy(Context),Type::getFloatTy(Context), Type::getDoubleTy(Context)}) {Constant *Vals[] = {ConstantFP::get(T, 0), ConstantFP::get(T, 1)};Constant *CV = ConstantVector::get(Vals);ASSERT_TRUE(isa<ConstantDataVector>(CV)) << " T = " << getNameOfType(T);auto *CDV = cast<ConstantDataVector>(CV);Constant *CV2 = ConstantDataVector::getRaw(CDV->getRawDataValues(), CDV->getNumElements(), CDV->getElementType());ASSERT_TRUE(CV == CV2) << " T = " << getNameOfType(T);}}void bitcastToGEPHelper(bool useOpaquePointers) {LLVMContext Context;Context.setOpaquePointers(useOpaquePointers);std::unique_ptr<Module> M(new Module("MyModule", Context));auto *i32 = Type::getInt32Ty(Context);auto *U = StructType::create(Context, "Unsized");Type *EltTys[] = {i32, U};auto *S = StructType::create(EltTys);auto *G =new GlobalVariable(*M, S, false, GlobalValue::ExternalLinkage, nullptr);auto *PtrTy = PointerType::get(i32, 0);auto *C = ConstantExpr::getBitCast(G, PtrTy);if (Context.supportsTypedPointers()) {EXPECT_EQ(cast<ConstantExpr>(C)->getOpcode(), Instruction::BitCast);} else {/* With opaque pointers, no cast is necessary. */EXPECT_EQ(C, G);}}TEST(ConstantsTest, BitcastToGEP) {bitcastToGEPHelper(true);bitcastToGEPHelper(false);}bool foldFuncPtrAndConstToNull(LLVMContext &Context, Module *TheModule,uint64_t AndValue,MaybeAlign FunctionAlign = llvm::None) {Type *VoidType(Type::getVoidTy(Context));FunctionType *FuncType(FunctionType::get(VoidType, false));Function *Func(Function::Create(FuncType, GlobalValue::ExternalLinkage, "", TheModule));if (FunctionAlign)Func->setAlignment(*FunctionAlign);IntegerType *ConstantIntType(Type::getInt32Ty(Context));ConstantInt *TheConstant(ConstantInt::get(ConstantIntType, AndValue));Constant *TheConstantExpr(ConstantExpr::getPtrToInt(Func, ConstantIntType));bool Result =ConstantExpr::get(Instruction::And, TheConstantExpr, TheConstant)->isNullValue();if (!TheModule) {// If the Module exists then it will delete the Function.delete Func;}return Result;}TEST(ConstantsTest, FoldFunctionPtrAlignUnknownAnd2) {LLVMContext Context;Module TheModule("TestModule", Context);// When the DataLayout doesn't specify a function pointer alignment we// assume in this case that it is 4 byte aligned. This is a bug but we can't// fix it directly because it causes a code size regression on X86.// FIXME: This test should be changed once existing targets have// appropriate defaults. See associated FIXME in ConstantFoldBinaryInstructionASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, 2));}TEST(ConstantsTest, DontFoldFunctionPtrAlignUnknownAnd4) {LLVMContext Context;Module TheModule("TestModule", Context);ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 4));}TEST(ConstantsTest, FoldFunctionPtrAlign4) {LLVMContext Context;Module TheModule("TestModule", Context);const char *AlignmentStrings[] = {"Fi32", "Fn32"};for (unsigned AndValue = 1; AndValue <= 2; ++AndValue) {for (const char *AlignmentString : AlignmentStrings) {TheModule.setDataLayout(AlignmentString);ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, AndValue));}}}TEST(ConstantsTest, DontFoldFunctionPtrAlign1) {LLVMContext Context;Module TheModule("TestModule", Context);const char *AlignmentStrings[] = {"Fi8", "Fn8"};for (const char *AlignmentString : AlignmentStrings) {TheModule.setDataLayout(AlignmentString);ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 2));}}TEST(ConstantsTest, FoldFunctionAlign4PtrAlignMultiple) {LLVMContext Context;Module TheModule("TestModule", Context);TheModule.setDataLayout("Fn8");ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, 2, Align(4)));}TEST(ConstantsTest, DontFoldFunctionAlign4PtrAlignIndependent) {LLVMContext Context;Module TheModule("TestModule", Context);TheModule.setDataLayout("Fi8");ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 2, Align(4)));}TEST(ConstantsTest, DontFoldFunctionPtrIfNoModule) {LLVMContext Context;// Even though the function is explicitly 4 byte aligned, in the absence of a// DataLayout we can't assume that the function pointer is aligned.ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, nullptr, 2, Align(4)));}TEST(ConstantsTest, FoldGlobalVariablePtr) {LLVMContext Context;IntegerType *IntType(Type::getInt32Ty(Context));std::unique_ptr<GlobalVariable> Global(new GlobalVariable(IntType, true, GlobalValue::ExternalLinkage));Global->setAlignment(Align(4));ConstantInt *TheConstant(ConstantInt::get(IntType, 2));Constant *TheConstantExpr(ConstantExpr::getPtrToInt(Global.get(), IntType));ASSERT_TRUE(ConstantExpr::get(Instruction::And, TheConstantExpr, TheConstant)->isNullValue());}// Check that containsUndefOrPoisonElement and containsPoisonElement is working// greatTEST(ConstantsTest, containsUndefElemTest) {LLVMContext Context;Type *Int32Ty = Type::getInt32Ty(Context);Constant *CU = UndefValue::get(Int32Ty);Constant *CP = PoisonValue::get(Int32Ty);Constant *C1 = ConstantInt::get(Int32Ty, 1);Constant *C2 = ConstantInt::get(Int32Ty, 2);{Constant *V1 = ConstantVector::get({C1, C2});EXPECT_FALSE(V1->containsUndefOrPoisonElement());EXPECT_FALSE(V1->containsPoisonElement());}{Constant *V2 = ConstantVector::get({C1, CU});EXPECT_TRUE(V2->containsUndefOrPoisonElement());EXPECT_FALSE(V2->containsPoisonElement());}{Constant *V3 = ConstantVector::get({C1, CP});EXPECT_TRUE(V3->containsUndefOrPoisonElement());EXPECT_TRUE(V3->containsPoisonElement());}{Constant *V4 = ConstantVector::get({CU, CP});EXPECT_TRUE(V4->containsUndefOrPoisonElement());EXPECT_TRUE(V4->containsPoisonElement());}}// Check that undefined elements in vector constants are matched// correctly for both integer and floating-point types. Just don't// crash on vectors of pointers (could be handled?).TEST(ConstantsTest, isElementWiseEqual) {LLVMContext Context;Type *Int32Ty = Type::getInt32Ty(Context);Constant *CU = UndefValue::get(Int32Ty);Constant *C1 = ConstantInt::get(Int32Ty, 1);Constant *C2 = ConstantInt::get(Int32Ty, 2);Constant *C1211 = ConstantVector::get({C1, C2, C1, C1});Constant *C12U1 = ConstantVector::get({C1, C2, CU, C1});Constant *C12U2 = ConstantVector::get({C1, C2, CU, C2});Constant *C12U21 = ConstantVector::get({C1, C2, CU, C2, C1});EXPECT_TRUE(C1211->isElementWiseEqual(C12U1));EXPECT_TRUE(C12U1->isElementWiseEqual(C1211));EXPECT_FALSE(C12U2->isElementWiseEqual(C12U1));EXPECT_FALSE(C12U1->isElementWiseEqual(C12U2));EXPECT_FALSE(C12U21->isElementWiseEqual(C12U2));Type *FltTy = Type::getFloatTy(Context);Constant *CFU = UndefValue::get(FltTy);Constant *CF1 = ConstantFP::get(FltTy, 1.0);Constant *CF2 = ConstantFP::get(FltTy, 2.0);Constant *CF1211 = ConstantVector::get({CF1, CF2, CF1, CF1});Constant *CF12U1 = ConstantVector::get({CF1, CF2, CFU, CF1});Constant *CF12U2 = ConstantVector::get({CF1, CF2, CFU, CF2});Constant *CFUU1U = ConstantVector::get({CFU, CFU, CF1, CFU});EXPECT_TRUE(CF1211->isElementWiseEqual(CF12U1));EXPECT_TRUE(CF12U1->isElementWiseEqual(CF1211));EXPECT_TRUE(CFUU1U->isElementWiseEqual(CF12U1));EXPECT_FALSE(CF12U2->isElementWiseEqual(CF12U1));EXPECT_FALSE(CF12U1->isElementWiseEqual(CF12U2));PointerType *PtrTy = Type::getInt8PtrTy(Context);Constant *CPU = UndefValue::get(PtrTy);Constant *CP0 = ConstantPointerNull::get(PtrTy);Constant *CP0000 = ConstantVector::get({CP0, CP0, CP0, CP0});Constant *CP00U0 = ConstantVector::get({CP0, CP0, CPU, CP0});Constant *CP00U = ConstantVector::get({CP0, CP0, CPU});EXPECT_FALSE(CP0000->isElementWiseEqual(CP00U0));EXPECT_FALSE(CP00U0->isElementWiseEqual(CP0000));EXPECT_FALSE(CP0000->isElementWiseEqual(CP00U));EXPECT_FALSE(CP00U->isElementWiseEqual(CP00U0));}// Check that vector/aggregate constants correctly store undef and poison// elements.TEST(ConstantsTest, CheckElementWiseUndefPoison) {LLVMContext Context;Type *Int32Ty = Type::getInt32Ty(Context);StructType *STy = StructType::get(Int32Ty, Int32Ty);ArrayType *ATy = ArrayType::get(Int32Ty, 2);Constant *CU = UndefValue::get(Int32Ty);Constant *CP = PoisonValue::get(Int32Ty);{Constant *CUU = ConstantVector::get({CU, CU});Constant *CPP = ConstantVector::get({CP, CP});Constant *CUP = ConstantVector::get({CU, CP});Constant *CPU = ConstantVector::get({CP, CU});EXPECT_EQ(CUU, UndefValue::get(CUU->getType()));EXPECT_EQ(CPP, PoisonValue::get(CPP->getType()));EXPECT_NE(CUP, UndefValue::get(CUP->getType()));EXPECT_NE(CPU, UndefValue::get(CPU->getType()));}{Constant *CUU = ConstantStruct::get(STy, {CU, CU});Constant *CPP = ConstantStruct::get(STy, {CP, CP});Constant *CUP = ConstantStruct::get(STy, {CU, CP});Constant *CPU = ConstantStruct::get(STy, {CP, CU});EXPECT_EQ(CUU, UndefValue::get(CUU->getType()));EXPECT_EQ(CPP, PoisonValue::get(CPP->getType()));EXPECT_NE(CUP, UndefValue::get(CUP->getType()));EXPECT_NE(CPU, UndefValue::get(CPU->getType()));}{Constant *CUU = ConstantArray::get(ATy, {CU, CU});Constant *CPP = ConstantArray::get(ATy, {CP, CP});Constant *CUP = ConstantArray::get(ATy, {CU, CP});Constant *CPU = ConstantArray::get(ATy, {CP, CU});EXPECT_EQ(CUU, UndefValue::get(CUU->getType()));EXPECT_EQ(CPP, PoisonValue::get(CPP->getType()));EXPECT_NE(CUP, UndefValue::get(CUP->getType()));EXPECT_NE(CPU, UndefValue::get(CPU->getType()));}}TEST(ConstantsTest, GetSplatValueRoundTrip) {LLVMContext Context;Type *FloatTy = Type::getFloatTy(Context);Type *Int32Ty = Type::getInt32Ty(Context);Type *Int8Ty = Type::getInt8Ty(Context);for (unsigned Min : {1, 2, 8}) {auto ScalableEC = ElementCount::getScalable(Min);auto FixedEC = ElementCount::getFixed(Min);for (auto EC : {ScalableEC, FixedEC}) {for (auto *Ty : {FloatTy, Int32Ty, Int8Ty}) {Constant *Zero = Constant::getNullValue(Ty);Constant *One = Constant::getAllOnesValue(Ty);for (auto *C : {Zero, One}) {Constant *Splat = ConstantVector::getSplat(EC, C);ASSERT_NE(nullptr, Splat);Constant *SplatVal = Splat->getSplatValue();EXPECT_NE(nullptr, SplatVal);EXPECT_EQ(SplatVal, C);}}}}}TEST(ConstantsTest, ComdatUserTracking) {LLVMContext Context;Module M("MyModule", Context);Comdat *C = M.getOrInsertComdat("comdat");const SmallPtrSetImpl<GlobalObject *> &Users = C->getUsers();EXPECT_TRUE(Users.size() == 0);Type *Ty = Type::getInt8Ty(Context);GlobalVariable *GV1 = cast<GlobalVariable>(M.getOrInsertGlobal("gv1", Ty));GV1->setComdat(C);EXPECT_TRUE(Users.size() == 1);EXPECT_TRUE(Users.contains(GV1));GlobalVariable *GV2 = cast<GlobalVariable>(M.getOrInsertGlobal("gv2", Ty));GV2->setComdat(C);EXPECT_TRUE(Users.size() == 2);EXPECT_TRUE(Users.contains(GV2));GV1->eraseFromParent();EXPECT_TRUE(Users.size() == 1);EXPECT_TRUE(Users.contains(GV2));GV2->eraseFromParent();EXPECT_TRUE(Users.size() == 0);}} // end anonymous namespace} // end namespace llvm
//===- ConstantRangeTest.cpp - ConstantRange tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/ConstantRange.h"#include "llvm/ADT/BitVector.h"#include "llvm/ADT/Sequence.h"#include "llvm/ADT/SmallBitVector.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/Operator.h"#include "llvm/Support/KnownBits.h"#include "gtest/gtest.h"using namespace llvm;namespace {class ConstantRangeTest : public ::testing::Test {protected:static ConstantRange Full;static ConstantRange Empty;static ConstantRange One;static ConstantRange Some;static ConstantRange Wrap;};template<typename Fn>static void EnumerateAPInts(unsigned Bits, Fn TestFn) {APInt N(Bits, 0);do {TestFn(N);} while (++N != 0);}template<typename Fn>static void EnumerateConstantRanges(unsigned Bits, Fn TestFn) {unsigned Max = 1 << Bits;for (unsigned Lo = 0; Lo < Max; Lo++) {for (unsigned Hi = 0; Hi < Max; Hi++) {// Enforce ConstantRange invariant.if (Lo == Hi && Lo != 0 && Lo != Max - 1)continue;ConstantRange CR(APInt(Bits, Lo), APInt(Bits, Hi));TestFn(CR);}}}template<typename Fn>static void EnumerateTwoConstantRanges(unsigned Bits, Fn TestFn) {EnumerateConstantRanges(Bits, [&](const ConstantRange &CR1) {EnumerateConstantRanges(Bits, [&](const ConstantRange &CR2) {TestFn(CR1, CR2);});});}template<typename Fn>static void ForeachNumInConstantRange(const ConstantRange &CR, Fn TestFn) {if (!CR.isEmptySet()) {APInt N = CR.getLower();do TestFn(N);while (++N != CR.getUpper());}}using PreferFn = llvm::function_ref<bool(const ConstantRange &,const ConstantRange &)>;bool PreferSmallest(const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.isSizeStrictlySmallerThan(CR2);}bool PreferSmallestUnsigned(const ConstantRange &CR1,const ConstantRange &CR2) {if (CR1.isWrappedSet() != CR2.isWrappedSet())return CR1.isWrappedSet() < CR2.isWrappedSet();return PreferSmallest(CR1, CR2);}bool PreferSmallestSigned(const ConstantRange &CR1, const ConstantRange &CR2) {if (CR1.isSignWrappedSet() != CR2.isSignWrappedSet())return CR1.isSignWrappedSet() < CR2.isSignWrappedSet();return PreferSmallest(CR1, CR2);}bool PreferSmallestNonFullUnsigned(const ConstantRange &CR1,const ConstantRange &CR2) {if (CR1.isFullSet() != CR2.isFullSet())return CR1.isFullSet() < CR2.isFullSet();return PreferSmallestUnsigned(CR1, CR2);}bool PreferSmallestNonFullSigned(const ConstantRange &CR1,const ConstantRange &CR2) {if (CR1.isFullSet() != CR2.isFullSet())return CR1.isFullSet() < CR2.isFullSet();return PreferSmallestSigned(CR1, CR2);}testing::AssertionResult rangeContains(const ConstantRange &CR, const APInt &N,ArrayRef<ConstantRange> Inputs) {if (CR.contains(N))return testing::AssertionSuccess();testing::AssertionResult Result = testing::AssertionFailure();Result << CR << " does not contain " << N << " for inputs: ";for (const ConstantRange &Input : Inputs)Result << Input << ", ";return Result;}// Check whether constant range CR is an optimal approximation of the set// Elems under the given PreferenceFn. The preference function should return// true if the first range argument is strictly preferred to the second one.static void TestRange(const ConstantRange &CR, const SmallBitVector &Elems,PreferFn PreferenceFn, ArrayRef<ConstantRange> Inputs,bool CheckOptimality = true) {unsigned BitWidth = CR.getBitWidth();// Check conservative correctness.for (unsigned Elem : Elems.set_bits()) {EXPECT_TRUE(rangeContains(CR, APInt(BitWidth, Elem), Inputs));}if (!CheckOptimality)return;// Make sure we have at least one element for the code below.if (Elems.none()) {EXPECT_TRUE(CR.isEmptySet());return;}auto NotPreferred = [&](const ConstantRange &PossibleCR) {if (!PreferenceFn(PossibleCR, CR))return testing::AssertionSuccess();testing::AssertionResult Result = testing::AssertionFailure();Result << "Inputs = ";for (const ConstantRange &Input : Inputs)Result << Input << ", ";Result << "CR = " << CR << ", BetterCR = " << PossibleCR;return Result;};// Look at all pairs of adjacent elements and the slack-free ranges// [Elem, PrevElem] they imply. Check that none of the ranges are strictly// preferred over the computed range (they may have equal preference).int FirstElem = Elems.find_first();int PrevElem = FirstElem, Elem;do {Elem = Elems.find_next(PrevElem);if (Elem < 0)Elem = FirstElem; // Wrap around to first element.ConstantRange PossibleCR =ConstantRange::getNonEmpty(APInt(BitWidth, Elem),APInt(BitWidth, PrevElem) + 1);// We get a full range any time PrevElem and Elem are adjacent. Avoid// repeated checks by skipping here, and explicitly checking below instead.if (!PossibleCR.isFullSet()) {EXPECT_TRUE(NotPreferred(PossibleCR));}PrevElem = Elem;} while (Elem != FirstElem);EXPECT_TRUE(NotPreferred(ConstantRange::getFull(BitWidth)));}using UnaryRangeFn = llvm::function_ref<ConstantRange(const ConstantRange &)>;using UnaryIntFn = llvm::function_ref<Optional<APInt>(const APInt &)>;static void TestUnaryOpExhaustive(UnaryRangeFn RangeFn, UnaryIntFn IntFn,PreferFn PreferenceFn = PreferSmallest) {unsigned Bits = 4;EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {SmallBitVector Elems(1 << Bits);ForeachNumInConstantRange(CR, [&](const APInt &N) {if (Optional<APInt> ResultN = IntFn(N))Elems.set(ResultN->getZExtValue());});TestRange(RangeFn(CR), Elems, PreferenceFn, {CR});});}using BinaryRangeFn = llvm::function_ref<ConstantRange(const ConstantRange &,const ConstantRange &)>;using BinaryIntFn = llvm::function_ref<Optional<APInt>(const APInt &,const APInt &)>;using BinaryCheckFn = llvm::function_ref<bool(const ConstantRange &,const ConstantRange &)>;static bool CheckAll(const ConstantRange &, const ConstantRange &) {return true;}static bool CheckSingleElementsOnly(const ConstantRange &CR1,const ConstantRange &CR2) {return CR1.isSingleElement() && CR2.isSingleElement();}static bool CheckNonWrappedOnly(const ConstantRange &CR1,const ConstantRange &CR2) {return !CR1.isWrappedSet() && !CR2.isWrappedSet();}static bool CheckNonSignWrappedOnly(const ConstantRange &CR1,const ConstantRange &CR2) {return !CR1.isSignWrappedSet() && !CR2.isSignWrappedSet();}static bool CheckNonWrappedOrSignWrappedOnly(const ConstantRange &CR1,const ConstantRange &CR2) {return !CR1.isWrappedSet() && !CR1.isSignWrappedSet() &&!CR2.isWrappedSet() && !CR2.isSignWrappedSet();}// CheckFn determines whether optimality is checked for a given range pair.// Correctness is always checked.static void TestBinaryOpExhaustive(BinaryRangeFn RangeFn, BinaryIntFn IntFn,PreferFn PreferenceFn = PreferSmallest,BinaryCheckFn CheckFn = CheckAll) {unsigned Bits = 4;EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {SmallBitVector Elems(1 << Bits);ForeachNumInConstantRange(CR1, [&](const APInt &N1) {ForeachNumInConstantRange(CR2, [&](const APInt &N2) {if (Optional<APInt> ResultN = IntFn(N1, N2))Elems.set(ResultN->getZExtValue());});});TestRange(RangeFn(CR1, CR2), Elems, PreferenceFn, {CR1, CR2},CheckFn(CR1, CR2));});}ConstantRange ConstantRangeTest::Full(16, true);ConstantRange ConstantRangeTest::Empty(16, false);ConstantRange ConstantRangeTest::One(APInt(16, 0xa));ConstantRange ConstantRangeTest::Some(APInt(16, 0xa), APInt(16, 0xaaa));ConstantRange ConstantRangeTest::Wrap(APInt(16, 0xaaa), APInt(16, 0xa));TEST_F(ConstantRangeTest, Basics) {EXPECT_TRUE(Full.isFullSet());EXPECT_FALSE(Full.isEmptySet());EXPECT_TRUE(Full.inverse().isEmptySet());EXPECT_FALSE(Full.isWrappedSet());EXPECT_TRUE(Full.contains(APInt(16, 0x0)));EXPECT_TRUE(Full.contains(APInt(16, 0x9)));EXPECT_TRUE(Full.contains(APInt(16, 0xa)));EXPECT_TRUE(Full.contains(APInt(16, 0xaa9)));EXPECT_TRUE(Full.contains(APInt(16, 0xaaa)));EXPECT_FALSE(Empty.isFullSet());EXPECT_TRUE(Empty.isEmptySet());EXPECT_TRUE(Empty.inverse().isFullSet());EXPECT_FALSE(Empty.isWrappedSet());EXPECT_FALSE(Empty.contains(APInt(16, 0x0)));EXPECT_FALSE(Empty.contains(APInt(16, 0x9)));EXPECT_FALSE(Empty.contains(APInt(16, 0xa)));EXPECT_FALSE(Empty.contains(APInt(16, 0xaa9)));EXPECT_FALSE(Empty.contains(APInt(16, 0xaaa)));EXPECT_FALSE(One.isFullSet());EXPECT_FALSE(One.isEmptySet());EXPECT_FALSE(One.isWrappedSet());EXPECT_FALSE(One.contains(APInt(16, 0x0)));EXPECT_FALSE(One.contains(APInt(16, 0x9)));EXPECT_TRUE(One.contains(APInt(16, 0xa)));EXPECT_FALSE(One.contains(APInt(16, 0xaa9)));EXPECT_FALSE(One.contains(APInt(16, 0xaaa)));EXPECT_FALSE(One.inverse().contains(APInt(16, 0xa)));EXPECT_FALSE(Some.isFullSet());EXPECT_FALSE(Some.isEmptySet());EXPECT_FALSE(Some.isWrappedSet());EXPECT_FALSE(Some.contains(APInt(16, 0x0)));EXPECT_FALSE(Some.contains(APInt(16, 0x9)));EXPECT_TRUE(Some.contains(APInt(16, 0xa)));EXPECT_TRUE(Some.contains(APInt(16, 0xaa9)));EXPECT_FALSE(Some.contains(APInt(16, 0xaaa)));EXPECT_FALSE(Wrap.isFullSet());EXPECT_FALSE(Wrap.isEmptySet());EXPECT_TRUE(Wrap.isWrappedSet());EXPECT_TRUE(Wrap.contains(APInt(16, 0x0)));EXPECT_TRUE(Wrap.contains(APInt(16, 0x9)));EXPECT_FALSE(Wrap.contains(APInt(16, 0xa)));EXPECT_FALSE(Wrap.contains(APInt(16, 0xaa9)));EXPECT_TRUE(Wrap.contains(APInt(16, 0xaaa)));}TEST_F(ConstantRangeTest, Equality) {EXPECT_EQ(Full, Full);EXPECT_EQ(Empty, Empty);EXPECT_EQ(One, One);EXPECT_EQ(Some, Some);EXPECT_EQ(Wrap, Wrap);EXPECT_NE(Full, Empty);EXPECT_NE(Full, One);EXPECT_NE(Full, Some);EXPECT_NE(Full, Wrap);EXPECT_NE(Empty, One);EXPECT_NE(Empty, Some);EXPECT_NE(Empty, Wrap);EXPECT_NE(One, Some);EXPECT_NE(One, Wrap);EXPECT_NE(Some, Wrap);}TEST_F(ConstantRangeTest, SingleElement) {EXPECT_EQ(Full.getSingleElement(), static_cast<APInt *>(nullptr));EXPECT_EQ(Empty.getSingleElement(), static_cast<APInt *>(nullptr));EXPECT_EQ(Full.getSingleMissingElement(), static_cast<APInt *>(nullptr));EXPECT_EQ(Empty.getSingleMissingElement(), static_cast<APInt *>(nullptr));EXPECT_EQ(*One.getSingleElement(), APInt(16, 0xa));EXPECT_EQ(Some.getSingleElement(), static_cast<APInt *>(nullptr));EXPECT_EQ(Wrap.getSingleElement(), static_cast<APInt *>(nullptr));EXPECT_EQ(One.getSingleMissingElement(), static_cast<APInt *>(nullptr));EXPECT_EQ(Some.getSingleMissingElement(), static_cast<APInt *>(nullptr));ConstantRange OneInverse = One.inverse();EXPECT_EQ(*OneInverse.getSingleMissingElement(), *One.getSingleElement());EXPECT_FALSE(Full.isSingleElement());EXPECT_FALSE(Empty.isSingleElement());EXPECT_TRUE(One.isSingleElement());EXPECT_FALSE(Some.isSingleElement());EXPECT_FALSE(Wrap.isSingleElement());}TEST_F(ConstantRangeTest, GetMinsAndMaxes) {EXPECT_EQ(Full.getUnsignedMax(), APInt(16, UINT16_MAX));EXPECT_EQ(One.getUnsignedMax(), APInt(16, 0xa));EXPECT_EQ(Some.getUnsignedMax(), APInt(16, 0xaa9));EXPECT_EQ(Wrap.getUnsignedMax(), APInt(16, UINT16_MAX));EXPECT_EQ(Full.getUnsignedMin(), APInt(16, 0));EXPECT_EQ(One.getUnsignedMin(), APInt(16, 0xa));EXPECT_EQ(Some.getUnsignedMin(), APInt(16, 0xa));EXPECT_EQ(Wrap.getUnsignedMin(), APInt(16, 0));EXPECT_EQ(Full.getSignedMax(), APInt(16, INT16_MAX));EXPECT_EQ(One.getSignedMax(), APInt(16, 0xa));EXPECT_EQ(Some.getSignedMax(), APInt(16, 0xaa9));EXPECT_EQ(Wrap.getSignedMax(), APInt(16, INT16_MAX));EXPECT_EQ(Full.getSignedMin(), APInt(16, (uint64_t)INT16_MIN));EXPECT_EQ(One.getSignedMin(), APInt(16, 0xa));EXPECT_EQ(Some.getSignedMin(), APInt(16, 0xa));EXPECT_EQ(Wrap.getSignedMin(), APInt(16, (uint64_t)INT16_MIN));// Found by KleeEXPECT_EQ(ConstantRange(APInt(4, 7), APInt(4, 0)).getSignedMax(),APInt(4, 7));}TEST_F(ConstantRangeTest, SignWrapped) {EXPECT_FALSE(Full.isSignWrappedSet());EXPECT_FALSE(Empty.isSignWrappedSet());EXPECT_FALSE(One.isSignWrappedSet());EXPECT_FALSE(Some.isSignWrappedSet());EXPECT_TRUE(Wrap.isSignWrappedSet());EXPECT_FALSE(ConstantRange(APInt(8, 127), APInt(8, 128)).isSignWrappedSet());EXPECT_TRUE(ConstantRange(APInt(8, 127), APInt(8, 129)).isSignWrappedSet());EXPECT_FALSE(ConstantRange(APInt(8, 128), APInt(8, 129)).isSignWrappedSet());EXPECT_TRUE(ConstantRange(APInt(8, 10), APInt(8, 9)).isSignWrappedSet());EXPECT_TRUE(ConstantRange(APInt(8, 10), APInt(8, 250)).isSignWrappedSet());EXPECT_FALSE(ConstantRange(APInt(8, 250), APInt(8, 10)).isSignWrappedSet());EXPECT_FALSE(ConstantRange(APInt(8, 250), APInt(8, 251)).isSignWrappedSet());}TEST_F(ConstantRangeTest, UpperWrapped) {// The behavior here is the same as for isWrappedSet() / isSignWrappedSet().EXPECT_FALSE(Full.isUpperWrapped());EXPECT_FALSE(Empty.isUpperWrapped());EXPECT_FALSE(One.isUpperWrapped());EXPECT_FALSE(Some.isUpperWrapped());EXPECT_TRUE(Wrap.isUpperWrapped());EXPECT_FALSE(Full.isUpperSignWrapped());EXPECT_FALSE(Empty.isUpperSignWrapped());EXPECT_FALSE(One.isUpperSignWrapped());EXPECT_FALSE(Some.isUpperSignWrapped());EXPECT_TRUE(Wrap.isUpperSignWrapped());// The behavior differs if Upper is the Min/SignedMin value.ConstantRange CR1(APInt(8, 42), APInt::getMinValue(8));EXPECT_FALSE(CR1.isWrappedSet());EXPECT_TRUE(CR1.isUpperWrapped());ConstantRange CR2(APInt(8, 42), APInt::getSignedMinValue(8));EXPECT_FALSE(CR2.isSignWrappedSet());EXPECT_TRUE(CR2.isUpperSignWrapped());}TEST_F(ConstantRangeTest, Trunc) {ConstantRange TFull = Full.truncate(10);ConstantRange TEmpty = Empty.truncate(10);ConstantRange TOne = One.truncate(10);ConstantRange TSome = Some.truncate(10);ConstantRange TWrap = Wrap.truncate(10);EXPECT_TRUE(TFull.isFullSet());EXPECT_TRUE(TEmpty.isEmptySet());EXPECT_EQ(TOne, ConstantRange(One.getLower().trunc(10),One.getUpper().trunc(10)));EXPECT_TRUE(TSome.isFullSet());EXPECT_TRUE(TWrap.isFullSet());// trunc([2, 5), 3->2) = [2, 1)ConstantRange TwoFive(APInt(3, 2), APInt(3, 5));EXPECT_EQ(TwoFive.truncate(2), ConstantRange(APInt(2, 2), APInt(2, 1)));// trunc([2, 6), 3->2) = fullConstantRange TwoSix(APInt(3, 2), APInt(3, 6));EXPECT_TRUE(TwoSix.truncate(2).isFullSet());// trunc([5, 7), 3->2) = [1, 3)ConstantRange FiveSeven(APInt(3, 5), APInt(3, 7));EXPECT_EQ(FiveSeven.truncate(2), ConstantRange(APInt(2, 1), APInt(2, 3)));// trunc([7, 1), 3->2) = [3, 1)ConstantRange SevenOne(APInt(3, 7), APInt(3, 1));EXPECT_EQ(SevenOne.truncate(2), ConstantRange(APInt(2, 3), APInt(2, 1)));}TEST_F(ConstantRangeTest, ZExt) {ConstantRange ZFull = Full.zeroExtend(20);ConstantRange ZEmpty = Empty.zeroExtend(20);ConstantRange ZOne = One.zeroExtend(20);ConstantRange ZSome = Some.zeroExtend(20);ConstantRange ZWrap = Wrap.zeroExtend(20);EXPECT_EQ(ZFull, ConstantRange(APInt(20, 0), APInt(20, 0x10000)));EXPECT_TRUE(ZEmpty.isEmptySet());EXPECT_EQ(ZOne, ConstantRange(One.getLower().zext(20),One.getUpper().zext(20)));EXPECT_EQ(ZSome, ConstantRange(Some.getLower().zext(20),Some.getUpper().zext(20)));EXPECT_EQ(ZWrap, ConstantRange(APInt(20, 0), APInt(20, 0x10000)));// zext([5, 0), 3->7) = [5, 8)ConstantRange FiveZero(APInt(3, 5), APInt(3, 0));EXPECT_EQ(FiveZero.zeroExtend(7), ConstantRange(APInt(7, 5), APInt(7, 8)));}TEST_F(ConstantRangeTest, SExt) {ConstantRange SFull = Full.signExtend(20);ConstantRange SEmpty = Empty.signExtend(20);ConstantRange SOne = One.signExtend(20);ConstantRange SSome = Some.signExtend(20);ConstantRange SWrap = Wrap.signExtend(20);EXPECT_EQ(SFull, ConstantRange(APInt(20, (uint64_t)INT16_MIN, true),APInt(20, INT16_MAX + 1, true)));EXPECT_TRUE(SEmpty.isEmptySet());EXPECT_EQ(SOne, ConstantRange(One.getLower().sext(20),One.getUpper().sext(20)));EXPECT_EQ(SSome, ConstantRange(Some.getLower().sext(20),Some.getUpper().sext(20)));EXPECT_EQ(SWrap, ConstantRange(APInt(20, (uint64_t)INT16_MIN, true),APInt(20, INT16_MAX + 1, true)));EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, 140)).signExtend(16),ConstantRange(APInt(16, -128), APInt(16, 128)));EXPECT_EQ(ConstantRange(APInt(16, 0x0200), APInt(16, 0x8000)).signExtend(19),ConstantRange(APInt(19, 0x0200), APInt(19, 0x8000)));}TEST_F(ConstantRangeTest, IntersectWith) {EXPECT_EQ(Empty.intersectWith(Full), Empty);EXPECT_EQ(Empty.intersectWith(Empty), Empty);EXPECT_EQ(Empty.intersectWith(One), Empty);EXPECT_EQ(Empty.intersectWith(Some), Empty);EXPECT_EQ(Empty.intersectWith(Wrap), Empty);EXPECT_EQ(Full.intersectWith(Full), Full);EXPECT_EQ(Some.intersectWith(Some), Some);EXPECT_EQ(Some.intersectWith(One), One);EXPECT_EQ(Full.intersectWith(One), One);EXPECT_EQ(Full.intersectWith(Some), Some);EXPECT_EQ(Some.intersectWith(Wrap), Empty);EXPECT_EQ(One.intersectWith(Wrap), Empty);EXPECT_EQ(One.intersectWith(Wrap), Wrap.intersectWith(One));// Klee generated testcase from PR4545.// The intersection of i16 [4, 2) and [6, 5) is disjoint, looking like// 01..4.6789ABCDEF where the dots represent values not in the intersection.ConstantRange LHS(APInt(16, 4), APInt(16, 2));ConstantRange RHS(APInt(16, 6), APInt(16, 5));EXPECT_TRUE(LHS.intersectWith(RHS) == LHS);// previous bug: intersection of [min, 3) and [2, max) should be 2LHS = ConstantRange(APInt(32, -2147483646), APInt(32, 3));RHS = ConstantRange(APInt(32, 2), APInt(32, 2147483646));EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 2)));// [2, 0) /\ [4, 3) = [2, 0)LHS = ConstantRange(APInt(32, 2), APInt(32, 0));RHS = ConstantRange(APInt(32, 4), APInt(32, 3));EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 2), APInt(32, 0)));// [2, 0) /\ [4, 2) = [4, 0)LHS = ConstantRange(APInt(32, 2), APInt(32, 0));RHS = ConstantRange(APInt(32, 4), APInt(32, 2));EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 4), APInt(32, 0)));// [4, 2) /\ [5, 1) = [5, 1)LHS = ConstantRange(APInt(32, 4), APInt(32, 2));RHS = ConstantRange(APInt(32, 5), APInt(32, 1));EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 5), APInt(32, 1)));// [2, 0) /\ [7, 4) = [7, 4)LHS = ConstantRange(APInt(32, 2), APInt(32, 0));RHS = ConstantRange(APInt(32, 7), APInt(32, 4));EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 7), APInt(32, 4)));// [4, 2) /\ [1, 0) = [1, 0)LHS = ConstantRange(APInt(32, 4), APInt(32, 2));RHS = ConstantRange(APInt(32, 1), APInt(32, 0));EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 4), APInt(32, 2)));// [15, 0) /\ [7, 6) = [15, 0)LHS = ConstantRange(APInt(32, 15), APInt(32, 0));RHS = ConstantRange(APInt(32, 7), APInt(32, 6));EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 15), APInt(32, 0)));}template<typename Fn1, typename Fn2, typename Fn3>void testBinarySetOperationExhaustive(Fn1 OpFn, Fn2 ExactOpFn, Fn3 InResultFn) {unsigned Bits = 4;EnumerateTwoConstantRanges(Bits,[=](const ConstantRange &CR1, const ConstantRange &CR2) {SmallBitVector Elems(1 << Bits);APInt Num(Bits, 0);for (unsigned I = 0, Limit = 1 << Bits; I < Limit; ++I, ++Num)if (InResultFn(CR1, CR2, Num))Elems.set(Num.getZExtValue());ConstantRange SmallestCR = OpFn(CR1, CR2, ConstantRange::Smallest);TestRange(SmallestCR, Elems, PreferSmallest, {CR1, CR2});ConstantRange UnsignedCR = OpFn(CR1, CR2, ConstantRange::Unsigned);TestRange(UnsignedCR, Elems, PreferSmallestNonFullUnsigned, {CR1, CR2});ConstantRange SignedCR = OpFn(CR1, CR2, ConstantRange::Signed);TestRange(SignedCR, Elems, PreferSmallestNonFullSigned, {CR1, CR2});Optional<ConstantRange> ExactCR = ExactOpFn(CR1, CR2);if (SmallestCR.isSizeLargerThan(Elems.count())) {EXPECT_TRUE(!ExactCR);} else {EXPECT_EQ(SmallestCR, *ExactCR);}});}TEST_F(ConstantRangeTest, IntersectWithExhaustive) {testBinarySetOperationExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2,ConstantRange::PreferredRangeType Type) {return CR1.intersectWith(CR2, Type);},[](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.exactIntersectWith(CR2);},[](const ConstantRange &CR1, const ConstantRange &CR2, const APInt &N) {return CR1.contains(N) && CR2.contains(N);});}TEST_F(ConstantRangeTest, UnionWithExhaustive) {testBinarySetOperationExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2,ConstantRange::PreferredRangeType Type) {return CR1.unionWith(CR2, Type);},[](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.exactUnionWith(CR2);},[](const ConstantRange &CR1, const ConstantRange &CR2, const APInt &N) {return CR1.contains(N) || CR2.contains(N);});}TEST_F(ConstantRangeTest, UnionWith) {EXPECT_EQ(Wrap.unionWith(One),ConstantRange(APInt(16, 0xaaa), APInt(16, 0xb)));EXPECT_EQ(One.unionWith(Wrap), Wrap.unionWith(One));EXPECT_EQ(Empty.unionWith(Empty), Empty);EXPECT_EQ(Full.unionWith(Full), Full);EXPECT_EQ(Some.unionWith(Wrap), Full);// PR4545EXPECT_EQ(ConstantRange(APInt(16, 14), APInt(16, 1)).unionWith(ConstantRange(APInt(16, 0), APInt(16, 8))),ConstantRange(APInt(16, 14), APInt(16, 8)));EXPECT_EQ(ConstantRange(APInt(16, 6), APInt(16, 4)).unionWith(ConstantRange(APInt(16, 4), APInt(16, 0))),ConstantRange::getFull(16));EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 0)).unionWith(ConstantRange(APInt(16, 2), APInt(16, 1))),ConstantRange::getFull(16));}TEST_F(ConstantRangeTest, SetDifference) {EXPECT_EQ(Full.difference(Empty), Full);EXPECT_EQ(Full.difference(Full), Empty);EXPECT_EQ(Empty.difference(Empty), Empty);EXPECT_EQ(Empty.difference(Full), Empty);ConstantRange A(APInt(16, 3), APInt(16, 7));ConstantRange B(APInt(16, 5), APInt(16, 9));ConstantRange C(APInt(16, 3), APInt(16, 5));ConstantRange D(APInt(16, 7), APInt(16, 9));ConstantRange E(APInt(16, 5), APInt(16, 4));ConstantRange F(APInt(16, 7), APInt(16, 3));EXPECT_EQ(A.difference(B), C);EXPECT_EQ(B.difference(A), D);EXPECT_EQ(E.difference(A), F);}TEST_F(ConstantRangeTest, getActiveBits) {unsigned Bits = 4;EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {unsigned Exact = 0;ForeachNumInConstantRange(CR, [&](const APInt &N) {Exact = std::max(Exact, N.getActiveBits());});unsigned ResultCR = CR.getActiveBits();EXPECT_EQ(Exact, ResultCR);});}TEST_F(ConstantRangeTest, losslessUnsignedTruncationZeroext) {unsigned Bits = 4;EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {unsigned MinBitWidth = CR.getActiveBits();if (MinBitWidth == 0) {EXPECT_TRUE(CR.isEmptySet() ||(CR.isSingleElement() && CR.getSingleElement()->isZero()));return;}if (MinBitWidth == Bits)return;EXPECT_EQ(CR, CR.truncate(MinBitWidth).zeroExtend(Bits));});}TEST_F(ConstantRangeTest, getMinSignedBits) {unsigned Bits = 4;EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {unsigned Exact = 0;ForeachNumInConstantRange(CR, [&](const APInt &N) {Exact = std::max(Exact, N.getMinSignedBits());});unsigned ResultCR = CR.getMinSignedBits();EXPECT_EQ(Exact, ResultCR);});}TEST_F(ConstantRangeTest, losslessSignedTruncationSignext) {unsigned Bits = 4;EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {unsigned MinBitWidth = CR.getMinSignedBits();if (MinBitWidth == 0) {EXPECT_TRUE(CR.isEmptySet());return;}if (MinBitWidth == Bits)return;EXPECT_EQ(CR, CR.truncate(MinBitWidth).signExtend(Bits));});}TEST_F(ConstantRangeTest, SubtractAPInt) {EXPECT_EQ(Full.subtract(APInt(16, 4)), Full);EXPECT_EQ(Empty.subtract(APInt(16, 4)), Empty);EXPECT_EQ(Some.subtract(APInt(16, 4)),ConstantRange(APInt(16, 0x6), APInt(16, 0xaa6)));EXPECT_EQ(Wrap.subtract(APInt(16, 4)),ConstantRange(APInt(16, 0xaa6), APInt(16, 0x6)));EXPECT_EQ(One.subtract(APInt(16, 4)),ConstantRange(APInt(16, 0x6)));}TEST_F(ConstantRangeTest, Add) {EXPECT_EQ(Full.add(APInt(16, 4)), Full);EXPECT_EQ(Full.add(Full), Full);EXPECT_EQ(Full.add(Empty), Empty);EXPECT_EQ(Full.add(One), Full);EXPECT_EQ(Full.add(Some), Full);EXPECT_EQ(Full.add(Wrap), Full);EXPECT_EQ(Empty.add(Empty), Empty);EXPECT_EQ(Empty.add(One), Empty);EXPECT_EQ(Empty.add(Some), Empty);EXPECT_EQ(Empty.add(Wrap), Empty);EXPECT_EQ(Empty.add(APInt(16, 4)), Empty);EXPECT_EQ(Some.add(APInt(16, 4)),ConstantRange(APInt(16, 0xe), APInt(16, 0xaae)));EXPECT_EQ(Wrap.add(APInt(16, 4)),ConstantRange(APInt(16, 0xaae), APInt(16, 0xe)));EXPECT_EQ(One.add(APInt(16, 4)),ConstantRange(APInt(16, 0xe)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.add(CR2);},[](const APInt &N1, const APInt &N2) {return N1 + N2;});}TEST_F(ConstantRangeTest, AddWithNoWrap) {typedef OverflowingBinaryOperator OBO;EXPECT_EQ(Empty.addWithNoWrap(Some, OBO::NoSignedWrap), Empty);EXPECT_EQ(Some.addWithNoWrap(Empty, OBO::NoSignedWrap), Empty);EXPECT_EQ(Full.addWithNoWrap(Full, OBO::NoSignedWrap), Full);EXPECT_NE(Full.addWithNoWrap(Some, OBO::NoSignedWrap), Full);EXPECT_NE(Some.addWithNoWrap(Full, OBO::NoSignedWrap), Full);EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, 1), APInt(16, 2)),OBO::NoSignedWrap),ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN)));EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 2)).addWithNoWrap(Full, OBO::NoSignedWrap),ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN)));EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, -1), APInt(16, 0)),OBO::NoSignedWrap),ConstantRange(APInt(16, INT16_MIN), APInt(16, INT16_MAX)));EXPECT_EQ(ConstantRange(APInt(8, 100), APInt(8, 120)).addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, 123)),OBO::NoSignedWrap),ConstantRange(8, false));EXPECT_EQ(ConstantRange(APInt(8, -120), APInt(8, -100)).addWithNoWrap(ConstantRange(APInt(8, -110), APInt(8, -100)),OBO::NoSignedWrap),ConstantRange(8, false));EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)).addWithNoWrap(ConstantRange(APInt(8, -128), APInt(8, 28)),OBO::NoSignedWrap),ConstantRange(8, true));EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)).addWithNoWrap(ConstantRange(APInt(8, -120), APInt(8, 29)),OBO::NoSignedWrap),ConstantRange(APInt(8, -120), APInt(8, -128)));EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50)).addWithNoWrap(ConstantRange(APInt(8, 10), APInt(8, 20)),OBO::NoSignedWrap),ConstantRange(APInt(8, -40), APInt(8, 69)));EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20)).addWithNoWrap(ConstantRange(APInt(8, -50), APInt(8, 50)),OBO::NoSignedWrap),ConstantRange(APInt(8, -40), APInt(8, 69)));EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10)).addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)),OBO::NoSignedWrap),ConstantRange(APInt(8, 125), APInt(8, 9)));EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20)).addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, -10)),OBO::NoSignedWrap),ConstantRange(APInt(8, 125), APInt(8, 9)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.addWithNoWrap(CR2, OBO::NoSignedWrap);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {bool IsOverflow;APInt Res = N1.sadd_ov(N2, IsOverflow);if (IsOverflow)return None;return Res;},PreferSmallest,CheckNonSignWrappedOnly);EXPECT_EQ(Empty.addWithNoWrap(Some, OBO::NoUnsignedWrap), Empty);EXPECT_EQ(Some.addWithNoWrap(Empty, OBO::NoUnsignedWrap), Empty);EXPECT_EQ(Full.addWithNoWrap(Full, OBO::NoUnsignedWrap), Full);EXPECT_NE(Full.addWithNoWrap(Some, OBO::NoUnsignedWrap), Full);EXPECT_NE(Some.addWithNoWrap(Full, OBO::NoUnsignedWrap), Full);EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, 1), APInt(16, 2)),OBO::NoUnsignedWrap),ConstantRange(APInt(16, 1), APInt(16, 0)));EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 2)).addWithNoWrap(Full, OBO::NoUnsignedWrap),ConstantRange(APInt(16, 1), APInt(16, 0)));EXPECT_EQ(ConstantRange(APInt(8, 200), APInt(8, 220)).addWithNoWrap(ConstantRange(APInt(8, 100), APInt(8, 123)),OBO::NoUnsignedWrap),ConstantRange(8, false));EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)).addWithNoWrap(ConstantRange(APInt(8, 0), APInt(8, 156)),OBO::NoUnsignedWrap),ConstantRange(8, true));EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)).addWithNoWrap(ConstantRange(APInt(8, 10), APInt(8, 29)),OBO::NoUnsignedWrap),ConstantRange(APInt(8, 10), APInt(8, 129)));EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, 10)).addWithNoWrap(ConstantRange(APInt(8, 50), APInt(8, 200)),OBO::NoUnsignedWrap),ConstantRange(APInt(8, 50), APInt(8, 0)));EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20)).addWithNoWrap(ConstantRange(APInt(8, 50), APInt(8, 200)),OBO::NoUnsignedWrap),ConstantRange(APInt(8, 60), APInt(8, -37)));EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, -30)).addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)),OBO::NoUnsignedWrap),ConstantRange(APInt(8, 25), APInt(8, -11)));EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20)).addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, -30)),OBO::NoUnsignedWrap),ConstantRange(APInt(8, 25), APInt(8, -11)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.addWithNoWrap(CR2, OBO::NoUnsignedWrap);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {bool IsOverflow;APInt Res = N1.uadd_ov(N2, IsOverflow);if (IsOverflow)return None;return Res;},PreferSmallest,CheckNonWrappedOnly);EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)).addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)),OBO::NoSignedWrap),ConstantRange(APInt(8, 70), APInt(8, -128)));EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)).addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)),OBO::NoUnsignedWrap),ConstantRange(APInt(8, 70), APInt(8, 169)));EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)).addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)),OBO::NoUnsignedWrap | OBO::NoSignedWrap),ConstantRange(APInt(8, 70), APInt(8, -128)));EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)).addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)),OBO::NoSignedWrap),ConstantRange(APInt(8, -80), APInt(8, -21)));EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)).addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)),OBO::NoUnsignedWrap),ConstantRange(APInt(8, 176), APInt(8, 235)));EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)).addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)),OBO::NoUnsignedWrap | OBO::NoSignedWrap),ConstantRange(APInt(8, 176), APInt(8, 235)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.addWithNoWrap(CR2, OBO::NoUnsignedWrap | OBO::NoSignedWrap);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {bool IsOverflow1, IsOverflow2;APInt Res1 = N1.uadd_ov(N2, IsOverflow1);APInt Res2 = N1.sadd_ov(N2, IsOverflow2);if (IsOverflow1 || IsOverflow2)return None;assert(Res1 == Res2 && "Addition results differ?");return Res1;},PreferSmallest,CheckNonWrappedOrSignWrappedOnly);}TEST_F(ConstantRangeTest, Sub) {EXPECT_EQ(Full.sub(APInt(16, 4)), Full);EXPECT_EQ(Full.sub(Full), Full);EXPECT_EQ(Full.sub(Empty), Empty);EXPECT_EQ(Full.sub(One), Full);EXPECT_EQ(Full.sub(Some), Full);EXPECT_EQ(Full.sub(Wrap), Full);EXPECT_EQ(Empty.sub(Empty), Empty);EXPECT_EQ(Empty.sub(One), Empty);EXPECT_EQ(Empty.sub(Some), Empty);EXPECT_EQ(Empty.sub(Wrap), Empty);EXPECT_EQ(Empty.sub(APInt(16, 4)), Empty);EXPECT_EQ(Some.sub(APInt(16, 4)),ConstantRange(APInt(16, 0x6), APInt(16, 0xaa6)));EXPECT_EQ(Some.sub(Some),ConstantRange(APInt(16, 0xf561), APInt(16, 0xaa0)));EXPECT_EQ(Wrap.sub(APInt(16, 4)),ConstantRange(APInt(16, 0xaa6), APInt(16, 0x6)));EXPECT_EQ(One.sub(APInt(16, 4)),ConstantRange(APInt(16, 0x6)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.sub(CR2);},[](const APInt &N1, const APInt &N2) {return N1 - N2;});}TEST_F(ConstantRangeTest, SubWithNoWrap) {typedef OverflowingBinaryOperator OBO;TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.subWithNoWrap(CR2, OBO::NoSignedWrap);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {bool IsOverflow;APInt Res = N1.ssub_ov(N2, IsOverflow);if (IsOverflow)return None;return Res;},PreferSmallest,CheckNonSignWrappedOnly);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.subWithNoWrap(CR2, OBO::NoUnsignedWrap);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {bool IsOverflow;APInt Res = N1.usub_ov(N2, IsOverflow);if (IsOverflow)return None;return Res;},PreferSmallest,CheckNonWrappedOnly);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.subWithNoWrap(CR2, OBO::NoUnsignedWrap | OBO::NoSignedWrap);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {bool IsOverflow1, IsOverflow2;APInt Res1 = N1.usub_ov(N2, IsOverflow1);APInt Res2 = N1.ssub_ov(N2, IsOverflow2);if (IsOverflow1 || IsOverflow2)return None;assert(Res1 == Res2 && "Subtraction results differ?");return Res1;},PreferSmallest,CheckNonWrappedOrSignWrappedOnly);}TEST_F(ConstantRangeTest, Multiply) {EXPECT_EQ(Full.multiply(Full), Full);EXPECT_EQ(Full.multiply(Empty), Empty);EXPECT_EQ(Full.multiply(One), Full);EXPECT_EQ(Full.multiply(Some), Full);EXPECT_EQ(Full.multiply(Wrap), Full);EXPECT_EQ(Empty.multiply(Empty), Empty);EXPECT_EQ(Empty.multiply(One), Empty);EXPECT_EQ(Empty.multiply(Some), Empty);EXPECT_EQ(Empty.multiply(Wrap), Empty);EXPECT_EQ(One.multiply(One), ConstantRange(APInt(16, 0xa*0xa),APInt(16, 0xa*0xa + 1)));EXPECT_EQ(One.multiply(Some), ConstantRange(APInt(16, 0xa*0xa),APInt(16, 0xa*0xaa9 + 1)));EXPECT_EQ(One.multiply(Wrap), Full);EXPECT_EQ(Some.multiply(Some), Full);EXPECT_EQ(Some.multiply(Wrap), Full);EXPECT_EQ(Wrap.multiply(Wrap), Full);ConstantRange Zero(APInt(16, 0));EXPECT_EQ(Zero.multiply(Full), Zero);EXPECT_EQ(Zero.multiply(Some), Zero);EXPECT_EQ(Zero.multiply(Wrap), Zero);EXPECT_EQ(Full.multiply(Zero), Zero);EXPECT_EQ(Some.multiply(Zero), Zero);EXPECT_EQ(Wrap.multiply(Zero), Zero);// http://llvm.org/PR4545EXPECT_EQ(ConstantRange(APInt(4, 1), APInt(4, 6)).multiply(ConstantRange(APInt(4, 6), APInt(4, 2))),ConstantRange(4, /*isFullSet=*/true));EXPECT_EQ(ConstantRange(APInt(8, 254), APInt(8, 0)).multiply(ConstantRange(APInt(8, 252), APInt(8, 4))),ConstantRange(APInt(8, 250), APInt(8, 9)));EXPECT_EQ(ConstantRange(APInt(8, 254), APInt(8, 255)).multiply(ConstantRange(APInt(8, 2), APInt(8, 4))),ConstantRange(APInt(8, 250), APInt(8, 253)));// TODO: This should be return [-2, 0]EXPECT_EQ(ConstantRange(APInt(8, -2)).multiply(ConstantRange(APInt(8, 0), APInt(8, 2))),ConstantRange(APInt(8, -2), APInt(8, 1)));}TEST_F(ConstantRangeTest, smul_fast) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.smul_fast(CR2);},[](const APInt &N1, const APInt &N2) {return N1 * N2;},PreferSmallest,[](const ConstantRange &, const ConstantRange &) {return false; // Check correctness only.});}TEST_F(ConstantRangeTest, UMax) {EXPECT_EQ(Full.umax(Full), Full);EXPECT_EQ(Full.umax(Empty), Empty);EXPECT_EQ(Full.umax(Some), ConstantRange(APInt(16, 0xa), APInt(16, 0)));EXPECT_EQ(Full.umax(Wrap), Full);EXPECT_EQ(Full.umax(Some), ConstantRange(APInt(16, 0xa), APInt(16, 0)));EXPECT_EQ(Empty.umax(Empty), Empty);EXPECT_EQ(Empty.umax(Some), Empty);EXPECT_EQ(Empty.umax(Wrap), Empty);EXPECT_EQ(Empty.umax(One), Empty);EXPECT_EQ(Some.umax(Some), Some);EXPECT_EQ(Some.umax(Wrap), ConstantRange(APInt(16, 0xa), APInt(16, 0)));EXPECT_EQ(Some.umax(One), Some);EXPECT_EQ(Wrap.umax(Wrap), Wrap);EXPECT_EQ(Wrap.umax(One), ConstantRange(APInt(16, 0xa), APInt(16, 0)));EXPECT_EQ(One.umax(One), One);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.umax(CR2);},[](const APInt &N1, const APInt &N2) {return APIntOps::umax(N1, N2);},PreferSmallestNonFullUnsigned);}TEST_F(ConstantRangeTest, SMax) {EXPECT_EQ(Full.smax(Full), Full);EXPECT_EQ(Full.smax(Empty), Empty);EXPECT_EQ(Full.smax(Some), ConstantRange(APInt(16, 0xa),APInt::getSignedMinValue(16)));EXPECT_EQ(Full.smax(Wrap), Full);EXPECT_EQ(Full.smax(One), ConstantRange(APInt(16, 0xa),APInt::getSignedMinValue(16)));EXPECT_EQ(Empty.smax(Empty), Empty);EXPECT_EQ(Empty.smax(Some), Empty);EXPECT_EQ(Empty.smax(Wrap), Empty);EXPECT_EQ(Empty.smax(One), Empty);EXPECT_EQ(Some.smax(Some), Some);EXPECT_EQ(Some.smax(Wrap), ConstantRange(APInt(16, 0xa),APInt(16, (uint64_t)INT16_MIN)));EXPECT_EQ(Some.smax(One), Some);EXPECT_EQ(Wrap.smax(One), ConstantRange(APInt(16, 0xa),APInt(16, (uint64_t)INT16_MIN)));EXPECT_EQ(One.smax(One), One);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.smax(CR2);},[](const APInt &N1, const APInt &N2) {return APIntOps::smax(N1, N2);},PreferSmallestNonFullSigned);}TEST_F(ConstantRangeTest, UMin) {EXPECT_EQ(Full.umin(Full), Full);EXPECT_EQ(Full.umin(Empty), Empty);EXPECT_EQ(Full.umin(Some), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));EXPECT_EQ(Full.umin(Wrap), Full);EXPECT_EQ(Empty.umin(Empty), Empty);EXPECT_EQ(Empty.umin(Some), Empty);EXPECT_EQ(Empty.umin(Wrap), Empty);EXPECT_EQ(Empty.umin(One), Empty);EXPECT_EQ(Some.umin(Some), Some);EXPECT_EQ(Some.umin(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));EXPECT_EQ(Some.umin(One), One);EXPECT_EQ(Wrap.umin(Wrap), Wrap);EXPECT_EQ(Wrap.umin(One), ConstantRange(APInt(16, 0), APInt(16, 0xb)));EXPECT_EQ(One.umin(One), One);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.umin(CR2);},[](const APInt &N1, const APInt &N2) {return APIntOps::umin(N1, N2);},PreferSmallestNonFullUnsigned);}TEST_F(ConstantRangeTest, SMin) {EXPECT_EQ(Full.smin(Full), Full);EXPECT_EQ(Full.smin(Empty), Empty);EXPECT_EQ(Full.smin(Some), ConstantRange(APInt(16, (uint64_t)INT16_MIN),APInt(16, 0xaaa)));EXPECT_EQ(Full.smin(Wrap), Full);EXPECT_EQ(Empty.smin(Empty), Empty);EXPECT_EQ(Empty.smin(Some), Empty);EXPECT_EQ(Empty.smin(Wrap), Empty);EXPECT_EQ(Empty.smin(One), Empty);EXPECT_EQ(Some.smin(Some), Some);EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN),APInt(16, 0xaaa)));EXPECT_EQ(Some.smin(One), One);EXPECT_EQ(Wrap.smin(Wrap), Wrap);EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN),APInt(16, 0xb)));EXPECT_EQ(One.smin(One), One);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.smin(CR2);},[](const APInt &N1, const APInt &N2) {return APIntOps::smin(N1, N2);},PreferSmallestNonFullSigned);}TEST_F(ConstantRangeTest, UDiv) {EXPECT_EQ(Full.udiv(Full), Full);EXPECT_EQ(Full.udiv(Empty), Empty);EXPECT_EQ(Full.udiv(One), ConstantRange(APInt(16, 0),APInt(16, 0xffff / 0xa + 1)));EXPECT_EQ(Full.udiv(Some), ConstantRange(APInt(16, 0),APInt(16, 0xffff / 0xa + 1)));EXPECT_EQ(Full.udiv(Wrap), Full);EXPECT_EQ(Empty.udiv(Empty), Empty);EXPECT_EQ(Empty.udiv(One), Empty);EXPECT_EQ(Empty.udiv(Some), Empty);EXPECT_EQ(Empty.udiv(Wrap), Empty);EXPECT_EQ(One.udiv(One), ConstantRange(APInt(16, 1)));EXPECT_EQ(One.udiv(Some), ConstantRange(APInt(16, 0), APInt(16, 2)));EXPECT_EQ(One.udiv(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xb)));EXPECT_EQ(Some.udiv(Some), ConstantRange(APInt(16, 0), APInt(16, 0x111)));EXPECT_EQ(Some.udiv(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));EXPECT_EQ(Wrap.udiv(Wrap), Full);ConstantRange Zero(APInt(16, 0));EXPECT_EQ(Zero.udiv(One), Zero);EXPECT_EQ(Zero.udiv(Full), Zero);EXPECT_EQ(ConstantRange(APInt(16, 0), APInt(16, 99)).udiv(Full),ConstantRange(APInt(16, 0), APInt(16, 99)));EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 99)).udiv(Full),ConstantRange(APInt(16, 0), APInt(16, 99)));}TEST_F(ConstantRangeTest, SDiv) {ConstantRange OneBit = ConstantRange::getFull(1);EXPECT_EQ(OneBit.sdiv(OneBit), ConstantRange(APInt(1, 0)));unsigned Bits = 4;EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,const ConstantRange &CR2) {// Collect possible results in a bit vector. We store the signed value plus// a bias to make it unsigned.int Bias = 1 << (Bits - 1);BitVector Results(1 << Bits);ForeachNumInConstantRange(CR1, [&](const APInt &N1) {ForeachNumInConstantRange(CR2, [&](const APInt &N2) {// Division by zero is UB.if (N2 == 0)return;// SignedMin / -1 is UB.if (N1.isMinSignedValue() && N2.isAllOnes())return;APInt N = N1.sdiv(N2);Results.set(N.getSExtValue() + Bias);});});ConstantRange CR = CR1.sdiv(CR2);if (Results.none()) {EXPECT_TRUE(CR.isEmptySet());return;}// If there is a non-full signed envelope, that should be the result.APInt SMin(Bits, Results.find_first() - Bias);APInt SMax(Bits, Results.find_last() - Bias);ConstantRange Envelope = ConstantRange::getNonEmpty(SMin, SMax + 1);if (!Envelope.isFullSet()) {EXPECT_EQ(Envelope, CR);return;}// If the signed envelope is a full set, try to find a smaller sign wrapped// set that is separated in negative and positive components (or one which// can also additionally contain zero).int LastNeg = Results.find_last_in(0, Bias) - Bias;int LastPos = Results.find_next(Bias) - Bias;if (Results[Bias]) {if (LastNeg == -1)++LastNeg;else if (LastPos == 1)--LastPos;}APInt WMax(Bits, LastNeg);APInt WMin(Bits, LastPos);ConstantRange Wrapped = ConstantRange::getNonEmpty(WMin, WMax + 1);EXPECT_EQ(Wrapped, CR);});}TEST_F(ConstantRangeTest, URem) {EXPECT_EQ(Full.urem(Empty), Empty);EXPECT_EQ(Empty.urem(Full), Empty);// urem by zero is poison.EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0))), Empty);// urem by full range doesn't contain MaxValue.EXPECT_EQ(Full.urem(Full), ConstantRange(APInt(16, 0), APInt(16, 0xffff)));// urem is upper bounded by maximum RHS minus one.EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0), APInt(16, 123))),ConstantRange(APInt(16, 0), APInt(16, 122)));// urem is upper bounded by maximum LHS.EXPECT_EQ(ConstantRange(APInt(16, 0), APInt(16, 123)).urem(Full),ConstantRange(APInt(16, 0), APInt(16, 123)));// If the LHS is always lower than the RHS, the result is the LHS.EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20)).urem(ConstantRange(APInt(16, 20), APInt(16, 30))),ConstantRange(APInt(16, 10), APInt(16, 20)));// It has to be strictly lower, otherwise the top value may wrap to zero.EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20)).urem(ConstantRange(APInt(16, 19), APInt(16, 30))),ConstantRange(APInt(16, 0), APInt(16, 20)));// [12, 14] % 10 is [2, 4], but we conservatively compute [0, 9].EXPECT_EQ(ConstantRange(APInt(16, 12), APInt(16, 15)).urem(ConstantRange(APInt(16, 10))),ConstantRange(APInt(16, 0), APInt(16, 10)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.urem(CR2);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {if (N2.isZero())return None;return N1.urem(N2);},PreferSmallest,CheckSingleElementsOnly);}TEST_F(ConstantRangeTest, SRem) {EXPECT_EQ(Full.srem(Empty), Empty);EXPECT_EQ(Empty.srem(Full), Empty);// srem by zero is UB.EXPECT_EQ(Full.srem(ConstantRange(APInt(16, 0))), Empty);// srem by full range doesn't contain SignedMinValue.EXPECT_EQ(Full.srem(Full), ConstantRange(APInt::getSignedMinValue(16) + 1,APInt::getSignedMinValue(16)));ConstantRange PosMod(APInt(16, 10), APInt(16, 21)); // [10, 20]ConstantRange NegMod(APInt(16, -20), APInt(16, -9)); // [-20, -10]ConstantRange IntMinMod(APInt::getSignedMinValue(16));ConstantRange Expected(16, true);// srem is bounded by abs(RHS) minus one.ConstantRange PosLargeLHS(APInt(16, 0), APInt(16, 41));Expected = ConstantRange(APInt(16, 0), APInt(16, 20));EXPECT_EQ(PosLargeLHS.srem(PosMod), Expected);EXPECT_EQ(PosLargeLHS.srem(NegMod), Expected);ConstantRange NegLargeLHS(APInt(16, -40), APInt(16, 1));Expected = ConstantRange(APInt(16, -19), APInt(16, 1));EXPECT_EQ(NegLargeLHS.srem(PosMod), Expected);EXPECT_EQ(NegLargeLHS.srem(NegMod), Expected);ConstantRange PosNegLargeLHS(APInt(16, -32), APInt(16, 38));Expected = ConstantRange(APInt(16, -19), APInt(16, 20));EXPECT_EQ(PosNegLargeLHS.srem(PosMod), Expected);EXPECT_EQ(PosNegLargeLHS.srem(NegMod), Expected);// srem is bounded by LHS.ConstantRange PosLHS(APInt(16, 0), APInt(16, 16));EXPECT_EQ(PosLHS.srem(PosMod), PosLHS);EXPECT_EQ(PosLHS.srem(NegMod), PosLHS);EXPECT_EQ(PosLHS.srem(IntMinMod), PosLHS);ConstantRange NegLHS(APInt(16, -15), APInt(16, 1));EXPECT_EQ(NegLHS.srem(PosMod), NegLHS);EXPECT_EQ(NegLHS.srem(NegMod), NegLHS);EXPECT_EQ(NegLHS.srem(IntMinMod), NegLHS);ConstantRange PosNegLHS(APInt(16, -12), APInt(16, 18));EXPECT_EQ(PosNegLHS.srem(PosMod), PosNegLHS);EXPECT_EQ(PosNegLHS.srem(NegMod), PosNegLHS);EXPECT_EQ(PosNegLHS.srem(IntMinMod), PosNegLHS);// srem is LHS if it is smaller than RHS.ConstantRange PosSmallLHS(APInt(16, 3), APInt(16, 8));EXPECT_EQ(PosSmallLHS.srem(PosMod), PosSmallLHS);EXPECT_EQ(PosSmallLHS.srem(NegMod), PosSmallLHS);EXPECT_EQ(PosSmallLHS.srem(IntMinMod), PosSmallLHS);ConstantRange NegSmallLHS(APInt(16, -7), APInt(16, -2));EXPECT_EQ(NegSmallLHS.srem(PosMod), NegSmallLHS);EXPECT_EQ(NegSmallLHS.srem(NegMod), NegSmallLHS);EXPECT_EQ(NegSmallLHS.srem(IntMinMod), NegSmallLHS);ConstantRange PosNegSmallLHS(APInt(16, -3), APInt(16, 8));EXPECT_EQ(PosNegSmallLHS.srem(PosMod), PosNegSmallLHS);EXPECT_EQ(PosNegSmallLHS.srem(NegMod), PosNegSmallLHS);EXPECT_EQ(PosNegSmallLHS.srem(IntMinMod), PosNegSmallLHS);// Example of a suboptimal result:// [12, 14] srem 10 is [2, 4], but we conservatively compute [0, 9].EXPECT_EQ(ConstantRange(APInt(16, 12), APInt(16, 15)).srem(ConstantRange(APInt(16, 10))),ConstantRange(APInt(16, 0), APInt(16, 10)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.srem(CR2);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {if (N2.isZero())return None;return N1.srem(N2);},PreferSmallest,CheckSingleElementsOnly);}TEST_F(ConstantRangeTest, Shl) {ConstantRange Some2(APInt(16, 0xfff), APInt(16, 0x8000));ConstantRange WrapNullMax(APInt(16, 0x1), APInt(16, 0x0));EXPECT_EQ(Full.shl(Full), Full);EXPECT_EQ(Full.shl(Empty), Empty);EXPECT_EQ(Full.shl(One), ConstantRange(APInt(16, 0),APInt(16, 0xfc00) + 1));EXPECT_EQ(Full.shl(Some), Full); // TODO: [0, (-1 << 0xa) + 1)EXPECT_EQ(Full.shl(Wrap), Full);EXPECT_EQ(Empty.shl(Empty), Empty);EXPECT_EQ(Empty.shl(One), Empty);EXPECT_EQ(Empty.shl(Some), Empty);EXPECT_EQ(Empty.shl(Wrap), Empty);EXPECT_EQ(One.shl(One), ConstantRange(APInt(16, 0xa << 0xa),APInt(16, (0xa << 0xa) + 1)));EXPECT_EQ(One.shl(Some), Full); // TODO: [0xa << 0xa, 0)EXPECT_EQ(One.shl(Wrap), Full); // TODO: [0xa, 0xa << 14 + 1)EXPECT_EQ(Some.shl(Some), Full); // TODO: [0xa << 0xa, 0xfc01)EXPECT_EQ(Some.shl(Wrap), Full); // TODO: [0xa, 0x7ff << 0x5 + 1)EXPECT_EQ(Wrap.shl(Wrap), Full);EXPECT_EQ(Some2.shl(ConstantRange(APInt(16, 0x1))),ConstantRange(APInt(16, 0xfff << 0x1), APInt(16, 0x7fff << 0x1) + 1));EXPECT_EQ(One.shl(WrapNullMax), Full);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.shl(CR2);},[](const APInt &N1, const APInt &N2) -> Optional<APInt> {if (N2.uge(N2.getBitWidth()))return None;return N1.shl(N2);},PreferSmallestUnsigned,[](const ConstantRange &, const ConstantRange &CR2) {// We currently only produce precise results for single element RHS.return CR2.isSingleElement();});}TEST_F(ConstantRangeTest, Lshr) {EXPECT_EQ(Full.lshr(Full), Full);EXPECT_EQ(Full.lshr(Empty), Empty);EXPECT_EQ(Full.lshr(One), ConstantRange(APInt(16, 0),APInt(16, (0xffff >> 0xa) + 1)));EXPECT_EQ(Full.lshr(Some), ConstantRange(APInt(16, 0),APInt(16, (0xffff >> 0xa) + 1)));EXPECT_EQ(Full.lshr(Wrap), Full);EXPECT_EQ(Empty.lshr(Empty), Empty);EXPECT_EQ(Empty.lshr(One), Empty);EXPECT_EQ(Empty.lshr(Some), Empty);EXPECT_EQ(Empty.lshr(Wrap), Empty);EXPECT_EQ(One.lshr(One), ConstantRange(APInt(16, 0)));EXPECT_EQ(One.lshr(Some), ConstantRange(APInt(16, 0)));EXPECT_EQ(One.lshr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xb)));EXPECT_EQ(Some.lshr(Some), ConstantRange(APInt(16, 0),APInt(16, (0xaaa >> 0xa) + 1)));EXPECT_EQ(Some.lshr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));EXPECT_EQ(Wrap.lshr(Wrap), Full);}TEST_F(ConstantRangeTest, Ashr) {EXPECT_EQ(Full.ashr(Full), Full);EXPECT_EQ(Full.ashr(Empty), Empty);EXPECT_EQ(Full.ashr(One), ConstantRange(APInt(16, 0xffe0),APInt(16, (0x7fff >> 0xa) + 1 )));ConstantRange Small(APInt(16, 0xa), APInt(16, 0xb));EXPECT_EQ(Full.ashr(Small), ConstantRange(APInt(16, 0xffe0),APInt(16, (0x7fff >> 0xa) + 1 )));EXPECT_EQ(Full.ashr(Some), ConstantRange(APInt(16, 0xffe0),APInt(16, (0x7fff >> 0xa) + 1 )));EXPECT_EQ(Full.ashr(Wrap), Full);EXPECT_EQ(Empty.ashr(Empty), Empty);EXPECT_EQ(Empty.ashr(One), Empty);EXPECT_EQ(Empty.ashr(Some), Empty);EXPECT_EQ(Empty.ashr(Wrap), Empty);EXPECT_EQ(One.ashr(One), ConstantRange(APInt(16, 0)));EXPECT_EQ(One.ashr(Some), ConstantRange(APInt(16, 0)));EXPECT_EQ(One.ashr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xb)));EXPECT_EQ(Some.ashr(Some), ConstantRange(APInt(16, 0),APInt(16, (0xaaa >> 0xa) + 1)));EXPECT_EQ(Some.ashr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));EXPECT_EQ(Wrap.ashr(Wrap), Full);ConstantRange Neg(APInt(16, 0xf3f0, true), APInt(16, 0xf7f8, true));EXPECT_EQ(Neg.ashr(Small), ConstantRange(APInt(16, 0xfffc, true),APInt(16, 0xfffe, true)));}TEST(ConstantRange, MakeAllowedICmpRegion) {// PR8250ConstantRange SMax = ConstantRange(APInt::getSignedMaxValue(32));EXPECT_TRUE(ConstantRange::makeAllowedICmpRegion(ICmpInst::ICMP_SGT, SMax).isEmptySet());}TEST(ConstantRange, MakeSatisfyingICmpRegion) {ConstantRange LowHalf(APInt(8, 0), APInt(8, 128));ConstantRange HighHalf(APInt(8, 128), APInt(8, 0));ConstantRange EmptySet(8, /* isFullSet = */ false);EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_NE, LowHalf),HighHalf);EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_NE, HighHalf),LowHalf);EXPECT_TRUE(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_EQ,HighHalf).isEmptySet());ConstantRange UnsignedSample(APInt(8, 5), APInt(8, 200));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_ULT,UnsignedSample),ConstantRange(APInt(8, 0), APInt(8, 5)));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_ULE,UnsignedSample),ConstantRange(APInt(8, 0), APInt(8, 6)));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_UGT,UnsignedSample),ConstantRange(APInt(8, 200), APInt(8, 0)));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_UGE,UnsignedSample),ConstantRange(APInt(8, 199), APInt(8, 0)));ConstantRange SignedSample(APInt(8, -5), APInt(8, 5));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLT, SignedSample),ConstantRange(APInt(8, -128), APInt(8, -5)));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLE, SignedSample),ConstantRange(APInt(8, -128), APInt(8, -4)));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGT, SignedSample),ConstantRange(APInt(8, 5), APInt(8, -128)));EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGE, SignedSample),ConstantRange(APInt(8, 4), APInt(8, -128)));}void ICmpTestImpl(CmpInst::Predicate Pred) {unsigned Bits = 4;EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {bool Exhaustive = true;ForeachNumInConstantRange(CR1, [&](const APInt &N1) {ForeachNumInConstantRange(CR2, [&](const APInt &N2) {Exhaustive &= ICmpInst::compare(N1, N2, Pred);});});EXPECT_EQ(CR1.icmp(Pred, CR2), Exhaustive);});}TEST(ConstantRange, ICmp) {for (auto Pred : ICmpInst::predicates())ICmpTestImpl(Pred);}TEST(ConstantRange, MakeGuaranteedNoWrapRegion) {const int IntMin4Bits = 8;const int IntMax4Bits = 7;typedef OverflowingBinaryOperator OBO;for (int Const : {0, -1, -2, 1, 2, IntMin4Bits, IntMax4Bits}) {APInt C(4, Const, true /* = isSigned */);auto NUWRegion = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, C, OBO::NoUnsignedWrap);EXPECT_FALSE(NUWRegion.isEmptySet());auto NSWRegion = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, C, OBO::NoSignedWrap);EXPECT_FALSE(NSWRegion.isEmptySet());for (APInt I = NUWRegion.getLower(), E = NUWRegion.getUpper(); I != E;++I) {bool Overflow = false;(void)I.uadd_ov(C, Overflow);EXPECT_FALSE(Overflow);}for (APInt I = NSWRegion.getLower(), E = NSWRegion.getUpper(); I != E;++I) {bool Overflow = false;(void)I.sadd_ov(C, Overflow);EXPECT_FALSE(Overflow);}}for (int Const : {0, -1, -2, 1, 2, IntMin4Bits, IntMax4Bits}) {APInt C(4, Const, true /* = isSigned */);auto NUWRegion = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, C, OBO::NoUnsignedWrap);EXPECT_FALSE(NUWRegion.isEmptySet());auto NSWRegion = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, C, OBO::NoSignedWrap);EXPECT_FALSE(NSWRegion.isEmptySet());for (APInt I = NUWRegion.getLower(), E = NUWRegion.getUpper(); I != E;++I) {bool Overflow = false;(void)I.usub_ov(C, Overflow);EXPECT_FALSE(Overflow);}for (APInt I = NSWRegion.getLower(), E = NSWRegion.getUpper(); I != E;++I) {bool Overflow = false;(void)I.ssub_ov(C, Overflow);EXPECT_FALSE(Overflow);}}auto NSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, ConstantRange(32, /* isFullSet = */ true),OBO::NoSignedWrap);EXPECT_TRUE(NSWForAllValues.isSingleElement() &&NSWForAllValues.getSingleElement()->isMinValue());NSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, ConstantRange(32, /* isFullSet = */ true),OBO::NoSignedWrap);EXPECT_TRUE(NSWForAllValues.isSingleElement() &&NSWForAllValues.getSingleElement()->isMaxValue());auto NUWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, ConstantRange(32, /* isFullSet = */ true),OBO::NoUnsignedWrap);EXPECT_TRUE(NUWForAllValues.isSingleElement() &&NUWForAllValues.getSingleElement()->isMinValue());NUWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, ConstantRange(32, /* isFullSet = */ true),OBO::NoUnsignedWrap);EXPECT_TRUE(NUWForAllValues.isSingleElement() &&NUWForAllValues.getSingleElement()->isMaxValue());EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, APInt(32, 0), OBO::NoUnsignedWrap).isFullSet());EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, APInt(32, 0), OBO::NoSignedWrap).isFullSet());EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, APInt(32, 0), OBO::NoUnsignedWrap).isFullSet());EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, APInt(32, 0), OBO::NoSignedWrap).isFullSet());ConstantRange OneToFive(APInt(32, 1), APInt(32, 6));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, OneToFive, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32),APInt::getSignedMaxValue(32) - 4));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, OneToFive, OBO::NoUnsignedWrap),ConstantRange(APInt::getMinValue(32), APInt::getMinValue(32) - 5));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, OneToFive, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32) + 5,APInt::getSignedMinValue(32)));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, OneToFive, OBO::NoUnsignedWrap),ConstantRange(APInt::getMinValue(32) + 5, APInt::getMinValue(32)));ConstantRange MinusFiveToMinusTwo(APInt(32, -5), APInt(32, -1));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, MinusFiveToMinusTwo, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32) + 5,APInt::getSignedMinValue(32)));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, MinusFiveToMinusTwo, OBO::NoUnsignedWrap),ConstantRange(APInt(32, 0), APInt(32, 2)));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, MinusFiveToMinusTwo, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32),APInt::getSignedMaxValue(32) - 4));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, MinusFiveToMinusTwo, OBO::NoUnsignedWrap),ConstantRange(APInt::getMaxValue(32) - 1,APInt::getMinValue(32)));ConstantRange MinusOneToOne(APInt(32, -1), APInt(32, 2));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, MinusOneToOne, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32) + 1,APInt::getSignedMinValue(32) - 1));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, MinusOneToOne, OBO::NoUnsignedWrap),ConstantRange(APInt(32, 0), APInt(32, 1)));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, MinusOneToOne, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32) + 1,APInt::getSignedMinValue(32) - 1));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, MinusOneToOne, OBO::NoUnsignedWrap),ConstantRange(APInt::getMaxValue(32),APInt::getMinValue(32)));ConstantRange One(APInt(32, 1), APInt(32, 2));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, One, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32),APInt::getSignedMaxValue(32)));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Add, One, OBO::NoUnsignedWrap),ConstantRange(APInt::getMinValue(32), APInt::getMaxValue(32)));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, One, OBO::NoSignedWrap),ConstantRange(APInt::getSignedMinValue(32) + 1,APInt::getSignedMinValue(32)));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Sub, One, OBO::NoUnsignedWrap),ConstantRange(APInt::getMinValue(32) + 1, APInt::getMinValue(32)));ConstantRange OneLessThanBitWidth(APInt(32, 0), APInt(32, 31) + 1);ConstantRange UpToBitWidth(APInt(32, 0), APInt(32, 32) + 1);EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, UpToBitWidth, OBO::NoUnsignedWrap),ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, OneLessThanBitWidth, OBO::NoUnsignedWrap));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap),ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, OneLessThanBitWidth, OBO::NoSignedWrap));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, UpToBitWidth, OBO::NoUnsignedWrap),ConstantRange(APInt(32, 0), APInt(32, 1) + 1));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap),ConstantRange(APInt(32, -1), APInt(32, 0) + 1));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, ConstantRange::getFull(32), OBO::NoUnsignedWrap),ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, OneLessThanBitWidth, OBO::NoUnsignedWrap));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, ConstantRange::getFull(32), OBO::NoSignedWrap),ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, OneLessThanBitWidth, OBO::NoSignedWrap));ConstantRange IllegalShAmt(APInt(32, 32), APInt(32, 0) + 1);EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, IllegalShAmt, OBO::NoUnsignedWrap),ConstantRange::getFull(32));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, IllegalShAmt, OBO::NoSignedWrap),ConstantRange::getFull(32));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1),OBO::NoUnsignedWrap),ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1),OBO::NoUnsignedWrap));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1),OBO::NoSignedWrap),ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1),OBO::NoSignedWrap));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl,ConstantRange(APInt(32, -32), APInt(32, 16) + 1),OBO::NoUnsignedWrap),ConstantRange(APInt(32, 0), APInt(32, 65535) + 1));EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(Instruction::Shl,ConstantRange(APInt(32, -32), APInt(32, 16) + 1),OBO::NoSignedWrap),ConstantRange(APInt(32, -32768), APInt(32, 32767) + 1));}template<typename Fn>void TestNoWrapRegionExhaustive(Instruction::BinaryOps BinOp,unsigned NoWrapKind, Fn OverflowFn) {unsigned Bits = 5;EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {if (CR.isEmptySet())return;if (Instruction::isShift(BinOp) && CR.getUnsignedMax().uge(Bits))return;ConstantRange NoWrap =ConstantRange::makeGuaranteedNoWrapRegion(BinOp, CR, NoWrapKind);EnumerateAPInts(Bits, [&](const APInt &N1) {bool NoOverflow = true;bool Overflow = true;ForeachNumInConstantRange(CR, [&](const APInt &N2) {if (OverflowFn(N1, N2))NoOverflow = false;elseOverflow = false;});EXPECT_EQ(NoOverflow, NoWrap.contains(N1));// The no-wrap range is exact for single-element ranges.if (CR.isSingleElement()) {EXPECT_EQ(Overflow, !NoWrap.contains(N1));}});});}// Show that makeGuaranteedNoWrapRegion() is maximal, and for single-element// ranges also exact.TEST(ConstantRange, NoWrapRegionExhaustive) {TestNoWrapRegionExhaustive(Instruction::Add, OverflowingBinaryOperator::NoUnsignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.uadd_ov(N2, Overflow);return Overflow;});TestNoWrapRegionExhaustive(Instruction::Add, OverflowingBinaryOperator::NoSignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.sadd_ov(N2, Overflow);return Overflow;});TestNoWrapRegionExhaustive(Instruction::Sub, OverflowingBinaryOperator::NoUnsignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.usub_ov(N2, Overflow);return Overflow;});TestNoWrapRegionExhaustive(Instruction::Sub, OverflowingBinaryOperator::NoSignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.ssub_ov(N2, Overflow);return Overflow;});TestNoWrapRegionExhaustive(Instruction::Mul, OverflowingBinaryOperator::NoUnsignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.umul_ov(N2, Overflow);return Overflow;});TestNoWrapRegionExhaustive(Instruction::Mul, OverflowingBinaryOperator::NoSignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.smul_ov(N2, Overflow);return Overflow;});TestNoWrapRegionExhaustive(Instruction::Shl,OverflowingBinaryOperator::NoUnsignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void)N1.ushl_ov(N2, Overflow);return Overflow;});TestNoWrapRegionExhaustive(Instruction::Shl,OverflowingBinaryOperator::NoSignedWrap,[](const APInt &N1, const APInt &N2) {bool Overflow;(void)N1.sshl_ov(N2, Overflow);return Overflow;});}TEST(ConstantRange, GetEquivalentICmp) {APInt RHS;CmpInst::Predicate Pred;EXPECT_TRUE(ConstantRange(APInt::getMinValue(32), APInt(32, 100)).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_ULT);EXPECT_EQ(RHS, APInt(32, 100));EXPECT_TRUE(ConstantRange(APInt::getSignedMinValue(32), APInt(32, 100)).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_SLT);EXPECT_EQ(RHS, APInt(32, 100));EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getMinValue(32)).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_UGE);EXPECT_EQ(RHS, APInt(32, 100));EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getSignedMinValue(32)).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_SGE);EXPECT_EQ(RHS, APInt(32, 100));EXPECT_TRUE(ConstantRange(32, /*isFullSet=*/true).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_UGE);EXPECT_EQ(RHS, APInt(32, 0));EXPECT_TRUE(ConstantRange(32, /*isFullSet=*/false).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_ULT);EXPECT_EQ(RHS, APInt(32, 0));EXPECT_FALSE(ConstantRange(APInt(32, 100), APInt(32, 200)).getEquivalentICmp(Pred, RHS));EXPECT_FALSE(ConstantRange(APInt::getSignedMinValue(32) - APInt(32, 100),APInt::getSignedMinValue(32) + APInt(32, 100)).getEquivalentICmp(Pred, RHS));EXPECT_FALSE(ConstantRange(APInt::getMinValue(32) - APInt(32, 100),APInt::getMinValue(32) + APInt(32, 100)).getEquivalentICmp(Pred, RHS));EXPECT_TRUE(ConstantRange(APInt(32, 100)).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_EQ);EXPECT_EQ(RHS, APInt(32, 100));EXPECT_TRUE(ConstantRange(APInt(32, 100)).inverse().getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_NE);EXPECT_EQ(RHS, APInt(32, 100));EXPECT_TRUE(ConstantRange(APInt(512, 100)).inverse().getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_NE);EXPECT_EQ(RHS, APInt(512, 100));// NB! It would be correct for the following four calls to getEquivalentICmp// to return ordered predicates like CmpInst::ICMP_ULT or CmpInst::ICMP_UGT.// However, that's not the case today.EXPECT_TRUE(ConstantRange(APInt(32, 0)).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_EQ);EXPECT_EQ(RHS, APInt(32, 0));EXPECT_TRUE(ConstantRange(APInt(32, 0)).inverse().getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_NE);EXPECT_EQ(RHS, APInt(32, 0));EXPECT_TRUE(ConstantRange(APInt(32, -1)).getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_EQ);EXPECT_EQ(RHS, APInt(32, -1));EXPECT_TRUE(ConstantRange(APInt(32, -1)).inverse().getEquivalentICmp(Pred, RHS));EXPECT_EQ(Pred, CmpInst::ICMP_NE);EXPECT_EQ(RHS, APInt(32, -1));unsigned Bits = 4;EnumerateConstantRanges(Bits, [Bits](const ConstantRange &CR) {CmpInst::Predicate Pred;APInt RHS, Offset;CR.getEquivalentICmp(Pred, RHS, Offset);EnumerateAPInts(Bits, [&](const APInt &N) {bool Result = ICmpInst::compare(N + Offset, RHS, Pred);EXPECT_EQ(CR.contains(N), Result);});if (CR.getEquivalentICmp(Pred, RHS)) {EnumerateAPInts(Bits, [&](const APInt &N) {bool Result = ICmpInst::compare(N, RHS, Pred);EXPECT_EQ(CR.contains(N), Result);});}});}#define EXPECT_MAY_OVERFLOW(op) \EXPECT_EQ(ConstantRange::OverflowResult::MayOverflow, (op))#define EXPECT_ALWAYS_OVERFLOWS_LOW(op) \EXPECT_EQ(ConstantRange::OverflowResult::AlwaysOverflowsLow, (op))#define EXPECT_ALWAYS_OVERFLOWS_HIGH(op) \EXPECT_EQ(ConstantRange::OverflowResult::AlwaysOverflowsHigh, (op))#define EXPECT_NEVER_OVERFLOWS(op) \EXPECT_EQ(ConstantRange::OverflowResult::NeverOverflows, (op))TEST_F(ConstantRangeTest, UnsignedAddOverflow) {// Ill-defined - may overflow is a conservative result.EXPECT_MAY_OVERFLOW(Some.unsignedAddMayOverflow(Empty));EXPECT_MAY_OVERFLOW(Empty.unsignedAddMayOverflow(Some));// Never overflow despite one full/wrap set.ConstantRange Zero(APInt::getZero(16));EXPECT_NEVER_OVERFLOWS(Full.unsignedAddMayOverflow(Zero));EXPECT_NEVER_OVERFLOWS(Wrap.unsignedAddMayOverflow(Zero));EXPECT_NEVER_OVERFLOWS(Zero.unsignedAddMayOverflow(Full));EXPECT_NEVER_OVERFLOWS(Zero.unsignedAddMayOverflow(Wrap));// But usually full/wrap always may overflow.EXPECT_MAY_OVERFLOW(Full.unsignedAddMayOverflow(One));EXPECT_MAY_OVERFLOW(Wrap.unsignedAddMayOverflow(One));EXPECT_MAY_OVERFLOW(One.unsignedAddMayOverflow(Full));EXPECT_MAY_OVERFLOW(One.unsignedAddMayOverflow(Wrap));ConstantRange A(APInt(16, 0xfd00), APInt(16, 0xfe00));ConstantRange B1(APInt(16, 0x0100), APInt(16, 0x0201));ConstantRange B2(APInt(16, 0x0100), APInt(16, 0x0202));EXPECT_NEVER_OVERFLOWS(A.unsignedAddMayOverflow(B1));EXPECT_MAY_OVERFLOW(A.unsignedAddMayOverflow(B2));EXPECT_NEVER_OVERFLOWS(B1.unsignedAddMayOverflow(A));EXPECT_MAY_OVERFLOW(B2.unsignedAddMayOverflow(A));ConstantRange C1(APInt(16, 0x0299), APInt(16, 0x0400));ConstantRange C2(APInt(16, 0x0300), APInt(16, 0x0400));EXPECT_MAY_OVERFLOW(A.unsignedAddMayOverflow(C1));EXPECT_ALWAYS_OVERFLOWS_HIGH(A.unsignedAddMayOverflow(C2));EXPECT_MAY_OVERFLOW(C1.unsignedAddMayOverflow(A));EXPECT_ALWAYS_OVERFLOWS_HIGH(C2.unsignedAddMayOverflow(A));}TEST_F(ConstantRangeTest, UnsignedSubOverflow) {// Ill-defined - may overflow is a conservative result.EXPECT_MAY_OVERFLOW(Some.unsignedSubMayOverflow(Empty));EXPECT_MAY_OVERFLOW(Empty.unsignedSubMayOverflow(Some));// Never overflow despite one full/wrap set.ConstantRange Zero(APInt::getZero(16));ConstantRange Max(APInt::getAllOnes(16));EXPECT_NEVER_OVERFLOWS(Full.unsignedSubMayOverflow(Zero));EXPECT_NEVER_OVERFLOWS(Wrap.unsignedSubMayOverflow(Zero));EXPECT_NEVER_OVERFLOWS(Max.unsignedSubMayOverflow(Full));EXPECT_NEVER_OVERFLOWS(Max.unsignedSubMayOverflow(Wrap));// But usually full/wrap always may overflow.EXPECT_MAY_OVERFLOW(Full.unsignedSubMayOverflow(One));EXPECT_MAY_OVERFLOW(Wrap.unsignedSubMayOverflow(One));EXPECT_MAY_OVERFLOW(One.unsignedSubMayOverflow(Full));EXPECT_MAY_OVERFLOW(One.unsignedSubMayOverflow(Wrap));ConstantRange A(APInt(16, 0x0000), APInt(16, 0x0100));ConstantRange B(APInt(16, 0x0100), APInt(16, 0x0200));EXPECT_NEVER_OVERFLOWS(B.unsignedSubMayOverflow(A));EXPECT_ALWAYS_OVERFLOWS_LOW(A.unsignedSubMayOverflow(B));ConstantRange A1(APInt(16, 0x0000), APInt(16, 0x0101));ConstantRange B1(APInt(16, 0x0100), APInt(16, 0x0201));EXPECT_NEVER_OVERFLOWS(B1.unsignedSubMayOverflow(A1));EXPECT_MAY_OVERFLOW(A1.unsignedSubMayOverflow(B1));ConstantRange A2(APInt(16, 0x0000), APInt(16, 0x0102));ConstantRange B2(APInt(16, 0x0100), APInt(16, 0x0202));EXPECT_MAY_OVERFLOW(B2.unsignedSubMayOverflow(A2));EXPECT_MAY_OVERFLOW(A2.unsignedSubMayOverflow(B2));}TEST_F(ConstantRangeTest, SignedAddOverflow) {// Ill-defined - may overflow is a conservative result.EXPECT_MAY_OVERFLOW(Some.signedAddMayOverflow(Empty));EXPECT_MAY_OVERFLOW(Empty.signedAddMayOverflow(Some));// Never overflow despite one full/wrap set.ConstantRange Zero(APInt::getZero(16));EXPECT_NEVER_OVERFLOWS(Full.signedAddMayOverflow(Zero));EXPECT_NEVER_OVERFLOWS(Wrap.signedAddMayOverflow(Zero));EXPECT_NEVER_OVERFLOWS(Zero.signedAddMayOverflow(Full));EXPECT_NEVER_OVERFLOWS(Zero.signedAddMayOverflow(Wrap));// But usually full/wrap always may overflow.EXPECT_MAY_OVERFLOW(Full.signedAddMayOverflow(One));EXPECT_MAY_OVERFLOW(Wrap.signedAddMayOverflow(One));EXPECT_MAY_OVERFLOW(One.signedAddMayOverflow(Full));EXPECT_MAY_OVERFLOW(One.signedAddMayOverflow(Wrap));ConstantRange A(APInt(16, 0x7d00), APInt(16, 0x7e00));ConstantRange B1(APInt(16, 0x0100), APInt(16, 0x0201));ConstantRange B2(APInt(16, 0x0100), APInt(16, 0x0202));EXPECT_NEVER_OVERFLOWS(A.signedAddMayOverflow(B1));EXPECT_MAY_OVERFLOW(A.signedAddMayOverflow(B2));ConstantRange B3(APInt(16, 0x8000), APInt(16, 0x0201));ConstantRange B4(APInt(16, 0x8000), APInt(16, 0x0202));EXPECT_NEVER_OVERFLOWS(A.signedAddMayOverflow(B3));EXPECT_MAY_OVERFLOW(A.signedAddMayOverflow(B4));ConstantRange B5(APInt(16, 0x0299), APInt(16, 0x0400));ConstantRange B6(APInt(16, 0x0300), APInt(16, 0x0400));EXPECT_MAY_OVERFLOW(A.signedAddMayOverflow(B5));EXPECT_ALWAYS_OVERFLOWS_HIGH(A.signedAddMayOverflow(B6));ConstantRange C(APInt(16, 0x8200), APInt(16, 0x8300));ConstantRange D1(APInt(16, 0xfe00), APInt(16, 0xff00));ConstantRange D2(APInt(16, 0xfd99), APInt(16, 0xff00));EXPECT_NEVER_OVERFLOWS(C.signedAddMayOverflow(D1));EXPECT_MAY_OVERFLOW(C.signedAddMayOverflow(D2));ConstantRange D3(APInt(16, 0xfe00), APInt(16, 0x8000));ConstantRange D4(APInt(16, 0xfd99), APInt(16, 0x8000));EXPECT_NEVER_OVERFLOWS(C.signedAddMayOverflow(D3));EXPECT_MAY_OVERFLOW(C.signedAddMayOverflow(D4));ConstantRange D5(APInt(16, 0xfc00), APInt(16, 0xfd02));ConstantRange D6(APInt(16, 0xfc00), APInt(16, 0xfd01));EXPECT_MAY_OVERFLOW(C.signedAddMayOverflow(D5));EXPECT_ALWAYS_OVERFLOWS_LOW(C.signedAddMayOverflow(D6));ConstantRange E(APInt(16, 0xff00), APInt(16, 0x0100));EXPECT_NEVER_OVERFLOWS(E.signedAddMayOverflow(E));ConstantRange F(APInt(16, 0xf000), APInt(16, 0x7000));EXPECT_MAY_OVERFLOW(F.signedAddMayOverflow(F));}TEST_F(ConstantRangeTest, SignedSubOverflow) {// Ill-defined - may overflow is a conservative result.EXPECT_MAY_OVERFLOW(Some.signedSubMayOverflow(Empty));EXPECT_MAY_OVERFLOW(Empty.signedSubMayOverflow(Some));// Never overflow despite one full/wrap set.ConstantRange Zero(APInt::getZero(16));EXPECT_NEVER_OVERFLOWS(Full.signedSubMayOverflow(Zero));EXPECT_NEVER_OVERFLOWS(Wrap.signedSubMayOverflow(Zero));// But usually full/wrap always may overflow.EXPECT_MAY_OVERFLOW(Full.signedSubMayOverflow(One));EXPECT_MAY_OVERFLOW(Wrap.signedSubMayOverflow(One));EXPECT_MAY_OVERFLOW(One.signedSubMayOverflow(Full));EXPECT_MAY_OVERFLOW(One.signedSubMayOverflow(Wrap));ConstantRange A(APInt(16, 0x7d00), APInt(16, 0x7e00));ConstantRange B1(APInt(16, 0xfe00), APInt(16, 0xff00));ConstantRange B2(APInt(16, 0xfd99), APInt(16, 0xff00));EXPECT_NEVER_OVERFLOWS(A.signedSubMayOverflow(B1));EXPECT_MAY_OVERFLOW(A.signedSubMayOverflow(B2));ConstantRange B3(APInt(16, 0xfc00), APInt(16, 0xfd02));ConstantRange B4(APInt(16, 0xfc00), APInt(16, 0xfd01));EXPECT_MAY_OVERFLOW(A.signedSubMayOverflow(B3));EXPECT_ALWAYS_OVERFLOWS_HIGH(A.signedSubMayOverflow(B4));ConstantRange C(APInt(16, 0x8200), APInt(16, 0x8300));ConstantRange D1(APInt(16, 0x0100), APInt(16, 0x0201));ConstantRange D2(APInt(16, 0x0100), APInt(16, 0x0202));EXPECT_NEVER_OVERFLOWS(C.signedSubMayOverflow(D1));EXPECT_MAY_OVERFLOW(C.signedSubMayOverflow(D2));ConstantRange D3(APInt(16, 0x0299), APInt(16, 0x0400));ConstantRange D4(APInt(16, 0x0300), APInt(16, 0x0400));EXPECT_MAY_OVERFLOW(C.signedSubMayOverflow(D3));EXPECT_ALWAYS_OVERFLOWS_LOW(C.signedSubMayOverflow(D4));ConstantRange E(APInt(16, 0xff00), APInt(16, 0x0100));EXPECT_NEVER_OVERFLOWS(E.signedSubMayOverflow(E));ConstantRange F(APInt(16, 0xf000), APInt(16, 0x7001));EXPECT_MAY_OVERFLOW(F.signedSubMayOverflow(F));}template<typename Fn1, typename Fn2>static void TestOverflowExhaustive(Fn1 OverflowFn, Fn2 MayOverflowFn) {// Constant range overflow checks are tested exhaustively on 4-bit numbers.unsigned Bits = 4;EnumerateTwoConstantRanges(Bits, [=](const ConstantRange &CR1,const ConstantRange &CR2) {// Loop over all N1 in CR1 and N2 in CR2 and check whether any of the// operations have overflow / have no overflow.bool RangeHasOverflowLow = false;bool RangeHasOverflowHigh = false;bool RangeHasNoOverflow = false;ForeachNumInConstantRange(CR1, [&](const APInt &N1) {ForeachNumInConstantRange(CR2, [&](const APInt &N2) {bool IsOverflowHigh;if (!OverflowFn(IsOverflowHigh, N1, N2)) {RangeHasNoOverflow = true;return;}if (IsOverflowHigh)RangeHasOverflowHigh = true;elseRangeHasOverflowLow = true;});});ConstantRange::OverflowResult OR = MayOverflowFn(CR1, CR2);switch (OR) {case ConstantRange::OverflowResult::AlwaysOverflowsLow:EXPECT_TRUE(RangeHasOverflowLow);EXPECT_FALSE(RangeHasOverflowHigh);EXPECT_FALSE(RangeHasNoOverflow);break;case ConstantRange::OverflowResult::AlwaysOverflowsHigh:EXPECT_TRUE(RangeHasOverflowHigh);EXPECT_FALSE(RangeHasOverflowLow);EXPECT_FALSE(RangeHasNoOverflow);break;case ConstantRange::OverflowResult::NeverOverflows:EXPECT_FALSE(RangeHasOverflowLow);EXPECT_FALSE(RangeHasOverflowHigh);EXPECT_TRUE(RangeHasNoOverflow);break;case ConstantRange::OverflowResult::MayOverflow:// We return MayOverflow for empty sets as a conservative result,// but of course neither the RangeHasOverflow nor the// RangeHasNoOverflow flags will be set.if (CR1.isEmptySet() || CR2.isEmptySet())break;EXPECT_TRUE(RangeHasOverflowLow || RangeHasOverflowHigh);EXPECT_TRUE(RangeHasNoOverflow);break;}});}TEST_F(ConstantRangeTest, UnsignedAddOverflowExhaustive) {TestOverflowExhaustive([](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.uadd_ov(N2, Overflow);IsOverflowHigh = true;return Overflow;},[](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.unsignedAddMayOverflow(CR2);});}TEST_F(ConstantRangeTest, UnsignedSubOverflowExhaustive) {TestOverflowExhaustive([](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.usub_ov(N2, Overflow);IsOverflowHigh = false;return Overflow;},[](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.unsignedSubMayOverflow(CR2);});}TEST_F(ConstantRangeTest, UnsignedMulOverflowExhaustive) {TestOverflowExhaustive([](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.umul_ov(N2, Overflow);IsOverflowHigh = true;return Overflow;},[](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.unsignedMulMayOverflow(CR2);});}TEST_F(ConstantRangeTest, SignedAddOverflowExhaustive) {TestOverflowExhaustive([](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.sadd_ov(N2, Overflow);IsOverflowHigh = N1.isNonNegative();return Overflow;},[](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.signedAddMayOverflow(CR2);});}TEST_F(ConstantRangeTest, SignedSubOverflowExhaustive) {TestOverflowExhaustive([](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {bool Overflow;(void) N1.ssub_ov(N2, Overflow);IsOverflowHigh = N1.isNonNegative();return Overflow;},[](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.signedSubMayOverflow(CR2);});}TEST_F(ConstantRangeTest, FromKnownBits) {KnownBits Unknown(16);EXPECT_EQ(Full, ConstantRange::fromKnownBits(Unknown, /*signed*/false));EXPECT_EQ(Full, ConstantRange::fromKnownBits(Unknown, /*signed*/true));// .10..01. -> unsigned 01000010 (66) to 11011011 (219)// -> signed 11000010 (194) to 01011011 (91)KnownBits Known(8);Known.Zero = 36;Known.One = 66;ConstantRange Unsigned(APInt(8, 66), APInt(8, 219 + 1));ConstantRange Signed(APInt(8, 194), APInt(8, 91 + 1));EXPECT_EQ(Unsigned, ConstantRange::fromKnownBits(Known, /*signed*/false));EXPECT_EQ(Signed, ConstantRange::fromKnownBits(Known, /*signed*/true));// 1.10.10. -> 10100100 (164) to 11101101 (237)Known.Zero = 18;Known.One = 164;ConstantRange CR1(APInt(8, 164), APInt(8, 237 + 1));EXPECT_EQ(CR1, ConstantRange::fromKnownBits(Known, /*signed*/false));EXPECT_EQ(CR1, ConstantRange::fromKnownBits(Known, /*signed*/true));// 01.0.1.0 -> 01000100 (68) to 01101110 (110)Known.Zero = 145;Known.One = 68;ConstantRange CR2(APInt(8, 68), APInt(8, 110 + 1));EXPECT_EQ(CR2, ConstantRange::fromKnownBits(Known, /*signed*/false));EXPECT_EQ(CR2, ConstantRange::fromKnownBits(Known, /*signed*/true));}TEST_F(ConstantRangeTest, FromKnownBitsExhaustive) {unsigned Bits = 4;unsigned Max = 1 << Bits;KnownBits Known(Bits);for (unsigned Zero = 0; Zero < Max; ++Zero) {for (unsigned One = 0; One < Max; ++One) {Known.Zero = Zero;Known.One = One;if (Known.hasConflict() || Known.isUnknown())continue;SmallBitVector Elems(1 << Bits);for (unsigned N = 0; N < Max; ++N) {APInt Num(Bits, N);if ((Num & Known.Zero) != 0 || (~Num & Known.One) != 0)continue;Elems.set(Num.getZExtValue());}TestRange(ConstantRange::fromKnownBits(Known, false),Elems, PreferSmallestUnsigned, {});TestRange(ConstantRange::fromKnownBits(Known, true),Elems, PreferSmallestSigned, {});}}}TEST_F(ConstantRangeTest, ToKnownBits) {unsigned Bits = 4;EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {KnownBits Known = CR.toKnownBits();KnownBits ExpectedKnown(Bits);ExpectedKnown.Zero.setAllBits();ExpectedKnown.One.setAllBits();ForeachNumInConstantRange(CR, [&](const APInt &N) {ExpectedKnown.One &= N;ExpectedKnown.Zero &= ~N;});// For an empty CR any result would be legal.if (!CR.isEmptySet()) {EXPECT_EQ(ExpectedKnown, Known);}});}TEST_F(ConstantRangeTest, Negative) {// All elements in an empty set (of which there are none) are both negative// and non-negative. Empty & full sets checked explicitly for clarity, but// they are also covered by the exhaustive test below.EXPECT_TRUE(Empty.isAllNegative());EXPECT_TRUE(Empty.isAllNonNegative());EXPECT_FALSE(Full.isAllNegative());EXPECT_FALSE(Full.isAllNonNegative());unsigned Bits = 4;EnumerateConstantRanges(Bits, [](const ConstantRange &CR) {bool AllNegative = true;bool AllNonNegative = true;ForeachNumInConstantRange(CR, [&](const APInt &N) {if (!N.isNegative())AllNegative = false;if (!N.isNonNegative())AllNonNegative = false;});assert((CR.isEmptySet() || !AllNegative || !AllNonNegative) &&"Only empty set can be both all negative and all non-negative");EXPECT_EQ(AllNegative, CR.isAllNegative());EXPECT_EQ(AllNonNegative, CR.isAllNonNegative());});}TEST_F(ConstantRangeTest, UAddSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.uadd_sat(CR2);},[](const APInt &N1, const APInt &N2) {return N1.uadd_sat(N2);},PreferSmallestUnsigned);}TEST_F(ConstantRangeTest, USubSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.usub_sat(CR2);},[](const APInt &N1, const APInt &N2) {return N1.usub_sat(N2);},PreferSmallestUnsigned);}TEST_F(ConstantRangeTest, UMulSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.umul_sat(CR2);},[](const APInt &N1, const APInt &N2) { return N1.umul_sat(N2); },PreferSmallestUnsigned);}TEST_F(ConstantRangeTest, UShlSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.ushl_sat(CR2);},[](const APInt &N1, const APInt &N2) { return N1.ushl_sat(N2); },PreferSmallestUnsigned);}TEST_F(ConstantRangeTest, SAddSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.sadd_sat(CR2);},[](const APInt &N1, const APInt &N2) {return N1.sadd_sat(N2);},PreferSmallestSigned);}TEST_F(ConstantRangeTest, SSubSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.ssub_sat(CR2);},[](const APInt &N1, const APInt &N2) {return N1.ssub_sat(N2);},PreferSmallestSigned);}TEST_F(ConstantRangeTest, SMulSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.smul_sat(CR2);},[](const APInt &N1, const APInt &N2) { return N1.smul_sat(N2); },PreferSmallestSigned);}TEST_F(ConstantRangeTest, SShlSat) {TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.sshl_sat(CR2);},[](const APInt &N1, const APInt &N2) { return N1.sshl_sat(N2); },PreferSmallestSigned);}TEST_F(ConstantRangeTest, Abs) {TestUnaryOpExhaustive([](const ConstantRange &CR) { return CR.abs(); },[](const APInt &N) { return N.abs(); });TestUnaryOpExhaustive([](const ConstantRange &CR) { return CR.abs(/*IntMinIsPoison=*/true); },[](const APInt &N) -> Optional<APInt> {if (N.isMinSignedValue())return None;return N.abs();});}TEST_F(ConstantRangeTest, castOps) {ConstantRange A(APInt(16, 66), APInt(16, 128));ConstantRange FpToI8 = A.castOp(Instruction::FPToSI, 8);EXPECT_EQ(8u, FpToI8.getBitWidth());EXPECT_TRUE(FpToI8.isFullSet());ConstantRange FpToI16 = A.castOp(Instruction::FPToSI, 16);EXPECT_EQ(16u, FpToI16.getBitWidth());EXPECT_EQ(A, FpToI16);ConstantRange FPExtToDouble = A.castOp(Instruction::FPExt, 64);EXPECT_EQ(64u, FPExtToDouble.getBitWidth());EXPECT_TRUE(FPExtToDouble.isFullSet());ConstantRange PtrToInt = A.castOp(Instruction::PtrToInt, 64);EXPECT_EQ(64u, PtrToInt.getBitWidth());EXPECT_TRUE(PtrToInt.isFullSet());ConstantRange IntToPtr = A.castOp(Instruction::IntToPtr, 64);EXPECT_EQ(64u, IntToPtr.getBitWidth());EXPECT_TRUE(IntToPtr.isFullSet());}TEST_F(ConstantRangeTest, binaryAnd) {// Single element ranges.ConstantRange R16(APInt(8, 16));ConstantRange R20(APInt(8, 20));EXPECT_EQ(*R16.binaryAnd(R16).getSingleElement(), APInt(8, 16));EXPECT_EQ(*R16.binaryAnd(R20).getSingleElement(), APInt(8, 16 & 20));ConstantRange R16_32(APInt(8, 16), APInt(8, 32));// 'And' with a high bits mask.ConstantRange R32(APInt(8, 32));EXPECT_TRUE(R16_32.binaryAnd(R32).getSingleElement()->isZero());EXPECT_TRUE(R32.binaryAnd(R16_32).getSingleElement()->isZero());// 'And' with a low bits mask. Handled conservatively for now.ConstantRange R4(APInt(8, 4));ConstantRange R0_5(APInt(8, 0), APInt(8, 5));EXPECT_EQ(R16_32.binaryAnd(R4), R0_5);EXPECT_EQ(R4.binaryAnd(R16_32), R0_5);// Ranges with more than one element. Handled conservatively for now.ConstantRange R0_99(APInt(8, 0), APInt(8, 99));ConstantRange R0_32(APInt(8, 0), APInt(8, 32));EXPECT_EQ(R16_32.binaryAnd(R0_99), R0_32);EXPECT_EQ(R0_99.binaryAnd(R16_32), R0_32);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.binaryAnd(CR2);},[](const APInt &N1, const APInt &N2) { return N1 & N2; }, PreferSmallest,CheckSingleElementsOnly);}TEST_F(ConstantRangeTest, binaryOr) {// Single element ranges.ConstantRange R16(APInt(8, 16));ConstantRange R20(APInt(8, 20));EXPECT_EQ(*R16.binaryOr(R16).getSingleElement(), APInt(8, 16));EXPECT_EQ(*R16.binaryOr(R20).getSingleElement(), APInt(8, 16 | 20));ConstantRange R16_32(APInt(8, 16), APInt(8, 32));// 'Or' with a high bits mask.// KnownBits estimate is important, otherwise the maximum included element// would be 2^8 - 1.ConstantRange R32(APInt(8, 32));ConstantRange R48_64(APInt(8, 48), APInt(8, 64));EXPECT_EQ(R16_32.binaryOr(R32), R48_64);EXPECT_EQ(R32.binaryOr(R16_32), R48_64);// 'Or' with a low bits mask.ConstantRange R4(APInt(8, 4));ConstantRange R0_16(APInt(8, 0), APInt(8, 16));ConstantRange R4_16(APInt(8, 4), APInt(8, 16));EXPECT_EQ(R0_16.binaryOr(R4), R4_16);EXPECT_EQ(R4.binaryOr(R0_16), R4_16);// Ranges with more than one element. Handled conservatively for now.// UMaxUMin estimate is important, otherwise the lower bound would be zero.ConstantRange R0_64(APInt(8, 0), APInt(8, 64));ConstantRange R5_32(APInt(8, 5), APInt(8, 32));ConstantRange R5_64(APInt(8, 5), APInt(8, 64));EXPECT_EQ(R0_64.binaryOr(R5_32), R5_64);EXPECT_EQ(R5_32.binaryOr(R0_64), R5_64);TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.binaryOr(CR2);},[](const APInt &N1, const APInt &N2) { return N1 | N2; }, PreferSmallest,CheckSingleElementsOnly);}TEST_F(ConstantRangeTest, binaryXor) {// Single element ranges.ConstantRange R16(APInt(8, 16));ConstantRange R20(APInt(8, 20));EXPECT_EQ(*R16.binaryXor(R16).getSingleElement(), APInt(8, 0));EXPECT_EQ(*R16.binaryXor(R20).getSingleElement(), APInt(8, 16 ^ 20));// Ranges with more than a single element.ConstantRange R16_35(APInt(8, 16), APInt(8, 35));ConstantRange R0_99(APInt(8, 0), APInt(8, 99));EXPECT_EQ(R16_35.binaryXor(R16_35), ConstantRange(APInt(8, 0), APInt(8, 64)));EXPECT_EQ(R16_35.binaryXor(R0_99), ConstantRange(APInt(8, 0), APInt(8, 128)));EXPECT_EQ(R0_99.binaryXor(R16_35), ConstantRange(APInt(8, 0), APInt(8, 128)));TestBinaryOpExhaustive([](const ConstantRange &CR1, const ConstantRange &CR2) {return CR1.binaryXor(CR2);},[](const APInt &N1, const APInt &N2) {return N1 ^ N2;},PreferSmallest,CheckSingleElementsOnly);}TEST_F(ConstantRangeTest, binaryNot) {TestUnaryOpExhaustive([](const ConstantRange &CR) { return CR.binaryNot(); },[](const APInt &N) { return ~N; },PreferSmallest);TestUnaryOpExhaustive([](const ConstantRange &CR) {return CR.binaryXor(ConstantRange(APInt::getAllOnes(CR.getBitWidth())));},[](const APInt &N) { return ~N; }, PreferSmallest);TestUnaryOpExhaustive([](const ConstantRange &CR) {return ConstantRange(APInt::getAllOnes(CR.getBitWidth())).binaryXor(CR);},[](const APInt &N) { return ~N; }, PreferSmallest);}template <typename T>void testConstantRangeICmpPredEquivalence(ICmpInst::Predicate SrcPred, T Func) {unsigned Bits = 4;EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {ICmpInst::Predicate TgtPred;bool ExpectedEquivalent;std::tie(TgtPred, ExpectedEquivalent) = Func(CR1, CR2);if (TgtPred == CmpInst::Predicate::BAD_ICMP_PREDICATE)return;bool TrulyEquivalent = true;ForeachNumInConstantRange(CR1, [&](const APInt &N1) {if (!TrulyEquivalent)return;ForeachNumInConstantRange(CR2, [&](const APInt &N2) {if (!TrulyEquivalent)return;TrulyEquivalent &= ICmpInst::compare(N1, N2, SrcPred) ==ICmpInst::compare(N1, N2, TgtPred);});});ASSERT_EQ(TrulyEquivalent, ExpectedEquivalent);});}TEST_F(ConstantRangeTest, areInsensitiveToSignednessOfICmpPredicate) {for (auto Pred : ICmpInst::predicates()) {if (ICmpInst::isEquality(Pred))continue;ICmpInst::Predicate FlippedSignednessPred =ICmpInst::getFlippedSignednessPredicate(Pred);testConstantRangeICmpPredEquivalence(Pred, [FlippedSignednessPred](const ConstantRange &CR1,const ConstantRange &CR2) {return std::make_pair(FlippedSignednessPred,ConstantRange::areInsensitiveToSignednessOfICmpPredicate(CR1, CR2));});}}TEST_F(ConstantRangeTest, areInsensitiveToSignednessOfInvertedICmpPredicate) {for (auto Pred : ICmpInst::predicates()) {if (ICmpInst::isEquality(Pred))continue;ICmpInst::Predicate InvertedFlippedSignednessPred =ICmpInst::getInversePredicate(ICmpInst::getFlippedSignednessPredicate(Pred));testConstantRangeICmpPredEquivalence(Pred, [InvertedFlippedSignednessPred](const ConstantRange &CR1,const ConstantRange &CR2) {return std::make_pair(InvertedFlippedSignednessPred,ConstantRange::areInsensitiveToSignednessOfInvertedICmpPredicate(CR1, CR2));});}}TEST_F(ConstantRangeTest, getEquivalentPredWithFlippedSignedness) {for (auto Pred : ICmpInst::predicates()) {if (ICmpInst::isEquality(Pred))continue;testConstantRangeICmpPredEquivalence(Pred, [Pred](const ConstantRange &CR1, const ConstantRange &CR2) {return std::make_pair(ConstantRange::getEquivalentPredWithFlippedSignedness(Pred, CR1,CR2),/*ExpectedEquivalent=*/true);});}}TEST_F(ConstantRangeTest, isSizeLargerThan) {EXPECT_FALSE(Empty.isSizeLargerThan(0));EXPECT_TRUE(Full.isSizeLargerThan(0));EXPECT_TRUE(Full.isSizeLargerThan(65535));EXPECT_FALSE(Full.isSizeLargerThan(65536));EXPECT_TRUE(One.isSizeLargerThan(0));EXPECT_FALSE(One.isSizeLargerThan(1));}} // anonymous namespace
set(LLVM_LINK_COMPONENTSAnalysisAsmParserCoreSupportPassesTransformUtilsScalarOpts)add_llvm_unittest(IRTestsAbstractCallSiteTest.cppAsmWriterTest.cppAttributesTest.cppBasicBlockTest.cppCFGBuilder.cppConstantRangeTest.cppConstantsTest.cppDataLayoutTest.cppDebugInfoTest.cppDebugTypeODRUniquingTest.cppDemandedBitsTest.cppDominatorTreeTest.cppDominatorTreeBatchUpdatesTest.cppFunctionTest.cppPassBuilderCallbacksTest.cppIRBuilderTest.cppInstructionsTest.cppIntrinsicsTest.cppLegacyPassManagerTest.cppMDBuilderTest.cppManglerTest.cppMetadataTest.cppModuleTest.cppPassManagerTest.cppPatternMatch.cppTimePassesTest.cppTypesTest.cppUseTest.cppUserTest.cppValueHandleTest.cppValueMapTest.cppValueTest.cppVectorBuilderTest.cppVectorTypesTest.cppVerifierTest.cppVPIntrinsicTest.cpp)target_link_libraries(IRTests PRIVATE LLVMTestingSupport)
//===- CFGBuilder.h - CFG building and updating utility ----------*- C++ -*-==////// 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////===----------------------------------------------------------------------===///// \file/// CFGBuilders provides utilities fo building and updating CFG for testing/// purposes./////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_CFG_BUILDER_H#define LLVM_UNITTESTS_CFG_BUILDER_H#include "llvm/ADT/DenseMap.h"#include "llvm/ADT/Optional.h"#include "llvm/ADT/StringMap.h"#include "llvm/ADT/StringRef.h"#include "llvm/Support/Debug.h"#include <memory>#include <set>#include <tuple>#include <vector>namespace llvm {class LLVMContext;class Module;class Function;class BasicBlock;class raw_ostream;struct CFGHolder {std::unique_ptr<LLVMContext> Context;std::unique_ptr<Module> M;Function *F;CFGHolder(StringRef ModuleName = "m", StringRef FunctionName = "foo");~CFGHolder(); // Defined in the .cpp file so we can use forward declarations.};/// \brief/// CFGBuilder builds IR with specific CFG, based on the supplied list of arcs./// It's able to apply the provided updates and automatically modify the IR.////// Internally it makes every basic block end with either SwitchInst or with/// UnreachableInst. When all arc to a BB are deleted, the BB remains in the/// function and doesn't get deleted.///class CFGBuilder {public:struct Arc {StringRef From;StringRef To;friend bool operator<(const Arc &LHS, const Arc &RHS) {return std::tie(LHS.From, LHS.To) <std::tie(RHS.From, RHS.To);}};enum class ActionKind { Insert, Delete };struct Update {ActionKind Action;Arc Edge;};CFGBuilder(Function *F, const std::vector<Arc> &InitialArcs,std::vector<Update> Updates);BasicBlock *getOrAddBlock(StringRef BlockName);Optional<Update> getNextUpdate() const;Optional<Update> applyUpdate();void dump(raw_ostream &OS = dbgs()) const;private:void buildCFG(const std::vector<Arc> &Arcs);bool connect(const Arc &A);bool disconnect(const Arc &A);Function *F;unsigned UpdateIdx = 0;StringMap<BasicBlock *> NameToBlock;std::set<Arc> Arcs;std::vector<Update> Updates;};} // namespace llvm#endif
//===- llvm/Testing/Support/CFGBuilder.cpp --------------------------------===////// 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////===----------------------------------------------------------------------===//#include "CFGBuilder.h"#include "llvm/IR/CFG.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/Debug.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#define DEBUG_TYPE "cfg-builder"using namespace llvm;CFGHolder::CFGHolder(StringRef ModuleName, StringRef FunctionName): Context(std::make_unique<LLVMContext>()),M(std::make_unique<Module>(ModuleName, *Context)) {FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Context), {}, false);F = Function::Create(FTy, Function::ExternalLinkage, FunctionName, M.get());}CFGHolder::~CFGHolder() = default;CFGBuilder::CFGBuilder(Function *F, const std::vector<Arc> &InitialArcs,std::vector<Update> Updates): F(F), Updates(std::move(Updates)) {assert(F);buildCFG(InitialArcs);}static void ConnectBlocks(BasicBlock *From, BasicBlock *To) {LLVM_DEBUG(dbgs() << "Creating BB arc " << From->getName() << " -> "<< To->getName() << "\n";dbgs().flush());auto *IntTy = IntegerType::get(From->getContext(), 32);if (isa<UnreachableInst>(From->getTerminator()))From->getTerminator()->eraseFromParent();if (!From->getTerminator()) {IRBuilder<> IRB(From);IRB.CreateSwitch(ConstantInt::get(IntTy, 0), To);return;}SwitchInst *SI = cast<SwitchInst>(From->getTerminator());const auto Last = SI->getNumCases();auto *IntVal = ConstantInt::get(IntTy, Last);SI->addCase(IntVal, To);}static void DisconnectBlocks(BasicBlock *From, BasicBlock *To) {LLVM_DEBUG(dbgs() << "Deleting BB arc " << From->getName() << " -> "<< To->getName() << "\n";dbgs().flush());SwitchInst *SI = cast<SwitchInst>(From->getTerminator());if (SI->getNumCases() == 0) {SI->eraseFromParent();IRBuilder<> IRB(From);IRB.CreateUnreachable();return;}if (SI->getDefaultDest() == To) {auto FirstC = SI->case_begin();SI->setDefaultDest(FirstC->getCaseSuccessor());SI->removeCase(FirstC);return;}for (auto CIt = SI->case_begin(); CIt != SI->case_end(); ++CIt)if (CIt->getCaseSuccessor() == To) {SI->removeCase(CIt);return;}}BasicBlock *CFGBuilder::getOrAddBlock(StringRef BlockName) {auto BIt = NameToBlock.find(BlockName);if (BIt != NameToBlock.end())return BIt->second;auto *BB = BasicBlock::Create(F->getParent()->getContext(), BlockName, F);IRBuilder<> IRB(BB);IRB.CreateUnreachable();NameToBlock[BlockName] = BB;return BB;}bool CFGBuilder::connect(const Arc &A) {BasicBlock *From = getOrAddBlock(A.From);BasicBlock *To = getOrAddBlock(A.To);if (Arcs.count(A) != 0)return false;Arcs.insert(A);ConnectBlocks(From, To);return true;}bool CFGBuilder::disconnect(const Arc &A) {assert(NameToBlock.count(A.From) != 0 && "No block to disconnect (From)");assert(NameToBlock.count(A.To) != 0 && "No block to disconnect (To)");if (Arcs.count(A) == 0)return false;BasicBlock *From = getOrAddBlock(A.From);BasicBlock *To = getOrAddBlock(A.To);Arcs.erase(A);DisconnectBlocks(From, To);return true;}void CFGBuilder::buildCFG(const std::vector<Arc> &NewArcs) {for (const auto &A : NewArcs) {const bool Connected = connect(A);(void)Connected;assert(Connected);}}Optional<CFGBuilder::Update> CFGBuilder::getNextUpdate() const {if (UpdateIdx == Updates.size())return None;return Updates[UpdateIdx];}Optional<CFGBuilder::Update> CFGBuilder::applyUpdate() {if (UpdateIdx == Updates.size())return None;Update NextUpdate = Updates[UpdateIdx++];if (NextUpdate.Action == ActionKind::Insert)connect(NextUpdate.Edge);elsedisconnect(NextUpdate.Edge);return NextUpdate;}void CFGBuilder::dump(raw_ostream &OS) const {OS << "Arcs:\n";size_t i = 0;for (const auto &A : Arcs)OS << " " << i++ << ":\t" << A.From << " -> " << A.To << "\n";OS << "Updates:\n";i = 0;for (const auto &U : Updates) {OS << (i + 1 == UpdateIdx ? "->" : " ") << i<< ((U.Action == ActionKind::Insert) ? "\tIns " : "\tDel ")<< U.Edge.From << " -> " << U.Edge.To << "\n";++i;}}//---- CFGBuilder tests ---------------------------------------------------===//TEST(CFGBuilder, Construction) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"entry", "a"}, {"a", "b"}, {"a", "c"},{"c", "d"}, {"d", "b"}, {"d", "e"},{"d", "f"}, {"e", "f"}};CFGBuilder B(Holder.F, Arcs, {});EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());// d has 3 successors, but one of them if going to be a default caseEXPECT_EQ(DSwitch->getNumCases(), 2U);EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.}TEST(CFGBuilder, Insertions) {CFGHolder Holder;const auto Insert = CFGBuilder::ActionKind::Insert;std::vector<CFGBuilder::Update> Updates = {{Insert, {"entry", "a"}}, {Insert, {"a", "b"}}, {Insert, {"a", "c"}},{Insert, {"c", "d"}}, {Insert, {"d", "b"}}, {Insert, {"d", "e"}},{Insert, {"d", "f"}}, {Insert, {"e", "f"}}};const size_t NumUpdates = Updates.size();CFGBuilder B(Holder.F, {}, Updates);size_t i = 0;while (B.applyUpdate())++i;EXPECT_EQ(i, NumUpdates);EXPECT_TRUE(B.getOrAddBlock("entry") == &Holder.F->getEntryBlock());EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("b")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));auto *DSwitch = cast<SwitchInst>(B.getOrAddBlock("d")->getTerminator());// d has 3 successors, but one of them if going to be a default caseEXPECT_EQ(DSwitch->getNumCases(), 2U);EXPECT_FALSE(B.getNextUpdate()); // No updates to apply.}TEST(CFGBuilder, Deletions) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};const auto Delete = CFGBuilder::ActionKind::Delete;std::vector<CFGBuilder::Update> Updates = {{Delete, {"c", "d"}}, {Delete, {"a", "c"}}, {Delete, {"entry", "a"}},};const size_t NumUpdates = Updates.size();CFGBuilder B(Holder.F, Arcs, Updates);EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));auto UpdateC = B.applyUpdate();EXPECT_TRUE(UpdateC);EXPECT_EQ(UpdateC->Action, CFGBuilder::ActionKind::Delete);EXPECT_EQ(UpdateC->Edge.From, "c");EXPECT_EQ(UpdateC->Edge.To, "d");EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("c")->getTerminator()));size_t i = 1;while (B.applyUpdate())++i;EXPECT_EQ(i, NumUpdates);EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));EXPECT_TRUE(isa<UnreachableInst>(B.getOrAddBlock("entry")->getTerminator()));}TEST(CFGBuilder, Rebuild) {CFGHolder Holder;std::vector<CFGBuilder::Arc> Arcs = {{"entry", "a"}, {"a", "b"}, {"a", "c"}, {"c", "d"}, {"d", "b"}};const auto Insert = CFGBuilder::ActionKind::Insert;const auto Delete = CFGBuilder::ActionKind::Delete;std::vector<CFGBuilder::Update> Updates = {{Delete, {"c", "d"}}, {Delete, {"a", "c"}}, {Delete, {"entry", "a"}},{Insert, {"c", "d"}}, {Insert, {"a", "c"}}, {Insert, {"entry", "a"}},};const size_t NumUpdates = Updates.size();CFGBuilder B(Holder.F, Arcs, Updates);size_t i = 0;while (B.applyUpdate())++i;EXPECT_EQ(i, NumUpdates);EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("entry")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("a")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("c")->getTerminator()));EXPECT_TRUE(isa<SwitchInst>(B.getOrAddBlock("d")->getTerminator()));}static_assert(std::is_trivially_copyable<succ_iterator>::value,"trivially copyable");static_assert(std::is_trivially_copyable<const_succ_iterator>::value,"trivially copyable");static_assert(std::is_trivially_copyable<succ_range>::value,"trivially copyable");static_assert(std::is_trivially_copyable<const_succ_range>::value,"trivially copyable");
//===- llvm/unittest/IR/BasicBlockTest.cpp - BasicBlock unit tests --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/BasicBlock.h"#include "llvm/ADT/STLExtras.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/NoFolder.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gmock/gmock-matchers.h"#include "gtest/gtest.h"#include <memory>namespace llvm {namespace {TEST(BasicBlockTest, PhiRange) {LLVMContext Context;// Create the main block.std::unique_ptr<BasicBlock> BB(BasicBlock::Create(Context));// Create some predecessors of it.std::unique_ptr<BasicBlock> BB1(BasicBlock::Create(Context));BranchInst::Create(BB.get(), BB1.get());std::unique_ptr<BasicBlock> BB2(BasicBlock::Create(Context));BranchInst::Create(BB.get(), BB2.get());// Make sure this doesn't crash if there are no phis.int PhiCount = 0;for (auto &PN : BB->phis()) {(void)PN;PhiCount++;}ASSERT_EQ(PhiCount, 0) << "empty block should have no phis";// Make it a cycle.auto *BI = BranchInst::Create(BB.get(), BB.get());// Now insert some PHI nodes.auto *Int32Ty = Type::getInt32Ty(Context);auto *P1 = PHINode::Create(Int32Ty, /*NumReservedValues*/ 3, "phi.1", BI);auto *P2 = PHINode::Create(Int32Ty, /*NumReservedValues*/ 3, "phi.2", BI);auto *P3 = PHINode::Create(Int32Ty, /*NumReservedValues*/ 3, "phi.3", BI);// Some non-PHI nodes.auto *Sum = BinaryOperator::CreateAdd(P1, P2, "sum", BI);// Now wire up the incoming values that are interesting.P1->addIncoming(P2, BB.get());P2->addIncoming(P1, BB.get());P3->addIncoming(Sum, BB.get());// Finally, let's iterate them, which is the thing we're trying to test.// We'll use this to wire up the rest of the incoming values.for (auto &PN : BB->phis()) {PN.addIncoming(UndefValue::get(Int32Ty), BB1.get());PN.addIncoming(UndefValue::get(Int32Ty), BB2.get());}// Test that we can use const iterators and generally that the iterators// behave like iterators.BasicBlock::const_phi_iterator CI;CI = BB->phis().begin();EXPECT_NE(CI, BB->phis().end());// Test that filtering iterators work with basic blocks.auto isPhi = [](Instruction &I) { return isa<PHINode>(&I); };auto Phis = make_filter_range(*BB, isPhi);auto ReversedPhis = reverse(make_filter_range(*BB, isPhi));EXPECT_EQ(std::distance(Phis.begin(), Phis.end()), 3);EXPECT_EQ(&*Phis.begin(), P1);EXPECT_EQ(std::distance(ReversedPhis.begin(), ReversedPhis.end()), 3);EXPECT_EQ(&*ReversedPhis.begin(), P3);// And iterate a const range.for (const auto &PN : const_cast<const BasicBlock *>(BB.get())->phis()) {EXPECT_EQ(BB.get(), PN.getIncomingBlock(0));EXPECT_EQ(BB1.get(), PN.getIncomingBlock(1));EXPECT_EQ(BB2.get(), PN.getIncomingBlock(2));}}#define CHECK_ITERATORS(Range1, Range2) \EXPECT_EQ(std::distance(Range1.begin(), Range1.end()), \std::distance(Range2.begin(), Range2.end())); \for (auto Pair : zip(Range1, Range2)) \EXPECT_EQ(&std::get<0>(Pair), std::get<1>(Pair));TEST(BasicBlockTest, TestInstructionsWithoutDebug) {LLVMContext Ctx;Module *M = new Module("MyModule", Ctx);Type *ArgTy1[] = {Type::getInt32PtrTy(Ctx)};FunctionType *FT = FunctionType::get(Type::getVoidTy(Ctx), ArgTy1, false);Argument *V = new Argument(Type::getInt32Ty(Ctx));Function *F = Function::Create(FT, Function::ExternalLinkage, "", M);Function *DbgAddr = Intrinsic::getDeclaration(M, Intrinsic::dbg_addr);Function *DbgDeclare = Intrinsic::getDeclaration(M, Intrinsic::dbg_declare);Function *DbgValue = Intrinsic::getDeclaration(M, Intrinsic::dbg_value);Value *DIV = MetadataAsValue::get(Ctx, (Metadata *)nullptr);SmallVector<Value *, 3> Args = {DIV, DIV, DIV};BasicBlock *BB1 = BasicBlock::Create(Ctx, "", F);const BasicBlock *BBConst = BB1;IRBuilder<> Builder1(BB1);AllocaInst *Var = Builder1.CreateAlloca(Builder1.getInt8Ty());Builder1.CreateCall(DbgValue, Args);Instruction *AddInst = cast<Instruction>(Builder1.CreateAdd(V, V));Instruction *MulInst = cast<Instruction>(Builder1.CreateMul(AddInst, V));Builder1.CreateCall(DbgDeclare, Args);Instruction *SubInst = cast<Instruction>(Builder1.CreateSub(MulInst, V));Builder1.CreateCall(DbgAddr, Args);SmallVector<Instruction *, 4> Exp = {Var, AddInst, MulInst, SubInst};CHECK_ITERATORS(BB1->instructionsWithoutDebug(), Exp);CHECK_ITERATORS(BBConst->instructionsWithoutDebug(), Exp);EXPECT_EQ(static_cast<size_t>(BB1->sizeWithoutDebug()), Exp.size());EXPECT_EQ(static_cast<size_t>(BBConst->sizeWithoutDebug()), Exp.size());delete M;delete V;}TEST(BasicBlockTest, ComesBefore) {const char *ModuleString = R"(define i32 @f(i32 %x) {%add = add i32 %x, 42ret i32 %add})";LLVMContext Ctx;SMDiagnostic Err;auto M = parseAssemblyString(ModuleString, Err, Ctx);ASSERT_TRUE(M.get());Function *F = M->getFunction("f");BasicBlock &BB = F->front();BasicBlock::iterator I = BB.begin();Instruction *Add = &*I++;Instruction *Ret = &*I++;// Intentionally duplicated to verify cached and uncached are the same.EXPECT_FALSE(BB.isInstrOrderValid());EXPECT_FALSE(Add->comesBefore(Add));EXPECT_TRUE(BB.isInstrOrderValid());EXPECT_FALSE(Add->comesBefore(Add));BB.invalidateOrders();EXPECT_FALSE(BB.isInstrOrderValid());EXPECT_TRUE(Add->comesBefore(Ret));EXPECT_TRUE(BB.isInstrOrderValid());EXPECT_TRUE(Add->comesBefore(Ret));BB.invalidateOrders();EXPECT_FALSE(Ret->comesBefore(Add));EXPECT_FALSE(Ret->comesBefore(Add));BB.invalidateOrders();EXPECT_FALSE(Ret->comesBefore(Ret));EXPECT_FALSE(Ret->comesBefore(Ret));}TEST(BasicBlockTest, EmptyPhi) {LLVMContext Ctx;Module *M = new Module("MyModule", Ctx);FunctionType *FT = FunctionType::get(Type::getVoidTy(Ctx), {}, false);Function *F = Function::Create(FT, Function::ExternalLinkage, "", M);BasicBlock *BB1 = BasicBlock::Create(Ctx, "", F);ReturnInst::Create(Ctx, BB1);Type *Ty = Type::getInt32PtrTy(Ctx);BasicBlock *BB2 = BasicBlock::Create(Ctx, "", F);PHINode::Create(Ty, 0, "", BB2);ReturnInst::Create(Ctx, BB2);EXPECT_FALSE(verifyModule(*M, &errs()));}class InstrOrderInvalidationTest : public ::testing::Test {protected:void SetUp() override {M.reset(new Module("MyModule", Ctx));Nop = Intrinsic::getDeclaration(M.get(), Intrinsic::donothing);FunctionType *FT = FunctionType::get(Type::getVoidTy(Ctx), {}, false);Function *F = Function::Create(FT, Function::ExternalLinkage, "foo", *M);BB = BasicBlock::Create(Ctx, "entry", F);IRBuilder<> Builder(BB);I1 = Builder.CreateCall(Nop);I2 = Builder.CreateCall(Nop);I3 = Builder.CreateCall(Nop);Ret = Builder.CreateRetVoid();}LLVMContext Ctx;std::unique_ptr<Module> M;Function *Nop = nullptr;BasicBlock *BB = nullptr;Instruction *I1 = nullptr;Instruction *I2 = nullptr;Instruction *I3 = nullptr;Instruction *Ret = nullptr;};TEST_F(InstrOrderInvalidationTest, InsertInvalidation) {EXPECT_FALSE(BB->isInstrOrderValid());EXPECT_TRUE(I1->comesBefore(I2));EXPECT_TRUE(BB->isInstrOrderValid());EXPECT_TRUE(I2->comesBefore(I3));EXPECT_TRUE(I3->comesBefore(Ret));EXPECT_TRUE(BB->isInstrOrderValid());// Invalidate orders.IRBuilder<> Builder(BB, I2->getIterator());Instruction *I1a = Builder.CreateCall(Nop);EXPECT_FALSE(BB->isInstrOrderValid());EXPECT_TRUE(I1->comesBefore(I1a));EXPECT_TRUE(BB->isInstrOrderValid());EXPECT_TRUE(I1a->comesBefore(I2));EXPECT_TRUE(I2->comesBefore(I3));EXPECT_TRUE(I3->comesBefore(Ret));EXPECT_TRUE(BB->isInstrOrderValid());}TEST_F(InstrOrderInvalidationTest, SpliceInvalidation) {EXPECT_TRUE(I1->comesBefore(I2));EXPECT_TRUE(I2->comesBefore(I3));EXPECT_TRUE(I3->comesBefore(Ret));EXPECT_TRUE(BB->isInstrOrderValid());// Use Instruction::moveBefore, which uses splice.I2->moveBefore(I1);EXPECT_FALSE(BB->isInstrOrderValid());EXPECT_TRUE(I2->comesBefore(I1));EXPECT_TRUE(I1->comesBefore(I3));EXPECT_TRUE(I3->comesBefore(Ret));EXPECT_TRUE(BB->isInstrOrderValid());}TEST_F(InstrOrderInvalidationTest, RemoveNoInvalidation) {// Cache the instruction order.EXPECT_FALSE(BB->isInstrOrderValid());EXPECT_TRUE(I1->comesBefore(I2));EXPECT_TRUE(BB->isInstrOrderValid());// Removing does not invalidate instruction order.I2->removeFromParent();I2->deleteValue();I2 = nullptr;EXPECT_TRUE(BB->isInstrOrderValid());EXPECT_TRUE(I1->comesBefore(I3));EXPECT_EQ(std::next(I1->getIterator()), I3->getIterator());}TEST_F(InstrOrderInvalidationTest, EraseNoInvalidation) {// Cache the instruction order.EXPECT_FALSE(BB->isInstrOrderValid());EXPECT_TRUE(I1->comesBefore(I2));EXPECT_TRUE(BB->isInstrOrderValid());// Removing does not invalidate instruction order.I2->eraseFromParent();I2 = nullptr;EXPECT_TRUE(BB->isInstrOrderValid());EXPECT_TRUE(I1->comesBefore(I3));EXPECT_EQ(std::next(I1->getIterator()), I3->getIterator());}} // End anonymous namespace.} // End llvm namespace.
//===- llvm/unittest/IR/AttributesTest.cpp - Attributes unit tests --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/Attributes.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/InstrTypes.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(Attributes, Uniquing) {LLVMContext C;Attribute AttrA = Attribute::get(C, Attribute::AlwaysInline);Attribute AttrB = Attribute::get(C, Attribute::AlwaysInline);EXPECT_EQ(AttrA, AttrB);AttributeList ASs[] = {AttributeList::get(C, 1, Attribute::ZExt),AttributeList::get(C, 2, Attribute::SExt)};AttributeList SetA = AttributeList::get(C, ASs);AttributeList SetB = AttributeList::get(C, ASs);EXPECT_EQ(SetA, SetB);}TEST(Attributes, Ordering) {LLVMContext C;Attribute Align4 = Attribute::get(C, Attribute::Alignment, 4);Attribute Align5 = Attribute::get(C, Attribute::Alignment, 5);Attribute Deref4 = Attribute::get(C, Attribute::Dereferenceable, 4);Attribute Deref5 = Attribute::get(C, Attribute::Dereferenceable, 5);EXPECT_TRUE(Align4 < Align5);EXPECT_TRUE(Align4 < Deref4);EXPECT_TRUE(Align4 < Deref5);EXPECT_TRUE(Align5 < Deref4);Attribute ByVal = Attribute::get(C, Attribute::ByVal, Type::getInt32Ty(C));EXPECT_FALSE(ByVal < Attribute::get(C, Attribute::ZExt));EXPECT_TRUE(ByVal < Align4);EXPECT_FALSE(ByVal < ByVal);AttributeList ASs[] = {AttributeList::get(C, 2, Attribute::ZExt),AttributeList::get(C, 1, Attribute::SExt)};AttributeList SetA = AttributeList::get(C, ASs);AttributeList SetB =SetA.removeParamAttributes(C, 0, ASs[1].getParamAttrs(0));EXPECT_NE(SetA, SetB);}TEST(Attributes, AddAttributes) {LLVMContext C;AttributeList AL;AttrBuilder B(C);B.addAttribute(Attribute::NoReturn);AL = AL.addFnAttributes(C, AttrBuilder(C, AttributeSet::get(C, B)));EXPECT_TRUE(AL.hasFnAttr(Attribute::NoReturn));B.clear();B.addAttribute(Attribute::SExt);AL = AL.addRetAttributes(C, B);EXPECT_TRUE(AL.hasRetAttr(Attribute::SExt));EXPECT_TRUE(AL.hasFnAttr(Attribute::NoReturn));}TEST(Attributes, RemoveAlign) {LLVMContext C;Attribute AlignAttr = Attribute::getWithAlignment(C, Align(8));Attribute StackAlignAttr = Attribute::getWithStackAlignment(C, Align(32));AttrBuilder B_align_readonly(C);B_align_readonly.addAttribute(AlignAttr);B_align_readonly.addAttribute(Attribute::ReadOnly);AttributeMask B_align;B_align.addAttribute(AlignAttr);AttrBuilder B_stackalign_optnone(C);B_stackalign_optnone.addAttribute(StackAlignAttr);B_stackalign_optnone.addAttribute(Attribute::OptimizeNone);AttributeMask B_stackalign;B_stackalign.addAttribute(StackAlignAttr);AttributeSet AS = AttributeSet::get(C, B_align_readonly);EXPECT_TRUE(AS.getAlignment() == MaybeAlign(8));EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly));AS = AS.removeAttribute(C, Attribute::Alignment);EXPECT_FALSE(AS.hasAttribute(Attribute::Alignment));EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly));AS = AttributeSet::get(C, B_align_readonly);AS = AS.removeAttributes(C, B_align);EXPECT_TRUE(AS.getAlignment() == None);EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly));AttributeList AL;AL = AL.addParamAttributes(C, 0, B_align_readonly);AL = AL.addRetAttributes(C, B_stackalign_optnone);EXPECT_TRUE(AL.hasRetAttrs());EXPECT_TRUE(AL.hasRetAttr(Attribute::StackAlignment));EXPECT_TRUE(AL.hasRetAttr(Attribute::OptimizeNone));EXPECT_TRUE(AL.getRetStackAlignment() == MaybeAlign(32));EXPECT_TRUE(AL.hasParamAttrs(0));EXPECT_TRUE(AL.hasParamAttr(0, Attribute::Alignment));EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly));EXPECT_TRUE(AL.getParamAlignment(0) == MaybeAlign(8));AL = AL.removeParamAttribute(C, 0, Attribute::Alignment);EXPECT_FALSE(AL.hasParamAttr(0, Attribute::Alignment));EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly));EXPECT_TRUE(AL.hasRetAttr(Attribute::StackAlignment));EXPECT_TRUE(AL.hasRetAttr(Attribute::OptimizeNone));EXPECT_TRUE(AL.getRetStackAlignment() == MaybeAlign(32));AL = AL.removeRetAttribute(C, Attribute::StackAlignment);EXPECT_FALSE(AL.hasParamAttr(0, Attribute::Alignment));EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly));EXPECT_FALSE(AL.hasRetAttr(Attribute::StackAlignment));EXPECT_TRUE(AL.hasRetAttr(Attribute::OptimizeNone));AttributeList AL2;AL2 = AL2.addParamAttributes(C, 0, B_align_readonly);AL2 = AL2.addRetAttributes(C, B_stackalign_optnone);AL2 = AL2.removeParamAttributes(C, 0, B_align);EXPECT_FALSE(AL2.hasParamAttr(0, Attribute::Alignment));EXPECT_TRUE(AL2.hasParamAttr(0, Attribute::ReadOnly));EXPECT_TRUE(AL2.hasRetAttr(Attribute::StackAlignment));EXPECT_TRUE(AL2.hasRetAttr(Attribute::OptimizeNone));EXPECT_TRUE(AL2.getRetStackAlignment() == MaybeAlign(32));AL2 = AL2.removeRetAttributes(C, B_stackalign);EXPECT_FALSE(AL2.hasParamAttr(0, Attribute::Alignment));EXPECT_TRUE(AL2.hasParamAttr(0, Attribute::ReadOnly));EXPECT_FALSE(AL2.hasRetAttr(Attribute::StackAlignment));EXPECT_TRUE(AL2.hasRetAttr(Attribute::OptimizeNone));}TEST(Attributes, AddMatchingAlignAttr) {LLVMContext C;AttributeList AL;AL = AL.addParamAttribute(C, 0, Attribute::getWithAlignment(C, Align(8)));AL = AL.addParamAttribute(C, 1, Attribute::getWithAlignment(C, Align(32)));EXPECT_EQ(Align(8), AL.getParamAlignment(0));EXPECT_EQ(Align(32), AL.getParamAlignment(1));AttrBuilder B(C);B.addAttribute(Attribute::NonNull);B.addAlignmentAttr(8);AL = AL.addParamAttributes(C, 0, B);EXPECT_EQ(Align(8), AL.getParamAlignment(0));EXPECT_EQ(Align(32), AL.getParamAlignment(1));EXPECT_TRUE(AL.hasParamAttr(0, Attribute::NonNull));}TEST(Attributes, EmptyGet) {LLVMContext C;AttributeList EmptyLists[] = {AttributeList(), AttributeList()};AttributeList AL = AttributeList::get(C, EmptyLists);EXPECT_TRUE(AL.isEmpty());}TEST(Attributes, OverflowGet) {LLVMContext C;std::pair<unsigned, Attribute> Attrs[] = { { AttributeList::ReturnIndex, Attribute::get(C, Attribute::SExt) },{ AttributeList::FunctionIndex, Attribute::get(C, Attribute::ReadOnly) } };AttributeList AL = AttributeList::get(C, Attrs);EXPECT_EQ(2U, AL.getNumAttrSets());}TEST(Attributes, StringRepresentation) {LLVMContext C;StructType *Ty = StructType::create(Type::getInt32Ty(C), "mystruct");// Insufficiently careful printing can result in byval(%mystruct = { i32 })Attribute A = Attribute::getWithByValType(C, Ty);EXPECT_EQ(A.getAsString(), "byval(%mystruct)");A = Attribute::getWithByValType(C, Type::getInt32Ty(C));EXPECT_EQ(A.getAsString(), "byval(i32)");}TEST(Attributes, HasParentContext) {LLVMContext C1, C2;{Attribute Attr1 = Attribute::get(C1, Attribute::AlwaysInline);Attribute Attr2 = Attribute::get(C2, Attribute::AlwaysInline);EXPECT_TRUE(Attr1.hasParentContext(C1));EXPECT_FALSE(Attr1.hasParentContext(C2));EXPECT_FALSE(Attr2.hasParentContext(C1));EXPECT_TRUE(Attr2.hasParentContext(C2));}{AttributeSet AS1 = AttributeSet::get(C1, makeArrayRef(Attribute::get(C1, Attribute::NoReturn)));AttributeSet AS2 = AttributeSet::get(C2, makeArrayRef(Attribute::get(C2, Attribute::NoReturn)));EXPECT_TRUE(AS1.hasParentContext(C1));EXPECT_FALSE(AS1.hasParentContext(C2));EXPECT_FALSE(AS2.hasParentContext(C1));EXPECT_TRUE(AS2.hasParentContext(C2));}{AttributeList AL1 = AttributeList::get(C1, 1, Attribute::ZExt);AttributeList AL2 = AttributeList::get(C2, 1, Attribute::ZExt);EXPECT_TRUE(AL1.hasParentContext(C1));EXPECT_FALSE(AL1.hasParentContext(C2));EXPECT_FALSE(AL2.hasParentContext(C1));EXPECT_TRUE(AL2.hasParentContext(C2));}}TEST(Attributes, AttributeListPrinting) {LLVMContext C;{std::string S;raw_string_ostream OS(S);AttributeList AL;AL.addFnAttribute(C, Attribute::AlwaysInline).print(OS);EXPECT_EQ(S, "AttributeList[\n"" { function => alwaysinline }\n""]\n");}{std::string S;raw_string_ostream OS(S);AttributeList AL;AL.addRetAttribute(C, Attribute::SExt).print(OS);EXPECT_EQ(S, "AttributeList[\n"" { return => signext }\n""]\n");}{std::string S;raw_string_ostream OS(S);AttributeList AL;AL.addParamAttribute(C, 5, Attribute::ZExt).print(OS);EXPECT_EQ(S, "AttributeList[\n"" { arg(5) => zeroext }\n""]\n");}}TEST(Attributes, MismatchedABIAttrs) {const char *IRString = R"IR(declare void @f1(i32* byval(i32))define void @g() {call void @f1(i32* null)ret void}declare void @f2(i32* preallocated(i32))define void @h() {call void @f2(i32* null)ret void}declare void @f3(i32* inalloca(i32))define void @i() {call void @f3(i32* null)ret void})IR";SMDiagnostic Err;LLVMContext Context;std::unique_ptr<Module> M = parseAssemblyString(IRString, Err, Context);ASSERT_TRUE(M);{auto *I = cast<CallBase>(&M->getFunction("g")->getEntryBlock().front());ASSERT_TRUE(I->isByValArgument(0));ASSERT_TRUE(I->getParamByValType(0));}{auto *I = cast<CallBase>(&M->getFunction("h")->getEntryBlock().front());ASSERT_TRUE(I->getParamPreallocatedType(0));}{auto *I = cast<CallBase>(&M->getFunction("i")->getEntryBlock().front());ASSERT_TRUE(I->isInAllocaArgument(0));ASSERT_TRUE(I->getParamInAllocaType(0));}}} // end anonymous namespace
//===- llvm/unittest/IR/AsmWriter.cpp - AsmWriter tests -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/MDBuilder.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(AsmWriterTest, DebugPrintDetachedInstruction) {// PR24852: Ensure that an instruction can be printed even when it// has metadata attached but no parent.LLVMContext Ctx;auto Ty = Type::getInt32Ty(Ctx);auto Undef = UndefValue::get(Ty);std::unique_ptr<BinaryOperator> Add(BinaryOperator::CreateAdd(Undef, Undef));Add->setMetadata("", MDNode::get(Ctx, {ConstantAsMetadata::get(ConstantInt::get(Ty, 1))}));std::string S;raw_string_ostream OS(S);Add->print(OS);std::size_t r = OS.str().find("<badref> = add i32 undef, undef, !<empty");EXPECT_TRUE(r != std::string::npos);}TEST(AsmWriterTest, DebugPrintDetachedArgument) {LLVMContext Ctx;auto Ty = Type::getInt32Ty(Ctx);auto Arg = new Argument(Ty);std::string S;raw_string_ostream OS(S);Arg->print(OS);EXPECT_EQ(S, "i32 <badref>");delete Arg;}TEST(AsmWriterTest, DumpDIExpression) {LLVMContext Ctx;uint64_t Ops[] = {dwarf::DW_OP_constu, 4,dwarf::DW_OP_minus,dwarf::DW_OP_deref,};DIExpression *Expr = DIExpression::get(Ctx, Ops);std::string S;raw_string_ostream OS(S);Expr->print(OS);EXPECT_EQ("!DIExpression(DW_OP_constu, 4, DW_OP_minus, DW_OP_deref)",OS.str());}}
//===----- AbstractCallSiteTest.cpp - AbstractCallSite Unittests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/AsmParser/Parser.h"#include "llvm/IR/AbstractCallSite.h"#include "llvm/IR/Function.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("AbstractCallSiteTests", errs());return Mod;}TEST(AbstractCallSite, CallbackCall) {LLVMContext C;const char *IR ="define void @callback(i8* %X, i32* %A) {\n"" ret void\n""}\n""define void @foo(i32* %A) {\n"" call void (i32, void (i8*, ...)*, ...) @broker(i32 1, void (i8*, ...)* bitcast (void (i8*, i32*)* @callback to void (i8*, ...)*), i32* %A)\n"" ret void\n""}\n""declare !callback !0 void @broker(i32, void (i8*, ...)*, ...)\n""!0 = !{!1}\n""!1 = !{i64 1, i64 -1, i1 true}";std::unique_ptr<Module> M = parseIR(C, IR);ASSERT_TRUE(M);Function *Callback = M->getFunction("callback");ASSERT_NE(Callback, nullptr);const Use *CallbackUse = Callback->getSingleUndroppableUse();ASSERT_NE(CallbackUse, nullptr);AbstractCallSite ACS(CallbackUse);EXPECT_TRUE(ACS);EXPECT_TRUE(ACS.isCallbackCall());EXPECT_TRUE(ACS.isCallee(CallbackUse));EXPECT_EQ(ACS.getCalledFunction(), Callback);}
//===- InjectorIRStrategyTest.cpp - Tests for injector strategy -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringRef.h"#include "llvm/AsmParser/Parser.h"#include "llvm/AsmParser/SlotMapping.h"#include "llvm/FuzzMutate/IRMutator.h"#include "llvm/FuzzMutate/Operations.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static constexpr int Seed = 5;namespace {std::unique_ptr<IRMutator> createInjectorMutator() {std::vector<TypeGetter> Types{Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty,Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;Strategies.push_back(std::make_unique<InjectorIRStrategy>(InjectorIRStrategy::getDefaultOps()));return std::make_unique<IRMutator>(std::move(Types), std::move(Strategies));}std::unique_ptr<IRMutator> createDeleterMutator() {std::vector<TypeGetter> Types{Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty,Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;Strategies.push_back(std::make_unique<InstDeleterIRStrategy>());return std::make_unique<IRMutator>(std::move(Types), std::move(Strategies));}std::unique_ptr<IRMutator> createInstModifierMutator() {std::vector<TypeGetter> Types{Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty,Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;Strategies.push_back(std::make_unique<InstModificationIRStrategy>());return std::make_unique<IRMutator>(std::move(Types), std::move(Strategies));}std::unique_ptr<Module> parseAssembly(const char *Assembly, LLVMContext &Context) {SMDiagnostic Error;std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);std::string ErrMsg;raw_string_ostream OS(ErrMsg);Error.print("", OS);assert(M && !verifyModule(*M, &errs()));return M;}void IterateOnSource(StringRef Source, IRMutator &Mutator) {LLVMContext Ctx;for (int i = 0; i < 10; ++i) {auto M = parseAssembly(Source.data(), Ctx);ASSERT_TRUE(M && !verifyModule(*M, &errs()));Mutator.mutateModule(*M, Seed, Source.size(), Source.size() + 100);EXPECT_TRUE(!verifyModule(*M, &errs()));}}TEST(InjectorIRStrategyTest, EmptyModule) {// Test that we can inject into empty moduleLLVMContext Ctx;auto M = std::make_unique<Module>("M", Ctx);ASSERT_TRUE(M && !verifyModule(*M, &errs()));auto Mutator = createInjectorMutator();ASSERT_TRUE(Mutator);Mutator->mutateModule(*M, Seed, 1, 1);EXPECT_TRUE(!verifyModule(*M, &errs()));}TEST(InstDeleterIRStrategyTest, EmptyFunction) {// Test that we don't crash even if we can't remove from one of the functions.StringRef Source = """define <8 x i32> @func1() {\n""ret <8 x i32> undef\n""}\n""\n""define i32 @func2() {\n""%A9 = alloca i32\n""%L6 = load i32, i32* %A9\n""ret i32 %L6\n""}\n";auto Mutator = createDeleterMutator();ASSERT_TRUE(Mutator);IterateOnSource(Source, *Mutator);}TEST(InstDeleterIRStrategyTest, PhiNodes) {// Test that inst deleter works correctly with the phi nodes.LLVMContext Ctx;StringRef Source = "\n\define i32 @earlyreturncrash(i32 %x) {\n\entry:\n\switch i32 %x, label %sw.epilog [\n\i32 1, label %sw.bb1\n\]\n\\n\sw.bb1:\n\br label %sw.epilog\n\\n\sw.epilog:\n\%a.0 = phi i32 [ 7, %entry ], [ 9, %sw.bb1 ]\n\%b.0 = phi i32 [ 10, %entry ], [ 4, %sw.bb1 ]\n\ret i32 %a.0\n\}";auto Mutator = createDeleterMutator();ASSERT_TRUE(Mutator);IterateOnSource(Source, *Mutator);}static void checkModifyNoUnsignedAndNoSignedWrap(StringRef Opc) {LLVMContext Ctx;std::string Source = std::string("\n\define i32 @test(i32 %x) {\n\%a = ") + Opc.str() +std::string(" i32 %x, 10\n\ret i32 %a\n\}");auto Mutator = createInstModifierMutator();ASSERT_TRUE(Mutator);auto M = parseAssembly(Source.data(), Ctx);auto &F = *M->begin();auto *AddI = &*F.begin()->begin();ASSERT_TRUE(M && !verifyModule(*M, &errs()));bool FoundNUW = false;bool FoundNSW = false;for (int i = 0; i < 100; ++i) {Mutator->mutateModule(*M, Seed + i, Source.size(), Source.size() + 100);EXPECT_TRUE(!verifyModule(*M, &errs()));FoundNUW |= AddI->hasNoUnsignedWrap();FoundNSW |= AddI->hasNoSignedWrap();}// The mutator should have added nuw and nsw during some mutations.EXPECT_TRUE(FoundNUW);EXPECT_TRUE(FoundNSW);}TEST(InstModificationIRStrategyTest, Add) {checkModifyNoUnsignedAndNoSignedWrap("add");}TEST(InstModificationIRStrategyTest, Sub) {checkModifyNoUnsignedAndNoSignedWrap("sub");}TEST(InstModificationIRStrategyTest, Mul) {checkModifyNoUnsignedAndNoSignedWrap("mul");}TEST(InstModificationIRStrategyTest, Shl) {checkModifyNoUnsignedAndNoSignedWrap("shl");}TEST(InstModificationIRStrategyTest, ICmp) {LLVMContext Ctx;StringRef Source = "\n\define i1 @test(i32 %x) {\n\%a = icmp eq i32 %x, 10\n\ret i1 %a\n\}";auto Mutator = createInstModifierMutator();ASSERT_TRUE(Mutator);auto M = parseAssembly(Source.data(), Ctx);auto &F = *M->begin();CmpInst *CI = cast<CmpInst>(&*F.begin()->begin());ASSERT_TRUE(M && !verifyModule(*M, &errs()));bool FoundNE = false;for (int i = 0; i < 100; ++i) {Mutator->mutateModule(*M, Seed + i, Source.size(), Source.size() + 100);EXPECT_TRUE(!verifyModule(*M, &errs()));FoundNE |= CI->getPredicate() == CmpInst::ICMP_NE;}EXPECT_TRUE(FoundNE);}TEST(InstModificationIRStrategyTest, GEP) {LLVMContext Ctx;StringRef Source = "\n\define i32* @test(i32* %ptr) {\n\%gep = getelementptr i32, i32* %ptr, i32 10\n\ret i32* %gep\n\}";auto Mutator = createInstModifierMutator();ASSERT_TRUE(Mutator);auto M = parseAssembly(Source.data(), Ctx);auto &F = *M->begin();GetElementPtrInst *GEP = cast<GetElementPtrInst>(&*F.begin()->begin());ASSERT_TRUE(M && !verifyModule(*M, &errs()));bool FoundInbounds = false;for (int i = 0; i < 100; ++i) {Mutator->mutateModule(*M, Seed + i, Source.size(), Source.size() + 100);EXPECT_TRUE(!verifyModule(*M, &errs()));FoundInbounds |= GEP->isInBounds();}EXPECT_TRUE(FoundInbounds);}}
//===- ReservoirSampler.cpp - Tests for the ReservoirSampler --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/FuzzMutate/Random.h"#include "gtest/gtest.h"#include <random>using namespace llvm;TEST(ReservoirSamplerTest, OneItem) {std::mt19937 Rand;auto Sampler = makeSampler(Rand, 7, 1);ASSERT_FALSE(Sampler.isEmpty());ASSERT_EQ(7, Sampler.getSelection());}TEST(ReservoirSamplerTest, NoWeight) {std::mt19937 Rand;auto Sampler = makeSampler(Rand, 7, 0);ASSERT_TRUE(Sampler.isEmpty());}TEST(ReservoirSamplerTest, Uniform) {std::mt19937 Rand;// Run three chi-squared tests to check that the distribution is reasonably// uniform.std::vector<int> Items = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};int Failures = 0;for (int Run = 0; Run < 3; ++Run) {std::vector<int> Counts(Items.size(), 0);// We need $np_s > 5$ at minimum, but we're better off going a couple of// orders of magnitude larger.int N = Items.size() * 5 * 100;for (int I = 0; I < N; ++I) {auto Sampler = makeSampler(Rand, Items);Counts[Sampler.getSelection()] += 1;}// Knuth. TAOCP Vol. 2, 3.3.1 (8):// $V = \frac{1}{n} \sum_{s=1}^{k} \left(\frac{Y_s^2}{p_s}\right) - n$double Ps = 1.0 / Items.size();double Sum = 0.0;for (int Ys : Counts)Sum += Ys * Ys / Ps;double V = (Sum / N) - N;assert(Items.size() == 10 && "Our chi-squared values assume 10 items");// Since we have 10 items, there are 9 degrees of freedom and the table of// chi-squared values is as follows://// | p=1% | 5% | 25% | 50% | 75% | 95% | 99% |// v=9 | 2.088 | 3.325 | 5.899 | 8.343 | 11.39 | 16.92 | 21.67 |//// Check that we're in the likely range of results.//if (V < 2.088 || V > 21.67)if (V < 2.088 || V > 21.67)++Failures;}EXPECT_LT(Failures, 3) << "Non-uniform distribution?";}
//===- RandomIRBuilderTest.cpp - Tests for injector strategy --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/FuzzMutate/RandomIRBuilder.h"#include "llvm/ADT/StringRef.h"#include "llvm/AsmParser/Parser.h"#include "llvm/AsmParser/SlotMapping.h"#include "llvm/FuzzMutate/IRMutator.h"#include "llvm/FuzzMutate/OpDescriptor.h"#include "llvm/FuzzMutate/Operations.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static constexpr int Seed = 5;namespace {std::unique_ptr<Module> parseAssembly(const char *Assembly, LLVMContext &Context) {SMDiagnostic Error;std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);std::string ErrMsg;raw_string_ostream OS(ErrMsg);Error.print("", OS);assert(M && !verifyModule(*M, &errs()));return M;}TEST(RandomIRBuilderTest, ShuffleVectorIncorrectOperands) {// Test that we don't create load instruction as a source for the shuffle// vector operation.LLVMContext Ctx;const char *Source ="define <2 x i32> @test(<2 x i1> %cond, <2 x i32> %a) {\n"" %A = alloca <2 x i32>\n"" %I = insertelement <2 x i32> %a, i32 1, i32 1\n"" ret <2 x i32> undef\n""}";auto M = parseAssembly(Source, Ctx);fuzzerop::OpDescriptor Descr = fuzzerop::shuffleVectorDescriptor(1);// Empty known types since we ShuffleVector descriptor doesn't care about themRandomIRBuilder IB(Seed, {});// Get first basic block of the first functionFunction &F = *M->begin();BasicBlock &BB = *F.begin();SmallVector<Instruction *, 32> Insts;for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I)Insts.push_back(&*I);// Pick first and second sourcesSmallVector<Value *, 2> Srcs;ASSERT_TRUE(Descr.SourcePreds[0].matches(Srcs, Insts[1]));Srcs.push_back(Insts[1]);ASSERT_TRUE(Descr.SourcePreds[1].matches(Srcs, Insts[1]));Srcs.push_back(Insts[1]);// Create new source. Check that it always matches with the descriptor.// Run some iterations to account for random decisions.for (int i = 0; i < 10; ++i) {Value *LastSrc = IB.newSource(BB, Insts, Srcs, Descr.SourcePreds[2]);ASSERT_TRUE(Descr.SourcePreds[2].matches(Srcs, LastSrc));}}TEST(RandomIRBuilderTest, InsertValueIndexes) {// Check that we will generate correct indexes for the insertvalue operationLLVMContext Ctx;const char *Source ="%T = type {i8, i32, i64}\n""define void @test() {\n"" %A = alloca %T\n"" %L = load %T, %T* %A"" ret void\n""}";auto M = parseAssembly(Source, Ctx);fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(1);std::vector<Type *> Types ={Type::getInt8Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt64Ty(Ctx)};RandomIRBuilder IB(Seed, Types);// Get first basic block of the first functionFunction &F = *M->begin();BasicBlock &BB = *F.begin();// Pick first sourceInstruction *Src = &*std::next(BB.begin());SmallVector<Value *, 2> Srcs(2);ASSERT_TRUE(IVDescr.SourcePreds[0].matches({}, Src));Srcs[0] = Src;// Generate constants for each of the types and check that we pick correct// index for the given typefor (auto *T: Types) {// Loop to account for possible random decisionsfor (int i = 0; i < 10; ++i) {// Create value we want to insert. Only it's type matters.Srcs[1] = ConstantInt::get(T, 5);// Try to pick correct indexValue *Src = IB.findOrCreateSource(BB, &*BB.begin(), Srcs, IVDescr.SourcePreds[2]);ASSERT_TRUE(IVDescr.SourcePreds[2].matches(Srcs, Src));}}}TEST(RandomIRBuilderTest, ShuffleVectorSink) {// Check that we will never use shuffle vector mask as a sink form the// unrelated operation.LLVMContext Ctx;const char *SourceCode ="define void @test(<4 x i32> %a) {\n"" %S1 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n"" %S2 = shufflevector <4 x i32> %a, <4 x i32> %a, <4 x i32> undef\n"" ret void\n""}";auto M = parseAssembly(SourceCode, Ctx);fuzzerop::OpDescriptor IVDescr = fuzzerop::insertValueDescriptor(1);RandomIRBuilder IB(Seed, {});// Get first basic block of the first functionFunction &F = *M->begin();BasicBlock &BB = *F.begin();// Source is %S1Instruction *Source = &*BB.begin();// Sink is %S2SmallVector<Instruction *, 1> Sinks = {&*std::next(BB.begin())};// Loop to account for random decisionsfor (int i = 0; i < 10; ++i) {// Try to connect S1 to S2. We should always create new sink.IB.connectToSink(BB, Sinks, Source);ASSERT_TRUE(!verifyModule(*M, &errs()));}}TEST(RandomIRBuilderTest, InsertValueArray) {// Check that we can generate insertvalue for the vector operationsLLVMContext Ctx;const char *SourceCode ="define void @test() {\n"" %A = alloca [8 x i32]\n"" %L = load [8 x i32], [8 x i32]* %A"" ret void\n""}";auto M = parseAssembly(SourceCode, Ctx);fuzzerop::OpDescriptor Descr = fuzzerop::insertValueDescriptor(1);std::vector<Type *> Types ={Type::getInt8Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt64Ty(Ctx)};RandomIRBuilder IB(Seed, Types);// Get first basic block of the first functionFunction &F = *M->begin();BasicBlock &BB = *F.begin();// Pick first sourceInstruction *Source = &*std::next(BB.begin());ASSERT_TRUE(Descr.SourcePreds[0].matches({}, Source));SmallVector<Value *, 2> Srcs(2);// Check that we can always pick the last two operands.for (int i = 0; i < 10; ++i) {Srcs[0] = Source;Srcs[1] = IB.findOrCreateSource(BB, {Source}, Srcs, Descr.SourcePreds[1]);IB.findOrCreateSource(BB, {}, Srcs, Descr.SourcePreds[2]);}}TEST(RandomIRBuilderTest, Invokes) {// Check that we never generate load or store after invoke instructionLLVMContext Ctx;const char *SourceCode ="declare i32* @f()""declare i32 @personality_function()""define i32* @test() personality i32 ()* @personality_function {\n""entry:\n"" %val = invoke i32* @f()\n"" to label %normal unwind label %exceptional\n""normal:\n"" ret i32* %val\n""exceptional:\n"" %landing_pad4 = landingpad token cleanup\n"" ret i32* undef\n""}";auto M = parseAssembly(SourceCode, Ctx);std::vector<Type *> Types = {Type::getInt8Ty(Ctx)};RandomIRBuilder IB(Seed, Types);// Get first basic block of the test functionFunction &F = *M->getFunction("test");BasicBlock &BB = *F.begin();Instruction *Invoke = &*BB.begin();// Find source but never insert new load after invokefor (int i = 0; i < 10; ++i) {(void)IB.findOrCreateSource(BB, {Invoke}, {}, fuzzerop::anyIntType());ASSERT_TRUE(!verifyModule(*M, &errs()));}}TEST(RandomIRBuilderTest, FirstClassTypes) {// Check that we never insert new source as a load from non first class// or unsized type.LLVMContext Ctx;const char *SourceCode = "%Opaque = type opaque\n""define void @test(i8* %ptr) {\n""entry:\n"" %tmp = bitcast i8* %ptr to i32* (i32*)*\n"" %tmp1 = bitcast i8* %ptr to %Opaque*\n"" ret void\n""}";auto M = parseAssembly(SourceCode, Ctx);std::vector<Type *> Types = {Type::getInt8Ty(Ctx)};RandomIRBuilder IB(Seed, Types);Function &F = *M->getFunction("test");BasicBlock &BB = *F.begin();// Non first class typeInstruction *FuncPtr = &*BB.begin();// Unsized typeInstruction *OpaquePtr = &*std::next(BB.begin());for (int i = 0; i < 10; ++i) {Value *V = IB.findOrCreateSource(BB, {FuncPtr, OpaquePtr});ASSERT_FALSE(isa<LoadInst>(V));}}TEST(RandomIRBuilderTest, SwiftError) {// Check that we never pick swifterror value as a source for operation// other than load, store and call.LLVMContext Ctx;const char *SourceCode = "declare void @use(i8** swifterror %err)""define void @test() {\n""entry:\n"" %err = alloca swifterror i8*, align 8\n"" call void @use(i8** swifterror %err)\n"" ret void\n""}";auto M = parseAssembly(SourceCode, Ctx);std::vector<Type *> Types = {Type::getInt8Ty(Ctx)};RandomIRBuilder IB(Seed, Types);// Get first basic block of the test functionFunction &F = *M->getFunction("test");BasicBlock &BB = *F.begin();Instruction *Alloca = &*BB.begin();fuzzerop::OpDescriptor Descr = fuzzerop::gepDescriptor(1);for (int i = 0; i < 10; ++i) {Value *V = IB.findOrCreateSource(BB, {Alloca}, {}, Descr.SourcePreds[0]);ASSERT_FALSE(isa<AllocaInst>(V));}}}
//===- OperationsTest.cpp - Tests for fuzzer operations -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/FuzzMutate/Operations.h"#include "llvm/AsmParser/Parser.h"#include "llvm/FuzzMutate/OpDescriptor.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <iostream>// Define some pretty printers to help with debugging failures.namespace llvm {void PrintTo(Type *T, ::std::ostream *OS) {raw_os_ostream ROS(*OS);T->print(ROS);}void PrintTo(BasicBlock *BB, ::std::ostream *OS) {raw_os_ostream ROS(*OS);ROS << BB << " (" << BB->getName() << ")";}void PrintTo(Value *V, ::std::ostream *OS) {raw_os_ostream ROS(*OS);ROS << V << " (";V->print(ROS);ROS << ")";}void PrintTo(Constant *C, ::std::ostream *OS) { PrintTo(cast<Value>(C), OS); }} // namespace llvmusing namespace llvm;using testing::AllOf;using testing::AnyOf;using testing::ElementsAre;using testing::Eq;using testing::Ge;using testing::Each;using testing::Truly;using testing::NotNull;using testing::PrintToString;using testing::SizeIs;namespace {std::unique_ptr<Module> parseAssembly(const char *Assembly, LLVMContext &Context) {SMDiagnostic Error;std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);std::string ErrMsg;raw_string_ostream OS(ErrMsg);Error.print("", OS);assert(M && !verifyModule(*M, &errs()));return M;}MATCHER_P(TypesMatch, V, "has type " + PrintToString(V->getType())) {return arg->getType() == V->getType();}MATCHER_P(HasType, T, "") { return arg->getType() == T; }TEST(OperationsTest, SourcePreds) {using namespace llvm::fuzzerop;LLVMContext Ctx;Constant *i1 = ConstantInt::getFalse(Ctx);Constant *i8 = ConstantInt::get(Type::getInt8Ty(Ctx), 3);Constant *i16 = ConstantInt::get(Type::getInt16Ty(Ctx), 1 << 15);Constant *i32 = ConstantInt::get(Type::getInt32Ty(Ctx), 0);Constant *i64 = ConstantInt::get(Type::getInt64Ty(Ctx),std::numeric_limits<uint64_t>::max());Constant *f16 = ConstantFP::getInfinity(Type::getHalfTy(Ctx));Constant *f32 = ConstantFP::get(Type::getFloatTy(Ctx), 0.0);Constant *f64 = ConstantFP::get(Type::getDoubleTy(Ctx), 123.45);Constant *s =ConstantStruct::get(StructType::create(Ctx, "OpaqueStruct"));Constant *a =ConstantArray::get(ArrayType::get(i32->getType(), 2), {i32, i32});Constant *v8i8 = ConstantVector::getSplat(ElementCount::getFixed(8), i8);Constant *v4f16 = ConstantVector::getSplat(ElementCount::getFixed(4), f16);Constant *p0i32 =ConstantPointerNull::get(PointerType::get(i32->getType(), 0));auto OnlyI32 = onlyType(i32->getType());EXPECT_TRUE(OnlyI32.matches({}, i32));EXPECT_FALSE(OnlyI32.matches({}, i64));EXPECT_FALSE(OnlyI32.matches({}, p0i32));EXPECT_FALSE(OnlyI32.matches({}, a));EXPECT_THAT(OnlyI32.generate({}, {}),AllOf(SizeIs(Ge(1u)), Each(TypesMatch(i32))));auto AnyType = anyType();EXPECT_TRUE(AnyType.matches({}, i1));EXPECT_TRUE(AnyType.matches({}, f64));EXPECT_TRUE(AnyType.matches({}, s));EXPECT_TRUE(AnyType.matches({}, v8i8));EXPECT_TRUE(AnyType.matches({}, p0i32));EXPECT_THAT(AnyType.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),Each(AnyOf(TypesMatch(i32), TypesMatch(f16), TypesMatch(v8i8))));auto AnyInt = anyIntType();EXPECT_TRUE(AnyInt.matches({}, i1));EXPECT_TRUE(AnyInt.matches({}, i64));EXPECT_FALSE(AnyInt.matches({}, f32));EXPECT_FALSE(AnyInt.matches({}, v4f16));EXPECT_THAT(AnyInt.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),AllOf(SizeIs(Ge(1u)), Each(TypesMatch(i32))));auto AnyFP = anyFloatType();EXPECT_TRUE(AnyFP.matches({}, f16));EXPECT_TRUE(AnyFP.matches({}, f32));EXPECT_FALSE(AnyFP.matches({}, i16));EXPECT_FALSE(AnyFP.matches({}, p0i32));EXPECT_FALSE(AnyFP.matches({}, v4f16));EXPECT_THAT(AnyFP.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),AllOf(SizeIs(Ge(1u)), Each(TypesMatch(f16))));auto AnyPtr = anyPtrType();EXPECT_TRUE(AnyPtr.matches({}, p0i32));EXPECT_FALSE(AnyPtr.matches({}, i8));EXPECT_FALSE(AnyPtr.matches({}, a));EXPECT_FALSE(AnyPtr.matches({}, v8i8));auto isPointer = [](Value *V) { return V->getType()->isPointerTy(); };EXPECT_THAT(AnyPtr.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),AllOf(SizeIs(Ge(3u)), Each(Truly(isPointer))));auto AnyVec = anyVectorType();EXPECT_TRUE(AnyVec.matches({}, v8i8));EXPECT_TRUE(AnyVec.matches({}, v4f16));EXPECT_FALSE(AnyVec.matches({}, i8));EXPECT_FALSE(AnyVec.matches({}, a));EXPECT_FALSE(AnyVec.matches({}, s));EXPECT_THAT(AnyVec.generate({}, {v8i8->getType()}),ElementsAre(TypesMatch(v8i8)));auto First = matchFirstType();EXPECT_TRUE(First.matches({i8}, i8));EXPECT_TRUE(First.matches({s, a}, s));EXPECT_FALSE(First.matches({f16}, f32));EXPECT_FALSE(First.matches({v4f16, f64}, f64));EXPECT_THAT(First.generate({i8}, {}), Each(TypesMatch(i8)));EXPECT_THAT(First.generate({f16}, {i8->getType()}),Each(TypesMatch(f16)));EXPECT_THAT(First.generate({v8i8, i32}, {}), Each(TypesMatch(v8i8)));}TEST(OperationsTest, SplitBlock) {LLVMContext Ctx;Module M("M", Ctx);Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Ctx), {},/*isVarArg=*/false),GlobalValue::ExternalLinkage, "f", &M);auto SBOp = fuzzerop::splitBlockDescriptor(1);// Create a block with only a return and split it on the return.auto *BB = BasicBlock::Create(Ctx, "BB", F);auto *RI = ReturnInst::Create(Ctx, BB);SBOp.BuilderFunc({UndefValue::get(Type::getInt1Ty(Ctx))}, RI);// We should end up with an unconditional branch from BB to BB1, and the// return ends up in BB1.auto *UncondBr = cast<BranchInst>(BB->getTerminator());ASSERT_TRUE(UncondBr->isUnconditional());auto *BB1 = UncondBr->getSuccessor(0);ASSERT_THAT(RI->getParent(), Eq(BB1));// Now add an instruction to BB1 and split on that.auto *AI = new AllocaInst(Type::getInt8Ty(Ctx), 0, "a", RI);Value *Cond = ConstantInt::getFalse(Ctx);SBOp.BuilderFunc({Cond}, AI);// We should end up with a loop back on BB1 and the instruction we split on// moves to BB2.auto *CondBr = cast<BranchInst>(BB1->getTerminator());EXPECT_THAT(CondBr->getCondition(), Eq(Cond));ASSERT_THAT(CondBr->getNumSuccessors(), Eq(2u));ASSERT_THAT(CondBr->getSuccessor(0), Eq(BB1));auto *BB2 = CondBr->getSuccessor(1);EXPECT_THAT(AI->getParent(), Eq(BB2));EXPECT_THAT(RI->getParent(), Eq(BB2));EXPECT_FALSE(verifyModule(M, &errs()));}TEST(OperationsTest, SplitEHBlock) {// Check that we will not try to branch back to the landingpad block using// regular branch instructionLLVMContext Ctx;const char *SourceCode ="declare i32* @f()""declare i32 @personality_function()""define i32* @test() personality i32 ()* @personality_function {\n""entry:\n"" %val = invoke i32* @f()\n"" to label %normal unwind label %exceptional\n""normal:\n"" ret i32* %val\n""exceptional:\n"" %landing_pad4 = landingpad token cleanup\n"" ret i32* undef\n""}";auto M = parseAssembly(SourceCode, Ctx);// Get the landingpad blockBasicBlock &BB = *std::next(M->getFunction("test")->begin(), 2);fuzzerop::OpDescriptor Descr = fuzzerop::splitBlockDescriptor(1);Descr.BuilderFunc({ConstantInt::getTrue(Ctx)},&*BB.getFirstInsertionPt());ASSERT_TRUE(!verifyModule(*M, &errs()));}TEST(OperationsTest, SplitBlockWithPhis) {LLVMContext Ctx;Type *Int8Ty = Type::getInt8Ty(Ctx);Module M("M", Ctx);Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Ctx), {},/*isVarArg=*/false),GlobalValue::ExternalLinkage, "f", &M);auto SBOp = fuzzerop::splitBlockDescriptor(1);// Create 3 blocks with an if-then branch.auto *BB1 = BasicBlock::Create(Ctx, "BB1", F);auto *BB2 = BasicBlock::Create(Ctx, "BB2", F);auto *BB3 = BasicBlock::Create(Ctx, "BB3", F);BranchInst::Create(BB2, BB3, ConstantInt::getFalse(Ctx), BB1);BranchInst::Create(BB3, BB2);// Set up phi nodes selecting values for the incoming edges.auto *PHI1 = PHINode::Create(Int8Ty, /*NumReservedValues=*/2, "p1", BB3);PHI1->addIncoming(ConstantInt::get(Int8Ty, 0), BB1);PHI1->addIncoming(ConstantInt::get(Int8Ty, 1), BB2);auto *PHI2 = PHINode::Create(Int8Ty, /*NumReservedValues=*/2, "p2", BB3);PHI2->addIncoming(ConstantInt::get(Int8Ty, 1), BB1);PHI2->addIncoming(ConstantInt::get(Int8Ty, 0), BB2);auto *RI = ReturnInst::Create(Ctx, BB3);// Now we split the block with PHI nodes, making sure they're all updated.Value *Cond = ConstantInt::getFalse(Ctx);SBOp.BuilderFunc({Cond}, RI);// Make sure the PHIs are updated with a value for the third incoming edge.EXPECT_THAT(PHI1->getNumIncomingValues(), Eq(3u));EXPECT_THAT(PHI2->getNumIncomingValues(), Eq(3u));EXPECT_FALSE(verifyModule(M, &errs()));}TEST(OperationsTest, GEP) {LLVMContext Ctx;Type *Int8PtrTy = Type::getInt8PtrTy(Ctx);Type *Int32Ty = Type::getInt32Ty(Ctx);Module M("M", Ctx);Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Ctx), {},/*isVarArg=*/false),GlobalValue::ExternalLinkage, "f", &M);auto *BB = BasicBlock::Create(Ctx, "BB", F);auto *RI = ReturnInst::Create(Ctx, BB);auto GEPOp = fuzzerop::gepDescriptor(1);EXPECT_TRUE(GEPOp.SourcePreds[0].matches({}, UndefValue::get(Int8PtrTy)));EXPECT_TRUE(GEPOp.SourcePreds[1].matches({UndefValue::get(Int8PtrTy)},ConstantInt::get(Int32Ty, 0)));GEPOp.BuilderFunc({UndefValue::get(Int8PtrTy), ConstantInt::get(Int32Ty, 0)},RI);EXPECT_FALSE(verifyModule(M, &errs()));}TEST(OperationsTest, GEPPointerOperand) {// Check that we only pick sized pointers for the GEP instructionsLLVMContext Ctx;const char *SourceCode ="declare void @f()\n""define void @test() {\n"" %v = bitcast void ()* @f to i64 (i8 addrspace(4)*)*\n"" %a = alloca i64, i32 10\n"" ret void\n""}";auto M = parseAssembly(SourceCode, Ctx);fuzzerop::OpDescriptor Descr = fuzzerop::gepDescriptor(1);// Get first basic block of the test functionFunction &F = *M->getFunction("test");BasicBlock &BB = *F.begin();// Don't match %vASSERT_FALSE(Descr.SourcePreds[0].matches({}, &*BB.begin()));// Match %aASSERT_TRUE(Descr.SourcePreds[0].matches({}, &*std::next(BB.begin())));}TEST(OperationsTest, ExtractAndInsertValue) {LLVMContext Ctx;Type *Int8PtrTy = Type::getInt8PtrTy(Ctx);Type *Int32Ty = Type::getInt32Ty(Ctx);Type *Int64Ty = Type::getInt64Ty(Ctx);Type *StructTy = StructType::create(Ctx, {Int8PtrTy, Int32Ty});Type *OpaqueTy = StructType::create(Ctx, "OpaqueStruct");Type *ZeroSizedArrayTy = ArrayType::get(Int64Ty, 0);Type *ArrayTy = ArrayType::get(Int64Ty, 4);Type *VectorTy = FixedVectorType::get(Int32Ty, 2);auto EVOp = fuzzerop::extractValueDescriptor(1);auto IVOp = fuzzerop::insertValueDescriptor(1);// Sanity check the source preds.Constant *SVal = UndefValue::get(StructTy);Constant *OVal = UndefValue::get(OpaqueTy);Constant *AVal = UndefValue::get(ArrayTy);Constant *ZAVal = UndefValue::get(ZeroSizedArrayTy);Constant *VVal = UndefValue::get(VectorTy);EXPECT_TRUE(EVOp.SourcePreds[0].matches({}, SVal));EXPECT_FALSE(EVOp.SourcePreds[0].matches({}, OVal));EXPECT_TRUE(EVOp.SourcePreds[0].matches({}, AVal));EXPECT_FALSE(EVOp.SourcePreds[0].matches({}, VVal));EXPECT_TRUE(IVOp.SourcePreds[0].matches({}, SVal));EXPECT_FALSE(IVOp.SourcePreds[0].matches({}, OVal));EXPECT_TRUE(IVOp.SourcePreds[0].matches({}, AVal));EXPECT_FALSE(IVOp.SourcePreds[0].matches({}, VVal));// Don't consider zero sized arrays as viable sourcesEXPECT_FALSE(EVOp.SourcePreds[0].matches({}, ZAVal));EXPECT_FALSE(IVOp.SourcePreds[0].matches({}, ZAVal));// Make sure we're range checking appropriately.EXPECT_TRUE(EVOp.SourcePreds[1].matches({SVal}, ConstantInt::get(Int32Ty, 0)));EXPECT_TRUE(EVOp.SourcePreds[1].matches({SVal}, ConstantInt::get(Int32Ty, 1)));EXPECT_FALSE(EVOp.SourcePreds[1].matches({SVal}, ConstantInt::get(Int32Ty, 2)));EXPECT_FALSE(EVOp.SourcePreds[1].matches({OVal}, ConstantInt::get(Int32Ty, 0)));EXPECT_FALSE(EVOp.SourcePreds[1].matches({OVal}, ConstantInt::get(Int32Ty, 65536)));EXPECT_TRUE(EVOp.SourcePreds[1].matches({AVal}, ConstantInt::get(Int32Ty, 0)));EXPECT_TRUE(EVOp.SourcePreds[1].matches({AVal}, ConstantInt::get(Int32Ty, 3)));EXPECT_FALSE(EVOp.SourcePreds[1].matches({AVal}, ConstantInt::get(Int32Ty, 4)));EXPECT_THAT(EVOp.SourcePreds[1].generate({SVal}, {}),ElementsAre(ConstantInt::get(Int32Ty, 0), ConstantInt::get(Int32Ty, 1)));// InsertValue should accept any type in the struct, but only in positions// where it makes sense.EXPECT_TRUE(IVOp.SourcePreds[1].matches({SVal}, UndefValue::get(Int8PtrTy)));EXPECT_TRUE(IVOp.SourcePreds[1].matches({SVal}, UndefValue::get(Int32Ty)));EXPECT_FALSE(IVOp.SourcePreds[1].matches({SVal}, UndefValue::get(Int64Ty)));EXPECT_FALSE(IVOp.SourcePreds[2].matches({SVal, UndefValue::get(Int32Ty)},ConstantInt::get(Int32Ty, 0)));EXPECT_TRUE(IVOp.SourcePreds[2].matches({SVal, UndefValue::get(Int32Ty)},ConstantInt::get(Int32Ty, 1)));EXPECT_THAT(IVOp.SourcePreds[1].generate({SVal}, {}),Each(AnyOf(HasType(Int32Ty), HasType(Int8PtrTy))));EXPECT_THAT(IVOp.SourcePreds[2].generate({SVal, ConstantInt::get(Int32Ty, 0)}, {}),ElementsAre(ConstantInt::get(Int32Ty, 1)));}}
set(LLVM_LINK_COMPONENTSAsmParserCoreFuzzMutateSupport)add_llvm_unittest(FuzzMutateTestsOperationsTest.cppReservoirSamplerTest.cppStrategiesTest.cppRandomIRBuilderTest.cpp)
//===- llvm/unittest/IR/OpenMPIRParsingTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Frontend/OpenMP/OMPConstants.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::omp;namespace {TEST(OpenMPParsingTest, OpenMPDirectiveKind) {EXPECT_EQ(getOpenMPDirectiveKind("foobar"), OMPD_unknown);EXPECT_EQ(getOpenMPDirectiveKind("for"), OMPD_for);EXPECT_EQ(getOpenMPDirectiveKind("simd"), OMPD_simd);EXPECT_EQ(getOpenMPDirectiveKind("for simd"), OMPD_for_simd);}TEST(OpenMPParsingTest, getOpenMPDirectiveName) {EXPECT_EQ(getOpenMPDirectiveName(OMPD_unknown), "unknown");EXPECT_EQ(getOpenMPDirectiveName(OMPD_for), "for");EXPECT_EQ(getOpenMPDirectiveName(OMPD_simd), "simd");EXPECT_EQ(getOpenMPDirectiveName(OMPD_for_simd), "for simd");}TEST(OpenMPParsingTest, getOpenMPClauseKind) {EXPECT_EQ(getOpenMPClauseKind("foobar"), OMPC_unknown);EXPECT_EQ(getOpenMPClauseKind("schedule"), OMPC_schedule);EXPECT_EQ(getOpenMPClauseKind("if"), OMPC_if);}TEST(OpenMPParsingTest, getOpenMPClauseName) {EXPECT_EQ(getOpenMPClauseName(OMPC_unknown), "unknown");EXPECT_EQ(getOpenMPClauseName(OMPC_schedule), "schedule");EXPECT_EQ(getOpenMPClauseName(OMPC_if), "if");}TEST(OpenMPParsingTest, isAllowedClauseForDirective) {EXPECT_TRUE(isAllowedClauseForDirective(OMPD_for, OMPC_schedule, 30));EXPECT_FALSE(isAllowedClauseForDirective(OMPD_for, OMPC_num_teams, 30));EXPECT_FALSE(isAllowedClauseForDirective(OMPD_for, OMPC_order, 30));EXPECT_FALSE(isAllowedClauseForDirective(OMPD_for, OMPC_order, 45));EXPECT_TRUE(isAllowedClauseForDirective(OMPD_for, OMPC_order, 50));EXPECT_TRUE(isAllowedClauseForDirective(OMPD_for, OMPC_order, 51));}TEST(OpenMPParsingTest, getOrderKind) {EXPECT_EQ(getOrderKind("foobar"), OMP_ORDER_unknown);EXPECT_EQ(getOrderKind("unknown"), OMP_ORDER_unknown);EXPECT_EQ(getOrderKind("concurrent"), OMP_ORDER_concurrent);}TEST(OpenMPParsingTest, getProcBindKind) {EXPECT_EQ(getProcBindKind("foobar"), OMP_PROC_BIND_unknown);EXPECT_EQ(getProcBindKind("master"), OMP_PROC_BIND_master);EXPECT_EQ(getProcBindKind("close"), OMP_PROC_BIND_close);EXPECT_EQ(getProcBindKind("spread"), OMP_PROC_BIND_spread);EXPECT_EQ(getProcBindKind("default"), OMP_PROC_BIND_default);EXPECT_EQ(getProcBindKind("unknown"), OMP_PROC_BIND_unknown);}TEST(OpenMPParsingTest, getScheduleKind) {EXPECT_EQ(getScheduleKind("foobar"), OMP_SCHEDULE_Default);EXPECT_EQ(getScheduleKind("static"), OMP_SCHEDULE_Static);EXPECT_EQ(getScheduleKind("dynamic"), OMP_SCHEDULE_Dynamic);EXPECT_EQ(getScheduleKind("guided"), OMP_SCHEDULE_Guided);EXPECT_EQ(getScheduleKind("auto"), OMP_SCHEDULE_Auto);EXPECT_EQ(getScheduleKind("runtime"), OMP_SCHEDULE_Runtime);EXPECT_EQ(getScheduleKind("default"), OMP_SCHEDULE_Default);}} // namespace
//===- llvm/unittest/IR/OpenMPIRBuilderTest.cpp - OpenMPIRBuilder tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Frontend/OpenMP/OMPConstants.h"#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/Function.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Passes/PassBuilder.h"#include "llvm/Support/Casting.h"#include "llvm/Transforms/Utils/BasicBlockUtils.h"#include "gtest/gtest.h"using namespace llvm;using namespace omp;namespace {/// Create an instruction that uses the values in \p Values. We use "printf"/// just because it is often used for this purpose in test code, but it is never/// executed here.static CallInst *createPrintfCall(IRBuilder<> &Builder, StringRef FormatStr,ArrayRef<Value *> Values) {Module *M = Builder.GetInsertBlock()->getParent()->getParent();GlobalVariable *GV = Builder.CreateGlobalString(FormatStr, "", 0, M);Constant *Zero = ConstantInt::get(Type::getInt32Ty(M->getContext()), 0);Constant *Indices[] = {Zero, Zero};Constant *FormatStrConst =ConstantExpr::getInBoundsGetElementPtr(GV->getValueType(), GV, Indices);Function *PrintfDecl = M->getFunction("printf");if (!PrintfDecl) {GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), true);PrintfDecl = Function::Create(Ty, Linkage, "printf", M);}SmallVector<Value *, 4> Args;Args.push_back(FormatStrConst);Args.append(Values.begin(), Values.end());return Builder.CreateCall(PrintfDecl, Args);}/// Verify that blocks in \p RefOrder are corresponds to the depth-first visit/// order the control flow of \p F.////// This is an easy way to verify the branching structure of the CFG without/// checking every branch instruction individually. For the CFG of a/// CanonicalLoopInfo, the Cond BB's terminating branch's first edge is entering/// the body, i.e. the DFS order corresponds to the execution order with one/// loop iteration.static testing::AssertionResultverifyDFSOrder(Function *F, ArrayRef<BasicBlock *> RefOrder) {ArrayRef<BasicBlock *>::iterator It = RefOrder.begin();ArrayRef<BasicBlock *>::iterator E = RefOrder.end();df_iterator_default_set<BasicBlock *, 16> Visited;auto DFS = llvm::depth_first_ext(&F->getEntryBlock(), Visited);BasicBlock *Prev = nullptr;for (BasicBlock *BB : DFS) {if (It != E && BB == *It) {Prev = *It;++It;}}if (It == E)return testing::AssertionSuccess();if (!Prev)return testing::AssertionFailure()<< "Did not find " << (*It)->getName() << " in control flow";return testing::AssertionFailure()<< "Expected " << Prev->getName() << " before " << (*It)->getName()<< " in control flow";}/// Verify that blocks in \p RefOrder are in the same relative order in the/// linked lists of blocks in \p F. The linked list may contain additional/// blocks in-between.////// While the order in the linked list is not relevant for semantics, keeping/// the order roughly in execution order makes its printout easier to read.static testing::AssertionResultverifyListOrder(Function *F, ArrayRef<BasicBlock *> RefOrder) {ArrayRef<BasicBlock *>::iterator It = RefOrder.begin();ArrayRef<BasicBlock *>::iterator E = RefOrder.end();BasicBlock *Prev = nullptr;for (BasicBlock &BB : *F) {if (It != E && &BB == *It) {Prev = *It;++It;}}if (It == E)return testing::AssertionSuccess();if (!Prev)return testing::AssertionFailure() << "Did not find " << (*It)->getName()<< " in function " << F->getName();return testing::AssertionFailure()<< "Expected " << Prev->getName() << " before " << (*It)->getName()<< " in function " << F->getName();}/// Populate Calls with call instructions calling the function with the given/// FnID from the given function F.static void findCalls(Function *F, omp::RuntimeFunction FnID,OpenMPIRBuilder &OMPBuilder,SmallVectorImpl<CallInst *> &Calls) {Function *Fn = OMPBuilder.getOrCreateRuntimeFunctionPtr(FnID);for (BasicBlock &BB : *F) {for (Instruction &I : BB) {auto *Call = dyn_cast<CallInst>(&I);if (Call && Call->getCalledFunction() == Fn)Calls.push_back(Call);}}}/// Assuming \p F contains only one call to the function with the given \p FnID,/// return that call.static CallInst *findSingleCall(Function *F, omp::RuntimeFunction FnID,OpenMPIRBuilder &OMPBuilder) {SmallVector<CallInst *, 1> Calls;findCalls(F, FnID, OMPBuilder, Calls);EXPECT_EQ(1u, Calls.size());if (Calls.size() != 1)return nullptr;return Calls.front();}static omp::ScheduleKind getSchedKind(omp::OMPScheduleType SchedType) {switch (SchedType & ~omp::OMPScheduleType::ModifierMask) {case omp::OMPScheduleType::BaseDynamicChunked:return omp::OMP_SCHEDULE_Dynamic;case omp::OMPScheduleType::BaseGuidedChunked:return omp::OMP_SCHEDULE_Guided;case omp::OMPScheduleType::BaseAuto:return omp::OMP_SCHEDULE_Auto;case omp::OMPScheduleType::BaseRuntime:return omp::OMP_SCHEDULE_Runtime;default:llvm_unreachable("unknown type for this test");}}class OpenMPIRBuilderTest : public testing::Test {protected:void SetUp() override {Ctx.setOpaquePointers(false); // TODO: Update tests for opaque pointers.M.reset(new Module("MyModule", Ctx));FunctionType *FTy =FunctionType::get(Type::getVoidTy(Ctx), {Type::getInt32Ty(Ctx)},/*isVarArg=*/false);F = Function::Create(FTy, Function::ExternalLinkage, "", M.get());BB = BasicBlock::Create(Ctx, "", F);DIBuilder DIB(*M);auto File = DIB.createFile("test.dbg", "/src", llvm::None,Optional<StringRef>("/src/test.dbg"));auto CU =DIB.createCompileUnit(dwarf::DW_LANG_C, File, "llvm-C", true, "", 0);auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));auto SP = DIB.createFunction(CU, "foo", "", File, 1, Type, 1, DINode::FlagZero,DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized);F->setSubprogram(SP);auto Scope = DIB.createLexicalBlockFile(SP, File, 0);DIB.finalize();DL = DILocation::get(Ctx, 3, 7, Scope);}void TearDown() override {BB = nullptr;M.reset();}/// Create a function with a simple loop that calls printf using the logical/// loop counter for use with tests that need a CanonicalLoopInfo object.CanonicalLoopInfo *buildSingleLoopFunction(DebugLoc DL,OpenMPIRBuilder &OMPBuilder,int UseIVBits,CallInst **Call = nullptr,BasicBlock **BodyCode = nullptr) {OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Value *TripCount = F->getArg(0);Type *IVType = Type::getIntNTy(Builder.getContext(), UseIVBits);Value *CastedTripCount =Builder.CreateZExtOrTrunc(TripCount, IVType, "tripcount");auto LoopBodyGenCB = [&](OpenMPIRBuilder::InsertPointTy CodeGenIP,llvm::Value *LC) {Builder.restoreIP(CodeGenIP);if (BodyCode)*BodyCode = Builder.GetInsertBlock();// Add something that consumes the induction variable to the body.CallInst *CallInst = createPrintfCall(Builder, "%d\\n", {LC});if (Call)*Call = CallInst;};CanonicalLoopInfo *Loop =OMPBuilder.createCanonicalLoop(Loc, LoopBodyGenCB, CastedTripCount);// Finalize the function.Builder.restoreIP(Loop->getAfterIP());Builder.CreateRetVoid();return Loop;}LLVMContext Ctx;std::unique_ptr<Module> M;Function *F;BasicBlock *BB;DebugLoc DL;};class OpenMPIRBuilderTestWithParams: public OpenMPIRBuilderTest,public ::testing::WithParamInterface<omp::OMPScheduleType> {};class OpenMPIRBuilderTestWithIVBits: public OpenMPIRBuilderTest,public ::testing::WithParamInterface<int> {};// Returns the value stored in the given allocation. Returns null if the given// value is not a result of an InstTy instruction, if no value is stored or if// there is more than one store.template <typename InstTy> static Value *findStoredValue(Value *AllocaValue) {Instruction *Inst = dyn_cast<InstTy>(AllocaValue);if (!Inst)return nullptr;StoreInst *Store = nullptr;for (Use &U : Inst->uses()) {if (auto *CandidateStore = dyn_cast<StoreInst>(U.getUser())) {EXPECT_EQ(Store, nullptr);Store = CandidateStore;}}if (!Store)return nullptr;return Store->getValueOperand();}// Returns the value stored in the aggregate argument of an outlined function,// or nullptr if it is not found.static Value *findStoredValueInAggregateAt(LLVMContext &Ctx, Value *Aggregate,unsigned Idx) {GetElementPtrInst *GEPAtIdx = nullptr;// Find GEP instruction at that index.for (User *Usr : Aggregate->users()) {GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Usr);if (!GEP)continue;if (GEP->getOperand(2) != ConstantInt::get(Type::getInt32Ty(Ctx), Idx))continue;EXPECT_EQ(GEPAtIdx, nullptr);GEPAtIdx = GEP;}EXPECT_NE(GEPAtIdx, nullptr);EXPECT_EQ(GEPAtIdx->getNumUses(), 1U);// Find the value stored to the aggregate.StoreInst *StoreToAgg = dyn_cast<StoreInst>(*GEPAtIdx->user_begin());Value *StoredAggValue = StoreToAgg->getValueOperand();Value *StoredValue = nullptr;// Find the value stored to the value stored in the aggregate.for (User *Usr : StoredAggValue->users()) {StoreInst *Store = dyn_cast<StoreInst>(Usr);if (!Store)continue;if (Store->getPointerOperand() != StoredAggValue)continue;EXPECT_EQ(StoredValue, nullptr);StoredValue = Store->getValueOperand();}return StoredValue;}// Returns the aggregate that the value is originating from.static Value *findAggregateFromValue(Value *V) {// Expects a load instruction that loads from the aggregate.LoadInst *Load = dyn_cast<LoadInst>(V);EXPECT_NE(Load, nullptr);// Find the GEP instruction used in the load instruction.GetElementPtrInst *GEP =dyn_cast<GetElementPtrInst>(Load->getPointerOperand());EXPECT_NE(GEP, nullptr);// Find the aggregate used in the GEP instruction.Value *Aggregate = GEP->getPointerOperand();return Aggregate;}TEST_F(OpenMPIRBuilderTest, CreateBarrier) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);OMPBuilder.createBarrier({IRBuilder<>::InsertPoint()}, OMPD_for);EXPECT_TRUE(M->global_empty());EXPECT_EQ(M->size(), 1U);EXPECT_EQ(F->size(), 1U);EXPECT_EQ(BB->size(), 0U);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()});OMPBuilder.createBarrier(Loc, OMPD_for);EXPECT_FALSE(M->global_empty());EXPECT_EQ(M->size(), 3U);EXPECT_EQ(F->size(), 1U);EXPECT_EQ(BB->size(), 2U);CallInst *GTID = dyn_cast<CallInst>(&BB->front());EXPECT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory());CallInst *Barrier = dyn_cast<CallInst>(GTID->getNextNode());EXPECT_NE(Barrier, nullptr);EXPECT_EQ(Barrier->arg_size(), 2U);EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_barrier");EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory());EXPECT_EQ(cast<CallInst>(Barrier)->getArgOperand(1), GTID);Builder.CreateUnreachable();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateCancel) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();BasicBlock *CBB = BasicBlock::Create(Ctx, "", F);new UnreachableInst(Ctx, CBB);auto FiniCB = [&](InsertPointTy IP) {ASSERT_NE(IP.getBlock(), nullptr);ASSERT_EQ(IP.getBlock()->end(), IP.getPoint());BranchInst::Create(CBB, IP.getBlock());};OMPBuilder.pushFinalizationCB({FiniCB, OMPD_parallel, true});IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()});auto NewIP = OMPBuilder.createCancel(Loc, nullptr, OMPD_parallel);Builder.restoreIP(NewIP);EXPECT_FALSE(M->global_empty());EXPECT_EQ(M->size(), 4U);EXPECT_EQ(F->size(), 4U);EXPECT_EQ(BB->size(), 4U);CallInst *GTID = dyn_cast<CallInst>(&BB->front());EXPECT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory());CallInst *Cancel = dyn_cast<CallInst>(GTID->getNextNode());EXPECT_NE(Cancel, nullptr);EXPECT_EQ(Cancel->arg_size(), 3U);EXPECT_EQ(Cancel->getCalledFunction()->getName(), "__kmpc_cancel");EXPECT_FALSE(Cancel->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(Cancel->getCalledFunction()->doesNotFreeMemory());EXPECT_EQ(Cancel->getNumUses(), 1U);Instruction *CancelBBTI = Cancel->getParent()->getTerminator();EXPECT_EQ(CancelBBTI->getNumSuccessors(), 2U);EXPECT_EQ(CancelBBTI->getSuccessor(0), NewIP.getBlock());EXPECT_EQ(CancelBBTI->getSuccessor(1)->size(), 3U);CallInst *GTID1 = dyn_cast<CallInst>(&CancelBBTI->getSuccessor(1)->front());EXPECT_NE(GTID1, nullptr);EXPECT_EQ(GTID1->arg_size(), 1U);EXPECT_EQ(GTID1->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID1->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID1->getCalledFunction()->doesNotFreeMemory());CallInst *Barrier = dyn_cast<CallInst>(GTID1->getNextNode());EXPECT_NE(Barrier, nullptr);EXPECT_EQ(Barrier->arg_size(), 2U);EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_cancel_barrier");EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory());EXPECT_EQ(Barrier->getNumUses(), 0U);EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getNumSuccessors(),1U);EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getSuccessor(0), CBB);EXPECT_EQ(cast<CallInst>(Cancel)->getArgOperand(1), GTID);OMPBuilder.popFinalizationCB();Builder.CreateUnreachable();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateCancelIfCond) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();BasicBlock *CBB = BasicBlock::Create(Ctx, "", F);new UnreachableInst(Ctx, CBB);auto FiniCB = [&](InsertPointTy IP) {ASSERT_NE(IP.getBlock(), nullptr);ASSERT_EQ(IP.getBlock()->end(), IP.getPoint());BranchInst::Create(CBB, IP.getBlock());};OMPBuilder.pushFinalizationCB({FiniCB, OMPD_parallel, true});IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()});auto NewIP = OMPBuilder.createCancel(Loc, Builder.getTrue(), OMPD_parallel);Builder.restoreIP(NewIP);EXPECT_FALSE(M->global_empty());EXPECT_EQ(M->size(), 4U);EXPECT_EQ(F->size(), 7U);EXPECT_EQ(BB->size(), 1U);ASSERT_TRUE(isa<BranchInst>(BB->getTerminator()));ASSERT_EQ(BB->getTerminator()->getNumSuccessors(), 2U);BB = BB->getTerminator()->getSuccessor(0);EXPECT_EQ(BB->size(), 4U);CallInst *GTID = dyn_cast<CallInst>(&BB->front());EXPECT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory());CallInst *Cancel = dyn_cast<CallInst>(GTID->getNextNode());EXPECT_NE(Cancel, nullptr);EXPECT_EQ(Cancel->arg_size(), 3U);EXPECT_EQ(Cancel->getCalledFunction()->getName(), "__kmpc_cancel");EXPECT_FALSE(Cancel->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(Cancel->getCalledFunction()->doesNotFreeMemory());EXPECT_EQ(Cancel->getNumUses(), 1U);Instruction *CancelBBTI = Cancel->getParent()->getTerminator();EXPECT_EQ(CancelBBTI->getNumSuccessors(), 2U);EXPECT_EQ(CancelBBTI->getSuccessor(0)->size(), 1U);EXPECT_EQ(CancelBBTI->getSuccessor(0)->getUniqueSuccessor(),NewIP.getBlock());EXPECT_EQ(CancelBBTI->getSuccessor(1)->size(), 3U);CallInst *GTID1 = dyn_cast<CallInst>(&CancelBBTI->getSuccessor(1)->front());EXPECT_NE(GTID1, nullptr);EXPECT_EQ(GTID1->arg_size(), 1U);EXPECT_EQ(GTID1->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID1->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID1->getCalledFunction()->doesNotFreeMemory());CallInst *Barrier = dyn_cast<CallInst>(GTID1->getNextNode());EXPECT_NE(Barrier, nullptr);EXPECT_EQ(Barrier->arg_size(), 2U);EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_cancel_barrier");EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory());EXPECT_EQ(Barrier->getNumUses(), 0U);EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getNumSuccessors(),1U);EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getSuccessor(0), CBB);EXPECT_EQ(cast<CallInst>(Cancel)->getArgOperand(1), GTID);OMPBuilder.popFinalizationCB();Builder.CreateUnreachable();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateCancelBarrier) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();BasicBlock *CBB = BasicBlock::Create(Ctx, "", F);new UnreachableInst(Ctx, CBB);auto FiniCB = [&](InsertPointTy IP) {ASSERT_NE(IP.getBlock(), nullptr);ASSERT_EQ(IP.getBlock()->end(), IP.getPoint());BranchInst::Create(CBB, IP.getBlock());};OMPBuilder.pushFinalizationCB({FiniCB, OMPD_parallel, true});IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()});auto NewIP = OMPBuilder.createBarrier(Loc, OMPD_for);Builder.restoreIP(NewIP);EXPECT_FALSE(M->global_empty());EXPECT_EQ(M->size(), 3U);EXPECT_EQ(F->size(), 4U);EXPECT_EQ(BB->size(), 4U);CallInst *GTID = dyn_cast<CallInst>(&BB->front());EXPECT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory());CallInst *Barrier = dyn_cast<CallInst>(GTID->getNextNode());EXPECT_NE(Barrier, nullptr);EXPECT_EQ(Barrier->arg_size(), 2U);EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_cancel_barrier");EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory());EXPECT_EQ(Barrier->getNumUses(), 1U);Instruction *BarrierBBTI = Barrier->getParent()->getTerminator();EXPECT_EQ(BarrierBBTI->getNumSuccessors(), 2U);EXPECT_EQ(BarrierBBTI->getSuccessor(0), NewIP.getBlock());EXPECT_EQ(BarrierBBTI->getSuccessor(1)->size(), 1U);EXPECT_EQ(BarrierBBTI->getSuccessor(1)->getTerminator()->getNumSuccessors(),1U);EXPECT_EQ(BarrierBBTI->getSuccessor(1)->getTerminator()->getSuccessor(0),CBB);EXPECT_EQ(cast<CallInst>(Barrier)->getArgOperand(1), GTID);OMPBuilder.popFinalizationCB();Builder.CreateUnreachable();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, DbgLoc) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});OMPBuilder.createBarrier(Loc, OMPD_for);CallInst *GTID = dyn_cast<CallInst>(&BB->front());CallInst *Barrier = dyn_cast<CallInst>(GTID->getNextNode());EXPECT_EQ(GTID->getDebugLoc(), DL);EXPECT_EQ(Barrier->getDebugLoc(), DL);EXPECT_TRUE(isa<GlobalVariable>(Barrier->getOperand(0)));if (!isa<GlobalVariable>(Barrier->getOperand(0)))return;GlobalVariable *Ident = cast<GlobalVariable>(Barrier->getOperand(0));EXPECT_TRUE(Ident->hasInitializer());if (!Ident->hasInitializer())return;Constant *Initializer = Ident->getInitializer();EXPECT_TRUE(isa<GlobalVariable>(Initializer->getOperand(4)->stripPointerCasts()));GlobalVariable *SrcStrGlob =cast<GlobalVariable>(Initializer->getOperand(4)->stripPointerCasts());if (!SrcStrGlob)return;EXPECT_TRUE(isa<ConstantDataArray>(SrcStrGlob->getInitializer()));ConstantDataArray *SrcSrc =dyn_cast<ConstantDataArray>(SrcStrGlob->getInitializer());if (!SrcSrc)return;EXPECT_EQ(SrcSrc->getAsCString(), ";/src/test.dbg;foo;3;7;;");}TEST_F(OpenMPIRBuilderTest, ParallelSimple) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "parallel.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI = nullptr;unsigned NumBodiesGenerated = 0;unsigned NumPrivatizedVars = 0;unsigned NumFinalizationPoints = 0;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumBodiesGenerated;Builder.restoreIP(AllocaIP);PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());Builder.CreateStore(F->arg_begin(), PrivAI);Builder.restoreIP(CodeGenIP);Value *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Value *Cmp = Builder.CreateICmpNE(F->arg_begin(), PrivLoad);Instruction *ThenTerm, *ElseTerm;SplitBlockAndInsertIfThenElse(Cmp, CodeGenIP.getBlock()->getTerminator(),&ThenTerm, &ElseTerm);};auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,Value &Orig, Value &Inner,Value *&ReplacementValue) -> InsertPointTy {++NumPrivatizedVars;if (!isa<AllocaInst>(Orig)) {EXPECT_EQ(&Orig, F->arg_begin());ReplacementValue = &Inner;return CodeGenIP;}// Since the original value is an allocation, it has a pointer type and// therefore no additional wrapping should happen.EXPECT_EQ(&Orig, &Inner);// Trivial copy (=firstprivate).Builder.restoreIP(AllocaIP);Type *VTy = ReplacementValue->getType();Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload");ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy");Builder.restoreIP(CodeGenIP);Builder.CreateStore(V, ReplacementValue);return CodeGenIP;};auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; };IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());IRBuilder<>::InsertPoint AfterIP =OMPBuilder.createParallel(Loc, AllocaIP, BodyGenCB, PrivCB, FiniCB,nullptr, nullptr, OMP_PROC_BIND_default, false);EXPECT_EQ(NumBodiesGenerated, 1U);EXPECT_EQ(NumPrivatizedVars, 1U);EXPECT_EQ(NumFinalizationPoints, 1U);Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_NE(PrivAI, nullptr);Function *OutlinedFn = PrivAI->getFunction();EXPECT_NE(F, OutlinedFn);EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_TRUE(OutlinedFn->hasFnAttribute(Attribute::NoUnwind));EXPECT_TRUE(OutlinedFn->hasFnAttribute(Attribute::NoRecurse));EXPECT_TRUE(OutlinedFn->hasParamAttribute(0, Attribute::NoAlias));EXPECT_TRUE(OutlinedFn->hasParamAttribute(1, Attribute::NoAlias));EXPECT_TRUE(OutlinedFn->hasInternalLinkage());EXPECT_EQ(OutlinedFn->arg_size(), 3U);EXPECT_EQ(&OutlinedFn->getEntryBlock(), PrivAI->getParent());EXPECT_EQ(OutlinedFn->getNumUses(), 1U);User *Usr = OutlinedFn->user_back();ASSERT_TRUE(isa<ConstantExpr>(Usr));CallInst *ForkCI = dyn_cast<CallInst>(Usr->user_back());ASSERT_NE(ForkCI, nullptr);EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call");EXPECT_EQ(ForkCI->arg_size(), 4U);EXPECT_TRUE(isa<GlobalVariable>(ForkCI->getArgOperand(0)));EXPECT_EQ(ForkCI->getArgOperand(1),ConstantInt::get(Type::getInt32Ty(Ctx), 1U));EXPECT_EQ(ForkCI->getArgOperand(2), Usr);Value *StoredValue =findStoredValueInAggregateAt(Ctx, ForkCI->getArgOperand(3), 0);EXPECT_EQ(StoredValue, F->arg_begin());}TEST_F(OpenMPIRBuilderTest, ParallelNested) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "parallel.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});unsigned NumInnerBodiesGenerated = 0;unsigned NumOuterBodiesGenerated = 0;unsigned NumFinalizationPoints = 0;auto InnerBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumInnerBodiesGenerated;};auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,Value &Orig, Value &Inner,Value *&ReplacementValue) -> InsertPointTy {// Trivial copy (=firstprivate).Builder.restoreIP(AllocaIP);Type *VTy = ReplacementValue->getType();Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload");ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy");Builder.restoreIP(CodeGenIP);Builder.CreateStore(V, ReplacementValue);return CodeGenIP;};auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; };auto OuterBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumOuterBodiesGenerated;Builder.restoreIP(CodeGenIP);BasicBlock *CGBB = CodeGenIP.getBlock();BasicBlock *NewBB = SplitBlock(CGBB, &*CodeGenIP.getPoint());CGBB->getTerminator()->eraseFromParent();;IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel(InsertPointTy(CGBB, CGBB->end()), AllocaIP, InnerBodyGenCB, PrivCB,FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false);Builder.restoreIP(AfterIP);Builder.CreateBr(NewBB);};IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());IRBuilder<>::InsertPoint AfterIP =OMPBuilder.createParallel(Loc, AllocaIP, OuterBodyGenCB, PrivCB, FiniCB,nullptr, nullptr, OMP_PROC_BIND_default, false);EXPECT_EQ(NumInnerBodiesGenerated, 1U);EXPECT_EQ(NumOuterBodiesGenerated, 1U);EXPECT_EQ(NumFinalizationPoints, 2U);Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_EQ(M->size(), 5U);for (Function &OutlinedFn : *M) {if (F == &OutlinedFn || OutlinedFn.isDeclaration())continue;EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_TRUE(OutlinedFn.hasFnAttribute(Attribute::NoUnwind));EXPECT_TRUE(OutlinedFn.hasFnAttribute(Attribute::NoRecurse));EXPECT_TRUE(OutlinedFn.hasParamAttribute(0, Attribute::NoAlias));EXPECT_TRUE(OutlinedFn.hasParamAttribute(1, Attribute::NoAlias));EXPECT_TRUE(OutlinedFn.hasInternalLinkage());EXPECT_EQ(OutlinedFn.arg_size(), 2U);EXPECT_EQ(OutlinedFn.getNumUses(), 1U);User *Usr = OutlinedFn.user_back();ASSERT_TRUE(isa<ConstantExpr>(Usr));CallInst *ForkCI = dyn_cast<CallInst>(Usr->user_back());ASSERT_NE(ForkCI, nullptr);EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call");EXPECT_EQ(ForkCI->arg_size(), 3U);EXPECT_TRUE(isa<GlobalVariable>(ForkCI->getArgOperand(0)));EXPECT_EQ(ForkCI->getArgOperand(1),ConstantInt::get(Type::getInt32Ty(Ctx), 0U));EXPECT_EQ(ForkCI->getArgOperand(2), Usr);}}TEST_F(OpenMPIRBuilderTest, ParallelNested2Inner) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "parallel.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});unsigned NumInnerBodiesGenerated = 0;unsigned NumOuterBodiesGenerated = 0;unsigned NumFinalizationPoints = 0;auto InnerBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumInnerBodiesGenerated;};auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,Value &Orig, Value &Inner,Value *&ReplacementValue) -> InsertPointTy {// Trivial copy (=firstprivate).Builder.restoreIP(AllocaIP);Type *VTy = ReplacementValue->getType();Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload");ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy");Builder.restoreIP(CodeGenIP);Builder.CreateStore(V, ReplacementValue);return CodeGenIP;};auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; };auto OuterBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumOuterBodiesGenerated;Builder.restoreIP(CodeGenIP);BasicBlock *CGBB = CodeGenIP.getBlock();BasicBlock *NewBB1 = SplitBlock(CGBB, &*CodeGenIP.getPoint());BasicBlock *NewBB2 = SplitBlock(NewBB1, &*NewBB1->getFirstInsertionPt());CGBB->getTerminator()->eraseFromParent();;NewBB1->getTerminator()->eraseFromParent();;IRBuilder<>::InsertPoint AfterIP1 = OMPBuilder.createParallel(InsertPointTy(CGBB, CGBB->end()), AllocaIP, InnerBodyGenCB, PrivCB,FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false);Builder.restoreIP(AfterIP1);Builder.CreateBr(NewBB1);IRBuilder<>::InsertPoint AfterIP2 = OMPBuilder.createParallel(InsertPointTy(NewBB1, NewBB1->end()), AllocaIP, InnerBodyGenCB, PrivCB,FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false);Builder.restoreIP(AfterIP2);Builder.CreateBr(NewBB2);};IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());IRBuilder<>::InsertPoint AfterIP =OMPBuilder.createParallel(Loc, AllocaIP, OuterBodyGenCB, PrivCB, FiniCB,nullptr, nullptr, OMP_PROC_BIND_default, false);EXPECT_EQ(NumInnerBodiesGenerated, 2U);EXPECT_EQ(NumOuterBodiesGenerated, 1U);EXPECT_EQ(NumFinalizationPoints, 3U);Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_EQ(M->size(), 6U);for (Function &OutlinedFn : *M) {if (F == &OutlinedFn || OutlinedFn.isDeclaration())continue;EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_TRUE(OutlinedFn.hasFnAttribute(Attribute::NoUnwind));EXPECT_TRUE(OutlinedFn.hasFnAttribute(Attribute::NoRecurse));EXPECT_TRUE(OutlinedFn.hasParamAttribute(0, Attribute::NoAlias));EXPECT_TRUE(OutlinedFn.hasParamAttribute(1, Attribute::NoAlias));EXPECT_TRUE(OutlinedFn.hasInternalLinkage());EXPECT_EQ(OutlinedFn.arg_size(), 2U);unsigned NumAllocas = 0;for (Instruction &I : instructions(OutlinedFn))NumAllocas += isa<AllocaInst>(I);EXPECT_EQ(NumAllocas, 1U);EXPECT_EQ(OutlinedFn.getNumUses(), 1U);User *Usr = OutlinedFn.user_back();ASSERT_TRUE(isa<ConstantExpr>(Usr));CallInst *ForkCI = dyn_cast<CallInst>(Usr->user_back());ASSERT_NE(ForkCI, nullptr);EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call");EXPECT_EQ(ForkCI->arg_size(), 3U);EXPECT_TRUE(isa<GlobalVariable>(ForkCI->getArgOperand(0)));EXPECT_EQ(ForkCI->getArgOperand(1),ConstantInt::get(Type::getInt32Ty(Ctx), 0U));EXPECT_EQ(ForkCI->getArgOperand(2), Usr);}}TEST_F(OpenMPIRBuilderTest, ParallelIfCond) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "parallel.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI = nullptr;unsigned NumBodiesGenerated = 0;unsigned NumPrivatizedVars = 0;unsigned NumFinalizationPoints = 0;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumBodiesGenerated;Builder.restoreIP(AllocaIP);PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());Builder.CreateStore(F->arg_begin(), PrivAI);Builder.restoreIP(CodeGenIP);Value *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Value *Cmp = Builder.CreateICmpNE(F->arg_begin(), PrivLoad);Instruction *ThenTerm, *ElseTerm;SplitBlockAndInsertIfThenElse(Cmp, &*Builder.GetInsertPoint(), &ThenTerm,&ElseTerm);};auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,Value &Orig, Value &Inner,Value *&ReplacementValue) -> InsertPointTy {++NumPrivatizedVars;if (!isa<AllocaInst>(Orig)) {EXPECT_EQ(&Orig, F->arg_begin());ReplacementValue = &Inner;return CodeGenIP;}// Since the original value is an allocation, it has a pointer type and// therefore no additional wrapping should happen.EXPECT_EQ(&Orig, &Inner);// Trivial copy (=firstprivate).Builder.restoreIP(AllocaIP);Type *VTy = ReplacementValue->getType();Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload");ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy");Builder.restoreIP(CodeGenIP);Builder.CreateStore(V, ReplacementValue);return CodeGenIP;};auto FiniCB = [&](InsertPointTy CodeGenIP) {++NumFinalizationPoints;// No destructors.};IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());IRBuilder<>::InsertPoint AfterIP =OMPBuilder.createParallel(Loc, AllocaIP, BodyGenCB, PrivCB, FiniCB,Builder.CreateIsNotNull(F->arg_begin()),nullptr, OMP_PROC_BIND_default, false);EXPECT_EQ(NumBodiesGenerated, 1U);EXPECT_EQ(NumPrivatizedVars, 1U);EXPECT_EQ(NumFinalizationPoints, 1U);Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_NE(PrivAI, nullptr);Function *OutlinedFn = PrivAI->getFunction();EXPECT_NE(F, OutlinedFn);EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_TRUE(OutlinedFn->hasInternalLinkage());EXPECT_EQ(OutlinedFn->arg_size(), 3U);EXPECT_EQ(&OutlinedFn->getEntryBlock(), PrivAI->getParent());ASSERT_EQ(OutlinedFn->getNumUses(), 2U);CallInst *DirectCI = nullptr;CallInst *ForkCI = nullptr;for (User *Usr : OutlinedFn->users()) {if (isa<CallInst>(Usr)) {ASSERT_EQ(DirectCI, nullptr);DirectCI = cast<CallInst>(Usr);} else {ASSERT_TRUE(isa<ConstantExpr>(Usr));ASSERT_EQ(Usr->getNumUses(), 1U);ASSERT_TRUE(isa<CallInst>(Usr->user_back()));ForkCI = cast<CallInst>(Usr->user_back());}}EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call");EXPECT_EQ(ForkCI->arg_size(), 4U);EXPECT_TRUE(isa<GlobalVariable>(ForkCI->getArgOperand(0)));EXPECT_EQ(ForkCI->getArgOperand(1),ConstantInt::get(Type::getInt32Ty(Ctx), 1));Value *StoredForkArg =findStoredValueInAggregateAt(Ctx, ForkCI->getArgOperand(3), 0);EXPECT_EQ(StoredForkArg, F->arg_begin());EXPECT_EQ(DirectCI->getCalledFunction(), OutlinedFn);EXPECT_EQ(DirectCI->arg_size(), 3U);EXPECT_TRUE(isa<AllocaInst>(DirectCI->getArgOperand(0)));EXPECT_TRUE(isa<AllocaInst>(DirectCI->getArgOperand(1)));Value *StoredDirectArg =findStoredValueInAggregateAt(Ctx, DirectCI->getArgOperand(2), 0);EXPECT_EQ(StoredDirectArg, F->arg_begin());}TEST_F(OpenMPIRBuilderTest, ParallelCancelBarrier) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "parallel.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});unsigned NumBodiesGenerated = 0;unsigned NumPrivatizedVars = 0;unsigned NumFinalizationPoints = 0;CallInst *CheckedBarrier = nullptr;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumBodiesGenerated;Builder.restoreIP(CodeGenIP);// Create three barriers, two cancel barriers but only one checked.Function *CBFn, *BFn;Builder.restoreIP(OMPBuilder.createBarrier(Builder.saveIP(), OMPD_parallel));CBFn = M->getFunction("__kmpc_cancel_barrier");BFn = M->getFunction("__kmpc_barrier");ASSERT_NE(CBFn, nullptr);ASSERT_EQ(BFn, nullptr);ASSERT_EQ(CBFn->getNumUses(), 1U);ASSERT_TRUE(isa<CallInst>(CBFn->user_back()));ASSERT_EQ(CBFn->user_back()->getNumUses(), 1U);CheckedBarrier = cast<CallInst>(CBFn->user_back());Builder.restoreIP(OMPBuilder.createBarrier(Builder.saveIP(), OMPD_parallel, true));CBFn = M->getFunction("__kmpc_cancel_barrier");BFn = M->getFunction("__kmpc_barrier");ASSERT_NE(CBFn, nullptr);ASSERT_NE(BFn, nullptr);ASSERT_EQ(CBFn->getNumUses(), 1U);ASSERT_EQ(BFn->getNumUses(), 1U);ASSERT_TRUE(isa<CallInst>(BFn->user_back()));ASSERT_EQ(BFn->user_back()->getNumUses(), 0U);Builder.restoreIP(OMPBuilder.createBarrier(Builder.saveIP(), OMPD_parallel,false, false));ASSERT_EQ(CBFn->getNumUses(), 2U);ASSERT_EQ(BFn->getNumUses(), 1U);ASSERT_TRUE(CBFn->user_back() != CheckedBarrier);ASSERT_TRUE(isa<CallInst>(CBFn->user_back()));ASSERT_EQ(CBFn->user_back()->getNumUses(), 0U);};auto PrivCB = [&](InsertPointTy, InsertPointTy, Value &V, Value &,Value *&) -> InsertPointTy {++NumPrivatizedVars;llvm_unreachable("No privatization callback call expected!");};FunctionType *FakeDestructorTy =FunctionType::get(Type::getVoidTy(Ctx), {Type::getInt32Ty(Ctx)},/*isVarArg=*/false);auto *FakeDestructor = Function::Create(FakeDestructorTy, Function::ExternalLinkage, "fakeDestructor", M.get());auto FiniCB = [&](InsertPointTy IP) {++NumFinalizationPoints;Builder.restoreIP(IP);Builder.CreateCall(FakeDestructor,{Builder.getInt32(NumFinalizationPoints)});};IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());IRBuilder<>::InsertPoint AfterIP =OMPBuilder.createParallel(Loc, AllocaIP, BodyGenCB, PrivCB, FiniCB,Builder.CreateIsNotNull(F->arg_begin()),nullptr, OMP_PROC_BIND_default, true);EXPECT_EQ(NumBodiesGenerated, 1U);EXPECT_EQ(NumPrivatizedVars, 0U);EXPECT_EQ(NumFinalizationPoints, 2U);EXPECT_EQ(FakeDestructor->getNumUses(), 2U);Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));BasicBlock *ExitBB = nullptr;for (const User *Usr : FakeDestructor->users()) {const CallInst *CI = dyn_cast<CallInst>(Usr);ASSERT_EQ(CI->getCalledFunction(), FakeDestructor);ASSERT_TRUE(isa<BranchInst>(CI->getNextNode()));ASSERT_EQ(CI->getNextNode()->getNumSuccessors(), 1U);if (ExitBB)ASSERT_EQ(CI->getNextNode()->getSuccessor(0), ExitBB);elseExitBB = CI->getNextNode()->getSuccessor(0);ASSERT_EQ(ExitBB->size(), 1U);if (!isa<ReturnInst>(ExitBB->front())) {ASSERT_TRUE(isa<BranchInst>(ExitBB->front()));ASSERT_EQ(cast<BranchInst>(ExitBB->front()).getNumSuccessors(), 1U);ASSERT_TRUE(isa<ReturnInst>(cast<BranchInst>(ExitBB->front()).getSuccessor(0)->front()));}}}TEST_F(OpenMPIRBuilderTest, ParallelForwardAsPointers) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});using InsertPointTy = OpenMPIRBuilder::InsertPointTy;Type *I32Ty = Type::getInt32Ty(M->getContext());Type *I32PtrTy = Type::getInt32PtrTy(M->getContext());Type *StructTy = StructType::get(I32Ty, I32PtrTy);Type *StructPtrTy = StructTy->getPointerTo();StructType *ArgStructTy =StructType::get(I32PtrTy, StructPtrTy, I32PtrTy, StructPtrTy);Type *VoidTy = Type::getVoidTy(M->getContext());FunctionCallee RetI32Func = M->getOrInsertFunction("ret_i32", I32Ty);FunctionCallee TakeI32Func =M->getOrInsertFunction("take_i32", VoidTy, I32Ty);FunctionCallee RetI32PtrFunc = M->getOrInsertFunction("ret_i32ptr", I32PtrTy);FunctionCallee TakeI32PtrFunc =M->getOrInsertFunction("take_i32ptr", VoidTy, I32PtrTy);FunctionCallee RetStructFunc = M->getOrInsertFunction("ret_struct", StructTy);FunctionCallee TakeStructFunc =M->getOrInsertFunction("take_struct", VoidTy, StructTy);FunctionCallee RetStructPtrFunc =M->getOrInsertFunction("ret_structptr", StructPtrTy);FunctionCallee TakeStructPtrFunc =M->getOrInsertFunction("take_structPtr", VoidTy, StructPtrTy);Value *I32Val = Builder.CreateCall(RetI32Func);Value *I32PtrVal = Builder.CreateCall(RetI32PtrFunc);Value *StructVal = Builder.CreateCall(RetStructFunc);Value *StructPtrVal = Builder.CreateCall(RetStructPtrFunc);Instruction *Internal;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {IRBuilder<>::InsertPointGuard Guard(Builder);Builder.restoreIP(CodeGenIP);Internal = Builder.CreateCall(TakeI32Func, I32Val);Builder.CreateCall(TakeI32PtrFunc, I32PtrVal);Builder.CreateCall(TakeStructFunc, StructVal);Builder.CreateCall(TakeStructPtrFunc, StructPtrVal);};auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &,Value &Inner, Value *&ReplacementValue) {ReplacementValue = &Inner;return CodeGenIP;};auto FiniCB = [](InsertPointTy) {};IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());IRBuilder<>::InsertPoint AfterIP =OMPBuilder.createParallel(Loc, AllocaIP, BodyGenCB, PrivCB, FiniCB,nullptr, nullptr, OMP_PROC_BIND_default, false);Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));Function *OutlinedFn = Internal->getFunction();Type *Arg2Type = OutlinedFn->getArg(2)->getType();EXPECT_TRUE(Arg2Type->isPointerTy());EXPECT_TRUE(cast<PointerType>(Arg2Type)->isOpaqueOrPointeeTypeMatches(ArgStructTy));}TEST_F(OpenMPIRBuilderTest, CanonicalLoopSimple) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Value *TripCount = F->getArg(0);unsigned NumBodiesGenerated = 0;auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, llvm::Value *LC) {NumBodiesGenerated += 1;Builder.restoreIP(CodeGenIP);Value *Cmp = Builder.CreateICmpEQ(LC, TripCount);Instruction *ThenTerm, *ElseTerm;SplitBlockAndInsertIfThenElse(Cmp, CodeGenIP.getBlock()->getTerminator(),&ThenTerm, &ElseTerm);};CanonicalLoopInfo *Loop =OMPBuilder.createCanonicalLoop(Loc, LoopBodyGenCB, TripCount);Builder.restoreIP(Loop->getAfterIP());ReturnInst *RetInst = Builder.CreateRetVoid();OMPBuilder.finalize();Loop->assertOK();EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_EQ(NumBodiesGenerated, 1U);// Verify control flow structure (in addition to Loop->assertOK()).EXPECT_EQ(Loop->getPreheader()->getSinglePredecessor(), &F->getEntryBlock());EXPECT_EQ(Loop->getAfter(), Builder.GetInsertBlock());Instruction *IndVar = Loop->getIndVar();EXPECT_TRUE(isa<PHINode>(IndVar));EXPECT_EQ(IndVar->getType(), TripCount->getType());EXPECT_EQ(IndVar->getParent(), Loop->getHeader());EXPECT_EQ(Loop->getTripCount(), TripCount);BasicBlock *Body = Loop->getBody();Instruction *CmpInst = &Body->getInstList().front();EXPECT_TRUE(isa<ICmpInst>(CmpInst));EXPECT_EQ(CmpInst->getOperand(0), IndVar);BasicBlock *LatchPred = Loop->getLatch()->getSinglePredecessor();EXPECT_TRUE(llvm::all_of(successors(Body), [=](BasicBlock *SuccBB) {return SuccBB->getSingleSuccessor() == LatchPred;}));EXPECT_EQ(&Loop->getAfter()->front(), RetInst);}TEST_F(OpenMPIRBuilderTest, CanonicalLoopBounds) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);// Check the trip count is computed correctly. We generate the canonical loop// but rely on the IRBuilder's constant folder to compute the final result// since all inputs are constant. To verify overflow situations, limit the// trip count / loop counter widths to 16 bits.auto EvalTripCount = [&](int64_t Start, int64_t Stop, int64_t Step,bool IsSigned, bool InclusiveStop) -> int64_t {OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Type *LCTy = Type::getInt16Ty(Ctx);Value *StartVal = ConstantInt::get(LCTy, Start);Value *StopVal = ConstantInt::get(LCTy, Stop);Value *StepVal = ConstantInt::get(LCTy, Step);auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, llvm::Value *LC) {};CanonicalLoopInfo *Loop =OMPBuilder.createCanonicalLoop(Loc, LoopBodyGenCB, StartVal, StopVal,StepVal, IsSigned, InclusiveStop);Loop->assertOK();Builder.restoreIP(Loop->getAfterIP());Value *TripCount = Loop->getTripCount();return cast<ConstantInt>(TripCount)->getValue().getZExtValue();};EXPECT_EQ(EvalTripCount(0, 0, 1, false, false), 0);EXPECT_EQ(EvalTripCount(0, 1, 2, false, false), 1);EXPECT_EQ(EvalTripCount(0, 42, 1, false, false), 42);EXPECT_EQ(EvalTripCount(0, 42, 2, false, false), 21);EXPECT_EQ(EvalTripCount(21, 42, 1, false, false), 21);EXPECT_EQ(EvalTripCount(0, 5, 5, false, false), 1);EXPECT_EQ(EvalTripCount(0, 9, 5, false, false), 2);EXPECT_EQ(EvalTripCount(0, 11, 5, false, false), 3);EXPECT_EQ(EvalTripCount(0, 0xFFFF, 1, false, false), 0xFFFF);EXPECT_EQ(EvalTripCount(0xFFFF, 0, 1, false, false), 0);EXPECT_EQ(EvalTripCount(0xFFFE, 0xFFFF, 1, false, false), 1);EXPECT_EQ(EvalTripCount(0, 0xFFFF, 0x100, false, false), 0x100);EXPECT_EQ(EvalTripCount(0, 0xFFFF, 0xFFFF, false, false), 1);EXPECT_EQ(EvalTripCount(0, 6, 5, false, false), 2);EXPECT_EQ(EvalTripCount(0, 0xFFFF, 0xFFFE, false, false), 2);EXPECT_EQ(EvalTripCount(0, 0, 1, false, true), 1);EXPECT_EQ(EvalTripCount(0, 0, 0xFFFF, false, true), 1);EXPECT_EQ(EvalTripCount(0, 0xFFFE, 1, false, true), 0xFFFF);EXPECT_EQ(EvalTripCount(0, 0xFFFE, 2, false, true), 0x8000);EXPECT_EQ(EvalTripCount(0, 0, -1, true, false), 0);EXPECT_EQ(EvalTripCount(0, 1, -1, true, true), 0);EXPECT_EQ(EvalTripCount(20, 5, -5, true, false), 3);EXPECT_EQ(EvalTripCount(20, 5, -5, true, true), 4);EXPECT_EQ(EvalTripCount(-4, -2, 2, true, false), 1);EXPECT_EQ(EvalTripCount(-4, -3, 2, true, false), 1);EXPECT_EQ(EvalTripCount(-4, -2, 2, true, true), 2);EXPECT_EQ(EvalTripCount(INT16_MIN, 0, 1, true, false), 0x8000);EXPECT_EQ(EvalTripCount(INT16_MIN, 0, 1, true, true), 0x8001);EXPECT_EQ(EvalTripCount(INT16_MIN, 0x7FFF, 1, true, false), 0xFFFF);EXPECT_EQ(EvalTripCount(INT16_MIN + 1, 0x7FFF, 1, true, true), 0xFFFF);EXPECT_EQ(EvalTripCount(INT16_MIN, 0, 0x7FFF, true, false), 2);EXPECT_EQ(EvalTripCount(0x7FFF, 0, -1, true, false), 0x7FFF);EXPECT_EQ(EvalTripCount(0, INT16_MIN, -1, true, false), 0x8000);EXPECT_EQ(EvalTripCount(0, INT16_MIN, -16, true, false), 0x800);EXPECT_EQ(EvalTripCount(0x7FFF, INT16_MIN, -1, true, false), 0xFFFF);EXPECT_EQ(EvalTripCount(0x7FFF, 1, INT16_MIN, true, false), 1);EXPECT_EQ(EvalTripCount(0x7FFF, -1, INT16_MIN, true, true), 2);// Finalize the function and verify it.Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CollapseNestedLoops) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);Type *LCTy = F->getArg(0)->getType();Constant *One = ConstantInt::get(LCTy, 1);Constant *Two = ConstantInt::get(LCTy, 2);Value *OuterTripCount =Builder.CreateAdd(F->getArg(0), Two, "tripcount.outer");Value *InnerTripCount =Builder.CreateAdd(F->getArg(0), One, "tripcount.inner");// Fix an insertion point for ComputeIP.BasicBlock *LoopNextEnter =BasicBlock::Create(M->getContext(), "loopnest.enter", F,Builder.GetInsertBlock()->getNextNode());BranchInst *EnterBr = Builder.CreateBr(LoopNextEnter);InsertPointTy ComputeIP{EnterBr->getParent(), EnterBr->getIterator()};Builder.SetInsertPoint(LoopNextEnter);OpenMPIRBuilder::LocationDescription OuterLoc(Builder.saveIP(), DL);CanonicalLoopInfo *InnerLoop = nullptr;CallInst *InbetweenLead = nullptr;CallInst *InbetweenTrail = nullptr;CallInst *Call = nullptr;auto OuterLoopBodyGenCB = [&](InsertPointTy OuterCodeGenIP, Value *OuterLC) {Builder.restoreIP(OuterCodeGenIP);InbetweenLead =createPrintfCall(Builder, "In-between lead i=%d\\n", {OuterLC});auto InnerLoopBodyGenCB = [&](InsertPointTy InnerCodeGenIP,Value *InnerLC) {Builder.restoreIP(InnerCodeGenIP);Call = createPrintfCall(Builder, "body i=%d j=%d\\n", {OuterLC, InnerLC});};InnerLoop = OMPBuilder.createCanonicalLoop(Builder.saveIP(), InnerLoopBodyGenCB, InnerTripCount, "inner");Builder.restoreIP(InnerLoop->getAfterIP());InbetweenTrail =createPrintfCall(Builder, "In-between trail i=%d\\n", {OuterLC});};CanonicalLoopInfo *OuterLoop = OMPBuilder.createCanonicalLoop(OuterLoc, OuterLoopBodyGenCB, OuterTripCount, "outer");// Finish the function.Builder.restoreIP(OuterLoop->getAfterIP());Builder.CreateRetVoid();CanonicalLoopInfo *Collapsed =OMPBuilder.collapseLoops(DL, {OuterLoop, InnerLoop}, ComputeIP);OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));// Verify control flow and BB order.BasicBlock *RefOrder[] = {Collapsed->getPreheader(), Collapsed->getHeader(),Collapsed->getCond(), Collapsed->getBody(),InbetweenLead->getParent(), Call->getParent(),InbetweenTrail->getParent(), Collapsed->getLatch(),Collapsed->getExit(), Collapsed->getAfter(),};EXPECT_TRUE(verifyDFSOrder(F, RefOrder));EXPECT_TRUE(verifyListOrder(F, RefOrder));// Verify the total trip count.auto *TripCount = cast<MulOperator>(Collapsed->getTripCount());EXPECT_EQ(TripCount->getOperand(0), OuterTripCount);EXPECT_EQ(TripCount->getOperand(1), InnerTripCount);// Verify the changed indvar.auto *OuterIV = cast<BinaryOperator>(Call->getOperand(1));EXPECT_EQ(OuterIV->getOpcode(), Instruction::UDiv);EXPECT_EQ(OuterIV->getParent(), Collapsed->getBody());EXPECT_EQ(OuterIV->getOperand(1), InnerTripCount);EXPECT_EQ(OuterIV->getOperand(0), Collapsed->getIndVar());auto *InnerIV = cast<BinaryOperator>(Call->getOperand(2));EXPECT_EQ(InnerIV->getOpcode(), Instruction::URem);EXPECT_EQ(InnerIV->getParent(), Collapsed->getBody());EXPECT_EQ(InnerIV->getOperand(0), Collapsed->getIndVar());EXPECT_EQ(InnerIV->getOperand(1), InnerTripCount);EXPECT_EQ(InbetweenLead->getOperand(1), OuterIV);EXPECT_EQ(InbetweenTrail->getOperand(1), OuterIV);}TEST_F(OpenMPIRBuilderTest, TileSingleLoop) {OpenMPIRBuilder OMPBuilder(*M);CallInst *Call;BasicBlock *BodyCode;CanonicalLoopInfo *Loop =buildSingleLoopFunction(DL, OMPBuilder, 32, &Call, &BodyCode);Instruction *OrigIndVar = Loop->getIndVar();EXPECT_EQ(Call->getOperand(1), OrigIndVar);// Tile the loop.Constant *TileSize = ConstantInt::get(Loop->getIndVarType(), APInt(32, 7));std::vector<CanonicalLoopInfo *> GenLoops =OMPBuilder.tileLoops(DL, {Loop}, {TileSize});OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_EQ(GenLoops.size(), 2u);CanonicalLoopInfo *Floor = GenLoops[0];CanonicalLoopInfo *Tile = GenLoops[1];BasicBlock *RefOrder[] = {Floor->getPreheader(), Floor->getHeader(), Floor->getCond(),Floor->getBody(), Tile->getPreheader(), Tile->getHeader(),Tile->getCond(), Tile->getBody(), BodyCode,Tile->getLatch(), Tile->getExit(), Tile->getAfter(),Floor->getLatch(), Floor->getExit(), Floor->getAfter(),};EXPECT_TRUE(verifyDFSOrder(F, RefOrder));EXPECT_TRUE(verifyListOrder(F, RefOrder));// Check the induction variable.EXPECT_EQ(Call->getParent(), BodyCode);auto *Shift = cast<AddOperator>(Call->getOperand(1));EXPECT_EQ(cast<Instruction>(Shift)->getParent(), Tile->getBody());EXPECT_EQ(Shift->getOperand(1), Tile->getIndVar());auto *Scale = cast<MulOperator>(Shift->getOperand(0));EXPECT_EQ(cast<Instruction>(Scale)->getParent(), Tile->getBody());EXPECT_EQ(Scale->getOperand(0), TileSize);EXPECT_EQ(Scale->getOperand(1), Floor->getIndVar());}TEST_F(OpenMPIRBuilderTest, TileNestedLoops) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Value *TripCount = F->getArg(0);Type *LCTy = TripCount->getType();BasicBlock *BodyCode = nullptr;CanonicalLoopInfo *InnerLoop = nullptr;auto OuterLoopBodyGenCB = [&](InsertPointTy OuterCodeGenIP,llvm::Value *OuterLC) {auto InnerLoopBodyGenCB = [&](InsertPointTy InnerCodeGenIP,llvm::Value *InnerLC) {Builder.restoreIP(InnerCodeGenIP);BodyCode = Builder.GetInsertBlock();// Add something that consumes the induction variables to the body.createPrintfCall(Builder, "i=%d j=%d\\n", {OuterLC, InnerLC});};InnerLoop = OMPBuilder.createCanonicalLoop(OuterCodeGenIP, InnerLoopBodyGenCB, TripCount, "inner");};CanonicalLoopInfo *OuterLoop = OMPBuilder.createCanonicalLoop(Loc, OuterLoopBodyGenCB, TripCount, "outer");// Finalize the function.Builder.restoreIP(OuterLoop->getAfterIP());Builder.CreateRetVoid();// Tile to loop nest.Constant *OuterTileSize = ConstantInt::get(LCTy, APInt(32, 11));Constant *InnerTileSize = ConstantInt::get(LCTy, APInt(32, 7));std::vector<CanonicalLoopInfo *> GenLoops = OMPBuilder.tileLoops(DL, {OuterLoop, InnerLoop}, {OuterTileSize, InnerTileSize});OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_EQ(GenLoops.size(), 4u);CanonicalLoopInfo *Floor1 = GenLoops[0];CanonicalLoopInfo *Floor2 = GenLoops[1];CanonicalLoopInfo *Tile1 = GenLoops[2];CanonicalLoopInfo *Tile2 = GenLoops[3];BasicBlock *RefOrder[] = {Floor1->getPreheader(),Floor1->getHeader(),Floor1->getCond(),Floor1->getBody(),Floor2->getPreheader(),Floor2->getHeader(),Floor2->getCond(),Floor2->getBody(),Tile1->getPreheader(),Tile1->getHeader(),Tile1->getCond(),Tile1->getBody(),Tile2->getPreheader(),Tile2->getHeader(),Tile2->getCond(),Tile2->getBody(),BodyCode,Tile2->getLatch(),Tile2->getExit(),Tile2->getAfter(),Tile1->getLatch(),Tile1->getExit(),Tile1->getAfter(),Floor2->getLatch(),Floor2->getExit(),Floor2->getAfter(),Floor1->getLatch(),Floor1->getExit(),Floor1->getAfter(),};EXPECT_TRUE(verifyDFSOrder(F, RefOrder));EXPECT_TRUE(verifyListOrder(F, RefOrder));}TEST_F(OpenMPIRBuilderTest, TileNestedLoopsWithBounds) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);Value *TripCount = F->getArg(0);Type *LCTy = TripCount->getType();Value *OuterStartVal = ConstantInt::get(LCTy, 2);Value *OuterStopVal = TripCount;Value *OuterStep = ConstantInt::get(LCTy, 5);Value *InnerStartVal = ConstantInt::get(LCTy, 13);Value *InnerStopVal = TripCount;Value *InnerStep = ConstantInt::get(LCTy, 3);// Fix an insertion point for ComputeIP.BasicBlock *LoopNextEnter =BasicBlock::Create(M->getContext(), "loopnest.enter", F,Builder.GetInsertBlock()->getNextNode());BranchInst *EnterBr = Builder.CreateBr(LoopNextEnter);InsertPointTy ComputeIP{EnterBr->getParent(), EnterBr->getIterator()};InsertPointTy LoopIP{LoopNextEnter, LoopNextEnter->begin()};OpenMPIRBuilder::LocationDescription Loc({LoopIP, DL});BasicBlock *BodyCode = nullptr;CanonicalLoopInfo *InnerLoop = nullptr;CallInst *Call = nullptr;auto OuterLoopBodyGenCB = [&](InsertPointTy OuterCodeGenIP,llvm::Value *OuterLC) {auto InnerLoopBodyGenCB = [&](InsertPointTy InnerCodeGenIP,llvm::Value *InnerLC) {Builder.restoreIP(InnerCodeGenIP);BodyCode = Builder.GetInsertBlock();// Add something that consumes the induction variable to the body.Call = createPrintfCall(Builder, "i=%d j=%d\\n", {OuterLC, InnerLC});};InnerLoop = OMPBuilder.createCanonicalLoop(OuterCodeGenIP, InnerLoopBodyGenCB, InnerStartVal, InnerStopVal,InnerStep, false, false, ComputeIP, "inner");};CanonicalLoopInfo *OuterLoop = OMPBuilder.createCanonicalLoop(Loc, OuterLoopBodyGenCB, OuterStartVal, OuterStopVal, OuterStep, false,false, ComputeIP, "outer");// Finalize the functionBuilder.restoreIP(OuterLoop->getAfterIP());Builder.CreateRetVoid();// Tile the loop nest.Constant *TileSize0 = ConstantInt::get(LCTy, APInt(32, 11));Constant *TileSize1 = ConstantInt::get(LCTy, APInt(32, 7));std::vector<CanonicalLoopInfo *> GenLoops =OMPBuilder.tileLoops(DL, {OuterLoop, InnerLoop}, {TileSize0, TileSize1});OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_EQ(GenLoops.size(), 4u);CanonicalLoopInfo *Floor0 = GenLoops[0];CanonicalLoopInfo *Floor1 = GenLoops[1];CanonicalLoopInfo *Tile0 = GenLoops[2];CanonicalLoopInfo *Tile1 = GenLoops[3];BasicBlock *RefOrder[] = {Floor0->getPreheader(),Floor0->getHeader(),Floor0->getCond(),Floor0->getBody(),Floor1->getPreheader(),Floor1->getHeader(),Floor1->getCond(),Floor1->getBody(),Tile0->getPreheader(),Tile0->getHeader(),Tile0->getCond(),Tile0->getBody(),Tile1->getPreheader(),Tile1->getHeader(),Tile1->getCond(),Tile1->getBody(),BodyCode,Tile1->getLatch(),Tile1->getExit(),Tile1->getAfter(),Tile0->getLatch(),Tile0->getExit(),Tile0->getAfter(),Floor1->getLatch(),Floor1->getExit(),Floor1->getAfter(),Floor0->getLatch(),Floor0->getExit(),Floor0->getAfter(),};EXPECT_TRUE(verifyDFSOrder(F, RefOrder));EXPECT_TRUE(verifyListOrder(F, RefOrder));EXPECT_EQ(Call->getParent(), BodyCode);auto *RangeShift0 = cast<AddOperator>(Call->getOperand(1));EXPECT_EQ(RangeShift0->getOperand(1), OuterStartVal);auto *RangeScale0 = cast<MulOperator>(RangeShift0->getOperand(0));EXPECT_EQ(RangeScale0->getOperand(1), OuterStep);auto *TileShift0 = cast<AddOperator>(RangeScale0->getOperand(0));EXPECT_EQ(cast<Instruction>(TileShift0)->getParent(), Tile1->getBody());EXPECT_EQ(TileShift0->getOperand(1), Tile0->getIndVar());auto *TileScale0 = cast<MulOperator>(TileShift0->getOperand(0));EXPECT_EQ(cast<Instruction>(TileScale0)->getParent(), Tile1->getBody());EXPECT_EQ(TileScale0->getOperand(0), TileSize0);EXPECT_EQ(TileScale0->getOperand(1), Floor0->getIndVar());auto *RangeShift1 = cast<AddOperator>(Call->getOperand(2));EXPECT_EQ(cast<Instruction>(RangeShift1)->getParent(), BodyCode);EXPECT_EQ(RangeShift1->getOperand(1), InnerStartVal);auto *RangeScale1 = cast<MulOperator>(RangeShift1->getOperand(0));EXPECT_EQ(cast<Instruction>(RangeScale1)->getParent(), BodyCode);EXPECT_EQ(RangeScale1->getOperand(1), InnerStep);auto *TileShift1 = cast<AddOperator>(RangeScale1->getOperand(0));EXPECT_EQ(cast<Instruction>(TileShift1)->getParent(), Tile1->getBody());EXPECT_EQ(TileShift1->getOperand(1), Tile1->getIndVar());auto *TileScale1 = cast<MulOperator>(TileShift1->getOperand(0));EXPECT_EQ(cast<Instruction>(TileScale1)->getParent(), Tile1->getBody());EXPECT_EQ(TileScale1->getOperand(0), TileSize1);EXPECT_EQ(TileScale1->getOperand(1), Floor1->getIndVar());}TEST_F(OpenMPIRBuilderTest, TileSingleLoopCounts) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);// Create a loop, tile it, and extract its trip count. All input values are// constant and IRBuilder evaluates all-constant arithmetic inplace, such that// the floor trip count itself will be a ConstantInt. Unfortunately we cannot// do the same for the tile loop.auto GetFloorCount = [&](int64_t Start, int64_t Stop, int64_t Step,bool IsSigned, bool InclusiveStop,int64_t TileSize) -> uint64_t {OpenMPIRBuilder::LocationDescription Loc(Builder.saveIP(), DL);Type *LCTy = Type::getInt16Ty(Ctx);Value *StartVal = ConstantInt::get(LCTy, Start);Value *StopVal = ConstantInt::get(LCTy, Stop);Value *StepVal = ConstantInt::get(LCTy, Step);// Generate a loop.auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, llvm::Value *LC) {};CanonicalLoopInfo *Loop =OMPBuilder.createCanonicalLoop(Loc, LoopBodyGenCB, StartVal, StopVal,StepVal, IsSigned, InclusiveStop);InsertPointTy AfterIP = Loop->getAfterIP();// Tile the loop.Value *TileSizeVal = ConstantInt::get(LCTy, TileSize);std::vector<CanonicalLoopInfo *> GenLoops =OMPBuilder.tileLoops(Loc.DL, {Loop}, {TileSizeVal});// Set the insertion pointer to after loop, where the next loop will be// emitted.Builder.restoreIP(AfterIP);// Extract the trip count.CanonicalLoopInfo *FloorLoop = GenLoops[0];Value *FloorTripCount = FloorLoop->getTripCount();return cast<ConstantInt>(FloorTripCount)->getValue().getZExtValue();};// Empty iteration domain.EXPECT_EQ(GetFloorCount(0, 0, 1, false, false, 7), 0u);EXPECT_EQ(GetFloorCount(0, -1, 1, false, true, 7), 0u);EXPECT_EQ(GetFloorCount(-1, -1, -1, true, false, 7), 0u);EXPECT_EQ(GetFloorCount(-1, 0, -1, true, true, 7), 0u);EXPECT_EQ(GetFloorCount(-1, -1, 3, true, false, 7), 0u);// Only complete tiles.EXPECT_EQ(GetFloorCount(0, 14, 1, false, false, 7), 2u);EXPECT_EQ(GetFloorCount(0, 14, 1, false, false, 7), 2u);EXPECT_EQ(GetFloorCount(1, 15, 1, false, false, 7), 2u);EXPECT_EQ(GetFloorCount(0, -14, -1, true, false, 7), 2u);EXPECT_EQ(GetFloorCount(-1, -14, -1, true, true, 7), 2u);EXPECT_EQ(GetFloorCount(0, 3 * 7 * 2, 3, false, false, 7), 2u);// Only a partial tile.EXPECT_EQ(GetFloorCount(0, 1, 1, false, false, 7), 1u);EXPECT_EQ(GetFloorCount(0, 6, 1, false, false, 7), 1u);EXPECT_EQ(GetFloorCount(-1, 1, 3, true, false, 7), 1u);EXPECT_EQ(GetFloorCount(-1, -2, -1, true, false, 7), 1u);EXPECT_EQ(GetFloorCount(0, 2, 3, false, false, 7), 1u);// Complete and partial tiles.EXPECT_EQ(GetFloorCount(0, 13, 1, false, false, 7), 2u);EXPECT_EQ(GetFloorCount(0, 15, 1, false, false, 7), 3u);EXPECT_EQ(GetFloorCount(-1, -14, -1, true, false, 7), 2u);EXPECT_EQ(GetFloorCount(0, 3 * 7 * 5 - 1, 3, false, false, 7), 5u);EXPECT_EQ(GetFloorCount(-1, -3 * 7 * 5, -3, true, false, 7), 5u);// Close to 16-bit integer range.EXPECT_EQ(GetFloorCount(0, 0xFFFF, 1, false, false, 1), 0xFFFFu);EXPECT_EQ(GetFloorCount(0, 0xFFFF, 1, false, false, 7), 0xFFFFu / 7 + 1);EXPECT_EQ(GetFloorCount(0, 0xFFFE, 1, false, true, 7), 0xFFFFu / 7 + 1);EXPECT_EQ(GetFloorCount(-0x8000, 0x7FFF, 1, true, false, 7), 0xFFFFu / 7 + 1);EXPECT_EQ(GetFloorCount(-0x7FFF, 0x7FFF, 1, true, true, 7), 0xFFFFu / 7 + 1);EXPECT_EQ(GetFloorCount(0, 0xFFFE, 1, false, false, 0xFFFF), 1u);EXPECT_EQ(GetFloorCount(-0x8000, 0x7FFF, 1, true, false, 0xFFFF), 1u);// Finalize the function.Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, ApplySimd) {OpenMPIRBuilder OMPBuilder(*M);CanonicalLoopInfo *CLI = buildSingleLoopFunction(DL, OMPBuilder, 32);// Simd-ize the loop.OMPBuilder.applySimd(CLI, nullptr);OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));PassBuilder PB;FunctionAnalysisManager FAM;PB.registerFunctionAnalyses(FAM);LoopInfo &LI = FAM.getResult<LoopAnalysis>(*F);const std::vector<Loop *> &TopLvl = LI.getTopLevelLoops();EXPECT_EQ(TopLvl.size(), 1u);Loop *L = TopLvl.front();EXPECT_TRUE(findStringMetadataForLoop(L, "llvm.loop.parallel_accesses"));EXPECT_TRUE(getBooleanLoopAttribute(L, "llvm.loop.vectorize.enable"));// Check for llvm.access.group metadata attached to the printf// function in the loop body.BasicBlock *LoopBody = CLI->getBody();EXPECT_TRUE(any_of(*LoopBody, [](Instruction &I) {return I.getMetadata("llvm.access.group") != nullptr;}));}TEST_F(OpenMPIRBuilderTest, ApplySimdlen) {OpenMPIRBuilder OMPBuilder(*M);CanonicalLoopInfo *CLI = buildSingleLoopFunction(DL, OMPBuilder, 32);// Simd-ize the loop.OMPBuilder.applySimd(CLI, ConstantInt::get(Type::getInt32Ty(Ctx), 3));OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));PassBuilder PB;FunctionAnalysisManager FAM;PB.registerFunctionAnalyses(FAM);LoopInfo &LI = FAM.getResult<LoopAnalysis>(*F);const std::vector<Loop *> &TopLvl = LI.getTopLevelLoops();EXPECT_EQ(TopLvl.size(), 1u);Loop *L = TopLvl.front();EXPECT_TRUE(findStringMetadataForLoop(L, "llvm.loop.parallel_accesses"));EXPECT_TRUE(getBooleanLoopAttribute(L, "llvm.loop.vectorize.enable"));EXPECT_EQ(getIntLoopAttribute(L, "llvm.loop.vectorize.width"), 3);// Check for llvm.access.group metadata attached to the printf// function in the loop body.BasicBlock *LoopBody = CLI->getBody();EXPECT_TRUE(any_of(*LoopBody, [](Instruction &I) {return I.getMetadata("llvm.access.group") != nullptr;}));}TEST_F(OpenMPIRBuilderTest, UnrollLoopFull) {OpenMPIRBuilder OMPBuilder(*M);CanonicalLoopInfo *CLI = buildSingleLoopFunction(DL, OMPBuilder, 32);// Unroll the loop.OMPBuilder.unrollLoopFull(DL, CLI);OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));PassBuilder PB;FunctionAnalysisManager FAM;PB.registerFunctionAnalyses(FAM);LoopInfo &LI = FAM.getResult<LoopAnalysis>(*F);const std::vector<Loop *> &TopLvl = LI.getTopLevelLoops();EXPECT_EQ(TopLvl.size(), 1u);Loop *L = TopLvl.front();EXPECT_TRUE(getBooleanLoopAttribute(L, "llvm.loop.unroll.enable"));EXPECT_TRUE(getBooleanLoopAttribute(L, "llvm.loop.unroll.full"));}TEST_F(OpenMPIRBuilderTest, UnrollLoopPartial) {OpenMPIRBuilder OMPBuilder(*M);CanonicalLoopInfo *CLI = buildSingleLoopFunction(DL, OMPBuilder, 32);// Unroll the loop.CanonicalLoopInfo *UnrolledLoop = nullptr;OMPBuilder.unrollLoopPartial(DL, CLI, 5, &UnrolledLoop);ASSERT_NE(UnrolledLoop, nullptr);OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));UnrolledLoop->assertOK();PassBuilder PB;FunctionAnalysisManager FAM;PB.registerFunctionAnalyses(FAM);LoopInfo &LI = FAM.getResult<LoopAnalysis>(*F);const std::vector<Loop *> &TopLvl = LI.getTopLevelLoops();EXPECT_EQ(TopLvl.size(), 1u);Loop *Outer = TopLvl.front();EXPECT_EQ(Outer->getHeader(), UnrolledLoop->getHeader());EXPECT_EQ(Outer->getLoopLatch(), UnrolledLoop->getLatch());EXPECT_EQ(Outer->getExitingBlock(), UnrolledLoop->getCond());EXPECT_EQ(Outer->getExitBlock(), UnrolledLoop->getExit());EXPECT_EQ(Outer->getSubLoops().size(), 1u);Loop *Inner = Outer->getSubLoops().front();EXPECT_TRUE(getBooleanLoopAttribute(Inner, "llvm.loop.unroll.enable"));EXPECT_EQ(getIntLoopAttribute(Inner, "llvm.loop.unroll.count"), 5);}TEST_F(OpenMPIRBuilderTest, UnrollLoopHeuristic) {OpenMPIRBuilder OMPBuilder(*M);CanonicalLoopInfo *CLI = buildSingleLoopFunction(DL, OMPBuilder, 32);// Unroll the loop.OMPBuilder.unrollLoopHeuristic(DL, CLI);OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));PassBuilder PB;FunctionAnalysisManager FAM;PB.registerFunctionAnalyses(FAM);LoopInfo &LI = FAM.getResult<LoopAnalysis>(*F);const std::vector<Loop *> &TopLvl = LI.getTopLevelLoops();EXPECT_EQ(TopLvl.size(), 1u);Loop *L = TopLvl.front();EXPECT_TRUE(getBooleanLoopAttribute(L, "llvm.loop.unroll.enable"));}TEST_F(OpenMPIRBuilderTest, StaticWorkShareLoop) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Type *LCTy = Type::getInt32Ty(Ctx);Value *StartVal = ConstantInt::get(LCTy, 10);Value *StopVal = ConstantInt::get(LCTy, 52);Value *StepVal = ConstantInt::get(LCTy, 2);auto LoopBodyGen = [&](InsertPointTy, llvm::Value *) {};CanonicalLoopInfo *CLI = OMPBuilder.createCanonicalLoop(Loc, LoopBodyGen, StartVal, StopVal, StepVal,/*IsSigned=*/false, /*InclusiveStop=*/false);BasicBlock *Preheader = CLI->getPreheader();BasicBlock *Body = CLI->getBody();Value *IV = CLI->getIndVar();BasicBlock *ExitBlock = CLI->getExit();Builder.SetInsertPoint(BB, BB->getFirstInsertionPt());InsertPointTy AllocaIP = Builder.saveIP();OMPBuilder.applyWorkshareLoop(DL, CLI, AllocaIP, /*NeedsBarrier=*/true,OMP_SCHEDULE_Static);BasicBlock *Cond = Body->getSinglePredecessor();Instruction *Cmp = &*Cond->begin();Value *TripCount = Cmp->getOperand(1);auto AllocaIter = BB->begin();ASSERT_GE(std::distance(BB->begin(), BB->end()), 4);AllocaInst *PLastIter = dyn_cast<AllocaInst>(&*(AllocaIter++));AllocaInst *PLowerBound = dyn_cast<AllocaInst>(&*(AllocaIter++));AllocaInst *PUpperBound = dyn_cast<AllocaInst>(&*(AllocaIter++));AllocaInst *PStride = dyn_cast<AllocaInst>(&*(AllocaIter++));EXPECT_NE(PLastIter, nullptr);EXPECT_NE(PLowerBound, nullptr);EXPECT_NE(PUpperBound, nullptr);EXPECT_NE(PStride, nullptr);auto PreheaderIter = Preheader->begin();ASSERT_GE(std::distance(Preheader->begin(), Preheader->end()), 7);StoreInst *LowerBoundStore = dyn_cast<StoreInst>(&*(PreheaderIter++));StoreInst *UpperBoundStore = dyn_cast<StoreInst>(&*(PreheaderIter++));StoreInst *StrideStore = dyn_cast<StoreInst>(&*(PreheaderIter++));ASSERT_NE(LowerBoundStore, nullptr);ASSERT_NE(UpperBoundStore, nullptr);ASSERT_NE(StrideStore, nullptr);auto *OrigLowerBound =dyn_cast<ConstantInt>(LowerBoundStore->getValueOperand());auto *OrigUpperBound =dyn_cast<ConstantInt>(UpperBoundStore->getValueOperand());auto *OrigStride = dyn_cast<ConstantInt>(StrideStore->getValueOperand());ASSERT_NE(OrigLowerBound, nullptr);ASSERT_NE(OrigUpperBound, nullptr);ASSERT_NE(OrigStride, nullptr);EXPECT_EQ(OrigLowerBound->getValue(), 0);EXPECT_EQ(OrigUpperBound->getValue(), 20);EXPECT_EQ(OrigStride->getValue(), 1);// Check that the loop IV is updated to account for the lower bound returned// by the OpenMP runtime call.BinaryOperator *Add = dyn_cast<BinaryOperator>(&Body->front());EXPECT_EQ(Add->getOperand(0), IV);auto *LoadedLowerBound = dyn_cast<LoadInst>(Add->getOperand(1));ASSERT_NE(LoadedLowerBound, nullptr);EXPECT_EQ(LoadedLowerBound->getPointerOperand(), PLowerBound);// Check that the trip count is updated to account for the lower and upper// bounds return by the OpenMP runtime call.auto *AddOne = dyn_cast<Instruction>(TripCount);ASSERT_NE(AddOne, nullptr);ASSERT_TRUE(AddOne->isBinaryOp());auto *One = dyn_cast<ConstantInt>(AddOne->getOperand(1));ASSERT_NE(One, nullptr);EXPECT_EQ(One->getValue(), 1);auto *Difference = dyn_cast<Instruction>(AddOne->getOperand(0));ASSERT_NE(Difference, nullptr);ASSERT_TRUE(Difference->isBinaryOp());EXPECT_EQ(Difference->getOperand(1), LoadedLowerBound);auto *LoadedUpperBound = dyn_cast<LoadInst>(Difference->getOperand(0));ASSERT_NE(LoadedUpperBound, nullptr);EXPECT_EQ(LoadedUpperBound->getPointerOperand(), PUpperBound);// The original loop iterator should only be used in the condition, in the// increment and in the statement that adds the lower bound to it.EXPECT_EQ(std::distance(IV->use_begin(), IV->use_end()), 3);// The exit block should contain the "fini" call and the barrier call,// plus the call to obtain the thread ID.size_t NumCallsInExitBlock =count_if(*ExitBlock, [](Instruction &I) { return isa<CallInst>(I); });EXPECT_EQ(NumCallsInExitBlock, 3u);}TEST_P(OpenMPIRBuilderTestWithIVBits, StaticChunkedWorkshareLoop) {unsigned IVBits = GetParam();using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);BasicBlock *Body;CallInst *Call;CanonicalLoopInfo *CLI =buildSingleLoopFunction(DL, OMPBuilder, IVBits, &Call, &Body);Instruction *OrigIndVar = CLI->getIndVar();EXPECT_EQ(Call->getOperand(1), OrigIndVar);Type *LCTy = Type::getInt32Ty(Ctx);Value *ChunkSize = ConstantInt::get(LCTy, 5);InsertPointTy AllocaIP{&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt()};OMPBuilder.applyWorkshareLoop(DL, CLI, AllocaIP, /*NeedsBarrier=*/true,OMP_SCHEDULE_Static, ChunkSize);OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));BasicBlock *Entry = &F->getEntryBlock();BasicBlock *Preheader = Entry->getSingleSuccessor();BasicBlock *DispatchPreheader = Preheader->getSingleSuccessor();BasicBlock *DispatchHeader = DispatchPreheader->getSingleSuccessor();BasicBlock *DispatchCond = DispatchHeader->getSingleSuccessor();BasicBlock *DispatchBody = succ_begin(DispatchCond)[0];BasicBlock *DispatchExit = succ_begin(DispatchCond)[1];BasicBlock *DispatchAfter = DispatchExit->getSingleSuccessor();BasicBlock *Return = DispatchAfter->getSingleSuccessor();BasicBlock *ChunkPreheader = DispatchBody->getSingleSuccessor();BasicBlock *ChunkHeader = ChunkPreheader->getSingleSuccessor();BasicBlock *ChunkCond = ChunkHeader->getSingleSuccessor();BasicBlock *ChunkBody = succ_begin(ChunkCond)[0];BasicBlock *ChunkExit = succ_begin(ChunkCond)[1];BasicBlock *ChunkInc = ChunkBody->getSingleSuccessor();BasicBlock *ChunkAfter = ChunkExit->getSingleSuccessor();BasicBlock *DispatchInc = ChunkAfter;EXPECT_EQ(ChunkBody, Body);EXPECT_EQ(ChunkInc->getSingleSuccessor(), ChunkHeader);EXPECT_EQ(DispatchInc->getSingleSuccessor(), DispatchHeader);EXPECT_TRUE(isa<ReturnInst>(Return->front()));Value *NewIV = Call->getOperand(1);EXPECT_EQ(NewIV->getType()->getScalarSizeInBits(), IVBits);CallInst *InitCall = findSingleCall(F,(IVBits > 32) ? omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u: omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u,OMPBuilder);EXPECT_EQ(InitCall->getParent(), Preheader);EXPECT_EQ(cast<ConstantInt>(InitCall->getArgOperand(2))->getSExtValue(), 33);EXPECT_EQ(cast<ConstantInt>(InitCall->getArgOperand(7))->getSExtValue(), 1);EXPECT_EQ(cast<ConstantInt>(InitCall->getArgOperand(8))->getSExtValue(), 5);CallInst *FiniCall = findSingleCall(F, omp::RuntimeFunction::OMPRTL___kmpc_for_static_fini, OMPBuilder);EXPECT_EQ(FiniCall->getParent(), DispatchExit);CallInst *BarrierCall = findSingleCall(F, omp::RuntimeFunction::OMPRTL___kmpc_barrier, OMPBuilder);EXPECT_EQ(BarrierCall->getParent(), DispatchExit);}INSTANTIATE_TEST_SUITE_P(IVBits, OpenMPIRBuilderTestWithIVBits,::testing::Values(8, 16, 32, 64));TEST_P(OpenMPIRBuilderTestWithParams, DynamicWorkShareLoop) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});omp::OMPScheduleType SchedType = GetParam();uint32_t ChunkSize = 1;switch (SchedType & ~OMPScheduleType::ModifierMask) {case omp::OMPScheduleType::BaseDynamicChunked:case omp::OMPScheduleType::BaseGuidedChunked:ChunkSize = 7;break;case omp::OMPScheduleType::BaseAuto:case omp::OMPScheduleType::BaseRuntime:ChunkSize = 1;break;default:assert(0 && "unknown type for this test");break;}Type *LCTy = Type::getInt32Ty(Ctx);Value *StartVal = ConstantInt::get(LCTy, 10);Value *StopVal = ConstantInt::get(LCTy, 52);Value *StepVal = ConstantInt::get(LCTy, 2);Value *ChunkVal =(ChunkSize == 1) ? nullptr : ConstantInt::get(LCTy, ChunkSize);auto LoopBodyGen = [&](InsertPointTy, llvm::Value *) {};CanonicalLoopInfo *CLI = OMPBuilder.createCanonicalLoop(Loc, LoopBodyGen, StartVal, StopVal, StepVal,/*IsSigned=*/false, /*InclusiveStop=*/false);Builder.SetInsertPoint(BB, BB->getFirstInsertionPt());InsertPointTy AllocaIP = Builder.saveIP();// Collect all the info from CLI, as it isn't usable after the call to// createDynamicWorkshareLoop.InsertPointTy AfterIP = CLI->getAfterIP();BasicBlock *Preheader = CLI->getPreheader();BasicBlock *ExitBlock = CLI->getExit();BasicBlock *LatchBlock = CLI->getLatch();Value *IV = CLI->getIndVar();InsertPointTy EndIP = OMPBuilder.applyWorkshareLoop(DL, CLI, AllocaIP, /*NeedsBarrier=*/true, getSchedKind(SchedType),ChunkVal, /*Simd=*/false,(SchedType & omp::OMPScheduleType::ModifierMonotonic) ==omp::OMPScheduleType::ModifierMonotonic,(SchedType & omp::OMPScheduleType::ModifierNonmonotonic) ==omp::OMPScheduleType::ModifierNonmonotonic,/*Ordered=*/false);// The returned value should be the "after" point.ASSERT_EQ(EndIP.getBlock(), AfterIP.getBlock());ASSERT_EQ(EndIP.getPoint(), AfterIP.getPoint());auto AllocaIter = BB->begin();ASSERT_GE(std::distance(BB->begin(), BB->end()), 4);AllocaInst *PLastIter = dyn_cast<AllocaInst>(&*(AllocaIter++));AllocaInst *PLowerBound = dyn_cast<AllocaInst>(&*(AllocaIter++));AllocaInst *PUpperBound = dyn_cast<AllocaInst>(&*(AllocaIter++));AllocaInst *PStride = dyn_cast<AllocaInst>(&*(AllocaIter++));EXPECT_NE(PLastIter, nullptr);EXPECT_NE(PLowerBound, nullptr);EXPECT_NE(PUpperBound, nullptr);EXPECT_NE(PStride, nullptr);auto PreheaderIter = Preheader->begin();ASSERT_GE(std::distance(Preheader->begin(), Preheader->end()), 6);StoreInst *LowerBoundStore = dyn_cast<StoreInst>(&*(PreheaderIter++));StoreInst *UpperBoundStore = dyn_cast<StoreInst>(&*(PreheaderIter++));StoreInst *StrideStore = dyn_cast<StoreInst>(&*(PreheaderIter++));ASSERT_NE(LowerBoundStore, nullptr);ASSERT_NE(UpperBoundStore, nullptr);ASSERT_NE(StrideStore, nullptr);CallInst *ThreadIdCall = dyn_cast<CallInst>(&*(PreheaderIter++));ASSERT_NE(ThreadIdCall, nullptr);EXPECT_EQ(ThreadIdCall->getCalledFunction()->getName(),"__kmpc_global_thread_num");CallInst *InitCall = dyn_cast<CallInst>(&*PreheaderIter);ASSERT_NE(InitCall, nullptr);EXPECT_EQ(InitCall->getCalledFunction()->getName(),"__kmpc_dispatch_init_4u");EXPECT_EQ(InitCall->arg_size(), 7U);EXPECT_EQ(InitCall->getArgOperand(6), ConstantInt::get(LCTy, ChunkSize));ConstantInt *SchedVal = cast<ConstantInt>(InitCall->getArgOperand(2));if ((SchedType & OMPScheduleType::MonotonicityMask) ==OMPScheduleType::None) {// Implementation is allowed to add default nonmonotonicity flagEXPECT_EQ(static_cast<OMPScheduleType>(SchedVal->getValue().getZExtValue()) |OMPScheduleType::ModifierNonmonotonic,SchedType | OMPScheduleType::ModifierNonmonotonic);} else {EXPECT_EQ(static_cast<OMPScheduleType>(SchedVal->getValue().getZExtValue()),SchedType);}ConstantInt *OrigLowerBound =dyn_cast<ConstantInt>(LowerBoundStore->getValueOperand());ConstantInt *OrigUpperBound =dyn_cast<ConstantInt>(UpperBoundStore->getValueOperand());ConstantInt *OrigStride =dyn_cast<ConstantInt>(StrideStore->getValueOperand());ASSERT_NE(OrigLowerBound, nullptr);ASSERT_NE(OrigUpperBound, nullptr);ASSERT_NE(OrigStride, nullptr);EXPECT_EQ(OrigLowerBound->getValue(), 1);EXPECT_EQ(OrigUpperBound->getValue(), 21);EXPECT_EQ(OrigStride->getValue(), 1);CallInst *FiniCall = dyn_cast<CallInst>(&*(LatchBlock->getTerminator()->getPrevNonDebugInstruction(true)));EXPECT_EQ(FiniCall, nullptr);// The original loop iterator should only be used in the condition, in the// increment and in the statement that adds the lower bound to it.EXPECT_EQ(std::distance(IV->use_begin(), IV->use_end()), 3);// The exit block should contain the barrier call, plus the call to obtain// the thread ID.size_t NumCallsInExitBlock =count_if(*ExitBlock, [](Instruction &I) { return isa<CallInst>(I); });EXPECT_EQ(NumCallsInExitBlock, 2u);// Add a termination to our block and check that it is internally consistent.Builder.restoreIP(EndIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}INSTANTIATE_TEST_SUITE_P(OpenMPWSLoopSchedulingTypes, OpenMPIRBuilderTestWithParams,::testing::Values(omp::OMPScheduleType::UnorderedDynamicChunked,omp::OMPScheduleType::UnorderedGuidedChunked,omp::OMPScheduleType::UnorderedAuto,omp::OMPScheduleType::UnorderedRuntime,omp::OMPScheduleType::UnorderedDynamicChunked |omp::OMPScheduleType::ModifierMonotonic,omp::OMPScheduleType::UnorderedDynamicChunked |omp::OMPScheduleType::ModifierNonmonotonic,omp::OMPScheduleType::UnorderedGuidedChunked |omp::OMPScheduleType::ModifierMonotonic,omp::OMPScheduleType::UnorderedGuidedChunked |omp::OMPScheduleType::ModifierNonmonotonic,omp::OMPScheduleType::UnorderedAuto |omp::OMPScheduleType::ModifierMonotonic,omp::OMPScheduleType::UnorderedRuntime |omp::OMPScheduleType::ModifierMonotonic));TEST_F(OpenMPIRBuilderTest, DynamicWorkShareLoopOrdered) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});uint32_t ChunkSize = 1;Type *LCTy = Type::getInt32Ty(Ctx);Value *StartVal = ConstantInt::get(LCTy, 10);Value *StopVal = ConstantInt::get(LCTy, 52);Value *StepVal = ConstantInt::get(LCTy, 2);Value *ChunkVal = ConstantInt::get(LCTy, ChunkSize);auto LoopBodyGen = [&](InsertPointTy, llvm::Value *) {};CanonicalLoopInfo *CLI = OMPBuilder.createCanonicalLoop(Loc, LoopBodyGen, StartVal, StopVal, StepVal,/*IsSigned=*/false, /*InclusiveStop=*/false);Builder.SetInsertPoint(BB, BB->getFirstInsertionPt());InsertPointTy AllocaIP = Builder.saveIP();// Collect all the info from CLI, as it isn't usable after the call to// createDynamicWorkshareLoop.BasicBlock *Preheader = CLI->getPreheader();BasicBlock *ExitBlock = CLI->getExit();BasicBlock *LatchBlock = CLI->getLatch();Value *IV = CLI->getIndVar();InsertPointTy EndIP = OMPBuilder.applyWorkshareLoop(DL, CLI, AllocaIP, /*NeedsBarrier=*/true, OMP_SCHEDULE_Static, ChunkVal,/*HasSimdModifier=*/false, /*HasMonotonicModifier=*/false,/*HasNonmonotonicModifier=*/false,/*HasOrderedClause=*/true);// Add a termination to our block and check that it is internally consistent.Builder.restoreIP(EndIP);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));CallInst *InitCall = nullptr;for (Instruction &EI : *Preheader) {Instruction *Cur = &EI;if (isa<CallInst>(Cur)) {InitCall = cast<CallInst>(Cur);if (InitCall->getCalledFunction()->getName() == "__kmpc_dispatch_init_4u")break;InitCall = nullptr;}}EXPECT_NE(InitCall, nullptr);EXPECT_EQ(InitCall->arg_size(), 7U);ConstantInt *SchedVal = cast<ConstantInt>(InitCall->getArgOperand(2));EXPECT_EQ(SchedVal->getValue(),static_cast<uint64_t>(OMPScheduleType::OrderedStaticChunked));CallInst *FiniCall = dyn_cast<CallInst>(&*(LatchBlock->getTerminator()->getPrevNonDebugInstruction(true)));ASSERT_NE(FiniCall, nullptr);EXPECT_EQ(FiniCall->getCalledFunction()->getName(),"__kmpc_dispatch_fini_4u");EXPECT_EQ(FiniCall->arg_size(), 2U);EXPECT_EQ(InitCall->getArgOperand(0), FiniCall->getArgOperand(0));EXPECT_EQ(InitCall->getArgOperand(1), FiniCall->getArgOperand(1));// The original loop iterator should only be used in the condition, in the// increment and in the statement that adds the lower bound to it.EXPECT_EQ(std::distance(IV->use_begin(), IV->use_end()), 3);// The exit block should contain the barrier call, plus the call to obtain// the thread ID.size_t NumCallsInExitBlock =count_if(*ExitBlock, [](Instruction &I) { return isa<CallInst>(I); });EXPECT_EQ(NumCallsInExitBlock, 2u);}TEST_F(OpenMPIRBuilderTest, MasterDirective) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI = nullptr;BasicBlock *EntryBB = nullptr;BasicBlock *ThenBB = nullptr;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {if (AllocaIP.isSet())Builder.restoreIP(AllocaIP);elseBuilder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt()));PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());Builder.CreateStore(F->arg_begin(), PrivAI);llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);Builder.restoreIP(CodeGenIP);// collect some info for checks laterThenBB = Builder.GetInsertBlock();EntryBB = ThenBB->getUniquePredecessor();// simple instructions for bodyValue *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto FiniCB = [&](InsertPointTy IP) {BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};Builder.restoreIP(OMPBuilder.createMaster(Builder, BodyGenCB, FiniCB));Value *EntryBBTI = EntryBB->getTerminator();EXPECT_NE(EntryBBTI, nullptr);EXPECT_TRUE(isa<BranchInst>(EntryBBTI));BranchInst *EntryBr = cast<BranchInst>(EntryBB->getTerminator());EXPECT_TRUE(EntryBr->isConditional());EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB);BasicBlock *ExitBB = ThenBB->getUniqueSuccessor();EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB);CmpInst *CondInst = cast<CmpInst>(EntryBr->getCondition());EXPECT_TRUE(isa<CallInst>(CondInst->getOperand(0)));CallInst *MasterEntryCI = cast<CallInst>(CondInst->getOperand(0));EXPECT_EQ(MasterEntryCI->arg_size(), 2U);EXPECT_EQ(MasterEntryCI->getCalledFunction()->getName(), "__kmpc_master");EXPECT_TRUE(isa<GlobalVariable>(MasterEntryCI->getArgOperand(0)));CallInst *MasterEndCI = nullptr;for (auto &FI : *ThenBB) {Instruction *cur = &FI;if (isa<CallInst>(cur)) {MasterEndCI = cast<CallInst>(cur);if (MasterEndCI->getCalledFunction()->getName() == "__kmpc_end_master")break;MasterEndCI = nullptr;}}EXPECT_NE(MasterEndCI, nullptr);EXPECT_EQ(MasterEndCI->arg_size(), 2U);EXPECT_TRUE(isa<GlobalVariable>(MasterEndCI->getArgOperand(0)));EXPECT_EQ(MasterEndCI->getArgOperand(1), MasterEntryCI->getArgOperand(1));}TEST_F(OpenMPIRBuilderTest, MaskedDirective) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI = nullptr;BasicBlock *EntryBB = nullptr;BasicBlock *ThenBB = nullptr;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {if (AllocaIP.isSet())Builder.restoreIP(AllocaIP);elseBuilder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt()));PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());Builder.CreateStore(F->arg_begin(), PrivAI);llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);Builder.restoreIP(CodeGenIP);// collect some info for checks laterThenBB = Builder.GetInsertBlock();EntryBB = ThenBB->getUniquePredecessor();// simple instructions for bodyValue *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto FiniCB = [&](InsertPointTy IP) {BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};Constant *Filter = ConstantInt::get(Type::getInt32Ty(M->getContext()), 0);Builder.restoreIP(OMPBuilder.createMasked(Builder, BodyGenCB, FiniCB, Filter));Value *EntryBBTI = EntryBB->getTerminator();EXPECT_NE(EntryBBTI, nullptr);EXPECT_TRUE(isa<BranchInst>(EntryBBTI));BranchInst *EntryBr = cast<BranchInst>(EntryBB->getTerminator());EXPECT_TRUE(EntryBr->isConditional());EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB);BasicBlock *ExitBB = ThenBB->getUniqueSuccessor();EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB);CmpInst *CondInst = cast<CmpInst>(EntryBr->getCondition());EXPECT_TRUE(isa<CallInst>(CondInst->getOperand(0)));CallInst *MaskedEntryCI = cast<CallInst>(CondInst->getOperand(0));EXPECT_EQ(MaskedEntryCI->arg_size(), 3U);EXPECT_EQ(MaskedEntryCI->getCalledFunction()->getName(), "__kmpc_masked");EXPECT_TRUE(isa<GlobalVariable>(MaskedEntryCI->getArgOperand(0)));CallInst *MaskedEndCI = nullptr;for (auto &FI : *ThenBB) {Instruction *cur = &FI;if (isa<CallInst>(cur)) {MaskedEndCI = cast<CallInst>(cur);if (MaskedEndCI->getCalledFunction()->getName() == "__kmpc_end_masked")break;MaskedEndCI = nullptr;}}EXPECT_NE(MaskedEndCI, nullptr);EXPECT_EQ(MaskedEndCI->arg_size(), 2U);EXPECT_TRUE(isa<GlobalVariable>(MaskedEndCI->getArgOperand(0)));EXPECT_EQ(MaskedEndCI->getArgOperand(1), MaskedEntryCI->getArgOperand(1));}TEST_F(OpenMPIRBuilderTest, CriticalDirective) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {// actual start for bodyCBllvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);// body beginBuilder.restoreIP(CodeGenIP);Builder.CreateStore(F->arg_begin(), PrivAI);Value *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto FiniCB = [&](InsertPointTy IP) {BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};BasicBlock *EntryBB = Builder.GetInsertBlock();Builder.restoreIP(OMPBuilder.createCritical(Builder, BodyGenCB, FiniCB,"testCRT", nullptr));CallInst *CriticalEntryCI = nullptr;for (auto &EI : *EntryBB) {Instruction *cur = &EI;if (isa<CallInst>(cur)) {CriticalEntryCI = cast<CallInst>(cur);if (CriticalEntryCI->getCalledFunction()->getName() == "__kmpc_critical")break;CriticalEntryCI = nullptr;}}EXPECT_NE(CriticalEntryCI, nullptr);EXPECT_EQ(CriticalEntryCI->arg_size(), 3U);EXPECT_EQ(CriticalEntryCI->getCalledFunction()->getName(), "__kmpc_critical");EXPECT_TRUE(isa<GlobalVariable>(CriticalEntryCI->getArgOperand(0)));CallInst *CriticalEndCI = nullptr;for (auto &FI : *EntryBB) {Instruction *cur = &FI;if (isa<CallInst>(cur)) {CriticalEndCI = cast<CallInst>(cur);if (CriticalEndCI->getCalledFunction()->getName() =="__kmpc_end_critical")break;CriticalEndCI = nullptr;}}EXPECT_NE(CriticalEndCI, nullptr);EXPECT_EQ(CriticalEndCI->arg_size(), 3U);EXPECT_TRUE(isa<GlobalVariable>(CriticalEndCI->getArgOperand(0)));EXPECT_EQ(CriticalEndCI->getArgOperand(1), CriticalEntryCI->getArgOperand(1));PointerType *CriticalNamePtrTy =PointerType::getUnqual(ArrayType::get(Type::getInt32Ty(Ctx), 8));EXPECT_EQ(CriticalEndCI->getArgOperand(2), CriticalEntryCI->getArgOperand(2));EXPECT_EQ(CriticalEndCI->getArgOperand(2)->getType(), CriticalNamePtrTy);}TEST_F(OpenMPIRBuilderTest, OrderedDirectiveDependSource) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);LLVMContext &Ctx = M->getContext();OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});InsertPointTy AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());unsigned NumLoops = 2;SmallVector<Value *, 2> StoreValues;Type *LCTy = Type::getInt64Ty(Ctx);StoreValues.emplace_back(ConstantInt::get(LCTy, 1));StoreValues.emplace_back(ConstantInt::get(LCTy, 2));// Test for "#omp ordered depend(source)"Builder.restoreIP(OMPBuilder.createOrderedDepend(Builder, AllocaIP, NumLoops,StoreValues, ".cnt.addr",/*IsDependSource=*/true));Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));AllocaInst *AllocInst = dyn_cast<AllocaInst>(&BB->front());ASSERT_NE(AllocInst, nullptr);ArrayType *ArrType = dyn_cast<ArrayType>(AllocInst->getAllocatedType());EXPECT_EQ(ArrType->getNumElements(), NumLoops);EXPECT_TRUE(AllocInst->getAllocatedType()->getArrayElementType()->isIntegerTy(64));Instruction *IterInst = dyn_cast<Instruction>(AllocInst);for (unsigned Iter = 0; Iter < NumLoops; Iter++) {GetElementPtrInst *DependAddrGEPIter =dyn_cast<GetElementPtrInst>(IterInst->getNextNode());ASSERT_NE(DependAddrGEPIter, nullptr);EXPECT_EQ(DependAddrGEPIter->getPointerOperand(), AllocInst);EXPECT_EQ(DependAddrGEPIter->getNumIndices(), (unsigned)2);auto *FirstIdx = dyn_cast<ConstantInt>(DependAddrGEPIter->getOperand(1));auto *SecondIdx = dyn_cast<ConstantInt>(DependAddrGEPIter->getOperand(2));ASSERT_NE(FirstIdx, nullptr);ASSERT_NE(SecondIdx, nullptr);EXPECT_EQ(FirstIdx->getValue(), 0);EXPECT_EQ(SecondIdx->getValue(), Iter);StoreInst *StoreValue =dyn_cast<StoreInst>(DependAddrGEPIter->getNextNode());ASSERT_NE(StoreValue, nullptr);EXPECT_EQ(StoreValue->getValueOperand(), StoreValues[Iter]);EXPECT_EQ(StoreValue->getPointerOperand(), DependAddrGEPIter);EXPECT_EQ(StoreValue->getAlign(), Align(8));IterInst = dyn_cast<Instruction>(StoreValue);}GetElementPtrInst *DependBaseAddrGEP =dyn_cast<GetElementPtrInst>(IterInst->getNextNode());ASSERT_NE(DependBaseAddrGEP, nullptr);EXPECT_EQ(DependBaseAddrGEP->getPointerOperand(), AllocInst);EXPECT_EQ(DependBaseAddrGEP->getNumIndices(), (unsigned)2);auto *FirstIdx = dyn_cast<ConstantInt>(DependBaseAddrGEP->getOperand(1));auto *SecondIdx = dyn_cast<ConstantInt>(DependBaseAddrGEP->getOperand(2));ASSERT_NE(FirstIdx, nullptr);ASSERT_NE(SecondIdx, nullptr);EXPECT_EQ(FirstIdx->getValue(), 0);EXPECT_EQ(SecondIdx->getValue(), 0);CallInst *GTID = dyn_cast<CallInst>(DependBaseAddrGEP->getNextNode());ASSERT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory());CallInst *Depend = dyn_cast<CallInst>(GTID->getNextNode());ASSERT_NE(Depend, nullptr);EXPECT_EQ(Depend->arg_size(), 3U);EXPECT_EQ(Depend->getCalledFunction()->getName(), "__kmpc_doacross_post");EXPECT_TRUE(isa<GlobalVariable>(Depend->getArgOperand(0)));EXPECT_EQ(Depend->getArgOperand(1), GTID);EXPECT_EQ(Depend->getArgOperand(2), DependBaseAddrGEP);}TEST_F(OpenMPIRBuilderTest, OrderedDirectiveDependSink) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);LLVMContext &Ctx = M->getContext();OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});InsertPointTy AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());unsigned NumLoops = 2;SmallVector<Value *, 2> StoreValues;Type *LCTy = Type::getInt64Ty(Ctx);StoreValues.emplace_back(ConstantInt::get(LCTy, 1));StoreValues.emplace_back(ConstantInt::get(LCTy, 2));// Test for "#omp ordered depend(sink: vec)"Builder.restoreIP(OMPBuilder.createOrderedDepend(Builder, AllocaIP, NumLoops,StoreValues, ".cnt.addr",/*IsDependSource=*/false));Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));AllocaInst *AllocInst = dyn_cast<AllocaInst>(&BB->front());ASSERT_NE(AllocInst, nullptr);ArrayType *ArrType = dyn_cast<ArrayType>(AllocInst->getAllocatedType());EXPECT_EQ(ArrType->getNumElements(), NumLoops);EXPECT_TRUE(AllocInst->getAllocatedType()->getArrayElementType()->isIntegerTy(64));Instruction *IterInst = dyn_cast<Instruction>(AllocInst);for (unsigned Iter = 0; Iter < NumLoops; Iter++) {GetElementPtrInst *DependAddrGEPIter =dyn_cast<GetElementPtrInst>(IterInst->getNextNode());ASSERT_NE(DependAddrGEPIter, nullptr);EXPECT_EQ(DependAddrGEPIter->getPointerOperand(), AllocInst);EXPECT_EQ(DependAddrGEPIter->getNumIndices(), (unsigned)2);auto *FirstIdx = dyn_cast<ConstantInt>(DependAddrGEPIter->getOperand(1));auto *SecondIdx = dyn_cast<ConstantInt>(DependAddrGEPIter->getOperand(2));ASSERT_NE(FirstIdx, nullptr);ASSERT_NE(SecondIdx, nullptr);EXPECT_EQ(FirstIdx->getValue(), 0);EXPECT_EQ(SecondIdx->getValue(), Iter);StoreInst *StoreValue =dyn_cast<StoreInst>(DependAddrGEPIter->getNextNode());ASSERT_NE(StoreValue, nullptr);EXPECT_EQ(StoreValue->getValueOperand(), StoreValues[Iter]);EXPECT_EQ(StoreValue->getPointerOperand(), DependAddrGEPIter);EXPECT_EQ(StoreValue->getAlign(), Align(8));IterInst = dyn_cast<Instruction>(StoreValue);}GetElementPtrInst *DependBaseAddrGEP =dyn_cast<GetElementPtrInst>(IterInst->getNextNode());ASSERT_NE(DependBaseAddrGEP, nullptr);EXPECT_EQ(DependBaseAddrGEP->getPointerOperand(), AllocInst);EXPECT_EQ(DependBaseAddrGEP->getNumIndices(), (unsigned)2);auto *FirstIdx = dyn_cast<ConstantInt>(DependBaseAddrGEP->getOperand(1));auto *SecondIdx = dyn_cast<ConstantInt>(DependBaseAddrGEP->getOperand(2));ASSERT_NE(FirstIdx, nullptr);ASSERT_NE(SecondIdx, nullptr);EXPECT_EQ(FirstIdx->getValue(), 0);EXPECT_EQ(SecondIdx->getValue(), 0);CallInst *GTID = dyn_cast<CallInst>(DependBaseAddrGEP->getNextNode());ASSERT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num");EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory());EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory());CallInst *Depend = dyn_cast<CallInst>(GTID->getNextNode());ASSERT_NE(Depend, nullptr);EXPECT_EQ(Depend->arg_size(), 3U);EXPECT_EQ(Depend->getCalledFunction()->getName(), "__kmpc_doacross_wait");EXPECT_TRUE(isa<GlobalVariable>(Depend->getArgOperand(0)));EXPECT_EQ(Depend->getArgOperand(1), GTID);EXPECT_EQ(Depend->getArgOperand(2), DependBaseAddrGEP);}TEST_F(OpenMPIRBuilderTest, OrderedDirectiveThreads) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI =Builder.CreateAlloca(F->arg_begin()->getType(), nullptr, "priv.inst");auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);Builder.restoreIP(CodeGenIP);Builder.CreateStore(F->arg_begin(), PrivAI);Value *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto FiniCB = [&](InsertPointTy IP) {BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};// Test for "#omp ordered [threads]"BasicBlock *EntryBB = Builder.GetInsertBlock();Builder.restoreIP(OMPBuilder.createOrderedThreadsSimd(Builder, BodyGenCB, FiniCB, true));Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_NE(EntryBB->getTerminator(), nullptr);CallInst *OrderedEntryCI = nullptr;for (auto &EI : *EntryBB) {Instruction *Cur = &EI;if (isa<CallInst>(Cur)) {OrderedEntryCI = cast<CallInst>(Cur);if (OrderedEntryCI->getCalledFunction()->getName() == "__kmpc_ordered")break;OrderedEntryCI = nullptr;}}EXPECT_NE(OrderedEntryCI, nullptr);EXPECT_EQ(OrderedEntryCI->arg_size(), 2U);EXPECT_EQ(OrderedEntryCI->getCalledFunction()->getName(), "__kmpc_ordered");EXPECT_TRUE(isa<GlobalVariable>(OrderedEntryCI->getArgOperand(0)));CallInst *OrderedEndCI = nullptr;for (auto &FI : *EntryBB) {Instruction *Cur = &FI;if (isa<CallInst>(Cur)) {OrderedEndCI = cast<CallInst>(Cur);if (OrderedEndCI->getCalledFunction()->getName() == "__kmpc_end_ordered")break;OrderedEndCI = nullptr;}}EXPECT_NE(OrderedEndCI, nullptr);EXPECT_EQ(OrderedEndCI->arg_size(), 2U);EXPECT_TRUE(isa<GlobalVariable>(OrderedEndCI->getArgOperand(0)));EXPECT_EQ(OrderedEndCI->getArgOperand(1), OrderedEntryCI->getArgOperand(1));}TEST_F(OpenMPIRBuilderTest, OrderedDirectiveSimd) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI =Builder.CreateAlloca(F->arg_begin()->getType(), nullptr, "priv.inst");auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);Builder.restoreIP(CodeGenIP);Builder.CreateStore(F->arg_begin(), PrivAI);Value *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto FiniCB = [&](InsertPointTy IP) {BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};// Test for "#omp ordered simd"BasicBlock *EntryBB = Builder.GetInsertBlock();Builder.restoreIP(OMPBuilder.createOrderedThreadsSimd(Builder, BodyGenCB, FiniCB, false));Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_NE(EntryBB->getTerminator(), nullptr);CallInst *OrderedEntryCI = nullptr;for (auto &EI : *EntryBB) {Instruction *Cur = &EI;if (isa<CallInst>(Cur)) {OrderedEntryCI = cast<CallInst>(Cur);if (OrderedEntryCI->getCalledFunction()->getName() == "__kmpc_ordered")break;OrderedEntryCI = nullptr;}}EXPECT_EQ(OrderedEntryCI, nullptr);CallInst *OrderedEndCI = nullptr;for (auto &FI : *EntryBB) {Instruction *Cur = &FI;if (isa<CallInst>(Cur)) {OrderedEndCI = cast<CallInst>(Cur);if (OrderedEndCI->getCalledFunction()->getName() == "__kmpc_end_ordered")break;OrderedEndCI = nullptr;}}EXPECT_EQ(OrderedEndCI, nullptr);}TEST_F(OpenMPIRBuilderTest, CopyinBlocks) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});IntegerType *Int32 = Type::getInt32Ty(M->getContext());AllocaInst *MasterAddress = Builder.CreateAlloca(Int32->getPointerTo());AllocaInst *PrivAddress = Builder.CreateAlloca(Int32->getPointerTo());BasicBlock *EntryBB = BB;OMPBuilder.createCopyinClauseBlocks(Builder.saveIP(), MasterAddress,PrivAddress, Int32, /*BranchtoEnd*/ true);BranchInst *EntryBr = dyn_cast_or_null<BranchInst>(EntryBB->getTerminator());EXPECT_NE(EntryBr, nullptr);EXPECT_TRUE(EntryBr->isConditional());BasicBlock *NotMasterBB = EntryBr->getSuccessor(0);BasicBlock *CopyinEnd = EntryBr->getSuccessor(1);CmpInst *CMP = dyn_cast_or_null<CmpInst>(EntryBr->getCondition());EXPECT_NE(CMP, nullptr);EXPECT_NE(NotMasterBB, nullptr);EXPECT_NE(CopyinEnd, nullptr);BranchInst *NotMasterBr =dyn_cast_or_null<BranchInst>(NotMasterBB->getTerminator());EXPECT_NE(NotMasterBr, nullptr);EXPECT_FALSE(NotMasterBr->isConditional());EXPECT_EQ(CopyinEnd, NotMasterBr->getSuccessor(0));}TEST_F(OpenMPIRBuilderTest, SingleDirective) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI = nullptr;BasicBlock *EntryBB = nullptr;BasicBlock *ThenBB = nullptr;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {if (AllocaIP.isSet())Builder.restoreIP(AllocaIP);elseBuilder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt()));PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());Builder.CreateStore(F->arg_begin(), PrivAI);llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);Builder.restoreIP(CodeGenIP);// collect some info for checks laterThenBB = Builder.GetInsertBlock();EntryBB = ThenBB->getUniquePredecessor();// simple instructions for bodyValue *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto FiniCB = [&](InsertPointTy IP) {BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};Builder.restoreIP(OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB, /*IsNowait*/ false, /*DidIt*/ nullptr));Value *EntryBBTI = EntryBB->getTerminator();EXPECT_NE(EntryBBTI, nullptr);EXPECT_TRUE(isa<BranchInst>(EntryBBTI));BranchInst *EntryBr = cast<BranchInst>(EntryBB->getTerminator());EXPECT_TRUE(EntryBr->isConditional());EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB);BasicBlock *ExitBB = ThenBB->getUniqueSuccessor();EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB);CmpInst *CondInst = cast<CmpInst>(EntryBr->getCondition());EXPECT_TRUE(isa<CallInst>(CondInst->getOperand(0)));CallInst *SingleEntryCI = cast<CallInst>(CondInst->getOperand(0));EXPECT_EQ(SingleEntryCI->arg_size(), 2U);EXPECT_EQ(SingleEntryCI->getCalledFunction()->getName(), "__kmpc_single");EXPECT_TRUE(isa<GlobalVariable>(SingleEntryCI->getArgOperand(0)));CallInst *SingleEndCI = nullptr;for (auto &FI : *ThenBB) {Instruction *cur = &FI;if (isa<CallInst>(cur)) {SingleEndCI = cast<CallInst>(cur);if (SingleEndCI->getCalledFunction()->getName() == "__kmpc_end_single")break;SingleEndCI = nullptr;}}EXPECT_NE(SingleEndCI, nullptr);EXPECT_EQ(SingleEndCI->arg_size(), 2U);EXPECT_TRUE(isa<GlobalVariable>(SingleEndCI->getArgOperand(0)));EXPECT_EQ(SingleEndCI->getArgOperand(1), SingleEntryCI->getArgOperand(1));bool FoundBarrier = false;for (auto &FI : *ExitBB) {Instruction *cur = &FI;if (auto CI = dyn_cast<CallInst>(cur)) {if (CI->getCalledFunction()->getName() == "__kmpc_barrier") {FoundBarrier = true;break;}}}EXPECT_TRUE(FoundBarrier);}TEST_F(OpenMPIRBuilderTest, SingleDirectiveNowait) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});AllocaInst *PrivAI = nullptr;BasicBlock *EntryBB = nullptr;BasicBlock *ThenBB = nullptr;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {if (AllocaIP.isSet())Builder.restoreIP(AllocaIP);elseBuilder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt()));PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());Builder.CreateStore(F->arg_begin(), PrivAI);llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);Builder.restoreIP(CodeGenIP);// collect some info for checks laterThenBB = Builder.GetInsertBlock();EntryBB = ThenBB->getUniquePredecessor();// simple instructions for bodyValue *PrivLoad =Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto FiniCB = [&](InsertPointTy IP) {BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};Builder.restoreIP(OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB, /*IsNowait*/ true, /*DidIt*/ nullptr));Value *EntryBBTI = EntryBB->getTerminator();EXPECT_NE(EntryBBTI, nullptr);EXPECT_TRUE(isa<BranchInst>(EntryBBTI));BranchInst *EntryBr = cast<BranchInst>(EntryBB->getTerminator());EXPECT_TRUE(EntryBr->isConditional());EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB);BasicBlock *ExitBB = ThenBB->getUniqueSuccessor();EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB);CmpInst *CondInst = cast<CmpInst>(EntryBr->getCondition());EXPECT_TRUE(isa<CallInst>(CondInst->getOperand(0)));CallInst *SingleEntryCI = cast<CallInst>(CondInst->getOperand(0));EXPECT_EQ(SingleEntryCI->arg_size(), 2U);EXPECT_EQ(SingleEntryCI->getCalledFunction()->getName(), "__kmpc_single");EXPECT_TRUE(isa<GlobalVariable>(SingleEntryCI->getArgOperand(0)));CallInst *SingleEndCI = nullptr;for (auto &FI : *ThenBB) {Instruction *cur = &FI;if (isa<CallInst>(cur)) {SingleEndCI = cast<CallInst>(cur);if (SingleEndCI->getCalledFunction()->getName() == "__kmpc_end_single")break;SingleEndCI = nullptr;}}EXPECT_NE(SingleEndCI, nullptr);EXPECT_EQ(SingleEndCI->arg_size(), 2U);EXPECT_TRUE(isa<GlobalVariable>(SingleEndCI->getArgOperand(0)));EXPECT_EQ(SingleEndCI->getArgOperand(1), SingleEntryCI->getArgOperand(1));CallInst *ExitBarrier = nullptr;for (auto &FI : *ExitBB) {Instruction *cur = &FI;if (auto CI = dyn_cast<CallInst>(cur)) {if (CI->getCalledFunction()->getName() == "__kmpc_barrier") {ExitBarrier = CI;break;}}}EXPECT_EQ(ExitBarrier, nullptr);}TEST_F(OpenMPIRBuilderTest, OMPAtomicReadFlt) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Type *Float32 = Type::getFloatTy(M->getContext());AllocaInst *XVal = Builder.CreateAlloca(Float32);XVal->setName("AtomicVar");AllocaInst *VVal = Builder.CreateAlloca(Float32);VVal->setName("AtomicRead");AtomicOrdering AO = AtomicOrdering::Monotonic;OpenMPIRBuilder::AtomicOpValue X = {XVal, Float32, false, false};OpenMPIRBuilder::AtomicOpValue V = {VVal, Float32, false, false};Builder.restoreIP(OMPBuilder.createAtomicRead(Loc, X, V, AO));IntegerType *IntCastTy =IntegerType::get(M->getContext(), Float32->getScalarSizeInBits());BitCastInst *CastFrmFlt = cast<BitCastInst>(VVal->getNextNode());EXPECT_EQ(CastFrmFlt->getSrcTy(), Float32->getPointerTo());EXPECT_EQ(CastFrmFlt->getDestTy(), IntCastTy->getPointerTo());EXPECT_EQ(CastFrmFlt->getOperand(0), XVal);LoadInst *AtomicLoad = cast<LoadInst>(CastFrmFlt->getNextNode());EXPECT_TRUE(AtomicLoad->isAtomic());EXPECT_EQ(AtomicLoad->getPointerOperand(), CastFrmFlt);BitCastInst *CastToFlt = cast<BitCastInst>(AtomicLoad->getNextNode());EXPECT_EQ(CastToFlt->getSrcTy(), IntCastTy);EXPECT_EQ(CastToFlt->getDestTy(), Float32);EXPECT_EQ(CastToFlt->getOperand(0), AtomicLoad);StoreInst *StoreofAtomic = cast<StoreInst>(CastToFlt->getNextNode());EXPECT_EQ(StoreofAtomic->getValueOperand(), CastToFlt);EXPECT_EQ(StoreofAtomic->getPointerOperand(), VVal);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicReadInt) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});IntegerType *Int32 = Type::getInt32Ty(M->getContext());AllocaInst *XVal = Builder.CreateAlloca(Int32);XVal->setName("AtomicVar");AllocaInst *VVal = Builder.CreateAlloca(Int32);VVal->setName("AtomicRead");AtomicOrdering AO = AtomicOrdering::Monotonic;OpenMPIRBuilder::AtomicOpValue X = {XVal, Int32, false, false};OpenMPIRBuilder::AtomicOpValue V = {VVal, Int32, false, false};BasicBlock *EntryBB = BB;Builder.restoreIP(OMPBuilder.createAtomicRead(Loc, X, V, AO));LoadInst *AtomicLoad = nullptr;StoreInst *StoreofAtomic = nullptr;for (Instruction &Cur : *EntryBB) {if (isa<LoadInst>(Cur)) {AtomicLoad = cast<LoadInst>(&Cur);if (AtomicLoad->getPointerOperand() == XVal)continue;AtomicLoad = nullptr;} else if (isa<StoreInst>(Cur)) {StoreofAtomic = cast<StoreInst>(&Cur);if (StoreofAtomic->getPointerOperand() == VVal)continue;StoreofAtomic = nullptr;}}EXPECT_NE(AtomicLoad, nullptr);EXPECT_TRUE(AtomicLoad->isAtomic());EXPECT_NE(StoreofAtomic, nullptr);EXPECT_EQ(StoreofAtomic->getValueOperand(), AtomicLoad);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicWriteFlt) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});LLVMContext &Ctx = M->getContext();Type *Float32 = Type::getFloatTy(Ctx);AllocaInst *XVal = Builder.CreateAlloca(Float32);XVal->setName("AtomicVar");OpenMPIRBuilder::AtomicOpValue X = {XVal, Float32, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;Constant *ValToWrite = ConstantFP::get(Float32, 1.0);Builder.restoreIP(OMPBuilder.createAtomicWrite(Loc, X, ValToWrite, AO));IntegerType *IntCastTy =IntegerType::get(M->getContext(), Float32->getScalarSizeInBits());BitCastInst *CastFrmFlt = cast<BitCastInst>(XVal->getNextNode());EXPECT_EQ(CastFrmFlt->getSrcTy(), Float32->getPointerTo());EXPECT_EQ(CastFrmFlt->getDestTy(), IntCastTy->getPointerTo());EXPECT_EQ(CastFrmFlt->getOperand(0), XVal);Value *ExprCast = Builder.CreateBitCast(ValToWrite, IntCastTy);StoreInst *StoreofAtomic = cast<StoreInst>(CastFrmFlt->getNextNode());EXPECT_EQ(StoreofAtomic->getValueOperand(), ExprCast);EXPECT_EQ(StoreofAtomic->getPointerOperand(), CastFrmFlt);EXPECT_TRUE(StoreofAtomic->isAtomic());Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicWriteInt) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});LLVMContext &Ctx = M->getContext();IntegerType *Int32 = Type::getInt32Ty(Ctx);AllocaInst *XVal = Builder.CreateAlloca(Int32);XVal->setName("AtomicVar");OpenMPIRBuilder::AtomicOpValue X = {XVal, Int32, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;ConstantInt *ValToWrite = ConstantInt::get(Type::getInt32Ty(Ctx), 1U);BasicBlock *EntryBB = BB;Builder.restoreIP(OMPBuilder.createAtomicWrite(Loc, X, ValToWrite, AO));StoreInst *StoreofAtomic = nullptr;for (Instruction &Cur : *EntryBB) {if (isa<StoreInst>(Cur)) {StoreofAtomic = cast<StoreInst>(&Cur);if (StoreofAtomic->getPointerOperand() == XVal)continue;StoreofAtomic = nullptr;}}EXPECT_NE(StoreofAtomic, nullptr);EXPECT_TRUE(StoreofAtomic->isAtomic());EXPECT_EQ(StoreofAtomic->getValueOperand(), ValToWrite);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicUpdate) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});IntegerType *Int32 = Type::getInt32Ty(M->getContext());AllocaInst *XVal = Builder.CreateAlloca(Int32);XVal->setName("AtomicVar");Builder.CreateStore(ConstantInt::get(Type::getInt32Ty(Ctx), 0U), XVal);OpenMPIRBuilder::AtomicOpValue X = {XVal, Int32, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;ConstantInt *ConstVal = ConstantInt::get(Type::getInt32Ty(Ctx), 1U);Value *Expr = nullptr;AtomicRMWInst::BinOp RMWOp = AtomicRMWInst::Sub;bool IsXLHSInRHSPart = false;BasicBlock *EntryBB = BB;OpenMPIRBuilder::InsertPointTy AllocaIP(EntryBB,EntryBB->getFirstInsertionPt());Value *Sub = nullptr;auto UpdateOp = [&](Value *Atomic, IRBuilder<> &IRB) {Sub = IRB.CreateSub(ConstVal, Atomic);return Sub;};Builder.restoreIP(OMPBuilder.createAtomicUpdate(Builder, AllocaIP, X, Expr, AO, RMWOp, UpdateOp, IsXLHSInRHSPart));BasicBlock *ContBB = EntryBB->getSingleSuccessor();BranchInst *ContTI = dyn_cast<BranchInst>(ContBB->getTerminator());EXPECT_NE(ContTI, nullptr);BasicBlock *EndBB = ContTI->getSuccessor(0);EXPECT_TRUE(ContTI->isConditional());EXPECT_EQ(ContTI->getSuccessor(1), ContBB);EXPECT_NE(EndBB, nullptr);PHINode *Phi = dyn_cast<PHINode>(&ContBB->front());EXPECT_NE(Phi, nullptr);EXPECT_EQ(Phi->getNumIncomingValues(), 2U);EXPECT_EQ(Phi->getIncomingBlock(0), EntryBB);EXPECT_EQ(Phi->getIncomingBlock(1), ContBB);EXPECT_EQ(Sub->getNumUses(), 1U);StoreInst *St = dyn_cast<StoreInst>(Sub->user_back());AllocaInst *UpdateTemp = dyn_cast<AllocaInst>(St->getPointerOperand());ExtractValueInst *ExVI1 =dyn_cast<ExtractValueInst>(Phi->getIncomingValueForBlock(ContBB));EXPECT_NE(ExVI1, nullptr);AtomicCmpXchgInst *CmpExchg =dyn_cast<AtomicCmpXchgInst>(ExVI1->getAggregateOperand());EXPECT_NE(CmpExchg, nullptr);EXPECT_EQ(CmpExchg->getPointerOperand(), XVal);EXPECT_EQ(CmpExchg->getCompareOperand(), Phi);EXPECT_EQ(CmpExchg->getSuccessOrdering(), AtomicOrdering::Monotonic);LoadInst *Ld = dyn_cast<LoadInst>(CmpExchg->getNewValOperand());EXPECT_NE(Ld, nullptr);EXPECT_EQ(UpdateTemp, Ld->getPointerOperand());Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicUpdateFloat) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Type *FloatTy = Type::getFloatTy(M->getContext());AllocaInst *XVal = Builder.CreateAlloca(FloatTy);XVal->setName("AtomicVar");Builder.CreateStore(ConstantFP::get(Type::getFloatTy(Ctx), 0.0), XVal);OpenMPIRBuilder::AtomicOpValue X = {XVal, FloatTy, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;Constant *ConstVal = ConstantFP::get(Type::getFloatTy(Ctx), 1.0);Value *Expr = nullptr;AtomicRMWInst::BinOp RMWOp = AtomicRMWInst::FSub;bool IsXLHSInRHSPart = false;BasicBlock *EntryBB = BB;OpenMPIRBuilder::InsertPointTy AllocaIP(EntryBB,EntryBB->getFirstInsertionPt());Value *Sub = nullptr;auto UpdateOp = [&](Value *Atomic, IRBuilder<> &IRB) {Sub = IRB.CreateFSub(ConstVal, Atomic);return Sub;};Builder.restoreIP(OMPBuilder.createAtomicUpdate(Builder, AllocaIP, X, Expr, AO, RMWOp, UpdateOp, IsXLHSInRHSPart));BasicBlock *ContBB = EntryBB->getSingleSuccessor();BranchInst *ContTI = dyn_cast<BranchInst>(ContBB->getTerminator());EXPECT_NE(ContTI, nullptr);BasicBlock *EndBB = ContTI->getSuccessor(0);EXPECT_TRUE(ContTI->isConditional());EXPECT_EQ(ContTI->getSuccessor(1), ContBB);EXPECT_NE(EndBB, nullptr);PHINode *Phi = dyn_cast<PHINode>(&ContBB->front());EXPECT_NE(Phi, nullptr);EXPECT_EQ(Phi->getNumIncomingValues(), 2U);EXPECT_EQ(Phi->getIncomingBlock(0), EntryBB);EXPECT_EQ(Phi->getIncomingBlock(1), ContBB);EXPECT_EQ(Sub->getNumUses(), 1U);StoreInst *St = dyn_cast<StoreInst>(Sub->user_back());AllocaInst *UpdateTemp = dyn_cast<AllocaInst>(St->getPointerOperand());ExtractValueInst *ExVI1 =dyn_cast<ExtractValueInst>(Phi->getIncomingValueForBlock(ContBB));EXPECT_NE(ExVI1, nullptr);AtomicCmpXchgInst *CmpExchg =dyn_cast<AtomicCmpXchgInst>(ExVI1->getAggregateOperand());EXPECT_NE(CmpExchg, nullptr);BitCastInst *BitCastNew =dyn_cast<BitCastInst>(CmpExchg->getPointerOperand());EXPECT_NE(BitCastNew, nullptr);EXPECT_EQ(BitCastNew->getOperand(0), XVal);EXPECT_EQ(CmpExchg->getCompareOperand(), Phi);EXPECT_EQ(CmpExchg->getSuccessOrdering(), AtomicOrdering::Monotonic);LoadInst *Ld = dyn_cast<LoadInst>(CmpExchg->getNewValOperand());EXPECT_NE(Ld, nullptr);BitCastInst *BitCastOld = dyn_cast<BitCastInst>(Ld->getPointerOperand());EXPECT_NE(BitCastOld, nullptr);EXPECT_EQ(UpdateTemp, BitCastOld->getOperand(0));Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicUpdateIntr) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});Type *IntTy = Type::getInt32Ty(M->getContext());AllocaInst *XVal = Builder.CreateAlloca(IntTy);XVal->setName("AtomicVar");Builder.CreateStore(ConstantInt::get(Type::getInt32Ty(Ctx), 0), XVal);OpenMPIRBuilder::AtomicOpValue X = {XVal, IntTy, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;Constant *ConstVal = ConstantInt::get(Type::getInt32Ty(Ctx), 1);Value *Expr = ConstantInt::get(Type::getInt32Ty(Ctx), 1);AtomicRMWInst::BinOp RMWOp = AtomicRMWInst::UMax;bool IsXLHSInRHSPart = false;BasicBlock *EntryBB = BB;OpenMPIRBuilder::InsertPointTy AllocaIP(EntryBB,EntryBB->getFirstInsertionPt());Value *Sub = nullptr;auto UpdateOp = [&](Value *Atomic, IRBuilder<> &IRB) {Sub = IRB.CreateSub(ConstVal, Atomic);return Sub;};Builder.restoreIP(OMPBuilder.createAtomicUpdate(Builder, AllocaIP, X, Expr, AO, RMWOp, UpdateOp, IsXLHSInRHSPart));BasicBlock *ContBB = EntryBB->getSingleSuccessor();BranchInst *ContTI = dyn_cast<BranchInst>(ContBB->getTerminator());EXPECT_NE(ContTI, nullptr);BasicBlock *EndBB = ContTI->getSuccessor(0);EXPECT_TRUE(ContTI->isConditional());EXPECT_EQ(ContTI->getSuccessor(1), ContBB);EXPECT_NE(EndBB, nullptr);PHINode *Phi = dyn_cast<PHINode>(&ContBB->front());EXPECT_NE(Phi, nullptr);EXPECT_EQ(Phi->getNumIncomingValues(), 2U);EXPECT_EQ(Phi->getIncomingBlock(0), EntryBB);EXPECT_EQ(Phi->getIncomingBlock(1), ContBB);EXPECT_EQ(Sub->getNumUses(), 1U);StoreInst *St = dyn_cast<StoreInst>(Sub->user_back());AllocaInst *UpdateTemp = dyn_cast<AllocaInst>(St->getPointerOperand());ExtractValueInst *ExVI1 =dyn_cast<ExtractValueInst>(Phi->getIncomingValueForBlock(ContBB));EXPECT_NE(ExVI1, nullptr);AtomicCmpXchgInst *CmpExchg =dyn_cast<AtomicCmpXchgInst>(ExVI1->getAggregateOperand());EXPECT_NE(CmpExchg, nullptr);EXPECT_EQ(CmpExchg->getPointerOperand(), XVal);EXPECT_EQ(CmpExchg->getCompareOperand(), Phi);EXPECT_EQ(CmpExchg->getSuccessOrdering(), AtomicOrdering::Monotonic);LoadInst *Ld = dyn_cast<LoadInst>(CmpExchg->getNewValOperand());EXPECT_NE(Ld, nullptr);EXPECT_EQ(UpdateTemp, Ld->getPointerOperand());Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicCapture) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});LLVMContext &Ctx = M->getContext();IntegerType *Int32 = Type::getInt32Ty(Ctx);AllocaInst *XVal = Builder.CreateAlloca(Int32);XVal->setName("AtomicVar");AllocaInst *VVal = Builder.CreateAlloca(Int32);VVal->setName("AtomicCapTar");StoreInst *Init =Builder.CreateStore(ConstantInt::get(Type::getInt32Ty(Ctx), 0U), XVal);OpenMPIRBuilder::AtomicOpValue X = {XVal, Int32, false, false};OpenMPIRBuilder::AtomicOpValue V = {VVal, Int32, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;ConstantInt *Expr = ConstantInt::get(Type::getInt32Ty(Ctx), 1U);AtomicRMWInst::BinOp RMWOp = AtomicRMWInst::Add;bool IsXLHSInRHSPart = true;bool IsPostfixUpdate = true;bool UpdateExpr = true;BasicBlock *EntryBB = BB;OpenMPIRBuilder::InsertPointTy AllocaIP(EntryBB,EntryBB->getFirstInsertionPt());// integer update - not usedauto UpdateOp = [&](Value *Atomic, IRBuilder<> &IRB) { return nullptr; };Builder.restoreIP(OMPBuilder.createAtomicCapture(Builder, AllocaIP, X, V, Expr, AO, RMWOp, UpdateOp, UpdateExpr,IsPostfixUpdate, IsXLHSInRHSPart));EXPECT_EQ(EntryBB->getParent()->size(), 1U);AtomicRMWInst *ARWM = dyn_cast<AtomicRMWInst>(Init->getNextNode());EXPECT_NE(ARWM, nullptr);EXPECT_EQ(ARWM->getPointerOperand(), XVal);EXPECT_EQ(ARWM->getOperation(), RMWOp);StoreInst *St = dyn_cast<StoreInst>(ARWM->user_back());EXPECT_NE(St, nullptr);EXPECT_EQ(St->getPointerOperand(), VVal);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicCompare) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});LLVMContext &Ctx = M->getContext();IntegerType *Int32 = Type::getInt32Ty(Ctx);AllocaInst *XVal = Builder.CreateAlloca(Int32);XVal->setName("x");StoreInst *Init =Builder.CreateStore(ConstantInt::get(Type::getInt32Ty(Ctx), 0U), XVal);OpenMPIRBuilder::AtomicOpValue XSigned = {XVal, Int32, true, false};OpenMPIRBuilder::AtomicOpValue XUnsigned = {XVal, Int32, false, false};// V and R are not used in atomic compareOpenMPIRBuilder::AtomicOpValue V = {nullptr, nullptr, false, false};OpenMPIRBuilder::AtomicOpValue R = {nullptr, nullptr, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;ConstantInt *Expr = ConstantInt::get(Type::getInt32Ty(Ctx), 1U);ConstantInt *D = ConstantInt::get(Type::getInt32Ty(Ctx), 1U);OMPAtomicCompareOp OpMax = OMPAtomicCompareOp::MAX;OMPAtomicCompareOp OpEQ = OMPAtomicCompareOp::EQ;Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, XSigned, V, R, Expr, nullptr, AO, OpMax, true, false, false));Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, XUnsigned, V, R, Expr, nullptr, AO, OpMax, false, false, false));Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, XSigned, V, R, Expr, D, AO, OpEQ, true, false, false));BasicBlock *EntryBB = BB;EXPECT_EQ(EntryBB->getParent()->size(), 1U);EXPECT_EQ(EntryBB->size(), 5U);AtomicRMWInst *ARWM1 = dyn_cast<AtomicRMWInst>(Init->getNextNode());EXPECT_NE(ARWM1, nullptr);EXPECT_EQ(ARWM1->getPointerOperand(), XVal);EXPECT_EQ(ARWM1->getValOperand(), Expr);EXPECT_EQ(ARWM1->getOperation(), AtomicRMWInst::Min);AtomicRMWInst *ARWM2 = dyn_cast<AtomicRMWInst>(ARWM1->getNextNode());EXPECT_NE(ARWM2, nullptr);EXPECT_EQ(ARWM2->getPointerOperand(), XVal);EXPECT_EQ(ARWM2->getValOperand(), Expr);EXPECT_EQ(ARWM2->getOperation(), AtomicRMWInst::UMax);AtomicCmpXchgInst *AXCHG = dyn_cast<AtomicCmpXchgInst>(ARWM2->getNextNode());EXPECT_NE(AXCHG, nullptr);EXPECT_EQ(AXCHG->getPointerOperand(), XVal);EXPECT_EQ(AXCHG->getCompareOperand(), Expr);EXPECT_EQ(AXCHG->getNewValOperand(), D);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, OMPAtomicCompareCapture) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});LLVMContext &Ctx = M->getContext();IntegerType *Int32 = Type::getInt32Ty(Ctx);AllocaInst *XVal = Builder.CreateAlloca(Int32);XVal->setName("x");AllocaInst *VVal = Builder.CreateAlloca(Int32);VVal->setName("v");AllocaInst *RVal = Builder.CreateAlloca(Int32);RVal->setName("r");StoreInst *Init =Builder.CreateStore(ConstantInt::get(Type::getInt32Ty(Ctx), 0U), XVal);OpenMPIRBuilder::AtomicOpValue X = {XVal, Int32, true, false};OpenMPIRBuilder::AtomicOpValue V = {VVal, Int32, false, false};OpenMPIRBuilder::AtomicOpValue NoV = {nullptr, nullptr, false, false};OpenMPIRBuilder::AtomicOpValue R = {RVal, Int32, false, false};OpenMPIRBuilder::AtomicOpValue NoR = {nullptr, nullptr, false, false};AtomicOrdering AO = AtomicOrdering::Monotonic;ConstantInt *Expr = ConstantInt::get(Type::getInt32Ty(Ctx), 1U);ConstantInt *D = ConstantInt::get(Type::getInt32Ty(Ctx), 1U);OMPAtomicCompareOp OpMax = OMPAtomicCompareOp::MAX;OMPAtomicCompareOp OpEQ = OMPAtomicCompareOp::EQ;// { cond-update-stmt v = x; }Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, X, V, NoR, Expr, D, AO, OpEQ, /* IsXBinopExpr */ true,/* IsPostfixUpdate */ false,/* IsFailOnly */ false));// { v = x; cond-update-stmt }Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, X, V, NoR, Expr, D, AO, OpEQ, /* IsXBinopExpr */ true,/* IsPostfixUpdate */ true,/* IsFailOnly */ false));// if(x == e) { x = d; } else { v = x; }Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, X, V, NoR, Expr, D, AO, OpEQ, /* IsXBinopExpr */ true,/* IsPostfixUpdate */ false,/* IsFailOnly */ true));// { r = x == e; if(r) { x = d; } }Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, X, NoV, R, Expr, D, AO, OpEQ, /* IsXBinopExpr */ true,/* IsPostfixUpdate */ false,/* IsFailOnly */ false));// { r = x == e; if(r) { x = d; } else { v = x; } }Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, X, V, R, Expr, D, AO, OpEQ, /* IsXBinopExpr */ true,/* IsPostfixUpdate */ false,/* IsFailOnly */ true));// { v = x; cond-update-stmt }Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, X, V, NoR, Expr, nullptr, AO, OpMax, /* IsXBinopExpr */ true,/* IsPostfixUpdate */ true,/* IsFailOnly */ false));// { cond-update-stmt v = x; }Builder.restoreIP(OMPBuilder.createAtomicCompare(Builder, X, V, NoR, Expr, nullptr, AO, OpMax, /* IsXBinopExpr */ false,/* IsPostfixUpdate */ false,/* IsFailOnly */ false));BasicBlock *EntryBB = BB;EXPECT_EQ(EntryBB->getParent()->size(), 5U);BasicBlock *Cont1 = dyn_cast<BasicBlock>(EntryBB->getNextNode());EXPECT_NE(Cont1, nullptr);BasicBlock *Exit1 = dyn_cast<BasicBlock>(Cont1->getNextNode());EXPECT_NE(Exit1, nullptr);BasicBlock *Cont2 = dyn_cast<BasicBlock>(Exit1->getNextNode());EXPECT_NE(Cont2, nullptr);BasicBlock *Exit2 = dyn_cast<BasicBlock>(Cont2->getNextNode());EXPECT_NE(Exit2, nullptr);AtomicCmpXchgInst *CmpXchg1 =dyn_cast<AtomicCmpXchgInst>(Init->getNextNode());EXPECT_NE(CmpXchg1, nullptr);EXPECT_EQ(CmpXchg1->getPointerOperand(), XVal);EXPECT_EQ(CmpXchg1->getCompareOperand(), Expr);EXPECT_EQ(CmpXchg1->getNewValOperand(), D);ExtractValueInst *ExtVal1 =dyn_cast<ExtractValueInst>(CmpXchg1->getNextNode());EXPECT_NE(ExtVal1, nullptr);EXPECT_EQ(ExtVal1->getAggregateOperand(), CmpXchg1);EXPECT_EQ(ExtVal1->getIndices(), ArrayRef<unsigned int>(0U));ExtractValueInst *ExtVal2 =dyn_cast<ExtractValueInst>(ExtVal1->getNextNode());EXPECT_NE(ExtVal2, nullptr);EXPECT_EQ(ExtVal2->getAggregateOperand(), CmpXchg1);EXPECT_EQ(ExtVal2->getIndices(), ArrayRef<unsigned int>(1U));SelectInst *Sel1 = dyn_cast<SelectInst>(ExtVal2->getNextNode());EXPECT_NE(Sel1, nullptr);EXPECT_EQ(Sel1->getCondition(), ExtVal2);EXPECT_EQ(Sel1->getTrueValue(), Expr);EXPECT_EQ(Sel1->getFalseValue(), ExtVal1);StoreInst *Store1 = dyn_cast<StoreInst>(Sel1->getNextNode());EXPECT_NE(Store1, nullptr);EXPECT_EQ(Store1->getPointerOperand(), VVal);EXPECT_EQ(Store1->getValueOperand(), Sel1);AtomicCmpXchgInst *CmpXchg2 =dyn_cast<AtomicCmpXchgInst>(Store1->getNextNode());EXPECT_NE(CmpXchg2, nullptr);EXPECT_EQ(CmpXchg2->getPointerOperand(), XVal);EXPECT_EQ(CmpXchg2->getCompareOperand(), Expr);EXPECT_EQ(CmpXchg2->getNewValOperand(), D);ExtractValueInst *ExtVal3 =dyn_cast<ExtractValueInst>(CmpXchg2->getNextNode());EXPECT_NE(ExtVal3, nullptr);EXPECT_EQ(ExtVal3->getAggregateOperand(), CmpXchg2);EXPECT_EQ(ExtVal3->getIndices(), ArrayRef<unsigned int>(0U));StoreInst *Store2 = dyn_cast<StoreInst>(ExtVal3->getNextNode());EXPECT_NE(Store2, nullptr);EXPECT_EQ(Store2->getPointerOperand(), VVal);EXPECT_EQ(Store2->getValueOperand(), ExtVal3);AtomicCmpXchgInst *CmpXchg3 =dyn_cast<AtomicCmpXchgInst>(Store2->getNextNode());EXPECT_NE(CmpXchg3, nullptr);EXPECT_EQ(CmpXchg3->getPointerOperand(), XVal);EXPECT_EQ(CmpXchg3->getCompareOperand(), Expr);EXPECT_EQ(CmpXchg3->getNewValOperand(), D);ExtractValueInst *ExtVal4 =dyn_cast<ExtractValueInst>(CmpXchg3->getNextNode());EXPECT_NE(ExtVal4, nullptr);EXPECT_EQ(ExtVal4->getAggregateOperand(), CmpXchg3);EXPECT_EQ(ExtVal4->getIndices(), ArrayRef<unsigned int>(0U));ExtractValueInst *ExtVal5 =dyn_cast<ExtractValueInst>(ExtVal4->getNextNode());EXPECT_NE(ExtVal5, nullptr);EXPECT_EQ(ExtVal5->getAggregateOperand(), CmpXchg3);EXPECT_EQ(ExtVal5->getIndices(), ArrayRef<unsigned int>(1U));BranchInst *Br1 = dyn_cast<BranchInst>(ExtVal5->getNextNode());EXPECT_NE(Br1, nullptr);EXPECT_EQ(Br1->isConditional(), true);EXPECT_EQ(Br1->getCondition(), ExtVal5);EXPECT_EQ(Br1->getSuccessor(0), Exit1);EXPECT_EQ(Br1->getSuccessor(1), Cont1);StoreInst *Store3 = dyn_cast<StoreInst>(&Cont1->front());EXPECT_NE(Store3, nullptr);EXPECT_EQ(Store3->getPointerOperand(), VVal);EXPECT_EQ(Store3->getValueOperand(), ExtVal4);BranchInst *Br2 = dyn_cast<BranchInst>(Store3->getNextNode());EXPECT_NE(Br2, nullptr);EXPECT_EQ(Br2->isUnconditional(), true);EXPECT_EQ(Br2->getSuccessor(0), Exit1);AtomicCmpXchgInst *CmpXchg4 = dyn_cast<AtomicCmpXchgInst>(&Exit1->front());EXPECT_NE(CmpXchg4, nullptr);EXPECT_EQ(CmpXchg4->getPointerOperand(), XVal);EXPECT_EQ(CmpXchg4->getCompareOperand(), Expr);EXPECT_EQ(CmpXchg4->getNewValOperand(), D);ExtractValueInst *ExtVal6 =dyn_cast<ExtractValueInst>(CmpXchg4->getNextNode());EXPECT_NE(ExtVal6, nullptr);EXPECT_EQ(ExtVal6->getAggregateOperand(), CmpXchg4);EXPECT_EQ(ExtVal6->getIndices(), ArrayRef<unsigned int>(1U));ZExtInst *ZExt1 = dyn_cast<ZExtInst>(ExtVal6->getNextNode());EXPECT_NE(ZExt1, nullptr);EXPECT_EQ(ZExt1->getDestTy(), Int32);StoreInst *Store4 = dyn_cast<StoreInst>(ZExt1->getNextNode());EXPECT_NE(Store4, nullptr);EXPECT_EQ(Store4->getPointerOperand(), RVal);EXPECT_EQ(Store4->getValueOperand(), ZExt1);AtomicCmpXchgInst *CmpXchg5 =dyn_cast<AtomicCmpXchgInst>(Store4->getNextNode());EXPECT_NE(CmpXchg5, nullptr);EXPECT_EQ(CmpXchg5->getPointerOperand(), XVal);EXPECT_EQ(CmpXchg5->getCompareOperand(), Expr);EXPECT_EQ(CmpXchg5->getNewValOperand(), D);ExtractValueInst *ExtVal7 =dyn_cast<ExtractValueInst>(CmpXchg5->getNextNode());EXPECT_NE(ExtVal7, nullptr);EXPECT_EQ(ExtVal7->getAggregateOperand(), CmpXchg5);EXPECT_EQ(ExtVal7->getIndices(), ArrayRef<unsigned int>(0U));ExtractValueInst *ExtVal8 =dyn_cast<ExtractValueInst>(ExtVal7->getNextNode());EXPECT_NE(ExtVal8, nullptr);EXPECT_EQ(ExtVal8->getAggregateOperand(), CmpXchg5);EXPECT_EQ(ExtVal8->getIndices(), ArrayRef<unsigned int>(1U));BranchInst *Br3 = dyn_cast<BranchInst>(ExtVal8->getNextNode());EXPECT_NE(Br3, nullptr);EXPECT_EQ(Br3->isConditional(), true);EXPECT_EQ(Br3->getCondition(), ExtVal8);EXPECT_EQ(Br3->getSuccessor(0), Exit2);EXPECT_EQ(Br3->getSuccessor(1), Cont2);StoreInst *Store5 = dyn_cast<StoreInst>(&Cont2->front());EXPECT_NE(Store5, nullptr);EXPECT_EQ(Store5->getPointerOperand(), VVal);EXPECT_EQ(Store5->getValueOperand(), ExtVal7);BranchInst *Br4 = dyn_cast<BranchInst>(Store5->getNextNode());EXPECT_NE(Br4, nullptr);EXPECT_EQ(Br4->isUnconditional(), true);EXPECT_EQ(Br4->getSuccessor(0), Exit2);ExtractValueInst *ExtVal9 = dyn_cast<ExtractValueInst>(&Exit2->front());EXPECT_NE(ExtVal9, nullptr);EXPECT_EQ(ExtVal9->getAggregateOperand(), CmpXchg5);EXPECT_EQ(ExtVal9->getIndices(), ArrayRef<unsigned int>(1U));ZExtInst *ZExt2 = dyn_cast<ZExtInst>(ExtVal9->getNextNode());EXPECT_NE(ZExt2, nullptr);EXPECT_EQ(ZExt2->getDestTy(), Int32);StoreInst *Store6 = dyn_cast<StoreInst>(ZExt2->getNextNode());EXPECT_NE(Store6, nullptr);EXPECT_EQ(Store6->getPointerOperand(), RVal);EXPECT_EQ(Store6->getValueOperand(), ZExt2);AtomicRMWInst *ARWM1 = dyn_cast<AtomicRMWInst>(Store6->getNextNode());EXPECT_NE(ARWM1, nullptr);EXPECT_EQ(ARWM1->getPointerOperand(), XVal);EXPECT_EQ(ARWM1->getValOperand(), Expr);EXPECT_EQ(ARWM1->getOperation(), AtomicRMWInst::Min);StoreInst *Store7 = dyn_cast<StoreInst>(ARWM1->getNextNode());EXPECT_NE(Store7, nullptr);EXPECT_EQ(Store7->getPointerOperand(), VVal);EXPECT_EQ(Store7->getValueOperand(), ARWM1);AtomicRMWInst *ARWM2 = dyn_cast<AtomicRMWInst>(Store7->getNextNode());EXPECT_NE(ARWM2, nullptr);EXPECT_EQ(ARWM2->getPointerOperand(), XVal);EXPECT_EQ(ARWM2->getValOperand(), Expr);EXPECT_EQ(ARWM2->getOperation(), AtomicRMWInst::Max);CmpInst *Cmp1 = dyn_cast<CmpInst>(ARWM2->getNextNode());EXPECT_NE(Cmp1, nullptr);EXPECT_EQ(Cmp1->getPredicate(), CmpInst::ICMP_SGT);EXPECT_EQ(Cmp1->getOperand(0), ARWM2);EXPECT_EQ(Cmp1->getOperand(1), Expr);SelectInst *Sel2 = dyn_cast<SelectInst>(Cmp1->getNextNode());EXPECT_NE(Sel2, nullptr);EXPECT_EQ(Sel2->getCondition(), Cmp1);EXPECT_EQ(Sel2->getTrueValue(), Expr);EXPECT_EQ(Sel2->getFalseValue(), ARWM2);StoreInst *Store8 = dyn_cast<StoreInst>(Sel2->getNextNode());EXPECT_NE(Store8, nullptr);EXPECT_EQ(Store8->getPointerOperand(), VVal);EXPECT_EQ(Store8->getValueOperand(), Sel2);Builder.CreateRetVoid();OMPBuilder.finalize();EXPECT_FALSE(verifyModule(*M, &errs()));}/// Returns the single instruction of InstTy type in BB that uses the value V./// If there is more than one such instruction, returns null.template <typename InstTy>static InstTy *findSingleUserInBlock(Value *V, BasicBlock *BB) {InstTy *Result = nullptr;for (User *U : V->users()) {auto *Inst = dyn_cast<InstTy>(U);if (!Inst || Inst->getParent() != BB)continue;if (Result)return nullptr;Result = Inst;}return Result;}/// Returns true if BB contains a simple binary reduction that loads a value/// from Accum, performs some binary operation with it, and stores it back to/// Accum.static bool isSimpleBinaryReduction(Value *Accum, BasicBlock *BB,Instruction::BinaryOps *OpCode = nullptr) {StoreInst *Store = findSingleUserInBlock<StoreInst>(Accum, BB);if (!Store)return false;auto *Stored = dyn_cast<BinaryOperator>(Store->getOperand(0));if (!Stored)return false;if (OpCode && *OpCode != Stored->getOpcode())return false;auto *Load = dyn_cast<LoadInst>(Stored->getOperand(0));return Load && Load->getOperand(0) == Accum;}/// Returns true if BB contains a binary reduction that reduces V using a binary/// operator into an accumulator that is a function argument.static bool isValueReducedToFuncArg(Value *V, BasicBlock *BB) {auto *ReductionOp = findSingleUserInBlock<BinaryOperator>(V, BB);if (!ReductionOp)return false;auto *GlobalLoad = dyn_cast<LoadInst>(ReductionOp->getOperand(0));if (!GlobalLoad)return false;auto *Store = findSingleUserInBlock<StoreInst>(ReductionOp, BB);if (!Store)return false;return Store->getPointerOperand() == GlobalLoad->getPointerOperand() &&isa<Argument>(findAggregateFromValue(GlobalLoad->getPointerOperand()));}/// Finds among users of Ptr a pair of GEP instructions with indices [0, 0] and/// [0, 1], respectively, and assigns results of these instructions to Zero and/// One. Returns true on success, false on failure or if such instructions are/// not unique among the users of Ptr.static bool findGEPZeroOne(Value *Ptr, Value *&Zero, Value *&One) {Zero = nullptr;One = nullptr;for (User *U : Ptr->users()) {if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) {if (GEP->getNumIndices() != 2)continue;auto *FirstIdx = dyn_cast<ConstantInt>(GEP->getOperand(1));auto *SecondIdx = dyn_cast<ConstantInt>(GEP->getOperand(2));EXPECT_NE(FirstIdx, nullptr);EXPECT_NE(SecondIdx, nullptr);EXPECT_TRUE(FirstIdx->isZero());if (SecondIdx->isZero()) {if (Zero)return false;Zero = GEP;} else if (SecondIdx->isOne()) {if (One)return false;One = GEP;} else {return false;}}}return Zero != nullptr && One != nullptr;}static OpenMPIRBuilder::InsertPointTysumReduction(OpenMPIRBuilder::InsertPointTy IP, Value *LHS, Value *RHS,Value *&Result) {IRBuilder<> Builder(IP.getBlock(), IP.getPoint());Result = Builder.CreateFAdd(LHS, RHS, "red.add");return Builder.saveIP();}static OpenMPIRBuilder::InsertPointTysumAtomicReduction(OpenMPIRBuilder::InsertPointTy IP, Type *Ty, Value *LHS,Value *RHS) {IRBuilder<> Builder(IP.getBlock(), IP.getPoint());Value *Partial = Builder.CreateLoad(Ty, RHS, "red.partial");Builder.CreateAtomicRMW(AtomicRMWInst::FAdd, LHS, Partial, None,AtomicOrdering::Monotonic);return Builder.saveIP();}static OpenMPIRBuilder::InsertPointTyxorReduction(OpenMPIRBuilder::InsertPointTy IP, Value *LHS, Value *RHS,Value *&Result) {IRBuilder<> Builder(IP.getBlock(), IP.getPoint());Result = Builder.CreateXor(LHS, RHS, "red.xor");return Builder.saveIP();}static OpenMPIRBuilder::InsertPointTyxorAtomicReduction(OpenMPIRBuilder::InsertPointTy IP, Type *Ty, Value *LHS,Value *RHS) {IRBuilder<> Builder(IP.getBlock(), IP.getPoint());Value *Partial = Builder.CreateLoad(Ty, RHS, "red.partial");Builder.CreateAtomicRMW(AtomicRMWInst::Xor, LHS, Partial, None,AtomicOrdering::Monotonic);return Builder.saveIP();}TEST_F(OpenMPIRBuilderTest, CreateReductions) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "parallel.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});// Create variables to be reduced.InsertPointTy OuterAllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());Type *SumType = Builder.getFloatTy();Type *XorType = Builder.getInt32Ty();Value *SumReduced;Value *XorReduced;{IRBuilderBase::InsertPointGuard Guard(Builder);Builder.restoreIP(OuterAllocaIP);SumReduced = Builder.CreateAlloca(SumType);XorReduced = Builder.CreateAlloca(XorType);}// Store initial values of reductions into global variables.Builder.CreateStore(ConstantFP::get(Builder.getFloatTy(), 0.0), SumReduced);Builder.CreateStore(Builder.getInt32(1), XorReduced);// The loop body computes two reductions:// sum of (float) thread-id;// xor of thread-id;// and store the result in global variables.InsertPointTy BodyIP, BodyAllocaIP;auto BodyGenCB = [&](InsertPointTy InnerAllocaIP, InsertPointTy CodeGenIP) {IRBuilderBase::InsertPointGuard Guard(Builder);Builder.restoreIP(CodeGenIP);uint32_t StrSize;Constant *SrcLocStr = OMPBuilder.getOrCreateSrcLocStr(Loc, StrSize);Value *Ident = OMPBuilder.getOrCreateIdent(SrcLocStr, StrSize);Value *TID = OMPBuilder.getOrCreateThreadID(Ident);Value *SumLocal =Builder.CreateUIToFP(TID, Builder.getFloatTy(), "sum.local");Value *SumPartial = Builder.CreateLoad(SumType, SumReduced, "sum.partial");Value *XorPartial = Builder.CreateLoad(XorType, XorReduced, "xor.partial");Value *Sum = Builder.CreateFAdd(SumPartial, SumLocal, "sum");Value *Xor = Builder.CreateXor(XorPartial, TID, "xor");Builder.CreateStore(Sum, SumReduced);Builder.CreateStore(Xor, XorReduced);BodyIP = Builder.saveIP();BodyAllocaIP = InnerAllocaIP;};// Privatization for reduction creates local copies of reduction variables and// initializes them to reduction-neutral values.Value *SumPrivatized;Value *XorPrivatized;auto PrivCB = [&](InsertPointTy InnerAllocaIP, InsertPointTy CodeGenIP,Value &Original, Value &Inner, Value *&ReplVal) {IRBuilderBase::InsertPointGuard Guard(Builder);Builder.restoreIP(InnerAllocaIP);if (&Original == SumReduced) {SumPrivatized = Builder.CreateAlloca(Builder.getFloatTy());ReplVal = SumPrivatized;} else if (&Original == XorReduced) {XorPrivatized = Builder.CreateAlloca(Builder.getInt32Ty());ReplVal = XorPrivatized;} else {ReplVal = &Inner;return CodeGenIP;}Builder.restoreIP(CodeGenIP);if (&Original == SumReduced)Builder.CreateStore(ConstantFP::get(Builder.getFloatTy(), 0.0),SumPrivatized);else if (&Original == XorReduced)Builder.CreateStore(Builder.getInt32(0), XorPrivatized);return Builder.saveIP();};// Do nothing in finalization.auto FiniCB = [&](InsertPointTy CodeGenIP) { return CodeGenIP; };InsertPointTy AfterIP =OMPBuilder.createParallel(Loc, OuterAllocaIP, BodyGenCB, PrivCB, FiniCB,/* IfCondition */ nullptr,/* NumThreads */ nullptr, OMP_PROC_BIND_default,/* IsCancellable */ false);Builder.restoreIP(AfterIP);OpenMPIRBuilder::ReductionInfo ReductionInfos[] = {{SumType, SumReduced, SumPrivatized, sumReduction, sumAtomicReduction},{XorType, XorReduced, XorPrivatized, xorReduction, xorAtomicReduction}};OMPBuilder.createReductions(BodyIP, BodyAllocaIP, ReductionInfos);Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize(F);// The IR must be valid.EXPECT_FALSE(verifyModule(*M));// Outlining must have happened.SmallVector<CallInst *> ForkCalls;findCalls(F, omp::RuntimeFunction::OMPRTL___kmpc_fork_call, OMPBuilder,ForkCalls);ASSERT_EQ(ForkCalls.size(), 1u);Value *CalleeVal = cast<Constant>(ForkCalls[0]->getOperand(2))->getOperand(0);Function *Outlined = dyn_cast<Function>(CalleeVal);EXPECT_NE(Outlined, nullptr);// Check that the lock variable was created with the expected name.GlobalVariable *LockVar =M->getGlobalVariable(".gomp_critical_user_.reduction.var");EXPECT_NE(LockVar, nullptr);// Find the allocation of a local array that will be used to call the runtime// reduciton function.BasicBlock &AllocBlock = Outlined->getEntryBlock();Value *LocalArray = nullptr;for (Instruction &I : AllocBlock) {if (AllocaInst *Alloc = dyn_cast<AllocaInst>(&I)) {if (!Alloc->getAllocatedType()->isArrayTy() ||!Alloc->getAllocatedType()->getArrayElementType()->isPointerTy())continue;LocalArray = Alloc;break;}}ASSERT_NE(LocalArray, nullptr);// Find the call to the runtime reduction function.BasicBlock *BB = AllocBlock.getUniqueSuccessor();Value *LocalArrayPtr = nullptr;Value *ReductionFnVal = nullptr;Value *SwitchArg = nullptr;for (Instruction &I : *BB) {if (CallInst *Call = dyn_cast<CallInst>(&I)) {if (Call->getCalledFunction() !=OMPBuilder.getOrCreateRuntimeFunctionPtr(RuntimeFunction::OMPRTL___kmpc_reduce))continue;LocalArrayPtr = Call->getOperand(4);ReductionFnVal = Call->getOperand(5);SwitchArg = Call;break;}}// Check that the local array is passed to the function.ASSERT_NE(LocalArrayPtr, nullptr);BitCastInst *BitCast = dyn_cast<BitCastInst>(LocalArrayPtr);ASSERT_NE(BitCast, nullptr);EXPECT_EQ(BitCast->getOperand(0), LocalArray);// Find the GEP instructions preceding stores to the local array.Value *FirstArrayElemPtr = nullptr;Value *SecondArrayElemPtr = nullptr;EXPECT_EQ(LocalArray->getNumUses(), 3u);ASSERT_TRUE(findGEPZeroOne(LocalArray, FirstArrayElemPtr, SecondArrayElemPtr));// Check that the values stored into the local array are privatized reduction// variables.auto *FirstStored = dyn_cast_or_null<BitCastInst>(findStoredValue<GetElementPtrInst>(FirstArrayElemPtr));auto *SecondStored = dyn_cast_or_null<BitCastInst>(findStoredValue<GetElementPtrInst>(SecondArrayElemPtr));ASSERT_NE(FirstStored, nullptr);ASSERT_NE(SecondStored, nullptr);Value *FirstPrivatized = FirstStored->getOperand(0);Value *SecondPrivatized = SecondStored->getOperand(0);EXPECT_TRUE(isSimpleBinaryReduction(FirstPrivatized, FirstStored->getParent()));EXPECT_TRUE(isSimpleBinaryReduction(SecondPrivatized, SecondStored->getParent()));// Check that the result of the runtime reduction call is used for further// dispatch.ASSERT_EQ(SwitchArg->getNumUses(), 1u);SwitchInst *Switch = dyn_cast<SwitchInst>(*SwitchArg->user_begin());ASSERT_NE(Switch, nullptr);EXPECT_EQ(Switch->getNumSuccessors(), 3u);BasicBlock *NonAtomicBB = Switch->case_begin()->getCaseSuccessor();BasicBlock *AtomicBB = std::next(Switch->case_begin())->getCaseSuccessor();// Non-atomic block contains reductions to the global reduction variable,// which is passed into the outlined function as an argument.Value *FirstLoad =findSingleUserInBlock<LoadInst>(FirstPrivatized, NonAtomicBB);Value *SecondLoad =findSingleUserInBlock<LoadInst>(SecondPrivatized, NonAtomicBB);EXPECT_TRUE(isValueReducedToFuncArg(FirstLoad, NonAtomicBB));EXPECT_TRUE(isValueReducedToFuncArg(SecondLoad, NonAtomicBB));// Atomic block also constains reductions to the global reduction variable.FirstLoad = findSingleUserInBlock<LoadInst>(FirstPrivatized, AtomicBB);SecondLoad = findSingleUserInBlock<LoadInst>(SecondPrivatized, AtomicBB);auto *FirstAtomic = findSingleUserInBlock<AtomicRMWInst>(FirstLoad, AtomicBB);auto *SecondAtomic =findSingleUserInBlock<AtomicRMWInst>(SecondLoad, AtomicBB);ASSERT_NE(FirstAtomic, nullptr);Value *AtomicStorePointer = FirstAtomic->getPointerOperand();EXPECT_TRUE(isa<Argument>(findAggregateFromValue(AtomicStorePointer)));ASSERT_NE(SecondAtomic, nullptr);AtomicStorePointer = SecondAtomic->getPointerOperand();EXPECT_TRUE(isa<Argument>(findAggregateFromValue(AtomicStorePointer)));// Check that the separate reduction function also performs (non-atomic)// reductions after extracting reduction variables from its arguments.Function *ReductionFn = cast<Function>(ReductionFnVal);BasicBlock *FnReductionBB = &ReductionFn->getEntryBlock();auto *Bitcast =findSingleUserInBlock<BitCastInst>(ReductionFn->getArg(0), FnReductionBB);Value *FirstLHSPtr;Value *SecondLHSPtr;ASSERT_TRUE(findGEPZeroOne(Bitcast, FirstLHSPtr, SecondLHSPtr));Value *Opaque = findSingleUserInBlock<LoadInst>(FirstLHSPtr, FnReductionBB);ASSERT_NE(Opaque, nullptr);Bitcast = findSingleUserInBlock<BitCastInst>(Opaque, FnReductionBB);ASSERT_NE(Bitcast, nullptr);EXPECT_TRUE(isSimpleBinaryReduction(Bitcast, FnReductionBB));Opaque = findSingleUserInBlock<LoadInst>(SecondLHSPtr, FnReductionBB);ASSERT_NE(Opaque, nullptr);Bitcast = findSingleUserInBlock<BitCastInst>(Opaque, FnReductionBB);ASSERT_NE(Bitcast, nullptr);EXPECT_TRUE(isSimpleBinaryReduction(Bitcast, FnReductionBB));Bitcast =findSingleUserInBlock<BitCastInst>(ReductionFn->getArg(1), FnReductionBB);Value *FirstRHS;Value *SecondRHS;EXPECT_TRUE(findGEPZeroOne(Bitcast, FirstRHS, SecondRHS));}TEST_F(OpenMPIRBuilderTest, CreateTwoReductions) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "parallel.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});// Create variables to be reduced.InsertPointTy OuterAllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());Type *SumType = Builder.getFloatTy();Type *XorType = Builder.getInt32Ty();Value *SumReduced;Value *XorReduced;{IRBuilderBase::InsertPointGuard Guard(Builder);Builder.restoreIP(OuterAllocaIP);SumReduced = Builder.CreateAlloca(SumType);XorReduced = Builder.CreateAlloca(XorType);}// Store initial values of reductions into global variables.Builder.CreateStore(ConstantFP::get(Builder.getFloatTy(), 0.0), SumReduced);Builder.CreateStore(Builder.getInt32(1), XorReduced);InsertPointTy FirstBodyIP, FirstBodyAllocaIP;auto FirstBodyGenCB = [&](InsertPointTy InnerAllocaIP,InsertPointTy CodeGenIP) {IRBuilderBase::InsertPointGuard Guard(Builder);Builder.restoreIP(CodeGenIP);uint32_t StrSize;Constant *SrcLocStr = OMPBuilder.getOrCreateSrcLocStr(Loc, StrSize);Value *Ident = OMPBuilder.getOrCreateIdent(SrcLocStr, StrSize);Value *TID = OMPBuilder.getOrCreateThreadID(Ident);Value *SumLocal =Builder.CreateUIToFP(TID, Builder.getFloatTy(), "sum.local");Value *SumPartial = Builder.CreateLoad(SumType, SumReduced, "sum.partial");Value *Sum = Builder.CreateFAdd(SumPartial, SumLocal, "sum");Builder.CreateStore(Sum, SumReduced);FirstBodyIP = Builder.saveIP();FirstBodyAllocaIP = InnerAllocaIP;};InsertPointTy SecondBodyIP, SecondBodyAllocaIP;auto SecondBodyGenCB = [&](InsertPointTy InnerAllocaIP,InsertPointTy CodeGenIP) {IRBuilderBase::InsertPointGuard Guard(Builder);Builder.restoreIP(CodeGenIP);uint32_t StrSize;Constant *SrcLocStr = OMPBuilder.getOrCreateSrcLocStr(Loc, StrSize);Value *Ident = OMPBuilder.getOrCreateIdent(SrcLocStr, StrSize);Value *TID = OMPBuilder.getOrCreateThreadID(Ident);Value *XorPartial = Builder.CreateLoad(XorType, XorReduced, "xor.partial");Value *Xor = Builder.CreateXor(XorPartial, TID, "xor");Builder.CreateStore(Xor, XorReduced);SecondBodyIP = Builder.saveIP();SecondBodyAllocaIP = InnerAllocaIP;};// Privatization for reduction creates local copies of reduction variables and// initializes them to reduction-neutral values. The same privatization// callback is used for both loops, with dispatch based on the value being// privatized.Value *SumPrivatized;Value *XorPrivatized;auto PrivCB = [&](InsertPointTy InnerAllocaIP, InsertPointTy CodeGenIP,Value &Original, Value &Inner, Value *&ReplVal) {IRBuilderBase::InsertPointGuard Guard(Builder);Builder.restoreIP(InnerAllocaIP);if (&Original == SumReduced) {SumPrivatized = Builder.CreateAlloca(Builder.getFloatTy());ReplVal = SumPrivatized;} else if (&Original == XorReduced) {XorPrivatized = Builder.CreateAlloca(Builder.getInt32Ty());ReplVal = XorPrivatized;} else {ReplVal = &Inner;return CodeGenIP;}Builder.restoreIP(CodeGenIP);if (&Original == SumReduced)Builder.CreateStore(ConstantFP::get(Builder.getFloatTy(), 0.0),SumPrivatized);else if (&Original == XorReduced)Builder.CreateStore(Builder.getInt32(0), XorPrivatized);return Builder.saveIP();};// Do nothing in finalization.auto FiniCB = [&](InsertPointTy CodeGenIP) { return CodeGenIP; };Builder.restoreIP(OMPBuilder.createParallel(Loc, OuterAllocaIP, FirstBodyGenCB, PrivCB,FiniCB, /* IfCondition */ nullptr,/* NumThreads */ nullptr, OMP_PROC_BIND_default,/* IsCancellable */ false));InsertPointTy AfterIP = OMPBuilder.createParallel({Builder.saveIP(), DL}, OuterAllocaIP, SecondBodyGenCB, PrivCB, FiniCB,/* IfCondition */ nullptr,/* NumThreads */ nullptr, OMP_PROC_BIND_default,/* IsCancellable */ false);OMPBuilder.createReductions(FirstBodyIP, FirstBodyAllocaIP,{{SumType, SumReduced, SumPrivatized, sumReduction, sumAtomicReduction}});OMPBuilder.createReductions(SecondBodyIP, SecondBodyAllocaIP,{{XorType, XorReduced, XorPrivatized, xorReduction, xorAtomicReduction}});Builder.restoreIP(AfterIP);Builder.CreateRetVoid();OMPBuilder.finalize(F);// The IR must be valid.EXPECT_FALSE(verifyModule(*M));// Two different outlined functions must have been created.SmallVector<CallInst *> ForkCalls;findCalls(F, omp::RuntimeFunction::OMPRTL___kmpc_fork_call, OMPBuilder,ForkCalls);ASSERT_EQ(ForkCalls.size(), 2u);Value *CalleeVal = cast<Constant>(ForkCalls[0]->getOperand(2))->getOperand(0);Function *FirstCallee = cast<Function>(CalleeVal);CalleeVal = cast<Constant>(ForkCalls[1]->getOperand(2))->getOperand(0);Function *SecondCallee = cast<Function>(CalleeVal);EXPECT_NE(FirstCallee, SecondCallee);// Two different reduction functions must have been created.SmallVector<CallInst *> ReduceCalls;findCalls(FirstCallee, omp::RuntimeFunction::OMPRTL___kmpc_reduce, OMPBuilder,ReduceCalls);ASSERT_EQ(ReduceCalls.size(), 1u);auto *AddReduction = cast<Function>(ReduceCalls[0]->getOperand(5));ReduceCalls.clear();findCalls(SecondCallee, omp::RuntimeFunction::OMPRTL___kmpc_reduce,OMPBuilder, ReduceCalls);auto *XorReduction = cast<Function>(ReduceCalls[0]->getOperand(5));EXPECT_NE(AddReduction, XorReduction);// Each reduction function does its own kind of reduction.BasicBlock *FnReductionBB = &AddReduction->getEntryBlock();auto *Bitcast = findSingleUserInBlock<BitCastInst>(AddReduction->getArg(0),FnReductionBB);ASSERT_NE(Bitcast, nullptr);Value *FirstLHSPtr =findSingleUserInBlock<GetElementPtrInst>(Bitcast, FnReductionBB);ASSERT_NE(FirstLHSPtr, nullptr);Value *Opaque = findSingleUserInBlock<LoadInst>(FirstLHSPtr, FnReductionBB);ASSERT_NE(Opaque, nullptr);Bitcast = findSingleUserInBlock<BitCastInst>(Opaque, FnReductionBB);ASSERT_NE(Bitcast, nullptr);Instruction::BinaryOps Opcode = Instruction::FAdd;EXPECT_TRUE(isSimpleBinaryReduction(Bitcast, FnReductionBB, &Opcode));FnReductionBB = &XorReduction->getEntryBlock();Bitcast = findSingleUserInBlock<BitCastInst>(XorReduction->getArg(0),FnReductionBB);ASSERT_NE(Bitcast, nullptr);Value *SecondLHSPtr =findSingleUserInBlock<GetElementPtrInst>(Bitcast, FnReductionBB);ASSERT_NE(FirstLHSPtr, nullptr);Opaque = findSingleUserInBlock<LoadInst>(SecondLHSPtr, FnReductionBB);ASSERT_NE(Opaque, nullptr);Bitcast = findSingleUserInBlock<BitCastInst>(Opaque, FnReductionBB);ASSERT_NE(Bitcast, nullptr);Opcode = Instruction::Xor;EXPECT_TRUE(isSimpleBinaryReduction(Bitcast, FnReductionBB, &Opcode));}TEST_F(OpenMPIRBuilderTest, CreateSectionsSimple) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;using BodyGenCallbackTy = llvm::OpenMPIRBuilder::StorableBodyGenCallbackTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "sections.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});llvm::SmallVector<BodyGenCallbackTy, 4> SectionCBVector;llvm::SmallVector<BasicBlock *, 4> CaseBBs;auto FiniCB = [&](InsertPointTy IP) {};auto SectionCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {};SectionCBVector.push_back(SectionCB);auto PrivCB = [](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,llvm::Value &, llvm::Value &Val,llvm::Value *&ReplVal) { return CodeGenIP; };IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());Builder.restoreIP(OMPBuilder.createSections(Loc, AllocaIP, SectionCBVector,PrivCB, FiniCB, false, false));Builder.CreateRetVoid(); // Required at the end of the functionEXPECT_NE(F->getEntryBlock().getTerminator(), nullptr);EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateSections) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;using BodyGenCallbackTy = llvm::OpenMPIRBuilder::StorableBodyGenCallbackTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});llvm::SmallVector<BodyGenCallbackTy, 4> SectionCBVector;llvm::SmallVector<BasicBlock *, 4> CaseBBs;BasicBlock *SwitchBB = nullptr;AllocaInst *PrivAI = nullptr;SwitchInst *Switch = nullptr;unsigned NumBodiesGenerated = 0;unsigned NumFiniCBCalls = 0;PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());auto FiniCB = [&](InsertPointTy IP) {++NumFiniCBCalls;BasicBlock *IPBB = IP.getBlock();EXPECT_NE(IPBB->end(), IP.getPoint());};auto SectionCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {++NumBodiesGenerated;CaseBBs.push_back(CodeGenIP.getBlock());SwitchBB = CodeGenIP.getBlock()->getSinglePredecessor();Builder.restoreIP(CodeGenIP);Builder.CreateStore(F->arg_begin(), PrivAI);Value *PrivLoad =Builder.CreateLoad(F->arg_begin()->getType(), PrivAI, "local.alloca");Builder.CreateICmpNE(F->arg_begin(), PrivLoad);};auto PrivCB = [](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,llvm::Value &, llvm::Value &Val, llvm::Value *&ReplVal) {// TODO: Privatization not implemented yetreturn CodeGenIP;};SectionCBVector.push_back(SectionCB);SectionCBVector.push_back(SectionCB);IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());Builder.restoreIP(OMPBuilder.createSections(Loc, AllocaIP, SectionCBVector,PrivCB, FiniCB, false, false));Builder.CreateRetVoid(); // Required at the end of the function// Switch BB's predecessor is loop condition BB, whose successor at index 1 is// loop's exit BBBasicBlock *ForExitBB =SwitchBB->getSinglePredecessor()->getTerminator()->getSuccessor(1);EXPECT_NE(ForExitBB, nullptr);EXPECT_NE(PrivAI, nullptr);Function *OutlinedFn = PrivAI->getFunction();EXPECT_EQ(F, OutlinedFn);EXPECT_FALSE(verifyModule(*M, &errs()));EXPECT_EQ(OutlinedFn->arg_size(), 1U);BasicBlock *LoopPreheaderBB =OutlinedFn->getEntryBlock().getSingleSuccessor();// loop variables are 5 - lower bound, upper bound, stride, islastiter, and// iterator/counterbool FoundForInit = false;for (Instruction &Inst : *LoopPreheaderBB) {if (isa<CallInst>(Inst)) {if (cast<CallInst>(&Inst)->getCalledFunction()->getName() =="__kmpc_for_static_init_4u") {FoundForInit = true;}}}EXPECT_EQ(FoundForInit, true);bool FoundForExit = false;bool FoundBarrier = false;for (Instruction &Inst : *ForExitBB) {if (isa<CallInst>(Inst)) {if (cast<CallInst>(&Inst)->getCalledFunction()->getName() =="__kmpc_for_static_fini") {FoundForExit = true;}if (cast<CallInst>(&Inst)->getCalledFunction()->getName() =="__kmpc_barrier") {FoundBarrier = true;}if (FoundForExit && FoundBarrier)break;}}EXPECT_EQ(FoundForExit, true);EXPECT_EQ(FoundBarrier, true);EXPECT_NE(SwitchBB, nullptr);EXPECT_NE(SwitchBB->getTerminator(), nullptr);EXPECT_EQ(isa<SwitchInst>(SwitchBB->getTerminator()), true);Switch = cast<SwitchInst>(SwitchBB->getTerminator());EXPECT_EQ(Switch->getNumCases(), 2U);EXPECT_EQ(CaseBBs.size(), 2U);for (auto *&CaseBB : CaseBBs) {EXPECT_EQ(CaseBB->getParent(), OutlinedFn);}ASSERT_EQ(NumBodiesGenerated, 2U);ASSERT_EQ(NumFiniCBCalls, 1U);EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateSectionsNoWait) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;using BodyGenCallbackTy = llvm::OpenMPIRBuilder::StorableBodyGenCallbackTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);BasicBlock *EnterBB = BasicBlock::Create(Ctx, "sections.enter", F);Builder.CreateBr(EnterBB);Builder.SetInsertPoint(EnterBB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());llvm::SmallVector<BodyGenCallbackTy, 4> SectionCBVector;auto PrivCB = [](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,llvm::Value &, llvm::Value &Val,llvm::Value *&ReplVal) { return CodeGenIP; };auto FiniCB = [&](InsertPointTy IP) {};Builder.restoreIP(OMPBuilder.createSections(Loc, AllocaIP, SectionCBVector,PrivCB, FiniCB, false, true));Builder.CreateRetVoid(); // Required at the end of the functionfor (auto &Inst : instructions(*F)) {EXPECT_FALSE(isa<CallInst>(Inst) &&cast<CallInst>(&Inst)->getCalledFunction()->getName() =="__kmpc_barrier" &&"call to function __kmpc_barrier found with nowait");}}TEST_F(OpenMPIRBuilderTest, CreateOffloadMaptypes) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);SmallVector<uint64_t> Mappings = {0, 1};GlobalVariable *OffloadMaptypesGlobal =OMPBuilder.createOffloadMaptypes(Mappings, "offload_maptypes");EXPECT_FALSE(M->global_empty());EXPECT_EQ(OffloadMaptypesGlobal->getName(), "offload_maptypes");EXPECT_TRUE(OffloadMaptypesGlobal->isConstant());EXPECT_TRUE(OffloadMaptypesGlobal->hasGlobalUnnamedAddr());EXPECT_TRUE(OffloadMaptypesGlobal->hasPrivateLinkage());EXPECT_TRUE(OffloadMaptypesGlobal->hasInitializer());Constant *Initializer = OffloadMaptypesGlobal->getInitializer();EXPECT_TRUE(isa<ConstantDataArray>(Initializer));ConstantDataArray *MappingInit = dyn_cast<ConstantDataArray>(Initializer);EXPECT_EQ(MappingInit->getNumElements(), Mappings.size());EXPECT_TRUE(MappingInit->getType()->getElementType()->isIntegerTy(64));Constant *CA = ConstantDataArray::get(Builder.getContext(), Mappings);EXPECT_EQ(MappingInit, CA);}TEST_F(OpenMPIRBuilderTest, CreateOffloadMapnames) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();IRBuilder<> Builder(BB);uint32_t StrSize;Constant *Cst1 =OMPBuilder.getOrCreateSrcLocStr("array1", "file1", 2, 5, StrSize);Constant *Cst2 =OMPBuilder.getOrCreateSrcLocStr("array2", "file1", 3, 5, StrSize);SmallVector<llvm::Constant *> Names = {Cst1, Cst2};GlobalVariable *OffloadMaptypesGlobal =OMPBuilder.createOffloadMapnames(Names, "offload_mapnames");EXPECT_FALSE(M->global_empty());EXPECT_EQ(OffloadMaptypesGlobal->getName(), "offload_mapnames");EXPECT_TRUE(OffloadMaptypesGlobal->isConstant());EXPECT_FALSE(OffloadMaptypesGlobal->hasGlobalUnnamedAddr());EXPECT_TRUE(OffloadMaptypesGlobal->hasPrivateLinkage());EXPECT_TRUE(OffloadMaptypesGlobal->hasInitializer());Constant *Initializer = OffloadMaptypesGlobal->getInitializer();EXPECT_TRUE(isa<Constant>(Initializer->getOperand(0)->stripPointerCasts()));EXPECT_TRUE(isa<Constant>(Initializer->getOperand(1)->stripPointerCasts()));GlobalVariable *Name1Gbl =cast<GlobalVariable>(Initializer->getOperand(0)->stripPointerCasts());EXPECT_TRUE(isa<ConstantDataArray>(Name1Gbl->getInitializer()));ConstantDataArray *Name1GblCA =dyn_cast<ConstantDataArray>(Name1Gbl->getInitializer());EXPECT_EQ(Name1GblCA->getAsCString(), ";file1;array1;2;5;;");GlobalVariable *Name2Gbl =cast<GlobalVariable>(Initializer->getOperand(1)->stripPointerCasts());EXPECT_TRUE(isa<ConstantDataArray>(Name2Gbl->getInitializer()));ConstantDataArray *Name2GblCA =dyn_cast<ConstantDataArray>(Name2Gbl->getInitializer());EXPECT_EQ(Name2GblCA->getAsCString(), ";file1;array2;3;5;;");EXPECT_TRUE(Initializer->getType()->getArrayElementType()->isPointerTy());EXPECT_EQ(Initializer->getType()->getArrayNumElements(), Names.size());}TEST_F(OpenMPIRBuilderTest, CreateMapperAllocas) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});unsigned TotalNbOperand = 2;OpenMPIRBuilder::MapperAllocas MapperAllocas;IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());OMPBuilder.createMapperAllocas(Loc, AllocaIP, TotalNbOperand, MapperAllocas);EXPECT_NE(MapperAllocas.ArgsBase, nullptr);EXPECT_NE(MapperAllocas.Args, nullptr);EXPECT_NE(MapperAllocas.ArgSizes, nullptr);EXPECT_TRUE(MapperAllocas.ArgsBase->getAllocatedType()->isArrayTy());ArrayType *ArrType =dyn_cast<ArrayType>(MapperAllocas.ArgsBase->getAllocatedType());EXPECT_EQ(ArrType->getNumElements(), TotalNbOperand);EXPECT_TRUE(MapperAllocas.ArgsBase->getAllocatedType()->getArrayElementType()->isPointerTy());EXPECT_TRUE(cast<PointerType>(MapperAllocas.ArgsBase->getAllocatedType()->getArrayElementType())->isOpaqueOrPointeeTypeMatches(Builder.getInt8Ty()));EXPECT_TRUE(MapperAllocas.Args->getAllocatedType()->isArrayTy());ArrType = dyn_cast<ArrayType>(MapperAllocas.Args->getAllocatedType());EXPECT_EQ(ArrType->getNumElements(), TotalNbOperand);EXPECT_TRUE(MapperAllocas.Args->getAllocatedType()->getArrayElementType()->isPointerTy());EXPECT_TRUE(cast<PointerType>(MapperAllocas.Args->getAllocatedType()->getArrayElementType())->isOpaqueOrPointeeTypeMatches(Builder.getInt8Ty()));EXPECT_TRUE(MapperAllocas.ArgSizes->getAllocatedType()->isArrayTy());ArrType = dyn_cast<ArrayType>(MapperAllocas.ArgSizes->getAllocatedType());EXPECT_EQ(ArrType->getNumElements(), TotalNbOperand);EXPECT_TRUE(MapperAllocas.ArgSizes->getAllocatedType()->getArrayElementType()->isIntegerTy(64));}TEST_F(OpenMPIRBuilderTest, EmitMapperCall) {OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);LLVMContext &Ctx = M->getContext();OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});unsigned TotalNbOperand = 2;OpenMPIRBuilder::MapperAllocas MapperAllocas;IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),F->getEntryBlock().getFirstInsertionPt());OMPBuilder.createMapperAllocas(Loc, AllocaIP, TotalNbOperand, MapperAllocas);auto *BeginMapperFunc = OMPBuilder.getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_begin_mapper);SmallVector<uint64_t> Flags = {0, 2};uint32_t StrSize;Constant *SrcLocCst =OMPBuilder.getOrCreateSrcLocStr("", "file1", 2, 5, StrSize);Value *SrcLocInfo = OMPBuilder.getOrCreateIdent(SrcLocCst, StrSize);Constant *Cst1 =OMPBuilder.getOrCreateSrcLocStr("array1", "file1", 2, 5, StrSize);Constant *Cst2 =OMPBuilder.getOrCreateSrcLocStr("array2", "file1", 3, 5, StrSize);SmallVector<llvm::Constant *> Names = {Cst1, Cst2};GlobalVariable *Maptypes =OMPBuilder.createOffloadMaptypes(Flags, ".offload_maptypes");Value *MaptypesArg = Builder.CreateConstInBoundsGEP2_32(ArrayType::get(Type::getInt64Ty(Ctx), TotalNbOperand), Maptypes,/*Idx0=*/0, /*Idx1=*/0);GlobalVariable *Mapnames =OMPBuilder.createOffloadMapnames(Names, ".offload_mapnames");Value *MapnamesArg = Builder.CreateConstInBoundsGEP2_32(ArrayType::get(Type::getInt8PtrTy(Ctx), TotalNbOperand), Mapnames,/*Idx0=*/0, /*Idx1=*/0);OMPBuilder.emitMapperCall(Builder.saveIP(), BeginMapperFunc, SrcLocInfo,MaptypesArg, MapnamesArg, MapperAllocas, -1,TotalNbOperand);CallInst *MapperCall = dyn_cast<CallInst>(&BB->back());EXPECT_NE(MapperCall, nullptr);EXPECT_EQ(MapperCall->arg_size(), 9U);EXPECT_EQ(MapperCall->getCalledFunction()->getName(),"__tgt_target_data_begin_mapper");EXPECT_EQ(MapperCall->getOperand(0), SrcLocInfo);EXPECT_TRUE(MapperCall->getOperand(1)->getType()->isIntegerTy(64));EXPECT_TRUE(MapperCall->getOperand(2)->getType()->isIntegerTy(32));EXPECT_EQ(MapperCall->getOperand(6), MaptypesArg);EXPECT_EQ(MapperCall->getOperand(7), MapnamesArg);EXPECT_TRUE(MapperCall->getOperand(8)->getType()->isPointerTy());}TEST_F(OpenMPIRBuilderTest, CreateTask) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);AllocaInst *ValPtr32 = Builder.CreateAlloca(Builder.getInt32Ty());AllocaInst *ValPtr128 = Builder.CreateAlloca(Builder.getInt128Ty());Value *Val128 =Builder.CreateLoad(Builder.getInt128Ty(), ValPtr128, "bodygen.load");auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {Builder.restoreIP(AllocaIP);AllocaInst *Local128 = Builder.CreateAlloca(Builder.getInt128Ty(), nullptr,"bodygen.alloca128");Builder.restoreIP(CodeGenIP);// Loading and storing captured pointer and valuesBuilder.CreateStore(Val128, Local128);Value *Val32 = Builder.CreateLoad(ValPtr32->getAllocatedType(), ValPtr32,"bodygen.load32");LoadInst *PrivLoad128 = Builder.CreateLoad(Local128->getAllocatedType(), Local128, "bodygen.local.load128");Value *Cmp = Builder.CreateICmpNE(Val32, Builder.CreateTrunc(PrivLoad128, Val32->getType()));Instruction *ThenTerm, *ElseTerm;SplitBlockAndInsertIfThenElse(Cmp, CodeGenIP.getBlock()->getTerminator(),&ThenTerm, &ElseTerm);};BasicBlock *AllocaBB = Builder.GetInsertBlock();BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "alloca.split");OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BodyBB, BodyBB->getFirstInsertionPt()), DL);Builder.restoreIP(OMPBuilder.createTask(Loc, InsertPointTy(AllocaBB, AllocaBB->getFirstInsertionPt()),BodyGenCB));OMPBuilder.finalize();Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M, &errs()));CallInst *TaskAllocCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)->user_back());// Verify the Ident argumentGlobalVariable *Ident = cast<GlobalVariable>(TaskAllocCall->getArgOperand(0));ASSERT_NE(Ident, nullptr);EXPECT_TRUE(Ident->hasInitializer());Constant *Initializer = Ident->getInitializer();GlobalVariable *SrcStrGlob =cast<GlobalVariable>(Initializer->getOperand(4)->stripPointerCasts());ASSERT_NE(SrcStrGlob, nullptr);ConstantDataArray *SrcSrc =dyn_cast<ConstantDataArray>(SrcStrGlob->getInitializer());ASSERT_NE(SrcSrc, nullptr);// Verify the num_threads argument.CallInst *GTID = dyn_cast<CallInst>(TaskAllocCall->getArgOperand(1));ASSERT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num");// Verify the flags// TODO: Check for others flags. Currently testing only for tiedness.ConstantInt *Flags = dyn_cast<ConstantInt>(TaskAllocCall->getArgOperand(2));ASSERT_NE(Flags, nullptr);EXPECT_EQ(Flags->getSExtValue(), 1);// Verify the data sizeConstantInt *DataSize =dyn_cast<ConstantInt>(TaskAllocCall->getArgOperand(3));ASSERT_NE(DataSize, nullptr);EXPECT_EQ(DataSize->getSExtValue(), 24); // 64-bit pointer + 128-bit integer// TODO: Verify size of shared clause variables// Verify Wrapper functionFunction *WrapperFunc =dyn_cast<Function>(TaskAllocCall->getArgOperand(5)->stripPointerCasts());ASSERT_NE(WrapperFunc, nullptr);EXPECT_FALSE(WrapperFunc->isDeclaration());CallInst *OutlinedFnCall = dyn_cast<CallInst>(WrapperFunc->begin()->begin());ASSERT_NE(OutlinedFnCall, nullptr);EXPECT_EQ(WrapperFunc->getArg(0)->getType(), Builder.getInt32Ty());EXPECT_EQ(OutlinedFnCall->getArgOperand(0), WrapperFunc->getArg(1));// Verify the presence of `trunc` and `icmp` instructions in Outlined functionFunction *OutlinedFn = OutlinedFnCall->getCalledFunction();ASSERT_NE(OutlinedFn, nullptr);EXPECT_TRUE(any_of(instructions(OutlinedFn),[](Instruction &inst) { return isa<TruncInst>(&inst); }));EXPECT_TRUE(any_of(instructions(OutlinedFn),[](Instruction &inst) { return isa<ICmpInst>(&inst); }));// Verify the execution of the taskCallInst *TaskCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task)->user_back());ASSERT_NE(TaskCall, nullptr);EXPECT_EQ(TaskCall->getArgOperand(0), Ident);EXPECT_EQ(TaskCall->getArgOperand(1), GTID);EXPECT_EQ(TaskCall->getArgOperand(2), TaskAllocCall);// Verify that the argument data has been copiedfor (User *in : TaskAllocCall->users()) {if (MemCpyInst *memCpyInst = dyn_cast<MemCpyInst>(in)) {EXPECT_EQ(memCpyInst->getDest(), TaskAllocCall);}}}TEST_F(OpenMPIRBuilderTest, CreateTaskNoArgs) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {};BasicBlock *AllocaBB = Builder.GetInsertBlock();BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "alloca.split");OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BodyBB, BodyBB->getFirstInsertionPt()), DL);Builder.restoreIP(OMPBuilder.createTask(Loc, InsertPointTy(AllocaBB, AllocaBB->getFirstInsertionPt()),BodyGenCB));OMPBuilder.finalize();Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateTaskUntied) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {};BasicBlock *AllocaBB = Builder.GetInsertBlock();BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "alloca.split");OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BodyBB, BodyBB->getFirstInsertionPt()), DL);Builder.restoreIP(OMPBuilder.createTask(Loc, InsertPointTy(AllocaBB, AllocaBB->getFirstInsertionPt()), BodyGenCB,/*Tied=*/false));OMPBuilder.finalize();Builder.CreateRetVoid();// Check for the `Tied` argumentCallInst *TaskAllocCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)->user_back());ASSERT_NE(TaskAllocCall, nullptr);ConstantInt *Flags = dyn_cast<ConstantInt>(TaskAllocCall->getArgOperand(2));ASSERT_NE(Flags, nullptr);EXPECT_EQ(Flags->getZExtValue() & 1U, 0U);EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateTaskFinal) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {};IRBuilderBase::InsertPoint AllocaIP = Builder.saveIP();BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "alloca.split");Builder.SetInsertPoint(BodyBB);Value *Final = Builder.CreateICmp(CmpInst::Predicate::ICMP_EQ, F->getArg(0),ConstantInt::get(Type::getInt32Ty(M->getContext()), 0U));OpenMPIRBuilder::LocationDescription Loc(Builder.saveIP(), DL);Builder.restoreIP(OMPBuilder.createTask(Loc, AllocaIP, BodyGenCB,/*Tied=*/false, Final));OMPBuilder.finalize();Builder.CreateRetVoid();// Check for the `Tied` argumentCallInst *TaskAllocCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)->user_back());ASSERT_NE(TaskAllocCall, nullptr);BinaryOperator *OrInst =dyn_cast<BinaryOperator>(TaskAllocCall->getArgOperand(2));ASSERT_NE(OrInst, nullptr);EXPECT_EQ(OrInst->getOpcode(), BinaryOperator::BinaryOps::Or);// One of the arguments to `or` instruction is the tied flag, which is equal// to zero.EXPECT_TRUE(any_of(OrInst->operands(), [](Value *op) {if (ConstantInt *TiedValue = dyn_cast<ConstantInt>(op))return TiedValue->getSExtValue() == 0;return false;}));// One of the arguments to `or` instruction is the final condition.EXPECT_TRUE(any_of(OrInst->operands(), [Final](Value *op) {if (SelectInst *Select = dyn_cast<SelectInst>(op)) {ConstantInt *TrueValue = dyn_cast<ConstantInt>(Select->getTrueValue());ConstantInt *FalseValue = dyn_cast<ConstantInt>(Select->getFalseValue());if (!TrueValue || !FalseValue)return false;return Select->getCondition() == Final &&TrueValue->getSExtValue() == 2 && FalseValue->getSExtValue() == 0;}return false;}));EXPECT_FALSE(verifyModule(*M, &errs()));}TEST_F(OpenMPIRBuilderTest, CreateTaskgroup) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);AllocaInst *ValPtr32 = Builder.CreateAlloca(Builder.getInt32Ty());AllocaInst *ValPtr128 = Builder.CreateAlloca(Builder.getInt128Ty());Value *Val128 =Builder.CreateLoad(Builder.getInt128Ty(), ValPtr128, "bodygen.load");Instruction *ThenTerm, *ElseTerm;Value *InternalStoreInst, *InternalLoad32, *InternalLoad128, *InternalIfCmp;auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {Builder.restoreIP(AllocaIP);AllocaInst *Local128 = Builder.CreateAlloca(Builder.getInt128Ty(), nullptr,"bodygen.alloca128");Builder.restoreIP(CodeGenIP);// Loading and storing captured pointer and valuesInternalStoreInst = Builder.CreateStore(Val128, Local128);InternalLoad32 = Builder.CreateLoad(ValPtr32->getAllocatedType(), ValPtr32,"bodygen.load32");InternalLoad128 = Builder.CreateLoad(Local128->getAllocatedType(), Local128,"bodygen.local.load128");InternalIfCmp = Builder.CreateICmpNE(InternalLoad32,Builder.CreateTrunc(InternalLoad128, InternalLoad32->getType()));SplitBlockAndInsertIfThenElse(InternalIfCmp,CodeGenIP.getBlock()->getTerminator(),&ThenTerm, &ElseTerm);};BasicBlock *AllocaBB = Builder.GetInsertBlock();BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "alloca.split");OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BodyBB, BodyBB->getFirstInsertionPt()), DL);Builder.restoreIP(OMPBuilder.createTaskgroup(Loc, InsertPointTy(AllocaBB, AllocaBB->getFirstInsertionPt()),BodyGenCB));OMPBuilder.finalize();Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M, &errs()));CallInst *TaskgroupCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup)->user_back());ASSERT_NE(TaskgroupCall, nullptr);CallInst *EndTaskgroupCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup)->user_back());ASSERT_NE(EndTaskgroupCall, nullptr);// Verify the Ident argumentGlobalVariable *Ident = cast<GlobalVariable>(TaskgroupCall->getArgOperand(0));ASSERT_NE(Ident, nullptr);EXPECT_TRUE(Ident->hasInitializer());Constant *Initializer = Ident->getInitializer();GlobalVariable *SrcStrGlob =cast<GlobalVariable>(Initializer->getOperand(4)->stripPointerCasts());ASSERT_NE(SrcStrGlob, nullptr);ConstantDataArray *SrcSrc =dyn_cast<ConstantDataArray>(SrcStrGlob->getInitializer());ASSERT_NE(SrcSrc, nullptr);// Verify the num_threads argument.CallInst *GTID = dyn_cast<CallInst>(TaskgroupCall->getArgOperand(1));ASSERT_NE(GTID, nullptr);EXPECT_EQ(GTID->arg_size(), 1U);EXPECT_EQ(GTID->getCalledFunction(), OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num));// Checking the general structure of the IR generated is same as expected.Instruction *GeneratedStoreInst = TaskgroupCall->getNextNonDebugInstruction();EXPECT_EQ(GeneratedStoreInst, InternalStoreInst);Instruction *GeneratedLoad32 =GeneratedStoreInst->getNextNonDebugInstruction();EXPECT_EQ(GeneratedLoad32, InternalLoad32);Instruction *GeneratedLoad128 = GeneratedLoad32->getNextNonDebugInstruction();EXPECT_EQ(GeneratedLoad128, InternalLoad128);// Checking the ordering because of the if statements and that// `__kmp_end_taskgroup` call is after the if branching.BasicBlock *RefOrder[] = {TaskgroupCall->getParent(), ThenTerm->getParent(),ThenTerm->getSuccessor(0),EndTaskgroupCall->getParent(),ElseTerm->getParent()};verifyDFSOrder(F, RefOrder);}TEST_F(OpenMPIRBuilderTest, CreateTaskgroupWithTasks) {using InsertPointTy = OpenMPIRBuilder::InsertPointTy;OpenMPIRBuilder OMPBuilder(*M);OMPBuilder.initialize();F->setName("func");IRBuilder<> Builder(BB);auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {Builder.restoreIP(AllocaIP);AllocaInst *Alloca32 =Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, "bodygen.alloca32");AllocaInst *Alloca64 =Builder.CreateAlloca(Builder.getInt64Ty(), nullptr, "bodygen.alloca64");Builder.restoreIP(CodeGenIP);auto TaskBodyGenCB1 = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {Builder.restoreIP(CodeGenIP);LoadInst *LoadValue =Builder.CreateLoad(Alloca64->getAllocatedType(), Alloca64);Value *AddInst = Builder.CreateAdd(LoadValue, Builder.getInt64(64));Builder.CreateStore(AddInst, Alloca64);};OpenMPIRBuilder::LocationDescription Loc(Builder.saveIP(), DL);Builder.restoreIP(OMPBuilder.createTask(Loc, AllocaIP, TaskBodyGenCB1));auto TaskBodyGenCB2 = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {Builder.restoreIP(CodeGenIP);LoadInst *LoadValue =Builder.CreateLoad(Alloca32->getAllocatedType(), Alloca32);Value *AddInst = Builder.CreateAdd(LoadValue, Builder.getInt32(32));Builder.CreateStore(AddInst, Alloca32);};OpenMPIRBuilder::LocationDescription Loc2(Builder.saveIP(), DL);Builder.restoreIP(OMPBuilder.createTask(Loc2, AllocaIP, TaskBodyGenCB2));};BasicBlock *AllocaBB = Builder.GetInsertBlock();BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "alloca.split");OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BodyBB, BodyBB->getFirstInsertionPt()), DL);Builder.restoreIP(OMPBuilder.createTaskgroup(Loc, InsertPointTy(AllocaBB, AllocaBB->getFirstInsertionPt()),BodyGenCB));OMPBuilder.finalize();Builder.CreateRetVoid();EXPECT_FALSE(verifyModule(*M, &errs()));CallInst *TaskgroupCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup)->user_back());ASSERT_NE(TaskgroupCall, nullptr);CallInst *EndTaskgroupCall = dyn_cast<CallInst>(OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup)->user_back());ASSERT_NE(EndTaskgroupCall, nullptr);Function *TaskAllocFn =OMPBuilder.getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);ASSERT_EQ(TaskAllocFn->getNumUses(), 2u);CallInst *FirstTaskAllocCall =dyn_cast_or_null<CallInst>(*TaskAllocFn->users().begin());CallInst *SecondTaskAllocCall =dyn_cast_or_null<CallInst>(*TaskAllocFn->users().begin()++);ASSERT_NE(FirstTaskAllocCall, nullptr);ASSERT_NE(SecondTaskAllocCall, nullptr);// Verify that the tasks have been generated in order and inside taskgroup// construct.BasicBlock *RefOrder[] = {TaskgroupCall->getParent(), FirstTaskAllocCall->getParent(),SecondTaskAllocCall->getParent(), EndTaskgroupCall->getParent()};verifyDFSOrder(F, RefOrder);}} // namespace
//===- unittest/IR/OpenMPContextTest.cpp - OpenMP Context handling tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Triple.h"#include "llvm/Frontend/OpenMP/OMPConstants.h"#include "llvm/Frontend/OpenMP/OMPContext.h"#include "gtest/gtest.h"using namespace llvm;using namespace omp;namespace {class OpenMPContextTest : public testing::Test {protected:void SetUp() override {}void TearDown() override {}};TEST_F(OpenMPContextTest, RoundTripAndAssociation) {#define OMP_TRAIT_SET(Enum, Str) \EXPECT_EQ(TraitSet::Enum, \getOpenMPContextTraitSetKind( \getOpenMPContextTraitSetName(TraitSet::Enum))); \EXPECT_EQ(Str, \getOpenMPContextTraitSetName(getOpenMPContextTraitSetKind(Str)));#define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, RequiresProperty) \EXPECT_EQ(TraitSelector::Enum, \getOpenMPContextTraitSelectorKind( \getOpenMPContextTraitSelectorName(TraitSelector::Enum))); \EXPECT_EQ(Str, getOpenMPContextTraitSelectorName( \getOpenMPContextTraitSelectorKind(Str)));#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \EXPECT_EQ(TraitProperty::Enum, \getOpenMPContextTraitPropertyKind( \TraitSet::TraitSetEnum, TraitSelector::TraitSelectorEnum, \getOpenMPContextTraitPropertyName(TraitProperty::Enum, Str))); \EXPECT_EQ(Str, getOpenMPContextTraitPropertyName( \getOpenMPContextTraitPropertyKind( \TraitSet::TraitSetEnum, \TraitSelector::TraitSelectorEnum, Str), \Str)); \EXPECT_EQ(TraitSet::TraitSetEnum, \getOpenMPContextTraitSetForProperty(TraitProperty::Enum)); \EXPECT_EQ(TraitSelector::TraitSelectorEnum, \getOpenMPContextTraitSelectorForProperty(TraitProperty::Enum));#include "llvm/Frontend/OpenMP/OMPKinds.def"}TEST_F(OpenMPContextTest, ValidNesting) {bool AllowsTraitScore, ReqProperty;#define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, RequiresProperty) \EXPECT_TRUE(isValidTraitSelectorForTraitSet(TraitSelector::Enum, \TraitSet::TraitSetEnum, \AllowsTraitScore, ReqProperty)); \EXPECT_EQ(RequiresProperty, ReqProperty);#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \EXPECT_TRUE(isValidTraitPropertyForTraitSetAndSelector( \TraitProperty::Enum, TraitSelector::TraitSelectorEnum, \TraitSet::TraitSetEnum));#include "llvm/Frontend/OpenMP/OMPKinds.def"}TEST_F(OpenMPContextTest, ApplicabilityNonConstruct) {OMPContext HostLinux(false, Triple("x86_64-unknown-linux"));OMPContext DeviceLinux(true, Triple("x86_64-unknown-linux"));OMPContext HostNVPTX(false, Triple("nvptx64-nvidia-cuda"));OMPContext DeviceNVPTX(true, Triple("nvptx64-nvidia-cuda"));VariantMatchInfo Empty;EXPECT_TRUE(isVariantApplicableInContext(Empty, HostLinux));EXPECT_TRUE(isVariantApplicableInContext(Empty, DeviceLinux));EXPECT_TRUE(isVariantApplicableInContext(Empty, HostNVPTX));EXPECT_TRUE(isVariantApplicableInContext(Empty, DeviceNVPTX));VariantMatchInfo UserCondFalse;UserCondFalse.addTrait(TraitProperty::user_condition_false, "");EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse, HostLinux));EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse, DeviceLinux));EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse, HostNVPTX));EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse, DeviceNVPTX));VariantMatchInfo DeviceArchArm;DeviceArchArm.addTrait(TraitProperty::device_arch_arm, "");EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm, HostLinux));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm, DeviceLinux));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm, HostNVPTX));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm, DeviceNVPTX));VariantMatchInfo LLVMHostUserCondTrue;LLVMHostUserCondTrue.addTrait(TraitProperty::implementation_vendor_llvm, "");LLVMHostUserCondTrue.addTrait(TraitProperty::device_kind_host, "");LLVMHostUserCondTrue.addTrait(TraitProperty::device_kind_any, "");LLVMHostUserCondTrue.addTrait(TraitProperty::user_condition_true, "");EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrue, HostLinux));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrue, DeviceLinux));EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrue, HostNVPTX));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrue, DeviceNVPTX));VariantMatchInfo LLVMHostUserCondTrueCPU = LLVMHostUserCondTrue;LLVMHostUserCondTrueCPU.addTrait(TraitProperty::device_kind_cpu, "");EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU, HostLinux));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU, DeviceLinux));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU, HostNVPTX));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU, DeviceNVPTX));VariantMatchInfo GPU;GPU.addTrait(TraitProperty::device_kind_gpu, "");EXPECT_FALSE(isVariantApplicableInContext(GPU, HostLinux));EXPECT_FALSE(isVariantApplicableInContext(GPU, DeviceLinux));EXPECT_TRUE(isVariantApplicableInContext(GPU, HostNVPTX));EXPECT_TRUE(isVariantApplicableInContext(GPU, DeviceNVPTX));VariantMatchInfo NoHost;NoHost.addTrait(TraitProperty::device_kind_nohost, "");EXPECT_FALSE(isVariantApplicableInContext(NoHost, HostLinux));EXPECT_TRUE(isVariantApplicableInContext(NoHost, DeviceLinux));EXPECT_FALSE(isVariantApplicableInContext(NoHost, HostNVPTX));EXPECT_TRUE(isVariantApplicableInContext(NoHost, DeviceNVPTX));}TEST_F(OpenMPContextTest, ApplicabilityAllTraits) {OMPContext HostLinuxParallelParallel(false, Triple("x86_64-unknown-linux"));HostLinuxParallelParallel.addTrait(TraitProperty::construct_parallel_parallel);HostLinuxParallelParallel.addTrait(TraitProperty::construct_parallel_parallel);OMPContext DeviceLinuxTargetParallel(true, Triple("x86_64-unknown-linux"));DeviceLinuxTargetParallel.addTrait(TraitProperty::construct_target_target);DeviceLinuxTargetParallel.addTrait(TraitProperty::construct_parallel_parallel);OMPContext HostNVPTXFor(false, Triple("nvptx64-nvidia-cuda"));HostNVPTXFor.addTrait(TraitProperty::construct_for_for);OMPContext DeviceNVPTXTargetTeamsParallel(true,Triple("nvptx64-nvidia-cuda"));DeviceNVPTXTargetTeamsParallel.addTrait(TraitProperty::construct_target_target);DeviceNVPTXTargetTeamsParallel.addTrait(TraitProperty::construct_teams_teams);DeviceNVPTXTargetTeamsParallel.addTrait(TraitProperty::construct_parallel_parallel);{ // non-construct variantsVariantMatchInfo Empty;EXPECT_TRUE(isVariantApplicableInContext(Empty, HostLinuxParallelParallel));EXPECT_TRUE(isVariantApplicableInContext(Empty, DeviceLinuxTargetParallel));EXPECT_TRUE(isVariantApplicableInContext(Empty, HostNVPTXFor));EXPECT_TRUE(isVariantApplicableInContext(Empty, DeviceNVPTXTargetTeamsParallel));VariantMatchInfo UserCondFalse;UserCondFalse.addTrait(TraitProperty::user_condition_false, "");EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse, HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse, DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse, HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(UserCondFalse,DeviceNVPTXTargetTeamsParallel));VariantMatchInfo DeviceArchArm;DeviceArchArm.addTrait(TraitProperty::device_arch_arm, "");EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm, HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm, DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm, HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArm,DeviceNVPTXTargetTeamsParallel));APInt Score(32, 1000);VariantMatchInfo LLVMHostUserCondTrue;LLVMHostUserCondTrue.addTrait(TraitProperty::implementation_vendor_llvm,"");LLVMHostUserCondTrue.addTrait(TraitProperty::device_kind_host, "");LLVMHostUserCondTrue.addTrait(TraitProperty::device_kind_any, "");LLVMHostUserCondTrue.addTrait(TraitProperty::user_condition_true, "",&Score);EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrue,HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrue,DeviceLinuxTargetParallel));EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrue, HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrue,DeviceNVPTXTargetTeamsParallel));VariantMatchInfo LLVMHostUserCondTrueCPU = LLVMHostUserCondTrue;LLVMHostUserCondTrueCPU.addTrait(TraitProperty::device_kind_cpu, "");EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU,HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU,DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU, HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueCPU,DeviceNVPTXTargetTeamsParallel));VariantMatchInfo GPU;GPU.addTrait(TraitProperty::device_kind_gpu, "");EXPECT_FALSE(isVariantApplicableInContext(GPU, HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(GPU, DeviceLinuxTargetParallel));EXPECT_TRUE(isVariantApplicableInContext(GPU, HostNVPTXFor));EXPECT_TRUE(isVariantApplicableInContext(GPU, DeviceNVPTXTargetTeamsParallel));VariantMatchInfo NoHost;NoHost.addTrait(TraitProperty::device_kind_nohost, "");EXPECT_FALSE(isVariantApplicableInContext(NoHost, HostLinuxParallelParallel));EXPECT_TRUE(isVariantApplicableInContext(NoHost, DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(NoHost, HostNVPTXFor));EXPECT_TRUE(isVariantApplicableInContext(NoHost, DeviceNVPTXTargetTeamsParallel));}{ // variants with all setsVariantMatchInfo DeviceArchArmParallel;DeviceArchArmParallel.addTrait(TraitProperty::construct_parallel_parallel,"");DeviceArchArmParallel.addTrait(TraitProperty::device_arch_arm, "");EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArmParallel,HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArmParallel,DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArmParallel, HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(DeviceArchArmParallel,DeviceNVPTXTargetTeamsParallel));VariantMatchInfo LLVMHostUserCondTrueParallel;LLVMHostUserCondTrueParallel.addTrait(TraitProperty::implementation_vendor_llvm, "");LLVMHostUserCondTrueParallel.addTrait(TraitProperty::device_kind_host, "");LLVMHostUserCondTrueParallel.addTrait(TraitProperty::device_kind_any, "");LLVMHostUserCondTrueParallel.addTrait(TraitProperty::user_condition_true,"");LLVMHostUserCondTrueParallel.addTrait(TraitProperty::construct_parallel_parallel, "");EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrueParallel,HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallel,DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallel,HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallel,DeviceNVPTXTargetTeamsParallel));VariantMatchInfo LLVMHostUserCondTrueParallelParallel =LLVMHostUserCondTrueParallel;LLVMHostUserCondTrueParallelParallel.addTrait(TraitProperty::construct_parallel_parallel, "");EXPECT_TRUE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallel, HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallel, DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallel, HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallel, DeviceNVPTXTargetTeamsParallel));VariantMatchInfo LLVMHostUserCondTrueParallelParallelParallel =LLVMHostUserCondTrueParallelParallel;LLVMHostUserCondTrueParallelParallelParallel.addTrait(TraitProperty::construct_parallel_parallel, "");EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallelParallel,HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallelParallel,DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallelParallel, HostNVPTXFor));EXPECT_FALSE(isVariantApplicableInContext(LLVMHostUserCondTrueParallelParallelParallel,DeviceNVPTXTargetTeamsParallel));VariantMatchInfo GPUTargetTeams;GPUTargetTeams.addTrait(TraitProperty::construct_target_target, "");GPUTargetTeams.addTrait(TraitProperty::construct_teams_teams, "");GPUTargetTeams.addTrait(TraitProperty::device_kind_gpu, "");EXPECT_FALSE(isVariantApplicableInContext(GPUTargetTeams,HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(GPUTargetTeams,DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(GPUTargetTeams, HostNVPTXFor));EXPECT_TRUE(isVariantApplicableInContext(GPUTargetTeams,DeviceNVPTXTargetTeamsParallel));VariantMatchInfo GPUTargetParallel;GPUTargetParallel.addTrait(TraitProperty::construct_target_target, "");GPUTargetParallel.addTrait(TraitProperty::construct_parallel_parallel, "");GPUTargetParallel.addTrait(TraitProperty::device_kind_gpu, "");EXPECT_FALSE(isVariantApplicableInContext(GPUTargetParallel,HostLinuxParallelParallel));EXPECT_FALSE(isVariantApplicableInContext(GPUTargetParallel,DeviceLinuxTargetParallel));EXPECT_FALSE(isVariantApplicableInContext(GPUTargetParallel, HostNVPTXFor));EXPECT_TRUE(isVariantApplicableInContext(GPUTargetParallel,DeviceNVPTXTargetTeamsParallel));}}TEST_F(OpenMPContextTest, ScoringSimple) {// TODO: Add scoring tests (via getBestVariantMatchForContext).}} // namespace
//===- llvm/unittest/Frontend/OpenACCTest.cpp - OpenACC Frontend tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/SmallSet.h"#include "llvm/ADT/SmallVector.h"#include "llvm/Frontend/OpenACC/ACC.h.inc"#include "gtest/gtest.h"using namespace llvm;using namespace acc;namespace {static const Clause AllClauses[] = {ACCC_unknown,ACCC_async,ACCC_attach,ACCC_auto,ACCC_bind,ACCC_capture,ACCC_collapse,ACCC_copy,ACCC_copyin,ACCC_copyout,ACCC_create,ACCC_default,ACCC_default_async,ACCC_delete,ACCC_detach,ACCC_device,ACCC_device_num,ACCC_deviceptr,ACCC_device_resident,ACCC_device_type,ACCC_finalize,ACCC_firstprivate,ACCC_gang,ACCC_host,ACCC_if,ACCC_if_present,ACCC_independent,ACCC_link,ACCC_no_create,ACCC_nohost,ACCC_num_gangs,ACCC_num_workers,ACCC_present,ACCC_private,ACCC_read,ACCC_reduction,ACCC_self,ACCC_seq,ACCC_tile,ACCC_unknown,ACCC_use_device,ACCC_vector,ACCC_vector_length,ACCC_wait,ACCC_worker,ACCC_write};TEST(OpenACCTest, DirectiveHelpers) {EXPECT_EQ(getOpenACCDirectiveKind(""), ACCD_unknown);EXPECT_EQ(getOpenACCDirectiveKind("dummy"), ACCD_unknown);EXPECT_EQ(getOpenACCDirectiveKind("atomic"), ACCD_atomic);EXPECT_EQ(getOpenACCDirectiveKind("cache"), ACCD_cache);EXPECT_EQ(getOpenACCDirectiveKind("data"), ACCD_data);EXPECT_EQ(getOpenACCDirectiveKind("declare"), ACCD_declare);EXPECT_EQ(getOpenACCDirectiveKind("enter data"), ACCD_enter_data);EXPECT_EQ(getOpenACCDirectiveKind("exit data"), ACCD_exit_data);EXPECT_EQ(getOpenACCDirectiveKind("host_data"), ACCD_host_data);EXPECT_EQ(getOpenACCDirectiveKind("init"), ACCD_init);EXPECT_EQ(getOpenACCDirectiveKind("kernels"), ACCD_kernels);EXPECT_EQ(getOpenACCDirectiveKind("kernels loop"), ACCD_kernels_loop);EXPECT_EQ(getOpenACCDirectiveKind("loop"), ACCD_loop);EXPECT_EQ(getOpenACCDirectiveKind("parallel"), ACCD_parallel);EXPECT_EQ(getOpenACCDirectiveKind("parallel loop"), ACCD_parallel_loop);EXPECT_EQ(getOpenACCDirectiveKind("routine"), ACCD_routine);EXPECT_EQ(getOpenACCDirectiveKind("serial"), ACCD_serial);EXPECT_EQ(getOpenACCDirectiveKind("serial loop"), ACCD_serial_loop);EXPECT_EQ(getOpenACCDirectiveKind("set"), ACCD_set);EXPECT_EQ(getOpenACCDirectiveKind("shutdown"), ACCD_shutdown);EXPECT_EQ(getOpenACCDirectiveKind("unknown"), ACCD_unknown);EXPECT_EQ(getOpenACCDirectiveKind("update"), ACCD_update);EXPECT_EQ(getOpenACCDirectiveKind("wait"), ACCD_wait);EXPECT_EQ(getOpenACCDirectiveName(ACCD_atomic), "atomic");EXPECT_EQ(getOpenACCDirectiveName(ACCD_cache), "cache");EXPECT_EQ(getOpenACCDirectiveName(ACCD_data), "data");EXPECT_EQ(getOpenACCDirectiveName(ACCD_declare), "declare");EXPECT_EQ(getOpenACCDirectiveName(ACCD_enter_data), "enter data");EXPECT_EQ(getOpenACCDirectiveName(ACCD_exit_data), "exit data");EXPECT_EQ(getOpenACCDirectiveName(ACCD_host_data), "host_data");EXPECT_EQ(getOpenACCDirectiveName(ACCD_init), "init");EXPECT_EQ(getOpenACCDirectiveName(ACCD_kernels), "kernels");EXPECT_EQ(getOpenACCDirectiveName(ACCD_kernels_loop), "kernels loop");EXPECT_EQ(getOpenACCDirectiveName(ACCD_loop), "loop");EXPECT_EQ(getOpenACCDirectiveName(ACCD_parallel), "parallel");EXPECT_EQ(getOpenACCDirectiveName(ACCD_parallel_loop), "parallel loop");EXPECT_EQ(getOpenACCDirectiveName(ACCD_routine), "routine");EXPECT_EQ(getOpenACCDirectiveName(ACCD_serial), "serial");EXPECT_EQ(getOpenACCDirectiveName(ACCD_serial_loop), "serial loop");EXPECT_EQ(getOpenACCDirectiveName(ACCD_set), "set");EXPECT_EQ(getOpenACCDirectiveName(ACCD_shutdown), "shutdown");EXPECT_EQ(getOpenACCDirectiveName(ACCD_unknown), "unknown");EXPECT_EQ(getOpenACCDirectiveName(ACCD_update), "update");EXPECT_EQ(getOpenACCDirectiveName(ACCD_wait), "wait");}TEST(OpenACCTest, ClauseHelpers) {EXPECT_EQ(getOpenACCClauseKind(""), ACCC_unknown);EXPECT_EQ(getOpenACCClauseKind("dummy"), ACCC_unknown);EXPECT_EQ(getOpenACCClauseKind("async"), ACCC_async);EXPECT_EQ(getOpenACCClauseKind("attach"), ACCC_attach);EXPECT_EQ(getOpenACCClauseKind("auto"), ACCC_auto);EXPECT_EQ(getOpenACCClauseKind("bind"), ACCC_bind);EXPECT_EQ(getOpenACCClauseKind("capture"), ACCC_capture);EXPECT_EQ(getOpenACCClauseKind("collapse"), ACCC_collapse);EXPECT_EQ(getOpenACCClauseKind("copy"), ACCC_copy);EXPECT_EQ(getOpenACCClauseKind("copyin"), ACCC_copyin);EXPECT_EQ(getOpenACCClauseKind("copyout"), ACCC_copyout);EXPECT_EQ(getOpenACCClauseKind("create"), ACCC_create);EXPECT_EQ(getOpenACCClauseKind("default"), ACCC_default);EXPECT_EQ(getOpenACCClauseKind("default_async"), ACCC_default_async);EXPECT_EQ(getOpenACCClauseKind("delete"), ACCC_delete);EXPECT_EQ(getOpenACCClauseKind("detach"), ACCC_detach);EXPECT_EQ(getOpenACCClauseKind("device"), ACCC_device);EXPECT_EQ(getOpenACCClauseKind("device_num"), ACCC_device_num);EXPECT_EQ(getOpenACCClauseKind("deviceptr"), ACCC_deviceptr);EXPECT_EQ(getOpenACCClauseKind("device_resident"), ACCC_device_resident);EXPECT_EQ(getOpenACCClauseKind("device_type"), ACCC_device_type);EXPECT_EQ(getOpenACCClauseKind("finalize"), ACCC_finalize);EXPECT_EQ(getOpenACCClauseKind("firstprivate"), ACCC_firstprivate);EXPECT_EQ(getOpenACCClauseKind("gang"), ACCC_gang);EXPECT_EQ(getOpenACCClauseKind("host"), ACCC_host);EXPECT_EQ(getOpenACCClauseKind("if"), ACCC_if);EXPECT_EQ(getOpenACCClauseKind("if_present"), ACCC_if_present);EXPECT_EQ(getOpenACCClauseKind("independent"), ACCC_independent);EXPECT_EQ(getOpenACCClauseKind("link"), ACCC_link);EXPECT_EQ(getOpenACCClauseKind("no_create"), ACCC_no_create);EXPECT_EQ(getOpenACCClauseKind("nohost"), ACCC_nohost);EXPECT_EQ(getOpenACCClauseKind("num_gangs"), ACCC_num_gangs);EXPECT_EQ(getOpenACCClauseKind("num_workers"), ACCC_num_workers);EXPECT_EQ(getOpenACCClauseKind("present"), ACCC_present);EXPECT_EQ(getOpenACCClauseKind("private"), ACCC_private);EXPECT_EQ(getOpenACCClauseKind("read"), ACCC_read);EXPECT_EQ(getOpenACCClauseKind("reduction"), ACCC_reduction);EXPECT_EQ(getOpenACCClauseKind("self"), ACCC_self);EXPECT_EQ(getOpenACCClauseKind("seq"), ACCC_seq);EXPECT_EQ(getOpenACCClauseKind("tile"), ACCC_tile);EXPECT_EQ(getOpenACCClauseKind("unknown"), ACCC_unknown);EXPECT_EQ(getOpenACCClauseKind("use_device"), ACCC_use_device);EXPECT_EQ(getOpenACCClauseKind("vector"), ACCC_vector);EXPECT_EQ(getOpenACCClauseKind("vector_length"), ACCC_vector_length);EXPECT_EQ(getOpenACCClauseKind("wait"), ACCC_wait);EXPECT_EQ(getOpenACCClauseKind("worker"), ACCC_worker);EXPECT_EQ(getOpenACCClauseKind("write"), ACCC_write);EXPECT_EQ(getOpenACCClauseName(ACCC_async), "async");EXPECT_EQ(getOpenACCClauseName(ACCC_attach), "attach");EXPECT_EQ(getOpenACCClauseName(ACCC_auto), "auto");EXPECT_EQ(getOpenACCClauseName(ACCC_bind), "bind");EXPECT_EQ(getOpenACCClauseName(ACCC_capture), "capture");EXPECT_EQ(getOpenACCClauseName(ACCC_collapse), "collapse");EXPECT_EQ(getOpenACCClauseName(ACCC_copy), "copy");EXPECT_EQ(getOpenACCClauseName(ACCC_copyin), "copyin");EXPECT_EQ(getOpenACCClauseName(ACCC_copyout), "copyout");EXPECT_EQ(getOpenACCClauseName(ACCC_create), "create");EXPECT_EQ(getOpenACCClauseName(ACCC_default), "default");EXPECT_EQ(getOpenACCClauseName(ACCC_default_async), "default_async");EXPECT_EQ(getOpenACCClauseName(ACCC_delete), "delete");EXPECT_EQ(getOpenACCClauseName(ACCC_detach), "detach");EXPECT_EQ(getOpenACCClauseName(ACCC_device), "device");EXPECT_EQ(getOpenACCClauseName(ACCC_device_num), "device_num");EXPECT_EQ(getOpenACCClauseName(ACCC_deviceptr), "deviceptr");EXPECT_EQ(getOpenACCClauseName(ACCC_device_resident), "device_resident");EXPECT_EQ(getOpenACCClauseName(ACCC_device_type), "device_type");EXPECT_EQ(getOpenACCClauseName(ACCC_finalize), "finalize");EXPECT_EQ(getOpenACCClauseName(ACCC_firstprivate), "firstprivate");EXPECT_EQ(getOpenACCClauseName(ACCC_gang), "gang");EXPECT_EQ(getOpenACCClauseName(ACCC_host), "host");EXPECT_EQ(getOpenACCClauseName(ACCC_if), "if");EXPECT_EQ(getOpenACCClauseName(ACCC_if_present), "if_present");EXPECT_EQ(getOpenACCClauseName(ACCC_independent), "independent");EXPECT_EQ(getOpenACCClauseName(ACCC_link), "link");EXPECT_EQ(getOpenACCClauseName(ACCC_no_create), "no_create");EXPECT_EQ(getOpenACCClauseName(ACCC_nohost), "nohost");EXPECT_EQ(getOpenACCClauseName(ACCC_num_gangs), "num_gangs");EXPECT_EQ(getOpenACCClauseName(ACCC_num_workers), "num_workers");EXPECT_EQ(getOpenACCClauseName(ACCC_present), "present");EXPECT_EQ(getOpenACCClauseName(ACCC_private), "private");EXPECT_EQ(getOpenACCClauseName(ACCC_read), "read");EXPECT_EQ(getOpenACCClauseName(ACCC_reduction), "reduction");EXPECT_EQ(getOpenACCClauseName(ACCC_self), "self");EXPECT_EQ(getOpenACCClauseName(ACCC_seq), "seq");EXPECT_EQ(getOpenACCClauseName(ACCC_tile), "tile");EXPECT_EQ(getOpenACCClauseName(ACCC_unknown), "unknown");EXPECT_EQ(getOpenACCClauseName(ACCC_use_device), "use_device");EXPECT_EQ(getOpenACCClauseName(ACCC_vector), "vector");EXPECT_EQ(getOpenACCClauseName(ACCC_vector_length), "vector_length");EXPECT_EQ(getOpenACCClauseName(ACCC_wait), "wait");EXPECT_EQ(getOpenACCClauseName(ACCC_worker), "worker");EXPECT_EQ(getOpenACCClauseName(ACCC_write), "write");}static void expectAllowedClauses(Directive Dir, unsigned Version,const ArrayRef<Clause> &AllowedClauses) {SmallSet<Clause, 30> AllowedClausesSet;for (Clause Cl : AllowedClauses) {EXPECT_TRUE(isAllowedClauseForDirective(Dir, Cl, Version));AllowedClausesSet.insert(Cl);}for (Clause Cl : AllClauses) {if (!AllowedClausesSet.contains(Cl)) {EXPECT_FALSE(isAllowedClauseForDirective(Dir, Cl, Version));}}}TEST(OpenACCTest, AllowedClause) {expectAllowedClauses(ACCD_atomic, 3, {});expectAllowedClauses(ACCD_cache, 3, {});expectAllowedClauses(ACCD_unknown, 3, {});expectAllowedClauses(ACCD_parallel, 0, {}); // Version starts at 1expectAllowedClauses(ACCD_data, 3,{ACCC_if, ACCC_attach, ACCC_copy, ACCC_copyin,ACCC_copyout, ACCC_create, ACCC_default, ACCC_deviceptr,ACCC_no_create, ACCC_present});expectAllowedClauses(ACCD_declare, 3,{ACCC_copy, ACCC_copyin, ACCC_copyout, ACCC_create,ACCC_present, ACCC_deviceptr, ACCC_device_resident,ACCC_link});expectAllowedClauses(ACCD_enter_data, 3,{ACCC_async, ACCC_if, ACCC_wait, ACCC_attach, ACCC_create, ACCC_copyin});expectAllowedClauses(ACCD_exit_data, 3,{ACCC_async, ACCC_if, ACCC_wait, ACCC_finalize,ACCC_copyout, ACCC_delete, ACCC_detach});expectAllowedClauses(ACCD_host_data, 3,{ACCC_if, ACCC_if_present, ACCC_use_device});expectAllowedClauses(ACCD_init, 3,{ACCC_device_num, ACCC_device_type, ACCC_if});expectAllowedClauses(ACCD_kernels, 3,{ACCC_attach, ACCC_copy, ACCC_copyin, ACCC_copyout,ACCC_create, ACCC_device_type, ACCC_no_create,ACCC_present, ACCC_deviceptr, ACCC_async, ACCC_default,ACCC_if, ACCC_num_gangs, ACCC_num_workers, ACCC_self,ACCC_vector_length, ACCC_wait});expectAllowedClauses(ACCD_kernels_loop, 3,{ACCC_copy, ACCC_copyin, ACCC_copyout, ACCC_create,ACCC_device_type, ACCC_no_create, ACCC_present, ACCC_private,ACCC_deviceptr, ACCC_attach, ACCC_async, ACCC_collapse,ACCC_default, ACCC_gang, ACCC_if, ACCC_num_gangs,ACCC_num_workers, ACCC_reduction, ACCC_self, ACCC_tile,ACCC_vector, ACCC_vector_length, ACCC_wait, ACCC_worker,ACCC_auto, ACCC_independent, ACCC_seq});expectAllowedClauses(ACCD_loop, 3,{ACCC_device_type, ACCC_private, ACCC_collapse,ACCC_gang, ACCC_reduction, ACCC_tile, ACCC_vector,ACCC_worker, ACCC_auto, ACCC_independent, ACCC_seq});expectAllowedClauses(ACCD_parallel, 3,{ACCC_async, ACCC_wait, ACCC_num_gangs,ACCC_num_workers, ACCC_vector_length, ACCC_device_type,ACCC_if, ACCC_self, ACCC_reduction,ACCC_copy, ACCC_copyin, ACCC_copyout,ACCC_create, ACCC_no_create, ACCC_present,ACCC_deviceptr, ACCC_attach, ACCC_private,ACCC_firstprivate, ACCC_default});expectAllowedClauses(ACCD_parallel_loop, 3,{ACCC_attach, ACCC_copy, ACCC_copyin, ACCC_copyout,ACCC_create, ACCC_deviceptr, ACCC_device_type, ACCC_firstprivate,ACCC_no_create, ACCC_present, ACCC_private, ACCC_tile,ACCC_wait, ACCC_async, ACCC_collapse, ACCC_default,ACCC_gang, ACCC_if, ACCC_num_gangs, ACCC_num_workers,ACCC_reduction, ACCC_self, ACCC_vector, ACCC_vector_length,ACCC_worker, ACCC_auto, ACCC_independent, ACCC_seq});expectAllowedClauses(ACCD_routine, 3,{ACCC_bind, ACCC_device_type, ACCC_nohost, ACCC_gang,ACCC_seq, ACCC_vector, ACCC_worker});expectAllowedClauses(ACCD_serial, 3,{ACCC_attach, ACCC_copy, ACCC_copyin, ACCC_copyout,ACCC_create, ACCC_deviceptr, ACCC_device_type,ACCC_firstprivate, ACCC_no_create, ACCC_present,ACCC_private, ACCC_wait, ACCC_async, ACCC_default,ACCC_if, ACCC_reduction, ACCC_self});expectAllowedClauses(ACCD_serial_loop, 3,{ACCC_attach, ACCC_copy, ACCC_copyin, ACCC_copyout,ACCC_create, ACCC_deviceptr, ACCC_device_type, ACCC_firstprivate,ACCC_no_create, ACCC_present, ACCC_private, ACCC_wait,ACCC_async, ACCC_collapse, ACCC_default, ACCC_gang,ACCC_if, ACCC_reduction, ACCC_self, ACCC_tile,ACCC_vector, ACCC_worker, ACCC_auto, ACCC_independent,ACCC_seq});expectAllowedClauses(ACCD_set, 3,{ACCC_default_async, ACCC_device_num, ACCC_device_type, ACCC_if});expectAllowedClauses(ACCD_shutdown, 3,{ACCC_device_num, ACCC_device_type, ACCC_if});expectAllowedClauses(ACCD_update, 3,{ACCC_async, ACCC_wait, ACCC_device_type, ACCC_if,ACCC_if_present, ACCC_self, ACCC_host, ACCC_device});expectAllowedClauses(ACCD_wait, 3, {ACCC_async, ACCC_if});}} // namespace
set(LLVM_LINK_COMPONENTSAnalysisCoreFrontendOpenMPFrontendOpenACCSupportPassesTransformUtils)add_llvm_unittest(LLVMFrontendTestsOpenACCTest.cppOpenMPContextTest.cppOpenMPIRBuilderTest.cppOpenMPParsingTest.cppDEPENDSacc_genomp_gen)target_link_libraries(LLVMFrontendTests PRIVATE LLVMTestingSupport)
//===- llvm/unittest/FileCheck/FileCheckTest.cpp - FileCheck tests --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/FileCheck/FileCheck.h"#include "../lib/FileCheck/FileCheckImpl.h"#include "llvm/Support/Regex.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <tuple>#include <unordered_set>using namespace llvm;namespace {class FileCheckTest : public ::testing::Test {};static StringRef bufferize(SourceMgr &SM, StringRef Str) {std::unique_ptr<MemoryBuffer> Buffer =MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");StringRef StrBufferRef = Buffer->getBuffer();SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());return StrBufferRef;}static std::string toString(const std::unordered_set<std::string> &Set) {bool First = true;std::string Str;for (StringRef S : Set) {Str += Twine(First ? "{" + S : ", " + S).str();First = false;}Str += '}';return Str;}template <typename ErrorT>static void expectSameErrors(std::unordered_set<std::string> ExpectedMsgs,Error Err) {auto AnyErrorMsgMatch = [&ExpectedMsgs](std::string &&ErrorMsg) -> bool {for (auto ExpectedMsgItr = ExpectedMsgs.begin(),ExpectedMsgEnd = ExpectedMsgs.end();ExpectedMsgItr != ExpectedMsgEnd; ++ExpectedMsgItr) {if (ErrorMsg.find(*ExpectedMsgItr) != std::string::npos) {ExpectedMsgs.erase(ExpectedMsgItr);return true;}}return false;};Error RemainingErrors = std::move(Err);do {RemainingErrors =handleErrors(std::move(RemainingErrors), [&](const ErrorT &E) {EXPECT_TRUE(AnyErrorMsgMatch(E.message()))<< "Unexpected error message:" << std::endl<< E.message();});} while (RemainingErrors && !ExpectedMsgs.empty());EXPECT_THAT_ERROR(std::move(RemainingErrors), Succeeded());EXPECT_TRUE(ExpectedMsgs.empty())<< "Error message(s) not found:" << std::endl<< toString(ExpectedMsgs);}template <typename ErrorT>static void expectError(StringRef ExpectedMsg, Error Err) {expectSameErrors<ErrorT>({ExpectedMsg.str()}, std::move(Err));}static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));}constexpr uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max();constexpr int64_t MaxInt64 = std::numeric_limits<int64_t>::max();constexpr int64_t MinInt64 = std::numeric_limits<int64_t>::min();constexpr uint64_t AbsoluteMinInt64 =static_cast<uint64_t>(-(MinInt64 + 1)) + 1;constexpr uint64_t AbsoluteMaxInt64 = static_cast<uint64_t>(MaxInt64);struct ExpressionFormatParameterisedFixture: public ::testing::TestWithParam<std::tuple<ExpressionFormat::Kind, unsigned, bool>> {bool AlternateForm;unsigned Precision;bool Signed;bool AllowHex;bool AllowUpperHex;ExpressionFormat Format;Regex WildcardRegex;StringRef TenStr;StringRef FifteenStr;std::string MaxUint64Str;std::string MaxInt64Str;std::string MinInt64Str;StringRef FirstInvalidCharDigits;StringRef AcceptedHexOnlyDigits;StringRef RefusedHexOnlyDigits;SourceMgr SM;void SetUp() override {ExpressionFormat::Kind Kind;std::tie(Kind, Precision, AlternateForm) = GetParam();AllowHex = Kind == ExpressionFormat::Kind::HexLower ||Kind == ExpressionFormat::Kind::HexUpper;AllowUpperHex = Kind == ExpressionFormat::Kind::HexUpper;Signed = Kind == ExpressionFormat::Kind::Signed;Format = ExpressionFormat(Kind, Precision, AlternateForm);if (!AllowHex) {MaxUint64Str = std::to_string(MaxUint64);MaxInt64Str = std::to_string(MaxInt64);MinInt64Str = std::to_string(MinInt64);TenStr = "10";FifteenStr = "15";FirstInvalidCharDigits = "aA";AcceptedHexOnlyDigits = RefusedHexOnlyDigits = "N/A";return;}MaxUint64Str = AllowUpperHex ? "FFFFFFFFFFFFFFFF" : "ffffffffffffffff";MaxInt64Str = AllowUpperHex ? "7FFFFFFFFFFFFFFF" : "7fffffffffffffff";TenStr = AllowUpperHex ? "A" : "a";FifteenStr = AllowUpperHex ? "F" : "f";AcceptedHexOnlyDigits = AllowUpperHex ? "ABCDEF" : "abcdef";RefusedHexOnlyDigits = AllowUpperHex ? "abcdef" : "ABCDEF";MinInt64Str = "N/A";FirstInvalidCharDigits = "gG";}void checkWildcardRegexMatch(StringRef Input,unsigned TrailExtendTo = 0) const {ASSERT_TRUE(TrailExtendTo == 0 || AllowHex);SmallVector<StringRef, 4> Matches;std::string ExtendedInput = Input.str();size_t PrefixSize = AlternateForm ? 2 : 0;if (TrailExtendTo > Input.size() - PrefixSize) {size_t ExtensionSize = PrefixSize + TrailExtendTo - Input.size();ExtendedInput.append(ExtensionSize, Input[PrefixSize]);}ASSERT_TRUE(WildcardRegex.match(ExtendedInput, &Matches))<< "Wildcard regex does not match " << ExtendedInput;EXPECT_EQ(Matches[0], ExtendedInput);}void checkWildcardRegexMatchFailure(StringRef Input) const {EXPECT_FALSE(WildcardRegex.match(Input));}std::string addBasePrefix(StringRef Num) const {StringRef Prefix = AlternateForm ? "0x" : "";return (Twine(Prefix) + Twine(Num)).str();}void checkPerCharWildcardRegexMatchFailure(StringRef Chars) const {for (auto C : Chars) {std::string Str = addBasePrefix(StringRef(&C, 1));EXPECT_FALSE(WildcardRegex.match(Str));}}std::string padWithLeadingZeros(StringRef NumStr) const {bool Negative = NumStr.startswith("-");if (NumStr.size() - unsigned(Negative) >= Precision)return NumStr.str();std::string PaddedStr;if (Negative) {PaddedStr = "-";NumStr = NumStr.drop_front();}PaddedStr.append(Precision - NumStr.size(), '0');PaddedStr.append(NumStr.str());return PaddedStr;}template <class T> void checkMatchingString(T Val, StringRef ExpectedStr) {Expected<std::string> MatchingString =Format.getMatchingString(ExpressionValue(Val));ASSERT_THAT_EXPECTED(MatchingString, Succeeded())<< "No matching string for " << Val;EXPECT_EQ(*MatchingString, ExpectedStr);}template <class T> void checkMatchingStringFailure(T Val) {Expected<std::string> MatchingString =Format.getMatchingString(ExpressionValue(Val));// Error message tested in ExpressionValue unit tests.EXPECT_THAT_EXPECTED(MatchingString, Failed());}Expected<ExpressionValue> getValueFromStringReprFailure(StringRef Str) {StringRef BufferizedStr = bufferize(SM, Str);return Format.valueFromStringRepr(BufferizedStr, SM);}template <class T>void checkValueFromStringRepr(StringRef Str, T ExpectedVal) {Expected<ExpressionValue> ResultValue = getValueFromStringReprFailure(Str);ASSERT_THAT_EXPECTED(ResultValue, Succeeded())<< "Failed to get value from " << Str;ASSERT_EQ(ResultValue->isNegative(), ExpectedVal < 0)<< "Value for " << Str << " is not " << ExpectedVal;if (ResultValue->isNegative())EXPECT_EQ(cantFail(ResultValue->getSignedValue()),static_cast<int64_t>(ExpectedVal));elseEXPECT_EQ(cantFail(ResultValue->getUnsignedValue()),static_cast<uint64_t>(ExpectedVal));}void checkValueFromStringReprFailure(StringRef Str, StringRef ErrorStr = "unable to represent numeric value") {Expected<ExpressionValue> ResultValue = getValueFromStringReprFailure(Str);expectDiagnosticError(ErrorStr, ResultValue.takeError());}};TEST_P(ExpressionFormatParameterisedFixture, FormatGetWildcardRegex) {// Wildcard regex is valid.Expected<std::string> WildcardPattern = Format.getWildcardRegex();ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());WildcardRegex = Regex((Twine("^") + *WildcardPattern + "$").str());ASSERT_TRUE(WildcardRegex.isValid());// Does not match empty string.checkWildcardRegexMatchFailure("");// Matches all decimal digits, matches several of them and match 0x prefix// if and only if AlternateForm is true.StringRef LongNumber = "12345678901234567890";StringRef PrefixedLongNumber = "0x12345678901234567890";if (AlternateForm) {checkWildcardRegexMatch(PrefixedLongNumber);checkWildcardRegexMatchFailure(LongNumber);} else {checkWildcardRegexMatch(LongNumber);checkWildcardRegexMatchFailure(PrefixedLongNumber);}// Matches negative digits.LongNumber = "-12345678901234567890";if (Signed)checkWildcardRegexMatch(LongNumber);elsecheckWildcardRegexMatchFailure(LongNumber);// Check non digits or digits with wrong casing are not matched.std::string LongNumberStr;if (AllowHex) {LongNumberStr = addBasePrefix(AcceptedHexOnlyDigits);checkWildcardRegexMatch(LongNumberStr, 16);checkPerCharWildcardRegexMatchFailure(RefusedHexOnlyDigits);}checkPerCharWildcardRegexMatchFailure(FirstInvalidCharDigits);// Check leading zeros are only accepted if number of digits is less than the// precision.LongNumber = "01234567890123456789";if (Precision) {LongNumberStr = addBasePrefix(LongNumber.take_front(Precision));checkWildcardRegexMatch(LongNumberStr);LongNumberStr = addBasePrefix(LongNumber.take_front(Precision - 1));checkWildcardRegexMatchFailure(LongNumberStr);if (Precision < LongNumber.size()) {LongNumberStr = addBasePrefix(LongNumber.take_front(Precision + 1));checkWildcardRegexMatchFailure(LongNumberStr);}} else {LongNumberStr = addBasePrefix(LongNumber);checkWildcardRegexMatch(LongNumberStr);}}TEST_P(ExpressionFormatParameterisedFixture, FormatGetMatchingString) {checkMatchingString(0, addBasePrefix(padWithLeadingZeros("0")));checkMatchingString(9, addBasePrefix(padWithLeadingZeros("9")));if (Signed) {checkMatchingString(-5, padWithLeadingZeros("-5"));checkMatchingStringFailure(MaxUint64);checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str));checkMatchingString(MinInt64, padWithLeadingZeros(MinInt64Str));} else {checkMatchingStringFailure(-5);checkMatchingString(MaxUint64,addBasePrefix(padWithLeadingZeros(MaxUint64Str)));checkMatchingString(MaxInt64,addBasePrefix(padWithLeadingZeros(MaxInt64Str)));checkMatchingStringFailure(MinInt64);}checkMatchingString(10, addBasePrefix(padWithLeadingZeros(TenStr)));checkMatchingString(15, addBasePrefix(padWithLeadingZeros(FifteenStr)));}TEST_P(ExpressionFormatParameterisedFixture, FormatValueFromStringRepr) {checkValueFromStringRepr(addBasePrefix("0"), 0);checkValueFromStringRepr(addBasePrefix("9"), 9);if (Signed) {checkValueFromStringRepr("-5", -5);checkValueFromStringReprFailure(MaxUint64Str);} else {checkValueFromStringReprFailure("-" + addBasePrefix("5"));checkValueFromStringRepr(addBasePrefix(MaxUint64Str), MaxUint64);}checkValueFromStringRepr(addBasePrefix(TenStr), 10);checkValueFromStringRepr(addBasePrefix(FifteenStr), 15);// Wrong casing is not tested because valueFromStringRepr() relies on// StringRef's getAsInteger() which does not allow to restrict casing.checkValueFromStringReprFailure(addBasePrefix("G"));if (AlternateForm)checkValueFromStringReprFailure("9", "missing alternate form prefix");}TEST_P(ExpressionFormatParameterisedFixture, FormatBoolOperator) {EXPECT_TRUE(bool(Format));}INSTANTIATE_TEST_SUITE_P(AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,::testing::Values(std::make_tuple(ExpressionFormat::Kind::Unsigned, 0, false),std::make_tuple(ExpressionFormat::Kind::Signed, 0, false),std::make_tuple(ExpressionFormat::Kind::HexLower, 0, false),std::make_tuple(ExpressionFormat::Kind::HexLower, 0, true),std::make_tuple(ExpressionFormat::Kind::HexUpper, 0, false),std::make_tuple(ExpressionFormat::Kind::HexUpper, 0, true),std::make_tuple(ExpressionFormat::Kind::Unsigned, 1, false),std::make_tuple(ExpressionFormat::Kind::Signed, 1, false),std::make_tuple(ExpressionFormat::Kind::HexLower, 1, false),std::make_tuple(ExpressionFormat::Kind::HexLower, 1, true),std::make_tuple(ExpressionFormat::Kind::HexUpper, 1, false),std::make_tuple(ExpressionFormat::Kind::HexUpper, 1, true),std::make_tuple(ExpressionFormat::Kind::Unsigned, 16, false),std::make_tuple(ExpressionFormat::Kind::Signed, 16, false),std::make_tuple(ExpressionFormat::Kind::HexLower, 16, false),std::make_tuple(ExpressionFormat::Kind::HexLower, 16, true),std::make_tuple(ExpressionFormat::Kind::HexUpper, 16, false),std::make_tuple(ExpressionFormat::Kind::HexUpper, 16, true),std::make_tuple(ExpressionFormat::Kind::Unsigned, 20, false),std::make_tuple(ExpressionFormat::Kind::Signed, 20, false)));TEST_F(FileCheckTest, NoFormatProperties) {ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);expectError<StringError>("trying to match value with invalid format",NoFormat.getWildcardRegex().takeError());expectError<StringError>("trying to match value with invalid format",NoFormat.getMatchingString(ExpressionValue(18u)).takeError());EXPECT_FALSE(bool(NoFormat));}TEST_F(FileCheckTest, FormatEqualityOperators) {ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);EXPECT_TRUE(UnsignedFormat == UnsignedFormat2);EXPECT_FALSE(UnsignedFormat != UnsignedFormat2);ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower);EXPECT_FALSE(UnsignedFormat == HexLowerFormat);EXPECT_TRUE(UnsignedFormat != HexLowerFormat);ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);EXPECT_FALSE(NoFormat == NoFormat2);EXPECT_TRUE(NoFormat != NoFormat2);}TEST_F(FileCheckTest, FormatKindEqualityOperators) {ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned);EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower);EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower);ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);}template <class T1, class T2>static Expected<ExpressionValue> doValueOperation(binop_eval_t Operation,T1 LeftValue, T2 RightValue) {ExpressionValue LeftOperand(LeftValue);ExpressionValue RightOperand(RightValue);return Operation(LeftOperand, RightOperand);}template <class T>static void expectValueEqual(ExpressionValue ActualValue, T ExpectedValue) {EXPECT_EQ(ExpectedValue < 0, ActualValue.isNegative());if (ExpectedValue < 0) {Expected<int64_t> SignedActualValue = ActualValue.getSignedValue();ASSERT_THAT_EXPECTED(SignedActualValue, Succeeded());EXPECT_EQ(*SignedActualValue, static_cast<int64_t>(ExpectedValue));} else {Expected<uint64_t> UnsignedActualValue = ActualValue.getUnsignedValue();ASSERT_THAT_EXPECTED(UnsignedActualValue, Succeeded());EXPECT_EQ(*UnsignedActualValue, static_cast<uint64_t>(ExpectedValue));}}template <class T1, class T2, class TR>static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue,T2 RightValue, TR ResultValue) {Expected<ExpressionValue> OperationResult =doValueOperation(Operation, LeftValue, RightValue);ASSERT_THAT_EXPECTED(OperationResult, Succeeded());expectValueEqual(*OperationResult, ResultValue);}template <class T1, class T2>static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue,T2 RightValue) {expectError<OverflowError>("overflow error",doValueOperation(Operation, LeftValue, RightValue).takeError());}TEST_F(FileCheckTest, ExpressionValueGetUnsigned) {// Test positive value.Expected<uint64_t> UnsignedValue = ExpressionValue(10).getUnsignedValue();ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());EXPECT_EQ(*UnsignedValue, 10U);// Test 0.UnsignedValue = ExpressionValue(0).getUnsignedValue();ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());EXPECT_EQ(*UnsignedValue, 0U);// Test max positive value.UnsignedValue = ExpressionValue(MaxUint64).getUnsignedValue();ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());EXPECT_EQ(*UnsignedValue, MaxUint64);// Test failure with negative value.expectError<OverflowError>("overflow error", ExpressionValue(-1).getUnsignedValue().takeError());// Test failure with min negative value.expectError<OverflowError>("overflow error",ExpressionValue(MinInt64).getUnsignedValue().takeError());}TEST_F(FileCheckTest, ExpressionValueGetSigned) {// Test positive value.Expected<int64_t> SignedValue = ExpressionValue(10).getSignedValue();ASSERT_THAT_EXPECTED(SignedValue, Succeeded());EXPECT_EQ(*SignedValue, 10);// Test 0.SignedValue = ExpressionValue(0).getSignedValue();ASSERT_THAT_EXPECTED(SignedValue, Succeeded());EXPECT_EQ(*SignedValue, 0);// Test max int64_t.SignedValue = ExpressionValue(MaxInt64).getSignedValue();ASSERT_THAT_EXPECTED(SignedValue, Succeeded());EXPECT_EQ(*SignedValue, MaxInt64);// Test failure with too big positive value.expectError<OverflowError>("overflow error", ExpressionValue(static_cast<uint64_t>(MaxInt64) + 1).getSignedValue().takeError());// Test failure with max uint64_t.expectError<OverflowError>("overflow error",ExpressionValue(MaxUint64).getSignedValue().takeError());// Test negative value.SignedValue = ExpressionValue(-10).getSignedValue();ASSERT_THAT_EXPECTED(SignedValue, Succeeded());EXPECT_EQ(*SignedValue, -10);// Test min int64_t.SignedValue = ExpressionValue(MinInt64).getSignedValue();ASSERT_THAT_EXPECTED(SignedValue, Succeeded());EXPECT_EQ(*SignedValue, MinInt64);}TEST_F(FileCheckTest, ExpressionValueAbsolute) {// Test positive value.expectValueEqual(ExpressionValue(10).getAbsolute(), 10);// Test 0.expectValueEqual(ExpressionValue(0).getAbsolute(), 0);// Test max uint64_t.expectValueEqual(ExpressionValue(MaxUint64).getAbsolute(), MaxUint64);// Test negative value.expectValueEqual(ExpressionValue(-10).getAbsolute(), 10);// Test absence of overflow on min int64_t.expectValueEqual(ExpressionValue(MinInt64).getAbsolute(),static_cast<uint64_t>(-(MinInt64 + 10)) + 10);}TEST_F(FileCheckTest, ExpressionValueAddition) {// Test both negative values.expectOperationValueResult(operator+, -10, -10, -20);// Test both negative values with underflow.expectOperationValueResult(operator+, MinInt64, -1);expectOperationValueResult(operator+, MinInt64, MinInt64);// Test negative and positive value.expectOperationValueResult(operator+, -10, 10, 0);expectOperationValueResult(operator+, -10, 11, 1);expectOperationValueResult(operator+, -11, 10, -1);// Test positive and negative value.expectOperationValueResult(operator+, 10, -10, 0);expectOperationValueResult(operator+, 10, -11, -1);expectOperationValueResult(operator+, 11, -10, 1);// Test both positive values.expectOperationValueResult(operator+, 10, 10, 20);// Test both positive values with overflow.expectOperationValueResult(operator+, MaxUint64, 1);expectOperationValueResult(operator+, MaxUint64, MaxUint64);}TEST_F(FileCheckTest, ExpressionValueSubtraction) {// Test negative value and value bigger than int64_t max.expectOperationValueResult(operator-, -10, MaxUint64);// Test negative and positive value with underflow.expectOperationValueResult(operator-, MinInt64, 1);// Test negative and positive value.expectOperationValueResult(operator-, -10, 10, -20);// Test both negative values.expectOperationValueResult(operator-, -10, -10, 0);expectOperationValueResult(operator-, -11, -10, -1);expectOperationValueResult(operator-, -10, -11, 1);// Test positive and negative values.expectOperationValueResult(operator-, 10, -10, 20);// Test both positive values with result positive.expectOperationValueResult(operator-, 10, 5, 5);// Test both positive values with underflow.expectOperationValueResult(operator-, 0, MaxUint64);expectOperationValueResult(operator-, 0,static_cast<uint64_t>(-(MinInt64 + 10)) + 11);// Test both positive values with result < -(max int64_t)expectOperationValueResult(operator-, 10,static_cast<uint64_t>(MaxInt64) + 11,-MaxInt64 - 1);// Test both positive values with 0 > result > -(max int64_t)expectOperationValueResult(operator-, 10, 11, -1);}TEST_F(FileCheckTest, ExpressionValueMultiplication) {// Test mixed signed values.expectOperationValueResult(operator*, -3, 10, -30);expectOperationValueResult(operator*, 2, -17, -34);expectOperationValueResult(operator*, 0, MinInt64, 0);expectOperationValueResult(operator*, MinInt64, 1, MinInt64);expectOperationValueResult(operator*, 1, MinInt64, MinInt64);expectOperationValueResult(operator*, MaxInt64, -1, -MaxInt64);expectOperationValueResult(operator*, -1, MaxInt64, -MaxInt64);// Test both negative values.expectOperationValueResult(operator*, -3, -10, 30);expectOperationValueResult(operator*, -2, -17, 34);expectOperationValueResult(operator*, MinInt64, -1, AbsoluteMinInt64);// Test both positive values.expectOperationValueResult(operator*, 3, 10, 30);expectOperationValueResult(operator*, 2, 17, 34);expectOperationValueResult(operator*, 0, MaxUint64, 0);// Test negative results that underflow.expectOperationValueResult(operator*, -10, MaxInt64);expectOperationValueResult(operator*, MaxInt64, -10);expectOperationValueResult(operator*, 10, MinInt64);expectOperationValueResult(operator*, MinInt64, 10);expectOperationValueResult(operator*, -1, MaxUint64);expectOperationValueResult(operator*, MaxUint64, -1);expectOperationValueResult(operator*, -1, AbsoluteMaxInt64 + 2);expectOperationValueResult(operator*, AbsoluteMaxInt64 + 2, -1);// Test positive results that overflow.expectOperationValueResult(operator*, 10, MaxUint64);expectOperationValueResult(operator*, MaxUint64, 10);expectOperationValueResult(operator*, MinInt64, -10);expectOperationValueResult(operator*, -10, MinInt64);}TEST_F(FileCheckTest, ExpressionValueDivision) {// Test mixed signed values.expectOperationValueResult(operator/, -30, 10, -3);expectOperationValueResult(operator/, 34, -17, -2);expectOperationValueResult(operator/, 0, -10, 0);expectOperationValueResult(operator/, MinInt64, 1, MinInt64);expectOperationValueResult(operator/, MaxInt64, -1, -MaxInt64);expectOperationValueResult(operator/, -MaxInt64, 1, -MaxInt64);// Test both negative values.expectOperationValueResult(operator/, -30, -10, 3);expectOperationValueResult(operator/, -34, -17, 2);// Test both positive values.expectOperationValueResult(operator/, 30, 10, 3);expectOperationValueResult(operator/, 34, 17, 2);expectOperationValueResult(operator/, 0, 10, 0);// Test divide by zero.expectOperationValueResult(operator/, -10, 0);expectOperationValueResult(operator/, 10, 0);expectOperationValueResult(operator/, 0, 0);// Test negative result that underflows.expectOperationValueResult(operator/, MaxUint64, -1);expectOperationValueResult(operator/, AbsoluteMaxInt64 + 2, -1);}TEST_F(FileCheckTest, ExpressionValueEquality) {// Test negative and positive value.EXPECT_FALSE(ExpressionValue(5) == ExpressionValue(-3));EXPECT_TRUE(ExpressionValue(5) != ExpressionValue(-3));EXPECT_FALSE(ExpressionValue(-2) == ExpressionValue(6));EXPECT_TRUE(ExpressionValue(-2) != ExpressionValue(6));EXPECT_FALSE(ExpressionValue(-7) == ExpressionValue(7));EXPECT_TRUE(ExpressionValue(-7) != ExpressionValue(7));EXPECT_FALSE(ExpressionValue(4) == ExpressionValue(-4));EXPECT_TRUE(ExpressionValue(4) != ExpressionValue(-4));EXPECT_FALSE(ExpressionValue(MaxUint64) == ExpressionValue(-1));EXPECT_TRUE(ExpressionValue(MaxUint64) != ExpressionValue(-1));// Test both negative values.EXPECT_FALSE(ExpressionValue(-2) == ExpressionValue(-7));EXPECT_TRUE(ExpressionValue(-2) != ExpressionValue(-7));EXPECT_TRUE(ExpressionValue(-3) == ExpressionValue(-3));EXPECT_FALSE(ExpressionValue(-3) != ExpressionValue(-3));EXPECT_FALSE(ExpressionValue(MinInt64) == ExpressionValue(-1));EXPECT_TRUE(ExpressionValue(MinInt64) != ExpressionValue(-1));EXPECT_FALSE(ExpressionValue(MinInt64) == ExpressionValue(-0));EXPECT_TRUE(ExpressionValue(MinInt64) != ExpressionValue(-0));// Test both positive values.EXPECT_FALSE(ExpressionValue(8) == ExpressionValue(9));EXPECT_TRUE(ExpressionValue(8) != ExpressionValue(9));EXPECT_TRUE(ExpressionValue(1) == ExpressionValue(1));EXPECT_FALSE(ExpressionValue(1) != ExpressionValue(1));// Check the signedness of zero doesn't affect equality.EXPECT_TRUE(ExpressionValue(0) == ExpressionValue(0));EXPECT_FALSE(ExpressionValue(0) != ExpressionValue(0));EXPECT_TRUE(ExpressionValue(0) == ExpressionValue(-0));EXPECT_FALSE(ExpressionValue(0) != ExpressionValue(-0));EXPECT_TRUE(ExpressionValue(-0) == ExpressionValue(0));EXPECT_FALSE(ExpressionValue(-0) != ExpressionValue(0));EXPECT_TRUE(ExpressionValue(-0) == ExpressionValue(-0));EXPECT_FALSE(ExpressionValue(-0) != ExpressionValue(-0));}TEST_F(FileCheckTest, Literal) {SourceMgr SM;// Eval returns the literal's value.ExpressionLiteral Ten(bufferize(SM, "10"), 10u);Expected<ExpressionValue> Value = Ten.eval();ASSERT_THAT_EXPECTED(Value, Succeeded());EXPECT_EQ(10, cantFail(Value->getSignedValue()));Expected<ExpressionFormat> ImplicitFormat = Ten.getImplicitFormat(SM);ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::NoFormat);// Min value can be correctly represented.ExpressionLiteral Min(bufferize(SM, std::to_string(MinInt64)), MinInt64);Value = Min.eval();ASSERT_TRUE(bool(Value));EXPECT_EQ(MinInt64, cantFail(Value->getSignedValue()));// Max value can be correctly represented.ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64);Value = Max.eval();ASSERT_THAT_EXPECTED(Value, Succeeded());EXPECT_EQ(MaxUint64, cantFail(Value->getUnsignedValue()));}TEST_F(FileCheckTest, Expression) {SourceMgr SM;std::unique_ptr<ExpressionLiteral> Ten =std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10u);ExpressionLiteral *TenPtr = Ten.get();Expression Expr(std::move(Ten),ExpressionFormat(ExpressionFormat::Kind::HexLower));EXPECT_EQ(Expr.getAST(), TenPtr);EXPECT_EQ(Expr.getFormat(), ExpressionFormat::Kind::HexLower);}static voidexpectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,Error Err) {EXPECT_THAT_ERROR(handleErrors(std::move(Err),[&](const UndefVarError &E) {EXPECT_EQ(ExpectedUndefVarNames.erase(std::string(E.getVarName())),1U);}),Succeeded());EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);}TEST_F(FileCheckTest, NumericVariable) {SourceMgr SM;// Undefined variable: getValue and eval fail, error returned by eval holds// the name of the undefined variable.NumericVariable FooVar("FOO",ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);EXPECT_EQ("FOO", FooVar.getName());EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);NumericVariableUse FooVarUse("FOO", &FooVar);Expected<ExpressionFormat> ImplicitFormat = FooVarUse.getImplicitFormat(SM);ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);EXPECT_FALSE(FooVar.getValue());Expected<ExpressionValue> EvalResult = FooVarUse.eval();expectUndefErrors({"FOO"}, EvalResult.takeError());// Defined variable without string: only getValue and eval return value set.FooVar.setValue(ExpressionValue(42u));Optional<ExpressionValue> Value = FooVar.getValue();ASSERT_TRUE(Value);EXPECT_EQ(42, cantFail(Value->getSignedValue()));EXPECT_FALSE(FooVar.getStringValue());EvalResult = FooVarUse.eval();ASSERT_THAT_EXPECTED(EvalResult, Succeeded());EXPECT_EQ(42, cantFail(EvalResult->getSignedValue()));// Defined variable with string: getValue, eval, and getStringValue return// value set.StringRef StringValue = "925";FooVar.setValue(ExpressionValue(925u), StringValue);Value = FooVar.getValue();ASSERT_TRUE(Value);EXPECT_EQ(925, cantFail(Value->getSignedValue()));// getStringValue should return the same memory not just the same characters.EXPECT_EQ(StringValue.begin(), FooVar.getStringValue().value().begin());EXPECT_EQ(StringValue.end(), FooVar.getStringValue().value().end());EvalResult = FooVarUse.eval();ASSERT_THAT_EXPECTED(EvalResult, Succeeded());EXPECT_EQ(925, cantFail(EvalResult->getSignedValue()));EXPECT_EQ(925, cantFail(EvalResult->getSignedValue()));// Clearing variable: getValue and eval fail. Error returned by eval holds// the name of the cleared variable.FooVar.clearValue();EXPECT_FALSE(FooVar.getValue());EXPECT_FALSE(FooVar.getStringValue());EvalResult = FooVarUse.eval();expectUndefErrors({"FOO"}, EvalResult.takeError());}TEST_F(FileCheckTest, Binop) {SourceMgr SM;StringRef ExprStr = bufferize(SM, "FOO+BAR");StringRef FooStr = ExprStr.take_front(3);NumericVariable FooVar(FooStr,ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);FooVar.setValue(ExpressionValue(42u));std::unique_ptr<NumericVariableUse> FooVarUse =std::make_unique<NumericVariableUse>(FooStr, &FooVar);StringRef BarStr = ExprStr.take_back(3);NumericVariable BarVar(BarStr,ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);BarVar.setValue(ExpressionValue(18u));std::unique_ptr<NumericVariableUse> BarVarUse =std::make_unique<NumericVariableUse>(BarStr, &BarVar);binop_eval_t doAdd = operator+;BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse),std::move(BarVarUse));// Defined variables: eval returns right value; implicit format is as// expected.Expected<ExpressionValue> Value = Binop.eval();ASSERT_THAT_EXPECTED(Value, Succeeded());EXPECT_EQ(60, cantFail(Value->getSignedValue()));Expected<ExpressionFormat> ImplicitFormat = Binop.getImplicitFormat(SM);ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);// 1 undefined variable: eval fails, error contains name of undefined// variable.FooVar.clearValue();Value = Binop.eval();expectUndefErrors({"FOO"}, Value.takeError());// 2 undefined variables: eval fails, error contains names of all undefined// variables.BarVar.clearValue();Value = Binop.eval();expectUndefErrors({"FOO", "BAR"}, Value.takeError());// Literal + Variable has format of variable.ExprStr = bufferize(SM, "FOO+18");FooStr = ExprStr.take_front(3);StringRef EighteenStr = ExprStr.take_back(2);FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);std::unique_ptr<ExpressionLiteral> Eighteen =std::make_unique<ExpressionLiteral>(EighteenStr, 18u);Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse),std::move(Eighteen));ImplicitFormat = Binop.getImplicitFormat(SM);ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);ExprStr = bufferize(SM, "18+FOO");FooStr = ExprStr.take_back(3);EighteenStr = ExprStr.take_front(2);FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);Eighteen = std::make_unique<ExpressionLiteral>(EighteenStr, 18u);Binop = BinaryOperation(ExprStr, doAdd, std::move(Eighteen),std::move(FooVarUse));ImplicitFormat = Binop.getImplicitFormat(SM);ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);// Variables with different implicit format conflict.ExprStr = bufferize(SM, "FOO+BAZ");FooStr = ExprStr.take_front(3);StringRef BazStr = ExprStr.take_back(3);NumericVariable BazVar(BazStr,ExpressionFormat(ExpressionFormat::Kind::HexLower), 3);FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);std::unique_ptr<NumericVariableUse> BazVarUse =std::make_unique<NumericVariableUse>(BazStr, &BazVar);Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse),std::move(BazVarUse));ImplicitFormat = Binop.getImplicitFormat(SM);expectDiagnosticError("implicit format conflict between 'FOO' (%u) and 'BAZ' (%x), ""need an explicit format specifier",ImplicitFormat.takeError());// All variable conflicts are reported.ExprStr = bufferize(SM, "(FOO+BAZ)+(FOO+QUUX)");StringRef Paren1ExprStr = ExprStr.substr(1, 7);FooStr = Paren1ExprStr.take_front(3);BazStr = Paren1ExprStr.take_back(3);StringRef Paren2ExprStr = ExprStr.substr(ExprStr.rfind('(') + 1, 8);StringRef FooStr2 = Paren2ExprStr.take_front(3);StringRef QuuxStr = Paren2ExprStr.take_back(4);FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);BazVarUse = std::make_unique<NumericVariableUse>(BazStr, &BazVar);std::unique_ptr<NumericVariableUse> FooVarUse2 =std::make_unique<NumericVariableUse>(FooStr2, &FooVar);NumericVariable QuuxVar(QuuxStr, ExpressionFormat(ExpressionFormat::Kind::HexLower), 4);std::unique_ptr<NumericVariableUse> QuuxVarUse =std::make_unique<NumericVariableUse>(QuuxStr, &QuuxVar);std::unique_ptr<BinaryOperation> Binop1 = std::make_unique<BinaryOperation>(ExprStr.take_front(9), doAdd, std::move(FooVarUse), std::move(BazVarUse));std::unique_ptr<BinaryOperation> Binop2 = std::make_unique<BinaryOperation>(ExprStr.take_back(10), doAdd, std::move(FooVarUse2),std::move(QuuxVarUse));std::unique_ptr<BinaryOperation> OuterBinop =std::make_unique<BinaryOperation>(ExprStr, doAdd, std::move(Binop1),std::move(Binop2));ImplicitFormat = OuterBinop->getImplicitFormat(SM);expectSameErrors<ErrorDiagnostic>({("implicit format conflict between 'FOO' (%u) and 'BAZ' (%x), need an ""explicit format specifier"),("implicit format conflict between 'FOO' (%u) and 'QUUX' (%x), need an ""explicit format specifier")},ImplicitFormat.takeError());}TEST_F(FileCheckTest, ValidVarNameStart) {EXPECT_TRUE(Pattern::isValidVarNameStart('a'));EXPECT_TRUE(Pattern::isValidVarNameStart('G'));EXPECT_TRUE(Pattern::isValidVarNameStart('_'));EXPECT_FALSE(Pattern::isValidVarNameStart('2'));EXPECT_FALSE(Pattern::isValidVarNameStart('$'));EXPECT_FALSE(Pattern::isValidVarNameStart('@'));EXPECT_FALSE(Pattern::isValidVarNameStart('+'));EXPECT_FALSE(Pattern::isValidVarNameStart('-'));EXPECT_FALSE(Pattern::isValidVarNameStart(':'));}TEST_F(FileCheckTest, ParseVar) {SourceMgr SM;StringRef OrigVarName = bufferize(SM, "GoodVar42");StringRef VarName = OrigVarName;Expected<Pattern::VariableProperties> ParsedVarResult =Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(ParsedVarResult->Name, OrigVarName);EXPECT_TRUE(VarName.empty());EXPECT_FALSE(ParsedVarResult->IsPseudo);VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");ParsedVarResult = Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(ParsedVarResult->Name, OrigVarName);EXPECT_TRUE(VarName.empty());EXPECT_FALSE(ParsedVarResult->IsPseudo);VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");ParsedVarResult = Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(ParsedVarResult->Name, OrigVarName);EXPECT_TRUE(VarName.empty());EXPECT_TRUE(ParsedVarResult->IsPseudo);VarName = bufferize(SM, "42BadVar");ParsedVarResult = Pattern::parseVariable(VarName, SM);expectDiagnosticError("invalid variable name", ParsedVarResult.takeError());VarName = bufferize(SM, "$@");ParsedVarResult = Pattern::parseVariable(VarName, SM);expectDiagnosticError("invalid variable name", ParsedVarResult.takeError());VarName = OrigVarName = bufferize(SM, "B@dVar");ParsedVarResult = Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(VarName, OrigVarName.substr(1));EXPECT_EQ(ParsedVarResult->Name, "B");EXPECT_FALSE(ParsedVarResult->IsPseudo);VarName = OrigVarName = bufferize(SM, "B$dVar");ParsedVarResult = Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(VarName, OrigVarName.substr(1));EXPECT_EQ(ParsedVarResult->Name, "B");EXPECT_FALSE(ParsedVarResult->IsPseudo);VarName = bufferize(SM, "BadVar+");ParsedVarResult = Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(VarName, "+");EXPECT_EQ(ParsedVarResult->Name, "BadVar");EXPECT_FALSE(ParsedVarResult->IsPseudo);VarName = bufferize(SM, "BadVar-");ParsedVarResult = Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(VarName, "-");EXPECT_EQ(ParsedVarResult->Name, "BadVar");EXPECT_FALSE(ParsedVarResult->IsPseudo);VarName = bufferize(SM, "BadVar:");ParsedVarResult = Pattern::parseVariable(VarName, SM);ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());EXPECT_EQ(VarName, ":");EXPECT_EQ(ParsedVarResult->Name, "BadVar");EXPECT_FALSE(ParsedVarResult->IsPseudo);}static void expectNotFoundError(Error Err) {expectError<NotFoundError>("String not found in input", std::move(Err));}class PatternTester {private:size_t LineNumber = 1;SourceMgr SM;FileCheckRequest Req;FileCheckPatternContext Context;Pattern P{Check::CheckPlain, &Context, LineNumber};public:PatternTester() {std::vector<StringRef> GlobalDefines = {"#FOO=42", "BAR=BAZ", "#add=7"};// An ASSERT_FALSE would make more sense but cannot be used in a// constructor.EXPECT_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, SM),Succeeded());Context.createLineVariable();// Call parsePattern to have @LINE defined.P.parsePattern("N/A", "CHECK", SM, Req);// parsePattern does not expect to be called twice for the same line and// will set FixedStr and RegExStr incorrectly if it is. Therefore prepare// a pattern for a different line.initNextPattern();}void initNextPattern() {P = Pattern(Check::CheckPlain, &Context, ++LineNumber);}size_t getLineNumber() const { return LineNumber; }Expected<std::unique_ptr<Expression>>parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {StringRef ExprBufferRef = bufferize(SM, Expr);Optional<NumericVariable *> DefinedNumericVariable;return P.parseNumericSubstitutionBlock(ExprBufferRef, DefinedNumericVariable, IsLegacyLineExpr, LineNumber,&Context, SM);}bool parsePattern(StringRef Pattern) {StringRef PatBufferRef = bufferize(SM, Pattern);return P.parsePattern(PatBufferRef, "CHECK", SM, Req);}Expected<size_t> match(StringRef Buffer) {StringRef BufferRef = bufferize(SM, Buffer);Pattern::MatchResult Res = P.match(BufferRef, SM);if (Res.TheError)return std::move(Res.TheError);return Res.TheMatch->Pos;}void printVariableDefs(FileCheckDiag::MatchType MatchTy,std::vector<FileCheckDiag> &Diags) {P.printVariableDefs(SM, MatchTy, &Diags);}};TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {PatternTester Tester;// Variable definition.expectDiagnosticError("invalid variable name",Tester.parseSubst("%VAR:").takeError());expectDiagnosticError("definition of pseudo numeric variable unsupported",Tester.parseSubst("@LINE:").takeError());expectDiagnosticError("string variable with name 'BAR' already exists",Tester.parseSubst("BAR:").takeError());expectDiagnosticError("unexpected characters after numeric variable name",Tester.parseSubst("VAR GARBAGE:").takeError());// Change of format.expectDiagnosticError("format different from previous variable definition",Tester.parseSubst("%X,FOO:").takeError());// Invalid format.expectDiagnosticError("invalid matching format specification in expression",Tester.parseSubst("X,VAR1:").takeError());expectDiagnosticError("invalid format specifier in expression",Tester.parseSubst("%F,VAR1:").takeError());expectDiagnosticError("invalid matching format specification in expression",Tester.parseSubst("%X a,VAR1:").takeError());// Acceptable variable definition.EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR1:"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst(" VAR2:"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3 :"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3: "), Succeeded());// Acceptable variable definition with format specifier. Use parsePattern for// variables whose definition needs to be visible for later checks.EXPECT_FALSE(Tester.parsePattern("[[#%u, VAR_UNSIGNED:]]"));EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]"));EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded());// Acceptable variable definition with precision specifier.EXPECT_FALSE(Tester.parsePattern("[[#%.8X, PADDED_ADDR:]]"));EXPECT_FALSE(Tester.parsePattern("[[#%.8, PADDED_NUM:]]"));// Acceptable variable definition in alternate form.EXPECT_THAT_EXPECTED(Tester.parseSubst("%#x, PREFIXED_ADDR:"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("%#X, PREFIXED_ADDR:"), Succeeded());// Acceptable variable definition in alternate form.expectDiagnosticError("alternate form only supported for hex values",Tester.parseSubst("%#u, PREFIXED_UNSI:").takeError());expectDiagnosticError("alternate form only supported for hex values",Tester.parseSubst("%#d, PREFIXED_UNSI:").takeError());// Acceptable variable definition from a numeric expression.EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());// Numeric expression. Switch to next line to make above valid definition// available in expressions.Tester.initNextPattern();// Invalid variable name.expectDiagnosticError("invalid matching constraint or operand format",Tester.parseSubst("%VAR").takeError());expectDiagnosticError("invalid pseudo numeric variable '@FOO'",Tester.parseSubst("@FOO").takeError());// parsePattern() is used here instead of parseSubst() for the variable to be// recorded in GlobalNumericVariableTable and thus appear defined to// parseNumericVariableUse(). Note that the same pattern object is used for// the parsePattern() and parseSubst() since no initNextPattern() is called,// thus appearing as being on the same line from the pattern's point of view.ASSERT_FALSE(Tester.parsePattern("[[#SAME_LINE_VAR:]]"));expectDiagnosticError("numeric variable 'SAME_LINE_VAR' defined earlier in ""the same CHECK directive",Tester.parseSubst("SAME_LINE_VAR").takeError());// Invalid use of variable defined on the same line from an expression not// using any variable defined on the same line.ASSERT_FALSE(Tester.parsePattern("[[#SAME_LINE_EXPR_VAR:@LINE+1]]"));expectDiagnosticError("numeric variable 'SAME_LINE_EXPR_VAR' defined earlier ""in the same CHECK directive",Tester.parseSubst("SAME_LINE_EXPR_VAR").takeError());// Valid use of undefined variable which creates the variable and record it// in GlobalNumericVariableTable.ASSERT_THAT_EXPECTED(Tester.parseSubst("UNDEF"), Succeeded());EXPECT_TRUE(Tester.parsePattern("[[UNDEF:.*]]"));// Invalid literal.expectDiagnosticError("unsupported operation 'U'",Tester.parseSubst("42U").takeError());// Valid empty expression.EXPECT_THAT_EXPECTED(Tester.parseSubst(""), Succeeded());// Invalid equality matching constraint with empty expression.expectDiagnosticError("empty numeric expression should not have a constraint",Tester.parseSubst("==").takeError());// Valid single operand expression.EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MaxUint64)),Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("0x12"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("-30"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)),Succeeded());// Valid optional matching constraint.EXPECT_THAT_EXPECTED(Tester.parseSubst("==FOO"), Succeeded());// Invalid matching constraint.expectDiagnosticError("invalid matching constraint or operand format",Tester.parseSubst("+=FOO").takeError());// Invalid format.expectDiagnosticError("invalid matching format specification in expression",Tester.parseSubst("X,FOO:").takeError());expectDiagnosticError("invalid format specifier in expression",Tester.parseSubst("%F,FOO").takeError());expectDiagnosticError("invalid matching format specification in expression",Tester.parseSubst("%X a,FOO").takeError());// Valid expression with 2 or more operands.EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+0xC"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded());expectDiagnosticError("unsupported operation '/'",Tester.parseSubst("@LINE/2").takeError());expectDiagnosticError("missing operand in expression",Tester.parseSubst("@LINE+").takeError());// Errors in RHS operand are bubbled up by parseBinop() to// parseNumericSubstitutionBlock().expectDiagnosticError("invalid operand format",Tester.parseSubst("@LINE+%VAR").takeError());// Invalid legacy @LINE expression with non literal rhs.expectDiagnosticError("invalid operand format",Tester.parseSubst("@LINE+@LINE", /*IsLegacyNumExpr=*/true).takeError());// Invalid legacy @LINE expression made of a single literal.expectDiagnosticError("invalid variable name",Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError());// Invalid hex literal in legacy @LINE expression.expectDiagnosticError("unexpected characters at end of expression 'xC'",Tester.parseSubst("@LINE+0xC", /*LegacyLineExpr=*/true).takeError());// Valid expression with format specifier.EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("%d, FOO"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());// Valid expression with precision specifier.EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8u, FOO"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8, FOO"), Succeeded());// Valid legacy @LINE expression.EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),Succeeded());// Invalid legacy @LINE expression with more than 2 operands.expectDiagnosticError("unexpected characters at end of expression '+@LINE'",Tester.parseSubst("@LINE+2+@LINE", /*IsLegacyNumExpr=*/true).takeError());expectDiagnosticError("unexpected characters at end of expression '+2'",Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError());// Valid expression with several variables when their implicit formats do not// conflict.EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+VAR_UNSIGNED"), Succeeded());// Valid implicit format conflict in presence of explicit formats.EXPECT_THAT_EXPECTED(Tester.parseSubst("%X,FOO+VAR_LOWER_HEX"), Succeeded());// Implicit format conflict.expectDiagnosticError("implicit format conflict between 'FOO' (%u) and ""'VAR_LOWER_HEX' (%x), need an explicit format specifier",Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());// Simple parenthesized expressions:EXPECT_THAT_EXPECTED(Tester.parseSubst("(1)"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("(1+1)"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("(1)+1"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("((1)+1)"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("((1)+X)"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("((X)+Y)"), Succeeded());expectDiagnosticError("missing operand in expression",Tester.parseSubst("(").takeError());expectDiagnosticError("missing ')' at end of nested expression",Tester.parseSubst("(1").takeError());expectDiagnosticError("missing operand in expression",Tester.parseSubst("(1+").takeError());expectDiagnosticError("missing ')' at end of nested expression",Tester.parseSubst("(1+1").takeError());expectDiagnosticError("missing ')' at end of nested expression",Tester.parseSubst("((1+2+3").takeError());expectDiagnosticError("missing ')' at end of nested expression",Tester.parseSubst("((1+2)+3").takeError());// Test missing operation between operands:expectDiagnosticError("unsupported operation '('",Tester.parseSubst("(1)(2)").takeError());expectDiagnosticError("unsupported operation '('",Tester.parseSubst("2(X)").takeError());// Test more closing than opening parentheses. The diagnostic messages are// not ideal, but for now simply check that we reject invalid input.expectDiagnosticError("invalid matching constraint or operand format",Tester.parseSubst(")").takeError());expectDiagnosticError("unsupported operation ')'",Tester.parseSubst("1)").takeError());expectDiagnosticError("unsupported operation ')'",Tester.parseSubst("(1+2))").takeError());expectDiagnosticError("unsupported operation ')'",Tester.parseSubst("(2))").takeError());expectDiagnosticError("unsupported operation ')'",Tester.parseSubst("(1))(").takeError());// Valid expression with function call.EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO,3)"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("add (FOO,3)"), Succeeded());// Valid expression with nested function call.EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min(BAR,10))"), Succeeded());// Valid expression with function call taking expression as argument.EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, (BAR+10) + 3)"),Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min (BAR,10) + 3)"),Succeeded());// Valid expression with variable named the same as a function.EXPECT_THAT_EXPECTED(Tester.parseSubst("add"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("add+FOO"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+add"), Succeeded());EXPECT_THAT_EXPECTED(Tester.parseSubst("add(add,add)+add"), Succeeded());// Malformed call syntax.expectDiagnosticError("missing ')' at end of call expression",Tester.parseSubst("add(FOO,(BAR+7)").takeError());expectDiagnosticError("missing ')' at end of call expression",Tester.parseSubst("add(FOO,min(BAR,7)").takeError());expectDiagnosticError("missing argument",Tester.parseSubst("add(FOO,)").takeError());expectDiagnosticError("missing argument",Tester.parseSubst("add(,FOO)").takeError());expectDiagnosticError("missing argument",Tester.parseSubst("add(FOO,,3)").takeError());// Valid call, but to an unknown function.expectDiagnosticError("call to undefined function 'bogus_function'",Tester.parseSubst("bogus_function(FOO,3)").takeError());expectDiagnosticError("call to undefined function '@add'",Tester.parseSubst("@add(2,3)").takeError());expectDiagnosticError("call to undefined function '$add'",Tester.parseSubst("$add(2,3)").takeError());expectDiagnosticError("call to undefined function 'FOO'",Tester.parseSubst("FOO(2,3)").takeError());expectDiagnosticError("call to undefined function 'FOO'",Tester.parseSubst("FOO (2,3)").takeError());// Valid call, but with incorrect argument count.expectDiagnosticError("function 'add' takes 2 arguments but 1 given",Tester.parseSubst("add(FOO)").takeError());expectDiagnosticError("function 'add' takes 2 arguments but 3 given",Tester.parseSubst("add(FOO,3,4)").takeError());// Valid call, but not part of a valid expression.expectDiagnosticError("unsupported operation 'a'",Tester.parseSubst("2add(FOO,2)").takeError());expectDiagnosticError("unsupported operation 'a'",Tester.parseSubst("FOO add(FOO,2)").takeError());expectDiagnosticError("unsupported operation 'a'",Tester.parseSubst("add(FOO,2)add(FOO,2)").takeError());}TEST_F(FileCheckTest, ParsePattern) {PatternTester Tester;// Invalid space in string substitution.EXPECT_TRUE(Tester.parsePattern("[[ BAR]]"));// Invalid variable name in string substitution.EXPECT_TRUE(Tester.parsePattern("[[42INVALID]]"));// Invalid string variable definition.EXPECT_TRUE(Tester.parsePattern("[[@PAT:]]"));EXPECT_TRUE(Tester.parsePattern("[[PAT+2:]]"));// Collision with numeric variable.EXPECT_TRUE(Tester.parsePattern("[[FOO:]]"));// Invalid use of string variable.EXPECT_TRUE(Tester.parsePattern("[[FOO-BAR]]"));// Valid use of string variable.EXPECT_FALSE(Tester.parsePattern("[[BAR]]"));// Valid string variable definition.EXPECT_FALSE(Tester.parsePattern("[[PAT:[0-9]+]]"));// Invalid numeric substitution.EXPECT_TRUE(Tester.parsePattern("[[#42INVALID]]"));// Valid numeric substitution.EXPECT_FALSE(Tester.parsePattern("[[#FOO]]"));// Valid legacy @LINE expression.EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]"));// Invalid legacy @LINE expression with non decimal literal.EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]"));}TEST_F(FileCheckTest, Match) {PatternTester Tester;// Check a substitution error is diagnosed.ASSERT_FALSE(Tester.parsePattern("[[#%u, -1]]"));expectDiagnosticError("unable to substitute variable or numeric expression: overflow error",Tester.match("").takeError());// Check matching an empty expression only matches a number.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#]]"));expectNotFoundError(Tester.match("FAIL").takeError());EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());// Check matching a definition only matches a number with the right format.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));expectNotFoundError(Tester.match("FAIL").takeError());expectNotFoundError(Tester.match("").takeError());EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());Tester.initNextPattern();Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]");expectNotFoundError(Tester.match("C").takeError());EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());Tester.initNextPattern();Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]");expectNotFoundError(Tester.match("g").takeError());expectNotFoundError(Tester.match("C").takeError());EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded());Tester.initNextPattern();Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]");expectNotFoundError(Tester.match("H").takeError());expectNotFoundError(Tester.match("b").takeError());EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded());// Check matching expressions with no explicit format matches the values in// the right format.Tester.initNextPattern();Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]");expectNotFoundError(Tester.match("f").takeError());expectNotFoundError(Tester.match("F").takeError());EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded());Tester.initNextPattern();Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]");expectNotFoundError(Tester.match("13").takeError());expectNotFoundError(Tester.match("D").takeError());EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded());Tester.initNextPattern();Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]");expectNotFoundError(Tester.match("12").takeError());expectNotFoundError(Tester.match("c").takeError());EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded());// Check matching an undefined variable returns a NotFound error.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("100"));expectNotFoundError(Tester.match("101").takeError());// Check matching the defined variable matches the correct number only.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]]"));EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());// Check matching several substitutions does not match them independently.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]] [[#NUMVAR+2]]"));expectNotFoundError(Tester.match("19 21").takeError());expectNotFoundError(Tester.match("18 21").takeError());EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded());// Check matching a numeric expression using @LINE after a match failure uses// the correct value for @LINE.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));// Ok, @LINE matches the current line number.EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),Succeeded());Tester.initNextPattern();// Match with substitution failure.ASSERT_FALSE(Tester.parsePattern("[[#UNKNOWN1+UNKNOWN2]]"));expectSameErrors<ErrorDiagnostic>({"undefined variable: UNKNOWN1", "undefined variable: UNKNOWN2"},Tester.match("FOO").takeError());Tester.initNextPattern();// Check that @LINE matches the later (given the calls to initNextPattern())// line number.EXPECT_FALSE(Tester.parsePattern("[[#@LINE]]"));EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),Succeeded());}TEST_F(FileCheckTest, MatchParen) {PatternTester Tester;// Check simple parenthesized expressionsTester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));expectNotFoundError(Tester.match("FAIL").takeError());expectNotFoundError(Tester.match("").takeError());EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2 + 2)]]"));expectNotFoundError(Tester.match("21").takeError());EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2)]]"));EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2)]]"));EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(NUMVAR)]]"));EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());// Check nested parenthesized expressions:Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(2))]]"));EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(NUMVAR))]]"));EXPECT_THAT_EXPECTED(Tester.match("38"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR))))]]"));EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR)))-1)-1]]"));EXPECT_THAT_EXPECTED(Tester.match("34"), Succeeded());// Parentheses can also be the first character after the '#':Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR)]]"));EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR+2)]]"));EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());}TEST_F(FileCheckTest, MatchBuiltinFunctions) {PatternTester Tester;// Esnure #NUMVAR has the expected value.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));expectNotFoundError(Tester.match("FAIL").takeError());expectNotFoundError(Tester.match("").takeError());EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());// Check each builtin function generates the expected result.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#add(NUMVAR,13)]]"));EXPECT_THAT_EXPECTED(Tester.match("31"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#div(NUMVAR,3)]]"));EXPECT_THAT_EXPECTED(Tester.match("6"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,5)]]"));EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,99)]]"));EXPECT_THAT_EXPECTED(Tester.match("99"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,5)]]"));EXPECT_THAT_EXPECTED(Tester.match("5"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,99)]]"));EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#mul(NUMVAR,3)]]"));EXPECT_THAT_EXPECTED(Tester.match("54"), Succeeded());Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#sub(NUMVAR,7)]]"));EXPECT_THAT_EXPECTED(Tester.match("11"), Succeeded());// Check nested function calls.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#add(min(7,2),max(4,10))]]"));EXPECT_THAT_EXPECTED(Tester.match("12"), Succeeded());// Check function call that uses a variable of the same name.Tester.initNextPattern();ASSERT_FALSE(Tester.parsePattern("[[#add(add,add)+min (add,3)+add]]"));EXPECT_THAT_EXPECTED(Tester.match("24"), Succeeded());}TEST_F(FileCheckTest, Substitution) {SourceMgr SM;FileCheckPatternContext Context;EXPECT_THAT_ERROR(Context.defineCmdlineVariables({"FOO=BAR"}, SM),Succeeded());// Substitution of an undefined string variable fails and error holds that// variable's name.StringSubstitution StringSubstitution(&Context, "VAR404", 42);Expected<std::string> SubstValue = StringSubstitution.getResult();expectUndefErrors({"VAR404"}, SubstValue.takeError());// Numeric substitution blocks constituted of defined numeric variables are// substituted for the variable's value.NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),1);NVar.setValue(ExpressionValue(10u));auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);auto ExpressionN = std::make_unique<Expression>(std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN),/*InsertIdx=*/30);SubstValue = SubstitutionN.getResult();ASSERT_THAT_EXPECTED(SubstValue, Succeeded());EXPECT_EQ("A", *SubstValue);// Substitution of an undefined numeric variable fails, error holds name of// undefined variable.NVar.clearValue();SubstValue = SubstitutionN.getResult();expectUndefErrors({"N"}, SubstValue.takeError());// Substitution of a defined string variable returns the right value.Pattern P(Check::CheckPlain, &Context, 1);StringSubstitution = llvm::StringSubstitution(&Context, "FOO", 42);SubstValue = StringSubstitution.getResult();ASSERT_THAT_EXPECTED(SubstValue, Succeeded());EXPECT_EQ("BAR", *SubstValue);}TEST_F(FileCheckTest, FileCheckContext) {FileCheckPatternContext Cxt;SourceMgr SM;// No definition.EXPECT_THAT_ERROR(Cxt.defineCmdlineVariables({}, SM), Succeeded());// Missing equal sign.expectDiagnosticError("missing equal sign in global definition",Cxt.defineCmdlineVariables({"LocalVar"}, SM));expectDiagnosticError("missing equal sign in global definition",Cxt.defineCmdlineVariables({"#LocalNumVar"}, SM));// Empty variable name.expectDiagnosticError("empty variable name",Cxt.defineCmdlineVariables({"=18"}, SM));expectDiagnosticError("empty variable name",Cxt.defineCmdlineVariables({"#=18"}, SM));// Invalid variable name.expectDiagnosticError("invalid variable name",Cxt.defineCmdlineVariables({"18LocalVar=18"}, SM));expectDiagnosticError("invalid variable name",Cxt.defineCmdlineVariables({"#18LocalNumVar=18"}, SM));// Name conflict between pattern and numeric variable.expectDiagnosticError("string variable with name 'LocalVar' already exists",Cxt.defineCmdlineVariables({"LocalVar=18", "#LocalVar=36"}, SM));Cxt = FileCheckPatternContext();expectDiagnosticError("numeric variable with name 'LocalNumVar' already exists",Cxt.defineCmdlineVariables({"#LocalNumVar=18", "LocalNumVar=36"}, SM));Cxt = FileCheckPatternContext();// Invalid numeric value for numeric variable.expectUndefErrors({"x"}, Cxt.defineCmdlineVariables({"#LocalNumVar=x"}, SM));// Define local variables from command-line.std::vector<StringRef> GlobalDefines;// Clear local variables to remove dummy numeric variable x that// parseNumericSubstitutionBlock would have created and stored in// GlobalNumericVariableTable.Cxt.clearLocalVars();GlobalDefines.emplace_back("LocalVar=FOO");GlobalDefines.emplace_back("EmptyVar=");GlobalDefines.emplace_back("#LocalNumVar1=18");GlobalDefines.emplace_back("#%x,LocalNumVar2=LocalNumVar1+2");GlobalDefines.emplace_back("#LocalNumVar3=0xc");ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());// Create @LINE pseudo numeric variable and check it is present by matching// it.size_t LineNumber = 1;Pattern P(Check::CheckPlain, &Cxt, LineNumber);FileCheckRequest Req;Cxt.createLineVariable();ASSERT_FALSE(P.parsePattern("[[@LINE]]", "CHECK", SM, Req));Pattern::MatchResult Res = P.match("1", SM);ASSERT_THAT_ERROR(std::move(Res.TheError), Succeeded());#ifndef NDEBUG// Recreating @LINE pseudo numeric variable fails.EXPECT_DEATH(Cxt.createLineVariable(),"@LINE pseudo numeric variable already created");#endif// Check defined variables are present and undefined ones are absent.StringRef LocalVarStr = "LocalVar";StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");StringRef EmptyVarStr = "EmptyVar";StringRef UnknownVarStr = "UnknownVar";Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);Optional<NumericVariable *> DefinedNumericVariable;Expected<std::unique_ptr<Expression>> ExpressionPointer =P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,/*IsLegacyLineExpr=*/false, LineNumber,&Cxt, SM);ASSERT_THAT_EXPECTED(LocalVar, Succeeded());EXPECT_EQ(*LocalVar, "FOO");Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());Expected<ExpressionValue> ExpressionVal =(*ExpressionPointer)->getAST()->eval();ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 18);ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable,/*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());ExpressionVal = (*ExpressionPointer)->getAST()->eval();ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 20);ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable,/*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());ExpressionVal = (*ExpressionPointer)->getAST()->eval();ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 12);ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());EXPECT_EQ(*EmptyVar, "");expectUndefErrors({std::string(UnknownVarStr)}, UnknownVar.takeError());// Clear local variables and check they become absent.Cxt.clearLocalVars();LocalVar = Cxt.getPatternVarValue(LocalVarStr);expectUndefErrors({std::string(LocalVarStr)}, LocalVar.takeError());// Check a numeric expression's evaluation fails if called after clearing of// local variables, if it was created before. This is important because local// variable clearing due to --enable-var-scope happens after numeric// expressions are linked to the numeric variables they use.expectUndefErrors({"LocalNumVar3"},(*ExpressionPointer)->getAST()->eval().takeError());P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,LineNumber, &Cxt, SM);ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());ExpressionVal = (*ExpressionPointer)->getAST()->eval();expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError());ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,LineNumber, &Cxt, SM);ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());ExpressionVal = (*ExpressionPointer)->getAST()->eval();expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError());EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);expectUndefErrors({"EmptyVar"}, EmptyVar.takeError());// Clear again because parseNumericSubstitutionBlock would have created a// dummy variable and stored it in GlobalNumericVariableTable.Cxt.clearLocalVars();// Redefine global variables and check variables are defined again.GlobalDefines.emplace_back("$GlobalVar=BAR");GlobalDefines.emplace_back("#$GlobalNumVar=36");ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());StringRef GlobalVarStr = "$GlobalVar";StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");Expected<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);ASSERT_THAT_EXPECTED(GlobalVar, Succeeded());EXPECT_EQ(*GlobalVar, "BAR");P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);ExpressionPointer = P.parseNumericSubstitutionBlock(GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,LineNumber, &Cxt, SM);ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());ExpressionVal = (*ExpressionPointer)->getAST()->eval();ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36);// Clear local variables and check global variables remain defined.Cxt.clearLocalVars();EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded());P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);ExpressionPointer = P.parseNumericSubstitutionBlock(GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,LineNumber, &Cxt, SM);ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());ExpressionVal = (*ExpressionPointer)->getAST()->eval();ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36);}TEST_F(FileCheckTest, CapturedVarDiags) {PatternTester Tester;ASSERT_FALSE(Tester.parsePattern("[[STRVAR:[a-z]+]] [[#NUMVAR:@LINE]]"));EXPECT_THAT_EXPECTED(Tester.match("foobar 2"), Succeeded());std::vector<FileCheckDiag> Diags;Tester.printVariableDefs(FileCheckDiag::MatchFoundAndExpected, Diags);EXPECT_EQ(Diags.size(), 2ul);for (FileCheckDiag Diag : Diags) {EXPECT_EQ(Diag.CheckTy, Check::CheckPlain);EXPECT_EQ(Diag.MatchTy, FileCheckDiag::MatchFoundAndExpected);EXPECT_EQ(Diag.InputStartLine, 1u);EXPECT_EQ(Diag.InputEndLine, 1u);}EXPECT_EQ(Diags[0].InputStartCol, 1u);EXPECT_EQ(Diags[0].InputEndCol, 7u);EXPECT_EQ(Diags[1].InputStartCol, 8u);EXPECT_EQ(Diags[1].InputEndCol, 9u);EXPECT_EQ(Diags[0].Note, "captured var \"STRVAR\"");EXPECT_EQ(Diags[1].Note, "captured var \"NUMVAR\"");}} // namespace
set(LLVM_LINK_COMPONENTSFileCheckSupport)add_llvm_unittest(FileCheckTestsFileCheckTest.cpp)target_link_libraries(FileCheckTests PRIVATE LLVMTestingSupport)
//===----- WrapperFunctionUtilsTest.cpp - Test Wrapper-Function utils -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"#include "llvm/ADT/FunctionExtras.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <future>using namespace llvm;using namespace llvm::orc;using namespace llvm::orc::shared;namespace {constexpr const char *TestString = "test string";} // end anonymous namespaceTEST(WrapperFunctionUtilsTest, DefaultWrapperFunctionResult) {WrapperFunctionResult R;EXPECT_TRUE(R.empty());EXPECT_EQ(R.size(), 0U);EXPECT_EQ(R.getOutOfBandError(), nullptr);}TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromRange) {auto R = WrapperFunctionResult::copyFrom(TestString, strlen(TestString) + 1);EXPECT_EQ(R.size(), strlen(TestString) + 1);EXPECT_TRUE(strcmp(R.data(), TestString) == 0);EXPECT_FALSE(R.empty());EXPECT_EQ(R.getOutOfBandError(), nullptr);}TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromCString) {auto R = WrapperFunctionResult::copyFrom(TestString);EXPECT_EQ(R.size(), strlen(TestString) + 1);EXPECT_TRUE(strcmp(R.data(), TestString) == 0);EXPECT_FALSE(R.empty());EXPECT_EQ(R.getOutOfBandError(), nullptr);}TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromStdString) {auto R = WrapperFunctionResult::copyFrom(std::string(TestString));EXPECT_EQ(R.size(), strlen(TestString) + 1);EXPECT_TRUE(strcmp(R.data(), TestString) == 0);EXPECT_FALSE(R.empty());EXPECT_EQ(R.getOutOfBandError(), nullptr);}TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromOutOfBandError) {auto R = WrapperFunctionResult::createOutOfBandError(TestString);EXPECT_FALSE(R.empty());EXPECT_TRUE(strcmp(R.getOutOfBandError(), TestString) == 0);}static void voidNoop() {}class AddClass {public:AddClass(int32_t X) : X(X) {}int32_t addMethod(int32_t Y) { return X + Y; }private:int32_t X;};static WrapperFunctionResult voidNoopWrapper(const char *ArgData,size_t ArgSize) {return WrapperFunction<void()>::handle(ArgData, ArgSize, voidNoop);}static WrapperFunctionResult addWrapper(const char *ArgData, size_t ArgSize) {return WrapperFunction<int32_t(int32_t, int32_t)>::handle(ArgData, ArgSize, [](int32_t X, int32_t Y) -> int32_t { return X + Y; });}static WrapperFunctionResult addMethodWrapper(const char *ArgData,size_t ArgSize) {return WrapperFunction<int32_t(SPSExecutorAddr, int32_t)>::handle(ArgData, ArgSize, makeMethodWrapperHandler(&AddClass::addMethod));}TEST(WrapperFunctionUtilsTest, WrapperFunctionCallAndHandleVoid) {EXPECT_FALSE(!!WrapperFunction<void()>::call(voidNoopWrapper));}TEST(WrapperFunctionUtilsTest, WrapperFunctionCallAndHandleRet) {int32_t Result;EXPECT_FALSE(!!WrapperFunction<int32_t(int32_t, int32_t)>::call(addWrapper, Result, 1, 2));EXPECT_EQ(Result, (int32_t)3);}TEST(WrapperFunctionUtilsTest, WrapperFunctionMethodCallAndHandleRet) {int32_t Result;AddClass AddObj(1);EXPECT_FALSE(!!WrapperFunction<int32_t(SPSExecutorAddr, int32_t)>::call(addMethodWrapper, Result, ExecutorAddr::fromPtr(&AddObj), 2));EXPECT_EQ(Result, (int32_t)3);}static void voidNoopAsync(unique_function<void(SPSEmpty)> SendResult) {SendResult(SPSEmpty());}static WrapperFunctionResult voidNoopAsyncWrapper(const char *ArgData,size_t ArgSize) {std::promise<WrapperFunctionResult> RP;auto RF = RP.get_future();WrapperFunction<void()>::handleAsync(ArgData, ArgSize, voidNoopAsync,[&](WrapperFunctionResult R) { RP.set_value(std::move(R)); });return RF.get();}static WrapperFunctionResult addAsyncWrapper(const char *ArgData,size_t ArgSize) {std::promise<WrapperFunctionResult> RP;auto RF = RP.get_future();WrapperFunction<int32_t(int32_t, int32_t)>::handleAsync(ArgData, ArgSize,[](unique_function<void(int32_t)> SendResult, int32_t X, int32_t Y) {SendResult(X + Y);},[&](WrapperFunctionResult R) { RP.set_value(std::move(R)); });return RF.get();}TEST(WrapperFunctionUtilsTest, WrapperFunctionCallAndHandleAsyncVoid) {EXPECT_FALSE(!!WrapperFunction<void()>::call(voidNoopAsyncWrapper));}TEST(WrapperFunctionUtilsTest, WrapperFunctionCallAndHandleAsyncRet) {int32_t Result;EXPECT_FALSE(!!WrapperFunction<int32_t(int32_t, int32_t)>::call(addAsyncWrapper, Result, 1, 2));EXPECT_EQ(Result, (int32_t)3);}static WrapperFunctionResult failingWrapper(const char *ArgData,size_t ArgSize) {return WrapperFunctionResult::createOutOfBandError("failed");}void asyncFailingWrapperCaller(unique_function<void(WrapperFunctionResult)> F,const char *ArgData, size_t ArgSize) {F(failingWrapper(ArgData, ArgSize));}TEST(WrapperFunctionUtilsTest, WrapperFunctionCallFailingAsync) {WrapperFunction<void()>::callAsync(asyncFailingWrapperCaller, [](Error Err) {EXPECT_THAT_ERROR(std::move(Err), Failed());});}
//===--- ThreadSafeModuleTest.cpp - Test basic use of ThreadSafeModule ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"#include "gtest/gtest.h"#include <atomic>#include <future>#include <thread>using namespace llvm;using namespace llvm::orc;namespace {TEST(ThreadSafeModuleTest, ContextWhollyOwnedByOneModule) {// Test that ownership of a context can be transferred to a single// ThreadSafeModule.ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());auto M = std::make_unique<Module>("M", *TSCtx.getContext());ThreadSafeModule TSM(std::move(M), std::move(TSCtx));}TEST(ThreadSafeModuleTest, ContextOwnershipSharedByTwoModules) {// Test that ownership of a context can be shared between more than one// ThreadSafeModule.ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());auto M1 = std::make_unique<Module>("M1", *TSCtx.getContext());ThreadSafeModule TSM1(std::move(M1), TSCtx);auto M2 = std::make_unique<Module>("M2", *TSCtx.getContext());ThreadSafeModule TSM2(std::move(M2), std::move(TSCtx));}TEST(ThreadSafeModuleTest, ContextOwnershipSharedWithClient) {// Test that ownership of a context can be shared with a client-held// ThreadSafeContext so that it can be re-used for new modules.ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());{// Create and destroy a module.auto M1 = std::make_unique<Module>("M1", *TSCtx.getContext());ThreadSafeModule TSM1(std::move(M1), TSCtx);}// Verify that the context is still available for re-use.auto M2 = std::make_unique<Module>("M2", *TSCtx.getContext());ThreadSafeModule TSM2(std::move(M2), std::move(TSCtx));}TEST(ThreadSafeModuleTest, ThreadSafeModuleMoveAssignment) {// Move assignment needs to move the module before the context (opposite// to the field order) to ensure that overwriting with an empty// ThreadSafeModule does not destroy the context early.ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());auto M = std::make_unique<Module>("M", *TSCtx.getContext());ThreadSafeModule TSM(std::move(M), std::move(TSCtx));TSM = ThreadSafeModule();}TEST(ThreadSafeModuleTest, BasicContextLockAPI) {// Test that basic lock API calls work.ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());auto M = std::make_unique<Module>("M", *TSCtx.getContext());ThreadSafeModule TSM(std::move(M), TSCtx);{ auto L = TSCtx.getLock(); }{ auto L = TSM.getContext().getLock(); }}TEST(ThreadSafeModuleTest, ContextLockPreservesContext) {// Test that the existence of a context lock preserves the attached// context.// The trick to verify this is a bit of a hack: We attach a Module// (without the ThreadSafeModule wrapper) to the context, then verify// that this Module destructs safely (which it will not if its context// has been destroyed) even though all references to the context have// been thrown away (apart from the lock).ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());auto L = TSCtx.getLock();auto &Ctx = *TSCtx.getContext();auto M = std::make_unique<Module>("M", Ctx);TSCtx = ThreadSafeContext();}} // end anonymous namespace
//===----------- TaskDispatchTest.cpp - Test TaskDispatch APIs ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/TaskDispatch.h"#include "gtest/gtest.h"#include <future>using namespace llvm;using namespace llvm::orc;TEST(InPlaceTaskDispatchTest, GenericNamedTask) {auto D = std::make_unique<InPlaceTaskDispatcher>();bool B = false;D->dispatch(makeGenericNamedTask([&]() { B = true; }));EXPECT_TRUE(B);D->shutdown();}#if LLVM_ENABLE_THREADSTEST(DynamicThreadPoolDispatchTest, GenericNamedTask) {auto D = std::make_unique<DynamicThreadPoolTaskDispatcher>();std::promise<bool> P;auto F = P.get_future();D->dispatch(makeGenericNamedTask([P = std::move(P)]() mutable { P.set_value(true); }));EXPECT_TRUE(F.get());D->shutdown();}#endif
//===----- SymbolStringPoolTest.cpp - Unit tests for SymbolStringPool -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"#include "llvm/ExecutionEngine/Orc/DebugUtils.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::orc;namespace {TEST(SymbolStringPool, UniquingAndComparisons) {SymbolStringPool SP;auto P1 = SP.intern("hello");std::string S("hel");S += "lo";auto P2 = SP.intern(S);auto P3 = SP.intern("goodbye");EXPECT_EQ(P1, P2) << "Failed to unique entries";EXPECT_NE(P1, P3) << "Inequal pooled symbol strings comparing equal";// We want to test that less-than comparison of SymbolStringPtrs compiles,// however we can't test the actual result as this is a pointer comparison and// SymbolStringPtr doesn't expose the underlying address of the string.(void)(P1 < P3);}TEST(SymbolStringPool, Dereference) {SymbolStringPool SP;auto Foo = SP.intern("foo");EXPECT_EQ(*Foo, "foo") << "Equality on dereferenced string failed";}TEST(SymbolStringPool, ClearDeadEntries) {SymbolStringPool SP;{auto P1 = SP.intern("s1");SP.clearDeadEntries();EXPECT_FALSE(SP.empty()) << "\"s1\" entry in pool should still be retained";}SP.clearDeadEntries();EXPECT_TRUE(SP.empty()) << "pool should be empty";}TEST(SymbolStringPool, DebugDump) {SymbolStringPool SP;auto A1 = SP.intern("a");auto A2 = A1;auto B = SP.intern("b");std::string DumpString;raw_string_ostream(DumpString) << SP;EXPECT_EQ(DumpString, "a: 2\nb: 1\n");}}
//===-------- SimplePackedSerializationTest.cpp - Test SPS scheme ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::orc::shared;TEST(SimplePackedSerializationTest, SPSOutputBuffer) {constexpr unsigned NumBytes = 8;char Buffer[NumBytes];char Zero = 0;SPSOutputBuffer OB(Buffer, NumBytes);// Expect that we can write NumBytes of content.for (unsigned I = 0; I != NumBytes; ++I) {char C = I;EXPECT_TRUE(OB.write(&C, 1));}// Expect an error when we attempt to write an extra byte.EXPECT_FALSE(OB.write(&Zero, 1));// Check that the buffer contains the expected content.for (unsigned I = 0; I != NumBytes; ++I)EXPECT_EQ(Buffer[I], (char)I);}TEST(SimplePackedSerializationTest, SPSInputBuffer) {char Buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};SPSInputBuffer IB(Buffer, sizeof(Buffer));char C;for (unsigned I = 0; I != sizeof(Buffer); ++I) {EXPECT_TRUE(IB.read(&C, 1));EXPECT_EQ(C, (char)I);}EXPECT_FALSE(IB.read(&C, 1));}template <typename SPSTagT, typename T>static void spsSerializationRoundTrip(const T &Value) {using BST = SPSSerializationTraits<SPSTagT, T>;size_t Size = BST::size(Value);auto Buffer = std::make_unique<char[]>(Size);SPSOutputBuffer OB(Buffer.get(), Size);EXPECT_TRUE(BST::serialize(OB, Value));SPSInputBuffer IB(Buffer.get(), Size);T DSValue;EXPECT_TRUE(BST::deserialize(IB, DSValue));EXPECT_EQ(Value, DSValue)<< "Incorrect value after serialization/deserialization round-trip";}template <typename T> static void testFixedIntegralTypeSerialization() {spsSerializationRoundTrip<T, T>(0);spsSerializationRoundTrip<T, T>(static_cast<T>(1));if (std::is_signed<T>::value) {spsSerializationRoundTrip<T, T>(static_cast<T>(-1));spsSerializationRoundTrip<T, T>(std::numeric_limits<T>::min());}spsSerializationRoundTrip<T, T>(std::numeric_limits<T>::max());}TEST(SimplePackedSerializationTest, BoolSerialization) {spsSerializationRoundTrip<bool, bool>(true);spsSerializationRoundTrip<bool, bool>(false);}TEST(SimplePackedSerializationTest, CharSerialization) {spsSerializationRoundTrip<char, char>((char)0x00);spsSerializationRoundTrip<char, char>((char)0xAA);spsSerializationRoundTrip<char, char>((char)0xFF);}TEST(SimplePackedSerializationTest, Int8Serialization) {testFixedIntegralTypeSerialization<int8_t>();}TEST(SimplePackedSerializationTest, UInt8Serialization) {testFixedIntegralTypeSerialization<uint8_t>();}TEST(SimplePackedSerializationTest, Int16Serialization) {testFixedIntegralTypeSerialization<int16_t>();}TEST(SimplePackedSerializationTest, UInt16Serialization) {testFixedIntegralTypeSerialization<uint16_t>();}TEST(SimplePackedSerializationTest, Int32Serialization) {testFixedIntegralTypeSerialization<int32_t>();}TEST(SimplePackedSerializationTest, UInt32Serialization) {testFixedIntegralTypeSerialization<uint32_t>();}TEST(SimplePackedSerializationTest, Int64Serialization) {testFixedIntegralTypeSerialization<int64_t>();}TEST(SimplePackedSerializationTest, UInt64Serialization) {testFixedIntegralTypeSerialization<uint64_t>();}TEST(SimplePackedSerializationTest, SequenceSerialization) {std::vector<int32_t> V({1, 2, -47, 139});spsSerializationRoundTrip<SPSSequence<int32_t>>(V);}TEST(SimplePackedSerializationTest, StringViewCharSequenceSerialization) {const char *HW = "Hello, world!";spsSerializationRoundTrip<SPSString>(StringRef(HW));}TEST(SimplePackedSerializationTest, StdTupleSerialization) {std::tuple<int32_t, std::string, bool> P(42, "foo", true);spsSerializationRoundTrip<SPSTuple<int32_t, SPSString, bool>>(P);}TEST(SimplePackedSerializationTest, StdPairSerialization) {std::pair<int32_t, std::string> P(42, "foo");spsSerializationRoundTrip<SPSTuple<int32_t, SPSString>>(P);}TEST(SimplePackedSerializationTest, ArgListSerialization) {using BAL = SPSArgList<bool, int32_t, SPSString>;bool Arg1 = true;int32_t Arg2 = 42;std::string Arg3 = "foo";size_t Size = BAL::size(Arg1, Arg2, Arg3);auto Buffer = std::make_unique<char[]>(Size);SPSOutputBuffer OB(Buffer.get(), Size);EXPECT_TRUE(BAL::serialize(OB, Arg1, Arg2, Arg3));SPSInputBuffer IB(Buffer.get(), Size);bool ArgOut1;int32_t ArgOut2;std::string ArgOut3;EXPECT_TRUE(BAL::deserialize(IB, ArgOut1, ArgOut2, ArgOut3));EXPECT_EQ(Arg1, ArgOut1);EXPECT_EQ(Arg2, ArgOut2);EXPECT_EQ(Arg3, ArgOut3);}TEST(SimplePackedSerialization, StringMap) {StringMap<int32_t> M({{"A", 1}, {"B", 2}});spsSerializationRoundTrip<SPSSequence<SPSTuple<SPSString, int32_t>>>(M);}TEST(SimplePackedSerializationTest, ArrayRef) {constexpr unsigned BufferSize = 6 + 8; // "hello\0" + sizeof(uint64_t)ArrayRef<char> HelloOut = "hello";char Buffer[BufferSize];memset(Buffer, 0, BufferSize);SPSOutputBuffer OB(Buffer, BufferSize);EXPECT_TRUE(SPSArgList<SPSSequence<char>>::serialize(OB, HelloOut));ArrayRef<char> HelloIn;SPSInputBuffer IB(Buffer, BufferSize);EXPECT_TRUE(SPSArgList<SPSSequence<char>>::deserialize(IB, HelloIn));// Output should be copied to buffer.EXPECT_NE(HelloOut.data(), Buffer);// Input should reference buffer.EXPECT_LT(HelloIn.data() - Buffer, BufferSize);}TEST(SimplePackedSerializationTest, ArrayRefEmpty) {// Make sure that empty ArrayRefs serialize and deserialize as expected.// Empty ArrayRefs should not succeed even when the data field is null, and// should deserialize to a default-constructed ArrayRef, not a pointer into// the stream.constexpr unsigned BufferSize = sizeof(uint64_t);char Buffer[BufferSize];memset(Buffer, 0, BufferSize);ArrayRef<char> AOut;SPSOutputBuffer OB(Buffer, BufferSize);EXPECT_TRUE(SPSArgList<SPSSequence<char>>::serialize(OB, AOut));ArrayRef<char> AIn;SPSInputBuffer IB(Buffer, BufferSize);EXPECT_TRUE(SPSArgList<SPSSequence<char>>::deserialize(IB, AIn));EXPECT_EQ(AIn.data(), nullptr);EXPECT_EQ(AIn.size(), 0U);}TEST(SimplePackedSerializationTest, StringRefEmpty) {// Make sure that empty StringRefs serialize and deserialize as expected.// Empty StringRefs should not succeed even when the data field is null, and// should deserialize to a default-constructed StringRef, not a pointer into// the stream.constexpr unsigned BufferSize = sizeof(uint64_t);char Buffer[BufferSize];memset(Buffer, 0, BufferSize);StringRef SROut;SPSOutputBuffer OB(Buffer, BufferSize);EXPECT_TRUE(SPSArgList<SPSSequence<char>>::serialize(OB, SROut));StringRef SRIn;SPSInputBuffer IB(Buffer, BufferSize);EXPECT_TRUE(SPSArgList<SPSSequence<char>>::deserialize(IB, SRIn));EXPECT_EQ(SRIn.data(), nullptr);EXPECT_EQ(SRIn.size(), 0U);}
//===---------------- SimpleExecutorMemoryManagerTest.cpp -----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <limits>#include <vector>using namespace llvm;using namespace llvm::orc;using namespace llvm::orc::shared;using namespace llvm::orc::rt_bootstrap;namespace {orc::shared::CWrapperFunctionResult incrementWrapper(const char *ArgData,size_t ArgSize) {return WrapperFunction<SPSError(SPSExecutorAddr)>::handle(ArgData, ArgSize,[](ExecutorAddr A) -> Error {*A.toPtr<int *>() += 1;return Error::success();}).release();}TEST(SimpleExecutorMemoryManagerTest, AllocFinalizeFree) {SimpleExecutorMemoryManager MemMgr;constexpr unsigned AllocSize = 16384;auto Mem = MemMgr.allocate(AllocSize);EXPECT_THAT_ERROR(Mem.takeError(), Succeeded());std::string HW = "Hello, world!";int FinalizeCounter = 0;int DeallocateCounter = 0;tpctypes::FinalizeRequest FR;FR.Segments.push_back(tpctypes::SegFinalizeRequest{tpctypes::WPF_Read | tpctypes::WPF_Write,*Mem,AllocSize,{HW.data(), HW.size() + 1}});FR.Actions.push_back({/* Finalize: */cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&FinalizeCounter))),/* Deallocate: */cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&DeallocateCounter)))});EXPECT_EQ(FinalizeCounter, 0);EXPECT_EQ(DeallocateCounter, 0);auto FinalizeErr = MemMgr.finalize(FR);EXPECT_THAT_ERROR(std::move(FinalizeErr), Succeeded());EXPECT_EQ(FinalizeCounter, 1);EXPECT_EQ(DeallocateCounter, 0);EXPECT_EQ(HW, std::string(Mem->toPtr<const char *>()));auto DeallocateErr = MemMgr.deallocate({*Mem});EXPECT_THAT_ERROR(std::move(DeallocateErr), Succeeded());EXPECT_EQ(FinalizeCounter, 1);EXPECT_EQ(DeallocateCounter, 1);}} // namespace
//===- SharedMemoryMapperTest.cpp -- Tests for SharedMemoryMapper ---------===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ExecutionEngine/Orc/MemoryMapper.h"#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h"#include "llvm/Testing/Support/Error.h"using namespace llvm;using namespace llvm::orc;using namespace llvm::orc::shared;using namespace llvm::orc::rt_bootstrap;#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)// A basic function to be used as both initializer/deinitializerorc::shared::CWrapperFunctionResult incrementWrapper(const char *ArgData,size_t ArgSize) {return WrapperFunction<SPSError(SPSExecutorAddr)>::handle(ArgData, ArgSize,[](ExecutorAddr A) -> Error {*A.toPtr<int *>() += 1;return Error::success();}).release();}TEST(SharedMemoryMapperTest, MemReserveInitializeDeinitializeRelease) {// These counters are used to track how many times the initializer and// deinitializer functions are calledint InitializeCounter = 0;int DeinitializeCounter = 0;auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());ExecutorSharedMemoryMapperService MapperService;SharedMemoryMapper::SymbolAddrs SAs;{StringMap<ExecutorAddr> Map;MapperService.addBootstrapSymbols(Map);SAs.Instance = Map[rt::ExecutorSharedMemoryMapperServiceInstanceName];SAs.Reserve = Map[rt::ExecutorSharedMemoryMapperServiceReserveWrapperName];SAs.Initialize =Map[rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName];SAs.Deinitialize =Map[rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName];SAs.Release = Map[rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName];}std::string TestString = "Hello, World!";// barrierstd::promise<void> P;auto F = P.get_future();{std::unique_ptr<MemoryMapper> Mapper =cantFail(SharedMemoryMapper::Create(*SelfEPC, SAs));auto PageSize = Mapper->getPageSize();size_t ReqSize = PageSize;Mapper->reserve(ReqSize, [&](Expected<ExecutorAddrRange> Result) {EXPECT_THAT_ERROR(Result.takeError(), Succeeded());auto Reservation = std::move(*Result);{char *Addr = Mapper->prepare(Reservation.Start, TestString.size() + 1);std::strcpy(Addr, TestString.c_str());}MemoryMapper::AllocInfo AI;{MemoryMapper::AllocInfo::SegInfo SI;SI.Offset = 0;SI.ContentSize = TestString.size() + 1;SI.ZeroFillSize = PageSize - SI.ContentSize;SI.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;AI.MappingBase = Reservation.Start;AI.Segments.push_back(SI);AI.Actions.push_back({cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&InitializeCounter))),cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&DeinitializeCounter)))});}EXPECT_EQ(InitializeCounter, 0);EXPECT_EQ(DeinitializeCounter, 0);Mapper->initialize(AI, [&, Reservation](Expected<ExecutorAddr> Result) {EXPECT_THAT_ERROR(Result.takeError(), Succeeded());EXPECT_EQ(TestString, std::string(static_cast<char *>(Reservation.Start.toPtr<char *>())));EXPECT_EQ(InitializeCounter, 1);EXPECT_EQ(DeinitializeCounter, 0);Mapper->deinitialize({*Result}, [&, Reservation](Error Err) {EXPECT_THAT_ERROR(std::move(Err), Succeeded());EXPECT_EQ(InitializeCounter, 1);EXPECT_EQ(DeinitializeCounter, 1);Mapper->release({Reservation.Start}, [&](Error Err) {EXPECT_THAT_ERROR(std::move(Err), Succeeded());P.set_value();});});});});// This will block the test if any of the above callbacks are not executedF.wait();// Mapper must be destructed before calling shutdown to avoid double free}EXPECT_THAT_ERROR(MapperService.shutdown(), Succeeded());cantFail(SelfEPC->disconnect());}#endif
//===------ ResourceTrackerTest.cpp - Unit tests ResourceTracker API ------===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ADT/FunctionExtras.h"#include "llvm/Config/llvm-config.h"#include "llvm/ExecutionEngine/Orc/Core.h"#include "llvm/ExecutionEngine/Orc/Shared/OrcError.h"#include "llvm/Testing/Support/Error.h"using namespace llvm;using namespace llvm::orc;class ResourceTrackerStandardTest : public CoreAPIsBasedStandardTest {};namespace {template <typename ResourceT = unsigned>class SimpleResourceManager : public ResourceManager {public:using HandleRemoveFunction = unique_function<Error(ResourceKey)>;using HandleTransferFunction =unique_function<void(ResourceKey, ResourceKey)>;using RecordedResourcesMap = DenseMap<ResourceKey, ResourceT>;SimpleResourceManager(ExecutionSession &ES) : ES(ES) {HandleRemove = [&](ResourceKey K) -> Error {ES.runSessionLocked([&] { removeResource(K); });return Error::success();};HandleTransfer = [this](ResourceKey DstKey, ResourceKey SrcKey) {transferResources(DstKey, SrcKey);};ES.registerResourceManager(*this);}SimpleResourceManager(const SimpleResourceManager &) = delete;SimpleResourceManager &operator=(const SimpleResourceManager &) = delete;SimpleResourceManager(SimpleResourceManager &&) = delete;SimpleResourceManager &operator=(SimpleResourceManager &&) = delete;~SimpleResourceManager() { ES.deregisterResourceManager(*this); }/// Set the HandleRemove function object.void setHandleRemove(HandleRemoveFunction HandleRemove) {this->HandleRemove = std::move(HandleRemove);}/// Set the HandleTransfer function object.void setHandleTransfer(HandleTransferFunction HandleTransfer) {this->HandleTransfer = std::move(HandleTransfer);}/// Create an association between the given key and resource.template <typename MergeOp = std::plus<ResourceT>>void recordResource(ResourceKey K, ResourceT Val = ResourceT(),MergeOp Merge = MergeOp()) {auto Tmp = std::move(Resources[K]);Resources[K] = Merge(std::move(Tmp), std::move(Val));}/// Remove the resource associated with K from the map if present.void removeResource(ResourceKey K) { Resources.erase(K); }/// Transfer resources from DstKey to SrcKey.template <typename MergeOp = std::plus<ResourceT>>void transferResources(ResourceKey DstKey, ResourceKey SrcKey,MergeOp Merge = MergeOp()) {auto &DstResourceRef = Resources[DstKey];ResourceT DstResources;std::swap(DstResourceRef, DstResources);auto SI = Resources.find(SrcKey);assert(SI != Resources.end() && "No resource associated with SrcKey");DstResourceRef = Merge(std::move(DstResources), std::move(SI->second));Resources.erase(SI);}/// Return a reference to the Resources map.RecordedResourcesMap &getRecordedResources() { return Resources; }const RecordedResourcesMap &getRecordedResources() const { return Resources; }Error handleRemoveResources(ResourceKey K) override {return HandleRemove(K);}void handleTransferResources(ResourceKey DstKey,ResourceKey SrcKey) override {HandleTransfer(DstKey, SrcKey);}static void transferNotAllowed(ResourceKey DstKey, ResourceKey SrcKey) {llvm_unreachable("Resource transfer not allowed");}private:ExecutionSession &ES;HandleRemoveFunction HandleRemove;HandleTransferFunction HandleTransfer;RecordedResourcesMap Resources;};TEST_F(ResourceTrackerStandardTest,BasicDefineAndRemoveAllBeforeMaterializing) {bool ResourceManagerGotRemove = false;SimpleResourceManager<> SRM(ES);SRM.setHandleRemove([&](ResourceKey K) -> Error {ResourceManagerGotRemove = true;EXPECT_EQ(SRM.getRecordedResources().size(), 0U)<< "Unexpected resources recorded";SRM.removeResource(K);return Error::success();});bool MaterializationUnitDestroyed = false;auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {llvm_unreachable("Never called");},nullptr, SimpleMaterializationUnit::DiscardFunction(),[&]() { MaterializationUnitDestroyed = true; });auto RT = JD.createResourceTracker();cantFail(JD.define(std::move(MU), RT));cantFail(RT->remove());auto SymFlags = cantFail(ES.lookupFlags(LookupKind::Static,{{&JD, JITDylibLookupFlags::MatchExportedSymbolsOnly}},SymbolLookupSet(Foo, SymbolLookupFlags::WeaklyReferencedSymbol)));EXPECT_EQ(SymFlags.size(), 0U)<< "Symbols should have been removed from the symbol table";EXPECT_TRUE(ResourceManagerGotRemove)<< "ResourceManager did not receive handleRemoveResources";EXPECT_TRUE(MaterializationUnitDestroyed)<< "MaterializationUnit not destroyed in response to removal";}TEST_F(ResourceTrackerStandardTest, BasicDefineAndRemoveAllAfterMaterializing) {bool ResourceManagerGotRemove = false;SimpleResourceManager<> SRM(ES);SRM.setHandleRemove([&](ResourceKey K) -> Error {ResourceManagerGotRemove = true;EXPECT_EQ(SRM.getRecordedResources().size(), 1U)<< "Unexpected number of resources recorded";EXPECT_EQ(SRM.getRecordedResources().count(K), 1U)<< "Unexpected recorded resource";SRM.removeResource(K);return Error::success();});auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->withResourceKeyDo([&](ResourceKey K) { SRM.recordResource(K); }));cantFail(R->notifyResolved({{Foo, FooSym}}));cantFail(R->notifyEmitted());});auto RT = JD.createResourceTracker();cantFail(JD.define(std::move(MU), RT));cantFail(ES.lookup({&JD}, Foo));cantFail(RT->remove());auto SymFlags = cantFail(ES.lookupFlags(LookupKind::Static,{{&JD, JITDylibLookupFlags::MatchExportedSymbolsOnly}},SymbolLookupSet(Foo, SymbolLookupFlags::WeaklyReferencedSymbol)));EXPECT_EQ(SymFlags.size(), 0U)<< "Symbols should have been removed from the symbol table";EXPECT_TRUE(ResourceManagerGotRemove)<< "ResourceManager did not receive handleRemoveResources";}TEST_F(ResourceTrackerStandardTest, BasicDefineAndRemoveAllWhileMaterializing) {bool ResourceManagerGotRemove = false;SimpleResourceManager<> SRM(ES);SRM.setHandleRemove([&](ResourceKey K) -> Error {ResourceManagerGotRemove = true;EXPECT_EQ(SRM.getRecordedResources().size(), 0U)<< "Unexpected resources recorded";SRM.removeResource(K);return Error::success();});std::unique_ptr<MaterializationResponsibility> MR;auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {MR = std::move(R);});auto RT = JD.createResourceTracker();cantFail(JD.define(std::move(MU), RT));ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo),SymbolState::Ready,[](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(Result, Failed<FailedToMaterialize>())<< "Lookup failed unexpectedly";},NoDependenciesToRegister);cantFail(RT->remove());auto SymFlags = cantFail(ES.lookupFlags(LookupKind::Static,{{&JD, JITDylibLookupFlags::MatchExportedSymbolsOnly}},SymbolLookupSet(Foo, SymbolLookupFlags::WeaklyReferencedSymbol)));EXPECT_EQ(SymFlags.size(), 0U)<< "Symbols should have been removed from the symbol table";EXPECT_TRUE(ResourceManagerGotRemove)<< "ResourceManager did not receive handleRemoveResources";EXPECT_THAT_ERROR(MR->withResourceKeyDo([](ResourceKey K) {ADD_FAILURE() << "Should not reach withResourceKeyDo body for removed key";}),Failed<ResourceTrackerDefunct>())<< "withResourceKeyDo on MR with removed tracker should have failed";EXPECT_THAT_ERROR(MR->notifyResolved({{Foo, FooSym}}),Failed<ResourceTrackerDefunct>())<< "notifyResolved on MR with removed tracker should have failed";MR->failMaterialization();}TEST_F(ResourceTrackerStandardTest, JITDylibClear) {SimpleResourceManager<> SRM(ES);// Add materializer for Foo.cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->withResourceKeyDo([&](ResourceKey K) { ++SRM.getRecordedResources()[K]; }));cantFail(R->notifyResolved({{Foo, FooSym}}));cantFail(R->notifyEmitted());})));// Add materializer for Bar.cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->withResourceKeyDo([&](ResourceKey K) { ++SRM.getRecordedResources()[K]; }));cantFail(R->notifyResolved({{Bar, BarSym}}));cantFail(R->notifyEmitted());})));EXPECT_TRUE(SRM.getRecordedResources().empty())<< "Expected no resources recorded yet.";cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo, Bar})));auto JDResourceKey = JD.getDefaultResourceTracker()->getKeyUnsafe();EXPECT_EQ(SRM.getRecordedResources().size(), 1U)<< "Expected exactly one entry (for JD's ResourceKey)";EXPECT_EQ(SRM.getRecordedResources().count(JDResourceKey), 1U)<< "Expected an entry for JD's ResourceKey";EXPECT_EQ(SRM.getRecordedResources()[JDResourceKey], 2U)<< "Expected value of 2 for JD's ResourceKey ""(+1 for each of Foo and Bar)";cantFail(JD.clear());EXPECT_TRUE(SRM.getRecordedResources().empty())<< "Expected no resources recorded after clear";}TEST_F(ResourceTrackerStandardTest,BasicDefineAndExplicitTransferBeforeMaterializing) {bool ResourceManagerGotTransfer = false;SimpleResourceManager<> SRM(ES);SRM.setHandleTransfer([&](ResourceKey DstKey, ResourceKey SrcKey) {ResourceManagerGotTransfer = true;auto &RR = SRM.getRecordedResources();EXPECT_EQ(RR.size(), 0U) << "Expected no resources recorded yet";});auto MakeMU = [&](SymbolStringPtr Name, JITEvaluatedSymbol Sym) {return std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Name, Sym.getFlags()}}),[=, &SRM](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->withResourceKeyDo([&](ResourceKey K) { SRM.recordResource(K); }));cantFail(R->notifyResolved({{Name, Sym}}));cantFail(R->notifyEmitted());});};auto FooRT = JD.createResourceTracker();cantFail(JD.define(MakeMU(Foo, FooSym), FooRT));auto BarRT = JD.createResourceTracker();cantFail(JD.define(MakeMU(Bar, BarSym), BarRT));BarRT->transferTo(*FooRT);EXPECT_TRUE(ResourceManagerGotTransfer)<< "ResourceManager did not receive transfer";EXPECT_TRUE(BarRT->isDefunct()) << "BarRT should now be defunct";cantFail(ES.lookup(makeJITDylibSearchOrder({&JD}), SymbolLookupSet({Foo, Bar})));EXPECT_EQ(SRM.getRecordedResources().size(), 1U)<< "Expected exactly one entry (for FooRT's Key)";EXPECT_EQ(SRM.getRecordedResources().count(FooRT->getKeyUnsafe()), 1U)<< "Expected an entry for FooRT's ResourceKey";EXPECT_EQ(SRM.getRecordedResources().count(BarRT->getKeyUnsafe()), 0U)<< "Expected no entry for BarRT's ResourceKey";// We need to explicitly destroy FooRT or its resources will be implicitly// transferred to the default tracker triggering a second call to our// transfer function above (which expects only one call).cantFail(FooRT->remove());}TEST_F(ResourceTrackerStandardTest,BasicDefineAndExplicitTransferAfterMaterializing) {bool ResourceManagerGotTransfer = false;SimpleResourceManager<> SRM(ES);SRM.setHandleTransfer([&](ResourceKey DstKey, ResourceKey SrcKey) {ResourceManagerGotTransfer = true;SRM.transferResources(DstKey, SrcKey);});auto MakeMU = [&](SymbolStringPtr Name, JITEvaluatedSymbol Sym) {return std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Name, Sym.getFlags()}}),[=, &SRM](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->withResourceKeyDo([&](ResourceKey K) { SRM.recordResource(K, 1); }));cantFail(R->notifyResolved({{Name, Sym}}));cantFail(R->notifyEmitted());});};auto FooRT = JD.createResourceTracker();cantFail(JD.define(MakeMU(Foo, FooSym), FooRT));auto BarRT = JD.createResourceTracker();cantFail(JD.define(MakeMU(Bar, BarSym), BarRT));EXPECT_EQ(SRM.getRecordedResources().size(), 0U)<< "Expected no recorded resources yet";cantFail(ES.lookup(makeJITDylibSearchOrder({&JD}), SymbolLookupSet({Foo, Bar})));EXPECT_EQ(SRM.getRecordedResources().size(), 2U)<< "Expected recorded resources for both Foo and Bar";BarRT->transferTo(*FooRT);EXPECT_TRUE(ResourceManagerGotTransfer)<< "ResourceManager did not receive transfer";EXPECT_TRUE(BarRT->isDefunct()) << "BarRT should now be defunct";EXPECT_EQ(SRM.getRecordedResources().size(), 1U)<< "Expected recorded resources for Foo only";EXPECT_EQ(SRM.getRecordedResources().count(FooRT->getKeyUnsafe()), 1U)<< "Expected recorded resources for Foo";EXPECT_EQ(SRM.getRecordedResources()[FooRT->getKeyUnsafe()], 2U)<< "Expected resources value for for Foo to be '2'";}TEST_F(ResourceTrackerStandardTest,BasicDefineAndExplicitTransferWhileMaterializing) {bool ResourceManagerGotTransfer = false;SimpleResourceManager<> SRM(ES);SRM.setHandleTransfer([&](ResourceKey DstKey, ResourceKey SrcKey) {ResourceManagerGotTransfer = true;SRM.transferResources(DstKey, SrcKey);});auto FooRT = JD.createResourceTracker();std::unique_ptr<MaterializationResponsibility> FooMR;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooMR = std::move(R);}),FooRT));auto BarRT = JD.createResourceTracker();ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo),SymbolState::Ready,[](Expected<SymbolMap> Result) { cantFail(Result.takeError()); },NoDependenciesToRegister);cantFail(FooMR->withResourceKeyDo([&](ResourceKey K) {EXPECT_EQ(FooRT->getKeyUnsafe(), K)<< "Expected FooRT's ResourceKey for Foo here";SRM.recordResource(K, 1);}));EXPECT_EQ(SRM.getRecordedResources().size(), 1U)<< "Expected one recorded resource here";EXPECT_EQ(SRM.getRecordedResources()[FooRT->getKeyUnsafe()], 1U)<< "Expected Resource value for FooRT to be '1' here";FooRT->transferTo(*BarRT);EXPECT_TRUE(ResourceManagerGotTransfer)<< "Expected resource manager to receive handleTransferResources call";cantFail(FooMR->withResourceKeyDo([&](ResourceKey K) {EXPECT_EQ(BarRT->getKeyUnsafe(), K)<< "Expected BarRT's ResourceKey for Foo here";SRM.recordResource(K, 1);}));EXPECT_EQ(SRM.getRecordedResources().size(), 1U)<< "Expected one recorded resource here";EXPECT_EQ(SRM.getRecordedResources().count(BarRT->getKeyUnsafe()), 1U)<< "Expected RecordedResources to contain an entry for BarRT";EXPECT_EQ(SRM.getRecordedResources()[BarRT->getKeyUnsafe()], 2U)<< "Expected Resource value for BarRT to be '2' here";cantFail(FooMR->notifyResolved({{Foo, FooSym}}));cantFail(FooMR->notifyEmitted());}} // namespace
//===--- RTDyldObjectLinkingLayerTest.cpp - RTDyld linking layer tests ---===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ExecutionEngine/ExecutionEngine.h"#include "llvm/ExecutionEngine/Orc/CompileUtils.h"#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"#include "llvm/ExecutionEngine/SectionMemoryManager.h"#include "llvm/IR/Constants.h"#include "llvm/IR/LLVMContext.h"#include "gtest/gtest.h"#include <string>using namespace llvm;using namespace llvm::orc;namespace {// Returns whether a non-alloc section was passed to the memory manager.static bool testSetProcessAllSections(std::unique_ptr<MemoryBuffer> Obj,bool ProcessAllSections) {class MemoryManagerWrapper : public SectionMemoryManager {public:MemoryManagerWrapper(bool &NonAllocSeen) : NonAllocSeen(NonAllocSeen) {}uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,unsigned SectionID, StringRef SectionName,bool IsReadOnly) override {// We check for ".note.GNU-stack" here because it is currently the only// non-alloc section seen in the module. If this changes in future any// other non-alloc section would do here.if (SectionName == ".note.GNU-stack")NonAllocSeen = true;return SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, IsReadOnly);}private:bool &NonAllocSeen;};bool NonAllocSectionSeen = false;ExecutionSession ES(std::make_unique<UnsupportedExecutorProcessControl>());auto &JD = ES.createBareJITDylib("main");auto Foo = ES.intern("foo");RTDyldObjectLinkingLayer ObjLayer(ES, [&NonAllocSectionSeen]() {return std::make_unique<MemoryManagerWrapper>(NonAllocSectionSeen);});auto OnResolveDoNothing = [](Expected<SymbolMap> R) {cantFail(std::move(R));};ObjLayer.setProcessAllSections(ProcessAllSections);cantFail(ObjLayer.add(JD, std::move(Obj)));ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Resolved, OnResolveDoNothing,NoDependenciesToRegister);if (auto Err = ES.endSession())ES.reportError(std::move(Err));return NonAllocSectionSeen;}TEST(RTDyldObjectLinkingLayerTest, TestSetProcessAllSections) {LLVMContext Context;auto M = std::make_unique<Module>("", Context);M->setTargetTriple("x86_64-unknown-linux-gnu");// These values are only here to ensure that the module is non-empty.// They are no longer relevant to the test.Constant *StrConstant = ConstantDataArray::getString(Context, "forty-two");auto *GV =new GlobalVariable(*M, StrConstant->getType(), true,GlobalValue::ExternalLinkage, StrConstant, "foo");GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);GV->setAlignment(Align(1));// Initialize the native target in case this is the first unit test// to try to build a TM.OrcNativeTarget::initialize();std::unique_ptr<TargetMachine> TM(EngineBuilder().selectTarget(Triple(M->getTargetTriple()), "", "", SmallVector<std::string, 1>()));if (!TM)return;auto Obj = cantFail(SimpleCompiler(*TM)(*M));EXPECT_FALSE(testSetProcessAllSections(MemoryBuffer::getMemBufferCopy(Obj->getBuffer()), false))<< "Non-alloc section seen despite ProcessAllSections being false";EXPECT_TRUE(testSetProcessAllSections(std::move(Obj), true))<< "Expected to see non-alloc section when ProcessAllSections is true";}TEST(RTDyldObjectLinkingLayerTest, TestOverrideObjectFlags) {OrcNativeTarget::initialize();std::unique_ptr<TargetMachine> TM(EngineBuilder().selectTarget(Triple("x86_64-unknown-linux-gnu"), "", "",SmallVector<std::string, 1>()));if (!TM)return;// Our compiler is going to modify symbol visibility settings without telling// ORC. This will test our ability to override the flags later.class FunkySimpleCompiler : public SimpleCompiler {public:FunkySimpleCompiler(TargetMachine &TM) : SimpleCompiler(TM) {}Expected<CompileResult> operator()(Module &M) override {auto *Foo = M.getFunction("foo");assert(Foo && "Expected function Foo not found");Foo->setVisibility(GlobalValue::HiddenVisibility);return SimpleCompiler::operator()(M);}};// Create a module with two void() functions: foo and bar.ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());ThreadSafeModule M;{ModuleBuilder MB(*TSCtx.getContext(), TM->getTargetTriple().str(), "dummy");MB.getModule()->setDataLayout(TM->createDataLayout());Function *FooImpl = MB.createFunctionDecl(FunctionType::get(Type::getVoidTy(*TSCtx.getContext()), {}, false),"foo");BasicBlock *FooEntry =BasicBlock::Create(*TSCtx.getContext(), "entry", FooImpl);IRBuilder<> B1(FooEntry);B1.CreateRetVoid();Function *BarImpl = MB.createFunctionDecl(FunctionType::get(Type::getVoidTy(*TSCtx.getContext()), {}, false),"bar");BasicBlock *BarEntry =BasicBlock::Create(*TSCtx.getContext(), "entry", BarImpl);IRBuilder<> B2(BarEntry);B2.CreateRetVoid();M = ThreadSafeModule(MB.takeModule(), std::move(TSCtx));}// Create a simple stack and set the override flags option.ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>()};auto &JD = ES.createBareJITDylib("main");auto Foo = ES.intern("foo");RTDyldObjectLinkingLayer ObjLayer(ES, []() { return std::make_unique<SectionMemoryManager>(); });IRCompileLayer CompileLayer(ES, ObjLayer,std::make_unique<FunkySimpleCompiler>(*TM));ObjLayer.setOverrideObjectFlagsWithResponsibilityFlags(true);cantFail(CompileLayer.add(JD, std::move(M)));ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo),SymbolState::Resolved,[](Expected<SymbolMap> R) { cantFail(std::move(R)); },NoDependenciesToRegister);if (auto Err = ES.endSession())ES.reportError(std::move(Err));}TEST(RTDyldObjectLinkingLayerTest, TestAutoClaimResponsibilityForSymbols) {OrcNativeTarget::initialize();std::unique_ptr<TargetMachine> TM(EngineBuilder().selectTarget(Triple("x86_64-unknown-linux-gnu"), "", "",SmallVector<std::string, 1>()));if (!TM)return;// Our compiler is going to add a new symbol without telling ORC.// This will test our ability to auto-claim responsibility later.class FunkySimpleCompiler : public SimpleCompiler {public:FunkySimpleCompiler(TargetMachine &TM) : SimpleCompiler(TM) {}Expected<CompileResult> operator()(Module &M) override {Function *BarImpl = Function::Create(FunctionType::get(Type::getVoidTy(M.getContext()), {}, false),GlobalValue::ExternalLinkage, "bar", &M);BasicBlock *BarEntry =BasicBlock::Create(M.getContext(), "entry", BarImpl);IRBuilder<> B(BarEntry);B.CreateRetVoid();return SimpleCompiler::operator()(M);}};// Create a module with two void() functions: foo and bar.ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());ThreadSafeModule M;{ModuleBuilder MB(*TSCtx.getContext(), TM->getTargetTriple().str(), "dummy");MB.getModule()->setDataLayout(TM->createDataLayout());Function *FooImpl = MB.createFunctionDecl(FunctionType::get(Type::getVoidTy(*TSCtx.getContext()), {}, false),"foo");BasicBlock *FooEntry =BasicBlock::Create(*TSCtx.getContext(), "entry", FooImpl);IRBuilder<> B(FooEntry);B.CreateRetVoid();M = ThreadSafeModule(MB.takeModule(), std::move(TSCtx));}// Create a simple stack and set the override flags option.ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>()};auto &JD = ES.createBareJITDylib("main");auto Foo = ES.intern("foo");RTDyldObjectLinkingLayer ObjLayer(ES, []() { return std::make_unique<SectionMemoryManager>(); });IRCompileLayer CompileLayer(ES, ObjLayer,std::make_unique<FunkySimpleCompiler>(*TM));ObjLayer.setAutoClaimResponsibilityForObjectSymbols(true);cantFail(CompileLayer.add(JD, std::move(M)));ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo),SymbolState::Resolved,[](Expected<SymbolMap> R) { cantFail(std::move(R)); },NoDependenciesToRegister);if (auto Err = ES.endSession())ES.reportError(std::move(Err));}} // end anonymous namespace
//===------ OrcTestCommon.h - Utilities for Orc Unit Tests ------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// Common utilities for the Orc unit tests.////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_EXECUTIONENGINE_ORC_ORCTESTCOMMON_H#define LLVM_UNITTESTS_EXECUTIONENGINE_ORC_ORCTESTCOMMON_H#include "llvm/ExecutionEngine/JITSymbol.h"#include "llvm/ExecutionEngine/Orc/Core.h"#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Object/ObjectFile.h"#include "llvm/Support/TargetSelect.h"#include "gtest/gtest.h"#include <memory>namespace llvm {namespace orc {// CoreAPIsStandardTest that saves a bunch of boilerplate by providing the// following://// (1) ES -- An ExecutionSession// (2) Foo, Bar, Baz, Qux -- SymbolStringPtrs for strings "foo", "bar", "baz",// and "qux" respectively.// (3) FooAddr, BarAddr, BazAddr, QuxAddr -- Dummy addresses. Guaranteed// distinct and non-null.// (4) FooSym, BarSym, BazSym, QuxSym -- JITEvaluatedSymbols with FooAddr,// BarAddr, BazAddr, and QuxAddr respectively. All with default strong,// linkage and non-hidden visibility.// (5) V -- A JITDylib associated with ES.class CoreAPIsBasedStandardTest : public testing::Test {public:~CoreAPIsBasedStandardTest() {if (auto Err = ES.endSession())ES.reportError(std::move(Err));}protected:std::shared_ptr<SymbolStringPool> SSP = std::make_shared<SymbolStringPool>();ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>(SSP)};JITDylib &JD = ES.createBareJITDylib("JD");SymbolStringPtr Foo = ES.intern("foo");SymbolStringPtr Bar = ES.intern("bar");SymbolStringPtr Baz = ES.intern("baz");SymbolStringPtr Qux = ES.intern("qux");static const JITTargetAddress FooAddr = 1U;static const JITTargetAddress BarAddr = 2U;static const JITTargetAddress BazAddr = 3U;static const JITTargetAddress QuxAddr = 4U;JITEvaluatedSymbol FooSym =JITEvaluatedSymbol(FooAddr, JITSymbolFlags::Exported);JITEvaluatedSymbol BarSym =JITEvaluatedSymbol(BarAddr, JITSymbolFlags::Exported);JITEvaluatedSymbol BazSym =JITEvaluatedSymbol(BazAddr, JITSymbolFlags::Exported);JITEvaluatedSymbol QuxSym =JITEvaluatedSymbol(QuxAddr, JITSymbolFlags::Exported);};} // end namespace orcclass OrcNativeTarget {public:static void initialize() {if (!NativeTargetInitialized) {InitializeNativeTarget();InitializeNativeTargetAsmParser();InitializeNativeTargetAsmPrinter();NativeTargetInitialized = true;}}private:static bool NativeTargetInitialized;};class SimpleMaterializationUnit : public orc::MaterializationUnit {public:using MaterializeFunction =std::function<void(std::unique_ptr<orc::MaterializationResponsibility>)>;using DiscardFunction =std::function<void(const orc::JITDylib &, orc::SymbolStringPtr)>;using DestructorFunction = std::function<void()>;SimpleMaterializationUnit(orc::SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize,orc::SymbolStringPtr InitSym = nullptr,DiscardFunction Discard = DiscardFunction(),DestructorFunction Destructor = DestructorFunction()): MaterializationUnit(Interface(std::move(SymbolFlags), std::move(InitSym))),Materialize(std::move(Materialize)), Discard(std::move(Discard)),Destructor(std::move(Destructor)) {}~SimpleMaterializationUnit() override {if (Destructor)Destructor();}StringRef getName() const override { return "<Simple>"; }voidmaterialize(std::unique_ptr<orc::MaterializationResponsibility> R) override {Materialize(std::move(R));}void discard(const orc::JITDylib &JD,const orc::SymbolStringPtr &Name) override {if (Discard)Discard(JD, std::move(Name));elsellvm_unreachable("Discard not supported");}private:MaterializeFunction Materialize;DiscardFunction Discard;DestructorFunction Destructor;};class ModuleBuilder {public:ModuleBuilder(LLVMContext &Context, StringRef Triple,StringRef Name);Function *createFunctionDecl(FunctionType *FTy, StringRef Name) {return Function::Create(FTy, GlobalValue::ExternalLinkage, Name, M.get());}Module* getModule() { return M.get(); }const Module* getModule() const { return M.get(); }std::unique_ptr<Module> takeModule() { return std::move(M); }private:std::unique_ptr<Module> M;};// Dummy struct type.struct DummyStruct {int X[256];};inline StructType *getDummyStructTy(LLVMContext &Context) {return StructType::get(ArrayType::get(Type::getInt32Ty(Context), 256));}} // namespace llvm#endif
//===--------- OrcTestCommon.cpp - Utilities for Orc Unit Tests -----------===////// 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////===----------------------------------------------------------------------===////// Common utilities for Orc unit tests.////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"using namespace llvm;const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::FooAddr;const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::BarAddr;const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::BazAddr;const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::QuxAddr;bool OrcNativeTarget::NativeTargetInitialized = false;ModuleBuilder::ModuleBuilder(LLVMContext &Context, StringRef Triple,StringRef Name): M(new Module(Name, Context)) {if (Triple != "")M->setTargetTriple(Triple);}
//===--- OrcCAPITest.cpp - Unit tests for the OrcJIT v2 C API ---*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm-c/Core.h"#include "llvm-c/Error.h"#include "llvm-c/LLJIT.h"#include "llvm-c/Orc.h"#include "gtest/gtest.h"#include "llvm/ADT/Triple.h"#include "llvm/ExecutionEngine/Orc/CompileUtils.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IRReader/IRReader.h"#include "llvm/Support/Error.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/Error.h"#include <string>using namespace llvm;using namespace llvm::orc;DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThreadSafeModule, LLVMOrcThreadSafeModuleRef)// OrcCAPITestBase contains several helper methods and pointers for unit tests// written for the LLVM-C API. It provides the following helpers://// 1. Jit: an LLVMOrcLLJIT instance which is freed upon test exit// 2. ExecutionSession: the LLVMOrcExecutionSession for the JIT// 3. MainDylib: the main JITDylib for the LLJIT instance// 4. materializationUnitFn: function pointer to an empty function, used for// materialization unit testing// 5. definitionGeneratorFn: function pointer for a basic// LLVMOrcCAPIDefinitionGeneratorTryToGenerateFunction// 6. createTestModule: helper method for creating a basic thread-safe-moduleclass OrcCAPITestBase : public testing::Test {protected:LLVMOrcLLJITRef Jit = nullptr;LLVMOrcExecutionSessionRef ExecutionSession = nullptr;LLVMOrcJITDylibRef MainDylib = nullptr;public:static void SetUpTestCase() {LLVMInitializeNativeTarget();LLVMInitializeNativeAsmParser();LLVMInitializeNativeAsmPrinter();// Attempt to set up a JIT instance once to verify that we can.LLVMOrcJITTargetMachineBuilderRef JTMB = nullptr;if (LLVMErrorRef E = LLVMOrcJITTargetMachineBuilderDetectHost(&JTMB)) {// If setup fails then disable these tests.LLVMConsumeError(E);TargetSupported = false;return;}// Capture the target triple. We'll use it for both verification that// this target is *supposed* to be supported, and error messages in// the case that it fails anyway.char *TT = LLVMOrcJITTargetMachineBuilderGetTargetTriple(JTMB);TargetTriple = TT;LLVMDisposeMessage(TT);if (!isSupported(TargetTriple)) {// If this triple isn't supported then bail out.TargetSupported = false;LLVMOrcDisposeJITTargetMachineBuilder(JTMB);return;}LLVMOrcLLJITBuilderRef Builder = LLVMOrcCreateLLJITBuilder();LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(Builder, JTMB);LLVMOrcLLJITRef J;if (LLVMErrorRef E = LLVMOrcCreateLLJIT(&J, Builder)) {// If setup fails then disable these tests.TargetSupported = false;LLVMConsumeError(E);return;}LLVMOrcDisposeLLJIT(J);TargetSupported = true;}void SetUp() override {if (!TargetSupported)GTEST_SKIP();LLVMOrcJITTargetMachineBuilderRef JTMB = nullptr;LLVMErrorRef E1 = LLVMOrcJITTargetMachineBuilderDetectHost(&JTMB);assert(E1 == LLVMErrorSuccess && "Expected call to detect host to succeed");(void)E1;LLVMOrcLLJITBuilderRef Builder = LLVMOrcCreateLLJITBuilder();LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(Builder, JTMB);LLVMErrorRef E2 = LLVMOrcCreateLLJIT(&Jit, Builder);assert(E2 == LLVMErrorSuccess &&"Expected call to create LLJIT to succeed");(void)E2;ExecutionSession = LLVMOrcLLJITGetExecutionSession(Jit);MainDylib = LLVMOrcLLJITGetMainJITDylib(Jit);}void TearDown() override {// Check whether Jit has already been torn down -- we allow clients to do// this manually to check teardown behavior.if (Jit) {LLVMOrcDisposeLLJIT(Jit);Jit = nullptr;}}protected:static bool isSupported(StringRef Triple) {// TODO: Print error messages in failure logs, use them to audit this list.// Some architectures may be unsupportable or missing key components, but// some may just be failing due to bugs in this testcase.if (Triple.startswith("armv7") || Triple.startswith("armv8l"))return false;llvm::Triple T(Triple);if (T.isOSAIX() && T.isPPC64())return false;return true;}static void materializationUnitFn() {}// Stub definition generator, where all Names are materialized from the// materializationUnitFn() test function and defined into the JIT Dylibstatic LLVMErrorRefdefinitionGeneratorFn(LLVMOrcDefinitionGeneratorRef G, void *Ctx,LLVMOrcLookupStateRef *LS, LLVMOrcLookupKind K,LLVMOrcJITDylibRef JD, LLVMOrcJITDylibLookupFlags F,LLVMOrcCLookupSet Names, size_t NamesCount) {for (size_t I = 0; I < NamesCount; I++) {LLVMOrcCLookupSetElement Element = Names[I];LLVMOrcJITTargetAddress Addr =(LLVMOrcJITTargetAddress)(&materializationUnitFn);LLVMJITSymbolFlags Flags = {LLVMJITSymbolGenericFlagsWeak, 0};LLVMJITEvaluatedSymbol Sym = {Addr, Flags};LLVMOrcRetainSymbolStringPoolEntry(Element.Name);LLVMOrcCSymbolMapPair Pair = {Element.Name, Sym};LLVMOrcCSymbolMapPair Pairs[] = {Pair};LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Pairs, 1);LLVMErrorRef Err = LLVMOrcJITDylibDefine(JD, MU);if (Err)return Err;}return LLVMErrorSuccess;}static Error createSMDiagnosticError(llvm::SMDiagnostic &Diag) {std::string Msg;{raw_string_ostream OS(Msg);Diag.print("", OS);}return make_error<StringError>(std::move(Msg), inconvertibleErrorCode());}// Create an LLVM IR module from the given StringRef.static Expected<std::unique_ptr<Module>>parseTestModule(LLVMContext &Ctx, StringRef Source, StringRef Name) {assert(TargetSupported &&"Attempted to create module for unsupported target");SMDiagnostic Err;if (auto M = parseIR(MemoryBufferRef(Source, Name), Err, Ctx))return std::move(M);return createSMDiagnosticError(Err);}// returns the sum of its two parametersstatic LLVMOrcThreadSafeModuleRef createTestModule(StringRef Source,StringRef Name) {auto Ctx = std::make_unique<LLVMContext>();auto M = cantFail(parseTestModule(*Ctx, Source, Name));return wrap(new ThreadSafeModule(std::move(M), std::move(Ctx)));}static LLVMMemoryBufferRef createTestObject(StringRef Source,StringRef Name) {auto Ctx = std::make_unique<LLVMContext>();auto M = cantFail(parseTestModule(*Ctx, Source, Name));auto JTMB = cantFail(JITTargetMachineBuilder::detectHost());M->setDataLayout(cantFail(JTMB.getDefaultDataLayoutForTarget()));auto TM = cantFail(JTMB.createTargetMachine());SimpleCompiler SC(*TM);auto ObjBuffer = cantFail(SC(*M));return wrap(ObjBuffer.release());}static std::string TargetTriple;static bool TargetSupported;};std::string OrcCAPITestBase::TargetTriple;bool OrcCAPITestBase::TargetSupported = false;namespace {constexpr StringRef SumExample =R"(define i32 @sum(i32 %x, i32 %y) {entry:%r = add nsw i32 %x, %yret i32 %r})";} // end anonymous namespace.// Consumes the given error ref and returns the string error message.static std::string toString(LLVMErrorRef E) {char *ErrMsg = LLVMGetErrorMessage(E);std::string Result(ErrMsg);LLVMDisposeErrorMessage(ErrMsg);return Result;}TEST_F(OrcCAPITestBase, SymbolStringPoolUniquing) {LLVMOrcSymbolStringPoolEntryRef E1 =LLVMOrcExecutionSessionIntern(ExecutionSession, "aaa");LLVMOrcSymbolStringPoolEntryRef E2 =LLVMOrcExecutionSessionIntern(ExecutionSession, "aaa");LLVMOrcSymbolStringPoolEntryRef E3 =LLVMOrcExecutionSessionIntern(ExecutionSession, "bbb");const char *SymbolName = LLVMOrcSymbolStringPoolEntryStr(E1);ASSERT_EQ(E1, E2) << "String pool entries are not unique";ASSERT_NE(E1, E3) << "Unique symbol pool entries are equal";ASSERT_STREQ("aaa", SymbolName) << "String value of symbol is not equal";LLVMOrcReleaseSymbolStringPoolEntry(E1);LLVMOrcReleaseSymbolStringPoolEntry(E2);LLVMOrcReleaseSymbolStringPoolEntry(E3);}TEST_F(OrcCAPITestBase, JITDylibLookup) {LLVMOrcJITDylibRef DoesNotExist =LLVMOrcExecutionSessionGetJITDylibByName(ExecutionSession, "test");ASSERT_FALSE(!!DoesNotExist);LLVMOrcJITDylibRef L1 =LLVMOrcExecutionSessionCreateBareJITDylib(ExecutionSession, "test");LLVMOrcJITDylibRef L2 =LLVMOrcExecutionSessionGetJITDylibByName(ExecutionSession, "test");ASSERT_EQ(L1, L2) << "Located JIT Dylib is not equal to original";}TEST_F(OrcCAPITestBase, MaterializationUnitCreation) {LLVMOrcSymbolStringPoolEntryRef Name =LLVMOrcLLJITMangleAndIntern(Jit, "test");LLVMJITSymbolFlags Flags = {LLVMJITSymbolGenericFlagsWeak, 0};LLVMOrcJITTargetAddress Addr =(LLVMOrcJITTargetAddress)(&materializationUnitFn);LLVMJITEvaluatedSymbol Sym = {Addr, Flags};LLVMOrcCSymbolMapPair Pair = {Name, Sym};LLVMOrcCSymbolMapPair Pairs[] = {Pair};LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Pairs, 1);if (LLVMErrorRef E = LLVMOrcJITDylibDefine(MainDylib, MU))FAIL() << "Unexpected error while adding \"test\" symbol (triple = "<< TargetTriple << "): " << toString(E);LLVMOrcJITTargetAddress OutAddr;if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &OutAddr, "test"))FAIL() << "Failed to look up \"test\" symbol (triple = " << TargetTriple<< "): " << toString(E);ASSERT_EQ(Addr, OutAddr);}struct ExecutionSessionLookupHelper {bool ExpectSuccess = true;bool CallbackReceived = false;size_t NumExpectedPairs;LLVMOrcCSymbolMapPair *ExpectedMapping;};static void executionSessionLookupHandlerCallback(LLVMErrorRef Err,LLVMOrcCSymbolMapPairs Result,size_t NumPairs,void *RawCtx) {auto *Ctx = static_cast<ExecutionSessionLookupHelper *>(RawCtx);Ctx->CallbackReceived = true;if (Ctx->ExpectSuccess) {EXPECT_THAT_ERROR(unwrap(Err), Succeeded());EXPECT_EQ(NumPairs, Ctx->NumExpectedPairs)<< "Expected " << Ctx->NumExpectedPairs << " entries in result, got "<< NumPairs;auto ExpectedMappingEnd = Ctx->ExpectedMapping + Ctx->NumExpectedPairs;for (unsigned I = 0; I != NumPairs; ++I) {auto J =std::find_if(Ctx->ExpectedMapping, ExpectedMappingEnd,[N = Result[I].Name](const LLVMOrcCSymbolMapPair &Val) {return Val.Name == N;});EXPECT_NE(J, ExpectedMappingEnd)<< "Missing symbol \""<< LLVMOrcSymbolStringPoolEntryStr(Result[I].Name) << "\"";if (J != ExpectedMappingEnd) {EXPECT_EQ(Result[I].Sym.Address, J->Sym.Address)<< "Result map for \"" << Result[I].Name<< "\" differs from expected value: "<< formatv("{0:x} vs {1:x}", Result[I].Sym.Address, J->Sym.Address);}}} elseEXPECT_THAT_ERROR(unwrap(Err), Failed());}TEST_F(OrcCAPITestBase, ExecutionSessionLookup_Success) {// Test a successful generic lookup. We will look up three symbols over two// JITDylibs: { "Foo" (Required), "Bar" (Weakly-ref), "Baz" (Required) } over// { MainJITDylib (Exported-only), ExtraJD (All symbols) }.//// Foo will be defined as exported in MainJD.// Bar will be defined as non-exported in MainJD.// Baz will be defined as non-exported in ExtraJD.//// This will require (1) that we find the regular exported symbol Foo in// MainJD, (2) that we *don't* find the non-exported symbol Bar in MainJD// but also don't error (since it's weakly referenced), and (3) that we// find the non-exported symbol Baz in ExtraJD (since we're searching all// symbols in ExtraJD).ExecutionSessionLookupHelper H;LLVMOrcSymbolStringPoolEntryRef Foo = LLVMOrcLLJITMangleAndIntern(Jit, "Foo");LLVMOrcSymbolStringPoolEntryRef Bar = LLVMOrcLLJITMangleAndIntern(Jit, "Bar");LLVMOrcSymbolStringPoolEntryRef Baz = LLVMOrcLLJITMangleAndIntern(Jit, "Baz");// Create ExtraJD.LLVMOrcJITDylibRef ExtraJD = nullptr;if (auto E = LLVMOrcExecutionSessionCreateJITDylib(ExecutionSession, &ExtraJD,"ExtraJD")) {FAIL() << "Unexpected error while creating JITDylib \"ExtraJD\" (triple = "<< TargetTriple << "): " << toString(E);return;}// Add exported symbols "Foo" and "Bar" to Main JITDylib.LLVMOrcRetainSymbolStringPoolEntry(Foo);LLVMOrcRetainSymbolStringPoolEntry(Bar);LLVMOrcCSymbolMapPair MainJDPairs[] = {{Foo, {0x1, {LLVMJITSymbolGenericFlagsExported, 0}}},{Bar, {0x2, {LLVMJITSymbolGenericFlagsNone, 0}}}};LLVMOrcMaterializationUnitRef MainJDMU =LLVMOrcAbsoluteSymbols(MainJDPairs, 2);if (LLVMErrorRef E = LLVMOrcJITDylibDefine(MainDylib, MainJDMU))FAIL() << "Unexpected error while adding MainDylib symbols (triple = "<< TargetTriple << "): " << toString(E);// Add non-exported symbol "Baz" to ExtraJD.LLVMOrcRetainSymbolStringPoolEntry(Baz);LLVMOrcCSymbolMapPair ExtraJDPairs[] = {{Baz, {0x3, {LLVMJITSymbolGenericFlagsNone, 0}}}};LLVMOrcMaterializationUnitRef ExtraJDMU =LLVMOrcAbsoluteSymbols(ExtraJDPairs, 1);if (LLVMErrorRef E = LLVMOrcJITDylibDefine(ExtraJD, ExtraJDMU))FAIL() << "Unexpected error while adding ExtraJD symbols (triple = "<< TargetTriple << "): " << toString(E);// Create expected mapping for result:LLVMOrcCSymbolMapPair ExpectedMapping[] = {{Foo, {0x1, {LLVMJITSymbolGenericFlagsExported, 0}}},{Baz, {0x3, {LLVMJITSymbolGenericFlagsNone, 0}}}};H.ExpectedMapping = ExpectedMapping;H.NumExpectedPairs = 2;// Issue the lookup. We're using the default same-thread dispatch, so the// handler should have run by the time we return from this call.LLVMOrcCJITDylibSearchOrderElement SO[] = {{MainDylib, LLVMOrcJITDylibLookupFlagsMatchExportedSymbolsOnly},{ExtraJD, LLVMOrcJITDylibLookupFlagsMatchAllSymbols}};LLVMOrcRetainSymbolStringPoolEntry(Foo);LLVMOrcRetainSymbolStringPoolEntry(Bar);LLVMOrcRetainSymbolStringPoolEntry(Baz);LLVMOrcCLookupSetElement LS[] = {{Foo, LLVMOrcSymbolLookupFlagsRequiredSymbol},{Bar, LLVMOrcSymbolLookupFlagsWeaklyReferencedSymbol},{Baz, LLVMOrcSymbolLookupFlagsRequiredSymbol}};LLVMOrcExecutionSessionLookup(ExecutionSession, LLVMOrcLookupKindStatic, SO,2, LS, 3, executionSessionLookupHandlerCallback,&H);EXPECT_TRUE(H.CallbackReceived) << "Lookup callback never received";// Release our local string ptrs.LLVMOrcReleaseSymbolStringPoolEntry(Baz);LLVMOrcReleaseSymbolStringPoolEntry(Bar);LLVMOrcReleaseSymbolStringPoolEntry(Foo);}TEST_F(OrcCAPITestBase, ExecutionSessionLookup_Failure) {// Test generic lookup failure case. We will look up a symbol in MainDylib// without defining it. We expect this to result in a symbol-not-found error.ExecutionSessionLookupHelper H;H.ExpectSuccess = false;LLVMOrcCJITDylibSearchOrderElement SO[] = {{MainDylib, LLVMOrcJITDylibLookupFlagsMatchExportedSymbolsOnly}};LLVMOrcCLookupSetElement LS[] = {{LLVMOrcLLJITMangleAndIntern(Jit, "Foo"),LLVMOrcSymbolLookupFlagsRequiredSymbol}};LLVMOrcExecutionSessionLookup(ExecutionSession, LLVMOrcLookupKindStatic, SO,1, LS, 1, executionSessionLookupHandlerCallback,&H);EXPECT_TRUE(H.CallbackReceived) << "Lookup callback never received";}TEST_F(OrcCAPITestBase, DefinitionGenerators) {LLVMOrcDefinitionGeneratorRef Gen =LLVMOrcCreateCustomCAPIDefinitionGenerator(&definitionGeneratorFn,nullptr, nullptr);LLVMOrcJITDylibAddGenerator(MainDylib, Gen);LLVMOrcJITTargetAddress OutAddr;if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &OutAddr, "test"))FAIL() << "The DefinitionGenerator did not create symbol \"test\" "<< "(triple = " << TargetTriple << "): " << toString(E);LLVMOrcJITTargetAddress ExpectedAddr =(LLVMOrcJITTargetAddress)(&materializationUnitFn);ASSERT_EQ(ExpectedAddr, OutAddr);}TEST_F(OrcCAPITestBase, ResourceTrackerDefinitionLifetime) {// This test case ensures that all symbols loaded into a JITDylib with a// ResourceTracker attached are cleared from the JITDylib once the RT is// removed.LLVMOrcResourceTrackerRef RT =LLVMOrcJITDylibCreateResourceTracker(MainDylib);LLVMOrcThreadSafeModuleRef TSM = createTestModule(SumExample, "sum.ll");if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModuleWithRT(Jit, RT, TSM))FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple<< "): " << toString(E);LLVMOrcJITTargetAddress TestFnAddr;if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &TestFnAddr, "sum"))FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple<< "): " << toString(E);ASSERT_TRUE(!!TestFnAddr);LLVMOrcResourceTrackerRemove(RT);LLVMOrcJITTargetAddress OutAddr;LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &OutAddr, "sum");ASSERT_TRUE(Err);LLVMConsumeError(Err);ASSERT_FALSE(OutAddr);LLVMOrcReleaseResourceTracker(RT);}TEST_F(OrcCAPITestBase, ResourceTrackerTransfer) {LLVMOrcResourceTrackerRef DefaultRT =LLVMOrcJITDylibGetDefaultResourceTracker(MainDylib);LLVMOrcResourceTrackerRef RT2 =LLVMOrcJITDylibCreateResourceTracker(MainDylib);LLVMOrcThreadSafeModuleRef TSM = createTestModule(SumExample, "sum.ll");if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModuleWithRT(Jit, DefaultRT, TSM))FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple<< "): " << toString(E);LLVMOrcJITTargetAddress Addr;if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &Addr, "sum"))FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple<< "): " << toString(E);LLVMOrcResourceTrackerTransferTo(DefaultRT, RT2);LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "sum");ASSERT_FALSE(Err);LLVMOrcReleaseResourceTracker(RT2);}TEST_F(OrcCAPITestBase, AddObjectBuffer) {LLVMOrcObjectLayerRef ObjLinkingLayer = LLVMOrcLLJITGetObjLinkingLayer(Jit);LLVMMemoryBufferRef ObjBuffer = createTestObject(SumExample, "sum.ll");if (LLVMErrorRef E = LLVMOrcObjectLayerAddObjectFile(ObjLinkingLayer,MainDylib, ObjBuffer))FAIL() << "Failed to add object file to ObjLinkingLayer (triple = "<< TargetTriple << "): " << toString(E);LLVMOrcJITTargetAddress SumAddr;if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &SumAddr, "sum"))FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple<< "): " << toString(E);ASSERT_TRUE(!!SumAddr);}TEST_F(OrcCAPITestBase, ExecutionTest) {using SumFunctionType = int32_t (*)(int32_t, int32_t);// This test performs OrcJIT compilation of a simple sum moduleLLVMInitializeNativeAsmPrinter();LLVMOrcThreadSafeModuleRef TSM = createTestModule(SumExample, "sum.ll");if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModule(Jit, MainDylib, TSM))FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple<< ")" << toString(E);LLVMOrcJITTargetAddress TestFnAddr;if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &TestFnAddr, "sum"))FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple<< "): " << toString(E);auto *SumFn = (SumFunctionType)(TestFnAddr);int32_t Result = SumFn(1, 1);ASSERT_EQ(2, Result);}void Destroy(void *Ctx) {}void TargetFn() {}void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) {LLVMOrcJITDylibRef JD =LLVMOrcMaterializationResponsibilityGetTargetDylib(MR);ASSERT_TRUE(!!JD);LLVMOrcExecutionSessionRef ES =LLVMOrcMaterializationResponsibilityGetExecutionSession(MR);ASSERT_TRUE(!!ES);LLVMOrcSymbolStringPoolEntryRef InitSym =LLVMOrcMaterializationResponsibilityGetInitializerSymbol(MR);ASSERT_TRUE(!InitSym);size_t NumSymbols;LLVMOrcCSymbolFlagsMapPairs Symbols =LLVMOrcMaterializationResponsibilityGetSymbols(MR, &NumSymbols);ASSERT_TRUE(!!Symbols);ASSERT_EQ(NumSymbols, (size_t)1);LLVMOrcSymbolStringPoolEntryRef *RequestedSymbols =LLVMOrcMaterializationResponsibilityGetRequestedSymbols(MR, &NumSymbols);ASSERT_TRUE(!!RequestedSymbols);ASSERT_EQ(NumSymbols, (size_t)1);LLVMOrcCSymbolFlagsMapPair TargetSym = Symbols[0];ASSERT_EQ(RequestedSymbols[0], TargetSym.Name);LLVMOrcRetainSymbolStringPoolEntry(TargetSym.Name);LLVMOrcDisposeCSymbolFlagsMap(Symbols);LLVMOrcDisposeSymbols(RequestedSymbols);LLVMOrcJITTargetAddress Addr = (LLVMOrcJITTargetAddress)(&TargetFn);LLVMJITSymbolFlags Flags = {LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0};ASSERT_EQ(TargetSym.Flags.GenericFlags, Flags.GenericFlags);ASSERT_EQ(TargetSym.Flags.TargetFlags, Flags.TargetFlags);LLVMJITEvaluatedSymbol Sym = {Addr, Flags};LLVMOrcLLJITRef J = (LLVMOrcLLJITRef)Ctx;LLVMOrcSymbolStringPoolEntryRef OtherSymbol =LLVMOrcLLJITMangleAndIntern(J, "other");LLVMOrcSymbolStringPoolEntryRef DependencySymbol =LLVMOrcLLJITMangleAndIntern(J, "dependency");LLVMOrcRetainSymbolStringPoolEntry(OtherSymbol);LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol);LLVMOrcCSymbolFlagsMapPair NewSymbols[] = {{OtherSymbol, Flags},{DependencySymbol, Flags},};LLVMOrcMaterializationResponsibilityDefineMaterializing(MR, NewSymbols, 2);LLVMOrcRetainSymbolStringPoolEntry(OtherSymbol);LLVMOrcMaterializationResponsibilityRef OtherMR = NULL;{LLVMErrorRef Err = LLVMOrcMaterializationResponsibilityDelegate(MR, &OtherSymbol, 1, &OtherMR);if (Err) {char *ErrMsg = LLVMGetErrorMessage(Err);fprintf(stderr, "Error: %s\n", ErrMsg);LLVMDisposeErrorMessage(ErrMsg);LLVMOrcMaterializationResponsibilityFailMaterialization(MR);LLVMOrcDisposeMaterializationResponsibility(MR);return;}}assert(OtherMR);LLVMOrcCSymbolMapPair OtherPair = {OtherSymbol, Sym};LLVMOrcMaterializationUnitRef OtherMU = LLVMOrcAbsoluteSymbols(&OtherPair, 1);// OtherSymbol is no longer owned by us{LLVMErrorRef Err =LLVMOrcMaterializationResponsibilityReplace(OtherMR, OtherMU);if (Err) {char *ErrMsg = LLVMGetErrorMessage(Err);fprintf(stderr, "Error: %s\n", ErrMsg);LLVMDisposeErrorMessage(ErrMsg);LLVMOrcMaterializationResponsibilityFailMaterialization(OtherMR);LLVMOrcMaterializationResponsibilityFailMaterialization(MR);LLVMOrcDisposeMaterializationResponsibility(OtherMR);LLVMOrcDisposeMaterializationResponsibility(MR);LLVMOrcDisposeMaterializationUnit(OtherMU);return;}}LLVMOrcDisposeMaterializationResponsibility(OtherMR);// FIXME: Implement async lookup// A real test of the dependence tracking in the success case would require// async lookups. You could:// 1. Materialize foo, making foo depend on other.// 2. In the caller, verify that the lookup callback for foo has not run (due// to the dependence)// 3. Materialize other by looking it up.// 4. In the caller, verify that the lookup callback for foo has now run.LLVMOrcRetainSymbolStringPoolEntry(TargetSym.Name);LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol);LLVMOrcCDependenceMapPair Dependency = {JD, {&DependencySymbol, 1}};LLVMOrcMaterializationResponsibilityAddDependencies(MR, TargetSym.Name,&Dependency, 1);LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol);LLVMOrcMaterializationResponsibilityAddDependenciesForAll(MR, &Dependency, 1);// See FIXME aboveLLVMOrcCSymbolMapPair Pair = {DependencySymbol, Sym};LLVMOrcMaterializationResponsibilityNotifyResolved(MR, &Pair, 1);// DependencySymbol no longer owned by usPair = {TargetSym.Name, Sym};LLVMOrcMaterializationResponsibilityNotifyResolved(MR, &Pair, 1);LLVMOrcMaterializationResponsibilityNotifyEmitted(MR);LLVMOrcDisposeMaterializationResponsibility(MR);return;}TEST_F(OrcCAPITestBase, MaterializationResponsibility) {LLVMJITSymbolFlags Flags = {LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0};LLVMOrcCSymbolFlagsMapPair Sym = {LLVMOrcLLJITMangleAndIntern(Jit, "foo"),Flags};LLVMOrcMaterializationUnitRef MU = LLVMOrcCreateCustomMaterializationUnit("MU", (void *)Jit, &Sym, 1, NULL, &Materialize, NULL, &Destroy);LLVMOrcJITDylibRef JD = LLVMOrcLLJITGetMainJITDylib(Jit);LLVMOrcJITDylibDefine(JD, MU);LLVMOrcJITTargetAddress Addr;if (LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "foo")) {FAIL() << "foo was not materialized " << toString(Err);}ASSERT_TRUE(!!Addr);ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn);if (LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "other")) {FAIL() << "other was not materialized " << toString(Err);}ASSERT_TRUE(!!Addr);ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn);if (LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "dependency")) {FAIL() << "dependency was not materialized " << toString(Err);}ASSERT_TRUE(!!Addr);ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn);}struct SuspendedLookupContext {std::function<void()> AsyncWork;LLVMOrcSymbolStringPoolEntryRef NameToGenerate;JITTargetAddress AddrToGenerate;bool Disposed = false;bool QueryCompleted = true;};static LLVMErrorRef TryToGenerateWithSuspendedLookup(LLVMOrcDefinitionGeneratorRef GeneratorObj, void *RawCtx,LLVMOrcLookupStateRef *LookupState, LLVMOrcLookupKind Kind,LLVMOrcJITDylibRef JD, LLVMOrcJITDylibLookupFlags JDLookupFlags,LLVMOrcCLookupSet LookupSet, size_t LookupSetSize) {auto *Ctx = static_cast<SuspendedLookupContext *>(RawCtx);assert(LookupSetSize == 1);assert(LookupSet[0].Name == Ctx->NameToGenerate);LLVMJITEvaluatedSymbol Sym = {0x1234, {LLVMJITSymbolGenericFlagsExported, 0}};LLVMOrcRetainSymbolStringPoolEntry(LookupSet[0].Name);LLVMOrcCSymbolMapPair Pair = {LookupSet[0].Name, Sym};LLVMOrcCSymbolMapPair Pairs[] = {Pair};LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Pairs, 1);// Capture and reset LookupState to suspend the lookup. We'll continue it in// the SuspendedLookup testcase below.Ctx->AsyncWork = [LS = *LookupState, JD, MU]() {LLVMErrorRef Err = LLVMOrcJITDylibDefine(JD, MU);LLVMOrcLookupStateContinueLookup(LS, Err);};*LookupState = nullptr;return LLVMErrorSuccess;}static void DisposeSuspendedLookupContext(void *Ctx) {static_cast<SuspendedLookupContext *>(Ctx)->Disposed = true;}static voidsuspendLookupTestLookupHandlerCallback(LLVMErrorRef Err,LLVMOrcCSymbolMapPairs Result,size_t NumPairs, void *RawCtx) {if (Err) {FAIL() << "Suspended DefinitionGenerator did not create symbol \"foo\": "<< toString(Err);return;}EXPECT_EQ(NumPairs, 1U)<< "Unexpected number of result entries: expected 1, got " << NumPairs;auto *Ctx = static_cast<SuspendedLookupContext *>(RawCtx);EXPECT_EQ(Result[0].Name, Ctx->NameToGenerate);EXPECT_EQ(Result[0].Sym.Address, Ctx->AddrToGenerate);Ctx->QueryCompleted = true;}TEST_F(OrcCAPITestBase, SuspendedLookup) {// Test that we can suspend lookup in a custom generator.SuspendedLookupContext Ctx;Ctx.NameToGenerate = LLVMOrcLLJITMangleAndIntern(Jit, "foo");Ctx.AddrToGenerate = 0x1234;// Add generator.LLVMOrcJITDylibAddGenerator(MainDylib,LLVMOrcCreateCustomCAPIDefinitionGenerator(&TryToGenerateWithSuspendedLookup, &Ctx,DisposeSuspendedLookupContext));// Expect no work to do before the lookup.EXPECT_FALSE(Ctx.AsyncWork) << "Unexpected generator work before lookup";// Issue lookup. This should trigger the generator, but generation should// be suspended.LLVMOrcCJITDylibSearchOrderElement SO[] = {{MainDylib, LLVMOrcJITDylibLookupFlagsMatchExportedSymbolsOnly}};LLVMOrcRetainSymbolStringPoolEntry(Ctx.NameToGenerate);LLVMOrcCLookupSetElement LS[] = {{Ctx.NameToGenerate, LLVMOrcSymbolLookupFlagsRequiredSymbol}};LLVMOrcExecutionSessionLookup(ExecutionSession, LLVMOrcLookupKindStatic, SO,1, LS, 1,suspendLookupTestLookupHandlerCallback, &Ctx);// Expect that we now have generator work to do.EXPECT_TRUE(Ctx.AsyncWork)<< "Failed to generator (or failed to suspend generator)";// Do the work. This should allow the query to complete.Ctx.AsyncWork();// Check that the query completed.EXPECT_TRUE(Ctx.QueryCompleted);// Release our local copy of the string.LLVMOrcReleaseSymbolStringPoolEntry(Ctx.NameToGenerate);// Explicitly tear down the JIT.LLVMOrcDisposeLLJIT(Jit);Jit = nullptr;// Check that the generator context was "destroyed".EXPECT_TRUE(Ctx.Disposed);}
//===-------- ObjectLinkingLayerTest.cpp - ObjectLinkingLayer tests -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"#include "llvm/ExecutionEngine/JITLink/JITLink.h"#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"#include "llvm/ExecutionEngine/JITLink/x86_64.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::jitlink;using namespace llvm::orc;namespace {const char BlockContentBytes[] = {0x01, 0x02, 0x03, 0x04,0x05, 0x06, 0x07, 0x08};ArrayRef<char> BlockContent(BlockContentBytes);class ObjectLinkingLayerTest : public testing::Test {public:~ObjectLinkingLayerTest() {if (auto Err = ES.endSession())ES.reportError(std::move(Err));}protected:ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>()};JITDylib &JD = ES.createBareJITDylib("main");ObjectLinkingLayer ObjLinkingLayer{ES, std::make_unique<InProcessMemoryManager>(4096)};};TEST_F(ObjectLinkingLayerTest, AddLinkGraph) {auto G =std::make_unique<LinkGraph>("foo", Triple("x86_64-apple-darwin"), 8,support::little, x86_64::getEdgeKindName);auto &Sec1 = G->createSection("__data", MemProt::Read | MemProt::Write);auto &B1 = G->createContentBlock(Sec1, BlockContent,orc::ExecutorAddr(0x1000), 8, 0);G->addDefinedSymbol(B1, 4, "_X", 4, Linkage::Strong, Scope::Default, false,false);EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G)), Succeeded());EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_X"), Succeeded());}} // end anonymous namespace
//===------------------------ MemoryMapperTest.cpp ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/MemoryMapper.h"#include "llvm/Support/Process.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::orc;using namespace llvm::orc::shared;namespace {Expected<ExecutorAddrRange> reserve(MemoryMapper &M, size_t NumBytes) {std::promise<MSVCPExpected<ExecutorAddrRange>> P;auto F = P.get_future();M.reserve(NumBytes, [&](auto R) { P.set_value(std::move(R)); });return F.get();}Expected<ExecutorAddr> initialize(MemoryMapper &M,MemoryMapper::AllocInfo &AI) {std::promise<MSVCPExpected<ExecutorAddr>> P;auto F = P.get_future();M.initialize(AI, [&](auto R) { P.set_value(std::move(R)); });return F.get();}Error deinitialize(MemoryMapper &M,const std::vector<ExecutorAddr> &Allocations) {std::promise<MSVCPError> P;auto F = P.get_future();M.deinitialize(Allocations, [&](auto R) { P.set_value(std::move(R)); });return F.get();}Error release(MemoryMapper &M, const std::vector<ExecutorAddr> &Reservations) {std::promise<MSVCPError> P;auto F = P.get_future();M.release(Reservations, [&](auto R) { P.set_value(std::move(R)); });return F.get();}// A basic function to be used as both initializer/deinitializerorc::shared::CWrapperFunctionResult incrementWrapper(const char *ArgData,size_t ArgSize) {return WrapperFunction<SPSError(SPSExecutorAddr)>::handle(ArgData, ArgSize,[](ExecutorAddr A) -> Error {*A.toPtr<int *>() += 1;return Error::success();}).release();}TEST(MemoryMapperTest, InitializeDeinitialize) {// These counters are used to track how many times the initializer and// deinitializer functions are calledint InitializeCounter = 0;int DeinitializeCounter = 0;{std::unique_ptr<MemoryMapper> Mapper =cantFail(InProcessMemoryMapper::Create());// We will do two separate allocationsauto PageSize = Mapper->getPageSize();auto TotalSize = PageSize * 2;// Reserve address spaceauto Mem1 = reserve(*Mapper, TotalSize);EXPECT_THAT_ERROR(Mem1.takeError(), Succeeded());// Test string for memory transferstd::string HW = "Hello, world!";{// Provide working memorychar *WA1 = Mapper->prepare(Mem1->Start, HW.size() + 1);std::strcpy(static_cast<char *>(WA1), HW.c_str());}// A structure to be passed to initializeMemoryMapper::AllocInfo Alloc1;{MemoryMapper::AllocInfo::SegInfo Seg1;Seg1.Offset = 0;Seg1.ContentSize = HW.size();Seg1.ZeroFillSize = PageSize - Seg1.ContentSize;Seg1.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;Alloc1.MappingBase = Mem1->Start;Alloc1.Segments.push_back(Seg1);Alloc1.Actions.push_back({cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&InitializeCounter))),cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&DeinitializeCounter)))});}{char *WA2 = Mapper->prepare(Mem1->Start + PageSize, HW.size() + 1);std::strcpy(static_cast<char *>(WA2), HW.c_str());}MemoryMapper::AllocInfo Alloc2;{MemoryMapper::AllocInfo::SegInfo Seg2;Seg2.Offset = PageSize;Seg2.ContentSize = HW.size();Seg2.ZeroFillSize = PageSize - Seg2.ContentSize;Seg2.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;Alloc2.MappingBase = Mem1->Start;Alloc2.Segments.push_back(Seg2);Alloc2.Actions.push_back({cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&InitializeCounter))),cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&DeinitializeCounter)))});}EXPECT_EQ(InitializeCounter, 0);EXPECT_EQ(DeinitializeCounter, 0);// Set memory protections and run initializersauto Init1 = initialize(*Mapper, Alloc1);EXPECT_THAT_ERROR(Init1.takeError(), Succeeded());EXPECT_EQ(HW, std::string(static_cast<char *>(Init1->toPtr<char *>())));EXPECT_EQ(InitializeCounter, 1);EXPECT_EQ(DeinitializeCounter, 0);auto Init2 = initialize(*Mapper, Alloc2);EXPECT_THAT_ERROR(Init2.takeError(), Succeeded());EXPECT_EQ(HW, std::string(static_cast<char *>(Init2->toPtr<char *>())));EXPECT_EQ(InitializeCounter, 2);EXPECT_EQ(DeinitializeCounter, 0);// Explicit deinitialization of first allocationstd::vector<ExecutorAddr> DeinitAddr = {*Init1};EXPECT_THAT_ERROR(deinitialize(*Mapper, DeinitAddr), Succeeded());EXPECT_EQ(InitializeCounter, 2);EXPECT_EQ(DeinitializeCounter, 1);// Test explicit release{auto Mem2 = reserve(*Mapper, PageSize);EXPECT_THAT_ERROR(Mem2.takeError(), Succeeded());char *WA = Mapper->prepare(Mem2->Start, HW.size() + 1);std::strcpy(static_cast<char *>(WA), HW.c_str());MemoryMapper::AllocInfo Alloc3;{MemoryMapper::AllocInfo::SegInfo Seg3;Seg3.Offset = 0;Seg3.ContentSize = HW.size();Seg3.ZeroFillSize = PageSize - Seg3.ContentSize;Seg3.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;Alloc3.MappingBase = Mem2->Start;Alloc3.Segments.push_back(Seg3);Alloc3.Actions.push_back({cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&InitializeCounter))),cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(ExecutorAddr::fromPtr(incrementWrapper),ExecutorAddr::fromPtr(&DeinitializeCounter)))});}auto Init3 = initialize(*Mapper, Alloc3);EXPECT_THAT_ERROR(Init3.takeError(), Succeeded());EXPECT_EQ(HW, std::string(static_cast<char *>(Init3->toPtr<char *>())));EXPECT_EQ(InitializeCounter, 3);EXPECT_EQ(DeinitializeCounter, 1);std::vector<ExecutorAddr> ReleaseAddrs = {Mem2->Start};EXPECT_THAT_ERROR(release(*Mapper, ReleaseAddrs), Succeeded());EXPECT_EQ(InitializeCounter, 3);EXPECT_EQ(DeinitializeCounter, 2);}}// Implicit deinitialization by the destructorEXPECT_EQ(InitializeCounter, 3);EXPECT_EQ(DeinitializeCounter, 3);}} // namespace
//===-------------- MapperJITLinkMemoryManagerTest.cpp ----------------===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"#include "llvm/Testing/Support/Error.h"#include <vector>using namespace llvm;using namespace llvm::jitlink;using namespace llvm::orc;using namespace llvm::orc::shared;namespace {TEST(MapperJITLinkMemoryManagerTest, InProcess) {auto MemMgr = cantFail(MapperJITLinkMemoryManager::CreateWithMapper<InProcessMemoryMapper>());StringRef Hello = "hello";auto SSA = jitlink::SimpleSegmentAlloc::Create(*MemMgr, nullptr, {{jitlink::MemProt::Read, {Hello.size(), Align(1)}}});EXPECT_THAT_EXPECTED(SSA, Succeeded());auto SegInfo = SSA->getSegInfo(jitlink::MemProt::Read);memcpy(SegInfo.WorkingMem.data(), Hello.data(), Hello.size());auto FA = SSA->finalize();EXPECT_THAT_EXPECTED(FA, Succeeded());ExecutorAddr TargetAddr(SegInfo.Addr);const char *TargetMem = TargetAddr.toPtr<const char *>();StringRef TargetHello(TargetMem, Hello.size());EXPECT_EQ(Hello, TargetHello);auto Err2 = MemMgr->deallocate(std::move(*FA));EXPECT_THAT_ERROR(std::move(Err2), Succeeded());}} // namespace
//===- LookupAndRecordAddrsTest.cpp - Unit tests for LookupAndRecordAddrs -===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"#include "llvm/Support/MSVCErrorWorkarounds.h"#include "llvm/Testing/Support/Error.h"#include <future>using namespace llvm;using namespace llvm::orc;class LookupAndRecordAddrsTest : public CoreAPIsBasedStandardTest {};namespace {TEST_F(LookupAndRecordAddrsTest, AsyncRequiredSuccess) {cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));ExecutorAddr FooAddress, BarAddress;std::promise<MSVCPError> ErrP;lookupAndRecordAddrs([&](Error Err) { ErrP.set_value(std::move(Err)); }, ES,LookupKind::Static, makeJITDylibSearchOrder(&JD),{{Foo, &FooAddress}, {Bar, &BarAddress}});Error Err = ErrP.get_future().get();EXPECT_THAT_ERROR(std::move(Err), Succeeded());EXPECT_EQ(FooAddress.getValue(), FooAddr);EXPECT_EQ(BarAddress.getValue(), BarAddr);}TEST_F(LookupAndRecordAddrsTest, AsyncRequiredFailure) {ExecutorAddr FooAddress, BarAddress;std::promise<MSVCPError> ErrP;lookupAndRecordAddrs([&](Error Err) { ErrP.set_value(std::move(Err)); }, ES,LookupKind::Static, makeJITDylibSearchOrder(&JD),{{Foo, &FooAddress}, {Bar, &BarAddress}});Error Err = ErrP.get_future().get();EXPECT_THAT_ERROR(std::move(Err), Failed());}TEST_F(LookupAndRecordAddrsTest, AsyncWeakReference) {cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));ExecutorAddr FooAddress, BarAddress;std::promise<MSVCPError> ErrP;lookupAndRecordAddrs([&](Error Err) { ErrP.set_value(std::move(Err)); }, ES,LookupKind::Static, makeJITDylibSearchOrder(&JD),{{Foo, &FooAddress}, {Bar, &BarAddress}},SymbolLookupFlags::WeaklyReferencedSymbol);Error Err = ErrP.get_future().get();EXPECT_THAT_ERROR(std::move(Err), Succeeded());EXPECT_EQ(FooAddress.getValue(), FooAddr);EXPECT_EQ(BarAddress.getValue(), 0U);}TEST_F(LookupAndRecordAddrsTest, BlockingRequiredSuccess) {cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));ExecutorAddr FooAddress, BarAddress;auto Err =lookupAndRecordAddrs(ES, LookupKind::Static, makeJITDylibSearchOrder(&JD),{{Foo, &FooAddress}, {Bar, &BarAddress}});EXPECT_THAT_ERROR(std::move(Err), Succeeded());EXPECT_EQ(FooAddress.getValue(), FooAddr);EXPECT_EQ(BarAddress.getValue(), BarAddr);}TEST_F(LookupAndRecordAddrsTest, BlockingRequiredFailure) {ExecutorAddr FooAddress, BarAddress;auto Err =lookupAndRecordAddrs(ES, LookupKind::Static, makeJITDylibSearchOrder(&JD),{{Foo, &FooAddress}, {Bar, &BarAddress}});EXPECT_THAT_ERROR(std::move(Err), Failed());}TEST_F(LookupAndRecordAddrsTest, BlockingWeakReference) {cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));ExecutorAddr FooAddress, BarAddress;auto Err =lookupAndRecordAddrs(ES, LookupKind::Static, makeJITDylibSearchOrder(&JD),{{Foo, &FooAddress}, {Bar, &BarAddress}},SymbolLookupFlags::WeaklyReferencedSymbol);EXPECT_THAT_ERROR(std::move(Err), Succeeded());EXPECT_EQ(FooAddress.getValue(), FooAddr);EXPECT_EQ(BarAddress.getValue(), 0U);}} // namespace
#include "OrcTestCommon.h"#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"#include "llvm/ExecutionEngine/Orc/LazyReexports.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::orc;class LazyReexportsTest : public CoreAPIsBasedStandardTest {};static int dummyTarget() { return 42; }TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) {// Create a callthrough manager for the host (if possible) and verify that// a call to the lazy call-through:// (1) Materializes the MU. This verifies that the symbol was looked up, and// that we didn't arrive at the target via some other path// (2) Returns the expected value (which we take as proof that the call// reached the target).auto JTMB = JITTargetMachineBuilder::detectHost();// Bail out if we can not detect the host.if (!JTMB) {consumeError(JTMB.takeError());return;}// Bail out if we can not build a local call-through manager.auto LCTM = createLocalLazyCallThroughManager(JTMB->getTargetTriple(), ES, 0);if (!LCTM) {consumeError(LCTM.takeError());return;}auto DummyTarget = ES.intern("DummyTarget");bool DummyTargetMaterialized = false;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{DummyTarget, JITSymbolFlags::Exported}}),[&](std::unique_ptr<MaterializationResponsibility> R) {DummyTargetMaterialized = true;// No dependencies registered, can't fail.cantFail(R->notifyResolved({{DummyTarget,JITEvaluatedSymbol(static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(&dummyTarget)),JITSymbolFlags::Exported)}}));cantFail(R->notifyEmitted());})));unsigned NotifyResolvedCount = 0;auto NotifyResolved = [&](JITTargetAddress ResolvedAddr) {++NotifyResolvedCount;return Error::success();};auto CallThroughTrampoline = cantFail((*LCTM)->getCallThroughTrampoline(JD, DummyTarget, std::move(NotifyResolved)));auto CTTPtr = reinterpret_cast<int (*)()>(static_cast<uintptr_t>(CallThroughTrampoline));// Call twice to verify nothing unexpected happens on redundant calls.auto Result = CTTPtr();(void)CTTPtr();EXPECT_TRUE(DummyTargetMaterialized)<< "CallThrough did not materialize target";EXPECT_EQ(NotifyResolvedCount, 1U)<< "CallThrough should have generated exactly one 'NotifyResolved' call";EXPECT_EQ(Result, 42) << "Failed to call through to target";}
//===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"#include "OrcTestCommon.h"using namespace llvm;using namespace llvm::orc;namespace {TEST(ExecutionUtilsTest, JITTargetMachineBuilder) {// Tests basic API usage.// Bails out on error, as it is valid to run this test without any targets// built.// Make sure LLVM has been initialized.OrcNativeTarget::initialize();auto JTMB = cantFail(JITTargetMachineBuilder::detectHost());// Test API by performing a bunch of no-ops.JTMB.setCPU("");JTMB.setRelocationModel(None);JTMB.setCodeModel(None);JTMB.setCodeGenOptLevel(CodeGenOpt::None);JTMB.addFeatures(std::vector<std::string>());SubtargetFeatures &STF = JTMB.getFeatures();(void)STF;TargetOptions &TO = JTMB.getOptions();(void)TO;Triple &TT = JTMB.getTargetTriple();(void)TT;auto TM = JTMB.createTargetMachine();if (!TM)consumeError(TM.takeError());else {EXPECT_NE(TM.get(), nullptr)<< "JITTargetMachineBuilder should return a non-null TargetMachine ""on success";}}} // namespace
//===- LazyEmittingLayerTest.cpp - Unit tests for the lazy emitting layer -===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"#include "OrcTestCommon.h"#include "llvm/ADT/SmallVector.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(IndirectionUtilsTest, MakeStub) {LLVMContext Context;ModuleBuilder MB(Context, "x86_64-apple-macosx10.10", "");StructType *ArgTy = getDummyStructTy(Context);Type *ArgPtrTy = PointerType::getUnqual(ArgTy);FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), {ArgPtrTy, ArgPtrTy}, false);Function *F = MB.createFunctionDecl(FTy, "");AttributeSet FnAttrs = AttributeSet::get(Context, AttrBuilder(Context).addAttribute(Attribute::NoUnwind));AttributeSet RetAttrs; // NoneAttributeSet ArgAttrs[2] = {AttributeSet::get(Context, AttrBuilder(Context).addStructRetAttr(ArgTy)),AttributeSet::get(Context, AttrBuilder(Context).addByValAttr(ArgTy)),};F->setAttributes(AttributeList::get(Context, FnAttrs, RetAttrs, ArgAttrs));auto ImplPtr = orc::createImplPointer(*F->getType(), *MB.getModule(), "", nullptr);orc::makeStub(*F, *ImplPtr);auto II = F->getEntryBlock().begin();EXPECT_TRUE(isa<LoadInst>(*II)) << "First instruction of stub should be a load.";auto *Call = dyn_cast<CallInst>(std::next(II));EXPECT_TRUE(Call != nullptr) << "Second instruction of stub should be a call.";EXPECT_TRUE(Call->isTailCall()) << "Indirect call from stub should be tail call.";EXPECT_TRUE(Call->hasStructRetAttr())<< "makeStub should propagate sret attr on 1st argument.";EXPECT_TRUE(Call->paramHasAttr(1U, Attribute::ByVal))<< "makeStub should propagate byval attr on 2nd argument.";}}
//===--------- ExecutorAddrTest.cpp - Unit tests for ExecutorAddr ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"#include "OrcTestCommon.h"using namespace llvm;using namespace llvm::orc;namespace {TEST(ExecutorAddrTest, DefaultAndNull) {// Check that default constructed values and isNull behave as expected.ExecutorAddr Default;ExecutorAddr Null(0);ExecutorAddr NonNull(1);EXPECT_TRUE(Null.isNull());EXPECT_EQ(Default, Null);EXPECT_FALSE(NonNull.isNull());EXPECT_NE(Default, NonNull);}TEST(ExecutorAddrTest, Ordering) {// Check that ordering operations.ExecutorAddr A1(1), A2(2);EXPECT_LE(A1, A1);EXPECT_LT(A1, A2);EXPECT_GT(A2, A1);EXPECT_GE(A2, A2);}TEST(ExecutorAddrTest, PtrConversion) {// Test toPtr / fromPtr round-tripping.int X = 0;auto XAddr = ExecutorAddr::fromPtr(&X);int *XPtr = XAddr.toPtr<int *>();EXPECT_EQ(XPtr, &X);}static void F() {}TEST(ExecutorAddrTest, PtrConversionWithFunctionType) {// Test that function types (as opposed to function pointer types) can be// used with toPtr.auto FAddr = ExecutorAddr::fromPtr(F);void (*FPtr)() = FAddr.toPtr<void()>();EXPECT_EQ(FPtr, &F);}TEST(ExecutorAddrTest, AddrRanges) {ExecutorAddr A0(0), A1(1), A2(2), A3(3);ExecutorAddrRange R0(A0, A1), R1(A1, A2), R2(A2, A3), R3(A0, A2), R4(A1, A3);// 012// R0: # -- Before R1// R1: # --// R2: # -- After R1// R3: ## -- Overlaps R1 start// R4: ## -- Overlaps R1 endEXPECT_EQ(R1, ExecutorAddrRange(A1, A2));EXPECT_EQ(R1, ExecutorAddrRange(A1, ExecutorAddrDiff(1)));EXPECT_NE(R1, R2);EXPECT_TRUE(R1.contains(A1));EXPECT_FALSE(R1.contains(A0));EXPECT_FALSE(R1.contains(A2));EXPECT_FALSE(R1.overlaps(R0));EXPECT_FALSE(R1.overlaps(R2));EXPECT_TRUE(R1.overlaps(R3));EXPECT_TRUE(R1.overlaps(R4));}} // namespace
//===- ExecutionSessionWrapperFunctionCallsTest.cpp -- Test wrapper calls -===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/Orc/Core.h"#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"#include "llvm/Support/MSVCErrorWorkarounds.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <future>using namespace llvm;using namespace llvm::orc;using namespace llvm::orc::shared;static llvm::orc::shared::CWrapperFunctionResult addWrapper(const char *ArgData,size_t ArgSize) {return WrapperFunction<int32_t(int32_t, int32_t)>::handle(ArgData, ArgSize, [](int32_t X, int32_t Y) { return X + Y; }).release();}static void addAsyncWrapper(unique_function<void(int32_t)> SendResult,int32_t X, int32_t Y) {SendResult(X + Y);}static llvm::orc::shared::CWrapperFunctionResultvoidWrapper(const char *ArgData, size_t ArgSize) {return WrapperFunction<void()>::handle(ArgData, ArgSize, []() {}).release();}TEST(ExecutionSessionWrapperFunctionCalls, RunWrapperTemplate) {ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));int32_t Result;EXPECT_THAT_ERROR(ES.callSPSWrapper<int32_t(int32_t, int32_t)>(ExecutorAddr::fromPtr(addWrapper), Result, 2, 3),Succeeded());EXPECT_EQ(Result, 5);cantFail(ES.endSession());}TEST(ExecutionSessionWrapperFunctionCalls, RunVoidWrapperAsyncTemplate) {ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));std::promise<MSVCPError> RP;ES.callSPSWrapperAsync<void()>(ExecutorAddr::fromPtr(voidWrapper),[&](Error SerializationErr) {RP.set_value(std::move(SerializationErr));});Error Err = RP.get_future().get();EXPECT_THAT_ERROR(std::move(Err), Succeeded());cantFail(ES.endSession());}TEST(ExecutionSessionWrapperFunctionCalls, RunNonVoidWrapperAsyncTemplate) {ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));std::promise<MSVCPExpected<int32_t>> RP;ES.callSPSWrapperAsync<int32_t(int32_t, int32_t)>(ExecutorAddr::fromPtr(addWrapper),[&](Error SerializationErr, int32_t R) {if (SerializationErr)RP.set_value(std::move(SerializationErr));RP.set_value(std::move(R));},2, 3);Expected<int32_t> Result = RP.get_future().get();EXPECT_THAT_EXPECTED(Result, HasValue(5));cantFail(ES.endSession());}TEST(ExecutionSessionWrapperFunctionCalls, RegisterAsyncHandlerAndRun) {constexpr JITTargetAddress AddAsyncTagAddr = 0x01;ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));auto &JD = ES.createBareJITDylib("JD");auto AddAsyncTag = ES.intern("addAsync_tag");cantFail(JD.define(absoluteSymbols({{AddAsyncTag,JITEvaluatedSymbol(AddAsyncTagAddr, JITSymbolFlags::Exported)}})));ExecutionSession::JITDispatchHandlerAssociationMap Associations;Associations[AddAsyncTag] =ES.wrapAsyncWithSPS<int32_t(int32_t, int32_t)>(addAsyncWrapper);cantFail(ES.registerJITDispatchHandlers(JD, std::move(Associations)));std::promise<int32_t> RP;auto RF = RP.get_future();using ArgSerialization = SPSArgList<int32_t, int32_t>;size_t ArgBufferSize = ArgSerialization::size(1, 2);auto ArgBuffer = WrapperFunctionResult::allocate(ArgBufferSize);SPSOutputBuffer OB(ArgBuffer.data(), ArgBuffer.size());EXPECT_TRUE(ArgSerialization::serialize(OB, 1, 2));ES.runJITDispatchHandler([&](WrapperFunctionResult ResultBuffer) {int32_t Result;SPSInputBuffer IB(ResultBuffer.data(), ResultBuffer.size());EXPECT_TRUE(SPSArgList<int32_t>::deserialize(IB, Result));RP.set_value(Result);},AddAsyncTagAddr, ArrayRef<char>(ArgBuffer.data(), ArgBuffer.size()));EXPECT_EQ(RF.get(), (int32_t)3);cantFail(ES.endSession());}
//===- EPCGenericMemoryAccessTest.cpp -- Tests for EPCGenericMemoryAccess -===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h"#include "llvm/Testing/Support/Error.h"using namespace llvm;using namespace llvm::orc;using namespace llvm::orc::shared;namespace {template <typename WriteT, typename SPSWriteT>llvm::orc::shared::CWrapperFunctionResult testWriteUInts(const char *ArgData,size_t ArgSize) {return WrapperFunction<void(SPSSequence<SPSWriteT>)>::handle(ArgData, ArgSize,[](std::vector<WriteT> Ws) {for (auto &W : Ws)*W.Addr.template toPtr<decltype(W.Value) *>() = W.Value;}).release();}llvm::orc::shared::CWrapperFunctionResult testWriteBuffers(const char *ArgData,size_t ArgSize) {return WrapperFunction<void(SPSSequence<SPSMemoryAccessBufferWrite>)>::handle(ArgData, ArgSize,[](std::vector<tpctypes::BufferWrite> Ws) {for (auto &W : Ws)memcpy(W.Addr.template toPtr<char *>(), W.Buffer.data(),W.Buffer.size());}).release();}TEST(EPCGenericMemoryAccessTest, MemWrites) {auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());EPCGenericMemoryAccess::FuncAddrs FAs;FAs.WriteUInt8s = ExecutorAddr::fromPtr(&testWriteUInts<tpctypes::UInt8Write, SPSMemoryAccessUInt8Write>);FAs.WriteUInt16s = ExecutorAddr::fromPtr(&testWriteUInts<tpctypes::UInt16Write, SPSMemoryAccessUInt16Write>);FAs.WriteUInt32s = ExecutorAddr::fromPtr(&testWriteUInts<tpctypes::UInt32Write, SPSMemoryAccessUInt32Write>);FAs.WriteUInt64s = ExecutorAddr::fromPtr(&testWriteUInts<tpctypes::UInt64Write, SPSMemoryAccessUInt64Write>);FAs.WriteBuffers = ExecutorAddr::fromPtr(&testWriteBuffers);auto MemAccess = std::make_unique<EPCGenericMemoryAccess>(*SelfEPC, FAs);uint8_t Test_UInt8_1 = 0;uint8_t Test_UInt8_2 = 0;uint16_t Test_UInt16 = 0;uint32_t Test_UInt32 = 0;uint64_t Test_UInt64 = 0;char Test_Buffer[21];auto Err1 =MemAccess->writeUInt8s({{ExecutorAddr::fromPtr(&Test_UInt8_1), 1},{ExecutorAddr::fromPtr(&Test_UInt8_2), 0xFE}});EXPECT_THAT_ERROR(std::move(Err1), Succeeded());EXPECT_EQ(Test_UInt8_1, 1U);EXPECT_EQ(Test_UInt8_2, 0xFE);auto Err2 =MemAccess->writeUInt16s({{ExecutorAddr::fromPtr(&Test_UInt16), 1}});EXPECT_THAT_ERROR(std::move(Err2), Succeeded());EXPECT_EQ(Test_UInt16, 1U);auto Err3 =MemAccess->writeUInt32s({{ExecutorAddr::fromPtr(&Test_UInt32), 1}});EXPECT_THAT_ERROR(std::move(Err3), Succeeded());EXPECT_EQ(Test_UInt32, 1U);auto Err4 =MemAccess->writeUInt64s({{ExecutorAddr::fromPtr(&Test_UInt64), 1}});EXPECT_THAT_ERROR(std::move(Err4), Succeeded());EXPECT_EQ(Test_UInt64, 1U);StringRef TestMsg("test-message");auto Err5 =MemAccess->writeBuffers({{ExecutorAddr::fromPtr(&Test_Buffer), TestMsg}});EXPECT_THAT_ERROR(std::move(Err5), Succeeded());EXPECT_EQ(StringRef(Test_Buffer, TestMsg.size()), TestMsg);cantFail(SelfEPC->disconnect());}} // namespace
//===-------------- EPCGenericJITLinkMemoryManagerTest.cpp ----------------===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h"#include "llvm/ADT/DenseMap.h"#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/Memory.h"#include "llvm/Testing/Support/Error.h"#include <limits>#include <vector>using namespace llvm;using namespace llvm::orc;using namespace llvm::orc::shared;namespace {class SimpleAllocator {public:Expected<ExecutorAddr> reserve(uint64_t Size) {std::error_code EC;auto MB = sys::Memory::allocateMappedMemory(Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);if (EC)return errorCodeToError(EC);Blocks[MB.base()] = sys::OwningMemoryBlock(std::move(MB));return ExecutorAddr::fromPtr(MB.base());}Error finalize(tpctypes::FinalizeRequest FR) {for (auto &Seg : FR.Segments) {char *Mem = Seg.Addr.toPtr<char *>();memcpy(Mem, Seg.Content.data(), Seg.Content.size());memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size());assert(Seg.Size <= std::numeric_limits<size_t>::max());if (auto EC = sys::Memory::protectMappedMemory({Mem, static_cast<size_t>(Seg.Size)},tpctypes::fromWireProtectionFlags(Seg.Prot)))return errorCodeToError(EC);if (Seg.Prot & tpctypes::WPF_Exec)sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);}return Error::success();}Error deallocate(std::vector<ExecutorAddr> &Bases) {Error Err = Error::success();for (auto &Base : Bases) {auto I = Blocks.find(Base.toPtr<void *>());if (I == Blocks.end()) {Err = joinErrors(std::move(Err),make_error<StringError>("No allocation for " +formatv("{0:x}", Base.getValue()),inconvertibleErrorCode()));continue;}auto MB = std::move(I->second);Blocks.erase(I);if (auto EC = MB.release())Err = joinErrors(std::move(Err), errorCodeToError(EC));}return Err;}private:DenseMap<void *, sys::OwningMemoryBlock> Blocks;};llvm::orc::shared::CWrapperFunctionResult testReserve(const char *ArgData,size_t ArgSize) {return WrapperFunction<rt::SPSSimpleExecutorMemoryManagerReserveSignature>::handle(ArgData, ArgSize,makeMethodWrapperHandler(&SimpleAllocator::reserve)).release();}llvm::orc::shared::CWrapperFunctionResult testFinalize(const char *ArgData,size_t ArgSize) {return WrapperFunction<rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>::handle(ArgData, ArgSize,makeMethodWrapperHandler(&SimpleAllocator::finalize)).release();}llvm::orc::shared::CWrapperFunctionResult testDeallocate(const char *ArgData,size_t ArgSize) {return WrapperFunction<rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>::handle(ArgData, ArgSize,makeMethodWrapperHandler(&SimpleAllocator::deallocate)).release();}TEST(EPCGenericJITLinkMemoryManagerTest, AllocFinalizeFree) {auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());SimpleAllocator SA;EPCGenericJITLinkMemoryManager::SymbolAddrs SAs;SAs.Allocator = ExecutorAddr::fromPtr(&SA);SAs.Reserve = ExecutorAddr::fromPtr(&testReserve);SAs.Finalize = ExecutorAddr::fromPtr(&testFinalize);SAs.Deallocate = ExecutorAddr::fromPtr(&testDeallocate);auto MemMgr = std::make_unique<EPCGenericJITLinkMemoryManager>(*SelfEPC, SAs);StringRef Hello = "hello";auto SSA = jitlink::SimpleSegmentAlloc::Create(*MemMgr, nullptr, {{jitlink::MemProt::Read, {Hello.size(), Align(1)}}});EXPECT_THAT_EXPECTED(SSA, Succeeded());auto SegInfo = SSA->getSegInfo(jitlink::MemProt::Read);memcpy(SegInfo.WorkingMem.data(), Hello.data(), Hello.size());auto FA = SSA->finalize();EXPECT_THAT_EXPECTED(FA, Succeeded());ExecutorAddr TargetAddr(SegInfo.Addr);const char *TargetMem = TargetAddr.toPtr<const char *>();EXPECT_NE(TargetMem, SegInfo.WorkingMem.data());StringRef TargetHello(TargetMem, Hello.size());EXPECT_EQ(Hello, TargetHello);auto Err2 = MemMgr->deallocate(std::move(*FA));EXPECT_THAT_ERROR(std::move(Err2), Succeeded());cantFail(SelfEPC->disconnect());}} // namespace
//===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===////// 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////===----------------------------------------------------------------------===//#include "OrcTestCommon.h"#include "llvm/ADT/ScopeExit.h"#include "llvm/Config/llvm-config.h"#include "llvm/ExecutionEngine/Orc/Core.h"#include "llvm/ExecutionEngine/Orc/Shared/OrcError.h"#include "llvm/Testing/Support/Error.h"#include <set>#include <thread>using namespace llvm;using namespace llvm::orc;class CoreAPIsStandardTest : public CoreAPIsBasedStandardTest {};namespace {TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {bool OnCompletionRun = false;auto OnCompletion = [&](Expected<SymbolMap> Result) {EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";auto &Resolved = *Result;auto I = Resolved.find(Foo);EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition";EXPECT_EQ(I->second.getAddress(), FooAddr)<< "Resolution returned incorrect result";OnCompletionRun = true;};std::unique_ptr<MaterializationResponsibility> FooMR;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooMR = std::move(R);})));ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Ready, OnCompletion,NoDependenciesToRegister);EXPECT_FALSE(OnCompletionRun) << "Should not have been resolved yet";cantFail(FooMR->notifyResolved({{Foo, FooSym}}));EXPECT_FALSE(OnCompletionRun) << "Should not be ready yet";cantFail(FooMR->notifyEmitted());EXPECT_TRUE(OnCompletionRun) << "Should have been marked ready";}TEST_F(CoreAPIsStandardTest, EmptyLookup) {bool OnCompletionRun = false;auto OnCompletion = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));OnCompletionRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(),SymbolState::Ready, OnCompletion, NoDependenciesToRegister);EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run for empty query";}TEST_F(CoreAPIsStandardTest, ResolveUnrequestedSymbol) {// Test that all symbols in a MaterializationUnit materialize corretly when// only a subset of symbols is looked up.// The aim here is to ensure that we're not relying on the query to set up// state needed to materialize the unrequested symbols.cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),[this](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->notifyResolved({{Foo, FooSym}, {Bar, BarSym}}));cantFail(R->notifyEmitted());})));auto Result =cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo})));EXPECT_EQ(Result.size(), 1U) << "Unexpected number of results";EXPECT_TRUE(Result.count(Foo)) << "Expected result for \"Foo\"";}TEST_F(CoreAPIsStandardTest, MaterializationSideEffctsOnlyBasic) {// Test that basic materialization-side-effects-only symbols work as expected:// that they can be emitted without being resolved, that queries for them// don't return until they're emitted, and that they don't appear in query// results.std::unique_ptr<MaterializationResponsibility> FooR;Optional<SymbolMap> Result;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, JITSymbolFlags::Exported |JITSymbolFlags::MaterializationSideEffectsOnly}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);})));ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo, SymbolLookupFlags::WeaklyReferencedSymbol),SymbolState::Ready,[&](Expected<SymbolMap> LookupResult) {if (LookupResult)Result = std::move(*LookupResult);elseADD_FAILURE() << "Unexpected lookup error: "<< toString(LookupResult.takeError());},NoDependenciesToRegister);EXPECT_FALSE(Result) << "Lookup returned unexpectedly";EXPECT_TRUE(FooR) << "Lookup failed to trigger materialization";EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())<< "Emission of materialization-side-effects-only symbol failed";EXPECT_TRUE(Result) << "Lookup failed to return";EXPECT_TRUE(Result->empty()) << "Lookup result contained unexpected value";}TEST_F(CoreAPIsStandardTest, MaterializationSideEffectsOnlyFailuresPersist) {// Test that when a MaterializationSideEffectsOnly symbol is failed it// remains in the failure state rather than vanishing.cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, JITSymbolFlags::Exported |JITSymbolFlags::MaterializationSideEffectsOnly}}),[&](std::unique_ptr<MaterializationResponsibility> R) {R->failMaterialization();})));EXPECT_THAT_EXPECTED(ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo})),Failed());EXPECT_THAT_EXPECTED(ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo})),Failed());}TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) {// Test that:// (1) Missing symbols generate a SymbolsNotFound error.// (2) Materializing symbols generate a SymbolCouldNotBeRemoved error.// (3) Removal of unmaterialized symbols triggers discard on the// materialization unit.// (4) Removal of symbols destroys empty materialization units.// (5) Removal of materialized symbols works.// Foo will be fully materialized.cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));// Bar will be unmaterialized.bool BarDiscarded = false;bool BarMaterializerDestructed = false;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[this](std::unique_ptr<MaterializationResponsibility> R) {ADD_FAILURE() << "Unexpected materialization of \"Bar\"";cantFail(R->notifyResolved({{Bar, BarSym}}));cantFail(R->notifyEmitted());},nullptr,[&](const JITDylib &JD, const SymbolStringPtr &Name) {EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded";if (Name == Bar)BarDiscarded = true;},[&]() { BarMaterializerDestructed = true; })));// Baz will be in the materializing state initially, then// materialized for the final removal attempt.std::unique_ptr<MaterializationResponsibility> BazR;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Baz, BazSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BazR = std::move(R);},nullptr,[](const JITDylib &JD, const SymbolStringPtr &Name) {ADD_FAILURE() << "\"Baz\" discarded unexpectedly";})));bool OnCompletionRun = false;ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet({Foo, Baz}), SymbolState::Ready,[&](Expected<SymbolMap> Result) {cantFail(Result.takeError());OnCompletionRun = true;},NoDependenciesToRegister);{// Attempt 1: Search for a missing symbol, Qux.auto Err = JD.remove({Foo, Bar, Baz, Qux});EXPECT_TRUE(!!Err) << "Expected failure";EXPECT_TRUE(Err.isA<SymbolsNotFound>())<< "Expected a SymbolsNotFound error";consumeError(std::move(Err));}{// Attempt 2: Search for a symbol that is still materializing, Baz.auto Err = JD.remove({Foo, Bar, Baz});EXPECT_TRUE(!!Err) << "Expected failure";EXPECT_TRUE(Err.isA<SymbolsCouldNotBeRemoved>())<< "Expected a SymbolsNotFound error";consumeError(std::move(Err));}cantFail(BazR->notifyResolved({{Baz, BazSym}}));cantFail(BazR->notifyEmitted());{// Attempt 3: Search now that all symbols are fully materialized// (Foo, Baz), or not yet materialized (Bar).auto Err = JD.remove({Foo, Bar, Baz});EXPECT_FALSE(!!Err) << "Expected success";}EXPECT_TRUE(BarDiscarded) << "\"Bar\" should have been discarded";EXPECT_TRUE(BarMaterializerDestructed)<< "\"Bar\"'s materializer should have been destructed";EXPECT_TRUE(OnCompletionRun) << "OnCompletion should have been run";}TEST_F(CoreAPIsStandardTest, LookupWithHiddenSymbols) {auto BarHiddenFlags = BarSym.getFlags() & ~JITSymbolFlags::Exported;auto BarHiddenSym = JITEvaluatedSymbol(BarSym.getAddress(), BarHiddenFlags);cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarHiddenSym}})));auto &JD2 = ES.createBareJITDylib("JD2");cantFail(JD2.define(absoluteSymbols({{Bar, QuxSym}})));/// Try a blocking lookup.auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder({&JD, &JD2}),SymbolLookupSet({Foo, Bar})));EXPECT_EQ(Result.size(), 2U) << "Unexpected number of results";EXPECT_EQ(Result.count(Foo), 1U) << "Missing result for \"Foo\"";EXPECT_EQ(Result.count(Bar), 1U) << "Missing result for \"Bar\"";EXPECT_EQ(Result[Bar].getAddress(), QuxSym.getAddress())<< "Wrong result for \"Bar\"";}TEST_F(CoreAPIsStandardTest, LookupFlagsTest) {// Test that lookupFlags works on a predefined symbol, and does not trigger// materialization of a lazy symbol. Make the lazy symbol weak to test that// the weak flag is propagated correctly.BarSym.setFlags(static_cast<JITSymbolFlags::FlagNames>(JITSymbolFlags::Exported | JITSymbolFlags::Weak));auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[](std::unique_ptr<MaterializationResponsibility> R) {llvm_unreachable("Symbol materialized on flags lookup");});cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));cantFail(JD.define(std::move(MU)));auto SymbolFlags = cantFail(ES.lookupFlags(LookupKind::Static,{{&JD, JITDylibLookupFlags::MatchExportedSymbolsOnly}},SymbolLookupSet({Foo, Bar, Baz},SymbolLookupFlags::WeaklyReferencedSymbol)));EXPECT_EQ(SymbolFlags.size(), 2U)<< "Returned symbol flags contains unexpected results";EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo";EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags())<< "Incorrect flags returned for Foo";EXPECT_EQ(SymbolFlags.count(Bar), 1U)<< "Missing lookupFlags result for Bar";EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags())<< "Incorrect flags returned for Bar";}TEST_F(CoreAPIsStandardTest, LookupWithGeneratorFailure) {class BadGenerator : public DefinitionGenerator {public:Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &,JITDylibLookupFlags, const SymbolLookupSet &) override {return make_error<StringError>("BadGenerator", inconvertibleErrorCode());}};JD.addGenerator(std::make_unique<BadGenerator>());EXPECT_THAT_ERROR(ES.lookupFlags(LookupKind::Static,{{&JD, JITDylibLookupFlags::MatchExportedSymbolsOnly}},SymbolLookupSet(Foo)).takeError(),Failed<StringError>())<< "Generator failure did not propagate through lookupFlags";EXPECT_THAT_ERROR(ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo)).takeError(),Failed<StringError>())<< "Generator failure did not propagate through lookup";}TEST_F(CoreAPIsStandardTest, TestBasicAliases) {cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));cantFail(JD.define(symbolAliases({{Baz, {Foo, JITSymbolFlags::Exported}},{Qux, {Bar, JITSymbolFlags::Weak}}})));cantFail(JD.define(absoluteSymbols({{Qux, QuxSym}})));auto Result =ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Baz, Qux}));EXPECT_TRUE(!!Result) << "Unexpected lookup failure";EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\"";EXPECT_EQ(Result->count(Qux), 1U) << "No result for \"qux\"";EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress())<< "\"Baz\"'s address should match \"Foo\"'s";EXPECT_EQ((*Result)[Qux].getAddress(), QuxSym.getAddress())<< "The \"Qux\" alias should have been overriden";}TEST_F(CoreAPIsStandardTest, TestChainedAliases) {cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));cantFail(JD.define(symbolAliases({{Baz, {Bar, BazSym.getFlags()}}, {Bar, {Foo, BarSym.getFlags()}}})));auto Result =ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Bar, Baz}));EXPECT_TRUE(!!Result) << "Unexpected lookup failure";EXPECT_EQ(Result->count(Bar), 1U) << "No result for \"bar\"";EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\"";EXPECT_EQ((*Result)[Bar].getAddress(), FooSym.getAddress())<< "\"Bar\"'s address should match \"Foo\"'s";EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress())<< "\"Baz\"'s address should match \"Foo\"'s";}TEST_F(CoreAPIsStandardTest, TestBasicReExports) {// Test that the basic use case of re-exporting a single symbol from another// JITDylib works.cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));auto &JD2 = ES.createBareJITDylib("JD2");cantFail(JD2.define(reexports(JD, {{Bar, {Foo, BarSym.getFlags()}}})));auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD2), Bar));EXPECT_EQ(Result.getAddress(), FooSym.getAddress())<< "Re-export Bar for symbol Foo should match FooSym's address";}TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) {// Test that re-exports do not materialize symbols that have not been queried// for.cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));bool BarMaterialized = false;auto BarMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BarMaterialized = true;cantFail(R->notifyResolved({{Bar, BarSym}}));cantFail(R->notifyEmitted());});cantFail(JD.define(BarMU));auto &JD2 = ES.createBareJITDylib("JD2");cantFail(JD2.define(reexports(JD, {{Baz, {Foo, BazSym.getFlags()}}, {Qux, {Bar, QuxSym.getFlags()}}})));auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD2), Baz));EXPECT_EQ(Result.getAddress(), FooSym.getAddress())<< "Re-export Baz for symbol Foo should match FooSym's address";EXPECT_FALSE(BarMaterialized) << "Bar should not have been materialized";}TEST_F(CoreAPIsStandardTest, TestReexportsGenerator) {// Test that a re-exports generator can dynamically generate reexports.auto &JD2 = ES.createBareJITDylib("JD2");cantFail(JD2.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));auto Filter = [this](SymbolStringPtr Name) { return Name != Bar; };JD.addGenerator(std::make_unique<ReexportsGenerator>(JD2, JITDylibLookupFlags::MatchExportedSymbolsOnly, Filter));auto Flags = cantFail(ES.lookupFlags(LookupKind::Static,{{&JD, JITDylibLookupFlags::MatchExportedSymbolsOnly}},SymbolLookupSet({Foo, Bar, Baz},SymbolLookupFlags::WeaklyReferencedSymbol)));EXPECT_EQ(Flags.size(), 1U) << "Unexpected number of results";EXPECT_EQ(Flags[Foo], FooSym.getFlags()) << "Unexpected flags for Foo";auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));EXPECT_EQ(Result.getAddress(), FooSym.getAddress())<< "Incorrect reexported symbol address";}TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) {std::unique_ptr<MaterializationResponsibility> FooR;auto FooMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);});cantFail(JD.define(FooMU));bool FooReady = false;auto OnCompletion = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));FooReady = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet({Foo}), SymbolState::Ready, OnCompletion,NoDependenciesToRegister);FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())<< "No symbols marked failed, but Foo failed to resolve";EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())<< "No symbols marked failed, but Foo failed to emit";EXPECT_TRUE(FooReady)<< "Self-dependency prevented symbol from being marked ready";}TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {// Test that a circular symbol dependency between three symbols in a JITDylib// does not prevent any symbol from becoming 'ready' once all symbols are// emitted.std::unique_ptr<MaterializationResponsibility> FooR;std::unique_ptr<MaterializationResponsibility> BarR;std::unique_ptr<MaterializationResponsibility> BazR;// Create a MaterializationUnit for each symbol that moves the// MaterializationResponsibility into one of the locals above.auto FooMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);});auto BarMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BarR = std::move(R);});auto BazMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Baz, BazSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BazR = std::move(R);});// Define the symbols.cantFail(JD.define(FooMU));cantFail(JD.define(BarMU));cantFail(JD.define(BazMU));// Query each of the symbols to trigger materialization.bool FooResolved = false;bool FooReady = false;auto OnFooResolution = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));FooResolved = true;};auto OnFooReady = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));FooReady = true;};// Issue lookups for Foo. Use NoDependenciesToRegister: We're going to add// the dependencies manually below.ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Resolved,std::move(OnFooResolution), NoDependenciesToRegister);ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),NoDependenciesToRegister);bool BarResolved = false;bool BarReady = false;auto OnBarResolution = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));BarResolved = true;};auto OnBarReady = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));BarReady = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Bar), SymbolState::Resolved,std::move(OnBarResolution), NoDependenciesToRegister);ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),NoDependenciesToRegister);bool BazResolved = false;bool BazReady = false;auto OnBazResolution = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));BazResolved = true;};auto OnBazReady = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));BazReady = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Baz), SymbolState::Resolved,std::move(OnBazResolution), NoDependenciesToRegister);ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Baz), SymbolState::Ready, std::move(OnBazReady),NoDependenciesToRegister);// Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo.FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});BarR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}});BazR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});// Add self-dependencies for good measure. This tests that the implementation// of addDependencies filters these out.FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});BarR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});BazR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}});// Check that nothing has been resolved yet.EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet";EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet";EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet";// Resolve the symbols (but do not emit them).EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())<< "No symbols failed, but Foo failed to resolve";EXPECT_THAT_ERROR(BarR->notifyResolved({{Bar, BarSym}}), Succeeded())<< "No symbols failed, but Bar failed to resolve";EXPECT_THAT_ERROR(BazR->notifyResolved({{Baz, BazSym}}), Succeeded())<< "No symbols failed, but Baz failed to resolve";// Verify that the symbols have been resolved, but are not ready yet.EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now";EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now";EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now";EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet";EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet";EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet";// Emit two of the symbols.EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())<< "No symbols failed, but Foo failed to emit";EXPECT_THAT_ERROR(BarR->notifyEmitted(), Succeeded())<< "No symbols failed, but Bar failed to emit";// Verify that nothing is ready until the circular dependence is resolved.EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready";EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready";EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready";// Emit the last symbol.EXPECT_THAT_ERROR(BazR->notifyEmitted(), Succeeded())<< "No symbols failed, but Baz failed to emit";// Verify that everything becomes ready once the circular dependence resolved.EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now";EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now";EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now";}TEST_F(CoreAPIsStandardTest, FailureInDependency) {std::unique_ptr<MaterializationResponsibility> FooR;std::unique_ptr<MaterializationResponsibility> BarR;// Create a MaterializationUnit for each symbol that moves the// MaterializationResponsibility into one of the locals above.auto FooMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);});auto BarMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BarR = std::move(R);});// Define the symbols.cantFail(JD.define(FooMU));cantFail(JD.define(BarMU));bool OnFooReadyRun = false;auto OnFooReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnFooReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),NoDependenciesToRegister);bool OnBarReadyRun = false;auto OnBarReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnBarReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),NoDependenciesToRegister);// Add a dependency by Foo on Bar.FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});// Fail bar.BarR->failMaterialization();// Verify that queries on Bar failed, but queries on Foo have not yet.EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";// Check that we can still resolve Foo (even though it has been failed).EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())<< "Expected resolution for \"Foo\" to fail.";FooR->failMaterialization();// Verify that queries on Foo have now failed.EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";// Verify that subsequent lookups on Bar and Foo fail.EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())<< "Lookup on failed symbol should fail";EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())<< "Lookup on failed symbol should fail";}TEST_F(CoreAPIsStandardTest, FailureInCircularDependency) {std::unique_ptr<MaterializationResponsibility> FooR;std::unique_ptr<MaterializationResponsibility> BarR;// Create a MaterializationUnit for each symbol that moves the// MaterializationResponsibility into one of the locals above.auto FooMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);});auto BarMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BarR = std::move(R);});// Define the symbols.cantFail(JD.define(FooMU));cantFail(JD.define(BarMU));bool OnFooReadyRun = false;auto OnFooReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnFooReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),NoDependenciesToRegister);bool OnBarReadyRun = false;auto OnBarReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnBarReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),NoDependenciesToRegister);// Add a dependency by Foo on Bar and vice-versa.FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});// Fail bar.BarR->failMaterialization();// Verify that queries on Bar failed, but queries on Foo have not yet.EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";// Verify that trying to resolve Foo fails.EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())<< "Expected resolution for \"Foo\" to fail.";FooR->failMaterialization();// Verify that queries on Foo have now failed.EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";// Verify that subsequent lookups on Bar and Foo fail.EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())<< "Lookup on failed symbol should fail";EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())<< "Lookup on failed symbol should fail";}TEST_F(CoreAPIsStandardTest, AddDependencyOnFailedSymbol) {std::unique_ptr<MaterializationResponsibility> FooR;std::unique_ptr<MaterializationResponsibility> BarR;// Create a MaterializationUnit for each symbol that moves the// MaterializationResponsibility into one of the locals above.auto FooMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);});auto BarMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BarR = std::move(R);});// Define the symbols.cantFail(JD.define(FooMU));cantFail(JD.define(BarMU));bool OnFooReadyRun = false;auto OnFooReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnFooReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),NoDependenciesToRegister);bool OnBarReadyRun = false;auto OnBarReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnBarReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),NoDependenciesToRegister);// Fail bar.BarR->failMaterialization();// We expect Bar's query to fail immediately, but Foo's query not to have run// yet.EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" should not have run yet";// Add dependency of Foo on Bar.FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});// Check that we can still resolve Foo (even though it has been failed).EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())<< "Expected resolution for \"Foo\" to fail.";FooR->failMaterialization();// Foo's query should have failed before we return from addDependencies.EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";// Verify that subsequent lookups on Bar and Foo fail.EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())<< "Lookup on failed symbol should fail";EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())<< "Lookup on failed symbol should fail";}TEST_F(CoreAPIsStandardTest, FailAfterMaterialization) {std::unique_ptr<MaterializationResponsibility> FooR;std::unique_ptr<MaterializationResponsibility> BarR;// Create a MaterializationUnit for each symbol that moves the// MaterializationResponsibility into one of the locals above.auto FooMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);});auto BarMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {BarR = std::move(R);});// Define the symbols.cantFail(JD.define(FooMU));cantFail(JD.define(BarMU));bool OnFooReadyRun = false;auto OnFooReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnFooReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),NoDependenciesToRegister);bool OnBarReadyRun = false;auto OnBarReady = [&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed());OnBarReadyRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),NoDependenciesToRegister);// Add a dependency by Foo on Bar and vice-versa.FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});// Materialize Foo.EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())<< "Expected resolution for \"Foo\" to succeed.";EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())<< "Expected emission for \"Foo\" to succeed.";// Fail bar.BarR->failMaterialization();// Verify that both queries failed.EXPECT_TRUE(OnFooReadyRun) << "Query for Foo did not run";EXPECT_TRUE(OnBarReadyRun) << "Query for Bar did not run";}TEST_F(CoreAPIsStandardTest, FailMaterializerWithUnqueriedSymbols) {// Make sure that symbols with no queries aganist them still// fail correctly.bool MaterializerRun = false;auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, JITSymbolFlags::Exported}}),[&](std::unique_ptr<MaterializationResponsibility> R) {MaterializerRun = true;R->failMaterialization();});cantFail(JD.define(std::move(MU)));// Issue a query for Foo, but not bar.EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())<< "Expected lookup to fail.";// Check that the materializer (and therefore failMaterialization) ran.EXPECT_TRUE(MaterializerRun) << "Expected materializer to have run by now";// Check that subsequent queries against both symbols fail.EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())<< "Expected lookup for Foo to fail.";EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())<< "Expected lookup for Bar to fail.";}TEST_F(CoreAPIsStandardTest, DropMaterializerWhenEmpty) {bool DestructorRun = false;JITSymbolFlags WeakExported(JITSymbolFlags::Exported);WeakExported |= JITSymbolFlags::Weak;auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, WeakExported}, {Bar, WeakExported}}),[](std::unique_ptr<MaterializationResponsibility> R) {llvm_unreachable("Unexpected call to materialize");},nullptr,[&](const JITDylib &JD, SymbolStringPtr Name) {EXPECT_TRUE(Name == Foo || Name == Bar)<< "Discard of unexpected symbol?";},[&]() { DestructorRun = true; });cantFail(JD.define(MU));cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));EXPECT_FALSE(DestructorRun)<< "MaterializationUnit should not have been destroyed yet";cantFail(JD.define(absoluteSymbols({{Bar, BarSym}})));EXPECT_TRUE(DestructorRun)<< "MaterializationUnit should have been destroyed";}TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) {bool FooMaterialized = false;bool BarDiscarded = false;JITSymbolFlags WeakExported(JITSymbolFlags::Exported);WeakExported |= JITSymbolFlags::Weak;auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}),[&](std::unique_ptr<MaterializationResponsibility> R) {assert(BarDiscarded && "Bar should have been discarded by this point");cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}})));cantFail(R->notifyEmitted());FooMaterialized = true;},nullptr,[&](const JITDylib &JD, SymbolStringPtr Name) {EXPECT_EQ(Name, Bar) << "Expected Name to be Bar";BarDiscarded = true;});cantFail(JD.define(MU));cantFail(JD.define(absoluteSymbols({{Bar, BarSym}})));bool OnCompletionRun = false;auto OnCompletion = [&](Expected<SymbolMap> Result) {EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";auto I = Result->find(Foo);EXPECT_NE(I, Result->end()) << "Could not find symbol definition";EXPECT_EQ(I->second.getAddress(), FooSym.getAddress())<< "Resolution returned incorrect result";OnCompletionRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnCompletion),NoDependenciesToRegister);EXPECT_TRUE(FooMaterialized) << "Foo was not materialized";EXPECT_TRUE(BarDiscarded) << "Bar was not discarded";EXPECT_TRUE(OnCompletionRun) << "OnResolutionCallback was not run";}TEST_F(CoreAPIsStandardTest, TestBasicWeakSymbolMaterialization) {// Test that weak symbols are materialized correctly when we look them up.BarSym.setFlags(BarSym.getFlags() | JITSymbolFlags::Weak);bool BarMaterialized = false;auto MU1 = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));cantFail(R->notifyEmitted());BarMaterialized = true;});bool DuplicateBarDiscarded = false;auto MU2 = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {ADD_FAILURE() << "Attempt to materialize Bar from the wrong unit";R->failMaterialization();},nullptr,[&](const JITDylib &JD, SymbolStringPtr Name) {EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded";DuplicateBarDiscarded = true;});cantFail(JD.define(MU1));cantFail(JD.define(MU2));bool OnCompletionRun = false;auto OnCompletion = [&](Expected<SymbolMap> Result) {cantFail(std::move(Result));OnCompletionRun = true;};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnCompletion),NoDependenciesToRegister);EXPECT_TRUE(OnCompletionRun) << "OnCompletion not run";EXPECT_TRUE(BarMaterialized) << "Bar was not materialized at all";EXPECT_TRUE(DuplicateBarDiscarded)<< "Duplicate bar definition not discarded";}TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) {bool ExpectNoMoreMaterialization = false;ES.setDispatchTask([&](std::unique_ptr<Task> T) {if (ExpectNoMoreMaterialization && isa<MaterializationTask>(*T))ADD_FAILURE() << "Unexpected materialization";T->run();});auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}})));cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));cantFail(R->notifyEmitted());});cantFail(JD.define(MU));cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));// Assert that materialization is complete by now.ExpectNoMoreMaterialization = true;// Look up bar to verify that no further materialization happens.auto BarResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Bar));EXPECT_EQ(BarResult.getAddress(), BarSym.getAddress())<< "Expected Bar == BarSym";}TEST_F(CoreAPIsStandardTest, GeneratorTest) {JITEvaluatedSymbol BazHiddenSym(BazSym.getAddress(), BazSym.getFlags() & ~JITSymbolFlags::Exported);cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Baz, BazHiddenSym}})));class TestGenerator : public DefinitionGenerator {public:TestGenerator(SymbolMap Symbols) : Symbols(std::move(Symbols)) {}Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,JITDylibLookupFlags JDLookupFlags,const SymbolLookupSet &Names) override {SymbolMap NewDefs;for (const auto &KV : Names) {const auto &Name = KV.first;if (Symbols.count(Name))NewDefs[Name] = Symbols[Name];}cantFail(JD.define(absoluteSymbols(std::move(NewDefs))));return Error::success();};private:SymbolMap Symbols;};JD.addGenerator(std::make_unique<TestGenerator>(SymbolMap({{Bar, BarSym}, {Baz, BazSym}})));auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD),SymbolLookupSet({Foo, Bar}).add(Baz, SymbolLookupFlags::WeaklyReferencedSymbol)));EXPECT_EQ(Result.count(Bar), 1U) << "Expected to find fallback def for 'bar'";EXPECT_EQ(Result[Bar].getAddress(), BarSym.getAddress())<< "Expected fallback def for Bar to be equal to BarSym";}TEST_F(CoreAPIsStandardTest, AsynchronousGeneratorTest) {class TestGenerator : public DefinitionGenerator {public:TestGenerator(LookupState &TLS) : TLS(TLS) {}Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,JITDylibLookupFlags JDLookupFlags,const SymbolLookupSet &Name) override {TLS = std::move(LS);return Error::success();}private:LookupState &TLS;};LookupState LS;JD.addGenerator(std::make_unique<TestGenerator>(LS));bool LookupCompleted = false;ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo),SymbolState::Ready,[&](Expected<SymbolMap> Result) {LookupCompleted = true;if (!Result) {ADD_FAILURE() << "Lookup failed unexpected";logAllUnhandledErrors(Result.takeError(), errs(), "");return;}EXPECT_EQ(Result->size(), 1U) << "Unexpected number of results";EXPECT_EQ(Result->count(Foo), 1U) << "Expected result for Foo";EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress())<< "Bad result for Foo";},NoDependenciesToRegister);EXPECT_FALSE(LookupCompleted);cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));LS.continueLookup(Error::success());EXPECT_TRUE(LookupCompleted);}TEST_F(CoreAPIsStandardTest, FailResolution) {auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, JITSymbolFlags::Exported | JITSymbolFlags::Weak},{Bar, JITSymbolFlags::Exported | JITSymbolFlags::Weak}}),[&](std::unique_ptr<MaterializationResponsibility> R) {R->failMaterialization();});cantFail(JD.define(MU));SymbolNameSet Names({Foo, Bar});auto Result = ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet(Names));EXPECT_FALSE(!!Result) << "Expected failure";if (!Result) {handleAllErrors(Result.takeError(),[&](FailedToMaterialize &F) {EXPECT_TRUE(F.getSymbols().count(&JD))<< "Expected to fail on JITDylib JD";EXPECT_EQ(F.getSymbols().find(&JD)->second, Names)<< "Expected to fail on symbols in Names";},[](ErrorInfoBase &EIB) {std::string ErrMsg;{raw_string_ostream ErrOut(ErrMsg);EIB.log(ErrOut);}ADD_FAILURE() << "Expected a FailedToResolve error. Got:\n" << ErrMsg;});}}TEST_F(CoreAPIsStandardTest, FailEmissionAfterResolution) {cantFail(JD.define(absoluteSymbols({{Baz, BazSym}})));auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet({Baz}), SymbolState::Resolved,[&](Expected<SymbolMap> Result) {// Called when "baz" is resolved. We don't actually depend// on or care about baz, but use it to trigger failure of// this materialization before Baz has been finalized in// order to test that error propagation is correct in this// scenario.cantFail(std::move(Result));R->failMaterialization();},[&](const SymbolDependenceMap &Deps) {R->addDependenciesForAll(Deps);});});cantFail(JD.define(MU));auto Result =ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo, Bar}));EXPECT_THAT_EXPECTED(std::move(Result), Failed())<< "Unexpected success while trying to test error propagation";}TEST_F(CoreAPIsStandardTest, FailAfterPartialResolution) {cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));// Fail materialization of bar.auto BarMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {R->failMaterialization();});cantFail(JD.define(std::move(BarMU)));bool QueryHandlerRun = false;ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet({Foo, Bar}), SymbolState::Resolved,[&](Expected<SymbolMap> Result) {EXPECT_THAT_EXPECTED(std::move(Result), Failed())<< "Expected query to fail";QueryHandlerRun = true;},NoDependenciesToRegister);EXPECT_TRUE(QueryHandlerRun) << "Query handler never ran";}TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) {auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),[&](std::unique_ptr<MaterializationResponsibility> R) {cantFail(R->notifyResolved({{Foo, FooSym}}));cantFail(R->notifyEmitted());});cantFail(JD.define(MU));auto FooLookupResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())<< "lookup returned an incorrect address";EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())<< "lookup returned incorrect flags";}TEST_F(CoreAPIsStandardTest, TestLookupWithThreadedMaterialization) {#if LLVM_ENABLE_THREADSstd::mutex WorkThreadsMutex;std::vector<std::thread> WorkThreads;ES.setDispatchTask([&](std::unique_ptr<Task> T) {std::promise<void> WaitP;std::lock_guard<std::mutex> Lock(WorkThreadsMutex);WorkThreads.push_back(std::thread([T = std::move(T), WaitF = WaitP.get_future()]() mutable {WaitF.get();T->run();}));WaitP.set_value();});cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));auto FooLookupResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())<< "lookup returned an incorrect address";EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())<< "lookup returned incorrect flags";for (auto &WT : WorkThreads)WT.join();#endif}TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) {// Test that GetRequestedSymbols returns the set of symbols that currently// have pending queries, and test that MaterializationResponsibility's// replace method can be used to return definitions to the JITDylib in a new// MaterializationUnit.SymbolNameSet Names({Foo, Bar});bool FooMaterialized = false;bool BarMaterialized = false;auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {auto Requested = R->getRequestedSymbols();EXPECT_EQ(Requested.size(), 1U) << "Expected one symbol requested";EXPECT_EQ(*Requested.begin(), Foo) << "Expected \"Foo\" requested";auto NewMU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R2) {cantFail(R2->notifyResolved(SymbolMap({{Bar, BarSym}})));cantFail(R2->notifyEmitted());BarMaterialized = true;});cantFail(R->replace(std::move(NewMU)));cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}})));cantFail(R->notifyEmitted());FooMaterialized = true;});cantFail(JD.define(MU));EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet";EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet";auto FooSymResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));EXPECT_EQ(FooSymResult.getAddress(), FooSym.getAddress())<< "Address mismatch for Foo";EXPECT_TRUE(FooMaterialized) << "Foo should be materialized now";EXPECT_FALSE(BarMaterialized) << "Bar still should not be materialized";auto BarSymResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Bar));EXPECT_EQ(BarSymResult.getAddress(), BarSym.getAddress())<< "Address mismatch for Bar";EXPECT_TRUE(BarMaterialized) << "Bar should be materialized now";}TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) {auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {auto R2 = cantFail(R->delegate({Bar}));cantFail(R->notifyResolved({{Foo, FooSym}}));cantFail(R->notifyEmitted());cantFail(R2->notifyResolved({{Bar, BarSym}}));cantFail(R2->notifyEmitted());});cantFail(JD.define(MU));auto Result =ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo, Bar}));EXPECT_TRUE(!!Result) << "Result should be a success value";EXPECT_EQ(Result->count(Foo), 1U) << "\"Foo\" entry missing";EXPECT_EQ(Result->count(Bar), 1U) << "\"Bar\" entry missing";EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress())<< "Address mismatch for \"Foo\"";EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress())<< "Address mismatch for \"Bar\"";}TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) {// Confirm that once a weak definition is selected for materialization it is// treated as strong.JITSymbolFlags WeakExported = JITSymbolFlags::Exported;WeakExported &= JITSymbolFlags::Weak;std::unique_ptr<MaterializationResponsibility> FooR;auto MU = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> R) {FooR = std::move(R);});cantFail(JD.define(MU));auto OnCompletion = [](Expected<SymbolMap> Result) {cantFail(std::move(Result));};ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),SymbolLookupSet({Foo}), SymbolState::Ready, std::move(OnCompletion),NoDependenciesToRegister);auto MU2 = std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),[](std::unique_ptr<MaterializationResponsibility> R) {llvm_unreachable("This unit should never be materialized");});auto Err = JD.define(MU2);EXPECT_TRUE(!!Err) << "Expected failure value";EXPECT_TRUE(Err.isA<DuplicateDefinition>())<< "Expected a duplicate definition error";consumeError(std::move(Err));// No dependencies registered, can't fail:cantFail(FooR->notifyResolved(SymbolMap({{Foo, FooSym}})));cantFail(FooR->notifyEmitted());}static bool linkOrdersEqual(const std::vector<JITDylibSP> &LHS,ArrayRef<JITDylib *> RHS) {if (LHS.size() != RHS.size())return false;auto *RHSE = RHS.begin();for (auto &LHSE : LHS)if (LHSE.get() != *RHSE)return false;else++RHSE;return true;}TEST(JITDylibTest, GetDFSLinkOrderTree) {// Test that DFS ordering behaves as expected when the linkage relationships// form a tree.ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>()};auto _ = make_scope_exit([&]() { cantFail(ES.endSession()); });auto &LibA = ES.createBareJITDylib("A");auto &LibB = ES.createBareJITDylib("B");auto &LibC = ES.createBareJITDylib("C");auto &LibD = ES.createBareJITDylib("D");auto &LibE = ES.createBareJITDylib("E");auto &LibF = ES.createBareJITDylib("F");// Linkage relationships:// A --- B -- D// \ \- E// \- C -- FLibA.setLinkOrder(makeJITDylibSearchOrder({&LibB, &LibC}));LibB.setLinkOrder(makeJITDylibSearchOrder({&LibD, &LibE}));LibC.setLinkOrder(makeJITDylibSearchOrder({&LibF}));auto DFSOrderFromB = cantFail(JITDylib::getDFSLinkOrder({&LibB}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromB, {&LibB, &LibD, &LibE}))<< "Incorrect DFS link order for LibB";auto DFSOrderFromA = cantFail(JITDylib::getDFSLinkOrder({&LibA}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromA,{&LibA, &LibB, &LibD, &LibE, &LibC, &LibF}))<< "Incorrect DFS link order for libA";auto DFSOrderFromAB = cantFail(JITDylib::getDFSLinkOrder({&LibA, &LibB}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromAB,{&LibA, &LibB, &LibD, &LibE, &LibC, &LibF}))<< "Incorrect DFS link order for { libA, libB }";auto DFSOrderFromBA = cantFail(JITDylib::getDFSLinkOrder({&LibB, &LibA}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromBA,{&LibB, &LibD, &LibE, &LibA, &LibC, &LibF}))<< "Incorrect DFS link order for { libB, libA }";}TEST(JITDylibTest, GetDFSLinkOrderDiamond) {// Test that DFS ordering behaves as expected when the linkage relationships// contain a diamond.ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>()};auto _ = make_scope_exit([&]() { cantFail(ES.endSession()); });auto &LibA = ES.createBareJITDylib("A");auto &LibB = ES.createBareJITDylib("B");auto &LibC = ES.createBareJITDylib("C");auto &LibD = ES.createBareJITDylib("D");// Linkage relationships:// A -- B --- D// \-- C --/LibA.setLinkOrder(makeJITDylibSearchOrder({&LibB, &LibC}));LibB.setLinkOrder(makeJITDylibSearchOrder({&LibD}));LibC.setLinkOrder(makeJITDylibSearchOrder({&LibD}));auto DFSOrderFromA = cantFail(JITDylib::getDFSLinkOrder({&LibA}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromA, {&LibA, &LibB, &LibD, &LibC}))<< "Incorrect DFS link order for libA";}TEST(JITDylibTest, GetDFSLinkOrderCycle) {// Test that DFS ordering behaves as expected when the linkage relationships// contain a cycle.ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>()};auto _ = make_scope_exit([&]() { cantFail(ES.endSession()); });auto &LibA = ES.createBareJITDylib("A");auto &LibB = ES.createBareJITDylib("B");auto &LibC = ES.createBareJITDylib("C");// Linkage relationships:// A -- B --- C -- ALibA.setLinkOrder(makeJITDylibSearchOrder({&LibB}));LibB.setLinkOrder(makeJITDylibSearchOrder({&LibC}));LibC.setLinkOrder(makeJITDylibSearchOrder({&LibA}));auto DFSOrderFromA = cantFail(JITDylib::getDFSLinkOrder({&LibA}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromA, {&LibA, &LibB, &LibC}))<< "Incorrect DFS link order for libA";auto DFSOrderFromB = cantFail(JITDylib::getDFSLinkOrder({&LibB}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromB, {&LibB, &LibC, &LibA}))<< "Incorrect DFS link order for libB";auto DFSOrderFromC = cantFail(JITDylib::getDFSLinkOrder({&LibC}));EXPECT_TRUE(linkOrdersEqual(DFSOrderFromC, {&LibC, &LibA, &LibB}))<< "Incorrect DFS link order for libC";}TEST_F(CoreAPIsStandardTest, RemoveJITDylibs) {// Foo will be fully materialized.cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));// Bar should not be materialized at all.bool BarMaterializerDestroyed = false;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Bar, BarSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> MR) {llvm_unreachable("Unexpected call to materialize");},nullptr,[](const JITDylib &, SymbolStringPtr Name) {llvm_unreachable("Unexpected call to discard");},[&]() { BarMaterializerDestroyed = true; })));// Baz will be in the materializing state.std::unique_ptr<MaterializationResponsibility> BazMR;cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Baz, BazSym.getFlags()}}),[&](std::unique_ptr<MaterializationResponsibility> MR) {BazMR = std::move(MR);})));// Lookup to force materialization of Foo.cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo})));// Start a lookup to force materialization of Baz.bool BazLookupFailed = false;ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet({Baz}),SymbolState::Ready,[&](Expected<SymbolMap> Result) {if (!Result) {BazLookupFailed = true;consumeError(Result.takeError());}},NoDependenciesToRegister);// Remove the JITDylib.auto Err = ES.removeJITDylib(JD);EXPECT_THAT_ERROR(std::move(Err), Succeeded());EXPECT_TRUE(BarMaterializerDestroyed);EXPECT_TRUE(BazLookupFailed);EXPECT_THAT_ERROR(BazMR->notifyResolved({{Baz, BazSym}}), Failed());EXPECT_THAT_EXPECTED(JD.getDFSLinkOrder(), Failed());BazMR->failMaterialization();}TEST(CoreAPIsExtraTest, SessionTeardownByFailedToMaterialize) {auto RunTestCase = []() -> Error {ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>(std::make_shared<SymbolStringPool>())};auto Foo = ES.intern("foo");auto FooFlags = JITSymbolFlags::Exported;auto &JD = ES.createBareJITDylib("Foo");cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(SymbolFlagsMap({{Foo, FooFlags}}),[&](std::unique_ptr<MaterializationResponsibility> R) {R->failMaterialization();})));auto Sym = ES.lookup({&JD}, Foo);assert(!Sym && "Query should have failed");cantFail(ES.endSession());return Sym.takeError();};auto Err = RunTestCase();EXPECT_TRUE(!!Err); // Expect that error occurred.EXPECT_TRUE(Err.isA<FailedToMaterialize>()); // Expect FailedToMaterialize error.// Make sure that we can log errors, even though the session has been// destroyed.logAllUnhandledErrors(std::move(Err), nulls(), "");}} // namespace
set(LLVM_LINK_COMPONENTSCoreExecutionEngineIRReaderJITLinkObjectOrcJITOrcSharedOrcTargetProcessPassesRuntimeDyldSupportnative)add_llvm_unittest(OrcJITTestsCoreAPIsTest.cppExecutorAddressTest.cppExecutionSessionWrapperFunctionCallsTest.cppEPCGenericJITLinkMemoryManagerTest.cppEPCGenericMemoryAccessTest.cppIndirectionUtilsTest.cppJITTargetMachineBuilderTest.cppLazyCallThroughAndReexportsTest.cppLookupAndRecordAddrsTest.cppMapperJITLinkMemoryManagerTest.cppMemoryMapperTest.cppObjectLinkingLayerTest.cppOrcCAPITest.cppOrcTestCommon.cppResourceTrackerTest.cppRTDyldObjectLinkingLayerTest.cppSharedMemoryMapperTest.cppSimpleExecutorMemoryManagerTest.cppSimplePackedSerializationTest.cppSymbolStringPoolTest.cppTaskDispatchTest.cppThreadSafeModuleTest.cppWrapperFunctionUtilsTest.cpp)target_link_libraries(OrcJITTests PRIVATELLVMTestingSupport${ORC_JIT_TEST_LIBS})set_property(TARGET OrcJITTests PROPERTY FOLDER "Tests/UnitTests/ExecutionTests")
EXPORTS
//===- MCJITTestBase.h - Common base class for MCJIT Unit tests -*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// This class implements common functionality required by the MCJIT unit tests,// as well as logic to skip tests on unsupported architectures and operating// systems.////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H#define LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H#include "MCJITTestAPICommon.h"#include "llvm/Config/config.h"#include "llvm/ExecutionEngine/ExecutionEngine.h"#include "llvm/ExecutionEngine/SectionMemoryManager.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Type.h"#include "llvm/Support/CodeGen.h"namespace llvm {/// Helper class that can build very simple Modulesclass TrivialModuleBuilder {protected:LLVMContext Context;IRBuilder<> Builder;std::string BuilderTriple;TrivialModuleBuilder(const std::string &Triple): Builder(Context), BuilderTriple(Triple) {}Module *createEmptyModule(StringRef Name = StringRef()) {Module * M = new Module(Name, Context);M->setTargetTriple(Triple::normalize(BuilderTriple));return M;}Function *startFunction(Module *M, FunctionType *FT, StringRef Name) {Function *Result =Function::Create(FT, GlobalValue::ExternalLinkage, Name, M);BasicBlock *BB = BasicBlock::Create(Context, Name, Result);Builder.SetInsertPoint(BB);return Result;}void endFunctionWithRet(Function *Func, Value *RetValue) {Builder.CreateRet(RetValue);}// Inserts a simple function that invokes Callee and takes the same arguments:// int Caller(...) { return Callee(...); }Function *insertSimpleCallFunction(Module *M, Function *Callee) {Function *Result = startFunction(M, Callee->getFunctionType(), "caller");SmallVector<Value*, 1> CallArgs;for (Argument &A : Result->args())CallArgs.push_back(&A);Value *ReturnCode = Builder.CreateCall(Callee, CallArgs);Builder.CreateRet(ReturnCode);return Result;}// Inserts a function named 'main' that returns a uint32_t:// int32_t main() { return X; }// where X is given by returnCodeFunction *insertMainFunction(Module *M, uint32_t returnCode) {Function *Result = startFunction(M, FunctionType::get(Type::getInt32Ty(Context), {}, false), "main");Value *ReturnVal = ConstantInt::get(Context, APInt(32, returnCode));endFunctionWithRet(Result, ReturnVal);return Result;}// Inserts a function// int32_t add(int32_t a, int32_t b) { return a + b; }// in the current module and returns a pointer to it.Function *insertAddFunction(Module *M, StringRef Name = "add") {Function *Result = startFunction(M,FunctionType::get(Type::getInt32Ty(Context),{Type::getInt32Ty(Context), Type::getInt32Ty(Context)}, false),Name);Function::arg_iterator args = Result->arg_begin();Value *Arg1 = &*args;Value *Arg2 = &*++args;Value *AddResult = Builder.CreateAdd(Arg1, Arg2);endFunctionWithRet(Result, AddResult);return Result;}// Inserts a declaration to a function defined elsewhereFunction *insertExternalReferenceToFunction(Module *M, FunctionType *FTy,StringRef Name) {Function *Result =Function::Create(FTy, GlobalValue::ExternalLinkage, Name, M);return Result;}// Inserts an declaration to a function defined elsewhereFunction *insertExternalReferenceToFunction(Module *M, Function *Func) {Function *Result = Function::Create(Func->getFunctionType(),GlobalValue::ExternalLinkage,Func->getName(), M);return Result;}// Inserts a global variable of type int32// FIXME: make this a template function to support any typeGlobalVariable *insertGlobalInt32(Module *M,StringRef name,int32_t InitialValue) {Type *GlobalTy = Type::getInt32Ty(Context);Constant *IV = ConstantInt::get(Context, APInt(32, InitialValue));GlobalVariable *Global = new GlobalVariable(*M,GlobalTy,false,GlobalValue::ExternalLinkage,IV,name);return Global;}// Inserts a function// int32_t recursive_add(int32_t num) {// if (num == 0) {// return num;// } else {// int32_t recursive_param = num - 1;// return num + Helper(recursive_param);// }// }// NOTE: if Helper is left as the default parameter, Helper == recursive_add.Function *insertAccumulateFunction(Module *M,Function *Helper = nullptr,StringRef Name = "accumulate") {Function *Result =startFunction(M,FunctionType::get(Type::getInt32Ty(Context),{Type::getInt32Ty(Context)}, false),Name);if (!Helper)Helper = Result;BasicBlock *BaseCase = BasicBlock::Create(Context, "", Result);BasicBlock *RecursiveCase = BasicBlock::Create(Context, "", Result);// if (num == 0)Value *Param = &*Result->arg_begin();Value *Zero = ConstantInt::get(Context, APInt(32, 0));Builder.CreateCondBr(Builder.CreateICmpEQ(Param, Zero),BaseCase, RecursiveCase);// return num;Builder.SetInsertPoint(BaseCase);Builder.CreateRet(Param);// int32_t recursive_param = num - 1;// return Helper(recursive_param);Builder.SetInsertPoint(RecursiveCase);Value *One = ConstantInt::get(Context, APInt(32, 1));Value *RecursiveParam = Builder.CreateSub(Param, One);Value *RecursiveReturn = Builder.CreateCall(Helper, RecursiveParam);Value *Accumulator = Builder.CreateAdd(Param, RecursiveReturn);Builder.CreateRet(Accumulator);return Result;}// Populates Modules A and B:// Module A { Extern FB1, Function FA which calls FB1 },// Module B { Extern FA, Function FB1, Function FB2 which calls FA },void createCrossModuleRecursiveCase(std::unique_ptr<Module> &A, Function *&FA,std::unique_ptr<Module> &B,Function *&FB1, Function *&FB2) {// Define FB1 in B.B.reset(createEmptyModule("B"));FB1 = insertAccumulateFunction(B.get(), nullptr, "FB1");// Declare FB1 in A (as an external).A.reset(createEmptyModule("A"));Function *FB1Extern = insertExternalReferenceToFunction(A.get(), FB1);// Define FA in A (with a call to FB1).FA = insertAccumulateFunction(A.get(), FB1Extern, "FA");// Declare FA in B (as an external)Function *FAExtern = insertExternalReferenceToFunction(B.get(), FA);// Define FB2 in B (with a call to FA)FB2 = insertAccumulateFunction(B.get(), FAExtern, "FB2");}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// Module C { Extern FB, Function FC which calls FB },voidcreateThreeModuleChainedCallsCase(std::unique_ptr<Module> &A, Function *&FA,std::unique_ptr<Module> &B, Function *&FB,std::unique_ptr<Module> &C, Function *&FC) {A.reset(createEmptyModule("A"));FA = insertAddFunction(A.get());B.reset(createEmptyModule("B"));Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);FB = insertSimpleCallFunction(B.get(), FAExtern_in_B);C.reset(createEmptyModule("C"));Function *FBExtern_in_C = insertExternalReferenceToFunction(C.get(), FB);FC = insertSimpleCallFunction(C.get(), FBExtern_in_C);}// Module A { Function FA },// Populates Modules A and B:// Module B { Function FB }void createTwoModuleCase(std::unique_ptr<Module> &A, Function *&FA,std::unique_ptr<Module> &B, Function *&FB) {A.reset(createEmptyModule("A"));FA = insertAddFunction(A.get());B.reset(createEmptyModule("B"));FB = insertAddFunction(B.get());}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA }void createTwoModuleExternCase(std::unique_ptr<Module> &A, Function *&FA,std::unique_ptr<Module> &B, Function *&FB) {A.reset(createEmptyModule("A"));FA = insertAddFunction(A.get());B.reset(createEmptyModule("B"));Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);FB = insertSimpleCallFunction(B.get(), FAExtern_in_B);}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// Module C { Extern FB, Function FC which calls FA },void createThreeModuleCase(std::unique_ptr<Module> &A, Function *&FA,std::unique_ptr<Module> &B, Function *&FB,std::unique_ptr<Module> &C, Function *&FC) {A.reset(createEmptyModule("A"));FA = insertAddFunction(A.get());B.reset(createEmptyModule("B"));Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);FB = insertSimpleCallFunction(B.get(), FAExtern_in_B);C.reset(createEmptyModule("C"));Function *FAExtern_in_C = insertExternalReferenceToFunction(C.get(), FA);FC = insertSimpleCallFunction(C.get(), FAExtern_in_C);}};class MCJITTestBase : public MCJITTestAPICommon, public TrivialModuleBuilder {protected:MCJITTestBase(): TrivialModuleBuilder(HostTriple), OptLevel(CodeGenOpt::None),CodeModel(CodeModel::Small), MArch(""), MM(new SectionMemoryManager) {// The architectures below are known to be compatible with MCJIT as they// are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be// kept in sync.SupportedArchs.push_back(Triple::aarch64);SupportedArchs.push_back(Triple::arm);SupportedArchs.push_back(Triple::mips);SupportedArchs.push_back(Triple::mipsel);SupportedArchs.push_back(Triple::mips64);SupportedArchs.push_back(Triple::mips64el);SupportedArchs.push_back(Triple::x86);SupportedArchs.push_back(Triple::x86_64);// Some architectures have sub-architectures in which tests will fail, like// ARM. These two vectors will define if they do have sub-archs (to avoid// extra work for those who don't), and if so, if they are listed to workHasSubArchs.push_back(Triple::arm);SupportedSubArchs.push_back("armv6");SupportedSubArchs.push_back("armv7");UnsupportedEnvironments.push_back(Triple::Cygnus);}void createJIT(std::unique_ptr<Module> M) {// Due to the EngineBuilder constructor, it is required to have a Module// in order to construct an ExecutionEngine (i.e. MCJIT)assert(M != 0 && "a non-null Module must be provided to create MCJIT");EngineBuilder EB(std::move(M));std::string Error;TheJIT.reset(EB.setEngineKind(EngineKind::JIT).setMCJITMemoryManager(std::move(MM)).setErrorStr(&Error).setOptLevel(CodeGenOpt::None).setMArch(MArch).setMCPU(sys::getHostCPUName())//.setMAttrs(MAttrs).create());// At this point, we cannot modify the module any more.assert(TheJIT.get() != NULL && "error creating MCJIT with EngineBuilder");}CodeGenOpt::Level OptLevel;CodeModel::Model CodeModel;StringRef MArch;SmallVector<std::string, 1> MAttrs;std::unique_ptr<ExecutionEngine> TheJIT;std::unique_ptr<RTDyldMemoryManager> MM;std::unique_ptr<Module> M;};} // namespace llvm#endif // LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H
//===- MCJITTestBase.h - Common base class for MCJIT Unit tests ----------===////// 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////===----------------------------------------------------------------------===////// This class implements functionality shared by both MCJIT C API tests, and// the C++ API tests.////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTAPICOMMON_H#define LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTAPICOMMON_H#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/Triple.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/InitializePasses.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/PassRegistry.h"#include "llvm/Support/Host.h"#include "llvm/Support/TargetSelect.h"// Used to skip tests on unsupported architectures and operating systems.// To skip a test, add this macro at the top of a test-case in a suite that// inherits from MCJITTestBase. See MCJITTest.cpp for examples.#define SKIP_UNSUPPORTED_PLATFORM \do \if (!ArchSupportsMCJIT() || !OSSupportsMCJIT() || !HostCanBeTargeted()) \return; \while(0)namespace llvm {class MCJITTestAPICommon {protected:MCJITTestAPICommon(): HostTriple(sys::getProcessTriple()){InitializeNativeTarget();InitializeNativeTargetAsmPrinter();// FIXME: It isn't at all clear why this is necesasry, but without it we// fail to initialize the AssumptionCacheTracker.initializeAssumptionCacheTrackerPass(*PassRegistry::getPassRegistry());#ifdef _WIN32// On Windows, generate ELF objects by specifying "-elf" in tripleHostTriple += "-elf";#endif // _WIN32HostTriple = Triple::normalize(HostTriple);}bool HostCanBeTargeted() {std::string Error;return TargetRegistry::lookupTarget(HostTriple, Error) != nullptr;}/// Returns true if the host architecture is known to support MCJITbool ArchSupportsMCJIT() {Triple Host(HostTriple);// If ARCH is not supported, bailif (!is_contained(SupportedArchs, Host.getArch()))return false;// If ARCH is supported and has no specific sub-arch supportif (!is_contained(HasSubArchs, Host.getArch()))return true;// If ARCH has sub-arch support, find itSmallVectorImpl<std::string>::const_iterator I = SupportedSubArchs.begin();for(; I != SupportedSubArchs.end(); ++I)if (Host.getArchName().startswith(*I))return true;return false;}/// Returns true if the host OS is known to support MCJITbool OSSupportsMCJIT() {Triple Host(HostTriple);if (find(UnsupportedEnvironments, Host.getEnvironment()) !=UnsupportedEnvironments.end())return false;if (!is_contained(UnsupportedOSs, Host.getOS()))return true;return false;}std::string HostTriple;SmallVector<Triple::ArchType, 4> SupportedArchs;SmallVector<Triple::ArchType, 1> HasSubArchs;SmallVector<std::string, 2> SupportedSubArchs; // We need to own the memorySmallVector<Triple::OSType, 4> UnsupportedOSs;SmallVector<Triple::EnvironmentType, 1> UnsupportedEnvironments;};} // namespace llvm#endif
//===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// This test suite verifies basic MCJIT functionality such as making function// calls, using global variables, and compiling multpile modules.////===----------------------------------------------------------------------===//#include "MCJITTestBase.h"#include "llvm/Support/DynamicLibrary.h"#include "gtest/gtest.h"using namespace llvm;namespace {class MCJITTest : public testing::Test, public MCJITTestBase {protected:void SetUp() override { M.reset(createEmptyModule("<main>")); }};// FIXME: Ensure creating an execution engine does not crash when constructed// with a null module./*TEST_F(MCJITTest, null_module) {createJIT(0);}*/// FIXME: In order to JIT an empty module, there needs to be// an interface to ExecutionEngine that forces compilation but// does not require retrieval of a pointer to a function/global./*TEST_F(MCJITTest, empty_module) {createJIT(M.take());//EXPECT_NE(0, TheJIT->getObjectImage())// << "Unable to generate executable loaded object image";}*/TEST_F(MCJITTest, global_variable) {SKIP_UNSUPPORTED_PLATFORM;int initialValue = 5;GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue);createJIT(std::move(M));void *globalPtr = TheJIT->getPointerToGlobal(Global);EXPECT_TRUE(nullptr != globalPtr)<< "Unable to get pointer to global value from JIT";EXPECT_EQ(initialValue, *(int32_t*)globalPtr)<< "Unexpected initial value of global";}TEST_F(MCJITTest, add_function) {SKIP_UNSUPPORTED_PLATFORM;Function *F = insertAddFunction(M.get());createJIT(std::move(M));uint64_t addPtr = TheJIT->getFunctionAddress(F->getName().str());EXPECT_TRUE(0 != addPtr)<< "Unable to get pointer to function from JIT";ASSERT_TRUE(addPtr != 0) << "Unable to get pointer to function .";int (*AddPtr)(int, int) = (int(*)(int, int))addPtr ;EXPECT_EQ(0, AddPtr(0, 0));EXPECT_EQ(1, AddPtr(1, 0));EXPECT_EQ(3, AddPtr(1, 2));EXPECT_EQ(-5, AddPtr(-2, -3));EXPECT_EQ(30, AddPtr(10, 20));EXPECT_EQ(-30, AddPtr(-10, -20));EXPECT_EQ(-40, AddPtr(-10, -30));}TEST_F(MCJITTest, run_main) {SKIP_UNSUPPORTED_PLATFORM;int rc = 6;Function *Main = insertMainFunction(M.get(), 6);createJIT(std::move(M));uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str());EXPECT_TRUE(0 != ptr)<< "Unable to get pointer to main() from JIT";int (*FuncPtr)() = (int(*)())ptr;int returnCode = FuncPtr();EXPECT_EQ(returnCode, rc);}TEST_F(MCJITTest, return_global) {SKIP_UNSUPPORTED_PLATFORM;int32_t initialNum = 7;GlobalVariable *GV = insertGlobalInt32(M.get(), "myglob", initialNum);Function *ReturnGlobal =startFunction(M.get(), FunctionType::get(Builder.getInt32Ty(), {}, false),"ReturnGlobal");Value *ReadGlobal = Builder.CreateLoad(Builder.getInt32Ty(), GV);endFunctionWithRet(ReturnGlobal, ReadGlobal);createJIT(std::move(M));uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str());EXPECT_TRUE(0 != rgvPtr);int32_t(*FuncPtr)() = (int32_t(*)())rgvPtr;EXPECT_EQ(initialNum, FuncPtr())<< "Invalid value for global returned from JITted function";}// FIXME: This case fails due to a bug with getPointerToGlobal().// The bug is due to MCJIT not having an implementation of getPointerToGlobal()// which results in falling back on the ExecutionEngine implementation that// allocates a new memory block for the global instead of using the same// global variable that is emitted by MCJIT. Hence, the pointer (gvPtr below)// has the correct initial value, but updates to the real global (accessed by// JITted code) are not propagated. Instead, getPointerToGlobal() should return// a pointer into the loaded ObjectImage to reference the emitted global./*TEST_F(MCJITTest, increment_global) {SKIP_UNSUPPORTED_PLATFORM;int32_t initialNum = 5;Function *IncrementGlobal = startFunction(M.get(),FunctionType::get(Builder.getInt32Ty(), {}, false),"IncrementGlobal");GlobalVariable *GV = insertGlobalInt32(M.get(), "my_global", initialNum);Value *DerefGV = Builder.CreateLoad(GV);Value *AddResult = Builder.CreateAdd(DerefGV,ConstantInt::get(Context, APInt(32, 1)));Builder.CreateStore(AddResult, GV);endFunctionWithRet(IncrementGlobal, AddResult);createJIT(M.take());void *gvPtr = TheJIT->getPointerToGlobal(GV);EXPECT_EQ(initialNum, *(int32_t*)gvPtr);void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str());EXPECT_TRUE(0 != vPtr)<< "Unable to get pointer to main() from JIT";int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr;for(int i = 1; i < 3; ++i) {int32_t result = FuncPtr();EXPECT_EQ(initialNum + i, result); // OKEXPECT_EQ(initialNum + i, *(int32_t*)gvPtr); // FAILS}}*/// PR16013: XFAIL this test on ARM, which currently can't handle multiple relocations.#if !defined(__arm__)TEST_F(MCJITTest, multiple_functions) {SKIP_UNSUPPORTED_PLATFORM;unsigned int numLevels = 23;int32_t innerRetVal= 5;Function *Inner = startFunction(M.get(), FunctionType::get(Builder.getInt32Ty(), {}, false), "Inner");endFunctionWithRet(Inner, ConstantInt::get(Context, APInt(32, innerRetVal)));Function *Outer;for (unsigned int i = 0; i < numLevels; ++i) {std::stringstream funcName;funcName << "level_" << i;Outer = startFunction(M.get(),FunctionType::get(Builder.getInt32Ty(), {}, false),funcName.str());Value *innerResult = Builder.CreateCall(Inner, {});endFunctionWithRet(Outer, innerResult);Inner = Outer;}createJIT(std::move(M));uint64_t ptr = TheJIT->getFunctionAddress(Outer->getName().str());EXPECT_TRUE(0 != ptr)<< "Unable to get pointer to outer function from JIT";int32_t(*FuncPtr)() = (int32_t(*)())ptr;EXPECT_EQ(innerRetVal, FuncPtr())<< "Incorrect result returned from function";}#endif /*!defined(__arm__)*/TEST_F(MCJITTest, multiple_decl_lookups) {SKIP_UNSUPPORTED_PLATFORM;Function *Foo = insertExternalReferenceToFunction(M.get(), FunctionType::get(Builder.getVoidTy(), {}, false), "_exit");createJIT(std::move(M));void *A = TheJIT->getPointerToFunction(Foo);void *B = TheJIT->getPointerToFunction(Foo);EXPECT_TRUE(A != nullptr) << "Failed lookup - test not correctly configured.";EXPECT_EQ(A, B) << "Repeat calls to getPointerToFunction fail.";}typedef void * (*FunctionHandlerPtr)(const std::string &str);TEST_F(MCJITTest, lazy_function_creator_pointer) {SKIP_UNSUPPORTED_PLATFORM;Function *Foo = insertExternalReferenceToFunction(M.get(), FunctionType::get(Builder.getInt32Ty(), {}, false),"\1Foo");startFunction(M.get(), FunctionType::get(Builder.getInt32Ty(), {}, false),"Parent");CallInst *Call = Builder.CreateCall(Foo, {});Builder.CreateRet(Call);createJIT(std::move(M));// Set up the lazy function creator that records the name of the last// unresolved external function found in the module. Using a function pointer// prevents us from capturing local variables, which is why this is static.static std::string UnresolvedExternal;FunctionHandlerPtr UnresolvedHandler = [] (const std::string &str) {// Try to resolve the function in the current process before marking it as// unresolved. This solves an issue on ARM where '__aeabi_*' function names// are passed to this handler.void *symbol =llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str());if (symbol) {return symbol;}UnresolvedExternal = str;return (void *)(uintptr_t)-1;};TheJIT->InstallLazyFunctionCreator(UnresolvedHandler);// JIT the module.TheJIT->finalizeObject();// Verify that our handler was called.EXPECT_EQ(UnresolvedExternal, "Foo");}TEST_F(MCJITTest, lazy_function_creator_lambda) {SKIP_UNSUPPORTED_PLATFORM;FunctionType *Int32VoidFnTy =FunctionType::get(Builder.getInt32Ty(), {}, false);Function *Foo1 =insertExternalReferenceToFunction(M.get(), Int32VoidFnTy, "\1Foo1");Function *Foo2 =insertExternalReferenceToFunction(M.get(), Int32VoidFnTy, "\1Foo2");startFunction(M.get(), Int32VoidFnTy, "Parent");CallInst *Call1 = Builder.CreateCall(Foo1, {});CallInst *Call2 = Builder.CreateCall(Foo2, {});Value *Result = Builder.CreateAdd(Call1, Call2);Builder.CreateRet(Result);createJIT(std::move(M));// Set up the lazy function creator that records the name of unresolved// external functions in the module.std::vector<std::string> UnresolvedExternals;auto UnresolvedHandler = [&UnresolvedExternals] (const std::string &str) {// Try to resolve the function in the current process before marking it as// unresolved. This solves an issue on ARM where '__aeabi_*' function names// are passed to this handler.void *symbol =llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str());if (symbol) {return symbol;}UnresolvedExternals.push_back(str);return (void *)(uintptr_t)-1;};TheJIT->InstallLazyFunctionCreator(UnresolvedHandler);// JIT the module.TheJIT->finalizeObject();// Verify that our handler was called for each unresolved function.auto I = UnresolvedExternals.begin(), E = UnresolvedExternals.end();EXPECT_EQ(UnresolvedExternals.size(), 2u);EXPECT_FALSE(std::find(I, E, "Foo1") == E);EXPECT_FALSE(std::find(I, E, "Foo2") == E);}} // end anonymous namespace
//===- MCJITObjectCacheTest.cpp - Unit tests for MCJIT object caching -----===////// 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////===----------------------------------------------------------------------===//#include "MCJITTestBase.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/StringMap.h"#include "llvm/ADT/StringSet.h"#include "llvm/ExecutionEngine/MCJIT.h"#include "llvm/ExecutionEngine/ObjectCache.h"#include "llvm/ExecutionEngine/SectionMemoryManager.h"#include "llvm/Support/MemoryBuffer.h"#include "gtest/gtest.h"using namespace llvm;namespace {class TestObjectCache : public ObjectCache {public:TestObjectCache() : DuplicateInserted(false) { }void notifyObjectCompiled(const Module *M, MemoryBufferRef Obj) override {// If we've seen this module before, note that.const std::string ModuleID = M->getModuleIdentifier();if (ObjMap.find(ModuleID) != ObjMap.end())DuplicateInserted = true;// Store a copy of the buffer in our map.ObjMap[ModuleID] = copyBuffer(Obj);}std::unique_ptr<MemoryBuffer> getObject(const Module *M) override {const MemoryBuffer* BufferFound = getObjectInternal(M);ModulesLookedUp.insert(M->getModuleIdentifier());if (!BufferFound)return nullptr;// Our test cache wants to maintain ownership of its object buffers// so we make a copy here for the execution engine.return MemoryBuffer::getMemBufferCopy(BufferFound->getBuffer());}// Test-harness-specific functionsbool wereDuplicatesInserted() { return DuplicateInserted; }bool wasModuleLookedUp(const Module *M) {return ModulesLookedUp.find(M->getModuleIdentifier())!= ModulesLookedUp.end();}const MemoryBuffer* getObjectInternal(const Module* M) {// Look for the module in our map.const std::string ModuleID = M->getModuleIdentifier();StringMap<const MemoryBuffer *>::iterator it = ObjMap.find(ModuleID);if (it == ObjMap.end())return nullptr;return it->second;}private:MemoryBuffer *copyBuffer(MemoryBufferRef Buf) {// Create a local copy of the buffer.std::unique_ptr<MemoryBuffer> NewBuffer =MemoryBuffer::getMemBufferCopy(Buf.getBuffer());MemoryBuffer *Ret = NewBuffer.get();AllocatedBuffers.push_back(std::move(NewBuffer));return Ret;}StringMap<const MemoryBuffer *> ObjMap;StringSet<> ModulesLookedUp;SmallVector<std::unique_ptr<MemoryBuffer>, 2> AllocatedBuffers;bool DuplicateInserted;};class MCJITObjectCacheTest : public testing::Test, public MCJITTestBase {protected:enum {OriginalRC = 6,ReplacementRC = 7};void SetUp() override {M.reset(createEmptyModule("<main>"));Main = insertMainFunction(M.get(), OriginalRC);}void compileAndRun(int ExpectedRC = OriginalRC) {// This function shouldn't be called until after SetUp.ASSERT_TRUE(bool(TheJIT));ASSERT_TRUE(nullptr != Main);// We may be using a null cache, so ensure compilation is valid.TheJIT->finalizeObject();void *vPtr = TheJIT->getPointerToFunction(Main);EXPECT_TRUE(nullptr != vPtr)<< "Unable to get pointer to main() from JIT";int (*FuncPtr)() = (int(*)())(intptr_t)vPtr;int returnCode = FuncPtr();EXPECT_EQ(returnCode, ExpectedRC);}Function *Main;};TEST_F(MCJITObjectCacheTest, SetNullObjectCache) {SKIP_UNSUPPORTED_PLATFORM;createJIT(std::move(M));TheJIT->setObjectCache(nullptr);compileAndRun();}TEST_F(MCJITObjectCacheTest, VerifyBasicObjectCaching) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);// Save a copy of the module pointer before handing it off to MCJIT.const Module * SavedModulePointer = M.get();createJIT(std::move(M));TheJIT->setObjectCache(Cache.get());// Verify that our object cache does not contain the module yet.const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SavedModulePointer);EXPECT_EQ(nullptr, ObjBuffer);compileAndRun();// Verify that MCJIT tried to look-up this module in the cache.EXPECT_TRUE(Cache->wasModuleLookedUp(SavedModulePointer));// Verify that our object cache now contains the module.ObjBuffer = Cache->getObjectInternal(SavedModulePointer);EXPECT_TRUE(nullptr != ObjBuffer);// Verify that the cache was only notified once.EXPECT_FALSE(Cache->wereDuplicatesInserted());}TEST_F(MCJITObjectCacheTest, VerifyLoadFromCache) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);// Compile this module with an MCJIT enginecreateJIT(std::move(M));TheJIT->setObjectCache(Cache.get());TheJIT->finalizeObject();// Destroy the MCJIT engine we just usedTheJIT.reset();// Create a new memory manager.MM.reset(new SectionMemoryManager());// Create a new module and save it. Use a different return code so we can// tell if MCJIT compiled this module or used the cache.M.reset(createEmptyModule("<main>"));Main = insertMainFunction(M.get(), ReplacementRC);const Module * SecondModulePointer = M.get();// Create a new MCJIT instance to load this module then execute it.createJIT(std::move(M));TheJIT->setObjectCache(Cache.get());compileAndRun();// Verify that MCJIT tried to look-up this module in the cache.EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));// Verify that MCJIT didn't try to cache this again.EXPECT_FALSE(Cache->wereDuplicatesInserted());}TEST_F(MCJITObjectCacheTest, VerifyNonLoadFromCache) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);// Compile this module with an MCJIT enginecreateJIT(std::move(M));TheJIT->setObjectCache(Cache.get());TheJIT->finalizeObject();// Destroy the MCJIT engine we just usedTheJIT.reset();// Create a new memory manager.MM.reset(new SectionMemoryManager());// Create a new module and save it. Use a different return code so we can// tell if MCJIT compiled this module or used the cache. Note that we use// a new module name here so the module shouldn't be found in the cache.M.reset(createEmptyModule("<not-main>"));Main = insertMainFunction(M.get(), ReplacementRC);const Module * SecondModulePointer = M.get();// Create a new MCJIT instance to load this module then execute it.createJIT(std::move(M));TheJIT->setObjectCache(Cache.get());// Verify that our object cache does not contain the module yet.const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SecondModulePointer);EXPECT_EQ(nullptr, ObjBuffer);// Run the function and look for the replacement return code.compileAndRun(ReplacementRC);// Verify that MCJIT tried to look-up this module in the cache.EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));// Verify that our object cache now contains the module.ObjBuffer = Cache->getObjectInternal(SecondModulePointer);EXPECT_TRUE(nullptr != ObjBuffer);// Verify that MCJIT didn't try to cache this again.EXPECT_FALSE(Cache->wereDuplicatesInserted());}} // end anonymous namespace
//===- MCJITMultipeModuleTest.cpp - Unit tests for the MCJIT ----*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// This test suite verifies MCJIT for handling multiple modules in a single// ExecutionEngine by building multiple modules, making function calls across// modules, accessing global variables, etc.//===----------------------------------------------------------------------===//#include "MCJITTestBase.h"#include "llvm/ExecutionEngine/MCJIT.h"#include "gtest/gtest.h"using namespace llvm;namespace {class MCJITMultipleModuleTest : public testing::Test, public MCJITTestBase {};// FIXME: ExecutionEngine has no support empty modules/*TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) {SKIP_UNSUPPORTED_PLATFORM;createJIT(M.take());// JIT-compileEXPECT_NE(0, TheJIT->getObjectImage())<< "Unable to generate executable loaded object image";TheJIT->addModule(createEmptyModule("<other module>"));TheJIT->addModule(createEmptyModule("<other other module>"));// JIT againEXPECT_NE(0, TheJIT->getObjectImage())<< "Unable to generate executable loaded object image";}*/// Helper Function to test add operationvoid checkAdd(uint64_t ptr) {ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function.";int (*AddPtr)(int, int) = (int (*)(int, int))ptr;EXPECT_EQ(0, AddPtr(0, 0));EXPECT_EQ(1, AddPtr(1, 0));EXPECT_EQ(3, AddPtr(1, 2));EXPECT_EQ(-5, AddPtr(-2, -3));EXPECT_EQ(30, AddPtr(10, 20));EXPECT_EQ(-30, AddPtr(-10, -20));EXPECT_EQ(-40, AddPtr(-10, -30));}void checkAccumulate(uint64_t ptr) {ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function.";int32_t (*FPtr)(int32_t) = (int32_t (*)(int32_t))(intptr_t)ptr;EXPECT_EQ(0, FPtr(0));EXPECT_EQ(1, FPtr(1));EXPECT_EQ(3, FPtr(2));EXPECT_EQ(6, FPtr(3));EXPECT_EQ(10, FPtr(4));EXPECT_EQ(15, FPtr(5));}// FIXME: ExecutionEngine has no support empty modules/*TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) {SKIP_UNSUPPORTED_PLATFORM;createJIT(M.take());// JIT-compileEXPECT_NE(0, TheJIT->getObjectImage())<< "Unable to generate executable loaded object image";TheJIT->addModule(createEmptyModule("<other module>"));TheJIT->addModule(createEmptyModule("<other other module>"));// JIT againEXPECT_NE(0, TheJIT->getObjectImage())<< "Unable to generate executable loaded object image";}*/// Module A { Function FA },// Module B { Function FB },// execute FA then FBTEST_F(MCJITMultipleModuleTest, two_module_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB;createTwoModuleCase(A, FA, B, FB);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FB->getName().str());checkAdd(ptr);}// Module A { Function FA },// Module B { Function FB },// execute FB then FATEST_F(MCJITMultipleModuleTest, two_module_reverse_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB;createTwoModuleCase(A, FA, B, FB);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());TheJIT->finalizeObject();checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// execute FB then FATEST_F(MCJITMultipleModuleTest, two_module_extern_reverse_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB;createTwoModuleExternCase(A, FA, B, FB);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());TheJIT->finalizeObject();checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// execute FA then FBTEST_F(MCJITMultipleModuleTest, two_module_extern_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB;createTwoModuleExternCase(A, FA, B, FB);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FB->getName().str());checkAdd(ptr);}// Module A { Function FA1, Function FA2 which calls FA1 },// Module B { Extern FA1, Function FB which calls FA1 },// execute FB then FA2TEST_F(MCJITMultipleModuleTest, two_module_consecutive_call_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA1, *FA2, *FB;createTwoModuleExternCase(A, FA1, B, FB);FA2 = insertSimpleCallFunction(A.get(), FA1);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());TheJIT->finalizeObject();checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FA2->getName().str());checkAdd(ptr);}// TODO:// Module A { Extern Global GVB, Global Variable GVA, Function FA loads GVB },// Module B { Extern Global GVA, Global Variable GVB, Function FB loads GVA },// Module A { Global Variable GVA, Function FA loads GVA },// Module B { Global Variable GVB, Internal Global GVC, Function FB loads GVB },// execute FB then FA, also check that the global variables are properly accesible// through the ExecutionEngine APIsTEST_F(MCJITMultipleModuleTest, two_module_global_variables_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB;GlobalVariable *GVA, *GVB, *GVC;A.reset(createEmptyModule("A"));B.reset(createEmptyModule("B"));int32_t initialNum = 7;GVA = insertGlobalInt32(A.get(), "GVA", initialNum);GVB = insertGlobalInt32(B.get(), "GVB", initialNum);FA = startFunction(A.get(),FunctionType::get(Builder.getInt32Ty(), {}, false), "FA");endFunctionWithRet(FA, Builder.CreateLoad(Builder.getInt32Ty(), GVA));FB = startFunction(B.get(),FunctionType::get(Builder.getInt32Ty(), {}, false), "FB");endFunctionWithRet(FB, Builder.CreateLoad(Builder.getInt32Ty(), GVB));GVC = insertGlobalInt32(B.get(), "GVC", initialNum);GVC->setLinkage(GlobalValue::InternalLinkage);createJIT(std::move(A));TheJIT->addModule(std::move(B));EXPECT_EQ(GVA, TheJIT->FindGlobalVariableNamed("GVA"));EXPECT_EQ(GVB, TheJIT->FindGlobalVariableNamed("GVB"));EXPECT_EQ(GVC, TheJIT->FindGlobalVariableNamed("GVC",true));EXPECT_EQ(nullptr, TheJIT->FindGlobalVariableNamed("GVC"));uint64_t FBPtr = TheJIT->getFunctionAddress(FB->getName().str());TheJIT->finalizeObject();EXPECT_TRUE(0 != FBPtr);int32_t(*FuncPtr)() = (int32_t(*)())FBPtr;EXPECT_EQ(initialNum, FuncPtr())<< "Invalid value for global returned from JITted function in module B";uint64_t FAPtr = TheJIT->getFunctionAddress(FA->getName().str());EXPECT_TRUE(0 != FAPtr);FuncPtr = (int32_t(*)())FAPtr;EXPECT_EQ(initialNum, FuncPtr())<< "Invalid value for global returned from JITted function in module A";}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// Module C { Extern FA, Function FC which calls FA },// execute FC, FB, FATEST_F(MCJITMultipleModuleTest, three_module_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B, C;Function *FA, *FB, *FC;createThreeModuleCase(A, FA, B, FB, C, FC);createJIT(std::move(A));TheJIT->addModule(std::move(B));TheJIT->addModule(std::move(C));uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FB->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// Module C { Extern FA, Function FC which calls FA },// execute FA, FB, FCTEST_F(MCJITMultipleModuleTest, three_module_case_reverse_order) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B, C;Function *FA, *FB, *FC;createThreeModuleCase(A, FA, B, FB, C, FC);createJIT(std::move(A));TheJIT->addModule(std::move(B));TheJIT->addModule(std::move(C));uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FB->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FC->getName().str());checkAdd(ptr);}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// Module C { Extern FB, Function FC which calls FB },// execute FC, FB, FATEST_F(MCJITMultipleModuleTest, three_module_chain_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B, C;Function *FA, *FB, *FC;createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC);createJIT(std::move(A));TheJIT->addModule(std::move(B));TheJIT->addModule(std::move(C));uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FB->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);}// Module A { Function FA },// Module B { Extern FA, Function FB which calls FA },// Module C { Extern FB, Function FC which calls FB },// execute FA, FB, FCTEST_F(MCJITMultipleModuleTest, three_modules_chain_case_reverse_order) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B, C;Function *FA, *FB, *FC;createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC);createJIT(std::move(A));TheJIT->addModule(std::move(B));TheJIT->addModule(std::move(C));uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FB->getName().str());checkAdd(ptr);ptr = TheJIT->getFunctionAddress(FC->getName().str());checkAdd(ptr);}// Module A { Extern FB, Function FA which calls FB1 },// Module B { Extern FA, Function FB1, Function FB2 which calls FA },// execute FA, then FB1// FIXME: this test case is not supported by MCJITTEST_F(MCJITMultipleModuleTest, cross_module_dependency_case) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB1, *FB2;createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAccumulate(ptr);ptr = TheJIT->getFunctionAddress(FB1->getName().str());checkAccumulate(ptr);}// Module A { Extern FB, Function FA which calls FB1 },// Module B { Extern FA, Function FB1, Function FB2 which calls FA },// execute FB1 then FA// FIXME: this test case is not supported by MCJITTEST_F(MCJITMultipleModuleTest, cross_module_dependency_case_reverse_order) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB1, *FB2;createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str());checkAccumulate(ptr);ptr = TheJIT->getFunctionAddress(FA->getName().str());checkAccumulate(ptr);}// Module A { Extern FB1, Function FA which calls FB1 },// Module B { Extern FA, Function FB1, Function FB2 which calls FA },// execute FB1 then FB2// FIXME: this test case is not supported by MCJITTEST_F(MCJITMultipleModuleTest, cross_module_dependency_case3) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB1, *FB2;createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);createJIT(std::move(A));TheJIT->addModule(std::move(B));uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str());checkAccumulate(ptr);ptr = TheJIT->getFunctionAddress(FB2->getName().str());checkAccumulate(ptr);}// Test that FindFunctionNamed finds the definition of// a function in the correct module. We check two functions// in two different modules, to make sure that for at least// one of them MCJIT had to ignore the extern declaration.TEST_F(MCJITMultipleModuleTest, FindFunctionNamed_test) {SKIP_UNSUPPORTED_PLATFORM;std::unique_ptr<Module> A, B;Function *FA, *FB1, *FB2;createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);createJIT(std::move(A));TheJIT->addModule(std::move(B));EXPECT_EQ(FA, TheJIT->FindFunctionNamed(FA->getName().data()));EXPECT_EQ(FB1, TheJIT->FindFunctionNamed(FB1->getName().data()));}} // end anonymous namespace
//===- MCJITMemoryManagerTest.cpp - Unit tests for the JIT memory manager -===////// 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////===----------------------------------------------------------------------===//#include "llvm/ExecutionEngine/SectionMemoryManager.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(MCJITMemoryManagerTest, BasicAllocations) {std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());uint8_t *code1 = MemMgr->allocateCodeSection(256, 0, 1, "");uint8_t *data1 = MemMgr->allocateDataSection(256, 0, 2, "", true);uint8_t *code2 = MemMgr->allocateCodeSection(256, 0, 3, "");uint8_t *data2 = MemMgr->allocateDataSection(256, 0, 4, "", false);EXPECT_NE((uint8_t*)nullptr, code1);EXPECT_NE((uint8_t*)nullptr, code2);EXPECT_NE((uint8_t*)nullptr, data1);EXPECT_NE((uint8_t*)nullptr, data2);// Initialize the datafor (unsigned i = 0; i < 256; ++i) {code1[i] = 1;code2[i] = 2;data1[i] = 3;data2[i] = 4;}// Verify the data (this is checking for overlaps in the addresses)for (unsigned i = 0; i < 256; ++i) {EXPECT_EQ(1, code1[i]);EXPECT_EQ(2, code2[i]);EXPECT_EQ(3, data1[i]);EXPECT_EQ(4, data2[i]);}std::string Error;EXPECT_FALSE(MemMgr->finalizeMemory(&Error));}TEST(MCJITMemoryManagerTest, LargeAllocations) {std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());uint8_t *code1 = MemMgr->allocateCodeSection(0x100000, 0, 1, "");uint8_t *data1 = MemMgr->allocateDataSection(0x100000, 0, 2, "", true);uint8_t *code2 = MemMgr->allocateCodeSection(0x100000, 0, 3, "");uint8_t *data2 = MemMgr->allocateDataSection(0x100000, 0, 4, "", false);EXPECT_NE((uint8_t*)nullptr, code1);EXPECT_NE((uint8_t*)nullptr, code2);EXPECT_NE((uint8_t*)nullptr, data1);EXPECT_NE((uint8_t*)nullptr, data2);// Initialize the datafor (unsigned i = 0; i < 0x100000; ++i) {code1[i] = 1;code2[i] = 2;data1[i] = 3;data2[i] = 4;}// Verify the data (this is checking for overlaps in the addresses)for (unsigned i = 0; i < 0x100000; ++i) {EXPECT_EQ(1, code1[i]);EXPECT_EQ(2, code2[i]);EXPECT_EQ(3, data1[i]);EXPECT_EQ(4, data2[i]);}std::string Error;EXPECT_FALSE(MemMgr->finalizeMemory(&Error));}TEST(MCJITMemoryManagerTest, ManyAllocations) {std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());uint8_t* code[10000];uint8_t* data[10000];for (unsigned i = 0; i < 10000; ++i) {const bool isReadOnly = i % 2 == 0;code[i] = MemMgr->allocateCodeSection(32, 0, 1, "");data[i] = MemMgr->allocateDataSection(32, 0, 2, "", isReadOnly);for (unsigned j = 0; j < 32; j++) {code[i][j] = 1 + (i % 254);data[i][j] = 2 + (i % 254);}EXPECT_NE((uint8_t *)nullptr, code[i]);EXPECT_NE((uint8_t *)nullptr, data[i]);}// Verify the data (this is checking for overlaps in the addresses)for (unsigned i = 0; i < 10000; ++i) {for (unsigned j = 0; j < 32;j++ ) {uint8_t ExpectedCode = 1 + (i % 254);uint8_t ExpectedData = 2 + (i % 254);EXPECT_EQ(ExpectedCode, code[i][j]);EXPECT_EQ(ExpectedData, data[i][j]);}}std::string Error;EXPECT_FALSE(MemMgr->finalizeMemory(&Error));}TEST(MCJITMemoryManagerTest, ManyVariedAllocations) {std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());uint8_t* code[10000];uint8_t* data[10000];for (unsigned i = 0; i < 10000; ++i) {uintptr_t CodeSize = i % 16 + 1;uintptr_t DataSize = i % 8 + 1;bool isReadOnly = i % 3 == 0;unsigned Align = 8 << (i % 4);code[i] = MemMgr->allocateCodeSection(CodeSize, Align, i, "");data[i] = MemMgr->allocateDataSection(DataSize, Align, i + 10000, "",isReadOnly);for (unsigned j = 0; j < CodeSize; j++) {code[i][j] = 1 + (i % 254);}for (unsigned j = 0; j < DataSize; j++) {data[i][j] = 2 + (i % 254);}EXPECT_NE((uint8_t *)nullptr, code[i]);EXPECT_NE((uint8_t *)nullptr, data[i]);uintptr_t CodeAlign = Align ? (uintptr_t)code[i] % Align : 0;uintptr_t DataAlign = Align ? (uintptr_t)data[i] % Align : 0;EXPECT_EQ((uintptr_t)0, CodeAlign);EXPECT_EQ((uintptr_t)0, DataAlign);}for (unsigned i = 0; i < 10000; ++i) {uintptr_t CodeSize = i % 16 + 1;uintptr_t DataSize = i % 8 + 1;for (unsigned j = 0; j < CodeSize; j++) {uint8_t ExpectedCode = 1 + (i % 254);EXPECT_EQ(ExpectedCode, code[i][j]);}for (unsigned j = 0; j < DataSize; j++) {uint8_t ExpectedData = 2 + (i % 254);EXPECT_EQ(ExpectedData, data[i][j]);}}}} // Namespace
//===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// This test suite verifies basic MCJIT functionality when invoked form the C// API.////===----------------------------------------------------------------------===//#include "MCJITTestAPICommon.h"#include "llvm-c/Analysis.h"#include "llvm-c/Core.h"#include "llvm-c/ExecutionEngine.h"#include "llvm-c/Target.h"#include "llvm-c/Transforms/PassManagerBuilder.h"#include "llvm-c/Transforms/Scalar.h"#include "llvm/ExecutionEngine/SectionMemoryManager.h"#include "llvm/Support/Debug.h"#include "llvm/Support/Host.h"#include "gtest/gtest.h"using namespace llvm;static bool didCallAllocateCodeSection;static bool didAllocateCompactUnwindSection;static bool didCallYield;static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size,unsigned alignment,unsigned sectionID,const char *sectionName) {didCallAllocateCodeSection = true;return static_cast<SectionMemoryManager*>(object)->allocateCodeSection(size, alignment, sectionID, sectionName);}static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size,unsigned alignment,unsigned sectionID,const char *sectionName,LLVMBool isReadOnly) {if (!strcmp(sectionName, "__compact_unwind"))didAllocateCompactUnwindSection = true;return static_cast<SectionMemoryManager*>(object)->allocateDataSection(size, alignment, sectionID, sectionName, isReadOnly);}static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) {std::string errMsgString;bool result =static_cast<SectionMemoryManager*>(object)->finalizeMemory(&errMsgString);if (result) {*errMsg = LLVMCreateMessage(errMsgString.c_str());return 1;}return 0;}static void roundTripDestroy(void *object) {delete static_cast<SectionMemoryManager*>(object);}static void yield(LLVMContextRef, void *) {didCallYield = true;}namespace {// memory manager to test reserve allocation space callbackclass TestReserveAllocationSpaceMemoryManager: public SectionMemoryManager {public:uintptr_t ReservedCodeSize;uintptr_t UsedCodeSize;uintptr_t ReservedDataSizeRO;uintptr_t UsedDataSizeRO;uintptr_t ReservedDataSizeRW;uintptr_t UsedDataSizeRW;TestReserveAllocationSpaceMemoryManager() :ReservedCodeSize(0), UsedCodeSize(0), ReservedDataSizeRO(0),UsedDataSizeRO(0), ReservedDataSizeRW(0), UsedDataSizeRW(0) {}bool needsToReserveAllocationSpace() override { return true; }void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign,uintptr_t DataSizeRO, uint32_t RODataAlign,uintptr_t DataSizeRW,uint32_t RWDataAlign) override {ReservedCodeSize = CodeSize;ReservedDataSizeRO = DataSizeRO;ReservedDataSizeRW = DataSizeRW;}void useSpace(uintptr_t* UsedSize, uintptr_t Size, unsigned Alignment) {uintptr_t AlignedSize = (Size + Alignment - 1) / Alignment * Alignment;uintptr_t AlignedBegin = (*UsedSize + Alignment - 1) / Alignment * Alignment;*UsedSize = AlignedBegin + AlignedSize;}uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,unsigned SectionID, StringRef SectionName,bool IsReadOnly) override {useSpace(IsReadOnly ? &UsedDataSizeRO : &UsedDataSizeRW, Size, Alignment);return SectionMemoryManager::allocateDataSection(Size, Alignment,SectionID, SectionName, IsReadOnly);}uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,unsigned SectionID,StringRef SectionName) override {useSpace(&UsedCodeSize, Size, Alignment);return SectionMemoryManager::allocateCodeSection(Size, Alignment,SectionID, SectionName);}};class MCJITCAPITest : public testing::Test, public MCJITTestAPICommon {protected:MCJITCAPITest() {// The architectures below are known to be compatible with MCJIT as they// are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be// kept in sync.SupportedArchs.push_back(Triple::aarch64);SupportedArchs.push_back(Triple::arm);SupportedArchs.push_back(Triple::mips);SupportedArchs.push_back(Triple::mips64);SupportedArchs.push_back(Triple::mips64el);SupportedArchs.push_back(Triple::x86);SupportedArchs.push_back(Triple::x86_64);// Some architectures have sub-architectures in which tests will fail, like// ARM. These two vectors will define if they do have sub-archs (to avoid// extra work for those who don't), and if so, if they are listed to workHasSubArchs.push_back(Triple::arm);SupportedSubArchs.push_back("armv6");SupportedSubArchs.push_back("armv7");// The operating systems below are known to be sufficiently incompatible// that they will fail the MCJIT C API tests.UnsupportedEnvironments.push_back(Triple::Cygnus);}void SetUp() override {didCallAllocateCodeSection = false;didAllocateCompactUnwindSection = false;didCallYield = false;Module = nullptr;Function = nullptr;Engine = nullptr;Error = nullptr;}void TearDown() override {if (Engine)LLVMDisposeExecutionEngine(Engine);else if (Module)LLVMDisposeModule(Module);}void buildSimpleFunction() {Module = LLVMModuleCreateWithName("simple_module");LLVMSetTarget(Module, HostTriple.c_str());Function = LLVMAddFunction(Module, "simple_function",LLVMFunctionType(LLVMInt32Type(), nullptr,0, 0));LLVMSetFunctionCallConv(Function, LLVMCCallConv);LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry");LLVMBuilderRef builder = LLVMCreateBuilder();LLVMPositionBuilderAtEnd(builder, entry);LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0));LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);LLVMDisposeMessage(Error);LLVMDisposeBuilder(builder);}void buildFunctionThatUsesStackmap() {Module = LLVMModuleCreateWithName("simple_module");LLVMSetTarget(Module, HostTriple.c_str());LLVMTypeRef stackmapParamTypes[] = { LLVMInt64Type(), LLVMInt32Type() };LLVMTypeRef stackmapTy =LLVMFunctionType(LLVMVoidType(), stackmapParamTypes, 2, 1);LLVMValueRef stackmap = LLVMAddFunction(Module, "llvm.experimental.stackmap", stackmapTy);LLVMSetLinkage(stackmap, LLVMExternalLinkage);Function = LLVMAddFunction(Module, "simple_function",LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry");LLVMBuilderRef builder = LLVMCreateBuilder();LLVMPositionBuilderAtEnd(builder, entry);LLVMValueRef stackmapArgs[] = {LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt32Type(), 5, 0),LLVMConstInt(LLVMInt32Type(), 42, 0)};LLVMBuildCall2(builder, stackmapTy, stackmap, stackmapArgs, 3, "");LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0));LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);LLVMDisposeMessage(Error);LLVMDisposeBuilder(builder);}void buildModuleWithCodeAndData() {Module = LLVMModuleCreateWithName("simple_module");LLVMSetTarget(Module, HostTriple.c_str());// build a global int32 variable initialized to 42.LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "intVal");LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0));{Function = LLVMAddFunction(Module, "getGlobal",LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));LLVMSetFunctionCallConv(Function, LLVMCCallConv);LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "entry");LLVMBuilderRef Builder = LLVMCreateBuilder();LLVMPositionBuilderAtEnd(Builder, Entry);LLVMValueRef IntVal =LLVMBuildLoad2(Builder, LLVMInt32Type(), GlobalVar, "intVal");LLVMBuildRet(Builder, IntVal);LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);LLVMDisposeMessage(Error);LLVMDisposeBuilder(Builder);}{LLVMTypeRef ParamTypes[] = { LLVMInt32Type() };Function2 = LLVMAddFunction(Module, "setGlobal", LLVMFunctionType(LLVMVoidType(), ParamTypes, 1, 0));LLVMSetFunctionCallConv(Function2, LLVMCCallConv);LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function2, "entry");LLVMBuilderRef Builder = LLVMCreateBuilder();LLVMPositionBuilderAtEnd(Builder, Entry);LLVMValueRef Arg = LLVMGetParam(Function2, 0);LLVMBuildStore(Builder, Arg, GlobalVar);LLVMBuildRetVoid(Builder);LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);LLVMDisposeMessage(Error);LLVMDisposeBuilder(Builder);}}void buildMCJITOptions() {LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options));Options.OptLevel = 2;// Just ensure that this field still exists.Options.NoFramePointerElim = false;}void useRoundTripSectionMemoryManager() {Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager(new SectionMemoryManager(),roundTripAllocateCodeSection,roundTripAllocateDataSection,roundTripFinalizeMemory,roundTripDestroy);}void buildMCJITEngine() {ASSERT_EQ(0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options,sizeof(Options), &Error));}void buildAndRunPasses() {LLVMPassManagerRef pass = LLVMCreatePassManager();LLVMAddInstructionCombiningPass(pass);LLVMRunPassManager(pass, Module);LLVMDisposePassManager(pass);}void buildAndRunOptPasses() {LLVMPassManagerBuilderRef passBuilder;passBuilder = LLVMPassManagerBuilderCreate();LLVMPassManagerBuilderSetOptLevel(passBuilder, 2);LLVMPassManagerBuilderSetSizeLevel(passBuilder, 0);LLVMPassManagerRef functionPasses =LLVMCreateFunctionPassManagerForModule(Module);LLVMPassManagerRef modulePasses =LLVMCreatePassManager();LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder,functionPasses);LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses);LLVMPassManagerBuilderDispose(passBuilder);LLVMInitializeFunctionPassManager(functionPasses);for (LLVMValueRef value = LLVMGetFirstFunction(Module);value; value = LLVMGetNextFunction(value))LLVMRunFunctionPassManager(functionPasses, value);LLVMFinalizeFunctionPassManager(functionPasses);LLVMRunPassManager(modulePasses, Module);LLVMDisposePassManager(functionPasses);LLVMDisposePassManager(modulePasses);}LLVMModuleRef Module;LLVMValueRef Function;LLVMValueRef Function2;LLVMMCJITCompilerOptions Options;LLVMExecutionEngineRef Engine;char *Error;};} // end anonymous namespaceTEST_F(MCJITCAPITest, simple_function) {SKIP_UNSUPPORTED_PLATFORM;buildSimpleFunction();buildMCJITOptions();buildMCJITEngine();buildAndRunPasses();auto *functionPointer = reinterpret_cast<int (*)()>(reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));EXPECT_EQ(42, functionPointer());}TEST_F(MCJITCAPITest, gva) {SKIP_UNSUPPORTED_PLATFORM;Module = LLVMModuleCreateWithName("simple_module");LLVMSetTarget(Module, HostTriple.c_str());LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "simple_value");LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0));buildMCJITOptions();buildMCJITEngine();buildAndRunPasses();uint64_t raw = LLVMGetGlobalValueAddress(Engine, "simple_value");int32_t *usable = (int32_t *) raw;EXPECT_EQ(42, *usable);}TEST_F(MCJITCAPITest, gfa) {SKIP_UNSUPPORTED_PLATFORM;buildSimpleFunction();buildMCJITOptions();buildMCJITEngine();buildAndRunPasses();uint64_t raw = LLVMGetFunctionAddress(Engine, "simple_function");int (*usable)() = (int (*)()) raw;EXPECT_EQ(42, usable());}TEST_F(MCJITCAPITest, custom_memory_manager) {SKIP_UNSUPPORTED_PLATFORM;buildSimpleFunction();buildMCJITOptions();useRoundTripSectionMemoryManager();buildMCJITEngine();buildAndRunPasses();auto *functionPointer = reinterpret_cast<int (*)()>(reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));EXPECT_EQ(42, functionPointer());EXPECT_TRUE(didCallAllocateCodeSection);}TEST_F(MCJITCAPITest, stackmap_creates_compact_unwind_on_darwin) {SKIP_UNSUPPORTED_PLATFORM;// This test is also not supported on non-x86 platforms.if (Triple(HostTriple).getArch() != Triple::x86_64)return;buildFunctionThatUsesStackmap();buildMCJITOptions();useRoundTripSectionMemoryManager();buildMCJITEngine();buildAndRunOptPasses();auto *functionPointer = reinterpret_cast<int (*)()>(reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));EXPECT_EQ(42, functionPointer());EXPECT_TRUE(didCallAllocateCodeSection);// Up to this point, the test is specific only to X86-64. But this next// expectation is only valid on Darwin because it assumes that unwind// data is made available only through compact_unwind. It would be// worthwhile to extend this to handle non-Darwin platforms, in which// case you'd want to look for an eh_frame or something.//// FIXME: Currently, MCJIT relies on a configure-time check to determine which// sections to emit. The JIT client should have runtime control over this.EXPECT_TRUE(Triple(HostTriple).getOS() != Triple::Darwin ||Triple(HostTriple).isMacOSXVersionLT(10, 7) ||didAllocateCompactUnwindSection);}#if defined(__APPLE__) && defined(__aarch64__)// FIXME: Figure out why this fails on mac/arm, PR46647#define MAYBE_reserve_allocation_space DISABLED_reserve_allocation_space#else#define MAYBE_reserve_allocation_space reserve_allocation_space#endifTEST_F(MCJITCAPITest, MAYBE_reserve_allocation_space) {SKIP_UNSUPPORTED_PLATFORM;TestReserveAllocationSpaceMemoryManager* MM = new TestReserveAllocationSpaceMemoryManager();buildModuleWithCodeAndData();buildMCJITOptions();Options.MCJMM = wrap(MM);buildMCJITEngine();buildAndRunPasses();auto GetGlobalFct = reinterpret_cast<int (*)()>(reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));auto SetGlobalFct = reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function2)));SetGlobalFct(789);EXPECT_EQ(789, GetGlobalFct());EXPECT_LE(MM->UsedCodeSize, MM->ReservedCodeSize);EXPECT_LE(MM->UsedDataSizeRO, MM->ReservedDataSizeRO);EXPECT_LE(MM->UsedDataSizeRW, MM->ReservedDataSizeRW);EXPECT_TRUE(MM->UsedCodeSize > 0);EXPECT_TRUE(MM->UsedDataSizeRW > 0);}TEST_F(MCJITCAPITest, yield) {SKIP_UNSUPPORTED_PLATFORM;buildSimpleFunction();buildMCJITOptions();buildMCJITEngine();LLVMContextRef C = LLVMGetGlobalContext();LLVMContextSetYieldCallback(C, yield, nullptr);buildAndRunPasses();auto *functionPointer = reinterpret_cast<int (*)()>(reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));EXPECT_EQ(42, functionPointer());EXPECT_TRUE(didCallYield);}static int localTestFunc() {return 42;}TEST_F(MCJITCAPITest, addGlobalMapping) {SKIP_UNSUPPORTED_PLATFORM;Module = LLVMModuleCreateWithName("testModule");LLVMSetTarget(Module, HostTriple.c_str());LLVMTypeRef FunctionType = LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0);LLVMValueRef MappedFn = LLVMAddFunction(Module, "mapped_fn", FunctionType);Function = LLVMAddFunction(Module, "test_fn", FunctionType);LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "");LLVMBuilderRef Builder = LLVMCreateBuilder();LLVMPositionBuilderAtEnd(Builder, Entry);LLVMValueRef RetVal =LLVMBuildCall2(Builder, FunctionType, MappedFn, nullptr, 0, "");LLVMBuildRet(Builder, RetVal);LLVMDisposeBuilder(Builder);LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);LLVMDisposeMessage(Error);buildMCJITOptions();buildMCJITEngine();LLVMAddGlobalMapping(Engine, MappedFn,reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(&localTestFunc)));buildAndRunPasses();uint64_t raw = LLVMGetFunctionAddress(Engine, "test_fn");int (*usable)() = (int (*)()) raw;EXPECT_EQ(42, usable());}
set(LLVM_LINK_COMPONENTSAnalysisCoreExecutionEngineIPOInstCombineMCMCJITRuntimeDyldScalarOptsSupportTargetnativecodegen)set(MCJITTestsSourcesMCJITTest.cppMCJITCAPITest.cppMCJITMemoryManagerTest.cppMCJITMultipleModuleTest.cppMCJITObjectCacheTest.cpp)if(MSVC)list(APPEND MCJITTestsSources MCJITTests.def)endif()add_llvm_unittest(MCJITTests${MCJITTestsSources})if(MINGW OR CYGWIN)set_property(TARGET MCJITTests PROPERTY LINK_FLAGS -Wl,--export-all-symbols)endif()set_property(TARGET MCJITTests PROPERTY FOLDER "Tests/UnitTests/ExecutionTests")
//===------ LinkGraphTests.cpp - Unit tests for core JITLink classes ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/ExecutionEngine/JITLink/JITLink.h"#include "llvm/Support/Endian.h"#include "llvm/Support/Memory.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::jitlink;static const char BlockContentBytes[] = {0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6d, 0x6f,0x76, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68,0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x66,0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x20,0x68, 0x61, 0x64, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x61,0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0a, 0x54, 0x68, 0x61, 0x74, 0x20, 0x74,0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d,0x20, 0x4f, 0x6c, 0x64, 0x20, 0x52, 0x65, 0x67, 0x72, 0x65, 0x74, 0x20,0x68, 0x61, 0x64, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x61, 0x77, 0x61, 0x79,0x2c, 0x0a, 0x41, 0x6e, 0x64, 0x20, 0x68, 0x61, 0x64, 0x20, 0x6a, 0x6f,0x69, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x69, 0x6c,0x64, 0x20, 0x62, 0x75, 0x73, 0x68, 0x20, 0x68, 0x6f, 0x72, 0x73, 0x65,0x73, 0x20, 0x2d, 0x2d, 0x20, 0x68, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20,0x77, 0x6f, 0x72, 0x74, 0x68, 0x20, 0x61, 0x20, 0x74, 0x68, 0x6f, 0x75,0x73, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x6f, 0x75, 0x6e, 0x64, 0x2c, 0x0a,0x53, 0x6f, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63,0x72, 0x61, 0x63, 0x6b, 0x73, 0x20, 0x68, 0x61, 0x64, 0x20, 0x67, 0x61,0x74, 0x68, 0x65, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68,0x65, 0x20, 0x66, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x41, 0x6c, 0x6c, 0x20,0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x69, 0x65, 0x64, 0x20, 0x61, 0x6e,0x64, 0x20, 0x6e, 0x6f, 0x74, 0x65, 0x64, 0x20, 0x72, 0x69, 0x64, 0x65,0x72, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20,0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6e, 0x65, 0x61,0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x61, 0x72, 0x0a, 0x48, 0x61,0x64, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x61,0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x6f, 0x6d, 0x65, 0x73, 0x74,0x65, 0x61, 0x64, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x69, 0x67, 0x68,0x74, 0x2c, 0x0a, 0x46, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62,0x75, 0x73, 0x68, 0x6d, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x20,0x68, 0x61, 0x72, 0x64, 0x20, 0x72, 0x69, 0x64, 0x69, 0x6e, 0x67, 0x20,0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x69,0x6c, 0x64, 0x20, 0x62, 0x75, 0x73, 0x68, 0x20, 0x68, 0x6f, 0x72, 0x73,0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x2c, 0x0a, 0x41, 0x6e, 0x64, 0x20,0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x63, 0x6b, 0x2d, 0x68, 0x6f,0x72, 0x73, 0x65, 0x20, 0x73, 0x6e, 0x75, 0x66, 0x66, 0x73, 0x20, 0x74,0x68, 0x65, 0x20, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x77, 0x69,0x74, 0x68, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x00};static ArrayRef<char> BlockContent(BlockContentBytes);TEST(LinkGraphTest, Construction) {// Check that LinkGraph construction works as expected.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);EXPECT_EQ(G.getName(), "foo");EXPECT_EQ(G.getTargetTriple().str(), "x86_64-apple-darwin");EXPECT_EQ(G.getPointerSize(), 8U);EXPECT_EQ(G.getEndianness(), support::little);EXPECT_TRUE(llvm::empty(G.external_symbols()));EXPECT_TRUE(llvm::empty(G.absolute_symbols()));EXPECT_TRUE(llvm::empty(G.defined_symbols()));EXPECT_TRUE(llvm::empty(G.blocks()));}TEST(LinkGraphTest, AddressAccess) {// Check that we can get addresses for blocks, symbols, and edges.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec1 = G.createSection("__data.1", MemProt::Read | MemProt::Write);orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec1, BlockContent, B1Addr, 8, 0);auto &S1 = G.addDefinedSymbol(B1, 4, "S1", 4, Linkage::Strong, Scope::Default,false, false);B1.addEdge(Edge::FirstRelocation, 8, S1, 0);auto &E1 = *B1.edges().begin();EXPECT_EQ(B1.getAddress(), B1Addr) << "Incorrect block address";EXPECT_EQ(S1.getAddress(), B1Addr + 4) << "Incorrect symbol address";EXPECT_EQ(B1.getFixupAddress(E1), B1Addr + 8) << "Incorrect fixup address";}TEST(LinkGraphTest, BlockAndSymbolIteration) {// Check that we can iterate over blocks within Sections and across sections.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec1 = G.createSection("__data.1", MemProt::Read | MemProt::Write);orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec1, BlockContent, B1Addr, 8, 0);orc::ExecutorAddr B2Addr(0x1000);auto &B2 = G.createContentBlock(Sec1, BlockContent, B2Addr, 8, 0);auto &S1 = G.addDefinedSymbol(B1, 0, "S1", 4, Linkage::Strong, Scope::Default,false, false);auto &S2 = G.addDefinedSymbol(B2, 4, "S2", 4, Linkage::Strong, Scope::Default,false, false);auto &Sec2 = G.createSection("__data.2", MemProt::Read | MemProt::Write);orc::ExecutorAddr B3Addr(0x3000);auto &B3 = G.createContentBlock(Sec2, BlockContent, B3Addr, 8, 0);orc::ExecutorAddr B4Addr(0x4000);auto &B4 = G.createContentBlock(Sec2, BlockContent, B4Addr, 8, 0);auto &S3 = G.addDefinedSymbol(B3, 0, "S3", 4, Linkage::Strong, Scope::Default,false, false);auto &S4 = G.addDefinedSymbol(B4, 4, "S4", 4, Linkage::Strong, Scope::Default,false, false);// Check that iteration of blocks within a section behaves as expected.EXPECT_EQ(std::distance(Sec1.blocks().begin(), Sec1.blocks().end()), 2);EXPECT_TRUE(llvm::count(Sec1.blocks(), &B1));EXPECT_TRUE(llvm::count(Sec1.blocks(), &B2));// Check that iteration of symbols within a section behaves as expected.EXPECT_EQ(std::distance(Sec1.symbols().begin(), Sec1.symbols().end()), 2);EXPECT_TRUE(llvm::count(Sec1.symbols(), &S1));EXPECT_TRUE(llvm::count(Sec1.symbols(), &S2));// Check that iteration of blocks across sections behaves as expected.EXPECT_EQ(std::distance(G.blocks().begin(), G.blocks().end()), 4);EXPECT_TRUE(llvm::count(G.blocks(), &B1));EXPECT_TRUE(llvm::count(G.blocks(), &B2));EXPECT_TRUE(llvm::count(G.blocks(), &B3));EXPECT_TRUE(llvm::count(G.blocks(), &B4));// Check that iteration of defined symbols across sections behaves as// expected.EXPECT_EQ(std::distance(G.defined_symbols().begin(), G.defined_symbols().end()), 4);EXPECT_TRUE(llvm::count(G.defined_symbols(), &S1));EXPECT_TRUE(llvm::count(G.defined_symbols(), &S2));EXPECT_TRUE(llvm::count(G.defined_symbols(), &S3));EXPECT_TRUE(llvm::count(G.defined_symbols(), &S4));}TEST(LinkGraphTest, ContentAccessAndUpdate) {// Check that we can make a defined symbol external.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec = G.createSection("__data", MemProt::Read | MemProt::Write);// Create an initial block.orc::ExecutorAddr BAddr(0x1000);auto &B = G.createContentBlock(Sec, BlockContent, BAddr, 8, 0);EXPECT_FALSE(B.isContentMutable()) << "Content unexpectedly mutable";EXPECT_EQ(B.getContent().data(), BlockContent.data())<< "Unexpected block content data pointer";EXPECT_EQ(B.getContent().size(), BlockContent.size())<< "Unexpected block content size";// Expect that attempting to get already-mutable content fails if the// content is not yet mutable (debug builds only).#ifndef NDEBUGEXPECT_DEATH({ (void)B.getAlreadyMutableContent(); },"Content is not mutable")<< "Unexpected mutable access allowed to immutable data";#endif// Check that mutable content is copied on request as expected.auto MutableContent = B.getMutableContent(G);EXPECT_TRUE(B.isContentMutable()) << "Content unexpectedly immutable";EXPECT_NE(MutableContent.data(), BlockContent.data())<< "Unexpected mutable content data pointer";EXPECT_EQ(MutableContent.size(), BlockContent.size())<< "Unexpected mutable content size";EXPECT_TRUE(std::equal(MutableContent.begin(), MutableContent.end(),BlockContent.begin()))<< "Unexpected mutable content value";// Check that already-mutable content behaves as expected, with no// further copies.auto MutableContent2 = B.getMutableContent(G);EXPECT_TRUE(B.isContentMutable()) << "Content unexpectedly immutable";EXPECT_EQ(MutableContent2.data(), MutableContent.data())<< "Unexpected mutable content 2 data pointer";EXPECT_EQ(MutableContent2.size(), MutableContent.size())<< "Unexpected mutable content 2 size";// Check that getAlreadyMutableContent behaves as expected, with no// further copies.auto MutableContent3 = B.getMutableContent(G);EXPECT_TRUE(B.isContentMutable()) << "Content unexpectedly immutable";EXPECT_EQ(MutableContent3.data(), MutableContent.data())<< "Unexpected mutable content 2 data pointer";EXPECT_EQ(MutableContent3.size(), MutableContent.size())<< "Unexpected mutable content 2 size";// Set content back to immutable and check that everything behaves as// expected again.B.setContent(BlockContent);EXPECT_FALSE(B.isContentMutable()) << "Content unexpectedly mutable";EXPECT_EQ(B.getContent().data(), BlockContent.data())<< "Unexpected block content data pointer";EXPECT_EQ(B.getContent().size(), BlockContent.size())<< "Unexpected block content size";// Create an initially mutable block.auto &B2 = G.createMutableContentBlock(Sec, MutableContent,orc::ExecutorAddr(0x10000), 8, 0);EXPECT_TRUE(B2.isContentMutable()) << "Expected B2 content to be mutable";}TEST(LinkGraphTest, MakeExternal) {// Check that we can make a defined symbol external.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec = G.createSection("__data", MemProt::Read | MemProt::Write);// Create an initial block.auto &B1 =G.createContentBlock(Sec, BlockContent, orc::ExecutorAddr(0x1000), 8, 0);// Add a symbol to the block.auto &S1 = G.addDefinedSymbol(B1, 0, "S1", 4, Linkage::Strong, Scope::Default,false, false);EXPECT_TRUE(S1.isDefined()) << "Symbol should be defined";EXPECT_FALSE(S1.isExternal()) << "Symbol should not be external";EXPECT_FALSE(S1.isAbsolute()) << "Symbol should not be absolute";EXPECT_TRUE(&S1.getBlock()) << "Symbol should have a non-null block";EXPECT_EQ(S1.getAddress(), orc::ExecutorAddr(0x1000))<< "Unexpected symbol address";EXPECT_EQ(std::distance(G.defined_symbols().begin(), G.defined_symbols().end()), 1U)<< "Unexpected number of defined symbols";EXPECT_EQ(std::distance(G.external_symbols().begin(), G.external_symbols().end()),0U)<< "Unexpected number of external symbols";// Make S1 external, confirm that the its flags are updated and that it is// moved from the defined symbols to the externals list.G.makeExternal(S1);EXPECT_FALSE(S1.isDefined()) << "Symbol should not be defined";EXPECT_TRUE(S1.isExternal()) << "Symbol should be external";EXPECT_FALSE(S1.isAbsolute()) << "Symbol should not be absolute";EXPECT_EQ(S1.getAddress(), orc::ExecutorAddr())<< "Unexpected symbol address";EXPECT_EQ(std::distance(G.defined_symbols().begin(), G.defined_symbols().end()), 0U)<< "Unexpected number of defined symbols";EXPECT_EQ(std::distance(G.external_symbols().begin(), G.external_symbols().end()),1U)<< "Unexpected number of external symbols";}TEST(LinkGraphTest, MakeDefined) {// Check that we can make an external symbol defined.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec = G.createSection("__data", MemProt::Read | MemProt::Write);// Create an initial block.orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec, BlockContent, B1Addr, 8, 0);// Add an external symbol.auto &S1 = G.addExternalSymbol("S1", 4, Linkage::Strong);EXPECT_FALSE(S1.isDefined()) << "Symbol should not be defined";EXPECT_TRUE(S1.isExternal()) << "Symbol should be external";EXPECT_FALSE(S1.isAbsolute()) << "Symbol should not be absolute";EXPECT_EQ(S1.getAddress(), orc::ExecutorAddr())<< "Unexpected symbol address";EXPECT_EQ(std::distance(G.defined_symbols().begin(), G.defined_symbols().end()), 0U)<< "Unexpected number of defined symbols";EXPECT_EQ(std::distance(G.external_symbols().begin(), G.external_symbols().end()),1U)<< "Unexpected number of external symbols";// Make S1 defined, confirm that its flags are updated and that it is// moved from the defined symbols to the externals list.G.makeDefined(S1, B1, 0, 4, Linkage::Strong, Scope::Default, false);EXPECT_TRUE(S1.isDefined()) << "Symbol should be defined";EXPECT_FALSE(S1.isExternal()) << "Symbol should not be external";EXPECT_FALSE(S1.isAbsolute()) << "Symbol should not be absolute";EXPECT_TRUE(&S1.getBlock()) << "Symbol should have a non-null block";EXPECT_EQ(S1.getAddress(), orc::ExecutorAddr(0x1000U))<< "Unexpected symbol address";EXPECT_EQ(std::distance(G.defined_symbols().begin(), G.defined_symbols().end()), 1U)<< "Unexpected number of defined symbols";EXPECT_EQ(std::distance(G.external_symbols().begin(), G.external_symbols().end()),0U)<< "Unexpected number of external symbols";}TEST(LinkGraphTest, TransferDefinedSymbol) {// Check that we can transfer a defined symbol from one block to another.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec = G.createSection("__data", MemProt::Read | MemProt::Write);// Create initial blocks.orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec, BlockContent, B1Addr, 8, 0);orc::ExecutorAddr B2Addr(0x2000);auto &B2 = G.createContentBlock(Sec, BlockContent, B2Addr, 8, 0);orc::ExecutorAddr B3Addr(0x3000);auto &B3 = G.createContentBlock(Sec, BlockContent.slice(0, 32), B3Addr, 8, 0);// Add a symbol.auto &S1 = G.addDefinedSymbol(B1, 0, "S1", B1.getSize(), Linkage::Strong,Scope::Default, false, false);// Transfer with zero offset, explicit size.G.transferDefinedSymbol(S1, B2, 0, 64);EXPECT_EQ(&S1.getBlock(), &B2) << "Block was not updated";EXPECT_EQ(S1.getOffset(), 0U) << "Unexpected offset";EXPECT_EQ(S1.getSize(), 64U) << "Size was not updated";// Transfer with non-zero offset, implicit truncation.G.transferDefinedSymbol(S1, B3, 16, None);EXPECT_EQ(&S1.getBlock(), &B3) << "Block was not updated";EXPECT_EQ(S1.getOffset(), 16U) << "Offset was not updated";EXPECT_EQ(S1.getSize(), 16U) << "Size was not updated";}TEST(LinkGraphTest, TransferDefinedSymbolAcrossSections) {// Check that we can transfer a defined symbol from an existing block in one// section to another.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec1 = G.createSection("__data.1", MemProt::Read | MemProt::Write);auto &Sec2 = G.createSection("__data.2", MemProt::Read | MemProt::Write);// Create blocks in each section.orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec1, BlockContent, B1Addr, 8, 0);orc::ExecutorAddr B2Addr(0x2000);auto &B2 = G.createContentBlock(Sec2, BlockContent, B2Addr, 8, 0);// Add a symbol to section 1.auto &S1 = G.addDefinedSymbol(B1, 0, "S1", B1.getSize(), Linkage::Strong,Scope::Default, false, false);// Transfer with zero offset, explicit size to section 2.G.transferDefinedSymbol(S1, B2, 0, 64);EXPECT_EQ(&S1.getBlock(), &B2) << "Block was not updated";EXPECT_EQ(S1.getOffset(), 0U) << "Unexpected offset";EXPECT_EQ(S1.getSize(), 64U) << "Size was not updated";EXPECT_EQ(Sec1.symbols_size(), 0u) << "Symbol was not removed from Sec1";EXPECT_EQ(Sec2.symbols_size(), 1u) << "Symbol was not added to Sec2";if (Sec2.symbols_size() == 1) {EXPECT_EQ(*Sec2.symbols().begin(), &S1) << "Unexpected symbol";}}TEST(LinkGraphTest, TransferBlock) {// Check that we can transfer a block (and all associated symbols) from one// section to another.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec1 = G.createSection("__data.1", MemProt::Read | MemProt::Write);auto &Sec2 = G.createSection("__data.2", MemProt::Read | MemProt::Write);// Create an initial block.orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec1, BlockContent, B1Addr, 8, 0);orc::ExecutorAddr B2Addr(0x2000);auto &B2 = G.createContentBlock(Sec1, BlockContent, B2Addr, 8, 0);// Add some symbols on B1...G.addDefinedSymbol(B1, 0, "S1", B1.getSize(), Linkage::Strong, Scope::Default,false, false);G.addDefinedSymbol(B1, 1, "S2", B1.getSize() - 1, Linkage::Strong,Scope::Default, false, false);// ... and on B2.G.addDefinedSymbol(B2, 0, "S3", B2.getSize(), Linkage::Strong, Scope::Default,false, false);G.addDefinedSymbol(B2, 1, "S4", B2.getSize() - 1, Linkage::Strong,Scope::Default, false, false);EXPECT_EQ(Sec1.blocks_size(), 2U) << "Expected two blocks in Sec1 initially";EXPECT_EQ(Sec1.symbols_size(), 4U)<< "Expected four symbols in Sec1 initially";EXPECT_EQ(Sec2.blocks_size(), 0U) << "Expected zero blocks in Sec2 initially";EXPECT_EQ(Sec2.symbols_size(), 0U)<< "Expected zero symbols in Sec2 initially";// Transfer with zero offset, explicit size.G.transferBlock(B1, Sec2);EXPECT_EQ(Sec1.blocks_size(), 1U)<< "Expected one blocks in Sec1 after transfer";EXPECT_EQ(Sec1.symbols_size(), 2U)<< "Expected two symbols in Sec1 after transfer";EXPECT_EQ(Sec2.blocks_size(), 1U)<< "Expected one blocks in Sec2 after transfer";EXPECT_EQ(Sec2.symbols_size(), 2U)<< "Expected two symbols in Sec2 after transfer";}TEST(LinkGraphTest, MergeSections) {// Check that we can transfer a block (and all associated symbols) from one// section to another.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec1 = G.createSection("__data.1", MemProt::Read | MemProt::Write);auto &Sec2 = G.createSection("__data.2", MemProt::Read | MemProt::Write);auto &Sec3 = G.createSection("__data.3", MemProt::Read | MemProt::Write);// Create an initial block.orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec1, BlockContent, B1Addr, 8, 0);orc::ExecutorAddr B2Addr(0x2000);auto &B2 = G.createContentBlock(Sec2, BlockContent, B2Addr, 8, 0);orc::ExecutorAddr B3Addr(0x3000);auto &B3 = G.createContentBlock(Sec3, BlockContent, B3Addr, 8, 0);// Add a symbols for each block.G.addDefinedSymbol(B1, 0, "S1", B1.getSize(), Linkage::Strong, Scope::Default,false, false);G.addDefinedSymbol(B2, 0, "S2", B2.getSize(), Linkage::Strong, Scope::Default,false, false);G.addDefinedSymbol(B3, 0, "S3", B2.getSize(), Linkage::Strong, Scope::Default,false, false);EXPECT_EQ(&B1.getSection(), &Sec1);EXPECT_EQ(&B2.getSection(), &Sec2);EXPECT_EQ(G.sections_size(), 3U) << "Expected three sections initially";EXPECT_EQ(Sec1.blocks_size(), 1U) << "Expected one block in Sec1 initially";EXPECT_EQ(Sec1.symbols_size(), 1U) << "Expected one symbol in Sec1 initially";EXPECT_EQ(Sec2.blocks_size(), 1U) << "Expected one block in Sec2 initially";EXPECT_EQ(Sec2.symbols_size(), 1U) << "Expected one symbol in Sec2 initially";EXPECT_EQ(Sec3.blocks_size(), 1U) << "Expected one block in Sec3 initially";EXPECT_EQ(Sec3.symbols_size(), 1U) << "Expected one symbol in Sec3 initially";// Check that self-merge is a no-op.G.mergeSections(Sec1, Sec1);EXPECT_EQ(&B1.getSection(), &Sec1)<< "Expected B1.getSection() to remain unchanged";EXPECT_EQ(G.sections_size(), 3U)<< "Expected three sections after first merge";EXPECT_EQ(Sec1.blocks_size(), 1U)<< "Expected one block in Sec1 after first merge";EXPECT_EQ(Sec1.symbols_size(), 1U)<< "Expected one symbol in Sec1 after first merge";EXPECT_EQ(Sec2.blocks_size(), 1U)<< "Expected one block in Sec2 after first merge";EXPECT_EQ(Sec2.symbols_size(), 1U)<< "Expected one symbol in Sec2 after first merge";EXPECT_EQ(Sec3.blocks_size(), 1U)<< "Expected one block in Sec3 after first merge";EXPECT_EQ(Sec3.symbols_size(), 1U)<< "Expected one symbol in Sec3 after first merge";// Merge Sec2 into Sec1, removing Sec2.G.mergeSections(Sec1, Sec2);EXPECT_EQ(&B2.getSection(), &Sec1)<< "Expected B2.getSection() to have been changed to &Sec1";EXPECT_EQ(G.sections_size(), 2U)<< "Expected two sections after section merge";EXPECT_EQ(Sec1.blocks_size(), 2U)<< "Expected two blocks in Sec1 after section merge";EXPECT_EQ(Sec1.symbols_size(), 2U)<< "Expected two symbols in Sec1 after section merge";EXPECT_EQ(Sec3.blocks_size(), 1U)<< "Expected one block in Sec3 after section merge";EXPECT_EQ(Sec3.symbols_size(), 1U)<< "Expected one symbol in Sec3 after section merge";G.mergeSections(Sec1, Sec3, true);EXPECT_EQ(G.sections_size(), 2U) << "Expected two sections after third merge";EXPECT_EQ(Sec1.blocks_size(), 3U)<< "Expected three blocks in Sec1 after third merge";EXPECT_EQ(Sec1.symbols_size(), 3U)<< "Expected three symbols in Sec1 after third merge";EXPECT_EQ(Sec3.blocks_size(), 0U)<< "Expected one block in Sec3 after third merge";EXPECT_EQ(Sec3.symbols_size(), 0U)<< "Expected one symbol in Sec3 after third merge";}TEST(LinkGraphTest, SplitBlock) {// Check that the LinkGraph::splitBlock test works as expected.LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,getGenericEdgeKindName);auto &Sec = G.createSection("__data", MemProt::Read | MemProt::Write);// Create the block to split.orc::ExecutorAddr B1Addr(0x1000);auto &B1 = G.createContentBlock(Sec, BlockContent, B1Addr, 8, 0);// Add some symbols to the block.auto &S1 = G.addDefinedSymbol(B1, 0, "S1", 4, Linkage::Strong, Scope::Default,false, false);auto &S2 = G.addDefinedSymbol(B1, 4, "S2", 4, Linkage::Strong, Scope::Default,false, false);auto &S3 = G.addDefinedSymbol(B1, 8, "S3", 4, Linkage::Strong, Scope::Default,false, false);auto &S4 = G.addDefinedSymbol(B1, 12, "S4", 4, Linkage::Strong,Scope::Default, false, false);// Add a symbol that extends beyond the split.auto &S5 = G.addDefinedSymbol(B1, 0, "S5", 16, Linkage::Strong,Scope::Default, false, false);// Add an extra block, EB, and target symbols, and use these to add edges// from B1 to EB.orc::ExecutorAddr EBAddr(0x2000);auto &EB = G.createContentBlock(Sec, BlockContent, EBAddr, 8, 0);auto &ES1 = G.addDefinedSymbol(EB, 0, "TS1", 4, Linkage::Strong,Scope::Default, false, false);auto &ES2 = G.addDefinedSymbol(EB, 4, "TS2", 4, Linkage::Strong,Scope::Default, false, false);auto &ES3 = G.addDefinedSymbol(EB, 8, "TS3", 4, Linkage::Strong,Scope::Default, false, false);auto &ES4 = G.addDefinedSymbol(EB, 12, "TS4", 4, Linkage::Strong,Scope::Default, false, false);// Add edges from B1 to EB.B1.addEdge(Edge::FirstRelocation, 0, ES1, 0);B1.addEdge(Edge::FirstRelocation, 4, ES2, 0);B1.addEdge(Edge::FirstRelocation, 8, ES3, 0);B1.addEdge(Edge::FirstRelocation, 12, ES4, 0);// Split B1.auto &B2 = G.splitBlock(B1, 8);// Check that the block addresses and content matches what we would expect.EXPECT_EQ(B1.getAddress(), B1Addr + 8);EXPECT_EQ(B1.getContent(), BlockContent.slice(8));EXPECT_EQ(B2.getAddress(), B1Addr);EXPECT_EQ(B2.getContent(), BlockContent.slice(0, 8));// Check that symbols in B1 were transferred as expected:// We expect S1 and S2 to have been transferred to B2, and S3 and S4 to have// remained attached to B1. Symbols S3 and S4 should have had their offsets// slid to account for the change in address of B2.EXPECT_EQ(&S1.getBlock(), &B2);EXPECT_EQ(S1.getOffset(), 0U);EXPECT_EQ(&S2.getBlock(), &B2);EXPECT_EQ(S2.getOffset(), 4U);EXPECT_EQ(&S3.getBlock(), &B1);EXPECT_EQ(S3.getOffset(), 0U);EXPECT_EQ(&S4.getBlock(), &B1);EXPECT_EQ(S4.getOffset(), 4U);EXPECT_EQ(&S5.getBlock(), &B2);EXPECT_EQ(S5.getOffset(), 0U);// Size shrinks to fit.EXPECT_EQ(S5.getSize(), 8U);// Check that edges in B1 have been transferred as expected:// Both blocks should now have two edges each at offsets 0 and 4.EXPECT_EQ(llvm::size(B1.edges()), 2);if (size(B1.edges()) == 2) {auto *E1 = &*B1.edges().begin();auto *E2 = &*(B1.edges().begin() + 1);if (E2->getOffset() < E1->getOffset())std::swap(E1, E2);EXPECT_EQ(E1->getOffset(), 0U);EXPECT_EQ(E2->getOffset(), 4U);}EXPECT_EQ(llvm::size(B2.edges()), 2);if (size(B2.edges()) == 2) {auto *E1 = &*B2.edges().begin();auto *E2 = &*(B2.edges().begin() + 1);if (E2->getOffset() < E1->getOffset())std::swap(E1, E2);EXPECT_EQ(E1->getOffset(), 0U);EXPECT_EQ(E2->getOffset(), 4U);}}
set(LLVM_LINK_COMPONENTS${LLVM_TARGETS_TO_BUILD}JITLinkObjectOrcSharedOrcTargetProcessRuntimeDyldSupport)add_llvm_unittest(JITLinkTestsLinkGraphTests.cpp)target_link_libraries(JITLinkTests PRIVATE LLVMTestingSupport)set_property(TARGET JITLinkTests PROPERTY FOLDER "Tests/UnitTests/ExecutionTests")
//===- ExecutionEngineTest.cpp - Unit tests for ExecutionEngine -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/ExecutionEngine/Interpreter.h"#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/DynamicLibrary.h"#include "llvm/Support/ManagedStatic.h"#include "gtest/gtest.h"using namespace llvm;namespace {class ExecutionEngineTest : public testing::Test {private:llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.protected:ExecutionEngineTest() {auto Owner = std::make_unique<Module>("<main>", Context);M = Owner.get();Engine.reset(EngineBuilder(std::move(Owner)).setErrorStr(&Error).create());}void SetUp() override {ASSERT_TRUE(Engine.get() != nullptr) << "EngineBuilder returned error: '"<< Error << "'";}GlobalVariable *NewExtGlobal(Type *T, const Twine &Name) {return new GlobalVariable(*M, T, false, // Not constant.GlobalValue::ExternalLinkage, nullptr, Name);}std::string Error;LLVMContext Context;Module *M; // Owned by ExecutionEngine.std::unique_ptr<ExecutionEngine> Engine;};TEST_F(ExecutionEngineTest, ForwardGlobalMapping) {GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");int32_t Mem1 = 3;Engine->addGlobalMapping(G1, &Mem1);EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G1));EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable("Global1"));int32_t Mem2 = 4;Engine->updateGlobalMapping(G1, &Mem2);EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1));Engine->updateGlobalMapping(G1, nullptr);EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G1));Engine->updateGlobalMapping(G1, &Mem2);EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1));GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G2))<< "The NULL return shouldn't depend on having called"<< " updateGlobalMapping(..., NULL)";// Check that update...() can be called before add...().Engine->updateGlobalMapping(G2, &Mem1);EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G2));EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1))<< "A second mapping shouldn't affect the first.";}TEST_F(ExecutionEngineTest, ReverseGlobalMapping) {GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");int32_t Mem1 = 3;Engine->addGlobalMapping(G1, &Mem1);EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));int32_t Mem2 = 4;Engine->updateGlobalMapping(G1, &Mem2);EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2");Engine->updateGlobalMapping(G2, &Mem1);EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1));EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));Engine->updateGlobalMapping(G1, nullptr);EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1))<< "Removing one mapping doesn't affect a different one.";EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem2));Engine->updateGlobalMapping(G2, &Mem2);EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem2))<< "Once a mapping is removed, we can point another GV at the"<< " now-free address.";}TEST_F(ExecutionEngineTest, ClearModuleMappings) {GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");int32_t Mem1 = 3;Engine->addGlobalMapping(G1, &Mem1);EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));Engine->clearGlobalMappingsFromModule(M);EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2");// After clearing the module mappings, we can assign a new GV to the// same address.Engine->addGlobalMapping(G2, &Mem1);EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1));}TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) {GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");int32_t Mem1 = 3;Engine->addGlobalMapping(G1, &Mem1);// Make sure the reverse mapping is enabled.EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));// When the GV goes away, the ExecutionEngine should remove any// mappings that refer to it.G1->eraseFromParent();EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));}TEST_F(ExecutionEngineTest, LookupWithMangledAndDemangledSymbol) {int x;int _x;llvm::sys::DynamicLibrary::AddSymbol("x", &x);llvm::sys::DynamicLibrary::AddSymbol("_x", &_x);// RTDyldMemoryManager::getSymbolAddressInProcess expects a mangled symbol,// but DynamicLibrary is a wrapper for dlsym, which expects the unmangled C// symbol name. This test verifies that getSymbolAddressInProcess strips the// leading '_' on Darwin, but not on other platforms.#ifdef __APPLE__EXPECT_EQ(reinterpret_cast<uint64_t>(&x),RTDyldMemoryManager::getSymbolAddressInProcess("_x"));#elseEXPECT_EQ(reinterpret_cast<uint64_t>(&_x),RTDyldMemoryManager::getSymbolAddressInProcess("_x"));#endif}}
set(LLVM_LINK_COMPONENTSCoreExecutionEngineInterpreterMCOrcJITRuntimeDyldSupport)add_llvm_unittest(ExecutionEngineTestsExecutionEngineTest.cpp)add_subdirectory(JITLink)add_subdirectory(Orc)# Include MCJIT tests only if native arch is a built JIT target.list(FIND LLVM_TARGETS_TO_BUILD "${LLVM_NATIVE_ARCH}" build_idx)list(FIND LLVM_TARGETS_WITH_JIT "${LLVM_NATIVE_ARCH}" jit_idx)if (NOT build_idx LESS 0 AND NOT jit_idx LESS 0)add_subdirectory(MCJIT)endif()set_property(TARGET ExecutionEngineTests PROPERTY FOLDER "Tests/UnitTests/ExecutionTests")
//===- llvm/unittest/StringViewTest.cpp - StringView unit tests -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Demangle/StringView.h"#include "gtest/gtest.h"using namespace llvm;using llvm::itanium_demangle::StringView;namespace llvm {namespace itanium_demangle {std::ostream &operator<<(std::ostream &OS, const StringView &S) {return OS.write(S.begin(), S.size());}} // namespace itanium_demangle} // namespace llvmTEST(StringViewTest, EmptyInitializerList) {StringView S = {};EXPECT_TRUE(S.empty());S = {};EXPECT_TRUE(S.empty());}TEST(StringViewTest, Substr) {StringView S("abcdef");EXPECT_EQ("abcdef", S.substr(0));EXPECT_EQ("f", S.substr(5));EXPECT_EQ("", S.substr(6));EXPECT_EQ("", S.substr(0, 0));EXPECT_EQ("a", S.substr(0, 1));EXPECT_EQ("abcde", S.substr(0, 5));EXPECT_EQ("abcdef", S.substr(0, 6));EXPECT_EQ("abcdef", S.substr(0, 7));EXPECT_EQ("f", S.substr(5, 100));EXPECT_EQ("", S.substr(6, 100));}
//===------------------ RustDemangleTest.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Demangle/Demangle.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <cstdlib>TEST(RustDemangle, Success) {char *Demangled = llvm::rustDemangle("_RNvC1a4main");EXPECT_STREQ(Demangled, "a::main");std::free(Demangled);}TEST(RustDemangle, Invalid) {char *Demangled = nullptr;// Invalid prefix.Demangled = llvm::rustDemangle("_ABCDEF");EXPECT_EQ(Demangled, nullptr);// Correct prefix but still invalid.Demangled = llvm::rustDemangle("_RRR");EXPECT_EQ(Demangled, nullptr);}
//===----------------------- PartialDemangleTest.cpp ----------------------===////// 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////===----------------------------------------------------------------------===//#include <cstdlib>#include "llvm/Demangle/Demangle.h"#include "gtest/gtest.h"struct ChoppedName {const char *Mangled;const char *ContextName, *BaseName, *ReturnType, *Params;};static ChoppedName NamesToTest[] = {{"_Z1fv", "", "f", "", "()"},{"_ZN1a1b1cIiiiEEvm", "a::b", "c", "void", "(unsigned long)"},{"_ZZ5OuterIiEivEN5Inner12inner_memberEv","int Outer<int>()::Inner", "inner_member", "", "()"},{"_Z1fIiEPFvvEv", "", "f", "void (*)()", "()"},{"_ZN1S1fIiEEvv", "S", "f", "void", "()"},// Call operator for a lambda in f().{"_ZZ1fvENK3$_0clEi", "f()::$_0", "operator()", "", "(int)"},// A call operator for a lambda in a lambda in f().{"_ZZZ1fvENK3$_0clEvENKUlvE_clEv","f()::$_0::operator()() const::'lambda'()", "operator()", "", "()"},{"_ZZN1S1fEiiEd0_NKUlvE_clEv","S::f(int, int)::'lambda'()", "operator()", "", "()"},{"_ZN1Scv7MuncherIJDpPT_EEIJFivEA_iEEEv","S", "operator Muncher<int (*)(), int (*) []>", "", "()"},// Attributes.{"_ZN5test4IdE1fEUa9enable_ifIXeqfL0p_Li1EEXeqfL0p0_Li2EEEi","test4<double>", "f", "", "(int)"},{"_ZN1SC2B8ctor_tagEv", "S", "S", "", "()"},{"_ZN1S1fB4MERPIiEEvv", "S", "f", "void", "()"},{"_ZNSsC1EmcRKSaIcE","std::basic_string<char, std::char_traits<char>, std::allocator<char>>","basic_string", "", "(unsigned long, char, std::allocator<char> const&)"},{"_ZNSsixEm", "std::string", "operator[]", "", "(unsigned long)"},{"_ZSt17__throw_bad_allocv", "std", "__throw_bad_alloc", "", "()"},{"_ZN1AI1BEC2Ev", "A<B>", "A", "", "()"},{"_ZN1AI1BED2Ev", "A<B>", "~A", "", "()"},{"_ZN1AI1BECI24BaseEi", "A<B>", "A", "", "(int)"},{"_ZNKR1AI1BE1fIiEEiv", "A<B>", "f", "int", "()"},{"_ZN1SIJicfEE3mfnIJjcdEEEvicfDpT_", "S<int, char, float>","mfn", "void", "(int, char, float, unsigned int, char, double)"},};TEST(PartialDemanglerTest, TestNameChopping) {size_t Size = 1;char *Buf = static_cast<char *>(std::malloc(Size));llvm::ItaniumPartialDemangler D;for (ChoppedName &N : NamesToTest) {EXPECT_FALSE(D.partialDemangle(N.Mangled));EXPECT_TRUE(D.isFunction());EXPECT_FALSE(D.isData());EXPECT_FALSE(D.isSpecialName());Buf = D.getFunctionDeclContextName(Buf, &Size);EXPECT_STREQ(Buf, N.ContextName);Buf = D.getFunctionBaseName(Buf, &Size);EXPECT_STREQ(Buf, N.BaseName);Buf = D.getFunctionReturnType(Buf, &Size);EXPECT_STREQ(Buf, N.ReturnType);Buf = D.getFunctionParameters(Buf, &Size);EXPECT_STREQ(Buf, N.Params);}std::free(Buf);}TEST(PartialDemanglerTest, TestNameMeta) {llvm::ItaniumPartialDemangler Demangler;EXPECT_FALSE(Demangler.partialDemangle("_ZNK1f1gEv"));EXPECT_TRUE(Demangler.isFunction());EXPECT_TRUE(Demangler.hasFunctionQualifiers());EXPECT_FALSE(Demangler.isSpecialName());EXPECT_FALSE(Demangler.isData());EXPECT_FALSE(Demangler.partialDemangle("_Z1fv"));EXPECT_FALSE(Demangler.hasFunctionQualifiers());EXPECT_FALSE(Demangler.partialDemangle("_ZTV1S"));EXPECT_TRUE(Demangler.isSpecialName());EXPECT_FALSE(Demangler.isData());EXPECT_FALSE(Demangler.isFunction());EXPECT_FALSE(Demangler.partialDemangle("_ZN1aDC1a1b1cEE"));EXPECT_FALSE(Demangler.isFunction());EXPECT_FALSE(Demangler.isSpecialName());EXPECT_TRUE(Demangler.isData());}TEST(PartialDemanglerTest, TestCtorOrDtor) {static const char *Pos[] = {"_ZN1AC1Ev", // A::A()"_ZN1AC1IiEET_", // A::A<int>(int)"_ZN1AD2Ev", // A::~A()"_ZN1BIiEC1IcEET_", // B<int>::B<char>(char)"_ZN1AC1B1TEv", // A::A[abi:T]()"_ZNSt1AD2Ev", // std::A::~A()"_ZN2ns1AD1Ev", // ns::A::~A()};static const char *Neg[] = {"_Z1fv","_ZN1A1gIiEEvT_", // void A::g<int>(int)};llvm::ItaniumPartialDemangler D;for (const char *N : Pos) {EXPECT_FALSE(D.partialDemangle(N));EXPECT_TRUE(D.isCtorOrDtor());}for (const char *N : Neg) {EXPECT_FALSE(D.partialDemangle(N));EXPECT_FALSE(D.isCtorOrDtor());}}TEST(PartialDemanglerTest, TestMisc) {llvm::ItaniumPartialDemangler D1, D2;EXPECT_FALSE(D1.partialDemangle("_Z1fv"));EXPECT_FALSE(D2.partialDemangle("_Z1g"));std::swap(D1, D2);EXPECT_FALSE(D1.isFunction());EXPECT_TRUE(D2.isFunction());EXPECT_TRUE(D1.partialDemangle("Not a mangled name!"));}TEST(PartialDemanglerTest, TestPrintCases) {llvm::ItaniumPartialDemangler D;const size_t OriginalSize = 4;char *Buf = static_cast<char *>(std::malloc(OriginalSize));const char *OriginalBuf = Buf;// Default success case: Result fits into the given buffer.// Res points to Buf. N returns string size including null termination.{EXPECT_FALSE(D.partialDemangle("_ZN1a1bEv"));size_t N = OriginalSize;char *Res = D.getFunctionDeclContextName(Buf, &N);EXPECT_STREQ("a", Res);EXPECT_EQ(OriginalBuf, Res);EXPECT_EQ(strlen(Res) + 1, N);}// Realloc success case: Result does not fit into the given buffer.// Res points to the new or extended buffer. N returns string size// including null termination. Buf was extended or freed.{EXPECT_FALSE(D.partialDemangle("_ZN1a1b1cIiiiEEvm"));size_t N = OriginalSize;char *Res = D.finishDemangle(Buf, &N);EXPECT_STREQ("void a::b::c<int, int, int>(unsigned long)", Res);EXPECT_EQ(strlen(Res) + 1, N);Buf = Res;}// Failure case: a::c is not a function.// Res is nullptr. N remains unchanged.{EXPECT_FALSE(D.partialDemangle("_ZN1a1cE"));size_t N = OriginalSize;char *Res = D.getFunctionName(Buf, &N);EXPECT_EQ(nullptr, Res);EXPECT_EQ(OriginalSize, N);}std::free(Buf);}
//===- llvm/unittest/OutputBufferTest.cpp - OutputStream unit tests -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Demangle/MicrosoftDemangleNodes.h"#include "llvm/Demangle/Utility.h"#include "gtest/gtest.h"#include <string>using namespace llvm;using llvm::itanium_demangle::OutputBuffer;static std::string toString(OutputBuffer &OB) {StringView SV = OB;return {SV.begin(), SV.end()};}template <typename T> static std::string printToString(const T &Value) {OutputBuffer OB;OB << Value;std::string s = toString(OB);std::free(OB.getBuffer());return s;}TEST(OutputBufferTest, Format) {EXPECT_EQ("0", printToString(0));EXPECT_EQ("1", printToString(1));EXPECT_EQ("-1", printToString(-1));EXPECT_EQ("-90", printToString(-90));EXPECT_EQ("109", printToString(109));EXPECT_EQ("400", printToString(400));EXPECT_EQ("a", printToString('a'));EXPECT_EQ("?", printToString('?'));EXPECT_EQ("abc", printToString("abc"));}TEST(OutputBufferTest, Insert) {OutputBuffer OB;OB.insert(0, "", 0);EXPECT_EQ("", toString(OB));OB.insert(0, "abcd", 4);EXPECT_EQ("abcd", toString(OB));OB.insert(0, "x", 1);EXPECT_EQ("xabcd", toString(OB));OB.insert(5, "y", 1);EXPECT_EQ("xabcdy", toString(OB));OB.insert(3, "defghi", 6);EXPECT_EQ("xabdefghicdy", toString(OB));std::free(OB.getBuffer());}TEST(OutputBufferTest, Prepend) {OutputBuffer OB;OB.prepend("n");EXPECT_EQ("n", toString(OB));OB << "abc";OB.prepend("def");EXPECT_EQ("defnabc", toString(OB));OB.setCurrentPosition(3);OB.prepend("abc");EXPECT_EQ("abcdef", toString(OB));std::free(OB.getBuffer());}// Test when initial needed size is larger than the default.TEST(OutputBufferTest, Extend) {OutputBuffer OB;char Massive[2000];std::memset(Massive, 'a', sizeof(Massive));Massive[sizeof(Massive) - 1] = 0;OB << Massive;EXPECT_EQ(Massive, toString(OB));std::free(OB.getBuffer());}
//===------------------ ItaniumDemangleTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Demangle/ItaniumDemangle.h"#include "llvm/Support/Allocator.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <cstdlib>#include <vector>using namespace llvm;using namespace llvm::itanium_demangle;namespace {class TestAllocator {BumpPtrAllocator Alloc;public:void reset() { Alloc.Reset(); }template <typename T, typename... Args> T *makeNode(Args &&... args) {return new (Alloc.Allocate(sizeof(T), alignof(T)))T(std::forward<Args>(args)...);}void *allocateNodeArray(size_t sz) {return Alloc.Allocate(sizeof(Node *) * sz, alignof(Node *));}};} // namespacenamespace NodeMatcher {// Make sure the node matchers provide constructor parameters. This is a// compilation test.template <typename NT> struct Ctor {template <typename... Args> void operator()(Args &&...args) {auto _ = NT(std::forward<Args>(args)...);}};template <typename NT> void Visit(const NT *Node) { Node->match(Ctor<NT>{}); }#define NOMATCHER(X) \template <> void Visit<itanium_demangle::X>(const itanium_demangle::X *) {}// Some nodes have no match member.NOMATCHER(ForwardTemplateReference)#undef NOMATCHERvoid Visitor() {#define NODE(X) Visit(static_cast<const itanium_demangle::X *>(nullptr));#include "llvm/Demangle/ItaniumNodes.def"}} // namespace NodeMatcher// Verify Operator table is orderedTEST(ItaniumDemangle, OperatorOrdering) {struct TestParser : AbstractManglingParser<TestParser, TestAllocator> {};for (const auto *Op = &TestParser::Ops[0];Op != &TestParser::Ops[TestParser::NumOps - 1]; Op++)ASSERT_LT(Op[0], Op[1]);}TEST(ItaniumDemangle, MethodOverride) {struct TestParser : AbstractManglingParser<TestParser, TestAllocator> {std::vector<char> Types;TestParser(const char *Str): AbstractManglingParser(Str, Str + strlen(Str)) {}Node *parseType() {Types.push_back(*First);return AbstractManglingParser<TestParser, TestAllocator>::parseType();}};TestParser Parser("_Z1fIiEjl");ASSERT_NE(nullptr, Parser.parse());EXPECT_THAT(Parser.Types, testing::ElementsAre('i', 'j', 'l'));}static std::string toString(OutputBuffer &OB) {StringView SV = OB;return {SV.begin(), SV.end()};}TEST(ItaniumDemangle, HalfType) {struct TestParser : AbstractManglingParser<TestParser, TestAllocator> {std::vector<std::string> Types;TestParser(const char *Str): AbstractManglingParser(Str, Str + strlen(Str)) {}Node *parseType() {OutputBuffer OB;Node *N = AbstractManglingParser<TestParser, TestAllocator>::parseType();N->printLeft(OB);StringView Name = N->getBaseName();if (!Name.empty())Types.push_back(std::string(Name.begin(), Name.end()));elseTypes.push_back(toString(OB));std::free(OB.getBuffer());return N;}};// void f(A<_Float16>, _Float16);TestParser Parser("_Z1f1AIDF16_EDF16_");ASSERT_NE(nullptr, Parser.parse());EXPECT_THAT(Parser.Types, testing::ElementsAre("_Float16", "A", "_Float16"));}
//===-- DemangleTest.cpp --------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Demangle/Demangle.h"#include "gmock/gmock.h"using namespace llvm;TEST(Demangle, demangleTest) {EXPECT_EQ(demangle("_"), "_");EXPECT_EQ(demangle("_Z3fooi"), "foo(int)");EXPECT_EQ(demangle("__Z3fooi"), "foo(int)");EXPECT_EQ(demangle("___Z3fooi_block_invoke"),"invocation function for block in foo(int)");EXPECT_EQ(demangle("____Z3fooi_block_invoke"),"invocation function for block in foo(int)");EXPECT_EQ(demangle("?foo@@YAXH@Z"), "void __cdecl foo(int)");EXPECT_EQ(demangle("foo"), "foo");EXPECT_EQ(demangle("_RNvC3foo3bar"), "foo::bar");EXPECT_EQ(demangle("__RNvC3foo3bar"), "foo::bar");EXPECT_EQ(demangle("_Dmain"), "D main");// Regression test for demangling of optional template-args for vendor// extended type qualifier (https://bugs.llvm.org/show_bug.cgi?id=48009)EXPECT_EQ(demangle("_Z3fooILi79EEbU7_ExtIntIXT_EEi"),"bool foo<79>(int _ExtInt<79>)");}
//===------------------ DLangDemangleTest.cpp -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Demangle/Demangle.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <cstdlib>#include <utility>struct DLangDemangleTestFixture: public testing::TestWithParam<std::pair<const char *, const char *>> {char *Demangled;void SetUp() override { Demangled = llvm::dlangDemangle(GetParam().first); }void TearDown() override { std::free(Demangled); }};TEST_P(DLangDemangleTestFixture, DLangDemangleTest) {EXPECT_STREQ(Demangled, GetParam().second);}INSTANTIATE_TEST_SUITE_P(DLangDemangleTest, DLangDemangleTestFixture,testing::Values(std::make_pair("_Dmain", "D main"), std::make_pair(nullptr, nullptr),std::make_pair("_Z", nullptr), std::make_pair("_DDD", nullptr),std::make_pair("_D88", nullptr),std::make_pair("_D8demangleZ", "demangle"),std::make_pair("_D8demangle4testZ", "demangle.test"),std::make_pair("_D8demangle4test5test2Z", "demangle.test.test2"),std::make_pair("_D8demangle4test0Z", "demangle.test"),std::make_pair("_D8demangle4test03fooZ", "demangle.test.foo"),std::make_pair("_D8demangle4test6__initZ","initializer for demangle.test"),std::make_pair("_D8demangle4test6__vtblZ", "vtable for demangle.test"),std::make_pair("_D8demangle4test7__ClassZ","ClassInfo for demangle.test"),std::make_pair("_D8demangle4test11__InterfaceZ","Interface for demangle.test"),std::make_pair("_D8demangle4test12__ModuleInfoZ","ModuleInfo for demangle.test"),std::make_pair("_D8demangle4__S14testZ", "demangle.test"),std::make_pair("_D8demangle4__Sd4testZ", "demangle.__Sd.test"),std::make_pair("_D8demangle3fooi", "demangle.foo"),std::make_pair("_D8demangle3foo",nullptr), // symbol without a type sequence.std::make_pair("_D8demangle3fooinvalidtypeseq",nullptr), // invalid type sequence.std::make_pair("_D8demangle3ABCQe1ai","demangle.ABC.ABC.a"), // symbol back reference: `Qe` is a back// reference for position 5, counting from e// char, so decoding it points to `3`. Since// `3` is a number, 3 chars get read and it// succeeded.std::make_pair("_D8demangle3ABCQa1ai",nullptr), // invalid symbol back reference (recursive).std::make_pair("_D8demangleQDXXXXXXXXXXXXx",nullptr), // overflow back reference position.std::make_pair("_D8demangle4ABCi1aQd","demangle.ABCi.a"), // type back reference: `Qd` is a back reference// for position 4, counting from `d` char, so// decoding it points to `i`.std::make_pair("_D8demangle3fooQXXXx",nullptr), // invalid type back reference position.std::make_pair("_D8demangle5recurQa",nullptr))); // invalid type back reference (recursive).
set(LLVM_LINK_COMPONENTSDemangleSupport)add_llvm_unittest(DemangleTestsDemangleTest.cppDLangDemangleTest.cppItaniumDemangleTest.cppOutputBufferTest.cppPartialDemangleTest.cppRustDemangleTest.cppStringViewTest.cpp)
//===-- llvm/unittest/Support/HTTPServer.cpp - unit tests -------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/Debuginfod/HTTPClient.h"#include "llvm/Debuginfod/HTTPServer.h"#include "llvm/Support/Error.h"#include "llvm/Support/ThreadPool.h"#include "llvm/Testing/Support/Error.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;#ifdef LLVM_ENABLE_HTTPLIBTEST(HTTPServer, IsAvailable) { EXPECT_TRUE(HTTPServer::isAvailable()); }HTTPResponse Response = {200u, "text/plain", "hello, world\n"};std::string UrlPathPattern = R"(/(.*))";std::string InvalidUrlPathPattern = R"(/(.*)";HTTPRequestHandler Handler = [](HTTPServerRequest &Request) {Request.setResponse(Response);};HTTPRequestHandler DelayHandler = [](HTTPServerRequest &Request) {std::this_thread::sleep_for(std::chrono::milliseconds(50));Request.setResponse(Response);};HTTPRequestHandler StreamingHandler = [](HTTPServerRequest &Request) {Request.setResponse({200, "text/plain", Response.Body.size(),[=](size_t Offset, size_t Length) -> StringRef {return Response.Body.substr(Offset, Length);}});};TEST(HTTPServer, InvalidUrlPath) {// test that we can bind to any addressHTTPServer Server;EXPECT_THAT_ERROR(Server.get(InvalidUrlPathPattern, Handler),Failed<StringError>());EXPECT_THAT_EXPECTED(Server.bind(), Succeeded());}TEST(HTTPServer, bind) {// test that we can bind to any addressHTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern, Handler), Succeeded());EXPECT_THAT_EXPECTED(Server.bind(), Succeeded());}TEST(HTTPServer, ListenBeforeBind) {// test that we can bind to any addressHTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern, Handler), Succeeded());EXPECT_THAT_ERROR(Server.listen(), Failed<StringError>());}#ifdef LLVM_ENABLE_CURL// Test the client and server against each other.// Test fixture to initialize and teardown the HTTP client for each// client-server testclass HTTPClientServerTest : public ::testing::Test {protected:void SetUp() override { HTTPClient::initialize(); }void TearDown() override { HTTPClient::cleanup(); }};/// A simple handler which writes returned data to a string.struct StringHTTPResponseHandler final : public HTTPResponseHandler {std::string ResponseBody = "";/// These callbacks store the body and status code in an HTTPResponseBuffer/// allocated based on Content-Length. The Content-Length header must be/// handled by handleHeaderLine before any calls to handleBodyChunk.Error handleBodyChunk(StringRef BodyChunk) override {ResponseBody = ResponseBody + BodyChunk.str();return Error::success();}};TEST_F(HTTPClientServerTest, Hello) {HTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern, Handler), Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port);HTTPRequest Request(Url);StringHTTPResponseHandler Handler;HTTPClient Client;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());EXPECT_EQ(Handler.ResponseBody, Response.Body);EXPECT_EQ(Client.responseCode(), Response.Code);Server.stop();}TEST_F(HTTPClientServerTest, LambdaHandlerHello) {HTTPServer Server;HTTPResponse LambdaResponse = {200u, "text/plain","hello, world from a lambda\n"};EXPECT_THAT_ERROR(Server.get(UrlPathPattern,[LambdaResponse](HTTPServerRequest &Request) {Request.setResponse(LambdaResponse);}),Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port);HTTPRequest Request(Url);StringHTTPResponseHandler Handler;HTTPClient Client;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());EXPECT_EQ(Handler.ResponseBody, LambdaResponse.Body);EXPECT_EQ(Client.responseCode(), LambdaResponse.Code);Server.stop();}// Test the streaming response.TEST_F(HTTPClientServerTest, StreamingHello) {HTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern, StreamingHandler), Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port);HTTPRequest Request(Url);StringHTTPResponseHandler Handler;HTTPClient Client;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());EXPECT_EQ(Handler.ResponseBody, Response.Body);EXPECT_EQ(Client.responseCode(), Response.Code);Server.stop();}// Writes a temporary file and streams it back using streamFile.HTTPRequestHandler TempFileStreamingHandler = [](HTTPServerRequest Request) {int FD;SmallString<64> TempFilePath;sys::fs::createTemporaryFile("http-stream-file-test", "temp", FD,TempFilePath);raw_fd_ostream OS(FD, true, /*unbuffered=*/true);OS << Response.Body;OS.close();streamFile(Request, TempFilePath);};// Test streaming back chunks of a file.TEST_F(HTTPClientServerTest, StreamingFileResponse) {HTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern, TempFileStreamingHandler),Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port);HTTPRequest Request(Url);StringHTTPResponseHandler Handler;HTTPClient Client;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());EXPECT_EQ(Handler.ResponseBody, Response.Body);EXPECT_EQ(Client.responseCode(), Response.Code);Server.stop();}// Deletes the temporary file before streaming it back, should give a 404 not// found status code.HTTPRequestHandler MissingTempFileStreamingHandler =[](HTTPServerRequest Request) {int FD;SmallString<64> TempFilePath;sys::fs::createTemporaryFile("http-stream-file-test", "temp", FD,TempFilePath);raw_fd_ostream OS(FD, true, /*unbuffered=*/true);OS << Response.Body;OS.close();// delete the filesys::fs::remove(TempFilePath);streamFile(Request, TempFilePath);};// Streaming a missing file should give a 404.TEST_F(HTTPClientServerTest, StreamingMissingFileResponse) {HTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern, MissingTempFileStreamingHandler),Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port);HTTPRequest Request(Url);StringHTTPResponseHandler Handler;HTTPClient Client;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());EXPECT_EQ(Client.responseCode(), 404u);Server.stop();}TEST_F(HTTPClientServerTest, ClientTimeout) {HTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern, DelayHandler), Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port);HTTPClient Client;// Timeout below 50ms, request should failClient.setTimeout(std::chrono::milliseconds(40));HTTPRequest Request(Url);StringHTTPResponseHandler Handler;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Failed<StringError>());Server.stop();}// Check that Url paths are dispatched to the first matching handler and provide// the correct path pattern match components.TEST_F(HTTPClientServerTest, PathMatching) {HTTPServer Server;EXPECT_THAT_ERROR(Server.get(R"(/abc/(.*)/(.*))",[&](HTTPServerRequest &Request) {EXPECT_EQ(Request.UrlPath, "/abc/1/2");ASSERT_THAT(Request.UrlPathMatches,testing::ElementsAre("1", "2"));Request.setResponse({200u, "text/plain", Request.UrlPath});}),Succeeded());EXPECT_THAT_ERROR(Server.get(UrlPathPattern,[&](HTTPServerRequest &Request) {llvm_unreachable("Should not reach this handler");Handler(Request);}),Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port) + "/abc/1/2";HTTPRequest Request(Url);StringHTTPResponseHandler Handler;HTTPClient Client;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());EXPECT_EQ(Handler.ResponseBody, "/abc/1/2");EXPECT_EQ(Client.responseCode(), 200u);Server.stop();}TEST_F(HTTPClientServerTest, FirstPathMatched) {HTTPServer Server;EXPECT_THAT_ERROR(Server.get(UrlPathPattern,[&](HTTPServerRequest Request) { Handler(Request); }),Succeeded());EXPECT_THAT_ERROR(Server.get(R"(/abc/(.*)/(.*))",[&](HTTPServerRequest Request) {EXPECT_EQ(Request.UrlPathMatches.size(), 2u);llvm_unreachable("Should not reach this handler");Request.setResponse({200u, "text/plain", Request.UrlPath});}),Succeeded());Expected<unsigned> PortOrErr = Server.bind();EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());unsigned Port = *PortOrErr;ThreadPool Pool(hardware_concurrency(1));Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });std::string Url = "http://localhost:" + utostr(Port) + "/abc/1/2";HTTPRequest Request(Url);StringHTTPResponseHandler Handler;HTTPClient Client;EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());EXPECT_EQ(Handler.ResponseBody, Response.Body);EXPECT_EQ(Client.responseCode(), Response.Code);Server.stop();}#endif#elseTEST(HTTPServer, IsAvailable) { EXPECT_FALSE(HTTPServer::isAvailable()); }#endif // LLVM_ENABLE_HTTPLIB
//===-- llvm/unittest/Support/DebuginfodTests.cpp - unit tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Debuginfod/Debuginfod.h"#include "llvm/Debuginfod/HTTPClient.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#ifdef _WIN32#define setenv(name, var, ignore) _putenv_s(name, var)#endif#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}using namespace llvm;// Check that the Debuginfod client can find locally cached artifacts.TEST(DebuginfodClient, CacheHit) {int FD;SmallString<64> CachedFilePath;sys::fs::createTemporaryFile("llvmcache-key", "temp", FD, CachedFilePath);StringRef CacheDir = sys::path::parent_path(CachedFilePath);StringRef UniqueKey = sys::path::filename(CachedFilePath);EXPECT_TRUE(UniqueKey.consume_front("llvmcache-"));raw_fd_ostream OF(FD, true, /*unbuffered=*/true);OF << "contents\n";OF << CacheDir << "\n";OF.close();Expected<std::string> PathOrErr = getCachedOrDownloadArtifact(UniqueKey, /*UrlPath=*/"/null", CacheDir,/*DebuginfodUrls=*/{}, /*Timeout=*/std::chrono::milliseconds(1));EXPECT_THAT_EXPECTED(PathOrErr, HasValue(CachedFilePath));}// Check that the Debuginfod client returns an Error when it fails to find an// artifact.TEST(DebuginfodClient, CacheMiss) {SmallString<32> CacheDir;ASSERT_NO_ERROR(sys::fs::createUniqueDirectory("debuginfod-unittest", CacheDir));sys::path::append(CacheDir, "cachedir");ASSERT_FALSE(sys::fs::exists(CacheDir));setenv("DEBUGINFOD_CACHE_PATH", CacheDir.c_str(),/*replace=*/1);// Ensure there are no urls to guarantee a cache miss.setenv("DEBUGINFOD_URLS", "", /*replace=*/1);HTTPClient::initialize();Expected<std::string> PathOrErr = getCachedOrDownloadArtifact(/*UniqueKey=*/"nonexistent-key", /*UrlPath=*/"/null");EXPECT_THAT_EXPECTED(PathOrErr, Failed<StringError>());// A cache miss with no possible URLs should not create the cache directory.EXPECT_FALSE(sys::fs::exists(CacheDir));}
add_llvm_unittest(DebuginfodTestsHTTPServerTests.cppDebuginfodTests.cpp)target_link_libraries(DebuginfodTests PRIVATELLVMDebuginfodLLVMTestingSupport)
//===- unittest/DebugInfo/Symbolizer/MarkupTest.cpp - Markup parser tests -===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/Symbolize/Markup.h"#include "llvm/ADT/Optional.h"#include "llvm/ADT/SmallString.h"#include "llvm/ADT/Twine.h"#include "llvm/Support/FormatVariadic.h"#include "gmock/gmock.h"#include "gtest/gtest.h"namespace {using namespace llvm;using namespace llvm::symbolize;using namespace testing;Matcher<MarkupNode> isNode(StringRef Text, StringRef Tag = "",Matcher<SmallVector<StringRef>> Fields = IsEmpty()) {return AllOf(Field("Text", &MarkupNode::Text, Text),Field("Tag", &MarkupNode::Tag, Tag),Field("Fields", &MarkupNode::Fields, Fields));}TEST(SymbolizerMarkup, NoLines) { EXPECT_EQ(MarkupParser{}.nextNode(), None); }TEST(SymbolizerMarkup, LinesWithoutMarkup) {MarkupParser Parser;Parser.parseLine("text");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("text")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("discarded");Parser.parseLine("kept");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("kept")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("text\n");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("text\n")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("text\r\n");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("text\r\n")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{}}")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{}}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{}}}")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{}}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{}}}")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{:field}}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{:field}}}")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{tag:");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{tag:")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{tag:field}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{tag:field}}")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("a\033[2mb");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("a\033[2mb")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("a\033[38mb");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("a\033[38mb")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("a\033[4mb");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("a\033[4mb")));EXPECT_THAT(Parser.nextNode(), None);}TEST(SymbolizerMarkup, LinesWithMarkup) {MarkupParser Parser;Parser.parseLine("{{{tag}}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{tag}}}", "tag")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{tag:f1:f2:f3}}}");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{tag:f1:f2:f3}}}", "tag",ElementsAre("f1", "f2", "f3"))));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{tag:}}}");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{tag:}}}", "tag", ElementsAre(""))));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{tag:}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{tag:}}")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{t2g}}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{t2g}}}", "t2g")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{tAg}}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{tAg}}}", "tAg")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("a{{{b}}}c{{{d}}}e");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("a")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{b}}}", "b")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("c")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{d}}}", "d")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("e")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{}}}{{{tag}}}");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{}}}")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{tag}}}", "tag")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("\033[0mA\033[1mB\033[30mC\033[37m");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("\033[0m")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("A")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("\033[1m")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("B")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("\033[30m")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("C")));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("\033[37m")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{tag:\033[0m}}}");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{tag:\033[0m}}}", "tag", ElementsAre("\033[0m"))));EXPECT_THAT(Parser.nextNode(), None);}TEST(SymbolizerMarkup, MultilineElements) {MarkupParser Parser(/*MultilineTags=*/{"first", "second"});Parser.parseLine("{{{tag:");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{tag:")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{first:");EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("}}}{{{second:");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{first:}}}", "first", ElementsAre(""))));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("}}}");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{second:}}}", "second", ElementsAre(""))));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{before{{{first:");EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{before")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("line");EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("}}}after");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{first:line}}}", "first", ElementsAre("line"))));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("after")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{first:");EXPECT_THAT(Parser.nextNode(), None);Parser.flush();EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("{{{first:")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{first:\n");EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("}}}\n");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{first:\n}}}", "first", ElementsAre("\n"))));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("\n")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{first:\r\n");EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("}}}\r\n");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{first:\r\n}}}", "first", ElementsAre("\r\n"))));EXPECT_THAT(Parser.nextNode(), testing::Optional(isNode("\r\n")));EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("{{{first:");EXPECT_THAT(Parser.nextNode(), None);Parser.parseLine("\033[0m}}}");EXPECT_THAT(Parser.nextNode(),testing::Optional(isNode("{{{first:\033[0m}}}", "first",ElementsAre("\033[0m"))));EXPECT_THAT(Parser.nextNode(), None);}} // namespace
set(LLVM_LINK_COMPONENTS Symbolize)add_llvm_unittest(DebugInfoSymbolizerTestsMarkupTest.cpp)target_link_libraries(DebugInfoSymbolizerTests PRIVATE LLVMTestingSupport)
//===- StringTableBuilderTest.cpp -----------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"#include "llvm/Support/BinaryByteStream.h"#include "llvm/Support/BinaryStreamReader.h"#include "llvm/Support/BinaryStreamWriter.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::pdb;using namespace llvm::support;TEST(StringTableBuilderTest, Simple) {// Create /names table contents.PDBStringTableBuilder Builder;// This test case is carefully constructed to ensure that at least one// string gets bucketed into slot 0, *and* to ensure that at least one// has a hash collision at the end of the bucket list so it has to// wrap around.uint32_t FooID = Builder.insert("foo");uint32_t BarID = Builder.insert("bar");uint32_t BazID = Builder.insert("baz");uint32_t BuzzID = Builder.insert("buzz");uint32_t BazzID = Builder.insert("bazz");uint32_t BarrID = Builder.insert("barr");// Re-inserting the same item should return the same id.EXPECT_EQ(FooID, Builder.insert("foo"));EXPECT_EQ(BarID, Builder.insert("bar"));EXPECT_EQ(BazID, Builder.insert("baz"));EXPECT_EQ(BuzzID, Builder.insert("buzz"));EXPECT_EQ(BazzID, Builder.insert("bazz"));EXPECT_EQ(BarrID, Builder.insert("barr"));// Each ID should be distinct.std::set<uint32_t> Distinct{FooID, BarID, BazID, BuzzID, BazzID, BarrID};EXPECT_EQ(6U, Distinct.size());std::vector<uint8_t> Buffer(Builder.calculateSerializedSize());MutableBinaryByteStream OutStream(Buffer, little);BinaryStreamWriter Writer(OutStream);EXPECT_THAT_ERROR(Builder.commit(Writer), Succeeded());// Reads the contents back.BinaryByteStream InStream(Buffer, little);BinaryStreamReader Reader(InStream);PDBStringTable Table;EXPECT_THAT_ERROR(Table.reload(Reader), Succeeded());EXPECT_EQ(6U, Table.getNameCount());EXPECT_EQ(1U, Table.getHashVersion());EXPECT_THAT_EXPECTED(Table.getStringForID(FooID), HasValue("foo"));EXPECT_THAT_EXPECTED(Table.getStringForID(BarID), HasValue("bar"));EXPECT_THAT_EXPECTED(Table.getStringForID(BazID), HasValue("baz"));EXPECT_THAT_EXPECTED(Table.getStringForID(BuzzID), HasValue("buzz"));EXPECT_THAT_EXPECTED(Table.getStringForID(BazzID), HasValue("bazz"));EXPECT_THAT_EXPECTED(Table.getStringForID(BarrID), HasValue("barr"));EXPECT_THAT_EXPECTED(Table.getIDForString("foo"), HasValue(FooID));EXPECT_THAT_EXPECTED(Table.getIDForString("bar"), HasValue(BarID));EXPECT_THAT_EXPECTED(Table.getIDForString("baz"), HasValue(BazID));EXPECT_THAT_EXPECTED(Table.getIDForString("buzz"), HasValue(BuzzID));EXPECT_THAT_EXPECTED(Table.getIDForString("bazz"), HasValue(BazzID));EXPECT_THAT_EXPECTED(Table.getIDForString("barr"), HasValue(BarrID));}TEST(StringTableHashTraitsTest, Simple) {PDBStringTableBuilder Builder;// Create more than 64kiB of dummy entries.for (int i = 0; i < 320; ++i) {std::string aaaaa = std::string(220, 'a') + std::to_string(i);Builder.insert(aaaaa);}std::string S = "foo.natvis";uint32_t Pos = Builder.insert(S);EXPECT_GT(Pos, 0xFFFFu);StringTableHashTraits Traits(Builder);EXPECT_LE(Traits.hashLookupKey(S), 0xFFFFu);}
//===- llvm/unittest/DebugInfo/PDB/PDBApiTest.cpp -------------------------===////// 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////===----------------------------------------------------------------------===//#include <unordered_map>#include "llvm/ADT/STLExtras.h"#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h"#include "llvm/DebugInfo/PDB/IPDBSession.h"#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"#include "llvm/DebugInfo/PDB/IPDBTable.h"#include "llvm/DebugInfo/PDB/PDBSymbol.h"#include "llvm/DebugInfo/PDB/PDBSymbolAnnotation.h"#include "llvm/DebugInfo/PDB/PDBSymbolBlock.h"#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h"#include "llvm/DebugInfo/PDB/PDBSymbolCompilandEnv.h"#include "llvm/DebugInfo/PDB/PDBSymbolCustom.h"#include "llvm/DebugInfo/PDB/PDBSymbolData.h"#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h"#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeCustom.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeDimension.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeFriend.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeManaged.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h"#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h"#include "llvm/DebugInfo/PDB/PDBSymbolUsingNamespace.h"#include "llvm/DebugInfo/PDB/PDBTypes.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::pdb;namespace {#define MOCK_SYMBOL_ACCESSOR(Func) \decltype(std::declval<IPDBRawSymbol>().Func()) Func() const override { \typedef decltype(IPDBRawSymbol::Func()) ReturnType; \return ReturnType(); \}class MockSession : public IPDBSession {uint64_t getLoadAddress() const override { return 0; }bool setLoadAddress(uint64_t Address) override { return false; }std::unique_ptr<PDBSymbolExe> getGlobalScope() override { return nullptr; }std::unique_ptr<PDBSymbol> getSymbolById(SymIndexId SymbolId) const override {return nullptr;}std::unique_ptr<IPDBSourceFile>getSourceFileById(uint32_t SymbolId) const override {return nullptr;}bool addressForVA(uint64_t VA, uint32_t &Section,uint32_t &Offset) const override {return false;}bool addressForRVA(uint32_t RVA, uint32_t &Section,uint32_t &Offset) const override {return false;}std::unique_ptr<PDBSymbol> findSymbolByAddress(uint64_t Address,PDB_SymType Type) override {return nullptr;}std::unique_ptr<PDBSymbol> findSymbolByRVA(uint32_t RVA,PDB_SymType Type) override {return nullptr;}std::unique_ptr<PDBSymbol> findSymbolBySectOffset(uint32_t Sect,uint32_t Offset,PDB_SymType Type) override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers>findLineNumbers(const PDBSymbolCompiland &Compiland,const IPDBSourceFile &File) const override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers>findLineNumbersByAddress(uint64_t Address, uint32_t Length) const override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers>findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers>findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset,uint32_t Length) const override {return nullptr;}std::unique_ptr<IPDBEnumSourceFiles>findSourceFiles(const PDBSymbolCompiland *Compiland, llvm::StringRef Pattern,PDB_NameSearchFlags Flags) const override {return nullptr;}std::unique_ptr<IPDBSourceFile>findOneSourceFile(const PDBSymbolCompiland *Compiland,llvm::StringRef Pattern,PDB_NameSearchFlags Flags) const override {return nullptr;}std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>>findCompilandsForSourceFile(llvm::StringRef Pattern,PDB_NameSearchFlags Flags) const override {return nullptr;}std::unique_ptr<PDBSymbolCompiland>findOneCompilandForSourceFile(llvm::StringRef Pattern,PDB_NameSearchFlags Flags) const override {return nullptr;}std::unique_ptr<IPDBEnumSourceFiles> getAllSourceFiles() const override {return nullptr;}std::unique_ptr<IPDBEnumSourceFiles> getSourceFilesForCompiland(const PDBSymbolCompiland &Compiland) const override {return nullptr;}std::unique_ptr<IPDBEnumDataStreams> getDebugStreams() const override {return nullptr;}std::unique_ptr<IPDBEnumTables> getEnumTables() const override {return nullptr;}std::unique_ptr<IPDBEnumInjectedSources> getInjectedSources() const override {return nullptr;}std::unique_ptr<IPDBEnumSectionContribs> getSectionContribs() const override {return nullptr;}std::unique_ptr<IPDBEnumFrameData> getFrameData() const override {return nullptr;}};class MockRawSymbol : public IPDBRawSymbol {public:MockRawSymbol(PDB_SymType SymType): Type(SymType) {}void dump(raw_ostream &OS, int Indent, PdbSymbolIdField ShowIdFields,PdbSymbolIdField RecurseIdFields) const override {}std::unique_ptr<IPDBEnumSymbols>findChildren(PDB_SymType Type) const override {return nullptr;}std::unique_ptr<IPDBEnumSymbols>findChildren(PDB_SymType Type, StringRef Name,PDB_NameSearchFlags Flags) const override {return nullptr;}std::unique_ptr<IPDBEnumSymbols>findChildrenByAddr(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags,uint32_t Section, uint32_t Offset) const override {return nullptr;}std::unique_ptr<IPDBEnumSymbols>findChildrenByVA(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags,uint64_t VA) const override {return nullptr;}std::unique_ptr<IPDBEnumSymbols>findChildrenByRVA(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags,uint32_t RVA) const override {return nullptr;}std::unique_ptr<IPDBEnumSymbols>findInlineFramesByAddr(uint32_t Section, uint32_t Offset) const override {return nullptr;}std::unique_ptr<IPDBEnumSymbols>findInlineFramesByRVA(uint32_t RVA) const override {return nullptr;}std::unique_ptr<IPDBEnumSymbols>findInlineFramesByVA(uint64_t VA) const override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers> findInlineeLines() const override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers>findInlineeLinesByAddr(uint32_t Section, uint32_t Offset,uint32_t Length) const override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers>findInlineeLinesByRVA(uint32_t RVA, uint32_t Length) const override {return nullptr;}std::unique_ptr<IPDBEnumLineNumbers>findInlineeLinesByVA(uint64_t VA, uint32_t Length) const override {return nullptr;}void getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) const override {}void getFrontEndVersion(VersionInfo &Version) const override {}void getBackEndVersion(VersionInfo &Version) const override {}PDB_SymType getSymTag() const override { return Type; }std::string getUndecoratedNameEx(PDB_UndnameFlags Flags) const override {return {};}std::unique_ptr<IPDBLineNumber> getSrcLineOnTypeDefn() const override {return nullptr;}MOCK_SYMBOL_ACCESSOR(getAccess)MOCK_SYMBOL_ACCESSOR(getAddressOffset)MOCK_SYMBOL_ACCESSOR(getAddressSection)MOCK_SYMBOL_ACCESSOR(getAge)MOCK_SYMBOL_ACCESSOR(getArrayIndexTypeId)MOCK_SYMBOL_ACCESSOR(getBaseDataOffset)MOCK_SYMBOL_ACCESSOR(getBaseDataSlot)MOCK_SYMBOL_ACCESSOR(getBaseSymbolId)MOCK_SYMBOL_ACCESSOR(getBuiltinType)MOCK_SYMBOL_ACCESSOR(getBitPosition)MOCK_SYMBOL_ACCESSOR(getCallingConvention)MOCK_SYMBOL_ACCESSOR(getClassParentId)MOCK_SYMBOL_ACCESSOR(getCompilerName)MOCK_SYMBOL_ACCESSOR(getCount)MOCK_SYMBOL_ACCESSOR(getCountLiveRanges)MOCK_SYMBOL_ACCESSOR(getLanguage)MOCK_SYMBOL_ACCESSOR(getLexicalParentId)MOCK_SYMBOL_ACCESSOR(getLibraryName)MOCK_SYMBOL_ACCESSOR(getLiveRangeStartAddressOffset)MOCK_SYMBOL_ACCESSOR(getLiveRangeStartAddressSection)MOCK_SYMBOL_ACCESSOR(getLiveRangeStartRelativeVirtualAddress)MOCK_SYMBOL_ACCESSOR(getLocalBasePointerRegisterId)MOCK_SYMBOL_ACCESSOR(getLowerBoundId)MOCK_SYMBOL_ACCESSOR(getMemorySpaceKind)MOCK_SYMBOL_ACCESSOR(getName)MOCK_SYMBOL_ACCESSOR(getNumberOfAcceleratorPointerTags)MOCK_SYMBOL_ACCESSOR(getNumberOfColumns)MOCK_SYMBOL_ACCESSOR(getNumberOfModifiers)MOCK_SYMBOL_ACCESSOR(getNumberOfRegisterIndices)MOCK_SYMBOL_ACCESSOR(getNumberOfRows)MOCK_SYMBOL_ACCESSOR(getObjectFileName)MOCK_SYMBOL_ACCESSOR(getOemId)MOCK_SYMBOL_ACCESSOR(getOemSymbolId)MOCK_SYMBOL_ACCESSOR(getOffsetInUdt)MOCK_SYMBOL_ACCESSOR(getPlatform)MOCK_SYMBOL_ACCESSOR(getRank)MOCK_SYMBOL_ACCESSOR(getRegisterId)MOCK_SYMBOL_ACCESSOR(getRegisterType)MOCK_SYMBOL_ACCESSOR(getRelativeVirtualAddress)MOCK_SYMBOL_ACCESSOR(getSamplerSlot)MOCK_SYMBOL_ACCESSOR(getSignature)MOCK_SYMBOL_ACCESSOR(getSizeInUdt)MOCK_SYMBOL_ACCESSOR(getSlot)MOCK_SYMBOL_ACCESSOR(getSourceFileName)MOCK_SYMBOL_ACCESSOR(getStride)MOCK_SYMBOL_ACCESSOR(getSubTypeId)MOCK_SYMBOL_ACCESSOR(getSymbolsFileName)MOCK_SYMBOL_ACCESSOR(getSymIndexId)MOCK_SYMBOL_ACCESSOR(getTargetOffset)MOCK_SYMBOL_ACCESSOR(getTargetRelativeVirtualAddress)MOCK_SYMBOL_ACCESSOR(getTargetVirtualAddress)MOCK_SYMBOL_ACCESSOR(getTargetSection)MOCK_SYMBOL_ACCESSOR(getTextureSlot)MOCK_SYMBOL_ACCESSOR(getTimeStamp)MOCK_SYMBOL_ACCESSOR(getToken)MOCK_SYMBOL_ACCESSOR(getTypeId)MOCK_SYMBOL_ACCESSOR(getUavSlot)MOCK_SYMBOL_ACCESSOR(getUndecoratedName)MOCK_SYMBOL_ACCESSOR(getUnmodifiedTypeId)MOCK_SYMBOL_ACCESSOR(getUpperBoundId)MOCK_SYMBOL_ACCESSOR(getVirtualBaseDispIndex)MOCK_SYMBOL_ACCESSOR(getVirtualBaseOffset)MOCK_SYMBOL_ACCESSOR(getVirtualTableShapeId)MOCK_SYMBOL_ACCESSOR(getDataKind)MOCK_SYMBOL_ACCESSOR(getGuid)MOCK_SYMBOL_ACCESSOR(getOffset)MOCK_SYMBOL_ACCESSOR(getThisAdjust)MOCK_SYMBOL_ACCESSOR(getVirtualBasePointerOffset)MOCK_SYMBOL_ACCESSOR(getLocationType)MOCK_SYMBOL_ACCESSOR(getMachineType)MOCK_SYMBOL_ACCESSOR(getThunkOrdinal)MOCK_SYMBOL_ACCESSOR(getLength)MOCK_SYMBOL_ACCESSOR(getVirtualBaseTableType)MOCK_SYMBOL_ACCESSOR(getLiveRangeLength)MOCK_SYMBOL_ACCESSOR(getVirtualAddress)MOCK_SYMBOL_ACCESSOR(getUdtKind)MOCK_SYMBOL_ACCESSOR(hasConstructor)MOCK_SYMBOL_ACCESSOR(hasCustomCallingConvention)MOCK_SYMBOL_ACCESSOR(hasFarReturn)MOCK_SYMBOL_ACCESSOR(isCode)MOCK_SYMBOL_ACCESSOR(isCompilerGenerated)MOCK_SYMBOL_ACCESSOR(isConstType)MOCK_SYMBOL_ACCESSOR(isEditAndContinueEnabled)MOCK_SYMBOL_ACCESSOR(isFunction)MOCK_SYMBOL_ACCESSOR(getAddressTaken)MOCK_SYMBOL_ACCESSOR(getNoStackOrdering)MOCK_SYMBOL_ACCESSOR(hasAlloca)MOCK_SYMBOL_ACCESSOR(hasAssignmentOperator)MOCK_SYMBOL_ACCESSOR(hasCTypes)MOCK_SYMBOL_ACCESSOR(hasCastOperator)MOCK_SYMBOL_ACCESSOR(hasDebugInfo)MOCK_SYMBOL_ACCESSOR(hasEH)MOCK_SYMBOL_ACCESSOR(hasEHa)MOCK_SYMBOL_ACCESSOR(hasFramePointer)MOCK_SYMBOL_ACCESSOR(hasInlAsm)MOCK_SYMBOL_ACCESSOR(hasInlineAttribute)MOCK_SYMBOL_ACCESSOR(hasInterruptReturn)MOCK_SYMBOL_ACCESSOR(hasLongJump)MOCK_SYMBOL_ACCESSOR(hasManagedCode)MOCK_SYMBOL_ACCESSOR(hasNestedTypes)MOCK_SYMBOL_ACCESSOR(hasNoInlineAttribute)MOCK_SYMBOL_ACCESSOR(hasNoReturnAttribute)MOCK_SYMBOL_ACCESSOR(hasOptimizedCodeDebugInfo)MOCK_SYMBOL_ACCESSOR(hasOverloadedOperator)MOCK_SYMBOL_ACCESSOR(hasSEH)MOCK_SYMBOL_ACCESSOR(hasSecurityChecks)MOCK_SYMBOL_ACCESSOR(hasSetJump)MOCK_SYMBOL_ACCESSOR(hasStrictGSCheck)MOCK_SYMBOL_ACCESSOR(isAcceleratorGroupSharedLocal)MOCK_SYMBOL_ACCESSOR(isAcceleratorPointerTagLiveRange)MOCK_SYMBOL_ACCESSOR(isAcceleratorStubFunction)MOCK_SYMBOL_ACCESSOR(isAggregated)MOCK_SYMBOL_ACCESSOR(isIntroVirtualFunction)MOCK_SYMBOL_ACCESSOR(isCVTCIL)MOCK_SYMBOL_ACCESSOR(isConstructorVirtualBase)MOCK_SYMBOL_ACCESSOR(isCxxReturnUdt)MOCK_SYMBOL_ACCESSOR(isDataAligned)MOCK_SYMBOL_ACCESSOR(isHLSLData)MOCK_SYMBOL_ACCESSOR(isHotpatchable)MOCK_SYMBOL_ACCESSOR(isIndirectVirtualBaseClass)MOCK_SYMBOL_ACCESSOR(isInterfaceUdt)MOCK_SYMBOL_ACCESSOR(isIntrinsic)MOCK_SYMBOL_ACCESSOR(isLTCG)MOCK_SYMBOL_ACCESSOR(isLocationControlFlowDependent)MOCK_SYMBOL_ACCESSOR(isMSILNetmodule)MOCK_SYMBOL_ACCESSOR(isMatrixRowMajor)MOCK_SYMBOL_ACCESSOR(isManagedCode)MOCK_SYMBOL_ACCESSOR(isMSILCode)MOCK_SYMBOL_ACCESSOR(isMultipleInheritance)MOCK_SYMBOL_ACCESSOR(isNaked)MOCK_SYMBOL_ACCESSOR(isNested)MOCK_SYMBOL_ACCESSOR(isOptimizedAway)MOCK_SYMBOL_ACCESSOR(isPacked)MOCK_SYMBOL_ACCESSOR(isPointerBasedOnSymbolValue)MOCK_SYMBOL_ACCESSOR(isPointerToDataMember)MOCK_SYMBOL_ACCESSOR(isPointerToMemberFunction)MOCK_SYMBOL_ACCESSOR(isPureVirtual)MOCK_SYMBOL_ACCESSOR(isRValueReference)MOCK_SYMBOL_ACCESSOR(isRefUdt)MOCK_SYMBOL_ACCESSOR(isReference)MOCK_SYMBOL_ACCESSOR(isRestrictedType)MOCK_SYMBOL_ACCESSOR(isReturnValue)MOCK_SYMBOL_ACCESSOR(isSafeBuffers)MOCK_SYMBOL_ACCESSOR(isScoped)MOCK_SYMBOL_ACCESSOR(isSdl)MOCK_SYMBOL_ACCESSOR(isSingleInheritance)MOCK_SYMBOL_ACCESSOR(isSplitted)MOCK_SYMBOL_ACCESSOR(isStatic)MOCK_SYMBOL_ACCESSOR(hasPrivateSymbols)MOCK_SYMBOL_ACCESSOR(isUnalignedType)MOCK_SYMBOL_ACCESSOR(isUnreached)MOCK_SYMBOL_ACCESSOR(isValueUdt)MOCK_SYMBOL_ACCESSOR(isVirtual)MOCK_SYMBOL_ACCESSOR(isVirtualBaseClass)MOCK_SYMBOL_ACCESSOR(isVirtualInheritance)MOCK_SYMBOL_ACCESSOR(isVolatileType)MOCK_SYMBOL_ACCESSOR(getValue)MOCK_SYMBOL_ACCESSOR(wasInlined)MOCK_SYMBOL_ACCESSOR(getUnused)private:PDB_SymType Type;};class PDBApiTest : public testing::Test {public:std::unordered_map<PDB_SymType, std::unique_ptr<PDBSymbol>> SymbolMap;void SetUp() override {Session.reset(new MockSession());InsertItemWithTag(PDB_SymType::None);InsertItemWithTag(PDB_SymType::Exe);InsertItemWithTag(PDB_SymType::Compiland);InsertItemWithTag(PDB_SymType::CompilandDetails);InsertItemWithTag(PDB_SymType::CompilandEnv);InsertItemWithTag(PDB_SymType::Function);InsertItemWithTag(PDB_SymType::Block);InsertItemWithTag(PDB_SymType::Data);InsertItemWithTag(PDB_SymType::Annotation);InsertItemWithTag(PDB_SymType::Label);InsertItemWithTag(PDB_SymType::PublicSymbol);InsertItemWithTag(PDB_SymType::UDT);InsertItemWithTag(PDB_SymType::Enum);InsertItemWithTag(PDB_SymType::FunctionSig);InsertItemWithTag(PDB_SymType::PointerType);InsertItemWithTag(PDB_SymType::ArrayType);InsertItemWithTag(PDB_SymType::BuiltinType);InsertItemWithTag(PDB_SymType::Typedef);InsertItemWithTag(PDB_SymType::BaseClass);InsertItemWithTag(PDB_SymType::Friend);InsertItemWithTag(PDB_SymType::FunctionArg);InsertItemWithTag(PDB_SymType::FuncDebugStart);InsertItemWithTag(PDB_SymType::FuncDebugEnd);InsertItemWithTag(PDB_SymType::UsingNamespace);InsertItemWithTag(PDB_SymType::VTableShape);InsertItemWithTag(PDB_SymType::VTable);InsertItemWithTag(PDB_SymType::Custom);InsertItemWithTag(PDB_SymType::Thunk);InsertItemWithTag(PDB_SymType::CustomType);InsertItemWithTag(PDB_SymType::ManagedType);InsertItemWithTag(PDB_SymType::Dimension);InsertItemWithTag(PDB_SymType::Max);}template <class ExpectedType> void VerifyDyncast(PDB_SymType Tag) {for (auto item = SymbolMap.begin(); item != SymbolMap.end(); ++item) {EXPECT_EQ(item->first == Tag, llvm::isa<ExpectedType>(*item->second));}}void VerifyUnknownDyncasts() {for (auto item = SymbolMap.begin(); item != SymbolMap.end(); ++item) {bool should_match = false;if (item->first == PDB_SymType::None || item->first >= PDB_SymType::Max)should_match = true;EXPECT_EQ(should_match, llvm::isa<PDBSymbolUnknown>(*item->second));}}private:std::unique_ptr<IPDBSession> Session;void InsertItemWithTag(PDB_SymType Tag) {auto RawSymbol = std::make_unique<MockRawSymbol>(Tag);auto Symbol = PDBSymbol::create(*Session, std::move(RawSymbol));SymbolMap.insert(std::make_pair(Tag, std::move(Symbol)));}};TEST_F(PDBApiTest, Dyncast) {// Most of the types have a one-to-one mapping between Tag and concrete type.VerifyDyncast<PDBSymbolExe>(PDB_SymType::Exe);VerifyDyncast<PDBSymbolCompiland>(PDB_SymType::Compiland);VerifyDyncast<PDBSymbolCompilandDetails>(PDB_SymType::CompilandDetails);VerifyDyncast<PDBSymbolCompilandEnv>(PDB_SymType::CompilandEnv);VerifyDyncast<PDBSymbolFunc>(PDB_SymType::Function);VerifyDyncast<PDBSymbolBlock>(PDB_SymType::Block);VerifyDyncast<PDBSymbolData>(PDB_SymType::Data);VerifyDyncast<PDBSymbolAnnotation>(PDB_SymType::Annotation);VerifyDyncast<PDBSymbolLabel>(PDB_SymType::Label);VerifyDyncast<PDBSymbolPublicSymbol>(PDB_SymType::PublicSymbol);VerifyDyncast<PDBSymbolTypeUDT>(PDB_SymType::UDT);VerifyDyncast<PDBSymbolTypeEnum>(PDB_SymType::Enum);VerifyDyncast<PDBSymbolTypeFunctionSig>(PDB_SymType::FunctionSig);VerifyDyncast<PDBSymbolTypePointer>(PDB_SymType::PointerType);VerifyDyncast<PDBSymbolTypeArray>(PDB_SymType::ArrayType);VerifyDyncast<PDBSymbolTypeBuiltin>(PDB_SymType::BuiltinType);VerifyDyncast<PDBSymbolTypeTypedef>(PDB_SymType::Typedef);VerifyDyncast<PDBSymbolTypeBaseClass>(PDB_SymType::BaseClass);VerifyDyncast<PDBSymbolTypeFriend>(PDB_SymType::Friend);VerifyDyncast<PDBSymbolTypeFunctionArg>(PDB_SymType::FunctionArg);VerifyDyncast<PDBSymbolFuncDebugStart>(PDB_SymType::FuncDebugStart);VerifyDyncast<PDBSymbolFuncDebugEnd>(PDB_SymType::FuncDebugEnd);VerifyDyncast<PDBSymbolUsingNamespace>(PDB_SymType::UsingNamespace);VerifyDyncast<PDBSymbolTypeVTableShape>(PDB_SymType::VTableShape);VerifyDyncast<PDBSymbolTypeVTable>(PDB_SymType::VTable);VerifyDyncast<PDBSymbolCustom>(PDB_SymType::Custom);VerifyDyncast<PDBSymbolThunk>(PDB_SymType::Thunk);VerifyDyncast<PDBSymbolTypeCustom>(PDB_SymType::CustomType);VerifyDyncast<PDBSymbolTypeManaged>(PDB_SymType::ManagedType);VerifyDyncast<PDBSymbolTypeDimension>(PDB_SymType::Dimension);VerifyUnknownDyncasts();}} // end anonymous namespace
//===- NativeSymbolReuseTest.cpp ------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/PDB/PDB.h"#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h"#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"#include "llvm/DebugInfo/PDB/IPDBSession.h"#include "llvm/DebugInfo/PDB/Native/NativeSession.h"#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"#include "llvm/Support/Path.h"#include "llvm/Testing/Support/Error.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::pdb;extern const char *TestMainArgv0;TEST(NativeSymbolReuseTest, GlobalSymbolReuse) {SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);llvm::sys::path::append(InputsDir, "empty.pdb");std::unique_ptr<IPDBSession> S;Error E = pdb::loadDataForPDB(PDB_ReaderType::Native, InputsDir, S);ASSERT_THAT_ERROR(std::move(E), Succeeded());SymIndexId GlobalId;{auto GS1 = S->getGlobalScope();auto GS2 = S->getGlobalScope();GlobalId = GS1->getSymIndexId();SymIndexId Id2 = GS1->getSymIndexId();EXPECT_EQ(GlobalId, Id2);}{auto GS3 = S->getGlobalScope();SymIndexId Id3 = GS3->getSymIndexId();EXPECT_EQ(GlobalId, Id3);}}TEST(NativeSymbolReuseTest, CompilandSymbolReuse) {SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);llvm::sys::path::append(InputsDir, "empty.pdb");std::unique_ptr<IPDBSession> S;Error E = pdb::loadDataForPDB(PDB_ReaderType::Native, InputsDir, S);ASSERT_THAT_ERROR(std::move(E), Succeeded());auto GS = S->getGlobalScope();std::vector<SymIndexId> CompilandIds;{auto Compilands = GS->findAllChildren<PDBSymbolCompiland>();ASSERT_NE(nullptr, Compilands);ASSERT_EQ(2U, Compilands->getChildCount());std::vector<SymIndexId> Ids2;// First try resetting the enumerator, then try destroying the enumerator// and constructing another one.while (auto Compiland = Compilands->getNext())CompilandIds.push_back(Compiland->getSymIndexId());Compilands->reset();while (auto Compiland = Compilands->getNext())Ids2.push_back(Compiland->getSymIndexId());EXPECT_EQ(CompilandIds, Ids2);}{auto Compilands = GS->findAllChildren<PDBSymbolCompiland>();ASSERT_NE(nullptr, Compilands);ASSERT_EQ(2U, Compilands->getChildCount());std::vector<SymIndexId> Ids3;while (auto Compiland = Compilands->getNext())Ids3.push_back(Compiland->getSymIndexId());EXPECT_EQ(CompilandIds, Ids3);}}TEST(NativeSymbolReuseTest, CompilandSymbolReuseBackwards) {SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);llvm::sys::path::append(InputsDir, "empty.pdb");std::unique_ptr<IPDBSession> S;Error E = pdb::loadDataForPDB(PDB_ReaderType::Native, InputsDir, S);ASSERT_THAT_ERROR(std::move(E), Succeeded());auto GS = S->getGlobalScope();// This time do the first iteration backwards, and make sure that when you// then iterate them forwards, the IDs come out in reverse.std::vector<SymIndexId> CompilandIds;{auto Compilands = GS->findAllChildren<PDBSymbolCompiland>();ASSERT_NE(nullptr, Compilands);ASSERT_EQ(2U, Compilands->getChildCount());std::vector<SymIndexId> Ids2;for (int I = Compilands->getChildCount() - 1; I >= 0; --I) {auto Compiland = Compilands->getChildAtIndex(I);CompilandIds.push_back(Compiland->getSymIndexId());}while (auto Compiland = Compilands->getNext())Ids2.push_back(Compiland->getSymIndexId());auto ReversedIter = llvm::reverse(Ids2);std::vector<SymIndexId> Reversed{ReversedIter.begin(), ReversedIter.end()};EXPECT_EQ(CompilandIds, Reversed);}}
//===- llvm/unittest/DebugInfo/PDB/NativeSessionTest.cpp ------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/PDB/Native/NativeSession.h"#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h"#include "llvm/DebugInfo/PDB/IPDBSession.h"#include "llvm/DebugInfo/PDB/PDB.h"#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"#include "llvm/Support/Path.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <vector>using namespace llvm;using namespace llvm::pdb;extern const char *TestMainArgv0;static std::string getExePath() {SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);llvm::sys::path::append(InputsDir, "SimpleTest.exe");return std::string(InputsDir);}TEST(NativeSessionTest, TestCreateFromExe) {std::unique_ptr<IPDBSession> S;std::string ExePath = getExePath();Expected<std::string> PdbPath = NativeSession::searchForPdb({ExePath});ASSERT_TRUE((bool)PdbPath);Error E = NativeSession::createFromPdbPath(PdbPath.get(), S);ASSERT_THAT_ERROR(std::move(E), Succeeded());}TEST(NativeSessionTest, TestSetLoadAddress) {std::unique_ptr<IPDBSession> S;Error E = pdb::loadDataForEXE(PDB_ReaderType::Native, getExePath(), S);ASSERT_THAT_ERROR(std::move(E), Succeeded());S->setLoadAddress(123);EXPECT_EQ(S->getLoadAddress(), 123U);}TEST(NativeSessionTest, TestAddressForVA) {std::unique_ptr<IPDBSession> S;Error E = pdb::loadDataForEXE(PDB_ReaderType::Native, getExePath(), S);ASSERT_THAT_ERROR(std::move(E), Succeeded());uint64_t LoadAddr = S->getLoadAddress();uint32_t Section;uint32_t Offset;ASSERT_TRUE(S->addressForVA(LoadAddr + 5000, Section, Offset));EXPECT_EQ(1U, Section);EXPECT_EQ(904U, Offset);ASSERT_TRUE(S->addressForVA(-1, Section, Offset));EXPECT_EQ(0U, Section);EXPECT_EQ(0U, Offset);ASSERT_TRUE(S->addressForVA(4, Section, Offset));EXPECT_EQ(0U, Section);EXPECT_EQ(4U, Offset);ASSERT_TRUE(S->addressForVA(LoadAddr + 100000, Section, Offset));EXPECT_EQ(3U, Section);EXPECT_EQ(83616U, Offset);}TEST(NativeSessionTest, TestAddressForRVA) {std::unique_ptr<IPDBSession> S;Error E = pdb::loadDataForEXE(PDB_ReaderType::Native, getExePath(), S);ASSERT_THAT_ERROR(std::move(E), Succeeded());uint32_t Section;uint32_t Offset;ASSERT_TRUE(S->addressForVA(5000, Section, Offset));EXPECT_EQ(1U, Section);EXPECT_EQ(904U, Offset);ASSERT_TRUE(S->addressForVA(-1, Section, Offset));EXPECT_EQ(0U, Section);EXPECT_EQ(0U, Offset);ASSERT_TRUE(S->addressForVA(4, Section, Offset));EXPECT_EQ(0U, Section);EXPECT_EQ(4U, Offset);ASSERT_TRUE(S->addressForVA(100000, Section, Offset));EXPECT_EQ(3U, Section);EXPECT_EQ(83616U, Offset);}
// Compile with "cl /c /Zi SimpleTest.cpp"// Link with "link SimpleTest.obj /debug /nodefaultlib /entry:main"int main() { return 0; }
//===- llvm/unittest/DebugInfo/PDB/HashTableTest.cpp ----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/PDB/Native/HashTable.h"#include "llvm/DebugInfo/PDB/Native/Hash.h"#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"#include "llvm/Support/Allocator.h"#include "llvm/Support/BinaryByteStream.h"#include "llvm/Support/BinaryStreamReader.h"#include "llvm/Support/BinaryStreamWriter.h"#include "llvm/Support/StringSaver.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <vector>using namespace llvm;using namespace llvm::pdb;using namespace llvm::support;namespace {struct IdentityHashTraits {uint32_t hashLookupKey(uint32_t N) const { return N; }uint32_t storageKeyToLookupKey(uint32_t N) const { return N; }uint32_t lookupKeyToStorageKey(uint32_t N) { return N; }};template <class T = uint32_t>class HashTableInternals : public HashTable<T> {public:using HashTable<T>::Buckets;using HashTable<T>::Present;using HashTable<T>::Deleted;};}TEST(HashTableTest, TestSimple) {HashTableInternals<> Table;EXPECT_EQ(0u, Table.size());EXPECT_GT(Table.capacity(), 0u);IdentityHashTraits Traits;Table.set_as(3u, 7, Traits);EXPECT_EQ(1u, Table.size());ASSERT_NE(Table.end(), Table.find_as(3u, Traits));EXPECT_EQ(7u, Table.get(3u, Traits));}TEST(HashTableTest, TestCollision) {HashTableInternals<> Table;EXPECT_EQ(0u, Table.size());EXPECT_GT(Table.capacity(), 0u);// We use knowledge of the hash table's implementation details to make sure// to add another value that is the equivalent to the first value modulo the// hash table's capacity.uint32_t N1 = Table.capacity() + 1;uint32_t N2 = 2 * N1;IdentityHashTraits Traits;Table.set_as(N1, 7, Traits);Table.set_as(N2, 12, Traits);EXPECT_EQ(2u, Table.size());ASSERT_NE(Table.end(), Table.find_as(N1, Traits));ASSERT_NE(Table.end(), Table.find_as(N2, Traits));EXPECT_EQ(7u, Table.get(N1, Traits));EXPECT_EQ(12u, Table.get(N2, Traits));}TEST(HashTableTest, TestRemove) {HashTableInternals<> Table;EXPECT_EQ(0u, Table.size());EXPECT_GT(Table.capacity(), 0u);IdentityHashTraits Traits;Table.set_as(1u, 2, Traits);Table.set_as(3u, 4, Traits);EXPECT_EQ(2u, Table.size());ASSERT_NE(Table.end(), Table.find_as(1u, Traits));ASSERT_NE(Table.end(), Table.find_as(3u, Traits));EXPECT_EQ(2u, Table.get(1u, Traits));EXPECT_EQ(4u, Table.get(3u, Traits));}TEST(HashTableTest, TestCollisionAfterMultipleProbes) {HashTableInternals<> Table;EXPECT_EQ(0u, Table.size());EXPECT_GT(Table.capacity(), 0u);// Probing looks for the first available slot. A slot may already be filled// as a result of an item with a *different* hash value already being there.// Test that when this happens, the probe still finds the value.uint32_t N1 = Table.capacity() + 1;uint32_t N2 = N1 + 1;uint32_t N3 = 2 * N1;IdentityHashTraits Traits;Table.set_as(N1, 7, Traits);Table.set_as(N2, 11, Traits);Table.set_as(N3, 13, Traits);EXPECT_EQ(3u, Table.size());ASSERT_NE(Table.end(), Table.find_as(N1, Traits));ASSERT_NE(Table.end(), Table.find_as(N2, Traits));ASSERT_NE(Table.end(), Table.find_as(N3, Traits));EXPECT_EQ(7u, Table.get(N1, Traits));EXPECT_EQ(11u, Table.get(N2, Traits));EXPECT_EQ(13u, Table.get(N3, Traits));}TEST(HashTableTest, Grow) {// So that we are independent of the load factor, `capacity` items, which is// guaranteed to trigger a grow. Then verify that the size is the same, the// capacity is larger, and all the original items are still in the table.HashTableInternals<> Table;IdentityHashTraits Traits;uint32_t OldCapacity = Table.capacity();for (uint32_t I = 0; I < OldCapacity; ++I) {Table.set_as(OldCapacity + I * 2 + 1, I * 2 + 3, Traits);}EXPECT_EQ(OldCapacity, Table.size());EXPECT_GT(Table.capacity(), OldCapacity);for (uint32_t I = 0; I < OldCapacity; ++I) {ASSERT_NE(Table.end(), Table.find_as(OldCapacity + I * 2 + 1, Traits));EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1, Traits));}}TEST(HashTableTest, Serialization) {HashTableInternals<> Table;IdentityHashTraits Traits;uint32_t Cap = Table.capacity();for (uint32_t I = 0; I < Cap; ++I) {Table.set_as(Cap + I * 2 + 1, I * 2 + 3, Traits);}std::vector<uint8_t> Buffer(Table.calculateSerializedLength());MutableBinaryByteStream Stream(Buffer, little);BinaryStreamWriter Writer(Stream);EXPECT_THAT_ERROR(Table.commit(Writer), Succeeded());// We should have written precisely the number of bytes we calculated earlier.EXPECT_EQ(Buffer.size(), Writer.getOffset());HashTableInternals<> Table2;BinaryStreamReader Reader(Stream);EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());// We should have read precisely the number of bytes we calculated earlier.EXPECT_EQ(Buffer.size(), Reader.getOffset());EXPECT_EQ(Table.size(), Table2.size());EXPECT_EQ(Table.capacity(), Table2.capacity());EXPECT_EQ(Table.Buckets, Table2.Buckets);EXPECT_EQ(Table.Present, Table2.Present);EXPECT_EQ(Table.Deleted, Table2.Deleted);}TEST(HashTableTest, NamedStreamMap) {std::vector<StringRef> Streams = {"One", "Two", "Three", "Four","Five", "Six", "Seven"};StringMap<uint32_t> ExpectedIndices;for (uint32_t I = 0; I < Streams.size(); ++I)ExpectedIndices[Streams[I]] = I + 1;// To verify the hash table actually works, we want to verify that insertion// order doesn't matter. So try inserting in every possible order of 7 items.do {NamedStreamMap NSM;for (StringRef S : Streams)NSM.set(S, ExpectedIndices[S]);EXPECT_EQ(Streams.size(), NSM.size());uint32_t N;EXPECT_TRUE(NSM.get("One", N));EXPECT_EQ(1U, N);EXPECT_TRUE(NSM.get("Two", N));EXPECT_EQ(2U, N);EXPECT_TRUE(NSM.get("Three", N));EXPECT_EQ(3U, N);EXPECT_TRUE(NSM.get("Four", N));EXPECT_EQ(4U, N);EXPECT_TRUE(NSM.get("Five", N));EXPECT_EQ(5U, N);EXPECT_TRUE(NSM.get("Six", N));EXPECT_EQ(6U, N);EXPECT_TRUE(NSM.get("Seven", N));EXPECT_EQ(7U, N);} while (std::next_permutation(Streams.begin(), Streams.end()));}struct FooBar {uint32_t X;uint32_t Y;bool operator==(const FooBar &RHS) const {return X == RHS.X && Y == RHS.Y;}};struct FooBarHashTraits {std::vector<char> Buffer;FooBarHashTraits() { Buffer.push_back(0); }uint32_t hashLookupKey(StringRef S) const {return llvm::pdb::hashStringV1(S);}StringRef storageKeyToLookupKey(uint32_t N) const {if (N >= Buffer.size())return StringRef();return StringRef(Buffer.data() + N);}uint32_t lookupKeyToStorageKey(StringRef S) {uint32_t N = Buffer.size();Buffer.insert(Buffer.end(), S.begin(), S.end());Buffer.push_back('\0');return N;}};TEST(HashTableTest, NonTrivialValueType) {HashTableInternals<FooBar> Table;FooBarHashTraits Traits;uint32_t Cap = Table.capacity();for (uint32_t I = 0; I < Cap; ++I) {FooBar F;F.X = I;F.Y = I + 1;Table.set_as(utostr(I), F, Traits);}std::vector<uint8_t> Buffer(Table.calculateSerializedLength());MutableBinaryByteStream Stream(Buffer, little);BinaryStreamWriter Writer(Stream);EXPECT_THAT_ERROR(Table.commit(Writer), Succeeded());// We should have written precisely the number of bytes we calculated earlier.EXPECT_EQ(Buffer.size(), Writer.getOffset());HashTableInternals<FooBar> Table2;BinaryStreamReader Reader(Stream);EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());// We should have read precisely the number of bytes we calculated earlier.EXPECT_EQ(Buffer.size(), Reader.getOffset());EXPECT_EQ(Table.size(), Table2.size());EXPECT_EQ(Table.capacity(), Table2.capacity());EXPECT_EQ(Table.Buckets, Table2.Buckets);EXPECT_EQ(Table.Present, Table2.Present);EXPECT_EQ(Table.Deleted, Table2.Deleted);}
set(LLVM_LINK_COMPONENTSDebugInfoCodeViewDebugInfoMSFDebugInfoPDB)add_llvm_unittest_with_input_files(DebugInfoPDBTestsHashTableTest.cppNativeSessionTest.cppNativeSymbolReuseTest.cppStringTableBuilderTest.cppPDBApiTest.cpp)target_link_libraries(DebugInfoPDBTests PRIVATE LLVMTestingSupport)set_property(TARGET DebugInfoPDBTests PROPERTY FOLDER "Tests/UnitTests/DebugInfoTests")
//===- llvm/unittest/DebugInfo/MSF/MappedBlockStreamTest.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/MSF/MappedBlockStream.h"#include "llvm/Support/BinaryByteStream.h"#include "llvm/Support/BinaryStreamReader.h"#include "llvm/Support/BinaryStreamRef.h"#include "llvm/Support/BinaryStreamWriter.h"#include "llvm/Testing/Support/Error.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::msf;using namespace llvm::support;namespace {static const uint32_t BlocksAry[] = {0, 1, 2, 5, 4, 3, 6, 7, 8, 9};static uint8_t DataAry[] = {'A', 'B', 'C', 'F', 'E', 'D', 'G', 'H', 'I', 'J'};class DiscontiguousStream : public WritableBinaryStream {public:DiscontiguousStream(ArrayRef<uint32_t> Blocks, MutableArrayRef<uint8_t> Data): Blocks(Blocks.begin(), Blocks.end()), Data(Data.begin(), Data.end()) {}uint32_t block_size() const { return 1; }uint32_t block_count() const { return Blocks.size(); }endianness getEndian() const override { return little; }Error readBytes(uint64_t Offset, uint64_t Size,ArrayRef<uint8_t> &Buffer) override {if (auto EC = checkOffsetForRead(Offset, Size))return EC;Buffer = Data.slice(Offset, Size);return Error::success();}Error readLongestContiguousChunk(uint64_t Offset,ArrayRef<uint8_t> &Buffer) override {if (auto EC = checkOffsetForRead(Offset, 1))return EC;Buffer = Data.drop_front(Offset);return Error::success();}uint64_t getLength() override { return Data.size(); }Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> SrcData) override {if (auto EC = checkOffsetForWrite(Offset, SrcData.size()))return EC;::memcpy(&Data[Offset], SrcData.data(), SrcData.size());return Error::success();}Error commit() override { return Error::success(); }MSFStreamLayout layout() const {return MSFStreamLayout{static_cast<uint32_t>(Data.size()), Blocks};}BumpPtrAllocator Allocator;private:std::vector<support::ulittle32_t> Blocks;MutableArrayRef<uint8_t> Data;};TEST(MappedBlockStreamTest, NumBlocks) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);EXPECT_EQ(F.block_size(), S->getBlockSize());EXPECT_EQ(F.layout().Blocks.size(), S->getNumBlocks());}// Tests that a read which is entirely contained within a single block works// and does not allocate.TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);BinaryStreamRef SR;EXPECT_THAT_ERROR(R.readStreamRef(SR, 0U), Succeeded());ArrayRef<uint8_t> Buffer;EXPECT_THAT_ERROR(SR.readBytes(0U, 1U, Buffer), Failed());EXPECT_THAT_ERROR(R.readStreamRef(SR, 1U), Succeeded());EXPECT_THAT_ERROR(SR.readBytes(1U, 1U, Buffer), Failed());}// Tests that a read which outputs into a full destination buffer works and// does not fail due to the length of the output buffer.TEST(MappedBlockStreamTest, ReadOntoNonEmptyBuffer) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA";EXPECT_THAT_ERROR(R.readFixedString(Str, 1), Succeeded());EXPECT_EQ(Str, StringRef("A"));EXPECT_EQ(0U, F.Allocator.getBytesAllocated());}// Tests that a read which crosses a block boundary, but where the subsequent// blocks are still contiguous in memory to the previous block works and does// not allocate memory.TEST(MappedBlockStreamTest, ZeroCopyReadContiguousBreak) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str;EXPECT_THAT_ERROR(R.readFixedString(Str, 2), Succeeded());EXPECT_EQ(Str, StringRef("AB"));EXPECT_EQ(0U, F.Allocator.getBytesAllocated());R.setOffset(6);EXPECT_THAT_ERROR(R.readFixedString(Str, 4), Succeeded());EXPECT_EQ(Str, StringRef("GHIJ"));EXPECT_EQ(0U, F.Allocator.getBytesAllocated());}// Tests that a read which crosses a block boundary and cannot be referenced// contiguously works and allocates only the precise amount of bytes// requested.TEST(MappedBlockStreamTest, CopyReadNonContiguousBreak) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str;EXPECT_THAT_ERROR(R.readFixedString(Str, 10), Succeeded());EXPECT_EQ(Str, StringRef("ABCDEFGHIJ"));EXPECT_EQ(10U, F.Allocator.getBytesAllocated());}// Test that an out of bounds read which doesn't cross a block boundary// fails and allocates no memory.TEST(MappedBlockStreamTest, InvalidReadSizeNoBreak) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str;R.setOffset(10);EXPECT_THAT_ERROR(R.readFixedString(Str, 1), Failed());EXPECT_EQ(0U, F.Allocator.getBytesAllocated());}// Test that an out of bounds read which crosses a contiguous block boundary// fails and allocates no memory.TEST(MappedBlockStreamTest, InvalidReadSizeContiguousBreak) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str;R.setOffset(6);EXPECT_THAT_ERROR(R.readFixedString(Str, 5), Failed());EXPECT_EQ(0U, F.Allocator.getBytesAllocated());}// Test that an out of bounds read which crosses a discontiguous block// boundary fails and allocates no memory.TEST(MappedBlockStreamTest, InvalidReadSizeNonContiguousBreak) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str;EXPECT_THAT_ERROR(R.readFixedString(Str, 11), Failed());EXPECT_EQ(0U, F.Allocator.getBytesAllocated());}// Tests that a read which is entirely contained within a single block but// beyond the end of a StreamRef fails.TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str;EXPECT_THAT_ERROR(R.readFixedString(Str, 1), Succeeded());EXPECT_EQ(Str, StringRef("A"));EXPECT_EQ(0U, F.Allocator.getBytesAllocated());}// Tests that a read which is not aligned on the same boundary as a previous// cached request, but which is known to overlap that request, shares the// previous allocation.TEST(MappedBlockStreamTest, UnalignedOverlappingRead) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str1;StringRef Str2;EXPECT_THAT_ERROR(R.readFixedString(Str1, 7), Succeeded());EXPECT_EQ(Str1, StringRef("ABCDEFG"));EXPECT_EQ(7U, F.Allocator.getBytesAllocated());R.setOffset(2);EXPECT_THAT_ERROR(R.readFixedString(Str2, 3), Succeeded());EXPECT_EQ(Str2, StringRef("CDE"));EXPECT_EQ(Str1.data() + 2, Str2.data());EXPECT_EQ(7U, F.Allocator.getBytesAllocated());}// Tests that a read which is not aligned on the same boundary as a previous// cached request, but which only partially overlaps a previous cached request,// still works correctly and allocates again from the shared pool.TEST(MappedBlockStreamTest, UnalignedOverlappingReadFail) {DiscontiguousStream F(BlocksAry, DataAry);auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,F.Allocator);BinaryStreamReader R(*S);StringRef Str1;StringRef Str2;EXPECT_THAT_ERROR(R.readFixedString(Str1, 6), Succeeded());EXPECT_EQ(Str1, StringRef("ABCDEF"));EXPECT_EQ(6U, F.Allocator.getBytesAllocated());R.setOffset(4);EXPECT_THAT_ERROR(R.readFixedString(Str2, 4), Succeeded());EXPECT_EQ(Str2, StringRef("EFGH"));EXPECT_EQ(10U, F.Allocator.getBytesAllocated());}TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) {static uint8_t Data[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};static uint8_t LargeBuffer[] = {'0', '1', '2', '3', '4', '5','6', '7', '8', '9', 'A'};static uint8_t SmallBuffer[] = {'0', '1', '2'};static_assert(sizeof(LargeBuffer) > sizeof(Data),"LargeBuffer is not big enough");DiscontiguousStream F(BlocksAry, Data);auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),F, F.Allocator);EXPECT_THAT_ERROR(S->writeBytes(0, ArrayRef<uint8_t>(LargeBuffer)), Failed());EXPECT_THAT_ERROR(S->writeBytes(0, ArrayRef<uint8_t>(SmallBuffer)),Succeeded());EXPECT_THAT_ERROR(S->writeBytes(7, ArrayRef<uint8_t>(SmallBuffer)),Succeeded());EXPECT_THAT_ERROR(S->writeBytes(8, ArrayRef<uint8_t>(SmallBuffer)), Failed());}TEST(MappedBlockStreamTest, TestWriteBytesNoBreakBoundary) {static uint8_t Data[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};DiscontiguousStream F(BlocksAry, Data);auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),F, F.Allocator);ArrayRef<uint8_t> Buffer;EXPECT_THAT_ERROR(S->readBytes(0, 1, Buffer), Succeeded());EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A'));EXPECT_THAT_ERROR(S->readBytes(9, 1, Buffer), Succeeded());EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J'));EXPECT_THAT_ERROR(S->writeBytes(0, ArrayRef<uint8_t>('J')), Succeeded());EXPECT_THAT_ERROR(S->writeBytes(9, ArrayRef<uint8_t>('A')), Succeeded());EXPECT_THAT_ERROR(S->readBytes(0, 1, Buffer), Succeeded());EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J'));EXPECT_THAT_ERROR(S->readBytes(9, 1, Buffer), Succeeded());EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A'));EXPECT_THAT_ERROR(S->writeBytes(0, ArrayRef<uint8_t>('A')), Succeeded());EXPECT_THAT_ERROR(S->writeBytes(9, ArrayRef<uint8_t>('J')), Succeeded());EXPECT_THAT_ERROR(S->readBytes(0, 1, Buffer), Succeeded());EXPECT_EQ(Buffer, ArrayRef<uint8_t>('A'));EXPECT_THAT_ERROR(S->readBytes(9, 1, Buffer), Succeeded());EXPECT_EQ(Buffer, ArrayRef<uint8_t>('J'));}TEST(MappedBlockStreamTest, TestWriteBytesBreakBoundary) {static uint8_t Data[] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};static uint8_t TestData[] = {'T', 'E', 'S', 'T', 'I', 'N', 'G', '.'};static uint8_t Expected[] = {'T', 'E', 'S', 'N', 'I','T', 'G', '.', '0', '0'};DiscontiguousStream F(BlocksAry, Data);auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),F, F.Allocator);ArrayRef<uint8_t> Buffer;EXPECT_THAT_ERROR(S->writeBytes(0, TestData), Succeeded());// First just compare the memory, then compare the result of reading the// string out.EXPECT_EQ(ArrayRef<uint8_t>(Data), ArrayRef<uint8_t>(Expected));EXPECT_THAT_ERROR(S->readBytes(0, 8, Buffer), Succeeded());EXPECT_EQ(Buffer, ArrayRef<uint8_t>(TestData));}TEST(MappedBlockStreamTest, TestWriteThenRead) {std::vector<uint8_t> DataBytes(10);MutableArrayRef<uint8_t> Data(DataBytes);const uint32_t Blocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};DiscontiguousStream F(Blocks, Data);auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),F, F.Allocator);enum class MyEnum : uint32_t { Val1 = 2908234, Val2 = 120891234 };using support::ulittle32_t;uint16_t u16[] = {31468, 0};uint32_t u32[] = {890723408, 0};MyEnum Enum[] = {MyEnum::Val1, MyEnum::Val2};StringRef ZStr[] = {"Zero Str", ""};StringRef FStr[] = {"Fixed Str", ""};uint8_t byteArray0[] = {'1', '2'};uint8_t byteArray1[] = {'0', '0'};ArrayRef<uint8_t> byteArrayRef0(byteArray0);ArrayRef<uint8_t> byteArrayRef1(byteArray1);ArrayRef<uint8_t> byteArray[] = {byteArrayRef0, byteArrayRef1};uint32_t intArr0[] = {890723408, 29082234};uint32_t intArr1[] = {890723408, 29082234};ArrayRef<uint32_t> intArray[] = {intArr0, intArr1};BinaryStreamReader Reader(*S);BinaryStreamWriter Writer(*S);EXPECT_THAT_ERROR(Writer.writeInteger(u16[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readInteger(u16[1]), Succeeded());EXPECT_EQ(u16[0], u16[1]);EXPECT_EQ(std::vector<uint8_t>({0, 0x7A, 0xEC, 0, 0, 0, 0, 0, 0, 0}),DataBytes);Reader.setOffset(0);Writer.setOffset(0);::memset(DataBytes.data(), 0, 10);EXPECT_THAT_ERROR(Writer.writeInteger(u32[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readInteger(u32[1]), Succeeded());EXPECT_EQ(u32[0], u32[1]);EXPECT_EQ(std::vector<uint8_t>({0x17, 0x5C, 0x50, 0, 0, 0, 0x35, 0, 0, 0}),DataBytes);Reader.setOffset(0);Writer.setOffset(0);::memset(DataBytes.data(), 0, 10);EXPECT_THAT_ERROR(Writer.writeEnum(Enum[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readEnum(Enum[1]), Succeeded());EXPECT_EQ(Enum[0], Enum[1]);EXPECT_EQ(std::vector<uint8_t>({0x2C, 0x60, 0x4A, 0, 0, 0, 0, 0, 0, 0}),DataBytes);Reader.setOffset(0);Writer.setOffset(0);::memset(DataBytes.data(), 0, 10);EXPECT_THAT_ERROR(Writer.writeCString(ZStr[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readCString(ZStr[1]), Succeeded());EXPECT_EQ(ZStr[0], ZStr[1]);EXPECT_EQ(std::vector<uint8_t>({'r', 'e', 'Z', ' ', 'S', 't', 'o', 'r', 0, 0}),DataBytes);Reader.setOffset(0);Writer.setOffset(0);::memset(DataBytes.data(), 0, 10);EXPECT_THAT_ERROR(Writer.writeFixedString(FStr[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readFixedString(FStr[1], FStr[0].size()),Succeeded());EXPECT_EQ(FStr[0], FStr[1]);EXPECT_EQ(std::vector<uint8_t>({'x', 'i', 'F', 'd', ' ', 'S', 'e', 't', 0, 'r'}),DataBytes);Reader.setOffset(0);Writer.setOffset(0);::memset(DataBytes.data(), 0, 10);EXPECT_THAT_ERROR(Writer.writeArray(byteArray[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readArray(byteArray[1], byteArray[0].size()),Succeeded());EXPECT_EQ(byteArray[0], byteArray[1]);EXPECT_EQ(std::vector<uint8_t>({0, 0x32, 0x31, 0, 0, 0, 0, 0, 0, 0}),DataBytes);Reader.setOffset(0);Writer.setOffset(0);::memset(DataBytes.data(), 0, 10);EXPECT_THAT_ERROR(Writer.writeArray(intArray[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readArray(intArray[1], intArray[0].size()),Succeeded());EXPECT_EQ(intArray[0], intArray[1]);}TEST(MappedBlockStreamTest, TestWriteContiguousStreamRef) {std::vector<uint8_t> DestDataBytes(10);MutableArrayRef<uint8_t> DestData(DestDataBytes);const uint32_t DestBlocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};std::vector<uint8_t> SrcDataBytes(10);MutableArrayRef<uint8_t> SrcData(SrcDataBytes);DiscontiguousStream F(DestBlocks, DestData);auto DestStream = WritableMappedBlockStream::createStream(F.block_size(), F.layout(), F, F.Allocator);// First write "Test Str" into the source stream.MutableBinaryByteStream SourceStream(SrcData, little);BinaryStreamWriter SourceWriter(SourceStream);EXPECT_THAT_ERROR(SourceWriter.writeCString("Test Str"), Succeeded());EXPECT_EQ(SrcDataBytes, std::vector<uint8_t>({'T', 'e', 's', 't', ' ', 'S', 't', 'r', 0, 0}));// Then write the source stream into the dest stream.BinaryStreamWriter DestWriter(*DestStream);EXPECT_THAT_ERROR(DestWriter.writeStreamRef(SourceStream), Succeeded());EXPECT_EQ(DestDataBytes, std::vector<uint8_t>({'s', 'e', 'T', ' ', 'S', 't', 't', 'r', 0, 0}));// Then read the string back out of the dest stream.StringRef Result;BinaryStreamReader DestReader(*DestStream);EXPECT_THAT_ERROR(DestReader.readCString(Result), Succeeded());EXPECT_EQ(Result, "Test Str");}TEST(MappedBlockStreamTest, TestWriteDiscontiguousStreamRef) {std::vector<uint8_t> DestDataBytes(10);MutableArrayRef<uint8_t> DestData(DestDataBytes);const uint32_t DestBlocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};std::vector<uint8_t> SrcDataBytes(10);MutableArrayRef<uint8_t> SrcData(SrcDataBytes);const uint32_t SrcBlocks[] = {1, 0, 6, 3, 4, 5, 2, 7, 8, 9};DiscontiguousStream DestF(DestBlocks, DestData);DiscontiguousStream SrcF(SrcBlocks, SrcData);auto Dest = WritableMappedBlockStream::createStream(DestF.block_size(), DestF.layout(), DestF, DestF.Allocator);auto Src = WritableMappedBlockStream::createStream(SrcF.block_size(), SrcF.layout(), SrcF, SrcF.Allocator);// First write "Test Str" into the source stream.BinaryStreamWriter SourceWriter(*Src);EXPECT_THAT_ERROR(SourceWriter.writeCString("Test Str"), Succeeded());EXPECT_EQ(SrcDataBytes, std::vector<uint8_t>({'e', 'T', 't', 't', ' ', 'S', 's', 'r', 0, 0}));// Then write the source stream into the dest stream.BinaryStreamWriter DestWriter(*Dest);EXPECT_THAT_ERROR(DestWriter.writeStreamRef(*Src), Succeeded());EXPECT_EQ(DestDataBytes, std::vector<uint8_t>({'s', 'e', 'T', ' ', 'S', 't', 't', 'r', 0, 0}));// Then read the string back out of the dest stream.StringRef Result;BinaryStreamReader DestReader(*Dest);EXPECT_THAT_ERROR(DestReader.readCString(Result), Succeeded());EXPECT_EQ(Result, "Test Str");}TEST(MappedBlockStreamTest, DataLivesAfterStreamDestruction) {std::vector<uint8_t> DataBytes(10);MutableArrayRef<uint8_t> Data(DataBytes);const uint32_t Blocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};StringRef Str[] = {"Zero Str", ""};DiscontiguousStream F(Blocks, Data);{auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),F, F.Allocator);BinaryStreamReader Reader(*S);BinaryStreamWriter Writer(*S);::memset(DataBytes.data(), 0, 10);EXPECT_THAT_ERROR(Writer.writeCString(Str[0]), Succeeded());EXPECT_THAT_ERROR(Reader.readCString(Str[1]), Succeeded());EXPECT_EQ(Str[0], Str[1]);}EXPECT_EQ(Str[0], Str[1]);}} // namespaceMATCHER_P3(BlockIsFilledWith, Layout, BlockIndex, Byte, "succeeded") {uint64_t Offset = msf::blockToOffset(BlockIndex, Layout.SB->BlockSize);ArrayRef<uint8_t> BufferRef = makeArrayRef(arg);BufferRef = BufferRef.slice(Offset, Layout.SB->BlockSize);return llvm::all_of(BufferRef, [this](uint8_t B) { return B == Byte; });}namespace {TEST(MappedBlockStreamTest, CreateFpmStream) {BumpPtrAllocator Allocator;SuperBlock SB;MSFLayout L;L.SB = &SB;SB.FreeBlockMapBlock = 1;SB.BlockSize = 4096;constexpr uint32_t NumFileBlocks = 4096 * 4;std::vector<uint8_t> MsfBuffer(NumFileBlocks * SB.BlockSize);MutableBinaryByteStream MsfStream(MsfBuffer, llvm::support::little);SB.NumBlocks = NumFileBlocks;auto FpmStream =WritableMappedBlockStream::createFpmStream(L, MsfStream, Allocator);// 4096 * 4 / 8 = 2048 bytes of FPM data is needed to describe 4096 * 4// blocks. This translates to 1 FPM block.EXPECT_EQ(2048u, FpmStream->getLength());EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks.size());EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks[0]);// All blocks from FPM1 should be 1 initialized, and all blocks from FPM2// should be 0 initialized (since we requested the main FPM, not the alt FPM)for (int I = 0; I < 4; ++I) {EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 1 + I * SB.BlockSize, 0xFF));EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 2 + I * SB.BlockSize, 0));}::memset(MsfBuffer.data(), 0, MsfBuffer.size());FpmStream =WritableMappedBlockStream::createFpmStream(L, MsfStream, Allocator, true);// 4096 * 4 / 8 = 2048 bytes of FPM data is needed to describe 4096 * 4// blocks. This translates to 1 FPM block.EXPECT_EQ(2048u, FpmStream->getLength());EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks.size());EXPECT_EQ(2u, FpmStream->getStreamLayout().Blocks[0]);// All blocks from FPM2 should be 1 initialized, and all blocks from FPM1// should be 0 initialized (since we requested the alt FPM, not the main FPM)for (int I = 0; I < 4; ++I) {EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 1 + I * SB.BlockSize, 0));EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 2 + I * SB.BlockSize, 0xFF));}}} // end anonymous namespace
//===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/MSF/MSFCommon.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::msf;TEST(MSFCommonTest, BytesToBlocks) {EXPECT_EQ(0ULL, bytesToBlocks(0, 4096));EXPECT_EQ(1ULL, bytesToBlocks(12, 4096));EXPECT_EQ(1ULL, bytesToBlocks(4096, 4096));EXPECT_EQ(2ULL, bytesToBlocks(4097, 4096));EXPECT_EQ(2ULL, bytesToBlocks(600, 512));}TEST(MSFCommonTest, FpmIntervals) {SuperBlock SB;SB.FreeBlockMapBlock = 1;SB.BlockSize = 4096;MSFLayout L;L.SB = &SB;SB.NumBlocks = 12;EXPECT_EQ(1u, getNumFpmIntervals(L, false));SB.NumBlocks = SB.BlockSize;EXPECT_EQ(1u, getNumFpmIntervals(L, false));SB.NumBlocks = SB.BlockSize + 1;EXPECT_EQ(1u, getNumFpmIntervals(L, false));SB.NumBlocks = SB.BlockSize * 8;EXPECT_EQ(1u, getNumFpmIntervals(L, false));SB.NumBlocks = SB.BlockSize * 8 + 1;EXPECT_EQ(2u, getNumFpmIntervals(L, false));SB.NumBlocks = 12;EXPECT_EQ(1u, getNumFpmIntervals(L, true));SB.NumBlocks = SB.BlockSize;EXPECT_EQ(1u, getNumFpmIntervals(L, true));SB.NumBlocks = SB.BlockSize * 8;EXPECT_EQ(8u, getNumFpmIntervals(L, true));// The FPM is going to look like this:// | 0 | 1 | 2 | ... | 4096 | 4097 | 4098 | ... |// | SB | FPM0 | FPM1 | Data | Data | FPM0 | FPM1 | ... |//// So when there are up to 4097 blocks (last index 4096), the final blocks// are data blocks. When there are 4098 blocks (last index 4097), there is// one terminating FPM block, and when there are 4099 blocks, there are two// terminating FPM blocks. Make sure all these cases are handled.// With 4096 or 4097 blocks, the last block is a data block so we only have// 1 interval.for (uint32_t I : {4096, 4097}) {// 1 FPM0 intervalEXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 1));EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 1));// 1 FPM1 intervalEXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 2));EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 2));}// With 4098 blocks, the last block belongs to FPM0 so we should have 2 FPM0// intervals.EXPECT_EQ(2U, getNumFpmIntervals(4096, 4098, true, 1));EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 1));// And 1 FPM1 interval.EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, true, 2));EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 2));// With 4099 blocks, the last block belongs to FPM1 so we should have 2// FPM0 intervals.EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 1));EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 1));// And 2 FPM1 intervals.EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 2));EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 2));}TEST(MSFCommonTest, FpmStreamLayout) {SuperBlock SB;MSFLayout L;L.SB = &SB;SB.FreeBlockMapBlock = 1;// Each FPM block has 4096 bytes for a maximum of 4096*8 allocation states.SB.BlockSize = 4096;// 1. When we're not including unused FPM data, the length of the FPM stream// should be only long enough to contain 1 bit for each block.// 1a. When the PDB has <= 4096*8 blocks, there should only be one FPM block.SB.NumBlocks = 8000;MSFStreamLayout SL = getFpmStreamLayout(L, false, false);EXPECT_EQ(1000u, SL.Length);EXPECT_EQ(1u, SL.Blocks.size());EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks.front());SL = getFpmStreamLayout(L, false, true);EXPECT_EQ(1000u, SL.Length);EXPECT_EQ(1u, SL.Blocks.size());EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks.front());// 1b. When the PDB has > 4096*8 blocks, there should be multiple FPM blocks.SB.NumBlocks = SB.BlockSize * 8 + 1;SL = getFpmStreamLayout(L, false, false);EXPECT_EQ(SB.BlockSize + 1, SL.Length);EXPECT_EQ(2u, SL.Blocks.size());EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks[0]);EXPECT_EQ(SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]);SL = getFpmStreamLayout(L, false, true);EXPECT_EQ(SB.BlockSize + 1, SL.Length);EXPECT_EQ(2u, SL.Blocks.size());EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks[0]);EXPECT_EQ(3u - SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]);// 2. When we are including unused FPM data, there should be one FPM block// at every BlockSize interval in the file, even if entire FPM blocks are// unused.SB.NumBlocks = SB.BlockSize * 8 + 3;SL = getFpmStreamLayout(L, true, false);EXPECT_EQ(SB.BlockSize * 9, SL.Length);EXPECT_EQ(9u, SL.Blocks.size());for (int I = 0; I < 9; ++I)EXPECT_EQ(I * SB.BlockSize + SB.FreeBlockMapBlock, SL.Blocks[I]);}
//===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/MSF/MSFBuilder.h"#include "llvm/DebugInfo/MSF/MSFCommon.h"#include "llvm/Testing/Support/Error.h"#include "gmock/gmock-matchers.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::msf;using namespace testing;namespace {class MSFBuilderTest : public testing::Test {protected:void initializeSimpleSuperBlock(msf::SuperBlock &SB) {initializeSuperBlock(SB);SB.NumBlocks = 1000;SB.NumDirectoryBytes = 8192;}void initializeSuperBlock(msf::SuperBlock &SB) {::memset(&SB, 0, sizeof(SB));::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic));SB.FreeBlockMapBlock = 1;SB.BlockMapAddr = 1;SB.BlockSize = 4096;SB.NumDirectoryBytes = 0;SB.NumBlocks = 2; // one for the Super Block, one for the directory}BumpPtrAllocator Allocator;};} // namespaceTEST_F(MSFBuilderTest, ValidateSuperBlockAccept) {// Test that a known good super block passes validation.SuperBlock SB;initializeSuperBlock(SB);EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Succeeded());}TEST_F(MSFBuilderTest, ValidateSuperBlockReject) {// Test that various known problems cause a super block to be rejected.SuperBlock SB;initializeSimpleSuperBlock(SB);// Mismatched magicSB.MagicBytes[0] = 8;EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());initializeSimpleSuperBlock(SB);// Block 0 is reserved for super block, can't be occupied by the block mapSB.BlockMapAddr = 0;EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());initializeSimpleSuperBlock(SB);// Block sizes have to be powers of 2.SB.BlockSize = 3120;EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());initializeSimpleSuperBlock(SB);// The directory itself has a maximum size.SB.NumDirectoryBytes = SB.BlockSize * SB.BlockSize / 4;EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Succeeded());SB.NumDirectoryBytes = SB.NumDirectoryBytes + 4;EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());}TEST_F(MSFBuilderTest, TestUsedBlocksMarkedAsUsed) {// Test that when assigning a stream to a known list of blocks, the blocks// are correctly marked as used after adding, but no other incorrect blocks// are accidentally marked as used.std::vector<uint32_t> Blocks = {4, 5, 6, 7, 8, 9, 10, 11, 12};// Allocate some extra blocks at the end so we can verify that they're free// after the initialization.uint32_t NumBlocks = msf::getMinimumBlockCount() + Blocks.size() + 10;auto ExpectedMsf = MSFBuilder::create(Allocator, 4096, NumBlocks);ASSERT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;EXPECT_THAT_EXPECTED(Msf.addStream(Blocks.size() * 4096, Blocks),Succeeded());for (auto B : Blocks) {EXPECT_FALSE(Msf.isBlockFree(B));}uint32_t FreeBlockStart = Blocks.back() + 1;for (uint32_t I = FreeBlockStart; I < NumBlocks; ++I) {EXPECT_TRUE(Msf.isBlockFree(I));}}TEST_F(MSFBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {// Test that adding a new stream correctly updates the directory. This only// tests the case where the directory *DOES NOT* grow large enough that it// crosses a Block boundary.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;auto ExpectedL1 = Msf.generateLayout();EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());MSFLayout &L1 = *ExpectedL1;auto OldDirBlocks = L1.DirectoryBlocks;EXPECT_EQ(1U, OldDirBlocks.size());auto ExpectedMsf2 = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf2, Succeeded());auto &Msf2 = *ExpectedMsf2;EXPECT_THAT_EXPECTED(Msf2.addStream(4000), Succeeded());EXPECT_EQ(1U, Msf2.getNumStreams());EXPECT_EQ(4000U, Msf2.getStreamSize(0));auto Blocks = Msf2.getStreamBlocks(0);EXPECT_EQ(1U, Blocks.size());auto ExpectedL2 = Msf2.generateLayout();EXPECT_THAT_EXPECTED(ExpectedL2, Succeeded());MSFLayout &L2 = *ExpectedL2;auto NewDirBlocks = L2.DirectoryBlocks;EXPECT_EQ(1U, NewDirBlocks.size());}TEST_F(MSFBuilderTest, TestAddStreamWithDirectoryBlockIncrease) {// Test that adding a new stream correctly updates the directory. This only// tests the case where the directory *DOES* grow large enough that it// crosses a Block boundary. This is because the newly added stream occupies// so many Blocks that need to be indexed in the directory that the directory// crosses a Block boundary.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;EXPECT_THAT_EXPECTED(Msf.addStream(4096 * 4096 / sizeof(uint32_t)),Succeeded());auto ExpectedL1 = Msf.generateLayout();EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());MSFLayout &L1 = *ExpectedL1;auto DirBlocks = L1.DirectoryBlocks;EXPECT_EQ(2U, DirBlocks.size());}TEST_F(MSFBuilderTest, TestGrowStreamNoBlockIncrease) {// Test growing an existing stream by a value that does not affect the number// of blocks it occupies.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;EXPECT_THAT_EXPECTED(Msf.addStream(1024), Succeeded());EXPECT_EQ(1024U, Msf.getStreamSize(0));auto OldStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(1U, OldStreamBlocks.size());EXPECT_THAT_ERROR(Msf.setStreamSize(0, 2048), Succeeded());EXPECT_EQ(2048U, Msf.getStreamSize(0));auto NewStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(1U, NewStreamBlocks.size());EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);}TEST_F(MSFBuilderTest, TestGrowStreamWithBlockIncrease) {// Test that growing an existing stream to a value large enough that it causes// the need to allocate new Blocks to the stream correctly updates the// stream's// block list.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;EXPECT_THAT_EXPECTED(Msf.addStream(2048), Succeeded());EXPECT_EQ(2048U, Msf.getStreamSize(0));std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(1U, OldStreamBlocks.size());EXPECT_THAT_ERROR(Msf.setStreamSize(0, 6144), Succeeded());EXPECT_EQ(6144U, Msf.getStreamSize(0));std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(2U, NewStreamBlocks.size());EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);EXPECT_NE(NewStreamBlocks[0], NewStreamBlocks[1]);}TEST_F(MSFBuilderTest, TestShrinkStreamNoBlockDecrease) {// Test that shrinking an existing stream by a value that does not affect the// number of Blocks it occupies makes no changes to stream's block list.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;EXPECT_THAT_EXPECTED(Msf.addStream(2048), Succeeded());EXPECT_EQ(2048U, Msf.getStreamSize(0));std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(1U, OldStreamBlocks.size());EXPECT_THAT_ERROR(Msf.setStreamSize(0, 1024), Succeeded());EXPECT_EQ(1024U, Msf.getStreamSize(0));std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(1U, NewStreamBlocks.size());EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);}TEST_F(MSFBuilderTest, TestShrinkStreamWithBlockDecrease) {// Test that shrinking an existing stream to a value large enough that it// causes the need to deallocate new Blocks to the stream correctly updates// the stream's block list.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;EXPECT_THAT_EXPECTED(Msf.addStream(6144), Succeeded());EXPECT_EQ(6144U, Msf.getStreamSize(0));std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(2U, OldStreamBlocks.size());EXPECT_THAT_ERROR(Msf.setStreamSize(0, 2048), Succeeded());EXPECT_EQ(2048U, Msf.getStreamSize(0));std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);EXPECT_EQ(1U, NewStreamBlocks.size());EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);}TEST_F(MSFBuilderTest, TestRejectReusedStreamBlock) {// Test that attempting to add a stream and assigning a block that is already// in use by another stream fails.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;EXPECT_THAT_EXPECTED(Msf.addStream(6144), Succeeded());std::vector<uint32_t> Blocks = {2, 3};EXPECT_THAT_EXPECTED(Msf.addStream(6144, Blocks), Failed());}TEST_F(MSFBuilderTest, TestBlockCountsWhenAddingStreams) {// Test that when adding multiple streams, the number of used and free Blocks// allocated to the MSF file are as expected.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;// one for the super block, one for the directory block mapuint32_t NumUsedBlocks = Msf.getNumUsedBlocks();EXPECT_EQ(msf::getMinimumBlockCount(), NumUsedBlocks);EXPECT_EQ(0U, Msf.getNumFreeBlocks());const uint32_t StreamSizes[] = {4000, 6193, 189723};for (int I = 0; I < 3; ++I) {EXPECT_THAT_EXPECTED(Msf.addStream(StreamSizes[I]), Succeeded());NumUsedBlocks += bytesToBlocks(StreamSizes[I], 4096);EXPECT_EQ(NumUsedBlocks, Msf.getNumUsedBlocks());EXPECT_EQ(0U, Msf.getNumFreeBlocks());}}TEST_F(MSFBuilderTest, BuildMsfLayout) {// Test that we can generate an MSFLayout structure from a valid layout// specification.auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;const uint32_t StreamSizes[] = {4000, 6193, 189723};uint32_t ExpectedNumBlocks = msf::getMinimumBlockCount();for (int I = 0; I < 3; ++I) {EXPECT_THAT_EXPECTED(Msf.addStream(StreamSizes[I]), Succeeded());ExpectedNumBlocks += bytesToBlocks(StreamSizes[I], 4096);}++ExpectedNumBlocks; // The directory itself should use 1 blockauto ExpectedLayout = Msf.generateLayout();EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());MSFLayout &L = *ExpectedLayout;EXPECT_EQ(4096U, L.SB->BlockSize);EXPECT_EQ(ExpectedNumBlocks, L.SB->NumBlocks);EXPECT_EQ(1U, L.DirectoryBlocks.size());EXPECT_EQ(3U, L.StreamMap.size());EXPECT_EQ(3U, L.StreamSizes.size());for (int I = 0; I < 3; ++I) {EXPECT_EQ(StreamSizes[I], L.StreamSizes[I]);uint32_t ExpectedNumBlocks = bytesToBlocks(StreamSizes[I], 4096);EXPECT_EQ(ExpectedNumBlocks, L.StreamMap[I].size());}}TEST_F(MSFBuilderTest, UseDirectoryBlockHint) {Expected<MSFBuilder> ExpectedMsf = MSFBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 1, false);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;uint32_t B = msf::getFirstUnreservedBlock();EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1}), Succeeded());EXPECT_THAT_EXPECTED(Msf.addStream(2048, {B + 2}), Succeeded());auto ExpectedLayout = Msf.generateLayout();EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());MSFLayout &L = *ExpectedLayout;EXPECT_EQ(msf::getMinimumBlockCount() + 2, L.SB->NumBlocks);EXPECT_EQ(1U, L.DirectoryBlocks.size());EXPECT_EQ(1U, L.StreamMap[0].size());EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);EXPECT_EQ(B + 2, L.StreamMap[0].front());}TEST_F(MSFBuilderTest, DirectoryBlockHintInsufficient) {Expected<MSFBuilder> ExpectedMsf =MSFBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;uint32_t B = msf::getFirstUnreservedBlock();EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1}), Succeeded());uint32_t Size = 4096 * 4096 / 4;EXPECT_THAT_EXPECTED(Msf.addStream(Size), Succeeded());auto ExpectedLayout = Msf.generateLayout();EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());MSFLayout &L = *ExpectedLayout;EXPECT_EQ(2U, L.DirectoryBlocks.size());EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);}TEST_F(MSFBuilderTest, DirectoryBlockHintOverestimated) {Expected<MSFBuilder> ExpectedMsf =MSFBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2);EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;uint32_t B = msf::getFirstUnreservedBlock();EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1, B + 2}), Succeeded());ASSERT_THAT_EXPECTED(Msf.addStream(2048), Succeeded());auto ExpectedLayout = Msf.generateLayout();ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());MSFLayout &L = *ExpectedLayout;EXPECT_EQ(1U, L.DirectoryBlocks.size());EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);}TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) {Expected<MSFBuilder> ExpectedMsf = MSFBuilder::create(Allocator, 4096);ASSERT_THAT_EXPECTED(ExpectedMsf, Succeeded());auto &Msf = *ExpectedMsf;// A block is 4096 bytes, and every 4096 blocks we have 2 reserved FPM blocks.// By creating add a stream that spans 4096*4096*3 bytes, we ensure that we// cross over a couple of reserved FPM blocks, and that none of them are// allocated to the stream.constexpr uint32_t StreamSize = 4096 * 4096 * 3;Expected<uint32_t> SN = Msf.addStream(StreamSize);ASSERT_THAT_EXPECTED(SN, Succeeded());auto ExpectedLayout = Msf.generateLayout();ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());MSFLayout &L = *ExpectedLayout;auto BlocksRef = L.StreamMap[*SN];std::vector<uint32_t> Blocks(BlocksRef.begin(), BlocksRef.end());EXPECT_EQ(StreamSize, L.StreamSizes[*SN]);for (uint32_t I = 0; I <= 3; ++I) {// Pages from both FPMs are always allocated.EXPECT_FALSE(L.FreePageMap.test(2 + I * 4096));EXPECT_FALSE(L.FreePageMap.test(1 + I * 4096));}for (uint32_t I = 1; I <= 3; ++I) {EXPECT_THAT(Blocks, Not(Contains(1 + I * 4096)));EXPECT_THAT(Blocks, Not(Contains(2 + I * 4096)));}}
set(LLVM_LINK_COMPONENTSDebugInfoMSF)add_llvm_unittest(DebugInfoMSFTestsMappedBlockStreamTest.cppMSFBuilderTest.cppMSFCommonTest.cpp)target_link_libraries(DebugInfoMSFTests PRIVATE LLVMTestingSupport)set_property(TARGET DebugInfoMSFTests PROPERTY FOLDER "Tests/UnitTests/DebugInfoTests")
//===- llvm/unittest/DebugInfo/GSYMTest.cpp -------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/DenseMap.h"#include "llvm/ADT/SmallString.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/DebugInfo/GSYM/DwarfTransformer.h"#include "llvm/DebugInfo/GSYM/ExtractRanges.h"#include "llvm/DebugInfo/GSYM/FileEntry.h"#include "llvm/DebugInfo/GSYM/FileWriter.h"#include "llvm/DebugInfo/GSYM/FunctionInfo.h"#include "llvm/DebugInfo/GSYM/GsymCreator.h"#include "llvm/DebugInfo/GSYM/GsymReader.h"#include "llvm/DebugInfo/GSYM/Header.h"#include "llvm/DebugInfo/GSYM/InlineInfo.h"#include "llvm/DebugInfo/GSYM/StringTable.h"#include "llvm/ObjectYAML/DWARFEmitter.h"#include "llvm/Support/DataExtractor.h"#include "llvm/Support/Endian.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include "gmock/gmock.h"#include <string>using namespace llvm;using namespace gsym;void checkError(ArrayRef<std::string> ExpectedMsgs, Error Err) {ASSERT_TRUE(bool(Err));size_t WhichMsg = 0;Error Remaining =handleErrors(std::move(Err), [&](const ErrorInfoBase &Actual) {ASSERT_LT(WhichMsg, ExpectedMsgs.size());// Use .str(), because googletest doesn't visualise a StringRef// properly.EXPECT_EQ(Actual.message(), ExpectedMsgs[WhichMsg++]);});EXPECT_EQ(WhichMsg, ExpectedMsgs.size());EXPECT_FALSE(Remaining);}void checkError(std::string ExpectedMsg, Error Err) {checkError(ArrayRef<std::string>{ExpectedMsg}, std::move(Err));}TEST(GSYMTest, TestFileEntry) {// Make sure default constructed GSYM FileEntry has zeroes in the// directory and basename string table indexes.FileEntry empty1;FileEntry empty2;EXPECT_EQ(empty1.Dir, 0u);EXPECT_EQ(empty1.Base, 0u);// Verify equality operator worksFileEntry a1(10, 30);FileEntry a2(10, 30);FileEntry b(10, 40);EXPECT_EQ(empty1, empty2);EXPECT_EQ(a1, a2);EXPECT_NE(a1, b);EXPECT_NE(a1, empty1);// Test we can use llvm::gsym::FileEntry in llvm::DenseMap.DenseMap<FileEntry, uint32_t> EntryToIndex;constexpr uint32_t Index1 = 1;constexpr uint32_t Index2 = 1;auto R = EntryToIndex.insert(std::make_pair(a1, Index1));EXPECT_TRUE(R.second);EXPECT_EQ(R.first->second, Index1);R = EntryToIndex.insert(std::make_pair(a1, Index1));EXPECT_FALSE(R.second);EXPECT_EQ(R.first->second, Index1);R = EntryToIndex.insert(std::make_pair(b, Index2));EXPECT_TRUE(R.second);EXPECT_EQ(R.first->second, Index2);R = EntryToIndex.insert(std::make_pair(a1, Index2));EXPECT_FALSE(R.second);EXPECT_EQ(R.first->second, Index2);}TEST(GSYMTest, TestFunctionInfo) {// Test GSYM FunctionInfo structs and functionality.FunctionInfo invalid;EXPECT_FALSE(invalid.isValid());EXPECT_FALSE(invalid.hasRichInfo());const uint64_t StartAddr = 0x1000;const uint64_t EndAddr = 0x1100;const uint64_t Size = EndAddr - StartAddr;const uint32_t NameOffset = 30;FunctionInfo FI(StartAddr, Size, NameOffset);EXPECT_TRUE(FI.isValid());EXPECT_FALSE(FI.hasRichInfo());EXPECT_EQ(FI.startAddress(), StartAddr);EXPECT_EQ(FI.endAddress(), EndAddr);EXPECT_EQ(FI.size(), Size);const uint32_t FileIdx = 1;const uint32_t Line = 12;FI.OptLineTable = LineTable();FI.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line));EXPECT_TRUE(FI.hasRichInfo());FI.clear();EXPECT_FALSE(FI.isValid());EXPECT_FALSE(FI.hasRichInfo());FunctionInfo A1(0x1000, 0x100, NameOffset);FunctionInfo A2(0x1000, 0x100, NameOffset);FunctionInfo B;// Check == operatorEXPECT_EQ(A1, A2);// Make sure things are not equal if they only differ by start address.B = A2;B.Range = {0x1001, B.endAddress()};EXPECT_NE(B, A2);// Make sure things are not equal if they only differ by size.B = A2;B.Range = {B.startAddress(), B.startAddress() + 0x101};EXPECT_NE(B, A2);// Make sure things are not equal if they only differ by name.B = A2;B.Name = 60;EXPECT_NE(B, A2);// Check < operator.// Check less than where address differs.B = A2;B.Range = {A2.startAddress() + 0x1000, A2.endAddress() + 0x1000};EXPECT_LT(A1, B);// We use the < operator to take a variety of different FunctionInfo// structs from a variety of sources: symtab, debug info, runtime info// and we sort them and want the sorting to allow us to quickly get the// best version of a function info.FunctionInfo FISymtab(StartAddr, Size, NameOffset);FunctionInfo FIWithLines(StartAddr, Size, NameOffset);FIWithLines.OptLineTable = LineTable();FIWithLines.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line));// Test that a FunctionInfo with just a name and size is less than one// that has name, size and any number of line table entriesEXPECT_LT(FISymtab, FIWithLines);FunctionInfo FIWithLinesAndInline = FIWithLines;FIWithLinesAndInline.Inline = InlineInfo();FIWithLinesAndInline.Inline->Ranges.insert(AddressRange(StartAddr, StartAddr + 0x10));// Test that a FunctionInfo with name, size, and line entries is less than// the same one with valid inline infoEXPECT_LT(FIWithLines, FIWithLinesAndInline);// Test if we have an entry with lines and one with more lines for the same// range, the ones with more lines is greater than the one with less.FunctionInfo FIWithMoreLines = FIWithLines;FIWithMoreLines.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line+5));EXPECT_LT(FIWithLines, FIWithMoreLines);// Test that if we have the same number of lines we compare the line entries// in the FunctionInfo.OptLineTable.Lines vector.FunctionInfo FIWithLinesWithHigherAddress = FIWithLines;FIWithLinesWithHigherAddress.OptLineTable->get(0).Addr += 0x10;EXPECT_LT(FIWithLines, FIWithLinesWithHigherAddress);}static void TestFunctionInfoDecodeError(llvm::support::endianness ByteOrder,StringRef Bytes,const uint64_t BaseAddr,std::string ExpectedErrorMsg) {uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<FunctionInfo> Decoded = FunctionInfo::decode(Data, BaseAddr);// Make sure decoding fails.ASSERT_FALSE((bool)Decoded);// Make sure decoded object is the same as the one we encoded.checkError(ExpectedErrorMsg, Decoded.takeError());}TEST(GSYMTest, TestFunctionInfoDecodeErrors) {// Test decoding FunctionInfo objects that ensure we report an appropriate// error message.const llvm::support::endianness ByteOrder = llvm::support::little;SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);const uint64_t BaseAddr = 0x100;TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000000: missing FunctionInfo Size");FW.writeU32(0x100); // Function size.TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000004: missing FunctionInfo Name");// Write out an invalid Name string table offset of zero.FW.writeU32(0);TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000004: invalid FunctionInfo Name value 0x00000000");// Modify the Name to be 0x00000001, which is a valid value.FW.fixup32(0x00000001, 4);TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000008: missing FunctionInfo InfoType value");auto FixupOffset = FW.tell();FW.writeU32(1); // InfoType::LineTableInfo.TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x0000000c: missing FunctionInfo InfoType length");FW.fixup32(4, FixupOffset); // Write an invalid InfoType enumeration valueFW.writeU32(0); // LineTableInfo InfoType data length.TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000008: unsupported InfoType 4");}static void TestFunctionInfoEncodeError(llvm::support::endianness ByteOrder,const FunctionInfo &FI,std::string ExpectedErrorMsg) {SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);Expected<uint64_t> ExpectedOffset = FI.encode(FW);ASSERT_FALSE(ExpectedOffset);checkError(ExpectedErrorMsg, ExpectedOffset.takeError());}TEST(GSYMTest, TestFunctionInfoEncodeErrors) {const uint64_t FuncAddr = 0x1000;const uint64_t FuncSize = 0x100;const uint32_t InvalidName = 0;const uint32_t ValidName = 1;FunctionInfo InvalidNameFI(FuncAddr, FuncSize, InvalidName);TestFunctionInfoEncodeError(llvm::support::little, InvalidNameFI,"attempted to encode invalid FunctionInfo object");FunctionInfo InvalidLineTableFI(FuncAddr, FuncSize, ValidName);// Empty line tables are not valid. Verify if the encoding of anything// in our line table fails, that we see get the error propagated.InvalidLineTableFI.OptLineTable = LineTable();TestFunctionInfoEncodeError(llvm::support::little, InvalidLineTableFI,"attempted to encode invalid LineTable object");FunctionInfo InvalidInlineInfoFI(FuncAddr, FuncSize, ValidName);// Empty line tables are not valid. Verify if the encoding of anything// in our line table fails, that we see get the error propagated.InvalidInlineInfoFI.Inline = InlineInfo();TestFunctionInfoEncodeError(llvm::support::little, InvalidInlineInfoFI,"attempted to encode invalid InlineInfo object");}static void TestFunctionInfoEncodeDecode(llvm::support::endianness ByteOrder,const FunctionInfo &FI) {// Test encoding and decoding FunctionInfo objects.SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);llvm::Expected<uint64_t> ExpectedOffset = FI.encode(FW);ASSERT_TRUE(bool(ExpectedOffset));// Verify we got the encoded offset back from the encode function.ASSERT_EQ(ExpectedOffset.get(), 0ULL);std::string Bytes(OutStrm.str());uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<FunctionInfo> Decoded =FunctionInfo::decode(Data, FI.Range.start());// Make sure decoding succeeded.ASSERT_TRUE((bool)Decoded);// Make sure decoded object is the same as the one we encoded.EXPECT_EQ(FI, Decoded.get());}static void AddLines(uint64_t FuncAddr, uint32_t FileIdx, FunctionInfo &FI) {FI.OptLineTable = LineTable();LineEntry Line0(FuncAddr + 0x000, FileIdx, 10);LineEntry Line1(FuncAddr + 0x010, FileIdx, 11);LineEntry Line2(FuncAddr + 0x100, FileIdx, 1000);FI.OptLineTable->push(Line0);FI.OptLineTable->push(Line1);FI.OptLineTable->push(Line2);}static void AddInline(uint64_t FuncAddr, uint64_t FuncSize, FunctionInfo &FI) {FI.Inline = InlineInfo();FI.Inline->Ranges.insert(AddressRange(FuncAddr, FuncAddr + FuncSize));InlineInfo Inline1;Inline1.Ranges.insert(AddressRange(FuncAddr + 0x10, FuncAddr + 0x30));Inline1.Name = 1;Inline1.CallFile = 1;Inline1.CallLine = 11;FI.Inline->Children.push_back(Inline1);}TEST(GSYMTest, TestFunctionInfoEncoding) {constexpr uint64_t FuncAddr = 0x1000;constexpr uint64_t FuncSize = 0x100;constexpr uint32_t FuncName = 1;constexpr uint32_t FileIdx = 1;// Make sure that we can encode and decode a FunctionInfo with no line table// or inline info.FunctionInfo FI(FuncAddr, FuncSize, FuncName);TestFunctionInfoEncodeDecode(llvm::support::little, FI);TestFunctionInfoEncodeDecode(llvm::support::big, FI);// Make sure that we can encode and decode a FunctionInfo with a line table// and no inline info.FunctionInfo FILines(FuncAddr, FuncSize, FuncName);AddLines(FuncAddr, FileIdx, FILines);TestFunctionInfoEncodeDecode(llvm::support::little, FILines);TestFunctionInfoEncodeDecode(llvm::support::big, FILines);// Make sure that we can encode and decode a FunctionInfo with no line table// and with inline info.FunctionInfo FIInline(FuncAddr, FuncSize, FuncName);AddInline(FuncAddr, FuncSize, FIInline);TestFunctionInfoEncodeDecode(llvm::support::little, FIInline);TestFunctionInfoEncodeDecode(llvm::support::big, FIInline);// Make sure that we can encode and decode a FunctionInfo with no line table// and with inline info.FunctionInfo FIBoth(FuncAddr, FuncSize, FuncName);AddLines(FuncAddr, FileIdx, FIBoth);AddInline(FuncAddr, FuncSize, FIBoth);TestFunctionInfoEncodeDecode(llvm::support::little, FIBoth);TestFunctionInfoEncodeDecode(llvm::support::big, FIBoth);}static void TestInlineInfoEncodeDecode(llvm::support::endianness ByteOrder,const InlineInfo &Inline) {// Test encoding and decoding InlineInfo objectsSmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);const uint64_t BaseAddr = Inline.Ranges[0].start();llvm::Error Err = Inline.encode(FW, BaseAddr);ASSERT_FALSE(Err);std::string Bytes(OutStrm.str());uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<InlineInfo> Decoded = InlineInfo::decode(Data, BaseAddr);// Make sure decoding succeeded.ASSERT_TRUE((bool)Decoded);// Make sure decoded object is the same as the one we encoded.EXPECT_EQ(Inline, Decoded.get());}static void TestInlineInfoDecodeError(llvm::support::endianness ByteOrder,StringRef Bytes, const uint64_t BaseAddr,std::string ExpectedErrorMsg) {uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<InlineInfo> Decoded = InlineInfo::decode(Data, BaseAddr);// Make sure decoding fails.ASSERT_FALSE((bool)Decoded);// Make sure decoded object is the same as the one we encoded.checkError(ExpectedErrorMsg, Decoded.takeError());}static void TestInlineInfoEncodeError(llvm::support::endianness ByteOrder,const InlineInfo &Inline,std::string ExpectedErrorMsg) {SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);const uint64_t BaseAddr =Inline.Ranges.empty() ? 0 : Inline.Ranges[0].start();llvm::Error Err = Inline.encode(FW, BaseAddr);checkError(ExpectedErrorMsg, std::move(Err));}TEST(GSYMTest, TestInlineInfo) {// Test InlineInfo structs.InlineInfo II;EXPECT_FALSE(II.isValid());II.Ranges.insert(AddressRange(0x1000, 0x2000));// Make sure InlineInfo in valid with just an address range since// top level InlineInfo objects have ranges with no name, call file// or call lineEXPECT_TRUE(II.isValid());// Make sure InlineInfo isn't after being cleared.II.clear();EXPECT_FALSE(II.isValid());// Create an InlineInfo that contains the following data. The// indentation of the address range indicates the parent child// relationships of the InlineInfo objects://// Variable Range and values// =========== ====================================================// Root [0x100-0x200) (no name, file, or line)// Inline1 [0x150-0x160) Name = 1, File = 1, Line = 11// Inline1Sub1 [0x152-0x155) Name = 2, File = 2, Line = 22// Inline1Sub2 [0x157-0x158) Name = 3, File = 3, Line = 33InlineInfo Root;Root.Ranges.insert(AddressRange(0x100, 0x200));InlineInfo Inline1;Inline1.Ranges.insert(AddressRange(0x150, 0x160));Inline1.Name = 1;Inline1.CallFile = 1;Inline1.CallLine = 11;InlineInfo Inline1Sub1;Inline1Sub1.Ranges.insert(AddressRange(0x152, 0x155));Inline1Sub1.Name = 2;Inline1Sub1.CallFile = 2;Inline1Sub1.CallLine = 22;InlineInfo Inline1Sub2;Inline1Sub2.Ranges.insert(AddressRange(0x157, 0x158));Inline1Sub2.Name = 3;Inline1Sub2.CallFile = 3;Inline1Sub2.CallLine = 33;Inline1.Children.push_back(Inline1Sub1);Inline1.Children.push_back(Inline1Sub2);Root.Children.push_back(Inline1);// Make sure an address that is out of range won't matchEXPECT_FALSE(Root.getInlineStack(0x50));// Verify that we get no inline stacks for addresses out of [0x100-0x200)EXPECT_FALSE(Root.getInlineStack(Root.Ranges[0].start() - 1));EXPECT_FALSE(Root.getInlineStack(Root.Ranges[0].end()));// Verify we get no inline stack entries for addresses that are in// [0x100-0x200) but not in [0x150-0x160)EXPECT_FALSE(Root.getInlineStack(Inline1.Ranges[0].start() - 1));EXPECT_FALSE(Root.getInlineStack(Inline1.Ranges[0].end()));// Verify we get one inline stack entry for addresses that are in// [[0x150-0x160)) but not in [0x152-0x155) or [0x157-0x158)auto InlineInfos = Root.getInlineStack(Inline1.Ranges[0].start());ASSERT_TRUE(InlineInfos);ASSERT_EQ(InlineInfos->size(), 1u);ASSERT_EQ(*InlineInfos->at(0), Inline1);InlineInfos = Root.getInlineStack(Inline1.Ranges[0].end() - 1);EXPECT_TRUE(InlineInfos);ASSERT_EQ(InlineInfos->size(), 1u);ASSERT_EQ(*InlineInfos->at(0), Inline1);// Verify we get two inline stack entries for addresses that are in// [0x152-0x155)InlineInfos = Root.getInlineStack(Inline1Sub1.Ranges[0].start());EXPECT_TRUE(InlineInfos);ASSERT_EQ(InlineInfos->size(), 2u);ASSERT_EQ(*InlineInfos->at(0), Inline1Sub1);ASSERT_EQ(*InlineInfos->at(1), Inline1);InlineInfos = Root.getInlineStack(Inline1Sub1.Ranges[0].end() - 1);EXPECT_TRUE(InlineInfos);ASSERT_EQ(InlineInfos->size(), 2u);ASSERT_EQ(*InlineInfos->at(0), Inline1Sub1);ASSERT_EQ(*InlineInfos->at(1), Inline1);// Verify we get two inline stack entries for addresses that are in// [0x157-0x158)InlineInfos = Root.getInlineStack(Inline1Sub2.Ranges[0].start());EXPECT_TRUE(InlineInfos);ASSERT_EQ(InlineInfos->size(), 2u);ASSERT_EQ(*InlineInfos->at(0), Inline1Sub2);ASSERT_EQ(*InlineInfos->at(1), Inline1);InlineInfos = Root.getInlineStack(Inline1Sub2.Ranges[0].end() - 1);EXPECT_TRUE(InlineInfos);ASSERT_EQ(InlineInfos->size(), 2u);ASSERT_EQ(*InlineInfos->at(0), Inline1Sub2);ASSERT_EQ(*InlineInfos->at(1), Inline1);// Test encoding and decoding InlineInfo objectsTestInlineInfoEncodeDecode(llvm::support::little, Root);TestInlineInfoEncodeDecode(llvm::support::big, Root);}TEST(GSYMTest, TestInlineInfoEncodeErrors) {// Test InlineInfo encoding errors.// Test that we get an error when trying to encode an InlineInfo object// that has no ranges.InlineInfo Empty;std::string EmptyErr("attempted to encode invalid InlineInfo object");TestInlineInfoEncodeError(llvm::support::little, Empty, EmptyErr);TestInlineInfoEncodeError(llvm::support::big, Empty, EmptyErr);// Verify that we get an error trying to encode an InlineInfo object that has// a child InlineInfo that has no ranges.InlineInfo ContainsEmpty;ContainsEmpty.Ranges.insert({0x100, 0x200});ContainsEmpty.Children.push_back(Empty);TestInlineInfoEncodeError(llvm::support::little, ContainsEmpty, EmptyErr);TestInlineInfoEncodeError(llvm::support::big, ContainsEmpty, EmptyErr);// Verify that we get an error trying to encode an InlineInfo object that has// a child whose address range is not contained in the parent address range.InlineInfo ChildNotContained;std::string ChildNotContainedErr("child range not contained in parent");ChildNotContained.Ranges.insert({0x100, 0x200});InlineInfo ChildNotContainedChild;ChildNotContainedChild.Ranges.insert({0x200, 0x300});ChildNotContained.Children.push_back(ChildNotContainedChild);TestInlineInfoEncodeError(llvm::support::little, ChildNotContained,ChildNotContainedErr);TestInlineInfoEncodeError(llvm::support::big, ChildNotContained,ChildNotContainedErr);}TEST(GSYMTest, TestInlineInfoDecodeErrors) {// Test decoding InlineInfo objects that ensure we report an appropriate// error message.const llvm::support::endianness ByteOrder = llvm::support::little;SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);const uint64_t BaseAddr = 0x100;TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000000: missing InlineInfo address ranges data");AddressRanges Ranges;Ranges.insert({BaseAddr, BaseAddr+0x100});encodeRanges(Ranges, FW, BaseAddr);TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000004: missing InlineInfo uint8_t indicating children");FW.writeU8(0);TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000005: missing InlineInfo uint32_t for name");FW.writeU32(0);TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000009: missing ULEB128 for InlineInfo call file");FW.writeU8(0);TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x0000000a: missing ULEB128 for InlineInfo call line");}TEST(GSYMTest, TestLineEntry) {// test llvm::gsym::LineEntry structs.const uint64_t ValidAddr = 0x1000;const uint64_t InvalidFileIdx = 0;const uint32_t ValidFileIdx = 1;const uint32_t ValidLine = 5;LineEntry Invalid;EXPECT_FALSE(Invalid.isValid());// Make sure that an entry is invalid if it has a bad file index.LineEntry BadFile(ValidAddr, InvalidFileIdx, ValidLine);EXPECT_FALSE(BadFile.isValid());// Test operatorsLineEntry E1(ValidAddr, ValidFileIdx, ValidLine);LineEntry E2(ValidAddr, ValidFileIdx, ValidLine);LineEntry DifferentAddr(ValidAddr + 1, ValidFileIdx, ValidLine);LineEntry DifferentFile(ValidAddr, ValidFileIdx + 1, ValidLine);LineEntry DifferentLine(ValidAddr, ValidFileIdx, ValidLine + 1);EXPECT_TRUE(E1.isValid());EXPECT_EQ(E1, E2);EXPECT_NE(E1, DifferentAddr);EXPECT_NE(E1, DifferentFile);EXPECT_NE(E1, DifferentLine);EXPECT_LT(E1, DifferentAddr);}TEST(GSYMTest, TestStringTable) {StringTable StrTab(StringRef("\0Hello\0World\0", 13));// Test extracting strings from a string table.EXPECT_EQ(StrTab.getString(0), "");EXPECT_EQ(StrTab.getString(1), "Hello");EXPECT_EQ(StrTab.getString(7), "World");EXPECT_EQ(StrTab.getString(8), "orld");// Test pointing to last NULL terminator gets empty string.EXPECT_EQ(StrTab.getString(12), "");// Test pointing to past end gets empty string.EXPECT_EQ(StrTab.getString(13), "");}static void TestFileWriterHelper(llvm::support::endianness ByteOrder) {SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);const int64_t MinSLEB = INT64_MIN;const int64_t MaxSLEB = INT64_MAX;const uint64_t MinULEB = 0;const uint64_t MaxULEB = UINT64_MAX;const uint8_t U8 = 0x10;const uint16_t U16 = 0x1122;const uint32_t U32 = 0x12345678;const uint64_t U64 = 0x33445566778899aa;const char *Hello = "hello";FW.writeU8(U8);FW.writeU16(U16);FW.writeU32(U32);FW.writeU64(U64);FW.alignTo(16);const off_t FixupOffset = FW.tell();FW.writeU32(0);FW.writeSLEB(MinSLEB);FW.writeSLEB(MaxSLEB);FW.writeULEB(MinULEB);FW.writeULEB(MaxULEB);FW.writeNullTerminated(Hello);// Test Seek, Tell using Fixup32.FW.fixup32(U32, FixupOffset);std::string Bytes(OutStrm.str());uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);uint64_t Offset = 0;EXPECT_EQ(Data.getU8(&Offset), U8);EXPECT_EQ(Data.getU16(&Offset), U16);EXPECT_EQ(Data.getU32(&Offset), U32);EXPECT_EQ(Data.getU64(&Offset), U64);Offset = alignTo(Offset, 16);EXPECT_EQ(Data.getU32(&Offset), U32);EXPECT_EQ(Data.getSLEB128(&Offset), MinSLEB);EXPECT_EQ(Data.getSLEB128(&Offset), MaxSLEB);EXPECT_EQ(Data.getULEB128(&Offset), MinULEB);EXPECT_EQ(Data.getULEB128(&Offset), MaxULEB);EXPECT_EQ(Data.getCStrRef(&Offset), StringRef(Hello));}TEST(GSYMTest, TestFileWriter) {TestFileWriterHelper(llvm::support::little);TestFileWriterHelper(llvm::support::big);}TEST(GSYMTest, TestAddressRangeEncodeDecode) {// Test encoding and decoding AddressRange objects. AddressRange objects// are always stored as offsets from the a base address. The base address// is the FunctionInfo's base address for function level ranges, and is// the base address of the parent range for subranges.SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = llvm::support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);const uint64_t BaseAddr = 0x1000;const AddressRange Range1(0x1000, 0x1010);const AddressRange Range2(0x1020, 0x1030);encodeRange(Range1, FW, BaseAddr);encodeRange(Range2, FW, BaseAddr);std::string Bytes(OutStrm.str());uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);AddressRange DecodedRange1, DecodedRange2;uint64_t Offset = 0;DecodedRange1 = decodeRange(Data, BaseAddr, Offset);DecodedRange2 = decodeRange(Data, BaseAddr, Offset);EXPECT_EQ(Range1, DecodedRange1);EXPECT_EQ(Range2, DecodedRange2);}static void TestAddressRangeEncodeDecodeHelper(const AddressRanges &Ranges,const uint64_t BaseAddr) {SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = llvm::support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);encodeRanges(Ranges, FW, BaseAddr);std::string Bytes(OutStrm.str());uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);AddressRanges DecodedRanges;uint64_t Offset = 0;decodeRanges(DecodedRanges, Data, BaseAddr, Offset);EXPECT_EQ(Ranges, DecodedRanges);}TEST(GSYMTest, TestAddressRangesEncodeDecode) {// Test encoding and decoding AddressRanges. AddressRanges objects contain// ranges that are stored as offsets from the a base address. The base address// is the FunctionInfo's base address for function level ranges, and is the// base address of the parent range for subranges.const uint64_t BaseAddr = 0x1000;// Test encoding and decoding with no ranges.AddressRanges Ranges;TestAddressRangeEncodeDecodeHelper(Ranges, BaseAddr);// Test encoding and decoding with 1 range.Ranges.insert(AddressRange(0x1000, 0x1010));TestAddressRangeEncodeDecodeHelper(Ranges, BaseAddr);// Test encoding and decoding with multiple ranges.Ranges.insert(AddressRange(0x1020, 0x1030));Ranges.insert(AddressRange(0x1050, 0x1070));TestAddressRangeEncodeDecodeHelper(Ranges, BaseAddr);}static void TestLineTableHelper(llvm::support::endianness ByteOrder,const LineTable <) {SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);const uint64_t BaseAddr = LT[0].Addr;llvm::Error Err = LT.encode(FW, BaseAddr);ASSERT_FALSE(Err);std::string Bytes(OutStrm.str());uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<LineTable> Decoded = LineTable::decode(Data, BaseAddr);// Make sure decoding succeeded.ASSERT_TRUE((bool)Decoded);// Make sure decoded object is the same as the one we encoded.EXPECT_EQ(LT, Decoded.get());}TEST(GSYMTest, TestLineTable) {const uint64_t StartAddr = 0x1000;const uint32_t FileIdx = 1;LineTable LT;LineEntry Line0(StartAddr+0x000, FileIdx, 10);LineEntry Line1(StartAddr+0x010, FileIdx, 11);LineEntry Line2(StartAddr+0x100, FileIdx, 1000);ASSERT_TRUE(LT.empty());ASSERT_EQ(LT.size(), (size_t)0);LT.push(Line0);ASSERT_EQ(LT.size(), (size_t)1);LT.push(Line1);LT.push(Line2);LT.push(LineEntry(StartAddr+0x120, FileIdx, 900));LT.push(LineEntry(StartAddr+0x120, FileIdx, 2000));LT.push(LineEntry(StartAddr+0x121, FileIdx, 2001));LT.push(LineEntry(StartAddr+0x122, FileIdx, 2002));LT.push(LineEntry(StartAddr+0x123, FileIdx, 2003));ASSERT_FALSE(LT.empty());ASSERT_EQ(LT.size(), (size_t)8);// Test operator[].ASSERT_EQ(LT[0], Line0);ASSERT_EQ(LT[1], Line1);ASSERT_EQ(LT[2], Line2);// Test encoding and decoding line tables.TestLineTableHelper(llvm::support::little, LT);TestLineTableHelper(llvm::support::big, LT);// Verify the clear method works as expected.LT.clear();ASSERT_TRUE(LT.empty());ASSERT_EQ(LT.size(), (size_t)0);LineTable LT1;LineTable LT2;// Test that two empty line tables are equal and neither are less than// each other.ASSERT_EQ(LT1, LT2);ASSERT_FALSE(LT1 < LT1);ASSERT_FALSE(LT1 < LT2);ASSERT_FALSE(LT2 < LT1);ASSERT_FALSE(LT2 < LT2);// Test that a line table with less number of line entries is less than a// line table with more line entries and that they are not equal.LT2.push(Line0);ASSERT_LT(LT1, LT2);ASSERT_NE(LT1, LT2);// Test that two line tables with the same entries are equal.LT1.push(Line0);ASSERT_EQ(LT1, LT2);ASSERT_FALSE(LT1 < LT2);ASSERT_FALSE(LT2 < LT2);}static void TestLineTableDecodeError(llvm::support::endianness ByteOrder,StringRef Bytes, const uint64_t BaseAddr,std::string ExpectedErrorMsg) {uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<LineTable> Decoded = LineTable::decode(Data, BaseAddr);// Make sure decoding fails.ASSERT_FALSE((bool)Decoded);// Make sure decoded object is the same as the one we encoded.checkError(ExpectedErrorMsg, Decoded.takeError());}TEST(GSYMTest, TestLineTableDecodeErrors) {// Test decoding InlineInfo objects that ensure we report an appropriate// error message.const llvm::support::endianness ByteOrder = llvm::support::little;SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);const uint64_t BaseAddr = 0x100;TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000000: missing LineTable MinDelta");FW.writeU8(1); // MinDelta (ULEB)TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000001: missing LineTable MaxDelta");FW.writeU8(10); // MaxDelta (ULEB)TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000002: missing LineTable FirstLine");FW.writeU8(20); // FirstLine (ULEB)TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000003: EOF found before EndSequence");// Test a SetFile with the argument missing from the streamFW.writeU8(1); // SetFile opcode (uint8_t)TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000004: EOF found before SetFile value");FW.writeU8(5); // SetFile value as index (ULEB)// Test a AdvancePC with the argument missing from the streamFW.writeU8(2); // AdvancePC opcode (uint8_t)TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000006: EOF found before AdvancePC value");FW.writeU8(20); // AdvancePC value as offset (ULEB)// Test a AdvancePC with the argument missing from the streamFW.writeU8(3); // AdvanceLine opcode (uint8_t)TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,"0x00000008: EOF found before AdvanceLine value");FW.writeU8(20); // AdvanceLine value as offset (LLEB)}TEST(GSYMTest, TestLineTableEncodeErrors) {const uint64_t BaseAddr = 0x1000;const uint32_t FileIdx = 1;const llvm::support::endianness ByteOrder = llvm::support::little;SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);LineTable LT;checkError("attempted to encode invalid LineTable object",LT.encode(FW, BaseAddr));// Try to encode a line table where a line entry has an address that is less// than BaseAddr and verify we get an appropriate error.LineEntry Line0(BaseAddr+0x000, FileIdx, 10);LineEntry Line1(BaseAddr+0x010, FileIdx, 11);LT.push(Line0);LT.push(Line1);checkError("LineEntry has address 0x1000 which is less than the function ""start address 0x1010", LT.encode(FW, BaseAddr+0x10));LT.clear();// Try to encode a line table where a line entries has an address that is less// than BaseAddr and verify we get an appropriate error.LT.push(Line1);LT.push(Line0);checkError("LineEntry in LineTable not in ascending order",LT.encode(FW, BaseAddr));LT.clear();}static void TestHeaderEncodeError(const Header &H,std::string ExpectedErrorMsg) {const support::endianness ByteOrder = llvm::support::little;SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);llvm::Error Err = H.encode(FW);checkError(ExpectedErrorMsg, std::move(Err));}static void TestHeaderDecodeError(StringRef Bytes,std::string ExpectedErrorMsg) {const support::endianness ByteOrder = llvm::support::little;uint8_t AddressSize = 4;DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<Header> Decoded = Header::decode(Data);// Make sure decoding fails.ASSERT_FALSE((bool)Decoded);// Make sure decoded object is the same as the one we encoded.checkError(ExpectedErrorMsg, Decoded.takeError());}// Populate a GSYM header with valid values.static void InitHeader(Header &H) {H.Magic = GSYM_MAGIC;H.Version = GSYM_VERSION;H.AddrOffSize = 4;H.UUIDSize = 16;H.BaseAddress = 0x1000;H.NumAddresses = 1;H.StrtabOffset= 0x2000;H.StrtabSize = 0x1000;for (size_t i=0; i<GSYM_MAX_UUID_SIZE; ++i) {if (i < H.UUIDSize)H.UUID[i] = i;elseH.UUID[i] = 0;}}TEST(GSYMTest, TestHeaderEncodeErrors) {Header H;InitHeader(H);H.Magic = 12;TestHeaderEncodeError(H, "invalid GSYM magic 0x0000000c");InitHeader(H);H.Version = 12;TestHeaderEncodeError(H, "unsupported GSYM version 12");InitHeader(H);H.AddrOffSize = 12;TestHeaderEncodeError(H, "invalid address offset size 12");InitHeader(H);H.UUIDSize = 128;TestHeaderEncodeError(H, "invalid UUID size 128");}TEST(GSYMTest, TestHeaderDecodeErrors) {const llvm::support::endianness ByteOrder = llvm::support::little;SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);Header H;InitHeader(H);llvm::Error Err = H.encode(FW);ASSERT_FALSE(Err);FW.fixup32(12, offsetof(Header, Magic));TestHeaderDecodeError(OutStrm.str(), "invalid GSYM magic 0x0000000c");FW.fixup32(GSYM_MAGIC, offsetof(Header, Magic));FW.fixup32(12, offsetof(Header, Version));TestHeaderDecodeError(OutStrm.str(), "unsupported GSYM version 12");FW.fixup32(GSYM_VERSION, offsetof(Header, Version));FW.fixup32(12, offsetof(Header, AddrOffSize));TestHeaderDecodeError(OutStrm.str(), "invalid address offset size 12");FW.fixup32(4, offsetof(Header, AddrOffSize));FW.fixup32(128, offsetof(Header, UUIDSize));TestHeaderDecodeError(OutStrm.str(), "invalid UUID size 128");}static void TestHeaderEncodeDecode(const Header &H,support::endianness ByteOrder) {uint8_t AddressSize = 4;SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);llvm::Error Err = H.encode(FW);ASSERT_FALSE(Err);std::string Bytes(OutStrm.str());DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);llvm::Expected<Header> Decoded = Header::decode(Data);// Make sure decoding succeeded.ASSERT_TRUE((bool)Decoded);EXPECT_EQ(H, Decoded.get());}TEST(GSYMTest, TestHeaderEncodeDecode) {Header H;InitHeader(H);TestHeaderEncodeDecode(H, llvm::support::little);TestHeaderEncodeDecode(H, llvm::support::big);}static void TestGsymCreatorEncodeError(llvm::support::endianness ByteOrder,const GsymCreator &GC,std::string ExpectedErrorMsg) {SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);llvm::Error Err = GC.encode(FW);ASSERT_TRUE(bool(Err));checkError(ExpectedErrorMsg, std::move(Err));}TEST(GSYMTest, TestGsymCreatorEncodeErrors) {const uint8_t ValidUUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,14, 15, 16};const uint8_t InvalidUUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,14, 15, 16, 17, 18, 19, 20, 21};// Verify we get an error when trying to encode an GsymCreator with no// function infos. We shouldn't be saving a GSYM file in this case since// there is nothing inside of it.GsymCreator GC;TestGsymCreatorEncodeError(llvm::support::little, GC,"no functions to encode");const uint64_t FuncAddr = 0x1000;const uint64_t FuncSize = 0x100;const uint32_t FuncName = GC.insertString("foo");// Verify we get an error trying to encode a GsymCreator that isn't// finalized.GC.addFunctionInfo(FunctionInfo(FuncAddr, FuncSize, FuncName));TestGsymCreatorEncodeError(llvm::support::little, GC,"GsymCreator wasn't finalized prior to encoding");std::string finalizeIssues;raw_string_ostream OS(finalizeIssues);llvm::Error finalizeErr = GC.finalize(OS);ASSERT_FALSE(bool(finalizeErr));finalizeErr = GC.finalize(OS);ASSERT_TRUE(bool(finalizeErr));checkError("already finalized", std::move(finalizeErr));// Verify we get an error trying to encode a GsymCreator with a UUID that is// too long.GC.setUUID(InvalidUUID);TestGsymCreatorEncodeError(llvm::support::little, GC,"invalid UUID size 21");GC.setUUID(ValidUUID);// Verify errors are propagated when we try to encoding an invalid line// table.GC.forEachFunctionInfo([](FunctionInfo &FI) -> bool {FI.OptLineTable = LineTable(); // Invalid line table.return false; // Stop iterating});TestGsymCreatorEncodeError(llvm::support::little, GC,"attempted to encode invalid LineTable object");// Verify errors are propagated when we try to encoding an invalid inline// info.GC.forEachFunctionInfo([](FunctionInfo &FI) -> bool {FI.OptLineTable = llvm::None;FI.Inline = InlineInfo(); // Invalid InlineInfo.return false; // Stop iterating});TestGsymCreatorEncodeError(llvm::support::little, GC,"attempted to encode invalid InlineInfo object");}static void Compare(const GsymCreator &GC, const GsymReader &GR) {// Verify that all of the data in a GsymCreator is correctly decoded from// a GsymReader. To do this, we iterator overGC.forEachFunctionInfo([&](const FunctionInfo &FI) -> bool {auto DecodedFI = GR.getFunctionInfo(FI.Range.start());EXPECT_TRUE(bool(DecodedFI));EXPECT_EQ(FI, *DecodedFI);return true; // Keep iterating over all FunctionInfo objects.});}static void TestEncodeDecode(const GsymCreator &GC,support::endianness ByteOrder, uint16_t Version,uint8_t AddrOffSize, uint64_t BaseAddress,uint32_t NumAddresses, ArrayRef<uint8_t> UUID) {SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);llvm::Error Err = GC.encode(FW);ASSERT_FALSE((bool)Err);Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_TRUE(bool(GR));const Header &Hdr = GR->getHeader();EXPECT_EQ(Hdr.Version, Version);EXPECT_EQ(Hdr.AddrOffSize, AddrOffSize);EXPECT_EQ(Hdr.UUIDSize, UUID.size());EXPECT_EQ(Hdr.BaseAddress, BaseAddress);EXPECT_EQ(Hdr.NumAddresses, NumAddresses);EXPECT_EQ(ArrayRef<uint8_t>(Hdr.UUID, Hdr.UUIDSize), UUID);Compare(GC, GR.get());}TEST(GSYMTest, TestGsymCreator1ByteAddrOffsets) {uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};GsymCreator GC;GC.setUUID(UUID);constexpr uint64_t BaseAddr = 0x1000;constexpr uint8_t AddrOffSize = 1;const uint32_t Func1Name = GC.insertString("foo");const uint32_t Func2Name = GC.insertString("bar");GC.addFunctionInfo(FunctionInfo(BaseAddr+0x00, 0x10, Func1Name));GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20, 0x10, Func2Name));Error Err = GC.finalize(llvm::nulls());ASSERT_FALSE(Err);TestEncodeDecode(GC, llvm::support::little,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));TestEncodeDecode(GC, llvm::support::big,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));}TEST(GSYMTest, TestGsymCreator2ByteAddrOffsets) {uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};GsymCreator GC;GC.setUUID(UUID);constexpr uint64_t BaseAddr = 0x1000;constexpr uint8_t AddrOffSize = 2;const uint32_t Func1Name = GC.insertString("foo");const uint32_t Func2Name = GC.insertString("bar");GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name));GC.addFunctionInfo(FunctionInfo(BaseAddr+0x200, 0x100, Func2Name));Error Err = GC.finalize(llvm::nulls());ASSERT_FALSE(Err);TestEncodeDecode(GC, llvm::support::little,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));TestEncodeDecode(GC, llvm::support::big,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));}TEST(GSYMTest, TestGsymCreator4ByteAddrOffsets) {uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};GsymCreator GC;GC.setUUID(UUID);constexpr uint64_t BaseAddr = 0x1000;constexpr uint8_t AddrOffSize = 4;const uint32_t Func1Name = GC.insertString("foo");const uint32_t Func2Name = GC.insertString("bar");GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name));GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20000, 0x100, Func2Name));Error Err = GC.finalize(llvm::nulls());ASSERT_FALSE(Err);TestEncodeDecode(GC, llvm::support::little,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));TestEncodeDecode(GC, llvm::support::big,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));}TEST(GSYMTest, TestGsymCreator8ByteAddrOffsets) {uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};GsymCreator GC;GC.setUUID(UUID);constexpr uint64_t BaseAddr = 0x1000;constexpr uint8_t AddrOffSize = 8;const uint32_t Func1Name = GC.insertString("foo");const uint32_t Func2Name = GC.insertString("bar");GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name));GC.addFunctionInfo(FunctionInfo(BaseAddr+0x100000000, 0x100, Func2Name));Error Err = GC.finalize(llvm::nulls());ASSERT_FALSE(Err);TestEncodeDecode(GC, llvm::support::little,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));TestEncodeDecode(GC, llvm::support::big,GSYM_VERSION,AddrOffSize,BaseAddr,2, // NumAddressesArrayRef<uint8_t>(UUID));}static void VerifyFunctionInfo(const GsymReader &GR, uint64_t Addr,const FunctionInfo &FI) {auto ExpFI = GR.getFunctionInfo(Addr);ASSERT_TRUE(bool(ExpFI));ASSERT_EQ(FI, ExpFI.get());}static void VerifyFunctionInfoError(const GsymReader &GR, uint64_t Addr,std::string ErrMessage) {auto ExpFI = GR.getFunctionInfo(Addr);ASSERT_FALSE(bool(ExpFI));checkError(ErrMessage, ExpFI.takeError());}TEST(GSYMTest, TestGsymReader) {uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};GsymCreator GC;GC.setUUID(UUID);constexpr uint64_t BaseAddr = 0x1000;constexpr uint64_t Func1Addr = BaseAddr;constexpr uint64_t Func2Addr = BaseAddr+0x20;constexpr uint64_t FuncSize = 0x10;const uint32_t Func1Name = GC.insertString("foo");const uint32_t Func2Name = GC.insertString("bar");const auto ByteOrder = support::endian::system_endianness();GC.addFunctionInfo(FunctionInfo(Func1Addr, FuncSize, Func1Name));GC.addFunctionInfo(FunctionInfo(Func2Addr, FuncSize, Func2Name));Error FinalizeErr = GC.finalize(llvm::nulls());ASSERT_FALSE(FinalizeErr);SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);llvm::Error Err = GC.encode(FW);ASSERT_FALSE((bool)Err);if (auto ExpectedGR = GsymReader::copyBuffer(OutStrm.str())) {const GsymReader &GR = ExpectedGR.get();VerifyFunctionInfoError(GR, Func1Addr-1, "address 0xfff is not in GSYM");FunctionInfo Func1(Func1Addr, FuncSize, Func1Name);VerifyFunctionInfo(GR, Func1Addr, Func1);VerifyFunctionInfo(GR, Func1Addr+1, Func1);VerifyFunctionInfo(GR, Func1Addr+FuncSize-1, Func1);VerifyFunctionInfoError(GR, Func1Addr+FuncSize,"address 0x1010 is not in GSYM");VerifyFunctionInfoError(GR, Func2Addr-1, "address 0x101f is not in GSYM");FunctionInfo Func2(Func2Addr, FuncSize, Func2Name);VerifyFunctionInfo(GR, Func2Addr, Func2);VerifyFunctionInfo(GR, Func2Addr+1, Func2);VerifyFunctionInfo(GR, Func2Addr+FuncSize-1, Func2);VerifyFunctionInfoError(GR, Func2Addr+FuncSize,"address 0x1030 is not in GSYM");}}TEST(GSYMTest, TestGsymLookups) {// Test creating a GSYM file with a function that has a inline information.// Verify that lookups work correctly. Lookups do not decode the entire// FunctionInfo or InlineInfo, they only extract information needed for the// lookup to happen which avoids allocations which can slow down// symbolication.GsymCreator GC;FunctionInfo FI(0x1000, 0x100, GC.insertString("main"));const auto ByteOrder = support::endian::system_endianness();FI.OptLineTable = LineTable();const uint32_t MainFileIndex = GC.insertFile("/tmp/main.c");const uint32_t FooFileIndex = GC.insertFile("/tmp/foo.h");FI.OptLineTable->push(LineEntry(0x1000, MainFileIndex, 5));FI.OptLineTable->push(LineEntry(0x1010, FooFileIndex, 10));FI.OptLineTable->push(LineEntry(0x1012, FooFileIndex, 20));FI.OptLineTable->push(LineEntry(0x1014, FooFileIndex, 11));FI.OptLineTable->push(LineEntry(0x1016, FooFileIndex, 30));FI.OptLineTable->push(LineEntry(0x1018, FooFileIndex, 12));FI.OptLineTable->push(LineEntry(0x1020, MainFileIndex, 8));FI.Inline = InlineInfo();FI.Inline->Name = GC.insertString("inline1");FI.Inline->CallFile = MainFileIndex;FI.Inline->CallLine = 6;FI.Inline->Ranges.insert(AddressRange(0x1010, 0x1020));InlineInfo Inline2;Inline2.Name = GC.insertString("inline2");Inline2.CallFile = FooFileIndex;Inline2.CallLine = 33;Inline2.Ranges.insert(AddressRange(0x1012, 0x1014));FI.Inline->Children.emplace_back(Inline2);InlineInfo Inline3;Inline3.Name = GC.insertString("inline3");Inline3.CallFile = FooFileIndex;Inline3.CallLine = 35;Inline3.Ranges.insert(AddressRange(0x1016, 0x1018));FI.Inline->Children.emplace_back(Inline3);GC.addFunctionInfo(std::move(FI));Error FinalizeErr = GC.finalize(llvm::nulls());ASSERT_FALSE(FinalizeErr);SmallString<512> Str;raw_svector_ostream OutStrm(Str);FileWriter FW(OutStrm, ByteOrder);llvm::Error Err = GC.encode(FW);ASSERT_FALSE((bool)Err);Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_TRUE(bool(GR));// Verify inline info is correct when doing lookups.auto LR = GR->lookup(0x1000);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 5}));LR = GR->lookup(0x100F);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 5, 15}));LR = GR->lookup(0x1010);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 10},SourceLocation{"main", "/tmp", "main.c", 6, 16}));LR = GR->lookup(0x1012);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline2", "/tmp", "foo.h", 20},SourceLocation{"inline1", "/tmp", "foo.h", 33, 2},SourceLocation{"main", "/tmp", "main.c", 6, 18}));LR = GR->lookup(0x1014);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 11, 4},SourceLocation{"main", "/tmp", "main.c", 6, 20}));LR = GR->lookup(0x1016);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline3", "/tmp", "foo.h", 30},SourceLocation{"inline1", "/tmp", "foo.h", 35, 6},SourceLocation{"main", "/tmp", "main.c", 6, 22}));LR = GR->lookup(0x1018);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 12, 8},SourceLocation{"main", "/tmp", "main.c", 6, 24}));LR = GR->lookup(0x1020);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 8, 32}));}TEST(GSYMTest, TestDWARFFunctionWithAddresses) {// Create a single compile unit with a single function and make sure it gets// converted to DWARF correctly. The function's address range is in where// DW_AT_low_pc and DW_AT_high_pc are both addresses.StringRef yamldata = R"(debug_str:- ''- /tmp/main.c- maindebug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_addr- Attribute: DW_AT_languageForm: DW_FORM_data2- Code: 0x00000002Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_addrdebug_info:- Version: 4AddrSize: 8Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000002000- Value: 0x0000000000000004- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- Value: 0x0000000000001000- Value: 0x0000000000002000- AbbrCode: 0x00000000)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// There should only be one function in our GSYM.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));EXPECT_FALSE(ExpFI->OptLineTable.has_value());EXPECT_FALSE(ExpFI->Inline.has_value());}TEST(GSYMTest, TestDWARFFunctionWithAddressAndOffset) {// Create a single compile unit with a single function and make sure it gets// converted to DWARF correctly. The function's address range is in where// DW_AT_low_pc is an address and the DW_AT_high_pc is an offset.StringRef yamldata = R"(debug_str:- ''- /tmp/main.c- maindebug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_languageForm: DW_FORM_data2- Code: 0x00000002Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4debug_info:- Version: 4AddrSize: 8Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000001000- Value: 0x0000000000000004- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- Value: 0x0000000000001000- Value: 0x0000000000001000- AbbrCode: 0x00000000)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// There should only be one function in our GSYM.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));EXPECT_FALSE(ExpFI->OptLineTable.has_value());EXPECT_FALSE(ExpFI->Inline.has_value());}TEST(GSYMTest, TestDWARFStructMethodNoMangled) {// Sometimes the compiler will omit the mangled name in the DWARF for static// and member functions of classes and structs. This test verifies that the// fully qualified name of the method is computed and used as the string for// the function in the GSYM in these cases. Otherwise we might just get a// function name like "erase" instead of "std::vector<int>::erase".StringRef yamldata = R"(debug_str:- ''- /tmp/main.c- Foo- dump- thisdebug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_addr- Attribute: DW_AT_languageForm: DW_FORM_data2- Code: 0x00000002Tag: DW_TAG_structure_typeChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Code: 0x00000003Tag: DW_TAG_subprogramChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_addr- Code: 0x00000004Tag: DW_TAG_formal_parameterChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_typeForm: DW_FORM_ref4- Attribute: DW_AT_artificialForm: DW_FORM_flag_presentdebug_info:- Version: 4AddrSize: 8Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000002000- Value: 0x0000000000000004- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- AbbrCode: 0x00000003Values:- Value: 0x0000000000000011- Value: 0x0000000000001000- Value: 0x0000000000002000- AbbrCode: 0x00000004Values:- Value: 0x0000000000000016- Value: 0x0000000000000022- Value: 0x0000000000000001- AbbrCode: 0x00000000- AbbrCode: 0x00000000- AbbrCode: 0x00000000)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// There should only be one function in our GSYM.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));EXPECT_FALSE(ExpFI->OptLineTable.has_value());EXPECT_FALSE(ExpFI->Inline.has_value());StringRef MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "Foo::dump");}TEST(GSYMTest, TestDWARFTextRanges) {// Linkers don't understand DWARF, they just like to concatenate and// relocate data within the DWARF sections. This means that if a function// gets dead stripped, and if those functions use an offset as the// DW_AT_high_pc, we can end up with many functions at address zero. The// DwarfTransformer allows clients to specify valid .text address ranges// and any addresses of any functions must fall within those ranges if any// have been specified. This means that an object file can calcuate the// address ranges within the binary where code lives and set these ranges// as constraints in the DwarfTransformer. ObjectFile instances can// add a address ranges of sections that have executable permissions. This// keeps bad information from being added to a GSYM file and causing issues// when symbolicating.StringRef yamldata = R"(debug_str:- ''- /tmp/main.c- main- dead_stripped- dead_stripped2debug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_languageForm: DW_FORM_data2- Code: 0x00000002Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4debug_info:- Version: 4AddrSize: 8Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000001000- Value: 0x0000000000000004- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- Value: 0x0000000000001000- Value: 0x0000000000001000- AbbrCode: 0x00000002Values:- Value: 0x0000000000000012- Value: 0x0000000000000000- Value: 0x0000000000000100- AbbrCode: 0x00000002Values:- Value: 0x0000000000000020- Value: 0x0000000000000000- Value: 0x0000000000000040- AbbrCode: 0x00000000)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);// Only allow addresses between [0x1000 - 0x2000) to be linked into the// GSYM.AddressRanges TextRanges;TextRanges.insert(AddressRange(0x1000, 0x2000));GC.SetValidTextRanges(TextRanges);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// There should only be one function in our GSYM.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));EXPECT_FALSE(ExpFI->OptLineTable.has_value());EXPECT_FALSE(ExpFI->Inline.has_value());StringRef MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "main");}TEST(GSYMTest, TestEmptySymbolEndAddressOfTextRanges) {// Test that if we have valid text ranges and we have a symbol with no size// as the last FunctionInfo entry that the size of the symbol gets set to the// end address of the text range.GsymCreator GC;AddressRanges TextRanges;TextRanges.insert(AddressRange(0x1000, 0x2000));GC.SetValidTextRanges(TextRanges);GC.addFunctionInfo(FunctionInfo(0x1500, 0, GC.insertString("symbol")));auto &OS = llvm::nulls();ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// There should only be one function in our GSYM.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1500);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1500, 0x2000));EXPECT_FALSE(ExpFI->OptLineTable.has_value());EXPECT_FALSE(ExpFI->Inline.has_value());StringRef MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "symbol");}TEST(GSYMTest, TestDWARFInlineInfo) {// Make sure we parse the line table and inline information correctly from// DWARF.StringRef yamldata = R"(debug_str:- ''- /tmp/main.c- main- inline1debug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_languageForm: DW_FORM_data2- Attribute: DW_AT_stmt_listForm: DW_FORM_sec_offset- Code: 0x00000002Tag: DW_TAG_subprogramChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Code: 0x00000003Tag: DW_TAG_inlined_subroutineChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_call_fileForm: DW_FORM_data4- Attribute: DW_AT_call_lineForm: DW_FORM_data4debug_info:- Version: 4AddrSize: 8Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000001000- Value: 0x0000000000000004- Value: 0x0000000000000000- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- Value: 0x0000000000001000- Value: 0x0000000000001000- AbbrCode: 0x00000003Values:- Value: 0x0000000000000012- Value: 0x0000000000001100- Value: 0x0000000000000100- Value: 0x0000000000000001- Value: 0x000000000000000A- AbbrCode: 0x00000000- AbbrCode: 0x00000000debug_line:- Length: 96Version: 2PrologueLength: 46MinInstLength: 1DefaultIsStmt: 1LineBase: 251LineRange: 14OpcodeBase: 13StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]IncludeDirs:- /tmpFiles:- Name: main.cDirIdx: 1ModTime: 0Length: 0- Name: inline.hDirIdx: 1ModTime: 0Length: 0Opcodes:- Opcode: DW_LNS_extended_opExtLen: 9SubOpcode: DW_LNE_set_addressData: 4096- Opcode: DW_LNS_advance_lineSData: 9Data: 4096- Opcode: DW_LNS_copyData: 4096- Opcode: DW_LNS_advance_pcData: 256- Opcode: DW_LNS_set_fileData: 2- Opcode: DW_LNS_advance_lineSData: 10Data: 2- Opcode: DW_LNS_copyData: 2- Opcode: DW_LNS_advance_pcData: 128- Opcode: DW_LNS_advance_lineSData: 1Data: 128- Opcode: DW_LNS_copyData: 128- Opcode: DW_LNS_advance_pcData: 128- Opcode: DW_LNS_set_fileData: 1- Opcode: DW_LNS_advance_lineSData: -10Data: 1- Opcode: DW_LNS_copyData: 1- Opcode: DW_LNS_advance_pcData: 3584- Opcode: DW_LNS_advance_lineSData: 1Data: 3584- Opcode: DW_LNS_extended_opExtLen: 1SubOpcode: DW_LNE_end_sequenceData: 3584)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// There should only be one function in our GSYM.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));EXPECT_TRUE(ExpFI->OptLineTable.has_value());EXPECT_TRUE(ExpFI->Inline.has_value());StringRef MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "main");// Verify inline info is correct when doing lookups.auto LR = GR->lookup(0x1000);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 10}));LR = GR->lookup(0x1100-1);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 10, 255}));LR = GR->lookup(0x1100);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 20},SourceLocation{"main", "/tmp", "main.c", 10, 256}));LR = GR->lookup(0x1180-1);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 20, 127},SourceLocation{"main", "/tmp", "main.c", 10, 383}));LR = GR->lookup(0x1180);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 21, 128},SourceLocation{"main", "/tmp", "main.c", 10, 384}));LR = GR->lookup(0x1200-1);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 21, 255},SourceLocation{"main", "/tmp", "main.c", 10, 511}));LR = GR->lookup(0x1200);ASSERT_THAT_EXPECTED(LR, Succeeded());EXPECT_THAT(LR->Locations,testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 11, 512}));}TEST(GSYMTest, TestDWARFNoLines) {// Check that if a DW_TAG_subprogram doesn't have line table entries that// we fall back and use the DW_AT_decl_file and DW_AT_decl_line to at least// point to the function definition. This DWARF file has 4 functions:// "lines_no_decl": has line table entries, no DW_AT_decl_file/line attrs.// "lines_with_decl": has line table entries and has DW_AT_decl_file/line,// make sure we don't use DW_AT_decl_file/line and make// sure there is a line table.// "no_lines_no_decl": no line table entries and no DW_AT_decl_file/line,// make sure there is no line table for this function.// "no_lines_with_decl": no line table and has DW_AT_decl_file/line, make// sure we have one line table entry that starts at// the function start address and the decl file and// line.//// 0x0000000b: DW_TAG_compile_unit// DW_AT_name ("/tmp/main.c")// DW_AT_low_pc (0x0000000000001000)// DW_AT_high_pc (0x0000000000002000)// DW_AT_language (DW_LANG_C_plus_plus)// DW_AT_stmt_list (0x00000000)//// 0x00000022: DW_TAG_subprogram// DW_AT_name ("lines_no_decl")// DW_AT_low_pc (0x0000000000001000)// DW_AT_high_pc (0x0000000000002000)//// 0x00000033: DW_TAG_subprogram// DW_AT_name ("lines_with_decl")// DW_AT_low_pc (0x0000000000002000)// DW_AT_high_pc (0x0000000000003000)// DW_AT_decl_file ("/tmp/main.c")// DW_AT_decl_line (20)//// 0x00000046: DW_TAG_subprogram// DW_AT_name ("no_lines_no_decl")// DW_AT_low_pc (0x0000000000003000)// DW_AT_high_pc (0x0000000000004000)//// 0x00000057: DW_TAG_subprogram// DW_AT_name ("no_lines_with_decl")// DW_AT_low_pc (0x0000000000004000)// DW_AT_high_pc (0x0000000000005000)// DW_AT_decl_file ("/tmp/main.c")// DW_AT_decl_line (40)//// 0x0000006a: NULLStringRef yamldata = R"(debug_str:- ''- '/tmp/main.c'- lines_no_decl- lines_with_decl- no_lines_no_decl- no_lines_with_decldebug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_languageForm: DW_FORM_data2- Attribute: DW_AT_stmt_listForm: DW_FORM_sec_offset- Code: 0x00000002Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Code: 0x00000003Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_decl_fileForm: DW_FORM_data1- Attribute: DW_AT_decl_lineForm: DW_FORM_data1debug_info:- Version: 4AddrSize: 8Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000001000- Value: 0x0000000000000004- Value: 0x0000000000000000- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- Value: 0x0000000000001000- Value: 0x0000000000001000- AbbrCode: 0x00000003Values:- Value: 0x000000000000001B- Value: 0x0000000000002000- Value: 0x0000000000001000- Value: 0x0000000000000001- Value: 0x0000000000000014- AbbrCode: 0x00000002Values:- Value: 0x000000000000002B- Value: 0x0000000000003000- Value: 0x0000000000001000- AbbrCode: 0x00000003Values:- Value: 0x000000000000003C- Value: 0x0000000000004000- Value: 0x0000000000001000- Value: 0x0000000000000001- Value: 0x0000000000000028- AbbrCode: 0x00000000debug_line:- Length: 92Version: 2PrologueLength: 34MinInstLength: 1DefaultIsStmt: 1LineBase: 251LineRange: 14OpcodeBase: 13StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]IncludeDirs:- '/tmp'Files:- Name: main.cDirIdx: 1ModTime: 0Length: 0Opcodes:- Opcode: DW_LNS_extended_opExtLen: 9SubOpcode: DW_LNE_set_addressData: 4096- Opcode: DW_LNS_advance_lineSData: 10Data: 0- Opcode: DW_LNS_copyData: 0- Opcode: DW_LNS_advance_pcData: 512- Opcode: DW_LNS_advance_lineSData: 1Data: 0- Opcode: DW_LNS_copyData: 0- Opcode: DW_LNS_advance_pcData: 3584- Opcode: DW_LNS_extended_opExtLen: 1SubOpcode: DW_LNE_end_sequenceData: 0- Opcode: DW_LNS_extended_opExtLen: 9SubOpcode: DW_LNE_set_addressData: 8192- Opcode: DW_LNS_advance_lineSData: 20Data: 0- Opcode: DW_LNS_copyData: 0- Opcode: DW_LNS_advance_pcData: 512- Opcode: DW_LNS_advance_lineSData: 1Data: 0- Opcode: DW_LNS_copyData: 0- Opcode: DW_LNS_advance_pcData: 3584- Opcode: DW_LNS_extended_opExtLen: 1SubOpcode: DW_LNE_end_sequenceData: 0)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());EXPECT_EQ(GR->getNumAddresses(), 4u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));EXPECT_TRUE(ExpFI->OptLineTable);StringRef MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "lines_no_decl");// Make sure have two line table entries and that get the first line entry// correct.EXPECT_EQ(ExpFI->OptLineTable->size(), 2u);EXPECT_EQ(ExpFI->OptLineTable->first()->Addr, 0x1000u);EXPECT_EQ(ExpFI->OptLineTable->first()->Line, 11u);ExpFI = GR->getFunctionInfo(0x2000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x2000, 0x3000));EXPECT_TRUE(ExpFI->OptLineTable);MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "lines_with_decl");// Make sure have two line table entries and that we don't use line 20// from the DW_AT_decl_file/line as a line table entry.EXPECT_EQ(ExpFI->OptLineTable->size(), 2u);EXPECT_EQ(ExpFI->OptLineTable->first()->Addr, 0x2000u);EXPECT_EQ(ExpFI->OptLineTable->first()->Line, 21u);ExpFI = GR->getFunctionInfo(0x3000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x3000, 0x4000));// Make sure we have no line table.EXPECT_FALSE(ExpFI->OptLineTable.has_value());MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "no_lines_no_decl");ExpFI = GR->getFunctionInfo(0x4000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x4000, 0x5000));EXPECT_TRUE(ExpFI->OptLineTable.has_value());MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "no_lines_with_decl");// Make sure we have one line table entry that uses the DW_AT_decl_file/line// as the one and only line entry.EXPECT_EQ(ExpFI->OptLineTable->size(), 1u);EXPECT_EQ(ExpFI->OptLineTable->first()->Addr, 0x4000u);EXPECT_EQ(ExpFI->OptLineTable->first()->Line, 40u);}TEST(GSYMTest, TestDWARFDeadStripAddr4) {// Check that various techniques that compilers use for dead code stripping// work for 4 byte addresses. Make sure we keep the good functions and// strip any functions whose name starts with "stripped".//// 1 - Compilers might set the low PC to -1 (UINT32_MAX) for compile unit// with 4 byte addresses ("stripped1")// 2 - Set the low and high PC to the same value ("stripped2")// 3 - Have the high PC lower than the low PC ("stripped3")//// 0x0000000b: DW_TAG_compile_unit// DW_AT_name ("/tmp/main.c")// DW_AT_low_pc (0x0000000000001000)// DW_AT_high_pc (0x0000000000002000)// DW_AT_language (DW_LANG_C_plus_plus)//// 0x0000001a: DW_TAG_subprogram// DW_AT_name ("main")// DW_AT_low_pc (0x0000000000001000)// DW_AT_high_pc (0x0000000000002000)//// 0x00000027: DW_TAG_subprogram// DW_AT_name ("stripped1")// DW_AT_low_pc (0x00000000ffffffff)// DW_AT_high_pc (0x0000000100000000)//// 0x00000034: DW_TAG_subprogram// DW_AT_name ("stripped2")// DW_AT_low_pc (0x0000000000003000)// DW_AT_high_pc (0x0000000000003000)//// 0x00000041: DW_TAG_subprogram// DW_AT_name ("stripped3")// DW_AT_low_pc (0x0000000000004000)// DW_AT_high_pc (0x0000000000003fff)//// 0x0000004e: NULLStringRef yamldata = R"(debug_str:- ''- '/tmp/main.c'- main- stripped1- stripped2- stripped3debug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_languageForm: DW_FORM_data2- Code: 0x00000002Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Code: 0x00000003Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_addrdebug_info:- Version: 4AddrSize: 4Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000001000- Value: 0x0000000000000004- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- Value: 0x0000000000001000- Value: 0x0000000000001000- AbbrCode: 0x00000002Values:- Value: 0x0000000000000012- Value: 0x00000000FFFFFFFF- Value: 0x0000000000000001- AbbrCode: 0x00000003Values:- Value: 0x000000000000001C- Value: 0x0000000000003000- Value: 0x0000000000003000- AbbrCode: 0x00000003Values:- Value: 0x0000000000000026- Value: 0x0000000000004000- Value: 0x0000000000003FFF- AbbrCode: 0x00000000)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 4);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// Test that the only function that made it was the "main" function.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));StringRef MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "main");}TEST(GSYMTest, TestDWARFDeadStripAddr8) {// Check that various techniques that compilers use for dead code stripping// work for 4 byte addresses. Make sure we keep the good functions and// strip any functions whose name starts with "stripped".//// 1 - Compilers might set the low PC to -1 (UINT64_MAX) for compile unit// with 8 byte addresses ("stripped1")// 2 - Set the low and high PC to the same value ("stripped2")// 3 - Have the high PC lower than the low PC ("stripped3")//// 0x0000000b: DW_TAG_compile_unit// DW_AT_name ("/tmp/main.c")// DW_AT_low_pc (0x0000000000001000)// DW_AT_high_pc (0x0000000000002000)// DW_AT_language (DW_LANG_C_plus_plus)//// 0x0000001e: DW_TAG_subprogram// DW_AT_name ("main")// DW_AT_low_pc (0x0000000000001000)// DW_AT_high_pc (0x0000000000002000)//// 0x0000002f: DW_TAG_subprogram// DW_AT_name ("stripped1")// DW_AT_low_pc (0xffffffffffffffff)// DW_AT_high_pc (0x0000000000000000)//// 0x00000040: DW_TAG_subprogram// DW_AT_name ("stripped2")// DW_AT_low_pc (0x0000000000003000)// DW_AT_high_pc (0x0000000000003000)//// 0x00000055: DW_TAG_subprogram// DW_AT_name ("stripped3")// DW_AT_low_pc (0x0000000000004000)// DW_AT_high_pc (0x0000000000003fff)//// 0x0000006a: NULLStringRef yamldata = R"(debug_str:- ''- '/tmp/main.c'- main- stripped1- stripped2- stripped3debug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Attribute: DW_AT_languageForm: DW_FORM_data2- Code: 0x00000002Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_data4- Code: 0x00000003Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_nameForm: DW_FORM_strp- Attribute: DW_AT_low_pcForm: DW_FORM_addr- Attribute: DW_AT_high_pcForm: DW_FORM_addrdebug_info:- Version: 4AddrSize: 8Entries:- AbbrCode: 0x00000001Values:- Value: 0x0000000000000001- Value: 0x0000000000001000- Value: 0x0000000000001000- Value: 0x0000000000000004- AbbrCode: 0x00000002Values:- Value: 0x000000000000000D- Value: 0x0000000000001000- Value: 0x0000000000001000- AbbrCode: 0x00000002Values:- Value: 0x0000000000000012- Value: 0xFFFFFFFFFFFFFFFF- Value: 0x0000000000000001- AbbrCode: 0x00000003Values:- Value: 0x000000000000001C- Value: 0x0000000000003000- Value: 0x0000000000003000- AbbrCode: 0x00000003Values:- Value: 0x0000000000000026- Value: 0x0000000000004000- Value: 0x0000000000003FFF- AbbrCode: 0x00000000)";auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);ASSERT_TRUE(DwarfContext.get() != nullptr);auto &OS = llvm::nulls();GsymCreator GC;DwarfTransformer DT(*DwarfContext, OS, GC);const uint32_t ThreadCount = 1;ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());SmallString<512> Str;raw_svector_ostream OutStrm(Str);const auto ByteOrder = support::endian::system_endianness();FileWriter FW(OutStrm, ByteOrder);ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());ASSERT_THAT_EXPECTED(GR, Succeeded());// Test that the only function that made it was the "main" function.EXPECT_EQ(GR->getNumAddresses(), 1u);auto ExpFI = GR->getFunctionInfo(0x1000);ASSERT_THAT_EXPECTED(ExpFI, Succeeded());ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));StringRef MethodName = GR->getString(ExpFI->Name);EXPECT_EQ(MethodName, "main");}TEST(GSYMTest, TestGsymCreatorMultipleSymbolsWithNoSize) {// Multiple symbols at the same address with zero size were being emitted// instead of being combined into a single entry. This function tests to make// sure we only get one symbol.uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};GsymCreator GC;GC.setUUID(UUID);constexpr uint64_t BaseAddr = 0x1000;constexpr uint8_t AddrOffSize = 1;const uint32_t Func1Name = GC.insertString("foo");const uint32_t Func2Name = GC.insertString("bar");GC.addFunctionInfo(FunctionInfo(BaseAddr, 0, Func1Name));GC.addFunctionInfo(FunctionInfo(BaseAddr, 0, Func2Name));Error Err = GC.finalize(llvm::nulls());ASSERT_FALSE(Err);TestEncodeDecode(GC, llvm::support::little, GSYM_VERSION, AddrOffSize,BaseAddr,1, // NumAddressesArrayRef<uint8_t>(UUID));TestEncodeDecode(GC, llvm::support::big, GSYM_VERSION, AddrOffSize, BaseAddr,1, // NumAddressesArrayRef<uint8_t>(UUID));}
set(LLVM_LINK_COMPONENTSDebugInfoDWARFDebugInfoGSYMMCSupportObjectYAML)add_llvm_unittest(DebugInfoGSYMTestsGSYMTest.cpp)target_link_libraries(DebugInfoGSYMTests PRIVATE LLVMTestingSupport)set_property(TARGET DebugInfoGSYMTests PROPERTY FOLDER "Tests/UnitTests/DebugInfoTests")
//===--- unittests/DebugInfo/DWARF/DwarfUtils.h -----------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H#define LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H#include <cstdint>namespace llvm {class Triple;namespace dwarf {namespace utils {Triple getDefaultTargetTripleForAddrSize(uint8_t AddrSize);Triple getNormalizedDefaultTargetTriple();bool isConfigurationSupported(Triple &T);} // end namespace utils} // end namespace dwarf} // end namespace llvm#endif // LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H
//===--- unittests/DebugInfo/DWARF/DwarfUtils.cpp ---------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "DwarfUtils.h"#include "llvm/ADT/Triple.h"#include "llvm/Config/llvm-config.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/Host.h"#include "llvm/Support/TargetSelect.h"using namespace llvm;static void initLLVMIfNeeded() {static bool gInitialized = false;if (!gInitialized) {gInitialized = true;InitializeAllTargets();InitializeAllTargetMCs();InitializeAllAsmPrinters();InitializeAllAsmParsers();}}Triple llvm::dwarf::utils::getNormalizedDefaultTargetTriple() {Triple T(Triple::normalize(sys::getDefaultTargetTriple()));return T;}Triple llvm::dwarf::utils::getDefaultTargetTripleForAddrSize(uint8_t AddrSize) {Triple T = getNormalizedDefaultTargetTriple();assert((AddrSize == 4 || AddrSize == 8) &&"Only 32-bit/64-bit address size variants are supported");// If a 32-bit/64-bit address size was specified, try to convert the triple// if it is for the wrong variant.if (AddrSize == 8 && T.isArch32Bit())return T.get64BitArchVariant();if (AddrSize == 4 && T.isArch64Bit())return T.get32BitArchVariant();return T;}bool llvm::dwarf::utils::isConfigurationSupported(Triple &T) {initLLVMIfNeeded();std::string Err;const Target *TheTarget = TargetRegistry::lookupTarget(T.getTriple(), Err);return TheTarget && TheTarget->hasMCAsmBackend();}
//===--- unittests/DebugInfo/DWARF/DwarfGenerator.h -------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// A file that can generate DWARF debug info for unit tests.////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H#define LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H#include "llvm/ADT/StringRef.h"#include "llvm/CodeGen/DIE.h"#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"#include "llvm/Support/Error.h"#include <memory>#include <string>#include <tuple>#include <vector>namespace llvm {class AsmPrinter;class DIE;class DIEAbbrev;class DwarfStringPool;class MCAsmBackend;class MCAsmInfo;class MCCodeEmitter;class MCContext;struct MCDwarfLineTableParams;class MCInstrInfo;class MCRegisterInfo;class MCStreamer;class MCSubtargetInfo;class raw_fd_ostream;class TargetLoweringObjectFile;class TargetMachine;class Triple;namespace dwarfgen {class Generator;class CompileUnit;/// A DWARF debug information entry class used to generate DWARF DIEs.////// This class is used to quickly generate DWARF debug information by creating/// child DIEs or adding attributes to the current DIE. Instances of this class/// are created from the compile unit (dwarfgen::CompileUnit::getUnitDIE()) or/// by calling dwarfgen::DIE::addChild(...) and using the returned DIE object.class DIE {dwarfgen::CompileUnit *CU;llvm::DIE *Die;protected:friend class Generator;friend class CompileUnit;DIE(CompileUnit *U = nullptr, llvm::DIE *D = nullptr) : CU(U), Die(D) {}/// Called with a compile/type unit relative offset prior to generating the/// DWARF debug info.////// \param CUOffset the compile/type unit relative offset where the/// abbreviation code for this DIE will be encoded.unsigned computeSizeAndOffsets(unsigned CUOffset);public:/// Add an attribute value that has no value.////// \param Attr a dwarf::Attribute enumeration value or any uint16_t that/// represents a user defined DWARF attribute./// \param Form the dwarf::Form to use when encoding the attribute. This is/// only used with the DW_FORM_flag_present form encoding.void addAttribute(uint16_t Attr, dwarf::Form Form);/// Add an attribute value to be encoded as a DIEInteger////// \param Attr a dwarf::Attribute enumeration value or any uint16_t that/// represents a user defined DWARF attribute./// \param Form the dwarf::Form to use when encoding the attribute./// \param U the unsigned integer to encode.void addAttribute(uint16_t Attr, dwarf::Form Form, uint64_t U);/// Add an attribute value to be encoded as a DIEExpr////// \param Attr a dwarf::Attribute enumeration value or any uint16_t that/// represents a user defined DWARF attribute./// \param Form the dwarf::Form to use when encoding the attribute./// \param Expr the MC expression used to compute the value.void addAttribute(uint16_t Attr, dwarf::Form Form, const MCExpr &Expr);/// Add an attribute value to be encoded as a DIEString or DIEInlinedString.////// \param Attr a dwarf::Attribute enumeration value or any uint16_t that/// represents a user defined DWARF attribute./// \param Form the dwarf::Form to use when encoding the attribute. The form/// must be one of DW_FORM_strp or DW_FORM_string./// \param String the string to encode.void addAttribute(uint16_t Attr, dwarf::Form Form, StringRef String);/// Add an attribute value to be encoded as a DIEEntry.////// DIEEntry attributes refer to other llvm::DIE objects that have been/// created.////// \param Attr a dwarf::Attribute enumeration value or any uint16_t that/// represents a user defined DWARF attribute./// \param Form the dwarf::Form to use when encoding the attribute. The form/// must be one of DW_FORM_strp or DW_FORM_string./// \param RefDie the DIE that this attriute refers to.void addAttribute(uint16_t Attr, dwarf::Form Form, dwarfgen::DIE &RefDie);/// Add an attribute value to be encoded as a DIEBlock.////// DIEBlock attributes refers to binary data that is stored as the/// attribute's value.////// \param Attr a dwarf::Attribute enumeration value or any uint16_t that/// represents a user defined DWARF attribute./// \param Form the dwarf::Form to use when encoding the attribute. The form/// must be one of DW_FORM_strp or DW_FORM_string./// \param P a pointer to the data to store as the attribute value./// \param S the size in bytes of the data pointed to by P .void addAttribute(uint16_t Attr, dwarf::Form Form, const void *P, size_t S);/// Add a DW_AT_str_offsets_base attribute to this DIE.void addStrOffsetsBaseAttribute();/// Add a new child to this DIE object.////// \param Tag the dwarf::Tag to assing to the llvm::DIE object./// \returns the newly created DIE object that is now a child owned by this/// object.dwarfgen::DIE addChild(dwarf::Tag Tag);};/// A DWARF compile unit used to generate DWARF compile/type units.////// Instances of these classes are created by instances of the Generator/// class. All information required to generate a DWARF compile unit is/// contained inside this class.class CompileUnit {Generator &DG;BasicDIEUnit DU;uint64_t Length; /// The length in bytes of all of the DIEs in this unit.const uint16_t Version; /// The Dwarf version number for this unit.const uint8_t AddrSize; /// The size in bytes of an address for this unit.public:CompileUnit(Generator &D, uint16_t V, uint8_t A): DG(D), DU(dwarf::DW_TAG_compile_unit), Version(V), AddrSize(A) {}DIE getUnitDIE();Generator &getGenerator() { return DG; }uint64_t getOffset() const { return DU.getDebugSectionOffset(); }uint64_t getLength() const { return Length; }uint16_t getVersion() const { return Version; }uint16_t getAddressSize() const { return AddrSize; }void setOffset(uint64_t Offset) { DU.setDebugSectionOffset(Offset); }void setLength(uint64_t L) { Length = L; }};/// A DWARF line unit-like class used to generate DWARF line units.////// Instances of this class are created by instances of the Generator class.class LineTable {public:enum ValueLength { Byte = 1, Half = 2, Long = 4, Quad = 8, ULEB, SLEB };struct ValueAndLength {uint64_t Value = 0;ValueLength Length = Byte;};LineTable(uint16_t Version, dwarf::DwarfFormat Format, uint8_t AddrSize,uint8_t SegSize = 0): Version(Version), Format(Format), AddrSize(AddrSize), SegSize(SegSize) {assert(Version >= 2 && Version <= 5 && "unsupported version");}// Create a Prologue suitable to pass to setPrologue, with a single file and// include directory entry.DWARFDebugLine::Prologue createBasicPrologue() const;// Set or replace the current prologue with the specified prologue. If no// prologue is set, a default one will be used when generating.void setPrologue(DWARFDebugLine::Prologue NewPrologue);// Used to write an arbitrary payload instead of the standard prologue. This// is useful if you wish to test handling of corrupt .debug_line sections.void setCustomPrologue(ArrayRef<ValueAndLength> NewPrologue);// Add a byte to the program, with the given value. This can be used to// specify a special opcode, or to add arbitrary contents to the section.void addByte(uint8_t Value);// Add a standard opcode to the program. The opcode and operands do not have// to be valid.void addStandardOpcode(uint8_t Opcode, ArrayRef<ValueAndLength> Operands);// Add an extended opcode to the program with the specified length, opcode,// and operands. These values do not have to be valid.void addExtendedOpcode(uint64_t Length, uint8_t Opcode,ArrayRef<ValueAndLength> Operands);// Write the contents of the LineUnit to the current section in the generator.void generate(MCContext &MC, AsmPrinter &Asm) const;private:void writeData(ArrayRef<ValueAndLength> Data, AsmPrinter &Asm) const;MCSymbol *writeDefaultPrologue(AsmPrinter &Asm) const;void writePrologue(AsmPrinter &Asm) const;void writeProloguePayload(const DWARFDebugLine::Prologue &Prologue,AsmPrinter &Asm) const;// Calculate the number of bytes the Contents will take up.size_t getContentsSize() const;llvm::Optional<DWARFDebugLine::Prologue> Prologue;std::vector<ValueAndLength> CustomPrologue;std::vector<ValueAndLength> Contents;// The Version field is used for determining how to write the Prologue, if a// non-custom prologue is used. The version value actually written, will be// that specified in the Prologue, if a custom prologue has been passed in.// Otherwise, it will be this value.uint16_t Version;dwarf::DwarfFormat Format;uint8_t AddrSize;uint8_t SegSize;};/// A DWARF generator.////// Generate DWARF for unit tests by creating any instance of this class and/// calling Generator::addCompileUnit(), and then getting the dwarfgen::DIE from/// the returned compile unit and adding attributes and children to each DIE.class Generator {std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<MCContext> MC;MCAsmBackend *MAB; // Owned by MCStreamerstd::unique_ptr<MCInstrInfo> MII;std::unique_ptr<MCSubtargetInfo> MSTI;MCCodeEmitter *MCE; // Owned by MCStreamerMCStreamer *MS; // Owned by AsmPrinterstd::unique_ptr<TargetMachine> TM;TargetLoweringObjectFile *TLOF; // Owned by TargetMachine;std::unique_ptr<AsmPrinter> Asm;BumpPtrAllocator Allocator;std::unique_ptr<DwarfStringPool> StringPool; // Entries owned by Allocator.std::vector<std::unique_ptr<CompileUnit>> CompileUnits;std::vector<std::unique_ptr<LineTable>> LineTables;DIEAbbrevSet Abbreviations;MCSymbol *StringOffsetsStartSym;SmallString<4096> FileBytes;/// The stream we use to generate the DWARF into as an ELF file.std::unique_ptr<raw_svector_ostream> Stream;/// The DWARF version to generate.uint16_t Version;/// Private constructor, call Generator::Create(...) to get a DWARF generator/// expected.Generator();/// Create the streamer and setup the output buffer.llvm::Error init(Triple TheTriple, uint16_t DwarfVersion);public:/// Create a DWARF generator or get an appropriate error.////// \param TheTriple the triple to use when creating any required support/// classes needed to emit the DWARF./// \param DwarfVersion the version of DWARF to emit.////// \returns a llvm::Expected that either contains a unique_ptr to a Generator/// or a llvm::Error.static llvm::Expected<std::unique_ptr<Generator>>create(Triple TheTriple, uint16_t DwarfVersion);~Generator();/// Generate all DWARF sections and return a memory buffer that/// contains an ELF file that contains the DWARF.StringRef generate();/// Add a compile unit to be generated.////// \returns a dwarfgen::CompileUnit that can be used to retrieve the compile/// unit dwarfgen::DIE that can be used to add attributes and add child DIE/// objects to.dwarfgen::CompileUnit &addCompileUnit();/// Add a line table unit to be generated./// \param DwarfFormat the DWARF format to use (DWARF32 or DWARF64).////// \returns a dwarfgen::LineTable that can be used to customise the contents/// of the line table.LineTable &addLineTable(dwarf::DwarfFormat DwarfFormat = dwarf::DwarfFormat::DWARF32);BumpPtrAllocator &getAllocator() { return Allocator; }AsmPrinter *getAsmPrinter() const { return Asm.get(); }MCContext *getMCContext() const { return MC.get(); }DIEAbbrevSet &getAbbrevSet() { return Abbreviations; }DwarfStringPool &getStringPool() { return *StringPool; }MCSymbol *getStringOffsetsStartSym() const { return StringOffsetsStartSym; }/// Save the generated DWARF file to disk.////// \param Path the path to save the ELF file to.bool saveFile(StringRef Path);};} // end namespace dwarfgen} // end namespace llvm#endif // LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H
//===--- unittests/DebugInfo/DWARF/DwarfGenerator.cpp -----------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "DwarfGenerator.h"#include "../lib/CodeGen/AsmPrinter/DwarfStringPool.h"#include "llvm/ADT/Triple.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/CodeGen/AsmPrinter.h"#include "llvm/CodeGen/DIE.h"#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"#include "llvm/MC/MCAsmBackend.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCCodeEmitter.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCDwarf.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCObjectFileInfo.h"#include "llvm/MC/MCObjectWriter.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCStreamer.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/MCTargetOptionsCommandFlags.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Pass.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/LEB128.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Target/TargetLoweringObjectFile.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"using namespace llvm;using namespace dwarf;mc::RegisterMCTargetOptionsFlags MOF;namespace {} // end anonymous namespace//===----------------------------------------------------------------------===///// dwarfgen::DIE implementation.//===----------------------------------------------------------------------===//unsigned dwarfgen::DIE::computeSizeAndOffsets(unsigned Offset) {auto &DG = CU->getGenerator();return Die->computeOffsetsAndAbbrevs(DG.getAsmPrinter()->getDwarfFormParams(),DG.getAbbrevSet(), Offset);}void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, uint64_t U) {auto &DG = CU->getGenerator();Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,DIEInteger(U));}void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, const MCExpr &Expr) {auto &DG = CU->getGenerator();Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,DIEExpr(&Expr));}void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form,StringRef String) {auto &DG = CU->getGenerator();switch (Form) {case DW_FORM_string:Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,new (DG.getAllocator())DIEInlineString(String, DG.getAllocator()));break;case DW_FORM_strp:Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,DIEString(DG.getStringPool().getEntry(*DG.getAsmPrinter(), String)));break;case DW_FORM_GNU_str_index:case DW_FORM_strx:case DW_FORM_strx1:case DW_FORM_strx2:case DW_FORM_strx3:case DW_FORM_strx4:Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,DIEString(DG.getStringPool().getIndexedEntry(*DG.getAsmPrinter(), String)));break;default:llvm_unreachable("Unhandled form!");}}void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form,dwarfgen::DIE &RefDie) {auto &DG = CU->getGenerator();Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,DIEEntry(*RefDie.Die));}void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, const void *P,size_t S) {auto &DG = CU->getGenerator();DIEBlock *Block = new (DG.getAllocator()) DIEBlock;for (size_t I = 0; I < S; ++I)Block->addValue(DG.getAllocator(), (dwarf::Attribute)0, dwarf::DW_FORM_data1,DIEInteger((const_cast<uint8_t *>(static_cast<const uint8_t *>(P)))[I]));Block->computeSize(DG.getAsmPrinter()->getDwarfFormParams());Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,Block);}void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form) {auto &DG = CU->getGenerator();assert(Form == DW_FORM_flag_present);Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,DIEInteger(1));}void dwarfgen::DIE::addStrOffsetsBaseAttribute() {auto &DG = CU->getGenerator();auto &MC = *DG.getMCContext();AsmPrinter *Asm = DG.getAsmPrinter();const MCSymbol *SectionStart =Asm->getObjFileLowering().getDwarfStrOffSection()->getBeginSymbol();const MCExpr *Expr =MCSymbolRefExpr::create(DG.getStringOffsetsStartSym(), MC);if (!Asm->MAI->doesDwarfUseRelocationsAcrossSections())Expr = MCBinaryExpr::createSub(Expr, MCSymbolRefExpr::create(SectionStart, MC), MC);addAttribute(dwarf::DW_AT_str_offsets_base, DW_FORM_sec_offset, *Expr);}dwarfgen::DIE dwarfgen::DIE::addChild(dwarf::Tag Tag) {auto &DG = CU->getGenerator();return dwarfgen::DIE(CU,&Die->addChild(llvm::DIE::get(DG.getAllocator(), Tag)));}dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() {return dwarfgen::DIE(this, &DU.getUnitDie());}//===----------------------------------------------------------------------===///// dwarfgen::LineTable implementation.//===----------------------------------------------------------------------===//DWARFDebugLine::Prologue dwarfgen::LineTable::createBasicPrologue() const {DWARFDebugLine::Prologue P;switch (Version) {case 2:case 3:P.TotalLength = 41;P.PrologueLength = 35;break;case 4:P.TotalLength = 42;P.PrologueLength = 36;break;case 5:P.TotalLength = 50;P.PrologueLength = 42;P.FormParams.AddrSize = AddrSize;break;default:llvm_unreachable("unsupported version");}if (Format == DWARF64) {P.TotalLength += 4;P.FormParams.Format = DWARF64;}P.TotalLength += getContentsSize();P.FormParams.Version = Version;P.MinInstLength = 1;P.MaxOpsPerInst = 1;P.DefaultIsStmt = 1;P.LineBase = -5;P.LineRange = 14;P.OpcodeBase = 13;P.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};P.IncludeDirectories.push_back(DWARFFormValue::createFromPValue(DW_FORM_string, "a dir"));P.FileNames.push_back(DWARFDebugLine::FileNameEntry());P.FileNames.back().Name =DWARFFormValue::createFromPValue(DW_FORM_string, "a file");return P;}void dwarfgen::LineTable::setPrologue(DWARFDebugLine::Prologue NewPrologue) {Prologue = NewPrologue;CustomPrologue.clear();}void dwarfgen::LineTable::setCustomPrologue(ArrayRef<ValueAndLength> NewPrologue) {Prologue.reset();CustomPrologue = NewPrologue;}void dwarfgen::LineTable::addByte(uint8_t Value) {Contents.push_back({Value, Byte});}void dwarfgen::LineTable::addStandardOpcode(uint8_t Opcode,ArrayRef<ValueAndLength> Operands) {Contents.push_back({Opcode, Byte});Contents.insert(Contents.end(), Operands.begin(), Operands.end());}void dwarfgen::LineTable::addExtendedOpcode(uint64_t Length, uint8_t Opcode,ArrayRef<ValueAndLength> Operands) {Contents.push_back({0, Byte});Contents.push_back({Length, ULEB});Contents.push_back({Opcode, Byte});Contents.insert(Contents.end(), Operands.begin(), Operands.end());}void dwarfgen::LineTable::generate(MCContext &MC, AsmPrinter &Asm) const {MC.setDwarfVersion(Version);MCSymbol *EndSymbol = nullptr;if (!CustomPrologue.empty()) {writeData(CustomPrologue, Asm);} else if (!Prologue) {EndSymbol = writeDefaultPrologue(Asm);} else {writePrologue(Asm);}writeData(Contents, Asm);if (EndSymbol != nullptr)Asm.OutStreamer->emitLabel(EndSymbol);}void dwarfgen::LineTable::writeData(ArrayRef<ValueAndLength> Data,AsmPrinter &Asm) const {for (auto Entry : Data) {switch (Entry.Length) {case Byte:case Half:case Long:case Quad:Asm.OutStreamer->emitIntValue(Entry.Value, Entry.Length);continue;case ULEB:Asm.emitULEB128(Entry.Value);continue;case SLEB:Asm.emitSLEB128(Entry.Value);continue;}llvm_unreachable("unsupported ValueAndLength Length value");}}size_t dwarfgen::LineTable::getContentsSize() const {size_t Size = 0;for (auto Entry : Contents) {switch (Entry.Length) {case ULEB:Size += getULEB128Size(Entry.Value);break;case SLEB:Size += getSLEB128Size(Entry.Value);break;default:Size += Entry.Length;break;}}return Size;}MCSymbol *dwarfgen::LineTable::writeDefaultPrologue(AsmPrinter &Asm) const {MCSymbol *UnitStart = Asm.createTempSymbol("line_unit_start");MCSymbol *UnitEnd = Asm.createTempSymbol("line_unit_end");if (Format == DwarfFormat::DWARF64) {Asm.emitInt32((int)dwarf::DW_LENGTH_DWARF64);Asm.emitLabelDifference(UnitEnd, UnitStart, 8);} else {Asm.emitLabelDifference(UnitEnd, UnitStart, 4);}Asm.OutStreamer->emitLabel(UnitStart);Asm.emitInt16(Version);if (Version == 5) {Asm.emitInt8(AddrSize);Asm.emitInt8(SegSize);}MCSymbol *PrologueStart = Asm.createTempSymbol("line_prologue_start");MCSymbol *PrologueEnd = Asm.createTempSymbol("line_prologue_end");Asm.emitLabelDifference(PrologueEnd, PrologueStart,Format == DwarfFormat::DWARF64 ? 8 : 4);Asm.OutStreamer->emitLabel(PrologueStart);DWARFDebugLine::Prologue DefaultPrologue = createBasicPrologue();writeProloguePayload(DefaultPrologue, Asm);Asm.OutStreamer->emitLabel(PrologueEnd);return UnitEnd;}void dwarfgen::LineTable::writePrologue(AsmPrinter &Asm) const {if (Format == DwarfFormat::DWARF64) {Asm.emitInt32((int)dwarf::DW_LENGTH_DWARF64);Asm.emitInt64(Prologue->TotalLength);} else {Asm.emitInt32(Prologue->TotalLength);}Asm.emitInt16(Prologue->getVersion());if (Version == 5) {Asm.emitInt8(Prologue->getAddressSize());Asm.emitInt8(Prologue->SegSelectorSize);}if (Format == DwarfFormat::DWARF64)Asm.emitInt64(Prologue->PrologueLength);elseAsm.emitInt32(Prologue->PrologueLength);writeProloguePayload(*Prologue, Asm);}static void writeCString(StringRef Str, AsmPrinter &Asm) {Asm.OutStreamer->emitBytes(Str);Asm.emitInt8(0);}static void writeV2IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,AsmPrinter &Asm) {for (auto Include : Prologue.IncludeDirectories)writeCString(*toString(Include), Asm);Asm.emitInt8(0);for (auto File : Prologue.FileNames) {writeCString(*toString(File.Name), Asm);Asm.emitULEB128(File.DirIdx);Asm.emitULEB128(File.ModTime);Asm.emitULEB128(File.Length);}Asm.emitInt8(0);}static void writeV5IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,AsmPrinter &Asm) {Asm.emitInt8(1); // directory_entry_format_count.// TODO: Add support for other content descriptions - we currently only// support a single DW_LNCT_path/DW_FORM_string.Asm.emitULEB128(DW_LNCT_path);Asm.emitULEB128(DW_FORM_string);Asm.emitULEB128(Prologue.IncludeDirectories.size());for (auto Include : Prologue.IncludeDirectories)writeCString(*toString(Include), Asm);Asm.emitInt8(2); // file_name_entry_format_count.Asm.emitULEB128(DW_LNCT_path);Asm.emitULEB128(DW_FORM_string);Asm.emitULEB128(DW_LNCT_directory_index);Asm.emitULEB128(DW_FORM_data1);Asm.emitULEB128(Prologue.FileNames.size());for (auto File : Prologue.FileNames) {writeCString(*toString(File.Name), Asm);Asm.emitInt8(File.DirIdx);}}void dwarfgen::LineTable::writeProloguePayload(const DWARFDebugLine::Prologue &Prologue, AsmPrinter &Asm) const {Asm.emitInt8(Prologue.MinInstLength);if (Version >= 4)Asm.emitInt8(Prologue.MaxOpsPerInst);Asm.emitInt8(Prologue.DefaultIsStmt);Asm.emitInt8(Prologue.LineBase);Asm.emitInt8(Prologue.LineRange);Asm.emitInt8(Prologue.OpcodeBase);for (auto Length : Prologue.StandardOpcodeLengths) {Asm.emitInt8(Length);}if (Version < 5)writeV2IncludeAndFileTable(Prologue, Asm);elsewriteV5IncludeAndFileTable(Prologue, Asm);}//===----------------------------------------------------------------------===///// dwarfgen::Generator implementation.//===----------------------------------------------------------------------===//dwarfgen::Generator::Generator(): MAB(nullptr), MCE(nullptr), MS(nullptr), TLOF(nullptr),StringPool(nullptr), Abbreviations(Allocator),StringOffsetsStartSym(nullptr), Version(0) {}dwarfgen::Generator::~Generator() = default;llvm::Expected<std::unique_ptr<dwarfgen::Generator>>dwarfgen::Generator::create(Triple TheTriple, uint16_t DwarfVersion) {std::unique_ptr<dwarfgen::Generator> GenUP(new dwarfgen::Generator());llvm::Error error = GenUP->init(TheTriple, DwarfVersion);if (error)return Expected<std::unique_ptr<dwarfgen::Generator>>(std::move(error));return Expected<std::unique_ptr<dwarfgen::Generator>>(std::move(GenUP));}llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {Version = V;std::string ErrorStr;std::string TripleName;// Get the target.const Target *TheTarget =TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr);if (!TheTarget)return make_error<StringError>(ErrorStr, inconvertibleErrorCode());TripleName = TheTriple.getTriple();// Create all the MC Objects.MRI.reset(TheTarget->createMCRegInfo(TripleName));if (!MRI)return make_error<StringError>(Twine("no register info for target ") +TripleName,inconvertibleErrorCode());MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));if (!MAI)return make_error<StringError>("no asm info for target " + TripleName,inconvertibleErrorCode());MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));if (!MSTI)return make_error<StringError>("no subtarget info for target " + TripleName,inconvertibleErrorCode());MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions);if (!MAB)return make_error<StringError>("no asm backend for target " + TripleName,inconvertibleErrorCode());MII.reset(TheTarget->createMCInstrInfo());if (!MII)return make_error<StringError>("no instr info info for target " +TripleName,inconvertibleErrorCode());TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(),None));if (!TM)return make_error<StringError>("no target machine for target " + TripleName,inconvertibleErrorCode());MC.reset(new MCContext(TheTriple, MAI.get(), MRI.get(), MSTI.get()));TLOF = TM->getObjFileLowering();TLOF->Initialize(*MC, *TM);MC->setObjectFileInfo(TLOF);MCE = TheTarget->createMCCodeEmitter(*MII, *MC);if (!MCE)return make_error<StringError>("no code emitter for target " + TripleName,inconvertibleErrorCode());Stream = std::make_unique<raw_svector_ostream>(FileBytes);MS = TheTarget->createMCObjectStreamer(TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB),MAB->createObjectWriter(*Stream), std::unique_ptr<MCCodeEmitter>(MCE),*MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,/*DWARFMustBeAtTheEnd*/ false);if (!MS)return make_error<StringError>("no object streamer for target " +TripleName,inconvertibleErrorCode());// Finally create the AsmPrinter we'll use to emit the DIEs.Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS)));if (!Asm)return make_error<StringError>("no asm printer for target " + TripleName,inconvertibleErrorCode());// Set the DWARF version correctly on all classes that we use.MC->setDwarfVersion(Version);Asm->setDwarfVersion(Version);StringPool = std::make_unique<DwarfStringPool>(Allocator, *Asm, StringRef());StringOffsetsStartSym = Asm->createTempSymbol("str_offsets_base");return Error::success();}StringRef dwarfgen::Generator::generate() {// Offset from the first CU in the debug info section is 0 initially.uint64_t SecOffset = 0;// Iterate over each compile unit and set the size and offsets for each// DIE within each compile unit. All offsets are CU relative.for (auto &CU : CompileUnits) {// Set the absolute .debug_info offset for this compile unit.CU->setOffset(SecOffset);// The DIEs contain compile unit relative offsets.unsigned CUOffset = 11;CUOffset = CU->getUnitDIE().computeSizeAndOffsets(CUOffset);// Update our absolute .debug_info offset.SecOffset += CUOffset;CU->setLength(CUOffset - 4);}Abbreviations.Emit(Asm.get(), TLOF->getDwarfAbbrevSection());StringPool->emitStringOffsetsTableHeader(*Asm, TLOF->getDwarfStrOffSection(),StringOffsetsStartSym);StringPool->emit(*Asm, TLOF->getDwarfStrSection(),TLOF->getDwarfStrOffSection());MS->switchSection(TLOF->getDwarfInfoSection());for (auto &CU : CompileUnits) {uint16_t Version = CU->getVersion();auto Length = CU->getLength();MC->setDwarfVersion(Version);assert(Length != -1U);Asm->emitInt32(Length);Asm->emitInt16(Version);if (Version <= 4) {Asm->emitInt32(0);Asm->emitInt8(CU->getAddressSize());} else {Asm->emitInt8(dwarf::DW_UT_compile);Asm->emitInt8(CU->getAddressSize());Asm->emitInt32(0);}Asm->emitDwarfDIE(*CU->getUnitDIE().Die);}MS->switchSection(TLOF->getDwarfLineSection());for (auto < : LineTables)LT->generate(*MC, *Asm);MS->finish();if (FileBytes.empty())return StringRef();return StringRef(FileBytes.data(), FileBytes.size());}bool dwarfgen::Generator::saveFile(StringRef Path) {if (FileBytes.empty())return false;std::error_code EC;raw_fd_ostream Strm(Path, EC, sys::fs::OF_None);if (EC)return false;Strm.write(FileBytes.data(), FileBytes.size());Strm.close();return true;}dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() {CompileUnits.push_back(std::make_unique<CompileUnit>(*this, Version, Asm->getPointerSize()));return *CompileUnits.back();}dwarfgen::LineTable &dwarfgen::Generator::addLineTable(DwarfFormat Format) {LineTables.push_back(std::make_unique<LineTable>(Version, Format, Asm->getPointerSize()));return *LineTables.back();}
//===- llvm/unittest/DebugInfo/DWARFLocationExpressionTest.cpp ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h"#include "llvm/Support/ScopedPrinter.h"#include "gtest/gtest.h"using namespace llvm;using object::SectionedAddress;TEST(DWARFLocationExpression, Equality) {EXPECT_EQ((DWARFLocationExpression{None, {}}),(DWARFLocationExpression{None, {}}));EXPECT_NE((DWARFLocationExpression{DWARFAddressRange{1, 47}, {}}),(DWARFLocationExpression{DWARFAddressRange{1, 48}, {}}));EXPECT_NE((DWARFLocationExpression{DWARFAddressRange{1, 47}, {}}),(DWARFLocationExpression{DWARFAddressRange{1, 47}, {42}}));}TEST(DWARFLocationExpression, StreamingOperator) {EXPECT_EQ("None: 1, 2", to_string(DWARFLocationExpression{None, {1, 2}}));EXPECT_EQ("[0x0000000000000042, 0x0000000000000047): 1",to_string(DWARFLocationExpression{DWARFAddressRange{0x42, 0x47}, {1}}));}
//===- DWARFListTableTest.cpp ---------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/DWARF/DWARFListTable.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(DWARFListTableHeader, TruncatedLength) {static const char SecData[] = "\x33\x22\x11"; // Truncated DWARF32 lengthDWARFDataExtractor Extractor(StringRef(SecData, sizeof(SecData) - 1),/*isLittleEndian=*/true,/*AddrSize=*/4);DWARFListTableHeader Header(/*SectionName=*/".debug_rnglists",/*ListTypeString=*/"range");uint64_t Offset = 0;EXPECT_THAT_ERROR(Header.extract(Extractor, &Offset),FailedWithMessage("parsing .debug_rnglists table at offset 0x0: unexpected end of data ""at offset 0x3 while reading [0x0, 0x4)"));// length() is expected to return 0 to indicate that the unit length field// can not be parsed and so we can not, for example, skip the current set// to continue parsing from the next one.EXPECT_EQ(Header.length(), 0u);}TEST(DWARFListTableHeader, TruncatedLengthDWARF64) {static const char SecData[] ="\xff\xff\xff\xff" // DWARF64 mark"\x55\x44\x33\x22\x11"; // Truncated DWARF64 lengthDWARFDataExtractor Extractor(StringRef(SecData, sizeof(SecData) - 1),/*isLittleEndian=*/true,/*AddrSize=*/4);DWARFListTableHeader Header(/*SectionName=*/".debug_rnglists",/*ListTypeString=*/"range");uint64_t Offset = 0;EXPECT_THAT_ERROR(Header.extract(Extractor, &Offset),FailedWithMessage("parsing .debug_rnglists table at offset 0x0: unexpected end of data ""at offset 0x9 while reading [0x4, 0xc)"));// length() is expected to return 0 to indicate that the unit length field// can not be parsed and so we can not, for example, skip the current set// to continue parsing from the next one.EXPECT_EQ(Header.length(), 0u);}TEST(DWARFListTableHeader, TruncatedHeader) {static const char SecData[] = "\x02\x00\x00\x00" // Length"\x05\x00"; // VersionDWARFDataExtractor Extractor(StringRef(SecData, sizeof(SecData) - 1),/*isLittleEndian=*/true,/*AddrSize=*/4);DWARFListTableHeader Header(/*SectionName=*/".debug_rnglists",/*ListTypeString=*/"range");uint64_t Offset = 0;EXPECT_THAT_ERROR(Header.extract(Extractor, &Offset),FailedWithMessage(".debug_rnglists table at offset 0x0 has too small ""length (0x6) to contain a complete header"));// length() is expected to return the full length of the set if the unit// length field is read, even if an error occurred during the parsing,// to allow skipping the current set and continue parsing from the next one.EXPECT_EQ(Header.length(), 6u);}TEST(DWARFListTableHeader, OffsetEntryCount) {static const char SecData[] = "\x10\x00\x00\x00" // Length"\x05\x00" // Version"\x08" // Address size"\x00" // Segment selector size"\x01\x00\x00\x00" // Offset entry count"\x04\x00\x00\x00" // offset[0]"\x04" // DW_RLE_offset_pair"\x01" // ULEB128 starting offset"\x02" // ULEB128 ending offset"\x00"; // DW_RLE_end_of_listDWARFDataExtractor Extractor(StringRef(SecData, sizeof(SecData) - 1),/*isLittleEndian=*/true,/*AddrSize=*/4);DWARFListTableHeader Header(/*SectionName=*/".debug_rnglists",/*ListTypeString=*/"range");uint64_t Offset = 0;EXPECT_FALSE(!!Header.extract(Extractor, &Offset));Optional<uint64_t> Offset0 = Header.getOffsetEntry(Extractor, 0);EXPECT_TRUE(!!Offset0);EXPECT_EQ(Offset0, uint64_t(4));Optional<uint64_t> Offset1 = Header.getOffsetEntry(Extractor, 1);EXPECT_FALSE(!!Offset1);EXPECT_EQ(Header.length(), sizeof(SecData) - 1);}} // end anonymous namespace
//===- llvm/unittest/DebugInfo/DWARFFormValueTest.cpp ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/SmallString.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/Host.h"#include "llvm/Support/LEB128.h"#include "gtest/gtest.h"#include <climits>using namespace llvm;using namespace dwarf;namespace {bool isFormClass(dwarf::Form Form, DWARFFormValue::FormClass FC) {return DWARFFormValue(Form).isFormClass(FC);}TEST(DWARFFormValue, FormClass) {EXPECT_TRUE(isFormClass(DW_FORM_addr, DWARFFormValue::FC_Address));EXPECT_FALSE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Address));EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Constant));EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_SectionOffset));EXPECT_TRUE(isFormClass(DW_FORM_sec_offset, DWARFFormValue::FC_SectionOffset));EXPECT_TRUE(isFormClass(DW_FORM_GNU_str_index, DWARFFormValue::FC_String));EXPECT_TRUE(isFormClass(DW_FORM_GNU_addr_index, DWARFFormValue::FC_Address));EXPECT_FALSE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Address));EXPECT_TRUE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Reference));EXPECT_TRUE(isFormClass(DW_FORM_ref_sig8, DWARFFormValue::FC_Reference));}template<typename RawTypeT>DWARFFormValue createDataXFormValue(dwarf::Form Form, RawTypeT Value) {char Raw[sizeof(RawTypeT)];memcpy(Raw, &Value, sizeof(RawTypeT));uint64_t Offset = 0;DWARFFormValue Result(Form);DWARFDataExtractor Data(StringRef(Raw, sizeof(RawTypeT)),sys::IsLittleEndianHost, sizeof(void *));Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});return Result;}DWARFFormValue createULEBFormValue(uint64_t Value) {SmallString<10> RawData;raw_svector_ostream OS(RawData);encodeULEB128(Value, OS);uint64_t Offset = 0;DWARFFormValue Result(DW_FORM_udata);DWARFDataExtractor Data(OS.str(), sys::IsLittleEndianHost, sizeof(void *));Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});return Result;}DWARFFormValue createSLEBFormValue(int64_t Value) {SmallString<10> RawData;raw_svector_ostream OS(RawData);encodeSLEB128(Value, OS);uint64_t Offset = 0;DWARFFormValue Result(DW_FORM_sdata);DWARFDataExtractor Data(OS.str(), sys::IsLittleEndianHost, sizeof(void *));Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});return Result;}TEST(DWARFFormValue, SignedConstantForms) {// Check that we correctly sign extend fixed size forms.auto Sign1 = createDataXFormValue<uint8_t>(DW_FORM_data1, -123);auto Sign2 = createDataXFormValue<uint16_t>(DW_FORM_data2, -12345);auto Sign4 = createDataXFormValue<uint32_t>(DW_FORM_data4, -123456789);auto Sign8 = createDataXFormValue<uint64_t>(DW_FORM_data8, -1);EXPECT_EQ(Sign1.getAsSignedConstant().value(), -123);EXPECT_EQ(Sign2.getAsSignedConstant().value(), -12345);EXPECT_EQ(Sign4.getAsSignedConstant().value(), -123456789);EXPECT_EQ(Sign8.getAsSignedConstant().value(), -1);// Check that we can handle big positive values, but that we return// an error just over the limit.auto UMax = createULEBFormValue(LLONG_MAX);auto TooBig = createULEBFormValue(uint64_t(LLONG_MAX) + 1);EXPECT_EQ(UMax.getAsSignedConstant().value(), LLONG_MAX);EXPECT_EQ(TooBig.getAsSignedConstant().has_value(), false);// Sanity check some other forms.auto Data1 = createDataXFormValue<uint8_t>(DW_FORM_data1, 120);auto Data2 = createDataXFormValue<uint16_t>(DW_FORM_data2, 32000);auto Data4 = createDataXFormValue<uint32_t>(DW_FORM_data4, 2000000000);auto Data8 = createDataXFormValue<uint64_t>(DW_FORM_data8, 0x1234567812345678LL);auto LEBMin = createSLEBFormValue(LLONG_MIN);auto LEBMax = createSLEBFormValue(LLONG_MAX);auto LEB1 = createSLEBFormValue(-42);auto LEB2 = createSLEBFormValue(42);EXPECT_EQ(Data1.getAsSignedConstant().value(), 120);EXPECT_EQ(Data2.getAsSignedConstant().value(), 32000);EXPECT_EQ(Data4.getAsSignedConstant().value(), 2000000000);EXPECT_EQ(Data8.getAsSignedConstant().value(), 0x1234567812345678LL);EXPECT_EQ(LEBMin.getAsSignedConstant().value(), LLONG_MIN);EXPECT_EQ(LEBMax.getAsSignedConstant().value(), LLONG_MAX);EXPECT_EQ(LEB1.getAsSignedConstant().value(), -42);EXPECT_EQ(LEB2.getAsSignedConstant().value(), 42);// Data16 is a little tricky.char Cksum[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};DWARFFormValue Data16(DW_FORM_data16);DWARFDataExtractor DE16(StringRef(Cksum, 16), sys::IsLittleEndianHost,sizeof(void *));uint64_t Offset = 0;Data16.extractValue(DE16, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});SmallString<32> Str;raw_svector_ostream Res(Str);Data16.dump(Res, DIDumpOptions());EXPECT_EQ(memcmp(Str.data(), "000102030405060708090a0b0c0d0e0f", 32), 0);}using ParamType = std::tuple<Form, uint16_t, uint8_t, DwarfFormat,ArrayRef<uint8_t>, uint64_t, bool>;struct FormSkipValueFixtureBase : public testing::TestWithParam<ParamType> {void SetUp() override {std::tie(Fm, Version, AddrSize, Dwarf, InitialData, ExpectedSkipped,ExpectedResult) = GetParam();}void doSkipValueTest() {SCOPED_TRACE("Inputs: Form = " + std::to_string(Fm) +", Version = " + std::to_string(Version) +", AddrSize = " + std::to_string(uint32_t(AddrSize)) +", DwarfFormat = " + std::to_string(Dwarf));std::vector<uint8_t> Buf(InitialData.data(),InitialData.data() + InitialData.size());// The data extractor only adjusts the offset to the end of the buffer when// attempting to read past the end, so the buffer must be bigger than the// expected amount to be skipped to identify cases where more data than// expected is skipped.Buf.resize(ExpectedSkipped + 1);DWARFDataExtractor Data(Buf, sys::IsLittleEndianHost, AddrSize);uint64_t Offset = 0;EXPECT_EQ(DWARFFormValue::skipValue(Fm, Data, &Offset,{Version, AddrSize, Dwarf}),ExpectedResult);EXPECT_EQ(Offset, ExpectedSkipped);}Form Fm;uint16_t Version;uint8_t AddrSize;DwarfFormat Dwarf;ArrayRef<uint8_t> InitialData;uint64_t ExpectedSkipped;bool ExpectedResult;};template <typename T> static ArrayRef<uint8_t> toBytes(const T &Input) {return ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&Input),sizeof(Input));}const uint8_t LEBData[] = {0x80, 0x1};ArrayRef<uint8_t> SampleLEB(LEBData, sizeof(LEBData));const uint8_t SampleLength8 = 0x80;const uint16_t SampleLength16 = 0x80;const uint32_t SampleLength = 0x80;ArrayRef<uint8_t> SampleU8 = toBytes(SampleLength8);ArrayRef<uint8_t> SampleU16 = toBytes(SampleLength16);ArrayRef<uint8_t> SampleU32 = toBytes(SampleLength);const uint8_t StringData[] = "abcdef";ArrayRef<uint8_t> SampleString(StringData, sizeof(StringData));const uint8_t IndirectData8[] = {DW_FORM_data8};const uint8_t IndirectData16[] = {DW_FORM_data16};const uint8_t IndirectAddr[] = {DW_FORM_addr};const uint8_t IndirectIndirectData1[] = {DW_FORM_indirect, DW_FORM_data1};const uint8_t IndirectIndirectEnd[] = {DW_FORM_indirect};// Gtest's paramterised tests only allow a maximum of 50 cases, so split the// test into multiple identical parts to share the cases.struct FormSkipValueFixture1 : FormSkipValueFixtureBase {};struct FormSkipValueFixture2 : FormSkipValueFixtureBase {};TEST_P(FormSkipValueFixture1, skipValuePart1) { doSkipValueTest(); }TEST_P(FormSkipValueFixture2, skipValuePart2) { doSkipValueTest(); }INSTANTIATE_TEST_SUITE_P(SkipValueTestParams1, FormSkipValueFixture1,testing::Values(// Form, Version, AddrSize, DwarfFormat, InitialData, ExpectedSize,// ExpectedResult.ParamType(DW_FORM_exprloc, 0, 0, DWARF32, SampleLEB,SampleLength + SampleLEB.size(), true),ParamType(DW_FORM_block, 0, 0, DWARF32, SampleLEB,SampleLength + SampleLEB.size(), true),ParamType(DW_FORM_block1, 0, 0, DWARF32, SampleU8, SampleLength8 + 1,true),ParamType(DW_FORM_block2, 0, 0, DWARF32, SampleU16, SampleLength16 + 2,true),ParamType(DW_FORM_block4, 0, 0, DWARF32, SampleU32, SampleLength + 4,true),ParamType(DW_FORM_string, 0, 0, DWARF32, SampleString,SampleString.size(), true),ParamType(DW_FORM_addr, 0, 42, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_addr, 4, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_addr, 4, 42, DWARF32, SampleU32, 42, true),ParamType(DW_FORM_ref_addr, 0, 1, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_ref_addr, 1, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_ref_addr, 1, 1, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_ref_addr, 1, 1, DWARF64, SampleU32, 8, true),ParamType(DW_FORM_ref_addr, 2, 42, DWARF32, SampleU32, 42, true),ParamType(DW_FORM_ref_addr, 2, 42, DWARF64, SampleU32, 42, true),ParamType(DW_FORM_ref_addr, 3, 3, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_ref_addr, 3, 3, DWARF64, SampleU32, 8, true),ParamType(DW_FORM_flag_present, 4, 4, DWARF32, SampleU32, 0, true),ParamType(DW_FORM_data1, 0, 0, DWARF32, SampleU32, 1, true),ParamType(DW_FORM_data2, 0, 0, DWARF32, SampleU32, 2, true),ParamType(DW_FORM_data4, 0, 0, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_data8, 0, 0, DWARF32, SampleU32, 8, true),ParamType(DW_FORM_data16, 0, 0, DWARF32, SampleU32, 16, true),ParamType(DW_FORM_flag, 0, 0, DWARF32, SampleU32, 1, true),ParamType(DW_FORM_ref1, 0, 0, DWARF32, SampleU32, 1, true),ParamType(DW_FORM_ref2, 0, 0, DWARF32, SampleU32, 2, true),ParamType(DW_FORM_ref4, 0, 0, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_ref8, 0, 0, DWARF32, SampleU32, 8, true),ParamType(DW_FORM_ref_sig8, 0, 0, DWARF32, SampleU32, 8, true),ParamType(DW_FORM_ref_sup4, 0, 0, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_ref_sup8, 0, 0, DWARF32, SampleU32, 8, true),ParamType(DW_FORM_strx1, 0, 0, DWARF32, SampleU32, 1, true),ParamType(DW_FORM_strx2, 0, 0, DWARF32, SampleU32, 2, true),ParamType(DW_FORM_strx4, 0, 0, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_addrx1, 0, 0, DWARF32, SampleU32, 1, true),ParamType(DW_FORM_addrx2, 0, 0, DWARF32, SampleU32, 2, true),ParamType(DW_FORM_addrx4, 0, 0, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_sec_offset, 0, 1, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_sec_offset, 1, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_sec_offset, 1, 1, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_sec_offset, 1, 1, DWARF64, SampleU32, 8, true),ParamType(DW_FORM_strp, 0, 1, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_strp, 1, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_strp, 1, 1, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_strp, 1, 1, DWARF64, SampleU32, 8, true),ParamType(DW_FORM_strp_sup, 0, 1, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_strp_sup, 1, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_strp_sup, 1, 1, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_strp_sup, 1, 1, DWARF64, SampleU32, 8, true)));INSTANTIATE_TEST_SUITE_P(SkipValueTestParams2, FormSkipValueFixture2,testing::Values(ParamType(DW_FORM_line_strp, 0, 1, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_line_strp, 1, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_line_strp, 1, 1, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_line_strp, 1, 1, DWARF64, SampleU32, 8, true),ParamType(DW_FORM_GNU_ref_alt, 0, 1, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_GNU_ref_alt, 1, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_GNU_ref_alt, 1, 1, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_GNU_ref_alt, 1, 1, DWARF64, SampleU32, 8, true),ParamType(DW_FORM_GNU_strp_alt, 0, 1, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_GNU_strp_alt, 1, 0, DWARF32, SampleU32, 0, false),ParamType(DW_FORM_GNU_strp_alt, 1, 1, DWARF32, SampleU32, 4, true),ParamType(DW_FORM_GNU_strp_alt, 1, 1, DWARF64, SampleU32, 8, true),ParamType(DW_FORM_sdata, 0, 0, DWARF32, SampleLEB, SampleLEB.size(),true),ParamType(DW_FORM_udata, 0, 0, DWARF32, SampleLEB, SampleLEB.size(),true),ParamType(DW_FORM_ref_udata, 0, 0, DWARF32, SampleLEB, SampleLEB.size(),true),ParamType(DW_FORM_strx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(),true),ParamType(DW_FORM_addrx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(),true),ParamType(DW_FORM_loclistx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(),true),ParamType(DW_FORM_rnglistx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(),true),ParamType(DW_FORM_GNU_addr_index, 0, 0, DWARF32, SampleLEB,SampleLEB.size(), true),ParamType(DW_FORM_GNU_str_index, 0, 0, DWARF32, SampleLEB,SampleLEB.size(), true),ParamType(DW_FORM_indirect, 0, 0, DWARF32,ArrayRef<uint8_t>(IndirectData8, sizeof(IndirectData8)), 9,true),ParamType(DW_FORM_indirect, 0, 0, DWARF32,ArrayRef<uint8_t>(IndirectData16, sizeof(IndirectData16)), 17,true),ParamType(DW_FORM_indirect, 4, 0, DWARF32,ArrayRef<uint8_t>(IndirectAddr, sizeof(IndirectAddr)), 1,false),ParamType(DW_FORM_indirect, 4, 4, DWARF32,ArrayRef<uint8_t>(IndirectAddr, sizeof(IndirectAddr)), 5,true),ParamType(DW_FORM_indirect, 4, 4, DWARF32,ArrayRef<uint8_t>(IndirectIndirectData1,sizeof(IndirectIndirectData1)),3, true),ParamType(DW_FORM_indirect, 4, 4, DWARF32,ArrayRef<uint8_t>(IndirectIndirectEnd,sizeof(IndirectIndirectEnd)),2, false),ParamType(/*Unknown=*/Form(0xff), 4, 4, DWARF32, SampleU32, 0, false)));using ErrorParams = std::tuple<Form, std::vector<uint8_t>>;struct ExtractValueErrorFixture : public testing::TestWithParam<ErrorParams> {void SetUp() override { std::tie(Fm, InitialData) = GetParam(); }Form Fm;ArrayRef<uint8_t> InitialData;};TEST_P(ExtractValueErrorFixture, Test) {SCOPED_TRACE(formatv("Fm = {0}, InitialData = {1}", Fm,make_range(InitialData.begin(), InitialData.end())).str());DWARFDataExtractor Data(InitialData, sys::IsLittleEndianHost, 4);DWARFFormValue Form(Fm);uint64_t Offset = 0;EXPECT_FALSE(Form.extractValue(Data, &Offset, {0, 0, DWARF32}));}INSTANTIATE_TEST_SUITE_P(ExtractValueErrorParams, ExtractValueErrorFixture,testing::Values(ErrorParams{DW_FORM_ref_addr, {}}, ErrorParams{DW_FORM_block, {}},ErrorParams{DW_FORM_block, {1}}, ErrorParams{DW_FORM_block, {2, 0}},ErrorParams{DW_FORM_block1, {}}, ErrorParams{DW_FORM_block2, {}},ErrorParams{DW_FORM_block4, {}}, ErrorParams{DW_FORM_data1, {}},ErrorParams{DW_FORM_data2, {}}, ErrorParams{DW_FORM_strx3, {}},ErrorParams{DW_FORM_data4, {}}, ErrorParams{DW_FORM_data8, {}},ErrorParams{DW_FORM_data16, {}}, ErrorParams{DW_FORM_sdata, {}},ErrorParams{DW_FORM_udata, {}}, ErrorParams{DW_FORM_string, {}},ErrorParams{DW_FORM_indirect, {}},ErrorParams{DW_FORM_indirect, {DW_FORM_data1}},ErrorParams{DW_FORM_strp_sup, {}}, ErrorParams{DW_FORM_ref_sig8, {}}));using DumpValueParams =std::tuple<Form, ArrayRef<uint8_t>, DwarfFormat, StringRef>;struct DumpValueFixture : public testing::TestWithParam<DumpValueParams> {void SetUp() override {std::tie(Fm, InitialData, Format, ExpectedResult) = GetParam();}Form Fm;ArrayRef<uint8_t> InitialData;DwarfFormat Format;StringRef ExpectedResult;};TEST_P(DumpValueFixture, Test) {SCOPED_TRACE(formatv("Fm = {0}, InitialData = [{1}], Format = {2}", Fm,toHex(InitialData),Format == DWARF64 ? "DWARF64" : "DWARF32"));DWARFDataExtractor Data(InitialData, sys::IsLittleEndianHost, 8);DWARFFormValue Form(Fm);uint64_t Offset = 0;Form.extractValue(Data, &Offset, {0, 0, Format});std::string Output;raw_string_ostream OS(Output);DIDumpOptions Opts;Opts.Verbose = true;Opts.ShowAddresses = true;Form.dump(OS, Opts);OS.flush();EXPECT_EQ(Output, ExpectedResult);}const uint32_t DumpTestSample32Val = 0x112233;ArrayRef<uint8_t> DumpTestSample32 = toBytes(DumpTestSample32Val);const uint64_t DumpTestSample64Val = 0x11223344556677;ArrayRef<uint8_t> DumpTestSample64 = toBytes(DumpTestSample64Val);INSTANTIATE_TEST_SUITE_P(DumpValueParams, DumpValueFixture,testing::Values(DumpValueParams{DW_FORM_strp, DumpTestSample32, DWARF32," .debug_str[0x00112233] = "},DumpValueParams{DW_FORM_strp, DumpTestSample64, DWARF64," .debug_str[0x0011223344556677] = "},DumpValueParams{DW_FORM_line_strp, DumpTestSample32,DWARF32, " .debug_line_str[0x00112233] = "},DumpValueParams{DW_FORM_line_strp, DumpTestSample64,DWARF64," .debug_line_str[0x0011223344556677] = "},DumpValueParams{DW_FORM_sec_offset, DumpTestSample32,DWARF32, "0x00112233"},DumpValueParams{DW_FORM_sec_offset, DumpTestSample64,DWARF64, "0x0011223344556677"}));} // end anonymous namespace
//===- llvm/unittest/DebugInfo/DWARFExpressionRawDataTest.cpp -------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/Triple.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"#include "llvm/DebugInfo/DWARF/DWARFDie.h"#include "llvm/DebugInfo/DWARF/DWARFExpression.h"#include "llvm/MC/MCAsmBackend.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCCodeEmitter.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCObjectWriter.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/MCStreamer.h"#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Object/Binary.h"#include "llvm/Object/ELFObjectFile.h"#include "llvm/Support/DataExtractor.h"#include "llvm/Support/LEB128.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace dwarf;namespace {/// Tests that a client of DebugInfo/DWARF is able to read raw data bytes of a/// DWARFExpression parsed from CFI with the intent of writing them back as is/// via MC layer / cfi_escape./// This is relevant for binary tools that need to rewrite/copy unwind and/// debug info from input to output binary.class DWARFExpressionCopyBytesTest : public ::testing::Test {public:const char *TripleName = "x86_64-pc-linux";std::unique_ptr<MCRegisterInfo> MRI;std::unique_ptr<MCAsmInfo> MAI;std::unique_ptr<const MCSubtargetInfo> STI;const Target *TheTarget;DWARFExpressionCopyBytesTest() {InitializeAllTargets();InitializeAllTargetMCs();InitializeAllAsmPrinters();std::string ErrorStr;TheTarget = TargetRegistry::lookupTarget(TripleName, ErrorStr);if (!TheTarget)return;MRI.reset(TheTarget->createMCRegInfo(TripleName));MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCTargetOptions()));STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));}struct StreamerContext {std::unique_ptr<MCObjectFileInfo> MOFI;std::unique_ptr<MCContext> Ctx;std::unique_ptr<const MCInstrInfo> MII;std::unique_ptr<MCStreamer> Streamer;};/// Create all data structures necessary to operate an assemblerStreamerContext createStreamer(raw_pwrite_stream &OS);/// Emit a dummy obj file with a single CFI instruction,/// DW_CFA_def_cfa_expression, encoding as its operand the DWARF expression/// represented by ExprBytesSmallString<0> emitObjFile(StringRef ExprBytes);/// Peruse the object file looking for the encoded DWARF expression, and check/// that its operand was encoded correctlyvoid parseCFIsAndCheckExpression(const llvm::object::ObjectFile &E,ArrayRef<uint8_t> Expected);/// Open the in-memory relocatable object file and verify that it contains/// the expected DWARF expression bytesvoid readAndCheckObjFile(StringRef ObjFileData, ArrayRef<uint8_t> Expected);/// Run this test on the DWARF expression represented by the bytes in/// ExprData. Check that the getData() API retrieves these original bytes and/// that we can use them to encode a CFI with those bytes as operands (via/// cfi_escape).void testExpr(ArrayRef<uint8_t> ExprData);};} // namespaceDWARFExpressionCopyBytesTest::StreamerContextDWARFExpressionCopyBytesTest::createStreamer(raw_pwrite_stream &OS) {StreamerContext Res;Res.Ctx =std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),/*MSTI=*/nullptr);Res.MOFI.reset(TheTarget->createMCObjectFileInfo(*Res.Ctx.get(),/*PIC=*/false));Res.Ctx->setObjectFileInfo(Res.MOFI.get());Res.MII.reset(TheTarget->createMCInstrInfo());MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*Res.MII, *Res.Ctx);MCAsmBackend *MAB =TheTarget->createMCAsmBackend(*STI, *MRI, MCTargetOptions());std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);Res.Streamer.reset(TheTarget->createMCObjectStreamer(Triple(TripleName), *Res.Ctx, std::unique_ptr<MCAsmBackend>(MAB),std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *STI,/* RelaxAll */ false,/* IncrementalLinkerCompatible */ false,/* DWARFMustBeAtTheEnd */ false));return Res;}SmallString<0> DWARFExpressionCopyBytesTest::emitObjFile(StringRef ExprBytes) {auto EncodeDefCfaExpr = [&](StringRef Bytes) {std::string Str;raw_string_ostream OS(Str);OS << static_cast<uint8_t>(dwarf::DW_CFA_def_cfa_expression);encodeULEB128(Bytes.size(), OS);OS << Bytes;return Str;};SmallString<0> Storage;raw_svector_ostream VecOS(Storage);StreamerContext C = createStreamer(VecOS);C.Streamer->initSections(false, *STI);MCSection *Section = C.MOFI->getTextSection();Section->setHasInstructions(true);C.Streamer->switchSection(Section);C.Streamer->emitCFIStartProc(true);auto Str = EncodeDefCfaExpr(ExprBytes);C.Streamer->emitCFIEscape(Str);C.Streamer->emitNops(4, 1, SMLoc(), *STI);C.Streamer->emitCFIEndProc();C.Streamer->finish();return Storage;}void DWARFExpressionCopyBytesTest::parseCFIsAndCheckExpression(const llvm::object::ObjectFile &E, ArrayRef<uint8_t> Expected) {auto FetchFirstCfaExpression =[](const DWARFDebugFrame &EHFrame) -> Optional<CFIProgram::Instruction> {for (const dwarf::FrameEntry &Entry : EHFrame.entries()) {const auto *CurFDE = dyn_cast<dwarf::FDE>(&Entry);if (!CurFDE)continue;for (const CFIProgram::Instruction &Instr : CurFDE->cfis()) {if (Instr.Opcode != dwarf::DW_CFA_def_cfa_expression)continue;return Instr;}}return NoneType();};std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(E);const DWARFDebugFrame *EHFrame = cantFail(Ctx->getEHFrame());ASSERT_NE(nullptr, EHFrame);auto CfiInstr = FetchFirstCfaExpression(*EHFrame);ASSERT_TRUE(CfiInstr);DWARFExpression Expr = *(CfiInstr->Expression);StringRef ExprData = Expr.getData();EXPECT_EQ(ExprData.size(), Expected.size());for (unsigned I = 0, E = ExprData.size(); I != E; ++I) {EXPECT_EQ(static_cast<uint8_t>(ExprData[I]), Expected[I]);}}void DWARFExpressionCopyBytesTest::readAndCheckObjFile(StringRef ObjFileData, ArrayRef<uint8_t> Expected) {std::unique_ptr<MemoryBuffer> MB =MemoryBuffer::getMemBuffer(ObjFileData, "", false);std::unique_ptr<object::Binary> Bin =cantFail(llvm::object::createBinary(MB->getMemBufferRef()));if (auto *E = dyn_cast<llvm::object::ELFObjectFileBase>(&*Bin)) {parseCFIsAndCheckExpression(*E, Expected);}}void DWARFExpressionCopyBytesTest::testExpr(ArrayRef<uint8_t> ExprData) {// If we didn't build x86, do not run the test.if (!MRI)GTEST_SKIP();DataExtractor DE(ExprData, true, 8);DWARFExpression Expr(DE, 8);// Copy this expression into the CFI of a binary and check that we are able to// get it back correctly from this binary.const SmallString<0> EmittedBinContents = emitObjFile(Expr.getData());readAndCheckObjFile(EmittedBinContents.str(), ExprData);}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg0) { testExpr({DW_OP_reg0}); }TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg10) { testExpr({DW_OP_reg10}); }TEST_F(DWARFExpressionCopyBytesTest, Test_OP_regx) {testExpr({DW_OP_regx, 0x80, 0x02});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0) {testExpr({DW_OP_breg0, 0x04});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_large_offset) {testExpr({DW_OP_breg0, 0x80, 0x02});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13) {testExpr({DW_OP_breg13, 0x10});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13_zero_offset) {testExpr({DW_OP_breg13, 0x00});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_negative) {testExpr({DW_OP_breg0, 0x70});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_bregx) {testExpr({DW_OP_bregx, 0x0d, 0x28});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_stack_value) {testExpr({DW_OP_breg13, 0x04, DW_OP_stack_value});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value) {testExpr({DW_OP_entry_value, 0x01, DW_OP_reg0, DW_OP_stack_value});}TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value_mem) {testExpr({DW_OP_entry_value, 0x02, DW_OP_breg13, 0x10, DW_OP_stack_value});}
//===- llvm/unittest/DebugInfo/DWARFExpressionCompactPrinterTest.cpp ------===////// 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////===----------------------------------------------------------------------===//#include "DwarfGenerator.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/DebugInfo/DWARF/DWARFDie.h"#include "llvm/DebugInfo/DWARF/DWARFExpression.h"#include "llvm/MC/MCInstrInfo.h"#include "llvm/MC/MCRegisterInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/DataExtractor.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace dwarf;namespace {class DWARFExpressionCompactPrinterTest : public ::testing::Test {public:std::unique_ptr<MCRegisterInfo> MRI;DWARFExpressionCompactPrinterTest() {InitializeAllTargets();InitializeAllTargetMCs();InitializeAllAsmPrinters();std::string TripleName = "armv8a-linux-gnueabi";std::string ErrorStr;const Target *TheTarget =TargetRegistry::lookupTarget(TripleName, ErrorStr);if (!TheTarget)return;MRI.reset(TheTarget->createMCRegInfo(TripleName));}void TestExprPrinter(ArrayRef<uint8_t> ExprData, StringRef Expected);};} // namespacevoid DWARFExpressionCompactPrinterTest::TestExprPrinter(ArrayRef<uint8_t> ExprData, StringRef Expected) {// If we didn't build ARM, do not run the test.if (!MRI)GTEST_SKIP();// Print the expression, passing in the subprogram DIE, and check that the// result is as expected.std::string Result;raw_string_ostream OS(Result);DataExtractor DE(ExprData, true, 8);DWARFExpression Expr(DE, 8);Expr.printCompact(OS, *MRI);EXPECT_EQ(OS.str(), Expected);}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_reg0) {TestExprPrinter({DW_OP_reg0}, "R0");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_reg10) {TestExprPrinter({DW_OP_reg10}, "R10");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_regx) {TestExprPrinter({DW_OP_regx, 0x80, 0x02}, "D0");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_breg0) {TestExprPrinter({DW_OP_breg0, 0x04}, "[R0+4]");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_breg0_large_offset) {TestExprPrinter({DW_OP_breg0, 0x80, 0x02}, "[R0+256]");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_breg13) {TestExprPrinter({DW_OP_breg13, 0x10}, "[SP+16]");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_breg13_zero_offset) {TestExprPrinter({DW_OP_breg13, 0x00}, "[SP]");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_breg0_negative) {TestExprPrinter({DW_OP_breg0, 0x70}, "[R0-16]");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_bregx) {TestExprPrinter({DW_OP_bregx, 0x0d, 0x28}, "[SP+40]");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_stack_value) {TestExprPrinter({DW_OP_breg13, 0x04, DW_OP_stack_value}, "SP+4");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_entry_value) {TestExprPrinter({DW_OP_entry_value, 0x01, DW_OP_reg0, DW_OP_stack_value},"entry(R0)");}TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_entry_value_mem) {TestExprPrinter({DW_OP_entry_value, 0x02, DW_OP_breg13, 0x10, DW_OP_stack_value},"entry([SP+16])");}
//===- llvm/unittest/DebugInfo/DWARFDieTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/ObjectYAML/DWARFEmitter.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::dwarf;using object::SectionedAddress;namespace {TEST(DWARFDie, getLocations) {const char *yamldata = R"(debug_abbrev:- Table:- Code: 0x00000001Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_locationForm: DW_FORM_sec_offset- Attribute: DW_AT_data_member_locationForm: DW_FORM_exprloc- Attribute: DW_AT_vtable_elem_locationForm: DW_FORM_sec_offset- Attribute: DW_AT_call_data_locationForm: DW_FORM_sec_offsetdebug_info:- Version: 5UnitType: DW_UT_compileAddrSize: 4Entries:- AbbrCode: 0x00000001Values:- Value: 12- Value: 0x0000000000000001BlockData: [ 0x47 ]- Value: 20- Value: 25debug_loclists:- AddressSize: 4OffsetEntryCount: 0Lists:- Entries:- Operator: DW_LLE_start_lengthValues: [ 0x01, 0x02 ]- Operator: DW_LLE_end_of_list- Entries:- Operator: DW_LLE_startx_lengthValues: [ 0x01, 0x02 ]- Operator: DW_LLE_end_of_list- Entries:- Operator: DW_LLE_start_lengthValues: [ 0x01, 0x02 ]## end_of_list intentionally missing.)";Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =DWARFYAML::emitDebugSections(StringRef(yamldata),/*IsLittleEndian=*/true,/*Is64BitAddrSize=*/false);ASSERT_THAT_EXPECTED(Sections, Succeeded());std::unique_ptr<DWARFContext> Ctx =DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);ASSERT_NE(nullptr, CU);DWARFDie Die = CU->getUnitDIE();ASSERT_TRUE(Die.isValid());EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_location),HasValue(testing::ElementsAre(DWARFLocationExpression{DWARFAddressRange{1, 3}, {}})));EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_data_member_location),HasValue(testing::ElementsAre(DWARFLocationExpression{None, {0x47}})));EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_vtable_elem_location),Failed<ErrorInfoBase>(testing::Property(&ErrorInfoBase::message,"unable to resolve indirect address 1 for: DW_LLE_startx_length")));EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_call_data_location),FailedWithMessage("unexpected end of data at offset 0x20 while reading [0x20, 0x21)"));EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_call_data_value),Failed<ErrorInfoBase>(testing::Property(&ErrorInfoBase::message,"No DW_AT_call_data_value")));}TEST(DWARFDie, getDeclFile) {const char *yamldata = R"(debug_str:- ''debug_abbrev:- ID: 0Table:- Code: 0x1Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_stmt_listForm: DW_FORM_sec_offset- Code: 0x2Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_decl_fileForm: DW_FORM_data1debug_info:- Length: 0xFVersion: 4AbbrevTableID: 0AbbrOffset: 0x0AddrSize: 8Entries:- AbbrCode: 0x1Values:- Value: 0x0- AbbrCode: 0x2Values:- Value: 0x1- AbbrCode: 0x0debug_line:- Length: 42Version: 2PrologueLength: 36MinInstLength: 1DefaultIsStmt: 1LineBase: 251LineRange: 14OpcodeBase: 13StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]IncludeDirs:- '/tmp'Files:- Name: main.cppDirIdx: 1ModTime: 0Length: 0)";// Given DWARF like this://// 0x0000000b: DW_TAG_compile_unit// DW_AT_stmt_list (0x00000000)//// 0x00000010: DW_TAG_subprogram// DW_AT_decl_file ("/tmp/main.cpp")//// 0x00000012: NULL//// This tests that we can extract the right DW_AT_decl_file from a DIE that// has a DW_AT_decl_file attribute.Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =DWARFYAML::emitDebugSections(StringRef(yamldata),/*IsLittleEndian=*/true,/*Is64BitAddrSize=*/true);ASSERT_THAT_EXPECTED(Sections, Succeeded());std::unique_ptr<DWARFContext> Ctx =DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);ASSERT_NE(nullptr, CU);DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);ASSERT_TRUE(Die.isValid());DWARFDie MainDie = Die.getFirstChild();ASSERT_TRUE(MainDie.isValid());std::string DeclFile = MainDie.getDeclFile(DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);std::string Ref =("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str();EXPECT_EQ(DeclFile, Ref);}TEST(DWARFDie, getDeclFileAbstractOrigin) {const char *yamldata = R"(debug_str:- ''debug_abbrev:- ID: 0Table:- Code: 0x1Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_stmt_listForm: DW_FORM_sec_offset- Code: 0x2Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_abstract_originForm: DW_FORM_ref_addr- Code: 0x3Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_decl_fileForm: DW_FORM_data1debug_info:- Length: 0x14Version: 4AbbrevTableID: 0AbbrOffset: 0x0AddrSize: 8Entries:- AbbrCode: 0x1Values:- Value: 0x0- AbbrCode: 0x2Values:- Value: 0x15- AbbrCode: 0x3Values:- Value: 0x1- AbbrCode: 0x0debug_line:- Length: 42Version: 2PrologueLength: 36MinInstLength: 1DefaultIsStmt: 1LineBase: 251LineRange: 14OpcodeBase: 13StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]IncludeDirs:- '/tmp'Files:- Name: main.cppDirIdx: 1ModTime: 0Length: 0)";// Given DWARF like this://// 0x0000000b: DW_TAG_compile_unit// DW_AT_stmt_list (0x00000000)//// 0x00000010: DW_TAG_subprogram// DW_AT_abstract_origin (0x0000000000000015)//// 0x00000015: DW_TAG_subprogram// DW_AT_decl_file ("/tmp/main.cpp")//// 0x00000017: NULL////// The DIE at 0x00000010 uses a DW_AT_abstract_origin to point to the DIE at// 0x00000015, make sure that DWARFDie::getDeclFile() succeeds by extracting// the right file name of "/tmp/main.cpp".//// This tests that when we have a DW_AT_abstract_origin with a compile unit// relative form (DW_FORM_ref4) to another DIE that we get the right// DW_AT_decl_file value.Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =DWARFYAML::emitDebugSections(StringRef(yamldata),/*IsLittleEndian=*/true,/*Is64BitAddrSize=*/true);ASSERT_THAT_EXPECTED(Sections, Succeeded());std::unique_ptr<DWARFContext> Ctx =DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);ASSERT_NE(nullptr, CU);DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);ASSERT_TRUE(Die.isValid());DWARFDie MainDie = Die.getFirstChild();ASSERT_TRUE(MainDie.isValid());std::string DeclFile = MainDie.getDeclFile(DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);std::string Ref =("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str();EXPECT_EQ(DeclFile, Ref);}TEST(DWARFDie, getDeclFileSpecification) {const char *yamldata = R"(debug_str:- ''debug_abbrev:- ID: 0Table:- Code: 0x1Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_stmt_listForm: DW_FORM_sec_offset- Code: 0x2Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_specificationForm: DW_FORM_ref_addr- Code: 0x3Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_decl_fileForm: DW_FORM_data1debug_info:- Length: 0x14Version: 4AbbrevTableID: 0AbbrOffset: 0x0AddrSize: 8Entries:- AbbrCode: 0x1Values:- Value: 0x0- AbbrCode: 0x2Values:- Value: 0x15- AbbrCode: 0x3Values:- Value: 0x1- AbbrCode: 0x0debug_line:- Length: 42Version: 2PrologueLength: 36MinInstLength: 1DefaultIsStmt: 1LineBase: 251LineRange: 14OpcodeBase: 13StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]IncludeDirs:- '/tmp'Files:- Name: main.cppDirIdx: 1ModTime: 0Length: 0)";// Given DWARF like this://// 0x0000000b: DW_TAG_compile_unit// DW_AT_stmt_list (0x00000000)//// 0x00000010: DW_TAG_subprogram// DW_AT_specification (0x0000000000000015)//// 0x00000015: DW_TAG_subprogram// DW_AT_decl_file ("/tmp/main.cpp")//// 0x00000017: NULL//// The DIE at 0x00000010 uses a DW_AT_specification to point to the DIE at// 0x00000015, make sure that DWARFDie::getDeclFile() succeeds by extracting// the right file name of "/tmp/main.cpp".//// This tests that when we have a DW_AT_specification with a compile unit// relative form (DW_FORM_ref4) to another DIE that we get the right// DW_AT_decl_file value.Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =DWARFYAML::emitDebugSections(StringRef(yamldata),/*IsLittleEndian=*/true,/*Is64BitAddrSize=*/true);ASSERT_THAT_EXPECTED(Sections, Succeeded());std::unique_ptr<DWARFContext> Ctx =DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);ASSERT_NE(nullptr, CU);DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);ASSERT_TRUE(Die.isValid());DWARFDie MainDie = Die.getFirstChild();ASSERT_TRUE(MainDie.isValid());std::string DeclFile = MainDie.getDeclFile(DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);std::string Ref =("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str();EXPECT_EQ(DeclFile, Ref);}TEST(DWARFDie, getDeclFileAbstractOriginAcrossCUBoundary) {const char *yamldata = R"(debug_str:- ''debug_abbrev:- ID: 0Table:- Code: 0x1Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yes- Code: 0x2Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_abstract_originForm: DW_FORM_ref_addr- Code: 0x3Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_stmt_listForm: DW_FORM_sec_offset- Code: 0x4Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_decl_fileForm: DW_FORM_data1debug_info:- Length: 0xEVersion: 4AbbrevTableID: 0AbbrOffset: 0x0AddrSize: 8Entries:- AbbrCode: 0x1- AbbrCode: 0x2Values:- Value: 0x22- AbbrCode: 0x0- Length: 0xFVersion: 4AbbrevTableID: 0AbbrOffset: 0x0AddrSize: 8Entries:- AbbrCode: 0x3Values:- Value: 0x0- AbbrCode: 0x4Values:- Value: 0x1- AbbrCode: 0x0debug_line:- Length: 42Version: 2PrologueLength: 36MinInstLength: 1DefaultIsStmt: 1LineBase: 251LineRange: 14OpcodeBase: 13StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]IncludeDirs:- '/tmp'Files:- Name: main.cppDirIdx: 1ModTime: 0Length: 0)";// Given DWARF like this://// 0x0000000b: DW_TAG_compile_unit//// 0x0000000c: DW_TAG_subprogram// DW_AT_abstract_origin (0x0000000000000022)//// 0x00000011: NULL//// 0x0000001d: DW_TAG_compile_unit// DW_AT_stmt_list (0x00000000)//// 0x00000022: DW_TAG_subprogram// DW_AT_decl_file ("/tmp/main.cpp")//// 0x00000024: NULL//// This tests that when we have a DW_AT_abstract_origin with a// DW_FORM_ref_addr to another DIE in another compile unit that we use the// right file table when converting the file index of the DW_AT_decl_file.//// The DIE at 0x0000000c uses a DW_AT_abstract_origin to point to the DIE at// 0x00000022, make sure that DWARFDie::getDeclFile() succeeds by extracting// the right file name of "/tmp/main.cpp". The DW_AT_decl_file must grab the// file from the line table prologue of the compile unit at offset// 0x0000001d.Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =DWARFYAML::emitDebugSections(StringRef(yamldata),/*IsLittleEndian=*/true,/*Is64BitAddrSize=*/true);ASSERT_THAT_EXPECTED(Sections, Succeeded());std::unique_ptr<DWARFContext> Ctx =DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);ASSERT_NE(nullptr, CU);DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);ASSERT_TRUE(Die.isValid());DWARFDie MainDie = Die.getFirstChild();ASSERT_TRUE(MainDie.isValid());std::string DeclFile = MainDie.getDeclFile(DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);std::string Ref =("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str();EXPECT_EQ(DeclFile, Ref);}TEST(DWARFDie, getDeclFileSpecificationAcrossCUBoundary) {const char *yamldata = R"(debug_str:- ''debug_abbrev:- ID: 0Table:- Code: 0x1Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yes- Code: 0x2Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_specificationForm: DW_FORM_ref_addr- Code: 0x3Tag: DW_TAG_compile_unitChildren: DW_CHILDREN_yesAttributes:- Attribute: DW_AT_stmt_listForm: DW_FORM_sec_offset- Code: 0x4Tag: DW_TAG_subprogramChildren: DW_CHILDREN_noAttributes:- Attribute: DW_AT_decl_fileForm: DW_FORM_data1debug_info:- Length: 0xEVersion: 4AbbrevTableID: 0AbbrOffset: 0x0AddrSize: 8Entries:- AbbrCode: 0x1- AbbrCode: 0x2Values:- Value: 0x22- AbbrCode: 0x0- Length: 0xFVersion: 4AbbrevTableID: 0AbbrOffset: 0x0AddrSize: 8Entries:- AbbrCode: 0x3Values:- Value: 0x0- AbbrCode: 0x4Values:- Value: 0x1- AbbrCode: 0x0debug_line:- Length: 42Version: 2PrologueLength: 36MinInstLength: 1DefaultIsStmt: 1LineBase: 251LineRange: 14OpcodeBase: 13StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]IncludeDirs:- '/tmp'Files:- Name: main.cppDirIdx: 1ModTime: 0Length: 0)";// Given DWARF like this://// 0x0000000b: DW_TAG_compile_unit//// 0x0000000c: DW_TAG_subprogram// DW_AT_specification (0x0000000000000022)//// 0x00000011: NULL//// 0x0000001d: DW_TAG_compile_unit// DW_AT_stmt_list (0x00000000)//// 0x00000022: DW_TAG_subprogram// DW_AT_decl_file ("/tmp/main.cpp")//// 0x00000024: NULL//// This tests that when we have a DW_AT_specification with a// DW_FORM_ref_addr to another DIE in another compile unit that we use the// right file table when converting the file index of the DW_AT_decl_file.//// The DIE at 0x0000000c uses a DW_AT_specification to point to the DIE at// 0x00000022, make sure that DWARFDie::getDeclFile() succeeds by extracting// the right file name of "/tmp/main.cpp". The DW_AT_decl_file must grab the// file from the line table prologue of the compile unit at offset// 0x0000001d.Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =DWARFYAML::emitDebugSections(StringRef(yamldata),/*IsLittleEndian=*/true,/*Is64BitAddrSize=*/true);ASSERT_THAT_EXPECTED(Sections, Succeeded());std::unique_ptr<DWARFContext> Ctx =DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);ASSERT_NE(nullptr, CU);DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);ASSERT_TRUE(Die.isValid());DWARFDie MainDie = Die.getFirstChild();ASSERT_TRUE(MainDie.isValid());std::string DeclFile = MainDie.getDeclFile(DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);std::string Ref =("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str();EXPECT_EQ(DeclFile, Ref);}} // end anonymous namespace
//===-llvm/unittest/DebugInfo/DWARFDieManualExtractTest.cpp---------------===////// 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////===----------------------------------------------------------------------===//#include "DwarfGenerator.h"#include "DwarfUtils.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/ObjectYAML/DWARFEmitter.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::dwarf;using namespace utils;namespace {TEST(DWARFDie, manualExtractDump) {typedef uint32_t AddrType;uint16_t Version = 4;Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType));if (!isConfigurationSupported(Triple))GTEST_SKIP();auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &DGCU = DG->addCompileUnit();dwarfgen::DIE CUDie = DGCU.getUnitDIE();CUDie.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c");CUDie.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C);dwarfgen::DIE SubprogramDie = CUDie.addChild(DW_TAG_subprogram);SubprogramDie.addAttribute(DW_AT_name, DW_FORM_strp, "main");SubprogramDie.addAttribute(DW_AT_low_pc, DW_FORM_addr, 0x1000U);SubprogramDie.addAttribute(DW_AT_high_pc, DW_FORM_addr, 0x2000U);StringRef FileBytes = DG->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(**Obj);DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);ASSERT_NE(nullptr, CU);// Manually extracting DWARF DIE.uint64_t DIEOffset = CU->getOffset() + CU->getHeaderSize();uint64_t NextCUOffset = CU->getNextUnitOffset();DWARFDebugInfoEntry DieInfo;DWARFDataExtractor DebugInfoData = CU->getDebugInfoExtractor();ASSERT_TRUE(DieInfo.extractFast(*CU, &DIEOffset, DebugInfoData, NextCUOffset,UINT32_MAX));DWARFDie Die(CU, &DieInfo);ASSERT_TRUE(Die.isValid());ASSERT_TRUE(Die.hasChildren());// Since we have extracted manually DieArray is empty.// Dump function should respect the default flags and print just current DIE,// and not explore children.SmallString<512> Output;raw_svector_ostream OS(Output);Die.dump(OS);constexpr size_t NumOfLines = 3;SmallVector<StringRef, NumOfLines> Strings;SmallVector<StringRef, NumOfLines> ValidStrings = {"0x0000000b: DW_TAG_compile_unit"," DW_AT_name (\"/tmp/main.c\")"," DW_AT_language (DW_LANG_C)"};Output.str().split(Strings, '\n', -1, false);ASSERT_EQ(Strings.size(), NumOfLines);for (size_t I = 0; I < NumOfLines; ++I)EXPECT_EQ(ValidStrings[I], Strings[I]);}} // end anonymous namespace
//===- DWARFDebugLineTest.cpp ---------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "DwarfGenerator.h"#include "DwarfUtils.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"#include "llvm/Object/ObjectFile.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace dwarf;using namespace dwarfgen;using namespace object;using namespace utils;using namespace testing;namespace {struct CommonFixture {CommonFixture(): LineData("", true, 0), Recoverable(Error::success()),RecordRecoverable(std::bind(&CommonFixture::recordRecoverable, this,std::placeholders::_1)),Unrecoverable(Error::success()),RecordUnrecoverable(std::bind(&CommonFixture::recordUnrecoverable, this,std::placeholders::_1)){};~CommonFixture() {EXPECT_FALSE(Recoverable);EXPECT_FALSE(Unrecoverable);}// Note: ASSERT_THAT_EXPECTED cannot be used in a non-void function, so// setupGenerator() is split into two.void setupGeneratorImpl(uint16_t Version, uint8_t AddrSize) {AddressSize = AddrSize;Triple T = getDefaultTargetTripleForAddrSize(AddressSize ? AddressSize : 8);if (!isConfigurationSupported(T))return;auto ExpectedGenerator = Generator::create(T, Version);ASSERT_THAT_EXPECTED(ExpectedGenerator, Succeeded());Gen = std::move(*ExpectedGenerator);}bool setupGenerator(uint16_t Version = 4, uint8_t AddrSize = 8) {setupGeneratorImpl(Version, AddrSize);return Gen != nullptr;}void generate() {Context = createContext();assert(Context != nullptr && "test state is not valid");const DWARFObject &Obj = Context->getDWARFObj();uint8_t TargetAddrSize = AddressSize == 0 ? 8 : AddressSize;LineData = DWARFDataExtractor(Obj, Obj.getLineSection(),getDefaultTargetTripleForAddrSize(TargetAddrSize).isLittleEndian(),AddressSize);}std::unique_ptr<DWARFContext> createContext() {assert(Gen != nullptr && "Generator is not set up");StringRef FileBytes = Gen->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);if (Obj)return DWARFContext::create(**Obj);return nullptr;}DWARFDebugLine::SectionParser setupParser() {LineTable < = Gen->addLineTable(DWARF32);LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});LT.addStandardOpcode(DW_LNS_copy, {});LT.addByte(0xaa);LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});LineTable <2 = Gen->addLineTable(DWARF64);LT2.addExtendedOpcode(9, DW_LNE_set_address,{{0x11223344, LineTable::Quad}});LT2.addStandardOpcode(DW_LNS_copy, {});LT2.addByte(0xbb);LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();return DWARFDebugLine::SectionParser(LineData, *Context, Units);}void recordRecoverable(Error Err) {Recoverable = joinErrors(std::move(Recoverable), std::move(Err));}void recordUnrecoverable(Error Err) {Unrecoverable = joinErrors(std::move(Unrecoverable), std::move(Err));}Expected<const DWARFDebugLine::LineTable *>getOrParseLineTableFatalErrors(uint64_t Offset = 0) {auto ExpectedLineTable = Line.getOrParseLineTable(LineData, Offset, *Context, nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());return ExpectedLineTable;}uint8_t AddressSize;std::unique_ptr<Generator> Gen;std::unique_ptr<DWARFContext> Context;DWARFDataExtractor LineData;DWARFDebugLine Line;Error Recoverable;std::function<void(Error)> RecordRecoverable;Error Unrecoverable;std::function<void(Error)> RecordUnrecoverable;SmallVector<std::unique_ptr<DWARFUnit>, 2> Units;};// Fixtures must derive from "Test", but parameterised fixtures from// "TestWithParam". It does not seem possible to inherit from both, so we share// the common state in a separate class, inherited by the two fixture classes.struct DebugLineBasicFixture : public Test, public CommonFixture {};struct DebugLineParameterisedFixture: public TestWithParam<std::pair<uint16_t, DwarfFormat>>,public CommonFixture {void SetUp() override { std::tie(Version, Format) = GetParam(); }uint16_t Version;DwarfFormat Format;};void checkDefaultPrologue(uint16_t Version, DwarfFormat Format,DWARFDebugLine::Prologue Prologue,uint64_t BodyLength) {// Check version specific fields and values.uint64_t UnitLength;uint64_t PrologueLength;switch (Version) {case 4:PrologueLength = 36;UnitLength = PrologueLength + 2;EXPECT_EQ(Prologue.MaxOpsPerInst, 1u);break;case 2:case 3:PrologueLength = 35;UnitLength = PrologueLength + 2;break;case 5:PrologueLength = 42;UnitLength = PrologueLength + 4;EXPECT_EQ(Prologue.getAddressSize(), 8u);EXPECT_EQ(Prologue.SegSelectorSize, 0u);break;default:llvm_unreachable("unsupported DWARF version");}UnitLength += BodyLength + (Format == DWARF32 ? 4 : 8);EXPECT_EQ(Prologue.TotalLength, UnitLength);EXPECT_EQ(Prologue.PrologueLength, PrologueLength);EXPECT_EQ(Prologue.MinInstLength, 1u);EXPECT_EQ(Prologue.DefaultIsStmt, 1u);EXPECT_EQ(Prologue.LineBase, -5);EXPECT_EQ(Prologue.LineRange, 14u);EXPECT_EQ(Prologue.OpcodeBase, 13u);std::vector<uint8_t> ExpectedLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};EXPECT_EQ(Prologue.StandardOpcodeLengths, ExpectedLengths);ASSERT_EQ(Prologue.IncludeDirectories.size(), 1u);ASSERT_EQ(Prologue.IncludeDirectories[0].getForm(), DW_FORM_string);EXPECT_STREQ(*toString(Prologue.IncludeDirectories[0]), "a dir");ASSERT_EQ(Prologue.FileNames.size(), 1u);ASSERT_EQ(Prologue.FileNames[0].Name.getForm(), DW_FORM_string);ASSERT_EQ(Prologue.FileNames[0].DirIdx, 0u);EXPECT_STREQ(*toString(Prologue.FileNames[0].Name), "a file");}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_GetOrParseLineTableAtInvalidOffset) {#elseTEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) {#endifif (!setupGenerator())GTEST_SKIP();generate();EXPECT_THAT_EXPECTED(getOrParseLineTableFatalErrors(0),FailedWithMessage("offset 0x00000000 is not a valid debug line section offset"));// Repeat to show that an error is reported each time.EXPECT_THAT_EXPECTED(getOrParseLineTableFatalErrors(0),FailedWithMessage("offset 0x00000000 is not a valid debug line section offset"));// Show that an error is reported for later offsets too.EXPECT_THAT_EXPECTED(getOrParseLineTableFatalErrors(1),FailedWithMessage("offset 0x00000001 is not a valid debug line section offset"));}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_GetOrParseLineTableAtInvalidOffsetAfterData) {#elseTEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.setCustomPrologue({{0, LineTable::Byte}});generate();EXPECT_THAT_EXPECTED(getOrParseLineTableFatalErrors(0),FailedWithMessage("parsing line table prologue at offset 0x00000000: ""unexpected end of data at offset 0x1 while reading [0x0, 0x4)"));EXPECT_THAT_EXPECTED(getOrParseLineTableFatalErrors(1),FailedWithMessage("offset 0x00000001 is not a valid debug line section offset"));}#ifdef _AIXTEST_P(DebugLineParameterisedFixture, DISABLED_PrologueGetLength) {#elseTEST_P(DebugLineParameterisedFixture, PrologueGetLength) {#endifif (!setupGenerator(Version))GTEST_SKIP();LineTable < = Gen->addLineTable(Format);DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();LT.setPrologue(Prologue);generate();// + 10 for sizes of DWARF-32 unit length, version, prologue length.uint64_t ExpectedLength = Prologue.PrologueLength + 10;if (Version == 5)// Add address and segment selector size fields.ExpectedLength += 2;if (Format == DWARF64)// Unit length grows by 8, prologue length by 4.ExpectedLength += 12;auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());EXPECT_EQ((*ExpectedLineTable)->Prologue.getLength(), ExpectedLength);}#ifdef _AIXTEST_P(DebugLineParameterisedFixture, DISABLED_GetOrParseLineTableValidTable) {#elseTEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) {#endifif (!setupGenerator(Version))GTEST_SKIP();SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +(Format == DWARF64 ? "DWARF64" : "DWARF32"));LineTable < = Gen->addLineTable(Format);LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});LT.addStandardOpcode(DW_LNS_copy, {});LT.addByte(0xaa);LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});LineTable <2 = Gen->addLineTable(Format);LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});LT2.addStandardOpcode(DW_LNS_copy, {});LT2.addByte(0xbb);LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});LT2.addStandardOpcode(DW_LNS_copy, {});LT2.addByte(0xcc);LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);ASSERT_TRUE(ExpectedLineTable.operator bool());EXPECT_FALSE(Recoverable);const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;checkDefaultPrologue(Version, Format, Expected->Prologue, 16);EXPECT_EQ(Expected->Sequences.size(), 1u);uint64_t SecondOffset =Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;Recoverable = Error::success();auto ExpectedLineTable2 = Line.getOrParseLineTable(LineData, SecondOffset, *Context, nullptr, RecordRecoverable);ASSERT_TRUE(ExpectedLineTable2.operator bool());EXPECT_FALSE(Recoverable);const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);EXPECT_EQ(Expected2->Sequences.size(), 2u);EXPECT_NE(Expected, Expected2);// Check that if the same offset is requested, the exact same pointer is// returned.Recoverable = Error::success();auto ExpectedLineTable3 = Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordRecoverable);ASSERT_TRUE(ExpectedLineTable3.operator bool());EXPECT_FALSE(Recoverable);EXPECT_EQ(Expected, *ExpectedLineTable3);Recoverable = Error::success();auto ExpectedLineTable4 = Line.getOrParseLineTable(LineData, SecondOffset, *Context, nullptr, RecordRecoverable);ASSERT_TRUE(ExpectedLineTable4.operator bool());EXPECT_FALSE(Recoverable);EXPECT_EQ(Expected2, *ExpectedLineTable4);// TODO: Add tests that show that the body of the programs have been read// correctly.}#ifdef _AIXTEST_P(DebugLineParameterisedFixture, DISABLED_ClearLineValidTable) {#elseTEST_P(DebugLineParameterisedFixture, ClearLineValidTable) {#endifif (!setupGenerator(Version))GTEST_SKIP();SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +(Format == DWARF64 ? "DWARF64" : "DWARF32"));LineTable < = Gen->addLineTable(Format);LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});LT.addStandardOpcode(DW_LNS_copy, {});LT.addByte(0xaa);LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});LineTable <2 = Gen->addLineTable(Format);LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});LT2.addStandardOpcode(DW_LNS_copy, {});LT2.addByte(0xbb);LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});LT2.addStandardOpcode(DW_LNS_copy, {});LT2.addByte(0xcc);LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();// Check that we have what we expect before calling clearLineTable().auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);ASSERT_TRUE((bool)ExpectedLineTable);EXPECT_FALSE(Recoverable);const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;checkDefaultPrologue(Version, Format, Expected->Prologue, 16);EXPECT_EQ(Expected->Sequences.size(), 1u);uint64_t SecondOffset =Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;Recoverable = Error::success();auto ExpectedLineTable2 = Line.getOrParseLineTable(LineData, SecondOffset, *Context, nullptr, RecordRecoverable);ASSERT_TRUE((bool)ExpectedLineTable2);EXPECT_FALSE(Recoverable);const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);EXPECT_EQ(Expected2->Sequences.size(), 2u);// Check that we no longer get the line tables after clearLineTable().Line.clearLineTable(0);Line.clearLineTable(SecondOffset);EXPECT_EQ(Line.getLineTable(0), nullptr);EXPECT_EQ(Line.getLineTable(SecondOffset), nullptr);// Check that if the same offset is requested, the contents match what we// had before.Recoverable = Error::success();auto ExpectedLineTable3 = Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordRecoverable);ASSERT_TRUE((bool)ExpectedLineTable3);EXPECT_FALSE(Recoverable);const DWARFDebugLine::LineTable *Expected3 = *ExpectedLineTable3;checkDefaultPrologue(Version, Format, Expected3->Prologue, 16);EXPECT_EQ(Expected3->Sequences.size(), 1u);Recoverable = Error::success();auto ExpectedLineTable4 = Line.getOrParseLineTable(LineData, SecondOffset, *Context, nullptr, RecordRecoverable);ASSERT_TRUE((bool)ExpectedLineTable4);EXPECT_FALSE(Recoverable);const DWARFDebugLine::LineTable *Expected4 = *ExpectedLineTable4;checkDefaultPrologue(Version, Format, Expected4->Prologue, 32);EXPECT_EQ(Expected4->Sequences.size(), 2u);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ErrorForReservedLength) {#elseTEST_F(DebugLineBasicFixture, ErrorForReservedLength) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});generate();EXPECT_THAT_EXPECTED(getOrParseLineTableFatalErrors(),FailedWithMessage("parsing line table prologue at offset 0x00000000: unsupported ""reserved unit length of value 0xfffffff0"));}struct DebugLineUnsupportedVersionFixture : public TestWithParam<uint16_t>,public CommonFixture {void SetUp() override { Version = GetParam(); }uint16_t Version;};#ifdef _AIXTEST_P(DebugLineUnsupportedVersionFixture,DISABLED_ErrorForUnsupportedVersion) {#elseTEST_P(DebugLineUnsupportedVersionFixture, ErrorForUnsupportedVersion) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.setCustomPrologue({{LineTable::Half, LineTable::Long}, {Version, LineTable::Half}});generate();EXPECT_THAT_EXPECTED(getOrParseLineTableFatalErrors(),FailedWithMessage("parsing line table prologue at offset 0x00000000: ""unsupported version " +std::to_string(Version)));}INSTANTIATE_TEST_SUITE_P(UnsupportedVersionTestParams,DebugLineUnsupportedVersionFixture,Values(/*1 below min */ 1, /* 1 above max */ 6,/* Maximum possible */ 0xffff));#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ErrorForInvalidV5IncludeDirTable) {#elseTEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {#endifif (!setupGenerator(5))GTEST_SKIP();LineTable < = Gen->addLineTable();LT.setCustomPrologue({{19, LineTable::Long}, // unit length{5, LineTable::Half}, // version{8, LineTable::Byte}, // addr size{0, LineTable::Byte}, // segment selector size{11, LineTable::Long}, // prologue length{1, LineTable::Byte}, // min instruction length{1, LineTable::Byte}, // max ops per instruction{1, LineTable::Byte}, // default is_stmt{0, LineTable::Byte}, // line base{14, LineTable::Byte}, // line range{2, LineTable::Byte}, // opcode base (small to reduce the amount of// setup required).{0, LineTable::Byte}, // standard opcode lengths{0, LineTable::Byte}, // directory entry format count (should not be// zero).{0, LineTable::ULEB}, // directories count{0, LineTable::Byte}, // file name entry format count{0, LineTable::ULEB} // file name entry count});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded());EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("parsing line table prologue at 0x00000000 found an invalid ""directory or file table description at 0x00000014","failed to parse entry content descriptions because no path was ""found"));}#ifdef _AIXTEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooLargePrologueLength) {#elseTEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {#endifif (!setupGenerator(Version))GTEST_SKIP();SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +(Format == DWARF64 ? "DWARF64" : "DWARF32"));LineTable < = Gen->addLineTable(Format);DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();++Prologue.PrologueLength;LT.setPrologue(Prologue);generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());DWARFDebugLine::LineTable Result(**ExpectedLineTable);// Undo the earlier modification so that it can be compared against a// "default" prologue.--Result.Prologue.PrologueLength;checkDefaultPrologue(Version, Format, Result.Prologue, 0);uint64_t ExpectedEnd =Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage(("unknown data in line table prologue at offset 0x00000000: ""parsing ended (at offset 0x000000" +Twine::utohexstr(ExpectedEnd - 1) +") before reaching the prologue end at offset 0x000000" +Twine::utohexstr(ExpectedEnd)).str()));}#ifdef _AIXTEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooShortPrologueLength) {#elseTEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {#endifif (!setupGenerator(Version))GTEST_SKIP();SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +(Format == DWARF64 ? "DWARF64" : "DWARF32"));LineTable < = Gen->addLineTable(Format);DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();Prologue.PrologueLength -= 2;LT.setPrologue(Prologue);generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());DWARFDebugLine::LineTable Result(**ExpectedLineTable);// Parsing will stop before reading a complete file entry.ASSERT_EQ(Result.Prologue.IncludeDirectories.size(), 1u);EXPECT_EQ(toStringRef(Result.Prologue.IncludeDirectories[0]), "a dir");EXPECT_EQ(Result.Prologue.FileNames.size(), 0u);// The exact place where the parsing will stop depends on the structure of the// prologue and the last complete field we are able to read. Before V5 we stop// before reading the file length. In V5, we stop before the filename.uint64_t ExpectedEnd = Prologue.TotalLength + Prologue.sizeofTotalLength() -(Version < 5 ? 2 : 8);std::vector<std::string> Errs;Errs.emplace_back((Twine("parsing line table prologue at 0x00000000 found an invalid ""directory or file table description at 0x000000") +Twine::utohexstr(ExpectedEnd)).str());if (Version < 5) {Errs.emplace_back("file names table was not null terminated before the end ""of the prologue");} else {Errs.emplace_back("failed to parse file entry because extracting the form value failed");}EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessageArray(testing::ElementsAreArray(Errs)));}INSTANTIATE_TEST_SUITE_P(LineTableTestParams, DebugLineParameterisedFixture,Values(std::make_pair(2, DWARF32), // Test lower-bound of v2-3 fields and DWARF32.std::make_pair(3, DWARF32), // Test upper-bound of v2-3 fields.std::make_pair(4, DWARF64), // Test v4 fields and DWARF64.std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)));#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ErrorForExtendedOpcodeLengthSmallerThanExpected) {#elseTEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.addByte(0xaa);// The Length should be 1 + sizeof(ULEB) for a set discriminator opcode.// The operand will be read for both the discriminator opcode and then parsed// again as DW_LNS_negate_stmt, to respect the claimed length.LT.addExtendedOpcode(1, DW_LNE_set_discriminator,{{DW_LNS_negate_stmt, LineTable::ULEB}});LT.addByte(0xbb);LT.addStandardOpcode(DW_LNS_const_add_pc, {});LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("unexpected line op length at offset ""0x00000031 expected 0x01 found 0x02"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);EXPECT_EQ((*ExpectedLineTable)->Rows[1].IsStmt, 0u);EXPECT_EQ((*ExpectedLineTable)->Rows[1].Discriminator, DW_LNS_negate_stmt);}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ErrorForExtendedOpcodeLengthLargerThanExpected) {#elseTEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.addByte(0xaa);LT.addStandardOpcode(DW_LNS_const_add_pc, {});// The Length should be 1 for an end sequence opcode.LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});// The negate statement opcode will be skipped.LT.addStandardOpcode(DW_LNS_negate_stmt, {});LT.addByte(0xbb);LT.addStandardOpcode(DW_LNS_const_add_pc, {});LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("unexpected line op length at offset ""0x00000032 expected 0x02 found 0x01"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 4u);EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 2u);ASSERT_EQ((*ExpectedLineTable)->Sequences[1].FirstRowIndex, 2u);EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ErrorForUnitLengthTooLarge) {#elseTEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) {#endifif (!setupGenerator())GTEST_SKIP();LineTable &Padding = Gen->addLineTable();// Add some padding to show that a non-zero offset is handled correctly.Padding.setCustomPrologue({{0, LineTable::Byte}});LineTable < = Gen->addLineTable();LT.addStandardOpcode(DW_LNS_copy, {});LT.addStandardOpcode(DW_LNS_const_add_pc, {});LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();// Set the total length to 1 higher than the actual length.++Prologue.TotalLength;LT.setPrologue(Prologue);generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 1, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("line table program with offset 0x00000001 has length ""0x00000034 but only 0x00000033 bytes are available"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 2u);EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ErrorForMismatchedAddressSize) {#elseTEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) {#endifif (!setupGenerator(4, 8))GTEST_SKIP();LineTable < = Gen->addLineTable();// The line data extractor expects size 8 (Quad) addresses.uint64_t Addr1 = 0x11223344;LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}});LT.addStandardOpcode(DW_LNS_copy, {});// Show that the expected address size is unchanged, so later valid lines// don't cause a problem.uint64_t Addr2 = 0x1122334455667788;LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}});LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("mismatching address size at offset ""0x00000030 expected 0x08 found 0x04"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u);EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1);EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2);}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ErrorForMismatchedAddressSizeUnsetInitialAddress) {#elseTEST_F(DebugLineBasicFixture,ErrorForMismatchedAddressSizeUnsetInitialAddress) {#endifif (!setupGenerator(4, 0))GTEST_SKIP();LineTable < = Gen->addLineTable();uint64_t Addr1 = 0x11223344;LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}});LT.addStandardOpcode(DW_LNS_copy, {});uint64_t Addr2 = 0x1122334455667788;LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}});LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("mismatching address size at offset ""0x00000038 expected 0x04 found 0x08"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u);EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1);EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2);}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ErrorForUnsupportedAddressSizeInSetAddressLength) {#elseTEST_F(DebugLineBasicFixture,ErrorForUnsupportedAddressSizeInSetAddressLength) {#endif// Use DWARF v4, and 0 for data extractor address size so that the address// size is derived from the opcode length.if (!setupGenerator(4, 0))GTEST_SKIP();LineTable < = Gen->addLineTable();// 4 == length of the extended opcode, i.e. 1 for the opcode itself and 3 for// the Half (2) + Byte (1) operand, representing the unsupported address size.LT.addExtendedOpcode(4, DW_LNE_set_address,{{0x1234, LineTable::Half}, {0x56, LineTable::Byte}});LT.addStandardOpcode(DW_LNS_copy, {});// Special opcode to ensure the address has changed between the first and last// row in the sequence. Without this, the sequence will not be recorded.LT.addByte(0xaa);LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("address size 0x03 of DW_LNE_set_address opcode at ""offset 0x00000030 is unsupported"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);// Show that the set address opcode is ignored in this case.EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ErrorForAddressSizeGreaterThanByteSize) {#elseTEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) {#endif// Use DWARF v4, and 0 for data extractor address size so that the address// size is derived from the opcode length.if (!setupGenerator(4, 0))GTEST_SKIP();LineTable < = Gen->addLineTable();// Specifically use an operand size that has a trailing byte of a supported// size (8), so that any potential truncation would result in a valid size.std::vector<LineTable::ValueAndLength> Operands(0x108);LT.addExtendedOpcode(Operands.size() + 1, DW_LNE_set_address, Operands);LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("address size 0x108 of DW_LNE_set_address opcode at ""offset 0x00000031 is unsupported"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ErrorForUnsupportedAddressSizeDefinedInHeader) {#elseTEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeDefinedInHeader) {#endif// Use 0 for data extractor address size so that it does not clash with the// header address size.if (!setupGenerator(5, 0))GTEST_SKIP();LineTable < = Gen->addLineTable();// AddressSize + 1 == length of the extended opcode, i.e. 1 for the opcode// itself and 9 for the Quad (8) + Byte (1) operand representing the// unsupported address size.uint8_t AddressSize = 9;LT.addExtendedOpcode(AddressSize + 1, DW_LNE_set_address,{{0x12345678, LineTable::Quad}, {0, LineTable::Byte}});LT.addStandardOpcode(DW_LNS_copy, {});// Special opcode to ensure the address has changed between the first and last// row in the sequence. Without this, the sequence will not be recorded.LT.addByte(0xaa);LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();Prologue.FormParams.AddrSize = AddressSize;LT.setPrologue(Prologue);generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("address size 0x09 of DW_LNE_set_address opcode at ""offset 0x00000038 is unsupported"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);// Show that the set address opcode is ignored in this case.EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_CallbackUsedForUnterminatedSequence) {#elseTEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.addExtendedOpcode(9, DW_LNE_set_address,{{0x1122334455667788, LineTable::Quad}});LT.addStandardOpcode(DW_LNS_copy, {});LT.addByte(0xaa);LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});LT.addExtendedOpcode(9, DW_LNE_set_address,{{0x99aabbccddeeff00, LineTable::Quad}});LT.addStandardOpcode(DW_LNS_copy, {});LT.addByte(0xbb);LT.addByte(0xcc);generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("last sequence in debug line table at ""offset 0x00000000 is not terminated"));ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6u);// The unterminated sequence is not added to the sequence list.EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);}struct AdjustAddressFixtureBase : public CommonFixture {virtual ~AdjustAddressFixtureBase() {}// Create and update the prologue as specified by the subclass, then return// the length of the table.virtual uint64_t editPrologue(LineTable <) = 0;virtual uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncrs,uint64_t SpecialIncrs,uint64_t AdvanceIncrs) {return Base + ConstIncrs + SpecialIncrs + AdvanceIncrs;}virtual uint64_t getAdjustedLine(uint64_t Base, uint64_t Incr) {return Base + Incr;}uint64_t setupNoProblemTable() {LineTable &NoProblem = Gen->addLineTable();NoProblem.addExtendedOpcode(9, DW_LNE_set_address,{{0xabcd, LineTable::Quad}});NoProblem.addExtendedOpcode(1, DW_LNE_end_sequence, {});return editPrologue(NoProblem);}uint64_t setupConstAddPcFirstTable() {LineTable &ConstAddPCFirst = Gen->addLineTable();ConstAddPCFirst.addExtendedOpcode(9, DW_LNE_set_address,{{ConstAddPCAddr, LineTable::Quad}});ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});ConstAddPCFirst.addStandardOpcode(DW_LNS_advance_pc,{{0x10, LineTable::ULEB}});ConstAddPCFirst.addByte(0x21); // Special opcode, +1 op, +1 line.ConstAddPCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});return editPrologue(ConstAddPCFirst);}uint64_t setupSpecialFirstTable() {LineTable &SpecialFirst = Gen->addLineTable();SpecialFirst.addExtendedOpcode(9, DW_LNE_set_address,{{SpecialAddr, LineTable::Quad}});SpecialFirst.addByte(0x22); // Special opcode, +1 op, +2 line.SpecialFirst.addStandardOpcode(DW_LNS_const_add_pc, {});SpecialFirst.addStandardOpcode(DW_LNS_advance_pc,{{0x20, LineTable::ULEB}});SpecialFirst.addByte(0x23); // Special opcode, +1 op, +3 line.SpecialFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});return editPrologue(SpecialFirst);}uint64_t setupAdvancePcFirstTable() {LineTable &AdvancePCFirst = Gen->addLineTable();AdvancePCFirst.addExtendedOpcode(9, DW_LNE_set_address,{{AdvancePCAddr, LineTable::Quad}});AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc,{{0x30, LineTable::ULEB}});AdvancePCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc,{{0x40, LineTable::ULEB}});AdvancePCFirst.addByte(0x24); // Special opcode, +1 op, +4 line.AdvancePCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});return editPrologue(AdvancePCFirst);}void setupTables(bool AddAdvancePCFirstTable) {LineTable &Padding = Gen->addLineTable();Padding.setCustomPrologue({{0, LineTable::Byte}});NoProblemOffset = 1;// Show that no warning is generated for the case where no// DW_LNS_const_add_pc or special opcode is used.ConstAddPCOffset = setupNoProblemTable() + NoProblemOffset;// Show that the warning is emitted for the first DW_LNS_const_add_pc opcode// and then not again.SpecialOffset = setupConstAddPcFirstTable() + ConstAddPCOffset;// Show that the warning is emitted for the first special opcode and then// not again.AdvancePCOffset = setupSpecialFirstTable() + SpecialOffset;// Show that the warning is emitted for the first DW_LNS_advance_pc opcode// (if requested) and then not again.if (AddAdvancePCFirstTable)setupAdvancePcFirstTable();}Expected<const DWARFDebugLine::LineTable *>checkTable(uint64_t Offset, StringRef OpcodeType, const Twine &MsgSuffix) {auto ExpectedTable = Line.getOrParseLineTable(LineData, Offset, *Context,nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());if (!IsErrorExpected) {EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());} else {if (!ExpectedTable)return ExpectedTable;uint64_t ExpectedOffset = Offset +(*ExpectedTable)->Prologue.getLength() +11; // 11 == size of DW_LNE_set_address.std::string OffsetHex = Twine::utohexstr(Offset).str();std::string OffsetZeroes = std::string(8 - OffsetHex.size(), '0');std::string ExpectedHex = Twine::utohexstr(ExpectedOffset).str();std::string ExpectedZeroes = std::string(8 - ExpectedHex.size(), '0');EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage(("line table program at offset 0x" + OffsetZeroes +OffsetHex + " contains a " + OpcodeType +" opcode at offset 0x" + ExpectedZeroes +ExpectedHex + ", " + MsgSuffix).str()));}return ExpectedTable;}void runTest(bool CheckAdvancePC, Twine MsgSuffix) {if (!setupGenerator(Version))GTEST_SKIP();setupTables(/*AddAdvancePCFirstTable=*/CheckAdvancePC);generate();auto ExpectedNoProblem = Line.getOrParseLineTable(LineData, NoProblemOffset, *Context, nullptr, RecordRecoverable);EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());ASSERT_THAT_EXPECTED(ExpectedNoProblem, Succeeded());auto ExpectedConstAddPC =checkTable(ConstAddPCOffset, "DW_LNS_const_add_pc", MsgSuffix);ASSERT_THAT_EXPECTED(ExpectedConstAddPC, Succeeded());ASSERT_EQ((*ExpectedConstAddPC)->Rows.size(), 2u);EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Address.Address,getAdjustedAddr(ConstAddPCAddr, ConstIncr * 2, 0x1, 0x10));EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Line, getAdjustedLine(1, 1));EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());auto ExpectedSpecial = checkTable(SpecialOffset, "special", MsgSuffix);ASSERT_THAT_EXPECTED(ExpectedSpecial, Succeeded());ASSERT_EQ((*ExpectedSpecial)->Rows.size(), 3u);EXPECT_EQ((*ExpectedSpecial)->Rows[0].Address.Address,getAdjustedAddr(SpecialAddr, 0, 1, 0));EXPECT_EQ((*ExpectedSpecial)->Rows[0].Line, getAdjustedLine(1, 2));EXPECT_EQ((*ExpectedSpecial)->Rows[1].Address.Address,getAdjustedAddr(SpecialAddr, ConstIncr, 0x2, 0x20));EXPECT_EQ((*ExpectedSpecial)->Rows[1].Line, getAdjustedLine(1, 5));EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());if (!CheckAdvancePC)return;auto ExpectedAdvancePC =checkTable(AdvancePCOffset, "DW_LNS_advance_pc", MsgSuffix);ASSERT_THAT_EXPECTED(ExpectedAdvancePC, Succeeded());ASSERT_EQ((*ExpectedAdvancePC)->Rows.size(), 2u);EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Address.Address,getAdjustedAddr(AdvancePCAddr, ConstIncr, 0x1, 0x70));EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Line, getAdjustedLine(1, 4));}uint64_t ConstIncr = 0x11;uint64_t ConstAddPCAddr = 0x1234;uint64_t SpecialAddr = 0x5678;uint64_t AdvancePCAddr = 0xabcd;uint64_t NoProblemOffset;uint64_t ConstAddPCOffset;uint64_t SpecialOffset;uint64_t AdvancePCOffset;uint16_t Version = 4;bool IsErrorExpected;};struct MaxOpsPerInstFixture: TestWithParam<std::tuple<uint16_t, uint8_t, bool>>,AdjustAddressFixtureBase {void SetUp() override {std::tie(Version, MaxOpsPerInst, IsErrorExpected) = GetParam();}uint64_t editPrologue(LineTable <) override {DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();Prologue.MaxOpsPerInst = MaxOpsPerInst;LT.setPrologue(Prologue);return Prologue.TotalLength + Prologue.sizeofTotalLength();}uint8_t MaxOpsPerInst;};#ifdef _AIXTEST_P(MaxOpsPerInstFixture, DISABLED_MaxOpsPerInstProblemsReportedCorrectly) {#elseTEST_P(MaxOpsPerInstFixture, MaxOpsPerInstProblemsReportedCorrectly) {#endifrunTest(/*CheckAdvancePC=*/true,"but the prologue maximum_operations_per_instruction value is " +Twine(unsigned(MaxOpsPerInst)) +", which is unsupported. Assuming a value of 1 instead");}INSTANTIATE_TEST_SUITE_P(MaxOpsPerInstParams, MaxOpsPerInstFixture,Values(std::make_tuple(3, 0, false), // Test for version < 4 (no error).std::make_tuple(4, 0, true), // Test zero value for V4 (error).std::make_tuple(4, 1, false), // Test good value for V4 (no error).std::make_tuple(4, 2, true))); // Test one higher than permitted V4 (error).struct LineRangeFixture : TestWithParam<std::tuple<uint8_t, bool>>,AdjustAddressFixtureBase {void SetUp() override { std::tie(LineRange, IsErrorExpected) = GetParam(); }uint64_t editPrologue(LineTable <) override {DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();Prologue.LineRange = LineRange;LT.setPrologue(Prologue);return Prologue.TotalLength + Prologue.sizeofTotalLength();}uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncr,uint64_t SpecialIncr,uint64_t AdvanceIncr) override {if (LineRange == 0)return Base + AdvanceIncr;return AdjustAddressFixtureBase::getAdjustedAddr(Base, ConstIncr,SpecialIncr, AdvanceIncr);}uint64_t getAdjustedLine(uint64_t Base, uint64_t Incr) override {return LineRange != 0? AdjustAddressFixtureBase::getAdjustedLine(Base, Incr): Base;}uint8_t LineRange;};#ifdef _AIXTEST_P(LineRangeFixture, DISABLED_LineRangeProblemsReportedCorrectly) {#elseTEST_P(LineRangeFixture, LineRangeProblemsReportedCorrectly) {#endifrunTest(/*CheckAdvancePC=*/false,"but the prologue line_range value is 0. The address and line will ""not be adjusted");}INSTANTIATE_TEST_SUITE_P(LineRangeParams, LineRangeFixture,Values(std::make_tuple(0, true), // Test zero value (error).std::make_tuple(14, false))); // Test non-zero value (no error).struct BadMinInstLenFixture : TestWithParam<std::tuple<uint8_t, bool>>,AdjustAddressFixtureBase {void SetUp() override {std::tie(MinInstLength, IsErrorExpected) = GetParam();}uint64_t editPrologue(LineTable <) override {DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();Prologue.MinInstLength = MinInstLength;LT.setPrologue(Prologue);return Prologue.TotalLength + Prologue.sizeofTotalLength();}uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncr,uint64_t SpecialIncr,uint64_t AdvanceIncr) override {return MinInstLength != 0 ? AdjustAddressFixtureBase::getAdjustedAddr(Base, ConstIncr, SpecialIncr, AdvanceIncr): Base;}uint8_t MinInstLength;};#ifdef _AIXTEST_P(BadMinInstLenFixture, DISABLED_MinInstLengthProblemsReportedCorrectly) {#elseTEST_P(BadMinInstLenFixture, MinInstLengthProblemsReportedCorrectly) {#endifrunTest(/*CheckAdvancePC=*/true,"but the prologue minimum_instruction_length value is 0, which ""prevents any address advancing");}INSTANTIATE_TEST_SUITE_P(BadMinInstLenParams, BadMinInstLenFixture,Values(std::make_tuple(0, true), // Test zero value (error).std::make_tuple(1, false))); // Test non-zero value (no error).#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ParserParsesCorrectly) {#elseTEST_F(DebugLineBasicFixture, ParserParsesCorrectly) {#endifif (!setupGenerator())GTEST_SKIP();DWARFDebugLine::SectionParser Parser = setupParser();EXPECT_EQ(Parser.getOffset(), 0u);ASSERT_FALSE(Parser.done());DWARFDebugLine::LineTable Parsed =Parser.parseNext(RecordRecoverable, RecordUnrecoverable);checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16);EXPECT_EQ(Parsed.Sequences.size(), 1u);EXPECT_EQ(Parser.getOffset(), 62u);ASSERT_FALSE(Parser.done());DWARFDebugLine::LineTable Parsed2 =Parser.parseNext(RecordRecoverable, RecordUnrecoverable);checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16);EXPECT_EQ(Parsed2.Sequences.size(), 1u);EXPECT_EQ(Parser.getOffset(), 136u);EXPECT_TRUE(Parser.done());EXPECT_FALSE(Recoverable);EXPECT_FALSE(Unrecoverable);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ParserSkipsCorrectly) {#elseTEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {#endifif (!setupGenerator())GTEST_SKIP();DWARFDebugLine::SectionParser Parser = setupParser();EXPECT_EQ(Parser.getOffset(), 0u);ASSERT_FALSE(Parser.done());Parser.skip(RecordRecoverable, RecordUnrecoverable);EXPECT_EQ(Parser.getOffset(), 62u);ASSERT_FALSE(Parser.done());Parser.skip(RecordRecoverable, RecordUnrecoverable);EXPECT_EQ(Parser.getOffset(), 136u);EXPECT_TRUE(Parser.done());EXPECT_FALSE(Recoverable);EXPECT_FALSE(Unrecoverable);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_ParserAlwaysDoneForEmptySection) {#elseTEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) {#endifif (!setupGenerator())GTEST_SKIP();generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);EXPECT_TRUE(Parser.done());}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ParserMarkedAsDoneForBadLengthWhenParsing) {#elseTEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});Gen->addLineTable();generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);Parser.parseNext(RecordRecoverable, RecordUnrecoverable);EXPECT_EQ(Parser.getOffset(), 0u);EXPECT_TRUE(Parser.done());EXPECT_FALSE(Recoverable);EXPECT_THAT_ERROR(std::move(Unrecoverable),FailedWithMessage("parsing line table prologue at offset 0x00000000: unsupported ""reserved unit length of value 0xfffffff0"));}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ParserMarkedAsDoneForBadLengthWhenSkipping) {#elseTEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable();LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});Gen->addLineTable();generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);Parser.skip(RecordRecoverable, RecordUnrecoverable);EXPECT_EQ(Parser.getOffset(), 0u);EXPECT_TRUE(Parser.done());EXPECT_FALSE(Recoverable);EXPECT_THAT_ERROR(std::move(Unrecoverable),FailedWithMessage("parsing line table prologue at offset 0x00000000: unsupported ""reserved unit length of value 0xfffffff0"));}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ParserReportsFirstErrorInEachTableWhenParsing) {#elseTEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable(DWARF32);LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});LineTable <2 = Gen->addLineTable(DWARF32);LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);Parser.parseNext(RecordRecoverable, RecordUnrecoverable);ASSERT_FALSE(Parser.done());Parser.parseNext(RecordRecoverable, RecordUnrecoverable);EXPECT_TRUE(Parser.done());EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());EXPECT_THAT_ERROR(std::move(Unrecoverable),FailedWithMessage("parsing line table prologue at offset 0x00000000: ""unsupported version 0","parsing line table prologue at offset 0x00000006: ""unsupported version 1"));}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ParserReportsNonPrologueProblemsWhenParsing) {#elseTEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable(DWARF32);LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {});LineTable <2 = Gen->addLineTable(DWARF32);LT2.addExtendedOpcode(9, DW_LNE_set_address,{{0x1234567890abcdef, LineTable::Quad}});LT2.addStandardOpcode(DW_LNS_copy, {});LT2.addByte(0xbb);generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);Parser.parseNext(RecordRecoverable, RecordUnrecoverable);EXPECT_FALSE(Unrecoverable);ASSERT_FALSE(Parser.done());EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("unexpected line op length at offset ""0x00000030 expected 0x42 found 0x01"));// Reset the error state so that it does not confuse the next set of checks.Unrecoverable = Error::success();Parser.parseNext(RecordRecoverable, RecordUnrecoverable);EXPECT_TRUE(Parser.done());EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage("last sequence in debug line table at ""offset 0x00000031 is not terminated"));EXPECT_FALSE(Unrecoverable);}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ParserReportsPrologueErrorsInEachTableWhenSkipping) {#elseTEST_F(DebugLineBasicFixture,ParserReportsPrologueErrorsInEachTableWhenSkipping) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable(DWARF32);LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});LineTable <2 = Gen->addLineTable(DWARF32);LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);Parser.skip(RecordRecoverable, RecordUnrecoverable);ASSERT_FALSE(Parser.done());Parser.skip(RecordRecoverable, RecordUnrecoverable);EXPECT_TRUE(Parser.done());EXPECT_FALSE(Recoverable);EXPECT_THAT_ERROR(std::move(Unrecoverable),FailedWithMessage("parsing line table prologue at offset 0x00000000: ""unsupported version 0","parsing line table prologue at offset 0x00000006: ""unsupported version 1"));}#ifdef _AIXTEST_F(DebugLineBasicFixture,DISABLED_ParserIgnoresNonPrologueErrorsWhenSkipping) {#elseTEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = Gen->addLineTable(DWARF32);LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);Parser.skip(RecordRecoverable, RecordUnrecoverable);EXPECT_TRUE(Parser.done());EXPECT_FALSE(Recoverable);EXPECT_FALSE(Unrecoverable);}#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_VerboseOutput) {#elseTEST_F(DebugLineBasicFixture, VerboseOutput) {#endifif (!setupGenerator(5))GTEST_SKIP();LineTable < = Gen->addLineTable();LT.addByte(0); // Extended opcode with zero length.LT.addByte(0);// Zero-value extended opcode.LT.addExtendedOpcode(2, 0, {{1, LineTable::Byte}});// Unknown extended opcode.LT.addExtendedOpcode(2, 0x42, {{1, LineTable::Byte}});LT.addExtendedOpcode(9, DW_LNE_set_address,{{0x123456789abcdef, LineTable::Quad}});LT.addExtendedOpcode(6, DW_LNE_define_file,{{'a', LineTable::Byte},{'\0', LineTable::Byte},{2, LineTable::ULEB},{3, LineTable::ULEB},{4, LineTable::ULEB}});LT.addExtendedOpcode(2, DW_LNE_set_discriminator, {{0x7f, LineTable::ULEB}});LT.addStandardOpcode(DW_LNS_copy, {});LT.addStandardOpcode(DW_LNS_advance_pc, {{11, LineTable::ULEB}});LT.addStandardOpcode(DW_LNS_advance_line, {{22, LineTable::SLEB}});LT.addStandardOpcode(DW_LNS_set_file, {{33, LineTable::ULEB}});LT.addStandardOpcode(DW_LNS_set_column, {{44, LineTable::ULEB}});LT.addStandardOpcode(DW_LNS_negate_stmt, {});LT.addStandardOpcode(DW_LNS_set_basic_block, {});LT.addStandardOpcode(DW_LNS_const_add_pc, {});LT.addStandardOpcode(DW_LNS_fixed_advance_pc, {{55, LineTable::Half}});LT.addStandardOpcode(DW_LNS_set_prologue_end, {});LT.addStandardOpcode(DW_LNS_set_epilogue_begin, {});LT.addStandardOpcode(DW_LNS_set_isa, {{66, LineTable::ULEB}});// Add unknown standard opcode with operands.LT.addStandardOpcode(0xd, {{1, LineTable::ULEB}, {0x123456789abcdef, LineTable::ULEB}});// Add unknown standard opcode without operands.LT.addStandardOpcode(0xe, {});LT.addByte(0xff); // Special opcode.LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});// Adjust the prologue to account for the extra standard opcode.DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();Prologue.TotalLength += 2;Prologue.PrologueLength += 2;Prologue.OpcodeBase += 2;Prologue.StandardOpcodeLengths.push_back(2);Prologue.StandardOpcodeLengths.push_back(0);LT.setPrologue(Prologue);generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);std::string Output;raw_string_ostream OS(Output);Parser.parseNext(RecordRecoverable, RecordUnrecoverable, &OS,/*Verbose=*/true);OS.flush();StringRef OutputRef(Output);size_t Pos = 0;auto NextLine = [&Pos, &OutputRef]() {size_t EOL = OutputRef.find_first_of('\n', Pos);StringRef Line = OutputRef.substr(Pos, EOL - Pos);Pos = EOL + 1;return Line;};EXPECT_EQ(NextLine(), "Line table prologue:");EXPECT_EQ(NextLine(), " total_length: 0x00000078");EXPECT_EQ(NextLine(), " format: DWARF32");EXPECT_EQ(NextLine(), " version: 5");EXPECT_EQ(NextLine(), " address_size: 8");EXPECT_EQ(NextLine(), " seg_select_size: 0");EXPECT_EQ(NextLine(), " prologue_length: 0x0000002c");EXPECT_EQ(NextLine(), " min_inst_length: 1");EXPECT_EQ(NextLine(), "max_ops_per_inst: 1");EXPECT_EQ(NextLine(), " default_is_stmt: 1");EXPECT_EQ(NextLine(), " line_base: -5");EXPECT_EQ(NextLine(), " line_range: 14");EXPECT_EQ(NextLine(), " opcode_base: 15");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_copy] = 0");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_advance_pc] = 1");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_advance_line] = 1");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_file] = 1");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_column] = 1");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_negate_stmt] = 0");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_basic_block] = 0");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_const_add_pc] = 0");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_prologue_end] = 0");EXPECT_EQ(NextLine(),"standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_isa] = 1");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_unknown_d] = 2");EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_unknown_e] = 0");EXPECT_EQ(NextLine(), "include_directories[ 0] = \"a dir\"");EXPECT_EQ(NextLine(), "file_names[ 0]:");EXPECT_EQ(NextLine(), " name: \"a file\"");EXPECT_EQ(NextLine(), " dir_index: 0");EXPECT_EQ(NextLine(), "");EXPECT_EQ(NextLine(), " Address Line Column File ISA Discriminator Flags");EXPECT_EQ(NextLine(), " ------------------ ------ ------ ------ --- ------------- -------------");EXPECT_EQ(NextLine(),"0x00000038: 00 Badly formed extended line op (length 0)");EXPECT_EQ(NextLine(),"0x0000003a: 00 Unrecognized extended op 0x00 length 2");EXPECT_EQ(NextLine(),"0x0000003e: 00 Unrecognized extended op 0x42 length 2");EXPECT_EQ(NextLine(),"0x00000042: 00 DW_LNE_set_address (0x0123456789abcdef)");EXPECT_EQ(NextLine(), "0x0000004d: 00 DW_LNE_define_file (a, dir=2, ""mod_time=(0x0000000000000003), length=4)");EXPECT_EQ(NextLine(), "0x00000055: 00 DW_LNE_set_discriminator (127)");EXPECT_EQ(NextLine(), "0x00000059: 01 DW_LNS_copy");EXPECT_EQ(NextLine(), " 0x0123456789abcdef 1 0 1 ""0 127 is_stmt");EXPECT_EQ(NextLine(), "0x0000005a: 02 DW_LNS_advance_pc (11)");EXPECT_EQ(NextLine(), "0x0000005c: 03 DW_LNS_advance_line (23)");EXPECT_EQ(NextLine(), "0x0000005e: 04 DW_LNS_set_file (33)");EXPECT_EQ(NextLine(), "0x00000060: 05 DW_LNS_set_column (44)");EXPECT_EQ(NextLine(), "0x00000062: 06 DW_LNS_negate_stmt");EXPECT_EQ(NextLine(), "0x00000063: 07 DW_LNS_set_basic_block");EXPECT_EQ(NextLine(),"0x00000064: 08 DW_LNS_const_add_pc (0x0000000000000011)");EXPECT_EQ(NextLine(), "0x00000065: 09 DW_LNS_fixed_advance_pc (0x0037)");EXPECT_EQ(NextLine(), "0x00000068: 0a DW_LNS_set_prologue_end");EXPECT_EQ(NextLine(), "0x00000069: 0b DW_LNS_set_epilogue_begin");EXPECT_EQ(NextLine(), "0x0000006a: 0c DW_LNS_set_isa (66)");EXPECT_EQ(NextLine(), "0x0000006c: 0d Unrecognized standard opcode ""(operands: 0x0000000000000001, 0x0123456789abcdef)");EXPECT_EQ(NextLine(), "0x00000077: 0e Unrecognized standard opcode");EXPECT_EQ(NextLine(), "0x00000078: ff address += 17, line += -3");EXPECT_EQ(NextLine()," 0x0123456789abce53 20 44 33 66 "" 0 basic_block prologue_end epilogue_begin");EXPECT_EQ(NextLine(), "0x00000079: 00 DW_LNE_end_sequence");EXPECT_EQ(NextLine(), " 0x0123456789abce53 20 44 33 ""66 0 end_sequence");EXPECT_EQ(NextLine(), "");EXPECT_EQ(Output.size(), Pos);}struct TruncatedPrologueFixture: public TestWithParam<std::tuple<uint64_t, uint64_t, uint16_t, DwarfFormat, StringRef>>,public CommonFixture {void SetUp() override {std::tie(Length, ExpectedOffset, Version, Format, ExpectedErr) = GetParam();}uint64_t Length;uint64_t ExpectedOffset;uint16_t Version;DwarfFormat Format;StringRef ExpectedErr;};#ifdef _AIXTEST_P(TruncatedPrologueFixture, DISABLED_ErrorForTruncatedPrologue) {#elseTEST_P(TruncatedPrologueFixture, ErrorForTruncatedPrologue) {#endifif (!setupGenerator(Version))GTEST_SKIP();LineTable &Padding = Gen->addLineTable();// Add some padding to show that a non-zero offset is handled correctly.Padding.setCustomPrologue({{0, LineTable::Byte}});// Add a table with only two standard opcodes - we don't need to test the full// set.LineTable &Table = Gen->addLineTable(Format);DWARFDebugLine::Prologue InputPrologue = Table.createBasicPrologue();InputPrologue.OpcodeBase = 3;InputPrologue.StandardOpcodeLengths.resize(2);Table.setPrologue(InputPrologue);generate();// Truncate the data extractor to the specified length.LineData = DWARFDataExtractor(LineData, Length);DWARFDebugLine::Prologue Prologue;uint64_t Offset = 1;Error Err = Prologue.parse(LineData, &Offset, RecordRecoverable, *Context);EXPECT_THAT_ERROR(std::move(Err), FailedWithMessage(ExpectedErr.str()));EXPECT_EQ(Offset, ExpectedOffset);}INSTANTIATE_TEST_SUITE_P(TruncatedPrologueParams, TruncatedPrologueFixture,Values(// Truncated length:std::make_tuple(4, 1, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x4 while reading [0x1, 0x5)"),std::make_tuple(4, 1, 4, DWARF64,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x4 while reading [0x1, 0x5)"),std::make_tuple(0xc, 1, 4, DWARF64,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0xc while reading [0x5, 0xd)"),// Truncated version:std::make_tuple(6, 5, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x6 while reading [0x5, 0x7)"),// Truncated address size:std::make_tuple(7, 7, 5, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x7 while reading [0x7, 0x8)"),// Truncated segment selector size:std::make_tuple(8, 8, 5, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x8 while reading [0x8, 0x9)"),// Truncated prologue length:std::make_tuple(0xa, 7, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0xa while reading [0x7, 0xb)"),std::make_tuple(0x16, 0xf, 4, DWARF64,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x16 while reading [0xf, 0x17)"),// Truncated min instruction length:std::make_tuple(0xb, 0xb, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0xb while reading [0xb, 0xc)"),// Truncated max ops per inst:std::make_tuple(0xc, 0xc, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0xc while reading [0xc, 0xd)"),// Truncated default is stmt:std::make_tuple(0xd, 0xd, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0xd while reading [0xd, 0xe)"),// Truncated line base:std::make_tuple(0xe, 0xe, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0xe while reading [0xe, 0xf)"),// Truncated line range:std::make_tuple(0xf, 0xf, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0xf while reading [0xf, 0x10)"),// Truncated opcode base:std::make_tuple(0x10, 0x10, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x10 while reading [0x10, 0x11)"),// Truncated first standard opcode:std::make_tuple(0x11, 0x11, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x11 while reading [0x11, 0x12)"),// Truncated second standard opcode:std::make_tuple(0x12, 0x12, 4, DWARF32,"parsing line table prologue at offset 0x00000001: unexpected end ""of data at offset 0x12 while reading [0x12, 0x13)")));using ValueAndLengths = std::vector<LineTable::ValueAndLength>;struct TruncatedOpcodeFixtureBase : public CommonFixture {LineTable &setupTable() {LineTable < = Gen->addLineTable();// Creating the prologue before adding any opcodes ensures that the unit// length does not include the table body.DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();// Add an unrecognised standard opcode, and adjust prologue properties// accordingly.Prologue.TotalLength += BodyLength + 1;++Prologue.PrologueLength;++Prologue.OpcodeBase;Prologue.StandardOpcodeLengths.push_back(2);LT.setPrologue(Prologue);return LT;}void runTest(uint8_t OpcodeValue) {generate();DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);std::string Output;raw_string_ostream OS(Output);Parser.parseNext(RecordRecoverable, RecordUnrecoverable, &OS,/*Verbose=*/true);OS.flush();std::string LinePrefix =("0x0000002f: 0" + Twine::utohexstr(OpcodeValue) + " ").str();StringRef OutputRef(Output);StringRef OutputToCheck = OutputRef.split(LinePrefix).second;// Each extended opcode ends with a new line and then the table ends with an// additional blank line.EXPECT_EQ((ExpectedOutput + "\n\n").str(), OutputToCheck);}uint64_t BodyLength;uint8_t Opcode;ValueAndLengths Operands;StringRef ExpectedOutput;StringRef ExpectedErr;};struct TruncatedStandardOpcodeFixture: public TestWithParam<std::tuple<uint64_t, uint8_t, ValueAndLengths, StringRef, StringRef>>,public TruncatedOpcodeFixtureBase {void SetUp() override {std::tie(BodyLength, Opcode, Operands, ExpectedOutput, ExpectedErr) =GetParam();}};struct TruncatedExtendedOpcodeFixture: public TestWithParam<std::tuple<uint64_t, uint64_t, uint8_t,ValueAndLengths, StringRef, StringRef>>,public TruncatedOpcodeFixtureBase {void SetUp() override {std::tie(BodyLength, OpcodeLength, Opcode, Operands, ExpectedOutput,ExpectedErr) = GetParam();}uint64_t OpcodeLength;};#ifdef _AIXTEST_P(TruncatedExtendedOpcodeFixture,DISABLED_ErrorForTruncatedExtendedOpcode) {#elseTEST_P(TruncatedExtendedOpcodeFixture, ErrorForTruncatedExtendedOpcode) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = setupTable();LT.addExtendedOpcode(OpcodeLength, Opcode, Operands);runTest(0);EXPECT_THAT_ERROR(std::move(Recoverable),FailedWithMessage(ExpectedErr.str()));}INSTANTIATE_TEST_SUITE_P(TruncatedExtendedOpcodeParams, TruncatedExtendedOpcodeFixture,Values(// Truncated length:std::make_tuple(1, 1, /*ArbitraryOpcode=*/0x7f, ValueAndLengths(), "","unable to decode LEB128 at offset 0x00000030: ""malformed uleb128, extends past end"),// Truncated opcode:std::make_tuple(2, 9, /*ArbitraryOpcode=*/0x7f, ValueAndLengths(), "","unexpected end of data at offset 0x31 while reading [0x31, 0x32)"),// Truncated operands:std::make_tuple(3, 9, DW_LNE_set_address,ValueAndLengths{{0x1234567890abcdef, LineTable::Quad}},"DW_LNE_set_address","unexpected end of data at offset 0x32 while reading [0x32, 0x3a)"),std::make_tuple(10, 9, DW_LNE_set_address,ValueAndLengths{{0x1234567878563412, LineTable::Quad}},"DW_LNE_set_address (<parsing error> 12 34 56 78 78 56 34)","unexpected end of data at offset 0x39 while reading [0x32, 0x3a)"),std::make_tuple(3, 6, DW_LNE_define_file,ValueAndLengths{{'a', LineTable::Byte},{'\0', LineTable::Byte},{1, LineTable::ULEB},{1, LineTable::ULEB},{1, LineTable::ULEB}},"DW_LNE_define_file","no null terminated string at offset 0x32"),std::make_tuple(5, 6, DW_LNE_define_file,ValueAndLengths{{'a', LineTable::Byte},{'\0', LineTable::Byte},{1, LineTable::ULEB},{1, LineTable::ULEB},{1, LineTable::ULEB}},"DW_LNE_define_file (<parsing error> 61 00)","unable to decode LEB128 at offset 0x00000034: ""malformed uleb128, extends past end"),std::make_tuple(6, 6, DW_LNE_define_file,ValueAndLengths{{'a', LineTable::Byte},{'\0', LineTable::Byte},{1, LineTable::ULEB},{1, LineTable::ULEB},{1, LineTable::ULEB}},"DW_LNE_define_file (<parsing error> 61 00 01)","unable to decode LEB128 at offset 0x00000035: ""malformed uleb128, extends past end"),std::make_tuple(7, 6, DW_LNE_define_file,ValueAndLengths{{'a', LineTable::Byte},{'\0', LineTable::Byte},{1, LineTable::ULEB},{1, LineTable::ULEB},{1, LineTable::ULEB}},"DW_LNE_define_file (<parsing error> 61 00 01 01)","unable to decode LEB128 at offset 0x00000036: ""malformed uleb128, extends past end"),std::make_tuple(3, 2, DW_LNE_set_discriminator,ValueAndLengths{{1, LineTable::ULEB}},"DW_LNE_set_discriminator","unable to decode LEB128 at offset 0x00000032: ""malformed uleb128, extends past end"),std::make_tuple(6, 5, /*Unknown=*/0x7f,ValueAndLengths{{0x12343412, LineTable::Long}},"Unrecognized extended op 0x7f length 5 (<parsing error> 12 34 34)","unexpected end of data at offset 0x35 while reading [0x32, ""0x36)")));#ifdef _AIXTEST_P(TruncatedStandardOpcodeFixture,DISABLED_ErrorForTruncatedStandardOpcode) {#elseTEST_P(TruncatedStandardOpcodeFixture, ErrorForTruncatedStandardOpcode) {#endifif (!setupGenerator())GTEST_SKIP();LineTable < = setupTable();LT.addStandardOpcode(Opcode, Operands);runTest(Opcode);EXPECT_THAT_ERROR(std::move(Unrecoverable),FailedWithMessage(ExpectedErr.str()));}INSTANTIATE_TEST_SUITE_P(TruncatedStandardOpcodeParams, TruncatedStandardOpcodeFixture,Values(std::make_tuple(2, DW_LNS_advance_pc,ValueAndLengths{{0x100, LineTable::ULEB}},"DW_LNS_advance_pc","unable to decode LEB128 at offset 0x00000030: ""malformed uleb128, extends past end"),std::make_tuple(2, DW_LNS_advance_line,ValueAndLengths{{0x200, LineTable::SLEB}},"DW_LNS_advance_line","unable to decode LEB128 at offset 0x00000030: ""malformed sleb128, extends past end"),std::make_tuple(2, DW_LNS_set_file,ValueAndLengths{{0x300, LineTable::ULEB}},"DW_LNS_set_file","unable to decode LEB128 at offset 0x00000030: ""malformed uleb128, extends past end"),std::make_tuple(2, DW_LNS_set_column,ValueAndLengths{{0x400, LineTable::ULEB}},"DW_LNS_set_column","unable to decode LEB128 at offset 0x00000030: ""malformed uleb128, extends past end"),std::make_tuple(2, DW_LNS_fixed_advance_pc,ValueAndLengths{{0x500, LineTable::Half}},"DW_LNS_fixed_advance_pc","unexpected end of data at offset 0x31 while reading [0x30, 0x32)"),std::make_tuple(2, DW_LNS_set_isa,ValueAndLengths{{0x600, LineTable::ULEB}},"DW_LNS_set_isa","unable to decode LEB128 at offset 0x00000030: ""malformed uleb128, extends past end"),std::make_tuple(2, 0xd,ValueAndLengths{{0x700, LineTable::ULEB},{0x800, LineTable::ULEB}},"Unrecognized standard opcode","unable to decode LEB128 at offset 0x00000030: ""malformed uleb128, extends past end"),std::make_tuple(4, 0xd,ValueAndLengths{{0x900, LineTable::ULEB}, {0xa00, LineTable::ULEB}},"Unrecognized standard opcode (operands: 0x0000000000000900)","unable to decode LEB128 at offset 0x00000032: ""malformed uleb128, extends past end")));#ifdef _AIXTEST_F(DebugLineBasicFixture, DISABLED_PrintPathsProperly) {#elseTEST_F(DebugLineBasicFixture, PrintPathsProperly) {#endifif (!setupGenerator(5))GTEST_SKIP();LineTable < = Gen->addLineTable();DWARFDebugLine::Prologue P = LT.createBasicPrologue();P.IncludeDirectories.push_back(DWARFFormValue::createFromPValue(DW_FORM_string, "b dir"));P.FileNames.push_back(DWARFDebugLine::FileNameEntry());P.FileNames.back().Name =DWARFFormValue::createFromPValue(DW_FORM_string, "b file");P.FileNames.back().DirIdx = 1;P.TotalLength += 14;P.PrologueLength += 14;LT.setPrologue(P);generate();auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,nullptr, RecordRecoverable);EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded());std::string Result;// DWARF 5 stores the compilation directory in two places: the Compilation// Unit and the directory table entry 0, and implementations are free to use// one or the other. This copy serves as the one stored in the CU.StringRef CompDir = "a dir";EXPECT_FALSE((*ExpectedLineTable)->Prologue.getFileNameByIndex(1, CompDir, DILineInfoSpecifier::FileLineInfoKind::None, Result));EXPECT_TRUE((*ExpectedLineTable)->Prologue.getFileNameByIndex(1, CompDir,DILineInfoSpecifier::FileLineInfoKind::RawValue, Result));EXPECT_TRUE((*ExpectedLineTable)->Prologue.getFileNameByIndex(1, CompDir,DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly,Result));EXPECT_STREQ(Result.c_str(), "b file");EXPECT_TRUE((*ExpectedLineTable)->Prologue.getFileNameByIndex(1, CompDir,DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath,Result));EXPECT_THAT(Result.c_str(), MatchesRegex("b dir.b file"));EXPECT_TRUE((*ExpectedLineTable)->Prologue.getFileNameByIndex(1, CompDir,DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,Result));EXPECT_THAT(Result.c_str(), MatchesRegex("a dir.b dir.b file"));}} // end anonymous namespace
//===- llvm/unittest/DebugInfo/DWARFDebugInfoTest.cpp ---------------------===////// 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////===----------------------------------------------------------------------===//#include "DwarfGenerator.h"#include "DwarfUtils.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/Optional.h"#include "llvm/ADT/SmallString.h"#include "llvm/ADT/StringRef.h"#include "llvm/ADT/Triple.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/CodeGen/AsmPrinter.h"#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"#include "llvm/DebugInfo/DWARF/DWARFDie.h"#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCSectionELF.h"#include "llvm/MC/MCStreamer.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Object/ObjectFile.h"#include "llvm/ObjectYAML/DWARFEmitter.h"#include "llvm/Support/Error.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"#include <string>using namespace llvm;using namespace dwarf;using namespace utils;using ::testing::HasSubstr;namespace {template <uint16_t Version, class AddrType, class RefAddrType>void TestAllForms() {Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType));if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test that we can decode all DW_FORM values correctly.const AddrType AddrValue = (AddrType)0x0123456789abcdefULL;const uint8_t BlockData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};const uint32_t BlockSize = sizeof(BlockData);const RefAddrType RefAddr = 0x12345678;const uint8_t Data1 = 0x01U;const uint16_t Data2 = 0x2345U;const uint32_t Data4 = 0x6789abcdU;const uint64_t Data8 = 0x0011223344556677ULL;const uint64_t Data8_2 = 0xAABBCCDDEEFF0011ULL;const uint8_t Data16[16] = {1, 2, 3, 4, 5, 6, 7, 8,9, 10, 11, 12, 13, 14, 15, 16};const int64_t SData = INT64_MIN;const int64_t ICSData = INT64_MAX; // DW_FORM_implicit_const SDataconst uint64_t UData[] = {UINT64_MAX - 1, UINT64_MAX - 2, UINT64_MAX - 3,UINT64_MAX - 4, UINT64_MAX - 5, UINT64_MAX - 6,UINT64_MAX - 7, UINT64_MAX - 8, UINT64_MAX - 9};#define UDATA_1 18446744073709551614ULLconst uint32_t Dwarf32Values[] = {1, 2, 3, 4, 5, 6, 7, 8};const char *StringValue = "Hello";const char *StrpValue = "World";const char *StrxValue = "Indexed";const char *Strx1Value = "Indexed1";const char *Strx2Value = "Indexed2";const char *Strx3Value = "Indexed3";const char *Strx4Value = "Indexed4";auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();dwarfgen::DIE CUDie = CU.getUnitDIE();if (Version >= 5)CUDie.addStrOffsetsBaseAttribute();uint16_t Attr = DW_AT_lo_user;//----------------------------------------------------------------------// Test address forms//----------------------------------------------------------------------const auto Attr_DW_FORM_addr = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_addr, DW_FORM_addr, AddrValue);//----------------------------------------------------------------------// Test block forms//----------------------------------------------------------------------const auto Attr_DW_FORM_block = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_block, DW_FORM_block, BlockData, BlockSize);const auto Attr_DW_FORM_block1 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_block1, DW_FORM_block1, BlockData, BlockSize);const auto Attr_DW_FORM_block2 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_block2, DW_FORM_block2, BlockData, BlockSize);const auto Attr_DW_FORM_block4 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_block4, DW_FORM_block4, BlockData, BlockSize);// We handle data16 as a block form.const auto Attr_DW_FORM_data16 = static_cast<dwarf::Attribute>(Attr++);if (Version >= 5)CUDie.addAttribute(Attr_DW_FORM_data16, DW_FORM_data16, Data16, 16);//----------------------------------------------------------------------// Test data forms//----------------------------------------------------------------------const auto Attr_DW_FORM_data1 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_data1, DW_FORM_data1, Data1);const auto Attr_DW_FORM_data2 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_data2, DW_FORM_data2, Data2);const auto Attr_DW_FORM_data4 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_data4, DW_FORM_data4, Data4);const auto Attr_DW_FORM_data8 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_data8, DW_FORM_data8, Data8);//----------------------------------------------------------------------// Test string forms//----------------------------------------------------------------------const auto Attr_DW_FORM_string = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_string, DW_FORM_string, StringValue);const auto Attr_DW_FORM_strx = static_cast<dwarf::Attribute>(Attr++);const auto Attr_DW_FORM_strx1 = static_cast<dwarf::Attribute>(Attr++);const auto Attr_DW_FORM_strx2 = static_cast<dwarf::Attribute>(Attr++);const auto Attr_DW_FORM_strx3 = static_cast<dwarf::Attribute>(Attr++);const auto Attr_DW_FORM_strx4 = static_cast<dwarf::Attribute>(Attr++);if (Version >= 5) {CUDie.addAttribute(Attr_DW_FORM_strx, DW_FORM_strx, StrxValue);CUDie.addAttribute(Attr_DW_FORM_strx1, DW_FORM_strx1, Strx1Value);CUDie.addAttribute(Attr_DW_FORM_strx2, DW_FORM_strx2, Strx2Value);CUDie.addAttribute(Attr_DW_FORM_strx3, DW_FORM_strx3, Strx3Value);CUDie.addAttribute(Attr_DW_FORM_strx4, DW_FORM_strx4, Strx4Value);}const auto Attr_DW_FORM_strp = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_strp, DW_FORM_strp, StrpValue);//----------------------------------------------------------------------// Test reference forms//----------------------------------------------------------------------const auto Attr_DW_FORM_ref_addr = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_ref_addr, DW_FORM_ref_addr, RefAddr);const auto Attr_DW_FORM_ref1 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_ref1, DW_FORM_ref1, Data1);const auto Attr_DW_FORM_ref2 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_ref2, DW_FORM_ref2, Data2);const auto Attr_DW_FORM_ref4 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_ref4, DW_FORM_ref4, Data4);const auto Attr_DW_FORM_ref8 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_ref8, DW_FORM_ref8, Data8);const auto Attr_DW_FORM_ref_sig8 = static_cast<dwarf::Attribute>(Attr++);if (Version >= 4)CUDie.addAttribute(Attr_DW_FORM_ref_sig8, DW_FORM_ref_sig8, Data8_2);const auto Attr_DW_FORM_ref_udata = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_ref_udata, DW_FORM_ref_udata, UData[0]);//----------------------------------------------------------------------// Test flag forms//----------------------------------------------------------------------const auto Attr_DW_FORM_flag_true = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_flag_true, DW_FORM_flag, true);const auto Attr_DW_FORM_flag_false = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_flag_false, DW_FORM_flag, false);const auto Attr_DW_FORM_flag_present = static_cast<dwarf::Attribute>(Attr++);if (Version >= 4)CUDie.addAttribute(Attr_DW_FORM_flag_present, DW_FORM_flag_present);//----------------------------------------------------------------------// Test SLEB128 based forms//----------------------------------------------------------------------const auto Attr_DW_FORM_sdata = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_sdata, DW_FORM_sdata, SData);const auto Attr_DW_FORM_implicit_const =static_cast<dwarf::Attribute>(Attr++);if (Version >= 5)CUDie.addAttribute(Attr_DW_FORM_implicit_const, DW_FORM_implicit_const,ICSData);//----------------------------------------------------------------------// Test ULEB128 based forms//----------------------------------------------------------------------const auto Attr_DW_FORM_udata = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_udata, DW_FORM_udata, UData[0]);//----------------------------------------------------------------------// Test DWARF32/DWARF64 forms//----------------------------------------------------------------------const auto Attr_DW_FORM_GNU_ref_alt = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_DW_FORM_GNU_ref_alt, DW_FORM_GNU_ref_alt,Dwarf32Values[0]);const auto Attr_DW_FORM_sec_offset = static_cast<dwarf::Attribute>(Attr++);if (Version >= 4)CUDie.addAttribute(Attr_DW_FORM_sec_offset, DW_FORM_sec_offset,Dwarf32Values[1]);//----------------------------------------------------------------------// Add an address at the end to make sure we can decode this value//----------------------------------------------------------------------const auto Attr_Last = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr_Last, DW_FORM_addr, AddrValue);//----------------------------------------------------------------------// Generate the DWARF//----------------------------------------------------------------------StringRef FileBytes = DG->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));auto DieDG = U->getUnitDIE(false);EXPECT_TRUE(DieDG.isValid());//----------------------------------------------------------------------// Test address forms//----------------------------------------------------------------------EXPECT_EQ(AddrValue, toAddress(DieDG.find(Attr_DW_FORM_addr), 0));//----------------------------------------------------------------------// Test block forms//----------------------------------------------------------------------Optional<DWARFFormValue> FormValue;ArrayRef<uint8_t> ExtractedBlockData;Optional<ArrayRef<uint8_t>> BlockDataOpt;FormValue = DieDG.find(Attr_DW_FORM_block);EXPECT_TRUE((bool)FormValue);BlockDataOpt = FormValue->getAsBlock();EXPECT_TRUE(BlockDataOpt.has_value());ExtractedBlockData = BlockDataOpt.value();EXPECT_EQ(ExtractedBlockData.size(), BlockSize);EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0);FormValue = DieDG.find(Attr_DW_FORM_block1);EXPECT_TRUE((bool)FormValue);BlockDataOpt = FormValue->getAsBlock();EXPECT_TRUE(BlockDataOpt.has_value());ExtractedBlockData = BlockDataOpt.value();EXPECT_EQ(ExtractedBlockData.size(), BlockSize);EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0);FormValue = DieDG.find(Attr_DW_FORM_block2);EXPECT_TRUE((bool)FormValue);BlockDataOpt = FormValue->getAsBlock();EXPECT_TRUE(BlockDataOpt.has_value());ExtractedBlockData = BlockDataOpt.value();EXPECT_EQ(ExtractedBlockData.size(), BlockSize);EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0);FormValue = DieDG.find(Attr_DW_FORM_block4);EXPECT_TRUE((bool)FormValue);BlockDataOpt = FormValue->getAsBlock();EXPECT_TRUE(BlockDataOpt.has_value());ExtractedBlockData = BlockDataOpt.value();EXPECT_EQ(ExtractedBlockData.size(), BlockSize);EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0);// Data16 is handled like a block.if (Version >= 5) {FormValue = DieDG.find(Attr_DW_FORM_data16);EXPECT_TRUE((bool)FormValue);BlockDataOpt = FormValue->getAsBlock();EXPECT_TRUE(BlockDataOpt.has_value());ExtractedBlockData = BlockDataOpt.value();EXPECT_EQ(ExtractedBlockData.size(), 16u);EXPECT_TRUE(memcmp(ExtractedBlockData.data(), Data16, 16) == 0);}//----------------------------------------------------------------------// Test data forms//----------------------------------------------------------------------EXPECT_EQ(Data1, toUnsigned(DieDG.find(Attr_DW_FORM_data1), 0));EXPECT_EQ(Data2, toUnsigned(DieDG.find(Attr_DW_FORM_data2), 0));EXPECT_EQ(Data4, toUnsigned(DieDG.find(Attr_DW_FORM_data4), 0));EXPECT_EQ(Data8, toUnsigned(DieDG.find(Attr_DW_FORM_data8), 0));//----------------------------------------------------------------------// Test string forms//----------------------------------------------------------------------auto ExtractedStringValue = toString(DieDG.find(Attr_DW_FORM_string));EXPECT_TRUE((bool)ExtractedStringValue);EXPECT_STREQ(StringValue, *ExtractedStringValue);if (Version >= 5) {auto ExtractedStrxValue = toString(DieDG.find(Attr_DW_FORM_strx));EXPECT_TRUE((bool)ExtractedStrxValue);EXPECT_STREQ(StrxValue, *ExtractedStrxValue);auto ExtractedStrx1Value = toString(DieDG.find(Attr_DW_FORM_strx1));EXPECT_TRUE((bool)ExtractedStrx1Value);EXPECT_STREQ(Strx1Value, *ExtractedStrx1Value);auto ExtractedStrx2Value = toString(DieDG.find(Attr_DW_FORM_strx2));EXPECT_TRUE((bool)ExtractedStrx2Value);EXPECT_STREQ(Strx2Value, *ExtractedStrx2Value);auto ExtractedStrx3Value = toString(DieDG.find(Attr_DW_FORM_strx3));EXPECT_TRUE((bool)ExtractedStrx3Value);EXPECT_STREQ(Strx3Value, *ExtractedStrx3Value);auto ExtractedStrx4Value = toString(DieDG.find(Attr_DW_FORM_strx4));EXPECT_TRUE((bool)ExtractedStrx4Value);EXPECT_STREQ(Strx4Value, *ExtractedStrx4Value);}auto ExtractedStrpValue = toString(DieDG.find(Attr_DW_FORM_strp));EXPECT_TRUE((bool)ExtractedStrpValue);EXPECT_STREQ(StrpValue, *ExtractedStrpValue);//----------------------------------------------------------------------// Test reference forms//----------------------------------------------------------------------EXPECT_EQ(RefAddr, toReference(DieDG.find(Attr_DW_FORM_ref_addr), 0));EXPECT_EQ(Data1, toReference(DieDG.find(Attr_DW_FORM_ref1), 0));EXPECT_EQ(Data2, toReference(DieDG.find(Attr_DW_FORM_ref2), 0));EXPECT_EQ(Data4, toReference(DieDG.find(Attr_DW_FORM_ref4), 0));EXPECT_EQ(Data8, toReference(DieDG.find(Attr_DW_FORM_ref8), 0));if (Version >= 4) {EXPECT_EQ(Data8_2, toReference(DieDG.find(Attr_DW_FORM_ref_sig8), 0));}EXPECT_EQ(UData[0], toReference(DieDG.find(Attr_DW_FORM_ref_udata), 0));//----------------------------------------------------------------------// Test flag forms//----------------------------------------------------------------------EXPECT_EQ(1ULL, toUnsigned(DieDG.find(Attr_DW_FORM_flag_true), 0));EXPECT_EQ(0ULL, toUnsigned(DieDG.find(Attr_DW_FORM_flag_false), 1));if (Version >= 4) {EXPECT_EQ(1ULL, toUnsigned(DieDG.find(Attr_DW_FORM_flag_present), 0));}//----------------------------------------------------------------------// Test SLEB128 based forms//----------------------------------------------------------------------EXPECT_EQ(SData, toSigned(DieDG.find(Attr_DW_FORM_sdata), 0));if (Version >= 5) {EXPECT_EQ(ICSData, toSigned(DieDG.find(Attr_DW_FORM_implicit_const), 0));}//----------------------------------------------------------------------// Test ULEB128 based forms//----------------------------------------------------------------------EXPECT_EQ(UData[0], toUnsigned(DieDG.find(Attr_DW_FORM_udata), 0));//----------------------------------------------------------------------// Test DWARF32/DWARF64 forms//----------------------------------------------------------------------EXPECT_EQ(Dwarf32Values[0],toReference(DieDG.find(Attr_DW_FORM_GNU_ref_alt), 0));if (Version >= 4) {EXPECT_EQ(Dwarf32Values[1],toSectionOffset(DieDG.find(Attr_DW_FORM_sec_offset), 0));}//----------------------------------------------------------------------// Add an address at the end to make sure we can decode this value//----------------------------------------------------------------------EXPECT_EQ(AddrValue, toAddress(DieDG.find(Attr_Last), 0));}TEST(DWARFDebugInfo, TestDWARF32Version2Addr4AllForms) {// Test that we can decode all forms for DWARF32, version 2, with 4 byte// addresses.typedef uint32_t AddrType;// DW_FORM_ref_addr are the same as the address type in DWARF32 version 2.typedef AddrType RefAddrType;TestAllForms<2, AddrType, RefAddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8AllForms) {#elseTEST(DWARFDebugInfo, TestDWARF32Version2Addr8AllForms) {#endif// Test that we can decode all forms for DWARF32, version 2, with 4 byte// addresses.typedef uint64_t AddrType;// DW_FORM_ref_addr are the same as the address type in DWARF32 version 2.typedef AddrType RefAddrType;TestAllForms<2, AddrType, RefAddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version3Addr4AllForms) {// Test that we can decode all forms for DWARF32, version 3, with 4 byte// addresses.typedef uint32_t AddrType;// DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and later.typedef uint32_t RefAddrType;TestAllForms<3, AddrType, RefAddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8AllForms) {#elseTEST(DWARFDebugInfo, TestDWARF32Version3Addr8AllForms) {#endif// Test that we can decode all forms for DWARF32, version 3, with 8 byte// addresses.typedef uint64_t AddrType;// DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and latertypedef uint32_t RefAddrType;TestAllForms<3, AddrType, RefAddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version4Addr4AllForms) {// Test that we can decode all forms for DWARF32, version 4, with 4 byte// addresses.typedef uint32_t AddrType;// DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and latertypedef uint32_t RefAddrType;TestAllForms<4, AddrType, RefAddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8AllForms) {#elseTEST(DWARFDebugInfo, TestDWARF32Version4Addr8AllForms) {#endif// Test that we can decode all forms for DWARF32, version 4, with 8 byte// addresses.typedef uint64_t AddrType;// DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and latertypedef uint32_t RefAddrType;TestAllForms<4, AddrType, RefAddrType>();}#ifdef _AIXTEST(DWARFDebigInfo, DISABLED_TestDWARF32Version5Addr4AllForms) {#elseTEST(DWARFDebugInfo, TestDWARF32Version5Addr4AllForms) {#endif// Test that we can decode all forms for DWARF32, version 5, with 4 byte// addresses.typedef uint32_t AddrType;// DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and latertypedef uint32_t RefAddrType;TestAllForms<5, AddrType, RefAddrType>();}#ifdef _AIXTEST(DWARFDebigInfo, DISABLED_TestDWARF32Version5Addr8AllForms) {#elseTEST(DWARFDebugInfo, TestDWARF32Version5Addr8AllForms) {#endif// Test that we can decode all forms for DWARF32, version 5, with 8 byte// addresses.typedef uint64_t AddrType;// DW_FORM_ref_addr are 4 bytes in DWARF32 for version 3 and latertypedef uint32_t RefAddrType;TestAllForms<5, AddrType, RefAddrType>();}template <uint16_t Version, class AddrType> void TestChildren() {Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType));if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test that we can decode DW_FORM_ref_addr values correctly in DWARF 2 with// 4 byte addresses. DW_FORM_ref_addr values should be 4 bytes when using// 8 byte addresses.auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();dwarfgen::DIE CUDie = CU.getUnitDIE();CUDie.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c");CUDie.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C);dwarfgen::DIE SubprogramDie = CUDie.addChild(DW_TAG_subprogram);SubprogramDie.addAttribute(DW_AT_name, DW_FORM_strp, "main");SubprogramDie.addAttribute(DW_AT_low_pc, DW_FORM_addr, 0x1000U);SubprogramDie.addAttribute(DW_AT_high_pc, DW_FORM_addr, 0x2000U);dwarfgen::DIE IntDie = CUDie.addChild(DW_TAG_base_type);IntDie.addAttribute(DW_AT_name, DW_FORM_strp, "int");IntDie.addAttribute(DW_AT_encoding, DW_FORM_data1, DW_ATE_signed);IntDie.addAttribute(DW_AT_byte_size, DW_FORM_data1, 4);dwarfgen::DIE ArgcDie = SubprogramDie.addChild(DW_TAG_formal_parameter);ArgcDie.addAttribute(DW_AT_name, DW_FORM_strp, "argc");// ArgcDie.addAttribute(DW_AT_type, DW_FORM_ref4, IntDie);ArgcDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, IntDie);StringRef FileBytes = DG->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto DieDG = U->getUnitDIE(false);EXPECT_TRUE(DieDG.isValid());// Verify the first child of the compile unit DIE is our subprogram.auto SubprogramDieDG = DieDG.getFirstChild();EXPECT_TRUE(SubprogramDieDG.isValid());EXPECT_EQ(SubprogramDieDG.getTag(), DW_TAG_subprogram);// Verify the first child of the subprogram is our formal parameter.auto ArgcDieDG = SubprogramDieDG.getFirstChild();EXPECT_TRUE(ArgcDieDG.isValid());EXPECT_EQ(ArgcDieDG.getTag(), DW_TAG_formal_parameter);// Verify our formal parameter has a NULL tag sibling.auto NullDieDG = ArgcDieDG.getSibling();EXPECT_TRUE(NullDieDG.isValid());if (NullDieDG) {EXPECT_EQ(NullDieDG.getTag(), DW_TAG_null);EXPECT_TRUE(!NullDieDG.getSibling().isValid());EXPECT_TRUE(!NullDieDG.getFirstChild().isValid());}// Verify the sibling of our subprogram is our integer base type.auto IntDieDG = SubprogramDieDG.getSibling();EXPECT_TRUE(IntDieDG.isValid());EXPECT_EQ(IntDieDG.getTag(), DW_TAG_base_type);// Verify the sibling of our subprogram is our integer base is a NULL tag.NullDieDG = IntDieDG.getSibling();EXPECT_TRUE(NullDieDG.isValid());if (NullDieDG) {EXPECT_EQ(NullDieDG.getTag(), DW_TAG_null);EXPECT_TRUE(!NullDieDG.getSibling().isValid());EXPECT_TRUE(!NullDieDG.getFirstChild().isValid());}// Verify the previous sibling of our subprogram is our integer base type.IntDieDG = NullDieDG.getPreviousSibling();EXPECT_TRUE(IntDieDG.isValid());EXPECT_EQ(IntDieDG.getTag(), DW_TAG_base_type);}TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Children) {// Test that we can decode all forms for DWARF32, version 2, with 4 byte// addresses.typedef uint32_t AddrType;TestChildren<2, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8Children) {#elseTEST(DWARFDebugInfo, TestDWARF32Version2Addr8Children) {#endif// Test that we can decode all forms for DWARF32, version 2, with 8 byte// addresses.typedef uint64_t AddrType;TestChildren<2, AddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Children) {// Test that we can decode all forms for DWARF32, version 3, with 4 byte// addresses.typedef uint32_t AddrType;TestChildren<3, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8Children) {#elseTEST(DWARFDebugInfo, TestDWARF32Version3Addr8Children) {#endif// Test that we can decode all forms for DWARF32, version 3, with 8 byte// addresses.typedef uint64_t AddrType;TestChildren<3, AddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Children) {// Test that we can decode all forms for DWARF32, version 4, with 4 byte// addresses.typedef uint32_t AddrType;TestChildren<4, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8Children) {#elseTEST(DWARFDebugInfo, TestDWARF32Version4Addr8Children) {#endif// Test that we can decode all forms for DWARF32, version 4, with 8 byte// addresses.typedef uint64_t AddrType;TestChildren<4, AddrType>();}template <uint16_t Version, class AddrType> void TestReferences() {Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType));if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test that we can decode DW_FORM_refXXX values correctly in DWARF.auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU1 = DG->addCompileUnit();dwarfgen::CompileUnit &CU2 = DG->addCompileUnit();dwarfgen::DIE CU1Die = CU1.getUnitDIE();CU1Die.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c");CU1Die.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C);dwarfgen::DIE CU1TypeDie = CU1Die.addChild(DW_TAG_base_type);CU1TypeDie.addAttribute(DW_AT_name, DW_FORM_strp, "int");CU1TypeDie.addAttribute(DW_AT_encoding, DW_FORM_data1, DW_ATE_signed);CU1TypeDie.addAttribute(DW_AT_byte_size, DW_FORM_data1, 4);dwarfgen::DIE CU1Ref1Die = CU1Die.addChild(DW_TAG_variable);CU1Ref1Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref1");CU1Ref1Die.addAttribute(DW_AT_type, DW_FORM_ref1, CU1TypeDie);dwarfgen::DIE CU1Ref2Die = CU1Die.addChild(DW_TAG_variable);CU1Ref2Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref2");CU1Ref2Die.addAttribute(DW_AT_type, DW_FORM_ref2, CU1TypeDie);dwarfgen::DIE CU1Ref4Die = CU1Die.addChild(DW_TAG_variable);CU1Ref4Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref4");CU1Ref4Die.addAttribute(DW_AT_type, DW_FORM_ref4, CU1TypeDie);dwarfgen::DIE CU1Ref8Die = CU1Die.addChild(DW_TAG_variable);CU1Ref8Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU1Ref8");CU1Ref8Die.addAttribute(DW_AT_type, DW_FORM_ref8, CU1TypeDie);dwarfgen::DIE CU1RefAddrDie = CU1Die.addChild(DW_TAG_variable);CU1RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU1RefAddr");CU1RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU1TypeDie);dwarfgen::DIE CU2Die = CU2.getUnitDIE();CU2Die.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/foo.c");CU2Die.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C);dwarfgen::DIE CU2TypeDie = CU2Die.addChild(DW_TAG_base_type);CU2TypeDie.addAttribute(DW_AT_name, DW_FORM_strp, "float");CU2TypeDie.addAttribute(DW_AT_encoding, DW_FORM_data1, DW_ATE_float);CU2TypeDie.addAttribute(DW_AT_byte_size, DW_FORM_data1, 4);dwarfgen::DIE CU2Ref1Die = CU2Die.addChild(DW_TAG_variable);CU2Ref1Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref1");CU2Ref1Die.addAttribute(DW_AT_type, DW_FORM_ref1, CU2TypeDie);dwarfgen::DIE CU2Ref2Die = CU2Die.addChild(DW_TAG_variable);CU2Ref2Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref2");CU2Ref2Die.addAttribute(DW_AT_type, DW_FORM_ref2, CU2TypeDie);dwarfgen::DIE CU2Ref4Die = CU2Die.addChild(DW_TAG_variable);CU2Ref4Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref4");CU2Ref4Die.addAttribute(DW_AT_type, DW_FORM_ref4, CU2TypeDie);dwarfgen::DIE CU2Ref8Die = CU2Die.addChild(DW_TAG_variable);CU2Ref8Die.addAttribute(DW_AT_name, DW_FORM_strp, "CU2Ref8");CU2Ref8Die.addAttribute(DW_AT_type, DW_FORM_ref8, CU2TypeDie);dwarfgen::DIE CU2RefAddrDie = CU2Die.addChild(DW_TAG_variable);CU2RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU2RefAddr");CU2RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU2TypeDie);// Refer to a type in CU1 from CU2dwarfgen::DIE CU2ToCU1RefAddrDie = CU2Die.addChild(DW_TAG_variable);CU2ToCU1RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU2ToCU1RefAddr");CU2ToCU1RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU1TypeDie);// Refer to a type in CU2 from CU1dwarfgen::DIE CU1ToCU2RefAddrDie = CU1Die.addChild(DW_TAG_variable);CU1ToCU2RefAddrDie.addAttribute(DW_AT_name, DW_FORM_strp, "CU1ToCU2RefAddr");CU1ToCU2RefAddrDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, CU2TypeDie);StringRef FileBytes = DG->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 2u);DWARFCompileUnit *U1 =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));DWARFCompileUnit *U2 =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(1));// Get the compile unit DIE is valid.auto Unit1DieDG = U1->getUnitDIE(false);EXPECT_TRUE(Unit1DieDG.isValid());auto Unit2DieDG = U2->getUnitDIE(false);EXPECT_TRUE(Unit2DieDG.isValid());// Verify the first child of the compile unit 1 DIE is our int base type.auto CU1TypeDieDG = Unit1DieDG.getFirstChild();EXPECT_TRUE(CU1TypeDieDG.isValid());EXPECT_EQ(CU1TypeDieDG.getTag(), DW_TAG_base_type);EXPECT_EQ(DW_ATE_signed, toUnsigned(CU1TypeDieDG.find(DW_AT_encoding), 0));// Verify the first child of the compile unit 2 DIE is our float base type.auto CU2TypeDieDG = Unit2DieDG.getFirstChild();EXPECT_TRUE(CU2TypeDieDG.isValid());EXPECT_EQ(CU2TypeDieDG.getTag(), DW_TAG_base_type);EXPECT_EQ(DW_ATE_float, toUnsigned(CU2TypeDieDG.find(DW_AT_encoding), 0));// Verify the sibling of the base type DIE is our Ref1 DIE and that its// DW_AT_type points to our base type DIE.auto CU1Ref1DieDG = CU1TypeDieDG.getSibling();EXPECT_TRUE(CU1Ref1DieDG.isValid());EXPECT_EQ(CU1Ref1DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU1TypeDieDG.getOffset(),toReference(CU1Ref1DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our Ref2 DIE and that its DW_AT_type points to our// base type DIE in CU1.auto CU1Ref2DieDG = CU1Ref1DieDG.getSibling();EXPECT_TRUE(CU1Ref2DieDG.isValid());EXPECT_EQ(CU1Ref2DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU1TypeDieDG.getOffset(),toReference(CU1Ref2DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our Ref4 DIE and that its DW_AT_type points to our// base type DIE in CU1.auto CU1Ref4DieDG = CU1Ref2DieDG.getSibling();EXPECT_TRUE(CU1Ref4DieDG.isValid());EXPECT_EQ(CU1Ref4DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU1TypeDieDG.getOffset(),toReference(CU1Ref4DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our Ref8 DIE and that its DW_AT_type points to our// base type DIE in CU1.auto CU1Ref8DieDG = CU1Ref4DieDG.getSibling();EXPECT_TRUE(CU1Ref8DieDG.isValid());EXPECT_EQ(CU1Ref8DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU1TypeDieDG.getOffset(),toReference(CU1Ref8DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our RefAddr DIE and that its DW_AT_type points to our// base type DIE in CU1.auto CU1RefAddrDieDG = CU1Ref8DieDG.getSibling();EXPECT_TRUE(CU1RefAddrDieDG.isValid());EXPECT_EQ(CU1RefAddrDieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU1TypeDieDG.getOffset(),toReference(CU1RefAddrDieDG.find(DW_AT_type), -1ULL));// Verify the sibling of the Ref4 DIE is our RefAddr DIE and that its// DW_AT_type points to our base type DIE.auto CU1ToCU2RefAddrDieDG = CU1RefAddrDieDG.getSibling();EXPECT_TRUE(CU1ToCU2RefAddrDieDG.isValid());EXPECT_EQ(CU1ToCU2RefAddrDieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU2TypeDieDG.getOffset(),toReference(CU1ToCU2RefAddrDieDG.find(DW_AT_type), -1ULL));// Verify the sibling of the base type DIE is our Ref1 DIE and that its// DW_AT_type points to our base type DIE.auto CU2Ref1DieDG = CU2TypeDieDG.getSibling();EXPECT_TRUE(CU2Ref1DieDG.isValid());EXPECT_EQ(CU2Ref1DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU2TypeDieDG.getOffset(),toReference(CU2Ref1DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our Ref2 DIE and that its DW_AT_type points to our// base type DIE in CU2.auto CU2Ref2DieDG = CU2Ref1DieDG.getSibling();EXPECT_TRUE(CU2Ref2DieDG.isValid());EXPECT_EQ(CU2Ref2DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU2TypeDieDG.getOffset(),toReference(CU2Ref2DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our Ref4 DIE and that its DW_AT_type points to our// base type DIE in CU2.auto CU2Ref4DieDG = CU2Ref2DieDG.getSibling();EXPECT_TRUE(CU2Ref4DieDG.isValid());EXPECT_EQ(CU2Ref4DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU2TypeDieDG.getOffset(),toReference(CU2Ref4DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our Ref8 DIE and that its DW_AT_type points to our// base type DIE in CU2.auto CU2Ref8DieDG = CU2Ref4DieDG.getSibling();EXPECT_TRUE(CU2Ref8DieDG.isValid());EXPECT_EQ(CU2Ref8DieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU2TypeDieDG.getOffset(),toReference(CU2Ref8DieDG.find(DW_AT_type), -1ULL));// Verify the sibling is our RefAddr DIE and that its DW_AT_type points to our// base type DIE in CU2.auto CU2RefAddrDieDG = CU2Ref8DieDG.getSibling();EXPECT_TRUE(CU2RefAddrDieDG.isValid());EXPECT_EQ(CU2RefAddrDieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU2TypeDieDG.getOffset(),toReference(CU2RefAddrDieDG.find(DW_AT_type), -1ULL));// Verify the sibling of the Ref4 DIE is our RefAddr DIE and that its// DW_AT_type points to our base type DIE.auto CU2ToCU1RefAddrDieDG = CU2RefAddrDieDG.getSibling();EXPECT_TRUE(CU2ToCU1RefAddrDieDG.isValid());EXPECT_EQ(CU2ToCU1RefAddrDieDG.getTag(), DW_TAG_variable);EXPECT_EQ(CU1TypeDieDG.getOffset(),toReference(CU2ToCU1RefAddrDieDG.find(DW_AT_type), -1ULL));}TEST(DWARFDebugInfo, TestDWARF32Version2Addr4References) {// Test that we can decode all forms for DWARF32, version 2, with 4 byte// addresses.typedef uint32_t AddrType;TestReferences<2, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8References) {#elseTEST(DWARFDebugInfo, TestDWARF32Version2Addr8References) {#endif// Test that we can decode all forms for DWARF32, version 2, with 8 byte// addresses.typedef uint64_t AddrType;TestReferences<2, AddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version3Addr4References) {// Test that we can decode all forms for DWARF32, version 3, with 4 byte// addresses.typedef uint32_t AddrType;TestReferences<3, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8References) {#elseTEST(DWARFDebugInfo, TestDWARF32Version3Addr8References) {#endif// Test that we can decode all forms for DWARF32, version 3, with 8 byte// addresses.typedef uint64_t AddrType;TestReferences<3, AddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version4Addr4References) {// Test that we can decode all forms for DWARF32, version 4, with 4 byte// addresses.typedef uint32_t AddrType;TestReferences<4, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8References) {#elseTEST(DWARFDebugInfo, TestDWARF32Version4Addr8References) {#endif// Test that we can decode all forms for DWARF32, version 4, with 8 byte// addresses.typedef uint64_t AddrType;TestReferences<4, AddrType>();}template <uint16_t Version, class AddrType> void TestAddresses() {Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType));if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test the DWARF APIs related to accessing the DW_AT_low_pc and// DW_AT_high_pc.const bool SupportsHighPCAsOffset = Version >= 4;auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();dwarfgen::DIE CUDie = CU.getUnitDIE();CUDie.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c");CUDie.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C);// Create a subprogram DIE with no low or high PC.dwarfgen::DIE SubprogramNoPC = CUDie.addChild(DW_TAG_subprogram);SubprogramNoPC.addAttribute(DW_AT_name, DW_FORM_strp, "no_pc");// Create a subprogram DIE with a low PC only.dwarfgen::DIE SubprogramLowPC = CUDie.addChild(DW_TAG_subprogram);SubprogramLowPC.addAttribute(DW_AT_name, DW_FORM_strp, "low_pc");const uint64_t ActualLowPC = 0x1000;const uint64_t ActualHighPC = 0x2000;const uint64_t ActualHighPCOffset = ActualHighPC - ActualLowPC;SubprogramLowPC.addAttribute(DW_AT_low_pc, DW_FORM_addr, ActualLowPC);// Create a subprogram DIE with a low and high PC.dwarfgen::DIE SubprogramLowHighPC = CUDie.addChild(DW_TAG_subprogram);SubprogramLowHighPC.addAttribute(DW_AT_name, DW_FORM_strp, "low_high_pc");SubprogramLowHighPC.addAttribute(DW_AT_low_pc, DW_FORM_addr, ActualLowPC);// Encode the high PC as an offset from the low PC if supported.if (SupportsHighPCAsOffset)SubprogramLowHighPC.addAttribute(DW_AT_high_pc, DW_FORM_data4,ActualHighPCOffset);elseSubprogramLowHighPC.addAttribute(DW_AT_high_pc, DW_FORM_addr, ActualHighPC);StringRef FileBytes = DG->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto DieDG = U->getUnitDIE(false);EXPECT_TRUE(DieDG.isValid());uint64_t LowPC, HighPC, SectionIndex;Optional<uint64_t> OptU64;// Verify the that our subprogram with no PC value fails appropriately when// asked for any PC values.auto SubprogramDieNoPC = DieDG.getFirstChild();EXPECT_TRUE(SubprogramDieNoPC.isValid());EXPECT_EQ(SubprogramDieNoPC.getTag(), DW_TAG_subprogram);OptU64 = toAddress(SubprogramDieNoPC.find(DW_AT_low_pc));EXPECT_FALSE((bool)OptU64);OptU64 = toAddress(SubprogramDieNoPC.find(DW_AT_high_pc));EXPECT_FALSE((bool)OptU64);EXPECT_FALSE(SubprogramDieNoPC.getLowAndHighPC(LowPC, HighPC, SectionIndex));OptU64 = toAddress(SubprogramDieNoPC.find(DW_AT_high_pc));EXPECT_FALSE((bool)OptU64);OptU64 = toUnsigned(SubprogramDieNoPC.find(DW_AT_high_pc));EXPECT_FALSE((bool)OptU64);OptU64 = SubprogramDieNoPC.getHighPC(ActualLowPC);EXPECT_FALSE((bool)OptU64);EXPECT_FALSE(SubprogramDieNoPC.getLowAndHighPC(LowPC, HighPC, SectionIndex));// Verify the that our subprogram with only a low PC value succeeds when// we ask for the Low PC, but fails appropriately when asked for the high PC// or both low and high PC values.auto SubprogramDieLowPC = SubprogramDieNoPC.getSibling();EXPECT_TRUE(SubprogramDieLowPC.isValid());EXPECT_EQ(SubprogramDieLowPC.getTag(), DW_TAG_subprogram);OptU64 = toAddress(SubprogramDieLowPC.find(DW_AT_low_pc));EXPECT_TRUE((bool)OptU64);EXPECT_EQ(*OptU64, ActualLowPC);OptU64 = toAddress(SubprogramDieLowPC.find(DW_AT_high_pc));EXPECT_FALSE((bool)OptU64);OptU64 = toUnsigned(SubprogramDieLowPC.find(DW_AT_high_pc));EXPECT_FALSE((bool)OptU64);OptU64 = SubprogramDieLowPC.getHighPC(ActualLowPC);EXPECT_FALSE((bool)OptU64);EXPECT_FALSE(SubprogramDieLowPC.getLowAndHighPC(LowPC, HighPC, SectionIndex));// Verify the that our subprogram with only a low PC value succeeds when// we ask for the Low PC, but fails appropriately when asked for the high PC// or both low and high PC values.auto SubprogramDieLowHighPC = SubprogramDieLowPC.getSibling();EXPECT_TRUE(SubprogramDieLowHighPC.isValid());EXPECT_EQ(SubprogramDieLowHighPC.getTag(), DW_TAG_subprogram);OptU64 = toAddress(SubprogramDieLowHighPC.find(DW_AT_low_pc));EXPECT_TRUE((bool)OptU64);EXPECT_EQ(*OptU64, ActualLowPC);// Get the high PC as an address. This should succeed if the high PC was// encoded as an address and fail if the high PC was encoded as an offset.OptU64 = toAddress(SubprogramDieLowHighPC.find(DW_AT_high_pc));if (SupportsHighPCAsOffset) {EXPECT_FALSE((bool)OptU64);} else {EXPECT_TRUE((bool)OptU64);EXPECT_EQ(OptU64.value(), ActualHighPC);}// Get the high PC as an unsigned constant. This should succeed if the high PC// was encoded as an offset and fail if the high PC was encoded as an address.OptU64 = toUnsigned(SubprogramDieLowHighPC.find(DW_AT_high_pc));if (SupportsHighPCAsOffset) {EXPECT_TRUE((bool)OptU64);EXPECT_EQ(OptU64.value(), ActualHighPCOffset);} else {EXPECT_FALSE((bool)OptU64);}OptU64 = SubprogramDieLowHighPC.getHighPC(ActualLowPC);EXPECT_TRUE((bool)OptU64);EXPECT_EQ(OptU64.value(), ActualHighPC);EXPECT_TRUE(SubprogramDieLowHighPC.getLowAndHighPC(LowPC, HighPC, SectionIndex));EXPECT_EQ(LowPC, ActualLowPC);EXPECT_EQ(HighPC, ActualHighPC);}TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Addresses) {// Test that we can decode address values in DWARF32, version 2, with 4 byte// addresses.typedef uint32_t AddrType;TestAddresses<2, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8Addresses) {#elseTEST(DWARFDebugInfo, TestDWARF32Version2Addr8Addresses) {#endif// Test that we can decode address values in DWARF32, version 2, with 8 byte// addresses.typedef uint64_t AddrType;TestAddresses<2, AddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Addresses) {// Test that we can decode address values in DWARF32, version 3, with 4 byte// addresses.typedef uint32_t AddrType;TestAddresses<3, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8Addresses) {#elseTEST(DWARFDebugInfo, TestDWARF32Version3Addr8Addresses) {#endif// Test that we can decode address values in DWARF32, version 3, with 8 byte// addresses.typedef uint64_t AddrType;TestAddresses<3, AddrType>();}TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Addresses) {// Test that we can decode address values in DWARF32, version 4, with 4 byte// addresses.typedef uint32_t AddrType;TestAddresses<4, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8Addresses) {#elseTEST(DWARFDebugInfo, TestDWARF32Version4Addr8Addresses) {#endif// Test that we can decode address values in DWARF32, version 4, with 8 byte// addresses.typedef uint64_t AddrType;TestAddresses<4, AddrType>();}#ifdef _AIXTEST(DWARFDebugInfo, DISABLED_TestStringOffsets) {#elseTEST(DWARFDebugInfo, TestStringOffsets) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();const char *String1 = "Hello";const char *String2 = "World";auto ExpectedDG = dwarfgen::Generator::create(Triple, 5);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();dwarfgen::DIE CUDie = CU.getUnitDIE();CUDie.addStrOffsetsBaseAttribute();uint16_t Attr = DW_AT_lo_user;// Create our strings. First we create a non-indexed reference to String1,// followed by an indexed String2. Finally, we add an indexed reference to// String1.const auto Attr1 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr1, DW_FORM_strp, String1);const auto Attr2 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr2, DW_FORM_strx, String2);const auto Attr3 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr3, DW_FORM_strx, String1);// Generate the DWARFStringRef FileBytes = DG->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);ASSERT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);uint32_t NumCUs = DwarfContext->getNumCompileUnits();ASSERT_EQ(NumCUs, 1u);DWARFUnit *U = DwarfContext->getUnitAtIndex(0);auto DieDG = U->getUnitDIE(false);ASSERT_TRUE(DieDG.isValid());// Now make sure the string offsets came out properly. Attr2 should have index// 0 (because it was the first indexed string) even though the string itself// was added eariler.auto Extracted1 = toString(DieDG.find(Attr1));ASSERT_TRUE((bool)Extracted1);EXPECT_STREQ(String1, *Extracted1);Optional<DWARFFormValue> Form2 = DieDG.find(Attr2);ASSERT_TRUE((bool)Form2);EXPECT_EQ(0u, Form2->getRawUValue());auto Extracted2 = toString(Form2);ASSERT_TRUE((bool)Extracted2);EXPECT_STREQ(String2, *Extracted2);Optional<DWARFFormValue> Form3 = DieDG.find(Attr3);ASSERT_TRUE((bool)Form3);EXPECT_EQ(1u, Form3->getRawUValue());auto Extracted3 = toString(Form3);ASSERT_TRUE((bool)Extracted3);EXPECT_STREQ(String1, *Extracted3);}#if defined(_AIX) && defined(__64BIT__)TEST(DWARFDebugInfo, DISABLED_TestEmptyStringOffsets) {#elseTEST(DWARFDebugInfo, TestEmptyStringOffsets) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();const char *String1 = "Hello";auto ExpectedDG = dwarfgen::Generator::create(Triple, 5);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();dwarfgen::DIE CUDie = CU.getUnitDIE();uint16_t Attr = DW_AT_lo_user;// We shall insert only one string. It will be referenced directly.const auto Attr1 = static_cast<dwarf::Attribute>(Attr++);CUDie.addAttribute(Attr1, DW_FORM_strp, String1);// Generate the DWARFStringRef FileBytes = DG->generate();MemoryBufferRef FileBuffer(FileBytes, "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);ASSERT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);EXPECT_TRUE(DwarfContext->getDWARFObj().getStrOffsetsSection().Data.empty());}#if defined(_AIX) && defined(__64BIT__)TEST(DWARFDebugInfo, DISABLED_TestRelations) {#elseTEST(DWARFDebugInfo, TestRelations) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test the DWARF APIs related to accessing the DW_AT_low_pc and// DW_AT_high_pc.uint16_t Version = 4;auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();enum class Tag: uint16_t {A = dwarf::DW_TAG_lo_user,B,C,C1,C2,D,D1};// Scope to allow us to re-use the same DIE names{// Create DWARF tree that looks like://// CU// A// B// C// C1// C2// D// D1dwarfgen::DIE CUDie = CU.getUnitDIE();dwarfgen::DIE A = CUDie.addChild((dwarf::Tag)Tag::A);A.addChild((dwarf::Tag)Tag::B);dwarfgen::DIE C = A.addChild((dwarf::Tag)Tag::C);dwarfgen::DIE D = A.addChild((dwarf::Tag)Tag::D);C.addChild((dwarf::Tag)Tag::C1);C.addChild((dwarf::Tag)Tag::C2);D.addChild((dwarf::Tag)Tag::D1);}MemoryBufferRef FileBuffer(DG->generate(), "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto CUDie = U->getUnitDIE(false);EXPECT_TRUE(CUDie.isValid());// The compile unit doesn't have a parent or a sibling.auto ParentDie = CUDie.getParent();EXPECT_FALSE(ParentDie.isValid());auto SiblingDie = CUDie.getSibling();EXPECT_FALSE(SiblingDie.isValid());// Get the children of the compile unitauto A = CUDie.getFirstChild();auto B = A.getFirstChild();auto C = B.getSibling();auto D = C.getSibling();auto Null = D.getSibling();// Verify NULL Die is NULL and has no children or siblingsEXPECT_TRUE(Null.isNULL());EXPECT_FALSE(Null.getSibling().isValid());EXPECT_FALSE(Null.getFirstChild().isValid());// Verify all children of the compile unit DIE are correct.EXPECT_EQ(A.getTag(), (dwarf::Tag)Tag::A);EXPECT_EQ(B.getTag(), (dwarf::Tag)Tag::B);EXPECT_EQ(C.getTag(), (dwarf::Tag)Tag::C);EXPECT_EQ(D.getTag(), (dwarf::Tag)Tag::D);// Verify who has childrenEXPECT_TRUE(A.hasChildren());EXPECT_FALSE(B.hasChildren());EXPECT_TRUE(C.hasChildren());EXPECT_TRUE(D.hasChildren());// Make sure the parent of all the children of the compile unit are the// compile unit.EXPECT_EQ(A.getParent(), CUDie);// Make sure the parent of all the children of A are the A.// B is the first child in A, so we need to verify we can get the previous// DIE as the parent.EXPECT_EQ(B.getParent(), A);// C is the second child in A, so we need to make sure we can backup across// other DIE (B) at the same level to get the correct parent.EXPECT_EQ(C.getParent(), A);// D is the third child of A. We need to verify we can backup across other DIE// (B and C) including DIE that have children (D) to get the correct parent.EXPECT_EQ(D.getParent(), A);// Verify that a DIE with no children returns an invalid DWARFDie.EXPECT_FALSE(B.getFirstChild().isValid());// Verify the children of the B DIEauto C1 = C.getFirstChild();auto C2 = C1.getSibling();EXPECT_TRUE(C2.getSibling().isNULL());// Verify all children of the B DIE correctly valid or invalid.EXPECT_EQ(C1.getTag(), (dwarf::Tag)Tag::C1);EXPECT_EQ(C2.getTag(), (dwarf::Tag)Tag::C2);// Make sure the parent of all the children of the B are the B.EXPECT_EQ(C1.getParent(), C);EXPECT_EQ(C2.getParent(), C);// Make sure iterators work as expected.EXPECT_THAT(std::vector<DWARFDie>(A.begin(), A.end()),testing::ElementsAre(B, C, D));EXPECT_THAT(std::vector<DWARFDie>(A.rbegin(), A.rend()),testing::ElementsAre(D, C, B));// Make sure conversion from reverse iterator works as expected.EXPECT_EQ(A.rbegin().base(), A.end());EXPECT_EQ(A.rend().base(), A.begin());// Make sure iterator is bidirectional.{auto Begin = A.begin();auto End = A.end();auto It = A.begin();EXPECT_EQ(It, Begin);EXPECT_EQ(*It, B);++It;EXPECT_EQ(*It, C);++It;EXPECT_EQ(*It, D);++It;EXPECT_EQ(It, End);--It;EXPECT_EQ(*It, D);--It;EXPECT_EQ(*It, C);--It;EXPECT_EQ(*It, B);EXPECT_EQ(It, Begin);}// Make sure reverse iterator is bidirectional.{auto Begin = A.rbegin();auto End = A.rend();auto It = A.rbegin();EXPECT_EQ(It, Begin);EXPECT_EQ(*It, D);++It;EXPECT_EQ(*It, C);++It;EXPECT_EQ(*It, B);++It;EXPECT_EQ(It, End);--It;EXPECT_EQ(*It, B);--It;EXPECT_EQ(*It, C);--It;EXPECT_EQ(*It, D);EXPECT_EQ(It, Begin);}}TEST(DWARFDebugInfo, TestDWARFDie) {// Make sure a default constructed DWARFDie doesn't have any parent, sibling// or child;DWARFDie DefaultDie;EXPECT_FALSE(DefaultDie.getParent().isValid());EXPECT_FALSE(DefaultDie.getFirstChild().isValid());EXPECT_FALSE(DefaultDie.getSibling().isValid());}#if defined(_AIX) && defined(__64BIT__)TEST(DWARFDebugInfo, DISABLED_TestChildIterators) {#elseTEST(DWARFDebugInfo, TestChildIterators) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test the DWARF APIs related to iterating across the children of a DIE using// the DWARFDie::iterator class.uint16_t Version = 4;auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();enum class Tag: uint16_t {A = dwarf::DW_TAG_lo_user,B,};// Scope to allow us to re-use the same DIE names{// Create DWARF tree that looks like://// CU// A// Bauto CUDie = CU.getUnitDIE();CUDie.addChild((dwarf::Tag)Tag::A);CUDie.addChild((dwarf::Tag)Tag::B);}MemoryBufferRef FileBuffer(DG->generate(), "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto CUDie = U->getUnitDIE(false);EXPECT_TRUE(CUDie.isValid());uint32_t Index;DWARFDie A;DWARFDie B;// Verify the compile unit DIE's children.Index = 0;for (auto Die : CUDie.children()) {switch (Index++) {case 0: A = Die; break;case 1: B = Die; break;}}EXPECT_EQ(A.getTag(), (dwarf::Tag)Tag::A);EXPECT_EQ(B.getTag(), (dwarf::Tag)Tag::B);// Verify that A has no children by verifying that the begin and end contain// invalid DIEs and also that the iterators are equal.EXPECT_EQ(A.begin(), A.end());}TEST(DWARFDebugInfo, TestChildIteratorsOnInvalidDie) {// Verify that an invalid DIE has no children.DWARFDie Invalid;auto begin = Invalid.begin();auto end = Invalid.end();EXPECT_FALSE(begin->isValid());EXPECT_FALSE(end->isValid());EXPECT_EQ(begin, end);}TEST(DWARFDebugInfo, TestEmptyChildren) {const char *yamldata = "debug_abbrev:\n"" - Table:\n"" - Code: 0x00000001\n"" Tag: DW_TAG_compile_unit\n"" Children: DW_CHILDREN_yes\n""debug_info:\n"" - Version: 4\n"" AddrSize: 8\n"" Entries:\n"" - AbbrCode: 0x00000001\n"" - AbbrCode: 0x00000000\n";auto ErrOrSections = DWARFYAML::emitDebugSections(StringRef(yamldata));ASSERT_TRUE((bool)ErrOrSections);std::unique_ptr<DWARFContext> DwarfContext =DWARFContext::create(*ErrOrSections, 8);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto CUDie = U->getUnitDIE(false);EXPECT_TRUE(CUDie.isValid());// Verify that the CU Die that says it has children, but doesn't, actually// has begin and end iterators that are equal. We want to make sure we don't// see the Null DIEs during iteration.EXPECT_EQ(CUDie.begin(), CUDie.end());}#if defined(_AIX) && defined(__64BIT__)TEST(DWARFDebugInfo, DISABLED_TestAttributeIterators) {#elseTEST(DWARFDebugInfo, TestAttributeIterators) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test the DWARF APIs related to iterating across all attribute values in a// a DWARFDie.uint16_t Version = 4;auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();const uint64_t CULowPC = 0x1000;StringRef CUPath("/tmp/main.c");// Scope to allow us to re-use the same DIE names{auto CUDie = CU.getUnitDIE();// Encode an attribute value before an attribute with no data.CUDie.addAttribute(DW_AT_name, DW_FORM_strp, CUPath.data());// Encode an attribute value with no data in .debug_info/types to ensure// the iteration works correctly.CUDie.addAttribute(DW_AT_declaration, DW_FORM_flag_present);// Encode an attribute value after an attribute with no data.CUDie.addAttribute(DW_AT_low_pc, DW_FORM_addr, CULowPC);}MemoryBufferRef FileBuffer(DG->generate(), "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto CUDie = U->getUnitDIE(false);EXPECT_TRUE(CUDie.isValid());auto R = CUDie.attributes();auto I = R.begin();auto E = R.end();ASSERT_NE(E, I);EXPECT_EQ(I->Attr, DW_AT_name);auto ActualCUPath = toString(I->Value);EXPECT_EQ(CUPath, *ActualCUPath);ASSERT_NE(E, ++I);EXPECT_EQ(I->Attr, DW_AT_declaration);EXPECT_EQ(1ull, *I->Value.getAsUnsignedConstant());ASSERT_NE(E, ++I);EXPECT_EQ(I->Attr, DW_AT_low_pc);EXPECT_EQ(CULowPC, *I->Value.getAsAddress());EXPECT_EQ(E, ++I);}#if defined(_AIX) && defined(__64BIT__)TEST(DWARFDebugInfo, DISABLED_TestFindRecurse) {#elseTEST(DWARFDebugInfo, TestFindRecurse) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();uint16_t Version = 4;auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();StringRef SpecDieName = "spec";StringRef SpecLinkageName = "spec_linkage";StringRef AbsDieName = "abs";// Scope to allow us to re-use the same DIE names{auto CUDie = CU.getUnitDIE();auto FuncSpecDie = CUDie.addChild(DW_TAG_subprogram);auto FuncAbsDie = CUDie.addChild(DW_TAG_subprogram);// Put the linkage name in a second abstract origin DIE to ensure we// recurse through more than just one DIE when looking for attributes.auto FuncAbsDie2 = CUDie.addChild(DW_TAG_subprogram);auto FuncDie = CUDie.addChild(DW_TAG_subprogram);auto VarAbsDie = CUDie.addChild(DW_TAG_variable);auto VarDie = CUDie.addChild(DW_TAG_variable);FuncSpecDie.addAttribute(DW_AT_name, DW_FORM_strp, SpecDieName);FuncAbsDie2.addAttribute(DW_AT_linkage_name, DW_FORM_strp, SpecLinkageName);FuncAbsDie.addAttribute(DW_AT_specification, DW_FORM_ref4, FuncSpecDie);FuncAbsDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, FuncAbsDie2);FuncDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, FuncAbsDie);VarAbsDie.addAttribute(DW_AT_name, DW_FORM_strp, AbsDieName);VarDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, VarAbsDie);}MemoryBufferRef FileBuffer(DG->generate(), "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto CUDie = U->getUnitDIE(false);EXPECT_TRUE(CUDie.isValid());auto FuncSpecDie = CUDie.getFirstChild();auto FuncAbsDie = FuncSpecDie.getSibling();auto FuncAbsDie2 = FuncAbsDie.getSibling();auto FuncDie = FuncAbsDie2.getSibling();auto VarAbsDie = FuncDie.getSibling();auto VarDie = VarAbsDie.getSibling();// Make sure we can't extract the name from the specification die when using// DWARFDie::find() since it won't check the DW_AT_specification DIE.EXPECT_FALSE(FuncDie.find(DW_AT_name));// Make sure we can extract the name from the specification die when using// DWARFDie::findRecursively() since it should recurse through the// DW_AT_specification DIE.auto NameOpt = FuncDie.findRecursively(DW_AT_name);EXPECT_TRUE(NameOpt);// Test the dwarf::toString() helper function.auto StringOpt = toString(NameOpt);EXPECT_TRUE(StringOpt);EXPECT_EQ(SpecDieName, StringOpt.value_or(nullptr));// Test the dwarf::toString() helper function with a default value specified.EXPECT_EQ(SpecDieName, toString(NameOpt, nullptr));auto LinkageNameOpt = FuncDie.findRecursively(DW_AT_linkage_name);EXPECT_EQ(SpecLinkageName, toString(LinkageNameOpt).value_or(nullptr));// Make sure we can't extract the name from the abstract origin die when using// DWARFDie::find() since it won't check the DW_AT_abstract_origin DIE.EXPECT_FALSE(VarDie.find(DW_AT_name));// Make sure we can extract the name from the abstract origin die when using// DWARFDie::findRecursively() since it should recurse through the// DW_AT_abstract_origin DIE.NameOpt = VarDie.findRecursively(DW_AT_name);EXPECT_TRUE(NameOpt);// Test the dwarf::toString() helper function.StringOpt = toString(NameOpt);EXPECT_TRUE(StringOpt);EXPECT_EQ(AbsDieName, StringOpt.value_or(nullptr));}TEST(DWARFDebugInfo, TestDwarfToFunctions) {// Test all of the dwarf::toXXX functions that take a// Optional<DWARFFormValue> and extract the values from it.uint64_t InvalidU64 = 0xBADBADBADBADBADB;int64_t InvalidS64 = 0xBADBADBADBADBADB;// First test that we don't get valid values back when using an optional with// no value.Optional<DWARFFormValue> FormValOpt1 = DWARFFormValue();EXPECT_FALSE(toString(FormValOpt1).has_value());EXPECT_FALSE(toUnsigned(FormValOpt1).has_value());EXPECT_FALSE(toReference(FormValOpt1).has_value());EXPECT_FALSE(toSigned(FormValOpt1).has_value());EXPECT_FALSE(toAddress(FormValOpt1).has_value());EXPECT_FALSE(toSectionOffset(FormValOpt1).has_value());EXPECT_FALSE(toBlock(FormValOpt1).has_value());EXPECT_EQ(nullptr, toString(FormValOpt1, nullptr));EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt1, InvalidU64));EXPECT_EQ(InvalidU64, toReference(FormValOpt1, InvalidU64));EXPECT_EQ(InvalidU64, toAddress(FormValOpt1, InvalidU64));EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt1, InvalidU64));EXPECT_EQ(InvalidS64, toSigned(FormValOpt1, InvalidS64));// Test successful and unsuccessful address decoding.uint64_t Address = 0x100000000ULL;Optional<DWARFFormValue> FormValOpt2 =DWARFFormValue::createFromUValue(DW_FORM_addr, Address);EXPECT_FALSE(toString(FormValOpt2).has_value());EXPECT_FALSE(toUnsigned(FormValOpt2).has_value());EXPECT_FALSE(toReference(FormValOpt2).has_value());EXPECT_FALSE(toSigned(FormValOpt2).has_value());EXPECT_TRUE(toAddress(FormValOpt2).has_value());EXPECT_FALSE(toSectionOffset(FormValOpt2).has_value());EXPECT_FALSE(toBlock(FormValOpt2).has_value());EXPECT_EQ(nullptr, toString(FormValOpt2, nullptr));EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt2, InvalidU64));EXPECT_EQ(InvalidU64, toReference(FormValOpt2, InvalidU64));EXPECT_EQ(Address, toAddress(FormValOpt2, InvalidU64));EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt2, InvalidU64));EXPECT_EQ(InvalidS64, toSigned(FormValOpt2, InvalidU64));// Test successful and unsuccessful unsigned constant decoding.uint64_t UData8 = 0x1020304050607080ULL;Optional<DWARFFormValue> FormValOpt3 =DWARFFormValue::createFromUValue(DW_FORM_udata, UData8);EXPECT_FALSE(toString(FormValOpt3).has_value());EXPECT_TRUE(toUnsigned(FormValOpt3).has_value());EXPECT_FALSE(toReference(FormValOpt3).has_value());EXPECT_TRUE(toSigned(FormValOpt3).has_value());EXPECT_FALSE(toAddress(FormValOpt3).has_value());EXPECT_FALSE(toSectionOffset(FormValOpt3).has_value());EXPECT_FALSE(toBlock(FormValOpt3).has_value());EXPECT_EQ(nullptr, toString(FormValOpt3, nullptr));EXPECT_EQ(UData8, toUnsigned(FormValOpt3, InvalidU64));EXPECT_EQ(InvalidU64, toReference(FormValOpt3, InvalidU64));EXPECT_EQ(InvalidU64, toAddress(FormValOpt3, InvalidU64));EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt3, InvalidU64));EXPECT_EQ((int64_t)UData8, toSigned(FormValOpt3, InvalidU64));// Test successful and unsuccessful reference decoding.uint32_t RefData = 0x11223344U;Optional<DWARFFormValue> FormValOpt4 =DWARFFormValue::createFromUValue(DW_FORM_ref_addr, RefData);EXPECT_FALSE(toString(FormValOpt4).has_value());EXPECT_FALSE(toUnsigned(FormValOpt4).has_value());EXPECT_TRUE(toReference(FormValOpt4).has_value());EXPECT_FALSE(toSigned(FormValOpt4).has_value());EXPECT_FALSE(toAddress(FormValOpt4).has_value());EXPECT_FALSE(toSectionOffset(FormValOpt4).has_value());EXPECT_FALSE(toBlock(FormValOpt4).has_value());EXPECT_EQ(nullptr, toString(FormValOpt4, nullptr));EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt4, InvalidU64));EXPECT_EQ(RefData, toReference(FormValOpt4, InvalidU64));EXPECT_EQ(InvalidU64, toAddress(FormValOpt4, InvalidU64));EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt4, InvalidU64));EXPECT_EQ(InvalidS64, toSigned(FormValOpt4, InvalidU64));// Test successful and unsuccessful signed constant decoding.int64_t SData8 = 0x1020304050607080ULL;Optional<DWARFFormValue> FormValOpt5 =DWARFFormValue::createFromSValue(DW_FORM_udata, SData8);EXPECT_FALSE(toString(FormValOpt5).has_value());EXPECT_TRUE(toUnsigned(FormValOpt5).has_value());EXPECT_FALSE(toReference(FormValOpt5).has_value());EXPECT_TRUE(toSigned(FormValOpt5).has_value());EXPECT_FALSE(toAddress(FormValOpt5).has_value());EXPECT_FALSE(toSectionOffset(FormValOpt5).has_value());EXPECT_FALSE(toBlock(FormValOpt5).has_value());EXPECT_EQ(nullptr, toString(FormValOpt5, nullptr));EXPECT_EQ((uint64_t)SData8, toUnsigned(FormValOpt5, InvalidU64));EXPECT_EQ(InvalidU64, toReference(FormValOpt5, InvalidU64));EXPECT_EQ(InvalidU64, toAddress(FormValOpt5, InvalidU64));EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt5, InvalidU64));EXPECT_EQ(SData8, toSigned(FormValOpt5, InvalidU64));// Test successful and unsuccessful block decoding.uint8_t Data[] = { 2, 3, 4 };ArrayRef<uint8_t> Array(Data);Optional<DWARFFormValue> FormValOpt6 =DWARFFormValue::createFromBlockValue(DW_FORM_block1, Array);EXPECT_FALSE(toString(FormValOpt6).has_value());EXPECT_FALSE(toUnsigned(FormValOpt6).has_value());EXPECT_FALSE(toReference(FormValOpt6).has_value());EXPECT_FALSE(toSigned(FormValOpt6).has_value());EXPECT_FALSE(toAddress(FormValOpt6).has_value());EXPECT_FALSE(toSectionOffset(FormValOpt6).has_value());auto BlockOpt = toBlock(FormValOpt6);EXPECT_TRUE(BlockOpt.has_value());EXPECT_EQ(*BlockOpt, Array);EXPECT_EQ(nullptr, toString(FormValOpt6, nullptr));EXPECT_EQ(InvalidU64, toUnsigned(FormValOpt6, InvalidU64));EXPECT_EQ(InvalidU64, toReference(FormValOpt6, InvalidU64));EXPECT_EQ(InvalidU64, toAddress(FormValOpt6, InvalidU64));EXPECT_EQ(InvalidU64, toSectionOffset(FormValOpt6, InvalidU64));EXPECT_EQ(InvalidS64, toSigned(FormValOpt6, InvalidU64));// Test}#if defined(_AIX) && defined(__64BIT__)TEST(DWARFDebugInfo, DISABLED_TestFindAttrs) {#elseTEST(DWARFDebugInfo, TestFindAttrs) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();// Test the DWARFDie::find() and DWARFDie::findRecursively() that take an// ArrayRef<dwarf::Attribute> value to make sure they work correctly.uint16_t Version = 4;auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();StringRef DieMangled("_Z3fooi");// Scope to allow us to re-use the same DIE names{auto CUDie = CU.getUnitDIE();auto FuncSpecDie = CUDie.addChild(DW_TAG_subprogram);auto FuncDie = CUDie.addChild(DW_TAG_subprogram);FuncSpecDie.addAttribute(DW_AT_MIPS_linkage_name, DW_FORM_strp, DieMangled);FuncDie.addAttribute(DW_AT_specification, DW_FORM_ref4, FuncSpecDie);}MemoryBufferRef FileBuffer(DG->generate(), "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);// Verify the number of compile units is correct.uint32_t NumCUs = DwarfContext->getNumCompileUnits();EXPECT_EQ(NumCUs, 1u);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));// Get the compile unit DIE is valid.auto CUDie = U->getUnitDIE(false);EXPECT_TRUE(CUDie.isValid());auto FuncSpecDie = CUDie.getFirstChild();auto FuncDie = FuncSpecDie.getSibling();// Make sure that passing in an empty attribute list behave correctly.EXPECT_FALSE(FuncDie.find(ArrayRef<dwarf::Attribute>()).has_value());// Make sure that passing in a list of attribute that are not contained// in the DIE returns nothing.EXPECT_FALSE(FuncDie.find({DW_AT_low_pc, DW_AT_entry_pc}).has_value());const dwarf::Attribute Attrs[] = {DW_AT_linkage_name,DW_AT_MIPS_linkage_name};// Make sure we can't extract the linkage name attributes when using// DWARFDie::find() since it won't check the DW_AT_specification DIE.EXPECT_FALSE(FuncDie.find(Attrs).has_value());// Make sure we can extract the name from the specification die when using// DWARFDie::findRecursively() since it should recurse through the// DW_AT_specification DIE.auto NameOpt = FuncDie.findRecursively(Attrs);EXPECT_TRUE(NameOpt.has_value());EXPECT_EQ(DieMangled, toString(NameOpt, ""));}#if defined(_AIX) && defined(__64BIT__)TEST(DWARFDebugInfo, DISABLED_TestImplicitConstAbbrevs) {#elseTEST(DWARFDebugInfo, TestImplicitConstAbbrevs) {#endifTriple Triple = getNormalizedDefaultTargetTriple();if (!isConfigurationSupported(Triple))GTEST_SKIP();uint16_t Version = 5;auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());dwarfgen::Generator *DG = ExpectedDG.get().get();dwarfgen::CompileUnit &CU = DG->addCompileUnit();dwarfgen::DIE CUDie = CU.getUnitDIE();const dwarf::Attribute Attr = DW_AT_lo_user;const int64_t Val1 = 42;const int64_t Val2 = 43;auto FirstVal1DIE = CUDie.addChild(DW_TAG_class_type);FirstVal1DIE.addAttribute(Attr, DW_FORM_implicit_const, Val1);auto SecondVal1DIE = CUDie.addChild(DW_TAG_class_type);SecondVal1DIE.addAttribute(Attr, DW_FORM_implicit_const, Val1);auto Val2DIE = CUDie.addChild(DW_TAG_class_type);Val2DIE.addAttribute(Attr, DW_FORM_implicit_const, Val2);MemoryBufferRef FileBuffer(DG->generate(), "dwarf");auto Obj = object::ObjectFile::createObjectFile(FileBuffer);EXPECT_TRUE((bool)Obj);std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj);DWARFCompileUnit *U =cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0));EXPECT_TRUE((bool)U);const auto *Abbrevs = U->getAbbreviations();EXPECT_TRUE((bool)Abbrevs);// Let's find implicit_const abbrevs and verify,// that there are exactly two of them and both of them// can be dumped correctly.typedef decltype(Abbrevs->begin()) AbbrevIt;AbbrevIt Val1Abbrev = Abbrevs->end();AbbrevIt Val2Abbrev = Abbrevs->end();for(auto it = Abbrevs->begin(); it != Abbrevs->end(); ++it) {if (it->getNumAttributes() == 0)continue; // root abbrev for DW_TAG_compile_unitauto A = it->getAttrByIndex(0);EXPECT_EQ(A, Attr);Optional<uint32_t> AttrIndex = it->findAttributeIndex(A);EXPECT_TRUE((bool)AttrIndex);EXPECT_EQ(*AttrIndex, 0u);uint64_t OffsetVal =it->getAttributeOffsetFromIndex(*AttrIndex, /* offset */ 0, *U);EXPECT_TRUE(it->getAttributeValueFromOffset(*AttrIndex, OffsetVal, *U));auto FormValue = it->getAttributeValue(/* offset */ 0, A, *U);EXPECT_TRUE((bool)FormValue);EXPECT_EQ(FormValue->getForm(), dwarf::DW_FORM_implicit_const);const auto V = FormValue->getAsSignedConstant();EXPECT_TRUE((bool)V);auto VerifyAbbrevDump = [&V](AbbrevIt it) {std::string S;llvm::raw_string_ostream OS(S);it->dump(OS);auto FormPos = OS.str().find("DW_FORM_implicit_const");EXPECT_NE(FormPos, std::string::npos);auto ValPos = S.find_first_of("-0123456789", FormPos);EXPECT_NE(ValPos, std::string::npos);int64_t Val = std::atoll(S.substr(ValPos).c_str());EXPECT_EQ(Val, *V);};switch(*V) {case Val1:EXPECT_EQ(Val1Abbrev, Abbrevs->end());Val1Abbrev = it;VerifyAbbrevDump(it);break;case Val2:EXPECT_EQ(Val2Abbrev, Abbrevs->end());Val2Abbrev = it;VerifyAbbrevDump(it);break;default:FAIL() << "Unexpected attribute value: " << *V;}}// Now let's make sure that two Val1-DIEs refer to the same abbrev,// and Val2-DIE refers to another one.auto DieDG = U->getUnitDIE(false);auto it = DieDG.begin();std::multimap<int64_t, decltype(it->getAbbreviationDeclarationPtr())> DIEs;const DWARFAbbreviationDeclaration *AbbrevPtrVal1 = nullptr;const DWARFAbbreviationDeclaration *AbbrevPtrVal2 = nullptr;for (; it != DieDG.end(); ++it) {const auto *AbbrevPtr = it->getAbbreviationDeclarationPtr();EXPECT_TRUE((bool)AbbrevPtr);auto FormValue = it->find(Attr);EXPECT_TRUE((bool)FormValue);const auto V = FormValue->getAsSignedConstant();EXPECT_TRUE((bool)V);switch(*V) {case Val1:AbbrevPtrVal1 = AbbrevPtr;break;case Val2:AbbrevPtrVal2 = AbbrevPtr;break;default:FAIL() << "Unexpected attribute value: " << *V;}DIEs.insert(std::make_pair(*V, AbbrevPtr));}EXPECT_EQ(DIEs.count(Val1), 2u);EXPECT_EQ(DIEs.count(Val2), 1u);auto Val1Range = DIEs.equal_range(Val1);for (auto it = Val1Range.first; it != Val1Range.second; ++it)EXPECT_EQ(it->second, AbbrevPtrVal1);EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2);}TEST(DWARFDebugInfo, TestDWARFDieRangeInfoContains) {DWARFVerifier::DieRangeInfo Empty;ASSERT_TRUE(Empty.contains(Empty));DWARFVerifier::DieRangeInfo Ranges({{0x10, 0x20}, {0x30, 0x40}, {0x40, 0x50}});ASSERT_TRUE(Ranges.contains(Empty));ASSERT_FALSE(Ranges.contains({{{0x0f, 0x10}}}));ASSERT_FALSE(Ranges.contains({{{0x0f, 0x20}}}));ASSERT_FALSE(Ranges.contains({{{0x0f, 0x21}}}));// Test ranges that start at R's start addressASSERT_TRUE(Ranges.contains({{{0x10, 0x10}}}));ASSERT_TRUE(Ranges.contains({{{0x10, 0x11}}}));ASSERT_TRUE(Ranges.contains({{{0x10, 0x20}}}));ASSERT_FALSE(Ranges.contains({{{0x10, 0x21}}}));ASSERT_TRUE(Ranges.contains({{{0x11, 0x12}}}));// Test ranges that start at last bytes of RangeASSERT_TRUE(Ranges.contains({{{0x1f, 0x20}}}));ASSERT_FALSE(Ranges.contains({{{0x1f, 0x21}}}));// Test ranges that start after RangeASSERT_TRUE(Ranges.contains({{{0x20, 0x20}}}));ASSERT_FALSE(Ranges.contains({{{0x20, 0x21}}}));ASSERT_TRUE(Ranges.contains({{{0x31, 0x32}}}));ASSERT_TRUE(Ranges.contains({{{0x3f, 0x40}}}));ASSERT_TRUE(Ranges.contains({{{0x10, 0x20}, {0x30, 0x40}}}));ASSERT_TRUE(Ranges.contains({{{0x11, 0x12}, {0x31, 0x32}}}));ASSERT_TRUE(Ranges.contains({{{0x11, 0x12}, {0x12, 0x13}, {0x31, 0x32}, {0x32, 0x33}}}));ASSERT_FALSE(Ranges.contains({{{0x11, 0x12},{0x12, 0x13},{0x20, 0x21},{0x31, 0x32},{0x32, 0x33}}}));ASSERT_FALSE(Ranges.contains({{{0x11, 0x12}, {0x12, 0x13}, {0x31, 0x32}, {0x32, 0x51}}}));ASSERT_TRUE(Ranges.contains({{{0x11, 0x12}, {0x30, 0x50}}}));ASSERT_FALSE(Ranges.contains({{{0x30, 0x51}}}));ASSERT_FALSE(Ranges.contains({{{0x50, 0x51}}}));}namespace {void AssertRangesIntersect(const DWARFAddressRange &LHS,const DWARFAddressRange &RHS) {ASSERT_TRUE(LHS.intersects(RHS));ASSERT_TRUE(RHS.intersects(LHS));}void AssertRangesDontIntersect(const DWARFAddressRange &LHS,const DWARFAddressRange &RHS) {ASSERT_FALSE(LHS.intersects(RHS));ASSERT_FALSE(RHS.intersects(LHS));}void AssertRangesIntersect(const DWARFVerifier::DieRangeInfo &LHS,const DWARFAddressRangesVector &Ranges) {DWARFVerifier::DieRangeInfo RHS(Ranges);ASSERT_TRUE(LHS.intersects(RHS));ASSERT_TRUE(RHS.intersects(LHS));}void AssertRangesDontIntersect(const DWARFVerifier::DieRangeInfo &LHS,const DWARFAddressRangesVector &Ranges) {DWARFVerifier::DieRangeInfo RHS(Ranges);ASSERT_FALSE(LHS.intersects(RHS));ASSERT_FALSE(RHS.intersects(LHS));}} // namespaceTEST(DWARFDebugInfo, TestDwarfRangesIntersect) {DWARFAddressRange R(0x10, 0x20);//----------------------------------------------------------------------// Test ranges that start before R...//----------------------------------------------------------------------// Other range ends before start of RAssertRangesDontIntersect(R, {0x00, 0x10});// Other range end address is start of a RAssertRangesIntersect(R, {0x00, 0x11});// Other range end address is in RAssertRangesIntersect(R, {0x00, 0x15});// Other range end address is at and of RAssertRangesIntersect(R, {0x00, 0x20});// Other range end address is past end of RAssertRangesIntersect(R, {0x00, 0x40});//----------------------------------------------------------------------// Test ranges that start at R's start address//----------------------------------------------------------------------// Ensure empty ranges doesn't matchAssertRangesDontIntersect(R, {0x10, 0x10});// 1 byte of RangeAssertRangesIntersect(R, {0x10, 0x11});// same as RangeAssertRangesIntersect(R, {0x10, 0x20});// 1 byte past RangeAssertRangesIntersect(R, {0x10, 0x21});//----------------------------------------------------------------------// Test ranges that start inside Range//----------------------------------------------------------------------// empty in rangeAssertRangesDontIntersect(R, {0x11, 0x11});// all in RangeAssertRangesIntersect(R, {0x11, 0x1f});// ends at end of RangeAssertRangesIntersect(R, {0x11, 0x20});// ends past RangeAssertRangesIntersect(R, {0x11, 0x21});//----------------------------------------------------------------------// Test ranges that start at last bytes of Range//----------------------------------------------------------------------// ends at end of RangeAssertRangesIntersect(R, {0x1f, 0x20});// ends past RangeAssertRangesIntersect(R, {0x1f, 0x21});//----------------------------------------------------------------------// Test ranges that start after Range//----------------------------------------------------------------------// empty just past in RangeAssertRangesDontIntersect(R, {0x20, 0x20});// valid past RangeAssertRangesDontIntersect(R, {0x20, 0x21});}TEST(DWARFDebugInfo, TestDWARFDieRangeInfoIntersects) {DWARFVerifier::DieRangeInfo Ranges({{0x10, 0x20}, {0x30, 0x40}});// Test empty rangeAssertRangesDontIntersect(Ranges, {});// Test range that appears before all ranges in RangesAssertRangesDontIntersect(Ranges, {{0x00, 0x10}});// Test range that appears between ranges in RangesAssertRangesDontIntersect(Ranges, {{0x20, 0x30}});// Test range that appears after ranges in RangesAssertRangesDontIntersect(Ranges, {{0x40, 0x50}});// Test range that start before first rangeAssertRangesIntersect(Ranges, {{0x00, 0x11}});// Test range that start at first rangeAssertRangesIntersect(Ranges, {{0x10, 0x11}});// Test range that start in first rangeAssertRangesIntersect(Ranges, {{0x11, 0x12}});// Test range that start at end of first rangeAssertRangesIntersect(Ranges, {{0x1f, 0x20}});// Test range that starts at end of first rangeAssertRangesDontIntersect(Ranges, {{0x20, 0x21}});// Test range that starts at end of first rangeAssertRangesIntersect(Ranges, {{0x20, 0x31}});// Test range that start before second range and ends before secondAssertRangesDontIntersect(Ranges, {{0x2f, 0x30}});// Test range that start before second range and ends in secondAssertRangesIntersect(Ranges, {{0x2f, 0x31}});// Test range that start at second rangeAssertRangesIntersect(Ranges, {{0x30, 0x31}});// Test range that start in second rangeAssertRangesIntersect(Ranges, {{0x31, 0x32}});// Test range that start at end of second rangeAssertRangesIntersect(Ranges, {{0x3f, 0x40}});// Test range that starts at end of second rangeAssertRangesDontIntersect(Ranges, {{0x40, 0x41}});AssertRangesDontIntersect(Ranges, {{0x20, 0x21}, {0x2f, 0x30}});AssertRangesIntersect(Ranges, {{0x20, 0x21}, {0x2f, 0x31}});}TEST(DWARFDebugInfo, TestDWARF64UnitLength) {static const char DebugInfoSecRaw[] ="\xff\xff\xff\xff" // DWARF64 mark"\x88\x77\x66\x55\x44\x33\x22\x11" // Length"\x05\x00" // Version"\x01" // DW_UT_compile"\x04" // Address size"\0\0\0\0\0\0\0\0"; // Offset Into Abbrev. Sec.StringMap<std::unique_ptr<MemoryBuffer>> Sections;Sections.insert(std::make_pair("debug_info", MemoryBuffer::getMemBuffer(StringRef(DebugInfoSecRaw, sizeof(DebugInfoSecRaw) - 1))));auto Context = DWARFContext::create(Sections, /* AddrSize = */ 4,/* isLittleEndian = */ true);const auto &Obj = Context->getDWARFObj();Obj.forEachInfoSections([&](const DWARFSection &Sec) {DWARFUnitHeader Header;DWARFDataExtractor Data(Obj, Sec, /* IsLittleEndian = */ true,/* AddressSize = */ 4);uint64_t Offset = 0;EXPECT_FALSE(Header.extract(*Context, Data, &Offset, DW_SECT_INFO));// Header.extract() returns false because there is not enough space// in the section for the declared length. Anyway, we can check that// the properties are read correctly.ASSERT_EQ(DwarfFormat::DWARF64, Header.getFormat());ASSERT_EQ(0x1122334455667788ULL, Header.getLength());ASSERT_EQ(5, Header.getVersion());ASSERT_EQ(DW_UT_compile, Header.getUnitType());ASSERT_EQ(4, Header.getAddressByteSize());// Check that the length can be correctly read in the unit class.DWARFUnitVector DummyUnitVector;DWARFSection DummySec;DWARFCompileUnit CU(*Context, Sec, Header, /* DA = */ 0, /* RS = */ 0,/* LocSection = */ 0, /* SS = */ StringRef(),/* SOS = */ DummySec, /* AOS = */ 0,/* LS = */ DummySec, /* LE = */ true,/* isDWO= */ false, DummyUnitVector);ASSERT_EQ(0x1122334455667788ULL, CU.getLength());});}} // end anonymous namespace
//===- llvm/unittest/DebugInfo/DWARFDebugFrameTest.cpp --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"#include "llvm/ADT/DenseSet.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/StringRef.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/DebugInfo/DIContext.h"#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {dwarf::CIE createCIE(bool IsDWARF64, uint64_t Offset, uint64_t Length) {return dwarf::CIE(IsDWARF64, Offset, Length,/*Version=*/3,/*Augmentation=*/StringRef(),/*AddressSize=*/8,/*SegmentDescriptorSize=*/0,/*CodeAlignmentFactor=*/1,/*DataAlignmentFactor=*/-8,/*ReturnAddressRegister=*/16,/*AugmentationData=*/StringRef(),/*FDEPointerEncoding=*/dwarf::DW_EH_PE_absptr,/*LSDAPointerEncoding=*/dwarf::DW_EH_PE_omit,/*Personality=*/None,/*PersonalityEnc=*/None,/*Arch=*/Triple::x86_64);}void expectDumpResult(const dwarf::CIE &TestCIE, bool IsEH,StringRef ExpectedFirstLine) {std::string Output;raw_string_ostream OS(Output);TestCIE.dump(OS, DIDumpOptions(), /*MRI=*/nullptr, IsEH);OS.flush();StringRef FirstLine = StringRef(Output).split('\n').first;EXPECT_EQ(FirstLine, ExpectedFirstLine);}void expectDumpResult(const dwarf::FDE &TestFDE, bool IsEH,StringRef ExpectedFirstLine) {std::string Output;raw_string_ostream OS(Output);TestFDE.dump(OS, DIDumpOptions(), /*MRI=*/nullptr, IsEH);OS.flush();StringRef FirstLine = StringRef(Output).split('\n').first;EXPECT_EQ(FirstLine, ExpectedFirstLine);}TEST(DWARFDebugFrame, DumpDWARF32CIE) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x1111abcd,/*Length=*/0x2222abcd);expectDumpResult(TestCIE, /*IsEH=*/false, "1111abcd 2222abcd ffffffff CIE");}TEST(DWARFDebugFrame, DumpDWARF64CIE) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true,/*Offset=*/0x1111abcdabcd,/*Length=*/0x2222abcdabcd);expectDumpResult(TestCIE, /*IsEH=*/false,"1111abcdabcd 00002222abcdabcd ffffffffffffffff CIE");}TEST(DWARFDebugFrame, DumpEHCIE) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x1000,/*Length=*/0x20);expectDumpResult(TestCIE, /*IsEH=*/true, "00001000 00000020 00000000 CIE");}TEST(DWARFDebugFrame, DumpEH64CIE) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true,/*Offset=*/0x1000,/*Length=*/0x20);expectDumpResult(TestCIE, /*IsEH=*/true,"00001000 0000000000000020 00000000 CIE");}TEST(DWARFDebugFrame, DumpDWARF64FDE) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true,/*Offset=*/0x1111abcdabcd,/*Length=*/0x2222abcdabcd);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x5555abcdabcd,/*AddressRange=*/0x111111111111,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);expectDumpResult(TestFDE, /*IsEH=*/false,"3333abcdabcd 00004444abcdabcd 00001111abcdabcd FDE ""cie=1111abcdabcd pc=5555abcdabcd...6666bcdebcde");}TEST(DWARFDebugFrame, DumpEH64FDE) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true,/*Offset=*/0x1111ab9a000c,/*Length=*/0x20);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x1111abcdabcd,/*Length=*/0x2222abcdabcd,/*CIEPointer=*/0x33abcd,/*InitialLocation=*/0x4444abcdabcd,/*AddressRange=*/0x111111111111,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);expectDumpResult(TestFDE, /*IsEH=*/true,"1111abcdabcd 00002222abcdabcd 0033abcd FDE ""cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde");}static Error parseCFI(dwarf::CIE &C, ArrayRef<uint8_t> Instructions,Optional<uint64_t> Size = None) {DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true,/*AddressSize=*/8);uint64_t Offset = 0;const uint64_t EndOffset = Size ? *Size : (uint64_t)Instructions.size();return C.cfis().parse(Data, &Offset, EndOffset);}static Error parseCFI(dwarf::FDE &FDE, ArrayRef<uint8_t> Instructions) {DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true,/*AddressSize=*/8);uint64_t Offset = 0;return FDE.cfis().parse(Data, &Offset, Instructions.size());}TEST(DWARFDebugFrame, InvalidCFIOpcodesTest) {llvm::DenseSet<uint8_t> ValidExtendedOpcodes = {dwarf::DW_CFA_nop,dwarf::DW_CFA_advance_loc,dwarf::DW_CFA_offset,dwarf::DW_CFA_restore,dwarf::DW_CFA_set_loc,dwarf::DW_CFA_advance_loc1,dwarf::DW_CFA_advance_loc2,dwarf::DW_CFA_advance_loc4,dwarf::DW_CFA_offset_extended,dwarf::DW_CFA_restore_extended,dwarf::DW_CFA_undefined,dwarf::DW_CFA_same_value,dwarf::DW_CFA_register,dwarf::DW_CFA_remember_state,dwarf::DW_CFA_restore_state,dwarf::DW_CFA_def_cfa,dwarf::DW_CFA_def_cfa_register,dwarf::DW_CFA_def_cfa_offset,dwarf::DW_CFA_def_cfa_expression,dwarf::DW_CFA_expression,dwarf::DW_CFA_offset_extended_sf,dwarf::DW_CFA_def_cfa_sf,dwarf::DW_CFA_def_cfa_offset_sf,dwarf::DW_CFA_LLVM_def_aspace_cfa,dwarf::DW_CFA_LLVM_def_aspace_cfa_sf,dwarf::DW_CFA_val_offset,dwarf::DW_CFA_val_offset_sf,dwarf::DW_CFA_val_expression,dwarf::DW_CFA_MIPS_advance_loc8,dwarf::DW_CFA_GNU_window_save,dwarf::DW_CFA_AARCH64_negate_ra_state,dwarf::DW_CFA_GNU_args_size};dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);// See DWARF standard v3, section 7.23: low 6 bits are used to encode an// extended opcode.for (uint8_t Code = 0; Code <= 63; ++Code) {if (ValidExtendedOpcodes.count(Code))continue;EXPECT_THAT_ERROR(parseCFI(TestCIE, Code),FailedWithMessage(("invalid extended CFI opcode 0x" +Twine::utohexstr(Code)).str().c_str()));}}// Here we test how truncated Call Frame Instructions are parsed.TEST(DWARFDebugFrame, ParseTruncatedCFITest) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);// Having an empty instructions list is fine.EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());// Unable to read an opcode, because the instructions list is empty, but we// say to the parser that it is not.EXPECT_THAT_ERROR(parseCFI(TestCIE, {}, /*Size=*/1),FailedWithMessage("unexpected end of data at offset 0x0 while reading [0x0, 0x1)"));// Unable to read a truncated DW_CFA_offset instruction.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_offset}),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed uleb128, extends past end"));// Unable to read a truncated DW_CFA_set_loc instruction.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_set_loc}),FailedWithMessage("unexpected end of data at offset 0x1 while reading [0x1, 0x9)"));// Unable to read a truncated DW_CFA_advance_loc1 instruction.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc1}),FailedWithMessage("unexpected end of data at offset 0x1 while reading [0x1, 0x2)"));// Unable to read a truncated DW_CFA_advance_loc2 instruction.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc2}),FailedWithMessage("unexpected end of data at offset 0x1 while reading [0x1, 0x3)"));// Unable to read a truncated DW_CFA_advance_loc4 instruction.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc4}),FailedWithMessage("unexpected end of data at offset 0x1 while reading [0x1, 0x5)"));// A test for an instruction with a single ULEB128 operand.auto CheckOp_ULEB128 = [&](uint8_t Inst) {EXPECT_THAT_ERROR(parseCFI(TestCIE, Inst),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed uleb128, extends past end"));};for (uint8_t Inst :{dwarf::DW_CFA_restore_extended, dwarf::DW_CFA_undefined,dwarf::DW_CFA_same_value, dwarf::DW_CFA_def_cfa_register,dwarf::DW_CFA_def_cfa_offset, dwarf::DW_CFA_GNU_args_size})CheckOp_ULEB128(Inst);// Unable to read a truncated DW_CFA_def_cfa_offset_sf instruction.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_offset_sf}),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed sleb128, extends past end"));// A test for an instruction with two ULEB128 operands.auto CheckOp_ULEB128_ULEB128 = [&](uint8_t Inst) {EXPECT_THAT_ERROR(parseCFI(TestCIE, Inst),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed uleb128, extends past end"));EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst, /*Op1=*/0}),FailedWithMessage("unable to decode LEB128 at offset 0x00000002: ""malformed uleb128, extends past end"));};for (uint8_t Inst : {dwarf::DW_CFA_offset_extended, dwarf::DW_CFA_register,dwarf::DW_CFA_def_cfa, dwarf::DW_CFA_LLVM_def_aspace_cfa,dwarf::DW_CFA_val_offset})CheckOp_ULEB128_ULEB128(Inst);// A test for an instruction with two operands: ULEB128, SLEB128.auto CheckOp_ULEB128_SLEB128 = [&](uint8_t Inst) {EXPECT_THAT_ERROR(parseCFI(TestCIE, Inst),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed uleb128, extends past end"));EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst, /*Op1=*/0}),FailedWithMessage("unable to decode LEB128 at offset 0x00000002: ""malformed sleb128, extends past end"));};for (uint8_t Inst :{dwarf::DW_CFA_offset_extended_sf, dwarf::DW_CFA_def_cfa_sf,dwarf::DW_CFA_LLVM_def_aspace_cfa_sf, dwarf::DW_CFA_val_offset_sf})CheckOp_ULEB128_SLEB128(Inst);// Unable to read a truncated DW_CFA_def_cfa_expression instruction.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression}),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed uleb128, extends past end"));EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,/*expression length=*/0x1}),FailedWithMessage("unexpected end of data at offset 0x2 while reading [0x2, 0x3)"));// The DW_CFA_def_cfa_expression can contain a zero length expression.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,/*ExprLen=*/0}),Succeeded());// A test for an instruction with three operands: ULEB128, expression length// (ULEB128) and expression bytes.auto CheckOp_ULEB128_Expr = [&](uint8_t Inst) {EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst}),FailedWithMessage("unable to decode LEB128 at offset 0x00000001: ""malformed uleb128, extends past end"));EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst, /*Op1=*/0}),FailedWithMessage("unable to decode LEB128 at offset 0x00000002: ""malformed uleb128, extends past end"));// A zero length expression is fineEXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst,/*Op1=*/0, /*ExprLen=*/0}),Succeeded());EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst,/*Op1=*/0, /*ExprLen=*/1}),FailedWithMessage("unexpected end of data at offset 0x3 while reading [0x3, 0x4)"));};for (uint8_t Inst : {dwarf::DW_CFA_expression, dwarf::DW_CFA_val_expression})CheckOp_ULEB128_Expr(Inst);}void expectDumpResult(const dwarf::UnwindLocation &Loc,StringRef ExpectedFirstLine) {std::string Output;raw_string_ostream OS(Output);OS << Loc;OS.flush();StringRef FirstLine = StringRef(Output).split('\n').first;EXPECT_EQ(FirstLine, ExpectedFirstLine);}TEST(DWARFDebugFrame, DumpUnwindLocations) {// Test constructing unwind locations and dumping each kind.constexpr int32_t PlusOff = 8;constexpr int32_t MinusOff = -8;constexpr uint8_t RegNum = 12;expectDumpResult(dwarf::UnwindLocation::createUnspecified(), "unspecified");expectDumpResult(dwarf::UnwindLocation::createUndefined(), "undefined");expectDumpResult(dwarf::UnwindLocation::createSame(), "same");expectDumpResult(dwarf::UnwindLocation::createIsCFAPlusOffset(PlusOff),"CFA+8");expectDumpResult(dwarf::UnwindLocation::createIsCFAPlusOffset(MinusOff),"CFA-8");expectDumpResult(dwarf::UnwindLocation::createAtCFAPlusOffset(PlusOff),"[CFA+8]");expectDumpResult(dwarf::UnwindLocation::createAtCFAPlusOffset(MinusOff),"[CFA-8]");expectDumpResult(dwarf::UnwindLocation::createIsRegisterPlusOffset(RegNum, PlusOff),"reg12+8");expectDumpResult(dwarf::UnwindLocation::createIsRegisterPlusOffset(RegNum, MinusOff),"reg12-8");expectDumpResult(dwarf::UnwindLocation::createAtRegisterPlusOffset(RegNum, PlusOff),"[reg12+8]");expectDumpResult(dwarf::UnwindLocation::createAtRegisterPlusOffset(RegNum, MinusOff),"[reg12-8]");expectDumpResult(dwarf::UnwindLocation::createIsConstant(12), "12");expectDumpResult(dwarf::UnwindLocation::createIsConstant(-32), "-32");}void expectDumpResult(const dwarf::RegisterLocations &Locs,StringRef ExpectedFirstLine) {std::string Output;raw_string_ostream OS(Output);OS << Locs;OS.flush();StringRef FirstLine = StringRef(Output).split('\n').first;EXPECT_EQ(FirstLine, ExpectedFirstLine);}TEST(DWARFDebugFrame, RegisterLocations) {// Test the functionality of the RegisterLocations class.dwarf::RegisterLocations Locs;expectDumpResult(Locs, "");EXPECT_FALSE(Locs.hasLocations());// Set a register location for reg12 to unspecified and verify it dumps// correctly.Locs.setRegisterLocation(12, dwarf::UnwindLocation::createUnspecified());EXPECT_TRUE(Locs.hasLocations());expectDumpResult(Locs, "reg12=unspecified");// Replace the register location for reg12 to "same" and verify it dumps// correctly after it is modifiedLocs.setRegisterLocation(12, dwarf::UnwindLocation::createSame());EXPECT_TRUE(Locs.hasLocations());expectDumpResult(Locs, "reg12=same");// Remove the register location for reg12 verify it dumps correctly after it// is removed.Locs.removeRegisterLocation(12);EXPECT_FALSE(Locs.hasLocations());expectDumpResult(Locs, "");// Verify multiple registers added to the list dump correctly.auto Reg12Loc = dwarf::UnwindLocation::createAtCFAPlusOffset(4);auto Reg13Loc = dwarf::UnwindLocation::createAtCFAPlusOffset(8);auto Reg14Loc = dwarf::UnwindLocation::createSame();Locs.setRegisterLocation(12, Reg12Loc);Locs.setRegisterLocation(13, Reg13Loc);Locs.setRegisterLocation(14, Reg14Loc);EXPECT_TRUE(Locs.hasLocations());expectDumpResult(Locs, "reg12=[CFA+4], reg13=[CFA+8], reg14=same");// Verify RegisterLocations::getRegisterLocation() works as expected.Optional<dwarf::UnwindLocation> OptionalLoc;OptionalLoc = Locs.getRegisterLocation(0);EXPECT_FALSE(OptionalLoc.has_value());OptionalLoc = Locs.getRegisterLocation(12);EXPECT_TRUE(OptionalLoc.has_value());EXPECT_EQ(*OptionalLoc, Reg12Loc);OptionalLoc = Locs.getRegisterLocation(13);EXPECT_TRUE(OptionalLoc.has_value());EXPECT_EQ(*OptionalLoc, Reg13Loc);OptionalLoc = Locs.getRegisterLocation(14);EXPECT_TRUE(OptionalLoc.has_value());EXPECT_EQ(*OptionalLoc, Reg14Loc);// Verify registers are correctly removed when multiple exist in the list.Locs.removeRegisterLocation(13);EXPECT_FALSE(Locs.getRegisterLocation(13).has_value());EXPECT_TRUE(Locs.hasLocations());expectDumpResult(Locs, "reg12=[CFA+4], reg14=same");Locs.removeRegisterLocation(14);EXPECT_FALSE(Locs.getRegisterLocation(14).has_value());EXPECT_TRUE(Locs.hasLocations());expectDumpResult(Locs, "reg12=[CFA+4]");Locs.removeRegisterLocation(12);EXPECT_FALSE(Locs.getRegisterLocation(12).has_value());EXPECT_FALSE(Locs.hasLocations());expectDumpResult(Locs, "");}// Test that empty rows are not added to UnwindTable when// dwarf::CIE::CFIs or dwarf::FDE::CFIs is empty.TEST(DWARFDebugFrame, UnwindTableEmptyRows) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);// Having an empty instructions list is fine.EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());EXPECT_TRUE(TestCIE.cfis().empty());// Verify dwarf::UnwindTable::create() won't result in errors and// and empty rows are not added to CIE UnwindTable.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestCIE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const size_t ExpectedNumOfRows = 0;EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Having an empty instructions list is fine.EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded());EXPECT_TRUE(TestFDE.cfis().empty());// Verify dwarf::UnwindTable::create() won't result in errors and// and empty rows are not added to FDE UnwindTable.RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);}// Test that empty rows are not added to UnwindTable when dwarf::CIE::CFIs// or dwarf::FDE::CFIs is not empty but has only DW_CFA_nop instructions.TEST(DWARFDebugFrame, UnwindTableEmptyRows_NOPs) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);// Make a CIE that has only DW_CFA_nop instructions.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_nop}), Succeeded());EXPECT_TRUE(!TestCIE.cfis().empty());// Verify dwarf::UnwindTable::create() won't result in errors and// and empty rows are not added to CIE UnwindTable.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestCIE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const size_t ExpectedNumOfRows = 0;EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make an FDE that has only DW_CFA_nop instructions.EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_nop}), Succeeded());EXPECT_TRUE(!TestFDE.cfis().empty());// Verify dwarf::UnwindTable::create() won't result in errors and// and empty rows are not added to FDE UnwindTable.RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);}TEST(DWARFDebugFrame, UnwindTableErrorNonAscendingFDERows) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition.constexpr uint8_t Reg = 12;constexpr uint8_t Offset = 32;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that have valid// syntax, but will cause an error when we parse them into a UnwindTable.// Here we encode two DW_CFA_set_loc opcodes:// DW_CFA_set_loc(0x1100)// DW_CFA_set_loc(0x1000)// These opcodes cause a new row to be appended to the rows in a UnwindTable// and the resulting rows are not in ascending address order and should cause// a state machine error.EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_set_loc, 0x00, 0x11, 0, 0, 0, 0, 0, 0,dwarf::DW_CFA_set_loc, 0x00, 0x10, 0, 0, 0, 0, 0, 0}),Succeeded());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(),FailedWithMessage("DW_CFA_set_loc with adrress 0x1000 which"" must be greater than the current row ""address 0x1100"));}TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_restore_state) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition.constexpr uint8_t Reg = 12;constexpr uint8_t Offset = 32;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that have valid// syntax, but will cause an error when we parse them into a UnwindTable.// Here we encode a DW_CFA_restore_state opcode that was not preceded by a// DW_CFA_remember_state, and an error should be returned.EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_restore_state}),Succeeded());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(),FailedWithMessage("DW_CFA_restore_state without a matching ""previous DW_CFA_remember_state"));}TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_GNU_window_save) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition.constexpr uint8_t Reg = 12;constexpr uint8_t Offset = 32;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that have valid// syntax, but will cause an error when we parse them into a UnwindTable.// Here we encode a DW_CFA_GNU_window_save that is not supported. I have not// found any documentation that describes what this does after some brief// searching.EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_GNU_window_save}),Succeeded());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(),FailedWithMessage("DW_CFA opcode 0x2d is not supported for ""architecture x86_64"));}TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_def_cfa_offset) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has an invalid CFA definition. We do this so we can try// and use a DW_CFA_def_cfa_register opcode in the FDE and get an appropriate// error back.EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());// Make a FDE with DWARF call frame instruction opcodes that have valid// syntax, but will cause an error when we parse them into a UnwindTable.// Here we encode a DW_CFA_def_cfa_offset with a offset of 16, but our CIE// didn't define the CFA in terms of a register plus offset, so this should// cause an error.EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_def_cfa_offset, 16}),Succeeded());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(),FailedWithMessage("DW_CFA_def_cfa_offset found when CFA ""rule was not RegPlusOffset"));}TEST(DWARFDebugFrame, UnwindTableDefCFAOffsetSFCFAError) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has an invalid CFA definition. We do this so we can try// and use a DW_CFA_def_cfa_offset_sf opcode in the FDE and get an// appropriate error back.EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());// Make a FDE with DWARF call frame instruction opcodes that have valid// syntax, but will cause an error when we parse them into a UnwindTable.// Here we encode a DW_CFA_def_cfa_offset_sf with a offset of 4, but our CIE// didn't define the CFA in terms of a register plus offset, so this should// cause an error.EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_def_cfa_offset_sf, 4}),Succeeded());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(),FailedWithMessage("DW_CFA_def_cfa_offset_sf found when CFA ""rule was not RegPlusOffset"));}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa_register) {dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has only defines the CFA register with no offset. Some// architectures do this and we must ensure that we set the CFA value to be// equal to that register with no offset.constexpr uint8_t CFAReg = 12;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_register, CFAReg}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that have valid// syntax, but will cause an error when we parse them into a UnwindTable.// Here we encode a DW_CFA_def_cfa_register with a register number of 12, but// our CIE didn't define the CFA in terms of a register plus offset, so this// should cause an error.EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg, 0));}TEST(DWARFDebugFrame, UnwindTableRowPushingOpcodes) {// Test all opcodes that should end up pushing a UnwindRow into a UnwindTable.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.constexpr uint8_t CFAReg = 12;constexpr uint8_t CFAOffset = 32;constexpr uint8_t Reg = 13;constexpr uint8_t InReg = 14;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,dwarf::DW_CFA_register, Reg, InReg}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that use all of the// row pushing opcodes. This will verify that all opcodes that should create// a row are correctly working. Each opcode will push a row prior to// advancing the address, and then a row will be automatically pushed at the// end of the parsing, so we should end up with 6 rows starting at address// 0x1000 (from the FDE) and incrementing each one by 4 * CodeAlignmentFactor// from the CIE.EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_advance_loc1,4,dwarf::DW_CFA_advance_loc2,4,0,dwarf::DW_CFA_advance_loc4,4,0,0,0,dwarf::DW_CFA_set_loc,0x14,0x10,0,0,0,0,0,0}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);ASSERT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 6u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[1].getAddress(), 0x1004u);EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[2].getAddress(), 0x1008u);EXPECT_EQ(Rows[2].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[3].getAddress(), 0x100cu);EXPECT_EQ(Rows[3].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[4].getAddress(), 0x1010u);EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[5].getAddress(), 0x1014u);EXPECT_EQ(Rows[5].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[5].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore) {// Test that DW_CFA_restore works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.constexpr uint8_t CFAReg = 12;constexpr uint8_t CFAOffset = 32;constexpr uint8_t Reg = 13;constexpr uint8_t InReg = 14;constexpr int32_t RegCFAOffset = -8;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,dwarf::DW_CFA_register, Reg, InReg}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that changes the rule// for register "Reg" to be [CFA-8], then push a row, and then restore the// register unwind rule for "Reg" using DW_CFA_restore. We should end up with// two rows:// - one with Reg = [CFA-8]// - one with Reg = InRegEXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_offset | Reg, 1,dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_restore | Reg}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs1;VerifyLocs1.setRegisterLocation(Reg, dwarf::UnwindLocation::createAtCFAPlusOffset(RegCFAOffset));dwarf::RegisterLocations VerifyLocs2;VerifyLocs2.setRegisterLocation(Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 2u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);EXPECT_EQ(Rows[1].getAddress(), 0x1004u);EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore_extended) {// Test that DW_CFA_restore works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.constexpr uint8_t CFAReg = 12;constexpr uint8_t CFAOffset = 32;constexpr uint8_t Reg = 13;constexpr uint8_t InReg = 14;constexpr int32_t RegCFAOffset = -8;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,dwarf::DW_CFA_register, Reg, InReg}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that changes the rule// for register "Reg" to be [CFA-8], then push a row, and then restore the// register unwind rule for "Reg" using DW_CFA_restore_extended. We should// end up with two rows:// - one with Reg = [CFA-8]// - one with Reg = InRegEXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_offset | Reg, 1,dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_restore_extended, Reg}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs1;VerifyLocs1.setRegisterLocation(Reg, dwarf::UnwindLocation::createAtCFAPlusOffset(RegCFAOffset));dwarf::RegisterLocations VerifyLocs2;VerifyLocs2.setRegisterLocation(Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 2u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);EXPECT_EQ(Rows[1].getAddress(), 0x1004u);EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_offset) {// Test that DW_CFA_offset, DW_CFA_offset_extended and// DW_CFA_offset_extended_sf work as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that changes the// unwind rules for the follwing registers:// Reg1 = [CFA-8]// Reg2 = [CFA-16]// Reg3 = [CFA+8]constexpr uint8_t Reg1 = 14;constexpr uint8_t Reg2 = 15;constexpr uint8_t Reg3 = 16;constexpr uint8_t Neg1SLEB = 0x7f;EXPECT_THAT_ERROR(parseCFI(TestFDE,{dwarf::DW_CFA_offset | Reg1, 1, dwarf::DW_CFA_offset_extended,Reg2, 2, dwarf::DW_CFA_offset_extended_sf, Reg3, Neg1SLEB}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));VerifyLocs.setRegisterLocation(Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));VerifyLocs.setRegisterLocation(Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(8));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_offset) {// Test that DW_CFA_val_offset and DW_CFA_val_offset_sf work as expected when// parsed in the state machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that changes the// unwind rules for the follwing registers:// Reg1 = [CFA-8]// Reg2 = [CFA-16]// Reg3 = [CFA+8]constexpr uint8_t Reg1 = 14;constexpr uint8_t Reg2 = 15;constexpr uint8_t Neg1SLEB = 0x7f;EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_val_offset, Reg1, 1,dwarf::DW_CFA_val_offset_sf, Reg2, Neg1SLEB}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createIsCFAPlusOffset(-8));VerifyLocs.setRegisterLocation(Reg2, dwarf::UnwindLocation::createIsCFAPlusOffset(8));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_nop) {// Test that DW_CFA_nop works as expected when parsed in the state machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that changes the// unwind rules for the follwing registers:// Reg1 = [CFA-8]// The opcodes for setting Reg1 are preceded by a DW_CFA_nop.constexpr uint8_t Reg1 = 14;EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_nop, dwarf::DW_CFA_offset | Reg1, 1}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_remember_state) {// Test that DW_CFA_remember_state and DW_CFA_restore_state work as expected// when parsed in the state machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that encodes the// follwing rows:// 0x1000: CFA=reg12+32: Reg1=[CFA-8]// 0x1004: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16]// 0x1008: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16] Reg3=[CFA-24]// 0x100C: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16]// 0x1010: CFA=reg12+32: Reg1=[CFA-8]// This state machine will:// - set Reg1 location// - push a row (from DW_CFA_advance_loc)// - remember the state// - set Reg2 location// - push a row (from DW_CFA_advance_loc)// - remember the state// - set Reg3 location// - push a row (from DW_CFA_advance_loc)// - remember the state where Reg1 and Reg2 were set// - push a row (from DW_CFA_advance_loc)// - remember the state where only Reg1 was set// - push a row (automatically at the end of instruction parsing)// Then we verify that all registers are correct in all generated rows.constexpr uint8_t Reg1 = 14;constexpr uint8_t Reg2 = 15;constexpr uint8_t Reg3 = 16;EXPECT_THAT_ERROR(parseCFI(TestFDE,{dwarf::DW_CFA_offset | Reg1, 1, dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_remember_state, dwarf::DW_CFA_offset | Reg2, 2,dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_remember_state,dwarf::DW_CFA_offset | Reg3, 3, dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_restore_state, dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_restore_state}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs1;VerifyLocs1.setRegisterLocation(Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));dwarf::RegisterLocations VerifyLocs2;VerifyLocs2.setRegisterLocation(Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));VerifyLocs2.setRegisterLocation(Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));dwarf::RegisterLocations VerifyLocs3;VerifyLocs3.setRegisterLocation(Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));VerifyLocs3.setRegisterLocation(Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));VerifyLocs3.setRegisterLocation(Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(-24));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 5u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);EXPECT_EQ(Rows[1].getAddress(), 0x1004u);EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);EXPECT_EQ(Rows[2].getAddress(), 0x1008u);EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs3);EXPECT_EQ(Rows[3].getAddress(), 0x100Cu);EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs2);EXPECT_EQ(Rows[4].getAddress(), 0x1010u);EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs1);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_undefined) {// Test that DW_CFA_undefined works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that encodes the// follwing rows:// 0x1000: CFA=reg12+32: Reg1=undefined// Then we verify that all registers are correct in all generated rows.constexpr uint8_t Reg1 = 14;EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_undefined, Reg1}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg1,dwarf::UnwindLocation::createUndefined());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_same_value) {// Test that DW_CFA_same_value works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that encodes the// follwing rows:// 0x1000: CFA=reg12+32: Reg1=same// Then we verify that all registers are correct in all generated rows.constexpr uint8_t Reg1 = 14;EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_same_value, Reg1}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createSame());// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_register) {// Test that DW_CFA_register works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that encodes the// follwing rows:// 0x1000: CFA=reg12+32: Reg1=same// Then we verify that all registers are correct in all generated rows.constexpr uint8_t Reg = 13;constexpr uint8_t InReg = 14;EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_register, Reg, InReg}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_expression) {// Test that DW_CFA_expression works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that encodes the// follwing rows:// 0x1000: CFA=reg12+32: Reg1=DWARFExpr(DW_OP_reg12)// Then we verify that all registers are correct in all generated rows.constexpr uint8_t Reg = 13;constexpr uint8_t AddrSize = 8;std::vector<uint8_t> CFIBytes = {dwarf::DW_CFA_expression, Reg, 1,dwarf::DW_OP_reg12};EXPECT_THAT_ERROR(parseCFI(TestFDE, CFIBytes), Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;std::vector<uint8_t> ExprBytes = {dwarf::DW_OP_reg12};DataExtractor ExprData(ExprBytes, true, AddrSize);DWARFExpression Expr(ExprData, AddrSize);VerifyLocs.setRegisterLocation(Reg, dwarf::UnwindLocation::createAtDWARFExpression(Expr));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_expression) {// Test that DW_CFA_val_expression works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that encodes the// follwing rows:// 0x1000: CFA=reg12+32: Reg1=DWARFExpr(DW_OP_reg12)// Then we verify that all registers are correct in all generated rows.constexpr uint8_t Reg = 13;constexpr uint8_t AddrSize = 8;std::vector<uint8_t> CFIBytes = {dwarf::DW_CFA_val_expression, Reg, 1,dwarf::DW_OP_reg12};EXPECT_THAT_ERROR(parseCFI(TestFDE, CFIBytes), Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;std::vector<uint8_t> ExprBytes = {dwarf::DW_OP_reg12};DataExtractor ExprData(ExprBytes, true, AddrSize);DWARFExpression Expr(ExprData, AddrSize);VerifyLocs.setRegisterLocation(Reg, dwarf::UnwindLocation::createIsDWARFExpression(Expr));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 1u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa) {// Test that DW_CFA_def_cfa, DW_CFA_def_cfa_sf, DW_CFA_def_cfa_register,// DW_CFA_def_cfa_offset, and DW_CFA_def_cfa_offset_sf works as expected when// parsed in the state machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.constexpr uint8_t CFAReg1 = 12;constexpr uint8_t CFAOff1 = 32;constexpr uint8_t CFAReg2 = 13;constexpr uint8_t CFAOff2 = 48;constexpr uint8_t Reg = 13;constexpr uint8_t InReg = 14;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg1, CFAOff1,dwarf::DW_CFA_register, Reg, InReg}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that use all of the// DW_CFA_def_cfa* opcodes. This will verify that all opcodes that should// create a row are correctly working.EXPECT_THAT_ERROR(parseCFI(TestFDE,{dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_register,CFAReg2, dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_def_cfa_offset, CFAOff2,dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_offset_sf,0x7c, // -4 SLEB to make offset = 32 (CFAOff1)dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_sf, CFAReg1,0x7a, // -6 SLEB to make CFA offset 48 (CFAOff2)}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 5u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff1));EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[1].getAddress(), 0x1004u);EXPECT_EQ(Rows[1].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1));EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[2].getAddress(), 0x1008u);EXPECT_EQ(Rows[2].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff2));EXPECT_EQ(Rows[2].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[3].getAddress(), 0x100cu);EXPECT_EQ(Rows[3].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1));EXPECT_EQ(Rows[3].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[4].getAddress(), 0x1010u);EXPECT_EQ(Rows[4].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff2));EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);}TEST(DWARFDebugFrame, UnwindTable_DW_CFA_LLVM_def_aspace_cfa) {// Test that DW_CFA_LLVM_def_aspace_cfa, DW_CFA_LLVM_def_aspace_cfa_sf,// DW_CFA_def_cfa_register, DW_CFA_def_cfa_offset, and// DW_CFA_def_cfa_offset_sf works as expected when parsed in the state// machine.dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,/*Offset=*/0x0,/*Length=*/0xff);dwarf::FDE TestFDE(/*IsDWARF64=*/true,/*Offset=*/0x3333abcdabcd,/*Length=*/0x4444abcdabcd,/*CIEPointer=*/0x1111abcdabcd,/*InitialLocation=*/0x1000,/*AddressRange=*/0x1000,/*Cie=*/&TestCIE,/*LSDAAddress=*/None,/*Arch=*/Triple::x86_64);// Make a CIE that has a valid CFA definition and a single register unwind// rule for register that we will verify is in all of the pushed rows.constexpr uint8_t CFAReg1 = 12;constexpr uint8_t CFAOff1 = 32;constexpr uint8_t CFAReg2 = 13;constexpr uint8_t CFAOff2 = 48;constexpr uint8_t Reg = 13;constexpr uint8_t InReg = 14;constexpr uint8_t AddrSpace = 2;EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_LLVM_def_aspace_cfa, CFAReg1, CFAOff1,AddrSpace, dwarf::DW_CFA_register, Reg, InReg}),Succeeded());// Make a FDE with DWARF call frame instruction opcodes that use all of the// DW_CFA_def_cfa* opcodes. This will verify that all opcodes that should// create a row are correctly working.EXPECT_THAT_ERROR(parseCFI(TestFDE,{dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_register,CFAReg2, dwarf::DW_CFA_advance_loc | 4,dwarf::DW_CFA_def_cfa_offset, CFAOff2,dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_offset_sf,0x7c, // -4 SLEB to make offset = 32 (CFAOff1)dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_sf, CFAReg1,0x7a, // -6 SLEB to make CFA offset 48 (CFAOff2)}),Succeeded());// Create locations that we expect the UnwindRow objects to contain after// parsing the DWARF call frame instructions.dwarf::RegisterLocations VerifyLocs;VerifyLocs.setRegisterLocation(Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));// Verify we catch state machine error.Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());const dwarf::UnwindTable &Rows = RowsOrErr.get();EXPECT_EQ(Rows.size(), 5u);EXPECT_EQ(Rows[0].getAddress(), 0x1000u);EXPECT_EQ(Rows[0].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff1,AddrSpace));EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[1].getAddress(), 0x1004u);EXPECT_EQ(Rows[1].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1,AddrSpace));EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[2].getAddress(), 0x1008u);EXPECT_EQ(Rows[2].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff2,AddrSpace));EXPECT_EQ(Rows[2].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[3].getAddress(), 0x100cu);EXPECT_EQ(Rows[3].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1,AddrSpace));EXPECT_EQ(Rows[3].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs);EXPECT_EQ(Rows[4].getAddress(), 0x1010u);EXPECT_EQ(Rows[4].getCFAValue(),dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff2,AddrSpace));EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);}} // end anonymous namespace
//===- llvm/unittest/DebugInfo/DWARFDebugArangeSetTest.cpp-----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h"#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct WarningHandler {~WarningHandler() { EXPECT_THAT_ERROR(std::move(Err), Succeeded()); }void operator()(Error E) { Err = joinErrors(std::move(Err), std::move(E)); }Error getWarning() { return std::move(Err); }Error Err = Error::success();};template <size_t SecSize>void ExpectExtractError(const char (&SecDataRaw)[SecSize],const char *ErrorMessage) {DWARFDataExtractor Extractor(StringRef(SecDataRaw, SecSize - 1),/* IsLittleEndian = */ true,/* AddressSize = */ 4);DWARFDebugArangeSet Set;uint64_t Offset = 0;WarningHandler Warnings;Error E = Set.extract(Extractor, &Offset, Warnings);ASSERT_TRUE(E.operator bool());EXPECT_STREQ(ErrorMessage, toString(std::move(E)).c_str());}TEST(DWARFDebugArangeSet, LengthExceedsSectionSize) {static const char DebugArangesSecRaw[] ="\x15\x00\x00\x00" // The length exceeds the section boundaries"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size"\x00\x00\x00\x00" // Padding"\x00\x00\x00\x00" // Termination tuple"\x00\x00\x00\x00";ExpectExtractError(DebugArangesSecRaw,"the length of address range table at offset 0x0 exceeds section size");}TEST(DWARFDebugArangeSet, LengthExceedsSectionSizeDWARF64) {static const char DebugArangesSecRaw[] ="\xff\xff\xff\xff" // DWARF64 mark"\x15\x00\x00\x00\x00\x00\x00\x00" // The length exceeds the section// boundaries"\x02\x00" // Version"\x00\x00\x00\x00\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size// No padding"\x00\x00\x00\x00" // Termination tuple"\x00\x00\x00\x00";ExpectExtractError(DebugArangesSecRaw,"the length of address range table at offset 0x0 exceeds section size");}TEST(DWARFDebugArangeSet, UnsupportedAddressSize) {static const char DebugArangesSecRaw[] ="\x0c\x00\x00\x00" // Length"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x03" // Address Size (not supported)"\x00" // Segment Selector Size// No padding"\x00\x00\x00\x00"; // Termination tupleExpectExtractError(DebugArangesSecRaw,"address range table at offset 0x0 has unsupported address size: 3 ""(supported are 2, 4, 8)");}TEST(DWARFDebugArangeSet, UnsupportedSegmentSelectorSize) {static const char DebugArangesSecRaw[] ="\x14\x00\x00\x00" // Length"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x04" // Segment Selector Size (not supported)// No padding"\x00\x00\x00\x00" // Termination tuple"\x00\x00\x00\x00""\x00\x00\x00\x00";ExpectExtractError(DebugArangesSecRaw,"non-zero segment selector size in address range table at offset 0x0 ""is not supported");}TEST(DWARFDebugArangeSet, NoTerminationEntry) {static const char DebugArangesSecRaw[] ="\x14\x00\x00\x00" // Length"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size"\x00\x00\x00\x00" // Padding"\x00\x00\x00\x00" // Entry: Address"\x01\x00\x00\x00" // Length; // No termination tupleExpectExtractError(DebugArangesSecRaw,"address range table at offset 0x0 is not terminated by null entry");}TEST(DWARFDebugArangeSet, ReservedUnitLength) {// Note: 12 is the minimum length to pass the basic check for the size of// the section. 1 will be automatically subtracted in ExpectExtractError().static const char DebugArangesSecRaw[12 + 1] ="\xf0\xff\xff\xff"; // Reserved unit length valueExpectExtractError(DebugArangesSecRaw,"parsing address ranges table at offset 0x0: unsupported ""reserved unit length of value 0xfffffff0");}TEST(DWARFDebugArangeSet, SectionTooShort) {// Note: 1 will be automatically subtracted in ExpectExtractError().static const char DebugArangesSecRaw[11 + 1] = {0};ExpectExtractError(DebugArangesSecRaw,"parsing address ranges table at offset 0x0: unexpected ""end of data at offset 0xb while reading [0xb, 0xc)");}TEST(DWARFDebugArangeSet, SectionTooShortDWARF64) {// Note: 1 will be automatically subtracted in ExpectExtractError().static const char DebugArangesSecRaw[23 + 1] ="\xff\xff\xff\xff"; // DWARF64 markExpectExtractError(DebugArangesSecRaw,"parsing address ranges table at offset 0x0: unexpected ""end of data at offset 0x17 while reading [0x17, 0x18)");}TEST(DWARFDebugArangeSet, NoSpaceForEntries) {static const char DebugArangesSecRaw[] ="\x0c\x00\x00\x00" // Length"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size"\x00\x00\x00\x00" // Padding; // No entriesExpectExtractError(DebugArangesSecRaw,"address range table at offset 0x0 has an insufficient length ""to contain any entries");}TEST(DWARFDebugArangeSet, UnevenLength) {static const char DebugArangesSecRaw[] ="\x1b\x00\x00\x00" // Length (not a multiple of tuple size)"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size"\x00\x00\x00\x00" // Padding"\x00\x00\x00\x00" // Entry: Address"\x01\x00\x00\x00" // Length"\x00\x00\x00\x00" // Termination tuple"\x00\x00\x00\x00";ExpectExtractError(DebugArangesSecRaw,"address range table at offset 0x0 has length that is not a multiple ""of the tuple size");}TEST(DWARFDebugArangeSet, ZeroAddressEntry) {static const char DebugArangesSecRaw[] ="\x1c\x00\x00\x00" // Length"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size"\x00\x00\x00\x00" // Padding"\x00\x00\x00\x00" // Entry1: Address"\x01\x00\x00\x00" // Length"\x00\x00\x00\x00" // Termination tuple"\x00\x00\x00\x00";DWARFDataExtractor Extractor(StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1),/*IsLittleEndian=*/true,/*AddressSize=*/4);DWARFDebugArangeSet Set;uint64_t Offset = 0;ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, WarningHandler()),Succeeded());auto Range = Set.descriptors();auto Iter = Range.begin();ASSERT_EQ(std::distance(Iter, Range.end()), 1);EXPECT_EQ(Iter->Address, 0u);EXPECT_EQ(Iter->Length, 1u);}TEST(DWARFDebugArangeSet, ZeroLengthEntry) {static const char DebugArangesSecRaw[] ="\x1c\x00\x00\x00" // Length"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size"\x00\x00\x00\x00" // Padding"\x01\x00\x00\x00" // Entry1: Address"\x00\x00\x00\x00" // Length"\x00\x00\x00\x00" // Termination tuple"\x00\x00\x00\x00";DWARFDataExtractor Extractor(StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1),/*IsLittleEndian=*/true,/*AddressSize=*/4);DWARFDebugArangeSet Set;uint64_t Offset = 0;ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, WarningHandler()),Succeeded());auto Range = Set.descriptors();auto Iter = Range.begin();ASSERT_EQ(std::distance(Iter, Range.end()), 1);EXPECT_EQ(Iter->Address, 1u);EXPECT_EQ(Iter->Length, 0u);}TEST(DWARFDebugArangesSet, PrematureTerminator) {static const char DebugArangesSecRaw[] ="\x24\x00\x00\x00" // Length"\x02\x00" // Version"\x00\x00\x00\x00" // Debug Info Offset"\x04" // Address Size"\x00" // Segment Selector Size"\x00\x00\x00\x00" // Padding"\x00\x00\x00\x00" // Entry1: Premature"\x00\x00\x00\x00" // terminator"\x01\x00\x00\x00" // Entry2: Address"\x01\x00\x00\x00" // Length"\x00\x00\x00\x00" // Termination tuple"\x00\x00\x00\x00";DWARFDataExtractor Extractor(StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1),/*IsLittleEndian=*/true,/*AddressSize=*/4);DWARFDebugArangeSet Set;uint64_t Offset = 0;WarningHandler Warnings;ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, Warnings), Succeeded());auto Range = Set.descriptors();auto Iter = Range.begin();ASSERT_EQ(std::distance(Iter, Range.end()), 2);EXPECT_EQ(Iter->Address, 0u);EXPECT_EQ(Iter->Length, 0u);++Iter;EXPECT_EQ(Iter->Address, 1u);EXPECT_EQ(Iter->Length, 1u);EXPECT_THAT_ERROR(Warnings.getWarning(),FailedWithMessage("address range table at offset 0x0 has a premature ""terminator entry at offset 0x10"));}} // end anonymous namespace
//===- DWARFDataExtractorTest.cpp -----------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/Object/ObjectFile.h"#include "llvm/ObjectYAML/yaml2obj.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(DWARFDataExtractorTest, getRelocatedValue) {StringRef Yaml = R"(!ELFFileHeader:Class: ELFCLASS32Data: ELFDATA2LSBType: ET_RELMachine: EM_386Sections:- Name: .textType: SHT_PROGBITSSize: 0x80- Name: .debug_lineType: SHT_PROGBITSContent: '000000000000'- Name: .rel.debug_lineType: SHT_RELInfo: .debug_lineRelocations:- Offset: 0Symbol: fType: R_386_32- Offset: 4Symbol: fType: R_386_32Symbols:- Name: fType: STT_SECTIONSection: .textValue: 0x42)";SmallString<0> Storage;std::unique_ptr<object::ObjectFile> Obj = yaml::yaml2ObjectFile(Storage, Yaml, [](const Twine &Err) { errs() << Err; });ASSERT_TRUE(Obj);std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(*Obj);const DWARFObject &DObj = Ctx->getDWARFObj();ASSERT_EQ(6u, DObj.getLineSection().Data.size());DWARFDataExtractor Data(DObj, DObj.getLineSection(), Obj->isLittleEndian(),Obj->getBytesInAddress());DataExtractor::Cursor C(0);EXPECT_EQ(0x42u, Data.getRelocatedAddress(C));EXPECT_EQ(0u, Data.getRelocatedAddress(C));EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("unexpected end of data at offset 0x6 while reading [0x4, 0x8)"));}TEST(DWARFDataExtractorTest, getInitialLength) {auto GetWithError = [](ArrayRef<uint8_t> Bytes)-> Expected<std::tuple<uint64_t, dwarf::DwarfFormat, uint64_t>> {DWARFDataExtractor Data(Bytes, /*IsLittleEndian=*/false, /*AddressSize=*/8);DWARFDataExtractor::Cursor C(0);uint64_t Length;dwarf::DwarfFormat Format;std::tie(Length, Format) = Data.getInitialLength(C);if (C)return std::make_tuple(Length, Format, C.tell());EXPECT_EQ(Length, 0u);EXPECT_EQ(Format, dwarf::DWARF32);EXPECT_EQ(C.tell(), 0u);return C.takeError();};auto GetWithoutError = [](ArrayRef<uint8_t> Bytes) {DWARFDataExtractor Data(Bytes, /*IsLittleEndian=*/false, /*AddressSize=*/8);uint64_t Offset = 0;uint64_t Length;dwarf::DwarfFormat Format;std::tie(Length, Format) = Data.getInitialLength(&Offset);return std::make_tuple(Length, Format, Offset);};auto ErrorResult = std::make_tuple(0, dwarf::DWARF32, 0);// Empty data.EXPECT_THAT_EXPECTED(GetWithError({}),FailedWithMessage("unexpected end of data at offset 0x0 while reading [0x0, 0x4)"));EXPECT_EQ(GetWithoutError({}), ErrorResult);// Not long enough for the U32 field.EXPECT_THAT_EXPECTED(GetWithError({0x00, 0x01, 0x02}),FailedWithMessage("unexpected end of data at offset 0x3 while reading [0x0, 0x4)"));EXPECT_EQ(GetWithoutError({0x00, 0x01, 0x02}), ErrorResult);EXPECT_THAT_EXPECTED(GetWithError({0x00, 0x01, 0x02, 0x03}),HasValue(std::make_tuple(0x00010203, dwarf::DWARF32, 4)));EXPECT_EQ(GetWithoutError({0x00, 0x01, 0x02, 0x03}),std::make_tuple(0x00010203, dwarf::DWARF32, 4));// Zeroes are not an error, but without the Error object it is hard to tell// them apart from a failed read.EXPECT_THAT_EXPECTED(GetWithError({0x00, 0x00, 0x00, 0x00}),HasValue(std::make_tuple(0x00000000, dwarf::DWARF32, 4)));EXPECT_EQ(GetWithoutError({0x00, 0x00, 0x00, 0x00}),std::make_tuple(0x00000000, dwarf::DWARF32, 4));// Smallest invalid value.EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xf0}),FailedWithMessage("unsupported reserved unit length of value 0xfffffff0"));EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xf0}), ErrorResult);// DWARF64 marker without the subsequent length field.EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xff}),FailedWithMessage("unexpected end of data at offset 0x4 while reading [0x4, 0xc)"));EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff}), ErrorResult);// Not enough data for the U64 length.EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03}),FailedWithMessage("unexpected end of data at offset 0x8 while reading [0x4, 0xc)"));EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03}),ErrorResult);EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,0x06, 0x07}),HasValue(std::make_tuple(0x0001020304050607, dwarf::DWARF64, 12)));EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03,0x04, 0x05, 0x06, 0x07}),std::make_tuple(0x0001020304050607, dwarf::DWARF64, 12));}TEST(DWARFDataExtractorTest, Truncation) {StringRef Yaml = R"(!ELFFileHeader:Class: ELFCLASS32Data: ELFDATA2LSBType: ET_RELMachine: EM_386Sections:- Name: .textType: SHT_PROGBITSSize: 0x80- Name: .debug_lineType: SHT_PROGBITSContent: '616263640000000065666768'- Name: .rel.debug_lineType: SHT_RELInfo: .debug_lineRelocations:- Offset: 4Symbol: fType: R_386_32Symbols:- Name: fType: STT_SECTIONSection: .textValue: 0x42)";SmallString<0> Storage;std::unique_ptr<object::ObjectFile> Obj = yaml::yaml2ObjectFile(Storage, Yaml, [](const Twine &Err) { errs() << Err; });ASSERT_TRUE(Obj);std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(*Obj);const DWARFObject &DObj = Ctx->getDWARFObj();ASSERT_EQ(12u, DObj.getLineSection().Data.size());DWARFDataExtractor Data(DObj, DObj.getLineSection(), Obj->isLittleEndian(),Obj->getBytesInAddress());DataExtractor::Cursor C(0);EXPECT_EQ(0x64636261u, Data.getRelocatedAddress(C));EXPECT_EQ(0x42u, Data.getRelocatedAddress(C));EXPECT_EQ(0x68676665u, Data.getRelocatedAddress(C));EXPECT_THAT_ERROR(C.takeError(), Succeeded());C = DataExtractor::Cursor{0};DWARFDataExtractor Truncated8(Data, 8);EXPECT_EQ(0x64636261u, Truncated8.getRelocatedAddress(C));EXPECT_EQ(0x42u, Truncated8.getRelocatedAddress(C));EXPECT_EQ(0x0u, Truncated8.getRelocatedAddress(C));EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("unexpected end of data at offset 0x8 while reading [0x8, 0xc)"));C = DataExtractor::Cursor{0};DWARFDataExtractor Truncated6(Data, 6);EXPECT_EQ(0x64636261u, Truncated6.getRelocatedAddress(C));EXPECT_EQ(0x0u, Truncated6.getRelocatedAddress(C));EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("unexpected end of data at offset 0x6 while reading [0x4, 0x8)"));C = DataExtractor::Cursor{0};DWARFDataExtractor Truncated2(Data, 2);EXPECT_EQ(0x0u, Truncated2.getRelocatedAddress(C));EXPECT_THAT_ERROR(C.takeError(),FailedWithMessage("unexpected end of data at offset 0x2 while reading [0x0, 0x4)"));}} // namespace
//===- DWARFAcceleratorTableTest.cpp --------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"#include "llvm/DebugInfo/DWARF/DWARFContext.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;static Error ExtractDebugNames(StringRef NamesSecData, StringRef StrSecData) {DWARFDataExtractor NamesExtractor(NamesSecData,/*isLittleEndian=*/true,/*AddrSize=*/4);DataExtractor StrExtractor(StrSecData,/*isLittleEndian=*/true,/*AddrSize=*/4);DWARFDebugNames Table(NamesExtractor, StrExtractor);return Table.extract();}namespace {TEST(DWARFDebugNames, ReservedUnitLength) {static const char NamesSecData[64] ="\xf0\xff\xff\xff"; // Reserved unit length valueEXPECT_THAT_ERROR(ExtractDebugNames(StringRef(NamesSecData, sizeof(NamesSecData)),StringRef()),FailedWithMessage("parsing .debug_names header at 0x0: unsupported ""reserved unit length of value 0xfffffff0"));}TEST(DWARFDebugNames, TooSmallForDWARF64) {// DWARF64 header takes at least 44 bytes.static const char NamesSecData[43] = "\xff\xff\xff\xff"; // DWARF64 markEXPECT_THAT_ERROR(ExtractDebugNames(StringRef(NamesSecData, sizeof(NamesSecData)),StringRef()),FailedWithMessage("parsing .debug_names header at 0x0: unexpected end of ""data at offset 0x2b while reading [0x28, 0x2c)"));}} // end anonymous namespace
set(LLVM_LINK_COMPONENTS${LLVM_TARGETS_TO_BUILD}AsmPrinterBinaryFormatDebugInfoDWARFMCObjectObjectYAMLSupport)add_llvm_unittest(DebugInfoDWARFTestsDwarfGenerator.cppDwarfUtils.cppDWARFAcceleratorTableTest.cppDWARFDataExtractorTest.cppDWARFDebugArangeSetTest.cppDWARFDebugFrameTest.cppDWARFDebugInfoTest.cppDWARFDebugLineTest.cppDWARFDieTest.cppDWARFDieManualExtractTest.cppDWARFExpressionCopyBytesTest.cppDWARFExpressionCompactPrinterTest.cppDWARFFormValueTest.cppDWARFListTableTest.cppDWARFLocationExpressionTest.cpp)target_link_libraries(DebugInfoDWARFTests PRIVATE LLVMTestingSupport)set_property(TARGET DebugInfoDWARFTests PROPERTY FOLDER "Tests/UnitTests/DebugInfoTests")
//===- llvm/unittest/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"#include "llvm/Support/Allocator.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::codeview;class TypeIndexIteratorTest : public testing::Test {public:TypeIndexIteratorTest() {}void SetUp() override {Refs.clear();TTB = std::make_unique<AppendingTypeTableBuilder>(Storage);CRB = std::make_unique<ContinuationRecordBuilder>();Symbols.clear();}void TearDown() override {CRB.reset();TTB.reset();}protected:template <typename... Indices>bool checkTypeReferences(uint32_t RecordIndex, Indices &&... TIs) const {EXPECT_EQ(sizeof...(Indices), countRefs(RecordIndex));// Choose between type or symbol records. The checking code doesn't care// which we have.std::vector<ArrayRef<uint8_t>> CVRecords;if (Symbols.empty()) {CVRecords = TTB->records();} else {for (const CVSymbol &S : Symbols)CVRecords.push_back(S.data());}return checkTypeReferencesImpl(RecordIndex, CVRecords,std::forward<Indices>(TIs)...);}template <typename... T> void writeFieldList(T &&... MemberRecords) {CRB->begin(ContinuationRecordKind::FieldList);writeFieldListImpl(std::forward<T>(MemberRecords)...);auto Records = CRB->end(TTB->nextTypeIndex());ASSERT_EQ(1u, Records.size());TTB->insertRecordBytes(Records.front().RecordData);discoverAllTypeIndices();}template <typename... T> void writeTypeRecords(T &&... Records) {writeTypeRecordsImpl(std::forward<T>(Records)...);ASSERT_EQ(sizeof...(T), TTB->records().size());discoverAllTypeIndices();}template <typename... T> void writeSymbolRecords(T &&... Records) {writeSymbolRecordsImpl(std::forward<T>(Records)...);ASSERT_EQ(sizeof...(T), Symbols.size());discoverTypeIndicesInSymbols();}std::unique_ptr<AppendingTypeTableBuilder> TTB;private:uint32_t countRefs(uint32_t RecordIndex) const {auto &R = Refs[RecordIndex];uint32_t Count = 0;for (auto &Ref : R) {Count += Ref.Count;}return Count;}bool checkOneTypeReference(uint32_t RecordIndex, ArrayRef<uint8_t> RecordData,TypeIndex TI) const {RecordData = RecordData.drop_front(sizeof(RecordPrefix));auto &RefList = Refs[RecordIndex];for (auto &Ref : RefList) {uint32_t Offset = Ref.Offset;ArrayRef<uint8_t> Loc = RecordData.drop_front(Offset);ArrayRef<TypeIndex> Indices(reinterpret_cast<const TypeIndex *>(Loc.data()), Ref.Count);if (llvm::any_of(Indices,[TI](const TypeIndex &Other) { return Other == TI; }))return true;}return false;}template <typename... Indices>bool checkTypeReferencesImpl(uint32_t RecordIndex,ArrayRef<ArrayRef<uint8_t>> CVRecords) const {return true;}template <typename... Indices>bool checkTypeReferencesImpl(uint32_t RecordIndex,ArrayRef<ArrayRef<uint8_t>> CVRecords,TypeIndex TI, Indices &&... Rest) const {ArrayRef<uint8_t> Record = CVRecords[RecordIndex];bool Success = checkOneTypeReference(RecordIndex, Record, TI);EXPECT_TRUE(Success);return Success & checkTypeReferencesImpl(RecordIndex, CVRecords,std::forward<Indices>(Rest)...);}void discoverAllTypeIndices() {Refs.resize(TTB->records().size());for (uint32_t I = 0; I < TTB->records().size(); ++I) {ArrayRef<uint8_t> Data = TTB->records()[I];discoverTypeIndices(Data, Refs[I]);}}void discoverTypeIndicesInSymbols() {Refs.resize(Symbols.size());for (uint32_t I = 0; I < Symbols.size(); ++I)discoverTypeIndicesInSymbol(Symbols[I], Refs[I]);}// Helper function to write out a field list record with the given list// of member records.void writeFieldListImpl() {}template <typename RecType, typename... Rest>void writeFieldListImpl(RecType &&Record, Rest &&... Records) {CRB->writeMemberType(Record);writeFieldListImpl(std::forward<Rest>(Records)...);}// Helper function to write out a list of type records.void writeTypeRecordsImpl() {}template <typename RecType, typename... Rest>void writeTypeRecordsImpl(RecType &&Record, Rest &&... Records) {TTB->writeLeafType(Record);writeTypeRecordsImpl(std::forward<Rest>(Records)...);}// Helper function to write out a list of symbol records.void writeSymbolRecordsImpl() {}template <typename RecType, typename... Rest>void writeSymbolRecordsImpl(RecType &&Record, Rest &&... Records) {Symbols.push_back(SymbolSerializer::writeOneSymbol(Record, Storage,CodeViewContainer::Pdb));writeSymbolRecordsImpl(std::forward<Rest>(Records)...);}std::vector<SmallVector<TiReference, 4>> Refs;std::unique_ptr<ContinuationRecordBuilder> CRB;std::vector<CVSymbol> Symbols;BumpPtrAllocator Storage;};namespace leafs {static FuncIdRecord FuncId(TypeIndex(1), TypeIndex(2), "FuncId");static MemberFuncIdRecord MemFuncId(TypeIndex(3), TypeIndex(4), "FuncId");static StringIdRecord StringId(TypeIndex(5), "TheString");static struct {std::vector<TypeIndex> Ids = {TypeIndex(6), TypeIndex(7), TypeIndex(8)};StringListRecord Record{TypeRecordKind::StringList, Ids};} StringList;static struct {std::vector<TypeIndex> Ids = {TypeIndex(9), TypeIndex(10), TypeIndex(11)};BuildInfoRecord Record{Ids};} BuildInfo;static UdtSourceLineRecord UdtSourceLine(TypeIndex(12), TypeIndex(13), 0);static UdtModSourceLineRecord UdtModSourceLine(TypeIndex(14), TypeIndex(15), 0,0);static ModifierRecord Modifier(TypeIndex(16), ModifierOptions::None);static ProcedureRecord Procedure(TypeIndex(17), CallingConvention::PpcCall,FunctionOptions::None, 0, TypeIndex(18));static MemberFunctionRecord MemberFunction(TypeIndex(19), TypeIndex(20),TypeIndex(21),CallingConvention::ThisCall,FunctionOptions::None, 2,TypeIndex(22), 0);static struct {std::vector<TypeIndex> Ids = {TypeIndex(23), TypeIndex(24), TypeIndex(25)};ArgListRecord Record{TypeRecordKind::ArgList, Ids};} ArgList;static ArrayRecord Array(TypeIndex(26), TypeIndex(27), 10, "MyArray");static ClassRecord Class(TypeRecordKind::Class, 3, ClassOptions::None,TypeIndex(28), TypeIndex(29), TypeIndex(30), 10,"MyClass", "MyClassUniqueName");static ClassRecord Struct(TypeRecordKind::Struct, 3, ClassOptions::None,TypeIndex(31), TypeIndex(32), TypeIndex(33), 10,"MyClass", "MyClassUniqueName");static UnionRecord Union(1, ClassOptions::None, TypeIndex(34), 10, "MyUnion","MyUnionUniqueName");static EnumRecord Enum(1, ClassOptions::None, TypeIndex(35), "MyEnum","EnumUniqueName", TypeIndex(36));static BitFieldRecord BitField(TypeIndex(37), 1, 0);static VFTableRecord VFTable(TypeIndex(38), TypeIndex(39), 1, "VFT", {});static VFTableShapeRecord VTableShape({});static struct {const TypeIndex T1{40};const TypeIndex T2{41};const TypeIndex T3{42};const TypeIndex T4{43};std::vector<OneMethodRecord> Methods{{T1, MemberAccess::Public, MethodKind::IntroducingVirtual,MethodOptions::None, 0, "Method1"},{T2, MemberAccess::Public, MethodKind::PureVirtual, MethodOptions::None,0, "Method1"},{T3, MemberAccess::Public, MethodKind::PureIntroducingVirtual,MethodOptions::None, 0, "Method1"},{T4, MemberAccess::Public, MethodKind::Static, MethodOptions::None, 0,"Method1"}};MethodOverloadListRecord Record{Methods};} MethodOverloadList;static PointerRecord Pointer(TypeIndex(44), PointerKind::Near32,PointerMode::Pointer, PointerOptions::Const, 3);static PointerRecord MemberPointer(TypeIndex(45), PointerKind::Near32, PointerMode::PointerToDataMember,PointerOptions::Const, 3,MemberPointerInfo(TypeIndex(46),PointerToMemberRepresentation::GeneralData));}namespace members {static BaseClassRecord BaseClass(MemberAccess::Public, TypeIndex(47), 0);static EnumeratorRecord Enumerator(MemberAccess::Public,APSInt(APInt(8, 3, false)), "Test");DataMemberRecord DataMember(MemberAccess::Public, TypeIndex(48), 0, "Test");OverloadedMethodRecord OverloadedMethod(3, TypeIndex(49), "MethodList");static struct {const TypeIndex T1{50};const TypeIndex T2{51};const TypeIndex T3{52};const TypeIndex T4{53};OneMethodRecord R1{T1,MemberAccess::Public,MethodKind::IntroducingVirtual,MethodOptions::None,0,"M1"};OneMethodRecord R2{T2,MemberAccess::Public,MethodKind::PureVirtual,MethodOptions::None,0,"M2"};OneMethodRecord R3{T3,MemberAccess::Public,MethodKind::PureIntroducingVirtual,MethodOptions::None,0,"M3"};OneMethodRecord R4{T4,MemberAccess::Protected,MethodKind::Vanilla,MethodOptions::CompilerGenerated,0,"M4"};} OneMethod;static NestedTypeRecord NestedType(TypeIndex(54), "MyClass");static StaticDataMemberRecord StaticDataMember(MemberAccess::Public,TypeIndex(55), "Foo");static VirtualBaseClassRecord VirtualBaseClass(TypeRecordKind::VirtualBaseClass,MemberAccess::Public,TypeIndex(56), TypeIndex(57), 0,0);static VFPtrRecord VFPtr(TypeIndex(58));static ListContinuationRecord Continuation(TypeIndex(59));}TEST_F(TypeIndexIteratorTest, FuncId) {using namespace leafs;writeTypeRecords(FuncId);checkTypeReferences(0, FuncId.FunctionType, FuncId.ParentScope);}TEST_F(TypeIndexIteratorTest, MemFuncId) {using namespace leafs;writeTypeRecords(MemFuncId);checkTypeReferences(0, MemFuncId.ClassType, MemFuncId.FunctionType);}TEST_F(TypeIndexIteratorTest, StringId) {using namespace leafs;writeTypeRecords(StringId);checkTypeReferences(0, StringId.Id);}TEST_F(TypeIndexIteratorTest, SubstrList) {using namespace leafs;writeTypeRecords(StringList.Record);checkTypeReferences(0, StringList.Ids[0], StringList.Ids[1],StringList.Ids[2]);}TEST_F(TypeIndexIteratorTest, BuildInfo) {using namespace leafs;writeTypeRecords(BuildInfo.Record);checkTypeReferences(0, BuildInfo.Ids[0], BuildInfo.Ids[1], BuildInfo.Ids[2]);}TEST_F(TypeIndexIteratorTest, UdtSrcLine) {using namespace leafs;writeTypeRecords(UdtSourceLine);checkTypeReferences(0, UdtSourceLine.UDT, UdtSourceLine.SourceFile);}TEST_F(TypeIndexIteratorTest, UdtModSrcLine) {using namespace leafs;writeTypeRecords(UdtModSourceLine);checkTypeReferences(0, UdtModSourceLine.UDT, UdtModSourceLine.SourceFile);}TEST_F(TypeIndexIteratorTest, Modifier) {using namespace leafs;writeTypeRecords(Modifier);checkTypeReferences(0, Modifier.ModifiedType);}TEST_F(TypeIndexIteratorTest, Procedure) {using namespace leafs;writeTypeRecords(Procedure);checkTypeReferences(0, Procedure.ReturnType, Procedure.ArgumentList);}TEST_F(TypeIndexIteratorTest, MemFunc) {using namespace leafs;writeTypeRecords(MemberFunction);checkTypeReferences(0, MemberFunction.ReturnType, MemberFunction.ClassType,MemberFunction.ThisType, MemberFunction.ArgumentList);}TEST_F(TypeIndexIteratorTest, ArgList) {using namespace leafs;writeTypeRecords(ArgList.Record);checkTypeReferences(0, ArgList.Ids[0], ArgList.Ids[1], ArgList.Ids[2]);}TEST_F(TypeIndexIteratorTest, Array) {using namespace leafs;writeTypeRecords(Array);checkTypeReferences(0, Array.ElementType, Array.IndexType);}TEST_F(TypeIndexIteratorTest, Class) {using namespace leafs;writeTypeRecords(Class);checkTypeReferences(0, Class.FieldList, Class.DerivationList,Class.VTableShape);}TEST_F(TypeIndexIteratorTest, Struct) {using namespace leafs;writeTypeRecords(Struct);checkTypeReferences(0, Struct.FieldList, Struct.DerivationList,Struct.VTableShape);}TEST_F(TypeIndexIteratorTest, Union) {using namespace leafs;writeTypeRecords(Union);checkTypeReferences(0, Union.FieldList);}TEST_F(TypeIndexIteratorTest, Enum) {using namespace leafs;writeTypeRecords(Enum);checkTypeReferences(0, Enum.FieldList, Enum.UnderlyingType);}TEST_F(TypeIndexIteratorTest, Bitfield) {using namespace leafs;writeTypeRecords(BitField);checkTypeReferences(0, BitField.Type);}TEST_F(TypeIndexIteratorTest, VTable) {using namespace leafs;writeTypeRecords(VFTable);checkTypeReferences(0, VFTable.CompleteClass, VFTable.OverriddenVFTable);}TEST_F(TypeIndexIteratorTest, VTShape) {using namespace leafs;writeTypeRecords(VTableShape);checkTypeReferences(0);}TEST_F(TypeIndexIteratorTest, OverloadList) {using namespace leafs;writeTypeRecords(MethodOverloadList.Record);checkTypeReferences(0, MethodOverloadList.T1, MethodOverloadList.T2,MethodOverloadList.T3, MethodOverloadList.T4);}TEST_F(TypeIndexIteratorTest, Pointer) {using namespace leafs;writeTypeRecords(Pointer);checkTypeReferences(0, Pointer.ReferentType);}TEST_F(TypeIndexIteratorTest, MemberPointer) {using namespace leafs;writeTypeRecords(MemberPointer);checkTypeReferences(0, MemberPointer.ReferentType,MemberPointer.MemberInfo->ContainingType);}TEST_F(TypeIndexIteratorTest, ManyTypes) {using namespace leafs;writeTypeRecords(FuncId, MemFuncId, StringId, StringList.Record,BuildInfo.Record, UdtSourceLine, UdtModSourceLine, Modifier,Procedure, MemberFunction, ArgList.Record, Array, Class,Union, Enum, BitField, VFTable, VTableShape,MethodOverloadList.Record, Pointer, MemberPointer);checkTypeReferences(0, FuncId.FunctionType, FuncId.ParentScope);checkTypeReferences(1, MemFuncId.ClassType, MemFuncId.FunctionType);checkTypeReferences(2, StringId.Id);checkTypeReferences(3, StringList.Ids[0], StringList.Ids[1],StringList.Ids[2]);checkTypeReferences(4, BuildInfo.Ids[0], BuildInfo.Ids[1], BuildInfo.Ids[2]);checkTypeReferences(5, UdtSourceLine.UDT, UdtSourceLine.SourceFile);checkTypeReferences(6, UdtModSourceLine.UDT, UdtModSourceLine.SourceFile);checkTypeReferences(7, Modifier.ModifiedType);checkTypeReferences(8, Procedure.ReturnType, Procedure.ArgumentList);checkTypeReferences(9, MemberFunction.ReturnType, MemberFunction.ClassType,MemberFunction.ThisType, MemberFunction.ArgumentList);checkTypeReferences(10, ArgList.Ids[0], ArgList.Ids[1], ArgList.Ids[2]);checkTypeReferences(11, Array.ElementType, Array.IndexType);checkTypeReferences(12, Class.FieldList, Class.DerivationList,Class.VTableShape);checkTypeReferences(13, Union.FieldList);checkTypeReferences(14, Enum.FieldList, Enum.UnderlyingType);checkTypeReferences(15, BitField.Type);checkTypeReferences(16, VFTable.CompleteClass, VFTable.OverriddenVFTable);checkTypeReferences(17);checkTypeReferences(18, MethodOverloadList.T1, MethodOverloadList.T2,MethodOverloadList.T3, MethodOverloadList.T4);checkTypeReferences(19, Pointer.ReferentType);checkTypeReferences(20, MemberPointer.ReferentType,MemberPointer.MemberInfo->ContainingType);}TEST_F(TypeIndexIteratorTest, FieldListBaseClass) {using namespace members;writeFieldList(BaseClass);checkTypeReferences(0, BaseClass.Type);}TEST_F(TypeIndexIteratorTest, FieldListEnumerator) {using namespace members;writeFieldList(Enumerator);checkTypeReferences(0);}TEST_F(TypeIndexIteratorTest, FieldListMember) {using namespace members;writeFieldList(DataMember);checkTypeReferences(0, DataMember.Type);}TEST_F(TypeIndexIteratorTest, FieldListMethod) {using namespace members;writeFieldList(OverloadedMethod);checkTypeReferences(0, OverloadedMethod.MethodList);}TEST_F(TypeIndexIteratorTest, FieldListOneMethod) {using namespace members;writeFieldList(OneMethod.R1, OneMethod.R2, OneMethod.R3, OneMethod.R4);checkTypeReferences(0, OneMethod.T1, OneMethod.T2, OneMethod.T3,OneMethod.T4);}TEST_F(TypeIndexIteratorTest, FieldListNestedType) {using namespace members;writeFieldList(NestedType);checkTypeReferences(0, NestedType.Type);}TEST_F(TypeIndexIteratorTest, FieldListStaticMember) {using namespace members;writeFieldList(StaticDataMember);checkTypeReferences(0, StaticDataMember.Type);}TEST_F(TypeIndexIteratorTest, FieldListVirtualBase) {using namespace members;writeFieldList(VirtualBaseClass);checkTypeReferences(0, VirtualBaseClass.BaseType, VirtualBaseClass.VBPtrType);}TEST_F(TypeIndexIteratorTest, FieldListVFTable) {using namespace members;writeFieldList(VFPtr);checkTypeReferences(0, VFPtr.Type);}TEST_F(TypeIndexIteratorTest, FieldListContinuation) {using namespace members;writeFieldList(Continuation);checkTypeReferences(0, Continuation.ContinuationIndex);}TEST_F(TypeIndexIteratorTest, ManyMembers) {using namespace members;writeFieldList(BaseClass, Enumerator, DataMember, OverloadedMethod,OneMethod.R1, OneMethod.R2, OneMethod.R3, OneMethod.R4,NestedType, StaticDataMember, VirtualBaseClass, VFPtr,Continuation);checkTypeReferences(0, BaseClass.Type, DataMember.Type, OverloadedMethod.MethodList,OneMethod.T1, OneMethod.T2, OneMethod.T3, OneMethod.T4, NestedType.Type,StaticDataMember.Type, VirtualBaseClass.BaseType,VirtualBaseClass.VBPtrType, VFPtr.Type, Continuation.ContinuationIndex);}TEST_F(TypeIndexIteratorTest, ProcSym) {ProcSym GS(SymbolRecordKind::GlobalProcSym);GS.FunctionType = TypeIndex::Float32();ProcSym LS(SymbolRecordKind::ProcSym);LS.FunctionType = TypeIndex::Float64();writeSymbolRecords(GS, LS);checkTypeReferences(0, GS.FunctionType);checkTypeReferences(1, LS.FunctionType);}TEST_F(TypeIndexIteratorTest, DataSym) {DataSym DS(SymbolRecordKind::GlobalData);DS.Type = TypeIndex::Float32();writeSymbolRecords(DS);checkTypeReferences(0, DS.Type);}TEST_F(TypeIndexIteratorTest, RegisterSym) {RegisterSym Reg(SymbolRecordKind::RegisterSym);Reg.Index = TypeIndex::UInt32();Reg.Register = RegisterId::EAX;Reg.Name = "Target";writeSymbolRecords(Reg);checkTypeReferences(0, Reg.Index);}TEST_F(TypeIndexIteratorTest, CallerSym) {CallerSym Callees(SymbolRecordKind::CalleeSym);Callees.Indices.push_back(TypeIndex(1));Callees.Indices.push_back(TypeIndex(2));Callees.Indices.push_back(TypeIndex(3));CallerSym Callers(SymbolRecordKind::CallerSym);Callers.Indices.push_back(TypeIndex(4));Callers.Indices.push_back(TypeIndex(5));Callers.Indices.push_back(TypeIndex(6));CallerSym Inlinees(SymbolRecordKind::InlineesSym);Inlinees.Indices.push_back(TypeIndex(7));Inlinees.Indices.push_back(TypeIndex(8));Inlinees.Indices.push_back(TypeIndex(9));writeSymbolRecords(Callees, Callers, Inlinees);checkTypeReferences(0, TypeIndex(1), TypeIndex(2), TypeIndex(3));checkTypeReferences(1, TypeIndex(4), TypeIndex(5), TypeIndex(6));checkTypeReferences(2, TypeIndex(7), TypeIndex(8), TypeIndex(9));}TEST_F(TypeIndexIteratorTest, Precomp) {PrecompRecord P(TypeRecordKind::Precomp);P.StartTypeIndex = TypeIndex::FirstNonSimpleIndex;P.TypesCount = 100;P.Signature = 0x12345678;P.PrecompFilePath = "C:/precomp.obj";EndPrecompRecord EP(TypeRecordKind::EndPrecomp);EP.Signature = P.Signature;writeTypeRecords(P, EP);checkTypeReferences(0);}// This is a test for getEncodedIntegerLength()TEST_F(TypeIndexIteratorTest, VariableSizeIntegers) {BaseClassRecord BaseClass1(MemberAccess::Public, TypeIndex(47), (uint64_t)-1);BaseClassRecord BaseClass2(MemberAccess::Public, TypeIndex(48), 1);writeFieldList(BaseClass1, BaseClass2);checkTypeReferences(0, TypeIndex(47), TypeIndex(48));}TEST_F(TypeIndexIteratorTest, UsingNamespace) {UsingNamespaceSym UN(SymbolRecordKind::UsingNamespaceSym);UN.Name = "std";writeSymbolRecords(UN);checkTypeReferences(0);}
//===- llvm/unittest/DebugInfo/CodeView/TypeHashingTest.cpp ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/CodeView/TypeHashing.h"#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"#include "llvm/DebugInfo/CodeView/TypeRecord.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::codeview;static TypeIndex createPointerRecord(AppendingTypeTableBuilder &Builder,TypeIndex TI) {PointerRecord PR(TypeRecordKind::Pointer);PR.setAttrs(PointerKind::Near32, PointerMode::Pointer, PointerOptions::None,4);PR.ReferentType = TI;return Builder.writeLeafType(PR);}static TypeIndex createArgListRecord(AppendingTypeTableBuilder &Builder,TypeIndex Q, TypeIndex R) {ArgListRecord AR(TypeRecordKind::ArgList);AR.ArgIndices.push_back(Q);AR.ArgIndices.push_back(R);return Builder.writeLeafType(AR);}static TypeIndex createProcedureRecord(AppendingTypeTableBuilder &Builder,uint32_t ParamCount, TypeIndex Return,TypeIndex ArgList) {ProcedureRecord PR(TypeRecordKind::Procedure);PR.ArgumentList = ArgList;PR.CallConv = CallingConvention::NearC;PR.Options = FunctionOptions::None;PR.ParameterCount = ParamCount;PR.ReturnType = Return;return Builder.writeLeafType(PR);}static ArrayRef<uint8_t> hash_of(ArrayRef<GloballyHashedType> Hashes,TypeIndex TI) {return Hashes[TI.toArrayIndex()].Hash;}static void verifyHashUniqueness(ArrayRef<GloballyHashedType> Hashes) {assert(!Hashes.empty());for (size_t I = 0; I < Hashes.size() - 1; ++I) {for (size_t J = I + 1; J < Hashes.size(); ++J) {EXPECT_NE(Hashes[I].Hash, Hashes[J].Hash);}}}TEST(TypeHashingTest, ContentHash) {SimpleTypeSerializer Serializer;TypeIndex CharStar(SimpleTypeKind::SignedCharacter,SimpleTypeMode::NearPointer32);BumpPtrAllocator Alloc;AppendingTypeTableBuilder Ordering1(Alloc);AppendingTypeTableBuilder Ordering2(Alloc);TypeIndex CharP(SimpleTypeKind::SignedCharacter, SimpleTypeMode::NearPointer);TypeIndex IntP(SimpleTypeKind::Int32, SimpleTypeMode::NearPointer);TypeIndex DoubleP(SimpleTypeKind::Float64, SimpleTypeMode::NearPointer);// We're going to the same type sequence with two different orderings, and// then confirm all records are hashed the same.TypeIndex CharPP[2];TypeIndex IntPP[2];TypeIndex IntPPP[2];TypeIndex DoublePP[2];TypeIndex Args[2];TypeIndex Proc[2];// Ordering 1// ----------------------------------------// LF_POINTER 0x1000 {char**}// Referent = char*// LF_POINTER 0x1001 {int**}// Referent = int*// LF_POINTER 0x1002 {int***}// Referent = 0x1001// LF_ARGLIST 0x1003 {(char**, int***)}// Arg[0] = 0x1000// Arg[1] = 0x1002// LF_PROCEDURE 0x1004 {int** func(char**, int***)}// ArgList = 0x1003// ReturnType = 0x1001std::vector<GloballyHashedType> Ordering1Hashes;CharPP[0] = createPointerRecord(Ordering1, CharP);IntPP[0] = createPointerRecord(Ordering1, IntP);IntPPP[0] = createPointerRecord(Ordering1, IntPP[0]);Args[0] = createArgListRecord(Ordering1, CharPP[0], IntPPP[0]);Proc[0] = createProcedureRecord(Ordering1, 2, IntPP[0], Args[0]);ASSERT_EQ(0x1000U, CharPP[0].getIndex());ASSERT_EQ(0x1001U, IntPP[0].getIndex());ASSERT_EQ(0x1002U, IntPPP[0].getIndex());ASSERT_EQ(0x1003U, Args[0].getIndex());ASSERT_EQ(0x1004U, Proc[0].getIndex());auto Hashes1 = GloballyHashedType::hashTypes(Ordering1.records());// Ordering 2// ----------------------------------------// LF_POINTER 0x1000 {int**}// Referent = int*// LF_POINTER 0x1001 {int***}// Referent = 0x1000// LF_POINTER 0x1002 {char**}// Referent = char*// LF_POINTER 0x1003 {double**}// Referent = double*// LF_ARGLIST 0x1004 {(char**, int***)}// Arg[0] = 0x1002// Arg[1] = 0x1001// LF_PROCEDURE 0x1005 {int** func(char**, int***)}// ArgList = 0x1004// ReturnType = 0x1000IntPP[1] = createPointerRecord(Ordering2, IntP);IntPPP[1] = createPointerRecord(Ordering2, IntPP[1]);CharPP[1] = createPointerRecord(Ordering2, CharP);DoublePP[1] = createPointerRecord(Ordering2, DoubleP);Args[1] = createArgListRecord(Ordering2, CharPP[1], IntPPP[1]);Proc[1] = createProcedureRecord(Ordering2, 2, IntPP[1], Args[1]);auto Hashes2 = GloballyHashedType::hashTypes(Ordering2.records());ASSERT_EQ(0x1000U, IntPP[1].getIndex());ASSERT_EQ(0x1001U, IntPPP[1].getIndex());ASSERT_EQ(0x1002U, CharPP[1].getIndex());ASSERT_EQ(0x1003U, DoublePP[1].getIndex());ASSERT_EQ(0x1004U, Args[1].getIndex());ASSERT_EQ(0x1005U, Proc[1].getIndex());// Sanity check to make sure all same-ordering hashes are different// from each other.verifyHashUniqueness(Hashes1);verifyHashUniqueness(Hashes2);EXPECT_EQ(hash_of(Hashes1, IntPP[0]), hash_of(Hashes2, IntPP[1]));EXPECT_EQ(hash_of(Hashes1, IntPPP[0]), hash_of(Hashes2, IntPPP[1]));EXPECT_EQ(hash_of(Hashes1, CharPP[0]), hash_of(Hashes2, CharPP[1]));EXPECT_EQ(hash_of(Hashes1, Args[0]), hash_of(Hashes2, Args[1]));EXPECT_EQ(hash_of(Hashes1, Proc[0]), hash_of(Hashes2, Proc[1]));}
//===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"#include "llvm/DebugInfo/CodeView/TypeRecord.h"#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"#include "llvm/DebugInfo/PDB/Native/RawTypes.h"#include "llvm/Support/Allocator.h"#include "llvm/Support/BinaryByteStream.h"#include "llvm/Support/BinaryItemStream.h"#include "llvm/Support/Error.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::codeview;using namespace llvm::pdb;namespace llvm {namespace codeview {inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) {if (R1.ElementType != R2.ElementType)return false;if (R1.IndexType != R2.IndexType)return false;if (R1.Name != R2.Name)return false;if (R1.Size != R2.Size)return false;return true;}inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) {return !(R1 == R2);}inline bool operator==(const CVType &R1, const CVType &R2) {if (R1.RecordData != R2.RecordData)return false;return true;}inline bool operator!=(const CVType &R1, const CVType &R2) {return !(R1 == R2);}}}namespace llvm {template <> struct BinaryItemTraits<CVType> {static size_t length(const CVType &Item) { return Item.length(); }static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); }};}namespace {class MockCallbacks : public TypeVisitorCallbacks {public:Error visitTypeBegin(CVType &CVR, TypeIndex Index) override {Indices.push_back(Index);return Error::success();}Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) override {VisitedRecords.push_back(AR);RawRecords.push_back(CVR);return Error::success();}uint32_t count() const {assert(Indices.size() == RawRecords.size());assert(Indices.size() == VisitedRecords.size());return Indices.size();}std::vector<TypeIndex> Indices;std::vector<CVType> RawRecords;std::vector<ArrayRecord> VisitedRecords;};class RandomAccessVisitorTest : public testing::Test {public:RandomAccessVisitorTest() {}static void SetUpTestCase() {GlobalState = std::make_unique<GlobalTestState>();AppendingTypeTableBuilder Builder(GlobalState->Allocator);uint32_t Offset = 0;for (int I = 0; I < 11; ++I) {ArrayRecord AR(TypeRecordKind::Array);AR.ElementType = TypeIndex::Int32();AR.IndexType = TypeIndex::UInt32();AR.Size = I;std::string Name;raw_string_ostream Stream(Name);Stream << "Array [" << I << "]";AR.Name = GlobalState->Strings.save(Stream.str());GlobalState->Records.push_back(AR);GlobalState->Indices.push_back(Builder.writeLeafType(AR));CVType Type(Builder.records().back());GlobalState->TypeVector.push_back(Type);GlobalState->AllOffsets.push_back({GlobalState->Indices.back(), ulittle32_t(Offset)});Offset += Type.length();}GlobalState->ItemStream.setItems(GlobalState->TypeVector);GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream);}static void TearDownTestCase() { GlobalState.reset(); }void SetUp() override {TestState = std::make_unique<PerTestState>();}void TearDown() override { TestState.reset(); }protected:bool ValidateDatabaseRecord(LazyRandomTypeCollection &Types, uint32_t Index) {TypeIndex TI = TypeIndex::fromArrayIndex(Index);if (!Types.contains(TI))return false;if (GlobalState->TypeVector[Index] != Types.getType(TI))return false;return true;}bool ValidateVisitedRecord(uint32_t VisitationOrder,uint32_t GlobalArrayIndex) {TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex);if (TI != TestState->Callbacks.Indices[VisitationOrder])return false;if (GlobalState->TypeVector[TI.toArrayIndex()] !=TestState->Callbacks.RawRecords[VisitationOrder])return false;if (GlobalState->Records[TI.toArrayIndex()] !=TestState->Callbacks.VisitedRecords[VisitationOrder])return false;return true;}struct GlobalTestState {GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {}BumpPtrAllocator Allocator;StringSaver Strings;std::vector<ArrayRecord> Records;std::vector<TypeIndex> Indices;std::vector<TypeIndexOffset> AllOffsets;std::vector<CVType> TypeVector;BinaryItemStream<CVType> ItemStream;VarStreamArray<CVType> TypeArray;MutableBinaryByteStream Stream;};struct PerTestState {FixedStreamArray<TypeIndexOffset> Offsets;MockCallbacks Callbacks;};FixedStreamArray<TypeIndexOffset>createPartialOffsets(MutableBinaryByteStream &Storage,std::initializer_list<uint32_t> Indices) {uint32_t Count = Indices.size();uint32_t Size = Count * sizeof(TypeIndexOffset);uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size);MutableArrayRef<uint8_t> Bytes(Buffer, Size);Storage = MutableBinaryByteStream(Bytes, support::little);BinaryStreamWriter Writer(Storage);for (const auto I : Indices)consumeError(Writer.writeObject(GlobalState->AllOffsets[I]));BinaryStreamReader Reader(Storage);FixedStreamArray<TypeIndexOffset> Result;consumeError(Reader.readArray(Result, Count));return Result;}static std::unique_ptr<GlobalTestState> GlobalState;std::unique_ptr<PerTestState> TestState;};std::unique_ptr<RandomAccessVisitorTest::GlobalTestState>RandomAccessVisitorTest::GlobalState;}TEST_F(RandomAccessVisitorTest, MultipleVisits) {TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});LazyRandomTypeCollection Types(GlobalState->TypeArray,GlobalState->TypeVector.size(),TestState->Offsets);std::vector<uint32_t> IndicesToVisit = {5, 5, 5};for (uint32_t I : IndicesToVisit) {TypeIndex TI = TypeIndex::fromArrayIndex(I);CVType T = Types.getType(TI);EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),Succeeded());}// [0,8) should be presentEXPECT_EQ(8u, Types.size());for (uint32_t I = 0; I < 8; ++I)EXPECT_TRUE(ValidateDatabaseRecord(Types, I));// 5, 5, 5EXPECT_EQ(3u, TestState->Callbacks.count());for (auto I : enumerate(IndicesToVisit))EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));}TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {// Visit multiple items from the same "chunk" in reverse order. In this// example, it's 7 then 4 then 2. At the end, all records from 0 to 7 should// be known by the database, but only 2, 4, and 7 should have been visited.TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});std::vector<uint32_t> IndicesToVisit = {7, 4, 2};LazyRandomTypeCollection Types(GlobalState->TypeArray,GlobalState->TypeVector.size(),TestState->Offsets);for (uint32_t I : IndicesToVisit) {TypeIndex TI = TypeIndex::fromArrayIndex(I);CVType T = Types.getType(TI);EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),Succeeded());}// [0, 7]EXPECT_EQ(8u, Types.size());for (uint32_t I = 0; I < 8; ++I)EXPECT_TRUE(ValidateDatabaseRecord(Types, I));// 2, 4, 7EXPECT_EQ(3u, TestState->Callbacks.count());for (auto I : enumerate(IndicesToVisit))EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));}TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {// * Visit multiple items from the same chunk in ascending order, ensuring// that intermediate items are not visited. In the below example, it's// 5 -> 6 -> 7 which come from the [4,8) chunk.TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});std::vector<uint32_t> IndicesToVisit = {2, 4, 7};LazyRandomTypeCollection Types(GlobalState->TypeArray,GlobalState->TypeVector.size(),TestState->Offsets);for (uint32_t I : IndicesToVisit) {TypeIndex TI = TypeIndex::fromArrayIndex(I);CVType T = Types.getType(TI);EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),Succeeded());}// [0, 7]EXPECT_EQ(8u, Types.size());for (uint32_t I = 0; I < 8; ++I)EXPECT_TRUE(ValidateDatabaseRecord(Types, I));// 2, 4, 7EXPECT_EQ(3u, TestState->Callbacks.count());for (auto &I : enumerate(IndicesToVisit))EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));}TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {// * Don't visit the last item in one chunk, ensuring that visitation stops// at the record you specify, and the chunk is only partially visited.// In the below example, this is tested by visiting 0 and 1 but not 2,// all from the [0,3) chunk.TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});std::vector<uint32_t> IndicesToVisit = {0, 1, 2};LazyRandomTypeCollection Types(GlobalState->TypeArray,GlobalState->TypeVector.size(),TestState->Offsets);for (uint32_t I : IndicesToVisit) {TypeIndex TI = TypeIndex::fromArrayIndex(I);CVType T = Types.getType(TI);EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),Succeeded());}// [0, 8) should be visited.EXPECT_EQ(8u, Types.size());for (uint32_t I = 0; I < 8; ++I)EXPECT_TRUE(ValidateDatabaseRecord(Types, I));// [0, 2]EXPECT_EQ(3u, TestState->Callbacks.count());for (auto I : enumerate(IndicesToVisit))EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));}TEST_F(RandomAccessVisitorTest, InnerChunk) {// Test that when a request comes from a chunk in the middle of the partial// offsets array, that items from surrounding chunks are not visited or// added to the database.TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9});std::vector<uint32_t> IndicesToVisit = {5, 7};LazyRandomTypeCollection Types(GlobalState->TypeArray,GlobalState->TypeVector.size(),TestState->Offsets);for (uint32_t I : IndicesToVisit) {TypeIndex TI = TypeIndex::fromArrayIndex(I);CVType T = Types.getType(TI);EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),Succeeded());}// [4, 9)EXPECT_EQ(5u, Types.size());for (uint32_t I = 4; I < 9; ++I)EXPECT_TRUE(ValidateDatabaseRecord(Types, I));// 5, 7EXPECT_EQ(2u, TestState->Callbacks.count());for (auto &I : enumerate(IndicesToVisit))EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));}TEST_F(RandomAccessVisitorTest, CrossChunkName) {AppendingTypeTableBuilder Builder(GlobalState->Allocator);// TypeIndex 0ClassRecord Class(TypeRecordKind::Class);Class.Name = "FooClass";Class.Options = ClassOptions::None;Class.MemberCount = 0;Class.Size = 4U;Class.DerivationList = TypeIndex::fromArrayIndex(0);Class.FieldList = TypeIndex::fromArrayIndex(0);Class.VTableShape = TypeIndex::fromArrayIndex(0);TypeIndex IndexZero = Builder.writeLeafType(Class);// TypeIndex 1 refers to type index 0.ModifierRecord Modifier(TypeRecordKind::Modifier);Modifier.ModifiedType = TypeIndex::fromArrayIndex(0);Modifier.Modifiers = ModifierOptions::Const;TypeIndex IndexOne = Builder.writeLeafType(Modifier);// set up a type stream that refers to the above two serialized records.std::vector<CVType> TypeArray = {{Builder.records()[0]},{Builder.records()[1]},};BinaryItemStream<CVType> ItemStream(llvm::support::little);ItemStream.setItems(TypeArray);VarStreamArray<CVType> TypeStream(ItemStream);// Figure out the byte offset of the second item.auto ItemOneIter = TypeStream.begin();++ItemOneIter;// Set up a partial offsets buffer that contains the first and second items// in separate chunks.std::vector<TypeIndexOffset> TIO;TIO.push_back({IndexZero, ulittle32_t(0u)});TIO.push_back({IndexOne, ulittle32_t(ItemOneIter.offset())});ArrayRef<uint8_t> Buffer(reinterpret_cast<const uint8_t *>(TIO.data()),TIO.size() * sizeof(TypeIndexOffset));BinaryStreamReader Reader(Buffer, llvm::support::little);FixedStreamArray<TypeIndexOffset> PartialOffsets;ASSERT_THAT_ERROR(Reader.readArray(PartialOffsets, 2), Succeeded());LazyRandomTypeCollection Types(TypeStream, 2, PartialOffsets);StringRef Name = Types.getTypeName(IndexOne);EXPECT_EQ("const FooClass", Name);}
//===- unittest/DebugInfo/CodeView/GUIDFormatTest.cpp - GUID formatting ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/StringRef.h"#include "llvm/DebugInfo/CodeView/Formatters.h"#include "llvm/DebugInfo/CodeView/GUID.h"#include "llvm/Support/FormatVariadic.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::codeview;// Variant 1 UUIDs, nowadays the most common variant, are encoded in a// big-endian format.// For example, 00112233-4455-6677-8899-aabbccddeeff is encoded as the bytes// 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff//// Variant 2 UUIDs, historically used in Microsoft's COM/OLE libraries, use a// mixed-endian format, whereby the first three components of the UUID are// little-endian, and the last two are big-endian.// For example, 00112233-4455-6677-8899-aabbccddeeff is encoded as the bytes// 33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff.//// Note: Only Variant 2 UUIDs are tested.namespace {using GuidPair = std::pair<StringRef, GUID>;using GuidData = SmallVector<GuidPair>;void checkData(GuidData &Data) {for (auto Item : Data) {std::string GuidText(formatv("{0}", Item.second).str());StringRef Scalar(GuidText);// GUID strings are 38 characters long.EXPECT_EQ(Scalar.size(), size_t(38));// GUID must be enclosed in {}EXPECT_EQ(Scalar.front(), '{');EXPECT_EQ(Scalar.back(), '}');Scalar = Scalar.substr(1, Scalar.size() - 2);SmallVector<StringRef, 6> Component;Scalar.split(Component, '-', 5);// GUID must have 5 components.EXPECT_EQ(Component.size(), size_t(5));// GUID components are properly delineated with dashes.EXPECT_EQ(Scalar[8], '-');EXPECT_EQ(Scalar[13], '-');EXPECT_EQ(Scalar[18], '-');EXPECT_EQ(Scalar[23], '-');// GUID only contains hex digits.struct {support::ulittle32_t Data0;support::ulittle16_t Data1;support::ulittle16_t Data2;support::ubig16_t Data3;support::ubig64_t Data4;} G = {};EXPECT_TRUE(to_integer(Component[0], G.Data0, 16));EXPECT_TRUE(to_integer(Component[1], G.Data1, 16));EXPECT_TRUE(to_integer(Component[2], G.Data2, 16));EXPECT_TRUE(to_integer(Component[3], G.Data3, 16));EXPECT_TRUE(to_integer(Component[4], G.Data4, 16));// Check the values are the same.EXPECT_EQ(Scalar, Item.first);}}TEST(GUIDFormatTest, ValidateFormat) {// Shifting 2 (0x00)GuidData Data = {// Non-zero values in all components.{"11223344-5566-7788-99AA-BBCCDDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},// Zero values in all components.{"00000000-0000-0000-0000-000000000000",{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00}},// Shift 2 (0x00) across all components{"00003344-5566-7788-99AA-BBCCDDEEFFAA",{0x44, 0x33, 0x00, 0x00, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11000044-5566-7788-99AA-BBCCDDEEFFAA",{0x44, 0x00, 0x00, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11220000-5566-7788-99AA-BBCCDDEEFFAA",{0x00, 0x00, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223300-0066-7788-99AA-BBCCDDEEFFAA",{0x00, 0x33, 0x22, 0x11, 0x66, 0x00, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223344-0000-7788-99AA-BBCCDDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223344-5500-0088-99AA-BBCCDDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x00, 0x55, 0x88, 0x00, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223344-5566-0000-99AA-BBCCDDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x00, 0x00, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223344-5566-7700-00AA-BBCCDDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x00, 0x77, 0x00, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223344-5566-7788-0000-BBCCDDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x00, 0x00, 0xbb, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223344-5566-7788-9900-00CCDDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0x00, 0x00, 0xcc,0xdd, 0xee, 0xff, 0xaa}},{"11223344-5566-7788-99AA-0000DDEEFFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0x00, 0x00,0xdd, 0xee, 0xff, 0xaa}},{"11223344-5566-7788-99AA-BB0000EEFFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0x00,0x00, 0xee, 0xff, 0xaa}},{"11223344-5566-7788-99AA-BBCC0000FFAA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0x00, 0x00, 0xff, 0xaa}},{"11223344-5566-7788-99AA-BBCCDD0000AA",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0x00, 0x00, 0xaa}},{"11223344-5566-7788-99AA-BBCCDDEE0000",{0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0xaa, 0xbb, 0xcc,0xdd, 0xee, 0x00, 0x00}},};checkData(Data);}} // namespace
set(LLVM_LINK_COMPONENTSDebugInfoCodeView)add_llvm_unittest(DebugInfoCodeViewTestsGUIDFormatTest.cppRandomAccessVisitorTest.cppTypeHashingTest.cppTypeIndexDiscoveryTest.cpp)target_link_libraries(DebugInfoCodeViewTests PRIVATE LLVMTestingSupport)set_property(TARGET DebugInfoCodeViewTests PROPERTY FOLDER "Tests/UnitTests/DebugInfoTests")
add_subdirectory(CodeView)add_subdirectory(DWARF)add_subdirectory(GSYM)add_subdirectory(MSF)add_subdirectory(PDB)add_subdirectory(Symbolizer)
//===- llvm/unittest/CodeGen/TypeTraitsTest.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/RegisterPressure.h"#include "llvm/CodeGen/ScheduleDAG.h"#include "llvm/CodeGen/SelectionDAGNodes.h"#include "llvm/CodeGen/SlotIndexes.h"#include "llvm/CodeGen/TargetPassConfig.h"#include "gtest/gtest.h"#include <type_traits>using namespace llvm;#if __has_feature(is_trivially_copyable) || (defined(__GNUC__) && __GNUC__ >= 5)static_assert(std::is_trivially_copyable<PressureChange>::value,"trivially copyable");static_assert(std::is_trivially_copyable<SDep>::value, "trivially copyable");static_assert(std::is_trivially_copyable<SDValue>::value, "trivially copyable");static_assert(std::is_trivially_copyable<SlotIndex>::value,"trivially copyable");static_assert(std::is_trivially_copyable<IdentifyingPassPtr>::value,"trivially copyable");#endif
//===--- unittests/CodeGen/TestAsmPrinter.h ---------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_CODEGEN_TESTASMPRINTER_H#define LLVM_UNITTESTS_CODEGEN_TESTASMPRINTER_H#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/MC/MCStreamer.h"#include "gmock/gmock.h"#include <memory>namespace llvm {class AsmPrinter;class MCContext;class Target;class TargetMachine;class MockMCStreamer : public MCStreamer {public:explicit MockMCStreamer(MCContext *Ctx);~MockMCStreamer();// These methods are pure virtual in MCStreamer, thus, have to be overridden:MOCK_METHOD2(emitSymbolAttribute,bool(MCSymbol *Symbol, MCSymbolAttr Attribute));MOCK_METHOD3(emitCommonSymbol,void(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment));MOCK_METHOD5(emitZerofill,void(MCSection *Section, MCSymbol *Symbol, uint64_t Size,unsigned ByteAlignment, SMLoc Loc));// The following are mock methods to be used in tests.MOCK_METHOD2(emitLabel, void(MCSymbol *Symbol, SMLoc Loc));MOCK_METHOD2(emitIntValue, void(uint64_t Value, unsigned Size));MOCK_METHOD3(emitValueImpl,void(const MCExpr *Value, unsigned Size, SMLoc Loc));MOCK_METHOD3(emitAbsoluteSymbolDiff,void(const MCSymbol *Hi, const MCSymbol *Lo, unsigned Size));MOCK_METHOD2(emitCOFFSecRel32, void(MCSymbol const *Symbol, uint64_t Offset));};class TestAsmPrinter {std::unique_ptr<MCContext> MC;MockMCStreamer *MS = nullptr; // Owned by AsmPrinterstd::unique_ptr<TargetMachine> TM;std::unique_ptr<AsmPrinter> Asm;/// Private constructor; call TestAsmPrinter::create(...)/// to create an instance.TestAsmPrinter();/// Initialize an AsmPrinter instance with a mocked MCStreamer.llvm::Error init(const Target *TheTarget, StringRef TripleStr,uint16_t DwarfVersion, dwarf::DwarfFormat DwarfFormat);public:/// Create an AsmPrinter and accompanied objects./// Returns ErrorSuccess() with an empty value if the requested target is not/// supported so that the corresponding test can be gracefully skipped.static llvm::Expected<std::unique_ptr<TestAsmPrinter>>create(const std::string &TripleStr, uint16_t DwarfVersion,dwarf::DwarfFormat DwarfFormat);~TestAsmPrinter();void setDwarfUsesRelocationsAcrossSections(bool Enable);AsmPrinter *getAP() const { return Asm.get(); }AsmPrinter *releaseAP() { return Asm.release(); }MCContext &getCtx() const { return *MC; }MockMCStreamer &getMS() const { return *MS; }};} // end namespace llvm#endif // LLVM_UNITTESTS_CODEGEN_TESTASMPRINTER_H
//===--- unittests/CodeGen/TestAsmPrinter.cpp -------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "TestAsmPrinter.h"#include "llvm/ADT/Triple.h"#include "llvm/CodeGen/AsmPrinter.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Target/TargetLoweringObjectFile.h"#include "llvm/Target/TargetMachine.h"using namespace llvm;using ::testing::StrictMock;// Note: a non-const reference argument cannot be passed through// testing::StrictMock, thus, we pass a pointer and dereference it here.MockMCStreamer::MockMCStreamer(MCContext *Ctx) : MCStreamer(*Ctx) {}MockMCStreamer::~MockMCStreamer() = default;TestAsmPrinter::TestAsmPrinter() = default;TestAsmPrinter::~TestAsmPrinter() = default;llvm::Expected<std::unique_ptr<TestAsmPrinter>>TestAsmPrinter::create(const std::string &TripleStr, uint16_t DwarfVersion,dwarf::DwarfFormat DwarfFormat) {std::string ErrorStr;const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrorStr);if (!TheTarget)return std::unique_ptr<TestAsmPrinter>();std::unique_ptr<TestAsmPrinter> TestPrinter(new TestAsmPrinter);if (llvm::Error E =TestPrinter->init(TheTarget, TripleStr, DwarfVersion, DwarfFormat))return std::move(E);return std::move(TestPrinter);}// Note:: based on dwarfgen::Generator::init() from// llvm/unittests/DebugInfo/DWARF/DwarfGenerator.cppllvm::Error TestAsmPrinter::init(const Target *TheTarget, StringRef TripleName,uint16_t DwarfVersion,dwarf::DwarfFormat DwarfFormat) {TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(),None));if (!TM)return make_error<StringError>("no target machine for target " + TripleName,inconvertibleErrorCode());Triple TheTriple(TripleName);MC.reset(new MCContext(TheTriple, TM->getMCAsmInfo(), TM->getMCRegisterInfo(),TM->getMCSubtargetInfo()));TM->getObjFileLowering()->Initialize(*MC, *TM);MC->setObjectFileInfo(TM->getObjFileLowering());MS = new StrictMock<MockMCStreamer>(MC.get());Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MockMCStreamer>(MS)));if (!Asm)return make_error<StringError>("no asm printer for target " + TripleName,inconvertibleErrorCode());// Set the DWARF version correctly on all classes that we use.MC->setDwarfVersion(DwarfVersion);Asm->setDwarfVersion(DwarfVersion);// Set the DWARF format.MC->setDwarfFormat(DwarfFormat);return Error::success();}void TestAsmPrinter::setDwarfUsesRelocationsAcrossSections(bool Enable) {struct HackMCAsmInfo : MCAsmInfo {void setDwarfUsesRelocationsAcrossSections(bool Enable) {DwarfUsesRelocationsAcrossSections = Enable;}};static_cast<HackMCAsmInfo *>(const_cast<MCAsmInfo *>(TM->getMCAsmInfo()))->setDwarfUsesRelocationsAcrossSections(Enable);}
#include "llvm/Target/TargetOptions.h"#include "llvm/CodeGen/TargetPassConfig.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/InitializePasses.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {void initializeTestPassPass(PassRegistry &);}namespace {void initLLVM() {InitializeAllTargets();InitializeAllTargetMCs();InitializeAllAsmPrinters();InitializeAllAsmParsers();PassRegistry *Registry = PassRegistry::getPassRegistry();initializeCore(*Registry);initializeCodeGen(*Registry);}/// Create a TargetMachine. We need a target that doesn't have IPRA enabled by/// default. That turns out to be all targets at the moment, so just use X86.std::unique_ptr<TargetMachine> createTargetMachine(bool EnableIPRA) {Triple TargetTriple("x86_64--");std::string Error;const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);if (!T)return nullptr;TargetOptions Options;Options.EnableIPRA = EnableIPRA;return std::unique_ptr<TargetMachine>(T->createTargetMachine("X86", "", "", Options, None, None, CodeGenOpt::Aggressive));}typedef std::function<void(bool)> TargetOptionsTest;static void targetOptionsTest(bool EnableIPRA) {std::unique_ptr<TargetMachine> TM = createTargetMachine(EnableIPRA);// This test is designed for the X86 backend; stop if it is not available.if (!TM)GTEST_SKIP();legacy::PassManager PM;LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(TM.get());TargetPassConfig *TPC = LLVMTM->createPassConfig(PM);(void)TPC;ASSERT_TRUE(TM->Options.EnableIPRA == EnableIPRA);delete TPC;}} // End of anonymous namespace.TEST(TargetOptionsTest, IPRASetToOff) {targetOptionsTest(false);}TEST(TargetOptionsTest, IPRASetToOn) {targetOptionsTest(true);}int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);initLLVM();return RUN_ALL_TESTS();}
//===- llvm/unittest/CodeGen/SelectionDAGAddressAnalysisTest.cpp ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/SelectionDAGAddressAnalysis.h"#include "llvm/Analysis/MemoryLocation.h"#include "llvm/Analysis/OptimizationRemarkEmitter.h"#include "llvm/AsmParser/Parser.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/SelectionDAG.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"namespace llvm {class SelectionDAGAddressAnalysisTest : public testing::Test {protected:static void SetUpTestCase() {InitializeAllTargets();InitializeAllTargetMCs();}void SetUp() override {StringRef Assembly = "@g = global i32 0\n""@g_alias = alias i32, i32* @g\n""define i32 @f() {\n"" %1 = load i32, i32* @g\n"" ret i32 %1\n""}";Triple TargetTriple("aarch64--");std::string Error;const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);// FIXME: These tests do not depend on AArch64 specifically, but we have to// initialize a target. A skeleton Target for unittests would allow us to// always run these tests.if (!T)GTEST_SKIP();TargetOptions Options;TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine("AArch64", "", "+sve", Options, None, None,CodeGenOpt::Aggressive)));if (!TM)GTEST_SKIP();SMDiagnostic SMError;M = parseAssemblyString(Assembly, SMError, Context);if (!M)report_fatal_error(SMError.getMessage());M->setDataLayout(TM->createDataLayout());F = M->getFunction("f");if (!F)report_fatal_error("F?");G = M->getGlobalVariable("g");if (!G)report_fatal_error("G?");AliasedG = M->getNamedAlias("g_alias");if (!AliasedG)report_fatal_error("AliasedG?");MachineModuleInfo MMI(TM.get());MF = std::make_unique<MachineFunction>(*F, *TM, *TM->getSubtargetImpl(*F),0, MMI);DAG = std::make_unique<SelectionDAG>(*TM, CodeGenOpt::None);if (!DAG)report_fatal_error("DAG?");OptimizationRemarkEmitter ORE(F);DAG->init(*MF, ORE, nullptr, nullptr, nullptr, nullptr, nullptr);}TargetLoweringBase::LegalizeTypeAction getTypeAction(EVT VT) {return DAG->getTargetLoweringInfo().getTypeAction(Context, VT);}EVT getTypeToTransformTo(EVT VT) {return DAG->getTargetLoweringInfo().getTypeToTransformTo(Context, VT);}LLVMContext Context;std::unique_ptr<LLVMTargetMachine> TM;std::unique_ptr<Module> M;Function *F;GlobalVariable *G;GlobalAlias *AliasedG;std::unique_ptr<MachineFunction> MF;std::unique_ptr<SelectionDAG> DAG;};TEST_F(SelectionDAGAddressAnalysisTest, sameFrameObject) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, Int8VT, 4);SDValue FIPtr = DAG->CreateStackTemporary(VecVT);int FI = cast<FrameIndexSDNode>(FIPtr.getNode())->getIndex();MachinePointerInfo PtrInfo = MachinePointerInfo::getFixedStack(*MF, FI);TypeSize Offset = TypeSize::Fixed(0);SDValue Value = DAG->getConstant(0, Loc, VecVT);SDValue Index = DAG->getMemBasePlusOffset(FIPtr, Offset, Loc);SDValue Store = DAG->getStore(DAG->getEntryNode(), Loc, Value, Index,PtrInfo.getWithOffset(Offset));Optional<int64_t> NumBytes = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store)->getMemoryVT().getStoreSize());bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store.getNode(), NumBytes, Store.getNode(), NumBytes, *DAG, IsAlias);EXPECT_TRUE(IsValid);EXPECT_TRUE(IsAlias);}TEST_F(SelectionDAGAddressAnalysisTest, sameFrameObjectUnknownSize) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, Int8VT, 4);SDValue FIPtr = DAG->CreateStackTemporary(VecVT);int FI = cast<FrameIndexSDNode>(FIPtr.getNode())->getIndex();MachinePointerInfo PtrInfo = MachinePointerInfo::getFixedStack(*MF, FI);TypeSize Offset = TypeSize::Fixed(0);SDValue Value = DAG->getConstant(0, Loc, VecVT);SDValue Index = DAG->getMemBasePlusOffset(FIPtr, Offset, Loc);SDValue Store = DAG->getStore(DAG->getEntryNode(), Loc, Value, Index,PtrInfo.getWithOffset(Offset));// Maybe unlikely that BaseIndexOffset::computeAliasing is used with the// optional NumBytes being unset like in this test, but it would be confusing// if that function determined IsAlias=false here.Optional<int64_t> NumBytes;bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store.getNode(), NumBytes, Store.getNode(), NumBytes, *DAG, IsAlias);EXPECT_FALSE(IsValid);}TEST_F(SelectionDAGAddressAnalysisTest, noAliasingFrameObjects) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);// <4 x i8>auto VecVT = EVT::getVectorVT(Context, Int8VT, 4);// <2 x i8>auto SubVecVT = EVT::getVectorVT(Context, Int8VT, 2);SDValue FIPtr = DAG->CreateStackTemporary(VecVT);int FI = cast<FrameIndexSDNode>(FIPtr.getNode())->getIndex();MachinePointerInfo PtrInfo = MachinePointerInfo::getFixedStack(*MF, FI);SDValue Value = DAG->getConstant(0, Loc, SubVecVT);TypeSize Offset0 = TypeSize::Fixed(0);TypeSize Offset1 = SubVecVT.getStoreSize();SDValue Index0 = DAG->getMemBasePlusOffset(FIPtr, Offset0, Loc);SDValue Index1 = DAG->getMemBasePlusOffset(FIPtr, Offset1, Loc);SDValue Store0 = DAG->getStore(DAG->getEntryNode(), Loc, Value, Index0,PtrInfo.getWithOffset(Offset0));SDValue Store1 = DAG->getStore(DAG->getEntryNode(), Loc, Value, Index1,PtrInfo.getWithOffset(Offset1));Optional<int64_t> NumBytes0 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store0)->getMemoryVT().getStoreSize());Optional<int64_t> NumBytes1 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store1)->getMemoryVT().getStoreSize());bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store0.getNode(), NumBytes0, Store1.getNode(), NumBytes1, *DAG, IsAlias);EXPECT_TRUE(IsValid);EXPECT_FALSE(IsAlias);}TEST_F(SelectionDAGAddressAnalysisTest, unknownSizeFrameObjects) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);// <vscale x 4 x i8>auto VecVT = EVT::getVectorVT(Context, Int8VT, 4, true);// <vscale x 2 x i8>auto SubVecVT = EVT::getVectorVT(Context, Int8VT, 2, true);SDValue FIPtr = DAG->CreateStackTemporary(VecVT);int FI = cast<FrameIndexSDNode>(FIPtr.getNode())->getIndex();MachinePointerInfo PtrInfo = MachinePointerInfo::getFixedStack(*MF, FI);SDValue Value = DAG->getConstant(0, Loc, SubVecVT);TypeSize Offset1 = SubVecVT.getStoreSize();SDValue Index1 = DAG->getMemBasePlusOffset(FIPtr, Offset1, Loc);SDValue Store0 =DAG->getStore(DAG->getEntryNode(), Loc, Value, FIPtr, PtrInfo);SDValue Store1 = DAG->getStore(DAG->getEntryNode(), Loc, Value, Index1,MachinePointerInfo(PtrInfo.getAddrSpace()));Optional<int64_t> NumBytes0 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store0)->getMemoryVT().getStoreSize());Optional<int64_t> NumBytes1 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store1)->getMemoryVT().getStoreSize());bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store0.getNode(), NumBytes0, Store1.getNode(), NumBytes1, *DAG, IsAlias);EXPECT_FALSE(IsValid);}TEST_F(SelectionDAGAddressAnalysisTest, globalWithFrameObject) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);// <vscale x 4 x i8>auto VecVT = EVT::getVectorVT(Context, Int8VT, 4, true);SDValue FIPtr = DAG->CreateStackTemporary(VecVT);int FI = cast<FrameIndexSDNode>(FIPtr.getNode())->getIndex();MachinePointerInfo PtrInfo = MachinePointerInfo::getFixedStack(*MF, FI);SDValue Value = DAG->getConstant(0, Loc, VecVT);TypeSize Offset = TypeSize::Fixed(0);SDValue Index = DAG->getMemBasePlusOffset(FIPtr, Offset, Loc);SDValue Store = DAG->getStore(DAG->getEntryNode(), Loc, Value, Index,PtrInfo.getWithOffset(Offset));Optional<int64_t> NumBytes = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store)->getMemoryVT().getStoreSize());EVT GTy = DAG->getTargetLoweringInfo().getValueType(DAG->getDataLayout(),G->getType());SDValue GValue = DAG->getConstant(0, Loc, GTy);SDValue GAddr = DAG->getGlobalAddress(G, Loc, GTy);SDValue GStore = DAG->getStore(DAG->getEntryNode(), Loc, GValue, GAddr,MachinePointerInfo(G, 0));Optional<int64_t> GNumBytes = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(GStore)->getMemoryVT().getStoreSize());bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store.getNode(), NumBytes, GStore.getNode(), GNumBytes, *DAG, IsAlias);EXPECT_TRUE(IsValid);EXPECT_FALSE(IsAlias);}TEST_F(SelectionDAGAddressAnalysisTest, globalWithAliasedGlobal) {SDLoc Loc;EVT GTy = DAG->getTargetLoweringInfo().getValueType(DAG->getDataLayout(),G->getType());SDValue GValue = DAG->getConstant(0, Loc, GTy);SDValue GAddr = DAG->getGlobalAddress(G, Loc, GTy);SDValue GStore = DAG->getStore(DAG->getEntryNode(), Loc, GValue, GAddr,MachinePointerInfo(G, 0));Optional<int64_t> GNumBytes = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(GStore)->getMemoryVT().getStoreSize());SDValue AliasedGValue = DAG->getConstant(1, Loc, GTy);SDValue AliasedGAddr = DAG->getGlobalAddress(AliasedG, Loc, GTy);SDValue AliasedGStore =DAG->getStore(DAG->getEntryNode(), Loc, AliasedGValue, AliasedGAddr,MachinePointerInfo(AliasedG, 0));bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(GStore.getNode(), GNumBytes,AliasedGStore.getNode(),GNumBytes, *DAG, IsAlias);// With some deeper analysis we could detect if G and AliasedG is aliasing or// not. But computeAliasing is currently defensive and assumes that a// GlobalAlias might alias with any global variable.EXPECT_FALSE(IsValid);}TEST_F(SelectionDAGAddressAnalysisTest, fixedSizeFrameObjectsWithinDiff) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);// <vscale x 4 x i8>auto VecVT = EVT::getVectorVT(Context, Int8VT, 4, true);// <vscale x 2 x i8>auto SubVecVT = EVT::getVectorVT(Context, Int8VT, 2, true);// <2 x i8>auto SubFixedVecVT2xi8 = EVT::getVectorVT(Context, Int8VT, 2);SDValue FIPtr = DAG->CreateStackTemporary(VecVT);int FI = cast<FrameIndexSDNode>(FIPtr.getNode())->getIndex();MachinePointerInfo PtrInfo = MachinePointerInfo::getFixedStack(*MF, FI);SDValue Value0 = DAG->getConstant(0, Loc, SubFixedVecVT2xi8);SDValue Value1 = DAG->getConstant(0, Loc, SubVecVT);TypeSize Offset0 = TypeSize::Fixed(0);TypeSize Offset1 = SubFixedVecVT2xi8.getStoreSize();SDValue Index0 = DAG->getMemBasePlusOffset(FIPtr, Offset0, Loc);SDValue Index1 = DAG->getMemBasePlusOffset(FIPtr, Offset1, Loc);SDValue Store0 = DAG->getStore(DAG->getEntryNode(), Loc, Value0, Index0,PtrInfo.getWithOffset(Offset0));SDValue Store1 = DAG->getStore(DAG->getEntryNode(), Loc, Value1, Index1,PtrInfo.getWithOffset(Offset1));Optional<int64_t> NumBytes0 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store0)->getMemoryVT().getStoreSize());Optional<int64_t> NumBytes1 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store1)->getMemoryVT().getStoreSize());bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store0.getNode(), NumBytes0, Store1.getNode(), NumBytes1, *DAG, IsAlias);EXPECT_TRUE(IsValid);EXPECT_FALSE(IsAlias);IsValid = BaseIndexOffset::computeAliasing(Store1.getNode(), NumBytes1, Store0.getNode(), NumBytes0, *DAG, IsAlias);EXPECT_TRUE(IsValid);EXPECT_FALSE(IsAlias);}TEST_F(SelectionDAGAddressAnalysisTest, fixedSizeFrameObjectsOutOfDiff) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);// <vscale x 4 x i8>auto VecVT = EVT::getVectorVT(Context, Int8VT, 4, true);// <vscale x 2 x i8>auto SubVecVT = EVT::getVectorVT(Context, Int8VT, 2, true);// <2 x i8>auto SubFixedVecVT2xi8 = EVT::getVectorVT(Context, Int8VT, 2);// <4 x i8>auto SubFixedVecVT4xi8 = EVT::getVectorVT(Context, Int8VT, 4);SDValue FIPtr = DAG->CreateStackTemporary(VecVT);int FI = cast<FrameIndexSDNode>(FIPtr.getNode())->getIndex();MachinePointerInfo PtrInfo = MachinePointerInfo::getFixedStack(*MF, FI);SDValue Value0 = DAG->getConstant(0, Loc, SubFixedVecVT4xi8);SDValue Value1 = DAG->getConstant(0, Loc, SubVecVT);TypeSize Offset0 = TypeSize::Fixed(0);TypeSize Offset1 = SubFixedVecVT2xi8.getStoreSize();SDValue Index0 = DAG->getMemBasePlusOffset(FIPtr, Offset0, Loc);SDValue Index1 = DAG->getMemBasePlusOffset(FIPtr, Offset1, Loc);SDValue Store0 = DAG->getStore(DAG->getEntryNode(), Loc, Value0, Index0,PtrInfo.getWithOffset(Offset0));SDValue Store1 = DAG->getStore(DAG->getEntryNode(), Loc, Value1, Index1,PtrInfo.getWithOffset(Offset1));Optional<int64_t> NumBytes0 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store0)->getMemoryVT().getStoreSize());Optional<int64_t> NumBytes1 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store1)->getMemoryVT().getStoreSize());bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store0.getNode(), NumBytes0, Store1.getNode(), NumBytes1, *DAG, IsAlias);EXPECT_TRUE(IsValid);EXPECT_TRUE(IsAlias);}TEST_F(SelectionDAGAddressAnalysisTest, twoFixedStackObjects) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);// <vscale x 2 x i8>auto VecVT = EVT::getVectorVT(Context, Int8VT, 2, true);// <2 x i8>auto FixedVecVT = EVT::getVectorVT(Context, Int8VT, 2);SDValue FIPtr0 = DAG->CreateStackTemporary(FixedVecVT);SDValue FIPtr1 = DAG->CreateStackTemporary(VecVT);int FI0 = cast<FrameIndexSDNode>(FIPtr0.getNode())->getIndex();int FI1 = cast<FrameIndexSDNode>(FIPtr1.getNode())->getIndex();MachinePointerInfo PtrInfo0 = MachinePointerInfo::getFixedStack(*MF, FI0);MachinePointerInfo PtrInfo1 = MachinePointerInfo::getFixedStack(*MF, FI1);SDValue Value0 = DAG->getConstant(0, Loc, FixedVecVT);SDValue Value1 = DAG->getConstant(0, Loc, VecVT);TypeSize Offset0 = TypeSize::Fixed(0);SDValue Index0 = DAG->getMemBasePlusOffset(FIPtr0, Offset0, Loc);SDValue Index1 = DAG->getMemBasePlusOffset(FIPtr1, Offset0, Loc);SDValue Store0 = DAG->getStore(DAG->getEntryNode(), Loc, Value0, Index0,PtrInfo0.getWithOffset(Offset0));SDValue Store1 = DAG->getStore(DAG->getEntryNode(), Loc, Value1, Index1,PtrInfo1.getWithOffset(Offset0));Optional<int64_t> NumBytes0 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store0)->getMemoryVT().getStoreSize());Optional<int64_t> NumBytes1 = MemoryLocation::getSizeOrUnknown(cast<StoreSDNode>(Store1)->getMemoryVT().getStoreSize());bool IsAlias;bool IsValid = BaseIndexOffset::computeAliasing(Store0.getNode(), NumBytes0, Store1.getNode(), NumBytes1, *DAG, IsAlias);EXPECT_TRUE(IsValid);EXPECT_FALSE(IsAlias);}} // end namespace llvm
//===-------- llvm/unittest/CodeGen/ScalableVectorMVTsTest.cpp ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/ValueTypes.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/MachineValueType.h"#include "llvm/Support/TypeSize.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ScalableVectorMVTsTest, IntegerMVTs) {for (MVT VecTy : MVT::integer_scalable_vector_valuetypes()) {ASSERT_TRUE(VecTy.isValid());ASSERT_TRUE(VecTy.isInteger());ASSERT_TRUE(VecTy.isVector());ASSERT_TRUE(VecTy.isScalableVector());ASSERT_TRUE(VecTy.getScalarType().isValid());ASSERT_FALSE(VecTy.isFloatingPoint());}}TEST(ScalableVectorMVTsTest, FloatMVTs) {for (MVT VecTy : MVT::fp_scalable_vector_valuetypes()) {ASSERT_TRUE(VecTy.isValid());ASSERT_TRUE(VecTy.isFloatingPoint());ASSERT_TRUE(VecTy.isVector());ASSERT_TRUE(VecTy.isScalableVector());ASSERT_TRUE(VecTy.getScalarType().isValid());ASSERT_FALSE(VecTy.isInteger());}}TEST(ScalableVectorMVTsTest, HelperFuncs) {LLVMContext Ctx;// Create with scalable flagEVT Vnx4i32 = EVT::getVectorVT(Ctx, MVT::i32, 4, /*Scalable=*/true);ASSERT_TRUE(Vnx4i32.isScalableVector());// Create with separate llvm::ElementCountauto EltCnt = ElementCount::getScalable(2);EVT Vnx2i32 = EVT::getVectorVT(Ctx, MVT::i32, EltCnt);ASSERT_TRUE(Vnx2i32.isScalableVector());// Create with inline llvm::ElementCountEVT Vnx2i64 = EVT::getVectorVT(Ctx, MVT::i64, ElementCount::getScalable(2));ASSERT_TRUE(Vnx2i64.isScalableVector());// Check that changing scalar types/element count worksEXPECT_EQ(Vnx2i32.widenIntegerVectorElementType(Ctx), Vnx2i64);EXPECT_EQ(Vnx4i32.getHalfNumVectorElementsVT(Ctx), Vnx2i32);// Check that operators workEXPECT_EQ(EVT::getVectorVT(Ctx, MVT::i64, EltCnt * 2), MVT::nxv4i64);EXPECT_EQ(EVT::getVectorVT(Ctx, MVT::i64, EltCnt.divideCoefficientBy(2)),MVT::nxv1i64);// Check that float->int conversion worksEVT Vnx2f64 = EVT::getVectorVT(Ctx, MVT::f64, ElementCount::getScalable(2));EXPECT_EQ(Vnx2f64.changeTypeToInteger(), Vnx2i64);// Check fields inside llvm::ElementCountEltCnt = Vnx4i32.getVectorElementCount();EXPECT_EQ(EltCnt.getKnownMinValue(), 4U);ASSERT_TRUE(EltCnt.isScalable());// Check that fixed-length vector types aren't scalable.EVT V8i32 = EVT::getVectorVT(Ctx, MVT::i32, 8);ASSERT_FALSE(V8i32.isScalableVector());EVT V4f64 = EVT::getVectorVT(Ctx, MVT::f64, ElementCount::getFixed(4));ASSERT_FALSE(V4f64.isScalableVector());// Check that llvm::ElementCount works for fixed-length types.EltCnt = V8i32.getVectorElementCount();EXPECT_EQ(EltCnt.getKnownMinValue(), 8U);ASSERT_FALSE(EltCnt.isScalable());}TEST(ScalableVectorMVTsTest, IRToVTTranslation) {LLVMContext Ctx;Type *Int64Ty = Type::getInt64Ty(Ctx);VectorType *ScV8Int64Ty =VectorType::get(Int64Ty, ElementCount::getScalable(8));// Check that we can map a scalable IR type to an MVTMVT Mnxv8i64 = MVT::getVT(ScV8Int64Ty);ASSERT_TRUE(Mnxv8i64.isScalableVector());ASSERT_EQ(ScV8Int64Ty->getElementCount(), Mnxv8i64.getVectorElementCount());ASSERT_EQ(MVT::getVT(ScV8Int64Ty->getElementType()),Mnxv8i64.getScalarType());// Check that we can map a scalable IR type to an EVTEVT Enxv8i64 = EVT::getEVT(ScV8Int64Ty);ASSERT_TRUE(Enxv8i64.isScalableVector());ASSERT_EQ(ScV8Int64Ty->getElementCount(), Enxv8i64.getVectorElementCount());ASSERT_EQ(EVT::getEVT(ScV8Int64Ty->getElementType()),Enxv8i64.getScalarType());}TEST(ScalableVectorMVTsTest, VTToIRTranslation) {LLVMContext Ctx;EVT Enxv4f64 = EVT::getVectorVT(Ctx, MVT::f64, ElementCount::getScalable(4));Type *Ty = Enxv4f64.getTypeForEVT(Ctx);VectorType *ScV4Float64Ty = cast<VectorType>(Ty);ASSERT_TRUE(isa<ScalableVectorType>(ScV4Float64Ty));ASSERT_EQ(Enxv4f64.getVectorElementCount(), ScV4Float64Ty->getElementCount());ASSERT_EQ(Enxv4f64.getScalarType().getTypeForEVT(Ctx),ScV4Float64Ty->getElementType());}TEST(ScalableVectorMVTsTest, SizeQueries) {LLVMContext Ctx;EVT nxv4i32 = EVT::getVectorVT(Ctx, MVT::i32, 4, /*Scalable=*/ true);EVT nxv2i32 = EVT::getVectorVT(Ctx, MVT::i32, 2, /*Scalable=*/ true);EVT nxv2i64 = EVT::getVectorVT(Ctx, MVT::i64, 2, /*Scalable=*/ true);EVT nxv2f64 = EVT::getVectorVT(Ctx, MVT::f64, 2, /*Scalable=*/ true);EVT v4i32 = EVT::getVectorVT(Ctx, MVT::i32, 4);EVT v2i32 = EVT::getVectorVT(Ctx, MVT::i32, 2);EVT v2i64 = EVT::getVectorVT(Ctx, MVT::i64, 2);EVT v2f64 = EVT::getVectorVT(Ctx, MVT::f64, 2);// Check equivalence and ordering on scalable types.EXPECT_EQ(nxv4i32.getSizeInBits(), nxv2i64.getSizeInBits());EXPECT_EQ(nxv2f64.getSizeInBits(), nxv2i64.getSizeInBits());EXPECT_NE(nxv2i32.getSizeInBits(), nxv4i32.getSizeInBits());EXPECT_LT(nxv2i32.getSizeInBits().getKnownMinSize(),nxv2i64.getSizeInBits().getKnownMinSize());EXPECT_LE(nxv4i32.getSizeInBits().getKnownMinSize(),nxv2i64.getSizeInBits().getKnownMinSize());EXPECT_GT(nxv4i32.getSizeInBits().getKnownMinSize(),nxv2i32.getSizeInBits().getKnownMinSize());EXPECT_GE(nxv2i64.getSizeInBits().getKnownMinSize(),nxv4i32.getSizeInBits().getKnownMinSize());// Check equivalence and ordering on fixed types.EXPECT_EQ(v4i32.getSizeInBits(), v2i64.getSizeInBits());EXPECT_EQ(v2f64.getSizeInBits(), v2i64.getSizeInBits());EXPECT_NE(v2i32.getSizeInBits(), v4i32.getSizeInBits());EXPECT_LT(v2i32.getFixedSizeInBits(), v2i64.getFixedSizeInBits());EXPECT_LE(v4i32.getFixedSizeInBits(), v2i64.getFixedSizeInBits());EXPECT_GT(v4i32.getFixedSizeInBits(), v2i32.getFixedSizeInBits());EXPECT_GE(v2i64.getFixedSizeInBits(), v4i32.getFixedSizeInBits());// Check that scalable and non-scalable types with the same minimum size// are not considered equal.ASSERT_TRUE(v4i32.getSizeInBits() != nxv4i32.getSizeInBits());ASSERT_FALSE(v2i64.getSizeInBits() == nxv2f64.getSizeInBits());// Check that we can obtain a known-exact size from a non-scalable type.EXPECT_EQ(v4i32.getFixedSizeInBits(), 128U);EXPECT_EQ(v2i64.getFixedSizeInBits(), 128U);// Check that we can query the known minimum size for both scalable and// fixed length types.EXPECT_EQ(nxv2i32.getSizeInBits().getKnownMinSize(), 64U);EXPECT_EQ(nxv2f64.getSizeInBits().getKnownMinSize(), 128U);EXPECT_EQ(v2i32.getSizeInBits().getKnownMinSize(),nxv2i32.getSizeInBits().getKnownMinSize());// Check scalable property.ASSERT_FALSE(v4i32.getSizeInBits().isScalable());ASSERT_TRUE(nxv4i32.getSizeInBits().isScalable());// Check convenience size scaling methods.EXPECT_EQ(v2i32.getSizeInBits() * 2, v4i32.getSizeInBits());EXPECT_EQ(2 * nxv2i32.getSizeInBits(), nxv4i32.getSizeInBits());EXPECT_EQ(nxv2f64.getSizeInBits().divideCoefficientBy(2),nxv2i32.getSizeInBits());}} // end anonymous namespace
//===- MachineInstrTest.cpp -----------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "../lib/CodeGen/RegAllocScore.h"#include "llvm/ADT/Triple.h"#include "llvm/CodeGen/MachineBasicBlock.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineInstr.h"#include "llvm/CodeGen/MachineMemOperand.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetFrameLowering.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/ModuleSlotTracker.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCSymbol.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;extern cl::opt<double> CopyWeight;extern cl::opt<double> LoadWeight;extern cl::opt<double> StoreWeight;extern cl::opt<double> CheapRematWeight;extern cl::opt<double> ExpensiveRematWeight;namespace {// Include helper functions to ease the manipulation of MachineFunctions.#include "MFCommon.inc"// MachineFunction::CreateMachineInstr doesn't copy the MCInstrDesc, it// takes its address. So we want a bunch of pre-allocated mock MCInstrDescs.#define MOCK_INSTR(MACRO) \MACRO(Copy, TargetOpcode::COPY, 0) \MACRO(Load, 0, 1ULL << MCID::MayLoad) \MACRO(Store, 0, 1ULL << MCID::MayStore) \MACRO(LoadStore, 0, (1ULL << MCID::MayLoad) | (1ULL << MCID::MayStore)) \MACRO(CheapRemat, 0, 1ULL << MCID::CheapAsAMove) \MACRO(ExpensiveRemat, 0, 0) \MACRO(Dbg, TargetOpcode::DBG_LABEL, \(1ULL << MCID::MayLoad) | (1ULL << MCID::MayStore)) \MACRO(InlAsm, TargetOpcode::INLINEASM, \(1ULL << MCID::MayLoad) | (1ULL << MCID::MayStore)) \MACRO(Kill, TargetOpcode::KILL, \(1ULL << MCID::MayLoad) | (1ULL << MCID::MayStore))enum MockInstrId {#define MOCK_INSTR_ID(ID, IGNORE, IGNORE2) ID,MOCK_INSTR(MOCK_INSTR_ID)#undef MOCK_INSTR_IDTotalMockInstrs};const std::array<MCInstrDesc, MockInstrId::TotalMockInstrs> MockInstrDescs{{#define MOCK_SPEC(IGNORE, OPCODE, FLAGS) \{OPCODE, 0, 0, 0, 0, FLAGS, 0, nullptr, nullptr, nullptr},MOCK_INSTR(MOCK_SPEC)#undef MOCK_SPEC}};MachineInstr *createMockCopy(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::Copy], DebugLoc());}MachineInstr *createMockLoad(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::Load], DebugLoc());}MachineInstr *createMockStore(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::Store], DebugLoc());}MachineInstr *createMockLoadStore(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::LoadStore],DebugLoc());}MachineInstr *createMockCheapRemat(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::CheapRemat],DebugLoc());}MachineInstr *createMockExpensiveRemat(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::ExpensiveRemat],DebugLoc());}MachineInstr *createMockDebug(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::Dbg], DebugLoc());}MachineInstr *createMockKill(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::Kill], DebugLoc());}MachineInstr *createMockInlineAsm(MachineFunction &MF) {return MF.CreateMachineInstr(MockInstrDescs[MockInstrId::InlAsm], DebugLoc());}TEST(RegAllocScoreTest, SkipDebugKillInlineAsm) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);auto *MBB = MF->CreateMachineBasicBlock();MF->insert(MF->end(), MBB);auto MBBFreqMock = [&](const MachineBasicBlock &_MBB) -> double {assert(&_MBB == MBB);return 0.5;};auto Next = MBB->end();Next = MBB->insertAfter(Next, createMockInlineAsm(*MF));Next = MBB->insertAfter(Next, createMockDebug(*MF));Next = MBB->insertAfter(Next, createMockKill(*MF));const auto Score = llvm::calculateRegAllocScore(*MF, MBBFreqMock, [](const MachineInstr &) { return false; });ASSERT_EQ(MF->size(), 1U);ASSERT_EQ(Score, RegAllocScore());}TEST(RegAllocScoreTest, Counts) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);auto *MBB1 = MF->CreateMachineBasicBlock();auto *MBB2 = MF->CreateMachineBasicBlock();MF->insert(MF->end(), MBB1);MF->insert(MF->end(), MBB2);const double Freq1 = 0.5;const double Freq2 = 10.0;auto MBBFreqMock = [&](const MachineBasicBlock &MBB) -> double {if (&MBB == MBB1)return Freq1;if (&MBB == MBB2)return Freq2;llvm_unreachable("We only created 2 basic blocks");};auto Next = MBB1->end();Next = MBB1->insertAfter(Next, createMockCopy(*MF));Next = MBB1->insertAfter(Next, createMockLoad(*MF));Next = MBB1->insertAfter(Next, createMockLoad(*MF));Next = MBB1->insertAfter(Next, createMockStore(*MF));auto *CheapRemat = createMockCheapRemat(*MF);MBB1->insertAfter(Next, CheapRemat);Next = MBB2->end();Next = MBB2->insertAfter(Next, createMockLoad(*MF));Next = MBB2->insertAfter(Next, createMockStore(*MF));Next = MBB2->insertAfter(Next, createMockLoadStore(*MF));auto *ExpensiveRemat = createMockExpensiveRemat(*MF);MBB2->insertAfter(Next, ExpensiveRemat);auto IsRemat = [&](const MachineInstr &MI) {return &MI == CheapRemat || &MI == ExpensiveRemat;};ASSERT_EQ(MF->size(), 2U);const auto TotalScore =llvm::calculateRegAllocScore(*MF, MBBFreqMock, IsRemat);ASSERT_EQ(Freq1, TotalScore.copyCounts());ASSERT_EQ(2.0 * Freq1 + Freq2, TotalScore.loadCounts());ASSERT_EQ(Freq1 + Freq2, TotalScore.storeCounts());ASSERT_EQ(Freq2, TotalScore.loadStoreCounts());ASSERT_EQ(Freq1, TotalScore.cheapRematCounts());ASSERT_EQ(Freq2, TotalScore.expensiveRematCounts());ASSERT_EQ(TotalScore.getScore(),TotalScore.copyCounts() * CopyWeight +TotalScore.loadCounts() * LoadWeight +TotalScore.storeCounts() * StoreWeight +TotalScore.loadStoreCounts() * (LoadWeight + StoreWeight) +TotalScore.cheapRematCounts() * CheapRematWeight +TotalScore.expensiveRematCounts() * ExpensiveRematWeight);}} // end namespace
//===- llvm/unittest/CodeGen/PassManager.cpp - PassManager tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Triple.h"#include "llvm/Analysis/CGSCCPassManager.h"#include "llvm/Analysis/LoopAnalysisManager.h"#include "llvm/AsmParser/Parser.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/MachinePassManager.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Passes/PassBuilder.h"#include "llvm/Support/Host.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"using namespace llvm;namespace {class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> {public:struct Result {Result(int Count) : InstructionCount(Count) {}int InstructionCount;};/// Run the analysis pass over the function and return a result.Result run(Function &F, FunctionAnalysisManager &AM) {int Count = 0;for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI)for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE;++II)++Count;return Result(Count);}private:friend AnalysisInfoMixin<TestFunctionAnalysis>;static AnalysisKey Key;};AnalysisKey TestFunctionAnalysis::Key;class TestMachineFunctionAnalysis: public AnalysisInfoMixin<TestMachineFunctionAnalysis> {public:struct Result {Result(int Count) : InstructionCount(Count) {}int InstructionCount;};/// Run the analysis pass over the machine function and return a result.Result run(MachineFunction &MF, MachineFunctionAnalysisManager::Base &AM) {auto &MFAM = static_cast<MachineFunctionAnalysisManager &>(AM);// Query function analysis result.TestFunctionAnalysis::Result &FAR =MFAM.getResult<TestFunctionAnalysis>(MF.getFunction());// + 5return FAR.InstructionCount;}private:friend AnalysisInfoMixin<TestMachineFunctionAnalysis>;static AnalysisKey Key;};AnalysisKey TestMachineFunctionAnalysis::Key;const std::string DoInitErrMsg = "doInitialization failed";const std::string DoFinalErrMsg = "doFinalization failed";struct TestMachineFunctionPass : public PassInfoMixin<TestMachineFunctionPass> {TestMachineFunctionPass(int &Count, std::vector<int> &BeforeInitialization,std::vector<int> &BeforeFinalization,std::vector<int> &MachineFunctionPassCount): Count(Count), BeforeInitialization(BeforeInitialization),BeforeFinalization(BeforeFinalization),MachineFunctionPassCount(MachineFunctionPassCount) {}Error doInitialization(Module &M, MachineFunctionAnalysisManager &MFAM) {// Force doInitialization fail by starting with big `Count`.if (Count > 10000)return make_error<StringError>(DoInitErrMsg, inconvertibleErrorCode());// + 1++Count;BeforeInitialization.push_back(Count);return Error::success();}Error doFinalization(Module &M, MachineFunctionAnalysisManager &MFAM) {// Force doFinalization fail by starting with big `Count`.if (Count > 1000)return make_error<StringError>(DoFinalErrMsg, inconvertibleErrorCode());// + 1++Count;BeforeFinalization.push_back(Count);return Error::success();}PreservedAnalyses run(MachineFunction &MF,MachineFunctionAnalysisManager &MFAM) {// Query function analysis result.TestFunctionAnalysis::Result &FAR =MFAM.getResult<TestFunctionAnalysis>(MF.getFunction());// 3 + 1 + 1 = 5Count += FAR.InstructionCount;// Query module analysis result.MachineModuleInfo &MMI =MFAM.getResult<MachineModuleAnalysis>(*MF.getFunction().getParent());// 1 + 1 + 1 = 3Count += (MMI.getModule() == MF.getFunction().getParent());// Query machine function analysis result.TestMachineFunctionAnalysis::Result &MFAR =MFAM.getResult<TestMachineFunctionAnalysis>(MF);// 3 + 1 + 1 = 5Count += MFAR.InstructionCount;MachineFunctionPassCount.push_back(Count);return PreservedAnalyses::none();}int &Count;std::vector<int> &BeforeInitialization;std::vector<int> &BeforeFinalization;std::vector<int> &MachineFunctionPassCount;};struct TestMachineModulePass : public PassInfoMixin<TestMachineModulePass> {TestMachineModulePass(int &Count, std::vector<int> &MachineModulePassCount): Count(Count), MachineModulePassCount(MachineModulePassCount) {}Error run(Module &M, MachineFunctionAnalysisManager &MFAM) {MachineModuleInfo &MMI = MFAM.getResult<MachineModuleAnalysis>(M);// + 1Count += (MMI.getModule() == &M);MachineModulePassCount.push_back(Count);return Error::success();}PreservedAnalyses run(MachineFunction &MF,MachineFunctionAnalysisManager &AM) {llvm_unreachable("This should never be reached because this is machine module pass");}int &Count;std::vector<int> &MachineModulePassCount;};std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) {SMDiagnostic Err;return parseAssemblyString(IR, Err, Context);}class PassManagerTest : public ::testing::Test {protected:LLVMContext Context;std::unique_ptr<Module> M;std::unique_ptr<TargetMachine> TM;public:PassManagerTest(): M(parseIR(Context, "define void @f() {\n""entry:\n"" call void @g()\n"" call void @h()\n"" ret void\n""}\n""define void @g() {\n"" ret void\n""}\n""define void @h() {\n"" ret void\n""}\n")) {// MachineModuleAnalysis needs a TargetMachine instance.llvm::InitializeAllTargets();std::string TripleName = Triple::normalize(sys::getDefaultTargetTriple());std::string Error;const Target *TheTarget =TargetRegistry::lookupTarget(TripleName, Error);if (!TheTarget)return;TargetOptions Options;TM.reset(TheTarget->createTargetMachine(TripleName, "", "",Options, None));}};TEST_F(PassManagerTest, Basic) {if (!TM)GTEST_SKIP();LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(TM.get());M->setDataLayout(TM->createDataLayout());LoopAnalysisManager LAM;FunctionAnalysisManager FAM;CGSCCAnalysisManager CGAM;ModuleAnalysisManager MAM;PassBuilder PB(TM.get());PB.registerModuleAnalyses(MAM);PB.registerFunctionAnalyses(FAM);PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);FAM.registerPass([&] { return TestFunctionAnalysis(); });FAM.registerPass([&] { return PassInstrumentationAnalysis(); });MAM.registerPass([&] { return MachineModuleAnalysis(LLVMTM); });MAM.registerPass([&] { return PassInstrumentationAnalysis(); });MachineFunctionAnalysisManager MFAM;{// Test move assignment.MachineFunctionAnalysisManager NestedMFAM(FAM, MAM);NestedMFAM.registerPass([&] { return PassInstrumentationAnalysis(); });NestedMFAM.registerPass([&] { return TestMachineFunctionAnalysis(); });MFAM = std::move(NestedMFAM);}int Count = 0;std::vector<int> BeforeInitialization[2];std::vector<int> BeforeFinalization[2];std::vector<int> TestMachineFunctionCount[2];std::vector<int> TestMachineModuleCount[2];MachineFunctionPassManager MFPM;{// Test move assignment.MachineFunctionPassManager NestedMFPM;NestedMFPM.addPass(TestMachineModulePass(Count, TestMachineModuleCount[0]));NestedMFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[0],BeforeFinalization[0],TestMachineFunctionCount[0]));NestedMFPM.addPass(TestMachineModulePass(Count, TestMachineModuleCount[1]));NestedMFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[1],BeforeFinalization[1],TestMachineFunctionCount[1]));MFPM = std::move(NestedMFPM);}ASSERT_FALSE(errorToBool(MFPM.run(*M, MFAM)));// Check first machine module passEXPECT_EQ(1u, TestMachineModuleCount[0].size());EXPECT_EQ(3, TestMachineModuleCount[0][0]);// Check first machine function passEXPECT_EQ(1u, BeforeInitialization[0].size());EXPECT_EQ(1, BeforeInitialization[0][0]);EXPECT_EQ(3u, TestMachineFunctionCount[0].size());EXPECT_EQ(10, TestMachineFunctionCount[0][0]);EXPECT_EQ(13, TestMachineFunctionCount[0][1]);EXPECT_EQ(16, TestMachineFunctionCount[0][2]);EXPECT_EQ(1u, BeforeFinalization[0].size());EXPECT_EQ(31, BeforeFinalization[0][0]);// Check second machine module passEXPECT_EQ(1u, TestMachineModuleCount[1].size());EXPECT_EQ(17, TestMachineModuleCount[1][0]);// Check second machine function passEXPECT_EQ(1u, BeforeInitialization[1].size());EXPECT_EQ(2, BeforeInitialization[1][0]);EXPECT_EQ(3u, TestMachineFunctionCount[1].size());EXPECT_EQ(24, TestMachineFunctionCount[1][0]);EXPECT_EQ(27, TestMachineFunctionCount[1][1]);EXPECT_EQ(30, TestMachineFunctionCount[1][2]);EXPECT_EQ(1u, BeforeFinalization[1].size());EXPECT_EQ(32, BeforeFinalization[1][0]);EXPECT_EQ(32, Count);// doInitialization returns errorCount = 10000;MFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[1],BeforeFinalization[1],TestMachineFunctionCount[1]));std::string Message;llvm::handleAllErrors(MFPM.run(*M, MFAM), [&](llvm::StringError &Error) {Message = Error.getMessage();});EXPECT_EQ(Message, DoInitErrMsg);// doFinalization returns errorCount = 1000;MFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[1],BeforeFinalization[1],TestMachineFunctionCount[1]));llvm::handleAllErrors(MFPM.run(*M, MFAM), [&](llvm::StringError &Error) {Message = Error.getMessage();});EXPECT_EQ(Message, DoFinalErrMsg);}} // namespace
//===- MachineOperandTest.cpp ---------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/MachineOperand.h"#include "llvm/ADT/ilist_node.h"#include "llvm/IR/Constants.h"#include "llvm/IR/InstrTypes.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/ModuleSlotTracker.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCContext.h"#include "llvm/Support/LowLevelTypeImpl.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(MachineOperandTest, ChangeToTargetIndexTest) {// Creating a MachineOperand to change it to TargetIndexMachineOperand MO = MachineOperand::CreateImm(50);// Checking some precondition on the newly created// MachineOperand.ASSERT_TRUE(MO.isImm());ASSERT_TRUE(MO.getImm() == 50);ASSERT_FALSE(MO.isTargetIndex());// Changing to TargetIndex with some arbitrary values// for index, offset and flags.MO.ChangeToTargetIndex(74, 57, 12);// Checking that the mutation to TargetIndex happened// correctly.ASSERT_TRUE(MO.isTargetIndex());ASSERT_TRUE(MO.getIndex() == 74);ASSERT_TRUE(MO.getOffset() == 57);ASSERT_TRUE(MO.getTargetFlags() == 12);}TEST(MachineOperandTest, PrintRegisterMask) {uint32_t Dummy;MachineOperand MO = MachineOperand::CreateRegMask(&Dummy);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isRegMask());ASSERT_TRUE(MO.getRegMask() == &Dummy);// Print a MachineOperand containing a RegMask. Here we check that without a// TRI and IntrinsicInfo we still print a less detailed regmask.std::string str;raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "<regmask ...>");}TEST(MachineOperandTest, PrintSubReg) {// Create a MachineOperand with RegNum=1 and SubReg=5.MachineOperand MO = MachineOperand::CreateReg(/*Reg=*/1, /*isDef=*/false, /*isImp=*/false, /*isKill=*/false,/*isDead=*/false, /*isUndef=*/false, /*isEarlyClobber=*/false,/*SubReg=*/5, /*isDebug=*/false, /*isInternalRead=*/false);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isReg());ASSERT_TRUE(MO.getReg() == 1);ASSERT_TRUE(MO.getSubReg() == 5);// Print a MachineOperand containing a SubReg. Here we check that without a// TRI and IntrinsicInfo we can still print the subreg index.std::string str;raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "$physreg1.subreg5");}TEST(MachineOperandTest, PrintCImm) {LLVMContext Context;APInt Int(128, UINT64_MAX);++Int;ConstantInt *CImm = ConstantInt::get(Context, Int);// Create a MachineOperand with an Imm=(UINT64_MAX + 1)MachineOperand MO = MachineOperand::CreateCImm(CImm);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isCImm());ASSERT_TRUE(MO.getCImm() == CImm);ASSERT_TRUE(MO.getCImm()->getValue() == Int);// Print a MachineOperand containing a SubReg. Here we check that without a// TRI and IntrinsicInfo we can still print the subreg index.std::string str;raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "i128 18446744073709551616");}TEST(MachineOperandTest, PrintSubRegIndex) {// Create a MachineOperand with an immediate and print it as a subreg index.MachineOperand MO = MachineOperand::CreateImm(3);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isImm());ASSERT_TRUE(MO.getImm() == 3);// Print a MachineOperand containing a SubRegIdx. Here we check that without a// TRI and IntrinsicInfo we can print the operand as a subreg index.std::string str;raw_string_ostream OS(str);MachineOperand::printSubRegIdx(OS, MO.getImm(), nullptr);ASSERT_TRUE(OS.str() == "%subreg.3");}TEST(MachineOperandTest, PrintCPI) {// Create a MachineOperand with a constant pool index and print it.MachineOperand MO = MachineOperand::CreateCPI(0, 8);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isCPI());ASSERT_TRUE(MO.getIndex() == 0);ASSERT_TRUE(MO.getOffset() == 8);// Print a MachineOperand containing a constant pool index and a positive// offset.std::string str;{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "%const.0 + 8");}str.clear();MO.setOffset(-12);// Print a MachineOperand containing a constant pool index and a negative// offset.{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "%const.0 - 12");}}TEST(MachineOperandTest, PrintTargetIndexName) {// Create a MachineOperand with a target index and print it.MachineOperand MO = MachineOperand::CreateTargetIndex(0, 8);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isTargetIndex());ASSERT_TRUE(MO.getIndex() == 0);ASSERT_TRUE(MO.getOffset() == 8);// Print a MachineOperand containing a target index and a positive offset.std::string str;{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "target-index(<unknown>) + 8");}str.clear();MO.setOffset(-12);// Print a MachineOperand containing a target index and a negative offset.{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "target-index(<unknown>) - 12");}}TEST(MachineOperandTest, PrintJumpTableIndex) {// Create a MachineOperand with a jump-table index and print it.MachineOperand MO = MachineOperand::CreateJTI(3);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isJTI());ASSERT_TRUE(MO.getIndex() == 3);// Print a MachineOperand containing a jump-table index.std::string str;raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "%jump-table.3");}TEST(MachineOperandTest, PrintExternalSymbol) {// Create a MachineOperand with an external symbol and print it.MachineOperand MO = MachineOperand::CreateES("foo");// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isSymbol());ASSERT_TRUE(MO.getSymbolName() == StringRef("foo"));// Print a MachineOperand containing an external symbol and no offset.std::string str;{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "&foo");}str.clear();MO.setOffset(12);// Print a MachineOperand containing an external symbol and a positive offset.{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "&foo + 12");}str.clear();MO.setOffset(-12);// Print a MachineOperand containing an external symbol and a negative offset.{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "&foo - 12");}}TEST(MachineOperandTest, PrintGlobalAddress) {LLVMContext Ctx;Module M("MachineOperandGVTest", Ctx);M.getOrInsertGlobal("foo", Type::getInt32Ty(Ctx));GlobalValue *GV = M.getNamedValue("foo");// Create a MachineOperand with a global address and a positive offset and// print it.MachineOperand MO = MachineOperand::CreateGA(GV, 12);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isGlobal());ASSERT_TRUE(MO.getGlobal() == GV);ASSERT_TRUE(MO.getOffset() == 12);std::string str;// Print a MachineOperand containing a global address and a positive offset.{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "@foo + 12");}str.clear();MO.setOffset(-12);// Print a MachineOperand containing a global address and a negative offset.{raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "@foo - 12");}}TEST(MachineOperandTest, PrintRegisterLiveOut) {// Create a MachineOperand with a register live out list and print it.uint32_t Mask = 0;MachineOperand MO = MachineOperand::CreateRegLiveOut(&Mask);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isRegLiveOut());ASSERT_TRUE(MO.getRegLiveOut() == &Mask);std::string str;// Print a MachineOperand containing a register live out list without a TRI.raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "liveout(<unknown>)");}TEST(MachineOperandTest, PrintMetadata) {LLVMContext Ctx;Module M("MachineOperandMDNodeTest", Ctx);NamedMDNode *MD = M.getOrInsertNamedMetadata("namedmd");ModuleSlotTracker MST(&M);Metadata *MDS = MDString::get(Ctx, "foo");MDNode *Node = MDNode::get(Ctx, MDS);MD->addOperand(Node);// Create a MachineOperand with a metadata and print it.MachineOperand MO = MachineOperand::CreateMetadata(Node);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isMetadata());ASSERT_TRUE(MO.getMetadata() == Node);std::string str;// Print a MachineOperand containing a metadata node.raw_string_ostream OS(str);MO.print(OS, MST, LLT{}, /*OpIdx*/~0U, /*PrintDef=*/false, /*IsStandalone=*/false,/*ShouldPrintRegisterTies=*/false, 0, /*TRI=*/nullptr,/*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "!0");}TEST(MachineOperandTest, PrintMCSymbol) {MCAsmInfo MAI;Triple T = Triple("unknown-unknown-unknown");MCContext Ctx(T, &MAI, /*MRI=*/nullptr, /*MSTI=*/nullptr);MCSymbol *Sym = Ctx.getOrCreateSymbol("foo");// Create a MachineOperand with a metadata and print it.MachineOperand MO = MachineOperand::CreateMCSymbol(Sym);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isMCSymbol());ASSERT_TRUE(MO.getMCSymbol() == Sym);std::string str;// Print a MachineOperand containing a metadata node.raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "<mcsymbol foo>");}TEST(MachineOperandTest, PrintCFI) {// Create a MachineOperand with a CFI index but no function and print it.MachineOperand MO = MachineOperand::CreateCFIIndex(8);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isCFIIndex());ASSERT_TRUE(MO.getCFIIndex() == 8);std::string str;// Print a MachineOperand containing a CFI Index node but no machine function// attached to it.raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "<cfi directive>");}TEST(MachineOperandTest, PrintIntrinsicID) {// Create a MachineOperand with a generic intrinsic ID.MachineOperand MO = MachineOperand::CreateIntrinsicID(Intrinsic::bswap);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isIntrinsicID());ASSERT_TRUE(MO.getIntrinsicID() == Intrinsic::bswap);std::string str;{// Print a MachineOperand containing a generic intrinsic ID.raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "intrinsic(@llvm.bswap)");}str.clear();// Set a target-specific intrinsic.MO = MachineOperand::CreateIntrinsicID((Intrinsic::ID)-1);{// Print a MachineOperand containing a target-specific intrinsic ID but not// IntrinsicInfo.raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "intrinsic(4294967295)");}}TEST(MachineOperandTest, PrintPredicate) {// Create a MachineOperand with a generic intrinsic ID.MachineOperand MO = MachineOperand::CreatePredicate(CmpInst::ICMP_EQ);// Checking some preconditions on the newly created// MachineOperand.ASSERT_TRUE(MO.isPredicate());ASSERT_TRUE(MO.getPredicate() == CmpInst::ICMP_EQ);std::string str;// Print a MachineOperand containing a int predicate ICMP_EQ.raw_string_ostream OS(str);MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr);ASSERT_TRUE(OS.str() == "intpred(eq)");}TEST(MachineOperandTest, HashValue) {char SymName1[] = "test";char SymName2[] = "test";MachineOperand MO1 = MachineOperand::CreateES(SymName1);MachineOperand MO2 = MachineOperand::CreateES(SymName2);ASSERT_NE(SymName1, SymName2);ASSERT_EQ(hash_value(MO1), hash_value(MO2));ASSERT_TRUE(MO1.isIdenticalTo(MO2));}} // end namespace
//===- MachineInstrTest.cpp -----------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/MachineInstr.h"#include "llvm/ADT/Triple.h"#include "llvm/CodeGen/MachineBasicBlock.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineMemOperand.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetFrameLowering.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/ModuleSlotTracker.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCSymbol.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Include helper functions to ease the manipulation of MachineFunctions.#include "MFCommon.inc"std::unique_ptr<MCContext> createMCContext(MCAsmInfo *AsmInfo) {Triple TheTriple(/*ArchStr=*/"", /*VendorStr=*/"", /*OSStr=*/"",/*EnvironmentStr=*/"elf");return std::make_unique<MCContext>(TheTriple, AsmInfo, nullptr, nullptr,nullptr, nullptr, false);}// This test makes sure that MachineInstr::isIdenticalTo handles Defs correctly// for various combinations of IgnoreDefs, and also that it is symmetrical.TEST(IsIdenticalToTest, DifferentDefs) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);unsigned short NumOps = 2;unsigned char NumDefs = 1;MCOperandInfo OpInfo[] = {{0, 0, MCOI::OPERAND_REGISTER, 0},{0, 1 << MCOI::OptionalDef, MCOI::OPERAND_REGISTER, 0}};MCInstrDesc MCID = {0, NumOps, NumDefs, 0, 0, 1ULL << MCID::HasOptionalDef,0, nullptr, nullptr, OpInfo};// Create two MIs with different virtual reg defs and the same uses.unsigned VirtualDef1 = -42; // The value doesn't matter, but the sign does.unsigned VirtualDef2 = -43;unsigned VirtualUse = -44;auto MI1 = MF->CreateMachineInstr(MCID, DebugLoc());MI1->addOperand(*MF, MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));MI1->addOperand(*MF, MachineOperand::CreateReg(VirtualUse, /*isDef*/ false));auto MI2 = MF->CreateMachineInstr(MCID, DebugLoc());MI2->addOperand(*MF, MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true));MI2->addOperand(*MF, MachineOperand::CreateReg(VirtualUse, /*isDef*/ false));// Check that they are identical when we ignore virtual register defs, but not// when we check defs.ASSERT_FALSE(MI1->isIdenticalTo(*MI2, MachineInstr::CheckDefs));ASSERT_FALSE(MI2->isIdenticalTo(*MI1, MachineInstr::CheckDefs));ASSERT_TRUE(MI1->isIdenticalTo(*MI2, MachineInstr::IgnoreVRegDefs));ASSERT_TRUE(MI2->isIdenticalTo(*MI1, MachineInstr::IgnoreVRegDefs));// Create two MIs with different virtual reg defs, and a def or use of a// sentinel register.unsigned SentinelReg = 0;auto MI3 = MF->CreateMachineInstr(MCID, DebugLoc());MI3->addOperand(*MF, MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));MI3->addOperand(*MF, MachineOperand::CreateReg(SentinelReg, /*isDef*/ true));auto MI4 = MF->CreateMachineInstr(MCID, DebugLoc());MI4->addOperand(*MF, MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true));MI4->addOperand(*MF, MachineOperand::CreateReg(SentinelReg, /*isDef*/ false));// Check that they are never identical.ASSERT_FALSE(MI3->isIdenticalTo(*MI4, MachineInstr::CheckDefs));ASSERT_FALSE(MI4->isIdenticalTo(*MI3, MachineInstr::CheckDefs));ASSERT_FALSE(MI3->isIdenticalTo(*MI4, MachineInstr::IgnoreVRegDefs));ASSERT_FALSE(MI4->isIdenticalTo(*MI3, MachineInstr::IgnoreVRegDefs));}// Check that MachineInstrExpressionTrait::isEqual is symmetric and in sync with// MachineInstrExpressionTrait::getHashValuevoid checkHashAndIsEqualMatch(MachineInstr *MI1, MachineInstr *MI2) {bool IsEqual1 = MachineInstrExpressionTrait::isEqual(MI1, MI2);bool IsEqual2 = MachineInstrExpressionTrait::isEqual(MI2, MI1);ASSERT_EQ(IsEqual1, IsEqual2);auto Hash1 = MachineInstrExpressionTrait::getHashValue(MI1);auto Hash2 = MachineInstrExpressionTrait::getHashValue(MI2);ASSERT_EQ(IsEqual1, Hash1 == Hash2);}// This test makes sure that MachineInstrExpressionTraits::isEqual is in sync// with MachineInstrExpressionTraits::getHashValue.TEST(MachineInstrExpressionTraitTest, IsEqualAgreesWithGetHashValue) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);unsigned short NumOps = 2;unsigned char NumDefs = 1;MCOperandInfo OpInfo[] = {{0, 0, MCOI::OPERAND_REGISTER, 0},{0, 1 << MCOI::OptionalDef, MCOI::OPERAND_REGISTER, 0}};MCInstrDesc MCID = {0, NumOps, NumDefs, 0, 0, 1ULL << MCID::HasOptionalDef,0, nullptr, nullptr, OpInfo};// Define a series of instructions with different kinds of operands and make// sure that the hash function is consistent with isEqual for various// combinations of them.unsigned VirtualDef1 = -42;unsigned VirtualDef2 = -43;unsigned VirtualReg = -44;unsigned SentinelReg = 0;unsigned PhysicalReg = 45;auto VD1VU = MF->CreateMachineInstr(MCID, DebugLoc());VD1VU->addOperand(*MF,MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));VD1VU->addOperand(*MF,MachineOperand::CreateReg(VirtualReg, /*isDef*/ false));auto VD2VU = MF->CreateMachineInstr(MCID, DebugLoc());VD2VU->addOperand(*MF,MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true));VD2VU->addOperand(*MF,MachineOperand::CreateReg(VirtualReg, /*isDef*/ false));auto VD1SU = MF->CreateMachineInstr(MCID, DebugLoc());VD1SU->addOperand(*MF,MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));VD1SU->addOperand(*MF,MachineOperand::CreateReg(SentinelReg, /*isDef*/ false));auto VD1SD = MF->CreateMachineInstr(MCID, DebugLoc());VD1SD->addOperand(*MF,MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));VD1SD->addOperand(*MF,MachineOperand::CreateReg(SentinelReg, /*isDef*/ true));auto VD2PU = MF->CreateMachineInstr(MCID, DebugLoc());VD2PU->addOperand(*MF,MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true));VD2PU->addOperand(*MF,MachineOperand::CreateReg(PhysicalReg, /*isDef*/ false));auto VD2PD = MF->CreateMachineInstr(MCID, DebugLoc());VD2PD->addOperand(*MF,MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true));VD2PD->addOperand(*MF,MachineOperand::CreateReg(PhysicalReg, /*isDef*/ true));checkHashAndIsEqualMatch(VD1VU, VD2VU);checkHashAndIsEqualMatch(VD1VU, VD1SU);checkHashAndIsEqualMatch(VD1VU, VD1SD);checkHashAndIsEqualMatch(VD1VU, VD2PU);checkHashAndIsEqualMatch(VD1VU, VD2PD);checkHashAndIsEqualMatch(VD2VU, VD1SU);checkHashAndIsEqualMatch(VD2VU, VD1SD);checkHashAndIsEqualMatch(VD2VU, VD2PU);checkHashAndIsEqualMatch(VD2VU, VD2PD);checkHashAndIsEqualMatch(VD1SU, VD1SD);checkHashAndIsEqualMatch(VD1SU, VD2PU);checkHashAndIsEqualMatch(VD1SU, VD2PD);checkHashAndIsEqualMatch(VD1SD, VD2PU);checkHashAndIsEqualMatch(VD1SD, VD2PD);checkHashAndIsEqualMatch(VD2PU, VD2PD);}TEST(MachineInstrPrintingTest, DebugLocPrinting) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);MCOperandInfo OpInfo{0, 0, MCOI::OPERAND_REGISTER, 0};MCInstrDesc MCID = {0, 1, 1, 0, 0, 0, 0, nullptr, nullptr, &OpInfo};DIFile *DIF = DIFile::getDistinct(Ctx, "filename", "");DISubprogram *DIS = DISubprogram::getDistinct(Ctx, nullptr, "", "", DIF, 0, nullptr, 0, nullptr, 0, 0, DINode::FlagZero,DISubprogram::SPFlagZero, nullptr);DILocation *DIL = DILocation::get(Ctx, 1, 5, DIS);DebugLoc DL(DIL);MachineInstr *MI = MF->CreateMachineInstr(MCID, DL);MI->addOperand(*MF, MachineOperand::CreateReg(0, /*isDef*/ true));std::string str;raw_string_ostream OS(str);MI->print(OS, /*IsStandalone*/true, /*SkipOpers*/false, /*SkipDebugLoc*/false,/*AddNewLine*/false);ASSERT_TRUE(StringRef(OS.str()).startswith("$noreg = UNKNOWN debug-location "));ASSERT_TRUE(StringRef(OS.str()).endswith("filename:1:5"));}TEST(MachineInstrSpan, DistanceBegin) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);auto MBB = MF->CreateMachineBasicBlock();MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr};auto MII = MBB->begin();MachineInstrSpan MIS(MII, MBB);ASSERT_TRUE(MIS.empty());auto MI = MF->CreateMachineInstr(MCID, DebugLoc());MBB->insert(MII, MI);ASSERT_TRUE(std::distance(MIS.begin(), MII) == 1);}TEST(MachineInstrSpan, DistanceEnd) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);auto MBB = MF->CreateMachineBasicBlock();MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr};auto MII = MBB->end();MachineInstrSpan MIS(MII, MBB);ASSERT_TRUE(MIS.empty());auto MI = MF->CreateMachineInstr(MCID, DebugLoc());MBB->insert(MII, MI);ASSERT_TRUE(std::distance(MIS.begin(), MII) == 1);}TEST(MachineInstrExtraInfo, AddExtraInfo) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr};auto MI = MF->CreateMachineInstr(MCID, DebugLoc());auto MAI = MCAsmInfo();auto MC = createMCContext(&MAI);auto MMO = MF->getMachineMemOperand(MachinePointerInfo(),MachineMemOperand::MOLoad, 8, Align(8));SmallVector<MachineMemOperand *, 2> MMOs;MMOs.push_back(MMO);MCSymbol *Sym1 = MC->createTempSymbol("pre_label", false);MCSymbol *Sym2 = MC->createTempSymbol("post_label", false);MDNode *MDN = MDNode::getDistinct(Ctx, None);ASSERT_TRUE(MI->memoperands_empty());ASSERT_FALSE(MI->getPreInstrSymbol());ASSERT_FALSE(MI->getPostInstrSymbol());ASSERT_FALSE(MI->getHeapAllocMarker());MI->setMemRefs(*MF, MMOs);ASSERT_TRUE(MI->memoperands().size() == 1);ASSERT_FALSE(MI->getPreInstrSymbol());ASSERT_FALSE(MI->getPostInstrSymbol());ASSERT_FALSE(MI->getHeapAllocMarker());MI->setPreInstrSymbol(*MF, Sym1);ASSERT_TRUE(MI->memoperands().size() == 1);ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);ASSERT_FALSE(MI->getPostInstrSymbol());ASSERT_FALSE(MI->getHeapAllocMarker());MI->setPostInstrSymbol(*MF, Sym2);ASSERT_TRUE(MI->memoperands().size() == 1);ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2);ASSERT_FALSE(MI->getHeapAllocMarker());MI->setHeapAllocMarker(*MF, MDN);ASSERT_TRUE(MI->memoperands().size() == 1);ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2);ASSERT_TRUE(MI->getHeapAllocMarker() == MDN);}TEST(MachineInstrExtraInfo, ChangeExtraInfo) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr};auto MI = MF->CreateMachineInstr(MCID, DebugLoc());auto MAI = MCAsmInfo();auto MC = createMCContext(&MAI);auto MMO = MF->getMachineMemOperand(MachinePointerInfo(),MachineMemOperand::MOLoad, 8, Align(8));SmallVector<MachineMemOperand *, 2> MMOs;MMOs.push_back(MMO);MCSymbol *Sym1 = MC->createTempSymbol("pre_label", false);MCSymbol *Sym2 = MC->createTempSymbol("post_label", false);MDNode *MDN = MDNode::getDistinct(Ctx, None);MI->setMemRefs(*MF, MMOs);MI->setPreInstrSymbol(*MF, Sym1);MI->setPostInstrSymbol(*MF, Sym2);MI->setHeapAllocMarker(*MF, MDN);MMOs.push_back(MMO);MI->setMemRefs(*MF, MMOs);ASSERT_TRUE(MI->memoperands().size() == 2);ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2);ASSERT_TRUE(MI->getHeapAllocMarker() == MDN);MI->setPostInstrSymbol(*MF, Sym1);ASSERT_TRUE(MI->memoperands().size() == 2);ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);ASSERT_TRUE(MI->getPostInstrSymbol() == Sym1);ASSERT_TRUE(MI->getHeapAllocMarker() == MDN);}TEST(MachineInstrExtraInfo, RemoveExtraInfo) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr};auto MI = MF->CreateMachineInstr(MCID, DebugLoc());auto MAI = MCAsmInfo();auto MC = createMCContext(&MAI);auto MMO = MF->getMachineMemOperand(MachinePointerInfo(),MachineMemOperand::MOLoad, 8, Align(8));SmallVector<MachineMemOperand *, 2> MMOs;MMOs.push_back(MMO);MMOs.push_back(MMO);MCSymbol *Sym1 = MC->createTempSymbol("pre_label", false);MCSymbol *Sym2 = MC->createTempSymbol("post_label", false);MDNode *MDN = MDNode::getDistinct(Ctx, None);MI->setMemRefs(*MF, MMOs);MI->setPreInstrSymbol(*MF, Sym1);MI->setPostInstrSymbol(*MF, Sym2);MI->setHeapAllocMarker(*MF, MDN);MI->setPostInstrSymbol(*MF, nullptr);ASSERT_TRUE(MI->memoperands().size() == 2);ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);ASSERT_FALSE(MI->getPostInstrSymbol());ASSERT_TRUE(MI->getHeapAllocMarker() == MDN);MI->setHeapAllocMarker(*MF, nullptr);ASSERT_TRUE(MI->memoperands().size() == 2);ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);ASSERT_FALSE(MI->getPostInstrSymbol());ASSERT_FALSE(MI->getHeapAllocMarker());MI->setPreInstrSymbol(*MF, nullptr);ASSERT_TRUE(MI->memoperands().size() == 2);ASSERT_FALSE(MI->getPreInstrSymbol());ASSERT_FALSE(MI->getPostInstrSymbol());ASSERT_FALSE(MI->getHeapAllocMarker());MI->setMemRefs(*MF, {});ASSERT_TRUE(MI->memoperands_empty());ASSERT_FALSE(MI->getPreInstrSymbol());ASSERT_FALSE(MI->getPostInstrSymbol());ASSERT_FALSE(MI->getHeapAllocMarker());}TEST(MachineInstrDebugValue, AddDebugValueOperand) {LLVMContext Ctx;Module Mod("Module", Ctx);auto MF = createMachineFunction(Ctx, Mod);for (const unsigned short Opcode :{TargetOpcode::DBG_VALUE, TargetOpcode::DBG_VALUE_LIST,TargetOpcode::DBG_INSTR_REF, TargetOpcode::DBG_PHI,TargetOpcode::DBG_LABEL}) {const MCInstrDesc MCID = {Opcode, 0, 0,0, 0, (1ULL << MCID::Pseudo) | (1ULL << MCID::Variadic),0, nullptr, nullptr,nullptr};auto *MI = MF->CreateMachineInstr(MCID, DebugLoc());MI->addOperand(*MF, MachineOperand::CreateReg(0, /*isDef*/ false));MI->addOperand(*MF, MachineOperand::CreateImm(0));MI->getOperand(1).ChangeToRegister(0, false);ASSERT_TRUE(MI->getOperand(0).isDebug());ASSERT_TRUE(MI->getOperand(1).isDebug());}}static_assert(std::is_trivially_copyable<MCOperand>::value,"trivially copyable");} // end namespace
//===- MachineInstrBundleIteratorTest.cpp ---------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/MachineInstrBundleIterator.h"#include "llvm/ADT/ilist_node.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct MyBundledInstr: public ilist_node<MyBundledInstr, ilist_sentinel_tracking<true>> {bool isBundledWithPred() const { return true; }bool isBundledWithSucc() const { return true; }};typedef MachineInstrBundleIterator<MyBundledInstr> bundled_iterator;typedef MachineInstrBundleIterator<const MyBundledInstr> const_bundled_iterator;typedef MachineInstrBundleIterator<MyBundledInstr, true>reverse_bundled_iterator;typedef MachineInstrBundleIterator<const MyBundledInstr, true>const_reverse_bundled_iterator;#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST(MachineInstrBundleIteratorTest, CheckForBundles) {MyBundledInstr MBI;auto I = MBI.getIterator();auto RI = I.getReverse();// Confirm that MBI is always considered bundled.EXPECT_TRUE(MBI.isBundledWithPred());EXPECT_TRUE(MBI.isBundledWithSucc());// Confirm that iterators check in their constructor for bundled iterators.EXPECT_DEATH((void)static_cast<bundled_iterator>(I),"not legal to initialize");EXPECT_DEATH((void)static_cast<bundled_iterator>(MBI),"not legal to initialize");EXPECT_DEATH((void)static_cast<bundled_iterator>(&MBI),"not legal to initialize");EXPECT_DEATH((void)static_cast<const_bundled_iterator>(I),"not legal to initialize");EXPECT_DEATH((void)static_cast<const_bundled_iterator>(MBI),"not legal to initialize");EXPECT_DEATH((void)static_cast<const_bundled_iterator>(&MBI),"not legal to initialize");EXPECT_DEATH((void)static_cast<reverse_bundled_iterator>(RI),"not legal to initialize");EXPECT_DEATH((void)static_cast<reverse_bundled_iterator>(MBI),"not legal to initialize");EXPECT_DEATH((void)static_cast<reverse_bundled_iterator>(&MBI),"not legal to initialize");EXPECT_DEATH((void)static_cast<const_reverse_bundled_iterator>(RI),"not legal to initialize");EXPECT_DEATH((void)static_cast<const_reverse_bundled_iterator>(MBI),"not legal to initialize");EXPECT_DEATH((void)static_cast<const_reverse_bundled_iterator>(&MBI),"not legal to initialize");}#endif#endifTEST(MachineInstrBundleIteratorTest, CompareToBundledMI) {MyBundledInstr MBI;const MyBundledInstr &CMBI = MBI;bundled_iterator I;const_bundled_iterator CI;// Confirm that MBI is always considered bundled.EXPECT_TRUE(MBI.isBundledWithPred());EXPECT_TRUE(MBI.isBundledWithSucc());// These invocations will crash when !NDEBUG if a conversion is taking place.// These checks confirm that comparison operators don't use any conversion// operators.ASSERT_FALSE(MBI == I);ASSERT_FALSE(&MBI == I);ASSERT_FALSE(CMBI == I);ASSERT_FALSE(&CMBI == I);ASSERT_FALSE(I == MBI);ASSERT_FALSE(I == &MBI);ASSERT_FALSE(I == CMBI);ASSERT_FALSE(I == &CMBI);ASSERT_FALSE(MBI == CI);ASSERT_FALSE(&MBI == CI);ASSERT_FALSE(CMBI == CI);ASSERT_FALSE(&CMBI == CI);ASSERT_FALSE(CI == MBI);ASSERT_FALSE(CI == &MBI);ASSERT_FALSE(CI == CMBI);ASSERT_FALSE(CI == &CMBI);ASSERT_FALSE(MBI.getIterator() == I);ASSERT_FALSE(CMBI.getIterator() == I);ASSERT_FALSE(I == MBI.getIterator());ASSERT_FALSE(I == CMBI.getIterator());ASSERT_FALSE(MBI.getIterator() == CI);ASSERT_FALSE(CMBI.getIterator() == CI);ASSERT_FALSE(CI == MBI.getIterator());ASSERT_FALSE(CI == CMBI.getIterator());ASSERT_TRUE(MBI != I);ASSERT_TRUE(&MBI != I);ASSERT_TRUE(CMBI != I);ASSERT_TRUE(&CMBI != I);ASSERT_TRUE(I != MBI);ASSERT_TRUE(I != &MBI);ASSERT_TRUE(I != CMBI);ASSERT_TRUE(I != &CMBI);ASSERT_TRUE(MBI != CI);ASSERT_TRUE(&MBI != CI);ASSERT_TRUE(CMBI != CI);ASSERT_TRUE(&CMBI != CI);ASSERT_TRUE(CI != MBI);ASSERT_TRUE(CI != &MBI);ASSERT_TRUE(CI != CMBI);ASSERT_TRUE(CI != &CMBI);ASSERT_TRUE(MBI.getIterator() != I);ASSERT_TRUE(CMBI.getIterator() != I);ASSERT_TRUE(I != MBI.getIterator());ASSERT_TRUE(I != CMBI.getIterator());ASSERT_TRUE(MBI.getIterator() != CI);ASSERT_TRUE(CMBI.getIterator() != CI);ASSERT_TRUE(CI != MBI.getIterator());ASSERT_TRUE(CI != CMBI.getIterator());}struct MyUnbundledInstr: ilist_node<MyUnbundledInstr, ilist_sentinel_tracking<true>> {bool isBundledWithPred() const { return false; }bool isBundledWithSucc() const { return false; }};typedef MachineInstrBundleIterator<MyUnbundledInstr> unbundled_iterator;typedef MachineInstrBundleIterator<const MyUnbundledInstr>const_unbundled_iterator;typedef MachineInstrBundleIterator<MyUnbundledInstr, true>reverse_unbundled_iterator;typedef MachineInstrBundleIterator<const MyUnbundledInstr, true>const_reverse_unbundled_iterator;TEST(MachineInstrBundleIteratorTest, ReverseConstructor) {simple_ilist<MyUnbundledInstr, ilist_sentinel_tracking<true>> L;const auto &CL = L;MyUnbundledInstr A, B;L.insert(L.end(), A);L.insert(L.end(), B);// Save typing.typedef MachineInstrBundleIterator<MyUnbundledInstr> iterator;typedef MachineInstrBundleIterator<MyUnbundledInstr, true> reverse_iterator;typedef MachineInstrBundleIterator<const MyUnbundledInstr> const_iterator;typedef MachineInstrBundleIterator<const MyUnbundledInstr, true>const_reverse_iterator;// Convert to bundle iterators.auto begin = [&]() -> iterator { return L.begin(); };auto end = [&]() -> iterator { return L.end(); };auto rbegin = [&]() -> reverse_iterator { return L.rbegin(); };auto rend = [&]() -> reverse_iterator { return L.rend(); };auto cbegin = [&]() -> const_iterator { return CL.begin(); };auto cend = [&]() -> const_iterator { return CL.end(); };auto crbegin = [&]() -> const_reverse_iterator { return CL.rbegin(); };auto crend = [&]() -> const_reverse_iterator { return CL.rend(); };// Check conversion values.EXPECT_EQ(begin(), iterator(rend()));EXPECT_EQ(++begin(), iterator(++rbegin()));EXPECT_EQ(end(), iterator(rbegin()));EXPECT_EQ(rbegin(), reverse_iterator(end()));EXPECT_EQ(++rbegin(), reverse_iterator(++begin()));EXPECT_EQ(rend(), reverse_iterator(begin()));// Check const iterator constructors.EXPECT_EQ(cbegin(), const_iterator(rend()));EXPECT_EQ(cbegin(), const_iterator(crend()));EXPECT_EQ(crbegin(), const_reverse_iterator(end()));EXPECT_EQ(crbegin(), const_reverse_iterator(cend()));// Confirm lack of implicit conversions.static_assert(!std::is_convertible<iterator, reverse_iterator>::value,"unexpected implicit conversion");static_assert(!std::is_convertible<reverse_iterator, iterator>::value,"unexpected implicit conversion");static_assert(!std::is_convertible<const_iterator, const_reverse_iterator>::value,"unexpected implicit conversion");static_assert(!std::is_convertible<const_reverse_iterator, const_iterator>::value,"unexpected implicit conversion");}} // end namespace
// Add a few Bogus backend classes so we can create MachineInstrs without// depending on a real target.class BogusTargetLowering : public TargetLowering {public:BogusTargetLowering(TargetMachine &TM) : TargetLowering(TM) {}};class BogusFrameLowering : public TargetFrameLowering {public:BogusFrameLowering(): TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(4), 4) {}void emitPrologue(MachineFunction &MF,MachineBasicBlock &MBB) const override {}void emitEpilogue(MachineFunction &MF,MachineBasicBlock &MBB) const override {}bool hasFP(const MachineFunction &MF) const override { return false; }};static TargetRegisterClass *const BogusRegisterClasses[] = {nullptr};class BogusRegisterInfo : public TargetRegisterInfo {public:BogusRegisterInfo(): TargetRegisterInfo(nullptr, BogusRegisterClasses, BogusRegisterClasses,nullptr, nullptr, LaneBitmask(~0u), nullptr) {InitMCRegisterInfo(nullptr, 0, 0, 0, nullptr, 0, nullptr, 0, nullptr,nullptr, nullptr, nullptr, nullptr, 0, nullptr, nullptr);}const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override {return nullptr;}ArrayRef<const uint32_t *> getRegMasks() const override { return None; }ArrayRef<const char *> getRegMaskNames() const override { return None; }BitVector getReservedRegs(const MachineFunction &MF) const override {return BitVector();}const RegClassWeight &getRegClassWeight(const TargetRegisterClass *RC) const override {static RegClassWeight Bogus{1, 16};return Bogus;}unsigned getRegUnitWeight(unsigned RegUnit) const override { return 1; }unsigned getNumRegPressureSets() const override { return 0; }const char *getRegPressureSetName(unsigned Idx) const override {return "bogus";}unsigned getRegPressureSetLimit(const MachineFunction &MF,unsigned Idx) const override {return 0;}const int *getRegClassPressureSets(const TargetRegisterClass *RC) const override {static const int Bogus[] = {0, -1};return &Bogus[0];}const int *getRegUnitPressureSets(unsigned RegUnit) const override {static const int Bogus[] = {0, -1};return &Bogus[0];}Register getFrameRegister(const MachineFunction &MF) const override {return 0;}void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj,unsigned FIOperandNum,RegScavenger *RS = nullptr) const override {}};class BogusSubtarget : public TargetSubtargetInfo {public:BogusSubtarget(TargetMachine &TM): TargetSubtargetInfo(Triple(""), "", "", "", {}, {}, nullptr, nullptr,nullptr, nullptr, nullptr, nullptr),FL(), TL(TM) {}~BogusSubtarget() override {}const TargetFrameLowering *getFrameLowering() const override { return &FL; }const TargetLowering *getTargetLowering() const override { return &TL; }const TargetInstrInfo *getInstrInfo() const override { return &TII; }const TargetRegisterInfo *getRegisterInfo() const override { return &TRI; }private:BogusFrameLowering FL;BogusRegisterInfo TRI;BogusTargetLowering TL;TargetInstrInfo TII;};static TargetOptions getTargetOptionsForBogusMachine() {TargetOptions Opts;Opts.EmitCallSiteInfo = true;return Opts;}class BogusTargetMachine : public LLVMTargetMachine {public:BogusTargetMachine(): LLVMTargetMachine(Target(), "", Triple(""), "", "",getTargetOptionsForBogusMachine(), Reloc::Static,CodeModel::Small, CodeGenOpt::Default),ST(*this) {}~BogusTargetMachine() override {}const TargetSubtargetInfo *getSubtargetImpl(const Function &) const override {return &ST;}private:BogusSubtarget ST;};std::unique_ptr<BogusTargetMachine> createTargetMachine() {return std::make_unique<BogusTargetMachine>();}std::unique_ptr<MachineFunction> createMachineFunction(LLVMContext &Ctx,Module &M) {auto Type = FunctionType::get(Type::getVoidTy(Ctx), false);auto F = Function::Create(Type, GlobalValue::ExternalLinkage, "Test", &M);auto TM = createTargetMachine();unsigned FunctionNum = 42;MachineModuleInfo MMI(TM.get());const TargetSubtargetInfo &STI = *TM->getSubtargetImpl(*F);return std::make_unique<MachineFunction>(*F, *TM, STI, FunctionNum, MMI);}
//===- llvm/unittest/CodeGen/GlobalISel/LowLevelTypeTest.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/LowLevelType.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/DerivedTypes.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Type.h"#include "llvm/Support/TypeSize.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(LowLevelTypeTest, Scalar) {LLVMContext C;DataLayout DL("");for (unsigned S : {0U, 1U, 17U, 32U, 64U, 0xfffffU}) {const LLT Ty = LLT::scalar(S);// Test kind.ASSERT_TRUE(Ty.isValid());ASSERT_TRUE(Ty.isScalar());ASSERT_FALSE(Ty.isPointer());ASSERT_FALSE(Ty.isVector());// Test sizes.EXPECT_EQ(S, Ty.getSizeInBits());EXPECT_EQ(S, Ty.getScalarSizeInBits());// Test equality operators.EXPECT_TRUE(Ty == Ty);EXPECT_FALSE(Ty != Ty);// Test Type->LLT conversion.if (S != 0) {Type *IRTy = IntegerType::get(C, S);EXPECT_EQ(Ty, getLLTForType(*IRTy, DL));}}}TEST(LowLevelTypeTest, Vector) {LLVMContext C;DataLayout DL("");for (unsigned S : {0U, 1U, 17U, 32U, 64U, 0xfffU}) {for (auto EC :{ElementCount::getFixed(2), ElementCount::getFixed(3),ElementCount::getFixed(4), ElementCount::getFixed(32),ElementCount::getFixed(0xff), ElementCount::getScalable(2),ElementCount::getScalable(3), ElementCount::getScalable(4),ElementCount::getScalable(32), ElementCount::getScalable(0xff)}) {const LLT STy = LLT::scalar(S);const LLT VTy = LLT::vector(EC, S);// Test the alternative vector().{const LLT VSTy = LLT::vector(EC, STy);EXPECT_EQ(VTy, VSTy);}// Test getElementType().EXPECT_EQ(STy, VTy.getElementType());// Test kind.ASSERT_TRUE(VTy.isValid());ASSERT_TRUE(VTy.isVector());ASSERT_FALSE(VTy.isScalar());ASSERT_FALSE(VTy.isPointer());// Test sizes.EXPECT_EQ(S, VTy.getScalarSizeInBits());EXPECT_EQ(EC, VTy.getElementCount());if (!EC.isScalable())EXPECT_EQ(S * EC.getFixedValue(), VTy.getSizeInBits());elseEXPECT_EQ(TypeSize::Scalable(S * EC.getKnownMinValue()),VTy.getSizeInBits());// Test equality operators.EXPECT_TRUE(VTy == VTy);EXPECT_FALSE(VTy != VTy);// Test inequality operators on..// ..different kind.EXPECT_NE(VTy, STy);// Test Type->LLT conversion.if (S != 0) {Type *IRSTy = IntegerType::get(C, S);Type *IRTy = VectorType::get(IRSTy, EC);EXPECT_EQ(VTy, getLLTForType(*IRTy, DL));}}}}TEST(LowLevelTypeTest, ScalarOrVector) {// Test version with number of bits for scalar type.EXPECT_EQ(LLT::scalar(32),LLT::scalarOrVector(ElementCount::getFixed(1), 32));EXPECT_EQ(LLT::fixed_vector(2, 32),LLT::scalarOrVector(ElementCount::getFixed(2), 32));EXPECT_EQ(LLT::scalable_vector(1, 32),LLT::scalarOrVector(ElementCount::getScalable(1), 32));// Test version with LLT for scalar type.EXPECT_EQ(LLT::scalar(32),LLT::scalarOrVector(ElementCount::getFixed(1), LLT::scalar(32)));EXPECT_EQ(LLT::fixed_vector(2, 32),LLT::scalarOrVector(ElementCount::getFixed(2), LLT::scalar(32)));// Test with pointer elements.EXPECT_EQ(LLT::pointer(1, 32), LLT::scalarOrVector(ElementCount::getFixed(1),LLT::pointer(1, 32)));EXPECT_EQ(LLT::fixed_vector(2, LLT::pointer(1, 32)),LLT::scalarOrVector(ElementCount::getFixed(2), LLT::pointer(1, 32)));}TEST(LowLevelTypeTest, ChangeElementType) {const LLT P0 = LLT::pointer(0, 32);const LLT P1 = LLT::pointer(1, 64);const LLT S32 = LLT::scalar(32);const LLT S64 = LLT::scalar(64);const LLT V2S32 = LLT::fixed_vector(2, 32);const LLT V2S64 = LLT::fixed_vector(2, 64);const LLT V2P0 = LLT::fixed_vector(2, P0);const LLT V2P1 = LLT::fixed_vector(2, P1);EXPECT_EQ(S64, S32.changeElementType(S64));EXPECT_EQ(S32, S32.changeElementType(S32));EXPECT_EQ(S32, S64.changeElementSize(32));EXPECT_EQ(S32, S32.changeElementSize(32));EXPECT_EQ(V2S64, V2S32.changeElementType(S64));EXPECT_EQ(V2S32, V2S64.changeElementType(S32));EXPECT_EQ(V2S64, V2S32.changeElementSize(64));EXPECT_EQ(V2S32, V2S64.changeElementSize(32));EXPECT_EQ(P0, S32.changeElementType(P0));EXPECT_EQ(S32, P0.changeElementType(S32));EXPECT_EQ(V2P1, V2P0.changeElementType(P1));EXPECT_EQ(V2S32, V2P0.changeElementType(S32));// Similar tests for for scalable vectors.const LLT NXV2S32 = LLT::scalable_vector(2, 32);const LLT NXV2S64 = LLT::scalable_vector(2, 64);const LLT NXV2P0 = LLT::scalable_vector(2, P0);const LLT NXV2P1 = LLT::scalable_vector(2, P1);EXPECT_EQ(NXV2S64, NXV2S32.changeElementType(S64));EXPECT_EQ(NXV2S32, NXV2S64.changeElementType(S32));EXPECT_EQ(NXV2S64, NXV2S32.changeElementSize(64));EXPECT_EQ(NXV2S32, NXV2S64.changeElementSize(32));EXPECT_EQ(NXV2P1, NXV2P0.changeElementType(P1));EXPECT_EQ(NXV2S32, NXV2P0.changeElementType(S32));}TEST(LowLevelTypeTest, ChangeNumElements) {const LLT P0 = LLT::pointer(0, 32);const LLT V2P0 = LLT::fixed_vector(2, P0);const LLT V3P0 = LLT::fixed_vector(3, P0);const LLT S64 = LLT::scalar(64);const LLT V2S64 = LLT::fixed_vector(2, 64);const LLT V3S64 = LLT::fixed_vector(3, 64);// Vector to scalarEXPECT_EQ(S64, V2S64.changeElementCount(ElementCount::getFixed(1)));// Vector to vectorEXPECT_EQ(V3S64, V2S64.changeElementCount(ElementCount::getFixed(3)));// Scalar to vectorEXPECT_EQ(V2S64, S64.changeElementCount(ElementCount::getFixed(2)));EXPECT_EQ(P0, V2P0.changeElementCount(ElementCount::getFixed(1)));EXPECT_EQ(V3P0, V2P0.changeElementCount(ElementCount::getFixed(3)));EXPECT_EQ(V2P0, P0.changeElementCount(ElementCount::getFixed(2)));const LLT NXV2S64 = LLT::scalable_vector(2, 64);const LLT NXV3S64 = LLT::scalable_vector(3, 64);const LLT NXV2P0 = LLT::scalable_vector(2, P0);// Scalable vector to scalarEXPECT_EQ(S64, NXV2S64.changeElementCount(ElementCount::getFixed(1)));EXPECT_EQ(P0, NXV2P0.changeElementCount(ElementCount::getFixed(1)));// Fixed-width vector to scalable vectorEXPECT_EQ(NXV3S64, V2S64.changeElementCount(ElementCount::getScalable(3)));// Scalable vector to fixed-width vectorEXPECT_EQ(V3P0, NXV2P0.changeElementCount(ElementCount::getFixed(3)));// Scalar to scalable vectorEXPECT_EQ(NXV2S64, S64.changeElementCount(ElementCount::getScalable(2)));EXPECT_EQ(NXV2P0, P0.changeElementCount(ElementCount::getScalable(2)));}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUG// Invalid to directly change the element size for pointers.TEST(LowLevelTypeTest, ChangeElementTypeDeath) {const LLT P0 = LLT::pointer(0, 32);const LLT V2P0 = LLT::fixed_vector(2, P0);EXPECT_DEATH(P0.changeElementSize(64),"invalid to directly change element size for pointers");EXPECT_DEATH(V2P0.changeElementSize(64),"invalid to directly change element size for pointers");// Make sure this still fails even without a change in size.EXPECT_DEATH(P0.changeElementSize(32),"invalid to directly change element size for pointers");EXPECT_DEATH(V2P0.changeElementSize(32),"invalid to directly change element size for pointers");}#endif#endifTEST(LowLevelTypeTest, Pointer) {LLVMContext C;DataLayout DL("p64:64:64-p127:512:512:512-p16777215:65528:8");for (unsigned AS : {0U, 1U, 127U, 0xffffU,static_cast<unsigned>(maxUIntN(23)),static_cast<unsigned>(maxUIntN(24))}) {for (ElementCount EC :{ElementCount::getFixed(2), ElementCount::getFixed(3),ElementCount::getFixed(4), ElementCount::getFixed(256),ElementCount::getFixed(65535), ElementCount::getScalable(2),ElementCount::getScalable(3), ElementCount::getScalable(4),ElementCount::getScalable(256), ElementCount::getScalable(65535)}) {const LLT Ty = LLT::pointer(AS, DL.getPointerSizeInBits(AS));const LLT VTy = LLT::vector(EC, Ty);// Test kind.ASSERT_TRUE(Ty.isValid());ASSERT_TRUE(Ty.isPointer());ASSERT_FALSE(Ty.isScalar());ASSERT_FALSE(Ty.isVector());ASSERT_TRUE(VTy.isValid());ASSERT_TRUE(VTy.isVector());ASSERT_TRUE(VTy.getElementType().isPointer());EXPECT_EQ(Ty, VTy.getElementType());EXPECT_EQ(Ty.getSizeInBits(), VTy.getScalarSizeInBits());// Test address space.EXPECT_EQ(AS, Ty.getAddressSpace());EXPECT_EQ(AS, VTy.getElementType().getAddressSpace());// Test equality operators.EXPECT_TRUE(Ty == Ty);EXPECT_FALSE(Ty != Ty);EXPECT_TRUE(VTy == VTy);EXPECT_FALSE(VTy != VTy);// Test Type->LLT conversion.Type *IRTy = PointerType::get(IntegerType::get(C, 8), AS);EXPECT_EQ(Ty, getLLTForType(*IRTy, DL));Type *IRVTy =VectorType::get(PointerType::get(IntegerType::get(C, 8), AS), EC);EXPECT_EQ(VTy, getLLTForType(*IRVTy, DL));}}}TEST(LowLevelTypeTest, Invalid) {const LLT Ty;ASSERT_FALSE(Ty.isValid());ASSERT_FALSE(Ty.isScalar());ASSERT_FALSE(Ty.isPointer());ASSERT_FALSE(Ty.isVector());}TEST(LowLevelTypeTest, Divide) {// Test basic scalar->scalar cases.EXPECT_EQ(LLT::scalar(16), LLT::scalar(32).divide(2));EXPECT_EQ(LLT::scalar(8), LLT::scalar(32).divide(4));EXPECT_EQ(LLT::scalar(8), LLT::scalar(32).divide(4));// Test pointer->scalarEXPECT_EQ(LLT::scalar(32), LLT::pointer(0, 64).divide(2));// Test dividing vectors.EXPECT_EQ(LLT::scalar(32), LLT::fixed_vector(2, 32).divide(2));EXPECT_EQ(LLT::fixed_vector(2, 32), LLT::fixed_vector(4, 32).divide(2));// Test vector of pointersEXPECT_EQ(LLT::pointer(1, 64),LLT::fixed_vector(4, LLT::pointer(1, 64)).divide(4));EXPECT_EQ(LLT::fixed_vector(2, LLT::pointer(1, 64)),LLT::fixed_vector(4, LLT::pointer(1, 64)).divide(2));}TEST(LowLevelTypeTest, MultiplyElements) {// Basic scalar->vector casesEXPECT_EQ(LLT::fixed_vector(2, 16), LLT::scalar(16).multiplyElements(2));EXPECT_EQ(LLT::fixed_vector(3, 16), LLT::scalar(16).multiplyElements(3));EXPECT_EQ(LLT::fixed_vector(4, 32), LLT::scalar(32).multiplyElements(4));EXPECT_EQ(LLT::fixed_vector(4, 7), LLT::scalar(7).multiplyElements(4));// Basic vector to vector casesEXPECT_EQ(LLT::fixed_vector(4, 32),LLT::fixed_vector(2, 32).multiplyElements(2));EXPECT_EQ(LLT::fixed_vector(9, 32),LLT::fixed_vector(3, 32).multiplyElements(3));// Pointer to vector of pointersEXPECT_EQ(LLT::fixed_vector(2, LLT::pointer(0, 32)),LLT::pointer(0, 32).multiplyElements(2));EXPECT_EQ(LLT::fixed_vector(3, LLT::pointer(1, 32)),LLT::pointer(1, 32).multiplyElements(3));EXPECT_EQ(LLT::fixed_vector(4, LLT::pointer(1, 64)),LLT::pointer(1, 64).multiplyElements(4));// Vector of pointers to vector of pointersEXPECT_EQ(LLT::fixed_vector(8, LLT::pointer(1, 64)),LLT::fixed_vector(2, LLT::pointer(1, 64)).multiplyElements(4));EXPECT_EQ(LLT::fixed_vector(9, LLT::pointer(1, 32)),LLT::fixed_vector(3, LLT::pointer(1, 32)).multiplyElements(3));// Scalable vectorsEXPECT_EQ(LLT::scalable_vector(4, 16),LLT::scalable_vector(2, 16).multiplyElements(2));EXPECT_EQ(LLT::scalable_vector(6, 16),LLT::scalable_vector(2, 16).multiplyElements(3));EXPECT_EQ(LLT::scalable_vector(9, 16),LLT::scalable_vector(3, 16).multiplyElements(3));EXPECT_EQ(LLT::scalable_vector(4, 32),LLT::scalable_vector(2, 32).multiplyElements(2));EXPECT_EQ(LLT::scalable_vector(256, 32),LLT::scalable_vector(8, 32).multiplyElements(32));// Scalable vectors of pointersEXPECT_EQ(LLT::scalable_vector(4, LLT::pointer(0, 32)),LLT::scalable_vector(2, LLT::pointer(0, 32)).multiplyElements(2));EXPECT_EQ(LLT::scalable_vector(32, LLT::pointer(1, 64)),LLT::scalable_vector(8, LLT::pointer(1, 64)).multiplyElements(4));}}
//===----------- llvm/unittest/CodeGen/LexicalScopesTest.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/LexicalScopes.h"#include "llvm/CodeGen/MachineBasicBlock.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineInstr.h"#include "llvm/CodeGen/MachineMemOperand.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetFrameLowering.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/ModuleSlotTracker.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCSymbol.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Include helper functions to ease the manipulation of MachineFunctions#include "MFCommon.inc"class LexicalScopesTest : public testing::Test {public:// Boilerplate,LLVMContext Ctx;Module Mod;std::unique_ptr<MachineFunction> MF;DICompileUnit *OurCU;DIFile *OurFile;DISubprogram *OurFunc;DILexicalBlock *OurBlock, *AnotherBlock;DISubprogram *ToInlineFunc;DILexicalBlock *ToInlineBlock;// DebugLocs that we'll used to create test environments.DebugLoc OutermostLoc, InBlockLoc, NotNestedBlockLoc, InlinedLoc;// Test environment blocks -- these form a diamond control flow pattern,// MBB1 being the entry block, blocks two and three being the branches, and// block four joining the branches and being an exit block.MachineBasicBlock *MBB1, *MBB2, *MBB3, *MBB4;// Some meaningless instructions -- the first is fully meaningless,// while the second is supposed to impersonate DBG_VALUEs through its// opcode.MCInstrDesc BeanInst;MCInstrDesc DbgValueInst;LexicalScopesTest() : Ctx(), Mod("beehives", Ctx) {memset(&BeanInst, 0, sizeof(BeanInst));BeanInst.Opcode = 1;BeanInst.Size = 1;memset(&DbgValueInst, 0, sizeof(DbgValueInst));DbgValueInst.Opcode = TargetOpcode::DBG_VALUE;DbgValueInst.Size = 1;DbgValueInst.Flags = 1U << MCID::Meta;// Boilerplate that creates a MachineFunction and associated blocks.MF = createMachineFunction(Ctx, Mod);llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());auto BB1 = BasicBlock::Create(Ctx, "a", &F);auto BB2 = BasicBlock::Create(Ctx, "b", &F);auto BB3 = BasicBlock::Create(Ctx, "c", &F);auto BB4 = BasicBlock::Create(Ctx, "d", &F);IRBuilder<> IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);IRB1.CreateBr(BB2);IRB2.CreateBr(BB3);IRB3.CreateBr(BB4);IRB4.CreateRetVoid();MBB1 = MF->CreateMachineBasicBlock(BB1);MF->insert(MF->end(), MBB1);MBB2 = MF->CreateMachineBasicBlock(BB2);MF->insert(MF->end(), MBB2);MBB3 = MF->CreateMachineBasicBlock(BB3);MF->insert(MF->end(), MBB3);MBB4 = MF->CreateMachineBasicBlock(BB4);MF->insert(MF->end(), MBB4);MBB1->addSuccessor(MBB2);MBB1->addSuccessor(MBB3);MBB2->addSuccessor(MBB4);MBB3->addSuccessor(MBB4);// Create metadata: CU, subprogram, some blocks and an inline function// scope.DIBuilder DIB(Mod);OurFile = DIB.createFile("xyzzy.c", "/cave");OurCU =DIB.createCompileUnit(dwarf::DW_LANG_C99, OurFile, "nou", false, "", 0);auto OurSubT = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));OurFunc =DIB.createFunction(OurCU, "bees", "", OurFile, 1, OurSubT, 1,DINode::FlagZero, DISubprogram::SPFlagDefinition);F.setSubprogram(OurFunc);OurBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 3);AnotherBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 6);ToInlineFunc =DIB.createFunction(OurFile, "shoes", "", OurFile, 10, OurSubT, 10,DINode::FlagZero, DISubprogram::SPFlagDefinition);// Make some nested scopes.OutermostLoc = DILocation::get(Ctx, 3, 1, OurFunc);InBlockLoc = DILocation::get(Ctx, 4, 1, OurBlock);InlinedLoc = DILocation::get(Ctx, 10, 1, ToInlineFunc, InBlockLoc.get());// Make a scope that isn't nested within the others.NotNestedBlockLoc = DILocation::get(Ctx, 4, 1, AnotherBlock);DIB.finalize();}};// Fill blocks with dummy instructions, test some base lexical scope// functionaliy.TEST_F(LexicalScopesTest, FlatLayout) {BuildMI(*MBB1, MBB1->end(), OutermostLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), OutermostLoc, BeanInst);BuildMI(*MBB3, MBB3->end(), OutermostLoc, BeanInst);BuildMI(*MBB4, MBB4->end(), OutermostLoc, BeanInst);LexicalScopes LS;EXPECT_TRUE(LS.empty());LS.reset();EXPECT_EQ(LS.getCurrentFunctionScope(), nullptr);LS.initialize(*MF);EXPECT_FALSE(LS.empty());LexicalScope *FuncScope = LS.getCurrentFunctionScope();EXPECT_EQ(FuncScope->getParent(), nullptr);EXPECT_EQ(FuncScope->getDesc(), OurFunc);EXPECT_EQ(FuncScope->getInlinedAt(), nullptr);EXPECT_EQ(FuncScope->getScopeNode(), OurFunc);EXPECT_FALSE(FuncScope->isAbstractScope());EXPECT_EQ(FuncScope->getChildren().size(), 0u);// There should be one range, covering the whole function. Test that it// points at the correct instructions.auto &Ranges = FuncScope->getRanges();ASSERT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges.front().first, &*MF->begin()->begin());auto BBIt = MF->end();BBIt = std::prev(BBIt);EXPECT_EQ(Ranges.front().second, &*BBIt->begin());EXPECT_TRUE(FuncScope->dominates(FuncScope));SmallPtrSet<const MachineBasicBlock *, 4> MBBVec;LS.getMachineBasicBlocks(OutermostLoc.get(), MBBVec);EXPECT_EQ(MBBVec.size(), 4u);// All the blocks should be in that set; the outermost loc should dominate// them; and no other scope should.for (auto &MBB : *MF) {EXPECT_EQ(MBBVec.count(&MBB), 1u);EXPECT_TRUE(LS.dominates(OutermostLoc.get(), &MBB));EXPECT_FALSE(LS.dominates(InBlockLoc.get(), &MBB));EXPECT_FALSE(LS.dominates(InlinedLoc.get(), &MBB));}}// Examine relationship between two nested scopes inside the function, the// outer function and the lexical block within it.TEST_F(LexicalScopesTest, BlockScopes) {BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), InBlockLoc, BeanInst);BuildMI(*MBB3, MBB3->end(), InBlockLoc, BeanInst);BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *FuncScope = LS.getCurrentFunctionScope();EXPECT_EQ(FuncScope->getDesc(), OurFunc);auto &Children = FuncScope->getChildren();ASSERT_EQ(Children.size(), 1u);auto *BlockScope = Children[0];EXPECT_EQ(LS.findLexicalScope(InBlockLoc.get()), BlockScope);EXPECT_EQ(BlockScope->getDesc(), InBlockLoc->getScope());EXPECT_FALSE(BlockScope->isAbstractScope());EXPECT_TRUE(FuncScope->dominates(BlockScope));EXPECT_FALSE(BlockScope->dominates(FuncScope));EXPECT_EQ(FuncScope->getParent(), nullptr);EXPECT_EQ(BlockScope->getParent(), FuncScope);SmallPtrSet<const MachineBasicBlock *, 4> MBBVec;LS.getMachineBasicBlocks(OutermostLoc.get(), MBBVec);EXPECT_EQ(MBBVec.size(), 4u);for (auto &MBB : *MF) {EXPECT_EQ(MBBVec.count(&MBB), 1u);EXPECT_TRUE(LS.dominates(OutermostLoc.get(), &MBB));EXPECT_TRUE(LS.dominates(InBlockLoc.get(), &MBB));EXPECT_FALSE(LS.dominates(InlinedLoc.get(), &MBB));}}// Test inlined scopes functionality and relationship with the outer scopes.TEST_F(LexicalScopesTest, InlinedScopes) {BuildMI(*MBB1, MBB1->end(), InlinedLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), InlinedLoc, BeanInst);BuildMI(*MBB3, MBB3->end(), InlinedLoc, BeanInst);BuildMI(*MBB4, MBB4->end(), InlinedLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *FuncScope = LS.getCurrentFunctionScope();auto &Children = FuncScope->getChildren();ASSERT_EQ(Children.size(), 1u);auto *BlockScope = Children[0];auto &BlockChildren = BlockScope->getChildren();ASSERT_EQ(BlockChildren.size(), 1u);auto *InlinedScope = BlockChildren[0];EXPECT_FALSE(InlinedScope->isAbstractScope());EXPECT_EQ(InlinedScope->getInlinedAt(), InlinedLoc.getInlinedAt());EXPECT_EQ(InlinedScope->getDesc(), InlinedLoc.getScope());EXPECT_EQ(InlinedScope->getChildren().size(), 0u);EXPECT_EQ(FuncScope->getParent(), nullptr);EXPECT_EQ(BlockScope->getParent(), FuncScope);EXPECT_EQ(InlinedScope->getParent(), BlockScope);const auto &AbstractScopes = LS.getAbstractScopesList();ASSERT_EQ(AbstractScopes.size(), 1u);const auto &AbstractScope = *AbstractScopes[0];EXPECT_TRUE(AbstractScope.isAbstractScope());EXPECT_EQ(AbstractScope.getDesc(), InlinedLoc.getScope());EXPECT_EQ(AbstractScope.getInlinedAt(), nullptr);EXPECT_EQ(AbstractScope.getParent(), nullptr);}// Test behaviour in a function that has empty DebugLocs.TEST_F(LexicalScopesTest, FuncWithEmptyGap) {BuildMI(*MBB1, MBB1->end(), OutermostLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), DebugLoc(), BeanInst);BuildMI(*MBB3, MBB3->end(), DebugLoc(), BeanInst);BuildMI(*MBB4, MBB4->end(), OutermostLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *FuncScope = LS.getCurrentFunctionScope();// A gap in a range that contains no other location, is not actually a// gap as far as lexical scopes are concerned.auto &Ranges = FuncScope->getRanges();ASSERT_EQ(Ranges.size(), 1u);EXPECT_EQ(Ranges[0].first, &*MF->begin()->begin());auto BBIt = MF->end();BBIt = std::prev(BBIt);EXPECT_EQ(Ranges[0].second, &*BBIt->begin());}// Now a function with intervening not-in-scope instructions.TEST_F(LexicalScopesTest, FuncWithRealGap) {MachineInstr *FirstI = BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), OutermostLoc, BeanInst);BuildMI(*MBB3, MBB3->end(), OutermostLoc, BeanInst);MachineInstr *LastI = BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get());ASSERT_NE(BlockScope, nullptr);// Within the block scope, there's a gap between the first and last// block / instruction, where it's only the outermost scope.auto &Ranges = BlockScope->getRanges();ASSERT_EQ(Ranges.size(), 2u);EXPECT_EQ(Ranges[0].first, FirstI);EXPECT_EQ(Ranges[0].second, FirstI);EXPECT_EQ(Ranges[1].first, LastI);EXPECT_EQ(Ranges[1].second, LastI);// The outer function scope should cover the whole function, including// blocks the lexicalblock covers.LexicalScope *FuncScope = LS.getCurrentFunctionScope();auto &FuncRanges = FuncScope->getRanges();ASSERT_EQ(FuncRanges.size(), 1u);EXPECT_NE(FuncRanges[0].first, FuncRanges[0].second);EXPECT_EQ(FuncRanges[0].first, FirstI);EXPECT_EQ(FuncRanges[0].second, LastI);}// Examine the relationship between two scopes that don't nest (are siblings).TEST_F(LexicalScopesTest, NotNested) {MachineInstr *FirstI = BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst);MachineInstr *SecondI =BuildMI(*MBB2, MBB2->end(), NotNestedBlockLoc, BeanInst);MachineInstr *ThirdI =BuildMI(*MBB3, MBB3->end(), NotNestedBlockLoc, BeanInst);MachineInstr *FourthI = BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *FuncScope = LS.getCurrentFunctionScope();LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get());LexicalScope *OtherBlockScope = LS.findLexicalScope(NotNestedBlockLoc.get());ASSERT_NE(FuncScope, nullptr);ASSERT_NE(BlockScope, nullptr);ASSERT_NE(OtherBlockScope, nullptr);// The function should cover everything; the two blocks are distinct and// should not.auto &FuncRanges = FuncScope->getRanges();ASSERT_EQ(FuncRanges.size(), 1u);EXPECT_EQ(FuncRanges[0].first, FirstI);EXPECT_EQ(FuncRanges[0].second, FourthI);// Two ranges, start and end instructions.auto &BlockRanges = BlockScope->getRanges();ASSERT_EQ(BlockRanges.size(), 2u);EXPECT_EQ(BlockRanges[0].first, FirstI);EXPECT_EQ(BlockRanges[0].second, FirstI);EXPECT_EQ(BlockRanges[1].first, FourthI);EXPECT_EQ(BlockRanges[1].second, FourthI);// One inner range, covering the two inner blocks.auto &OtherBlockRanges = OtherBlockScope->getRanges();ASSERT_EQ(OtherBlockRanges.size(), 1u);EXPECT_EQ(OtherBlockRanges[0].first, SecondI);EXPECT_EQ(OtherBlockRanges[0].second, ThirdI);}// Test the scope-specific and block-specific dominates methods.TEST_F(LexicalScopesTest, TestDominates) {BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), NotNestedBlockLoc, BeanInst);BuildMI(*MBB3, MBB3->end(), NotNestedBlockLoc, BeanInst);BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *FuncScope = LS.getCurrentFunctionScope();LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get());LexicalScope *OtherBlockScope = LS.findLexicalScope(NotNestedBlockLoc.get());ASSERT_NE(FuncScope, nullptr);ASSERT_NE(BlockScope, nullptr);ASSERT_NE(OtherBlockScope, nullptr);EXPECT_TRUE(FuncScope->dominates(BlockScope));EXPECT_TRUE(FuncScope->dominates(OtherBlockScope));EXPECT_FALSE(BlockScope->dominates(FuncScope));EXPECT_FALSE(BlockScope->dominates(OtherBlockScope));EXPECT_FALSE(OtherBlockScope->dominates(FuncScope));EXPECT_FALSE(OtherBlockScope->dominates(BlockScope));// Outermost scope dominates everything, as all insts are within it.EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB1));EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB2));EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB3));EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB4));// One inner block dominates the outer pair of blocks,EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB1));EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB2));EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB3));EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB4));// While the other dominates the inner two blocks.EXPECT_FALSE(LS.dominates(NotNestedBlockLoc.get(), MBB1));EXPECT_TRUE(LS.dominates(NotNestedBlockLoc.get(), MBB2));EXPECT_TRUE(LS.dominates(NotNestedBlockLoc.get(), MBB3));EXPECT_FALSE(LS.dominates(NotNestedBlockLoc.get(), MBB4));}// Test getMachineBasicBlocks returns all dominated blocks.TEST_F(LexicalScopesTest, TestGetBlocks) {BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), NotNestedBlockLoc, BeanInst);BuildMI(*MBB3, MBB3->end(), NotNestedBlockLoc, BeanInst);BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *FuncScope = LS.getCurrentFunctionScope();LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get());LexicalScope *OtherBlockScope = LS.findLexicalScope(NotNestedBlockLoc.get());ASSERT_NE(FuncScope, nullptr);ASSERT_NE(BlockScope, nullptr);ASSERT_NE(OtherBlockScope, nullptr);SmallPtrSet<const MachineBasicBlock *, 4> OutermostBlocks, InBlockBlocks,NotNestedBlockBlocks;LS.getMachineBasicBlocks(OutermostLoc.get(), OutermostBlocks);LS.getMachineBasicBlocks(InBlockLoc.get(), InBlockBlocks);LS.getMachineBasicBlocks(NotNestedBlockLoc.get(), NotNestedBlockBlocks);EXPECT_EQ(OutermostBlocks.count(MBB1), 1u);EXPECT_EQ(OutermostBlocks.count(MBB2), 1u);EXPECT_EQ(OutermostBlocks.count(MBB3), 1u);EXPECT_EQ(OutermostBlocks.count(MBB4), 1u);EXPECT_EQ(InBlockBlocks.count(MBB1), 1u);EXPECT_EQ(InBlockBlocks.count(MBB2), 0u);EXPECT_EQ(InBlockBlocks.count(MBB3), 0u);EXPECT_EQ(InBlockBlocks.count(MBB4), 1u);EXPECT_EQ(NotNestedBlockBlocks.count(MBB1), 0u);EXPECT_EQ(NotNestedBlockBlocks.count(MBB2), 1u);EXPECT_EQ(NotNestedBlockBlocks.count(MBB3), 1u);EXPECT_EQ(NotNestedBlockBlocks.count(MBB4), 0u);}TEST_F(LexicalScopesTest, TestMetaInst) {// Instruction Layout looks like this, where 'F' means funcscope, and// 'B' blockscope:// bb1:// F: bean// B: bean// bb2:// F: bean// B: DBG_VALUE// bb3:// F: bean// B: DBG_VALUE// bb4:// F: bean// B: bean// The block / 'B' should only dominate bb1 and bb4. DBG_VALUE is a meta// instruction, and shouldn't contribute to scopes.BuildMI(*MBB1, MBB1->end(), OutermostLoc, BeanInst);BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), OutermostLoc, BeanInst);BuildMI(*MBB2, MBB2->end(), InBlockLoc, DbgValueInst);BuildMI(*MBB3, MBB3->end(), OutermostLoc, BeanInst);BuildMI(*MBB3, MBB3->end(), InBlockLoc, DbgValueInst);BuildMI(*MBB4, MBB4->end(), OutermostLoc, BeanInst);BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst);LexicalScopes LS;LS.initialize(*MF);LexicalScope *FuncScope = LS.getCurrentFunctionScope();LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get());ASSERT_NE(FuncScope, nullptr);ASSERT_NE(BlockScope, nullptr);EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB1));EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB2));EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB3));EXPECT_TRUE(LS.dominates(OutermostLoc.get(), MBB4));EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB1));EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB2));EXPECT_FALSE(LS.dominates(InBlockLoc.get(), MBB3));EXPECT_TRUE(LS.dominates(InBlockLoc.get(), MBB4));}} // anonymous namespace
//===------------- llvm/unittest/CodeGen/InstrRefLDVTest.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineDominators.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetFrameLowering.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/CodeGen/TargetRegisterInfo.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/IR/DIBuilder.h"#include "llvm/IR/DebugInfoMetadata.h"#include "llvm/IR/IRBuilder.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "../lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h"#include "gtest/gtest.h"using namespace llvm;using namespace LiveDebugValues;// Include helper functions to ease the manipulation of MachineFunctions#include "MFCommon.inc"class InstrRefLDVTest : public testing::Test {public:friend class InstrRefBasedLDV;using MLocTransferMap = InstrRefBasedLDV::MLocTransferMap;LLVMContext Ctx;std::unique_ptr<Module> Mod;std::unique_ptr<TargetMachine> Machine;std::unique_ptr<MachineFunction> MF;std::unique_ptr<MachineDominatorTree> DomTree;std::unique_ptr<MachineModuleInfo> MMI;DICompileUnit *OurCU;DIFile *OurFile;DISubprogram *OurFunc;DILexicalBlock *OurBlock, *AnotherBlock;DISubprogram *ToInlineFunc;DILexicalBlock *ToInlineBlock;DILocalVariable *FuncVariable;DIBasicType *LongInt;DIExpression *EmptyExpr;LiveDebugValues::OverlapMap Overlaps;DebugLoc OutermostLoc, InBlockLoc, NotNestedBlockLoc, InlinedLoc;MachineBasicBlock *MBB0, *MBB1, *MBB2, *MBB3, *MBB4;std::unique_ptr<InstrRefBasedLDV> LDV;std::unique_ptr<MLocTracker> MTracker;std::unique_ptr<VLocTracker> VTracker;SmallString<256> MIRStr;InstrRefLDVTest() : Ctx(), Mod(std::make_unique<Module>("beehives", Ctx)) {}void SetUp() {// Boilerplate that creates a MachineFunction and associated blocks.Mod->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-""n8:16:32:64-S128");Triple TargetTriple("x86_64--");std::string Error;const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);if (!T)GTEST_SKIP();TargetOptions Options;Machine = std::unique_ptr<TargetMachine>(T->createTargetMachine(Triple::normalize("x86_64--"), "", "", Options,None, None, CodeGenOpt::Aggressive));auto Type = FunctionType::get(Type::getVoidTy(Ctx), false);auto F =Function::Create(Type, GlobalValue::ExternalLinkage, "Test", &*Mod);unsigned FunctionNum = 42;MMI = std::make_unique<MachineModuleInfo>((LLVMTargetMachine *)&*Machine);const TargetSubtargetInfo &STI = *Machine->getSubtargetImpl(*F);MF = std::make_unique<MachineFunction>(*F, (LLVMTargetMachine &)*Machine,STI, FunctionNum, *MMI);// Create metadata: CU, subprogram, some blocks and an inline function// scope.DIBuilder DIB(*Mod);OurFile = DIB.createFile("xyzzy.c", "/cave");OurCU =DIB.createCompileUnit(dwarf::DW_LANG_C99, OurFile, "nou", false, "", 0);auto OurSubT = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));OurFunc =DIB.createFunction(OurCU, "bees", "", OurFile, 1, OurSubT, 1,DINode::FlagZero, DISubprogram::SPFlagDefinition);F->setSubprogram(OurFunc);OurBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 3);AnotherBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 6);ToInlineFunc =DIB.createFunction(OurFile, "shoes", "", OurFile, 10, OurSubT, 10,DINode::FlagZero, DISubprogram::SPFlagDefinition);// Make some nested scopes.OutermostLoc = DILocation::get(Ctx, 3, 1, OurFunc);InBlockLoc = DILocation::get(Ctx, 4, 1, OurBlock);InlinedLoc = DILocation::get(Ctx, 10, 1, ToInlineFunc, InBlockLoc.get());// Make a scope that isn't nested within the others.NotNestedBlockLoc = DILocation::get(Ctx, 4, 1, AnotherBlock);LongInt = DIB.createBasicType("long", 64, llvm::dwarf::DW_ATE_unsigned);FuncVariable = DIB.createAutoVariable(OurFunc, "lala", OurFile, 1, LongInt);EmptyExpr = DIExpression::get(Ctx, {});DIB.finalize();}Register getRegByName(const char *WantedName) {auto *TRI = MF->getRegInfo().getTargetRegisterInfo();// Slow, but works.for (unsigned int I = 1; I < TRI->getNumRegs(); ++I) {const char *Name = TRI->getName(I);if (strcmp(WantedName, Name) == 0)return I;}// If this ever fails, something is very wrong with this unit test.llvm_unreachable("Can't find register by name");}InstrRefBasedLDV *setupLDVObj(MachineFunction *MF) {// Create a new LDV object, and plug some relevant object ptrs into it.LDV = std::make_unique<InstrRefBasedLDV>();const TargetSubtargetInfo &STI = MF->getSubtarget();LDV->TII = STI.getInstrInfo();LDV->TRI = STI.getRegisterInfo();LDV->TFI = STI.getFrameLowering();LDV->MFI = &MF->getFrameInfo();LDV->MRI = &MF->getRegInfo();DomTree = std::make_unique<MachineDominatorTree>(*MF);LDV->DomTree = &*DomTree;// Future work: unit tests for mtracker / vtracker / ttracker.// Setup things like the artifical block map, and BlockNo <=> RPO Order// mappings.LDV->initialSetup(*MF);LDV->LS.initialize(*MF);addMTracker(MF);return &*LDV;}void addMTracker(MachineFunction *MF) {ASSERT_TRUE(LDV);// Add a machine-location-tracking object to LDV. Don't initialize any// register locations within it though.const TargetSubtargetInfo &STI = MF->getSubtarget();MTracker = std::make_unique<MLocTracker>(*MF, *LDV->TII, *LDV->TRI, *STI.getTargetLowering());LDV->MTracker = &*MTracker;}void addVTracker() {ASSERT_TRUE(LDV);VTracker = std::make_unique<VLocTracker>(Overlaps, EmptyExpr);LDV->VTracker = &*VTracker;}// Some routines for bouncing into LDV,void buildMLocValueMap(FuncValueTable &MInLocs, FuncValueTable &MOutLocs,SmallVectorImpl<MLocTransferMap> &MLocTransfer) {LDV->buildMLocValueMap(*MF, MInLocs, MOutLocs, MLocTransfer);}void placeMLocPHIs(MachineFunction &MF,SmallPtrSetImpl<MachineBasicBlock *> &AllBlocks,FuncValueTable &MInLocs,SmallVectorImpl<MLocTransferMap> &MLocTransfer) {LDV->placeMLocPHIs(MF, AllBlocks, MInLocs, MLocTransfer);}Optional<ValueIDNum>pickVPHILoc(const MachineBasicBlock &MBB, const DebugVariable &Var,const InstrRefBasedLDV::LiveIdxT &LiveOuts, FuncValueTable &MOutLocs,const SmallVectorImpl<const MachineBasicBlock *> &BlockOrders) {return LDV->pickVPHILoc(MBB, Var, LiveOuts, MOutLocs, BlockOrders);}bool vlocJoin(MachineBasicBlock &MBB, InstrRefBasedLDV::LiveIdxT &VLOCOutLocs,SmallPtrSet<const MachineBasicBlock *, 8> &BlocksToExplore,DbgValue &InLoc) {return LDV->vlocJoin(MBB, VLOCOutLocs, BlocksToExplore, InLoc);}void buildVLocValueMap(const DILocation *DILoc,const SmallSet<DebugVariable, 4> &VarsWeCareAbout,SmallPtrSetImpl<MachineBasicBlock *> &AssignBlocks,InstrRefBasedLDV::LiveInsT &Output, FuncValueTable &MOutLocs,FuncValueTable &MInLocs,SmallVectorImpl<VLocTracker> &AllTheVLocs) {LDV->buildVLocValueMap(DILoc, VarsWeCareAbout, AssignBlocks, Output,MOutLocs, MInLocs, AllTheVLocs);}void initValueArray(FuncValueTable &Nums, unsigned Blks, unsigned Locs) {for (unsigned int I = 0; I < Blks; ++I)for (unsigned int J = 0; J < Locs; ++J)Nums[I][J] = ValueIDNum::EmptyValue;}void setupSingleBlock() {// Add an entry block with nothing but 'ret void' in it.Function &F = const_cast<llvm::Function &>(MF->getFunction());auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);IRBuilder<> IRB(BB0);IRB.CreateRetVoid();MBB0 = MF->CreateMachineBasicBlock(BB0);MF->insert(MF->end(), MBB0);MF->RenumberBlocks();setupLDVObj(&*MF);}void setupDiamondBlocks() {// entry// / \// br1 br2// \ /// retllvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());auto *BB0 = BasicBlock::Create(Ctx, "a", &F);auto *BB1 = BasicBlock::Create(Ctx, "b", &F);auto *BB2 = BasicBlock::Create(Ctx, "c", &F);auto *BB3 = BasicBlock::Create(Ctx, "d", &F);IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3);IRB0.CreateBr(BB1);IRB1.CreateBr(BB2);IRB2.CreateBr(BB3);IRB3.CreateRetVoid();MBB0 = MF->CreateMachineBasicBlock(BB0);MF->insert(MF->end(), MBB0);MBB1 = MF->CreateMachineBasicBlock(BB1);MF->insert(MF->end(), MBB1);MBB2 = MF->CreateMachineBasicBlock(BB2);MF->insert(MF->end(), MBB2);MBB3 = MF->CreateMachineBasicBlock(BB3);MF->insert(MF->end(), MBB3);MBB0->addSuccessor(MBB1);MBB0->addSuccessor(MBB2);MBB1->addSuccessor(MBB3);MBB2->addSuccessor(MBB3);MF->RenumberBlocks();setupLDVObj(&*MF);}void setupSimpleLoop() {// entry// |// |/-----\// loopblk |// |\-----/// |// retllvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);auto *BB1 = BasicBlock::Create(Ctx, "loop", &F);auto *BB2 = BasicBlock::Create(Ctx, "ret", &F);IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2);IRB0.CreateBr(BB1);IRB1.CreateBr(BB2);IRB2.CreateRetVoid();MBB0 = MF->CreateMachineBasicBlock(BB0);MF->insert(MF->end(), MBB0);MBB1 = MF->CreateMachineBasicBlock(BB1);MF->insert(MF->end(), MBB1);MBB2 = MF->CreateMachineBasicBlock(BB2);MF->insert(MF->end(), MBB2);MBB0->addSuccessor(MBB1);MBB1->addSuccessor(MBB2);MBB1->addSuccessor(MBB1);MF->RenumberBlocks();setupLDVObj(&*MF);}void setupNestedLoops() {// entry// |// loop1// ^\// | \ /-\// | loop2 |// | / \-/// ^ /// join// |// retllvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);auto *BB1 = BasicBlock::Create(Ctx, "loop1", &F);auto *BB2 = BasicBlock::Create(Ctx, "loop2", &F);auto *BB3 = BasicBlock::Create(Ctx, "join", &F);auto *BB4 = BasicBlock::Create(Ctx, "ret", &F);IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);IRB0.CreateBr(BB1);IRB1.CreateBr(BB2);IRB2.CreateBr(BB3);IRB3.CreateBr(BB4);IRB4.CreateRetVoid();MBB0 = MF->CreateMachineBasicBlock(BB0);MF->insert(MF->end(), MBB0);MBB1 = MF->CreateMachineBasicBlock(BB1);MF->insert(MF->end(), MBB1);MBB2 = MF->CreateMachineBasicBlock(BB2);MF->insert(MF->end(), MBB2);MBB3 = MF->CreateMachineBasicBlock(BB3);MF->insert(MF->end(), MBB3);MBB4 = MF->CreateMachineBasicBlock(BB4);MF->insert(MF->end(), MBB4);MBB0->addSuccessor(MBB1);MBB1->addSuccessor(MBB2);MBB2->addSuccessor(MBB2);MBB2->addSuccessor(MBB3);MBB3->addSuccessor(MBB1);MBB3->addSuccessor(MBB4);MF->RenumberBlocks();setupLDVObj(&*MF);}void setupNoDominatingLoop() {// entry// / \// / \// / \// head1 head2// ^ \ / ^// ^ \ / ^// \-joinblk -/// |// retllvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);auto *BB1 = BasicBlock::Create(Ctx, "head1", &F);auto *BB2 = BasicBlock::Create(Ctx, "head2", &F);auto *BB3 = BasicBlock::Create(Ctx, "joinblk", &F);auto *BB4 = BasicBlock::Create(Ctx, "ret", &F);IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);IRB0.CreateBr(BB1);IRB1.CreateBr(BB2);IRB2.CreateBr(BB3);IRB3.CreateBr(BB4);IRB4.CreateRetVoid();MBB0 = MF->CreateMachineBasicBlock(BB0);MF->insert(MF->end(), MBB0);MBB1 = MF->CreateMachineBasicBlock(BB1);MF->insert(MF->end(), MBB1);MBB2 = MF->CreateMachineBasicBlock(BB2);MF->insert(MF->end(), MBB2);MBB3 = MF->CreateMachineBasicBlock(BB3);MF->insert(MF->end(), MBB3);MBB4 = MF->CreateMachineBasicBlock(BB4);MF->insert(MF->end(), MBB4);MBB0->addSuccessor(MBB1);MBB0->addSuccessor(MBB2);MBB1->addSuccessor(MBB3);MBB2->addSuccessor(MBB3);MBB3->addSuccessor(MBB1);MBB3->addSuccessor(MBB2);MBB3->addSuccessor(MBB4);MF->RenumberBlocks();setupLDVObj(&*MF);}void setupBadlyNestedLoops() {// entry// |// loop1 -o// | ^// | ^// loop2 -o// | ^// | ^// loop3 -o// |// ret//// NB: the loop blocks self-loop, which is a bit too fiddly to draw on// accurately.llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);auto *BB1 = BasicBlock::Create(Ctx, "loop1", &F);auto *BB2 = BasicBlock::Create(Ctx, "loop2", &F);auto *BB3 = BasicBlock::Create(Ctx, "loop3", &F);auto *BB4 = BasicBlock::Create(Ctx, "ret", &F);IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);IRB0.CreateBr(BB1);IRB1.CreateBr(BB2);IRB2.CreateBr(BB3);IRB3.CreateBr(BB4);IRB4.CreateRetVoid();MBB0 = MF->CreateMachineBasicBlock(BB0);MF->insert(MF->end(), MBB0);MBB1 = MF->CreateMachineBasicBlock(BB1);MF->insert(MF->end(), MBB1);MBB2 = MF->CreateMachineBasicBlock(BB2);MF->insert(MF->end(), MBB2);MBB3 = MF->CreateMachineBasicBlock(BB3);MF->insert(MF->end(), MBB3);MBB4 = MF->CreateMachineBasicBlock(BB4);MF->insert(MF->end(), MBB4);MBB0->addSuccessor(MBB1);MBB1->addSuccessor(MBB1);MBB1->addSuccessor(MBB2);MBB2->addSuccessor(MBB1);MBB2->addSuccessor(MBB2);MBB2->addSuccessor(MBB3);MBB3->addSuccessor(MBB2);MBB3->addSuccessor(MBB3);MBB3->addSuccessor(MBB4);MF->RenumberBlocks();setupLDVObj(&*MF);}MachineFunction *readMIRBlock(const char *Input) {MIRStr.clear();StringRef S = Twine(Twine(R"MIR(--- |target triple = "x86_64-unknown-linux-gnu"define void @test() { ret void }...---name: testtracksRegLiveness: truestack:- { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,stack-id: default, callee-saved-register: '', callee-saved-restored: true,debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }body: |bb.0:liveins: $rdi, $rsi)MIR") + Twine(Input) + Twine("...\n")).toNullTerminatedStringRef(MIRStr);;// Clear the "test" function from MMI if it's still present.if (Function *Fn = Mod->getFunction("test"))MMI->deleteMachineFunctionFor(*Fn);auto MemBuf = MemoryBuffer::getMemBuffer(S, "<input>");auto MIRParse = createMIRParser(std::move(MemBuf), Ctx);Mod = MIRParse->parseIRModule();assert(Mod);Mod->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-""n8:16:32:64-S128");bool Result = MIRParse->parseMachineFunctions(*Mod, *MMI);assert(!Result && "Failed to parse unit test machine function?");(void)Result;Function *Fn = Mod->getFunction("test");assert(Fn && "Failed to parse a unit test module string?");Fn->setSubprogram(OurFunc);return MMI->getMachineFunction(*Fn);}voidproduceMLocTransferFunction(MachineFunction &MF,SmallVectorImpl<MLocTransferMap> &MLocTransfer,unsigned MaxNumBlocks) {LDV->produceMLocTransferFunction(MF, MLocTransfer, MaxNumBlocks);}std::pair<FuncValueTable, FuncValueTable>allocValueTables(unsigned Blocks, unsigned Locs) {FuncValueTable MOutLocs = std::make_unique<ValueTable[]>(Blocks);FuncValueTable MInLocs = std::make_unique<ValueTable[]>(Blocks);for (unsigned int I = 0; I < Blocks; ++I) {MOutLocs[I] = std::make_unique<ValueIDNum[]>(Locs);MInLocs[I] = std::make_unique<ValueIDNum[]>(Locs);}return std::make_pair(std::move(MOutLocs), std::move(MInLocs));}};TEST_F(InstrRefLDVTest, MTransferDefs) {MachineFunction *MF = readMIRBlock(" $rax = MOV64ri 0\n"" RET64 $rax\n");setupLDVObj(MF);// We should start with only SP tracked.EXPECT_TRUE(MTracker->getNumLocs() == 1);SmallVector<MLocTransferMap, 1> TransferMap;TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);// Code contains only one register write: that should assign to each of the// aliasing registers. Test that all of them get locations, and have a// corresponding def at the first instr in the function.const char *RegNames[] = {"RAX", "HAX", "EAX", "AX", "AH", "AL"};EXPECT_TRUE(MTracker->getNumLocs() == 7);for (const char *RegName : RegNames) {Register R = getRegByName(RegName);ASSERT_TRUE(MTracker->isRegisterTracked(R));LocIdx L = MTracker->getRegMLoc(R);ValueIDNum V = MTracker->readReg(R);// Value of this register should be: block zero, instruction 1, and the// location it's defined in is itself.ValueIDNum ToCmp(0, 1, L);EXPECT_EQ(V, ToCmp);}// Do the same again, but with an aliasing write. This should write to all// the same registers again, except $ah and $hax (the upper 8 bits of $ax// and 32 bits of $rax resp.).MF = readMIRBlock(" $rax = MOV64ri 0\n"" $al = MOV8ri 0\n"" RET64 $rax\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);auto TestRegSetSite = [&](const char *Name, unsigned InstrNum) {Register R = getRegByName(Name);ASSERT_TRUE(MTracker->isRegisterTracked(R));LocIdx L = MTracker->getRegMLoc(R);ValueIDNum V = MTracker->readMLoc(L);ValueIDNum ToCmp(0, InstrNum, L);EXPECT_EQ(V, ToCmp);};TestRegSetSite("AL", 2);TestRegSetSite("AH", 1);TestRegSetSite("AX", 2);TestRegSetSite("EAX", 2);TestRegSetSite("HAX", 1);TestRegSetSite("RAX", 2);// This call should:// * Def rax via the implicit-def,// * Clobber rsi/rdi and all their subregs, via the register mask// * Same for rcx, despite it not being a use in the instr, it's in the mask// * NOT clobber $rsp / $esp $ sp, LiveDebugValues deliberately ignores// these.// * NOT clobber $rbx, because it's non-volatile// * Not track every other register in the machine, only those needed.MF = readMIRBlock(" $rax = MOV64ri 0\n" // instr 1" $rbx = MOV64ri 0\n" // instr 2" $rcx = MOV64ri 0\n" // instr 3" $rdi = MOV64ri 0\n" // instr 4" $rsi = MOV64ri 0\n" // instr 5" CALL64r $rax, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax, implicit-def $esp, implicit-def $sp\n\n\n\n" // instr 6" RET64 $rax\n"); // instr 7setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);const char *RegsSetInCall[] = {"AL", "AH", "AX", "EAX", "HAX", "RAX","DIL", "DIH", "DI", "EDI", "HDI", "RDI","SIL", "SIH", "SI", "ESI", "HSI", "RSI","CL", "CH", "CX", "ECX", "HCX", "RCX"};for (const char *RegSetInCall : RegsSetInCall)TestRegSetSite(RegSetInCall, 6);const char *RegsLeftAlone[] = {"BL", "BH", "BX", "EBX", "HBX", "RBX"};for (const char *RegLeftAlone : RegsLeftAlone)TestRegSetSite(RegLeftAlone, 2);// Stack pointer should be the live-in to the function, instruction zero.TestRegSetSite("RSP", 0);// These stack regs should not be tracked either. Nor the (fake) subregs.EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("ESP")));EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("SP")));EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("SPL")));EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("SPH")));EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("HSP")));// Should only be tracking: 6 x {A, B, C, DI, SI} registers = 30,// Plus RSP, SSP = 32.EXPECT_EQ(32u, MTracker->getNumLocs());// When we DBG_PHI something, we should track all its subregs.MF = readMIRBlock(" DBG_PHI $rdi, 0\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);// All DI regs and RSP tracked.EXPECT_EQ(7u, MTracker->getNumLocs());// All the DI registers should have block live-in values, i.e. the argument// to the function.const char *DIRegs[] = {"DIL", "DIH", "DI", "EDI", "HDI", "RDI"};for (const char *DIReg : DIRegs)TestRegSetSite(DIReg, 0);}TEST_F(InstrRefLDVTest, MTransferMeta) {// Meta instructions should not have any effect on register values.SmallVector<MLocTransferMap, 1> TransferMap;MachineFunction *MF = readMIRBlock(" $rax = MOV64ri 0\n"" $rax = IMPLICIT_DEF\n"" $rax = KILL killed $rax\n"" RET64 $rax\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);LocIdx RaxLoc = MTracker->getRegMLoc(getRegByName("RAX"));ValueIDNum V = MTracker->readMLoc(RaxLoc);// Def of rax should be from instruction 1, i.e., unmodified.ValueIDNum Cmp(0, 1, RaxLoc);EXPECT_EQ(Cmp, V);}TEST_F(InstrRefLDVTest, MTransferCopies) {SmallVector<MLocTransferMap, 1> TransferMap;// This memory spill should be recognised, and a spill slot created.MachineFunction *MF = readMIRBlock(" $rax = MOV64ri 0\n"" MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)\n"" RET64 $rax\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);// Check that the spill location contains the value defined in rax by// instruction 1. The MIR header says -16 offset, but it's stored as -8;// it's not completely clear why, but here we only care about correctly// identifying the slot, not that all the surrounding data is correct.SpillLoc L = {getRegByName("RSP"), StackOffset::getFixed(-8)};SpillLocationNo SpillNo = *MTracker->getOrTrackSpillLoc(L);unsigned SpillLocID = MTracker->getLocID(SpillNo, {64, 0});LocIdx SpillLoc = MTracker->getSpillMLoc(SpillLocID);ValueIDNum V = MTracker->readMLoc(SpillLoc);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->getRegMLoc(RAX);ValueIDNum Cmp(0, 1, RaxLoc);EXPECT_EQ(V, Cmp);// A spill and restore should be recognised.MF = readMIRBlock(" $rax = MOV64ri 0\n"" MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)\n"" $rbx = MOV64rm $rsp, 1, $noreg, 0, $noreg :: (load 8 from %stack.0)\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);// Test that rbx contains rax from instruction 1.RAX = getRegByName("RAX");RaxLoc = MTracker->getRegMLoc(RAX);Register RBX = getRegByName("RBX");LocIdx RbxLoc = MTracker->getRegMLoc(RBX);Cmp = ValueIDNum(0, 1, RaxLoc);ValueIDNum RbxVal = MTracker->readMLoc(RbxLoc);EXPECT_EQ(RbxVal, Cmp);// Testing that all the subregisters are transferred happens in// MTransferSubregSpills.// Copies and x86 movs should be recognised and honoured. In addition, all// of the subregisters should be copied across too.MF = readMIRBlock(" $rax = MOV64ri 0\n"" $rcx = COPY $rax\n"" $rbx = MOV64rr $rcx\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);const char *ARegs[] = {"AL", "AH", "AX", "EAX", "HAX", "RAX"};const char *BRegs[] = {"BL", "BH", "BX", "EBX", "HBX", "RBX"};const char *CRegs[] = {"CL", "CH", "CX", "ECX", "HCX", "RCX"};auto CheckReg = [&](unsigned int I) {LocIdx A = MTracker->getRegMLoc(getRegByName(ARegs[I]));LocIdx B = MTracker->getRegMLoc(getRegByName(BRegs[I]));LocIdx C = MTracker->getRegMLoc(getRegByName(CRegs[I]));ValueIDNum ARefVal(0, 1, A);ValueIDNum AVal = MTracker->readMLoc(A);ValueIDNum BVal = MTracker->readMLoc(B);ValueIDNum CVal = MTracker->readMLoc(C);EXPECT_EQ(ARefVal, AVal);EXPECT_EQ(ARefVal, BVal);EXPECT_EQ(ARefVal, CVal);};for (unsigned int I = 0; I < 6; ++I)CheckReg(I);// When we copy to a subregister, the super-register should be def'd too: it's// value will have changed.MF = readMIRBlock(" $rax = MOV64ri 0\n"" $ecx = COPY $eax\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);// First four regs [al, ah, ax, eax] should be copied to *cx.for (unsigned int I = 0; I < 4; ++I) {LocIdx A = MTracker->getRegMLoc(getRegByName(ARegs[I]));LocIdx C = MTracker->getRegMLoc(getRegByName(CRegs[I]));ValueIDNum ARefVal(0, 1, A);ValueIDNum AVal = MTracker->readMLoc(A);ValueIDNum CVal = MTracker->readMLoc(C);EXPECT_EQ(ARefVal, AVal);EXPECT_EQ(ARefVal, CVal);}// But rcx should contain a value defined by the COPY.LocIdx RcxLoc = MTracker->getRegMLoc(getRegByName("RCX"));ValueIDNum RcxVal = MTracker->readMLoc(RcxLoc);ValueIDNum RcxDefVal(0, 2, RcxLoc); // instr 2 -> the copyEXPECT_EQ(RcxVal, RcxDefVal);}TEST_F(InstrRefLDVTest, MTransferSubregSpills) {SmallVector<MLocTransferMap, 1> TransferMap;MachineFunction *MF = readMIRBlock(" $rax = MOV64ri 0\n"" MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)\n"" $rbx = MOV64rm $rsp, 1, $noreg, 0, $noreg :: (load 8 from %stack.0)\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);// Check that all the subregs of rax and rbx contain the same values. One// should completely transfer to the other.const char *ARegs[] = {"AL", "AH", "AX", "EAX", "HAX", "RAX"};const char *BRegs[] = {"BL", "BH", "BX", "EBX", "HBX", "RBX"};for (unsigned int I = 0; I < 6; ++I) {LocIdx A = MTracker->getRegMLoc(getRegByName(ARegs[I]));LocIdx B = MTracker->getRegMLoc(getRegByName(BRegs[I]));EXPECT_EQ(MTracker->readMLoc(A), MTracker->readMLoc(B));}// Explicitly check what's in the different subreg slots, on the stack.// Pair up subreg idx fields with the corresponding subregister in $rax.MLocTracker::StackSlotPos SubRegIdxes[] = {{8, 0}, {8, 8}, {16, 0}, {32, 0}, {64, 0}};const char *SubRegNames[] = {"AL", "AH", "AX", "EAX", "RAX"};for (unsigned int I = 0; I < 5; ++I) {// Value number where it's defined,LocIdx RegLoc = MTracker->getRegMLoc(getRegByName(SubRegNames[I]));ValueIDNum DefNum(0, 1, RegLoc);// Read the corresponding subreg field from the stack.SpillLoc L = {getRegByName("RSP"), StackOffset::getFixed(-8)};SpillLocationNo SpillNo = *MTracker->getOrTrackSpillLoc(L);unsigned SpillID = MTracker->getLocID(SpillNo, SubRegIdxes[I]);LocIdx SpillLoc = MTracker->getSpillMLoc(SpillID);ValueIDNum SpillValue = MTracker->readMLoc(SpillLoc);EXPECT_EQ(DefNum, SpillValue);}// If we have exactly the same code, but we write $eax to the stack slot after// $rax, then we should still have exactly the same output in the lower five// subregisters. Storing $eax to the start of the slot will overwrite with the// same values. $rax, as an aliasing register, should be reset to something// else by that write.// In theory, we could try and recognise that we're writing the _same_ values// to the stack again, and so $rax doesn't need to be reset to something else.// It seems vanishingly unlikely that LLVM would generate such code though,// so the benefits would be small.MF = readMIRBlock(" $rax = MOV64ri 0\n"" MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)\n"" MOV32mr $rsp, 1, $noreg, 16, $noreg, $eax :: (store 4 into %stack.0)\n"" $rbx = MOV64rm $rsp, 1, $noreg, 0, $noreg :: (load 8 from %stack.0)\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);// Check lower five registers up to and include $eax == $ebx,for (unsigned int I = 0; I < 5; ++I) {LocIdx A = MTracker->getRegMLoc(getRegByName(ARegs[I]));LocIdx B = MTracker->getRegMLoc(getRegByName(BRegs[I]));EXPECT_EQ(MTracker->readMLoc(A), MTracker->readMLoc(B));}// $rbx should contain something else; today it's a def at the spill point// of the 4 byte value.SpillLoc L = {getRegByName("RSP"), StackOffset::getFixed(-8)};SpillLocationNo SpillNo = *MTracker->getOrTrackSpillLoc(L);unsigned SpillID = MTracker->getLocID(SpillNo, {64, 0});LocIdx Spill64Loc = MTracker->getSpillMLoc(SpillID);ValueIDNum DefAtSpill64(0, 3, Spill64Loc);LocIdx RbxLoc = MTracker->getRegMLoc(getRegByName("RBX"));EXPECT_EQ(MTracker->readMLoc(RbxLoc), DefAtSpill64);// Same again, test that the lower four subreg slots on the stack are the// value defined by $rax in instruction 1.for (unsigned int I = 0; I < 4; ++I) {// Value number where it's defined,LocIdx RegLoc = MTracker->getRegMLoc(getRegByName(SubRegNames[I]));ValueIDNum DefNum(0, 1, RegLoc);// Read the corresponding subreg field from the stack.SpillNo = *MTracker->getOrTrackSpillLoc(L);SpillID = MTracker->getLocID(SpillNo, SubRegIdxes[I]);LocIdx SpillLoc = MTracker->getSpillMLoc(SpillID);ValueIDNum SpillValue = MTracker->readMLoc(SpillLoc);EXPECT_EQ(DefNum, SpillValue);}// Stack slot for $rax should be a different value, today it's EmptyValue.ValueIDNum SpillValue = MTracker->readMLoc(Spill64Loc);EXPECT_EQ(SpillValue, DefAtSpill64);// If we write something to the stack, then over-write with some register// from a completely different hierarchy, none of the "old" values should be// readable.// NB: slight hack, store 16 in to a 8 byte stack slot.MF = readMIRBlock(" $rax = MOV64ri 0\n"" MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)\n"" $xmm0 = IMPLICIT_DEF\n"" MOVUPDmr $rsp, 1, $noreg, 16, $noreg, killed $xmm0 :: (store (s128) into %stack.0)\n"" $rbx = MOV64rm $rsp, 1, $noreg, 0, $noreg :: (load 8 from %stack.0)\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);for (unsigned int I = 0; I < 5; ++I) {// Read subreg fields from the stack.SpillLocationNo SpillNo = *MTracker->getOrTrackSpillLoc(L);unsigned SpillID = MTracker->getLocID(SpillNo, SubRegIdxes[I]);LocIdx SpillLoc = MTracker->getSpillMLoc(SpillID);ValueIDNum SpillValue = MTracker->readMLoc(SpillLoc);// Value should be defined by the spill-to-xmm0 instr, get value of a def// at the point of the spill.ValueIDNum SpillDef(0, 4, SpillLoc);EXPECT_EQ(SpillValue, SpillDef);}// Read xmm0's position and ensure it has a value. Should be the live-in// value to the block, as IMPLICIT_DEF isn't a real def.SpillNo = *MTracker->getOrTrackSpillLoc(L);SpillID = MTracker->getLocID(SpillNo, {128, 0});LocIdx Spill128Loc = MTracker->getSpillMLoc(SpillID);SpillValue = MTracker->readMLoc(Spill128Loc);Register XMM0 = getRegByName("XMM0");LocIdx Xmm0Loc = MTracker->getRegMLoc(XMM0);EXPECT_EQ(ValueIDNum(0, 0, Xmm0Loc), SpillValue);// What happens if we spill ah to the stack, then load al? It should find// the same value.MF = readMIRBlock(" $rax = MOV64ri 0\n"" MOV8mr $rsp, 1, $noreg, 16, $noreg, $ah :: (store 1 into %stack.0)\n"" $al = MOV8rm $rsp, 1, $noreg, 0, $noreg :: (load 1 from %stack.0)\n"" RET64\n");setupLDVObj(MF);TransferMap.clear();TransferMap.resize(1);produceMLocTransferFunction(*MF, TransferMap, 1);Register AL = getRegByName("AL");Register AH = getRegByName("AH");LocIdx AlLoc = MTracker->getRegMLoc(AL);LocIdx AhLoc = MTracker->getRegMLoc(AH);ValueIDNum AHDef(0, 1, AhLoc);ValueIDNum ALValue = MTracker->readMLoc(AlLoc);EXPECT_EQ(ALValue, AHDef);}TEST_F(InstrRefLDVTest, MLocSingleBlock) {// Test some very simple properties about interpreting the transfer function.setupSingleBlock();// We should start with a single location, the stack pointer.ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);// Set up live-in and live-out tables for this function: two locations (we// add one later) in a single block.FuncValueTable MOutLocs, MInLocs;std::tie(MOutLocs, MInLocs) = allocValueTables(1, 2);// Transfer function: nothing.SmallVector<MLocTransferMap, 1> TransferFunc;TransferFunc.resize(1);// Try and build value maps...buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);// The result should be that RSP is marked as a live-in-PHI -- this represents// an argument. And as there's no transfer function, the block live-out should// be the same.EXPECT_EQ(MInLocs[0][0], ValueIDNum(0, 0, RspLoc));EXPECT_EQ(MOutLocs[0][0], ValueIDNum(0, 0, RspLoc));// Try again, this time initialising the in-locs to be defined by an// instruction. The entry block should always be re-assigned to be the// arguments.initValueArray(MInLocs, 1, 2);initValueArray(MOutLocs, 1, 2);MInLocs[0][0] = ValueIDNum(0, 1, RspLoc);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], ValueIDNum(0, 0, RspLoc));EXPECT_EQ(MOutLocs[0][0], ValueIDNum(0, 0, RspLoc));// Now insert something into the transfer function to assign to the single// machine location.TransferFunc[0].insert({RspLoc, ValueIDNum(0, 1, RspLoc)});initValueArray(MInLocs, 1, 2);initValueArray(MOutLocs, 1, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], ValueIDNum(0, 0, RspLoc));EXPECT_EQ(MOutLocs[0][0], ValueIDNum(0, 1, RspLoc));TransferFunc[0].clear();// Add a new register to be tracked, and insert it into the transfer function// as a copy. The output of $rax should be the live-in value of $rsp.Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);TransferFunc[0].insert({RspLoc, ValueIDNum(0, 1, RspLoc)});TransferFunc[0].insert({RaxLoc, ValueIDNum(0, 0, RspLoc)});initValueArray(MInLocs, 1, 2);initValueArray(MOutLocs, 1, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], ValueIDNum(0, 0, RspLoc));EXPECT_EQ(MInLocs[0][1], ValueIDNum(0, 0, RaxLoc));EXPECT_EQ(MOutLocs[0][0], ValueIDNum(0, 1, RspLoc));EXPECT_EQ(MOutLocs[0][1], ValueIDNum(0, 0, RspLoc)); // Rax contains RspLoc.TransferFunc[0].clear();}TEST_F(InstrRefLDVTest, MLocDiamondBlocks) {// Test that information flows from the entry block to two successors.// entry// / \// br1 br2// \ /// retsetupDiamondBlocks();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(4, 2);// Transfer function: start with nothing.SmallVector<MLocTransferMap, 1> TransferFunc;TransferFunc.resize(4);// Name some values.unsigned EntryBlk = 0, BrBlk1 = 1, BrBlk2 = 2, RetBlk = 3;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum RspDefInBlk0(EntryBlk, 1, RspLoc);ValueIDNum RspDefInBlk1(BrBlk1, 1, RspLoc);ValueIDNum RspDefInBlk2(BrBlk2, 1, RspLoc);ValueIDNum RspPHIInBlk3(RetBlk, 0, RspLoc);ValueIDNum RaxLiveInBlk1(BrBlk1, 0, RaxLoc);ValueIDNum RaxLiveInBlk2(BrBlk2, 0, RaxLoc);// With no transfer function, the live-in values to the entry block should// propagate to all live-outs and the live-ins to the two successor blocks.// IN ADDITION: this checks that the exit block doesn't get a PHI put in it.initValueArray(MInLocs, 4, 2);initValueArray(MOutLocs, 4, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], LiveInRsp);EXPECT_EQ(MInLocs[2][0], LiveInRsp);EXPECT_EQ(MInLocs[3][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[3][0], LiveInRsp);// (Skipped writing out locations for $rax).// Check that a def of $rsp in the entry block will likewise reach all the// successors.TransferFunc[0].insert({RspLoc, RspDefInBlk0});initValueArray(MInLocs, 4, 2);initValueArray(MOutLocs, 4, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspDefInBlk0);EXPECT_EQ(MInLocs[2][0], RspDefInBlk0);EXPECT_EQ(MInLocs[3][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[0][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk0);TransferFunc[0].clear();// Def in one branch of the diamond means that we need a PHI in the ret blockTransferFunc[0].insert({RspLoc, RspDefInBlk0});TransferFunc[1].insert({RspLoc, RspDefInBlk1});initValueArray(MInLocs, 4, 2);initValueArray(MOutLocs, 4, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);// This value map: like above, where RspDefInBlk0 is propagated through one// branch of the diamond, but is def'ed in the live-outs of the other. The// ret / merging block should have a PHI in its live-ins.EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspDefInBlk0);EXPECT_EQ(MInLocs[2][0], RspDefInBlk0);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MOutLocs[0][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[3][0], RspPHIInBlk3);TransferFunc[0].clear();TransferFunc[1].clear();// If we have differeing defs in either side of the diamond, we should// continue to produce a PHI,TransferFunc[0].insert({RspLoc, RspDefInBlk0});TransferFunc[1].insert({RspLoc, RspDefInBlk1});TransferFunc[2].insert({RspLoc, RspDefInBlk2});initValueArray(MInLocs, 4, 2);initValueArray(MOutLocs, 4, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspDefInBlk0);EXPECT_EQ(MInLocs[2][0], RspDefInBlk0);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MOutLocs[0][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[3][0], RspPHIInBlk3);TransferFunc[0].clear();TransferFunc[1].clear();TransferFunc[2].clear();// If we have defs of the same value on either side of the branch, a PHI will// initially be created, however value propagation should then eliminate it.// Encode this by copying the live-in value to $rax, and copying it to $rsp// from $rax in each branch of the diamond. We don't allow the definition of// arbitary values in transfer functions.TransferFunc[0].insert({RspLoc, RspDefInBlk0});TransferFunc[0].insert({RaxLoc, LiveInRsp});TransferFunc[1].insert({RspLoc, RaxLiveInBlk1});TransferFunc[2].insert({RspLoc, RaxLiveInBlk2});initValueArray(MInLocs, 4, 2);initValueArray(MOutLocs, 4, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspDefInBlk0);EXPECT_EQ(MInLocs[2][0], RspDefInBlk0);EXPECT_EQ(MInLocs[3][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], RspDefInBlk0);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[3][0], LiveInRsp);TransferFunc[0].clear();TransferFunc[1].clear();TransferFunc[2].clear();}TEST_F(InstrRefLDVTest, MLocDiamondSpills) {// Test that defs in stack locations that require PHIs, cause PHIs to be// installed in aliasing locations. i.e., if there's a PHI in the lower// 8 bits of the stack, there should be PHIs for 16/32/64 bit locations// on the stack too.// Technically this isn't needed for accuracy: we should calculate PHIs// independently for each location. However, because there's an optimisation// that only places PHIs for the lower "interfering" parts of stack slots,// test for this behaviour.setupDiamondBlocks();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);// Create a stack location and ensure it's tracked.SpillLoc SL = {getRegByName("RSP"), StackOffset::getFixed(-8)};SpillLocationNo SpillNo = *MTracker->getOrTrackSpillLoc(SL);ASSERT_EQ(MTracker->getNumLocs(), 11u); // Tracks all possible stack locs.// Locations are: RSP, stack slots from 2^3 bits wide up to 2^9 for zmm regs,// then slots for sub_8bit_hi and sub_16bit_hi ({8, 8} and {16, 16}).// Finally, one for spilt fp80 registers.// Pick out the locations on the stack that various x86 regs would be written// to. HAX is the upper 16 bits of EAX.unsigned ALID = MTracker->getLocID(SpillNo, {8, 0});unsigned AHID = MTracker->getLocID(SpillNo, {8, 8});unsigned AXID = MTracker->getLocID(SpillNo, {16, 0});unsigned EAXID = MTracker->getLocID(SpillNo, {32, 0});unsigned HAXID = MTracker->getLocID(SpillNo, {16, 16});unsigned RAXID = MTracker->getLocID(SpillNo, {64, 0});LocIdx ALStackLoc = MTracker->getSpillMLoc(ALID);LocIdx AHStackLoc = MTracker->getSpillMLoc(AHID);LocIdx AXStackLoc = MTracker->getSpillMLoc(AXID);LocIdx EAXStackLoc = MTracker->getSpillMLoc(EAXID);LocIdx HAXStackLoc = MTracker->getSpillMLoc(HAXID);LocIdx RAXStackLoc = MTracker->getSpillMLoc(RAXID);// There are other locations, for things like xmm0, which we're going to// ignore here.FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(4, 11);// Transfer function: start with nothing.SmallVector<MLocTransferMap, 1> TransferFunc;TransferFunc.resize(4);// Name some values.unsigned EntryBlk = 0, Blk1 = 1, RetBlk = 3;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum ALLiveIn(EntryBlk, 0, ALStackLoc);ValueIDNum AHLiveIn(EntryBlk, 0, AHStackLoc);ValueIDNum HAXLiveIn(EntryBlk, 0, HAXStackLoc);ValueIDNum ALPHI(RetBlk, 0, ALStackLoc);ValueIDNum AXPHI(RetBlk, 0, AXStackLoc);ValueIDNum EAXPHI(RetBlk, 0, EAXStackLoc);ValueIDNum HAXPHI(RetBlk, 0, HAXStackLoc);ValueIDNum RAXPHI(RetBlk, 0, RAXStackLoc);ValueIDNum ALDefInBlk1(Blk1, 1, ALStackLoc);ValueIDNum HAXDefInBlk1(Blk1, 1, HAXStackLoc);SmallPtrSet<MachineBasicBlock *, 4> AllBlocks{MBB0, MBB1, MBB2, MBB3};// If we put defs into one side of the diamond, for AL and HAX, then we should// find all aliasing positions have PHIs placed. This isn't technically what// the transfer function says to do: but we're testing that the optimisation// to reduce IDF calculation does the right thing.// AH should not be def'd: it don't alias AL or HAX.//// NB: we don't call buildMLocValueMap, because it will try to eliminate the// upper-slot PHIs, and succeed because of our slightly cooked transfer// function.TransferFunc[1].insert({ALStackLoc, ALDefInBlk1});TransferFunc[1].insert({HAXStackLoc, HAXDefInBlk1});initValueArray(MInLocs, 4, 11);placeMLocPHIs(*MF, AllBlocks, MInLocs, TransferFunc);EXPECT_EQ(MInLocs[3][ALStackLoc.asU64()], ALPHI);EXPECT_EQ(MInLocs[3][AXStackLoc.asU64()], AXPHI);EXPECT_EQ(MInLocs[3][EAXStackLoc.asU64()], EAXPHI);EXPECT_EQ(MInLocs[3][HAXStackLoc.asU64()], HAXPHI);EXPECT_EQ(MInLocs[3][RAXStackLoc.asU64()], RAXPHI);// AH should be left untouched,EXPECT_EQ(MInLocs[3][AHStackLoc.asU64()], ValueIDNum::EmptyValue);}TEST_F(InstrRefLDVTest, MLocSimpleLoop) {// entry// |// |/-----\// loopblk |// |\-----/// |// retsetupSimpleLoop();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(3, 2);SmallVector<MLocTransferMap, 1> TransferFunc;TransferFunc.resize(3);// Name some values.unsigned EntryBlk = 0, LoopBlk = 1, RetBlk = 2;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);ValueIDNum RspDefInBlk1(LoopBlk, 1, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum RaxPHIInBlk1(LoopBlk, 0, RaxLoc);ValueIDNum RaxPHIInBlk2(RetBlk, 0, RaxLoc);// Begin test with all locations being live-through.initValueArray(MInLocs, 3, 2);initValueArray(MOutLocs, 3, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], LiveInRsp);EXPECT_EQ(MInLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);// Add a def of $rsp to the loop block: it should be in the live-outs, but// should cause a PHI to be placed in the live-ins. Test the transfer function// by copying that PHI into $rax in the loop, then back to $rsp in the ret// block.TransferFunc[1].insert({RspLoc, RspDefInBlk1});TransferFunc[1].insert({RaxLoc, RspPHIInBlk1});TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});initValueArray(MInLocs, 3, 2);initValueArray(MOutLocs, 3, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspPHIInBlk1);// Check rax as well,EXPECT_EQ(MInLocs[0][1], LiveInRax);EXPECT_EQ(MInLocs[1][1], RaxPHIInBlk1);EXPECT_EQ(MInLocs[2][1], RspPHIInBlk1);EXPECT_EQ(MOutLocs[0][1], LiveInRax);EXPECT_EQ(MOutLocs[1][1], RspPHIInBlk1);EXPECT_EQ(MOutLocs[2][1], RspPHIInBlk1);TransferFunc[1].clear();TransferFunc[2].clear();// As with the diamond case, a PHI will be created if there's a (implicit)// def in the entry block and loop block; but should be value propagated away// if it copies in the same value. Copy live-in $rsp to $rax, then copy it// into $rsp in the loop. Encoded as copying the live-in $rax value in block 1// to $rsp.TransferFunc[0].insert({RaxLoc, LiveInRsp});TransferFunc[1].insert({RspLoc, RaxPHIInBlk1});initValueArray(MInLocs, 3, 2);initValueArray(MOutLocs, 3, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], LiveInRsp);EXPECT_EQ(MInLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);// Check $rax's values.EXPECT_EQ(MInLocs[0][1], LiveInRax);EXPECT_EQ(MInLocs[1][1], LiveInRsp);EXPECT_EQ(MInLocs[2][1], LiveInRsp);EXPECT_EQ(MOutLocs[0][1], LiveInRsp);EXPECT_EQ(MOutLocs[1][1], LiveInRsp);EXPECT_EQ(MOutLocs[2][1], LiveInRsp);TransferFunc[0].clear();TransferFunc[1].clear();}TEST_F(InstrRefLDVTest, MLocNestedLoop) {// entry// |// loop1// ^\// | \ /-\// | loop2 |// | / \-/// ^ /// join// |// retsetupNestedLoops();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(5, 2);SmallVector<MLocTransferMap, 1> TransferFunc;TransferFunc.resize(5);unsigned EntryBlk = 0, Loop1Blk = 1, Loop2Blk = 2, JoinBlk = 3;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum RspPHIInBlk1(Loop1Blk, 0, RspLoc);ValueIDNum RspDefInBlk1(Loop1Blk, 1, RspLoc);ValueIDNum RspPHIInBlk2(Loop2Blk, 0, RspLoc);ValueIDNum RspDefInBlk2(Loop2Blk, 1, RspLoc);ValueIDNum RspDefInBlk3(JoinBlk, 1, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum RaxPHIInBlk1(Loop1Blk, 0, RaxLoc);ValueIDNum RaxPHIInBlk2(Loop2Blk, 0, RaxLoc);// Like the other tests: first ensure that if there's nothing in the transfer// function, then everything is live-through (check $rsp).initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], LiveInRsp);EXPECT_EQ(MInLocs[2][0], LiveInRsp);EXPECT_EQ(MInLocs[3][0], LiveInRsp);EXPECT_EQ(MInLocs[4][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[3][0], LiveInRsp);EXPECT_EQ(MOutLocs[4][0], LiveInRsp);// A def in the inner loop means we should get PHIs at the heads of both// loops. Live-outs of the last three blocks will be the def, as it dominates// those.TransferFunc[2].insert({RspLoc, RspDefInBlk2});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspDefInBlk2);EXPECT_EQ(MInLocs[4][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk2);TransferFunc[2].clear();// Adding a def to the outer loop header shouldn't affect this much -- the// live-out of block 1 changes.TransferFunc[1].insert({RspLoc, RspDefInBlk1});TransferFunc[2].insert({RspLoc, RspDefInBlk2});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspDefInBlk2);EXPECT_EQ(MInLocs[4][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk2);TransferFunc[1].clear();TransferFunc[2].clear();// Likewise, putting a def in the outer loop tail shouldn't affect where// the PHIs go, and should propagate into the ret block.TransferFunc[1].insert({RspLoc, RspDefInBlk1});TransferFunc[2].insert({RspLoc, RspDefInBlk2});TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspDefInBlk2);EXPECT_EQ(MInLocs[4][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk3);TransferFunc[1].clear();TransferFunc[2].clear();TransferFunc[3].clear();// However: if we don't def in the inner-loop, then we just have defs in the// head and tail of the outer loop. The inner loop should be live-through.TransferFunc[1].insert({RspLoc, RspDefInBlk1});TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspDefInBlk1);EXPECT_EQ(MInLocs[3][0], RspDefInBlk1);EXPECT_EQ(MInLocs[4][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk3);TransferFunc[1].clear();TransferFunc[3].clear();// Check that this still works if we copy RspDefInBlk1 to $rax and then// copy it back into $rsp in the inner loop.TransferFunc[1].insert({RspLoc, RspDefInBlk1});TransferFunc[1].insert({RaxLoc, RspDefInBlk1});TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspDefInBlk1);EXPECT_EQ(MInLocs[3][0], RspDefInBlk1);EXPECT_EQ(MInLocs[4][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk3);// Look at raxes value in the relevant blocks,EXPECT_EQ(MInLocs[2][1], RspDefInBlk1);EXPECT_EQ(MOutLocs[1][1], RspDefInBlk1);TransferFunc[1].clear();TransferFunc[2].clear();TransferFunc[3].clear();// If we have a single def in the tail of the outer loop, that should produce// a PHI at the loop head, and be live-through the inner loop.TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[4][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MOutLocs[2][0], RspPHIInBlk1);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk3);TransferFunc[3].clear();// And if we copy from $rsp to $rax in block 2, it should resolve to the PHI// in block 1, and we should keep that value in rax until the ret block.// There'll be a PHI in block 1 and 2, because we're putting a def in the// inner loop.TransferFunc[2].insert({RaxLoc, RspPHIInBlk2});TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);// Examining the values of rax,EXPECT_EQ(MInLocs[0][1], LiveInRax);EXPECT_EQ(MInLocs[1][1], RaxPHIInBlk1);EXPECT_EQ(MInLocs[2][1], RaxPHIInBlk2);EXPECT_EQ(MInLocs[3][1], RspPHIInBlk1);EXPECT_EQ(MInLocs[4][1], RspPHIInBlk1);EXPECT_EQ(MOutLocs[0][1], LiveInRax);EXPECT_EQ(MOutLocs[1][1], RaxPHIInBlk1);EXPECT_EQ(MOutLocs[2][1], RspPHIInBlk1);EXPECT_EQ(MOutLocs[3][1], RspPHIInBlk1);EXPECT_EQ(MOutLocs[4][1], RspPHIInBlk1);TransferFunc[2].clear();TransferFunc[3].clear();}TEST_F(InstrRefLDVTest, MLocNoDominatingLoop) {// entry// / \// / \// / \// head1 head2// ^ \ / ^// ^ \ / ^// \-joinblk -/// |// retsetupNoDominatingLoop();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(5, 2);SmallVector<MLocTransferMap, 1> TransferFunc;TransferFunc.resize(5);unsigned EntryBlk = 0, Head1Blk = 1, Head2Blk = 2, JoinBlk = 3;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum RspPHIInBlk1(Head1Blk, 0, RspLoc);ValueIDNum RspDefInBlk1(Head1Blk, 1, RspLoc);ValueIDNum RspPHIInBlk2(Head2Blk, 0, RspLoc);ValueIDNum RspDefInBlk2(Head2Blk, 1, RspLoc);ValueIDNum RspPHIInBlk3(JoinBlk, 0, RspLoc);ValueIDNum RspDefInBlk3(JoinBlk, 1, RspLoc);ValueIDNum RaxPHIInBlk1(Head1Blk, 0, RaxLoc);ValueIDNum RaxPHIInBlk2(Head2Blk, 0, RaxLoc);// As ever, test that everything is live-through if there are no defs.initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], LiveInRsp);EXPECT_EQ(MInLocs[2][0], LiveInRsp);EXPECT_EQ(MInLocs[3][0], LiveInRsp);EXPECT_EQ(MInLocs[4][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[3][0], LiveInRsp);EXPECT_EQ(MOutLocs[4][0], LiveInRsp);// Putting a def in the 'join' block will cause us to have two distinct// PHIs in each loop head, then on entry to the join block.TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MInLocs[4][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MOutLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk3);TransferFunc[3].clear();// We should get the same behaviour if we put the def in either of the// loop heads -- it should force the other head to be a PHI.TransferFunc[1].insert({RspLoc, RspDefInBlk1});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MInLocs[4][0], RspPHIInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MOutLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MOutLocs[4][0], RspPHIInBlk3);TransferFunc[1].clear();// Check symmetry,TransferFunc[2].insert({RspLoc, RspDefInBlk2});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MInLocs[4][0], RspPHIInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk2);EXPECT_EQ(MOutLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MOutLocs[4][0], RspPHIInBlk3);TransferFunc[2].clear();// Test some scenarios where there _shouldn't_ be any PHIs created at heads.// These are those PHIs are created, but value propagation eliminates them.// For example, lets copy rsp-livein to $rsp inside each loop head, so that// there's no need for a PHI in the join block. Put a def of $rsp in block 3// to force PHIs elsewhere.TransferFunc[0].insert({RaxLoc, LiveInRsp});TransferFunc[1].insert({RspLoc, RaxPHIInBlk1});TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], LiveInRsp);EXPECT_EQ(MInLocs[4][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk3);TransferFunc[0].clear();TransferFunc[1].clear();TransferFunc[2].clear();TransferFunc[3].clear();// In fact, if we eliminate the def in block 3, none of those PHIs are// necessary, as we're just repeatedly copying LiveInRsp into $rsp. They// should all be value propagated out.TransferFunc[0].insert({RaxLoc, LiveInRsp});TransferFunc[1].insert({RspLoc, RaxPHIInBlk1});TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], LiveInRsp);EXPECT_EQ(MInLocs[2][0], LiveInRsp);EXPECT_EQ(MInLocs[3][0], LiveInRsp);EXPECT_EQ(MInLocs[4][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[3][0], LiveInRsp);EXPECT_EQ(MOutLocs[4][0], LiveInRsp);TransferFunc[0].clear();TransferFunc[1].clear();TransferFunc[2].clear();}TEST_F(InstrRefLDVTest, MLocBadlyNestedLoops) {// entry// |// loop1 -o// | ^// | ^// loop2 -o// | ^// | ^// loop3 -o// |// retsetupBadlyNestedLoops();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(5, 2);SmallVector<MLocTransferMap, 1> TransferFunc;TransferFunc.resize(5);unsigned EntryBlk = 0, Loop1Blk = 1, Loop2Blk = 2, Loop3Blk = 3;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum RspPHIInBlk1(Loop1Blk, 0, RspLoc);ValueIDNum RspDefInBlk1(Loop1Blk, 1, RspLoc);ValueIDNum RspPHIInBlk2(Loop2Blk, 0, RspLoc);ValueIDNum RspPHIInBlk3(Loop3Blk, 0, RspLoc);ValueIDNum RspDefInBlk3(Loop3Blk, 1, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum RaxPHIInBlk3(Loop3Blk, 0, RaxLoc);// As ever, test that everything is live-through if there are no defs.initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], LiveInRsp);EXPECT_EQ(MInLocs[2][0], LiveInRsp);EXPECT_EQ(MInLocs[3][0], LiveInRsp);EXPECT_EQ(MInLocs[4][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], LiveInRsp);EXPECT_EQ(MOutLocs[2][0], LiveInRsp);EXPECT_EQ(MOutLocs[3][0], LiveInRsp);EXPECT_EQ(MOutLocs[4][0], LiveInRsp);// A def in loop3 should cause PHIs in every loop block: they're all// reachable from each other.TransferFunc[3].insert({RspLoc, RspDefInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MInLocs[4][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MOutLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk3);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk3);TransferFunc[3].clear();// A def in loop1 should cause a PHI in loop1, but not the other blocks.// loop2 and loop3 are dominated by the def in loop1, so they should have// that value live-through.TransferFunc[1].insert({RspLoc, RspDefInBlk1});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspDefInBlk1);EXPECT_EQ(MInLocs[3][0], RspDefInBlk1);EXPECT_EQ(MInLocs[4][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[2][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[3][0], RspDefInBlk1);EXPECT_EQ(MOutLocs[4][0], RspDefInBlk1);TransferFunc[1].clear();// As with earlier tricks: copy $rsp to $rax in the entry block, then $rax// to $rsp in block 3. The only def of $rsp is simply copying the same value// back into itself, and the value of $rsp is LiveInRsp all the way through.// PHIs should be created, then value-propagated away... however this// doesn't work in practice.// Consider the entry to loop3: we can determine that there's an incoming// PHI value from loop2, and LiveInRsp from the self-loop. This would still// justify having a PHI on entry to loop3. The only way to completely// value-propagate these PHIs away would be to speculatively explore what// PHIs could be eliminated and what that would lead to; which is// combinatorially complex.// Happily:// a) In this scenario, we always have a tracked location for LiveInRsp// anyway, so there's no loss in availability,// b) Only DBG_PHIs of a register would be vunlerable to this scenario, and// even then only if a true PHI became a DBG_PHI and was then optimised// through branch folding to no longer be at a CFG join,// c) The register allocator can spot this kind of redundant COPY easily,// and eliminate it.//// This unit test left in as a reference for the limitations of this// approach. PHIs will be left in $rsp on entry to each block.TransferFunc[0].insert({RaxLoc, LiveInRsp});TransferFunc[3].insert({RspLoc, RaxPHIInBlk3});initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);buildMLocValueMap(MInLocs, MOutLocs, TransferFunc);EXPECT_EQ(MInLocs[0][0], LiveInRsp);EXPECT_EQ(MInLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MInLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MInLocs[3][0], RspPHIInBlk3);EXPECT_EQ(MInLocs[4][0], LiveInRsp);EXPECT_EQ(MOutLocs[0][0], LiveInRsp);EXPECT_EQ(MOutLocs[1][0], RspPHIInBlk1);EXPECT_EQ(MOutLocs[2][0], RspPHIInBlk2);EXPECT_EQ(MOutLocs[3][0], LiveInRsp);EXPECT_EQ(MOutLocs[4][0], LiveInRsp);// Check $rax's value. It should have $rsps value from the entry block// onwards.EXPECT_EQ(MInLocs[0][1], LiveInRax);EXPECT_EQ(MInLocs[1][1], LiveInRsp);EXPECT_EQ(MInLocs[2][1], LiveInRsp);EXPECT_EQ(MInLocs[3][1], LiveInRsp);EXPECT_EQ(MInLocs[4][1], LiveInRsp);EXPECT_EQ(MOutLocs[0][1], LiveInRsp);EXPECT_EQ(MOutLocs[1][1], LiveInRsp);EXPECT_EQ(MOutLocs[2][1], LiveInRsp);EXPECT_EQ(MOutLocs[3][1], LiveInRsp);EXPECT_EQ(MOutLocs[4][1], LiveInRsp);}TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {// entry// / \// br1 br2// \ /// retsetupDiamondBlocks();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(4, 2);initValueArray(MOutLocs, 4, 2);unsigned EntryBlk = 0, Br2Blk = 2, RetBlk = 3;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum RspPHIInBlk2(Br2Blk, 0, RspLoc);ValueIDNum RspPHIInBlk3(RetBlk, 0, RspLoc);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallVector<DbgValue, 32> VLiveOuts;VLiveOuts.resize(4, DbgValue(EmptyProps, DbgValue::Undef));InstrRefBasedLDV::LiveIdxT VLiveOutIdx;VLiveOutIdx[MBB0] = &VLiveOuts[0];VLiveOutIdx[MBB1] = &VLiveOuts[1];VLiveOutIdx[MBB2] = &VLiveOuts[2];VLiveOutIdx[MBB3] = &VLiveOuts[3];SmallVector<const MachineBasicBlock *, 2> Preds;for (const auto *Pred : MBB3->predecessors())Preds.push_back(Pred);// Specify the live-outs around the joining block.MOutLocs[1][0] = LiveInRsp;MOutLocs[2][0] = LiveInRax;Optional<ValueIDNum> Result;// Simple case: join two distinct values on entry to the block.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);// Should have picked a PHI in $rsp in block 3.EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RspPHIInBlk3);}// If the incoming values are swapped between blocks, we should not// successfully join. The CFG merge would select the right values, but in// the wrong conditions.std::swap(VLiveOuts[1], VLiveOuts[2]);Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// Swap back,std::swap(VLiveOuts[1], VLiveOuts[2]);// Setting one of these to being a constant should prohibit merging.VLiveOuts[1].Kind = DbgValue::Const;VLiveOuts[1].MO = MachineOperand::CreateImm(0);Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// Seeing both to being a constant -> still prohibit, it shouldn't become// a value in the register file anywhere.VLiveOuts[2] = VLiveOuts[1];Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// NoVals shouldn't join with anything else.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::NoVal);Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// We might merge in another VPHI in such a join. Present pickVPHILoc with// such a scenario: first, where one incoming edge has a VPHI with no known// value. This represents an edge where there was a PHI value that can't be// found in the register file -- we can't subsequently find a PHI here.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::VPHI);EXPECT_EQ(VLiveOuts[2].ID, ValueIDNum::EmptyValue);Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// However, if we know the value of the incoming VPHI, we can search for its// location. Use a PHI machine-value for doing this, as VPHIs should always// have PHI values, or they should have been eliminated.MOutLocs[2][0] = RspPHIInBlk2;VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::VPHI);VLiveOuts[2].ID = RspPHIInBlk2; // Set location where PHI happens.Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RspPHIInBlk3);}// If that value isn't available from that block, don't join.MOutLocs[2][0] = LiveInRsp;Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// Check that we don't pick values when the properties disagree, for example// different indirectness or DIExpression.DIExpression *NewExpr =DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);DbgValueProperties PropsWithExpr(NewExpr, false);VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithExpr, DbgValue::Def);Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);DbgValueProperties PropsWithIndirect(EmptyExpr, true);VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def);Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);}TEST_F(InstrRefLDVTest, pickVPHILocLoops) {setupSimpleLoop();// entry// |// |/-----\// loopblk |// |\-----/// |// retASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(3, 2);initValueArray(MOutLocs, 3, 2);unsigned EntryBlk = 0, LoopBlk = 1;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);ValueIDNum RaxPHIInBlk1(LoopBlk, 0, RaxLoc);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallVector<DbgValue, 32> VLiveOuts;VLiveOuts.resize(3, DbgValue(EmptyProps, DbgValue::Undef));InstrRefBasedLDV::LiveIdxT VLiveOutIdx;VLiveOutIdx[MBB0] = &VLiveOuts[0];VLiveOutIdx[MBB1] = &VLiveOuts[1];VLiveOutIdx[MBB2] = &VLiveOuts[2];SmallVector<const MachineBasicBlock *, 2> Preds;for (const auto *Pred : MBB1->predecessors())Preds.push_back(Pred);// Specify the live-outs around the joining block.MOutLocs[0][0] = LiveInRsp;MOutLocs[1][0] = LiveInRax;Optional<ValueIDNum> Result;// See that we can merge as normal on a backedge.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);// Should have picked a PHI in $rsp in block 1.EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RspPHIInBlk1);}// And that, if the desired values aren't available, we don't merge.MOutLocs[1][0] = LiveInRsp;Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// Test the backedge behaviour: PHIs that feed back into themselves can// carry this variables value. Feed in LiveInRsp in both $rsp and $rax// from the entry block, but only put an appropriate backedge PHI in $rax.// Only the $rax location can form the correct PHI.MOutLocs[0][0] = LiveInRsp;MOutLocs[0][1] = LiveInRsp;MOutLocs[1][0] = RaxPHIInBlk1;MOutLocs[1][1] = RaxPHIInBlk1;VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);// Crucially, a VPHI originating in this block:VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RaxPHIInBlk1);}// Merging should not be permitted if there's a usable PHI on the backedge,// but it's in the wrong place. (Overwrite $rax).MOutLocs[1][1] = LiveInRax;Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// Additionally, if the VPHI coming back on the loop backedge isn't from// this block (block 1), we can't merge it.MOutLocs[1][1] = RaxPHIInBlk1;VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(0, EmptyProps, DbgValue::VPHI);Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);}TEST_F(InstrRefLDVTest, pickVPHILocBadlyNestedLoops) {// Run some tests similar to pickVPHILocLoops, with more than one backedge,// and check that we merge correctly over many candidate locations.setupBadlyNestedLoops();// entry// |// loop1 -o// | ^// | ^// loop2 -o// | ^// | ^// loop3 -o// |// retASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);Register RBX = getRegByName("RBX");LocIdx RbxLoc = MTracker->lookupOrTrackRegister(RBX);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(5, 3);initValueArray(MOutLocs, 5, 3);unsigned EntryBlk = 0, Loop1Blk = 1;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum LiveInRbx(EntryBlk, 0, RbxLoc);ValueIDNum RspPHIInBlk1(Loop1Blk, 0, RspLoc);ValueIDNum RaxPHIInBlk1(Loop1Blk, 0, RaxLoc);ValueIDNum RbxPHIInBlk1(Loop1Blk, 0, RbxLoc);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallVector<DbgValue, 32> VLiveOuts;VLiveOuts.resize(5, DbgValue(EmptyProps, DbgValue::Undef));InstrRefBasedLDV::LiveIdxT VLiveOutIdx;VLiveOutIdx[MBB0] = &VLiveOuts[0];VLiveOutIdx[MBB1] = &VLiveOuts[1];VLiveOutIdx[MBB2] = &VLiveOuts[2];VLiveOutIdx[MBB3] = &VLiveOuts[3];VLiveOutIdx[MBB4] = &VLiveOuts[4];// We're going to focus on block 1.SmallVector<const MachineBasicBlock *, 2> Preds;for (const auto *Pred : MBB1->predecessors())Preds.push_back(Pred);// Specify the live-outs around the joining block. Incoming edges from the// entry block, self, and loop2.MOutLocs[0][0] = LiveInRsp;MOutLocs[1][0] = LiveInRax;MOutLocs[2][0] = LiveInRbx;Optional<ValueIDNum> Result;// See that we can merge as normal on a backedge.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRbx, EmptyProps, DbgValue::Def);Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);// Should have picked a PHI in $rsp in block 1.EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RspPHIInBlk1);}// Check too that permuting the live-out locations prevents mergingMOutLocs[0][0] = LiveInRax;MOutLocs[1][0] = LiveInRbx;MOutLocs[2][0] = LiveInRsp;Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);MOutLocs[0][0] = LiveInRsp;MOutLocs[1][0] = LiveInRax;MOutLocs[2][0] = LiveInRbx;// Feeding a PHI back on one backedge shouldn't merge (block 1 self backedge// wants LiveInRax).MOutLocs[1][0] = RspPHIInBlk1;Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// If the variables value on that edge is a VPHI feeding into itself, that's// fine.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);VLiveOuts[2] = DbgValue(LiveInRbx, EmptyProps, DbgValue::Def);Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RspPHIInBlk1);}// Likewise: the other backedge being a VPHI from block 1 should be accepted.MOutLocs[2][0] = RspPHIInBlk1;VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);VLiveOuts[2] = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RspPHIInBlk1);}// Here's where it becomes tricky: we should not merge if there are two// _distinct_ backedge PHIs. We can't have a PHI that happens in both rsp// and rax for example. We can only pick one location as the live-in.MOutLocs[2][0] = RaxPHIInBlk1;Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// The above test sources correct machine-PHI-value from two places. Now// try with one machine-PHI-value, but placed in two different locations// on the backedge. Again, we can't merge a location here, there's no// location that works on all paths.MOutLocs[0][0] = LiveInRsp;MOutLocs[1][0] = RspPHIInBlk1;MOutLocs[2][0] = LiveInRsp;MOutLocs[2][1] = RspPHIInBlk1;Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_FALSE(Result);// Scatter various PHI values across the available locations. Only rbx (loc 2)// has the right value in both backedges -- that's the loc that should be// picked.MOutLocs[0][2] = LiveInRsp;MOutLocs[1][0] = RspPHIInBlk1;MOutLocs[1][1] = RaxPHIInBlk1;MOutLocs[1][2] = RbxPHIInBlk1;MOutLocs[2][0] = LiveInRsp;MOutLocs[2][1] = RspPHIInBlk1;MOutLocs[2][2] = RbxPHIInBlk1;Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);EXPECT_TRUE(Result);if (Result) {EXPECT_EQ(*Result, RbxPHIInBlk1);}}TEST_F(InstrRefLDVTest, vlocJoinDiamond) {// entry// / \// br1 br2// \ /// retsetupDiamondBlocks();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);unsigned EntryBlk = 0, Br2Blk = 2, RetBlk = 3;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum RspPHIInBlkBr2Blk(Br2Blk, 0, RspLoc);ValueIDNum RspPHIInBlkRetBlk(RetBlk, 0, RspLoc);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallVector<DbgValue, 32> VLiveOuts;VLiveOuts.resize(4, DbgValue(EmptyProps, DbgValue::Undef));InstrRefBasedLDV::LiveIdxT VLiveOutIdx;VLiveOutIdx[MBB0] = &VLiveOuts[0];VLiveOutIdx[MBB1] = &VLiveOuts[1];VLiveOutIdx[MBB2] = &VLiveOuts[2];VLiveOutIdx[MBB3] = &VLiveOuts[3];SmallPtrSet<const MachineBasicBlock *, 8> AllBlocks;AllBlocks.insert(MBB0);AllBlocks.insert(MBB1);AllBlocks.insert(MBB2);AllBlocks.insert(MBB3);SmallVector<const MachineBasicBlock *, 2> Preds;for (const auto *Pred : MBB3->predecessors())Preds.push_back(Pred);SmallSet<DebugVariable, 4> AllVars;AllVars.insert(Var);// vlocJoin is here to propagate incoming values, and eliminate PHIs. Start// off by propagating a value into the merging block, number 3.DbgValue JoinedLoc = DbgValue(3, EmptyProps, DbgValue::NoVal);VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);bool Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result); // Output locs should have changed.EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);EXPECT_EQ(JoinedLoc.ID, LiveInRsp);// And if we did it a second time, leaving the live-ins as it was, then// we should report no change.Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);// If the live-in variable values are different, but there's no PHI placed// in this block, then just pick a location. It should be the first (in RPO)// predecessor to avoid being a backedge.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);JoinedLoc = DbgValue(3, EmptyProps, DbgValue::NoVal);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);// RPO is blocks 0 2 1 3, so LiveInRax is picked as the first predecessor// of this join.EXPECT_EQ(JoinedLoc.ID, LiveInRax);// No tests for whether vlocJoin will pass-through a variable with differing// expressions / properties. Those can only come about due to assignments; and// for any assignment at all, a PHI should have been placed at the dominance// frontier. We rely on the IDF calculator being accurate (which is OK,// because so does the rest of LLVM).// Try placing a PHI. With differing input values (LiveInRsp, LiveInRax),// this PHI should not be eliminated.JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);// Expect no change.EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);// This should not have been assigned a fixed value.EXPECT_EQ(JoinedLoc.ID, ValueIDNum::EmptyValue);EXPECT_EQ(JoinedLoc.BlockNo, 3);// Try a simple PHI elimination. Put a PHI in block 3, but LiveInRsp on both// incoming edges. Re-load in and out-locs with unrelated values; they're// irrelevant.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);EXPECT_EQ(JoinedLoc.ID, LiveInRsp);// If the "current" live-in is a VPHI, but not a VPHI generated in the current// block, then it's the remains of an earlier value propagation. We should// value propagate through this merge. Even if the current incoming values// disagree, because we've previously determined any VPHI here is redundant.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);JoinedLoc = DbgValue(2, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);EXPECT_EQ(JoinedLoc.ID, LiveInRax); // from block 2// The above test, but test that we will install one value-propagated VPHI// over another.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(0, EmptyProps, DbgValue::VPHI);JoinedLoc = DbgValue(2, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 0);// We shouldn't eliminate PHIs when properties disagree.DbgValueProperties PropsWithIndirect(EmptyExpr, true);VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def);JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 3);// Even if properties disagree, we should still value-propagate if there's no// PHI to be eliminated. The disagreeing values should work themselves out,// seeing how we've determined no PHI is necessary.VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def);JoinedLoc = DbgValue(2, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);EXPECT_EQ(JoinedLoc.ID, LiveInRsp);// Also check properties come from block 2, the first RPO predecessor to block// three.EXPECT_EQ(JoinedLoc.Properties, PropsWithIndirect);// Again, disagreeing properties, this time the expr, should cause a PHI to// not be eliminated.DIExpression *NewExpr =DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);DbgValueProperties PropsWithExpr(NewExpr, false);VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithExpr, DbgValue::Def);JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);}TEST_F(InstrRefLDVTest, vlocJoinLoops) {setupSimpleLoop();// entry// |// |/-----\// loopblk |// |\-----/// |// retASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);unsigned EntryBlk = 0, LoopBlk = 1;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallVector<DbgValue, 32> VLiveOuts;VLiveOuts.resize(3, DbgValue(EmptyProps, DbgValue::Undef));InstrRefBasedLDV::LiveIdxT VLiveOutIdx;VLiveOutIdx[MBB0] = &VLiveOuts[0];VLiveOutIdx[MBB1] = &VLiveOuts[1];VLiveOutIdx[MBB2] = &VLiveOuts[2];SmallPtrSet<const MachineBasicBlock *, 8> AllBlocks;AllBlocks.insert(MBB0);AllBlocks.insert(MBB1);AllBlocks.insert(MBB2);SmallVector<const MachineBasicBlock *, 2> Preds;for (const auto *Pred : MBB1->predecessors())Preds.push_back(Pred);SmallSet<DebugVariable, 4> AllVars;AllVars.insert(Var);// Test some back-edge-specific behaviours of vloc join. Mostly: the fact that// VPHIs that arrive on backedges can be eliminated, despite having different// values to the predecessor.// First: when there's no VPHI placed already, propagate the live-in value of// the first RPO predecessor.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);DbgValue JoinedLoc = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);bool Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);EXPECT_EQ(JoinedLoc.ID, LiveInRsp);// If there is a VPHI: don't elimiante it if there are disagreeing values.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 1);// If we feed this VPHI back into itself though, we can eliminate it.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);EXPECT_EQ(JoinedLoc.ID, LiveInRsp);// Don't eliminate backedge VPHIs if the predecessors have different// properties.DIExpression *NewExpr =DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);DbgValueProperties PropsWithExpr(NewExpr, false);VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(1, PropsWithExpr, DbgValue::VPHI);JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 1);// Backedges with VPHIs, but from the wrong block, shouldn't be eliminated.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(0, EmptyProps, DbgValue::VPHI);JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 1);}TEST_F(InstrRefLDVTest, vlocJoinBadlyNestedLoops) {// Test PHI elimination in the presence of multiple backedges.setupBadlyNestedLoops();// entry// |// loop1 -o// | ^// | ^// loop2 -o// | ^// | ^// loop3 -o// |// retASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);Register RBX = getRegByName("RBX");LocIdx RbxLoc = MTracker->lookupOrTrackRegister(RBX);unsigned EntryBlk = 0;ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);ValueIDNum LiveInRbx(EntryBlk, 0, RbxLoc);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallVector<DbgValue, 32> VLiveOuts;VLiveOuts.resize(5, DbgValue(EmptyProps, DbgValue::Undef));InstrRefBasedLDV::LiveIdxT VLiveOutIdx;VLiveOutIdx[MBB0] = &VLiveOuts[0];VLiveOutIdx[MBB1] = &VLiveOuts[1];VLiveOutIdx[MBB2] = &VLiveOuts[2];VLiveOutIdx[MBB3] = &VLiveOuts[3];VLiveOutIdx[MBB4] = &VLiveOuts[4];SmallPtrSet<const MachineBasicBlock *, 8> AllBlocks;AllBlocks.insert(MBB0);AllBlocks.insert(MBB1);AllBlocks.insert(MBB2);AllBlocks.insert(MBB3);AllBlocks.insert(MBB4);// We're going to focus on block 1.SmallVector<const MachineBasicBlock *, 3> Preds;for (const auto *Pred : MBB1->predecessors())Preds.push_back(Pred);SmallSet<DebugVariable, 4> AllVars;AllVars.insert(Var);// Test a normal VPHI isn't eliminated.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);VLiveOuts[2] = DbgValue(LiveInRbx, EmptyProps, DbgValue::Def);DbgValue JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);bool Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 1);// Common VPHIs on backedges should merge.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);VLiveOuts[2] = DbgValue(1, EmptyProps, DbgValue::VPHI);JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_TRUE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);EXPECT_EQ(JoinedLoc.ID, LiveInRsp);// They shouldn't merge if one of their properties is different.DbgValueProperties PropsWithIndirect(EmptyExpr, true);VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);VLiveOuts[2] = DbgValue(1, PropsWithIndirect, DbgValue::VPHI);JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 1);// VPHIs from different blocks should not merge.VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::VPHI);JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);EXPECT_FALSE(Result);EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);EXPECT_EQ(JoinedLoc.BlockNo, 1);}// Above are tests for picking VPHI locations, and eliminating VPHIs. No// unit-tests are written for evaluating the transfer function as that's// pretty straight forwards, or applying VPHI-location-picking to live-ins.// Instead, pre-set some machine locations and apply buildVLocValueMap to the// existing CFG patterns.TEST_F(InstrRefLDVTest, VLocSingleBlock) {setupSingleBlock();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(1, 2);ValueIDNum LiveInRsp = ValueIDNum(0, 0, RspLoc);MInLocs[0][0] = MOutLocs[0][0] = LiveInRsp;DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallSet<DebugVariable, 4> AllVars;AllVars.insert(Var);// Mild hack: rather than constructing machine instructions in each block// and creating lexical scopes across them, instead just tell// buildVLocValueMap that there's an assignment in every block. That makes// every block in scope.SmallPtrSet<MachineBasicBlock *, 4> AssignBlocks;AssignBlocks.insert(MBB0);SmallVector<VLocTracker, 1> VLocs;VLocs.resize(1, VLocTracker(Overlaps, EmptyExpr));InstrRefBasedLDV::LiveInsT Output;// Test that, with no assignments at all, no mappings are created for the// variable in this function.buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output.size(), 0ul);// If we put an assignment in the transfer function, that should... well,// do nothing, because we don't store the live-outs.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output.size(), 0ul);// There is pretty much nothing else of interest to test with a single block.// It's not relevant to the SSA-construction parts of variable values.}TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {setupDiamondBlocks();// entry// / \// br1 br2// \ /// retASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);unsigned EntryBlk = 0, RetBlk = 3;ValueIDNum LiveInRsp = ValueIDNum(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax = ValueIDNum(EntryBlk, 0, RaxLoc);ValueIDNum RspPHIInBlk3 = ValueIDNum(RetBlk, 0, RspLoc);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(4, 2);initValueArray(MInLocs, 4, 2);initValueArray(MOutLocs, 4, 2);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallSet<DebugVariable, 4> AllVars;AllVars.insert(Var);// Mild hack: rather than constructing machine instructions in each block// and creating lexical scopes across them, instead just tell// buildVLocValueMap that there's an assignment in every block. That makes// every block in scope.SmallPtrSet<MachineBasicBlock *, 4> AssignBlocks;AssignBlocks.insert(MBB0);AssignBlocks.insert(MBB1);AssignBlocks.insert(MBB2);AssignBlocks.insert(MBB3);SmallVector<VLocTracker, 1> VLocs;VLocs.resize(4, VLocTracker(Overlaps, EmptyExpr));InstrRefBasedLDV::LiveInsT Output;// Start off with LiveInRsp in every location.for (unsigned int I = 0; I < 4; ++I) {MInLocs[I][0] = MInLocs[I][1] = LiveInRsp;MOutLocs[I][0] = MOutLocs[I][1] = LiveInRsp;}auto ClearOutputs = [&]() {for (auto &Elem : Output)Elem.clear();};Output.resize(4);// No assignments -> no values.buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);EXPECT_EQ(Output[3].size(), 0ul);// An assignment in the end block should also not affect other blocks; or// produce any live-ins.VLocs[3].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);EXPECT_EQ(Output[3].size(), 0ul);ClearOutputs();// Assignments in either of the side-of-diamond blocks should also not be// propagated anywhere.VLocs[3].Vars.clear();VLocs[2].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);EXPECT_EQ(Output[3].size(), 0ul);VLocs[2].Vars.clear();ClearOutputs();VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);EXPECT_EQ(Output[3].size(), 0ul);VLocs[1].Vars.clear();ClearOutputs();// However: putting an assignment in the first block should propagate variable// values through to all other blocks, as it dominates.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);ASSERT_EQ(Output[3].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();// Additionally, even if that value isn't available in the register file, it// should still be propagated, as buildVLocValueMap shouldn't care about// what's in the registers (except for PHIs).// values through to all other blocks, as it dominates.VLocs[0].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);ASSERT_EQ(Output[3].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRax);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRax);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRax);ClearOutputs();VLocs[0].Vars.clear();// We should get a live-in to the merging block, if there are two assigns of// the same value in either side of the diamond.VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[2].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);ASSERT_EQ(Output[3].size(), 1ul);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);ClearOutputs();VLocs[1].Vars.clear();VLocs[2].Vars.clear();// If we assign a value in the entry block, then 'undef' on a branch, we// shouldn't have a live-in in the merge block.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(EmptyProps, DbgValue::Undef)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[3].size(), 0ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// Having different values joining into the merge block should mean we have// no live-in in that block. Block ones LiveInRax value doesn't appear as a// live-in anywhere, it's block internal.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[3].size(), 0ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// But on the other hand, if there's a location in the register file where// those two values can be joined, do so.MOutLocs[1][0] = LiveInRax;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);ASSERT_EQ(Output[3].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, RspPHIInBlk3);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();}TEST_F(InstrRefLDVTest, VLocSimpleLoop) {setupSimpleLoop();// entry// |// |/-----\// loopblk |// |\-----/// |// retASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);unsigned EntryBlk = 0, LoopBlk = 1;ValueIDNum LiveInRsp = ValueIDNum(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax = ValueIDNum(EntryBlk, 0, RaxLoc);ValueIDNum RspPHIInBlk1 = ValueIDNum(LoopBlk, 0, RspLoc);ValueIDNum RspDefInBlk1 = ValueIDNum(LoopBlk, 1, RspLoc);ValueIDNum RaxPHIInBlk1 = ValueIDNum(LoopBlk, 0, RaxLoc);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(3, 2);initValueArray(MInLocs, 3, 2);initValueArray(MOutLocs, 3, 2);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallSet<DebugVariable, 4> AllVars;AllVars.insert(Var);SmallPtrSet<MachineBasicBlock *, 4> AssignBlocks;AssignBlocks.insert(MBB0);AssignBlocks.insert(MBB1);AssignBlocks.insert(MBB2);SmallVector<VLocTracker, 3> VLocs;VLocs.resize(3, VLocTracker(Overlaps, EmptyExpr));InstrRefBasedLDV::LiveInsT Output;// Start off with LiveInRsp in every location.for (unsigned int I = 0; I < 3; ++I) {MInLocs[I][0] = MInLocs[I][1] = LiveInRsp;MOutLocs[I][0] = MOutLocs[I][1] = LiveInRsp;}auto ClearOutputs = [&]() {for (auto &Elem : Output)Elem.clear();};Output.resize(3);// Easy starter: a dominating assign should propagate to all blocks.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// Put an undef assignment in the loop. Should get no live-in value.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(EmptyProps, DbgValue::Undef)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// Assignment of the same value should naturally join.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// Assignment of different values shouldn't join with no machine PHI vals.// Will be live-in to exit block as it's dominated.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRax);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// Install a completely unrelated PHI value, that we should not join on. Try// with unrelated assign in loop block again.MInLocs[1][0] = RspPHIInBlk1;MOutLocs[1][0] = RspDefInBlk1;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRax);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// Now, if we assign RspDefInBlk1 in the loop block, we should be able to// find the appropriate PHI.MInLocs[1][0] = RspPHIInBlk1;MOutLocs[1][0] = RspDefInBlk1;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// If the PHI happens in a different location, the live-in should happen// there.MInLocs[1][0] = LiveInRsp;MOutLocs[1][0] = LiveInRsp;MInLocs[1][1] = RaxPHIInBlk1;MOutLocs[1][1] = RspDefInBlk1;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, RaxPHIInBlk1);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// The PHI happening in both places should be handled too. Exactly where// isn't important, but if the location picked changes, this test will let// you know.MInLocs[1][0] = RaxPHIInBlk1;MOutLocs[1][0] = RspDefInBlk1;MInLocs[1][1] = RaxPHIInBlk1;MOutLocs[1][1] = RspDefInBlk1;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);// Today, the first register is picked.EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// If the loop block looked a bit like this:// %0 = PHI %1, %2// [...]// DBG_VALUE %0// Then with instr-ref it becomes:// DBG_PHI %0// [...]// DBG_INSTR_REF// And we would be feeding a machine PHI-value back around the loop. However:// this does not mean we can eliminate the variable value PHI and use the// variable value from the entry block: they are distinct values that must be// joined at some location by the control flow.// [This test input would never occur naturally, the machine-PHI would be// eliminated]MInLocs[1][0] = RspPHIInBlk1;MOutLocs[1][0] = RspPHIInBlk1;MInLocs[1][1] = LiveInRax;MOutLocs[1][1] = LiveInRax;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(RspPHIInBlk1, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, RspPHIInBlk1);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// Test that we can eliminate PHIs. A PHI will be placed at the loop head// because there's a def in in.MInLocs[1][0] = LiveInRsp;MOutLocs[1][0] = LiveInRsp;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();}// test phi elimination with the nested situationTEST_F(InstrRefLDVTest, VLocNestedLoop) {// entry// |// loop1// ^\// | \ /-\// | loop2 |// | / \-/// ^ /// join// |// retsetupNestedLoops();ASSERT_TRUE(MTracker->getNumLocs() == 1);LocIdx RspLoc(0);Register RAX = getRegByName("RAX");LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);unsigned EntryBlk = 0, Loop1Blk = 1, Loop2Blk = 2;ValueIDNum LiveInRsp = ValueIDNum(EntryBlk, 0, RspLoc);ValueIDNum LiveInRax = ValueIDNum(EntryBlk, 0, RaxLoc);ValueIDNum RspPHIInBlk1 = ValueIDNum(Loop1Blk, 0, RspLoc);ValueIDNum RspPHIInBlk2 = ValueIDNum(Loop2Blk, 0, RspLoc);ValueIDNum RspDefInBlk2 = ValueIDNum(Loop2Blk, 1, RspLoc);FuncValueTable MInLocs, MOutLocs;std::tie(MInLocs, MOutLocs) = allocValueTables(5, 2);initValueArray(MInLocs, 5, 2);initValueArray(MOutLocs, 5, 2);DebugVariable Var(FuncVariable, None, nullptr);DbgValueProperties EmptyProps(EmptyExpr, false);SmallSet<DebugVariable, 4> AllVars;AllVars.insert(Var);SmallPtrSet<MachineBasicBlock *, 5> AssignBlocks;AssignBlocks.insert(MBB0);AssignBlocks.insert(MBB1);AssignBlocks.insert(MBB2);AssignBlocks.insert(MBB3);AssignBlocks.insert(MBB4);SmallVector<VLocTracker, 5> VLocs;VLocs.resize(5, VLocTracker(Overlaps, EmptyExpr));InstrRefBasedLDV::LiveInsT Output;// Start off with LiveInRsp in every location.for (unsigned int I = 0; I < 5; ++I) {MInLocs[I][0] = MInLocs[I][1] = LiveInRsp;MOutLocs[I][0] = MOutLocs[I][1] = LiveInRsp;}auto ClearOutputs = [&]() {for (auto &Elem : Output)Elem.clear();};Output.resize(5);// A dominating assign should propagate to all blocks.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);ASSERT_EQ(Output[3].size(), 1ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();// Test that an assign in the inner loop causes unresolved PHIs at the heads// of both loops, and no output location. Dominated blocks do get values.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[2].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);ASSERT_EQ(Output[3].size(), 1ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRax);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, LiveInRax);ClearOutputs();VLocs[0].Vars.clear();VLocs[2].Vars.clear();// Same test, but with no assignment in block 0. We should still get values// in dominated blocks.VLocs[2].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);ASSERT_EQ(Output[3].size(), 1ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRax);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, LiveInRax);ClearOutputs();VLocs[2].Vars.clear();// Similarly, assignments in the outer loop gives location to dominated// blocks, but no PHI locations are found at the outer loop head.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[3].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);EXPECT_EQ(Output[3].size(), 0ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, LiveInRax);ClearOutputs();VLocs[0].Vars.clear();VLocs[3].Vars.clear();VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);ASSERT_EQ(Output[2].size(), 1ul);ASSERT_EQ(Output[3].size(), 1ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRax);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRax);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, LiveInRax);ClearOutputs();VLocs[0].Vars.clear();VLocs[1].Vars.clear();// With an assignment of the same value in the inner loop, we should work out// that all PHIs can be eliminated and the same value is live-through the// whole function.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[2].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 1ul);EXPECT_EQ(Output[2].size(), 1ul);ASSERT_EQ(Output[3].size(), 1ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, LiveInRsp);ClearOutputs();VLocs[0].Vars.clear();VLocs[2].Vars.clear();// If we have an assignment in the inner loop, and a PHI for it at the inner// loop head, we could find a live-in location for the inner loop. But because// the outer loop has no PHI, we can't find a variable value for outer loop// head, so can't have a live-in value for the inner loop head.MInLocs[2][0] = RspPHIInBlk2;MOutLocs[2][0] = LiveInRax;// NB: all other machine locations are LiveInRsp, disallowing a PHI in block// one. Even though RspPHIInBlk2 isn't available later in the function, we// should still produce a live-in value. The fact it's unavailable is a// different concern.VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[2].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);EXPECT_EQ(Output[1].size(), 0ul);EXPECT_EQ(Output[2].size(), 0ul);ASSERT_EQ(Output[3].size(), 1ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, LiveInRax);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, LiveInRax);ClearOutputs();VLocs[0].Vars.clear();VLocs[2].Vars.clear();// Have an assignment in inner loop that can have a PHI resolved; and add a// machine value PHI to the outer loop head, so that we can find a location// all the way through the function.MInLocs[1][0] = RspPHIInBlk1;MOutLocs[1][0] = RspPHIInBlk1;MInLocs[2][0] = RspPHIInBlk2;MOutLocs[2][0] = RspDefInBlk2;MInLocs[3][0] = RspDefInBlk2;MOutLocs[3][0] = RspDefInBlk2;VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});VLocs[2].Vars.insert({Var, DbgValue(RspDefInBlk2, EmptyProps, DbgValue::Def)});buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,MOutLocs, MInLocs, VLocs);EXPECT_EQ(Output[0].size(), 0ul);ASSERT_EQ(Output[1].size(), 1ul);ASSERT_EQ(Output[2].size(), 1ul);ASSERT_EQ(Output[3].size(), 1ul);ASSERT_EQ(Output[4].size(), 1ul);EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[2][0].second.ID, RspPHIInBlk2);EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[3][0].second.ID, RspDefInBlk2);EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);EXPECT_EQ(Output[4][0].second.ID, RspDefInBlk2);ClearOutputs();VLocs[0].Vars.clear();VLocs[2].Vars.clear();}
//===- PatternMatchTest.cpp -----------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"#include "llvm/CodeGen/GlobalISel/Utils.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetFrameLowering.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;using namespace MIPatternMatch;namespace {TEST_F(AArch64GISelMITest, MatchIntConstant) {setUp();if (!TM)return;auto MIBCst = B.buildConstant(LLT::scalar(64), 42);int64_t Cst;bool match = mi_match(MIBCst.getReg(0), *MRI, m_ICst(Cst));EXPECT_TRUE(match);EXPECT_EQ(Cst, 42);}TEST_F(AArch64GISelMITest, MatchIntConstantRegister) {setUp();if (!TM)return;auto MIBCst = B.buildConstant(LLT::scalar(64), 42);Optional<ValueAndVReg> Src0;bool match = mi_match(MIBCst.getReg(0), *MRI, m_GCst(Src0));EXPECT_TRUE(match);EXPECT_EQ(Src0->VReg, MIBCst.getReg(0));}TEST_F(AArch64GISelMITest, MatchIntConstantSplat) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);LLT v4s64 = LLT::fixed_vector(4, s64);MachineInstrBuilder FortyTwoSplat =B.buildSplatVector(v4s64, B.buildConstant(s64, 42));int64_t Cst;EXPECT_TRUE(mi_match(FortyTwoSplat.getReg(0), *MRI, m_ICstOrSplat(Cst)));EXPECT_EQ(Cst, 42);MachineInstrBuilder NonConstantSplat =B.buildBuildVector(v4s64, {Copies[0], Copies[0], Copies[0], Copies[0]});EXPECT_FALSE(mi_match(NonConstantSplat.getReg(0), *MRI, m_ICstOrSplat(Cst)));}TEST_F(AArch64GISelMITest, MachineInstrPtrBind) {setUp();if (!TM)return;auto MIBAdd = B.buildAdd(LLT::scalar(64), Copies[0], Copies[1]);// Test 'MachineInstr *' bind.// Default mi_match.MachineInstr *MIPtr = MIBAdd.getInstr();bool match = mi_match(MIPtr, *MRI, m_GAdd(m_Reg(), m_Reg()));EXPECT_TRUE(match);// Specialized mi_match for MachineInstr &.MachineInstr &MI = *MIBAdd.getInstr();match = mi_match(MI, *MRI, m_GAdd(m_Reg(), m_Reg()));EXPECT_TRUE(match);// MachineInstrBuilder has automatic conversion to MachineInstr *.match = mi_match(MIBAdd, *MRI, m_GAdd(m_Reg(), m_Reg()));EXPECT_TRUE(match);// Match instruction without def.auto MIBBrcond = B.buildBrCond(Copies[0], B.getMBB());MachineInstr *MatchedMI;match = mi_match(MIBBrcond, *MRI, m_MInstr(MatchedMI));EXPECT_TRUE(match);EXPECT_TRUE(MIBBrcond.getInstr() == MatchedMI);// Match instruction with two defs.auto MIBUAddO =B.buildUAddo(LLT::scalar(64), LLT::scalar(1), Copies[0], Copies[1]);match = mi_match(MIBUAddO, *MRI, m_MInstr(MatchedMI));EXPECT_TRUE(match);EXPECT_TRUE(MIBUAddO.getInstr() == MatchedMI);}TEST_F(AArch64GISelMITest, MatchBinaryOp) {setUp();if (!TM)return;LLT s32 = LLT::scalar(32);LLT s64 = LLT::scalar(64);LLT p0 = LLT::pointer(0, 64);auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);// Test case for no bind.bool match =mi_match(MIBAdd.getReg(0), *MRI, m_GAdd(m_Reg(), m_Reg()));EXPECT_TRUE(match);Register Src0, Src1, Src2;match = mi_match(MIBAdd.getReg(0), *MRI,m_GAdd(m_Reg(Src0), m_Reg(Src1)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, Copies[1]);// Build MUL(ADD %0, %1), %2auto MIBMul = B.buildMul(s64, MIBAdd, Copies[2]);// Try to match MUL.match = mi_match(MIBMul.getReg(0), *MRI,m_GMul(m_Reg(Src0), m_Reg(Src1)));EXPECT_TRUE(match);EXPECT_EQ(Src0, MIBAdd.getReg(0));EXPECT_EQ(Src1, Copies[2]);// Try to match MUL(ADD)match = mi_match(MIBMul.getReg(0), *MRI,m_GMul(m_GAdd(m_Reg(Src0), m_Reg(Src1)), m_Reg(Src2)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, Copies[1]);EXPECT_EQ(Src2, Copies[2]);// Test Commutativity.auto MIBMul2 = B.buildMul(s64, Copies[0], B.buildConstant(s64, 42));// Try to match MUL(Cst, Reg) on src of MUL(Reg, Cst) to validate// commutativity.int64_t Cst;match = mi_match(MIBMul2.getReg(0), *MRI,m_GMul(m_ICst(Cst), m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Cst, 42);EXPECT_EQ(Src0, Copies[0]);// Make sure commutative doesn't work with something like SUB.auto MIBSub = B.buildSub(s64, Copies[0], B.buildConstant(s64, 42));match = mi_match(MIBSub.getReg(0), *MRI,m_GSub(m_ICst(Cst), m_Reg(Src0)));EXPECT_FALSE(match);auto MIBFMul = B.buildInstr(TargetOpcode::G_FMUL, {s64},{Copies[0], B.buildConstant(s64, 42)});// Match and test commutativity for FMUL.match = mi_match(MIBFMul.getReg(0), *MRI,m_GFMul(m_ICst(Cst), m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Cst, 42);EXPECT_EQ(Src0, Copies[0]);// FSUBauto MIBFSub = B.buildInstr(TargetOpcode::G_FSUB, {s64},{Copies[0], B.buildConstant(s64, 42)});match = mi_match(MIBFSub.getReg(0), *MRI,m_GFSub(m_Reg(Src0), m_Reg()));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);// Build AND %0, %1auto MIBAnd = B.buildAnd(s64, Copies[0], Copies[1]);// Try to match AND.match = mi_match(MIBAnd.getReg(0), *MRI,m_GAnd(m_Reg(Src0), m_Reg(Src1)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, Copies[1]);// Build OR %0, %1auto MIBOr = B.buildOr(s64, Copies[0], Copies[1]);// Try to match OR.match = mi_match(MIBOr.getReg(0), *MRI,m_GOr(m_Reg(Src0), m_Reg(Src1)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, Copies[1]);// Match lshr, and make sure a different shift amount type works.auto TruncCopy1 = B.buildTrunc(s32, Copies[1]);auto LShr = B.buildLShr(s64, Copies[0], TruncCopy1);match = mi_match(LShr.getReg(0), *MRI,m_GLShr(m_Reg(Src0), m_Reg(Src1)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, TruncCopy1.getReg(0));// Match shl, and make sure a different shift amount type works.auto Shl = B.buildShl(s64, Copies[0], TruncCopy1);match = mi_match(Shl.getReg(0), *MRI,m_GShl(m_Reg(Src0), m_Reg(Src1)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, TruncCopy1.getReg(0));// Build a G_PTR_ADD and check that we can match it.auto PtrAdd = B.buildPtrAdd(p0, {B.buildUndef(p0)}, Copies[0]);match = mi_match(PtrAdd.getReg(0), *MRI, m_GPtrAdd(m_Reg(Src0), m_Reg(Src1)));EXPECT_TRUE(match);EXPECT_EQ(Src0, PtrAdd->getOperand(1).getReg());EXPECT_EQ(Src1, Copies[0]);auto MIBCst = B.buildConstant(s64, 42);auto MIBAddCst = B.buildAdd(s64, MIBCst, Copies[0]);auto MIBUnmerge = B.buildUnmerge({s32, s32}, B.buildConstant(s64, 42));// m_BinOp with opcode.// Match binary instruction, opcode and its non-commutative operands.match = mi_match(MIBAddCst, *MRI,m_BinOp(TargetOpcode::G_ADD, m_ICst(Cst), m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Cst, 42);// Opcode doesn't match.match = mi_match(MIBAddCst, *MRI,m_BinOp(TargetOpcode::G_MUL, m_ICst(Cst), m_Reg(Src0)));EXPECT_FALSE(match);match = mi_match(MIBAddCst, *MRI,m_BinOp(TargetOpcode::G_ADD, m_Reg(Src0), m_ICst(Cst)));EXPECT_FALSE(match);// Instruction is not binary.match = mi_match(MIBCst, *MRI,m_BinOp(TargetOpcode::G_MUL, m_Reg(Src0), m_Reg(Src1)));EXPECT_FALSE(match);match = mi_match(MIBUnmerge, *MRI,m_BinOp(TargetOpcode::G_MUL, m_Reg(Src0), m_Reg(Src1)));EXPECT_FALSE(match);// m_CommutativeBinOp with opcode.match = mi_match(MIBAddCst, *MRI,m_CommutativeBinOp(TargetOpcode::G_ADD, m_ICst(Cst), m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Cst, 42);match = mi_match(MIBAddCst, *MRI,m_CommutativeBinOp(TargetOpcode::G_MUL, m_ICst(Cst), m_Reg(Src0)));EXPECT_FALSE(match);match = mi_match(MIBAddCst, *MRI,m_CommutativeBinOp(TargetOpcode::G_ADD, m_Reg(Src0), m_ICst(Cst)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Cst, 42);match = mi_match(MIBCst, *MRI,m_CommutativeBinOp(TargetOpcode::G_MUL, m_Reg(Src0), m_Reg(Src1)));EXPECT_FALSE(match);match = mi_match(MIBUnmerge, *MRI,m_CommutativeBinOp(TargetOpcode::G_MUL, m_Reg(Src0), m_Reg(Src1)));EXPECT_FALSE(match);}TEST_F(AArch64GISelMITest, MatchICmp) {setUp();if (!TM)return;const LLT s1 = LLT::scalar(1);auto CmpEq = B.buildICmp(CmpInst::ICMP_EQ, s1, Copies[0], Copies[1]);// Check match any predicate.bool match =mi_match(CmpEq.getReg(0), *MRI, m_GICmp(m_Pred(), m_Reg(), m_Reg()));EXPECT_TRUE(match);// Check we get the predicate and registers.CmpInst::Predicate Pred;Register Reg0;Register Reg1;match = mi_match(CmpEq.getReg(0), *MRI,m_GICmp(m_Pred(Pred), m_Reg(Reg0), m_Reg(Reg1)));EXPECT_TRUE(match);EXPECT_EQ(CmpInst::ICMP_EQ, Pred);EXPECT_EQ(Copies[0], Reg0);EXPECT_EQ(Copies[1], Reg1);}TEST_F(AArch64GISelMITest, MatchFCmp) {setUp();if (!TM)return;const LLT s1 = LLT::scalar(1);auto CmpEq = B.buildFCmp(CmpInst::FCMP_OEQ, s1, Copies[0], Copies[1]);// Check match any predicate.bool match =mi_match(CmpEq.getReg(0), *MRI, m_GFCmp(m_Pred(), m_Reg(), m_Reg()));EXPECT_TRUE(match);// Check we get the predicate and registers.CmpInst::Predicate Pred;Register Reg0;Register Reg1;match = mi_match(CmpEq.getReg(0), *MRI,m_GFCmp(m_Pred(Pred), m_Reg(Reg0), m_Reg(Reg1)));EXPECT_TRUE(match);EXPECT_EQ(CmpInst::FCMP_OEQ, Pred);EXPECT_EQ(Copies[0], Reg0);EXPECT_EQ(Copies[1], Reg1);}TEST_F(AArch64GISelMITest, MatchFPUnaryOp) {setUp();if (!TM)return;// Truncate s64 to s32.LLT s32 = LLT::scalar(32);auto Copy0s32 = B.buildFPTrunc(s32, Copies[0]);// Match G_FABS.auto MIBFabs = B.buildInstr(TargetOpcode::G_FABS, {s32}, {Copy0s32});bool match =mi_match(MIBFabs.getReg(0), *MRI, m_GFabs(m_Reg()));EXPECT_TRUE(match);Register Src;auto MIBFNeg = B.buildInstr(TargetOpcode::G_FNEG, {s32}, {Copy0s32});match = mi_match(MIBFNeg.getReg(0), *MRI, m_GFNeg(m_Reg(Src)));EXPECT_TRUE(match);EXPECT_EQ(Src, Copy0s32.getReg(0));match = mi_match(MIBFabs.getReg(0), *MRI, m_GFabs(m_Reg(Src)));EXPECT_TRUE(match);EXPECT_EQ(Src, Copy0s32.getReg(0));// Build and match FConstant.auto MIBFCst = B.buildFConstant(s32, .5);const ConstantFP *TmpFP{};match = mi_match(MIBFCst.getReg(0), *MRI, m_GFCst(TmpFP));EXPECT_TRUE(match);EXPECT_TRUE(TmpFP);APFloat APF((float).5);auto *CFP = ConstantFP::get(Context, APF);EXPECT_EQ(CFP, TmpFP);// Build double float.LLT s64 = LLT::scalar(64);auto MIBFCst64 = B.buildFConstant(s64, .5);const ConstantFP *TmpFP64{};match = mi_match(MIBFCst64.getReg(0), *MRI, m_GFCst(TmpFP64));EXPECT_TRUE(match);EXPECT_TRUE(TmpFP64);APFloat APF64(.5);auto CFP64 = ConstantFP::get(Context, APF64);EXPECT_EQ(CFP64, TmpFP64);EXPECT_NE(TmpFP64, TmpFP);// Build half float.LLT s16 = LLT::scalar(16);auto MIBFCst16 = B.buildFConstant(s16, .5);const ConstantFP *TmpFP16{};match = mi_match(MIBFCst16.getReg(0), *MRI, m_GFCst(TmpFP16));EXPECT_TRUE(match);EXPECT_TRUE(TmpFP16);bool Ignored;APFloat APF16(.5);APF16.convert(APFloat::IEEEhalf(), APFloat::rmNearestTiesToEven, &Ignored);auto CFP16 = ConstantFP::get(Context, APF16);EXPECT_EQ(TmpFP16, CFP16);EXPECT_NE(TmpFP16, TmpFP);}TEST_F(AArch64GISelMITest, MatchExtendsTrunc) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);LLT s32 = LLT::scalar(32);auto MIBTrunc = B.buildTrunc(s32, Copies[0]);auto MIBAExt = B.buildAnyExt(s64, MIBTrunc);auto MIBZExt = B.buildZExt(s64, MIBTrunc);auto MIBSExt = B.buildSExt(s64, MIBTrunc);Register Src0;bool match =mi_match(MIBTrunc.getReg(0), *MRI, m_GTrunc(m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);match =mi_match(MIBAExt.getReg(0), *MRI, m_GAnyExt(m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Src0, MIBTrunc.getReg(0));match = mi_match(MIBSExt.getReg(0), *MRI, m_GSExt(m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Src0, MIBTrunc.getReg(0));match = mi_match(MIBZExt.getReg(0), *MRI, m_GZExt(m_Reg(Src0)));EXPECT_TRUE(match);EXPECT_EQ(Src0, MIBTrunc.getReg(0));// Match ext(trunc src)match = mi_match(MIBAExt.getReg(0), *MRI,m_GAnyExt(m_GTrunc(m_Reg(Src0))));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);match = mi_match(MIBSExt.getReg(0), *MRI,m_GSExt(m_GTrunc(m_Reg(Src0))));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);match = mi_match(MIBZExt.getReg(0), *MRI,m_GZExt(m_GTrunc(m_Reg(Src0))));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);}TEST_F(AArch64GISelMITest, MatchSpecificType) {setUp();if (!TM)return;// Try to match a 64bit add.LLT s64 = LLT::scalar(64);LLT s32 = LLT::scalar(32);auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);EXPECT_FALSE(mi_match(MIBAdd.getReg(0), *MRI,m_GAdd(m_SpecificType(s32), m_Reg())));EXPECT_TRUE(mi_match(MIBAdd.getReg(0), *MRI,m_GAdd(m_SpecificType(s64), m_Reg())));// Try to match the destination type of a bitcast.LLT v2s32 = LLT::fixed_vector(2, 32);auto MIBCast = B.buildCast(v2s32, Copies[0]);EXPECT_TRUE(mi_match(MIBCast.getReg(0), *MRI, m_GBitcast(m_Reg())));EXPECT_TRUE(mi_match(MIBCast.getReg(0), *MRI, m_SpecificType(v2s32)));EXPECT_TRUE(mi_match(MIBCast.getReg(1), *MRI, m_SpecificType(s64)));// Build a PTRToInt and INTTOPTR and match and test them.LLT PtrTy = LLT::pointer(0, 64);auto MIBIntToPtr = B.buildCast(PtrTy, Copies[0]);auto MIBPtrToInt = B.buildCast(s64, MIBIntToPtr);Register Src0;// match the ptrtoint(inttoptr reg)bool match = mi_match(MIBPtrToInt.getReg(0), *MRI,m_GPtrToInt(m_GIntToPtr(m_Reg(Src0))));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);}TEST_F(AArch64GISelMITest, MatchCombinators) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);LLT s32 = LLT::scalar(32);auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);Register Src0, Src1;bool match =mi_match(MIBAdd.getReg(0), *MRI,m_all_of(m_SpecificType(s64), m_GAdd(m_Reg(Src0), m_Reg(Src1))));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, Copies[1]);// Check for s32 (which should fail).match =mi_match(MIBAdd.getReg(0), *MRI,m_all_of(m_SpecificType(s32), m_GAdd(m_Reg(Src0), m_Reg(Src1))));EXPECT_FALSE(match);match =mi_match(MIBAdd.getReg(0), *MRI,m_any_of(m_SpecificType(s32), m_GAdd(m_Reg(Src0), m_Reg(Src1))));EXPECT_TRUE(match);EXPECT_EQ(Src0, Copies[0]);EXPECT_EQ(Src1, Copies[1]);// Match a case where none of the predicates hold true.match = mi_match(MIBAdd.getReg(0), *MRI,m_any_of(m_SpecificType(LLT::scalar(16)), m_GSub(m_Reg(), m_Reg())));EXPECT_FALSE(match);}TEST_F(AArch64GISelMITest, MatchMiscellaneous) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);Register Reg = MIBAdd.getReg(0);// Only one use of Reg.B.buildCast(LLT::pointer(0, 32), MIBAdd);EXPECT_TRUE(mi_match(Reg, *MRI, m_OneUse(m_GAdd(m_Reg(), m_Reg()))));EXPECT_TRUE(mi_match(Reg, *MRI, m_OneNonDBGUse(m_GAdd(m_Reg(), m_Reg()))));// Add multiple debug uses of Reg.B.buildInstr(TargetOpcode::DBG_VALUE, {}, {Reg});B.buildInstr(TargetOpcode::DBG_VALUE, {}, {Reg});EXPECT_FALSE(mi_match(Reg, *MRI, m_OneUse(m_GAdd(m_Reg(), m_Reg()))));EXPECT_TRUE(mi_match(Reg, *MRI, m_OneNonDBGUse(m_GAdd(m_Reg(), m_Reg()))));// Multiple non-debug uses of Reg.B.buildCast(LLT::pointer(1, 32), MIBAdd);EXPECT_FALSE(mi_match(Reg, *MRI, m_OneUse(m_GAdd(m_Reg(), m_Reg()))));EXPECT_FALSE(mi_match(Reg, *MRI, m_OneNonDBGUse(m_GAdd(m_Reg(), m_Reg()))));}TEST_F(AArch64GISelMITest, MatchSpecificConstant) {setUp();if (!TM)return;// Basic case: Can we match a G_CONSTANT with a specific value?auto FortyTwo = B.buildConstant(LLT::scalar(64), 42);EXPECT_TRUE(mi_match(FortyTwo.getReg(0), *MRI, m_SpecificICst(42)));EXPECT_FALSE(mi_match(FortyTwo.getReg(0), *MRI, m_SpecificICst(123)));// Test that this works inside of a more complex pattern.LLT s64 = LLT::scalar(64);auto MIBAdd = B.buildAdd(s64, Copies[0], FortyTwo);EXPECT_TRUE(mi_match(MIBAdd.getReg(2), *MRI, m_SpecificICst(42)));// Wrong constant.EXPECT_FALSE(mi_match(MIBAdd.getReg(2), *MRI, m_SpecificICst(123)));// No constant on the LHS.EXPECT_FALSE(mi_match(MIBAdd.getReg(1), *MRI, m_SpecificICst(42)));}TEST_F(AArch64GISelMITest, MatchSpecificConstantSplat) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);LLT v4s64 = LLT::fixed_vector(4, s64);MachineInstrBuilder FortyTwoSplat =B.buildSplatVector(v4s64, B.buildConstant(s64, 42));MachineInstrBuilder FortyTwo = B.buildConstant(s64, 42);EXPECT_TRUE(mi_match(FortyTwoSplat.getReg(0), *MRI, m_SpecificICstSplat(42)));EXPECT_FALSE(mi_match(FortyTwoSplat.getReg(0), *MRI, m_SpecificICstSplat(43)));EXPECT_FALSE(mi_match(FortyTwo.getReg(0), *MRI, m_SpecificICstSplat(42)));MachineInstrBuilder NonConstantSplat =B.buildBuildVector(v4s64, {Copies[0], Copies[0], Copies[0], Copies[0]});MachineInstrBuilder AddSplat =B.buildAdd(v4s64, NonConstantSplat, FortyTwoSplat);EXPECT_TRUE(mi_match(AddSplat.getReg(2), *MRI, m_SpecificICstSplat(42)));EXPECT_FALSE(mi_match(AddSplat.getReg(2), *MRI, m_SpecificICstSplat(43)));EXPECT_FALSE(mi_match(AddSplat.getReg(1), *MRI, m_SpecificICstSplat(42)));MachineInstrBuilder Add = B.buildAdd(s64, Copies[0], FortyTwo);EXPECT_FALSE(mi_match(Add.getReg(2), *MRI, m_SpecificICstSplat(42)));}TEST_F(AArch64GISelMITest, MatchSpecificConstantOrSplat) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);LLT v4s64 = LLT::fixed_vector(4, s64);MachineInstrBuilder FortyTwoSplat =B.buildSplatVector(v4s64, B.buildConstant(s64, 42));MachineInstrBuilder FortyTwo = B.buildConstant(s64, 42);EXPECT_TRUE(mi_match(FortyTwoSplat.getReg(0), *MRI, m_SpecificICstOrSplat(42)));EXPECT_FALSE(mi_match(FortyTwoSplat.getReg(0), *MRI, m_SpecificICstOrSplat(43)));EXPECT_TRUE(mi_match(FortyTwo.getReg(0), *MRI, m_SpecificICstOrSplat(42)));MachineInstrBuilder NonConstantSplat =B.buildBuildVector(v4s64, {Copies[0], Copies[0], Copies[0], Copies[0]});MachineInstrBuilder AddSplat =B.buildAdd(v4s64, NonConstantSplat, FortyTwoSplat);EXPECT_TRUE(mi_match(AddSplat.getReg(2), *MRI, m_SpecificICstOrSplat(42)));EXPECT_FALSE(mi_match(AddSplat.getReg(2), *MRI, m_SpecificICstOrSplat(43)));EXPECT_FALSE(mi_match(AddSplat.getReg(1), *MRI, m_SpecificICstOrSplat(42)));MachineInstrBuilder Add = B.buildAdd(s64, Copies[0], FortyTwo);EXPECT_TRUE(mi_match(Add.getReg(2), *MRI, m_SpecificICstOrSplat(42)));}TEST_F(AArch64GISelMITest, MatchZeroInt) {setUp();if (!TM)return;auto Zero = B.buildConstant(LLT::scalar(64), 0);EXPECT_TRUE(mi_match(Zero.getReg(0), *MRI, m_ZeroInt()));auto FortyTwo = B.buildConstant(LLT::scalar(64), 42);EXPECT_FALSE(mi_match(FortyTwo.getReg(0), *MRI, m_ZeroInt()));}TEST_F(AArch64GISelMITest, MatchAllOnesInt) {setUp();if (!TM)return;auto AllOnes = B.buildConstant(LLT::scalar(64), -1);EXPECT_TRUE(mi_match(AllOnes.getReg(0), *MRI, m_AllOnesInt()));auto FortyTwo = B.buildConstant(LLT::scalar(64), 42);EXPECT_FALSE(mi_match(FortyTwo.getReg(0), *MRI, m_AllOnesInt()));}TEST_F(AArch64GISelMITest, MatchFPOrIntConst) {setUp();if (!TM)return;Register IntOne = B.buildConstant(LLT::scalar(64), 1).getReg(0);Register FPOne = B.buildFConstant(LLT::scalar(64), 1.0).getReg(0);Optional<ValueAndVReg> ValReg;Optional<FPValueAndVReg> FValReg;EXPECT_TRUE(mi_match(IntOne, *MRI, m_GCst(ValReg)));EXPECT_EQ(IntOne, ValReg->VReg);EXPECT_FALSE(mi_match(IntOne, *MRI, m_GFCst(FValReg)));EXPECT_FALSE(mi_match(FPOne, *MRI, m_GCst(ValReg)));EXPECT_TRUE(mi_match(FPOne, *MRI, m_GFCst(FValReg)));EXPECT_EQ(FPOne, FValReg->VReg);}TEST_F(AArch64GISelMITest, MatchConstantSplat) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);LLT v4s64 = LLT::fixed_vector(4, 64);Register FPOne = B.buildFConstant(s64, 1.0).getReg(0);Register FPZero = B.buildFConstant(s64, 0.0).getReg(0);Register Undef = B.buildUndef(s64).getReg(0);Optional<FPValueAndVReg> FValReg;// GFCstOrSplatGFCstMatch allows undef as part of splat. Undef often comes// from padding to legalize into available operation and then ignore added// elements e.g. v3s64 to v4s64.EXPECT_TRUE(mi_match(FPZero, *MRI, GFCstOrSplatGFCstMatch(FValReg)));EXPECT_EQ(FPZero, FValReg->VReg);EXPECT_FALSE(mi_match(Undef, *MRI, GFCstOrSplatGFCstMatch(FValReg)));auto ZeroSplat = B.buildBuildVector(v4s64, {FPZero, FPZero, FPZero, FPZero});EXPECT_TRUE(mi_match(ZeroSplat.getReg(0), *MRI, GFCstOrSplatGFCstMatch(FValReg)));EXPECT_EQ(FPZero, FValReg->VReg);auto ZeroUndef = B.buildBuildVector(v4s64, {FPZero, FPZero, FPZero, Undef});EXPECT_TRUE(mi_match(ZeroUndef.getReg(0), *MRI, GFCstOrSplatGFCstMatch(FValReg)));EXPECT_EQ(FPZero, FValReg->VReg);// All undefs are not constant splat.auto UndefSplat = B.buildBuildVector(v4s64, {Undef, Undef, Undef, Undef});EXPECT_FALSE(mi_match(UndefSplat.getReg(0), *MRI, GFCstOrSplatGFCstMatch(FValReg)));auto ZeroOne = B.buildBuildVector(v4s64, {FPZero, FPZero, FPZero, FPOne});EXPECT_FALSE(mi_match(ZeroOne.getReg(0), *MRI, GFCstOrSplatGFCstMatch(FValReg)));auto NonConstantSplat =B.buildBuildVector(v4s64, {Copies[0], Copies[0], Copies[0], Copies[0]});EXPECT_FALSE(mi_match(NonConstantSplat.getReg(0), *MRI,GFCstOrSplatGFCstMatch(FValReg)));auto Mixed = B.buildBuildVector(v4s64, {FPZero, FPZero, FPZero, Copies[0]});EXPECT_FALSE(mi_match(Mixed.getReg(0), *MRI, GFCstOrSplatGFCstMatch(FValReg)));}TEST_F(AArch64GISelMITest, MatchNeg) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);auto Zero = B.buildConstant(LLT::scalar(64), 0);auto NegInst = B.buildSub(s64, Zero, Copies[0]);Register NegatedReg;// Match: G_SUB = 0, %RegEXPECT_TRUE(mi_match(NegInst.getReg(0), *MRI, m_Neg(m_Reg(NegatedReg))));EXPECT_EQ(NegatedReg, Copies[0]);// Don't match: G_SUB = %Reg, 0auto NotNegInst1 = B.buildSub(s64, Copies[0], Zero);EXPECT_FALSE(mi_match(NotNegInst1.getReg(0), *MRI, m_Neg(m_Reg(NegatedReg))));// Don't match: G_SUB = 42, %Regauto FortyTwo = B.buildConstant(LLT::scalar(64), 42);auto NotNegInst2 = B.buildSub(s64, FortyTwo, Copies[0]);EXPECT_FALSE(mi_match(NotNegInst2.getReg(0), *MRI, m_Neg(m_Reg(NegatedReg))));// Complex testcase.// %sub = G_SUB = 0, %negated_reg// %add = G_ADD = %x, %subauto AddInst = B.buildAdd(s64, Copies[1], NegInst);NegatedReg = Register();EXPECT_TRUE(mi_match(AddInst.getReg(2), *MRI, m_Neg(m_Reg(NegatedReg))));EXPECT_EQ(NegatedReg, Copies[0]);}TEST_F(AArch64GISelMITest, MatchNot) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);auto AllOnes = B.buildConstant(LLT::scalar(64), -1);auto NotInst1 = B.buildXor(s64, Copies[0], AllOnes);Register NotReg;// Match: G_XOR %NotReg, -1EXPECT_TRUE(mi_match(NotInst1.getReg(0), *MRI, m_Not(m_Reg(NotReg))));EXPECT_EQ(NotReg, Copies[0]);// Match: G_XOR -1, %NotRegauto NotInst2 = B.buildXor(s64, AllOnes, Copies[1]);EXPECT_TRUE(mi_match(NotInst2.getReg(0), *MRI, m_Not(m_Reg(NotReg))));EXPECT_EQ(NotReg, Copies[1]);// Don't match: G_XOR %NotReg, 42auto FortyTwo = B.buildConstant(LLT::scalar(64), 42);auto WrongCst = B.buildXor(s64, Copies[0], FortyTwo);EXPECT_FALSE(mi_match(WrongCst.getReg(0), *MRI, m_Not(m_Reg(NotReg))));// Complex testcase.// %xor = G_XOR %NotReg, -1// %add = G_ADD %x, %xorauto AddInst = B.buildAdd(s64, Copies[1], NotInst1);NotReg = Register();EXPECT_TRUE(mi_match(AddInst.getReg(2), *MRI, m_Not(m_Reg(NotReg))));EXPECT_EQ(NotReg, Copies[0]);}} // namespaceint main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);initLLVM();return RUN_ALL_TESTS();}
//===- MachineIRBuilderTest.cpp -------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"TEST_F(AArch64GISelMITest, TestBuildConstantFConstant) {setUp();if (!TM)return;B.buildConstant(LLT::scalar(32), 42);B.buildFConstant(LLT::scalar(32), 1.0);B.buildConstant(LLT::fixed_vector(2, 32), 99);B.buildFConstant(LLT::fixed_vector(2, 32), 2.0);// Test APFloat overload.APFloat KVal(APFloat::IEEEdouble(), "4.0");B.buildFConstant(LLT::scalar(64), KVal);auto CheckStr = R"(CHECK: [[CONST0:%[0-9]+]]:_(s32) = G_CONSTANT i32 42CHECK: [[FCONST0:%[0-9]+]]:_(s32) = G_FCONSTANT float 1.000000e+00CHECK: [[CONST1:%[0-9]+]]:_(s32) = G_CONSTANT i32 99CHECK: [[VEC0:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[CONST1]]:_(s32), [[CONST1]]:_(s32)CHECK: [[FCONST1:%[0-9]+]]:_(s32) = G_FCONSTANT float 2.000000e+00CHECK: [[VEC1:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[FCONST1]]:_(s32), [[FCONST1]]:_(s32)CHECK: [[FCONST2:%[0-9]+]]:_(s64) = G_FCONSTANT double 4.000000e+00)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST_F(AArch64GISelMITest, TestBuildConstantFConstantDeath) {setUp();if (!TM)return;LLVMContext &Ctx = MF->getFunction().getContext();APInt APV32(32, 12345);// Test APInt version breaksEXPECT_DEATH(B.buildConstant(LLT::scalar(16), APV32),"creating constant with the wrong size");EXPECT_DEATH(B.buildConstant(LLT::fixed_vector(2, 16), APV32),"creating constant with the wrong size");// Test ConstantInt version breaksConstantInt *CI = ConstantInt::get(Ctx, APV32);EXPECT_DEATH(B.buildConstant(LLT::scalar(16), *CI),"creating constant with the wrong size");EXPECT_DEATH(B.buildConstant(LLT::fixed_vector(2, 16), *CI),"creating constant with the wrong size");APFloat DoubleVal(APFloat::IEEEdouble());ConstantFP *CF = ConstantFP::get(Ctx, DoubleVal);EXPECT_DEATH(B.buildFConstant(LLT::scalar(16), *CF),"creating fconstant with the wrong size");EXPECT_DEATH(B.buildFConstant(LLT::fixed_vector(2, 16), *CF),"creating fconstant with the wrong size");}#endif#endifTEST_F(AArch64GISelMITest, DstOpSrcOp) {setUp();if (!TM)return;SmallVector<Register, 4> Copies;collectCopies(Copies, MF);LLT s64 = LLT::scalar(64);auto MIBAdd = B.buildAdd(s64, Copies[0], Copies[1]);// Test SrcOp and DstOp can be constructed directly from MachineOperand by// copying the instructionB.buildAdd(MIBAdd->getOperand(0), MIBAdd->getOperand(1), MIBAdd->getOperand(2));auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[ADD:%[0-9]+]]:_(s64) = G_ADD [[COPY0]]:_, [[COPY1]]:_; CHECK: [[ADD]]:_(s64) = G_ADD [[COPY0]]:_, [[COPY1]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildUnmerge) {setUp();if (!TM)return;SmallVector<Register, 4> Copies;collectCopies(Copies, MF);B.buildUnmerge(LLT::scalar(32), Copies[0]);B.buildUnmerge(LLT::scalar(16), Copies[1]);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[UNMERGE32_0:%[0-9]+]]:_(s32), [[UNMERGE32_1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY0]]; CHECK: [[UNMERGE16_0:%[0-9]+]]:_(s16), [[UNMERGE16_1:%[0-9]+]]:_(s16), [[UNMERGE16_2:%[0-9]+]]:_(s16), [[UNMERGE16_3:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[COPY1]])";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, TestBuildFPInsts) {setUp();if (!TM)return;SmallVector<Register, 4> Copies;collectCopies(Copies, MF);LLT S64 = LLT::scalar(64);B.buildFAdd(S64, Copies[0], Copies[1]);B.buildFSub(S64, Copies[0], Copies[1]);B.buildFMA(S64, Copies[0], Copies[1], Copies[2]);B.buildFMAD(S64, Copies[0], Copies[1], Copies[2]);B.buildFMAD(S64, Copies[0], Copies[1], Copies[2], MachineInstr::FmNoNans);B.buildFNeg(S64, Copies[0]);B.buildFAbs(S64, Copies[0]);B.buildFCopysign(S64, Copies[0], Copies[1]);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[COPY2:%[0-9]+]]:_(s64) = COPY $x2; CHECK: [[FADD:%[0-9]+]]:_(s64) = G_FADD [[COPY0]]:_, [[COPY1]]:_; CHECK: [[FSUB:%[0-9]+]]:_(s64) = G_FSUB [[COPY0]]:_, [[COPY1]]:_; CHECK: [[FMA:%[0-9]+]]:_(s64) = G_FMA [[COPY0]]:_, [[COPY1]]:_, [[COPY2]]:_; CHECK: [[FMAD0:%[0-9]+]]:_(s64) = G_FMAD [[COPY0]]:_, [[COPY1]]:_, [[COPY2]]:_; CHECK: [[FMAD1:%[0-9]+]]:_(s64) = nnan G_FMAD [[COPY0]]:_, [[COPY1]]:_, [[COPY2]]:_; CHECK: [[FNEG:%[0-9]+]]:_(s64) = G_FNEG [[COPY0]]:_; CHECK: [[FABS:%[0-9]+]]:_(s64) = G_FABS [[COPY0]]:_; CHECK: [[FCOPYSIGN:%[0-9]+]]:_(s64) = G_FCOPYSIGN [[COPY0]]:_, [[COPY1]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildIntrinsic) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);// Make sure DstOp version works. sqrt is just a placeholder intrinsic.B.buildIntrinsic(Intrinsic::sqrt, {S64}, false).addUse(Copies[0]);// Make sure register version worksSmallVector<Register, 1> Results;Results.push_back(MRI->createGenericVirtualRegister(S64));B.buildIntrinsic(Intrinsic::sqrt, Results, false).addUse(Copies[1]);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[SQRT0:%[0-9]+]]:_(s64) = G_INTRINSIC intrinsic(@llvm.sqrt), [[COPY0]]:_; CHECK: [[SQRT1:%[0-9]+]]:_(s64) = G_INTRINSIC intrinsic(@llvm.sqrt), [[COPY1]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildXor) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);LLT S128 = LLT::scalar(128);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);B.buildXor(S64, Copies[0], Copies[1]);B.buildNot(S64, Copies[0]);// Make sure this works with > 64-bit typesauto Merge = B.buildMerge(S128, {Copies[0], Copies[1]});B.buildNot(S128, Merge);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[XOR0:%[0-9]+]]:_(s64) = G_XOR [[COPY0]]:_, [[COPY1]]:_; CHECK: [[NEGONE64:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1; CHECK: [[XOR1:%[0-9]+]]:_(s64) = G_XOR [[COPY0]]:_, [[NEGONE64]]:_; CHECK: [[MERGE:%[0-9]+]]:_(s128) = G_MERGE_VALUES [[COPY0]]:_(s64), [[COPY1]]:_(s64); CHECK: [[NEGONE128:%[0-9]+]]:_(s128) = G_CONSTANT i128 -1; CHECK: [[XOR2:%[0-9]+]]:_(s128) = G_XOR [[MERGE]]:_, [[NEGONE128]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildBitCounts) {setUp();if (!TM)return;LLT S32 = LLT::scalar(32);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);B.buildCTPOP(S32, Copies[0]);B.buildCTLZ(S32, Copies[0]);B.buildCTLZ_ZERO_UNDEF(S32, Copies[1]);B.buildCTTZ(S32, Copies[0]);B.buildCTTZ_ZERO_UNDEF(S32, Copies[1]);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[CTPOP:%[0-9]+]]:_(s32) = G_CTPOP [[COPY0]]:_; CHECK: [[CTLZ0:%[0-9]+]]:_(s32) = G_CTLZ [[COPY0]]:_; CHECK: [[CTLZ_UNDEF0:%[0-9]+]]:_(s32) = G_CTLZ_ZERO_UNDEF [[COPY1]]:_; CHECK: [[CTTZ:%[0-9]+]]:_(s32) = G_CTTZ [[COPY0]]:_; CHECK: [[CTTZ_UNDEF0:%[0-9]+]]:_(s32) = G_CTTZ_ZERO_UNDEF [[COPY1]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildCasts) {setUp();if (!TM)return;LLT S32 = LLT::scalar(32);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);B.buildUITOFP(S32, Copies[0]);B.buildSITOFP(S32, Copies[0]);B.buildFPTOUI(S32, Copies[0]);B.buildFPTOSI(S32, Copies[0]);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[UITOFP:%[0-9]+]]:_(s32) = G_UITOFP [[COPY0]]:_; CHECK: [[SITOFP:%[0-9]+]]:_(s32) = G_SITOFP [[COPY0]]:_; CHECK: [[FPTOUI:%[0-9]+]]:_(s32) = G_FPTOUI [[COPY0]]:_; CHECK: [[FPTOSI:%[0-9]+]]:_(s32) = G_FPTOSI [[COPY0]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildMinMaxAbs) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);B.buildSMin(S64, Copies[0], Copies[1]);B.buildSMax(S64, Copies[0], Copies[1]);B.buildUMin(S64, Copies[0], Copies[1]);B.buildUMax(S64, Copies[0], Copies[1]);B.buildAbs(S64, Copies[0]);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[SMIN0:%[0-9]+]]:_(s64) = G_SMIN [[COPY0]]:_, [[COPY1]]:_; CHECK: [[SMAX0:%[0-9]+]]:_(s64) = G_SMAX [[COPY0]]:_, [[COPY1]]:_; CHECK: [[UMIN0:%[0-9]+]]:_(s64) = G_UMIN [[COPY0]]:_, [[COPY1]]:_; CHECK: [[UMAX0:%[0-9]+]]:_(s64) = G_UMAX [[COPY0]]:_, [[COPY1]]:_; CHECK: [[UABS0:%[0-9]+]]:_(s64) = G_ABS [[COPY0]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildAtomicRMW) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);LLT P0 = LLT::pointer(0, 64);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);MachineMemOperand *MMO = MF->getMachineMemOperand(MachinePointerInfo(),MachineMemOperand::MOLoad | MachineMemOperand::MOStore, 8, Align(8),AAMDNodes(), nullptr, SyncScope::System, AtomicOrdering::Unordered);auto Ptr = B.buildUndef(P0);B.buildAtomicRMWFAdd(S64, Ptr, Copies[0], *MMO);B.buildAtomicRMWFSub(S64, Ptr, Copies[0], *MMO);auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[PTR:%[0-9]+]]:_(p0) = G_IMPLICIT_DEF; CHECK: [[FADD:%[0-9]+]]:_(s64) = G_ATOMICRMW_FADD [[PTR]]:_(p0), [[COPY0]]:_ :: (load store unordered (s64)); CHECK: [[FSUB:%[0-9]+]]:_(s64) = G_ATOMICRMW_FSUB [[PTR]]:_(p0), [[COPY0]]:_ :: (load store unordered (s64)))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildMerge) {setUp();if (!TM)return;LLT S32 = LLT::scalar(32);Register RegC0 = B.buildConstant(S32, 0).getReg(0);Register RegC1 = B.buildConstant(S32, 1).getReg(0);Register RegC2 = B.buildConstant(S32, 2).getReg(0);Register RegC3 = B.buildConstant(S32, 3).getReg(0);// Merging plain constants as one big blob of bit should produce a// G_MERGE_VALUES.B.buildMerge(LLT::scalar(128), {RegC0, RegC1, RegC2, RegC3});// Merging plain constants to a vector should produce a G_BUILD_VECTOR.LLT V2x32 = LLT::fixed_vector(2, 32);Register RegC0C1 =B.buildMerge(V2x32, {RegC0, RegC1}).getReg(0);Register RegC2C3 =B.buildMerge(V2x32, {RegC2, RegC3}).getReg(0);// Merging vector constants to a vector should produce a G_CONCAT_VECTORS.B.buildMerge(LLT::fixed_vector(4, 32), {RegC0C1, RegC2C3});// Merging vector constants to a plain type is not allowed.// Nothing else to test.auto CheckStr = R"(; CHECK: [[C0:%[0-9]+]]:_(s32) = G_CONSTANT i32 0; CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1; CHECK: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 2; CHECK: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 3; CHECK: {{%[0-9]+}}:_(s128) = G_MERGE_VALUES [[C0]]:_(s32), [[C1]]:_(s32), [[C2]]:_(s32), [[C3]]:_(s32); CHECK: [[LOW2x32:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[C0]]:_(s32), [[C1]]:_(s32); CHECK: [[HIGH2x32:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[C2]]:_(s32), [[C3]]:_(s32); CHECK: {{%[0-9]+}}:_(<4 x s32>) = G_CONCAT_VECTORS [[LOW2x32]]:_(<2 x s32>), [[HIGH2x32]]:_(<2 x s32>))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildAddoSubo) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S64 = LLT::scalar(64);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);auto UAddo = B.buildUAddo(S64, S1, Copies[0], Copies[1]);auto USubo = B.buildUSubo(S64, S1, Copies[0], Copies[1]);auto SAddo = B.buildSAddo(S64, S1, Copies[0], Copies[1]);auto SSubo = B.buildSSubo(S64, S1, Copies[0], Copies[1]);B.buildUAdde(S64, S1, Copies[0], Copies[1], UAddo.getReg(1));B.buildUSube(S64, S1, Copies[0], Copies[1], USubo.getReg(1));B.buildSAdde(S64, S1, Copies[0], Copies[1], SAddo.getReg(1));B.buildSSube(S64, S1, Copies[0], Copies[1], SSubo.getReg(1));auto CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[UADDO:%[0-9]+]]:_(s64), [[UADDO_FLAG:%[0-9]+]]:_(s1) = G_UADDO [[COPY0]]:_, [[COPY1]]:_; CHECK: [[USUBO:%[0-9]+]]:_(s64), [[USUBO_FLAG:%[0-9]+]]:_(s1) = G_USUBO [[COPY0]]:_, [[COPY1]]:_; CHECK: [[SADDO:%[0-9]+]]:_(s64), [[SADDO_FLAG:%[0-9]+]]:_(s1) = G_SADDO [[COPY0]]:_, [[COPY1]]:_; CHECK: [[SSUBO:%[0-9]+]]:_(s64), [[SSUBO_FLAG:%[0-9]+]]:_(s1) = G_SSUBO [[COPY0]]:_, [[COPY1]]:_; CHECK: [[UADDE:%[0-9]+]]:_(s64), [[UADDE_FLAG:%[0-9]+]]:_(s1) = G_UADDE [[COPY0]]:_, [[COPY1]]:_, [[UADDO_FLAG]]; CHECK: [[USUBE:%[0-9]+]]:_(s64), [[USUBE_FLAG:%[0-9]+]]:_(s1) = G_USUBE [[COPY0]]:_, [[COPY1]]:_, [[USUBO_FLAG]]; CHECK: [[SADDE:%[0-9]+]]:_(s64), [[SADDE_FLAG:%[0-9]+]]:_(s1) = G_SADDE [[COPY0]]:_, [[COPY1]]:_, [[SADDO_FLAG]]; CHECK: [[SSUBE:%[0-9]+]]:_(s64), [[SSUBE_FLAG:%[0-9]+]]:_(s1) = G_SSUBE [[COPY0]]:_, [[COPY1]]:_, [[SSUBO_FLAG]])";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BuildBitfieldExtract) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);SmallVector<Register, 4> Copies;collectCopies(Copies, MF);auto Ubfx = B.buildUbfx(S64, Copies[0], Copies[1], Copies[2]);B.buildSbfx(S64, Ubfx, Copies[0], Copies[2]);const auto *CheckStr = R"(; CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPY $x0; CHECK: [[COPY1:%[0-9]+]]:_(s64) = COPY $x1; CHECK: [[COPY2:%[0-9]+]]:_(s64) = COPY $x2; CHECK: [[UBFX:%[0-9]+]]:_(s64) = G_UBFX [[COPY0]]:_, [[COPY1]]:_(s64), [[COPY2]]:_; CHECK: [[SBFX:%[0-9]+]]:_(s64) = G_SBFX [[UBFX]]:_, [[COPY0]]:_(s64), [[COPY2]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}
//===- LegalizerTest.cpp --------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/GlobalISel/Legalizer.h"#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/LostDebugLocObserver.h"#define DEBUG_TYPE "legalizer-test"using namespace LegalizeActions;using namespace LegalizeMutations;using namespace LegalityPredicates;namespace {::testing::AssertionResult isNullMIPtr(const MachineInstr *MI) {if (MI == nullptr)return ::testing::AssertionSuccess();std::string MIBuffer;raw_string_ostream MISStream(MIBuffer);MI->print(MISStream, /*IsStandalone=*/true, /*SkipOpers=*/false,/*SkipDebugLoc=*/false, /*AddNewLine=*/false);return ::testing::AssertionFailure()<< "unable to legalize instruction: " << MISStream.str();}DefineLegalizerInfo(ALegalizer, {auto p0 = LLT::pointer(0, 64);auto s8 = LLT::scalar(8);auto v2s8 = LLT::fixed_vector(2, 8);auto v2s16 = LLT::fixed_vector(2, 16);getActionDefinitionsBuilder(G_LOAD).legalForTypesWithMemDesc({{s16, p0, s8, 8}}).scalarize(0).clampScalar(0, s16, s16);getActionDefinitionsBuilder(G_PTR_ADD).legalFor({{p0, s64}});getActionDefinitionsBuilder(G_CONSTANT).legalFor({s32, s64});getActionDefinitionsBuilder(G_BUILD_VECTOR).legalFor({{v2s16, s16}}).clampScalar(1, s16, s16);getActionDefinitionsBuilder(G_BUILD_VECTOR_TRUNC).legalFor({{v2s8, s16}});getActionDefinitionsBuilder(G_ANYEXT).legalFor({{s32, s16}});getActionDefinitionsBuilder(G_ZEXT).legalFor({{s32, s16}});getActionDefinitionsBuilder(G_SEXT).legalFor({{s32, s16}});getActionDefinitionsBuilder(G_AND).legalFor({s32});getActionDefinitionsBuilder(G_SEXT_INREG).lower();getActionDefinitionsBuilder(G_ASHR).legalFor({{s32, s32}});getActionDefinitionsBuilder(G_SHL).legalFor({{s32, s32}});})TEST_F(AArch64GISelMITest, BasicLegalizerTest) {StringRef MIRString = R"(%vptr:_(p0) = COPY $x4%v:_(<2 x s8>) = G_LOAD %vptr:_(p0) :: (load (<2 x s8>), align 1)$h4 = COPY %v:_(<2 x s8>))";setUp(MIRString.rtrim(' '));if (!TM)return;ALegalizerInfo LI(MF->getSubtarget());LostDebugLocObserver LocObserver(DEBUG_TYPE);Legalizer::MFResult Result = Legalizer::legalizeMachineFunction(*MF, LI, {&LocObserver}, LocObserver, B);EXPECT_TRUE(isNullMIPtr(Result.FailedOn));EXPECT_TRUE(Result.Changed);StringRef CheckString = R"(CHECK: %vptr:_(p0) = COPY $x4CHECK-NEXT: [[LOAD_0:%[0-9]+]]:_(s16) = G_LOAD %vptr:_(p0) :: (load (s8))CHECK-NEXT: [[OFFSET_1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1CHECK-NEXT: [[VPTR_1:%[0-9]+]]:_(p0) = G_PTR_ADD %vptr:_, [[OFFSET_1]]:_(s64)CHECK-NEXT: [[LOAD_1:%[0-9]+]]:_(s16) = G_LOAD [[VPTR_1]]:_(p0) :: (load (s8) from unknown-address + 1)CHECK-NEXT: %v:_(<2 x s8>) = G_BUILD_VECTOR_TRUNC [[LOAD_0]]:_(s16), [[LOAD_1]]:_(s16)CHECK-NEXT: $h4 = COPY %v:_(<2 x s8>))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckString)) << *MF;}// Making sure the legalization finishes successfully w/o failure to combine// away all the legalization artifacts regardless of the order of their// creation.TEST_F(AArch64GISelMITest, UnorderedArtifactCombiningTest) {StringRef MIRString = R"(%vptr:_(p0) = COPY $x4%v:_(<2 x s8>) = G_LOAD %vptr:_(p0) :: (load (<2 x s8>), align 1)%v0:_(s8), %v1:_(s8) = G_UNMERGE_VALUES %v:_(<2 x s8>)%v0_ext:_(s16) = G_ANYEXT %v0:_(s8)$h4 = COPY %v0_ext:_(s16))";setUp(MIRString.rtrim(' '));if (!TM)return;ALegalizerInfo LI(MF->getSubtarget());LostDebugLocObserver LocObserver(DEBUG_TYPE);// The events here unfold as follows:// 1. First, the function is scanned pre-forming the worklist of artifacts://// UNMERGE (1): pushed into the worklist first, will be processed last.// |// ANYEXT (2)//// 2. Second, the load is scalarized, and then its destination is widened,// forming the following chain of legalization artifacts://// TRUNC (4): created last, will be processed first.// |// BUILD_VECTOR (3)// |// UNMERGE (1): pushed into the worklist first, will be processed last.// |// ANYEXT (2)//// 3. Third, the artifacts are attempted to be combined in pairs, looking// through the def-use chain from the roots towards the leafs, visiting the// roots in order they happen to be in the worklist:// (4) - (trunc): can not be combined;// (3) - (build_vector (trunc)): can not be combined;// (2) - (anyext (unmerge)): can not be combined;// (1) - (unmerge (build_vector)): combined and eliminated;//// leaving the function in the following state://// TRUNC (1): moved to non-artifact instructions worklist first.// |// ANYEXT (2): also moved to non-artifact instructions worklist.//// Every other instruction is successfully legalized in full.// If combining (unmerge (build_vector)) does not re-insert every artifact// that had its def-use chain modified (shortened) into the artifact// worklist (here it's just ANYEXT), the process moves on onto the next// outer loop iteration of the top-level legalization algorithm here, w/o// performing all the artifact combines possible. Let's consider this// scenario first:// 4.A. Neither TRUNC, nor ANYEXT can be legalized in isolation, both of them// get moved to the retry worklist, but no additional artifacts were// created in the process, thus algorithm concludes no progress could be// made, and fails.// 4.B. If, however, combining (unmerge (build_vector)) had re-inserted// ANYEXT into the worklist (as ANYEXT's source changes, not by value,// but by implementation), (anyext (trunc)) combine happens next, which// fully eliminates all the artifacts and legalization succeeds.//// We're looking into making sure that (4.B) happens here, not (4.A). Note// that in that case the first scan through the artifacts worklist, while not// being done in any guaranteed order, only needs to find the innermost// pair(s) of artifacts that could be immediately combined out. After that// the process follows def-use chains, making them shorter at each step, thus// combining everything that can be combined in O(n) time.Legalizer::MFResult Result = Legalizer::legalizeMachineFunction(*MF, LI, {&LocObserver}, LocObserver, B);EXPECT_TRUE(isNullMIPtr(Result.FailedOn));EXPECT_TRUE(Result.Changed);StringRef CheckString = R"(CHECK: %vptr:_(p0) = COPY $x4CHECK-NEXT: [[LOAD_0:%[0-9]+]]:_(s16) = G_LOAD %vptr:_(p0) :: (load (s8))CHECK: $h4 = COPY [[LOAD_0]]:_(s16))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckString)) << *MF;}TEST_F(AArch64GISelMITest, UnorderedArtifactCombiningManyCopiesTest) {StringRef MIRString = R"(%vptr:_(p0) = COPY $x4%v:_(<2 x s8>) = G_LOAD %vptr:_(p0) :: (load (<2 x s8>), align 1)%vc0:_(<2 x s8>) = COPY %v:_(<2 x s8>)%vc1:_(<2 x s8>) = COPY %v:_(<2 x s8>)%vc00:_(s8), %vc01:_(s8) = G_UNMERGE_VALUES %vc0:_(<2 x s8>)%vc10:_(s8), %vc11:_(s8) = G_UNMERGE_VALUES %vc1:_(<2 x s8>)%v0t:_(s8) = COPY %vc00:_(s8)%v0:_(s8) = COPY %v0t:_(s8)%v1t:_(s8) = COPY %vc11:_(s8)%v1:_(s8) = COPY %v1t:_(s8)%v0_zext:_(s32) = G_ZEXT %v0:_(s8)%v1_sext:_(s32) = G_SEXT %v1:_(s8)$w4 = COPY %v0_zext:_(s32)$w5 = COPY %v1_sext:_(s32))";setUp(MIRString.rtrim(' '));if (!TM)return;ALegalizerInfo LI(MF->getSubtarget());LostDebugLocObserver LocObserver(DEBUG_TYPE);Legalizer::MFResult Result = Legalizer::legalizeMachineFunction(*MF, LI, {&LocObserver}, LocObserver, B);EXPECT_TRUE(isNullMIPtr(Result.FailedOn));EXPECT_TRUE(Result.Changed);StringRef CheckString = R"(CHECK: %vptr:_(p0) = COPY $x4CHECK-NEXT: [[LOAD_0:%[0-9]+]]:_(s16) = G_LOAD %vptr:_(p0) :: (load (s8))CHECK-NEXT: [[OFFSET_1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1CHECK-NEXT: [[VPTR_1:%[0-9]+]]:_(p0) = G_PTR_ADD %vptr:_, [[OFFSET_1]]:_(s64)CHECK-NEXT: [[LOAD_1:%[0-9]+]]:_(s16) = G_LOAD [[VPTR_1]]:_(p0) :: (load (s8) from unknown-address + 1)CHECK-NEXT: [[FF_MASK:%[0-9]+]]:_(s32) = G_CONSTANT i32 255CHECK-NEXT: [[V0_EXT:%[0-9]+]]:_(s32) = G_ANYEXT [[LOAD_0]]:_(s16)CHECK-NEXT: %v0_zext:_(s32) = G_AND [[V0_EXT]]:_, [[FF_MASK]]:_CHECK-NEXT: [[V1_EXT:%[0-9]+]]:_(s32) = G_ANYEXT [[LOAD_1]]:_(s16)CHECK-NEXT: [[SHAMNT:%[0-9]+]]:_(s32) = G_CONSTANT i32 24CHECK-NEXT: [[V1_SHL:%[0-9]+]]:_(s32) = G_SHL [[V1_EXT]]:_, [[SHAMNT]]:_(s32)CHECK-NEXT: %v1_sext:_(s32) = G_ASHR [[V1_SHL]]:_, [[SHAMNT]]:_(s32)CHECK-NEXT: $w4 = COPY %v0_zext:_(s32)CHECK-NEXT: $w5 = COPY %v1_sext:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckString)) << *MF;}} // namespace
//===- llvm/unittest/CodeGen/GlobalISel/LegalizerInfoTest.cpp -------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"#include "llvm/CodeGen/TargetOpcodes.h"#include "GISelMITest.h"#include "gtest/gtest.h"using namespace llvm;using namespace LegalizeActions;using namespace LegalityPredicates;using namespace LegalizeMutations;// Define a couple of pretty printers to help debugging when things go wrong.namespace llvm {std::ostream &operator<<(std::ostream &OS, const LegalizeAction Act) {switch (Act) {case Lower: OS << "Lower"; break;case Legal: OS << "Legal"; break;case NarrowScalar: OS << "NarrowScalar"; break;case WidenScalar: OS << "WidenScalar"; break;case FewerElements: OS << "FewerElements"; break;case MoreElements: OS << "MoreElements"; break;case Libcall: OS << "Libcall"; break;case Custom: OS << "Custom"; break;case Bitcast: OS << "Bitcast"; break;case Unsupported: OS << "Unsupported"; break;case NotFound: OS << "NotFound"; break;case UseLegacyRules: OS << "UseLegacyRules"; break;}return OS;}std::ostream &operator<<(std::ostream &OS, const llvm::LegalizeActionStep Ty) {OS << "LegalizeActionStep(" << Ty.Action << ", " << Ty.TypeIdx << ", "<< Ty.NewType << ')';return OS;}}namespace {TEST(LegalizerInfoTest, ScalarRISC) {using namespace TargetOpcode;LegalizerInfo L;auto &LegacyInfo = L.getLegacyLegalizerInfo();// Typical RISCy set of operations based on AArch64.for (unsigned Op : {G_ADD, G_SUB}) {for (unsigned Size : {32, 64})LegacyInfo.setAction({Op, 0, LLT::scalar(Size)},LegacyLegalizeActions::Legal);LegacyInfo.setLegalizeScalarToDifferentSizeStrategy(Op, 0, LegacyLegalizerInfo::widenToLargerTypesAndNarrowToLargest);}LegacyInfo.computeTables();for (unsigned opcode : {G_ADD, G_SUB}) {// Check we infer the correct types and actually do what we're told.EXPECT_EQ(L.getAction({opcode, {LLT::scalar(8)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));EXPECT_EQ(L.getAction({opcode, {LLT::scalar(16)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));EXPECT_EQ(L.getAction({opcode, {LLT::scalar(32)}}),LegalizeActionStep(Legal, 0, LLT{}));EXPECT_EQ(L.getAction({opcode, {LLT::scalar(64)}}),LegalizeActionStep(Legal, 0, LLT{}));// Make sure the default for over-sized types applies.EXPECT_EQ(L.getAction({opcode, {LLT::scalar(128)}}),LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64)));// Make sure we also handle unusual sizesEXPECT_EQ(L.getAction({opcode, {LLT::scalar(1)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));EXPECT_EQ(L.getAction({opcode, {LLT::scalar(31)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));EXPECT_EQ(L.getAction({opcode, {LLT::scalar(33)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(64)));EXPECT_EQ(L.getAction({opcode, {LLT::scalar(63)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(64)));EXPECT_EQ(L.getAction({opcode, {LLT::scalar(65)}}),LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64)));}}TEST(LegalizerInfoTest, VectorRISC) {using namespace TargetOpcode;LegalizerInfo L;auto &LegacyInfo = L.getLegacyLegalizerInfo();// Typical RISCy set of operations based on ARM.LegacyInfo.setAction({G_ADD, LLT::fixed_vector(8, 8)},LegacyLegalizeActions::Legal);LegacyInfo.setAction({G_ADD, LLT::fixed_vector(16, 8)},LegacyLegalizeActions::Legal);LegacyInfo.setAction({G_ADD, LLT::fixed_vector(4, 16)},LegacyLegalizeActions::Legal);LegacyInfo.setAction({G_ADD, LLT::fixed_vector(8, 16)},LegacyLegalizeActions::Legal);LegacyInfo.setAction({G_ADD, LLT::fixed_vector(2, 32)},LegacyLegalizeActions::Legal);LegacyInfo.setAction({G_ADD, LLT::fixed_vector(4, 32)},LegacyLegalizeActions::Legal);LegacyInfo.setLegalizeVectorElementToDifferentSizeStrategy(G_ADD, 0, LegacyLegalizerInfo::widenToLargerTypesUnsupportedOtherwise);LegacyInfo.setAction({G_ADD, 0, LLT::scalar(32)},LegacyLegalizeActions::Legal);LegacyInfo.computeTables();// Check we infer the correct types and actually do what we're told for some// simple cases.EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(8, 8)}}),LegalizeActionStep(Legal, 0, LLT{}));EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(8, 7)}}),LegalizeActionStep(WidenScalar, 0, LLT::fixed_vector(8, 8)));EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(2, 8)}}),LegalizeActionStep(MoreElements, 0, LLT::fixed_vector(8, 8)));EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(8, 32)}}),LegalizeActionStep(FewerElements, 0, LLT::fixed_vector(4, 32)));// Check a few non-power-of-2 sizes:EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(3, 3)}}),LegalizeActionStep(WidenScalar, 0, LLT::fixed_vector(3, 8)));EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(3, 8)}}),LegalizeActionStep(MoreElements, 0, LLT::fixed_vector(8, 8)));}TEST(LegalizerInfoTest, MultipleTypes) {using namespace TargetOpcode;LegalizerInfo L;auto &LegacyInfo = L.getLegacyLegalizerInfo();LLT p0 = LLT::pointer(0, 64);LLT s64 = LLT::scalar(64);// Typical RISCy set of operations based on AArch64.LegacyInfo.setAction({G_PTRTOINT, 0, s64}, LegacyLegalizeActions::Legal);LegacyInfo.setAction({G_PTRTOINT, 1, p0}, LegacyLegalizeActions::Legal);LegacyInfo.setLegalizeScalarToDifferentSizeStrategy(G_PTRTOINT, 0, LegacyLegalizerInfo::widenToLargerTypesAndNarrowToLargest);LegacyInfo.computeTables();// Check we infer the correct types and actually do what we're told.EXPECT_EQ(L.getAction({G_PTRTOINT, {s64, p0}}),LegalizeActionStep(Legal, 0, LLT{}));// Make sure we also handle unusual sizesEXPECT_EQ(L.getAction({G_PTRTOINT, {LLT::scalar(65), s64}}),LegalizeActionStep(NarrowScalar, 0, s64));EXPECT_EQ(L.getAction({G_PTRTOINT, {s64, LLT::pointer(0, 32)}}),LegalizeActionStep(Unsupported, 1, LLT::pointer(0, 32)));}TEST(LegalizerInfoTest, MultipleSteps) {using namespace TargetOpcode;LegalizerInfo L;auto &LegacyInfo = L.getLegacyLegalizerInfo();LLT s32 = LLT::scalar(32);LLT s64 = LLT::scalar(64);LegacyInfo.setLegalizeScalarToDifferentSizeStrategy(G_UREM, 0, LegacyLegalizerInfo::widenToLargerTypesUnsupportedOtherwise);LegacyInfo.setAction({G_UREM, 0, s32}, LegacyLegalizeActions::Lower);LegacyInfo.setAction({G_UREM, 0, s64}, LegacyLegalizeActions::Lower);LegacyInfo.computeTables();EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(16)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(32)}}),LegalizeActionStep(Lower, 0, LLT::scalar(32)));}TEST(LegalizerInfoTest, SizeChangeStrategy) {using namespace TargetOpcode;LegalizerInfo L;auto &LegacyInfo = L.getLegacyLegalizerInfo();for (unsigned Size : {1, 8, 16, 32})LegacyInfo.setAction({G_UREM, 0, LLT::scalar(Size)},LegacyLegalizeActions::Legal);LegacyInfo.setLegalizeScalarToDifferentSizeStrategy(G_UREM, 0, LegacyLegalizerInfo::widenToLargerTypesUnsupportedOtherwise);LegacyInfo.computeTables();// Check we infer the correct types and actually do what we're told.for (unsigned Size : {1, 8, 16, 32}) {EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(Size)}}),LegalizeActionStep(Legal, 0, LLT{}));}EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(2)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(8)));EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(7)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(8)));EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(9)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(16)));EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(17)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(31)}}),LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(33)}}),LegalizeActionStep(Unsupported, 0, LLT::scalar(33)));}}#define EXPECT_ACTION(Action, Index, Type, Query) \do { \auto A = LI.getAction(Query); \EXPECT_EQ(LegalizeActionStep(Action, Index, Type), A) << A; \} while (0)TEST(LegalizerInfoTest, RuleSets) {using namespace TargetOpcode;const LLT s5 = LLT::scalar(5);const LLT s8 = LLT::scalar(8);const LLT s16 = LLT::scalar(16);const LLT s32 = LLT::scalar(32);const LLT s33 = LLT::scalar(33);const LLT s64 = LLT::scalar(64);const LLT v2s5 = LLT::fixed_vector(2, 5);const LLT v2s8 = LLT::fixed_vector(2, 8);const LLT v2s16 = LLT::fixed_vector(2, 16);const LLT v2s32 = LLT::fixed_vector(2, 32);const LLT v3s32 = LLT::fixed_vector(3, 32);const LLT v4s32 = LLT::fixed_vector(4, 32);const LLT v8s32 = LLT::fixed_vector(8, 32);const LLT v2s33 = LLT::fixed_vector(2, 33);const LLT v2s64 = LLT::fixed_vector(2, 64);const LLT p0 = LLT::pointer(0, 32);const LLT v3p0 = LLT::fixed_vector(3, p0);const LLT v4p0 = LLT::fixed_vector(4, p0);const LLT s1 = LLT::scalar(1);const LLT v2s1 = LLT::fixed_vector(2, 1);const LLT v4s1 = LLT::fixed_vector(4, 1);{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_IMPLICIT_DEF).legalFor({v4s32, v4p0}).moreElementsToNextPow2(0);LegacyInfo.computeTables();EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {s32}));EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {v2s32}));EXPECT_ACTION(MoreElements, 0, v4p0, LegalityQuery(G_IMPLICIT_DEF, {v3p0}));EXPECT_ACTION(MoreElements, 0, v4s32, LegalityQuery(G_IMPLICIT_DEF, {v3s32}));}// Test minScalarOrElt{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_OR).legalFor({s32}).minScalarOrElt(0, s32);LegacyInfo.computeTables();EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16}));EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_OR, {v2s16}));}// Test maxScalarOrELt{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_AND).legalFor({s16}).maxScalarOrElt(0, s16);LegacyInfo.computeTables();EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32}));EXPECT_ACTION(NarrowScalar, 0, v2s16, LegalityQuery(G_AND, {v2s32}));}// Test clampScalarOrElt{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_XOR).legalFor({s16}).clampScalarOrElt(0, s16, s32);LegacyInfo.computeTables();EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64}));EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8}));// Make sure the number of elements is preserved.EXPECT_ACTION(NarrowScalar, 0, v2s32, LegalityQuery(G_XOR, {v2s64}));EXPECT_ACTION(WidenScalar, 0, v2s16, LegalityQuery(G_XOR, {v2s8}));}// Test minScalar{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_OR).legalFor({s32}).minScalar(0, s32);LegacyInfo.computeTables();// Only handle scalars, ignore vectors.EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16}));EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_OR, {v2s16}));}// Test maxScalar{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_AND).legalFor({s16}).maxScalar(0, s16);LegacyInfo.computeTables();// Only handle scalars, ignore vectors.EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32}));EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s32}));}// Test clampScalar{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_XOR).legalFor({s16}).clampScalar(0, s16, s32);LegacyInfo.computeTables();EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64}));EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8}));// Only handle scalars, ignore vectors.EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s64}));EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s8}));}// Test widenScalarOrEltToNextPow2{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_AND).legalFor({s32}).widenScalarOrEltToNextPow2(0, 32);LegacyInfo.computeTables();// Handle scalars and vectorsEXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5}));EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_AND, {v2s5}));EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33}));EXPECT_ACTION(WidenScalar, 0, v2s64, LegalityQuery(G_AND, {v2s33}));}// Test widenScalarToNextPow2{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_AND).legalFor({s32}).widenScalarToNextPow2(0, 32);LegacyInfo.computeTables();EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5}));EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33}));// Do nothing for vectors.EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s5}));EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s33}));}// Test changeElementCountTo{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();// Type index formLI.getActionDefinitionsBuilder(G_SELECT).moreElementsIf(isScalar(1), changeElementCountTo(1, 0));// Raw type formLI.getActionDefinitionsBuilder(G_ADD).fewerElementsIf(typeIs(0, v4s32), changeElementCountTo(0, v2s32)).fewerElementsIf(typeIs(0, v8s32), changeElementCountTo(0, s32)).fewerElementsIf(typeIs(0, LLT::scalable_vector(4, 16)),changeElementCountTo(0, LLT::scalable_vector(2, 16))).fewerElementsIf(typeIs(0, LLT::scalable_vector(8, 16)),changeElementCountTo(0, s16));LegacyInfo.computeTables();EXPECT_ACTION(MoreElements, 1, v4s1, LegalityQuery(G_SELECT, {v4s32, s1}));EXPECT_ACTION(MoreElements, 1, v2s1, LegalityQuery(G_SELECT, {v2s32, s1}));EXPECT_ACTION(MoreElements, 1, v2s1, LegalityQuery(G_SELECT, {v2s32, s1}));EXPECT_ACTION(MoreElements, 1, v4s1, LegalityQuery(G_SELECT, {v4p0, s1}));EXPECT_ACTION(MoreElements, 1, LLT::scalable_vector(2, 1),LegalityQuery(G_SELECT, {LLT::scalable_vector(2, 32), s1}));EXPECT_ACTION(MoreElements, 1, LLT::scalable_vector(4, 1),LegalityQuery(G_SELECT, {LLT::scalable_vector(4, 32), s1}));EXPECT_ACTION(MoreElements, 1, LLT::scalable_vector(2, s1),LegalityQuery(G_SELECT, {LLT::scalable_vector(2, p0), s1}));EXPECT_ACTION(FewerElements, 0, v2s32, LegalityQuery(G_ADD, {v4s32}));EXPECT_ACTION(FewerElements, 0, s32, LegalityQuery(G_ADD, {v8s32}));EXPECT_ACTION(FewerElements, 0, LLT::scalable_vector(2, 16),LegalityQuery(G_ADD, {LLT::scalable_vector(4, 16)}));EXPECT_ACTION(FewerElements, 0, s16,LegalityQuery(G_ADD, {LLT::scalable_vector(8, 16)}));}}TEST(LegalizerInfoTest, MMOAlignment) {using namespace TargetOpcode;const LLT s32 = LLT::scalar(32);const LLT p0 = LLT::pointer(0, 64);{LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_LOAD).legalForTypesWithMemDesc({{s32, p0, s32, 32}});LegacyInfo.computeTables();EXPECT_ACTION(Legal, 0, LLT(),LegalityQuery(G_LOAD, {s32, p0},LegalityQuery::MemDesc{s32, 32, AtomicOrdering::NotAtomic}));EXPECT_ACTION(Unsupported, 0, LLT(),LegalityQuery(G_LOAD, {s32, p0},LegalityQuery::MemDesc{s32, 16, AtomicOrdering::NotAtomic }));EXPECT_ACTION(Unsupported, 0, LLT(),LegalityQuery(G_LOAD, {s32, p0},LegalityQuery::MemDesc{s32, 8, AtomicOrdering::NotAtomic}));}// Test that the maximum supported alignment value isn't truncated{// Maximum IR defined alignment in bytes.const uint64_t MaxAlignment = UINT64_C(1) << 29;const uint64_t MaxAlignInBits = 8 * MaxAlignment;LegalizerInfo LI;auto &LegacyInfo = LI.getLegacyLegalizerInfo();LI.getActionDefinitionsBuilder(G_LOAD).legalForTypesWithMemDesc({{s32, p0, s32, MaxAlignInBits}});LegacyInfo.computeTables();EXPECT_ACTION(Legal, 0, LLT(),LegalityQuery(G_LOAD, {s32, p0},LegalityQuery::MemDesc{s32,MaxAlignInBits, AtomicOrdering::NotAtomic}));EXPECT_ACTION(Unsupported, 0, LLT(),LegalityQuery(G_LOAD, {s32, p0},LegalityQuery::MemDesc{s32, 8, AtomicOrdering::NotAtomic }));}}// This code sequence doesn't do anything, but it covers a previously uncovered// codepath that used to crash in MSVC x86_32 debug mode.TEST(LegalizerInfoTest, MSVCDebugMiscompile) {const LLT S1 = LLT::scalar(1);const LLT P0 = LLT::pointer(0, 32);LegalizerInfo LI;auto Builder = LI.getActionDefinitionsBuilder(TargetOpcode::G_PTRTOINT);(void)Builder.legalForCartesianProduct({S1}, {P0});}
//===- LegalizerHelperTest.cpp//-----------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/LostDebugLocObserver.h"using namespace LegalizeActions;using namespace LegalizeMutations;using namespace LegalityPredicates;namespace {class DummyGISelObserver : public GISelChangeObserver {public:void changingInstr(MachineInstr &MI) override {}void changedInstr(MachineInstr &MI) override {}void createdInstr(MachineInstr &MI) override {}void erasingInstr(MachineInstr &MI) override {}};// Test G_ROTL/G_ROTR lowering.TEST_F(AArch64GISelMITest, LowerRotates) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_ROTR, G_ROTL}).lower(); });LLT S32 = LLT::scalar(32);auto Src = B.buildTrunc(S32, Copies[0]);auto Amt = B.buildTrunc(S32, Copies[1]);auto ROTR = B.buildInstr(TargetOpcode::G_ROTR, {S32}, {Src, Amt});auto ROTL = B.buildInstr(TargetOpcode::G_ROTL, {S32}, {Src, Amt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*ROTR, 0, S32));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*ROTL, 0, S32));auto CheckStr = R"(; Check G_ROTRCHECK: [[SRC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[AMT:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 31CHECK: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[C]]:_, [[AMT]]:_CHECK: [[AND:%[0-9]+]]:_(s32) = G_AND [[AMT]]:_, [[C1]]:_CHECK: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[SRC]]:_, [[AND]]:_(s32)CHECK: [[AND1:%[0-9]+]]:_(s32) = G_AND [[SUB]]:_, [[C1]]:_CHECK: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SRC]]:_, [[AND1]]:_(s32)CHECK: G_OR [[LSHR]]:_, [[SHL]]:_; Check G_ROTLCHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 31CHECK: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[C]]:_, [[AMT]]:_CHECK: [[AND:%[0-9]+]]:_(s32) = G_AND [[AMT]]:_, [[C1]]:_CHECK: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SRC]]:_, [[AND]]:_(s32)CHECK: [[AND1:%[0-9]+]]:_(s32) = G_AND [[SUB]]:_, [[C1]]:_CHECK: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[SRC]]:_, [[AND1]]:_(s32)CHECK: G_OR [[SHL]]:_, [[LSHR]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test G_ROTL/G_ROTR non-pow2 lowering.TEST_F(AArch64GISelMITest, LowerRotatesNonPow2) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_ROTR, G_ROTL}).lower(); });LLT S24 = LLT::scalar(24);auto Src = B.buildTrunc(S24, Copies[0]);auto Amt = B.buildTrunc(S24, Copies[1]);auto ROTR = B.buildInstr(TargetOpcode::G_ROTR, {S24}, {Src, Amt});auto ROTL = B.buildInstr(TargetOpcode::G_ROTL, {S24}, {Src, Amt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*ROTR, 0, S24));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*ROTL, 0, S24));auto CheckStr = R"(; Check G_ROTRCHECK: [[SRC:%[0-9]+]]:_(s24) = G_TRUNCCHECK: [[AMT:%[0-9]+]]:_(s24) = G_TRUNCCHECK: [[C:%[0-9]+]]:_(s24) = G_CONSTANT i24 0CHECK: [[C1:%[0-9]+]]:_(s24) = G_CONSTANT i24 23CHECK: [[C2:%[0-9]+]]:_(s24) = G_CONSTANT i24 24CHECK: [[UREM:%[0-9]+]]:_(s24) = G_UREM [[AMT]]:_, [[C2]]:_CHECK: [[LSHR:%[0-9]+]]:_(s24) = G_LSHR [[SRC]]:_, [[UREM]]:_(s24)CHECK: [[SUB:%[0-9]+]]:_(s24) = G_SUB [[C1]]:_, [[UREM]]:_CHECK: [[C4:%[0-9]+]]:_(s24) = G_CONSTANT i24 1CHECK: [[SHL:%[0-9]+]]:_(s24) = G_SHL [[SRC]]:_, [[C4]]:_(s24)CHECK: [[SHL2:%[0-9]+]]:_(s24) = G_SHL [[SHL]]:_, [[SUB]]:_(s24)CHECK: G_OR [[LSHR]]:_, [[SHL2]]:_; Check G_ROTLCHECK: [[C:%[0-9]+]]:_(s24) = G_CONSTANT i24 0CHECK: [[C1:%[0-9]+]]:_(s24) = G_CONSTANT i24 23CHECK: [[C2:%[0-9]+]]:_(s24) = G_CONSTANT i24 24CHECK: [[UREM:%[0-9]+]]:_(s24) = G_UREM [[AMT]]:_, [[C2]]:_CHECK: [[SHL:%[0-9]+]]:_(s24) = G_SHL [[SRC]]:_, [[UREM]]:_(s24)CHECK: [[SUB:%[0-9]+]]:_(s24) = G_SUB [[C1]]:_, [[UREM]]:_CHECK: [[C4:%[0-9]+]]:_(s24) = G_CONSTANT i24 1CHECK: [[LSHR:%[0-9]+]]:_(s24) = G_LSHR [[SRC]]:_, [[C4]]:_(s24)CHECK: [[LSHR2:%[0-9]+]]:_(s24) = G_LSHR [[LSHR]]:_, [[SUB]]:_(s24)CHECK: G_OR [[SHL]]:_, [[LSHR2]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test vector G_ROTR lowering.TEST_F(AArch64GISelMITest, LowerRotatesVector) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_ROTR, G_ROTL}).lower(); });LLT S32 = LLT::scalar(32);LLT V4S32 = LLT::fixed_vector(4, S32);auto SrcTrunc = B.buildTrunc(S32, Copies[0]);auto Src = B.buildSplatVector(V4S32, SrcTrunc);auto AmtTrunc = B.buildTrunc(S32, Copies[1]);auto Amt = B.buildSplatVector(V4S32, AmtTrunc);auto ROTR = B.buildInstr(TargetOpcode::G_ROTR, {V4S32}, {Src, Amt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*ROTR, 0, V4S32));auto CheckStr = R"(CHECK: [[SRCTRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[SRC:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[SRCTRUNC]]CHECK: [[AMTTRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[AMT:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[AMTTRUNC]]CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0CHECK: [[ZERO:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[C]]CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 31CHECK: [[VEC31:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[C1]]CHECK: [[SUB:%[0-9]+]]:_(<4 x s32>) = G_SUB [[ZERO]]:_, [[AMT]]:_CHECK: [[AND:%[0-9]+]]:_(<4 x s32>) = G_AND [[AMT]]:_, [[VEC31]]:_CHECK: [[LSHR:%[0-9]+]]:_(<4 x s32>) = G_LSHR [[SRC]]:_, [[AND]]:_(<4 x s32>)CHECK: [[AND1:%[0-9]+]]:_(<4 x s32>) = G_AND [[SUB]]:_, [[VEC31]]:_CHECK: [[SHL:%[0-9]+]]:_(<4 x s32>) = G_SHL [[SRC]]:_, [[AND1]]:_(<4 x s32>)CHECK: G_OR [[LSHR]]:_, [[SHL]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test CTTZ expansion when CTTZ_ZERO_UNDEF is legal or custom,// in which case it becomes CTTZ_ZERO_UNDEF with select.TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ0) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTTZ_ZERO_UNDEF).legalFor({{s32, s64}});});// Build Instrauto MIBCTTZ =B.buildInstr(TargetOpcode::G_CTTZ, {LLT::scalar(32)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)));auto CheckStr = R"(CHECK: [[CZU:%[0-9]+]]:_(s32) = G_CTTZ_ZERO_UNDEF %0CHECK: [[ZERO:%[0-9]+]]:_(s64) = G_CONSTANT i64 0CHECK: [[CMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), %0:_(s64), [[ZERO]]CHECK: [[SIXTY4:%[0-9]+]]:_(s32) = G_CONSTANT i32 64CHECK: [[SEL:%[0-9]+]]:_(s32) = G_SELECT [[CMP]]:_(s1), [[SIXTY4]]:_, [[CZU]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTTZ expansion in terms of CTLZTEST_F(AArch64GISelMITest, LowerBitCountingCTTZ1) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTLZ).legalFor({{s64, s64}});});// Build Instrauto MIBCTTZ =B.buildInstr(TargetOpcode::G_CTTZ, {LLT::scalar(64)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[NEG1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1CHECK: [[NOT:%[0-9]+]]:_(s64) = G_XOR %0:_, [[NEG1]]CHECK: [[SUB1:%[0-9]+]]:_(s64) = G_ADD %0:_, [[NEG1]]CHECK: [[AND1:%[0-9]+]]:_(s64) = G_AND [[NOT]]:_, [[SUB1]]:_CHECK: [[CST64:%[0-9]+]]:_(s64) = G_CONSTANT i64 64CHECK: [[CTLZ:%[0-9]+]]:_(s64) = G_CTLZ [[AND1]]:_CHECK: G_SUB [[CST64]]:_, [[CTLZ]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTLZ scalar narrowingTEST_F(AArch64GISelMITest, NarrowScalarCTLZ) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTLZ).legalFor({{s32, s32}});});// Build Instrauto CTLZ =B.buildInstr(TargetOpcode::G_CTLZ, {LLT::scalar(32)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*CTLZ, 1, LLT::scalar(32)));auto CheckStr = R"(CHECK: [[UNMERGE_LO:%[0-9]+]]:_(s32), [[UNMERGE_HI:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES %0:_(s64)CHECK: [[ZERO:%[0-9]+]]:_(s32) = G_CONSTANT i32 0CHECK: [[CMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[UNMERGE_HI]]:_(s32), [[ZERO]]:_CHECK: [[CTLZ_LO:%[0-9]+]]:_(s32) = G_CTLZ [[UNMERGE_LO]]:_(s32)CHECK: [[THIRTYTWO:%[0-9]+]]:_(s32) = G_CONSTANT i32 32CHECK: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[CTLZ_LO]]:_, [[THIRTYTWO]]:_CHECK: [[CTLZ_HI:%[0-9]+]]:_(s32) = G_CTLZ_ZERO_UNDEF [[UNMERGE_HI]]:_(s32)CHECK: %{{[0-9]+}}:_(s32) = G_SELECT [[CMP]]:_(s1), [[ADD]]:_, [[CTLZ_HI]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTTZ scalar narrowingTEST_F(AArch64GISelMITest, NarrowScalarCTTZ) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTTZ).legalFor({{s32, s64}});});// Build Instrauto CTTZ =B.buildInstr(TargetOpcode::G_CTTZ, {LLT::scalar(32)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*CTTZ, 1, LLT::scalar(32)));auto CheckStr = R"(CHECK: [[UNMERGE_LO:%[0-9]+]]:_(s32), [[UNMERGE_HI:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES %0:_(s64)CHECK: [[ZERO:%[0-9]+]]:_(s32) = G_CONSTANT i32 0CHECK: [[CMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[UNMERGE_LO]]:_(s32), [[ZERO]]:_CHECK: [[CTTZ_HI:%[0-9]+]]:_(s32) = G_CTTZ [[UNMERGE_HI]]:_(s32)CHECK: [[THIRTYTWO:%[0-9]+]]:_(s32) = G_CONSTANT i32 32CHECK: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[CTTZ_HI]]:_, [[THIRTYTWO]]:_CHECK: [[CTTZ_LO:%[0-9]+]]:_(s32) = G_CTTZ_ZERO_UNDEF [[UNMERGE_LO]]:_(s32)CHECK: %{{[0-9]+}}:_(s32) = G_SELECT [[CMP]]:_(s1), [[ADD]]:_, [[CTTZ_LO]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTTZ expansion in terms of CTPOPTEST_F(AArch64GISelMITest, LowerBitCountingCTTZ2) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTPOP).legalFor({{s64, s64}});});// Buildauto MIBCTTZ =B.buildInstr(TargetOpcode::G_CTTZ, {LLT::scalar(64)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInsertPt(*EntryMBB, MIBCTTZ->getIterator());EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[NEG1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1CHECK: [[NOT:%[0-9]+]]:_(s64) = G_XOR %0:_, [[NEG1]]CHECK: [[SUB1:%[0-9]+]]:_(s64) = G_ADD %0:_, [[NEG1]]CHECK: [[AND1:%[0-9]+]]:_(s64) = G_AND [[NOT]]:_, [[SUB1]]:_CHECK: [[POP:%[0-9]+]]:_(s64) = G_CTPOP [[AND1]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTPOP widening.TEST_F(AArch64GISelMITest, WidenBitCountingCTPOP1) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTPOP).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTPOP = B.buildInstr(TargetOpcode::G_CTPOP, {s16}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInstr(*MIBCTPOP);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*MIBCTPOP, 1, s16));auto CheckStr = R"(CHECK: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC %0:_(s64)CHECK: [[ZEXT:%[0-9]+]]:_(s16) = G_ZEXT [[TRUNC]]:_(s8)CHECK: [[CTPOP:%[0-9]+]]:_(s16) = G_CTPOP [[ZEXT]]CHECK: [[COPY:%[0-9]+]]:_(s16) = COPY [[CTPOP]]:_(s16))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test a strange case where the result is wider than the sourceTEST_F(AArch64GISelMITest, WidenBitCountingCTPOP2) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTPOP).legalFor({{s32, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};LLT s32{LLT::scalar(32)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTPOP = B.buildInstr(TargetOpcode::G_CTPOP, {s32}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInstr(*MIBCTPOP);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*MIBCTPOP, 1, s16));auto CheckStr = R"(CHECK: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC %0:_(s64)CHECK: [[ZEXT:%[0-9]+]]:_(s16) = G_ZEXT [[TRUNC]]:_(s8)CHECK: [[CTPOP:%[0-9]+]]:_(s16) = G_CTPOP [[ZEXT]]CHECK: [[COPY:%[0-9]+]]:_(s32) = G_ZEXT [[CTPOP]]:_(s16))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTTZ_ZERO_UNDEF expansion in terms of CTTZTEST_F(AArch64GISelMITest, LowerBitCountingCTTZ3) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTTZ).legalFor({{s64, s64}});});// Buildauto MIBCTTZ = B.buildInstr(TargetOpcode::G_CTTZ_ZERO_UNDEF,{LLT::scalar(64)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: CTTZ)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTLZ expansion in terms of CTLZ_ZERO_UNDEFTEST_F(AArch64GISelMITest, LowerBitCountingCTLZ0) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF).legalFor({{s64, s64}});});// Buildauto MIBCTLZ =B.buildInstr(TargetOpcode::G_CTLZ, {LLT::scalar(64)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.lower(*MIBCTLZ, 0, LLT::scalar(64)) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[CZU:%[0-9]+]]:_(s64) = G_CTLZ_ZERO_UNDEF %0CHECK: [[ZERO:%[0-9]+]]:_(s64) = G_CONSTANT i64 0CHECK: [[CMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), %0:_(s64), [[ZERO]]CHECK: [[SIXTY4:%[0-9]+]]:_(s64) = G_CONSTANT i64 64CHECK: [[SEL:%[0-9]+]]:_(s64) = G_SELECT [[CMP]]:_(s1), [[SIXTY4]]:_, [[CZU]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTLZ expansion in terms of CTLZ_ZERO_UNDEF if the latter is a libcallTEST_F(AArch64GISelMITest, LowerBitCountingCTLZLibcall) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF).libcallFor({{s32, s64}});});// Buildauto MIBCTLZ =B.buildInstr(TargetOpcode::G_CTLZ, {LLT::scalar(32)}, {Copies[0]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*MIBCTLZ, 0, LLT::scalar(32)));auto CheckStr = R"(CHECK: [[CZU:%[0-9]+]]:_(s32) = G_CTLZ_ZERO_UNDEF %0CHECK: [[ZERO:%[0-9]+]]:_(s64) = G_CONSTANT i64 0CHECK: [[CMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), %0:_(s64), [[ZERO]]CHECK: [[THIRTY2:%[0-9]+]]:_(s32) = G_CONSTANT i32 64CHECK: [[SEL:%[0-9]+]]:_(s32) = G_SELECT [[CMP]]:_(s1), [[THIRTY2]]:_, [[CZU]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTLZ expansionTEST_F(AArch64GISelMITest, LowerBitCountingCTLZ1) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTPOP).legalFor({{s8, s8}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTLZ = B.buildInstr(TargetOpcode::G_CTLZ, {s8}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.lower(*MIBCTLZ, 0, s8) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Cst1:%[0-9]+]]:_(s8) = G_CONSTANT i8 1CHECK: [[Sh1:%[0-9]+]]:_(s8) = G_LSHR [[Trunc]]:_, [[Cst1]]:_CHECK: [[Or1:%[0-9]+]]:_(s8) = G_OR [[Trunc]]:_, [[Sh1]]:_CHECK: [[Cst2:%[0-9]+]]:_(s8) = G_CONSTANT i8 2CHECK: [[Sh2:%[0-9]+]]:_(s8) = G_LSHR [[Or1]]:_, [[Cst2]]:_CHECK: [[Or2:%[0-9]+]]:_(s8) = G_OR [[Or1]]:_, [[Sh2]]:_CHECK: [[Cst4:%[0-9]+]]:_(s8) = G_CONSTANT i8 4CHECK: [[Sh4:%[0-9]+]]:_(s8) = G_LSHR [[Or2]]:_, [[Cst4]]:_CHECK: [[Or4:%[0-9]+]]:_(s8) = G_OR [[Or2]]:_, [[Sh4]]:_CHECK: [[CTPOP:%[0-9]+]]:_(s8) = G_CTPOP [[Or4]]:_CHECK: [[Len:%[0-9]+]]:_(s8) = G_CONSTANT i8 8CHECK: [[Sub:%[0-9]+]]:_(s8) = G_SUB [[Len]]:_, [[CTPOP]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTLZ widening.TEST_F(AArch64GISelMITest, WidenBitCountingCTLZ) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTLZ).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTLZ = B.buildInstr(TargetOpcode::G_CTLZ, {s8}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBCTLZ, 1, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Zext:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[Ctlz:%[0-9]+]]:_(s16) = G_CTLZ [[Zext]]CHECK: [[Cst8:%[0-9]+]]:_(s16) = G_CONSTANT i16 8CHECK: [[Sub:%[0-9]+]]:_(s16) = G_SUB [[Ctlz]]:_, [[Cst8]]:_CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNC [[Sub]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTLZ_ZERO_UNDEF widening.TEST_F(AArch64GISelMITest, WidenBitCountingCTLZZeroUndef) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTLZ_ZU =B.buildInstr(TargetOpcode::G_CTLZ_ZERO_UNDEF, {s8}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBCTLZ_ZU, 1, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Zext:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[CtlzZu:%[0-9]+]]:_(s16) = G_CTLZ_ZERO_UNDEF [[Zext]]CHECK: [[Cst8:%[0-9]+]]:_(s16) = G_CONSTANT i16 8CHECK: [[Sub:%[0-9]+]]:_(s16) = G_SUB [[CtlzZu]]:_, [[Cst8]]:_CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNC [[Sub]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTPOP widening.TEST_F(AArch64GISelMITest, WidenBitCountingCTPOP) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTPOP).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTPOP = B.buildInstr(TargetOpcode::G_CTPOP, {s8}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBCTPOP, 1, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Zext:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[Ctpop:%[0-9]+]]:_(s16) = G_CTPOP [[Zext]]CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNC [[Ctpop]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTTZ_ZERO_UNDEF widening.TEST_F(AArch64GISelMITest, WidenBitCountingCTTZ_ZERO_UNDEF) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTTZ_ZERO_UNDEF).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTTZ_ZERO_UNDEF =B.buildInstr(TargetOpcode::G_CTTZ_ZERO_UNDEF, {s8}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBCTTZ_ZERO_UNDEF, 1, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[AnyExt:%[0-9]+]]:_(s16) = G_ANYEXT [[Trunc]]CHECK: [[CttzZu:%[0-9]+]]:_(s16) = G_CTTZ_ZERO_UNDEF [[AnyExt]]CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNC [[CttzZu]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// CTTZ widening.TEST_F(AArch64GISelMITest, WidenBitCountingCTTZ) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTTZ).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto MIBCTTZ = B.buildInstr(TargetOpcode::G_CTTZ, {s8}, {MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBCTTZ, 1, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[AnyExt:%[0-9]+]]:_(s16) = G_ANYEXT [[Trunc]]CHECK: [[Cst:%[0-9]+]]:_(s16) = G_CONSTANT i16 256CHECK: [[Or:%[0-9]+]]:_(s16) = G_OR [[AnyExt]]:_, [[Cst]]CHECK: [[Cttz:%[0-9]+]]:_(s16) = G_CTTZ_ZERO_UNDEF [[Or]]CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNC [[Cttz]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// UADDO widening.TEST_F(AArch64GISelMITest, WidenUADDO) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_ADD).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);unsigned CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBUAddO =B.buildInstr(TargetOpcode::G_UADDO, {s8, CarryReg}, {MIBTrunc, MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBUAddO, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[LHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[ADD:%[0-9]+]]:_(s16) = G_ADD [[LHS]]:_, [[RHS]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[ADD]]CHECK: [[ZEXT:%[0-9]+]]:_(s16) = G_ZEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[ADD]]:_(s16), [[ZEXT]]:_CHECK: G_TRUNC [[ADD]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// USUBO widening.TEST_F(AArch64GISelMITest, WidenUSUBO) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_SUB).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);unsigned CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBUSUBO =B.buildInstr(TargetOpcode::G_USUBO, {s8, CarryReg}, {MIBTrunc, MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBUSUBO, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[LHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[SUB:%[0-9]+]]:_(s16) = G_SUB [[LHS]]:_, [[RHS]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[SUB]]CHECK: [[ZEXT:%[0-9]+]]:_(s16) = G_ZEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[SUB]]:_(s16), [[ZEXT]]:_CHECK: G_TRUNC [[SUB]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// SADDO widening.TEST_F(AArch64GISelMITest, WidenSADDO) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_ADD).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);unsigned CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBSAddO =B.buildInstr(TargetOpcode::G_SADDO, {s8, CarryReg}, {MIBTrunc, MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBSAddO, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[LHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[ADD:%[0-9]+]]:_(s16) = G_ADD [[LHS]]:_, [[RHS]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[ADD]]CHECK: [[SEXT:%[0-9]+]]:_(s16) = G_SEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[ADD]]:_(s16), [[SEXT]]:_CHECK: G_TRUNC [[ADD]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// SSUBO widening.TEST_F(AArch64GISelMITest, WidenSSUBO) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_SUB).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);unsigned CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBSSUBO =B.buildInstr(TargetOpcode::G_SSUBO, {s8, CarryReg}, {MIBTrunc, MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBSSUBO, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[LHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[SUB:%[0-9]+]]:_(s16) = G_SUB [[LHS]]:_, [[RHS]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[SUB]]CHECK: [[SEXT:%[0-9]+]]:_(s16) = G_SEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[SUB]]:_(s16), [[SEXT]]:_CHECK: G_TRUNC [[SUB]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenUADDE) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_UADDE).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto CarryIn = B.buildUndef(LLT::scalar(1));Register CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBUAddO = B.buildInstr(TargetOpcode::G_UADDE, {s8, CarryReg},{MIBTrunc, MIBTrunc, CarryIn});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBUAddO, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);const char *CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Implicit:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[LHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[UADDE:%[0-9]+]]:_(s16), [[CARRY:%[0-9]+]]:_(s1) = G_UADDE [[LHS]]:_, [[RHS]]:_, [[Implicit]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[UADDE]]CHECK: [[ZEXT:%[0-9]+]]:_(s16) = G_ZEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[UADDE]]:_(s16), [[ZEXT]]:_CHECK: G_TRUNC [[UADDE]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenUSUBE) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_USUBE).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto CarryIn = B.buildUndef(LLT::scalar(1));Register CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBUSUBE = B.buildInstr(TargetOpcode::G_USUBE, {s8, CarryReg},{MIBTrunc, MIBTrunc, CarryIn});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBUSUBE, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);const char *CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Implicit:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[LHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_ZEXT [[Trunc]]CHECK: [[USUBE:%[0-9]+]]:_(s16), [[CARRY:%[0-9]+]]:_(s1) = G_USUBE [[LHS]]:_, [[RHS]]:_, [[Implicit]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[USUBE]]CHECK: [[ZEXT:%[0-9]+]]:_(s16) = G_ZEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[USUBE]]:_(s16), [[ZEXT]]:_CHECK: G_TRUNC [[USUBE]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenSADDE) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_SADDE, G_UADDE}).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto CarryIn = B.buildUndef(LLT::scalar(1));Register CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBUAddO = B.buildInstr(TargetOpcode::G_SADDE, {s8, CarryReg},{MIBTrunc, MIBTrunc, CarryIn});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBUAddO, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);const char *CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Implicit:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[LHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[SADDE:%[0-9]+]]:_(s16), [[CARRY:%[0-9]+]]:_(s1) = G_UADDE [[LHS]]:_, [[RHS]]:_, [[Implicit]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[SADDE]]CHECK: [[SEXT:%[0-9]+]]:_(s16) = G_SEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[SADDE]]:_(s16), [[SEXT]]:_CHECK: G_TRUNC [[SADDE]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenSSUBE) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_SSUBE, G_USUBE}).legalFor({{s16, s16}});});// Build// Trunc it to s8.LLT s8{LLT::scalar(8)};LLT s16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(s8, Copies[0]);auto CarryIn = B.buildUndef(LLT::scalar(1));Register CarryReg = MRI->createGenericVirtualRegister(LLT::scalar(1));auto MIBSSUBE = B.buildInstr(TargetOpcode::G_SSUBE, {s8, CarryReg},{MIBTrunc, MIBTrunc, CarryIn});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_TRUE(Helper.widenScalar(*MIBSSUBE, 0, s16) ==LegalizerHelper::LegalizeResult::Legalized);const char *CheckStr = R"(CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNCCHECK: [[Implicit:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[LHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[RHS:%[0-9]+]]:_(s16) = G_SEXT [[Trunc]]CHECK: [[SSUBE:%[0-9]+]]:_(s16), [[CARRY:%[0-9]+]]:_(s1) = G_USUBE [[LHS]]:_, [[RHS]]:_, [[Implicit]]:_CHECK: [[TRUNC1:%[0-9]+]]:_(s8) = G_TRUNC [[SSUBE]]CHECK: [[SEXT:%[0-9]+]]:_(s16) = G_SEXT [[TRUNC1]]CHECK: G_ICMP intpred(ne), [[SSUBE]]:_(s16), [[SEXT]]:_CHECK: G_TRUNC [[SSUBE]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenUMULOCondition) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_ADD).legalFor({{s16, s16}});});LLT s32 = LLT::scalar(32);LLT s64 = LLT::scalar(64);auto UMulo =B.buildInstr(TargetOpcode::G_UMULO, {s64, LLT::scalar(1)},{Copies[0], Copies[1]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInstrAndDebugLoc(*UMulo);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*UMulo, 1, s32));auto CheckStr = R"(CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY1:%[0-9]+]]:_(s64) = COPYCHECK: [[ADD:%[0-9]+]]:_(s64), [[OVERFLOW:%[0-9]+]]:_(s32) = G_UMULO [[COPY0]]:_, [[COPY1]]:_CHECK: {{[0-9]+}}:_(s1) = G_TRUNC [[OVERFLOW]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowUADDO) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_UADDO, G_UADDE}).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto UADDO = B.buildUAddo(S96, S1, Op0, Op1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*UADDO, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[UADDO0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_UADDO [[OP0_0]]:_, [[OP1_0]]:_CHECK: [[UADDO1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_UADDE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[UADDO2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_UADDE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[UADDO:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[UADDO0]]:_(s32), [[UADDO1]]:_(s32), [[UADDO2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowUSUBO) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_USUBO, G_USUBE}).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto USUBO = B.buildUSubo(S96, S1, Op0, Op1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*USUBO, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[USUBO0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_USUBO [[OP0_0]]:_, [[OP1_0]]:_CHECK: [[USUBO1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_USUBE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[USUBO2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_USUBE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[USUBO:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[USUBO0]]:_(s32), [[USUBO1]]:_(s32), [[USUBO2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowSADDO) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_UADDO, G_UADDE, G_SADDE}).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto SADDO = B.buildSAddo(S96, S1, Op0, Op1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*SADDO, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[SADDO0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_UADDO [[OP0_0]]:_, [[OP1_0]]:_CHECK: [[SADDO1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_UADDE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[SADDO2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_SADDE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[SADDO:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[SADDO0]]:_(s32), [[SADDO1]]:_(s32), [[SADDO2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowSSUBO) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_USUBO, G_USUBE, G_SSUBE}).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto SSUBO = B.buildSSubo(S96, S1, Op0, Op1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*SSUBO, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[SSUBO0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_USUBO [[OP0_0]]:_, [[OP1_0]]:_CHECK: [[SSUBO1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_USUBE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[SSUBO2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_SSUBE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[SSUBO:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[SSUBO0]]:_(s32), [[SSUBO1]]:_(s32), [[SSUBO2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowUADDE) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_UADDE).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto Op2 = B.buildUndef(S1);auto UADDE = B.buildUAdde(S96, S1, Op0, Op1, Op2);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*UADDE, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF2:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[UADDE0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_UADDE [[OP0_0]]:_, [[OP1_0]]:_, [[IMP_DEF2]]:_CHECK: [[UADDE1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_UADDE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[UADDE2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_UADDE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[UADDE:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[UADDE0]]:_(s32), [[UADDE1]]:_(s32), [[UADDE2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowUSUBE) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_USUBE).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto Op2 = B.buildUndef(S1);auto USUBE = B.buildUSube(S96, S1, Op0, Op1, Op2);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*USUBE, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF2:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[USUBE0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_USUBE [[OP0_0]]:_, [[OP1_0]]:_, [[IMP_DEF2]]:_CHECK: [[USUBE1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_USUBE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[USUBE2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_USUBE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[USUBE:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[USUBE0]]:_(s32), [[USUBE1]]:_(s32), [[USUBE2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowSADDE) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_SADDE, G_UADDE}).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto Op2 = B.buildUndef(S1);auto SADDE = B.buildSAdde(S96, S1, Op0, Op1, Op2);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*SADDE, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF2:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[SADDE0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_UADDE [[OP0_0]]:_, [[OP1_0]]:_, [[IMP_DEF2]]:_CHECK: [[SADDE1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_UADDE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[SADDE2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_SADDE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[SADDE:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[SADDE0]]:_(s32), [[SADDE1]]:_(s32), [[SADDE2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowSSUBE) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT S96 = LLT::scalar(96);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_SSUBE, G_USUBE}).legalFor({{LLT::scalar(32), LLT::scalar(1)}});});auto Op0 = B.buildUndef(S96);auto Op1 = B.buildUndef(S96);auto Op2 = B.buildUndef(S1);auto SSUBE = B.buildSSube(S96, S1, Op0, Op1, Op2);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*SSUBE, 0, S32));const char *CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(s96) = G_IMPLICIT_DEFCHECK: [[IMP_DEF2:%[0-9]+]]:_(s1) = G_IMPLICIT_DEFCHECK: [[OP0_0:%[0-9]+]]:_(s32), [[OP0_1:%[0-9]+]]:_(s32), [[OP0_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]CHECK: [[OP1_0:%[0-9]+]]:_(s32), [[OP1_1:%[0-9]+]]:_(s32), [[OP1_2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]CHECK: [[SSUBE0:%[0-9]+]]:_(s32), [[CARRY0:%[0-9]+]]:_(s1) = G_USUBE [[OP0_0]]:_, [[OP1_0]]:_, [[IMP_DEF2]]:_CHECK: [[SSUBE1:%[0-9]+]]:_(s32), [[CARRY1:%[0-9]+]]:_(s1) = G_USUBE [[OP0_1]]:_, [[OP1_1]]:_, [[CARRY0]]:_CHECK: [[SSUBE2:%[0-9]+]]:_(s32), [[CARRY2:%[0-9]+]]:_(s1) = G_SSUBE [[OP0_2]]:_, [[OP1_2]]:_, [[CARRY1]]:_CHECK: [[SSUBE:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[SSUBE0]]:_(s32), [[SSUBE1]]:_(s32), [[SSUBE2]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, FewerElementsAnd) {setUp();if (!TM)return;const LLT V2S32 = LLT::fixed_vector(2, 32);const LLT V5S32 = LLT::fixed_vector(5, 32);// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_AND).legalFor({s32});});auto Op0 = B.buildUndef(V5S32);auto Op1 = B.buildUndef(V5S32);auto And = B.buildAnd(V5S32, Op0, Op1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInstr(*And);EXPECT_TRUE(Helper.fewerElementsVector(*And, 0, V2S32) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[IMP_DEF0:%[0-9]+]]:_(<5 x s32>) = G_IMPLICIT_DEFCHECK: [[IMP_DEF1:%[0-9]+]]:_(<5 x s32>) = G_IMPLICIT_DEFCHECK: [[VALUE0:%[0-9]+]]:_(s32), [[VALUE1:%[0-9]+]]:_(s32), [[VALUE2:%[0-9]+]]:_(s32), [[VALUE3:%[0-9]+]]:_(s32), [[VALUE4:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF0]]:_(<5 x s32>)CHECK: [[VECTOR0:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[VALUE0]]:_(s32), [[VALUE1]]:_(s32)CHECK: [[VECTOR1:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[VALUE2]]:_(s32), [[VALUE3]]:_(s32)CHECK: [[VALUE5:%[0-9]+]]:_(s32), [[VALUE6:%[0-9]+]]:_(s32), [[VALUE7:%[0-9]+]]:_(s32), [[VALUE8:%[0-9]+]]:_(s32), [[VALUE9:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[IMP_DEF1]]:_(<5 x s32>)CHECK: [[VECTOR2:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[VALUE5]]:_(s32), [[VALUE6]]:_(s32)CHECK: [[VECTOR3:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[VALUE7]]:_(s32), [[VALUE8]]:_(s32)CHECK: [[AND0:%[0-9]+]]:_(<2 x s32>) = G_AND [[VECTOR0]]:_, [[VECTOR2]]:_CHECK: [[AND1:%[0-9]+]]:_(<2 x s32>) = G_AND [[VECTOR1]]:_, [[VECTOR3]]:_CHECK: [[AND2:%[0-9]+]]:_(s32) = G_AND [[VALUE4]]:_, [[VALUE9]]:_CHECK: [[AND0_E0:%[0-9]+]]:_(s32), [[AND0_E1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[AND0]]:_(<2 x s32>)CHECK: [[AND1_E0:%[0-9]+]]:_(s32), [[AND1_E1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[AND1]]:_(<2 x s32>)CHECK: [[RESULT:%[0-9]+]]:_(<5 x s32>) = G_BUILD_VECTOR [[AND0_E0]]:_(s32), [[AND0_E1]]:_(s32), [[AND1_E0]]:_(s32), [[AND1_E1]]:_(s32), [[AND2]]:_(s32))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, MoreElementsAnd) {setUp();if (!TM)return;LLT s32 = LLT::scalar(32);LLT v2s32 = LLT::fixed_vector(2, 32);LLT v6s32 = LLT::fixed_vector(6, 32);LegalizerInfo LI;LI.getActionDefinitionsBuilder(TargetOpcode::G_AND).legalFor({v6s32}).clampMinNumElements(0, s32, 6);LI.getLegacyLegalizerInfo().computeTables();DummyGISelObserver Observer;LegalizerHelper Helper(*MF, LI, Observer, B);B.setInsertPt(*EntryMBB, EntryMBB->end());auto Val0 = B.buildBitcast(v2s32, Copies[0]);auto Val1 = B.buildBitcast(v2s32, Copies[1]);auto And = B.buildAnd(v2s32, Val0, Val1);B.setInstr(*And);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.moreElementsVector(*And, 0, v6s32));auto CheckStr = R"(CHECK: [[BITCAST0:%[0-9]+]]:_(<2 x s32>) = G_BITCASTCHECK: [[BITCAST1:%[0-9]+]]:_(<2 x s32>) = G_BITCASTCHECK: [[BITCAST0_E0:%[0-9]+]]:_(s32), [[BITCAST0_E1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[BITCAST0]]:_(<2 x s32>)CHECK: [[IMP_DEF0:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[BITCAST0_LARGE:%[0-9]+]]:_(<6 x s32>) = G_BUILD_VECTOR [[BITCAST0_E0]]:_(s32), [[BITCAST0_E1]]:_(s32), [[IMP_DEF0]]:_(s32), [[IMP_DEF0]]:_(s32), [[IMP_DEF0]]:_(s32), [[IMP_DEF0]]:_(s32)CHECK: [[BITCAST1_E0:%[0-9]+]]:_(s32), [[BITCAST1_E1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[BITCAST1]]:_(<2 x s32>)CHECK: [[IMP_DEF1:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[BITCAST1_LARGE:%[0-9]+]]:_(<6 x s32>) = G_BUILD_VECTOR [[BITCAST1_E0]]:_(s32), [[BITCAST1_E1]]:_(s32), [[IMP_DEF1]]:_(s32), [[IMP_DEF1]]:_(s32), [[IMP_DEF1]]:_(s32), [[IMP_DEF1]]:_(s32)CHECK: [[AND:%[0-9]+]]:_(<6 x s32>) = G_AND [[BITCAST0_LARGE]]:_, [[BITCAST1_LARGE]]:_CHECK: [[AND_E0:%[0-9]+]]:_(s32), [[AND_E1:%[0-9]+]]:_(s32), [[AND_E2:%[0-9]+]]:_(s32), [[AND_E3:%[0-9]+]]:_(s32), [[AND_E4:%[0-9]+]]:_(s32), [[AND_E5:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[AND]]:_(<6 x s32>)CHECK: (<2 x s32>) = G_BUILD_VECTOR [[AND_E0]]:_(s32), [[AND_E1]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, FewerElementsPhi) {setUp();if (!TM)return;LLT s1 = LLT::scalar(1);LLT s32 = LLT::scalar(32);LLT s64 = LLT::scalar(64);LLT v2s32 = LLT::fixed_vector(2, 32);LLT v5s32 = LLT::fixed_vector(5, 32);LegalizerInfo LI;LI.getActionDefinitionsBuilder(TargetOpcode::G_PHI).legalFor({v2s32}).clampMinNumElements(0, s32, 2);LI.getLegacyLegalizerInfo().computeTables();LLT PhiTy = v5s32;DummyGISelObserver Observer;LegalizerHelper Helper(*MF, LI, Observer, B);B.setMBB(*EntryMBB);MachineBasicBlock *MidMBB = MF->CreateMachineBasicBlock();MachineBasicBlock *EndMBB = MF->CreateMachineBasicBlock();MF->insert(MF->end(), MidMBB);MF->insert(MF->end(), EndMBB);EntryMBB->addSuccessor(MidMBB);EntryMBB->addSuccessor(EndMBB);MidMBB->addSuccessor(EndMBB);auto InitVal = B.buildUndef(PhiTy);auto InitOtherVal = B.buildConstant(s64, 999);auto ICmp = B.buildICmp(CmpInst::ICMP_EQ, s1, Copies[0], Copies[1]);B.buildBrCond(ICmp.getReg(0), *MidMBB);B.buildBr(*EndMBB);B.setMBB(*MidMBB);auto MidVal = B.buildUndef(PhiTy);auto MidOtherVal = B.buildConstant(s64, 345);B.buildBr(*EndMBB);B.setMBB(*EndMBB);auto Phi = B.buildInstr(TargetOpcode::G_PHI).addDef(MRI->createGenericVirtualRegister(PhiTy)).addUse(InitVal.getReg(0)).addMBB(EntryMBB).addUse(MidVal.getReg(0)).addMBB(MidMBB);// Insert another irrelevant phi to make sure the rebuild is inserted after// it.B.buildInstr(TargetOpcode::G_PHI).addDef(MRI->createGenericVirtualRegister(s64)).addUse(InitOtherVal.getReg(0)).addMBB(EntryMBB).addUse(MidOtherVal.getReg(0)).addMBB(MidMBB);// Add some use instruction after the phis.B.buildAnd(PhiTy, Phi.getReg(0), Phi.getReg(0));B.setInstr(*Phi);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.fewerElementsVector(*Phi, 0, v2s32));auto CheckStr = R"(CHECK: [[INITVAL:%[0-9]+]]:_(<5 x s32>) = G_IMPLICIT_DEFCHECK: [[INITVAL_E0:%[0-9]+]]:_(s32), [[INITVAL_E1:%[0-9]+]]:_(s32), [[INITVAL_E2:%[0-9]+]]:_(s32), [[INITVAL_E3:%[0-9]+]]:_(s32), [[INITVAL_E4:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[INITVAL]]:_(<5 x s32>)CHECK: [[INITVAL_E01:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[INITVAL_E0]]:_(s32), [[INITVAL_E1]]:_(s32)CHECK: [[INITVAL_E23:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[INITVAL_E2]]:_(s32), [[INITVAL_E3]]:_(s32)CHECK: G_BRCONDCHECK: [[MIDVAL:%[0-9]+]]:_(<5 x s32>) = G_IMPLICIT_DEFCHECK: [[MIDVAL_E0:%[0-9]+]]:_(s32), [[MIDVAL_E1:%[0-9]+]]:_(s32), [[MIDVAL_E2:%[0-9]+]]:_(s32), [[MIDVAL_E3:%[0-9]+]]:_(s32), [[MIDVAL_E4:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[MIDVAL]]:_(<5 x s32>)CHECK: [[MIDVAL_E01:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[MIDVAL_E0]]:_(s32), [[MIDVAL_E1]]:_(s32)CHECK: [[MIDVAL_E23:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[MIDVAL_E2]]:_(s32), [[MIDVAL_E3]]:_(s32)CHECK: G_BRCHECK: [[PHI0:%[0-9]+]]:_(<2 x s32>) = G_PHI [[INITVAL_E01]]:_(<2 x s32>), %bb.0, [[MIDVAL_E01]]:_(<2 x s32>), %bb.1CHECK: [[PHI1:%[0-9]+]]:_(<2 x s32>) = G_PHI [[INITVAL_E23]]:_(<2 x s32>), %bb.0, [[MIDVAL_E23]]:_(<2 x s32>), %bb.1CHECK: [[PHI2:%[0-9]+]]:_(s32) = G_PHI [[INITVAL_E4]]:_(s32), %bb.0, [[MIDVAL_E4]]:_(s32), %bb.1CHECK: [[UNMERGE0:%[0-9]+]]:_(s32), [[UNMERGE1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[PHI0]]:_(<2 x s32>)CHECK: [[UNMERGE2:%[0-9]+]]:_(s32), [[UNMERGE3:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[PHI1]]:_(<2 x s32>)CHECK: [[BV:%[0-9]+]]:_(<5 x s32>) = G_BUILD_VECTOR [[UNMERGE0]]:_(s32), [[UNMERGE1]]:_(s32), [[UNMERGE2]]:_(s32), [[UNMERGE3]]:_(s32), [[PHI2]]:_(s32)CHECK: [[OTHER_PHI:%[0-9]+]]:_(s64) = G_PHICHECK: [[USE_OP:%[0-9]+]]:_(<5 x s32>) = G_AND [[BV]]:_, [[BV]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// FNEG expansion in terms of XORTEST_F(AArch64GISelMITest, LowerFNEG) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FSUB).legalFor({s64});});// Build Instr. Make sure FMF are preserved.auto FAdd =B.buildInstr(TargetOpcode::G_FADD, {LLT::scalar(64)}, {Copies[0], Copies[1]},MachineInstr::MIFlag::FmNsz);// Should not propagate the flags of src instruction.auto FNeg0 =B.buildInstr(TargetOpcode::G_FNEG, {LLT::scalar(64)}, {FAdd.getReg(0)},{MachineInstr::MIFlag::FmArcp});// Preserve the one flag.auto FNeg1 =B.buildInstr(TargetOpcode::G_FNEG, {LLT::scalar(64)}, {Copies[0]},MachineInstr::MIFlag::FmNoInfs);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInstr(*FNeg0);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*FNeg0, 0, LLT::scalar(64)));B.setInstr(*FNeg1);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*FNeg1, 0, LLT::scalar(64)));auto CheckStr = R"(CHECK: [[FADD:%[0-9]+]]:_(s64) = nsz G_FADD %0:_, %1:_CHECK: [[CONST0:%[0-9]+]]:_(s64) = G_CONSTANT i64 -9223372036854775808CHECK: [[FSUB0:%[0-9]+]]:_(s64) = G_XOR [[FADD]]:_, [[CONST0]]:_CHECK: [[CONST1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -9223372036854775808CHECK: [[FSUB1:%[0-9]+]]:_(s64) = G_XOR %0:_, [[CONST1]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LowerMinMax) {setUp();if (!TM)return;LLT s64 = LLT::scalar(64);LLT v2s32 = LLT::fixed_vector(2, 32);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_SMIN, G_SMAX, G_UMIN, G_UMAX}).lowerFor({s64, LLT::fixed_vector(2, s32)});});auto SMin = B.buildSMin(s64, Copies[0], Copies[1]);auto SMax = B.buildSMax(s64, Copies[0], Copies[1]);auto UMin = B.buildUMin(s64, Copies[0], Copies[1]);auto UMax = B.buildUMax(s64, Copies[0], Copies[1]);auto VecVal0 = B.buildBitcast(v2s32, Copies[0]);auto VecVal1 = B.buildBitcast(v2s32, Copies[1]);auto SMinV = B.buildSMin(v2s32, VecVal0, VecVal1);auto SMaxV = B.buildSMax(v2s32, VecVal0, VecVal1);auto UMinV = B.buildUMin(v2s32, VecVal0, VecVal1);auto UMaxV = B.buildUMax(v2s32, VecVal0, VecVal1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInstr(*SMin);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*SMin, 0, s64));B.setInstr(*SMax);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*SMax, 0, s64));B.setInstr(*UMin);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*UMin, 0, s64));B.setInstr(*UMax);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*UMax, 0, s64));B.setInstr(*SMinV);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*SMinV, 0, v2s32));B.setInstr(*SMaxV);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*SMaxV, 0, v2s32));B.setInstr(*UMinV);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*UMinV, 0, v2s32));B.setInstr(*UMaxV);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*UMaxV, 0, v2s32));auto CheckStr = R"(CHECK: [[CMP0:%[0-9]+]]:_(s1) = G_ICMP intpred(slt), %0:_(s64), %1:_CHECK: [[SMIN:%[0-9]+]]:_(s64) = G_SELECT [[CMP0]]:_(s1), %0:_, %1:_CHECK: [[CMP1:%[0-9]+]]:_(s1) = G_ICMP intpred(sgt), %0:_(s64), %1:_CHECK: [[SMAX:%[0-9]+]]:_(s64) = G_SELECT [[CMP1]]:_(s1), %0:_, %1:_CHECK: [[CMP2:%[0-9]+]]:_(s1) = G_ICMP intpred(ult), %0:_(s64), %1:_CHECK: [[UMIN:%[0-9]+]]:_(s64) = G_SELECT [[CMP2]]:_(s1), %0:_, %1:_CHECK: [[CMP3:%[0-9]+]]:_(s1) = G_ICMP intpred(ugt), %0:_(s64), %1:_CHECK: [[UMAX:%[0-9]+]]:_(s64) = G_SELECT [[CMP3]]:_(s1), %0:_, %1:_CHECK: [[VEC0:%[0-9]+]]:_(<2 x s32>) = G_BITCAST %0:_(s64)CHECK: [[VEC1:%[0-9]+]]:_(<2 x s32>) = G_BITCAST %1:_(s64)CHECK: [[VCMP0:%[0-9]+]]:_(<2 x s1>) = G_ICMP intpred(slt), [[VEC0]]:_(<2 x s32>), [[VEC1]]:_CHECK: [[SMINV:%[0-9]+]]:_(<2 x s32>) = G_SELECT [[VCMP0]]:_(<2 x s1>), [[VEC0]]:_, [[VEC1]]:_CHECK: [[VCMP1:%[0-9]+]]:_(<2 x s1>) = G_ICMP intpred(sgt), [[VEC0]]:_(<2 x s32>), [[VEC1]]:_CHECK: [[SMAXV:%[0-9]+]]:_(<2 x s32>) = G_SELECT [[VCMP1]]:_(<2 x s1>), [[VEC0]]:_, [[VEC1]]:_CHECK: [[VCMP2:%[0-9]+]]:_(<2 x s1>) = G_ICMP intpred(ult), [[VEC0]]:_(<2 x s32>), [[VEC1]]:_CHECK: [[UMINV:%[0-9]+]]:_(<2 x s32>) = G_SELECT [[VCMP2]]:_(<2 x s1>), [[VEC0]]:_, [[VEC1]]:_CHECK: [[VCMP3:%[0-9]+]]:_(<2 x s1>) = G_ICMP intpred(ugt), [[VEC0]]:_(<2 x s32>), [[VEC1]]:_CHECK: [[UMAXV:%[0-9]+]]:_(<2 x s32>) = G_SELECT [[VCMP3]]:_(<2 x s1>), [[VEC0]]:_, [[VEC1]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenScalarBuildVector) {setUp();if (!TM)return;LLT S32 = LLT::scalar(32);LLT S16 = LLT::scalar(16);LLT V2S16 = LLT::fixed_vector(2, S16);LLT V2S32 = LLT::fixed_vector(2, S32);DefineLegalizerInfo(A, {getActionDefinitionsBuilder({G_SMIN, G_SMAX, G_UMIN, G_UMAX}).lowerFor({s64, LLT::fixed_vector(2, s32)});});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInsertPt(*EntryMBB, EntryMBB->end());Register Constant0 = B.buildConstant(S16, 1).getReg(0);Register Constant1 = B.buildConstant(S16, 2).getReg(0);auto BV0 = B.buildBuildVector(V2S16, {Constant0, Constant1});auto BV1 = B.buildBuildVector(V2S16, {Constant0, Constant1});B.setInstr(*BV0);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*BV0, 0, V2S32));B.setInstr(*BV1);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*BV1, 1, S32));auto CheckStr = R"(CHECK: [[K0:%[0-9]+]]:_(s16) = G_CONSTANT i16 1CHECK-NEXT: [[K1:%[0-9]+]]:_(s16) = G_CONSTANT i16 2CHECK-NEXT: [[EXT_K0_0:%[0-9]+]]:_(s32) = G_ANYEXT [[K0]]CHECK-NEXT: [[EXT_K1_0:%[0-9]+]]:_(s32) = G_ANYEXT [[K1]]CHECK-NEXT: [[BV0:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[EXT_K0_0]]:_(s32), [[EXT_K1_0]]:_(s32)CHECK-NEXT: [[BV0_TRUNC:%[0-9]+]]:_(<2 x s16>) = G_TRUNC [[BV0]]CHECK: [[EXT_K0_1:%[0-9]+]]:_(s32) = G_ANYEXT [[K0]]CHECK-NEXT: [[EXT_K1_1:%[0-9]+]]:_(s32) = G_ANYEXT [[K1]]CHECK-NEXT: [[BV1:%[0-9]+]]:_(<2 x s16>) = G_BUILD_VECTOR_TRUNC [[EXT_K0_1]]:_(s32), [[EXT_K1_1]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LowerMergeValues) {setUp();if (!TM)return;const LLT S32 = LLT::scalar(32);const LLT S24 = LLT::scalar(24);const LLT S21 = LLT::scalar(21);const LLT S16 = LLT::scalar(16);const LLT S9 = LLT::scalar(9);const LLT S8 = LLT::scalar(8);const LLT S3 = LLT::scalar(3);DefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_UNMERGE_VALUES).widenScalarIf(typeIs(1, LLT::scalar(3)), changeTo(1, LLT::scalar(9)));});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInsertPt(*EntryMBB, EntryMBB->end());// 24 = 3 3 3 3 3 3 3 3// => 9//// This can do 3 merges, but need an extra implicit_def.SmallVector<Register, 8> Merge0Ops;for (int I = 0; I != 8; ++I)Merge0Ops.push_back(B.buildConstant(S3, I).getReg(0));auto Merge0 = B.buildMerge(S24, Merge0Ops);// 21 = 3 3 3 3 3 3 3// => 9, 2 extra implicit_def needed//SmallVector<Register, 8> Merge1Ops;for (int I = 0; I != 7; ++I)Merge1Ops.push_back(B.buildConstant(S3, I).getReg(0));auto Merge1 = B.buildMerge(S21, Merge1Ops);SmallVector<Register, 8> Merge2Ops;for (int I = 0; I != 2; ++I)Merge2Ops.push_back(B.buildConstant(S8, I).getReg(0));auto Merge2 = B.buildMerge(S16, Merge2Ops);B.setInstr(*Merge0);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*Merge0, 1, S9));B.setInstr(*Merge1);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*Merge1, 1, S9));// Request a source size greater than the original destination size.B.setInstr(*Merge2);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*Merge2, 1, S32));auto CheckStr = R"(CHECK: [[K0:%[0-9]+]]:_(s3) = G_CONSTANT i3 0CHECK-NEXT: [[K1:%[0-9]+]]:_(s3) = G_CONSTANT i3 1CHECK-NEXT: [[K2:%[0-9]+]]:_(s3) = G_CONSTANT i3 2CHECK-NEXT: [[K3:%[0-9]+]]:_(s3) = G_CONSTANT i3 3CHECK-NEXT: [[K4:%[0-9]+]]:_(s3) = G_CONSTANT i3 -4CHECK-NEXT: [[K5:%[0-9]+]]:_(s3) = G_CONSTANT i3 -3CHECK-NEXT: [[K6:%[0-9]+]]:_(s3) = G_CONSTANT i3 -2CHECK-NEXT: [[K7:%[0-9]+]]:_(s3) = G_CONSTANT i3 -1CHECK-NEXT: [[IMPDEF0:%[0-9]+]]:_(s3) = G_IMPLICIT_DEFCHECK-NEXT: [[MERGE0:%[0-9]+]]:_(s9) = G_MERGE_VALUES [[K0]]:_(s3), [[K1]]:_(s3), [[K2]]:_(s3)CHECK-NEXT: [[MERGE1:%[0-9]+]]:_(s9) = G_MERGE_VALUES [[K3]]:_(s3), [[K4]]:_(s3), [[K5]]:_(s3)CHECK-NEXT: [[MERGE2:%[0-9]+]]:_(s9) = G_MERGE_VALUES [[K6]]:_(s3), [[K7]]:_(s3), [[IMPDEF0]]:_(s3)CHECK-NEXT: [[MERGE3:%[0-9]+]]:_(s27) = G_MERGE_VALUES [[MERGE0]]:_(s9), [[MERGE1]]:_(s9), [[MERGE2]]:_(s9)CHECK-NEXT: (s24) = G_TRUNC [[MERGE3]]:_(s27)CHECK: [[K8:%[0-9]+]]:_(s3) = G_CONSTANT i3 0CHECK-NEXT: [[K9:%[0-9]+]]:_(s3) = G_CONSTANT i3 1CHECK-NEXT: [[K10:%[0-9]+]]:_(s3) = G_CONSTANT i3 2CHECK-NEXT: [[K11:%[0-9]+]]:_(s3) = G_CONSTANT i3 3CHECK-NEXT: [[K12:%[0-9]+]]:_(s3) = G_CONSTANT i3 -4CHECK-NEXT: [[K13:%[0-9]+]]:_(s3) = G_CONSTANT i3 -3CHECK-NEXT: [[K14:%[0-9]+]]:_(s3) = G_CONSTANT i3 -2CHECK-NEXT: [[IMPDEF1:%[0-9]+]]:_(s3) = G_IMPLICIT_DEFCHECK-NEXT: [[MERGE4:%[0-9]+]]:_(s9) = G_MERGE_VALUES [[K8]]:_(s3), [[K9]]:_(s3), [[K10]]:_(s3)CHECK-NEXT: [[MERGE5:%[0-9]+]]:_(s9) = G_MERGE_VALUES [[K11]]:_(s3), [[K12]]:_(s3), [[K13]]:_(s3)CHECK-NEXT: [[MERGE6:%[0-9]+]]:_(s9) = G_MERGE_VALUES [[K14]]:_(s3), [[IMPDEF1]]:_(s3), [[IMPDEF1]]:_(s3)CHECK-NEXT: [[MERGE7:%[0-9]+]]:_(s27) = G_MERGE_VALUES [[MERGE4]]:_(s9), [[MERGE5]]:_(s9), [[MERGE6]]:_(s9)CHECK-NEXT: (s21) = G_TRUNC [[MERGE7]]:_(s27)CHECK: [[K15:%[0-9]+]]:_(s8) = G_CONSTANT i8 0CHECK-NEXT: [[K16:%[0-9]+]]:_(s8) = G_CONSTANT i8 1CHECK-NEXT: [[ZEXT_K15:[0-9]+]]:_(s32) = G_ZEXT [[K15]]:_(s8)CHECK-NEXT: [[ZEXT_K16:[0-9]+]]:_(s32) = G_ZEXT [[K16]]:_(s8)[[K16:%[0-9]+]]:_(s32) = G_CONSTANT i32 8[[SHL:%[0-9]+]]:_(s32) = G_SHL [[ZEXT_K16]]:_, [[K16]]:_(s32)[[OR:%[0-9]+]]:_(s32) = G_OR [[ZEXT_K16]]:_, [[SHL]]:_(s16) = G_TRUNC [[OR]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenScalarMergeValuesPointer) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInsertPt(*EntryMBB, EntryMBB->end());const LLT S32 = LLT::scalar(32);const LLT S64 = LLT::scalar(64);const LLT P0 = LLT::pointer(0, 64);auto Lo = B.buildTrunc(S32, Copies[0]);auto Hi = B.buildTrunc(S32, Copies[1]);auto Merge = B.buildMerge(P0, {Lo, Hi});B.setInstr(*Merge);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*Merge, 1, S64));auto CheckStr = R"(CHECK: [[TRUNC0:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[TRUNC1:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ZEXT_TRUNC0:%[0-9]+]]:_(s64) = G_ZEXT [[TRUNC0]]CHECK: [[ZEXT_TRUNC1:%[0-9]+]]:_(s64) = G_ZEXT [[TRUNC1]]CHECK: [[SHIFT_AMT:%[0-9]+]]:_(s64) = G_CONSTANT i64 32CHECK: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[ZEXT_TRUNC1]]:_, [[SHIFT_AMT]]CHECK: [[OR:%[0-9]+]]:_(s64) = G_OR [[ZEXT_TRUNC0]]:_, [[SHL]]CHECK: [[INTTOPTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[OR]]:_(s64))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, WidenSEXTINREG) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_SEXT_INREG).legalForTypeWithAnyImm({s64});});// Build Instrauto MIB = B.buildInstr(TargetOpcode::G_SEXT_INREG, {LLT::scalar(32)},{B.buildInstr(TargetOpcode::G_TRUNC, {LLT::scalar(32)}, {Copies[0]}),uint64_t(8)});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInstr(*MIB);ASSERT_TRUE(Helper.widenScalar(*MIB, 0, LLT::scalar(64)) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[T0:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[T1:%[0-9]+]]:_(s64) = G_ANYEXT [[T0]]:_(s32)CHECK: [[T2:%[0-9]+]]:_(s64) = G_SEXT_INREG [[T1]]:_, 8CHECK: [[T3:%[0-9]+]]:_(s32) = G_TRUNC [[T2]]:_(s64))";// CheckASSERT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowSEXTINREG) {setUp();if (!TM)return;// Declare your legalization info, these aren't actually relevant to the test.DefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_SEXT_INREG).legalForTypeWithAnyImm({s64});});// Build Instrauto MIB = B.buildInstr(TargetOpcode::G_SEXT_INREG, {LLT::scalar(16)},{B.buildInstr(TargetOpcode::G_TRUNC, {LLT::scalar(16)}, {Copies[0]}),uint64_t(8)});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInstr(*MIB);ASSERT_TRUE(Helper.narrowScalar(*MIB, 0, LLT::scalar(10)) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[T0:%[0-9]+]]:_(s16) = G_TRUNCCHECK: [[T1:%[0-9]+]]:_(s10) = G_TRUNC [[T0]]:_(s16)CHECK: [[T2:%[0-9]+]]:_(s10) = G_SEXT_INREG [[T1]]:_, 8CHECK: [[T3:%[0-9]+]]:_(s16) = G_SEXT [[T2]]:_(s10))";// CheckASSERT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowSEXTINREG2) {setUp();if (!TM)return;// Declare your legalization info, these aren't actually relevant to the test.DefineLegalizerInfo(A, { getActionDefinitionsBuilder(G_SEXT_INREG).legalForTypeWithAnyImm({s64}); });// Build Instrauto MIB = B.buildInstr(TargetOpcode::G_SEXT_INREG, {LLT::scalar(32)},{B.buildInstr(TargetOpcode::G_TRUNC, {LLT::scalar(32)}, {Copies[0]}),uint64_t(9)});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInstr(*MIB);ASSERT_TRUE(Helper.narrowScalar(*MIB, 0, LLT::scalar(8)) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[T0:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[T1:%[0-9]+]]:_(s8), [[T2:%[0-9]+]]:_(s8), [[T3:%[0-9]+]]:_(s8), [[T4:%[0-9]+]]:_(s8) = G_UNMERGE_VALUES [[T0]]:_(s32)CHECK: [[CST2:%[0-9]+]]:_(s8) = G_CONSTANT i8 7CHECK: [[T5:%[0-9]+]]:_(s8) = G_SEXT_INREG [[T2]]:_, 1CHECK: [[T6:%[0-9]+]]:_(s8) = G_ASHR [[T5]]:_, [[CST2]]:_CHECK: [[T7:%[0-9]+]]:_(s32) = G_MERGE_VALUES [[T1]]:_(s8), [[T5]]:_(s8), [[T6]]:_(s8), [[T6]]:_(s8))";// CheckASSERT_TRUE(CheckMachineFunction(*MF, CheckStr));}TEST_F(AArch64GISelMITest, LowerSEXTINREG) {setUp();if (!TM)return;// Declare your legalization info, these aren't actually relevant to the test.DefineLegalizerInfo(A, { getActionDefinitionsBuilder(G_SEXT_INREG).legalForTypeWithAnyImm({s64}); });// Build Instrauto MIB = B.buildInstr(TargetOpcode::G_SEXT_INREG, {LLT::scalar(32)},{B.buildInstr(TargetOpcode::G_TRUNC, {LLT::scalar(32)}, {Copies[0]}),uint64_t(8)});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInstr(*MIB);ASSERT_TRUE(Helper.lower(*MIB, 0, LLT()) ==LegalizerHelper::LegalizeResult::Legalized);auto CheckStr = R"(CHECK: [[T1:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[CST:%[0-9]+]]:_(s32) = G_CONSTANT i32 24CHECK: [[T2:%[0-9]+]]:_(s32) = G_SHL [[T1]]:_, [[CST]]:_CHECK: [[T3:%[0-9]+]]:_(s32) = G_ASHR [[T2]]:_, [[CST]]:_)";// CheckASSERT_TRUE(CheckMachineFunction(*MF, CheckStr));}TEST_F(AArch64GISelMITest, LibcallFPExt) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FPEXT).libcallFor({{s32, s16}, {s128, s64}});});LLT S16{LLT::scalar(16)};LLT S32{LLT::scalar(32)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S16, Copies[0]);auto MIBFPExt1 =B.buildInstr(TargetOpcode::G_FPEXT, {S32}, {MIBTrunc});auto MIBFPExt2 =B.buildInstr(TargetOpcode::G_FPEXT, {S128}, {Copies[1]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFPExt1, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFPExt2, DummyLocObserver));auto CheckStr = R"(CHECK: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNCCHECK: $h0 = COPY [[TRUNC]]CHECK: BL &__gnu_h2f_ieeeCHECK: $d0 = COPYCHECK: BL &__extenddftf2)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFPTrunc) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FPTRUNC).libcallFor({{s16, s32}, {s64, s128}});});LLT S16{LLT::scalar(16)};LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBFPTrunc1 =B.buildInstr(TargetOpcode::G_FPTRUNC, {S16}, {MIBTrunc});auto MIBMerge = B.buildMerge(S128, {Copies[1], Copies[2]});auto MIBFPTrunc2 =B.buildInstr(TargetOpcode::G_FPTRUNC, {S64}, {MIBMerge});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFPTrunc1, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFPTrunc2, DummyLocObserver));auto CheckStr = R"(CHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &__gnu_f2h_ieeeCHECK: $q0 = COPYCHECK: BL &__trunctfdf2)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallSimple) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FADD).libcallFor({s16});});LLT S16{LLT::scalar(16)};auto MIBTrunc = B.buildTrunc(S16, Copies[0]);auto MIBFADD =B.buildInstr(TargetOpcode::G_FADD, {S16}, {MIBTrunc, MIBTrunc});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);// Make sure we do not crash anymoreEXPECT_EQ(LegalizerHelper::LegalizeResult::UnableToLegalize,Helper.libcall(*MIBFADD, DummyLocObserver));}TEST_F(AArch64GISelMITest, LibcallSRem) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_SREM).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBSRem32 =B.buildInstr(TargetOpcode::G_SREM, {S32}, {MIBTrunc, MIBTrunc});auto MIBSRem64 =B.buildInstr(TargetOpcode::G_SREM, {S64}, {Copies[0], Copies[0]});auto MIBSRem128 =B.buildInstr(TargetOpcode::G_SREM, {S128}, {MIBExt, MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSRem32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSRem64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSRem128, DummyLocObserver));auto CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $w0 = COPY [[TRUNC]]CHECK: $w1 = COPY [[TRUNC]]CHECK: BL &__modsi3CHECK: $x0 = COPY [[COPY]]CHECK: $x1 = COPY [[COPY]]CHECK: BL &__moddi3CHECK: [[UV:%[0-9]+]]:_(s64), [[UV1:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[ANYEXT]]CHECK: [[UV2:%[0-9]+]]:_(s64), [[UV3:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[ANYEXT]]CHECK: $x0 = COPY [[UV]]CHECK: $x1 = COPY [[UV1]]CHECK: $x2 = COPY [[UV2]]CHECK: $x3 = COPY [[UV3]]CHECK: BL &__modti3)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallURem) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_UREM).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBURem32 =B.buildInstr(TargetOpcode::G_UREM, {S32}, {MIBTrunc, MIBTrunc});auto MIBURem64 =B.buildInstr(TargetOpcode::G_UREM, {S64}, {Copies[0], Copies[0]});auto MIBURem128 =B.buildInstr(TargetOpcode::G_UREM, {S128}, {MIBExt, MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBURem32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBURem64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBURem128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $w0 = COPY [[TRUNC]]CHECK: $w1 = COPY [[TRUNC]]CHECK: BL &__umodsi3CHECK: $x0 = COPY [[COPY]]CHECK: $x1 = COPY [[COPY]]CHECK: BL &__umoddi3CHECK: [[UV:%[0-9]+]]:_(s64), [[UV1:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[ANYEXT]]CHECK: [[UV2:%[0-9]+]]:_(s64), [[UV3:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[ANYEXT]]CHECK: $x0 = COPY [[UV]]CHECK: $x1 = COPY [[UV1]]CHECK: $x2 = COPY [[UV2]]CHECK: $x3 = COPY [[UV3]]CHECK: BL &__umodti3)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallCtlzZeroUndef) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF).libcallFor({{s32, s32}, {s64, s64}, {s128, s128}});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBCtlz32 =B.buildInstr(TargetOpcode::G_CTLZ_ZERO_UNDEF, {S32}, {MIBTrunc});auto MIBCtlz64 =B.buildInstr(TargetOpcode::G_CTLZ_ZERO_UNDEF, {S64}, {Copies[0]});auto MIBCtlz128 =B.buildInstr(TargetOpcode::G_CTLZ_ZERO_UNDEF, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBCtlz32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBCtlz64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBCtlz128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $w0 = COPY [[TRUNC]]CHECK: BL &__clzsi2CHECK: $x0 = COPY [[COPY]]CHECK: BL &__clzdi2CHECK: [[UV:%[0-9]+]]:_(s64), [[UV1:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[ANYEXT]]CHECK: $x0 = COPY [[UV]]CHECK: $x1 = COPY [[UV1]]CHECK: BL &__clzti2)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFAdd) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FADD).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBAdd32 =B.buildInstr(TargetOpcode::G_FADD, {S32}, {MIBTrunc, MIBTrunc});auto MIBAdd64 =B.buildInstr(TargetOpcode::G_FADD, {S64}, {Copies[0], Copies[0]});auto MIBAdd128 = B.buildInstr(TargetOpcode::G_FADD, {S128}, {MIBExt, MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBAdd32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBAdd64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBAdd128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: $s1 = COPY [[TRUNC]]CHECK: BL &__addsf3CHECK: $d0 = COPY [[COPY]]CHECK: $d1 = COPY [[COPY]]CHECK: BL &__adddf3CHECK: $q0 = COPY [[ANYEXT]]CHECK: $q1 = COPY [[ANYEXT]]CHECK: BL &__addtf3)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFSub) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FSUB).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBSub32 =B.buildInstr(TargetOpcode::G_FSUB, {S32}, {MIBTrunc, MIBTrunc});auto MIBSub64 =B.buildInstr(TargetOpcode::G_FSUB, {S64}, {Copies[0], Copies[0]});auto MIBSub128 = B.buildInstr(TargetOpcode::G_FSUB, {S128}, {MIBExt, MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSub32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSub64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSub128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: $s1 = COPY [[TRUNC]]CHECK: BL &__subsf3CHECK: $d0 = COPY [[COPY]]CHECK: $d1 = COPY [[COPY]]CHECK: BL &__subdf3CHECK: $q0 = COPY [[ANYEXT]]CHECK: $q1 = COPY [[ANYEXT]]CHECK: BL &__subtf3)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFMul) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FMUL).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBMul32 =B.buildInstr(TargetOpcode::G_FMUL, {S32}, {MIBTrunc, MIBTrunc});auto MIBMul64 =B.buildInstr(TargetOpcode::G_FMUL, {S64}, {Copies[0], Copies[0]});auto MIBMul128 = B.buildInstr(TargetOpcode::G_FMUL, {S128}, {MIBExt, MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMul32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMul64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMul128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: $s1 = COPY [[TRUNC]]CHECK: BL &__mulsf3CHECK: $d0 = COPY [[COPY]]CHECK: $d1 = COPY [[COPY]]CHECK: BL &__muldf3CHECK: $q0 = COPY [[ANYEXT]]CHECK: $q1 = COPY [[ANYEXT]]CHECK: BL &__multf3)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFDiv) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FDIV).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBDiv32 =B.buildInstr(TargetOpcode::G_FDIV, {S32}, {MIBTrunc, MIBTrunc});auto MIBDiv64 =B.buildInstr(TargetOpcode::G_FDIV, {S64}, {Copies[0], Copies[0]});auto MIBDiv128 = B.buildInstr(TargetOpcode::G_FDIV, {S128}, {MIBExt, MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBDiv32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBDiv64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBDiv128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: $s1 = COPY [[TRUNC]]CHECK: BL &__divsf3CHECK: $d0 = COPY [[COPY]]CHECK: $d1 = COPY [[COPY]]CHECK: BL &__divdf3CHECK: $q0 = COPY [[ANYEXT]]CHECK: $q1 = COPY [[ANYEXT]]CHECK: BL &__divtf3)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFExp) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FEXP).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBExp32 = B.buildInstr(TargetOpcode::G_FEXP, {S32}, {MIBTrunc});auto MIBExp64 = B.buildInstr(TargetOpcode::G_FEXP, {S64}, {Copies[0]});auto MIBExp128 = B.buildInstr(TargetOpcode::G_FEXP, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBExp32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBExp64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBExp128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &expfCHECK: $d0 = COPY [[COPY]]CHECK: BL &expCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &expl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFExp2) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FEXP2).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBExp232 = B.buildInstr(TargetOpcode::G_FEXP2, {S32}, {MIBTrunc});auto MIBExp264 = B.buildInstr(TargetOpcode::G_FEXP2, {S64}, {Copies[0]});auto MIBExp2128 = B.buildInstr(TargetOpcode::G_FEXP2, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBExp232, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBExp264, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBExp2128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &exp2fCHECK: $d0 = COPY [[COPY]]CHECK: BL &exp2CHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &exp2l)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFRem) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FREM).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBFRem32 = B.buildInstr(TargetOpcode::G_FREM, {S32}, {MIBTrunc});auto MIBFRem64 = B.buildInstr(TargetOpcode::G_FREM, {S64}, {Copies[0]});auto MIBFRem128 = B.buildInstr(TargetOpcode::G_FREM, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFRem32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFRem64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFRem128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &fmodfCHECK: $d0 = COPY [[COPY]]CHECK: BL &fmodCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &fmodl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFPow) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FPOW).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBPow32 = B.buildInstr(TargetOpcode::G_FPOW, {S32}, {MIBTrunc});auto MIBPow64 = B.buildInstr(TargetOpcode::G_FPOW, {S64}, {Copies[0]});auto MIBPow128 = B.buildInstr(TargetOpcode::G_FPOW, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBPow32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBPow64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBPow128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &powfCHECK: $d0 = COPY [[COPY]]CHECK: BL &powCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &powl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFMa) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FMA).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBMa32 = B.buildInstr(TargetOpcode::G_FMA, {S32}, {MIBTrunc, MIBTrunc});auto MIBMa64 =B.buildInstr(TargetOpcode::G_FMA, {S64}, {Copies[0], Copies[0]});auto MIBMa128 = B.buildInstr(TargetOpcode::G_FMA, {S128}, {MIBExt, MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LostDebugLocObserver DummyLocObserver("");LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMa32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMa64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMa128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &fmafCHECK: $d0 = COPY [[COPY]]CHECK: BL &fmaCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &fmal)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFCeil) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FCEIL).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBCeil32 = B.buildInstr(TargetOpcode::G_FCEIL, {S32}, {MIBTrunc});auto MIBCeil64 = B.buildInstr(TargetOpcode::G_FCEIL, {S64}, {Copies[0]});auto MIBCeil128 = B.buildInstr(TargetOpcode::G_FCEIL, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBCeil32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBCeil64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBCeil128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &ceilfCHECK: $d0 = COPY [[COPY]]CHECK: BL &ceilCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &ceill)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFFloor) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FFLOOR).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBFloor32 = B.buildInstr(TargetOpcode::G_FFLOOR, {S32}, {MIBTrunc});auto MIBFloor64 = B.buildInstr(TargetOpcode::G_FFLOOR, {S64}, {Copies[0]});auto MIBFloor128 = B.buildInstr(TargetOpcode::G_FFLOOR, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFloor32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFloor64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBFloor128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &floorfCHECK: $d0 = COPY [[COPY]]CHECK: BL &floorCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &floorl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFMinNum) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FMINNUM).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBMin32 = B.buildFMinNum(S32, MIBTrunc, MIBTrunc);auto MIBMin64 = B.buildFMinNum(S64, Copies[0], Copies[0]);auto MIBMin128 = B.buildFMinNum(S128, MIBExt, MIBExt);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMin32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMin64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMin128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: $s1 = COPY [[TRUNC]]CHECK: BL &fminfCHECK: $d0 = COPY [[COPY]]CHECK: $d1 = COPY [[COPY]]CHECK: BL &fminCHECK: $q0 = COPY [[ANYEXT]]CHECK: $q1 = COPY [[ANYEXT]]CHECK: BL &fminl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFMaxNum) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FMAXNUM).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBMax32 = B.buildFMaxNum(S32, MIBTrunc, MIBTrunc);auto MIBMax64 = B.buildFMaxNum(S64, Copies[0], Copies[0]);auto MIBMax128 = B.buildFMaxNum(S128, MIBExt, MIBExt);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMax32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMax64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBMax128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: $s1 = COPY [[TRUNC]]CHECK: BL &fmaxfCHECK: $d0 = COPY [[COPY]]CHECK: $d1 = COPY [[COPY]]CHECK: BL &fmaxCHECK: $q0 = COPY [[ANYEXT]]CHECK: $q1 = COPY [[ANYEXT]]CHECK: BL &fmaxl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFSqrt) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FSQRT).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBSqrt32 = B.buildInstr(TargetOpcode::G_FSQRT, {S32}, {MIBTrunc});auto MIBSqrt64 = B.buildInstr(TargetOpcode::G_FSQRT, {S64}, {Copies[0]});auto MIBSqrt128 = B.buildInstr(TargetOpcode::G_FSQRT, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSqrt32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSqrt64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBSqrt128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &sqrtfCHECK: $d0 = COPY [[COPY]]CHECK: BL &sqrtCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &sqrtl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFRint) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FRINT).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBRint32 = B.buildInstr(TargetOpcode::G_FRINT, {S32}, {MIBTrunc});auto MIBRint64 = B.buildInstr(TargetOpcode::G_FRINT, {S64}, {Copies[0]});auto MIBRint128 = B.buildInstr(TargetOpcode::G_FRINT, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBRint32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBRint64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBRint128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &rintfCHECK: $d0 = COPY [[COPY]]CHECK: BL &rintCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &rintl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LibcallFNearbyInt) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_FNEARBYINT).libcallFor({s32, s64, s128});});LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};auto MIBTrunc = B.buildTrunc(S32, Copies[0]);auto MIBExt = B.buildAnyExt(S128, Copies[0]);auto MIBNearbyInt32 =B.buildInstr(TargetOpcode::G_FNEARBYINT, {S32}, {MIBTrunc});auto MIBNearbyInt64 =B.buildInstr(TargetOpcode::G_FNEARBYINT, {S64}, {Copies[0]});auto MIBNearbyInt128 =B.buildInstr(TargetOpcode::G_FNEARBYINT, {S128}, {MIBExt});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);LostDebugLocObserver DummyLocObserver("");EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBNearbyInt32, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBNearbyInt64, DummyLocObserver));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.libcall(*MIBNearbyInt128, DummyLocObserver));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNCCHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXTCHECK: $s0 = COPY [[TRUNC]]CHECK: BL &nearbyintfCHECK: $d0 = COPY [[COPY]]CHECK: BL &nearbyintCHECK: $q0 = COPY [[ANYEXT]]CHECK: BL &nearbyintl)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, NarrowScalarExtract) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {getActionDefinitionsBuilder(G_UNMERGE_VALUES).legalFor({{s32, s64}});getActionDefinitionsBuilder(G_EXTRACT).legalForTypeWithAnyImm({{s16, s32}});});LLT S16{LLT::scalar(16)};LLT S32{LLT::scalar(32)};auto MIBExtractS32 = B.buildExtract(S32, Copies[1], 32);auto MIBExtractS16 = B.buildExtract(S16, Copies[1], 0);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*MIBExtractS32, 1, S32));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*MIBExtractS16, 1, S32));const auto *CheckStr = R"(CHECK: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUESCHECK: [[COPY:%[0-9]+]]:_(s32) = COPY [[UV1]]CHECK: [[UV3:%[0-9]+]]:_(s32), [[UV4:%[0-9]+]]:_(s32) = G_UNMERGE_VALUESCHECK: [[EXTR:%[0-9]+]]:_(s16) = G_EXTRACT [[UV3]]:_(s32), 0CHECK: [[COPY:%[0-9]+]]:_(s16) = COPY [[EXTR]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, LowerInsert) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, { getActionDefinitionsBuilder(G_INSERT).lower(); });LLT S32{LLT::scalar(32)};LLT S64{LLT::scalar(64)};LLT P0{LLT::pointer(0, 64)};LLT P1{LLT::pointer(1, 32)};LLT V2S32{LLT::fixed_vector(2, 32)};auto TruncS32 = B.buildTrunc(S32, Copies[0]);auto IntToPtrP0 = B.buildIntToPtr(P0, Copies[0]);auto IntToPtrP1 = B.buildIntToPtr(P1, TruncS32);auto BitcastV2S32 = B.buildBitcast(V2S32, Copies[0]);auto InsertS64S32 = B.buildInsert(S64, Copies[0], TruncS32, 0);auto InsertS64P1 = B.buildInsert(S64, Copies[0], IntToPtrP1, 8);auto InsertP0S32 = B.buildInsert(P0, IntToPtrP0, TruncS32, 16);auto InsertP0P1 = B.buildInsert(P0, IntToPtrP0, IntToPtrP1, 4);auto InsertV2S32S32 = B.buildInsert(V2S32, BitcastV2S32, TruncS32, 32);auto InsertV2S32P1 = B.buildInsert(V2S32, BitcastV2S32, IntToPtrP1, 0);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*InsertS64S32, 0, LLT{}));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*InsertS64P1, 0, LLT{}));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*InsertP0S32, 0, LLT{}));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*InsertP0P1, 0, LLT{}));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*InsertV2S32S32, 0, LLT{}));EXPECT_EQ(LegalizerHelper::LegalizeResult::UnableToLegalize,Helper.lower(*InsertV2S32P1, 0, LLT{}));const auto *CheckStr = R"(CHECK: [[S64:%[0-9]+]]:_(s64) = COPYCHECK: [[S32:%[0-9]+]]:_(s32) = G_TRUNC [[S64]]CHECK: [[P0:%[0-9]+]]:_(p0) = G_INTTOPTR [[S64]]CHECK: [[P1:%[0-9]+]]:_(p1) = G_INTTOPTR [[S32]]CHECK: [[V2S32:%[0-9]+]]:_(<2 x s32>) = G_BITCAST [[S64]]CHECK: [[ZEXT:%[0-9]+]]:_(s64) = G_ZEXT [[S32]]CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANTCHECK: [[AND:%[0-9]+]]:_(s64) = G_AND [[S64]]:_, [[C]]:_CHECK: [[OR:%[0-9]+]]:_(s64) = G_OR [[AND]]:_, [[ZEXT]]:_CHECK: [[PTRTOINT:%[0-9]+]]:_(s32) = G_PTRTOINT [[P1]]CHECK: [[ZEXT:%[0-9]+]]:_(s64) = G_ZEXT [[PTRTOINT]]CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANTCHECK: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[ZEXT]]:_, [[C]]:_(s64)CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANTCHECK: [[AND:%[0-9]+]]:_(s64) = G_AND [[S64]]:_, [[C]]:_CHECK: [[OR:%[0-9]+]]:_(s64) = G_OR [[AND]]:_, [[SHL]]:_CHECK: [[PTRTOINT:%[0-9]+]]:_(s64) = G_PTRTOINT [[P0]]CHECK: [[ZEXT:%[0-9]+]]:_(s64) = G_ZEXT [[S32]]CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANTCHECK: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[ZEXT]]:_, [[C]]:_(s64)CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANTCHECK: [[AND:%[0-9]+]]:_(s64) = G_AND [[PTRTOINT]]:_, [[C]]:_CHECK: [[OR:%[0-9]+]]:_(s64) = G_OR [[AND]]:_, [[SHL]]:_CHECK: [[INTTOPTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[OR]]CHECK: [[PTRTOINT:%[0-9]+]]:_(s64) = G_PTRTOINT [[P0]]CHECK: [[PTRTOINT1:%[0-9]+]]:_(s32) = G_PTRTOINT [[P1]]CHECK: [[ZEXT:%[0-9]+]]:_(s64) = G_ZEXT [[PTRTOINT1]]CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANTCHECK: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[ZEXT]]:_, [[C]]:_(s64)CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANTCHECK: [[AND:%[0-9]+]]:_(s64) = G_AND [[PTRTOINT]]:_, [[C]]:_CHECK: [[OR:%[0-9]+]]:_(s64) = G_OR [[AND]]:_, [[SHL]]:_CHECK: [[INTTOPTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[OR]]CHECK: [[V2S32_E0:%[0-9]+]]:_(s32), [[V2S32_E1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[V2S32]]CHECK: [[BV:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[V2S32_E0]]:_(s32), [[S32]]:_(s32))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test lowering of G_FFLOORTEST_F(AArch64GISelMITest, LowerFFloor) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, {});// Build Instrauto Floor = B.buildFFloor(LLT::scalar(64), Copies[0], MachineInstr::MIFlag::FmNoInfs);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*Floor, 0, LLT()));auto CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s64) = ninf G_INTRINSIC_TRUNC [[COPY]]CHECK: [[ZERO:%[0-9]+]]:_(s64) = G_FCONSTANT double 0.000000e+00CHECK: [[CMP0:%[0-9]+]]:_(s1) = ninf G_FCMP floatpred(olt), [[COPY]]:_(s64), [[ZERO]]:_CHECK: [[CMP1:%[0-9]+]]:_(s1) = ninf G_FCMP floatpred(one), [[COPY]]:_(s64), [[TRUNC]]:_CHECK: [[AND:%[0-9]+]]:_(s1) = G_AND [[CMP0]]:_, [[CMP1]]:_CHECK: [[ITOFP:%[0-9]+]]:_(s64) = G_SITOFP [[AND]]= ninf G_FADD [[TRUNC]]:_, [[ITOFP]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test lowering of G_BSWAPTEST_F(AArch64GISelMITest, LowerBSWAP) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});// Make sure vector lowering doesn't assert.auto Cast = B.buildBitcast(LLT::fixed_vector(2, 32), Copies[0]);auto BSwap = B.buildBSwap(LLT::fixed_vector(2, 32), Cast);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*BSwap, 0, LLT()));auto CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[VEC:%[0-9]+]]:_(<2 x s32>) = G_BITCAST [[COPY]]CHECK: [[K24:%[0-9]+]]:_(s32) = G_CONSTANT i32 24CHECK: [[SPLAT24:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[K24]]:_(s32), [[K24]]:_(s32)CHECK: [[SHL0:%[0-9]+]]:_(<2 x s32>) = G_SHL [[VEC]]:_, [[SPLAT24]]CHECK: [[SHR0:%[0-9]+]]:_(<2 x s32>) = G_LSHR [[VEC]]:_, [[SPLAT24]]CHECK: [[OR0:%[0-9]+]]:_(<2 x s32>) = G_OR [[SHR0]]:_, [[SHL0]]:_CHECK: [[KMASK:%[0-9]+]]:_(s32) = G_CONSTANT i32 65280CHECK: [[SPLATMASK:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[KMASK]]:_(s32), [[KMASK]]:_(s32)CHECK: [[K8:%[0-9]+]]:_(s32) = G_CONSTANT i32 8CHECK: [[SPLAT8:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[K8]]:_(s32), [[K8]]:_(s32)CHECK: [[AND0:%[0-9]+]]:_(<2 x s32>) = G_AND [[VEC]]:_, [[SPLATMASK]]:_CHECK: [[SHL1:%[0-9]+]]:_(<2 x s32>) = G_SHL [[AND0]]:_, [[SPLAT8]]CHECK: [[OR1:%[0-9]+]]:_(<2 x s32>) = G_OR [[OR0]]:_, [[SHL1]]:_CHECK: [[SHR1:%[0-9]+]]:_(<2 x s32>) = G_LSHR [[VEC]]:_, [[SPLAT8]]CHECK: [[AND1:%[0-9]+]]:_(<2 x s32>) = G_AND [[SHR1]]:_, [[SPLATMASK]]:_CHECK: [[BSWAP:%[0-9]+]]:_(<2 x s32>) = G_OR [[OR1]]:_, [[AND1]]:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test lowering of G_SDIVREM into G_SDIV and G_SREMTEST_F(AArch64GISelMITest, LowerSDIVREM) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, { getActionDefinitionsBuilder(G_SDIVREM).lowerFor({s64}); });LLT S64{LLT::scalar(64)};// Build Instrauto SDivrem =B.buildInstr(TargetOpcode::G_SDIVREM, {S64, S64}, {Copies[0], Copies[1]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*SDivrem, 0, S64));const auto *CheckStr = R"(CHECK: [[DIV:%[0-9]+]]:_(s64) = G_SDIV %0:_, %1:_CHECK: [[REM:%[0-9]+]]:_(s64) = G_SREM %0:_, %1:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test lowering of G_UDIVREM into G_UDIV and G_UREMTEST_F(AArch64GISelMITest, LowerUDIVREM) {setUp();if (!TM)return;// Declare your legalization infoDefineLegalizerInfo(A, { getActionDefinitionsBuilder(G_UDIVREM).lowerFor({s64}); });LLT S64{LLT::scalar(64)};// Build Instrauto UDivrem =B.buildInstr(TargetOpcode::G_UDIVREM, {S64, S64}, {Copies[0], Copies[1]});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.lower(*UDivrem, 0, S64));const auto *CheckStr = R"(CHECK: [[DIV:%[0-9]+]]:_(s64) = G_UDIV %0:_, %1:_CHECK: [[REM:%[0-9]+]]:_(s64) = G_UREM %0:_, %1:_)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test widening of G_UNMERGE_VALUESTEST_F(AArch64GISelMITest, WidenUnmerge) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});// Check that widening G_UNMERGE_VALUES to a larger type than the source type// works as expectedLLT P0{LLT::pointer(0, 64)};LLT S32{LLT::scalar(32)};LLT S96{LLT::scalar(96)};auto IntToPtr = B.buildIntToPtr(P0, Copies[0]);auto UnmergePtr = B.buildUnmerge(S32, IntToPtr);auto UnmergeScalar = B.buildUnmerge(S32, Copies[0]);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*UnmergePtr, 0, S96));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*UnmergeScalar, 0, S96));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[PTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[COPY]]CHECK: [[INT:%[0-9]+]]:_(s64) = G_PTRTOINT [[PTR]]CHECK: [[ANYEXT:%[0-9]+]]:_(s96) = G_ANYEXT [[INT]]CHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[ANYEXT]]CHECK: [[C:%[0-9]+]]:_(s96) = G_CONSTANT i96 32CHECK: [[LSHR:%[0-9]+]]:_(s96) = G_LSHR [[ANYEXT]]:_, [[C]]CHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[LSHR]]CHECK: [[ANYEXT:%[0-9]+]]:_(s96) = G_ANYEXT [[COPY]]CHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[ANYEXT]]CHECK: [[C:%[0-9]+]]:_(s96) = G_CONSTANT i96 32CHECK: [[LSHR:%[0-9]+]]:_(s96) = G_LSHR [[ANYEXT]]:_, [[C]]CHECK: [[TRUNC1:%[0-9]+]]:_(s32) = G_TRUNC [[LSHR]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BitcastLoad) {setUp();if (!TM)return;LLT P0 = LLT::pointer(0, 64);LLT S32 = LLT::scalar(32);LLT V4S8 = LLT::fixed_vector(4, 8);auto Ptr = B.buildUndef(P0);DefineLegalizerInfo(A, {});MachineMemOperand *MMO = B.getMF().getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad, 4, Align(4));auto Load = B.buildLoad(V4S8, Ptr, *MMO);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;B.setInsertPt(*EntryMBB, Load->getIterator());LegalizerHelper Helper(*MF, Info, Observer, B);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.bitcast(*Load, 0, S32));auto CheckStr = R"(CHECK: [[PTR:%[0-9]+]]:_(p0) = G_IMPLICIT_DEFCHECK: [[LOAD:%[0-9]+]]:_(s32) = G_LOADCHECK: [[CAST:%[0-9]+]]:_(<4 x s8>) = G_BITCAST [[LOAD]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BitcastStore) {setUp();if (!TM)return;LLT P0 = LLT::pointer(0, 64);LLT S32 = LLT::scalar(32);LLT V4S8 = LLT::fixed_vector(4, 8);auto Ptr = B.buildUndef(P0);DefineLegalizerInfo(A, {});MachineMemOperand *MMO = B.getMF().getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOStore, 4, Align(4));auto Val = B.buildUndef(V4S8);auto Store = B.buildStore(Val, Ptr, *MMO);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInsertPt(*EntryMBB, Store->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.bitcast(*Store, 0, S32));auto CheckStr = R"(CHECK: [[VAL:%[0-9]+]]:_(<4 x s8>) = G_IMPLICIT_DEFCHECK: [[CAST:%[0-9]+]]:_(s32) = G_BITCAST [[VAL]]CHECK: G_STORE [[CAST]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, BitcastSelect) {setUp();if (!TM)return;LLT S1 = LLT::scalar(1);LLT S32 = LLT::scalar(32);LLT V4S8 = LLT::fixed_vector(4, 8);DefineLegalizerInfo(A, {});auto Cond = B.buildUndef(S1);auto Val0 = B.buildConstant(V4S8, 123);auto Val1 = B.buildConstant(V4S8, 99);auto Select = B.buildSelect(V4S8, Cond, Val0, Val1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInsertPt(*EntryMBB, Select->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.bitcast(*Select, 0, S32));auto CheckStr = R"(CHECK: [[VAL0:%[0-9]+]]:_(<4 x s8>) = G_BUILD_VECTORCHECK: [[VAL1:%[0-9]+]]:_(<4 x s8>) = G_BUILD_VECTORCHECK: [[CAST0:%[0-9]+]]:_(s32) = G_BITCAST [[VAL0]]CHECK: [[CAST1:%[0-9]+]]:_(s32) = G_BITCAST [[VAL1]]CHECK: [[SELECT:%[0-9]+]]:_(s32) = G_SELECT %{{[0-9]+}}:_(s1), [[CAST0]]:_, [[CAST1]]:_CHECK: [[CAST2:%[0-9]+]]:_(<4 x s8>) = G_BITCAST [[SELECT]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;// Doesn't make senseauto VCond = B.buildUndef(LLT::fixed_vector(4, 1));auto VSelect = B.buildSelect(V4S8, VCond, Val0, Val1);B.setInsertPt(*EntryMBB, VSelect->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::UnableToLegalize,Helper.bitcast(*VSelect, 0, S32));EXPECT_EQ(LegalizerHelper::LegalizeResult::UnableToLegalize,Helper.bitcast(*VSelect, 1, LLT::scalar(4)));}TEST_F(AArch64GISelMITest, BitcastBitOps) {setUp();if (!TM)return;LLT S32 = LLT::scalar(32);LLT V4S8 = LLT::fixed_vector(4, 8);DefineLegalizerInfo(A, {});auto Val0 = B.buildConstant(V4S8, 123);auto Val1 = B.buildConstant(V4S8, 99);auto And = B.buildAnd(V4S8, Val0, Val1);auto Or = B.buildOr(V4S8, Val0, Val1);auto Xor = B.buildXor(V4S8, Val0, Val1);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);B.setInsertPt(*EntryMBB, And->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.bitcast(*And, 0, S32));B.setInsertPt(*EntryMBB, Or->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.bitcast(*Or, 0, S32));B.setInsertPt(*EntryMBB, Xor->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.bitcast(*Xor, 0, S32));auto CheckStr = R"(CHECK: [[VAL0:%[0-9]+]]:_(<4 x s8>) = G_BUILD_VECTORCHECK: [[VAL1:%[0-9]+]]:_(<4 x s8>) = G_BUILD_VECTORCHECK: [[CAST0:%[0-9]+]]:_(s32) = G_BITCAST [[VAL0]]CHECK: [[CAST1:%[0-9]+]]:_(s32) = G_BITCAST [[VAL1]]CHECK: [[AND:%[0-9]+]]:_(s32) = G_AND [[CAST0]]:_, [[CAST1]]:_CHECK: [[CAST_AND:%[0-9]+]]:_(<4 x s8>) = G_BITCAST [[AND]]CHECK: [[CAST2:%[0-9]+]]:_(s32) = G_BITCAST [[VAL0]]CHECK: [[CAST3:%[0-9]+]]:_(s32) = G_BITCAST [[VAL1]]CHECK: [[OR:%[0-9]+]]:_(s32) = G_OR [[CAST2]]:_, [[CAST3]]:_CHECK: [[CAST_OR:%[0-9]+]]:_(<4 x s8>) = G_BITCAST [[OR]]CHECK: [[CAST4:%[0-9]+]]:_(s32) = G_BITCAST [[VAL0]]CHECK: [[CAST5:%[0-9]+]]:_(s32) = G_BITCAST [[VAL1]]CHECK: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[CAST4]]:_, [[CAST5]]:_CHECK: [[CAST_XOR:%[0-9]+]]:_(<4 x s8>) = G_BITCAST [[XOR]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, CreateLibcall) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LLVMContext &Ctx = MF->getFunction().getContext();auto *RetTy = Type::getVoidTy(Ctx);EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,createLibcall(B, "abort", {{}, RetTy, 0}, {}, CallingConv::C));auto CheckStr = R"(CHECK: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $spCHECK: BL &abortCHECK: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp)";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test narrowing of G_IMPLICIT_DEFTEST_F(AArch64GISelMITest, NarrowImplicitDef) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});// Make sure that G_IMPLICIT_DEF can be narrowed if the original size is not a// multiple of narrow sizeLLT S32{LLT::scalar(32)};LLT S48{LLT::scalar(48)};LLT S64{LLT::scalar(64)};LLT V2S64{{LLT::fixed_vector(2, 64)}};auto Implicit1 = B.buildUndef(S64);auto Implicit2 = B.buildUndef(S64);auto Implicit3 = B.buildUndef(V2S64);auto Implicit4 = B.buildUndef(V2S64);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, Implicit1->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*Implicit1, 0, S48));B.setInsertPt(*EntryMBB, Implicit2->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*Implicit2, 0, S32));B.setInsertPt(*EntryMBB, Implicit3->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*Implicit3, 0, S48));B.setInsertPt(*EntryMBB, Implicit4->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*Implicit4, 0, S32));const auto *CheckStr = R"(CHECK: [[DEF:%[0-9]+]]:_(s48) = G_IMPLICIT_DEFCHECK: [[ANYEXT:%[0-9]+]]:_(s64) = G_ANYEXT [[DEF]]CHECK: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[DEF1:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[DEF]]:_(s32), [[DEF1]]CHECK: [[DEF:%[0-9]+]]:_(<2 x s48>) = G_IMPLICIT_DEFCHECK: [[ANYEXT:%[0-9]+]]:_(<2 x s64>) = G_ANYEXT [[DEF]]CHECK: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[DEF1:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[DEF2:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[DEF3:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[BV:%[0-9]+]]:_(<2 x s64>) = G_BUILD_VECTOR [[DEF]]:_(s32), [[DEF1]]:_(s32), [[DEF2]]:_(s32), [[DEF3]]:_(s32))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test widening of G_FREEZETEST_F(AArch64GISelMITest, WidenFreeze) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});// Make sure that G_FREEZE is widened with anyextLLT S64{LLT::scalar(64)};LLT S128{LLT::scalar(128)};LLT V2S32{LLT::fixed_vector(2, 32)};LLT V2S64{LLT::fixed_vector(2, 64)};auto Vector = B.buildBitcast(V2S32, Copies[0]);auto FreezeScalar = B.buildInstr(TargetOpcode::G_FREEZE, {S64}, {Copies[0]});auto FreezeVector = B.buildInstr(TargetOpcode::G_FREEZE, {V2S32}, {Vector});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, FreezeScalar->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*FreezeScalar, 0, S128));B.setInsertPt(*EntryMBB, FreezeVector->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*FreezeVector, 0, V2S64));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[BITCAST:%[0-9]+]]:_(<2 x s32>) = G_BITCAST [[COPY]]CHECK: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXT [[COPY]]CHECK: [[FREEZE:%[0-9]+]]:_(s128) = G_FREEZE [[ANYEXT]]CHECK: [[TRUNC:%[0-9]+]]:_(s64) = G_TRUNC [[FREEZE]]CHECK: [[ANYEXT1:%[0-9]+]]:_(<2 x s64>) = G_ANYEXT [[BITCAST]]CHECK: [[FREEZE1:%[0-9]+]]:_(<2 x s64>) = G_FREEZE [[ANYEXT1]]CHECK: [[TRUNC1:%[0-9]+]]:_(<2 x s32>) = G_TRUNC [[FREEZE1]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test narrowing of G_FREEZETEST_F(AArch64GISelMITest, NarrowFreeze) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});// Make sure that G_FREEZE is narrowed using unmerge/extractLLT S32{LLT::scalar(32)};LLT S33{LLT::scalar(33)};LLT S48{LLT::scalar(48)};LLT S64{LLT::scalar(64)};LLT V2S16{LLT::fixed_vector(2, 16)};LLT V3S16{LLT::fixed_vector(3, 16)};LLT V4S16{LLT::fixed_vector(4, 16)};auto Trunc = B.buildTrunc(S33, {Copies[0]});auto Trunc1 = B.buildTrunc(S48, {Copies[0]});auto Vector = B.buildBitcast(V3S16, Trunc1);auto FreezeScalar = B.buildInstr(TargetOpcode::G_FREEZE, {S64}, {Copies[0]});auto FreezeOdd = B.buildInstr(TargetOpcode::G_FREEZE, {S33}, {Trunc});auto FreezeVector = B.buildInstr(TargetOpcode::G_FREEZE, {V3S16}, {Vector});auto FreezeVector1 = B.buildInstr(TargetOpcode::G_FREEZE, {V3S16}, {Vector});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, FreezeScalar->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalar(*FreezeScalar, 0, S32));// This should be followed by narrowScalar to S32.B.setInsertPt(*EntryMBB, FreezeOdd->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*FreezeOdd, 0, S64));B.setInsertPt(*EntryMBB, FreezeVector->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.fewerElementsVector(*FreezeVector, 0, V2S16));// This should be followed by fewerElements to V2S16.B.setInsertPt(*EntryMBB, FreezeVector1->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.moreElementsVector(*FreezeVector1, 0, V4S16));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[TRUNC:%[0-9]+]]:_(s33) = G_TRUNC [[COPY]]CHECK: [[TRUNC1:%[0-9]+]]:_(s48) = G_TRUNC [[COPY]]CHECK: [[BITCAST:%[0-9]+]]:_(<3 x s16>) = G_BITCAST [[TRUNC1]]CHECK: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY]]CHECK: [[FREEZE:%[0-9]+]]:_(s32) = G_FREEZE [[UV]]CHECK: [[FREEZE1:%[0-9]+]]:_(s32) = G_FREEZE [[UV1]]CHECK: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[FREEZE]]:_(s32), [[FREEZE1]]CHECK: [[ANYEXT:%[0-9]+]]:_(s64) = G_ANYEXT [[TRUNC]]CHECK: [[FREEZE2:%[0-9]+]]:_(s64) = G_FREEZE [[ANYEXT]]CHECK: [[TRUNC1:%[0-9]+]]:_(s33) = G_TRUNC [[FREEZE2]]CHECK: [[UV2:%[0-9]+]]:_(s16), [[UV3:%[0-9]+]]:_(s16), [[UV4:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[BITCAST]]CHECK: [[BV:%[0-9]+]]:_(<2 x s16>) = G_BUILD_VECTOR [[UV2]]:_(s16), [[UV3]]:_(s16)CHECK: [[FREEZE3:%[0-9]+]]:_(<2 x s16>) = G_FREEZE [[BV]]CHECK: [[FREEZE4:%[0-9]+]]:_(s16) = G_FREEZE [[UV4]]CHECK: [[FREEZE3_E0:%[0-9]+]]:_(s16), [[FREEZE3_E1:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[FREEZE3]]CHECK: [[BV1:%[0-9]+]]:_(<3 x s16>) = G_BUILD_VECTOR [[FREEZE3_E0]]:_(s16), [[FREEZE3_E1]]:_(s16), [[FREEZE4]]:_(s16)CHECK: [[UV5:%[0-9]+]]:_(s16), [[UV6:%[0-9]+]]:_(s16), [[UV7:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[BITCAST]]CHECK: [[IMP_DEF:%[0-9]+]]:_(s16) = G_IMPLICIT_DEFCHECK: [[BV1:%[0-9]+]]:_(<4 x s16>) = G_BUILD_VECTOR [[UV5]]:_(s16), [[UV6]]:_(s16), [[UV7]]:_(s16), [[IMP_DEF]]:_(s16)CHECK: [[FREEZE5:%[0-9]+]]:_(<4 x s16>) = G_FREEZE [[BV1]]CHECK: [[FREEZE5_E0:%[0-9]+]]:_(s16), [[FREEZE5_E1:%[0-9]+]]:_(s16), [[FREEZE5_E2:%[0-9]+]]:_(s16), [[FREEZE5_E3:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[FREEZE5]]CHECK: [[BV2:%[0-9]+]]:_(<3 x s16>) = G_BUILD_VECTOR [[FREEZE5_E0]]:_(s16), [[FREEZE5_E1]]:_(s16), [[FREEZE5_E2]]:_(s16))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test fewer elements of G_FREEZETEST_F(AArch64GISelMITest, FewerElementsFreeze) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});LLT S32{LLT::scalar(32)};LLT V2S16{LLT::fixed_vector(2, 16)};LLT V2S32{LLT::fixed_vector(2, 32)};LLT V4S16{LLT::fixed_vector(4, 16)};auto Vector1 = B.buildBitcast(V2S32, Copies[0]);auto Vector2 = B.buildBitcast(V4S16, Copies[0]);auto FreezeVector1 = B.buildInstr(TargetOpcode::G_FREEZE, {V2S32}, {Vector1});auto FreezeVector2 = B.buildInstr(TargetOpcode::G_FREEZE, {V4S16}, {Vector2});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, FreezeVector1->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.fewerElementsVector(*FreezeVector1, 0, S32));B.setInsertPt(*EntryMBB, FreezeVector2->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.fewerElementsVector(*FreezeVector2, 0, V2S16));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[BITCAST:%[0-9]+]]:_(<2 x s32>) = G_BITCAST [[COPY]]CHECK: [[BITCAST1:%[0-9]+]]:_(<4 x s16>) = G_BITCAST [[COPY]]CHECK: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[BITCAST]]CHECK: [[FREEZE:%[0-9]+]]:_(s32) = G_FREEZE [[UV]]CHECK: [[FREEZE1:%[0-9]+]]:_(s32) = G_FREEZE [[UV1]]CHECK: [[MV:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[FREEZE]]:_(s32), [[FREEZE1]]CHECK: [[UV:%[0-9]+]]:_(<2 x s16>), [[UV1:%[0-9]+]]:_(<2 x s16>) = G_UNMERGE_VALUES [[BITCAST1]]CHECK: [[FREEZE2:%[0-9]+]]:_(<2 x s16>) = G_FREEZE [[UV]]CHECK: [[FREEZE3:%[0-9]+]]:_(<2 x s16>) = G_FREEZE [[UV1]]CHECK: [[MV:%[0-9]+]]:_(<4 x s16>) = G_CONCAT_VECTORS [[FREEZE2]]:_(<2 x s16>), [[FREEZE3]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test more elements of G_FREEZETEST_F(AArch64GISelMITest, MoreElementsFreeze) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});LLT V2S32{LLT::fixed_vector(2, 32)};LLT V4S32{LLT::fixed_vector(4, 32)};auto Vector1 = B.buildBitcast(V2S32, Copies[0]);auto FreezeVector1 = B.buildInstr(TargetOpcode::G_FREEZE, {V2S32}, {Vector1});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, FreezeVector1->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.moreElementsVector(*FreezeVector1, 0, V4S32));const auto *CheckStr = R"(CHECK: [[COPY:%[0-9]+]]:_(s64) = COPYCHECK: [[BITCAST:%[0-9]+]]:_(<2 x s32>) = G_BITCAST [[COPY]]CHECK: [[BITCAST_E0:%[0-9]+]]:_(s32), [[BITCAST_E1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[BITCAST]]:_(<2 x s32>)CHECK: [[IMP_DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEFCHECK: [[BITCAST_LARGE:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[BITCAST_E0]]:_(s32), [[BITCAST_E1]]:_(s32), [[IMP_DEF]]:_(s32), [[IMP_DEF]]:_(s32)CHECK: [[FREEZE:%[0-9]+]]:_(<4 x s32>) = G_FREEZE [[BITCAST_LARGE]]CHECK: [[FREEZE_E0:%[0-9]+]]:_(s32), [[FREEZE_E1:%[0-9]+]]:_(s32), [[FREEZE_E2:%[0-9]+]]:_(s32), [[FREEZE_E3:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[FREEZE]]:_(<4 x s32>)CHECK: (<2 x s32>) = G_BUILD_VECTOR [[FREEZE_E0]]:_(s32), [[FREEZE_E1]]:_(s32))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test fewer elements of G_INSERT_VECTOR_ELEMENTTEST_F(AArch64GISelMITest, FewerElementsInsertVectorElt) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});LLT P0{LLT::pointer(0, 64)};LLT S64{LLT::scalar(64)};LLT S16{LLT::scalar(16)};LLT V2S16{LLT::fixed_vector(2, 16)};LLT V3S16{LLT::fixed_vector(3, 16)};LLT V8S16{LLT::fixed_vector(8, 16)};auto Ptr0 = B.buildIntToPtr(P0, Copies[0]);auto VectorV8 = B.buildLoad(V8S16, Ptr0, MachinePointerInfo(), Align(8));auto Value = B.buildTrunc(S16, Copies[1]);auto Seven = B.buildConstant(S64, 7);auto InsertV8Constant7_0 =B.buildInsertVectorElement(V8S16, VectorV8, Value, Seven);auto InsertV8Constant7_1 =B.buildInsertVectorElement(V8S16, VectorV8, Value, Seven);B.buildStore(InsertV8Constant7_0, Ptr0, MachinePointerInfo(), Align(8),MachineMemOperand::MOVolatile);B.buildStore(InsertV8Constant7_1, Ptr0, MachinePointerInfo(), Align(8),MachineMemOperand::MOVolatile);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, InsertV8Constant7_0->getIterator());// This should index the high element of the 4th piece of an unmerge.EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.fewerElementsVector(*InsertV8Constant7_0, 0, V2S16));// This case requires extracting an intermediate vector type into the target// v4s16.B.setInsertPt(*EntryMBB, InsertV8Constant7_1->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.fewerElementsVector(*InsertV8Constant7_1, 0, V3S16));const auto *CheckStr = R"(CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY1:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY2:%[0-9]+]]:_(s64) = COPYCHECK: [[PTR0:%[0-9]+]]:_(p0) = G_INTTOPTR [[COPY0]]CHECK: [[VEC8:%[0-9]+]]:_(<8 x s16>) = G_LOAD [[PTR0]]:_(p0) :: (load (<8 x s16>), align 8)CHECK: [[INSERT_VAL:%[0-9]+]]:_(s16) = G_TRUNC [[COPY1]]CHECK: [[UNMERGE0:%[0-9]+]]:_(<2 x s16>), [[UNMERGE1:%[0-9]+]]:_(<2 x s16>), [[UNMERGE2:%[0-9]+]]:_(<2 x s16>), [[UNMERGE3:%[0-9]+]]:_(<2 x s16>) = G_UNMERGE_VALUES [[VEC8]]CHECK: [[ONE:%[0-9]+]]:_(s64) = G_CONSTANT i64 1CHECK: [[SUB_INSERT_7:%[0-9]+]]:_(<2 x s16>) = G_INSERT_VECTOR_ELT [[UNMERGE3]]:_, [[INSERT_VAL]]:_(s16), [[ONE]]CHECK: [[INSERT_V8_7_0:%[0-9]+]]:_(<8 x s16>) = G_CONCAT_VECTORS [[UNMERGE0]]:_(<2 x s16>), [[UNMERGE1]]:_(<2 x s16>), [[UNMERGE2]]:_(<2 x s16>), [[SUB_INSERT_7]]:_(<2 x s16>)CHECK: [[UNMERGE1_0:%[0-9]+]]:_(s16), [[UNMERGE1_1:%[0-9]+]]:_(s16), [[UNMERGE1_2:%[0-9]+]]:_(s16), [[UNMERGE1_3:%[0-9]+]]:_(s16), [[UNMERGE1_4:%[0-9]+]]:_(s16), [[UNMERGE1_5:%[0-9]+]]:_(s16), [[UNMERGE1_6:%[0-9]+]]:_(s16), [[UNMERGE1_7:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[VEC8]]:_(<8 x s16>)CHECK: [[IMPDEF_S16:%[0-9]+]]:_(s16) = G_IMPLICIT_DEFCHECK: [[BUILD0:%[0-9]+]]:_(<3 x s16>) = G_BUILD_VECTOR [[UNMERGE1_0]]:_(s16), [[UNMERGE1_1]]:_(s16), [[UNMERGE1_2]]:_(s16)CHECK: [[BUILD1:%[0-9]+]]:_(<3 x s16>) = G_BUILD_VECTOR [[UNMERGE1_3]]:_(s16), [[UNMERGE1_4]]:_(s16), [[UNMERGE1_5]]:_(s16)CHECK: [[BUILD2:%[0-9]+]]:_(<3 x s16>) = G_BUILD_VECTOR [[UNMERGE1_6]]:_(s16), [[UNMERGE1_7]]:_(s16), [[IMPDEF_S16]]:_(s16)CHECK: [[IMPDEF_V3S16:%[0-9]+]]:_(<3 x s16>) = G_IMPLICIT_DEFCHECK: [[ONE_1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1CHECK: [[SUB_INSERT_7_V3S16:%[0-9]+]]:_(<3 x s16>) = G_INSERT_VECTOR_ELT [[BUILD2]]:_, [[INSERT_VAL]]:_(s16), [[ONE_1]]CHECK: [[WIDE_CONCAT_DEAD:%[0-9]+]]:_(<24 x s16>) = G_CONCAT_VECTORS [[BUILD0]]:_(<3 x s16>), [[BUILD1]]:_(<3 x s16>), [[SUB_INSERT_7_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>)CHECK: [[WIDE_CONCAT:%[0-9]+]]:_(<24 x s16>) = G_CONCAT_VECTORS [[BUILD0]]:_(<3 x s16>), [[BUILD1]]:_(<3 x s16>), [[SUB_INSERT_7_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>), [[IMPDEF_V3S16]]:_(<3 x s16>)CHECK: [[INSERT_V8_7_1:%[0-9]+]]:_(<8 x s16>), %{{[0-9]+}}:_(<8 x s16>), %{{[0-9]+}}:_(<8 x s16>) = G_UNMERGE_VALUES [[WIDE_CONCAT]]:_(<24 x s16>)CHECK: G_STORE [[INSERT_V8_7_0]]CHECK: G_STORE [[INSERT_V8_7_1]])";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test widen scalar of G_UNMERGE_VALUESTEST_F(AArch64GISelMITest, widenScalarUnmerge) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});LLT S96{LLT::scalar(96)};LLT S64{LLT::scalar(64)};LLT S48{LLT::scalar(48)};auto Src = B.buildAnyExt(S96, Copies[0]);auto Unmerge = B.buildUnmerge(S48, Src);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, Unmerge->getIterator());// This should create unmerges to a GCD type (S16), then remerge to S48EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.widenScalar(*Unmerge, 0, S64));const auto *CheckStr = R"(CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY1:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY2:%[0-9]+]]:_(s64) = COPYCHECK: [[ANYEXT:%[0-9]+]]:_(s96) = G_ANYEXT [[COPY0]]CHECK: [[ANYEXT1:%[0-9]+]]:_(s192) = G_ANYEXT [[ANYEXT]]CHECK: [[UNMERGE:%[0-9]+]]:_(s64), [[UNMERGE1:%[0-9]+]]:_(s64), [[UNMERGE2:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[ANYEXT1]]CHECK: [[UNMERGE3:%[0-9]+]]:_(s16), [[UNMERGE4:%[0-9]+]]:_(s16), [[UNMERGE5:%[0-9]+]]:_(s16), [[UNMERGE6:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[UNMERGE]]CHECK: [[UNMERGE7:%[0-9]+]]:_(s16), [[UNMERGE8:%[0-9]+]]:_(s16), [[UNMERGE9:%[0-9]+]]:_(s16), [[UNMERGE10:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[UNMERGE1]]CHECK: [[UNMERGE11:%[0-9]+]]:_(s16), [[UNMERGE12:%[0-9]+]]:_(s16), [[UNMERGE13:%[0-9]+]]:_(s16), [[UNMERGE14:%[0-9]+]]:_(s16) = G_UNMERGE_VALUES [[UNMERGE2]]CHECK: [[MERGE:%[0-9]+]]:_(s48) = G_MERGE_VALUES [[UNMERGE3]]:_(s16), [[UNMERGE4]]:_(s16), [[UNMERGE5]]:_(s16)CHECK: [[MERGE1:%[0-9]+]]:_(s48) = G_MERGE_VALUES [[UNMERGE6]]:_(s16), [[UNMERGE7]]:_(s16), [[UNMERGE8]]:_(s16))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test moreElements of G_SHUFFLE_VECTOR.TEST_F(AArch64GISelMITest, moreElementsShuffle) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});LLT S64{LLT::scalar(64)};LLT V6S64 = LLT::fixed_vector(6, S64);auto V1 = B.buildBuildVector(V6S64, {Copies[0], Copies[1], Copies[0],Copies[1], Copies[0], Copies[1]});auto V2 = B.buildBuildVector(V6S64, {Copies[0], Copies[1], Copies[0],Copies[1], Copies[0], Copies[1]});auto Shuffle = B.buildShuffleVector(V6S64, V1, V2, {3, 4, 7, 0, 1, 11});AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, Shuffle->getIterator());EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.moreElementsVector(*Shuffle, 0, LLT::fixed_vector(8, S64)));const auto *CheckStr = R"(CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY1:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY2:%[0-9]+]]:_(s64) = COPYCHECK: [[BV1:%[0-9]+]]:_(<6 x s64>) = G_BUILD_VECTORCHECK: [[BV2:%[0-9]+]]:_(<6 x s64>) = G_BUILD_VECTORCHECK: [[BV1_E0:%[0-9]+]]:_(s64), [[BV1_E1:%[0-9]+]]:_(s64), [[BV1_E2:%[0-9]+]]:_(s64), [[BV1_E3:%[0-9]+]]:_(s64), [[BV1_E4:%[0-9]+]]:_(s64), [[BV1_E5:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[BV1]]:_(<6 x s64>)CHECK: [[IMP_DEF0:%[0-9]+]]:_(s64) = G_IMPLICIT_DEFCHECK: [[BV1_LARGE:%[0-9]+]]:_(<8 x s64>) = G_BUILD_VECTOR [[BV1_E0]]:_(s64), [[BV1_E1]]:_(s64), [[BV1_E2]]:_(s64), [[BV1_E3]]:_(s64), [[BV1_E4]]:_(s64), [[BV1_E5]]:_(s64), [[IMP_DEF0]]:_(s64), [[IMP_DEF0]]:_(s64)CHECK: [[BV2_E0:%[0-9]+]]:_(s64), [[BV2_E1:%[0-9]+]]:_(s64), [[BV2_E2:%[0-9]+]]:_(s64), [[BV2_E3:%[0-9]+]]:_(s64), [[BV2_E4:%[0-9]+]]:_(s64), [[BV2_E5:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[BV2]]:_(<6 x s64>)CHECK: [[IMP_DEF1:%[0-9]+]]:_(s64) = G_IMPLICIT_DEFCHECK: [[BV2_LARGE:%[0-9]+]]:_(<8 x s64>) = G_BUILD_VECTOR [[BV2_E0]]:_(s64), [[BV2_E1]]:_(s64), [[BV2_E2]]:_(s64), [[BV2_E3]]:_(s64), [[BV2_E4]]:_(s64), [[BV2_E5]]:_(s64), [[IMP_DEF1]]:_(s64), [[IMP_DEF1]]:_(s64)CHECK: [[SHUF:%[0-9]+]]:_(<8 x s64>) = G_SHUFFLE_VECTOR [[BV1_LARGE]]:_(<8 x s64>), [[BV2_LARGE]]:_, shufflemask(3, 4, 9, 0, 1, 13, undef, undef)CHECK: [[SHUF_E0:%[0-9]+]]:_(s64), [[SHUF_E1:%[0-9]+]]:_(s64), [[SHUF_E2:%[0-9]+]]:_(s64), [[SHUF_E3:%[0-9]+]]:_(s64), [[SHUF_E4:%[0-9]+]]:_(s64), [[SHUF_E5:%[0-9]+]]:_(s64), [[SHUF_E6:%[0-9]+]]:_(s64), [[SHUF_E7:%[0-9]+]]:_(s64) = G_UNMERGE_VALUES [[SHUF]]:_(<8 x s64>)CHECK: (<6 x s64>) = G_BUILD_VECTOR [[SHUF_E0]]:_(s64), [[SHUF_E1]]:_(s64), [[SHUF_E2]]:_(s64), [[SHUF_E3]]:_(s64), [[SHUF_E4]]:_(s64), [[SHUF_E5]]:_(s64))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}// Test narror scalar of G_SHL with constant shift amountTEST_F(AArch64GISelMITest, narrowScalarShiftByConstant) {setUp();if (!TM)return;DefineLegalizerInfo(A, {});LLT S64{LLT::scalar(64)};LLT S32{LLT::scalar(32)};auto Constant = B.buildConstant(S64, 33);auto Trunc = B.buildTrunc(S32, Constant);auto Shift = B.buildShl(S64, Copies[0], Trunc);AInfo Info(MF->getSubtarget());DummyGISelObserver Observer;LegalizerHelper Helper(*MF, Info, Observer, B);// Perform LegalizationB.setInsertPt(*EntryMBB, Shift->getIterator());// This should detect the G_CONSTANT feeding the G_SHL through a G_TRUNCEXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.narrowScalarShift(*Shift, 0, S32));const auto *CheckStr = R"(CHECK: [[COPY0:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY1:%[0-9]+]]:_(s64) = COPYCHECK: [[COPY2:%[0-9]+]]:_(s64) = COPYCHECK: [[THIRTY3:%[0-9]+]]:_(s64) = G_CONSTANT i64 33CHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC %4:_(s64)CHECK: [[UNMERGE:%[0-9]+]]:_(s32), [[UNMERGE2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY0]]CHECK: [[ZERO:%[0-9]+]]:_(s32) = G_CONSTANT i32 0CHECK: [[ONE:%[0-9]+]]:_(s32) = G_CONSTANT i32 1CHECK: [[SHIFT:%[0-9]+]]:_(s32) = G_SHL [[UNMERGE]]:_, [[ONE]]:_(s32)CHECK: [[MERGE:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[ZERO]]:_(s32), [[SHIFT]]:_(s32))";// CheckEXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}TEST_F(AArch64GISelMITest, MoreElementsSelect) {setUp();if (!TM)return;LLT s1 = LLT::scalar(1);LLT s64 = LLT::scalar(64);LLT v2s1 = LLT::fixed_vector(2, 1);LLT v2s32 = LLT::fixed_vector(2, 32);LegalizerInfo LI;DummyGISelObserver Observer;LegalizerHelper Helper(*MF, LI, Observer, B);B.setInsertPt(*EntryMBB, EntryMBB->end());auto Val0 = B.buildBitcast(v2s32, Copies[0]);auto Val1 = B.buildBitcast(v2s32, Copies[1]);// Build select of vectors with scalar condition.auto Zero = B.buildConstant(s64, 0);auto Cond = B.buildICmp(CmpInst::ICMP_EQ, s1, Copies[2], Zero);auto Select = B.buildSelect(v2s32, Cond, Val0, Val1);// Splat the condition into a vector selectB.setInstr(*Select);EXPECT_EQ(LegalizerHelper::LegalizeResult::UnableToLegalize,Helper.moreElementsVector(*Select, 1, LLT::fixed_vector(3, 1)));EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,Helper.moreElementsVector(*Select, 1, v2s1));auto CheckStr = R"(CHECK: [[BITCAST0:%[0-9]+]]:_(<2 x s32>) = G_BITCASTCHECK: [[BITCAST1:%[0-9]+]]:_(<2 x s32>) = G_BITCASTCHECK: [[ZERO0:%[0-9]+]]:_(s64) = G_CONSTANT i64 0CHECK: [[CMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), %{{[0-9]+}}:_(s64), [[ZERO0]]CHECK: [[IMPDEF:%[0-9]+]]:_(<2 x s1>) = G_IMPLICIT_DEFCHECK: [[ZERO1:%[0-9]+]]:_(s64) = G_CONSTANT i64 0CHECK: [[INSERT:%[0-9]+]]:_(<2 x s1>) = G_INSERT_VECTOR_ELT [[IMPDEF]]:_, [[CMP]]:_(s1), [[ZERO1]]CHECK: [[SHUFFLE:%[0-9]+]]:_(<2 x s1>) = G_SHUFFLE_VECTOR [[INSERT]]:_(<2 x s1>), [[IMPDEF]]:_, shufflemask(0, 0)CHECK: [[SELECT:%[0-9]+]]:_(<2 x s32>) = G_SELECT [[SHUFFLE]]:_(<2 x s1>), [[BITCAST0]]:_, [[BITCAST1]]:_)";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}} // namespace
//===- KnownBitsTest.cpp -------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h"#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"TEST_F(AArch64GISelMITest, TestKnownBitsBuildVector) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 24%mask1:_(s8) = G_CONSTANT i8 224%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 146%mask3:_(s8) = G_CONSTANT i8 36%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%vector:_(<2 x s8>) = G_BUILD_VECTOR %val0, %val1%copy_vector:_(<2 x s8>) = COPY %vector)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// BuildVector KnownBits takes common bits of all elements.// 111??000// common ?01?01?0// = ??1????0EXPECT_EQ(0x20u, Res.One.getZExtValue());EXPECT_EQ(0x01u, Res.Zero.getZExtValue());}// Vector KnownBits track bits that are common for all vector scalar elements.// For tests below KnownBits analysis is same as for scalar/pointer types, tests// are mostly copied from KnownBitsTest.cpp using splat vectors and have the// same result.TEST_F(AArch64GISelMITest, TestKnownBitsVectorCstPHI) {StringRef MIRString = R"(bb.10:%10:_(s8) = G_CONSTANT i8 3%11:_(<2 x s8>) = G_BUILD_VECTOR %10:_(s8), %10:_(s8)%12:_(s1) = G_IMPLICIT_DEFG_BRCOND %12(s1), %bb.11G_BR %bb.12bb.11:%13:_(s8) = G_CONSTANT i8 2%14:_(<2 x s8>) = G_BUILD_VECTOR %13:_(s8), %13:_(s8)G_BR %bb.12bb.12:%15:_(<2 x s8>) = PHI %11(<2 x s8>), %bb.10, %14(<2 x s8>), %bb.11%16:_(<2 x s8>) = COPY %15)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)2, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xfc, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorCstPHIToNonGenericReg) {StringRef MIRString = R"(bb.10:%10:gpr32 = MOVi32imm 771%11:_(s1) = G_IMPLICIT_DEFG_BRCOND %11(s1), %bb.11G_BR %bb.12bb.11:%12:_(s16) = G_CONSTANT i16 2%13:_(<2 x s16>) = G_BUILD_VECTOR %12:_(s16), %12:_(s16)G_BR %bb.12bb.12:%15:_(<2 x s16>) = PHI %10, %bb.10, %13(<2 x s16>), %bb.11%16:_(<2 x s16>) = COPY %15)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorUnknownPHI) {StringRef MIRString = R"(bb.10:%10:_(<2 x s32>) = G_BITCAST %0%11:_(s1) = G_IMPLICIT_DEFG_BRCOND %11(s1), %bb.11G_BR %bb.12bb.11:%12:_(s32) = G_CONSTANT i32 2%13:_(<2 x s32>) = G_BUILD_VECTOR %12:_(s32), %12:_(s32)G_BR %bb.12bb.12:%14:_(<2 x s32>) = PHI %10(<2 x s32>), %bb.10, %13(<2 x s32>), %bb.11%15:_(<2 x s32>) = COPY %14)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorCstPHIWithLoop) {StringRef MIRString = R"(bb.10:%10:_(s8) = G_CONSTANT i8 3%11:_(<2 x s8>) = G_BUILD_VECTOR %10:_(s8), %10:_(s8)%12:_(s1) = G_IMPLICIT_DEFG_BRCOND %12(s1), %bb.11G_BR %bb.12bb.11:%13:_(s8) = G_CONSTANT i8 2%14:_(<2 x s8>) = G_BUILD_VECTOR %13:_(s8), %13:_(s8)G_BR %bb.12bb.12:%15:_(<2 x s8>) = PHI %11(<2 x s8>), %bb.10, %14(<2 x s8>), %bb.11, %16(<2 x s8>), %bb.12%16:_(<2 x s8>) = COPY %15G_BR %bb.12)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorDecreasingCstPHIWithLoop) {StringRef MIRString = R"(bb.10:%10:_(s8) = G_CONSTANT i8 5%11:_(<2 x s8>) = G_BUILD_VECTOR %10:_(s8), %10:_(s8)%12:_(s8) = G_CONSTANT i8 1%16:_(<2 x s8>) = G_BUILD_VECTOR %12:_(s8), %12:_(s8)bb.12:%13:_(<2 x s8>) = PHI %11(<2 x s8>), %bb.10, %14(<2 x s8>), %bb.12%14:_(<2 x s8>) = G_LSHR %13, %16%15:_(<2 x s8>) = COPY %14G_BR %bb.12)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF, /*MaxDepth=*/24);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xC0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorAND) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 52%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 10%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 32%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 24%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%and:_(<2 x s8>) = G_AND %val0, %val1%copy_and:_(<2 x s8>) = COPY %and)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x08u, Res.One.getZExtValue());EXPECT_EQ(0xC7u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorOR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 52%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 10%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 32%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 24%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%or:_(<2 x s8>) = G_OR %val0, %val1%copy_or:_(<2 x s8>) = COPY %or)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x1Au, Res.One.getZExtValue());EXPECT_EQ(0xC1u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorXOR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 52%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 10%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 32%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 24%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%xor:_(<2 x s8>) = G_XOR %val0, %val1%copy_xor:_(<2 x s8>) = COPY %xor)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x02u, Res.One.getZExtValue());EXPECT_EQ(0xC9u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorXORConstant) {StringRef MIRString = R"(%3:_(s8) = G_CONSTANT i8 4%4:_(<2 x s8>) = G_BUILD_VECTOR %3:_(s8), %3:_(s8)%5:_(s8) = G_CONSTANT i8 7%6:_(<2 x s8>) = G_BUILD_VECTOR %5:_(s8), %5:_(s8)%7:_(<2 x s8>) = G_XOR %4, %6%8:_(<2 x s8>) = COPY %7%9:_(s8) = G_CONSTANT i8 12%10:_(<2 x s8>) = G_BUILD_VECTOR %3:_(s8), %9:_(s8)%11:_(<2 x s8>) = G_XOR %10, %6%12:_(<2 x s8>) = COPY %11)";setUp(MIRString);if (!TM)return;GISelKnownBits Info(*MF);Register CopySplatReg = Copies[Copies.size() - 2];MachineInstr *FinalSplatCopy = MRI->getVRegDef(CopySplatReg);Register SrcSplatReg = FinalSplatCopy->getOperand(1).getReg();KnownBits ResNonSplat = Info.getKnownBits(SrcSplatReg);EXPECT_EQ(3u, ResNonSplat.One.getZExtValue());EXPECT_EQ(252u, ResNonSplat.Zero.getZExtValue());Register CopyNonSplatReg = Copies[Copies.size() - 1];MachineInstr *FinalNonSplatCopy = MRI->getVRegDef(CopyNonSplatReg);Register SrcNonSplatReg = FinalNonSplatCopy->getOperand(1).getReg();KnownBits ResSplat = Info.getKnownBits(SrcNonSplatReg);EXPECT_EQ(3u, ResSplat.One.getZExtValue());EXPECT_EQ(244u, ResSplat.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorASHR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 38%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 202%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%cst0:_(s8) = G_CONSTANT i8 2%cst0_splat:_(<2 x s8>) = G_BUILD_VECTOR %cst0, %cst0%ashr0:_(<2 x s8>) = G_ASHR %val0, %cst0_splat%copy_ashr0:_(<2 x s8>) = COPY %ashr0%mask2:_(s8) = G_CONSTANT i8 204%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 18%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%ashr1:_(<2 x s8>) = G_ASHR %val1, %cst0_splat%copy_ashr1:_(<2 x s8>) = COPY %ashr1)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 2];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);EXPECT_EQ(0xF2u, Res0.One.getZExtValue());EXPECT_EQ(0x04u, Res0.Zero.getZExtValue());Register CopyReg1 = Copies[Copies.size() - 1];MachineInstr *FinalCopy1 = MRI->getVRegDef(CopyReg1);Register SrcReg1 = FinalCopy1->getOperand(1).getReg();KnownBits Res1 = Info.getKnownBits(SrcReg1);EXPECT_EQ(0x04u, Res1.One.getZExtValue());EXPECT_EQ(0x08u, Res1.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorLSHR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 38%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 202%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%cst0:_(s8) = G_CONSTANT i8 2%cst0_splat:_(<2 x s8>) = G_BUILD_VECTOR %cst0, %cst0%lshr0:_(<2 x s8>) = G_LSHR %val0, %cst0_splat%copy_lshr0:_(<2 x s8>) = COPY %lshr0%mask2:_(s8) = G_CONSTANT i8 204%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 18%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%lshr1:_(<2 x s8>) = G_LSHR %val1, %cst0_splat%copy_lshr1:_(<2 x s8>) = COPY %lshr1)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 2];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);EXPECT_EQ(0x32u, Res0.One.getZExtValue());EXPECT_EQ(0xC4u, Res0.Zero.getZExtValue());Register CopyReg1 = Copies[Copies.size() - 1];MachineInstr *FinalCopy1 = MRI->getVRegDef(CopyReg1);Register SrcReg1 = FinalCopy1->getOperand(1).getReg();KnownBits Res1 = Info.getKnownBits(SrcReg1);EXPECT_EQ(0x04u, Res1.One.getZExtValue());EXPECT_EQ(0xC8u, Res1.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorSHL) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 51%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 72%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val:_(<2 x s8>) = G_OR %tmp, %mask1_splat%cst:_(s8) = G_CONSTANT i8 3%cst_splat:_(<2 x s8>) = G_BUILD_VECTOR %cst, %cst%shl:_(<2 x s8>) = G_SHL %val, %cst_splat%copy_shl:_(<2 x s8>) = COPY %shl)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x40u, Res.One.getZExtValue());EXPECT_EQ(0x27u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorADD) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s16>) = G_LOAD %ptr(p0) :: (load (<2 x s16>))%mask0:_(s16) = G_CONSTANT i16 4642%mask0_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s16) = G_CONSTANT i16 9536%mask1_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s16>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s16>) = G_OR %tmp0, %mask1_splat%mask2:_(s16) = G_CONSTANT i16 4096%mask2_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s16) = G_CONSTANT i16 371%mask3_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s16>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s16>) = G_OR %tmp1, %mask3_splat%add:_(<2 x s16>) = G_ADD %val0, %val1%copy_add:_(<2 x s16>) = COPY %add)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x0091u, Res.One.getZExtValue());EXPECT_EQ(0x8108u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorSUB) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s16>) = G_LOAD %ptr(p0) :: (load (<2 x s16>))%mask0:_(s16) = G_CONSTANT i16 4642%mask0_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s16) = G_CONSTANT i16 9536%mask1_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s16>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s16>) = G_OR %tmp0, %mask1_splat%mask2:_(s16) = G_CONSTANT i16 4096%mask2_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s16) = G_CONSTANT i16 371%mask3_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s16>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s16>) = G_OR %tmp1, %mask3_splat%sub:_(<2 x s16>) = G_SUB %val0, %val1%copy_sub:_(<2 x s16>) = COPY %sub)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x01CDu, Res.One.getZExtValue());EXPECT_EQ(0xC810u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorMUL) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s16>) = G_LOAD %ptr(p0) :: (load (<2 x s16>))%mask0:_(s16) = G_CONSTANT i16 4%mask0_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s16) = G_CONSTANT i16 18%mask1_splat:_(<2 x s16>) = G_BUILD_VECTOR %mask1, %mask1%tmp:_(<2 x s16>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s16>) = G_OR %tmp, %mask1_splat%cst:_(s16) = G_CONSTANT i16 12%cst_splat:_(<2 x s16>) = G_BUILD_VECTOR %cst, %cst%mul:_(<2 x s16>) = G_MUL %val0, %cst_splat%copy_mul:_(<2 x s16>) = COPY %mul)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x0008u, Res.One.getZExtValue());EXPECT_EQ(0xFE07u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorSelect) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 24%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 224%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 146%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 36%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%cond:_(s1) = G_CONSTANT i1 false%cond_splat:_(<2 x s1>) = G_BUILD_VECTOR %cond, %cond%select:_(<2 x s8>) = G_SELECT %cond_splat, %val0, %val1%copy_select:_(<2 x s8>) = COPY %select)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(0x20u, Res.One.getZExtValue());EXPECT_EQ(0x01u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestVectorSignBitIsZero) {setUp();if (!TM)return;const LLT V2S32 = LLT::fixed_vector(2, 32);// Vector buildConstant makes splat G_BUILD_VECTOR instruction.auto SignBit = B.buildConstant(V2S32, 0x80000000);auto Zero = B.buildConstant(V2S32, 0);const LLT S32 = LLT::scalar(32);auto NonSplat =B.buildBuildVector(V2S32, {B.buildConstant(S32, 1).getReg(0),B.buildConstant(S32, 2).getReg(0)});auto NonSplat2 =B.buildBuildVector(V2S32, {B.buildConstant(S32, 0x80000000).getReg(0),B.buildConstant(S32, 0x80000004).getReg(0)});// signBitIsZero is true for elt 0 and false for elt 1 GISelKnownBits takes// common bits so this is false.auto NonSplat3 =B.buildBuildVector(V2S32, {B.buildConstant(S32, 0x80000000).getReg(0),B.buildConstant(S32, 0x8).getReg(0)});GISelKnownBits KnownBits(*MF);EXPECT_TRUE(KnownBits.signBitIsZero(Zero.getReg(0)));EXPECT_FALSE(KnownBits.signBitIsZero(SignBit.getReg(0)));EXPECT_TRUE(KnownBits.signBitIsZero(NonSplat.getReg(0)));EXPECT_FALSE(KnownBits.signBitIsZero(NonSplat2.getReg(0)));EXPECT_FALSE(KnownBits.signBitIsZero(NonSplat3.getReg(0)));}TEST_F(AArch64GISelMITest, TestVectorNumSignBitsConstant) {StringRef MIRString = R"(%3:_(s8) = G_CONSTANT i8 1%4:_(<2 x s8>) = G_BUILD_VECTOR %3:_(s8), %3:_(s8)%5:_(<2 x s8>) = COPY %4%6:_(s8) = G_CONSTANT i8 -1%7:_(<2 x s8>) = G_BUILD_VECTOR %6:_(s8), %6:_(s8)%8:_(<2 x s8>) = COPY %7%9:_(s8) = G_CONSTANT i8 127%10:_(<2 x s8>) = G_BUILD_VECTOR %9:_(s8), %9:_(s8)%11:_(<2 x s8>) = COPY %10%12:_(s8) = G_CONSTANT i8 32%13:_(<2 x s8>) = G_BUILD_VECTOR %12:_(s8), %12:_(s8)%14:_(<2 x s8>) = COPY %13%15:_(s8) = G_CONSTANT i8 -32%16:_(<2 x s8>) = G_BUILD_VECTOR %15:_(s8), %15:_(s8)%17:_(<2 x s8>) = COPY %16%18:_(<2 x s8>) = G_BUILD_VECTOR %6:_(s8), %15:_(s8)%19:_(<2 x s8>) = COPY %18%20:_(<2 x s8>) = G_BUILD_VECTOR %12:_(s8), %15:_(s8)%21:_(<2 x s8>) = COPY %20)";setUp(MIRString);if (!TM)return;Register CopyReg1 = Copies[Copies.size() - 7];Register CopyRegNeg1 = Copies[Copies.size() - 6];Register CopyReg127 = Copies[Copies.size() - 5];Register CopyReg32 = Copies[Copies.size() - 4];Register CopyRegNeg32 = Copies[Copies.size() - 3];Register NonSplatSameSign = Copies[Copies.size() - 2];Register NonSplatDifferentSign = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);// If it is known that all elts have same sign looks at common bits and// effectively returns smallest NumSignBits of all the elts. Otherwise returns// default value 1.EXPECT_EQ(7u, Info.computeNumSignBits(CopyReg1));EXPECT_EQ(8u, Info.computeNumSignBits(CopyRegNeg1));EXPECT_EQ(1u, Info.computeNumSignBits(CopyReg127));EXPECT_EQ(2u, Info.computeNumSignBits(CopyReg32));EXPECT_EQ(3u, Info.computeNumSignBits(CopyRegNeg32));EXPECT_EQ(3u, Info.computeNumSignBits(NonSplatSameSign));EXPECT_EQ(1u, Info.computeNumSignBits(NonSplatDifferentSign));}TEST_F(AArch64GISelMITest, TestVectorNumSignBitsSext) {StringRef MIRString = R"(%3:_(p0) = G_IMPLICIT_DEF%4:_(<2 x s8>) = G_LOAD %3 :: (load (<2 x s8>))%5:_(<2 x s32>) = G_SEXT %4%6:_(<2 x s32>) = COPY %5%7:_(s8) = G_CONSTANT i8 -1%8:_(<2 x s8>) = G_BUILD_VECTOR %7:_(s8), %7:_(s8)%9:_(<2 x s32>) = G_SEXT %8%10:_(<2 x s32>) = COPY %9%11:_(s8) = G_CONSTANT i8 -10%12:_(<2 x s8>) = G_BUILD_VECTOR %7:_(s8), %11:_(s8)%13:_(<2 x s32>) = G_SEXT %12%14:_(<2 x s32>) = COPY %13)";setUp(MIRString);if (!TM)return;Register CopySextLoad = Copies[Copies.size() - 3];Register CopySextNeg1 = Copies[Copies.size() - 2];Register CopySextNonSplat = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(25u, Info.computeNumSignBits(CopySextLoad));EXPECT_EQ(32u, Info.computeNumSignBits(CopySextNeg1));EXPECT_EQ(28u, Info.computeNumSignBits(CopySextNonSplat));}TEST_F(AArch64GISelMITest, TestVectorNumSignBitsSextInReg) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%load2x4:_(<2 x s32>) = G_LOAD %ptr :: (load (<2 x s32>))%inreg7:_(<2 x s32>) = G_SEXT_INREG %load2x4, 7%copy_inreg7:_(<2 x s32>) = COPY %inreg7%inreg8:_(<2 x s32>) = G_SEXT_INREG %load2x4, 8%copy_inreg8:_(<2 x s32>) = COPY %inreg8%inreg9:_(<2 x s32>) = G_SEXT_INREG %load2x4, 9%copy_inreg9:_(<2 x s32>) = COPY %inreg9%inreg31:_(<2 x s32>) = G_SEXT_INREG %load2x4, 31%copy_inreg31:_(<2 x s32>) = COPY %inreg31%load2x1:_(<2 x s8>) = G_LOAD %ptr :: (load (<2 x s8>))%sext_load2x1:_(<2 x s32>) = G_SEXT %load2x1%inreg6_sext:_(<2 x s32>) = G_SEXT_INREG %sext_load2x1, 6%copy_inreg6_sext:_(<2 x s32>) = COPY %inreg6_sext%inreg7_sext:_(<2 x s32>) = G_SEXT_INREG %sext_load2x1, 7%copy_inreg7_sext:_(<2 x s32>) = COPY %inreg7_sext%inreg8_sext:_(<2 x s32>) = G_SEXT_INREG %sext_load2x1, 8%copy_inreg8_sext:_(<2 x s32>) = COPY %inreg8_sext%inreg9_sext:_(<2 x s32>) = G_SEXT_INREG %sext_load2x1, 9%copy_inreg9_sext:_(<2 x s32>) = COPY %inreg9_sext%inreg31_sext:_(<2 x s32>) = G_SEXT_INREG %sext_load2x1, 31%copy_inreg31_sext:_(<2 x s32>) = COPY %inreg31_sext)";setUp(MIRString);if (!TM)return;Register CopyInReg7 = Copies[Copies.size() - 9];Register CopyInReg8 = Copies[Copies.size() - 8];Register CopyInReg9 = Copies[Copies.size() - 7];Register CopyInReg31 = Copies[Copies.size() - 6];Register CopyInReg6Sext = Copies[Copies.size() - 5];Register CopyInReg7Sext = Copies[Copies.size() - 4];Register CopyInReg8Sext = Copies[Copies.size() - 3];Register CopyInReg9Sext = Copies[Copies.size() - 2];Register CopyInReg31Sext = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8));EXPECT_EQ(24u, Info.computeNumSignBits(CopyInReg9));EXPECT_EQ(2u, Info.computeNumSignBits(CopyInReg31));EXPECT_EQ(27u, Info.computeNumSignBits(CopyInReg6Sext));EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg9Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg31Sext));}TEST_F(AArch64GISelMITest, TestNumSignBitsVectorAssertSext) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%load2x4:_(<2 x s32>) = G_LOAD %ptr :: (load (<2 x s32>))%assert_sext1:_(<2 x s32>) = G_ASSERT_SEXT %load2x4, 1%copy_assert_sext1:_(<2 x s32>) = COPY %assert_sext1%assert_sext7:_(<2 x s32>) = G_ASSERT_SEXT %load2x4, 7%copy_assert_sext7:_(<2 x s32>) = COPY %assert_sext7%assert_sext8:_(<2 x s32>) = G_ASSERT_SEXT %load2x4, 8%copy_assert_sext8:_(<2 x s32>) = COPY %assert_sext8%assert_sext9:_(<2 x s32>) = G_ASSERT_SEXT %load2x4, 9%copy_assert_sext9:_(<2 x s32>) = COPY %assert_sext9%assert_sext31:_(<2 x s32>) = G_ASSERT_SEXT %load2x4, 31%copy_assert_sext31:_(<2 x s32>) = COPY %assert_sext31%load2x1:_(<2 x s8>) = G_LOAD %ptr :: (load (<2 x s8>))%sext_load2x1:_(<2 x s32>) = G_SEXT %load2x1%assert_sext6_sext:_(<2 x s32>) = G_ASSERT_SEXT %sext_load2x1, 6%copy_assert_sext6_sext:_(<2 x s32>) = COPY %assert_sext6_sext%assert_sext7_sext:_(<2 x s32>) = G_ASSERT_SEXT %sext_load2x1, 7%copy_assert_sext7_sext:_(<2 x s32>) = COPY %assert_sext7_sext%assert_sext8_sext:_(<2 x s32>) = G_ASSERT_SEXT %sext_load2x1, 8%copy_assert_sext8_sext:_(<2 x s32>) = COPY %assert_sext8_sext%assert_sext9_sext:_(<2 x s32>) = G_ASSERT_SEXT %sext_load2x1, 9%copy_assert_sext9_sext:_(<2 x s32>) = COPY %assert_sext9_sext%assert_sext31_sext:_(<2 x s32>) = G_ASSERT_SEXT %sext_load2x1, 31%copy_assert_sext31_sext:_(<2 x s32>) = COPY %assert_sext31_sext)";setUp(MIRString);if (!TM)return;Register CopyInReg1 = Copies[Copies.size() - 10];Register CopyInReg7 = Copies[Copies.size() - 9];Register CopyInReg8 = Copies[Copies.size() - 8];Register CopyInReg9 = Copies[Copies.size() - 7];Register CopyInReg31 = Copies[Copies.size() - 6];Register CopyInReg6Sext = Copies[Copies.size() - 5];Register CopyInReg7Sext = Copies[Copies.size() - 4];Register CopyInReg8Sext = Copies[Copies.size() - 3];Register CopyInReg9Sext = Copies[Copies.size() - 2];Register CopyInReg31Sext = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(32u, Info.computeNumSignBits(CopyInReg1));EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8));EXPECT_EQ(24u, Info.computeNumSignBits(CopyInReg9));EXPECT_EQ(2u, Info.computeNumSignBits(CopyInReg31));EXPECT_EQ(27u, Info.computeNumSignBits(CopyInReg6Sext));EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg9Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg31Sext));}TEST_F(AArch64GISelMITest, TestVectorNumSignBitsTrunc) {StringRef MIRString = R"(%3:_(p0) = G_IMPLICIT_DEF%4:_(<2 x s32>) = G_LOAD %3 :: (load (<2 x s32>))%5:_(<2 x s8>) = G_TRUNC %4%6:_(<2 x s8>) = COPY %5%7:_(s32) = G_CONSTANT i32 -1%8:_(<2 x s32>) = G_BUILD_VECTOR %7:_(s32), %7:_(s32)%9:_(<2 x s8>) = G_TRUNC %8%10:_(<2 x s8>) = COPY %9%11:_(s32) = G_CONSTANT i32 7%12:_(<2 x s32>) = G_BUILD_VECTOR %11:_(s32), %11:_(s32)%13:_(<2 x s8>) = G_TRUNC %12%14:_(<2 x s8>) = COPY %13)";setUp(MIRString);if (!TM)return;Register CopyTruncLoad = Copies[Copies.size() - 3];Register CopyTruncNeg1 = Copies[Copies.size() - 2];Register CopyTrunc7 = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(1u, Info.computeNumSignBits(CopyTruncLoad));EXPECT_EQ(8u, Info.computeNumSignBits(CopyTruncNeg1));EXPECT_EQ(5u, Info.computeNumSignBits(CopyTrunc7));}TEST_F(AMDGPUGISelMITest, TestVectorIsKnownToBeAPowerOfTwo) {StringRef MIRString = R"(%zero:_(s32) = G_CONSTANT i32 0%zero_splat:_(<2 x s32>) = G_BUILD_VECTOR %zero:_(s32), %zero:_(s32)%one:_(s32) = G_CONSTANT i32 1%one_splat:_(<2 x s32>) = G_BUILD_VECTOR %one:_(s32), %one:_(s32)%two:_(s32) = G_CONSTANT i32 2%two_splat:_(<2 x s32>) = G_BUILD_VECTOR %two:_(s32), %two:_(s32)%three:_(s32) = G_CONSTANT i32 3%three_splat:_(<2 x s32>) = G_BUILD_VECTOR %three:_(s32), %three:_(s32)%five:_(s32) = G_CONSTANT i32 5%five_splat:_(<2 x s32>) = G_BUILD_VECTOR %five:_(s32), %five:_(s32)%copy_zero_splat:_(<2 x s32>) = COPY %zero_splat%copy_one_splat:_(<2 x s32>) = COPY %one_splat%copy_two_splat:_(<2 x s32>) = COPY %two_splat%copy_three_splat:_(<2 x s32>) = COPY %three_splat%trunc_two_splat:_(<2 x s1>) = G_TRUNC %two_splat%trunc_three_splat:_(<2 x s1>) = G_TRUNC %three_splat%trunc_five_splat:_(<2 x s1>) = G_TRUNC %five_splat%copy_trunc_two_splat:_(<2 x s1>) = COPY %trunc_two_splat%copy_trunc_three_splat:_(<2 x s1>) = COPY %trunc_three_splat%copy_trunc_five_splat:_(<2 x s1>) = COPY %trunc_five_splat%ptr:_(p1) = G_IMPLICIT_DEF%shift_amt:_(<2 x s32>) = G_LOAD %ptr :: (load (<2 x s32>), addrspace 1)%shl_1:_(<2 x s32>) = G_SHL %one_splat, %shift_amt%copy_shl_1:_(<2 x s32>) = COPY %shl_1%shl_2:_(<2 x s32>) = G_SHL %two_splat, %shift_amt%copy_shl_2:_(<2 x s32>) = COPY %shl_2%not_sign_mask:_(<2 x s32>) = G_LOAD %ptr :: (load (<2 x s32>), addrspace 1)%sign_mask:_(s32) = G_CONSTANT i32 -2147483648%sign_mask_splat:_(<2 x s32>) = G_BUILD_VECTOR %sign_mask:_(s32), %sign_mask:_(s32)%lshr_not_sign_mask:_(<2 x s32>) = G_LSHR %not_sign_mask, %shift_amt%copy_lshr_not_sign_mask:_(<2 x s32>) = COPY %lshr_not_sign_mask%lshr_sign_mask:_(<2 x s32>) = G_LSHR %sign_mask_splat, %shift_amt%copy_lshr_sign_mask:_(<2 x s32>) = COPY %lshr_sign_mask%or_pow2:_(<2 x s32>) = G_OR %zero_splat, %two_splat%copy_or_pow2:_(<2 x s32>) = COPY %or_pow2)";setUp(MIRString);if (!TM)return;GISelKnownBits KB(*MF);Register CopyZero = Copies[Copies.size() - 12];Register CopyOne = Copies[Copies.size() - 11];Register CopyTwo = Copies[Copies.size() - 10];Register CopyThree = Copies[Copies.size() - 9];Register CopyTruncTwo = Copies[Copies.size() - 8];Register CopyTruncThree = Copies[Copies.size() - 7];Register CopyTruncFive = Copies[Copies.size() - 6];Register CopyShl1 = Copies[Copies.size() - 5];Register CopyShl2 = Copies[Copies.size() - 4];Register CopyLShrNotSignMask = Copies[Copies.size() - 3];Register CopyLShrSignMask = Copies[Copies.size() - 2];Register CopyOrPow2 = Copies[Copies.size() - 1];EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyZero, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyOne, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyTwo, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyThree, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyTruncTwo, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyTruncThree, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyTruncFive, *MRI, &KB));// TODO: check for vector(splat) shift amount.EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyShl1, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyShl2, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyLShrNotSignMask, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyLShrSignMask, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyOrPow2, *MRI, &KB));}TEST_F(AArch64GISelMITest, TestVectorMetadata) {StringRef MIRString = R"(%imp:_(p0) = G_IMPLICIT_DEF%load:_(<2 x s8>) = G_LOAD %imp(p0) :: (load (<2 x s8>))%ext:_(<2 x s32>) = G_ZEXT %load(<2 x s8>)%cst_elt:_(s32) = G_CONSTANT i32 1%cst:_(<2 x s32>) = G_BUILD_VECTOR %cst_elt:_(s32), %cst_elt:_(s32)%and:_(<2 x s32>) = G_AND %ext, %cst%copy:_(<2 x s32>) = COPY %and(<2 x s32>))";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();MachineInstr *And = MRI->getVRegDef(SrcReg);MachineInstr *Ext = MRI->getVRegDef(And->getOperand(1).getReg());MachineInstr *Load = MRI->getVRegDef(Ext->getOperand(1).getReg());IntegerType *Int8Ty = Type::getInt8Ty(Context);Metadata *LowAndHigh[] = {ConstantAsMetadata::get(ConstantInt::get(Int8Ty, 0)),ConstantAsMetadata::get(ConstantInt::get(Int8Ty, 2))};auto *NewMDNode = MDNode::get(Context, LowAndHigh);const MachineMemOperand *OldMMO = *Load->memoperands_begin();MachineMemOperand NewMMO(OldMMO->getPointerInfo(), OldMMO->getFlags(),OldMMO->getSizeInBits(), OldMMO->getAlign(),OldMMO->getAAInfo(), NewMDNode);MachineIRBuilder MIB(*Load);MIB.buildLoad(Load->getOperand(0), Load->getOperand(1), NewMMO);Load->eraseFromParent();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(And->getOperand(1).getReg());EXPECT_TRUE(Res.One.isZero());APInt Mask(Res.getBitWidth(), 1);Mask.flipAllBits();EXPECT_EQ(Mask.getZExtValue(), Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestVectorKnownBitsExt) {StringRef MIRString = R"(%c1:_(s16) = G_CONSTANT i16 1%c1_splat:_(<2 x s16>) = G_BUILD_VECTOR %c1:_(s16), %c1:_(s16)%x:_(<2 x s16>) = G_IMPLICIT_DEF%y:_(<2 x s16>) = G_AND %x, %c1_splat%anyext:_(<2 x s32>) = G_ANYEXT %y(<2 x s16>)%r1:_(<2 x s32>) = COPY %anyext%zext:_(<2 x s32>) = G_ZEXT %y(<2 x s16>)%r2:_(<2 x s32>) = COPY %zext%sext:_(<2 x s32>) = G_SEXT %y(<2 x s16>)%r3:_(<2 x s32>) = COPY %sext)";setUp(MIRString);if (!TM)return;Register CopyRegAny = Copies[Copies.size() - 3];Register CopyRegZ = Copies[Copies.size() - 2];Register CopyRegS = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);MachineInstr *Copy;Register SrcReg;KnownBits Res;Copy = MRI->getVRegDef(CopyRegAny);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)32, Res.getBitWidth());EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0x0000fffe, Res.Zero.getZExtValue());Copy = MRI->getVRegDef(CopyRegZ);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)32, Res.getBitWidth());EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xfffffffe, Res.Zero.getZExtValue());Copy = MRI->getVRegDef(CopyRegS);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)32, Res.getBitWidth());EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xfffffffe, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorSextInReg) {StringRef MIRString = R"(; 000...0001%one:_(s32) = G_CONSTANT i32 1%one_splat:_(<2 x s32>) = G_BUILD_VECTOR %one:_(s32), %one:_(s32); 000...0010%two:_(s32) = G_CONSTANT i32 2%two_splat:_(<2 x s32>) = G_BUILD_VECTOR %two:_(s32), %two:_(s32); 000...1010%ten:_(s32) = G_CONSTANT i32 10%ten_splat:_(<2 x s32>) = G_BUILD_VECTOR %ten:_(s32), %ten:_(s32); ???...????%x0:_(<2 x s32>) = COPY $x0; ???...?1?%or:_(<2 x s32>) = G_OR %x0, %two_splat; All bits are known.%inreg1:_(<2 x s32>) = G_SEXT_INREG %one_splat, 1%copy_inreg1:_(<2 x s32>) = COPY %inreg1; All bits unknown%inreg2:_(<2 x s32>) = G_SEXT_INREG %or, 1%copy_inreg2:_(<2 x s32>) = COPY %inreg2; Extending from the only (known) set bit; 111...11?%inreg3:_(<2 x s32>) = G_SEXT_INREG %or, 2%copy_inreg3:_(<2 x s32>) = COPY %inreg3; Extending from a known set bit, overwriting all of the high set bits.; 111...1110%inreg4:_(<2 x s32>) = G_SEXT_INREG %ten_splat, 2%copy_inreg4:_(<2 x s32>) = COPY %inreg4)";setUp(MIRString);if (!TM)return;GISelKnownBits Info(*MF);KnownBits Res;auto GetKB = [&](unsigned Idx) {Register CopyReg = Copies[Idx];auto *Copy = MRI->getVRegDef(CopyReg);return Info.getKnownBits(Copy->getOperand(1).getReg());};Res = GetKB(Copies.size() - 4);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isAllOnes());Res = GetKB(Copies.size() - 3);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isUnknown());Res = GetKB(Copies.size() - 2);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(0u, Res.Zero.getZExtValue());Res = GetKB(Copies.size() - 1);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(1u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorAssertSext) {StringRef MIRString = R"(; 000...0001%one:_(s32) = G_CONSTANT i32 1%one_splat:_(<2 x s32>) = G_BUILD_VECTOR %one, %one; 000...0010%two:_(s32) = G_CONSTANT i32 2%two_splat:_(<2 x s32>) = G_BUILD_VECTOR %two, %two; 000...1010%ten:_(s32) = G_CONSTANT i32 10%ten_splat:_(<2 x s32>) = G_BUILD_VECTOR %ten, %ten; ???...????%x0:_(<2 x s32>) = COPY $x0; ???...?1?%or:_(<2 x s32>) = G_OR %x0, %two_splat; All bits are known.%assert_sext1:_(<2 x s32>) = G_ASSERT_SEXT %one_splat, 1%copy_assert_sext1:_(<2 x s32>) = COPY %assert_sext1; All bits unknown%assert_sext2:_(<2 x s32>) = G_ASSERT_SEXT %or, 1%copy_assert_sext2:_(<2 x s32>) = COPY %assert_sext2; Extending from the only (known) set bit; 111...11?%assert_sext3:_(<2 x s32>) = G_ASSERT_SEXT %or, 2%copy_assert_sext3:_(<2 x s32>) = COPY %assert_sext3; Extending from a known set bit, overwriting all of the high set bits.; 111...1110%assert_sext4:_(<2 x s32>) = G_ASSERT_SEXT %ten_splat, 2%copy_assert_sext4:_(<2 x s32>) = COPY %assert_sext4)";setUp(MIRString);if (!TM)return;GISelKnownBits Info(*MF);KnownBits Res;auto GetKB = [&](unsigned Idx) {Register CopyReg = Copies[Idx];auto *Copy = MRI->getVRegDef(CopyReg);return Info.getKnownBits(Copy->getOperand(1).getReg());};// Every bit is known to be a 1.Res = GetKB(Copies.size() - 4);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isAllOnes());// All bits are unknownRes = GetKB(Copies.size() - 3);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isUnknown());// Extending from the only known set bit// 111...11?Res = GetKB(Copies.size() - 2);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(0u, Res.Zero.getZExtValue());// Extending from a known set bit, overwriting all of the high set bits.// 111...1110Res = GetKB(Copies.size() - 1);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(1u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestVectorKnownBitsBSwapBitReverse) {StringRef MIRString = R"(%const:_(s32) = G_CONSTANT i32 287454020%const_splat:_(<2 x s32>) = G_BUILD_VECTOR %const:_(s32), %const:_(s32)%bswap:_(<2 x s32>) = G_BSWAP %const_splat%bitreverse:_(<2 x s32>) = G_BITREVERSE %const_splat%copy_bswap:_(<2 x s32>) = COPY %bswap%copy_bitreverse:_(<2 x s32>) = COPY %bitreverse)";setUp(MIRString);if (!TM)return;const uint32_t ByteSwappedVal = 0x44332211;const uint32_t BitSwappedVal = 0x22cc4488;Register CopyBSwap = Copies[Copies.size() - 2];Register CopyBitReverse = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);KnownBits BSwapKnown = Info.getKnownBits(CopyBSwap);EXPECT_EQ(32u, BSwapKnown.getBitWidth());EXPECT_EQ(ByteSwappedVal, BSwapKnown.One.getZExtValue());EXPECT_EQ(~ByteSwappedVal, BSwapKnown.Zero.getZExtValue());KnownBits BitReverseKnown = Info.getKnownBits(CopyBitReverse);EXPECT_EQ(32u, BitReverseKnown.getBitWidth());EXPECT_EQ(BitSwappedVal, BitReverseKnown.One.getZExtValue());EXPECT_EQ(~BitSwappedVal, BitReverseKnown.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorUMAX) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 10%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 1%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 3%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 12%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%umax0:_(<2 x s8>) = G_UMAX %val0, %val1%copy_umax0:_(<2 x s8>) = COPY %umax0%mask4:_(s8) = G_CONSTANT i8 14%mask4_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask4, %mask4%mask5:_(s8) = G_CONSTANT i8 2%mask5_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask5, %mask5%tmp3:_(<2 x s8>) = G_AND %unknown, %mask4_splat%val3:_(<2 x s8>) = G_OR %tmp3, %mask5_splat%mask6:_(s8) = G_CONSTANT i8 4%mask6_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask6, %mask6%mask7:_(s8) = G_CONSTANT i8 11%mask7_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask7, %mask7%tmp4:_(<2 x s8>) = G_AND %unknown, %mask6_splat%val4:_(<2 x s8>) = G_OR %tmp4, %mask7_splat%umax1:_(<2 x s8>) = G_UMAX %val3, %val4%copy_umax1:_(<2 x s8>) = COPY %umax1)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 2];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);EXPECT_EQ(0x0Cu, Res0.One.getZExtValue());EXPECT_EQ(0xF0u, Res0.Zero.getZExtValue());Register CopyReg1 = Copies[Copies.size() - 1];MachineInstr *FinalCopy1 = MRI->getVRegDef(CopyReg1);Register SrcReg1 = FinalCopy1->getOperand(1).getReg();KnownBits Res1 = Info.getKnownBits(SrcReg1);EXPECT_EQ(0x0Au, Res1.One.getZExtValue());EXPECT_EQ(0xF0u, Res1.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestVectorKnownBitsUMax) {StringRef MIRString = R"(%val:_(<2 x s32>) = COPY $x0%zext:_(<2 x s64>) = G_ZEXT %val%const:_(s64) = G_CONSTANT i64 -256%const_splat:_(<2 x s64>) = G_BUILD_VECTOR %const:_(s64), %const:_(s64)%umax:_(<2 x s64>) = G_UMAX %zext, %const_splat%copy_umax:_(<2 x s64>) = COPY %umax)";setUp(MIRString);if (!TM)return;Register CopyUMax = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);KnownBits KnownUmax = Info.getKnownBits(CopyUMax);EXPECT_EQ(64u, KnownUmax.getBitWidth());EXPECT_EQ(0xffu, KnownUmax.Zero.getZExtValue());EXPECT_EQ(0xffffffffffffff00, KnownUmax.One.getZExtValue());EXPECT_EQ(0xffu, KnownUmax.Zero.getZExtValue());EXPECT_EQ(0xffffffffffffff00, KnownUmax.One.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorUMIN) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 10%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 1%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 3%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 12%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%umin:_(<2 x s8>) = G_UMIN %val0, %val1%copy_umin:_(<2 x s8>) = COPY %umin)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 1];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);EXPECT_EQ(0x01u, Res0.One.getZExtValue());EXPECT_EQ(0xF4u, Res0.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorSMAX) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 128%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 64%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 1%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 128%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%smax:_(<2 x s8>) = G_SMAX %val0, %val1%copy_smax:_(<2 x s8>) = COPY %smax)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 1];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);EXPECT_EQ(0x40u, Res0.One.getZExtValue());EXPECT_EQ(0x3Fu, Res0.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorSMIN) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(<2 x s8>) = G_LOAD %ptr(p0) :: (load (<2 x s8>))%mask0:_(s8) = G_CONSTANT i8 128%mask0_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask0, %mask0%mask1:_(s8) = G_CONSTANT i8 64%mask1_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask1, %mask1%tmp0:_(<2 x s8>) = G_AND %unknown, %mask0_splat%val0:_(<2 x s8>) = G_OR %tmp0, %mask1_splat%mask2:_(s8) = G_CONSTANT i8 1%mask2_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask2, %mask2%mask3:_(s8) = G_CONSTANT i8 128%mask3_splat:_(<2 x s8>) = G_BUILD_VECTOR %mask3, %mask3%tmp1:_(<2 x s8>) = G_AND %unknown, %mask2_splat%val1:_(<2 x s8>) = G_OR %tmp1, %mask3_splat%smin:_(<2 x s8>) = G_SMIN %val0, %val1%copy_smin:_(<2 x s8>) = COPY %smin)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 1];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);EXPECT_EQ(0x80u, Res0.One.getZExtValue());EXPECT_EQ(0x7Eu, Res0.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestVectorInvalidQueries) {StringRef MIRString = R"(%src:_(<2 x s32>) = COPY $x0%thirty2:_(s32) = G_CONSTANT i32 32%thirty2_splat:_(<2 x s32>) = G_BUILD_VECTOR %thirty2:_(s32), %thirty2:_(s32)%equalSized:_(<2 x s32>) = G_SHL %src, %thirty2_splat%copy1:_(<2 x s32>) = COPY %equalSized%thirty3:_(s32) = G_CONSTANT i32 33%thirty3_splat:_(<2 x s32>) = G_BUILD_VECTOR %thirty3:_(s32), %thirty3:_(s32)%biggerSized:_(<2 x s32>) = G_SHL %src, %thirty3_splat%copy2:_(<2 x s32>) = COPY %biggerSized)";setUp(MIRString);if (!TM)return;Register EqSizedCopyReg = Copies[Copies.size() - 2];MachineInstr *EqSizedCopy = MRI->getVRegDef(EqSizedCopyReg);Register EqSizedShl = EqSizedCopy->getOperand(1).getReg();Register BiggerSizedCopyReg = Copies[Copies.size() - 1];MachineInstr *BiggerSizedCopy = MRI->getVRegDef(BiggerSizedCopyReg);Register BiggerSizedShl = BiggerSizedCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits EqSizeRes = Info.getKnownBits(EqSizedShl);KnownBits BiggerSizeRes = Info.getKnownBits(BiggerSizedShl);EXPECT_TRUE(EqSizeRes.One.isZero());EXPECT_TRUE(EqSizeRes.Zero.isZero());EXPECT_TRUE(BiggerSizeRes.One.isZero());EXPECT_TRUE(BiggerSizeRes.Zero.isZero());}TEST_F(AArch64GISelMITest, TestKnownBitsVectorAssertZext) {StringRef MIRString = R"(%copy_x0:_(s64) = COPY $x0%copy_x1:_(s64) = COPY $x1%x0_x1:_(<2 x s64>) = G_BUILD_VECTOR %copy_x0, %copy_x1%assert8:_(<2 x s64>) = G_ASSERT_ZEXT %x0_x1, 8%copy_assert8:_(<2 x s64>) = COPY %assert8%assert1:_(<2 x s64>) = G_ASSERT_ZEXT %x0_x1, 1%copy_assert1:_(<2 x s64>) = COPY %assert1%assert63:_(<2 x s64>) = G_ASSERT_ZEXT %x0_x1, 63%copy_assert63:_(<2 x s64>) = COPY %assert63%assert3:_(<2 x s64>) = G_ASSERT_ZEXT %x0_x1, 3%copy_assert3:_(<2 x s64>) = COPY %assert3)";setUp(MIRString);if (!TM)return;Register CopyAssert8 = Copies[Copies.size() - 4];Register CopyAssert1 = Copies[Copies.size() - 3];Register CopyAssert63 = Copies[Copies.size() - 2];Register CopyAssert3 = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);MachineInstr *Copy;Register SrcReg;KnownBits Res;// Assert zero-extension from an 8-bit value.Copy = MRI->getVRegDef(CopyAssert8);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFFFFFFFF00u, Res.Zero.getZExtValue());// Assert zero-extension from a 1-bit value.Copy = MRI->getVRegDef(CopyAssert1);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFFFFFFFFFE, Res.Zero.getZExtValue());// Assert zero-extension from a 63-bit value.Copy = MRI->getVRegDef(CopyAssert63);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0x8000000000000000u, Res.Zero.getZExtValue());// Assert zero-extension from a 3-bit value.Copy = MRI->getVRegDef(CopyAssert3);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFFFFFFFFF8u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestNumSignBitsUAddoOverflow) {StringRef MIRString = R"(%copy_x0:_(s64) = COPY $x0%copy_x1:_(s64) = COPY $x1%x0_x1:_(<2 x s64>) = G_BUILD_VECTOR %copy_x0, %copy_x1%uaddo:_(<2 x s64>), %overflow:_(<2 x s32>) = G_UADDO %x0_x1, %x0_x1%result:_(<2 x s32>) = COPY %overflow)";setUp(MIRString);if (!TM)return;Register CopyOverflow = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);// Assert sign-extension from vector booleanEXPECT_EQ(32u, Info.computeNumSignBits(CopyOverflow));}
//===- KnownBitsTest.cpp -------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h"#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"TEST_F(AArch64GISelMITest, TestKnownBitsCst) {StringRef MIRString = " %3:_(s8) = G_CONSTANT i8 1\n"" %4:_(s8) = COPY %3\n";setUp(MIRString);if (!TM)return;unsigned CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);unsigned SrcReg = FinalCopy->getOperand(1).getReg();unsigned DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)1, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xfe, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsCstWithClass) {StringRef MIRString = " %10:gpr32 = MOVi32imm 1\n"" %4:_(s32) = COPY %10\n";setUp(MIRString);if (!TM)return;unsigned CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);unsigned SrcReg = FinalCopy->getOperand(1).getReg();unsigned DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// We can't analyze %3 due to the register class constraint. We will get a// default-constructed KnownBits back.EXPECT_EQ((uint64_t)1, Res.getBitWidth());EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);// We still don't know the values due to the register class constraint but %4// did reveal the size of %3.EXPECT_EQ((uint64_t)32, Res2.getBitWidth());EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}// Check that we are able to track bits through PHIs// and get the intersections of everything we know on each operand.TEST_F(AArch64GISelMITest, TestKnownBitsCstPHI) {StringRef MIRString = " bb.10:\n"" %10:_(s8) = G_CONSTANT i8 3\n"" %11:_(s1) = G_IMPLICIT_DEF\n"" G_BRCOND %11(s1), %bb.11\n"" G_BR %bb.12\n""\n"" bb.11:\n"" %12:_(s8) = G_CONSTANT i8 2\n"" G_BR %bb.12\n""\n"" bb.12:\n"" %13:_(s8) = PHI %10(s8), %bb.10, %12(s8), %bb.11\n"" %14:_(s8) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)2, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xfc, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}// Check that we report we know nothing when we hit a// non-generic register.// Note: this could be improved though!TEST_F(AArch64GISelMITest, TestKnownBitsCstPHIToNonGenericReg) {StringRef MIRString = " bb.10:\n"" %10:gpr32 = MOVi32imm 3\n"" %11:_(s1) = G_IMPLICIT_DEF\n"" G_BRCOND %11(s1), %bb.11\n"" G_BR %bb.12\n""\n"" bb.11:\n"" %12:_(s8) = G_CONSTANT i8 2\n"" G_BR %bb.12\n""\n"" bb.12:\n"" %13:_(s8) = PHI %10, %bb.10, %12(s8), %bb.11\n"" %14:_(s8) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}// Check that we know nothing when at least one value of a PHI// comes from something we cannot analysis.// This test is not particularly interesting, it is just// here to cover the code that stops the analysis of PHIs// earlier. In that case, we would not even look at the// second incoming value.TEST_F(AArch64GISelMITest, TestKnownBitsUnknownPHI) {StringRef MIRString =" bb.10:\n"" %10:_(s64) = COPY %0\n"" %11:_(s1) = G_IMPLICIT_DEF\n"" G_BRCOND %11(s1), %bb.11\n"" G_BR %bb.12\n""\n"" bb.11:\n"" %12:_(s64) = G_CONSTANT i64 2\n"" G_BR %bb.12\n""\n"" bb.12:\n"" %13:_(s64) = PHI %10(s64), %bb.10, %12(s64), %bb.11\n"" %14:_(s64) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}// Check that we manage to process PHIs that loop on themselves.// For now, the analysis just stops and assumes it knows nothing,// eventually we could teach it how to properly track phis that// loop back.TEST_F(AArch64GISelMITest, TestKnownBitsCstPHIWithLoop) {StringRef MIRString =" bb.10:\n"" %10:_(s8) = G_CONSTANT i8 3\n"" %11:_(s1) = G_IMPLICIT_DEF\n"" G_BRCOND %11(s1), %bb.11\n"" G_BR %bb.12\n""\n"" bb.11:\n"" %12:_(s8) = G_CONSTANT i8 2\n"" G_BR %bb.12\n""\n"" bb.12:\n"" %13:_(s8) = PHI %10(s8), %bb.10, %12(s8), %bb.11, %14(s8), %bb.12\n"" %14:_(s8) = COPY %13\n"" G_BR %bb.12\n";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}// Check that we don't try to analysis PHIs progression.// Setting a deep enough max depth would allow to effectively simulate// what happens in the loop.// Thus, with a deep enough depth, we could actually figure out// that %14's zero known bits are actually at least what we know// for %10, right shifted by one.// However, this process is super expensive compile-time wise and// we don't want to reach that conclusion while playing with max depth.// For now, the analysis just stops and assumes it knows nothing// on PHIs, but eventually we could teach it how to properly track// phis that loop back without relying on the luck effect of max// depth.TEST_F(AArch64GISelMITest, TestKnownBitsDecreasingCstPHIWithLoop) {StringRef MIRString = " bb.10:\n"" %10:_(s8) = G_CONSTANT i8 5\n"" %11:_(s8) = G_CONSTANT i8 1\n""\n"" bb.12:\n"" %13:_(s8) = PHI %10(s8), %bb.10, %14(s8), %bb.12\n"" %14:_(s8) = G_LSHR %13, %11\n"" %15:_(s8) = COPY %14\n"" G_BR %bb.12\n";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();Register DstReg = FinalCopy->getOperand(0).getReg();GISelKnownBits Info(*MF, /*MaxDepth=*/24);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());// A single iteration on the PHI (%13) gives:// %10 has known zero of 0xFA// %12 has known zero of 0x80 (we shift right by one so high bit is zero)// Therefore, %14's known zero are 0x80 shifted by one 0xC0.// If we had simulated the loop we could have more zero bits, basically// up to 0xFC (count leading zero of 5, + 1).EXPECT_EQ((uint64_t)0xC0, Res.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(DstReg);EXPECT_EQ(Res.One.getZExtValue(), Res2.One.getZExtValue());EXPECT_EQ(Res.Zero.getZExtValue(), Res2.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsPtrToIntViceVersa) {StringRef MIRString = " %3:_(s16) = G_CONSTANT i16 256\n"" %4:_(p0) = G_INTTOPTR %3\n"" %5:_(s32) = G_PTRTOINT %4\n"" %6:_(s32) = COPY %5\n";setUp(MIRString);if (!TM)return;unsigned CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);unsigned SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(256u, Res.One.getZExtValue());EXPECT_EQ(0xfffffeffu, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsAND) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 52%mask1:_(s8) = G_CONSTANT i8 10%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 32%mask3:_(s8) = G_CONSTANT i8 24%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%and:_(s8) = G_AND %val0, %val1%copy_and:_(s8) = COPY %and)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// 00??1?10// & 00?11000// = 00??1000EXPECT_EQ(0x08u, Res.One.getZExtValue());EXPECT_EQ(0xC7u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsOR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 52%mask1:_(s8) = G_CONSTANT i8 10%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 32%mask3:_(s8) = G_CONSTANT i8 24%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%or:_(s8) = G_OR %val0, %val1%copy_or:_(s8) = COPY %or)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// 00??1?10// | 00?11000// = 00?11?10EXPECT_EQ(0x1Au, Res.One.getZExtValue());EXPECT_EQ(0xC1u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsXOR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 52%mask1:_(s8) = G_CONSTANT i8 10%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 32%mask3:_(s8) = G_CONSTANT i8 24%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%xor:_(s8) = G_XOR %val0, %val1%copy_xor:_(s8) = COPY %xor)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// Xor KnowBits does not track if we are doing xor of unknown bit with itself// or negated itself.// 00??1?10// ^ 00?11000// = 00??0?10EXPECT_EQ(0x02u, Res.One.getZExtValue());EXPECT_EQ(0xC9u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsXORConstant) {StringRef MIRString = " %3:_(s8) = G_CONSTANT i8 4\n"" %4:_(s8) = G_CONSTANT i8 7\n"" %5:_(s8) = G_XOR %3, %4\n"" %6:_(s8) = COPY %5\n";setUp(MIRString);if (!TM)return;unsigned CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);unsigned SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);EXPECT_EQ(3u, Res.One.getZExtValue());EXPECT_EQ(252u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsASHR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 38%mask1:_(s8) = G_CONSTANT i8 202%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%cst0:_(s8) = G_CONSTANT i8 2%ashr0:_(s8) = G_ASHR %val0, %cst0%copy_ashr0:_(s8) = COPY %ashr0%mask2:_(s8) = G_CONSTANT i8 204%mask3:_(s8) = G_CONSTANT i8 18%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%ashr1:_(s8) = G_ASHR %val1, %cst0%copy_ashr1:_(s8) = COPY %ashr1)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 2];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);// 11?01??0 >> 2// = 1111?01?EXPECT_EQ(0xF2u, Res0.One.getZExtValue());EXPECT_EQ(0x04u, Res0.Zero.getZExtValue());Register CopyReg1 = Copies[Copies.size() - 1];MachineInstr *FinalCopy1 = MRI->getVRegDef(CopyReg1);Register SrcReg1 = FinalCopy1->getOperand(1).getReg();KnownBits Res1 = Info.getKnownBits(SrcReg1);// ??01??10 >> 2// = ????01??EXPECT_EQ(0x04u, Res1.One.getZExtValue());EXPECT_EQ(0x08u, Res1.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsLSHR) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 38%mask1:_(s8) = G_CONSTANT i8 202%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%cst0:_(s8) = G_CONSTANT i8 2%lshr0:_(s8) = G_LSHR %val0, %cst0%copy_lshr0:_(s8) = COPY %lshr0%mask2:_(s8) = G_CONSTANT i8 204%mask3:_(s8) = G_CONSTANT i8 18%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%lshr1:_(s8) = G_LSHR %val1, %cst0%copy_lshr1:_(s8) = COPY %lshr1)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 2];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);// 11?01??0 >> 2// = 0011?01?EXPECT_EQ(0x32u, Res0.One.getZExtValue());EXPECT_EQ(0xC4u, Res0.Zero.getZExtValue());Register CopyReg1 = Copies[Copies.size() - 1];MachineInstr *FinalCopy1 = MRI->getVRegDef(CopyReg1);Register SrcReg1 = FinalCopy1->getOperand(1).getReg();KnownBits Res1 = Info.getKnownBits(SrcReg1);// ??01??10 >> 2// = 00??01??EXPECT_EQ(0x04u, Res1.One.getZExtValue());EXPECT_EQ(0xC8u, Res1.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsSHL) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 51%mask1:_(s8) = G_CONSTANT i8 72%tmp:_(s8) = G_AND %unknown, %mask0%val:_(s8) = G_OR %tmp, %mask1%cst:_(s8) = G_CONSTANT i8 3%shl:_(s8) = G_SHL %val, %cst%copy_shl:_(s8) = COPY %shl)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// 01??10?? << 3// = ?10??000EXPECT_EQ(0x40u, Res.One.getZExtValue());EXPECT_EQ(0x27u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsADD) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s16) = G_LOAD %ptr(p0) :: (load (s16))%mask0:_(s16) = G_CONSTANT i16 4642%mask1:_(s16) = G_CONSTANT i16 9536%tmp0:_(s16) = G_AND %unknown, %mask0%val0:_(s16) = G_OR %tmp0, %mask1%mask2:_(s16) = G_CONSTANT i16 4096%mask3:_(s16) = G_CONSTANT i16 371%tmp1:_(s16) = G_AND %unknown, %mask2%val1:_(s16) = G_OR %tmp1, %mask3%add:_(s16) = G_ADD %val0, %val1%copy_add:_(s16) = COPY %add)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// Add KnowBits works out known carry bits first and then calculates result.// 001?01?101?000?0// + 000?000101110011// = 0??????01??10??1EXPECT_EQ(0x0091u, Res.One.getZExtValue());EXPECT_EQ(0x8108u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsSUB) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s16) = G_LOAD %ptr(p0) :: (load (s16))%mask0:_(s16) = G_CONSTANT i16 4642%mask1:_(s16) = G_CONSTANT i16 9536%tmp0:_(s16) = G_AND %unknown, %mask0%val0:_(s16) = G_OR %tmp0, %mask1%mask2:_(s16) = G_CONSTANT i16 4096%mask3:_(s16) = G_CONSTANT i16 371%tmp1:_(s16) = G_AND %unknown, %mask2%val1:_(s16) = G_OR %tmp1, %mask3%sub:_(s16) = G_SUB %val0, %val1%copy_sub:_(s16) = COPY %sub)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// Sub KnowBits for LHS - RHS use Add KnownBits for LHS + ~RHS + 1.EXPECT_EQ(0x01CDu, Res.One.getZExtValue());EXPECT_EQ(0xC810u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsMUL) {StringRef MIRString = R"(%ptr0:_(p0) = G_IMPLICIT_DEF%load0:_(s16) = G_LOAD %ptr0(p0) :: (load (s16))%mask0:_(s16) = G_CONSTANT i16 4%mask1:_(s16) = G_CONSTANT i16 18%tmp:_(s16) = G_AND %load0, %mask0%val0:_(s16) = G_OR %tmp, %mask1%cst:_(s16) = G_CONSTANT i16 12%mul:_(s16) = G_MUL %val0, %cst%copy_mul:_(s16) = COPY %mul)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// Mul KnowBits are conservatively correct, but not guaranteed to be precise.// Precise for trailing bits up to the first unknown bit.// 00010?10 * 00001100 =// 00010?1000// + 00010?10000// = 0000000010??1000// KB 0000000?????1000EXPECT_EQ(0x0008u, Res.One.getZExtValue());EXPECT_EQ(0xFE07u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsICMP) {StringRef MIRString = R"(%cst0:_(s32) = G_CONSTANT i32 0%cst1:_(s32) = G_CONSTANT i32 1%icmp:_(s32) = G_ICMP intpred(ne), %cst0, %cst1%copy_icmp:_(s32) = COPY %icmp)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// For targets that use 0 or 1 as icmp result in large register set high bits// to 0, does not analyze operands/compare predicate.EXPECT_EQ(0x00000000u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFEu, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsFCMP) {StringRef MIRString = R"(%cst0:_(s32) = G_FCONSTANT float 0.0%cst1:_(s32) = G_FCONSTANT float 1.0%fcmp:_(s32) = G_FCMP floatpred(one), %cst0, %cst1%copy_fcmp:_(s32) = COPY %fcmp)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// For targets that use 0 or 1 as fcmp result in large register set high bits// to 0, does not analyze operands/compare predicate.EXPECT_EQ(0x00000000u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFEu, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsSelect) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 24%mask1:_(s8) = G_CONSTANT i8 224%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 146%mask3:_(s8) = G_CONSTANT i8 36%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%cond:_(s1) = G_CONSTANT i1 false%select:_(s8) = G_SELECT %cond, %val0, %val1%copy_select:_(s8) = COPY %select)";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(SrcReg);// Select KnownBits takes common bits of LHS and RHS, does not analyze// condition operand.// 111??000// select ?01?01?0// = ??1????0EXPECT_EQ(0x20u, Res.One.getZExtValue());EXPECT_EQ(0x01u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBits) {StringRef MIR = " %3:_(s32) = G_TRUNC %0\n"" %4:_(s32) = G_TRUNC %1\n"" %5:_(s32) = G_CONSTANT i32 5\n"" %6:_(s32) = G_CONSTANT i32 24\n"" %7:_(s32) = G_CONSTANT i32 28\n"" %14:_(p0) = G_INTTOPTR %7\n"" %16:_(s32) = G_PTRTOINT %14\n"" %8:_(s32) = G_SHL %3, %5\n"" %9:_(s32) = G_SHL %4, %5\n"" %10:_(s32) = G_OR %8, %6\n"" %11:_(s32) = G_OR %9, %16\n"" %12:_(s32) = G_MUL %10, %11\n"" %13:_(s32) = COPY %12\n";setUp(MIR);if (!TM)return;unsigned CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);unsigned SrcReg = FinalCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Known = Info.getKnownBits(SrcReg);EXPECT_FALSE(Known.hasConflict());EXPECT_EQ(32u, Known.One.getZExtValue());EXPECT_EQ(95u, Known.Zero.getZExtValue());APInt Zeroes = Info.getKnownZeroes(SrcReg);EXPECT_EQ(Known.Zero, Zeroes);}TEST_F(AArch64GISelMITest, TestSignBitIsZero) {setUp();if (!TM)return;const LLT S32 = LLT::scalar(32);auto SignBit = B.buildConstant(S32, 0x80000000);auto Zero = B.buildConstant(S32, 0);GISelKnownBits KnownBits(*MF);EXPECT_TRUE(KnownBits.signBitIsZero(Zero.getReg(0)));EXPECT_FALSE(KnownBits.signBitIsZero(SignBit.getReg(0)));}TEST_F(AArch64GISelMITest, TestNumSignBitsConstant) {StringRef MIRString = " %3:_(s8) = G_CONSTANT i8 1\n"" %4:_(s8) = COPY %3\n"" %5:_(s8) = G_CONSTANT i8 -1\n"" %6:_(s8) = COPY %5\n"" %7:_(s8) = G_CONSTANT i8 127\n"" %8:_(s8) = COPY %7\n"" %9:_(s8) = G_CONSTANT i8 32\n"" %10:_(s8) = COPY %9\n"" %11:_(s8) = G_CONSTANT i8 -32\n"" %12:_(s8) = COPY %11\n";setUp(MIRString);if (!TM)return;Register CopyReg1 = Copies[Copies.size() - 5];Register CopyRegNeg1 = Copies[Copies.size() - 4];Register CopyReg127 = Copies[Copies.size() - 3];Register CopyReg32 = Copies[Copies.size() - 2];Register CopyRegNeg32 = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(7u, Info.computeNumSignBits(CopyReg1));EXPECT_EQ(8u, Info.computeNumSignBits(CopyRegNeg1));EXPECT_EQ(1u, Info.computeNumSignBits(CopyReg127));EXPECT_EQ(2u, Info.computeNumSignBits(CopyReg32));EXPECT_EQ(3u, Info.computeNumSignBits(CopyRegNeg32));}TEST_F(AArch64GISelMITest, TestNumSignBitsSext) {StringRef MIRString = " %3:_(p0) = G_IMPLICIT_DEF\n"" %4:_(s8) = G_LOAD %3 :: (load (s8))\n"" %5:_(s32) = G_SEXT %4\n"" %6:_(s32) = COPY %5\n"" %7:_(s8) = G_CONSTANT i8 -1\n"" %8:_(s32) = G_SEXT %7\n"" %9:_(s32) = COPY %8\n";setUp(MIRString);if (!TM)return;Register CopySextLoad = Copies[Copies.size() - 2];Register CopySextNeg1 = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(25u, Info.computeNumSignBits(CopySextLoad));EXPECT_EQ(32u, Info.computeNumSignBits(CopySextNeg1));}TEST_F(AArch64GISelMITest, TestNumSignBitsSextInReg) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%load4:_(s32) = G_LOAD %ptr :: (load (s32))%inreg7:_(s32) = G_SEXT_INREG %load4, 7%copy_inreg7:_(s32) = COPY %inreg7%inreg8:_(s32) = G_SEXT_INREG %load4, 8%copy_inreg8:_(s32) = COPY %inreg8%inreg9:_(s32) = G_SEXT_INREG %load4, 9%copy_inreg9:_(s32) = COPY %inreg9%inreg31:_(s32) = G_SEXT_INREG %load4, 31%copy_inreg31:_(s32) = COPY %inreg31%load1:_(s8) = G_LOAD %ptr :: (load (s8))%sext_load1:_(s32) = G_SEXT %load1%inreg6_sext:_(s32) = G_SEXT_INREG %sext_load1, 6%copy_inreg6_sext:_(s32) = COPY %inreg6_sext%inreg7_sext:_(s32) = G_SEXT_INREG %sext_load1, 7%copy_inreg7_sext:_(s32) = COPY %inreg7_sext%inreg8_sext:_(s32) = G_SEXT_INREG %sext_load1, 8%copy_inreg8_sext:_(s32) = COPY %inreg8_sext%inreg9_sext:_(s32) = G_SEXT_INREG %sext_load1, 9%copy_inreg9_sext:_(s32) = COPY %inreg9_sext%inreg31_sext:_(s32) = G_SEXT_INREG %sext_load1, 31%copy_inreg31_sext:_(s32) = COPY %inreg31_sext)";setUp(MIRString);if (!TM)return;Register CopyInReg7 = Copies[Copies.size() - 9];Register CopyInReg8 = Copies[Copies.size() - 8];Register CopyInReg9 = Copies[Copies.size() - 7];Register CopyInReg31 = Copies[Copies.size() - 6];Register CopyInReg6Sext = Copies[Copies.size() - 5];Register CopyInReg7Sext = Copies[Copies.size() - 4];Register CopyInReg8Sext = Copies[Copies.size() - 3];Register CopyInReg9Sext = Copies[Copies.size() - 2];Register CopyInReg31Sext = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8));EXPECT_EQ(24u, Info.computeNumSignBits(CopyInReg9));EXPECT_EQ(2u, Info.computeNumSignBits(CopyInReg31));EXPECT_EQ(27u, Info.computeNumSignBits(CopyInReg6Sext));EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg9Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg31Sext));}TEST_F(AArch64GISelMITest, TestNumSignBitsAssertSext) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%load4:_(s32) = G_LOAD %ptr :: (load (s32))%assert_sext1:_(s32) = G_ASSERT_SEXT %load4, 1%copy_assert_sext1:_(s32) = COPY %assert_sext1%assert_sext7:_(s32) = G_ASSERT_SEXT %load4, 7%copy_assert_sext7:_(s32) = COPY %assert_sext7%assert_sext8:_(s32) = G_ASSERT_SEXT %load4, 8%copy_assert_sext8:_(s32) = COPY %assert_sext8%assert_sext9:_(s32) = G_ASSERT_SEXT %load4, 9%copy_assert_sext9:_(s32) = COPY %assert_sext9%assert_sext31:_(s32) = G_ASSERT_SEXT %load4, 31%copy_assert_sext31:_(s32) = COPY %assert_sext31%load1:_(s8) = G_LOAD %ptr :: (load (s8))%sext_load1:_(s32) = G_SEXT %load1%assert_sext6_sext:_(s32) = G_ASSERT_SEXT %sext_load1, 6%copy_assert_sext6_sext:_(s32) = COPY %assert_sext6_sext%assert_sext7_sext:_(s32) = G_ASSERT_SEXT %sext_load1, 7%copy_assert_sext7_sext:_(s32) = COPY %assert_sext7_sext%assert_sext8_sext:_(s32) = G_ASSERT_SEXT %sext_load1, 8%copy_assert_sext8_sext:_(s32) = COPY %assert_sext8_sext%assert_sext9_sext:_(s32) = G_ASSERT_SEXT %sext_load1, 9%copy_assert_sext9_sext:_(s32) = COPY %assert_sext9_sext%assert_sext31_sext:_(s32) = G_ASSERT_SEXT %sext_load1, 31%copy_assert_sext31_sext:_(s32) = COPY %assert_sext31_sext)";setUp(MIRString);if (!TM)return;Register CopyInReg1 = Copies[Copies.size() - 10];Register CopyInReg7 = Copies[Copies.size() - 9];Register CopyInReg8 = Copies[Copies.size() - 8];Register CopyInReg9 = Copies[Copies.size() - 7];Register CopyInReg31 = Copies[Copies.size() - 6];Register CopyInReg6Sext = Copies[Copies.size() - 5];Register CopyInReg7Sext = Copies[Copies.size() - 4];Register CopyInReg8Sext = Copies[Copies.size() - 3];Register CopyInReg9Sext = Copies[Copies.size() - 2];Register CopyInReg31Sext = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(32u, Info.computeNumSignBits(CopyInReg1));EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8));EXPECT_EQ(24u, Info.computeNumSignBits(CopyInReg9));EXPECT_EQ(2u, Info.computeNumSignBits(CopyInReg31));EXPECT_EQ(27u, Info.computeNumSignBits(CopyInReg6Sext));EXPECT_EQ(26u, Info.computeNumSignBits(CopyInReg7Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg8Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg9Sext));EXPECT_EQ(25u, Info.computeNumSignBits(CopyInReg31Sext));}TEST_F(AArch64GISelMITest, TestNumSignBitsTrunc) {StringRef MIRString = " %3:_(p0) = G_IMPLICIT_DEF\n"" %4:_(s32) = G_LOAD %3 :: (load (s32))\n"" %5:_(s8) = G_TRUNC %4\n"" %6:_(s8) = COPY %5\n"" %7:_(s32) = G_CONSTANT i32 -1\n"" %8:_(s8) = G_TRUNC %7\n"" %9:_(s8) = COPY %8\n"" %10:_(s32) = G_CONSTANT i32 7\n"" %11:_(s8) = G_TRUNC %10\n"" %12:_(s8) = COPY %11\n";setUp(MIRString);if (!TM)return;Register CopyTruncLoad = Copies[Copies.size() - 3];Register CopyTruncNeg1 = Copies[Copies.size() - 2];Register CopyTrunc7 = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(1u, Info.computeNumSignBits(CopyTruncLoad));EXPECT_EQ(8u, Info.computeNumSignBits(CopyTruncNeg1));EXPECT_EQ(5u, Info.computeNumSignBits(CopyTrunc7));}TEST_F(AMDGPUGISelMITest, TestNumSignBitsTrunc) {StringRef MIRString =" %3:_(<4 x s32>) = G_IMPLICIT_DEF\n"" %4:_(s32) = G_IMPLICIT_DEF\n"" %5:_(s32) = G_AMDGPU_BUFFER_LOAD_UBYTE %3, %4, %4, %4, 0, 0, 0 :: (load (s8))\n"" %6:_(s32) = COPY %5\n"" %7:_(s32) = G_AMDGPU_BUFFER_LOAD_SBYTE %3, %4, %4, %4, 0, 0, 0 :: (load (s8))\n"" %8:_(s32) = COPY %7\n"" %9:_(s32) = G_AMDGPU_BUFFER_LOAD_USHORT %3, %4, %4, %4, 0, 0, 0 :: (load (s16))\n"" %10:_(s32) = COPY %9\n"" %11:_(s32) = G_AMDGPU_BUFFER_LOAD_SSHORT %3, %4, %4, %4, 0, 0, 0 :: (load (s16))\n"" %12:_(s32) = COPY %11\n";setUp(MIRString);if (!TM)return;Register CopyLoadUByte = Copies[Copies.size() - 4];Register CopyLoadSByte = Copies[Copies.size() - 3];Register CopyLoadUShort = Copies[Copies.size() - 2];Register CopyLoadSShort = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(24u, Info.computeNumSignBits(CopyLoadUByte));EXPECT_EQ(25u, Info.computeNumSignBits(CopyLoadSByte));EXPECT_EQ(16u, Info.computeNumSignBits(CopyLoadUShort));EXPECT_EQ(17u, Info.computeNumSignBits(CopyLoadSShort));}TEST_F(AMDGPUGISelMITest, TestTargetKnownAlign) {StringRef MIRString =" %5:_(p4) = G_INTRINSIC intrinsic(@llvm.amdgcn.dispatch.ptr)\n"" %6:_(p4) = COPY %5\n"" %7:_(p4) = G_INTRINSIC intrinsic(@llvm.amdgcn.queue.ptr)\n"" %8:_(p4) = COPY %7\n"" %9:_(p4) = G_INTRINSIC intrinsic(@llvm.amdgcn.kernarg.segment.ptr)\n"" %10:_(p4) = COPY %9\n"" %11:_(p4) = G_INTRINSIC intrinsic(@llvm.amdgcn.implicitarg.ptr)\n"" %12:_(p4) = COPY %11\n"" %13:_(p4) = G_INTRINSIC intrinsic(@llvm.amdgcn.implicit.buffer.ptr)\n"" %14:_(p4) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyDispatchPtr = Copies[Copies.size() - 5];Register CopyQueuePtr = Copies[Copies.size() - 4];Register CopyKernargSegmentPtr = Copies[Copies.size() - 3];Register CopyImplicitArgPtr = Copies[Copies.size() - 2];Register CopyImplicitBufferPtr = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(Align(4), Info.computeKnownAlignment(CopyDispatchPtr));EXPECT_EQ(Align(4), Info.computeKnownAlignment(CopyQueuePtr));EXPECT_EQ(Align(4), Info.computeKnownAlignment(CopyKernargSegmentPtr));EXPECT_EQ(Align(4), Info.computeKnownAlignment(CopyImplicitArgPtr));EXPECT_EQ(Align(4), Info.computeKnownAlignment(CopyImplicitBufferPtr));}TEST_F(AMDGPUGISelMITest, TestIsKnownToBeAPowerOfTwo) {StringRef MIRString = R"MIR(%zero:_(s32) = G_CONSTANT i32 0%one:_(s32) = G_CONSTANT i32 1%two:_(s32) = G_CONSTANT i32 2%three:_(s32) = G_CONSTANT i32 3%five:_(s32) = G_CONSTANT i32 5%copy_zero:_(s32) = COPY %zero%copy_one:_(s32) = COPY %one%copy_two:_(s32) = COPY %two%copy_three:_(s32) = COPY %three%trunc_two:_(s1) = G_TRUNC %two%trunc_three:_(s1) = G_TRUNC %three%trunc_five:_(s1) = G_TRUNC %five%copy_trunc_two:_(s1) = COPY %trunc_two%copy_trunc_three:_(s1) = COPY %trunc_three%copy_trunc_five:_(s1) = COPY %trunc_five%ptr:_(p1) = G_IMPLICIT_DEF%shift_amt:_(s32) = G_LOAD %ptr :: (load (s32), addrspace 1)%shl_1:_(s32) = G_SHL %one, %shift_amt%copy_shl_1:_(s32) = COPY %shl_1%shl_2:_(s32) = G_SHL %two, %shift_amt%copy_shl_2:_(s32) = COPY %shl_2%not_sign_mask:_(s32) = G_LOAD %ptr :: (load (s32), addrspace 1)%sign_mask:_(s32) = G_CONSTANT i32 -2147483648%lshr_not_sign_mask:_(s32) = G_LSHR %not_sign_mask, %shift_amt%copy_lshr_not_sign_mask:_(s32) = COPY %lshr_not_sign_mask%lshr_sign_mask:_(s32) = G_LSHR %sign_mask, %shift_amt%copy_lshr_sign_mask:_(s32) = COPY %lshr_sign_mask%or_pow2:_(s32) = G_OR %zero, %two%copy_or_pow2:_(s32) = COPY %or_pow2)MIR";setUp(MIRString);if (!TM)return;GISelKnownBits KB(*MF);Register CopyZero = Copies[Copies.size() - 12];Register CopyOne = Copies[Copies.size() - 11];Register CopyTwo = Copies[Copies.size() - 10];Register CopyThree = Copies[Copies.size() - 9];Register CopyTruncTwo = Copies[Copies.size() - 8];Register CopyTruncThree = Copies[Copies.size() - 7];Register CopyTruncFive = Copies[Copies.size() - 6];Register CopyShl1 = Copies[Copies.size() - 5];Register CopyShl2 = Copies[Copies.size() - 4];Register CopyLShrNotSignMask = Copies[Copies.size() - 3];Register CopyLShrSignMask = Copies[Copies.size() - 2];Register CopyOrPow2 = Copies[Copies.size() - 1];EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyZero, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyOne, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyTwo, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyThree, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyTruncTwo, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyTruncThree, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyTruncFive, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyShl1, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyShl2, *MRI, &KB));EXPECT_FALSE(isKnownToBeAPowerOfTwo(CopyLShrNotSignMask, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyLShrSignMask, *MRI, &KB));EXPECT_TRUE(isKnownToBeAPowerOfTwo(CopyOrPow2, *MRI, &KB));}TEST_F(AArch64GISelMITest, TestMetadata) {StringRef MIRString = " %imp:_(p0) = G_IMPLICIT_DEF\n"" %load:_(s8) = G_LOAD %imp(p0) :: (load (s8))\n"" %ext:_(s32) = G_ZEXT %load(s8)\n"" %cst:_(s32) = G_CONSTANT i32 1\n"" %and:_(s32) = G_AND %ext, %cst\n"" %copy:_(s32) = COPY %and(s32)\n";setUp(MIRString);if (!TM)return;Register CopyReg = Copies[Copies.size() - 1];MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);Register SrcReg = FinalCopy->getOperand(1).getReg();// We need a load with a metadata range for this to break. Fudge the load in// the string and replace it with something we can work with.MachineInstr *And = MRI->getVRegDef(SrcReg);MachineInstr *Ext = MRI->getVRegDef(And->getOperand(1).getReg());MachineInstr *Load = MRI->getVRegDef(Ext->getOperand(1).getReg());IntegerType *Int8Ty = Type::getInt8Ty(Context);// Value must be in [0, 2)Metadata *LowAndHigh[] = {ConstantAsMetadata::get(ConstantInt::get(Int8Ty, 0)),ConstantAsMetadata::get(ConstantInt::get(Int8Ty, 2))};auto NewMDNode = MDNode::get(Context, LowAndHigh);const MachineMemOperand *OldMMO = *Load->memoperands_begin();MachineMemOperand NewMMO(OldMMO->getPointerInfo(), OldMMO->getFlags(),OldMMO->getSizeInBits(), OldMMO->getAlign(),OldMMO->getAAInfo(), NewMDNode);MachineIRBuilder MIB(*Load);MIB.buildLoad(Load->getOperand(0), Load->getOperand(1), NewMMO);Load->eraseFromParent();GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(And->getOperand(1).getReg());// We don't know what the result of the load is, so we don't know any ones.EXPECT_TRUE(Res.One.isZero());// We know that the value is in [0, 2). So, we don't know if the first bit// is 0 or not. However, we do know that every other bit must be 0.APInt Mask(Res.getBitWidth(), 1);Mask.flipAllBits();EXPECT_EQ(Mask.getZExtValue(), Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsExt) {StringRef MIRString = " %c1:_(s16) = G_CONSTANT i16 1\n"" %x:_(s16) = G_IMPLICIT_DEF\n"" %y:_(s16) = G_AND %x, %c1\n"" %anyext:_(s32) = G_ANYEXT %y(s16)\n"" %r1:_(s32) = COPY %anyext\n"" %zext:_(s32) = G_ZEXT %y(s16)\n"" %r2:_(s32) = COPY %zext\n"" %sext:_(s32) = G_SEXT %y(s16)\n"" %r3:_(s32) = COPY %sext\n";setUp(MIRString);if (!TM)return;Register CopyRegAny = Copies[Copies.size() - 3];Register CopyRegZ = Copies[Copies.size() - 2];Register CopyRegS = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);MachineInstr *Copy;Register SrcReg;KnownBits Res;Copy = MRI->getVRegDef(CopyRegAny);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)32, Res.getBitWidth());EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0x0000fffe, Res.Zero.getZExtValue());Copy = MRI->getVRegDef(CopyRegZ);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)32, Res.getBitWidth());EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xfffffffe, Res.Zero.getZExtValue());Copy = MRI->getVRegDef(CopyRegS);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ((uint64_t)32, Res.getBitWidth());EXPECT_EQ((uint64_t)0, Res.One.getZExtValue());EXPECT_EQ((uint64_t)0xfffffffe, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsSextInReg) {StringRef MIRString = R"(; 000...0001%one:_(s32) = G_CONSTANT i32 1; 000...0010%two:_(s32) = G_CONSTANT i32 2; 000...1010%ten:_(s32) = G_CONSTANT i32 10; ???...????%w0:_(s32) = COPY $w0; ???...?1?%or:_(s32) = G_OR %w0, %two; All bits are known.%inreg1:_(s32) = G_SEXT_INREG %one, 1%copy_inreg1:_(s32) = COPY %inreg1; All bits unknown%inreg2:_(s32) = G_SEXT_INREG %or, 1%copy_inreg2:_(s32) = COPY %inreg2; Extending from the only (known) set bit; 111...11?%inreg3:_(s32) = G_SEXT_INREG %or, 2%copy_inreg3:_(s32) = COPY %inreg3; Extending from a known set bit, overwriting all of the high set bits.; 111...1110%inreg4:_(s32) = G_SEXT_INREG %ten, 2%copy_inreg4:_(s32) = COPY %inreg4)";setUp(MIRString);if (!TM)return;GISelKnownBits Info(*MF);KnownBits Res;auto GetKB = [&](unsigned Idx) {Register CopyReg = Copies[Idx];auto *Copy = MRI->getVRegDef(CopyReg);return Info.getKnownBits(Copy->getOperand(1).getReg());};// Every bit is known to be a 1.Res = GetKB(Copies.size() - 4);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isAllOnes());// All bits are unknownRes = GetKB(Copies.size() - 3);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isUnknown());// Extending from the only known set bit// 111...11?Res = GetKB(Copies.size() - 2);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(0u, Res.Zero.getZExtValue());// Extending from a known set bit, overwriting all of the high set bits.// 111...1110Res = GetKB(Copies.size() - 1);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(1u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsAssertSext) {StringRef MIRString = R"(; 000...0001%one:_(s32) = G_CONSTANT i32 1; 000...0010%two:_(s32) = G_CONSTANT i32 2; 000...1010%ten:_(s32) = G_CONSTANT i32 10; ???...????%w0:_(s32) = COPY $w0; ???...?1?%or:_(s32) = G_OR %w0, %two; All bits are known.%assert_sext1:_(s32) = G_ASSERT_SEXT %one, 1%copy_assert_sext1:_(s32) = COPY %assert_sext1; All bits unknown%assert_sext2:_(s32) = G_ASSERT_SEXT %or, 1%copy_assert_sext2:_(s32) = COPY %assert_sext2; Extending from the only (known) set bit; 111...11?%assert_sext3:_(s32) = G_ASSERT_SEXT %or, 2%copy_assert_sext3:_(s32) = COPY %assert_sext3; Extending from a known set bit, overwriting all of the high set bits.; 111...1110%assert_sext4:_(s32) = G_ASSERT_SEXT %ten, 2%copy_assert_sext4:_(s32) = COPY %assert_sext4)";setUp(MIRString);if (!TM)return;GISelKnownBits Info(*MF);KnownBits Res;auto GetKB = [&](unsigned Idx) {Register CopyReg = Copies[Idx];auto *Copy = MRI->getVRegDef(CopyReg);return Info.getKnownBits(Copy->getOperand(1).getReg());};// Every bit is known to be a 1.Res = GetKB(Copies.size() - 4);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isAllOnes());// All bits are unknownRes = GetKB(Copies.size() - 3);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_TRUE(Res.isUnknown());// Extending from the only known set bit// 111...11?Res = GetKB(Copies.size() - 2);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(0u, Res.Zero.getZExtValue());// Extending from a known set bit, overwriting all of the high set bits.// 111...1110Res = GetKB(Copies.size() - 1);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0xFFFFFFFEu, Res.One.getZExtValue());EXPECT_EQ(1u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsMergeValues) {StringRef MIRString = R"(%val0:_(s16) = G_CONSTANT i16 35224%val1:_(s16) = G_CONSTANT i16 17494%val2:_(s16) = G_CONSTANT i16 4659%val3:_(s16) = G_CONSTANT i16 43981%merge:_(s64) = G_MERGE_VALUES %val0, %val1, %val2, %val3%mergecopy:_(s64) = COPY %merge)";setUp(MIRString);if (!TM)return;const uint64_t TestVal = UINT64_C(0xabcd123344568998);Register CopyMerge = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(CopyMerge);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(TestVal, Res.One.getZExtValue());EXPECT_EQ(~TestVal, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsUnmergeValues) {StringRef MIRString = R"(%val:_(s64) = G_CONSTANT i64 12379570962110515608%val0:_(s16), %val1:_(s16), %val2:_(s16), %val3:_(s16) = G_UNMERGE_VALUES %val%part0:_(s16) = COPY %val0%part1:_(s16) = COPY %val1%part2:_(s16) = COPY %val2%part3:_(s16) = COPY %val3)";setUp(MIRString);if (!TM)return;const uint64_t TestVal = UINT64_C(0xabcd123344568998);GISelKnownBits Info(*MF);int Offset = -4;for (unsigned BitOffset = 0; BitOffset != 64; BitOffset += 16, ++Offset) {Register Part = Copies[Copies.size() + Offset];KnownBits PartKnown = Info.getKnownBits(Part);EXPECT_EQ(16u, PartKnown.getBitWidth());uint16_t PartTestVal = static_cast<uint16_t>(TestVal >> BitOffset);EXPECT_EQ(PartTestVal, PartKnown.One.getZExtValue());EXPECT_EQ(static_cast<uint16_t>(~PartTestVal), PartKnown.Zero.getZExtValue());}}TEST_F(AArch64GISelMITest, TestKnownBitsBSwapBitReverse) {StringRef MIRString = R"(%const:_(s32) = G_CONSTANT i32 287454020%bswap:_(s32) = G_BSWAP %const%bitreverse:_(s32) = G_BITREVERSE %const%copy_bswap:_(s32) = COPY %bswap%copy_bitreverse:_(s32) = COPY %bitreverse)";setUp(MIRString);if (!TM)return;const uint32_t ByteSwappedVal = 0x44332211;const uint32_t BitSwappedVal = 0x22cc4488;Register CopyBSwap = Copies[Copies.size() - 2];Register CopyBitReverse = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);KnownBits BSwapKnown = Info.getKnownBits(CopyBSwap);EXPECT_EQ(32u, BSwapKnown.getBitWidth());EXPECT_EQ(ByteSwappedVal, BSwapKnown.One.getZExtValue());EXPECT_EQ(~ByteSwappedVal, BSwapKnown.Zero.getZExtValue());KnownBits BitReverseKnown = Info.getKnownBits(CopyBitReverse);EXPECT_EQ(32u, BitReverseKnown.getBitWidth());EXPECT_EQ(BitSwappedVal, BitReverseKnown.One.getZExtValue());EXPECT_EQ(~BitSwappedVal, BitReverseKnown.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsUMAX) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 10%mask1:_(s8) = G_CONSTANT i8 1%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 3%mask3:_(s8) = G_CONSTANT i8 12%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%umax0:_(s8) = G_UMAX %val0, %val1%copy_umax0:_(s8) = COPY %umax0%mask4:_(s8) = G_CONSTANT i8 14%mask5:_(s8) = G_CONSTANT i8 2%tmp3:_(s8) = G_AND %unknown, %mask4%val3:_(s8) = G_OR %tmp3, %mask5%mask6:_(s8) = G_CONSTANT i8 4%mask7:_(s8) = G_CONSTANT i8 11%tmp4:_(s8) = G_AND %unknown, %mask6%val4:_(s8) = G_OR %tmp4, %mask7%umax1:_(s8) = G_UMAX %val3, %val4%copy_umax1:_(s8) = COPY %umax1)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 2];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);// Compares min/max of LHS and RHS, min uses 0 for unknown bits, max uses 1.// If min(LHS) >= max(RHS) returns KnownBits for LHS, similar for RHS. If this// fails tries to calculate individual bits: common bits for both operands and// a few leading bits in some cases.// 0000?0?1// umax 000011??// = 000011??KnownBits Res0 = Info.getKnownBits(SrcReg0);EXPECT_EQ(0x0Cu, Res0.One.getZExtValue());EXPECT_EQ(0xF0u, Res0.Zero.getZExtValue());Register CopyReg1 = Copies[Copies.size() - 1];MachineInstr *FinalCopy1 = MRI->getVRegDef(CopyReg1);Register SrcReg1 = FinalCopy1->getOperand(1).getReg();KnownBits Res1 = Info.getKnownBits(SrcReg1);// 0000??10// umax 00001?11// = 00001?1?EXPECT_EQ(0x0Au, Res1.One.getZExtValue());EXPECT_EQ(0xF0u, Res1.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsUMax) {StringRef MIRString = R"(%val:_(s32) = COPY $w0%zext:_(s64) = G_ZEXT %val%const:_(s64) = G_CONSTANT i64 -256%umax:_(s64) = G_UMAX %zext, %const%copy_umax:_(s64) = COPY %umax)";setUp(MIRString);if (!TM)return;Register CopyUMax = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);KnownBits KnownUmax = Info.getKnownBits(CopyUMax);EXPECT_EQ(64u, KnownUmax.getBitWidth());EXPECT_EQ(0xffu, KnownUmax.Zero.getZExtValue());EXPECT_EQ(0xffffffffffffff00, KnownUmax.One.getZExtValue());EXPECT_EQ(0xffu, KnownUmax.Zero.getZExtValue());EXPECT_EQ(0xffffffffffffff00, KnownUmax.One.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsUMIN) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 10%mask1:_(s8) = G_CONSTANT i8 1%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 3%mask3:_(s8) = G_CONSTANT i8 12%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%umin:_(s8) = G_UMIN %val0, %val1%copy_umin:_(s8) = COPY %umin)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 1];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);// Flips the range of operands: [0, 0xFFFFFFFF] <-> [0xFFFFFFFF, 0],// uses umax and flips result back.// 0000?0?1// umin 000011??// = 0000?0?1EXPECT_EQ(0x01u, Res0.One.getZExtValue());EXPECT_EQ(0xF4u, Res0.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsSMAX) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 128%mask1:_(s8) = G_CONSTANT i8 64%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 1%mask3:_(s8) = G_CONSTANT i8 128%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%smax:_(s8) = G_SMAX %val0, %val1%copy_smax:_(s8) = COPY %smax)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 1];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);// Flips the range of operands: [-0x80000000, 0x7FFFFFFF] <-> [0, 0xFFFFFFFF],// uses umax and flips result back.// RHS is negative, LHS is either positive or negative with smaller abs value.// ?1000000// smax 1000000?// = ?1000000EXPECT_EQ(0x40u, Res0.One.getZExtValue());EXPECT_EQ(0x3Fu, Res0.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsSMIN) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%unknown:_(s8) = G_LOAD %ptr(p0) :: (load (s8))%mask0:_(s8) = G_CONSTANT i8 128%mask1:_(s8) = G_CONSTANT i8 64%tmp0:_(s8) = G_AND %unknown, %mask0%val0:_(s8) = G_OR %tmp0, %mask1%mask2:_(s8) = G_CONSTANT i8 1%mask3:_(s8) = G_CONSTANT i8 128%tmp1:_(s8) = G_AND %unknown, %mask2%val1:_(s8) = G_OR %tmp1, %mask3%smin:_(s8) = G_SMIN %val0, %val1%copy_smin:_(s8) = COPY %smin)";setUp(MIRString);if (!TM)return;Register CopyReg0 = Copies[Copies.size() - 1];MachineInstr *FinalCopy0 = MRI->getVRegDef(CopyReg0);Register SrcReg0 = FinalCopy0->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res0 = Info.getKnownBits(SrcReg0);// Flips the range of operands: [-0x80000000, 0x7FFFFFFF] <-> [0xFFFFFFFF, 0],// uses umax and flips result back.// RHS is negative, LHS is either positive or negative with smaller abs value.// ?1000000// smin 1000000?// = 1000000?EXPECT_EQ(0x80u, Res0.One.getZExtValue());EXPECT_EQ(0x7Eu, Res0.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestInvalidQueries) {StringRef MIRString = R"(%src:_(s32) = COPY $w0%thirty2:_(s32) = G_CONSTANT i32 32%equalSized:_(s32) = G_SHL %src, %thirty2%copy1:_(s32) = COPY %equalSized%thirty3:_(s32) = G_CONSTANT i32 33%biggerSized:_(s32) = G_SHL %src, %thirty3%copy2:_(s32) = COPY %biggerSized)";setUp(MIRString);if (!TM)return;Register EqSizedCopyReg = Copies[Copies.size() - 2];MachineInstr *EqSizedCopy = MRI->getVRegDef(EqSizedCopyReg);Register EqSizedShl = EqSizedCopy->getOperand(1).getReg();Register BiggerSizedCopyReg = Copies[Copies.size() - 1];MachineInstr *BiggerSizedCopy = MRI->getVRegDef(BiggerSizedCopyReg);Register BiggerSizedShl = BiggerSizedCopy->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits EqSizeRes = Info.getKnownBits(EqSizedShl);KnownBits BiggerSizeRes = Info.getKnownBits(BiggerSizedShl);// We don't know what the result of the shift is, but we should not crashEXPECT_TRUE(EqSizeRes.One.isZero());EXPECT_TRUE(EqSizeRes.Zero.isZero());EXPECT_TRUE(BiggerSizeRes.One.isZero());EXPECT_TRUE(BiggerSizeRes.Zero.isZero());}TEST_F(AArch64GISelMITest, TestKnownBitsAssertZext) {StringRef MIRString = R"(%copy:_(s64) = COPY $x0%assert8:_(s64) = G_ASSERT_ZEXT %copy, 8%copy_assert8:_(s64) = COPY %assert8%assert1:_(s64) = G_ASSERT_ZEXT %copy, 1%copy_assert1:_(s64) = COPY %assert1%assert63:_(s64) = G_ASSERT_ZEXT %copy, 63%copy_assert63:_(s64) = COPY %assert63%assert3:_(s64) = G_ASSERT_ZEXT %copy, 3%copy_assert3:_(s64) = COPY %assert3)";setUp(MIRString);if (!TM)return;Register CopyAssert8 = Copies[Copies.size() - 4];Register CopyAssert1 = Copies[Copies.size() - 3];Register CopyAssert63 = Copies[Copies.size() - 2];Register CopyAssert3 = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);MachineInstr *Copy;Register SrcReg;KnownBits Res;// Assert zero-extension from an 8-bit value.Copy = MRI->getVRegDef(CopyAssert8);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFFFFFFFF00u, Res.Zero.getZExtValue());// Assert zero-extension from a 1-bit value.Copy = MRI->getVRegDef(CopyAssert1);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFFFFFFFFFE, Res.Zero.getZExtValue());// Assert zero-extension from a 63-bit value.Copy = MRI->getVRegDef(CopyAssert63);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0x8000000000000000u, Res.Zero.getZExtValue());// Assert zero-extension from a 3-bit value.Copy = MRI->getVRegDef(CopyAssert3);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFFFFFFFFF8u, Res.Zero.getZExtValue());}TEST_F(AArch64GISelMITest, TestKnownBitsCTPOP) {StringRef MIRString = R"(%src:_(s32) = COPY $w0%unknown:_(s32) = G_CTPOP %src%unknown_copy:_(s32) = COPY %unknown%constant_4294967295:_(s32) = G_CONSTANT i32 4294967295%thirtytwo:_(s32) = G_CTPOP %constant_4294967295%thirtytwo_copy:_(s32) = COPY %thirtytwo%constant_15:_(s32) = G_CONSTANT i32 15%four:_(s32) = G_CTPOP %constant_15%four_copy:_(s32) = COPY %four%constant_1:_(s32) = G_CONSTANT i32 1%one:_(s32) = G_CTPOP %constant_1%one_copy:_(s32) = COPY %one)";setUp(MIRString);if (!TM)return;Register UnknownCopy = Copies[Copies.size() - 4];Register ThirtytwoCopy = Copies[Copies.size() - 3];Register FourCopy = Copies[Copies.size() - 2];Register OneCopy = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);MachineInstr *Copy;Register SrcReg;KnownBits Res;Copy = MRI->getVRegDef(UnknownCopy);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFC0u, Res.Zero.getZExtValue());Copy = MRI->getVRegDef(ThirtytwoCopy);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFC0u, Res.Zero.getZExtValue());Copy = MRI->getVRegDef(FourCopy);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFF8u, Res.Zero.getZExtValue());Copy = MRI->getVRegDef(OneCopy);SrcReg = Copy->getOperand(1).getReg();Res = Info.getKnownBits(SrcReg);EXPECT_EQ(32u, Res.getBitWidth());EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(0xFFFFFFFEu, Res.Zero.getZExtValue());}TEST_F(AMDGPUGISelMITest, TestKnownBitsUBFX) {StringRef MIRString = " %3:_(s32) = G_IMPLICIT_DEF\n"" %4:_(s32) = G_CONSTANT i32 12\n"" %5:_(s32) = G_CONSTANT i32 8\n"" %6:_(s32) = G_UBFX %3, %4(s32), %5\n"" %ubfx_copy:_(s32) = COPY %6\n"" %7:_(s32) = G_CONSTANT i32 28672\n"" %8:_(s32) = G_UBFX %7, %4(s32), %5\n"" %ubfx_copy_val:_(s32) = COPY %8\n"" %9:_(s32) = G_IMPLICIT_DEF\n"" %10:_(s32) = G_IMPLICIT_DEF\n"" %11:_(s32) = G_UBFX %3, %9(s32), %10\n"" %ubfx_copy_unk:_(s32) = COPY %11\n"" %12:_(s32) = G_UBFX %3, %9(s32), %5\n"" %ubfx_copy_unk_off:_(s32) = COPY %12\n"" %13:_(s32) = G_UBFX %3, %4(s32), %10\n"" %ubfx_copy_unk_width:_(s32) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyBfxReg = Copies[Copies.size() - 5];Register CopyValBfxReg = Copies[Copies.size() - 4];Register CopyUnkBfxReg = Copies[Copies.size() - 3];Register CopyUnkOffBfxReg = Copies[Copies.size() - 2];Register CopyUnkWidthBfxReg = Copies[Copies.size() - 1];MachineInstr *CopyBfx = MRI->getVRegDef(CopyBfxReg);Register SrcReg = CopyBfx->getOperand(1).getReg();MachineInstr *CopyValBfx = MRI->getVRegDef(CopyValBfxReg);Register ValSrcReg = CopyValBfx->getOperand(1).getReg();MachineInstr *CopyUnkBfx = MRI->getVRegDef(CopyUnkBfxReg);Register UnkSrcReg = CopyUnkBfx->getOperand(1).getReg();MachineInstr *CopyUnkOffBfx = MRI->getVRegDef(CopyUnkOffBfxReg);Register UnkOffSrcReg = CopyUnkOffBfx->getOperand(1).getReg();MachineInstr *CopyUnkWidthBfx = MRI->getVRegDef(CopyUnkWidthBfxReg);Register UnkWidthSrcReg = CopyUnkWidthBfx->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res1 = Info.getKnownBits(SrcReg);EXPECT_EQ(0u, Res1.One.getZExtValue());EXPECT_EQ(0xffffff00u, Res1.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(ValSrcReg);EXPECT_EQ(7u, Res2.One.getZExtValue());EXPECT_EQ(0xfffffff8u, Res2.Zero.getZExtValue());KnownBits Res3 = Info.getKnownBits(UnkSrcReg);EXPECT_EQ(0u, Res3.One.getZExtValue());EXPECT_EQ(0u, Res3.Zero.getZExtValue());KnownBits Res4 = Info.getKnownBits(UnkOffSrcReg);EXPECT_EQ(0u, Res4.One.getZExtValue());EXPECT_EQ(0xffffff00u, Res4.Zero.getZExtValue());KnownBits Res5 = Info.getKnownBits(UnkWidthSrcReg);EXPECT_EQ(0u, Res5.One.getZExtValue());EXPECT_EQ(0xfff00000u, Res5.Zero.getZExtValue());}TEST_F(AMDGPUGISelMITest, TestKnownBitsSBFX) {StringRef MIRString = " %3:_(s32) = G_IMPLICIT_DEF\n"" %4:_(s32) = G_CONSTANT i32 8\n"" %5:_(s32) = G_CONSTANT i32 4\n"" %6:_(s32) = G_SBFX %3, %4(s32), %5\n"" %sbfx_copy:_(s32) = COPY %6\n"" %7:_(s32) = G_CONSTANT i32 2047\n"" %8:_(s32) = G_SBFX %7, %4(s32), %5\n"" %sbfx_copy_val:_(s32) = COPY %8\n"" %9:_(s32) = G_CONSTANT i32 2048\n"" %10:_(s32) = G_SBFX %9, %4(s32), %5\n"" %sbfx_copy_neg_val:_(s32) = COPY %10\n"" %11:_(s32) = G_IMPLICIT_DEF\n"" %12:_(s32) = G_SBFX %7, %11(s32), %5\n"" %sbfx_copy_unk_off:_(s32) = COPY %12\n"" %13:_(s32) = G_SBFX %9, %4(s32), %11\n"" %sbfx_copy_unk_width:_(s32) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyBfxReg = Copies[Copies.size() - 5];Register CopyValBfxReg = Copies[Copies.size() - 4];Register CopyNegValBfxReg = Copies[Copies.size() - 3];Register CopyUnkOffBfxReg = Copies[Copies.size() - 2];Register CopyUnkWidthBfxReg = Copies[Copies.size() - 1];MachineInstr *CopyBfx = MRI->getVRegDef(CopyBfxReg);Register SrcReg = CopyBfx->getOperand(1).getReg();MachineInstr *CopyValBfx = MRI->getVRegDef(CopyValBfxReg);Register ValSrcReg = CopyValBfx->getOperand(1).getReg();MachineInstr *CopyNegValBfx = MRI->getVRegDef(CopyNegValBfxReg);Register NegValSrcReg = CopyNegValBfx->getOperand(1).getReg();MachineInstr *CopyUnkOffBfx = MRI->getVRegDef(CopyUnkOffBfxReg);Register UnkOffSrcReg = CopyUnkOffBfx->getOperand(1).getReg();MachineInstr *CopyUnkWidthBfx = MRI->getVRegDef(CopyUnkWidthBfxReg);Register UnkWidthSrcReg = CopyUnkWidthBfx->getOperand(1).getReg();GISelKnownBits Info(*MF);KnownBits Res1 = Info.getKnownBits(SrcReg);EXPECT_EQ(0u, Res1.One.getZExtValue());EXPECT_EQ(0u, Res1.Zero.getZExtValue());KnownBits Res2 = Info.getKnownBits(ValSrcReg);EXPECT_EQ(7u, Res2.One.getZExtValue());EXPECT_EQ(0xfffffff8u, Res2.Zero.getZExtValue());KnownBits Res3 = Info.getKnownBits(NegValSrcReg);EXPECT_EQ(0xfffffff8u, Res3.One.getZExtValue());EXPECT_EQ(7u, Res3.Zero.getZExtValue());KnownBits Res4 = Info.getKnownBits(UnkOffSrcReg);EXPECT_EQ(0u, Res4.One.getZExtValue());EXPECT_EQ(0u, Res4.Zero.getZExtValue());KnownBits Res5 = Info.getKnownBits(UnkWidthSrcReg);EXPECT_EQ(0u, Res5.One.getZExtValue());EXPECT_EQ(0u, Res5.Zero.getZExtValue());}TEST_F(AMDGPUGISelMITest, TestNumSignBitsUBFX) {StringRef MIRString = " %3:_(s32) = G_IMPLICIT_DEF\n"" %4:_(s32) = G_CONSTANT i32 12\n"" %5:_(s32) = G_CONSTANT i32 8\n"" %6:_(s32) = G_UBFX %3, %4(s32), %5\n"" %ubfx_copy_unk:_(s32) = COPY %6\n"" %7:_(s32) = G_CONSTANT i32 28672\n"" %8:_(s32) = G_UBFX %7, %4(s32), %5\n"" %ubfx_copy_pos:_(s32) = COPY %8\n"" %9:_(s32) = G_CONSTANT i32 -1\n"" %10:_(s32) = G_UBFX %9, %4(s32), %5\n"" %ubfx_copy_neg:_(s32) = COPY %10\n"" %11:_(s32) = G_IMPLICIT_DEF\n"" %12:_(s32) = G_UBFX %7, %11(s32), %5\n"" %ubfx_copy_unk_off:_(s32) = COPY %12\n"" %13:_(s32) = G_UBFX %7, %4(s32), %11\n"" %ubfx_copy_unk_width:_(s32) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyUnkBfxReg = Copies[Copies.size() - 5];Register CopyPosBfxReg = Copies[Copies.size() - 4];Register CopyNegBfxReg = Copies[Copies.size() - 3];Register CopyUnkOffBfxReg = Copies[Copies.size() - 2];Register CopyUnkWidthBfxReg = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(24u, Info.computeNumSignBits(CopyUnkBfxReg));EXPECT_EQ(29u, Info.computeNumSignBits(CopyPosBfxReg));EXPECT_EQ(24u, Info.computeNumSignBits(CopyNegBfxReg));EXPECT_EQ(24u, Info.computeNumSignBits(CopyUnkOffBfxReg));EXPECT_EQ(29u, Info.computeNumSignBits(CopyUnkWidthBfxReg));}TEST_F(AMDGPUGISelMITest, TestNumSignBitsSBFX) {StringRef MIRString = " %3:_(s32) = G_CONSTANT i32 -1\n"" %4:_(s32) = G_CONSTANT i32 8\n"" %5:_(s32) = G_CONSTANT i32 4\n"" %6:_(s32) = G_SBFX %3, %4(s32), %5\n"" %sbfx_copy_neg:_(s32) = COPY %6\n"" %7:_(s32) = G_CONSTANT i32 2047\n"" %8:_(s32) = G_SBFX %7, %4(s32), %5\n"" %sbfx_copy_pos:_(s32) = COPY %8\n"" %9:_(s32) = G_CONSTANT i32 2048\n"" %10:_(s32) = G_SBFX %9, %4(s32), %5\n"" %sbfx_copy_hiset:_(s32) = COPY %10\n"" %11:_(s32) = G_IMPLICIT_DEF\n"" %12:_(s32) = G_SBFX %11, %4(s32), %5\n"" %sbfx_copy_unk:_(s32) = COPY %12\n"" %13:_(s32) = G_SBFX %3, %11(s32), %5\n"" %sbfx_copy_unk_off:_(s32) = COPY %13\n";setUp(MIRString);if (!TM)return;Register CopyNegBfxReg = Copies[Copies.size() - 5];Register CopyPosBfxReg = Copies[Copies.size() - 4];Register CopyHiSetBfxReg = Copies[Copies.size() - 3];Register CopyUnkValBfxReg = Copies[Copies.size() - 2];Register CopyUnkOffBfxReg = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);EXPECT_EQ(32u, Info.computeNumSignBits(CopyNegBfxReg));EXPECT_EQ(29u, Info.computeNumSignBits(CopyPosBfxReg));EXPECT_EQ(29u, Info.computeNumSignBits(CopyHiSetBfxReg));EXPECT_EQ(1u, Info.computeNumSignBits(CopyUnkValBfxReg));EXPECT_EQ(1u, Info.computeNumSignBits(CopyUnkOffBfxReg));}TEST_F(AMDGPUGISelMITest, TestKnownBitsAssertAlign) {StringRef MIRString = R"MIR(%val:_(s64) = COPY $vgpr0_vgpr1%ptrval:_(p1) = COPY $vgpr0_vgpr1%assert_align0:_(s64) = G_ASSERT_ALIGN %val, 0%copy_assert_align0:_(s64) = COPY %assert_align0%assert_align1:_(s64) = G_ASSERT_ALIGN %val, 1%copy_assert_align1:_(s64) = COPY %assert_align1%assert_align2:_(s64) = G_ASSERT_ALIGN %val, 2%copy_assert_align2:_(s64) = COPY %assert_align2%assert_align3:_(s64) = G_ASSERT_ALIGN %val, 3%copy_assert_align3:_(s64) = COPY %assert_align3%assert_align8:_(s64) = G_ASSERT_ALIGN %val, 8%copy_assert_align8:_(s64) = COPY %assert_align8%assert_maxalign:_(s64) = G_ASSERT_ALIGN %val, 30%copy_assert_maxalign:_(s64) = COPY %assert_maxalign%assert_ptr_align5:_(p1) = G_ASSERT_ALIGN %ptrval, 5%copy_assert_ptr_align5:_(p1) = COPY %assert_ptr_align5)MIR";setUp(MIRString);if (!TM)return;GISelKnownBits Info(*MF);KnownBits Res;auto GetKB = [&](unsigned Idx) {Register CopyReg = Copies[Idx];auto *Copy = MRI->getVRegDef(CopyReg);return Info.getKnownBits(Copy->getOperand(1).getReg());};auto CheckBits = [&](unsigned NumBits, unsigned Idx) {Res = GetKB(Idx);EXPECT_EQ(64u, Res.getBitWidth());EXPECT_EQ(NumBits, Res.Zero.countTrailingOnes());EXPECT_EQ(64u, Res.One.countTrailingZeros());EXPECT_EQ(Align(1ull << NumBits), Info.computeKnownAlignment(Copies[Idx]));};CheckBits(0, Copies.size() - 7);CheckBits(1, Copies.size() - 6);CheckBits(2, Copies.size() - 5);CheckBits(3, Copies.size() - 4);CheckBits(8, Copies.size() - 3);CheckBits(30, Copies.size() - 2);CheckBits(5, Copies.size() - 1);}TEST_F(AArch64GISelMITest, TestKnownBitsUADDO) {StringRef MIRString = R"(%ptr:_(p0) = G_IMPLICIT_DEF%ld0:_(s32) = G_LOAD %ptr(p0) :: (load (s16))%ld1:_(s32) = G_LOAD %ptr(p0) :: (load (s16))%add:_(s32), %overflow:_(s32) = G_UADDO %ld0, %ld1%copy_overflow:_(s32) = COPY %overflow)";setUp(MIRString);if (!TM)return;Register CopyOverflow = Copies[Copies.size() - 1];GISelKnownBits Info(*MF);KnownBits Res = Info.getKnownBits(CopyOverflow);EXPECT_EQ(0u, Res.One.getZExtValue());EXPECT_EQ(31u, Res.Zero.countLeadingOnes());}
//===- GISelUtilsTest.cpp -------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/GlobalISel/Utils.h"#include "gtest/gtest.h"using namespace llvm;namespace {static const LLT S1 = LLT::scalar(1);static const LLT S8 = LLT::scalar(8);static const LLT S16 = LLT::scalar(16);static const LLT S32 = LLT::scalar(32);static const LLT S64 = LLT::scalar(64);static const LLT P0 = LLT::pointer(0, 64);static const LLT P1 = LLT::pointer(1, 32);static const LLT V2S8 = LLT::fixed_vector(2, 8);static const LLT V4S8 = LLT::fixed_vector(4, 8);static const LLT V8S8 = LLT::fixed_vector(8, 8);static const LLT V2S16 = LLT::fixed_vector(2, 16);static const LLT V3S16 = LLT::fixed_vector(3, 16);static const LLT V4S16 = LLT::fixed_vector(4, 16);static const LLT V2S32 = LLT::fixed_vector(2, 32);static const LLT V3S32 = LLT::fixed_vector(3, 32);static const LLT V4S32 = LLT::fixed_vector(4, 32);static const LLT V6S32 = LLT::fixed_vector(6, 32);static const LLT V2S64 = LLT::fixed_vector(2, 64);static const LLT V3S64 = LLT::fixed_vector(3, 64);static const LLT V4S64 = LLT::fixed_vector(4, 64);static const LLT V2P0 = LLT::fixed_vector(2, P0);static const LLT V3P0 = LLT::fixed_vector(3, P0);static const LLT V4P0 = LLT::fixed_vector(4, P0);static const LLT V6P0 = LLT::fixed_vector(6, P0);static const LLT V2P1 = LLT::fixed_vector(2, P1);static const LLT V4P1 = LLT::fixed_vector(4, P1);TEST(GISelUtilsTest, getGCDType) {EXPECT_EQ(S1, getGCDType(S1, S1));EXPECT_EQ(S32, getGCDType(S32, S32));EXPECT_EQ(S1, getGCDType(S1, S32));EXPECT_EQ(S1, getGCDType(S32, S1));EXPECT_EQ(S16, getGCDType(S16, S32));EXPECT_EQ(S16, getGCDType(S32, S16));EXPECT_EQ(V2S32, getGCDType(V2S32, V2S32));EXPECT_EQ(S32, getGCDType(V3S32, V2S32));EXPECT_EQ(S32, getGCDType(V2S32, V3S32));EXPECT_EQ(V2S16, getGCDType(V4S16, V2S16));EXPECT_EQ(V2S16, getGCDType(V2S16, V4S16));EXPECT_EQ(V2S32, getGCDType(V4S32, V2S32));EXPECT_EQ(V2S32, getGCDType(V2S32, V4S32));EXPECT_EQ(S16, getGCDType(P0, S16));EXPECT_EQ(S16, getGCDType(S16, P0));EXPECT_EQ(S32, getGCDType(P0, S32));EXPECT_EQ(S32, getGCDType(S32, P0));EXPECT_EQ(P0, getGCDType(P0, S64));EXPECT_EQ(S64, getGCDType(S64, P0));EXPECT_EQ(S32, getGCDType(P0, P1));EXPECT_EQ(S32, getGCDType(P1, P0));EXPECT_EQ(P0, getGCDType(V3P0, V2P0));EXPECT_EQ(P0, getGCDType(V2P0, V3P0));EXPECT_EQ(P0, getGCDType(P0, V2P0));EXPECT_EQ(P0, getGCDType(V2P0, P0));EXPECT_EQ(V2P0, getGCDType(V2P0, V2P0));EXPECT_EQ(P0, getGCDType(V3P0, V2P0));EXPECT_EQ(P0, getGCDType(V2P0, V3P0));EXPECT_EQ(V2P0, getGCDType(V4P0, V2P0));EXPECT_EQ(V2P0, getGCDType(V2P0, V4P1));EXPECT_EQ(V4P1, getGCDType(V4P1, V2P0));EXPECT_EQ(V2P0, getGCDType(V4P0, V4P1));EXPECT_EQ(V4P1, getGCDType(V4P1, V4P0));// Elements have same size, but have different pointeriness, so prefer the// original element type.EXPECT_EQ(V2P0, getGCDType(V2P0, V4S64));EXPECT_EQ(V2S64, getGCDType(V4S64, V2P0));EXPECT_EQ(V2S16, getGCDType(V2S16, V4P1));EXPECT_EQ(P1, getGCDType(V4P1, V2S16));EXPECT_EQ(V2P1, getGCDType(V4P1, V4S16));EXPECT_EQ(V4S16, getGCDType(V4S16, V2P1));EXPECT_EQ(P0, getGCDType(P0, V2S64));EXPECT_EQ(S64, getGCDType(V2S64, P0));EXPECT_EQ(S16, getGCDType(V2S16, V3S16));EXPECT_EQ(S16, getGCDType(V3S16, V2S16));EXPECT_EQ(S16, getGCDType(V3S16, S16));EXPECT_EQ(S16, getGCDType(S16, V3S16));EXPECT_EQ(V2S16, getGCDType(V2S16, V2S32));EXPECT_EQ(S32, getGCDType(V2S32, V2S16));EXPECT_EQ(V4S8, getGCDType(V4S8, V2S32));EXPECT_EQ(S32, getGCDType(V2S32, V4S8));// Test cases where neither element type nicely divides.EXPECT_EQ(LLT::scalar(3),getGCDType(LLT::fixed_vector(3, 5), LLT::fixed_vector(2, 6)));EXPECT_EQ(LLT::scalar(3),getGCDType(LLT::fixed_vector(2, 6), LLT::fixed_vector(3, 5)));// Have to go smaller than a pointer element.EXPECT_EQ(LLT::scalar(3), getGCDType(LLT::fixed_vector(2, LLT::pointer(3, 6)),LLT::fixed_vector(3, 5)));EXPECT_EQ(LLT::scalar(3),getGCDType(LLT::fixed_vector(3, 5),LLT::fixed_vector(2, LLT::pointer(3, 6))));EXPECT_EQ(V4S8, getGCDType(V4S8, S32));EXPECT_EQ(S32, getGCDType(S32, V4S8));EXPECT_EQ(V4S8, getGCDType(V4S8, P1));EXPECT_EQ(P1, getGCDType(P1, V4S8));EXPECT_EQ(V2S8, getGCDType(V2S8, V4S16));EXPECT_EQ(S16, getGCDType(V4S16, V2S8));EXPECT_EQ(S8, getGCDType(V2S8, LLT::fixed_vector(4, 2)));EXPECT_EQ(LLT::fixed_vector(4, 2), getGCDType(LLT::fixed_vector(4, 2), S8));EXPECT_EQ(LLT::pointer(4, 8),getGCDType(LLT::fixed_vector(2, LLT::pointer(4, 8)),LLT::fixed_vector(4, 2)));EXPECT_EQ(LLT::fixed_vector(4, 2),getGCDType(LLT::fixed_vector(4, 2),LLT::fixed_vector(2, LLT::pointer(4, 8))));EXPECT_EQ(LLT::scalar(4), getGCDType(LLT::fixed_vector(3, 4), S8));EXPECT_EQ(LLT::scalar(4), getGCDType(S8, LLT::fixed_vector(3, 4)));}TEST(GISelUtilsTest, getLCMType) {EXPECT_EQ(S1, getLCMType(S1, S1));EXPECT_EQ(S32, getLCMType(S32, S1));EXPECT_EQ(S32, getLCMType(S1, S32));EXPECT_EQ(S32, getLCMType(S32, S32));EXPECT_EQ(S32, getLCMType(S32, S16));EXPECT_EQ(S32, getLCMType(S16, S32));EXPECT_EQ(S64, getLCMType(S64, P0));EXPECT_EQ(P0, getLCMType(P0, S64));EXPECT_EQ(P0, getLCMType(S32, P0));EXPECT_EQ(P0, getLCMType(P0, S32));EXPECT_EQ(S32, getLCMType(S32, P1));EXPECT_EQ(P1, getLCMType(P1, S32));EXPECT_EQ(P0, getLCMType(P0, P0));EXPECT_EQ(P1, getLCMType(P1, P1));EXPECT_EQ(P0, getLCMType(P0, P1));EXPECT_EQ(P0, getLCMType(P1, P0));EXPECT_EQ(V2S32, getLCMType(V2S32, V2S32));EXPECT_EQ(V2S32, getLCMType(V2S32, S32));EXPECT_EQ(V2S32, getLCMType(S32, V2S32));EXPECT_EQ(V2S32, getLCMType(V2S32, V2S32));EXPECT_EQ(V6S32, getLCMType(V2S32, V3S32));EXPECT_EQ(V6S32, getLCMType(V3S32, V2S32));EXPECT_EQ(LLT::fixed_vector(12, S32), getLCMType(V4S32, V3S32));EXPECT_EQ(LLT::fixed_vector(12, S32), getLCMType(V3S32, V4S32));EXPECT_EQ(V2P0, getLCMType(V2P0, V2P0));EXPECT_EQ(V2P0, getLCMType(V2P0, P0));EXPECT_EQ(V2P0, getLCMType(P0, V2P0));EXPECT_EQ(V2P0, getLCMType(V2P0, V2P0));EXPECT_EQ(V6P0, getLCMType(V2P0, V3P0));EXPECT_EQ(V6P0, getLCMType(V3P0, V2P0));EXPECT_EQ(LLT::fixed_vector(12, P0), getLCMType(V4P0, V3P0));EXPECT_EQ(LLT::fixed_vector(12, P0), getLCMType(V3P0, V4P0));EXPECT_EQ(LLT::fixed_vector(12, S64), getLCMType(V4S64, V3P0));EXPECT_EQ(LLT::fixed_vector(12, P0), getLCMType(V3P0, V4S64));EXPECT_EQ(LLT::fixed_vector(12, P0), getLCMType(V4P0, V3S64));EXPECT_EQ(LLT::fixed_vector(12, S64), getLCMType(V3S64, V4P0));EXPECT_EQ(V2P0, getLCMType(V2P0, S32));EXPECT_EQ(V4S32, getLCMType(S32, V2P0));EXPECT_EQ(V2P0, getLCMType(V2P0, S64));EXPECT_EQ(V2S64, getLCMType(S64, V2P0));EXPECT_EQ(V2P0, getLCMType(V2P0, V2P1));EXPECT_EQ(V4P1, getLCMType(V2P1, V2P0));EXPECT_EQ(V2P0, getLCMType(V2P0, V4P1));EXPECT_EQ(V4P1, getLCMType(V4P1, V2P0));EXPECT_EQ(V2S32, getLCMType(V2S32, S64));EXPECT_EQ(S64, getLCMType(S64, V2S32));EXPECT_EQ(V4S16, getLCMType(V4S16, V2S32));EXPECT_EQ(V2S32, getLCMType(V2S32, V4S16));EXPECT_EQ(V2S32, getLCMType(V2S32, V4S8));EXPECT_EQ(V8S8, getLCMType(V4S8, V2S32));EXPECT_EQ(V2S16, getLCMType(V2S16, V4S8));EXPECT_EQ(V4S8, getLCMType(V4S8, V2S16));EXPECT_EQ(LLT::fixed_vector(6, S16), getLCMType(V3S16, V4S8));EXPECT_EQ(LLT::fixed_vector(12, S8), getLCMType(V4S8, V3S16));EXPECT_EQ(V4S16, getLCMType(V4S16, V4S8));EXPECT_EQ(V8S8, getLCMType(V4S8, V4S16));EXPECT_EQ(LLT::fixed_vector(6, 4), getLCMType(LLT::fixed_vector(3, 4), S8));EXPECT_EQ(LLT::fixed_vector(3, 8), getLCMType(S8, LLT::fixed_vector(3, 4)));EXPECT_EQ(LLT::fixed_vector(6, 4),getLCMType(LLT::fixed_vector(3, 4), LLT::pointer(4, 8)));EXPECT_EQ(LLT::fixed_vector(3, LLT::pointer(4, 8)),getLCMType(LLT::pointer(4, 8), LLT::fixed_vector(3, 4)));EXPECT_EQ(V2S64, getLCMType(V2S64, P0));EXPECT_EQ(V2P0, getLCMType(P0, V2S64));EXPECT_EQ(V2S64, getLCMType(V2S64, P1));EXPECT_EQ(V4P1, getLCMType(P1, V2S64));}}
//===- GISelMITest.h --------------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTEST_CODEGEN_GLOBALISEL_GISELMI_H#define LLVM_UNITTEST_CODEGEN_GLOBALISEL_GISELMI_H#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"#include "llvm/CodeGen/GlobalISel/Utils.h"#include "llvm/CodeGen/MIRParser/MIRParser.h"#include "llvm/CodeGen/MachineFunction.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/TargetFrameLowering.h"#include "llvm/CodeGen/TargetInstrInfo.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/FileCheck/FileCheck.h"#include "llvm/InitializePasses.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Target/TargetOptions.h"#include "gtest/gtest.h"using namespace llvm;using namespace MIPatternMatch;static inline void initLLVM() {InitializeAllTargets();InitializeAllTargetMCs();InitializeAllAsmPrinters();InitializeAllAsmParsers();PassRegistry *Registry = PassRegistry::getPassRegistry();initializeCore(*Registry);initializeCodeGen(*Registry);}// Define a printers to help debugging when things go wrong.namespace llvm {std::ostream &operator<<(std::ostream &OS, const LLT Ty);std::ostream &operator<<(std::ostream &OS, const MachineFunction &MF);}static std::unique_ptr<Module> parseMIR(LLVMContext &Context,std::unique_ptr<MIRParser> &MIR,const TargetMachine &TM,StringRef MIRCode, const char *FuncName,MachineModuleInfo &MMI) {SMDiagnostic Diagnostic;std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);MIR = createMIRParser(std::move(MBuffer), Context);if (!MIR)return nullptr;std::unique_ptr<Module> M = MIR->parseIRModule();if (!M)return nullptr;M->setDataLayout(TM.createDataLayout());if (MIR->parseMachineFunctions(*M, MMI))return nullptr;return M;}static std::pair<std::unique_ptr<Module>, std::unique_ptr<MachineModuleInfo>>createDummyModule(LLVMContext &Context, const LLVMTargetMachine &TM,StringRef MIRString, const char *FuncName) {std::unique_ptr<MIRParser> MIR;auto MMI = std::make_unique<MachineModuleInfo>(&TM);std::unique_ptr<Module> M =parseMIR(Context, MIR, TM, MIRString, FuncName, *MMI);return make_pair(std::move(M), std::move(MMI));}static MachineFunction *getMFFromMMI(const Module *M,const MachineModuleInfo *MMI) {Function *F = M->getFunction("func");auto *MF = MMI->getMachineFunction(*F);return MF;}static void collectCopies(SmallVectorImpl<Register> &Copies,MachineFunction *MF) {for (auto &MBB : *MF)for (MachineInstr &MI : MBB) {if (MI.getOpcode() == TargetOpcode::COPY)Copies.push_back(MI.getOperand(0).getReg());}}class GISelMITest : public ::testing::Test {protected:GISelMITest() : ::testing::Test() {}/// Prepare a target specific LLVMTargetMachine.virtual std::unique_ptr<LLVMTargetMachine> createTargetMachine() const = 0;/// Get the stub sample MIR test function.virtual void getTargetTestModuleString(SmallString<512> &S,StringRef MIRFunc) const = 0;void setUp(StringRef ExtraAssembly = "") {TM = createTargetMachine();if (!TM)return;SmallString<512> MIRString;getTargetTestModuleString(MIRString, ExtraAssembly);ModuleMMIPair = createDummyModule(Context, *TM, MIRString, "func");MF = getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get());collectCopies(Copies, MF);EntryMBB = &*MF->begin();B.setMF(*MF);MRI = &MF->getRegInfo();B.setInsertPt(*EntryMBB, EntryMBB->end());}LLVMContext Context;std::unique_ptr<LLVMTargetMachine> TM;MachineFunction *MF;std::pair<std::unique_ptr<Module>, std::unique_ptr<MachineModuleInfo>>ModuleMMIPair;SmallVector<Register, 4> Copies;MachineBasicBlock *EntryMBB;MachineIRBuilder B;MachineRegisterInfo *MRI;};class AArch64GISelMITest : public GISelMITest {std::unique_ptr<LLVMTargetMachine> createTargetMachine() const override;void getTargetTestModuleString(SmallString<512> &S,StringRef MIRFunc) const override;};class AMDGPUGISelMITest : public GISelMITest {std::unique_ptr<LLVMTargetMachine> createTargetMachine() const override;void getTargetTestModuleString(SmallString<512> &S,StringRef MIRFunc) const override;};#define DefineLegalizerInfo(Name, SettingUpActionsBlock) \class Name##Info : public LegalizerInfo { \public: \Name##Info(const TargetSubtargetInfo &ST) { \using namespace TargetOpcode; \const LLT s8 = LLT::scalar(8); \(void)s8; \const LLT s16 = LLT::scalar(16); \(void)s16; \const LLT s32 = LLT::scalar(32); \(void)s32; \const LLT s64 = LLT::scalar(64); \(void)s64; \const LLT s128 = LLT::scalar(128); \(void)s128; \do \SettingUpActionsBlock while (0); \getLegacyLegalizerInfo().computeTables(); \verify(*ST.getInstrInfo()); \} \};static inline bool CheckMachineFunction(const MachineFunction &MF,StringRef CheckStr) {SmallString<512> Msg;raw_svector_ostream OS(Msg);MF.print(OS);auto OutputBuf = MemoryBuffer::getMemBuffer(Msg, "Output", false);auto CheckBuf = MemoryBuffer::getMemBuffer(CheckStr, "");SmallString<4096> CheckFileBuffer;FileCheckRequest Req;FileCheck FC(Req);StringRef CheckFileText =FC.CanonicalizeFile(*CheckBuf.get(), CheckFileBuffer);SourceMgr SM;SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(CheckFileText, "CheckFile"),SMLoc());Regex PrefixRE = FC.buildCheckPrefixRegex();if (FC.readCheckFile(SM, CheckFileText, PrefixRE))return false;auto OutBuffer = OutputBuf->getBuffer();SM.AddNewSourceBuffer(std::move(OutputBuf), SMLoc());return FC.checkInput(SM, OutBuffer);}#endif
//===------------------------------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"namespace llvm {std::ostream &operator<<(std::ostream &OS, const LLT Ty) {std::string Repr;raw_string_ostream SS{Repr};Ty.print(SS);OS << SS.str();return OS;}std::ostream &operator<<(std::ostream &OS, const MachineFunction &MF) {std::string Repr;raw_string_ostream SS{Repr};MF.print(SS);OS << SS.str();return OS;}}std::unique_ptr<LLVMTargetMachine>AArch64GISelMITest::createTargetMachine() const {Triple TargetTriple("aarch64--");std::string Error;const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);if (!T)return nullptr;TargetOptions Options;return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine("AArch64", "", "", Options, None, None, CodeGenOpt::Aggressive)));}void AArch64GISelMITest::getTargetTestModuleString(SmallString<512> &S,StringRef MIRFunc) const {(Twine(R"MIR(---...name: functracksRegLiveness: trueregisters:- { id: 0, class: _ }- { id: 1, class: _ }- { id: 2, class: _ }- { id: 3, class: _ }body: |bb.1:liveins: $x0, $x1, $x2, $x4%0(s64) = COPY $x0%1(s64) = COPY $x1%2(s64) = COPY $x2)MIR") +Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S);}std::unique_ptr<LLVMTargetMachine>AMDGPUGISelMITest::createTargetMachine() const {Triple TargetTriple("amdgcn-amd-amdhsa");std::string Error;const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);if (!T)return nullptr;TargetOptions Options;return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine("amdgcn-amd-amdhsa", "gfx900", "", Options, None, None,CodeGenOpt::Aggressive)));}void AMDGPUGISelMITest::getTargetTestModuleString(SmallString<512> &S, StringRef MIRFunc) const {(Twine(R"MIR(---...name: functracksRegLiveness: trueregisters:- { id: 0, class: _ }- { id: 1, class: _ }- { id: 2, class: _ }- { id: 3, class: _ }body: |bb.1:liveins: $vgpr0, $vgpr1, $vgpr2%0(s32) = COPY $vgpr0%1(s32) = COPY $vgpr1%2(s32) = COPY $vgpr2)MIR") + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S);}
//===- GISelAliasTest.cpp--------------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/LoadStoreOpt.h"#include "llvm/CodeGen/MachineFrameInfo.h"#include "llvm/CodeGen/MachineMemOperand.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Metadata.h"#include "llvm/Support/AtomicOrdering.h"#include "gtest/gtest.h"namespace {// Test simple aliasing.TEST_F(AArch64GISelMITest, SimpleAlias) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);LLT P0 = LLT::pointer(0, 64);auto Base = B.buildIntToPtr(P0, Copies[0]);auto Base2 = B.buildIntToPtr(P0, Copies[1]);// These two addresses are identical.auto Addr = B.buildPtrAdd(P0, Base, B.buildConstant(S64, 8));auto Addr2 = B.buildPtrAdd(P0, Base, B.buildConstant(S64, 8));MachinePointerInfo PtrInfo;auto *LoadMMO = MF->getMachineMemOperand(PtrInfo, MachineMemOperand::Flags::MOLoad, S64, Align());auto Ld1 = B.buildLoad(S64, Addr, *LoadMMO);auto Ld2 = B.buildLoad(S64, Addr2, *LoadMMO);// We expect the same address to return alias.EXPECT_TRUE(GISelAddressing::instMayAlias(*Ld1, *Ld2, *MRI, nullptr));// Expect both being volatile to say alias, since we can't reorder them.auto *LoadVolMMO = MF->getMachineMemOperand(LoadMMO,MachineMemOperand::Flags::MOLoad | MachineMemOperand::Flags::MOVolatile);// Pick a different address so we don't trivially match the alias case above.auto VolLd1 = B.buildLoad(S64, Addr, *LoadVolMMO);auto VolLd2 = B.buildLoad(S64, Base2, *LoadVolMMO);EXPECT_TRUE(GISelAddressing::instMayAlias(*VolLd1, *VolLd2, *MRI, nullptr));// Same for atomics.auto *LoadAtomicMMO = MF->getMachineMemOperand(PtrInfo, MachineMemOperand::Flags::MOLoad, S64, Align(8), AAMDNodes(),nullptr, SyncScope::System, AtomicOrdering::Acquire);auto AtomicLd1 = B.buildLoad(S64, Addr, *LoadAtomicMMO);auto AtomicLd2 = B.buildLoad(S64, Base2, *LoadAtomicMMO);EXPECT_TRUE(GISelAddressing::instMayAlias(*AtomicLd1, *AtomicLd2, *MRI, nullptr));// Invariant memory with stores.auto *LoadInvariantMMO = MF->getMachineMemOperand(LoadMMO,MachineMemOperand::Flags::MOLoad | MachineMemOperand::Flags::MOInvariant);auto InvariantLd = B.buildLoad(S64, Addr, *LoadInvariantMMO);auto Store = B.buildStore(B.buildConstant(S64, 0), Base2, PtrInfo, Align());EXPECT_FALSE(GISelAddressing::instMayAlias(*InvariantLd, *Store, *MRI, nullptr));}// Test aliasing checks for same base + different offsets.TEST_F(AArch64GISelMITest, OffsetAliasing) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);LLT P0 = LLT::pointer(0, 64);auto Base = B.buildIntToPtr(P0, Copies[0]);auto Addr = B.buildPtrAdd(P0, Base, B.buildConstant(S64, 8));auto Addr2 = B.buildPtrAdd(P0, Base, B.buildConstant(S64, 16));MachinePointerInfo PtrInfo;auto *LoadMMO = MF->getMachineMemOperand(PtrInfo, MachineMemOperand::Flags::MOLoad, S64, Align());auto Ld1 = B.buildLoad(S64, Addr, *LoadMMO);auto Ld2 = B.buildLoad(S64, Addr2, *LoadMMO);// The offset between the two addresses is >= than the size of access.// Can't alias.EXPECT_FALSE(GISelAddressing::instMayAlias(*Ld1, *Ld2, *MRI, nullptr));EXPECT_FALSE(GISelAddressing::instMayAlias(*Ld2, *Ld1, *MRI, nullptr));auto Addr3 = B.buildPtrAdd(P0, Base, B.buildConstant(S64, 4));auto Ld3 = B.buildLoad(S64, Addr3, *LoadMMO);// Offset of 4 is < the size of access, 8 bytes.EXPECT_TRUE(GISelAddressing::instMayAlias(*Ld1, *Ld3, *MRI, nullptr));}// Test aliasing checks for frame indexes.TEST_F(AArch64GISelMITest, FrameIndexAliasing) {setUp();if (!TM)return;LLT S64 = LLT::scalar(64);LLT P0 = LLT::pointer(0, 64);auto &MFI = MF->getFrameInfo();auto FixedFI1 = MFI.CreateFixedObject(8, 0, true);auto FixedFI2 = MFI.CreateFixedObject(8, 8, true);auto FI1 = MFI.CreateStackObject(8, Align(8), false);auto GFI1 = B.buildFrameIndex(P0, FI1);// This G_FRAME_INDEX is separate but refers to the same index.auto GFI2 = B.buildFrameIndex(P0, FI1);MachinePointerInfo PtrInfo;auto *LoadMMO = MF->getMachineMemOperand(PtrInfo, MachineMemOperand::Flags::MOLoad, S64, Align());auto Ld1 = B.buildLoad(S64, GFI1, *LoadMMO);auto Ld2 = B.buildLoad(S64, GFI2, *LoadMMO);// The offset between the two addresses is >= than the size of access.// Can't alias.EXPECT_FALSE(GISelAddressing::instMayAlias(*Ld1, *Ld2, *MRI, nullptr));auto GFixedFI1 = B.buildFrameIndex(P0, FixedFI1);auto GFixedFI2 = B.buildFrameIndex(P0, FixedFI2);auto FixedFILd1 = B.buildLoad(S64, GFixedFI1, *LoadMMO);auto FixedFILd2 = B.buildLoad(S64, GFixedFI2, *LoadMMO);// If we have two different FrameIndex bases, but at least one is not a fixed// object, then we can say they don't alias. If both were fixed, then we could// have multiple frameindex slots being accessed at once since their relative// positions are known. However, if one is not fixed, then they can't alias// because non-fixed FIs are only given offsets during PEI.EXPECT_FALSE(GISelAddressing::instMayAlias(*FixedFILd1, *Ld1, *MRI, nullptr));EXPECT_TRUE(GISelAddressing::instMayAlias(*FixedFILd1, *FixedFILd2, *MRI, nullptr));}} // namespace
//===- ConstantFoldingTest.cpp -------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"#include "llvm/CodeGen/GlobalISel/Utils.h"#include "llvm/CodeGen/MachineFunction.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST_F(AArch64GISelMITest, FoldWithBuilder) {setUp();if (!TM)return;// Try to use the FoldableInstructionsBuilder to build binary ops.CSEMIRBuilder CFB(B.getState());LLT s32 = LLT::scalar(32);int64_t Cst;auto MIBCAdd =CFB.buildAdd(s32, CFB.buildConstant(s32, 0), CFB.buildConstant(s32, 1));// This should be a constant now.bool match = mi_match(MIBCAdd.getReg(0), *MRI, m_ICst(Cst));EXPECT_TRUE(match);EXPECT_EQ(Cst, 1);auto MIBCAdd1 =CFB.buildInstr(TargetOpcode::G_ADD, {s32},{CFB.buildConstant(s32, 0), CFB.buildConstant(s32, 1)});// This should be a constant now.match = mi_match(MIBCAdd1.getReg(0), *MRI, m_ICst(Cst));EXPECT_TRUE(match);EXPECT_EQ(Cst, 1);// Try one of the other constructors of MachineIRBuilder to make sure it's// compatible.CSEMIRBuilder CFB1(*MF);CFB1.setInsertPt(*EntryMBB, EntryMBB->end());auto MIBCSub =CFB1.buildInstr(TargetOpcode::G_SUB, {s32},{CFB1.buildConstant(s32, 1), CFB1.buildConstant(s32, 1)});// This should be a constant now.match = mi_match(MIBCSub.getReg(0), *MRI, m_ICst(Cst));EXPECT_TRUE(match);EXPECT_EQ(Cst, 0);auto MIBCSext1 =CFB1.buildInstr(TargetOpcode::G_SEXT_INREG, {s32},{CFB1.buildConstant(s32, 0x01), uint64_t(8)});// This should be a constant now.match = mi_match(MIBCSext1.getReg(0), *MRI, m_ICst(Cst));EXPECT_TRUE(match);EXPECT_EQ(1, Cst);auto MIBCSext2 =CFB1.buildInstr(TargetOpcode::G_SEXT_INREG, {s32},{CFB1.buildConstant(s32, 0x80), uint64_t(8)});// This should be a constant now.match = mi_match(MIBCSext2.getReg(0), *MRI, m_ICst(Cst));EXPECT_TRUE(match);EXPECT_EQ(-0x80, Cst);}TEST_F(AArch64GISelMITest, FoldBinOp) {setUp();if (!TM)return;LLT s32{LLT::scalar(32)};auto MIBCst1 = B.buildConstant(s32, 16);auto MIBCst2 = B.buildConstant(s32, 9);auto MIBFCst1 = B.buildFConstant(s32, 1.0000001);auto MIBFCst2 = B.buildFConstant(s32, 2.0);// Test G_ADD folding Integer + Mixed Int-Float casesOptional<APInt> FoldGAddInt =ConstantFoldBinOp(TargetOpcode::G_ADD, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGAddInt.has_value());EXPECT_EQ(25ULL, FoldGAddInt.value().getLimitedValue());Optional<APInt> FoldGAddMix =ConstantFoldBinOp(TargetOpcode::G_ADD, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGAddMix.has_value());EXPECT_EQ(1073741840ULL, FoldGAddMix.value().getLimitedValue());// Test G_AND folding Integer + Mixed Int-Float casesOptional<APInt> FoldGAndInt =ConstantFoldBinOp(TargetOpcode::G_AND, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGAndInt.has_value());EXPECT_EQ(0ULL, FoldGAndInt.value().getLimitedValue());Optional<APInt> FoldGAndMix =ConstantFoldBinOp(TargetOpcode::G_AND, MIBCst2.getReg(0),MIBFCst1.getReg(0), *MRI);EXPECT_TRUE(FoldGAndMix.has_value());EXPECT_EQ(1ULL, FoldGAndMix.value().getLimitedValue());// Test G_ASHR folding Integer + Mixed casesOptional<APInt> FoldGAShrInt =ConstantFoldBinOp(TargetOpcode::G_ASHR, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGAShrInt.has_value());EXPECT_EQ(0ULL, FoldGAShrInt.value().getLimitedValue());Optional<APInt> FoldGAShrMix =ConstantFoldBinOp(TargetOpcode::G_ASHR, MIBFCst2.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGAShrMix.has_value());EXPECT_EQ(2097152ULL, FoldGAShrMix.value().getLimitedValue());// Test G_LSHR folding Integer + Mixed Int-Float casesOptional<APInt> FoldGLShrInt =ConstantFoldBinOp(TargetOpcode::G_LSHR, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGLShrInt.has_value());EXPECT_EQ(0ULL, FoldGLShrInt.value().getLimitedValue());Optional<APInt> FoldGLShrMix =ConstantFoldBinOp(TargetOpcode::G_LSHR, MIBFCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGLShrMix.has_value());EXPECT_EQ(2080768ULL, FoldGLShrMix.value().getLimitedValue());// Test G_MUL folding Integer + Mixed Int-Float casesOptional<APInt> FoldGMulInt =ConstantFoldBinOp(TargetOpcode::G_MUL, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGMulInt.has_value());EXPECT_EQ(144ULL, FoldGMulInt.value().getLimitedValue());Optional<APInt> FoldGMulMix =ConstantFoldBinOp(TargetOpcode::G_MUL, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGMulMix.has_value());EXPECT_EQ(0ULL, FoldGMulMix.value().getLimitedValue());// Test G_OR folding Integer + Mixed Int-Float casesOptional<APInt> FoldGOrInt =ConstantFoldBinOp(TargetOpcode::G_OR, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGOrInt.has_value());EXPECT_EQ(25ULL, FoldGOrInt.value().getLimitedValue());Optional<APInt> FoldGOrMix =ConstantFoldBinOp(TargetOpcode::G_OR, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGOrMix.has_value());EXPECT_EQ(1073741840ULL, FoldGOrMix.value().getLimitedValue());// Test G_SHL folding Integer + Mixed Int-Float casesOptional<APInt> FoldGShlInt =ConstantFoldBinOp(TargetOpcode::G_SHL, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGShlInt.has_value());EXPECT_EQ(8192ULL, FoldGShlInt.value().getLimitedValue());Optional<APInt> FoldGShlMix =ConstantFoldBinOp(TargetOpcode::G_SHL, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGShlMix.has_value());EXPECT_EQ(0ULL, FoldGShlMix.value().getLimitedValue());// Test G_SUB folding Integer + Mixed Int-Float casesOptional<APInt> FoldGSubInt =ConstantFoldBinOp(TargetOpcode::G_SUB, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGSubInt.has_value());EXPECT_EQ(7ULL, FoldGSubInt.value().getLimitedValue());Optional<APInt> FoldGSubMix =ConstantFoldBinOp(TargetOpcode::G_SUB, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGSubMix.has_value());EXPECT_EQ(3221225488ULL, FoldGSubMix.value().getLimitedValue());// Test G_XOR folding Integer + Mixed Int-Float casesOptional<APInt> FoldGXorInt =ConstantFoldBinOp(TargetOpcode::G_XOR, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGXorInt.has_value());EXPECT_EQ(25ULL, FoldGXorInt.value().getLimitedValue());Optional<APInt> FoldGXorMix =ConstantFoldBinOp(TargetOpcode::G_XOR, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGXorMix.has_value());EXPECT_EQ(1073741840ULL, FoldGXorMix.value().getLimitedValue());// Test G_UDIV folding Integer + Mixed Int-Float casesOptional<APInt> FoldGUdivInt =ConstantFoldBinOp(TargetOpcode::G_UDIV, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGUdivInt.has_value());EXPECT_EQ(1ULL, FoldGUdivInt.value().getLimitedValue());Optional<APInt> FoldGUdivMix =ConstantFoldBinOp(TargetOpcode::G_UDIV, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGUdivMix.has_value());EXPECT_EQ(0ULL, FoldGUdivMix.value().getLimitedValue());// Test G_SDIV folding Integer + Mixed Int-Float casesOptional<APInt> FoldGSdivInt =ConstantFoldBinOp(TargetOpcode::G_SDIV, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGSdivInt.has_value());EXPECT_EQ(1ULL, FoldGSdivInt.value().getLimitedValue());Optional<APInt> FoldGSdivMix =ConstantFoldBinOp(TargetOpcode::G_SDIV, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGSdivMix.has_value());EXPECT_EQ(0ULL, FoldGSdivMix.value().getLimitedValue());// Test G_UREM folding Integer + Mixed Int-Float casesOptional<APInt> FoldGUremInt =ConstantFoldBinOp(TargetOpcode::G_UDIV, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGUremInt.has_value());EXPECT_EQ(1ULL, FoldGUremInt.value().getLimitedValue());Optional<APInt> FoldGUremMix =ConstantFoldBinOp(TargetOpcode::G_UDIV, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGUremMix.has_value());EXPECT_EQ(0ULL, FoldGUremMix.value().getLimitedValue());// Test G_SREM folding Integer + Mixed Int-Float casesOptional<APInt> FoldGSremInt =ConstantFoldBinOp(TargetOpcode::G_SREM, MIBCst1.getReg(0),MIBCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGSremInt.has_value());EXPECT_EQ(7ULL, FoldGSremInt.value().getLimitedValue());Optional<APInt> FoldGSremMix =ConstantFoldBinOp(TargetOpcode::G_SREM, MIBCst1.getReg(0),MIBFCst2.getReg(0), *MRI);EXPECT_TRUE(FoldGSremMix.has_value());EXPECT_EQ(16ULL, FoldGSremMix.value().getLimitedValue());}} // namespace
//===- CSETest.cpp -----------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "GISelMITest.h"#include "llvm/CodeGen/GlobalISel/CSEInfo.h"#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"#include "gtest/gtest.h"namespace {TEST_F(AArch64GISelMITest, TestCSE) {setUp();if (!TM)return;LLT s16{LLT::scalar(16)};LLT s32{LLT::scalar(32)};auto MIBInput = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[0]});auto MIBInput1 = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[1]});auto MIBAdd = B.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});GISelCSEInfo CSEInfo;CSEInfo.setCSEConfig(std::make_unique<CSEConfigFull>());CSEInfo.analyze(*MF);B.setCSEInfo(&CSEInfo);CSEMIRBuilder CSEB(B.getState());CSEB.setInsertPt(B.getMBB(), B.getInsertPt());Register AddReg = MRI->createGenericVirtualRegister(s16);auto MIBAddCopy =CSEB.buildInstr(TargetOpcode::G_ADD, {AddReg}, {MIBInput, MIBInput});EXPECT_EQ(MIBAddCopy->getOpcode(), TargetOpcode::COPY);auto MIBAdd2 =CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});EXPECT_TRUE(&*MIBAdd == &*MIBAdd2);auto MIBAdd4 =CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});EXPECT_TRUE(&*MIBAdd == &*MIBAdd4);auto MIBAdd5 =CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput1});EXPECT_TRUE(&*MIBAdd != &*MIBAdd5);// Try building G_CONSTANTS.auto MIBCst = CSEB.buildConstant(s32, 0);auto MIBCst1 = CSEB.buildConstant(s32, 0);EXPECT_TRUE(&*MIBCst == &*MIBCst1);// Try the CFing of BinaryOps.auto MIBCF1 = CSEB.buildInstr(TargetOpcode::G_ADD, {s32}, {MIBCst, MIBCst});EXPECT_TRUE(&*MIBCF1 == &*MIBCst);// Try out building FCONSTANTs.auto MIBFP0 = CSEB.buildFConstant(s32, 1.0);auto MIBFP0_1 = CSEB.buildFConstant(s32, 1.0);EXPECT_TRUE(&*MIBFP0 == &*MIBFP0_1);CSEInfo.print();// Make sure buildConstant with a vector type doesn't crash, and the elements// CSE.auto Splat0 = CSEB.buildConstant(LLT::fixed_vector(2, s32), 0);EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, Splat0->getOpcode());EXPECT_EQ(Splat0.getReg(1), Splat0.getReg(2));EXPECT_EQ(&*MIBCst, MRI->getVRegDef(Splat0.getReg(1)));auto FSplat = CSEB.buildFConstant(LLT::fixed_vector(2, s32), 1.0);EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, FSplat->getOpcode());EXPECT_EQ(FSplat.getReg(1), FSplat.getReg(2));EXPECT_EQ(&*MIBFP0, MRI->getVRegDef(FSplat.getReg(1)));// Check G_UNMERGE_VALUESauto MIBUnmerge = CSEB.buildUnmerge({s32, s32}, Copies[0]);auto MIBUnmerge2 = CSEB.buildUnmerge({s32, s32}, Copies[0]);EXPECT_TRUE(&*MIBUnmerge == &*MIBUnmerge2);// Check G_IMPLICIT_DEFauto Undef0 = CSEB.buildUndef(s32);auto Undef1 = CSEB.buildUndef(s32);EXPECT_EQ(&*Undef0, &*Undef1);// If the observer is installed to the MF, CSE can also// track new instructions built without the CSEBuilder and// the newly built instructions are available for CSEing next// time a build call is made through the CSEMIRBuilder.// Additionally, the CSE implementation lazily hashes instructions// (every build call) to give chance for the instruction to be fully// built (say using .addUse().addDef().. so on).GISelObserverWrapper WrapperObserver(&CSEInfo);RAIIMFObsDelInstaller Installer(*MF, WrapperObserver);MachineIRBuilder RegularBuilder(*MF);RegularBuilder.setInsertPt(*EntryMBB, EntryMBB->begin());auto NonCSEFMul = RegularBuilder.buildInstr(TargetOpcode::G_AND).addDef(MRI->createGenericVirtualRegister(s32)).addUse(Copies[0]).addUse(Copies[1]);auto CSEFMul =CSEB.buildInstr(TargetOpcode::G_AND, {s32}, {Copies[0], Copies[1]});EXPECT_EQ(&*CSEFMul, &*NonCSEFMul);auto ExtractMIB = CSEB.buildInstr(TargetOpcode::G_EXTRACT, {s16},{Copies[0], static_cast<uint64_t>(0)});auto ExtractMIB1 = CSEB.buildInstr(TargetOpcode::G_EXTRACT, {s16},{Copies[0], static_cast<uint64_t>(0)});auto ExtractMIB2 = CSEB.buildInstr(TargetOpcode::G_EXTRACT, {s16},{Copies[0], static_cast<uint64_t>(1)});EXPECT_EQ(&*ExtractMIB, &*ExtractMIB1);EXPECT_NE(&*ExtractMIB, &*ExtractMIB2);}TEST_F(AArch64GISelMITest, TestCSEConstantConfig) {setUp();if (!TM)return;LLT s16{LLT::scalar(16)};auto MIBInput = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[0]});auto MIBAdd = B.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});auto MIBZero = B.buildConstant(s16, 0);GISelCSEInfo CSEInfo;CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>());CSEInfo.analyze(*MF);B.setCSEInfo(&CSEInfo);CSEMIRBuilder CSEB(B.getState());CSEB.setInsertPt(*EntryMBB, EntryMBB->begin());auto MIBAdd1 =CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});// We should CSE constants only. Adds should not be CSEd.EXPECT_TRUE(MIBAdd1->getOpcode() != TargetOpcode::COPY);EXPECT_TRUE(&*MIBAdd1 != &*MIBAdd);// We should CSE constant.auto MIBZeroTmp = CSEB.buildConstant(s16, 0);EXPECT_TRUE(&*MIBZero == &*MIBZeroTmp);// Check G_IMPLICIT_DEFauto Undef0 = CSEB.buildUndef(s16);auto Undef1 = CSEB.buildUndef(s16);EXPECT_EQ(&*Undef0, &*Undef1);}TEST_F(AArch64GISelMITest, TestCSEImmediateNextCSE) {setUp();if (!TM)return;LLT s32{LLT::scalar(32)};// We want to check that when the CSE hit is on the next instruction, i.e. at// the current insert pt, that the insertion point is moved ahead of the// instruction.GISelCSEInfo CSEInfo;CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>());CSEInfo.analyze(*MF);B.setCSEInfo(&CSEInfo);CSEMIRBuilder CSEB(B.getState());CSEB.buildConstant(s32, 0);auto MIBCst2 = CSEB.buildConstant(s32, 2);// Move the insert point before the second constant.CSEB.setInsertPt(CSEB.getMBB(), --CSEB.getInsertPt());auto MIBCst3 = CSEB.buildConstant(s32, 2);EXPECT_TRUE(&*MIBCst2 == &*MIBCst3);EXPECT_TRUE(CSEB.getInsertPt() == CSEB.getMBB().end());}TEST_F(AArch64GISelMITest, TestConstantFoldCTL) {setUp();if (!TM)return;LLT s32 = LLT::scalar(32);GISelCSEInfo CSEInfo;CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>());CSEInfo.analyze(*MF);B.setCSEInfo(&CSEInfo);CSEMIRBuilder CSEB(B.getState());auto Cst8 = CSEB.buildConstant(s32, 8);auto *CtlzDef = &*CSEB.buildCTLZ(s32, Cst8);EXPECT_TRUE(CtlzDef->getOpcode() == TargetOpcode::G_CONSTANT);EXPECT_TRUE(CtlzDef->getOperand(1).getCImm()->getZExtValue() == 28);// Test vector.auto Cst16 = CSEB.buildConstant(s32, 16);auto Cst32 = CSEB.buildConstant(s32, 32);auto Cst64 = CSEB.buildConstant(s32, 64);LLT VecTy = LLT::fixed_vector(4, s32);auto BV = CSEB.buildBuildVector(VecTy, {Cst8.getReg(0), Cst16.getReg(0),Cst32.getReg(0), Cst64.getReg(0)});CSEB.buildCTLZ(VecTy, BV);auto CheckStr = R"(; CHECK: [[CST8:%[0-9]+]]:_(s32) = G_CONSTANT i32 8; CHECK: [[CST28:%[0-9]+]]:_(s32) = G_CONSTANT i32 28; CHECK: [[CST16:%[0-9]+]]:_(s32) = G_CONSTANT i32 16; CHECK: [[CST32:%[0-9]+]]:_(s32) = G_CONSTANT i32 32; CHECK: [[CST64:%[0-9]+]]:_(s32) = G_CONSTANT i32 64; CHECK: [[BV1:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[CST8]]:_(s32), [[CST16]]:_(s32), [[CST32]]:_(s32), [[CST64]]:_(s32); CHECK: [[CST27:%[0-9]+]]:_(s32) = G_CONSTANT i32 27; CHECK: [[CST26:%[0-9]+]]:_(s32) = G_CONSTANT i32 26; CHECK: [[CST25:%[0-9]+]]:_(s32) = G_CONSTANT i32 25; CHECK: [[BV2:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[CST28]]:_(s32), [[CST27]]:_(s32), [[CST26]]:_(s32), [[CST25]]:_(s32))";EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;}} // namespace
set(LLVM_LINK_COMPONENTS${LLVM_TARGETS_TO_BUILD}CodeGenCoreFileCheckGlobalISelMCMIRParserSupportTarget)add_llvm_unittest(GlobalISelTestsConstantFoldingTest.cppCSETest.cppLegalizerTest.cppLegalizerHelperTest.cppLegalizerInfoTest.cppMachineIRBuilderTest.cppGISelMITest.cppPatternMatchTest.cppKnownBitsTest.cppKnownBitsVectorTest.cppGISelUtilsTest.cppGISelAliasTest.cpp)
//===- llvm/unittest/CodeGen/DwarfStringPoolEntryRefTest.cpp --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/DwarfStringPoolEntry.h"#include "llvm/Support/Allocator.h"#include "llvm/Testing/Support/Error.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <string>using namespace llvm;TEST(DwarfStringPoolEntryRefTest, TestFullEntry) {BumpPtrAllocator Allocator;StringMapEntry<DwarfStringPoolEntry> *StringEntry1 =StringMapEntry<DwarfStringPoolEntry>::Create("Key1", Allocator, DwarfStringPoolEntry{nullptr, 0, 0});EXPECT_TRUE(StringEntry1->getKey() == "Key1");EXPECT_TRUE(StringEntry1->second.Symbol == nullptr);EXPECT_TRUE(StringEntry1->second.Offset == 0);EXPECT_TRUE(StringEntry1->second.Index == 0);DwarfStringPoolEntryRef Ref1(*StringEntry1);EXPECT_TRUE(Ref1.getString() == "Key1");EXPECT_TRUE(Ref1.getOffset() == 0);EXPECT_TRUE(Ref1.getIndex() == 0);DwarfStringPoolEntryRef Ref2(*StringEntry1);EXPECT_TRUE(Ref2.getString() == "Key1");EXPECT_TRUE(Ref2.getOffset() == 0);EXPECT_TRUE(Ref2.getIndex() == 0);EXPECT_TRUE(Ref1 == Ref2);EXPECT_FALSE(Ref1 != Ref2);StringMapEntry<DwarfStringPoolEntry> *StringEntry2 =StringMapEntry<DwarfStringPoolEntry>::Create("Key2", Allocator, DwarfStringPoolEntry{nullptr, 0x1000, 1});EXPECT_TRUE(StringEntry2->getKey() == "Key2");EXPECT_TRUE(StringEntry2->second.Symbol == nullptr);EXPECT_TRUE(StringEntry2->second.Offset == 0x1000);EXPECT_TRUE(StringEntry2->second.Index == 1);DwarfStringPoolEntryRef Ref3(*StringEntry2);EXPECT_TRUE(Ref3.getString() == "Key2");EXPECT_TRUE(Ref3.getOffset() == 0x1000);EXPECT_TRUE(Ref3.getIndex() == 1);EXPECT_TRUE(Ref1 != Ref3);}bool isEntryEqual(const DwarfStringPoolEntry &LHS,const DwarfStringPoolEntry &RHS) {return LHS.Symbol == RHS.Symbol && LHS.Offset == RHS.Offset &&LHS.Index == RHS.Index;}TEST(DwarfStringPoolEntryRefTest, TestShortEntry) {BumpPtrAllocator Allocator;DwarfStringPoolEntry DwarfEntry1 = {nullptr, 0, 0};StringMapEntry<DwarfStringPoolEntry *> *StringEntry1 =StringMapEntry<DwarfStringPoolEntry *>::Create("Key1", Allocator,&DwarfEntry1);EXPECT_TRUE(StringEntry1->getKey() == "Key1");EXPECT_TRUE(StringEntry1->second->Symbol == nullptr);EXPECT_TRUE(StringEntry1->second->Offset == 0);EXPECT_TRUE(StringEntry1->second->Index == 0);DwarfStringPoolEntryRef Ref1(*StringEntry1);EXPECT_TRUE(Ref1.getString() == "Key1");EXPECT_TRUE(Ref1.getOffset() == 0);EXPECT_TRUE(Ref1.getIndex() == 0);EXPECT_TRUE(isEntryEqual(Ref1.getEntry(), DwarfEntry1));DwarfStringPoolEntryRef Ref2(*StringEntry1);EXPECT_TRUE(Ref2.getString() == "Key1");EXPECT_TRUE(Ref2.getOffset() == 0);EXPECT_TRUE(isEntryEqual(Ref2.getEntry(), DwarfEntry1));EXPECT_TRUE(Ref1 == Ref2);EXPECT_FALSE(Ref1 != Ref2);DwarfStringPoolEntry DwarfEntry2 = {nullptr, 0x1000, 1};StringMapEntry<DwarfStringPoolEntry *> *StringEntry2 =StringMapEntry<DwarfStringPoolEntry *>::Create("Key2", Allocator,&DwarfEntry2);EXPECT_TRUE(StringEntry2->getKey() == "Key2");EXPECT_TRUE(StringEntry2->second->Symbol == nullptr);EXPECT_TRUE(StringEntry2->second->Offset == 0x1000);EXPECT_TRUE(StringEntry2->second->Index == 1);DwarfStringPoolEntryRef Ref3(*StringEntry2);EXPECT_TRUE(Ref3.getString() == "Key2");EXPECT_TRUE(Ref3.getOffset() == 0x1000);EXPECT_TRUE(Ref3.getIndex() == 1);EXPECT_TRUE(isEntryEqual(Ref3.getEntry(), DwarfEntry2));EXPECT_TRUE(Ref1 != Ref3);}TEST(DwarfStringPoolEntryRefTest, CompareFullAndShort) {BumpPtrAllocator Allocator;DwarfStringPoolEntry DwarfEntry1 = {nullptr, 0, 0};StringMapEntry<DwarfStringPoolEntry *> *StringEntry1 =StringMapEntry<DwarfStringPoolEntry *>::Create("Key1", Allocator,&DwarfEntry1);DwarfStringPoolEntryRef Ref1(*StringEntry1);StringMapEntry<DwarfStringPoolEntry> *StringEntry2 =StringMapEntry<DwarfStringPoolEntry>::Create("Key1", Allocator, DwarfStringPoolEntry{nullptr, 0, 0});DwarfStringPoolEntryRef Ref2(*StringEntry2);EXPECT_FALSE(Ref1 == Ref2);}
//===- llvm/unittest/CodeGen/DIETest.cpp ----------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/CodeGen/DIE.h"#include "TestAsmPrinter.h"#include "llvm/CodeGen/AsmPrinter.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCExpr.h"#include "llvm/Testing/Support/Error.h"using namespace llvm;using testing::_;using testing::SaveArg;namespace {using DIETestParams =std::tuple<unsigned, dwarf::DwarfFormat, dwarf::Form, unsigned>;class DIEFixtureBase : public testing::TestWithParam<DIETestParams> {protected:void SetUp() override {unsigned Version;dwarf::DwarfFormat Format;std::tie(Version, Format, Form, Size) = GetParam();auto ExpectedTestPrinter =TestAsmPrinter::create("x86_64-pc-linux", Version, Format);ASSERT_THAT_EXPECTED(ExpectedTestPrinter, Succeeded());TestPrinter = std::move(ExpectedTestPrinter.get());if (!TestPrinter)GTEST_SKIP();}dwarf::Form Form;unsigned Size;std::unique_ptr<TestAsmPrinter> TestPrinter;};struct DIEExprFixture : public DIEFixtureBase {void SetUp() override {DIEFixtureBase::SetUp();if (!TestPrinter)return;Val = MCConstantExpr::create(42, TestPrinter->getCtx());}const MCExpr *Val = nullptr;};TEST_P(DIEExprFixture, SizeOf) {DIEExpr Tst(Val);EXPECT_EQ(Size, Tst.sizeOf(TestPrinter->getAP()->getDwarfFormParams(), Form));}TEST_P(DIEExprFixture, EmitValue) {DIEExpr Tst(Val);EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(Val, Size, _));Tst.emitValue(TestPrinter->getAP(), Form);}INSTANTIATE_TEST_SUITE_P(DIETestParams, DIEExprFixture,testing::Values(DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_data4, 4u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_data8, 8u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_sec_offset, 4u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_data4, 4u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_data8, 8u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_sec_offset, 8u}));struct DIELabelFixture : public DIEFixtureBase {void SetUp() override {DIEFixtureBase::SetUp();if (!TestPrinter)return;Val = TestPrinter->getCtx().createTempSymbol();}const MCSymbol *Val = nullptr;};TEST_P(DIELabelFixture, SizeOf) {DIELabel Tst(Val);EXPECT_EQ(Size, Tst.sizeOf(TestPrinter->getAP()->getDwarfFormParams(), Form));}TEST_P(DIELabelFixture, EmitValue) {DIELabel Tst(Val);const MCExpr *Arg0 = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, Size, _)).WillOnce(SaveArg<0>(&Arg0));Tst.emitValue(TestPrinter->getAP(), Form);const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);ASSERT_NE(ActualArg0, nullptr);EXPECT_EQ(&(ActualArg0->getSymbol()), Val);}INSTANTIATE_TEST_SUITE_P(DIETestParams, DIELabelFixture,testing::Values(DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_data4, 4u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_data8, 8u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_sec_offset, 4u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_strp, 4u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_addr, 8u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_data4, 4u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_data8, 8u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_sec_offset, 8u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_strp, 8u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_addr, 8u}));struct DIEDeltaFixture : public DIEFixtureBase {void SetUp() override {DIEFixtureBase::SetUp();if (!TestPrinter)return;Hi = TestPrinter->getCtx().createTempSymbol();Lo = TestPrinter->getCtx().createTempSymbol();}const MCSymbol *Hi = nullptr;const MCSymbol *Lo = nullptr;};TEST_P(DIEDeltaFixture, SizeOf) {DIEDelta Tst(Hi, Lo);EXPECT_EQ(Size, Tst.sizeOf(TestPrinter->getAP()->getDwarfFormParams(), Form));}TEST_P(DIEDeltaFixture, EmitValue) {DIEDelta Tst(Hi, Lo);EXPECT_CALL(TestPrinter->getMS(), emitAbsoluteSymbolDiff(Hi, Lo, Size));Tst.emitValue(TestPrinter->getAP(), Form);}INSTANTIATE_TEST_SUITE_P(DIETestParams, DIEDeltaFixture,testing::Values(DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_data4, 4u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_data8, 8u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_sec_offset, 4u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_data4, 4u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_data8, 8u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_sec_offset, 8u}));struct DIELocListFixture : public DIEFixtureBase {void SetUp() override { DIEFixtureBase::SetUp(); }};TEST_P(DIELocListFixture, SizeOf) {DIELocList Tst(999);EXPECT_EQ(Size, Tst.sizeOf(TestPrinter->getAP()->getDwarfFormParams(), Form));}INSTANTIATE_TEST_SUITE_P(DIETestParams, DIELocListFixture,testing::Values(DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_loclistx, 2u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_data4, 4u},DIETestParams{4, dwarf::DWARF32, dwarf::DW_FORM_sec_offset, 4u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_loclistx, 2u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_data8, 8u},DIETestParams{4, dwarf::DWARF64, dwarf::DW_FORM_sec_offset, 8u}));} // end namespace
//===- llvm/unittest/CodeGen/DIEHashTest.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===//#include "../lib/CodeGen/AsmPrinter/DIEHash.h"#include "TestAsmPrinter.h"#include "llvm/ADT/STLExtras.h"#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/CodeGen/DIE.h"#include "llvm/CodeGen/DwarfStringPoolEntry.h"#include "llvm/Support/Debug.h"#include "llvm/Support/Format.h"#include "llvm/Support/Host.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Test fixtureclass DIEHashTest : public testing::Test {public:BumpPtrAllocator Alloc;private:StringMap<DwarfStringPoolEntry> Pool;std::unique_ptr<TestAsmPrinter> TestPrinter;void setupTestPrinter() {auto ExpectedTestPrinter = TestAsmPrinter::create(sys::getDefaultTargetTriple(), /*DwarfVersion=*/4, dwarf::DWARF32);ASSERT_THAT_EXPECTED(ExpectedTestPrinter, Succeeded());TestPrinter = std::move(ExpectedTestPrinter.get());}public:DIEString getString(StringRef S) {DwarfStringPoolEntry Entry = {nullptr, 1, 1};return DIEString(DwarfStringPoolEntryRef(*Pool.insert(std::make_pair(S, Entry)).first));}AsmPrinter *getAsmPrinter() {if (!TestPrinter)setupTestPrinter();return TestPrinter ? TestPrinter->getAP() : nullptr;}};TEST_F(DIEHashTest, Data1) {DIEHash Hash;DIE &Die = *DIE::get(Alloc, dwarf::DW_TAG_base_type);DIEInteger Size(4);Die.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Size);uint64_t MD5Res = Hash.computeTypeSignature(Die);ASSERT_EQ(0x1AFE116E83701108ULL, MD5Res);}// struct {};TEST_F(DIEHashTest, TrivialType) {DIE &Unnamed = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);Unnamed.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);// Line and file number are ignored.Unnamed.addValue(Alloc, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, One);Unnamed.addValue(Alloc, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, One);uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed);// The exact same hash GCC produces for this DIE.ASSERT_EQ(0x715305CE6CFD9AD1ULL, MD5Res);}// struct foo { };TEST_F(DIEHashTest, NamedType) {DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);DIEString FooStr = getString("foo");Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);uint64_t MD5Res = DIEHash().computeTypeSignature(Foo);// The exact same hash GCC produces for this DIE.ASSERT_EQ(0xD566DBD2CA5265FFULL, MD5Res);}// namespace space { struct foo { }; }TEST_F(DIEHashTest, NamespacedType) {DIE &CU = *DIE::get(Alloc, dwarf::DW_TAG_compile_unit);auto Space = DIE::get(Alloc, dwarf::DW_TAG_namespace);DIEInteger One(1);DIEString SpaceStr = getString("space");Space->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, SpaceStr);// DW_AT_declaration is ignored.Space->addValue(Alloc, dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present,One);// sibling?auto Foo = DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEString FooStr = getString("foo");Foo->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);Foo->addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);DIE &N = *Foo;Space->addChild(std::move(Foo));CU.addChild(std::move(Space));uint64_t MD5Res = DIEHash().computeTypeSignature(N);// The exact same hash GCC produces for this DIE.ASSERT_EQ(0x7b80381fd17f1e33ULL, MD5Res);}// struct { int member; };TEST_F(DIEHashTest, TypeWithMember) {DIE &Unnamed = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger Four(4);Unnamed.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Four);DIE &Int = *DIE::get(Alloc, dwarf::DW_TAG_base_type);DIEString IntStr = getString("int");Int.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, IntStr);Int.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Four);DIEInteger Five(5);Int.addValue(Alloc, dwarf::DW_AT_encoding, dwarf::DW_FORM_data1, Five);DIEEntry IntRef(Int);auto Member = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString MemberStr = getString("member");Member->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemberStr);DIEInteger Zero(0);Member->addValue(Alloc, dwarf::DW_AT_data_member_location,dwarf::DW_FORM_data1, Zero);Member->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, IntRef);Unnamed.addChild(std::move(Member));uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed);ASSERT_EQ(0x5646aa436b7e07c6ULL, MD5Res);}// struct foo { int mem1, mem2; };TEST_F(DIEHashTest, ReusedType) {DIE &Unnamed = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger Eight(8);Unnamed.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEInteger Four(4);DIE &Int = *DIE::get(Alloc, dwarf::DW_TAG_base_type);DIEString IntStr = getString("int");Int.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, IntStr);Int.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Four);DIEInteger Five(5);Int.addValue(Alloc, dwarf::DW_AT_encoding, dwarf::DW_FORM_data1, Five);DIEEntry IntRef(Int);auto Mem1 = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString Mem1Str = getString("mem1");Mem1->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, Mem1Str);DIEInteger Zero(0);Mem1->addValue(Alloc, dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1,Zero);Mem1->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, IntRef);Unnamed.addChild(std::move(Mem1));auto Mem2 = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString Mem2Str = getString("mem2");Mem2->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, Mem2Str);Mem2->addValue(Alloc, dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1,Four);Mem2->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, IntRef);Unnamed.addChild(std::move(Mem2));uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed);ASSERT_EQ(0x3a7dc3ed7b76b2f8ULL, MD5Res);}// struct foo { static foo f; };TEST_F(DIEHashTest, RecursiveType) {DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);DIEString FooStr = getString("foo");Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString MemStr = getString("mem");Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);DIEEntry FooRef(Foo);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooRef);// DW_AT_external and DW_AT_declaration are ignored anyway, so skip them.Foo.addChild(std::move(Mem));uint64_t MD5Res = DIEHash().computeTypeSignature(Foo);ASSERT_EQ(0x73d8b25aef227b06ULL, MD5Res);}// struct foo { foo *mem; };TEST_F(DIEHashTest, Pointer) {DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger Eight(8);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEString FooStr = getString("foo");Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString MemStr = getString("mem");Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);DIEInteger Zero(0);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1,Zero);DIE &FooPtr = *DIE::get(Alloc, dwarf::DW_TAG_pointer_type);FooPtr.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEEntry FooRef(Foo);FooPtr.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooRef);DIEEntry FooPtrRef(FooPtr);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooPtrRef);Foo.addChild(std::move(Mem));uint64_t MD5Res = DIEHash().computeTypeSignature(Foo);ASSERT_EQ(0x74ea73862e8708d2ULL, MD5Res);}// struct foo { foo &mem; };TEST_F(DIEHashTest, Reference) {DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger Eight(8);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEString FooStr = getString("foo");Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString MemStr = getString("mem");Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);DIEInteger Zero(0);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1,Zero);DIE &FooRef = *DIE::get(Alloc, dwarf::DW_TAG_reference_type);FooRef.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEEntry FooEntry(Foo);FooRef.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooEntry);DIE &FooRefConst = *DIE::get(Alloc, dwarf::DW_TAG_const_type);DIEEntry FooRefRef(FooRef);FooRefConst.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,FooRefRef);DIEEntry FooRefConstRef(FooRefConst);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooRefConstRef);Foo.addChild(std::move(Mem));uint64_t MD5Res = DIEHash().computeTypeSignature(Foo);ASSERT_EQ(0xa0b15f467ad4525bULL, MD5Res);}// struct foo { foo &&mem; };TEST_F(DIEHashTest, RValueReference) {DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger Eight(8);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEString FooStr = getString("foo");Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString MemStr = getString("mem");Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);DIEInteger Zero(0);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1,Zero);DIE &FooRef = *DIE::get(Alloc, dwarf::DW_TAG_rvalue_reference_type);FooRef.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEEntry FooEntry(Foo);FooRef.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooEntry);DIE &FooRefConst = *DIE::get(Alloc, dwarf::DW_TAG_const_type);DIEEntry FooRefRef(FooRef);FooRefConst.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,FooRefRef);DIEEntry FooRefConstRef(FooRefConst);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooRefConstRef);Foo.addChild(std::move(Mem));uint64_t MD5Res = DIEHash().computeTypeSignature(Foo);ASSERT_EQ(0xad211c8c3b31e57ULL, MD5Res);}// struct foo { foo foo::*mem; };TEST_F(DIEHashTest, PtrToMember) {DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger Eight(8);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);DIEString FooStr = getString("foo");Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString MemStr = getString("mem");Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);DIEInteger Zero(0);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1,Zero);DIE &PtrToFooMem = *DIE::get(Alloc, dwarf::DW_TAG_ptr_to_member_type);DIEEntry FooEntry(Foo);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FooEntry);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_containing_type, dwarf::DW_FORM_ref4,FooEntry);DIEEntry PtrToFooMemRef(PtrToFooMem);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, PtrToFooMemRef);Foo.addChild(std::move(Mem));uint64_t MD5Res = DIEHash().computeTypeSignature(Foo);ASSERT_EQ(0x852e0c9ff7c04ebULL, MD5Res);}// Check that the hash for a pointer-to-member matches regardless of whether the// pointed-to type is a declaration or a definition.//// struct bar; // { };// struct foo { bar foo::*mem; };TEST_F(DIEHashTest, PtrToMemberDeclDefMatch) {DIEInteger Zero(0);DIEInteger One(1);DIEInteger Eight(8);DIEString FooStr = getString("foo");DIEString BarStr = getString("bar");DIEString MemStr = getString("mem");uint64_t MD5ResDecl;{DIE &Bar = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Bar.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, BarStr);Bar.addValue(Alloc, dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present,One);DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location,dwarf::DW_FORM_data1, Zero);DIE &PtrToFooMem = *DIE::get(Alloc, dwarf::DW_TAG_ptr_to_member_type);DIEEntry BarEntry(Bar);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,BarEntry);DIEEntry FooEntry(Foo);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_containing_type,dwarf::DW_FORM_ref4, FooEntry);DIEEntry PtrToFooMemRef(PtrToFooMem);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,PtrToFooMemRef);Foo.addChild(std::move(Mem));MD5ResDecl = DIEHash().computeTypeSignature(Foo);}uint64_t MD5ResDef;{DIE &Bar = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Bar.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, BarStr);Bar.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location,dwarf::DW_FORM_data1, Zero);DIE &PtrToFooMem = *DIE::get(Alloc, dwarf::DW_TAG_ptr_to_member_type);DIEEntry BarEntry(Bar);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,BarEntry);DIEEntry FooEntry(Foo);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_containing_type,dwarf::DW_FORM_ref4, FooEntry);DIEEntry PtrToFooMemRef(PtrToFooMem);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,PtrToFooMemRef);Foo.addChild(std::move(Mem));MD5ResDef = DIEHash().computeTypeSignature(Foo);}ASSERT_EQ(MD5ResDef, MD5ResDecl);}// Check that the hash for a pointer-to-member matches regardless of whether the// pointed-to type is a declaration or a definition.//// struct bar; // { };// struct foo { bar bar::*mem; };TEST_F(DIEHashTest, PtrToMemberDeclDefMisMatch) {DIEInteger Zero(0);DIEInteger One(1);DIEInteger Eight(8);DIEString FooStr = getString("foo");DIEString BarStr = getString("bar");DIEString MemStr = getString("mem");uint64_t MD5ResDecl;{DIE &Bar = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Bar.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, BarStr);Bar.addValue(Alloc, dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present,One);DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location,dwarf::DW_FORM_data1, Zero);DIE &PtrToFooMem = *DIE::get(Alloc, dwarf::DW_TAG_ptr_to_member_type);DIEEntry BarEntry(Bar);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,BarEntry);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_containing_type,dwarf::DW_FORM_ref4, BarEntry);DIEEntry PtrToFooMemRef(PtrToFooMem);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,PtrToFooMemRef);Foo.addChild(std::move(Mem));MD5ResDecl = DIEHash().computeTypeSignature(Foo);}uint64_t MD5ResDef;{DIE &Bar = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Bar.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, BarStr);Bar.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location,dwarf::DW_FORM_data1, Zero);DIE &PtrToFooMem = *DIE::get(Alloc, dwarf::DW_TAG_ptr_to_member_type);DIEEntry BarEntry(Bar);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,BarEntry);PtrToFooMem.addValue(Alloc, dwarf::DW_AT_containing_type,dwarf::DW_FORM_ref4, BarEntry);DIEEntry PtrToFooMemRef(PtrToFooMem);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,PtrToFooMemRef);Foo.addChild(std::move(Mem));MD5ResDef = DIEHash().computeTypeSignature(Foo);}// FIXME: This seems to be a bug in the DWARF type hashing specification that// only uses the brief name hashing for types referenced via DW_AT_type. In// this case the type is referenced via DW_AT_containing_type and full hashing// causes a hash to differ when the containing type is a declaration in one TU// and a definition in another.ASSERT_NE(MD5ResDef, MD5ResDecl);}// struct { } a;// struct foo { decltype(a) mem; };TEST_F(DIEHashTest, RefUnnamedType) {DIEInteger Zero(0);DIEInteger One(1);DIEInteger Eight(8);DIEString FooStr = getString("foo");DIEString MemStr = getString("mem");DIE &Unnamed = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Unnamed.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);DIE &Foo = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);Foo.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Eight);Foo.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);auto Mem = DIE::get(Alloc, dwarf::DW_TAG_member);Mem->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, MemStr);Mem->addValue(Alloc, dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1,Zero);DIE &UnnamedPtr = *DIE::get(Alloc, dwarf::DW_TAG_pointer_type);UnnamedPtr.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1,Eight);DIEEntry UnnamedRef(Unnamed);UnnamedPtr.addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4,UnnamedRef);DIEEntry UnnamedPtrRef(UnnamedPtr);Mem->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, UnnamedPtrRef);Foo.addChild(std::move(Mem));uint64_t MD5Res = DIEHash().computeTypeSignature(Foo);ASSERT_EQ(0x954e026f01c02529ULL, MD5Res);}// struct { struct foo { }; };TEST_F(DIEHashTest, NestedType) {DIE &Unnamed = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);Unnamed.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);auto Foo = DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEString FooStr = getString("foo");Foo->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FooStr);Foo->addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);Unnamed.addChild(std::move(Foo));uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed);// The exact same hash GCC produces for this DIE.ASSERT_EQ(0xde8a3b7b43807f4aULL, MD5Res);}// struct { static void func(); };TEST_F(DIEHashTest, MemberFunc) {DIE &Unnamed = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);Unnamed.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);auto Func = DIE::get(Alloc, dwarf::DW_TAG_subprogram);DIEString FuncStr = getString("func");Func->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FuncStr);Unnamed.addChild(std::move(Func));uint64_t MD5Res = DIEHash().computeTypeSignature(Unnamed);// The exact same hash GCC produces for this DIE.ASSERT_EQ(0xd36a1b6dfb604ba0ULL, MD5Res);}// struct A {// static void func();// };TEST_F(DIEHashTest, MemberFuncFlag) {DIE &A = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);DIEString AStr = getString("A");A.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, AStr);A.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);A.addValue(Alloc, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, One);A.addValue(Alloc, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, One);auto Func = DIE::get(Alloc, dwarf::DW_TAG_subprogram);DIEString FuncStr = getString("func");DIEString FuncLinkage = getString("_ZN1A4funcEv");DIEInteger Two(2);Func->addValue(Alloc, dwarf::DW_AT_external, dwarf::DW_FORM_flag_present,One);Func->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FuncStr);Func->addValue(Alloc, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, One);Func->addValue(Alloc, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, Two);Func->addValue(Alloc, dwarf::DW_AT_linkage_name, dwarf::DW_FORM_strp,FuncLinkage);Func->addValue(Alloc, dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present,One);A.addChild(std::move(Func));uint64_t MD5Res = DIEHash().computeTypeSignature(A);// The exact same hash GCC produces for this DIE.ASSERT_EQ(0x8F78211DDCE3DF10ULL, MD5Res);}// Derived from:// struct A {// const static int PI = -3;// };// A a;TEST_F(DIEHashTest, MemberSdata) {DIE &A = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);DIEString AStr = getString("A");A.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, AStr);A.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);A.addValue(Alloc, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, One);A.addValue(Alloc, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, One);DIEInteger Four(4);DIEInteger Five(5);DIEString FStr = getString("int");DIE &IntTyDIE = *DIE::get(Alloc, dwarf::DW_TAG_base_type);IntTyDIE.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, Four);IntTyDIE.addValue(Alloc, dwarf::DW_AT_encoding, dwarf::DW_FORM_data1, Five);IntTyDIE.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FStr);DIEEntry IntTy(IntTyDIE);auto PITyDIE = DIE::get(Alloc, dwarf::DW_TAG_const_type);PITyDIE->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, IntTy);DIEEntry PITy(*PITyDIE);auto PI = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString PIStr = getString("PI");DIEInteger Two(2);DIEInteger NegThree(-3);PI->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, PIStr);PI->addValue(Alloc, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, One);PI->addValue(Alloc, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, Two);PI->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, PITy);PI->addValue(Alloc, dwarf::DW_AT_external, dwarf::DW_FORM_flag_present, One);PI->addValue(Alloc, dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present,One);PI->addValue(Alloc, dwarf::DW_AT_const_value, dwarf::DW_FORM_sdata, NegThree);A.addChild(std::move(PI));uint64_t MD5Res = DIEHash().computeTypeSignature(A);ASSERT_EQ(0x9A216000DD3788A7ULL, MD5Res);}// Derived from:// struct A {// const static float PI = 3.14;// };// A a;TEST_F(DIEHashTest, MemberBlock) {if (!this->getAsmPrinter())GTEST_SKIP();DIE &A = *DIE::get(Alloc, dwarf::DW_TAG_structure_type);DIEInteger One(1);DIEString AStr = getString("A");A.addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, AStr);A.addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, One);A.addValue(Alloc, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, One);A.addValue(Alloc, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, One);DIEInteger Four(4);DIEString FStr = getString("float");auto FloatTyDIE = DIE::get(Alloc, dwarf::DW_TAG_base_type);FloatTyDIE->addValue(Alloc, dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1,Four);FloatTyDIE->addValue(Alloc, dwarf::DW_AT_encoding, dwarf::DW_FORM_data1,Four);FloatTyDIE->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, FStr);DIEEntry FloatTy(*FloatTyDIE);auto PITyDIE = DIE::get(Alloc, dwarf::DW_TAG_const_type);PITyDIE->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, FloatTy);DIEEntry PITy(*PITyDIE);auto PI = DIE::get(Alloc, dwarf::DW_TAG_member);DIEString PIStr = getString("PI");DIEInteger Two(2);PI->addValue(Alloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, PIStr);PI->addValue(Alloc, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, One);PI->addValue(Alloc, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, Two);PI->addValue(Alloc, dwarf::DW_AT_type, dwarf::DW_FORM_ref4, PITy);PI->addValue(Alloc, dwarf::DW_AT_external, dwarf::DW_FORM_flag_present, One);PI->addValue(Alloc, dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present,One);DIEBlock PIBlock;DIEInteger Blk1(0xc3);DIEInteger Blk2(0xf5);DIEInteger Blk3(0x48);DIEInteger Blk4(0x40);PIBlock.addValue(Alloc, (dwarf::Attribute)0, dwarf::DW_FORM_data1, Blk1);PIBlock.addValue(Alloc, (dwarf::Attribute)0, dwarf::DW_FORM_data1, Blk2);PIBlock.addValue(Alloc, (dwarf::Attribute)0, dwarf::DW_FORM_data1, Blk3);PIBlock.addValue(Alloc, (dwarf::Attribute)0, dwarf::DW_FORM_data1, Blk4);PI->addValue(Alloc, dwarf::DW_AT_const_value, dwarf::DW_FORM_block1,&PIBlock);A.addChild(std::move(PI));uint64_t MD5Res = DIEHash(this->getAsmPrinter()).computeTypeSignature(A);ASSERT_EQ(0x493AF53AD3D3F651ULL, MD5Res);}}
set(LLVM_LINK_COMPONENTS${LLVM_TARGETS_TO_BUILD}AnalysisAsmParserAsmPrinterCodeGenCoreFileCheckMCMIRParserPassesSelectionDAGSupportTarget)add_llvm_unittest(CodeGenTestsAArch64SelectionDAGTest.cppAllocationOrderTest.cppAMDGPUMetadataTest.cppAsmPrinterDwarfTest.cppDIEHashTest.cppDIETest.cppDwarfStringPoolEntryRefTest.cppInstrRefLDVTest.cppLowLevelTypeTest.cppLexicalScopesTest.cppMachineInstrBundleIteratorTest.cppMachineInstrTest.cppMachineOperandTest.cppRegAllocScoreTest.cppPassManagerTest.cppScalableVectorMVTsTest.cppSelectionDAGAddressAnalysisTest.cppTypeTraitsTest.cppTargetOptionsTest.cppTestAsmPrinter.cpp)add_subdirectory(GlobalISel)target_link_libraries(CodeGenTests PRIVATE LLVMTestingSupport)
//===- llvm/unittest/CodeGen/AsmPrinterDwarfTest.cpp ----------------------===////// 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////===----------------------------------------------------------------------===//#include "TestAsmPrinter.h"#include "llvm/BinaryFormat/ELF.h"#include "llvm/CodeGen/AsmPrinter.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/Module.h"#include "llvm/IR/PassManager.h"#include "llvm/MC/MCContext.h"#include "llvm/MC/MCSectionELF.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Testing/Support/Error.h"using namespace llvm;using testing::_;using testing::InSequence;using testing::SaveArg;namespace {class AsmPrinterFixtureBase : public testing::Test {void setupTestPrinter(const std::string &TripleStr, unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat) {auto ExpectedTestPrinter =TestAsmPrinter::create(TripleStr, DwarfVersion, DwarfFormat);ASSERT_THAT_EXPECTED(ExpectedTestPrinter, Succeeded());TestPrinter = std::move(ExpectedTestPrinter.get());}protected:bool init(const std::string &TripleStr, unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat) {setupTestPrinter(TripleStr, DwarfVersion, DwarfFormat);return TestPrinter != nullptr;}std::unique_ptr<TestAsmPrinter> TestPrinter;};class AsmPrinterEmitDwarfSymbolReferenceTest : public AsmPrinterFixtureBase {protected:bool init(const std::string &TripleStr, unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat) {if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))return false;// AsmPrinter::emitDwarfSymbolReference(Label, true) gets the associated// section from `Label` to find its BeginSymbol.// Prepare the test symbol `Val` accordingly.Val = TestPrinter->getCtx().createTempSymbol();MCSection *Sec =TestPrinter->getCtx().getELFSection(".tst", ELF::SHT_PROGBITS, 0);SecBeginSymbol = Sec->getBeginSymbol();TestPrinter->getMS().switchSection(Sec);Val->setFragment(&Sec->getDummyFragment());return true;}MCSymbol *Val = nullptr;MCSymbol *SecBeginSymbol = nullptr;};TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, COFF) {if (!init("x86_64-pc-windows", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();EXPECT_CALL(TestPrinter->getMS(), emitCOFFSecRel32(Val, 0));TestPrinter->getAP()->emitDwarfSymbolReference(Val, false);}TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, COFFForceOffset) {if (!init("x86_64-pc-windows", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();EXPECT_CALL(TestPrinter->getMS(),emitAbsoluteSymbolDiff(Val, SecBeginSymbol, 4));TestPrinter->getAP()->emitDwarfSymbolReference(Val, true);}TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF32) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();const MCExpr *Arg0 = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 4, _)).WillOnce(SaveArg<0>(&Arg0));TestPrinter->getAP()->emitDwarfSymbolReference(Val, false);const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);ASSERT_NE(ActualArg0, nullptr);EXPECT_EQ(&(ActualArg0->getSymbol()), Val);}TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF32ForceOffset) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();EXPECT_CALL(TestPrinter->getMS(),emitAbsoluteSymbolDiff(Val, SecBeginSymbol, 4));TestPrinter->getAP()->emitDwarfSymbolReference(Val, true);}TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF64) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();const MCExpr *Arg0 = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 8, _)).WillOnce(SaveArg<0>(&Arg0));TestPrinter->getAP()->emitDwarfSymbolReference(Val, false);const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);ASSERT_NE(ActualArg0, nullptr);EXPECT_EQ(&(ActualArg0->getSymbol()), Val);}TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF64ForceOffset) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();EXPECT_CALL(TestPrinter->getMS(),emitAbsoluteSymbolDiff(Val, SecBeginSymbol, 8));TestPrinter->getAP()->emitDwarfSymbolReference(Val, true);}class AsmPrinterEmitDwarfStringOffsetTest : public AsmPrinterFixtureBase {protected:bool init(const std::string &TripleStr, unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat) {if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))return false;Val.Index = DwarfStringPoolEntry::NotIndexed;Val.Symbol = TestPrinter->getCtx().createTempSymbol();Val.Offset = 42;return true;}DwarfStringPoolEntry Val;};TEST_F(AsmPrinterEmitDwarfStringOffsetTest, DWARF32) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();const MCExpr *Arg0 = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 4, _)).WillOnce(SaveArg<0>(&Arg0));TestPrinter->getAP()->emitDwarfStringOffset(Val);const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);ASSERT_NE(ActualArg0, nullptr);EXPECT_EQ(&(ActualArg0->getSymbol()), Val.Symbol);}TEST_F(AsmPrinterEmitDwarfStringOffsetTest,DWARF32NoRelocationsAcrossSections) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();TestPrinter->setDwarfUsesRelocationsAcrossSections(false);EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val.Offset, 4));TestPrinter->getAP()->emitDwarfStringOffset(Val);}TEST_F(AsmPrinterEmitDwarfStringOffsetTest, DWARF64) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();const MCExpr *Arg0 = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 8, _)).WillOnce(SaveArg<0>(&Arg0));TestPrinter->getAP()->emitDwarfStringOffset(Val);const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);ASSERT_NE(ActualArg0, nullptr);EXPECT_EQ(&(ActualArg0->getSymbol()), Val.Symbol);}TEST_F(AsmPrinterEmitDwarfStringOffsetTest,DWARF64NoRelocationsAcrossSections) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();TestPrinter->setDwarfUsesRelocationsAcrossSections(false);EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val.Offset, 8));TestPrinter->getAP()->emitDwarfStringOffset(Val);}class AsmPrinterEmitDwarfOffsetTest : public AsmPrinterFixtureBase {protected:bool init(const std::string &TripleStr, unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat) {if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))return false;Label = TestPrinter->getCtx().createTempSymbol();return true;}MCSymbol *Label = nullptr;uint64_t Offset = 42;};TEST_F(AsmPrinterEmitDwarfOffsetTest, DWARF32) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();const MCExpr *Arg0 = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 4, _)).WillOnce(SaveArg<0>(&Arg0));TestPrinter->getAP()->emitDwarfOffset(Label, Offset);const MCBinaryExpr *ActualArg0 = dyn_cast_or_null<MCBinaryExpr>(Arg0);ASSERT_NE(ActualArg0, nullptr);EXPECT_EQ(ActualArg0->getOpcode(), MCBinaryExpr::Add);const MCSymbolRefExpr *ActualLHS =dyn_cast_or_null<MCSymbolRefExpr>(ActualArg0->getLHS());ASSERT_NE(ActualLHS, nullptr);EXPECT_EQ(&(ActualLHS->getSymbol()), Label);const MCConstantExpr *ActualRHS =dyn_cast_or_null<MCConstantExpr>(ActualArg0->getRHS());ASSERT_NE(ActualRHS, nullptr);EXPECT_EQ(static_cast<uint64_t>(ActualRHS->getValue()), Offset);}TEST_F(AsmPrinterEmitDwarfOffsetTest, DWARF64) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();const MCExpr *Arg0 = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 8, _)).WillOnce(SaveArg<0>(&Arg0));TestPrinter->getAP()->emitDwarfOffset(Label, Offset);const MCBinaryExpr *ActualArg0 = dyn_cast_or_null<MCBinaryExpr>(Arg0);ASSERT_NE(ActualArg0, nullptr);EXPECT_EQ(ActualArg0->getOpcode(), MCBinaryExpr::Add);const MCSymbolRefExpr *ActualLHS =dyn_cast_or_null<MCSymbolRefExpr>(ActualArg0->getLHS());ASSERT_NE(ActualLHS, nullptr);EXPECT_EQ(&(ActualLHS->getSymbol()), Label);const MCConstantExpr *ActualRHS =dyn_cast_or_null<MCConstantExpr>(ActualArg0->getRHS());ASSERT_NE(ActualRHS, nullptr);EXPECT_EQ(static_cast<uint64_t>(ActualRHS->getValue()), Offset);}class AsmPrinterEmitDwarfLengthOrOffsetTest : public AsmPrinterFixtureBase {protected:uint64_t Val = 42;};TEST_F(AsmPrinterEmitDwarfLengthOrOffsetTest, DWARF32) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 4));TestPrinter->getAP()->emitDwarfLengthOrOffset(Val);}TEST_F(AsmPrinterEmitDwarfLengthOrOffsetTest, DWARF64) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 8));TestPrinter->getAP()->emitDwarfLengthOrOffset(Val);}class AsmPrinterGetUnitLengthFieldByteSizeTest : public AsmPrinterFixtureBase {};TEST_F(AsmPrinterGetUnitLengthFieldByteSizeTest, DWARF32) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();EXPECT_EQ(TestPrinter->getAP()->getUnitLengthFieldByteSize(), 4u);}TEST_F(AsmPrinterGetUnitLengthFieldByteSizeTest, DWARF64) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();EXPECT_EQ(TestPrinter->getAP()->getUnitLengthFieldByteSize(), 12u);}class AsmPrinterEmitDwarfUnitLengthAsIntTest : public AsmPrinterFixtureBase {protected:uint64_t Val = 42;};TEST_F(AsmPrinterEmitDwarfUnitLengthAsIntTest, DWARF32) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 4));TestPrinter->getAP()->emitDwarfUnitLength(Val, "");}TEST_F(AsmPrinterEmitDwarfUnitLengthAsIntTest, DWARF64) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();InSequence S;EXPECT_CALL(TestPrinter->getMS(), emitIntValue(dwarf::DW_LENGTH_DWARF64, 4));EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 8));TestPrinter->getAP()->emitDwarfUnitLength(Val, "");}class AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest: public AsmPrinterFixtureBase {protected:bool init(const std::string &TripleStr, unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat) {if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))return false;return true;}};TEST_F(AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest, DWARF32) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();InSequence S;const MCSymbol *Hi = nullptr;const MCSymbol *Lo = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitAbsoluteSymbolDiff(_, _, 4)).WillOnce(DoAll(SaveArg<0>(&Hi), SaveArg<1>(&Lo)));MCSymbol *LTmp = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitLabel(_, _)).WillOnce(SaveArg<0>(<mp));MCSymbol *HTmp = TestPrinter->getAP()->emitDwarfUnitLength("", "");EXPECT_NE(Lo, nullptr);EXPECT_EQ(Lo, LTmp);EXPECT_NE(Hi, nullptr);EXPECT_EQ(Hi, HTmp);}TEST_F(AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest, DWARF64) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))GTEST_SKIP();InSequence S;const MCSymbol *Hi = nullptr;const MCSymbol *Lo = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitIntValue(dwarf::DW_LENGTH_DWARF64, 4));EXPECT_CALL(TestPrinter->getMS(), emitAbsoluteSymbolDiff(_, _, 8)).WillOnce(DoAll(SaveArg<0>(&Hi), SaveArg<1>(&Lo)));MCSymbol *LTmp = nullptr;EXPECT_CALL(TestPrinter->getMS(), emitLabel(_, _)).WillOnce(SaveArg<0>(<mp));MCSymbol *HTmp = TestPrinter->getAP()->emitDwarfUnitLength("", "");EXPECT_NE(Lo, nullptr);EXPECT_EQ(Lo, LTmp);EXPECT_NE(Hi, nullptr);EXPECT_EQ(Hi, HTmp);}class AsmPrinterHandlerTest : public AsmPrinterFixtureBase {class TestHandler : public AsmPrinterHandler {AsmPrinterHandlerTest &Test;public:TestHandler(AsmPrinterHandlerTest &Test) : Test(Test) {}virtual ~TestHandler() {}virtual void setSymbolSize(const MCSymbol *Sym, uint64_t Size) override {}virtual void beginModule(Module *M) override { Test.BeginCount++; }virtual void endModule() override { Test.EndCount++; }virtual void beginFunction(const MachineFunction *MF) override {}virtual void endFunction(const MachineFunction *MF) override {}virtual void beginInstruction(const MachineInstr *MI) override {}virtual void endInstruction() override {}};protected:bool init(const std::string &TripleStr, unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat) {if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))return false;auto *AP = TestPrinter->getAP();AP->addAsmPrinterHandler(AsmPrinter::HandlerInfo(std::unique_ptr<AsmPrinterHandler>(new TestHandler(*this)),"TestTimerName", "TestTimerDesc", "TestGroupName", "TestGroupDesc"));LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(&AP->TM);legacy::PassManager PM;PM.add(new MachineModuleInfoWrapperPass(LLVMTM));PM.add(TestPrinter->releaseAP()); // Takes ownership of destroying APLLVMContext Context;std::unique_ptr<Module> M(new Module("TestModule", Context));M->setDataLayout(LLVMTM->createDataLayout());PM.run(*M);// Now check that we can run it twice.AP->addAsmPrinterHandler(AsmPrinter::HandlerInfo(std::unique_ptr<AsmPrinterHandler>(new TestHandler(*this)),"TestTimerName", "TestTimerDesc", "TestGroupName", "TestGroupDesc"));PM.run(*M);return true;}int BeginCount = 0;int EndCount = 0;};TEST_F(AsmPrinterHandlerTest, Basic) {if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))GTEST_SKIP();ASSERT_EQ(BeginCount, 3);ASSERT_EQ(EndCount, 3);}} // end namespace
//===- llvm/unittest/CodeGen/AllocationOrderTest.cpp - AllocationOrder tests =////// 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////===----------------------------------------------------------------------===//#include "../lib/CodeGen/AllocationOrder.h"#include "gtest/gtest.h"using namespace llvm;namespace {std::vector<MCPhysReg> loadOrder(const AllocationOrder &O, unsigned Limit = 0) {std::vector<MCPhysReg> Ret;if (Limit == 0)for (auto R : O)Ret.push_back(R);elsefor (auto I = O.begin(), E = O.getOrderLimitEnd(Limit); I != E; ++I)Ret.push_back(*I);return Ret;}} // namespaceTEST(AllocationOrderTest, Basic) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {4, 5, 6, 7};AllocationOrder O(std::move(Hints), Order, false);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4, 5, 6, 7}), loadOrder(O));}TEST(AllocationOrderTest, Duplicates) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {4, 1, 5, 6};AllocationOrder O(std::move(Hints), Order, false);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4, 5, 6}), loadOrder(O));}TEST(AllocationOrderTest, HardHints) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {4, 5, 6, 7};AllocationOrder O(std::move(Hints), Order, true);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3}), loadOrder(O));}TEST(AllocationOrderTest, LimitsBasic) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {4, 5, 6, 7};AllocationOrder O(std::move(Hints), Order, false);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4, 5, 6, 7}), loadOrder(O, 0));EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4}), loadOrder(O, 1));EXPECT_EQ(O.end(), O.getOrderLimitEnd(0));}TEST(AllocationOrderTest, LimitsDuplicates) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {4, 1, 5, 6};AllocationOrder O(std::move(Hints), Order, false);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4}), loadOrder(O, 1));EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4}), loadOrder(O, 2));EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4, 5}), loadOrder(O, 3));EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4, 5, 6}), loadOrder(O, 4));}TEST(AllocationOrderTest, LimitsHardHints) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {4, 1, 5, 6};AllocationOrder O(std::move(Hints), Order, true);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3}), loadOrder(O, 1));}TEST(AllocationOrderTest, DuplicateIsFirst) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {1, 4, 5, 6};AllocationOrder O(std::move(Hints), Order, false);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4, 5, 6}), loadOrder(O));}TEST(AllocationOrderTest, DuplicateIsFirstWithLimits) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {1, 4, 5, 6};AllocationOrder O(std::move(Hints), Order, false);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3}), loadOrder(O, 1));EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4}), loadOrder(O, 2));EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4, 5}), loadOrder(O, 3));}TEST(AllocationOrderTest, NoHints) {SmallVector<MCPhysReg, 16> Hints;SmallVector<MCPhysReg, 16> Order = {1, 2, 3, 4};AllocationOrder O(std::move(Hints), Order, false);EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3, 4}), loadOrder(O));EXPECT_EQ((std::vector<MCPhysReg>{1, 2}), loadOrder(O, 2));EXPECT_EQ((std::vector<MCPhysReg>{1, 2, 3}), loadOrder(O, 3));}TEST(AllocationOrderTest, IsHintTest) {SmallVector<MCPhysReg, 16> Hints = {1, 2, 3};SmallVector<MCPhysReg, 16> Order = {4, 1, 5, 6};AllocationOrder O(std::move(Hints), Order, false);auto I = O.begin();auto V = *I;EXPECT_TRUE(I.isHint());EXPECT_EQ(V, 1U);++I;EXPECT_TRUE(I.isHint());++I;EXPECT_TRUE(I.isHint());V = *(++I);EXPECT_FALSE(I.isHint());EXPECT_EQ(V, 4U);V = *(++I);EXPECT_TRUE(O.isHint(1));EXPECT_FALSE(I.isHint());EXPECT_EQ(V, 5U);}
//===- llvm/unittest/CodeGen/AMDGPUMetadataTest.cpp -----------------------===////// 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////===----------------------------------------------------------------------===/////// \file/// Test that amdgpu metadata that is added in a pass is read by the asm emitter/// and stored in the ELF.////===----------------------------------------------------------------------===//#include "llvm/IR/LegacyPassManager.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Pass.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"namespace llvm {namespace {// Pass that adds global metadatastruct AddMetadataPass : public ModulePass {std::string PalMDString;public:static char ID;AddMetadataPass(std::string PalMDString): ModulePass(ID), PalMDString(PalMDString) {}bool runOnModule(Module &M) override {auto &Ctx = M.getContext();auto *MD = M.getOrInsertNamedMetadata("amdgpu.pal.metadata.msgpack");auto *PalMD = MDString::get(Ctx, PalMDString);auto *TMD = MDTuple::get(Ctx, {PalMD});MD->addOperand(TMD);return true;}};char AddMetadataPass::ID = 0;} // end anonymous namespaceclass AMDGPUSelectionDAGTest : public testing::Test {protected:static void SetUpTestCase() {InitializeAllTargets();InitializeAllTargetMCs();}void SetUp() override {std::string Error;const Target *T = TargetRegistry::lookupTarget("amdgcn--amdpal", Error);if (!T)GTEST_SKIP();TargetOptions Options;TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine("amdgcn--amdpal", "gfx1010", "", Options, None)));if (!TM)GTEST_SKIP();LLVMContext Context;std::unique_ptr<Module> M(new Module("TestModule", Context));M->setDataLayout(TM->createDataLayout());legacy::PassManager PM;PM.add(new AddMetadataPass(PalMDString));raw_svector_ostream OutStream(Elf);if (TM->addPassesToEmitFile(PM, OutStream, nullptr,CodeGenFileType::CGFT_ObjectFile))report_fatal_error("Target machine cannot emit a file of this type");PM.run(*M);}static std::string PalMDString;LLVMContext Context;std::unique_ptr<LLVMTargetMachine> TM;std::unique_ptr<Module> M;SmallString<1024> Elf;};std::string AMDGPUSelectionDAGTest::PalMDString ="\x81\xB0""amdpal.pipelines\x91\x81\xA4.api\xA6Vulkan";TEST_F(AMDGPUSelectionDAGTest, checkMetadata) {// Check that the string is contained in the ELFEXPECT_NE(Elf.find("Vulkan"), std::string::npos);}} // end namespace llvm
//===- llvm/unittest/CodeGen/AArch64SelectionDAGTest.cpp -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/OptimizationRemarkEmitter.h"#include "llvm/AsmParser/Parser.h"#include "llvm/CodeGen/MachineModuleInfo.h"#include "llvm/CodeGen/SelectionDAG.h"#include "llvm/CodeGen/TargetLowering.h"#include "llvm/MC/TargetRegistry.h"#include "llvm/Support/KnownBits.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/TargetSelect.h"#include "llvm/Target/TargetMachine.h"#include "gtest/gtest.h"namespace llvm {class AArch64SelectionDAGTest : public testing::Test {protected:static void SetUpTestCase() {InitializeAllTargets();InitializeAllTargetMCs();}void SetUp() override {StringRef Assembly = "define void @f() { ret void }";Triple TargetTriple("aarch64--");std::string Error;const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);// FIXME: These tests do not depend on AArch64 specifically, but we have to// initialize a target. A skeleton Target for unittests would allow us to// always run these tests.if (!T)GTEST_SKIP();TargetOptions Options;TM = std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(T->createTargetMachine("AArch64", "", "+sve", Options, None, None,CodeGenOpt::Aggressive)));if (!TM)GTEST_SKIP();SMDiagnostic SMError;M = parseAssemblyString(Assembly, SMError, Context);if (!M)report_fatal_error(SMError.getMessage());M->setDataLayout(TM->createDataLayout());F = M->getFunction("f");if (!F)report_fatal_error("F?");MachineModuleInfo MMI(TM.get());MF = std::make_unique<MachineFunction>(*F, *TM, *TM->getSubtargetImpl(*F), 0,MMI);DAG = std::make_unique<SelectionDAG>(*TM, CodeGenOpt::None);if (!DAG)report_fatal_error("DAG?");OptimizationRemarkEmitter ORE(F);DAG->init(*MF, ORE, nullptr, nullptr, nullptr, nullptr, nullptr);}TargetLoweringBase::LegalizeTypeAction getTypeAction(EVT VT) {return DAG->getTargetLoweringInfo().getTypeAction(Context, VT);}EVT getTypeToTransformTo(EVT VT) {return DAG->getTargetLoweringInfo().getTypeToTransformTo(Context, VT);}LLVMContext Context;std::unique_ptr<LLVMTargetMachine> TM;std::unique_ptr<Module> M;Function *F;std::unique_ptr<MachineFunction> MF;std::unique_ptr<SelectionDAG> DAG;};TEST_F(AArch64SelectionDAGTest, computeKnownBits_ZERO_EXTEND_VECTOR_INREG) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto Int16VT = EVT::getIntegerVT(Context, 16);auto InVecVT = EVT::getVectorVT(Context, Int8VT, 4);auto OutVecVT = EVT::getVectorVT(Context, Int16VT, 2);auto InVec = DAG->getConstant(0, Loc, InVecVT);auto Op = DAG->getNode(ISD::ZERO_EXTEND_VECTOR_INREG, Loc, OutVecVT, InVec);auto DemandedElts = APInt(2, 3);KnownBits Known = DAG->computeKnownBits(Op, DemandedElts);EXPECT_TRUE(Known.isZero());}TEST_F(AArch64SelectionDAGTest, computeKnownBitsSVE_ZERO_EXTEND_VECTOR_INREG) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto Int16VT = EVT::getIntegerVT(Context, 16);auto InVecVT = EVT::getVectorVT(Context, Int8VT, 4, true);auto OutVecVT = EVT::getVectorVT(Context, Int16VT, 2, true);auto InVec = DAG->getConstant(0, Loc, InVecVT);auto Op = DAG->getNode(ISD::ZERO_EXTEND_VECTOR_INREG, Loc, OutVecVT, InVec);auto DemandedElts = APInt(2, 3);KnownBits Known = DAG->computeKnownBits(Op, DemandedElts);// We don't know anything for SVE at the moment.EXPECT_EQ(Known.Zero, APInt(16, 0u));EXPECT_EQ(Known.One, APInt(16, 0u));EXPECT_FALSE(Known.isZero());}TEST_F(AArch64SelectionDAGTest, computeKnownBits_EXTRACT_SUBVECTOR) {SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 3);auto IdxVT = EVT::getIntegerVT(Context, 64);auto Vec = DAG->getConstant(0, Loc, VecVT);auto ZeroIdx = DAG->getConstant(0, Loc, IdxVT);auto Op = DAG->getNode(ISD::EXTRACT_SUBVECTOR, Loc, VecVT, Vec, ZeroIdx);auto DemandedElts = APInt(3, 7);KnownBits Known = DAG->computeKnownBits(Op, DemandedElts);EXPECT_TRUE(Known.isZero());}TEST_F(AArch64SelectionDAGTest, ComputeNumSignBits_SIGN_EXTEND_VECTOR_INREG) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto Int16VT = EVT::getIntegerVT(Context, 16);auto InVecVT = EVT::getVectorVT(Context, Int8VT, 4);auto OutVecVT = EVT::getVectorVT(Context, Int16VT, 2);auto InVec = DAG->getConstant(1, Loc, InVecVT);auto Op = DAG->getNode(ISD::SIGN_EXTEND_VECTOR_INREG, Loc, OutVecVT, InVec);auto DemandedElts = APInt(2, 3);EXPECT_EQ(DAG->ComputeNumSignBits(Op, DemandedElts), 15u);}TEST_F(AArch64SelectionDAGTest, ComputeNumSignBitsSVE_SIGN_EXTEND_VECTOR_INREG) {SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto Int16VT = EVT::getIntegerVT(Context, 16);auto InVecVT = EVT::getVectorVT(Context, Int8VT, 4, /*IsScalable=*/true);auto OutVecVT = EVT::getVectorVT(Context, Int16VT, 2, /*IsScalable=*/true);auto InVec = DAG->getConstant(1, Loc, InVecVT);auto Op = DAG->getNode(ISD::SIGN_EXTEND_VECTOR_INREG, Loc, OutVecVT, InVec);auto DemandedElts = APInt(2, 3);EXPECT_EQ(DAG->ComputeNumSignBits(Op, DemandedElts), 1u);}TEST_F(AArch64SelectionDAGTest, ComputeNumSignBits_EXTRACT_SUBVECTOR) {SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 3);auto IdxVT = EVT::getIntegerVT(Context, 64);auto Vec = DAG->getConstant(1, Loc, VecVT);auto ZeroIdx = DAG->getConstant(0, Loc, IdxVT);auto Op = DAG->getNode(ISD::EXTRACT_SUBVECTOR, Loc, VecVT, Vec, ZeroIdx);auto DemandedElts = APInt(3, 7);EXPECT_EQ(DAG->ComputeNumSignBits(Op, DemandedElts), 7u);}TEST_F(AArch64SelectionDAGTest, SimplifyDemandedVectorElts_EXTRACT_SUBVECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 3);auto IdxVT = EVT::getIntegerVT(Context, 64);auto Vec = DAG->getConstant(1, Loc, VecVT);auto ZeroIdx = DAG->getConstant(0, Loc, IdxVT);auto Op = DAG->getNode(ISD::EXTRACT_SUBVECTOR, Loc, VecVT, Vec, ZeroIdx);auto DemandedElts = APInt(3, 7);auto KnownUndef = APInt(3, 0);auto KnownZero = APInt(3, 0);TargetLowering::TargetLoweringOpt TLO(*DAG, false, false);EXPECT_EQ(TL.SimplifyDemandedVectorElts(Op, DemandedElts, KnownUndef,KnownZero, TLO),false);}TEST_F(AArch64SelectionDAGTest, SimplifyDemandedBitsNEON) {TargetLowering TL(*TM);SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto InVecVT = EVT::getVectorVT(Context, Int8VT, 16);SDValue UnknownOp = DAG->getRegister(0, InVecVT);SDValue Mask1S = DAG->getConstant(0x8A, Loc, Int8VT);SDValue Mask1V = DAG->getSplatBuildVector(InVecVT, Loc, Mask1S);SDValue N0 = DAG->getNode(ISD::AND, Loc, InVecVT, Mask1V, UnknownOp);SDValue Mask2S = DAG->getConstant(0x55, Loc, Int8VT);SDValue Mask2V = DAG->getSplatBuildVector(InVecVT, Loc, Mask2S);SDValue Op = DAG->getNode(ISD::AND, Loc, InVecVT, N0, Mask2V);// N0 = ?000?0?0// Mask2V = 01010101// =>// Known.Zero = 00100000 (0xAA)KnownBits Known;APInt DemandedBits = APInt(8, 0xFF);TargetLowering::TargetLoweringOpt TLO(*DAG, false, false);EXPECT_TRUE(TL.SimplifyDemandedBits(Op, DemandedBits, Known, TLO));EXPECT_EQ(Known.Zero, APInt(8, 0xAA));}TEST_F(AArch64SelectionDAGTest, SimplifyDemandedBitsSVE) {TargetLowering TL(*TM);SDLoc Loc;auto Int8VT = EVT::getIntegerVT(Context, 8);auto InVecVT = EVT::getVectorVT(Context, Int8VT, 16, /*IsScalable=*/true);SDValue UnknownOp = DAG->getRegister(0, InVecVT);SDValue Mask1S = DAG->getConstant(0x8A, Loc, Int8VT);SDValue Mask1V = DAG->getSplatVector(InVecVT, Loc, Mask1S);SDValue N0 = DAG->getNode(ISD::AND, Loc, InVecVT, Mask1V, UnknownOp);SDValue Mask2S = DAG->getConstant(0x55, Loc, Int8VT);SDValue Mask2V = DAG->getSplatVector(InVecVT, Loc, Mask2S);SDValue Op = DAG->getNode(ISD::AND, Loc, InVecVT, N0, Mask2V);KnownBits Known;APInt DemandedBits = APInt(8, 0xFF);TargetLowering::TargetLoweringOpt TLO(*DAG, false, false);EXPECT_FALSE(TL.SimplifyDemandedBits(Op, DemandedBits, Known, TLO));EXPECT_EQ(Known.Zero, APInt(8, 0));}// Piggy-backing on the AArch64 tests to verify SelectionDAG::computeKnownBits.TEST_F(AArch64SelectionDAGTest, ComputeKnownBits_ADD) {SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto UnknownOp = DAG->getRegister(0, IntVT);auto Mask = DAG->getConstant(0x8A, Loc, IntVT);auto N0 = DAG->getNode(ISD::AND, Loc, IntVT, Mask, UnknownOp);auto N1 = DAG->getConstant(0x55, Loc, IntVT);auto Op = DAG->getNode(ISD::ADD, Loc, IntVT, N0, N1);// N0 = ?000?0?0// N1 = 01010101// =>// Known.One = 01010101 (0x55)// Known.Zero = 00100000 (0x20)KnownBits Known = DAG->computeKnownBits(Op);EXPECT_EQ(Known.Zero, APInt(8, 0x20));EXPECT_EQ(Known.One, APInt(8, 0x55));}// Piggy-backing on the AArch64 tests to verify SelectionDAG::computeKnownBits.TEST_F(AArch64SelectionDAGTest, ComputeKnownBits_SUB) {SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto N0 = DAG->getConstant(0x55, Loc, IntVT);auto UnknownOp = DAG->getRegister(0, IntVT);auto Mask = DAG->getConstant(0x2e, Loc, IntVT);auto N1 = DAG->getNode(ISD::AND, Loc, IntVT, Mask, UnknownOp);auto Op = DAG->getNode(ISD::SUB, Loc, IntVT, N0, N1);// N0 = 01010101// N1 = 00?0???0// =>// Known.One = 00000001 (0x1)// Known.Zero = 10000000 (0x80)KnownBits Known = DAG->computeKnownBits(Op);EXPECT_EQ(Known.Zero, APInt(8, 0x80));EXPECT_EQ(Known.One, APInt(8, 0x1));}TEST_F(AArch64SelectionDAGTest, isSplatValue_Fixed_BUILD_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, false);// Create a BUILD_VECTORSDValue Op = DAG->getConstant(1, Loc, VecVT);EXPECT_EQ(Op->getOpcode(), ISD::BUILD_VECTOR);EXPECT_TRUE(DAG->isSplatValue(Op, /*AllowUndefs=*/false));APInt UndefElts;APInt DemandedElts;EXPECT_FALSE(DAG->isSplatValue(Op, DemandedElts, UndefElts));// Width=16, Mask=3DemandedElts = APInt(16, 3);EXPECT_TRUE(DAG->isSplatValue(Op, DemandedElts, UndefElts));}TEST_F(AArch64SelectionDAGTest, isSplatValue_Fixed_ADD_of_BUILD_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, false);// Should create BUILD_VECTORsSDValue Val1 = DAG->getConstant(1, Loc, VecVT);SDValue Val2 = DAG->getConstant(3, Loc, VecVT);EXPECT_EQ(Val1->getOpcode(), ISD::BUILD_VECTOR);SDValue Op = DAG->getNode(ISD::ADD, Loc, VecVT, Val1, Val2);EXPECT_TRUE(DAG->isSplatValue(Op, /*AllowUndefs=*/false));APInt UndefElts;APInt DemandedElts;EXPECT_FALSE(DAG->isSplatValue(Op, DemandedElts, UndefElts));// Width=16, Mask=3DemandedElts = APInt(16, 3);EXPECT_TRUE(DAG->isSplatValue(Op, DemandedElts, UndefElts));}TEST_F(AArch64SelectionDAGTest, isSplatValue_Scalable_SPLAT_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, true);// Create a SPLAT_VECTORSDValue Op = DAG->getConstant(1, Loc, VecVT);EXPECT_EQ(Op->getOpcode(), ISD::SPLAT_VECTOR);EXPECT_TRUE(DAG->isSplatValue(Op, /*AllowUndefs=*/false));APInt UndefElts;APInt DemandedElts;EXPECT_TRUE(DAG->isSplatValue(Op, DemandedElts, UndefElts));// Width=16, Mask=3. These bits should be ignored.DemandedElts = APInt(16, 3);EXPECT_TRUE(DAG->isSplatValue(Op, DemandedElts, UndefElts));}TEST_F(AArch64SelectionDAGTest, isSplatValue_Scalable_ADD_of_SPLAT_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, true);// Should create SPLAT_VECTORSSDValue Val1 = DAG->getConstant(1, Loc, VecVT);SDValue Val2 = DAG->getConstant(3, Loc, VecVT);EXPECT_EQ(Val1->getOpcode(), ISD::SPLAT_VECTOR);SDValue Op = DAG->getNode(ISD::ADD, Loc, VecVT, Val1, Val2);EXPECT_TRUE(DAG->isSplatValue(Op, /*AllowUndefs=*/false));APInt UndefElts;APInt DemandedElts;EXPECT_TRUE(DAG->isSplatValue(Op, DemandedElts, UndefElts));// Width=16, Mask=3. These bits should be ignored.DemandedElts = APInt(16, 3);EXPECT_TRUE(DAG->isSplatValue(Op, DemandedElts, UndefElts));}TEST_F(AArch64SelectionDAGTest, getSplatSourceVector_Fixed_BUILD_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, false);// Create a BUILD_VECTORSDValue Op = DAG->getConstant(1, Loc, VecVT);EXPECT_EQ(Op->getOpcode(), ISD::BUILD_VECTOR);int SplatIdx = -1;EXPECT_EQ(DAG->getSplatSourceVector(Op, SplatIdx), Op);EXPECT_EQ(SplatIdx, 0);}TEST_F(AArch64SelectionDAGTest, getSplatSourceVector_Fixed_ADD_of_BUILD_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, false);// Should create BUILD_VECTORsSDValue Val1 = DAG->getConstant(1, Loc, VecVT);SDValue Val2 = DAG->getConstant(3, Loc, VecVT);EXPECT_EQ(Val1->getOpcode(), ISD::BUILD_VECTOR);SDValue Op = DAG->getNode(ISD::ADD, Loc, VecVT, Val1, Val2);int SplatIdx = -1;EXPECT_EQ(DAG->getSplatSourceVector(Op, SplatIdx), Op);EXPECT_EQ(SplatIdx, 0);}TEST_F(AArch64SelectionDAGTest, getSplatSourceVector_Scalable_SPLAT_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, true);// Create a SPLAT_VECTORSDValue Op = DAG->getConstant(1, Loc, VecVT);EXPECT_EQ(Op->getOpcode(), ISD::SPLAT_VECTOR);int SplatIdx = -1;EXPECT_EQ(DAG->getSplatSourceVector(Op, SplatIdx), Op);EXPECT_EQ(SplatIdx, 0);}TEST_F(AArch64SelectionDAGTest, getSplatSourceVector_Scalable_ADD_of_SPLAT_VECTOR) {TargetLowering TL(*TM);SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, IntVT, 16, true);// Should create SPLAT_VECTORSSDValue Val1 = DAG->getConstant(1, Loc, VecVT);SDValue Val2 = DAG->getConstant(3, Loc, VecVT);EXPECT_EQ(Val1->getOpcode(), ISD::SPLAT_VECTOR);SDValue Op = DAG->getNode(ISD::ADD, Loc, VecVT, Val1, Val2);int SplatIdx = -1;EXPECT_EQ(DAG->getSplatSourceVector(Op, SplatIdx), Op);EXPECT_EQ(SplatIdx, 0);}TEST_F(AArch64SelectionDAGTest, getRepeatedSequence_Patterns) {TargetLowering TL(*TM);SDLoc Loc;unsigned NumElts = 16;MVT IntVT = MVT::i8;MVT VecVT = MVT::getVectorVT(IntVT, NumElts);// Base scalar constants.SDValue Val0 = DAG->getConstant(0, Loc, IntVT);SDValue Val1 = DAG->getConstant(1, Loc, IntVT);SDValue Val2 = DAG->getConstant(2, Loc, IntVT);SDValue Val3 = DAG->getConstant(3, Loc, IntVT);SDValue UndefVal = DAG->getUNDEF(IntVT);// Build some repeating sequences.SmallVector<SDValue, 16> Pattern1111, Pattern1133, Pattern0123;for(int I = 0; I != 4; ++I) {Pattern1111.append(4, Val1);Pattern1133.append(2, Val1);Pattern1133.append(2, Val3);Pattern0123.push_back(Val0);Pattern0123.push_back(Val1);Pattern0123.push_back(Val2);Pattern0123.push_back(Val3);}// Build a non-pow2 repeating sequence.SmallVector<SDValue, 16> Pattern022;Pattern022.push_back(Val0);Pattern022.append(2, Val2);Pattern022.push_back(Val0);Pattern022.append(2, Val2);Pattern022.push_back(Val0);Pattern022.append(2, Val2);Pattern022.push_back(Val0);Pattern022.append(2, Val2);Pattern022.push_back(Val0);Pattern022.append(2, Val2);Pattern022.push_back(Val0);// Build a non-repeating sequence.SmallVector<SDValue, 16> Pattern1_3;Pattern1_3.append(8, Val1);Pattern1_3.append(8, Val3);// Add some undefs to make it trickier.Pattern1111[1] = Pattern1111[2] = Pattern1111[15] = UndefVal;Pattern1133[0] = Pattern1133[2] = UndefVal;auto *BV1111 =cast<BuildVectorSDNode>(DAG->getBuildVector(VecVT, Loc, Pattern1111));auto *BV1133 =cast<BuildVectorSDNode>(DAG->getBuildVector(VecVT, Loc, Pattern1133));auto *BV0123=cast<BuildVectorSDNode>(DAG->getBuildVector(VecVT, Loc, Pattern0123));auto *BV022 =cast<BuildVectorSDNode>(DAG->getBuildVector(VecVT, Loc, Pattern022));auto *BV1_3 =cast<BuildVectorSDNode>(DAG->getBuildVector(VecVT, Loc, Pattern1_3));// Check for sequences.SmallVector<SDValue, 16> Seq1111, Seq1133, Seq0123, Seq022, Seq1_3;BitVector Undefs1111, Undefs1133, Undefs0123, Undefs022, Undefs1_3;EXPECT_TRUE(BV1111->getRepeatedSequence(Seq1111, &Undefs1111));EXPECT_EQ(Undefs1111.count(), 3u);EXPECT_EQ(Seq1111.size(), 1u);EXPECT_EQ(Seq1111[0], Val1);EXPECT_TRUE(BV1133->getRepeatedSequence(Seq1133, &Undefs1133));EXPECT_EQ(Undefs1133.count(), 2u);EXPECT_EQ(Seq1133.size(), 4u);EXPECT_EQ(Seq1133[0], Val1);EXPECT_EQ(Seq1133[1], Val1);EXPECT_EQ(Seq1133[2], Val3);EXPECT_EQ(Seq1133[3], Val3);EXPECT_TRUE(BV0123->getRepeatedSequence(Seq0123, &Undefs0123));EXPECT_EQ(Undefs0123.count(), 0u);EXPECT_EQ(Seq0123.size(), 4u);EXPECT_EQ(Seq0123[0], Val0);EXPECT_EQ(Seq0123[1], Val1);EXPECT_EQ(Seq0123[2], Val2);EXPECT_EQ(Seq0123[3], Val3);EXPECT_FALSE(BV022->getRepeatedSequence(Seq022, &Undefs022));EXPECT_FALSE(BV1_3->getRepeatedSequence(Seq1_3, &Undefs1_3));// Try again with DemandedElts masks.APInt Mask1111_0 = APInt::getOneBitSet(NumElts, 0);EXPECT_TRUE(BV1111->getRepeatedSequence(Mask1111_0, Seq1111, &Undefs1111));EXPECT_EQ(Undefs1111.count(), 0u);EXPECT_EQ(Seq1111.size(), 1u);EXPECT_EQ(Seq1111[0], Val1);APInt Mask1111_1 = APInt::getOneBitSet(NumElts, 2);EXPECT_TRUE(BV1111->getRepeatedSequence(Mask1111_1, Seq1111, &Undefs1111));EXPECT_EQ(Undefs1111.count(), 1u);EXPECT_EQ(Seq1111.size(), 1u);EXPECT_EQ(Seq1111[0], UndefVal);APInt Mask0123 = APInt(NumElts, 0x7777);EXPECT_TRUE(BV0123->getRepeatedSequence(Mask0123, Seq0123, &Undefs0123));EXPECT_EQ(Undefs0123.count(), 0u);EXPECT_EQ(Seq0123.size(), 4u);EXPECT_EQ(Seq0123[0], Val0);EXPECT_EQ(Seq0123[1], Val1);EXPECT_EQ(Seq0123[2], Val2);EXPECT_EQ(Seq0123[3], SDValue());APInt Mask1_3 = APInt::getHighBitsSet(16, 8);EXPECT_TRUE(BV1_3->getRepeatedSequence(Mask1_3, Seq1_3, &Undefs1_3));EXPECT_EQ(Undefs1_3.count(), 0u);EXPECT_EQ(Seq1_3.size(), 1u);EXPECT_EQ(Seq1_3[0], Val3);}TEST_F(AArch64SelectionDAGTest, getTypeConversion_SplitScalableMVT) {MVT VT = MVT::nxv4i64;EXPECT_EQ(getTypeAction(VT), TargetLoweringBase::TypeSplitVector);ASSERT_TRUE(getTypeToTransformTo(VT).isScalableVector());}TEST_F(AArch64SelectionDAGTest, getTypeConversion_PromoteScalableMVT) {MVT VT = MVT::nxv2i32;EXPECT_EQ(getTypeAction(VT), TargetLoweringBase::TypePromoteInteger);ASSERT_TRUE(getTypeToTransformTo(VT).isScalableVector());}TEST_F(AArch64SelectionDAGTest, getTypeConversion_NoScalarizeMVT_nxv1f32) {MVT VT = MVT::nxv1f32;EXPECT_NE(getTypeAction(VT), TargetLoweringBase::TypeScalarizeVector);ASSERT_TRUE(getTypeToTransformTo(VT).isScalableVector());}TEST_F(AArch64SelectionDAGTest, getTypeConversion_SplitScalableEVT) {EVT VT = EVT::getVectorVT(Context, MVT::i64, 256, true);EXPECT_EQ(getTypeAction(VT), TargetLoweringBase::TypeSplitVector);EXPECT_EQ(getTypeToTransformTo(VT), VT.getHalfNumVectorElementsVT(Context));}TEST_F(AArch64SelectionDAGTest, getTypeConversion_WidenScalableEVT) {EVT FromVT = EVT::getVectorVT(Context, MVT::i64, 6, true);EVT ToVT = EVT::getVectorVT(Context, MVT::i64, 8, true);EXPECT_EQ(getTypeAction(FromVT), TargetLoweringBase::TypeWidenVector);EXPECT_EQ(getTypeToTransformTo(FromVT), ToVT);}TEST_F(AArch64SelectionDAGTest,getTypeConversion_ScalarizeScalableEVT_nxv1f128) {EVT VT = EVT::getVectorVT(Context, MVT::f128, ElementCount::getScalable(1));EXPECT_EQ(getTypeAction(VT), TargetLoweringBase::TypeScalarizeScalableVector);EXPECT_EQ(getTypeToTransformTo(VT), MVT::f128);}TEST_F(AArch64SelectionDAGTest, TestFold_STEP_VECTOR) {SDLoc Loc;auto IntVT = EVT::getIntegerVT(Context, 8);auto VecVT = EVT::getVectorVT(Context, MVT::i8, 16, true);// Should create SPLAT_VECTORSDValue Zero = DAG->getConstant(0, Loc, IntVT);SDValue Op = DAG->getNode(ISD::STEP_VECTOR, Loc, VecVT, Zero);EXPECT_EQ(Op.getOpcode(), ISD::SPLAT_VECTOR);}} // end namespace llvm
add_custom_target(UnitTests)set_target_properties(UnitTests PROPERTIES FOLDER "Tests/UnitTests")function(add_llvm_unittest test_dirname)add_unittest(UnitTests ${test_dirname} ${ARGN})endfunction()function(add_llvm_unittest_with_input_files test_dirname)add_unittest_with_input_files(UnitTests ${test_dirname} ${ARGN})endfunction()# The target unittests may test APIs that aren't exported in libLLVM.so, so# we need to always link against the static libraries.function(add_llvm_target_unittest test_dir_name)add_llvm_unittest(${test_dir_name} DISABLE_LLVM_LINK_LLVM_DYLIB ${ARGN})endfunction()add_subdirectory(ADT)add_subdirectory(Analysis)add_subdirectory(AsmParser)add_subdirectory(BinaryFormat)add_subdirectory(Bitcode)add_subdirectory(Bitstream)add_subdirectory(CodeGen)add_subdirectory(DebugInfo)add_subdirectory(Debuginfod)add_subdirectory(Demangle)add_subdirectory(ExecutionEngine)add_subdirectory(FileCheck)add_subdirectory(Frontend)add_subdirectory(FuzzMutate)add_subdirectory(InterfaceStub)add_subdirectory(IR)add_subdirectory(LineEditor)add_subdirectory(Linker)add_subdirectory(MC)add_subdirectory(MI)add_subdirectory(MIR)add_subdirectory(ObjCopy)add_subdirectory(Object)add_subdirectory(ObjectYAML)add_subdirectory(Option)add_subdirectory(Remarks)add_subdirectory(Passes)add_subdirectory(ProfileData)add_subdirectory(Support)add_subdirectory(TableGen)add_subdirectory(Target)add_subdirectory(Testing)add_subdirectory(TextAPI)add_subdirectory(Transforms)add_subdirectory(XRay)add_subdirectory(tools)
set(LLVM_LINK_COMPONENTSBitstreamReader)add_llvm_unittest(BitstreamTestsBitstreamReaderTest.cppBitstreamWriterTest.cpp)
//===- BitstreamWriterTest.cpp - Tests for BitstreamWriter ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Bitstream/BitstreamWriter.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SmallString.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(BitstreamWriterTest, emitBlob) {SmallString<64> Buffer;BitstreamWriter W(Buffer);W.emitBlob("str", /* ShouldEmitSize */ false);EXPECT_EQ(StringRef("str\0", 4), Buffer);}TEST(BitstreamWriterTest, emitBlobWithSize) {SmallString<64> Buffer;{BitstreamWriter W(Buffer);W.emitBlob("str");}SmallString<64> Expected;{BitstreamWriter W(Expected);W.EmitVBR(3, 6);W.FlushToWord();W.Emit('s', 8);W.Emit('t', 8);W.Emit('r', 8);W.Emit(0, 8);}EXPECT_EQ(Expected.str(), Buffer);}TEST(BitstreamWriterTest, emitBlobEmpty) {SmallString<64> Buffer;BitstreamWriter W(Buffer);W.emitBlob("", /* ShouldEmitSize */ false);EXPECT_EQ(StringRef(""), Buffer);}TEST(BitstreamWriterTest, emitBlob4ByteAligned) {SmallString<64> Buffer;BitstreamWriter W(Buffer);W.emitBlob("str0", /* ShouldEmitSize */ false);EXPECT_EQ(StringRef("str0"), Buffer);}} // end namespace
//===- BitstreamReaderTest.cpp - Tests for BitstreamReader ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Bitstream/BitstreamReader.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Bitstream/BitstreamWriter.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(BitstreamReaderTest, AtEndOfStream) {uint8_t Bytes[4] = {0x00, 0x01, 0x02, 0x03};BitstreamCursor Cursor(Bytes);EXPECT_FALSE(Cursor.AtEndOfStream());Expected<SimpleBitstreamCursor::word_t> MaybeRead = Cursor.Read(8);EXPECT_TRUE((bool)MaybeRead);EXPECT_FALSE(Cursor.AtEndOfStream());MaybeRead = Cursor.Read(24);EXPECT_TRUE((bool)MaybeRead);EXPECT_TRUE(Cursor.AtEndOfStream());EXPECT_FALSE(Cursor.JumpToBit(0));EXPECT_FALSE(Cursor.AtEndOfStream());EXPECT_FALSE(Cursor.JumpToBit(32));EXPECT_TRUE(Cursor.AtEndOfStream());}TEST(BitstreamReaderTest, AtEndOfStreamJump) {uint8_t Bytes[4] = {0x00, 0x01, 0x02, 0x03};BitstreamCursor Cursor(Bytes);EXPECT_FALSE(Cursor.JumpToBit(32));EXPECT_TRUE(Cursor.AtEndOfStream());}TEST(BitstreamReaderTest, AtEndOfStreamEmpty) {BitstreamCursor Cursor(ArrayRef<uint8_t>{});EXPECT_TRUE(Cursor.AtEndOfStream());}TEST(BitstreamReaderTest, getCurrentByteNo) {uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03};SimpleBitstreamCursor Cursor(Bytes);for (unsigned I = 0, E = 32; I != E; ++I) {EXPECT_EQ(I / 8, Cursor.getCurrentByteNo());Expected<SimpleBitstreamCursor::word_t> MaybeRead = Cursor.Read(1);EXPECT_TRUE((bool)MaybeRead);}EXPECT_EQ(4u, Cursor.getCurrentByteNo());}TEST(BitstreamReaderTest, getPointerToByte) {uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};SimpleBitstreamCursor Cursor(Bytes);for (unsigned I = 0, E = 8; I != E; ++I) {EXPECT_EQ(Bytes + I, Cursor.getPointerToByte(I, 1));}}TEST(BitstreamReaderTest, getPointerToBit) {uint8_t Bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};SimpleBitstreamCursor Cursor(Bytes);for (unsigned I = 0, E = 8; I != E; ++I) {EXPECT_EQ(Bytes + I, Cursor.getPointerToBit(I * 8, 1));}}TEST(BitstreamReaderTest, readRecordWithBlobWhileStreaming) {SmallVector<uint8_t, 1> BlobData;for (unsigned I = 0, E = 1024; I != E; ++I)BlobData.push_back(I);// Try a bunch of different sizes.const unsigned Magic = 0x12345678;const unsigned BlockID = bitc::FIRST_APPLICATION_BLOCKID;const unsigned RecordID = 1;for (unsigned I = 0, BlobSize = 0, E = BlobData.size(); BlobSize < E;BlobSize += ++I) {StringRef BlobIn((const char *)BlobData.begin(), BlobSize);// Write the bitcode.SmallVector<char, 1> Buffer;unsigned AbbrevID;{BitstreamWriter Stream(Buffer);Stream.Emit(Magic, 32);Stream.EnterSubblock(BlockID, 3);auto Abbrev = std::make_shared<BitCodeAbbrev>();Abbrev->Add(BitCodeAbbrevOp(RecordID));Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));AbbrevID = Stream.EmitAbbrev(std::move(Abbrev));unsigned Record[] = {RecordID};Stream.EmitRecordWithBlob(AbbrevID, makeArrayRef(Record), BlobIn);Stream.ExitBlock();}// Stream the buffer into the reader.BitstreamCursor Stream(ArrayRef<uint8_t>((const uint8_t *)Buffer.begin(), Buffer.size()));// Header. Included in test so that we can run llvm-bcanalyzer to debug// when there are problems.Expected<SimpleBitstreamCursor::word_t> MaybeRead = Stream.Read(32);ASSERT_TRUE((bool)MaybeRead);ASSERT_EQ(Magic, MaybeRead.get());// Block.Expected<BitstreamEntry> MaybeEntry =Stream.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs);ASSERT_TRUE((bool)MaybeEntry);BitstreamEntry Entry = MaybeEntry.get();ASSERT_EQ(BitstreamEntry::SubBlock, Entry.Kind);ASSERT_EQ(BlockID, Entry.ID);ASSERT_FALSE(Stream.EnterSubBlock(BlockID));// Abbreviation.MaybeEntry = Stream.advance();ASSERT_TRUE((bool)MaybeEntry);Entry = MaybeEntry.get();ASSERT_EQ(BitstreamEntry::Record, Entry.Kind);ASSERT_EQ(AbbrevID, Entry.ID);// Record.StringRef BlobOut;SmallVector<uint64_t, 1> Record;Expected<unsigned> MaybeRecord =Stream.readRecord(Entry.ID, Record, &BlobOut);ASSERT_TRUE((bool)MaybeRecord);ASSERT_EQ(RecordID, MaybeRecord.get());EXPECT_TRUE(Record.empty());EXPECT_EQ(BlobIn, BlobOut);}}TEST(BitstreamReaderTest, shortRead) {uint8_t Bytes[] = {8, 7, 6, 5, 4, 3, 2, 1};for (unsigned I = 1; I != 8; ++I) {SimpleBitstreamCursor Cursor(ArrayRef<uint8_t>(Bytes, I));Expected<SimpleBitstreamCursor::word_t> MaybeRead = Cursor.Read(8);ASSERT_TRUE((bool)MaybeRead);EXPECT_EQ(8ull, MaybeRead.get());}}static_assert(std::is_trivially_copyable<BitCodeAbbrevOp>::value,"trivially copyable");} // end anonymous namespace
//===- DataLayoutUpgradeTest.cpp - Tests for DataLayout upgrades ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/IR/AutoUpgrade.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(DataLayoutUpgradeTest, ValidDataLayoutUpgrade) {std::string DL1 =UpgradeDataLayoutString("e-m:e-p:32:32-i64:64-f80:128-n8:16:32:64-S128","x86_64-unknown-linux-gnu");std::string DL2 = UpgradeDataLayoutString("e-m:w-p:32:32-i64:64-f80:32-n8:16:32-S32", "i686-pc-windows-msvc");std::string DL3 = UpgradeDataLayoutString("e-m:o-i64:64-i128:128-n32:64-S128","x86_64-apple-macosx");EXPECT_EQ(DL1, "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64""-f80:128-n8:16:32:64-S128");EXPECT_EQ(DL2, "e-m:w-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64""-f80:128-n8:16:32-S32");EXPECT_EQ(DL3, "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128""-n32:64-S128");// Check that AMDGPU targets add -G1 if it's not present.EXPECT_EQ(UpgradeDataLayoutString("e-p:32:32", "r600"), "e-p:32:32-G1");EXPECT_EQ(UpgradeDataLayoutString("e-p:64:64", "amdgcn"), "e-p:64:64-G1");}TEST(DataLayoutUpgradeTest, NoDataLayoutUpgrade) {std::string DL1 = UpgradeDataLayoutString("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32""-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128""-n8:16:32:64-S128","x86_64-unknown-linux-gnu");std::string DL2 = UpgradeDataLayoutString("e-p:32:32", "i686-apple-darwin9");std::string DL3 = UpgradeDataLayoutString("e-m:e-i64:64-n32:64","powerpc64le-unknown-linux-gnu");std::string DL4 =UpgradeDataLayoutString("e-m:o-i64:64-i128:128-n32:64-S128", "aarch64--");EXPECT_EQ(DL1, "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64""-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64""-f80:128:128-n8:16:32:64-S128");EXPECT_EQ(DL2, "e-p:32:32");EXPECT_EQ(DL3, "e-m:e-i64:64-n32:64");EXPECT_EQ(DL4, "e-m:o-i64:64-i128:128-n32:64-S128");// Check that AMDGPU targets don't add -G1 if there is already a -G flag.EXPECT_EQ(UpgradeDataLayoutString("e-p:32:32-G2", "r600"), "e-p:32:32-G2");EXPECT_EQ(UpgradeDataLayoutString("G2", "r600"), "G2");EXPECT_EQ(UpgradeDataLayoutString("e-p:64:64-G2", "amdgcn"), "e-p:64:64-G2");EXPECT_EQ(UpgradeDataLayoutString("G2-e-p:64:64", "amdgcn"), "G2-e-p:64:64");EXPECT_EQ(UpgradeDataLayoutString("e-p:64:64-G0", "amdgcn"), "e-p:64:64-G0");}TEST(DataLayoutUpgradeTest, EmptyDataLayout) {std::string DL1 = UpgradeDataLayoutString("", "x86_64-unknown-linux-gnu");std::string DL2 = UpgradeDataLayoutString("e-m:e-p:32:32-i64:64-f80:128-n8:16:32:64-S128", "");EXPECT_EQ(DL1, "");EXPECT_EQ(DL2, "e-m:e-p:32:32-i64:64-f80:128-n8:16:32:64-S128");// Check that AMDGPU targets add G1 if it's not present.EXPECT_EQ(UpgradeDataLayoutString("", "r600"), "G1");EXPECT_EQ(UpgradeDataLayoutString("", "amdgcn"), "G1");}} // end namespace
set(LLVM_LINK_COMPONENTSAsmParserBitReaderBitWriterCoreSupport)add_llvm_unittest(BitcodeTestsBitReaderTest.cppDataLayoutUpgradeTest.cpp)
//===- llvm/unittest/Bitcode/BitReaderTest.cpp - Tests for BitReader ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SmallString.h"#include "llvm/AsmParser/Parser.h"#include "llvm/Bitcode/BitcodeReader.h"#include "llvm/Bitcode/BitcodeWriter.h"#include "llvm/IR/InstrTypes.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/Debug.h"#include "llvm/Support/Error.h"#include "llvm/Support/MemoryBuffer.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {std::unique_ptr<Module> parseAssembly(LLVMContext &Context,const char *Assembly) {SMDiagnostic Error;std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);std::string ErrMsg;raw_string_ostream OS(ErrMsg);Error.print("", OS);// A failure here means that the test itself is buggy.if (!M)report_fatal_error(OS.str().c_str());return M;}static void writeModuleToBuffer(std::unique_ptr<Module> Mod,SmallVectorImpl<char> &Buffer) {raw_svector_ostream OS(Buffer);WriteBitcodeToFile(*Mod, OS);}static std::unique_ptr<Module> getLazyModuleFromAssembly(LLVMContext &Context,SmallString<1024> &Mem,const char *Assembly) {writeModuleToBuffer(parseAssembly(Context, Assembly), Mem);Expected<std::unique_ptr<Module>> ModuleOrErr =getLazyBitcodeModule(MemoryBufferRef(Mem.str(), "test"), Context);if (!ModuleOrErr)report_fatal_error("Could not parse bitcode module");return std::move(ModuleOrErr.get());}// Tests that lazy evaluation can parse functions out of order.TEST(BitReaderTest, MaterializeFunctionsOutOfOrder) {SmallString<1024> Mem;LLVMContext Context;std::unique_ptr<Module> M = getLazyModuleFromAssembly(Context, Mem, "define void @f() {\n"" unreachable\n""}\n""define void @g() {\n"" unreachable\n""}\n""define void @h() {\n"" unreachable\n""}\n""define void @j() {\n"" unreachable\n""}\n");EXPECT_FALSE(verifyModule(*M, &dbgs()));Function *F = M->getFunction("f");Function *G = M->getFunction("g");Function *H = M->getFunction("h");Function *J = M->getFunction("j");// Initially all functions are not materialized (no basic blocks).EXPECT_TRUE(F->empty());EXPECT_TRUE(G->empty());EXPECT_TRUE(H->empty());EXPECT_TRUE(J->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));// Materialize h.ASSERT_FALSE(H->materialize());EXPECT_TRUE(F->empty());EXPECT_TRUE(G->empty());EXPECT_FALSE(H->empty());EXPECT_TRUE(J->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));// Materialize g.ASSERT_FALSE(G->materialize());EXPECT_TRUE(F->empty());EXPECT_FALSE(G->empty());EXPECT_FALSE(H->empty());EXPECT_TRUE(J->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));// Materialize j.ASSERT_FALSE(J->materialize());EXPECT_TRUE(F->empty());EXPECT_FALSE(G->empty());EXPECT_FALSE(H->empty());EXPECT_FALSE(J->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));// Materialize f.ASSERT_FALSE(F->materialize());EXPECT_FALSE(F->empty());EXPECT_FALSE(G->empty());EXPECT_FALSE(H->empty());EXPECT_FALSE(J->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));}TEST(BitReaderTest, MaterializeFunctionsStrictFP) {SmallString<1024> Mem;LLVMContext Context;std::unique_ptr<Module> M = getLazyModuleFromAssembly(Context, Mem, "define double @foo(double %a) {\n"" %result = call double @bar(double %a) strictfp\n"" ret double %result\n""}\n""declare double @bar(double)\n");Function *Foo = M->getFunction("foo");ASSERT_FALSE(Foo->materialize());EXPECT_FALSE(Foo->empty());for (auto &BB : *Foo) {auto It = BB.begin();while (It != BB.end()) {Instruction &I = *It;++It;if (auto *Call = dyn_cast<CallBase>(&I)) {EXPECT_FALSE(Call->isStrictFP());EXPECT_TRUE(Call->isNoBuiltin());}}}EXPECT_FALSE(verifyModule(*M, &dbgs()));}TEST(BitReaderTest, MaterializeConstrainedFPStrictFP) {SmallString<1024> Mem;LLVMContext Context;std::unique_ptr<Module> M = getLazyModuleFromAssembly(Context, Mem,"define double @foo(double %a) {\n"" %result = call double @llvm.experimental.constrained.sqrt.f64(double ""%a, metadata !\"round.tonearest\", metadata !\"fpexcept.strict\") ""strictfp\n"" ret double %result\n""}\n""declare double @llvm.experimental.constrained.sqrt.f64(double, ""metadata, metadata)\n");Function *Foo = M->getFunction("foo");ASSERT_FALSE(Foo->materialize());EXPECT_FALSE(Foo->empty());for (auto &BB : *Foo) {auto It = BB.begin();while (It != BB.end()) {Instruction &I = *It;++It;if (auto *Call = dyn_cast<CallBase>(&I)) {EXPECT_TRUE(Call->isStrictFP());EXPECT_FALSE(Call->isNoBuiltin());}}}EXPECT_FALSE(verifyModule(*M, &dbgs()));}TEST(BitReaderTest, MaterializeFunctionsForBlockAddr) { // PR11677SmallString<1024> Mem;LLVMContext Context;std::unique_ptr<Module> M = getLazyModuleFromAssembly(Context, Mem, "@table = constant i8* blockaddress(@func, %bb)\n""define void @func() {\n"" unreachable\n""bb:\n"" unreachable\n""}\n");EXPECT_FALSE(verifyModule(*M, &dbgs()));EXPECT_FALSE(M->getFunction("func")->empty());}TEST(BitReaderTest, MaterializeFunctionsForBlockAddrInFunctionBefore) {SmallString<1024> Mem;LLVMContext Context;std::unique_ptr<Module> M = getLazyModuleFromAssembly(Context, Mem, "define i8* @before() {\n"" ret i8* blockaddress(@func, %bb)\n""}\n""define void @other() {\n"" unreachable\n""}\n""define void @func() {\n"" unreachable\n""bb:\n"" unreachable\n""}\n");EXPECT_TRUE(M->getFunction("before")->empty());EXPECT_TRUE(M->getFunction("func")->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));// Materialize @before, pulling in @func.EXPECT_FALSE(M->getFunction("before")->materialize());EXPECT_FALSE(M->getFunction("func")->empty());EXPECT_TRUE(M->getFunction("other")->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));}TEST(BitReaderTest, MaterializeFunctionsForBlockAddrInFunctionAfter) {SmallString<1024> Mem;LLVMContext Context;std::unique_ptr<Module> M = getLazyModuleFromAssembly(Context, Mem, "define void @func() {\n"" unreachable\n""bb:\n"" unreachable\n""}\n""define void @other() {\n"" unreachable\n""}\n""define i8* @after() {\n"" ret i8* blockaddress(@func, %bb)\n""}\n");EXPECT_TRUE(M->getFunction("after")->empty());EXPECT_TRUE(M->getFunction("func")->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));// Materialize @after, pulling in @func.EXPECT_FALSE(M->getFunction("after")->materialize());EXPECT_FALSE(M->getFunction("func")->empty());EXPECT_TRUE(M->getFunction("other")->empty());EXPECT_FALSE(verifyModule(*M, &dbgs()));}} // end namespace
//===- llvm/unittest/BinaryFormat/TestFileMagic.cpp - File magic tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "llvm/ADT/StringRef.h"#include "llvm/BinaryFormat/Magic.h"#include "llvm/Support/FileSystem.h"#include "llvm/Support/Path.h"#include "gtest/gtest.h"using namespace llvm;namespace fs = llvm::sys::fs;#define ASSERT_NO_ERROR(x) \if (std::error_code ASSERT_NO_ERROR_ec = x) { \SmallString<128> MessageStorage; \raw_svector_ostream Message(MessageStorage); \Message << #x ": did not return errc::success.\n" \<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \} else { \}class MagicTest : public testing::Test {protected:/// Unique temporary directory in which all created filesystem entities must/// be placed. It is removed at the end of each test (must be empty).SmallString<128> TestDirectory;void SetUp() override {ASSERT_NO_ERROR(fs::createUniqueDirectory("file-system-test", TestDirectory));// We don't care about this specific file.errs() << "Test Directory: " << TestDirectory << '\n';errs().flush();}void TearDown() override { ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); }};const char archive[] = "!<arch>\x0A";const char big_archive[] = "<bigaf>\x0A";const char bitcode[] = "\xde\xc0\x17\x0b";const char coff_object[] = "\x00\x00......";const char coff_bigobj[] ="\x00\x00\xff\xff\x00\x02......""\xc7\xa1\xba\xd1\xee\xba\xa9\x4b\xaf\x20\xfa\xf6\x6a\xa4\xdc\xb8";const char coff_import_library[] = "\x00\x00\xff\xff....";const char elf_relocatable[] = {0x7f, 'E', 'L', 'F', 1, 2, 1, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 1};const char goff_object[] = "\x03\xF0\x00";const char macho_universal_binary[] = "\xca\xfe\xba\xbe...\x00";const char macho_object[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x01............";const char macho_executable[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x02............";const char macho_fixed_virtual_memory_shared_lib[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x03............";const char macho_core[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x04............";const char macho_preload_executable[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x05............";const char macho_dynamically_linked_shared_lib[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x06............";const char macho_dynamic_linker[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x07............";const char macho_bundle[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x08............";const char macho_dsym_companion[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x0a............";const char macho_kext_bundle[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x0b............";const char windows_resource[] ="\x00\x00\x00\x00\x020\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00";const char macho_dynamically_linked_shared_lib_stub[] ="\xfe\xed\xfa\xce........\x00\x00\x00\x09............";const char ms_dos_stub_broken[] = "\x4d\x5a\x20\x20";const char pdb[] = "Microsoft C/C++ MSF 7.00\r\n\x1a""DS\x00\x00\x00";const char tapi_file[] = "--- !tapi-tbd-v1\n";const char tapi_file_tbd_v1[] = "---\narchs: [";TEST_F(MagicTest, Magic) {struct type {const char *filename;const char *magic_str;size_t magic_str_len;file_magic magic;} types[] = {#define DEFINE(magic) {#magic, magic, sizeof(magic), file_magic::magic}DEFINE(archive),{"big_archive", big_archive, sizeof(big_archive), file_magic::archive},DEFINE(bitcode),DEFINE(coff_object),{"coff_bigobj", coff_bigobj, sizeof(coff_bigobj),file_magic::coff_object},DEFINE(coff_import_library),DEFINE(elf_relocatable),DEFINE(goff_object),DEFINE(macho_universal_binary),DEFINE(macho_object),DEFINE(macho_executable),DEFINE(macho_fixed_virtual_memory_shared_lib),DEFINE(macho_core),DEFINE(macho_preload_executable),DEFINE(macho_dynamically_linked_shared_lib),DEFINE(macho_dynamic_linker),DEFINE(macho_bundle),DEFINE(macho_dynamically_linked_shared_lib_stub),DEFINE(macho_dsym_companion),DEFINE(macho_kext_bundle),DEFINE(windows_resource),DEFINE(pdb),{"ms_dos_stub_broken", ms_dos_stub_broken, sizeof(ms_dos_stub_broken),file_magic::unknown},DEFINE(tapi_file),{"tapi_file_tbd_v1", tapi_file_tbd_v1, sizeof(tapi_file_tbd_v1),file_magic::tapi_file},#undef DEFINE};// Create some files filled with magic.for (type *i = types, *e = types + (sizeof(types) / sizeof(type)); i != e;++i) {SmallString<128> file_pathname(TestDirectory);llvm::sys::path::append(file_pathname, i->filename);std::error_code EC;raw_fd_ostream file(file_pathname, EC, sys::fs::OF_None);ASSERT_FALSE(file.has_error());StringRef magic(i->magic_str, i->magic_str_len);file << magic;file.close();EXPECT_EQ(i->magic, identify_magic(magic));ASSERT_NO_ERROR(fs::remove(Twine(file_pathname)));}}
//===- MsgPackWriterTest.cpp ------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/MsgPackWriter.h"#include "llvm/BinaryFormat/MsgPack.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::msgpack;struct MsgPackWriter : testing::Test {std::string Buffer;llvm::raw_string_ostream OStream;Writer MPWriter;MsgPackWriter() : OStream(Buffer), MPWriter(OStream) {}};TEST_F(MsgPackWriter, TestWriteNil) {MPWriter.writeNil();EXPECT_EQ(OStream.str(), "\xc0");}TEST_F(MsgPackWriter, TestWriteBool) {MPWriter.write(true);MPWriter.write(false);EXPECT_EQ(OStream.str(), "\xc3\xc2");}TEST_F(MsgPackWriter, TestWriteFixPositiveInt) {// FixPositiveInt form bitpattern starts with 0, so max FixPositiveInt// is 01111111 = 127for (uint64_t u = 0; u <= 127; ++u) {Buffer.clear();MPWriter.write(u);std::string Output = OStream.str();EXPECT_EQ(Output.size(), 1u);EXPECT_EQ(Output.data()[0], static_cast<uint8_t>(u));}}TEST_F(MsgPackWriter, TestWriteUInt8Min) {// See TestWriteFixPositiveInt for why 128 is the min non-fix Int8uint64_t u = 128;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xcc\x80");}TEST_F(MsgPackWriter, TestWriteUInt8) {uint64_t u = 221;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xcc\xdd");}TEST_F(MsgPackWriter, TestWriteUInt8Max) {uint64_t u = UINT8_MAX;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xcc\xff");}TEST_F(MsgPackWriter, TestWriteUInt16Min) {uint64_t u = static_cast<uint64_t>(UINT8_MAX) + 1;MPWriter.write(u);EXPECT_EQ(OStream.str(), std::string("\xcd\x01\x00", 3));}TEST_F(MsgPackWriter, TestWriteUInt16) {uint64_t u = 43981;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xcd\xab\xcd");}TEST_F(MsgPackWriter, TestWriteUInt16Max) {uint64_t u = UINT16_MAX;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xcd\xff\xff");}TEST_F(MsgPackWriter, TestWriteUInt32Min) {uint64_t u = static_cast<uint64_t>(UINT16_MAX) + 1;MPWriter.write(u);EXPECT_EQ(OStream.str(), std::string("\xce\x00\x01\x00\x00", 5));}TEST_F(MsgPackWriter, TestWriteUInt32) {uint64_t u = 2882400186;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xce\xab\xcd\xef\xba");}TEST_F(MsgPackWriter, TestWriteUInt32Max) {uint64_t u = UINT32_MAX;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xce\xff\xff\xff\xff");}TEST_F(MsgPackWriter, TestWriteUInt64Min) {uint64_t u = static_cast<uint64_t>(UINT32_MAX) + 1;MPWriter.write(u);EXPECT_EQ(OStream.str(),std::string("\xcf\x00\x00\x00\x01\x00\x00\x00\x00", 9));}TEST_F(MsgPackWriter, TestWriteUInt64) {uint64_t u = 0x010203040506074a;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xcf\x01\x02\x03\x04\x05\x06\x07\x4a");}TEST_F(MsgPackWriter, TestWriteUInt64Max) {uint64_t u = UINT64_MAX;MPWriter.write(u);EXPECT_EQ(OStream.str(), "\xcf\xff\xff\xff\xff\xff\xff\xff\xff");}TEST_F(MsgPackWriter, TestWriteFixNegativeInt) {// Positive values will be written in a UInt form, so max FixNegativeInt is -1//// FixNegativeInt form bitpattern starts with 111, so min FixNegativeInt// is 11100000 = -32for (int64_t i = -1; i >= -32; --i) {Buffer.clear();MPWriter.write(i);std::string Output = OStream.str();EXPECT_EQ(Output.size(), 1u);EXPECT_EQ(static_cast<int8_t>(Output.data()[0]), static_cast<int8_t>(i));}}TEST_F(MsgPackWriter, TestWriteInt8Max) {// See TestWriteFixNegativeInt for why -33 is the max non-fix Int8int64_t i = -33;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd0\xdf");}TEST_F(MsgPackWriter, TestWriteInt8) {int64_t i = -40;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd0\xd8");}TEST_F(MsgPackWriter, TestWriteInt8Min) {int64_t i = INT8_MIN;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd0\x80");}TEST_F(MsgPackWriter, TestWriteInt16Max) {int64_t i = static_cast<int64_t>(INT8_MIN) - 1;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd1\xff\x7f");}TEST_F(MsgPackWriter, TestWriteInt16) {int64_t i = -4369;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd1\xee\xef");}TEST_F(MsgPackWriter, TestWriteInt16Min) {int64_t i = INT16_MIN;MPWriter.write(i);EXPECT_EQ(OStream.str(), std::string("\xd1\x80\x00", 3));}TEST_F(MsgPackWriter, TestWriteInt32Max) {int64_t i = static_cast<int64_t>(INT16_MIN) - 1;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd2\xff\xff\x7f\xff");}TEST_F(MsgPackWriter, TestWriteInt32) {int64_t i = -286331153;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd2\xee\xee\xee\xef");}TEST_F(MsgPackWriter, TestWriteInt32Min) {int64_t i = INT32_MIN;MPWriter.write(i);EXPECT_EQ(OStream.str(), std::string("\xd2\x80\x00\x00\x00", 5));}TEST_F(MsgPackWriter, TestWriteInt64Max) {int64_t i = static_cast<int64_t>(INT32_MIN) - 1;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd3\xff\xff\xff\xff\x7f\xff\xff\xff");}TEST_F(MsgPackWriter, TestWriteInt64) {int64_t i = -1229782938247303441;MPWriter.write(i);EXPECT_EQ(OStream.str(), "\xd3\xee\xee\xee\xee\xee\xee\xee\xef");}TEST_F(MsgPackWriter, TestWriteInt64Min) {int64_t i = INT64_MIN;MPWriter.write(i);EXPECT_EQ(OStream.str(),std::string("\xd3\x80\x00\x00\x00\x00\x00\x00\x00", 9));}TEST_F(MsgPackWriter, TestWriteFloat32) {float f = -3.6973142664068907e+28;MPWriter.write(f);EXPECT_EQ(OStream.str(), "\xca\xee\xee\xee\xef");}TEST_F(MsgPackWriter, TestWriteFloat64) {double d = -2.2899894549927042e+226;MPWriter.write(d);EXPECT_EQ(OStream.str(), "\xcb\xee\xee\xee\xee\xee\xee\xee\xef");}TEST_F(MsgPackWriter, TestWriteFixStrMin) {std::string s;MPWriter.write(s);EXPECT_EQ(OStream.str(), "\xa0");}TEST_F(MsgPackWriter, TestWriteFixStr) {std::string s = "foo";MPWriter.write(s);EXPECT_EQ(OStream.str(), "\xa3""foo");}TEST_F(MsgPackWriter, TestWriteFixStrMax) {// FixStr format's size is a 5 bit unsigned integer, so max is 11111 = 31std::string s(31, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xbf") + s);}TEST_F(MsgPackWriter, TestWriteStr8Min) {// See TestWriteFixStrMax for why 32 is the min non-fix Str8std::string s(32, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xd9\x20") + s);}TEST_F(MsgPackWriter, TestWriteStr8) {std::string s(33, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xd9\x21") + s);}TEST_F(MsgPackWriter, TestWriteStr8Max) {std::string s(UINT8_MAX, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xd9\xff") + s);}TEST_F(MsgPackWriter, TestWriteStr16Min) {std::string s(static_cast<uint64_t>(UINT8_MAX) + 1, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xda\x01\x00", 3) + s);}TEST_F(MsgPackWriter, TestWriteStr16) {std::string s(511, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xda\x01\xff") + s);}TEST_F(MsgPackWriter, TestWriteStr16Max) {std::string s(UINT16_MAX, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xda\xff\xff") + s);}TEST_F(MsgPackWriter, TestWriteStr32Min) {std::string s(static_cast<uint64_t>(UINT16_MAX) + 1, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xdb\x00\x01\x00\x00", 5) + s);}TEST_F(MsgPackWriter, TestWriteStr32) {std::string s(131071, 'a');MPWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xdb\x00\x01\xff\xff", 5) + s);}TEST_F(MsgPackWriter, TestWriteBin8Min) {std::string s;MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc4\x00", 2) + s);}TEST_F(MsgPackWriter, TestWriteBin8) {std::string s(5, 'a');MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc4\x05") + s);}TEST_F(MsgPackWriter, TestWriteBin8Max) {std::string s(UINT8_MAX, 'a');MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc4\xff") + s);}TEST_F(MsgPackWriter, TestWriteBin16Min) {std::string s(static_cast<uint64_t>(UINT8_MAX) + 1, 'a');MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc5\x01\x00", 3) + s);}TEST_F(MsgPackWriter, TestWriteBin16) {std::string s(511, 'a');MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), "\xc5\x01\xff" + s);}TEST_F(MsgPackWriter, TestWriteBin16Max) {std::string s(UINT16_MAX, 'a');MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc5\xff\xff") + s);}TEST_F(MsgPackWriter, TestWriteBin32Min) {std::string s(static_cast<uint64_t>(UINT16_MAX) + 1, 'a');MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc6\x00\x01\x00\x00", 5) + s);}TEST_F(MsgPackWriter, TestWriteBin32) {std::string s(131071, 'a');MPWriter.write(MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc6\x00\x01\xff\xff", 5) + s);}TEST_F(MsgPackWriter, TestWriteFixArrayMin) {MPWriter.writeArraySize(0);EXPECT_EQ(OStream.str(), "\x90");}TEST_F(MsgPackWriter, TestWriteFixArray) {MPWriter.writeArraySize(4);EXPECT_EQ(OStream.str(), "\x94");}TEST_F(MsgPackWriter, TestWriteFixArrayMax) {// FixArray format's size is a 4 bit unsigned integer, so max is 1111 = 15MPWriter.writeArraySize(15);EXPECT_EQ(OStream.str(), "\x9f");}TEST_F(MsgPackWriter, TestWriteArray16Min) {// See TestWriteFixArrayMax for why 16 is the min non-fix Array16MPWriter.writeArraySize(16);EXPECT_EQ(OStream.str(), std::string("\xdc\x00\x10", 3));}TEST_F(MsgPackWriter, TestWriteArray16) {MPWriter.writeArraySize(273);EXPECT_EQ(OStream.str(), "\xdc\x01\x11");}TEST_F(MsgPackWriter, TestWriteArray16Max) {MPWriter.writeArraySize(UINT16_MAX);EXPECT_EQ(OStream.str(), "\xdc\xff\xff");}TEST_F(MsgPackWriter, TestWriteArray32Min) {MPWriter.writeArraySize(static_cast<uint64_t>(UINT16_MAX) + 1);EXPECT_EQ(OStream.str(), std::string("\xdd\x00\x01\x00\x00", 5));}TEST_F(MsgPackWriter, TestWriteArray32) {MPWriter.writeArraySize(131071);EXPECT_EQ(OStream.str(), std::string("\xdd\x00\x01\xff\xff", 5));}TEST_F(MsgPackWriter, TestWriteArray32Max) {MPWriter.writeArraySize(UINT32_MAX);EXPECT_EQ(OStream.str(), "\xdd\xff\xff\xff\xff");}TEST_F(MsgPackWriter, TestWriteFixMapMin) {MPWriter.writeMapSize(0);EXPECT_EQ(OStream.str(), "\x80");}TEST_F(MsgPackWriter, TestWriteFixMap) {MPWriter.writeMapSize(4);EXPECT_EQ(OStream.str(), "\x84");}TEST_F(MsgPackWriter, TestWriteFixMapMax) {// FixMap format's size is a 4 bit unsigned integer, so max is 1111 = 15MPWriter.writeMapSize(15);EXPECT_EQ(OStream.str(), "\x8f");}TEST_F(MsgPackWriter, TestWriteMap16Min) {// See TestWriteFixMapMax for why 16 is the min non-fix Map16MPWriter.writeMapSize(16);EXPECT_EQ(OStream.str(), std::string("\xde\x00\x10", 3));}TEST_F(MsgPackWriter, TestWriteMap16) {MPWriter.writeMapSize(273);EXPECT_EQ(OStream.str(), "\xde\x01\x11");}TEST_F(MsgPackWriter, TestWriteMap16Max) {MPWriter.writeMapSize(UINT16_MAX);EXPECT_EQ(OStream.str(), "\xde\xff\xff");}TEST_F(MsgPackWriter, TestWriteMap32Min) {MPWriter.writeMapSize(static_cast<uint64_t>(UINT16_MAX) + 1);EXPECT_EQ(OStream.str(), std::string("\xdf\x00\x01\x00\x00", 5));}TEST_F(MsgPackWriter, TestWriteMap32) {MPWriter.writeMapSize(131071);EXPECT_EQ(OStream.str(), std::string("\xdf\x00\x01\xff\xff", 5));}TEST_F(MsgPackWriter, TestWriteMap32Max) {MPWriter.writeMapSize(UINT32_MAX);EXPECT_EQ(OStream.str(), std::string("\xdf\xff\xff\xff\xff", 5));}// FixExt formats are only available for these specific lengths: 1, 2, 4, 8, 16TEST_F(MsgPackWriter, TestWriteFixExt1) {std::string s(1, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xd4\x01") + s);}TEST_F(MsgPackWriter, TestWriteFixExt2) {std::string s(2, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xd5\x01") + s);}TEST_F(MsgPackWriter, TestWriteFixExt4) {std::string s(4, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xd6\x01") + s);}TEST_F(MsgPackWriter, TestWriteFixExt8) {std::string s(8, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xd7\x01") + s);}TEST_F(MsgPackWriter, TestWriteFixExt16) {std::string s(16, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xd8\x01") + s);}TEST_F(MsgPackWriter, TestWriteExt8Min) {std::string s;MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc7\x00\x01", 3) + s);}TEST_F(MsgPackWriter, TestWriteExt8) {std::string s(0x2a, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc7\x2a\x01") + s);}TEST_F(MsgPackWriter, TestWriteExt8Max) {std::string s(UINT8_MAX, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc7\xff\x01") + s);}TEST_F(MsgPackWriter, TestWriteExt16Min) {std::string s(static_cast<uint16_t>(UINT8_MAX) + 1, 'a');MPWriter.writeExt(0x02, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc8\x01\x00\x02", 4) + s);}TEST_F(MsgPackWriter, TestWriteExt16) {std::string s(273, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc8\x01\x11\x01") + s);}TEST_F(MsgPackWriter, TestWriteExt16Max) {std::string s(UINT16_MAX, 'a');MPWriter.writeExt(0x01, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc8\xff\xff\x01") + s);}TEST_F(MsgPackWriter, TestWriteExt32Min) {std::string s(static_cast<uint32_t>(UINT16_MAX) + 1, 'a');MPWriter.writeExt(0x02, MemoryBufferRef(s, ""));EXPECT_EQ(OStream.str(), std::string("\xc9\x00\x01\x00\x00\x02", 6) + s);}TEST_F(MsgPackWriter, TestWriteCompatibleNoStr8) {Writer CompatWriter(OStream, true);std::string s(32, 'a');CompatWriter.write(s);EXPECT_EQ(OStream.str(), std::string("\xda\x00\x20", 3) + s);}TEST_F(MsgPackWriter, TestWriteCompatibleNoBin) {Writer CompatWriter(OStream, true);std::string s;#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGEXPECT_DEATH(CompatWriter.write(MemoryBufferRef(s, "")), "compatible mode");#endif#endif}
//===- MsgPackReaderTest.cpp ------------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/MsgPackReader.h"#include "llvm/BinaryFormat/MsgPack.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::msgpack;struct MsgPackReader : testing::Test {std::string Buffer;Object Obj;};TEST_F(MsgPackReader, TestReadMultiple) {Buffer = "\xc0\xc2";Reader MPReader(Buffer);{auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Nil);}{auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Boolean);EXPECT_EQ(Obj.Bool, false);}{auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_FALSE(*ContinueOrErr);}}TEST_F(MsgPackReader, TestReadNil) {Buffer = "\xc0";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Nil);}TEST_F(MsgPackReader, TestReadBoolFalse) {Buffer = "\xc2";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Boolean);EXPECT_EQ(Obj.Bool, false);}TEST_F(MsgPackReader, TestReadBoolTrue) {Buffer = "\xc3";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Boolean);EXPECT_EQ(Obj.Bool, true);}TEST_F(MsgPackReader, TestReadFixNegativeInt) {// Positive values will be written in a UInt form, so max FixNegativeInt is -1//// FixNegativeInt form bitpattern starts with 111, so min FixNegativeInt// is 11100000 = -32for (int8_t i = -1; i >= -32; --i) {Buffer.assign(1, static_cast<char>(i));Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, i);}}TEST_F(MsgPackReader, TestReadInt8Max) {Buffer = "\xd0\x7f";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT8_MAX);}TEST_F(MsgPackReader, TestReadInt8Zero) {Buffer.assign("\xd0\x00", 2);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, 0);}TEST_F(MsgPackReader, TestReadInt8Min) {Buffer = "\xd0\x80";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT8_MIN);}TEST_F(MsgPackReader, TestReadInt16Max) {Buffer = "\xd1\x7f\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT16_MAX);}TEST_F(MsgPackReader, TestReadInt16Zero) {Buffer.assign("\xd1\x00\x00", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, 0);}TEST_F(MsgPackReader, TestReadInt16Min) {Buffer.assign("\xd1\x80\x00", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT16_MIN);}TEST_F(MsgPackReader, TestReadInt32Max) {Buffer = "\xd2\x7f\xff\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT32_MAX);}TEST_F(MsgPackReader, TestReadInt32Zero) {Buffer.assign("\xd2\x00\x00\x00\x00", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, 0);}TEST_F(MsgPackReader, TestReadInt32Min) {Buffer.assign("\xd2\x80\x00\x00\x00", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT32_MIN);}TEST_F(MsgPackReader, TestReadInt64Max) {Buffer = "\xd3\x7f\xff\xff\xff\xff\xff\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT64_MAX);}TEST_F(MsgPackReader, TestReadInt64Zero) {Buffer.assign("\xd3\x00\x00\x00\x00\x00\x00\x00\x00", 9);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, 0);}TEST_F(MsgPackReader, TestReadInt64Min) {Buffer.assign("\xd3\x80\x00\x00\x00\x00\x00\x00\x00", 9);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Int);EXPECT_EQ(Obj.Int, INT64_MIN);}TEST_F(MsgPackReader, TestReadFixPositiveInt) {// FixPositiveInt form bitpattern starts with 0, so max FixPositiveInt// is 01111111 = 127for (uint64_t u = 0; u <= 127; ++u) {Buffer.assign(1, static_cast<char>(u));Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, u);}}TEST_F(MsgPackReader, TestReadUInt8Zero) {Buffer.assign("\xcc\x00", 2);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 0u);}TEST_F(MsgPackReader, TestReadUInt8One) {Buffer = "\xcc\x01";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 1u);}TEST_F(MsgPackReader, TestReadUInt8Max) {Buffer = "\xcc\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, static_cast<uint8_t>(UINT8_MAX));}TEST_F(MsgPackReader, TestReadUInt16Zero) {Buffer.assign("\xcd\x00\x00", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 0u);}TEST_F(MsgPackReader, TestReadUInt16One) {Buffer.assign("\xcd\x00\x01", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 1u);}TEST_F(MsgPackReader, TestReadUInt16Max) {Buffer = "\xcd\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, static_cast<uint16_t>(UINT16_MAX));}TEST_F(MsgPackReader, TestReadUInt32Zero) {Buffer.assign("\xce\x00\x00\x00\x00", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 0u);}TEST_F(MsgPackReader, TestReadUInt32One) {Buffer.assign("\xce\x00\x00\x00\x01", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 1u);}TEST_F(MsgPackReader, TestReadUInt32Max) {Buffer = "\xce\xff\xff\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, static_cast<uint32_t>(UINT32_MAX));}TEST_F(MsgPackReader, TestReadUInt64Zero) {Buffer.assign("\xcf\x00\x00\x00\x00\x00\x00\x00\x00", 9);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 0u);}TEST_F(MsgPackReader, TestReadUInt64One) {Buffer.assign("\xcf\x00\x00\x00\x00\x00\x00\x00\x01", 9);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, 1u);}TEST_F(MsgPackReader, TestReadUInt64Max) {Buffer = "\xcf\xff\xff\xff\xff\xff\xff\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::UInt);EXPECT_EQ(Obj.UInt, static_cast<uint64_t>(UINT64_MAX));}TEST_F(MsgPackReader, TestReadFloat32) {Buffer = "\xca\xee\xee\xee\xef";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Float);EXPECT_EQ(Obj.Float, -3.6973142664068907e+28f);}TEST_F(MsgPackReader, TestReadFloat64) {Buffer = "\xcb\xee\xee\xee\xee\xee\xee\xee\xef";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Float);EXPECT_EQ(Obj.Float, -2.2899894549927042e+226);}TEST_F(MsgPackReader, TestReadFixStrZero) {Buffer = "\xa0";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, StringRef());}TEST_F(MsgPackReader, TestReadFixStrOne) {std::string Result(1, 'a');Buffer = std::string("\xa1") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadFixStrMax) {// FixStr format's size is a 5 bit unsigned integer, so max is 11111 = 31std::string Result(31, 'a');Buffer = std::string("\xbf") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadStr8Zero) {Buffer.assign("\xd9\x00", 2);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, StringRef());}TEST_F(MsgPackReader, TestReadStr8One) {std::string Result(1, 'a');Buffer = std::string("\xd9\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadStr8Max) {std::string Result(UINT8_MAX, 'a');Buffer = std::string("\xd9\xff") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadStr16Zero) {Buffer.assign("\xda\x00\x00", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, StringRef());}TEST_F(MsgPackReader, TestReadStr16One) {std::string Result(1, 'a');Buffer = std::string("\xda\x00\x01", 3) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadStr16Max) {std::string Result(UINT16_MAX, 'a');Buffer = std::string("\xda\xff\xff") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadStr32Zero) {Buffer.assign("\xdb\x00\x00\x00\x00", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, StringRef());}TEST_F(MsgPackReader, TestReadStr32One) {std::string Result(1, 'a');Buffer = std::string("\xdb\x00\x00\x00\x01", 5) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadStr32Max) {std::string Result(static_cast<uint32_t>(UINT16_MAX) + 1, 'a');Buffer = std::string("\xdb\x00\x01\x00\x00", 5) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::String);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadBin8Zero) {Buffer.assign("\xc4\x00", 2);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, StringRef());}TEST_F(MsgPackReader, TestReadBin8One) {std::string Result(1, 'a');Buffer = std::string("\xc4\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadBin8Max) {std::string Result(UINT8_MAX, 'a');Buffer = std::string("\xc4\xff") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadBin16Zero) {Buffer.assign("\xc5\x00\x00", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, StringRef());}TEST_F(MsgPackReader, TestReadBin16One) {std::string Result(1, 'a');Buffer = std::string("\xc5\x00\x01", 3) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadBin16Max) {std::string Result(UINT16_MAX, 'a');Buffer = std::string("\xc5\xff\xff") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadBin32Zero) {Buffer.assign("\xc6\x00\x00\x00\x00", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, StringRef());}TEST_F(MsgPackReader, TestReadBin32One) {std::string Result(1, 'a');Buffer = std::string("\xc6\x00\x00\x00\x01", 5) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadBin32Max) {std::string Result(static_cast<uint32_t>(UINT16_MAX) + 1, 'a');Buffer = std::string("\xc6\x00\x01\x00\x00", 5) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Binary);EXPECT_EQ(Obj.Raw, Result);}TEST_F(MsgPackReader, TestReadFixArrayZero) {Buffer = "\x90";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, 0u);}TEST_F(MsgPackReader, TestReadFixArrayOne) {Buffer = "\x91";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, 1u);}TEST_F(MsgPackReader, TestReadFixArrayMax) {Buffer = "\x9f";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);// FixArray format's size is a 4 bit unsigned integer, so max is 1111 = 15EXPECT_EQ(Obj.Length, 15u);}TEST_F(MsgPackReader, TestReadArray16Zero) {Buffer.assign("\xdc\x00\x00", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, 0u);}TEST_F(MsgPackReader, TestReadArray16One) {Buffer.assign("\xdc\x00\x01", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, 1u);}TEST_F(MsgPackReader, TestReadArray16Max) {Buffer = "\xdc\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, static_cast<uint16_t>(UINT16_MAX));}TEST_F(MsgPackReader, TestReadArray32Zero) {Buffer.assign("\xdd\x00\x00\x00\x00", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, 0u);}TEST_F(MsgPackReader, TestReadArray32One) {Buffer.assign("\xdd\x00\x00\x00\x01", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, 1u);}TEST_F(MsgPackReader, TestReadArray32Max) {Buffer = "\xdd\xff\xff\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Array);EXPECT_EQ(Obj.Length, static_cast<uint32_t>(UINT32_MAX));}TEST_F(MsgPackReader, TestReadFixMapZero) {Buffer = "\x80";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, 0u);}TEST_F(MsgPackReader, TestReadFixMapOne) {Buffer = "\x81";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, 1u);}TEST_F(MsgPackReader, TestReadFixMapMax) {Buffer = "\x8f";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);// FixMap format's size is a 4 bit unsigned integer, so max is 1111 = 15EXPECT_EQ(Obj.Length, 15u);}TEST_F(MsgPackReader, TestReadMap16Zero) {Buffer.assign("\xde\x00\x00", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, 0u);}TEST_F(MsgPackReader, TestReadMap16One) {Buffer.assign("\xde\x00\x01", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, 1u);}TEST_F(MsgPackReader, TestReadMap16Max) {Buffer = "\xde\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, static_cast<uint16_t>(UINT16_MAX));}TEST_F(MsgPackReader, TestReadMap32Zero) {Buffer.assign("\xdf\x00\x00\x00\x00", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, 0u);}TEST_F(MsgPackReader, TestReadMap32One) {Buffer.assign("\xdf\x00\x00\x00\x01", 5);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, 1u);}TEST_F(MsgPackReader, TestReadMap32Max) {Buffer = "\xdf\xff\xff\xff\xff";Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Map);EXPECT_EQ(Obj.Length, static_cast<uint32_t>(UINT32_MAX));}// FixExt formats are only available for these specific lengths: 1, 2, 4, 8, 16TEST_F(MsgPackReader, TestReadFixExt1) {std::string Result(1, 'a');Buffer = std::string("\xd4\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadFixExt2) {std::string Result(2, 'a');Buffer = std::string("\xd5\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadFixExt4) {std::string Result(4, 'a');Buffer = std::string("\xd6\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadFixExt8) {std::string Result(8, 'a');Buffer = std::string("\xd7\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadFixExt16) {std::string Result(16, 'a');Buffer = std::string("\xd8\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadExt8Min) {// There are fix variants for sizes 1 and 2Buffer.assign("\xc7\x00\x01", 3);Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, StringRef());}TEST_F(MsgPackReader, TestReadExt8Max) {std::string Result(UINT8_MAX, 'a');Buffer = std::string("\xc7\xff\x01", 3) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadExt16Min) {std::string Result(static_cast<uint16_t>(UINT8_MAX) + 1, 'a');Buffer = std::string("\xc8\x01\x00\x01", 4) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadExt16Max) {std::string Result(UINT16_MAX, 'a');Buffer = std::string("\xc8\xff\xff\x01") + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}TEST_F(MsgPackReader, TestReadExt32Min) {std::string Result(static_cast<uint32_t>(UINT16_MAX) + 1, 'a');Buffer = std::string("\xc9\x00\x01\x00\x00\x01", 6) + Result;Reader MPReader(Buffer);auto ContinueOrErr = MPReader.read(Obj);EXPECT_TRUE(static_cast<bool>(ContinueOrErr));EXPECT_TRUE(*ContinueOrErr);EXPECT_EQ(Obj.Kind, Type::Extension);EXPECT_EQ(Obj.Extension.Type, 0x01);EXPECT_EQ(Obj.Extension.Bytes, Result);}
//===- MsgPackDocumentTest.cpp --------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/MsgPackDocument.h"#include "gtest/gtest.h"using namespace llvm;using namespace msgpack;TEST(MsgPackDocument, DocNodeTest) {Document Doc;DocNode Int1 = Doc.getNode(1), Int2 = Doc.getNode(2);DocNode Str1 = Doc.getNode("ab"), Str2 = Doc.getNode("ab");ASSERT_TRUE(Int1 != Int2);ASSERT_TRUE(Str1 == Str2);}TEST(MsgPackDocument, TestReadInt) {Document Doc;bool Ok = Doc.readFromBlob(StringRef("\xd0\x00", 2), /*Multi=*/false);ASSERT_TRUE(Ok);ASSERT_EQ(Doc.getRoot().getKind(), Type::Int);ASSERT_EQ(Doc.getRoot().getInt(), 0);}TEST(MsgPackDocument, TestReadMergeArray) {Document Doc;bool Ok = Doc.readFromBlob(StringRef("\x92\xd0\x01\xc0"), /*Multi=*/false);ASSERT_TRUE(Ok);ASSERT_EQ(Doc.getRoot().getKind(), Type::Array);auto A = Doc.getRoot().getArray();ASSERT_EQ(A.size(), 2u);auto SI = A[0];ASSERT_EQ(SI.getKind(), Type::Int);ASSERT_EQ(SI.getInt(), 1);auto SN = A[1];ASSERT_EQ(SN.getKind(), Type::Nil);Ok = Doc.readFromBlob(StringRef("\x91\xd0\x2a"), /*Multi=*/false,[](DocNode *DestNode, DocNode SrcNode, DocNode MapKey) {// Allow array, merging into existing elements, ORing// ints.if (DestNode->getKind() == Type::Int &&SrcNode.getKind() == Type::Int) {*DestNode = DestNode->getDocument()->getNode(DestNode->getInt() | SrcNode.getInt());return 0;}return DestNode->isArray() && SrcNode.isArray() ? 0: -1;});ASSERT_TRUE(Ok);A = Doc.getRoot().getArray();ASSERT_EQ(A.size(), 2u);SI = A[0];ASSERT_EQ(SI.getKind(), Type::Int);ASSERT_EQ(SI.getInt(), 43);SN = A[1];ASSERT_EQ(SN.getKind(), Type::Nil);}TEST(MsgPackDocument, TestReadAppendArray) {Document Doc;bool Ok = Doc.readFromBlob(StringRef("\x92\xd0\x01\xc0"), /*Multi=*/false);ASSERT_TRUE(Ok);ASSERT_EQ(Doc.getRoot().getKind(), Type::Array);auto A = Doc.getRoot().getArray();ASSERT_EQ(A.size(), 2u);auto SI = A[0];ASSERT_EQ(SI.getKind(), Type::Int);ASSERT_EQ(SI.getInt(), 1);auto SN = A[1];ASSERT_EQ(SN.getKind(), Type::Nil);Ok = Doc.readFromBlob(StringRef("\x91\xd0\x2a"), /*Multi=*/false,[](DocNode *DestNode, DocNode SrcNode, DocNode MapKey) {// Allow array, appending after existing elementsreturn DestNode->isArray() && SrcNode.isArray()? DestNode->getArray().size(): -1;});ASSERT_TRUE(Ok);A = Doc.getRoot().getArray();ASSERT_EQ(A.size(), 3u);SI = A[0];ASSERT_EQ(SI.getKind(), Type::Int);ASSERT_EQ(SI.getInt(), 1);SN = A[1];ASSERT_EQ(SN.getKind(), Type::Nil);SI = A[2];ASSERT_EQ(SI.getKind(), Type::Int);ASSERT_EQ(SI.getInt(), 42);}TEST(MsgPackDocument, TestReadMergeMap) {Document Doc;bool Ok = Doc.readFromBlob(StringRef("\x82\xa3""foo""\xd0\x01\xa3""bar""\xd0\x02"),/*Multi=*/false);ASSERT_TRUE(Ok);ASSERT_EQ(Doc.getRoot().getKind(), Type::Map);auto M = Doc.getRoot().getMap();ASSERT_EQ(M.size(), 2u);auto FooS = M["foo"];ASSERT_EQ(FooS.getKind(), Type::Int);ASSERT_EQ(FooS.getInt(), 1);auto BarS = M["bar"];ASSERT_EQ(BarS.getKind(), Type::Int);ASSERT_EQ(BarS.getInt(), 2);Ok = Doc.readFromBlob(StringRef("\x82\xa3""foz""\xd0\x03\xa3""baz""\xd0\x04"),/*Multi=*/false,[](DocNode *DestNode, DocNode SrcNode, DocNode MapKey) {return DestNode->isMap() && SrcNode.isMap() ? 0 : -1;});ASSERT_TRUE(Ok);ASSERT_EQ(M.size(), 4u);FooS = M["foo"];ASSERT_EQ(FooS.getKind(), Type::Int);ASSERT_EQ(FooS.getInt(), 1);BarS = M["bar"];ASSERT_EQ(BarS.getKind(), Type::Int);ASSERT_EQ(BarS.getInt(), 2);auto FozS = M["foz"];ASSERT_EQ(FozS.getKind(), Type::Int);ASSERT_EQ(FozS.getInt(), 3);auto BazS = M["baz"];ASSERT_EQ(BazS.getKind(), Type::Int);ASSERT_EQ(BazS.getInt(), 4);Ok = Doc.readFromBlob(StringRef("\x82\xa3""foz""\xd0\x06\xa3""bay""\xd0\x08"),/*Multi=*/false, [](DocNode *Dest, DocNode Src, DocNode MapKey) {// Merger function that merges two ints by ORing their values, as long// as the map key is "foz".if (Src.isMap())return Dest->isMap();if (Src.isArray())return Dest->isArray();if (MapKey.isString() && MapKey.getString() == "foz" &&Dest->getKind() == Type::Int && Src.getKind() == Type::Int) {*Dest = Src.getDocument()->getNode(Dest->getInt() | Src.getInt());return true;}return false;});ASSERT_TRUE(Ok);ASSERT_EQ(M.size(), 5u);FooS = M["foo"];ASSERT_EQ(FooS.getKind(), Type::Int);ASSERT_EQ(FooS.getInt(), 1);BarS = M["bar"];ASSERT_EQ(BarS.getKind(), Type::Int);ASSERT_EQ(BarS.getInt(), 2);FozS = M["foz"];ASSERT_EQ(FozS.getKind(), Type::Int);ASSERT_EQ(FozS.getInt(), 7);BazS = M["baz"];ASSERT_EQ(BazS.getKind(), Type::Int);ASSERT_EQ(BazS.getInt(), 4);auto BayS = M["bay"];ASSERT_EQ(BayS.getKind(), Type::Int);ASSERT_EQ(BayS.getInt(), 8);}TEST(MsgPackDocument, TestWriteInt) {Document Doc;Doc.getRoot() = 1;std::string Buffer;Doc.writeToBlob(Buffer);ASSERT_EQ(Buffer, "\x01");}TEST(MsgPackDocument, TestWriteArray) {Document Doc;auto A = Doc.getRoot().getArray(/*Convert=*/true);A.push_back(Doc.getNode(int64_t(1)));A.push_back(Doc.getNode());std::string Buffer;Doc.writeToBlob(Buffer);ASSERT_EQ(Buffer, "\x92\x01\xc0");}TEST(MsgPackDocument, TestWriteMap) {Document Doc;auto M = Doc.getRoot().getMap(/*Convert=*/true);M["foo"] = 1;M["bar"] = 2;std::string Buffer;Doc.writeToBlob(Buffer);ASSERT_EQ(Buffer, "\x82\xa3""bar""\x02\xa3""foo""\x01");}TEST(MsgPackDocument, TestOutputYAMLArray) {Document Doc;auto A = Doc.getRoot().getArray(/*Convert=*/true);A.push_back(Doc.getNode(int64_t(1)));A.push_back(Doc.getNode(int64_t(2)));std::string Buffer;raw_string_ostream OStream(Buffer);Doc.toYAML(OStream);ASSERT_EQ(OStream.str(), "---\n- 1\n- 2\n...\n");}TEST(MsgPackDocument, TestInputYAMLArray) {Document Doc;bool Ok = Doc.fromYAML("---\n- !int 0x1\n- !str 2\n...\n");ASSERT_TRUE(Ok);ASSERT_EQ(Doc.getRoot().getKind(), Type::Array);auto A = Doc.getRoot().getArray();ASSERT_EQ(A.size(), 2u);auto SI = A[0];ASSERT_EQ(SI.getKind(), Type::UInt);ASSERT_EQ(SI.getUInt(), 1u);auto SS = A[1];ASSERT_EQ(SS.getKind(), Type::String);ASSERT_EQ(SS.getString(), "2");}TEST(MsgPackDocument, TestOutputYAMLMap) {Document Doc;auto M = Doc.getRoot().getMap(/*Convert=*/true);M["foo"] = 1;M["bar"] = 2U;auto N = Doc.getMapNode();M["qux"] = N;N["baz"] = true;std::string Buffer;raw_string_ostream OStream(Buffer);Doc.toYAML(OStream);ASSERT_EQ(OStream.str(), "---\n""bar: 2\n""foo: 1\n""qux:\n"" baz: true\n""...\n");}TEST(MsgPackDocument, TestOutputYAMLMapWithErase) {Document Doc;auto M = Doc.getRoot().getMap(/*Convert=*/true);M["foo"] = 1;M["bar"] = 2U;auto N = Doc.getMapNode();M["qux"] = N;N["baz"] = true;M.erase(Doc.getNode("bar"));std::string Buffer;raw_string_ostream OStream(Buffer);Doc.toYAML(OStream);ASSERT_EQ(OStream.str(), "---\n""foo: 1\n""qux:\n"" baz: true\n""...\n");}TEST(MsgPackDocument, TestOutputYAMLMapHex) {Document Doc;Doc.setHexMode();auto M = Doc.getRoot().getMap(/*Convert=*/true);M["foo"] = 1;M["bar"] = 2U;auto N = Doc.getMapNode();M["qux"] = N;N["baz"] = true;std::string Buffer;raw_string_ostream OStream(Buffer);Doc.toYAML(OStream);ASSERT_EQ(OStream.str(), "---\n""bar: 0x2\n""foo: 1\n""qux:\n"" baz: true\n""...\n");}TEST(MsgPackDocument, TestInputYAMLMap) {Document Doc;bool Ok = Doc.fromYAML("---\nfoo: !int 0x1\nbaz: !str 2\n...\n");ASSERT_TRUE(Ok);ASSERT_EQ(Doc.getRoot().getKind(), Type::Map);auto M = Doc.getRoot().getMap();ASSERT_EQ(M.size(), 2u);auto SI = M["foo"];ASSERT_EQ(SI.getKind(), Type::UInt);ASSERT_EQ(SI.getUInt(), 1u);auto SS = M["baz"];ASSERT_EQ(SS.getKind(), Type::String);ASSERT_EQ(SS.getString(), "2");}
//===- unittest/BinaryFormat/MachOTest.cpp - MachO support tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/MachO.h"#include "llvm/ADT/Triple.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::MachO;TEST(MachOTest, UnalignedLC) {unsigned char Valid32BitMachO[] = {0xCE, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00,0x85, 0x80, 0x21, 0x01, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,0x5F, 0x5F, 0x50, 0x41, 0x47, 0x45, 0x5A, 0x45, 0x52, 0x4F, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x4C, 0x49,0x4E, 0x4B, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,0x8C, 0x0B, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};mach_header *Header =reinterpret_cast<mach_header *>(Valid32BitMachO);if (!sys::IsLittleEndianHost)swapStruct(*Header);ASSERT_EQ(Header->magic, MH_MAGIC);unsigned char *Current = Valid32BitMachO + sizeof(mach_header);unsigned char *BufferEnd =Valid32BitMachO + sizeof(mach_header) + Header->sizeofcmds;while (Current < BufferEnd) {macho_load_command *LC =reinterpret_cast<macho_load_command *>(Current);if (!sys::IsLittleEndianHost)swapStruct(LC->load_command_data);ASSERT_EQ(LC->load_command_data.cmd, LC_SEGMENT);Current += LC->load_command_data.cmdsize;}}TEST(MachOTest, CPUType) {#define CHECK_CPUTYPE(StrTriple, ExpectedCPUType) \ASSERT_EQ((MachO::CPUType)cantFail(MachO::getCPUType(Triple(StrTriple))), \(ExpectedCPUType))CHECK_CPUTYPE("x86_64-apple-darwin", MachO::CPU_TYPE_X86_64);CHECK_CPUTYPE("x86_64h-apple-darwin", MachO::CPU_TYPE_X86_64);CHECK_CPUTYPE("i386-apple-darwin", MachO::CPU_TYPE_X86);CHECK_CPUTYPE("armv7-apple-darwin", MachO::CPU_TYPE_ARM);CHECK_CPUTYPE("thumbv7-apple-darwin", MachO::CPU_TYPE_ARM);CHECK_CPUTYPE("arm64-apple-darwin", MachO::CPU_TYPE_ARM64);CHECK_CPUTYPE("arm64e-apple-darwin", MachO::CPU_TYPE_ARM64);CHECK_CPUTYPE("arm64_32-apple-darwin", MachO::CPU_TYPE_ARM64_32);{// Not a mach-o.Expected<uint32_t> Type = MachO::getCPUType(Triple("x86_64-linux-unknown"));ASSERT_EQ(toString(Type.takeError()),"Unsupported triple for mach-o cpu type: x86_64-linux-unknown");}{// Not a valid mach-o architecture.Expected<uint32_t> Type = MachO::getCPUType(Triple("mips-apple-darwin"));ASSERT_EQ(toString(Type.takeError()),"Unsupported triple for mach-o cpu type: mips-apple-darwin");}#undef CHECK_CPUTYPE}TEST(MachOTest, CPUSubType) {#define CHECK_CPUSUBTYPE(StrTriple, ExpectedCPUSubType) \ASSERT_EQ(cantFail(MachO::getCPUSubType(Triple(StrTriple))), \((uint32_t)ExpectedCPUSubType))CHECK_CPUSUBTYPE("x86_64-apple-darwin", MachO::CPU_SUBTYPE_X86_64_ALL);CHECK_CPUSUBTYPE("x86_64h-apple-darwin", MachO::CPU_SUBTYPE_X86_64_H);CHECK_CPUSUBTYPE("i386-apple-darwin", MachO::CPU_SUBTYPE_I386_ALL);CHECK_CPUSUBTYPE("arm-apple-darwin", MachO::CPU_SUBTYPE_ARM_V7); // DefaultCHECK_CPUSUBTYPE("armv4t-apple-darwin", MachO::CPU_SUBTYPE_ARM_V4T);CHECK_CPUSUBTYPE("armv5t-apple-darwin", MachO::CPU_SUBTYPE_ARM_V5);CHECK_CPUSUBTYPE("armv5te-apple-darwin", MachO::CPU_SUBTYPE_ARM_V5);CHECK_CPUSUBTYPE("armv5tej-apple-darwin", MachO::CPU_SUBTYPE_ARM_V5);CHECK_CPUSUBTYPE("armv6-apple-darwin", MachO::CPU_SUBTYPE_ARM_V6);CHECK_CPUSUBTYPE("armv6k-apple-darwin", MachO::CPU_SUBTYPE_ARM_V6);CHECK_CPUSUBTYPE("armv7a-apple-darwin", MachO::CPU_SUBTYPE_ARM_V7);CHECK_CPUSUBTYPE("armv7s-apple-darwin", MachO::CPU_SUBTYPE_ARM_V7S);CHECK_CPUSUBTYPE("armv7k-apple-darwin", MachO::CPU_SUBTYPE_ARM_V7K);CHECK_CPUSUBTYPE("armv6m-apple-darwin", MachO::CPU_SUBTYPE_ARM_V6M);CHECK_CPUSUBTYPE("armv7m-apple-darwin", MachO::CPU_SUBTYPE_ARM_V7M);CHECK_CPUSUBTYPE("armv7em-apple-darwin", MachO::CPU_SUBTYPE_ARM_V7EM);CHECK_CPUSUBTYPE("thumbv7-apple-darwin", MachO::CPU_SUBTYPE_ARM_V7);CHECK_CPUSUBTYPE("thumbv6-apple-darwin", MachO::CPU_SUBTYPE_ARM_V6);CHECK_CPUSUBTYPE("arm64-apple-darwin", MachO::CPU_SUBTYPE_ARM64_ALL);CHECK_CPUSUBTYPE("arm64e-apple-darwin", MachO::CPU_SUBTYPE_ARM64E);CHECK_CPUSUBTYPE("arm64_32-apple-darwin", MachO::CPU_SUBTYPE_ARM64_32_V8);{// Not a mach-o.Expected<uint32_t> Type =MachO::getCPUSubType(Triple("x86_64-linux-unknown"));ASSERT_EQ(toString(Type.takeError()),"Unsupported triple for mach-o cpu subtype: x86_64-linux-unknown");}{// Not a valid mach-o architecture.Expected<uint32_t> Type = MachO::getCPUSubType(Triple("mips-apple-darwin"));ASSERT_EQ(toString(Type.takeError()),"Unsupported triple for mach-o cpu subtype: mips-apple-darwin");}#undef CHECK_CPUSUBTYPE}
//===- unittest/BinaryFormat/DwarfTest.cpp - Dwarf support tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/BinaryFormat/Dwarf.h"#include "llvm/ADT/StringRef.h"#include "llvm/Support/FormatVariadic.h"#include "gtest/gtest.h"using namespace llvm;using namespace llvm::dwarf;namespace {TEST(DwarfTest, TagStringOnInvalid) {// This is invalid, so it shouldn't be stringified.EXPECT_EQ(StringRef(), TagString(DW_TAG_invalid));// These aren't really tags: they describe ranges within tags. They// shouldn't be stringified either.EXPECT_EQ(StringRef(), TagString(DW_TAG_lo_user));EXPECT_EQ(StringRef(), TagString(DW_TAG_hi_user));EXPECT_EQ(StringRef(), TagString(DW_TAG_user_base));}TEST(DwarfTest, getTag) {// A couple of valid tags.EXPECT_EQ(DW_TAG_array_type, getTag("DW_TAG_array_type"));EXPECT_EQ(DW_TAG_module, getTag("DW_TAG_module"));// Invalid tags.EXPECT_EQ(DW_TAG_invalid, getTag("DW_TAG_invalid"));EXPECT_EQ(DW_TAG_invalid, getTag("DW_TAG_madeuptag"));EXPECT_EQ(DW_TAG_invalid, getTag("something else"));// Tag range markers should not be recognized.EXPECT_EQ(DW_TAG_invalid, getTag("DW_TAG_lo_user"));EXPECT_EQ(DW_TAG_invalid, getTag("DW_TAG_hi_user"));EXPECT_EQ(DW_TAG_invalid, getTag("DW_TAG_user_base"));}TEST(DwarfTest, getOperationEncoding) {// Some valid ops.EXPECT_EQ(DW_OP_deref, getOperationEncoding("DW_OP_deref"));EXPECT_EQ(DW_OP_bit_piece, getOperationEncoding("DW_OP_bit_piece"));// Invalid ops.EXPECT_EQ(0u, getOperationEncoding("DW_OP_otherthings"));EXPECT_EQ(0u, getOperationEncoding("other"));// Markers shouldn't be recognized.EXPECT_EQ(0u, getOperationEncoding("DW_OP_lo_user"));EXPECT_EQ(0u, getOperationEncoding("DW_OP_hi_user"));}TEST(DwarfTest, LanguageStringOnInvalid) {// This is invalid, so it shouldn't be stringified.EXPECT_EQ(StringRef(), LanguageString(0));// These aren't really tags: they describe ranges within tags. They// shouldn't be stringified either.EXPECT_EQ(StringRef(), LanguageString(DW_LANG_lo_user));EXPECT_EQ(StringRef(), LanguageString(DW_LANG_hi_user));}TEST(DwarfTest, getLanguage) {// A couple of valid languages.EXPECT_EQ(DW_LANG_C89, getLanguage("DW_LANG_C89"));EXPECT_EQ(DW_LANG_C_plus_plus_11, getLanguage("DW_LANG_C_plus_plus_11"));EXPECT_EQ(DW_LANG_OCaml, getLanguage("DW_LANG_OCaml"));EXPECT_EQ(DW_LANG_Mips_Assembler, getLanguage("DW_LANG_Mips_Assembler"));// Invalid languages.EXPECT_EQ(0u, getLanguage("DW_LANG_invalid"));EXPECT_EQ(0u, getLanguage("DW_TAG_array_type"));EXPECT_EQ(0u, getLanguage("something else"));// Language range markers should not be recognized.EXPECT_EQ(0u, getLanguage("DW_LANG_lo_user"));EXPECT_EQ(0u, getLanguage("DW_LANG_hi_user"));}TEST(DwarfTest, AttributeEncodingStringOnInvalid) {// This is invalid, so it shouldn't be stringified.EXPECT_EQ(StringRef(), AttributeEncodingString(0));// These aren't really tags: they describe ranges within tags. They// shouldn't be stringified either.EXPECT_EQ(StringRef(), AttributeEncodingString(DW_ATE_lo_user));EXPECT_EQ(StringRef(), AttributeEncodingString(DW_ATE_hi_user));}TEST(DwarfTest, getAttributeEncoding) {// A couple of valid languages.EXPECT_EQ(DW_ATE_boolean, getAttributeEncoding("DW_ATE_boolean"));EXPECT_EQ(DW_ATE_imaginary_float,getAttributeEncoding("DW_ATE_imaginary_float"));// Invalid languages.EXPECT_EQ(0u, getAttributeEncoding("DW_ATE_invalid"));EXPECT_EQ(0u, getAttributeEncoding("DW_TAG_array_type"));EXPECT_EQ(0u, getAttributeEncoding("something else"));// AttributeEncoding range markers should not be recognized.EXPECT_EQ(0u, getAttributeEncoding("DW_ATE_lo_user"));EXPECT_EQ(0u, getAttributeEncoding("DW_ATE_hi_user"));}TEST(DwarfTest, VirtualityString) {EXPECT_EQ(StringRef("DW_VIRTUALITY_none"),VirtualityString(DW_VIRTUALITY_none));EXPECT_EQ(StringRef("DW_VIRTUALITY_virtual"),VirtualityString(DW_VIRTUALITY_virtual));EXPECT_EQ(StringRef("DW_VIRTUALITY_pure_virtual"),VirtualityString(DW_VIRTUALITY_pure_virtual));// DW_VIRTUALITY_max should be pure virtual.EXPECT_EQ(StringRef("DW_VIRTUALITY_pure_virtual"),VirtualityString(DW_VIRTUALITY_max));// Invalid numbers shouldn't be stringified.EXPECT_EQ(StringRef(), VirtualityString(DW_VIRTUALITY_max + 1));EXPECT_EQ(StringRef(), VirtualityString(DW_VIRTUALITY_max + 77));}TEST(DwarfTest, getVirtuality) {EXPECT_EQ(DW_VIRTUALITY_none, getVirtuality("DW_VIRTUALITY_none"));EXPECT_EQ(DW_VIRTUALITY_virtual, getVirtuality("DW_VIRTUALITY_virtual"));EXPECT_EQ(DW_VIRTUALITY_pure_virtual,getVirtuality("DW_VIRTUALITY_pure_virtual"));// Invalid strings.EXPECT_EQ(DW_VIRTUALITY_invalid, getVirtuality("DW_VIRTUALITY_invalid"));EXPECT_EQ(DW_VIRTUALITY_invalid, getVirtuality("DW_VIRTUALITY_max"));EXPECT_EQ(DW_VIRTUALITY_invalid, getVirtuality("something else"));}TEST(DwarfTest, FixedFormSizes) {Optional<uint8_t> RefSize;Optional<uint8_t> AddrSize;// Test 32 bit DWARF version 2 with 4 byte addresses.FormParams Params_2_4_32 = {2, 4, DWARF32};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_4_32);AddrSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_4_32);EXPECT_TRUE(RefSize.has_value());EXPECT_TRUE(AddrSize.has_value());EXPECT_EQ(*RefSize, *AddrSize);// Test 32 bit DWARF version 2 with 8 byte addresses.FormParams Params_2_8_32 = {2, 8, DWARF32};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_8_32);AddrSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_2_8_32);EXPECT_TRUE(RefSize.has_value());EXPECT_TRUE(AddrSize.has_value());EXPECT_EQ(*RefSize, *AddrSize);// DW_FORM_ref_addr is 4 bytes in DWARF 32 in DWARF version 3 and beyond.FormParams Params_3_4_32 = {3, 4, DWARF32};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_3_4_32);EXPECT_TRUE(RefSize.has_value());EXPECT_EQ(*RefSize, 4);FormParams Params_4_4_32 = {4, 4, DWARF32};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_4_4_32);EXPECT_TRUE(RefSize.has_value());EXPECT_EQ(*RefSize, 4);FormParams Params_5_4_32 = {5, 4, DWARF32};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_5_4_32);EXPECT_TRUE(RefSize.has_value());EXPECT_EQ(*RefSize, 4);// DW_FORM_ref_addr is 8 bytes in DWARF 64 in DWARF version 3 and beyond.FormParams Params_3_8_64 = {3, 8, DWARF64};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_3_8_64);EXPECT_TRUE(RefSize.has_value());EXPECT_EQ(*RefSize, 8);FormParams Params_4_8_64 = {4, 8, DWARF64};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_4_8_64);EXPECT_TRUE(RefSize.has_value());EXPECT_EQ(*RefSize, 8);FormParams Params_5_8_64 = {5, 8, DWARF64};RefSize = getFixedFormByteSize(DW_FORM_ref_addr, Params_5_8_64);EXPECT_TRUE(RefSize.has_value());EXPECT_EQ(*RefSize, 8);}TEST(DwarfTest, format_provider) {EXPECT_EQ("DW_AT_name", formatv("{0}", DW_AT_name).str());EXPECT_EQ("DW_AT_unknown_3fff", formatv("{0}", DW_AT_hi_user).str());EXPECT_EQ("DW_FORM_addr", formatv("{0}", DW_FORM_addr).str());EXPECT_EQ("DW_FORM_unknown_1f00", formatv("{0}", DW_FORM_lo_user).str());EXPECT_EQ("DW_IDX_compile_unit", formatv("{0}", DW_IDX_compile_unit).str());EXPECT_EQ("DW_IDX_unknown_3fff", formatv("{0}", DW_IDX_hi_user).str());EXPECT_EQ("DW_TAG_compile_unit", formatv("{0}", DW_TAG_compile_unit).str());EXPECT_EQ("DW_TAG_unknown_ffff", formatv("{0}", DW_TAG_hi_user).str());EXPECT_EQ("DW_OP_lit0", formatv("{0}", DW_OP_lit0).str());EXPECT_EQ("DW_OP_unknown_ff", formatv("{0}", DW_OP_hi_user).str());}} // end namespace
set(LLVM_LINK_COMPONENTSBinaryFormat)add_llvm_unittest(BinaryFormatTestsDwarfTest.cppMachOTest.cppMsgPackDocumentTest.cppMsgPackReaderTest.cppMsgPackWriterTest.cppTestFileMagic.cpp)
set(LLVM_LINK_COMPONENTSAsmParserCoreSupport)add_llvm_unittest(AsmParserTestsAsmParserTest.cpp)
//===- llvm/unittest/AsmParser/AsmParserTest.cpp - asm parser unittests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringRef.h"#include "llvm/AsmParser/Parser.h"#include "llvm/AsmParser/SlotMapping.h"#include "llvm/IR/Constants.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(AsmParserTest, NullTerminatedInput) {LLVMContext Ctx;StringRef Source = "; Empty module \n";SMDiagnostic Error;auto Mod = parseAssemblyString(Source, Error, Ctx);EXPECT_TRUE(Mod != nullptr);EXPECT_TRUE(Error.getMessage().empty());}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST(AsmParserTest, NonNullTerminatedInput) {LLVMContext Ctx;StringRef Source = "; Empty module \n\1\2";SMDiagnostic Error;std::unique_ptr<Module> Mod;EXPECT_DEATH(Mod = parseAssemblyString(Source.substr(0, Source.size() - 2),Error, Ctx),"Buffer is not null terminated!");}#endif#endifTEST(AsmParserTest, SlotMappingTest) {LLVMContext Ctx;StringRef Source = "@0 = global i32 0\n !0 = !{}\n !42 = !{i32 42}";SMDiagnostic Error;SlotMapping Mapping;auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping);EXPECT_TRUE(Mod != nullptr);EXPECT_TRUE(Error.getMessage().empty());ASSERT_EQ(Mapping.GlobalValues.size(), 1u);EXPECT_TRUE(isa<GlobalVariable>(Mapping.GlobalValues[0]));EXPECT_EQ(Mapping.MetadataNodes.size(), 2u);EXPECT_EQ(Mapping.MetadataNodes.count(0), 1u);EXPECT_EQ(Mapping.MetadataNodes.count(42), 1u);EXPECT_EQ(Mapping.MetadataNodes.count(1), 0u);}TEST(AsmParserTest, TypeAndConstantValueParsing) {LLVMContext Ctx;SMDiagnostic Error;StringRef Source = "define void @test() {\n entry:\n ret void\n}";auto Mod = parseAssemblyString(Source, Error, Ctx);ASSERT_TRUE(Mod != nullptr);auto &M = *Mod;const Value *V;V = parseConstantValue("double 3.5", Error, M);ASSERT_TRUE(V);EXPECT_TRUE(V->getType()->isDoubleTy());ASSERT_TRUE(isa<ConstantFP>(V));EXPECT_TRUE(cast<ConstantFP>(V)->isExactlyValue(3.5));V = parseConstantValue("i32 42", Error, M);ASSERT_TRUE(V);EXPECT_TRUE(V->getType()->isIntegerTy());ASSERT_TRUE(isa<ConstantInt>(V));EXPECT_TRUE(cast<ConstantInt>(V)->equalsInt(42));V = parseConstantValue("<4 x i32> <i32 0, i32 1, i32 2, i32 3>", Error, M);ASSERT_TRUE(V);EXPECT_TRUE(V->getType()->isVectorTy());ASSERT_TRUE(isa<ConstantDataVector>(V));V = parseConstantValue("i32 add (i32 1, i32 2)", Error, M);ASSERT_TRUE(V);ASSERT_TRUE(isa<ConstantInt>(V));V = parseConstantValue("i8* blockaddress(@test, %entry)", Error, M);ASSERT_TRUE(V);ASSERT_TRUE(isa<BlockAddress>(V));V = parseConstantValue("i8** undef", Error, M);ASSERT_TRUE(V);ASSERT_TRUE(isa<UndefValue>(V));EXPECT_FALSE(parseConstantValue("duble 3.25", Error, M));EXPECT_EQ(Error.getMessage(), "expected type");EXPECT_FALSE(parseConstantValue("i32 3.25", Error, M));EXPECT_EQ(Error.getMessage(), "floating point constant invalid for type");EXPECT_FALSE(parseConstantValue("i32* @foo", Error, M));EXPECT_EQ(Error.getMessage(), "expected a constant value");EXPECT_FALSE(parseConstantValue("i32 3, ", Error, M));EXPECT_EQ(Error.getMessage(), "expected end of string");}TEST(AsmParserTest, TypeAndConstantValueWithSlotMappingParsing) {LLVMContext Ctx;SMDiagnostic Error;StringRef Source ="%st = type { i32, i32 }\n""@v = common global [50 x %st] zeroinitializer, align 16\n""%0 = type { i32, i32, i32, i32 }\n""@g = common global [50 x %0] zeroinitializer, align 16\n""define void @marker4(i64 %d) {\n""entry:\n"" %conv = trunc i64 %d to i32\n"" store i32 %conv, i32* getelementptr inbounds "" ([50 x %st], [50 x %st]* @v, i64 0, i64 0, i32 0), align 16\n"" store i32 %conv, i32* getelementptr inbounds "" ([50 x %0], [50 x %0]* @g, i64 0, i64 0, i32 0), align 16\n"" ret void\n""}";SlotMapping Mapping;auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping);ASSERT_TRUE(Mod != nullptr);auto &M = *Mod;const Value *V;V = parseConstantValue("i32* getelementptr inbounds ([50 x %st], [50 x %st]* ""@v, i64 0, i64 0, i32 0)",Error, M, &Mapping);ASSERT_TRUE(V);ASSERT_TRUE(isa<ConstantExpr>(V));V = parseConstantValue("i32* getelementptr inbounds ([50 x %0], [50 x %0]* ""@g, i64 0, i64 0, i32 0)",Error, M, &Mapping);ASSERT_TRUE(V);ASSERT_TRUE(isa<ConstantExpr>(V));}TEST(AsmParserTest, TypeWithSlotMappingParsing) {LLVMContext Ctx;SMDiagnostic Error;StringRef Source ="%st = type { i32, i32 }\n""@v = common global [50 x %st] zeroinitializer, align 16\n""%0 = type { i32, i32, i32, i32 }\n""@g = common global [50 x %0] zeroinitializer, align 16\n""define void @marker4(i64 %d) {\n""entry:\n"" %conv = trunc i64 %d to i32\n"" store i32 %conv, i32* getelementptr inbounds "" ([50 x %st], [50 x %st]* @v, i64 0, i64 0, i32 0), align 16\n"" store i32 %conv, i32* getelementptr inbounds "" ([50 x %0], [50 x %0]* @g, i64 0, i64 0, i32 0), align 16\n"" ret void\n""}";SlotMapping Mapping;auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping);ASSERT_TRUE(Mod != nullptr);auto &M = *Mod;// Check we properly parse integer types.Type *Ty;Ty = parseType("i32", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);// Check we properly parse integer types with exotic size.Ty = parseType("i13", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13);// Check we properly parse floating point types.Ty = parseType("float", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isFloatTy());Ty = parseType("double", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isDoubleTy());// Check we properly parse struct types.// Named struct.Ty = parseType("%st", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isStructTy());// Check the details of the struct.StructType *ST = cast<StructType>(Ty);ASSERT_TRUE(ST->getNumElements() == 2);for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {Ty = ST->getElementType(i);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);}// Anonymous struct.Ty = parseType("%0", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isStructTy());// Check the details of the struct.ST = cast<StructType>(Ty);ASSERT_TRUE(ST->getNumElements() == 4);for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {Ty = ST->getElementType(i);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);}// Check we properly parse vector types.Ty = parseType("<5 x i32>", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isVectorTy());// Check the details of the vector.auto *VT = cast<FixedVectorType>(Ty);ASSERT_TRUE(VT->getNumElements() == 5);ASSERT_TRUE(VT->getPrimitiveSizeInBits().getFixedSize() == 160);Ty = VT->getElementType();ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);// Opaque struct.Ty = parseType("%opaque", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isStructTy());ST = cast<StructType>(Ty);ASSERT_TRUE(ST->isOpaque());// Check we properly parse pointer types.// One indirection.Ty = parseType("i32*", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isPointerTy());PointerType *PT = cast<PointerType>(Ty);ASSERT_TRUE(PT->isOpaqueOrPointeeTypeMatches(Type::getIntNTy(Ctx, 32)));// Two indirections.Ty = parseType("i32**", Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isPointerTy());PT = cast<PointerType>(Ty);Type *ExpectedElemTy = PointerType::getUnqual(Type::getIntNTy(Ctx, 32));ASSERT_TRUE(PT->isOpaqueOrPointeeTypeMatches(ExpectedElemTy));// Check that we reject types with garbage.Ty = parseType("i32 garbage", Error, M, &Mapping);ASSERT_TRUE(!Ty);}TEST(AsmParserTest, TypeAtBeginningWithSlotMappingParsing) {LLVMContext Ctx;SMDiagnostic Error;StringRef Source ="%st = type { i32, i32 }\n""@v = common global [50 x %st] zeroinitializer, align 16\n""%0 = type { i32, i32, i32, i32 }\n""@g = common global [50 x %0] zeroinitializer, align 16\n""define void @marker4(i64 %d) {\n""entry:\n"" %conv = trunc i64 %d to i32\n"" store i32 %conv, i32* getelementptr inbounds "" ([50 x %st], [50 x %st]* @v, i64 0, i64 0, i32 0), align 16\n"" store i32 %conv, i32* getelementptr inbounds "" ([50 x %0], [50 x %0]* @g, i64 0, i64 0, i32 0), align 16\n"" ret void\n""}";SlotMapping Mapping;auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping);ASSERT_TRUE(Mod != nullptr);auto &M = *Mod;unsigned Read;// Check we properly parse integer types.Type *Ty;Ty = parseTypeAtBeginning("i32", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);ASSERT_TRUE(Read == 3);// Check we properly parse integer types with exotic size.Ty = parseTypeAtBeginning("i13", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13);ASSERT_TRUE(Read == 3);// Check we properly parse floating point types.Ty = parseTypeAtBeginning("float", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isFloatTy());ASSERT_TRUE(Read == 5);Ty = parseTypeAtBeginning("double", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isDoubleTy());ASSERT_TRUE(Read == 6);// Check we properly parse struct types.// Named struct.Ty = parseTypeAtBeginning("%st", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isStructTy());ASSERT_TRUE(Read == 3);// Check the details of the struct.StructType *ST = cast<StructType>(Ty);ASSERT_TRUE(ST->getNumElements() == 2);for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {Ty = ST->getElementType(i);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);}// Anonymous struct.Ty = parseTypeAtBeginning("%0", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isStructTy());ASSERT_TRUE(Read == 2);// Check the details of the struct.ST = cast<StructType>(Ty);ASSERT_TRUE(ST->getNumElements() == 4);for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {Ty = ST->getElementType(i);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);}// Check we properly parse vector types.Ty = parseTypeAtBeginning("<5 x i32>", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isVectorTy());ASSERT_TRUE(Read == 9);// Check the details of the vector.auto *VT = cast<FixedVectorType>(Ty);ASSERT_TRUE(VT->getNumElements() == 5);ASSERT_TRUE(VT->getPrimitiveSizeInBits().getFixedSize() == 160);Ty = VT->getElementType();ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);// Opaque struct.Ty = parseTypeAtBeginning("%opaque", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isStructTy());ASSERT_TRUE(Read == 7);ST = cast<StructType>(Ty);ASSERT_TRUE(ST->isOpaque());// Check we properly parse pointer types.// One indirection.Ty = parseTypeAtBeginning("i32*", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isPointerTy());ASSERT_TRUE(Read == 4);PointerType *PT = cast<PointerType>(Ty);ASSERT_TRUE(PT->isOpaqueOrPointeeTypeMatches(Type::getIntNTy(Ctx, 32)));// Two indirections.Ty = parseTypeAtBeginning("i32**", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isPointerTy());ASSERT_TRUE(Read == 5);PT = cast<PointerType>(Ty);Type *ExpectedElemTy = PointerType::getUnqual(Type::getIntNTy(Ctx, 32));ASSERT_TRUE(PT->isOpaqueOrPointeeTypeMatches(ExpectedElemTy));// Check that we reject types with garbage.Ty = parseTypeAtBeginning("i32 garbage", Read, Error, M, &Mapping);ASSERT_TRUE(Ty);ASSERT_TRUE(Ty->isIntegerTy());ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32);// We go to the next token, i.e., we read "i32" + ' '.ASSERT_TRUE(Read == 4);}} // end anonymous namespace
//===- VectorUtilsTest.cpp - VectorUtils tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/VectorUtils.h"#include "llvm/Analysis/ValueTracking.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Function.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/NoFolder.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/KnownBits.h"#include "gtest/gtest.h"using namespace llvm;namespace {class VectorUtilsTest : public testing::Test {protected:void parseAssembly(const char *Assembly) {SMDiagnostic Error;M = parseAssemblyString(Assembly, Error, Context);std::string errMsg;raw_string_ostream os(errMsg);Error.print("", os);// A failure here means that the test itself is buggy.if (!M)report_fatal_error(Twine(os.str()));Function *F = M->getFunction("test");if (F == nullptr)report_fatal_error("Test must have a function named @test");A = nullptr;for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {if (I->hasName()) {if (I->getName() == "A")A = &*I;}}if (A == nullptr)report_fatal_error("@test must have an instruction %A");}LLVMContext Context;std::unique_ptr<Module> M;Instruction *A;};struct BasicTest : public testing::Test {LLVMContext Ctx;std::unique_ptr<Module> M;Function *F;BasicBlock *BB;IRBuilder<NoFolder> IRB;BasicTest(): M(new Module("VectorUtils", Ctx)),F(Function::Create(FunctionType::get(Type::getVoidTy(Ctx), /* IsVarArg */ false),Function::ExternalLinkage, "f", M.get())),BB(BasicBlock::Create(Ctx, "entry", F)), IRB(BB) {}};} // namespaceTEST_F(BasicTest, isSplat) {Value *UndefVec = UndefValue::get(FixedVectorType::get(IRB.getInt8Ty(), 4));EXPECT_TRUE(isSplatValue(UndefVec));Constant *UndefScalar = UndefValue::get(IRB.getInt8Ty());EXPECT_FALSE(isSplatValue(UndefScalar));Constant *ScalarC = IRB.getInt8(42);EXPECT_FALSE(isSplatValue(ScalarC));Constant *OtherScalarC = IRB.getInt8(-42);Constant *NonSplatC = ConstantVector::get({ScalarC, OtherScalarC});EXPECT_FALSE(isSplatValue(NonSplatC));Value *SplatC = IRB.CreateVectorSplat(5, ScalarC);EXPECT_TRUE(isSplatValue(SplatC));Value *SplatC_SVE =IRB.CreateVectorSplat(ElementCount::getScalable(5), ScalarC);EXPECT_TRUE(isSplatValue(SplatC_SVE));// FIXME: Constant splat analysis does not allow undef elements.Constant *SplatWithUndefC = ConstantVector::get({ScalarC, UndefScalar});EXPECT_FALSE(isSplatValue(SplatWithUndefC));}TEST_F(BasicTest, narrowShuffleMaskElts) {SmallVector<int, 16> ScaledMask;narrowShuffleMaskElts(1, {3,2,0,-2}, ScaledMask);EXPECT_EQ(makeArrayRef(ScaledMask), makeArrayRef({3,2,0,-2}));narrowShuffleMaskElts(4, {3,2,0,-1}, ScaledMask);EXPECT_EQ(makeArrayRef(ScaledMask), makeArrayRef({12,13,14,15,8,9,10,11,0,1,2,3,-1,-1,-1,-1}));}TEST_F(BasicTest, widenShuffleMaskElts) {SmallVector<int, 16> WideMask;SmallVector<int, 16> NarrowMask;// scale == 1 is a copyEXPECT_TRUE(widenShuffleMaskElts(1, {3,2,0,-1}, WideMask));EXPECT_EQ(makeArrayRef(WideMask), makeArrayRef({3,2,0,-1}));// back to original masknarrowShuffleMaskElts(1, makeArrayRef(WideMask), NarrowMask);EXPECT_EQ(makeArrayRef(NarrowMask), makeArrayRef({3,2,0,-1}));// can't widen non-consecutive 3/2EXPECT_FALSE(widenShuffleMaskElts(2, {3,2,0,-1}, WideMask));// can't widen if not evenly divisibleEXPECT_FALSE(widenShuffleMaskElts(2, {0,1,2}, WideMask));// can always widen identity to single elementEXPECT_TRUE(widenShuffleMaskElts(3, {0,1,2}, WideMask));EXPECT_EQ(makeArrayRef(WideMask), makeArrayRef({0}));// back to original masknarrowShuffleMaskElts(3, makeArrayRef(WideMask), NarrowMask);EXPECT_EQ(makeArrayRef(NarrowMask), makeArrayRef({0,1,2}));// groups of 4 must be consecutive/undefEXPECT_TRUE(widenShuffleMaskElts(4, {12,13,14,15,8,9,10,11,0,1,2,3,-1,-1,-1,-1}, WideMask));EXPECT_EQ(makeArrayRef(WideMask), makeArrayRef({3,2,0,-1}));// back to original masknarrowShuffleMaskElts(4, makeArrayRef(WideMask), NarrowMask);EXPECT_EQ(makeArrayRef(NarrowMask), makeArrayRef({12,13,14,15,8,9,10,11,0,1,2,3,-1,-1,-1,-1}));// groups of 2 must be consecutive/undefEXPECT_FALSE(widenShuffleMaskElts(2, {12,12,14,15,8,9,10,11,0,1,2,3,-1,-1,-1,-1}, WideMask));// groups of 3 must be consecutive/undefEXPECT_TRUE(widenShuffleMaskElts(3, {6,7,8,0,1,2,-1,-1,-1}, WideMask));EXPECT_EQ(makeArrayRef(WideMask), makeArrayRef({2,0,-1}));// back to original masknarrowShuffleMaskElts(3, makeArrayRef(WideMask), NarrowMask);EXPECT_EQ(makeArrayRef(NarrowMask), makeArrayRef({6,7,8,0,1,2,-1,-1,-1}));// groups of 3 must be consecutive/undef (partial undefs are not ok)EXPECT_FALSE(widenShuffleMaskElts(3, {-1,7,8,0,-1,2,-1,-1,-1}, WideMask));// negative indexes must match across a wide elementEXPECT_FALSE(widenShuffleMaskElts(2, {-1,-2,-1,-1}, WideMask));// negative indexes must match across a wide elementEXPECT_TRUE(widenShuffleMaskElts(2, {-2,-2,-3,-3}, WideMask));EXPECT_EQ(makeArrayRef(WideMask), makeArrayRef({-2,-3}));}TEST_F(BasicTest, getSplatIndex) {EXPECT_EQ(getSplatIndex({0,0,0}), 0);EXPECT_EQ(getSplatIndex({1,0,0}), -1); // no splatEXPECT_EQ(getSplatIndex({0,1,1}), -1); // no splatEXPECT_EQ(getSplatIndex({42,42,42}), 42); // array size is independent of splat indexEXPECT_EQ(getSplatIndex({42,42,-1}), 42); // ignore negativeEXPECT_EQ(getSplatIndex({-1,42,-1}), 42); // ignore negativesEXPECT_EQ(getSplatIndex({-4,42,-42}), 42); // ignore all negativesEXPECT_EQ(getSplatIndex({-4,-1,-42}), -1); // all negative values map to -1}TEST_F(VectorUtilsTest, isSplatValue_00) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> zeroinitializer\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_00_index0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> zeroinitializer\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A, 0));}TEST_F(VectorUtilsTest, isSplatValue_00_index1) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> zeroinitializer\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 1));}TEST_F(VectorUtilsTest, isSplatValue_11) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_11_index0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 0));}TEST_F(VectorUtilsTest, isSplatValue_11_index1) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A, 1));}TEST_F(VectorUtilsTest, isSplatValue_01) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_01_index0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 0));}TEST_F(VectorUtilsTest, isSplatValue_01_index1) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 1));}// FIXME: Allow undef matching with Constant (mask) splat analysis.TEST_F(VectorUtilsTest, isSplatValue_0u) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 undef>\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A));}// FIXME: Allow undef matching with Constant (mask) splat analysis.TEST_F(VectorUtilsTest, isSplatValue_0u_index0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 undef>\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 0));}TEST_F(VectorUtilsTest, isSplatValue_0u_index1) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 undef>\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 1));}TEST_F(VectorUtilsTest, isSplatValue_Binop) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = udiv <2 x i8> %v0, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Binop_index0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = udiv <2 x i8> %v0, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 0));}TEST_F(VectorUtilsTest, isSplatValue_Binop_index1) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = udiv <2 x i8> %v0, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 1));}TEST_F(VectorUtilsTest, isSplatValue_Binop_ConstantOp0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = ashr <2 x i8> <i8 42, i8 42>, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Binop_ConstantOp0_index0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = ashr <2 x i8> <i8 42, i8 42>, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A, 0));}TEST_F(VectorUtilsTest, isSplatValue_Binop_ConstantOp0_index1) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = ashr <2 x i8> <i8 42, i8 42>, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A, 1));}TEST_F(VectorUtilsTest, isSplatValue_Binop_Not_Op0) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 0>\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = add <2 x i8> %v0, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Binop_Not_Op1) {parseAssembly("define <2 x i8> @test(<2 x i8> %x) {\n"" %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"" %A = shl <2 x i8> %v0, %v1\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Select) {parseAssembly("define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"" %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"" %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"" %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = select <2 x i1> %v0, <2 x i8> %v1, <2 x i8> %v2\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Select_ConstantOp) {parseAssembly("define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"" %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"" %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = select <2 x i1> %v0, <2 x i8> <i8 42, i8 42>, <2 x i8> %v2\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Select_NotCond) {parseAssembly("define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"" %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"" %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = select <2 x i1> %x, <2 x i8> %v1, <2 x i8> %v2\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Select_NotOp1) {parseAssembly("define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"" %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"" %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %A = select <2 x i1> %v0, <2 x i8> %y, <2 x i8> %v2\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_Select_NotOp2) {parseAssembly("define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"" %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"" %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"" %A = select <2 x i1> %v0, <2 x i8> %v1, <2 x i8> %z\n"" ret <2 x i8> %A\n""}\n");EXPECT_FALSE(isSplatValue(A));}TEST_F(VectorUtilsTest, isSplatValue_SelectBinop) {parseAssembly("define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"" %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"" %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"" %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" %bo = xor <2 x i8> %v1, %v2\n"" %A = select <2 x i1> %v0, <2 x i8> %bo, <2 x i8> %v2\n"" ret <2 x i8> %A\n""}\n");EXPECT_TRUE(isSplatValue(A));}TEST_F(VectorUtilsTest, getSplatValueElt0) {parseAssembly("define <2 x i8> @test(i8 %x) {\n"" %ins = insertelement <2 x i8> undef, i8 %x, i32 0\n"" %A = shufflevector <2 x i8> %ins, <2 x i8> undef, <2 x i32> zeroinitializer\n"" ret <2 x i8> %A\n""}\n");EXPECT_EQ(getSplatValue(A)->getName(), "x");}TEST_F(VectorUtilsTest, getSplatValueEltMismatch) {parseAssembly("define <2 x i8> @test(i8 %x) {\n"" %ins = insertelement <2 x i8> undef, i8 %x, i32 1\n"" %A = shufflevector <2 x i8> %ins, <2 x i8> undef, <2 x i32> zeroinitializer\n"" ret <2 x i8> %A\n""}\n");EXPECT_EQ(getSplatValue(A), nullptr);}// TODO: This is a splat, but we don't recognize it.TEST_F(VectorUtilsTest, getSplatValueElt1) {parseAssembly("define <2 x i8> @test(i8 %x) {\n"" %ins = insertelement <2 x i8> undef, i8 %x, i32 1\n"" %A = shufflevector <2 x i8> %ins, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"" ret <2 x i8> %A\n""}\n");EXPECT_EQ(getSplatValue(A), nullptr);}////////////////////////////////////////////////////////////////////////////////// VFShape API tests.////////////////////////////////////////////////////////////////////////////////class VFShapeAPITest : public testing::Test {protected:void SetUp() override {M = parseAssemblyString(IR, Err, Ctx);// Get the only call instruction in the block, which is the first// instruction.CI = dyn_cast<CallInst>(&*(instructions(M->getFunction("f")).begin()));}const char *IR = "define i32 @f(i32 %a, i64 %b, double %c) {\n"" %1 = call i32 @g(i32 %a, i64 %b, double %c)\n"" ret i32 %1\n""}\n""declare i32 @g(i32, i64, double)\n";LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M;CallInst *CI;// Dummy shape with no parameters, overwritten by buildShape when invoked.VFShape Shape = {/*VF*/ ElementCount::getFixed(2), /*Parameters*/ {}};VFShape Expected;SmallVector<VFParameter, 8> &ExpectedParams = Expected.Parameters;void buildShape(ElementCount VF, bool HasGlobalPred) {Shape = VFShape::get(*CI, VF, HasGlobalPred);}bool validParams(ArrayRef<VFParameter> Parameters) {Shape.Parameters =SmallVector<VFParameter, 8>(Parameters.begin(), Parameters.end());return Shape.hasValidParameterList();}};TEST_F(VFShapeAPITest, API_buildVFShape) {buildShape(/*VF*/ ElementCount::getFixed(2), /*HasGlobalPred*/ false);Expected = {/*VF*/ ElementCount::getFixed(2), /*Parameters*/ {{0, VFParamKind::Vector},{1, VFParamKind::Vector},{2, VFParamKind::Vector},}};EXPECT_EQ(Shape, Expected);buildShape(/*VF*/ ElementCount::getFixed(4), /*HasGlobalPred*/ true);Expected = {/*VF*/ ElementCount::getFixed(4), /*Parameters*/ {{0, VFParamKind::Vector},{1, VFParamKind::Vector},{2, VFParamKind::Vector},{3, VFParamKind::GlobalPredicate},}};EXPECT_EQ(Shape, Expected);buildShape(/*VF*/ ElementCount::getScalable(16), /*HasGlobalPred*/ false);Expected = {/*VF*/ ElementCount::getScalable(16), /*Parameters*/ {{0, VFParamKind::Vector},{1, VFParamKind::Vector},{2, VFParamKind::Vector},}};EXPECT_EQ(Shape, Expected);}TEST_F(VFShapeAPITest, API_getScalarShape) {buildShape(/*VF*/ ElementCount::getFixed(1), /*HasGlobalPred*/ false);EXPECT_EQ(VFShape::getScalarShape(*CI), Shape);}TEST_F(VFShapeAPITest, API_getVectorizedFunction) {VFShape ScalarShape = VFShape::getScalarShape(*CI);EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(ScalarShape),M->getFunction("g"));buildShape(/*VF*/ ElementCount::getScalable(1), /*HasGlobalPred*/ false);EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(Shape), nullptr);buildShape(/*VF*/ ElementCount::getFixed(1), /*HasGlobalPred*/ true);EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(Shape), nullptr);buildShape(/*VF*/ ElementCount::getScalable(1), /*HasGlobalPred*/ true);EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(Shape), nullptr);}TEST_F(VFShapeAPITest, API_updateVFShape) {buildShape(/*VF*/ ElementCount::getFixed(2), /*HasGlobalPred*/ false);Shape.updateParam({0 /*Pos*/, VFParamKind::OMP_Linear, 1, Align(4)});Expected = {/*VF*/ ElementCount::getFixed(2), /*Parameters*/ {{0, VFParamKind::OMP_Linear, 1, Align(4)},{1, VFParamKind::Vector},{2, VFParamKind::Vector},}};EXPECT_EQ(Shape, Expected);// From this point on, we update only the parameters of the VFShape,// so we update only the reference of the expected Parameters.Shape.updateParam({1 /*Pos*/, VFParamKind::OMP_Uniform});ExpectedParams = {{0, VFParamKind::OMP_Linear, 1, Align(4)},{1, VFParamKind::OMP_Uniform},{2, VFParamKind::Vector},};EXPECT_EQ(Shape, Expected);Shape.updateParam({2 /*Pos*/, VFParamKind::OMP_LinearRefPos, 1});ExpectedParams = {{0, VFParamKind::OMP_Linear, 1, Align(4)},{1, VFParamKind::OMP_Uniform},{2, VFParamKind::OMP_LinearRefPos, 1},};EXPECT_EQ(Shape, Expected);}TEST_F(VFShapeAPITest, API_updateVFShape_GlobalPredicate) {buildShape(/*VF*/ ElementCount::getScalable(2), /*HasGlobalPred*/ true);Shape.updateParam({1 /*Pos*/, VFParamKind::OMP_Uniform});Expected = {/*VF*/ ElementCount::getScalable(2),/*Parameters*/ {{0, VFParamKind::Vector},{1, VFParamKind::OMP_Uniform},{2, VFParamKind::Vector},{3, VFParamKind::GlobalPredicate}}};EXPECT_EQ(Shape, Expected);}TEST_F(VFShapeAPITest, Parameters_Valid) {// ParamPos in order.EXPECT_TRUE(validParams({{0, VFParamKind::Vector}}));EXPECT_TRUE(validParams({{0, VFParamKind::Vector}, {1, VFParamKind::Vector}}));EXPECT_TRUE(validParams({{0, VFParamKind::Vector},{1, VFParamKind::Vector},{2, VFParamKind::Vector}}));// GlocalPredicate is unique.EXPECT_TRUE(validParams({{0, VFParamKind::Vector},{1, VFParamKind::Vector},{2, VFParamKind::Vector},{3, VFParamKind::GlobalPredicate}}));EXPECT_TRUE(validParams({{0, VFParamKind::Vector},{1, VFParamKind::GlobalPredicate},{2, VFParamKind::Vector}}));}TEST_F(VFShapeAPITest, Parameters_ValidOpenMPLinear) {// Valid linear constant step (>0).#define __BUILD_PARAMETERS(Kind, Val) \{ \{ 0, Kind, Val } \}EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_Linear, 1)));EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRef, 2)));EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearVal, 4)));EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUVal, 33)));#undef __BUILD_PARAMETERS// Valid linear runtime step (the step parameter is marked uniform).#define __BUILD_PARAMETERS(Kind) \{ \{0, VFParamKind::OMP_Uniform}, {1, VFParamKind::Vector}, { 2, Kind, 0 } \}EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));#undef __BUILD_PARAMETERS}TEST_F(VFShapeAPITest, Parameters_Invalid) {#ifndef NDEBUG// Wrong order is checked by an assertion: make sure that the// assertion is not removed.EXPECT_DEATH(validParams({{1, VFParamKind::Vector}}),"Broken parameter list.");EXPECT_DEATH(validParams({{1, VFParamKind::Vector}, {0, VFParamKind::Vector}}),"Broken parameter list.");#endif// GlobalPredicate is not uniqueEXPECT_FALSE(validParams({{0, VFParamKind::Vector},{1, VFParamKind::GlobalPredicate},{2, VFParamKind::GlobalPredicate}}));EXPECT_FALSE(validParams({{0, VFParamKind::GlobalPredicate},{1, VFParamKind::Vector},{2, VFParamKind::GlobalPredicate}}));}TEST_F(VFShapeAPITest, Parameters_InvalidOpenMPLinear) {// Compile time linear steps must be non-zero (compile time invariant).#define __BUILD_PARAMETERS(Kind) \{ \{ 0, Kind, 0 } \}EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_Linear)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRef)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearVal)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUVal)));#undef __BUILD_PARAMETERS// The step of a runtime linear parameter must be marked// as uniform (runtime invariant).#define __BUILD_PARAMETERS(Kind) \{ \{0, VFParamKind::OMP_Uniform}, {1, VFParamKind::Vector}, { 2, Kind, 1 } \}EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));#undef __BUILD_PARAMETERS// The linear step parameter can't point at itself.#define __BUILD_PARAMETERS(Kind) \{ \{0, VFParamKind::Vector}, {1, VFParamKind::Vector}, { 2, Kind, 2 } \}EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));#undef __BUILD_PARAMETERS// Linear parameter (runtime) is out of range.#define __BUILD_PARAMETERS(Kind) \{ \{0, VFParamKind::Vector}, {1, VFParamKind::Vector}, { 2, Kind, 3 } \}EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));#undef __BUILD_PARAMETERS}
//===------- VectorFunctionABITest.cpp - VFABI Unittests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/VectorUtils.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/InstIterator.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Test fixture needed that holds the veariables needed by the parser.class VFABIParserTest : public ::testing::Test {private:// Parser output.VFInfo Info;// Reset the data needed for the test.void reset(const StringRef Name, const StringRef IRType) {M = parseAssemblyString("declare void @dummy()", Err, Ctx);EXPECT_NE(M.get(), nullptr) << "Loading an invalid module.\n "<< Err.getMessage() << "\n";Type *Ty = parseType(IRType, Err, *(M.get()));FunctionType *FTy = dyn_cast<FunctionType>(Ty);EXPECT_NE(FTy, nullptr) << "Invalid function type string: " << IRType<< "\n"<< Err.getMessage() << "\n";FunctionCallee F = M->getOrInsertFunction(Name, FTy);EXPECT_NE(F.getCallee(), nullptr)<< "The function must be present in the module\n";// Reset the VFInfoInfo = VFInfo();}// Data needed to load the optional IR passed to invokeParserLLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M;// CallInst *CI;protected:// Referencies to the parser output field.ElementCount &VF = Info.Shape.VF;VFISAKind &ISA = Info.ISA;SmallVector<VFParameter, 8> &Parameters = Info.Shape.Parameters;std::string &ScalarName = Info.ScalarName;std::string &VectorName = Info.VectorName;// Invoke the parser. We need to make sure that a function exist in// the module because the parser fails if such function don't// exists. Every time this method is invoked the state of the test// is reset.//// \p MangledName -> the string the parser has to demangle.//// \p VectorName -> optional vector name that the method needs to// use to create the function in the module if it differs from the// standard mangled name.//// \p IRType -> FunctionType string to be used for the signature of// the vector function. The correct signature is needed by the// parser only for scalable functions. For the sake of testing, the// generic fixed-length case can use as signature `void()`.//bool invokeParser(const StringRef MangledName,const StringRef VectorName = "",const StringRef IRType = "void()") {StringRef Name = MangledName;if (!VectorName.empty())Name = VectorName;// Reset the VFInfo and the Module to be able to invoke// `invokeParser` multiple times in the same test.reset(Name, IRType);const auto OptInfo = VFABI::tryDemangleForVFABI(MangledName, *(M.get()));if (OptInfo) {Info = OptInfo.value();return true;}return false;}// Checks that 1. the last Parameter in the Shape is of type// VFParamKind::GlobalPredicate and 2. it is the only one of such// type.bool IsMasked() const {const auto NGlobalPreds =std::count_if(Info.Shape.Parameters.begin(),Info.Shape.Parameters.end(), [](const VFParameter PK) {return PK.ParamKind == VFParamKind::GlobalPredicate;});return NGlobalPreds == 1 && Info.Shape.Parameters.back().ParamKind ==VFParamKind::GlobalPredicate;}};} // unnamed namespace// This test makes sure correct mangling occurs for given string.TEST_F(VFABIParserTest, ManglingVectorTLINames) {EXPECT_EQ(VFABI::mangleTLIVectorName("vec", "scalar", 3, ElementCount::getFixed(4)),"_ZGV_LLVM_N4vvv_scalar(vec)");EXPECT_EQ(VFABI::mangleTLIVectorName("vec", "scalar", 3,ElementCount::getScalable(4)),"_ZGV_LLVM_Nxvvv_scalar(vec)");EXPECT_EQ(VFABI::mangleTLIVectorName("custom.call.v5", "custom.call", 1,ElementCount::getFixed(5)),"_ZGV_LLVM_N5v_custom.call(custom.call.v5)");}// This test makes sure that the demangling method succeeds only on// valid values of the string.TEST_F(VFABIParserTest, OnlyValidNames) {// Incomplete string.EXPECT_FALSE(invokeParser(""));EXPECT_FALSE(invokeParser("_ZGV"));EXPECT_FALSE(invokeParser("_ZGVn"));EXPECT_FALSE(invokeParser("_ZGVnN"));EXPECT_FALSE(invokeParser("_ZGVnN2"));EXPECT_FALSE(invokeParser("_ZGVnN2v"));EXPECT_FALSE(invokeParser("_ZGVnN2v_"));// Missing parameters.EXPECT_FALSE(invokeParser("_ZGVnN2_foo"));// Missing _ZGV prefix.EXPECT_FALSE(invokeParser("_ZVnN2v_foo"));// Missing <isa>.EXPECT_FALSE(invokeParser("_ZGVN2v_foo"));// Missing <mask>.EXPECT_FALSE(invokeParser("_ZGVn2v_foo"));// Missing <vlen>.EXPECT_FALSE(invokeParser("_ZGVnNv_foo"));// Missing <scalarname>.EXPECT_FALSE(invokeParser("_ZGVnN2v_"));// Missing _ separator.EXPECT_FALSE(invokeParser("_ZGVnN2vfoo"));// Missing <vectorname>. Using `fakename` because the string being// parsed is not a valid function name that `invokeParser` can add.EXPECT_FALSE(invokeParser("_ZGVnN2v_foo()", "fakename"));// Unterminated name. Using `fakename` because the string being// parsed is not a valid function name that `invokeParser` can add.EXPECT_FALSE(invokeParser("_ZGVnN2v_foo(bar", "fakename"));}TEST_F(VFABIParserTest, ParamListParsing) {EXPECT_TRUE(invokeParser("_ZGVnN2vl16Ls32R3l_foo"));EXPECT_EQ(Parameters.size(), (unsigned)5);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_Linear, 16}));EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearValPos, 32}));EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearRef, 3}));EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_Linear, 1}));}TEST_F(VFABIParserTest, ScalarNameAndVectorName_01) {EXPECT_TRUE(invokeParser("_ZGVnM2v_sin"));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "_ZGVnM2v_sin");}TEST_F(VFABIParserTest, ScalarNameAndVectorName_02) {EXPECT_TRUE(invokeParser("_ZGVnM2v_sin(UserFunc)", "UserFunc"));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "UserFunc");}TEST_F(VFABIParserTest, ScalarNameAndVectorName_03) {EXPECT_TRUE(invokeParser("_ZGVnM2v___sin_sin_sin"));EXPECT_EQ(ScalarName, "__sin_sin_sin");EXPECT_EQ(VectorName, "_ZGVnM2v___sin_sin_sin");}TEST_F(VFABIParserTest, Parse) {EXPECT_TRUE(invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_FALSE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);EXPECT_EQ(Parameters.size(), (unsigned)9);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearPos, 2}));EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearValPos, 27}));EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearUValPos, 4}));EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_LinearRefPos, 5}));EXPECT_EQ(Parameters[5], VFParameter({5, VFParamKind::OMP_Linear, 1}));EXPECT_EQ(Parameters[6], VFParameter({6, VFParamKind::OMP_LinearVal, 10}));EXPECT_EQ(Parameters[7], VFParameter({7, VFParamKind::OMP_LinearUVal, 100}));EXPECT_EQ(Parameters[8], VFParameter({8, VFParamKind::OMP_LinearRef, 1000}));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_sin");}TEST_F(VFABIParserTest, ParseVectorName) {EXPECT_TRUE(invokeParser("_ZGVnN2v_sin(my_v_sin)", "my_v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_FALSE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);EXPECT_EQ(Parameters.size(), (unsigned)1);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0}));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "my_v_sin");}TEST_F(VFABIParserTest, LinearWithCompileTimeNegativeStep) {EXPECT_TRUE(invokeParser("_ZGVnN2ln1Ln10Un100Rn1000_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_FALSE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);EXPECT_EQ(Parameters.size(), (unsigned)4);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, -1}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearVal, -10}));EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearUVal, -100}));EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearRef, -1000}));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "_ZGVnN2ln1Ln10Un100Rn1000_sin");}TEST_F(VFABIParserTest, ParseScalableSVE) {EXPECT_TRUE(invokeParser("_ZGVsMxv_sin(custom_vg)", "custom_vg","<vscale x 2 x i32>(<vscale x 2 x i32>, <vscale x 2 x i1>)"));EXPECT_EQ(VF, ElementCount::getScalable(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::SVE);EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "custom_vg");}TEST_F(VFABIParserTest, ParseFixedWidthSVE) {EXPECT_TRUE(invokeParser("_ZGVsM2v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::SVE);EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "_ZGVsM2v_sin");}TEST_F(VFABIParserTest, NotAVectorFunctionABIName) {// Vector names should start with `_ZGV`.EXPECT_FALSE(invokeParser("ZGVnN2v_sin"));}TEST_F(VFABIParserTest, LinearWithRuntimeStep) {EXPECT_FALSE(invokeParser("_ZGVnN2ls_sin"))<< "A number should be present after \"ls\".";EXPECT_TRUE(invokeParser("_ZGVnN2ls2_sin"));EXPECT_FALSE(invokeParser("_ZGVnN2Rs_sin"))<< "A number should be present after \"Rs\".";EXPECT_TRUE(invokeParser("_ZGVnN2Rs4_sin"));EXPECT_FALSE(invokeParser("_ZGVnN2Ls_sin"))<< "A number should be present after \"Ls\".";EXPECT_TRUE(invokeParser("_ZGVnN2Ls6_sin"));EXPECT_FALSE(invokeParser("_ZGVnN2Us_sin"))<< "A number should be present after \"Us\".";EXPECT_TRUE(invokeParser("_ZGVnN2Us8_sin"));}TEST_F(VFABIParserTest, LinearWithoutCompileTime) {EXPECT_TRUE(invokeParser("_ZGVnN3lLRUlnLnRnUn_sin"));EXPECT_EQ(Parameters.size(), (unsigned)8);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, 1}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearVal, 1}));EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearRef, 1}));EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearUVal, 1}));EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_Linear, -1}));EXPECT_EQ(Parameters[5], VFParameter({5, VFParamKind::OMP_LinearVal, -1}));EXPECT_EQ(Parameters[6], VFParameter({6, VFParamKind::OMP_LinearRef, -1}));EXPECT_EQ(Parameters[7], VFParameter({7, VFParamKind::OMP_LinearUVal, -1}));}TEST_F(VFABIParserTest, ISA) {EXPECT_TRUE(invokeParser("_ZGVqN2v_sin"));EXPECT_EQ(ISA, VFISAKind::Unknown);EXPECT_TRUE(invokeParser("_ZGVnN2v_sin"));EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);EXPECT_TRUE(invokeParser("_ZGVsN2v_sin"));EXPECT_EQ(ISA, VFISAKind::SVE);EXPECT_TRUE(invokeParser("_ZGVbN2v_sin"));EXPECT_EQ(ISA, VFISAKind::SSE);EXPECT_TRUE(invokeParser("_ZGVcN2v_sin"));EXPECT_EQ(ISA, VFISAKind::AVX);EXPECT_TRUE(invokeParser("_ZGVdN2v_sin"));EXPECT_EQ(ISA, VFISAKind::AVX2);EXPECT_TRUE(invokeParser("_ZGVeN2v_sin"));EXPECT_EQ(ISA, VFISAKind::AVX512);}TEST_F(VFABIParserTest, LLVM_ISA) {EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_sin"));EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_sin_(vector_name)", "vector_name"));EXPECT_EQ(ISA, VFISAKind::LLVM);}TEST_F(VFABIParserTest, InvalidMask) {EXPECT_FALSE(invokeParser("_ZGVsK2v_sin"));}TEST_F(VFABIParserTest, InvalidParameter) {EXPECT_FALSE(invokeParser("_ZGVsM2vX_sin"));}TEST_F(VFABIParserTest, Align) {EXPECT_TRUE(invokeParser("_ZGVsN2l2a2_sin"));EXPECT_EQ(Parameters.size(), (unsigned)1);EXPECT_EQ(Parameters[0].Alignment, Align(2));// Missing alignment value.EXPECT_FALSE(invokeParser("_ZGVsM2l2a_sin"));// Invalid alignment token "x".EXPECT_FALSE(invokeParser("_ZGVsM2l2ax_sin"));// Alignment MUST be associated to a paramater.EXPECT_FALSE(invokeParser("_ZGVsM2a2_sin"));// Alignment must be a power of 2.EXPECT_FALSE(invokeParser("_ZGVsN2l2a0_sin"));EXPECT_TRUE(invokeParser("_ZGVsN2l2a1_sin"));EXPECT_FALSE(invokeParser("_ZGVsN2l2a3_sin"));EXPECT_FALSE(invokeParser("_ZGVsN2l2a6_sin"));}TEST_F(VFABIParserTest, ParseUniform) {EXPECT_TRUE(invokeParser("_ZGVnN2u_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_FALSE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);EXPECT_EQ(Parameters.size(), (unsigned)1);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Uniform, 0}));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "_ZGVnN2u_sin");// Uniform doesn't expect extra data.EXPECT_FALSE(invokeParser("_ZGVnN2u0_sin"));}TEST_F(VFABIParserTest, ISAIndependentMangling) {// This test makes sure that the mangling of the parameters in// independent on the <isa> token.const SmallVector<VFParameter, 8> ExpectedParams = {VFParameter({0, VFParamKind::Vector, 0}),VFParameter({1, VFParamKind::OMP_LinearPos, 2}),VFParameter({2, VFParamKind::OMP_LinearValPos, 27}),VFParameter({3, VFParamKind::OMP_LinearUValPos, 4}),VFParameter({4, VFParamKind::OMP_LinearRefPos, 5}),VFParameter({5, VFParamKind::OMP_Linear, 1}),VFParameter({6, VFParamKind::OMP_LinearVal, 10}),VFParameter({7, VFParamKind::OMP_LinearUVal, 100}),VFParameter({8, VFParamKind::OMP_LinearRef, 1000}),VFParameter({9, VFParamKind::OMP_Uniform, 0}),};#define __COMMON_CHECKS \do { \EXPECT_EQ(VF, ElementCount::getFixed(2)); \EXPECT_FALSE(IsMasked()); \EXPECT_EQ(Parameters.size(), (unsigned)10); \EXPECT_EQ(Parameters, ExpectedParams); \EXPECT_EQ(ScalarName, "sin"); \} while (0)// Advanced SIMD: <isa> = "n"EXPECT_TRUE(invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000u_sin"));EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);__COMMON_CHECKS;EXPECT_EQ(VectorName, "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000u_sin");// SVE: <isa> = "s"EXPECT_TRUE(invokeParser("_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u_sin"));EXPECT_EQ(ISA, VFISAKind::SVE);__COMMON_CHECKS;EXPECT_EQ(VectorName, "_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u_sin");// SSE: <isa> = "b"EXPECT_TRUE(invokeParser("_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u_sin"));EXPECT_EQ(ISA, VFISAKind::SSE);__COMMON_CHECKS;EXPECT_EQ(VectorName, "_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u_sin");// AVX: <isa> = "c"EXPECT_TRUE(invokeParser("_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u_sin"));EXPECT_EQ(ISA, VFISAKind::AVX);__COMMON_CHECKS;EXPECT_EQ(VectorName, "_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u_sin");// AVX2: <isa> = "d"EXPECT_TRUE(invokeParser("_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u_sin"));EXPECT_EQ(ISA, VFISAKind::AVX2);__COMMON_CHECKS;EXPECT_EQ(VectorName, "_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u_sin");// AVX512: <isa> = "e"EXPECT_TRUE(invokeParser("_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u_sin"));EXPECT_EQ(ISA, VFISAKind::AVX512);__COMMON_CHECKS;EXPECT_EQ(VectorName, "_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u_sin");// LLVM: <isa> = "_LLVM_" internal vector function.EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2vls2Ls27Us4Rs5l1L10U100R1000u_sin(vectorf)", "vectorf"));EXPECT_EQ(ISA, VFISAKind::LLVM);__COMMON_CHECKS;EXPECT_EQ(VectorName, "vectorf");// Unknown ISA (randomly using "q"). This test will need update if// some targets decide to use "q" as their ISA token.EXPECT_TRUE(invokeParser("_ZGVqN2vls2Ls27Us4Rs5l1L10U100R1000u_sin"));EXPECT_EQ(ISA, VFISAKind::Unknown);__COMMON_CHECKS;EXPECT_EQ(VectorName, "_ZGVqN2vls2Ls27Us4Rs5l1L10U100R1000u_sin");#undef __COMMON_CHECKS}TEST_F(VFABIParserTest, MissingScalarName) {EXPECT_FALSE(invokeParser("_ZGVnN2v_"));}TEST_F(VFABIParserTest, MissingVectorName) {EXPECT_FALSE(invokeParser("_ZGVnN2v_foo()"));}TEST_F(VFABIParserTest, MissingVectorNameTermination) {EXPECT_FALSE(invokeParser("_ZGVnN2v_foo(bar"));}TEST_F(VFABIParserTest, ParseMaskingNEON) {EXPECT_TRUE(invokeParser("_ZGVnM2v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");}TEST_F(VFABIParserTest, ParseMaskingSVE) {EXPECT_TRUE(invokeParser("_ZGVsM2v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::SVE);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");}TEST_F(VFABIParserTest, ParseMaskingSSE) {EXPECT_TRUE(invokeParser("_ZGVbM2v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::SSE);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");}TEST_F(VFABIParserTest, ParseMaskingAVX) {EXPECT_TRUE(invokeParser("_ZGVcM2v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AVX);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");}TEST_F(VFABIParserTest, ParseMaskingAVX2) {EXPECT_TRUE(invokeParser("_ZGVdM2v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AVX2);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");}TEST_F(VFABIParserTest, ParseMaskingAVX512) {EXPECT_TRUE(invokeParser("_ZGVeM2v_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::AVX512);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");}TEST_F(VFABIParserTest, ParseMaskingLLVM) {EXPECT_TRUE(invokeParser("_ZGV_LLVM_M2v_sin(custom_vector_sin)","custom_vector_sin"));EXPECT_EQ(VF, ElementCount::getFixed(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::LLVM);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "custom_vector_sin");}TEST_F(VFABIParserTest, ParseScalableMaskingLLVM) {EXPECT_TRUE(invokeParser("_ZGV_LLVM_Mxv_sin(custom_vector_sin)", "custom_vector_sin","<vscale x 2 x i32> (<vscale x 2 x i32>, <vscale x 2 x i1>)"));EXPECT_TRUE(IsMasked());EXPECT_EQ(VF, ElementCount::getScalable(2));EXPECT_EQ(ISA, VFISAKind::LLVM);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sin");EXPECT_EQ(VectorName, "custom_vector_sin");}TEST_F(VFABIParserTest, ParseScalableMaskingLLVMSincos) {EXPECT_TRUE(invokeParser("_ZGV_LLVM_Mxvl8l8_sincos(custom_vector_sincos)","custom_vector_sincos","void(<vscale x 2 x double>, double *, double *)"));EXPECT_EQ(VF, ElementCount::getScalable(2));EXPECT_TRUE(IsMasked());EXPECT_EQ(ISA, VFISAKind::LLVM);EXPECT_EQ(Parameters.size(), (unsigned)4);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_Linear, 8}));EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_Linear, 8}));EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::GlobalPredicate}));EXPECT_EQ(ScalarName, "sincos");EXPECT_EQ(VectorName, "custom_vector_sincos");}class VFABIAttrTest : public testing::Test {protected:void SetUp() override {M = parseAssemblyString(IR, Err, Ctx);// Get the only call instruction in the block, which is the first// instruction.CI = dyn_cast<CallInst>(&*(instructions(M->getFunction("f")).begin()));}const char *IR = "define i32 @f(i32 %a) {\n"" %1 = call i32 @g(i32 %a) #0\n"" ret i32 %1\n""}\n""declare i32 @g(i32)\n""declare <2 x i32> @custom_vg(<2 x i32>)""declare <4 x i32> @_ZGVnN4v_g(<4 x i32>)""declare <8 x i32> @_ZGVnN8v_g(<8 x i32>)""attributes #0 = { ""\"vector-function-abi-variant\"=\"""_ZGVnN2v_g(custom_vg),_ZGVnN4v_g\" }";LLVMContext Ctx;SMDiagnostic Err;std::unique_ptr<Module> M;CallInst *CI;SmallVector<std::string, 8> Mappings;};TEST_F(VFABIAttrTest, Read) {VFABI::getVectorVariantNames(*CI, Mappings);SmallVector<std::string, 8> Exp;Exp.push_back("_ZGVnN2v_g(custom_vg)");Exp.push_back("_ZGVnN4v_g");EXPECT_EQ(Mappings, Exp);}TEST_F(VFABIParserTest, LLVM_InternalISA) {EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_sin"));EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_sin_(vector_name)", "vector_name"));EXPECT_EQ(ISA, VFISAKind::LLVM);}TEST_F(VFABIParserTest, IntrinsicsInLLVMIsa) {EXPECT_TRUE(invokeParser("_ZGV_LLVM_N4vv_llvm.pow.f32(__svml_powf4)","__svml_powf4"));EXPECT_EQ(VF, ElementCount::getFixed(4));EXPECT_FALSE(IsMasked());EXPECT_EQ(ISA, VFISAKind::LLVM);EXPECT_EQ(Parameters.size(), (unsigned)2);EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::Vector}));EXPECT_EQ(ScalarName, "llvm.pow.f32");}TEST_F(VFABIParserTest, ParseScalableRequiresDeclaration) {const char *MangledName = "_ZGVsMxv_sin(custom_vg)";// The parser succeds only when the correct function definition of// `custom_vg` is added to the module.EXPECT_FALSE(invokeParser(MangledName));EXPECT_TRUE(invokeParser(MangledName, "custom_vg","<vscale x 4 x double>(<vscale x 4 x double>, <vscale x 4 x i1>)"));}TEST_F(VFABIParserTest, ZeroIsInvalidVLEN) {EXPECT_FALSE(invokeParser("_ZGVeM0v_sin"));EXPECT_FALSE(invokeParser("_ZGVeN0v_sin"));EXPECT_FALSE(invokeParser("_ZGVsM0v_sin"));EXPECT_FALSE(invokeParser("_ZGVsN0v_sin"));}static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("VectorFunctionABITests", errs());return Mod;}TEST(VFABIGetMappingsTest, IndirectCallInst) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C, R"IR(define void @call(void () * %f) {entry:call void %f()ret void})IR");auto F = dyn_cast_or_null<Function>(M->getNamedValue("call"));ASSERT_TRUE(F);auto CI = dyn_cast<CallInst>(&F->front().front());ASSERT_TRUE(CI);ASSERT_TRUE(CI->isIndirectCall());auto Mappings = VFDatabase::getMappings(*CI);EXPECT_EQ(Mappings.size(), (unsigned)0);}
//===- ValueTrackingTest.cpp - ValueTracking tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/ValueTracking.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/ConstantRange.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/KnownBits.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/Local.h"#include "gtest/gtest.h"using namespace llvm;namespace {static Instruction *findInstructionByNameOrNull(Function *F, StringRef Name) {for (Instruction &I : instructions(F))if (I.getName() == Name)return &I;return nullptr;}static Instruction &findInstructionByName(Function *F, StringRef Name) {auto *I = findInstructionByNameOrNull(F, Name);if (I)return *I;llvm_unreachable("Expected value not found");}class ValueTrackingTest : public testing::Test {protected:std::unique_ptr<Module> parseModule(StringRef Assembly) {SMDiagnostic Error;std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);std::string errMsg;raw_string_ostream os(errMsg);Error.print("", os);EXPECT_TRUE(M) << os.str();return M;}void parseAssembly(StringRef Assembly) {M = parseModule(Assembly);ASSERT_TRUE(M);F = M->getFunction("test");ASSERT_TRUE(F) << "Test must have a function @test";if (!F)return;A = findInstructionByNameOrNull(F, "A");ASSERT_TRUE(A) << "@test must have an instruction %A";A2 = findInstructionByNameOrNull(F, "A2");A3 = findInstructionByNameOrNull(F, "A3");A4 = findInstructionByNameOrNull(F, "A4");CxtI = findInstructionByNameOrNull(F, "CxtI");CxtI2 = findInstructionByNameOrNull(F, "CxtI2");CxtI3 = findInstructionByNameOrNull(F, "CxtI3");}LLVMContext Context;std::unique_ptr<Module> M;Function *F = nullptr;Instruction *A = nullptr;// Instructions (optional)Instruction *A2 = nullptr, *A3 = nullptr, *A4 = nullptr;// Context instructions (optional)Instruction *CxtI = nullptr, *CxtI2 = nullptr, *CxtI3 = nullptr;};class MatchSelectPatternTest : public ValueTrackingTest {protected:void expectPattern(const SelectPatternResult &P) {Value *LHS, *RHS;Instruction::CastOps CastOp;SelectPatternResult R = matchSelectPattern(A, LHS, RHS, &CastOp);EXPECT_EQ(P.Flavor, R.Flavor);EXPECT_EQ(P.NaNBehavior, R.NaNBehavior);EXPECT_EQ(P.Ordered, R.Ordered);}};class ComputeKnownBitsTest : public ValueTrackingTest {protected:void expectKnownBits(uint64_t Zero, uint64_t One) {auto Known = computeKnownBits(A, M->getDataLayout());ASSERT_FALSE(Known.hasConflict());EXPECT_EQ(Known.One.getZExtValue(), One);EXPECT_EQ(Known.Zero.getZExtValue(), Zero);}};}TEST_F(MatchSelectPatternTest, SimpleFMin) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ult float %a, 5.0\n"" %A = select i1 %1, float %a, float 5.0\n"" ret float %A\n""}\n");expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, false});}TEST_F(MatchSelectPatternTest, SimpleFMax) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float %a, 5.0\n"" %A = select i1 %1, float %a, float 5.0\n"" ret float %A\n""}\n");expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, SwappedFMax) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float 5.0, %a\n"" %A = select i1 %1, float %a, float 5.0\n"" ret float %A\n""}\n");expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, false});}TEST_F(MatchSelectPatternTest, SwappedFMax2) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float %a, 5.0\n"" %A = select i1 %1, float 5.0, float %a\n"" ret float %A\n""}\n");expectPattern({SPF_FMAXNUM, SPNB_RETURNS_NAN, false});}TEST_F(MatchSelectPatternTest, SwappedFMax3) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ult float %a, 5.0\n"" %A = select i1 %1, float 5.0, float %a\n"" ret float %A\n""}\n");expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, FastFMin) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp nnan olt float %a, 5.0\n"" %A = select i1 %1, float %a, float 5.0\n"" ret float %A\n""}\n");expectPattern({SPF_FMINNUM, SPNB_RETURNS_ANY, false});}TEST_F(MatchSelectPatternTest, FMinConstantZero) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ole float %a, 0.0\n"" %A = select i1 %1, float %a, float 0.0\n"" ret float %A\n""}\n");// This shouldn't be matched, as %a could be -0.0.expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, FMinConstantZeroNsz) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp nsz ole float %a, 0.0\n"" %A = select i1 %1, float %a, float 0.0\n"" ret float %A\n""}\n");// But this should be, because we've ignored signed zeroes.expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero1) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float -0.0, %a\n"" %A = select i1 %1, float 0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, true});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero2) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float %a, -0.0\n"" %A = select i1 %1, float 0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, false});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero3) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float 0.0, %a\n"" %A = select i1 %1, float -0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, true});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero4) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float %a, 0.0\n"" %A = select i1 %1, float -0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, false});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero5) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float -0.0, %a\n"" %A = select i1 %1, float %a, float 0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, false});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero6) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float %a, -0.0\n"" %A = select i1 %1, float %a, float 0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero7) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float 0.0, %a\n"" %A = select i1 %1, float %a, float -0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, false});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZero8) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float %a, 0.0\n"" %A = select i1 %1, float %a, float -0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero1) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float -0.0, %a\n"" %A = select i1 %1, float 0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_NAN, true});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero2) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float %a, -0.0\n"" %A = select i1 %1, float 0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_NAN, false});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero3) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float 0.0, %a\n"" %A = select i1 %1, float -0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_NAN, true});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero4) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float %a, 0.0\n"" %A = select i1 %1, float -0.0, float %a\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_NAN, false});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero5) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float -0.0, %a\n"" %A = select i1 %1, float %a, float 0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, false});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero6) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float %a, -0.0\n"" %A = select i1 %1, float %a, float 0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero7) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp olt float 0.0, %a\n"" %A = select i1 %1, float %a, float -0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, false});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZero8) {parseAssembly("define float @test(float %a) {\n"" %1 = fcmp ogt float %a, 0.0\n"" %A = select i1 %1, float %a, float -0.0\n"" ret float %A\n""}\n");// The sign of zero doesn't matter in fcmp.expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, FMinMismatchConstantZeroVecUndef) {parseAssembly("define <2 x float> @test(<2 x float> %a) {\n"" %1 = fcmp ogt <2 x float> %a, <float -0.0, float -0.0>\n"" %A = select <2 x i1> %1, <2 x float> <float undef, float 0.0>, <2 x float> %a\n"" ret <2 x float> %A\n""}\n");// An undef in a vector constant can not be back-propagated for this analysis.expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, FMaxMismatchConstantZeroVecUndef) {parseAssembly("define <2 x float> @test(<2 x float> %a) {\n"" %1 = fcmp ogt <2 x float> %a, zeroinitializer\n"" %A = select <2 x i1> %1, <2 x float> %a, <2 x float> <float -0.0, float undef>\n"" ret <2 x float> %A\n""}\n");// An undef in a vector constant can not be back-propagated for this analysis.expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, VectorFMinimum) {parseAssembly("define <4 x float> @test(<4 x float> %a) {\n"" %1 = fcmp ule <4 x float> %a, \n"" <float 5.0, float 5.0, float 5.0, float 5.0>\n"" %A = select <4 x i1> %1, <4 x float> %a,\n"" <4 x float> <float 5.0, float 5.0, float 5.0, float 5.0>\n"" ret <4 x float> %A\n""}\n");// Check that pattern matching works on vectors where each lane has the same// unordered pattern.expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, false});}TEST_F(MatchSelectPatternTest, VectorFMinOtherOrdered) {parseAssembly("define <4 x float> @test(<4 x float> %a) {\n"" %1 = fcmp ole <4 x float> %a, \n"" <float 5.0, float 5.0, float 5.0, float 5.0>\n"" %A = select <4 x i1> %1, <4 x float> %a,\n"" <4 x float> <float 5.0, float 5.0, float 5.0, float 5.0>\n"" ret <4 x float> %A\n""}\n");// Check that pattern matching works on vectors where each lane has the same// ordered pattern.expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, true});}TEST_F(MatchSelectPatternTest, VectorNotFMinimum) {parseAssembly("define <4 x float> @test(<4 x float> %a) {\n"" %1 = fcmp ule <4 x float> %a, \n"" <float 5.0, float 0x7ff8000000000000, float 5.0, float 5.0>\n"" %A = select <4 x i1> %1, <4 x float> %a,\n"" <4 x float> <float 5.0, float 0x7ff8000000000000, float 5.0, float ""5.0>\n"" ret <4 x float> %A\n""}\n");// The lane that contains a NaN (0x7ff80...) behaves like a// non-NaN-propagating min and the other lines behave like a NaN-propagating// min, so check that neither is returned.expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, VectorNotFMinZero) {parseAssembly("define <4 x float> @test(<4 x float> %a) {\n"" %1 = fcmp ule <4 x float> %a, \n"" <float 5.0, float -0.0, float 5.0, float 5.0>\n"" %A = select <4 x i1> %1, <4 x float> %a,\n"" <4 x float> <float 5.0, float 0.0, float 5.0, float 5.0>\n"" ret <4 x float> %A\n""}\n");// Always selects the second lane of %a if it is positive or negative zero, so// this is stricter than a min.expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, DoubleCastU) {parseAssembly("define i32 @test(i8 %a, i8 %b) {\n"" %1 = icmp ult i8 %a, %b\n"" %2 = zext i8 %a to i32\n"" %3 = zext i8 %b to i32\n"" %A = select i1 %1, i32 %2, i32 %3\n"" ret i32 %A\n""}\n");// We should be able to look through the situation where we cast both operands// to the select.expectPattern({SPF_UMIN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, DoubleCastS) {parseAssembly("define i32 @test(i8 %a, i8 %b) {\n"" %1 = icmp slt i8 %a, %b\n"" %2 = sext i8 %a to i32\n"" %3 = sext i8 %b to i32\n"" %A = select i1 %1, i32 %2, i32 %3\n"" ret i32 %A\n""}\n");// We should be able to look through the situation where we cast both operands// to the select.expectPattern({SPF_SMIN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, DoubleCastBad) {parseAssembly("define i32 @test(i8 %a, i8 %b) {\n"" %1 = icmp ult i8 %a, %b\n"" %2 = zext i8 %a to i32\n"" %3 = sext i8 %b to i32\n"" %A = select i1 %1, i32 %2, i32 %3\n"" ret i32 %A\n""}\n");// The cast types here aren't the same, so we cannot match an UMIN.expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotSMin) {parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %cmp = icmp sgt i8 %a, %b\n"" %an = xor i8 %a, -1\n"" %bn = xor i8 %b, -1\n"" %A = select i1 %cmp, i8 %an, i8 %bn\n"" ret i8 %A\n""}\n");expectPattern({SPF_SMIN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotSMinSwap) {parseAssembly("define <2 x i8> @test(<2 x i8> %a, <2 x i8> %b) {\n"" %cmp = icmp slt <2 x i8> %a, %b\n"" %an = xor <2 x i8> %a, <i8 -1, i8-1>\n"" %bn = xor <2 x i8> %b, <i8 -1, i8-1>\n"" %A = select <2 x i1> %cmp, <2 x i8> %bn, <2 x i8> %an\n"" ret <2 x i8> %A\n""}\n");expectPattern({SPF_SMIN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotSMax) {parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %cmp = icmp slt i8 %a, %b\n"" %an = xor i8 %a, -1\n"" %bn = xor i8 %b, -1\n"" %A = select i1 %cmp, i8 %an, i8 %bn\n"" ret i8 %A\n""}\n");expectPattern({SPF_SMAX, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotSMaxSwap) {parseAssembly("define <2 x i8> @test(<2 x i8> %a, <2 x i8> %b) {\n"" %cmp = icmp sgt <2 x i8> %a, %b\n"" %an = xor <2 x i8> %a, <i8 -1, i8-1>\n"" %bn = xor <2 x i8> %b, <i8 -1, i8-1>\n"" %A = select <2 x i1> %cmp, <2 x i8> %bn, <2 x i8> %an\n"" ret <2 x i8> %A\n""}\n");expectPattern({SPF_SMAX, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotUMin) {parseAssembly("define <2 x i8> @test(<2 x i8> %a, <2 x i8> %b) {\n"" %cmp = icmp ugt <2 x i8> %a, %b\n"" %an = xor <2 x i8> %a, <i8 -1, i8-1>\n"" %bn = xor <2 x i8> %b, <i8 -1, i8-1>\n"" %A = select <2 x i1> %cmp, <2 x i8> %an, <2 x i8> %bn\n"" ret <2 x i8> %A\n""}\n");expectPattern({SPF_UMIN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotUMinSwap) {parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %cmp = icmp ult i8 %a, %b\n"" %an = xor i8 %a, -1\n"" %bn = xor i8 %b, -1\n"" %A = select i1 %cmp, i8 %bn, i8 %an\n"" ret i8 %A\n""}\n");expectPattern({SPF_UMIN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotUMax) {parseAssembly("define <2 x i8> @test(<2 x i8> %a, <2 x i8> %b) {\n"" %cmp = icmp ult <2 x i8> %a, %b\n"" %an = xor <2 x i8> %a, <i8 -1, i8-1>\n"" %bn = xor <2 x i8> %b, <i8 -1, i8-1>\n"" %A = select <2 x i1> %cmp, <2 x i8> %an, <2 x i8> %bn\n"" ret <2 x i8> %A\n""}\n");expectPattern({SPF_UMAX, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotUMaxSwap) {parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %cmp = icmp ugt i8 %a, %b\n"" %an = xor i8 %a, -1\n"" %bn = xor i8 %b, -1\n"" %A = select i1 %cmp, i8 %bn, i8 %an\n"" ret i8 %A\n""}\n");expectPattern({SPF_UMAX, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotEq) {parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %cmp = icmp eq i8 %a, %b\n"" %an = xor i8 %a, -1\n"" %bn = xor i8 %b, -1\n"" %A = select i1 %cmp, i8 %bn, i8 %an\n"" ret i8 %A\n""}\n");expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST_F(MatchSelectPatternTest, NotNotNe) {parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %cmp = icmp ne i8 %a, %b\n"" %an = xor i8 %a, -1\n"" %bn = xor i8 %b, -1\n"" %A = select i1 %cmp, i8 %bn, i8 %an\n"" ret i8 %A\n""}\n");expectPattern({SPF_UNKNOWN, SPNB_NA, false});}TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) {StringRef Assembly ="declare void @nounwind_readonly(i32*) nounwind readonly ""declare void @nounwind_argmemonly(i32*) nounwind argmemonly ""declare void @nounwind_willreturn(i32*) nounwind willreturn ""declare void @throws_but_readonly(i32*) readonly ""declare void @throws_but_argmemonly(i32*) argmemonly ""declare void @throws_but_willreturn(i32*) willreturn "" ""declare void @unknown(i32*) "" ""define void @f(i32* %p) { "" call void @nounwind_readonly(i32* %p) "" call void @nounwind_argmemonly(i32* %p) "" call void @nounwind_willreturn(i32* %p)"" call void @throws_but_readonly(i32* %p) "" call void @throws_but_argmemonly(i32* %p) "" call void @throws_but_willreturn(i32* %p) "" call void @unknown(i32* %p) nounwind readonly "" call void @unknown(i32* %p) nounwind argmemonly "" call void @unknown(i32* %p) nounwind willreturn "" call void @unknown(i32* %p) readonly "" call void @unknown(i32* %p) argmemonly "" call void @unknown(i32* %p) willreturn "" ret void ""} ";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);assert(M && "Bad assembly?");auto *F = M->getFunction("f");assert(F && "Bad assembly?");auto &BB = F->getEntryBlock();bool ExpectedAnswers[] = {false, // call void @nounwind_readonly(i32* %p)false, // call void @nounwind_argmemonly(i32* %p)true, // call void @nounwind_willreturn(i32* %p)false, // call void @throws_but_readonly(i32* %p)false, // call void @throws_but_argmemonly(i32* %p)false, // call void @throws_but_willreturn(i32* %p)false, // call void @unknown(i32* %p) nounwind readonlyfalse, // call void @unknown(i32* %p) nounwind argmemonlytrue, // call void @unknown(i32* %p) nounwind willreturnfalse, // call void @unknown(i32* %p) readonlyfalse, // call void @unknown(i32* %p) argmemonlyfalse, // call void @unknown(i32* %p) willreturnfalse, // ret void};int Index = 0;for (auto &I : BB) {EXPECT_EQ(isGuaranteedToTransferExecutionToSuccessor(&I),ExpectedAnswers[Index])<< "Incorrect answer at instruction " << Index << " = " << I;Index++;}}TEST_F(ValueTrackingTest, ComputeNumSignBits_PR32045) {parseAssembly("define i32 @test(i32 %a) {\n"" %A = ashr i32 %a, -1\n"" ret i32 %A\n""}\n");EXPECT_EQ(ComputeNumSignBits(A, M->getDataLayout()), 1u);}// No guarantees for canonical IR in this analysis, so this just bails out.TEST_F(ValueTrackingTest, ComputeNumSignBits_Shuffle) {parseAssembly("define <2 x i32> @test() {\n"" %A = shufflevector <2 x i32> undef, <2 x i32> undef, <2 x i32> <i32 0, i32 0>\n"" ret <2 x i32> %A\n""}\n");EXPECT_EQ(ComputeNumSignBits(A, M->getDataLayout()), 1u);}// No guarantees for canonical IR in this analysis, so a shuffle element that// references an undef value means this can't return any extra information.TEST_F(ValueTrackingTest, ComputeNumSignBits_Shuffle2) {parseAssembly("define <2 x i32> @test(<2 x i1> %x) {\n"" %sext = sext <2 x i1> %x to <2 x i32>\n"" %A = shufflevector <2 x i32> %sext, <2 x i32> undef, <2 x i32> <i32 0, i32 2>\n"" ret <2 x i32> %A\n""}\n");EXPECT_EQ(ComputeNumSignBits(A, M->getDataLayout()), 1u);}TEST_F(ValueTrackingTest, impliesPoisonTest_Identity) {parseAssembly("define void @test(i32 %x, i32 %y) {\n"" %A = add i32 %x, %y\n"" ret void\n""}");EXPECT_TRUE(impliesPoison(A, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_ICmp) {parseAssembly("define void @test(i32 %x) {\n"" %A2 = icmp eq i32 %x, 0\n"" %A = icmp eq i32 %x, 1\n"" ret void\n""}");EXPECT_TRUE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_ICmpUnknown) {parseAssembly("define void @test(i32 %x, i32 %y) {\n"" %A2 = icmp eq i32 %x, %y\n"" %A = icmp eq i32 %x, 1\n"" ret void\n""}");EXPECT_FALSE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_AddNswOkay) {parseAssembly("define void @test(i32 %x) {\n"" %A2 = add nsw i32 %x, 1\n"" %A = add i32 %A2, 1\n"" ret void\n""}");EXPECT_TRUE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_AddNswOkay2) {parseAssembly("define void @test(i32 %x) {\n"" %A2 = add i32 %x, 1\n"" %A = add nsw i32 %A2, 1\n"" ret void\n""}");EXPECT_TRUE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_AddNsw) {parseAssembly("define void @test(i32 %x) {\n"" %A2 = add nsw i32 %x, 1\n"" %A = add i32 %x, 1\n"" ret void\n""}");EXPECT_FALSE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_Cmp) {parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"" %A2 = icmp eq i32 %x, %y\n"" %A0 = icmp ult i32 %x, %y\n"" %A = or i1 %A0, %c\n"" ret void\n""}");EXPECT_TRUE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_FCmpFMF) {parseAssembly("define void @test(float %x, float %y, i1 %c) {\n"" %A2 = fcmp nnan oeq float %x, %y\n"" %A0 = fcmp olt float %x, %y\n"" %A = or i1 %A0, %c\n"" ret void\n""}");EXPECT_FALSE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_AddSubSameOps) {parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"" %A2 = add i32 %x, %y\n"" %A = sub i32 %x, %y\n"" ret void\n""}");EXPECT_TRUE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, impliesPoisonTest_MaskCmp) {parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"" %M2 = and i32 %x, 7\n"" %A2 = icmp eq i32 %M2, 1\n"" %M = and i32 %x, 15\n"" %A = icmp eq i32 %M, 3\n"" ret void\n""}");EXPECT_TRUE(impliesPoison(A2, A));}TEST_F(ValueTrackingTest, ComputeNumSignBits_Shuffle_Pointers) {parseAssembly("define <2 x i32*> @test(<2 x i32*> %x) {\n"" %A = shufflevector <2 x i32*> zeroinitializer, <2 x i32*> undef, <2 x i32> zeroinitializer\n"" ret <2 x i32*> %A\n""}\n");EXPECT_EQ(ComputeNumSignBits(A, M->getDataLayout()), 64u);}TEST(ValueTracking, propagatesPoison) {std::string AsmHead ="declare i32 @g(i32)\n""declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)\n""declare float @llvm.sqrt.f32(float)\n""declare float @llvm.powi.f32.i32(float, i32)\n""declare float @llvm.sin.f32(float)\n""declare float @llvm.cos.f32(float)\n""declare float @llvm.pow.f32(float, float)\n""declare float @llvm.exp.f32(float)\n""declare float @llvm.exp2.f32(float)\n""declare float @llvm.log.f32(float)\n""declare float @llvm.log10.f32(float)\n""declare float @llvm.log2.f32(float)\n""declare float @llvm.fma.f32(float, float, float)\n""declare float @llvm.fabs.f32(float)\n""declare float @llvm.minnum.f32(float, float)\n""declare float @llvm.maxnum.f32(float, float)\n""declare float @llvm.minimum.f32(float, float)\n""declare float @llvm.maximum.f32(float, float)\n""declare float @llvm.copysign.f32(float, float)\n""declare float @llvm.floor.f32(float)\n""declare float @llvm.ceil.f32(float)\n""declare float @llvm.trunc.f32(float)\n""declare float @llvm.rint.f32(float)\n""declare float @llvm.nearbyint.f32(float)\n""declare float @llvm.round.f32(float)\n""declare float @llvm.roundeven.f32(float)\n""declare i32 @llvm.lround.f32(float)\n""declare i64 @llvm.llround.f32(float)\n""declare i32 @llvm.lrint.f32(float)\n""declare i64 @llvm.llrint.f32(float)\n""declare float @llvm.fmuladd.f32(float, float, float)\n""define void @f(i32 %x, i32 %y, float %fx, float %fy, ""i1 %cond, i8* %p) {\n";std::string AsmTail = " ret void\n}";// (propagates poison?, IR instruction)SmallVector<std::pair<bool, std::string>, 32> Data = {{true, "add i32 %x, %y"},{true, "add nsw nuw i32 %x, %y"},{true, "ashr i32 %x, %y"},{true, "lshr exact i32 %x, 31"},{true, "fadd float %fx, %fy"},{true, "fsub float %fx, %fy"},{true, "fmul float %fx, %fy"},{true, "fdiv float %fx, %fy"},{true, "frem float %fx, %fy"},{true, "fneg float %fx"},{true, "fcmp oeq float %fx, %fy"},{true, "icmp eq i32 %x, %y"},{true, "getelementptr i8, i8* %p, i32 %x"},{true, "getelementptr inbounds i8, i8* %p, i32 %x"},{true, "bitcast float %fx to i32"},{false, "select i1 %cond, i32 %x, i32 %y"},{false, "freeze i32 %x"},{true, "udiv i32 %x, %y"},{true, "urem i32 %x, %y"},{true, "sdiv exact i32 %x, %y"},{true, "srem i32 %x, %y"},{false, "call i32 @g(i32 %x)"},{true, "call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)"},{true, "call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)"},{true, "call {i32, i1} @llvm.smul.with.overflow.i32(i32 %x, i32 %y)"},{true, "call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)"},{true, "call {i32, i1} @llvm.usub.with.overflow.i32(i32 %x, i32 %y)"},{true, "call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %y)"},{false, "call float @llvm.sqrt.f32(float %fx)"},{false, "call float @llvm.powi.f32.i32(float %fx, i32 %x)"},{false, "call float @llvm.sin.f32(float %fx)"},{false, "call float @llvm.cos.f32(float %fx)"},{false, "call float @llvm.pow.f32(float %fx, float %fy)"},{false, "call float @llvm.exp.f32(float %fx)"},{false, "call float @llvm.exp2.f32(float %fx)"},{false, "call float @llvm.log.f32(float %fx)"},{false, "call float @llvm.log10.f32(float %fx)"},{false, "call float @llvm.log2.f32(float %fx)"},{false, "call float @llvm.fma.f32(float %fx, float %fx, float %fy)"},{false, "call float @llvm.fabs.f32(float %fx)"},{false, "call float @llvm.minnum.f32(float %fx, float %fy)"},{false, "call float @llvm.maxnum.f32(float %fx, float %fy)"},{false, "call float @llvm.minimum.f32(float %fx, float %fy)"},{false, "call float @llvm.maximum.f32(float %fx, float %fy)"},{false, "call float @llvm.copysign.f32(float %fx, float %fy)"},{false, "call float @llvm.floor.f32(float %fx)"},{false, "call float @llvm.ceil.f32(float %fx)"},{false, "call float @llvm.trunc.f32(float %fx)"},{false, "call float @llvm.rint.f32(float %fx)"},{false, "call float @llvm.nearbyint.f32(float %fx)"},{false, "call float @llvm.round.f32(float %fx)"},{false, "call float @llvm.roundeven.f32(float %fx)"},{false, "call i32 @llvm.lround.f32(float %fx)"},{false, "call i64 @llvm.llround.f32(float %fx)"},{false, "call i32 @llvm.lrint.f32(float %fx)"},{false, "call i64 @llvm.llrint.f32(float %fx)"},{false, "call float @llvm.fmuladd.f32(float %fx, float %fx, float %fy)"}};std::string AssemblyStr = AsmHead;for (auto &Itm : Data)AssemblyStr += Itm.second + "\n";AssemblyStr += AsmTail;LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(AssemblyStr, Error, Context);assert(M && "Bad assembly?");auto *F = M->getFunction("f");assert(F && "Bad assembly?");auto &BB = F->getEntryBlock();int Index = 0;for (auto &I : BB) {if (isa<ReturnInst>(&I))break;EXPECT_EQ(propagatesPoison(cast<Operator>(&I)), Data[Index].first)<< "Incorrect answer at instruction " << Index << " = " << I;Index++;}}TEST_F(ValueTrackingTest, programUndefinedIfPoison) {parseAssembly("declare i32 @any_num()""define void @test(i32 %mask) {\n"" %A = call i32 @any_num()\n"" %B = or i32 %A, %mask\n"" udiv i32 1, %B"" ret void\n""}\n");// If %A was poison, udiv raises UB regardless of %mask's valueEXPECT_EQ(programUndefinedIfPoison(A), true);}TEST_F(ValueTrackingTest, programUndefinedIfUndefOrPoison) {parseAssembly("declare i32 @any_num()""define void @test(i32 %mask) {\n"" %A = call i32 @any_num()\n"" %B = or i32 %A, %mask\n"" udiv i32 1, %B"" ret void\n""}\n");// If %A was undef and %mask was 1, udiv does not raise UBEXPECT_EQ(programUndefinedIfUndefOrPoison(A), false);}TEST_F(ValueTrackingTest, isGuaranteedNotToBePoison_exploitBranchCond) {parseAssembly("declare i1 @any_bool()""define void @test(i1 %y) {\n"" %A = call i1 @any_bool()\n"" %cond = and i1 %A, %y\n"" br i1 %cond, label %BB1, label %BB2\n""BB1:\n"" ret void\n""BB2:\n"" ret void\n""}\n");DominatorTree DT(*F);for (auto &BB : *F) {if (&BB == &F->getEntryBlock())continue;EXPECT_EQ(isGuaranteedNotToBePoison(A, nullptr, BB.getTerminator(), &DT),true)<< "isGuaranteedNotToBePoison does not hold at " << *BB.getTerminator();}}TEST_F(ValueTrackingTest, isGuaranteedNotToBePoison_phi) {parseAssembly("declare i32 @any_i32(i32)""define void @test() {\n""ENTRY:\n"" br label %LOOP\n""LOOP:\n"" %A = phi i32 [0, %ENTRY], [%A.next, %NEXT]\n"" %A.next = call i32 @any_i32(i32 %A)\n"" %cond = icmp eq i32 %A.next, 0\n"" br i1 %cond, label %NEXT, label %EXIT\n""NEXT:\n"" br label %LOOP\n""EXIT:\n"" ret void\n""}\n");DominatorTree DT(*F);for (auto &BB : *F) {if (BB.getName() == "LOOP") {EXPECT_EQ(isGuaranteedNotToBePoison(A, nullptr, A, &DT), true)<< "isGuaranteedNotToBePoison does not hold";}}}TEST_F(ValueTrackingTest, isGuaranteedNotToBeUndefOrPoison) {parseAssembly("declare void @f(i32 noundef)""define void @test(i32 %x) {\n"" %A = bitcast i32 %x to i32\n"" call void @f(i32 noundef %x)\n"" ret void\n""}\n");EXPECT_EQ(isGuaranteedNotToBeUndefOrPoison(A), true);EXPECT_EQ(isGuaranteedNotToBeUndefOrPoison(UndefValue::get(IntegerType::get(Context, 8))), false);EXPECT_EQ(isGuaranteedNotToBeUndefOrPoison(PoisonValue::get(IntegerType::get(Context, 8))), false);EXPECT_EQ(isGuaranteedNotToBePoison(UndefValue::get(IntegerType::get(Context, 8))), true);EXPECT_EQ(isGuaranteedNotToBePoison(PoisonValue::get(IntegerType::get(Context, 8))), false);Type *Int32Ty = Type::getInt32Ty(Context);Constant *CU = UndefValue::get(Int32Ty);Constant *CP = PoisonValue::get(Int32Ty);Constant *C1 = ConstantInt::get(Int32Ty, 1);Constant *C2 = ConstantInt::get(Int32Ty, 2);{Constant *V1 = ConstantVector::get({C1, C2});EXPECT_TRUE(isGuaranteedNotToBeUndefOrPoison(V1));EXPECT_TRUE(isGuaranteedNotToBePoison(V1));}{Constant *V2 = ConstantVector::get({C1, CU});EXPECT_FALSE(isGuaranteedNotToBeUndefOrPoison(V2));EXPECT_TRUE(isGuaranteedNotToBePoison(V2));}{Constant *V3 = ConstantVector::get({C1, CP});EXPECT_FALSE(isGuaranteedNotToBeUndefOrPoison(V3));EXPECT_FALSE(isGuaranteedNotToBePoison(V3));}}TEST_F(ValueTrackingTest, isGuaranteedNotToBeUndefOrPoison_assume) {parseAssembly("declare i1 @f_i1()\n""declare i32 @f_i32()\n""declare void @llvm.assume(i1)\n""define void @test() {\n"" %A = call i32 @f_i32()\n"" %cond = call i1 @f_i1()\n"" %CxtI = add i32 0, 0\n"" br i1 %cond, label %BB1, label %EXIT\n""BB1:\n"" %CxtI2 = add i32 0, 0\n"" %cond2 = call i1 @f_i1()\n"" call void @llvm.assume(i1 true) [ \"noundef\"(i32 %A) ]\n"" br i1 %cond2, label %BB2, label %EXIT\n""BB2:\n"" %CxtI3 = add i32 0, 0\n"" ret void\n""EXIT:\n"" ret void\n""}");AssumptionCache AC(*F);DominatorTree DT(*F);EXPECT_FALSE(isGuaranteedNotToBeUndefOrPoison(A, &AC, CxtI, &DT));EXPECT_FALSE(isGuaranteedNotToBeUndefOrPoison(A, &AC, CxtI2, &DT));EXPECT_TRUE(isGuaranteedNotToBeUndefOrPoison(A, &AC, CxtI3, &DT));}TEST(ValueTracking, canCreatePoisonOrUndef) {std::string AsmHead ="@s = external dso_local global i32, align 1\n""declare i32 @g(i32)\n""declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)\n""declare {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)\n""define void @f(i32 %x, i32 %y, float %fx, float %fy, i1 %cond, ""<4 x i32> %vx, <4 x i32> %vx2, <vscale x 4 x i32> %svx, i8* %p) {\n";std::string AsmTail = " ret void\n}";// (can create poison?, can create undef?, IR instruction)SmallVector<std::pair<std::pair<bool, bool>, std::string>, 32> Data = {{{false, false}, "add i32 %x, %y"},{{true, false}, "add nsw nuw i32 %x, %y"},{{true, false}, "shl i32 %x, %y"},{{true, false}, "shl <4 x i32> %vx, %vx2"},{{true, false}, "shl nsw i32 %x, %y"},{{true, false}, "shl nsw <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},{{false, false}, "shl i32 %x, 31"},{{true, false}, "shl i32 %x, 32"},{{false, false}, "shl <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},{{true, false}, "shl <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 32>"},{{true, false}, "ashr i32 %x, %y"},{{true, false}, "ashr exact i32 %x, %y"},{{false, false}, "ashr i32 %x, 31"},{{true, false}, "ashr exact i32 %x, 31"},{{false, false}, "ashr <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},{{true, false}, "ashr <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 32>"},{{true, false}, "ashr exact <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},{{true, false}, "lshr i32 %x, %y"},{{true, false}, "lshr exact i32 %x, 31"},{{false, false}, "udiv i32 %x, %y"},{{true, false}, "udiv exact i32 %x, %y"},{{false, false}, "getelementptr i8, i8* %p, i32 %x"},{{true, false}, "getelementptr inbounds i8, i8* %p, i32 %x"},{{true, false}, "fneg nnan float %fx"},{{false, false}, "fneg float %fx"},{{false, false}, "fadd float %fx, %fy"},{{true, false}, "fadd nnan float %fx, %fy"},{{false, false}, "urem i32 %x, %y"},{{true, false}, "fptoui float %fx to i32"},{{true, false}, "fptosi float %fx to i32"},{{false, false}, "bitcast float %fx to i32"},{{false, false}, "select i1 %cond, i32 %x, i32 %y"},{{true, false}, "select nnan i1 %cond, float %fx, float %fy"},{{true, false}, "extractelement <4 x i32> %vx, i32 %x"},{{false, false}, "extractelement <4 x i32> %vx, i32 3"},{{true, false}, "extractelement <vscale x 4 x i32> %svx, i32 4"},{{true, false}, "insertelement <4 x i32> %vx, i32 %x, i32 %y"},{{false, false}, "insertelement <4 x i32> %vx, i32 %x, i32 3"},{{true, false}, "insertelement <vscale x 4 x i32> %svx, i32 %x, i32 4"},{{false, false}, "freeze i32 %x"},{{false, false},"shufflevector <4 x i32> %vx, <4 x i32> %vx2, ""<4 x i32> <i32 0, i32 1, i32 2, i32 3>"},{{false, true},"shufflevector <4 x i32> %vx, <4 x i32> %vx2, ""<4 x i32> <i32 0, i32 1, i32 2, i32 undef>"},{{false, true},"shufflevector <vscale x 4 x i32> %svx, ""<vscale x 4 x i32> %svx, <vscale x 4 x i32> undef"},{{true, false}, "call i32 @g(i32 %x)"},{{false, false}, "call noundef i32 @g(i32 %x)"},{{true, false}, "fcmp nnan oeq float %fx, %fy"},{{false, false}, "fcmp oeq float %fx, %fy"},{{true, false},"ashr <4 x i32> %vx, select (i1 icmp sgt (i32 ptrtoint (i32* @s to ""i32), i32 1), <4 x i32> zeroinitializer, <4 x i32> <i32 0, i32 1, i32 ""2, i32 3>)"},{{false, false},"call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)"},{{false, false},"call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)"},{{false, false},"call {i32, i1} @llvm.smul.with.overflow.i32(i32 %x, i32 %y)"},{{false, false},"call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)"},{{false, false},"call {i32, i1} @llvm.usub.with.overflow.i32(i32 %x, i32 %y)"},{{false, false},"call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %y)"}};std::string AssemblyStr = AsmHead;for (auto &Itm : Data)AssemblyStr += Itm.second + "\n";AssemblyStr += AsmTail;LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(AssemblyStr, Error, Context);assert(M && "Bad assembly?");auto *F = M->getFunction("f");assert(F && "Bad assembly?");auto &BB = F->getEntryBlock();int Index = 0;for (auto &I : BB) {if (isa<ReturnInst>(&I))break;bool Poison = Data[Index].first.first;bool Undef = Data[Index].first.second;EXPECT_EQ(canCreatePoison(cast<Operator>(&I)), Poison)<< "Incorrect answer of canCreatePoison at instruction " << Index<< " = " << I;EXPECT_EQ(canCreateUndefOrPoison(cast<Operator>(&I)), Undef || Poison)<< "Incorrect answer of canCreateUndef at instruction " << Index<< " = " << I;Index++;}}TEST_F(ValueTrackingTest, computePtrAlignment) {parseAssembly("declare i1 @f_i1()\n""declare i8* @f_i8p()\n""declare void @llvm.assume(i1)\n""define void @test() {\n"" %A = call i8* @f_i8p()\n"" %cond = call i1 @f_i1()\n"" %CxtI = add i32 0, 0\n"" br i1 %cond, label %BB1, label %EXIT\n""BB1:\n"" %CxtI2 = add i32 0, 0\n"" %cond2 = call i1 @f_i1()\n"" call void @llvm.assume(i1 true) [ \"align\"(i8* %A, i64 16) ]\n"" br i1 %cond2, label %BB2, label %EXIT\n""BB2:\n"" %CxtI3 = add i32 0, 0\n"" ret void\n""EXIT:\n"" ret void\n""}");AssumptionCache AC(*F);DominatorTree DT(*F);const DataLayout &DL = M->getDataLayout();EXPECT_EQ(getKnownAlignment(A, DL, CxtI, &AC, &DT), Align(1));EXPECT_EQ(getKnownAlignment(A, DL, CxtI2, &AC, &DT), Align(1));EXPECT_EQ(getKnownAlignment(A, DL, CxtI3, &AC, &DT), Align(16));}TEST_F(ComputeKnownBitsTest, ComputeKnownBits) {parseAssembly("define i32 @test(i32 %a, i32 %b) {\n"" %ash = mul i32 %a, 8\n"" %aad = add i32 %ash, 7\n"" %aan = and i32 %aad, 4095\n"" %bsh = shl i32 %b, 4\n"" %bad = or i32 %bsh, 6\n"" %ban = and i32 %bad, 4095\n"" %A = mul i32 %aan, %ban\n"" ret i32 %A\n""}\n");expectKnownBits(/*zero*/ 4278190085u, /*one*/ 10u);}TEST_F(ComputeKnownBitsTest, ComputeKnownMulBits) {parseAssembly("define i32 @test(i32 %a, i32 %b) {\n"" %aa = shl i32 %a, 5\n"" %bb = shl i32 %b, 5\n"" %aaa = or i32 %aa, 24\n"" %bbb = or i32 %bb, 28\n"" %A = mul i32 %aaa, %bbb\n"" ret i32 %A\n""}\n");expectKnownBits(/*zero*/ 95u, /*one*/ 32u);}TEST_F(ValueTrackingTest, isNonZeroRecurrence) {parseAssembly(R"(define i1 @test(i8 %n, i8 %r) {entry:br label %looploop:%p = phi i8 [ -1, %entry ], [ %next, %loop ]%next = add nsw i8 %p, -1%cmp1 = icmp eq i8 %p, %nbr i1 %cmp1, label %exit, label %loopexit:%A = or i8 %p, %r%CxtI = icmp eq i8 %A, 0ret i1 %CxtI})");const DataLayout &DL = M->getDataLayout();AssumptionCache AC(*F);EXPECT_TRUE(isKnownNonZero(A, DL, 0, &AC, CxtI));}TEST_F(ValueTrackingTest, KnownNonZeroFromDomCond) {parseAssembly(R"(declare i8* @f_i8()define void @test(i1 %c) {%A = call i8* @f_i8()%B = call i8* @f_i8()%c1 = icmp ne i8* %A, null%cond = and i1 %c1, %cbr i1 %cond, label %T, label %QT:%CxtI = add i32 0, 0ret voidQ:%CxtI2 = add i32 0, 0ret void})");AssumptionCache AC(*F);DominatorTree DT(*F);const DataLayout &DL = M->getDataLayout();EXPECT_EQ(isKnownNonZero(A, DL, 0, &AC, CxtI, &DT), true);EXPECT_EQ(isKnownNonZero(A, DL, 0, &AC, CxtI2, &DT), false);}TEST_F(ValueTrackingTest, KnownNonZeroFromDomCond2) {parseAssembly(R"(declare i8* @f_i8()define void @test(i1 %c) {%A = call i8* @f_i8()%B = call i8* @f_i8()%c1 = icmp ne i8* %A, null%cond = select i1 %c, i1 %c1, i1 falsebr i1 %cond, label %T, label %QT:%CxtI = add i32 0, 0ret voidQ:%CxtI2 = add i32 0, 0ret void})");AssumptionCache AC(*F);DominatorTree DT(*F);const DataLayout &DL = M->getDataLayout();EXPECT_EQ(isKnownNonZero(A, DL, 0, &AC, CxtI, &DT), true);EXPECT_EQ(isKnownNonZero(A, DL, 0, &AC, CxtI2, &DT), false);}TEST_F(ValueTrackingTest, IsImpliedConditionAnd) {parseAssembly(R"(define void @test(i32 %x, i32 %y) {%c1 = icmp ult i32 %x, 10%c2 = icmp ult i32 %y, 15%A = and i1 %c1, %c2; x < 10 /\ y < 15%A2 = icmp ult i32 %x, 20%A3 = icmp uge i32 %y, 20%A4 = icmp ult i32 %x, 5ret void})");const DataLayout &DL = M->getDataLayout();EXPECT_EQ(isImpliedCondition(A, A2, DL), true);EXPECT_EQ(isImpliedCondition(A, A3, DL), false);EXPECT_EQ(isImpliedCondition(A, A4, DL), None);}TEST_F(ValueTrackingTest, IsImpliedConditionAnd2) {parseAssembly(R"(define void @test(i32 %x, i32 %y) {%c1 = icmp ult i32 %x, 10%c2 = icmp ult i32 %y, 15%A = select i1 %c1, i1 %c2, i1 false; x < 10 /\ y < 15%A2 = icmp ult i32 %x, 20%A3 = icmp uge i32 %y, 20%A4 = icmp ult i32 %x, 5ret void})");const DataLayout &DL = M->getDataLayout();EXPECT_EQ(isImpliedCondition(A, A2, DL), true);EXPECT_EQ(isImpliedCondition(A, A3, DL), false);EXPECT_EQ(isImpliedCondition(A, A4, DL), None);}TEST_F(ValueTrackingTest, IsImpliedConditionAndVec) {parseAssembly(R"(define void @test(<2 x i8> %x, <2 x i8> %y) {%A = icmp ult <2 x i8> %x, %y%A2 = icmp ule <2 x i8> %x, %yret void})");const DataLayout &DL = M->getDataLayout();EXPECT_EQ(isImpliedCondition(A, A2, DL), true);}TEST_F(ValueTrackingTest, IsImpliedConditionOr) {parseAssembly(R"(define void @test(i32 %x, i32 %y) {%c1 = icmp ult i32 %x, 10%c2 = icmp ult i32 %y, 15%A = or i1 %c1, %c2 ; negated; x >= 10 /\ y >= 15%A2 = icmp ult i32 %x, 5%A3 = icmp uge i32 %y, 10%A4 = icmp ult i32 %x, 15ret void})");const DataLayout &DL = M->getDataLayout();EXPECT_EQ(isImpliedCondition(A, A2, DL, false), false);EXPECT_EQ(isImpliedCondition(A, A3, DL, false), true);EXPECT_EQ(isImpliedCondition(A, A4, DL, false), None);}TEST_F(ValueTrackingTest, IsImpliedConditionOr2) {parseAssembly(R"(define void @test(i32 %x, i32 %y) {%c1 = icmp ult i32 %x, 10%c2 = icmp ult i32 %y, 15%A = select i1 %c1, i1 true, i1 %c2 ; negated; x >= 10 /\ y >= 15%A2 = icmp ult i32 %x, 5%A3 = icmp uge i32 %y, 10%A4 = icmp ult i32 %x, 15ret void})");const DataLayout &DL = M->getDataLayout();EXPECT_EQ(isImpliedCondition(A, A2, DL, false), false);EXPECT_EQ(isImpliedCondition(A, A3, DL, false), true);EXPECT_EQ(isImpliedCondition(A, A4, DL, false), None);}TEST_F(ComputeKnownBitsTest, KnownNonZeroShift) {// %q is known nonzero without known bits.// Because %q is nonzero, %A[0] is known to be zero.parseAssembly("define i8 @test(i8 %p, i8* %pq) {\n"" %q = load i8, i8* %pq, !range !0\n"" %A = shl i8 %p, %q\n"" ret i8 %A\n""}\n""!0 = !{ i8 1, i8 5 }\n");expectKnownBits(/*zero*/ 1u, /*one*/ 0u);}TEST_F(ComputeKnownBitsTest, ComputeKnownFshl) {// fshl(....1111....0000, 00..1111........, 6)// = 11....000000..11parseAssembly("define i16 @test(i16 %a, i16 %b) {\n"" %aa = shl i16 %a, 4\n"" %bb = lshr i16 %b, 2\n"" %aaa = or i16 %aa, 3840\n"" %bbb = or i16 %bb, 3840\n"" %A = call i16 @llvm.fshl.i16(i16 %aaa, i16 %bbb, i16 6)\n"" ret i16 %A\n""}\n""declare i16 @llvm.fshl.i16(i16, i16, i16)\n");expectKnownBits(/*zero*/ 1008u, /*one*/ 49155u);}TEST_F(ComputeKnownBitsTest, ComputeKnownFshr) {// fshr(....1111....0000, 00..1111........, 26)// = 11....000000..11parseAssembly("define i16 @test(i16 %a, i16 %b) {\n"" %aa = shl i16 %a, 4\n"" %bb = lshr i16 %b, 2\n"" %aaa = or i16 %aa, 3840\n"" %bbb = or i16 %bb, 3840\n"" %A = call i16 @llvm.fshr.i16(i16 %aaa, i16 %bbb, i16 26)\n"" ret i16 %A\n""}\n""declare i16 @llvm.fshr.i16(i16, i16, i16)\n");expectKnownBits(/*zero*/ 1008u, /*one*/ 49155u);}TEST_F(ComputeKnownBitsTest, ComputeKnownFshlZero) {// fshl(....1111....0000, 00..1111........, 0)// = ....1111....0000parseAssembly("define i16 @test(i16 %a, i16 %b) {\n"" %aa = shl i16 %a, 4\n"" %bb = lshr i16 %b, 2\n"" %aaa = or i16 %aa, 3840\n"" %bbb = or i16 %bb, 3840\n"" %A = call i16 @llvm.fshl.i16(i16 %aaa, i16 %bbb, i16 0)\n"" ret i16 %A\n""}\n""declare i16 @llvm.fshl.i16(i16, i16, i16)\n");expectKnownBits(/*zero*/ 15u, /*one*/ 3840u);}TEST_F(ComputeKnownBitsTest, ComputeKnownUAddSatLeadingOnes) {// uadd.sat(1111...1, ........)// = 1111....parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %aa = or i8 %a, 241\n"" %A = call i8 @llvm.uadd.sat.i8(i8 %aa, i8 %b)\n"" ret i8 %A\n""}\n""declare i8 @llvm.uadd.sat.i8(i8, i8)\n");expectKnownBits(/*zero*/ 0u, /*one*/ 240u);}TEST_F(ComputeKnownBitsTest, ComputeKnownUAddSatOnesPreserved) {// uadd.sat(00...011, .1...110)// = .......1parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %aa = or i8 %a, 3\n"" %aaa = and i8 %aa, 59\n"" %bb = or i8 %b, 70\n"" %bbb = and i8 %bb, 254\n"" %A = call i8 @llvm.uadd.sat.i8(i8 %aaa, i8 %bbb)\n"" ret i8 %A\n""}\n""declare i8 @llvm.uadd.sat.i8(i8, i8)\n");expectKnownBits(/*zero*/ 0u, /*one*/ 1u);}TEST_F(ComputeKnownBitsTest, ComputeKnownUSubSatLHSLeadingZeros) {// usub.sat(0000...0, ........)// = 0000....parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %aa = and i8 %a, 14\n"" %A = call i8 @llvm.usub.sat.i8(i8 %aa, i8 %b)\n"" ret i8 %A\n""}\n""declare i8 @llvm.usub.sat.i8(i8, i8)\n");expectKnownBits(/*zero*/ 240u, /*one*/ 0u);}TEST_F(ComputeKnownBitsTest, ComputeKnownUSubSatRHSLeadingOnes) {// usub.sat(........, 1111...1)// = 0000....parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %bb = or i8 %a, 241\n"" %A = call i8 @llvm.usub.sat.i8(i8 %a, i8 %bb)\n"" ret i8 %A\n""}\n""declare i8 @llvm.usub.sat.i8(i8, i8)\n");expectKnownBits(/*zero*/ 240u, /*one*/ 0u);}TEST_F(ComputeKnownBitsTest, ComputeKnownUSubSatZerosPreserved) {// usub.sat(11...011, .1...110)// = ......0.parseAssembly("define i8 @test(i8 %a, i8 %b) {\n"" %aa = or i8 %a, 195\n"" %aaa = and i8 %aa, 251\n"" %bb = or i8 %b, 70\n"" %bbb = and i8 %bb, 254\n"" %A = call i8 @llvm.usub.sat.i8(i8 %aaa, i8 %bbb)\n"" ret i8 %A\n""}\n""declare i8 @llvm.usub.sat.i8(i8, i8)\n");expectKnownBits(/*zero*/ 2u, /*one*/ 0u);}TEST_F(ComputeKnownBitsTest, ComputeKnownBitsPtrToIntTrunc) {// ptrtoint truncates the pointer type.parseAssembly("define void @test(i8** %p) {\n"" %A = load i8*, i8** %p\n"" %i = ptrtoint i8* %A to i32\n"" %m = and i32 %i, 31\n"" %c = icmp eq i32 %m, 0\n"" call void @llvm.assume(i1 %c)\n"" ret void\n""}\n""declare void @llvm.assume(i1)\n");AssumptionCache AC(*F);KnownBits Known = computeKnownBits(A, M->getDataLayout(), /* Depth */ 0, &AC, F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), 31u);EXPECT_EQ(Known.One.getZExtValue(), 0u);}TEST_F(ComputeKnownBitsTest, ComputeKnownBitsPtrToIntZext) {// ptrtoint zero extends the pointer type.parseAssembly("define void @test(i8** %p) {\n"" %A = load i8*, i8** %p\n"" %i = ptrtoint i8* %A to i128\n"" %m = and i128 %i, 31\n"" %c = icmp eq i128 %m, 0\n"" call void @llvm.assume(i1 %c)\n"" ret void\n""}\n""declare void @llvm.assume(i1)\n");AssumptionCache AC(*F);KnownBits Known = computeKnownBits(A, M->getDataLayout(), /* Depth */ 0, &AC, F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), 31u);EXPECT_EQ(Known.One.getZExtValue(), 0u);}TEST_F(ComputeKnownBitsTest, ComputeKnownBitsFreeze) {parseAssembly("define void @test() {\n"" %m = call i32 @any_num()\n"" %A = freeze i32 %m\n"" %n = and i32 %m, 31\n"" %c = icmp eq i32 %n, 0\n"" call void @llvm.assume(i1 %c)\n"" ret void\n""}\n""declare void @llvm.assume(i1)\n""declare i32 @any_num()\n");AssumptionCache AC(*F);KnownBits Known = computeKnownBits(A, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), 31u);EXPECT_EQ(Known.One.getZExtValue(), 0u);}TEST_F(ComputeKnownBitsTest, ComputeKnownBitsAddWithRange) {parseAssembly("define void @test(i64* %p) {\n"" %A = load i64, i64* %p, !range !{i64 64, i64 65536}\n"" %APlus512 = add i64 %A, 512\n"" %c = icmp ugt i64 %APlus512, 523\n"" call void @llvm.assume(i1 %c)\n"" ret void\n""}\n""declare void @llvm.assume(i1)\n");AssumptionCache AC(*F);KnownBits Known = computeKnownBits(A, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), ~(65536llu - 1));EXPECT_EQ(Known.One.getZExtValue(), 0u);Instruction &APlus512 = findInstructionByName(F, "APlus512");Known = computeKnownBits(&APlus512, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());// We know of one less zero because 512 may have produced a 1 that// got carried all the way to the first trailing zero.EXPECT_EQ(Known.Zero.getZExtValue(), (~(65536llu - 1)) << 1);EXPECT_EQ(Known.One.getZExtValue(), 0u);// The known range is not precise given computeKnownBits works// with the masks of zeros and ones, not the ranges.EXPECT_EQ(Known.getMinValue(), 0u);EXPECT_EQ(Known.getMaxValue(), 131071);}TEST_F(ComputeKnownBitsTest, ComputeKnownBitsUnknownVScale) {Module M("", Context);IRBuilder<> Builder(Context);Function *TheFn =Intrinsic::getDeclaration(&M, Intrinsic::vscale, {Builder.getInt32Ty()});CallInst *CI = Builder.CreateCall(TheFn, {}, {}, "");KnownBits Known = computeKnownBits(CI, M.getDataLayout(), /* Depth */ 0);// There is no parent function so we cannot look up the vscale_range// attribute to determine the number of bits.EXPECT_EQ(Known.One.getZExtValue(), 0u);EXPECT_EQ(Known.Zero.getZExtValue(), 0u);BasicBlock *BB = BasicBlock::Create(Context);BB->getInstList().push_back(CI);Known = computeKnownBits(CI, M.getDataLayout(), /* Depth */ 0);// There is no parent function so we cannot look up the vscale_range// attribute to determine the number of bits.EXPECT_EQ(Known.One.getZExtValue(), 0u);EXPECT_EQ(Known.Zero.getZExtValue(), 0u);CI->removeFromParent();delete CI;delete BB;}// 512 + [32, 64) doesn't produce overlapping bits.// Make sure we get all the individual bits properly.TEST_F(ComputeKnownBitsTest, ComputeKnownBitsAddWithRangeNoOverlap) {parseAssembly("define void @test(i64* %p) {\n"" %A = load i64, i64* %p, !range !{i64 32, i64 64}\n"" %APlus512 = add i64 %A, 512\n"" %c = icmp ugt i64 %APlus512, 523\n"" call void @llvm.assume(i1 %c)\n"" ret void\n""}\n""declare void @llvm.assume(i1)\n");AssumptionCache AC(*F);KnownBits Known = computeKnownBits(A, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), ~(64llu - 1));EXPECT_EQ(Known.One.getZExtValue(), 32u);Instruction &APlus512 = findInstructionByName(F, "APlus512");Known = computeKnownBits(&APlus512, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), ~512llu & ~(64llu - 1));EXPECT_EQ(Known.One.getZExtValue(), 512u | 32u);// The known range is not precise given computeKnownBits works// with the masks of zeros and ones, not the ranges.EXPECT_EQ(Known.getMinValue(), 544);EXPECT_EQ(Known.getMaxValue(), 575);}TEST_F(ComputeKnownBitsTest, ComputeKnownBitsGEPWithRange) {parseAssembly("define void @test(i64* %p) {\n"" %A = load i64, i64* %p, !range !{i64 64, i64 65536}\n"" %APtr = inttoptr i64 %A to float*"" %APtrPlus512 = getelementptr float, float* %APtr, i32 128\n"" %c = icmp ugt float* %APtrPlus512, inttoptr (i32 523 to float*)\n"" call void @llvm.assume(i1 %c)\n"" ret void\n""}\n""declare void @llvm.assume(i1)\n");AssumptionCache AC(*F);KnownBits Known = computeKnownBits(A, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), ~(65536llu - 1));EXPECT_EQ(Known.One.getZExtValue(), 0u);Instruction &APtrPlus512 = findInstructionByName(F, "APtrPlus512");Known = computeKnownBits(&APtrPlus512, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());// We know of one less zero because 512 may have produced a 1 that// got carried all the way to the first trailing zero.EXPECT_EQ(Known.Zero.getZExtValue(), ~(65536llu - 1) << 1);EXPECT_EQ(Known.One.getZExtValue(), 0u);// The known range is not precise given computeKnownBits works// with the masks of zeros and ones, not the ranges.EXPECT_EQ(Known.getMinValue(), 0u);EXPECT_EQ(Known.getMaxValue(), 131071);}// 4*128 + [32, 64) doesn't produce overlapping bits.// Make sure we get all the individual bits properly.// This test is useful to check that we account for the scaling factor// in the gep. Indeed, gep float, [32,64), 128 is not 128 + [32,64).TEST_F(ComputeKnownBitsTest, ComputeKnownBitsGEPWithRangeNoOverlap) {parseAssembly("define void @test(i64* %p) {\n"" %A = load i64, i64* %p, !range !{i64 32, i64 64}\n"" %APtr = inttoptr i64 %A to float*"" %APtrPlus512 = getelementptr float, float* %APtr, i32 128\n"" %c = icmp ugt float* %APtrPlus512, inttoptr (i32 523 to float*)\n"" call void @llvm.assume(i1 %c)\n"" ret void\n""}\n""declare void @llvm.assume(i1)\n");AssumptionCache AC(*F);KnownBits Known = computeKnownBits(A, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), ~(64llu - 1));EXPECT_EQ(Known.One.getZExtValue(), 32u);Instruction &APtrPlus512 = findInstructionByName(F, "APtrPlus512");Known = computeKnownBits(&APtrPlus512, M->getDataLayout(), /* Depth */ 0, &AC,F->front().getTerminator());EXPECT_EQ(Known.Zero.getZExtValue(), ~512llu & ~(64llu - 1));EXPECT_EQ(Known.One.getZExtValue(), 512u | 32u);// The known range is not precise given computeKnownBits works// with the masks of zeros and ones, not the ranges.EXPECT_EQ(Known.getMinValue(), 544);EXPECT_EQ(Known.getMaxValue(), 575);}TEST_F(ValueTrackingTest, HaveNoCommonBitsSet) {{// Check for an inverted mask: (X & ~M) op (Y & M).auto M = parseModule(R"(define i32 @test(i32 %X, i32 %Y, i32 %M) {%1 = xor i32 %M, -1%LHS = and i32 %1, %X%RHS = and i32 %Y, %M%Ret = add i32 %LHS, %RHSret i32 %Ret})");auto *F = M->getFunction("test");auto *LHS = findInstructionByNameOrNull(F, "LHS");auto *RHS = findInstructionByNameOrNull(F, "RHS");const DataLayout &DL = M->getDataLayout();EXPECT_TRUE(haveNoCommonBitsSet(LHS, RHS, DL));EXPECT_TRUE(haveNoCommonBitsSet(RHS, LHS, DL));}{// Check for (A & B) and ~(A | B)auto M = parseModule(R"(define void @test(i32 %A, i32 %B) {%LHS = and i32 %A, %B%or = or i32 %A, %B%RHS = xor i32 %or, -1%LHS2 = and i32 %B, %A%or2 = or i32 %A, %B%RHS2 = xor i32 %or2, -1ret void})");auto *F = M->getFunction("test");const DataLayout &DL = M->getDataLayout();auto *LHS = findInstructionByNameOrNull(F, "LHS");auto *RHS = findInstructionByNameOrNull(F, "RHS");EXPECT_TRUE(haveNoCommonBitsSet(LHS, RHS, DL));EXPECT_TRUE(haveNoCommonBitsSet(RHS, LHS, DL));auto *LHS2 = findInstructionByNameOrNull(F, "LHS2");auto *RHS2 = findInstructionByNameOrNull(F, "RHS2");EXPECT_TRUE(haveNoCommonBitsSet(LHS2, RHS2, DL));EXPECT_TRUE(haveNoCommonBitsSet(RHS2, LHS2, DL));}{// Check for (A & B) and ~(A | B) in vector versionauto M = parseModule(R"(define void @test(<2 x i32> %A, <2 x i32> %B) {%LHS = and <2 x i32> %A, %B%or = or <2 x i32> %A, %B%RHS = xor <2 x i32> %or, <i32 -1, i32 -1>%LHS2 = and <2 x i32> %B, %A%or2 = or <2 x i32> %A, %B%RHS2 = xor <2 x i32> %or2, <i32 -1, i32 -1>ret void})");auto *F = M->getFunction("test");const DataLayout &DL = M->getDataLayout();auto *LHS = findInstructionByNameOrNull(F, "LHS");auto *RHS = findInstructionByNameOrNull(F, "RHS");EXPECT_TRUE(haveNoCommonBitsSet(LHS, RHS, DL));EXPECT_TRUE(haveNoCommonBitsSet(RHS, LHS, DL));auto *LHS2 = findInstructionByNameOrNull(F, "LHS2");auto *RHS2 = findInstructionByNameOrNull(F, "RHS2");EXPECT_TRUE(haveNoCommonBitsSet(LHS2, RHS2, DL));EXPECT_TRUE(haveNoCommonBitsSet(RHS2, LHS2, DL));}}class IsBytewiseValueTest : public ValueTrackingTest,public ::testing::WithParamInterface<std::pair<const char *, const char *>> {protected:};const std::pair<const char *, const char *> IsBytewiseValueTests[] = {{"i8 0","i48* null",},{"i8 undef","i48* undef",},{"i8 0","i8 zeroinitializer",},{"i8 0","i8 0",},{"i8 -86","i8 -86",},{"i8 -1","i8 -1",},{"i8 undef","i16 undef",},{"i8 0","i16 0",},{"","i16 7",},{"i8 -86","i16 -21846",},{"i8 -1","i16 -1",},{"i8 0","i48 0",},{"i8 -1","i48 -1",},{"i8 0","i49 0",},{"","i49 -1",},{"i8 0","half 0xH0000",},{"i8 -85","half 0xHABAB",},{"i8 0","float 0.0",},{"i8 -1","float 0xFFFFFFFFE0000000",},{"i8 0","double 0.0",},{"i8 -15","double 0xF1F1F1F1F1F1F1F1",},{"i8 undef","i16* undef",},{"i8 0","i16* inttoptr (i64 0 to i16*)",},{"i8 -1","i16* inttoptr (i64 -1 to i16*)",},{"i8 -86","i16* inttoptr (i64 -6148914691236517206 to i16*)",},{"","i16* inttoptr (i48 -1 to i16*)",},{"i8 -1","i16* inttoptr (i96 -1 to i16*)",},{"i8 undef","[0 x i8] zeroinitializer",},{"i8 undef","[0 x i8] undef",},{"i8 undef","[5 x [0 x i8]] zeroinitializer",},{"i8 undef","[5 x [0 x i8]] undef",},{"i8 0","[6 x i8] zeroinitializer",},{"i8 undef","[6 x i8] undef",},{"i8 1","[5 x i8] [i8 1, i8 1, i8 1, i8 1, i8 1]",},{"","[5 x i64] [i64 1, i64 1, i64 1, i64 1, i64 1]",},{"i8 -1","[5 x i64] [i64 -1, i64 -1, i64 -1, i64 -1, i64 -1]",},{"","[4 x i8] [i8 1, i8 2, i8 1, i8 1]",},{"i8 1","[4 x i8] [i8 1, i8 undef, i8 1, i8 1]",},{"i8 0","<6 x i8> zeroinitializer",},{"i8 undef","<6 x i8> undef",},{"i8 1","<5 x i8> <i8 1, i8 1, i8 1, i8 1, i8 1>",},{"","<5 x i64> <i64 1, i64 1, i64 1, i64 1, i64 1>",},{"i8 -1","<5 x i64> <i64 -1, i64 -1, i64 -1, i64 -1, i64 -1>",},{"","<4 x i8> <i8 1, i8 1, i8 2, i8 1>",},{"i8 5","<2 x i8> < i8 5, i8 undef >",},{"i8 0","[2 x [2 x i16]] zeroinitializer",},{"i8 undef","[2 x [2 x i16]] undef",},{"i8 -86","[2 x [2 x i16]] [[2 x i16] [i16 -21846, i16 -21846], ""[2 x i16] [i16 -21846, i16 -21846]]",},{"","[2 x [2 x i16]] [[2 x i16] [i16 -21846, i16 -21846], ""[2 x i16] [i16 -21836, i16 -21846]]",},{"i8 undef","{ } zeroinitializer",},{"i8 undef","{ } undef",},{"i8 undef","{ {}, {} } zeroinitializer",},{"i8 undef","{ {}, {} } undef",},{"i8 0","{i8, i64, i16*} zeroinitializer",},{"i8 undef","{i8, i64, i16*} undef",},{"i8 -86","{i8, i64, i16*} {i8 -86, i64 -6148914691236517206, i16* undef}",},{"","{i8, i64, i16*} {i8 86, i64 -6148914691236517206, i16* undef}",},};INSTANTIATE_TEST_SUITE_P(IsBytewiseValueParamTests, IsBytewiseValueTest,::testing::ValuesIn(IsBytewiseValueTests));TEST_P(IsBytewiseValueTest, IsBytewiseValue) {auto M = parseModule(std::string("@test = global ") + GetParam().second);GlobalVariable *GV = dyn_cast<GlobalVariable>(M->getNamedValue("test"));Value *Actual = isBytewiseValue(GV->getInitializer(), M->getDataLayout());std::string Buff;raw_string_ostream S(Buff);if (Actual)S << *Actual;EXPECT_EQ(GetParam().first, S.str());}TEST_F(ValueTrackingTest, ComputeConstantRange) {{// Assumptions:// * stride >= 5// * stride < 10//// stride = [5, 10)auto M = parseModule(R"(declare void @llvm.assume(i1)define i32 @test(i32 %stride) {%gt = icmp uge i32 %stride, 5call void @llvm.assume(i1 %gt)%lt = icmp ult i32 %stride, 10call void @llvm.assume(i1 %lt)%stride.plus.one = add nsw nuw i32 %stride, 1ret i32 %stride.plus.one})");Function *F = M->getFunction("test");AssumptionCache AC(*F);Value *Stride = &*F->arg_begin();ConstantRange CR1 = computeConstantRange(Stride, false, true, &AC, nullptr);EXPECT_TRUE(CR1.isFullSet());Instruction *I = &findInstructionByName(F, "stride.plus.one");ConstantRange CR2 = computeConstantRange(Stride, false, true, &AC, I);EXPECT_EQ(5, CR2.getLower());EXPECT_EQ(10, CR2.getUpper());}{// Assumptions:// * stride >= 5// * stride < 200// * stride == 99//// stride = [99, 100)auto M = parseModule(R"(declare void @llvm.assume(i1)define i32 @test(i32 %stride) {%gt = icmp uge i32 %stride, 5call void @llvm.assume(i1 %gt)%lt = icmp ult i32 %stride, 200call void @llvm.assume(i1 %lt)%eq = icmp eq i32 %stride, 99call void @llvm.assume(i1 %eq)%stride.plus.one = add nsw nuw i32 %stride, 1ret i32 %stride.plus.one})");Function *F = M->getFunction("test");AssumptionCache AC(*F);Value *Stride = &*F->arg_begin();Instruction *I = &findInstructionByName(F, "stride.plus.one");ConstantRange CR = computeConstantRange(Stride, false, true, &AC, I);EXPECT_EQ(99, *CR.getSingleElement());}{// Assumptions:// * stride >= 5// * stride >= 50// * stride < 100// * stride < 200//// stride = [50, 100)auto M = parseModule(R"(declare void @llvm.assume(i1)define i32 @test(i32 %stride, i1 %cond) {%gt = icmp uge i32 %stride, 5call void @llvm.assume(i1 %gt)%gt.2 = icmp uge i32 %stride, 50call void @llvm.assume(i1 %gt.2)br i1 %cond, label %bb1, label %bb2bb1:%lt = icmp ult i32 %stride, 200call void @llvm.assume(i1 %lt)%lt.2 = icmp ult i32 %stride, 100call void @llvm.assume(i1 %lt.2)%stride.plus.one = add nsw nuw i32 %stride, 1ret i32 %stride.plus.onebb2:ret i32 0})");Function *F = M->getFunction("test");AssumptionCache AC(*F);Value *Stride = &*F->arg_begin();Instruction *GT2 = &findInstructionByName(F, "gt.2");ConstantRange CR = computeConstantRange(Stride, false, true, &AC, GT2);EXPECT_EQ(5, CR.getLower());EXPECT_EQ(0, CR.getUpper());Instruction *I = &findInstructionByName(F, "stride.plus.one");ConstantRange CR2 = computeConstantRange(Stride, false, true, &AC, I);EXPECT_EQ(50, CR2.getLower());EXPECT_EQ(100, CR2.getUpper());}{// Assumptions:// * stride > 5// * stride < 5//// stride = empty range, as the assumptions contradict each other.auto M = parseModule(R"(declare void @llvm.assume(i1)define i32 @test(i32 %stride, i1 %cond) {%gt = icmp ugt i32 %stride, 5call void @llvm.assume(i1 %gt)%lt = icmp ult i32 %stride, 5call void @llvm.assume(i1 %lt)%stride.plus.one = add nsw nuw i32 %stride, 1ret i32 %stride.plus.one})");Function *F = M->getFunction("test");AssumptionCache AC(*F);Value *Stride = &*F->arg_begin();Instruction *I = &findInstructionByName(F, "stride.plus.one");ConstantRange CR = computeConstantRange(Stride, false, true, &AC, I);EXPECT_TRUE(CR.isEmptySet());}{// Assumptions:// * x.1 >= 5// * x.2 < x.1//// stride = [0, -1)auto M = parseModule(R"(declare void @llvm.assume(i1)define i32 @test(i32 %x.1, i32 %x.2) {%gt = icmp uge i32 %x.1, 5call void @llvm.assume(i1 %gt)%lt = icmp ult i32 %x.2, %x.1call void @llvm.assume(i1 %lt)%stride.plus.one = add nsw nuw i32 %x.1, 1ret i32 %stride.plus.one})");Function *F = M->getFunction("test");AssumptionCache AC(*F);Value *X1 = &*(F->arg_begin());Value *X2 = &*std::next(F->arg_begin());Instruction *I = &findInstructionByName(F, "stride.plus.one");ConstantRange CR1 = computeConstantRange(X1, false, true, &AC, I);ConstantRange CR2 = computeConstantRange(X2, false, true, &AC, I);EXPECT_EQ(5, CR1.getLower());EXPECT_EQ(0, CR1.getUpper());EXPECT_EQ(0, CR2.getLower());EXPECT_EQ(0xffffffff, CR2.getUpper());// Check the depth cutoff results in a conservative result (full set) by// passing Depth == MaxDepth == 6.ConstantRange CR3 = computeConstantRange(X2, false, true, &AC, I, nullptr, 6);EXPECT_TRUE(CR3.isFullSet());}{// Assumptions:// * x.2 <= x.1auto M = parseModule(R"(declare void @llvm.assume(i1)define i32 @test(i32 %x.1, i32 %x.2) {%lt = icmp ule i32 %x.2, %x.1call void @llvm.assume(i1 %lt)%stride.plus.one = add nsw nuw i32 %x.1, 1ret i32 %stride.plus.one})");Function *F = M->getFunction("test");AssumptionCache AC(*F);Value *X2 = &*std::next(F->arg_begin());Instruction *I = &findInstructionByName(F, "stride.plus.one");ConstantRange CR1 = computeConstantRange(X2, false, true, &AC, I);// If we don't know the value of x.2, we don't know the value of x.1.EXPECT_TRUE(CR1.isFullSet());}}struct FindAllocaForValueTestParams {const char *IR;bool AnyOffsetResult;bool ZeroOffsetResult;};class FindAllocaForValueTest: public ValueTrackingTest,public ::testing::WithParamInterface<FindAllocaForValueTestParams> {protected:};const FindAllocaForValueTestParams FindAllocaForValueTests[] = {{R"(define void @test() {%a = alloca i64%r = bitcast i64* %a to i32*ret void})",true, true},{R"(define void @test() {%a = alloca i32%r = getelementptr i32, i32* %a, i32 1ret void})",true, false},{R"(define void @test() {%a = alloca i32%r = getelementptr i32, i32* %a, i32 0ret void})",true, true},{R"(define void @test(i1 %cond) {entry:%a = alloca i32br label %bb1bb1:%r = phi i32* [ %a, %entry ], [ %r, %bb1 ]br i1 %cond, label %bb1, label %exitexit:ret void})",true, true},{R"(define void @test(i1 %cond) {%a = alloca i32%r = select i1 %cond, i32* %a, i32* %aret void})",true, true},{R"(define void @test(i1 %cond) {%a = alloca i32%b = alloca i32%r = select i1 %cond, i32* %a, i32* %bret void})",false, false},{R"(define void @test(i1 %cond) {entry:%a = alloca i64%a32 = bitcast i64* %a to i32*br label %bb1bb1:%x = phi i32* [ %a32, %entry ], [ %x, %bb1 ]%r = getelementptr i32, i32* %x, i32 1br i1 %cond, label %bb1, label %exitexit:ret void})",true, false},{R"(define void @test(i1 %cond) {entry:%a = alloca i64%a32 = bitcast i64* %a to i32*br label %bb1bb1:%x = phi i32* [ %a32, %entry ], [ %r, %bb1 ]%r = getelementptr i32, i32* %x, i32 1br i1 %cond, label %bb1, label %exitexit:ret void})",true, false},{R"(define void @test(i1 %cond, i64* %a) {entry:%r = bitcast i64* %a to i32*ret void})",false, false},{R"(define void @test(i1 %cond) {entry:%a = alloca i32%b = alloca i32br label %bb1bb1:%r = phi i32* [ %a, %entry ], [ %b, %bb1 ]br i1 %cond, label %bb1, label %exitexit:ret void})",false, false},{R"(declare i32* @retptr(i32* returned)define void @test(i1 %cond) {%a = alloca i32%r = call i32* @retptr(i32* %a)ret void})",true, true},{R"(declare i32* @fun(i32*)define void @test(i1 %cond) {%a = alloca i32%r = call i32* @fun(i32* %a)ret void})",false, false},};TEST_P(FindAllocaForValueTest, findAllocaForValue) {auto M = parseModule(GetParam().IR);Function *F = M->getFunction("test");Instruction *I = &findInstructionByName(F, "r");const AllocaInst *AI = findAllocaForValue(I);EXPECT_EQ(!!AI, GetParam().AnyOffsetResult);}TEST_P(FindAllocaForValueTest, findAllocaForValueZeroOffset) {auto M = parseModule(GetParam().IR);Function *F = M->getFunction("test");Instruction *I = &findInstructionByName(F, "r");const AllocaInst *AI = findAllocaForValue(I, true);EXPECT_EQ(!!AI, GetParam().ZeroOffsetResult);}INSTANTIATE_TEST_SUITE_P(FindAllocaForValueTest, FindAllocaForValueTest,::testing::ValuesIn(FindAllocaForValueTests));
//===- ValueLatticeTest.cpp - ScalarEvolution unit tests --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/ValueLattice.h"#include "llvm/ADT/SmallVector.h"#include "llvm/IR/ConstantRange.h"#include "llvm/IR/Constants.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"namespace llvm {namespace {// We use this fixture to ensure that we clean up ScalarEvolution before// deleting the PassManager.class ValueLatticeTest : public testing::Test {protected:LLVMContext Context;};TEST_F(ValueLatticeTest, ValueLatticeGetters) {auto I32Ty = IntegerType::get(Context, 32);auto *C1 = ConstantInt::get(I32Ty, 1);EXPECT_TRUE(ValueLatticeElement::get(C1).isConstantRange());EXPECT_TRUE(ValueLatticeElement::getRange({C1->getValue()}).isConstantRange());EXPECT_TRUE(ValueLatticeElement::getOverdefined().isOverdefined());auto FloatTy = Type::getFloatTy(Context);auto *C2 = ConstantFP::get(FloatTy, 1.1);EXPECT_TRUE(ValueLatticeElement::get(C2).isConstant());EXPECT_TRUE(ValueLatticeElement::getNot(C2).isNotConstant());}TEST_F(ValueLatticeTest, MarkConstantRange) {auto LV1 =ValueLatticeElement::getRange({APInt(32, 10, true), APInt(32, 20, true)});// Test markConstantRange() with an equal range.EXPECT_FALSE(LV1.markConstantRange({APInt(32, 10, true), APInt(32, 20, true)}));// Test markConstantRange() with supersets of existing range.EXPECT_TRUE(LV1.markConstantRange({APInt(32, 5, true), APInt(32, 20, true)}));EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 5U);EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 20U);EXPECT_TRUE(LV1.markConstantRange({APInt(32, 5, true), APInt(32, 23, true)}));EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 5U);EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 23U);}TEST_F(ValueLatticeTest, MergeIn) {auto I32Ty = IntegerType::get(Context, 32);auto *C1 = ConstantInt::get(I32Ty, 1);// Merge to lattice values with equal integer constant.auto LV1 = ValueLatticeElement::get(C1);EXPECT_FALSE(LV1.mergeIn(ValueLatticeElement::get(C1)));EXPECT_TRUE(LV1.isConstantRange());EXPECT_EQ(LV1.asConstantInteger()->getLimitedValue(), 1U);// Merge LV1 with different integer constant.EXPECT_TRUE(LV1.mergeIn(ValueLatticeElement::get(ConstantInt::get(I32Ty, 99))));EXPECT_TRUE(LV1.isConstantRange());EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 1U);EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 100U);// Merge constant range with same constant range.EXPECT_FALSE(LV1.mergeIn(LV1));EXPECT_TRUE(LV1.isConstantRange());EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 1U);EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 100U);// Merge LV1 in undefined value.ValueLatticeElement LV2;EXPECT_TRUE(LV2.mergeIn(LV1));EXPECT_TRUE(LV1.isConstantRange());EXPECT_EQ(LV1.getConstantRange().getLower().getLimitedValue(), 1U);EXPECT_EQ(LV1.getConstantRange().getUpper().getLimitedValue(), 100U);EXPECT_TRUE(LV2.isConstantRange());EXPECT_EQ(LV2.getConstantRange().getLower().getLimitedValue(), 1U);EXPECT_EQ(LV2.getConstantRange().getUpper().getLimitedValue(), 100U);// Merge LV1 with overdefined.EXPECT_TRUE(LV1.mergeIn(ValueLatticeElement::getOverdefined()));EXPECT_TRUE(LV1.isOverdefined());// Merge overdefined with overdefined.EXPECT_FALSE(LV1.mergeIn(ValueLatticeElement::getOverdefined()));EXPECT_TRUE(LV1.isOverdefined());}TEST_F(ValueLatticeTest, getCompareIntegers) {auto *I32Ty = IntegerType::get(Context, 32);auto *I1Ty = IntegerType::get(Context, 1);auto *C1 = ConstantInt::get(I32Ty, 1);auto LV1 = ValueLatticeElement::get(C1);// Check getCompare for equal integer constants.EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_EQ, I1Ty, LV1)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGE, I1Ty, LV1)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLE, I1Ty, LV1)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_NE, I1Ty, LV1)->isZeroValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLT, I1Ty, LV1)->isZeroValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGT, I1Ty, LV1)->isZeroValue());auto LV2 =ValueLatticeElement::getRange({APInt(32, 10, true), APInt(32, 20, true)});// Check getCompare with distinct integer ranges.EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLT, I1Ty, LV2)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SLE, I1Ty, LV2)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_NE, I1Ty, LV2)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_EQ, I1Ty, LV2)->isZeroValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGE, I1Ty, LV2)->isZeroValue());EXPECT_TRUE(LV1.getCompare(CmpInst::ICMP_SGT, I1Ty, LV2)->isZeroValue());auto LV3 =ValueLatticeElement::getRange({APInt(32, 15, true), APInt(32, 19, true)});// Check getCompare with a subset integer ranges.EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SLT, I1Ty, LV3), nullptr);EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SLE, I1Ty, LV3), nullptr);EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_NE, I1Ty, LV3), nullptr);EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_EQ, I1Ty, LV3), nullptr);EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SGE, I1Ty, LV3), nullptr);EXPECT_EQ(LV2.getCompare(CmpInst::ICMP_SGT, I1Ty, LV3), nullptr);auto LV4 =ValueLatticeElement::getRange({APInt(32, 15, true), APInt(32, 25, true)});// Check getCompare with overlapping integer ranges.EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SLT, I1Ty, LV4), nullptr);EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SLE, I1Ty, LV4), nullptr);EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_NE, I1Ty, LV4), nullptr);EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_EQ, I1Ty, LV4), nullptr);EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SGE, I1Ty, LV4), nullptr);EXPECT_EQ(LV3.getCompare(CmpInst::ICMP_SGT, I1Ty, LV4), nullptr);}TEST_F(ValueLatticeTest, getCompareFloat) {auto *FloatTy = IntegerType::getFloatTy(Context);auto *I1Ty = IntegerType::get(Context, 1);auto *C1 = ConstantFP::get(FloatTy, 1.0);auto LV1 = ValueLatticeElement::get(C1);auto LV2 = ValueLatticeElement::get(C1);// Check getCompare for equal floating point constants.EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OEQ, I1Ty, LV2)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OGE, I1Ty, LV2)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OLE, I1Ty, LV2)->isOneValue());EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_ONE, I1Ty, LV2)->isZeroValue());EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OLT, I1Ty, LV2)->isZeroValue());EXPECT_TRUE(LV1.getCompare(CmpInst::FCMP_OGT, I1Ty, LV2)->isZeroValue());EXPECT_TRUE(LV1.mergeIn(ValueLatticeElement::get(ConstantFP::get(FloatTy, 2.2))));EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OEQ, I1Ty, LV2), nullptr);EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OGE, I1Ty, LV2), nullptr);EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OLE, I1Ty, LV2), nullptr);EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_ONE, I1Ty, LV2), nullptr);EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OLT, I1Ty, LV2), nullptr);EXPECT_EQ(LV1.getCompare(CmpInst::FCMP_OGT, I1Ty, LV2), nullptr);}TEST_F(ValueLatticeTest, getCompareUndef) {auto *I32Ty = IntegerType::get(Context, 32);auto *I1Ty = IntegerType::get(Context, 1);auto LV1 = ValueLatticeElement::get(UndefValue::get(I32Ty));auto LV2 =ValueLatticeElement::getRange({APInt(32, 10, true), APInt(32, 20, true)});EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SLT, I1Ty, LV2)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SLE, I1Ty, LV2)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_NE, I1Ty, LV2)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_EQ, I1Ty, LV2)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SGE, I1Ty, LV2)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::ICMP_SGT, I1Ty, LV2)));auto *FloatTy = IntegerType::getFloatTy(Context);auto LV3 = ValueLatticeElement::get(ConstantFP::get(FloatTy, 1.0));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OEQ, I1Ty, LV3)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OGE, I1Ty, LV3)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OLE, I1Ty, LV3)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_ONE, I1Ty, LV3)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OLT, I1Ty, LV3)));EXPECT_TRUE(isa<UndefValue>(LV1.getCompare(CmpInst::FCMP_OGT, I1Ty, LV3)));}} // end anonymous namespace} // end namespace llvm
//===- UnrollAnalyzerTest.cpp - UnrollAnalyzer unit tests -----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/LoopUnrollAnalyzer.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/InitializePasses.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {void initializeUnrollAnalyzerTestPass(PassRegistry &);static SmallVector<DenseMap<Value *, Value *>, 16> SimplifiedValuesVector;static unsigned TripCount = 0;namespace {struct UnrollAnalyzerTest : public FunctionPass {static char ID;bool runOnFunction(Function &F) override {LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();ScalarEvolution *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();Function::iterator FI = F.begin();FI++; // First basic block is entry - skip it.BasicBlock *Header = &*FI++;Loop *L = LI->getLoopFor(Header);BasicBlock *Exiting = L->getExitingBlock();SimplifiedValuesVector.clear();TripCount = SE->getSmallConstantTripCount(L, Exiting);for (unsigned Iteration = 0; Iteration < TripCount; Iteration++) {DenseMap<Value *, Value *> SimplifiedValues;UnrolledInstAnalyzer Analyzer(Iteration, SimplifiedValues, *SE, L);for (auto *BB : L->getBlocks())for (Instruction &I : *BB)Analyzer.visit(I);SimplifiedValuesVector.push_back(SimplifiedValues);}return false;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.addRequired<DominatorTreeWrapperPass>();AU.addRequired<LoopInfoWrapperPass>();AU.addRequired<ScalarEvolutionWrapperPass>();AU.setPreservesAll();}UnrollAnalyzerTest() : FunctionPass(ID) {initializeUnrollAnalyzerTestPass(*PassRegistry::getPassRegistry());}};}char UnrollAnalyzerTest::ID = 0;std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,const char *ModuleStr) {SMDiagnostic Err;return parseAssemblyString(ModuleStr, Err, Context);}TEST(UnrollAnalyzerTest, BasicSimplifications) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define i64 @propagate_loop_phis() {\n""entry:\n"" br label %loop\n""loop:\n"" %iv = phi i64 [ 0, %entry ], [ %inc, %loop ]\n"" %x0 = phi i64 [ 0, %entry ], [ %x2, %loop ]\n"" %x1 = or i64 %x0, 1\n"" %x2 = or i64 %x1, 2\n"" %inc = add nuw nsw i64 %iv, 1\n"" %cond = icmp sge i64 %inc, 8\n"" br i1 %cond, label %loop.end, label %loop\n""loop.end:\n"" %x.lcssa = phi i64 [ %x2, %loop ]\n"" ret i64 %x.lcssa\n""}\n";UnrollAnalyzerTest *P = new UnrollAnalyzerTest();LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);legacy::PassManager Passes;Passes.add(P);Passes.run(*M);// Perform checksModule::iterator MI = M->begin();Function *F = &*MI++;Function::iterator FI = F->begin();FI++; // First basic block is entry - skip it.BasicBlock *Header = &*FI++;BasicBlock::iterator BBI = Header->begin();std::advance(BBI, 4);Instruction *Y1 = &*BBI++;Instruction *Y2 = &*BBI++;// Check simplification expected on the 1st iteration.// Check that "%inc = add nuw nsw i64 %iv, 1" is simplified to 1auto I1 = SimplifiedValuesVector[0].find(Y1);EXPECT_TRUE(I1 != SimplifiedValuesVector[0].end());EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 1U);// Check that "%cond = icmp sge i64 %inc, 10" is simplified to falseauto I2 = SimplifiedValuesVector[0].find(Y2);EXPECT_TRUE(I2 != SimplifiedValuesVector[0].end());EXPECT_FALSE(cast<ConstantInt>((*I2).second)->getZExtValue());// Check simplification expected on the last iteration.// Check that "%inc = add nuw nsw i64 %iv, 1" is simplified to 8I1 = SimplifiedValuesVector[TripCount - 1].find(Y1);EXPECT_TRUE(I1 != SimplifiedValuesVector[TripCount - 1].end());EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), TripCount);// Check that "%cond = icmp sge i64 %inc, 10" is simplified to falseI2 = SimplifiedValuesVector[TripCount - 1].find(Y2);EXPECT_TRUE(I2 != SimplifiedValuesVector[TripCount - 1].end());EXPECT_TRUE(cast<ConstantInt>((*I2).second)->getZExtValue());}TEST(UnrollAnalyzerTest, OuterLoopSimplification) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo() {\n""entry:\n"" br label %outer.loop\n""outer.loop:\n"" %iv.outer = phi i64 [ 0, %entry ], [ %iv.outer.next, %outer.loop.latch ]\n"" %iv.outer.next = add nuw nsw i64 %iv.outer, 1\n"" br label %inner.loop\n""inner.loop:\n"" %iv.inner = phi i64 [ 0, %outer.loop ], [ %iv.inner.next, %inner.loop ]\n"" %iv.inner.next = add nuw nsw i64 %iv.inner, 1\n"" %exitcond.inner = icmp eq i64 %iv.inner.next, 1000\n"" br i1 %exitcond.inner, label %outer.loop.latch, label %inner.loop\n""outer.loop.latch:\n"" %exitcond.outer = icmp eq i64 %iv.outer.next, 40\n"" br i1 %exitcond.outer, label %exit, label %outer.loop\n""exit:\n"" ret void\n""}\n";UnrollAnalyzerTest *P = new UnrollAnalyzerTest();LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);legacy::PassManager Passes;Passes.add(P);Passes.run(*M);Module::iterator MI = M->begin();Function *F = &*MI++;Function::iterator FI = F->begin();FI++;BasicBlock *Header = &*FI++;BasicBlock *InnerBody = &*FI++;BasicBlock::iterator BBI = Header->begin();BBI++;Instruction *Y1 = &*BBI;BBI = InnerBody->begin();BBI++;Instruction *Y2 = &*BBI;// Check that we can simplify IV of the outer loop, but can't simplify the IV// of the inner loop if we only know the iteration number of the outer loop.//// Y1 is %iv.outer.next, Y2 is %iv.inner.nextauto I1 = SimplifiedValuesVector[0].find(Y1);EXPECT_TRUE(I1 != SimplifiedValuesVector[0].end());auto I2 = SimplifiedValuesVector[0].find(Y2);EXPECT_TRUE(I2 == SimplifiedValuesVector[0].end());}TEST(UnrollAnalyzerTest, CmpSimplifications) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @branch_iv_trunc() {\n""entry:\n"" br label %for.body\n""for.body:\n"" %indvars.iv = phi i64 [ 0, %entry ], [ %tmp3, %for.body ]\n"" %tmp2 = trunc i64 %indvars.iv to i32\n"" %cmp3 = icmp eq i32 %tmp2, 5\n"" %tmp3 = add nuw nsw i64 %indvars.iv, 1\n"" %exitcond = icmp eq i64 %tmp3, 10\n"" br i1 %exitcond, label %for.end, label %for.body\n""for.end:\n"" ret void\n""}\n";UnrollAnalyzerTest *P = new UnrollAnalyzerTest();LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);legacy::PassManager Passes;Passes.add(P);Passes.run(*M);// Perform checksModule::iterator MI = M->begin();Function *F = &*MI++;Function::iterator FI = F->begin();FI++; // First basic block is entry - skip it.BasicBlock *Header = &*FI++;BasicBlock::iterator BBI = Header->begin();BBI++;Instruction *Y1 = &*BBI++;Instruction *Y2 = &*BBI++;// Check simplification expected on the 5th iteration.// Check that "%tmp2 = trunc i64 %indvars.iv to i32" is simplified to 5// and "%cmp3 = icmp eq i32 %tmp2, 5" is simplified to 1 (i.e. true).auto I1 = SimplifiedValuesVector[5].find(Y1);EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end());EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 5U);auto I2 = SimplifiedValuesVector[5].find(Y2);EXPECT_TRUE(I2 != SimplifiedValuesVector[5].end());EXPECT_EQ(cast<ConstantInt>((*I2).second)->getZExtValue(), 1U);}TEST(UnrollAnalyzerTest, PtrCmpSimplifications) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @ptr_cmp(i8 *%a) {\n""entry:\n"" %limit = getelementptr i8, i8* %a, i64 40\n"" %start.iv2 = getelementptr i8, i8* %a, i64 7\n"" br label %loop.body\n""loop.body:\n"" %iv.0 = phi i8* [ %a, %entry ], [ %iv.1, %loop.body ]\n"" %iv2.0 = phi i8* [ %start.iv2, %entry ], [ %iv2.1, %loop.body ]\n"" %cmp = icmp eq i8* %iv2.0, %iv.0\n"" %iv.1 = getelementptr inbounds i8, i8* %iv.0, i64 1\n"" %iv2.1 = getelementptr inbounds i8, i8* %iv2.0, i64 1\n"" %exitcond = icmp ne i8* %iv.1, %limit\n"" br i1 %exitcond, label %loop.body, label %loop.exit\n""loop.exit:\n"" ret void\n""}\n";UnrollAnalyzerTest *P = new UnrollAnalyzerTest();LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);legacy::PassManager Passes;Passes.add(P);Passes.run(*M);// Perform checksModule::iterator MI = M->begin();Function *F = &*MI++;Function::iterator FI = F->begin();FI++; // First basic block is entry - skip it.BasicBlock *Header = &*FI;BasicBlock::iterator BBI = Header->begin();std::advance(BBI, 2);Instruction *Y1 = &*BBI;// Check simplification expected on the 5th iteration.// Check that "%cmp = icmp eq i8* %iv2.0, %iv.0" is simplified to 0.auto I1 = SimplifiedValuesVector[5].find(Y1);EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end());EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 0U);}TEST(UnrollAnalyzerTest, CastSimplifications) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""@known_constant = internal unnamed_addr constant [10 x i32] [i32 0, i32 1, i32 0, i32 1, i32 0, i32 259, i32 0, i32 1, i32 0, i32 1], align 16\n""define void @const_load_cast() {\n""entry:\n"" br label %loop\n""\n""loop:\n"" %iv = phi i64 [ 0, %entry ], [ %inc, %loop ]\n"" %array_const_idx = getelementptr inbounds [10 x i32], [10 x i32]* @known_constant, i64 0, i64 %iv\n"" %const_array_element = load i32, i32* %array_const_idx, align 4\n"" %se = sext i32 %const_array_element to i64\n"" %ze = zext i32 %const_array_element to i64\n"" %tr = trunc i32 %const_array_element to i8\n"" %inc = add nuw nsw i64 %iv, 1\n"" %exitcond86.i = icmp eq i64 %inc, 10\n"" br i1 %exitcond86.i, label %loop.end, label %loop\n""\n""loop.end:\n"" ret void\n""}\n";UnrollAnalyzerTest *P = new UnrollAnalyzerTest();LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);legacy::PassManager Passes;Passes.add(P);Passes.run(*M);// Perform checksModule::iterator MI = M->begin();Function *F = &*MI++;Function::iterator FI = F->begin();FI++; // First basic block is entry - skip it.BasicBlock *Header = &*FI++;BasicBlock::iterator BBI = Header->begin();std::advance(BBI, 3);Instruction *Y1 = &*BBI++;Instruction *Y2 = &*BBI++;Instruction *Y3 = &*BBI++;// Check simplification expected on the 5th iteration.// "%se = sext i32 %const_array_element to i64" should be simplified to 259,// "%ze = zext i32 %const_array_element to i64" should be simplified to 259,// "%tr = trunc i32 %const_array_element to i8" should be simplified to 3.auto I1 = SimplifiedValuesVector[5].find(Y1);EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end());EXPECT_EQ(cast<ConstantInt>((*I1).second)->getZExtValue(), 259U);auto I2 = SimplifiedValuesVector[5].find(Y2);EXPECT_TRUE(I2 != SimplifiedValuesVector[5].end());EXPECT_EQ(cast<ConstantInt>((*I2).second)->getZExtValue(), 259U);auto I3 = SimplifiedValuesVector[5].find(Y3);EXPECT_TRUE(I3 != SimplifiedValuesVector[5].end());EXPECT_EQ(cast<ConstantInt>((*I3).second)->getZExtValue(), 3U);}} // end namespace llvmINITIALIZE_PASS_BEGIN(UnrollAnalyzerTest, "unrollanalyzertestpass","unrollanalyzertestpass", false, false)INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)INITIALIZE_PASS_END(UnrollAnalyzerTest, "unrollanalyzertestpass","unrollanalyzertestpass", false, false)
//===- TensorSpecTest.cpp - test for TensorSpec ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/TensorSpec.h"#include "llvm/Support/Path.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"using namespace llvm;extern const char *TestMainArgv0;TEST(TensorSpecTest, JSONParsing) {auto Value = json::parse(R"({"name": "tensor_name","port": 2,"type": "int32_t","shape":[1,4]})");EXPECT_TRUE(!!Value);LLVMContext Ctx;Optional<TensorSpec> Spec = getTensorSpecFromJSON(Ctx, *Value);EXPECT_TRUE(Spec);EXPECT_EQ(*Spec, TensorSpec::createSpec<int32_t>("tensor_name", {1, 4}, 2));}TEST(TensorSpecTest, JSONParsingInvalidTensorType) {auto Value = json::parse(R"({"name": "tensor_name","port": 2,"type": "no such type","shape":[1,4]})");EXPECT_TRUE(!!Value);LLVMContext Ctx;auto Spec = getTensorSpecFromJSON(Ctx, *Value);EXPECT_FALSE(Spec);}TEST(TensorSpecTest, TensorSpecSizesAndTypes) {auto Spec1D = TensorSpec::createSpec<int16_t>("Hi1", {1});auto Spec2D = TensorSpec::createSpec<int16_t>("Hi2", {1, 1});auto Spec1DLarge = TensorSpec::createSpec<float>("Hi3", {10});auto Spec3DLarge = TensorSpec::createSpec<float>("Hi3", {2, 4, 10});EXPECT_TRUE(Spec1D.isElementType<int16_t>());EXPECT_FALSE(Spec3DLarge.isElementType<double>());EXPECT_EQ(Spec1D.getElementCount(), 1U);EXPECT_EQ(Spec2D.getElementCount(), 1U);EXPECT_EQ(Spec1DLarge.getElementCount(), 10U);EXPECT_EQ(Spec3DLarge.getElementCount(), 80U);EXPECT_EQ(Spec3DLarge.getElementByteSize(), sizeof(float));EXPECT_EQ(Spec1D.getElementByteSize(), sizeof(int16_t));}
//===--- TargetLibraryInfoTest.cpp - TLI/LibFunc unit tests ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {class TargetLibraryInfoTest : public testing::Test {protected:LLVMContext Context;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;std::unique_ptr<Module> M;TargetLibraryInfoTest() : TLI(TLII) {}void parseAssembly(const char *Assembly) {SMDiagnostic Error;M = parseAssemblyString(Assembly, Error, Context);std::string errMsg;raw_string_ostream os(errMsg);Error.print("", os);if (!M)report_fatal_error(Twine(os.str()));}::testing::AssertionResult isLibFunc(const Function *FDecl,LibFunc ExpectedLF) {StringRef ExpectedLFName = TLI.getName(ExpectedLF);if (!FDecl)return ::testing::AssertionFailure() << ExpectedLFName << " not found";LibFunc F;if (!TLI.getLibFunc(*FDecl, F))return ::testing::AssertionFailure() << ExpectedLFName << " invalid";return ::testing::AssertionSuccess() << ExpectedLFName << " is LibFunc";}};} // end anonymous namespace// Check that we don't accept egregiously incorrect prototypes.TEST_F(TargetLibraryInfoTest, InvalidProto) {parseAssembly("%foo = type { %foo }\n");auto *StructTy = StructType::getTypeByName(Context, "foo");auto *InvalidFTy = FunctionType::get(StructTy, /*isVarArg=*/false);for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) {LibFunc LF = (LibFunc)FI;auto *F = cast<Function>(M->getOrInsertFunction(TLI.getName(LF), InvalidFTy).getCallee());EXPECT_FALSE(isLibFunc(F, LF));}}// Check that we do accept know-correct prototypes.TEST_F(TargetLibraryInfoTest, ValidProto) {parseAssembly(// These functions use a 64-bit size_t; use the appropriate datalayout."target datalayout = \"p:64:64:64\"\n"// Struct pointers are replaced with an opaque pointer."%struct = type opaque\n"// These functions were extracted as-is from the OS X headers."declare double @__cospi(double)\n""declare float @__cospif(float)\n""declare { double, double } @__sincospi_stret(double)\n""declare <2 x float> @__sincospif_stret(float)\n""declare double @__sinpi(double)\n""declare float @__sinpif(float)\n""declare i32 @abs(i32)\n""declare i32 @access(i8*, i32)\n""declare double @acos(double)\n""declare float @acosf(float)\n""declare double @acosh(double)\n""declare float @acoshf(float)\n""declare x86_fp80 @acoshl(x86_fp80)\n""declare x86_fp80 @acosl(x86_fp80)\n""declare i8* @aligned_alloc(i64, i64)\n""declare double @asin(double)\n""declare float @asinf(float)\n""declare double @asinh(double)\n""declare float @asinhf(float)\n""declare x86_fp80 @asinhl(x86_fp80)\n""declare x86_fp80 @asinl(x86_fp80)\n""declare double @atan(double)\n""declare double @atan2(double, double)\n""declare float @atan2f(float, float)\n""declare x86_fp80 @atan2l(x86_fp80, x86_fp80)\n""declare float @atanf(float)\n""declare double @atanh(double)\n""declare float @atanhf(float)\n""declare x86_fp80 @atanhl(x86_fp80)\n""declare x86_fp80 @atanl(x86_fp80)\n""declare double @atof(i8*)\n""declare i32 @atoi(i8*)\n""declare i64 @atol(i8*)\n""declare i64 @atoll(i8*)\n""declare i32 @bcmp(i8*, i8*, i64)\n""declare void @bcopy(i8*, i8*, i64)\n""declare void @bzero(i8*, i64)\n""declare i8* @calloc(i64, i64)\n""declare double @cbrt(double)\n""declare float @cbrtf(float)\n""declare x86_fp80 @cbrtl(x86_fp80)\n""declare double @ceil(double)\n""declare float @ceilf(float)\n""declare x86_fp80 @ceill(x86_fp80)\n""declare i32 @chown(i8*, i32, i32)\n""declare void @clearerr(%struct*)\n""declare double @copysign(double, double)\n""declare float @copysignf(float, float)\n""declare x86_fp80 @copysignl(x86_fp80, x86_fp80)\n""declare double @cabs([2 x double])\n""declare float @cabsf([2 x float])\n""declare x86_fp80 @cabsl([2 x x86_fp80])\n""declare double @cos(double)\n""declare float @cosf(float)\n""declare double @cosh(double)\n""declare float @coshf(float)\n""declare x86_fp80 @coshl(x86_fp80)\n""declare x86_fp80 @cosl(x86_fp80)\n""declare i8* @ctermid(i8*)\n""declare double @exp(double)\n""declare double @exp2(double)\n""declare float @exp2f(float)\n""declare x86_fp80 @exp2l(x86_fp80)\n""declare float @expf(float)\n""declare x86_fp80 @expl(x86_fp80)\n""declare double @expm1(double)\n""declare float @expm1f(float)\n""declare x86_fp80 @expm1l(x86_fp80)\n""declare double @fabs(double)\n""declare float @fabsf(float)\n""declare x86_fp80 @fabsl(x86_fp80)\n""declare i32 @fclose(%struct*)\n""declare i32 @feof(%struct*)\n""declare i32 @ferror(%struct*)\n""declare i32 @fflush(%struct*)\n""declare i32 @ffs(i32)\n""declare i32 @ffsl(i64)\n""declare i32 @ffsll(i64)\n""declare i32 @fgetc(%struct*)\n""declare i32 @fgetc_unlocked(%struct*)\n""declare i32 @fgetpos(%struct*, i64*)\n""declare i8* @fgets(i8*, i32, %struct*)\n""declare i8* @fgets_unlocked(i8*, i32, %struct*)\n""declare i32 @fileno(%struct*)\n""declare void @flockfile(%struct*)\n""declare double @floor(double)\n""declare float @floorf(float)\n""declare x86_fp80 @floorl(x86_fp80)\n""declare i32 @fls(i32)\n""declare i32 @flsl(i64)\n""declare i32 @flsll(i64)\n""declare double @fmax(double, double)\n""declare float @fmaxf(float, float)\n""declare x86_fp80 @fmaxl(x86_fp80, x86_fp80)\n""declare double @fmin(double, double)\n""declare float @fminf(float, float)\n""declare x86_fp80 @fminl(x86_fp80, x86_fp80)\n""declare double @fmod(double, double)\n""declare float @fmodf(float, float)\n""declare x86_fp80 @fmodl(x86_fp80, x86_fp80)\n""declare i32 @fprintf(%struct*, i8*, ...)\n""declare i32 @fputc(i32, %struct*)\n""declare i32 @fputc_unlocked(i32, %struct*)\n""declare i64 @fread(i8*, i64, i64, %struct*)\n""declare i64 @fread_unlocked(i8*, i64, i64, %struct*)\n""declare void @free(i8*)\n""declare double @frexp(double, i32*)\n""declare float @frexpf(float, i32*)\n""declare x86_fp80 @frexpl(x86_fp80, i32*)\n""declare i32 @fscanf(%struct*, i8*, ...)\n""declare i32 @fseek(%struct*, i64, i32)\n""declare i32 @fseeko(%struct*, i64, i32)\n""declare i32 @fsetpos(%struct*, i64*)\n""declare i32 @fstatvfs(i32, %struct*)\n""declare i64 @ftell(%struct*)\n""declare i64 @ftello(%struct*)\n""declare i32 @ftrylockfile(%struct*)\n""declare void @funlockfile(%struct*)\n""declare i32 @getc(%struct*)\n""declare i32 @getc_unlocked(%struct*)\n""declare i32 @getchar()\n""declare i32 @getchar_unlocked()\n""declare i8* @getenv(i8*)\n""declare i32 @getitimer(i32, %struct*)\n""declare i32 @getlogin_r(i8*, i64)\n""declare %struct* @getpwnam(i8*)\n""declare i8* @gets(i8*)\n""declare i32 @gettimeofday(%struct*, i8*)\n""declare i32 @_Z7isasciii(i32)\n""declare i32 @_Z7isdigiti(i32)\n""declare i64 @labs(i64)\n""declare double @ldexp(double, i32)\n""declare float @ldexpf(float, i32)\n""declare x86_fp80 @ldexpl(x86_fp80, i32)\n""declare i64 @llabs(i64)\n""declare double @log(double)\n""declare double @log10(double)\n""declare float @log10f(float)\n""declare x86_fp80 @log10l(x86_fp80)\n""declare double @log1p(double)\n""declare float @log1pf(float)\n""declare x86_fp80 @log1pl(x86_fp80)\n""declare double @log2(double)\n""declare float @log2f(float)\n""declare x86_fp80 @log2l(x86_fp80)\n""declare double @logb(double)\n""declare float @logbf(float)\n""declare x86_fp80 @logbl(x86_fp80)\n""declare float @logf(float)\n""declare x86_fp80 @logl(x86_fp80)\n""declare i8* @malloc(i64)\n""declare i8* @memccpy(i8*, i8*, i32, i64)\n""declare i8* @memchr(i8*, i32, i64)\n""declare i32 @memcmp(i8*, i8*, i64)\n""declare i8* @memcpy(i8*, i8*, i64)\n""declare i8* @memmove(i8*, i8*, i64)\n""declare i8* @memset(i8*, i32, i64)\n""declare void @memset_pattern16(i8*, i8*, i64)\n""declare void @memset_pattern4(i8*, i8*, i64)\n""declare void @memset_pattern8(i8*, i8*, i64)\n""declare i32 @mkdir(i8*, i16)\n""declare double @modf(double, double*)\n""declare float @modff(float, float*)\n""declare x86_fp80 @modfl(x86_fp80, x86_fp80*)\n""declare double @nearbyint(double)\n""declare float @nearbyintf(float)\n""declare x86_fp80 @nearbyintl(x86_fp80)\n""declare i32 @pclose(%struct*)\n""declare void @perror(i8*)\n""declare i32 @posix_memalign(i8**, i64, i64)\n""declare double @pow(double, double)\n""declare float @powf(float, float)\n""declare x86_fp80 @powl(x86_fp80, x86_fp80)\n""declare i32 @printf(i8*, ...)\n""declare i32 @putc(i32, %struct*)\n""declare i32 @putc_unlocked(i32, %struct*)\n""declare i32 @putchar(i32)\n""declare i32 @putchar_unlocked(i32)\n""declare i32 @puts(i8*)\n""declare void @qsort(i8*, i64, i64, i32 (i8*, i8*)*)\n""declare i64 @readlink(i8*, i8*, i64)\n""declare i8* @realloc(i8*, i64)\n""declare i8* @reallocf(i8*, i64)\n""declare double @remainder(double, double)\n""declare float @remainderf(float, float)\n""declare x86_fp80 @remainderl(x86_fp80, x86_fp80)\n""declare i32 @remove(i8*)\n""declare i32 @rename(i8*, i8*)\n""declare void @rewind(%struct*)\n""declare double @rint(double)\n""declare float @rintf(float)\n""declare x86_fp80 @rintl(x86_fp80)\n""declare i32 @rmdir(i8*)\n""declare double @round(double)\n""declare float @roundf(float)\n""declare x86_fp80 @roundl(x86_fp80)\n""declare double @roundeven(double)\n""declare float @roundevenf(float)\n""declare x86_fp80 @roundevenl(x86_fp80)\n""declare i32 @scanf(i8*, ...)\n""declare void @setbuf(%struct*, i8*)\n""declare i32 @setitimer(i32, %struct*, %struct*)\n""declare i32 @setvbuf(%struct*, i8*, i32, i64)\n""declare double @sin(double)\n""declare float @sinf(float)\n""declare double @sinh(double)\n""declare float @sinhf(float)\n""declare x86_fp80 @sinhl(x86_fp80)\n""declare x86_fp80 @sinl(x86_fp80)\n""declare i32 @snprintf(i8*, i64, i8*, ...)\n""declare i32 @sprintf(i8*, i8*, ...)\n""declare double @sqrt(double)\n""declare float @sqrtf(float)\n""declare x86_fp80 @sqrtl(x86_fp80)\n""declare i32 @sscanf(i8*, i8*, ...)\n""declare i32 @statvfs(i8*, %struct*)\n""declare i8* @stpcpy(i8*, i8*)\n""declare i8* @stpncpy(i8*, i8*, i64)\n""declare i32 @strcasecmp(i8*, i8*)\n""declare i8* @strcat(i8*, i8*)\n""declare i8* @strchr(i8*, i32)\n""declare i32 @strcmp(i8*, i8*)\n""declare i32 @strcoll(i8*, i8*)\n""declare i8* @strcpy(i8*, i8*)\n""declare i64 @strcspn(i8*, i8*)\n""declare i8* @strdup(i8*)\n""declare i64 @strlen(i8*)\n""declare i32 @strncasecmp(i8*, i8*, i64)\n""declare i8* @strncat(i8*, i8*, i64)\n""declare i32 @strncmp(i8*, i8*, i64)\n""declare i8* @strncpy(i8*, i8*, i64)\n""declare i8* @strndup(i8*, i64)\n""declare i64 @strnlen(i8*, i64)\n""declare i8* @strpbrk(i8*, i8*)\n""declare i8* @strrchr(i8*, i32)\n""declare i64 @strspn(i8*, i8*)\n""declare i8* @strstr(i8*, i8*)\n""declare i8* @strtok(i8*, i8*)\n""declare i8* @strtok_r(i8*, i8*, i8**)\n""declare i64 @strtol(i8*, i8**, i32)\n""declare i64 @strlcat(i8*, i8**, i64)\n""declare i64 @strlcpy(i8*, i8**, i64)\n""declare x86_fp80 @strtold(i8*, i8**)\n""declare i64 @strtoll(i8*, i8**, i32)\n""declare i64 @strtoul(i8*, i8**, i32)\n""declare i64 @strtoull(i8*, i8**, i32)\n""declare i64 @strxfrm(i8*, i8*, i64)\n""declare double @tan(double)\n""declare float @tanf(float)\n""declare double @tanh(double)\n""declare float @tanhf(float)\n""declare x86_fp80 @tanhl(x86_fp80)\n""declare x86_fp80 @tanl(x86_fp80)\n""declare i64 @times(%struct*)\n""declare %struct* @tmpfile()\n""declare i32 @_Z7toasciii(i32)\n""declare double @trunc(double)\n""declare float @truncf(float)\n""declare x86_fp80 @truncl(x86_fp80)\n""declare i32 @uname(%struct*)\n""declare i32 @ungetc(i32, %struct*)\n""declare i32 @unlink(i8*)\n""declare i32 @utime(i8*, %struct*)\n""declare i32 @utimes(i8*, %struct*)\n""declare i8* @valloc(i64)\n""declare i32 @vfprintf(%struct*, i8*, %struct*)\n""declare i32 @vfscanf(%struct*, i8*, %struct*)\n""declare i32 @vprintf(i8*, %struct*)\n""declare i32 @vscanf(i8*, %struct*)\n""declare i32 @vsnprintf(i8*, i64, i8*, %struct*)\n""declare i32 @vsprintf(i8*, i8*, %struct*)\n""declare i32 @vsscanf(i8*, i8*, %struct*)\n""declare i64 @wcslen(i32*)\n""declare i32 @fork()\n""declare i32 @execl(i8*, i8*, ...)\n""declare i32 @execle(i8*, i8*, ...)\n""declare i32 @execlp(i8*, i8*, ...)\n""declare i32 @execv(i8*, i8**)\n""declare i32 @execvP(i8*, i8*, i8**)\n""declare i32 @execve(i8*, i8**, i8**)\n""declare i32 @execvp(i8*, i8**)\n""declare i32 @execvpe(i8*, i8**, i8**)\n"// These functions were also extracted from the OS X headers, but they are// available with a special name on darwin.// This test uses the default TLI name instead."declare i32 @chmod(i8*, i16)\n""declare i32 @closedir(%struct*)\n""declare %struct* @fdopen(i32, i8*)\n""declare %struct* @fopen(i8*, i8*)\n""declare i32 @fputs(i8*, %struct*)\n""declare i32 @fputs_unlocked(i8*, %struct*)\n""declare i32 @fstat(i32, %struct*)\n""declare i64 @fwrite(i8*, i64, i64, %struct*)\n""declare i64 @fwrite_unlocked(i8*, i64, i64, %struct*)\n""declare i32 @lchown(i8*, i32, i32)\n""declare i32 @lstat(i8*, %struct*)\n""declare i64 @mktime(%struct*)\n""declare i32 @open(i8*, i32, ...)\n""declare %struct* @opendir(i8*)\n""declare %struct* @popen(i8*, i8*)\n""declare i64 @pread(i32, i8*, i64, i64)\n""declare i64 @pwrite(i32, i8*, i64, i64)\n""declare i64 @read(i32, i8*, i64)\n""declare i8* @realpath(i8*, i8*)\n""declare i32 @stat(i8*, %struct*)\n""declare double @strtod(i8*, i8**)\n""declare float @strtof(i8*, i8**)\n""declare i32 @system(i8*)\n""declare i32 @unsetenv(i8*)\n""declare i64 @write(i32, i8*, i64)\n"// These functions are available on Linux but not Darwin; they only differ// from their non-64 counterparts in the struct type.// Use the same prototype as the non-64 variant."declare %struct* @fopen64(i8*, i8*)\n""declare i32 @fstat64(i32, %struct*)\n""declare i32 @fstatvfs64(i32, %struct*)\n""declare i32 @lstat64(i8*, %struct*)\n""declare i32 @open64(i8*, i32, ...)\n""declare i32 @stat64(i8*, %struct*)\n""declare i32 @statvfs64(i8*, %struct*)\n""declare %struct* @tmpfile64()\n"// These functions are also -64 variants, but do differ in the type of the// off_t (vs off64_t) parameter. The non-64 variants declared above used// a 64-bit off_t, so, in practice, they are also equivalent."declare i32 @fseeko64(%struct*, i64, i32)\n""declare i64 @ftello64(%struct*)\n""declare void @_ZdaPv(i8*)\n""declare void @_ZdaPvRKSt9nothrow_t(i8*, %struct*)\n""declare void @_ZdaPvSt11align_val_t(i8*, i64)\n""declare void @_ZdaPvSt11align_val_tRKSt9nothrow_t(i8*, i64, %struct*)\n""declare void @_ZdaPvj(i8*, i32)\n""declare void @_ZdaPvjSt11align_val_t(i8*, i32, i32)\n""declare void @_ZdaPvm(i8*, i64)\n""declare void @_ZdaPvmSt11align_val_t(i8*, i64, i64)\n""declare void @_ZdlPv(i8*)\n""declare void @_ZdlPvRKSt9nothrow_t(i8*, %struct*)\n""declare void @_ZdlPvSt11align_val_t(i8*, i64)\n""declare void @_ZdlPvSt11align_val_tRKSt9nothrow_t(i8*, i64, %struct*)\n""declare void @_ZdlPvj(i8*, i32)\n""declare void @_ZdlPvjSt11align_val_t(i8*, i32, i32)\n""declare void @_ZdlPvm(i8*, i64)\n""declare void @_ZdlPvmSt11align_val_t(i8*, i64, i64)\n""declare i8* @_Znaj(i32)\n""declare i8* @_ZnajRKSt9nothrow_t(i32, %struct*)\n""declare i8* @_ZnajSt11align_val_t(i32, i32)\n""declare i8* @_ZnajSt11align_val_tRKSt9nothrow_t(i32, i32, %struct*)\n""declare i8* @_Znam(i64)\n""declare i8* @_ZnamRKSt9nothrow_t(i64, %struct*)\n""declare i8* @_ZnamSt11align_val_t(i64, i64)\n""declare i8* @_ZnamSt11align_val_tRKSt9nothrow_t(i64, i64, %struct*)\n""declare i8* @_Znwj(i32)\n""declare i8* @_ZnwjRKSt9nothrow_t(i32, %struct*)\n""declare i8* @_ZnwjSt11align_val_t(i32, i32)\n""declare i8* @_ZnwjSt11align_val_tRKSt9nothrow_t(i32, i32, %struct*)\n""declare i8* @_Znwm(i64)\n""declare i8* @_ZnwmRKSt9nothrow_t(i64, %struct*)\n""declare i8* @_ZnwmSt11align_val_t(i64, i64)\n""declare i8* @_ZnwmSt11align_val_tRKSt9nothrow_t(i64, i64, %struct*)\n""declare void @\"??3@YAXPEAX@Z\"(i8*)\n""declare void @\"??3@YAXPEAXAEBUnothrow_t@std@@@Z\"(i8*, %struct*)\n""declare void @\"??3@YAXPEAX_K@Z\"(i8*, i64)\n""declare void @\"??_V@YAXPEAX@Z\"(i8*)\n""declare void @\"??_V@YAXPEAXAEBUnothrow_t@std@@@Z\"(i8*, %struct*)\n""declare void @\"??_V@YAXPEAX_K@Z\"(i8*, i64)\n""declare i8* @\"??2@YAPAXI@Z\"(i32)\n""declare i8* @\"??2@YAPAXIABUnothrow_t@std@@@Z\"(i32, %struct*)\n""declare i8* @\"??2@YAPEAX_K@Z\"(i64)\n""declare i8* @\"??2@YAPEAX_KAEBUnothrow_t@std@@@Z\"(i64, %struct*)\n""declare i8* @\"??_U@YAPAXI@Z\"(i32)\n""declare i8* @\"??_U@YAPAXIABUnothrow_t@std@@@Z\"(i32, %struct*)\n""declare i8* @\"??_U@YAPEAX_K@Z\"(i64)\n""declare i8* @\"??_U@YAPEAX_KAEBUnothrow_t@std@@@Z\"(i64, %struct*)\n""declare void @\"??3@YAXPAX@Z\"(i8*)\n""declare void @\"??3@YAXPAXABUnothrow_t@std@@@Z\"(i8*, %struct*)\n""declare void @\"??3@YAXPAXI@Z\"(i8*, i32)\n""declare void @\"??_V@YAXPAX@Z\"(i8*)\n""declare void @\"??_V@YAXPAXABUnothrow_t@std@@@Z\"(i8*, %struct*)\n""declare void @\"??_V@YAXPAXI@Z\"(i8*, i32)\n"// These other functions were derived from the .def C declaration."declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*)\n""declare void @__cxa_guard_abort(%struct*)\n""declare i32 @__cxa_guard_acquire(%struct*)\n""declare void @__cxa_guard_release(%struct*)\n""declare i32 @__nvvm_reflect(i8*)\n""declare i8* @__memcpy_chk(i8*, i8*, i64, i64)\n""declare i8* @__memmove_chk(i8*, i8*, i64, i64)\n""declare i8* @__memset_chk(i8*, i32, i64, i64)\n""declare i8* @__stpcpy_chk(i8*, i8*, i64)\n""declare i8* @__stpncpy_chk(i8*, i8*, i64, i64)\n""declare i8* @__strcpy_chk(i8*, i8*, i64)\n""declare i8* @__strncpy_chk(i8*, i8*, i64, i64)\n""declare i8* @__memccpy_chk(i8*, i8*, i32, i64)\n""declare i8* @__mempcpy_chk(i8*, i8*, i64, i64)\n""declare i32 @__snprintf_chk(i8*, i64, i32, i64, i8*, ...)\n""declare i32 @__sprintf_chk(i8*, i32, i64, i8*, ...)\n""declare i8* @__strcat_chk(i8*, i8*, i64)\n""declare i64 @__strlcat_chk(i8*, i8*, i64, i64)\n""declare i64 @__strlen_chk(i8*, i64)\n""declare i8* @__strncat_chk(i8*, i8*, i64, i64)\n""declare i64 @__strlcpy_chk(i8*, i8*, i64, i64)\n""declare i32 @__vsnprintf_chk(i8*, i64, i32, i64, i8*, %struct*)\n""declare i32 @__vsprintf_chk(i8*, i32, i64, i8*, %struct*)\n""declare i8* @memalign(i64, i64)\n""declare i8* @mempcpy(i8*, i8*, i64)\n""declare i8* @memrchr(i8*, i32, i64)\n""declare void @__atomic_load(i64, i8*, i8*, i32)\n""declare void @__atomic_store(i64, i8*, i8*, i32)\n"// These are similar to the FILE* fgetc/fputc."declare i32 @_IO_getc(%struct*)\n""declare i32 @_IO_putc(i32, %struct*)\n""declare i32 @__isoc99_scanf(i8*, ...)\n""declare i32 @__isoc99_sscanf(i8*, i8*, ...)\n""declare i8* @__strdup(i8*)\n""declare i8* @__strndup(i8*, i64)\n""declare i8* @__strtok_r(i8*, i8*, i8**)\n""declare double @__sqrt_finite(double)\n""declare float @__sqrtf_finite(float)\n""declare x86_fp80 @__sqrtl_finite(x86_fp80)\n""declare double @exp10(double)\n""declare float @exp10f(float)\n""declare x86_fp80 @exp10l(x86_fp80)\n"// These printf variants have the same prototype as the non-'i' versions."declare i32 @fiprintf(%struct*, i8*, ...)\n""declare i32 @iprintf(i8*, ...)\n""declare i32 @siprintf(i8*, i8*, ...)\n"// __small_printf variants have the same prototype as the non-'i'// versions."declare i32 @__small_fprintf(%struct*, i8*, ...)\n""declare i32 @__small_printf(i8*, ...)\n""declare i32 @__small_sprintf(i8*, i8*, ...)\n""declare i32 @htonl(i32)\n""declare i16 @htons(i16)\n""declare i32 @ntohl(i32)\n""declare i16 @ntohs(i16)\n""declare i32 @isascii(i32)\n""declare i32 @isdigit(i32)\n""declare i32 @toascii(i32)\n"// These functions were extracted from math-finite.h which provides// functions similar to those in math.h, but optimized for handling// finite values only."declare double @__acos_finite(double)\n""declare float @__acosf_finite(float)\n""declare x86_fp80 @__acosl_finite(x86_fp80)\n""declare double @__acosh_finite(double)\n""declare float @__acoshf_finite(float)\n""declare x86_fp80 @__acoshl_finite(x86_fp80)\n""declare double @__asin_finite(double)\n""declare float @__asinf_finite(float)\n""declare x86_fp80 @__asinl_finite(x86_fp80)\n""declare double @__atan2_finite(double, double)\n""declare float @__atan2f_finite(float, float)\n""declare x86_fp80 @__atan2l_finite(x86_fp80, x86_fp80)\n""declare double @__atanh_finite(double)\n""declare float @__atanhf_finite(float)\n""declare x86_fp80 @__atanhl_finite(x86_fp80)\n""declare double @__cosh_finite(double)\n""declare float @__coshf_finite(float)\n""declare x86_fp80 @__coshl_finite(x86_fp80)\n""declare double @__exp10_finite(double)\n""declare float @__exp10f_finite(float)\n""declare x86_fp80 @__exp10l_finite(x86_fp80)\n""declare double @__exp2_finite(double)\n""declare float @__exp2f_finite(float)\n""declare x86_fp80 @__exp2l_finite(x86_fp80)\n""declare double @__exp_finite(double)\n""declare float @__expf_finite(float)\n""declare x86_fp80 @__expl_finite(x86_fp80)\n""declare double @__log10_finite(double)\n""declare float @__log10f_finite(float)\n""declare x86_fp80 @__log10l_finite(x86_fp80)\n""declare double @__log2_finite(double)\n""declare float @__log2f_finite(float)\n""declare x86_fp80 @__log2l_finite(x86_fp80)\n""declare double @__log_finite(double)\n""declare float @__logf_finite(float)\n""declare x86_fp80 @__logl_finite(x86_fp80)\n""declare double @__pow_finite(double, double)\n""declare float @__powf_finite(float, float)\n""declare x86_fp80 @__powl_finite(x86_fp80, x86_fp80)\n""declare double @__sinh_finite(double)\n""declare float @__sinhf_finite(float)\n""declare x86_fp80 @__sinhl_finite(x86_fp80)\n"// These functions are aix vec allocation/free routines"declare i8* @vec_calloc(i64, i64)\n""declare i8* @vec_malloc(i64)\n""declare i8* @vec_realloc(i8*, i64)\n""declare void @vec_free(i8*)\n"// These functions are OpenMP Offloading allocation / free routines"declare i8* @__kmpc_alloc_shared(i64)\n""declare void @__kmpc_free_shared(i8*, i64)\n");for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) {LibFunc LF = (LibFunc)FI;// Make sure everything is available; we're not testing target defaults.TLII.setAvailable(LF);Function *F = M->getFunction(TLI.getName(LF));EXPECT_TRUE(isLibFunc(F, LF));}}
//===- TFUtilsTest.cpp - test for TFUtils ---------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/Utils/TFUtils.h"#include "google/protobuf/struct.pb.h"#include "tensorflow/core/example/example.pb.h"#include "tensorflow/core/example/feature.pb.h"#include "llvm/Analysis/ModelUnderTrainingRunner.h"#include "llvm/Analysis/TensorSpec.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/Path.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Testing/Support/SupportHelpers.h"#include "gtest/gtest.h"using namespace llvm;extern const char *TestMainArgv0;// NOTE! This test model is currently also used by test/Transforms/Inline/ML tests//- relevant if updating this model.static std::string getModelPath() {SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);llvm::sys::path::append(InputsDir, "ir2native_x86_64_model");return std::string(InputsDir);}// Test observable behavior when no model is provided.TEST(TFUtilsTest, NoModel) {TFModelEvaluator Evaluator("", {}, {});EXPECT_FALSE(Evaluator.isValid());}// Test we can correctly load a savedmodel and evaluate it.TEST(TFUtilsTest, LoadAndExecuteTest) {// We use the ir2native model for test. We know it has one feature of// dimension (1, 214)const static int64_t KnownSize = 214;std::vector<TensorSpec> InputSpecs{TensorSpec::createSpec<int32_t>("serving_default_input_1", {1, KnownSize})};std::vector<TensorSpec> OutputSpecs{TensorSpec::createSpec<float>("StatefulPartitionedCall", {1})};TFModelEvaluator Evaluator(getModelPath(), InputSpecs, OutputSpecs);EXPECT_TRUE(Evaluator.isValid());int32_t *V = Evaluator.getInput<int32_t>(0);// Fill it up with 1's, we know the output.for (auto I = 0; I < KnownSize; ++I) {V[I] = 1;}{auto ER = Evaluator.evaluate();EXPECT_TRUE(ER.hasValue());float Ret = *ER->getTensorValue<float>(0);EXPECT_EQ(static_cast<int64_t>(Ret), 80);EXPECT_EQ(ER->getUntypedTensorValue(0),reinterpret_cast<const void *>(ER->getTensorValue<float>(0)));}// The input vector should be unchangedfor (auto I = 0; I < KnownSize; ++I) {EXPECT_EQ(V[I], 1);}// Zero-out the unused position '0' of the instruction histogram, which is// after the first 9 calculated values. Should the the same result.V[9] = 0;{auto ER = Evaluator.evaluate();EXPECT_TRUE(ER.hasValue());float Ret = *ER->getTensorValue<float>(0);EXPECT_EQ(static_cast<int64_t>(Ret), 80);}}// Test incorrect input setupTEST(TFUtilsTest, EvalError) {// We use the ir2native model for test. We know it has one feature of// dimension (1, 214)const static int64_t KnownSize = 213;std::vector<TensorSpec> InputSpecs{TensorSpec::createSpec<int32_t>("serving_default_input_1", {1, KnownSize})};std::vector<TensorSpec> OutputSpecs{TensorSpec::createSpec<float>("StatefulPartitionedCall", {1})};TFModelEvaluator Evaluator(getModelPath(), InputSpecs, OutputSpecs);EXPECT_TRUE(Evaluator.isValid());int32_t *V = Evaluator.getInput<int32_t>(0);// Fill it up with 1's, we know the output.for (auto I = 0; I < KnownSize; ++I) {V[I] = 1;}auto ER = Evaluator.evaluate();EXPECT_FALSE(ER.hasValue());EXPECT_FALSE(Evaluator.isValid());}TEST(TFUtilsTest, UnsupportedFeature) {const static int64_t KnownSize = 214;std::vector<TensorSpec> InputSpecs{TensorSpec::createSpec<int32_t>("serving_default_input_1",{1, KnownSize}),TensorSpec::createSpec<float>("this_feature_does_not_exist", {2, 5})};LLVMContext Ctx;auto Evaluator = ModelUnderTrainingRunner::createAndEnsureValid(Ctx, getModelPath(), "StatefulPartitionedCall", InputSpecs,{LoggedFeatureSpec{TensorSpec::createSpec<float>("StatefulPartitionedCall", {1}),None}});int32_t *V = Evaluator->getTensor<int32_t>(0);// Fill it up with 1s, we know the output.for (auto I = 0; I < KnownSize; ++I)V[I] = 1;float *F = Evaluator->getTensor<float>(1);for (auto I = 0; I < 2 * 5; ++I)F[I] = 3.14 + I;float Ret = Evaluator->evaluate<float>();EXPECT_EQ(static_cast<int64_t>(Ret), 80);// The input vector should be unchangedfor (auto I = 0; I < KnownSize; ++I)EXPECT_EQ(V[I], 1);for (auto I = 0; I < 2 * 5; ++I)EXPECT_FLOAT_EQ(F[I], 3.14 + I);}#define PROTO_CHECKER(FNAME, TYPE, INDEX, EXP) \do { \const auto &V = Expected.feature_lists() \.feature_list() \.at(FNAME) \.feature(INDEX) \.TYPE() \.value(); \for (auto I = 0; I < V.size(); ++I) \EXPECT_EQ(V.at(I), EXP[I]); \} while (false)TEST(TFUtilsTest, Logger) {std::vector<LoggedFeatureSpec> Features;Features.push_back({TensorSpec::createSpec<float>("the_float", {2, 3}), None});Features.push_back({TensorSpec::createSpec<int64_t>("the_int", {2}),std::string("alternate_name")});auto Rewards = TensorSpec::createSpec<float>("reward", {1});Logger L(Features, Rewards, true);const float F00[]{0.0, 0.1, 0.2, 0.3, 0.4, 0.5};const int64_t F01[]{2, 3};L.logFloatValue(0, F00);L.logInt64Value(1, F01);L.logFloatReward(3.4);const float F10[]{0.0, 1.0, 2.0, 3.0, 4.0, 5.0};const int64_t F11[]{-2, -3};L.logFloatValue(0, F10);L.logInt64Value(1, F11);L.logFloatReward(-3.0);std::string Result;raw_string_ostream OS(Result);L.flush(OS);tensorflow::SequenceExample Expected;ASSERT_TRUE(Expected.ParseFromString(Result));PROTO_CHECKER("the_float", float_list, 0, F00);PROTO_CHECKER("the_float", float_list, 1, F10);PROTO_CHECKER("alternate_name", int64_list, 0, F01);PROTO_CHECKER("alternate_name", int64_list, 1, F11);float R0[]{3.4};float R1[]{-3.0};PROTO_CHECKER("reward", float_list, 0, R0);PROTO_CHECKER("reward", float_list, 1, R1);}TEST(TFUtilsTest, LoggerInt32FeaturesAndReward) {std::vector<LoggedFeatureSpec> Features;Features.push_back({TensorSpec::createSpec<float>("the_float", {2, 3}), None});Features.push_back({TensorSpec::createSpec<int32_t>("the_int", {2}),std::string("alternate_name")});auto Rewards = TensorSpec::createSpec<int32_t>("reward", {1});Logger L(Features, Rewards, true);const float F00[]{0.0, 0.1, 0.2, 0.3, 0.4, 0.5};const int32_t F01[]{2, 3};L.logFloatValue(0, F00);L.logInt32Value(1, F01);L.logInt32Reward(3);const float F10[]{0.0, 1.0, 2.0, 3.0, 4.0, 5.0};const int32_t F11[]{-2, -3};L.logFloatValue(0, F10);L.logInt32Value(1, F11);L.logInt32Reward(-3);std::string Result;raw_string_ostream OS(Result);L.flush(OS);tensorflow::SequenceExample Expected;ASSERT_TRUE(Expected.ParseFromString(Result));PROTO_CHECKER("the_float", float_list, 0, F00);PROTO_CHECKER("the_float", float_list, 1, F10);PROTO_CHECKER("alternate_name", int64_list, 0, F01);PROTO_CHECKER("alternate_name", int64_list, 1, F11);int32_t R0[]{3};int32_t R1[]{-3};PROTO_CHECKER("reward", int64_list, 0, R0);PROTO_CHECKER("reward", int64_list, 1, R1);}TEST(TFUtilsTest, LoggerNoReward) {std::vector<LoggedFeatureSpec> Features;Features.push_back({TensorSpec::createSpec<float>("the_float", {2, 3}), None});Features.push_back({TensorSpec::createSpec<int64_t>("the_int", {2}),std::string("alternate_name")});auto Rewards = TensorSpec::createSpec<float>("reward", {1});Logger L(Features, Rewards, false);const float F00[]{0.0, 0.1, 0.2, 0.3, 0.4, 0.5};const int64_t F01[]{2, 3};L.logFloatValue(0, F00);L.logInt64Value(1, F01);const float F10[]{0.0, 1.0, 2.0, 3.0, 4.0, 5.0};const int64_t F11[]{-2, -3};L.logFloatValue(0, F10);L.logInt64Value(1, F11);std::string Result;raw_string_ostream OS(Result);L.flush(OS);tensorflow::SequenceExample Expected;ASSERT_TRUE(Expected.ParseFromString(Result));PROTO_CHECKER("the_float", float_list, 0, F00);PROTO_CHECKER("the_float", float_list, 1, F10);PROTO_CHECKER("alternate_name", int64_list, 0, F01);PROTO_CHECKER("alternate_name", int64_list, 1, F11);}TEST(TFUtilsTest, LoggerFinalReward) {std::vector<LoggedFeatureSpec> Features;Features.push_back({TensorSpec::createSpec<float>("the_float", {1}), None});Features.push_back({TensorSpec::createSpec<int64_t>("the_int", {1}), None});auto Rewards = TensorSpec::createSpec<float>("reward", {1});Logger L(Features, Rewards, true);for (int64_t I = 0; I < 3; ++I) {float F = static_cast<float>(I);L.logFloatValue(0, &F);L.logInt64Value(1, &I);}L.logFloatFinalReward(3.14);std::string Result;raw_string_ostream OS(Result);L.flush(OS);const float Zero[]{0.0};const float R[]{3.14};tensorflow::SequenceExample Expected;ASSERT_TRUE(Expected.ParseFromString(Result));PROTO_CHECKER("reward", float_list, 0, Zero);PROTO_CHECKER("reward", float_list, 1, Zero);PROTO_CHECKER("reward", float_list, 2, R);}TEST(TFUtilsTest, LoggerGroup) {std::vector<LoggedFeatureSpec> Features;Features.push_back({TensorSpec::createSpec<float>("the_float", {1}), None});Features.push_back({TensorSpec::createSpec<int64_t>("the_int", {1}), None});auto Rewards = TensorSpec::createSpec<float>("reward", {1});StringMap<std::unique_ptr<Logger>> Loggers;std::vector<std::string> Names{"a", "b"};size_t Bump = 0;for (auto Name : Names) {auto L = std::make_unique<Logger>(Features, Rewards, true);for (int64_t I = 0; I < 3; ++I) {float F = static_cast<float>(I) + Bump;L->logFloatValue(0, &F);L->logInt64Value(1, &I);}L->logFloatFinalReward(3.14 + Bump);Loggers.insert(std::make_pair(Name, std::move(L)));}std::string Result;raw_string_ostream OS(Result);Logger::flushLogs(OS, Loggers);google::protobuf::Struct Expected;ASSERT_TRUE(Expected.ParseFromString(Result));EXPECT_EQ(Expected.fields_size(), 2);EXPECT_TRUE(Expected.fields().contains("a"));EXPECT_TRUE(Expected.fields().contains("b"));}
//===--- TBAATest.cpp - Mixed TBAA unit tests -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/AliasAnalysisEvaluator.h"#include "llvm/Analysis/Passes.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/MDBuilder.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/CommandLine.h"#include "gtest/gtest.h"namespace llvm {namespace {class TBAATest : public testing::Test {protected:TBAATest() : M("TBAATest", C), MD(C) {}LLVMContext C;Module M;MDBuilder MD;};static StoreInst *getFunctionWithSingleStore(Module *M, StringRef Name) {auto &C = M->getContext();FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), {});auto *F = Function::Create(FTy, Function::ExternalLinkage, Name, M);auto *BB = BasicBlock::Create(C, "entry", F);auto *IntType = Type::getInt32Ty(C);auto *PtrType = Type::getInt32PtrTy(C);auto *SI = new StoreInst(ConstantInt::get(IntType, 42),ConstantPointerNull::get(PtrType), BB);ReturnInst::Create(C, nullptr, BB);return SI;}TEST_F(TBAATest, checkVerifierBehaviorForOldTBAA) {auto *SI = getFunctionWithSingleStore(&M, "f1");auto *F = SI->getFunction();// C++ unit test case to avoid going through the auto upgrade logic.auto *RootMD = MD.createTBAARoot("Simple C/C++ TBAA");auto *MD1 = MD.createTBAANode("omnipotent char", RootMD);auto *MD2 = MD.createTBAANode("int", MD1);SI->setMetadata(LLVMContext::MD_tbaa, MD2);SmallVector<char, 0> ErrorMsg;raw_svector_ostream Outs(ErrorMsg);StringRef ExpectedFailureMsg("Old-style TBAA is no longer allowed, use struct-path TBAA instead");EXPECT_TRUE(verifyFunction(*F, &Outs));EXPECT_TRUE(StringRef(ErrorMsg.begin(), ErrorMsg.size()).startswith(ExpectedFailureMsg));}TEST_F(TBAATest, checkTBAAMerging) {auto *SI = getFunctionWithSingleStore(&M, "f2");auto *F = SI->getFunction();auto *RootMD = MD.createTBAARoot("tbaa-root");auto *MD1 = MD.createTBAANode("scalar-a", RootMD);auto *StructTag1 = MD.createTBAAStructTagNode(MD1, MD1, 0);auto *MD2 = MD.createTBAANode("scalar-b", RootMD);auto *StructTag2 = MD.createTBAAStructTagNode(MD2, MD2, 0);auto *GenericMD = MDNode::getMostGenericTBAA(StructTag1, StructTag2);EXPECT_EQ(GenericMD, nullptr);// Despite GenericMD being nullptr, we expect the setMetadata call to be well// defined and produce a well-formed function.SI->setMetadata(LLVMContext::MD_tbaa, GenericMD);EXPECT_TRUE(!verifyFunction(*F));}} // end anonymous namspace} // end llvm namespace
//===- SparsePropagation.cpp - Unit tests for the generic solver ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/SparsePropagation.h"#include "llvm/ADT/PointerIntPair.h"#include "llvm/IR/IRBuilder.h"#include "gtest/gtest.h"using namespace llvm;namespace {/// To enable interprocedural analysis, we assign LLVM values to the following/// groups. The register group represents SSA registers, the return group/// represents the return values of functions, and the memory group represents/// in-memory values. An LLVM Value can technically be in more than one group./// It's necessary to distinguish these groups so we can, for example, track a/// global variable separately from the value stored at its location.enum class IPOGrouping { Register, Return, Memory };/// Our LatticeKeys are PointerIntPairs composed of LLVM values and groupings./// The PointerIntPair header provides a DenseMapInfo specialization, so using/// these as LatticeKeys is fine.using TestLatticeKey = PointerIntPair<Value *, 2, IPOGrouping>;} // namespacenamespace llvm {/// A specialization of LatticeKeyInfo for TestLatticeKeys. The generic solver/// must translate between LatticeKeys and LLVM Values when adding Values to/// its work list and inspecting the state of control-flow related values.template <> struct LatticeKeyInfo<TestLatticeKey> {static inline Value *getValueFromLatticeKey(TestLatticeKey Key) {return Key.getPointer();}static inline TestLatticeKey getLatticeKeyFromValue(Value *V) {return TestLatticeKey(V, IPOGrouping::Register);}};} // namespace llvmnamespace {/// This class defines a simple test lattice value that could be used for/// solving problems similar to constant propagation. The value is maintained/// as a PointerIntPair.class TestLatticeVal {public:/// The states of the lattices value. Only the ConstantVal state is/// interesting; the rest are special states used by the generic solver. The/// UntrackedVal state differs from the other three in that the generic/// solver uses it to avoid doing unnecessary work. In particular, when a/// value moves to the UntrackedVal state, it's users are not notified.enum TestLatticeStateTy {UndefinedVal,ConstantVal,OverdefinedVal,UntrackedVal};TestLatticeVal() : LatticeVal(nullptr, UndefinedVal) {}TestLatticeVal(Constant *C, TestLatticeStateTy State): LatticeVal(C, State) {}/// Return true if this lattice value is in the Constant state. This is used/// for checking the solver results.bool isConstant() const { return LatticeVal.getInt() == ConstantVal; }/// Return true if this lattice value is in the Overdefined state. This is/// used for checking the solver results.bool isOverdefined() const { return LatticeVal.getInt() == OverdefinedVal; }bool operator==(const TestLatticeVal &RHS) const {return LatticeVal == RHS.LatticeVal;}bool operator!=(const TestLatticeVal &RHS) const {return LatticeVal != RHS.LatticeVal;}private:/// A simple lattice value type for problems similar to constant propagation./// It holds the constant value and the lattice state.PointerIntPair<const Constant *, 2, TestLatticeStateTy> LatticeVal;};/// This class defines a simple test lattice function that could be used for/// solving problems similar to constant propagation. The test lattice differs/// from a "real" lattice in a few ways. First, it initializes all return/// values, values stored in global variables, and arguments in the undefined/// state. This means that there are no limitations on what we can track/// interprocedurally. For simplicity, all global values in the tests will be/// given internal linkage, since this is not something this lattice function/// tracks. Second, it only handles the few instructions necessary for the/// tests.class TestLatticeFunc: public AbstractLatticeFunction<TestLatticeKey, TestLatticeVal> {public:/// Construct a new test lattice function with special values for the/// Undefined, Overdefined, and Untracked states.TestLatticeFunc(): AbstractLatticeFunction(TestLatticeVal(nullptr, TestLatticeVal::UndefinedVal),TestLatticeVal(nullptr, TestLatticeVal::OverdefinedVal),TestLatticeVal(nullptr, TestLatticeVal::UntrackedVal)) {}/// Compute and return a TestLatticeVal for the given TestLatticeKey. For the/// test analysis, a LatticeKey will begin in the undefined state, unless it/// represents an LLVM Constant in the register grouping.TestLatticeVal ComputeLatticeVal(TestLatticeKey Key) override {if (Key.getInt() == IPOGrouping::Register)if (auto *C = dyn_cast<Constant>(Key.getPointer()))return TestLatticeVal(C, TestLatticeVal::ConstantVal);return getUndefVal();}/// Merge the two given lattice values. This merge should be equivalent to/// what is done for constant propagation. That is, the resulting lattice/// value is constant only if the two given lattice values are constant and/// hold the same value.TestLatticeVal MergeValues(TestLatticeVal X, TestLatticeVal Y) override {if (X == getUntrackedVal() || Y == getUntrackedVal())return getUntrackedVal();if (X == getOverdefinedVal() || Y == getOverdefinedVal())return getOverdefinedVal();if (X == getUndefVal() && Y == getUndefVal())return getUndefVal();if (X == getUndefVal())return Y;if (Y == getUndefVal())return X;if (X == Y)return X;return getOverdefinedVal();}/// Compute the lattice values that change as a result of executing the given/// instruction. We only handle the few instructions needed for the tests.void ComputeInstructionState(Instruction &I, DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues,SparseSolver<TestLatticeKey, TestLatticeVal> &SS) override {switch (I.getOpcode()) {case Instruction::Call:return visitCallBase(cast<CallBase>(I), ChangedValues, SS);case Instruction::Ret:return visitReturn(*cast<ReturnInst>(&I), ChangedValues, SS);case Instruction::Store:return visitStore(*cast<StoreInst>(&I), ChangedValues, SS);default:return visitInst(I, ChangedValues, SS);}}private:/// Handle call sites. The state of a called function's argument is the merge/// of the current formal argument state with the call site's corresponding/// actual argument state. The call site state is the merge of the call site/// state with the returned value state of the called function.void visitCallBase(CallBase &I,DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues,SparseSolver<TestLatticeKey, TestLatticeVal> &SS) {Function *F = I.getCalledFunction();auto RegI = TestLatticeKey(&I, IPOGrouping::Register);if (!F) {ChangedValues[RegI] = getOverdefinedVal();return;}SS.MarkBlockExecutable(&F->front());for (Argument &A : F->args()) {auto RegFormal = TestLatticeKey(&A, IPOGrouping::Register);auto RegActual =TestLatticeKey(I.getArgOperand(A.getArgNo()), IPOGrouping::Register);ChangedValues[RegFormal] =MergeValues(SS.getValueState(RegFormal), SS.getValueState(RegActual));}auto RetF = TestLatticeKey(F, IPOGrouping::Return);ChangedValues[RegI] =MergeValues(SS.getValueState(RegI), SS.getValueState(RetF));}/// Handle return instructions. The function's return state is the merge of/// the returned value state and the function's current return state.void visitReturn(ReturnInst &I,DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues,SparseSolver<TestLatticeKey, TestLatticeVal> &SS) {Function *F = I.getParent()->getParent();if (F->getReturnType()->isVoidTy())return;auto RegR = TestLatticeKey(I.getReturnValue(), IPOGrouping::Register);auto RetF = TestLatticeKey(F, IPOGrouping::Return);ChangedValues[RetF] =MergeValues(SS.getValueState(RegR), SS.getValueState(RetF));}/// Handle store instructions. If the pointer operand of the store is a/// global variable, we attempt to track the value. The global variable state/// is the merge of the stored value state with the current global variable/// state.void visitStore(StoreInst &I,DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues,SparseSolver<TestLatticeKey, TestLatticeVal> &SS) {auto *GV = dyn_cast<GlobalVariable>(I.getPointerOperand());if (!GV)return;auto RegVal = TestLatticeKey(I.getValueOperand(), IPOGrouping::Register);auto MemPtr = TestLatticeKey(GV, IPOGrouping::Memory);ChangedValues[MemPtr] =MergeValues(SS.getValueState(RegVal), SS.getValueState(MemPtr));}/// Handle all other instructions. All other instructions are marked/// overdefined.void visitInst(Instruction &I,DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues,SparseSolver<TestLatticeKey, TestLatticeVal> &SS) {auto RegI = TestLatticeKey(&I, IPOGrouping::Register);ChangedValues[RegI] = getOverdefinedVal();}};/// This class defines the common data used for all of the tests. The tests/// should add code to the module and then run the solver.class SparsePropagationTest : public testing::Test {protected:LLVMContext Context;Module M;IRBuilder<> Builder;TestLatticeFunc Lattice;SparseSolver<TestLatticeKey, TestLatticeVal> Solver;public:SparsePropagationTest(): M("", Context), Builder(Context), Solver(&Lattice) {}};} // namespace/// Test that we mark discovered functions executable.////// define internal void @f() {/// call void @g()/// ret void/// }////// define internal void @g() {/// call void @f()/// ret void/// }////// For this test, we initially mark "f" executable, and the solver discovers/// "g" because of the call in "f". The mutually recursive call in "g" also/// tests that we don't add a block to the basic block work list if it is/// already executable. Doing so would put the solver into an infinite loop.TEST_F(SparsePropagationTest, MarkBlockExecutable) {Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "f", &M);Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "g", &M);BasicBlock *FEntry = BasicBlock::Create(Context, "", F);BasicBlock *GEntry = BasicBlock::Create(Context, "", G);Builder.SetInsertPoint(FEntry);Builder.CreateCall(G);Builder.CreateRetVoid();Builder.SetInsertPoint(GEntry);Builder.CreateCall(F);Builder.CreateRetVoid();Solver.MarkBlockExecutable(FEntry);Solver.Solve();EXPECT_TRUE(Solver.isBlockExecutable(GEntry));}/// Test that we propagate information through global variables.////// @gv = internal global i64////// define internal void @f() {/// store i64 1, i64* @gv/// ret void/// }////// define internal void @g() {/// store i64 1, i64* @gv/// ret void/// }////// For this test, we initially mark both "f" and "g" executable, and the/// solver computes the lattice state of the global variable as constant.TEST_F(SparsePropagationTest, GlobalVariableConstant) {Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "f", &M);Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "g", &M);GlobalVariable *GV =new GlobalVariable(M, Builder.getInt64Ty(), false,GlobalValue::InternalLinkage, nullptr, "gv");BasicBlock *FEntry = BasicBlock::Create(Context, "", F);BasicBlock *GEntry = BasicBlock::Create(Context, "", G);Builder.SetInsertPoint(FEntry);Builder.CreateStore(Builder.getInt64(1), GV);Builder.CreateRetVoid();Builder.SetInsertPoint(GEntry);Builder.CreateStore(Builder.getInt64(1), GV);Builder.CreateRetVoid();Solver.MarkBlockExecutable(FEntry);Solver.MarkBlockExecutable(GEntry);Solver.Solve();auto MemGV = TestLatticeKey(GV, IPOGrouping::Memory);EXPECT_TRUE(Solver.getExistingValueState(MemGV).isConstant());}/// Test that we propagate information through global variables.////// @gv = internal global i64////// define internal void @f() {/// store i64 0, i64* @gv/// ret void/// }////// define internal void @g() {/// store i64 1, i64* @gv/// ret void/// }////// For this test, we initially mark both "f" and "g" executable, and the/// solver computes the lattice state of the global variable as overdefined.TEST_F(SparsePropagationTest, GlobalVariableOverDefined) {Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "f", &M);Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "g", &M);GlobalVariable *GV =new GlobalVariable(M, Builder.getInt64Ty(), false,GlobalValue::InternalLinkage, nullptr, "gv");BasicBlock *FEntry = BasicBlock::Create(Context, "", F);BasicBlock *GEntry = BasicBlock::Create(Context, "", G);Builder.SetInsertPoint(FEntry);Builder.CreateStore(Builder.getInt64(0), GV);Builder.CreateRetVoid();Builder.SetInsertPoint(GEntry);Builder.CreateStore(Builder.getInt64(1), GV);Builder.CreateRetVoid();Solver.MarkBlockExecutable(FEntry);Solver.MarkBlockExecutable(GEntry);Solver.Solve();auto MemGV = TestLatticeKey(GV, IPOGrouping::Memory);EXPECT_TRUE(Solver.getExistingValueState(MemGV).isOverdefined());}/// Test that we propagate information through function returns.////// define internal i64 @f(i1* %cond) {/// if:/// %0 = load i1, i1* %cond/// br i1 %0, label %then, label %else////// then:/// ret i64 1////// else:/// ret i64 1/// }////// For this test, we initially mark "f" executable, and the solver computes/// the return value of the function as constant.TEST_F(SparsePropagationTest, FunctionDefined) {Function *F =Function::Create(FunctionType::get(Builder.getInt64Ty(),{Type::getInt1PtrTy(Context)}, false),GlobalValue::InternalLinkage, "f", &M);BasicBlock *If = BasicBlock::Create(Context, "if", F);BasicBlock *Then = BasicBlock::Create(Context, "then", F);BasicBlock *Else = BasicBlock::Create(Context, "else", F);F->arg_begin()->setName("cond");Builder.SetInsertPoint(If);LoadInst *Cond = Builder.CreateLoad(Type::getInt1Ty(Context), F->arg_begin());Builder.CreateCondBr(Cond, Then, Else);Builder.SetInsertPoint(Then);Builder.CreateRet(Builder.getInt64(1));Builder.SetInsertPoint(Else);Builder.CreateRet(Builder.getInt64(1));Solver.MarkBlockExecutable(If);Solver.Solve();auto RetF = TestLatticeKey(F, IPOGrouping::Return);EXPECT_TRUE(Solver.getExistingValueState(RetF).isConstant());}/// Test that we propagate information through function returns.////// define internal i64 @f(i1* %cond) {/// if:/// %0 = load i1, i1* %cond/// br i1 %0, label %then, label %else////// then:/// ret i64 0////// else:/// ret i64 1/// }////// For this test, we initially mark "f" executable, and the solver computes/// the return value of the function as overdefined.TEST_F(SparsePropagationTest, FunctionOverDefined) {Function *F =Function::Create(FunctionType::get(Builder.getInt64Ty(),{Type::getInt1PtrTy(Context)}, false),GlobalValue::InternalLinkage, "f", &M);BasicBlock *If = BasicBlock::Create(Context, "if", F);BasicBlock *Then = BasicBlock::Create(Context, "then", F);BasicBlock *Else = BasicBlock::Create(Context, "else", F);F->arg_begin()->setName("cond");Builder.SetInsertPoint(If);LoadInst *Cond = Builder.CreateLoad(Type::getInt1Ty(Context), F->arg_begin());Builder.CreateCondBr(Cond, Then, Else);Builder.SetInsertPoint(Then);Builder.CreateRet(Builder.getInt64(0));Builder.SetInsertPoint(Else);Builder.CreateRet(Builder.getInt64(1));Solver.MarkBlockExecutable(If);Solver.Solve();auto RetF = TestLatticeKey(F, IPOGrouping::Return);EXPECT_TRUE(Solver.getExistingValueState(RetF).isOverdefined());}/// Test that we propagate information through arguments.////// define internal void @f() {/// call void @g(i64 0, i64 1)/// call void @g(i64 1, i64 1)/// ret void/// }////// define internal void @g(i64 %a, i64 %b) {/// ret void/// }////// For this test, we initially mark "f" executable, and the solver discovers/// "g" because of the calls in "f". The solver computes the state of argument/// "a" as overdefined and the state of "b" as constant.////// In addition, this test demonstrates that ComputeInstructionState can alter/// the state of multiple lattice values, in addition to the one associated/// with the instruction definition. Each call instruction in this test updates/// the state of arguments "a" and "b".TEST_F(SparsePropagationTest, ComputeInstructionState) {Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "f", &M);Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(),{Builder.getInt64Ty(), Builder.getInt64Ty()}, false),GlobalValue::InternalLinkage, "g", &M);Argument *A = G->arg_begin();Argument *B = std::next(G->arg_begin());A->setName("a");B->setName("b");BasicBlock *FEntry = BasicBlock::Create(Context, "", F);BasicBlock *GEntry = BasicBlock::Create(Context, "", G);Builder.SetInsertPoint(FEntry);Builder.CreateCall(G, {Builder.getInt64(0), Builder.getInt64(1)});Builder.CreateCall(G, {Builder.getInt64(1), Builder.getInt64(1)});Builder.CreateRetVoid();Builder.SetInsertPoint(GEntry);Builder.CreateRetVoid();Solver.MarkBlockExecutable(FEntry);Solver.Solve();auto RegA = TestLatticeKey(A, IPOGrouping::Register);auto RegB = TestLatticeKey(B, IPOGrouping::Register);EXPECT_TRUE(Solver.getExistingValueState(RegA).isOverdefined());EXPECT_TRUE(Solver.getExistingValueState(RegB).isConstant());}/// Test that we can handle exceptional terminator instructions.////// declare internal void @p()////// declare internal void @g()////// define internal void @f() personality i8* bitcast (void ()* @p to i8*) {/// entry:/// invoke void @g()/// to label %exit unwind label %catch.pad////// catch.pad:/// %0 = catchswitch within none [label %catch.body] unwind to caller////// catch.body:/// %1 = catchpad within %0 []/// catchret from %1 to label %exit////// exit:/// ret void/// }////// For this test, we initially mark the entry block executable. The solver/// then discovers the rest of the blocks in the function are executable.TEST_F(SparsePropagationTest, ExceptionalTerminatorInsts) {Function *P = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "p", &M);Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "g", &M);Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false),GlobalValue::InternalLinkage, "f", &M);Constant *C =ConstantExpr::getCast(Instruction::BitCast, P, Builder.getInt8PtrTy());F->setPersonalityFn(C);BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);BasicBlock *Pad = BasicBlock::Create(Context, "catch.pad", F);BasicBlock *Body = BasicBlock::Create(Context, "catch.body", F);BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);Builder.SetInsertPoint(Entry);Builder.CreateInvoke(G, Exit, Pad);Builder.SetInsertPoint(Pad);CatchSwitchInst *CatchSwitch =Builder.CreateCatchSwitch(ConstantTokenNone::get(Context), nullptr, 1);CatchSwitch->addHandler(Body);Builder.SetInsertPoint(Body);CatchPadInst *CatchPad = Builder.CreateCatchPad(CatchSwitch, {});Builder.CreateCatchRet(CatchPad, Exit);Builder.SetInsertPoint(Exit);Builder.CreateRetVoid();Solver.MarkBlockExecutable(Entry);Solver.Solve();EXPECT_TRUE(Solver.isBlockExecutable(Pad));EXPECT_TRUE(Solver.isBlockExecutable(Body));EXPECT_TRUE(Solver.isBlockExecutable(Exit));}
//===- ScalarEvolutionsTest.cpp - ScalarEvolution unit tests --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallVector.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ScalarEvolutionExpressions.h"#include "llvm/Analysis/ScalarEvolutionNormalization.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"namespace llvm {// We use this fixture to ensure that we clean up ScalarEvolution before// deleting the PassManager.class ScalarEvolutionsTest : public testing::Test {protected:LLVMContext Context;Module M;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;std::unique_ptr<AssumptionCache> AC;std::unique_ptr<DominatorTree> DT;std::unique_ptr<LoopInfo> LI;ScalarEvolutionsTest() : M("", Context), TLII(), TLI(TLII) {}ScalarEvolution buildSE(Function &F) {AC.reset(new AssumptionCache(F));DT.reset(new DominatorTree(F));LI.reset(new LoopInfo(*DT));return ScalarEvolution(F, TLI, *AC, *DT, *LI);}void runWithSE(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI, ScalarEvolution &SE)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;ScalarEvolution SE = buildSE(*F);Test(*F, *LI, SE);}static Optional<APInt> computeConstantDifference(ScalarEvolution &SE,const SCEV *LHS,const SCEV *RHS) {return SE.computeConstantDifference(LHS, RHS);}static bool matchURem(ScalarEvolution &SE, const SCEV *Expr, const SCEV *&LHS,const SCEV *&RHS) {return SE.matchURem(Expr, LHS, RHS);}static bool isImpliedCond(ScalarEvolution &SE, ICmpInst::Predicate Pred, const SCEV *LHS,const SCEV *RHS, ICmpInst::Predicate FoundPred, const SCEV *FoundLHS,const SCEV *FoundRHS) {return SE.isImpliedCond(Pred, LHS, RHS, FoundPred, FoundLHS, FoundRHS);}};TEST_F(ScalarEvolutionsTest, SCEVUnknownRAUW) {FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context),std::vector<Type *>(), false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);BasicBlock *BB = BasicBlock::Create(Context, "entry", F);ReturnInst::Create(Context, nullptr, BB);Type *Ty = Type::getInt1Ty(Context);Constant *Init = Constant::getNullValue(Ty);Value *V0 = new GlobalVariable(M, Ty, false, GlobalValue::ExternalLinkage, Init, "V0");Value *V1 = new GlobalVariable(M, Ty, false, GlobalValue::ExternalLinkage, Init, "V1");Value *V2 = new GlobalVariable(M, Ty, false, GlobalValue::ExternalLinkage, Init, "V2");ScalarEvolution SE = buildSE(*F);const SCEV *S0 = SE.getSCEV(V0);const SCEV *S1 = SE.getSCEV(V1);const SCEV *S2 = SE.getSCEV(V2);const SCEV *P0 = SE.getAddExpr(S0, SE.getConstant(S0->getType(), 2));const SCEV *P1 = SE.getAddExpr(S1, SE.getConstant(S0->getType(), 2));const SCEV *P2 = SE.getAddExpr(S2, SE.getConstant(S0->getType(), 2));auto *M0 = cast<SCEVAddExpr>(P0);auto *M1 = cast<SCEVAddExpr>(P1);auto *M2 = cast<SCEVAddExpr>(P2);EXPECT_EQ(cast<SCEVConstant>(M0->getOperand(0))->getValue()->getZExtValue(),2u);EXPECT_EQ(cast<SCEVConstant>(M1->getOperand(0))->getValue()->getZExtValue(),2u);EXPECT_EQ(cast<SCEVConstant>(M2->getOperand(0))->getValue()->getZExtValue(),2u);// Before the RAUWs, these are all pointing to separate values.EXPECT_EQ(cast<SCEVUnknown>(M0->getOperand(1))->getValue(), V0);EXPECT_EQ(cast<SCEVUnknown>(M1->getOperand(1))->getValue(), V1);EXPECT_EQ(cast<SCEVUnknown>(M2->getOperand(1))->getValue(), V2);// Do some RAUWs.V2->replaceAllUsesWith(V1);V1->replaceAllUsesWith(V0);// After the RAUWs, these should all be pointing to V0.EXPECT_EQ(cast<SCEVUnknown>(M0->getOperand(1))->getValue(), V0);EXPECT_EQ(cast<SCEVUnknown>(M1->getOperand(1))->getValue(), V0);EXPECT_EQ(cast<SCEVUnknown>(M2->getOperand(1))->getValue(), V0);}TEST_F(ScalarEvolutionsTest, SimplifiedPHI) {FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context),std::vector<Type *>(), false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F);BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F);BranchInst::Create(LoopBB, EntryBB);BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)),LoopBB);ReturnInst::Create(Context, nullptr, ExitBB);auto *Ty = Type::getInt32Ty(Context);auto *PN = PHINode::Create(Ty, 2, "", &*LoopBB->begin());PN->addIncoming(Constant::getNullValue(Ty), EntryBB);PN->addIncoming(UndefValue::get(Ty), LoopBB);ScalarEvolution SE = buildSE(*F);auto *S1 = SE.getSCEV(PN);auto *S2 = SE.getSCEV(PN);auto *ZeroConst = SE.getConstant(Ty, 0);// At some point, only the first call to getSCEV returned the simplified// SCEVConstant and later calls just returned a SCEVUnknown referencing the// PHI node.EXPECT_EQ(S1, ZeroConst);EXPECT_EQ(S1, S2);}static Instruction *getInstructionByName(Function &F, StringRef Name) {for (auto &I : instructions(F))if (I.getName() == Name)return &I;llvm_unreachable("Expected to find instruction!");}static Value *getArgByName(Function &F, StringRef Name) {for (auto &Arg : F.args())if (Arg.getName() == Name)return &Arg;llvm_unreachable("Expected to find instruction!");}TEST_F(ScalarEvolutionsTest, CommutativeExprOperandOrder) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "" ""@var_0 = external global i32, align 4""@var_1 = external global i32, align 4""@var_2 = external global i32, align 4"" ""declare i32 @unknown(i32, i32, i32)"" ""define void @f_1(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "" local_unnamed_addr { ""entry: "" %entrycond = icmp sgt i32 %n, 0 "" br i1 %entrycond, label %loop.ph, label %for.end "" ""loop.ph: "" %a = load i32, i32* %A, align 4 "" %b = load i32, i32* %B, align 4 "" %mul = mul nsw i32 %b, %a "" %iv0.init = getelementptr inbounds i8, i8* %arr, i32 %mul "" br label %loop "" ""loop: "" %iv0 = phi i8* [ %iv0.inc, %loop ], [ %iv0.init, %loop.ph ] "" %iv1 = phi i32 [ %iv1.inc, %loop ], [ 0, %loop.ph ] "" %conv = trunc i32 %iv1 to i8 "" store i8 %conv, i8* %iv0, align 1 "" %iv0.inc = getelementptr inbounds i8, i8* %iv0, i32 %b "" %iv1.inc = add nuw nsw i32 %iv1, 1 "" %exitcond = icmp eq i32 %iv1.inc, %n "" br i1 %exitcond, label %for.end.loopexit, label %loop "" ""for.end.loopexit: "" br label %for.end "" ""for.end: "" ret void ""} "" ""define void @f_2(i32* %X, i32* %Y, i32* %Z) { "" %x = load i32, i32* %X "" %y = load i32, i32* %Y "" %z = load i32, i32* %Z "" ret void ""} "" ""define void @f_3() { "" %x = load i32, i32* @var_0"" %y = load i32, i32* @var_1"" %z = load i32, i32* @var_2"" ret void""} "" ""define void @f_4(i32 %a, i32 %b, i32 %c) { "" %x = call i32 @unknown(i32 %a, i32 %b, i32 %c)"" %y = call i32 @unknown(i32 %b, i32 %c, i32 %a)"" %z = call i32 @unknown(i32 %c, i32 %a, i32 %b)"" ret void""} ",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "f_1", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *IV0 = getInstructionByName(F, "iv0");auto *IV0Inc = getInstructionByName(F, "iv0.inc");auto *FirstExprForIV0 = SE.getSCEV(IV0);auto *FirstExprForIV0Inc = SE.getSCEV(IV0Inc);auto *SecondExprForIV0 = SE.getSCEV(IV0);EXPECT_TRUE(isa<SCEVAddRecExpr>(FirstExprForIV0));EXPECT_TRUE(isa<SCEVAddRecExpr>(FirstExprForIV0Inc));EXPECT_TRUE(isa<SCEVAddRecExpr>(SecondExprForIV0));});auto CheckCommutativeMulExprs = [&](ScalarEvolution &SE, const SCEV *A,const SCEV *B, const SCEV *C) {EXPECT_EQ(SE.getMulExpr(A, B), SE.getMulExpr(B, A));EXPECT_EQ(SE.getMulExpr(B, C), SE.getMulExpr(C, B));EXPECT_EQ(SE.getMulExpr(A, C), SE.getMulExpr(C, A));SmallVector<const SCEV *, 3> Ops0 = {A, B, C};SmallVector<const SCEV *, 3> Ops1 = {A, C, B};SmallVector<const SCEV *, 3> Ops2 = {B, A, C};SmallVector<const SCEV *, 3> Ops3 = {B, C, A};SmallVector<const SCEV *, 3> Ops4 = {C, B, A};SmallVector<const SCEV *, 3> Ops5 = {C, A, B};auto *Mul0 = SE.getMulExpr(Ops0);auto *Mul1 = SE.getMulExpr(Ops1);auto *Mul2 = SE.getMulExpr(Ops2);auto *Mul3 = SE.getMulExpr(Ops3);auto *Mul4 = SE.getMulExpr(Ops4);auto *Mul5 = SE.getMulExpr(Ops5);EXPECT_EQ(Mul0, Mul1) << "Expected " << *Mul0 << " == " << *Mul1;EXPECT_EQ(Mul1, Mul2) << "Expected " << *Mul1 << " == " << *Mul2;EXPECT_EQ(Mul2, Mul3) << "Expected " << *Mul2 << " == " << *Mul3;EXPECT_EQ(Mul3, Mul4) << "Expected " << *Mul3 << " == " << *Mul4;EXPECT_EQ(Mul4, Mul5) << "Expected " << *Mul4 << " == " << *Mul5;};for (StringRef FuncName : {"f_2", "f_3", "f_4"})runWithSE(*M, FuncName, [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {CheckCommutativeMulExprs(SE, SE.getSCEV(getInstructionByName(F, "x")),SE.getSCEV(getInstructionByName(F, "y")),SE.getSCEV(getInstructionByName(F, "z")));});}TEST_F(ScalarEvolutionsTest, CompareSCEVComplexity) {FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), std::vector<Type *>(), false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);BasicBlock *LoopBB = BasicBlock::Create(Context, "bb1", F);BranchInst::Create(LoopBB, EntryBB);auto *Ty = Type::getInt32Ty(Context);SmallVector<Instruction*, 8> Muls(8), Acc(8), NextAcc(8);Acc[0] = PHINode::Create(Ty, 2, "", LoopBB);Acc[1] = PHINode::Create(Ty, 2, "", LoopBB);Acc[2] = PHINode::Create(Ty, 2, "", LoopBB);Acc[3] = PHINode::Create(Ty, 2, "", LoopBB);Acc[4] = PHINode::Create(Ty, 2, "", LoopBB);Acc[5] = PHINode::Create(Ty, 2, "", LoopBB);Acc[6] = PHINode::Create(Ty, 2, "", LoopBB);Acc[7] = PHINode::Create(Ty, 2, "", LoopBB);for (int i = 0; i < 20; i++) {Muls[0] = BinaryOperator::CreateMul(Acc[0], Acc[0], "", LoopBB);NextAcc[0] = BinaryOperator::CreateAdd(Muls[0], Acc[4], "", LoopBB);Muls[1] = BinaryOperator::CreateMul(Acc[1], Acc[1], "", LoopBB);NextAcc[1] = BinaryOperator::CreateAdd(Muls[1], Acc[5], "", LoopBB);Muls[2] = BinaryOperator::CreateMul(Acc[2], Acc[2], "", LoopBB);NextAcc[2] = BinaryOperator::CreateAdd(Muls[2], Acc[6], "", LoopBB);Muls[3] = BinaryOperator::CreateMul(Acc[3], Acc[3], "", LoopBB);NextAcc[3] = BinaryOperator::CreateAdd(Muls[3], Acc[7], "", LoopBB);Muls[4] = BinaryOperator::CreateMul(Acc[4], Acc[4], "", LoopBB);NextAcc[4] = BinaryOperator::CreateAdd(Muls[4], Acc[0], "", LoopBB);Muls[5] = BinaryOperator::CreateMul(Acc[5], Acc[5], "", LoopBB);NextAcc[5] = BinaryOperator::CreateAdd(Muls[5], Acc[1], "", LoopBB);Muls[6] = BinaryOperator::CreateMul(Acc[6], Acc[6], "", LoopBB);NextAcc[6] = BinaryOperator::CreateAdd(Muls[6], Acc[2], "", LoopBB);Muls[7] = BinaryOperator::CreateMul(Acc[7], Acc[7], "", LoopBB);NextAcc[7] = BinaryOperator::CreateAdd(Muls[7], Acc[3], "", LoopBB);Acc = NextAcc;}auto II = LoopBB->begin();for (int i = 0; i < 8; i++) {PHINode *Phi = cast<PHINode>(&*II++);Phi->addIncoming(Acc[i], LoopBB);Phi->addIncoming(UndefValue::get(Ty), EntryBB);}BasicBlock *ExitBB = BasicBlock::Create(Context, "bb2", F);BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)),LoopBB);Acc[0] = BinaryOperator::CreateAdd(Acc[0], Acc[1], "", ExitBB);Acc[1] = BinaryOperator::CreateAdd(Acc[2], Acc[3], "", ExitBB);Acc[2] = BinaryOperator::CreateAdd(Acc[4], Acc[5], "", ExitBB);Acc[3] = BinaryOperator::CreateAdd(Acc[6], Acc[7], "", ExitBB);Acc[0] = BinaryOperator::CreateAdd(Acc[0], Acc[1], "", ExitBB);Acc[1] = BinaryOperator::CreateAdd(Acc[2], Acc[3], "", ExitBB);Acc[0] = BinaryOperator::CreateAdd(Acc[0], Acc[1], "", ExitBB);ReturnInst::Create(Context, nullptr, ExitBB);ScalarEvolution SE = buildSE(*F);EXPECT_NE(nullptr, SE.getSCEV(Acc[0]));}TEST_F(ScalarEvolutionsTest, CompareValueComplexity) {IntegerType *IntPtrTy = M.getDataLayout().getIntPtrType(Context);PointerType *IntPtrPtrTy = IntPtrTy->getPointerTo();FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {IntPtrTy, IntPtrTy}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);Value *X = &*F->arg_begin();Value *Y = &*std::next(F->arg_begin());const int ValueDepth = 10;for (int i = 0; i < ValueDepth; i++) {X = new LoadInst(IntPtrTy, new IntToPtrInst(X, IntPtrPtrTy, "", EntryBB),"",/*isVolatile*/ false, EntryBB);Y = new LoadInst(IntPtrTy, new IntToPtrInst(Y, IntPtrPtrTy, "", EntryBB),"",/*isVolatile*/ false, EntryBB);}auto *MulA = BinaryOperator::CreateMul(X, Y, "", EntryBB);auto *MulB = BinaryOperator::CreateMul(Y, X, "", EntryBB);ReturnInst::Create(Context, nullptr, EntryBB);// This test isn't checking for correctness. Today making A and B resolve to// the same SCEV would require deeper searching in CompareValueComplexity,// which will slow down compilation. However, this test can fail (with LLVM's// behavior still being correct) if we ever have a smarter// CompareValueComplexity that is both fast and more accurate.ScalarEvolution SE = buildSE(*F);auto *A = SE.getSCEV(MulA);auto *B = SE.getSCEV(MulB);EXPECT_NE(A, B);}TEST_F(ScalarEvolutionsTest, SCEVAddExpr) {Type *Ty32 = Type::getInt32Ty(Context);Type *ArgTys[] = {Type::getInt64Ty(Context), Ty32, Ty32, Ty32, Ty32, Ty32};FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), ArgTys, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);Argument *A1 = &*F->arg_begin();Argument *A2 = &*(std::next(F->arg_begin()));BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);Instruction *Trunc = CastInst::CreateTruncOrBitCast(A1, Ty32, "", EntryBB);Instruction *Mul1 = BinaryOperator::CreateMul(Trunc, A2, "", EntryBB);Instruction *Add1 = BinaryOperator::CreateAdd(Mul1, Trunc, "", EntryBB);Mul1 = BinaryOperator::CreateMul(Add1, Trunc, "", EntryBB);Instruction *Add2 = BinaryOperator::CreateAdd(Mul1, Add1, "", EntryBB);// FIXME: The size of this is arbitrary and doesn't seem to change the// result, but SCEV will do quadratic work for these so a large number here// will be extremely slow. We should revisit what and how this is testing// SCEV.for (int i = 0; i < 10; i++) {Mul1 = BinaryOperator::CreateMul(Add2, Add1, "", EntryBB);Add1 = Add2;Add2 = BinaryOperator::CreateAdd(Mul1, Add1, "", EntryBB);}ReturnInst::Create(Context, nullptr, EntryBB);ScalarEvolution SE = buildSE(*F);EXPECT_NE(nullptr, SE.getSCEV(Mul1));Argument *A3 = &*(std::next(F->arg_begin(), 2));Argument *A4 = &*(std::next(F->arg_begin(), 3));Argument *A5 = &*(std::next(F->arg_begin(), 4));Argument *A6 = &*(std::next(F->arg_begin(), 5));auto *AddWithNUW = cast<SCEVAddExpr>(SE.getAddExpr(SE.getAddExpr(SE.getSCEV(A2), SE.getSCEV(A3), SCEV::FlagNUW),SE.getConstant(APInt(/*numBits=*/32, 5)), SCEV::FlagNUW));EXPECT_EQ(AddWithNUW->getNumOperands(), 3u);EXPECT_EQ(AddWithNUW->getNoWrapFlags(), SCEV::FlagNUW);auto *AddWithAnyWrap =SE.getAddExpr(SE.getSCEV(A3), SE.getSCEV(A4), SCEV::FlagAnyWrap);auto *AddWithAnyWrapNUW = cast<SCEVAddExpr>(SE.getAddExpr(AddWithAnyWrap, SE.getSCEV(A5), SCEV::FlagNUW));EXPECT_EQ(AddWithAnyWrapNUW->getNumOperands(), 3u);EXPECT_EQ(AddWithAnyWrapNUW->getNoWrapFlags(), SCEV::FlagAnyWrap);auto *AddWithNSW = SE.getAddExpr(SE.getSCEV(A2), SE.getConstant(APInt(32, 99)), SCEV::FlagNSW);auto *AddWithNSW_NUW = cast<SCEVAddExpr>(SE.getAddExpr(AddWithNSW, SE.getSCEV(A5), SCEV::FlagNUW));EXPECT_EQ(AddWithNSW_NUW->getNumOperands(), 3u);EXPECT_EQ(AddWithNSW_NUW->getNoWrapFlags(), SCEV::FlagAnyWrap);auto *AddWithNSWNUW =SE.getAddExpr(SE.getSCEV(A2), SE.getSCEV(A4),ScalarEvolution::setFlags(SCEV::FlagNUW, SCEV::FlagNSW));auto *AddWithNSWNUW_NUW = cast<SCEVAddExpr>(SE.getAddExpr(AddWithNSWNUW, SE.getSCEV(A5), SCEV::FlagNUW));EXPECT_EQ(AddWithNSWNUW_NUW->getNumOperands(), 3u);EXPECT_EQ(AddWithNSWNUW_NUW->getNoWrapFlags(), SCEV::FlagNUW);auto *AddWithNSW_NSWNUW = cast<SCEVAddExpr>(SE.getAddExpr(AddWithNSW, SE.getSCEV(A6),ScalarEvolution::setFlags(SCEV::FlagNUW, SCEV::FlagNSW)));EXPECT_EQ(AddWithNSW_NSWNUW->getNumOperands(), 3u);EXPECT_EQ(AddWithNSW_NSWNUW->getNoWrapFlags(), SCEV::FlagAnyWrap);}static Instruction &GetInstByName(Function &F, StringRef Name) {for (auto &I : instructions(F))if (I.getName() == Name)return I;llvm_unreachable("Could not find instructions!");}TEST_F(ScalarEvolutionsTest, SCEVNormalization) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "" ""@var_0 = external global i32, align 4""@var_1 = external global i32, align 4""@var_2 = external global i32, align 4"" ""declare i32 @unknown(i32, i32, i32)"" ""define void @f_1(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "" local_unnamed_addr { ""entry: "" br label %loop.ph "" ""loop.ph: "" br label %loop "" ""loop: "" %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] "" %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] "" %iv0.inc = add i32 %iv0, 1 "" %iv1.inc = add i32 %iv1, 3 "" br i1 undef, label %for.end.loopexit, label %loop "" ""for.end.loopexit: "" ret void ""} "" ""define void @f_2(i32 %a, i32 %b, i32 %c, i32 %d) "" local_unnamed_addr { ""entry: "" br label %loop_0 "" ""loop_0: "" br i1 undef, label %loop_0, label %loop_1 "" ""loop_1: "" br i1 undef, label %loop_2, label %loop_1 "" "" ""loop_2: "" br i1 undef, label %end, label %loop_2 "" ""end: "" ret void ""} ",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "f_1", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto &I0 = GetInstByName(F, "iv0");auto &I1 = *I0.getNextNode();auto *S0 = cast<SCEVAddRecExpr>(SE.getSCEV(&I0));PostIncLoopSet Loops;Loops.insert(S0->getLoop());auto *N0 = normalizeForPostIncUse(S0, Loops, SE);auto *D0 = denormalizeForPostIncUse(N0, Loops, SE);EXPECT_EQ(S0, D0) << *S0 << " " << *D0;auto *S1 = cast<SCEVAddRecExpr>(SE.getSCEV(&I1));Loops.clear();Loops.insert(S1->getLoop());auto *N1 = normalizeForPostIncUse(S1, Loops, SE);auto *D1 = denormalizeForPostIncUse(N1, Loops, SE);EXPECT_EQ(S1, D1) << *S1 << " " << *D1;});runWithSE(*M, "f_2", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *L2 = *LI.begin();auto *L1 = *std::next(LI.begin());auto *L0 = *std::next(LI.begin(), 2);auto GetAddRec = [&SE](const Loop *L, std::initializer_list<const SCEV *> Ops) {SmallVector<const SCEV *, 4> OpsCopy(Ops);return SE.getAddRecExpr(OpsCopy, L, SCEV::FlagAnyWrap);};auto GetAdd = [&SE](std::initializer_list<const SCEV *> Ops) {SmallVector<const SCEV *, 4> OpsCopy(Ops);return SE.getAddExpr(OpsCopy, SCEV::FlagAnyWrap);};// We first populate the AddRecs vector with a few "interesting" SCEV// expressions, and then we go through the list and assert that each// expression in it has an invertible normalization.std::vector<const SCEV *> Exprs;{const SCEV *V0 = SE.getSCEV(&*F.arg_begin());const SCEV *V1 = SE.getSCEV(&*std::next(F.arg_begin(), 1));const SCEV *V2 = SE.getSCEV(&*std::next(F.arg_begin(), 2));const SCEV *V3 = SE.getSCEV(&*std::next(F.arg_begin(), 3));Exprs.push_back(GetAddRec(L0, {V0})); // 0Exprs.push_back(GetAddRec(L0, {V0, V1})); // 1Exprs.push_back(GetAddRec(L0, {V0, V1, V2})); // 2Exprs.push_back(GetAddRec(L0, {V0, V1, V2, V3})); // 3Exprs.push_back(GetAddRec(L1, {Exprs[1], Exprs[2], Exprs[3], Exprs[0]})); // 4Exprs.push_back(GetAddRec(L1, {Exprs[1], Exprs[2], Exprs[0], Exprs[3]})); // 5Exprs.push_back(GetAddRec(L1, {Exprs[1], Exprs[3], Exprs[3], Exprs[1]})); // 6Exprs.push_back(GetAdd({Exprs[6], Exprs[3], V2})); // 7Exprs.push_back(GetAddRec(L2, {Exprs[4], Exprs[3], Exprs[3], Exprs[5]})); // 8Exprs.push_back(GetAddRec(L2, {Exprs[4], Exprs[6], Exprs[7], Exprs[3], V0})); // 9}std::vector<PostIncLoopSet> LoopSets;for (int i = 0; i < 8; i++) {LoopSets.emplace_back();if (i & 1)LoopSets.back().insert(L0);if (i & 2)LoopSets.back().insert(L1);if (i & 4)LoopSets.back().insert(L2);}for (const auto &LoopSet : LoopSets)for (auto *S : Exprs) {{auto *N = llvm::normalizeForPostIncUse(S, LoopSet, SE);auto *D = llvm::denormalizeForPostIncUse(N, LoopSet, SE);// Normalization and then denormalizing better give us back the same// value.EXPECT_EQ(S, D) << "S = " << *S << " D = " << *D << " N = " << *N;}{auto *D = llvm::denormalizeForPostIncUse(S, LoopSet, SE);auto *N = llvm::normalizeForPostIncUse(D, LoopSet, SE);// Denormalization and then normalizing better give us back the same// value.EXPECT_EQ(S, N) << "S = " << *S << " N = " << *N;}}});}// Expect the call of getZeroExtendExpr will not cost exponential time.TEST_F(ScalarEvolutionsTest, SCEVZeroExtendExpr) {LLVMContext C;SMDiagnostic Err;// Generate a function like below:// define void @foo() {// entry:// br label %for.cond//// for.cond:// %0 = phi i64 [ 100, %entry ], [ %dec, %for.inc ]// %cmp = icmp sgt i64 %0, 90// br i1 %cmp, label %for.inc, label %for.cond1//// for.inc:// %dec = add nsw i64 %0, -1// br label %for.cond//// for.cond1:// %1 = phi i64 [ 100, %for.cond ], [ %dec5, %for.inc2 ]// %cmp3 = icmp sgt i64 %1, 90// br i1 %cmp3, label %for.inc2, label %for.cond4//// for.inc2:// %dec5 = add nsw i64 %1, -1// br label %for.cond1//// ......//// for.cond89:// %19 = phi i64 [ 100, %for.cond84 ], [ %dec94, %for.inc92 ]// %cmp93 = icmp sgt i64 %19, 90// br i1 %cmp93, label %for.inc92, label %for.end//// for.inc92:// %dec94 = add nsw i64 %19, -1// br label %for.cond89//// for.end:// %gep = getelementptr i8, i8* null, i64 %dec// %gep6 = getelementptr i8, i8* %gep, i64 %dec5// ......// %gep95 = getelementptr i8, i8* %gep91, i64 %dec94// ret void// }FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), {}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);BasicBlock *CondBB = BasicBlock::Create(Context, "for.cond", F);BasicBlock *EndBB = BasicBlock::Create(Context, "for.end", F);BranchInst::Create(CondBB, EntryBB);BasicBlock *PrevBB = EntryBB;Type *I64Ty = Type::getInt64Ty(Context);Type *I8Ty = Type::getInt8Ty(Context);Type *I8PtrTy = Type::getInt8PtrTy(Context);Value *Accum = Constant::getNullValue(I8PtrTy);int Iters = 20;for (int i = 0; i < Iters; i++) {BasicBlock *IncBB = BasicBlock::Create(Context, "for.inc", F, EndBB);auto *PN = PHINode::Create(I64Ty, 2, "", CondBB);PN->addIncoming(ConstantInt::get(Context, APInt(64, 100)), PrevBB);auto *Cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_SGT, PN,ConstantInt::get(Context, APInt(64, 90)), "cmp",CondBB);BasicBlock *NextBB;if (i != Iters - 1)NextBB = BasicBlock::Create(Context, "for.cond", F, EndBB);elseNextBB = EndBB;BranchInst::Create(IncBB, NextBB, Cmp, CondBB);auto *Dec = BinaryOperator::CreateNSWAdd(PN, ConstantInt::get(Context, APInt(64, -1)), "dec", IncBB);PN->addIncoming(Dec, IncBB);BranchInst::Create(CondBB, IncBB);Accum = GetElementPtrInst::Create(I8Ty, Accum, PN, "gep", EndBB);PrevBB = CondBB;CondBB = NextBB;}ReturnInst::Create(Context, nullptr, EndBB);ScalarEvolution SE = buildSE(*F);const SCEV *S = SE.getSCEV(Accum);S = SE.getLosslessPtrToIntExpr(S);Type *I128Ty = Type::getInt128Ty(Context);SE.getZeroExtendExpr(S, I128Ty);}// Make sure that SCEV invalidates exit limits after invalidating the values it// depends on when we forget a loop.TEST_F(ScalarEvolutionsTest, SCEVExitLimitForgetLoop) {/** Create the following code:* func(i64 addrspace(10)* %arg)* top:* br label %L.ph* L.ph:* br label %L* L:* %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ]* %add = add i64 %phi2, 1* %cond = icmp slt i64 %add, 1000; then becomes 2000.* br i1 %cond, label %post, label %L2* post:* ret void**/// Create a module with non-integral pointers in it's datalayoutModule NIM("nonintegral", Context);std::string DataLayout = M.getDataLayoutStr();if (!DataLayout.empty())DataLayout += "-";DataLayout += "ni:10";NIM.setDataLayout(DataLayout);Type *T_int64 = Type::getInt64Ty(Context);Type *T_pint64 = T_int64->getPointerTo(10);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM);BasicBlock *Top = BasicBlock::Create(Context, "top", F);BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F);BasicBlock *L = BasicBlock::Create(Context, "L", F);BasicBlock *Post = BasicBlock::Create(Context, "post", F);IRBuilder<> Builder(Top);Builder.CreateBr(LPh);Builder.SetInsertPoint(LPh);Builder.CreateBr(L);Builder.SetInsertPoint(L);PHINode *Phi = Builder.CreatePHI(T_int64, 2);auto *Add = cast<Instruction>(Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add"));auto *Limit = ConstantInt::get(T_int64, 1000);auto *Cond = cast<Instruction>(Builder.CreateICmp(ICmpInst::ICMP_SLT, Add, Limit, "cond"));auto *Br = cast<Instruction>(Builder.CreateCondBr(Cond, L, Post));Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh);Phi->addIncoming(Add, L);Builder.SetInsertPoint(Post);Builder.CreateRetVoid();ScalarEvolution SE = buildSE(*F);auto *Loop = LI->getLoopFor(L);const SCEV *EC = SE.getBackedgeTakenCount(Loop);EXPECT_FALSE(isa<SCEVCouldNotCompute>(EC));EXPECT_TRUE(isa<SCEVConstant>(EC));EXPECT_EQ(cast<SCEVConstant>(EC)->getAPInt().getLimitedValue(), 999u);// The add recurrence {5,+,1} does not correspond to any PHI in the IR, and// that is relevant to this test.auto *Five = SE.getConstant(APInt(/*numBits=*/64, 5));auto *AR =SE.getAddRecExpr(Five, SE.getOne(T_int64), Loop, SCEV::FlagAnyWrap);const SCEV *ARAtLoopExit = SE.getSCEVAtScope(AR, nullptr);EXPECT_FALSE(isa<SCEVCouldNotCompute>(ARAtLoopExit));EXPECT_TRUE(isa<SCEVConstant>(ARAtLoopExit));EXPECT_EQ(cast<SCEVConstant>(ARAtLoopExit)->getAPInt().getLimitedValue(),1004u);SE.forgetLoop(Loop);Br->eraseFromParent();Cond->eraseFromParent();Builder.SetInsertPoint(L);auto *NewCond = Builder.CreateICmp(ICmpInst::ICMP_SLT, Add, ConstantInt::get(T_int64, 2000), "new.cond");Builder.CreateCondBr(NewCond, L, Post);const SCEV *NewEC = SE.getBackedgeTakenCount(Loop);EXPECT_FALSE(isa<SCEVCouldNotCompute>(NewEC));EXPECT_TRUE(isa<SCEVConstant>(NewEC));EXPECT_EQ(cast<SCEVConstant>(NewEC)->getAPInt().getLimitedValue(), 1999u);const SCEV *NewARAtLoopExit = SE.getSCEVAtScope(AR, nullptr);EXPECT_FALSE(isa<SCEVCouldNotCompute>(NewARAtLoopExit));EXPECT_TRUE(isa<SCEVConstant>(NewARAtLoopExit));EXPECT_EQ(cast<SCEVConstant>(NewARAtLoopExit)->getAPInt().getLimitedValue(),2004u);}// Make sure that SCEV invalidates exit limits after invalidating the values it// depends on when we forget a value.TEST_F(ScalarEvolutionsTest, SCEVExitLimitForgetValue) {/** Create the following code:* func(i64 addrspace(10)* %arg)* top:* br label %L.ph* L.ph:* %load = load i64 addrspace(10)* %arg* br label %L* L:* %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ]* %add = add i64 %phi2, 1* %cond = icmp slt i64 %add, %load ; then becomes 2000.* br i1 %cond, label %post, label %L2* post:* ret void**/// Create a module with non-integral pointers in it's datalayoutModule NIM("nonintegral", Context);std::string DataLayout = M.getDataLayoutStr();if (!DataLayout.empty())DataLayout += "-";DataLayout += "ni:10";NIM.setDataLayout(DataLayout);Type *T_int64 = Type::getInt64Ty(Context);Type *T_pint64 = T_int64->getPointerTo(10);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM);Argument *Arg = &*F->arg_begin();BasicBlock *Top = BasicBlock::Create(Context, "top", F);BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F);BasicBlock *L = BasicBlock::Create(Context, "L", F);BasicBlock *Post = BasicBlock::Create(Context, "post", F);IRBuilder<> Builder(Top);Builder.CreateBr(LPh);Builder.SetInsertPoint(LPh);auto *Load = cast<Instruction>(Builder.CreateLoad(T_int64, Arg, "load"));Builder.CreateBr(L);Builder.SetInsertPoint(L);PHINode *Phi = Builder.CreatePHI(T_int64, 2);auto *Add = cast<Instruction>(Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add"));auto *Cond = cast<Instruction>(Builder.CreateICmp(ICmpInst::ICMP_SLT, Add, Load, "cond"));auto *Br = cast<Instruction>(Builder.CreateCondBr(Cond, L, Post));Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh);Phi->addIncoming(Add, L);Builder.SetInsertPoint(Post);Builder.CreateRetVoid();ScalarEvolution SE = buildSE(*F);auto *Loop = LI->getLoopFor(L);const SCEV *EC = SE.getBackedgeTakenCount(Loop);EXPECT_FALSE(isa<SCEVCouldNotCompute>(EC));EXPECT_FALSE(isa<SCEVConstant>(EC));SE.forgetValue(Load);Br->eraseFromParent();Cond->eraseFromParent();Load->eraseFromParent();Builder.SetInsertPoint(L);auto *NewCond = Builder.CreateICmp(ICmpInst::ICMP_SLT, Add, ConstantInt::get(T_int64, 2000), "new.cond");Builder.CreateCondBr(NewCond, L, Post);const SCEV *NewEC = SE.getBackedgeTakenCount(Loop);EXPECT_FALSE(isa<SCEVCouldNotCompute>(NewEC));EXPECT_TRUE(isa<SCEVConstant>(NewEC));EXPECT_EQ(cast<SCEVConstant>(NewEC)->getAPInt().getLimitedValue(), 1999u);}TEST_F(ScalarEvolutionsTest, SCEVAddRecFromPHIwithLargeConstants) {// Reference: https://reviews.llvm.org/D37265// Make sure that SCEV does not blow up when constructing an AddRec// with predicates for a phi with the update pattern:// (SExt/ZExt ix (Trunc iy (%SymbolicPHI) to ix) to iy) + InvariantAccum// when either the initial value of the Phi or the InvariantAccum are// constants that are too large to fit in an ix but are zero when truncated to// ix.FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), std::vector<Type *>(), false);Function *F =Function::Create(FTy, Function::ExternalLinkage, "addrecphitest", M);/*Create IR:entry:br label %looploop:%0 = phi i64 [-9223372036854775808, %entry], [%3, %loop]%1 = shl i64 %0, 32%2 = ashr exact i64 %1, 32%3 = add i64 %2, -9223372036854775808br i1 undef, label %exit, label %loopexit:ret void*/BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F);BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F);// entry:BranchInst::Create(LoopBB, EntryBB);// loop:auto *MinInt64 =ConstantInt::get(Context, APInt(64, 0x8000000000000000U, true));auto *Int64_32 = ConstantInt::get(Context, APInt(64, 32));auto *Br = BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB);auto *Phi = PHINode::Create(Type::getInt64Ty(Context), 2, "", Br);auto *Shl = BinaryOperator::CreateShl(Phi, Int64_32, "", Br);auto *AShr = BinaryOperator::CreateExactAShr(Shl, Int64_32, "", Br);auto *Add = BinaryOperator::CreateAdd(AShr, MinInt64, "", Br);Phi->addIncoming(MinInt64, EntryBB);Phi->addIncoming(Add, LoopBB);// exit:ReturnInst::Create(Context, nullptr, ExitBB);// Make sure that SCEV doesn't blow upScalarEvolution SE = buildSE(*F);const SCEV *Expr = SE.getSCEV(Phi);EXPECT_NE(nullptr, Expr);EXPECT_TRUE(isa<SCEVUnknown>(Expr));auto Result = SE.createAddRecFromPHIWithCasts(cast<SCEVUnknown>(Expr));}TEST_F(ScalarEvolutionsTest, SCEVAddRecFromPHIwithLargeConstantAccum) {// Make sure that SCEV does not blow up when constructing an AddRec// with predicates for a phi with the update pattern:// (SExt/ZExt ix (Trunc iy (%SymbolicPHI) to ix) to iy) + InvariantAccum// when the InvariantAccum is a constant that is too large to fit in an// ix but are zero when truncated to ix, and the initial value of the// phi is not a constant.Type *Int32Ty = Type::getInt32Ty(Context);SmallVector<Type *, 1> Types;Types.push_back(Int32Ty);FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), Types, false);Function *F =Function::Create(FTy, Function::ExternalLinkage, "addrecphitest", M);/*Create IR:define @addrecphitest(i32)entry:br label %looploop:%1 = phi i32 [%0, %entry], [%4, %loop]%2 = shl i32 %1, 16%3 = ashr exact i32 %2, 16%4 = add i32 %3, -2147483648br i1 undef, label %exit, label %loopexit:ret void*/BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F);BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F);// entry:BranchInst::Create(LoopBB, EntryBB);// loop:auto *MinInt32 = ConstantInt::get(Context, APInt(32, 0x80000000U, true));auto *Int32_16 = ConstantInt::get(Context, APInt(32, 16));auto *Br = BranchInst::Create(LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB);auto *Phi = PHINode::Create(Int32Ty, 2, "", Br);auto *Shl = BinaryOperator::CreateShl(Phi, Int32_16, "", Br);auto *AShr = BinaryOperator::CreateExactAShr(Shl, Int32_16, "", Br);auto *Add = BinaryOperator::CreateAdd(AShr, MinInt32, "", Br);auto *Arg = &*(F->arg_begin());Phi->addIncoming(Arg, EntryBB);Phi->addIncoming(Add, LoopBB);// exit:ReturnInst::Create(Context, nullptr, ExitBB);// Make sure that SCEV doesn't blow upScalarEvolution SE = buildSE(*F);const SCEV *Expr = SE.getSCEV(Phi);EXPECT_NE(nullptr, Expr);EXPECT_TRUE(isa<SCEVUnknown>(Expr));auto Result = SE.createAddRecFromPHIWithCasts(cast<SCEVUnknown>(Expr));}TEST_F(ScalarEvolutionsTest, SCEVFoldSumOfTruncs) {// Verify that the following SCEV gets folded to a zero:// (-1 * (trunc i64 (-1 * %0) to i32)) + (-1 * (trunc i64 %0 to i32)Type *ArgTy = Type::getInt64Ty(Context);Type *Int32Ty = Type::getInt32Ty(Context);SmallVector<Type *, 1> Types;Types.push_back(ArgTy);FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), Types, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);BasicBlock *BB = BasicBlock::Create(Context, "entry", F);ReturnInst::Create(Context, nullptr, BB);ScalarEvolution SE = buildSE(*F);auto *Arg = &*(F->arg_begin());const auto *ArgSCEV = SE.getSCEV(Arg);// Build the SCEVconst auto *A0 = SE.getNegativeSCEV(ArgSCEV);const auto *A1 = SE.getTruncateExpr(A0, Int32Ty);const auto *A = SE.getNegativeSCEV(A1);const auto *B0 = SE.getTruncateExpr(ArgSCEV, Int32Ty);const auto *B = SE.getNegativeSCEV(B0);const auto *Expr = SE.getAddExpr(A, B);// Verify that the SCEV was folded to 0const auto *ZeroConst = SE.getConstant(Int32Ty, 0);EXPECT_EQ(Expr, ZeroConst);}// Check logic of SCEV expression size computation.TEST_F(ScalarEvolutionsTest, SCEVComputeExpressionSize) {/** Create the following code:* void func(i64 %a, i64 %b)* entry:* %s1 = add i64 %a, 1* %s2 = udiv i64 %s1, %b* br label %exit* exit:* ret*/// Create a module.Module M("SCEVComputeExpressionSize", Context);Type *T_int64 = Type::getInt64Ty(Context);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), { T_int64, T_int64 }, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M);Argument *A = &*F->arg_begin();Argument *B = &*std::next(F->arg_begin());ConstantInt *C = ConstantInt::get(Context, APInt(64, 1));BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);BasicBlock *Exit = BasicBlock::Create(Context, "exit", F);IRBuilder<> Builder(Entry);auto *S1 = cast<Instruction>(Builder.CreateAdd(A, C, "s1"));auto *S2 = cast<Instruction>(Builder.CreateUDiv(S1, B, "s2"));Builder.CreateBr(Exit);Builder.SetInsertPoint(Exit);Builder.CreateRetVoid();ScalarEvolution SE = buildSE(*F);// Get S2 first to move it to cache.const SCEV *AS = SE.getSCEV(A);const SCEV *BS = SE.getSCEV(B);const SCEV *CS = SE.getSCEV(C);const SCEV *S1S = SE.getSCEV(S1);const SCEV *S2S = SE.getSCEV(S2);EXPECT_EQ(AS->getExpressionSize(), 1u);EXPECT_EQ(BS->getExpressionSize(), 1u);EXPECT_EQ(CS->getExpressionSize(), 1u);EXPECT_EQ(S1S->getExpressionSize(), 3u);EXPECT_EQ(S2S->getExpressionSize(), 5u);}TEST_F(ScalarEvolutionsTest, SCEVLoopDecIntrinsic) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 %N) { ""entry: "" %cmp3 = icmp sgt i32 %N, 0 "" br i1 %cmp3, label %for.body, label %for.cond.cleanup ""for.cond.cleanup: "" ret void ""for.body: "" %i.04 = phi i32 [ %inc, %for.body ], [ 100, %entry ] "" %inc = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 %i.04, i32 1) "" %exitcond = icmp ne i32 %inc, 0 "" br i1 %exitcond, label %for.cond.cleanup, label %for.body ""} ""declare i32 @llvm.loop.decrement.reg.i32.i32.i32(i32, i32) ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevInc = SE.getSCEV(getInstructionByName(F, "inc"));EXPECT_TRUE(isa<SCEVAddRecExpr>(ScevInc));});}TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 %sz, i32 %pp) { ""entry: "" %v0 = add i32 %pp, 0 "" %v3 = add i32 %pp, 3 "" br label %loop.body ""loop.body: "" %iv = phi i32 [ %iv.next, %loop.body ], [ 0, %entry ] "" %xa = add nsw i32 %iv, %v0 "" %yy = add nsw i32 %iv, %v3 "" %xb = sub nsw i32 %yy, 3 "" %iv.next = add nsw i32 %iv, 1 "" %cmp = icmp sle i32 %iv.next, %sz "" br i1 %cmp, label %loop.body, label %exit ""exit: "" ret void ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevV0 = SE.getSCEV(getInstructionByName(F, "v0")); // %ppauto *ScevV3 = SE.getSCEV(getInstructionByName(F, "v3")); // (3 + %pp)auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv")); // {0,+,1}auto *ScevXA = SE.getSCEV(getInstructionByName(F, "xa")); // {%pp,+,1}auto *ScevYY = SE.getSCEV(getInstructionByName(F, "yy")); // {(3 + %pp),+,1}auto *ScevXB = SE.getSCEV(getInstructionByName(F, "xb")); // {%pp,+,1}auto *ScevIVNext = SE.getSCEV(getInstructionByName(F, "iv.next")); // {1,+,1}auto diff = [&SE](const SCEV *LHS, const SCEV *RHS) -> Optional<int> {auto ConstantDiffOrNone = computeConstantDifference(SE, LHS, RHS);if (!ConstantDiffOrNone)return None;auto ExtDiff = ConstantDiffOrNone->getSExtValue();int Diff = ExtDiff;assert(Diff == ExtDiff && "Integer overflow");return Diff;};EXPECT_EQ(diff(ScevV3, ScevV0), 3);EXPECT_EQ(diff(ScevV0, ScevV3), -3);EXPECT_EQ(diff(ScevV0, ScevV0), 0);EXPECT_EQ(diff(ScevV3, ScevV3), 0);EXPECT_EQ(diff(ScevIV, ScevIV), 0);EXPECT_EQ(diff(ScevXA, ScevXB), 0);EXPECT_EQ(diff(ScevXA, ScevYY), -3);EXPECT_EQ(diff(ScevYY, ScevXB), 3);EXPECT_EQ(diff(ScevIV, ScevIVNext), -1);EXPECT_EQ(diff(ScevIVNext, ScevIV), 1);EXPECT_EQ(diff(ScevIVNext, ScevIVNext), 0);EXPECT_EQ(diff(ScevV0, ScevIV), None);EXPECT_EQ(diff(ScevIVNext, ScevV3), None);EXPECT_EQ(diff(ScevYY, ScevV3), None);});}TEST_F(ScalarEvolutionsTest, SCEVrewriteUnknowns) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 %i) { ""entry: "" %cmp3 = icmp ult i32 %i, 16 "" br i1 %cmp3, label %loop.body, label %exit ""loop.body: "" %iv = phi i32 [ %iv.next, %loop.body ], [ %i, %entry ] "" %iv.next = add nsw i32 %iv, 1 "" %cmp = icmp eq i32 %iv.next, 16 "" br i1 %cmp, label %exit, label %loop.body ""exit: "" ret void ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv")); // {0,+,1}auto *ScevI = SE.getSCEV(getArgByName(F, "i")); // {0,+,1}ValueToSCEVMapTy RewriteMap;RewriteMap[cast<SCEVUnknown>(ScevI)->getValue()] =SE.getUMinExpr(ScevI, SE.getConstant(ScevI->getType(), 17));auto *WithUMin = SCEVParameterRewriter::rewrite(ScevIV, SE, RewriteMap);EXPECT_NE(WithUMin, ScevIV);auto *AR = dyn_cast<SCEVAddRecExpr>(WithUMin);EXPECT_TRUE(AR);EXPECT_EQ(AR->getStart(),SE.getUMinExpr(ScevI, SE.getConstant(ScevI->getType(), 17)));EXPECT_EQ(AR->getStepRecurrence(SE),cast<SCEVAddRecExpr>(ScevIV)->getStepRecurrence(SE));});}TEST_F(ScalarEvolutionsTest, SCEVAddNUW) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 %x) { "" ret void ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *X = SE.getSCEV(getArgByName(F, "x"));auto *One = SE.getOne(X->getType());auto *Sum = SE.getAddExpr(X, One, SCEV::FlagNUW);EXPECT_TRUE(SE.isKnownPredicate(ICmpInst::ICMP_UGE, Sum, X));EXPECT_TRUE(SE.isKnownPredicate(ICmpInst::ICMP_UGT, Sum, X));});}TEST_F(ScalarEvolutionsTest, SCEVgetRanges) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 %i) { ""entry: "" br label %loop.body ""loop.body: "" %iv = phi i32 [ %iv.next, %loop.body ], [ 0, %entry ] "" %iv.next = add nsw i32 %iv, 1 "" %cmp = icmp eq i32 %iv.next, 16 "" br i1 %cmp, label %exit, label %loop.body ""exit: "" ret void ""} ",Err, C);runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv")); // {0,+,1}auto *ScevI = SE.getSCEV(getArgByName(F, "i"));EXPECT_EQ(SE.getUnsignedRange(ScevIV).getLower(), 0);EXPECT_EQ(SE.getUnsignedRange(ScevIV).getUpper(), 16);auto *Add = SE.getAddExpr(ScevI, ScevIV);ValueToSCEVMapTy RewriteMap;RewriteMap[cast<SCEVUnknown>(ScevI)->getValue()] =SE.getUMinExpr(ScevI, SE.getConstant(ScevI->getType(), 17));auto *AddWithUMin = SCEVParameterRewriter::rewrite(Add, SE, RewriteMap);EXPECT_EQ(SE.getUnsignedRange(AddWithUMin).getLower(), 0);EXPECT_EQ(SE.getUnsignedRange(AddWithUMin).getUpper(), 33);});}TEST_F(ScalarEvolutionsTest, SCEVgetExitLimitForGuardedLoop) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 %i) { ""entry: "" %cmp3 = icmp ult i32 %i, 16 "" br i1 %cmp3, label %loop.body, label %exit ""loop.body: "" %iv = phi i32 [ %iv.next, %loop.body ], [ %i, %entry ] "" %iv.next = add nsw i32 %iv, 1 "" %cmp = icmp eq i32 %iv.next, 16 "" br i1 %cmp, label %exit, label %loop.body ""exit: "" ret void ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv")); // {0,+,1}const Loop *L = cast<SCEVAddRecExpr>(ScevIV)->getLoop();const SCEV *BTC = SE.getBackedgeTakenCount(L);EXPECT_FALSE(isa<SCEVConstant>(BTC));const SCEV *MaxBTC = SE.getConstantMaxBackedgeTakenCount(L);EXPECT_EQ(cast<SCEVConstant>(MaxBTC)->getAPInt(), 15);});}TEST_F(ScalarEvolutionsTest, ImpliedViaAddRecStart) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32* %p) { ""entry: "" %x = load i32, i32* %p, !range !0 "" br label %loop ""loop: "" %iv = phi i32 [ %x, %entry], [%iv.next, %backedge] "" %ne.check = icmp ne i32 %iv, 0 "" br i1 %ne.check, label %backedge, label %exit ""backedge: "" %iv.next = add i32 %iv, -1 "" br label %loop ""exit:"" ret void ""} ""!0 = !{i32 0, i32 2147483647}",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *X = SE.getSCEV(getInstructionByName(F, "x"));auto *Context = getInstructionByName(F, "iv.next");EXPECT_TRUE(SE.isKnownPredicateAt(ICmpInst::ICMP_NE, X,SE.getZero(X->getType()), Context));});}TEST_F(ScalarEvolutionsTest, UnsignedIsImpliedViaOperations) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M =parseAssemblyString("define void @foo(i32* %p1, i32* %p2) { ""entry: "" %x = load i32, i32* %p1, !range !0 "" %cond = icmp ne i32 %x, 0 "" br i1 %cond, label %guarded, label %exit ""guarded: "" %y = add i32 %x, -1 "" ret void ""exit: "" ret void ""} ""!0 = !{i32 0, i32 2147483647}",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *X = SE.getSCEV(getInstructionByName(F, "x"));auto *Y = SE.getSCEV(getInstructionByName(F, "y"));auto *Guarded = getInstructionByName(F, "y")->getParent();ASSERT_TRUE(Guarded);EXPECT_TRUE(SE.isBasicBlockEntryGuardedByCond(Guarded, ICmpInst::ICMP_ULT, Y, X));EXPECT_TRUE(SE.isBasicBlockEntryGuardedByCond(Guarded, ICmpInst::ICMP_UGT, X, Y));});}TEST_F(ScalarEvolutionsTest, ProveImplicationViaNarrowing) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define i32 @foo(i32 %start, i32* %q) { ""entry: "" %wide.start = zext i32 %start to i64 "" br label %loop ""loop: "" %wide.iv = phi i64 [%wide.start, %entry], [%wide.iv.next, %backedge] "" %iv = phi i32 [%start, %entry], [%iv.next, %backedge] "" %cond = icmp eq i64 %wide.iv, 0 "" br i1 %cond, label %exit, label %backedge ""backedge: "" %iv.next = add i32 %iv, -1 "" %index = zext i32 %iv.next to i64 "" %load.addr = getelementptr i32, i32* %q, i64 %index "" %stop = load i32, i32* %load.addr "" %loop.cond = icmp eq i32 %stop, 0 "" %wide.iv.next = add nsw i64 %wide.iv, -1 "" br i1 %loop.cond, label %loop, label %failure ""exit: "" ret i32 0 ""failure: "" unreachable ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *IV = SE.getSCEV(getInstructionByName(F, "iv"));auto *Zero = SE.getZero(IV->getType());auto *Backedge = getInstructionByName(F, "iv.next")->getParent();ASSERT_TRUE(Backedge);(void)IV;(void)Zero;// FIXME: This can only be proved with turned on option// scalar-evolution-use-expensive-range-sharpening which is currently off.// Enable the check once it's switched true by default.// EXPECT_TRUE(SE.isBasicBlockEntryGuardedByCond(Backedge,// ICmpInst::ICMP_UGT,// IV, Zero));});}TEST_F(ScalarEvolutionsTest, ImpliedCond) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 %len) { ""entry: "" br label %loop ""loop: "" %iv = phi i32 [ 0, %entry], [%iv.next, %loop] "" %iv.next = add nsw i32 %iv, 1 "" %cmp = icmp slt i32 %iv, %len "" br i1 %cmp, label %loop, label %exit ""exit:"" ret void ""}",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Instruction *IV = getInstructionByName(F, "iv");Type *Ty = IV->getType();const SCEV *Zero = SE.getZero(Ty);const SCEV *MinusOne = SE.getMinusOne(Ty);// {0,+,1}<nuw><nsw>const SCEV *AddRec_0_1 = SE.getSCEV(IV);// {0,+,-1}<nw>const SCEV *AddRec_0_N1 = SE.getNegativeSCEV(AddRec_0_1);// {0,+,1}<nuw><nsw> > 0 -> {0,+,-1}<nw> < 0EXPECT_TRUE(isImpliedCond(SE, ICmpInst::ICMP_SLT, AddRec_0_N1, Zero,ICmpInst::ICMP_SGT, AddRec_0_1, Zero));// {0,+,-1}<nw> < -1 -> {0,+,1}<nuw><nsw> > 0EXPECT_TRUE(isImpliedCond(SE, ICmpInst::ICMP_SGT, AddRec_0_1, Zero,ICmpInst::ICMP_SLT, AddRec_0_N1, MinusOne));});}TEST_F(ScalarEvolutionsTest, MatchURem) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "" ""define void @test(i32 %a, i32 %b, i16 %c, i64 %d) {""entry: "" %rem1 = urem i32 %a, 2"" %rem2 = urem i32 %a, 5"" %rem3 = urem i32 %a, %b"" %c.ext = zext i16 %c to i32"" %rem4 = urem i32 %c.ext, 2"" %ext = zext i32 %rem4 to i64"" %rem5 = urem i64 %d, 17179869184"" ret void ""} ",Err, C);assert(M && "Could not parse module?");assert(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {for (auto *N : {"rem1", "rem2", "rem3", "rem5"}) {auto *URemI = getInstructionByName(F, N);auto *S = SE.getSCEV(URemI);const SCEV *LHS, *RHS;EXPECT_TRUE(matchURem(SE, S, LHS, RHS));EXPECT_EQ(LHS, SE.getSCEV(URemI->getOperand(0)));EXPECT_EQ(RHS, SE.getSCEV(URemI->getOperand(1)));EXPECT_EQ(LHS->getType(), S->getType());EXPECT_EQ(RHS->getType(), S->getType());}// Check the case where the urem operand is zero-extended. Make sure the// match results are extended to the size of the input expression.auto *Ext = getInstructionByName(F, "ext");auto *URem1 = getInstructionByName(F, "rem4");auto *S = SE.getSCEV(Ext);const SCEV *LHS, *RHS;EXPECT_TRUE(matchURem(SE, S, LHS, RHS));EXPECT_NE(LHS, SE.getSCEV(URem1->getOperand(0)));// RHS and URem1->getOperand(1) have different widths, so compare the// integer values.EXPECT_EQ(cast<SCEVConstant>(RHS)->getValue()->getZExtValue(),cast<SCEVConstant>(SE.getSCEV(URem1->getOperand(1)))->getValue()->getZExtValue());EXPECT_EQ(LHS->getType(), S->getType());EXPECT_EQ(RHS->getType(), S->getType());});}TEST_F(ScalarEvolutionsTest, SCEVUDivFloorCeiling) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo() { "" ret void ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {// Check that SCEV's udiv and uceil handling produce the correct results// for all 8 bit options. Div-by-zero is deliberately excluded.for (unsigned N = 0; N < 256; N++)for (unsigned D = 1; D < 256; D++) {APInt NInt(8, N);APInt DInt(8, D);using namespace llvm::APIntOps;APInt FloorInt = RoundingUDiv(NInt, DInt, APInt::Rounding::DOWN);APInt CeilingInt = RoundingUDiv(NInt, DInt, APInt::Rounding::UP);auto *NS = SE.getConstant(NInt);auto *DS = SE.getConstant(DInt);auto *FloorS = cast<SCEVConstant>(SE.getUDivExpr(NS, DS));auto *CeilingS = cast<SCEVConstant>(SE.getUDivCeilSCEV(NS, DS));ASSERT_TRUE(FloorS->getAPInt() == FloorInt);ASSERT_TRUE(CeilingS->getAPInt() == CeilingInt);}});}TEST_F(ScalarEvolutionsTest, ComputeMaxTripCountFromArrayNormal) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 signext %len) { ""entry: "" %a = alloca [7 x i32], align 4 "" %cmp4 = icmp sgt i32 %len, 0 "" br i1 %cmp4, label %for.body.preheader, label %for.cond.cleanup ""for.body.preheader: "" br label %for.body ""for.cond.cleanup.loopexit: "" br label %for.cond.cleanup ""for.cond.cleanup: "" ret void ""for.body: "" %iv = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] "" %idxprom = zext i32 %iv to i64 "" %arrayidx = getelementptr inbounds [7 x i32], [7 x i32]* %a, i64 0, \i64 %idxprom "" store i32 0, i32* %arrayidx, align 4 "" %inc = add nuw nsw i32 %iv, 1 "" %cmp = icmp slt i32 %inc, %len "" br i1 %cmp, label %for.body, label %for.cond.cleanup.loopexit ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv"));const Loop *L = cast<SCEVAddRecExpr>(ScevIV)->getLoop();const SCEV *ITC = SE.getConstantMaxTripCountFromArray(L);EXPECT_FALSE(isa<SCEVCouldNotCompute>(ITC));EXPECT_TRUE(isa<SCEVConstant>(ITC));EXPECT_EQ(cast<SCEVConstant>(ITC)->getAPInt().getSExtValue(), 8);});}TEST_F(ScalarEvolutionsTest, ComputeMaxTripCountFromZeroArray) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 signext %len) { ""entry: "" %a = alloca [0 x i32], align 4 "" %cmp4 = icmp sgt i32 %len, 0 "" br i1 %cmp4, label %for.body.preheader, label %for.cond.cleanup ""for.body.preheader: "" br label %for.body ""for.cond.cleanup.loopexit: "" br label %for.cond.cleanup ""for.cond.cleanup: "" ret void ""for.body: "" %iv = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] "" %idxprom = zext i32 %iv to i64 "" %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* %a, i64 0, \i64 %idxprom "" store i32 0, i32* %arrayidx, align 4 "" %inc = add nuw nsw i32 %iv, 1 "" %cmp = icmp slt i32 %inc, %len "" br i1 %cmp, label %for.body, label %for.cond.cleanup.loopexit ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv"));const Loop *L = cast<SCEVAddRecExpr>(ScevIV)->getLoop();const SCEV *ITC = SE.getConstantMaxTripCountFromArray(L);EXPECT_FALSE(isa<SCEVCouldNotCompute>(ITC));EXPECT_TRUE(isa<SCEVConstant>(ITC));EXPECT_EQ(cast<SCEVConstant>(ITC)->getAPInt().getSExtValue(), 1);});}TEST_F(ScalarEvolutionsTest, ComputeMaxTripCountFromExtremArray) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 signext %len) { ""entry: "" %a = alloca [4294967295 x i1], align 4 "" %cmp4 = icmp sgt i32 %len, 0 "" br i1 %cmp4, label %for.body.preheader, label %for.cond.cleanup ""for.body.preheader: "" br label %for.body ""for.cond.cleanup.loopexit: "" br label %for.cond.cleanup ""for.cond.cleanup: "" ret void ""for.body: "" %iv = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] "" %idxprom = zext i32 %iv to i64 "" %arrayidx = getelementptr inbounds [4294967295 x i1], \[4294967295 x i1]* %a, i64 0, i64 %idxprom "" store i1 0, i1* %arrayidx, align 4 "" %inc = add nuw nsw i32 %iv, 1 "" %cmp = icmp slt i32 %inc, %len "" br i1 %cmp, label %for.body, label %for.cond.cleanup.loopexit ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv"));const Loop *L = cast<SCEVAddRecExpr>(ScevIV)->getLoop();const SCEV *ITC = SE.getConstantMaxTripCountFromArray(L);EXPECT_TRUE(isa<SCEVCouldNotCompute>(ITC));});}TEST_F(ScalarEvolutionsTest, ComputeMaxTripCountFromArrayInBranch) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 signext %len) { ""entry: "" %a = alloca [8 x i32], align 4 "" br label %for.cond ""for.cond: "" %iv = phi i32 [ %inc, %for.inc ], [ 0, %entry ] "" %cmp = icmp slt i32 %iv, %len "" br i1 %cmp, label %for.body, label %for.cond.cleanup ""for.cond.cleanup: "" br label %for.end ""for.body: "" %cmp1 = icmp slt i32 %iv, 8 "" br i1 %cmp1, label %if.then, label %if.end ""if.then: "" %idxprom = sext i32 %iv to i64 "" %arrayidx = getelementptr inbounds [8 x i32], [8 x i32]* %a, i64 0, \i64 %idxprom "" store i32 0, i32* %arrayidx, align 4 "" br label %if.end ""if.end: "" br label %for.inc ""for.inc: "" %inc = add nsw i32 %iv, 1 "" br label %for.cond ""for.end: "" ret void ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv"));const Loop *L = cast<SCEVAddRecExpr>(ScevIV)->getLoop();const SCEV *ITC = SE.getConstantMaxTripCountFromArray(L);EXPECT_TRUE(isa<SCEVCouldNotCompute>(ITC));});}TEST_F(ScalarEvolutionsTest, ComputeMaxTripCountFromMultiDemArray) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("define void @foo(i32 signext %len) { ""entry: "" %a = alloca [3 x [5 x i32]], align 4 "" br label %for.cond ""for.cond: "" %iv = phi i32 [ %inc, %for.inc ], [ 0, %entry ] "" %cmp = icmp slt i32 %iv, %len "" br i1 %cmp, label %for.body, label %for.cond.cleanup ""for.cond.cleanup: "" br label %for.end ""for.body: "" %arrayidx = getelementptr inbounds [3 x [5 x i32]], \[3 x [5 x i32]]* %a, i64 0, i64 3 "" %idxprom = sext i32 %iv to i64 "" %arrayidx1 = getelementptr inbounds [5 x i32], [5 x i32]* %arrayidx, \i64 0, i64 %idxprom "" store i32 0, i32* %arrayidx1, align 4"" br label %for.inc ""for.inc: "" %inc = add nsw i32 %iv, 1 "" br label %for.cond ""for.end: "" ret void ""} ",Err, C);ASSERT_TRUE(M && "Could not parse module?");ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv"));const Loop *L = cast<SCEVAddRecExpr>(ScevIV)->getLoop();const SCEV *ITC = SE.getConstantMaxTripCountFromArray(L);EXPECT_TRUE(isa<SCEVCouldNotCompute>(ITC));});}} // end namespace llvm
//===- ProfileSummaryInfoTest.cpp - ProfileSummaryInfo unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/ProfileSummaryInfo.h"#include "llvm/Analysis/BlockFrequencyInfo.h"#include "llvm/Analysis/BranchProbabilityInfo.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/MDBuilder.h"#include "llvm/IR/Module.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/DataTypes.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"extern llvm::cl::opt<bool> ScalePartialSampleProfileWorkingSetSize;namespace llvm {namespace {class ProfileSummaryInfoTest : public testing::Test {protected:LLVMContext C;std::unique_ptr<BranchProbabilityInfo> BPI;std::unique_ptr<DominatorTree> DT;std::unique_ptr<LoopInfo> LI;ProfileSummaryInfo buildPSI(Module *M) {return ProfileSummaryInfo(*M);}BlockFrequencyInfo buildBFI(Function &F) {DT.reset(new DominatorTree(F));LI.reset(new LoopInfo(*DT));BPI.reset(new BranchProbabilityInfo(F, *LI));return BlockFrequencyInfo(F, *BPI, *LI);}std::unique_ptr<Module> makeLLVMModule(const char *ProfKind = nullptr,uint64_t NumCounts = 3,uint64_t IsPartialProfile = 0,double PartialProfileRatio = 0.0,uint64_t HotNumCounts = 3,uint64_t ColdNumCounts = 10) {const char *ModuleString ="define i32 @g(i32 %x) !prof !21 {{\n"" ret i32 0\n""}\n""define i32 @h(i32 %x) !prof !22 {{\n"" ret i32 0\n""}\n""define i32 @f(i32 %x) !prof !20 {{\n""bb0:\n"" %y1 = icmp eq i32 %x, 0 \n"" br i1 %y1, label %bb1, label %bb2, !prof !23 \n""bb1:\n"" %z1 = call i32 @g(i32 %x)\n"" br label %bb3\n""bb2:\n"" %z2 = call i32 @h(i32 %x)\n"" br label %bb3\n""bb3:\n"" %y2 = phi i32 [0, %bb1], [1, %bb2] \n"" ret i32 %y2\n""}\n""define i32 @l(i32 %x) {{\n""bb0:\n"" %y1 = icmp eq i32 %x, 0 \n"" br i1 %y1, label %bb1, label %bb2, !prof !23 \n""bb1:\n"" %z1 = call i32 @g(i32 %x)\n"" br label %bb3\n""bb2:\n"" %z2 = call i32 @h(i32 %x)\n"" br label %bb3\n""bb3:\n"" %y2 = phi i32 [0, %bb1], [1, %bb2] \n"" ret i32 %y2\n""}\n""!20 = !{{!\"function_entry_count\", i64 400}\n""!21 = !{{!\"function_entry_count\", i64 1}\n""!22 = !{{!\"function_entry_count\", i64 100}\n""!23 = !{{!\"branch_weights\", i32 64, i32 4}\n""{0}";const char *SummaryString ="!llvm.module.flags = !{{!1}\n""!1 = !{{i32 1, !\"ProfileSummary\", !2}\n""!2 = !{{!3, !4, !5, !6, !7, !8, !9, !10, !11, !12}\n""!3 = !{{!\"ProfileFormat\", !\"{0}\"}\n""!4 = !{{!\"TotalCount\", i64 10000}\n""!5 = !{{!\"MaxCount\", i64 10}\n""!6 = !{{!\"MaxInternalCount\", i64 1}\n""!7 = !{{!\"MaxFunctionCount\", i64 1000}\n""!8 = !{{!\"NumCounts\", i64 {1}}\n""!9 = !{{!\"NumFunctions\", i64 3}\n""!10 = !{{!\"IsPartialProfile\", i64 {2}}\n""!11 = !{{!\"PartialProfileRatio\", double {3}}\n""!12 = !{{!\"DetailedSummary\", !13}\n""!13 = !{{!14, !15, !16}\n""!14 = !{{i32 10000, i64 1000, i32 1}\n""!15 = !{{i32 990000, i64 300, i32 {4}}\n""!16 = !{{i32 999999, i64 5, i32 {5}}\n";SMDiagnostic Err;if (ProfKind) {auto Summary =formatv(SummaryString, ProfKind, NumCounts, IsPartialProfile,PartialProfileRatio, HotNumCounts, ColdNumCounts).str();return parseAssemblyString(formatv(ModuleString, Summary).str(), Err, C);} elsereturn parseAssemblyString(formatv(ModuleString, "").str(), Err, C);}};TEST_F(ProfileSummaryInfoTest, TestNoProfile) {auto M = makeLLVMModule(/*ProfKind=*/nullptr);Function *F = M->getFunction("f");ProfileSummaryInfo PSI = buildPSI(M.get());EXPECT_FALSE(PSI.hasProfileSummary());EXPECT_FALSE(PSI.hasSampleProfile());EXPECT_FALSE(PSI.hasInstrumentationProfile());// In the absence of profiles, is{Hot|Cold}X methods should always return// false.EXPECT_FALSE(PSI.isHotCount(1000));EXPECT_FALSE(PSI.isHotCount(0));EXPECT_FALSE(PSI.isColdCount(1000));EXPECT_FALSE(PSI.isColdCount(0));EXPECT_FALSE(PSI.isFunctionEntryHot(F));EXPECT_FALSE(PSI.isFunctionEntryCold(F));BasicBlock &BB0 = F->getEntryBlock();BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);BlockFrequencyInfo BFI = buildBFI(*F);EXPECT_FALSE(PSI.isHotBlock(&BB0, &BFI));EXPECT_FALSE(PSI.isColdBlock(&BB0, &BFI));CallBase &CS1 = cast<CallBase>(*BB1->getFirstNonPHI());EXPECT_FALSE(PSI.isHotCallSite(CS1, &BFI));EXPECT_FALSE(PSI.isColdCallSite(CS1, &BFI));}TEST_F(ProfileSummaryInfoTest, TestCommon) {auto M = makeLLVMModule("InstrProf");Function *F = M->getFunction("f");Function *G = M->getFunction("g");Function *H = M->getFunction("h");ProfileSummaryInfo PSI = buildPSI(M.get());EXPECT_TRUE(PSI.hasProfileSummary());EXPECT_TRUE(PSI.isHotCount(400));EXPECT_TRUE(PSI.isColdCount(2));EXPECT_FALSE(PSI.isColdCount(100));EXPECT_FALSE(PSI.isHotCount(100));EXPECT_TRUE(PSI.isHotCountNthPercentile(990000, 400));EXPECT_FALSE(PSI.isHotCountNthPercentile(990000, 100));EXPECT_FALSE(PSI.isHotCountNthPercentile(990000, 2));EXPECT_FALSE(PSI.isColdCountNthPercentile(990000, 400));EXPECT_TRUE(PSI.isColdCountNthPercentile(990000, 100));EXPECT_TRUE(PSI.isColdCountNthPercentile(990000, 2));EXPECT_TRUE(PSI.isHotCountNthPercentile(999999, 400));EXPECT_TRUE(PSI.isHotCountNthPercentile(999999, 100));EXPECT_FALSE(PSI.isHotCountNthPercentile(999999, 2));EXPECT_FALSE(PSI.isColdCountNthPercentile(999999, 400));EXPECT_FALSE(PSI.isColdCountNthPercentile(999999, 100));EXPECT_TRUE(PSI.isColdCountNthPercentile(999999, 2));EXPECT_FALSE(PSI.isHotCountNthPercentile(10000, 400));EXPECT_FALSE(PSI.isHotCountNthPercentile(10000, 100));EXPECT_FALSE(PSI.isHotCountNthPercentile(10000, 2));EXPECT_TRUE(PSI.isColdCountNthPercentile(10000, 400));EXPECT_TRUE(PSI.isColdCountNthPercentile(10000, 100));EXPECT_TRUE(PSI.isColdCountNthPercentile(10000, 2));EXPECT_TRUE(PSI.isFunctionEntryHot(F));EXPECT_FALSE(PSI.isFunctionEntryHot(G));EXPECT_FALSE(PSI.isFunctionEntryHot(H));}TEST_F(ProfileSummaryInfoTest, InstrProf) {auto M = makeLLVMModule("InstrProf");Function *F = M->getFunction("f");ProfileSummaryInfo PSI = buildPSI(M.get());EXPECT_TRUE(PSI.hasProfileSummary());EXPECT_TRUE(PSI.hasInstrumentationProfile());BasicBlock &BB0 = F->getEntryBlock();BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1);BasicBlock *BB3 = BB1->getSingleSuccessor();BlockFrequencyInfo BFI = buildBFI(*F);EXPECT_TRUE(PSI.isHotBlock(&BB0, &BFI));EXPECT_TRUE(PSI.isHotBlock(BB1, &BFI));EXPECT_FALSE(PSI.isHotBlock(BB2, &BFI));EXPECT_TRUE(PSI.isHotBlock(BB3, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(990000, &BB0, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(990000, BB1, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB2, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(990000, BB3, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, &BB0, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB1, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(990000, BB2, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB3, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, &BB0, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, BB1, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, BB2, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, BB3, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, &BB0, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, BB1, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, BB2, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, BB3, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, &BB0, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, BB1, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, BB2, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, BB3, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, &BB0, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, BB1, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, BB2, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, BB3, &BFI));CallBase &CS1 = cast<CallBase>(*BB1->getFirstNonPHI());auto *CI2 = BB2->getFirstNonPHI();CallBase &CS2 = cast<CallBase>(*CI2);EXPECT_TRUE(PSI.isHotCallSite(CS1, &BFI));EXPECT_FALSE(PSI.isHotCallSite(CS2, &BFI));// Test that adding an MD_prof metadata with a hot count on CS2 does not// change its hotness as it has no effect in instrumented profiling.MDBuilder MDB(M->getContext());CI2->setMetadata(llvm::LLVMContext::MD_prof, MDB.createBranchWeights({400}));EXPECT_FALSE(PSI.isHotCallSite(CS2, &BFI));EXPECT_TRUE(PSI.isFunctionHotInCallGraphNthPercentile(990000, F, BFI));EXPECT_FALSE(PSI.isFunctionColdInCallGraphNthPercentile(990000, F, BFI));EXPECT_FALSE(PSI.isFunctionHotInCallGraphNthPercentile(10000, F, BFI));EXPECT_TRUE(PSI.isFunctionColdInCallGraphNthPercentile(10000, F, BFI));}TEST_F(ProfileSummaryInfoTest, InstrProfNoFuncEntryCount) {auto M = makeLLVMModule("InstrProf");Function *F = M->getFunction("l");ProfileSummaryInfo PSI = buildPSI(M.get());EXPECT_TRUE(PSI.hasProfileSummary());EXPECT_TRUE(PSI.hasInstrumentationProfile());BasicBlock &BB0 = F->getEntryBlock();BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1);BasicBlock *BB3 = BB1->getSingleSuccessor();BlockFrequencyInfo BFI = buildBFI(*F);// Without the entry count, all should return false.EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, &BB0, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB1, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB2, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB3, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, &BB0, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB1, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB2, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB3, &BFI));EXPECT_FALSE(PSI.isFunctionHotInCallGraphNthPercentile(990000, F, BFI));EXPECT_FALSE(PSI.isFunctionColdInCallGraphNthPercentile(990000, F, BFI));}TEST_F(ProfileSummaryInfoTest, SampleProf) {auto M = makeLLVMModule("SampleProfile");Function *F = M->getFunction("f");ProfileSummaryInfo PSI = buildPSI(M.get());EXPECT_TRUE(PSI.hasProfileSummary());EXPECT_TRUE(PSI.hasSampleProfile());EXPECT_FALSE(PSI.hasPartialSampleProfile());BasicBlock &BB0 = F->getEntryBlock();BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1);BasicBlock *BB3 = BB1->getSingleSuccessor();BlockFrequencyInfo BFI = buildBFI(*F);EXPECT_TRUE(PSI.isHotBlock(&BB0, &BFI));EXPECT_TRUE(PSI.isHotBlock(BB1, &BFI));EXPECT_FALSE(PSI.isHotBlock(BB2, &BFI));EXPECT_TRUE(PSI.isHotBlock(BB3, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(990000, &BB0, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(990000, BB1, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB2, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(990000, BB3, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, &BB0, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB1, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(990000, BB2, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB3, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, &BB0, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, BB1, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, BB2, &BFI));EXPECT_TRUE(PSI.isHotBlockNthPercentile(999900, BB3, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, &BB0, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, BB1, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, BB2, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(999900, BB3, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, &BB0, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, BB1, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, BB2, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(10000, BB3, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, &BB0, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, BB1, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, BB2, &BFI));EXPECT_TRUE(PSI.isColdBlockNthPercentile(10000, BB3, &BFI));CallBase &CS1 = cast<CallBase>(*BB1->getFirstNonPHI());auto *CI2 = BB2->getFirstNonPHI();// Manually attach branch weights metadata to the call instruction.SmallVector<uint32_t, 1> Weights;Weights.push_back(1000);MDBuilder MDB(M->getContext());CI2->setMetadata(LLVMContext::MD_prof, MDB.createBranchWeights(Weights));CallBase &CS2 = cast<CallBase>(*CI2);EXPECT_FALSE(PSI.isHotCallSite(CS1, &BFI));EXPECT_TRUE(PSI.isHotCallSite(CS2, &BFI));// Test that CS2 is considered hot when it gets an MD_prof metadata with// weights that exceed the hot count threshold.CI2->setMetadata(llvm::LLVMContext::MD_prof, MDB.createBranchWeights({400}));EXPECT_TRUE(PSI.isHotCallSite(CS2, &BFI));EXPECT_TRUE(PSI.isFunctionHotInCallGraphNthPercentile(990000, F, BFI));EXPECT_FALSE(PSI.isFunctionColdInCallGraphNthPercentile(990000, F, BFI));EXPECT_FALSE(PSI.isFunctionHotInCallGraphNthPercentile(10000, F, BFI));EXPECT_TRUE(PSI.isFunctionColdInCallGraphNthPercentile(10000, F, BFI));}TEST_F(ProfileSummaryInfoTest, SampleProfNoFuncEntryCount) {auto M = makeLLVMModule("SampleProfile");Function *F = M->getFunction("l");ProfileSummaryInfo PSI = buildPSI(M.get());EXPECT_TRUE(PSI.hasProfileSummary());EXPECT_TRUE(PSI.hasSampleProfile());BasicBlock &BB0 = F->getEntryBlock();BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1);BasicBlock *BB3 = BB1->getSingleSuccessor();BlockFrequencyInfo BFI = buildBFI(*F);// Without the entry count, all should return false.EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, &BB0, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB1, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB2, &BFI));EXPECT_FALSE(PSI.isHotBlockNthPercentile(990000, BB3, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, &BB0, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB1, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB2, &BFI));EXPECT_FALSE(PSI.isColdBlockNthPercentile(990000, BB3, &BFI));EXPECT_FALSE(PSI.isFunctionHotInCallGraphNthPercentile(990000, F, BFI));EXPECT_FALSE(PSI.isFunctionColdInCallGraphNthPercentile(990000, F, BFI));}TEST_F(ProfileSummaryInfoTest, PartialSampleProfWorkingSetSize) {ScalePartialSampleProfileWorkingSetSize.setValue(true);// With PartialProfileRatio unset (zero.)auto M1 = makeLLVMModule("SampleProfile", /*NumCounts*/ 3,/*IsPartialProfile*/ 1,/*PartialProfileRatio*/ 0.0,/*HotNumCounts*/ 3, /*ColdNumCounts*/ 10);ProfileSummaryInfo PSI1 = buildPSI(M1.get());EXPECT_TRUE(PSI1.hasProfileSummary());EXPECT_TRUE(PSI1.hasSampleProfile());EXPECT_TRUE(PSI1.hasPartialSampleProfile());EXPECT_FALSE(PSI1.hasHugeWorkingSetSize());EXPECT_FALSE(PSI1.hasLargeWorkingSetSize());// With PartialProfileRatio set (non-zero) and a small working set size.auto M2 = makeLLVMModule("SampleProfile", /*NumCounts*/ 27493235,/*IsPartialProfile*/ 1,/*PartialProfileRatio*/ 0.00000012,/*HotNumCounts*/ 3102082,/*ColdNumCounts*/ 18306149);ProfileSummaryInfo PSI2 = buildPSI(M2.get());EXPECT_TRUE(PSI2.hasProfileSummary());EXPECT_TRUE(PSI2.hasSampleProfile());EXPECT_TRUE(PSI2.hasPartialSampleProfile());EXPECT_FALSE(PSI2.hasHugeWorkingSetSize());EXPECT_FALSE(PSI2.hasLargeWorkingSetSize());// With PartialProfileRatio is set (non-zero) and a large working set size.auto M3 = makeLLVMModule("SampleProfile", /*NumCounts*/ 27493235,/*IsPartialProfile*/ 1,/*PartialProfileRatio*/ 0.9,/*HotNumCounts*/ 3102082,/*ColdNumCounts*/ 18306149);ProfileSummaryInfo PSI3 = buildPSI(M3.get());EXPECT_TRUE(PSI3.hasProfileSummary());EXPECT_TRUE(PSI3.hasSampleProfile());EXPECT_TRUE(PSI3.hasPartialSampleProfile());EXPECT_TRUE(PSI3.hasHugeWorkingSetSize());EXPECT_TRUE(PSI3.hasLargeWorkingSetSize());}} // end anonymous namespace} // end namespace llvm
//===- PhiValuesTest.cpp - PhiValues unit tests ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/PhiValues.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Function.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/Module.h"#include "llvm/IR/Type.h"#include "gtest/gtest.h"using namespace llvm;TEST(PhiValuesTest, SimplePhi) {LLVMContext C;Module M("PhiValuesTest", C);Type *VoidTy = Type::getVoidTy(C);Type *I1Ty = Type::getInt1Ty(C);Type *I32Ty = Type::getInt32Ty(C);Type *I32PtrTy = Type::getInt32PtrTy(C);// Create a function with phis that do not have other phis as incoming valuesFunction *F = Function::Create(FunctionType::get(VoidTy, false),Function::ExternalLinkage, "f", M);BasicBlock *Entry = BasicBlock::Create(C, "entry", F);BasicBlock *If = BasicBlock::Create(C, "if", F);BasicBlock *Else = BasicBlock::Create(C, "else", F);BasicBlock *Then = BasicBlock::Create(C, "then", F);BranchInst::Create(If, Else, UndefValue::get(I1Ty), Entry);BranchInst::Create(Then, If);BranchInst::Create(Then, Else);Value *Val1 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val1", Entry);Value *Val2 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val2", Entry);Value *Val3 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val3", Entry);Value *Val4 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val4", Entry);PHINode *Phi1 = PHINode::Create(I32Ty, 2, "phi1", Then);Phi1->addIncoming(Val1, If);Phi1->addIncoming(Val2, Else);PHINode *Phi2 = PHINode::Create(I32Ty, 2, "phi2", Then);Phi2->addIncoming(Val1, If);Phi2->addIncoming(Val3, Else);PhiValues PV(*F);PhiValues::ValueSet Vals;// Check that simple usage worksVals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val2));Vals = PV.getValuesForPhi(Phi2);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val3));// Check that values are updated when one value is replaced with anotherVal1->replaceAllUsesWith(Val4);PV.invalidateValue(Val1);Vals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val4));EXPECT_TRUE(Vals.count(Val2));Vals = PV.getValuesForPhi(Phi2);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val4));EXPECT_TRUE(Vals.count(Val3));// Check that setting in incoming value directly updates the valuesPhi1->setIncomingValue(0, Val1);PV.invalidateValue(Phi1);Vals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val2));}TEST(PhiValuesTest, DependentPhi) {LLVMContext C;Module M("PhiValuesTest", C);Type *VoidTy = Type::getVoidTy(C);Type *I1Ty = Type::getInt1Ty(C);Type *I32Ty = Type::getInt32Ty(C);Type *I32PtrTy = Type::getInt32PtrTy(C);// Create a function with a phi that has another phi as an incoming valueFunction *F = Function::Create(FunctionType::get(VoidTy, false),Function::ExternalLinkage, "f", M);BasicBlock *Entry = BasicBlock::Create(C, "entry", F);BasicBlock *If1 = BasicBlock::Create(C, "if1", F);BasicBlock *Else1 = BasicBlock::Create(C, "else1", F);BasicBlock *Then = BasicBlock::Create(C, "then", F);BasicBlock *If2 = BasicBlock::Create(C, "if2", F);BasicBlock *Else2 = BasicBlock::Create(C, "else2", F);BasicBlock *End = BasicBlock::Create(C, "then", F);BranchInst::Create(If1, Else1, UndefValue::get(I1Ty), Entry);BranchInst::Create(Then, If1);BranchInst::Create(Then, Else1);BranchInst::Create(If2, Else2, UndefValue::get(I1Ty), Then);BranchInst::Create(End, If2);BranchInst::Create(End, Else2);Value *Val1 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val1", Entry);Value *Val2 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val2", Entry);Value *Val3 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val3", Entry);Value *Val4 = new LoadInst(I32Ty, UndefValue::get(I32PtrTy), "val4", Entry);PHINode *Phi1 = PHINode::Create(I32Ty, 2, "phi1", Then);Phi1->addIncoming(Val1, If1);Phi1->addIncoming(Val2, Else1);PHINode *Phi2 = PHINode::Create(I32Ty, 2, "phi2", Then);Phi2->addIncoming(Val2, If1);Phi2->addIncoming(Val3, Else1);PHINode *Phi3 = PHINode::Create(I32Ty, 2, "phi3", End);Phi3->addIncoming(Phi1, If2);Phi3->addIncoming(Val3, Else2);PhiValues PV(*F);PhiValues::ValueSet Vals;// Check that simple usage worksVals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val2));Vals = PV.getValuesForPhi(Phi2);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val2));EXPECT_TRUE(Vals.count(Val3));Vals = PV.getValuesForPhi(Phi3);EXPECT_EQ(Vals.size(), 3u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val2));EXPECT_TRUE(Vals.count(Val3));// Check that changing an incoming value in the dependent phi changes the depending phiPhi1->setIncomingValue(0, Val4);PV.invalidateValue(Phi1);Vals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val4));EXPECT_TRUE(Vals.count(Val2));Vals = PV.getValuesForPhi(Phi2);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val2));EXPECT_TRUE(Vals.count(Val3));Vals = PV.getValuesForPhi(Phi3);EXPECT_EQ(Vals.size(), 3u);EXPECT_TRUE(Vals.count(Val4));EXPECT_TRUE(Vals.count(Val2));EXPECT_TRUE(Vals.count(Val3));// Check that replacing an incoming phi with a value worksPhi3->setIncomingValue(0, Val1);PV.invalidateValue(Phi3);Vals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val4));EXPECT_TRUE(Vals.count(Val2));Vals = PV.getValuesForPhi(Phi2);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val2));EXPECT_TRUE(Vals.count(Val3));Vals = PV.getValuesForPhi(Phi3);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val3));// Check that adding a phi as an incoming value worksPhi3->setIncomingValue(1, Phi2);PV.invalidateValue(Phi3);Vals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val4));EXPECT_TRUE(Vals.count(Val2));Vals = PV.getValuesForPhi(Phi2);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val2));EXPECT_TRUE(Vals.count(Val3));Vals = PV.getValuesForPhi(Phi3);EXPECT_EQ(Vals.size(), 3u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val2));EXPECT_TRUE(Vals.count(Val3));// Check that replacing an incoming phi then deleting it worksPhi3->setIncomingValue(1, Val2);PV.invalidateValue(Phi2);Phi2->eraseFromParent();PV.invalidateValue(Phi3);Vals = PV.getValuesForPhi(Phi1);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val4));EXPECT_TRUE(Vals.count(Val2));Vals = PV.getValuesForPhi(Phi3);EXPECT_EQ(Vals.size(), 2u);EXPECT_TRUE(Vals.count(Val1));EXPECT_TRUE(Vals.count(Val2));}
//===- MemorySSA.cpp - Unit tests for MemorySSA ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/MemorySSA.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/BasicAliasAnalysis.h"#include "llvm/Analysis/MemorySSAUpdater.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/DataLayout.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;const static char DLString[] = "e-i64:64-f80:128-n8:16:32:64-S128";/// There's a lot of common setup between these tests. This fixture helps reduce/// that. Tests should mock up a function, store it in F, and then call/// setupAnalyses().class MemorySSATest : public testing::Test {protected:// N.B. Many of these members depend on each other (e.g. the Module depends on// the Context, etc.). So, order matters here (and in TestAnalyses).LLVMContext C;Module M;IRBuilder<> B;DataLayout DL;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;Function *F;// Things that we need to build after the function is created.struct TestAnalyses {DominatorTree DT;AssumptionCache AC;AAResults AA;BasicAAResult BAA;// We need to defer MSSA construction until AA is *entirely* set up, which// requires calling addAAResult. Hence, we just use a pointer here.std::unique_ptr<MemorySSA> MSSA;MemorySSAWalker *Walker;TestAnalyses(MemorySSATest &Test): DT(*Test.F), AC(*Test.F), AA(Test.TLI),BAA(Test.DL, *Test.F, Test.TLI, AC, &DT) {AA.addAAResult(BAA);MSSA = std::make_unique<MemorySSA>(*Test.F, &AA, &DT);Walker = MSSA->getWalker();}};std::unique_ptr<TestAnalyses> Analyses;void setupAnalyses() {assert(F);Analyses.reset(new TestAnalyses(*this));}public:MemorySSATest(): M("MemorySSATest", C), B(C), DL(DLString), TLI(TLII), F(nullptr) {}};TEST_F(MemorySSATest, CreateALoad) {// We create a diamond where there is a store on one side, and then after// building MemorySSA, create a load after the merge point, and use it to test// updating by creating an access for the load.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left);Argument *PointerArg = &*F->arg_begin();B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Merge, Left);BranchInst::Create(Merge, Right);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Add the loadB.SetInsertPoint(Merge);LoadInst *LoadInst = B.CreateLoad(B.getInt8Ty(), PointerArg);// MemoryPHI should already exist.MemoryPhi *MP = MSSA.getMemoryAccess(Merge);EXPECT_NE(MP, nullptr);// Create the load memory acccessMemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(LoadInst, MP, Merge, MemorySSA::Beginning));MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, CreateLoadsAndStoreUpdater) {// We create a diamond, then build memoryssa with no memory accesses, and// incrementally update it by inserting a store in the, entry, a load in the// merge point, then a store in the branch, another load in the merge point,// and then a store in the entry.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left, Left->begin());Argument *PointerArg = &*F->arg_begin();B.SetInsertPoint(Left);B.CreateBr(Merge);B.SetInsertPoint(Right);B.CreateBr(Merge);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Add the storeB.SetInsertPoint(Entry, Entry->begin());StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg);MemoryAccess *EntryStoreAccess = Updater.createMemoryAccessInBB(EntryStore, nullptr, Entry, MemorySSA::Beginning);Updater.insertDef(cast<MemoryDef>(EntryStoreAccess));// Add the loadB.SetInsertPoint(Merge, Merge->begin());LoadInst *FirstLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);// MemoryPHI should not already exist.MemoryPhi *MP = MSSA.getMemoryAccess(Merge);EXPECT_EQ(MP, nullptr);// Create the load memory accessMemoryUse *FirstLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(FirstLoad, nullptr, Merge, MemorySSA::Beginning));Updater.insertUse(FirstLoadAccess);// Should just have a load using the entry access, because it should discover// the phi is trivialEXPECT_EQ(FirstLoadAccess->getDefiningAccess(), EntryStoreAccess);// Create a store on the left// Add the storeB.SetInsertPoint(Left, Left->begin());StoreInst *LeftStore = B.CreateStore(B.getInt8(16), PointerArg);MemoryAccess *LeftStoreAccess = Updater.createMemoryAccessInBB(LeftStore, nullptr, Left, MemorySSA::Beginning);Updater.insertDef(cast<MemoryDef>(LeftStoreAccess), false);// MemoryPHI should exist after adding LeftStore.MP = MSSA.getMemoryAccess(Merge);EXPECT_NE(MP, nullptr);// Add the second loadB.SetInsertPoint(Merge, Merge->begin());LoadInst *SecondLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);// Create the load memory accessMemoryUse *SecondLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(SecondLoad, nullptr, Merge, MemorySSA::Beginning));Updater.insertUse(SecondLoadAccess);// Now the load should be a phi of the entry store and the left storeMemoryPhi *MergePhi =dyn_cast<MemoryPhi>(SecondLoadAccess->getDefiningAccess());EXPECT_NE(MergePhi, nullptr);EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(1), LeftStoreAccess);// Now create a store below the existing one in the entryB.SetInsertPoint(Entry, --Entry->end());StoreInst *SecondEntryStore = B.CreateStore(B.getInt8(16), PointerArg);MemoryAccess *SecondEntryStoreAccess = Updater.createMemoryAccessInBB(SecondEntryStore, nullptr, Entry, MemorySSA::End);// Insert it twice just to test renamingUpdater.insertDef(cast<MemoryDef>(SecondEntryStoreAccess), false);EXPECT_NE(FirstLoadAccess->getDefiningAccess(), MergePhi);Updater.insertDef(cast<MemoryDef>(SecondEntryStoreAccess), true);EXPECT_EQ(FirstLoadAccess->getDefiningAccess(), MergePhi);// and make sure the phi below it got updated, despite being blocks awayMergePhi = dyn_cast<MemoryPhi>(SecondLoadAccess->getDefiningAccess());EXPECT_NE(MergePhi, nullptr);EXPECT_EQ(MergePhi->getIncomingValue(0), SecondEntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(1), LeftStoreAccess);MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, CreateALoadUpdater) {// We create a diamond, then build memoryssa with no memory accesses, and// incrementally update it by inserting a store in one of the branches, and a// load in the merge pointF = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left, Left->begin());Argument *PointerArg = &*F->arg_begin();B.SetInsertPoint(Left);B.CreateBr(Merge);B.SetInsertPoint(Right);B.CreateBr(Merge);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);B.SetInsertPoint(Left, Left->begin());// Add the storeStoreInst *SI = B.CreateStore(B.getInt8(16), PointerArg);MemoryAccess *StoreAccess =Updater.createMemoryAccessInBB(SI, nullptr, Left, MemorySSA::Beginning);Updater.insertDef(cast<MemoryDef>(StoreAccess));// MemoryPHI should be created when inserting the defMemoryPhi *MP = MSSA.getMemoryAccess(Merge);EXPECT_NE(MP, nullptr);// Add the loadB.SetInsertPoint(Merge, Merge->begin());LoadInst *LoadInst = B.CreateLoad(B.getInt8Ty(), PointerArg);// Create the load memory acccessMemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(LoadInst, nullptr, Merge, MemorySSA::Beginning));Updater.insertUse(LoadAccess);MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, SinkLoad) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left, Left->begin());Argument *PointerArg = &*F->arg_begin();B.SetInsertPoint(Left);B.CreateBr(Merge);B.SetInsertPoint(Right);B.CreateBr(Merge);// Load in left blockB.SetInsertPoint(Left, Left->begin());LoadInst *LoadInst1 = B.CreateLoad(B.getInt8Ty(), PointerArg);// Store in merge blockB.SetInsertPoint(Merge, Merge->begin());B.CreateStore(B.getInt8(16), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Mimic sinking of a load:// - clone load// - insert in "exit" block// - insert in mssa// - remove from original blockLoadInst *LoadInstClone = cast<LoadInst>(LoadInst1->clone());Merge->getInstList().insert(Merge->begin(), LoadInstClone);MemoryAccess * NewLoadAccess =Updater.createMemoryAccessInBB(LoadInstClone, nullptr,LoadInstClone->getParent(),MemorySSA::Beginning);Updater.insertUse(cast<MemoryUse>(NewLoadAccess));MSSA.verifyMemorySSA();Updater.removeMemoryAccess(MSSA.getMemoryAccess(LoadInst1));MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, MoveAStore) {// We create a diamond where there is a in the entry, a store on one side, and// a load at the end. After building MemorySSA, we test updating by moving// the store from the side block to the entry block. This destroys the old// access.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Argument *PointerArg = &*F->arg_begin();StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left);StoreInst *SideStore = B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Merge, Left);BranchInst::Create(Merge, Right);B.SetInsertPoint(Merge);B.CreateLoad(B.getInt8Ty(), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Move the storeSideStore->moveBefore(Entry->getTerminator());MemoryAccess *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);MemoryAccess *SideStoreAccess = MSSA.getMemoryAccess(SideStore);MemoryAccess *NewStoreAccess = Updater.createMemoryAccessAfter(SideStore, EntryStoreAccess, EntryStoreAccess);EntryStoreAccess->replaceAllUsesWith(NewStoreAccess);Updater.removeMemoryAccess(SideStoreAccess);MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, MoveAStoreUpdater) {// We create a diamond where there is a in the entry, a store on one side, and// a load at the end. After building MemorySSA, we test updating by moving// the store from the side block to the entry block. This destroys the old// access.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Argument *PointerArg = &*F->arg_begin();StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left);auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Merge, Left);BranchInst::Create(Merge, Right);B.SetInsertPoint(Merge);auto *MergeLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Move the storeSideStore->moveBefore(Entry->getTerminator());auto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore);auto *NewStoreAccess = Updater.createMemoryAccessAfter(SideStore, EntryStoreAccess, EntryStoreAccess);// Before, the load will point to a phi of the EntryStore and SideStore.auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad));EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess()));MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess());EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);Updater.removeMemoryAccess(SideStoreAccess);Updater.insertDef(cast<MemoryDef>(NewStoreAccess));// After it's a phi of the new side store access.EXPECT_EQ(MergePhi->getIncomingValue(0), NewStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(1), NewStoreAccess);MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, MoveAStoreUpdaterMove) {// We create a diamond where there is a in the entry, a store on one side, and// a load at the end. After building MemorySSA, we test updating by moving// the store from the side block to the entry block. This does not destroy// the old access.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Argument *PointerArg = &*F->arg_begin();StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left);auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Merge, Left);BranchInst::Create(Merge, Right);B.SetInsertPoint(Merge);auto *MergeLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Move the storeauto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore);// Before, the load will point to a phi of the EntryStore and SideStore.auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad));EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess()));MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess());EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);SideStore->moveBefore(*EntryStore->getParent(), ++EntryStore->getIterator());Updater.moveAfter(SideStoreAccess, EntryStoreAccess);// After, it's a phi of the side store.EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(1), SideStoreAccess);MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, MoveAStoreAllAround) {// We create a diamond where there is a in the entry, a store on one side, and// a load at the end. After building MemorySSA, we test updating by moving// the store from the side block to the entry block, then to the other side// block, then to before the load. This does not destroy the old access.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Argument *PointerArg = &*F->arg_begin();StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left);auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Merge, Left);BranchInst::Create(Merge, Right);B.SetInsertPoint(Merge);auto *MergeLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Move the storeauto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore);// Before, the load will point to a phi of the EntryStore and SideStore.auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad));EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess()));MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess());EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);// Move the store before the entry storeSideStore->moveBefore(*EntryStore->getParent(), EntryStore->getIterator());Updater.moveBefore(SideStoreAccess, EntryStoreAccess);// After, it's a phi of the entry store.EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);MSSA.verifyMemorySSA();// Now move the store to the right branchSideStore->moveBefore(*Right, Right->begin());Updater.moveToPlace(SideStoreAccess, Right, MemorySSA::Beginning);MSSA.verifyMemorySSA();EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(1), SideStoreAccess);// Now move it before the loadSideStore->moveBefore(MergeLoad);Updater.moveBefore(SideStoreAccess, LoadAccess);EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, RemoveAPhi) {// We create a diamond where there is a store on one side, and then a load// after the merge point. This enables us to test a bunch of different// removal cases.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left);Argument *PointerArg = &*F->arg_begin();StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Merge, Left);BranchInst::Create(Merge, Right);B.SetInsertPoint(Merge);LoadInst *LoadInst = B.CreateLoad(B.getInt8Ty(), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Before, the load will be a use of a phi<store, liveonentry>.MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst));MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst));MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));// Kill the storeUpdater.removeMemoryAccess(StoreAccess);MemoryPhi *MP = cast<MemoryPhi>(DefiningAccess);// Verify the phi ended up as liveonentry, liveonentryfor (auto &Op : MP->incoming_values())EXPECT_TRUE(MSSA.isLiveOnEntryDef(cast<MemoryAccess>(Op.get())));// Replace the phi uses with the live on entry defMP->replaceAllUsesWith(MSSA.getLiveOnEntryDef());// Verify the load is now defined by liveOnEntryDefEXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess()));// Remove the PHIUpdater.removeMemoryAccess(MP);MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, RemoveMemoryAccess) {// We create a diamond where there is a store on one side, and then a load// after the merge point. This enables us to test a bunch of different// removal cases.F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *Left(BasicBlock::Create(C, "", F));BasicBlock *Right(BasicBlock::Create(C, "", F));BasicBlock *Merge(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Left, Right);B.SetInsertPoint(Left);Argument *PointerArg = &*F->arg_begin();StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Merge, Left);BranchInst::Create(Merge, Right);B.SetInsertPoint(Merge);LoadInst *LoadInst = B.CreateLoad(B.getInt8Ty(), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;MemorySSAUpdater Updater(&MSSA);// Before, the load will be a use of a phi<store, liveonentry>. It should be// the same after.MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst));MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst));MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));// The load is currently clobbered by one of the phi arguments, so the walker// should determine the clobbering access as the phi.EXPECT_EQ(DefiningAccess, Walker->getClobberingMemoryAccess(LoadInst));Updater.removeMemoryAccess(StoreAccess);MSSA.verifyMemorySSA();// After the removeaccess, let's see if we got the right accesses// The load should still point to the phi ...EXPECT_EQ(DefiningAccess, LoadAccess->getDefiningAccess());// but we should now get live on entry for the clobbering definition of the// load, since it will walk past the phi node since every argument is the// same.// XXX: This currently requires either removing the phi or resetting optimized// on the loadEXPECT_FALSE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst)));// If we reset optimized, we get live on entry.LoadAccess->resetOptimized();EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst)));// The phi should now be a two entry phi with two live on entry defs.for (const auto &Op : DefiningAccess->operands()) {MemoryAccess *Operand = cast<MemoryAccess>(&*Op);EXPECT_TRUE(MSSA.isLiveOnEntryDef(Operand));}// Now we try to remove the single valued phiUpdater.removeMemoryAccess(DefiningAccess);MSSA.verifyMemorySSA();// Now the load should be a load of live on entry.EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess()));}// We had a bug with caching where the walker would report MemoryDef#3's clobber// (below) was MemoryDef#1.//// define void @F(i8*) {// %A = alloca i8, i8 1// ; 1 = MemoryDef(liveOnEntry)// store i8 0, i8* %A// ; 2 = MemoryDef(1)// store i8 1, i8* %A// ; 3 = MemoryDef(2)// store i8 2, i8* %A// }TEST_F(MemorySSATest, TestTripleStore) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");StoreInst *S1 = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);StoreInst *S2 = B.CreateStore(ConstantInt::get(Int8, 1), Alloca);StoreInst *S3 = B.CreateStore(ConstantInt::get(Int8, 2), Alloca);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;unsigned I = 0;for (StoreInst *V : {S1, S2, S3}) {// Everything should be clobbered by its defining accessMemoryAccess *DefiningAccess = MSSA.getMemoryAccess(V)->getDefiningAccess();MemoryAccess *WalkerClobber = Walker->getClobberingMemoryAccess(V);EXPECT_EQ(DefiningAccess, WalkerClobber)<< "Store " << I << " doesn't have the correct clobbering access";// EXPECT_EQ expands such that if we increment I above, it won't get// incremented except when we try to print the error message.++I;}}// ...And fixing the above bug made it obvious that, when walking, MemorySSA's// walker was caching the initial node it walked. This was fine (albeit// mostly redundant) unless the initial node being walked is a clobber for the// query. In that case, we'd cache that the node clobbered itself.TEST_F(MemorySSATest, TestStoreAndLoad) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");Instruction *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);Instruction *LI = B.CreateLoad(Int8, Alloca);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LI);EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SI));EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SI)));}// Another bug (related to the above two fixes): It was noted that, given the// following code:// ; 1 = MemoryDef(liveOnEntry)// store i8 0, i8* %1//// ...A query to getClobberingMemoryAccess(MemoryAccess*, MemoryLocation) would// hand back the store (correctly). A later call to// getClobberingMemoryAccess(const Instruction*) would also hand back the store// (incorrectly; it should return liveOnEntry).//// This test checks that repeated calls to either function returns what they're// meant to.TEST_F(MemorySSATest, TestStoreDoubleQuery) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");StoreInst *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;MemoryAccess *StoreAccess = MSSA.getMemoryAccess(SI);MemoryLocation StoreLoc = MemoryLocation::get(SI);MemoryAccess *Clobber =Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc);MemoryAccess *LiveOnEntry = Walker->getClobberingMemoryAccess(SI);EXPECT_EQ(Clobber, StoreAccess);EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry));// Try again (with entries in the cache already) for good measure...Clobber = Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc);LiveOnEntry = Walker->getClobberingMemoryAccess(SI);EXPECT_EQ(Clobber, StoreAccess);EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry));}// Bug: During phi optimization, the walker wouldn't cache to the proper result// in the farthest-walked BB.//// Specifically, it would assume that whatever we walked to was a clobber.// "Whatever we walked to" isn't a clobber if we hit a cache entry.//// ...So, we need a test case that looks like:// A// / \// B |// \ /// C//// Where, when we try to optimize a thing in 'C', a blocker is found in 'B'.// The walk must determine that the blocker exists by using cache entries *while// walking* 'B'.TEST_F(MemorySSATest, PartialWalkerCacheWithPhis) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "A", F));Type *Int8 = Type::getInt8Ty(C);Constant *One = ConstantInt::get(Int8, 1);Constant *Zero = ConstantInt::get(Int8, 0);Value *AllocA = B.CreateAlloca(Int8, One, "a");Value *AllocB = B.CreateAlloca(Int8, One, "b");BasicBlock *IfThen = BasicBlock::Create(C, "B", F);BasicBlock *IfEnd = BasicBlock::Create(C, "C", F);B.CreateCondBr(UndefValue::get(Type::getInt1Ty(C)), IfThen, IfEnd);B.SetInsertPoint(IfThen);Instruction *FirstStore = B.CreateStore(Zero, AllocA);B.CreateStore(Zero, AllocB);Instruction *ALoad0 = B.CreateLoad(Int8, AllocA, "");Instruction *BStore = B.CreateStore(Zero, AllocB);// Due to use optimization/etc. we make a store to A, which is removed after// we build MSSA. This helps keep the test case simple-ish.Instruction *KillStore = B.CreateStore(Zero, AllocA);Instruction *ALoad = B.CreateLoad(Int8, AllocA, "");B.CreateBr(IfEnd);B.SetInsertPoint(IfEnd);Instruction *BelowPhi = B.CreateStore(Zero, AllocA);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;MemorySSAUpdater Updater(&MSSA);// Kill `KillStore`; it exists solely so that the load after it won't be// optimized to FirstStore.Updater.removeMemoryAccess(MSSA.getMemoryAccess(KillStore));KillStore->eraseFromParent();auto *ALoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(ALoad));EXPECT_EQ(ALoadMA->getDefiningAccess(), MSSA.getMemoryAccess(BStore));// Populate the cache for the store to AllocB directly after FirstStore. It// should point to something in block B (so something in D can't be optimized// to it).MemoryAccess *Load0Clobber = Walker->getClobberingMemoryAccess(ALoad0);EXPECT_EQ(MSSA.getMemoryAccess(FirstStore), Load0Clobber);// If the bug exists, this will introduce a bad cache entry for %a on BStore.// It will point to the store to %b after FirstStore. This only happens during// phi optimization.MemoryAccess *BottomClobber = Walker->getClobberingMemoryAccess(BelowPhi);MemoryAccess *Phi = MSSA.getMemoryAccess(IfEnd);EXPECT_EQ(BottomClobber, Phi);// This query will first check the cache for {%a, BStore}. It should point to// FirstStore, not to the store after FirstStore.MemoryAccess *UseClobber = Walker->getClobberingMemoryAccess(ALoad);EXPECT_EQ(UseClobber, MSSA.getMemoryAccess(FirstStore));}// Test that our walker properly handles loads with the invariant group// attribute. It's a bit hacky, since we add the invariant attribute *after*// building MSSA. Otherwise, the use optimizer will optimize it for us, which// isn't what we want.// FIXME: It may be easier/cleaner to just add an 'optimize uses?' flag to MSSA.TEST_F(MemorySSATest, WalkerInvariantLoadOpt) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Constant *One = ConstantInt::get(Int8, 1);Value *AllocA = B.CreateAlloca(Int8, One, "");Instruction *Store = B.CreateStore(One, AllocA);Instruction *Load = B.CreateLoad(Int8, AllocA);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;auto *LoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(Load));auto *StoreMA = cast<MemoryDef>(MSSA.getMemoryAccess(Store));EXPECT_EQ(LoadMA->getDefiningAccess(), StoreMA);// ...At the time of writing, no cache should exist for LoadMA. Be a bit// flexible to future changes.Walker->invalidateInfo(LoadMA);Load->setMetadata(LLVMContext::MD_invariant_load, MDNode::get(C, {}));MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LoadMA);EXPECT_EQ(LoadClobber, MSSA.getLiveOnEntryDef());}// Test loads get reoptimized properly by the walker.TEST_F(MemorySSATest, WalkerReopt) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *AllocaA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");Instruction *SIA = B.CreateStore(ConstantInt::get(Int8, 0), AllocaA);Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");Instruction *SIB = B.CreateStore(ConstantInt::get(Int8, 0), AllocaB);Instruction *LIA = B.CreateLoad(Int8, AllocaA);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;MemorySSAUpdater Updater(&MSSA);MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LIA);MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LIA));EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SIA));EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SIA)));Updater.removeMemoryAccess(LoadAccess);// Create the load memory access pointing to an unoptimized place.MemoryUse *NewLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(LIA, MSSA.getMemoryAccess(SIB), LIA->getParent(), MemorySSA::End));// This should it cause it to be optimizedEXPECT_EQ(Walker->getClobberingMemoryAccess(NewLoadAccess), LoadClobber);EXPECT_EQ(NewLoadAccess->getDefiningAccess(), LoadClobber);}// Test out MemorySSAUpdater::moveBeforeTEST_F(MemorySSATest, MoveAboveMemoryDef) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *A = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");Value *B_ = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");Value *C = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "C");StoreInst *StoreA0 = B.CreateStore(ConstantInt::get(Int8, 0), A);StoreInst *StoreB = B.CreateStore(ConstantInt::get(Int8, 0), B_);LoadInst *LoadB = B.CreateLoad(Int8, B_);StoreInst *StoreA1 = B.CreateStore(ConstantInt::get(Int8, 4), A);StoreInst *StoreC = B.CreateStore(ConstantInt::get(Int8, 4), C);StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 4), A);LoadInst *LoadC = B.CreateLoad(Int8, C);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker &Walker = *Analyses->Walker;MemorySSAUpdater Updater(&MSSA);StoreC->moveBefore(StoreB);Updater.moveBefore(cast<MemoryDef>(MSSA.getMemoryAccess(StoreC)),cast<MemoryDef>(MSSA.getMemoryAccess(StoreB)));MSSA.verifyMemorySSA();EXPECT_EQ(MSSA.getMemoryAccess(StoreB)->getDefiningAccess(),MSSA.getMemoryAccess(StoreC));EXPECT_EQ(MSSA.getMemoryAccess(StoreC)->getDefiningAccess(),MSSA.getMemoryAccess(StoreA0));EXPECT_EQ(MSSA.getMemoryAccess(StoreA2)->getDefiningAccess(),MSSA.getMemoryAccess(StoreA1));EXPECT_EQ(Walker.getClobberingMemoryAccess(LoadB),MSSA.getMemoryAccess(StoreB));EXPECT_EQ(Walker.getClobberingMemoryAccess(LoadC),MSSA.getMemoryAccess(StoreC));// exercise block numberingEXPECT_TRUE(MSSA.locallyDominates(MSSA.getMemoryAccess(StoreC),MSSA.getMemoryAccess(StoreB)));EXPECT_TRUE(MSSA.locallyDominates(MSSA.getMemoryAccess(StoreA1),MSSA.getMemoryAccess(StoreA2)));}TEST_F(MemorySSATest, Irreducible) {// Create the equivalent of// x = something// if (...)// goto second_loop_entry// while (...) {// second_loop_entry:// }// use(x)SmallVector<PHINode *, 8> Inserted;IRBuilder<> B(C);F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);// Make blocksBasicBlock *IfBB = BasicBlock::Create(C, "if", F);BasicBlock *LoopStartBB = BasicBlock::Create(C, "loopstart", F);BasicBlock *LoopMainBB = BasicBlock::Create(C, "loopmain", F);BasicBlock *AfterLoopBB = BasicBlock::Create(C, "afterloop", F);B.SetInsertPoint(IfBB);B.CreateCondBr(B.getTrue(), LoopMainBB, LoopStartBB);B.SetInsertPoint(LoopStartBB);B.CreateBr(LoopMainBB);B.SetInsertPoint(LoopMainBB);B.CreateCondBr(B.getTrue(), LoopStartBB, AfterLoopBB);B.SetInsertPoint(AfterLoopBB);Argument *FirstArg = &*F->arg_begin();setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);// Create the load memory acccessLoadInst *LoadInst = B.CreateLoad(B.getInt8Ty(), FirstArg);MemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(LoadInst, nullptr, AfterLoopBB, MemorySSA::Beginning));Updater.insertUse(LoadAccess);MSSA.verifyMemorySSA();}TEST_F(MemorySSATest, MoveToBeforeLiveOnEntryInvalidatesCache) {// Create:// %1 = alloca i8// ; 1 = MemoryDef(liveOnEntry)// store i8 0, i8* %1// ; 2 = MemoryDef(1)// store i8 0, i8* %1//// ...And be sure that MSSA's caching doesn't give us `1` for the clobber of// `2` after `1` is removed.IRBuilder<> B(C);F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry = BasicBlock::Create(C, "if", F);B.SetInsertPoint(Entry);Value *A = B.CreateAlloca(B.getInt8Ty());StoreInst *StoreA = B.CreateStore(B.getInt8(0), A);StoreInst *StoreB = B.CreateStore(B.getInt8(0), A);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;auto *DefA = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA));auto *DefB = cast<MemoryDef>(MSSA.getMemoryAccess(StoreB));MemoryAccess *BClobber = MSSA.getWalker()->getClobberingMemoryAccess(DefB);ASSERT_EQ(DefA, BClobber);MemorySSAUpdater(&MSSA).removeMemoryAccess(DefA);StoreA->eraseFromParent();EXPECT_EQ(DefB->getDefiningAccess(), MSSA.getLiveOnEntryDef());EXPECT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(DefB),MSSA.getLiveOnEntryDef())<< "(DefA = " << DefA << ")";}TEST_F(MemorySSATest, RemovingDefInvalidatesCache) {// Create:// %x = alloca i8// %y = alloca i8// ; 1 = MemoryDef(liveOnEntry)// store i8 0, i8* %x// ; 2 = MemoryDef(1)// store i8 0, i8* %y// ; 3 = MemoryDef(2)// store i8 0, i8* %x//// And be sure that MSSA's caching handles the removal of def `1`// appropriately.IRBuilder<> B(C);F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry = BasicBlock::Create(C, "if", F);B.SetInsertPoint(Entry);Value *X = B.CreateAlloca(B.getInt8Ty());Value *Y = B.CreateAlloca(B.getInt8Ty());StoreInst *StoreX1 = B.CreateStore(B.getInt8(0), X);StoreInst *StoreY = B.CreateStore(B.getInt8(0), Y);StoreInst *StoreX2 = B.CreateStore(B.getInt8(0), X);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;auto *DefX1 = cast<MemoryDef>(MSSA.getMemoryAccess(StoreX1));auto *DefY = cast<MemoryDef>(MSSA.getMemoryAccess(StoreY));auto *DefX2 = cast<MemoryDef>(MSSA.getMemoryAccess(StoreX2));EXPECT_EQ(DefX2->getDefiningAccess(), DefY);MemoryAccess *X2Clobber = MSSA.getWalker()->getClobberingMemoryAccess(DefX2);ASSERT_EQ(DefX1, X2Clobber);MemorySSAUpdater(&MSSA).removeMemoryAccess(DefX1);StoreX1->eraseFromParent();EXPECT_EQ(DefX2->getDefiningAccess(), DefY);EXPECT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(DefX2),MSSA.getLiveOnEntryDef())<< "(DefX1 = " << DefX1 << ")";}// Test Must alias for optimized usesTEST_F(MemorySSATest, TestLoadMustAlias) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *AllocaA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");B.CreateStore(ConstantInt::get(Int8, 1), AllocaB);// Check load from LOELoadInst *LA1 = B.CreateLoad(Int8, AllocaA, "");// Check load alias cached for second loadLoadInst *LA2 = B.CreateLoad(Int8, AllocaA, "");B.CreateStore(ConstantInt::get(Int8, 1), AllocaA);// Check load from store/defLoadInst *LA3 = B.CreateLoad(Int8, AllocaA, "");// Check load alias cached for second loadLoadInst *LA4 = B.CreateLoad(Int8, AllocaA, "");setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MSSA.ensureOptimizedUses();unsigned I = 0;for (LoadInst *V : {LA1, LA2}) {MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));EXPECT_EQ(MemUse->getOptimizedAccessType(), None)<< "Load " << I << " doesn't have the correct alias information";// EXPECT_EQ expands such that if we increment I above, it won't get// incremented except when we try to print the error message.++I;}for (LoadInst *V : {LA3, LA4}) {MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));EXPECT_EQ(*MemUse->getOptimizedAccessType(), AliasResult::MustAlias)<< "Load " << I << " doesn't have the correct alias information";// EXPECT_EQ expands such that if we increment I above, it won't get// incremented except when we try to print the error message.++I;}}// Test Must alias for optimized defs.TEST_F(MemorySSATest, TestStoreMustAlias) {F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *AllocaA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");StoreInst *SA1 = B.CreateStore(ConstantInt::get(Int8, 1), AllocaA);StoreInst *SB1 = B.CreateStore(ConstantInt::get(Int8, 1), AllocaB);StoreInst *SA2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaA);StoreInst *SB2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaB);StoreInst *SA3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaA);StoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaB);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;unsigned I = 0;for (StoreInst *V : {SA1, SB1, SA2, SB2, SA3, SB3}) {MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));EXPECT_EQ(MemDef->isOptimized(), false)<< "Store " << I << " is optimized from the start?";EXPECT_EQ(MemDef->getOptimizedAccessType(), None)<< "Store " << I<< " has correct alias information before being optimized?";if (V == SA1)Walker->getClobberingMemoryAccess(V);else {MemoryAccess *Def = MemDef->getDefiningAccess();MemoryAccess *Clob = Walker->getClobberingMemoryAccess(V);EXPECT_NE(Def, Clob) << "Store " << I<< " has Defining Access equal to Clobbering Access";}EXPECT_EQ(MemDef->isOptimized(), true)<< "Store " << I << " was not optimized";if (I == 0 || I == 1)EXPECT_EQ(MemDef->getOptimizedAccessType(), None)<< "Store " << I << " doesn't have the correct alias information";elseEXPECT_EQ(*MemDef->getOptimizedAccessType(), AliasResult::MustAlias)<< "Store " << I << " doesn't have the correct alias information";// EXPECT_EQ expands such that if we increment I above, it won't get// incremented except when we try to print the error message.++I;}}// Test May alias for optimized uses.TEST_F(MemorySSATest, TestLoadMayAlias) {F = Function::Create(FunctionType::get(B.getVoidTy(),{B.getInt8PtrTy(), B.getInt8PtrTy()},false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);auto *ArgIt = F->arg_begin();Argument *PointerA = &*ArgIt;Argument *PointerB = &*(++ArgIt);B.CreateStore(ConstantInt::get(Int8, 1), PointerB);LoadInst *LA1 = B.CreateLoad(Int8, PointerA, "");B.CreateStore(ConstantInt::get(Int8, 0), PointerA);LoadInst *LB1 = B.CreateLoad(Int8, PointerB, "");B.CreateStore(ConstantInt::get(Int8, 0), PointerA);LoadInst *LA2 = B.CreateLoad(Int8, PointerA, "");B.CreateStore(ConstantInt::get(Int8, 0), PointerB);LoadInst *LB2 = B.CreateLoad(Int8, PointerB, "");setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MSSA.ensureOptimizedUses();unsigned I = 0;for (LoadInst *V : {LA1, LB1}) {MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));EXPECT_EQ(*MemUse->getOptimizedAccessType(), AliasResult::MayAlias)<< "Load " << I << " doesn't have the correct alias information";// EXPECT_EQ expands such that if we increment I above, it won't get// incremented except when we try to print the error message.++I;}for (LoadInst *V : {LA2, LB2}) {MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));EXPECT_EQ(*MemUse->getOptimizedAccessType(), AliasResult::MustAlias)<< "Load " << I << " doesn't have the correct alias information";// EXPECT_EQ expands such that if we increment I above, it won't get// incremented except when we try to print the error message.++I;}}// Test May alias for optimized defs.TEST_F(MemorySSATest, TestStoreMayAlias) {F = Function::Create(FunctionType::get(B.getVoidTy(),{B.getInt8PtrTy(), B.getInt8PtrTy()},false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);auto *ArgIt = F->arg_begin();Argument *PointerA = &*ArgIt;Argument *PointerB = &*(++ArgIt);Value *AllocaC = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "C");// Store into arg1, must alias because it's LOE => mustStoreInst *SA1 = B.CreateStore(ConstantInt::get(Int8, 0), PointerA);// Store into arg2, may alias store to arg1 => mayStoreInst *SB1 = B.CreateStore(ConstantInt::get(Int8, 1), PointerB);// Store into aloca, no alias with args, so must alias LOE => mustStoreInst *SC1 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaC);// Store into arg1, may alias store to arg2 => mayStoreInst *SA2 = B.CreateStore(ConstantInt::get(Int8, 3), PointerA);// Store into arg2, may alias store to arg1 => mayStoreInst *SB2 = B.CreateStore(ConstantInt::get(Int8, 4), PointerB);// Store into aloca, no alias with args, so must alias SC1 => mustStoreInst *SC2 = B.CreateStore(ConstantInt::get(Int8, 5), AllocaC);// Store into arg2, must alias store to arg2 => mustStoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 6), PointerB);std::initializer_list<StoreInst *> Sts = {SA1, SB1, SC1, SA2, SB2, SC2, SB3};setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;unsigned I = 0;for (StoreInst *V : Sts) {MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));EXPECT_EQ(MemDef->isOptimized(), false)<< "Store " << I << " is optimized from the start?";EXPECT_EQ(MemDef->getOptimizedAccessType(), None)<< "Store " << I<< " has correct alias information before being optimized?";++I;}for (StoreInst *V : Sts)Walker->getClobberingMemoryAccess(V);I = 0;for (StoreInst *V : Sts) {MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));EXPECT_EQ(MemDef->isOptimized(), true)<< "Store " << I << " was not optimized";if (I == 1 || I == 3 || I == 4)EXPECT_EQ(MemDef->getOptimizedAccessType().value(), AliasResult::MayAlias)<< "Store " << I << " doesn't have the correct alias information";else if (I == 0 || I == 2)EXPECT_EQ(MemDef->getOptimizedAccessType(), None)<< "Store " << I << " doesn't have the correct alias information";elseEXPECT_EQ(MemDef->getOptimizedAccessType().value(),AliasResult::MustAlias)<< "Store " << I << " doesn't have the correct alias information";// EXPECT_EQ expands such that if we increment I above, it won't get// incremented except when we try to print the error message.++I;}}TEST_F(MemorySSATest, LifetimeMarkersAreClobbers) {// Example code:// define void @a(i8* %foo) {// %bar = getelementptr i8, i8* %foo, i64 1// %baz = getelementptr i8, i8* %foo, i64 2// store i8 0, i8* %foo// store i8 0, i8* %bar// call void @llvm.lifetime.end.p0i8(i64 3, i8* %foo)// call void @llvm.lifetime.start.p0i8(i64 3, i8* %foo)// store i8 0, i8* %foo// store i8 0, i8* %bar// call void @llvm.memset.p0i8(i8* %baz, i8 0, i64 1)// ret void// }//// Patterns like this are possible after inlining; the stores to %foo and %bar// should both be clobbered by the lifetime.start call if they're dominated by// it.IRBuilder<> B(C);F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);// Make blocksBasicBlock *Entry = BasicBlock::Create(C, "entry", F);B.SetInsertPoint(Entry);Value *Foo = &*F->arg_begin();Value *Bar = B.CreateGEP(B.getInt8Ty(), Foo, B.getInt64(1), "bar");Value *Baz = B.CreateGEP(B.getInt8Ty(), Foo, B.getInt64(2), "baz");B.CreateStore(B.getInt8(0), Foo);B.CreateStore(B.getInt8(0), Bar);auto GetLifetimeIntrinsic = [&](Intrinsic::ID ID) {return Intrinsic::getDeclaration(&M, ID, {Foo->getType()});};B.CreateCall(GetLifetimeIntrinsic(Intrinsic::lifetime_end),{B.getInt64(3), Foo});Instruction *LifetimeStart = B.CreateCall(GetLifetimeIntrinsic(Intrinsic::lifetime_start), {B.getInt64(3), Foo});Instruction *FooStore = B.CreateStore(B.getInt8(0), Foo);Instruction *BarStore = B.CreateStore(B.getInt8(0), Bar);Instruction *BazMemSet = B.CreateMemSet(Baz, B.getInt8(0), 1, Align(1));setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemoryAccess *LifetimeStartAccess = MSSA.getMemoryAccess(LifetimeStart);ASSERT_NE(LifetimeStartAccess, nullptr);MemoryAccess *FooAccess = MSSA.getMemoryAccess(FooStore);ASSERT_NE(FooAccess, nullptr);MemoryAccess *BarAccess = MSSA.getMemoryAccess(BarStore);ASSERT_NE(BarAccess, nullptr);MemoryAccess *BazAccess = MSSA.getMemoryAccess(BazMemSet);ASSERT_NE(BazAccess, nullptr);MemoryAccess *FooClobber =MSSA.getWalker()->getClobberingMemoryAccess(FooAccess);EXPECT_EQ(FooClobber, LifetimeStartAccess);MemoryAccess *BarClobber =MSSA.getWalker()->getClobberingMemoryAccess(BarAccess);EXPECT_EQ(BarClobber, LifetimeStartAccess);MemoryAccess *BazClobber =MSSA.getWalker()->getClobberingMemoryAccess(BazAccess);EXPECT_EQ(BazClobber, LifetimeStartAccess);MemoryAccess *LifetimeStartClobber =MSSA.getWalker()->getClobberingMemoryAccess(LifetimeStartAccess, MemoryLocation::getAfter(Foo));EXPECT_EQ(LifetimeStartClobber, LifetimeStartAccess);}TEST_F(MemorySSATest, DefOptimizationsAreInvalidatedOnMoving) {IRBuilder<> B(C);F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt1Ty()}, false),GlobalValue::ExternalLinkage, "F", &M);// Make a CFG like// entry// / \// a b// \ /// c//// Put a def in A and a def in B, move the def from A -> B, observe as the// optimization is invalidated.BasicBlock *Entry = BasicBlock::Create(C, "entry", F);BasicBlock *BlockA = BasicBlock::Create(C, "a", F);BasicBlock *BlockB = BasicBlock::Create(C, "b", F);BasicBlock *BlockC = BasicBlock::Create(C, "c", F);B.SetInsertPoint(Entry);Type *Int8 = Type::getInt8Ty(C);Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "alloc");StoreInst *StoreEntry = B.CreateStore(B.getInt8(0), Alloca);B.CreateCondBr(B.getTrue(), BlockA, BlockB);B.SetInsertPoint(BlockA);StoreInst *StoreA = B.CreateStore(B.getInt8(1), Alloca);B.CreateBr(BlockC);B.SetInsertPoint(BlockB);StoreInst *StoreB = B.CreateStore(B.getInt8(2), Alloca);B.CreateBr(BlockC);B.SetInsertPoint(BlockC);B.CreateUnreachable();setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;auto *AccessEntry = cast<MemoryDef>(MSSA.getMemoryAccess(StoreEntry));auto *StoreAEntry = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA));auto *StoreBEntry = cast<MemoryDef>(MSSA.getMemoryAccess(StoreB));ASSERT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(StoreAEntry),AccessEntry);ASSERT_TRUE(StoreAEntry->isOptimized());ASSERT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(StoreBEntry),AccessEntry);ASSERT_TRUE(StoreBEntry->isOptimized());// Note that if we did InsertionPlace::Beginning, we don't go out of our way// to invalidate the cache for StoreBEntry. If the user wants to actually do// moves like these, it's up to them to ensure that nearby cache entries are// correctly invalidated (which, in general, requires walking all instructions// that the moved instruction dominates. So we probably shouldn't be doing// moves like this in general. Still, works as a test-case. ;) )MemorySSAUpdater(&MSSA).moveToPlace(StoreAEntry, BlockB,MemorySSA::InsertionPlace::End);ASSERT_FALSE(StoreAEntry->isOptimized());ASSERT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(StoreAEntry),StoreBEntry);}TEST_F(MemorySSATest, TestOptimizedDefsAreProperUses) {F = Function::Create(FunctionType::get(B.getVoidTy(),{B.getInt8PtrTy(), B.getInt8PtrTy()},false),GlobalValue::ExternalLinkage, "F", &M);B.SetInsertPoint(BasicBlock::Create(C, "", F));Type *Int8 = Type::getInt8Ty(C);Value *AllocA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");Value *AllocB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");StoreInst *StoreA = B.CreateStore(ConstantInt::get(Int8, 0), AllocA);StoreInst *StoreB = B.CreateStore(ConstantInt::get(Int8, 1), AllocB);StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocA);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;// If these don't hold, there's no chance of the test result being useful.ASSERT_EQ(Walker->getClobberingMemoryAccess(StoreA),MSSA.getLiveOnEntryDef());ASSERT_EQ(Walker->getClobberingMemoryAccess(StoreB),MSSA.getLiveOnEntryDef());auto *StoreAAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA));auto *StoreA2Access = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA2));ASSERT_EQ(Walker->getClobberingMemoryAccess(StoreA2), StoreAAccess);ASSERT_EQ(StoreA2Access->getOptimized(), StoreAAccess);auto *StoreBAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreB));ASSERT_LT(StoreAAccess->getID(), StoreBAccess->getID());ASSERT_LT(StoreBAccess->getID(), StoreA2Access->getID());auto SortVecByID = [](std::vector<const MemoryDef *> &Defs) {llvm::sort(Defs, [](const MemoryDef *LHS, const MemoryDef *RHS) {return LHS->getID() < RHS->getID();});};auto SortedUserList = [&](const MemoryDef *MD) {std::vector<const MemoryDef *> Result;transform(MD->users(), std::back_inserter(Result),[](const User *U) { return cast<MemoryDef>(U); });SortVecByID(Result);return Result;};// Use std::vectors, since they have nice pretty-printing if the test fails.// Parens are necessary because EXPECT_EQ is a macro, and we have commas in// our init lists...EXPECT_EQ(SortedUserList(StoreAAccess),(std::vector<const MemoryDef *>{StoreBAccess, StoreA2Access}));EXPECT_EQ(SortedUserList(StoreBAccess),std::vector<const MemoryDef *>{StoreA2Access});// StoreAAccess should be present twice, since it uses liveOnEntry for both// its defining and optimized accesses. This is a bit awkward, and is not// relied upon anywhere at the moment. If this is painful, we can fix it.EXPECT_EQ(SortedUserList(cast<MemoryDef>(MSSA.getLiveOnEntryDef())),(std::vector<const MemoryDef *>{StoreAAccess, StoreAAccess,StoreBAccess}));}// entry// |// header// / \// body |// \ /// exit// header:// ; 1 = MemoryDef(liveOnEntry)// body:// ; 2 = MemoryDef(1)// exit:// ; 3 = MemoryPhi({body, 2}, {header, 1})// ; 4 = MemoryDef(3); optimized to 3, cannot optimize thorugh phi.// Insert edge: entry -> exit, check mssa Update is correct.TEST_F(MemorySSATest, TestAddedEdgeToBlockWithPhiNotOpt) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);Argument *PointerArg = &*F->arg_begin();BasicBlock *Entry(BasicBlock::Create(C, "entry", F));BasicBlock *Header(BasicBlock::Create(C, "header", F));BasicBlock *Body(BasicBlock::Create(C, "body", F));BasicBlock *Exit(BasicBlock::Create(C, "exit", F));B.SetInsertPoint(Entry);BranchInst::Create(Header, Entry);B.SetInsertPoint(Header);B.CreateStore(B.getInt8(16), PointerArg);B.CreateCondBr(B.getTrue(), Exit, Body);B.SetInsertPoint(Body);B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(Exit, Body);B.SetInsertPoint(Exit);StoreInst *S1 = B.CreateStore(B.getInt8(16), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;std::unique_ptr<MemorySSAUpdater> MSSAU =std::make_unique<MemorySSAUpdater>(&MSSA);MemoryPhi *Phi = MSSA.getMemoryAccess(Exit);EXPECT_EQ(Phi, Walker->getClobberingMemoryAccess(S1));// Alter CFG, add edge: entry -> exitEntry->getTerminator()->eraseFromParent();B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Header, Exit);SmallVector<CFGUpdate, 1> Updates;Updates.push_back({cfg::UpdateKind::Insert, Entry, Exit});Analyses->DT.applyUpdates(Updates);MSSAU->applyInsertUpdates(Updates, Analyses->DT);EXPECT_EQ(Phi, Walker->getClobberingMemoryAccess(S1));}// entry// |// header// / \// body |// \ /// exit// header:// ; 1 = MemoryDef(liveOnEntry)// body:// ; 2 = MemoryDef(1)// exit:// ; 3 = MemoryPhi({body, 2}, {header, 1})// ; 4 = MemoryDef(3); optimize this to 1 now, added edge should invalidate// the optimized access.// Insert edge: entry -> exit, check mssa Update is correct.TEST_F(MemorySSATest, TestAddedEdgeToBlockWithPhiOpt) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);Argument *PointerArg = &*F->arg_begin();Type *Int8 = Type::getInt8Ty(C);BasicBlock *Entry(BasicBlock::Create(C, "entry", F));BasicBlock *Header(BasicBlock::Create(C, "header", F));BasicBlock *Body(BasicBlock::Create(C, "body", F));BasicBlock *Exit(BasicBlock::Create(C, "exit", F));B.SetInsertPoint(Entry);Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");BranchInst::Create(Header, Entry);B.SetInsertPoint(Header);StoreInst *S1 = B.CreateStore(B.getInt8(16), PointerArg);B.CreateCondBr(B.getTrue(), Exit, Body);B.SetInsertPoint(Body);B.CreateStore(ConstantInt::get(Int8, 0), Alloca);BranchInst::Create(Exit, Body);B.SetInsertPoint(Exit);StoreInst *S2 = B.CreateStore(B.getInt8(16), PointerArg);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;std::unique_ptr<MemorySSAUpdater> MSSAU =std::make_unique<MemorySSAUpdater>(&MSSA);MemoryDef *DefS1 = cast<MemoryDef>(MSSA.getMemoryAccess(S1));EXPECT_EQ(DefS1, Walker->getClobberingMemoryAccess(S2));// Alter CFG, add edge: entry -> exitEntry->getTerminator()->eraseFromParent();B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), Header, Exit);SmallVector<CFGUpdate, 1> Updates;Updates.push_back({cfg::UpdateKind::Insert, Entry, Exit});Analyses->DT.applyUpdates(Updates);MSSAU->applyInsertUpdates(Updates, Analyses->DT);MemoryPhi *Phi = MSSA.getMemoryAccess(Exit);EXPECT_EQ(Phi, Walker->getClobberingMemoryAccess(S2));}// entry// / |// a |// / \ |// b c f// \ / |// d |// \ /// e// f:// ; 1 = MemoryDef(liveOnEntry)// e:// ; 2 = MemoryPhi({d, liveOnEntry}, {f, 1})//// Insert edge: f -> c, check update is correct.// After update:// f:// ; 1 = MemoryDef(liveOnEntry)// c:// ; 3 = MemoryPhi({a, liveOnEntry}, {f, 1})// d:// ; 4 = MemoryPhi({b, liveOnEntry}, {c, 3})// e:// ; 2 = MemoryPhi({d, 4}, {f, 1})TEST_F(MemorySSATest, TestAddedEdgeToBlockWithNoPhiAddNewPhis) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);Argument *PointerArg = &*F->arg_begin();BasicBlock *Entry(BasicBlock::Create(C, "entry", F));BasicBlock *ABlock(BasicBlock::Create(C, "a", F));BasicBlock *BBlock(BasicBlock::Create(C, "b", F));BasicBlock *CBlock(BasicBlock::Create(C, "c", F));BasicBlock *DBlock(BasicBlock::Create(C, "d", F));BasicBlock *EBlock(BasicBlock::Create(C, "e", F));BasicBlock *FBlock(BasicBlock::Create(C, "f", F));B.SetInsertPoint(Entry);B.CreateCondBr(B.getTrue(), ABlock, FBlock);B.SetInsertPoint(ABlock);B.CreateCondBr(B.getTrue(), BBlock, CBlock);B.SetInsertPoint(BBlock);BranchInst::Create(DBlock, BBlock);B.SetInsertPoint(CBlock);BranchInst::Create(DBlock, CBlock);B.SetInsertPoint(DBlock);BranchInst::Create(EBlock, DBlock);B.SetInsertPoint(FBlock);B.CreateStore(B.getInt8(16), PointerArg);BranchInst::Create(EBlock, FBlock);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;std::unique_ptr<MemorySSAUpdater> MSSAU =std::make_unique<MemorySSAUpdater>(&MSSA);// Alter CFG, add edge: f -> cFBlock->getTerminator()->eraseFromParent();B.SetInsertPoint(FBlock);B.CreateCondBr(B.getTrue(), CBlock, EBlock);SmallVector<CFGUpdate, 1> Updates;Updates.push_back({cfg::UpdateKind::Insert, FBlock, CBlock});Analyses->DT.applyUpdates(Updates);MSSAU->applyInsertUpdates(Updates, Analyses->DT);MemoryPhi *MPC = MSSA.getMemoryAccess(CBlock);EXPECT_NE(MPC, nullptr);MemoryPhi *MPD = MSSA.getMemoryAccess(DBlock);EXPECT_NE(MPD, nullptr);MemoryPhi *MPE = MSSA.getMemoryAccess(EBlock);EXPECT_EQ(MPD, MPE->getIncomingValueForBlock(DBlock));}TEST_F(MemorySSATest, TestCallClobber) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);Value *Pointer1 = &*F->arg_begin();BasicBlock *Entry(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Value *Pointer2 = B.CreateGEP(B.getInt8Ty(), Pointer1, B.getInt64(1));Instruction *StorePointer1 = B.CreateStore(B.getInt8(0), Pointer1);Instruction *StorePointer2 = B.CreateStore(B.getInt8(0), Pointer2);Instruction *MemSet = B.CreateMemSet(Pointer2, B.getInt8(0), 1, Align(1));setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;MemoryUseOrDef *Store1Access = MSSA.getMemoryAccess(StorePointer1);MemoryUseOrDef *Store2Access = MSSA.getMemoryAccess(StorePointer2);MemoryUseOrDef *MemSetAccess = MSSA.getMemoryAccess(MemSet);MemoryAccess *Pointer1Clobber = Walker->getClobberingMemoryAccess(MemSetAccess, MemoryLocation(Pointer1, LocationSize::precise(1)));EXPECT_EQ(Pointer1Clobber, Store1Access);MemoryAccess *Pointer2Clobber = Walker->getClobberingMemoryAccess(MemSetAccess, MemoryLocation(Pointer2, LocationSize::precise(1)));EXPECT_EQ(Pointer2Clobber, MemSetAccess);MemoryAccess *MemSetClobber = Walker->getClobberingMemoryAccess(MemSetAccess);EXPECT_EQ(MemSetClobber, Store2Access);}TEST_F(MemorySSATest, TestLoadClobber) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);Value *Pointer1 = &*F->arg_begin();BasicBlock *Entry(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Value *Pointer2 = B.CreateGEP(B.getInt8Ty(), Pointer1, B.getInt64(1));Instruction *LoadPointer1 =B.CreateLoad(B.getInt8Ty(), Pointer1, /* Volatile */ true);Instruction *LoadPointer2 =B.CreateLoad(B.getInt8Ty(), Pointer2, /* Volatile */ true);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;MemoryUseOrDef *Load1Access = MSSA.getMemoryAccess(LoadPointer1);MemoryUseOrDef *Load2Access = MSSA.getMemoryAccess(LoadPointer2);// When providing a memory location, we should never return a load as the// clobber.MemoryAccess *Pointer1Clobber = Walker->getClobberingMemoryAccess(Load2Access, MemoryLocation(Pointer1, LocationSize::precise(1)));EXPECT_TRUE(MSSA.isLiveOnEntryDef(Pointer1Clobber));MemoryAccess *Pointer2Clobber = Walker->getClobberingMemoryAccess(Load2Access, MemoryLocation(Pointer2, LocationSize::precise(1)));EXPECT_TRUE(MSSA.isLiveOnEntryDef(Pointer2Clobber));MemoryAccess *Load2Clobber = Walker->getClobberingMemoryAccess(Load2Access);EXPECT_EQ(Load2Clobber, Load1Access);}// We want to test if the location information are retained// when the IsGuaranteedLoopInvariant function handles a// memory access referring to a pointer defined in the entry// block, hence automatically guaranteed to be loop invariant.TEST_F(MemorySSATest, TestLoopInvariantEntryBlockPointer) {SMDiagnostic E;auto LocalM =parseAssemblyString("define void @test(i64 %a0, i8* %a1, i1* %a2) {\n""entry:\n""%v0 = getelementptr i8, i8* %a1, i64 %a0\n""%v1 = bitcast i8* %v0 to i64*\n""%v2 = bitcast i8* %v0 to i32*\n""%v3 = load i1, i1* %a2\n""br i1 %v3, label %body, label %exit\n""body:\n""store i32 1, i32* %v2\n""br label %exit\n""exit:\n""store i64 0, i64* %v1\n""ret void\n""}",E, C);ASSERT_TRUE(LocalM);F = LocalM->getFunction("test");ASSERT_TRUE(F);// Setup the analysissetupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;// Find the exit blockfor (auto &BB : *F) {if (BB.getName() == "exit") {// Get the store instructionauto *SI = BB.getFirstNonPHI();// Get the memory access and locationMemoryAccess *MA = MSSA.getMemoryAccess(SI);MemoryLocation ML = MemoryLocation::get(SI);// Use the 'upward_defs_iterator' which internally calls// IsGuaranteedLoopInvariantauto ItA = upward_defs_begin({MA, ML}, MSSA.getDomTree());auto ItB =upward_defs_begin({ItA->first, ItA->second}, MSSA.getDomTree());// Check if the location information have been retainedEXPECT_TRUE(ItB->second.Size.isPrecise());EXPECT_TRUE(ItB->second.Size.hasValue());EXPECT_TRUE(ItB->second.Size.getValue() == 8);}}}TEST_F(MemorySSATest, TestInvariantGroup) {SMDiagnostic E;auto M = parseAssemblyString("declare void @f(i8*)\n""define i8 @test(i8* %p) {\n""entry:\n"" store i8 42, i8* %p, !invariant.group !0\n"" call void @f(i8* %p)\n"" %v = load i8, i8* %p, !invariant.group !0\n"" ret i8 %v\n""}\n""!0 = !{}",E, C);ASSERT_TRUE(M);F = M->getFunction("test");ASSERT_TRUE(F);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAWalker *Walker = Analyses->Walker;auto &BB = F->getEntryBlock();auto &SI = cast<StoreInst>(*BB.begin());auto &Call = cast<CallBase>(*std::next(BB.begin()));auto &LI = cast<LoadInst>(*std::next(std::next(BB.begin())));{MemoryAccess *SAccess = MSSA.getMemoryAccess(&SI);MemoryAccess *LAccess = MSSA.getMemoryAccess(&LI);MemoryAccess *SClobber = Walker->getClobberingMemoryAccess(SAccess);EXPECT_TRUE(MSSA.isLiveOnEntryDef(SClobber));MemoryAccess *LClobber = Walker->getClobberingMemoryAccess(LAccess);EXPECT_EQ(SAccess, LClobber);}// remove store and verify that the memory accesses still make senseMemorySSAUpdater Updater(&MSSA);Updater.removeMemoryAccess(&SI);SI.eraseFromParent();{MemoryAccess *CallAccess = MSSA.getMemoryAccess(&Call);MemoryAccess *LAccess = MSSA.getMemoryAccess(&LI);MemoryAccess *LClobber = Walker->getClobberingMemoryAccess(LAccess);EXPECT_EQ(CallAccess, LClobber);}}static BasicBlock *getBasicBlockByName(Function &F, StringRef Name) {for (BasicBlock &BB : F)if (BB.getName() == Name)return &BB;llvm_unreachable("Expected to find basic block!");}static Instruction *getInstructionByName(Function &F, StringRef Name) {for (BasicBlock &BB : F)for (Instruction &I : BB)if (I.getName() == Name)return &I;llvm_unreachable("Expected to find instruction!");}TEST_F(MemorySSATest, TestVisitedBlocks) {SMDiagnostic E;auto M = parseAssemblyString("define void @test(i64* noalias %P, i64 %N) {\n""preheader.n:\n"" br label %header.n\n""header.n:\n"" %n = phi i64 [ 0, %preheader.n ], [ %inc.n, %latch.n ]\n"" %guard.cond.i = icmp slt i64 0, %N\n"" br i1 %guard.cond.i, label %header.i.check, label %other.i\n""header.i.check:\n"" br label %preheader.i\n""preheader.i:\n"" br label %header.i\n""header.i:\n"" %i = phi i64 [ 0, %preheader.i ], [ %inc.i, %header.i ]\n"" %v1 = load i64, i64* %P, align 8\n"" %v2 = load i64, i64* %P, align 8\n"" %inc.i = add nsw i64 %i, 1\n"" %cmp.i = icmp slt i64 %inc.i, %N\n"" br i1 %cmp.i, label %header.i, label %exit.i\n""exit.i:\n"" br label %commonexit\n""other.i:\n"" br label %commonexit\n""commonexit:\n"" br label %latch.n\n""latch.n:\n"" %inc.n = add nsw i64 %n, 1\n"" %cmp.n = icmp slt i64 %inc.n, %N\n"" br i1 %cmp.n, label %header.n, label %exit.n\n""exit.n:\n"" ret void\n""}\n",E, C);ASSERT_TRUE(M);F = M->getFunction("test");ASSERT_TRUE(F);setupAnalyses();MemorySSA &MSSA = *Analyses->MSSA;MemorySSAUpdater Updater(&MSSA);{// Move %v1 before the terminator of %header.i.checkBasicBlock *BB = getBasicBlockByName(*F, "header.i.check");Instruction *LI = getInstructionByName(*F, "v1");LI->moveBefore(BB->getTerminator());if (MemoryUseOrDef *MUD = MSSA.getMemoryAccess(LI))Updater.moveToPlace(MUD, BB, MemorySSA::BeforeTerminator);// Change the termiantor of %header.i.check to `br label true, label// %preheader.i, label %other.i`BB->getTerminator()->eraseFromParent();ConstantInt *BoolTrue = ConstantInt::getTrue(F->getContext());BranchInst::Create(getBasicBlockByName(*F, "preheader.i"),getBasicBlockByName(*F, "other.i"), BoolTrue, BB);SmallVector<DominatorTree::UpdateType, 4> DTUpdates;DTUpdates.push_back(DominatorTree::UpdateType(DominatorTree::Insert, BB, getBasicBlockByName(*F, "other.i")));Updater.applyUpdates(DTUpdates, Analyses->DT, true);}// After the first moveToPlace(), %other.i is in VisitedBlocks, even after// there is a new edge to %other.i, which makes the second moveToPlace()// traverse incorrectly.{// Move %v2 before the terminator of %preheader.iBasicBlock *BB = getBasicBlockByName(*F, "preheader.i");Instruction *LI = getInstructionByName(*F, "v2");LI->moveBefore(BB->getTerminator());// Check that there is no assertion of "Incomplete phi during partial// rename"if (MemoryUseOrDef *MUD = MSSA.getMemoryAccess(LI))Updater.moveToPlace(MUD, BB, MemorySSA::BeforeTerminator);}}
//===- MemoryProfileInfoTest.cpp - Memory Profile Info Unit Tests-===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/MemoryProfileInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"#include <cstring>using namespace llvm;using namespace llvm::memprof;extern cl::opt<float> MemProfAccessesPerByteColdThreshold;extern cl::opt<unsigned> MemProfMinLifetimeColdThreshold;namespace {class MemoryProfileInfoTest : public testing::Test {protected:std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("MemoryProfileInfoTest", errs());return Mod;}// This looks for a call that has the given value name, which// is the name of the value being assigned the call return value.CallBase *findCall(Function &F, const char *Name = nullptr) {for (auto &BB : F)for (auto &I : BB)if (auto *CB = dyn_cast<CallBase>(&I))if (!Name || CB->getName() == Name)return CB;return nullptr;}};// Test getAllocType helper.// Basic checks on the allocation type for values just above and below// the thresholds.TEST_F(MemoryProfileInfoTest, GetAllocType) {// Long lived with more accesses per byte than threshold is not cold.EXPECT_EQ(getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1,/*MinSize=*/1,/*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1),AllocationType::NotCold);// Long lived with less accesses per byte than threshold is cold.EXPECT_EQ(getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1,/*MinSize=*/1,/*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1),AllocationType::Cold);// Short lived with more accesses per byte than threshold is not cold.EXPECT_EQ(getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1,/*MinSize=*/1,/*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1),AllocationType::NotCold);// Short lived with less accesses per byte than threshold is not cold.EXPECT_EQ(getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1,/*MinSize=*/1,/*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1),AllocationType::NotCold);}// Test buildCallstackMetadata helper.TEST_F(MemoryProfileInfoTest, BuildCallStackMD) {LLVMContext C;MDNode *CallStack = buildCallstackMetadata({1, 2, 3}, C);ASSERT_EQ(CallStack->getNumOperands(), 3u);unsigned ExpectedId = 1;for (auto &Op : CallStack->operands()) {auto *StackId = mdconst::dyn_extract<ConstantInt>(Op);EXPECT_EQ(StackId->getZExtValue(), ExpectedId++);}}// Test CallStackTrie::addCallStack interface taking allocation type and list of// call stack ids.// Check that allocations with a single allocation type along all call stacks// get an attribute instead of memprof metadata.TEST_F(MemoryProfileInfoTest, Attribute) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32* @test() {entry:%call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)%0 = bitcast i8* %call1 to i32*%call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)%1 = bitcast i8* %call2 to i32*ret i32* %1}declare dso_local noalias noundef i8* @malloc(i64 noundef))IR");Function *Func = M->getFunction("test");// First call has all cold contexts.CallStackTrie Trie1;Trie1.addCallStack(AllocationType::Cold, {1, 2});Trie1.addCallStack(AllocationType::Cold, {1, 3, 4});CallBase *Call1 = findCall(*Func, "call1");Trie1.buildAndAttachMIBMetadata(Call1);EXPECT_FALSE(Call1->hasMetadata(LLVMContext::MD_memprof));EXPECT_TRUE(Call1->hasFnAttr("memprof"));EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");// Second call has all non-cold contexts.CallStackTrie Trie2;Trie2.addCallStack(AllocationType::NotCold, {5, 6});Trie2.addCallStack(AllocationType::NotCold, {5, 7, 8});CallBase *Call2 = findCall(*Func, "call2");Trie2.buildAndAttachMIBMetadata(Call2);EXPECT_FALSE(Call2->hasMetadata(LLVMContext::MD_memprof));EXPECT_TRUE(Call2->hasFnAttr("memprof"));EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");}// Test CallStackTrie::addCallStack interface taking allocation type and list of// call stack ids.// Test that an allocation call reached by both cold and non cold call stacks// gets memprof metadata representing the different allocation type contexts.TEST_F(MemoryProfileInfoTest, ColdAndNotColdMIB) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32* @test() {entry:%call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)%0 = bitcast i8* %call to i32*ret i32* %0}declare dso_local noalias noundef i8* @malloc(i64 noundef))IR");Function *Func = M->getFunction("test");CallStackTrie Trie;Trie.addCallStack(AllocationType::Cold, {1, 2});Trie.addCallStack(AllocationType::NotCold, {1, 3});CallBase *Call = findCall(*Func, "call");Trie.buildAndAttachMIBMetadata(Call);EXPECT_FALSE(Call->hasFnAttr("memprof"));EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);ASSERT_EQ(MemProfMD->getNumOperands(), 2u);for (auto &MIBOp : MemProfMD->operands()) {MDNode *MIB = dyn_cast<MDNode>(MIBOp);MDNode *StackMD = getMIBStackNode(MIB);ASSERT_NE(StackMD, nullptr);ASSERT_EQ(StackMD->getNumOperands(), 2u);auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));ASSERT_EQ(StackId->getZExtValue(), 1u);StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));if (StackId->getZExtValue() == 2u)EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);else {ASSERT_EQ(StackId->getZExtValue(), 3u);EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);}}}// Test CallStackTrie::addCallStack interface taking allocation type and list of// call stack ids.// Test that an allocation call reached by multiple call stacks has memprof// metadata with the contexts trimmed to the minimum context required to// identify the allocation type.TEST_F(MemoryProfileInfoTest, TrimmedMIBContext) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32* @test() {entry:%call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)%0 = bitcast i8* %call to i32*ret i32* %0}declare dso_local noalias noundef i8* @malloc(i64 noundef))IR");Function *Func = M->getFunction("test");CallStackTrie Trie;// We should be able to trim the following two and combine into a single MIB// with the cold context {1, 2}.Trie.addCallStack(AllocationType::Cold, {1, 2, 3});Trie.addCallStack(AllocationType::Cold, {1, 2, 4});// We should be able to trim the following two and combine into a single MIB// with the non-cold context {1, 5}.Trie.addCallStack(AllocationType::NotCold, {1, 5, 6});Trie.addCallStack(AllocationType::NotCold, {1, 5, 7});CallBase *Call = findCall(*Func, "call");Trie.buildAndAttachMIBMetadata(Call);EXPECT_FALSE(Call->hasFnAttr("memprof"));EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);ASSERT_EQ(MemProfMD->getNumOperands(), 2u);for (auto &MIBOp : MemProfMD->operands()) {MDNode *MIB = dyn_cast<MDNode>(MIBOp);MDNode *StackMD = getMIBStackNode(MIB);ASSERT_NE(StackMD, nullptr);ASSERT_EQ(StackMD->getNumOperands(), 2u);auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));EXPECT_EQ(StackId->getZExtValue(), 1u);StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));if (StackId->getZExtValue() == 2u)EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);else {ASSERT_EQ(StackId->getZExtValue(), 5u);EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);}}}// Test CallStackTrie::addCallStack interface taking memprof MIB metadata.// Check that allocations annotated with memprof metadata with a single// allocation type get simplified to an attribute.TEST_F(MemoryProfileInfoTest, SimplifyMIBToAttribute) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32* @test() {entry:%call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0%0 = bitcast i8* %call1 to i32*%call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3%1 = bitcast i8* %call2 to i32*ret i32* %1}declare dso_local noalias noundef i8* @malloc(i64 noundef)!0 = !{!1}!1 = !{!2, !"cold"}!2 = !{i64 1, i64 2, i64 3}!3 = !{!4}!4 = !{!5, !"notcold"}!5 = !{i64 4, i64 5, i64 6, i64 7})IR");Function *Func = M->getFunction("test");// First call has all cold contexts.CallStackTrie Trie1;CallBase *Call1 = findCall(*Func, "call1");MDNode *MemProfMD1 = Call1->getMetadata(LLVMContext::MD_memprof);ASSERT_EQ(MemProfMD1->getNumOperands(), 1u);MDNode *MIB1 = dyn_cast<MDNode>(MemProfMD1->getOperand(0));Trie1.addCallStack(MIB1);Trie1.buildAndAttachMIBMetadata(Call1);EXPECT_TRUE(Call1->hasFnAttr("memprof"));EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");// Second call has all non-cold contexts.CallStackTrie Trie2;CallBase *Call2 = findCall(*Func, "call2");MDNode *MemProfMD2 = Call2->getMetadata(LLVMContext::MD_memprof);ASSERT_EQ(MemProfMD2->getNumOperands(), 1u);MDNode *MIB2 = dyn_cast<MDNode>(MemProfMD2->getOperand(0));Trie2.addCallStack(MIB2);Trie2.buildAndAttachMIBMetadata(Call2);EXPECT_TRUE(Call2->hasFnAttr("memprof"));EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");}// Test CallStackTrie::addCallStack interface taking memprof MIB metadata.// Test that allocations annotated with memprof metadata with multiple call// stacks gets new memprof metadata with the contexts trimmed to the minimum// context required to identify the allocation type.TEST_F(MemoryProfileInfoTest, ReTrimMIBContext) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32* @test() {entry:%call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0%0 = bitcast i8* %call to i32*ret i32* %0}declare dso_local noalias noundef i8* @malloc(i64 noundef)!0 = !{!1, !3, !5, !7}!1 = !{!2, !"cold"}!2 = !{i64 1, i64 2, i64 3}!3 = !{!4, !"cold"}!4 = !{i64 1, i64 2, i64 4}!5 = !{!6, !"notcold"}!6 = !{i64 1, i64 5, i64 6}!7 = !{!8, !"notcold"}!8 = !{i64 1, i64 5, i64 7})IR");Function *Func = M->getFunction("test");CallStackTrie Trie;ASSERT_TRUE(Trie.empty());CallBase *Call = findCall(*Func, "call");MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);for (auto &MIBOp : MemProfMD->operands()) {MDNode *MIB = dyn_cast<MDNode>(MIBOp);Trie.addCallStack(MIB);}ASSERT_FALSE(Trie.empty());Trie.buildAndAttachMIBMetadata(Call);// We should be able to trim the first two and combine into a single MIB// with the cold context {1, 2}.// We should be able to trim the second two and combine into a single MIB// with the non-cold context {1, 5}.EXPECT_FALSE(Call->hasFnAttr("memprof"));EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);ASSERT_EQ(MemProfMD->getNumOperands(), 2u);for (auto &MIBOp : MemProfMD->operands()) {MDNode *MIB = dyn_cast<MDNode>(MIBOp);MDNode *StackMD = getMIBStackNode(MIB);ASSERT_NE(StackMD, nullptr);ASSERT_EQ(StackMD->getNumOperands(), 2u);auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));EXPECT_EQ(StackId->getZExtValue(), 1u);StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));if (StackId->getZExtValue() == 2u)EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);else {ASSERT_EQ(StackId->getZExtValue(), 5u);EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);}}}} // end anonymous namespace
//===- MemoryBuiltinsTest.cpp - Tests for utilities in MemoryBuiltins.h ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/MemoryBuiltins.h"#include "llvm/IR/Attributes.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Function.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;namespace {// allocsize should not imply that a function is a traditional allocation// function (e.g. that can be optimized out/...); it just tells us how many// bytes exist at the pointer handed back by the function.TEST(AllocSize, AllocationBuiltinsTest) {LLVMContext Context;Module M("", Context);IntegerType *ArgTy = Type::getInt32Ty(Context);Function *AllocSizeFn = Function::Create(FunctionType::get(Type::getInt8PtrTy(Context), {ArgTy}, false),GlobalValue::ExternalLinkage, "F", &M);AllocSizeFn->addFnAttr(Attribute::getWithAllocSizeArgs(Context, 1, None));// 100 is arbitrary.std::unique_ptr<CallInst> Caller(CallInst::Create(AllocSizeFn, {ConstantInt::get(ArgTy, 100)}));const TargetLibraryInfo *TLI = nullptr;EXPECT_FALSE(isAllocLikeFn(Caller.get(), TLI));// FIXME: We might be able to treat allocsize functions as general allocation// functions.EXPECT_FALSE(isAllocationFn(Caller.get(), TLI));}}
//===- MLModelRunnerTest.cpp - test for MLModelRunner ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/MLModelRunner.h"#include "llvm/Analysis/NoInferenceModelRunner.h"#include "llvm/Analysis/ReleaseModeModelRunner.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {// This is a mock of the kind of AOT-generated model evaluator. It has 2 tensors// of shape {1}, and 'evaluation' adds them.// The interface is the one expected by ReleaseModelRunner.class MockAOTModel final {int64_t A = 0;int64_t B = 0;int64_t R = 0;public:MockAOTModel() = default;int LookupArgIndex(const std::string &Name) {if (Name == "prefix_a")return 0;if (Name == "prefix_b")return 1;return -1;}int LookupResultIndex(const std::string &) { return 0; }void Run() { R = A + B; }void *result_data(int RIndex) {if (RIndex == 0)return &R;return nullptr;}void *arg_data(int Index) {switch (Index) {case 0:return &A;case 1:return &B;default:return nullptr;}}};} // namespace llvmTEST(NoInferenceModelRunner, AccessTensors) {const std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("F1", {1}),TensorSpec::createSpec<int64_t>("F2", {10}),TensorSpec::createSpec<float>("F2", {5}),};LLVMContext Ctx;NoInferenceModelRunner NIMR(Ctx, Inputs);NIMR.getTensor<int64_t>(0)[0] = 1;std::memcpy(NIMR.getTensor<int64_t>(1),std::vector<int64_t>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.data(),10 * sizeof(int64_t));std::memcpy(NIMR.getTensor<float>(2),std::vector<float>{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}.data(),5 * sizeof(float));ASSERT_EQ(NIMR.getTensor<int64_t>(0)[0], 1);ASSERT_EQ(NIMR.getTensor<int64_t>(1)[8], 9);ASSERT_EQ(NIMR.getTensor<float>(2)[1], 0.2f);}TEST(ReleaseModeRunner, NormalUse) {LLVMContext Ctx;std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("a", {1}),TensorSpec::createSpec<int64_t>("b", {1})};auto Evaluator = std::make_unique<ReleaseModeModelRunner<MockAOTModel>>(Ctx, Inputs, "", "prefix_");*Evaluator->getTensor<int64_t>(0) = 1;*Evaluator->getTensor<int64_t>(1) = 2;EXPECT_EQ(Evaluator->evaluate<int64_t>(), 3);EXPECT_EQ(*Evaluator->getTensor<int64_t>(0), 1);EXPECT_EQ(*Evaluator->getTensor<int64_t>(1), 2);}TEST(ReleaseModeRunner, ExtraFeatures) {LLVMContext Ctx;std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("a", {1}),TensorSpec::createSpec<int64_t>("b", {1}),TensorSpec::createSpec<int64_t>("c", {1})};auto Evaluator = std::make_unique<ReleaseModeModelRunner<MockAOTModel>>(Ctx, Inputs, "", "prefix_");*Evaluator->getTensor<int64_t>(0) = 1;*Evaluator->getTensor<int64_t>(1) = 2;*Evaluator->getTensor<int64_t>(2) = -3;EXPECT_EQ(Evaluator->evaluate<int64_t>(), 3);EXPECT_EQ(*Evaluator->getTensor<int64_t>(0), 1);EXPECT_EQ(*Evaluator->getTensor<int64_t>(1), 2);EXPECT_EQ(*Evaluator->getTensor<int64_t>(2), -3);}TEST(ReleaseModeRunner, ExtraFeaturesOutOfOrder) {LLVMContext Ctx;std::vector<TensorSpec> Inputs{TensorSpec::createSpec<int64_t>("a", {1}),TensorSpec::createSpec<int64_t>("c", {1}),TensorSpec::createSpec<int64_t>("b", {1}),};auto Evaluator = std::make_unique<ReleaseModeModelRunner<MockAOTModel>>(Ctx, Inputs, "", "prefix_");*Evaluator->getTensor<int64_t>(0) = 1; // a*Evaluator->getTensor<int64_t>(1) = 2; // c*Evaluator->getTensor<int64_t>(2) = -3; // bEXPECT_EQ(Evaluator->evaluate<int64_t>(), -2); // a + bEXPECT_EQ(*Evaluator->getTensor<int64_t>(0), 1);EXPECT_EQ(*Evaluator->getTensor<int64_t>(1), 2);EXPECT_EQ(*Evaluator->getTensor<int64_t>(2), -3);}
//===- LoopNestTest.cpp - LoopNestAnalysis unit tests ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/LoopNestAnalysis.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;/// Build the loop nest analysis for a loop nest and run the given test \p Test.static void runTest(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI, ScalarEvolution &SE)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AssumptionCache AC(*F);DominatorTree DT(*F);LoopInfo LI(DT);ScalarEvolution SE(*F, TLI, AC, DT, LI);Test(*F, LI, SE);}static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,const char *ModuleStr) {SMDiagnostic Err;return parseAssemblyString(ModuleStr, Err, Context);}static Instruction *getInstructionByName(Function &F, StringRef Name) {for (BasicBlock &BB : F)for (Instruction &I : BB)if (I.getName() == Name)return &I;llvm_unreachable("Expected to find instruction!");}TEST(LoopNestTest, PerfectLoopNest) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i64 signext %nx, i64 signext %ny) {\n""entry:\n"" br label %for.outer\n""for.outer:\n"" %i = phi i64 [ 0, %entry ], [ %inc13, %for.outer.latch ]\n"" %cmp21 = icmp slt i64 0, %ny\n"" br i1 %cmp21, label %for.inner.preheader, label %for.outer.latch\n""for.inner.preheader:\n"" br label %for.inner\n""for.inner:\n"" %j = phi i64 [ 0, %for.inner.preheader ], [ %inc, %for.inner.latch ]\n"" br label %for.inner.latch\n""for.inner.latch:\n"" %inc = add nsw i64 %j, 1\n"" %cmp2 = icmp slt i64 %inc, %ny\n"" br i1 %cmp2, label %for.inner, label %for.inner.exit\n""for.inner.exit:\n"" br label %for.outer.latch\n""for.outer.latch:\n"" %inc13 = add nsw i64 %i, 1\n"" %cmp = icmp slt i64 %inc13, %nx\n"" br i1 %cmp, label %for.outer, label %for.outer.exit\n""for.outer.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runTest(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// Skip the first basic block (entry), get to the outer loop header.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.outer");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);LoopNest LN(*L, SE);EXPECT_TRUE(LN.areAllLoopsSimplifyForm());// Ensure that we can identify the outermost loop in the nest.const Loop &OL = LN.getOutermostLoop();EXPECT_EQ(OL.getName(), "for.outer");// Ensure that we can identify the innermost loop in the nest.const Loop *IL = LN.getInnermostLoop();EXPECT_NE(IL, nullptr);EXPECT_EQ(IL->getName(), "for.inner");// Ensure the loop nest is recognized as having 2 loops.const ArrayRef<Loop*> Loops = LN.getLoops();EXPECT_EQ(Loops.size(), 2ull);// Ensure that we can obtain loops by depth.LoopVectorTy LoopsAtDepth1 = LN.getLoopsAtDepth(1);EXPECT_EQ(LoopsAtDepth1.size(), 1u);EXPECT_EQ(LoopsAtDepth1[0], &OL);LoopVectorTy LoopsAtDepth2 = LN.getLoopsAtDepth(2);EXPECT_EQ(LoopsAtDepth2.size(), 1u);EXPECT_EQ(LoopsAtDepth2[0], IL);// Ensure that we can obtain the loop index of a given loop, and get back// the loop with that index.EXPECT_EQ(LN.getLoop(LN.getLoopIndex(OL)), &OL);EXPECT_EQ(LN.getLoop(LN.getLoopIndex(*IL)), IL);// Ensure the loop nest is recognized as perfect in its entirety.const SmallVector<LoopVectorTy, 4> &PLV = LN.getPerfectLoops(SE);EXPECT_EQ(PLV.size(), 1ull);EXPECT_EQ(PLV.front().size(), 2ull);// Ensure the nest depth and perfect nest depth are computed correctly.EXPECT_EQ(LN.getNestDepth(), 2u);EXPECT_EQ(LN.getMaxPerfectDepth(), 2u);EXPECT_TRUE(LN.getInterveningInstructions(OL, *IL, SE).empty());});}TEST(LoopNestTest, ImperfectLoopNest) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i32 signext %nx, i32 signext %ny, i32 signext %nk) {\n""entry:\n"" br label %loop.i\n""loop.i:\n"" %i = phi i32 [ 0, %entry ], [ %inci, %for.inci ]\n"" %cmp21 = icmp slt i32 0, %ny\n"" br i1 %cmp21, label %loop.j.preheader, label %for.inci\n""loop.j.preheader:\n"" br label %loop.j\n""loop.j:\n"" %j = phi i32 [ %incj, %for.incj ], [ 0, %loop.j.preheader ]\n"" %cmp22 = icmp slt i32 0, %nk\n"" br i1 %cmp22, label %loop.k.preheader, label %for.incj\n""loop.k.preheader:\n"" call void @bar()\n"" br label %loop.k\n""loop.k:\n"" %k = phi i32 [ %inck, %for.inck ], [ 0, %loop.k.preheader ]\n"" br label %for.inck\n""for.inck:\n"" %inck = add nsw i32 %k, 1\n"" %cmp5 = icmp slt i32 %inck, %nk\n"" br i1 %cmp5, label %loop.k, label %for.incj.loopexit\n""for.incj.loopexit:\n"" br label %for.incj\n""for.incj:\n"" %incj = add nsw i32 %j, 1\n"" %cmp2 = icmp slt i32 %incj, %ny\n"" br i1 %cmp2, label %loop.j, label %for.inci.loopexit\n""for.inci.loopexit:\n"" br label %for.inci\n""for.inci:\n"" %inci = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inci, %nx\n"" br i1 %cmp, label %loop.i, label %loop.i.end\n""loop.i.end:\n"" ret void\n""}\n""declare void @bar()\n";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runTest(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// Skip the first basic block (entry), get to the outermost loop header.BasicBlock *Header = &*(++FI);assert(Header->getName() == "loop.i");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);LoopNest LN(*L, SE);EXPECT_TRUE(LN.areAllLoopsSimplifyForm());dbgs() << "LN: " << LN << "\n";// Ensure that we can identify the outermost loop in the nest.const Loop &OL = LN.getOutermostLoop();EXPECT_EQ(OL.getName(), "loop.i");// Ensure that we can identify the innermost loop in the nest.const Loop *IL = LN.getInnermostLoop();EXPECT_NE(IL, nullptr);EXPECT_EQ(IL->getName(), "loop.k");// Ensure the loop nest is recognized as having 3 loops.const ArrayRef<Loop*> Loops = LN.getLoops();EXPECT_EQ(Loops.size(), 3ull);// Ensure the loop nest is recognized as having 2 separate perfect loops groups.const SmallVector<LoopVectorTy, 4> &PLV = LN.getPerfectLoops(SE);EXPECT_EQ(PLV.size(), 2ull);EXPECT_EQ(PLV.front().size(), 2ull);EXPECT_EQ(PLV.back().size(), 1ull);// Ensure the nest depth and perfect nest depth are computed correctly.EXPECT_EQ(LN.getNestDepth(), 3u);EXPECT_EQ(LN.getMaxPerfectDepth(), 2u);EXPECT_TRUE(LN.getInterveningInstructions(OL, *IL, SE).empty());});}TEST(LoopNestTest, InterveningInstrLoopNest) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i64 signext %nx, i64 signext %ny, i32* noalias %A, i32 ""%op0, i32 %op1){\n""entry:\n"" br label %for.outer\n""for.outer:\n"" %i = phi i64 [ 0, %entry ], [ %inc13, %for.outer.latch ]\n"" %cmp21 = icmp slt i64 0, %ny\n"" call void @outerheader()\n"" br i1 %cmp21, label %for.inner.preheader, label %for.outer.latch\n""for.inner.preheader:\n"" %varr = getelementptr inbounds i32, i32* %A, i64 5\n"" store i32 5, i32* %varr, align 4\n"" call void @innerpreheader()\n"" br label %for.inner\n""for.inner:\n"" %j = phi i64 [ 0, %for.inner.preheader ], [ %inc, %for.inner.latch ]\n"" br label %for.inner.latch\n""for.inner.latch:\n"" %inc = add nsw i64 %j, 1\n"" %cmp2 = icmp slt i64 %inc, %ny\n"" br i1 %cmp2, label %for.inner, label %for.inner.exit\n""for.inner.exit:\n"" %varr1 = getelementptr inbounds i32, i32* %A, i64 5\n"" call void @innerexit()\n"" br label %for.outer.latch\n""for.outer.latch:\n"" %inc13 = add nsw i64 %i, 1\n"" call void @outerlatch()\n"" %cmp = icmp slt i64 %inc13, %nx\n"" br i1 %cmp, label %for.outer, label %for.outer.exit\n""for.outer.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n""declare void @innerpreheader()\n""declare void @outerheader()\n""declare void @outerlatch()\n""declare void @innerexit()\n";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runTest(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// Skip the first basic block (entry), get to the outer loop header.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.outer");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);LoopNest LN(*L, SE);EXPECT_TRUE(LN.areAllLoopsSimplifyForm());// Ensure that we can identify the outermost loop in the nest.const Loop &OL = LN.getOutermostLoop();EXPECT_EQ(OL.getName(), "for.outer");// Ensure that we can identify the innermost loop in the nest.const Loop *IL = LN.getInnermostLoop();EXPECT_NE(IL, nullptr);EXPECT_EQ(IL->getName(), "for.inner");// Ensure the loop nest is recognized as having 2 loops.const ArrayRef<Loop *> Loops = LN.getLoops();EXPECT_EQ(Loops.size(), 2ull);// Ensure the loop nest is not recognized as perfect in its entirety.const SmallVector<LoopVectorTy, 4> &PLV = LN.getPerfectLoops(SE);EXPECT_EQ(PLV.size(), 2ull);EXPECT_EQ(PLV.front().size(), 1ull);EXPECT_EQ(PLV.back().size(), 1ull);// Ensure the nest depth and perfect nest depth are computed correctly.EXPECT_EQ(LN.getNestDepth(), 2u);EXPECT_EQ(LN.getMaxPerfectDepth(), 1u);// Ensure enclosed instructions are recognizedconst LoopNest::InstrVectorTy InstrV =LN.getInterveningInstructions(OL, *IL, SE);EXPECT_EQ(InstrV.size(), 5u);Instruction *SI = getInstructionByName(F, "varr")->getNextNode();Instruction *CI = SI->getNextNode();Instruction *OLH =getInstructionByName(F, "i")->getNextNode()->getNextNode();Instruction *OLL = getInstructionByName(F, "inc13")->getNextNode();Instruction *IE = getInstructionByName(F, "varr1")->getNextNode();EXPECT_EQ(InstrV.front(), OLH);EXPECT_EQ(InstrV[1], OLL);EXPECT_EQ(InstrV[2], IE);EXPECT_EQ(InstrV[3], SI);EXPECT_EQ(InstrV.back(), CI);});}
//===- LoopInfoTest.cpp - LoopInfo unit tests -----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;/// Build the loop info for the function and run the Test.static voidrunWithLoopInfo(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;// Compute the dominator tree and the loop info for the function.DominatorTree DT(*F);LoopInfo LI(DT);Test(*F, LI);}/// Build the loop info and scalar evolution for the function and run the Test.static void runWithLoopInfoPlus(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI, ScalarEvolution &SE)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AssumptionCache AC(*F);DominatorTree DT(*F);LoopInfo LI(DT);ScalarEvolution SE(*F, TLI, AC, DT, LI);Test(*F, LI, SE);}static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,const char *ModuleStr) {SMDiagnostic Err;return parseAssemblyString(ModuleStr, Err, Context);}// This tests that for a loop with a single latch, we get the loop id from// its only latch, even in case the loop may not be in a simplified form.TEST(LoopInfoTest, LoopWithSingleLatch) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i32 %n) {\n""entry:\n"" br i1 undef, label %for.cond, label %for.end\n""for.cond:\n"" %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]\n"" %cmp = icmp slt i32 %i.0, %n\n"" br i1 %cmp, label %for.inc, label %for.end\n""for.inc:\n"" %inc = add nsw i32 %i.0, 1\n"" br label %for.cond, !llvm.loop !0\n""for.end:\n"" ret void\n""}\n""!0 = distinct !{!0, !1}\n""!1 = !{!\"llvm.loop.distribute.enable\", i1 true}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.cond");Loop *L = LI.getLoopFor(Header);// This loop is not in simplified form.EXPECT_FALSE(L->isLoopSimplifyForm());// Analyze the loop metadata id.bool loopIDFoundAndSet = false;// Try to get and set the metadata id for the loop.if (MDNode *D = L->getLoopID()) {L->setLoopID(D);loopIDFoundAndSet = true;}// We must have successfully found and set the loop id in the// only latch the loop has.EXPECT_TRUE(loopIDFoundAndSet);});}// Test loop id handling for a loop with multiple latches.TEST(LoopInfoTest, LoopWithMultipleLatches) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i32 %n) {\n""entry:\n"" br i1 undef, label %for.cond, label %for.end\n""for.cond:\n"" %i.0 = phi i32 [ 0, %entry ], [ %inc, %latch.1 ], [ %inc, %latch.2 ]\n"" %inc = add nsw i32 %i.0, 1\n"" %cmp = icmp slt i32 %i.0, %n\n"" br i1 %cmp, label %latch.1, label %for.end\n""latch.1:\n"" br i1 undef, label %for.cond, label %latch.2, !llvm.loop !0\n""latch.2:\n"" br label %for.cond, !llvm.loop !0\n""for.end:\n"" ret void\n""}\n""!0 = distinct !{!0, !1}\n""!1 = !{!\"llvm.loop.distribute.enable\", i1 true}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.cond");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);// This loop is not in simplified form.EXPECT_FALSE(L->isLoopSimplifyForm());// Try to get and set the metadata id for the loop.MDNode *OldLoopID = L->getLoopID();EXPECT_NE(OldLoopID, nullptr);MDNode *NewLoopID = MDNode::get(Context, {nullptr});// Set operand 0 to refer to the loop id itself.NewLoopID->replaceOperandWith(0, NewLoopID);L->setLoopID(NewLoopID);EXPECT_EQ(L->getLoopID(), NewLoopID);EXPECT_NE(L->getLoopID(), OldLoopID);L->setLoopID(OldLoopID);EXPECT_EQ(L->getLoopID(), OldLoopID);EXPECT_NE(L->getLoopID(), NewLoopID);});}TEST(LoopInfoTest, PreorderTraversals) {const char *ModuleStr = "define void @f() {\n""entry:\n"" br label %loop.0\n""loop.0:\n"" br i1 undef, label %loop.0.0, label %loop.1\n""loop.0.0:\n"" br i1 undef, label %loop.0.0, label %loop.0.1\n""loop.0.1:\n"" br i1 undef, label %loop.0.1, label %loop.0.2\n""loop.0.2:\n"" br i1 undef, label %loop.0.2, label %loop.0\n""loop.1:\n"" br i1 undef, label %loop.1.0, label %end\n""loop.1.0:\n"" br i1 undef, label %loop.1.0, label %loop.1.1\n""loop.1.1:\n"" br i1 undef, label %loop.1.1, label %loop.1.2\n""loop.1.2:\n"" br i1 undef, label %loop.1.2, label %loop.1\n""end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);Function &F = *M->begin();DominatorTree DT(F);LoopInfo LI;LI.analyze(DT);Function::iterator I = F.begin();ASSERT_EQ("entry", I->getName());++I;Loop &L_0 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.0", L_0.getHeader()->getName());Loop &L_0_0 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.0.0", L_0_0.getHeader()->getName());Loop &L_0_1 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.0.1", L_0_1.getHeader()->getName());Loop &L_0_2 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.0.2", L_0_2.getHeader()->getName());Loop &L_1 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.1", L_1.getHeader()->getName());Loop &L_1_0 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.1.0", L_1_0.getHeader()->getName());Loop &L_1_1 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.1.1", L_1_1.getHeader()->getName());Loop &L_1_2 = *LI.getLoopFor(&*I++);ASSERT_EQ("loop.1.2", L_1_2.getHeader()->getName());auto Preorder = LI.getLoopsInPreorder();ASSERT_EQ(8u, Preorder.size());EXPECT_EQ(&L_0, Preorder[0]);EXPECT_EQ(&L_0_0, Preorder[1]);EXPECT_EQ(&L_0_1, Preorder[2]);EXPECT_EQ(&L_0_2, Preorder[3]);EXPECT_EQ(&L_1, Preorder[4]);EXPECT_EQ(&L_1_0, Preorder[5]);EXPECT_EQ(&L_1_1, Preorder[6]);EXPECT_EQ(&L_1_2, Preorder[7]);auto ReverseSiblingPreorder = LI.getLoopsInReverseSiblingPreorder();ASSERT_EQ(8u, ReverseSiblingPreorder.size());EXPECT_EQ(&L_1, ReverseSiblingPreorder[0]);EXPECT_EQ(&L_1_2, ReverseSiblingPreorder[1]);EXPECT_EQ(&L_1_1, ReverseSiblingPreorder[2]);EXPECT_EQ(&L_1_0, ReverseSiblingPreorder[3]);EXPECT_EQ(&L_0, ReverseSiblingPreorder[4]);EXPECT_EQ(&L_0_2, ReverseSiblingPreorder[5]);EXPECT_EQ(&L_0_1, ReverseSiblingPreorder[6]);EXPECT_EQ(&L_0_0, ReverseSiblingPreorder[7]);}TEST(LoopInfoTest, CanonicalLoop) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopWithInverseGuardSuccs) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp sge i32 0, %ub\n"" br i1 %guardcmp, label %for.end, label %for.preheader\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopWithSwappedGuardCmp) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp sgt i32 %ub, 0\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp sge i32 %inc, %ub\n"" br i1 %cmp, label %for.exit, label %for.body\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopWithInverseLatchSuccs) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp sge i32 %inc, %ub\n"" br i1 %cmp, label %for.exit, label %for.body\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopWithLatchCmpNE) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp ne i32 %i, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopWithGuardCmpSLE) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %ubPlusOne = add i32 %ub, 1\n"" %guardcmp = icmp sle i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp ne i32 %i, %ubPlusOne\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ubPlusOne");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopNonConstantStep) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub, i32 %step) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = zext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, %step\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");EXPECT_EQ(Bounds->getStepValue()->getName(), "step");EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(), Loop::LoopBounds::Direction::Unknown);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopUnsignedBounds) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp ult i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = zext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add i32 %i, 1\n"" %cmp = icmp ult i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_ULT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, DecreasingLoop) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ %ub, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = sub nsw i32 %i, 1\n"" %cmp = icmp sgt i32 %inc, 0\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);EXPECT_EQ(Bounds->getInitialIVValue().getName(), "ub");EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_EQ(StepValue, nullptr);ConstantInt *FinalIVValue =dyn_cast<ConstantInt>(&Bounds->getFinalIVValue());EXPECT_TRUE(FinalIVValue && FinalIVValue->isZero());EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SGT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Decreasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, CannotFindDirection) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub, i32 %step) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, %step\n"" %cmp = icmp ne i32 %i, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader// - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");EXPECT_EQ(Bounds->getStepValue()->getName(), "step");EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(),ICmpInst::BAD_ICMP_PREDICATE);EXPECT_EQ(Bounds->getDirection(), Loop::LoopBounds::Direction::Unknown);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, ZextIndVar) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %indvars.iv = phi i64 [ 0, %for.preheader ], [ %indvars.iv.next, %for.body ]\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %inc = add nsw i32 %i, 1\n"" %wide.trip.count = zext i32 %ub to i64\n"" %exitcond = icmp ne i64 %indvars.iv.next, %wide.trip.count\n"" br i1 %exitcond, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "indvars.iv.next");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "wide.trip.count");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_NE);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "indvars.iv");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, MultiExitingLoop) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub, i1 %cond) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body.1 ]\n"" br i1 %cond, label %for.body.1, label %for.exit\n""for.body.1:\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());});}TEST(LoopInfoTest, MultiExitLoop) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub, i1 %cond) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body.1 ]\n"" br i1 %cond, label %for.body.1, label %for.exit\n""for.body.1:\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit.1\n""for.exit:\n"" br label %for.end\n""for.exit.1:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), nullptr);EXPECT_FALSE(L->isGuarded());});}TEST(LoopInfoTest, UnguardedLoop) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %entry ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), nullptr);EXPECT_FALSE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, UnguardedLoopWithControlFlow) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub, i1 %cond) {\n""entry:\n"" br i1 %cond, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopNest) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.outer.preheader, label %for.end\n""for.outer.preheader:\n"" br label %for.outer\n""for.outer:\n"" %j = phi i32 [ 0, %for.outer.preheader ], [ %inc.outer, %for.outer.latch ]\n"" br i1 %guardcmp, label %for.inner.preheader, label %for.outer.latch\n""for.inner.preheader:\n"" br label %for.inner\n""for.inner:\n"" %i = phi i32 [ 0, %for.inner.preheader ], [ %inc, %for.inner ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.inner, label %for.inner.exit\n""for.inner.exit:\n"" br label %for.outer.latch\n""for.outer.latch:\n"" %inc.outer = add nsw i32 %j, 1\n"" %cmp.outer = icmp slt i32 %inc.outer, %ub\n"" br i1 %cmp.outer, label %for.outer, label %for.outer.exit\n""for.outer.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *OuterGuard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.outer.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.outer");BranchInst *InnerGuard = dyn_cast<BranchInst>(Header->getTerminator());Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc.outer");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "j");EXPECT_EQ(L->getLoopGuardBranch(), OuterGuard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());// Next two basic blocks are for.outer and for.inner.preheader - skip// them.++FI;Header = &*(++FI);assert(Header->getName() == "for.inner");L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> InnerBounds = L->getBounds(SE);EXPECT_NE(InnerBounds, None);InitialIVValue =dyn_cast<ConstantInt>(&InnerBounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(InnerBounds->getStepInst().getName(), "inc");StepValue = dyn_cast_or_null<ConstantInt>(InnerBounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(InnerBounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(InnerBounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(InnerBounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");EXPECT_EQ(L->getLoopGuardBranch(), InnerGuard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, AuxiliaryIV) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %aux = phi i32 [ 0, %for.preheader ], [ %auxinc, %for.body ]\n"" %loopvariant = phi i32 [ 0, %for.preheader ], [ %loopvariantinc, %for.body ]\n"" %usedoutside = phi i32 [ 0, %for.preheader ], [ %usedoutsideinc, %for.body ]\n"" %mulopcode = phi i32 [ 0, %for.preheader ], [ %mulopcodeinc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %mulopcodeinc = mul nsw i32 %mulopcode, 5\n"" %usedoutsideinc = add nsw i32 %usedoutside, 5\n"" %loopvariantinc = add nsw i32 %loopvariant, %i\n"" %auxinc = add nsw i32 %aux, 5\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.body, label %for.exit\n""for.exit:\n"" %lcssa = phi i32 [ %usedoutside, %for.body ]\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Entry = &*(FI);BranchInst *Guard = dyn_cast<BranchInst>(Entry->getTerminator());// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);Optional<Loop::LoopBounds> Bounds = L->getBounds(SE);EXPECT_NE(Bounds, None);ConstantInt *InitialIVValue =dyn_cast<ConstantInt>(&Bounds->getInitialIVValue());EXPECT_TRUE(InitialIVValue && InitialIVValue->isZero());EXPECT_EQ(Bounds->getStepInst().getName(), "inc");ConstantInt *StepValue =dyn_cast_or_null<ConstantInt>(Bounds->getStepValue());EXPECT_TRUE(StepValue && StepValue->isOne());EXPECT_EQ(Bounds->getFinalIVValue().getName(), "ub");EXPECT_EQ(Bounds->getCanonicalPredicate(), ICmpInst::ICMP_SLT);EXPECT_EQ(Bounds->getDirection(),Loop::LoopBounds::Direction::Increasing);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "i");BasicBlock::iterator II = Header->begin();PHINode &Instruction_i = cast<PHINode>(*(II));EXPECT_TRUE(L->isAuxiliaryInductionVariable(Instruction_i, SE));PHINode &Instruction_aux = cast<PHINode>(*(++II));EXPECT_TRUE(L->isAuxiliaryInductionVariable(Instruction_aux, SE));PHINode &Instruction_loopvariant = cast<PHINode>(*(++II));EXPECT_FALSE(L->isAuxiliaryInductionVariable(Instruction_loopvariant, SE));PHINode &Instruction_usedoutside = cast<PHINode>(*(++II));EXPECT_FALSE(L->isAuxiliaryInductionVariable(Instruction_usedoutside, SE));PHINode &Instruction_mulopcode = cast<PHINode>(*(++II));EXPECT_FALSE(L->isAuxiliaryInductionVariable(Instruction_mulopcode, SE));EXPECT_EQ(L->getLoopGuardBranch(), Guard);EXPECT_TRUE(L->isGuarded());EXPECT_TRUE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopNotInSimplifyForm) {const char *ModuleStr ="define void @foo(i32 %n) {\n""entry:\n"" %guard.cmp = icmp sgt i32 %n, 0\n"" br i1 %guard.cmp, label %for.cond, label %for.end\n""for.cond:\n"" %i.0 = phi i32 [ 0, %entry ], [ %inc, %latch.1 ], [ %inc, %latch.2 ]\n"" %inc = add nsw i32 %i.0, 1\n"" %cmp = icmp slt i32 %i.0, %n\n"" br i1 %cmp, label %latch.1, label %for.end\n""latch.1:\n"" br i1 undef, label %for.cond, label %latch.2\n""latch.2:\n"" br label %for.cond\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header && "No header");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);EXPECT_FALSE(L->isLoopSimplifyForm());// No loop guard because loop in not in simplify form.EXPECT_EQ(L->getLoopGuardBranch(), nullptr);EXPECT_FALSE(L->isGuarded());});}TEST(LoopInfoTest, LoopLatchNotExiting) {const char *ModuleStr ="define void @foo(i32* %A, i32 %ub) {\n""entry:\n"" %guardcmp = icmp slt i32 0, %ub\n"" br i1 %guardcmp, label %for.preheader, label %for.end\n""for.preheader:\n"" br label %for.body\n""for.body:\n"" %i = phi i32 [ 0, %for.preheader ], [ %inc, %for.body ]\n"" %idxprom = sext i32 %i to i64\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom\n"" store i32 %i, i32* %arrayidx, align 4\n"" %inc = add nsw i32 %i, 1\n"" %cmp = icmp slt i32 %inc, %ub\n"" br i1 %cmp, label %for.latch, label %for.exit\n""for.latch:\n"" br label %for.body\n""for.exit:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// First two basic block are entry and for.preheader - skip them.++FI;BasicBlock *Header = &*(++FI);BasicBlock *Latch = &*(++FI);assert(Header && "No header");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);EXPECT_TRUE(L->isLoopSimplifyForm());EXPECT_EQ(L->getLoopLatch(), Latch);EXPECT_FALSE(L->isLoopExiting(Latch));// No loop guard becuase loop is not exiting on latch.EXPECT_EQ(L->getLoopGuardBranch(), nullptr);EXPECT_FALSE(L->isGuarded());});}// Examine getUniqueExitBlocks/getUniqueNonLatchExitBlocks functions.TEST(LoopInfoTest, LoopUniqueExitBlocks) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i32 %n, i1 %cond) {\n""entry:\n"" br label %for.cond\n""for.cond:\n"" %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]\n"" %cmp = icmp slt i32 %i.0, %n\n"" br i1 %cond, label %for.inc, label %for.end1\n""for.inc:\n"" %inc = add nsw i32 %i.0, 1\n"" br i1 %cmp, label %for.cond, label %for.end2, !llvm.loop !0\n""for.end1:\n"" br label %for.end\n""for.end2:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n""!0 = distinct !{!0, !1}\n""!1 = !{!\"llvm.loop.distribute.enable\", i1 true}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.cond");Loop *L = LI.getLoopFor(Header);SmallVector<BasicBlock *, 2> Exits;// This loop has 2 unique exits.L->getUniqueExitBlocks(Exits);EXPECT_TRUE(Exits.size() == 2);// And one unique non latch exit.Exits.clear();L->getUniqueNonLatchExitBlocks(Exits);EXPECT_TRUE(Exits.size() == 1);});}// Regression test for getUniqueNonLatchExitBlocks functions.// It should detect the exit if it comes from both latch and non-latch blocks.TEST(LoopInfoTest, LoopNonLatchUniqueExitBlocks) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i32 %n, i1 %cond) {\n""entry:\n"" br label %for.cond\n""for.cond:\n"" %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]\n"" %cmp = icmp slt i32 %i.0, %n\n"" br i1 %cond, label %for.inc, label %for.end\n""for.inc:\n"" %inc = add nsw i32 %i.0, 1\n"" br i1 %cmp, label %for.cond, label %for.end, !llvm.loop !0\n""for.end:\n"" ret void\n""}\n""!0 = distinct !{!0, !1}\n""!1 = !{!\"llvm.loop.distribute.enable\", i1 true}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.cond");Loop *L = LI.getLoopFor(Header);SmallVector<BasicBlock *, 2> Exits;// This loop has 1 unique exit.L->getUniqueExitBlocks(Exits);EXPECT_TRUE(Exits.size() == 1);// And one unique non latch exit.Exits.clear();L->getUniqueNonLatchExitBlocks(Exits);EXPECT_TRUE(Exits.size() == 1);});}// Test that a pointer-chasing loop is not rotated.TEST(LoopInfoTest, LoopNotRotated) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i32* %elem) {\n""entry:\n"" br label %while.cond\n""while.cond:\n"" %elem.addr.0 = phi i32* [ %elem, %entry ], [ %incdec.ptr, %while.body ""]\n"" %tobool = icmp eq i32* %elem.addr.0, null\n"" br i1 %tobool, label %while.end, label %while.body\n""while.body:\n"" %incdec.ptr = getelementptr inbounds i32, i32* %elem.addr.0, i64 1\n"" br label %while.cond\n""while.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "while.cond");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);// This loop is in simplified form.EXPECT_TRUE(L->isLoopSimplifyForm());// This loop is not rotated.EXPECT_FALSE(L->isRotatedForm());});}TEST(LoopInfoTest, LoopUserBranch) {const char *ModuleStr ="target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n""define void @foo(i32* %B, i64 signext %nx, i1 %cond) {\n""entry:\n"" br i1 %cond, label %bb, label %guard\n""guard:\n"" %cmp.guard = icmp slt i64 0, %nx\n"" br i1 %cmp.guard, label %for.i.preheader, label %for.end\n""for.i.preheader:\n"" br label %for.i\n""for.i:\n"" %i = phi i64 [ 0, %for.i.preheader ], [ %inc13, %for.i ]\n"" %Bi = getelementptr inbounds i32, i32* %B, i64 %i\n"" store i32 0, i32* %Bi, align 4\n"" %inc13 = add nsw i64 %i, 1\n"" %cmp = icmp slt i64 %inc13, %nx\n"" br i1 %cmp, label %for.i, label %for.i.exit\n""for.i.exit:\n"" br label %bb\n""bb:\n"" br label %for.end\n""for.end:\n"" ret void\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfo(*M, "foo", [&](Function &F, LoopInfo &LI) {Function::iterator FI = F.begin();FI = ++FI;assert(FI->getName() == "guard");FI = ++FI;BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.i");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);// L should not have a guard branchEXPECT_EQ(L->getLoopGuardBranch(), nullptr);});}TEST(LoopInfoTest, LoopInductionVariable) {const char *ModuleStr ="define i32 @foo(i32* %addr) {\n""entry:\n"" br label %for.body\n""for.body:\n"" %sum.08 = phi i32 [ 0, %entry ], [ %add, %for.body ]\n"" %addr.addr.06 = phi i32* [ %addr, %entry ], [ %incdec.ptr, %for.body ""]\n"" %count.07 = phi i32 [ 6000, %entry ], [ %dec, %for.body ]\n"" %0 = load i32, i32* %addr.addr.06, align 4\n"" %add = add nsw i32 %0, %sum.08\n"" %dec = add nsw i32 %count.07, -1\n"" %incdec.ptr = getelementptr inbounds i32, i32* %addr.addr.06, i64 1\n"" %cmp = icmp ugt i32 %count.07, 1\n"" br i1 %cmp, label %for.body, label %for.end\n""for.end:\n"" %cmp1 = icmp eq i32 %add, -1\n"" %conv = zext i1 %cmp1 to i32\n"" ret i32 %conv\n""}\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *Header = &*(++FI);Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);EXPECT_EQ(L->getInductionVariable(SE)->getName(), "count.07");});}// Test that we correctly identify tokens breaching LCSSA form.TEST(LoopInfoTest, TokenLCSSA) {const char *ModuleStr ="define void @test() gc \"statepoint-example\" {\n""entry:\n"" br label %outer_loop\n""outer_loop:\n"" br label %inner_loop\n""inner_loop:\n"" %token = call token (i64, i32, i8 addrspace(1)* (i64, i32, i32, ""i32)*, i32, i32, ...) ""@llvm.experimental.gc.statepoint.p0f_p1i8i64i32i32i32f(i64 2882400000, ""i32 0, i8 addrspace(1)* (i64, i32, i32, i32)* nonnull elementtype(i8 ""addrspace(1)* (i64, i32, i32, i32)) @foo, i32 4, i32 0, i64 undef, i32 ""5, i32 5, i32 undef, i32 0, i32 0) [ \"deopt\"(), \"gc-live\"(i8 ""addrspace(1)* undef) ]\n"" br i1 undef, label %inner_loop, label %outer_backedge\n""outer_backedge:\n"" br i1 undef, label %outer_loop, label %exit\n""exit:\n"" %tmp35 = call coldcc i8 addrspace(1)* ""@llvm.experimental.gc.relocate.p1i8(token %token, i32 0, i32 0) ; ""(undef, undef)\n"" ret void\n""}\n""declare i8 addrspace(1)* @foo(i64, i32, i32, i32)\n""declare i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token, i32 ""immarg, i32 immarg) #0\n""declare token ""@llvm.experimental.gc.statepoint.p0f_p1i8i64i32i32i32f(i64 immarg, i32 ""immarg, i8 addrspace(1)* (i64, i32, i32, i32)*, i32 immarg, i32 immarg, ""...)\n""attributes #0 = { nounwind readnone }\n";// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runWithLoopInfoPlus(*M, "test",[&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();BasicBlock *OuterHeader = &*(++FI);Loop *OuterLoop = LI.getLoopFor(OuterHeader);BasicBlock *InnerHeader = &*(++FI);Loop *InnerLoop = LI.getLoopFor(InnerHeader);EXPECT_NE(OuterLoop, nullptr);EXPECT_NE(InnerLoop, nullptr);DominatorTree DT(F);EXPECT_TRUE(OuterLoop->isLCSSAForm(DT, /*IgnoreTokens*/ true));EXPECT_FALSE(OuterLoop->isLCSSAForm(DT, /*IgnoreTokens*/ false));EXPECT_TRUE(InnerLoop->isLCSSAForm(DT, /*IgnoreTokens*/ true));EXPECT_FALSE(InnerLoop->isLCSSAForm(DT, /*IgnoreTokens*/ false));EXPECT_TRUE(OuterLoop->isRecursivelyLCSSAForm(DT, LI, /*IgnoreTokens*/ true));EXPECT_FALSE(OuterLoop->isRecursivelyLCSSAForm(DT, LI, /*IgnoreTokens*/ false));EXPECT_TRUE(InnerLoop->isRecursivelyLCSSAForm(DT, LI, /*IgnoreTokens*/ true));EXPECT_FALSE(InnerLoop->isRecursivelyLCSSAForm(DT, LI, /*IgnoreTokens*/ false));});}
//===- LoadsTest.cpp - local load analysis unit tests ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/Loads.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("AnalysisTests", errs());return Mod;}TEST(LoadsTest, FindAvailableLoadedValueSameBasePtrConstantOffsetsNullAA) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(target datalayout = "p:64:64:64:32"%class = type <{ i32, i32 }>define i32 @f() {entry:%o = alloca %class%f1 = getelementptr inbounds %class, %class* %o, i32 0, i32 0store i32 42, i32* %f1%f2 = getelementptr inbounds %class, %class* %o, i32 0, i32 1store i32 43, i32* %f2%v = load i32, i32* %f1ret i32 %v})IR");auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);Instruction *Inst = &F->front().front();auto *AI = dyn_cast<AllocaInst>(Inst);ASSERT_TRUE(AI);Inst = &*++F->front().rbegin();auto *LI = dyn_cast<LoadInst>(Inst);ASSERT_TRUE(LI);BasicBlock::iterator BBI(LI);Value *Loaded = FindAvailableLoadedValue(LI, LI->getParent(), BBI, 0, nullptr, nullptr);ASSERT_TRUE(Loaded);auto *CI = dyn_cast<ConstantInt>(Loaded);ASSERT_TRUE(CI);ASSERT_TRUE(CI->equalsInt(42));}TEST(LoadsTest, CanReplacePointersIfEqual) {LLVMContext C;std::unique_ptr<Module> M = parseIR(C,R"IR(@y = common global [1 x i32] zeroinitializer, align 4@x = common global [1 x i32] zeroinitializer, align 4declare void @use(i32*)define void @f(i32* %p) {call void @use(i32* getelementptr inbounds ([1 x i32], [1 x i32]* @y, i64 0, i64 0))call void @use(i32* getelementptr inbounds (i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @x, i64 0, i64 0), i64 1))ret void})IR");const auto &DL = M->getDataLayout();auto *GV = M->getNamedValue("f");ASSERT_TRUE(GV);auto *F = dyn_cast<Function>(GV);ASSERT_TRUE(F);// NOTE: the implementation of canReplacePointersIfEqual is incomplete.// Currently the only the cases it returns false for are really sound and// returning true means unknown.Value *P = &*F->arg_begin();auto InstIter = F->front().begin();Value *ConstDerefPtr = *cast<CallInst>(&*InstIter)->arg_begin();// ConstDerefPtr is a constant pointer that is provably de-referenceable. We// can replace an arbitrary pointer with it.EXPECT_TRUE(canReplacePointersIfEqual(P, ConstDerefPtr, DL, nullptr));++InstIter;Value *ConstUnDerefPtr = *cast<CallInst>(&*InstIter)->arg_begin();// ConstUndDerefPtr is a constant pointer that is provably not// de-referenceable. We cannot replace an arbitrary pointer with it.EXPECT_FALSE(canReplacePointersIfEqual(ConstDerefPtr, ConstUnDerefPtr, DL, nullptr));}
//===- LazyCallGraphTest.cpp - Unit tests for the lazy CG analysis --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/LazyCallGraph.h"#include "llvm/ADT/Triple.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Function.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"#include <memory>using namespace llvm;namespace {std::unique_ptr<Module> parseAssembly(LLVMContext &Context,const char *Assembly) {SMDiagnostic Error;std::unique_ptr<Module> M = parseAssemblyString(Assembly, Error, Context);std::string ErrMsg;raw_string_ostream OS(ErrMsg);Error.print("", OS);// A failure here means that the test itself is buggy.if (!M)report_fatal_error(OS.str().c_str());return M;}/*IR forming a call graph with a diamond of triangle-shaped SCCs:d1/ \d3--d2/ \b1 c1/ \ / \b3--b2 c3--c2\ /a1/ \a3--a2All call edges go up between SCCs, and clockwise around the SCC.*/static const char DiamondOfTriangles[] ="define void @a1() {\n""entry:\n"" call void @a2()\n"" call void @b2()\n"" call void @c3()\n"" ret void\n""}\n""define void @a2() {\n""entry:\n"" call void @a3()\n"" ret void\n""}\n""define void @a3() {\n""entry:\n"" call void @a1()\n"" ret void\n""}\n""define void @b1() {\n""entry:\n"" call void @b2()\n"" call void @d3()\n"" ret void\n""}\n""define void @b2() {\n""entry:\n"" call void @b3()\n"" ret void\n""}\n""define void @b3() {\n""entry:\n"" call void @b1()\n"" ret void\n""}\n""define void @c1() {\n""entry:\n"" call void @c2()\n"" call void @d2()\n"" ret void\n""}\n""define void @c2() {\n""entry:\n"" call void @c3()\n"" ret void\n""}\n""define void @c3() {\n""entry:\n"" call void @c1()\n"" ret void\n""}\n""define void @d1() {\n""entry:\n"" call void @d2()\n"" ret void\n""}\n""define void @d2() {\n""entry:\n"" call void @d3()\n"" ret void\n""}\n""define void @d3() {\n""entry:\n"" call void @d1()\n"" ret void\n""}\n";/*IR forming a reference graph with a diamond of triangle-shaped RefSCCsd1/ \d3--d2/ \b1 c1/ \ / \b3--b2 c3--c2\ /a1/ \a3--a2All call edges go up between RefSCCs, and clockwise around the RefSCC.*/static const char DiamondOfTrianglesRefGraph[] ="define void @a1() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @a2, void ()** %a\n"" store void ()* @b2, void ()** %a\n"" store void ()* @c3, void ()** %a\n"" ret void\n""}\n""define void @a2() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @a3, void ()** %a\n"" ret void\n""}\n""define void @a3() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @a1, void ()** %a\n"" ret void\n""}\n""define void @b1() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @b2, void ()** %a\n"" store void ()* @d3, void ()** %a\n"" ret void\n""}\n""define void @b2() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @b3, void ()** %a\n"" ret void\n""}\n""define void @b3() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @b1, void ()** %a\n"" ret void\n""}\n""define void @c1() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @c2, void ()** %a\n"" store void ()* @d2, void ()** %a\n"" ret void\n""}\n""define void @c2() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @c3, void ()** %a\n"" ret void\n""}\n""define void @c3() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @c1, void ()** %a\n"" ret void\n""}\n""define void @d1() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @d2, void ()** %a\n"" ret void\n""}\n""define void @d2() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @d3, void ()** %a\n"" ret void\n""}\n""define void @d3() {\n""entry:\n"" %a = alloca void ()*\n"" store void ()* @d1, void ()** %a\n"" ret void\n""}\n";static LazyCallGraph buildCG(Module &M) {TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple()));TargetLibraryInfo TLI(TLII);auto GetTLI = [&TLI](Function &F) -> TargetLibraryInfo & { return TLI; };LazyCallGraph CG(M, GetTLI);return CG;}TEST(LazyCallGraphTest, BasicGraphFormation) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);LazyCallGraph CG = buildCG(*M);// The order of the entry nodes should be stable w.r.t. the source order of// the IR, and everything in our module is an entry node, so just directly// build variables for each node.auto I = CG.begin();LazyCallGraph::Node &A1 = (I++)->getNode();EXPECT_EQ("a1", A1.getFunction().getName());LazyCallGraph::Node &A2 = (I++)->getNode();EXPECT_EQ("a2", A2.getFunction().getName());LazyCallGraph::Node &A3 = (I++)->getNode();EXPECT_EQ("a3", A3.getFunction().getName());LazyCallGraph::Node &B1 = (I++)->getNode();EXPECT_EQ("b1", B1.getFunction().getName());LazyCallGraph::Node &B2 = (I++)->getNode();EXPECT_EQ("b2", B2.getFunction().getName());LazyCallGraph::Node &B3 = (I++)->getNode();EXPECT_EQ("b3", B3.getFunction().getName());LazyCallGraph::Node &C1 = (I++)->getNode();EXPECT_EQ("c1", C1.getFunction().getName());LazyCallGraph::Node &C2 = (I++)->getNode();EXPECT_EQ("c2", C2.getFunction().getName());LazyCallGraph::Node &C3 = (I++)->getNode();EXPECT_EQ("c3", C3.getFunction().getName());LazyCallGraph::Node &D1 = (I++)->getNode();EXPECT_EQ("d1", D1.getFunction().getName());LazyCallGraph::Node &D2 = (I++)->getNode();EXPECT_EQ("d2", D2.getFunction().getName());LazyCallGraph::Node &D3 = (I++)->getNode();EXPECT_EQ("d3", D3.getFunction().getName());EXPECT_EQ(CG.end(), I);// Build vectors and sort them for the rest of the assertions to make them// independent of order.std::vector<std::string> Nodes;for (LazyCallGraph::Edge &E : A1.populate())Nodes.push_back(std::string(E.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ("a2", Nodes[0]);EXPECT_EQ("b2", Nodes[1]);EXPECT_EQ("c3", Nodes[2]);Nodes.clear();A2.populate();EXPECT_EQ(A2->end(), std::next(A2->begin()));EXPECT_EQ("a3", A2->begin()->getFunction().getName());A3.populate();EXPECT_EQ(A3->end(), std::next(A3->begin()));EXPECT_EQ("a1", A3->begin()->getFunction().getName());for (LazyCallGraph::Edge &E : B1.populate())Nodes.push_back(std::string(E.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ("b2", Nodes[0]);EXPECT_EQ("d3", Nodes[1]);Nodes.clear();B2.populate();EXPECT_EQ(B2->end(), std::next(B2->begin()));EXPECT_EQ("b3", B2->begin()->getFunction().getName());B3.populate();EXPECT_EQ(B3->end(), std::next(B3->begin()));EXPECT_EQ("b1", B3->begin()->getFunction().getName());for (LazyCallGraph::Edge &E : C1.populate())Nodes.push_back(std::string(E.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ("c2", Nodes[0]);EXPECT_EQ("d2", Nodes[1]);Nodes.clear();C2.populate();EXPECT_EQ(C2->end(), std::next(C2->begin()));EXPECT_EQ("c3", C2->begin()->getFunction().getName());C3.populate();EXPECT_EQ(C3->end(), std::next(C3->begin()));EXPECT_EQ("c1", C3->begin()->getFunction().getName());D1.populate();EXPECT_EQ(D1->end(), std::next(D1->begin()));EXPECT_EQ("d2", D1->begin()->getFunction().getName());D2.populate();EXPECT_EQ(D2->end(), std::next(D2->begin()));EXPECT_EQ("d3", D2->begin()->getFunction().getName());D3.populate();EXPECT_EQ(D3->end(), std::next(D3->begin()));EXPECT_EQ("d1", D3->begin()->getFunction().getName());// Now lets look at the RefSCCs and SCCs.CG.buildRefSCCs();auto J = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &D = *J++;ASSERT_EQ(1, D.size());for (LazyCallGraph::Node &N : *D.begin())Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("d1", Nodes[0]);EXPECT_EQ("d2", Nodes[1]);EXPECT_EQ("d3", Nodes[2]);Nodes.clear();EXPECT_FALSE(D.isParentOf(D));EXPECT_FALSE(D.isChildOf(D));EXPECT_FALSE(D.isAncestorOf(D));EXPECT_FALSE(D.isDescendantOf(D));EXPECT_EQ(&D, &*CG.postorder_ref_scc_begin());LazyCallGraph::RefSCC &B = *J++;ASSERT_EQ(1, B.size());for (LazyCallGraph::Node &N : *B.begin())Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("b1", Nodes[0]);EXPECT_EQ("b2", Nodes[1]);EXPECT_EQ("b3", Nodes[2]);Nodes.clear();EXPECT_TRUE(B.isParentOf(D));EXPECT_FALSE(B.isChildOf(D));EXPECT_TRUE(B.isAncestorOf(D));EXPECT_FALSE(B.isDescendantOf(D));EXPECT_EQ(&B, &*std::next(CG.postorder_ref_scc_begin()));LazyCallGraph::RefSCC &C = *J++;ASSERT_EQ(1, C.size());for (LazyCallGraph::Node &N : *C.begin())Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("c1", Nodes[0]);EXPECT_EQ("c2", Nodes[1]);EXPECT_EQ("c3", Nodes[2]);Nodes.clear();EXPECT_FALSE(B.isAncestorOf(C));EXPECT_FALSE(C.isAncestorOf(B));EXPECT_TRUE(C.isParentOf(D));EXPECT_FALSE(C.isChildOf(D));EXPECT_TRUE(C.isAncestorOf(D));EXPECT_FALSE(C.isDescendantOf(D));EXPECT_EQ(&C, &*std::next(CG.postorder_ref_scc_begin(), 2));LazyCallGraph::RefSCC &A = *J++;ASSERT_EQ(1, A.size());for (LazyCallGraph::Node &N : *A.begin())Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("a1", Nodes[0]);EXPECT_EQ("a2", Nodes[1]);EXPECT_EQ("a3", Nodes[2]);Nodes.clear();EXPECT_TRUE(A.isParentOf(B));EXPECT_TRUE(A.isParentOf(C));EXPECT_FALSE(A.isParentOf(D));EXPECT_TRUE(A.isAncestorOf(B));EXPECT_TRUE(A.isAncestorOf(C));EXPECT_TRUE(A.isAncestorOf(D));EXPECT_EQ(&A, &*std::next(CG.postorder_ref_scc_begin(), 3));EXPECT_EQ(CG.postorder_ref_scc_end(), J);EXPECT_EQ(J, std::next(CG.postorder_ref_scc_begin(), 4));}static Function &lookupFunction(Module &M, StringRef Name) {for (Function &F : M)if (F.getName() == Name)return F;report_fatal_error("Couldn't find function!");}TEST(LazyCallGraphTest, BasicGraphMutation) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @b()\n"" call void @c()\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);LazyCallGraph::Node &A = CG.get(lookupFunction(*M, "a"));LazyCallGraph::Node &B = CG.get(lookupFunction(*M, "b"));A.populate();EXPECT_EQ(2, std::distance(A->begin(), A->end()));B.populate();EXPECT_EQ(0, std::distance(B->begin(), B->end()));LazyCallGraph::Node &C = CG.get(lookupFunction(*M, "c"));C.populate();CG.insertEdge(B, C, LazyCallGraph::Edge::Call);EXPECT_EQ(1, std::distance(B->begin(), B->end()));EXPECT_EQ(0, std::distance(C->begin(), C->end()));CG.insertEdge(C, B, LazyCallGraph::Edge::Call);EXPECT_EQ(1, std::distance(C->begin(), C->end()));EXPECT_EQ(&B, &C->begin()->getNode());CG.insertEdge(C, C, LazyCallGraph::Edge::Call);EXPECT_EQ(2, std::distance(C->begin(), C->end()));EXPECT_EQ(&B, &C->begin()->getNode());EXPECT_EQ(&C, &std::next(C->begin())->getNode());CG.removeEdge(C, B);EXPECT_EQ(1, std::distance(C->begin(), C->end()));EXPECT_EQ(&C, &C->begin()->getNode());CG.removeEdge(C, C);EXPECT_EQ(0, std::distance(C->begin(), C->end()));CG.removeEdge(B, C);EXPECT_EQ(0, std::distance(B->begin(), B->end()));}TEST(LazyCallGraphTest, InnerSCCFormation) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);LazyCallGraph CG = buildCG(*M);// Now mutate the graph to connect every node into a single RefSCC to ensure// that our inner SCC formation handles the rest.LazyCallGraph::Node &D1 = CG.get(lookupFunction(*M, "d1"));LazyCallGraph::Node &A1 = CG.get(lookupFunction(*M, "a1"));A1.populate();D1.populate();CG.insertEdge(D1, A1, LazyCallGraph::Edge::Ref);// Build vectors and sort them for the rest of the assertions to make them// independent of order.std::vector<std::string> Nodes;// We should build a single RefSCC for the entire graph.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);// Now walk the four SCCs which should be in post-order.auto J = RC.begin();LazyCallGraph::SCC &D = *J++;for (LazyCallGraph::Node &N : D)Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("d1", Nodes[0]);EXPECT_EQ("d2", Nodes[1]);EXPECT_EQ("d3", Nodes[2]);Nodes.clear();LazyCallGraph::SCC &B = *J++;for (LazyCallGraph::Node &N : B)Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("b1", Nodes[0]);EXPECT_EQ("b2", Nodes[1]);EXPECT_EQ("b3", Nodes[2]);Nodes.clear();LazyCallGraph::SCC &C = *J++;for (LazyCallGraph::Node &N : C)Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("c1", Nodes[0]);EXPECT_EQ("c2", Nodes[1]);EXPECT_EQ("c3", Nodes[2]);Nodes.clear();LazyCallGraph::SCC &A = *J++;for (LazyCallGraph::Node &N : A)Nodes.push_back(std::string(N.getFunction().getName()));llvm::sort(Nodes);EXPECT_EQ(3u, Nodes.size());EXPECT_EQ("a1", Nodes[0]);EXPECT_EQ("a2", Nodes[1]);EXPECT_EQ("a3", Nodes[2]);Nodes.clear();EXPECT_EQ(RC.end(), J);}TEST(LazyCallGraphTest, MultiArmSCC) {LLVMContext Context;// Two interlocking cycles. The really useful thing about this SCC is that it// will require Tarjan's DFS to backtrack and finish processing all of the// children of each node in the SCC. Since this involves call edges, both// Tarjan implementations will have to successfully navigate the structure.std::unique_ptr<Module> M = parseAssembly(Context, "define void @f1() {\n""entry:\n"" call void @f2()\n"" call void @f4()\n"" ret void\n""}\n""define void @f2() {\n""entry:\n"" call void @f3()\n"" ret void\n""}\n""define void @f3() {\n""entry:\n"" call void @f1()\n"" ret void\n""}\n""define void @f4() {\n""entry:\n"" call void @f5()\n"" ret void\n""}\n""define void @f5() {\n""entry:\n"" call void @f1()\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);LazyCallGraph::Node &N1 = *CG.lookup(lookupFunction(*M, "f1"));LazyCallGraph::Node &N2 = *CG.lookup(lookupFunction(*M, "f2"));LazyCallGraph::Node &N3 = *CG.lookup(lookupFunction(*M, "f3"));LazyCallGraph::Node &N4 = *CG.lookup(lookupFunction(*M, "f4"));LazyCallGraph::Node &N5 = *CG.lookup(lookupFunction(*M, "f4"));EXPECT_EQ(&RC, CG.lookupRefSCC(N1));EXPECT_EQ(&RC, CG.lookupRefSCC(N2));EXPECT_EQ(&RC, CG.lookupRefSCC(N3));EXPECT_EQ(&RC, CG.lookupRefSCC(N4));EXPECT_EQ(&RC, CG.lookupRefSCC(N5));ASSERT_EQ(1, RC.size());LazyCallGraph::SCC &C = *RC.begin();EXPECT_EQ(&C, CG.lookupSCC(N1));EXPECT_EQ(&C, CG.lookupSCC(N2));EXPECT_EQ(&C, CG.lookupSCC(N3));EXPECT_EQ(&C, CG.lookupSCC(N4));EXPECT_EQ(&C, CG.lookupSCC(N5));}TEST(LazyCallGraphTest, OutgoingEdgeMutation) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @b()\n"" call void @c()\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" call void @d()\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" call void @d()\n"" ret void\n""}\n""define void @d() {\n""entry:\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())dbgs() << "Formed RefSCC: " << RC << "\n";LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));LazyCallGraph::SCC &AC = *CG.lookupSCC(A);LazyCallGraph::SCC &BC = *CG.lookupSCC(B);LazyCallGraph::SCC &CC = *CG.lookupSCC(C);LazyCallGraph::SCC &DC = *CG.lookupSCC(D);LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A);LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B);LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C);LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D);EXPECT_TRUE(ARC.isParentOf(BRC));EXPECT_TRUE(AC.isParentOf(BC));EXPECT_TRUE(ARC.isParentOf(CRC));EXPECT_TRUE(AC.isParentOf(CC));EXPECT_FALSE(ARC.isParentOf(DRC));EXPECT_FALSE(AC.isParentOf(DC));EXPECT_TRUE(ARC.isAncestorOf(DRC));EXPECT_TRUE(AC.isAncestorOf(DC));EXPECT_FALSE(DRC.isChildOf(ARC));EXPECT_FALSE(DC.isChildOf(AC));EXPECT_TRUE(DRC.isDescendantOf(ARC));EXPECT_TRUE(DC.isDescendantOf(AC));EXPECT_TRUE(DRC.isChildOf(BRC));EXPECT_TRUE(DC.isChildOf(BC));EXPECT_TRUE(DRC.isChildOf(CRC));EXPECT_TRUE(DC.isChildOf(CC));EXPECT_EQ(2, std::distance(A->begin(), A->end()));ARC.insertOutgoingEdge(A, D, LazyCallGraph::Edge::Call);EXPECT_EQ(3, std::distance(A->begin(), A->end()));const LazyCallGraph::Edge &NewE = (*A)[D];EXPECT_TRUE(NewE);EXPECT_TRUE(NewE.isCall());EXPECT_EQ(&D, &NewE.getNode());// Only the parent and child tests sholud have changed. The rest of the graph// remains the same.EXPECT_TRUE(ARC.isParentOf(DRC));EXPECT_TRUE(AC.isParentOf(DC));EXPECT_TRUE(ARC.isAncestorOf(DRC));EXPECT_TRUE(AC.isAncestorOf(DC));EXPECT_TRUE(DRC.isChildOf(ARC));EXPECT_TRUE(DC.isChildOf(AC));EXPECT_TRUE(DRC.isDescendantOf(ARC));EXPECT_TRUE(DC.isDescendantOf(AC));EXPECT_EQ(&AC, CG.lookupSCC(A));EXPECT_EQ(&BC, CG.lookupSCC(B));EXPECT_EQ(&CC, CG.lookupSCC(C));EXPECT_EQ(&DC, CG.lookupSCC(D));EXPECT_EQ(&ARC, CG.lookupRefSCC(A));EXPECT_EQ(&BRC, CG.lookupRefSCC(B));EXPECT_EQ(&CRC, CG.lookupRefSCC(C));EXPECT_EQ(&DRC, CG.lookupRefSCC(D));ARC.switchOutgoingEdgeToRef(A, D);EXPECT_FALSE(NewE.isCall());// Verify the reference graph remains the same but the SCC graph is updated.EXPECT_TRUE(ARC.isParentOf(DRC));EXPECT_FALSE(AC.isParentOf(DC));EXPECT_TRUE(ARC.isAncestorOf(DRC));EXPECT_TRUE(AC.isAncestorOf(DC));EXPECT_TRUE(DRC.isChildOf(ARC));EXPECT_FALSE(DC.isChildOf(AC));EXPECT_TRUE(DRC.isDescendantOf(ARC));EXPECT_TRUE(DC.isDescendantOf(AC));EXPECT_EQ(&AC, CG.lookupSCC(A));EXPECT_EQ(&BC, CG.lookupSCC(B));EXPECT_EQ(&CC, CG.lookupSCC(C));EXPECT_EQ(&DC, CG.lookupSCC(D));EXPECT_EQ(&ARC, CG.lookupRefSCC(A));EXPECT_EQ(&BRC, CG.lookupRefSCC(B));EXPECT_EQ(&CRC, CG.lookupRefSCC(C));EXPECT_EQ(&DRC, CG.lookupRefSCC(D));ARC.switchOutgoingEdgeToCall(A, D);EXPECT_TRUE(NewE.isCall());// Verify the reference graph remains the same but the SCC graph is updated.EXPECT_TRUE(ARC.isParentOf(DRC));EXPECT_TRUE(AC.isParentOf(DC));EXPECT_TRUE(ARC.isAncestorOf(DRC));EXPECT_TRUE(AC.isAncestorOf(DC));EXPECT_TRUE(DRC.isChildOf(ARC));EXPECT_TRUE(DC.isChildOf(AC));EXPECT_TRUE(DRC.isDescendantOf(ARC));EXPECT_TRUE(DC.isDescendantOf(AC));EXPECT_EQ(&AC, CG.lookupSCC(A));EXPECT_EQ(&BC, CG.lookupSCC(B));EXPECT_EQ(&CC, CG.lookupSCC(C));EXPECT_EQ(&DC, CG.lookupSCC(D));EXPECT_EQ(&ARC, CG.lookupRefSCC(A));EXPECT_EQ(&BRC, CG.lookupRefSCC(B));EXPECT_EQ(&CRC, CG.lookupRefSCC(C));EXPECT_EQ(&DRC, CG.lookupRefSCC(D));ARC.removeOutgoingEdge(A, D);EXPECT_EQ(2, std::distance(A->begin(), A->end()));// Now the parent and child tests fail again but the rest remains the same.EXPECT_FALSE(ARC.isParentOf(DRC));EXPECT_FALSE(AC.isParentOf(DC));EXPECT_TRUE(ARC.isAncestorOf(DRC));EXPECT_TRUE(AC.isAncestorOf(DC));EXPECT_FALSE(DRC.isChildOf(ARC));EXPECT_FALSE(DC.isChildOf(AC));EXPECT_TRUE(DRC.isDescendantOf(ARC));EXPECT_TRUE(DC.isDescendantOf(AC));EXPECT_EQ(&AC, CG.lookupSCC(A));EXPECT_EQ(&BC, CG.lookupSCC(B));EXPECT_EQ(&CC, CG.lookupSCC(C));EXPECT_EQ(&DC, CG.lookupSCC(D));EXPECT_EQ(&ARC, CG.lookupRefSCC(A));EXPECT_EQ(&BRC, CG.lookupRefSCC(B));EXPECT_EQ(&CRC, CG.lookupRefSCC(C));EXPECT_EQ(&DRC, CG.lookupRefSCC(D));}TEST(LazyCallGraphTest, IncomingEdgeInsertion) {LLVMContext Context;// We want to ensure we can add edges even across complex diamond graphs, so// we use the diamond of triangles graph defined above. The ascii diagram is// repeated here for easy reference.//// d1 |// / \ |// d3--d2 |// / \ |// b1 c1 |// / \ / \ |// b3--b2 c3--c2 |// \ / |// a1 |// / \ |// a3--a2 |//std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())dbgs() << "Formed RefSCC: " << RC << "\n";LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1"));LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2"));LazyCallGraph::Node &A3 = *CG.lookup(lookupFunction(*M, "a3"));LazyCallGraph::Node &B1 = *CG.lookup(lookupFunction(*M, "b1"));LazyCallGraph::Node &B2 = *CG.lookup(lookupFunction(*M, "b2"));LazyCallGraph::Node &B3 = *CG.lookup(lookupFunction(*M, "b3"));LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1"));LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2"));LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3"));LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1"));LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2"));LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3"));LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A1);LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B1);LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C1);LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D1);ASSERT_EQ(&ARC, CG.lookupRefSCC(A2));ASSERT_EQ(&ARC, CG.lookupRefSCC(A3));ASSERT_EQ(&BRC, CG.lookupRefSCC(B2));ASSERT_EQ(&BRC, CG.lookupRefSCC(B3));ASSERT_EQ(&CRC, CG.lookupRefSCC(C2));ASSERT_EQ(&CRC, CG.lookupRefSCC(C3));ASSERT_EQ(&DRC, CG.lookupRefSCC(D2));ASSERT_EQ(&DRC, CG.lookupRefSCC(D3));ASSERT_EQ(1, std::distance(D2->begin(), D2->end()));// Add an edge to make the graph://// d1 |// / \ |// d3--d2---. |// / \ | |// b1 c1 | |// / \ / \ / |// b3--b2 c3--c2 |// \ / |// a1 |// / \ |// a3--a2 |auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2);// Make sure we connected the nodes.for (LazyCallGraph::Edge E : *D2) {if (&E.getNode() == &D3)continue;EXPECT_EQ(&C2, &E.getNode());}// And marked the D ref-SCC as no longer valid.EXPECT_EQ(1u, MergedRCs.size());EXPECT_EQ(&DRC, MergedRCs[0]);// Make sure we have the correct nodes in the SCC sets.EXPECT_EQ(&ARC, CG.lookupRefSCC(A1));EXPECT_EQ(&ARC, CG.lookupRefSCC(A2));EXPECT_EQ(&ARC, CG.lookupRefSCC(A3));EXPECT_EQ(&BRC, CG.lookupRefSCC(B1));EXPECT_EQ(&BRC, CG.lookupRefSCC(B2));EXPECT_EQ(&BRC, CG.lookupRefSCC(B3));EXPECT_EQ(&CRC, CG.lookupRefSCC(C1));EXPECT_EQ(&CRC, CG.lookupRefSCC(C2));EXPECT_EQ(&CRC, CG.lookupRefSCC(C3));EXPECT_EQ(&CRC, CG.lookupRefSCC(D1));EXPECT_EQ(&CRC, CG.lookupRefSCC(D2));EXPECT_EQ(&CRC, CG.lookupRefSCC(D3));// And that ancestry tests have been updated.EXPECT_TRUE(ARC.isParentOf(CRC));EXPECT_TRUE(BRC.isParentOf(CRC));// And verify the post-order walk reflects the updated structure.auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();ASSERT_NE(I, E);EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I;ASSERT_NE(++I, E);EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I;ASSERT_NE(++I, E);EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I;EXPECT_EQ(++I, E);}TEST(LazyCallGraphTest, IncomingEdgeInsertionRefGraph) {LLVMContext Context;// Another variation of the above test but with all the edges switched to// references rather than calls.std::unique_ptr<Module> M =parseAssembly(Context, DiamondOfTrianglesRefGraph);LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())dbgs() << "Formed RefSCC: " << RC << "\n";LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1"));LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2"));LazyCallGraph::Node &A3 = *CG.lookup(lookupFunction(*M, "a3"));LazyCallGraph::Node &B1 = *CG.lookup(lookupFunction(*M, "b1"));LazyCallGraph::Node &B2 = *CG.lookup(lookupFunction(*M, "b2"));LazyCallGraph::Node &B3 = *CG.lookup(lookupFunction(*M, "b3"));LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1"));LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2"));LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3"));LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1"));LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2"));LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3"));LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A1);LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B1);LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C1);LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D1);ASSERT_EQ(&ARC, CG.lookupRefSCC(A2));ASSERT_EQ(&ARC, CG.lookupRefSCC(A3));ASSERT_EQ(&BRC, CG.lookupRefSCC(B2));ASSERT_EQ(&BRC, CG.lookupRefSCC(B3));ASSERT_EQ(&CRC, CG.lookupRefSCC(C2));ASSERT_EQ(&CRC, CG.lookupRefSCC(C3));ASSERT_EQ(&DRC, CG.lookupRefSCC(D2));ASSERT_EQ(&DRC, CG.lookupRefSCC(D3));ASSERT_EQ(1, std::distance(D2->begin(), D2->end()));// Add an edge to make the graph://// d1 |// / \ |// d3--d2---. |// / \ | |// b1 c1 | |// / \ / \ / |// b3--b2 c3--c2 |// \ / |// a1 |// / \ |// a3--a2 |auto MergedRCs = CRC.insertIncomingRefEdge(D2, C2);// Make sure we connected the nodes.for (LazyCallGraph::Edge E : *D2) {if (&E.getNode() == &D3)continue;EXPECT_EQ(&C2, &E.getNode());}// And marked the D ref-SCC as no longer valid.EXPECT_EQ(1u, MergedRCs.size());EXPECT_EQ(&DRC, MergedRCs[0]);// Make sure we have the correct nodes in the SCC sets.EXPECT_EQ(&ARC, CG.lookupRefSCC(A1));EXPECT_EQ(&ARC, CG.lookupRefSCC(A2));EXPECT_EQ(&ARC, CG.lookupRefSCC(A3));EXPECT_EQ(&BRC, CG.lookupRefSCC(B1));EXPECT_EQ(&BRC, CG.lookupRefSCC(B2));EXPECT_EQ(&BRC, CG.lookupRefSCC(B3));EXPECT_EQ(&CRC, CG.lookupRefSCC(C1));EXPECT_EQ(&CRC, CG.lookupRefSCC(C2));EXPECT_EQ(&CRC, CG.lookupRefSCC(C3));EXPECT_EQ(&CRC, CG.lookupRefSCC(D1));EXPECT_EQ(&CRC, CG.lookupRefSCC(D2));EXPECT_EQ(&CRC, CG.lookupRefSCC(D3));// And that ancestry tests have been updated.EXPECT_TRUE(ARC.isParentOf(CRC));EXPECT_TRUE(BRC.isParentOf(CRC));// And verify the post-order walk reflects the updated structure.auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();ASSERT_NE(I, E);EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I;ASSERT_NE(++I, E);EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I;ASSERT_NE(++I, E);EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I;EXPECT_EQ(++I, E);}TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeCallCycle) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @b()\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" call void @c()\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" call void @d()\n"" ret void\n""}\n""define void @d() {\n""entry:\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())dbgs() << "Formed RefSCC: " << RC << "\n";LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));LazyCallGraph::SCC &AC = *CG.lookupSCC(A);LazyCallGraph::SCC &BC = *CG.lookupSCC(B);LazyCallGraph::SCC &CC = *CG.lookupSCC(C);LazyCallGraph::SCC &DC = *CG.lookupSCC(D);LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A);LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B);LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C);LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D);// Connect the top to the bottom forming a large RefSCC made up mostly of calls.auto MergedRCs = ARC.insertIncomingRefEdge(D, A);// Make sure we connected the nodes.EXPECT_NE(D->begin(), D->end());EXPECT_EQ(&A, &D->begin()->getNode());// Check that we have the dead RCs, but ignore the order.EXPECT_EQ(3u, MergedRCs.size());EXPECT_NE(find(MergedRCs, &BRC), MergedRCs.end());EXPECT_NE(find(MergedRCs, &CRC), MergedRCs.end());EXPECT_NE(find(MergedRCs, &DRC), MergedRCs.end());// Make sure the nodes point to the right place now.EXPECT_EQ(&ARC, CG.lookupRefSCC(A));EXPECT_EQ(&ARC, CG.lookupRefSCC(B));EXPECT_EQ(&ARC, CG.lookupRefSCC(C));EXPECT_EQ(&ARC, CG.lookupRefSCC(D));// Check that the SCCs are in postorder.EXPECT_EQ(4, ARC.size());EXPECT_EQ(&DC, &ARC[0]);EXPECT_EQ(&CC, &ARC[1]);EXPECT_EQ(&BC, &ARC[2]);EXPECT_EQ(&AC, &ARC[3]);// And verify the post-order walk reflects the updated structure.auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();ASSERT_NE(I, E);EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I;EXPECT_EQ(++I, E);}TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeRefCycle) {LLVMContext Context;std::unique_ptr<Module> M =parseAssembly(Context, "define void @a() {\n""entry:\n"" %p = alloca void ()*\n"" store void ()* @b, void ()** %p\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" %p = alloca void ()*\n"" store void ()* @c, void ()** %p\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" %p = alloca void ()*\n"" store void ()* @d, void ()** %p\n"" ret void\n""}\n""define void @d() {\n""entry:\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())dbgs() << "Formed RefSCC: " << RC << "\n";LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A);LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B);LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C);LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D);// Connect the top to the bottom forming a large RefSCC made up just of// references.auto MergedRCs = ARC.insertIncomingRefEdge(D, A);// Make sure we connected the nodes.EXPECT_NE(D->begin(), D->end());EXPECT_EQ(&A, &D->begin()->getNode());// Check that we have the dead RCs, but ignore the order.EXPECT_EQ(3u, MergedRCs.size());EXPECT_NE(find(MergedRCs, &BRC), MergedRCs.end());EXPECT_NE(find(MergedRCs, &CRC), MergedRCs.end());EXPECT_NE(find(MergedRCs, &DRC), MergedRCs.end());// Make sure the nodes point to the right place now.EXPECT_EQ(&ARC, CG.lookupRefSCC(A));EXPECT_EQ(&ARC, CG.lookupRefSCC(B));EXPECT_EQ(&ARC, CG.lookupRefSCC(C));EXPECT_EQ(&ARC, CG.lookupRefSCC(D));// And verify the post-order walk reflects the updated structure.auto I = CG.postorder_ref_scc_begin(), End = CG.postorder_ref_scc_end();ASSERT_NE(I, End);EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I;EXPECT_EQ(++I, End);}TEST(LazyCallGraphTest, InlineAndDeleteFunction) {LLVMContext Context;// We want to ensure we can delete nodes from relatively complex graphs and// so use the diamond of triangles graph defined above.//// The ascii diagram is repeated here for easy reference.//// d1 |// / \ |// d3--d2 |// / \ |// b1 c1 |// / \ / \ |// b3--b2 c3--c2 |// \ / |// a1 |// / \ |// a3--a2 |//std::unique_ptr<Module> M = parseAssembly(Context, DiamondOfTriangles);LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs())dbgs() << "Formed RefSCC: " << RC << "\n";LazyCallGraph::Node &A1 = *CG.lookup(lookupFunction(*M, "a1"));LazyCallGraph::Node &A2 = *CG.lookup(lookupFunction(*M, "a2"));LazyCallGraph::Node &A3 = *CG.lookup(lookupFunction(*M, "a3"));LazyCallGraph::Node &B1 = *CG.lookup(lookupFunction(*M, "b1"));LazyCallGraph::Node &B2 = *CG.lookup(lookupFunction(*M, "b2"));LazyCallGraph::Node &B3 = *CG.lookup(lookupFunction(*M, "b3"));LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1"));LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2"));LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3"));LazyCallGraph::Node &D1 = *CG.lookup(lookupFunction(*M, "d1"));LazyCallGraph::Node &D2 = *CG.lookup(lookupFunction(*M, "d2"));LazyCallGraph::Node &D3 = *CG.lookup(lookupFunction(*M, "d3"));LazyCallGraph::RefSCC &ARC = *CG.lookupRefSCC(A1);LazyCallGraph::RefSCC &BRC = *CG.lookupRefSCC(B1);LazyCallGraph::RefSCC &CRC = *CG.lookupRefSCC(C1);LazyCallGraph::RefSCC &DRC = *CG.lookupRefSCC(D1);ASSERT_EQ(&ARC, CG.lookupRefSCC(A2));ASSERT_EQ(&ARC, CG.lookupRefSCC(A3));ASSERT_EQ(&BRC, CG.lookupRefSCC(B2));ASSERT_EQ(&BRC, CG.lookupRefSCC(B3));ASSERT_EQ(&CRC, CG.lookupRefSCC(C2));ASSERT_EQ(&CRC, CG.lookupRefSCC(C3));ASSERT_EQ(&DRC, CG.lookupRefSCC(D2));ASSERT_EQ(&DRC, CG.lookupRefSCC(D3));ASSERT_EQ(1, std::distance(D2->begin(), D2->end()));// Delete d2 from the graph, as if it had been inlined.//// d1 |// / / |// d3--. |// / \ |// b1 c1 |// / \ / \ |// b3--b2 c3--c2 |// \ / |// a1 |// / \ |// a3--a2 |Function &D2F = D2.getFunction();CallInst *C1Call = nullptr, *D1Call = nullptr;for (User *U : D2F.users()) {CallInst *CI = dyn_cast<CallInst>(U);ASSERT_TRUE(CI) << "Expected a call: " << *U;if (CI->getParent()->getParent() == &C1.getFunction()) {ASSERT_EQ(nullptr, C1Call) << "Found too many C1 calls: " << *CI;C1Call = CI;} else if (CI->getParent()->getParent() == &D1.getFunction()) {ASSERT_EQ(nullptr, D1Call) << "Found too many D1 calls: " << *CI;D1Call = CI;} else {FAIL() << "Found an unexpected call instruction: " << *CI;}}ASSERT_NE(C1Call, nullptr);ASSERT_NE(D1Call, nullptr);ASSERT_EQ(&D2F, C1Call->getCalledFunction());ASSERT_EQ(&D2F, D1Call->getCalledFunction());C1Call->setCalledFunction(&D3.getFunction());D1Call->setCalledFunction(&D3.getFunction());ASSERT_EQ(0u, D2F.getNumUses());// Insert new edges first.CRC.insertTrivialCallEdge(C1, D3);DRC.insertTrivialCallEdge(D1, D3);// Then remove the old ones.LazyCallGraph::SCC &DC = *CG.lookupSCC(D2);auto NewCs = DRC.switchInternalEdgeToRef(D1, D2);EXPECT_EQ(&DC, CG.lookupSCC(D2));EXPECT_EQ(NewCs.end(), std::next(NewCs.begin()));LazyCallGraph::SCC &NewDC = *NewCs.begin();EXPECT_EQ(&NewDC, CG.lookupSCC(D1));EXPECT_EQ(&NewDC, CG.lookupSCC(D3));auto NewRCs = DRC.removeInternalRefEdge(D1, {&D2});ASSERT_EQ(2u, NewRCs.size());LazyCallGraph::RefSCC &NewDRC = *NewRCs[0];EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D1));EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3));LazyCallGraph::RefSCC &D2RC = *NewRCs[1];EXPECT_EQ(&D2RC, CG.lookupRefSCC(D2));EXPECT_FALSE(NewDRC.isParentOf(D2RC));EXPECT_TRUE(CRC.isParentOf(D2RC));EXPECT_TRUE(CRC.isParentOf(NewDRC));EXPECT_TRUE(D2RC.isParentOf(NewDRC));CRC.removeOutgoingEdge(C1, D2);EXPECT_FALSE(CRC.isParentOf(D2RC));EXPECT_TRUE(CRC.isParentOf(NewDRC));EXPECT_TRUE(D2RC.isParentOf(NewDRC));// Now that we've updated the call graph, D2 is dead, so remove it.CG.removeDeadFunction(D2F);// Check that the graph still looks the same.EXPECT_EQ(&ARC, CG.lookupRefSCC(A1));EXPECT_EQ(&ARC, CG.lookupRefSCC(A2));EXPECT_EQ(&ARC, CG.lookupRefSCC(A3));EXPECT_EQ(&BRC, CG.lookupRefSCC(B1));EXPECT_EQ(&BRC, CG.lookupRefSCC(B2));EXPECT_EQ(&BRC, CG.lookupRefSCC(B3));EXPECT_EQ(&CRC, CG.lookupRefSCC(C1));EXPECT_EQ(&CRC, CG.lookupRefSCC(C2));EXPECT_EQ(&CRC, CG.lookupRefSCC(C3));EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D1));EXPECT_EQ(&NewDRC, CG.lookupRefSCC(D3));EXPECT_TRUE(CRC.isParentOf(NewDRC));// Verify the post-order walk hasn't changed.auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();ASSERT_NE(I, E);EXPECT_EQ(&NewDRC, &*I) << "Actual RefSCC: " << *I;ASSERT_NE(++I, E);EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I;ASSERT_NE(++I, E);EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I;ASSERT_NE(++I, E);EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I;EXPECT_EQ(++I, E);}TEST(LazyCallGraphTest, InternalEdgeMutation) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @b()\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" call void @c()\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" call void @a()\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));EXPECT_EQ(&RC, CG.lookupRefSCC(A));EXPECT_EQ(&RC, CG.lookupRefSCC(B));EXPECT_EQ(&RC, CG.lookupRefSCC(C));EXPECT_EQ(1, RC.size());EXPECT_EQ(&*RC.begin(), CG.lookupSCC(A));EXPECT_EQ(&*RC.begin(), CG.lookupSCC(B));EXPECT_EQ(&*RC.begin(), CG.lookupSCC(C));// Insert an edge from 'a' to 'c'. Nothing changes about the graph.RC.insertInternalRefEdge(A, C);EXPECT_EQ(2, std::distance(A->begin(), A->end()));EXPECT_EQ(&RC, CG.lookupRefSCC(A));EXPECT_EQ(&RC, CG.lookupRefSCC(B));EXPECT_EQ(&RC, CG.lookupRefSCC(C));EXPECT_EQ(1, RC.size());EXPECT_EQ(&*RC.begin(), CG.lookupSCC(A));EXPECT_EQ(&*RC.begin(), CG.lookupSCC(B));EXPECT_EQ(&*RC.begin(), CG.lookupSCC(C));// Switch the call edge from 'b' to 'c' to a ref edge. This will break the// call cycle and cause us to form more SCCs. The RefSCC will remain the same// though.auto NewCs = RC.switchInternalEdgeToRef(B, C);EXPECT_EQ(&RC, CG.lookupRefSCC(A));EXPECT_EQ(&RC, CG.lookupRefSCC(B));EXPECT_EQ(&RC, CG.lookupRefSCC(C));auto J = RC.begin();// The SCCs must be in *post-order* which means successors before// predecessors. At this point we have call edges from C to A and from A to// B. The only valid postorder is B, A, C.EXPECT_EQ(&*J++, CG.lookupSCC(B));EXPECT_EQ(&*J++, CG.lookupSCC(A));EXPECT_EQ(&*J++, CG.lookupSCC(C));EXPECT_EQ(RC.end(), J);// And the returned range must be the slice of this sequence containing new// SCCs.EXPECT_EQ(RC.begin(), NewCs.begin());EXPECT_EQ(std::prev(RC.end()), NewCs.end());// Test turning the ref edge from A to C into a call edge. This will form an// SCC out of A and C. Since we previously had a call edge from C to A, the// C SCC should be preserved and have A merged into it while the A SCC should// be invalidated.LazyCallGraph::SCC &AC = *CG.lookupSCC(A);LazyCallGraph::SCC &CC = *CG.lookupSCC(C);EXPECT_TRUE(RC.switchInternalEdgeToCall(A, C, [&](ArrayRef<LazyCallGraph::SCC *> MergedCs) {ASSERT_EQ(1u, MergedCs.size());EXPECT_EQ(&AC, MergedCs[0]);}));EXPECT_EQ(2, CC.size());EXPECT_EQ(&CC, CG.lookupSCC(A));EXPECT_EQ(&CC, CG.lookupSCC(C));J = RC.begin();EXPECT_EQ(&*J++, CG.lookupSCC(B));EXPECT_EQ(&*J++, CG.lookupSCC(C));EXPECT_EQ(RC.end(), J);}TEST(LazyCallGraphTest, InternalEdgeRemoval) {LLVMContext Context;// A nice fully connected (including self-edges) RefSCC.std::unique_ptr<Module> M = parseAssembly(Context, "define void @a(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n""define void @b(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n""define void @c(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();LazyCallGraph::RefSCC &RC = *I;EXPECT_EQ(E, std::next(I));LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));EXPECT_EQ(&RC, CG.lookupRefSCC(A));EXPECT_EQ(&RC, CG.lookupRefSCC(B));EXPECT_EQ(&RC, CG.lookupRefSCC(C));// Remove the edge from b -> a, which should leave the 3 functions still in// a single connected component because of a -> b -> c -> a.SmallVector<LazyCallGraph::RefSCC *, 1> NewRCs =RC.removeInternalRefEdge(B, {&A});EXPECT_EQ(0u, NewRCs.size());EXPECT_EQ(&RC, CG.lookupRefSCC(A));EXPECT_EQ(&RC, CG.lookupRefSCC(B));EXPECT_EQ(&RC, CG.lookupRefSCC(C));auto J = CG.postorder_ref_scc_begin();EXPECT_EQ(I, J);EXPECT_EQ(&RC, &*J);EXPECT_EQ(E, std::next(J));// Increment I before we actually mutate the structure so that it remains// a valid iterator.++I;// Remove the edge from c -> a, which should leave 'a' in the original RefSCC// and form a new RefSCC for 'b' and 'c'.NewRCs = RC.removeInternalRefEdge(C, {&A});ASSERT_EQ(2u, NewRCs.size());LazyCallGraph::RefSCC &BCRC = *NewRCs[0];LazyCallGraph::RefSCC &ARC = *NewRCs[1];EXPECT_EQ(&ARC, CG.lookupRefSCC(A));EXPECT_EQ(1, std::distance(ARC.begin(), ARC.end()));EXPECT_EQ(&BCRC, CG.lookupRefSCC(B));EXPECT_EQ(&BCRC, CG.lookupRefSCC(C));J = CG.postorder_ref_scc_begin();EXPECT_NE(I, J);EXPECT_EQ(&BCRC, &*J);++J;EXPECT_NE(I, J);EXPECT_EQ(&ARC, &*J);++J;EXPECT_EQ(I, J);EXPECT_EQ(E, J);}TEST(LazyCallGraphTest, InternalMultiEdgeRemoval) {LLVMContext Context;// A nice fully connected (including self-edges) RefSCC.std::unique_ptr<Module> M = parseAssembly(Context, "define void @a(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n""define void @b(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n""define void @c(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();LazyCallGraph::RefSCC &RC = *I;EXPECT_EQ(E, std::next(I));LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));EXPECT_EQ(&RC, CG.lookupRefSCC(A));EXPECT_EQ(&RC, CG.lookupRefSCC(B));EXPECT_EQ(&RC, CG.lookupRefSCC(C));// Increment I before we actually mutate the structure so that it remains// a valid iterator.++I;// Remove the edges from b -> a and b -> c, leaving b in its own RefSCC.SmallVector<LazyCallGraph::RefSCC *, 1> NewRCs =RC.removeInternalRefEdge(B, {&A, &C});ASSERT_EQ(2u, NewRCs.size());LazyCallGraph::RefSCC &BRC = *NewRCs[0];LazyCallGraph::RefSCC &ACRC = *NewRCs[1];EXPECT_EQ(&BRC, CG.lookupRefSCC(B));EXPECT_EQ(1, std::distance(BRC.begin(), BRC.end()));EXPECT_EQ(&ACRC, CG.lookupRefSCC(A));EXPECT_EQ(&ACRC, CG.lookupRefSCC(C));auto J = CG.postorder_ref_scc_begin();EXPECT_NE(I, J);EXPECT_EQ(&BRC, &*J);++J;EXPECT_NE(I, J);EXPECT_EQ(&ACRC, &*J);++J;EXPECT_EQ(I, J);EXPECT_EQ(E, J);}TEST(LazyCallGraphTest, InternalNoOpEdgeRemoval) {LLVMContext Context;// A graph with a single cycle formed both from call and reference edges// which makes the reference edges trivial to delete. The graph looks like://// Reference edges: a -> b -> c -> a// Call edges: a -> c -> b -> astd::unique_ptr<Module> M = parseAssembly(Context, "define void @a(i8** %ptr) {\n""entry:\n"" call void @b(i8** %ptr)\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n""define void @b(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"" call void @c(i8** %ptr)\n"" ret void\n""}\n""define void @c(i8** %ptr) {\n""entry:\n"" call void @a(i8** %ptr)\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();LazyCallGraph::RefSCC &RC = *I;EXPECT_EQ(E, std::next(I));LazyCallGraph::SCC &C = *RC.begin();EXPECT_EQ(RC.end(), std::next(RC.begin()));LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c"));EXPECT_EQ(&RC, CG.lookupRefSCC(AN));EXPECT_EQ(&RC, CG.lookupRefSCC(BN));EXPECT_EQ(&RC, CG.lookupRefSCC(CN));EXPECT_EQ(&C, CG.lookupSCC(AN));EXPECT_EQ(&C, CG.lookupSCC(BN));EXPECT_EQ(&C, CG.lookupSCC(CN));// Remove the edge from a -> c which doesn't change anything.SmallVector<LazyCallGraph::RefSCC *, 1> NewRCs =RC.removeInternalRefEdge(AN, {&CN});EXPECT_EQ(0u, NewRCs.size());EXPECT_EQ(&RC, CG.lookupRefSCC(AN));EXPECT_EQ(&RC, CG.lookupRefSCC(BN));EXPECT_EQ(&RC, CG.lookupRefSCC(CN));EXPECT_EQ(&C, CG.lookupSCC(AN));EXPECT_EQ(&C, CG.lookupSCC(BN));EXPECT_EQ(&C, CG.lookupSCC(CN));auto J = CG.postorder_ref_scc_begin();EXPECT_EQ(I, J);EXPECT_EQ(&RC, &*J);EXPECT_EQ(E, std::next(J));// Remove the edge from b -> a and c -> b; again this doesn't change// anything.NewRCs = RC.removeInternalRefEdge(BN, {&AN});NewRCs = RC.removeInternalRefEdge(CN, {&BN});EXPECT_EQ(0u, NewRCs.size());EXPECT_EQ(&RC, CG.lookupRefSCC(AN));EXPECT_EQ(&RC, CG.lookupRefSCC(BN));EXPECT_EQ(&RC, CG.lookupRefSCC(CN));EXPECT_EQ(&C, CG.lookupSCC(AN));EXPECT_EQ(&C, CG.lookupSCC(BN));EXPECT_EQ(&C, CG.lookupSCC(CN));J = CG.postorder_ref_scc_begin();EXPECT_EQ(I, J);EXPECT_EQ(&RC, &*J);EXPECT_EQ(E, std::next(J));}TEST(LazyCallGraphTest, InternalCallEdgeToRef) {LLVMContext Context;// A nice fully connected (including self-edges) SCC (and RefSCC)std::unique_ptr<Module> M = parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @a()\n"" call void @b()\n"" call void @c()\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" call void @a()\n"" call void @b()\n"" call void @c()\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" call void @a()\n"" call void @b()\n"" call void @c()\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);EXPECT_EQ(1, RC.size());LazyCallGraph::SCC &AC = *RC.begin();LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c"));EXPECT_EQ(&AC, CG.lookupSCC(AN));EXPECT_EQ(&AC, CG.lookupSCC(BN));EXPECT_EQ(&AC, CG.lookupSCC(CN));// Remove the call edge from b -> a to a ref edge, which should leave the// 3 functions still in a single connected component because of a -> b ->// c -> a.auto NewCs = RC.switchInternalEdgeToRef(BN, AN);EXPECT_EQ(NewCs.begin(), NewCs.end());EXPECT_EQ(1, RC.size());EXPECT_EQ(&AC, CG.lookupSCC(AN));EXPECT_EQ(&AC, CG.lookupSCC(BN));EXPECT_EQ(&AC, CG.lookupSCC(CN));// Remove the edge from c -> a, which should leave 'a' in the original SCC// and form a new SCC for 'b' and 'c'.NewCs = RC.switchInternalEdgeToRef(CN, AN);EXPECT_EQ(1, std::distance(NewCs.begin(), NewCs.end()));EXPECT_EQ(2, RC.size());EXPECT_EQ(&AC, CG.lookupSCC(AN));LazyCallGraph::SCC &BC = *CG.lookupSCC(BN);EXPECT_NE(&BC, &AC);EXPECT_EQ(&BC, CG.lookupSCC(CN));auto J = RC.find(AC);EXPECT_EQ(&AC, &*J);--J;EXPECT_EQ(&BC, &*J);EXPECT_EQ(RC.begin(), J);EXPECT_EQ(J, NewCs.begin());// Remove the edge from c -> b, which should leave 'b' in the original SCC// and form a new SCC for 'c'. It shouldn't change 'a's SCC.NewCs = RC.switchInternalEdgeToRef(CN, BN);EXPECT_EQ(1, std::distance(NewCs.begin(), NewCs.end()));EXPECT_EQ(3, RC.size());EXPECT_EQ(&AC, CG.lookupSCC(AN));EXPECT_EQ(&BC, CG.lookupSCC(BN));LazyCallGraph::SCC &CC = *CG.lookupSCC(CN);EXPECT_NE(&CC, &AC);EXPECT_NE(&CC, &BC);J = RC.find(AC);EXPECT_EQ(&AC, &*J);--J;EXPECT_EQ(&BC, &*J);--J;EXPECT_EQ(&CC, &*J);EXPECT_EQ(RC.begin(), J);EXPECT_EQ(J, NewCs.begin());}TEST(LazyCallGraphTest, InternalRefEdgeToCall) {LLVMContext Context;// Basic tests for making a ref edge a call. This hits the basics of the// process only.std::unique_ptr<Module> M =parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @b()\n"" call void @c()\n"" store void()* @d, void()** undef\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" store void()* @c, void()** undef\n"" call void @d()\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" store void()* @b, void()** undef\n"" call void @d()\n"" ret void\n""}\n""define void @d() {\n""entry:\n"" store void()* @a, void()** undef\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));LazyCallGraph::SCC &AC = *CG.lookupSCC(A);LazyCallGraph::SCC &BC = *CG.lookupSCC(B);LazyCallGraph::SCC &CC = *CG.lookupSCC(C);LazyCallGraph::SCC &DC = *CG.lookupSCC(D);// Check the initial post-order. Note that B and C could be flipped here (and// in our mutation) without changing the nature of this test.ASSERT_EQ(4, RC.size());EXPECT_EQ(&DC, &RC[0]);EXPECT_EQ(&BC, &RC[1]);EXPECT_EQ(&CC, &RC[2]);EXPECT_EQ(&AC, &RC[3]);// Switch the ref edge from A -> D to a call edge. This should have no// effect as it is already in postorder and no new cycles are formed.EXPECT_FALSE(RC.switchInternalEdgeToCall(A, D));ASSERT_EQ(4, RC.size());EXPECT_EQ(&DC, &RC[0]);EXPECT_EQ(&BC, &RC[1]);EXPECT_EQ(&CC, &RC[2]);EXPECT_EQ(&AC, &RC[3]);// Switch B -> C to a call edge. This doesn't form any new cycles but does// require reordering the SCCs.EXPECT_FALSE(RC.switchInternalEdgeToCall(B, C));ASSERT_EQ(4, RC.size());EXPECT_EQ(&DC, &RC[0]);EXPECT_EQ(&CC, &RC[1]);EXPECT_EQ(&BC, &RC[2]);EXPECT_EQ(&AC, &RC[3]);// Switch C -> B to a call edge. This forms a cycle and forces merging SCCs.EXPECT_TRUE(RC.switchInternalEdgeToCall(C, B, [&](ArrayRef<LazyCallGraph::SCC *> MergedCs) {ASSERT_EQ(1u, MergedCs.size());EXPECT_EQ(&CC, MergedCs[0]);}));ASSERT_EQ(3, RC.size());EXPECT_EQ(&DC, &RC[0]);EXPECT_EQ(&BC, &RC[1]);EXPECT_EQ(&AC, &RC[2]);EXPECT_EQ(2, BC.size());EXPECT_EQ(&BC, CG.lookupSCC(B));EXPECT_EQ(&BC, CG.lookupSCC(C));}TEST(LazyCallGraphTest, InternalRefEdgeToCallNoCycleInterleaved) {LLVMContext Context;// Test for having a post-order prior to changing a ref edge to a call edge// with SCCs connecting to the source and connecting to the target, but not// connecting to both, interleaved between the source and target. This// ensures we correctly partition the range rather than simply moving one or// the other.std::unique_ptr<Module> M =parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @b1()\n"" call void @c1()\n"" ret void\n""}\n""define void @b1() {\n""entry:\n"" call void @c1()\n"" call void @b2()\n"" ret void\n""}\n""define void @c1() {\n""entry:\n"" call void @b2()\n"" call void @c2()\n"" ret void\n""}\n""define void @b2() {\n""entry:\n"" call void @c2()\n"" call void @b3()\n"" ret void\n""}\n""define void @c2() {\n""entry:\n"" call void @b3()\n"" call void @c3()\n"" ret void\n""}\n""define void @b3() {\n""entry:\n"" call void @c3()\n"" call void @d()\n"" ret void\n""}\n""define void @c3() {\n""entry:\n"" store void()* @b1, void()** undef\n"" call void @d()\n"" ret void\n""}\n""define void @d() {\n""entry:\n"" store void()* @a, void()** undef\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B1 = *CG.lookup(lookupFunction(*M, "b1"));LazyCallGraph::Node &B2 = *CG.lookup(lookupFunction(*M, "b2"));LazyCallGraph::Node &B3 = *CG.lookup(lookupFunction(*M, "b3"));LazyCallGraph::Node &C1 = *CG.lookup(lookupFunction(*M, "c1"));LazyCallGraph::Node &C2 = *CG.lookup(lookupFunction(*M, "c2"));LazyCallGraph::Node &C3 = *CG.lookup(lookupFunction(*M, "c3"));LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));LazyCallGraph::SCC &AC = *CG.lookupSCC(A);LazyCallGraph::SCC &B1C = *CG.lookupSCC(B1);LazyCallGraph::SCC &B2C = *CG.lookupSCC(B2);LazyCallGraph::SCC &B3C = *CG.lookupSCC(B3);LazyCallGraph::SCC &C1C = *CG.lookupSCC(C1);LazyCallGraph::SCC &C2C = *CG.lookupSCC(C2);LazyCallGraph::SCC &C3C = *CG.lookupSCC(C3);LazyCallGraph::SCC &DC = *CG.lookupSCC(D);// Several call edges are initially present to force a particual post-order.// Remove them now, leaving an interleaved post-order pattern.RC.switchTrivialInternalEdgeToRef(B3, C3);RC.switchTrivialInternalEdgeToRef(C2, B3);RC.switchTrivialInternalEdgeToRef(B2, C2);RC.switchTrivialInternalEdgeToRef(C1, B2);RC.switchTrivialInternalEdgeToRef(B1, C1);// Check the initial post-order. We ensure this order with the extra edges// that are nuked above.ASSERT_EQ(8, RC.size());EXPECT_EQ(&DC, &RC[0]);EXPECT_EQ(&C3C, &RC[1]);EXPECT_EQ(&B3C, &RC[2]);EXPECT_EQ(&C2C, &RC[3]);EXPECT_EQ(&B2C, &RC[4]);EXPECT_EQ(&C1C, &RC[5]);EXPECT_EQ(&B1C, &RC[6]);EXPECT_EQ(&AC, &RC[7]);// Switch C3 -> B1 to a call edge. This doesn't form any new cycles but does// require reordering the SCCs in the face of tricky internal node// structures.EXPECT_FALSE(RC.switchInternalEdgeToCall(C3, B1));ASSERT_EQ(8, RC.size());EXPECT_EQ(&DC, &RC[0]);EXPECT_EQ(&B3C, &RC[1]);EXPECT_EQ(&B2C, &RC[2]);EXPECT_EQ(&B1C, &RC[3]);EXPECT_EQ(&C3C, &RC[4]);EXPECT_EQ(&C2C, &RC[5]);EXPECT_EQ(&C1C, &RC[6]);EXPECT_EQ(&AC, &RC[7]);}TEST(LazyCallGraphTest, InternalRefEdgeToCallBothPartitionAndMerge) {LLVMContext Context;// Test for having a postorder where between the source and target are all// three kinds of other SCCs:// 1) One connected to the target only that have to be shifted below the// source.// 2) One connected to the source only that have to be shifted below the// target.// 3) One connected to both source and target that has to remain and get// merged away.//// To achieve this we construct a heavily connected graph to force// a particular post-order. Then we remove the forcing edges and connect// a cycle.//// Diagram for the graph we want on the left and the graph we use to force// the ordering on the right. Edges ponit down or right.//// A | A |// / \ | / \ |// B E | B \ |// |\ | | |\ | |// | D | | C-D-E |// | \| | | \| |// C F | \ F |// \ / | \ / |// G | G |//// And we form a cycle by connecting F to B.std::unique_ptr<Module> M =parseAssembly(Context, "define void @a() {\n""entry:\n"" call void @b()\n"" call void @e()\n"" ret void\n""}\n""define void @b() {\n""entry:\n"" call void @c()\n"" call void @d()\n"" ret void\n""}\n""define void @c() {\n""entry:\n"" call void @d()\n"" call void @g()\n"" ret void\n""}\n""define void @d() {\n""entry:\n"" call void @e()\n"" call void @f()\n"" ret void\n""}\n""define void @e() {\n""entry:\n"" call void @f()\n"" ret void\n""}\n""define void @f() {\n""entry:\n"" store void()* @b, void()** undef\n"" call void @g()\n"" ret void\n""}\n""define void @g() {\n""entry:\n"" store void()* @a, void()** undef\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);LazyCallGraph::Node &A = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &B = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &C = *CG.lookup(lookupFunction(*M, "c"));LazyCallGraph::Node &D = *CG.lookup(lookupFunction(*M, "d"));LazyCallGraph::Node &E = *CG.lookup(lookupFunction(*M, "e"));LazyCallGraph::Node &F = *CG.lookup(lookupFunction(*M, "f"));LazyCallGraph::Node &G = *CG.lookup(lookupFunction(*M, "g"));LazyCallGraph::SCC &AC = *CG.lookupSCC(A);LazyCallGraph::SCC &BC = *CG.lookupSCC(B);LazyCallGraph::SCC &CC = *CG.lookupSCC(C);LazyCallGraph::SCC &DC = *CG.lookupSCC(D);LazyCallGraph::SCC &EC = *CG.lookupSCC(E);LazyCallGraph::SCC &FC = *CG.lookupSCC(F);LazyCallGraph::SCC &GC = *CG.lookupSCC(G);// Remove the extra edges that were used to force a particular post-order.RC.switchTrivialInternalEdgeToRef(C, D);RC.switchTrivialInternalEdgeToRef(D, E);// Check the initial post-order. We ensure this order with the extra edges// that are nuked above.ASSERT_EQ(7, RC.size());EXPECT_EQ(&GC, &RC[0]);EXPECT_EQ(&FC, &RC[1]);EXPECT_EQ(&EC, &RC[2]);EXPECT_EQ(&DC, &RC[3]);EXPECT_EQ(&CC, &RC[4]);EXPECT_EQ(&BC, &RC[5]);EXPECT_EQ(&AC, &RC[6]);// Switch F -> B to a call edge. This merges B, D, and F into a single SCC,// and has to place the C and E SCCs on either side of it:// A A |// / \ / \ |// B E | E |// |\ | \ / |// | D | -> B |// | \| / \ |// C F C | |// \ / \ / |// G G |EXPECT_TRUE(RC.switchInternalEdgeToCall(F, B, [&](ArrayRef<LazyCallGraph::SCC *> MergedCs) {ASSERT_EQ(2u, MergedCs.size());EXPECT_EQ(&FC, MergedCs[0]);EXPECT_EQ(&DC, MergedCs[1]);}));EXPECT_EQ(3, BC.size());// And make sure the postorder was updated.ASSERT_EQ(5, RC.size());EXPECT_EQ(&GC, &RC[0]);EXPECT_EQ(&CC, &RC[1]);EXPECT_EQ(&BC, &RC[2]);EXPECT_EQ(&EC, &RC[3]);EXPECT_EQ(&AC, &RC[4]);}// Test for IR containing constants using blockaddress constant expressions.// These are truly unique constructs: constant expressions with non-constant// operands.TEST(LazyCallGraphTest, HandleBlockAddress) {LLVMContext Context;std::unique_ptr<Module> M =parseAssembly(Context, "define void @f() {\n""entry:\n"" ret void\n""bb:\n"" unreachable\n""}\n""define void @g(i8** %ptr) {\n""entry:\n"" store i8* blockaddress(@f, %bb), i8** %ptr\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &FRC = *I++;LazyCallGraph::RefSCC &GRC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);LazyCallGraph::Node &F = *CG.lookup(lookupFunction(*M, "f"));LazyCallGraph::Node &G = *CG.lookup(lookupFunction(*M, "g"));EXPECT_EQ(&FRC, CG.lookupRefSCC(F));EXPECT_EQ(&GRC, CG.lookupRefSCC(G));EXPECT_FALSE(GRC.isParentOf(FRC));EXPECT_FALSE(FRC.isParentOf(GRC));}// Test that a blockaddress that refers to itself creates no new RefSCC// connections. https://bugs.llvm.org/show_bug.cgi?id=40722TEST(LazyCallGraphTest, HandleBlockAddress2) {LLVMContext Context;std::unique_ptr<Module> M =parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n""define void @g(i8** %ptr) {\n""bb:\n"" store i8* blockaddress(@g, %bb), i8** %ptr\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &FRC = *I++;LazyCallGraph::RefSCC &GRC = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);LazyCallGraph::Node &F = *CG.lookup(lookupFunction(*M, "f"));LazyCallGraph::Node &G = *CG.lookup(lookupFunction(*M, "g"));EXPECT_EQ(&FRC, CG.lookupRefSCC(F));EXPECT_EQ(&GRC, CG.lookupRefSCC(G));EXPECT_FALSE(GRC.isParentOf(FRC));EXPECT_FALSE(FRC.isParentOf(GRC));}TEST(LazyCallGraphTest, ReplaceNodeFunction) {LLVMContext Context;// A graph with several different kinds of edges pointing at a particular// function.std::unique_ptr<Module> M =parseAssembly(Context,"define void @a(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n"" ret void\n""}\n""define void @b(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n"" store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n"" call void @d(i8** %ptr)"" ret void\n""}\n""define void @c(i8** %ptr) {\n""entry:\n"" call void @d(i8** %ptr)"" call void @d(i8** %ptr)"" store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n"" ret void\n""}\n""define void @d(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" call void @c(i8** %ptr)"" call void @d(i8** %ptr)"" store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &RC1 = *I++;LazyCallGraph::RefSCC &RC2 = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);ASSERT_EQ(2, RC1.size());LazyCallGraph::SCC &C1 = RC1[0];LazyCallGraph::SCC &C2 = RC1[1];LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a"));LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b"));LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c"));LazyCallGraph::Node &DN = *CG.lookup(lookupFunction(*M, "d"));EXPECT_EQ(&C1, CG.lookupSCC(DN));EXPECT_EQ(&C1, CG.lookupSCC(CN));EXPECT_EQ(&C2, CG.lookupSCC(BN));EXPECT_EQ(&RC1, CG.lookupRefSCC(DN));EXPECT_EQ(&RC1, CG.lookupRefSCC(CN));EXPECT_EQ(&RC1, CG.lookupRefSCC(BN));EXPECT_EQ(&RC2, CG.lookupRefSCC(AN));// Now we need to build a new function 'e' with the same signature as 'd'.Function &D = DN.getFunction();Function &E = *Function::Create(D.getFunctionType(), D.getLinkage(), "e");D.getParent()->getFunctionList().insert(D.getIterator(), &E);// Change each use of 'd' to use 'e'. This is particularly easy as they have// the same type.D.replaceAllUsesWith(&E);// Splice the body of the old function into the new one.E.getBasicBlockList().splice(E.begin(), D.getBasicBlockList());// And fix up the one argument.D.arg_begin()->replaceAllUsesWith(&*E.arg_begin());E.arg_begin()->takeName(&*D.arg_begin());// Now replace the function in the graph.RC1.replaceNodeFunction(DN, E);EXPECT_EQ(&E, &DN.getFunction());EXPECT_EQ(&DN, &(*CN)[DN].getNode());EXPECT_EQ(&DN, &(*BN)[DN].getNode());}TEST(LazyCallGraphTest, RemoveFunctionWithSpurriousRef) {LLVMContext Context;// A graph with a couple of RefSCCs.std::unique_ptr<Module> M =parseAssembly(Context,"define void @a(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n"" ret void\n""}\n""define void @b(i8** %ptr) {\n""entry:\n"" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"" ret void\n""}\n""define void @c(i8** %ptr) {\n""entry:\n"" call void @d(i8** %ptr)"" ret void\n""}\n""define void @d(i8** %ptr) {\n""entry:\n"" call void @c(i8** %ptr)"" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"" ret void\n""}\n""define void @dead() {\n""entry:\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);// Insert spurious ref edges.LazyCallGraph::Node &AN = CG.get(lookupFunction(*M, "a"));LazyCallGraph::Node &BN = CG.get(lookupFunction(*M, "b"));LazyCallGraph::Node &CN = CG.get(lookupFunction(*M, "c"));LazyCallGraph::Node &DN = CG.get(lookupFunction(*M, "d"));LazyCallGraph::Node &DeadN = CG.get(lookupFunction(*M, "dead"));AN.populate();BN.populate();CN.populate();DN.populate();DeadN.populate();CG.insertEdge(AN, DeadN, LazyCallGraph::Edge::Ref);CG.insertEdge(BN, DeadN, LazyCallGraph::Edge::Ref);CG.insertEdge(CN, DeadN, LazyCallGraph::Edge::Ref);CG.insertEdge(DN, DeadN, LazyCallGraph::Edge::Ref);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC &DeadRC = *I++;LazyCallGraph::RefSCC &RC1 = *I++;LazyCallGraph::RefSCC &RC2 = *I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);ASSERT_EQ(2, RC1.size());LazyCallGraph::SCC &C1 = RC1[0];LazyCallGraph::SCC &C2 = RC1[1];EXPECT_EQ(&DeadRC, CG.lookupRefSCC(DeadN));EXPECT_EQ(&C1, CG.lookupSCC(DN));EXPECT_EQ(&C1, CG.lookupSCC(CN));EXPECT_EQ(&C2, CG.lookupSCC(BN));EXPECT_EQ(&RC1, CG.lookupRefSCC(DN));EXPECT_EQ(&RC1, CG.lookupRefSCC(CN));EXPECT_EQ(&RC1, CG.lookupRefSCC(BN));EXPECT_EQ(&RC2, CG.lookupRefSCC(AN));// Now delete 'dead'. There are no uses of this function but there are// spurious references.CG.removeDeadFunction(DeadN.getFunction());// The only observable change should be that the RefSCC is gone from the// postorder sequence.I = CG.postorder_ref_scc_begin();EXPECT_EQ(&RC1, &*I++);EXPECT_EQ(&RC2, &*I++);EXPECT_EQ(CG.postorder_ref_scc_end(), I);}TEST(LazyCallGraphTest, AddSplitFunction1) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);(void)ReturnInst::Create(Context, GBB);// Create f -call-> g.(void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC1 = &*I++;EXPECT_EQ(RC1, CG.lookupRefSCC(*GN));LazyCallGraph::RefSCC *RC2 = &*I++;EXPECT_EQ(RC2, ORC);EXPECT_EQ(RC2, CG.lookupRefSCC(FN));EXPECT_EQ(CG.postorder_ref_scc_end(), I);}TEST(LazyCallGraphTest, AddSplitFunction2) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);(void)ReturnInst::Create(Context, GBB);// Create f -ref-> g.(void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC1 = &*I++;EXPECT_EQ(RC1, CG.lookupRefSCC(*GN));LazyCallGraph::RefSCC *RC2 = &*I++;EXPECT_EQ(RC2, ORC);EXPECT_EQ(RC2, CG.lookupRefSCC(FN));EXPECT_EQ(CG.postorder_ref_scc_end(), I);}TEST(LazyCallGraphTest, AddSplitFunction3) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -ref-> f.(void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -call-> g.(void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);EXPECT_EQ(2, RC->size());EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[1]);}TEST(LazyCallGraphTest, AddSplitFunction4) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -ref-> f.(void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -ref-> g.(void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);// Order doesn't matter for sibling SCCs.EXPECT_EQ(2, RC->size());EXPECT_EQ(&CG.lookupSCC(*GN)->getOuterRefSCC(), RC);EXPECT_EQ(&CG.lookupSCC(FN)->getOuterRefSCC(), RC);}TEST(LazyCallGraphTest, AddSplitFunction5) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -call-> f.(void)CallInst::Create(&F, {}, "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -ref-> g.(void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);EXPECT_EQ(2, RC->size());EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[1]);}TEST(LazyCallGraphTest, AddSplitFunction6) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -call-> f.(void)CallInst::Create(&F, {}, "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -call-> g.(void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);EXPECT_EQ(1, RC->size());EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]);}TEST(LazyCallGraphTest, AddSplitFunction7) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" call void @f2()\n"" ret void\n""}\n""define void @f2() {\n"" call void @f()\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);Function &F2 = lookupFunction(*M, "f2");LazyCallGraph::Node &F2N = CG.get(F2);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -call-> f2.(void)CallInst::Create(&F2, {}, "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -call-> g.(void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);EXPECT_EQ(1, RC->size());EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(F2N), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]);}TEST(LazyCallGraphTest, AddSplitFunction8) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" call void @f2()\n"" ret void\n""}\n""define void @f2() {\n"" call void @f()\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);Function &F2 = lookupFunction(*M, "f2");LazyCallGraph::Node &F2N = CG.get(F2);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -call-> f2.(void)CallInst::Create(&F2, {}, "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -ref-> g.(void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);EXPECT_EQ(2, RC->size());EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(F2N), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[1]);}TEST(LazyCallGraphTest, AddSplitFunction9) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" call void @f2()\n"" ret void\n""}\n""define void @f2() {\n"" call void @f()\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);Function &F2 = lookupFunction(*M, "f2");LazyCallGraph::Node &F2N = CG.get(F2);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -ref-> f2.(void)CastInst::CreatePointerCast(&F2, Type::getInt8PtrTy(Context), "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -call-> g.(void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitFunction(F, *G);LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);EXPECT_EQ(2, RC->size());EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]);EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[1]);EXPECT_EQ(CG.lookupSCC(F2N), &(*RC)[1]);}TEST(LazyCallGraphTest, AddSplitFunctions1) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);(void)ReturnInst::Create(Context, GBB);// Create f -ref-> g.(void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G}));LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC1 = &*I++;EXPECT_EQ(RC1, CG.lookupRefSCC(*GN));LazyCallGraph::RefSCC *RC2 = &*I++;EXPECT_EQ(RC2, ORC);EXPECT_EQ(RC2, CG.lookupRefSCC(FN));EXPECT_EQ(CG.postorder_ref_scc_end(), I);}TEST(LazyCallGraphTest, AddSplitFunctions2) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());BasicBlock *GBB = BasicBlock::Create(Context, "", G);// Create g -ref-> f.(void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", GBB);(void)ReturnInst::Create(Context, GBB);// Create f -ref-> g.(void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G}));LazyCallGraph::Node *GN = CG.lookup(*G);EXPECT_TRUE(GN);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, CG.lookupRefSCC(*GN));EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);// Order doesn't matter for sibling SCCs.EXPECT_EQ(2, RC->size());EXPECT_EQ(&CG.lookupSCC(*GN)->getOuterRefSCC(), RC);EXPECT_EQ(&CG.lookupSCC(FN)->getOuterRefSCC(), RC);}TEST(LazyCallGraphTest, AddSplitFunctions3) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G1 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g1", F.getParent());auto *G2 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g2", F.getParent());BasicBlock *G1BB = BasicBlock::Create(Context, "", G1);BasicBlock *G2BB = BasicBlock::Create(Context, "", G2);// Create g1 -ref-> g2 and g2 -ref-> g1.(void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", G1BB);(void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", G2BB);(void)ReturnInst::Create(Context, G1BB);(void)ReturnInst::Create(Context, G2BB);// Create f -ref-> g1 and f -ref-> g2.(void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());(void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G1, G2}));LazyCallGraph::Node *G1N = CG.lookup(*G1);EXPECT_TRUE(G1N);LazyCallGraph::Node *G2N = CG.lookup(*G2);EXPECT_TRUE(G2N);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC1 = &*I++;EXPECT_EQ(2, RC1->size());EXPECT_EQ(RC1, CG.lookupRefSCC(*G1N));EXPECT_EQ(RC1, CG.lookupRefSCC(*G2N));LazyCallGraph::RefSCC *RC2 = &*I++;EXPECT_EQ(RC2, ORC);EXPECT_EQ(RC2, CG.lookupRefSCC(FN));EXPECT_EQ(CG.postorder_ref_scc_end(), I);}TEST(LazyCallGraphTest, AddSplitFunctions4) {LLVMContext Context;std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G1 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g1", F.getParent());auto *G2 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g2", F.getParent());BasicBlock *G1BB = BasicBlock::Create(Context, "", G1);BasicBlock *G2BB = BasicBlock::Create(Context, "", G2);// Create g1 -ref-> g2 and g2 -ref-> g1.(void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", G1BB);(void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", G2BB);// Create g2 -ref-> f.(void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", G2BB);(void)ReturnInst::Create(Context, G1BB);(void)ReturnInst::Create(Context, G2BB);// Create f -ref-> g1 and f -ref-> g2.(void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());(void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G1, G2}));LazyCallGraph::Node *G1N = CG.lookup(*G1);EXPECT_TRUE(G1N);LazyCallGraph::Node *G2N = CG.lookup(*G2);EXPECT_TRUE(G2N);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(RC, ORC);EXPECT_EQ(CG.postorder_ref_scc_end(), I);// Order doesn't matter for sibling SCCs.EXPECT_EQ(3, RC->size());EXPECT_EQ(&CG.lookupSCC(FN)->getOuterRefSCC(), RC);EXPECT_EQ(&CG.lookupSCC(*G1N)->getOuterRefSCC(), RC);EXPECT_EQ(&CG.lookupSCC(*G2N)->getOuterRefSCC(), RC);EXPECT_EQ(RC, CG.lookupRefSCC(*G1N));EXPECT_EQ(RC, CG.lookupRefSCC(*G2N));}TEST(LazyCallGraphTest, AddSplitFunctions5) {LLVMContext Context;std::unique_ptr<Module> M =parseAssembly(Context, "define void @f() {\n"" %1 = bitcast void ()* @f2 to i8*\n"" ret void\n""}\n""define void @f2() {\n"" call void @f()\n"" ret void\n""}\n");LazyCallGraph CG = buildCG(*M);Function &F = lookupFunction(*M, "f");LazyCallGraph::Node &FN = CG.get(F);Function &F2 = lookupFunction(*M, "f2");LazyCallGraph::Node &F2N = CG.get(F);// Force the graph to be fully expanded.CG.buildRefSCCs();auto I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *ORC = &*I++;EXPECT_EQ(CG.postorder_ref_scc_end(), I);auto *G1 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g1", F.getParent());auto *G2 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g2", F.getParent());BasicBlock *G1BB = BasicBlock::Create(Context, "", G1);BasicBlock *G2BB = BasicBlock::Create(Context, "", G2);// Create g1 -ref-> g2 and g2 -ref-> g1.(void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", G1BB);(void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", G2BB);// Create g2 -ref-> f2.(void)CastInst::CreatePointerCast(&F2, Type::getInt8PtrTy(Context), "", G2BB);(void)ReturnInst::Create(Context, G1BB);(void)ReturnInst::Create(Context, G2BB);// Create f -ref-> g1 and f -ref-> g2.(void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());(void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "",&*F.getEntryBlock().begin());EXPECT_FALSE(verifyModule(*M, &errs()));CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G1, G2}));LazyCallGraph::Node *G1N = CG.lookup(*G1);EXPECT_TRUE(G1N);LazyCallGraph::Node *G2N = CG.lookup(*G2);EXPECT_TRUE(G2N);I = CG.postorder_ref_scc_begin();LazyCallGraph::RefSCC *RC = &*I++;EXPECT_EQ(4, RC->size());EXPECT_EQ(RC, ORC);EXPECT_EQ(RC, CG.lookupRefSCC(*G1N));EXPECT_EQ(RC, CG.lookupRefSCC(*G2N));EXPECT_EQ(RC, CG.lookupRefSCC(FN));EXPECT_EQ(RC, CG.lookupRefSCC(F2N));EXPECT_EQ(CG.postorder_ref_scc_end(), I);}}
saved_model_schema_version: 1meta_graphs {meta_info_def {stripped_op_list {op {name: "Const"output_arg {name: "output"type_attr: "dtype"}attr {name: "value"type: "tensor"}attr {name: "dtype"type: "type"}}op {name: "NoOp"}op {name: "Placeholder"output_arg {name: "output"type_attr: "dtype"}attr {name: "dtype"type: "type"}attr {name: "shape"type: "shape"default_value {shape {unknown_rank: true}}}}op {name: "ReadVariableOp"input_arg {name: "resource"type: DT_RESOURCE}output_arg {name: "value"type_attr: "dtype"}attr {name: "dtype"type: "type"}is_stateful: true}op {name: "StatefulPartitionedCall"input_arg {name: "args"type_list_attr: "Tin"}output_arg {name: "output"type_list_attr: "Tout"}attr {name: "Tin"type: "list(type)"has_minimum: true}attr {name: "Tout"type: "list(type)"has_minimum: true}attr {name: "f"type: "func"}attr {name: "config"type: "string"default_value {s: ""}}attr {name: "config_proto"type: "string"default_value {s: ""}}attr {name: "executor_type"type: "string"default_value {s: ""}}is_stateful: true}op {name: "VarHandleOp"output_arg {name: "resource"type: DT_RESOURCE}attr {name: "container"type: "string"default_value {s: ""}}attr {name: "shared_name"type: "string"default_value {s: ""}}attr {name: "dtype"type: "type"}attr {name: "shape"type: "shape"}is_stateful: true}}tags: "serve"tensorflow_version: "1.15.0"tensorflow_git_version: "unknown"stripped_default_attrs: true}graph_def {node {name: "dense/kernel"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {dim {size: 214}dim {size: 100}}}}attr {key: "shared_name"value {s: "dense/kernel"}}}node {name: "dense/kernel/Read/ReadVariableOp"op: "ReadVariableOp"input: "dense/kernel"attr {key: "_output_shapes"value {list {shape {dim {size: 214}dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "dense/bias"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {dim {size: 100}}}}attr {key: "shared_name"value {s: "dense/bias"}}}node {name: "dense/bias/Read/ReadVariableOp"op: "ReadVariableOp"input: "dense/bias"attr {key: "_output_shapes"value {list {shape {dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "dense_1/kernel"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {dim {size: 100}dim {size: 1}}}}attr {key: "shared_name"value {s: "dense_1/kernel"}}}node {name: "dense_1/kernel/Read/ReadVariableOp"op: "ReadVariableOp"input: "dense_1/kernel"attr {key: "_output_shapes"value {list {shape {dim {size: 100}dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "dense_1/bias"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {dim {size: 1}}}}attr {key: "shared_name"value {s: "dense_1/bias"}}}node {name: "dense_1/bias/Read/ReadVariableOp"op: "ReadVariableOp"input: "dense_1/bias"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "total"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {}}}attr {key: "shared_name"value {s: "total"}}}node {name: "total/Read/ReadVariableOp"op: "ReadVariableOp"input: "total"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "count"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {}}}attr {key: "shared_name"value {s: "count"}}}node {name: "count/Read/ReadVariableOp"op: "ReadVariableOp"input: "count"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "total_1"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {}}}attr {key: "shared_name"value {s: "total_1"}}}node {name: "total_1/Read/ReadVariableOp"op: "ReadVariableOp"input: "total_1"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "count_1"op: "VarHandleOp"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}attr {key: "shape"value {shape {}}}attr {key: "shared_name"value {s: "count_1"}}}node {name: "count_1/Read/ReadVariableOp"op: "ReadVariableOp"input: "count_1"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_FLOAT}}}node {name: "NoOp"op: "NoOp"}node {name: "Const"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {}string_val: "\n\277\001\n\030\010\001\022\024layer_with_weights-0\n\013\010\001\022\007layer-0\n\030\010\002\022\024layer_with_weights-1\n\013\010\002\022\007layer-1\n\r\010\003\022\toptimizer\n\031\010\004\022\025regularization_losses\n\r\010\005\022\tvariables\n\027\010\006\022\023trainable_variables\n\r\010\007\022\tkeras_api\n\016\010\010\022\nsignatures\nh\n\n\010\t\022\006kernel\n\010\010\n\022\004bias\n\031\010\013\022\025regularization_losses\n\r\010\014\022\tvariables\n\027\010\r\022\023trainable_variables\n\r\010\016\022\tkeras_api\nh\n\n\010\017\022\006kernel\n\010\010\020\022\004bias\n\031\010\021\022\025regularization_losses\n\r\010\022\022\tvariables\n\027\010\023\022\023trainable_variables\n\r\010\024\022\tkeras_api\n\000\n\000\n\034\n\005\010\t\022\0010\n\005\010\n\022\0011\n\005\010\017\022\0012\n\005\010\020\022\0013\n\034\n\005\010\t\022\0010\n\005\010\n\022\0011\n\005\010\017\022\0012\n\005\010\020\022\0013\n\255\001\n\n\010\025\022\006layers\n\037\010\026\022\033layer_regularization_losses\n\033\010\027\022\027non_trainable_variables\n\021\010\030\022\rlayer_metrics\n\031\010\004\022\025regularization_losses\n\013\010\031\022\007metrics\n\r\010\005\022\tvariables\n\027\010\006\022\023trainable_variables\n\000\nX\022V\n\016VARIABLE_VALUE\022\014dense/kernel\0326layer_with_weights-0/kernel/.ATTRIBUTES/VARIABLE_VALUE\nT\022R\n\016VARIABLE_VALUE\022\ndense/bias\0324layer_with_weights-0/bias/.ATTRIBUTES/VARIABLE_VALUE\n\000\n\016\n\005\010\t\022\0010\n\005\010\n\022\0011\n\016\n\005\010\t\022\0010\n\005\010\n\022\0011\n\255\001\n\n\010\032\022\006layers\n\037\010\033\022\033layer_regularization_losses\n\033\010\034\022\027non_trainable_variables\n\021\010\035\022\rlayer_metrics\n\031\010\013\022\025regularization_losses\n\013\010\036\022\007metrics\n\r\010\014\022\tvariables\n\027\010\r\022\023trainable_variables\nZ\022X\n\016VARIABLE_VALUE\022\016dense_1/kernel\0326layer_with_weights-1/kernel/.ATTRIBUTES/VARIABLE_VALUE\nV\022T\n\016VARIABLE_VALUE\022\014dense_1/bias\0324layer_with_weights-1/bias/.ATTRIBUTES/VARIABLE_VALUE\n\000\n\016\n\005\010\017\022\0010\n\005\010\020\022\0011\n\016\n\005\010\017\022\0010\n\005\010\020\022\0011\n\255\001\n\n\010\037\022\006layers\n\037\010 \022\033layer_regularization_losses\n\033\010!\022\027non_trainable_variables\n\021\010\"\022\rlayer_metrics\n\031\010\021\022\025regularization_losses\n\013\010#\022\007metrics\n\r\010\022\022\tvariables\n\027\010\023\022\023trainable_variables\n\016\n\005\010\001\022\0010\n\005\010\002\022\0011\n\000\n\000\n\000\n\016\n\005\010$\022\0010\n\005\010%\022\0011\n\000\n\000\n\000\n\000\n\000\n\000\n\000\n\000\n\000\n\000\n4\n\t\010&\022\005total\n\t\010\'\022\005count\n\r\010(\022\tvariables\n\r\010)\022\tkeras_api\nD\n\t\010*\022\005total\n\t\010+\022\005count\n\016\010,\022\n_fn_kwargs\n\r\010-\022\tvariables\n\r\010.\022\tkeras_api\nO\022M\n\016VARIABLE_VALUE\022\005total\0324keras_api/metrics/0/total/.ATTRIBUTES/VARIABLE_VALUE\nO\022M\n\016VARIABLE_VALUE\022\005count\0324keras_api/metrics/0/count/.ATTRIBUTES/VARIABLE_VALUE\n\016\n\005\010&\022\0010\n\005\010\'\022\0011\n\017\n\r\010(\022\tvariables\nQ\022O\n\016VARIABLE_VALUE\022\007total_1\0324keras_api/metrics/1/total/.ATTRIBUTES/VARIABLE_VALUE\nQ\022O\n\016VARIABLE_VALUE\022\007count_1\0324keras_api/metrics/1/count/.ATTRIBUTES/VARIABLE_VALUE\n\000\n\016\n\005\010*\022\0010\n\005\010+\022\0011\n\017\n\r\010-\022\tvariables"}}}}node {name: "serving_default_input_1"op: "Placeholder"attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "dtype"value {type: DT_INT32}}attr {key: "shape"value {shape {dim {size: -1}dim {size: 214}}}}}node {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "serving_default_input_1"input: "dense/kernel"input: "dense/bias"input: "dense_1/kernel"input: "dense_1/bias"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2i: 3i: 4}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_signature_wrapper_6671"}}}}node {name: "saver_filename"op: "Placeholder"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "shape"value {shape {}}}}node {name: "StatefulPartitionedCall_1"op: "StatefulPartitionedCall"input: "saver_filename"input: "dense/kernel/Read/ReadVariableOp"input: "dense/bias/Read/ReadVariableOp"input: "dense_1/kernel/Read/ReadVariableOp"input: "dense_1/bias/Read/ReadVariableOp"input: "total/Read/ReadVariableOp"input: "count/Read/ReadVariableOp"input: "total_1/Read/ReadVariableOp"input: "count_1/Read/ReadVariableOp"input: "Const"attr {key: "Tin"value {list {type: DT_STRINGtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_STRING}}}attr {key: "Tout"value {list {type: DT_STRING}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "_read_only_resource_inputs"value {list {}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference__traced_save_6824"}}}}node {name: "StatefulPartitionedCall_2"op: "StatefulPartitionedCall"input: "saver_filename"input: "dense/kernel"input: "dense/bias"input: "dense_1/kernel"input: "dense_1/bias"input: "total"input: "count"input: "total_1"input: "count_1"attr {key: "Tin"value {list {type: DT_STRINGtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_STRING}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "_read_only_resource_inputs"value {list {}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference__traced_restore_6860"}}}}library {function {signature {name: "__inference__traced_restore_6860"input_arg {name: "file_prefix"type: DT_STRING}input_arg {name: "assignvariableop_dense_kernel"type: DT_RESOURCE}input_arg {name: "assignvariableop_1_dense_bias"type: DT_RESOURCE}input_arg {name: "assignvariableop_2_dense_1_kernel"type: DT_RESOURCE}input_arg {name: "assignvariableop_3_dense_1_bias"type: DT_RESOURCE}input_arg {name: "assignvariableop_4_total"type: DT_RESOURCE}input_arg {name: "assignvariableop_5_count"type: DT_RESOURCE}input_arg {name: "assignvariableop_6_total_1"type: DT_RESOURCE}input_arg {name: "assignvariableop_7_count_1"type: DT_RESOURCE}output_arg {name: "identity_9"type: DT_STRING}is_stateful: truecontrol_output: "AssignVariableOp"control_output: "AssignVariableOp_1"control_output: "AssignVariableOp_2"control_output: "AssignVariableOp_3"control_output: "AssignVariableOp_4"control_output: "AssignVariableOp_5"control_output: "AssignVariableOp_6"control_output: "AssignVariableOp_7"control_output: "RestoreV2"control_output: "RestoreV2_1"}node_def {name: "RestoreV2/tensor_names"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 8}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 8}}string_val: "layer_with_weights-0/kernel/.ATTRIBUTES/VARIABLE_VALUE"string_val: "layer_with_weights-0/bias/.ATTRIBUTES/VARIABLE_VALUE"string_val: "layer_with_weights-1/kernel/.ATTRIBUTES/VARIABLE_VALUE"string_val: "layer_with_weights-1/bias/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/0/total/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/0/count/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/1/total/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/1/count/.ATTRIBUTES/VARIABLE_VALUE"}}}experimental_debug_info {original_node_names: "RestoreV2/tensor_names"}}node_def {name: "RestoreV2/shape_and_slices"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 8}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 8}}string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""}}}experimental_debug_info {original_node_names: "RestoreV2/shape_and_slices"}}node_def {name: "RestoreV2"op: "RestoreV2"input: "file_prefix"input: "RestoreV2/tensor_names:output:0"input: "RestoreV2/shape_and_slices:output:0"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}attr {key: "dtypes"value {list {type: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOAT}}}experimental_debug_info {original_node_names: "RestoreV2"}}node_def {name: "Identity"op: "Identity"input: "RestoreV2:tensors:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity"}}node_def {name: "AssignVariableOp"op: "AssignVariableOp"input: "assignvariableop_dense_kernel"input: "Identity:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp"}}node_def {name: "Identity_1"op: "Identity"input: "RestoreV2:tensors:1"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity_1"}}node_def {name: "AssignVariableOp_1"op: "AssignVariableOp"input: "assignvariableop_1_dense_bias"input: "Identity_1:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp_1"}}node_def {name: "Identity_2"op: "Identity"input: "RestoreV2:tensors:2"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity_2"}}node_def {name: "AssignVariableOp_2"op: "AssignVariableOp"input: "assignvariableop_2_dense_1_kernel"input: "Identity_2:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp_2"}}node_def {name: "Identity_3"op: "Identity"input: "RestoreV2:tensors:3"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity_3"}}node_def {name: "AssignVariableOp_3"op: "AssignVariableOp"input: "assignvariableop_3_dense_1_bias"input: "Identity_3:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp_3"}}node_def {name: "Identity_4"op: "Identity"input: "RestoreV2:tensors:4"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity_4"}}node_def {name: "AssignVariableOp_4"op: "AssignVariableOp"input: "assignvariableop_4_total"input: "Identity_4:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp_4"}}node_def {name: "Identity_5"op: "Identity"input: "RestoreV2:tensors:5"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity_5"}}node_def {name: "AssignVariableOp_5"op: "AssignVariableOp"input: "assignvariableop_5_count"input: "Identity_5:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp_5"}}node_def {name: "Identity_6"op: "Identity"input: "RestoreV2:tensors:6"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity_6"}}node_def {name: "AssignVariableOp_6"op: "AssignVariableOp"input: "assignvariableop_6_total_1"input: "Identity_6:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp_6"}}node_def {name: "Identity_7"op: "Identity"input: "RestoreV2:tensors:7"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}experimental_debug_info {original_node_names: "Identity_7"}}node_def {name: "AssignVariableOp_7"op: "AssignVariableOp"input: "assignvariableop_7_count_1"input: "Identity_7:output:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "AssignVariableOp_7"}}node_def {name: "RestoreV2_1/tensor_names"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 1}}string_val: "_CHECKPOINTABLE_OBJECT_GRAPH"}}}experimental_debug_info {original_node_names: "RestoreV2_1/tensor_names"}}node_def {name: "RestoreV2_1/shape_and_slices"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 1}}string_val: ""}}}experimental_debug_info {original_node_names: "RestoreV2_1/shape_and_slices"}}node_def {name: "RestoreV2_1"op: "RestoreV2"input: "file_prefix"input: "RestoreV2_1/tensor_names:output:0"input: "RestoreV2_1/shape_and_slices:output:0"input: "^RestoreV2"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {unknown_rank: true}}}}attr {key: "dtypes"value {list {type: DT_STRING}}}experimental_debug_info {original_node_names: "RestoreV2_1"}}node_def {name: "NoOp"op: "NoOp"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {}}}experimental_debug_info {original_node_names: "NoOp"}}node_def {name: "Identity_8"op: "Identity"input: "file_prefix"input: "^AssignVariableOp"input: "^AssignVariableOp_1"input: "^AssignVariableOp_2"input: "^AssignVariableOp_3"input: "^AssignVariableOp_4"input: "^AssignVariableOp_5"input: "^AssignVariableOp_6"input: "^AssignVariableOp_7"input: "^NoOp"device: "/device:CPU:0"attr {key: "T"value {type: DT_STRING}}attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "Identity_8"}}node_def {name: "Identity_9"op: "Identity"input: "Identity_8:output:0"input: "^AssignVariableOp"input: "^AssignVariableOp_1"input: "^AssignVariableOp_2"input: "^AssignVariableOp_3"input: "^AssignVariableOp_4"input: "^AssignVariableOp_5"input: "^AssignVariableOp_6"input: "^AssignVariableOp_7"input: "^RestoreV2"input: "^RestoreV2_1"attr {key: "T"value {type: DT_STRING}}attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "Identity_9"}}ret {key: "identity_9"value: "Identity_9:output:0"}attr {key: "_input_shapes"value {list {shape {}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "AssignVariableOp"value: "AssignVariableOp"}control_ret {key: "AssignVariableOp_1"value: "AssignVariableOp_1"}control_ret {key: "AssignVariableOp_2"value: "AssignVariableOp_2"}control_ret {key: "AssignVariableOp_3"value: "AssignVariableOp_3"}control_ret {key: "AssignVariableOp_4"value: "AssignVariableOp_4"}control_ret {key: "AssignVariableOp_5"value: "AssignVariableOp_5"}control_ret {key: "AssignVariableOp_6"value: "AssignVariableOp_6"}control_ret {key: "AssignVariableOp_7"value: "AssignVariableOp_7"}control_ret {key: "RestoreV2"value: "RestoreV2"}control_ret {key: "RestoreV2_1"value: "RestoreV2_1"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "_user_specified_name"value {s: "file_prefix"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 5value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 6value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 7value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 8value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_fn_6629"input_arg {name: "input_1"type: DT_INT32}input_arg {name: "unknown"type: DT_RESOURCE}input_arg {name: "unknown_0"type: DT_RESOURCE}input_arg {name: "unknown_1"type: DT_RESOURCE}input_arg {name: "unknown_2"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "StatefulPartitionedCall"}node_def {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "input_1"input: "unknown"input: "unknown_0"input: "unknown_1"input: "unknown_2"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2i: 3i: 4}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_sequential_layer_call_and_return_conditional_losses_6618"}}}experimental_debug_info {original_node_names: "StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "StatefulPartitionedCall:output:0"input: "^StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "StatefulPartitionedCall"value: "StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "input_1"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_and_return_conditional_losses_6587"input_arg {name: "input_1"type: DT_INT32}input_arg {name: "dense_6555"type: DT_RESOURCE}input_arg {name: "dense_6557"type: DT_RESOURCE}input_arg {name: "dense_1_6581"type: DT_RESOURCE}input_arg {name: "dense_1_6583"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "dense/StatefulPartitionedCall"control_output: "dense_1/StatefulPartitionedCall"}node_def {name: "dense/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "input_1"input: "dense_6555"input: "dense_6557"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_layer_call_and_return_conditional_losses_6544"}}}experimental_debug_info {original_node_names: "dense/StatefulPartitionedCall"}}node_def {name: "dense_1/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "dense/StatefulPartitionedCall:output:0"input: "dense_1_6581"input: "dense_1_6583"attr {key: "Tin"value {list {type: DT_FLOATtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_1_layer_call_and_return_conditional_losses_6570"}}}experimental_debug_info {original_node_names: "dense_1/StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "dense_1/StatefulPartitionedCall:output:0"input: "^dense/StatefulPartitionedCall"input: "^dense_1/StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "dense/StatefulPartitionedCall"value: "dense/StatefulPartitionedCall"}control_ret {key: "dense_1/StatefulPartitionedCall"value: "dense_1/StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "input_1"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_and_return_conditional_losses_6618"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "dense_6607"type: DT_RESOURCE}input_arg {name: "dense_6609"type: DT_RESOURCE}input_arg {name: "dense_1_6612"type: DT_RESOURCE}input_arg {name: "dense_1_6614"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "dense/StatefulPartitionedCall"control_output: "dense_1/StatefulPartitionedCall"}node_def {name: "dense/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "inputs"input: "dense_6607"input: "dense_6609"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_layer_call_and_return_conditional_losses_6544"}}}experimental_debug_info {original_node_names: "dense/StatefulPartitionedCall"}}node_def {name: "dense_1/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "dense/StatefulPartitionedCall:output:0"input: "dense_1_6612"input: "dense_1_6614"attr {key: "Tin"value {list {type: DT_FLOATtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_1_layer_call_and_return_conditional_losses_6570"}}}experimental_debug_info {original_node_names: "dense_1/StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "dense_1/StatefulPartitionedCall:output:0"input: "^dense/StatefulPartitionedCall"input: "^dense_1/StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "dense/StatefulPartitionedCall"value: "dense/StatefulPartitionedCall"}control_ret {key: "dense_1/StatefulPartitionedCall"value: "dense_1/StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_fn_6656"input_arg {name: "input_1"type: DT_INT32}input_arg {name: "unknown"type: DT_RESOURCE}input_arg {name: "unknown_0"type: DT_RESOURCE}input_arg {name: "unknown_1"type: DT_RESOURCE}input_arg {name: "unknown_2"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "StatefulPartitionedCall"}node_def {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "input_1"input: "unknown"input: "unknown_0"input: "unknown_1"input: "unknown_2"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2i: 3i: 4}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_sequential_layer_call_and_return_conditional_losses_6645"}}}experimental_debug_info {original_node_names: "StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "StatefulPartitionedCall:output:0"input: "^StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "StatefulPartitionedCall"value: "StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "input_1"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_dense_1_layer_call_and_return_conditional_losses_6764"input_arg {name: "inputs"type: DT_FLOAT}input_arg {name: "matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "biasadd_readvariableop_resource"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: true}node_def {name: "MatMul/ReadVariableOp"op: "ReadVariableOp"input: "matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "MatMul/ReadVariableOp"}}node_def {name: "MatMul"op: "MatMul"input: "inputs"input: "MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "MatMul"}}node_def {name: "BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "BiasAdd/ReadVariableOp"}}node_def {name: "BiasAdd"op: "BiasAdd"input: "MatMul:product:0"input: "BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "BiasAdd"}}node_def {name: "Identity"op: "Identity"input: "BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 100}}shape {unknown_rank: true}shape {unknown_rank: true}}}}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_dense_layer_call_fn_6754"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "unknown"type: DT_RESOURCE}input_arg {name: "unknown_0"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "StatefulPartitionedCall"}node_def {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "inputs"input: "unknown"input: "unknown_0"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_layer_call_and_return_conditional_losses_6544"}}}experimental_debug_info {original_node_names: "StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "StatefulPartitionedCall:output:0"input: "^StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "StatefulPartitionedCall"value: "StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference__traced_save_6824"input_arg {name: "file_prefix"type: DT_STRING}input_arg {name: "savev2_dense_kernel_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_dense_bias_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_dense_1_kernel_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_dense_1_bias_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_total_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_count_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_total_1_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_count_1_read_readvariableop"type: DT_FLOAT}input_arg {name: "savev2_1_const"type: DT_STRING}output_arg {name: "identity_1"type: DT_STRING}is_stateful: truecontrol_output: "MergeV2Checkpoints"control_output: "SaveV2"control_output: "SaveV2_1"}node_def {name: "StaticRegexFullMatch"op: "StaticRegexFullMatch"input: "file_prefix"device: "/device:CPU:*"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "pattern"value {s: "^s3://.*"}}experimental_debug_info {original_node_names: "StaticRegexFullMatch"}}node_def {name: "Const"op: "Const"device: "/device:CPU:*"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {}string_val: ".part"}}}experimental_debug_info {original_node_names: "Const"}}node_def {name: "Const_1"op: "Const"device: "/device:CPU:*"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {}string_val: "_temp_6f1e5fef49bb4c06ace07a8a95dfbb1b/part"}}}experimental_debug_info {original_node_names: "Const_1"}}node_def {name: "Select"op: "Select"input: "StaticRegexFullMatch:output:0"input: "Const:output:0"input: "Const_1:output:0"device: "/device:CPU:*"attr {key: "T"value {type: DT_STRING}}attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "Select"}}node_def {name: "StringJoin"op: "StringJoin"input: "file_prefix"input: "Select:output:0"device: "/device:CPU:*"attr {key: "N"value {i: 2}}attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "StringJoin"}}node_def {name: "num_shards"op: "Const"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_INT32}}attr {key: "value"value {tensor {dtype: DT_INT32tensor_shape {}int_val: 2}}}experimental_debug_info {original_node_names: "num_shards"}}node_def {name: "ShardedFilename/shard"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_INT32}}attr {key: "value"value {tensor {dtype: DT_INT32tensor_shape {}int_val: 0}}}experimental_debug_info {original_node_names: "ShardedFilename/shard"}}node_def {name: "ShardedFilename"op: "ShardedFilename"input: "StringJoin:output:0"input: "ShardedFilename/shard:output:0"input: "num_shards:output:0"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "ShardedFilename"}}node_def {name: "SaveV2/tensor_names"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 8}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 8}}string_val: "layer_with_weights-0/kernel/.ATTRIBUTES/VARIABLE_VALUE"string_val: "layer_with_weights-0/bias/.ATTRIBUTES/VARIABLE_VALUE"string_val: "layer_with_weights-1/kernel/.ATTRIBUTES/VARIABLE_VALUE"string_val: "layer_with_weights-1/bias/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/0/total/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/0/count/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/1/total/.ATTRIBUTES/VARIABLE_VALUE"string_val: "keras_api/metrics/1/count/.ATTRIBUTES/VARIABLE_VALUE"}}}experimental_debug_info {original_node_names: "SaveV2/tensor_names"}}node_def {name: "SaveV2/shape_and_slices"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 8}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 8}}string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""string_val: ""}}}experimental_debug_info {original_node_names: "SaveV2/shape_and_slices"}}node_def {name: "SaveV2"op: "SaveV2"input: "ShardedFilename:filename:0"input: "SaveV2/tensor_names:output:0"input: "SaveV2/shape_and_slices:output:0"input: "savev2_dense_kernel_read_readvariableop"input: "savev2_dense_bias_read_readvariableop"input: "savev2_dense_1_kernel_read_readvariableop"input: "savev2_dense_1_bias_read_readvariableop"input: "savev2_total_read_readvariableop"input: "savev2_count_read_readvariableop"input: "savev2_total_1_read_readvariableop"input: "savev2_count_1_read_readvariableop"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtypes"value {list {type: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOATtype: DT_FLOAT}}}experimental_debug_info {original_node_names: "SaveV2"}}node_def {name: "ShardedFilename_1/shard"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "dtype"value {type: DT_INT32}}attr {key: "value"value {tensor {dtype: DT_INT32tensor_shape {}int_val: 1}}}experimental_debug_info {original_node_names: "ShardedFilename_1/shard"}}node_def {name: "ShardedFilename_1"op: "ShardedFilename"input: "StringJoin:output:0"input: "ShardedFilename_1/shard:output:0"input: "num_shards:output:0"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "ShardedFilename_1"}}node_def {name: "SaveV2_1/tensor_names"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 1}}string_val: "_CHECKPOINTABLE_OBJECT_GRAPH"}}}experimental_debug_info {original_node_names: "SaveV2_1/tensor_names"}}node_def {name: "SaveV2_1/shape_and_slices"op: "Const"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_STRING}}attr {key: "value"value {tensor {dtype: DT_STRINGtensor_shape {dim {size: 1}}string_val: ""}}}experimental_debug_info {original_node_names: "SaveV2_1/shape_and_slices"}}node_def {name: "SaveV2_1"op: "SaveV2"input: "ShardedFilename_1:filename:0"input: "SaveV2_1/tensor_names:output:0"input: "SaveV2_1/shape_and_slices:output:0"input: "savev2_1_const"input: "^SaveV2"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {}}}attr {key: "dtypes"value {list {type: DT_STRING}}}experimental_debug_info {original_node_names: "SaveV2_1"}}node_def {name: "MergeV2Checkpoints/checkpoint_prefixes"op: "Pack"input: "ShardedFilename:filename:0"input: "ShardedFilename_1:filename:0"input: "^SaveV2"input: "^SaveV2_1"device: "/device:CPU:0"attr {key: "N"value {i: 2}}attr {key: "T"value {type: DT_STRING}}attr {key: "_output_shapes"value {list {shape {dim {size: 2}}}}}experimental_debug_info {original_node_names: "MergeV2Checkpoints/checkpoint_prefixes"}}node_def {name: "MergeV2Checkpoints"op: "MergeV2Checkpoints"input: "MergeV2Checkpoints/checkpoint_prefixes:output:0"input: "file_prefix"input: "^SaveV2_1"device: "/device:CPU:0"attr {key: "_output_shapes"value {list {}}}experimental_debug_info {original_node_names: "MergeV2Checkpoints"}}node_def {name: "Identity"op: "Identity"input: "file_prefix"input: "^MergeV2Checkpoints"device: "/device:CPU:0"attr {key: "T"value {type: DT_STRING}}attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "Identity"}}node_def {name: "Identity_1"op: "Identity"input: "Identity:output:0"input: "^MergeV2Checkpoints"input: "^SaveV2"input: "^SaveV2_1"attr {key: "T"value {type: DT_STRING}}attr {key: "_output_shapes"value {list {shape {}}}}experimental_debug_info {original_node_names: "Identity_1"}}ret {key: "identity_1"value: "Identity_1:output:0"}attr {key: "_input_shapes"value {list {shape {}shape {dim {size: 214}dim {size: 100}}shape {dim {size: 100}}shape {dim {size: 100}dim {size: 1}}shape {dim {size: 1}}shape {}shape {}shape {}shape {}shape {}}}}control_ret {key: "MergeV2Checkpoints"value: "MergeV2Checkpoints"}control_ret {key: "SaveV2"value: "SaveV2"}control_ret {key: "SaveV2_1"value: "SaveV2_1"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {}}}}attr {key: "_user_specified_name"value {s: "file_prefix"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {dim {size: 214}dim {size: 100}}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {dim {size: 100}}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {dim {size: 100}dim {size: 1}}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}}}arg_attr {key: 5value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 6value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 7value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 8value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 9value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_and_return_conditional_losses_6689"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "dense_matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "dense_biasadd_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "dense_1_matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "dense_1_biasadd_readvariableop_resource"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: true}node_def {name: "dense/Cast"op: "Cast"input: "inputs"attr {key: "DstT"value {type: DT_FLOAT}}attr {key: "SrcT"value {type: DT_INT32}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}experimental_debug_info {original_node_names: "dense/Cast"}}node_def {name: "dense/MatMul/ReadVariableOp"op: "ReadVariableOp"input: "dense_matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 214}dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense/MatMul/ReadVariableOp"}}node_def {name: "dense/MatMul"op: "MatMul"input: "dense/Cast:y:0"input: "dense/MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "dense/MatMul"}}node_def {name: "dense/BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "dense_biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense/BiasAdd/ReadVariableOp"}}node_def {name: "dense/BiasAdd"op: "BiasAdd"input: "dense/MatMul:product:0"input: "dense/BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "dense/BiasAdd"}}node_def {name: "dense/Relu"op: "Relu"input: "dense/BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "dense/Relu"}}node_def {name: "dense_1/MatMul/ReadVariableOp"op: "ReadVariableOp"input: "dense_1_matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense_1/MatMul/ReadVariableOp"}}node_def {name: "dense_1/MatMul"op: "MatMul"input: "dense/Relu:activations:0"input: "dense_1/MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "dense_1/MatMul"}}node_def {name: "dense_1/BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "dense_1_biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense_1/BiasAdd/ReadVariableOp"}}node_def {name: "dense_1/BiasAdd"op: "BiasAdd"input: "dense_1/MatMul:product:0"input: "dense_1/BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "dense_1/BiasAdd"}}node_def {name: "Identity"op: "Identity"input: "dense_1/BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_dense_layer_call_and_return_conditional_losses_6745"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "biasadd_readvariableop_resource"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: true}node_def {name: "Cast"op: "Cast"input: "inputs"attr {key: "DstT"value {type: DT_FLOAT}}attr {key: "SrcT"value {type: DT_INT32}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}experimental_debug_info {original_node_names: "Cast"}}node_def {name: "MatMul/ReadVariableOp"op: "ReadVariableOp"input: "matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 214}dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "MatMul/ReadVariableOp"}}node_def {name: "MatMul"op: "MatMul"input: "Cast:y:0"input: "MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "MatMul"}}node_def {name: "BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "BiasAdd/ReadVariableOp"}}node_def {name: "BiasAdd"op: "BiasAdd"input: "MatMul:product:0"input: "BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "BiasAdd"}}node_def {name: "Relu"op: "Relu"input: "BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "Relu"}}node_def {name: "Identity"op: "Identity"input: "Relu:activations:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}}}}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_dense_1_layer_call_fn_6773"input_arg {name: "inputs"type: DT_FLOAT}input_arg {name: "unknown"type: DT_RESOURCE}input_arg {name: "unknown_0"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "StatefulPartitionedCall"}node_def {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "inputs"input: "unknown"input: "unknown_0"attr {key: "Tin"value {list {type: DT_FLOATtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_1_layer_call_and_return_conditional_losses_6570"}}}experimental_debug_info {original_node_names: "StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "StatefulPartitionedCall:output:0"input: "^StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 100}}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "StatefulPartitionedCall"value: "StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference__wrapped_model_6528"input_arg {name: "input_1"type: DT_INT32}input_arg {name: "sequential_dense_matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "sequential_dense_biasadd_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "sequential_dense_1_matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "sequential_dense_1_biasadd_readvariableop_resource"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: true}node_def {name: "sequential/dense/Cast"op: "Cast"input: "input_1"attr {key: "DstT"value {type: DT_FLOAT}}attr {key: "SrcT"value {type: DT_INT32}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}experimental_debug_info {original_node_names: "sequential/dense/Cast"}}node_def {name: "sequential/dense/MatMul/ReadVariableOp"op: "ReadVariableOp"input: "sequential_dense_matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 214}dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "sequential/dense/MatMul/ReadVariableOp"}}node_def {name: "sequential/dense/MatMul"op: "MatMul"input: "sequential/dense/Cast:y:0"input: "sequential/dense/MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "sequential/dense/MatMul"}}node_def {name: "sequential/dense/BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "sequential_dense_biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "sequential/dense/BiasAdd/ReadVariableOp"}}node_def {name: "sequential/dense/BiasAdd"op: "BiasAdd"input: "sequential/dense/MatMul:product:0"input: "sequential/dense/BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "sequential/dense/BiasAdd"}}node_def {name: "sequential/dense/Relu"op: "Relu"input: "sequential/dense/BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "sequential/dense/Relu"}}node_def {name: "sequential/dense_1/MatMul/ReadVariableOp"op: "ReadVariableOp"input: "sequential_dense_1_matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "sequential/dense_1/MatMul/ReadVariableOp"}}node_def {name: "sequential/dense_1/MatMul"op: "MatMul"input: "sequential/dense/Relu:activations:0"input: "sequential/dense_1/MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "sequential/dense_1/MatMul"}}node_def {name: "sequential/dense_1/BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "sequential_dense_1_biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "sequential/dense_1/BiasAdd/ReadVariableOp"}}node_def {name: "sequential/dense_1/BiasAdd"op: "BiasAdd"input: "sequential/dense_1/MatMul:product:0"input: "sequential/dense_1/BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "sequential/dense_1/BiasAdd"}}node_def {name: "Identity"op: "Identity"input: "sequential/dense_1/BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "input_1"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_dense_layer_call_and_return_conditional_losses_6544"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "biasadd_readvariableop_resource"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: true}node_def {name: "Cast"op: "Cast"input: "inputs"attr {key: "DstT"value {type: DT_FLOAT}}attr {key: "SrcT"value {type: DT_INT32}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}experimental_debug_info {original_node_names: "Cast"}}node_def {name: "MatMul/ReadVariableOp"op: "ReadVariableOp"input: "matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 214}dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "MatMul/ReadVariableOp"}}node_def {name: "MatMul"op: "MatMul"input: "Cast:y:0"input: "MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "MatMul"}}node_def {name: "BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "BiasAdd/ReadVariableOp"}}node_def {name: "BiasAdd"op: "BiasAdd"input: "MatMul:product:0"input: "BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "BiasAdd"}}node_def {name: "Relu"op: "Relu"input: "BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "Relu"}}node_def {name: "Identity"op: "Identity"input: "Relu:activations:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}}}}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_and_return_conditional_losses_6601"input_arg {name: "input_1"type: DT_INT32}input_arg {name: "dense_6590"type: DT_RESOURCE}input_arg {name: "dense_6592"type: DT_RESOURCE}input_arg {name: "dense_1_6595"type: DT_RESOURCE}input_arg {name: "dense_1_6597"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "dense/StatefulPartitionedCall"control_output: "dense_1/StatefulPartitionedCall"}node_def {name: "dense/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "input_1"input: "dense_6590"input: "dense_6592"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_layer_call_and_return_conditional_losses_6544"}}}experimental_debug_info {original_node_names: "dense/StatefulPartitionedCall"}}node_def {name: "dense_1/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "dense/StatefulPartitionedCall:output:0"input: "dense_1_6595"input: "dense_1_6597"attr {key: "Tin"value {list {type: DT_FLOATtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_1_layer_call_and_return_conditional_losses_6570"}}}experimental_debug_info {original_node_names: "dense_1/StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "dense_1/StatefulPartitionedCall:output:0"input: "^dense/StatefulPartitionedCall"input: "^dense_1/StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "dense/StatefulPartitionedCall"value: "dense/StatefulPartitionedCall"}control_ret {key: "dense_1/StatefulPartitionedCall"value: "dense_1/StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "input_1"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_fn_6733"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "unknown"type: DT_RESOURCE}input_arg {name: "unknown_0"type: DT_RESOURCE}input_arg {name: "unknown_1"type: DT_RESOURCE}input_arg {name: "unknown_2"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "StatefulPartitionedCall"}node_def {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "inputs"input: "unknown"input: "unknown_0"input: "unknown_1"input: "unknown_2"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2i: 3i: 4}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_sequential_layer_call_and_return_conditional_losses_6645"}}}experimental_debug_info {original_node_names: "StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "StatefulPartitionedCall:output:0"input: "^StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "StatefulPartitionedCall"value: "StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_and_return_conditional_losses_6645"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "dense_6634"type: DT_RESOURCE}input_arg {name: "dense_6636"type: DT_RESOURCE}input_arg {name: "dense_1_6639"type: DT_RESOURCE}input_arg {name: "dense_1_6641"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "dense/StatefulPartitionedCall"control_output: "dense_1/StatefulPartitionedCall"}node_def {name: "dense/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "inputs"input: "dense_6634"input: "dense_6636"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_layer_call_and_return_conditional_losses_6544"}}}experimental_debug_info {original_node_names: "dense/StatefulPartitionedCall"}}node_def {name: "dense_1/StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "dense/StatefulPartitionedCall:output:0"input: "dense_1_6639"input: "dense_1_6641"attr {key: "Tin"value {list {type: DT_FLOATtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_dense_1_layer_call_and_return_conditional_losses_6570"}}}experimental_debug_info {original_node_names: "dense_1/StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "dense_1/StatefulPartitionedCall:output:0"input: "^dense/StatefulPartitionedCall"input: "^dense_1/StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "dense/StatefulPartitionedCall"value: "dense/StatefulPartitionedCall"}control_ret {key: "dense_1/StatefulPartitionedCall"value: "dense_1/StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_dense_1_layer_call_and_return_conditional_losses_6570"input_arg {name: "inputs"type: DT_FLOAT}input_arg {name: "matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "biasadd_readvariableop_resource"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: true}node_def {name: "MatMul/ReadVariableOp"op: "ReadVariableOp"input: "matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "MatMul/ReadVariableOp"}}node_def {name: "MatMul"op: "MatMul"input: "inputs"input: "MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "MatMul"}}node_def {name: "BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "BiasAdd/ReadVariableOp"}}node_def {name: "BiasAdd"op: "BiasAdd"input: "MatMul:product:0"input: "BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "BiasAdd"}}node_def {name: "Identity"op: "Identity"input: "BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 100}}shape {unknown_rank: true}shape {unknown_rank: true}}}}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_signature_wrapper_6671"input_arg {name: "input_1"type: DT_INT32}input_arg {name: "unknown"type: DT_RESOURCE}input_arg {name: "unknown_0"type: DT_RESOURCE}input_arg {name: "unknown_1"type: DT_RESOURCE}input_arg {name: "unknown_2"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "StatefulPartitionedCall"}node_def {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "input_1"input: "unknown"input: "unknown_0"input: "unknown_1"input: "unknown_2"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2i: 3i: 4}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference__wrapped_model_6528"}}}experimental_debug_info {original_node_names: "StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "StatefulPartitionedCall:output:0"input: "^StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "StatefulPartitionedCall"value: "StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "input_1"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_fn_6720"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "unknown"type: DT_RESOURCE}input_arg {name: "unknown_0"type: DT_RESOURCE}input_arg {name: "unknown_1"type: DT_RESOURCE}input_arg {name: "unknown_2"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: truecontrol_output: "StatefulPartitionedCall"}node_def {name: "StatefulPartitionedCall"op: "StatefulPartitionedCall"input: "inputs"input: "unknown"input: "unknown_0"input: "unknown_1"input: "unknown_2"attr {key: "Tin"value {list {type: DT_INT32type: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCEtype: DT_RESOURCE}}}attr {key: "Tout"value {list {type: DT_FLOAT}}}attr {key: "_collective_manager_ids"value {list {}}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}attr {key: "_read_only_resource_inputs"value {list {i: 1i: 2i: 3i: 4}}}attr {key: "config_proto"value {s: "\n\007\n\003CPU\020\001\n\007\n\003GPU\020\0002\002J\0008\001"}}attr {key: "f"value {func {name: "__inference_sequential_layer_call_and_return_conditional_losses_6618"}}}experimental_debug_info {original_node_names: "StatefulPartitionedCall"}}node_def {name: "Identity"op: "Identity"input: "StatefulPartitionedCall:output:0"input: "^StatefulPartitionedCall"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}control_ret {key: "StatefulPartitionedCall"value: "StatefulPartitionedCall"}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}function {signature {name: "__inference_sequential_layer_call_and_return_conditional_losses_6707"input_arg {name: "inputs"type: DT_INT32}input_arg {name: "dense_matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "dense_biasadd_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "dense_1_matmul_readvariableop_resource"type: DT_RESOURCE}input_arg {name: "dense_1_biasadd_readvariableop_resource"type: DT_RESOURCE}output_arg {name: "identity"type: DT_FLOAT}is_stateful: true}node_def {name: "dense/Cast"op: "Cast"input: "inputs"attr {key: "DstT"value {type: DT_FLOAT}}attr {key: "SrcT"value {type: DT_INT32}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}experimental_debug_info {original_node_names: "dense/Cast"}}node_def {name: "dense/MatMul/ReadVariableOp"op: "ReadVariableOp"input: "dense_matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 214}dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense/MatMul/ReadVariableOp"}}node_def {name: "dense/MatMul"op: "MatMul"input: "dense/Cast:y:0"input: "dense/MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "dense/MatMul"}}node_def {name: "dense/BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "dense_biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense/BiasAdd/ReadVariableOp"}}node_def {name: "dense/BiasAdd"op: "BiasAdd"input: "dense/MatMul:product:0"input: "dense/BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "dense/BiasAdd"}}node_def {name: "dense/Relu"op: "Relu"input: "dense/BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 100}}}}}experimental_debug_info {original_node_names: "dense/Relu"}}node_def {name: "dense_1/MatMul/ReadVariableOp"op: "ReadVariableOp"input: "dense_1_matmul_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 100}dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense_1/MatMul/ReadVariableOp"}}node_def {name: "dense_1/MatMul"op: "MatMul"input: "dense/Relu:activations:0"input: "dense_1/MatMul/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "dense_1/MatMul"}}node_def {name: "dense_1/BiasAdd/ReadVariableOp"op: "ReadVariableOp"input: "dense_1_biasadd_readvariableop_resource"attr {key: "_output_shapes"value {list {shape {dim {size: 1}}}}}attr {key: "dtype"value {type: DT_FLOAT}}experimental_debug_info {original_node_names: "dense_1/BiasAdd/ReadVariableOp"}}node_def {name: "dense_1/BiasAdd"op: "BiasAdd"input: "dense_1/MatMul:product:0"input: "dense_1/BiasAdd/ReadVariableOp:value:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "dense_1/BiasAdd"}}node_def {name: "Identity"op: "Identity"input: "dense_1/BiasAdd:output:0"attr {key: "T"value {type: DT_FLOAT}}attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 1}}}}}experimental_debug_info {original_node_names: "Identity"}}ret {key: "identity"value: "Identity:output:0"}attr {key: "_input_shapes"value {list {shape {dim {size: -1}dim {size: 214}}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}shape {unknown_rank: true}}}}arg_attr {key: 0value {attr {key: "_output_shapes"value {list {shape {dim {size: -1}dim {size: 214}}}}}attr {key: "_user_specified_name"value {s: "inputs"}}}}arg_attr {key: 1value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 2value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 3value {attr {key: "_output_shapes"value {list {shape {}}}}}}arg_attr {key: 4value {attr {key: "_output_shapes"value {list {shape {}}}}}}}}versions {producer: 331min_consumer: 12}}saver_def {filename_tensor_name: "saver_filename:0"save_tensor_name: "StatefulPartitionedCall_1:0"restore_op_name: "StatefulPartitionedCall_2"version: V2}collection_def {key: "saved_model_main_op"value {node_list {value: "NoOp"}}}signature_def {key: "__saved_model_init_op"value {outputs {key: "__saved_model_init_op"value {name: "NoOp"tensor_shape {unknown_rank: true}}}}}signature_def {key: "serving_default"value {inputs {key: "input_1"value {name: "serving_default_input_1:0"dtype: DT_INT32tensor_shape {dim {size: -1}dim {size: 214}}}}outputs {key: "output_1"value {name: "StatefulPartitionedCall:0"dtype: DT_FLOATtensor_shape {dim {size: -1}dim {size: 1}}}}method_name: "tensorflow/serving/predict"}}object_graph_def {nodes {children {node_id: 1local_name: "layer_with_weights-0"}children {node_id: 1local_name: "layer-0"}children {node_id: 2local_name: "layer_with_weights-1"}children {node_id: 2local_name: "layer-1"}children {node_id: 3local_name: "optimizer"}children {node_id: 4local_name: "regularization_losses"}children {node_id: 5local_name: "variables"}children {node_id: 6local_name: "trainable_variables"}children {node_id: 7local_name: "keras_api"}children {node_id: 8local_name: "signatures"}children {node_id: 47local_name: "__call__"}children {node_id: 48local_name: "_default_save_signature"}children {node_id: 49local_name: "call_and_return_all_conditional_losses"}user_object {identifier: "_tf_keras_sequential"version {producer: 1min_consumer: 1}metadata: "{\"class_name\": \"Sequential\", \"name\": \"sequential\", \"trainable\": true, \"expects_training_arg\": true, \"dtype\": \"float32\", \"batch_input_shape\": null, \"config\": {\"name\": \"sequential\", \"layers\": [{\"class_name\": \"Dense\", \"config\": {\"name\": \"dense\", \"trainable\": true, \"dtype\": \"float32\", \"units\": 100, \"activation\": \"relu\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}}, {\"class_name\": \"Dense\", \"config\": {\"name\": \"dense_1\", \"trainable\": true, \"dtype\": \"float32\", \"units\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}}], \"build_input_shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 214]}}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 2, \"axes\": {\"-1\": 214}}}, \"build_input_shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 214]}, \"is_graph_network\": false, \"keras_version\": \"2.2.4-tf\", \"backend\": \"tensorflow\", \"model_config\": {\"class_name\": \"Sequential\", \"config\": {\"name\": \"sequential\", \"layers\": [{\"class_name\": \"Dense\", \"config\": {\"name\": \"dense\", \"trainable\": true, \"dtype\": \"float32\", \"units\": 100, \"activation\": \"relu\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}}, {\"class_name\": \"Dense\", \"config\": {\"name\": \"dense_1\", \"trainable\": true, \"dtype\": \"float32\", \"units\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}}], \"build_input_shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 214]}}}, \"training_config\": {\"loss\": \"mean_absolute_error\", \"metrics\": [\"mean_squared_error\"], \"weighted_metrics\": null, \"loss_weights\": null, \"sample_weight_mode\": null, \"optimizer_config\": {\"class_name\": \"Adam\", \"config\": {\"name\": \"Adam\", \"learning_rate\": 0.0003000000142492354, \"decay\": 0.0, \"beta_1\": 0.8999999761581421, \"beta_2\": 0.9990000128746033, \"epsilon\": 1e-07, \"amsgrad\": false}}}}"}}nodes {children {node_id: 9local_name: "kernel"}children {node_id: 10local_name: "bias"}children {node_id: 11local_name: "regularization_losses"}children {node_id: 12local_name: "variables"}children {node_id: 13local_name: "trainable_variables"}children {node_id: 14local_name: "keras_api"}children {node_id: 50local_name: "__call__"}children {node_id: 51local_name: "call_and_return_all_conditional_losses"}user_object {identifier: "_tf_keras_layer"version {producer: 1min_consumer: 1}metadata: "{\"class_name\": \"Dense\", \"name\": \"dense\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"config\": {\"name\": \"dense\", \"trainable\": true, \"dtype\": \"float32\", \"units\": 100, \"activation\": \"relu\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 2, \"axes\": {\"-1\": 214}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 214]}}"}}nodes {children {node_id: 15local_name: "kernel"}children {node_id: 16local_name: "bias"}children {node_id: 17local_name: "regularization_losses"}children {node_id: 18local_name: "variables"}children {node_id: 19local_name: "trainable_variables"}children {node_id: 20local_name: "keras_api"}children {node_id: 52local_name: "__call__"}children {node_id: 53local_name: "call_and_return_all_conditional_losses"}user_object {identifier: "_tf_keras_layer"version {producer: 1min_consumer: 1}metadata: "{\"class_name\": \"Dense\", \"name\": \"dense_1\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"config\": {\"name\": \"dense_1\", \"trainable\": true, \"dtype\": \"float32\", \"units\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 2, \"axes\": {\"-1\": 100}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 100]}}"}}nodes {user_object {identifier: "optimizer"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 9local_name: "0"}children {node_id: 10local_name: "1"}children {node_id: 15local_name: "2"}children {node_id: 16local_name: "3"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 9local_name: "0"}children {node_id: 10local_name: "1"}children {node_id: 15local_name: "2"}children {node_id: 16local_name: "3"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 21local_name: "layers"}children {node_id: 22local_name: "layer_regularization_losses"}children {node_id: 23local_name: "non_trainable_variables"}children {node_id: 24local_name: "layer_metrics"}children {node_id: 4local_name: "regularization_losses"}children {node_id: 25local_name: "metrics"}children {node_id: 5local_name: "variables"}children {node_id: 6local_name: "trainable_variables"}children {node_id: 47local_name: "__call__"}children {node_id: 48local_name: "_default_save_signature"}children {node_id: 49local_name: "call_and_return_all_conditional_losses"}children {node_id: 49local_name: "call_and_return_conditional_losses"}user_object {identifier: "_generic_user_object"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 54local_name: "serving_default"}user_object {identifier: "signature_map"version {producer: 1min_consumer: 1}}}nodes {variable {dtype: DT_FLOATshape {dim {size: 214}dim {size: 100}}trainable: truename: "dense/kernel"}}nodes {variable {dtype: DT_FLOATshape {dim {size: 100}}trainable: truename: "dense/bias"}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 9local_name: "0"}children {node_id: 10local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 9local_name: "0"}children {node_id: 10local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 26local_name: "layers"}children {node_id: 27local_name: "layer_regularization_losses"}children {node_id: 28local_name: "non_trainable_variables"}children {node_id: 29local_name: "layer_metrics"}children {node_id: 11local_name: "regularization_losses"}children {node_id: 30local_name: "metrics"}children {node_id: 12local_name: "variables"}children {node_id: 13local_name: "trainable_variables"}children {node_id: 50local_name: "__call__"}children {node_id: 51local_name: "call_and_return_all_conditional_losses"}children {node_id: 51local_name: "call_and_return_conditional_losses"}user_object {identifier: "_generic_user_object"version {producer: 1min_consumer: 1}}}nodes {variable {dtype: DT_FLOATshape {dim {size: 100}dim {size: 1}}trainable: truename: "dense_1/kernel"}}nodes {variable {dtype: DT_FLOATshape {dim {size: 1}}trainable: truename: "dense_1/bias"}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 15local_name: "0"}children {node_id: 16local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 15local_name: "0"}children {node_id: 16local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 31local_name: "layers"}children {node_id: 32local_name: "layer_regularization_losses"}children {node_id: 33local_name: "non_trainable_variables"}children {node_id: 34local_name: "layer_metrics"}children {node_id: 17local_name: "regularization_losses"}children {node_id: 35local_name: "metrics"}children {node_id: 18local_name: "variables"}children {node_id: 19local_name: "trainable_variables"}children {node_id: 52local_name: "__call__"}children {node_id: 53local_name: "call_and_return_all_conditional_losses"}children {node_id: 53local_name: "call_and_return_conditional_losses"}user_object {identifier: "_generic_user_object"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 1local_name: "0"}children {node_id: 2local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_dict_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 36local_name: "0"}children {node_id: 37local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_dict_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_dict_wrapper"version {producer: 1min_consumer: 1}}}nodes {user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 38local_name: "total"}children {node_id: 39local_name: "count"}children {node_id: 40local_name: "variables"}children {node_id: 41local_name: "keras_api"}user_object {identifier: "_tf_keras_metric"version {producer: 1min_consumer: 1}metadata: "{\"class_name\": \"Mean\", \"name\": \"loss\", \"dtype\": \"float32\", \"config\": {\"name\": \"loss\", \"dtype\": \"float32\"}}"}}nodes {children {node_id: 42local_name: "total"}children {node_id: 43local_name: "count"}children {node_id: 44local_name: "_fn_kwargs"}children {node_id: 45local_name: "variables"}children {node_id: 46local_name: "keras_api"}user_object {identifier: "_tf_keras_metric"version {producer: 1min_consumer: 1}metadata: "{\"class_name\": \"MeanMetricWrapper\", \"name\": \"mean_squared_error\", \"dtype\": \"float32\", \"config\": {\"name\": \"mean_squared_error\", \"dtype\": \"float32\", \"fn\": \"mean_squared_error\"}}"}}nodes {variable {dtype: DT_FLOATshape {}synchronization: VARIABLE_SYNCHRONIZATION_ON_READaggregation: VARIABLE_AGGREGATION_SUMname: "total"}}nodes {variable {dtype: DT_FLOATshape {}synchronization: VARIABLE_SYNCHRONIZATION_ON_READaggregation: VARIABLE_AGGREGATION_SUMname: "count"}}nodes {children {node_id: 38local_name: "0"}children {node_id: 39local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 40local_name: "variables"}user_object {identifier: "_generic_user_object"version {producer: 1min_consumer: 1}}}nodes {variable {dtype: DT_FLOATshape {}synchronization: VARIABLE_SYNCHRONIZATION_ON_READaggregation: VARIABLE_AGGREGATION_SUMname: "total"}}nodes {variable {dtype: DT_FLOATshape {}synchronization: VARIABLE_SYNCHRONIZATION_ON_READaggregation: VARIABLE_AGGREGATION_SUMname: "count"}}nodes {user_object {identifier: "trackable_dict_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 42local_name: "0"}children {node_id: 43local_name: "1"}user_object {identifier: "trackable_list_wrapper"version {producer: 1min_consumer: 1}}}nodes {children {node_id: 45local_name: "variables"}user_object {identifier: "_generic_user_object"version {producer: 1min_consumer: 1}}}nodes {function {concrete_functions: "__inference_sequential_layer_call_fn_6629"concrete_functions: "__inference_sequential_layer_call_fn_6733"concrete_functions: "__inference_sequential_layer_call_fn_6720"concrete_functions: "__inference_sequential_layer_call_fn_6656"function_spec {fullargspec {named_tuple_value {name: "FullArgSpec"values {key: "args"value {list_value {values {string_value: "self"}values {string_value: "inputs"}values {string_value: "training"}values {string_value: "mask"}}}}values {key: "varargs"value {none_value {}}}values {key: "varkw"value {none_value {}}}values {key: "defaults"value {list_value {values {bool_value: false}values {none_value {}}}}}values {key: "kwonlyargs"value {list_value {}}}values {key: "kwonlydefaults"value {dict_value {}}}values {key: "annotations"value {dict_value {}}}}}is_method: trueinput_signature {none_value {}}}}}nodes {function {concrete_functions: "__inference__wrapped_model_6528"function_spec {fullargspec {named_tuple_value {name: "FullArgSpec"values {key: "args"value {list_value {}}}values {key: "varargs"value {string_value: "args"}}values {key: "varkw"value {none_value {}}}values {key: "defaults"value {none_value {}}}values {key: "kwonlyargs"value {list_value {}}}values {key: "kwonlydefaults"value {none_value {}}}values {key: "annotations"value {dict_value {}}}}}input_signature {tuple_value {values {tensor_spec_value {name: "input_1"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}}}}}}nodes {function {concrete_functions: "__inference_sequential_layer_call_and_return_conditional_losses_6689"concrete_functions: "__inference_sequential_layer_call_and_return_conditional_losses_6587"concrete_functions: "__inference_sequential_layer_call_and_return_conditional_losses_6707"concrete_functions: "__inference_sequential_layer_call_and_return_conditional_losses_6601"function_spec {fullargspec {named_tuple_value {name: "FullArgSpec"values {key: "args"value {list_value {values {string_value: "self"}values {string_value: "inputs"}values {string_value: "training"}values {string_value: "mask"}}}}values {key: "varargs"value {none_value {}}}values {key: "varkw"value {none_value {}}}values {key: "defaults"value {list_value {values {bool_value: false}values {none_value {}}}}}values {key: "kwonlyargs"value {list_value {}}}values {key: "kwonlydefaults"value {dict_value {}}}values {key: "annotations"value {dict_value {}}}}}is_method: trueinput_signature {none_value {}}}}}nodes {function {concrete_functions: "__inference_dense_layer_call_fn_6754"function_spec {fullargspec {named_tuple_value {name: "FullArgSpec"values {key: "args"value {list_value {values {string_value: "self"}values {string_value: "inputs"}}}}values {key: "varargs"value {none_value {}}}values {key: "varkw"value {none_value {}}}values {key: "defaults"value {none_value {}}}values {key: "kwonlyargs"value {list_value {}}}values {key: "kwonlydefaults"value {none_value {}}}values {key: "annotations"value {dict_value {}}}}}is_method: trueinput_signature {none_value {}}}}}nodes {function {concrete_functions: "__inference_dense_layer_call_and_return_conditional_losses_6745"function_spec {fullargspec {named_tuple_value {name: "FullArgSpec"values {key: "args"value {list_value {values {string_value: "self"}values {string_value: "inputs"}}}}values {key: "varargs"value {none_value {}}}values {key: "varkw"value {none_value {}}}values {key: "defaults"value {none_value {}}}values {key: "kwonlyargs"value {list_value {}}}values {key: "kwonlydefaults"value {none_value {}}}values {key: "annotations"value {dict_value {}}}}}is_method: trueinput_signature {none_value {}}}}}nodes {function {concrete_functions: "__inference_dense_1_layer_call_fn_6773"function_spec {fullargspec {named_tuple_value {name: "FullArgSpec"values {key: "args"value {list_value {values {string_value: "self"}values {string_value: "inputs"}}}}values {key: "varargs"value {none_value {}}}values {key: "varkw"value {none_value {}}}values {key: "defaults"value {none_value {}}}values {key: "kwonlyargs"value {list_value {}}}values {key: "kwonlydefaults"value {none_value {}}}values {key: "annotations"value {dict_value {}}}}}is_method: trueinput_signature {none_value {}}}}}nodes {function {concrete_functions: "__inference_dense_1_layer_call_and_return_conditional_losses_6764"function_spec {fullargspec {named_tuple_value {name: "FullArgSpec"values {key: "args"value {list_value {values {string_value: "self"}values {string_value: "inputs"}}}}values {key: "varargs"value {none_value {}}}values {key: "varkw"value {none_value {}}}values {key: "defaults"value {none_value {}}}values {key: "kwonlyargs"value {list_value {}}}values {key: "kwonlydefaults"value {none_value {}}}values {key: "annotations"value {dict_value {}}}}}is_method: trueinput_signature {none_value {}}}}}nodes {bare_concrete_function {concrete_function_name: "__inference_signature_wrapper_6671"argument_keywords: "input_1"allowed_positional_arguments: 1}}concrete_functions {key: "__inference__wrapped_model_6528"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "input_1"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}}}values {dict_value {}}}}output_signature {dict_value {fields {key: "output_1"value {tensor_spec_value {name: "output_1"shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}}}}}}concrete_functions {key: "__inference_dense_1_layer_call_and_return_conditional_losses_6764"value {bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 100}}dtype: DT_FLOAT}}}}values {dict_value {}}}}output_signature {tuple_value {values {tensor_spec_value {name: "0"shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}values {list_value {}}}}}}concrete_functions {key: "__inference_dense_1_layer_call_fn_6773"value {bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 100}}dtype: DT_FLOAT}}}}values {dict_value {}}}}output_signature {tensor_spec_value {shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}}}concrete_functions {key: "__inference_dense_layer_call_and_return_conditional_losses_6745"value {bound_inputs: 9bound_inputs: 10canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}}}values {dict_value {}}}}output_signature {tuple_value {values {tensor_spec_value {name: "0"shape {dim {size: -1}dim {size: 100}}dtype: DT_FLOAT}}values {list_value {}}}}}}concrete_functions {key: "__inference_dense_layer_call_fn_6754"value {bound_inputs: 9bound_inputs: 10canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}}}values {dict_value {}}}}output_signature {tensor_spec_value {shape {dim {size: -1}dim {size: 100}}dtype: DT_FLOAT}}}}concrete_functions {key: "__inference_sequential_layer_call_and_return_conditional_losses_6587"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "input_1"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: true}values {none_value {}}}}values {dict_value {}}}}output_signature {tuple_value {values {tensor_spec_value {name: "0"shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}values {list_value {}}}}}}concrete_functions {key: "__inference_sequential_layer_call_and_return_conditional_losses_6601"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "input_1"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: false}values {none_value {}}}}values {dict_value {}}}}output_signature {tuple_value {values {tensor_spec_value {name: "0"shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}values {list_value {}}}}}}concrete_functions {key: "__inference_sequential_layer_call_and_return_conditional_losses_6689"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: true}values {none_value {}}}}values {dict_value {}}}}output_signature {tuple_value {values {tensor_spec_value {name: "0"shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}values {list_value {}}}}}}concrete_functions {key: "__inference_sequential_layer_call_and_return_conditional_losses_6707"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: false}values {none_value {}}}}values {dict_value {}}}}output_signature {tuple_value {values {tensor_spec_value {name: "0"shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}values {list_value {}}}}}}concrete_functions {key: "__inference_sequential_layer_call_fn_6629"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "input_1"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: true}values {none_value {}}}}values {dict_value {}}}}output_signature {tensor_spec_value {shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}}}concrete_functions {key: "__inference_sequential_layer_call_fn_6656"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "input_1"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: false}values {none_value {}}}}values {dict_value {}}}}output_signature {tensor_spec_value {shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}}}concrete_functions {key: "__inference_sequential_layer_call_fn_6720"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: true}values {none_value {}}}}values {dict_value {}}}}output_signature {tensor_spec_value {shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}}}concrete_functions {key: "__inference_sequential_layer_call_fn_6733"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {values {tensor_spec_value {name: "inputs"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}values {bool_value: false}values {none_value {}}}}values {dict_value {}}}}output_signature {tensor_spec_value {shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}}}concrete_functions {key: "__inference_signature_wrapper_6671"value {bound_inputs: 9bound_inputs: 10bound_inputs: 15bound_inputs: 16canonicalized_input_signature {tuple_value {values {tuple_value {}}values {dict_value {fields {key: "input_1"value {tensor_spec_value {name: "input_1"shape {dim {size: -1}dim {size: 214}}dtype: DT_INT32}}}}}}}output_signature {dict_value {fields {key: "output_1"value {tensor_spec_value {name: "output_1"shape {dim {size: -1}dim {size: 1}}dtype: DT_FLOAT}}}}}}}}}
//===- InlineCostTest.cpp - test for InlineCost ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/InlineCost.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/TargetTransformInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"namespace {// Tests that we can retrieve the CostFeatures without an errorTEST(InlineCostTest, CostFeatures) {using namespace llvm;const auto *const IR = R"IR(define i32 @f(i32) {ret i32 4}define i32 @g(i32) {%2 = call i32 @f(i32 0)ret i32 %2})IR";LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(IR, Err, C);ASSERT_TRUE(M);auto *G = M->getFunction("g");ASSERT_TRUE(G);// find the call to f in gCallBase *CB = nullptr;for (auto &BB : *G) {for (auto &I : BB) {if ((CB = dyn_cast<CallBase>(&I)))break;}}ASSERT_TRUE(CB);ModuleAnalysisManager MAM;FunctionAnalysisManager FAM;FAM.registerPass([&] { return TargetIRAnalysis(); });FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });FAM.registerPass([&] { return AssumptionAnalysis(); });MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });MAM.registerPass([&] { return PassInstrumentationAnalysis(); });FAM.registerPass([&] { return PassInstrumentationAnalysis(); });ModulePassManager MPM;MPM.run(*M, MAM);auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {return FAM.getResult<AssumptionAnalysis>(F);};auto &TIR = FAM.getResult<TargetIRAnalysis>(*G);const auto Features =llvm::getInliningCostFeatures(*CB, TIR, GetAssumptionCache);// Check that the optional is not emptyASSERT_TRUE(Features);}} // namespace
//===- IVDescriptorsTest.cpp - IVDescriptors unit tests -------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/IVDescriptors.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;/// Build the loop info and scalar evolution for the function and run the Test.static void runWithLoopInfoAndSE(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI, ScalarEvolution &SE)> Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AssumptionCache AC(*F);DominatorTree DT(*F);LoopInfo LI(DT);ScalarEvolution SE(*F, TLI, AC, DT, LI);Test(*F, LI, SE);}static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("IVDescriptorsTests", errs());return Mod;}// This tests that IVDescriptors can obtain the induction binary operator for// integer induction variables. And getExactFPMathInst() correctly return the// expected behavior, i.e. no FMF algebra.TEST(IVDescriptorsTest, LoopWithSingleLatch) {// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = parseIR(Context,R"(define void @foo(i32* %A, i32 %ub) {entry:br label %for.bodyfor.body:%i = phi i32 [ 0, %entry ], [ %inc, %for.body ]%idxprom = sext i32 %i to i64%arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxpromstore i32 %i, i32* %arrayidx, align 4%inc = add nsw i32 %i, 1%cmp = icmp slt i32 %inc, %ubbr i1 %cmp, label %for.body, label %for.exitfor.exit:br label %for.endfor.end:ret void})");runWithLoopInfoAndSE(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);PHINode *Inst_i = dyn_cast<PHINode>(&Header->front());assert(Inst_i->getName() == "i");InductionDescriptor IndDesc;bool IsInductionPHI =InductionDescriptor::isInductionPHI(Inst_i, L, &SE, IndDesc);EXPECT_TRUE(IsInductionPHI);Instruction *Inst_inc = nullptr;BasicBlock::iterator BBI = Header->begin();do {if ((&*BBI)->getName() == "inc")Inst_inc = &*BBI;++BBI;} while (!Inst_inc);assert(Inst_inc->getName() == "inc");EXPECT_EQ(IndDesc.getInductionBinOp(), Inst_inc);EXPECT_EQ(IndDesc.getExactFPMathInst(), nullptr);});}TEST(IVDescriptorsTest, LoopWithScalableTypes) {// Parse the module.LLVMContext Context;std::unique_ptr<Module> M =parseIR(Context,R"(define void @foo(<vscale x 4 x float>* %ptr) {entry:br label %for.bodyfor.body:%lsr.iv1 = phi <vscale x 4 x float>* [ %0, %for.body ], [ %ptr, %entry ]%j.0117 = phi i64 [ %inc, %for.body ], [ 0, %entry ]%lsr.iv12 = bitcast <vscale x 4 x float>* %lsr.iv1 to i8*%inc = add nuw nsw i64 %j.0117, 1%uglygep = getelementptr i8, i8* %lsr.iv12, i64 4%0 = bitcast i8* %uglygep to <vscale x 4 x float>*%cmp = icmp ne i64 %inc, 1024br i1 %cmp, label %for.body, label %endend:ret void})");runWithLoopInfoAndSE(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++FI);assert(Header->getName() == "for.body");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);PHINode *Inst_iv = dyn_cast<PHINode>(&Header->front());assert(Inst_iv->getName() == "lsr.iv1");InductionDescriptor IndDesc;bool IsInductionPHI =InductionDescriptor::isInductionPHI(Inst_iv, L, &SE, IndDesc);EXPECT_FALSE(IsInductionPHI);});}// Depending on how SCEV deals with ptrtoint cast, the step of a phi could be// a pointer, and InductionDescriptor used to fail with an assertion.// So just check that it doesn't assert.TEST(IVDescriptorsTest, LoopWithPtrToInt) {// Parse the module.LLVMContext Context;std::unique_ptr<Module> M = parseIR(Context, R"(target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"target triple = "thumbv6m-arm-none-eabi"declare void @widget()declare void @wobble(i32)define void @barney(i8* %arg, i8* %arg18, i32 %arg19) {bb:%tmp = ptrtoint i8* %arg to i32%tmp20 = ptrtoint i8* %arg18 to i32%tmp21 = or i32 %tmp20, %tmp%tmp22 = and i32 %tmp21, 3%tmp23 = icmp eq i32 %tmp22, 0br i1 %tmp23, label %bb24, label %bb25bb24:tail call void @widget()br label %bb34bb25:%tmp26 = sub i32 %tmp, %tmp20%tmp27 = icmp ult i32 %tmp26, %arg19br i1 %tmp27, label %bb28, label %bb34bb28:br label %bb29bb29:%tmp30 = phi i32 [ %tmp31, %bb29 ], [ %arg19, %bb28 ]tail call void @wobble(i32 %tmp26)%tmp31 = sub i32 %tmp30, %tmp26%tmp32 = icmp ugt i32 %tmp31, %tmp26br i1 %tmp32, label %bb29, label %bb33bb33:br label %bb34bb34:ret void})");runWithLoopInfoAndSE(*M, "barney", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {Function::iterator FI = F.begin();// First basic block is entry - skip it.BasicBlock *Header = &*(++(++(++(++FI))));assert(Header->getName() == "bb29");Loop *L = LI.getLoopFor(Header);EXPECT_NE(L, nullptr);PHINode *Inst_i = dyn_cast<PHINode>(&Header->front());assert(Inst_i->getName() == "tmp30");InductionDescriptor IndDesc;bool IsInductionPHI =InductionDescriptor::isInductionPHI(Inst_i, L, &SE, IndDesc);EXPECT_TRUE(IsInductionPHI);});}
//===- IRSimilarityIdentifierTest.cpp - IRSimilarityIdentifier unit tests -===////// 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////===----------------------------------------------------------------------===////// Tests for components for finding similarity such as the instruction mapper,// suffix tree usage, and structural analysis.////===----------------------------------------------------------------------===//#include "llvm/Analysis/IRSimilarityIdentifier.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/Allocator.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;using namespace IRSimilarity;static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,StringRef ModuleStr) {SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleStr, Err, Context);assert(M && "Bad LLVM IR?");return M;}void getVectors(Module &M, IRInstructionMapper &Mapper,std::vector<IRInstructionData *> &InstrList,std::vector<unsigned> &UnsignedVec) {for (Function &F : M)for (BasicBlock &BB : F)Mapper.convertToUnsignedVec(BB, InstrList, UnsignedVec);}void getSimilarities(Module &M,std::vector<std::vector<IRSimilarityCandidate>> &SimilarityCandidates) {// In order to keep the size of the tests from becoming too large, we do not// recognize similarity for branches unless explicitly needed.IRSimilarityIdentifier Identifier(/*EnableBranchMatching = */false);SimilarityCandidates = Identifier.findSimilarity(M);}// Checks that different opcodes are mapped to different valuesTEST(IRInstructionMapper, OpcodeDifferentiation) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = mul i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check that the size of the unsigned vector and the instruction list are the// same as a safety check.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());// Make sure that the unsigned vector is the expected size.ASSERT_TRUE(UnsignedVec.size() == 3);// Check whether the instructions are not mapped to the same value.ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that the same opcodes and types are mapped to the same values.TEST(IRInstructionMapper, OpcodeTypeSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);// Check whether the instructions are mapped to the same value.ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that the same opcode and different types are mapped to different// values.TEST(IRInstructionMapper, TypeDifferentiation) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b, i64 %c, i64 %d) {bb0:%0 = add i32 %a, %b%1 = add i64 %c, %dret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that different predicates map to different values.TEST(IRInstructionMapper, PredicateDifferentiation) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = icmp sge i32 %b, %a%1 = icmp slt i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that predicates where that can be considered the same when the// operands are swapped, i.e. greater than to less than are mapped to the same// unsigned integer.TEST(IRInstructionMapper, PredicateIsomorphism) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = icmp sgt i32 %a, %b%1 = icmp slt i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that the same predicate maps to the same value.TEST(IRInstructionMapper, PredicateSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = icmp slt i32 %a, %b%1 = icmp slt i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that the same predicate maps to the same value for floating point// CmpInsts.TEST(IRInstructionMapper, FPPredicateSimilarity) {StringRef ModuleString = R"(define i32 @f(double %a, double %b) {bb0:%0 = fcmp olt double %a, %b%1 = fcmp olt double %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that the different predicate maps to a different value for floating// point CmpInsts.TEST(IRInstructionMapper, FPPredicatDifference) {StringRef ModuleString = R"(define i32 @f(double %a, double %b) {bb0:%0 = fcmp olt double %a, %b%1 = fcmp oge double %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that the zexts that have the same type parameters map to the same// unsigned integer.TEST(IRInstructionMapper, ZextTypeSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a) {bb0:%0 = zext i32 %a to i64%1 = zext i32 %a to i64ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that the sexts that have the same type parameters map to the same// unsigned integer.TEST(IRInstructionMapper, SextTypeSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a) {bb0:%0 = sext i32 %a to i64%1 = sext i32 %a to i64ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that the zexts that have the different type parameters map to the// different unsigned integers.TEST(IRInstructionMapper, ZextTypeDifference) {StringRef ModuleString = R"(define i32 @f(i32 %a, i8 %b) {bb0:%0 = zext i32 %a to i64%1 = zext i8 %b to i32ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that the sexts that have the different type parameters map to the// different unsigned integers.TEST(IRInstructionMapper, SextTypeDifference) {StringRef ModuleString = R"(define i32 @f(i32 %a, i8 %b) {bb0:%0 = sext i32 %a to i64%1 = sext i8 %b to i32ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that loads that have the same type are mapped to the same unsigned// integer.TEST(IRInstructionMapper, LoadSimilarType) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:%0 = load i32, i32* %a%1 = load i32, i32* %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that loads that have the different types are mapped to// different unsigned integers.TEST(IRInstructionMapper, LoadDifferentType) {StringRef ModuleString = R"(define i32 @f(i32* %a, i64* %b) {bb0:%0 = load i32, i32* %a%1 = load i64, i64* %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that loads that have the different aligns are mapped to different// unsigned integers.TEST(IRInstructionMapper, LoadDifferentAlign) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:%0 = load i32, i32* %a, align 4%1 = load i32, i32* %b, align 8ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that loads that have the different volatile settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, LoadDifferentVolatile) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:%0 = load volatile i32, i32* %a%1 = load i32, i32* %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that loads that have the same volatile settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, LoadSameVolatile) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:%0 = load volatile i32, i32* %a%1 = load volatile i32, i32* %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that loads that have the different atomicity settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, LoadDifferentAtomic) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:%0 = load atomic i32, i32* %a unordered, align 4%1 = load atomic i32, i32* %b monotonic, align 4ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that loads that have the same atomicity settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, LoadSameAtomic) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:%0 = load atomic i32, i32* %a unordered, align 4%1 = load atomic i32, i32* %b unordered, align 4ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that stores that have the same type are mapped to the same unsigned// integer.TEST(IRInstructionMapper, StoreSimilarType) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:store i32 1, i32* %astore i32 2, i32* %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that stores that have the different types are mapped to// different unsigned integers.TEST(IRInstructionMapper, StoreDifferentType) {StringRef ModuleString = R"(define i32 @f(i32* %a, i64* %b) {bb0:store i32 1, i32* %astore i64 1, i64* %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that stores that have the different aligns are mapped to different// unsigned integers.TEST(IRInstructionMapper, StoreDifferentAlign) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:store i32 1, i32* %a, align 4store i32 1, i32* %b, align 8ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that stores that have the different volatile settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, StoreDifferentVolatile) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:store volatile i32 1, i32* %astore i32 1, i32* %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that stores that have the same volatile settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, StoreSameVolatile) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:store volatile i32 1, i32* %astore volatile i32 1, i32* %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that loads that have the same atomicity settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, StoreSameAtomic) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:store atomic i32 1, i32* %a unordered, align 4store atomic i32 1, i32* %b unordered, align 4ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]);}// Checks that loads that have the different atomicity settings are mapped to// different unsigned integers.TEST(IRInstructionMapper, StoreDifferentAtomic) {StringRef ModuleString = R"(define i32 @f(i32* %a, i32* %b) {bb0:store atomic i32 1, i32* %a unordered, align 4store atomic i32 1, i32* %b monotonic, align 4ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(UnsignedVec.size() == 3);ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]);}// Checks that the branch is mapped to legal when the option is set.TEST(IRInstructionMapper, BranchLegal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = icmp slt i32 %a, %bbr i1 %0, label %bb0, label %bb1bb1:ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_TRUE(UnsignedVec[1] > UnsignedVec[0]);ASSERT_TRUE(UnsignedVec[1] < UnsignedVec[2]);}// Checks that a PHINode is mapped to be legal.TEST(IRInstructionMapper, PhiLegal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = phi i1 [ 0, %bb0 ], [ %0, %bb1 ]%1 = add i32 %a, %bret i32 0bb1:ret i32 1})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));}// Checks that a PHINode is mapped to be legal.TEST(IRInstructionMapper, PhiIllegal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = phi i1 [ 0, %bb0 ], [ %0, %bb1 ]%1 = add i32 %a, %bret i32 0bb1:ret i32 1})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// In most cases, the illegal instructions we are collecting don't require any// sort of setup. In these cases, we can just only have illegal instructions,// and the mapper will create 0 length vectors, and we can check that.// In cases where we have legal instructions needed to set up the illegal// instruction, to check illegal instructions are assigned unsigned integers// from the maximum value decreasing to 0, it will be greater than a legal// instruction that comes after. So to check that we have an illegal// instruction, we place a legal instruction after an illegal instruction, and// check that the illegal unsigned integer is greater than the unsigned integer// of the legal instruction.// Checks that an alloca instruction is mapped to be illegal.TEST(IRInstructionMapper, AllocaIllegal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = alloca i32ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that an getelementptr instruction is mapped to be legal. And that// the operands in getelementpointer instructions are the exact same after the// first element operand, which only requires the same type.TEST(IRInstructionMapper, GetElementPtrSameEndOperands) {StringRef ModuleString = R"(%struct.RT = type { i8, [10 x [20 x i32]], i8 }%struct.ST = type { i32, double, %struct.RT }define i32 @f(%struct.ST* %s, i64 %a, i64 %b) {bb0:%0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a, i32 0%1 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %b, i32 0ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_EQ(UnsignedVec[0], UnsignedVec[1]);}// Check that when the operands in getelementpointer instructions are not the// exact same after the first element operand, the instructions are mapped to// different values.TEST(IRInstructionMapper, GetElementPtrDifferentEndOperands) {StringRef ModuleString = R"(%struct.RT = type { i8, [10 x [20 x i32]], i8 }%struct.ST = type { i32, double, %struct.RT }define i32 @f(%struct.ST* %s, i64 %a, i64 %b) {bb0:%0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a, i32 0%1 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %b, i32 2ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Check that when the operands in getelementpointer instructions are not the// same initial base type, each instruction is mapped to a different value.TEST(IRInstructionMapper, GetElementPtrDifferentBaseType) {StringRef ModuleString = R"(%struct.RT = type { i8, [10 x [20 x i32]], i8 }%struct.ST = type { i32, double, %struct.RT }define i32 @f(%struct.ST* %s, %struct.RT* %r, i64 %a, i64 %b) {bb0:%0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a%1 = getelementptr inbounds %struct.RT, %struct.RT* %r, i64 %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Check that when the operands in getelementpointer instructions do not have// the same inbounds modifier, they are not counted as the same.TEST(IRInstructionMapper, GetElementPtrDifferentInBounds) {StringRef ModuleString = R"(%struct.RT = type { i8, [10 x [20 x i32]], i8 }%struct.ST = type { i32, double, %struct.RT }define i32 @f(%struct.ST* %s, %struct.RT* %r, i64 %a, i64 %b) {bb0:%0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a, i32 0%1 = getelementptr %struct.ST, %struct.ST* %s, i64 %b, i32 0ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Checks that indirect call instructions are mapped to be illegal when it is// specified to disallow them.TEST(IRInstructionMapper, CallsIllegalIndirect) {StringRef ModuleString = R"(define i32 @f(void()* %func) {bb0:call void %func()ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableIndirectCalls = false;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that indirect call instructions are mapped to be legal when it is not// specified to disallow them.TEST(IRInstructionMapper, CallsLegalIndirect) {StringRef ModuleString = R"(define i32 @f(void()* %func) {bb0:call void %func()call void %func()ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableIndirectCalls = true;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));}// Checks that a call instruction is mapped to be legal. Here we check that// a call with the same name, and same types are mapped to the same// value.TEST(IRInstructionMapper, CallsSameTypeSameName) {StringRef ModuleString = R"(declare i32 @f1(i32, i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = call i32 @f1(i32 %a, i32 %b)%1 = call i32 @f1(i32 %a, i32 %b)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_EQ(UnsignedVec[0], UnsignedVec[1]);}// Here we check that a calls with different names, but the same arguments types// are mapped to different value when specified that the name must match.TEST(IRInstructionMapper, CallsSameArgTypeDifferentNameDisallowed) {StringRef ModuleString = R"(declare i32 @f1(i32, i32)declare i32 @f2(i32, i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = call i32 @f1(i32 %a, i32 %b)%1 = call i32 @f2(i32 %a, i32 %b)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.EnableMatchCallsByName = true;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Here we check that a calls with different names, but the same arguments types// are mapped to the same value when it is not specifed that they must match.TEST(IRInstructionMapper, CallsSameArgTypeDifferentName) {StringRef ModuleString = R"(declare i32 @f1(i32, i32)declare i32 @f2(i32, i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = call i32 @f1(i32 %a, i32 %b)%1 = call i32 @f2(i32 %a, i32 %b)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.EnableMatchCallsByName = false;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_EQ(UnsignedVec[0], UnsignedVec[1]);}// Here we check that a calls with different names, and different arguments// types are mapped to different value.TEST(IRInstructionMapper, CallsDifferentArgTypeDifferentName) {StringRef ModuleString = R"(declare i32 @f1(i32, i32)declare i32 @f2(i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = call i32 @f1(i32 %a, i32 %b)%1 = call i32 @f2(i32 %a)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Here we check that calls with different names, and different return// types are mapped to different value.TEST(IRInstructionMapper, CallsDifferentReturnTypeDifferentName) {StringRef ModuleString = R"(declare i64 @f1(i32, i32)declare i32 @f2(i32, i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = call i64 @f1(i32 %a, i32 %b)%1 = call i32 @f2(i32 %a, i32 %b)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Here we check that calls with the same name, types, and parameters map to the// same unsigned integer.TEST(IRInstructionMapper, CallsSameParameters) {StringRef ModuleString = R"(declare i32 @f1(i32, i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = tail call fastcc i32 @f1(i32 %a, i32 %b)%1 = tail call fastcc i32 @f1(i32 %a, i32 %b)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_EQ(UnsignedVec[0], UnsignedVec[1]);}// Here we check that calls with different tail call settings are mapped to// different values.TEST(IRInstructionMapper, CallsDifferentTails) {StringRef ModuleString = R"(declare i32 @f1(i32, i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = tail call i32 @f1(i32 %a, i32 %b)%1 = call i32 @f1(i32 %a, i32 %b)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Here we check that calls with different calling convention settings are// mapped to different values.TEST(IRInstructionMapper, CallsDifferentCallingConventions) {StringRef ModuleString = R"(declare i32 @f1(i32, i32)define i32 @f(i32 %a, i32 %b) {bb0:%0 = call fastcc i32 @f1(i32 %a, i32 %b)%1 = call i32 @f1(i32 %a, i32 %b)ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));ASSERT_NE(UnsignedVec[0], UnsignedVec[1]);}// Checks that an invoke instruction is mapped to be illegal. Invoke// instructions are considered to be illegal because of the change in the// control flow that is currently not recognized.TEST(IRInstructionMapper, InvokeIllegal) {StringRef ModuleString = R"(define i32 @f(i8 *%gep1, i32 %b) {then:invoke i32 undef(i8* undef)to label %invoke unwind label %lpadinvoke:unreachablelpad:landingpad { i8*, i32 }catch i8* nullunreachable})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that an callbr instructions are considered to be illegal. Callbr// instructions are considered to be illegal because of the change in the// control flow that is currently not recognized.TEST(IRInstructionMapper, CallBrInstIllegal) {StringRef ModuleString = R"(define void @test() {fail:ret void}define i32 @f(i32 %a, i32 %b) {bb0:callbr void asm "xorl $0, $0; jmp ${1:l}", "r,X,~{dirflag},~{fpsr},~{flags}"(i32 %a, i8* blockaddress(@test, %fail)) to label %normal [label %fail]fail:ret i32 0normal:ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that an debuginfo intrinsics are mapped to be invisible. Since they// do not semantically change the program, they can be recognized as similar.TEST(IRInstructionMapper, DebugInfoInvisible) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {then:%0 = add i32 %a, %bcall void @llvm.dbg.value(metadata !0)%1 = add i32 %a, %bret i32 0}declare void @llvm.dbg.value(metadata)!0 = distinct !{!"test\00", i32 10})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3));}// The following are all exception handling intrinsics. We do not currently// handle these instruction because they are very context dependent.// Checks that an eh.typeid.for intrinsic is mapped to be illegal.TEST(IRInstructionMapper, ExceptionHandlingTypeIdIllegal) {StringRef ModuleString = R"(@_ZTIi = external constant i8*define i32 @f() {then:%0 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))ret i32 0}declare i32 @llvm.eh.typeid.for(i8*))";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that an eh.exceptioncode intrinsic is mapped to be illegal.TEST(IRInstructionMapper, ExceptionHandlingExceptionCodeIllegal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {entry:%0 = catchswitch within none [label %__except] unwind to caller__except:%1 = catchpad within %0 [i8* null]catchret from %1 to label %__exceptthen:%2 = call i32 @llvm.eh.exceptioncode(token %1)ret i32 0}declare i32 @llvm.eh.exceptioncode(token))";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that an eh.unwind intrinsic is mapped to be illegal.TEST(IRInstructionMapper, ExceptionHandlingUnwindIllegal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {entry:call void @llvm.eh.unwind.init()ret i32 0}declare void @llvm.eh.unwind.init())";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that an eh.exceptionpointer intrinsic is mapped to be illegal.TEST(IRInstructionMapper, ExceptionHandlingExceptionPointerIllegal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {entry:%0 = call i8* @llvm.eh.exceptionpointer.p0i8(i32 0)ret i32 0}declare i8* @llvm.eh.exceptionpointer.p0i8(i32))";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that a catchpad instruction is mapped to an illegal value.TEST(IRInstructionMapper, CatchpadIllegal) {StringRef ModuleString = R"(declare void @llvm.donothing() nounwind readnonedefine void @function() personality i8 3 {entry:invoke void @llvm.donothing() to label %normal unwind label %exceptionexception:%cs1 = catchswitch within none [label %catchpad1] unwind to callercatchpad1:catchpad within %cs1 []br label %normalnormal:ret void})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// Checks that a cleanuppad instruction is mapped to an illegal value.TEST(IRInstructionMapper, CleanuppadIllegal) {StringRef ModuleString = R"(declare void @llvm.donothing() nounwind readnonedefine void @function() personality i8 3 {entry:invoke void @llvm.donothing() to label %normal unwind label %exceptionexception:%cs1 = catchswitch within none [label %catchpad1] unwind to callercatchpad1:%clean = cleanuppad within none []br label %normalnormal:ret void})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(1));ASSERT_GT(UnsignedVec[0], Mapper.IllegalInstrNumber);}// The following three instructions are memory transfer and setting based, which// are considered illegal since is extra checking needed to handle the address// space checking.// Checks that a memset instruction is mapped to an illegal value when// specified.TEST(IRInstructionMapper, MemSetIllegal) {StringRef ModuleString = R"(declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1)define i64 @function(i64 %x, i64 %z, i64 %n) {entry:%pool = alloca [59 x i64], align 4%tmp = bitcast [59 x i64]* %pool to i8*call void @llvm.memset.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false)%cmp3 = icmp eq i64 %n, 0%a = add i64 %x, %z%c = add i64 %x, %zret i64 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableIntrinsics = false;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(7));ASSERT_TRUE(UnsignedVec[2] < UnsignedVec[0]);}// Checks that a memcpy instruction is mapped to an illegal value when// specified.TEST(IRInstructionMapper, MemCpyIllegal) {StringRef ModuleString = R"(declare void @llvm.memcpy.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1)define i64 @function(i64 %x, i64 %z, i64 %n) {entry:%pool = alloca [59 x i64], align 4%tmp = bitcast [59 x i64]* %pool to i8*call void @llvm.memcpy.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false)%cmp3 = icmp eq i64 %n, 0%a = add i64 %x, %z%c = add i64 %x, %zret i64 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableIntrinsics = false;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(7));ASSERT_GT(UnsignedVec[2], UnsignedVec[3]);ASSERT_LT(UnsignedVec[2], UnsignedVec[0]);}// Checks that a memmove instruction is mapped to an illegal value when// specified.TEST(IRInstructionMapper, MemMoveIllegal) {StringRef ModuleString = R"(declare void @llvm.memmove.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1)define i64 @function(i64 %x, i64 %z, i64 %n) {entry:%pool = alloca [59 x i64], align 4%tmp = bitcast [59 x i64]* %pool to i8*call void @llvm.memmove.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false)%cmp3 = icmp eq i64 %n, 0%a = add i64 %x, %z%c = add i64 %x, %zret i64 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableIntrinsics = false;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(7));ASSERT_LT(UnsignedVec[2], UnsignedVec[0]);}// Checks that mem* instructions are mapped to an legal value when not// specified, and that all the intrinsics are marked differently.TEST(IRInstructionMapper, MemOpsLegal) {StringRef ModuleString = R"(declare void @llvm.memmove.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1)declare void @llvm.memcpy.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1)declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1)define i64 @function(i64 %x, i64 %z, i64 %n) {entry:%pool = alloca [59 x i64], align 4%tmp = bitcast [59 x i64]* %pool to i8*call void @llvm.memmove.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false)call void @llvm.memcpy.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false)call void @llvm.memset.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false)%cmp3 = icmp eq i64 %n, 0%a = add i64 %x, %z%c = add i64 %x, %zret i64 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableIntrinsics = true;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(9));ASSERT_LT(UnsignedVec[2], UnsignedVec[3]);ASSERT_LT(UnsignedVec[3], UnsignedVec[4]);ASSERT_LT(UnsignedVec[4], UnsignedVec[5]);}// Checks that a variable argument instructions are mapped to an illegal value.// We exclude variable argument instructions since variable arguments// requires extra checking of the argument list.TEST(IRInstructionMapper, VarArgsIllegal) {StringRef ModuleString = R"(declare void @llvm.va_start(i8*)declare void @llvm.va_copy(i8*, i8*)declare void @llvm.va_end(i8*)define i32 @func1(i32 %a, double %b, i8* %v, ...) nounwind {entry:%a.addr = alloca i32, align 4%b.addr = alloca double, align 8%ap = alloca i8*, align 4%c = alloca i32, align 4store i32 %a, i32* %a.addr, align 4store double %b, double* %b.addr, align 8%ap1 = bitcast i8** %ap to i8*call void @llvm.va_start(i8* %ap1)store double %b, double* %b.addr, align 8store double %b, double* %b.addr, align 8%0 = va_arg i8** %ap, i32store double %b, double* %b.addr, align 8store double %b, double* %b.addr, align 8call void @llvm.va_copy(i8* %v, i8* %ap1)store double %b, double* %b.addr, align 8store double %b, double* %b.addr, align 8call void @llvm.va_end(i8* %ap1)store i32 %0, i32* %c, align 4%tmp = load i32, i32* %c, align 4ret i32 %tmp})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableIntrinsics = false;getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_EQ(InstrList.size(), UnsignedVec.size());ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(17));ASSERT_TRUE(UnsignedVec[7] < UnsignedVec[0]);ASSERT_TRUE(UnsignedVec[13] < UnsignedVec[10]);ASSERT_TRUE(UnsignedVec[16] < UnsignedVec[13]);}// Check the length of adding two illegal instructions one after th other. We// should find that only one element is added for each illegal range.TEST(IRInstructionMapper, RepeatedIllegalLength) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = mul i32 %a, %b%2 = alloca i32%3 = alloca i32%4 = add i32 %a, %b%5 = mul i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check that the size of the unsigned vector and the instruction list are the// same as a safety check.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());// Make sure that the unsigned vector is the expected size.ASSERT_TRUE(UnsignedVec.size() == 6);}// A helper function that accepts an instruction list from a module made up of// two blocks of two legal instructions and terminator, and checks them for// instruction similarity.static bool longSimCandCompare(std::vector<IRInstructionData *> &InstrList,bool Structure = false, unsigned Length = 2,unsigned StartIdxOne = 0,unsigned StartIdxTwo = 3) {std::vector<IRInstructionData *>::iterator Start, End;Start = InstrList.begin();End = InstrList.begin();std::advance(End, StartIdxOne + Length - 1);IRSimilarityCandidate Cand1(StartIdxOne, Length, *Start, *End);Start = InstrList.begin();End = InstrList.begin();std::advance(Start, StartIdxTwo);std::advance(End, StartIdxTwo + Length - 1);IRSimilarityCandidate Cand2(StartIdxTwo, Length, *Start, *End);if (Structure)return IRSimilarityCandidate::compareStructure(Cand1, Cand2);return IRSimilarityCandidate::isSimilar(Cand1, Cand2);}// Checks that two adds with commuted operands are considered to be the same// instructions.TEST(IRSimilarityCandidate, CheckIdenticalInstructions) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(3));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());std::vector<IRInstructionData *>::iterator Start, End;Start = InstrList.begin();End = InstrList.begin();std::advance(End, 1);IRSimilarityCandidate Cand1(0, 2, *Start, *End);IRSimilarityCandidate Cand2(0, 2, *Start, *End);ASSERT_TRUE(IRSimilarityCandidate::isSimilar(Cand1, Cand2));}// Checks that comparison instructions are found to be similar instructions// when the operands are flipped and the predicate is also swapped.TEST(IRSimilarityCandidate, PredicateIsomorphism) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = icmp sgt i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = icmp slt i32 %a, %b%3 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() > 5);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());std::vector<IRInstructionData *>::iterator Start, End;Start = InstrList.begin();End = InstrList.begin();std::advance(End, 1);IRSimilarityCandidate Cand1(0, 2, *Start, *End);Start = InstrList.begin();End = InstrList.begin();std::advance(Start, 3);std::advance(End, 4);IRSimilarityCandidate Cand2(3, 2, *Start, *End);ASSERT_TRUE(IRSimilarityCandidate::isSimilar(Cand1, Cand2));}// Checks that IRSimilarityCandidates wrapping these two regions of instructions// are able to differentiate between instructions that have different opcodes.TEST(IRSimilarityCandidate, CheckRegionsDifferentInstruction) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %aret i32 0bb1:%2 = sub i32 %a, %b%3 = add i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_FALSE(longSimCandCompare(InstrList));}// Checks that IRSimilarityCandidates wrapping these two regions of instructions// are able to differentiate between instructions that have different types.TEST(IRSimilarityCandidate, CheckRegionsDifferentTypes) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b, i64 %c, i64 %d) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %aret i32 0bb1:%2 = add i64 %c, %d%3 = add i64 %d, %cret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_FALSE(longSimCandCompare(InstrList));}// Check that debug instructions do not impact similarity. They are marked as// invisible.TEST(IRSimilarityCandidate, IdenticalWithDebug) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %bcall void @llvm.dbg.value(metadata !0)%1 = add i32 %b, %aret i32 0bb1:%2 = add i32 %a, %bcall void @llvm.dbg.value(metadata !1)%3 = add i32 %b, %aret i32 0bb2:%4 = add i32 %a, %b%5 = add i32 %b, %aret i32 0}declare void @llvm.dbg.value(metadata)!0 = distinct !{!"test\00", i32 10}!1 = distinct !{!"test\00", i32 11})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(9));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList));}// Checks that IRSimilarityCandidates that include illegal instructions, are not// considered to be the same set of instructions. In these sets of instructions// the allocas are illegal.TEST(IRSimilarityCandidate, IllegalInCandidate) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %a, %b%2 = alloca i32ret i32 0bb1:%3 = add i32 %a, %b%4 = add i32 %a, %b%5 = alloca i32ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());std::vector<IRInstructionData *>::iterator Start, End;Start = InstrList.begin();End = InstrList.begin();std::advance(End, 2);IRSimilarityCandidate Cand1(0, 3, *Start, *End);Start = InstrList.begin();End = InstrList.begin();std::advance(Start, 3);std::advance(End, 5);IRSimilarityCandidate Cand2(3, 3, *Start, *End);ASSERT_FALSE(IRSimilarityCandidate::isSimilar(Cand1, Cand2));}// Checks that different structure, in this case, where we introduce a new// needed input in one region, is recognized as different.TEST(IRSimilarityCandidate, DifferentStructure) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %aret i32 0bb1:%2 = add i32 %a, %b%3 = add i32 %b, %0ret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_FALSE(longSimCandCompare(InstrList, true));}// Checks that comparison instructions are found to have the same structure// when the operands are flipped and the predicate is also swapped.TEST(IRSimilarityCandidate, PredicateIsomorphismStructure) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = icmp sgt i32 %a, %b%1 = add i32 %a, %bbr label %bb1bb1:%2 = icmp slt i32 %b, %a%3 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() > 5);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList, true));}// Checks that different predicates are counted as diferent.TEST(IRSimilarityCandidate, PredicateDifference) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = icmp sge i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = icmp slt i32 %b, %a%3 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);ASSERT_TRUE(InstrList.size() > 5);ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_FALSE(longSimCandCompare(InstrList));}// Checks that the same structure is recognized between two candidates. The// items %a and %b are used in the same way in both sets of instructions.TEST(IRSimilarityCandidate, SameStructure) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = sub i32 %b, %aret i32 0bb1:%2 = add i32 %a, %b%3 = sub i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList, true));}// Checks that the canonical numbering between two candidates matches the found// mapping between two candidates.TEST(IRSimilarityCandidate, CanonicalNumbering) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = sub i32 %b, %aret i32 0bb1:%2 = add i32 %a, %b%3 = sub i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6));// Check that the instructions were added correctly to both vectors.ASSERT_EQ(InstrList.size(), UnsignedVec.size());std::vector<IRInstructionData *>::iterator Start, End;Start = InstrList.begin();End = InstrList.begin();std::advance(End, 1);IRSimilarityCandidate Cand1(0, 2, *Start, *End);Start = InstrList.begin();End = InstrList.begin();std::advance(Start, 3);std::advance(End, 4);IRSimilarityCandidate Cand2(3, 2, *Start, *End);DenseMap<unsigned, DenseSet<unsigned>> Mapping1;DenseMap<unsigned, DenseSet<unsigned>> Mapping2;ASSERT_TRUE(IRSimilarityCandidate::compareStructure(Cand1, Cand2, Mapping1,Mapping2));IRSimilarityCandidate::createCanonicalMappingFor(Cand1);Cand2.createCanonicalRelationFrom(Cand1, Mapping1, Mapping2);for (std::pair<unsigned, DenseSet<unsigned>> &P : Mapping2) {unsigned Source = P.first;ASSERT_TRUE(Cand2.getCanonicalNum(Source).has_value());unsigned Canon = *Cand2.getCanonicalNum(Source);ASSERT_TRUE(Cand1.fromCanonicalNum(Canon).has_value());unsigned Dest = *Cand1.fromCanonicalNum(Canon);DenseSet<unsigned>::iterator It = P.second.find(Dest);ASSERT_NE(It, P.second.end());}}// Checks that the same structure is recognized between two candidates. While// the input names are reversed, they still perform the same overall operation.TEST(IRSimilarityCandidate, DifferentNameSameStructure) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %aret i32 0bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList, true));}// Checks that the same structure is recognized between two candidates when// the branches target other blocks inside the same region, the relative// distance between the blocks must be the same.TEST(IRSimilarityCandidate, SameBranchStructureInternal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bret i32 0}define i32 @f2(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(12));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList, true, 5, 0, 6));}// Checks that the different structure is recognized between two candidates,// when the branches target other blocks inside the same region, the relative// distance between the blocks must be the same.TEST(IRSimilarityCandidate, DifferentBranchStructureInternal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb2bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bbr label %bb2bb2:%4 = add i32 %b, %a%5 = add i32 %a, %bret i32 0}define i32 @f2(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bbr label %bb2bb2:%4 = add i32 %b, %a%5 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(18));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_FALSE(longSimCandCompare(InstrList, true, 6, 0, 9));}// Checks that the same structure is recognized between two candidates, when// the branches target other blocks outside region, the relative distance// does not need to be the same.TEST(IRSimilarityCandidate, SameBranchStructureOutside) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bret i32 0}define i32 @f2(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(12));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList, true, 3, 0, 6));}// Checks that the same structure is recognized between two candidates, when// the branches target other blocks outside region, the relative distance// does not need to be the same.TEST(IRSimilarityCandidate, DifferentBranchStructureOutside) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bret i32 0}define i32 @f2(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb2bb1:%2 = add i32 %b, %a%3 = add i32 %a, %bbr label %bb2bb2:%4 = add i32 %b, %a%5 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(15));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList, true, 3, 0, 6));}// Checks that the same structure is recognized between two candidates,// when the phi predecessor are other blocks inside the same region,// the relative distance between the blocks must be the same.TEST(IRSimilarityCandidate, SamePHIStructureInternal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:br label %bb2bb1:br label %bb2bb2:%0 = phi i32 [ %a, %bb0 ], [ %b, %bb1 ]%1 = add i32 %b, %a%2 = add i32 %a, %bret i32 0}define i32 @f2(i32 %a, i32 %b) {bb0:br label %bb2bb1:br label %bb2bb2:%0 = phi i32 [ %a, %bb0 ], [ %b, %bb1 ]%1 = add i32 %b, %a%2 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(12));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_TRUE(longSimCandCompare(InstrList, true, 4, 0, 6));}// Checks that the different structure is recognized between two candidates,// when the phi predecessor are other blocks inside the same region,// the relative distance between the blocks must be the same.TEST(IRSimilarityCandidate, DifferentPHIStructureInternal) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:br label %bb2bb1:br label %bb2bb3:br label %bb2bb2:%0 = phi i32 [ %a, %bb0 ], [ %b, %bb1 ]%1 = add i32 %b, %a%2 = add i32 %a, %bret i32 0}define i32 @f2(i32 %a, i32 %b) {bb0:br label %bb2bb1:br label %bb2bb3:br label %bb2bb2:%0 = phi i32 [ %a, %bb0 ], [ %b, %bb3 ]%1 = add i32 %b, %a%2 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<IRInstructionData *> InstrList;std::vector<unsigned> UnsignedVec;SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator;IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator);Mapper.InstClassifier.EnableBranches = true;Mapper.initializeForBBs(*M);getVectors(*M, Mapper, InstrList, UnsignedVec);// Check to make sure that we have a long enough region.ASSERT_EQ(InstrList.size(), static_cast<unsigned>(14));// Check that the instructions were added correctly to both vectors.ASSERT_TRUE(InstrList.size() == UnsignedVec.size());ASSERT_FALSE(longSimCandCompare(InstrList, true, 5, 0, 7));}// Checks that two sets of identical instructions are found to be the same.// Both sequences of adds have the same operand ordering, and the same// instructions, making them strcturally equivalent.TEST(IRSimilarityIdentifier, IdentitySimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = sub i32 %b, %abr label %bb1bb1:%2 = add i32 %a, %b%3 = sub i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 1);for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) {ASSERT_TRUE(Cands.size() == 2);unsigned InstIdx = 0;for (IRSimilarityCandidate &Cand : Cands) {ASSERT_TRUE(Cand.getStartIdx() == InstIdx);InstIdx += 3;}}}// Checks that incorrect sequences are not found as similar. In this case,// we have different sequences of instructions.TEST(IRSimilarityIdentifier, InstructionDifference) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) {bb0:%0 = sub i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %c, %d%3 = sub i32 %d, %cret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.empty());}// This test checks to see whether we can detect similarity for commutative// instructions where the operands have been reversed.TEST(IRSimilarityIdentifier, CommutativeSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %b, %abr label %bb1bb1:%2 = add i32 %a, %b%3 = add i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 1);for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) {ASSERT_TRUE(Cands.size() == 2);unsigned InstIdx = 0;for (IRSimilarityCandidate &Cand : Cands) {ASSERT_TRUE(Cand.getStartIdx() == InstIdx);InstIdx += 3;}}}// This test ensures that when the first instruction in a sequence is// a commutative instruction with the same value (mcomm_inst_same_val), but the// corresponding instruction (comm_inst_diff_val) is not, we mark the regions// and not similar.TEST(IRSimilarityIdentifier, CommutativeSameValueFirstMisMatch) {StringRef ModuleString = R"(define void @v_1_0(i64 %v_33) {entry:%comm_inst_same_val = mul i64 undef, undef%add = add i64 %comm_inst_same_val, %v_33%comm_inst_diff_val = mul i64 0, undef%mul.i = add i64 %comm_inst_diff_val, %comm_inst_diff_valunreachable})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 0);}// This test makes sure that intrinsic functions that are marked commutative// are still treated as non-commutative since they are function calls.TEST(IRSimilarityIdentifier, IntrinsicCommutative) {// If treated as commutative, we will fail to find a valid mapping, causing// an assertion error.StringRef ModuleString = R"(define void @foo() {entry:%0 = call i16 @llvm.smul.fix.i16(i16 16384, i16 16384, i32 15)store i16 %0, i16* undef, align 1%1 = icmp eq i16 undef, 8192call void @bar()%2 = call i16 @llvm.smul.fix.i16(i16 -16384, i16 16384, i32 15)store i16 %2, i16* undef, align 1%3 = icmp eq i16 undef, -8192call void @bar()%4 = call i16 @llvm.smul.fix.i16(i16 -16384, i16 -16384, i32 15)ret void}declare void @bar(); Function Attrs: nofree nosync nounwind readnone speculatable willreturndeclare i16 @llvm.smul.fix.i16(i16, i16, i32 immarg))";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 0);}// This test checks to see whether we can detect different structure in// commutative instructions. In this case, the second operand in the second// add is different.TEST(IRSimilarityIdentifier, NoCommutativeSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = add i32 %1, %bbr label %bb1bb1:%2 = add i32 %a, %b%3 = add i32 %2, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 0);}// Check that we are not finding similarity in non commutative// instructions. That is, while the instruction and operands used are the same// in the two subtraction sequences, they are in a different order, and cannot// be counted as the same since a subtraction is not commutative.TEST(IRSimilarityIdentifier, NonCommutativeDifference) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = sub i32 %a, %b%1 = sub i32 %b, %abr label %bb1bb1:%2 = sub i32 %a, %b%3 = sub i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.empty());}// Check that we find similarity despite changing the register names.TEST(IRSimilarityIdentifier, MappingSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) {bb0:%0 = add i32 %a, %b%1 = sub i32 %b, %abr label %bb1bb1:%2 = add i32 %c, %d%3 = sub i32 %d, %cret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 1);for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) {ASSERT_TRUE(Cands.size() == 2);unsigned InstIdx = 0;for (IRSimilarityCandidate &Cand : Cands) {ASSERT_TRUE(Cand.getStartIdx() == InstIdx);InstIdx += 3;}}}// Check that we find instances of swapped predicate isomorphism. That is,// for predicates that can be flipped, e.g. greater than to less than,// we can identify that instances of these different literal predicates, but are// the same within a single swap can be found.TEST(IRSimilarityIdentifier, PredicateIsomorphism) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 %a, %b%1 = icmp sgt i32 %b, %abr label %bb1bb1:%2 = add i32 %a, %b%3 = icmp slt i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 1);for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) {ASSERT_TRUE(Cands.size() == 2);unsigned InstIdx = 0;for (IRSimilarityCandidate &Cand : Cands) {ASSERT_TRUE(Cand.getStartIdx() == InstIdx);InstIdx += 3;}}}// Checks that constants are detected as the same operand in each use in the// sequences of instructions. Also checks that we can find structural// equivalence using constants. In this case the 1 has the same use pattern as// %a.TEST(IRSimilarityIdentifier, ConstantMappingSimilarity) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 1, %b%1 = icmp sgt i32 %b, 1br label %bb1bb1:%2 = add i32 %a, %b%3 = icmp sgt i32 %b, %aret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.size() == 1);for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) {ASSERT_TRUE(Cands.size() == 2);unsigned InstIdx = 0;for (IRSimilarityCandidate &Cand : Cands) {ASSERT_TRUE(Cand.getStartIdx() == InstIdx);InstIdx += 3;}}}// Check that constants are uniquely identified. i.e. two different constants// are not considered the same. This means that this should not find any// structural similarity.TEST(IRSimilarityIdentifier, ConstantMappingDifference) {StringRef ModuleString = R"(define i32 @f(i32 %a, i32 %b) {bb0:%0 = add i32 1, %b%1 = icmp sgt i32 %b, 2br label %bb1bb1:%2 = add i32 %a, %b%3 = icmp slt i32 %a, %bret i32 0})";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates;getSimilarities(*M, SimilarityCandidates);ASSERT_TRUE(SimilarityCandidates.empty());}
//===--- GlobalsModRefTest.cpp - Mixed TBAA unit tests --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/GlobalsModRef.h"#include "llvm/ADT/Triple.h"#include "llvm/Analysis/CallGraph.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;TEST(GlobalsModRef, OptNone) {StringRef Assembly = R"(define void @f1() optnone {ret void}define void @f2() optnone readnone {ret void}define void @f3() optnone readonly {ret void})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";const auto &funcs = M->functions();auto I = funcs.begin();ASSERT_NE(I, funcs.end());const Function &F1 = *I;ASSERT_NE(++I, funcs.end());const Function &F2 = *I;ASSERT_NE(++I, funcs.end());const Function &F3 = *I;EXPECT_EQ(++I, funcs.end());Triple Trip(M->getTargetTriple());TargetLibraryInfoImpl TLII(Trip);TargetLibraryInfo TLI(TLII);auto GetTLI = [&TLI](Function &F) -> TargetLibraryInfo & { return TLI; };llvm::CallGraph CG(*M);auto AAR = GlobalsAAResult::analyzeModule(*M, GetTLI, CG);EXPECT_EQ(FMRB_UnknownModRefBehavior, AAR.getModRefBehavior(&F1));EXPECT_EQ(FMRB_DoesNotAccessMemory, AAR.getModRefBehavior(&F2));EXPECT_EQ(FMRB_OnlyReadsMemory, AAR.getModRefBehavior(&F3));}
//===- FunctionPropertiesAnalysisTest.cpp - Function Properties Unit Tests-===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/FunctionPropertiesAnalysis.h"#include "llvm/ADT/iterator_range.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/PassManager.h"#include "llvm/Passes/PassBuilder.h"#include "llvm/Passes/StandardInstrumentations.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/Cloning.h"#include "gtest/gtest.h"#include <cstring>using namespace llvm;namespace {class FunctionPropertiesAnalysisTest : public testing::Test {public:FunctionPropertiesAnalysisTest() {FAM.registerPass([&] { return DominatorTreeAnalysis(); });FAM.registerPass([&] { return LoopAnalysis(); });FAM.registerPass([&] { return PassInstrumentationAnalysis(); });}protected:std::unique_ptr<DominatorTree> DT;std::unique_ptr<LoopInfo> LI;FunctionAnalysisManager FAM;FunctionPropertiesInfo buildFPI(Function &F) {return FunctionPropertiesInfo::getFunctionPropertiesInfo(F, FAM);}void invalidate(Function &F) {PreservedAnalyses PA = PreservedAnalyses::none();FAM.invalidate(F, PA);}std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("MLAnalysisTests", errs());return Mod;}CallBase* findCall(Function& F, const char* Name = nullptr) {for (auto &BB : F)for (auto &I : BB )if (auto *CB = dyn_cast<CallBase>(&I))if (!Name || CB->getName() == Name)return CB;return nullptr;}};TEST_F(FunctionPropertiesAnalysisTest, BasicTest) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"declare i32 @f1(i32)declare i32 @f2(i32)define i32 @branches(i32) {%cond = icmp slt i32 %0, 3br i1 %cond, label %then, label %elsethen:%ret.1 = call i32 @f1(i32 %0)br label %last.blockelse:%ret.2 = call i32 @f2(i32 %0)br label %last.blocklast.block:%ret = phi i32 [%ret.1, %then], [%ret.2, %else]ret i32 %ret}define internal i32 @top() {%1 = call i32 @branches(i32 2)%2 = call i32 @f1(i32 %1)ret i32 %2})IR");Function *BranchesFunction = M->getFunction("branches");FunctionPropertiesInfo BranchesFeatures = buildFPI(*BranchesFunction);EXPECT_EQ(BranchesFeatures.BasicBlockCount, 4);EXPECT_EQ(BranchesFeatures.BlocksReachedFromConditionalInstruction, 2);// 2 Users: top is one. The other is added because @branches is not internal,// so it may have external callers.EXPECT_EQ(BranchesFeatures.Uses, 2);EXPECT_EQ(BranchesFeatures.DirectCallsToDefinedFunctions, 0);EXPECT_EQ(BranchesFeatures.LoadInstCount, 0);EXPECT_EQ(BranchesFeatures.StoreInstCount, 0);EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0);EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0);Function *TopFunction = M->getFunction("top");FunctionPropertiesInfo TopFeatures = buildFPI(*TopFunction);EXPECT_EQ(TopFeatures.BasicBlockCount, 1);EXPECT_EQ(TopFeatures.BlocksReachedFromConditionalInstruction, 0);EXPECT_EQ(TopFeatures.Uses, 0);EXPECT_EQ(TopFeatures.DirectCallsToDefinedFunctions, 1);EXPECT_EQ(BranchesFeatures.LoadInstCount, 0);EXPECT_EQ(BranchesFeatures.StoreInstCount, 0);EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0);EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0);}TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32 @f1(i32 %a) {%b = call i32 @f2(i32 %a)%c = add i32 %b, 2ret i32 %c}define i32 @f2(i32 %a) {%b = add i32 %a, 1ret i32 %b})IR");Function *F1 = M->getFunction("f1");CallBase* CB = findCall(*F1, "b");EXPECT_NE(CB, nullptr);FunctionPropertiesInfo ExpectedInitial;ExpectedInitial.BasicBlockCount = 1;ExpectedInitial.TotalInstructionCount = 3;ExpectedInitial.Uses = 1;ExpectedInitial.DirectCallsToDefinedFunctions = 1;FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;ExpectedFinal.DirectCallsToDefinedFunctions = 0;auto FPI = buildFPI(*F1);EXPECT_EQ(FPI, ExpectedInitial);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(FPI, ExpectedFinal);}TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32 @f1(i32 %a) {entry:%i = icmp slt i32 %a, 0br i1 %i, label %if.then, label %if.elseif.then:%b = call i32 @f2(i32 %a)%c1 = add i32 %b, 2br label %endif.else:%c2 = add i32 %a, 1br label %endend:%ret = phi i32 [%c1, %if.then],[%c2, %if.else]ret i32 %ret}define i32 @f2(i32 %a) {%b = add i32 %a, 1ret i32 %b})IR");Function *F1 = M->getFunction("f1");CallBase* CB = findCall(*F1, "b");EXPECT_NE(CB, nullptr);FunctionPropertiesInfo ExpectedInitial;ExpectedInitial.BasicBlockCount = 4;ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;ExpectedInitial.TotalInstructionCount = 9;ExpectedInitial.Uses = 1;ExpectedInitial.DirectCallsToDefinedFunctions = 1;FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;ExpectedFinal.DirectCallsToDefinedFunctions = 0;auto FPI = buildFPI(*F1);EXPECT_EQ(FPI, ExpectedInitial);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(FPI, ExpectedFinal);}TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i32 @f1(i32 %a) {entry:%i = icmp slt i32 %a, 0br i1 %i, label %if.then, label %if.elseif.then:%b = call i32 @f2(i32 %a)%c1 = add i32 %b, 2br label %endif.else:%c2 = add i32 %a, 1br label %endend:%ret = phi i32 [%c1, %if.then],[%c2, %if.else]ret i32 %ret}define i32 @f2(i32 %a) {entry:br label %looploop:%indvar = phi i32 [%indvar.next, %loop], [0, %entry]%b = add i32 %a, %indvar%indvar.next = add i32 %indvar, 1%cond = icmp slt i32 %indvar.next, %abr i1 %cond, label %loop, label %exitexit:ret i32 %b})IR");Function *F1 = M->getFunction("f1");CallBase* CB = findCall(*F1, "b");EXPECT_NE(CB, nullptr);FunctionPropertiesInfo ExpectedInitial;ExpectedInitial.BasicBlockCount = 4;ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;ExpectedInitial.TotalInstructionCount = 9;ExpectedInitial.Uses = 1;ExpectedInitial.DirectCallsToDefinedFunctions = 1;FunctionPropertiesInfo ExpectedFinal;ExpectedFinal.BasicBlockCount = 6;ExpectedFinal.BlocksReachedFromConditionalInstruction = 4;ExpectedFinal.Uses = 1;ExpectedFinal.MaxLoopDepth = 1;ExpectedFinal.TopLevelLoopCount = 1;ExpectedFinal.TotalInstructionCount = 14;auto FPI = buildFPI(*F1);EXPECT_EQ(FPI, ExpectedInitial);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(FPI, ExpectedFinal);}TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"declare void @might_throw()define internal void @callee() {entry:call void @might_throw()ret void}define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {entry:invoke void @callee()to label %cont unwind label %exccont:ret i32 0exc:%exn = landingpad {i8*, i32}cleanupret i32 1}declare i32 @__gxx_personality_v0(...))IR");Function *F1 = M->getFunction("caller");CallBase* CB = findCall(*F1);EXPECT_NE(CB, nullptr);auto FPI = buildFPI(*F1);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),F1->getBasicBlockList().size());EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),F1->getInstructionCount());}TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(declare void @might_throw()define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {entry:invoke void @might_throw()to label %cont unwind label %exccont:ret i32 0exc:%exn = landingpad {i8*, i32}cleanupresume { i8*, i32 } %exn}define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {entry:%X = invoke i32 @callee()to label %cont unwind label %Handlercont:ret i32 %XHandler:%exn = landingpad {i8*, i32}cleanupret i32 1}declare i32 @__gxx_personality_v0(...))IR");Function *F1 = M->getFunction("caller");CallBase* CB = findCall(*F1);EXPECT_NE(CB, nullptr);auto FPI = buildFPI(*F1);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),F1->getBasicBlockList().size() - 1);EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),F1->getInstructionCount() - 2);EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));}TEST_F(FunctionPropertiesAnalysisTest, Rethrow) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(declare void @might_throw()define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {entry:invoke void @might_throw()to label %cont unwind label %exccont:ret i32 0exc:%exn = landingpad {i8*, i32}cleanupresume { i8*, i32 } %exn}define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {entry:%X = invoke i32 @callee()to label %cont unwind label %Handlercont:ret i32 %XHandler:%exn = landingpad {i8*, i32}cleanupret i32 1}declare i32 @__gxx_personality_v0(...))IR");Function *F1 = M->getFunction("caller");CallBase* CB = findCall(*F1);EXPECT_NE(CB, nullptr);auto FPI = buildFPI(*F1);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),F1->getBasicBlockList().size() - 1);EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),F1->getInstructionCount() - 2);EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));}TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(declare void @external_func()@exception_type1 = external global i8@exception_type2 = external global i8define internal void @inner() personality i8* null {invoke void @external_func()to label %cont unwind label %lpadcont:ret voidlpad:%lp = landingpad i32catch i8* @exception_type1resume i32 %lp}define void @outer() personality i8* null {invoke void @inner()to label %cont unwind label %lpadcont:ret voidlpad:%lp = landingpad i32cleanupcatch i8* @exception_type2resume i32 %lp})IR");Function *F1 = M->getFunction("outer");CallBase* CB = findCall(*F1);EXPECT_NE(CB, nullptr);auto FPI = buildFPI(*F1);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),F1->getBasicBlockList().size() - 1);EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),F1->getInstructionCount() - 2);EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));}TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(declare void @external_func()@exception_type1 = external global i8@exception_type2 = external global i8define internal void @inner() personality i8* null {invoke void @external_func()to label %cont unwind label %lpadcont:ret voidlpad:%lp = landingpad i32catch i8* @exception_type1resume i32 %lp}define void @outer(i32 %a) personality i8* null {entry:%i = icmp slt i32 %a, 0br i1 %i, label %if.then, label %contif.then:invoke void @inner()to label %cont unwind label %lpadcont:ret voidlpad:%lp = landingpad i32cleanupcatch i8* @exception_type2resume i32 %lp})IR");Function *F1 = M->getFunction("outer");CallBase* CB = findCall(*F1);EXPECT_NE(CB, nullptr);auto FPI = buildFPI(*F1);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),F1->getBasicBlockList().size() - 1);EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),F1->getInstructionCount() - 2);EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));}TEST_F(FunctionPropertiesAnalysisTest, InlineSameLoopBB) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"declare i32 @a()declare i32 @b()define i32 @f1(i32 %a) {entry:br label %looploop:%i = call i32 @f2(i32 %a)%c = icmp slt i32 %i, %abr i1 %c, label %loop, label %endend:%r = phi i32 [%i, %loop], [%a, %entry]ret i32 %r}define i32 @f2(i32 %a) {%cnd = icmp slt i32 %a, 0br i1 %cnd, label %then, label %elsethen:%r1 = call i32 @a()br label %endelse:%r2 = call i32 @b()br label %endend:%r = phi i32 [%r1, %then], [%r2, %else]ret i32 %r})IR");Function *F1 = M->getFunction("f1");CallBase *CB = findCall(*F1);EXPECT_NE(CB, nullptr);FunctionPropertiesInfo ExpectedInitial;ExpectedInitial.BasicBlockCount = 3;ExpectedInitial.TotalInstructionCount = 6;ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;ExpectedInitial.Uses = 1;ExpectedInitial.DirectCallsToDefinedFunctions = 1;ExpectedInitial.MaxLoopDepth = 1;ExpectedInitial.TopLevelLoopCount = 1;FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;ExpectedFinal.BasicBlockCount = 6;ExpectedFinal.DirectCallsToDefinedFunctions = 0;ExpectedFinal.BlocksReachedFromConditionalInstruction = 4;ExpectedFinal.TotalInstructionCount = 12;auto FPI = buildFPI(*F1);EXPECT_EQ(FPI, ExpectedInitial);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(FPI, ExpectedFinal);}TEST_F(FunctionPropertiesAnalysisTest, Unreachable) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i64 @f1(i32 noundef %value) {entry:br i1 true, label %cond.true, label %cond.falsecond.true: ; preds = %entry%conv2 = sext i32 %value to i64br label %cond.endcond.false: ; preds = %entry%call3 = call noundef i64 @f2()br label %extraextra:br label %extra2extra2:br label %cond.endcond.end: ; preds = %cond.false, %cond.true%cond = phi i64 [ %conv2, %cond.true ], [ %call3, %extra ]ret i64 %cond}define i64 @f2() {entry:tail call void @llvm.trap()unreachable}declare void @llvm.trap())IR");Function *F1 = M->getFunction("f1");CallBase *CB = findCall(*F1);EXPECT_NE(CB, nullptr);FunctionPropertiesInfo ExpectedInitial;ExpectedInitial.BasicBlockCount = 6;ExpectedInitial.TotalInstructionCount = 9;ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;ExpectedInitial.Uses = 1;ExpectedInitial.DirectCallsToDefinedFunctions = 1;FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;ExpectedFinal.BasicBlockCount = 4;ExpectedFinal.DirectCallsToDefinedFunctions = 0;ExpectedFinal.TotalInstructionCount = 7;auto FPI = buildFPI(*F1);EXPECT_EQ(FPI, ExpectedInitial);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(FPI, ExpectedFinal);}TEST_F(FunctionPropertiesAnalysisTest, InvokeSkipLP) {LLVMContext C;std::unique_ptr<Module> M = makeLLVMModule(C,R"IR(target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"target triple = "x86_64-pc-linux-gnu"define i64 @f1(i32 noundef %value) {entry:invoke fastcc void @f2() to label %cont unwind label %lpadcont:ret i64 1lpad:%lp = landingpad i32 cleanupbr label %ehcleanupehcleanup:resume i32 0}define void @f2() {invoke noundef void @f3() to label %exit unwind label %lpadexit:ret voidlpad:%lp = landingpad i32 cleanupresume i32 %lp}declare void @f3())IR");// The outcome of inlining will be that lpad becomes unreachable. The landing// pad of the invoke inherited from f2 will land on a new bb which will branch// to a bb containing the body of lpad.Function *F1 = M->getFunction("f1");CallBase *CB = findCall(*F1);EXPECT_NE(CB, nullptr);FunctionPropertiesInfo ExpectedInitial;ExpectedInitial.BasicBlockCount = 4;ExpectedInitial.TotalInstructionCount = 5;ExpectedInitial.BlocksReachedFromConditionalInstruction = 0;ExpectedInitial.Uses = 1;ExpectedInitial.DirectCallsToDefinedFunctions = 1;FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;ExpectedFinal.BasicBlockCount = 6;ExpectedFinal.DirectCallsToDefinedFunctions = 0;ExpectedFinal.TotalInstructionCount = 8;auto FPI = buildFPI(*F1);EXPECT_EQ(FPI, ExpectedInitial);FunctionPropertiesUpdater FPU(FPI, *CB);InlineFunctionInfo IFI;auto IR = llvm::InlineFunction(*CB, IFI);EXPECT_TRUE(IR.isSuccess());invalidate(*F1);FPU.finish(FAM);EXPECT_EQ(FPI, ExpectedFinal);}} // end anonymous namespace
//===- DomTreeUpdaterTest.cpp - DomTreeUpdater unit tests -----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/DomTreeUpdater.h"#include "llvm/Analysis/PostDominators.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"#include <algorithm>using namespace llvm;static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,StringRef ModuleStr) {SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(ModuleStr, Err, Context);assert(M && "Bad LLVM IR?");return M;}TEST(DomTreeUpdater, EagerUpdateBasicOperations) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f(i32 %i, i32 *%p) {bb0:store i32 %i, i32 *%pswitch i32 %i, label %bb1 [i32 1, label %bb2i32 2, label %bb3]bb1:ret i32 1bb2:ret i32 2bb3:ret i32 3})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DomTreeUpdater.DominatorTree DT(*F);PostDominatorTree PDT(*F);DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);ASSERT_TRUE(DTU.hasDomTree());ASSERT_TRUE(DTU.hasPostDomTree());ASSERT_TRUE(DTU.isEager());ASSERT_FALSE(DTU.isLazy());ASSERT_TRUE(DTU.getDomTree().verify());ASSERT_TRUE(DTU.getPostDomTree().verify());ASSERT_FALSE(DTU.hasPendingUpdates());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;BasicBlock *BB2 = &*FI++;BasicBlock *BB3 = &*FI++;SwitchInst *SI = dyn_cast<SwitchInst>(BB0->getTerminator());ASSERT_NE(SI, nullptr) << "Couldn't get SwitchInst.";DTU.applyUpdatesPermissive({{DominatorTree::Insert, BB0, BB0}, {DominatorTree::Delete, BB0, BB0}});ASSERT_FALSE(DTU.hasPendingUpdates());// Delete edge bb0 -> bb3 and push the update twice to verify duplicate// entries are discarded.std::vector<DominatorTree::UpdateType> Updates;Updates.reserve(4);Updates.push_back({DominatorTree::Delete, BB0, BB3});Updates.push_back({DominatorTree::Delete, BB0, BB3});// Invalid Insert: no edge bb1 -> bb2 after change to bb0.Updates.push_back({DominatorTree::Insert, BB1, BB2});// Invalid Delete: edge exists bb0 -> bb1 after change to bb0.Updates.push_back({DominatorTree::Delete, BB0, BB1});// CFG Change: remove edge bb0 -> bb3.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);BB3->removePredecessor(BB0);for (auto i = SI->case_begin(), e = SI->case_end(); i != e; ++i) {if (i->getCaseSuccessor() == BB3) {SI->removeCase(i);break;}}EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);// Deletion of a BasicBlock is an immediate event. We remove all uses to the// contained Instructions and change the Terminator to "unreachable" when// queued for deletion.ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));DTU.applyUpdatesPermissive(Updates);ASSERT_FALSE(DTU.hasPendingUpdates());// Invalid Insert: no edge bb1 -> bb2 after change to bb0.// Invalid Delete: edge exists bb0 -> bb1 after change to bb0.DTU.applyUpdatesPermissive({{DominatorTree::Insert, BB1, BB2}, {DominatorTree::Delete, BB0, BB1}});// DTU working with Eager UpdateStrategy does not need to flush.ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());// Test callback utils.ASSERT_EQ(BB3->getParent(), F);DTU.callbackDeleteBB(BB3,[&F](BasicBlock *BB) { ASSERT_NE(BB->getParent(), F); });ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());ASSERT_FALSE(DTU.hasPendingUpdates());// Unnecessary flush() testDTU.flush();EXPECT_TRUE(DT.verify());EXPECT_TRUE(PDT.verify());// Remove all case branch to BB2 to test Eager recalculation.// Code section from llvm::ConstantFoldTerminatorfor (auto i = SI->case_begin(), e = SI->case_end(); i != e;) {if (i->getCaseSuccessor() == BB2) {// Remove this entry.BB2->removePredecessor(BB0);i = SI->removeCase(i);e = SI->case_end();} else++i;}ASSERT_FALSE(DT.verify());ASSERT_FALSE(PDT.verify());DTU.recalculate(*F);ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());}TEST(DomTreeUpdater, EagerUpdateReplaceEntryBB) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f() {bb0:br label %bb1bb1:ret i32 1})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DTU.DominatorTree DT(*F);PostDominatorTree PDT(*F);DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Eager);ASSERT_TRUE(DTU.hasDomTree());ASSERT_TRUE(DTU.hasPostDomTree());ASSERT_TRUE(DTU.isEager());ASSERT_FALSE(DTU.isLazy());ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;// Add a block as the new function entry BB. We also link it to BB0.BasicBlock *NewEntry =BasicBlock::Create(F->getContext(), "new_entry", F, BB0);BranchInst::Create(BB0, NewEntry);EXPECT_EQ(F->begin()->getName(), NewEntry->getName());EXPECT_TRUE(&F->getEntryBlock() == NewEntry);DTU.applyUpdates({{DominatorTree::Insert, NewEntry, BB0}});// Changing the Entry BB requires a full recalculation of DomTree.DTU.recalculate(*F);ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());// CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1.EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u);NewEntry->getTerminator()->eraseFromParent();BranchInst::Create(BB1, NewEntry);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);// Update the DTU. At this point bb0 now has no predecessors but is still a// Child of F.DTU.applyUpdates({{DominatorTree::Delete, NewEntry, BB0},{DominatorTree::Insert, NewEntry, BB1}});ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());// Now remove bb0 from F.ASSERT_FALSE(isa<UnreachableInst>(BB0->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB0));DTU.deleteBB(BB0);ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());}TEST(DomTreeUpdater, LazyUpdateDTBasicOperations) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f(i32 %i, i32 *%p) {bb0:store i32 %i, i32 *%pswitch i32 %i, label %bb1 [i32 0, label %bb2i32 1, label %bb2i32 2, label %bb3]bb1:ret i32 1bb2:ret i32 2bb3:ret i32 3})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DTU.DominatorTree DT(*F);PostDominatorTree *PDT = nullptr;DomTreeUpdater DTU(&DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);ASSERT_TRUE(DTU.hasDomTree());ASSERT_FALSE(DTU.hasPostDomTree());ASSERT_FALSE(DTU.isEager());ASSERT_TRUE(DTU.isLazy());ASSERT_TRUE(DTU.getDomTree().verify());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;BasicBlock *BB2 = &*FI++;BasicBlock *BB3 = &*FI++;// Test discards of self-domination update.DTU.applyUpdatesPermissive({{DominatorTree::Insert, BB0, BB0}});ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());// Delete edge bb0 -> bb3 and push the update twice to verify duplicate// entries are discarded.std::vector<DominatorTree::UpdateType> Updates;Updates.reserve(4);Updates.push_back({DominatorTree::Delete, BB0, BB3});Updates.push_back({DominatorTree::Delete, BB0, BB3});// Invalid Insert: no edge bb1 -> bb2 after change to bb0.Updates.push_back({DominatorTree::Insert, BB1, BB2});// Invalid Delete: edge exists bb0 -> bb1 after change to bb0.Updates.push_back({DominatorTree::Delete, BB0, BB1});// CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);BB0->getTerminator()->eraseFromParent();BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);// Verify. Updates to DTU must be applied *after* all changes to the CFG// (including block deletion).DTU.applyUpdatesPermissive(Updates);ASSERT_TRUE(DTU.getDomTree().verify());// Deletion of a BasicBlock is an immediate event. We remove all uses to the// contained Instructions and change the Terminator to "unreachable" when// queued for deletion. Its parent is still F until all the pending updates// are applied to all trees held by the DomTreeUpdater (DomTree/PostDomTree).// We don't defer this action because it can cause problems for other// transforms or analysis as it's part of the actual CFG. We only defer// updates to the DominatorTrees. This code will crash if it is placed before// the BranchInst::Create() call above. After a deletion of a BasicBlock. Only// an explicit flush event can trigger the flushing of deleteBBs. Because some// passes using Lazy UpdateStrategy rely on this behavior.ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));EXPECT_FALSE(DTU.hasPendingDeletedBB());DTU.deleteBB(BB3);EXPECT_TRUE(DTU.isBBPendingDeletion(BB3));EXPECT_TRUE(DTU.hasPendingDeletedBB());ASSERT_TRUE(isa<UnreachableInst>(BB3->getTerminator()));EXPECT_EQ(BB3->getParent(), F);DTU.recalculate(*F);EXPECT_FALSE(DTU.hasPendingDeletedBB());}TEST(DomTreeUpdater, LazyUpdateDTInheritedPreds) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f(i32 %i, i32 *%p) {bb0:store i32 %i, i32 *%pswitch i32 %i, label %bb1 [i32 2, label %bb2i32 3, label %bb3]bb1:br label %bb3bb2:br label %bb3bb3:ret i32 3})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DTU.DominatorTree DT(*F);PostDominatorTree *PDT = nullptr;DomTreeUpdater DTU(&DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);ASSERT_TRUE(DTU.hasDomTree());ASSERT_FALSE(DTU.hasPostDomTree());ASSERT_FALSE(DTU.isEager());ASSERT_TRUE(DTU.isLazy());ASSERT_TRUE(DTU.getDomTree().verify());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;BasicBlock *BB2 = &*FI++;BasicBlock *BB3 = &*FI++;// There are several CFG locations where we have://// pred1..predN// | |// +> curr <+ converted into: pred1..predN curr// | | |// v +> succ <+// succ//// There is a specific shape of this we have to be careful of://// pred1..predN// || |// |+> curr <+ converted into: pred1..predN curr// | | | |// | v +> succ <+// +-> succ//// While the final CFG form is functionally identical the updates to// DTU are not. In the first case we must have// DTU.applyUpdates({{DominatorTree::Insert, Pred1, Succ}}) while in// the latter case we must *NOT* have// DTU.applyUpdates({{DominatorTree::Insert, Pred1, Succ}}).// CFG Change: bb0 now only has bb0 -> bb1 and bb0 -> bb3. We are preparing to// remove bb2.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);BB0->getTerminator()->eraseFromParent();BranchInst::Create(BB1, BB3, ConstantInt::getTrue(F->getContext()), BB0);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);// Test callback utils.std::vector<BasicBlock *> BasicBlocks;BasicBlocks.push_back(BB1);BasicBlocks.push_back(BB2);auto Eraser = [&](BasicBlock *BB) {BasicBlocks.erase(std::remove_if(BasicBlocks.begin(), BasicBlocks.end(),[&](const BasicBlock *i) { return i == BB; }),BasicBlocks.end());};ASSERT_EQ(BasicBlocks.size(), static_cast<size_t>(2));// Remove bb2 from F. This has to happen before the call to// applyUpdates() for DTU to detect there is no longer an edge between// bb2 -> bb3. The deleteBB() method converts bb2's TI into "unreachable".ASSERT_FALSE(isa<UnreachableInst>(BB2->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB2));DTU.callbackDeleteBB(BB2, Eraser);EXPECT_TRUE(DTU.isBBPendingDeletion(BB2));ASSERT_TRUE(isa<UnreachableInst>(BB2->getTerminator()));EXPECT_EQ(BB2->getParent(), F);// Queue up the DTU updates.std::vector<DominatorTree::UpdateType> Updates;Updates.reserve(4);Updates.push_back({DominatorTree::Delete, BB0, BB2});Updates.push_back({DominatorTree::Delete, BB2, BB3});// Handle the specific shape case next.// CFG Change: bb0 now only branches to bb3. We are preparing to remove bb1.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);BB0->getTerminator()->eraseFromParent();BranchInst::Create(BB3, BB0);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);// Remove bb1 from F. This has to happen before the call to// applyUpdates() for DTU to detect there is no longer an edge between// bb1 -> bb3. The deleteBB() method converts bb1's TI into "unreachable".ASSERT_FALSE(isa<UnreachableInst>(BB1->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB1));DTU.callbackDeleteBB(BB1, Eraser);EXPECT_TRUE(DTU.isBBPendingDeletion(BB1));ASSERT_TRUE(isa<UnreachableInst>(BB1->getTerminator()));EXPECT_EQ(BB1->getParent(), F);// Update the DTU. In this case we don't submit {DominatorTree::Insert, BB0,// BB3} because the edge previously existed at the start of this test when DT// was first created.Updates.push_back({DominatorTree::Delete, BB0, BB1});Updates.push_back({DominatorTree::Delete, BB1, BB3});// Verify everything.DTU.applyUpdatesPermissive(Updates);ASSERT_EQ(BasicBlocks.size(), static_cast<size_t>(2));DTU.flush();ASSERT_EQ(BasicBlocks.size(), static_cast<size_t>(0));ASSERT_TRUE(DT.verify());}TEST(DomTreeUpdater, LazyUpdateBasicOperations) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f(i32 %i, i32 *%p) {bb0:store i32 %i, i32 *%pswitch i32 %i, label %bb1 [i32 0, label %bb2i32 1, label %bb2i32 2, label %bb3]bb1:ret i32 1bb2:ret i32 2bb3:ret i32 3})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DTU.DominatorTree DT(*F);PostDominatorTree PDT(*F);DomTreeUpdater DTU(&DT, &PDT, DomTreeUpdater::UpdateStrategy::Lazy);ASSERT_TRUE(DTU.hasDomTree());ASSERT_TRUE(DTU.hasPostDomTree());ASSERT_FALSE(DTU.isEager());ASSERT_TRUE(DTU.isLazy());ASSERT_TRUE(DTU.getDomTree().verify());ASSERT_TRUE(DTU.getPostDomTree().verify());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;BasicBlock *BB2 = &*FI++;BasicBlock *BB3 = &*FI++;// Test discards of self-domination update.DTU.applyUpdates({{DominatorTree::Delete, BB0, BB0}});// Delete edge bb0 -> bb3 and push the update twice to verify duplicate// entries are discarded.std::vector<DominatorTree::UpdateType> Updates;Updates.reserve(4);Updates.push_back({DominatorTree::Delete, BB0, BB3});Updates.push_back({DominatorTree::Delete, BB0, BB3});// Unnecessary Insert: no edge bb1 -> bb2 after change to bb0.Updates.push_back({DominatorTree::Insert, BB1, BB2});// Unnecessary Delete: edge exists bb0 -> bb1 after change to bb0.Updates.push_back({DominatorTree::Delete, BB0, BB1});// CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);BB0->getTerminator()->eraseFromParent();BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);// Deletion of a BasicBlock is an immediate event. We remove all uses to the// contained Instructions and change the Terminator to "unreachable" when// queued for deletion. Its parent is still F until DTU.flushDomTree is// called. We don't defer this action because it can cause problems for other// transforms or analysis as it's part of the actual CFG. We only defer// updates to the DominatorTree. This code will crash if it is placed before// the BranchInst::Create() call above.bool CallbackFlag = false;ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));DTU.callbackDeleteBB(BB3, [&](BasicBlock *) { CallbackFlag = true; });EXPECT_TRUE(DTU.isBBPendingDeletion(BB3));ASSERT_TRUE(isa<UnreachableInst>(BB3->getTerminator()));EXPECT_EQ(BB3->getParent(), F);// Verify. Updates to DTU must be applied *after* all changes to the CFG// (including block deletion).DTU.applyUpdatesPermissive(Updates);ASSERT_TRUE(DTU.getDomTree().verify());ASSERT_TRUE(DTU.hasPendingUpdates());ASSERT_TRUE(DTU.hasPendingPostDomTreeUpdates());ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());ASSERT_TRUE(DTU.hasPendingDeletedBB());ASSERT_TRUE(DTU.getPostDomTree().verify());ASSERT_FALSE(DTU.hasPendingUpdates());ASSERT_FALSE(DTU.hasPendingPostDomTreeUpdates());ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());ASSERT_FALSE(DTU.hasPendingDeletedBB());ASSERT_EQ(CallbackFlag, true);}TEST(DomTreeUpdater, LazyUpdateReplaceEntryBB) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f() {bb0:br label %bb1bb1:ret i32 1})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DTU.DominatorTree DT(*F);PostDominatorTree PDT(*F);DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);ASSERT_TRUE(DTU.hasDomTree());ASSERT_TRUE(DTU.hasPostDomTree());ASSERT_FALSE(DTU.isEager());ASSERT_TRUE(DTU.isLazy());ASSERT_TRUE(DTU.getDomTree().verify());ASSERT_TRUE(DTU.getPostDomTree().verify());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;// Add a block as the new function entry BB. We also link it to BB0.BasicBlock *NewEntry =BasicBlock::Create(F->getContext(), "new_entry", F, BB0);BranchInst::Create(BB0, NewEntry);EXPECT_EQ(F->begin()->getName(), NewEntry->getName());EXPECT_TRUE(&F->getEntryBlock() == NewEntry);// Insert the new edge between new_entry -> bb0. Without this the// recalculate() call below will not actually recalculate the DT as there// are no changes pending and no blocks deleted.DTU.applyUpdates({{DominatorTree::Insert, NewEntry, BB0}});// Changing the Entry BB requires a full recalculation.DTU.recalculate(*F);ASSERT_TRUE(DTU.getDomTree().verify());ASSERT_TRUE(DTU.getPostDomTree().verify());// CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1.EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u);NewEntry->getTerminator()->eraseFromParent();BranchInst::Create(BB1, NewEntry);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);// Update the DTU. At this point bb0 now has no predecessors but is still a// Child of F.DTU.applyUpdates({{DominatorTree::Delete, NewEntry, BB0},{DominatorTree::Insert, NewEntry, BB1}});DTU.flush();ASSERT_TRUE(DT.verify());ASSERT_TRUE(PDT.verify());// Now remove bb0 from F.ASSERT_FALSE(isa<UnreachableInst>(BB0->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB0));DTU.deleteBB(BB0);EXPECT_TRUE(DTU.isBBPendingDeletion(BB0));ASSERT_TRUE(isa<UnreachableInst>(BB0->getTerminator()));EXPECT_EQ(BB0->getParent(), F);// Perform a full recalculation of the DTU. It is not necessary here but we// do this to test the case when there are no pending DT updates but there are// pending deleted BBs.ASSERT_TRUE(DTU.hasPendingDeletedBB());DTU.recalculate(*F);ASSERT_FALSE(DTU.hasPendingDeletedBB());}TEST(DomTreeUpdater, LazyUpdateStepTest) {// This test focus on testing a DTU holding both trees applying multiple// updates and DT/PDT not flushed together.StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f(i32 %i, i32 *%p) {bb0:store i32 %i, i32 *%pswitch i32 %i, label %bb1 [i32 0, label %bb1i32 1, label %bb2i32 2, label %bb3i32 3, label %bb1]bb1:ret i32 1bb2:ret i32 2bb3:ret i32 3})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DomTreeUpdater.DominatorTree DT(*F);PostDominatorTree PDT(*F);DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);ASSERT_TRUE(DTU.hasDomTree());ASSERT_TRUE(DTU.hasPostDomTree());ASSERT_FALSE(DTU.isEager());ASSERT_TRUE(DTU.isLazy());ASSERT_TRUE(DTU.getDomTree().verify());ASSERT_TRUE(DTU.getPostDomTree().verify());ASSERT_FALSE(DTU.hasPendingUpdates());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;FI++;BasicBlock *BB2 = &*FI++;BasicBlock *BB3 = &*FI++;SwitchInst *SI = dyn_cast<SwitchInst>(BB0->getTerminator());ASSERT_NE(SI, nullptr) << "Couldn't get SwitchInst.";// Delete edge bb0 -> bb3.std::vector<DominatorTree::UpdateType> Updates;Updates.reserve(1);Updates.push_back({DominatorTree::Delete, BB0, BB3});// CFG Change: remove edge bb0 -> bb3.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 5u);BB3->removePredecessor(BB0);for (auto i = SI->case_begin(), e = SI->case_end(); i != e; ++i) {if (i->getCaseIndex() == 2) {SI->removeCase(i);break;}}EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);// Deletion of a BasicBlock is an immediate event. We remove all uses to the// contained Instructions and change the Terminator to "unreachable" when// queued for deletion.ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));EXPECT_FALSE(DTU.isBBPendingDeletion(BB3));DTU.applyUpdates(Updates);// Only flush DomTree.ASSERT_TRUE(DTU.getDomTree().verify());ASSERT_TRUE(DTU.hasPendingPostDomTreeUpdates());ASSERT_FALSE(DTU.hasPendingDomTreeUpdates());ASSERT_EQ(BB3->getParent(), F);DTU.deleteBB(BB3);Updates.clear();// Remove all case branch to BB2 to test Eager recalculation.// Code section from llvm::ConstantFoldTerminatorfor (auto i = SI->case_begin(), e = SI->case_end(); i != e;) {if (i->getCaseSuccessor() == BB2) {// Remove this entry.BB2->removePredecessor(BB0);i = SI->removeCase(i);e = SI->case_end();Updates.push_back({DominatorTree::Delete, BB0, BB2});} else++i;}DTU.applyUpdatesPermissive(Updates);// flush PostDomTreeASSERT_TRUE(DTU.getPostDomTree().verify());ASSERT_FALSE(DTU.hasPendingPostDomTreeUpdates());ASSERT_TRUE(DTU.hasPendingDomTreeUpdates());// flush both treesDTU.flush();ASSERT_TRUE(DT.verify());}TEST(DomTreeUpdater, NoTreeTest) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f() {bb0:ret i32 0})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DTU.DomTreeUpdater DTU(nullptr, nullptr, DomTreeUpdater::UpdateStrategy::Lazy);ASSERT_FALSE(DTU.hasDomTree());ASSERT_FALSE(DTU.hasPostDomTree());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;// Test whether PendingDeletedBB is flushed after the recalculation.DTU.deleteBB(BB0);ASSERT_TRUE(DTU.hasPendingDeletedBB());DTU.recalculate(*F);ASSERT_FALSE(DTU.hasPendingDeletedBB());}TEST(DomTreeUpdater, LazyUpdateDeduplicationTest) {StringRef FuncName = "f";StringRef ModuleString = R"(define i32 @f() {bb0:br label %bb1bb1:ret i32 1bb2:ret i32 1})";// Make the module.LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);Function *F = M->getFunction(FuncName);// Make the DTU.DominatorTree DT(*F);DomTreeUpdater DTU(&DT, nullptr, DomTreeUpdater::UpdateStrategy::Lazy);ASSERT_TRUE(DTU.getDomTree().verify());Function::iterator FI = F->begin();BasicBlock *BB0 = &*FI++;BasicBlock *BB1 = &*FI++;BasicBlock *BB2 = &*FI++;// CFG Change: remove bb0 -> bb1 and add back bb0 -> bb1.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);BB0->getTerminator()->eraseFromParent();BranchInst::Create(BB1, BB0);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);// Update the DTU and simulate duplicates.DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB0, BB1},{DominatorTree::Delete, BB0, BB1},{DominatorTree::Insert, BB0, BB1},{DominatorTree::Insert, BB0, BB1},{DominatorTree::Insert, BB0, BB1}});// The above operations result in a no-op.ASSERT_FALSE(DTU.hasPendingUpdates());// Update the DTU. Simulate an invalid update.DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB0, BB1}});ASSERT_FALSE(DTU.hasPendingUpdates());// CFG Change: remove bb0 -> bb1.EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);BB0->getTerminator()->eraseFromParent();// Update the DTU and simulate invalid updates.DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB0, BB1},{DominatorTree::Insert, BB0, BB1},{DominatorTree::Delete, BB0, BB1},{DominatorTree::Insert, BB0, BB1},{DominatorTree::Insert, BB0, BB1}});ASSERT_TRUE(DTU.hasPendingUpdates());// CFG Change: add bb0 -> bb2.BranchInst::Create(BB2, BB0);EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);DTU.applyUpdates({{DominatorTree::Insert, BB0, BB2}});ASSERT_TRUE(DTU.getDomTree().verify());}
//===- DivergenceAnalysisTest.cpp - DivergenceAnalysis unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallVector.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/DivergenceAnalysis.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/PostDominators.h"#include "llvm/Analysis/SyncDependenceAnalysis.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/GlobalVariable.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/Module.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"namespace llvm {namespace {BasicBlock *GetBlockByName(StringRef BlockName, Function &F) {for (auto &BB : F) {if (BB.getName() != BlockName)continue;return &BB;}return nullptr;}// We use this fixture to ensure that we clean up DivergenceAnalysisImpl before// deleting the PassManager.class DivergenceAnalysisTest : public testing::Test {protected:LLVMContext Context;Module M;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;std::unique_ptr<DominatorTree> DT;std::unique_ptr<PostDominatorTree> PDT;std::unique_ptr<LoopInfo> LI;std::unique_ptr<SyncDependenceAnalysis> SDA;DivergenceAnalysisTest() : M("", Context), TLII(), TLI(TLII) {}DivergenceAnalysisImpl buildDA(Function &F, bool IsLCSSA) {DT.reset(new DominatorTree(F));PDT.reset(new PostDominatorTree(F));LI.reset(new LoopInfo(*DT));SDA.reset(new SyncDependenceAnalysis(*DT, *PDT, *LI));return DivergenceAnalysisImpl(F, nullptr, *DT, *LI, *SDA, IsLCSSA);}void runWithDA(Module &M, StringRef FuncName, bool IsLCSSA,function_ref<void(Function &F, LoopInfo &LI, DivergenceAnalysisImpl &DA)>Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;DivergenceAnalysisImpl DA = buildDA(*F, IsLCSSA);Test(*F, *LI, DA);}};// Simple initial state testTEST_F(DivergenceAnalysisTest, DAInitialState) {IntegerType *IntTy = IntegerType::getInt32Ty(Context);FunctionType *FTy =FunctionType::get(Type::getVoidTy(Context), {IntTy}, false);Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);BasicBlock *BB = BasicBlock::Create(Context, "entry", F);ReturnInst::Create(Context, nullptr, BB);DivergenceAnalysisImpl DA = buildDA(*F, false);// Whole function regionEXPECT_EQ(DA.getRegionLoop(), nullptr);// No divergence in initial stateEXPECT_FALSE(DA.hasDetectedDivergence());// No spurious divergenceDA.compute();EXPECT_FALSE(DA.hasDetectedDivergence());// Detected divergence after markingArgument &arg = *F->arg_begin();DA.markDivergent(arg);EXPECT_TRUE(DA.hasDetectedDivergence());EXPECT_TRUE(DA.isDivergent(arg));DA.compute();EXPECT_TRUE(DA.hasDetectedDivergence());EXPECT_TRUE(DA.isDivergent(arg));}TEST_F(DivergenceAnalysisTest, DANoLCSSA) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "" ""define i32 @f_1(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "" local_unnamed_addr { ""entry: "" br label %loop.ph "" ""loop.ph: "" br label %loop "" ""loop: "" %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] "" %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] "" %iv0.inc = add i32 %iv0, 1 "" %iv1.inc = add i32 %iv1, 3 "" %cond.cont = icmp slt i32 %iv0, %n "" br i1 %cond.cont, label %loop, label %for.end.loopexit "" ""for.end.loopexit: "" ret i32 %iv0 ""} ",Err, C);Function *F = M->getFunction("f_1");DivergenceAnalysisImpl DA = buildDA(*F, false);EXPECT_FALSE(DA.hasDetectedDivergence());auto ItArg = F->arg_begin();ItArg++;auto &NArg = *ItArg;// Seed divergence in argument %nDA.markDivergent(NArg);DA.compute();EXPECT_TRUE(DA.hasDetectedDivergence());// Verify that "ret %iv.0" is divergentauto ItBlock = F->begin();std::advance(ItBlock, 3);auto &ExitBlock = *GetBlockByName("for.end.loopexit", *F);auto &RetInst = *cast<ReturnInst>(ExitBlock.begin());EXPECT_TRUE(DA.isDivergent(RetInst));}TEST_F(DivergenceAnalysisTest, DALCSSA) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "" ""define i32 @f_lcssa(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "" local_unnamed_addr { ""entry: "" br label %loop.ph "" ""loop.ph: "" br label %loop "" ""loop: "" %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] "" %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] "" %iv0.inc = add i32 %iv0, 1 "" %iv1.inc = add i32 %iv1, 3 "" %cond.cont = icmp slt i32 %iv0, %n "" br i1 %cond.cont, label %loop, label %for.end.loopexit "" ""for.end.loopexit: "" %val.ret = phi i32 [ %iv0, %loop ] "" br label %detached.return "" ""detached.return: "" ret i32 %val.ret ""} ",Err, C);Function *F = M->getFunction("f_lcssa");DivergenceAnalysisImpl DA = buildDA(*F, true);EXPECT_FALSE(DA.hasDetectedDivergence());auto ItArg = F->arg_begin();ItArg++;auto &NArg = *ItArg;// Seed divergence in argument %nDA.markDivergent(NArg);DA.compute();EXPECT_TRUE(DA.hasDetectedDivergence());// Verify that "ret %iv.0" is divergentauto ItBlock = F->begin();std::advance(ItBlock, 4);auto &ExitBlock = *GetBlockByName("detached.return", *F);auto &RetInst = *cast<ReturnInst>(ExitBlock.begin());EXPECT_TRUE(DA.isDivergent(RetInst));}TEST_F(DivergenceAnalysisTest, DAJoinDivergence) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "" ""define void @f_1(i1 %a, i1 %b, i1 %c) "" local_unnamed_addr { ""A: "" br i1 %a, label %B, label %C "" ""B: "" br i1 %b, label %C, label %D "" ""C: "" %c.join = phi i32 [ 0, %A ], [ 1, %B ] "" br i1 %c, label %D, label %E "" ""D: "" %d.join = phi i32 [ 0, %B ], [ 1, %C ] "" br label %E "" ""E: "" %e.join = phi i32 [ 0, %C ], [ 1, %D ] "" ret void ""} "" ""define void @f_2(i1 %a, i1 %b, i1 %c) "" local_unnamed_addr { ""A: "" br i1 %a, label %B, label %E "" ""B: "" br i1 %b, label %C, label %D "" ""C: "" br label %D "" ""D: "" %d.join = phi i32 [ 0, %B ], [ 1, %C ] "" br label %E "" ""E: "" %e.join = phi i32 [ 0, %A ], [ 1, %D ] "" ret void ""} "" ""define void @f_3(i1 %a, i1 %b, i1 %c)"" local_unnamed_addr { ""A: "" br i1 %a, label %B, label %C "" ""B: "" br label %C "" ""C: "" %c.join = phi i32 [ 0, %A ], [ 1, %B ] "" br i1 %c, label %D, label %E "" ""D: "" br label %E "" ""E: "" %e.join = phi i32 [ 0, %C ], [ 1, %D ] "" ret void ""} ",Err, C);// Maps divergent conditions to the basic blocks whose Phi nodes become// divergent. Blocks need to be listed in IR order.using SmallBlockVec = SmallVector<const BasicBlock *, 4>;using InducedDivJoinMap = std::map<const Value *, SmallBlockVec>;// Actual function performing the checks.auto CheckDivergenceFunc = [this](Function &F,InducedDivJoinMap &ExpectedDivJoins) {for (auto &ItCase : ExpectedDivJoins) {auto *DivVal = ItCase.first;auto DA = buildDA(F, false);DA.markDivergent(*DivVal);DA.compute();// List of basic blocks that shall host divergent Phi nodes.auto ItDivJoins = ItCase.second.begin();for (auto &BB : F) {auto *Phi = dyn_cast<PHINode>(BB.begin());if (!Phi)continue;if (ItDivJoins != ItCase.second.end() && &BB == *ItDivJoins) {EXPECT_TRUE(DA.isDivergent(*Phi));// Advance to next block with expected divergent PHI node.++ItDivJoins;} else {EXPECT_FALSE(DA.isDivergent(*Phi));}}}};{auto *F = M->getFunction("f_1");auto ItBlocks = F->begin();ItBlocks++; // Skip AItBlocks++; // Skip Bauto *C = &*ItBlocks++;auto *D = &*ItBlocks++;auto *E = &*ItBlocks;auto ItArg = F->arg_begin();auto *AArg = &*ItArg++;auto *BArg = &*ItArg++;auto *CArg = &*ItArg;InducedDivJoinMap DivJoins;DivJoins.emplace(AArg, SmallBlockVec({C, D, E}));DivJoins.emplace(BArg, SmallBlockVec({D, E}));DivJoins.emplace(CArg, SmallBlockVec({E}));CheckDivergenceFunc(*F, DivJoins);}{auto *F = M->getFunction("f_2");auto ItBlocks = F->begin();ItBlocks++; // Skip AItBlocks++; // Skip BItBlocks++; // Skip Cauto *D = &*ItBlocks++;auto *E = &*ItBlocks;auto ItArg = F->arg_begin();auto *AArg = &*ItArg++;auto *BArg = &*ItArg++;auto *CArg = &*ItArg;InducedDivJoinMap DivJoins;DivJoins.emplace(AArg, SmallBlockVec({E}));DivJoins.emplace(BArg, SmallBlockVec({D}));DivJoins.emplace(CArg, SmallBlockVec({}));CheckDivergenceFunc(*F, DivJoins);}{auto *F = M->getFunction("f_3");auto ItBlocks = F->begin();ItBlocks++; // Skip AItBlocks++; // Skip Bauto *C = &*ItBlocks++;ItBlocks++; // Skip Dauto *E = &*ItBlocks;auto ItArg = F->arg_begin();auto *AArg = &*ItArg++;auto *BArg = &*ItArg++;auto *CArg = &*ItArg;InducedDivJoinMap DivJoins;DivJoins.emplace(AArg, SmallBlockVec({C}));DivJoins.emplace(BArg, SmallBlockVec({}));DivJoins.emplace(CArg, SmallBlockVec({E}));CheckDivergenceFunc(*F, DivJoins);}}TEST_F(DivergenceAnalysisTest, DASwitchUnreachableDefault) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString("target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "" ""define void @switch_unreachable_default(i32 %cond) local_unnamed_addr { ""entry: "" switch i32 %cond, label %sw.default [ "" i32 0, label %sw.bb0 "" i32 1, label %sw.bb1 "" ] "" ""sw.bb0: "" br label %sw.epilog "" ""sw.bb1: "" br label %sw.epilog "" ""sw.default: "" unreachable "" ""sw.epilog: "" %div.dbl = phi double [ 0.0, %sw.bb0], [ -1.0, %sw.bb1 ] "" ret void ""}",Err, C);auto *F = M->getFunction("switch_unreachable_default");auto &CondArg = *F->arg_begin();auto DA = buildDA(*F, false);EXPECT_FALSE(DA.hasDetectedDivergence());DA.markDivergent(CondArg);DA.compute();// Still %CondArg is divergent.EXPECT_TRUE(DA.hasDetectedDivergence());// The join uni.dbl is not divergent (see D52221)auto &ExitBlock = *GetBlockByName("sw.epilog", *F);auto &DivDblPhi = *cast<PHINode>(ExitBlock.begin());EXPECT_TRUE(DA.isDivergent(DivDblPhi));}} // end anonymous namespace} // end namespace llvm
//===- DDGTest.cpp - DDGAnalysis unit tests -------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/DDG.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/BasicAliasAnalysis.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/ScalarEvolution.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;/// Build the DDG analysis for a loop and run the given test \p Test.static void runTest(Module &M, StringRef FuncName,function_ref<void(Function &F, LoopInfo &LI,DependenceInfo &DI, ScalarEvolution &SE)>Test) {auto *F = M.getFunction(FuncName);ASSERT_NE(F, nullptr) << "Could not find " << FuncName;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI(TLII);AssumptionCache AC(*F);DominatorTree DT(*F);LoopInfo LI(DT);ScalarEvolution SE(*F, TLI, AC, DT, LI);AAResults AA(TLI);DependenceInfo DI(F, &AA, &SE, &LI);Test(*F, LI, DI, SE);}static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,const char *ModuleStr) {SMDiagnostic Err;return parseAssemblyString(ModuleStr, Err, Context);}TEST(DDGTest, getDependencies) {const char *ModuleStr ="target datalayout = \"e-m:e-i64:64-n32:64\"\n""target triple = \"powerpc64le-unknown-linux-gnu\"\n""\n""define dso_local void @foo(i32 signext %n, i32* noalias %A, i32* ""noalias %B) {\n""entry:\n"" %cmp1 = icmp sgt i32 %n, 0\n"" br i1 %cmp1, label %for.body.preheader, label %for.end\n""\n""for.body.preheader:\n"" %wide.trip.count = zext i32 %n to i64\n"" br label %for.body\n"" \n"" for.body:\n"" %indvars.iv = phi i64 [ 0, %for.body.preheader ], [ ""%indvars.iv.next, %for.body ]\n"" %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv\n"" %0 = trunc i64 %indvars.iv to i32\n"" store i32 %0, i32* %arrayidx, align 4\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 ""%indvars.iv.next\n"" %1 = load i32, i32* %arrayidx2, align 4\n"" %add3 = add nsw i32 %1, 1\n"" %arrayidx5 = getelementptr inbounds i32, i32* %B, i64 %indvars.iv\n"" store i32 %add3, i32* %arrayidx5, align 4\n"" %exitcond = icmp ne i64 %indvars.iv.next, %wide.trip.count\n"" br i1 %exitcond, label %for.body, label %for.end.loopexit\n""\n""for.end.loopexit:\n"" br label %for.end\n""\n""for.end:\n"" ret void\n""}\n";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runTest(*M, "foo",[&](Function &F, LoopInfo &LI, DependenceInfo &DI, ScalarEvolution &SE) {Loop *L = *LI.begin();assert(L && "expected the loop to be identified.");DataDependenceGraph DDG(*L, LI, DI);// Collect all the nodes that have an outgoing memory edge// while collecting all memory edges as well. There should// only be one node with an outgoing memory edge and there// should only be one memory edge in the entire graph.std::vector<DDGNode *> DependenceSourceNodes;std::vector<DDGEdge *> MemoryEdges;for (DDGNode *N : DDG) {for (DDGEdge *E : *N) {bool SourceAdded = false;if (E->isMemoryDependence()) {MemoryEdges.push_back(E);if (!SourceAdded) {DependenceSourceNodes.push_back(N);SourceAdded = true;}}}}EXPECT_EQ(DependenceSourceNodes.size(), 1ull);EXPECT_EQ(MemoryEdges.size(), 1ull);DataDependenceGraph::DependenceList DL;DDG.getDependencies(*DependenceSourceNodes.back(),MemoryEdges.back()->getTargetNode(), DL);EXPECT_EQ(DL.size(), 1ull);EXPECT_TRUE(DL.back()->isAnti());EXPECT_EQ(DL.back()->getLevels(), 1u);EXPECT_NE(DL.back()->getDistance(1), nullptr);EXPECT_EQ(DL.back()->getDistance(1),SE.getOne(DL.back()->getDistance(1)->getType()));});}/// Test to make sure that when pi-blocks are formed, multiple edges of the same/// kind and direction are collapsed into a single edge./// In the test below, %loadASubI belongs to an outside node, which has input/// dependency with multiple load instructions in the pi-block containing/// %loadBSubI. We expect a single memory dependence edge from the outside node/// to this pi-block. The pi-block also contains %add and %add7 both of which/// feed a phi in an outside node. We expect a single def-use edge from the/// pi-block to the node containing that phi.TEST(DDGTest, avoidDuplicateEdgesToFromPiBlocks) {const char *ModuleStr ="target datalayout = \"e-m:e-i64:64-n32:64-v256:256:256-v512:512:512\"\n""\n""define void @foo(float* noalias %A, float* noalias %B, float* noalias ""%C, float* noalias %D, i32 signext %n) {\n""entry:\n"" %cmp1 = icmp sgt i32 %n, 0\n"" br i1 %cmp1, label %for.body.preheader, label %for.end\n""\n""for.body.preheader: ; preds = %entry\n"" %wide.trip.count = zext i32 %n to i64\n"" br label %for.body\n""\n""for.body: ; preds = ""%for.body.preheader, %if.end\n"" %indvars.iv = phi i64 [ 0, %for.body.preheader ], [ %indvars.iv.next, ""%if.end ]\n"" %arrayidx = getelementptr inbounds float, float* %A, i64 %indvars.iv\n"" %loadASubI = load float, float* %arrayidx, align 4\n"" %arrayidx2 = getelementptr inbounds float, float* %B, i64 ""%indvars.iv\n"" %loadBSubI = load float, float* %arrayidx2, align 4\n"" %add = fadd fast float %loadASubI, %loadBSubI\n"" %arrayidx4 = getelementptr inbounds float, float* %A, i64 ""%indvars.iv\n"" store float %add, float* %arrayidx4, align 4\n"" %arrayidx6 = getelementptr inbounds float, float* %A, i64 ""%indvars.iv\n"" %0 = load float, float* %arrayidx6, align 4\n"" %add7 = fadd fast float %0, 1.000000e+00\n"" %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"" %arrayidx10 = getelementptr inbounds float, float* %B, i64 ""%indvars.iv.next\n"" store float %add7, float* %arrayidx10, align 4\n"" %arrayidx12 = getelementptr inbounds float, float* %A, i64 ""%indvars.iv\n"" %1 = load float, float* %arrayidx12, align 4\n"" %cmp13 = fcmp fast ogt float %1, 1.000000e+02\n"" br i1 %cmp13, label %if.then, label %if.else\n""\n""if.then: ; preds = %for.body\n"" br label %if.end\n""\n""if.else: ; preds = %for.body\n"" br label %if.end\n""\n""if.end: ; preds = %if.else, ""%if.then\n"" %ff.0 = phi float [ %add, %if.then ], [ %add7, %if.else ]\n"" store float %ff.0, float* %C, align 4\n"" %exitcond = icmp ne i64 %indvars.iv.next, %wide.trip.count\n"" br i1 %exitcond, label %for.body, label %for.end.loopexit\n""\n""for.end.loopexit: ; preds = %if.end\n"" br label %for.end\n""\n""for.end: ; preds = ""%for.end.loopexit, %entry\n"" ret void\n""}\n";LLVMContext Context;std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);runTest(*M, "foo",[&](Function &F, LoopInfo &LI, DependenceInfo &DI, ScalarEvolution &SE) {Loop *L = *LI.begin();assert(L && "expected the loop to be identified.");DataDependenceGraph DDG(*L, LI, DI);const DDGNode *LoadASubI = nullptr;for (DDGNode *N : DDG) {if (!isa<SimpleDDGNode>(N))continue;SmallVector<Instruction *, 8> IList;N->collectInstructions([](const Instruction *I) { return true; },IList);if (llvm::any_of(IList, [](Instruction *I) {return I->getName() == "loadASubI";})) {LoadASubI = N;break;}}assert(LoadASubI && "Did not find load of A[i]");const PiBlockDDGNode *PiBlockWithBSubI = nullptr;for (DDGNode *N : DDG) {if (!isa<PiBlockDDGNode>(N))continue;for (DDGNode *M : cast<PiBlockDDGNode>(N)->getNodes()) {SmallVector<Instruction *, 8> IList;M->collectInstructions([](const Instruction *I) { return true; },IList);if (llvm::any_of(IList, [](Instruction *I) {return I->getName() == "loadBSubI";})) {PiBlockWithBSubI = static_cast<PiBlockDDGNode *>(N);break;}}if (PiBlockWithBSubI)break;}assert(PiBlockWithBSubI &&"Did not find pi-block containing load of B[i]");const DDGNode *FFPhi = nullptr;for (DDGNode *N : DDG) {if (!isa<SimpleDDGNode>(N))continue;SmallVector<Instruction *, 8> IList;N->collectInstructions([](const Instruction *I) { return true; },IList);if (llvm::any_of(IList, [](Instruction *I) {return I->getName() == "ff.0";})) {FFPhi = N;break;}}assert(FFPhi && "Did not find ff.0 phi instruction");// Expect a single memory edge from '%0 = A[i]' to the pi-block. This// means the duplicate incoming memory edges are removed during pi-block// formation.SmallVector<DDGEdge *, 4> EL;LoadASubI->findEdgesTo(*PiBlockWithBSubI, EL);unsigned NumMemoryEdges = llvm::count_if(EL, [](DDGEdge *Edge) { return Edge->isMemoryDependence(); });EXPECT_EQ(NumMemoryEdges, 1ull);/// Expect a single def-use edge from the pi-block to '%ff.0 = phi...`./// This means the duplicate outgoing def-use edges are removed during/// pi-block formation.EL.clear();PiBlockWithBSubI->findEdgesTo(*FFPhi, EL);NumMemoryEdges =llvm::count_if(EL, [](DDGEdge *Edge) { return Edge->isDefUse(); });EXPECT_EQ(NumMemoryEdges, 1ull);});}
//===--- ConstraintSystemTests.cpp ----------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/ConstraintSystem.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ConstraintSolverTest, TestSolutionChecks) {{ConstraintSystem CS;// x + y <= 10, x >= 5, y >= 6, x <= 10, y <= 10CS.addVariableRow({10, 1, 1});CS.addVariableRow({-5, -1, 0});CS.addVariableRow({-6, 0, -1});CS.addVariableRow({10, 1, 0});CS.addVariableRow({10, 0, 1});EXPECT_FALSE(CS.mayHaveSolution());}{ConstraintSystem CS;// x + y <= 10, x >= 2, y >= 3, x <= 10, y <= 10CS.addVariableRow({10, 1, 1});CS.addVariableRow({-2, -1, 0});CS.addVariableRow({-3, 0, -1});CS.addVariableRow({10, 1, 0});CS.addVariableRow({10, 0, 1});EXPECT_TRUE(CS.mayHaveSolution());}{ConstraintSystem CS;// x + y <= 10, 10 >= x, 10 >= y; does not have a solution.CS.addVariableRow({10, 1, 1});CS.addVariableRow({-10, -1, 0});CS.addVariableRow({-10, 0, -1});EXPECT_FALSE(CS.mayHaveSolution());}{ConstraintSystem CS;// x + y >= 20, 10 >= x, 10 >= y; does HAVE a solution.CS.addVariableRow({-20, -1, -1});CS.addVariableRow({-10, -1, 0});CS.addVariableRow({-10, 0, -1});EXPECT_TRUE(CS.mayHaveSolution());}{ConstraintSystem CS;// 2x + y + 3z <= 10, 2x + y >= 10, y >= 1CS.addVariableRow({10, 2, 1, 3});CS.addVariableRow({-10, -2, -1, 0});CS.addVariableRow({-1, 0, 0, -1});EXPECT_FALSE(CS.mayHaveSolution());}{ConstraintSystem CS;// 2x + y + 3z <= 10, 2x + y >= 10CS.addVariableRow({10, 2, 1, 3});CS.addVariableRow({-10, -2, -1, 0});EXPECT_TRUE(CS.mayHaveSolution());}}TEST(ConstraintSolverTest, IsConditionImplied) {{// For the test below, we assume we know// x <= 5 && y <= 3ConstraintSystem CS;CS.addVariableRow({5, 1, 0});CS.addVariableRow({3, 0, 1});// x + y <= 6 does not hold.EXPECT_FALSE(CS.isConditionImplied({6, 1, 1}));// x + y <= 7 does not hold.EXPECT_FALSE(CS.isConditionImplied({7, 1, 1}));// x + y <= 8 does hold.EXPECT_TRUE(CS.isConditionImplied({8, 1, 1}));// 2 * x + y <= 12 does hold.EXPECT_FALSE(CS.isConditionImplied({12, 2, 1}));// 2 * x + y <= 13 does hold.EXPECT_TRUE(CS.isConditionImplied({13, 2, 1}));// x + y <= 12 does hold.EXPECT_FALSE(CS.isConditionImplied({12, 2, 1}));// 2 * x + y <= 13 does hold.EXPECT_TRUE(CS.isConditionImplied({13, 2, 1}));// x <= y == x - y <= 0 does not hold.EXPECT_FALSE(CS.isConditionImplied({0, 1, -1}));// y <= x == -x + y <= 0 does not hold.EXPECT_FALSE(CS.isConditionImplied({0, -1, 1}));}{// For the test below, we assume we know// x + 1 <= y + 1 == x - y <= 0ConstraintSystem CS;CS.addVariableRow({0, 1, -1});// x <= y == x - y <= 0 does hold.EXPECT_TRUE(CS.isConditionImplied({0, 1, -1}));// y <= x == -x + y <= 0 does not hold.EXPECT_FALSE(CS.isConditionImplied({0, -1, 1}));// x <= y + 10 == x - y <= 10 does hold.EXPECT_TRUE(CS.isConditionImplied({10, 1, -1}));// x + 10 <= y == x - y <= -10 does NOT hold.EXPECT_FALSE(CS.isConditionImplied({-10, 1, -1}));}{// For the test below, we assume we know// x <= y == x - y <= 0// y <= z == y - x <= 0ConstraintSystem CS;CS.addVariableRow({0, 1, -1, 0});CS.addVariableRow({0, 0, 1, -1});// z <= y == -y + z <= 0 does not hold.EXPECT_FALSE(CS.isConditionImplied({0, 0, -1, 1}));// x <= z == x - z <= 0 does hold.EXPECT_TRUE(CS.isConditionImplied({0, 1, 0, -1}));}}TEST(ConstraintSolverTest, IsConditionImpliedOverflow) {ConstraintSystem CS;// Make sure isConditionImplied returns false when there is an overflow.int64_t Limit = std::numeric_limits<int64_t>::max();CS.addVariableRow({Limit - 1, Limit - 2, Limit - 3});EXPECT_FALSE(CS.isConditionImplied({Limit - 1, Limit - 2, Limit - 3}));}} // namespace
//=======- CaptureTrackingTest.cpp - Unit test for the Capture Tracking ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/CaptureTracking.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;TEST(CaptureTracking, MaxUsesToExplore) {StringRef Assembly = R"(; Function Attrs: nounwind ssp uwtabledeclare void @doesnt_capture(i8* nocapture, i8* nocapture, i8* nocapture,i8* nocapture, i8* nocapture); %arg has 5 usesdefine void @test_few_uses(i8* %arg) {call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)ret void}; %arg has 50 usesdefine void @test_many_uses(i8* %arg) {call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)call void @doesnt_capture(i8* %arg, i8* %arg, i8* %arg, i8* %arg, i8* %arg)ret void})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";auto Test = [&M](const char *FName, unsigned FalseMaxUsesLimit,unsigned TrueMaxUsesLimit) {Function *F = M->getFunction(FName);ASSERT_NE(F, nullptr);Value *Arg = &*F->arg_begin();ASSERT_NE(Arg, nullptr);ASSERT_FALSE(PointerMayBeCaptured(Arg, true, true, FalseMaxUsesLimit));ASSERT_TRUE(PointerMayBeCaptured(Arg, true, true, TrueMaxUsesLimit));BasicBlock *EntryBB = &F->getEntryBlock();DominatorTree DT(*F);Instruction *Ret = EntryBB->getTerminator();ASSERT_TRUE(isa<ReturnInst>(Ret));ASSERT_FALSE(PointerMayBeCapturedBefore(Arg, true, true, Ret, &DT, false,FalseMaxUsesLimit));ASSERT_TRUE(PointerMayBeCapturedBefore(Arg, true, true, Ret, &DT, false,TrueMaxUsesLimit));};Test("test_few_uses", 6, 4);Test("test_many_uses", 50, 30);}struct CollectingCaptureTracker : public CaptureTracker {SmallVector<const Use *, 4> Captures;void tooManyUses() override { }bool captured(const Use *U) override {Captures.push_back(U);return false;}};TEST(CaptureTracking, MultipleUsesInSameInstruction) {StringRef Assembly = R"(declare void @call(i8*, i8*, i8*)define void @test(i8* %arg, i8** %ptr) {call void @call(i8* %arg, i8* nocapture %arg, i8* %arg) [ "bundle"(i8* %arg) ]cmpxchg i8** %ptr, i8* %arg, i8* %arg acq_rel monotonicicmp eq i8* %arg, %argret void})";LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";Function *F = M->getFunction("test");Value *Arg = &*F->arg_begin();BasicBlock *BB = &F->getEntryBlock();Instruction *Call = &*BB->begin();Instruction *CmpXChg = Call->getNextNode();Instruction *ICmp = CmpXChg->getNextNode();CollectingCaptureTracker CT;PointerMayBeCaptured(Arg, &CT);EXPECT_EQ(7u, CT.Captures.size());// Call arg 1EXPECT_EQ(Call, CT.Captures[0]->getUser());EXPECT_EQ(0u, CT.Captures[0]->getOperandNo());// Call arg 3EXPECT_EQ(Call, CT.Captures[1]->getUser());EXPECT_EQ(2u, CT.Captures[1]->getOperandNo());// Operand bundle argEXPECT_EQ(Call, CT.Captures[2]->getUser());EXPECT_EQ(3u, CT.Captures[2]->getOperandNo());// Cmpxchg compare operandEXPECT_EQ(CmpXChg, CT.Captures[3]->getUser());EXPECT_EQ(1u, CT.Captures[3]->getOperandNo());// Cmpxchg new value operandEXPECT_EQ(CmpXChg, CT.Captures[4]->getUser());EXPECT_EQ(2u, CT.Captures[4]->getOperandNo());// ICmp first operandEXPECT_EQ(ICmp, CT.Captures[5]->getUser());EXPECT_EQ(0u, CT.Captures[5]->getOperandNo());// ICmp second operandEXPECT_EQ(ICmp, CT.Captures[6]->getUser());EXPECT_EQ(1u, CT.Captures[6]->getOperandNo());}
//=======- CallGraphTest.cpp - Unit tests for the CG analysis -------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/CallGraph.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "gtest/gtest.h"using namespace llvm;namespace {template <typename Ty> void canSpecializeGraphTraitsIterators(Ty *G) {typedef typename GraphTraits<Ty *>::NodeRef NodeRef;auto I = GraphTraits<Ty *>::nodes_begin(G);auto E = GraphTraits<Ty *>::nodes_end(G);auto X = ++I;// Should be able to iterate over all nodes of the graph.static_assert(std::is_same<decltype(*I), NodeRef>::value,"Node type does not match");static_assert(std::is_same<decltype(*X), NodeRef>::value,"Node type does not match");static_assert(std::is_same<decltype(*E), NodeRef>::value,"Node type does not match");NodeRef N = GraphTraits<Ty *>::getEntryNode(G);auto S = GraphTraits<NodeRef>::child_begin(N);auto F = GraphTraits<NodeRef>::child_end(N);// Should be able to iterate over immediate successors of a node.static_assert(std::is_same<decltype(*S), NodeRef>::value,"Node type does not match");static_assert(std::is_same<decltype(*F), NodeRef>::value,"Node type does not match");}TEST(CallGraphTest, GraphTraitsSpecialization) {LLVMContext Context;Module M("", Context);CallGraph CG(M);canSpecializeGraphTraitsIterators(&CG);}TEST(CallGraphTest, GraphTraitsConstSpecialization) {LLVMContext Context;Module M("", Context);CallGraph CG(M);canSpecializeGraphTraitsIterators(const_cast<const CallGraph *>(&CG));}}
set(LLVM_LINK_COMPONENTSAnalysisAsmParserCoreSupportTransformUtils)set(MLGO_TESTS TFUtilsTest.cpp)if (DEFINED LLVM_HAVE_TF_API)LIST(APPEND EXTRA_TESTS ${MLGO_TESTS})else()LIST(APPEND LLVM_OPTIONAL_SOURCES ${MLGO_TESTS})endif()add_llvm_unittest_with_input_files(AnalysisTestsAliasAnalysisTest.cppAliasSetTrackerTest.cppAssumeBundleQueriesTest.cppBasicAliasAnalysisTest.cppBlockFrequencyInfoTest.cppBranchProbabilityInfoTest.cppCallGraphTest.cppCaptureTrackingTest.cppCFGTest.cppCGSCCPassManagerTest.cppConstraintSystemTest.cppDDGTest.cppDivergenceAnalysisTest.cppDomTreeUpdaterTest.cppGlobalsModRefTest.cppFunctionPropertiesAnalysisTest.cppInlineCostTest.cppIRSimilarityIdentifierTest.cppIVDescriptorsTest.cppLazyCallGraphTest.cppLoadsTest.cppLoopInfoTest.cppLoopNestTest.cppMemoryBuiltinsTest.cppMemoryProfileInfoTest.cppMemorySSATest.cppMLModelRunnerTest.cppPhiValuesTest.cppProfileSummaryInfoTest.cppScalarEvolutionTest.cppVectorFunctionABITest.cppSparsePropagation.cppTargetLibraryInfoTest.cppTensorSpecTest.cppTBAATest.cppUnrollAnalyzerTest.cppValueLatticeTest.cppValueTrackingTest.cppVectorUtilsTest.cpp${EXTRA_TESTS})target_link_libraries(AnalysisTests PRIVATE LLVMTestingSupport)
//===- CGSCCPassManagerTest.cpp -------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/CGSCCPassManager.h"#include "llvm/Analysis/LazyCallGraph.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Function.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/IR/PassManager.h"#include "llvm/IR/Verifier.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Transforms/Utils/CallGraphUpdater.h"#include "gtest/gtest.h"using namespace llvm;namespace {class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> {public:struct Result {Result(int Count) : FunctionCount(Count) {}int FunctionCount;bool invalidate(Module &, const PreservedAnalyses &PA,ModuleAnalysisManager::Invalidator &) {// Check whether the analysis or all analyses on modules have been// preserved.auto PAC = PA.getChecker<TestModuleAnalysis>();return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Module>>());}};TestModuleAnalysis(int &Runs) : Runs(Runs) {}Result run(Module &M, ModuleAnalysisManager &AM) {++Runs;return Result(M.size());}private:friend AnalysisInfoMixin<TestModuleAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestModuleAnalysis::Key;class TestSCCAnalysis : public AnalysisInfoMixin<TestSCCAnalysis> {public:struct Result {Result(int Count) : FunctionCount(Count) {}int FunctionCount;bool invalidate(LazyCallGraph::SCC &, const PreservedAnalyses &PA,CGSCCAnalysisManager::Invalidator &) {// Check whether the analysis or all analyses on SCCs have been// preserved.auto PAC = PA.getChecker<TestSCCAnalysis>();return !(PAC.preserved() ||PAC.preservedSet<AllAnalysesOn<LazyCallGraph::SCC>>());}};TestSCCAnalysis(int &Runs) : Runs(Runs) {}Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &) {++Runs;return Result(C.size());}private:friend AnalysisInfoMixin<TestSCCAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestSCCAnalysis::Key;class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> {public:struct Result {Result(int Count) : InstructionCount(Count) {}int InstructionCount;bool invalidate(Function &, const PreservedAnalyses &PA,FunctionAnalysisManager::Invalidator &) {// Check whether the analysis or all analyses on functions have been// preserved.auto PAC = PA.getChecker<TestFunctionAnalysis>();return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>());}};TestFunctionAnalysis(int &Runs) : Runs(Runs) {}Result run(Function &F, FunctionAnalysisManager &AM) {++Runs;int Count = 0;for (Instruction &I : instructions(F)) {(void)I;++Count;}return Result(Count);}private:friend AnalysisInfoMixin<TestFunctionAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestFunctionAnalysis::Key;class TestImmutableFunctionAnalysis: public AnalysisInfoMixin<TestImmutableFunctionAnalysis> {public:struct Result {bool invalidate(Function &, const PreservedAnalyses &,FunctionAnalysisManager::Invalidator &) {return false;}};TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {}Result run(Function &F, FunctionAnalysisManager &AM) {++Runs;return Result();}private:friend AnalysisInfoMixin<TestImmutableFunctionAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestImmutableFunctionAnalysis::Key;struct LambdaModulePass : public PassInfoMixin<LambdaModulePass> {template <typename T>LambdaModulePass(T &&Arg) : Func(std::forward<T>(Arg)) {}PreservedAnalyses run(Module &F, ModuleAnalysisManager &AM) {return Func(F, AM);}std::function<PreservedAnalyses(Module &, ModuleAnalysisManager &)> Func;};struct LambdaSCCPass : public PassInfoMixin<LambdaSCCPass> {template <typename T> LambdaSCCPass(T &&Arg) : Func(std::forward<T>(Arg)) {}PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &UR) {return Func(C, AM, CG, UR);}std::function<PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &,LazyCallGraph &, CGSCCUpdateResult &)>Func;};struct LambdaFunctionPass : public PassInfoMixin<LambdaFunctionPass> {template <typename T>LambdaFunctionPass(T &&Arg) : Func(std::forward<T>(Arg)) {}PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {return Func(F, AM);}std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func;};std::unique_ptr<Module> parseIR(const char *IR) {// We just use a static context here. This is never called from multiple// threads so it is harmless no matter how it is implemented. We just need// the context to outlive the module which it does.static LLVMContext C;SMDiagnostic Err;return parseAssemblyString(IR, Err, C);}class CGSCCPassManagerTest : public ::testing::Test {protected:LLVMContext Context;FunctionAnalysisManager FAM;CGSCCAnalysisManager CGAM;ModuleAnalysisManager MAM;std::unique_ptr<Module> M;public:CGSCCPassManagerTest(): FAM(), CGAM(), MAM(),M(parseIR(// Define a module with the following call graph, where calls go// out the bottom of nodes and enter the top://// f// |\ _// | \ / |// g h1 |// | | |// | h2 |// | | |// | h3 |// | / \_/// |/// x//"define void @x() {\n""entry:\n"" ret void\n""}\n""define void @h3() {\n""entry:\n"" call void @h1()\n"" ret void\n""}\n""define void @h2() {\n""entry:\n"" call void @h3()\n"" call void @x()\n"" ret void\n""}\n""define void @h1() {\n""entry:\n"" call void @h2()\n"" ret void\n""}\n""define void @g() {\n""entry:\n"" call void @g()\n"" call void @x()\n"" ret void\n""}\n""define void @f() {\n""entry:\n"" call void @g()\n"" call void @h1()\n"" ret void\n""}\n")) {FAM.registerPass([&] { return TargetLibraryAnalysis(); });MAM.registerPass([&] { return LazyCallGraphAnalysis(); });MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });// Register required pass instrumentation analysis.MAM.registerPass([&] { return PassInstrumentationAnalysis(); });CGAM.registerPass([&] { return PassInstrumentationAnalysis(); });FAM.registerPass([&] { return PassInstrumentationAnalysis(); });// Cross-register proxies.MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); });CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(); });CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); });FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); });FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });}};TEST_F(CGSCCPassManagerTest, Basic) {int FunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });int ImmutableFunctionAnalysisRuns = 0;FAM.registerPass([&] {return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns);});int SCCAnalysisRuns = 0;CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });int ModuleAnalysisRuns = 0;MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });ModulePassManager MPM;MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());CGSCCPassManager CGPM1;FunctionPassManager FPM1;int FunctionPassRunCount1 = 0;FPM1.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) {++FunctionPassRunCount1;return PreservedAnalyses::none();}));CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));int SCCPassRunCount1 = 0;int AnalyzedInstrCount1 = 0;int AnalyzedSCCFunctionCount1 = 0;int AnalyzedModuleFunctionCount1 = 0;CGPM1.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &UR) {++SCCPassRunCount1;// Note: The proper way to get to a module pass from a CGSCC pass is// through the ModuleAnalysisManagerCGSCCProxy:// ```// const auto &MAMProxy =// AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG);// ```// However getting a stateful analysis is incorrect usage, and the call// to getCachedResult below asserts:// ```// if (TestModuleAnalysis::Result *TMA =// MAMProxy.getCachedResult<TestModuleAnalysis>(// *C.begin()->getFunction().getParent()))// AnalyzedModuleFunctionCount1 += TMA->FunctionCount;// ```// For the purposes of this unittest, use the above MAM directly.if (TestModuleAnalysis::Result *TMA =MAM.getCachedResult<TestModuleAnalysis>(*C.begin()->getFunction().getParent()))AnalyzedModuleFunctionCount1 += TMA->FunctionCount;FunctionAnalysisManager &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C, CG);AnalyzedSCCFunctionCount1 += AR.FunctionCount;for (LazyCallGraph::Node &N : C) {TestFunctionAnalysis::Result &FAR =FAM.getResult<TestFunctionAnalysis>(N.getFunction());AnalyzedInstrCount1 += FAR.InstructionCount;// Just ensure we get the immutable results.(void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction());}return PreservedAnalyses::all();}));FunctionPassManager FPM2;int FunctionPassRunCount2 = 0;FPM2.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) {++FunctionPassRunCount2;return PreservedAnalyses::none();}));CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));FunctionPassManager FPM3;int FunctionPassRunCount3 = 0;FPM3.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) {++FunctionPassRunCount3;return PreservedAnalyses::none();}));MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM3)));MPM.run(*M, MAM);EXPECT_EQ(4, SCCPassRunCount1);EXPECT_EQ(6, FunctionPassRunCount1);EXPECT_EQ(6, FunctionPassRunCount2);EXPECT_EQ(6, FunctionPassRunCount3);EXPECT_EQ(1, ModuleAnalysisRuns);EXPECT_EQ(4, SCCAnalysisRuns);EXPECT_EQ(6, FunctionAnalysisRuns);EXPECT_EQ(6, ImmutableFunctionAnalysisRuns);EXPECT_EQ(14, AnalyzedInstrCount1);EXPECT_EQ(6, AnalyzedSCCFunctionCount1);EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1);}// Test that an SCC pass which fails to preserve a module analysis does in fact// invalidate that module analysis.TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesModuleAnalysis) {int ModuleAnalysisRuns = 0;MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });ModulePassManager MPM;MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());// The first CGSCC run we preserve everything and make sure that works and// the module analysis is available in the second CGSCC run from the one// required module pass above.CGSCCPassManager CGPM1;int CountFoundModuleAnalysis1 = 0;CGPM1.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C,CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {const auto &MAMProxy = AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG);if (MAMProxy.cachedResultExists<TestModuleAnalysis>(*C.begin()->getFunction().getParent()))++CountFoundModuleAnalysis1;return PreservedAnalyses::all();}));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// The second CGSCC run checks that the module analysis got preserved the// previous time and in one SCC fails to preserve it.CGSCCPassManager CGPM2;int CountFoundModuleAnalysis2 = 0;CGPM2.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &UR) {const auto &MAMProxy =AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG);if (MAMProxy.cachedResultExists<TestModuleAnalysis>(*C.begin()->getFunction().getParent()))++CountFoundModuleAnalysis2;// Only fail to preserve analyses on one SCC and make sure that gets// propagated.return C.getName() == "(g)" ? PreservedAnalyses::none(): PreservedAnalyses::all();}));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));// The third CGSCC run should fail to find a cached module analysis as it// should have been invalidated by the above CGSCC run.CGSCCPassManager CGPM3;int CountFoundModuleAnalysis3 = 0;CGPM3.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C,CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {const auto &MAMProxy = AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG);if (MAMProxy.cachedResultExists<TestModuleAnalysis>(*C.begin()->getFunction().getParent()))++CountFoundModuleAnalysis3;return PreservedAnalyses::none();}));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3)));MPM.run(*M, MAM);EXPECT_EQ(1, ModuleAnalysisRuns);EXPECT_EQ(4, CountFoundModuleAnalysis1);EXPECT_EQ(4, CountFoundModuleAnalysis2);EXPECT_EQ(0, CountFoundModuleAnalysis3);}// Similar to the above, but test that this works for function passes embedded// *within* a CGSCC layer.TEST_F(CGSCCPassManagerTest, TestFunctionPassInsideCGSCCInvalidatesModuleAnalysis) {int ModuleAnalysisRuns = 0;MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });ModulePassManager MPM;MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());// The first run we preserve everything and make sure that works and the// module analysis is available in the second run from the one required// module pass above.FunctionPassManager FPM1;// Start true and mark false if we ever failed to find a module analysis// because we expect this to succeed for each SCC.bool FoundModuleAnalysis1 = true;FPM1.addPass(LambdaFunctionPass([&](Function &F,FunctionAnalysisManager &AM) {const auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);if (!MAMProxy.cachedResultExists<TestModuleAnalysis>(*F.getParent()))FoundModuleAnalysis1 = false;return PreservedAnalyses::all();}));CGSCCPassManager CGPM1;CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// The second run checks that the module analysis got preserved the previous// time and in one function fails to preserve it.FunctionPassManager FPM2;// Again, start true and mark false if we ever failed to find a module analysis// because we expect this to succeed for each SCC.bool FoundModuleAnalysis2 = true;FPM2.addPass(LambdaFunctionPass([&](Function &F,FunctionAnalysisManager &AM) {const auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);if (!MAMProxy.cachedResultExists<TestModuleAnalysis>(*F.getParent()))FoundModuleAnalysis2 = false;// Only fail to preserve analyses on one SCC and make sure that gets// propagated.return F.getName() == "h2" ? PreservedAnalyses::none(): PreservedAnalyses::all();}));CGSCCPassManager CGPM2;CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));// The third run should fail to find a cached module analysis as it should// have been invalidated by the above run.FunctionPassManager FPM3;// Start false and mark true if we ever *succeeded* to find a module// analysis, as we expect this to fail for every function.bool FoundModuleAnalysis3 = false;FPM3.addPass(LambdaFunctionPass([&](Function &F,FunctionAnalysisManager &AM) {const auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);if (MAMProxy.cachedResultExists<TestModuleAnalysis>(*F.getParent()))FoundModuleAnalysis3 = true;return PreservedAnalyses::none();}));CGSCCPassManager CGPM3;CGPM3.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM3)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3)));MPM.run(*M, MAM);EXPECT_EQ(1, ModuleAnalysisRuns);EXPECT_TRUE(FoundModuleAnalysis1);EXPECT_TRUE(FoundModuleAnalysis2);EXPECT_FALSE(FoundModuleAnalysis3);}// Test that a Module pass which fails to preserve an SCC analysis in fact// invalidates that analysis.TEST_F(CGSCCPassManagerTest, TestModulePassInvalidatesSCCAnalysis) {int SCCAnalysisRuns = 0;CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });ModulePassManager MPM;// First force the analysis to be run.CGSCCPassManager CGPM1;CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// Now run a module pass that preserves the LazyCallGraph and the proxy but// not the SCC analysis.MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) {PreservedAnalyses PA;PA.preserve<LazyCallGraphAnalysis>();PA.preserve<CGSCCAnalysisManagerModuleProxy>();PA.preserve<FunctionAnalysisManagerModuleProxy>();return PA;}));// And now a second CGSCC run which requires the SCC analysis again. This// will trigger re-running it.CGSCCPassManager CGPM2;CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.run(*M, MAM);// Two runs and four SCCs.EXPECT_EQ(2 * 4, SCCAnalysisRuns);}// Check that marking the SCC analysis preserved is sufficient to avoid// invaliadtion. This should only run the analysis once for each SCC.TEST_F(CGSCCPassManagerTest, TestModulePassCanPreserveSCCAnalysis) {int SCCAnalysisRuns = 0;CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });ModulePassManager MPM;// First force the analysis to be run.CGSCCPassManager CGPM1;CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// Now run a module pass that preserves each of the necessary components// (but not everything).MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) {PreservedAnalyses PA;PA.preserve<LazyCallGraphAnalysis>();PA.preserve<CGSCCAnalysisManagerModuleProxy>();PA.preserve<FunctionAnalysisManagerModuleProxy>();PA.preserve<TestSCCAnalysis>();return PA;}));// And now a second CGSCC run which requires the SCC analysis again but find// it in the cache.CGSCCPassManager CGPM2;CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.run(*M, MAM);// Four SCCsEXPECT_EQ(4, SCCAnalysisRuns);}// Check that even when the analysis is preserved, if the SCC information isn't// we still nuke things because the SCC keys could change.TEST_F(CGSCCPassManagerTest, TestModulePassInvalidatesSCCAnalysisOnCGChange) {int SCCAnalysisRuns = 0;CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });ModulePassManager MPM;// First force the analysis to be run.CGSCCPassManager CGPM1;CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// Now run a module pass that preserves the analysis but not the call// graph or proxy.MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) {PreservedAnalyses PA;PA.preserve<TestSCCAnalysis>();return PA;}));// And now a second CGSCC run which requires the SCC analysis again.CGSCCPassManager CGPM2;CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.run(*M, MAM);// Two runs and four SCCs.EXPECT_EQ(2 * 4, SCCAnalysisRuns);}// Test that an SCC pass which fails to preserve a Function analysis in fact// invalidates that analysis.TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesFunctionAnalysis) {int FunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });// Create a very simple module with a single function and SCC to make testing// these issues much easier.std::unique_ptr<Module> M = parseIR("declare void @g()\n""declare void @h()\n""define void @f() {\n""entry:\n"" call void @g()\n"" call void @h()\n"" ret void\n""}\n");CGSCCPassManager CGPM;// First force the analysis to be run.FunctionPassManager FPM1;FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));// Now run a module pass that preserves the LazyCallGraph and proxy but not// the SCC analysis.CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &,LazyCallGraph &, CGSCCUpdateResult &) {PreservedAnalyses PA;PA.preserve<LazyCallGraphAnalysis>();return PA;}));// And now a second CGSCC run which requires the SCC analysis again. This// will trigger re-running it.FunctionPassManager FPM2;FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2)));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);EXPECT_EQ(2, FunctionAnalysisRuns);}// Check that marking the SCC analysis preserved is sufficient. This should// only run the analysis once the SCC.TEST_F(CGSCCPassManagerTest, TestSCCPassCanPreserveFunctionAnalysis) {int FunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });// Create a very simple module with a single function and SCC to make testing// these issues much easier.std::unique_ptr<Module> M = parseIR("declare void @g()\n""declare void @h()\n""define void @f() {\n""entry:\n"" call void @g()\n"" call void @h()\n"" ret void\n""}\n");CGSCCPassManager CGPM;// First force the analysis to be run.FunctionPassManager FPM1;FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));// Now run a module pass that preserves each of the necessary components// (but// not everything).CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &,LazyCallGraph &, CGSCCUpdateResult &) {PreservedAnalyses PA;PA.preserve<LazyCallGraphAnalysis>();PA.preserve<FunctionAnalysisManagerCGSCCProxy>();PA.preserve<TestFunctionAnalysis>();return PA;}));// And now a second CGSCC run which requires the SCC analysis again but find// it in the cache.FunctionPassManager FPM2;FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2)));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);EXPECT_EQ(1, FunctionAnalysisRuns);}// Note that there is no test for invalidating the call graph or other// structure with an SCC pass because there is no mechanism to do that from// withinsuch a pass. Instead, such a pass has to directly update the call// graph structure.// Test that a madule pass invalidates function analyses when the CGSCC proxies// and pass manager.TEST_F(CGSCCPassManagerTest,TestModulePassInvalidatesFunctionAnalysisNestedInCGSCC) {MAM.registerPass([&] { return LazyCallGraphAnalysis(); });int FunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });ModulePassManager MPM;// First force the analysis to be run.FunctionPassManager FPM1;FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGSCCPassManager CGPM1;CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// Now run a module pass that preserves the LazyCallGraph and proxies but not// the Function analysis.MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) {PreservedAnalyses PA;PA.preserve<LazyCallGraphAnalysis>();PA.preserve<CGSCCAnalysisManagerModuleProxy>();PA.preserve<FunctionAnalysisManagerCGSCCProxy>();PA.preserve<FunctionAnalysisManagerModuleProxy>();return PA;}));// And now a second CGSCC run which requires the SCC analysis again. This// will trigger re-running it.FunctionPassManager FPM2;FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGSCCPassManager CGPM2;CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.run(*M, MAM);// Two runs and 6 functions.EXPECT_EQ(2 * 6, FunctionAnalysisRuns);}// Check that by marking the function pass and proxies as preserved, this// propagates all the way through.TEST_F(CGSCCPassManagerTest,TestModulePassCanPreserveFunctionAnalysisNestedInCGSCC) {MAM.registerPass([&] { return LazyCallGraphAnalysis(); });int FunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });ModulePassManager MPM;// First force the analysis to be run.FunctionPassManager FPM1;FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGSCCPassManager CGPM1;CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// Now run a module pass that preserves the LazyCallGraph, the proxy, and// the Function analysis.MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) {PreservedAnalyses PA;PA.preserve<LazyCallGraphAnalysis>();PA.preserve<CGSCCAnalysisManagerModuleProxy>();PA.preserve<FunctionAnalysisManagerCGSCCProxy>();PA.preserve<FunctionAnalysisManagerModuleProxy>();PA.preserve<TestFunctionAnalysis>();return PA;}));// And now a second CGSCC run which requires the SCC analysis again. This// will trigger re-running it.FunctionPassManager FPM2;FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGSCCPassManager CGPM2;CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.run(*M, MAM);// One run and 6 functions.EXPECT_EQ(6, FunctionAnalysisRuns);}// Check that if the lazy call graph itself isn't preserved we still manage to// invalidate everything.TEST_F(CGSCCPassManagerTest,TestModulePassInvalidatesFunctionAnalysisNestedInCGSCCOnCGChange) {MAM.registerPass([&] { return LazyCallGraphAnalysis(); });int FunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });ModulePassManager MPM;// First force the analysis to be run.FunctionPassManager FPM1;FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGSCCPassManager CGPM1;CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));// Now run a module pass that preserves the LazyCallGraph but not the// Function analysis.MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) {PreservedAnalyses PA;return PA;}));// And now a second CGSCC run which requires the SCC analysis again. This// will trigger re-running it.FunctionPassManager FPM2;FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>());CGSCCPassManager CGPM2;CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.run(*M, MAM);// Two runs and 6 functions.EXPECT_EQ(2 * 6, FunctionAnalysisRuns);}/// A test CGSCC-level analysis pass which caches in its result another/// analysis pass and uses it to serve queries. This requires the result to/// invalidate itself when its dependency is invalidated.////// FIXME: Currently this doesn't also depend on a function analysis, and if it/// did we would fail to invalidate it correctly.struct TestIndirectSCCAnalysis: public AnalysisInfoMixin<TestIndirectSCCAnalysis> {struct Result {Result(TestSCCAnalysis::Result &SCCDep, TestModuleAnalysis::Result &MDep): SCCDep(SCCDep), MDep(MDep) {}TestSCCAnalysis::Result &SCCDep;TestModuleAnalysis::Result &MDep;bool invalidate(LazyCallGraph::SCC &C, const PreservedAnalyses &PA,CGSCCAnalysisManager::Invalidator &Inv) {auto PAC = PA.getChecker<TestIndirectSCCAnalysis>();return !(PAC.preserved() ||PAC.preservedSet<AllAnalysesOn<LazyCallGraph::SCC>>()) ||Inv.invalidate<TestSCCAnalysis>(C, PA);}};TestIndirectSCCAnalysis(int &Runs, ModuleAnalysisManager &MAM): Runs(Runs), MAM(MAM) {}/// Run the analysis pass over the function and return a result.Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG) {++Runs;auto &SCCDep = AM.getResult<TestSCCAnalysis>(C, CG);auto &ModuleProxy = AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG);// For the test, we insist that the module analysis starts off in the// cache. Getting a cached result that isn't stateless triggers an assert.// auto &MDep = *ModuleProxy.getCachedResult<TestModuleAnalysis>(// *C.begin()->getFunction().getParent());// Use MAM, for the purposes of this unittest.auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*C.begin()->getFunction().getParent());// Register the dependency as module analysis dependencies have to be// pre-registered on the proxy.ModuleProxy.registerOuterAnalysisInvalidation<TestModuleAnalysis,TestIndirectSCCAnalysis>();return Result(SCCDep, MDep);}private:friend AnalysisInfoMixin<TestIndirectSCCAnalysis>;static AnalysisKey Key;int &Runs;ModuleAnalysisManager &MAM;};AnalysisKey TestIndirectSCCAnalysis::Key;/// A test analysis pass which caches in its result the result from the above/// indirect analysis pass.////// This allows us to ensure that whenever an analysis pass is invalidated due/// to dependencies (especially dependencies across IR units that trigger/// asynchronous invalidation) we correctly detect that this may in turn cause/// other analysis to be invalidated.struct TestDoublyIndirectSCCAnalysis: public AnalysisInfoMixin<TestDoublyIndirectSCCAnalysis> {struct Result {Result(TestIndirectSCCAnalysis::Result &IDep) : IDep(IDep) {}TestIndirectSCCAnalysis::Result &IDep;bool invalidate(LazyCallGraph::SCC &C, const PreservedAnalyses &PA,CGSCCAnalysisManager::Invalidator &Inv) {auto PAC = PA.getChecker<TestDoublyIndirectSCCAnalysis>();return !(PAC.preserved() ||PAC.preservedSet<AllAnalysesOn<LazyCallGraph::SCC>>()) ||Inv.invalidate<TestIndirectSCCAnalysis>(C, PA);}};TestDoublyIndirectSCCAnalysis(int &Runs) : Runs(Runs) {}/// Run the analysis pass over the function and return a result.Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG) {++Runs;auto &IDep = AM.getResult<TestIndirectSCCAnalysis>(C, CG);return Result(IDep);}private:friend AnalysisInfoMixin<TestDoublyIndirectSCCAnalysis>;static AnalysisKey Key;int &Runs;};AnalysisKey TestDoublyIndirectSCCAnalysis::Key;/// A test analysis pass which caches results from three different IR unit/// layers and requires intermediate layers to correctly propagate the entire/// distance.struct TestIndirectFunctionAnalysis: public AnalysisInfoMixin<TestIndirectFunctionAnalysis> {struct Result {Result(TestFunctionAnalysis::Result &FDep, TestModuleAnalysis::Result &MDep,TestSCCAnalysis::Result &SCCDep): FDep(FDep), MDep(MDep), SCCDep(SCCDep) {}TestFunctionAnalysis::Result &FDep;TestModuleAnalysis::Result &MDep;TestSCCAnalysis::Result &SCCDep;bool invalidate(Function &F, const PreservedAnalyses &PA,FunctionAnalysisManager::Invalidator &Inv) {auto PAC = PA.getChecker<TestIndirectFunctionAnalysis>();return !(PAC.preserved() ||PAC.preservedSet<AllAnalysesOn<Function>>()) ||Inv.invalidate<TestFunctionAnalysis>(F, PA);}};TestIndirectFunctionAnalysis(int &Runs, ModuleAnalysisManager &MAM,CGSCCAnalysisManager &CGAM): Runs(Runs), MAM(MAM), CGAM(CGAM) {}/// Run the analysis pass over the function and return a result.Result run(Function &F, FunctionAnalysisManager &AM) {++Runs;auto &FDep = AM.getResult<TestFunctionAnalysis>(F);auto &ModuleProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);// For the test, we insist that the module analysis starts off in the// cache. Getting a cached result that isn't stateless triggers an assert.// Use MAM, for the purposes of this unittest.auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*F.getParent());// Register the dependency as module analysis dependencies have to be// pre-registered on the proxy.ModuleProxy.registerOuterAnalysisInvalidation<TestModuleAnalysis, TestIndirectFunctionAnalysis>();// For the test we assume this is run inside a CGSCC pass manager.// Use MAM, for the purposes of this unittest.const LazyCallGraph &CG =*MAM.getCachedResult<LazyCallGraphAnalysis>(*F.getParent());auto &CGSCCProxy = AM.getResult<CGSCCAnalysisManagerFunctionProxy>(F);// For the test, we insist that the CGSCC analysis starts off in the cache.// Getting a cached result that isn't stateless triggers an assert.// Use CGAM, for the purposes of this unittest.auto &SCCDep =*CGAM.getCachedResult<TestSCCAnalysis>(*CG.lookupSCC(*CG.lookup(F)));// Register the dependency as CGSCC analysis dependencies have to be// pre-registered on the proxy.CGSCCProxy.registerOuterAnalysisInvalidation<TestSCCAnalysis, TestIndirectFunctionAnalysis>();return Result(FDep, MDep, SCCDep);}private:friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>;static AnalysisKey Key;int &Runs;ModuleAnalysisManager &MAM;CGSCCAnalysisManager &CGAM;};AnalysisKey TestIndirectFunctionAnalysis::Key;TEST_F(CGSCCPassManagerTest, TestIndirectAnalysisInvalidation) {int ModuleAnalysisRuns = 0;MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });int SCCAnalysisRuns = 0, IndirectSCCAnalysisRuns = 0,DoublyIndirectSCCAnalysisRuns = 0;CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });CGAM.registerPass([&] { return TestIndirectSCCAnalysis(IndirectSCCAnalysisRuns, MAM); });CGAM.registerPass([&] {return TestDoublyIndirectSCCAnalysis(DoublyIndirectSCCAnalysisRuns);});int FunctionAnalysisRuns = 0, IndirectFunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });FAM.registerPass([&] {return TestIndirectFunctionAnalysis(IndirectFunctionAnalysisRuns, MAM,CGAM);});ModulePassManager MPM;int FunctionCount = 0;CGSCCPassManager CGPM;// First just use the analysis to get the function count and preserve// everything.CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG);auto &IndirectResult = DoublyIndirectResult.IDep;FunctionCount += IndirectResult.SCCDep.FunctionCount;return PreservedAnalyses::all();}));CGPM.addPass(createCGSCCToFunctionPassAdaptor(RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>()));// Next, invalidate// - both analyses for the (f) and (x) SCCs,// - just the underlying (indirect) analysis for (g) SCC, and// - just the direct analysis for (h1,h2,h3) SCC.CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG);auto &IndirectResult = DoublyIndirectResult.IDep;FunctionCount += IndirectResult.SCCDep.FunctionCount;auto PA = PreservedAnalyses::none();PA.preserve<FunctionAnalysisManagerCGSCCProxy>();PA.preserveSet<AllAnalysesOn<Function>>();if (C.getName() == "(g)")PA.preserve<TestSCCAnalysis>();else if (C.getName() == "(h3, h1, h2)")PA.preserve<TestIndirectSCCAnalysis>();return PA;}));// Finally, use the analysis again on each SCC (and function), forcing// re-computation for all of them.CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG);auto &IndirectResult = DoublyIndirectResult.IDep;FunctionCount += IndirectResult.SCCDep.FunctionCount;return PreservedAnalyses::all();}));CGPM.addPass(createCGSCCToFunctionPassAdaptor(RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>()));// Create a second CGSCC pass manager. This will cause the module-level// invalidation to occur, which will force yet another invalidation of the// indirect SCC-level analysis as the module analysis it depends on gets// invalidated.CGSCCPassManager CGPM2;CGPM2.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &) {auto &DoublyIndirectResult =AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG);auto &IndirectResult = DoublyIndirectResult.IDep;FunctionCount += IndirectResult.SCCDep.FunctionCount;return PreservedAnalyses::all();}));CGPM2.addPass(createCGSCCToFunctionPassAdaptor(RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>()));// Add a requires pass to populate the module analysis and then our CGSCC// pass pipeline.MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));// Now require the module analysis again (it will have been invalidated once)// and then use it again from our second CGSCC pipeline..MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.run(*M, MAM);// There are generally two possible runs for each of the four SCCs. But// for one SCC, we only invalidate the indirect analysis so the base one// only gets run seven times.EXPECT_EQ(7, SCCAnalysisRuns);// The module analysis pass should be run twice here.EXPECT_EQ(2, ModuleAnalysisRuns);// The indirect analysis is invalidated (either directly or indirectly) three// times for each of four SCCs.EXPECT_EQ(3 * 4, IndirectSCCAnalysisRuns);EXPECT_EQ(3 * 4, DoublyIndirectSCCAnalysisRuns);// We run the indirect function analysis once per function the first time.// Then we re-run it for every SCC but "(g)". Then we re-run it for every// function again.EXPECT_EQ(6 + 5 + 6, IndirectFunctionAnalysisRuns);// Four passes count each of six functions once (via SCCs).EXPECT_EQ(4 * 6, FunctionCount);}TEST_F(CGSCCPassManagerTest, TestAnalysisInvalidationCGSCCUpdate) {int ModuleAnalysisRuns = 0;MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });int SCCAnalysisRuns = 0, IndirectSCCAnalysisRuns = 0,DoublyIndirectSCCAnalysisRuns = 0;CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });CGAM.registerPass([&] { return TestIndirectSCCAnalysis(IndirectSCCAnalysisRuns, MAM); });CGAM.registerPass([&] {return TestDoublyIndirectSCCAnalysis(DoublyIndirectSCCAnalysisRuns);});int FunctionAnalysisRuns = 0, IndirectFunctionAnalysisRuns = 0;FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });FAM.registerPass([&] {return TestIndirectFunctionAnalysis(IndirectFunctionAnalysisRuns, MAM,CGAM);});ModulePassManager MPM;CGSCCPassManager CGPM;// First just use the analysis to get the function count and preserve// everything.using RequireTestIndirectFunctionAnalysisPass =RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>;using RequireTestDoublyIndirectSCCAnalysisPass =RequireAnalysisPass<TestDoublyIndirectSCCAnalysis, LazyCallGraph::SCC,CGSCCAnalysisManager, LazyCallGraph &,CGSCCUpdateResult &>;CGPM.addPass(RequireTestDoublyIndirectSCCAnalysisPass());CGPM.addPass(createCGSCCToFunctionPassAdaptor(RequireTestIndirectFunctionAnalysisPass()));// Next, we inject an SCC pass that invalidates everything for the `(h3, h1,// h2)` SCC but also deletes the call edge from `h2` to `h3` and updates the// CG. This should successfully invalidate (and force to be re-run) all the// analyses for that SCC and for the functions.CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &UR) {(void)AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG);if (C.getName() != "(h3, h1, h2)")return PreservedAnalyses::all();// Build the preserved set.auto PA = PreservedAnalyses::none();PA.preserve<FunctionAnalysisManagerCGSCCProxy>();PA.preserve<TestIndirectSCCAnalysis>();PA.preserve<TestDoublyIndirectSCCAnalysis>();// Delete the call from `h2` to `h3`.auto &H2N = *llvm::find_if(C, [](LazyCallGraph::Node &N) { return N.getName() == "h2"; });auto &H2F = H2N.getFunction();auto &H3F = *cast<CallInst>(H2F.begin()->begin())->getCalledFunction();assert(H3F.getName() == "h3" && "Wrong called function!");H2F.begin()->begin()->eraseFromParent();// Insert a bitcast of `h3` so that we retain a ref edge to it.(void)CastInst::CreatePointerCast(&H3F,Type::getInt8PtrTy(H2F.getContext()),"dummy", &*H2F.begin()->begin());// Now update the call graph.auto &NewC =updateCGAndAnalysisManagerForFunctionPass(CG, C, H2N, AM, UR, FAM);assert(&NewC != &C && "Should get a new SCC due to update!");(void)&NewC;return PA;}));// Now use the analysis again on each SCC and function, forcing// re-computation for all of them.CGPM.addPass(RequireTestDoublyIndirectSCCAnalysisPass());CGPM.addPass(createCGSCCToFunctionPassAdaptor(RequireTestIndirectFunctionAnalysisPass()));// Create another CGSCC pipeline that requires all the analyses again.CGSCCPassManager CGPM2;CGPM2.addPass(RequireTestDoublyIndirectSCCAnalysisPass());CGPM2.addPass(createCGSCCToFunctionPassAdaptor(RequireTestIndirectFunctionAnalysisPass()));// Next we inject an SCC pass that finds the `(h2)` SCC, adds a call to `h3`// back to `h2`, and then invalidates everything for what will then be the// `(h3, h1, h2)` SCC again.CGSCCPassManager CGPM3;CGPM3.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &UR) {(void)AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG);if (C.getName() != "(h2)")return PreservedAnalyses::all();// Build the preserved set.auto PA = PreservedAnalyses::none();PA.preserve<FunctionAnalysisManagerCGSCCProxy>();PA.preserve<TestIndirectSCCAnalysis>();PA.preserve<TestDoublyIndirectSCCAnalysis>();// Delete the bitcast of `h3` that we added earlier.auto &H2N = *C.begin();auto &H2F = H2N.getFunction();auto &H3F = *cast<Function>(cast<BitCastInst>(H2F.begin()->begin())->getOperand(0));assert(H3F.getName() == "h3" && "Wrong called function!");H2F.begin()->begin()->eraseFromParent();// And insert a call to `h3`.(void)CallInst::Create(&H3F, {}, "", &*H2F.begin()->begin());// Now update the call graph.auto &NewC =updateCGAndAnalysisManagerForFunctionPass(CG, C, H2N, AM, UR, FAM);assert(&NewC != &C && "Should get a new SCC due to update!");(void)&NewC;return PA;}));// Now use the analysis again on each SCC and function, forcing// re-computation for all of them.CGPM3.addPass(RequireTestDoublyIndirectSCCAnalysisPass());CGPM3.addPass(createCGSCCToFunctionPassAdaptor(RequireTestIndirectFunctionAnalysisPass()));// Create a second CGSCC pass manager. This will cause the module-level// invalidation to occur, which will force yet another invalidation of the// indirect SCC-level analysis as the module analysis it depends on gets// invalidated.CGSCCPassManager CGPM4;CGPM4.addPass(RequireTestDoublyIndirectSCCAnalysisPass());CGPM4.addPass(createCGSCCToFunctionPassAdaptor(RequireTestIndirectFunctionAnalysisPass()));// Add a requires pass to populate the module analysis and then one of our// CGSCC pipelines. Repeat for all four CGSCC pipelines.MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3)));MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM4)));MPM.run(*M, MAM);// We run over four SCCs the first time. But then we split an SCC into three.// And then we merge those three back into one. However, this also// invalidates all three SCCs further down in the PO walk.EXPECT_EQ(4 + 3 + 3, SCCAnalysisRuns);// The module analysis pass should be run three times.EXPECT_EQ(3, ModuleAnalysisRuns);// We run over four SCCs the first time. Then over the two new ones. Then the// entire module is invalidated causing a full run over all seven. Then we// fold three SCCs back to one, re-compute for it and the two SCCs above it// in the graph, and then run over the whole module again.EXPECT_EQ(4 + 2 + 7 + 3 + 4, IndirectSCCAnalysisRuns);EXPECT_EQ(4 + 2 + 7 + 3 + 4, DoublyIndirectSCCAnalysisRuns);// First we run over all six functions. Then we re-run it over three when we// split their SCCs. Then we re-run over the whole module. Then we re-run// over three functions merged back into a single SCC, then those three// functions again, the two functions in SCCs above it in the graph, and then// over the whole module again.EXPECT_EQ(6 + 3 + 6 + 3 + 2 + 6, FunctionAnalysisRuns);// Re run the function analysis over the entire module, and then re-run it// over the `(h3, h1, h2)` SCC due to invalidation. Then we re-run it over// the entire module, then the three functions merged back into a single SCC,// those three functions again, then the two functions in SCCs above it in// the graph, and then over the whole module.EXPECT_EQ(6 + 3 + 6 + 3 + 2 + 6, IndirectFunctionAnalysisRuns);}// The (negative) tests below check for assertions so we only run them if NDEBUG// is not defined.#ifndef NDEBUGstruct LambdaSCCPassNoPreserve : public PassInfoMixin<LambdaSCCPassNoPreserve> {template <typename T>LambdaSCCPassNoPreserve(T &&Arg) : Func(std::forward<T>(Arg)) {}PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &UR) {Func(C, AM, CG, UR);PreservedAnalyses PA;// We update the core CGSCC data structures and so can preserve the proxy to// the function analysis manager.PA.preserve<FunctionAnalysisManagerCGSCCProxy>();return PA;}std::function<void(LazyCallGraph::SCC &, CGSCCAnalysisManager &,LazyCallGraph &, CGSCCUpdateResult &)>Func;};TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses0) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(h3, h1, h2)")return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();Function *FnX = M->getFunction("x");Function *FnH1 = M->getFunction("h1");Function *FnH2 = M->getFunction("h2");Function *FnH3 = M->getFunction("h3");ASSERT_NE(FnX, nullptr);ASSERT_NE(FnH1, nullptr);ASSERT_NE(FnH2, nullptr);ASSERT_NE(FnH3, nullptr);// And insert a call to `h1`, `h2`, and `h3`.Instruction *IP = &FnH2->getEntryBlock().front();(void)CallInst::Create(FnH1, {}, "", IP);(void)CallInst::Create(FnH2, {}, "", IP);(void)CallInst::Create(FnH3, {}, "", IP);auto &H2N = *llvm::find_if(C, [](LazyCallGraph::Node &N) { return N.getName() == "h2"; });ASSERT_NO_FATAL_FAILURE(updateCGAndAnalysisManagerForCGSCCPass(CG, C, H2N, AM, UR, FAM));}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses1) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C,CGSCCAnalysisManager &AM,LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(h3, h1, h2)")return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();Function *FnX = M->getFunction("x");Function *FnH1 = M->getFunction("h1");Function *FnH2 = M->getFunction("h2");Function *FnH3 = M->getFunction("h3");ASSERT_NE(FnX, nullptr);ASSERT_NE(FnH1, nullptr);ASSERT_NE(FnH2, nullptr);ASSERT_NE(FnH3, nullptr);// And insert a call to `h1`, `h2`, and `h3`.Instruction *IP = &FnH2->getEntryBlock().front();(void)CallInst::Create(FnH1, {}, "", IP);(void)CallInst::Create(FnH2, {}, "", IP);(void)CallInst::Create(FnH3, {}, "", IP);auto &H2N = *llvm::find_if(C, [](LazyCallGraph::Node &N) { return N.getName() == "h2"; });ASSERT_DEATH(updateCGAndAnalysisManagerForFunctionPass(CG, C, H2N, AM, UR, FAM),"Any new calls should be modeled as");}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses2) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(f)")return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();Function *FnF = M->getFunction("f");Function *FnH2 = M->getFunction("h2");ASSERT_NE(FnF, nullptr);ASSERT_NE(FnH2, nullptr);// And insert a call to `h2`Instruction *IP = &FnF->getEntryBlock().front();(void)CallInst::Create(FnH2, {}, "", IP);auto &FN = *llvm::find_if(C, [](LazyCallGraph::Node &N) { return N.getName() == "f"; });ASSERT_NO_FATAL_FAILURE(updateCGAndAnalysisManagerForCGSCCPass(CG, C, FN, AM, UR, FAM));}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses3) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C,CGSCCAnalysisManager &AM,LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(f)")return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();Function *FnF = M->getFunction("f");Function *FnH2 = M->getFunction("h2");ASSERT_NE(FnF, nullptr);ASSERT_NE(FnH2, nullptr);// And insert a call to `h2`Instruction *IP = &FnF->getEntryBlock().front();(void)CallInst::Create(FnH2, {}, "", IP);auto &FN = *llvm::find_if(C, [](LazyCallGraph::Node &N) { return N.getName() == "f"; });ASSERT_DEATH(updateCGAndAnalysisManagerForFunctionPass(CG, C, FN, AM, UR, FAM),"Any new calls should be modeled as");}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses4) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(f)")return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();Function *FnF = M->getFunction("f");Function *FnewF = Function::Create(FnF->getFunctionType(),FnF->getLinkage(), "newF", *M);BasicBlock *BB = BasicBlock::Create(FnewF->getContext(), "", FnewF);ReturnInst::Create(FnewF->getContext(), BB);// And insert a call to `newF`Instruction *IP = &FnF->getEntryBlock().front();(void)CallInst::Create(FnewF, {}, "", IP);// Use the CallGraphUpdater to update the call graph for the new// function.CallGraphUpdater CGU;CGU.initialize(CG, C, AM, UR);CGU.registerOutlinedFunction(*FnF, *FnewF);auto &FN = *llvm::find_if(C, [](LazyCallGraph::Node &N) { return N.getName() == "f"; });ASSERT_NO_FATAL_FAILURE(updateCGAndAnalysisManagerForCGSCCPass(CG, C, FN, AM, UR, FAM));}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses5) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C,CGSCCAnalysisManager &AM,LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(f)")return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();Function *FnF = M->getFunction("f");Function *FnewF =Function::Create(FnF->getFunctionType(), FnF->getLinkage(), "newF", *M);BasicBlock *BB = BasicBlock::Create(FnewF->getContext(), "", FnewF);ReturnInst::Create(FnewF->getContext(), BB);// Use the CallGraphUpdater to update the call graph for the new// function.CallGraphUpdater CGU;CGU.initialize(CG, C, AM, UR);// And insert a call to `newF`Instruction *IP = &FnF->getEntryBlock().front();(void)CallInst::Create(FnewF, {}, "", IP);auto &FN = *llvm::find_if(C, [](LazyCallGraph::Node &N) { return N.getName() == "f"; });ASSERT_DEATH(updateCGAndAnalysisManagerForCGSCCPass(CG, C, FN, AM, UR, FAM),"should already have an associated node");}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses6) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(h3, h1, h2)")return;Function *FnX = M->getFunction("x");Function *FnH1 = M->getFunction("h1");Function *FnH2 = M->getFunction("h2");Function *FnH3 = M->getFunction("h3");ASSERT_NE(FnX, nullptr);ASSERT_NE(FnH1, nullptr);ASSERT_NE(FnH2, nullptr);ASSERT_NE(FnH3, nullptr);// And insert a call to `h1`, `h2`, and `h3`.Instruction *IP = &FnH2->getEntryBlock().front();(void)CallInst::Create(FnH1, {}, "", IP);(void)CallInst::Create(FnH2, {}, "", IP);(void)CallInst::Create(FnH3, {}, "", IP);// Use the CallGraphUpdater to update the call graph for the new// function.CallGraphUpdater CGU;CGU.initialize(CG, C, AM, UR);ASSERT_NO_FATAL_FAILURE(CGU.reanalyzeFunction(*FnH2));}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses7) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(f)")return;Function *FnF = M->getFunction("f");Function *FnH2 = M->getFunction("h2");ASSERT_NE(FnF, nullptr);ASSERT_NE(FnH2, nullptr);// And insert a call to `h2`Instruction *IP = &FnF->getEntryBlock().front();(void)CallInst::Create(FnH2, {}, "", IP);// Use the CallGraphUpdater to update the call graph for the new// function.CallGraphUpdater CGU;CGU.initialize(CG, C, AM, UR);ASSERT_NO_FATAL_FAILURE(CGU.reanalyzeFunction(*FnF));}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses8) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(f)")return;Function *FnF = M->getFunction("f");Function *FnewF = Function::Create(FnF->getFunctionType(),FnF->getLinkage(), "newF", *M);BasicBlock *BB = BasicBlock::Create(FnewF->getContext(), "", FnewF);auto *RI = ReturnInst::Create(FnewF->getContext(), BB);while (FnF->getEntryBlock().size() > 1)FnF->getEntryBlock().front().moveBefore(RI);ASSERT_NE(FnF, nullptr);// Create an unsused constant that is referencing the old (=replaced)// function.ConstantExpr::getBitCast(FnF, Type::getInt8PtrTy(FnF->getContext()));// Use the CallGraphUpdater to update the call graph.CallGraphUpdater CGU;CGU.initialize(CG, C, AM, UR);ASSERT_NO_FATAL_FAILURE(CGU.replaceFunctionWith(*FnF, *FnewF));ASSERT_TRUE(FnF->isDeclaration());ASSERT_EQ(FnF->getNumUses(), 0U);}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses9) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(f)")return;Function *FnF = M->getFunction("f");// Use the CallGraphUpdater to update the call graph.{CallGraphUpdater CGU;CGU.initialize(CG, C, AM, UR);ASSERT_NO_FATAL_FAILURE(CGU.removeFunction(*FnF));ASSERT_EQ(M->getFunctionList().size(), 6U);}ASSERT_EQ(M->getFunctionList().size(), 5U);}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}TEST_F(CGSCCPassManagerTest, TestUpdateCGAndAnalysisManagerForPasses10) {CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (C.getName() != "(h3, h1, h2)")return;Function *FnX = M->getFunction("x");Function *FnH1 = M->getFunction("h1");Function *FnH2 = M->getFunction("h2");Function *FnH3 = M->getFunction("h3");ASSERT_NE(FnX, nullptr);ASSERT_NE(FnH1, nullptr);ASSERT_NE(FnH2, nullptr);ASSERT_NE(FnH3, nullptr);// And insert a call to `h1`, and `h3`.Instruction *IP = &FnH1->getEntryBlock().front();(void)CallInst::Create(FnH1, {}, "", IP);(void)CallInst::Create(FnH3, {}, "", IP);// Remove the `h2` call.ASSERT_TRUE(isa<CallBase>(IP));ASSERT_EQ(cast<CallBase>(IP)->getCalledFunction(), FnH2);IP->eraseFromParent();// Use the CallGraphUpdater to update the call graph.CallGraphUpdater CGU;CGU.initialize(CG, C, AM, UR);ASSERT_NO_FATAL_FAILURE(CGU.reanalyzeFunction(*FnH1));ASSERT_NO_FATAL_FAILURE(CGU.removeFunction(*FnH2));}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);}// Returns a vector containing the SCC's nodes. Useful for not iterating over an// SCC while mutating it.static SmallVector<LazyCallGraph::Node *> SCCNodes(LazyCallGraph::SCC &C) {SmallVector<LazyCallGraph::Node *> Nodes;for (auto &N : C)Nodes.push_back(&N);return Nodes;}// Start with call recursive f, create f -> g and ref recursive f.TEST_F(CGSCCPassManagerTest, TestInsertionOfNewFunctions1) {std::unique_ptr<Module> M = parseIR("define void @f() {\n""entry:\n"" call void @f()\n"" ret void\n""}\n");bool Ran = false;CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (Ran)return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();for (LazyCallGraph::Node *N : SCCNodes(C)) {Function &F = N->getFunction();if (F.getName() != "f")continue;// Create a new function 'g'.auto *G = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g", F.getParent());auto *GBB =BasicBlock::Create(F.getParent()->getContext(), "entry", G);(void)ReturnInst::Create(G->getContext(), GBB);// Instruct the LazyCallGraph to create a new node for 'g', as the// single node in a new SCC, into the call graph. As a result// the call graph is composed of a single RefSCC with two SCCs:// [(f), (g)].// "Demote" the 'f -> f' call edge to a ref edge.// 1. Erase the call edge from 'f' to 'f'.F.getEntryBlock().front().eraseFromParent();// 2. Insert a ref edge from 'f' to 'f'.(void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(F.getContext()), "f.ref",&F.getEntryBlock().front());// 3. Insert a ref edge from 'f' to 'g'.(void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(F.getContext()), "g.ref",&F.getEntryBlock().front());CG.addSplitFunction(F, *G);ASSERT_FALSE(verifyModule(*F.getParent(), &errs()));ASSERT_NO_FATAL_FAILURE(updateCGAndAnalysisManagerForCGSCCPass(CG, C, *N, AM, UR, FAM))<< "Updating the call graph with a demoted, self-referential ""call edge 'f -> f', and a newly inserted ref edge 'f -> g', ""caused a fatal failure";Ran = true;}}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);ASSERT_TRUE(Ran);}// Start with f, end with f -> g1, f -> g2, and f -ref-> (h1 <-ref-> h2).TEST_F(CGSCCPassManagerTest, TestInsertionOfNewFunctions2) {std::unique_ptr<Module> M = parseIR("define void @f() {\n""entry:\n"" ret void\n""}\n");bool Ran = false;CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C,CGSCCAnalysisManager &AM,LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (Ran)return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();for (LazyCallGraph::Node *N : SCCNodes(C)) {Function &F = N->getFunction();if (F.getName() != "f")continue;// Create g1 and g2.auto *G1 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g1", F.getParent());auto *G2 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "g2", F.getParent());BasicBlock *G1BB =BasicBlock::Create(F.getParent()->getContext(), "entry", G1);BasicBlock *G2BB =BasicBlock::Create(F.getParent()->getContext(), "entry", G2);(void)ReturnInst::Create(G1->getContext(), G1BB);(void)ReturnInst::Create(G2->getContext(), G2BB);// Add 'f -> g1' call edge.(void)CallInst::Create(G1, {}, "", &F.getEntryBlock().front());// Add 'f -> g2' call edge.(void)CallInst::Create(G2, {}, "", &F.getEntryBlock().front());CG.addSplitFunction(F, *G1);CG.addSplitFunction(F, *G2);// Create mutually recursive functions (ref only) 'h1' and 'h2'.auto *H1 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "h1", F.getParent());auto *H2 = Function::Create(F.getFunctionType(), F.getLinkage(),F.getAddressSpace(), "h2", F.getParent());BasicBlock *H1BB =BasicBlock::Create(F.getParent()->getContext(), "entry", H1);BasicBlock *H2BB =BasicBlock::Create(F.getParent()->getContext(), "entry", H2);(void)CastInst::CreatePointerCast(H2, Type::getInt8PtrTy(F.getContext()),"h2.ref", H1BB);(void)ReturnInst::Create(H1->getContext(), H1BB);(void)CastInst::CreatePointerCast(H1, Type::getInt8PtrTy(F.getContext()),"h1.ref", H2BB);(void)ReturnInst::Create(H2->getContext(), H2BB);// Add 'f -> h1' ref edge.(void)CastInst::CreatePointerCast(H1, Type::getInt8PtrTy(F.getContext()),"h1.ref", &F.getEntryBlock().front());// Add 'f -> h2' ref edge.(void)CastInst::CreatePointerCast(H2, Type::getInt8PtrTy(F.getContext()),"h2.ref", &F.getEntryBlock().front());CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 2>({H1, H2}));ASSERT_FALSE(verifyModule(*F.getParent(), &errs()));ASSERT_NO_FATAL_FAILURE(updateCGAndAnalysisManagerForCGSCCPass(CG, C, *N, AM, UR, FAM))<< "Updating the call graph with mutually recursive g1 <-> g2, h1 ""<-> h2 caused a fatal failure";Ran = true;}}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);ASSERT_TRUE(Ran);}TEST_F(CGSCCPassManagerTest, TestInsertionOfNewNonTrivialCallEdge) {std::unique_ptr<Module> M = parseIR("define void @f1() {\n""entry:\n"" %a = bitcast void ()* @f4 to i8*\n"" %b = bitcast void ()* @f2 to i8*\n"" ret void\n""}\n""define void @f2() {\n""entry:\n"" %a = bitcast void ()* @f1 to i8*\n"" %b = bitcast void ()* @f3 to i8*\n"" ret void\n""}\n""define void @f3() {\n""entry:\n"" %a = bitcast void ()* @f2 to i8*\n"" %b = bitcast void ()* @f4 to i8*\n"" ret void\n""}\n""define void @f4() {\n""entry:\n"" %a = bitcast void ()* @f3 to i8*\n"" %b = bitcast void ()* @f1 to i8*\n"" ret void\n""}\n");bool Ran = false;CGSCCPassManager CGPM;CGPM.addPass(LambdaSCCPassNoPreserve([&](LazyCallGraph::SCC &C,CGSCCAnalysisManager &AM,LazyCallGraph &CG,CGSCCUpdateResult &UR) {if (Ran)return;auto &FAM =AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();for (LazyCallGraph::Node *N : SCCNodes(C)) {Function &F = N->getFunction();if (F.getName() != "f1")continue;Function *F3 = F.getParent()->getFunction("f3");ASSERT_TRUE(F3 != nullptr);// Create call from f1 to f3.(void)CallInst::Create(F3, {}, "", F.getEntryBlock().getTerminator());ASSERT_NO_FATAL_FAILURE(updateCGAndAnalysisManagerForCGSCCPass(CG, C, *N, AM, UR, FAM))<< "Updating the call graph with mutually recursive g1 <-> g2, h1 ""<-> h2 caused a fatal failure";Ran = true;}}));ModulePassManager MPM;MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);ASSERT_TRUE(Ran);}TEST_F(CGSCCPassManagerTest, TestFunctionPassesAreQueriedForInvalidation) {std::unique_ptr<Module> M = parseIR("define void @f() { ret void }");CGSCCPassManager CGPM;bool SCCCalled = false;FunctionPassManager FPM;int ImmRuns = 0;FAM.registerPass([&] { return TestImmutableFunctionAnalysis(ImmRuns); });FPM.addPass(RequireAnalysisPass<TestImmutableFunctionAnalysis, Function>());CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,LazyCallGraph &CG, CGSCCUpdateResult &UR) {SCCCalled = true;return PreservedAnalyses::none();}));CGPM.addPass(createCGSCCToFunctionPassAdaptor(RequireAnalysisPass<TestImmutableFunctionAnalysis, Function>()));ModulePassManager MPM;MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));MPM.run(*M, MAM);ASSERT_EQ(ImmRuns, 1);ASSERT_TRUE(SCCCalled);}#endif} // namespace
//===- CFGTest.cpp - CFG tests --------------------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/CFG.h"#include "llvm/ADT/SmallPtrSet.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/Module.h"#include "llvm/InitializePasses.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;namespace {// This fixture assists in running the isPotentiallyReachable utility four ways// and ensuring it produces the correct answer each time.class IsPotentiallyReachableTest : public testing::Test {protected:void ParseAssembly(const char *Assembly) {SMDiagnostic Error;M = parseAssemblyString(Assembly, Error, Context);std::string errMsg;raw_string_ostream os(errMsg);Error.print("", os);// A failure here means that the test itself is buggy.if (!M)report_fatal_error(os.str().c_str());Function *F = M->getFunction("test");if (F == nullptr)report_fatal_error("Test must have a function named @test");A = B = nullptr;for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {if (I->hasName()) {if (I->getName() == "A")A = &*I;else if (I->getName() == "B")B = &*I;}}if (A == nullptr)report_fatal_error("@test must have an instruction %A");if (B == nullptr)report_fatal_error("@test must have an instruction %B");assert(ExclusionSet.empty());for (auto I = F->begin(), E = F->end(); I != E; ++I) {if (I->hasName() && I->getName().startswith("excluded"))ExclusionSet.insert(&*I);}}void ExpectPath(bool ExpectedResult) {static char ID;class IsPotentiallyReachableTestPass : public FunctionPass {public:IsPotentiallyReachableTestPass(bool ExpectedResult, Instruction *A,Instruction *B,SmallPtrSet<BasicBlock *, 4> ExclusionSet): FunctionPass(ID), ExpectedResult(ExpectedResult), A(A), B(B),ExclusionSet(ExclusionSet) {}static int initialize() {PassInfo *PI = new PassInfo("isPotentiallyReachable testing pass", "",&ID, nullptr, true, true);PassRegistry::getPassRegistry()->registerPass(*PI, true);initializeLoopInfoWrapperPassPass(*PassRegistry::getPassRegistry());initializeDominatorTreeWrapperPassPass(*PassRegistry::getPassRegistry());return 0;}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();AU.addRequired<LoopInfoWrapperPass>();AU.addRequired<DominatorTreeWrapperPass>();}bool runOnFunction(Function &F) override {if (!F.hasName() || F.getName() != "test")return false;LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();DominatorTree *DT =&getAnalysis<DominatorTreeWrapperPass>().getDomTree();EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, nullptr, nullptr),ExpectedResult);EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, DT, nullptr),ExpectedResult);EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, nullptr, LI),ExpectedResult);EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, DT, LI),ExpectedResult);return false;}bool ExpectedResult;Instruction *A, *B;SmallPtrSet<BasicBlock *, 4> ExclusionSet;};static int initialize = IsPotentiallyReachableTestPass::initialize();(void)initialize;IsPotentiallyReachableTestPass *P =new IsPotentiallyReachableTestPass(ExpectedResult, A, B, ExclusionSet);legacy::PassManager PM;PM.add(P);PM.run(*M);}LLVMContext Context;std::unique_ptr<Module> M;Instruction *A, *B;SmallPtrSet<BasicBlock *, 4> ExclusionSet;};}TEST_F(IsPotentiallyReachableTest, SameBlockNoPath) {ParseAssembly("define void @test() {\n""entry:\n"" bitcast i8 undef to i8\n"" %B = bitcast i8 undef to i8\n"" bitcast i8 undef to i8\n"" bitcast i8 undef to i8\n"" %A = bitcast i8 undef to i8\n"" ret void\n""}\n");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, SameBlockPath) {ParseAssembly("define void @test() {\n""entry:\n"" %A = bitcast i8 undef to i8\n"" bitcast i8 undef to i8\n"" bitcast i8 undef to i8\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}\n");ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, SameBlockNoLoop) {ParseAssembly("define void @test() {\n""entry:\n"" br label %middle\n""middle:\n"" %B = bitcast i8 undef to i8\n"" bitcast i8 undef to i8\n"" bitcast i8 undef to i8\n"" %A = bitcast i8 undef to i8\n"" br label %nextblock\n""nextblock:\n"" ret void\n""}\n");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, StraightNoPath) {ParseAssembly("define void @test() {\n""entry:\n"" %B = bitcast i8 undef to i8\n"" br label %exit\n""exit:\n"" %A = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, StraightPath) {ParseAssembly("define void @test() {\n""entry:\n"" %A = bitcast i8 undef to i8\n"" br label %exit\n""exit:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, DestUnreachable) {ParseAssembly("define void @test() {\n""entry:\n"" br label %midblock\n""midblock:\n"" %A = bitcast i8 undef to i8\n"" ret void\n""unreachable:\n"" %B = bitcast i8 undef to i8\n"" br label %midblock\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, BranchToReturn) {ParseAssembly("define void @test(i1 %x) {\n""entry:\n"" %A = bitcast i8 undef to i8\n"" br i1 %x, label %block1, label %block2\n""block1:\n"" ret void\n""block2:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, SimpleLoop1) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" br label %loop\n""loop:\n"" %B = bitcast i8 undef to i8\n"" %A = bitcast i8 undef to i8\n"" %x = call i1 @switch()\n"" br i1 %x, label %loop, label %exit\n""exit:\n"" ret void\n""}");ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, SimpleLoop2) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" %B = bitcast i8 undef to i8\n"" br label %loop\n""loop:\n"" %A = bitcast i8 undef to i8\n"" %x = call i1 @switch()\n"" br i1 %x, label %loop, label %exit\n""exit:\n"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, SimpleLoop3) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" br label %loop\n""loop:\n"" %B = bitcast i8 undef to i8\n"" %x = call i1 @switch()\n"" br i1 %x, label %loop, label %exit\n""exit:\n"" %A = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOther1) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" br label %loop1\n""loop1:\n"" %A = bitcast i8 undef to i8\n"" %x = call i1 @switch()\n"" br i1 %x, label %loop1, label %loop1exit\n""loop1exit:\n"" br label %loop2\n""loop2:\n"" %B = bitcast i8 undef to i8\n"" %y = call i1 @switch()\n"" br i1 %x, label %loop2, label %loop2exit\n""loop2exit:"" ret void\n""}");ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOther2) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" br label %loop1\n""loop1:\n"" %B = bitcast i8 undef to i8\n"" %x = call i1 @switch()\n"" br i1 %x, label %loop1, label %loop1exit\n""loop1exit:\n"" br label %loop2\n""loop2:\n"" %A = bitcast i8 undef to i8\n"" %y = call i1 @switch()\n"" br i1 %x, label %loop2, label %loop2exit\n""loop2exit:"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, OneLoopAfterTheOtherInsideAThirdLoop) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" br label %outerloop3\n""outerloop3:\n"" br label %innerloop1\n""innerloop1:\n"" %B = bitcast i8 undef to i8\n"" %x = call i1 @switch()\n"" br i1 %x, label %innerloop1, label %innerloop1exit\n""innerloop1exit:\n"" br label %innerloop2\n""innerloop2:\n"" %A = bitcast i8 undef to i8\n"" %y = call i1 @switch()\n"" br i1 %x, label %innerloop2, label %innerloop2exit\n""innerloop2exit:"" ;; In outer loop3 now.\n"" %z = call i1 @switch()\n"" br i1 %z, label %outerloop3, label %exit\n""exit:\n"" ret void\n""}");ExpectPath(true);}static const char *BranchInsideLoopIR ="declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" br label %loop\n""loop:\n"" %x = call i1 @switch()\n"" br i1 %x, label %nextloopblock, label %exit\n""nextloopblock:\n"" %y = call i1 @switch()\n"" br i1 %y, label %left, label %right\n""left:\n"" %A = bitcast i8 undef to i8\n"" br label %loop\n""right:\n"" %B = bitcast i8 undef to i8\n"" br label %loop\n""exit:\n"" ret void\n""}";TEST_F(IsPotentiallyReachableTest, BranchInsideLoop) {ParseAssembly(BranchInsideLoopIR);ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, ModifyTest) {ParseAssembly(BranchInsideLoopIR);succ_iterator S = succ_begin(&*++M->getFunction("test")->begin());BasicBlock *OldBB = S[0];S[0] = S[1];ExpectPath(false);S[0] = OldBB;ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, UnreachableFromEntryTest) {ParseAssembly("define void @test() {\n""entry:\n"" %A = bitcast i8 undef to i8\n"" ret void\n""not.reachable:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, UnreachableBlocksTest1) {ParseAssembly("define void @test() {\n""entry:\n"" ret void\n""not.reachable.1:\n"" %A = bitcast i8 undef to i8\n"" br label %not.reachable.2\n""not.reachable.2:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, UnreachableBlocksTest2) {ParseAssembly("define void @test() {\n""entry:\n"" ret void\n""not.reachable.1:\n"" %B = bitcast i8 undef to i8\n"" br label %not.reachable.2\n""not.reachable.2:\n"" %A = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, SimpleExclusionTest) {ParseAssembly("define void @test() {\n""entry:\n"" %A = bitcast i8 undef to i8\n"" br label %excluded\n""excluded:\n"" br label %exit\n""exit:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, DiamondExcludedTest) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" %x = call i1 @switch()\n"" %A = bitcast i8 undef to i8\n"" br i1 %x, label %excluded.1, label %excluded.2\n""excluded.1:\n"" br label %exit\n""excluded.2:\n"" br label %exit\n""exit:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(false);}TEST_F(IsPotentiallyReachableTest, DiamondOneSideExcludedTest) {ParseAssembly("declare i1 @switch()\n""\n""define void @test() {\n""entry:\n"" %x = call i1 @switch()\n"" %A = bitcast i8 undef to i8\n"" br i1 %x, label %excluded, label %diamond\n""excluded:\n"" br label %exit\n""diamond:\n"" br label %exit\n""exit:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(true);}TEST_F(IsPotentiallyReachableTest, UnreachableToReachable) {ParseAssembly("define void @test() {\n""entry:\n"" br label %exit\n""unreachableblock:\n"" %A = bitcast i8 undef to i8\n"" br label %exit\n""exit:\n"" %B = bitcast i8 undef to i8\n"" ret void\n""}");ExpectPath(true);}
//===- BranchProbabilityInfoTest.cpp - BranchProbabilityInfo unit tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/BranchProbabilityInfo.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Constants.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/DataTypes.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"namespace llvm {namespace {struct BranchProbabilityInfoTest : public testing::Test {std::unique_ptr<BranchProbabilityInfo> BPI;std::unique_ptr<DominatorTree> DT;std::unique_ptr<LoopInfo> LI;LLVMContext C;BranchProbabilityInfo &buildBPI(Function &F) {DT.reset(new DominatorTree(F));LI.reset(new LoopInfo(*DT));BPI.reset(new BranchProbabilityInfo(F, *LI));return *BPI;}std::unique_ptr<Module> makeLLVMModule() {const char *ModuleString = "define void @f() { exit: ret void }\n";SMDiagnostic Err;return parseAssemblyString(ModuleString, Err, C);}};TEST_F(BranchProbabilityInfoTest, StressUnreachableHeuristic) {auto M = makeLLVMModule();Function *F = M->getFunction("f");// define void @f() {// entry:// switch i32 undef, label %exit, [// i32 0, label %preexit// ... ;;< Add lots of cases to stress the heuristic.// ]// preexit:// unreachable// exit:// ret void// }auto *ExitBB = &F->back();auto *EntryBB = BasicBlock::Create(C, "entry", F, /*insertBefore=*/ExitBB);auto *PreExitBB =BasicBlock::Create(C, "preexit", F, /*insertBefore=*/ExitBB);new UnreachableInst(C, PreExitBB);unsigned NumCases = 4096;auto *I32 = IntegerType::get(C, 32);auto *Undef = UndefValue::get(I32);auto *Switch = SwitchInst::Create(Undef, ExitBB, NumCases, EntryBB);for (unsigned I = 0; I < NumCases; ++I)Switch->addCase(ConstantInt::get(I32, I), PreExitBB);BranchProbabilityInfo &BPI = buildBPI(*F);// FIXME: This doesn't seem optimal. Since all of the cases handled by the// switch have the *same* destination block ("preexit"), shouldn't it be the// hot one? I'd expect the results to be reversed here...EXPECT_FALSE(BPI.isEdgeHot(EntryBB, PreExitBB));EXPECT_TRUE(BPI.isEdgeHot(EntryBB, ExitBB));}} // end anonymous namespace} // end namespace llvm
//===- BlockFrequencyInfoTest.cpp - BlockFrequencyInfo unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/BlockFrequencyInfo.h"#include "llvm/Analysis/BlockFrequencyInfoImpl.h"#include "llvm/Analysis/BranchProbabilityInfo.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/Function.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/DataTypes.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"namespace llvm {namespace {class BlockFrequencyInfoTest : public testing::Test {protected:std::unique_ptr<BranchProbabilityInfo> BPI;std::unique_ptr<DominatorTree> DT;std::unique_ptr<LoopInfo> LI;LLVMContext C;BlockFrequencyInfo buildBFI(Function &F) {DT.reset(new DominatorTree(F));LI.reset(new LoopInfo(*DT));BPI.reset(new BranchProbabilityInfo(F, *LI));return BlockFrequencyInfo(F, *BPI, *LI);}std::unique_ptr<Module> makeLLVMModule() {const char *ModuleStrig = "define i32 @f(i32 %x) {\n""bb0:\n"" %y1 = icmp eq i32 %x, 0 \n"" br i1 %y1, label %bb1, label %bb2 \n""bb1:\n"" br label %bb3\n""bb2:\n"" br label %bb3\n""bb3:\n"" %y2 = phi i32 [0, %bb1], [1, %bb2] \n"" ret i32 %y2\n""}\n";SMDiagnostic Err;return parseAssemblyString(ModuleStrig, Err, C);}};TEST_F(BlockFrequencyInfoTest, Basic) {auto M = makeLLVMModule();Function *F = M->getFunction("f");F->setEntryCount(100);BlockFrequencyInfo BFI = buildBFI(*F);BasicBlock &BB0 = F->getEntryBlock();BasicBlock *BB1 = BB0.getTerminator()->getSuccessor(0);BasicBlock *BB2 = BB0.getTerminator()->getSuccessor(1);BasicBlock *BB3 = BB1->getSingleSuccessor();uint64_t BB0Freq = BFI.getBlockFreq(&BB0).getFrequency();uint64_t BB1Freq = BFI.getBlockFreq(BB1).getFrequency();uint64_t BB2Freq = BFI.getBlockFreq(BB2).getFrequency();uint64_t BB3Freq = BFI.getBlockFreq(BB3).getFrequency();EXPECT_EQ(BB0Freq, BB3Freq);EXPECT_EQ(BB0Freq, BB1Freq + BB2Freq);EXPECT_EQ(BB0Freq, BB3Freq);EXPECT_EQ(BFI.getBlockProfileCount(&BB0).value(), UINT64_C(100));EXPECT_EQ(BFI.getBlockProfileCount(BB3).value(), UINT64_C(100));EXPECT_EQ(BFI.getBlockProfileCount(BB1).value(),(100 * BB1Freq + BB0Freq / 2) / BB0Freq);EXPECT_EQ(BFI.getBlockProfileCount(BB2).value(),(100 * BB2Freq + BB0Freq / 2) / BB0Freq);// Scale the frequencies of BB0, BB1 and BB2 by a factor of two.SmallPtrSet<BasicBlock *, 4> BlocksToScale({BB1, BB2});BFI.setBlockFreqAndScale(&BB0, BB0Freq * 2, BlocksToScale);EXPECT_EQ(BFI.getBlockFreq(&BB0).getFrequency(), 2 * BB0Freq);EXPECT_EQ(BFI.getBlockFreq(BB1).getFrequency(), 2 * BB1Freq);EXPECT_EQ(BFI.getBlockFreq(BB2).getFrequency(), 2 * BB2Freq);EXPECT_EQ(BFI.getBlockFreq(BB3).getFrequency(), BB3Freq);}static_assert(std::is_trivially_copyable<bfi_detail::BlockMass>::value,"trivially copyable");} // end anonymous namespace} // end namespace llvm
//===- BasicAliasAnalysisTest.cpp - Unit tests for BasicAA ----------------===////// 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////===----------------------------------------------------------------------===////// Targeted tests that are hard/convoluted to make happen with just `opt`.//#include "llvm/Analysis/BasicAliasAnalysis.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Dominators.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;// FIXME: This is duplicated between this file and MemorySSATest. Refactor.const static char DLString[] = "e-i64:64-f80:128-n8:16:32:64-S128";/// There's a lot of common setup between these tests. This fixture helps reduce/// that. Tests should mock up a function, store it in F, and then call/// setupAnalyses().class BasicAATest : public testing::Test {protected:// N.B. Many of these members depend on each other (e.g. the Module depends on// the Context, etc.). So, order matters here (and in TestAnalyses).LLVMContext C;Module M;IRBuilder<> B;DataLayout DL;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;Function *F;// Things that we need to build after the function is created.struct TestAnalyses {DominatorTree DT;AssumptionCache AC;BasicAAResult BAA;SimpleAAQueryInfo AAQI;TestAnalyses(BasicAATest &Test): DT(*Test.F), AC(*Test.F), BAA(Test.DL, *Test.F, Test.TLI, AC, &DT),AAQI() {}};llvm::Optional<TestAnalyses> Analyses;TestAnalyses &setupAnalyses() {assert(F);Analyses.emplace(*this);return *Analyses;}public:BasicAATest(): M("BasicAATest", C), B(C), DL(DLString), TLI(TLII), F(nullptr) {C.setOpaquePointers(true);}};// Check that a function arg can't trivially alias a global when we're accessing// >sizeof(global) bytes through that arg, unless the access size is just an// upper-bound.TEST_F(BasicAATest, AliasInstWithObjectOfImpreciseSize) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getPtrTy()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Value *IncomingI32Ptr = F->arg_begin();auto *GlobalPtr =cast<GlobalVariable>(M.getOrInsertGlobal("some_global", B.getInt8Ty()));// Without sufficiently restricted linkage/an init, some of the object size// checking bits get more conservative.GlobalPtr->setLinkage(GlobalValue::LinkageTypes::InternalLinkage);GlobalPtr->setInitializer(B.getInt8(0));auto &AllAnalyses = setupAnalyses();BasicAAResult &BasicAA = AllAnalyses.BAA;AAQueryInfo &AAQI = AllAnalyses.AAQI;ASSERT_EQ(BasicAA.alias(MemoryLocation(IncomingI32Ptr, LocationSize::precise(4)),MemoryLocation(GlobalPtr, LocationSize::precise(1)), AAQI),AliasResult::NoAlias);ASSERT_EQ(BasicAA.alias(MemoryLocation(IncomingI32Ptr, LocationSize::upperBound(4)),MemoryLocation(GlobalPtr, LocationSize::precise(1)), AAQI),AliasResult::MayAlias);}// Check that we fall back to MayAlias if we see an access of an entire object// that's just an upper-bound.TEST_F(BasicAATest, AliasInstWithFullObjectOfImpreciseSize) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt64Ty()}, false),GlobalValue::ExternalLinkage, "F", &M);BasicBlock *Entry(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);Value *ArbitraryI32 = F->arg_begin();AllocaInst *I8 = B.CreateAlloca(B.getInt8Ty(), B.getInt32(2));auto *I8AtUncertainOffset =cast<GetElementPtrInst>(B.CreateGEP(B.getInt8Ty(), I8, ArbitraryI32));auto &AllAnalyses = setupAnalyses();BasicAAResult &BasicAA = AllAnalyses.BAA;AAQueryInfo &AAQI = AllAnalyses.AAQI;ASSERT_EQ(BasicAA.alias(MemoryLocation(I8, LocationSize::precise(2)),MemoryLocation(I8AtUncertainOffset, LocationSize::precise(1)),AAQI),AliasResult::PartialAlias);ASSERT_EQ(BasicAA.alias(MemoryLocation(I8, LocationSize::upperBound(2)),MemoryLocation(I8AtUncertainOffset, LocationSize::precise(1)),AAQI),AliasResult::MayAlias);}TEST_F(BasicAATest, PartialAliasOffsetPhi) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getPtrTy(), B.getInt1Ty()}, false),GlobalValue::ExternalLinkage, "F", &M);Value *Ptr = F->arg_begin();Value *I = F->arg_begin() + 1;BasicBlock *Entry(BasicBlock::Create(C, "", F));BasicBlock *B1(BasicBlock::Create(C, "", F));BasicBlock *B2(BasicBlock::Create(C, "", F));BasicBlock *End(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);B.CreateCondBr(I, B1, B2);B.SetInsertPoint(B1);auto *Ptr1 =cast<GetElementPtrInst>(B.CreateGEP(B.getInt8Ty(), Ptr, B.getInt32(1)));B.CreateBr(End);B.SetInsertPoint(B2);auto *Ptr2 =cast<GetElementPtrInst>(B.CreateGEP(B.getInt8Ty(), Ptr, B.getInt32(1)));B.CreateBr(End);B.SetInsertPoint(End);auto *Phi = B.CreatePHI(B.getPtrTy(), 2);Phi->addIncoming(Ptr1, B1);Phi->addIncoming(Ptr2, B2);B.CreateRetVoid();auto &AllAnalyses = setupAnalyses();BasicAAResult &BasicAA = AllAnalyses.BAA;AAQueryInfo &AAQI = AllAnalyses.AAQI;AliasResult AR =BasicAA.alias(MemoryLocation(Ptr, LocationSize::precise(2)),MemoryLocation(Phi, LocationSize::precise(1)), AAQI);ASSERT_EQ(AR.getOffset(), 1);}TEST_F(BasicAATest, PartialAliasOffsetSelect) {F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getPtrTy(), B.getInt1Ty()}, false),GlobalValue::ExternalLinkage, "F", &M);Value *Ptr = F->arg_begin();Value *I = F->arg_begin() + 1;BasicBlock *Entry(BasicBlock::Create(C, "", F));B.SetInsertPoint(Entry);auto *Ptr1 =cast<GetElementPtrInst>(B.CreateGEP(B.getInt8Ty(), Ptr, B.getInt32(1)));auto *Ptr2 =cast<GetElementPtrInst>(B.CreateGEP(B.getInt8Ty(), Ptr, B.getInt32(1)));auto *Select = B.CreateSelect(I, Ptr1, Ptr2);B.CreateRetVoid();auto &AllAnalyses = setupAnalyses();BasicAAResult &BasicAA = AllAnalyses.BAA;AAQueryInfo &AAQI = AllAnalyses.AAQI;AliasResult AR =BasicAA.alias(MemoryLocation(Ptr, LocationSize::precise(2)),MemoryLocation(Select, LocationSize::precise(1)), AAQI);ASSERT_EQ(AR.getOffset(), 1);}
//===- AssumeBundleQueriesTest.cpp ------------------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/AssumeBundleQueries.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/IntrinsicInst.h"#include "llvm/Support/Regex.h"#include "llvm/Support/SourceMgr.h"#include "llvm/Support/CommandLine.h"#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"#include "gtest/gtest.h"#include <random>using namespace llvm;namespace llvm {extern cl::opt<bool> ShouldPreserveAllAttributes;extern cl::opt<bool> EnableKnowledgeRetention;} // namespace llvmstatic void RunTest(StringRef Head, StringRef Tail,std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>&Tests) {for (auto &Elem : Tests) {std::string IR;IR.append(Head.begin(), Head.end());IR.append(Elem.first.begin(), Elem.first.end());IR.append(Tail.begin(), Tail.end());LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);if (!Mod)Err.print("AssumeQueryAPI", errs());Elem.second(&*(Mod->getFunction("test")->begin()->begin()));}}bool hasMatchesExactlyAttributes(AssumeInst *Assume, Value *WasOn,StringRef AttrToMatch) {Regex Reg(AttrToMatch);SmallVector<StringRef, 1> Matches;for (StringRef Attr : {#define GET_ATTR_NAMES#define ATTRIBUTE_ALL(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),#include "llvm/IR/Attributes.inc"}) {bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;if (ShouldHaveAttr != hasAttributeInAssume(*Assume, WasOn, Attr))return false;}return true;}bool hasTheRightValue(AssumeInst *Assume, Value *WasOn,Attribute::AttrKind Kind, unsigned Value) {uint64_t ArgVal = 0;if (!hasAttributeInAssume(*Assume, WasOn, Kind, &ArgVal))return false;if (ArgVal != Value)return false;return true;}TEST(AssumeQueryAPI, hasAttributeInAssume) {EnableKnowledgeRetention.setValue(true);StringRef Head ="declare void @llvm.assume(i1)\n""declare void @func(i32*, i32*, i32*)\n""declare void @func1(i32*, i32*, i32*, i32*)\n""declare void @func_many(i32*) \"no-jump-tables\" nounwind ""\"less-precise-fpmad\" willreturn norecurse\n""define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";StringRef Tail = "ret void\n""}";std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>Tests;Tests.push_back(std::make_pair("call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align ""8 noalias %P1, i32* align 8 noundef %P2)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),"()"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),"(align|noundef)"));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Dereferenceable, 16));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Alignment, 4));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Alignment, 4));}));Tests.push_back(std::make_pair("call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* ""nonnull ""align 8 dereferenceable(28) %P, i32* nonnull align 64 ""dereferenceable(4) ""%P, i32* nonnull align 16 dereferenceable(12) %P)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Dereferenceable, 48));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Alignment, 64));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),Attribute::AttrKind::Alignment, 64));}));Tests.push_back(std::make_pair("call void @func_many(i32* align 8 noundef %P1) cold\n", [](Instruction *I) {ShouldPreserveAllAttributes.setValue(true);auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, nullptr,"(align|nounwind|norecurse|noundef|willreturn|cold)"));ShouldPreserveAllAttributes.setValue(false);}));Tests.push_back(std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {auto *Assume = cast<AssumeInst>(I);ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, nullptr, ""));}));Tests.push_back(std::make_pair("call void @func1(i32* readnone align 32 ""dereferenceable(48) noalias %P, i32* ""align 8 dereferenceable(28) %P1, i32* align 64 ""dereferenceable(4) ""%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),"(align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),"(align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),"(align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Alignment, 32));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Dereferenceable, 48));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),Attribute::AttrKind::Dereferenceable, 28));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),Attribute::AttrKind::Alignment, 8));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(2),Attribute::AttrKind::Alignment, 64));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(2),Attribute::AttrKind::Dereferenceable, 4));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(3),Attribute::AttrKind::Alignment, 16));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(3),Attribute::AttrKind::Dereferenceable, 12));}));Tests.push_back(std::make_pair("call void @func1(i32* readnone align 32 ""dereferenceable(48) noalias %P, i32* ""align 8 dereferenceable(28) %P1, i32* align 64 ""dereferenceable(4) ""%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);I->getOperand(1)->dropDroppableUses();I->getOperand(2)->dropDroppableUses();I->getOperand(3)->dropDroppableUses();ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),"(align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),""));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),""));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),""));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Alignment, 32));ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),Attribute::AttrKind::Dereferenceable, 48));}));Tests.push_back(std::make_pair("call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align ""8 noalias %P1, i32* %P1)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);Value *New = I->getFunction()->getArg(3);Value *Old = I->getOperand(0);ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, New, ""));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, Old,"(nonnull|align|dereferenceable)"));Old->replaceAllUsesWith(New);ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, New,"(nonnull|align|dereferenceable)"));ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, Old, ""));}));RunTest(Head, Tail, Tests);}static bool FindExactlyAttributes(RetainedKnowledgeMap &Map, Value *WasOn,StringRef AttrToMatch) {Regex Reg(AttrToMatch);SmallVector<StringRef, 1> Matches;for (StringRef Attr : {#define GET_ATTR_NAMES#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),#include "llvm/IR/Attributes.inc"}) {bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;if (ShouldHaveAttr != (Map.find(RetainedKnowledgeKey{WasOn, Attribute::getAttrKindFromName(Attr)}) != Map.end()))return false;}return true;}static bool MapHasRightValue(RetainedKnowledgeMap &Map, AssumeInst *II,RetainedKnowledgeKey Key, MinMax MM) {auto LookupIt = Map.find(Key);return (LookupIt != Map.end()) && (LookupIt->second[II].Min == MM.Min) &&(LookupIt->second[II].Max == MM.Max);}TEST(AssumeQueryAPI, fillMapFromAssume) {EnableKnowledgeRetention.setValue(true);StringRef Head ="declare void @llvm.assume(i1)\n""declare void @func(i32*, i32*, i32*)\n""declare void @func1(i32*, i32*, i32*, i32*)\n""declare void @func_many(i32*) \"no-jump-tables\" nounwind ""\"less-precise-fpmad\" willreturn norecurse\n""define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";StringRef Tail = "ret void\n""}";std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>Tests;Tests.push_back(std::make_pair("call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align ""8 noalias %P1, i32* align 8 dereferenceable(8) %P2)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);RetainedKnowledgeMap Map;fillMapFromAssume(*Assume, Map);ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),"(nonnull|align|dereferenceable)"));ASSERT_FALSE(FindExactlyAttributes(Map, I->getOperand(1),"(align)"));ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),"(align|dereferenceable)"));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},{4, 4}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},{4, 4}));}));Tests.push_back(std::make_pair("call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* ""nonnull ""align 8 dereferenceable(28) %P, i32* nonnull align 64 ""dereferenceable(4) ""%P, i32* nonnull align 16 dereferenceable(12) %P)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);RetainedKnowledgeMap Map;fillMapFromAssume(*Assume, Map);ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Dereferenceable},{48, 48}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment}, {64, 64}));}));Tests.push_back(std::make_pair("call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {ShouldPreserveAllAttributes.setValue(true);auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);RetainedKnowledgeMap Map;fillMapFromAssume(*Assume, Map);ASSERT_TRUE(FindExactlyAttributes(Map, nullptr, "(nounwind|norecurse|willreturn|cold)"));ShouldPreserveAllAttributes.setValue(false);}));Tests.push_back(std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {RetainedKnowledgeMap Map;fillMapFromAssume(*cast<AssumeInst>(I), Map);ASSERT_TRUE(FindExactlyAttributes(Map, nullptr, ""));ASSERT_TRUE(Map.empty());}));Tests.push_back(std::make_pair("call void @func1(i32* readnone align 32 ""dereferenceable(48) noalias %P, i32* ""align 8 dereferenceable(28) %P1, i32* align 64 ""dereferenceable(4) ""%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);RetainedKnowledgeMap Map;fillMapFromAssume(*Assume, Map);ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),"(align|dereferenceable)"));ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),"(align|dereferenceable)"));ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),"(align|dereferenceable)"));ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),"(nonnull|align|dereferenceable)"));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},{32, 32}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(1), Attribute::Alignment},{8, 8}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(2), Attribute::Alignment},{64, 64}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(3), Attribute::Alignment},{16, 16}));ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12}));}));/// Keep this test last as it modifies the function.Tests.push_back(std::make_pair("call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align ""8 noalias %P1, i32* %P2)\n",[](Instruction *I) {auto *Assume = buildAssumeFromInst(I);Assume->insertBefore(I);RetainedKnowledgeMap Map;fillMapFromAssume(*Assume, Map);Value *New = I->getFunction()->getArg(3);Value *Old = I->getOperand(0);ASSERT_TRUE(FindExactlyAttributes(Map, New, ""));ASSERT_TRUE(FindExactlyAttributes(Map, Old,"(nonnull|align|dereferenceable)"));Old->replaceAllUsesWith(New);Map.clear();fillMapFromAssume(*Assume, Map);ASSERT_TRUE(FindExactlyAttributes(Map, New,"(nonnull|align|dereferenceable)"));ASSERT_TRUE(FindExactlyAttributes(Map, Old, ""));}));Tests.push_back(std::make_pair("call void @llvm.assume(i1 true) [\"align\"(i8* undef, i32 undef)]",[](Instruction *I) {// Don't crash but don't learn from undef.RetainedKnowledgeMap Map;fillMapFromAssume(*cast<AssumeInst>(I), Map);ASSERT_TRUE(Map.empty());}));RunTest(Head, Tail, Tests);}static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount,unsigned MaxValue) {LLVMContext C;SMDiagnostic Err;std::random_device dev;std::mt19937 Rng(Seed);std::uniform_int_distribution<int> DistCount(MinCount, MaxCount);std::uniform_int_distribution<unsigned> DistValue(0, MaxValue);std::uniform_int_distribution<unsigned> DistAttr(0,Attribute::EndAttrKinds - 1);std::unique_ptr<Module> Mod = std::make_unique<Module>("AssumeQueryAPI", C);if (!Mod)Err.print("AssumeQueryAPI", errs());std::vector<Type *> TypeArgs;for (int i = 0; i < (Size * 2); i++)TypeArgs.push_back(Type::getInt32PtrTy(C));FunctionType *FuncType =FunctionType::get(Type::getVoidTy(C), TypeArgs, false);Function *F =Function::Create(FuncType, GlobalValue::ExternalLinkage, "test", &*Mod);BasicBlock *BB = BasicBlock::Create(C);BB->insertInto(F);Instruction *Ret = ReturnInst::Create(C);BB->getInstList().insert(BB->begin(), Ret);Function *FnAssume = Intrinsic::getDeclaration(Mod.get(), Intrinsic::assume);std::vector<Argument *> ShuffledArgs;BitVector HasArg;for (auto &Arg : F->args()) {ShuffledArgs.push_back(&Arg);HasArg.push_back(false);}std::shuffle(ShuffledArgs.begin(), ShuffledArgs.end(), Rng);std::vector<OperandBundleDef> OpBundle;OpBundle.reserve(Size);std::vector<Value *> Args;Args.reserve(2);for (int i = 0; i < Size; i++) {int count = DistCount(Rng);int value = DistValue(Rng);int attr = DistAttr(Rng);std::string str;raw_string_ostream ss(str);ss << Attribute::getNameFromAttrKind(static_cast<Attribute::AttrKind>(attr));Args.clear();if (count > 0) {Args.push_back(ShuffledArgs[i]);HasArg[i] = true;}if (count > 1)Args.push_back(ConstantInt::get(Type::getInt32Ty(C), value));OpBundle.push_back(OperandBundleDef{ss.str().c_str(), std::move(Args)});}auto *Assume = cast<AssumeInst>(CallInst::Create(FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle));Assume->insertBefore(&F->begin()->front());RetainedKnowledgeMap Map;fillMapFromAssume(*Assume, Map);for (int i = 0; i < (Size * 2); i++) {if (!HasArg[i])continue;RetainedKnowledge K =getKnowledgeFromUseInAssume(&*ShuffledArgs[i]->use_begin());auto LookupIt = Map.find(RetainedKnowledgeKey{K.WasOn, K.AttrKind});ASSERT_TRUE(LookupIt != Map.end());MinMax MM = LookupIt->second[Assume];ASSERT_TRUE(MM.Min == MM.Max);ASSERT_TRUE(MM.Min == K.ArgValue);}}TEST(AssumeQueryAPI, getKnowledgeFromUseInAssume) {// // For Fuzzing// std::random_device dev;// std::mt19937 Rng(dev());// while (true) {// unsigned Seed = Rng();// dbgs() << Seed << "\n";// RunRandTest(Seed, 100000, 0, 2, 100);// }RunRandTest(23456, 4, 0, 2, 100);RunRandTest(560987, 25, -3, 2, 100);// Large bundles can lead to special cases. this is why this test is soo// large.RunRandTest(9876789, 100000, -0, 7, 100);}TEST(AssumeQueryAPI, AssumptionCache) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString("declare void @llvm.assume(i1)\n""define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3, i1 %B) {\n""call void @llvm.assume(i1 true) [\"nonnull\"(i32* %P), \"align\"(i32* ""%P2, i32 4), \"align\"(i32* %P, i32 8)]\n""call void @llvm.assume(i1 %B) [\"test\"(i32* %P1), ""\"dereferenceable\"(i32* %P, i32 4)]\n""ret void\n}\n",Err, C);if (!Mod)Err.print("AssumeQueryAPI", errs());Function *F = Mod->getFunction("test");BasicBlock::iterator First = F->begin()->begin();BasicBlock::iterator Second = F->begin()->begin();Second++;AssumptionCache AC(*F);auto AR = AC.assumptionsFor(F->getArg(3));ASSERT_EQ(AR.size(), 0u);AR = AC.assumptionsFor(F->getArg(1));ASSERT_EQ(AR.size(), 1u);ASSERT_EQ(AR[0].Index, 0u);ASSERT_EQ(AR[0].Assume, &*Second);AR = AC.assumptionsFor(F->getArg(2));ASSERT_EQ(AR.size(), 1u);ASSERT_EQ(AR[0].Index, 1u);ASSERT_EQ(AR[0].Assume, &*First);AR = AC.assumptionsFor(F->getArg(0));ASSERT_EQ(AR.size(), 3u);llvm::sort(AR,[](const auto &L, const auto &R) { return L.Index < R.Index; });ASSERT_EQ(AR[0].Assume, &*First);ASSERT_EQ(AR[0].Index, 0u);ASSERT_EQ(AR[1].Assume, &*Second);ASSERT_EQ(AR[1].Index, 1u);ASSERT_EQ(AR[2].Assume, &*First);ASSERT_EQ(AR[2].Index, 2u);AR = AC.assumptionsFor(F->getArg(4));ASSERT_EQ(AR.size(), 1u);ASSERT_EQ(AR[0].Assume, &*Second);ASSERT_EQ(AR[0].Index, AssumptionCache::ExprResultIdx);AC.unregisterAssumption(cast<AssumeInst>(&*Second));AR = AC.assumptionsFor(F->getArg(1));ASSERT_EQ(AR.size(), 0u);AR = AC.assumptionsFor(F->getArg(0));ASSERT_EQ(AR.size(), 3u);llvm::sort(AR,[](const auto &L, const auto &R) { return L.Index < R.Index; });ASSERT_EQ(AR[0].Assume, &*First);ASSERT_EQ(AR[0].Index, 0u);ASSERT_EQ(AR[1].Assume, nullptr);ASSERT_EQ(AR[1].Index, 1u);ASSERT_EQ(AR[2].Assume, &*First);ASSERT_EQ(AR[2].Index, 2u);AR = AC.assumptionsFor(F->getArg(2));ASSERT_EQ(AR.size(), 1u);ASSERT_EQ(AR[0].Index, 1u);ASSERT_EQ(AR[0].Assume, &*First);}TEST(AssumeQueryAPI, Alignment) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> Mod = parseAssemblyString("declare void @llvm.assume(i1)\n""define void @test(i32* %P, i32* %P1, i32* %P2, i32 %I3, i1 %B) {\n""call void @llvm.assume(i1 true) [\"align\"(i32* %P, i32 8, i32 %I3)]\n""call void @llvm.assume(i1 true) [\"align\"(i32* %P1, i32 %I3, i32 ""%I3)]\n""call void @llvm.assume(i1 true) [\"align\"(i32* %P2, i32 16, i32 8)]\n""ret void\n}\n",Err, C);if (!Mod)Err.print("AssumeQueryAPI", errs());Function *F = Mod->getFunction("test");BasicBlock::iterator Start = F->begin()->begin();AssumeInst *II;RetainedKnowledge RK;II = cast<AssumeInst>(&*Start);RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);ASSERT_EQ(RK.AttrKind, Attribute::Alignment);ASSERT_EQ(RK.WasOn, F->getArg(0));ASSERT_EQ(RK.ArgValue, 1u);Start++;II = cast<AssumeInst>(&*Start);RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);ASSERT_EQ(RK.AttrKind, Attribute::Alignment);ASSERT_EQ(RK.WasOn, F->getArg(1));ASSERT_EQ(RK.ArgValue, 1u);Start++;II = cast<AssumeInst>(&*Start);RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);ASSERT_EQ(RK.AttrKind, Attribute::Alignment);ASSERT_EQ(RK.WasOn, F->getArg(2));ASSERT_EQ(RK.ArgValue, 8u);}
//=======- AliasSetTrackerTest.cpp - Unit test for the Alias Set Tracker -===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/AliasSetTracker.h"#include "llvm/ADT/Triple.h"#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/Analysis/TypeBasedAliasAnalysis.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/Module.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;TEST(AliasSetTracker, AliasUnknownInst) {StringRef Assembly = R"(@a = common global i32 0, align 4@b = common global float 0.000000e+00, align 4; Function Attrs: nounwind ssp uwtabledefine i32 @read_a() #0 {%1 = load i32, i32* @a, align 4, !tbaa !3ret i32 %1}; Function Attrs: nounwind ssp uwtabledefine void @write_b() #0 {store float 1.000000e+01, float* @b, align 4, !tbaa !7ret void}; Function Attrs: nounwind ssp uwtabledefine void @test() #0 {%1 = call i32 @read_a(), !tbaa !3call void @write_b(), !tbaa !7ret void}!3 = !{!4, !4, i64 0}!4 = !{!"int", !5, i64 0}!5 = !{!"omnipotent char", !6, i64 0}!6 = !{!"Simple C/C++ TBAA"}!7 = !{!8, !8, i64 0}!8 = !{!"float", !5, i64 0})";// Parse the IR. The two calls in @test can not access aliasing elements.LLVMContext Context;SMDiagnostic Error;auto M = parseAssemblyString(Assembly, Error, Context);ASSERT_TRUE(M) << "Bad assembly?";// Initialize the alias result.Triple Trip(M->getTargetTriple());TargetLibraryInfoImpl TLII(Trip);TargetLibraryInfo TLI(TLII);AAResults AA(TLI);TypeBasedAAResult TBAAR;AA.addAAResult(TBAAR);// Initialize the alias set tracker for the @test function.Function *Test = M->getFunction("test");ASSERT_NE(Test, nullptr);AliasSetTracker AST(AA);for (auto &BB : *Test)AST.add(BB);// There should be 2 disjoint alias sets. 1 from each call.ASSERT_EQ((int)AST.getAliasSets().size(), 2);// Directly test aliasesUnknownInst.// Now every call instruction should only alias one alias set.for (auto &Inst : *Test->begin()) {bool FoundAS = false;for (AliasSet &AS : AST) {if (!Inst.mayReadOrWriteMemory())continue;if (!AS.aliasesUnknownInst(&Inst, AA))continue;ASSERT_NE(FoundAS, true);FoundAS = true;}}}
//===--- AliasAnalysisTest.cpp - Mixed TBAA unit tests --------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/Analysis/AliasAnalysis.h"#include "llvm/ADT/SetVector.h"#include "llvm/Analysis/AssumptionCache.h"#include "llvm/Analysis/BasicAliasAnalysis.h"#include "llvm/Analysis/TargetLibraryInfo.h"#include "llvm/AsmParser/Parser.h"#include "llvm/IR/Constants.h"#include "llvm/IR/InstIterator.h"#include "llvm/IR/Instructions.h"#include "llvm/IR/LLVMContext.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/Module.h"#include "llvm/InitializePasses.h"#include "llvm/Support/SourceMgr.h"#include "gtest/gtest.h"using namespace llvm;// Set up some test passes.namespace llvm {void initializeAATestPassPass(PassRegistry&);void initializeTestCustomAAWrapperPassPass(PassRegistry&);}namespace {struct AATestPass : FunctionPass {static char ID;AATestPass() : FunctionPass(ID) {initializeAATestPassPass(*PassRegistry::getPassRegistry());}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.addRequired<AAResultsWrapperPass>();AU.setPreservesAll();}bool runOnFunction(Function &F) override {AliasAnalysis &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();SetVector<Value *> Pointers;for (Argument &A : F.args())if (A.getType()->isPointerTy())Pointers.insert(&A);for (Instruction &I : instructions(F))if (I.getType()->isPointerTy())Pointers.insert(&I);for (Value *P1 : Pointers)for (Value *P2 : Pointers)(void)AA.alias(P1, LocationSize::beforeOrAfterPointer(), P2,LocationSize::beforeOrAfterPointer());return false;}};}char AATestPass::ID = 0;INITIALIZE_PASS_BEGIN(AATestPass, "aa-test-pas", "Alias Analysis Test Pass",false, true)INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)INITIALIZE_PASS_END(AATestPass, "aa-test-pass", "Alias Analysis Test Pass",false, true)namespace {/// A test customizable AA result. It merely accepts a callback to run whenever/// it receives an alias query. Useful for testing that a particular AA result/// is reached.struct TestCustomAAResult : AAResultBase<TestCustomAAResult> {friend AAResultBase<TestCustomAAResult>;std::function<void()> CB;explicit TestCustomAAResult(std::function<void()> CB): AAResultBase(), CB(std::move(CB)) {}TestCustomAAResult(TestCustomAAResult &&Arg): AAResultBase(std::move(Arg)), CB(std::move(Arg.CB)) {}bool invalidate(Function &, const PreservedAnalyses &) { return false; }AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,AAQueryInfo &AAQI) {CB();return AliasResult::MayAlias;}};}namespace {/// A wrapper pass for the legacy pass manager to use with the above custom AA/// result.class TestCustomAAWrapperPass : public ImmutablePass {std::function<void()> CB;std::unique_ptr<TestCustomAAResult> Result;public:static char ID;explicit TestCustomAAWrapperPass(std::function<void()> CB = std::function<void()>()): ImmutablePass(ID), CB(std::move(CB)) {initializeTestCustomAAWrapperPassPass(*PassRegistry::getPassRegistry());}void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();AU.addRequired<TargetLibraryInfoWrapperPass>();}bool doInitialization(Module &M) override {Result.reset(new TestCustomAAResult(std::move(CB)));return true;}bool doFinalization(Module &M) override {Result.reset();return true;}TestCustomAAResult &getResult() { return *Result; }const TestCustomAAResult &getResult() const { return *Result; }};}char TestCustomAAWrapperPass::ID = 0;INITIALIZE_PASS_BEGIN(TestCustomAAWrapperPass, "test-custom-aa","Test Custom AA Wrapper Pass", false, true)INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)INITIALIZE_PASS_END(TestCustomAAWrapperPass, "test-custom-aa","Test Custom AA Wrapper Pass", false, true)namespace {class AliasAnalysisTest : public testing::Test {protected:LLVMContext C;Module M;TargetLibraryInfoImpl TLII;TargetLibraryInfo TLI;std::unique_ptr<AssumptionCache> AC;std::unique_ptr<BasicAAResult> BAR;std::unique_ptr<AAResults> AAR;AliasAnalysisTest() : M("AliasAnalysisTest", C), TLI(TLII) {}AAResults &getAAResults(Function &F) {// Reset the Function AA results first to clear out any references.AAR.reset(new AAResults(TLI));// Build the various AA results and register them.AC.reset(new AssumptionCache(F));BAR.reset(new BasicAAResult(M.getDataLayout(), F, TLI, *AC));AAR->addAAResult(*BAR);return *AAR;}};TEST_F(AliasAnalysisTest, getModRefInfo) {// Setup function.FunctionType *FTy =FunctionType::get(Type::getVoidTy(C), std::vector<Type *>(), false);auto *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);auto *BB = BasicBlock::Create(C, "entry", F);auto IntType = Type::getInt32Ty(C);auto PtrType = Type::getInt32PtrTy(C);auto *Value = ConstantInt::get(IntType, 42);auto *Addr = ConstantPointerNull::get(PtrType);auto Alignment = Align(IntType->getBitWidth() / 8);auto *Store1 = new StoreInst(Value, Addr, BB);auto *Load1 = new LoadInst(IntType, Addr, "load", BB);auto *Add1 = BinaryOperator::CreateAdd(Value, Value, "add", BB);auto *VAArg1 = new VAArgInst(Addr, PtrType, "vaarg", BB);auto *CmpXChg1 = new AtomicCmpXchgInst(Addr, ConstantInt::get(IntType, 0), ConstantInt::get(IntType, 1),Alignment, AtomicOrdering::Monotonic, AtomicOrdering::Monotonic,SyncScope::System, BB);auto *AtomicRMW = new AtomicRMWInst(AtomicRMWInst::Xchg, Addr, ConstantInt::get(IntType, 1), Alignment,AtomicOrdering::Monotonic, SyncScope::System, BB);ReturnInst::Create(C, nullptr, BB);auto &AA = getAAResults(*F);// Check basic resultsEXPECT_EQ(AA.getModRefInfo(Store1, MemoryLocation()), ModRefInfo::Mod);EXPECT_EQ(AA.getModRefInfo(Store1, None), ModRefInfo::Mod);EXPECT_EQ(AA.getModRefInfo(Load1, MemoryLocation()), ModRefInfo::Ref);EXPECT_EQ(AA.getModRefInfo(Load1, None), ModRefInfo::Ref);EXPECT_EQ(AA.getModRefInfo(Add1, MemoryLocation()), ModRefInfo::NoModRef);EXPECT_EQ(AA.getModRefInfo(Add1, None), ModRefInfo::NoModRef);EXPECT_EQ(AA.getModRefInfo(VAArg1, MemoryLocation()), ModRefInfo::ModRef);EXPECT_EQ(AA.getModRefInfo(VAArg1, None), ModRefInfo::ModRef);EXPECT_EQ(AA.getModRefInfo(CmpXChg1, MemoryLocation()), ModRefInfo::ModRef);EXPECT_EQ(AA.getModRefInfo(CmpXChg1, None), ModRefInfo::ModRef);EXPECT_EQ(AA.getModRefInfo(AtomicRMW, MemoryLocation()), ModRefInfo::ModRef);EXPECT_EQ(AA.getModRefInfo(AtomicRMW, None), ModRefInfo::ModRef);}static Instruction *getInstructionByName(Function &F, StringRef Name) {for (auto &I : instructions(F))if (I.getName() == Name)return &I;llvm_unreachable("Expected to find instruction!");}TEST_F(AliasAnalysisTest, BatchAAPhiCycles) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(R"(define void @f(i8* noalias %a, i1 %c) {entry:br label %looploop:%phi = phi i8* [ null, %entry ], [ %a2, %loop ]%offset1 = phi i64 [ 0, %entry ], [ %offset2, %loop]%offset2 = add i64 %offset1, 1%a1 = getelementptr i8, i8* %a, i64 %offset1%a2 = getelementptr i8, i8* %a, i64 %offset2%s1 = select i1 %c, i8* %a1, i8* %phi%s2 = select i1 %c, i8* %a2, i8* %a1br label %loop})", Err, C);Function *F = M->getFunction("f");Instruction *Phi = getInstructionByName(*F, "phi");Instruction *A1 = getInstructionByName(*F, "a1");Instruction *A2 = getInstructionByName(*F, "a2");Instruction *S1 = getInstructionByName(*F, "s1");Instruction *S2 = getInstructionByName(*F, "s2");MemoryLocation PhiLoc(Phi, LocationSize::precise(1));MemoryLocation A1Loc(A1, LocationSize::precise(1));MemoryLocation A2Loc(A2, LocationSize::precise(1));MemoryLocation S1Loc(S1, LocationSize::precise(1));MemoryLocation S2Loc(S2, LocationSize::precise(1));auto &AA = getAAResults(*F);EXPECT_EQ(AliasResult::NoAlias, AA.alias(A1Loc, A2Loc));EXPECT_EQ(AliasResult::MayAlias, AA.alias(PhiLoc, A1Loc));EXPECT_EQ(AliasResult::MayAlias, AA.alias(S1Loc, S2Loc));BatchAAResults BatchAA(AA);EXPECT_EQ(AliasResult::NoAlias, BatchAA.alias(A1Loc, A2Loc));EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(PhiLoc, A1Loc));EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(S1Loc, S2Loc));BatchAAResults BatchAA2(AA);EXPECT_EQ(AliasResult::NoAlias, BatchAA2.alias(A1Loc, A2Loc));EXPECT_EQ(AliasResult::MayAlias, BatchAA2.alias(S1Loc, S2Loc));EXPECT_EQ(AliasResult::MayAlias, BatchAA2.alias(PhiLoc, A1Loc));}TEST_F(AliasAnalysisTest, BatchAAPhiAssumption) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(R"(define void @f(i8* %a.base, i8* %b.base, i1 %c) {entry:br label %looploop:%a = phi i8* [ %a.next, %loop ], [ %a.base, %entry ]%b = phi i8* [ %b.next, %loop ], [ %b.base, %entry ]%a.next = getelementptr i8, i8* %a, i64 1%b.next = getelementptr i8, i8* %b, i64 1br label %loop})", Err, C);Function *F = M->getFunction("f");Instruction *A = getInstructionByName(*F, "a");Instruction *B = getInstructionByName(*F, "b");Instruction *ANext = getInstructionByName(*F, "a.next");Instruction *BNext = getInstructionByName(*F, "b.next");MemoryLocation ALoc(A, LocationSize::precise(1));MemoryLocation BLoc(B, LocationSize::precise(1));MemoryLocation ANextLoc(ANext, LocationSize::precise(1));MemoryLocation BNextLoc(BNext, LocationSize::precise(1));auto &AA = getAAResults(*F);EXPECT_EQ(AliasResult::MayAlias, AA.alias(ALoc, BLoc));EXPECT_EQ(AliasResult::MayAlias, AA.alias(ANextLoc, BNextLoc));BatchAAResults BatchAA(AA);EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(ALoc, BLoc));EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(ANextLoc, BNextLoc));}// Check that two aliased GEPs with non-constant offsets are correctly// analyzed and their relative offset can be requested from AA.TEST_F(AliasAnalysisTest, PartialAliasOffset) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(R"(define void @foo(float* %arg, i32 %i) {bb:%i2 = zext i32 %i to i64%i3 = getelementptr inbounds float, float* %arg, i64 %i2%i4 = bitcast float* %i3 to <2 x float>*%L1 = load <2 x float>, <2 x float>* %i4, align 16%i7 = add nuw nsw i32 %i, 1%i8 = zext i32 %i7 to i64%i9 = getelementptr inbounds float, float* %arg, i64 %i8%L2 = load float, float* %i9, align 4ret void})",Err, C);if (!M)Err.print("PartialAliasOffset", errs());Function *F = M->getFunction("foo");const auto Loc1 = MemoryLocation::get(getInstructionByName(*F, "L1"));const auto Loc2 = MemoryLocation::get(getInstructionByName(*F, "L2"));auto &AA = getAAResults(*F);const auto AR = AA.alias(Loc1, Loc2);EXPECT_EQ(AR, AliasResult::PartialAlias);EXPECT_EQ(4, AR.getOffset());}// Check that swapping the order of parameters to `AA.alias()` changes offset// sign and that the sign is such that FirstLoc + Offset == SecondLoc.TEST_F(AliasAnalysisTest, PartialAliasOffsetSign) {LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M = parseAssemblyString(R"(define void @f(i64* %p) {%L1 = load i64, i64* %p%p.i8 = bitcast i64* %p to i8*%q = getelementptr i8, i8* %p.i8, i32 1%L2 = load i8, i8* %qret void})",Err, C);if (!M)Err.print("PartialAliasOffsetSign", errs());Function *F = M->getFunction("f");const auto Loc1 = MemoryLocation::get(getInstructionByName(*F, "L1"));const auto Loc2 = MemoryLocation::get(getInstructionByName(*F, "L2"));auto &AA = getAAResults(*F);auto AR = AA.alias(Loc1, Loc2);EXPECT_EQ(AR, AliasResult::PartialAlias);EXPECT_EQ(1, AR.getOffset());AR = AA.alias(Loc2, Loc1);EXPECT_EQ(AR, AliasResult::PartialAlias);EXPECT_EQ(-1, AR.getOffset());}class AAPassInfraTest : public testing::Test {protected:LLVMContext C;SMDiagnostic Err;std::unique_ptr<Module> M;public:AAPassInfraTest(): M(parseAssemblyString("define i32 @f(i32* %x, i32* %y) {\n""entry:\n"" %lx = load i32, i32* %x\n"" %ly = load i32, i32* %y\n"" %sum = add i32 %lx, %ly\n"" ret i32 %sum\n""}\n",Err, C)) {assert(M && "Failed to build the module!");}};TEST_F(AAPassInfraTest, injectExternalAA) {legacy::PassManager PM;// Register our custom AA's wrapper pass manually.bool IsCustomAAQueried = false;PM.add(new TestCustomAAWrapperPass([&] { IsCustomAAQueried = true; }));// Now add the external AA wrapper with a lambda which queries for the// wrapper around our custom AA and adds it to the results.PM.add(createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) {if (auto *WrapperPass = P.getAnalysisIfAvailable<TestCustomAAWrapperPass>())AAR.addAAResult(WrapperPass->getResult());}));// And run a pass that will make some alias queries. This will automatically// trigger the rest of the alias analysis stack to be run. It is analagous to// building a full pass pipeline with any of the existing pass manager// builders.PM.add(new AATestPass());PM.run(*M);// Finally, ensure that our custom AA was indeed queried.EXPECT_TRUE(IsCustomAAQueried);}} // end anonymous namspace
//===- TypeTraitsTest.cpp - type_traits unit tests ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"using namespace llvm;//===----------------------------------------------------------------------===//// function_traits//===----------------------------------------------------------------------===//namespace {/// Check a callable type of the form `bool(const int &)`.template <typename CallableT> struct CheckFunctionTraits {static_assert(std::is_same<typename function_traits<CallableT>::result_t, bool>::value,"expected result_t to be `bool`");static_assert(std::is_same<typename function_traits<CallableT>::template arg_t<0>,const int &>::value,"expected arg_t<0> to be `const int &`");static_assert(function_traits<CallableT>::num_args == 1,"expected num_args to be 1");};/// Test function pointers.using FuncType = bool (*)(const int &);struct CheckFunctionPointer : CheckFunctionTraits<FuncType> {};/// Test method pointers.struct Foo {bool func(const int &v);};struct CheckMethodPointer : CheckFunctionTraits<decltype(&Foo::func)> {};/// Test lambda references.LLVM_ATTRIBUTE_UNUSED auto lambdaFunc = [](const int &v) -> bool {return true;};struct CheckLambda : CheckFunctionTraits<decltype(lambdaFunc)> {};} // end anonymous namespace//===----------------------------------------------------------------------===//// is_detected//===----------------------------------------------------------------------===//namespace {struct HasFooMethod {void foo() {}};struct NoFooMethod {};template <class T> using has_foo_method_t = decltype(std::declval<T &>().foo());static_assert(is_detected<has_foo_method_t, HasFooMethod>::value,"expected foo method to be detected");static_assert(!is_detected<has_foo_method_t, NoFooMethod>::value,"expected no foo method to be detected");} // end anonymous namespace//===----------------------------------------------------------------------===//// is_invocable//===----------------------------------------------------------------------===//void invocable_fn(int);static_assert(is_invocable<decltype(invocable_fn), int>::value,"expected function to be invocable");static_assert(!is_invocable<decltype(invocable_fn), void *>::value,"expected function not to be invocable");static_assert(!is_invocable<decltype(invocable_fn), int, int>::value,"expected function not to be invocable");
//===- TypeSwitchTest.cpp - TypeSwitch unit tests -------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/TypeSwitch.h"#include "gtest/gtest.h"using namespace llvm;namespace {/// Utility classes to setup casting functionality.struct Base {enum Kind { DerivedA, DerivedB, DerivedC, DerivedD, DerivedE };Kind kind;};template <Base::Kind DerivedKind> struct DerivedImpl : Base {DerivedImpl() : Base{DerivedKind} {}static bool classof(const Base *base) { return base->kind == DerivedKind; }};struct DerivedA : public DerivedImpl<Base::DerivedA> {};struct DerivedB : public DerivedImpl<Base::DerivedB> {};struct DerivedC : public DerivedImpl<Base::DerivedC> {};struct DerivedD : public DerivedImpl<Base::DerivedD> {};struct DerivedE : public DerivedImpl<Base::DerivedE> {};} // end anonymous namespaceTEST(TypeSwitchTest, CaseResult) {auto translate = [](auto value) {return TypeSwitch<Base *, int>(&value).Case<DerivedA>([](DerivedA *) { return 0; }).Case([](DerivedB *) { return 1; }).Case([](DerivedC *) { return 2; }).Default([](Base *) { return -1; });};EXPECT_EQ(0, translate(DerivedA()));EXPECT_EQ(1, translate(DerivedB()));EXPECT_EQ(2, translate(DerivedC()));EXPECT_EQ(-1, translate(DerivedD()));}TEST(TypeSwitchTest, CasesResult) {auto translate = [](auto value) {return TypeSwitch<Base *, int>(&value).Case<DerivedA, DerivedB, DerivedD>([](auto *) { return 0; }).Case([](DerivedC *) { return 1; }).Default(-1);};EXPECT_EQ(0, translate(DerivedA()));EXPECT_EQ(0, translate(DerivedB()));EXPECT_EQ(1, translate(DerivedC()));EXPECT_EQ(0, translate(DerivedD()));EXPECT_EQ(-1, translate(DerivedE()));}TEST(TypeSwitchTest, CaseVoid) {auto translate = [](auto value) {int result = -2;TypeSwitch<Base *>(&value).Case([&](DerivedA *) { result = 0; }).Case([&](DerivedB *) { result = 1; }).Case([&](DerivedC *) { result = 2; }).Default([&](Base *) { result = -1; });return result;};EXPECT_EQ(0, translate(DerivedA()));EXPECT_EQ(1, translate(DerivedB()));EXPECT_EQ(2, translate(DerivedC()));EXPECT_EQ(-1, translate(DerivedD()));}TEST(TypeSwitchTest, CasesVoid) {auto translate = [](auto value) {int result = -1;TypeSwitch<Base *>(&value).Case<DerivedA, DerivedB, DerivedD>([&](auto *) { result = 0; }).Case([&](DerivedC *) { result = 1; });return result;};EXPECT_EQ(0, translate(DerivedA()));EXPECT_EQ(0, translate(DerivedB()));EXPECT_EQ(1, translate(DerivedC()));EXPECT_EQ(0, translate(DerivedD()));EXPECT_EQ(-1, translate(DerivedE()));}
//===- TwineTest.cpp - Twine unit tests -----------------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Twine.h"#include "llvm/ADT/SmallString.h"#include "llvm/Support/FormatAdapters.h"#include "llvm/Support/FormatVariadic.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace {std::string repr(const Twine &Value) {std::string res;llvm::raw_string_ostream OS(res);Value.printRepr(OS);return OS.str();}TEST(TwineTest, Construction) {EXPECT_EQ("", Twine().str());EXPECT_EQ("hi", Twine("hi").str());EXPECT_EQ("hi", Twine(std::string("hi")).str());EXPECT_EQ("hi", Twine(StringRef("hi")).str());EXPECT_EQ("hi", Twine(StringRef(std::string("hi"))).str());EXPECT_EQ("hi", Twine(StringRef("hithere", 2)).str());EXPECT_EQ("hi", Twine(SmallString<4>("hi")).str());EXPECT_EQ("hi", Twine(formatv("{0}", "hi")).str());#if __cplusplus > 201402LEXPECT_EQ("hi", Twine(std::string_view("hi")).str());#endif}TEST(TwineTest, Numbers) {EXPECT_EQ("123", Twine(123U).str());EXPECT_EQ("123", Twine(123).str());EXPECT_EQ("-123", Twine(-123).str());EXPECT_EQ("123", Twine(123).str());EXPECT_EQ("-123", Twine(-123).str());EXPECT_EQ("7b", Twine::utohexstr(123).str());}TEST(TwineTest, Characters) {EXPECT_EQ("x", Twine('x').str());EXPECT_EQ("x", Twine(static_cast<unsigned char>('x')).str());EXPECT_EQ("x", Twine(static_cast<signed char>('x')).str());}TEST(TwineTest, Concat) {// Check verse repr, since we care about the actual representation not just// the result.// Concat with null.EXPECT_EQ("(Twine null empty)",repr(Twine("hi").concat(Twine::createNull())));EXPECT_EQ("(Twine null empty)",repr(Twine::createNull().concat(Twine("hi"))));// Concat with empty.EXPECT_EQ("(Twine cstring:\"hi\" empty)",repr(Twine("hi").concat(Twine())));EXPECT_EQ("(Twine cstring:\"hi\" empty)",repr(Twine().concat(Twine("hi"))));EXPECT_EQ("(Twine ptrAndLength:\"hi\" empty)",repr(Twine().concat(Twine(SmallString<5>("hi")))));EXPECT_EQ("(Twine formatv:\"howdy\" empty)",repr(Twine(formatv("howdy")).concat(Twine())));EXPECT_EQ("(Twine formatv:\"howdy\" empty)",repr(Twine().concat(Twine(formatv("howdy")))));EXPECT_EQ("(Twine ptrAndLength:\"hey\" cstring:\"there\")",repr(Twine(SmallString<7>("hey")).concat(Twine("there"))));#if __cplusplus > 201402LEXPECT_EQ("(Twine ptrAndLength:\"hey\" cstring:\"there\")",repr(Twine(std::string_view("hey")).concat(Twine("there"))));#endif// Concatenation of unary ropes.EXPECT_EQ("(Twine cstring:\"a\" cstring:\"b\")",repr(Twine("a").concat(Twine("b"))));// Concatenation of other ropes.EXPECT_EQ("(Twine rope:(Twine cstring:\"a\" cstring:\"b\") cstring:\"c\")",repr(Twine("a").concat(Twine("b")).concat(Twine("c"))));EXPECT_EQ("(Twine cstring:\"a\" rope:(Twine cstring:\"b\" cstring:\"c\"))",repr(Twine("a").concat(Twine("b").concat(Twine("c")))));EXPECT_EQ("(Twine cstring:\"a\" rope:(Twine ptrAndLength:\"b\" cstring:\"c\"))",repr(Twine("a").concat(Twine(SmallString<3>("b")).concat(Twine("c")))));}TEST(TwineTest, toNullTerminatedStringRef) {SmallString<8> storage;EXPECT_EQ(0, *Twine("hello").toNullTerminatedStringRef(storage).end());EXPECT_EQ(0,*Twine(StringRef("hello")).toNullTerminatedStringRef(storage).end());EXPECT_EQ(0, *Twine(SmallString<11>("hello")).toNullTerminatedStringRef(storage).end());EXPECT_EQ(0, *Twine(formatv("{0}{1}", "how", "dy")).toNullTerminatedStringRef(storage).end());}TEST(TwineTest, LazyEvaluation) {struct formatter : FormatAdapter<int> {explicit formatter(int &Count) : FormatAdapter(0), Count(Count) {}int &Count;void format(raw_ostream &OS, StringRef Style) override { ++Count; }};int Count = 0;formatter Formatter(Count);(void)Twine(formatv("{0}", Formatter));EXPECT_EQ(0, Count);(void)Twine(formatv("{0}", Formatter)).str();EXPECT_EQ(1, Count);}// I suppose linking in the entire code generator to add a unit test to check// the code size of the concat operation is overkill... :)} // end anonymous namespace
//===----------- Triple.cpp - Triple unit tests ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Triple.h"#include "llvm/Support/VersionTuple.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(TripleTest, BasicParsing) {Triple T;T = Triple("");EXPECT_EQ("", T.getArchName().str());EXPECT_EQ("", T.getVendorName().str());EXPECT_EQ("", T.getOSName().str());EXPECT_EQ("", T.getEnvironmentName().str());T = Triple("-");EXPECT_EQ("", T.getArchName().str());EXPECT_EQ("", T.getVendorName().str());EXPECT_EQ("", T.getOSName().str());EXPECT_EQ("", T.getEnvironmentName().str());T = Triple("--");EXPECT_EQ("", T.getArchName().str());EXPECT_EQ("", T.getVendorName().str());EXPECT_EQ("", T.getOSName().str());EXPECT_EQ("", T.getEnvironmentName().str());T = Triple("---");EXPECT_EQ("", T.getArchName().str());EXPECT_EQ("", T.getVendorName().str());EXPECT_EQ("", T.getOSName().str());EXPECT_EQ("", T.getEnvironmentName().str());T = Triple("----");EXPECT_EQ("", T.getArchName().str());EXPECT_EQ("", T.getVendorName().str());EXPECT_EQ("", T.getOSName().str());EXPECT_EQ("-", T.getEnvironmentName().str());T = Triple("a");EXPECT_EQ("a", T.getArchName().str());EXPECT_EQ("", T.getVendorName().str());EXPECT_EQ("", T.getOSName().str());EXPECT_EQ("", T.getEnvironmentName().str());T = Triple("a-b");EXPECT_EQ("a", T.getArchName().str());EXPECT_EQ("b", T.getVendorName().str());EXPECT_EQ("", T.getOSName().str());EXPECT_EQ("", T.getEnvironmentName().str());T = Triple("a-b-c");EXPECT_EQ("a", T.getArchName().str());EXPECT_EQ("b", T.getVendorName().str());EXPECT_EQ("c", T.getOSName().str());EXPECT_EQ("", T.getEnvironmentName().str());T = Triple("a-b-c-d");EXPECT_EQ("a", T.getArchName().str());EXPECT_EQ("b", T.getVendorName().str());EXPECT_EQ("c", T.getOSName().str());EXPECT_EQ("d", T.getEnvironmentName().str());}TEST(TripleTest, ParsedIDs) {Triple T;T = Triple("i386-apple-darwin");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::Apple, T.getVendor());EXPECT_EQ(Triple::Darwin, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("i386-pc-elfiamcu");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::ELFIAMCU, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("i386-pc-contiki-unknown");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Contiki, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("i386-pc-hurd-gnu");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Hurd, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());T = Triple("x86_64-pc-linux-gnu");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());T = Triple("x86_64-pc-linux-musl");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::Musl, T.getEnvironment());T = Triple("x86_64-pc-linux-muslx32");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::MuslX32, T.getEnvironment());T = Triple("arm-unknown-linux-android16");EXPECT_EQ(Triple::arm, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::Android, T.getEnvironment());T = Triple("aarch64-unknown-linux-android21");EXPECT_EQ(Triple::aarch64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::Android, T.getEnvironment());// PS4 has two spellings for the vendor.T = Triple("x86_64-scei-ps4");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::SCEI, T.getVendor());EXPECT_EQ(Triple::PS4, T.getOS());T = Triple("x86_64-sie-ps4");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::SCEI, T.getVendor());EXPECT_EQ(Triple::PS4, T.getOS());T = Triple("x86_64-sie-ps5");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::SCEI, T.getVendor());EXPECT_EQ(Triple::PS5, T.getOS());T = Triple("powerpc-ibm-aix");EXPECT_EQ(Triple::ppc, T.getArch());EXPECT_EQ(Triple::IBM, T.getVendor());EXPECT_EQ(Triple::AIX, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("powerpc64-ibm-aix");EXPECT_EQ(Triple::ppc64, T.getArch());EXPECT_EQ(Triple::IBM, T.getVendor());EXPECT_EQ(Triple::AIX, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("powerpc-dunno-notsure");EXPECT_EQ(Triple::ppc, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("powerpcspe-unknown-freebsd");EXPECT_EQ(Triple::ppc, T.getArch());EXPECT_EQ(Triple::PPCSubArch_spe, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::FreeBSD, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("s390x-ibm-zos");EXPECT_EQ(Triple::systemz, T.getArch());EXPECT_EQ(Triple::IBM, T.getVendor());EXPECT_EQ(Triple::ZOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("systemz-ibm-zos");EXPECT_EQ(Triple::systemz, T.getArch());EXPECT_EQ(Triple::IBM, T.getVendor());EXPECT_EQ(Triple::ZOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("arm-none-none-eabi");EXPECT_EQ(Triple::arm, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::EABI, T.getEnvironment());T = Triple("arm-none-linux-musleabi");EXPECT_EQ(Triple::arm, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::MuslEABI, T.getEnvironment());T = Triple("armv6hl-none-linux-gnueabi");EXPECT_EQ(Triple::arm, T.getArch());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUEABI, T.getEnvironment());T = Triple("armv7hl-none-linux-gnueabi");EXPECT_EQ(Triple::arm, T.getArch());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUEABI, T.getEnvironment());T = Triple("amdil-unknown-unknown");EXPECT_EQ(Triple::amdil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("amdil64-unknown-unknown");EXPECT_EQ(Triple::amdil64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("hsail-unknown-unknown");EXPECT_EQ(Triple::hsail, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("hsail64-unknown-unknown");EXPECT_EQ(Triple::hsail64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("sparcel-unknown-unknown");EXPECT_EQ(Triple::sparcel, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spir-unknown-unknown");EXPECT_EQ(Triple::spir, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spir64-unknown-unknown");EXPECT_EQ(Triple::spir64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv32-unknown-unknown");EXPECT_EQ(Triple::spirv32, T.getArch());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv32v1.0-unknown-unknown");EXPECT_EQ(Triple::spirv32, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v10, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv32v1.1-unknown-unknown");EXPECT_EQ(Triple::spirv32, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v11, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv32v1.2-unknown-unknown");EXPECT_EQ(Triple::spirv32, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v12, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv32v1.3-unknown-unknown");EXPECT_EQ(Triple::spirv32, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v13, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv32v1.4-unknown-unknown");EXPECT_EQ(Triple::spirv32, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v14, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv32v1.5-unknown-unknown");EXPECT_EQ(Triple::spirv32, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v15, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv64-unknown-unknown");EXPECT_EQ(Triple::spirv64, T.getArch());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv64v1.0-unknown-unknown");EXPECT_EQ(Triple::spirv64, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v10, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv64v1.1-unknown-unknown");EXPECT_EQ(Triple::spirv64, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v11, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv64v1.2-unknown-unknown");EXPECT_EQ(Triple::spirv64, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v12, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv64v1.3-unknown-unknown");EXPECT_EQ(Triple::spirv64, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v13, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv64v1.4-unknown-unknown");EXPECT_EQ(Triple::spirv64, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v14, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("spirv64v1.5-unknown-unknown");EXPECT_EQ(Triple::spirv64, T.getArch());EXPECT_EQ(Triple::SPIRVSubArch_v15, T.getSubArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());T = Triple("x86_64-unknown-ananas");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Ananas, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("x86_64-unknown-cloudabi");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::CloudABI, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("x86_64-unknown-fuchsia");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Fuchsia, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("x86_64-unknown-hermit");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::HermitCore, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("wasm32-unknown-unknown");EXPECT_EQ(Triple::wasm32, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("wasm32-unknown-wasi");EXPECT_EQ(Triple::wasm32, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::WASI, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("wasm64-unknown-unknown");EXPECT_EQ(Triple::wasm64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("wasm64-unknown-wasi");EXPECT_EQ(Triple::wasm64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::WASI, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("avr-unknown-unknown");EXPECT_EQ(Triple::avr, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("avr");EXPECT_EQ(Triple::avr, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("lanai-unknown-unknown");EXPECT_EQ(Triple::lanai, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("lanai");EXPECT_EQ(Triple::lanai, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("amdgcn-mesa-mesa3d");EXPECT_EQ(Triple::amdgcn, T.getArch());EXPECT_EQ(Triple::Mesa, T.getVendor());EXPECT_EQ(Triple::Mesa3D, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("amdgcn-amd-amdhsa");EXPECT_EQ(Triple::amdgcn, T.getArch());EXPECT_EQ(Triple::AMD, T.getVendor());EXPECT_EQ(Triple::AMDHSA, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("amdgcn-amd-amdpal");EXPECT_EQ(Triple::amdgcn, T.getArch());EXPECT_EQ(Triple::AMD, T.getVendor());EXPECT_EQ(Triple::AMDPAL, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("ve-unknown-linux");EXPECT_EQ(Triple::ve, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("csky-unknown-unknown");EXPECT_EQ(Triple::csky, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("csky-unknown-linux");EXPECT_EQ(Triple::csky, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("loongarch32-unknown-unknown");EXPECT_EQ(Triple::loongarch32, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("loongarch64-unknown-linux");EXPECT_EQ(Triple::loongarch64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("riscv32-unknown-unknown");EXPECT_EQ(Triple::riscv32, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("riscv64-unknown-linux");EXPECT_EQ(Triple::riscv64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("riscv64-unknown-freebsd");EXPECT_EQ(Triple::riscv64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::FreeBSD, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("riscv64-suse-linux");EXPECT_EQ(Triple::riscv64, T.getArch());EXPECT_EQ(Triple::SUSE, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("armv7hl-suse-linux-gnueabi");EXPECT_EQ(Triple::arm, T.getArch());EXPECT_EQ(Triple::SUSE, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUEABI, T.getEnvironment());T = Triple("i586-pc-haiku");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Haiku, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("x86_64-unknown-haiku");EXPECT_EQ(Triple::x86_64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Haiku, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T = Triple("mips-mti-linux-gnu");EXPECT_EQ(Triple::mips, T.getArch());EXPECT_EQ(Triple::MipsTechnologies, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());T = Triple("mipsel-img-linux-gnu");EXPECT_EQ(Triple::mipsel, T.getArch());EXPECT_EQ(Triple::ImaginationTechnologies, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());T = Triple("mips64-mti-linux-gnu");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::MipsTechnologies, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());T = Triple("mips64el-img-linux-gnu");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::ImaginationTechnologies, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());T = Triple("mips64el-img-linux-gnuabin32");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::ImaginationTechnologies, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());T = Triple("mips64el-unknown-linux-gnuabi64");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mips64el");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mips64-unknown-linux-gnuabi64");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mips64");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mipsisa64r6el-unknown-linux-gnuabi64");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mips64r6el");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsisa64r6el");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsisa64r6-unknown-linux-gnuabi64");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mips64r6");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsisa64r6");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mips64el-unknown-linux-gnuabin32");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mipsn32el");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mips64-unknown-linux-gnuabin32");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mipsn32");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mipsisa64r6el-unknown-linux-gnuabin32");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsn32r6el");EXPECT_EQ(Triple::mips64el, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsisa64r6-unknown-linux-gnuabin32");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsn32r6");EXPECT_EQ(Triple::mips64, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsel-unknown-linux-gnu");EXPECT_EQ(Triple::mipsel, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mipsel");EXPECT_EQ(Triple::mipsel, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mips-unknown-linux-gnu");EXPECT_EQ(Triple::mips, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mips");EXPECT_EQ(Triple::mips, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::NoSubArch, T.getSubArch());T = Triple("mipsisa32r6el-unknown-linux-gnu");EXPECT_EQ(Triple::mipsel, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsr6el");EXPECT_EQ(Triple::mipsel, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsisa32r6el");EXPECT_EQ(Triple::mipsel, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsisa32r6-unknown-linux-gnu");EXPECT_EQ(Triple::mips, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsr6");EXPECT_EQ(Triple::mips, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("mipsisa32r6");EXPECT_EQ(Triple::mips, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::GNU, T.getEnvironment());EXPECT_EQ(Triple::MipsSubArch_r6, T.getSubArch());T = Triple("arm-oe-linux-gnueabi");EXPECT_EQ(Triple::arm, T.getArch());EXPECT_EQ(Triple::OpenEmbedded, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::GNUEABI, T.getEnvironment());T = Triple("aarch64-oe-linux");EXPECT_EQ(Triple::aarch64, T.getArch());EXPECT_EQ(Triple::OpenEmbedded, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());EXPECT_TRUE(T.isArch64Bit());T = Triple("arm64_32-apple-ios");EXPECT_EQ(Triple::aarch64_32, T.getArch());EXPECT_EQ(Triple::IOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());EXPECT_TRUE(T.isArch32Bit());T = Triple("dxil-unknown-shadermodel-pixel");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Pixel, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-vertex");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Vertex, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-geometry");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Geometry, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-hull");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Hull, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-domain");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Domain, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-compute");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Compute, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-library");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Library, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-raygeneration");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::RayGeneration, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-intersection");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Intersection, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-anyhit");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::AnyHit, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-closesthit");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::ClosestHit, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-miss");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Miss, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-callable");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Callable, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-mesh");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Mesh, T.getEnvironment());T = Triple("dxil-unknown-shadermodel-amplification");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());EXPECT_EQ(Triple::Amplification, T.getEnvironment());T = Triple("huh");EXPECT_EQ(Triple::UnknownArch, T.getArch());}static std::string Join(StringRef A, StringRef B, StringRef C) {std::string Str = std::string(A);Str += '-';Str += B;Str += '-';Str += C;return Str;}static std::string Join(StringRef A, StringRef B, StringRef C, StringRef D) {std::string Str = std::string(A);Str += '-';Str += B;Str += '-';Str += C;Str += '-';Str += D;return Str;}TEST(TripleTest, Normalization) {EXPECT_EQ("unknown", Triple::normalize(""));EXPECT_EQ("unknown-unknown", Triple::normalize("-"));EXPECT_EQ("unknown-unknown-unknown", Triple::normalize("--"));EXPECT_EQ("unknown-unknown-unknown-unknown", Triple::normalize("---"));EXPECT_EQ("unknown-unknown-unknown-unknown-unknown",Triple::normalize("----"));EXPECT_EQ("a", Triple::normalize("a"));EXPECT_EQ("a-b", Triple::normalize("a-b"));EXPECT_EQ("a-b-c", Triple::normalize("a-b-c"));EXPECT_EQ("a-b-c-d", Triple::normalize("a-b-c-d"));EXPECT_EQ("i386-b-c", Triple::normalize("i386-b-c"));EXPECT_EQ("i386-a-c", Triple::normalize("a-i386-c"));EXPECT_EQ("i386-a-b", Triple::normalize("a-b-i386"));EXPECT_EQ("i386-a-b-c", Triple::normalize("a-b-c-i386"));EXPECT_EQ("a-pc-c", Triple::normalize("a-pc-c"));EXPECT_EQ("unknown-pc-b-c", Triple::normalize("pc-b-c"));EXPECT_EQ("a-pc-b", Triple::normalize("a-b-pc"));EXPECT_EQ("a-pc-b-c", Triple::normalize("a-b-c-pc"));EXPECT_EQ("a-b-linux", Triple::normalize("a-b-linux"));EXPECT_EQ("unknown-unknown-linux-b-c", Triple::normalize("linux-b-c"));EXPECT_EQ("a-unknown-linux-c", Triple::normalize("a-linux-c"));EXPECT_EQ("i386-pc-a", Triple::normalize("a-pc-i386"));EXPECT_EQ("i386-pc-unknown", Triple::normalize("-pc-i386"));EXPECT_EQ("unknown-pc-linux-c", Triple::normalize("linux-pc-c"));EXPECT_EQ("unknown-pc-linux", Triple::normalize("linux-pc-"));EXPECT_EQ("i386", Triple::normalize("i386"));EXPECT_EQ("unknown-pc", Triple::normalize("pc"));EXPECT_EQ("unknown-unknown-linux", Triple::normalize("linux"));EXPECT_EQ("x86_64-unknown-linux-gnu", Triple::normalize("x86_64-gnu-linux"));// Check that normalizing a permutated set of valid components returns a// triple with the unpermuted components.//// We don't check every possible combination. For the set of architectures A,// vendors V, operating systems O, and environments E, that would require |A|// * |V| * |O| * |E| * 4! tests. Instead we check every option for any given// slot and make sure it gets normalized to the correct position from every// permutation. This should cover the core logic while being a tractable// number of tests at (|A| + |V| + |O| + |E|) * 4!.auto FirstArchType = Triple::ArchType(Triple::UnknownArch + 1);auto FirstVendorType = Triple::VendorType(Triple::UnknownVendor + 1);auto FirstOSType = Triple::OSType(Triple::UnknownOS + 1);auto FirstEnvType = Triple::EnvironmentType(Triple::UnknownEnvironment + 1);StringRef InitialC[] = {Triple::getArchTypeName(FirstArchType),Triple::getVendorTypeName(FirstVendorType),Triple::getOSTypeName(FirstOSType),Triple::getEnvironmentTypeName(FirstEnvType)};for (int Arch = FirstArchType; Arch <= Triple::LastArchType; ++Arch) {StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]};C[0] = Triple::getArchTypeName(Triple::ArchType(Arch));std::string E = Join(C[0], C[1], C[2]);int I[] = {0, 1, 2};do {EXPECT_EQ(E, Triple::normalize(Join(C[I[0]], C[I[1]], C[I[2]])));} while (std::next_permutation(std::begin(I), std::end(I)));std::string F = Join(C[0], C[1], C[2], C[3]);int J[] = {0, 1, 2, 3};do {EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]])));} while (std::next_permutation(std::begin(J), std::end(J)));}for (int Vendor = FirstVendorType; Vendor <= Triple::LastVendorType;++Vendor) {StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]};C[1] = Triple::getVendorTypeName(Triple::VendorType(Vendor));std::string E = Join(C[0], C[1], C[2]);int I[] = {0, 1, 2};do {EXPECT_EQ(E, Triple::normalize(Join(C[I[0]], C[I[1]], C[I[2]])));} while (std::next_permutation(std::begin(I), std::end(I)));std::string F = Join(C[0], C[1], C[2], C[3]);int J[] = {0, 1, 2, 3};do {EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]])));} while (std::next_permutation(std::begin(J), std::end(J)));}for (int OS = FirstOSType; OS <= Triple::LastOSType; ++OS) {if (OS == Triple::Win32)continue;StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]};C[2] = Triple::getOSTypeName(Triple::OSType(OS));std::string E = Join(C[0], C[1], C[2]);int I[] = {0, 1, 2};do {EXPECT_EQ(E, Triple::normalize(Join(C[I[0]], C[I[1]], C[I[2]])));} while (std::next_permutation(std::begin(I), std::end(I)));std::string F = Join(C[0], C[1], C[2], C[3]);int J[] = {0, 1, 2, 3};do {EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]])));} while (std::next_permutation(std::begin(J), std::end(J)));}for (int Env = FirstEnvType; Env <= Triple::LastEnvironmentType; ++Env) {StringRef C[] = {InitialC[0], InitialC[1], InitialC[2], InitialC[3]};C[3] = Triple::getEnvironmentTypeName(Triple::EnvironmentType(Env));std::string F = Join(C[0], C[1], C[2], C[3]);int J[] = {0, 1, 2, 3};do {EXPECT_EQ(F, Triple::normalize(Join(C[J[0]], C[J[1]], C[J[2]], C[J[3]])));} while (std::next_permutation(std::begin(J), std::end(J)));}// Various real-world funky triples. The value returned by GCC's config.sub// is given in the comment.EXPECT_EQ("i386-unknown-windows-gnu",Triple::normalize("i386-mingw32")); // i386-pc-mingw32EXPECT_EQ("x86_64-unknown-linux-gnu",Triple::normalize("x86_64-linux-gnu")); // x86_64-pc-linux-gnuEXPECT_EQ("i486-unknown-linux-gnu",Triple::normalize("i486-linux-gnu")); // i486-pc-linux-gnuEXPECT_EQ("i386-redhat-linux",Triple::normalize("i386-redhat-linux")); // i386-redhat-linux-gnuEXPECT_EQ("i686-unknown-linux",Triple::normalize("i686-linux")); // i686-pc-linux-gnuEXPECT_EQ("arm-none-unknown-eabi",Triple::normalize("arm-none-eabi")); // arm-none-eabiEXPECT_EQ("ve-unknown-linux",Triple::normalize("ve-linux")); // ve-linuxEXPECT_EQ("wasm32-unknown-wasi",Triple::normalize("wasm32-wasi")); // wasm32-unknown-wasiEXPECT_EQ("wasm64-unknown-wasi",Triple::normalize("wasm64-wasi")); // wasm64-unknown-wasi}TEST(TripleTest, MutateName) {Triple T;EXPECT_EQ(Triple::UnknownArch, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::UnknownOS, T.getOS());EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());T.setArchName("i386");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ("i386--", T.getTriple());T.setVendorName("pc");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ("i386-pc-", T.getTriple());T.setOSName("linux");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ("i386-pc-linux", T.getTriple());T.setEnvironmentName("gnu");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Linux, T.getOS());EXPECT_EQ("i386-pc-linux-gnu", T.getTriple());T.setOSName("freebsd");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::FreeBSD, T.getOS());EXPECT_EQ("i386-pc-freebsd-gnu", T.getTriple());T.setOSAndEnvironmentName("darwin");EXPECT_EQ(Triple::x86, T.getArch());EXPECT_EQ(Triple::PC, T.getVendor());EXPECT_EQ(Triple::Darwin, T.getOS());EXPECT_EQ("i386-pc-darwin", T.getTriple());}TEST(TripleTest, BitWidthPredicates) {Triple T;EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::arm);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::hexagon);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::mips);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::mips64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::msp430);EXPECT_TRUE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::ppc);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::ppc64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::x86);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::x86_64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::amdil);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::amdil64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::hsail);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::hsail64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::spir);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::spir64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::spirv32);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());EXPECT_TRUE(T.isSPIRV());T.setArch(Triple::spirv64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());EXPECT_TRUE(T.isSPIRV());T.setArch(Triple::sparc);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::sparcel);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::sparcv9);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::wasm32);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::wasm64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.setArch(Triple::avr);EXPECT_TRUE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::lanai);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.setArch(Triple::riscv32);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());EXPECT_TRUE(T.isRISCV());T.setArch(Triple::riscv64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());EXPECT_TRUE(T.isRISCV());T.setArch(Triple::csky);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());EXPECT_TRUE(T.isCSKY());T.setArch(Triple::loongarch32);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());EXPECT_TRUE(T.isLoongArch());T.setArch(Triple::loongarch64);EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());EXPECT_TRUE(T.isLoongArch());T.setArch(Triple::dxil);EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());EXPECT_TRUE(T.isDXIL());}TEST(TripleTest, BitWidthArchVariants) {Triple T;EXPECT_EQ(Triple::UnknownArch, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch());T.setArch(Triple::UnknownArch);EXPECT_EQ(Triple::UnknownArch, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch());T.setArch(Triple::mips);EXPECT_EQ(Triple::mips, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get64BitArchVariant().getSubArch());T.setArch(Triple::mips, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mips, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get64BitArchVariant().getSubArch());T.setArch(Triple::mipsel);EXPECT_EQ(Triple::mipsel, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get64BitArchVariant().getSubArch());T.setArch(Triple::mipsel, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mipsel, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get64BitArchVariant().getSubArch());T.setArch(Triple::ppc);EXPECT_EQ(Triple::ppc, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::ppc64, T.get64BitArchVariant().getArch());T.setArch(Triple::nvptx);EXPECT_EQ(Triple::nvptx, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::nvptx64, T.get64BitArchVariant().getArch());T.setArch(Triple::sparc);EXPECT_EQ(Triple::sparc, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::sparcv9, T.get64BitArchVariant().getArch());T.setArch(Triple::x86);EXPECT_EQ(Triple::x86, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::x86_64, T.get64BitArchVariant().getArch());T.setArch(Triple::mips64);EXPECT_EQ(Triple::mips, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get64BitArchVariant().getSubArch());T.setArch(Triple::mips64, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mips, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get64BitArchVariant().getSubArch());T.setArch(Triple::mips64el);EXPECT_EQ(Triple::mipsel, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.get64BitArchVariant().getSubArch());T.setArch(Triple::mips64el, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mipsel, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get32BitArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.get64BitArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.get64BitArchVariant().getSubArch());T.setArch(Triple::ppc64);EXPECT_EQ(Triple::ppc, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::ppc64, T.get64BitArchVariant().getArch());T.setArch(Triple::nvptx64);EXPECT_EQ(Triple::nvptx, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::nvptx64, T.get64BitArchVariant().getArch());T.setArch(Triple::sparcv9);EXPECT_EQ(Triple::sparc, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::sparcv9, T.get64BitArchVariant().getArch());T.setArch(Triple::x86_64);EXPECT_EQ(Triple::x86, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::x86_64, T.get64BitArchVariant().getArch());T.setArch(Triple::amdil);EXPECT_EQ(Triple::amdil, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::amdil64, T.get64BitArchVariant().getArch());T.setArch(Triple::amdil64);EXPECT_EQ(Triple::amdil, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::amdil64, T.get64BitArchVariant().getArch());T.setArch(Triple::hsail);EXPECT_EQ(Triple::hsail, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::hsail64, T.get64BitArchVariant().getArch());T.setArch(Triple::hsail64);EXPECT_EQ(Triple::hsail, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::hsail64, T.get64BitArchVariant().getArch());T.setArch(Triple::spir);EXPECT_EQ(Triple::spir, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::spir64, T.get64BitArchVariant().getArch());T.setArch(Triple::spir64);EXPECT_EQ(Triple::spir, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::spir64, T.get64BitArchVariant().getArch());T.setArch(Triple::spirv32);EXPECT_EQ(Triple::spirv32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::spirv64, T.get64BitArchVariant().getArch());T.setArch(Triple::spirv64);EXPECT_EQ(Triple::spirv32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::spirv64, T.get64BitArchVariant().getArch());T.setArch(Triple::wasm32);EXPECT_EQ(Triple::wasm32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::wasm64, T.get64BitArchVariant().getArch());T.setArch(Triple::wasm64);EXPECT_EQ(Triple::wasm32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::wasm64, T.get64BitArchVariant().getArch());T.setArch(Triple::riscv32);EXPECT_EQ(Triple::riscv32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::riscv64, T.get64BitArchVariant().getArch());T.setArch(Triple::riscv64);EXPECT_EQ(Triple::riscv32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::riscv64, T.get64BitArchVariant().getArch());T.setArch(Triple::csky);EXPECT_EQ(Triple::csky, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch());T.setArch(Triple::loongarch32);EXPECT_EQ(Triple::loongarch32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::loongarch64, T.get64BitArchVariant().getArch());T.setArch(Triple::loongarch64);EXPECT_EQ(Triple::loongarch32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::loongarch64, T.get64BitArchVariant().getArch());T.setArch(Triple::thumbeb);EXPECT_EQ(Triple::thumbeb, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::aarch64_be, T.get64BitArchVariant().getArch());T.setArch(Triple::thumb);EXPECT_EQ(Triple::thumb, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::aarch64, T.get64BitArchVariant().getArch());T.setArch(Triple::aarch64);EXPECT_EQ(Triple::arm, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::aarch64, T.get64BitArchVariant().getArch());T.setArch(Triple::aarch64_be);EXPECT_EQ(Triple::armeb, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::aarch64_be, T.get64BitArchVariant().getArch());T.setArch(Triple::renderscript32);EXPECT_EQ(Triple::renderscript32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::renderscript64, T.get64BitArchVariant().getArch());T.setArch(Triple::renderscript64);EXPECT_EQ(Triple::renderscript32, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::renderscript64, T.get64BitArchVariant().getArch());T.setArch(Triple::armeb);EXPECT_EQ(Triple::armeb, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::aarch64_be, T.get64BitArchVariant().getArch());T.setArch(Triple::arm);EXPECT_EQ(Triple::arm, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::aarch64, T.get64BitArchVariant().getArch());T.setArch(Triple::systemz);EXPECT_EQ(Triple::UnknownArch, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::systemz, T.get64BitArchVariant().getArch());T.setArch(Triple::xcore);EXPECT_EQ(Triple::xcore, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch());T.setArch(Triple::dxil);EXPECT_EQ(Triple::dxil, T.get32BitArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch());}TEST(TripleTest, EndianArchVariants) {Triple T;EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::UnknownArch);EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::aarch64_be);EXPECT_EQ(Triple::aarch64_be, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::aarch64, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::aarch64);EXPECT_EQ(Triple::aarch64_be, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::aarch64, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::armeb);EXPECT_EQ(Triple::armeb, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::arm);EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::arm, T.getLittleEndianArchVariant().getArch());T = Triple("arm");EXPECT_TRUE(T.isLittleEndian());T = Triple("thumb");EXPECT_TRUE(T.isLittleEndian());T = Triple("armeb");EXPECT_FALSE(T.isLittleEndian());T = Triple("thumbeb");EXPECT_FALSE(T.isLittleEndian());T.setArch(Triple::bpfeb);EXPECT_EQ(Triple::bpfeb, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::bpfel, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::bpfel);EXPECT_EQ(Triple::bpfeb, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::bpfel, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::mips64);EXPECT_EQ(Triple::mips64, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::mips64, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mips64, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6,T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::mips64el);EXPECT_EQ(Triple::mips64, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::mips64el, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mips64, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mips64el, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6,T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::mips);EXPECT_EQ(Triple::mips, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mipsel, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::mips, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mips, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mipsel, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6,T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::mipsel);EXPECT_EQ(Triple::mips, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mipsel, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::NoSubArch, T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::mipsel, Triple::MipsSubArch_r6);EXPECT_EQ(Triple::mips, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6, T.getBigEndianArchVariant().getSubArch());EXPECT_EQ(Triple::mipsel, T.getLittleEndianArchVariant().getArch());EXPECT_EQ(Triple::MipsSubArch_r6,T.getLittleEndianArchVariant().getSubArch());T.setArch(Triple::ppc);EXPECT_EQ(Triple::ppc, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::ppcle, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::ppc64);EXPECT_EQ(Triple::ppc64, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::ppc64le, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::ppc64le);EXPECT_EQ(Triple::ppc64, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::ppc64le, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::sparc);EXPECT_EQ(Triple::sparc, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::sparcel, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::sparcel);EXPECT_EQ(Triple::sparc, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::sparcel, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::thumb);EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::thumb, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::thumbeb);EXPECT_EQ(Triple::thumbeb, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::lanai);EXPECT_EQ(Triple::lanai, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::tcele);EXPECT_EQ(Triple::tce, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::tcele, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::tce);EXPECT_EQ(Triple::tce, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::tcele, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::csky);EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::csky, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::loongarch32);EXPECT_TRUE(T.isLittleEndian());EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::loongarch32, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::loongarch64);EXPECT_TRUE(T.isLittleEndian());EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::loongarch64, T.getLittleEndianArchVariant().getArch());T.setArch(Triple::dxil);EXPECT_TRUE(T.isLittleEndian());EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch());EXPECT_EQ(Triple::dxil, T.getLittleEndianArchVariant().getArch());}TEST(TripleTest, getOSVersion) {Triple T;VersionTuple Version;T = Triple("i386-apple-darwin9");EXPECT_TRUE(T.isMacOSX());EXPECT_FALSE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(10, 5), Version);Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(5), Version);T = Triple("x86_64-apple-darwin9");EXPECT_TRUE(T.isMacOSX());EXPECT_FALSE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(10, 5), Version);Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(5), Version);T = Triple("x86_64-apple-macosx");EXPECT_TRUE(T.isMacOSX());EXPECT_FALSE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(10, 4), Version);Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(5), Version);T = Triple("x86_64-apple-macosx10.7");EXPECT_TRUE(T.isMacOSX());EXPECT_FALSE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(10, 7), Version);Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(5), Version);T = Triple("x86_64-apple-macos11.0");EXPECT_TRUE(T.isMacOSX());EXPECT_FALSE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(11, 0), Version);T = Triple("arm64-apple-macosx11.5.8");EXPECT_TRUE(T.isMacOSX());EXPECT_FALSE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_FALSE(T.isArch32Bit());EXPECT_TRUE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(11, 5, 8), Version);// 10.16 forms a valid triple, even though it's not// a version of a macOS.T = Triple("x86_64-apple-macos10.16");EXPECT_TRUE(T.isMacOSX());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(10, 16), Version);T = Triple("x86_64-apple-darwin20");EXPECT_TRUE(T.isMacOSX());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(11), Version);// For darwin triples on macOS 11, only compare the major version.T = Triple("x86_64-apple-darwin20.2");EXPECT_TRUE(T.isMacOSX());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(11), Version);T = Triple("armv7-apple-ios");EXPECT_FALSE(T.isMacOSX());EXPECT_TRUE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(10, 4), Version);Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(5), Version);T = Triple("armv7-apple-ios7.0");EXPECT_FALSE(T.isMacOSX());EXPECT_TRUE(T.isiOS());EXPECT_FALSE(T.isArch16Bit());EXPECT_TRUE(T.isArch32Bit());EXPECT_FALSE(T.isArch64Bit());T.getMacOSXVersion(Version);EXPECT_EQ(VersionTuple(10, 4), Version);Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(7, 0), Version);EXPECT_FALSE(T.isSimulatorEnvironment());T = Triple("x86_64-apple-ios10.3-simulator");EXPECT_TRUE(T.isiOS());Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(10, 3), Version);EXPECT_TRUE(T.isSimulatorEnvironment());EXPECT_FALSE(T.isMacCatalystEnvironment());T = Triple("x86_64-apple-ios13.0-macabi");EXPECT_TRUE(T.isiOS());Version = T.getiOSVersion();EXPECT_EQ(VersionTuple(13, 0), Version);EXPECT_TRUE(T.getEnvironment() == Triple::MacABI);EXPECT_TRUE(T.isMacCatalystEnvironment());EXPECT_FALSE(T.isSimulatorEnvironment());T = Triple("x86_64-apple-driverkit20.1.0");EXPECT_TRUE(T.isDriverKit());EXPECT_TRUE(T.isOSDarwin());EXPECT_FALSE(T.isMacOSX());EXPECT_FALSE(T.isiOS());Version = T.getDriverKitVersion();EXPECT_EQ(VersionTuple(20, 1), Version);T = Triple("x86_64-apple-driverkit20");Version = T.getDriverKitVersion();EXPECT_EQ(VersionTuple(20, 0), Version);// DriverKit version should default to 19.0.T = Triple("x86_64-apple-driverkit");Version = T.getDriverKitVersion();EXPECT_EQ(VersionTuple(19, 0), Version);T = Triple("dxil-unknown-shadermodel6.6-pixel");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());Version = T.getOSVersion();EXPECT_EQ(VersionTuple(6, 6), Version);EXPECT_EQ(Triple::Pixel, T.getEnvironment());T = Triple("dxil-unknown-shadermodel6.0-pixel");EXPECT_EQ(Triple::dxil, T.getArch());EXPECT_EQ(Triple::UnknownVendor, T.getVendor());EXPECT_EQ(Triple::ShaderModel, T.getOS());Version = T.getOSVersion();EXPECT_EQ(VersionTuple(6, 0), Version);EXPECT_EQ(Triple::Pixel, T.getEnvironment());}TEST(TripleTest, getEnvironmentVersion) {Triple T;VersionTuple Version;T = Triple("arm-unknown-linux-android16");EXPECT_TRUE(T.isAndroid());Version = T.getEnvironmentVersion();EXPECT_EQ(VersionTuple(16), Version);EXPECT_EQ(Triple::Android, T.getEnvironment());T = Triple("aarch64-unknown-linux-android21");EXPECT_TRUE(T.isAndroid());Version = T.getEnvironmentVersion();EXPECT_EQ(VersionTuple(21), Version);EXPECT_EQ(Triple::Android, T.getEnvironment());}TEST(TripleTest, isMacOSVersionLT) {Triple T = Triple("x86_64-apple-macos11");EXPECT_TRUE(T.isMacOSXVersionLT(11, 1, 0));EXPECT_FALSE(T.isMacOSXVersionLT(10, 15, 0));T = Triple("x86_64-apple-darwin20");EXPECT_TRUE(T.isMacOSXVersionLT(11, 1, 0));EXPECT_FALSE(T.isMacOSXVersionLT(11, 0, 0));EXPECT_FALSE(T.isMacOSXVersionLT(10, 15, 0));}TEST(TripleTest, CanonicalizeOSVersion) {EXPECT_EQ(VersionTuple(10, 15, 4),Triple::getCanonicalVersionForOS(Triple::MacOSX,VersionTuple(10, 15, 4)));EXPECT_EQ(VersionTuple(11, 0), Triple::getCanonicalVersionForOS(Triple::MacOSX, VersionTuple(10, 16)));EXPECT_EQ(VersionTuple(20),Triple::getCanonicalVersionForOS(Triple::Darwin, VersionTuple(20)));}TEST(TripleTest, FileFormat) {EXPECT_EQ(Triple::ELF, Triple("i686-unknown-linux-gnu").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("i686-unknown-freebsd").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("i686-unknown-netbsd").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("i686--win32-elf").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("i686---elf").getObjectFormat());EXPECT_EQ(Triple::MachO, Triple("i686-apple-macosx").getObjectFormat());EXPECT_EQ(Triple::MachO, Triple("i686-apple-ios").getObjectFormat());EXPECT_EQ(Triple::MachO, Triple("i686---macho").getObjectFormat());EXPECT_EQ(Triple::COFF, Triple("i686--win32").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("i686-pc-windows-msvc-elf").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("i686-pc-cygwin-elf").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("systemz-ibm-linux").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("systemz-ibm-unknown").getObjectFormat());EXPECT_EQ(Triple::GOFF, Triple("s390x-ibm-zos").getObjectFormat());EXPECT_EQ(Triple::GOFF, Triple("systemz-ibm-zos").getObjectFormat());EXPECT_EQ(Triple::GOFF, Triple("s390x-ibm-zos-goff").getObjectFormat());EXPECT_EQ(Triple::GOFF, Triple("s390x-unknown-zos-goff").getObjectFormat());EXPECT_EQ(Triple::GOFF, Triple("s390x---goff").getObjectFormat());EXPECT_EQ(Triple::Wasm, Triple("wasm32-unknown-unknown").getObjectFormat());EXPECT_EQ(Triple::Wasm, Triple("wasm64-unknown-unknown").getObjectFormat());EXPECT_EQ(Triple::Wasm, Triple("wasm32-wasi").getObjectFormat());EXPECT_EQ(Triple::Wasm, Triple("wasm64-wasi").getObjectFormat());EXPECT_EQ(Triple::Wasm, Triple("wasm32-unknown-wasi").getObjectFormat());EXPECT_EQ(Triple::Wasm, Triple("wasm64-unknown-wasi").getObjectFormat());EXPECT_EQ(Triple::Wasm,Triple("wasm32-unknown-unknown-wasm").getObjectFormat());EXPECT_EQ(Triple::Wasm,Triple("wasm64-unknown-unknown-wasm").getObjectFormat());EXPECT_EQ(Triple::Wasm,Triple("wasm32-wasi-wasm").getObjectFormat());EXPECT_EQ(Triple::Wasm,Triple("wasm64-wasi-wasm").getObjectFormat());EXPECT_EQ(Triple::Wasm,Triple("wasm32-unknown-wasi-wasm").getObjectFormat());EXPECT_EQ(Triple::Wasm,Triple("wasm64-unknown-wasi-wasm").getObjectFormat());EXPECT_EQ(Triple::XCOFF, Triple("powerpc-ibm-aix").getObjectFormat());EXPECT_EQ(Triple::XCOFF, Triple("powerpc64-ibm-aix").getObjectFormat());EXPECT_EQ(Triple::XCOFF, Triple("powerpc---xcoff").getObjectFormat());EXPECT_EQ(Triple::XCOFF, Triple("powerpc64---xcoff").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("csky-unknown-unknown").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("csky-unknown-linux").getObjectFormat());EXPECT_EQ(Triple::SPIRV, Triple("spirv32-unknown-unknown").getObjectFormat());EXPECT_EQ(Triple::SPIRV, Triple("spirv64-unknown-unknown").getObjectFormat());EXPECT_EQ(Triple::ELF,Triple("loongarch32-unknown-unknown").getObjectFormat());EXPECT_EQ(Triple::ELF, Triple("loongarch64-unknown-linux").getObjectFormat());Triple MSVCNormalized(Triple::normalize("i686-pc-windows-msvc-elf"));EXPECT_EQ(Triple::ELF, MSVCNormalized.getObjectFormat());Triple GNUWindowsNormalized(Triple::normalize("i686-pc-windows-gnu-elf"));EXPECT_EQ(Triple::ELF, GNUWindowsNormalized.getObjectFormat());Triple CygnusNormalised(Triple::normalize("i686-pc-windows-cygnus-elf"));EXPECT_EQ(Triple::ELF, CygnusNormalised.getObjectFormat());Triple CygwinNormalized(Triple::normalize("i686-pc-cygwin-elf"));EXPECT_EQ(Triple::ELF, CygwinNormalized.getObjectFormat());EXPECT_EQ(Triple::DXContainer,Triple("dxil-unknown-shadermodel").getObjectFormat());Triple T = Triple("");T.setObjectFormat(Triple::ELF);EXPECT_EQ(Triple::ELF, T.getObjectFormat());T.setObjectFormat(Triple::MachO);EXPECT_EQ(Triple::MachO, T.getObjectFormat());T.setObjectFormat(Triple::XCOFF);EXPECT_EQ(Triple::XCOFF, T.getObjectFormat());T.setObjectFormat(Triple::GOFF);EXPECT_EQ(Triple::GOFF, T.getObjectFormat());T.setObjectFormat(Triple::SPIRV);EXPECT_EQ(Triple::SPIRV, T.getObjectFormat());}TEST(TripleTest, NormalizeWindows) {EXPECT_EQ("i686-pc-windows-msvc", Triple::normalize("i686-pc-win32"));EXPECT_EQ("i686-unknown-windows-msvc", Triple::normalize("i686-win32"));EXPECT_EQ("i686-pc-windows-gnu", Triple::normalize("i686-pc-mingw32"));EXPECT_EQ("i686-unknown-windows-gnu", Triple::normalize("i686-mingw32"));EXPECT_EQ("i686-pc-windows-gnu", Triple::normalize("i686-pc-mingw32-w64"));EXPECT_EQ("i686-unknown-windows-gnu", Triple::normalize("i686-mingw32-w64"));EXPECT_EQ("i686-pc-windows-cygnus", Triple::normalize("i686-pc-cygwin"));EXPECT_EQ("i686-unknown-windows-cygnus", Triple::normalize("i686-cygwin"));EXPECT_EQ("x86_64-pc-windows-msvc", Triple::normalize("x86_64-pc-win32"));EXPECT_EQ("x86_64-unknown-windows-msvc", Triple::normalize("x86_64-win32"));EXPECT_EQ("x86_64-pc-windows-gnu", Triple::normalize("x86_64-pc-mingw32"));EXPECT_EQ("x86_64-unknown-windows-gnu", Triple::normalize("x86_64-mingw32"));EXPECT_EQ("x86_64-pc-windows-gnu",Triple::normalize("x86_64-pc-mingw32-w64"));EXPECT_EQ("x86_64-unknown-windows-gnu",Triple::normalize("x86_64-mingw32-w64"));EXPECT_EQ("i686-pc-windows-elf", Triple::normalize("i686-pc-win32-elf"));EXPECT_EQ("i686-unknown-windows-elf", Triple::normalize("i686-win32-elf"));EXPECT_EQ("i686-pc-windows-macho", Triple::normalize("i686-pc-win32-macho"));EXPECT_EQ("i686-unknown-windows-macho",Triple::normalize("i686-win32-macho"));EXPECT_EQ("x86_64-pc-windows-elf", Triple::normalize("x86_64-pc-win32-elf"));EXPECT_EQ("x86_64-unknown-windows-elf",Triple::normalize("x86_64-win32-elf"));EXPECT_EQ("x86_64-pc-windows-macho",Triple::normalize("x86_64-pc-win32-macho"));EXPECT_EQ("x86_64-unknown-windows-macho",Triple::normalize("x86_64-win32-macho"));EXPECT_EQ("i686-pc-windows-cygnus",Triple::normalize("i686-pc-windows-cygnus"));EXPECT_EQ("i686-pc-windows-gnu", Triple::normalize("i686-pc-windows-gnu"));EXPECT_EQ("i686-pc-windows-itanium",Triple::normalize("i686-pc-windows-itanium"));EXPECT_EQ("i686-pc-windows-msvc", Triple::normalize("i686-pc-windows-msvc"));EXPECT_EQ("i686-pc-windows-elf",Triple::normalize("i686-pc-windows-elf-elf"));EXPECT_TRUE(Triple("x86_64-pc-win32").isWindowsMSVCEnvironment());}TEST(TripleTest, NormalizeAndroid) {EXPECT_EQ("arm-unknown-linux-android16",Triple::normalize("arm-linux-androideabi16"));EXPECT_EQ("armv7a-unknown-linux-android",Triple::normalize("armv7a-linux-androideabi"));EXPECT_EQ("aarch64-unknown-linux-android21",Triple::normalize("aarch64-linux-android21"));}TEST(TripleTest, getARMCPUForArch) {// Platform specific defaults.{llvm::Triple Triple("arm--nacl");EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch());}{llvm::Triple Triple("arm--openbsd");EXPECT_EQ("cortex-a8", Triple.getARMCPUForArch());}{llvm::Triple Triple("armv6-unknown-freebsd");EXPECT_EQ("arm1176jzf-s", Triple.getARMCPUForArch());}{llvm::Triple Triple("thumbv6-unknown-freebsd");EXPECT_EQ("arm1176jzf-s", Triple.getARMCPUForArch());}{llvm::Triple Triple("armebv6-unknown-freebsd");EXPECT_EQ("arm1176jzf-s", Triple.getARMCPUForArch());}{llvm::Triple Triple("arm--win32");EXPECT_EQ("cortex-a9", Triple.getARMCPUForArch());EXPECT_EQ("generic", Triple.getARMCPUForArch("armv8-a"));}// Some alternative architectures{llvm::Triple Triple("armv7k-apple-ios9");EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch());}{llvm::Triple Triple("armv7k-apple-watchos3");EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch());}{llvm::Triple Triple("armv7k-apple-tvos9");EXPECT_EQ("cortex-a7", Triple.getARMCPUForArch());}// armeb is permitted, but armebeb is not{llvm::Triple Triple("armeb-none-eabi");EXPECT_EQ("arm7tdmi", Triple.getARMCPUForArch());}{llvm::Triple Triple("armebeb-none-eabi");EXPECT_EQ("", Triple.getARMCPUForArch());}{llvm::Triple Triple("armebv6eb-none-eabi");EXPECT_EQ("", Triple.getARMCPUForArch());}// xscaleeb is permitted, but armebxscale is not{llvm::Triple Triple("xscaleeb-none-eabi");EXPECT_EQ("xscale", Triple.getARMCPUForArch());}{llvm::Triple Triple("armebxscale-none-eabi");EXPECT_EQ("", Triple.getARMCPUForArch());}}TEST(TripleTest, NormalizeARM) {EXPECT_EQ("armv6-unknown-netbsd-eabi",Triple::normalize("armv6-netbsd-eabi"));EXPECT_EQ("armv7-unknown-netbsd-eabi",Triple::normalize("armv7-netbsd-eabi"));EXPECT_EQ("armv6eb-unknown-netbsd-eabi",Triple::normalize("armv6eb-netbsd-eabi"));EXPECT_EQ("armv7eb-unknown-netbsd-eabi",Triple::normalize("armv7eb-netbsd-eabi"));EXPECT_EQ("armv6-unknown-netbsd-eabihf",Triple::normalize("armv6-netbsd-eabihf"));EXPECT_EQ("armv7-unknown-netbsd-eabihf",Triple::normalize("armv7-netbsd-eabihf"));EXPECT_EQ("armv6eb-unknown-netbsd-eabihf",Triple::normalize("armv6eb-netbsd-eabihf"));EXPECT_EQ("armv7eb-unknown-netbsd-eabihf",Triple::normalize("armv7eb-netbsd-eabihf"));EXPECT_EQ("armv7-suse-linux-gnueabihf",Triple::normalize("armv7-suse-linux-gnueabi"));Triple T;T = Triple("armv6--netbsd-eabi");EXPECT_EQ(Triple::arm, T.getArch());T = Triple("armv6eb--netbsd-eabi");EXPECT_EQ(Triple::armeb, T.getArch());T = Triple("armv7-suse-linux-gnueabihf");EXPECT_EQ(Triple::GNUEABIHF, T.getEnvironment());}TEST(TripleTest, ParseARMArch) {// ARM{Triple T = Triple("arm");EXPECT_EQ(Triple::arm, T.getArch());}{Triple T = Triple("armeb");EXPECT_EQ(Triple::armeb, T.getArch());}// THUMB{Triple T = Triple("thumb");EXPECT_EQ(Triple::thumb, T.getArch());}{Triple T = Triple("thumbeb");EXPECT_EQ(Triple::thumbeb, T.getArch());}// AARCH64{Triple T = Triple("arm64");EXPECT_EQ(Triple::aarch64, T.getArch());}{Triple T = Triple("arm64_32");EXPECT_EQ(Triple::aarch64_32, T.getArch());}{Triple T = Triple("aarch64");EXPECT_EQ(Triple::aarch64, T.getArch());}{Triple T = Triple("aarch64_be");EXPECT_EQ(Triple::aarch64_be, T.getArch());}{Triple T = Triple("arm64e");EXPECT_EQ(Triple::aarch64, T.getArch());EXPECT_EQ(Triple::AArch64SubArch_arm64e, T.getSubArch());}}TEST(TripleTest, isArmT32) {// Not isArmT32{Triple T = Triple("thumbv6m");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv8m.base");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv7s");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv7k");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv7ve");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv6");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv6m");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv6k");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv6t2");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv5");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv5te");EXPECT_FALSE(T.isArmT32());}{Triple T = Triple("armv4t");EXPECT_FALSE(T.isArmT32());}// isArmT32{Triple T = Triple("arm");EXPECT_TRUE(T.isArmT32());}{Triple T = Triple("armv7m");EXPECT_TRUE(T.isArmT32());}{Triple T = Triple("armv7em");EXPECT_TRUE(T.isArmT32());}{Triple T = Triple("armv8m.main");EXPECT_TRUE(T.isArmT32());}{Triple T = Triple("armv8.1m.main");EXPECT_TRUE(T.isArmT32());}}TEST(TripleTest, isArmMClass) {// not M-class{Triple T = Triple("armv7s");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv7k");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv7ve");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv6");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv6k");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv6t2");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv5");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv5te");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("armv4t");EXPECT_FALSE(T.isArmMClass());}{Triple T = Triple("arm");EXPECT_FALSE(T.isArmMClass());}// is M-class{Triple T = Triple("armv6m");EXPECT_TRUE(T.isArmMClass());}{Triple T = Triple("armv7m");EXPECT_TRUE(T.isArmMClass());}{Triple T = Triple("armv7em");EXPECT_TRUE(T.isArmMClass());}{Triple T = Triple("armv8m.base");EXPECT_TRUE(T.isArmMClass());}{Triple T = Triple("armv8m.main");EXPECT_TRUE(T.isArmMClass());}{Triple T = Triple("armv8.1m.main");EXPECT_TRUE(T.isArmMClass());}}} // end anonymous namespace
//===- llvm/unittest/ADT/TinyPtrVectorTest.cpp ----------------------------===////// 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////===----------------------------------------------------------------------===////// TinyPtrVector unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/TinyPtrVector.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Support/type_traits.h"#include "gtest/gtest.h"#include <algorithm>#include <random>#include <vector>using namespace llvm;namespace {template <typename T> struct RemovePointer : std::remove_pointer<T> {};template <typename PointerTy, unsigned IntBits, typename IntType,typename PtrTraits, typename Info>struct RemovePointer<PointerIntPair<PointerTy, IntBits, IntType, PtrTraits, Info>> {typedef typename RemovePointer<PointerTy>::type type;};template <typename VectorT>class TinyPtrVectorTest : public testing::Test {protected:typedef typename VectorT::value_type PtrT;typedef typename RemovePointer<PtrT>::type ValueT;using PtrTraits = PointerLikeTypeTraits<PtrT>;VectorT V;VectorT V2;ValueT TestValues[1024];std::vector<PtrT> TestPtrs;TinyPtrVectorTest() {for (size_t i = 0, e = array_lengthof(TestValues); i != e; ++i)TestPtrs.push_back(PtrT(&TestValues[i]));std::shuffle(TestPtrs.begin(), TestPtrs.end(), std::mt19937{});}PtrT makePtr(ValueT *V) { return PtrT(V); }ArrayRef<PtrT> testArray(size_t N) {return makeArrayRef(&TestPtrs[0], N);}void appendValues(VectorT &V, ArrayRef<PtrT> Values) {for (size_t i = 0, e = Values.size(); i != e; ++i)V.push_back(Values[i]);}void setVectors(ArrayRef<PtrT> Values1, ArrayRef<PtrT> Values2) {V.clear();appendValues(V, Values1);V2.clear();appendValues(V2, Values2);}void expectValues(const VectorT &V, ArrayRef<PtrT> Values) {EXPECT_EQ(Values.empty(), V.empty());EXPECT_EQ(Values.size(), V.size());for (size_t i = 0, e = Values.size(); i != e; ++i) {EXPECT_EQ(Values[i], V[i]);EXPECT_EQ(Values[i], *std::next(V.begin(), i));}EXPECT_EQ(V.end(), std::next(V.begin(), Values.size()));}};typedef ::testing::Types<TinyPtrVector<int *>, TinyPtrVector<double *>,TinyPtrVector<PointerIntPair<int *, 1>>>TinyPtrVectorTestTypes;TYPED_TEST_SUITE(TinyPtrVectorTest, TinyPtrVectorTestTypes, );TYPED_TEST(TinyPtrVectorTest, EmptyTest) {this->expectValues(this->V, this->testArray(0));}TYPED_TEST(TinyPtrVectorTest, PushPopBack) {this->V.push_back(this->TestPtrs[0]);this->expectValues(this->V, this->testArray(1));this->V.push_back(this->TestPtrs[1]);this->expectValues(this->V, this->testArray(2));this->V.push_back(this->TestPtrs[2]);this->expectValues(this->V, this->testArray(3));this->V.push_back(this->TestPtrs[3]);this->expectValues(this->V, this->testArray(4));this->V.push_back(this->TestPtrs[4]);this->expectValues(this->V, this->testArray(5));// Pop and clobber a few values to keep things interesting.this->V.pop_back();this->expectValues(this->V, this->testArray(4));this->V.pop_back();this->expectValues(this->V, this->testArray(3));this->TestPtrs[3] = this->makePtr(&this->TestValues[42]);this->TestPtrs[4] = this->makePtr(&this->TestValues[43]);this->V.push_back(this->TestPtrs[3]);this->expectValues(this->V, this->testArray(4));this->V.push_back(this->TestPtrs[4]);this->expectValues(this->V, this->testArray(5));this->V.pop_back();this->expectValues(this->V, this->testArray(4));this->V.pop_back();this->expectValues(this->V, this->testArray(3));this->V.pop_back();this->expectValues(this->V, this->testArray(2));this->V.pop_back();this->expectValues(this->V, this->testArray(1));this->V.pop_back();this->expectValues(this->V, this->testArray(0));this->appendValues(this->V, this->testArray(42));this->expectValues(this->V, this->testArray(42));}TYPED_TEST(TinyPtrVectorTest, ClearTest) {this->expectValues(this->V, this->testArray(0));this->V.clear();this->expectValues(this->V, this->testArray(0));this->appendValues(this->V, this->testArray(1));this->expectValues(this->V, this->testArray(1));this->V.clear();this->expectValues(this->V, this->testArray(0));this->appendValues(this->V, this->testArray(42));this->expectValues(this->V, this->testArray(42));this->V.clear();this->expectValues(this->V, this->testArray(0));}TYPED_TEST(TinyPtrVectorTest, CopyAndMoveCtorTest) {this->appendValues(this->V, this->testArray(42));TypeParam Copy(this->V);this->expectValues(Copy, this->testArray(42));// This is a separate copy, and so it shouldn't destroy the original.Copy.clear();this->expectValues(Copy, this->testArray(0));this->expectValues(this->V, this->testArray(42));TypeParam Copy2(this->V2);this->appendValues(Copy2, this->testArray(42));this->expectValues(Copy2, this->testArray(42));this->expectValues(this->V2, this->testArray(0));TypeParam Move(std::move(Copy2));this->expectValues(Move, this->testArray(42));this->expectValues(Copy2, this->testArray(0));TypeParam MultipleElements(this->testArray(2));TypeParam SingleElement(this->testArray(1));MultipleElements = std::move(SingleElement);this->expectValues(MultipleElements, this->testArray(1));this->expectValues(SingleElement, this->testArray(0));}TYPED_TEST(TinyPtrVectorTest, CopyAndMoveTest) {this->V = this->V2;this->expectValues(this->V, this->testArray(0));this->expectValues(this->V2, this->testArray(0));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(0));this->setVectors(this->testArray(1), this->testArray(0));this->V = this->V2;this->expectValues(this->V, this->testArray(0));this->expectValues(this->V2, this->testArray(0));this->setVectors(this->testArray(1), this->testArray(0));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(0));this->setVectors(this->testArray(2), this->testArray(0));this->V = this->V2;this->expectValues(this->V, this->testArray(0));this->expectValues(this->V2, this->testArray(0));this->setVectors(this->testArray(2), this->testArray(0));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(0));this->setVectors(this->testArray(42), this->testArray(0));this->V = this->V2;this->expectValues(this->V, this->testArray(0));this->expectValues(this->V2, this->testArray(0));this->setVectors(this->testArray(42), this->testArray(0));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(0));this->setVectors(this->testArray(0), this->testArray(1));this->V = this->V2;this->expectValues(this->V, this->testArray(1));this->expectValues(this->V2, this->testArray(1));this->setVectors(this->testArray(0), this->testArray(1));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(1));this->setVectors(this->testArray(0), this->testArray(2));this->V = this->V2;this->expectValues(this->V, this->testArray(2));this->expectValues(this->V2, this->testArray(2));this->setVectors(this->testArray(0), this->testArray(2));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(2));this->setVectors(this->testArray(0), this->testArray(42));this->V = this->V2;this->expectValues(this->V, this->testArray(42));this->expectValues(this->V2, this->testArray(42));this->setVectors(this->testArray(0), this->testArray(42));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(42));this->setVectors(this->testArray(1), this->testArray(1));this->V = this->V2;this->expectValues(this->V, this->testArray(1));this->expectValues(this->V2, this->testArray(1));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(1));this->setVectors(this->testArray(1), this->testArray(2));this->V = this->V2;this->expectValues(this->V, this->testArray(2));this->expectValues(this->V2, this->testArray(2));this->setVectors(this->testArray(1), this->testArray(2));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(2));this->setVectors(this->testArray(1), this->testArray(42));this->V = this->V2;this->expectValues(this->V, this->testArray(42));this->expectValues(this->V2, this->testArray(42));this->setVectors(this->testArray(1), this->testArray(42));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(42));this->setVectors(this->testArray(2), this->testArray(1));this->V = this->V2;this->expectValues(this->V, this->testArray(1));this->expectValues(this->V2, this->testArray(1));this->setVectors(this->testArray(2), this->testArray(1));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(1));this->setVectors(this->testArray(2), this->testArray(2));this->V = this->V2;this->expectValues(this->V, this->testArray(2));this->expectValues(this->V2, this->testArray(2));this->setVectors(this->testArray(2), this->testArray(2));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(2));this->setVectors(this->testArray(2), this->testArray(42));this->V = this->V2;this->expectValues(this->V, this->testArray(42));this->expectValues(this->V2, this->testArray(42));this->setVectors(this->testArray(2), this->testArray(42));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(42));this->setVectors(this->testArray(42), this->testArray(1));this->V = this->V2;this->expectValues(this->V, this->testArray(1));this->expectValues(this->V2, this->testArray(1));this->setVectors(this->testArray(42), this->testArray(1));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(1));this->setVectors(this->testArray(42), this->testArray(2));this->V = this->V2;this->expectValues(this->V, this->testArray(2));this->expectValues(this->V2, this->testArray(2));this->setVectors(this->testArray(42), this->testArray(2));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(2));this->setVectors(this->testArray(42), this->testArray(42));this->V = this->V2;this->expectValues(this->V, this->testArray(42));this->expectValues(this->V2, this->testArray(42));this->setVectors(this->testArray(42), this->testArray(42));this->V = std::move(this->V2);this->expectValues(this->V, this->testArray(42));}TYPED_TEST(TinyPtrVectorTest, EraseTest) {this->appendValues(this->V, this->testArray(1));this->expectValues(this->V, this->testArray(1));this->V.erase(this->V.begin());this->expectValues(this->V, this->testArray(0));this->appendValues(this->V, this->testArray(42));this->expectValues(this->V, this->testArray(42));this->V.erase(this->V.begin());this->TestPtrs.erase(this->TestPtrs.begin());this->expectValues(this->V, this->testArray(41));this->V.erase(std::next(this->V.begin(), 1));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 1));this->expectValues(this->V, this->testArray(40));this->V.erase(std::next(this->V.begin(), 2));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 2));this->expectValues(this->V, this->testArray(39));this->V.erase(std::next(this->V.begin(), 5));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 5));this->expectValues(this->V, this->testArray(38));this->V.erase(std::next(this->V.begin(), 13));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 13));this->expectValues(this->V, this->testArray(37));typename TypeParam::iterator I = this->V.begin();do {I = this->V.erase(I);} while (I != this->V.end());this->expectValues(this->V, this->testArray(0));}TYPED_TEST(TinyPtrVectorTest, EraseRangeTest) {this->appendValues(this->V, this->testArray(1));this->expectValues(this->V, this->testArray(1));this->V.erase(this->V.begin(), this->V.begin());this->expectValues(this->V, this->testArray(1));this->V.erase(this->V.end(), this->V.end());this->expectValues(this->V, this->testArray(1));this->V.erase(this->V.begin(), this->V.end());this->expectValues(this->V, this->testArray(0));this->appendValues(this->V, this->testArray(42));this->expectValues(this->V, this->testArray(42));this->V.erase(this->V.begin(), std::next(this->V.begin(), 1));this->TestPtrs.erase(this->TestPtrs.begin(),std::next(this->TestPtrs.begin(), 1));this->expectValues(this->V, this->testArray(41));this->V.erase(std::next(this->V.begin(), 1), std::next(this->V.begin(), 2));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 1),std::next(this->TestPtrs.begin(), 2));this->expectValues(this->V, this->testArray(40));this->V.erase(std::next(this->V.begin(), 2), std::next(this->V.begin(), 4));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 2),std::next(this->TestPtrs.begin(), 4));this->expectValues(this->V, this->testArray(38));this->V.erase(std::next(this->V.begin(), 5), std::next(this->V.begin(), 10));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 5),std::next(this->TestPtrs.begin(), 10));this->expectValues(this->V, this->testArray(33));this->V.erase(std::next(this->V.begin(), 13), std::next(this->V.begin(), 26));this->TestPtrs.erase(std::next(this->TestPtrs.begin(), 13),std::next(this->TestPtrs.begin(), 26));this->expectValues(this->V, this->testArray(20));this->V.erase(std::next(this->V.begin(), 7), this->V.end());this->expectValues(this->V, this->testArray(7));this->V.erase(this->V.begin(), this->V.end());this->expectValues(this->V, this->testArray(0));}TYPED_TEST(TinyPtrVectorTest, Insert) {this->V.insert(this->V.end(), this->TestPtrs[0]);this->expectValues(this->V, this->testArray(1));this->V.clear();this->appendValues(this->V, this->testArray(4));this->expectValues(this->V, this->testArray(4));this->V.insert(this->V.end(), this->TestPtrs[4]);this->expectValues(this->V, this->testArray(5));this->V.insert(this->V.begin(), this->TestPtrs[42]);this->TestPtrs.insert(this->TestPtrs.begin(), this->TestPtrs[42]);this->expectValues(this->V, this->testArray(6));this->V.insert(std::next(this->V.begin(), 3), this->TestPtrs[43]);this->TestPtrs.insert(std::next(this->TestPtrs.begin(), 3),this->TestPtrs[43]);this->expectValues(this->V, this->testArray(7));}TYPED_TEST(TinyPtrVectorTest, InsertRange) {this->V.insert(this->V.end(), this->TestPtrs.begin(), this->TestPtrs.begin());this->expectValues(this->V, this->testArray(0));this->V.insert(this->V.begin(), this->TestPtrs.begin(),this->TestPtrs.begin());this->expectValues(this->V, this->testArray(0));this->V.insert(this->V.end(), this->TestPtrs.end(), this->TestPtrs.end());this->expectValues(this->V, this->testArray(0));this->V.insert(this->V.end(), this->TestPtrs.begin(),std::next(this->TestPtrs.begin()));this->expectValues(this->V, this->testArray(1));this->V.clear();this->V.insert(this->V.end(), this->TestPtrs.begin(),std::next(this->TestPtrs.begin(), 2));this->expectValues(this->V, this->testArray(2));this->V.clear();this->V.insert(this->V.end(), this->TestPtrs.begin(),std::next(this->TestPtrs.begin(), 42));this->expectValues(this->V, this->testArray(42));this->V.clear();this->V.insert(this->V.end(),std::next(this->TestPtrs.begin(), 5),std::next(this->TestPtrs.begin(), 13));this->V.insert(this->V.begin(),std::next(this->TestPtrs.begin(), 0),std::next(this->TestPtrs.begin(), 3));this->V.insert(std::next(this->V.begin(), 2),std::next(this->TestPtrs.begin(), 2),std::next(this->TestPtrs.begin(), 4));this->V.erase(std::next(this->V.begin(), 4));this->V.insert(std::next(this->V.begin(), 4),std::next(this->TestPtrs.begin(), 4),std::next(this->TestPtrs.begin(), 5));this->expectValues(this->V, this->testArray(13));}}TEST(TinyPtrVectorTest, SingleEltCtorTest) {int v = 55;TinyPtrVector<int *> V(&v);EXPECT_TRUE(V.size() == 1);EXPECT_FALSE(V.empty());EXPECT_TRUE(V.front() == &v);}TEST(TinyPtrVectorTest, ArrayRefCtorTest) {int data_array[128];std::vector<int *> data;for (unsigned i = 0, e = 128; i != e; ++i) {data_array[i] = 324 - int(i);data.push_back(&data_array[i]);}TinyPtrVector<int *> V(data);EXPECT_TRUE(V.size() == 128);EXPECT_FALSE(V.empty());for (unsigned i = 0, e = 128; i != e; ++i) {EXPECT_TRUE(V[i] == data[i]);}}TEST(TinyPtrVectorTest, MutableArrayRefTest) {int data_array[128];std::vector<int *> data;for (unsigned i = 0, e = 128; i != e; ++i) {data_array[i] = 324 - int(i);data.push_back(&data_array[i]);}TinyPtrVector<int *> V(data);EXPECT_TRUE(V.size() == 128);EXPECT_FALSE(V.empty());MutableArrayRef<int *> mut_array = V;for (unsigned i = 0, e = 128; i != e; ++i) {EXPECT_TRUE(mut_array[i] == data[i]);mut_array[i] = 324 + mut_array[i];EXPECT_TRUE(mut_array[i] == (324 + data[i]));}}
//===- llvm/unittest/ADT/TestGraph.h - Graph for testing ------------------===////// 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////===----------------------------------------------------------------------===////// Common graph data structure for testing.////===----------------------------------------------------------------------===//#ifndef LLVM_UNITTESTS_ADT_TEST_GRAPH_H#define LLVM_UNITTESTS_ADT_TEST_GRAPH_H#include "llvm/ADT/GraphTraits.h"#include <cassert>#include <climits>#include <utility>namespace llvm {/// Graph<N> - A graph with N nodes. Note that N can be at most 8.template <unsigned N>class Graph {private:// Disable copying.Graph(const Graph&);Graph& operator=(const Graph&);static void ValidateIndex(unsigned Idx) {assert(Idx < N && "Invalid node index!");}public:/// NodeSubset - A subset of the graph's nodes.class NodeSubset {typedef unsigned char BitVector; // Where the limitation N <= 8 comes from.BitVector Elements;NodeSubset(BitVector e) : Elements(e) {}public:/// NodeSubset - Default constructor, creates an empty subset.NodeSubset() : Elements(0) {assert(N <= sizeof(BitVector)*CHAR_BIT && "Graph too big!");}/// Comparison operators.bool operator==(const NodeSubset &other) const {return other.Elements == this->Elements;}bool operator!=(const NodeSubset &other) const {return !(*this == other);}/// AddNode - Add the node with the given index to the subset.void AddNode(unsigned Idx) {ValidateIndex(Idx);Elements |= 1U << Idx;}/// DeleteNode - Remove the node with the given index from the subset.void DeleteNode(unsigned Idx) {ValidateIndex(Idx);Elements &= ~(1U << Idx);}/// count - Return true if the node with the given index is in the subset.bool count(unsigned Idx) {ValidateIndex(Idx);return (Elements & (1U << Idx)) != 0;}/// isEmpty - Return true if this is the empty set.bool isEmpty() const {return Elements == 0;}/// isSubsetOf - Return true if this set is a subset of the given one.bool isSubsetOf(const NodeSubset &other) const {return (this->Elements | other.Elements) == other.Elements;}/// Complement - Return the complement of this subset.NodeSubset Complement() const {return ~(unsigned)this->Elements & ((1U << N) - 1);}/// Join - Return the union of this subset and the given one.NodeSubset Join(const NodeSubset &other) const {return this->Elements | other.Elements;}/// Meet - Return the intersection of this subset and the given one.NodeSubset Meet(const NodeSubset &other) const {return this->Elements & other.Elements;}};/// NodeType - Node index and set of children of the node.typedef std::pair<unsigned, NodeSubset> NodeType;private:/// Nodes - The list of nodes for this graph.NodeType Nodes[N];public:/// Graph - Default constructor. Creates an empty graph.Graph() {// Let each node know which node it is. This allows us to find the start of// the Nodes array given a pointer to any element of it.for (unsigned i = 0; i != N; ++i)Nodes[i].first = i;}/// AddEdge - Add an edge from the node with index FromIdx to the node with/// index ToIdx.void AddEdge(unsigned FromIdx, unsigned ToIdx) {ValidateIndex(FromIdx);Nodes[FromIdx].second.AddNode(ToIdx);}/// DeleteEdge - Remove the edge (if any) from the node with index FromIdx to/// the node with index ToIdx.void DeleteEdge(unsigned FromIdx, unsigned ToIdx) {ValidateIndex(FromIdx);Nodes[FromIdx].second.DeleteNode(ToIdx);}/// AccessNode - Get a pointer to the node with the given index.NodeType *AccessNode(unsigned Idx) const {ValidateIndex(Idx);// The constant cast is needed when working with GraphTraits, which insists// on taking a constant Graph.return const_cast<NodeType *>(&Nodes[Idx]);}/// NodesReachableFrom - Return the set of all nodes reachable from the given/// node.NodeSubset NodesReachableFrom(unsigned Idx) const {// This algorithm doesn't scale, but that doesn't matter given the small// size of our graphs.NodeSubset Reachable;// The initial node is reachable.Reachable.AddNode(Idx);do {NodeSubset Previous(Reachable);// Add in all nodes which are children of a reachable node.for (unsigned i = 0; i != N; ++i)if (Previous.count(i))Reachable = Reachable.Join(Nodes[i].second);// If nothing changed then we have found all reachable nodes.if (Reachable == Previous)return Reachable;// Rinse and repeat.} while (1);}/// ChildIterator - Visit all children of a node.class ChildIterator {friend class Graph;/// FirstNode - Pointer to first node in the graph's Nodes array.NodeType *FirstNode;/// Children - Set of nodes which are children of this one and that haven't/// yet been visited.NodeSubset Children;ChildIterator(); // Disable default constructor.protected:ChildIterator(NodeType *F, NodeSubset C) : FirstNode(F), Children(C) {}public:/// ChildIterator - Copy constructor.ChildIterator(const ChildIterator &other) = default;ChildIterator &operator=(const ChildIterator &other) = default;/// Comparison operators.bool operator==(const ChildIterator &other) const {return other.FirstNode == this->FirstNode &&other.Children == this->Children;}bool operator!=(const ChildIterator &other) const {return !(*this == other);}/// Prefix increment operator.ChildIterator& operator++() {// Find the next unvisited child node.for (unsigned i = 0; i != N; ++i)if (Children.count(i)) {// Remove that child - it has been visited. This is the increment!Children.DeleteNode(i);return *this;}assert(false && "Incrementing end iterator!");return *this; // Avoid compiler warnings.}/// Postfix increment operator.ChildIterator operator++(int) {ChildIterator Result(*this);++(*this);return Result;}/// Dereference operator.NodeType *operator*() {// Find the next unvisited child node.for (unsigned i = 0; i != N; ++i)if (Children.count(i))// Return a pointer to it.return FirstNode + i;assert(false && "Dereferencing end iterator!");return nullptr; // Avoid compiler warning.}};/// child_begin - Return an iterator pointing to the first child of the given/// node.static ChildIterator child_begin(NodeType *Parent) {return ChildIterator(Parent - Parent->first, Parent->second);}/// child_end - Return the end iterator for children of the given node.static ChildIterator child_end(NodeType *Parent) {return ChildIterator(Parent - Parent->first, NodeSubset());}};template <unsigned N>struct GraphTraits<Graph<N> > {typedef typename Graph<N>::NodeType *NodeRef;typedef typename Graph<N>::ChildIterator ChildIteratorType;static NodeRef getEntryNode(const Graph<N> &G) { return G.AccessNode(0); }static ChildIteratorType child_begin(NodeRef Node) {return Graph<N>::child_begin(Node);}static ChildIteratorType child_end(NodeRef Node) {return Graph<N>::child_end(Node);}};} // End namespace llvm#endif
//===- llvm/unittest/ADT/StringSwitchTest.cpp - StringSwitch unit tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringSwitch.h"#include "gtest/gtest.h"using namespace llvm;TEST(StringSwitchTest, Case) {auto Translate = [](StringRef S) {return llvm::StringSwitch<int>(S).Case("0", 0).Case("1", 1).Case("2", 2).Case("3", 3).Case("4", 4).Case("5", 5).Case("6", 6).Case("7", 7).Case("8", 8).Case("9", 9).Case("A", 10).Case("B", 11).Case("C", 12).Case("D", 13).Case("E", 14).Case("F", 15).Default(-1);};EXPECT_EQ(1, Translate("1"));EXPECT_EQ(2, Translate("2"));EXPECT_EQ(11, Translate("B"));EXPECT_EQ(-1, Translate("b"));EXPECT_EQ(-1, Translate(""));EXPECT_EQ(-1, Translate("Test"));}TEST(StringSwitchTest, CaseLower) {auto Translate = [](StringRef S) {return llvm::StringSwitch<int>(S).Case("0", 0).Case("1", 1).Case("2", 2).Case("3", 3).Case("4", 4).Case("5", 5).Case("6", 6).Case("7", 7).Case("8", 8).Case("9", 9).CaseLower("A", 10).CaseLower("B", 11).CaseLower("C", 12).CaseLower("D", 13).CaseLower("E", 14).CaseLower("F", 15).Default(-1);};EXPECT_EQ(1, Translate("1"));EXPECT_EQ(2, Translate("2"));EXPECT_EQ(11, Translate("B"));EXPECT_EQ(11, Translate("b"));EXPECT_EQ(-1, Translate(""));EXPECT_EQ(-1, Translate("Test"));}TEST(StringSwitchTest, StartsWith) {auto Translate = [](StringRef S) {return llvm::StringSwitch<std::function<int(int, int)>>(S).StartsWith("add", [](int X, int Y) { return X + Y; }).StartsWith("sub", [](int X, int Y) { return X - Y; }).StartsWith("mul", [](int X, int Y) { return X * Y; }).StartsWith("div", [](int X, int Y) { return X / Y; }).Default([](int X, int Y) { return 0; });};EXPECT_EQ(15, Translate("adder")(10, 5));EXPECT_EQ(5, Translate("subtracter")(10, 5));EXPECT_EQ(50, Translate("multiplier")(10, 5));EXPECT_EQ(2, Translate("divider")(10, 5));EXPECT_EQ(0, Translate("nothing")(10, 5));EXPECT_EQ(0, Translate("ADDER")(10, 5));}TEST(StringSwitchTest, StartsWithLower) {auto Translate = [](StringRef S) {return llvm::StringSwitch<std::function<int(int, int)>>(S).StartsWithLower("add", [](int X, int Y) { return X + Y; }).StartsWithLower("sub", [](int X, int Y) { return X - Y; }).StartsWithLower("mul", [](int X, int Y) { return X * Y; }).StartsWithLower("div", [](int X, int Y) { return X / Y; }).Default([](int X, int Y) { return 0; });};EXPECT_EQ(15, Translate("adder")(10, 5));EXPECT_EQ(5, Translate("subtracter")(10, 5));EXPECT_EQ(50, Translate("multiplier")(10, 5));EXPECT_EQ(2, Translate("divider")(10, 5));EXPECT_EQ(15, Translate("AdDeR")(10, 5));EXPECT_EQ(5, Translate("SuBtRaCtEr")(10, 5));EXPECT_EQ(50, Translate("MuLtIpLiEr")(10, 5));EXPECT_EQ(2, Translate("DiViDeR")(10, 5));EXPECT_EQ(0, Translate("nothing")(10, 5));}TEST(StringSwitchTest, EndsWith) {enum class Suffix { Possible, PastTense, Process, InProgressAction, Unknown };auto Translate = [](StringRef S) {return llvm::StringSwitch<Suffix>(S).EndsWith("able", Suffix::Possible).EndsWith("ed", Suffix::PastTense).EndsWith("ation", Suffix::Process).EndsWith("ing", Suffix::InProgressAction).Default(Suffix::Unknown);};EXPECT_EQ(Suffix::Possible, Translate("optimizable"));EXPECT_EQ(Suffix::PastTense, Translate("optimized"));EXPECT_EQ(Suffix::Process, Translate("optimization"));EXPECT_EQ(Suffix::InProgressAction, Translate("optimizing"));EXPECT_EQ(Suffix::Unknown, Translate("optimizer"));EXPECT_EQ(Suffix::Unknown, Translate("OPTIMIZABLE"));}TEST(StringSwitchTest, EndsWithLower) {enum class Suffix { Possible, PastTense, Process, InProgressAction, Unknown };auto Translate = [](StringRef S) {return llvm::StringSwitch<Suffix>(S).EndsWithLower("able", Suffix::Possible).EndsWithLower("ed", Suffix::PastTense).EndsWithLower("ation", Suffix::Process).EndsWithLower("ing", Suffix::InProgressAction).Default(Suffix::Unknown);};EXPECT_EQ(Suffix::Possible, Translate("optimizable"));EXPECT_EQ(Suffix::Possible, Translate("OPTIMIZABLE"));EXPECT_EQ(Suffix::PastTense, Translate("optimized"));EXPECT_EQ(Suffix::Process, Translate("optimization"));EXPECT_EQ(Suffix::InProgressAction, Translate("optimizing"));EXPECT_EQ(Suffix::Unknown, Translate("optimizer"));}TEST(StringSwitchTest, Cases) {enum class OSType { Windows, Linux, Unknown };auto Translate = [](StringRef S) {return llvm::StringSwitch<OSType>(S).Cases(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt",OSType::Windows).Cases("linux", "unix", "*nix", "posix", OSType::Linux).Default(OSType::Unknown);};EXPECT_EQ(OSType::Windows, Translate(llvm::StringRef("wind\0ws", 7)));EXPECT_EQ(OSType::Windows, Translate("win32"));EXPECT_EQ(OSType::Windows, Translate("winnt"));EXPECT_EQ(OSType::Linux, Translate("linux"));EXPECT_EQ(OSType::Linux, Translate("unix"));EXPECT_EQ(OSType::Linux, Translate("*nix"));EXPECT_EQ(OSType::Linux, Translate("posix"));// Note that the whole null-terminator embedded string is required for the// case to match.EXPECT_EQ(OSType::Unknown, Translate("wind"));EXPECT_EQ(OSType::Unknown, Translate("Windows"));EXPECT_EQ(OSType::Unknown, Translate(""));}TEST(StringSwitchTest, CasesLower) {enum class OSType { Windows, Linux, Unknown };auto Translate = [](StringRef S) {return llvm::StringSwitch<OSType>(S).CasesLower(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt",OSType::Windows).CasesLower("linux", "unix", "*nix", "posix", OSType::Linux).Default(OSType::Unknown);};EXPECT_EQ(OSType::Windows, Translate(llvm::StringRef("WIND\0WS", 7)));EXPECT_EQ(OSType::Windows, Translate("WIN32"));EXPECT_EQ(OSType::Windows, Translate("WINNT"));EXPECT_EQ(OSType::Linux, Translate("LINUX"));EXPECT_EQ(OSType::Linux, Translate("UNIX"));EXPECT_EQ(OSType::Linux, Translate("*NIX"));EXPECT_EQ(OSType::Linux, Translate("POSIX"));EXPECT_EQ(OSType::Windows, Translate(llvm::StringRef("wind\0ws", 7)));EXPECT_EQ(OSType::Linux, Translate("linux"));EXPECT_EQ(OSType::Unknown, Translate("wind"));EXPECT_EQ(OSType::Unknown, Translate(""));}
//===- llvm/unittest/ADT/StringSetTest.cpp - StringSet unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringSet.h"#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Test fixtureclass StringSetTest : public testing::Test {};TEST_F(StringSetTest, IterSetKeys) {StringSet<> Set;Set.insert("A");Set.insert("B");Set.insert("C");Set.insert("D");auto Keys = to_vector<4>(Set.keys());llvm::sort(Keys);SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"};EXPECT_EQ(Expected, Keys);}TEST_F(StringSetTest, InsertAndCountStringMapEntry) {// Test insert(StringMapEntry) and count(StringMapEntry)// which are required for set_difference(StringSet, StringSet).StringSet<> Set;StringMapEntry<StringRef> *Element =StringMapEntry<StringRef>::Create("A", Set.getAllocator());Set.insert(*Element);size_t Count = Set.count(*Element);size_t Expected = 1;EXPECT_EQ(Expected, Count);Element->Destroy(Set.getAllocator());}TEST_F(StringSetTest, EmptyString) {// Verify that the empty string can by successfully insertedStringSet<> Set;size_t Count = Set.count("");EXPECT_EQ(Count, 0UL);Set.insert("");Count = Set.count("");EXPECT_EQ(Count, 1UL);}TEST_F(StringSetTest, Contains) {StringSet<> Set;EXPECT_FALSE(Set.contains(""));EXPECT_FALSE(Set.contains("test"));Set.insert("");Set.insert("test");EXPECT_TRUE(Set.contains(""));EXPECT_TRUE(Set.contains("test"));Set.insert("test");EXPECT_TRUE(Set.contains(""));EXPECT_TRUE(Set.contains("test"));Set.erase("test");EXPECT_TRUE(Set.contains(""));EXPECT_FALSE(Set.contains("test"));}} // end anonymous namespace
//===- llvm/unittest/ADT/StringRefTest.cpp - StringRef unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringRef.h"#include "llvm/ADT/Hashing.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/StringExtras.h"#include "llvm/Support/Allocator.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {std::ostream &operator<<(std::ostream &OS, const StringRef &S) {OS << S.str();return OS;}std::ostream &operator<<(std::ostream &OS,const std::pair<StringRef, StringRef> &P) {OS << "(" << P.first << ", " << P.second << ")";return OS;}}// Check that we can't accidentally assign a temporary std::string to a// StringRef. (Unfortunately we can't make use of the same thing with// constructors.)static_assert(!std::is_assignable<StringRef&, std::string>::value,"Assigning from prvalue std::string");static_assert(!std::is_assignable<StringRef&, std::string &&>::value,"Assigning from xvalue std::string");static_assert(std::is_assignable<StringRef&, std::string &>::value,"Assigning from lvalue std::string");static_assert(std::is_assignable<StringRef&, const char *>::value,"Assigning from prvalue C string");static_assert(std::is_assignable<StringRef&, const char * &&>::value,"Assigning from xvalue C string");static_assert(std::is_assignable<StringRef&, const char * &>::value,"Assigning from lvalue C string");namespace {TEST(StringRefTest, Construction) {EXPECT_EQ("", StringRef());EXPECT_EQ("hello", StringRef("hello"));EXPECT_EQ("hello", StringRef("hello world", 5));EXPECT_EQ("hello", StringRef(std::string("hello")));#if __cplusplus > 201402LEXPECT_EQ("hello", StringRef(std::string_view("hello")));#endif}TEST(StringRefTest, Conversion) {EXPECT_EQ("hello", std::string(StringRef("hello")));#if __cplusplus > 201402LEXPECT_EQ("hello", std::string_view(StringRef("hello")));#endif}TEST(StringRefTest, EmptyInitializerList) {StringRef S = {};EXPECT_TRUE(S.empty());S = {};EXPECT_TRUE(S.empty());}TEST(StringRefTest, Iteration) {StringRef S("hello");const char *p = "hello";for (const char *it = S.begin(), *ie = S.end(); it != ie; ++it, ++p)EXPECT_EQ(*it, *p);}TEST(StringRefTest, StringOps) {const char *p = "hello";EXPECT_EQ(p, StringRef(p, 0).data());EXPECT_TRUE(StringRef().empty());EXPECT_EQ((size_t) 5, StringRef("hello").size());EXPECT_EQ(-1, StringRef("aab").compare("aad"));EXPECT_EQ( 0, StringRef("aab").compare("aab"));EXPECT_EQ( 1, StringRef("aab").compare("aaa"));EXPECT_EQ(-1, StringRef("aab").compare("aabb"));EXPECT_EQ( 1, StringRef("aab").compare("aa"));EXPECT_EQ( 1, StringRef("\xFF").compare("\1"));EXPECT_EQ(-1, StringRef("AaB").compare_insensitive("aAd"));EXPECT_EQ( 0, StringRef("AaB").compare_insensitive("aab"));EXPECT_EQ( 1, StringRef("AaB").compare_insensitive("AAA"));EXPECT_EQ(-1, StringRef("AaB").compare_insensitive("aaBb"));EXPECT_EQ(-1, StringRef("AaB").compare_insensitive("bb"));EXPECT_EQ( 1, StringRef("aaBb").compare_insensitive("AaB"));EXPECT_EQ( 1, StringRef("bb").compare_insensitive("AaB"));EXPECT_EQ( 1, StringRef("AaB").compare_insensitive("aA"));EXPECT_EQ( 1, StringRef("\xFF").compare_insensitive("\1"));EXPECT_EQ(-1, StringRef("aab").compare_numeric("aad"));EXPECT_EQ( 0, StringRef("aab").compare_numeric("aab"));EXPECT_EQ( 1, StringRef("aab").compare_numeric("aaa"));EXPECT_EQ(-1, StringRef("aab").compare_numeric("aabb"));EXPECT_EQ( 1, StringRef("aab").compare_numeric("aa"));EXPECT_EQ(-1, StringRef("1").compare_numeric("10"));EXPECT_EQ( 0, StringRef("10").compare_numeric("10"));EXPECT_EQ( 0, StringRef("10a").compare_numeric("10a"));EXPECT_EQ( 1, StringRef("2").compare_numeric("1"));EXPECT_EQ( 0, StringRef("llvm_v1i64_ty").compare_numeric("llvm_v1i64_ty"));EXPECT_EQ( 1, StringRef("\xFF").compare_numeric("\1"));EXPECT_EQ( 1, StringRef("V16").compare_numeric("V1_q0"));EXPECT_EQ(-1, StringRef("V1_q0").compare_numeric("V16"));EXPECT_EQ(-1, StringRef("V8_q0").compare_numeric("V16"));EXPECT_EQ( 1, StringRef("V16").compare_numeric("V8_q0"));EXPECT_EQ(-1, StringRef("V1_q0").compare_numeric("V8_q0"));EXPECT_EQ( 1, StringRef("V8_q0").compare_numeric("V1_q0"));}TEST(StringRefTest, Operators) {EXPECT_EQ("", StringRef());EXPECT_TRUE(StringRef("aab") < StringRef("aad"));EXPECT_FALSE(StringRef("aab") < StringRef("aab"));EXPECT_TRUE(StringRef("aab") <= StringRef("aab"));EXPECT_FALSE(StringRef("aab") <= StringRef("aaa"));EXPECT_TRUE(StringRef("aad") > StringRef("aab"));EXPECT_FALSE(StringRef("aab") > StringRef("aab"));EXPECT_TRUE(StringRef("aab") >= StringRef("aab"));EXPECT_FALSE(StringRef("aaa") >= StringRef("aab"));EXPECT_EQ(StringRef("aab"), StringRef("aab"));EXPECT_FALSE(StringRef("aab") == StringRef("aac"));EXPECT_FALSE(StringRef("aab") != StringRef("aab"));EXPECT_TRUE(StringRef("aab") != StringRef("aac"));EXPECT_EQ('a', StringRef("aab")[1]);}TEST(StringRefTest, Substr) {StringRef Str("hello");EXPECT_EQ("lo", Str.substr(3));EXPECT_EQ("", Str.substr(100));EXPECT_EQ("hello", Str.substr(0, 100));EXPECT_EQ("o", Str.substr(4, 10));}TEST(StringRefTest, Slice) {StringRef Str("hello");EXPECT_EQ("l", Str.slice(2, 3));EXPECT_EQ("ell", Str.slice(1, 4));EXPECT_EQ("llo", Str.slice(2, 100));EXPECT_EQ("", Str.slice(2, 1));EXPECT_EQ("", Str.slice(10, 20));}TEST(StringRefTest, Split) {StringRef Str("hello");EXPECT_EQ(std::make_pair(StringRef("hello"), StringRef("")),Str.split('X'));EXPECT_EQ(std::make_pair(StringRef("h"), StringRef("llo")),Str.split('e'));EXPECT_EQ(std::make_pair(StringRef(""), StringRef("ello")),Str.split('h'));EXPECT_EQ(std::make_pair(StringRef("he"), StringRef("lo")),Str.split('l'));EXPECT_EQ(std::make_pair(StringRef("hell"), StringRef("")),Str.split('o'));EXPECT_EQ(std::make_pair(StringRef("hello"), StringRef("")),Str.rsplit('X'));EXPECT_EQ(std::make_pair(StringRef("h"), StringRef("llo")),Str.rsplit('e'));EXPECT_EQ(std::make_pair(StringRef(""), StringRef("ello")),Str.rsplit('h'));EXPECT_EQ(std::make_pair(StringRef("hel"), StringRef("o")),Str.rsplit('l'));EXPECT_EQ(std::make_pair(StringRef("hell"), StringRef("")),Str.rsplit('o'));EXPECT_EQ(std::make_pair(StringRef("he"), StringRef("o")),Str.rsplit("ll"));EXPECT_EQ(std::make_pair(StringRef(""), StringRef("ello")),Str.rsplit("h"));EXPECT_EQ(std::make_pair(StringRef("hell"), StringRef("")),Str.rsplit("o"));EXPECT_EQ(std::make_pair(StringRef("hello"), StringRef("")),Str.rsplit("::"));EXPECT_EQ(std::make_pair(StringRef("hel"), StringRef("o")),Str.rsplit("l"));}TEST(StringRefTest, Split2) {SmallVector<StringRef, 5> parts;SmallVector<StringRef, 5> expected;expected.push_back("ab"); expected.push_back("c");StringRef(",ab,,c,").split(parts, ",", -1, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back(""); expected.push_back("ab"); expected.push_back("");expected.push_back("c"); expected.push_back("");StringRef(",ab,,c,").split(parts, ",", -1, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("");StringRef("").split(parts, ",", -1, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();StringRef("").split(parts, ",", -1, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();StringRef(",").split(parts, ",", -1, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back(""); expected.push_back("");StringRef(",").split(parts, ",", -1, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back("b");StringRef("a,b").split(parts, ",", -1, true);EXPECT_TRUE(parts == expected);// Test MaxSplitexpected.clear(); parts.clear();expected.push_back("a,,b,c");StringRef("a,,b,c").split(parts, ",", 0, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a,,b,c");StringRef("a,,b,c").split(parts, ",", 0, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back(",b,c");StringRef("a,,b,c").split(parts, ",", 1, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back(",b,c");StringRef("a,,b,c").split(parts, ",", 1, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back(""); expected.push_back("b,c");StringRef("a,,b,c").split(parts, ",", 2, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back("b,c");StringRef("a,,b,c").split(parts, ",", 2, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back(""); expected.push_back("b");expected.push_back("c");StringRef("a,,b,c").split(parts, ",", 3, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back("b"); expected.push_back("c");StringRef("a,,b,c").split(parts, ",", 3, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a"); expected.push_back("b"); expected.push_back("c");StringRef("a,,b,c").split(parts, ',', 3, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("");StringRef().split(parts, ",", 0, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back(StringRef());StringRef("").split(parts, ",", 0, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();StringRef("").split(parts, ",", 0, false);EXPECT_TRUE(parts == expected);StringRef().split(parts, ",", 0, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a");expected.push_back("");expected.push_back("b");expected.push_back("c,d");StringRef("a,,b,c,d").split(parts, ",", 3, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("");StringRef().split(parts, ',', 0, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back(StringRef());StringRef("").split(parts, ',', 0, true);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();StringRef("").split(parts, ',', 0, false);EXPECT_TRUE(parts == expected);StringRef().split(parts, ',', 0, false);EXPECT_TRUE(parts == expected);expected.clear(); parts.clear();expected.push_back("a");expected.push_back("");expected.push_back("b");expected.push_back("c,d");StringRef("a,,b,c,d").split(parts, ',', 3, true);EXPECT_TRUE(parts == expected);}TEST(StringRefTest, Trim) {StringRef Str0("hello");StringRef Str1(" hello ");StringRef Str2(" hello ");StringRef Str3("\t\n\v\f\r hello \t\n\v\f\r");EXPECT_EQ(StringRef("hello"), Str0.rtrim());EXPECT_EQ(StringRef(" hello"), Str1.rtrim());EXPECT_EQ(StringRef(" hello"), Str2.rtrim());EXPECT_EQ(StringRef("\t\n\v\f\r hello"), Str3.rtrim());EXPECT_EQ(StringRef("hello"), Str0.ltrim());EXPECT_EQ(StringRef("hello "), Str1.ltrim());EXPECT_EQ(StringRef("hello "), Str2.ltrim());EXPECT_EQ(StringRef("hello \t\n\v\f\r"), Str3.ltrim());EXPECT_EQ(StringRef("hello"), Str0.trim());EXPECT_EQ(StringRef("hello"), Str1.trim());EXPECT_EQ(StringRef("hello"), Str2.trim());EXPECT_EQ(StringRef("hello"), Str3.trim());EXPECT_EQ(StringRef("ello"), Str0.trim("hhhhhhhhhhh"));EXPECT_EQ(StringRef(""), StringRef("").trim());EXPECT_EQ(StringRef(""), StringRef(" ").trim());EXPECT_EQ(StringRef("\0", 1), StringRef(" \0 ", 3).trim());EXPECT_EQ(StringRef("\0\0", 2), StringRef("\0\0", 2).trim());EXPECT_EQ(StringRef("x"), StringRef("\0\0x\0\0", 5).trim('\0'));}TEST(StringRefTest, StartsWith) {StringRef Str("hello");EXPECT_TRUE(Str.startswith(""));EXPECT_TRUE(Str.startswith("he"));EXPECT_FALSE(Str.startswith("helloworld"));EXPECT_FALSE(Str.startswith("hi"));}TEST(StringRefTest, StartsWithInsensitive) {StringRef Str("heLLo");EXPECT_TRUE(Str.startswith_insensitive(""));EXPECT_TRUE(Str.startswith_insensitive("he"));EXPECT_TRUE(Str.startswith_insensitive("hell"));EXPECT_TRUE(Str.startswith_insensitive("HELlo"));EXPECT_FALSE(Str.startswith_insensitive("helloworld"));EXPECT_FALSE(Str.startswith_insensitive("hi"));}TEST(StringRefTest, ConsumeFront) {StringRef Str("hello");EXPECT_TRUE(Str.consume_front(""));EXPECT_EQ("hello", Str);EXPECT_TRUE(Str.consume_front("he"));EXPECT_EQ("llo", Str);EXPECT_FALSE(Str.consume_front("lloworld"));EXPECT_EQ("llo", Str);EXPECT_FALSE(Str.consume_front("lol"));EXPECT_EQ("llo", Str);EXPECT_TRUE(Str.consume_front("llo"));EXPECT_EQ("", Str);EXPECT_FALSE(Str.consume_front("o"));EXPECT_TRUE(Str.consume_front(""));}TEST(StringRefTest, ConsumeFrontInsensitive) {StringRef Str("heLLo");EXPECT_TRUE(Str.consume_front_insensitive(""));EXPECT_EQ("heLLo", Str);EXPECT_FALSE(Str.consume_front("HEl"));EXPECT_EQ("heLLo", Str);EXPECT_TRUE(Str.consume_front_insensitive("HEl"));EXPECT_EQ("Lo", Str);EXPECT_FALSE(Str.consume_front_insensitive("loworld"));EXPECT_EQ("Lo", Str);EXPECT_FALSE(Str.consume_front_insensitive("ol"));EXPECT_EQ("Lo", Str);EXPECT_TRUE(Str.consume_front_insensitive("lo"));EXPECT_EQ("", Str);EXPECT_FALSE(Str.consume_front_insensitive("o"));EXPECT_TRUE(Str.consume_front_insensitive(""));}TEST(StringRefTest, EndsWith) {StringRef Str("hello");EXPECT_TRUE(Str.endswith(""));EXPECT_TRUE(Str.endswith("lo"));EXPECT_FALSE(Str.endswith("helloworld"));EXPECT_FALSE(Str.endswith("worldhello"));EXPECT_FALSE(Str.endswith("so"));}TEST(StringRefTest, EndsWithInsensitive) {StringRef Str("heLLo");EXPECT_TRUE(Str.endswith_insensitive(""));EXPECT_TRUE(Str.endswith_insensitive("lo"));EXPECT_TRUE(Str.endswith_insensitive("LO"));EXPECT_TRUE(Str.endswith_insensitive("ELlo"));EXPECT_FALSE(Str.endswith_insensitive("helloworld"));EXPECT_FALSE(Str.endswith_insensitive("hi"));}TEST(StringRefTest, ConsumeBack) {StringRef Str("hello");EXPECT_TRUE(Str.consume_back(""));EXPECT_EQ("hello", Str);EXPECT_TRUE(Str.consume_back("lo"));EXPECT_EQ("hel", Str);EXPECT_FALSE(Str.consume_back("helhel"));EXPECT_EQ("hel", Str);EXPECT_FALSE(Str.consume_back("hle"));EXPECT_EQ("hel", Str);EXPECT_TRUE(Str.consume_back("hel"));EXPECT_EQ("", Str);EXPECT_FALSE(Str.consume_back("h"));EXPECT_TRUE(Str.consume_back(""));}TEST(StringRefTest, ConsumeBackInsensitive) {StringRef Str("heLLo");EXPECT_TRUE(Str.consume_back_insensitive(""));EXPECT_EQ("heLLo", Str);EXPECT_FALSE(Str.consume_back("lO"));EXPECT_EQ("heLLo", Str);EXPECT_TRUE(Str.consume_back_insensitive("lO"));EXPECT_EQ("heL", Str);EXPECT_FALSE(Str.consume_back_insensitive("helhel"));EXPECT_EQ("heL", Str);EXPECT_FALSE(Str.consume_back_insensitive("hle"));EXPECT_EQ("heL", Str);EXPECT_TRUE(Str.consume_back_insensitive("hEl"));EXPECT_EQ("", Str);EXPECT_FALSE(Str.consume_back_insensitive("h"));EXPECT_TRUE(Str.consume_back_insensitive(""));}TEST(StringRefTest, Find) {StringRef Str("helloHELLO");StringRef LongStr("hellx xello hell ello world foo bar hello HELLO");struct {StringRef Str;char C;std::size_t From;std::size_t Pos;std::size_t InsensitivePos;} CharExpectations[] = {{Str, 'h', 0U, 0U, 0U},{Str, 'e', 0U, 1U, 1U},{Str, 'l', 0U, 2U, 2U},{Str, 'l', 3U, 3U, 3U},{Str, 'o', 0U, 4U, 4U},{Str, 'L', 0U, 7U, 2U},{Str, 'z', 0U, StringRef::npos, StringRef::npos},};struct {StringRef Str;llvm::StringRef S;std::size_t From;std::size_t Pos;std::size_t InsensitivePos;} StrExpectations[] = {{Str, "helloword", 0, StringRef::npos, StringRef::npos},{Str, "hello", 0, 0U, 0U},{Str, "ello", 0, 1U, 1U},{Str, "zz", 0, StringRef::npos, StringRef::npos},{Str, "ll", 2U, 2U, 2U},{Str, "ll", 3U, StringRef::npos, 7U},{Str, "LL", 2U, 7U, 2U},{Str, "LL", 3U, 7U, 7U},{Str, "", 0U, 0U, 0U},{LongStr, "hello", 0U, 36U, 36U},{LongStr, "foo", 0U, 28U, 28U},{LongStr, "hell", 2U, 12U, 12U},{LongStr, "HELL", 2U, 42U, 12U},{LongStr, "", 0U, 0U, 0U}};for (auto &E : CharExpectations) {EXPECT_EQ(E.Pos, E.Str.find(E.C, E.From));EXPECT_EQ(E.InsensitivePos, E.Str.find_insensitive(E.C, E.From));EXPECT_EQ(E.InsensitivePos, E.Str.find_insensitive(toupper(E.C), E.From));}for (auto &E : StrExpectations) {EXPECT_EQ(E.Pos, E.Str.find(E.S, E.From));EXPECT_EQ(E.InsensitivePos, E.Str.find_insensitive(E.S, E.From));EXPECT_EQ(E.InsensitivePos, E.Str.find_insensitive(E.S.upper(), E.From));}EXPECT_EQ(3U, Str.rfind('l'));EXPECT_EQ(StringRef::npos, Str.rfind('z'));EXPECT_EQ(StringRef::npos, Str.rfind("helloworld"));EXPECT_EQ(0U, Str.rfind("hello"));EXPECT_EQ(1U, Str.rfind("ello"));EXPECT_EQ(StringRef::npos, Str.rfind("zz"));EXPECT_EQ(8U, Str.rfind_insensitive('l'));EXPECT_EQ(8U, Str.rfind_insensitive('L'));EXPECT_EQ(StringRef::npos, Str.rfind_insensitive('z'));EXPECT_EQ(StringRef::npos, Str.rfind_insensitive("HELLOWORLD"));EXPECT_EQ(5U, Str.rfind("HELLO"));EXPECT_EQ(6U, Str.rfind("ELLO"));EXPECT_EQ(StringRef::npos, Str.rfind("ZZ"));EXPECT_EQ(2U, Str.find_first_of('l'));EXPECT_EQ(1U, Str.find_first_of("el"));EXPECT_EQ(StringRef::npos, Str.find_first_of("xyz"));Str = "hello";EXPECT_EQ(1U, Str.find_first_not_of('h'));EXPECT_EQ(4U, Str.find_first_not_of("hel"));EXPECT_EQ(StringRef::npos, Str.find_first_not_of("hello"));EXPECT_EQ(3U, Str.find_last_not_of('o'));EXPECT_EQ(1U, Str.find_last_not_of("lo"));EXPECT_EQ(StringRef::npos, Str.find_last_not_of("helo"));}TEST(StringRefTest, Count) {StringRef Str("hello");EXPECT_EQ(2U, Str.count('l'));EXPECT_EQ(1U, Str.count('o'));EXPECT_EQ(0U, Str.count('z'));EXPECT_EQ(0U, Str.count("helloworld"));EXPECT_EQ(1U, Str.count("hello"));EXPECT_EQ(1U, Str.count("ello"));EXPECT_EQ(0U, Str.count("zz"));EXPECT_EQ(0U, Str.count(""));StringRef OverlappingAbba("abbabba");EXPECT_EQ(1U, OverlappingAbba.count("abba"));StringRef NonOverlappingAbba("abbaabba");EXPECT_EQ(2U, NonOverlappingAbba.count("abba"));StringRef ComplexAbba("abbabbaxyzabbaxyz");EXPECT_EQ(2U, ComplexAbba.count("abba"));}TEST(StringRefTest, EditDistance) {StringRef Hello("hello");EXPECT_EQ(2U, Hello.edit_distance("hill"));StringRef Industry("industry");EXPECT_EQ(6U, Industry.edit_distance("interest"));StringRef Soylent("soylent green is people");EXPECT_EQ(19U, Soylent.edit_distance("people soiled our green"));EXPECT_EQ(26U, Soylent.edit_distance("people soiled our green",/* allow replacements = */ false));EXPECT_EQ(9U, Soylent.edit_distance("people soiled our green",/* allow replacements = */ true,/* max edit distance = */ 8));EXPECT_EQ(53U, Soylent.edit_distance("people soiled our green ""people soiled our green ""people soiled our green "));}TEST(StringRefTest, EditDistanceInsensitive) {StringRef Hello("HELLO");EXPECT_EQ(2U, Hello.edit_distance_insensitive("hill"));EXPECT_EQ(0U, Hello.edit_distance_insensitive("hello"));StringRef Industry("InDuStRy");EXPECT_EQ(6U, Industry.edit_distance_insensitive("iNtErEsT"));}TEST(StringRefTest, Misc) {std::string Storage;raw_string_ostream OS(Storage);OS << StringRef("hello");EXPECT_EQ("hello", OS.str());}TEST(StringRefTest, Hashing) {EXPECT_EQ(hash_value(std::string()), hash_value(StringRef()));EXPECT_EQ(hash_value(std::string()), hash_value(StringRef("")));std::string S = "hello world";hash_code H = hash_value(S);EXPECT_EQ(H, hash_value(StringRef("hello world")));EXPECT_EQ(H, hash_value(StringRef(S)));EXPECT_NE(H, hash_value(StringRef("hello worl")));EXPECT_EQ(hash_value(std::string("hello worl")),hash_value(StringRef("hello worl")));EXPECT_NE(H, hash_value(StringRef("hello world ")));EXPECT_EQ(hash_value(std::string("hello world ")),hash_value(StringRef("hello world ")));EXPECT_EQ(H, hash_value(StringRef("hello world\0")));EXPECT_NE(hash_value(std::string("ello worl")),hash_value(StringRef("hello world").slice(1, -1)));}struct UnsignedPair {const char *Str;uint64_t Expected;} Unsigned[] ={ {"0", 0}, {"255", 255}, {"256", 256}, {"65535", 65535}, {"65536", 65536}, {"4294967295", 4294967295ULL}, {"4294967296", 4294967296ULL}, {"18446744073709551615", 18446744073709551615ULL}, {"042", 34}, {"0x42", 66}, {"0b101010", 42}};struct SignedPair {const char *Str;int64_t Expected;} Signed[] ={ {"0", 0}, {"-0", 0}, {"127", 127}, {"128", 128}, {"-128", -128}, {"-129", -129}, {"32767", 32767}, {"32768", 32768}, {"-32768", -32768}, {"-32769", -32769}, {"2147483647", 2147483647LL}, {"2147483648", 2147483648LL}, {"-2147483648", -2147483648LL}, {"-2147483649", -2147483649LL}, {"-9223372036854775808", -(9223372036854775807LL) - 1}, {"042", 34}, {"0x42", 66}, {"0b101010", 42}, {"-042", -34}, {"-0x42", -66}, {"-0b101010", -42}};TEST(StringRefTest, getAsInteger) {uint8_t U8;uint16_t U16;uint32_t U32;uint64_t U64;for (size_t i = 0; i < array_lengthof(Unsigned); ++i) {bool U8Success = StringRef(Unsigned[i].Str).getAsInteger(0, U8);if (static_cast<uint8_t>(Unsigned[i].Expected) == Unsigned[i].Expected) {ASSERT_FALSE(U8Success);EXPECT_EQ(U8, Unsigned[i].Expected);} else {ASSERT_TRUE(U8Success);}bool U16Success = StringRef(Unsigned[i].Str).getAsInteger(0, U16);if (static_cast<uint16_t>(Unsigned[i].Expected) == Unsigned[i].Expected) {ASSERT_FALSE(U16Success);EXPECT_EQ(U16, Unsigned[i].Expected);} else {ASSERT_TRUE(U16Success);}bool U32Success = StringRef(Unsigned[i].Str).getAsInteger(0, U32);if (static_cast<uint32_t>(Unsigned[i].Expected) == Unsigned[i].Expected) {ASSERT_FALSE(U32Success);EXPECT_EQ(U32, Unsigned[i].Expected);} else {ASSERT_TRUE(U32Success);}bool U64Success = StringRef(Unsigned[i].Str).getAsInteger(0, U64);ASSERT_FALSE(U64Success);EXPECT_EQ(U64, Unsigned[i].Expected);}int8_t S8;int16_t S16;int32_t S32;int64_t S64;for (size_t i = 0; i < array_lengthof(Signed); ++i) {bool S8Success = StringRef(Signed[i].Str).getAsInteger(0, S8);if (static_cast<int8_t>(Signed[i].Expected) == Signed[i].Expected) {ASSERT_FALSE(S8Success);EXPECT_EQ(S8, Signed[i].Expected);} else {ASSERT_TRUE(S8Success);}bool S16Success = StringRef(Signed[i].Str).getAsInteger(0, S16);if (static_cast<int16_t>(Signed[i].Expected) == Signed[i].Expected) {ASSERT_FALSE(S16Success);EXPECT_EQ(S16, Signed[i].Expected);} else {ASSERT_TRUE(S16Success);}bool S32Success = StringRef(Signed[i].Str).getAsInteger(0, S32);if (static_cast<int32_t>(Signed[i].Expected) == Signed[i].Expected) {ASSERT_FALSE(S32Success);EXPECT_EQ(S32, Signed[i].Expected);} else {ASSERT_TRUE(S32Success);}bool S64Success = StringRef(Signed[i].Str).getAsInteger(0, S64);ASSERT_FALSE(S64Success);EXPECT_EQ(S64, Signed[i].Expected);}}static const char* BadStrings[] = {"" // empty string, "18446744073709551617" // value just over max, "123456789012345678901" // value way too large, "4t23v" // illegal decimal characters, "0x123W56" // illegal hex characters, "0b2" // illegal bin characters, "08" // illegal oct characters, "0o8" // illegal oct characters, "-123" // negative unsigned value, "0x", "0b"};TEST(StringRefTest, getAsUnsignedIntegerBadStrings) {unsigned long long U64;for (size_t i = 0; i < array_lengthof(BadStrings); ++i) {bool IsBadNumber = StringRef(BadStrings[i]).getAsInteger(0, U64);ASSERT_TRUE(IsBadNumber);}}struct ConsumeUnsignedPair {const char *Str;uint64_t Expected;const char *Leftover;} ConsumeUnsigned[] = {{"0", 0, ""},{"255", 255, ""},{"256", 256, ""},{"65535", 65535, ""},{"65536", 65536, ""},{"4294967295", 4294967295ULL, ""},{"4294967296", 4294967296ULL, ""},{"255A376", 255, "A376"},{"18446744073709551615", 18446744073709551615ULL, ""},{"18446744073709551615ABC", 18446744073709551615ULL, "ABC"},{"042", 34, ""},{"0x42", 66, ""},{"0x42-0x34", 66, "-0x34"},{"0b101010", 42, ""},{"0429F", 042, "9F"}, // Auto-sensed octal radix, invalid digit{"0x42G12", 0x42, "G12"}, // Auto-sensed hex radix, invalid digit{"0b10101020101", 42, "20101"}}; // Auto-sensed binary radix, invalid digit.struct ConsumeSignedPair {const char *Str;int64_t Expected;const char *Leftover;} ConsumeSigned[] = {{"0", 0, ""},{"-0", 0, ""},{"0-1", 0, "-1"},{"-0-1", 0, "-1"},{"127", 127, ""},{"128", 128, ""},{"127-1", 127, "-1"},{"128-1", 128, "-1"},{"-128", -128, ""},{"-129", -129, ""},{"-128-1", -128, "-1"},{"-129-1", -129, "-1"},{"32767", 32767, ""},{"32768", 32768, ""},{"32767-1", 32767, "-1"},{"32768-1", 32768, "-1"},{"-32768", -32768, ""},{"-32769", -32769, ""},{"-32768-1", -32768, "-1"},{"-32769-1", -32769, "-1"},{"2147483647", 2147483647LL, ""},{"2147483648", 2147483648LL, ""},{"2147483647-1", 2147483647LL, "-1"},{"2147483648-1", 2147483648LL, "-1"},{"-2147483648", -2147483648LL, ""},{"-2147483649", -2147483649LL, ""},{"-2147483648-1", -2147483648LL, "-1"},{"-2147483649-1", -2147483649LL, "-1"},{"-9223372036854775808", -(9223372036854775807LL) - 1, ""},{"-9223372036854775808-1", -(9223372036854775807LL) - 1, "-1"},{"042", 34, ""},{"042-1", 34, "-1"},{"0x42", 66, ""},{"0x42-1", 66, "-1"},{"0b101010", 42, ""},{"0b101010-1", 42, "-1"},{"-042", -34, ""},{"-042-1", -34, "-1"},{"-0x42", -66, ""},{"-0x42-1", -66, "-1"},{"-0b101010", -42, ""},{"-0b101010-1", -42, "-1"}};TEST(StringRefTest, consumeIntegerUnsigned) {uint8_t U8;uint16_t U16;uint32_t U32;uint64_t U64;for (size_t i = 0; i < array_lengthof(ConsumeUnsigned); ++i) {StringRef Str = ConsumeUnsigned[i].Str;bool U8Success = Str.consumeInteger(0, U8);if (static_cast<uint8_t>(ConsumeUnsigned[i].Expected) ==ConsumeUnsigned[i].Expected) {ASSERT_FALSE(U8Success);EXPECT_EQ(U8, ConsumeUnsigned[i].Expected);EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover);} else {ASSERT_TRUE(U8Success);}Str = ConsumeUnsigned[i].Str;bool U16Success = Str.consumeInteger(0, U16);if (static_cast<uint16_t>(ConsumeUnsigned[i].Expected) ==ConsumeUnsigned[i].Expected) {ASSERT_FALSE(U16Success);EXPECT_EQ(U16, ConsumeUnsigned[i].Expected);EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover);} else {ASSERT_TRUE(U16Success);}Str = ConsumeUnsigned[i].Str;bool U32Success = Str.consumeInteger(0, U32);if (static_cast<uint32_t>(ConsumeUnsigned[i].Expected) ==ConsumeUnsigned[i].Expected) {ASSERT_FALSE(U32Success);EXPECT_EQ(U32, ConsumeUnsigned[i].Expected);EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover);} else {ASSERT_TRUE(U32Success);}Str = ConsumeUnsigned[i].Str;bool U64Success = Str.consumeInteger(0, U64);ASSERT_FALSE(U64Success);EXPECT_EQ(U64, ConsumeUnsigned[i].Expected);EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover);}}TEST(StringRefTest, consumeIntegerSigned) {int8_t S8;int16_t S16;int32_t S32;int64_t S64;for (size_t i = 0; i < array_lengthof(ConsumeSigned); ++i) {StringRef Str = ConsumeSigned[i].Str;bool S8Success = Str.consumeInteger(0, S8);if (static_cast<int8_t>(ConsumeSigned[i].Expected) ==ConsumeSigned[i].Expected) {ASSERT_FALSE(S8Success);EXPECT_EQ(S8, ConsumeSigned[i].Expected);EXPECT_EQ(Str, ConsumeSigned[i].Leftover);} else {ASSERT_TRUE(S8Success);}Str = ConsumeSigned[i].Str;bool S16Success = Str.consumeInteger(0, S16);if (static_cast<int16_t>(ConsumeSigned[i].Expected) ==ConsumeSigned[i].Expected) {ASSERT_FALSE(S16Success);EXPECT_EQ(S16, ConsumeSigned[i].Expected);EXPECT_EQ(Str, ConsumeSigned[i].Leftover);} else {ASSERT_TRUE(S16Success);}Str = ConsumeSigned[i].Str;bool S32Success = Str.consumeInteger(0, S32);if (static_cast<int32_t>(ConsumeSigned[i].Expected) ==ConsumeSigned[i].Expected) {ASSERT_FALSE(S32Success);EXPECT_EQ(S32, ConsumeSigned[i].Expected);EXPECT_EQ(Str, ConsumeSigned[i].Leftover);} else {ASSERT_TRUE(S32Success);}Str = ConsumeSigned[i].Str;bool S64Success = Str.consumeInteger(0, S64);ASSERT_FALSE(S64Success);EXPECT_EQ(S64, ConsumeSigned[i].Expected);EXPECT_EQ(Str, ConsumeSigned[i].Leftover);}}struct GetDoubleStrings {const char *Str;bool AllowInexact;bool ShouldFail;double D;} DoubleStrings[] = {{"0", false, false, 0.0},{"0.0", false, false, 0.0},{"-0.0", false, false, -0.0},{"123.45", false, true, 123.45},{"123.45", true, false, 123.45},{"1.8e308", true, false, std::numeric_limits<double>::infinity()},{"1.8e308", false, true, std::numeric_limits<double>::infinity()},{"0x0.0000000000001P-1023", false, true, 0.0},{"0x0.0000000000001P-1023", true, false, 0.0},};TEST(StringRefTest, getAsDouble) {for (const auto &Entry : DoubleStrings) {double Result;StringRef S(Entry.Str);EXPECT_EQ(Entry.ShouldFail, S.getAsDouble(Result, Entry.AllowInexact));if (!Entry.ShouldFail) {EXPECT_EQ(Result, Entry.D);}}}static const char *join_input[] = { "a", "b", "c" };static const char join_result1[] = "a";static const char join_result2[] = "a:b:c";static const char join_result3[] = "a::b::c";TEST(StringRefTest, joinStrings) {std::vector<StringRef> v1;std::vector<std::string> v2;for (size_t i = 0; i < array_lengthof(join_input); ++i) {v1.push_back(join_input[i]);v2.push_back(join_input[i]);}bool v1_join1 = join(v1.begin(), v1.begin() + 1, ":") == join_result1;EXPECT_TRUE(v1_join1);bool v1_join2 = join(v1.begin(), v1.end(), ":") == join_result2;EXPECT_TRUE(v1_join2);bool v1_join3 = join(v1.begin(), v1.end(), "::") == join_result3;EXPECT_TRUE(v1_join3);bool v2_join1 = join(v2.begin(), v2.begin() + 1, ":") == join_result1;EXPECT_TRUE(v2_join1);bool v2_join2 = join(v2.begin(), v2.end(), ":") == join_result2;EXPECT_TRUE(v2_join2);bool v2_join3 = join(v2.begin(), v2.end(), "::") == join_result3;EXPECT_TRUE(v2_join3);v2_join3 = join(v2, "::") == join_result3;EXPECT_TRUE(v2_join3);}TEST(StringRefTest, AllocatorCopy) {BumpPtrAllocator Alloc;// First test empty strings. We don't want these to allocate anything on the// allocator.StringRef StrEmpty = "";StringRef StrEmptyc = StrEmpty.copy(Alloc);EXPECT_TRUE(StrEmpty.equals(StrEmptyc));EXPECT_EQ(StrEmptyc.data(), nullptr);EXPECT_EQ(StrEmptyc.size(), 0u);EXPECT_EQ(Alloc.getTotalMemory(), 0u);StringRef Str1 = "hello";StringRef Str2 = "bye";StringRef Str1c = Str1.copy(Alloc);StringRef Str2c = Str2.copy(Alloc);EXPECT_TRUE(Str1.equals(Str1c));EXPECT_NE(Str1.data(), Str1c.data());EXPECT_TRUE(Str2.equals(Str2c));EXPECT_NE(Str2.data(), Str2c.data());}TEST(StringRefTest, Drop) {StringRef Test("StringRefTest::Drop");StringRef Dropped = Test.drop_front(5);EXPECT_EQ(Dropped, "gRefTest::Drop");Dropped = Test.drop_back(5);EXPECT_EQ(Dropped, "StringRefTest:");Dropped = Test.drop_front(0);EXPECT_EQ(Dropped, Test);Dropped = Test.drop_back(0);EXPECT_EQ(Dropped, Test);Dropped = Test.drop_front(Test.size());EXPECT_TRUE(Dropped.empty());Dropped = Test.drop_back(Test.size());EXPECT_TRUE(Dropped.empty());}TEST(StringRefTest, Take) {StringRef Test("StringRefTest::Take");StringRef Taken = Test.take_front(5);EXPECT_EQ(Taken, "Strin");Taken = Test.take_back(5);EXPECT_EQ(Taken, ":Take");Taken = Test.take_front(Test.size());EXPECT_EQ(Taken, Test);Taken = Test.take_back(Test.size());EXPECT_EQ(Taken, Test);Taken = Test.take_front(0);EXPECT_TRUE(Taken.empty());Taken = Test.take_back(0);EXPECT_TRUE(Taken.empty());}TEST(StringRefTest, FindIf) {StringRef Punct("Test.String");StringRef NoPunct("ABCDEFG");StringRef Empty;auto IsPunct = [](char c) { return ::ispunct(c); };auto IsAlpha = [](char c) { return ::isalpha(c); };EXPECT_EQ(4U, Punct.find_if(IsPunct));EXPECT_EQ(StringRef::npos, NoPunct.find_if(IsPunct));EXPECT_EQ(StringRef::npos, Empty.find_if(IsPunct));EXPECT_EQ(4U, Punct.find_if_not(IsAlpha));EXPECT_EQ(StringRef::npos, NoPunct.find_if_not(IsAlpha));EXPECT_EQ(StringRef::npos, Empty.find_if_not(IsAlpha));}TEST(StringRefTest, TakeWhileUntil) {StringRef Test("String With 1 Number");StringRef Taken = Test.take_while([](char c) { return ::isdigit(c); });EXPECT_EQ("", Taken);Taken = Test.take_until([](char c) { return ::isdigit(c); });EXPECT_EQ("String With ", Taken);Taken = Test.take_while([](char c) { return true; });EXPECT_EQ(Test, Taken);Taken = Test.take_until([](char c) { return true; });EXPECT_EQ("", Taken);Test = "";Taken = Test.take_while([](char c) { return true; });EXPECT_EQ("", Taken);}TEST(StringRefTest, DropWhileUntil) {StringRef Test("String With 1 Number");StringRef Taken = Test.drop_while([](char c) { return ::isdigit(c); });EXPECT_EQ(Test, Taken);Taken = Test.drop_until([](char c) { return ::isdigit(c); });EXPECT_EQ("1 Number", Taken);Taken = Test.drop_while([](char c) { return true; });EXPECT_EQ("", Taken);Taken = Test.drop_until([](char c) { return true; });EXPECT_EQ(Test, Taken);StringRef EmptyString = "";Taken = EmptyString.drop_while([](char c) { return true; });EXPECT_EQ("", Taken);}TEST(StringRefTest, StringLiteral) {constexpr StringRef StringRefs[] = {"Foo", "Bar"};EXPECT_EQ(StringRef("Foo"), StringRefs[0]);EXPECT_EQ(3u, (std::integral_constant<size_t, StringRefs[0].size()>::value));EXPECT_EQ(false, (std::integral_constant<bool, StringRefs[0].empty()>::value));EXPECT_EQ(StringRef("Bar"), StringRefs[1]);constexpr StringLiteral Strings[] = {"Foo", "Bar"};EXPECT_EQ(StringRef("Foo"), Strings[0]);EXPECT_EQ(3u, (std::integral_constant<size_t, Strings[0].size()>::value));EXPECT_EQ(false, (std::integral_constant<bool, Strings[0].empty()>::value));EXPECT_EQ(StringRef("Bar"), Strings[1]);}// Check gtest prints StringRef as a string instead of a container of chars.// The code is in utils/unittest/googletest/internal/custom/gtest-printers.hTEST(StringRefTest, GTestPrinter) {EXPECT_EQ(R"("foo")", ::testing::PrintToString(StringRef("foo")));}TEST(StringRefTest, LFLineEnding) {constexpr StringRef Cases[] = {"\nDoggo\nPupper", "Floofer\n", "Woofer"};EXPECT_EQ(StringRef("\n"), Cases[0].detectEOL());EXPECT_EQ(StringRef("\n"), Cases[1].detectEOL());EXPECT_EQ(StringRef("\n"), Cases[2].detectEOL());}TEST(StringRefTest, CRLineEnding) {constexpr StringRef Cases[] = {"\rDoggo\rPupper", "Floofer\r", "Woo\rfer\n"};EXPECT_EQ(StringRef("\r"), Cases[0].detectEOL());EXPECT_EQ(StringRef("\r"), Cases[1].detectEOL());EXPECT_EQ(StringRef("\r"), Cases[2].detectEOL());}TEST(StringRefTest, CRLFLineEnding) {constexpr StringRef Cases[] = {"\r\nDoggo\r\nPupper", "Floofer\r\n","Woofer\r\nSubWoofer\n"};EXPECT_EQ(StringRef("\r\n"), Cases[0].detectEOL());EXPECT_EQ(StringRef("\r\n"), Cases[1].detectEOL());EXPECT_EQ(StringRef("\r\n"), Cases[2].detectEOL());}TEST(StringRefTest, LFCRLineEnding) {constexpr StringRef Cases[] = {"\n\rDoggo\n\rPupper", "Floofer\n\r","Woofer\n\rSubWoofer\n"};EXPECT_EQ(StringRef("\n\r"), Cases[0].detectEOL());EXPECT_EQ(StringRef("\n\r"), Cases[1].detectEOL());EXPECT_EQ(StringRef("\n\r"), Cases[2].detectEOL());}static_assert(std::is_trivially_copyable<StringRef>::value,"trivially copyable");} // end anonymous namespace
//===- llvm/unittest/ADT/StringMapMap.cpp - StringMap unit tests ----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringMap.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/Twine.h"#include "llvm/Support/DataTypes.h"#include "gtest/gtest.h"#include <limits>#include <tuple>using namespace llvm;namespace {static_assert(sizeof(StringMap<uint32_t>) <sizeof(StringMap<uint32_t, MallocAllocator &>),"Ensure empty base optimization happens with default allocator");// Test fixtureclass StringMapTest : public testing::Test {protected:StringMap<uint32_t> testMap;static const char testKey[];static const uint32_t testValue;static const char* testKeyFirst;static size_t testKeyLength;static const std::string testKeyStr;void assertEmptyMap() {// Size testsEXPECT_EQ(0u, testMap.size());EXPECT_TRUE(testMap.empty());// Iterator testsEXPECT_TRUE(testMap.begin() == testMap.end());// Lookup testsEXPECT_EQ(0u, testMap.count(testKey));EXPECT_EQ(0u, testMap.count(StringRef(testKeyFirst, testKeyLength)));EXPECT_EQ(0u, testMap.count(testKeyStr));EXPECT_TRUE(testMap.find(testKey) == testMap.end());EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) ==testMap.end());EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end());}void assertSingleItemMap() {// Size testsEXPECT_EQ(1u, testMap.size());EXPECT_FALSE(testMap.begin() == testMap.end());EXPECT_FALSE(testMap.empty());// Iterator testsStringMap<uint32_t>::iterator it = testMap.begin();EXPECT_STREQ(testKey, it->first().data());EXPECT_EQ(testValue, it->second);++it;EXPECT_TRUE(it == testMap.end());// Lookup testsEXPECT_EQ(1u, testMap.count(testKey));EXPECT_EQ(1u, testMap.count(StringRef(testKeyFirst, testKeyLength)));EXPECT_EQ(1u, testMap.count(testKeyStr));EXPECT_TRUE(testMap.find(testKey) == testMap.begin());EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) ==testMap.begin());EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin());}};const char StringMapTest::testKey[] = "key";const uint32_t StringMapTest::testValue = 1u;const char* StringMapTest::testKeyFirst = testKey;size_t StringMapTest::testKeyLength = sizeof(testKey) - 1;const std::string StringMapTest::testKeyStr(testKey);struct CountCopyAndMove {CountCopyAndMove() = default;CountCopyAndMove(const CountCopyAndMove &) { copy = 1; }CountCopyAndMove(CountCopyAndMove &&) { move = 1; }void operator=(const CountCopyAndMove &) { ++copy; }void operator=(CountCopyAndMove &&) { ++move; }int copy = 0;int move = 0;};// Empty map tests.TEST_F(StringMapTest, EmptyMapTest) {assertEmptyMap();}// Constant map tests.TEST_F(StringMapTest, ConstEmptyMapTest) {const StringMap<uint32_t>& constTestMap = testMap;// Size testsEXPECT_EQ(0u, constTestMap.size());EXPECT_TRUE(constTestMap.empty());// Iterator testsEXPECT_TRUE(constTestMap.begin() == constTestMap.end());// Lookup testsEXPECT_EQ(0u, constTestMap.count(testKey));EXPECT_EQ(0u, constTestMap.count(StringRef(testKeyFirst, testKeyLength)));EXPECT_EQ(0u, constTestMap.count(testKeyStr));EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end());EXPECT_TRUE(constTestMap.find(StringRef(testKeyFirst, testKeyLength)) ==constTestMap.end());EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end());}// initializer_list ctor test; also implicitly tests initializer_list and// iterator overloads of insert().TEST_F(StringMapTest, InitializerListCtor) {testMap = StringMap<uint32_t>({{"key", 1}});assertSingleItemMap();}// A map with a single entry.TEST_F(StringMapTest, SingleEntryMapTest) {testMap[testKey] = testValue;assertSingleItemMap();}// Test clear() method.TEST_F(StringMapTest, ClearTest) {testMap[testKey] = testValue;testMap.clear();assertEmptyMap();}// Test erase(iterator) method.TEST_F(StringMapTest, EraseIteratorTest) {testMap[testKey] = testValue;testMap.erase(testMap.begin());assertEmptyMap();}// Test erase(value) method.TEST_F(StringMapTest, EraseValueTest) {testMap[testKey] = testValue;testMap.erase(testKey);assertEmptyMap();}// Test inserting two values and erasing one.TEST_F(StringMapTest, InsertAndEraseTest) {testMap[testKey] = testValue;testMap["otherKey"] = 2;testMap.erase("otherKey");assertSingleItemMap();}TEST_F(StringMapTest, SmallFullMapTest) {// StringMap has a tricky corner case when the map is small (<8 buckets) and// it fills up through a balanced pattern of inserts and erases. This can// lead to inf-loops in some cases (PR13148) so we test it explicitly here.llvm::StringMap<int> Map(2);Map["eins"] = 1;Map["zwei"] = 2;Map["drei"] = 3;Map.erase("drei");Map.erase("eins");Map["veir"] = 4;Map["funf"] = 5;EXPECT_EQ(3u, Map.size());EXPECT_EQ(0, Map.lookup("eins"));EXPECT_EQ(2, Map.lookup("zwei"));EXPECT_EQ(0, Map.lookup("drei"));EXPECT_EQ(4, Map.lookup("veir"));EXPECT_EQ(5, Map.lookup("funf"));}TEST_F(StringMapTest, CopyCtorTest) {llvm::StringMap<int> Map;Map["eins"] = 1;Map["zwei"] = 2;Map["drei"] = 3;Map.erase("drei");Map.erase("eins");Map["veir"] = 4;Map["funf"] = 5;EXPECT_EQ(3u, Map.size());EXPECT_EQ(0, Map.lookup("eins"));EXPECT_EQ(2, Map.lookup("zwei"));EXPECT_EQ(0, Map.lookup("drei"));EXPECT_EQ(4, Map.lookup("veir"));EXPECT_EQ(5, Map.lookup("funf"));llvm::StringMap<int> Map2(Map);EXPECT_EQ(3u, Map2.size());EXPECT_EQ(0, Map2.lookup("eins"));EXPECT_EQ(2, Map2.lookup("zwei"));EXPECT_EQ(0, Map2.lookup("drei"));EXPECT_EQ(4, Map2.lookup("veir"));EXPECT_EQ(5, Map2.lookup("funf"));}// A more complex iteration test.TEST_F(StringMapTest, IterationTest) {bool visited[100];// Insert 100 numbers into the mapfor (int i = 0; i < 100; ++i) {std::stringstream ss;ss << "key_" << i;testMap[ss.str()] = i;visited[i] = false;}// Iterate over all numbers and mark each one found.for (StringMap<uint32_t>::iterator it = testMap.begin();it != testMap.end(); ++it) {std::stringstream ss;ss << "key_" << it->second;ASSERT_STREQ(ss.str().c_str(), it->first().data());visited[it->second] = true;}// Ensure every number was visited.for (int i = 0; i < 100; ++i) {ASSERT_TRUE(visited[i]) << "Entry #" << i << " was never visited";}}// Test StringMapEntry::Create() method.TEST_F(StringMapTest, StringMapEntryTest) {MallocAllocator Allocator;StringMap<uint32_t>::value_type *entry =StringMap<uint32_t>::value_type::Create(StringRef(testKeyFirst, testKeyLength), Allocator, 1u);EXPECT_STREQ(testKey, entry->first().data());EXPECT_EQ(1u, entry->second);entry->Destroy(Allocator);}// Test insert() method.TEST_F(StringMapTest, InsertTest) {SCOPED_TRACE("InsertTest");testMap.insert(StringMap<uint32_t>::value_type::Create(StringRef(testKeyFirst, testKeyLength),testMap.getAllocator(), 1u));assertSingleItemMap();}// Test insert(pair<K, V>) methodTEST_F(StringMapTest, InsertPairTest) {bool Inserted;StringMap<uint32_t>::iterator NewIt;std::tie(NewIt, Inserted) =testMap.insert(std::make_pair(testKeyFirst, testValue));EXPECT_EQ(1u, testMap.size());EXPECT_EQ(testValue, testMap[testKeyFirst]);EXPECT_EQ(testKeyFirst, NewIt->first());EXPECT_EQ(testValue, NewIt->second);EXPECT_TRUE(Inserted);StringMap<uint32_t>::iterator ExistingIt;std::tie(ExistingIt, Inserted) =testMap.insert(std::make_pair(testKeyFirst, testValue + 1));EXPECT_EQ(1u, testMap.size());EXPECT_EQ(testValue, testMap[testKeyFirst]);EXPECT_FALSE(Inserted);EXPECT_EQ(NewIt, ExistingIt);}// Test insert(pair<K, V>) method when rehashing occursTEST_F(StringMapTest, InsertRehashingPairTest) {// Check that the correct iterator is returned when the inserted element is// moved to a different bucket during internal rehashing. This depends on// the particular key, and the implementation of StringMap and HashString.// Changes to those might result in this test not actually checking that.StringMap<uint32_t> t(0);EXPECT_EQ(0u, t.getNumBuckets());StringMap<uint32_t>::iterator It =t.insert(std::make_pair("abcdef", 42)).first;EXPECT_EQ(16u, t.getNumBuckets());EXPECT_EQ("abcdef", It->first());EXPECT_EQ(42u, It->second);}TEST_F(StringMapTest, InsertOrAssignTest) {struct A : CountCopyAndMove {A(int v) : v(v) {}int v;};StringMap<A> t(0);auto try1 = t.insert_or_assign("A", A(1));EXPECT_TRUE(try1.second);EXPECT_EQ(1, try1.first->second.v);EXPECT_EQ(1, try1.first->second.move);auto try2 = t.insert_or_assign("A", A(2));EXPECT_FALSE(try2.second);EXPECT_EQ(2, try2.first->second.v);EXPECT_EQ(2, try1.first->second.move);EXPECT_EQ(try1.first, try2.first);EXPECT_EQ(0, try1.first->second.copy);}TEST_F(StringMapTest, IterMapKeysVector) {StringMap<int> Map;Map["A"] = 1;Map["B"] = 2;Map["C"] = 3;Map["D"] = 3;std::vector<StringRef> Keys{Map.keys().begin(), Map.keys().end()};llvm::sort(Keys);std::vector<StringRef> Expected{{"A", "B", "C", "D"}};EXPECT_EQ(Expected, Keys);}TEST_F(StringMapTest, IterMapKeysSmallVector) {StringMap<int> Map;Map["A"] = 1;Map["B"] = 2;Map["C"] = 3;Map["D"] = 3;auto Keys = to_vector<4>(Map.keys());llvm::sort(Keys);SmallVector<StringRef, 4> Expected = {"A", "B", "C", "D"};EXPECT_EQ(Expected, Keys);}// Create a non-default constructable valuestruct StringMapTestStruct {StringMapTestStruct(int i) : i(i) {}StringMapTestStruct() = delete;int i;};TEST_F(StringMapTest, NonDefaultConstructable) {StringMap<StringMapTestStruct> t;t.insert(std::make_pair("Test", StringMapTestStruct(123)));StringMap<StringMapTestStruct>::iterator iter = t.find("Test");ASSERT_NE(iter, t.end());ASSERT_EQ(iter->second.i, 123);}struct Immovable {Immovable() {}Immovable(Immovable&&) = delete; // will disable the other special members};struct MoveOnly {int i;MoveOnly(int i) : i(i) {}MoveOnly(const Immovable&) : i(0) {}MoveOnly(MoveOnly &&RHS) : i(RHS.i) {}MoveOnly &operator=(MoveOnly &&RHS) {i = RHS.i;return *this;}private:MoveOnly(const MoveOnly &) = delete;MoveOnly &operator=(const MoveOnly &) = delete;};TEST_F(StringMapTest, MoveOnly) {StringMap<MoveOnly> t;t.insert(std::make_pair("Test", MoveOnly(42)));StringRef Key = "Test";StringMapEntry<MoveOnly>::Create(Key, t.getAllocator(), MoveOnly(42))->Destroy(t.getAllocator());}TEST_F(StringMapTest, CtorArg) {StringRef Key = "Test";MallocAllocator Allocator;StringMapEntry<MoveOnly>::Create(Key, Allocator, Immovable())->Destroy(Allocator);}TEST_F(StringMapTest, MoveConstruct) {StringMap<int> A;A["x"] = 42;StringMap<int> B = std::move(A);ASSERT_EQ(A.size(), 0u);ASSERT_EQ(B.size(), 1u);ASSERT_EQ(B["x"], 42);ASSERT_EQ(B.count("y"), 0u);}TEST_F(StringMapTest, MoveAssignment) {StringMap<int> A;A["x"] = 42;StringMap<int> B;B["y"] = 117;A = std::move(B);ASSERT_EQ(A.size(), 1u);ASSERT_EQ(B.size(), 0u);ASSERT_EQ(A["y"], 117);ASSERT_EQ(B.count("x"), 0u);}TEST_F(StringMapTest, EqualEmpty) {StringMap<int> A;StringMap<int> B;ASSERT_TRUE(A == B);ASSERT_FALSE(A != B);ASSERT_TRUE(A == A); // self check}TEST_F(StringMapTest, EqualWithValues) {StringMap<int> A;A["A"] = 1;A["B"] = 2;A["C"] = 3;A["D"] = 3;StringMap<int> B;B["A"] = 1;B["B"] = 2;B["C"] = 3;B["D"] = 3;ASSERT_TRUE(A == B);ASSERT_TRUE(B == A);ASSERT_FALSE(A != B);ASSERT_FALSE(B != A);ASSERT_TRUE(A == A); // self check}TEST_F(StringMapTest, NotEqualMissingKeys) {StringMap<int> A;A["A"] = 1;A["B"] = 2;StringMap<int> B;B["A"] = 1;B["B"] = 2;B["C"] = 3;B["D"] = 3;ASSERT_FALSE(A == B);ASSERT_FALSE(B == A);ASSERT_TRUE(A != B);ASSERT_TRUE(B != A);}TEST_F(StringMapTest, NotEqualWithDifferentValues) {StringMap<int> A;A["A"] = 1;A["B"] = 2;A["C"] = 100;A["D"] = 3;StringMap<int> B;B["A"] = 1;B["B"] = 2;B["C"] = 3;B["D"] = 3;ASSERT_FALSE(A == B);ASSERT_FALSE(B == A);ASSERT_TRUE(A != B);ASSERT_TRUE(B != A);}struct Countable {int &InstanceCount;int Number;Countable(int Number, int &InstanceCount): InstanceCount(InstanceCount), Number(Number) {++InstanceCount;}Countable(Countable &&C) : InstanceCount(C.InstanceCount), Number(C.Number) {++InstanceCount;C.Number = -1;}Countable(const Countable &C): InstanceCount(C.InstanceCount), Number(C.Number) {++InstanceCount;}Countable &operator=(Countable C) {Number = C.Number;return *this;}~Countable() { --InstanceCount; }};TEST_F(StringMapTest, MoveDtor) {int InstanceCount = 0;StringMap<Countable> A;A.insert(std::make_pair("x", Countable(42, InstanceCount)));ASSERT_EQ(InstanceCount, 1);auto I = A.find("x");ASSERT_NE(I, A.end());ASSERT_EQ(I->second.Number, 42);StringMap<Countable> B;B = std::move(A);ASSERT_EQ(InstanceCount, 1);ASSERT_TRUE(A.empty());I = B.find("x");ASSERT_NE(I, B.end());ASSERT_EQ(I->second.Number, 42);B = StringMap<Countable>();ASSERT_EQ(InstanceCount, 0);ASSERT_TRUE(B.empty());}namespace {// Simple class that counts how many moves and copy happens when growing a mapstruct CountCtorCopyAndMove {static unsigned Ctor;static unsigned Move;static unsigned Copy;int Data = 0;CountCtorCopyAndMove(int Data) : Data(Data) { Ctor++; }CountCtorCopyAndMove() { Ctor++; }CountCtorCopyAndMove(const CountCtorCopyAndMove &) { Copy++; }CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &) {Copy++;return *this;}CountCtorCopyAndMove(CountCtorCopyAndMove &&) { Move++; }CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &&) {Move++;return *this;}};unsigned CountCtorCopyAndMove::Copy = 0;unsigned CountCtorCopyAndMove::Move = 0;unsigned CountCtorCopyAndMove::Ctor = 0;} // anonymous namespace// Make sure creating the map with an initial size of N actually gives us enough// buckets to insert N items without increasing allocation size.TEST(StringMapCustomTest, InitialSizeTest) {// 1 is an "edge value", 32 is an arbitrary power of two, and 67 is an// arbitrary prime, picked without any good reason.for (auto Size : {1, 32, 67}) {StringMap<CountCtorCopyAndMove> Map(Size);auto NumBuckets = Map.getNumBuckets();CountCtorCopyAndMove::Move = 0;CountCtorCopyAndMove::Copy = 0;for (int i = 0; i < Size; ++i)Map.insert(std::pair<std::string, CountCtorCopyAndMove>(std::piecewise_construct, std::forward_as_tuple(Twine(i).str()),std::forward_as_tuple(i)));// After the initial move, the map will move the Elts in the Entry.EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move);// We copy once the pair from the Elts vectorEXPECT_EQ(0u, CountCtorCopyAndMove::Copy);// Check that the map didn't growEXPECT_EQ(Map.getNumBuckets(), NumBuckets);}}TEST(StringMapCustomTest, BracketOperatorCtor) {StringMap<CountCtorCopyAndMove> Map;CountCtorCopyAndMove::Ctor = 0;Map["abcd"];EXPECT_EQ(1u, CountCtorCopyAndMove::Ctor);// Test that operator[] does not create a value when it is already in the mapCountCtorCopyAndMove::Ctor = 0;Map["abcd"];EXPECT_EQ(0u, CountCtorCopyAndMove::Ctor);}namespace {struct NonMoveableNonCopyableType {int Data = 0;NonMoveableNonCopyableType() = default;NonMoveableNonCopyableType(int Data) : Data(Data) {}NonMoveableNonCopyableType(const NonMoveableNonCopyableType &) = delete;NonMoveableNonCopyableType(NonMoveableNonCopyableType &&) = delete;};}// Test that we can "emplace" an element in the map without involving map/moveTEST(StringMapCustomTest, EmplaceTest) {StringMap<NonMoveableNonCopyableType> Map;Map.try_emplace("abcd", 42);EXPECT_EQ(1u, Map.count("abcd"));EXPECT_EQ(42, Map["abcd"].Data);}// Test that StringMapEntryBase can handle size_t wide sizes.TEST(StringMapCustomTest, StringMapEntryBaseSize) {size_t LargeValue;// Test that the entry can represent max-unsigned.if (sizeof(size_t) <= sizeof(unsigned))LargeValue = std::numeric_limits<unsigned>::max();elseLargeValue = std::numeric_limits<unsigned>::max() + 1ULL;StringMapEntryBase LargeBase(LargeValue);EXPECT_EQ(LargeValue, LargeBase.getKeyLength());// Test that the entry can hold at least max size_t.LargeValue = std::numeric_limits<size_t>::max();StringMapEntryBase LargerBase(LargeValue);LargeValue = std::numeric_limits<size_t>::max();EXPECT_EQ(LargeValue, LargerBase.getKeyLength());}// Test that StringMapEntry can handle size_t wide sizes.TEST(StringMapCustomTest, StringMapEntrySize) {size_t LargeValue;// Test that the entry can represent max-unsigned.if (sizeof(size_t) <= sizeof(unsigned))LargeValue = std::numeric_limits<unsigned>::max();elseLargeValue = std::numeric_limits<unsigned>::max() + 1ULL;StringMapEntry<int> LargeEntry(LargeValue);StringRef Key = LargeEntry.getKey();EXPECT_EQ(LargeValue, Key.size());// Test that the entry can hold at least max size_t.LargeValue = std::numeric_limits<size_t>::max();StringMapEntry<int> LargerEntry(LargeValue);Key = LargerEntry.getKey();EXPECT_EQ(LargeValue, Key.size());}} // end anonymous namespace
//===- StringExtrasTest.cpp - Unit tests for String extras ----------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringExtras.h"#include "llvm/Support/raw_ostream.h"#include "gmock/gmock.h"#include "gtest/gtest.h"using namespace llvm;TEST(StringExtrasTest, isPrint) {EXPECT_FALSE(isPrint('\0'));EXPECT_FALSE(isPrint('\t'));EXPECT_TRUE(isPrint('0'));EXPECT_TRUE(isPrint('a'));EXPECT_TRUE(isPrint('A'));EXPECT_TRUE(isPrint(' '));EXPECT_TRUE(isPrint('~'));EXPECT_TRUE(isPrint('?'));}TEST(StringExtrasTest, isSpace) {EXPECT_TRUE(isSpace(' '));EXPECT_TRUE(isSpace('\t'));EXPECT_TRUE(isSpace('\n'));EXPECT_TRUE(isSpace('\v'));EXPECT_TRUE(isSpace('\f'));EXPECT_TRUE(isSpace('\v'));EXPECT_FALSE(isSpace('\0'));EXPECT_FALSE(isSpace('_'));}TEST(StringExtrasTest, Join) {std::vector<std::string> Items;EXPECT_EQ("", join(Items.begin(), Items.end(), " <sep> "));Items = {"foo"};EXPECT_EQ("foo", join(Items.begin(), Items.end(), " <sep> "));Items = {"foo", "bar"};EXPECT_EQ("foo <sep> bar", join(Items.begin(), Items.end(), " <sep> "));Items = {"foo", "bar", "baz"};EXPECT_EQ("foo <sep> bar <sep> baz",join(Items.begin(), Items.end(), " <sep> "));}TEST(StringExtrasTest, JoinItems) {const char *Foo = "foo";std::string Bar = "bar";llvm::StringRef Baz = "baz";char X = 'x';EXPECT_EQ("", join_items(" <sep> "));EXPECT_EQ("", join_items('/'));EXPECT_EQ("foo", join_items(" <sep> ", Foo));EXPECT_EQ("foo", join_items('/', Foo));EXPECT_EQ("foo <sep> bar", join_items(" <sep> ", Foo, Bar));EXPECT_EQ("foo/bar", join_items('/', Foo, Bar));EXPECT_EQ("foo <sep> bar <sep> baz", join_items(" <sep> ", Foo, Bar, Baz));EXPECT_EQ("foo/bar/baz", join_items('/', Foo, Bar, Baz));EXPECT_EQ("foo <sep> bar <sep> baz <sep> x",join_items(" <sep> ", Foo, Bar, Baz, X));EXPECT_EQ("foo/bar/baz/x", join_items('/', Foo, Bar, Baz, X));}TEST(StringExtrasTest, ToAndFromHex) {std::vector<uint8_t> OddBytes = {0x5, 0xBD, 0x0D, 0x3E, 0xCD};std::string OddStr = "05BD0D3ECD";StringRef OddData(reinterpret_cast<const char *>(OddBytes.data()),OddBytes.size());EXPECT_EQ(OddStr, toHex(OddData));EXPECT_EQ(OddData, fromHex(StringRef(OddStr).drop_front()));EXPECT_EQ(StringRef(OddStr).lower(), toHex(OddData, true));std::vector<uint8_t> EvenBytes = {0xA5, 0xBD, 0x0D, 0x3E, 0xCD};std::string EvenStr = "A5BD0D3ECD";StringRef EvenData(reinterpret_cast<const char *>(EvenBytes.data()),EvenBytes.size());EXPECT_EQ(EvenStr, toHex(EvenData));EXPECT_EQ(EvenData, fromHex(EvenStr));EXPECT_EQ(StringRef(EvenStr).lower(), toHex(EvenData, true));std::string InvalidStr = "A50\xFF";std::string IgnoredOutput;EXPECT_FALSE(tryGetFromHex(InvalidStr, IgnoredOutput));}TEST(StringExtrasTest, UINT64ToHex) {EXPECT_EQ(utohexstr(0xA0u), "A0");EXPECT_EQ(utohexstr(0xA0u, false, 4), "00A0");EXPECT_EQ(utohexstr(0xA0u, false, 8), "000000A0");}TEST(StringExtrasTest, to_float) {float F;EXPECT_TRUE(to_float("4.7", F));EXPECT_FLOAT_EQ(4.7f, F);double D;EXPECT_TRUE(to_float("4.7", D));EXPECT_DOUBLE_EQ(4.7, D);long double LD;EXPECT_TRUE(to_float("4.7", LD));EXPECT_DOUBLE_EQ(4.7, LD);EXPECT_FALSE(to_float("foo", F));EXPECT_FALSE(to_float("7.4 foo", F));EXPECT_FLOAT_EQ(4.7f, F); // F should be unchanged}TEST(StringExtrasTest, printLowerCase) {std::string str;raw_string_ostream OS(str);printLowerCase("ABCdefg01234.,&!~`'}\"", OS);EXPECT_EQ("abcdefg01234.,&!~`'}\"", OS.str());}TEST(StringExtrasTest, printEscapedString) {std::string str;raw_string_ostream OS(str);printEscapedString("ABCdef123&<>\\\"'\t", OS);EXPECT_EQ("ABCdef123&<>\\\\\\22'\\09", OS.str());}TEST(StringExtrasTest, printHTMLEscaped) {std::string str;raw_string_ostream OS(str);printHTMLEscaped("ABCdef123&<>\"'", OS);EXPECT_EQ("ABCdef123&<>"'", OS.str());}TEST(StringExtrasTest, ConvertToSnakeFromCamelCase) {auto testConvertToSnakeCase = [](llvm::StringRef input,llvm::StringRef expected) {EXPECT_EQ(convertToSnakeFromCamelCase(input), expected.str());};testConvertToSnakeCase("OpName", "op_name");testConvertToSnakeCase("opName", "op_name");testConvertToSnakeCase("_OpName", "_op_name");testConvertToSnakeCase("Op_Name", "op_name");testConvertToSnakeCase("", "");testConvertToSnakeCase("A", "a");testConvertToSnakeCase("_", "_");testConvertToSnakeCase("a", "a");testConvertToSnakeCase("op_name", "op_name");testConvertToSnakeCase("_op_name", "_op_name");testConvertToSnakeCase("__op_name", "__op_name");testConvertToSnakeCase("op__name", "op__name");}TEST(StringExtrasTest, ConvertToCamelFromSnakeCase) {auto testConvertToCamelCase = [](bool capitalizeFirst, llvm::StringRef input,llvm::StringRef expected) {EXPECT_EQ(convertToCamelFromSnakeCase(input, capitalizeFirst),expected.str());};testConvertToCamelCase(false, "op_name", "opName");testConvertToCamelCase(false, "_op_name", "_opName");testConvertToCamelCase(false, "__op_name", "_OpName");testConvertToCamelCase(false, "op__name", "op_Name");testConvertToCamelCase(false, "", "");testConvertToCamelCase(false, "A", "A");testConvertToCamelCase(false, "_", "_");testConvertToCamelCase(false, "a", "a");testConvertToCamelCase(false, "OpName", "OpName");testConvertToCamelCase(false, "opName", "opName");testConvertToCamelCase(false, "_OpName", "_OpName");testConvertToCamelCase(false, "Op_Name", "Op_Name");testConvertToCamelCase(true, "op_name", "OpName");testConvertToCamelCase(true, "_op_name", "_opName");testConvertToCamelCase(true, "__op_name", "_OpName");testConvertToCamelCase(true, "op__name", "Op_Name");testConvertToCamelCase(true, "", "");testConvertToCamelCase(true, "A", "A");testConvertToCamelCase(true, "_", "_");testConvertToCamelCase(true, "a", "A");testConvertToCamelCase(true, "OpName", "OpName");testConvertToCamelCase(true, "_OpName", "_OpName");testConvertToCamelCase(true, "Op_Name", "Op_Name");testConvertToCamelCase(true, "opName", "OpName");}constexpr uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max();constexpr int64_t MaxInt64 = std::numeric_limits<int64_t>::max();constexpr int64_t MinInt64 = std::numeric_limits<int64_t>::min();TEST(StringExtrasTest, UToStr) {EXPECT_EQ("0", utostr(0));EXPECT_EQ("0", utostr(0, /*isNeg=*/false));EXPECT_EQ("1", utostr(1));EXPECT_EQ("1", utostr(1, /*isNeg=*/false));EXPECT_EQ(std::to_string(MaxUint64), utostr(MaxUint64));EXPECT_EQ(std::to_string(MaxUint64), utostr(MaxUint64, /*isNeg=*/false));EXPECT_EQ("-0", utostr(0, /*isNeg=*/true));EXPECT_EQ("-1", utostr(1, /*isNeg=*/true));EXPECT_EQ("-" + std::to_string(MaxInt64), utostr(MaxInt64, /*isNeg=*/true));constexpr uint64_t MinusMinInt64 = -static_cast<uint64_t>(MinInt64);EXPECT_EQ("-" + std::to_string(MinusMinInt64),utostr(MinusMinInt64, /*isNeg=*/true));EXPECT_EQ("-" + std::to_string(MaxUint64), utostr(MaxUint64, /*isNeg=*/true));}TEST(StringExtrasTest, IToStr) {EXPECT_EQ("0", itostr(0));EXPECT_EQ("1", itostr(1));EXPECT_EQ("-1", itostr(-1));EXPECT_EQ(std::to_string(MinInt64), itostr(MinInt64));EXPECT_EQ(std::to_string(MaxInt64), itostr(MaxInt64));}TEST(StringExtrasTest, ListSeparator) {ListSeparator LS;StringRef S = LS;EXPECT_EQ(S, "");S = LS;EXPECT_EQ(S, ", ");ListSeparator LS2(" ");S = LS2;EXPECT_EQ(S, "");S = LS2;EXPECT_EQ(S, " ");}TEST(StringExtrasTest, toStringAPInt) {bool isSigned;EXPECT_EQ(toString(APInt(8, 0), 2, true, true), "0b0");EXPECT_EQ(toString(APInt(8, 0), 8, true, true), "00");EXPECT_EQ(toString(APInt(8, 0), 10, true, true), "0");EXPECT_EQ(toString(APInt(8, 0), 16, true, true), "0x0");EXPECT_EQ(toString(APInt(8, 0), 36, true, false), "0");isSigned = false;EXPECT_EQ(toString(APInt(8, 255, isSigned), 2, isSigned, true), "0b11111111");EXPECT_EQ(toString(APInt(8, 255, isSigned), 8, isSigned, true), "0377");EXPECT_EQ(toString(APInt(8, 255, isSigned), 10, isSigned, true), "255");EXPECT_EQ(toString(APInt(8, 255, isSigned), 16, isSigned, true), "0xFF");EXPECT_EQ(toString(APInt(8, 255, isSigned), 36, isSigned, false), "73");isSigned = true;EXPECT_EQ(toString(APInt(8, 255, isSigned), 2, isSigned, true), "-0b1");EXPECT_EQ(toString(APInt(8, 255, isSigned), 8, isSigned, true), "-01");EXPECT_EQ(toString(APInt(8, 255, isSigned), 10, isSigned, true), "-1");EXPECT_EQ(toString(APInt(8, 255, isSigned), 16, isSigned, true), "-0x1");EXPECT_EQ(toString(APInt(8, 255, isSigned), 36, isSigned, false), "-1");}TEST(StringExtrasTest, toStringAPSInt) {bool isUnsigned;EXPECT_EQ(toString(APSInt(APInt(8, 0), false), 2), "0");EXPECT_EQ(toString(APSInt(APInt(8, 0), false), 8), "0");EXPECT_EQ(toString(APSInt(APInt(8, 0), false), 10), "0");EXPECT_EQ(toString(APSInt(APInt(8, 0), false), 16), "0");isUnsigned = true;EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 2), "11111111");EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 8), "377");EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 10), "255");EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 16), "FF");isUnsigned = false;EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 2), "-1");EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 8), "-1");EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 10), "-1");EXPECT_EQ(toString(APSInt(APInt(8, 255), isUnsigned), 16), "-1");}TEST(StringExtrasTest, splitStringRef) {auto Spl = split("foo<=>bar<=><=>baz", "<=>");auto It = Spl.begin();auto End = Spl.end();ASSERT_NE(It, End);EXPECT_EQ(*It, StringRef("foo"));ASSERT_NE(++It, End);EXPECT_EQ(*It, StringRef("bar"));ASSERT_NE(++It, End);EXPECT_EQ(*It, StringRef(""));ASSERT_NE(++It, End);EXPECT_EQ(*It, StringRef("baz"));ASSERT_EQ(++It, End);}TEST(StringExtrasTest, splitStringRefForLoop) {llvm::SmallVector<StringRef, 4> Result;for (StringRef x : split("foo<=>bar<=><=>baz", "<=>"))Result.push_back(x);EXPECT_THAT(Result, testing::ElementsAre("foo", "bar", "", "baz"));}TEST(StringExtrasTest, splitChar) {auto Spl = split("foo,bar,,baz", ',');auto It = Spl.begin();auto End = Spl.end();ASSERT_NE(It, End);EXPECT_EQ(*It, StringRef("foo"));ASSERT_NE(++It, End);EXPECT_EQ(*It, StringRef("bar"));ASSERT_NE(++It, End);EXPECT_EQ(*It, StringRef(""));ASSERT_NE(++It, End);EXPECT_EQ(*It, StringRef("baz"));ASSERT_EQ(++It, End);}TEST(StringExtrasTest, splitCharForLoop) {llvm::SmallVector<StringRef, 4> Result;for (StringRef x : split("foo,bar,,baz", ','))Result.push_back(x);EXPECT_THAT(Result, testing::ElementsAre("foo", "bar", "", "baz"));}
//===- llvm/unittest/ADT/StatisticTest.cpp - Statistic unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Statistic.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"using namespace llvm;using OptionalStatistic = Optional<std::pair<StringRef, uint64_t>>;namespace {#define DEBUG_TYPE "unittest"STATISTIC(Counter, "Counts things");STATISTIC(Counter2, "Counts other things");ALWAYS_ENABLED_STATISTIC(AlwaysCounter, "Counts things always");#if LLVM_ENABLE_STATSstatic voidextractCounters(const std::vector<std::pair<StringRef, uint64_t>> &Range,OptionalStatistic &S1, OptionalStatistic &S2) {for (const auto &S : Range) {if (S.first == "Counter")S1 = S;if (S.first == "Counter2")S2 = S;}}#endifTEST(StatisticTest, Count) {EnableStatistics();Counter = 0;EXPECT_EQ(Counter, 0ull);Counter++;Counter++;Counter += (std::numeric_limits<uint64_t>::max() - 3);#if LLVM_ENABLE_STATSEXPECT_EQ(Counter, std::numeric_limits<uint64_t>::max() - 1);#elseEXPECT_EQ(Counter, UINT64_C(0));#endifAlwaysCounter = 0;EXPECT_EQ(AlwaysCounter, 0ull);AlwaysCounter++;++AlwaysCounter;EXPECT_EQ(AlwaysCounter, 2ull);}TEST(StatisticTest, Assign) {EnableStatistics();Counter = 2;#if LLVM_ENABLE_STATSEXPECT_EQ(Counter, 2u);#elseEXPECT_EQ(Counter, 0u);#endifAlwaysCounter = 2;EXPECT_EQ(AlwaysCounter, 2u);}TEST(StatisticTest, API) {EnableStatistics();// Reset beforehand to make sure previous tests don't effect this one.ResetStatistics();Counter = 0;EXPECT_EQ(Counter, 0u);Counter++;Counter++;#if LLVM_ENABLE_STATSEXPECT_EQ(Counter, 2u);#elseEXPECT_EQ(Counter, 0u);#endif#if LLVM_ENABLE_STATS{const auto Range1 = GetStatistics();EXPECT_NE(Range1.begin(), Range1.end());EXPECT_EQ(Range1.begin() + 1, Range1.end());OptionalStatistic S1;OptionalStatistic S2;extractCounters(Range1, S1, S2);EXPECT_EQ(S1.has_value(), true);EXPECT_EQ(S2.has_value(), false);}// Counter2 will be registered when it's first touched.Counter2++;{const auto Range = GetStatistics();EXPECT_NE(Range.begin(), Range.end());EXPECT_EQ(Range.begin() + 2, Range.end());OptionalStatistic S1;OptionalStatistic S2;extractCounters(Range, S1, S2);EXPECT_EQ(S1.has_value(), true);EXPECT_EQ(S2.has_value(), true);EXPECT_EQ(S1->first, "Counter");EXPECT_EQ(S1->second, 2u);EXPECT_EQ(S2->first, "Counter2");EXPECT_EQ(S2->second, 1u);}#elseCounter2++;auto &Range = GetStatistics();EXPECT_EQ(Range.begin(), Range.end());#endif#if LLVM_ENABLE_STATS// Check that resetting the statistics works correctly.// It should empty the list and zero the counters.ResetStatistics();{auto &Range = GetStatistics();EXPECT_EQ(Range.begin(), Range.end());EXPECT_EQ(Counter, 0u);EXPECT_EQ(Counter2, 0u);OptionalStatistic S1;OptionalStatistic S2;extractCounters(Range, S1, S2);EXPECT_EQ(S1.has_value(), false);EXPECT_EQ(S2.has_value(), false);}// Now check that they successfully re-register and count.Counter++;Counter2++;{auto &Range = GetStatistics();EXPECT_EQ(Range.begin() + 2, Range.end());EXPECT_EQ(Counter, 1u);EXPECT_EQ(Counter2, 1u);OptionalStatistic S1;OptionalStatistic S2;extractCounters(Range, S1, S2);EXPECT_EQ(S1.has_value(), true);EXPECT_EQ(S2.has_value(), true);EXPECT_EQ(S1->first, "Counter");EXPECT_EQ(S1->second, 1u);EXPECT_EQ(S2->first, "Counter2");EXPECT_EQ(S2->second, 1u);}#else// No need to test the output ResetStatistics(), there's nothing to reset so// we can't tell if it failed anyway.ResetStatistics();#endif}} // end anonymous namespace
//===------ ADT/SparseSetTest.cpp - SparseSet unit tests - -----*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SparseSet.h"#include "gtest/gtest.h"using namespace llvm;namespace {typedef SparseSet<unsigned> USet;// Empty set tests.TEST(SparseSetTest, EmptySet) {USet Set;EXPECT_TRUE(Set.empty());EXPECT_TRUE(Set.begin() == Set.end());EXPECT_EQ(0u, Set.size());Set.setUniverse(10);// Lookups on empty set.EXPECT_FALSE(Set.contains(0));EXPECT_FALSE(Set.contains(9));// Same thing on a const reference.const USet &CSet = Set;EXPECT_TRUE(CSet.empty());EXPECT_TRUE(CSet.begin() == CSet.end());EXPECT_EQ(0u, CSet.size());EXPECT_FALSE(CSet.contains(0));USet::const_iterator I = CSet.find(5);EXPECT_TRUE(I == CSet.end());}// Single entry set tests.TEST(SparseSetTest, SingleEntrySet) {USet Set;Set.setUniverse(10);std::pair<USet::iterator, bool> IP = Set.insert(5);EXPECT_TRUE(IP.second);EXPECT_TRUE(IP.first == Set.begin());EXPECT_FALSE(Set.empty());EXPECT_FALSE(Set.begin() == Set.end());EXPECT_TRUE(Set.begin() + 1 == Set.end());EXPECT_EQ(1u, Set.size());EXPECT_FALSE(Set.contains(0));EXPECT_FALSE(Set.contains(9));EXPECT_TRUE(Set.contains(5));EXPECT_FALSE(Set.count(0));EXPECT_TRUE(Set.count(5));// Redundant insert.IP = Set.insert(5);EXPECT_FALSE(IP.second);EXPECT_TRUE(IP.first == Set.begin());// Erase non-existent element.EXPECT_FALSE(Set.erase(1));EXPECT_EQ(1u, Set.size());EXPECT_EQ(5u, *Set.begin());// Erase iterator.USet::iterator I = Set.find(5);EXPECT_TRUE(I == Set.begin());I = Set.erase(I);EXPECT_FALSE(Set.contains(5));EXPECT_TRUE(I == Set.end());EXPECT_TRUE(Set.empty());}// Multiple entry set tests.TEST(SparseSetTest, MultipleEntrySet) {USet Set;Set.setUniverse(10);Set.insert(5);Set.insert(3);Set.insert(2);Set.insert(1);Set.insert(4);EXPECT_EQ(5u, Set.size());// Without deletions, iteration order == insertion order.USet::const_iterator I = Set.begin();EXPECT_EQ(5u, *I);++I;EXPECT_EQ(3u, *I);++I;EXPECT_EQ(2u, *I);++I;EXPECT_EQ(1u, *I);++I;EXPECT_EQ(4u, *I);++I;EXPECT_TRUE(I == Set.end());// Redundant insert.std::pair<USet::iterator, bool> IP = Set.insert(3);EXPECT_FALSE(IP.second);EXPECT_TRUE(IP.first == Set.begin() + 1);// Erase last element by key.EXPECT_TRUE(Set.erase(4));EXPECT_EQ(4u, Set.size());EXPECT_FALSE(Set.count(4));EXPECT_FALSE(Set.erase(4));EXPECT_EQ(4u, Set.size());EXPECT_FALSE(Set.count(4));// Erase first element by key.EXPECT_TRUE(Set.count(5));EXPECT_TRUE(Set.find(5) == Set.begin());EXPECT_TRUE(Set.erase(5));EXPECT_EQ(3u, Set.size());EXPECT_FALSE(Set.count(5));EXPECT_FALSE(Set.erase(5));EXPECT_EQ(3u, Set.size());EXPECT_FALSE(Set.count(5));Set.insert(6);Set.insert(7);EXPECT_EQ(5u, Set.size());// Erase last element by iterator.I = Set.erase(Set.end() - 1);EXPECT_TRUE(I == Set.end());EXPECT_EQ(4u, Set.size());// Erase second element by iterator.I = Set.erase(Set.begin() + 1);EXPECT_TRUE(I == Set.begin() + 1);// Clear and resize the universe.Set.clear();EXPECT_FALSE(Set.count(5));Set.setUniverse(1000);// Add more than 256 elements.for (unsigned i = 100; i != 800; ++i)Set.insert(i);for (unsigned i = 0; i != 10; ++i)Set.erase(i);for (unsigned i = 100; i != 800; ++i)EXPECT_TRUE(Set.count(i));EXPECT_FALSE(Set.count(99));EXPECT_FALSE(Set.count(800));EXPECT_EQ(700u, Set.size());}struct Alt {unsigned Value;explicit Alt(unsigned x) : Value(x) {}unsigned getSparseSetIndex() const { return Value - 1000; }};TEST(SparseSetTest, AltStructSet) {typedef SparseSet<Alt> ASet;ASet Set;Set.setUniverse(10);Set.insert(Alt(1005));ASet::iterator I = Set.find(5);ASSERT_TRUE(I == Set.begin());EXPECT_EQ(1005u, I->Value);Set.insert(Alt(1006));Set.insert(Alt(1006));I = Set.erase(Set.begin());ASSERT_TRUE(I == Set.begin());EXPECT_EQ(1006u, I->Value);EXPECT_FALSE(Set.erase(5));EXPECT_TRUE(Set.erase(6));}TEST(SparseSetTest, PopBack) {USet Set;const unsigned UpperBound = 300;Set.setUniverse(UpperBound);for (unsigned i = 0; i < UpperBound; ++i)Set.insert(i);// Make sure pop back returns the values in the reverse order we// inserted them.unsigned Expected = UpperBound;while (!Set.empty())ASSERT_TRUE(--Expected == Set.pop_back_val());// Insert again the same elements in the sparse set and make sure// each insertion actually inserts the elements. I.e., check// that the underlying data structure are properly cleared.for (unsigned i = 0; i < UpperBound; ++i)ASSERT_TRUE(Set.insert(i).second);}} // namespace
//===------ ADT/SparseSetTest.cpp - SparseSet unit tests - -----*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SparseMultiSet.h"#include "gtest/gtest.h"using namespace llvm;namespace {typedef SparseMultiSet<unsigned> USet;// Empty set tests.TEST(SparseMultiSetTest, EmptySet) {USet Set;EXPECT_TRUE(Set.empty());EXPECT_EQ(0u, Set.size());Set.setUniverse(10);// Lookups on empty set.EXPECT_TRUE(Set.find(0) == Set.end());EXPECT_TRUE(Set.find(9) == Set.end());// Same thing on a const reference.const USet &CSet = Set;EXPECT_TRUE(CSet.empty());EXPECT_EQ(0u, CSet.size());EXPECT_TRUE(CSet.find(0) == CSet.end());USet::const_iterator I = CSet.find(5);EXPECT_TRUE(I == CSet.end());}// Single entry set tests.TEST(SparseMultiSetTest, SingleEntrySet) {USet Set;Set.setUniverse(10);USet::iterator I = Set.insert(5);EXPECT_TRUE(I != Set.end());EXPECT_TRUE(*I == 5);EXPECT_FALSE(Set.empty());EXPECT_EQ(1u, Set.size());EXPECT_TRUE(Set.find(0) == Set.end());EXPECT_TRUE(Set.find(9) == Set.end());EXPECT_FALSE(Set.contains(0));EXPECT_TRUE(Set.contains(5));// Extra insert.I = Set.insert(5);EXPECT_TRUE(I != Set.end());EXPECT_TRUE(I == ++Set.find(5));I--;EXPECT_TRUE(I == Set.find(5));// Erase non-existent element.I = Set.find(1);EXPECT_TRUE(I == Set.end());EXPECT_EQ(2u, Set.size());EXPECT_EQ(5u, *Set.find(5));// Erase iterator.I = Set.find(5);EXPECT_TRUE(I != Set.end());I = Set.erase(I);EXPECT_TRUE(I != Set.end());I = Set.erase(I);EXPECT_TRUE(I == Set.end());EXPECT_TRUE(Set.empty());}// Multiple entry set tests.TEST(SparseMultiSetTest, MultipleEntrySet) {USet Set;Set.setUniverse(10);Set.insert(5);Set.insert(5);Set.insert(5);Set.insert(3);Set.insert(2);Set.insert(1);Set.insert(4);EXPECT_EQ(7u, Set.size());// Erase last element by key.EXPECT_TRUE(Set.erase(Set.find(4)) == Set.end());EXPECT_EQ(6u, Set.size());EXPECT_FALSE(Set.contains(4));EXPECT_TRUE(Set.find(4) == Set.end());// Erase first element by key.EXPECT_EQ(3u, Set.count(5));EXPECT_TRUE(Set.find(5) != Set.end());EXPECT_TRUE(Set.erase(Set.find(5)) != Set.end());EXPECT_EQ(5u, Set.size());EXPECT_EQ(2u, Set.count(5));Set.insert(6);Set.insert(7);EXPECT_EQ(7u, Set.size());// Erase tail by iterator.EXPECT_TRUE(Set.getTail(6) == Set.getHead(6));USet::iterator I = Set.erase(Set.find(6));EXPECT_TRUE(I == Set.end());EXPECT_EQ(6u, Set.size());// Erase tails by iterator.EXPECT_EQ(2u, Set.count(5));I = Set.getTail(5);I = Set.erase(I);EXPECT_TRUE(I == Set.end());--I;EXPECT_EQ(1u, Set.count(5));EXPECT_EQ(5u, *I);I = Set.erase(I);EXPECT_TRUE(I == Set.end());EXPECT_EQ(0u, Set.count(5));Set.insert(8);Set.insert(8);Set.insert(8);Set.insert(8);Set.insert(8);// Erase all the 8sEXPECT_EQ(5, std::distance(Set.getHead(8), Set.end()));Set.eraseAll(8);EXPECT_EQ(0, std::distance(Set.getHead(8), Set.end()));// Clear and resize the universe.Set.clear();EXPECT_EQ(0u, Set.size());EXPECT_FALSE(Set.contains(3));Set.setUniverse(1000);// Add more than 256 elements.for (unsigned i = 100; i != 800; ++i)Set.insert(i);for (unsigned i = 0; i != 10; ++i)Set.eraseAll(i);for (unsigned i = 100; i != 800; ++i)EXPECT_EQ(1u, Set.count(i));EXPECT_FALSE(Set.contains(99));EXPECT_FALSE(Set.contains(800));EXPECT_EQ(700u, Set.size());}// Test out iteratorsTEST(SparseMultiSetTest, Iterators) {USet Set;Set.setUniverse(100);Set.insert(0);Set.insert(1);Set.insert(2);Set.insert(0);Set.insert(1);Set.insert(0);USet::RangePair RangePair = Set.equal_range(0);USet::iterator B = RangePair.first;USet::iterator E = RangePair.second;// Move the iterators around, going to end and coming back.EXPECT_EQ(3, std::distance(B, E));EXPECT_EQ(B, --(--(--E)));EXPECT_EQ(++(++(++E)), Set.end());EXPECT_EQ(B, --(--(--E)));EXPECT_EQ(++(++(++E)), Set.end());// Insert into the tail, and move around againSet.insert(0);EXPECT_EQ(B, --(--(--(--E))));EXPECT_EQ(++(++(++(++E))), Set.end());EXPECT_EQ(B, --(--(--(--E))));EXPECT_EQ(++(++(++(++E))), Set.end());// Erase a tail, and move around againUSet::iterator Erased = Set.erase(Set.getTail(0));EXPECT_EQ(Erased, E);EXPECT_EQ(B, --(--(--E)));USet Set2;Set2.setUniverse(11);Set2.insert(3);EXPECT_TRUE(!Set2.contains(0));EXPECT_TRUE(!Set.contains(3));EXPECT_EQ(Set2.getHead(3), Set2.getTail(3));EXPECT_EQ(Set2.getHead(0), Set2.getTail(0));B = Set2.find(3);EXPECT_EQ(Set2.find(3), --(++B));}struct Alt {unsigned Value;explicit Alt(unsigned x) : Value(x) {}unsigned getSparseSetIndex() const { return Value - 1000; }};TEST(SparseMultiSetTest, AltStructSet) {typedef SparseMultiSet<Alt> ASet;ASet Set;Set.setUniverse(10);Set.insert(Alt(1005));ASet::iterator I = Set.find(5);ASSERT_TRUE(I != Set.end());EXPECT_EQ(1005u, I->Value);Set.insert(Alt(1006));Set.insert(Alt(1006));I = Set.erase(Set.find(6));ASSERT_TRUE(I != Set.end());EXPECT_EQ(1006u, I->Value);I = Set.erase(Set.find(6));ASSERT_TRUE(I == Set.end());EXPECT_TRUE(Set.contains(5));EXPECT_FALSE(Set.contains(6));}} // namespace
//===- llvm/unittest/ADT/SparseBitVectorTest.cpp - SparseBitVector tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SparseBitVector.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(SparseBitVectorTest, TrivialOperation) {SparseBitVector<> Vec;EXPECT_EQ(0U, Vec.count());EXPECT_FALSE(Vec.test(17));Vec.set(5);EXPECT_TRUE(Vec.test(5));EXPECT_FALSE(Vec.test(17));Vec.reset(6);EXPECT_TRUE(Vec.test(5));EXPECT_FALSE(Vec.test(6));Vec.reset(5);EXPECT_FALSE(Vec.test(5));EXPECT_TRUE(Vec.test_and_set(17));EXPECT_FALSE(Vec.test_and_set(17));EXPECT_TRUE(Vec.test(17));Vec.clear();EXPECT_FALSE(Vec.test(17));Vec.set(5);const SparseBitVector<> ConstVec = Vec;EXPECT_TRUE(ConstVec.test(5));EXPECT_FALSE(ConstVec.test(17));Vec.set(1337);EXPECT_TRUE(Vec.test(1337));Vec = ConstVec;EXPECT_FALSE(Vec.test(1337));Vec.set(1337);EXPECT_FALSE(Vec.empty());SparseBitVector<> MovedVec(std::move(Vec));EXPECT_TRUE(Vec.empty());EXPECT_TRUE(MovedVec.test(5));EXPECT_TRUE(MovedVec.test(1337));Vec = std::move(MovedVec);EXPECT_TRUE(MovedVec.empty());EXPECT_FALSE(Vec.empty());}TEST(SparseBitVectorTest, IntersectWith) {SparseBitVector<> Vec, Other;Vec.set(1);Other.set(1);EXPECT_FALSE(Vec &= Other);EXPECT_TRUE(Vec.test(1));Vec.clear();Vec.set(5);Other.clear();Other.set(6);EXPECT_TRUE(Vec &= Other);EXPECT_TRUE(Vec.empty());Vec.clear();Vec.set(5);Other.clear();Other.set(225);EXPECT_TRUE(Vec &= Other);EXPECT_TRUE(Vec.empty());Vec.clear();Vec.set(225);Other.clear();Other.set(5);EXPECT_TRUE(Vec &= Other);EXPECT_TRUE(Vec.empty());}TEST(SparseBitVectorTest, SelfAssignment) {SparseBitVector<> Vec, Other;Vec.set(23);Vec.set(234);Vec = static_cast<SparseBitVector<> &>(Vec);EXPECT_TRUE(Vec.test(23));EXPECT_TRUE(Vec.test(234));Vec.clear();Vec.set(17);Vec.set(256);EXPECT_FALSE(Vec |= Vec);EXPECT_TRUE(Vec.test(17));EXPECT_TRUE(Vec.test(256));Vec.clear();Vec.set(56);Vec.set(517);EXPECT_FALSE(Vec &= Vec);EXPECT_TRUE(Vec.test(56));EXPECT_TRUE(Vec.test(517));Vec.clear();Vec.set(99);Vec.set(333);EXPECT_TRUE(Vec.intersectWithComplement(Vec));EXPECT_TRUE(Vec.empty());EXPECT_FALSE(Vec.intersectWithComplement(Vec));Vec.clear();Vec.set(28);Vec.set(43);Vec.intersectWithComplement(Vec, Vec);EXPECT_TRUE(Vec.empty());Vec.clear();Vec.set(42);Vec.set(567);Other.set(55);Other.set(567);Vec.intersectWithComplement(Vec, Other);EXPECT_TRUE(Vec.test(42));EXPECT_FALSE(Vec.test(567));Vec.clear();Vec.set(19);Vec.set(21);Other.clear();Other.set(19);Other.set(31);Vec.intersectWithComplement(Other, Vec);EXPECT_FALSE(Vec.test(19));EXPECT_TRUE(Vec.test(31));Vec.clear();Vec.set(1);Other.clear();Other.set(59);Other.set(75);Vec.intersectWithComplement(Other, Other);EXPECT_TRUE(Vec.empty());}TEST(SparseBitVectorTest, Find) {SparseBitVector<> Vec;Vec.set(1);EXPECT_EQ(1, Vec.find_first());EXPECT_EQ(1, Vec.find_last());Vec.set(2);EXPECT_EQ(1, Vec.find_first());EXPECT_EQ(2, Vec.find_last());Vec.set(0);Vec.set(3);EXPECT_EQ(0, Vec.find_first());EXPECT_EQ(3, Vec.find_last());Vec.reset(1);Vec.reset(0);Vec.reset(3);EXPECT_EQ(2, Vec.find_first());EXPECT_EQ(2, Vec.find_last());// Set some large bits to ensure we are pulling bits from more than just a// single bitword.Vec.set(500);Vec.set(2000);Vec.set(3000);Vec.set(4000);Vec.reset(2);EXPECT_EQ(500, Vec.find_first());EXPECT_EQ(4000, Vec.find_last());Vec.reset(500);Vec.reset(3000);Vec.reset(4000);EXPECT_EQ(2000, Vec.find_first());EXPECT_EQ(2000, Vec.find_last());Vec.clear();}}
//===- llvm/unittest/ADT/SmallVectorTest.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===////// SmallVector unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/Support/Compiler.h"#include "gtest/gtest.h"#include <list>#include <stdarg.h>using namespace llvm;namespace {/// A helper class that counts the total number of constructor and/// destructor calls.class Constructable {private:static int numConstructorCalls;static int numMoveConstructorCalls;static int numCopyConstructorCalls;static int numDestructorCalls;static int numAssignmentCalls;static int numMoveAssignmentCalls;static int numCopyAssignmentCalls;bool constructed;int value;public:Constructable() : constructed(true), value(0) {++numConstructorCalls;}Constructable(int val) : constructed(true), value(val) {++numConstructorCalls;}Constructable(const Constructable & src) : constructed(true) {value = src.value;++numConstructorCalls;++numCopyConstructorCalls;}Constructable(Constructable && src) : constructed(true) {value = src.value;src.value = 0;++numConstructorCalls;++numMoveConstructorCalls;}~Constructable() {EXPECT_TRUE(constructed);++numDestructorCalls;constructed = false;}Constructable & operator=(const Constructable & src) {EXPECT_TRUE(constructed);value = src.value;++numAssignmentCalls;++numCopyAssignmentCalls;return *this;}Constructable & operator=(Constructable && src) {EXPECT_TRUE(constructed);value = src.value;src.value = 0;++numAssignmentCalls;++numMoveAssignmentCalls;return *this;}int getValue() const {return abs(value);}static void reset() {numConstructorCalls = 0;numMoveConstructorCalls = 0;numCopyConstructorCalls = 0;numDestructorCalls = 0;numAssignmentCalls = 0;numMoveAssignmentCalls = 0;numCopyAssignmentCalls = 0;}static int getNumConstructorCalls() {return numConstructorCalls;}static int getNumMoveConstructorCalls() {return numMoveConstructorCalls;}static int getNumCopyConstructorCalls() {return numCopyConstructorCalls;}static int getNumDestructorCalls() {return numDestructorCalls;}static int getNumAssignmentCalls() {return numAssignmentCalls;}static int getNumMoveAssignmentCalls() {return numMoveAssignmentCalls;}static int getNumCopyAssignmentCalls() {return numCopyAssignmentCalls;}friend bool operator==(const Constructable &c0, const Constructable &c1) {return c0.getValue() == c1.getValue();}friend bool LLVM_ATTRIBUTE_UNUSED operator!=(const Constructable &c0,const Constructable &c1) {return c0.getValue() != c1.getValue();}friend bool operator<(const Constructable &c0, const Constructable &c1) {return c0.getValue() < c1.getValue();}friend bool LLVM_ATTRIBUTE_UNUSED operator<=(const Constructable &c0,const Constructable &c1) {return c0.getValue() <= c1.getValue();}friend bool LLVM_ATTRIBUTE_UNUSED operator>(const Constructable &c0,const Constructable &c1) {return c0.getValue() > c1.getValue();}friend bool LLVM_ATTRIBUTE_UNUSED operator>=(const Constructable &c0,const Constructable &c1) {return c0.getValue() >= c1.getValue();}};int Constructable::numConstructorCalls;int Constructable::numCopyConstructorCalls;int Constructable::numMoveConstructorCalls;int Constructable::numDestructorCalls;int Constructable::numAssignmentCalls;int Constructable::numCopyAssignmentCalls;int Constructable::numMoveAssignmentCalls;struct NonCopyable {NonCopyable() {}NonCopyable(NonCopyable &&) {}NonCopyable &operator=(NonCopyable &&) { return *this; }private:NonCopyable(const NonCopyable &) = delete;NonCopyable &operator=(const NonCopyable &) = delete;};LLVM_ATTRIBUTE_USED void CompileTest() {SmallVector<NonCopyable, 0> V;V.resize(42);}class SmallVectorTestBase : public testing::Test {protected:void SetUp() override { Constructable::reset(); }template <typename VectorT>void assertEmpty(VectorT & v) {// Size testsEXPECT_EQ(0u, v.size());EXPECT_TRUE(v.empty());// Iterator testsEXPECT_TRUE(v.begin() == v.end());}// Assert that v contains the specified values, in order.template <typename VectorT>void assertValuesInOrder(VectorT & v, size_t size, ...) {EXPECT_EQ(size, v.size());va_list ap;va_start(ap, size);for (size_t i = 0; i < size; ++i) {int value = va_arg(ap, int);EXPECT_EQ(value, v[i].getValue());}va_end(ap);}// Generate a sequence of values to initialize the vector.template <typename VectorT>void makeSequence(VectorT & v, int start, int end) {for (int i = start; i <= end; ++i) {v.push_back(Constructable(i));}}};// Test fixture classtemplate <typename VectorT>class SmallVectorTest : public SmallVectorTestBase {protected:VectorT theVector;VectorT otherVector;};typedef ::testing::Types<SmallVector<Constructable, 0>,SmallVector<Constructable, 1>,SmallVector<Constructable, 2>,SmallVector<Constructable, 4>,SmallVector<Constructable, 5>> SmallVectorTestTypes;TYPED_TEST_SUITE(SmallVectorTest, SmallVectorTestTypes, );// Constructor test.TYPED_TEST(SmallVectorTest, ConstructorNonIterTest) {SCOPED_TRACE("ConstructorTest");this->theVector = SmallVector<Constructable, 2>(2, 2);this->assertValuesInOrder(this->theVector, 2u, 2, 2);}// Constructor test.TYPED_TEST(SmallVectorTest, ConstructorIterTest) {SCOPED_TRACE("ConstructorTest");int arr[] = {1, 2, 3};this->theVector =SmallVector<Constructable, 4>(std::begin(arr), std::end(arr));this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);}// New vector test.TYPED_TEST(SmallVectorTest, EmptyVectorTest) {SCOPED_TRACE("EmptyVectorTest");this->assertEmpty(this->theVector);EXPECT_TRUE(this->theVector.rbegin() == this->theVector.rend());EXPECT_EQ(0, Constructable::getNumConstructorCalls());EXPECT_EQ(0, Constructable::getNumDestructorCalls());}// Simple insertions and deletions.TYPED_TEST(SmallVectorTest, PushPopTest) {SCOPED_TRACE("PushPopTest");// Track whether the vector will potentially have to grow.bool RequiresGrowth = this->theVector.capacity() < 3;// Push an elementthis->theVector.push_back(Constructable(1));// Size teststhis->assertValuesInOrder(this->theVector, 1u, 1);EXPECT_FALSE(this->theVector.begin() == this->theVector.end());EXPECT_FALSE(this->theVector.empty());// Push another elementthis->theVector.push_back(Constructable(2));this->assertValuesInOrder(this->theVector, 2u, 1, 2);// Insert at beginning. Reserve space to avoid reference invalidation from// this->theVector[1].this->theVector.reserve(this->theVector.size() + 1);this->theVector.insert(this->theVector.begin(), this->theVector[1]);this->assertValuesInOrder(this->theVector, 3u, 2, 1, 2);// Pop one elementthis->theVector.pop_back();this->assertValuesInOrder(this->theVector, 2u, 2, 1);// Pop remaining elementsthis->theVector.pop_back_n(2);this->assertEmpty(this->theVector);// Check number of constructor calls. Should be 2 for each list element,// one for the argument to push_back, one for the argument to insert,// and one for the list element itself.if (!RequiresGrowth) {EXPECT_EQ(5, Constructable::getNumConstructorCalls());EXPECT_EQ(5, Constructable::getNumDestructorCalls());} else {// If we had to grow the vector, these only have a lower bound, but should// always be equal.EXPECT_LE(5, Constructable::getNumConstructorCalls());EXPECT_EQ(Constructable::getNumConstructorCalls(),Constructable::getNumDestructorCalls());}}// Clear test.TYPED_TEST(SmallVectorTest, ClearTest) {SCOPED_TRACE("ClearTest");this->theVector.reserve(2);this->makeSequence(this->theVector, 1, 2);this->theVector.clear();this->assertEmpty(this->theVector);EXPECT_EQ(4, Constructable::getNumConstructorCalls());EXPECT_EQ(4, Constructable::getNumDestructorCalls());}// Resize smaller test.TYPED_TEST(SmallVectorTest, ResizeShrinkTest) {SCOPED_TRACE("ResizeShrinkTest");this->theVector.reserve(3);this->makeSequence(this->theVector, 1, 3);this->theVector.resize(1);this->assertValuesInOrder(this->theVector, 1u, 1);EXPECT_EQ(6, Constructable::getNumConstructorCalls());EXPECT_EQ(5, Constructable::getNumDestructorCalls());}// Truncate test.TYPED_TEST(SmallVectorTest, TruncateTest) {SCOPED_TRACE("TruncateTest");this->theVector.reserve(3);this->makeSequence(this->theVector, 1, 3);this->theVector.truncate(1);this->assertValuesInOrder(this->theVector, 1u, 1);EXPECT_EQ(6, Constructable::getNumConstructorCalls());EXPECT_EQ(5, Constructable::getNumDestructorCalls());#if !defined(NDEBUG) && GTEST_HAS_DEATH_TESTEXPECT_DEATH(this->theVector.truncate(2), "Cannot increase size");#endifthis->theVector.truncate(1);this->assertValuesInOrder(this->theVector, 1u, 1);EXPECT_EQ(6, Constructable::getNumConstructorCalls());EXPECT_EQ(5, Constructable::getNumDestructorCalls());this->theVector.truncate(0);this->assertEmpty(this->theVector);EXPECT_EQ(6, Constructable::getNumConstructorCalls());EXPECT_EQ(6, Constructable::getNumDestructorCalls());}// Resize bigger test.TYPED_TEST(SmallVectorTest, ResizeGrowTest) {SCOPED_TRACE("ResizeGrowTest");this->theVector.resize(2);EXPECT_EQ(2, Constructable::getNumConstructorCalls());EXPECT_EQ(0, Constructable::getNumDestructorCalls());EXPECT_EQ(2u, this->theVector.size());}TYPED_TEST(SmallVectorTest, ResizeWithElementsTest) {this->theVector.resize(2);Constructable::reset();this->theVector.resize(4);size_t Ctors = Constructable::getNumConstructorCalls();EXPECT_TRUE(Ctors == 2 || Ctors == 4);size_t MoveCtors = Constructable::getNumMoveConstructorCalls();EXPECT_TRUE(MoveCtors == 0 || MoveCtors == 2);size_t Dtors = Constructable::getNumDestructorCalls();EXPECT_TRUE(Dtors == 0 || Dtors == 2);}// Resize with fill value.TYPED_TEST(SmallVectorTest, ResizeFillTest) {SCOPED_TRACE("ResizeFillTest");this->theVector.resize(3, Constructable(77));this->assertValuesInOrder(this->theVector, 3u, 77, 77, 77);}TEST(SmallVectorTest, ResizeForOverwrite) {{// Heap allocated storage.SmallVector<unsigned, 0> V;V.push_back(5U);V.pop_back();V.resize_for_overwrite(V.size() + 1U);EXPECT_EQ(5U, V.back());V.pop_back();V.resize(V.size() + 1);EXPECT_EQ(0U, V.back());}{// Inline storage.SmallVector<unsigned, 2> V;V.push_back(5U);V.pop_back();V.resize_for_overwrite(V.size() + 1U);EXPECT_EQ(5U, V.back());V.pop_back();V.resize(V.size() + 1);EXPECT_EQ(0U, V.back());}}// Overflow past fixed size.TYPED_TEST(SmallVectorTest, OverflowTest) {SCOPED_TRACE("OverflowTest");// Push more elements than the fixed size.this->makeSequence(this->theVector, 1, 10);// Test size and values.EXPECT_EQ(10u, this->theVector.size());for (int i = 0; i < 10; ++i) {EXPECT_EQ(i+1, this->theVector[i].getValue());}// Now resize back to fixed size.this->theVector.resize(1);this->assertValuesInOrder(this->theVector, 1u, 1);}// Iteration tests.TYPED_TEST(SmallVectorTest, IterationTest) {this->makeSequence(this->theVector, 1, 2);// Forward Iterationtypename TypeParam::iterator it = this->theVector.begin();EXPECT_TRUE(*it == this->theVector.front());EXPECT_TRUE(*it == this->theVector[0]);EXPECT_EQ(1, it->getValue());++it;EXPECT_TRUE(*it == this->theVector[1]);EXPECT_TRUE(*it == this->theVector.back());EXPECT_EQ(2, it->getValue());++it;EXPECT_TRUE(it == this->theVector.end());--it;EXPECT_TRUE(*it == this->theVector[1]);EXPECT_EQ(2, it->getValue());--it;EXPECT_TRUE(*it == this->theVector[0]);EXPECT_EQ(1, it->getValue());// Reverse Iterationtypename TypeParam::reverse_iterator rit = this->theVector.rbegin();EXPECT_TRUE(*rit == this->theVector[1]);EXPECT_EQ(2, rit->getValue());++rit;EXPECT_TRUE(*rit == this->theVector[0]);EXPECT_EQ(1, rit->getValue());++rit;EXPECT_TRUE(rit == this->theVector.rend());--rit;EXPECT_TRUE(*rit == this->theVector[0]);EXPECT_EQ(1, rit->getValue());--rit;EXPECT_TRUE(*rit == this->theVector[1]);EXPECT_EQ(2, rit->getValue());}// Swap test.TYPED_TEST(SmallVectorTest, SwapTest) {SCOPED_TRACE("SwapTest");this->makeSequence(this->theVector, 1, 2);std::swap(this->theVector, this->otherVector);this->assertEmpty(this->theVector);this->assertValuesInOrder(this->otherVector, 2u, 1, 2);}// Append testTYPED_TEST(SmallVectorTest, AppendTest) {SCOPED_TRACE("AppendTest");this->makeSequence(this->otherVector, 2, 3);this->theVector.push_back(Constructable(1));this->theVector.append(this->otherVector.begin(), this->otherVector.end());this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);}// Append repeated testTYPED_TEST(SmallVectorTest, AppendRepeatedTest) {SCOPED_TRACE("AppendRepeatedTest");this->theVector.push_back(Constructable(1));this->theVector.append(2, Constructable(77));this->assertValuesInOrder(this->theVector, 3u, 1, 77, 77);}// Append testTYPED_TEST(SmallVectorTest, AppendNonIterTest) {SCOPED_TRACE("AppendRepeatedTest");this->theVector.push_back(Constructable(1));this->theVector.append(2, 7);this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);}struct output_iterator {typedef std::output_iterator_tag iterator_category;typedef int value_type;typedef int difference_type;typedef value_type *pointer;typedef value_type &reference;operator int() { return 2; }operator Constructable() { return 7; }};TYPED_TEST(SmallVectorTest, AppendRepeatedNonForwardIterator) {SCOPED_TRACE("AppendRepeatedTest");this->theVector.push_back(Constructable(1));this->theVector.append(output_iterator(), output_iterator());this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);}TYPED_TEST(SmallVectorTest, AppendSmallVector) {SCOPED_TRACE("AppendSmallVector");SmallVector<Constructable, 3> otherVector = {7, 7};this->theVector.push_back(Constructable(1));this->theVector.append(otherVector);this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);}// Assign testTYPED_TEST(SmallVectorTest, AssignTest) {SCOPED_TRACE("AssignTest");this->theVector.push_back(Constructable(1));this->theVector.assign(2, Constructable(77));this->assertValuesInOrder(this->theVector, 2u, 77, 77);}// Assign testTYPED_TEST(SmallVectorTest, AssignRangeTest) {SCOPED_TRACE("AssignTest");this->theVector.push_back(Constructable(1));int arr[] = {1, 2, 3};this->theVector.assign(std::begin(arr), std::end(arr));this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);}// Assign testTYPED_TEST(SmallVectorTest, AssignNonIterTest) {SCOPED_TRACE("AssignTest");this->theVector.push_back(Constructable(1));this->theVector.assign(2, 7);this->assertValuesInOrder(this->theVector, 2u, 7, 7);}TYPED_TEST(SmallVectorTest, AssignSmallVector) {SCOPED_TRACE("AssignSmallVector");SmallVector<Constructable, 3> otherVector = {7, 7};this->theVector.push_back(Constructable(1));this->theVector.assign(otherVector);this->assertValuesInOrder(this->theVector, 2u, 7, 7);}// Move-assign testTYPED_TEST(SmallVectorTest, MoveAssignTest) {SCOPED_TRACE("MoveAssignTest");// Set up our vector with a single element, but enough capacity for 4.this->theVector.reserve(4);this->theVector.push_back(Constructable(1));// Set up the other vector with 2 elements.this->otherVector.push_back(Constructable(2));this->otherVector.push_back(Constructable(3));// Move-assign from the other vector.this->theVector = std::move(this->otherVector);// Make sure we have the right result.this->assertValuesInOrder(this->theVector, 2u, 2, 3);// Make sure the # of constructor/destructor calls line up. There// are two live objects after clearing the other vector.this->otherVector.clear();EXPECT_EQ(Constructable::getNumConstructorCalls()-2,Constructable::getNumDestructorCalls());// There shouldn't be any live objects any more.this->theVector.clear();EXPECT_EQ(Constructable::getNumConstructorCalls(),Constructable::getNumDestructorCalls());}// Erase a single elementTYPED_TEST(SmallVectorTest, EraseTest) {SCOPED_TRACE("EraseTest");this->makeSequence(this->theVector, 1, 3);const auto &theConstVector = this->theVector;this->theVector.erase(theConstVector.begin());this->assertValuesInOrder(this->theVector, 2u, 2, 3);}// Erase a range of elementsTYPED_TEST(SmallVectorTest, EraseRangeTest) {SCOPED_TRACE("EraseRangeTest");this->makeSequence(this->theVector, 1, 3);const auto &theConstVector = this->theVector;this->theVector.erase(theConstVector.begin(), theConstVector.begin() + 2);this->assertValuesInOrder(this->theVector, 1u, 3);}// Insert a single element.TYPED_TEST(SmallVectorTest, InsertTest) {SCOPED_TRACE("InsertTest");this->makeSequence(this->theVector, 1, 3);typename TypeParam::iterator I =this->theVector.insert(this->theVector.begin() + 1, Constructable(77));EXPECT_EQ(this->theVector.begin() + 1, I);this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3);}// Insert a copy of a single element.TYPED_TEST(SmallVectorTest, InsertCopy) {SCOPED_TRACE("InsertTest");this->makeSequence(this->theVector, 1, 3);Constructable C(77);typename TypeParam::iterator I =this->theVector.insert(this->theVector.begin() + 1, C);EXPECT_EQ(this->theVector.begin() + 1, I);this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3);}// Insert repeated elements.TYPED_TEST(SmallVectorTest, InsertRepeatedTest) {SCOPED_TRACE("InsertRepeatedTest");this->makeSequence(this->theVector, 1, 4);Constructable::reset();auto I =this->theVector.insert(this->theVector.begin() + 1, 2, Constructable(16));// Move construct the top element into newly allocated space, and optionally// reallocate the whole buffer, move constructing into it.// FIXME: This is inefficient, we shouldn't move things into newly allocated// space, then move them up/around, there should only be 2 or 4 move// constructions here.EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 2 ||Constructable::getNumMoveConstructorCalls() == 6);// Move assign the next two to shift them up and make a gap.EXPECT_EQ(1, Constructable::getNumMoveAssignmentCalls());// Copy construct the two new elements from the parameter.EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls());// All without any copy construction.EXPECT_EQ(0, Constructable::getNumCopyConstructorCalls());EXPECT_EQ(this->theVector.begin() + 1, I);this->assertValuesInOrder(this->theVector, 6u, 1, 16, 16, 2, 3, 4);}TYPED_TEST(SmallVectorTest, InsertRepeatedNonIterTest) {SCOPED_TRACE("InsertRepeatedTest");this->makeSequence(this->theVector, 1, 4);Constructable::reset();auto I = this->theVector.insert(this->theVector.begin() + 1, 2, 7);EXPECT_EQ(this->theVector.begin() + 1, I);this->assertValuesInOrder(this->theVector, 6u, 1, 7, 7, 2, 3, 4);}TYPED_TEST(SmallVectorTest, InsertRepeatedAtEndTest) {SCOPED_TRACE("InsertRepeatedTest");this->makeSequence(this->theVector, 1, 4);Constructable::reset();auto I = this->theVector.insert(this->theVector.end(), 2, Constructable(16));// Just copy construct them into newly allocated spaceEXPECT_EQ(2, Constructable::getNumCopyConstructorCalls());// Move everything across if reallocation is needed.EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 0 ||Constructable::getNumMoveConstructorCalls() == 4);// Without ever moving or copying anything else.EXPECT_EQ(0, Constructable::getNumCopyAssignmentCalls());EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls());EXPECT_EQ(this->theVector.begin() + 4, I);this->assertValuesInOrder(this->theVector, 6u, 1, 2, 3, 4, 16, 16);}TYPED_TEST(SmallVectorTest, InsertRepeatedEmptyTest) {SCOPED_TRACE("InsertRepeatedTest");this->makeSequence(this->theVector, 10, 15);// Empty insert.EXPECT_EQ(this->theVector.end(),this->theVector.insert(this->theVector.end(),0, Constructable(42)));EXPECT_EQ(this->theVector.begin() + 1,this->theVector.insert(this->theVector.begin() + 1,0, Constructable(42)));}// Insert range.TYPED_TEST(SmallVectorTest, InsertRangeTest) {SCOPED_TRACE("InsertRangeTest");Constructable Arr[3] ={ Constructable(77), Constructable(77), Constructable(77) };this->makeSequence(this->theVector, 1, 3);Constructable::reset();auto I = this->theVector.insert(this->theVector.begin() + 1, Arr, Arr + 3);// Move construct the top 3 elements into newly allocated space.// Possibly move the whole sequence into new space first.// FIXME: This is inefficient, we shouldn't move things into newly allocated// space, then move them up/around, there should only be 2 or 3 move// constructions here.EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 2 ||Constructable::getNumMoveConstructorCalls() == 5);// Copy assign the lower 2 new elements into existing space.EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls());// Copy construct the third element into newly allocated space.EXPECT_EQ(1, Constructable::getNumCopyConstructorCalls());EXPECT_EQ(this->theVector.begin() + 1, I);this->assertValuesInOrder(this->theVector, 6u, 1, 77, 77, 77, 2, 3);}TYPED_TEST(SmallVectorTest, InsertRangeAtEndTest) {SCOPED_TRACE("InsertRangeTest");Constructable Arr[3] ={ Constructable(77), Constructable(77), Constructable(77) };this->makeSequence(this->theVector, 1, 3);// Insert at end.Constructable::reset();auto I = this->theVector.insert(this->theVector.end(), Arr, Arr+3);// Copy construct the 3 elements into new space at the top.EXPECT_EQ(3, Constructable::getNumCopyConstructorCalls());// Don't copy/move anything else.EXPECT_EQ(0, Constructable::getNumCopyAssignmentCalls());// Reallocation might occur, causing all elements to be moved into the new// buffer.EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 0 ||Constructable::getNumMoveConstructorCalls() == 3);EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls());EXPECT_EQ(this->theVector.begin() + 3, I);this->assertValuesInOrder(this->theVector, 6u,1, 2, 3, 77, 77, 77);}TYPED_TEST(SmallVectorTest, InsertEmptyRangeTest) {SCOPED_TRACE("InsertRangeTest");this->makeSequence(this->theVector, 1, 3);// Empty insert.EXPECT_EQ(this->theVector.end(),this->theVector.insert(this->theVector.end(),this->theVector.begin(),this->theVector.begin()));EXPECT_EQ(this->theVector.begin() + 1,this->theVector.insert(this->theVector.begin() + 1,this->theVector.begin(),this->theVector.begin()));}// Comparison tests.TYPED_TEST(SmallVectorTest, ComparisonEqualityTest) {SCOPED_TRACE("ComparisonEqualityTest");this->makeSequence(this->theVector, 1, 3);this->makeSequence(this->otherVector, 1, 3);EXPECT_TRUE(this->theVector == this->otherVector);EXPECT_FALSE(this->theVector != this->otherVector);this->otherVector.clear();this->makeSequence(this->otherVector, 2, 4);EXPECT_FALSE(this->theVector == this->otherVector);EXPECT_TRUE(this->theVector != this->otherVector);}// Comparison tests.TYPED_TEST(SmallVectorTest, ComparisonLessThanTest) {SCOPED_TRACE("ComparisonLessThanTest");this->theVector = {1, 2, 4};this->otherVector = {1, 4};EXPECT_TRUE(this->theVector < this->otherVector);EXPECT_TRUE(this->theVector <= this->otherVector);EXPECT_FALSE(this->theVector > this->otherVector);EXPECT_FALSE(this->theVector >= this->otherVector);EXPECT_FALSE(this->otherVector < this->theVector);EXPECT_FALSE(this->otherVector <= this->theVector);EXPECT_TRUE(this->otherVector > this->theVector);EXPECT_TRUE(this->otherVector >= this->theVector);this->otherVector = {1, 2, 4};EXPECT_FALSE(this->theVector < this->otherVector);EXPECT_TRUE(this->theVector <= this->otherVector);EXPECT_FALSE(this->theVector > this->otherVector);EXPECT_TRUE(this->theVector >= this->otherVector);EXPECT_FALSE(this->otherVector < this->theVector);EXPECT_TRUE(this->otherVector <= this->theVector);EXPECT_FALSE(this->otherVector > this->theVector);EXPECT_TRUE(this->otherVector >= this->theVector);}// Constant vector tests.TYPED_TEST(SmallVectorTest, ConstVectorTest) {const TypeParam constVector;EXPECT_EQ(0u, constVector.size());EXPECT_TRUE(constVector.empty());EXPECT_TRUE(constVector.begin() == constVector.end());}// Direct array access.TYPED_TEST(SmallVectorTest, DirectVectorTest) {EXPECT_EQ(0u, this->theVector.size());this->theVector.reserve(4);EXPECT_LE(4u, this->theVector.capacity());EXPECT_EQ(0, Constructable::getNumConstructorCalls());this->theVector.push_back(1);this->theVector.push_back(2);this->theVector.push_back(3);this->theVector.push_back(4);EXPECT_EQ(4u, this->theVector.size());EXPECT_EQ(8, Constructable::getNumConstructorCalls());EXPECT_EQ(1, this->theVector[0].getValue());EXPECT_EQ(2, this->theVector[1].getValue());EXPECT_EQ(3, this->theVector[2].getValue());EXPECT_EQ(4, this->theVector[3].getValue());}TYPED_TEST(SmallVectorTest, IteratorTest) {std::list<int> L;this->theVector.insert(this->theVector.end(), L.begin(), L.end());}template <typename InvalidType> class DualSmallVectorsTest;template <typename VectorT1, typename VectorT2>class DualSmallVectorsTest<std::pair<VectorT1, VectorT2>> : public SmallVectorTestBase {protected:VectorT1 theVector;VectorT2 otherVector;template <typename T, unsigned N>static unsigned NumBuiltinElts(const SmallVector<T, N>&) { return N; }};typedef ::testing::Types<// Small mode -> Small mode.std::pair<SmallVector<Constructable, 4>, SmallVector<Constructable, 4>>,// Small mode -> Big mode.std::pair<SmallVector<Constructable, 4>, SmallVector<Constructable, 2>>,// Big mode -> Small mode.std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 4>>,// Big mode -> Big mode.std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 2>>> DualSmallVectorTestTypes;TYPED_TEST_SUITE(DualSmallVectorsTest, DualSmallVectorTestTypes, );TYPED_TEST(DualSmallVectorsTest, MoveAssignment) {SCOPED_TRACE("MoveAssignTest-DualVectorTypes");// Set up our vector with four elements.for (unsigned I = 0; I < 4; ++I)this->otherVector.push_back(Constructable(I));const Constructable *OrigDataPtr = this->otherVector.data();// Move-assign from the other vector.this->theVector =std::move(static_cast<SmallVectorImpl<Constructable>&>(this->otherVector));// Make sure we have the right result.this->assertValuesInOrder(this->theVector, 4u, 0, 1, 2, 3);// Make sure the # of constructor/destructor calls line up. There// are two live objects after clearing the other vector.this->otherVector.clear();EXPECT_EQ(Constructable::getNumConstructorCalls()-4,Constructable::getNumDestructorCalls());// If the source vector (otherVector) was in small-mode, assert that we just// moved the data pointer over.EXPECT_TRUE(this->NumBuiltinElts(this->otherVector) == 4 ||this->theVector.data() == OrigDataPtr);// There shouldn't be any live objects any more.this->theVector.clear();EXPECT_EQ(Constructable::getNumConstructorCalls(),Constructable::getNumDestructorCalls());// We shouldn't have copied anything in this whole process.EXPECT_EQ(Constructable::getNumCopyConstructorCalls(), 0);}struct notassignable {int &x;notassignable(int &x) : x(x) {}};TEST(SmallVectorCustomTest, NoAssignTest) {int x = 0;SmallVector<notassignable, 2> vec;vec.push_back(notassignable(x));x = 42;EXPECT_EQ(42, vec.pop_back_val().x);}struct MovedFrom {bool hasValue;MovedFrom() : hasValue(true) {}MovedFrom(MovedFrom&& m) : hasValue(m.hasValue) {m.hasValue = false;}MovedFrom &operator=(MovedFrom&& m) {hasValue = m.hasValue;m.hasValue = false;return *this;}};TEST(SmallVectorTest, MidInsert) {SmallVector<MovedFrom, 3> v;v.push_back(MovedFrom());v.insert(v.begin(), MovedFrom());for (MovedFrom &m : v)EXPECT_TRUE(m.hasValue);}enum EmplaceableArgState {EAS_Defaulted,EAS_Arg,EAS_LValue,EAS_RValue,EAS_Failure};template <int I> struct EmplaceableArg {EmplaceableArgState State;EmplaceableArg() : State(EAS_Defaulted) {}EmplaceableArg(EmplaceableArg &&X): State(X.State == EAS_Arg ? EAS_RValue : EAS_Failure) {}EmplaceableArg(EmplaceableArg &X): State(X.State == EAS_Arg ? EAS_LValue : EAS_Failure) {}explicit EmplaceableArg(bool) : State(EAS_Arg) {}private:EmplaceableArg &operator=(EmplaceableArg &&) = delete;EmplaceableArg &operator=(const EmplaceableArg &) = delete;};enum EmplaceableState { ES_Emplaced, ES_Moved };struct Emplaceable {EmplaceableArg<0> A0;EmplaceableArg<1> A1;EmplaceableArg<2> A2;EmplaceableArg<3> A3;EmplaceableState State;Emplaceable() : State(ES_Emplaced) {}template <class A0Ty>explicit Emplaceable(A0Ty &&A0): A0(std::forward<A0Ty>(A0)), State(ES_Emplaced) {}template <class A0Ty, class A1Ty>Emplaceable(A0Ty &&A0, A1Ty &&A1): A0(std::forward<A0Ty>(A0)), A1(std::forward<A1Ty>(A1)),State(ES_Emplaced) {}template <class A0Ty, class A1Ty, class A2Ty>Emplaceable(A0Ty &&A0, A1Ty &&A1, A2Ty &&A2): A0(std::forward<A0Ty>(A0)), A1(std::forward<A1Ty>(A1)),A2(std::forward<A2Ty>(A2)), State(ES_Emplaced) {}template <class A0Ty, class A1Ty, class A2Ty, class A3Ty>Emplaceable(A0Ty &&A0, A1Ty &&A1, A2Ty &&A2, A3Ty &&A3): A0(std::forward<A0Ty>(A0)), A1(std::forward<A1Ty>(A1)),A2(std::forward<A2Ty>(A2)), A3(std::forward<A3Ty>(A3)),State(ES_Emplaced) {}Emplaceable(Emplaceable &&) : State(ES_Moved) {}Emplaceable &operator=(Emplaceable &&) {State = ES_Moved;return *this;}private:Emplaceable(const Emplaceable &) = delete;Emplaceable &operator=(const Emplaceable &) = delete;};TEST(SmallVectorTest, EmplaceBack) {EmplaceableArg<0> A0(true);EmplaceableArg<1> A1(true);EmplaceableArg<2> A2(true);EmplaceableArg<3> A3(true);{SmallVector<Emplaceable, 3> V;Emplaceable &back = V.emplace_back();EXPECT_TRUE(&back == &V.back());EXPECT_TRUE(V.size() == 1);EXPECT_TRUE(back.State == ES_Emplaced);EXPECT_TRUE(back.A0.State == EAS_Defaulted);EXPECT_TRUE(back.A1.State == EAS_Defaulted);EXPECT_TRUE(back.A2.State == EAS_Defaulted);EXPECT_TRUE(back.A3.State == EAS_Defaulted);}{SmallVector<Emplaceable, 3> V;Emplaceable &back = V.emplace_back(std::move(A0));EXPECT_TRUE(&back == &V.back());EXPECT_TRUE(V.size() == 1);EXPECT_TRUE(back.State == ES_Emplaced);EXPECT_TRUE(back.A0.State == EAS_RValue);EXPECT_TRUE(back.A1.State == EAS_Defaulted);EXPECT_TRUE(back.A2.State == EAS_Defaulted);EXPECT_TRUE(back.A3.State == EAS_Defaulted);}{SmallVector<Emplaceable, 3> V;Emplaceable &back = V.emplace_back(A0);EXPECT_TRUE(&back == &V.back());EXPECT_TRUE(V.size() == 1);EXPECT_TRUE(back.State == ES_Emplaced);EXPECT_TRUE(back.A0.State == EAS_LValue);EXPECT_TRUE(back.A1.State == EAS_Defaulted);EXPECT_TRUE(back.A2.State == EAS_Defaulted);EXPECT_TRUE(back.A3.State == EAS_Defaulted);}{SmallVector<Emplaceable, 3> V;Emplaceable &back = V.emplace_back(A0, A1);EXPECT_TRUE(&back == &V.back());EXPECT_TRUE(V.size() == 1);EXPECT_TRUE(back.State == ES_Emplaced);EXPECT_TRUE(back.A0.State == EAS_LValue);EXPECT_TRUE(back.A1.State == EAS_LValue);EXPECT_TRUE(back.A2.State == EAS_Defaulted);EXPECT_TRUE(back.A3.State == EAS_Defaulted);}{SmallVector<Emplaceable, 3> V;Emplaceable &back = V.emplace_back(std::move(A0), std::move(A1));EXPECT_TRUE(&back == &V.back());EXPECT_TRUE(V.size() == 1);EXPECT_TRUE(back.State == ES_Emplaced);EXPECT_TRUE(back.A0.State == EAS_RValue);EXPECT_TRUE(back.A1.State == EAS_RValue);EXPECT_TRUE(back.A2.State == EAS_Defaulted);EXPECT_TRUE(back.A3.State == EAS_Defaulted);}{SmallVector<Emplaceable, 3> V;Emplaceable &back = V.emplace_back(std::move(A0), A1, std::move(A2), A3);EXPECT_TRUE(&back == &V.back());EXPECT_TRUE(V.size() == 1);EXPECT_TRUE(back.State == ES_Emplaced);EXPECT_TRUE(back.A0.State == EAS_RValue);EXPECT_TRUE(back.A1.State == EAS_LValue);EXPECT_TRUE(back.A2.State == EAS_RValue);EXPECT_TRUE(back.A3.State == EAS_LValue);}{SmallVector<int, 1> V;V.emplace_back();V.emplace_back(42);EXPECT_EQ(2U, V.size());EXPECT_EQ(0, V[0]);EXPECT_EQ(42, V[1]);}}TEST(SmallVectorTest, DefaultInlinedElements) {SmallVector<int> V;EXPECT_TRUE(V.empty());V.push_back(7);EXPECT_EQ(V[0], 7);// Check that at least a couple layers of nested SmallVector<T>'s are allowed// by the default inline elements policy. This pattern happens in practice// with some frequency, and it seems fairly harmless even though each layer of// SmallVector's will grow the total sizeof by a vector header beyond the// "preferred" maximum sizeof.SmallVector<SmallVector<SmallVector<int>>> NestedV;NestedV.emplace_back().emplace_back().emplace_back(42);EXPECT_EQ(NestedV[0][0][0], 42);}TEST(SmallVectorTest, InitializerList) {SmallVector<int, 2> V1 = {};EXPECT_TRUE(V1.empty());V1 = {0, 0};EXPECT_TRUE(makeArrayRef(V1).equals({0, 0}));V1 = {-1, -1};EXPECT_TRUE(makeArrayRef(V1).equals({-1, -1}));SmallVector<int, 2> V2 = {1, 2, 3, 4};EXPECT_TRUE(makeArrayRef(V2).equals({1, 2, 3, 4}));V2.assign({4});EXPECT_TRUE(makeArrayRef(V2).equals({4}));V2.append({3, 2});EXPECT_TRUE(makeArrayRef(V2).equals({4, 3, 2}));V2.insert(V2.begin() + 1, 5);EXPECT_TRUE(makeArrayRef(V2).equals({4, 5, 3, 2}));}template <class VectorT>class SmallVectorReferenceInvalidationTest : public SmallVectorTestBase {protected:const char *AssertionMessage ="Attempting to reference an element of the vector in an operation \" ""\"that invalidates it";VectorT V;template <typename T, unsigned N>static unsigned NumBuiltinElts(const SmallVector<T, N> &) {return N;}template <class T> static bool isValueType() {return std::is_same<T, typename VectorT::value_type>::value;}void SetUp() override {SmallVectorTestBase::SetUp();// Fill up the small size so that insertions move the elements.for (int I = 0, E = NumBuiltinElts(V); I != E; ++I)V.emplace_back(I + 1);}};// Test one type that's trivially copyable (int) and one that isn't// (Constructable) since reference invalidation may be fixed differently for// each.using SmallVectorReferenceInvalidationTestTypes =::testing::Types<SmallVector<int, 3>, SmallVector<Constructable, 3>>;TYPED_TEST_SUITE(SmallVectorReferenceInvalidationTest,SmallVectorReferenceInvalidationTestTypes, );TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBack) {// Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.auto &V = this->V;int N = this->NumBuiltinElts(V);// Push back a reference to last element when growing from small storage.V.push_back(V.back());EXPECT_EQ(N, V.back());// Check that the old value is still there (not moved away).EXPECT_EQ(N, V[V.size() - 2]);// Fill storage again.V.back() = V.size();while (V.size() < V.capacity())V.push_back(V.size() + 1);// Push back a reference to last element when growing from large storage.V.push_back(V.back());EXPECT_EQ(int(V.size()) - 1, V.back());}TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBackMoved) {// Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.auto &V = this->V;int N = this->NumBuiltinElts(V);// Push back a reference to last element when growing from small storage.V.push_back(std::move(V.back()));EXPECT_EQ(N, V.back());if (this->template isValueType<Constructable>()) {// Check that the value was moved (not copied).EXPECT_EQ(0, V[V.size() - 2]);}// Fill storage again.V.back() = V.size();while (V.size() < V.capacity())V.push_back(V.size() + 1);// Push back a reference to last element when growing from large storage.V.push_back(std::move(V.back()));// Check the values.EXPECT_EQ(int(V.size()) - 1, V.back());if (this->template isValueType<Constructable>()) {// Check the value got moved out.EXPECT_EQ(0, V[V.size() - 2]);}}TYPED_TEST(SmallVectorReferenceInvalidationTest, Resize) {auto &V = this->V;(void)V;int N = this->NumBuiltinElts(V);V.resize(N + 1, V.back());EXPECT_EQ(N, V.back());// Resize to add enough elements that V will grow again. If reference// invalidation breaks in the future, sanitizers should be able to catch a// use-after-free here.V.resize(V.capacity() + 1, V.front());EXPECT_EQ(1, V.back());}TYPED_TEST(SmallVectorReferenceInvalidationTest, Append) {auto &V = this->V;(void)V;V.append(1, V.back());int N = this->NumBuiltinElts(V);EXPECT_EQ(N, V[N - 1]);// Append enough more elements that V will grow again. This tests growing// when already in large mode.//// If reference invalidation breaks in the future, sanitizers should be able// to catch a use-after-free here.V.append(V.capacity() - V.size() + 1, V.front());EXPECT_EQ(1, V.back());}TYPED_TEST(SmallVectorReferenceInvalidationTest, AppendRange) {auto &V = this->V;(void)V;#if !defined(NDEBUG) && GTEST_HAS_DEATH_TESTEXPECT_DEATH(V.append(V.begin(), V.begin() + 1), this->AssertionMessage);ASSERT_EQ(3u, this->NumBuiltinElts(V));ASSERT_EQ(3u, V.size());V.pop_back();ASSERT_EQ(2u, V.size());// Confirm this checks for growth when there's more than one element// appended.EXPECT_DEATH(V.append(V.begin(), V.end()), this->AssertionMessage);#endif}TYPED_TEST(SmallVectorReferenceInvalidationTest, Assign) {// Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.auto &V = this->V;(void)V;int N = this->NumBuiltinElts(V);ASSERT_EQ(unsigned(N), V.size());ASSERT_EQ(unsigned(N), V.capacity());// Check assign that shrinks in small mode.V.assign(1, V.back());EXPECT_EQ(1u, V.size());EXPECT_EQ(N, V[0]);// Check assign that grows within small mode.ASSERT_LT(V.size(), V.capacity());V.assign(V.capacity(), V.back());for (int I = 0, E = V.size(); I != E; ++I) {EXPECT_EQ(N, V[I]);// Reset to [1, 2, ...].V[I] = I + 1;}// Check assign that grows to large mode.ASSERT_EQ(2, V[1]);V.assign(V.capacity() + 1, V[1]);for (int I = 0, E = V.size(); I != E; ++I) {EXPECT_EQ(2, V[I]);// Reset to [1, 2, ...].V[I] = I + 1;}// Check assign that shrinks in large mode.V.assign(1, V[1]);EXPECT_EQ(2, V[0]);}TYPED_TEST(SmallVectorReferenceInvalidationTest, AssignRange) {auto &V = this->V;#if !defined(NDEBUG) && GTEST_HAS_DEATH_TESTEXPECT_DEATH(V.assign(V.begin(), V.end()), this->AssertionMessage);EXPECT_DEATH(V.assign(V.begin(), V.end() - 1), this->AssertionMessage);#endifV.assign(V.begin(), V.begin());EXPECT_TRUE(V.empty());}TYPED_TEST(SmallVectorReferenceInvalidationTest, Insert) {// Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.auto &V = this->V;(void)V;// Insert a reference to the back (not at end() or else insert delegates to// push_back()), growing out of small mode. Confirm the value was copied out// (moving out Constructable sets it to 0).V.insert(V.begin(), V.back());EXPECT_EQ(int(V.size() - 1), V.front());EXPECT_EQ(int(V.size() - 1), V.back());// Fill up the vector again.while (V.size() < V.capacity())V.push_back(V.size() + 1);// Grow again from large storage to large storage.V.insert(V.begin(), V.back());EXPECT_EQ(int(V.size() - 1), V.front());EXPECT_EQ(int(V.size() - 1), V.back());}TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertMoved) {// Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.auto &V = this->V;(void)V;// Insert a reference to the back (not at end() or else insert delegates to// push_back()), growing out of small mode. Confirm the value was copied out// (moving out Constructable sets it to 0).V.insert(V.begin(), std::move(V.back()));EXPECT_EQ(int(V.size() - 1), V.front());if (this->template isValueType<Constructable>()) {// Check the value got moved out.EXPECT_EQ(0, V.back());}// Fill up the vector again.while (V.size() < V.capacity())V.push_back(V.size() + 1);// Grow again from large storage to large storage.V.insert(V.begin(), std::move(V.back()));EXPECT_EQ(int(V.size() - 1), V.front());if (this->template isValueType<Constructable>()) {// Check the value got moved out.EXPECT_EQ(0, V.back());}}TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertN) {auto &V = this->V;(void)V;// Cover NumToInsert <= this->end() - I.V.insert(V.begin() + 1, 1, V.back());int N = this->NumBuiltinElts(V);EXPECT_EQ(N, V[1]);// Cover NumToInsert > this->end() - I, inserting enough elements that V will// also grow again; V.capacity() will be more elements than necessary but// it's a simple way to cover both conditions.//// If reference invalidation breaks in the future, sanitizers should be able// to catch a use-after-free here.V.insert(V.begin(), V.capacity(), V.front());EXPECT_EQ(1, V.front());}TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertRange) {auto &V = this->V;(void)V;#if !defined(NDEBUG) && GTEST_HAS_DEATH_TESTEXPECT_DEATH(V.insert(V.begin(), V.begin(), V.begin() + 1),this->AssertionMessage);ASSERT_EQ(3u, this->NumBuiltinElts(V));ASSERT_EQ(3u, V.size());V.pop_back();ASSERT_EQ(2u, V.size());// Confirm this checks for growth when there's more than one element// inserted.EXPECT_DEATH(V.insert(V.begin(), V.begin(), V.end()), this->AssertionMessage);#endif}TYPED_TEST(SmallVectorReferenceInvalidationTest, EmplaceBack) {// Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.auto &V = this->V;int N = this->NumBuiltinElts(V);// Push back a reference to last element when growing from small storage.V.emplace_back(V.back());EXPECT_EQ(N, V.back());// Check that the old value is still there (not moved away).EXPECT_EQ(N, V[V.size() - 2]);// Fill storage again.V.back() = V.size();while (V.size() < V.capacity())V.push_back(V.size() + 1);// Push back a reference to last element when growing from large storage.V.emplace_back(V.back());EXPECT_EQ(int(V.size()) - 1, V.back());}template <class VectorT>class SmallVectorInternalReferenceInvalidationTest: public SmallVectorTestBase {protected:const char *AssertionMessage ="Attempting to reference an element of the vector in an operation \" ""\"that invalidates it";VectorT V;template <typename T, unsigned N>static unsigned NumBuiltinElts(const SmallVector<T, N> &) {return N;}void SetUp() override {SmallVectorTestBase::SetUp();// Fill up the small size so that insertions move the elements.for (int I = 0, E = NumBuiltinElts(V); I != E; ++I)V.emplace_back(I + 1, I + 1);}};// Test pairs of the same types from SmallVectorReferenceInvalidationTestTypes.using SmallVectorInternalReferenceInvalidationTestTypes =::testing::Types<SmallVector<std::pair<int, int>, 3>,SmallVector<std::pair<Constructable, Constructable>, 3>>;TYPED_TEST_SUITE(SmallVectorInternalReferenceInvalidationTest,SmallVectorInternalReferenceInvalidationTestTypes, );TYPED_TEST(SmallVectorInternalReferenceInvalidationTest, EmplaceBack) {// Note: setup adds [1, 2, ...] to V until it's at capacity in small mode.auto &V = this->V;int N = this->NumBuiltinElts(V);// Push back a reference to last element when growing from small storage.V.emplace_back(V.back().first, V.back().second);EXPECT_EQ(N, V.back().first);EXPECT_EQ(N, V.back().second);// Check that the old value is still there (not moved away).EXPECT_EQ(N, V[V.size() - 2].first);EXPECT_EQ(N, V[V.size() - 2].second);// Fill storage again.V.back().first = V.back().second = V.size();while (V.size() < V.capacity())V.emplace_back(V.size() + 1, V.size() + 1);// Push back a reference to last element when growing from large storage.V.emplace_back(V.back().first, V.back().second);EXPECT_EQ(int(V.size()) - 1, V.back().first);EXPECT_EQ(int(V.size()) - 1, V.back().second);}} // end namespace
//===- llvm/unittest/ADT/SmallStringTest.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===////// SmallString unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallString.h"#include "gtest/gtest.h"#include <climits>#include <cstring>#include <stdarg.h>using namespace llvm;namespace {// Test fixture classclass SmallStringTest : public testing::Test {protected:typedef SmallString<40> StringType;StringType theString;void assertEmpty(StringType & v) {// Size testsEXPECT_EQ(0u, v.size());EXPECT_TRUE(v.empty());// Iterator testsEXPECT_TRUE(v.begin() == v.end());}};// New string test.TEST_F(SmallStringTest, EmptyStringTest) {SCOPED_TRACE("EmptyStringTest");assertEmpty(theString);EXPECT_TRUE(theString.rbegin() == theString.rend());}TEST_F(SmallStringTest, AssignRepeated) {theString.assign(3, 'a');EXPECT_EQ(3u, theString.size());EXPECT_STREQ("aaa", theString.c_str());}TEST_F(SmallStringTest, AssignIterPair) {StringRef abc = "abc";theString.assign(abc.begin(), abc.end());EXPECT_EQ(3u, theString.size());EXPECT_STREQ("abc", theString.c_str());}TEST_F(SmallStringTest, AssignStringRef) {StringRef abc = "abc";theString.assign(abc);EXPECT_EQ(3u, theString.size());EXPECT_STREQ("abc", theString.c_str());}TEST_F(SmallStringTest, AssignSmallVector) {StringRef abc = "abc";SmallVector<char, 10> abcVec(abc.begin(), abc.end());theString.assign(abcVec);EXPECT_EQ(3u, theString.size());EXPECT_STREQ("abc", theString.c_str());}TEST_F(SmallStringTest, AssignStringRefs) {theString.assign({"abc", "def", "ghi"});EXPECT_EQ(9u, theString.size());EXPECT_STREQ("abcdefghi", theString.c_str());}TEST_F(SmallStringTest, AppendIterPair) {StringRef abc = "abc";theString.append(abc.begin(), abc.end());theString.append(abc.begin(), abc.end());EXPECT_EQ(6u, theString.size());EXPECT_STREQ("abcabc", theString.c_str());}TEST_F(SmallStringTest, AppendStringRef) {StringRef abc = "abc";theString.append(abc);theString.append(abc);EXPECT_EQ(6u, theString.size());EXPECT_STREQ("abcabc", theString.c_str());}TEST_F(SmallStringTest, AppendSmallVector) {StringRef abc = "abc";SmallVector<char, 10> abcVec(abc.begin(), abc.end());theString.append(abcVec);theString.append(abcVec);EXPECT_EQ(6u, theString.size());EXPECT_STREQ("abcabc", theString.c_str());}TEST_F(SmallStringTest, AppendStringRefs) {theString.append({"abc", "def", "ghi"});EXPECT_EQ(9u, theString.size());EXPECT_STREQ("abcdefghi", theString.c_str());StringRef Jkl = "jkl";std::string Mno = "mno";SmallString<4> Pqr("pqr");const char *Stu = "stu";theString.append({Jkl, Mno, Pqr, Stu});EXPECT_EQ(21u, theString.size());EXPECT_STREQ("abcdefghijklmnopqrstu", theString.c_str());}TEST_F(SmallStringTest, StringRefConversion) {StringRef abc = "abc";theString.assign(abc.begin(), abc.end());StringRef theStringRef = theString;EXPECT_EQ("abc", theStringRef);}TEST_F(SmallStringTest, StdStringConversion) {StringRef abc = "abc";theString.assign(abc.begin(), abc.end());std::string theStdString = std::string(theString);EXPECT_EQ("abc", theStdString);}TEST_F(SmallStringTest, Substr) {theString = "hello";EXPECT_EQ("lo", theString.substr(3));EXPECT_EQ("", theString.substr(100));EXPECT_EQ("hello", theString.substr(0, 100));EXPECT_EQ("o", theString.substr(4, 10));}TEST_F(SmallStringTest, Slice) {theString = "hello";EXPECT_EQ("l", theString.slice(2, 3));EXPECT_EQ("ell", theString.slice(1, 4));EXPECT_EQ("llo", theString.slice(2, 100));EXPECT_EQ("", theString.slice(2, 1));EXPECT_EQ("", theString.slice(10, 20));}TEST_F(SmallStringTest, Find) {theString = "hello";EXPECT_EQ(2U, theString.find('l'));EXPECT_EQ(StringRef::npos, theString.find('z'));EXPECT_EQ(StringRef::npos, theString.find("helloworld"));EXPECT_EQ(0U, theString.find("hello"));EXPECT_EQ(1U, theString.find("ello"));EXPECT_EQ(StringRef::npos, theString.find("zz"));EXPECT_EQ(2U, theString.find("ll", 2));EXPECT_EQ(StringRef::npos, theString.find("ll", 3));EXPECT_EQ(0U, theString.find(""));EXPECT_EQ(3U, theString.rfind('l'));EXPECT_EQ(StringRef::npos, theString.rfind('z'));EXPECT_EQ(StringRef::npos, theString.rfind("helloworld"));EXPECT_EQ(0U, theString.rfind("hello"));EXPECT_EQ(1U, theString.rfind("ello"));EXPECT_EQ(StringRef::npos, theString.rfind("zz"));EXPECT_EQ(2U, theString.find_first_of('l'));EXPECT_EQ(1U, theString.find_first_of("el"));EXPECT_EQ(StringRef::npos, theString.find_first_of("xyz"));EXPECT_EQ(1U, theString.find_first_not_of('h'));EXPECT_EQ(4U, theString.find_first_not_of("hel"));EXPECT_EQ(StringRef::npos, theString.find_first_not_of("hello"));theString = "hellx xello hell ello world foo bar hello";EXPECT_EQ(36U, theString.find("hello"));EXPECT_EQ(28U, theString.find("foo"));EXPECT_EQ(12U, theString.find("hell", 2));EXPECT_EQ(0U, theString.find(""));}TEST_F(SmallStringTest, Count) {theString = "hello";EXPECT_EQ(2U, theString.count('l'));EXPECT_EQ(1U, theString.count('o'));EXPECT_EQ(0U, theString.count('z'));EXPECT_EQ(0U, theString.count("helloworld"));EXPECT_EQ(1U, theString.count("hello"));EXPECT_EQ(1U, theString.count("ello"));EXPECT_EQ(0U, theString.count("zz"));}TEST_F(SmallStringTest, Realloc) {theString = "abcd";theString.reserve(100);EXPECT_EQ("abcd", theString);unsigned const N = 100000;theString.reserve(N);for (unsigned i = 0; i < N - 4; ++i)theString.push_back('y');EXPECT_EQ("abcdyyy", theString.slice(0, 7));}TEST_F(SmallStringTest, Comparisons) {EXPECT_EQ(-1, SmallString<10>("aab").compare("aad"));EXPECT_EQ( 0, SmallString<10>("aab").compare("aab"));EXPECT_EQ( 1, SmallString<10>("aab").compare("aaa"));EXPECT_EQ(-1, SmallString<10>("aab").compare("aabb"));EXPECT_EQ( 1, SmallString<10>("aab").compare("aa"));EXPECT_EQ( 1, SmallString<10>("\xFF").compare("\1"));EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aAd"));EXPECT_EQ( 0, SmallString<10>("AaB").compare_insensitive("aab"));EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("AAA"));EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aaBb"));EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("aA"));EXPECT_EQ( 1, SmallString<10>("\xFF").compare_insensitive("\1"));EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aad"));EXPECT_EQ( 0, SmallString<10>("aab").compare_numeric("aab"));EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aaa"));EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aabb"));EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aa"));EXPECT_EQ(-1, SmallString<10>("1").compare_numeric("10"));EXPECT_EQ( 0, SmallString<10>("10").compare_numeric("10"));EXPECT_EQ( 0, SmallString<10>("10a").compare_numeric("10a"));EXPECT_EQ( 1, SmallString<10>("2").compare_numeric("1"));EXPECT_EQ( 0, SmallString<10>("llvm_v1i64_ty").compare_numeric("llvm_v1i64_ty"));EXPECT_EQ( 1, SmallString<10>("\xFF").compare_numeric("\1"));EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V1_q0"));EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V16"));EXPECT_EQ(-1, SmallString<10>("V8_q0").compare_numeric("V16"));EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V8_q0"));EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V8_q0"));EXPECT_EQ( 1, SmallString<10>("V8_q0").compare_numeric("V1_q0"));}// Check gtest prints SmallString as a string instead of a container of chars.// The code is in utils/unittest/googletest/internal/custom/gtest-printers.hTEST_F(SmallStringTest, GTestPrinter) {EXPECT_EQ(R"("foo")", ::testing::PrintToString(SmallString<1>("foo")));const SmallVectorImpl<char> &ErasedSmallString = SmallString<1>("foo");EXPECT_EQ(R"("foo")", ::testing::PrintToString(ErasedSmallString));}} // namespace
//===- llvm/unittest/ADT/SmallSetTest.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===////// SmallSet unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallSet.h"#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"#include <string>using namespace llvm;TEST(SmallSetTest, Insert) {SmallSet<int, 4> s1;for (int i = 0; i < 4; i++)s1.insert(i);for (int i = 0; i < 4; i++)s1.insert(i);EXPECT_EQ(4u, s1.size());for (int i = 0; i < 4; i++)EXPECT_EQ(1u, s1.count(i));EXPECT_EQ(0u, s1.count(4));}TEST(SmallSetTest, Grow) {SmallSet<int, 4> s1;for (int i = 0; i < 8; i++)s1.insert(i);EXPECT_EQ(8u, s1.size());for (int i = 0; i < 8; i++)EXPECT_EQ(1u, s1.count(i));EXPECT_EQ(0u, s1.count(8));}TEST(SmallSetTest, Erase) {SmallSet<int, 4> s1;for (int i = 0; i < 8; i++)s1.insert(i);EXPECT_EQ(8u, s1.size());// Remove elements one by one and check if all other elements are still there.for (int i = 0; i < 8; i++) {EXPECT_EQ(1u, s1.count(i));EXPECT_TRUE(s1.erase(i));EXPECT_EQ(0u, s1.count(i));EXPECT_EQ(8u - i - 1, s1.size());for (int j = i + 1; j < 8; j++)EXPECT_EQ(1u, s1.count(j));}EXPECT_EQ(0u, s1.count(8));}TEST(SmallSetTest, IteratorInt) {SmallSet<int, 4> s1;// Test the 'small' case.for (int i = 0; i < 3; i++)s1.insert(i);std::vector<int> V(s1.begin(), s1.end());// Make sure the elements are in the expected order.llvm::sort(V);for (int i = 0; i < 3; i++)EXPECT_EQ(i, V[i]);// Test the 'big' case by adding a few more elements to switch to std::set// internally.for (int i = 3; i < 6; i++)s1.insert(i);V.assign(s1.begin(), s1.end());// Make sure the elements are in the expected order.llvm::sort(V);for (int i = 0; i < 6; i++)EXPECT_EQ(i, V[i]);}TEST(SmallSetTest, IteratorString) {// Test SmallSetIterator for SmallSet with a type with non-trivial// ctors/dtors.SmallSet<std::string, 2> s1;s1.insert("str 1");s1.insert("str 2");s1.insert("str 1");std::vector<std::string> V(s1.begin(), s1.end());llvm::sort(V);EXPECT_EQ(2u, s1.size());EXPECT_EQ("str 1", V[0]);EXPECT_EQ("str 2", V[1]);s1.insert("str 4");s1.insert("str 0");s1.insert("str 4");V.assign(s1.begin(), s1.end());// Make sure the elements are in the expected order.llvm::sort(V);EXPECT_EQ(4u, s1.size());EXPECT_EQ("str 0", V[0]);EXPECT_EQ("str 1", V[1]);EXPECT_EQ("str 2", V[2]);EXPECT_EQ("str 4", V[3]);}TEST(SmallSetTest, IteratorIncMoveCopy) {// Test SmallSetIterator for SmallSet with a type with non-trivial// ctors/dtors.SmallSet<std::string, 2> s1;s1.insert("str 1");s1.insert("str 2");auto Iter = s1.begin();EXPECT_EQ("str 1", *Iter);++Iter;EXPECT_EQ("str 2", *Iter);s1.insert("str 4");s1.insert("str 0");auto Iter2 = s1.begin();Iter = std::move(Iter2);EXPECT_EQ("str 0", *Iter);}TEST(SmallSetTest, EqualityComparisonTest) {SmallSet<int, 8> s1small;SmallSet<int, 10> s2small;SmallSet<int, 3> s3large;SmallSet<int, 8> s4large;for (int i = 1; i < 5; i++) {s1small.insert(i);s2small.insert(5 - i);s3large.insert(i);}for (int i = 1; i < 11; i++)s4large.insert(i);EXPECT_EQ(s1small, s1small);EXPECT_EQ(s3large, s3large);EXPECT_EQ(s1small, s2small);EXPECT_EQ(s1small, s3large);EXPECT_EQ(s2small, s3large);EXPECT_NE(s1small, s4large);EXPECT_NE(s4large, s3large);}TEST(SmallSetTest, Contains) {SmallSet<int, 2> Set;EXPECT_FALSE(Set.contains(0));EXPECT_FALSE(Set.contains(1));Set.insert(0);Set.insert(1);EXPECT_TRUE(Set.contains(0));EXPECT_TRUE(Set.contains(1));Set.insert(1);EXPECT_TRUE(Set.contains(0));EXPECT_TRUE(Set.contains(1));Set.erase(1);EXPECT_TRUE(Set.contains(0));EXPECT_FALSE(Set.contains(1));Set.insert(1);Set.insert(2);EXPECT_TRUE(Set.contains(0));EXPECT_TRUE(Set.contains(1));EXPECT_TRUE(Set.contains(2));}
//===- llvm/unittest/ADT/SmallPtrSetTest.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===////// SmallPtrSet unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/SmallPtrSet.h"#include "llvm/ADT/PointerIntPair.h"#include "llvm/ADT/STLExtras.h"#include "llvm/Support/PointerLikeTypeTraits.h"#include "gtest/gtest.h"using namespace llvm;TEST(SmallPtrSetTest, Assignment) {int buf[8];for (int i = 0; i < 8; ++i)buf[i] = 0;SmallPtrSet<int *, 4> s1 = {&buf[0], &buf[1]};SmallPtrSet<int *, 4> s2;(s2 = s1).insert(&buf[2]);// Self assign as well.(s2 = static_cast<SmallPtrSet<int *, 4> &>(s2)).insert(&buf[3]);s1 = s2;EXPECT_EQ(4U, s1.size());for (int i = 0; i < 8; ++i)if (i < 4)EXPECT_TRUE(s1.count(&buf[i]));elseEXPECT_FALSE(s1.count(&buf[i]));// Assign and insert with initializer lists, and ones that contain both// duplicates and out-of-order elements.(s2 = {&buf[6], &buf[7], &buf[6]}).insert({&buf[5], &buf[4]});for (int i = 0; i < 8; ++i)if (i < 4)EXPECT_FALSE(s2.count(&buf[i]));elseEXPECT_TRUE(s2.count(&buf[i]));}TEST(SmallPtrSetTest, GrowthTest) {int i;int buf[8];for(i=0; i<8; ++i) buf[i]=0;SmallPtrSet<int *, 4> s;typedef SmallPtrSet<int *, 4>::iterator iter;s.insert(&buf[0]);s.insert(&buf[1]);s.insert(&buf[2]);s.insert(&buf[3]);EXPECT_EQ(4U, s.size());i = 0;for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i)(**I)++;EXPECT_EQ(4, i);for(i=0; i<8; ++i)EXPECT_EQ(i<4?1:0,buf[i]);s.insert(&buf[4]);s.insert(&buf[5]);s.insert(&buf[6]);s.insert(&buf[7]);i = 0;for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i)(**I)++;EXPECT_EQ(8, i);s.erase(&buf[4]);s.erase(&buf[5]);s.erase(&buf[6]);s.erase(&buf[7]);EXPECT_EQ(4U, s.size());i = 0;for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i)(**I)++;EXPECT_EQ(4, i);for(i=0; i<8; ++i)EXPECT_EQ(i<4?3:1,buf[i]);s.clear();for(i=0; i<8; ++i) buf[i]=0;for(i=0; i<128; ++i) s.insert(&buf[i%8]); // test repeated entiresEXPECT_EQ(8U, s.size());for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i)(**I)++;for(i=0; i<8; ++i)EXPECT_EQ(1,buf[i]);}TEST(SmallPtrSetTest, CopyAndMoveTest) {int buf[8];for (int i = 0; i < 8; ++i)buf[i] = 0;SmallPtrSet<int *, 4> s1;s1.insert(&buf[0]);s1.insert(&buf[1]);s1.insert(&buf[2]);s1.insert(&buf[3]);EXPECT_EQ(4U, s1.size());for (int i = 0; i < 8; ++i)if (i < 4)EXPECT_TRUE(s1.count(&buf[i]));elseEXPECT_FALSE(s1.count(&buf[i]));SmallPtrSet<int *, 4> s2(s1);EXPECT_EQ(4U, s2.size());for (int i = 0; i < 8; ++i)if (i < 4)EXPECT_TRUE(s2.count(&buf[i]));elseEXPECT_FALSE(s2.count(&buf[i]));s1 = s2;EXPECT_EQ(4U, s1.size());EXPECT_EQ(4U, s2.size());for (int i = 0; i < 8; ++i)if (i < 4)EXPECT_TRUE(s1.count(&buf[i]));elseEXPECT_FALSE(s1.count(&buf[i]));SmallPtrSet<int *, 4> s3(std::move(s1));EXPECT_EQ(4U, s3.size());EXPECT_TRUE(s1.empty());for (int i = 0; i < 8; ++i)if (i < 4)EXPECT_TRUE(s3.count(&buf[i]));elseEXPECT_FALSE(s3.count(&buf[i]));// Move assign into the moved-from object. Also test move of a non-small// container.s3.insert(&buf[4]);s3.insert(&buf[5]);s3.insert(&buf[6]);s3.insert(&buf[7]);s1 = std::move(s3);EXPECT_EQ(8U, s1.size());EXPECT_TRUE(s3.empty());for (int i = 0; i < 8; ++i)EXPECT_TRUE(s1.count(&buf[i]));// Copy assign into a moved-from object.s3 = s1;EXPECT_EQ(8U, s3.size());EXPECT_EQ(8U, s1.size());for (int i = 0; i < 8; ++i)EXPECT_TRUE(s3.count(&buf[i]));}TEST(SmallPtrSetTest, SwapTest) {int buf[10];SmallPtrSet<int *, 2> a;SmallPtrSet<int *, 2> b;a.insert(&buf[0]);a.insert(&buf[1]);b.insert(&buf[2]);EXPECT_EQ(2U, a.size());EXPECT_EQ(1U, b.size());EXPECT_TRUE(a.count(&buf[0]));EXPECT_TRUE(a.count(&buf[1]));EXPECT_FALSE(a.count(&buf[2]));EXPECT_FALSE(a.count(&buf[3]));EXPECT_FALSE(b.count(&buf[0]));EXPECT_FALSE(b.count(&buf[1]));EXPECT_TRUE(b.count(&buf[2]));EXPECT_FALSE(b.count(&buf[3]));std::swap(a, b);EXPECT_EQ(1U, a.size());EXPECT_EQ(2U, b.size());EXPECT_FALSE(a.count(&buf[0]));EXPECT_FALSE(a.count(&buf[1]));EXPECT_TRUE(a.count(&buf[2]));EXPECT_FALSE(a.count(&buf[3]));EXPECT_TRUE(b.count(&buf[0]));EXPECT_TRUE(b.count(&buf[1]));EXPECT_FALSE(b.count(&buf[2]));EXPECT_FALSE(b.count(&buf[3]));b.insert(&buf[3]);std::swap(a, b);EXPECT_EQ(3U, a.size());EXPECT_EQ(1U, b.size());EXPECT_TRUE(a.count(&buf[0]));EXPECT_TRUE(a.count(&buf[1]));EXPECT_FALSE(a.count(&buf[2]));EXPECT_TRUE(a.count(&buf[3]));EXPECT_FALSE(b.count(&buf[0]));EXPECT_FALSE(b.count(&buf[1]));EXPECT_TRUE(b.count(&buf[2]));EXPECT_FALSE(b.count(&buf[3]));std::swap(a, b);EXPECT_EQ(1U, a.size());EXPECT_EQ(3U, b.size());EXPECT_FALSE(a.count(&buf[0]));EXPECT_FALSE(a.count(&buf[1]));EXPECT_TRUE(a.count(&buf[2]));EXPECT_FALSE(a.count(&buf[3]));EXPECT_TRUE(b.count(&buf[0]));EXPECT_TRUE(b.count(&buf[1]));EXPECT_FALSE(b.count(&buf[2]));EXPECT_TRUE(b.count(&buf[3]));a.insert(&buf[4]);a.insert(&buf[5]);a.insert(&buf[6]);std::swap(b, a);EXPECT_EQ(3U, a.size());EXPECT_EQ(4U, b.size());EXPECT_TRUE(b.count(&buf[2]));EXPECT_TRUE(b.count(&buf[4]));EXPECT_TRUE(b.count(&buf[5]));EXPECT_TRUE(b.count(&buf[6]));EXPECT_TRUE(a.count(&buf[0]));EXPECT_TRUE(a.count(&buf[1]));EXPECT_TRUE(a.count(&buf[3]));}void checkEraseAndIterators(SmallPtrSetImpl<int*> &S) {int buf[3];S.insert(&buf[0]);S.insert(&buf[1]);S.insert(&buf[2]);// Iterators must still be valid after erase() calls;auto B = S.begin();auto M = std::next(B);auto E = S.end();EXPECT_TRUE(*B == &buf[0] || *B == &buf[1] || *B == &buf[2]);EXPECT_TRUE(*M == &buf[0] || *M == &buf[1] || *M == &buf[2]);EXPECT_TRUE(*B != *M);int *Removable = *std::next(M);// No iterator points to Removable now.EXPECT_TRUE(Removable == &buf[0] || Removable == &buf[1] ||Removable == &buf[2]);EXPECT_TRUE(Removable != *B && Removable != *M);S.erase(Removable);// B,M,E iterators should still be validEXPECT_EQ(B, S.begin());EXPECT_EQ(M, std::next(B));EXPECT_EQ(E, S.end());EXPECT_EQ(std::next(M), E);}TEST(SmallPtrSetTest, EraseTest) {// Test when set stays small.SmallPtrSet<int *, 8> B;checkEraseAndIterators(B);// Test when set grows big.SmallPtrSet<int *, 2> A;checkEraseAndIterators(A);}// Verify that dereferencing and iteration work.TEST(SmallPtrSetTest, dereferenceAndIterate) {int Ints[] = {0, 1, 2, 3, 4, 5, 6, 7};SmallPtrSet<const int *, 4> S;for (int &I : Ints) {EXPECT_EQ(&I, *S.insert(&I).first);EXPECT_EQ(&I, *S.find(&I));}// Iterate from each and count how many times each element is found.int Found[sizeof(Ints)/sizeof(int)] = {0};for (int &I : Ints)for (auto F = S.find(&I), E = S.end(); F != E; ++F)++Found[*F - Ints];// Sort. We should hit the first element just once and the final element N// times.llvm::sort(Found);for (auto F = std::begin(Found), E = std::end(Found); F != E; ++F)EXPECT_EQ(F - Found + 1, *F);}// Verify that const pointers work for count and find even when the underlying// SmallPtrSet is not for a const pointer type.TEST(SmallPtrSetTest, ConstTest) {SmallPtrSet<int *, 8> IntSet;int A;int *B = &A;const int *C = &A;IntSet.insert(B);EXPECT_EQ(IntSet.count(B), 1u);EXPECT_EQ(IntSet.count(C), 1u);EXPECT_TRUE(IntSet.contains(B));EXPECT_TRUE(IntSet.contains(C));}// Verify that we automatically get the const version of PointerLikeTypeTraits// filled in for us, even for a non-pointer typeusing TestPair = PointerIntPair<int *, 1>;TEST(SmallPtrSetTest, ConstNonPtrTest) {SmallPtrSet<TestPair, 8> IntSet;int A[1];TestPair Pair(&A[0], 1);IntSet.insert(Pair);EXPECT_EQ(IntSet.count(Pair), 1u);EXPECT_TRUE(IntSet.contains(Pair));}// Test equality comparison.TEST(SmallPtrSetTest, EqualityComparison) {int buf[3];for (int i = 0; i < 3; ++i)buf[i] = 0;SmallPtrSet<int *, 1> a;a.insert(&buf[0]);a.insert(&buf[1]);SmallPtrSet<int *, 2> b;b.insert(&buf[1]);b.insert(&buf[0]);SmallPtrSet<int *, 3> c;c.insert(&buf[1]);c.insert(&buf[2]);SmallPtrSet<int *, 4> d;d.insert(&buf[0]);SmallPtrSet<int *, 5> e;e.insert(&buf[0]);e.insert(&buf[1]);e.insert(&buf[2]);EXPECT_EQ(a, b);EXPECT_EQ(b, a);EXPECT_NE(b, c);EXPECT_NE(c, a);EXPECT_NE(d, a);EXPECT_NE(a, d);EXPECT_NE(a, e);EXPECT_NE(e, a);EXPECT_NE(c, e);EXPECT_NE(e, d);}TEST(SmallPtrSetTest, Contains) {SmallPtrSet<int *, 2> Set;int buf[4] = {0, 11, 22, 11};EXPECT_FALSE(Set.contains(&buf[0]));EXPECT_FALSE(Set.contains(&buf[1]));Set.insert(&buf[0]);Set.insert(&buf[1]);EXPECT_TRUE(Set.contains(&buf[0]));EXPECT_TRUE(Set.contains(&buf[1]));EXPECT_FALSE(Set.contains(&buf[3]));Set.insert(&buf[1]);EXPECT_TRUE(Set.contains(&buf[0]));EXPECT_TRUE(Set.contains(&buf[1]));EXPECT_FALSE(Set.contains(&buf[3]));Set.erase(&buf[1]);EXPECT_TRUE(Set.contains(&buf[0]));EXPECT_FALSE(Set.contains(&buf[1]));Set.insert(&buf[1]);Set.insert(&buf[2]);EXPECT_TRUE(Set.contains(&buf[0]));EXPECT_TRUE(Set.contains(&buf[1]));EXPECT_TRUE(Set.contains(&buf[2]));}TEST(SmallPtrSetTest, InsertIterator) {SmallPtrSet<int *, 5> Set;int Vals[5] = {11, 22, 33, 44, 55};int *Buf[5] = {&Vals[0], &Vals[1], &Vals[2], &Vals[3], &Vals[4]};for (int *Ptr : Buf)Set.insert(Set.begin(), Ptr);// Ensure that all of the values were copied into the set.for (const auto *Ptr : Buf)EXPECT_TRUE(Set.contains(Ptr));}
//===- unittests/ADT/SimpleIListTest.cpp - simple_ilist unit tests --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/simple_ilist.h"#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct Node : ilist_node<Node> {};bool operator<(const Node &L, const Node &R) { return &L < &R; }bool makeFalse(const Node &, const Node &) { return false; }struct deleteNode : std::default_delete<Node> {};void doNothing(Node *) {}TEST(SimpleIListTest, DefaultConstructor) {simple_ilist<Node> L;EXPECT_EQ(L.begin(), L.end());EXPECT_TRUE(L.empty());EXPECT_EQ(0u, L.size());}TEST(SimpleIListTest, pushPopFront) {simple_ilist<Node> L;Node A, B;L.push_front(B);L.push_front(A);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &L.back());EXPECT_FALSE(L.empty());EXPECT_EQ(2u, L.size());// Pop front and check the new front.L.pop_front();EXPECT_EQ(&B, &L.front());// Pop to empty.L.pop_front();EXPECT_TRUE(L.empty());}TEST(SimpleIListTest, pushPopBack) {simple_ilist<Node> L;Node A, B;L.push_back(A);L.push_back(B);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &L.back());EXPECT_FALSE(L.empty());EXPECT_EQ(2u, L.size());// Pop back and check the new front.L.pop_back();EXPECT_EQ(&A, &L.back());// Pop to empty.L.pop_back();EXPECT_TRUE(L.empty());}TEST(SimpleIListTest, swap) {simple_ilist<Node> L1, L2;Node A, B;L1.push_back(A);L1.push_back(B);L1.swap(L2);EXPECT_TRUE(L1.empty());EXPECT_EQ(0u, L1.size());EXPECT_EQ(&A, &L2.front());EXPECT_EQ(&B, &L2.back());EXPECT_FALSE(L2.empty());EXPECT_EQ(2u, L2.size());}TEST(SimpleIListTest, insertEraseAtEnd) {simple_ilist<Node> L;Node A, B;L.insert(L.end(), A);L.insert(L.end(), B);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &L.back());EXPECT_FALSE(L.empty());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, insertAtBegin) {simple_ilist<Node> L;Node A, B;L.insert(L.begin(), B);L.insert(L.begin(), A);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &L.back());EXPECT_FALSE(L.empty());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, remove) {simple_ilist<Node> L;Node A, B, C;L.push_back(A);L.push_back(B);L.push_back(C);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &*++L.begin());EXPECT_EQ(&C, &L.back());EXPECT_EQ(3u, L.size());L.remove(B);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&C, &L.back());EXPECT_EQ(2u, L.size());L.remove(A);EXPECT_EQ(&C, &L.front());EXPECT_EQ(1u, L.size());L.remove(C);EXPECT_TRUE(L.empty());}TEST(SimpleIListTest, removeAndDispose) {simple_ilist<Node> L;Node A, C;Node *B = new Node;L.push_back(A);L.push_back(*B);L.push_back(C);EXPECT_EQ(&A, &L.front());EXPECT_EQ(B, &*++L.begin());EXPECT_EQ(&C, &L.back());EXPECT_EQ(3u, L.size());L.removeAndDispose(*B, deleteNode());EXPECT_EQ(&A, &L.front());EXPECT_EQ(&C, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, removeAndDisposeNullDeleter) {simple_ilist<Node> L;Node A, B, C;L.push_back(A);L.push_back(B);L.push_back(C);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &*++L.begin());EXPECT_EQ(&C, &L.back());EXPECT_EQ(3u, L.size());L.removeAndDispose(B, doNothing);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&C, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, erase) {simple_ilist<Node> L;Node A, B, C;L.push_back(A);L.push_back(B);L.push_back(C);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &*++L.begin());EXPECT_EQ(&C, &L.back());EXPECT_EQ(3u, L.size());EXPECT_EQ(C.getIterator(), L.erase(B.getIterator()));EXPECT_EQ(&A, &L.front());EXPECT_EQ(&C, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, reverse_iterator) {simple_ilist<Node> L;Node A, B, C;L.push_back(A);L.push_back(B);L.push_back(C);auto ReverseIter = L.rbegin();EXPECT_EQ(C.getReverseIterator(), ReverseIter);++ReverseIter;EXPECT_EQ(B.getReverseIterator(), ReverseIter);++ReverseIter;EXPECT_EQ(A.getReverseIterator(), ReverseIter);++ReverseIter;EXPECT_EQ(L.rend(), ReverseIter);}TEST(SimpleIListTest, eraseAndDispose) {simple_ilist<Node> L;Node A, C;Node *B = new Node;L.push_back(A);L.push_back(*B);L.push_back(C);EXPECT_EQ(&A, &L.front());EXPECT_EQ(B, &*++L.begin());EXPECT_EQ(&C, &L.back());EXPECT_EQ(3u, L.size());L.eraseAndDispose(B->getIterator(), deleteNode());EXPECT_EQ(&A, &L.front());EXPECT_EQ(&C, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, eraseAndDisposeNullDeleter) {simple_ilist<Node> L;Node A, B, C;L.push_back(A);L.push_back(B);L.push_back(C);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&B, &*++L.begin());EXPECT_EQ(&C, &L.back());EXPECT_EQ(3u, L.size());L.eraseAndDispose(B.getIterator(), doNothing);EXPECT_EQ(&A, &L.front());EXPECT_EQ(&C, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, eraseRange) {simple_ilist<Node> L;Node A, B, C, D, E;L.push_back(A);L.push_back(B);L.push_back(C);L.push_back(D);L.push_back(E);auto I = L.begin();EXPECT_EQ(&A, &*I++);EXPECT_EQ(&B, &*I++);EXPECT_EQ(&C, &*I++);EXPECT_EQ(&D, &*I++);EXPECT_EQ(&E, &*I++);EXPECT_EQ(L.end(), I);EXPECT_EQ(5u, L.size());// Erase a range.EXPECT_EQ(E.getIterator(), L.erase(B.getIterator(), E.getIterator()));EXPECT_EQ(&A, &L.front());EXPECT_EQ(&E, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, eraseAndDisposeRange) {simple_ilist<Node> L;Node A, *B = new Node, *C = new Node, *D = new Node, E;L.push_back(A);L.push_back(*B);L.push_back(*C);L.push_back(*D);L.push_back(E);auto I = L.begin();EXPECT_EQ(&A, &*I++);EXPECT_EQ(B, &*I++);EXPECT_EQ(C, &*I++);EXPECT_EQ(D, &*I++);EXPECT_EQ(&E, &*I++);EXPECT_EQ(L.end(), I);EXPECT_EQ(5u, L.size());// Erase a range.EXPECT_EQ(E.getIterator(),L.eraseAndDispose(B->getIterator(), E.getIterator(), deleteNode()));EXPECT_EQ(&A, &L.front());EXPECT_EQ(&E, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, eraseAndDisposeRangeNullDeleter) {simple_ilist<Node> L;Node A, B, C, D, E;L.push_back(A);L.push_back(B);L.push_back(C);L.push_back(D);L.push_back(E);auto I = L.begin();EXPECT_EQ(&A, &*I++);EXPECT_EQ(&B, &*I++);EXPECT_EQ(&C, &*I++);EXPECT_EQ(&D, &*I++);EXPECT_EQ(&E, &*I++);EXPECT_EQ(L.end(), I);EXPECT_EQ(5u, L.size());// Erase a range.EXPECT_EQ(E.getIterator(),L.eraseAndDispose(B.getIterator(), E.getIterator(), doNothing));EXPECT_EQ(&A, &L.front());EXPECT_EQ(&E, &L.back());EXPECT_EQ(2u, L.size());}TEST(SimpleIListTest, clear) {simple_ilist<Node> L;Node A, B;L.push_back(A);L.push_back(B);L.clear();EXPECT_TRUE(L.empty());EXPECT_EQ(0u, L.size());}TEST(SimpleIListTest, clearAndDispose) {simple_ilist<Node> L;Node *A = new Node;Node *B = new Node;L.push_back(*A);L.push_back(*B);L.clearAndDispose(deleteNode());EXPECT_TRUE(L.empty());EXPECT_EQ(0u, L.size());}TEST(SimpleIListTest, clearAndDisposeNullDeleter) {simple_ilist<Node> L;Node A, B;L.push_back(A);L.push_back(B);L.clearAndDispose(doNothing);EXPECT_TRUE(L.empty());EXPECT_EQ(0u, L.size());}TEST(SimpleIListTest, spliceList) {simple_ilist<Node> L1, L2;Node A, B, C, D;// [A, D].L1.push_back(A);L1.push_back(D);// [B, C].L2.push_back(B);L2.push_back(C);// Splice in L2, giving [A, B, C, D].L1.splice(--L1.end(), L2);EXPECT_TRUE(L2.empty());EXPECT_EQ(4u, L1.size());auto I = L1.begin();EXPECT_EQ(&A, &*I++);EXPECT_EQ(&B, &*I++);EXPECT_EQ(&C, &*I++);EXPECT_EQ(&D, &*I++);EXPECT_EQ(L1.end(), I);}TEST(SimpleIListTest, spliceSingle) {simple_ilist<Node> L1, L2;Node A, B, C, D, E;// [A, C].L1.push_back(A);L1.push_back(C);// [D, B, E].L2.push_back(D);L2.push_back(B);L2.push_back(E);// Splice B from L2 to L1, giving [A, B, C] and [D, E].L1.splice(--L1.end(), L2, ++L2.begin());auto I = L1.begin();EXPECT_EQ(&A, &*I++);EXPECT_EQ(&B, &*I++);EXPECT_EQ(&C, &*I++);EXPECT_EQ(L1.end(), I);I = L2.begin();EXPECT_EQ(&D, &*I++);EXPECT_EQ(&E, &*I++);EXPECT_EQ(L2.end(), I);}TEST(SimpleIListTest, spliceRange) {simple_ilist<Node> L1, L2;Node A, B, C, D, E, F;// [A, D].L1.push_back(A);L1.push_back(D);// [E, B, C, F].L2.push_back(E);L2.push_back(B);L2.push_back(C);L2.push_back(F);// Splice B from L2 to L1, giving [A, B, C, D] and [E, F].L1.splice(--L1.end(), L2, ++L2.begin(), --L2.end());auto I = L1.begin();EXPECT_EQ(&A, &*I++);EXPECT_EQ(&B, &*I++);EXPECT_EQ(&C, &*I++);EXPECT_EQ(&D, &*I++);EXPECT_EQ(L1.end(), I);I = L2.begin();EXPECT_EQ(&E, &*I++);EXPECT_EQ(&F, &*I++);EXPECT_EQ(L2.end(), I);}TEST(SimpleIListTest, merge) {for (bool IsL1LHS : {false, true}) {simple_ilist<Node> L1, L2;Node Ns[10];// Fill L1.L1.push_back(Ns[0]);L1.push_back(Ns[3]);L1.push_back(Ns[4]);L1.push_back(Ns[8]);// Fill L2.L2.push_back(Ns[1]);L2.push_back(Ns[2]);L2.push_back(Ns[5]);L2.push_back(Ns[6]);L2.push_back(Ns[7]);L2.push_back(Ns[9]);// Check setup.EXPECT_EQ(4u, L1.size());EXPECT_EQ(6u, L2.size());EXPECT_TRUE(llvm::is_sorted(L1));EXPECT_TRUE(llvm::is_sorted(L2));// Merge.auto &LHS = IsL1LHS ? L1 : L2;auto &RHS = IsL1LHS ? L2 : L1;LHS.merge(RHS);EXPECT_TRUE(RHS.empty());EXPECT_FALSE(LHS.empty());EXPECT_TRUE(llvm::is_sorted(LHS));auto I = LHS.begin();for (Node &N : Ns)EXPECT_EQ(&N, &*I++);EXPECT_EQ(LHS.end(), I);}}TEST(SimpleIListTest, mergeIsStable) {simple_ilist<Node> L1, L2;Node Ns[5];auto setup = [&]() {EXPECT_TRUE(L1.empty());EXPECT_TRUE(L2.empty());// Fill L1.L1.push_back(Ns[0]);L1.push_back(Ns[3]);L1.push_back(Ns[4]);// Fill L2.L2.push_back(Ns[1]);L2.push_back(Ns[2]);// Check setup.EXPECT_EQ(3u, L1.size());EXPECT_EQ(2u, L2.size());EXPECT_TRUE(llvm::is_sorted(L1, makeFalse));EXPECT_TRUE(llvm::is_sorted(L2, makeFalse));};// Merge. Should be stable.setup();L1.merge(L2, makeFalse);EXPECT_TRUE(L2.empty());EXPECT_FALSE(L1.empty());EXPECT_TRUE(llvm::is_sorted(L1, makeFalse));auto I = L1.begin();EXPECT_EQ(&Ns[0], &*I++);EXPECT_EQ(&Ns[3], &*I++);EXPECT_EQ(&Ns[4], &*I++);EXPECT_EQ(&Ns[1], &*I++);EXPECT_EQ(&Ns[2], &*I++);EXPECT_EQ(L1.end(), I);// Merge the other way. Should be stable.L1.clear();setup();L2.merge(L1, makeFalse);EXPECT_TRUE(L1.empty());EXPECT_FALSE(L2.empty());EXPECT_TRUE(llvm::is_sorted(L2, makeFalse));I = L2.begin();EXPECT_EQ(&Ns[1], &*I++);EXPECT_EQ(&Ns[2], &*I++);EXPECT_EQ(&Ns[0], &*I++);EXPECT_EQ(&Ns[3], &*I++);EXPECT_EQ(&Ns[4], &*I++);EXPECT_EQ(L2.end(), I);}TEST(SimpleIListTest, mergeEmpty) {for (bool IsL1LHS : {false, true}) {simple_ilist<Node> L1, L2;Node Ns[4];// Fill L1.L1.push_back(Ns[0]);L1.push_back(Ns[1]);L1.push_back(Ns[2]);L1.push_back(Ns[3]);// Check setup.EXPECT_EQ(4u, L1.size());EXPECT_TRUE(L2.empty());EXPECT_TRUE(llvm::is_sorted(L1));// Merge.auto &LHS = IsL1LHS ? L1 : L2;auto &RHS = IsL1LHS ? L2 : L1;LHS.merge(RHS);EXPECT_TRUE(RHS.empty());EXPECT_FALSE(LHS.empty());EXPECT_TRUE(llvm::is_sorted(LHS));auto I = LHS.begin();for (Node &N : Ns)EXPECT_EQ(&N, &*I++);EXPECT_EQ(LHS.end(), I);}}TEST(SimpleIListTest, mergeBothEmpty) {simple_ilist<Node> L1, L2;L1.merge(L2);EXPECT_TRUE(L1.empty());EXPECT_TRUE(L2.empty());}TEST(SimpleIListTest, sort) {simple_ilist<Node> L;Node Ns[10];// Fill L.for (int I : {3, 4, 0, 8, 1, 2, 6, 7, 9, 5})L.push_back(Ns[I]);// Check setup.EXPECT_EQ(10u, L.size());EXPECT_FALSE(llvm::is_sorted(L));// Sort.L.sort();EXPECT_TRUE(llvm::is_sorted(L));auto I = L.begin();for (Node &N : Ns)EXPECT_EQ(&N, &*I++);EXPECT_EQ(L.end(), I);}TEST(SimpleIListTest, sortIsStable) {simple_ilist<Node> L;Node Ns[10];// Compare such that nodes are partitioned but not fully sorted.auto partition = [&](const Node &N) { return &N >= &Ns[5]; };auto compare = [&](const Node &L, const Node &R) {return partition(L) < partition(R);};// Fill L.for (int I : {3, 4, 7, 8, 1, 2, 6, 0, 9, 5})L.push_back(Ns[I]);// Check setup.EXPECT_EQ(10u, L.size());EXPECT_FALSE(llvm::is_sorted(L, compare));// Sort.L.sort(compare);EXPECT_TRUE(llvm::is_sorted(L, compare));auto I = L.begin();for (int O : {3, 4, 1, 2, 0})EXPECT_EQ(&Ns[O], &*I++);for (int O : {7, 8, 6, 9, 5})EXPECT_EQ(&Ns[O], &*I++);EXPECT_EQ(L.end(), I);}TEST(SimpleIListTest, sortEmpty) {simple_ilist<Node> L;L.sort();}struct Tag1 {};struct Tag2 {};struct DoubleNode : ilist_node<DoubleNode, ilist_tag<Tag1>>,ilist_node<DoubleNode, ilist_tag<Tag2>> {typedef ilist_node<DoubleNode, ilist_tag<Tag1>> Node1Type;typedef ilist_node<DoubleNode, ilist_tag<Tag2>> Node2Type;Node1Type::self_iterator getIterator1() { return Node1Type::getIterator(); }Node2Type::self_iterator getIterator2() { return Node2Type::getIterator(); }Node1Type::const_self_iterator getIterator1() const {return Node1Type::getIterator();}Node2Type::const_self_iterator getIterator2() const {return Node2Type::getIterator();}};typedef simple_ilist<DoubleNode, ilist_tag<Tag1>> TaggedList1Type;typedef simple_ilist<DoubleNode, ilist_tag<Tag2>> TaggedList2Type;TEST(SimpleIListTest, TaggedLists) {TaggedList1Type L1;TaggedList2Type L2;// Build the two lists, sharing a couple of nodes.DoubleNode Ns[10];int Order1[] = {0, 1, 2, 3, 4, 7, 9};int Order2[] = {2, 5, 6, 7, 8, 4, 9, 1};for (int I : Order1)L1.push_back(Ns[I]);for (int I : Order2)L2.push_back(Ns[I]);// Check that each list is correct.EXPECT_EQ(sizeof(Order1) / sizeof(int), L1.size());auto I1 = L1.begin();for (int I : Order1) {EXPECT_EQ(Ns[I].getIterator1(), I1);EXPECT_EQ(&Ns[I], &*I1++);}EXPECT_EQ(L1.end(), I1);EXPECT_EQ(sizeof(Order2) / sizeof(int), L2.size());auto I2 = L2.begin();for (int I : Order2) {EXPECT_EQ(Ns[I].getIterator2(), I2);EXPECT_EQ(&Ns[I], &*I2++);}EXPECT_EQ(L2.end(), I2);}} // end namespace
//===- llvm/unittest/ADT/SetVector.cpp ------------------------------===////// 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////===----------------------------------------------------------------------===////// SetVector unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/SetVector.h"#include "gtest/gtest.h"using namespace llvm;TEST(SetVector, EraseTest) {SetVector<int> S;S.insert(0);S.insert(1);S.insert(2);auto I = S.erase(std::next(S.begin()));// Test that the returned iterator is the expected one-after-erase// and the size/contents is the expected sequence {0, 2}.EXPECT_EQ(std::next(S.begin()), I);EXPECT_EQ(2u, S.size());EXPECT_EQ(0, *S.begin());EXPECT_EQ(2, *std::next(S.begin()));}TEST(SetVector, ContainsTest) {SetVector<int> S;S.insert(0);S.insert(1);S.insert(2);EXPECT_TRUE(S.contains(0));EXPECT_TRUE(S.contains(1));EXPECT_TRUE(S.contains(2));EXPECT_FALSE(S.contains(-1));S.insert(2);EXPECT_TRUE(S.contains(2));S.remove(2);EXPECT_FALSE(S.contains(2));}
//===- SequenceTest.cpp - Unit tests for a sequence abstraciton -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Sequence.h"#include "llvm/ADT/STLExtras.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <algorithm>#include <numeric>using namespace llvm;using testing::ElementsAre;using testing::IsEmpty;namespace {using detail::canTypeFitValue;using detail::CheckedInt;using IntegralTypes = testing::Types<uint8_t, // 0uint16_t, // 1uint32_t, // 2uint64_t, // 3uintmax_t, // 4int8_t, // 5int16_t, // 6int32_t, // 7int64_t, // 8intmax_t // 9>;template <class T> class StrongIntTest : public testing::Test {};TYPED_TEST_SUITE(StrongIntTest, IntegralTypes, );TYPED_TEST(StrongIntTest, Operations) {using T = TypeParam;auto Max = std::numeric_limits<T>::max();auto Min = std::numeric_limits<T>::min();// We bail out for types that are not entirely representable within intmax_t.if (!canTypeFitValue<intmax_t>(Max) || !canTypeFitValue<intmax_t>(Min))return;// All representable values convert back and forth.EXPECT_EQ(CheckedInt::from(Min).template to<T>(), Min);EXPECT_EQ(CheckedInt::from(Max).template to<T>(), Max);// Addition -2, -1, 0, 1, 2.const T Expected = Max / 2;const CheckedInt Actual = CheckedInt::from(Expected);EXPECT_EQ((Actual + -2).template to<T>(), Expected - 2);EXPECT_EQ((Actual + -1).template to<T>(), Expected - 1);EXPECT_EQ((Actual + 0).template to<T>(), Expected);EXPECT_EQ((Actual + 1).template to<T>(), Expected + 1);EXPECT_EQ((Actual + 2).template to<T>(), Expected + 2);// EQ/NEQEXPECT_EQ(Actual, Actual);EXPECT_NE(Actual, Actual + 1);// DifferenceEXPECT_EQ(Actual - Actual, 0);EXPECT_EQ((Actual + 1) - Actual, 1);EXPECT_EQ(Actual - (Actual + 2), -2);}#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)TEST(StrongIntDeathTest, OutOfBounds) {// Values above 'INTMAX_MAX' are not representable.EXPECT_DEATH(CheckedInt::from<uintmax_t>(INTMAX_MAX + 1ULL), "Out of bounds");EXPECT_DEATH(CheckedInt::from<uintmax_t>(UINTMAX_MAX), "Out of bounds");// Casting to narrower type asserts when out of bounds.EXPECT_DEATH(CheckedInt::from(-1).to<uint8_t>(), "Out of bounds");EXPECT_DEATH(CheckedInt::from(256).to<uint8_t>(), "Out of bounds");// Operations leading to intmax_t overflow assert.EXPECT_DEATH(CheckedInt::from(INTMAX_MAX) + 1, "Out of bounds");EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) + -1, "Out of bounds");EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) - CheckedInt::from(INTMAX_MAX),"Out of bounds");}#endifTEST(SafeIntIteratorTest, Operations) {detail::SafeIntIterator<int, false> Forward(0);detail::SafeIntIterator<int, true> Reverse(0);const auto SetToZero = [&]() {Forward = detail::SafeIntIterator<int, false>(0);Reverse = detail::SafeIntIterator<int, true>(0);};// Equality / ComparisonsSetToZero();EXPECT_EQ(Forward, Forward);EXPECT_LT(Forward - 1, Forward);EXPECT_LE(Forward, Forward);EXPECT_LE(Forward - 1, Forward);EXPECT_GT(Forward + 1, Forward);EXPECT_GE(Forward, Forward);EXPECT_GE(Forward + 1, Forward);EXPECT_EQ(Reverse, Reverse);EXPECT_LT(Reverse - 1, Reverse);EXPECT_LE(Reverse, Reverse);EXPECT_LE(Reverse - 1, Reverse);EXPECT_GT(Reverse + 1, Reverse);EXPECT_GE(Reverse, Reverse);EXPECT_GE(Reverse + 1, Reverse);// DereferenceSetToZero();EXPECT_EQ(*Forward, 0);EXPECT_EQ(*Reverse, 0);// IndexingSetToZero();EXPECT_EQ(Forward[2], 2);EXPECT_EQ(Reverse[2], -2);// Pre-incrementSetToZero();++Forward;EXPECT_EQ(*Forward, 1);++Reverse;EXPECT_EQ(*Reverse, -1);// Pre-decrementSetToZero();--Forward;EXPECT_EQ(*Forward, -1);--Reverse;EXPECT_EQ(*Reverse, 1);// Post-incrementSetToZero();EXPECT_EQ(*(Forward++), 0);EXPECT_EQ(*Forward, 1);EXPECT_EQ(*(Reverse++), 0);EXPECT_EQ(*Reverse, -1);// Post-decrementSetToZero();EXPECT_EQ(*(Forward--), 0);EXPECT_EQ(*Forward, -1);EXPECT_EQ(*(Reverse--), 0);EXPECT_EQ(*Reverse, 1);// Compound assignment operatorsSetToZero();Forward += 1;EXPECT_EQ(*Forward, 1);Reverse += 1;EXPECT_EQ(*Reverse, -1);SetToZero();Forward -= 2;EXPECT_EQ(*Forward, -2);Reverse -= 2;EXPECT_EQ(*Reverse, 2);// ArithmeticSetToZero();EXPECT_EQ(*(Forward + 3), 3);EXPECT_EQ(*(Reverse + 3), -3);SetToZero();EXPECT_EQ(*(Forward - 4), -4);EXPECT_EQ(*(Reverse - 4), 4);// DifferenceSetToZero();EXPECT_EQ(Forward - Forward, 0);EXPECT_EQ(Reverse - Reverse, 0);EXPECT_EQ((Forward + 1) - Forward, 1);EXPECT_EQ(Forward - (Forward + 1), -1);EXPECT_EQ((Reverse + 1) - Reverse, 1);EXPECT_EQ(Reverse - (Reverse + 1), -1);}TEST(SequenceTest, Iteration) {EXPECT_THAT(seq(-4, 5), ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4));EXPECT_THAT(reverse(seq(-4, 5)), ElementsAre(4, 3, 2, 1, 0, -1, -2, -3, -4));EXPECT_THAT(seq_inclusive(-4, 5),ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4, 5));EXPECT_THAT(reverse(seq_inclusive(-4, 5)),ElementsAre(5, 4, 3, 2, 1, 0, -1, -2, -3, -4));}TEST(SequenceTest, Distance) {const auto Forward = seq(0, 10);EXPECT_EQ(std::distance(Forward.begin(), Forward.end()), 10);EXPECT_EQ(std::distance(Forward.rbegin(), Forward.rend()), 10);}TEST(SequenceTest, Dereference) {const auto Forward = seq(0, 10).begin();EXPECT_EQ(Forward[0], 0);EXPECT_EQ(Forward[2], 2);const auto Backward = seq(0, 10).rbegin();EXPECT_EQ(Backward[0], 9);EXPECT_EQ(Backward[2], 7);}enum UntypedEnum { A = 3 };enum TypedEnum : uint32_t { B = 3 };namespace X {enum class ScopedEnum : uint16_t { C = 3 };} // namespace Xstruct S {enum NestedEnum { D = 4 };enum NestedEnum2 { E = 5 };private:enum NestedEnum3 { F = 6 };friend struct llvm::enum_iteration_traits<NestedEnum3>;public:static auto getNestedEnum3() { return NestedEnum3::F; }};} // namespacenamespace llvm {template <> struct enum_iteration_traits<UntypedEnum> {static constexpr bool is_iterable = true;};template <> struct enum_iteration_traits<TypedEnum> {static constexpr bool is_iterable = true;};template <> struct enum_iteration_traits<X::ScopedEnum> {static constexpr bool is_iterable = true;};template <> struct enum_iteration_traits<S::NestedEnum> {static constexpr bool is_iterable = true;};template <> struct enum_iteration_traits<S::NestedEnum3> {static constexpr bool is_iterable = true;};} // namespace llvmnamespace {TEST(StrongIntTest, Enums) {EXPECT_EQ(CheckedInt::from(A).to<UntypedEnum>(), A);EXPECT_EQ(CheckedInt::from(B).to<TypedEnum>(), B);EXPECT_EQ(CheckedInt::from(X::ScopedEnum::C).to<X::ScopedEnum>(),X::ScopedEnum::C);}TEST(SequenceTest, IterableEnums) {EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A), IsEmpty());EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A),ElementsAre(UntypedEnum::A));EXPECT_THAT(enum_seq(TypedEnum::B, TypedEnum::B), IsEmpty());EXPECT_THAT(enum_seq_inclusive(TypedEnum::B, TypedEnum::B),ElementsAre(TypedEnum::B));EXPECT_THAT(enum_seq(X::ScopedEnum::C, X::ScopedEnum::C), IsEmpty());EXPECT_THAT(enum_seq_inclusive(X::ScopedEnum::C, X::ScopedEnum::C),ElementsAre(X::ScopedEnum::C));EXPECT_THAT(enum_seq_inclusive(S::NestedEnum::D, S::NestedEnum::D),ElementsAre(S::NestedEnum::D));EXPECT_THAT(enum_seq_inclusive(S::getNestedEnum3(), S::getNestedEnum3()),ElementsAre(S::getNestedEnum3()));}TEST(SequenceTest, NonIterableEnums) {EXPECT_THAT(enum_seq(S::NestedEnum2::E, S::NestedEnum2::E,force_iteration_on_noniterable_enum),IsEmpty());EXPECT_THAT(enum_seq_inclusive(S::NestedEnum2::E, S::NestedEnum2::E,force_iteration_on_noniterable_enum),ElementsAre(S::NestedEnum2::E));// Check that this also works with enums marked as iterable.EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A,force_iteration_on_noniterable_enum),IsEmpty());EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A,force_iteration_on_noniterable_enum),ElementsAre(UntypedEnum::A));}} // namespace
//===- llvm/unittest/ADT/ScopeExit.cpp - Scope exit unit tests --*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ScopeExit.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ScopeExitTest, Basic) {struct Callable {bool &Called;Callable(bool &Called) : Called(Called) {}Callable(Callable &&RHS) : Called(RHS.Called) {}void operator()() { Called = true; }};bool Called = false;{auto g = make_scope_exit(Callable(Called));EXPECT_FALSE(Called);}EXPECT_TRUE(Called);}TEST(ScopeExitTest, Release) {int Count = 0;auto Increment = [&] { ++Count; };{auto G = make_scope_exit(Increment);auto H = std::move(G);auto I = std::move(G);EXPECT_EQ(0, Count);}EXPECT_EQ(1, Count);{auto G = make_scope_exit(Increment);G.release();}EXPECT_EQ(1, Count);}} // end anonymous namespace
//===- STLForwardCompatTest.cpp - Unit tests for STLForwardCompat ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLForwardCompat.h"#include "gtest/gtest.h"namespace {TEST(STLForwardCompatTest, NegationTest) {EXPECT_TRUE((llvm::negation<std::false_type>::value));EXPECT_FALSE((llvm::negation<std::true_type>::value));}struct incomplete_type;TEST(STLForwardCompatTest, ConjunctionTest) {EXPECT_TRUE((llvm::conjunction<>::value));EXPECT_FALSE((llvm::conjunction<std::false_type>::value));EXPECT_TRUE((llvm::conjunction<std::true_type>::value));EXPECT_FALSE((llvm::conjunction<std::false_type, incomplete_type>::value));EXPECT_FALSE((llvm::conjunction<std::false_type, std::true_type>::value));EXPECT_FALSE((llvm::conjunction<std::true_type, std::false_type>::value));EXPECT_TRUE((llvm::conjunction<std::true_type, std::true_type>::value));EXPECT_TRUE((llvm::conjunction<std::true_type, std::true_type,std::true_type>::value));}TEST(STLForwardCompatTest, DisjunctionTest) {EXPECT_FALSE((llvm::disjunction<>::value));EXPECT_FALSE((llvm::disjunction<std::false_type>::value));EXPECT_TRUE((llvm::disjunction<std::true_type>::value));EXPECT_TRUE((llvm::disjunction<std::true_type, incomplete_type>::value));EXPECT_TRUE((llvm::disjunction<std::false_type, std::true_type>::value));EXPECT_TRUE((llvm::disjunction<std::true_type, std::false_type>::value));EXPECT_TRUE((llvm::disjunction<std::true_type, std::true_type>::value));EXPECT_TRUE((llvm::disjunction<std::true_type, std::true_type,std::true_type>::value));}template <typename T>class STLForwardCompatRemoveCVRefTest : public ::testing::Test {};using STLForwardCompatRemoveCVRefTestTypes = ::testing::Types<// clang-format offstd::pair<int, int>,std::pair<int &, int>,std::pair<const int, int>,std::pair<volatile int, int>,std::pair<const volatile int &, int>,std::pair<int *, int *>,std::pair<int *const, int *>,std::pair<const int *, const int *>,std::pair<int *&, int *>// clang-format on>;TYPED_TEST_SUITE(STLForwardCompatRemoveCVRefTest,STLForwardCompatRemoveCVRefTestTypes, );TYPED_TEST(STLForwardCompatRemoveCVRefTest, RemoveCVRef) {using From = typename TypeParam::first_type;using To = typename TypeParam::second_type;EXPECT_TRUE((std::is_same<typename llvm::remove_cvref<From>::type, To>::value));}TYPED_TEST(STLForwardCompatRemoveCVRefTest, RemoveCVRefT) {using From = typename TypeParam::first_type;EXPECT_TRUE((std::is_same<typename llvm::remove_cvref<From>::type,llvm::remove_cvref_t<From>>::value));}} // namespace
//===- STLExtrasTest.cpp - Unit tests for STL extras ----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"#include <climits>#include <list>#include <vector>using namespace llvm;namespace {int f(rank<0>) { return 0; }int f(rank<1>) { return 1; }int f(rank<2>) { return 2; }int f(rank<4>) { return 4; }TEST(STLExtrasTest, Rank) {// We shouldn't get ambiguities and should select the overload of the same// rank as the argument.EXPECT_EQ(0, f(rank<0>()));EXPECT_EQ(1, f(rank<1>()));EXPECT_EQ(2, f(rank<2>()));// This overload is missing so we end up back at 2.EXPECT_EQ(2, f(rank<3>()));// But going past 3 should work fine.EXPECT_EQ(4, f(rank<4>()));// And we can even go higher and just fall back to the last overload.EXPECT_EQ(4, f(rank<5>()));EXPECT_EQ(4, f(rank<6>()));}TEST(STLExtrasTest, EnumerateLValue) {// Test that a simple LValue can be enumerated and gives correct results with// multiple types, including the empty container.std::vector<char> foo = {'a', 'b', 'c'};typedef std::pair<std::size_t, char> CharPairType;std::vector<CharPairType> CharResults;for (auto X : llvm::enumerate(foo)) {CharResults.emplace_back(X.index(), X.value());}ASSERT_EQ(3u, CharResults.size());EXPECT_EQ(CharPairType(0u, 'a'), CharResults[0]);EXPECT_EQ(CharPairType(1u, 'b'), CharResults[1]);EXPECT_EQ(CharPairType(2u, 'c'), CharResults[2]);// Test a const range of a different type.typedef std::pair<std::size_t, int> IntPairType;std::vector<IntPairType> IntResults;const std::vector<int> bar = {1, 2, 3};for (auto X : llvm::enumerate(bar)) {IntResults.emplace_back(X.index(), X.value());}ASSERT_EQ(3u, IntResults.size());EXPECT_EQ(IntPairType(0u, 1), IntResults[0]);EXPECT_EQ(IntPairType(1u, 2), IntResults[1]);EXPECT_EQ(IntPairType(2u, 3), IntResults[2]);// Test an empty range.IntResults.clear();const std::vector<int> baz{};for (auto X : llvm::enumerate(baz)) {IntResults.emplace_back(X.index(), X.value());}EXPECT_TRUE(IntResults.empty());}TEST(STLExtrasTest, EnumerateModifyLValue) {// Test that you can modify the underlying entries of an lvalue range through// the enumeration iterator.std::vector<char> foo = {'a', 'b', 'c'};for (auto X : llvm::enumerate(foo)) {++X.value();}EXPECT_EQ('b', foo[0]);EXPECT_EQ('c', foo[1]);EXPECT_EQ('d', foo[2]);}TEST(STLExtrasTest, EnumerateRValueRef) {// Test that an rvalue can be enumerated.typedef std::pair<std::size_t, int> PairType;std::vector<PairType> Results;auto Enumerator = llvm::enumerate(std::vector<int>{1, 2, 3});for (auto X : llvm::enumerate(std::vector<int>{1, 2, 3})) {Results.emplace_back(X.index(), X.value());}ASSERT_EQ(3u, Results.size());EXPECT_EQ(PairType(0u, 1), Results[0]);EXPECT_EQ(PairType(1u, 2), Results[1]);EXPECT_EQ(PairType(2u, 3), Results[2]);}TEST(STLExtrasTest, EnumerateModifyRValue) {// Test that when enumerating an rvalue, modification still works (even if// this isn't terribly useful, it at least shows that we haven't snuck an// extra const in there somewhere.typedef std::pair<std::size_t, char> PairType;std::vector<PairType> Results;for (auto X : llvm::enumerate(std::vector<char>{'1', '2', '3'})) {++X.value();Results.emplace_back(X.index(), X.value());}ASSERT_EQ(3u, Results.size());EXPECT_EQ(PairType(0u, '2'), Results[0]);EXPECT_EQ(PairType(1u, '3'), Results[1]);EXPECT_EQ(PairType(2u, '4'), Results[2]);}template <bool B> struct CanMove {};template <> struct CanMove<false> {CanMove(CanMove &&) = delete;CanMove() = default;CanMove(const CanMove &) = default;};template <bool B> struct CanCopy {};template <> struct CanCopy<false> {CanCopy(const CanCopy &) = delete;CanCopy() = default;CanCopy(CanCopy &&) = default;};template <bool Moveable, bool Copyable>class Counted : CanMove<Moveable>, CanCopy<Copyable> {int &C;int &M;int &D;public:explicit Counted(int &C, int &M, int &D) : C(C), M(M), D(D) {}Counted(const Counted &O) : CanCopy<Copyable>(O), C(O.C), M(O.M), D(O.D) {++C;}Counted(Counted &&O): CanMove<Moveable>(std::move(O)), C(O.C), M(O.M), D(O.D) {++M;}~Counted() { ++D; }};template <bool Moveable, bool Copyable>struct Range : Counted<Moveable, Copyable> {using Counted<Moveable, Copyable>::Counted;int *begin() { return nullptr; }int *end() { return nullptr; }};TEST(STLExtrasTest, EnumerateLifetimeSemanticsPRValue) {int Copies = 0;int Moves = 0;int Destructors = 0;{auto E = enumerate(Range<true, false>(Copies, Moves, Destructors));(void)E;// Doesn't compile. rvalue ranges must be moveable.// auto E2 = enumerate(Range<false, true>(Copies, Moves, Destructors));EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(1, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(2, Destructors);}TEST(STLExtrasTest, EnumerateLifetimeSemanticsRValue) {// With an rvalue, it should not be destroyed until the end of the scope.int Copies = 0;int Moves = 0;int Destructors = 0;{Range<true, false> R(Copies, Moves, Destructors);{auto E = enumerate(std::move(R));(void)E;// Doesn't compile. rvalue ranges must be moveable.// auto E2 = enumerate(Range<false, true>(Copies, Moves, Destructors));EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(0, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(1, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(2, Destructors);}TEST(STLExtrasTest, EnumerateLifetimeSemanticsLValue) {// With an lvalue, it should not be destroyed even after the end of the scope.// lvalue ranges need be neither copyable nor moveable.int Copies = 0;int Moves = 0;int Destructors = 0;{Range<false, false> R(Copies, Moves, Destructors);{auto E = enumerate(R);(void)E;EXPECT_EQ(0, Copies);EXPECT_EQ(0, Moves);EXPECT_EQ(0, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(0, Moves);EXPECT_EQ(0, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(0, Moves);EXPECT_EQ(1, Destructors);}TEST(STLExtrasTest, ApplyTuple) {auto T = std::make_tuple(1, 3, 7);auto U = llvm::apply_tuple([](int A, int B, int C) { return std::make_tuple(A - B, B - C, C - A); },T);EXPECT_EQ(-2, std::get<0>(U));EXPECT_EQ(-4, std::get<1>(U));EXPECT_EQ(6, std::get<2>(U));auto V = llvm::apply_tuple([](int A, int B, int C) {return std::make_tuple(std::make_pair(A, char('A' + A)),std::make_pair(B, char('A' + B)),std::make_pair(C, char('A' + C)));},T);EXPECT_EQ(std::make_pair(1, 'B'), std::get<0>(V));EXPECT_EQ(std::make_pair(3, 'D'), std::get<1>(V));EXPECT_EQ(std::make_pair(7, 'H'), std::get<2>(V));}class apply_variadic {static int apply_one(int X) { return X + 1; }static char apply_one(char C) { return C + 1; }static StringRef apply_one(StringRef S) { return S.drop_back(); }public:template <typename... Ts> auto operator()(Ts &&... Items) {return std::make_tuple(apply_one(Items)...);}};TEST(STLExtrasTest, ApplyTupleVariadic) {auto Items = std::make_tuple(1, llvm::StringRef("Test"), 'X');auto Values = apply_tuple(apply_variadic(), Items);EXPECT_EQ(2, std::get<0>(Values));EXPECT_EQ("Tes", std::get<1>(Values));EXPECT_EQ('Y', std::get<2>(Values));}TEST(STLExtrasTest, CountAdaptor) {std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(1);v.push_back(4);v.push_back(3);v.push_back(2);v.push_back(1);EXPECT_EQ(3, count(v, 1));EXPECT_EQ(2, count(v, 2));EXPECT_EQ(1, count(v, 3));EXPECT_EQ(1, count(v, 4));}TEST(STLExtrasTest, for_each) {std::vector<int> v{0, 1, 2, 3, 4};int count = 0;llvm::for_each(v, [&count](int) { ++count; });EXPECT_EQ(5, count);}TEST(STLExtrasTest, ToVector) {std::vector<char> v = {'a', 'b', 'c'};auto Enumerated = to_vector<4>(enumerate(v));ASSERT_EQ(3u, Enumerated.size());for (size_t I = 0; I < v.size(); ++I) {EXPECT_EQ(I, Enumerated[I].index());EXPECT_EQ(v[I], Enumerated[I].value());}auto EnumeratedImplicitSize = to_vector(enumerate(v));ASSERT_EQ(3u, EnumeratedImplicitSize.size());for (size_t I = 0; I < v.size(); ++I) {EXPECT_EQ(I, EnumeratedImplicitSize[I].index());EXPECT_EQ(v[I], EnumeratedImplicitSize[I].value());}}TEST(STLExtrasTest, ConcatRange) {std::vector<int> Expected = {1, 2, 3, 4, 5, 6, 7, 8};std::vector<int> Test;std::vector<int> V1234 = {1, 2, 3, 4};std::list<int> L56 = {5, 6};SmallVector<int, 2> SV78 = {7, 8};// Use concat across different sized ranges of different types with different// iterators.for (int &i : concat<int>(V1234, L56, SV78))Test.push_back(i);EXPECT_EQ(Expected, Test);// Use concat between a temporary, an L-value, and an R-value to make sure// complex lifetimes work well.Test.clear();for (int &i : concat<int>(std::vector<int>(V1234), L56, std::move(SV78)))Test.push_back(i);EXPECT_EQ(Expected, Test);}TEST(STLExtrasTest, PartitionAdaptor) {std::vector<int> V = {1, 2, 3, 4, 5, 6, 7, 8};auto I = partition(V, [](int i) { return i % 2 == 0; });ASSERT_EQ(V.begin() + 4, I);// Sort the two halves as partition may have messed with the order.llvm::sort(V.begin(), I);llvm::sort(I, V.end());EXPECT_EQ(2, V[0]);EXPECT_EQ(4, V[1]);EXPECT_EQ(6, V[2]);EXPECT_EQ(8, V[3]);EXPECT_EQ(1, V[4]);EXPECT_EQ(3, V[5]);EXPECT_EQ(5, V[6]);EXPECT_EQ(7, V[7]);}TEST(STLExtrasTest, EraseIf) {std::vector<int> V = {1, 2, 3, 4, 5, 6, 7, 8};erase_if(V, [](int i) { return i % 2 == 0; });EXPECT_EQ(4u, V.size());EXPECT_EQ(1, V[0]);EXPECT_EQ(3, V[1]);EXPECT_EQ(5, V[2]);EXPECT_EQ(7, V[3]);}TEST(STLExtrasTest, AppendRange) {auto AppendVals = {3};std::vector<int> V = {1, 2};append_range(V, AppendVals);EXPECT_EQ(1, V[0]);EXPECT_EQ(2, V[1]);EXPECT_EQ(3, V[2]);}namespace some_namespace {struct some_struct {std::vector<int> data;std::string swap_val;};std::vector<int>::const_iterator begin(const some_struct &s) {return s.data.begin();}std::vector<int>::const_iterator end(const some_struct &s) {return s.data.end();}void swap(some_struct &lhs, some_struct &rhs) {// make swap visible as non-adl swap would even seem to// work with std::swap which defaults to movinglhs.swap_val = "lhs";rhs.swap_val = "rhs";}} // namespace some_namespaceTEST(STLExtrasTest, ADLTest) {some_namespace::some_struct s{{1, 2, 3, 4, 5}, ""};some_namespace::some_struct s2{{2, 4, 6, 8, 10}, ""};EXPECT_EQ(*adl_begin(s), 1);EXPECT_EQ(*(adl_end(s) - 1), 5);adl_swap(s, s2);EXPECT_EQ(s.swap_val, "lhs");EXPECT_EQ(s2.swap_val, "rhs");int count = 0;llvm::for_each(s, [&count](int) { ++count; });EXPECT_EQ(5, count);}TEST(STLExtrasTest, EmptyTest) {std::vector<void*> V;EXPECT_TRUE(llvm::empty(V));V.push_back(nullptr);EXPECT_FALSE(llvm::empty(V));std::initializer_list<int> E = {};std::initializer_list<int> NotE = {7, 13, 42};EXPECT_TRUE(llvm::empty(E));EXPECT_FALSE(llvm::empty(NotE));auto R0 = make_range(V.begin(), V.begin());EXPECT_TRUE(llvm::empty(R0));auto R1 = make_range(V.begin(), V.end());EXPECT_FALSE(llvm::empty(R1));}TEST(STLExtrasTest, DropBeginTest) {SmallVector<int, 5> vec{0, 1, 2, 3, 4};for (int n = 0; n < 5; ++n) {int i = n;for (auto &v : drop_begin(vec, n)) {EXPECT_EQ(v, i);i += 1;}EXPECT_EQ(i, 5);}}TEST(STLExtrasTest, DropBeginDefaultTest) {SmallVector<int, 5> vec{0, 1, 2, 3, 4};int i = 1;for (auto &v : drop_begin(vec)) {EXPECT_EQ(v, i);i += 1;}EXPECT_EQ(i, 5);}TEST(STLExtrasTest, DropEndTest) {SmallVector<int, 5> vec{0, 1, 2, 3, 4};for (int n = 0; n < 5; ++n) {int i = 0;for (auto &v : drop_end(vec, n)) {EXPECT_EQ(v, i);i += 1;}EXPECT_EQ(i, 5 - n);}}TEST(STLExtrasTest, DropEndDefaultTest) {SmallVector<int, 5> vec{0, 1, 2, 3, 4};int i = 0;for (auto &v : drop_end(vec)) {EXPECT_EQ(v, i);i += 1;}EXPECT_EQ(i, 4);}TEST(STLExtrasTest, EarlyIncrementTest) {std::list<int> L = {1, 2, 3, 4};auto EIR = make_early_inc_range(L);auto I = EIR.begin();auto EI = EIR.end();EXPECT_NE(I, EI);EXPECT_EQ(1, *I);#if LLVM_ENABLE_ABI_BREAKING_CHECKS#ifndef NDEBUG// Repeated dereferences are not allowed.EXPECT_DEATH(*I, "Cannot dereference");// Comparison after dereference is not allowed.EXPECT_DEATH((void)(I == EI), "Cannot compare");EXPECT_DEATH((void)(I != EI), "Cannot compare");#endif#endif++I;EXPECT_NE(I, EI);#if LLVM_ENABLE_ABI_BREAKING_CHECKS#ifndef NDEBUG// You cannot increment prior to dereference.EXPECT_DEATH(++I, "Cannot increment");#endif#endifEXPECT_EQ(2, *I);#if LLVM_ENABLE_ABI_BREAKING_CHECKS#ifndef NDEBUG// Repeated dereferences are not allowed.EXPECT_DEATH(*I, "Cannot dereference");#endif#endif// Inserting shouldn't break anything. We should be able to keep dereferencing// the currrent iterator and increment. The increment to go to the "next"// iterator from before we inserted.L.insert(std::next(L.begin(), 2), -1);++I;EXPECT_EQ(3, *I);// Erasing the front including the current doesn't break incrementing.L.erase(L.begin(), std::prev(L.end()));++I;EXPECT_EQ(4, *I);++I;EXPECT_EQ(EIR.end(), I);}// A custom iterator that returns a pointer when dereferenced. This is used to// test make_early_inc_range with iterators that do not return a reference on// dereferencing.struct CustomPointerIterator: public iterator_adaptor_base<CustomPointerIterator,std::list<int>::iterator,std::forward_iterator_tag> {using base_type =iterator_adaptor_base<CustomPointerIterator, std::list<int>::iterator,std::forward_iterator_tag>;explicit CustomPointerIterator(std::list<int>::iterator I) : base_type(I) {}// Retrieve a pointer to the current int.int *operator*() const { return &*base_type::wrapped(); }};// Make sure make_early_inc_range works with iterators that do not return a// reference on dereferencing. The test is similar to EarlyIncrementTest, but// uses CustomPointerIterator.TEST(STLExtrasTest, EarlyIncrementTestCustomPointerIterator) {std::list<int> L = {1, 2, 3, 4};auto CustomRange = make_range(CustomPointerIterator(L.begin()),CustomPointerIterator(L.end()));auto EIR = make_early_inc_range(CustomRange);auto I = EIR.begin();auto EI = EIR.end();EXPECT_NE(I, EI);EXPECT_EQ(&*L.begin(), *I);#if LLVM_ENABLE_ABI_BREAKING_CHECKS#ifndef NDEBUG// Repeated dereferences are not allowed.EXPECT_DEATH(*I, "Cannot dereference");// Comparison after dereference is not allowed.EXPECT_DEATH((void)(I == EI), "Cannot compare");EXPECT_DEATH((void)(I != EI), "Cannot compare");#endif#endif++I;EXPECT_NE(I, EI);#if LLVM_ENABLE_ABI_BREAKING_CHECKS#ifndef NDEBUG// You cannot increment prior to dereference.EXPECT_DEATH(++I, "Cannot increment");#endif#endifEXPECT_EQ(&*std::next(L.begin()), *I);#if LLVM_ENABLE_ABI_BREAKING_CHECKS#ifndef NDEBUG// Repeated dereferences are not allowed.EXPECT_DEATH(*I, "Cannot dereference");#endif#endif// Inserting shouldn't break anything. We should be able to keep dereferencing// the currrent iterator and increment. The increment to go to the "next"// iterator from before we inserted.L.insert(std::next(L.begin(), 2), -1);++I;EXPECT_EQ(&*std::next(L.begin(), 3), *I);// Erasing the front including the current doesn't break incrementing.L.erase(L.begin(), std::prev(L.end()));++I;EXPECT_EQ(&*L.begin(), *I);++I;EXPECT_EQ(EIR.end(), I);}TEST(STLExtrasTest, splat) {std::vector<int> V;EXPECT_FALSE(is_splat(V));V.push_back(1);EXPECT_TRUE(is_splat(V));V.push_back(1);V.push_back(1);EXPECT_TRUE(is_splat(V));V.push_back(2);EXPECT_FALSE(is_splat(V));}TEST(STLExtrasTest, to_address) {int *V1 = new int;EXPECT_EQ(V1, to_address(V1));// Check fancy pointer overload for unique_ptrstd::unique_ptr<int> V2 = std::make_unique<int>(0);EXPECT_EQ(V2.get(), llvm::to_address(V2));V2.reset(V1);EXPECT_EQ(V1, llvm::to_address(V2));V2.release();// Check fancy pointer overload for shared_ptrstd::shared_ptr<int> V3 = std::make_shared<int>(0);std::shared_ptr<int> V4 = V3;EXPECT_EQ(V3.get(), V4.get());EXPECT_EQ(V3.get(), llvm::to_address(V3));EXPECT_EQ(V4.get(), llvm::to_address(V4));V3.reset(V1);EXPECT_EQ(V1, llvm::to_address(V3));}TEST(STLExtrasTest, partition_point) {std::vector<int> V = {1, 3, 5, 7, 9};// Range version.EXPECT_EQ(V.begin() + 3,partition_point(V, [](unsigned X) { return X < 7; }));EXPECT_EQ(V.begin(), partition_point(V, [](unsigned X) { return X < 1; }));EXPECT_EQ(V.end(), partition_point(V, [](unsigned X) { return X < 50; }));}TEST(STLExtrasTest, hasSingleElement) {const std::vector<int> V0 = {}, V1 = {1}, V2 = {1, 2};const std::vector<int> V10(10);EXPECT_EQ(hasSingleElement(V0), false);EXPECT_EQ(hasSingleElement(V1), true);EXPECT_EQ(hasSingleElement(V2), false);EXPECT_EQ(hasSingleElement(V10), false);}TEST(STLExtrasTest, hasNItems) {const std::list<int> V0 = {}, V1 = {1}, V2 = {1, 2};const std::list<int> V3 = {1, 3, 5};EXPECT_TRUE(hasNItems(V0, 0));EXPECT_FALSE(hasNItems(V0, 2));EXPECT_TRUE(hasNItems(V1, 1));EXPECT_FALSE(hasNItems(V1, 2));EXPECT_TRUE(hasNItems(V3.begin(), V3.end(), 3, [](int x) { return x < 10; }));EXPECT_TRUE(hasNItems(V3.begin(), V3.end(), 0, [](int x) { return x > 10; }));EXPECT_TRUE(hasNItems(V3.begin(), V3.end(), 2, [](int x) { return x < 5; }));}TEST(STLExtras, hasNItemsOrMore) {const std::list<int> V0 = {}, V1 = {1}, V2 = {1, 2};const std::list<int> V3 = {1, 3, 5};EXPECT_TRUE(hasNItemsOrMore(V1, 1));EXPECT_FALSE(hasNItemsOrMore(V1, 2));EXPECT_TRUE(hasNItemsOrMore(V2, 1));EXPECT_TRUE(hasNItemsOrMore(V2, 2));EXPECT_FALSE(hasNItemsOrMore(V2, 3));EXPECT_TRUE(hasNItemsOrMore(V3, 3));EXPECT_FALSE(hasNItemsOrMore(V3, 4));EXPECT_TRUE(hasNItemsOrMore(V3.begin(), V3.end(), 3, [](int x) { return x < 10; }));EXPECT_FALSE(hasNItemsOrMore(V3.begin(), V3.end(), 3, [](int x) { return x > 10; }));EXPECT_TRUE(hasNItemsOrMore(V3.begin(), V3.end(), 2, [](int x) { return x < 5; }));}TEST(STLExtras, hasNItemsOrLess) {const std::list<int> V0 = {}, V1 = {1}, V2 = {1, 2};const std::list<int> V3 = {1, 3, 5};EXPECT_TRUE(hasNItemsOrLess(V0, 0));EXPECT_TRUE(hasNItemsOrLess(V0, 1));EXPECT_TRUE(hasNItemsOrLess(V0, 2));EXPECT_FALSE(hasNItemsOrLess(V1, 0));EXPECT_TRUE(hasNItemsOrLess(V1, 1));EXPECT_TRUE(hasNItemsOrLess(V1, 2));EXPECT_FALSE(hasNItemsOrLess(V2, 0));EXPECT_FALSE(hasNItemsOrLess(V2, 1));EXPECT_TRUE(hasNItemsOrLess(V2, 2));EXPECT_TRUE(hasNItemsOrLess(V2, 3));EXPECT_FALSE(hasNItemsOrLess(V3, 0));EXPECT_FALSE(hasNItemsOrLess(V3, 1));EXPECT_FALSE(hasNItemsOrLess(V3, 2));EXPECT_TRUE(hasNItemsOrLess(V3, 3));EXPECT_TRUE(hasNItemsOrLess(V3, 4));EXPECT_TRUE(hasNItemsOrLess(V3.begin(), V3.end(), 1, [](int x) { return x == 1; }));EXPECT_TRUE(hasNItemsOrLess(V3.begin(), V3.end(), 2, [](int x) { return x < 5; }));EXPECT_TRUE(hasNItemsOrLess(V3.begin(), V3.end(), 5, [](int x) { return x < 5; }));EXPECT_FALSE(hasNItemsOrLess(V3.begin(), V3.end(), 2, [](int x) { return x < 10; }));}TEST(STLExtras, MoveRange) {class Foo {bool A;public:Foo() : A(true) {}Foo(const Foo &) = delete;Foo(Foo &&Other) : A(Other.A) { Other.A = false; }Foo &operator=(const Foo &) = delete;Foo &operator=(Foo &&Other) {if (this != &Other) {A = Other.A;Other.A = false;}return *this;}operator bool() const { return A; }};SmallVector<Foo, 4U> V1, V2, V3, V4;auto HasVal = [](const Foo &Item) { return static_cast<bool>(Item); };auto Build = [&] {SmallVector<Foo, 4U> Foos;Foos.resize(4U);return Foos;};V1.resize(4U);EXPECT_TRUE(llvm::all_of(V1, HasVal));llvm::move(V1, std::back_inserter(V2));// Ensure input container is same size, but its contents were moved out.EXPECT_EQ(V1.size(), 4U);EXPECT_TRUE(llvm::none_of(V1, HasVal));// Ensure output container has the contents of the input container.EXPECT_EQ(V2.size(), 4U);EXPECT_TRUE(llvm::all_of(V2, HasVal));llvm::move(std::move(V2), std::back_inserter(V3));EXPECT_TRUE(llvm::none_of(V2, HasVal));EXPECT_EQ(V3.size(), 4U);EXPECT_TRUE(llvm::all_of(V3, HasVal));llvm::move(Build(), std::back_inserter(V4));EXPECT_EQ(V4.size(), 4U);EXPECT_TRUE(llvm::all_of(V4, HasVal));}TEST(STLExtras, Unique) {std::vector<int> V = {1, 5, 5, 4, 3, 3, 3};auto I = llvm::unique(V, [](int a, int b) { return a == b; });EXPECT_EQ(I, V.begin() + 4);EXPECT_EQ(1, V[0]);EXPECT_EQ(5, V[1]);EXPECT_EQ(4, V[2]);EXPECT_EQ(3, V[3]);}TEST(STLExtrasTest, MakeVisitorOneCallable) {auto IdentityLambda = [](auto X) { return X; };auto IdentityVisitor = makeVisitor(IdentityLambda);EXPECT_EQ(IdentityLambda(1), IdentityVisitor(1));EXPECT_EQ(IdentityLambda(2.0f), IdentityVisitor(2.0f));EXPECT_TRUE((std::is_same<decltype(IdentityLambda(IdentityLambda)),decltype(IdentityLambda)>::value));EXPECT_TRUE((std::is_same<decltype(IdentityVisitor(IdentityVisitor)),decltype(IdentityVisitor)>::value));}TEST(STLExtrasTest, MakeVisitorTwoCallables) {auto Visitor =makeVisitor([](int) { return 0; }, [](std::string) { return 1; });EXPECT_EQ(Visitor(42), 0);EXPECT_EQ(Visitor("foo"), 1);}TEST(STLExtrasTest, MakeVisitorCallableMultipleOperands) {auto Second = makeVisitor([](int I, float F) { return F; },[](float F, int I) { return I; });EXPECT_EQ(Second(1.f, 1), 1);EXPECT_EQ(Second(1, 1.f), 1.f);}TEST(STLExtrasTest, MakeVisitorDefaultCase) {{auto Visitor = makeVisitor([](int I) { return I + 100; },[](float F) { return F * 2; },[](auto) { return -1; });EXPECT_EQ(Visitor(24), 124);EXPECT_EQ(Visitor(2.f), 4.f);EXPECT_EQ(Visitor(2.), -1);EXPECT_EQ(Visitor(Visitor), -1);}{auto Visitor = makeVisitor([](auto) { return -1; },[](int I) { return I + 100; },[](float F) { return F * 2; });EXPECT_EQ(Visitor(24), 124);EXPECT_EQ(Visitor(2.f), 4.f);EXPECT_EQ(Visitor(2.), -1);EXPECT_EQ(Visitor(Visitor), -1);}}template <bool Moveable, bool Copyable>struct Functor : Counted<Moveable, Copyable> {using Counted<Moveable, Copyable>::Counted;void operator()() {}};TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsPRValue) {int Copies = 0;int Moves = 0;int Destructors = 0;{auto V = makeVisitor(Functor<true, false>(Copies, Moves, Destructors));(void)V;EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(1, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(2, Destructors);}TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsRValue) {int Copies = 0;int Moves = 0;int Destructors = 0;{Functor<true, false> F(Copies, Moves, Destructors);{auto V = makeVisitor(std::move(F));(void)V;EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(0, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(1, Destructors);}EXPECT_EQ(0, Copies);EXPECT_EQ(1, Moves);EXPECT_EQ(2, Destructors);}TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsLValue) {int Copies = 0;int Moves = 0;int Destructors = 0;{Functor<true, true> F(Copies, Moves, Destructors);{auto V = makeVisitor(F);(void)V;EXPECT_EQ(1, Copies);EXPECT_EQ(0, Moves);EXPECT_EQ(0, Destructors);}EXPECT_EQ(1, Copies);EXPECT_EQ(0, Moves);EXPECT_EQ(1, Destructors);}EXPECT_EQ(1, Copies);EXPECT_EQ(0, Moves);EXPECT_EQ(2, Destructors);}TEST(STLExtrasTest, AllOfZip) {std::vector<int> v1 = {0, 4, 2, 1};std::vector<int> v2 = {1, 4, 3, 6};EXPECT_TRUE(all_of_zip(v1, v2, [](int v1, int v2) { return v1 <= v2; }));EXPECT_FALSE(all_of_zip(v1, v2, [](int L, int R) { return L < R; }));// Triple vectorsstd::vector<int> v3 = {1, 6, 5, 7};EXPECT_EQ(true, all_of_zip(v1, v2, v3, [](int a, int b, int c) {return a <= b && b <= c;}));EXPECT_EQ(false, all_of_zip(v1, v2, v3, [](int a, int b, int c) {return a < b && b < c;}));// Shorter vector should fail even with an always-true predicate.std::vector<int> v_short = {1, 4};EXPECT_EQ(false, all_of_zip(v1, v_short, [](int, int) { return true; }));EXPECT_EQ(false,all_of_zip(v1, v2, v_short, [](int, int, int) { return true; }));}TEST(STLExtrasTest, TypesAreDistinct) {EXPECT_TRUE((llvm::TypesAreDistinct<>::value));EXPECT_TRUE((llvm::TypesAreDistinct<int>::value));EXPECT_FALSE((llvm::TypesAreDistinct<int, int>::value));EXPECT_TRUE((llvm::TypesAreDistinct<int, float>::value));EXPECT_FALSE((llvm::TypesAreDistinct<int, float, int>::value));EXPECT_TRUE((llvm::TypesAreDistinct<int, float, double>::value));EXPECT_FALSE((llvm::TypesAreDistinct<int, float, double, float>::value));EXPECT_TRUE((llvm::TypesAreDistinct<int, int *>::value));EXPECT_TRUE((llvm::TypesAreDistinct<int, int &>::value));EXPECT_TRUE((llvm::TypesAreDistinct<int, int &&>::value));EXPECT_TRUE((llvm::TypesAreDistinct<int, const int>::value));}TEST(STLExtrasTest, FirstIndexOfType) {EXPECT_EQ((llvm::FirstIndexOfType<int, int>::value), 0u);EXPECT_EQ((llvm::FirstIndexOfType<int, int, int>::value), 0u);EXPECT_EQ((llvm::FirstIndexOfType<int, float, int>::value), 1u);EXPECT_EQ((llvm::FirstIndexOfType<int const *, float, int, int const *,const int>::value),2u);}TEST(STLExtrasTest, TypeAtIndex) {EXPECT_TRUE((std::is_same<int, llvm::TypeAtIndex<0, int>>::value));EXPECT_TRUE((std::is_same<int, llvm::TypeAtIndex<0, int, float>>::value));EXPECT_TRUE((std::is_same<float, llvm::TypeAtIndex<1, int, float>>::value));EXPECT_TRUE((std::is_same<float, llvm::TypeAtIndex<1, int, float, double>>::value));EXPECT_TRUE((std::is_same<float, llvm::TypeAtIndex<1, int, float, double>>::value));EXPECT_TRUE((std::is_same<double, llvm::TypeAtIndex<2, int, float, double>>::value));}enum Doggos {Floofer,Woofer,SubWoofer,Pupper,Pupperino,Longboi,};TEST(STLExtrasTest, IsContainedInitializerList) {EXPECT_TRUE(is_contained({Woofer, SubWoofer}, Woofer));EXPECT_TRUE(is_contained({Woofer, SubWoofer}, SubWoofer));EXPECT_FALSE(is_contained({Woofer, SubWoofer}, Pupper));EXPECT_FALSE(is_contained({}, Longboi));static_assert(is_contained({Woofer, SubWoofer}, SubWoofer), "SubWoofer!");static_assert(!is_contained({Woofer, SubWoofer}, Pupper), "Missing Pupper!");EXPECT_TRUE(is_contained({1, 2, 3, 4}, 3));EXPECT_FALSE(is_contained({1, 2, 3, 4}, 5));static_assert(is_contained({1, 2, 3, 4}, 3), "It's there!");static_assert(!is_contained({1, 2, 3, 4}, 5), "It's not there :(");}TEST(STLExtrasTest, addEnumValues) {enum A { Zero = 0, One = 1 };enum B { IntMax = INT_MAX, ULongLongMax = ULLONG_MAX };enum class C : unsigned { Two = 2 };// Non-fixed underlying types, with same underlying typesstatic_assert(addEnumValues(Zero, One) == 1,"addEnumValues(Zero, One) failed.");static_assert(addEnumValues(IntMax, ULongLongMax) ==INT_MAX + static_cast<unsigned long long>(ULLONG_MAX),"addEnumValues(IntMax, ULongLongMax) failed.");// Non-fixed underlying types, with different underlying typesstatic_assert(addEnumValues(Zero, IntMax) == INT_MAX,"addEnumValues(Zero, IntMax) failed.");static_assert(addEnumValues(One, ULongLongMax) ==1 + static_cast<unsigned long long>(ULLONG_MAX),"addEnumValues(One, ULongLongMax) failed.");// Non-fixed underlying type enum and fixed underlying type enum, with same// underlying typesstatic_assert(addEnumValues(One, C::Two) == 3,"addEnumValues(One, C::Two) failed.");// Non-fixed underlying type enum and fixed underlying type enum, with// different underlying typesstatic_assert(addEnumValues(ULongLongMax, C::Two) ==static_cast<unsigned long long>(ULLONG_MAX) + 2,"addEnumValues(ULongLongMax, C::Two) failed.");}} // namespace
//===----- llvm/unittest/ADT/SCCIteratorTest.cpp - SCCIterator tests ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/SCCIterator.h"#include "TestGraph.h"#include "gtest/gtest.h"#include <limits.h>using namespace llvm;namespace llvm {TEST(SCCIteratorTest, AllSmallGraphs) {// Test SCC computation against every graph with NUM_NODES nodes or less.// Since SCC considers every node to have an implicit self-edge, we only// create graphs for which every node has a self-edge.#define NUM_NODES 4#define NUM_GRAPHS (NUM_NODES * (NUM_NODES - 1))typedef Graph<NUM_NODES> GT;/// Enumerate all graphs using NUM_GRAPHS bits.static_assert(NUM_GRAPHS < sizeof(unsigned) * CHAR_BIT, "Too many graphs!");for (unsigned GraphDescriptor = 0; GraphDescriptor < (1U << NUM_GRAPHS);++GraphDescriptor) {GT G;// Add edges as specified by the descriptor.unsigned DescriptorCopy = GraphDescriptor;for (unsigned i = 0; i != NUM_NODES; ++i)for (unsigned j = 0; j != NUM_NODES; ++j) {// Always add a self-edge.if (i == j) {G.AddEdge(i, j);continue;}if (DescriptorCopy & 1)G.AddEdge(i, j);DescriptorCopy >>= 1;}// Test the SCC logic on this graph./// NodesInSomeSCC - Those nodes which are in some SCC.GT::NodeSubset NodesInSomeSCC;for (scc_iterator<GT> I = scc_begin(G), E = scc_end(G); I != E; ++I) {const std::vector<GT::NodeType *> &SCC = *I;// Get the nodes in this SCC as a NodeSubset rather than a vector.GT::NodeSubset NodesInThisSCC;for (unsigned i = 0, e = SCC.size(); i != e; ++i)NodesInThisSCC.AddNode(SCC[i]->first);// There should be at least one node in every SCC.EXPECT_FALSE(NodesInThisSCC.isEmpty());// Check that every node in the SCC is reachable from every other node in// the SCC.for (unsigned i = 0; i != NUM_NODES; ++i)if (NodesInThisSCC.count(i)) {EXPECT_TRUE(NodesInThisSCC.isSubsetOf(G.NodesReachableFrom(i)));}// OK, now that we now that every node in the SCC is reachable from every// other, this means that the set of nodes reachable from any node in the// SCC is the same as the set of nodes reachable from every node in the// SCC. Check that for every node N not in the SCC but reachable from the// SCC, no element of the SCC is reachable from N.for (unsigned i = 0; i != NUM_NODES; ++i)if (NodesInThisSCC.count(i)) {GT::NodeSubset NodesReachableFromSCC = G.NodesReachableFrom(i);GT::NodeSubset ReachableButNotInSCC =NodesReachableFromSCC.Meet(NodesInThisSCC.Complement());for (unsigned j = 0; j != NUM_NODES; ++j)if (ReachableButNotInSCC.count(j)) {EXPECT_TRUE(G.NodesReachableFrom(j).Meet(NodesInThisSCC).isEmpty());}// The result must be the same for all other nodes in this SCC, so// there is no point in checking them.break;}// This is indeed a SCC: a maximal set of nodes for which each node is// reachable from every other.// Check that we didn't already see this SCC.EXPECT_TRUE(NodesInSomeSCC.Meet(NodesInThisSCC).isEmpty());NodesInSomeSCC = NodesInSomeSCC.Join(NodesInThisSCC);// Check a property that is specific to the LLVM SCC iterator and// guaranteed by it: if a node in SCC S1 has an edge to a node in// SCC S2, then S1 is visited *after* S2. This means that the set// of nodes reachable from this SCC must be contained either in the// union of this SCC and all previously visited SCC's.for (unsigned i = 0; i != NUM_NODES; ++i)if (NodesInThisSCC.count(i)) {GT::NodeSubset NodesReachableFromSCC = G.NodesReachableFrom(i);EXPECT_TRUE(NodesReachableFromSCC.isSubsetOf(NodesInSomeSCC));// The result must be the same for all other nodes in this SCC, so// there is no point in checking them.break;}}// Finally, check that the nodes in some SCC are exactly those that are// reachable from the initial node.EXPECT_EQ(NodesInSomeSCC, G.NodesReachableFrom(0));}}}
//===- RangeAdapterTest.cpp - Unit tests for range adapters --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/iterator_range.h"#include "gtest/gtest.h"#include <iterator>#include <list>#include <vector>using namespace llvm;namespace {// A wrapper around vector which exposes rbegin(), rend().class ReverseOnlyVector {std::vector<int> Vec;public:ReverseOnlyVector(std::initializer_list<int> list) : Vec(list) {}typedef std::vector<int>::reverse_iterator reverse_iterator;typedef std::vector<int>::const_reverse_iterator const_reverse_iterator;reverse_iterator rbegin() { return Vec.rbegin(); }reverse_iterator rend() { return Vec.rend(); }const_reverse_iterator rbegin() const { return Vec.rbegin(); }const_reverse_iterator rend() const { return Vec.rend(); }};// A wrapper around vector which exposes begin(), end(), rbegin() and rend().// begin() and end() don't have implementations as this ensures that we will// get a linker error if reverse() chooses begin()/end() over rbegin(), rend().class BidirectionalVector {mutable std::vector<int> Vec;public:BidirectionalVector(std::initializer_list<int> list) : Vec(list) {}typedef std::vector<int>::iterator iterator;iterator begin() const;iterator end() const;typedef std::vector<int>::reverse_iterator reverse_iterator;reverse_iterator rbegin() const { return Vec.rbegin(); }reverse_iterator rend() const { return Vec.rend(); }};/// This is the same as BidirectionalVector but with the addition of const/// begin/rbegin methods to ensure that the type traits for has_rbegin works.class BidirectionalVectorConsts {std::vector<int> Vec;public:BidirectionalVectorConsts(std::initializer_list<int> list) : Vec(list) {}typedef std::vector<int>::iterator iterator;typedef std::vector<int>::const_iterator const_iterator;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;typedef std::vector<int>::reverse_iterator reverse_iterator;typedef std::vector<int>::const_reverse_iterator const_reverse_iterator;reverse_iterator rbegin() { return Vec.rbegin(); }reverse_iterator rend() { return Vec.rend(); }const_reverse_iterator rbegin() const { return Vec.rbegin(); }const_reverse_iterator rend() const { return Vec.rend(); }};/// Check that types with custom iterators work.class CustomIteratorVector {mutable std::vector<int> V;public:CustomIteratorVector(std::initializer_list<int> list) : V(list) {}typedef std::vector<int>::iterator iterator;class reverse_iterator {std::vector<int>::iterator I;public:reverse_iterator() = default;reverse_iterator(const reverse_iterator &) = default;reverse_iterator &operator=(const reverse_iterator &) = default;explicit reverse_iterator(std::vector<int>::iterator I) : I(I) {}reverse_iterator &operator++() {--I;return *this;}reverse_iterator &operator--() {++I;return *this;}int &operator*() const { return *std::prev(I); }int *operator->() const { return &*std::prev(I); }friend bool operator==(const reverse_iterator &L,const reverse_iterator &R) {return L.I == R.I;}friend bool operator!=(const reverse_iterator &L,const reverse_iterator &R) {return !(L == R);}};iterator begin() const { return V.begin(); }iterator end() const { return V.end(); }reverse_iterator rbegin() const { return reverse_iterator(V.end()); }reverse_iterator rend() const { return reverse_iterator(V.begin()); }};template <typename R> void TestRev(const R &r) {int counter = 3;for (int i : r)EXPECT_EQ(i, counter--);}// Test fixturetemplate <typename T> class RangeAdapterLValueTest : public ::testing::Test {};typedef ::testing::Types<std::vector<int>, std::list<int>, int[4]>RangeAdapterLValueTestTypes;TYPED_TEST_SUITE(RangeAdapterLValueTest, RangeAdapterLValueTestTypes, );TYPED_TEST(RangeAdapterLValueTest, TrivialOperation) {TypeParam v = {0, 1, 2, 3};TestRev(reverse(v));const TypeParam c = {0, 1, 2, 3};TestRev(reverse(c));}template <typename T> struct RangeAdapterRValueTest : testing::Test {};typedef ::testing::Types<std::vector<int>, std::list<int>, CustomIteratorVector,ReverseOnlyVector, BidirectionalVector,BidirectionalVectorConsts>RangeAdapterRValueTestTypes;TYPED_TEST_SUITE(RangeAdapterRValueTest, RangeAdapterRValueTestTypes, );TYPED_TEST(RangeAdapterRValueTest, TrivialOperation) {TestRev(reverse(TypeParam({0, 1, 2, 3})));}TYPED_TEST(RangeAdapterRValueTest, HasRbegin) {static_assert(has_rbegin<TypeParam>::value, "rbegin() should be defined");}TYPED_TEST(RangeAdapterRValueTest, RangeType) {static_assert(std::is_same<decltype(reverse(*static_cast<TypeParam *>(nullptr)).begin()),decltype(static_cast<TypeParam *>(nullptr)->rbegin())>::value,"reverse().begin() should have the same type as rbegin()");static_assert(std::is_same<decltype(reverse(*static_cast<const TypeParam *>(nullptr)).begin()),decltype(static_cast<const TypeParam *>(nullptr)->rbegin())>::value,"reverse().begin() should have the same type as rbegin() [const]");}} // anonymous namespace
//===- llvm/unittest/ADT/PriorityWorklist.cpp -----------------------------===////// 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////===----------------------------------------------------------------------===////// PriorityWorklist unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/PriorityWorklist.h"#include "gtest/gtest.h"#include <list>#include <vector>namespace {using namespace llvm;template <typename T> class PriorityWorklistTest : public ::testing::Test {};typedef ::testing::Types<PriorityWorklist<int>, SmallPriorityWorklist<int, 2>>TestTypes;TYPED_TEST_SUITE(PriorityWorklistTest, TestTypes, );TYPED_TEST(PriorityWorklistTest, Basic) {TypeParam W;EXPECT_TRUE(W.empty());EXPECT_EQ(0u, W.size());EXPECT_FALSE(W.count(42));EXPECT_TRUE(W.insert(21));EXPECT_TRUE(W.insert(42));EXPECT_TRUE(W.insert(17));EXPECT_FALSE(W.empty());EXPECT_EQ(3u, W.size());EXPECT_TRUE(W.count(42));EXPECT_FALSE(W.erase(75));EXPECT_EQ(3u, W.size());EXPECT_EQ(17, W.back());EXPECT_TRUE(W.erase(17));EXPECT_FALSE(W.count(17));EXPECT_EQ(2u, W.size());EXPECT_EQ(42, W.back());W.clear();EXPECT_TRUE(W.empty());EXPECT_EQ(0u, W.size());EXPECT_TRUE(W.insert(21));EXPECT_TRUE(W.insert(42));EXPECT_TRUE(W.insert(12));EXPECT_TRUE(W.insert(17));EXPECT_TRUE(W.count(12));EXPECT_TRUE(W.count(17));EXPECT_EQ(4u, W.size());EXPECT_EQ(17, W.back());EXPECT_TRUE(W.erase(12));EXPECT_FALSE(W.count(12));EXPECT_TRUE(W.count(17));EXPECT_EQ(3u, W.size());EXPECT_EQ(17, W.back());EXPECT_FALSE(W.insert(42));EXPECT_EQ(3u, W.size());EXPECT_EQ(42, W.pop_back_val());EXPECT_EQ(17, W.pop_back_val());EXPECT_EQ(21, W.pop_back_val());EXPECT_TRUE(W.empty());}TYPED_TEST(PriorityWorklistTest, InsertSequence) {TypeParam W;ASSERT_TRUE(W.insert(2));ASSERT_TRUE(W.insert(4));ASSERT_TRUE(W.insert(7));// Insert a sequence that has internal duplicates and a duplicate among// existing entries.W.insert(std::vector<int>({42, 13, 42, 7, 8}));EXPECT_EQ(8, W.pop_back_val());EXPECT_EQ(7, W.pop_back_val());EXPECT_EQ(42, W.pop_back_val());EXPECT_EQ(13, W.pop_back_val());EXPECT_EQ(4, W.pop_back_val());EXPECT_EQ(2, W.pop_back_val());ASSERT_TRUE(W.empty());// Simpler tests with various other input types.ASSERT_TRUE(W.insert(2));ASSERT_TRUE(W.insert(7));// Use a non-random-access container.W.insert(std::list<int>({7, 5}));EXPECT_EQ(5, W.pop_back_val());EXPECT_EQ(7, W.pop_back_val());EXPECT_EQ(2, W.pop_back_val());ASSERT_TRUE(W.empty());ASSERT_TRUE(W.insert(2));ASSERT_TRUE(W.insert(7));// Use a raw array.int A[] = {7, 5};W.insert(A);EXPECT_EQ(5, W.pop_back_val());EXPECT_EQ(7, W.pop_back_val());EXPECT_EQ(2, W.pop_back_val());ASSERT_TRUE(W.empty());ASSERT_TRUE(W.insert(2));ASSERT_TRUE(W.insert(7));// Inserting an empty sequence does nothing.W.insert(std::vector<int>());EXPECT_EQ(7, W.pop_back_val());EXPECT_EQ(2, W.pop_back_val());ASSERT_TRUE(W.empty());}TYPED_TEST(PriorityWorklistTest, EraseIf) {TypeParam W;W.insert(23);W.insert(10);W.insert(47);W.insert(42);W.insert(23);W.insert(13);W.insert(26);W.insert(42);EXPECT_EQ(6u, W.size());EXPECT_FALSE(W.erase_if([](int i) { return i > 100; }));EXPECT_EQ(6u, W.size());EXPECT_EQ(42, W.back());EXPECT_TRUE(W.erase_if([](int i) {assert(i != 0 && "Saw a null value!");return (i & 1) == 0;}));EXPECT_EQ(3u, W.size());EXPECT_FALSE(W.count(42));EXPECT_FALSE(W.count(26));EXPECT_FALSE(W.count(10));EXPECT_FALSE(W.insert(47));EXPECT_FALSE(W.insert(23));EXPECT_EQ(23, W.pop_back_val());EXPECT_EQ(47, W.pop_back_val());EXPECT_EQ(13, W.pop_back_val());}}
//===- PostOrderIteratorTest.cpp - PostOrderIterator unit tests -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/PostOrderIterator.h"#include "llvm/IR/BasicBlock.h"#include "llvm/IR/CFG.h"#include "gtest/gtest.h"#include "TestGraph.h"using namespace llvm;namespace {// Whether we're able to compileTEST(PostOrderIteratorTest, Compiles) {typedef SmallPtrSet<void *, 4> ExtSetTy;// Tests that template specializations are kept up to datevoid *Null = nullptr;po_iterator_storage<std::set<void *>, false> PIS;PIS.insertEdge(Optional<void *>(), Null);ExtSetTy Ext;po_iterator_storage<ExtSetTy, true> PISExt(Ext);PIS.insertEdge(Optional<void *>(), Null);// Test above, but going through po_iterator (which inherits from template// base)BasicBlock *NullBB = nullptr;auto PI = po_end(NullBB);PI.insertEdge(Optional<BasicBlock *>(), NullBB);auto PIExt = po_ext_end(NullBB, Ext);PIExt.insertEdge(Optional<BasicBlock *>(), NullBB);}// Test post-order and reverse post-order traversals for simple graph type.TEST(PostOrderIteratorTest, PostOrderAndReversePostOrderTraverrsal) {Graph<6> G;G.AddEdge(0, 1);G.AddEdge(0, 2);G.AddEdge(0, 3);G.AddEdge(1, 4);G.AddEdge(2, 5);G.AddEdge(5, 2);G.AddEdge(2, 4);G.AddEdge(1, 4);SmallVector<int> FromIterator;for (auto N : post_order(G))FromIterator.push_back(N->first);EXPECT_EQ(6u, FromIterator.size());EXPECT_EQ(4, FromIterator[0]);EXPECT_EQ(1, FromIterator[1]);EXPECT_EQ(5, FromIterator[2]);EXPECT_EQ(2, FromIterator[3]);EXPECT_EQ(3, FromIterator[4]);EXPECT_EQ(0, FromIterator[5]);FromIterator.clear();ReversePostOrderTraversal<Graph<6>> RPOT(G);for (auto N : RPOT)FromIterator.push_back(N->first);EXPECT_EQ(6u, FromIterator.size());EXPECT_EQ(0, FromIterator[0]);EXPECT_EQ(3, FromIterator[1]);EXPECT_EQ(2, FromIterator[2]);EXPECT_EQ(5, FromIterator[3]);EXPECT_EQ(1, FromIterator[4]);EXPECT_EQ(4, FromIterator[5]);}}
//===- llvm/unittest/ADT/PointerUnionTest.cpp - Optional unit tests -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/PointerUnion.h"#include "gtest/gtest.h"using namespace llvm;namespace {typedef PointerUnion<int *, float *> PU;typedef PointerUnion<int *, float *, long long *> PU3;typedef PointerUnion<int *, float *, long long *, double *> PU4;struct PointerUnionTest : public testing::Test {float f;int i;double d;long long l;PU a, b, c, n;PU3 i3, f3, l3;PU4 i4, f4, l4, d4;PU4 i4null, f4null, l4null, d4null;PointerUnionTest(): f(3.14f), i(42), d(3.14), l(42), a(&f), b(&i), c(&i), n(), i3(&i),f3(&f), l3(&l), i4(&i), f4(&f), l4(&l), d4(&d), i4null((int *)nullptr),f4null((float *)nullptr), l4null((long long *)nullptr),d4null((double *)nullptr) {}};TEST_F(PointerUnionTest, Comparison) {EXPECT_TRUE(a == a);EXPECT_FALSE(a != a);EXPECT_TRUE(a != b);EXPECT_FALSE(a == b);EXPECT_TRUE(b == c);EXPECT_FALSE(b != c);EXPECT_TRUE(b != n);EXPECT_FALSE(b == n);EXPECT_TRUE(i3 == i3);EXPECT_FALSE(i3 != i3);EXPECT_TRUE(i3 != f3);EXPECT_TRUE(f3 != l3);EXPECT_TRUE(i4 == i4);EXPECT_FALSE(i4 != i4);EXPECT_TRUE(i4 != f4);EXPECT_TRUE(i4 != l4);EXPECT_TRUE(f4 != l4);EXPECT_TRUE(l4 != d4);EXPECT_TRUE(i4null != f4null);EXPECT_TRUE(i4null != l4null);EXPECT_TRUE(i4null != d4null);}TEST_F(PointerUnionTest, Null) {EXPECT_FALSE(a.isNull());EXPECT_FALSE(b.isNull());EXPECT_TRUE(n.isNull());EXPECT_FALSE(!a);EXPECT_FALSE(!b);EXPECT_TRUE(!n);// workaround an issue with EXPECT macros and explicit boolEXPECT_TRUE((bool)a);EXPECT_TRUE((bool)b);EXPECT_FALSE(n);EXPECT_NE(n, b);EXPECT_EQ(b, c);b = nullptr;EXPECT_EQ(n, b);EXPECT_NE(b, c);EXPECT_FALSE(i3.isNull());EXPECT_FALSE(f3.isNull());EXPECT_FALSE(l3.isNull());EXPECT_FALSE(i4.isNull());EXPECT_FALSE(f4.isNull());EXPECT_FALSE(l4.isNull());EXPECT_FALSE(d4.isNull());EXPECT_TRUE(i4null.isNull());EXPECT_TRUE(f4null.isNull());EXPECT_TRUE(l4null.isNull());EXPECT_TRUE(d4null.isNull());}TEST_F(PointerUnionTest, Is) {EXPECT_FALSE(a.is<int *>());EXPECT_TRUE(a.is<float *>());EXPECT_TRUE(b.is<int *>());EXPECT_FALSE(b.is<float *>());EXPECT_TRUE(n.is<int *>());EXPECT_FALSE(n.is<float *>());EXPECT_TRUE(i3.is<int *>());EXPECT_TRUE(f3.is<float *>());EXPECT_TRUE(l3.is<long long *>());EXPECT_TRUE(i4.is<int *>());EXPECT_TRUE(f4.is<float *>());EXPECT_TRUE(l4.is<long long *>());EXPECT_TRUE(d4.is<double *>());EXPECT_TRUE(i4null.is<int *>());EXPECT_TRUE(f4null.is<float *>());EXPECT_TRUE(l4null.is<long long *>());EXPECT_TRUE(d4null.is<double *>());}TEST_F(PointerUnionTest, Get) {EXPECT_EQ(a.get<float *>(), &f);EXPECT_EQ(b.get<int *>(), &i);EXPECT_EQ(n.get<int *>(), (int *)nullptr);}template<int I> struct alignas(8) Aligned {};typedef PointerUnion<Aligned<0> *, Aligned<1> *, Aligned<2> *, Aligned<3> *,Aligned<4> *, Aligned<5> *, Aligned<6> *, Aligned<7> *>PU8;TEST_F(PointerUnionTest, ManyElements) {Aligned<0> a0;Aligned<7> a7;PU8 a = &a0;EXPECT_TRUE(a.is<Aligned<0>*>());EXPECT_FALSE(a.is<Aligned<1>*>());EXPECT_FALSE(a.is<Aligned<2>*>());EXPECT_FALSE(a.is<Aligned<3>*>());EXPECT_FALSE(a.is<Aligned<4>*>());EXPECT_FALSE(a.is<Aligned<5>*>());EXPECT_FALSE(a.is<Aligned<6>*>());EXPECT_FALSE(a.is<Aligned<7>*>());EXPECT_EQ(a.dyn_cast<Aligned<0>*>(), &a0);EXPECT_EQ(*a.getAddrOfPtr1(), &a0);a = &a7;EXPECT_FALSE(a.is<Aligned<0>*>());EXPECT_FALSE(a.is<Aligned<1>*>());EXPECT_FALSE(a.is<Aligned<2>*>());EXPECT_FALSE(a.is<Aligned<3>*>());EXPECT_FALSE(a.is<Aligned<4>*>());EXPECT_FALSE(a.is<Aligned<5>*>());EXPECT_FALSE(a.is<Aligned<6>*>());EXPECT_TRUE(a.is<Aligned<7>*>());EXPECT_EQ(a.dyn_cast<Aligned<7>*>(), &a7);EXPECT_TRUE(a == PU8(&a7));EXPECT_TRUE(a != PU8(&a0));}TEST_F(PointerUnionTest, GetAddrOfPtr1) {EXPECT_TRUE((void *)b.getAddrOfPtr1() == (void *)&b);EXPECT_TRUE((void *)n.getAddrOfPtr1() == (void *)&n);}TEST_F(PointerUnionTest, NewCastInfra) {// test isa<>EXPECT_TRUE(isa<float *>(a));EXPECT_TRUE(isa<int *>(b));EXPECT_TRUE(isa<int *>(c));EXPECT_TRUE(isa<int *>(n));EXPECT_TRUE(isa<int *>(i3));EXPECT_TRUE(isa<float *>(f3));EXPECT_TRUE(isa<long long *>(l3));EXPECT_TRUE(isa<int *>(i4));EXPECT_TRUE(isa<float *>(f4));EXPECT_TRUE(isa<long long *>(l4));EXPECT_TRUE(isa<double *>(d4));EXPECT_TRUE(isa<int *>(i4null));EXPECT_TRUE(isa<float *>(f4null));EXPECT_TRUE(isa<long long *>(l4null));EXPECT_TRUE(isa<double *>(d4null));EXPECT_FALSE(isa<int *>(a));EXPECT_FALSE(isa<float *>(b));EXPECT_FALSE(isa<float *>(c));EXPECT_FALSE(isa<float *>(n));EXPECT_FALSE(isa<float *>(i3));EXPECT_FALSE(isa<long long *>(i3));EXPECT_FALSE(isa<int *>(f3));EXPECT_FALSE(isa<long long *>(f3));EXPECT_FALSE(isa<int *>(l3));EXPECT_FALSE(isa<float *>(l3));EXPECT_FALSE(isa<float *>(i4));EXPECT_FALSE(isa<long long *>(i4));EXPECT_FALSE(isa<double *>(i4));EXPECT_FALSE(isa<int *>(f4));EXPECT_FALSE(isa<long long *>(f4));EXPECT_FALSE(isa<double *>(f4));EXPECT_FALSE(isa<int *>(l4));EXPECT_FALSE(isa<float *>(l4));EXPECT_FALSE(isa<double *>(l4));EXPECT_FALSE(isa<int *>(d4));EXPECT_FALSE(isa<float *>(d4));EXPECT_FALSE(isa<long long *>(d4));EXPECT_FALSE(isa<float *>(i4null));EXPECT_FALSE(isa<long long *>(i4null));EXPECT_FALSE(isa<double *>(i4null));EXPECT_FALSE(isa<int *>(f4null));EXPECT_FALSE(isa<long long *>(f4null));EXPECT_FALSE(isa<double *>(f4null));EXPECT_FALSE(isa<int *>(l4null));EXPECT_FALSE(isa<float *>(l4null));EXPECT_FALSE(isa<double *>(l4null));EXPECT_FALSE(isa<int *>(d4null));EXPECT_FALSE(isa<float *>(d4null));EXPECT_FALSE(isa<long long *>(d4null));// test cast<>EXPECT_EQ(cast<float *>(a), &f);EXPECT_EQ(cast<int *>(b), &i);EXPECT_EQ(cast<int *>(c), &i);EXPECT_EQ(cast<int *>(i3), &i);EXPECT_EQ(cast<float *>(f3), &f);EXPECT_EQ(cast<long long *>(l3), &l);EXPECT_EQ(cast<int *>(i4), &i);EXPECT_EQ(cast<float *>(f4), &f);EXPECT_EQ(cast<long long *>(l4), &l);EXPECT_EQ(cast<double *>(d4), &d);// test dyn_castEXPECT_EQ(dyn_cast<int *>(a), nullptr);EXPECT_EQ(dyn_cast<float *>(a), &f);EXPECT_EQ(dyn_cast<int *>(b), &i);EXPECT_EQ(dyn_cast<float *>(b), nullptr);EXPECT_EQ(dyn_cast<int *>(c), &i);EXPECT_EQ(dyn_cast<float *>(c), nullptr);EXPECT_EQ(dyn_cast<int *>(n), nullptr);EXPECT_EQ(dyn_cast<float *>(n), nullptr);EXPECT_EQ(dyn_cast<int *>(i3), &i);EXPECT_EQ(dyn_cast<float *>(i3), nullptr);EXPECT_EQ(dyn_cast<long long *>(i3), nullptr);EXPECT_EQ(dyn_cast<int *>(f3), nullptr);EXPECT_EQ(dyn_cast<float *>(f3), &f);EXPECT_EQ(dyn_cast<long long *>(f3), nullptr);EXPECT_EQ(dyn_cast<int *>(l3), nullptr);EXPECT_EQ(dyn_cast<float *>(l3), nullptr);EXPECT_EQ(dyn_cast<long long *>(l3), &l);EXPECT_EQ(dyn_cast<int *>(i4), &i);EXPECT_EQ(dyn_cast<float *>(i4), nullptr);EXPECT_EQ(dyn_cast<long long *>(i4), nullptr);EXPECT_EQ(dyn_cast<double *>(i4), nullptr);EXPECT_EQ(dyn_cast<int *>(f4), nullptr);EXPECT_EQ(dyn_cast<float *>(f4), &f);EXPECT_EQ(dyn_cast<long long *>(f4), nullptr);EXPECT_EQ(dyn_cast<double *>(f4), nullptr);EXPECT_EQ(dyn_cast<int *>(l4), nullptr);EXPECT_EQ(dyn_cast<float *>(l4), nullptr);EXPECT_EQ(dyn_cast<long long *>(l4), &l);EXPECT_EQ(dyn_cast<double *>(l4), nullptr);EXPECT_EQ(dyn_cast<int *>(d4), nullptr);EXPECT_EQ(dyn_cast<float *>(d4), nullptr);EXPECT_EQ(dyn_cast<long long *>(d4), nullptr);EXPECT_EQ(dyn_cast<double *>(d4), &d);EXPECT_EQ(dyn_cast<int *>(i4null), nullptr);EXPECT_EQ(dyn_cast<float *>(i4null), nullptr);EXPECT_EQ(dyn_cast<long long *>(i4null), nullptr);EXPECT_EQ(dyn_cast<double *>(i4null), nullptr);EXPECT_EQ(dyn_cast<int *>(f4null), nullptr);EXPECT_EQ(dyn_cast<float *>(f4null), nullptr);EXPECT_EQ(dyn_cast<long long *>(f4null), nullptr);EXPECT_EQ(dyn_cast<double *>(f4null), nullptr);EXPECT_EQ(dyn_cast<int *>(l4null), nullptr);EXPECT_EQ(dyn_cast<float *>(l4null), nullptr);EXPECT_EQ(dyn_cast<long long *>(l4null), nullptr);EXPECT_EQ(dyn_cast<double *>(l4null), nullptr);EXPECT_EQ(dyn_cast<int *>(d4null), nullptr);EXPECT_EQ(dyn_cast<float *>(d4null), nullptr);EXPECT_EQ(dyn_cast<long long *>(d4null), nullptr);EXPECT_EQ(dyn_cast<double *>(d4null), nullptr);// test for constconst PU4 constd4(&d);EXPECT_TRUE(isa<double *>(constd4));EXPECT_FALSE(isa<int *>(constd4));EXPECT_EQ(cast<double *>(constd4), &d);EXPECT_EQ(dyn_cast<long long *>(constd4), nullptr);auto *result1 = cast<double *>(constd4);static_assert(std::is_same<double *, decltype(result1)>::value,"type mismatch for cast with PointerUnion");PointerUnion<int *, const double *> constd2(&d);auto *result2 = cast<const double *>(constd2);EXPECT_EQ(result2, &d);static_assert(std::is_same<const double *, decltype(result2)>::value,"type mismatch for cast with PointerUnion");}} // end anonymous namespace
//===- llvm/unittest/ADT/PointerSumTypeTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/PointerSumType.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct PointerSumTypeTest : public testing::Test {enum Kinds { Float, Int1, Int2 };float f;int i1, i2;typedef PointerSumType<Kinds, PointerSumTypeMember<Float, float *>,PointerSumTypeMember<Int1, int *>,PointerSumTypeMember<Int2, int *>>SumType;SumType a, b, c, n;PointerSumTypeTest(): f(3.14f), i1(42), i2(-1), a(SumType::create<Float>(&f)),b(SumType::create<Int1>(&i1)), c(SumType::create<Int2>(&i2)), n() {}};TEST_F(PointerSumTypeTest, NullTest) {EXPECT_TRUE(a);EXPECT_TRUE(b);EXPECT_TRUE(c);EXPECT_FALSE(n);}TEST_F(PointerSumTypeTest, GetTag) {EXPECT_EQ(Float, a.getTag());EXPECT_EQ(Int1, b.getTag());EXPECT_EQ(Int2, c.getTag());EXPECT_EQ((Kinds)0, n.getTag());}TEST_F(PointerSumTypeTest, Is) {EXPECT_TRUE(a.is<Float>());EXPECT_FALSE(a.is<Int1>());EXPECT_FALSE(a.is<Int2>());EXPECT_FALSE(b.is<Float>());EXPECT_TRUE(b.is<Int1>());EXPECT_FALSE(b.is<Int2>());EXPECT_FALSE(c.is<Float>());EXPECT_FALSE(c.is<Int1>());EXPECT_TRUE(c.is<Int2>());}TEST_F(PointerSumTypeTest, Get) {EXPECT_EQ(&f, a.get<Float>());EXPECT_EQ(nullptr, a.get<Int1>());EXPECT_EQ(nullptr, a.get<Int2>());EXPECT_EQ(nullptr, b.get<Float>());EXPECT_EQ(&i1, b.get<Int1>());EXPECT_EQ(nullptr, b.get<Int2>());EXPECT_EQ(nullptr, c.get<Float>());EXPECT_EQ(nullptr, c.get<Int1>());EXPECT_EQ(&i2, c.get<Int2>());// Note that we can use .get even on a null sum type. It just always produces// a null pointer, even if one of the discriminants is null.EXPECT_EQ(nullptr, n.get<Float>());EXPECT_EQ(nullptr, n.get<Int1>());EXPECT_EQ(nullptr, n.get<Int2>());}TEST_F(PointerSumTypeTest, Cast) {EXPECT_EQ(&f, a.cast<Float>());EXPECT_EQ(&i1, b.cast<Int1>());EXPECT_EQ(&i2, c.cast<Int2>());}TEST_F(PointerSumTypeTest, Assignment) {b = SumType::create<Int2>(&i2);EXPECT_EQ(nullptr, b.get<Float>());EXPECT_EQ(nullptr, b.get<Int1>());EXPECT_EQ(&i2, b.get<Int2>());b = SumType::create<Int2>(&i1);EXPECT_EQ(nullptr, b.get<Float>());EXPECT_EQ(nullptr, b.get<Int1>());EXPECT_EQ(&i1, b.get<Int2>());float Local = 1.616f;b = SumType::create<Float>(&Local);EXPECT_EQ(&Local, b.get<Float>());EXPECT_EQ(nullptr, b.get<Int1>());EXPECT_EQ(nullptr, b.get<Int2>());n = SumType::create<Int1>(&i2);EXPECT_TRUE(n);EXPECT_EQ(nullptr, n.get<Float>());EXPECT_EQ(&i2, n.get<Int1>());EXPECT_EQ(nullptr, n.get<Int2>());n = SumType::create<Float>(nullptr);EXPECT_FALSE(n);EXPECT_EQ(nullptr, n.get<Float>());EXPECT_EQ(nullptr, n.get<Int1>());EXPECT_EQ(nullptr, n.get<Int2>());}} // end anonymous namespace
//===- llvm/unittest/ADT/PointerIntPairTest.cpp - Unit tests --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/PointerIntPair.h"#include "gtest/gtest.h"#include <limits>using namespace llvm;namespace {TEST(PointerIntPairTest, GetSet) {struct S {int i;};S s;PointerIntPair<S *, 2> Pair(&s, 1U);EXPECT_EQ(&s, Pair.getPointer());EXPECT_EQ(1U, Pair.getInt());Pair.setInt(2);EXPECT_EQ(&s, Pair.getPointer());EXPECT_EQ(2U, Pair.getInt());Pair.setPointer(nullptr);EXPECT_EQ(nullptr, Pair.getPointer());EXPECT_EQ(2U, Pair.getInt());Pair.setPointerAndInt(&s, 3U);EXPECT_EQ(&s, Pair.getPointer());EXPECT_EQ(3U, Pair.getInt());// Make sure that we can perform all of our operations on enum classes.//// The concern is that enum classes are only explicitly convertible to// integers. This means that if we assume in PointerIntPair this, a// compilation error will result. This group of tests exercises the enum class// code to make sure that we do not run into such issues in the future.enum class E : unsigned {Case1,Case2,Case3,};PointerIntPair<S *, 2, E> Pair2(&s, E::Case1);EXPECT_EQ(&s, Pair2.getPointer());EXPECT_EQ(E::Case1, Pair2.getInt());Pair2.setInt(E::Case2);EXPECT_EQ(&s, Pair2.getPointer());EXPECT_EQ(E::Case2, Pair2.getInt());Pair2.setPointer(nullptr);EXPECT_EQ(nullptr, Pair2.getPointer());EXPECT_EQ(E::Case2, Pair2.getInt());Pair2.setPointerAndInt(&s, E::Case3);EXPECT_EQ(&s, Pair2.getPointer());EXPECT_EQ(E::Case3, Pair2.getInt());static_assert(std::is_trivially_copyable<PointerIntPair<S *, 2, E>>::value,"trivially copyable");}TEST(PointerIntPairTest, DefaultInitialize) {PointerIntPair<float *, 2> Pair;EXPECT_EQ(nullptr, Pair.getPointer());EXPECT_EQ(0U, Pair.getInt());}// In real code this would be a word-sized integer limited to 31 bits.struct Fixnum31 {uintptr_t Value;};struct FixnumPointerTraits {static inline void *getAsVoidPointer(Fixnum31 Num) {return reinterpret_cast<void *>(Num.Value << NumLowBitsAvailable);}static inline Fixnum31 getFromVoidPointer(void *P) {// In real code this would assert that the value is in range.return {reinterpret_cast<uintptr_t>(P) >> NumLowBitsAvailable};}static constexpr int NumLowBitsAvailable =std::numeric_limits<uintptr_t>::digits - 31;};TEST(PointerIntPairTest, ManyUnusedBits) {PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits> pair;EXPECT_EQ((uintptr_t)0, pair.getPointer().Value);EXPECT_FALSE(pair.getInt());pair.setPointerAndInt({ 0x7FFFFFFF }, true );EXPECT_EQ((uintptr_t)0x7FFFFFFF, pair.getPointer().Value);EXPECT_TRUE(pair.getInt());EXPECT_EQ(FixnumPointerTraits::NumLowBitsAvailable - 1,(int)PointerLikeTypeTraits<decltype(pair)>::NumLowBitsAvailable);static_assert(std::is_trivially_copyable<PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits>>::value,"trivially copyable");}} // end anonymous namespace
//===- llvm/unittest/ADT/PointerEmbeddedIntTest.cpp -----------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/PointerEmbeddedInt.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(PointerEmbeddedIntTest, Basic) {PointerEmbeddedInt<int, CHAR_BIT> I = 42, J = 43;EXPECT_EQ(42, I);EXPECT_EQ(43, I + 1);EXPECT_EQ((int)sizeof(uintptr_t) * CHAR_BIT - CHAR_BIT,(int)PointerLikeTypeTraits<decltype(I)>::NumLowBitsAvailable);EXPECT_FALSE(I == J);EXPECT_TRUE(I != J);EXPECT_TRUE(I < J);EXPECT_FALSE(I > J);EXPECT_TRUE(I <= J);EXPECT_FALSE(I >= J);EXPECT_FALSE(I == 43);EXPECT_TRUE(I != 43);EXPECT_TRUE(I < 43);EXPECT_FALSE(I > 43);EXPECT_TRUE(I <= 43);EXPECT_FALSE(I >= 43);EXPECT_FALSE(42 == J);EXPECT_TRUE(42 != J);EXPECT_TRUE(42 < J);EXPECT_FALSE(42 > J);EXPECT_TRUE(42 <= J);EXPECT_FALSE(42 >= J);}TEST(PointerEmbeddedIntTest, intptr_t) {PointerEmbeddedInt<intptr_t, CHAR_BIT> IPos = 42, INeg = -42;EXPECT_EQ(42, IPos);EXPECT_EQ(-42, INeg);PointerEmbeddedInt<uintptr_t, CHAR_BIT> U = 42, USaturated = 255;EXPECT_EQ(42U, U);EXPECT_EQ(255U, USaturated);PointerEmbeddedInt<intptr_t, std::numeric_limits<intptr_t>::digits>IMax = std::numeric_limits<intptr_t>::max() >> 1,IMin = std::numeric_limits<intptr_t>::min() >> 1;EXPECT_EQ(std::numeric_limits<intptr_t>::max() >> 1, IMax);EXPECT_EQ(std::numeric_limits<intptr_t>::min() >> 1, IMin);PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1>UMax = std::numeric_limits<uintptr_t>::max() >> 1,UMin = std::numeric_limits<uintptr_t>::min() >> 1;EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1, UMax);EXPECT_EQ(std::numeric_limits<uintptr_t>::min() >> 1, UMin);}TEST(PointerEmbeddedIntTest, PointerLikeTypeTraits) {PointerEmbeddedInt<int, CHAR_BIT> I = 42;using ITraits = PointerLikeTypeTraits<decltype(I)>;EXPECT_EQ(42, ITraits::getFromVoidPointer(ITraits::getAsVoidPointer(I)));PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1>Max = std::numeric_limits<uintptr_t>::max() >> 1;using MaxTraits = PointerLikeTypeTraits<decltype(Max)>;EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1,MaxTraits::getFromVoidPointer(MaxTraits::getAsVoidPointer(Max)));}} // end anonymous namespace
//===- llvm/unittest/ADT/PackedVectorTest.cpp - PackedVector tests --------===////// 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////===----------------------------------------------------------------------===//// BitVectorTest tests fail on PowerPC for unknown reasons, so disable this// as well since it depends on a BitVector.#ifndef __ppc__#include "llvm/ADT/PackedVector.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(PackedVectorTest, Operation) {PackedVector<unsigned, 2> Vec;EXPECT_EQ(0U, Vec.size());EXPECT_TRUE(Vec.empty());Vec.resize(5);EXPECT_EQ(5U, Vec.size());EXPECT_FALSE(Vec.empty());Vec.resize(11);EXPECT_EQ(11U, Vec.size());EXPECT_FALSE(Vec.empty());PackedVector<unsigned, 2> Vec2(3);EXPECT_EQ(3U, Vec2.size());EXPECT_FALSE(Vec2.empty());Vec.clear();EXPECT_EQ(0U, Vec.size());EXPECT_TRUE(Vec.empty());Vec.push_back(2);Vec.push_back(0);Vec.push_back(1);Vec.push_back(3);EXPECT_EQ(2U, Vec[0]);EXPECT_EQ(0U, Vec[1]);EXPECT_EQ(1U, Vec[2]);EXPECT_EQ(3U, Vec[3]);EXPECT_FALSE(Vec == Vec2);EXPECT_TRUE(Vec != Vec2);Vec = Vec2;EXPECT_TRUE(Vec == Vec2);EXPECT_FALSE(Vec != Vec2);Vec[1] = 1;Vec2[1] = 2;Vec |= Vec2;EXPECT_EQ(3U, Vec[1]);}#ifdef EXPECT_DEBUG_DEATHTEST(PackedVectorTest, UnsignedValues) {PackedVector<unsigned, 2> Vec(1);Vec[0] = 0;Vec[0] = 1;Vec[0] = 2;Vec[0] = 3;EXPECT_DEBUG_DEATH(Vec[0] = 4, "value is too big");EXPECT_DEBUG_DEATH(Vec[0] = -1, "value is too big");EXPECT_DEBUG_DEATH(Vec[0] = 0x100, "value is too big");PackedVector<unsigned, 3> Vec2(1);Vec2[0] = 0;Vec2[0] = 7;EXPECT_DEBUG_DEATH(Vec[0] = 8, "value is too big");}TEST(PackedVectorTest, SignedValues) {PackedVector<signed, 2> Vec(1);Vec[0] = -2;Vec[0] = -1;Vec[0] = 0;Vec[0] = 1;EXPECT_DEBUG_DEATH(Vec[0] = -3, "value is too big");EXPECT_DEBUG_DEATH(Vec[0] = 2, "value is too big");PackedVector<signed, 3> Vec2(1);Vec2[0] = -4;Vec2[0] = 3;EXPECT_DEBUG_DEATH(Vec[0] = -5, "value is too big");EXPECT_DEBUG_DEATH(Vec[0] = 4, "value is too big");}#endif}#endif
//===- llvm/unittest/ADT/OptionalTest.cpp - Optional unit tests -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Optional.h"#include "llvm/ADT/SmallString.h"#include "llvm/ADT/StringMap.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest-spi.h"#include "gtest/gtest.h"#include <array>using namespace llvm;static_assert(std::is_trivially_copyable<Optional<int>>::value,"trivially copyable");static_assert(std::is_trivially_copyable<Optional<std::array<int, 3>>>::value,"trivially copyable");void OptionalWorksInConstexpr() {constexpr auto x1 = Optional<int>();constexpr Optional<int> x2{};static_assert(!x1.has_value() && !x2.has_value(),"Default construction and hasValue() are contexpr");static_assert(!x1.has_value() && !x2.has_value(),"Default construction and hasValue() are contexpr");constexpr auto y1 = Optional<int>(3);constexpr Optional<int> y2{3};static_assert(y1.value() == y2.value() && y1.value() == 3,"Construction with value and getValue() are constexpr");static_assert(y1.value() == y2.value() && y1.value() == 3,"Construction with value and getValue() are constexpr");static_assert(Optional<int>{3} >= 2 && Optional<int>{1} < Optional<int>{2},"Comparisons work in constexpr");}namespace {struct NonDefaultConstructible {static unsigned CopyConstructions;static unsigned Destructions;static unsigned CopyAssignments;explicit NonDefaultConstructible(int) {}NonDefaultConstructible(const NonDefaultConstructible&) {++CopyConstructions;}NonDefaultConstructible &operator=(const NonDefaultConstructible&) {++CopyAssignments;return *this;}~NonDefaultConstructible() {++Destructions;}static void ResetCounts() {CopyConstructions = 0;Destructions = 0;CopyAssignments = 0;}};unsigned NonDefaultConstructible::CopyConstructions = 0;unsigned NonDefaultConstructible::Destructions = 0;unsigned NonDefaultConstructible::CopyAssignments = 0;static_assert(!std::is_trivially_copyable<Optional<NonDefaultConstructible>>::value,"not trivially copyable");TEST(OptionalTest, NonDefaultConstructibleTest) {Optional<NonDefaultConstructible> O;EXPECT_FALSE(O);}TEST(OptionalTest, ResetTest) {NonDefaultConstructible::ResetCounts();Optional<NonDefaultConstructible> O(NonDefaultConstructible(3));EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(1u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();O.reset();EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(1u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, InitializationLeakTest) {NonDefaultConstructible::ResetCounts();Optional<NonDefaultConstructible>(NonDefaultConstructible(3));EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(2u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, CopyConstructionTest) {NonDefaultConstructible::ResetCounts();{Optional<NonDefaultConstructible> A(NonDefaultConstructible(3));EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(1u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();Optional<NonDefaultConstructible> B(A);EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(0u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();}EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(2u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, ConstructingCopyAssignmentTest) {NonDefaultConstructible::ResetCounts();{Optional<NonDefaultConstructible> A(NonDefaultConstructible(3));Optional<NonDefaultConstructible> B;EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(1u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();B = A;EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(0u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();}EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(2u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, CopyingCopyAssignmentTest) {NonDefaultConstructible::ResetCounts();{Optional<NonDefaultConstructible> A(NonDefaultConstructible(3));Optional<NonDefaultConstructible> B(NonDefaultConstructible(4));EXPECT_EQ(2u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(2u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();B = A;EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(1u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(0u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();}EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(2u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, DeletingCopyAssignmentTest) {NonDefaultConstructible::ResetCounts();{Optional<NonDefaultConstructible> A;Optional<NonDefaultConstructible> B(NonDefaultConstructible(3));EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(1u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();B = A;EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(1u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();}EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(0u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, NullCopyConstructionTest) {NonDefaultConstructible::ResetCounts();{Optional<NonDefaultConstructible> A;Optional<NonDefaultConstructible> B;EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(0u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();B = A;EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(0u, NonDefaultConstructible::Destructions);NonDefaultConstructible::ResetCounts();}EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(0u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, InPlaceConstructionNonDefaultConstructibleTest) {NonDefaultConstructible::ResetCounts();{ Optional<NonDefaultConstructible> A{in_place, 1}; }EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);EXPECT_EQ(1u, NonDefaultConstructible::Destructions);}TEST(OptionalTest, GetValueOr) {Optional<int> A;EXPECT_EQ(42, A.value_or(42));A = 5;EXPECT_EQ(5, A.value_or(42));}struct MultiArgConstructor {int x, y;MultiArgConstructor(int x, int y) : x(x), y(y) {}explicit MultiArgConstructor(int x, bool positive): x(x), y(positive ? x : -x) {}MultiArgConstructor(const MultiArgConstructor &) = delete;MultiArgConstructor(MultiArgConstructor &&) = delete;MultiArgConstructor &operator=(const MultiArgConstructor &) = delete;MultiArgConstructor &operator=(MultiArgConstructor &&) = delete;friend bool operator==(const MultiArgConstructor &LHS,const MultiArgConstructor &RHS) {return LHS.x == RHS.x && LHS.y == RHS.y;}static unsigned Destructions;~MultiArgConstructor() {++Destructions;}static void ResetCounts() {Destructions = 0;}};unsigned MultiArgConstructor::Destructions = 0;static_assert(!std::is_trivially_copyable<Optional<MultiArgConstructor>>::value,"not trivially copyable");TEST(OptionalTest, Emplace) {MultiArgConstructor::ResetCounts();Optional<MultiArgConstructor> A;A.emplace(1, 2);EXPECT_TRUE(A.has_value());EXPECT_TRUE(A.has_value());EXPECT_EQ(1, A->x);EXPECT_EQ(2, A->y);EXPECT_EQ(0u, MultiArgConstructor::Destructions);A.emplace(5, false);EXPECT_TRUE(A.has_value());EXPECT_TRUE(A.has_value());EXPECT_EQ(5, A->x);EXPECT_EQ(-5, A->y);EXPECT_EQ(1u, MultiArgConstructor::Destructions);}TEST(OptionalTest, InPlaceConstructionMultiArgConstructorTest) {MultiArgConstructor::ResetCounts();{Optional<MultiArgConstructor> A{in_place, 1, 2};EXPECT_TRUE(A.has_value());EXPECT_TRUE(A.has_value());EXPECT_EQ(1, A->x);EXPECT_EQ(2, A->y);Optional<MultiArgConstructor> B{in_place, 5, false};EXPECT_TRUE(B.has_value());EXPECT_TRUE(B.has_value());EXPECT_EQ(5, B->x);EXPECT_EQ(-5, B->y);EXPECT_EQ(0u, MultiArgConstructor::Destructions);}EXPECT_EQ(2u, MultiArgConstructor::Destructions);}TEST(OptionalTest, InPlaceConstructionAndEmplaceEquivalentTest) {MultiArgConstructor::ResetCounts();{Optional<MultiArgConstructor> A{in_place, 1, 2};Optional<MultiArgConstructor> B;B.emplace(1, 2);EXPECT_EQ(0u, MultiArgConstructor::Destructions);ASSERT_EQ(A, B);}EXPECT_EQ(2u, MultiArgConstructor::Destructions);}struct MoveOnly {static unsigned MoveConstructions;static unsigned Destructions;static unsigned MoveAssignments;int val;explicit MoveOnly(int val) : val(val) {}MoveOnly(MoveOnly&& other) {val = other.val;++MoveConstructions;}MoveOnly &operator=(MoveOnly&& other) {val = other.val;++MoveAssignments;return *this;}~MoveOnly() {++Destructions;}static void ResetCounts() {MoveConstructions = 0;Destructions = 0;MoveAssignments = 0;}};unsigned MoveOnly::MoveConstructions = 0;unsigned MoveOnly::Destructions = 0;unsigned MoveOnly::MoveAssignments = 0;static_assert(!std::is_trivially_copyable<Optional<MoveOnly>>::value,"not trivially copyable");TEST(OptionalTest, MoveOnlyNull) {MoveOnly::ResetCounts();Optional<MoveOnly> O;EXPECT_EQ(0u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(0u, MoveOnly::Destructions);}TEST(OptionalTest, MoveOnlyConstruction) {MoveOnly::ResetCounts();Optional<MoveOnly> O(MoveOnly(3));EXPECT_TRUE((bool)O);EXPECT_EQ(3, O->val);EXPECT_EQ(1u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(1u, MoveOnly::Destructions);}TEST(OptionalTest, MoveOnlyMoveConstruction) {Optional<MoveOnly> A(MoveOnly(3));MoveOnly::ResetCounts();Optional<MoveOnly> B(std::move(A));EXPECT_TRUE((bool)A);EXPECT_TRUE((bool)B);EXPECT_EQ(3, B->val);EXPECT_EQ(1u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(0u, MoveOnly::Destructions);}TEST(OptionalTest, MoveOnlyAssignment) {MoveOnly::ResetCounts();Optional<MoveOnly> O;O = MoveOnly(3);EXPECT_TRUE((bool)O);EXPECT_EQ(3, O->val);EXPECT_EQ(1u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(1u, MoveOnly::Destructions);}TEST(OptionalTest, MoveOnlyInitializingAssignment) {Optional<MoveOnly> A(MoveOnly(3));Optional<MoveOnly> B;MoveOnly::ResetCounts();B = std::move(A);EXPECT_TRUE((bool)A);EXPECT_TRUE((bool)B);EXPECT_EQ(3, B->val);EXPECT_EQ(1u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(0u, MoveOnly::Destructions);}TEST(OptionalTest, MoveOnlyNullingAssignment) {Optional<MoveOnly> A;Optional<MoveOnly> B(MoveOnly(3));MoveOnly::ResetCounts();B = std::move(A);EXPECT_FALSE((bool)A);EXPECT_FALSE((bool)B);EXPECT_EQ(0u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(1u, MoveOnly::Destructions);}TEST(OptionalTest, MoveOnlyAssigningAssignment) {Optional<MoveOnly> A(MoveOnly(3));Optional<MoveOnly> B(MoveOnly(4));MoveOnly::ResetCounts();B = std::move(A);EXPECT_TRUE((bool)A);EXPECT_TRUE((bool)B);EXPECT_EQ(3, B->val);EXPECT_EQ(0u, MoveOnly::MoveConstructions);EXPECT_EQ(1u, MoveOnly::MoveAssignments);EXPECT_EQ(0u, MoveOnly::Destructions);}struct Immovable {static unsigned Constructions;static unsigned Destructions;int val;explicit Immovable(int val) : val(val) {++Constructions;}~Immovable() {++Destructions;}static void ResetCounts() {Constructions = 0;Destructions = 0;}private:// This should disable all move/copy operations.Immovable(Immovable&& other) = delete;};unsigned Immovable::Constructions = 0;unsigned Immovable::Destructions = 0;static_assert(!std::is_trivially_copyable<Optional<Immovable>>::value,"not trivially copyable");TEST(OptionalTest, ImmovableEmplace) {Optional<Immovable> A;Immovable::ResetCounts();A.emplace(4);EXPECT_TRUE((bool)A);EXPECT_EQ(4, A->val);EXPECT_EQ(1u, Immovable::Constructions);EXPECT_EQ(0u, Immovable::Destructions);}TEST(OptionalTest, ImmovableInPlaceConstruction) {Immovable::ResetCounts();Optional<Immovable> A{in_place, 4};EXPECT_TRUE((bool)A);EXPECT_EQ(4, A->val);EXPECT_EQ(1u, Immovable::Constructions);EXPECT_EQ(0u, Immovable::Destructions);}// Craft a class which is_trivially_copyable, but not// is_trivially_copy_constructible.struct NonTCopy {NonTCopy() = default;// Delete the volatile copy constructor to engage the "rule of 3" and delete// any unspecified copy assignment or constructor.NonTCopy(volatile NonTCopy const &) = delete;// Leave the non-volatile default copy constructor unspecified (deleted by// rule of 3)// This template can serve as the copy constructor, but isn't chosen// by =default in a class with a 'NonTCopy' member.template <typename Self = NonTCopy>NonTCopy(Self const &Other) : Val(Other.Val) {}NonTCopy &operator=(NonTCopy const &) = default;int Val{0};};#if defined(_MSC_VER) && _MSC_VER >= 1927 && !defined(__clang__)// Currently only true on recent MSVC releases.static_assert(std::is_trivially_copyable<NonTCopy>::value,"Expect NonTCopy to be trivially copyable");static_assert(!std::is_trivially_copy_constructible<NonTCopy>::value,"Expect NonTCopy not to be trivially copy constructible.");#endif // defined(_MSC_VER) && _MSC_VER >= 1927TEST(OptionalTest, DeletedCopyConstructor) {// Expect compile to fail if 'trivial' version of// optional_detail::OptionalStorage is chosen.using NonTCopyOptT = Optional<NonTCopy>;NonTCopyOptT NonTCopy1;// Check that the Optional can be copy constructed.NonTCopyOptT NonTCopy2{NonTCopy1};// Check that the Optional can be copy assigned.NonTCopy1 = NonTCopy2;}// Craft a class which is_trivially_copyable, but not// is_trivially_copy_assignable.class NonTAssign {public:NonTAssign() = default;NonTAssign(NonTAssign const &) = default;// Delete the volatile copy assignment to engage the "rule of 3" and delete// any unspecified copy assignment or constructor.NonTAssign &operator=(volatile NonTAssign const &) = delete;// Leave the non-volatile default copy assignment unspecified (deleted by rule// of 3).// This template can serve as the copy assignment, but isn't chosen// by =default in a class with a 'NonTAssign' member.template <typename Self = NonTAssign>NonTAssign &operator=(Self const &Other) {A = Other.A;return *this;}int A{0};};#if defined(_MSC_VER) && _MSC_VER >= 1927 && !defined(__clang__)// Currently only true on recent MSVC releases.static_assert(std::is_trivially_copyable<NonTAssign>::value,"Expect NonTAssign to be trivially copyable");static_assert(!std::is_trivially_copy_assignable<NonTAssign>::value,"Expect NonTAssign not to be trivially assignable.");#endif // defined(_MSC_VER) && _MSC_VER >= 1927TEST(OptionalTest, DeletedCopyAssignment) {// Expect compile to fail if 'trivial' version of// optional_detail::OptionalStorage is chosen.using NonTAssignOptT = Optional<NonTAssign>;NonTAssignOptT NonTAssign1;// Check that the Optional can be copy constructed.NonTAssignOptT NonTAssign2{NonTAssign1};// Check that the Optional can be copy assigned.NonTAssign1 = NonTAssign2;}struct NoTMove {NoTMove() = default;NoTMove(NoTMove const &) = default;NoTMove &operator=(NoTMove const &) = default;// Delete move constructor / assignment. Compiler should fall-back to the// trivial copy constructor / assignment in the trivial OptionalStorage// specialization.NoTMove(NoTMove &&) = delete;NoTMove &operator=(NoTMove &&) = delete;int Val{0};};TEST(OptionalTest, DeletedMoveConstructor) {using NoTMoveOptT = Optional<NoTMove>;NoTMoveOptT NonTMove1;NoTMoveOptT NonTMove2{std::move(NonTMove1)};NonTMove1 = std::move(NonTMove2);static_assert(std::is_trivially_copyable<NoTMoveOptT>::value,"Expect Optional<NoTMove> to still use the trivial specialization ""of OptionalStorage despite the deleted move constructor / assignment.");}class NoCopyStringMap {public:NoCopyStringMap() = default;private:llvm::StringMap<std::unique_ptr<int>> Map;};TEST(OptionalTest, DeletedCopyStringMap) {// Old versions of gcc (7.3 and prior) instantiate the copy constructor when// std::is_trivially_copyable is instantiated. This test will fail// compilation if std::is_trivially_copyable is used in the OptionalStorage// specialization condition by gcc <= 7.3.Optional<NoCopyStringMap> TestInstantiation;}TEST(OptionalTest, MoveValueOr) {Optional<MoveOnly> A;MoveOnly::ResetCounts();EXPECT_EQ(42, std::move(A).value_or(MoveOnly(42)).val);EXPECT_EQ(1u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(2u, MoveOnly::Destructions);A = MoveOnly(5);MoveOnly::ResetCounts();EXPECT_EQ(5, std::move(A).value_or(MoveOnly(42)).val);EXPECT_EQ(1u, MoveOnly::MoveConstructions);EXPECT_EQ(0u, MoveOnly::MoveAssignments);EXPECT_EQ(2u, MoveOnly::Destructions);}struct EqualTo {template <typename T, typename U> static bool apply(const T &X, const U &Y) {return X == Y;}};struct NotEqualTo {template <typename T, typename U> static bool apply(const T &X, const U &Y) {return X != Y;}};struct Less {template <typename T, typename U> static bool apply(const T &X, const U &Y) {return X < Y;}};struct Greater {template <typename T, typename U> static bool apply(const T &X, const U &Y) {return X > Y;}};struct LessEqual {template <typename T, typename U> static bool apply(const T &X, const U &Y) {return X <= Y;}};struct GreaterEqual {template <typename T, typename U> static bool apply(const T &X, const U &Y) {return X >= Y;}};template <typename OperatorT, typename T>void CheckRelation(const Optional<T> &Lhs, const Optional<T> &Rhs,bool Expected) {EXPECT_EQ(Expected, OperatorT::apply(Lhs, Rhs));if (Lhs)EXPECT_EQ(Expected, OperatorT::apply(*Lhs, Rhs));elseEXPECT_EQ(Expected, OperatorT::apply(None, Rhs));if (Rhs)EXPECT_EQ(Expected, OperatorT::apply(Lhs, *Rhs));elseEXPECT_EQ(Expected, OperatorT::apply(Lhs, None));}struct EqualityMock {};const Optional<EqualityMock> NoneEq, EqualityLhs((EqualityMock())),EqualityRhs((EqualityMock()));bool IsEqual;bool operator==(const EqualityMock &Lhs, const EqualityMock &Rhs) {EXPECT_EQ(&*EqualityLhs, &Lhs);EXPECT_EQ(&*EqualityRhs, &Rhs);return IsEqual;}TEST(OptionalTest, OperatorEqual) {CheckRelation<EqualTo>(NoneEq, NoneEq, true);CheckRelation<EqualTo>(NoneEq, EqualityRhs, false);CheckRelation<EqualTo>(EqualityLhs, NoneEq, false);IsEqual = false;CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual);IsEqual = true;CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual);}TEST(OptionalTest, OperatorNotEqual) {CheckRelation<NotEqualTo>(NoneEq, NoneEq, false);CheckRelation<NotEqualTo>(NoneEq, EqualityRhs, true);CheckRelation<NotEqualTo>(EqualityLhs, NoneEq, true);IsEqual = false;CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual);IsEqual = true;CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual);}struct InequalityMock {};const Optional<InequalityMock> NoneIneq, InequalityLhs((InequalityMock())),InequalityRhs((InequalityMock()));bool IsLess;bool operator<(const InequalityMock &Lhs, const InequalityMock &Rhs) {EXPECT_EQ(&*InequalityLhs, &Lhs);EXPECT_EQ(&*InequalityRhs, &Rhs);return IsLess;}TEST(OptionalTest, OperatorLess) {CheckRelation<Less>(NoneIneq, NoneIneq, false);CheckRelation<Less>(NoneIneq, InequalityRhs, true);CheckRelation<Less>(InequalityLhs, NoneIneq, false);IsLess = false;CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess);IsLess = true;CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess);}TEST(OptionalTest, OperatorGreater) {CheckRelation<Greater>(NoneIneq, NoneIneq, false);CheckRelation<Greater>(NoneIneq, InequalityRhs, false);CheckRelation<Greater>(InequalityLhs, NoneIneq, true);IsLess = false;CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess);IsLess = true;CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess);}TEST(OptionalTest, OperatorLessEqual) {CheckRelation<LessEqual>(NoneIneq, NoneIneq, true);CheckRelation<LessEqual>(NoneIneq, InequalityRhs, true);CheckRelation<LessEqual>(InequalityLhs, NoneIneq, false);IsLess = false;CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess);IsLess = true;CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess);}TEST(OptionalTest, OperatorGreaterEqual) {CheckRelation<GreaterEqual>(NoneIneq, NoneIneq, true);CheckRelation<GreaterEqual>(NoneIneq, InequalityRhs, false);CheckRelation<GreaterEqual>(InequalityLhs, NoneIneq, true);IsLess = false;CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess);IsLess = true;CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess);}struct ComparableAndStreamable {friend bool operator==(ComparableAndStreamable,ComparableAndStreamable) LLVM_ATTRIBUTE_USED {return true;}friend raw_ostream &operator<<(raw_ostream &OS, ComparableAndStreamable) {return OS << "ComparableAndStreamable";}static Optional<ComparableAndStreamable> get() {return ComparableAndStreamable();}};TEST(OptionalTest, StreamOperator) {auto to_string = [](Optional<ComparableAndStreamable> O) {SmallString<16> S;raw_svector_ostream OS(S);OS << O;return S;};EXPECT_EQ("ComparableAndStreamable",to_string(ComparableAndStreamable::get()));EXPECT_EQ("None", to_string(None));}struct Comparable {friend bool operator==(Comparable, Comparable) LLVM_ATTRIBUTE_USED {return true;}static Optional<Comparable> get() { return Comparable(); }};TEST(OptionalTest, UseInUnitTests) {// Test that we invoke the streaming operators when pretty-printing values in// EXPECT macros.EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, ComparableAndStreamable::get()),"Expected equality of these values:\n"" llvm::None\n"" Which is: None\n"" ComparableAndStreamable::get()\n"" Which is: ComparableAndStreamable");// Test that it is still possible to compare objects which do not have a// custom streaming operator.EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, Comparable::get()), "object");}TEST(OptionalTest, HashValue) {// Check that None, false, and true all hash differently.Optional<bool> B, B0 = false, B1 = true;EXPECT_NE(hash_value(B0), hash_value(B));EXPECT_NE(hash_value(B1), hash_value(B));EXPECT_NE(hash_value(B1), hash_value(B0));// Check that None, 0, and 1 all hash differently.Optional<int> I, I0 = 0, I1 = 1;EXPECT_NE(hash_value(I0), hash_value(I));EXPECT_NE(hash_value(I1), hash_value(I));EXPECT_NE(hash_value(I1), hash_value(I0));// Check None hash the same way regardless of type.EXPECT_EQ(hash_value(B), hash_value(I));}struct NotTriviallyCopyable {NotTriviallyCopyable(); // Constructor out-of-line.virtual ~NotTriviallyCopyable() = default;Optional<MoveOnly> MO;};TEST(OptionalTest, GCCIsTriviallyMoveConstructibleCompat) {Optional<NotTriviallyCopyable> V;EXPECT_FALSE(V);}} // end anonymous namespace
//===------ MappedIteratorTest.cpp - Unit tests for mapped_iterator -------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(MappedIteratorTest, ApplyFunctionOnDereference) {std::vector<int> V({0});auto I = map_iterator(V.begin(), [](int X) { return X + 1; });EXPECT_EQ(*I, 1) << "should have applied function in dereference";}TEST(MappedIteratorTest, ApplyFunctionOnArrow) {struct S {int Z = 0;};std::vector<int> V({0});S Y;S* P = &Y;auto I = map_iterator(V.begin(), [&](int X) -> S& { return *(P + X); });I->Z = 42;EXPECT_EQ(Y.Z, 42) << "should have applied function during arrow";}TEST(MappedIteratorTest, FunctionPreservesReferences) {std::vector<int> V({1});std::map<int, int> M({ {1, 1} });auto I = map_iterator(V.begin(), [&](int X) -> int& { return M[X]; });*I = 42;EXPECT_EQ(M[1], 42) << "assignment should have modified M";}TEST(MappedIteratorTest, CustomIteratorApplyFunctionOnDereference) {struct CustomMapIterator: public llvm::mapped_iterator_base<CustomMapIterator,std::vector<int>::iterator, int> {using BaseT::BaseT;/// Map the element to the iterator result type.int mapElement(int X) const { return X + 1; }};std::vector<int> V({0});CustomMapIterator I(V.begin());EXPECT_EQ(*I, 1) << "should have applied function in dereference";}TEST(MappedIteratorTest, CustomIteratorApplyFunctionOnArrow) {struct S {int Z = 0;};struct CustomMapIterator: public llvm::mapped_iterator_base<CustomMapIterator,std::vector<int>::iterator, S &> {CustomMapIterator(std::vector<int>::iterator it, S *P) : BaseT(it), P(P) {}/// Map the element to the iterator result type.S &mapElement(int X) const { return *(P + X); }S *P;};std::vector<int> V({0});S Y;CustomMapIterator I(V.begin(), &Y);I->Z = 42;EXPECT_EQ(Y.Z, 42) << "should have applied function during arrow";}TEST(MappedIteratorTest, CustomIteratorFunctionPreservesReferences) {struct CustomMapIterator: public llvm::mapped_iterator_base<CustomMapIterator,std::vector<int>::iterator, int &> {CustomMapIterator(std::vector<int>::iterator it, std::map<int, int> &M): BaseT(it), M(M) {}/// Map the element to the iterator result type.int &mapElement(int X) const { return M[X]; }std::map<int, int> &M;};std::vector<int> V({1});std::map<int, int> M({{1, 1}});auto I = CustomMapIterator(V.begin(), M);*I = 42;EXPECT_EQ(M[1], 42) << "assignment should have modified M";}} // anonymous namespace
//===- unittest/ADT/MapVectorTest.cpp - MapVector unit tests ----*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/MapVector.h"#include "llvm/ADT/iterator_range.h"#include "gtest/gtest.h"#include <utility>using namespace llvm;TEST(MapVectorTest, swap) {MapVector<int, int> MV1, MV2;std::pair<MapVector<int, int>::iterator, bool> R;R = MV1.insert(std::make_pair(1, 2));ASSERT_EQ(R.first, MV1.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_TRUE(R.second);EXPECT_FALSE(MV1.empty());EXPECT_TRUE(MV2.empty());MV2.swap(MV1);EXPECT_TRUE(MV1.empty());EXPECT_FALSE(MV2.empty());auto I = MV1.find(1);ASSERT_EQ(MV1.end(), I);I = MV2.find(1);ASSERT_EQ(I, MV2.begin());EXPECT_EQ(I->first, 1);EXPECT_EQ(I->second, 2);}TEST(MapVectorTest, insert_pop) {MapVector<int, int> MV;std::pair<MapVector<int, int>::iterator, bool> R;R = MV.insert(std::make_pair(1, 2));ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_TRUE(R.second);R = MV.insert(std::make_pair(1, 3));ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_FALSE(R.second);R = MV.insert(std::make_pair(4, 5));ASSERT_NE(R.first, MV.end());EXPECT_EQ(R.first->first, 4);EXPECT_EQ(R.first->second, 5);EXPECT_TRUE(R.second);EXPECT_EQ(MV.size(), 2u);EXPECT_EQ(MV[1], 2);EXPECT_EQ(MV[4], 5);MV.pop_back();EXPECT_EQ(MV.size(), 1u);EXPECT_EQ(MV[1], 2);R = MV.insert(std::make_pair(4, 7));ASSERT_NE(R.first, MV.end());EXPECT_EQ(R.first->first, 4);EXPECT_EQ(R.first->second, 7);EXPECT_TRUE(R.second);EXPECT_EQ(MV.size(), 2u);EXPECT_EQ(MV[1], 2);EXPECT_EQ(MV[4], 7);}TEST(MapVectorTest, erase) {MapVector<int, int> MV;MV.insert(std::make_pair(1, 2));MV.insert(std::make_pair(3, 4));MV.insert(std::make_pair(5, 6));ASSERT_EQ(MV.size(), 3u);MV.erase(MV.find(1));ASSERT_EQ(MV.size(), 2u);ASSERT_EQ(MV.find(1), MV.end());ASSERT_EQ(MV[3], 4);ASSERT_EQ(MV[5], 6);ASSERT_EQ(MV.erase(3), 1u);ASSERT_EQ(MV.size(), 1u);ASSERT_EQ(MV.find(3), MV.end());ASSERT_EQ(MV[5], 6);ASSERT_EQ(MV.erase(79), 0u);ASSERT_EQ(MV.size(), 1u);}TEST(MapVectorTest, remove_if) {MapVector<int, int> MV;MV.insert(std::make_pair(1, 11));MV.insert(std::make_pair(2, 12));MV.insert(std::make_pair(3, 13));MV.insert(std::make_pair(4, 14));MV.insert(std::make_pair(5, 15));MV.insert(std::make_pair(6, 16));ASSERT_EQ(MV.size(), 6u);MV.remove_if([](const std::pair<int, int> &Val) { return Val.second % 2; });ASSERT_EQ(MV.size(), 3u);ASSERT_EQ(MV.find(1), MV.end());ASSERT_EQ(MV.find(3), MV.end());ASSERT_EQ(MV.find(5), MV.end());ASSERT_EQ(MV[2], 12);ASSERT_EQ(MV[4], 14);ASSERT_EQ(MV[6], 16);}TEST(MapVectorTest, iteration_test) {MapVector<int, int> MV;MV.insert(std::make_pair(1, 11));MV.insert(std::make_pair(2, 12));MV.insert(std::make_pair(3, 13));MV.insert(std::make_pair(4, 14));MV.insert(std::make_pair(5, 15));MV.insert(std::make_pair(6, 16));ASSERT_EQ(MV.size(), 6u);int count = 1;for (auto P : make_range(MV.begin(), MV.end())) {ASSERT_EQ(P.first, count);count++;}count = 6;for (auto P : make_range(MV.rbegin(), MV.rend())) {ASSERT_EQ(P.first, count);count--;}}TEST(MapVectorTest, NonCopyable) {MapVector<int, std::unique_ptr<int>> MV;MV.insert(std::make_pair(1, std::make_unique<int>(1)));MV.insert(std::make_pair(2, std::make_unique<int>(2)));ASSERT_EQ(MV.count(1), 1u);ASSERT_EQ(*MV.find(2)->second, 2);}template <class IntType> struct MapVectorMappedTypeTest : ::testing::Test {using int_type = IntType;};using MapIntTypes = ::testing::Types<int, long, long long, unsigned,unsigned long, unsigned long long>;TYPED_TEST_SUITE(MapVectorMappedTypeTest, MapIntTypes, );TYPED_TEST(MapVectorMappedTypeTest, DifferentDenseMap) {// Test that using a map with a mapped type other than 'unsigned' compiles// and works.using IntType = typename TestFixture::int_type;using MapVectorType = MapVector<int, int, DenseMap<int, IntType>>;MapVectorType MV;std::pair<typename MapVectorType::iterator, bool> R;R = MV.insert(std::make_pair(1, 2));ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_TRUE(R.second);const std::pair<int, int> Elem(1, 3);R = MV.insert(Elem);ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_FALSE(R.second);int& value = MV[4];EXPECT_EQ(value, 0);value = 5;EXPECT_EQ(MV.size(), 2u);EXPECT_EQ(MV[1], 2);EXPECT_EQ(MV[4], 5);}TEST(SmallMapVectorSmallTest, insert_pop) {SmallMapVector<int, int, 32> MV;std::pair<SmallMapVector<int, int, 32>::iterator, bool> R;R = MV.insert(std::make_pair(1, 2));ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_TRUE(R.second);R = MV.insert(std::make_pair(1, 3));ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_FALSE(R.second);R = MV.insert(std::make_pair(4, 5));ASSERT_NE(R.first, MV.end());EXPECT_EQ(R.first->first, 4);EXPECT_EQ(R.first->second, 5);EXPECT_TRUE(R.second);EXPECT_EQ(MV.size(), 2u);EXPECT_EQ(MV[1], 2);EXPECT_EQ(MV[4], 5);MV.pop_back();EXPECT_EQ(MV.size(), 1u);EXPECT_EQ(MV[1], 2);R = MV.insert(std::make_pair(4, 7));ASSERT_NE(R.first, MV.end());EXPECT_EQ(R.first->first, 4);EXPECT_EQ(R.first->second, 7);EXPECT_TRUE(R.second);EXPECT_EQ(MV.size(), 2u);EXPECT_EQ(MV[1], 2);EXPECT_EQ(MV[4], 7);}TEST(SmallMapVectorSmallTest, erase) {SmallMapVector<int, int, 32> MV;MV.insert(std::make_pair(1, 2));MV.insert(std::make_pair(3, 4));MV.insert(std::make_pair(5, 6));ASSERT_EQ(MV.size(), 3u);MV.erase(MV.find(1));ASSERT_EQ(MV.size(), 2u);ASSERT_EQ(MV.find(1), MV.end());ASSERT_EQ(MV[3], 4);ASSERT_EQ(MV[5], 6);ASSERT_EQ(MV.erase(3), 1u);ASSERT_EQ(MV.size(), 1u);ASSERT_EQ(MV.find(3), MV.end());ASSERT_EQ(MV[5], 6);ASSERT_EQ(MV.erase(79), 0u);ASSERT_EQ(MV.size(), 1u);}TEST(SmallMapVectorSmallTest, remove_if) {SmallMapVector<int, int, 32> MV;MV.insert(std::make_pair(1, 11));MV.insert(std::make_pair(2, 12));MV.insert(std::make_pair(3, 13));MV.insert(std::make_pair(4, 14));MV.insert(std::make_pair(5, 15));MV.insert(std::make_pair(6, 16));ASSERT_EQ(MV.size(), 6u);MV.remove_if([](const std::pair<int, int> &Val) { return Val.second % 2; });ASSERT_EQ(MV.size(), 3u);ASSERT_EQ(MV.find(1), MV.end());ASSERT_EQ(MV.find(3), MV.end());ASSERT_EQ(MV.find(5), MV.end());ASSERT_EQ(MV[2], 12);ASSERT_EQ(MV[4], 14);ASSERT_EQ(MV[6], 16);}TEST(SmallMapVectorSmallTest, iteration_test) {SmallMapVector<int, int, 32> MV;MV.insert(std::make_pair(1, 11));MV.insert(std::make_pair(2, 12));MV.insert(std::make_pair(3, 13));MV.insert(std::make_pair(4, 14));MV.insert(std::make_pair(5, 15));MV.insert(std::make_pair(6, 16));ASSERT_EQ(MV.size(), 6u);int count = 1;for (auto P : make_range(MV.begin(), MV.end())) {ASSERT_EQ(P.first, count);count++;}count = 6;for (auto P : make_range(MV.rbegin(), MV.rend())) {ASSERT_EQ(P.first, count);count--;}}TEST(SmallMapVectorSmallTest, NonCopyable) {SmallMapVector<int, std::unique_ptr<int>, 8> MV;MV.insert(std::make_pair(1, std::make_unique<int>(1)));MV.insert(std::make_pair(2, std::make_unique<int>(2)));ASSERT_EQ(MV.count(1), 1u);ASSERT_EQ(*MV.find(2)->second, 2);}TEST(SmallMapVectorLargeTest, insert_pop) {SmallMapVector<int, int, 1> MV;std::pair<SmallMapVector<int, int, 1>::iterator, bool> R;R = MV.insert(std::make_pair(1, 2));ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_TRUE(R.second);R = MV.insert(std::make_pair(1, 3));ASSERT_EQ(R.first, MV.begin());EXPECT_EQ(R.first->first, 1);EXPECT_EQ(R.first->second, 2);EXPECT_FALSE(R.second);R = MV.insert(std::make_pair(4, 5));ASSERT_NE(R.first, MV.end());EXPECT_EQ(R.first->first, 4);EXPECT_EQ(R.first->second, 5);EXPECT_TRUE(R.second);EXPECT_EQ(MV.size(), 2u);EXPECT_EQ(MV[1], 2);EXPECT_EQ(MV[4], 5);MV.pop_back();EXPECT_EQ(MV.size(), 1u);EXPECT_EQ(MV[1], 2);R = MV.insert(std::make_pair(4, 7));ASSERT_NE(R.first, MV.end());EXPECT_EQ(R.first->first, 4);EXPECT_EQ(R.first->second, 7);EXPECT_TRUE(R.second);EXPECT_EQ(MV.size(), 2u);EXPECT_EQ(MV[1], 2);EXPECT_EQ(MV[4], 7);}TEST(SmallMapVectorLargeTest, erase) {SmallMapVector<int, int, 1> MV;MV.insert(std::make_pair(1, 2));MV.insert(std::make_pair(3, 4));MV.insert(std::make_pair(5, 6));ASSERT_EQ(MV.size(), 3u);MV.erase(MV.find(1));ASSERT_EQ(MV.size(), 2u);ASSERT_EQ(MV.find(1), MV.end());ASSERT_EQ(MV[3], 4);ASSERT_EQ(MV[5], 6);ASSERT_EQ(MV.erase(3), 1u);ASSERT_EQ(MV.size(), 1u);ASSERT_EQ(MV.find(3), MV.end());ASSERT_EQ(MV[5], 6);ASSERT_EQ(MV.erase(79), 0u);ASSERT_EQ(MV.size(), 1u);}TEST(SmallMapVectorLargeTest, remove_if) {SmallMapVector<int, int, 1> MV;MV.insert(std::make_pair(1, 11));MV.insert(std::make_pair(2, 12));MV.insert(std::make_pair(3, 13));MV.insert(std::make_pair(4, 14));MV.insert(std::make_pair(5, 15));MV.insert(std::make_pair(6, 16));ASSERT_EQ(MV.size(), 6u);MV.remove_if([](const std::pair<int, int> &Val) { return Val.second % 2; });ASSERT_EQ(MV.size(), 3u);ASSERT_EQ(MV.find(1), MV.end());ASSERT_EQ(MV.find(3), MV.end());ASSERT_EQ(MV.find(5), MV.end());ASSERT_EQ(MV[2], 12);ASSERT_EQ(MV[4], 14);ASSERT_EQ(MV[6], 16);}TEST(SmallMapVectorLargeTest, iteration_test) {SmallMapVector<int, int, 1> MV;MV.insert(std::make_pair(1, 11));MV.insert(std::make_pair(2, 12));MV.insert(std::make_pair(3, 13));MV.insert(std::make_pair(4, 14));MV.insert(std::make_pair(5, 15));MV.insert(std::make_pair(6, 16));ASSERT_EQ(MV.size(), 6u);int count = 1;for (auto P : make_range(MV.begin(), MV.end())) {ASSERT_EQ(P.first, count);count++;}count = 6;for (auto P : make_range(MV.rbegin(), MV.rend())) {ASSERT_EQ(P.first, count);count--;}}
//===- IteratorTest.cpp - Unit tests for iterator utilities ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ilist.h"#include "llvm/ADT/iterator.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/SmallVector.h"#include "gtest/gtest.h"using namespace llvm;namespace {template <int> struct Shadow;struct WeirdIter : std::iterator<std::input_iterator_tag, Shadow<0>, Shadow<1>,Shadow<2>, Shadow<3>> {};struct AdaptedIter : iterator_adaptor_base<AdaptedIter, WeirdIter> {};// Test that iterator_adaptor_base forwards typedefs, if value_type is// unchanged.static_assert(std::is_same<typename AdaptedIter::value_type, Shadow<0>>::value,"");static_assert(std::is_same<typename AdaptedIter::difference_type, Shadow<1>>::value, "");static_assert(std::is_same<typename AdaptedIter::pointer, Shadow<2>>::value,"");static_assert(std::is_same<typename AdaptedIter::reference, Shadow<3>>::value,"");// Ensure that pointe{e,r}_iterator adaptors correctly forward the category of// the underlying iterator.using RandomAccessIter = SmallVectorImpl<int*>::iterator;using BidiIter = ilist<int*>::iterator;template<class T>using pointee_iterator_defaulted = pointee_iterator<T>;template<class T>using pointer_iterator_defaulted = pointer_iterator<T>;// Ensures that an iterator and its adaptation have the same iterator_category.template<template<typename> class A, typename It>using IsAdaptedIterCategorySame =std::is_same<typename std::iterator_traits<It>::iterator_category,typename std::iterator_traits<A<It>>::iterator_category>;// Check that dereferencing works correctly adapting pointers and proxies.template <class T>struct PointerWrapper : public iterator_adaptor_base<PointerWrapper<T>, T *> {PointerWrapper(T *I) : PointerWrapper::iterator_adaptor_base(I) {}};struct IntProxy {int &I;IntProxy(int &I) : I(I) {}void operator=(int NewValue) { I = NewValue; }};struct ConstIntProxy {const int &I;ConstIntProxy(const int &I) : I(I) {}};template <class T, class ProxyT>struct PointerProxyWrapper: public iterator_adaptor_base<PointerProxyWrapper<T, ProxyT>, T *,std::random_access_iterator_tag, T,ptrdiff_t, T *, ProxyT> {PointerProxyWrapper(T *I) : PointerProxyWrapper::iterator_adaptor_base(I) {}};using IntIterator = PointerWrapper<int>;using ConstIntIterator = PointerWrapper<const int>;using IntProxyIterator = PointerProxyWrapper<int, IntProxy>;using ConstIntProxyIterator = PointerProxyWrapper<const int, ConstIntProxy>;// There should only be a single (const-qualified) operator*, operator->, and// operator[]. This test confirms that there isn't a non-const overload. Rather// than adding those, users should double-check that T, PointerT, and ReferenceT// have the right constness, and/or make fields mutable.static_assert(&IntIterator::operator* == &IntIterator::operator*, "");static_assert(&IntIterator::operator-> == &IntIterator::operator->, "");static_assert(&IntIterator::operator[] == &IntIterator::operator[], "");template <class T,std::enable_if_t<std::is_assignable<T, int>::value, bool> = false>constexpr bool canAssignFromInt(T &&) {return true;}template <class T,std::enable_if_t<!std::is_assignable<T, int>::value, bool> = false>constexpr bool canAssignFromInt(T &&) {return false;}TEST(IteratorAdaptorTest, Dereference) {int Number = 1;// Construct some iterators and check whether they can be assigned to.IntIterator I(&Number);const IntIterator IC(&Number);ConstIntIterator CI(&Number);const ConstIntIterator CIC(&Number);EXPECT_EQ(true, canAssignFromInt(*I)); // int *EXPECT_EQ(true, canAssignFromInt(*IC)); // int *constEXPECT_EQ(false, canAssignFromInt(*CI)); // const int *EXPECT_EQ(false, canAssignFromInt(*CIC)); // const int *const// Prove that dereference and assignment work.EXPECT_EQ(1, *I);EXPECT_EQ(1, *IC);EXPECT_EQ(1, *CI);EXPECT_EQ(1, *CIC);*I = 2;EXPECT_EQ(2, Number);*IC = 3;EXPECT_EQ(3, Number);// Construct some proxy iterators and check whether they can be assigned to.IntProxyIterator P(&Number);const IntProxyIterator PC(&Number);ConstIntProxyIterator CP(&Number);const ConstIntProxyIterator CPC(&Number);EXPECT_EQ(true, canAssignFromInt(*P)); // int *EXPECT_EQ(true, canAssignFromInt(*PC)); // int *constEXPECT_EQ(false, canAssignFromInt(*CP)); // const int *EXPECT_EQ(false, canAssignFromInt(*CPC)); // const int *const// Prove that dereference and assignment work.EXPECT_EQ(3, (*P).I);EXPECT_EQ(3, (*PC).I);EXPECT_EQ(3, (*CP).I);EXPECT_EQ(3, (*CPC).I);*P = 4;EXPECT_EQ(4, Number);*PC = 5;EXPECT_EQ(5, Number);}// pointeE_iteratorstatic_assert(IsAdaptedIterCategorySame<pointee_iterator_defaulted,RandomAccessIter>::value, "");static_assert(IsAdaptedIterCategorySame<pointee_iterator_defaulted,BidiIter>::value, "");// pointeR_iteratorstatic_assert(IsAdaptedIterCategorySame<pointer_iterator_defaulted,RandomAccessIter>::value, "");static_assert(IsAdaptedIterCategorySame<pointer_iterator_defaulted,BidiIter>::value, "");TEST(PointeeIteratorTest, Basic) {int arr[4] = {1, 2, 3, 4};SmallVector<int *, 4> V;V.push_back(&arr[0]);V.push_back(&arr[1]);V.push_back(&arr[2]);V.push_back(&arr[3]);typedef pointee_iterator<SmallVectorImpl<int *>::const_iterator>test_iterator;test_iterator Begin, End;Begin = V.begin();End = test_iterator(V.end());test_iterator I = Begin;for (int i = 0; i < 4; ++i) {EXPECT_EQ(*V[i], *I);EXPECT_EQ(I, Begin + i);EXPECT_EQ(I, std::next(Begin, i));test_iterator J = Begin;J += i;EXPECT_EQ(I, J);EXPECT_EQ(*V[i], Begin[i]);EXPECT_NE(I, End);EXPECT_GT(End, I);EXPECT_LT(I, End);EXPECT_GE(I, Begin);EXPECT_LE(Begin, I);EXPECT_EQ(i, I - Begin);EXPECT_EQ(i, std::distance(Begin, I));EXPECT_EQ(Begin, I - i);test_iterator K = I++;EXPECT_EQ(K, std::prev(I));}EXPECT_EQ(End, I);}TEST(PointeeIteratorTest, SmartPointer) {SmallVector<std::unique_ptr<int>, 4> V;V.push_back(std::make_unique<int>(1));V.push_back(std::make_unique<int>(2));V.push_back(std::make_unique<int>(3));V.push_back(std::make_unique<int>(4));typedef pointee_iterator<SmallVectorImpl<std::unique_ptr<int>>::const_iterator>test_iterator;test_iterator Begin, End;Begin = V.begin();End = test_iterator(V.end());test_iterator I = Begin;for (int i = 0; i < 4; ++i) {EXPECT_EQ(*V[i], *I);EXPECT_EQ(I, Begin + i);EXPECT_EQ(I, std::next(Begin, i));test_iterator J = Begin;J += i;EXPECT_EQ(I, J);EXPECT_EQ(*V[i], Begin[i]);EXPECT_NE(I, End);EXPECT_GT(End, I);EXPECT_LT(I, End);EXPECT_GE(I, Begin);EXPECT_LE(Begin, I);EXPECT_EQ(i, I - Begin);EXPECT_EQ(i, std::distance(Begin, I));EXPECT_EQ(Begin, I - i);test_iterator K = I++;EXPECT_EQ(K, std::prev(I));}EXPECT_EQ(End, I);}TEST(PointeeIteratorTest, Range) {int A[] = {1, 2, 3, 4};SmallVector<int *, 4> V{&A[0], &A[1], &A[2], &A[3]};int I = 0;for (int II : make_pointee_range(V))EXPECT_EQ(A[I++], II);}TEST(PointeeIteratorTest, PointeeType) {struct S {int X;bool operator==(const S &RHS) const { return X == RHS.X; };};S A[] = {S{0}, S{1}};SmallVector<S *, 2> V{&A[0], &A[1]};pointee_iterator<SmallVectorImpl<S *>::const_iterator, const S> I = V.begin();for (int j = 0; j < 2; ++j, ++I) {EXPECT_EQ(*V[j], *I);}}TEST(FilterIteratorTest, Lambda) {auto IsOdd = [](int N) { return N % 2 == 1; };int A[] = {0, 1, 2, 3, 4, 5, 6};auto Range = make_filter_range(A, IsOdd);SmallVector<int, 3> Actual(Range.begin(), Range.end());EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);}TEST(FilterIteratorTest, Enumerate) {auto IsOdd = [](auto N) { return N.value() % 2 == 1; };int A[] = {0, 1, 2, 3, 4, 5, 6};auto Enumerate = llvm::enumerate(A);SmallVector<int> Actual;for (auto IndexedValue : make_filter_range(Enumerate, IsOdd))Actual.push_back(IndexedValue.value());EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);}TEST(FilterIteratorTest, CallableObject) {int Counter = 0;struct Callable {int &Counter;Callable(int &Counter) : Counter(Counter) {}bool operator()(int N) {Counter++;return N % 2 == 1;}};Callable IsOdd(Counter);int A[] = {0, 1, 2, 3, 4, 5, 6};auto Range = make_filter_range(A, IsOdd);EXPECT_EQ(2, Counter);SmallVector<int, 3> Actual(Range.begin(), Range.end());EXPECT_GE(Counter, 7);EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);}TEST(FilterIteratorTest, FunctionPointer) {bool (*IsOdd)(int) = [](int N) { return N % 2 == 1; };int A[] = {0, 1, 2, 3, 4, 5, 6};auto Range = make_filter_range(A, IsOdd);SmallVector<int, 3> Actual(Range.begin(), Range.end());EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);}TEST(FilterIteratorTest, Composition) {auto IsOdd = [](int N) { return N % 2 == 1; };std::unique_ptr<int> A[] = {std::make_unique<int>(0), std::make_unique<int>(1),std::make_unique<int>(2), std::make_unique<int>(3),std::make_unique<int>(4), std::make_unique<int>(5),std::make_unique<int>(6)};using PointeeIterator = pointee_iterator<std::unique_ptr<int> *>;auto Range = make_filter_range(make_range(PointeeIterator(std::begin(A)), PointeeIterator(std::end(A))),IsOdd);SmallVector<int, 3> Actual(Range.begin(), Range.end());EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);}TEST(FilterIteratorTest, InputIterator) {struct InputIterator: iterator_adaptor_base<InputIterator, int *, std::input_iterator_tag> {InputIterator(int *It) : InputIterator::iterator_adaptor_base(It) {}};auto IsOdd = [](int N) { return N % 2 == 1; };int A[] = {0, 1, 2, 3, 4, 5, 6};auto Range = make_filter_range(make_range(InputIterator(std::begin(A)), InputIterator(std::end(A))),IsOdd);SmallVector<int, 3> Actual(Range.begin(), Range.end());EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);}TEST(FilterIteratorTest, ReverseFilterRange) {auto IsOdd = [](int N) { return N % 2 == 1; };int A[] = {0, 1, 2, 3, 4, 5, 6};// Check basic reversal.auto Range = reverse(make_filter_range(A, IsOdd));SmallVector<int, 3> Actual(Range.begin(), Range.end());EXPECT_EQ((SmallVector<int, 3>{5, 3, 1}), Actual);// Check that the reverse of the reverse is the original.auto Range2 = reverse(reverse(make_filter_range(A, IsOdd)));SmallVector<int, 3> Actual2(Range2.begin(), Range2.end());EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual2);// Check empty ranges.auto Range3 = reverse(make_filter_range(ArrayRef<int>(), IsOdd));SmallVector<int, 0> Actual3(Range3.begin(), Range3.end());EXPECT_EQ((SmallVector<int, 0>{}), Actual3);// Check that we don't skip the first element, provided it isn't filtered// away.auto IsEven = [](int N) { return N % 2 == 0; };auto Range4 = reverse(make_filter_range(A, IsEven));SmallVector<int, 4> Actual4(Range4.begin(), Range4.end());EXPECT_EQ((SmallVector<int, 4>{6, 4, 2, 0}), Actual4);}TEST(PointerIterator, Basic) {int A[] = {1, 2, 3, 4};pointer_iterator<int *> Begin(std::begin(A)), End(std::end(A));EXPECT_EQ(A, *Begin);++Begin;EXPECT_EQ(A + 1, *Begin);++Begin;EXPECT_EQ(A + 2, *Begin);++Begin;EXPECT_EQ(A + 3, *Begin);++Begin;EXPECT_EQ(Begin, End);}TEST(PointerIterator, Const) {int A[] = {1, 2, 3, 4};const pointer_iterator<int *> Begin(std::begin(A));EXPECT_EQ(A, *Begin);EXPECT_EQ(A + 1, std::next(*Begin, 1));EXPECT_EQ(A + 2, std::next(*Begin, 2));EXPECT_EQ(A + 3, std::next(*Begin, 3));EXPECT_EQ(A + 4, std::next(*Begin, 4));}TEST(PointerIterator, Range) {int A[] = {1, 2, 3, 4};int I = 0;for (int *P : make_pointer_range(A))EXPECT_EQ(A + I++, P);}TEST(ZipIteratorTest, Basic) {using namespace std;const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9};SmallVector<bool, 6> odd{1, 1, 0, 1, 1, 1};const char message[] = "yynyyy\0";for (auto tup : zip(pi, odd, message)) {EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup));EXPECT_EQ(get<0>(tup) & 0x01 ? 'y' : 'n', get<2>(tup));}// note the rvaluefor (auto tup : zip(pi, SmallVector<bool, 0>{1, 1, 0, 1, 1})) {EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup));}}TEST(ZipIteratorTest, ZipFirstBasic) {using namespace std;const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9};unsigned iters = 0;for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) {EXPECT_EQ(get<0>(tup), get<1>(tup) & 0x01);iters += 1;}EXPECT_EQ(iters, 4u);}TEST(ZipIteratorTest, ZipLongestBasic) {using namespace std;const vector<unsigned> pi{3, 1, 4, 1, 5, 9};const vector<StringRef> e{"2", "7", "1", "8"};{// Check left range longer than right.const vector<tuple<Optional<unsigned>, Optional<StringRef>>> expected{make_tuple(3, StringRef("2")), make_tuple(1, StringRef("7")),make_tuple(4, StringRef("1")), make_tuple(1, StringRef("8")),make_tuple(5, None), make_tuple(9, None)};size_t iters = 0;for (auto tup : zip_longest(pi, e)) {EXPECT_EQ(tup, expected[iters]);iters += 1;}EXPECT_EQ(iters, expected.size());}{// Check right range longer than left.const vector<tuple<Optional<StringRef>, Optional<unsigned>>> expected{make_tuple(StringRef("2"), 3), make_tuple(StringRef("7"), 1),make_tuple(StringRef("1"), 4), make_tuple(StringRef("8"), 1),make_tuple(None, 5), make_tuple(None, 9)};size_t iters = 0;for (auto tup : zip_longest(e, pi)) {EXPECT_EQ(tup, expected[iters]);iters += 1;}EXPECT_EQ(iters, expected.size());}}TEST(ZipIteratorTest, Mutability) {using namespace std;const SmallVector<unsigned, 4> pi{3, 1, 4, 1, 5, 9};char message[] = "hello zip\0";for (auto tup : zip(pi, message, message)) {EXPECT_EQ(get<1>(tup), get<2>(tup));get<2>(tup) = get<0>(tup) & 0x01 ? 'y' : 'n';}// note the rvaluefor (auto tup : zip(message, "yynyyyzip\0")) {EXPECT_EQ(get<0>(tup), get<1>(tup));}}TEST(ZipIteratorTest, ZipFirstMutability) {using namespace std;vector<unsigned> pi{3, 1, 4, 1, 5, 9};unsigned iters = 0;for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) {get<1>(tup) = get<0>(tup);iters += 1;}EXPECT_EQ(iters, 4u);for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) {EXPECT_EQ(get<0>(tup), get<1>(tup));}}TEST(ZipIteratorTest, Filter) {using namespace std;vector<unsigned> pi{3, 1, 4, 1, 5, 9};unsigned iters = 0;// pi is length 6, but the zip RHS is length 7.auto zipped = zip_first(pi, vector<bool>{1, 1, 0, 1, 1, 1, 0});for (auto tup : make_filter_range(zipped, [](decltype(zipped)::value_type t) { return get<1>(t); })) {EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup));get<0>(tup) += 1;iters += 1;}// Should have skipped pi[2].EXPECT_EQ(iters, 5u);// Ensure that in-place mutation works.EXPECT_TRUE(all_of(pi, [](unsigned n) { return (n & 0x01) == 0; }));}TEST(ZipIteratorTest, Reverse) {using namespace std;vector<unsigned> ascending{0, 1, 2, 3, 4, 5};auto zipped = zip_first(ascending, vector<bool>{0, 1, 0, 1, 0, 1});unsigned last = 6;for (auto tup : reverse(zipped)) {// Check that this is in reverse.EXPECT_LT(get<0>(tup), last);last = get<0>(tup);EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup));}auto odds = [](decltype(zipped)::value_type tup) { return get<1>(tup); };last = 6;for (auto tup : make_filter_range(reverse(zipped), odds)) {EXPECT_LT(get<0>(tup), last);last = get<0>(tup);EXPECT_TRUE(get<0>(tup) & 0x01);get<0>(tup) += 1;}// Ensure that in-place mutation works.EXPECT_TRUE(all_of(ascending, [](unsigned n) { return (n & 0x01) == 0; }));}TEST(RangeTest, Distance) {std::vector<int> v1;std::vector<int> v2{1, 2, 3};EXPECT_EQ(std::distance(v1.begin(), v1.end()), size(v1));EXPECT_EQ(std::distance(v2.begin(), v2.end()), size(v2));}} // anonymous namespace
//===- unittest/ADT/IntrusiveRefCntPtrTest.cpp ----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/IntrusiveRefCntPtr.h"#include "gtest/gtest.h"namespace llvm {namespace {int NumInstances = 0;template <template <typename> class Base>struct SimpleRefCounted : Base<SimpleRefCounted<Base>> {SimpleRefCounted() { ++NumInstances; }SimpleRefCounted(const SimpleRefCounted &RHS) : Base<SimpleRefCounted>(RHS) {++NumInstances;}~SimpleRefCounted() { --NumInstances; }};} // anonymous namespacetemplate <typename T> struct IntrusiveRefCntPtrTest : testing::Test {};typedef ::testing::Types<SimpleRefCounted<RefCountedBase>,SimpleRefCounted<ThreadSafeRefCountedBase>>IntrusiveRefCntTypes;TYPED_TEST_SUITE(IntrusiveRefCntPtrTest, IntrusiveRefCntTypes, );TYPED_TEST(IntrusiveRefCntPtrTest, RefCountedBaseCopyDoesNotLeak) {EXPECT_EQ(0, NumInstances);{TypeParam *S1 = new TypeParam;IntrusiveRefCntPtr<TypeParam> R1 = S1;TypeParam *S2 = new TypeParam(*S1);IntrusiveRefCntPtr<TypeParam> R2 = S2;EXPECT_EQ(2, NumInstances);}EXPECT_EQ(0, NumInstances);}TYPED_TEST(IntrusiveRefCntPtrTest, InteropsWithUniquePtr) {EXPECT_EQ(0, NumInstances);{auto S1 = std::make_unique<TypeParam>();IntrusiveRefCntPtr<TypeParam> R1 = std::move(S1);EXPECT_EQ(1, NumInstances);EXPECT_EQ(S1, nullptr);}EXPECT_EQ(0, NumInstances);}TYPED_TEST(IntrusiveRefCntPtrTest, MakeIntrusiveRefCnt) {EXPECT_EQ(0, NumInstances);{auto S1 = makeIntrusiveRefCnt<TypeParam>();auto S2 = makeIntrusiveRefCnt<const TypeParam>();EXPECT_EQ(2, NumInstances);static_assert(std::is_same<decltype(S1), IntrusiveRefCntPtr<TypeParam>>::value,"Non-const type mismatch");static_assert(std::is_same<decltype(S2), IntrusiveRefCntPtr<const TypeParam>>::value,"Const type mismatch");}EXPECT_EQ(0, NumInstances);}struct InterceptRefCounted : public RefCountedBase<InterceptRefCounted> {InterceptRefCounted(bool *Released, bool *Retained): Released(Released), Retained(Retained) {}bool * const Released;bool * const Retained;};template <> struct IntrusiveRefCntPtrInfo<InterceptRefCounted> {static void retain(InterceptRefCounted *I) {*I->Retained = true;I->Retain();}static void release(InterceptRefCounted *I) {*I->Released = true;I->Release();}};TEST(IntrusiveRefCntPtr, UsesTraitsToRetainAndRelease) {bool Released = false;bool Retained = false;{InterceptRefCounted *I = new InterceptRefCounted(&Released, &Retained);IntrusiveRefCntPtr<InterceptRefCounted> R = I;}EXPECT_TRUE(Released);EXPECT_TRUE(Retained);}// Test that the generic constructors use SFINAE to disable invalid// conversions.struct X : RefCountedBase<X> {};struct Y : X {};struct Z : RefCountedBase<Z> {};static_assert(!std::is_convertible<IntrusiveRefCntPtr<X> &&,IntrusiveRefCntPtr<Y>>::value,"X&& -> Y should be rejected with SFINAE");static_assert(!std::is_convertible<const IntrusiveRefCntPtr<X> &,IntrusiveRefCntPtr<Y>>::value,"const X& -> Y should be rejected with SFINAE");static_assert(!std::is_convertible<std::unique_ptr<X>, IntrusiveRefCntPtr<Y>>::value,"X -> Y should be rejected with SFINAE");static_assert(!std::is_convertible<IntrusiveRefCntPtr<X> &&,IntrusiveRefCntPtr<Z>>::value,"X&& -> Z should be rejected with SFINAE");static_assert(!std::is_convertible<const IntrusiveRefCntPtr<X> &,IntrusiveRefCntPtr<Z>>::value,"const X& -> Z should be rejected with SFINAE");static_assert(!std::is_convertible<std::unique_ptr<X>, IntrusiveRefCntPtr<Z>>::value,"X -> Z should be rejected with SFINAE");TEST(IntrusiveRefCntPtr, InteropsWithConvertible) {// Check converting constructors and operator=.auto Y1 = makeIntrusiveRefCnt<Y>();auto Y2 = makeIntrusiveRefCnt<Y>();auto Y3 = makeIntrusiveRefCnt<Y>();auto Y4 = makeIntrusiveRefCnt<Y>();const void *P1 = Y1.get();const void *P2 = Y2.get();const void *P3 = Y3.get();const void *P4 = Y4.get();IntrusiveRefCntPtr<X> X1 = std::move(Y1);IntrusiveRefCntPtr<X> X2 = Y2;IntrusiveRefCntPtr<X> X3;IntrusiveRefCntPtr<X> X4;X3 = std::move(Y3);X4 = Y4;EXPECT_EQ(P1, X1.get());EXPECT_EQ(P2, X2.get());EXPECT_EQ(P3, X3.get());EXPECT_EQ(P4, X4.get());}} // end namespace llvm
//===---- ADT/IntervalMapTest.cpp - IntervalMap unit tests ------*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/IntervalMap.h"#include "gtest/gtest.h"#include <type_traits>using namespace llvm;namespace {typedef IntervalMap<unsigned, unsigned, 4> UUMap;typedef IntervalMap<unsigned, unsigned, 4,IntervalMapHalfOpenInfo<unsigned>> UUHalfOpenMap;static_assert(!std::is_copy_constructible<UUMap>::value,"IntervalMap copy constructor should be deleted");static_assert(!std::is_move_constructible<UUMap>::value,"IntervalMap move constructor should be deleted");static_assert(!std::is_copy_assignable<UUMap>::value,"IntervalMap copy assignment should be deleted");static_assert(!std::is_move_assignable<UUMap>::value,"IntervalMap move assignment should be deleted");// Empty map testsTEST(IntervalMapTest, EmptyMap) {UUMap::Allocator allocator;UUMap map(allocator);EXPECT_TRUE(map.empty());// Lookup on empty map.EXPECT_EQ(0u, map.lookup(0));EXPECT_EQ(7u, map.lookup(0, 7));EXPECT_EQ(0u, map.lookup(~0u-1));EXPECT_EQ(7u, map.lookup(~0u-1, 7));// Iterators.EXPECT_TRUE(map.begin() == map.begin());EXPECT_TRUE(map.begin() == map.end());EXPECT_TRUE(map.end() == map.end());EXPECT_FALSE(map.begin() != map.begin());EXPECT_FALSE(map.begin() != map.end());EXPECT_FALSE(map.end() != map.end());EXPECT_FALSE(map.begin().valid());EXPECT_FALSE(map.end().valid());UUMap::iterator I = map.begin();EXPECT_FALSE(I.valid());EXPECT_TRUE(I == map.end());// Default constructor and cross-constness compares.UUMap::const_iterator CI;CI = map.begin();EXPECT_TRUE(CI == I);UUMap::iterator I2;I2 = map.end();EXPECT_TRUE(I2 == CI);}// Test one-element closed ranges.TEST(IntervalMapTest, OneElementRanges) {UUMap::Allocator allocator;UUMap map(allocator);map.insert(1, 1, 1);map.insert(2, 2, 2);EXPECT_EQ(1u, map.lookup(1));EXPECT_EQ(2u, map.lookup(2));}// Single entry map testsTEST(IntervalMapTest, SingleEntryMap) {UUMap::Allocator allocator;UUMap map(allocator);map.insert(100, 150, 1);EXPECT_FALSE(map.empty());// Lookup around interval.EXPECT_EQ(0u, map.lookup(0));EXPECT_EQ(0u, map.lookup(99));EXPECT_EQ(1u, map.lookup(100));EXPECT_EQ(1u, map.lookup(101));EXPECT_EQ(1u, map.lookup(125));EXPECT_EQ(1u, map.lookup(149));EXPECT_EQ(1u, map.lookup(150));EXPECT_EQ(0u, map.lookup(151));EXPECT_EQ(0u, map.lookup(200));EXPECT_EQ(0u, map.lookup(~0u-1));// Iterators.EXPECT_TRUE(map.begin() == map.begin());EXPECT_FALSE(map.begin() == map.end());EXPECT_TRUE(map.end() == map.end());EXPECT_TRUE(map.begin().valid());EXPECT_FALSE(map.end().valid());// Iter deref.UUMap::iterator I = map.begin();ASSERT_TRUE(I.valid());EXPECT_EQ(100u, I.start());EXPECT_EQ(150u, I.stop());EXPECT_EQ(1u, I.value());// Preincrement.++I;EXPECT_FALSE(I.valid());EXPECT_FALSE(I == map.begin());EXPECT_TRUE(I == map.end());// PreDecrement.--I;ASSERT_TRUE(I.valid());EXPECT_EQ(100u, I.start());EXPECT_EQ(150u, I.stop());EXPECT_EQ(1u, I.value());EXPECT_TRUE(I == map.begin());EXPECT_FALSE(I == map.end());// Change the value.I.setValue(2);ASSERT_TRUE(I.valid());EXPECT_EQ(100u, I.start());EXPECT_EQ(150u, I.stop());EXPECT_EQ(2u, I.value());// Grow the bounds.I.setStart(0);ASSERT_TRUE(I.valid());EXPECT_EQ(0u, I.start());EXPECT_EQ(150u, I.stop());EXPECT_EQ(2u, I.value());I.setStop(200);ASSERT_TRUE(I.valid());EXPECT_EQ(0u, I.start());EXPECT_EQ(200u, I.stop());EXPECT_EQ(2u, I.value());// Shrink the bounds.I.setStart(150);ASSERT_TRUE(I.valid());EXPECT_EQ(150u, I.start());EXPECT_EQ(200u, I.stop());EXPECT_EQ(2u, I.value());// Shrink the interval to have a length of 1I.setStop(150);ASSERT_TRUE(I.valid());EXPECT_EQ(150u, I.start());EXPECT_EQ(150u, I.stop());EXPECT_EQ(2u, I.value());I.setStop(160);ASSERT_TRUE(I.valid());EXPECT_EQ(150u, I.start());EXPECT_EQ(160u, I.stop());EXPECT_EQ(2u, I.value());// Shrink the interval to have a length of 1I.setStart(160);ASSERT_TRUE(I.valid());EXPECT_EQ(160u, I.start());EXPECT_EQ(160u, I.stop());EXPECT_EQ(2u, I.value());// Erase last elem.I.erase();EXPECT_TRUE(map.empty());EXPECT_EQ(0, std::distance(map.begin(), map.end()));}// Single entry half-open map testsTEST(IntervalMapTest, SingleEntryHalfOpenMap) {UUHalfOpenMap::Allocator allocator;UUHalfOpenMap map(allocator);map.insert(100, 150, 1);EXPECT_FALSE(map.empty());UUHalfOpenMap::iterator I = map.begin();ASSERT_TRUE(I.valid());// Shrink the interval to have a length of 1I.setStart(149);ASSERT_TRUE(I.valid());EXPECT_EQ(149u, I.start());EXPECT_EQ(150u, I.stop());EXPECT_EQ(1u, I.value());I.setStop(160);ASSERT_TRUE(I.valid());EXPECT_EQ(149u, I.start());EXPECT_EQ(160u, I.stop());EXPECT_EQ(1u, I.value());// Shrink the interval to have a length of 1I.setStop(150);ASSERT_TRUE(I.valid());EXPECT_EQ(149u, I.start());EXPECT_EQ(150u, I.stop());EXPECT_EQ(1u, I.value());}// Flat coalescing tests.TEST(IntervalMapTest, RootCoalescing) {UUMap::Allocator allocator;UUMap map(allocator);map.insert(100, 150, 1);// Coalesce from the left.map.insert(90, 99, 1);EXPECT_EQ(1, std::distance(map.begin(), map.end()));EXPECT_EQ(90u, map.start());EXPECT_EQ(150u, map.stop());// Coalesce from the right.map.insert(151, 200, 1);EXPECT_EQ(1, std::distance(map.begin(), map.end()));EXPECT_EQ(90u, map.start());EXPECT_EQ(200u, map.stop());// Non-coalesce from the left.map.insert(60, 89, 2);EXPECT_EQ(2, std::distance(map.begin(), map.end()));EXPECT_EQ(60u, map.start());EXPECT_EQ(200u, map.stop());EXPECT_EQ(2u, map.lookup(89));EXPECT_EQ(1u, map.lookup(90));UUMap::iterator I = map.begin();EXPECT_EQ(60u, I.start());EXPECT_EQ(89u, I.stop());EXPECT_EQ(2u, I.value());++I;EXPECT_EQ(90u, I.start());EXPECT_EQ(200u, I.stop());EXPECT_EQ(1u, I.value());++I;EXPECT_FALSE(I.valid());// Non-coalesce from the right.map.insert(201, 210, 2);EXPECT_EQ(3, std::distance(map.begin(), map.end()));EXPECT_EQ(60u, map.start());EXPECT_EQ(210u, map.stop());EXPECT_EQ(2u, map.lookup(201));EXPECT_EQ(1u, map.lookup(200));// Erase from the left.map.begin().erase();EXPECT_EQ(2, std::distance(map.begin(), map.end()));EXPECT_EQ(90u, map.start());EXPECT_EQ(210u, map.stop());// Erase from the right.(--map.end()).erase();EXPECT_EQ(1, std::distance(map.begin(), map.end()));EXPECT_EQ(90u, map.start());EXPECT_EQ(200u, map.stop());// Add non-coalescing, then trigger coalescing with setValue.map.insert(80, 89, 2);map.insert(201, 210, 2);EXPECT_EQ(3, std::distance(map.begin(), map.end()));(++map.begin()).setValue(2);EXPECT_EQ(1, std::distance(map.begin(), map.end()));I = map.begin();ASSERT_TRUE(I.valid());EXPECT_EQ(80u, I.start());EXPECT_EQ(210u, I.stop());EXPECT_EQ(2u, I.value());}// Flat multi-coalescing tests.TEST(IntervalMapTest, RootMultiCoalescing) {UUMap::Allocator allocator;UUMap map(allocator);map.insert(140, 150, 1);map.insert(160, 170, 1);map.insert(100, 110, 1);map.insert(120, 130, 1);EXPECT_EQ(4, std::distance(map.begin(), map.end()));EXPECT_EQ(100u, map.start());EXPECT_EQ(170u, map.stop());// Verify inserts.UUMap::iterator I = map.begin();EXPECT_EQ(100u, I.start());EXPECT_EQ(110u, I.stop());++I;EXPECT_EQ(120u, I.start());EXPECT_EQ(130u, I.stop());++I;EXPECT_EQ(140u, I.start());EXPECT_EQ(150u, I.stop());++I;EXPECT_EQ(160u, I.start());EXPECT_EQ(170u, I.stop());++I;EXPECT_FALSE(I.valid());// Test advanceTo on flat tree.I = map.begin();I.advanceTo(135);ASSERT_TRUE(I.valid());EXPECT_EQ(140u, I.start());EXPECT_EQ(150u, I.stop());I.advanceTo(145);ASSERT_TRUE(I.valid());EXPECT_EQ(140u, I.start());EXPECT_EQ(150u, I.stop());I.advanceTo(200);EXPECT_FALSE(I.valid());I.advanceTo(300);EXPECT_FALSE(I.valid());// Coalesce left with followers.// [100;110] [120;130] [140;150] [160;170]map.insert(111, 115, 1);I = map.begin();ASSERT_TRUE(I.valid());EXPECT_EQ(100u, I.start());EXPECT_EQ(115u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(120u, I.start());EXPECT_EQ(130u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(140u, I.start());EXPECT_EQ(150u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(160u, I.start());EXPECT_EQ(170u, I.stop());++I;EXPECT_FALSE(I.valid());// Coalesce right with followers.// [100;115] [120;130] [140;150] [160;170]map.insert(135, 139, 1);I = map.begin();ASSERT_TRUE(I.valid());EXPECT_EQ(100u, I.start());EXPECT_EQ(115u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(120u, I.start());EXPECT_EQ(130u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(135u, I.start());EXPECT_EQ(150u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(160u, I.start());EXPECT_EQ(170u, I.stop());++I;EXPECT_FALSE(I.valid());// Coalesce left and right with followers.// [100;115] [120;130] [135;150] [160;170]map.insert(131, 134, 1);I = map.begin();ASSERT_TRUE(I.valid());EXPECT_EQ(100u, I.start());EXPECT_EQ(115u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(120u, I.start());EXPECT_EQ(150u, I.stop());++I;ASSERT_TRUE(I.valid());EXPECT_EQ(160u, I.start());EXPECT_EQ(170u, I.stop());++I;EXPECT_FALSE(I.valid());// Test clear() on non-branched map.map.clear();EXPECT_TRUE(map.empty());EXPECT_TRUE(map.begin() == map.end());}// Branched, non-coalescing tests.TEST(IntervalMapTest, Branched) {UUMap::Allocator allocator;UUMap map(allocator);// Insert enough intervals to force a branched tree.// This creates 9 leaf nodes with 11 elements each, tree height = 1.for (unsigned i = 1; i < 100; ++i) {map.insert(10*i, 10*i+5, i);EXPECT_EQ(10u, map.start());EXPECT_EQ(10*i+5, map.stop());}// Tree limits.EXPECT_FALSE(map.empty());EXPECT_EQ(10u, map.start());EXPECT_EQ(995u, map.stop());// Tree lookup.for (unsigned i = 1; i < 100; ++i) {EXPECT_EQ(0u, map.lookup(10*i-1));EXPECT_EQ(i, map.lookup(10*i));EXPECT_EQ(i, map.lookup(10*i+5));EXPECT_EQ(0u, map.lookup(10*i+6));}// Forward iteration.UUMap::iterator I = map.begin();for (unsigned i = 1; i < 100; ++i) {ASSERT_TRUE(I.valid());EXPECT_EQ(10*i, I.start());EXPECT_EQ(10*i+5, I.stop());EXPECT_EQ(i, *I);++I;}EXPECT_FALSE(I.valid());EXPECT_TRUE(I == map.end());// Backwards iteration.for (unsigned i = 99; i; --i) {--I;ASSERT_TRUE(I.valid());EXPECT_EQ(10*i, I.start());EXPECT_EQ(10*i+5, I.stop());EXPECT_EQ(i, *I);}EXPECT_TRUE(I == map.begin());// Test advanceTo in same node.I.advanceTo(20);ASSERT_TRUE(I.valid());EXPECT_EQ(20u, I.start());EXPECT_EQ(25u, I.stop());// Change value, no coalescing.I.setValue(0);ASSERT_TRUE(I.valid());EXPECT_EQ(20u, I.start());EXPECT_EQ(25u, I.stop());EXPECT_EQ(0u, I.value());// Close the gap right, no coalescing.I.setStop(29);ASSERT_TRUE(I.valid());EXPECT_EQ(20u, I.start());EXPECT_EQ(29u, I.stop());EXPECT_EQ(0u, I.value());// Change value, no coalescing.I.setValue(2);ASSERT_TRUE(I.valid());EXPECT_EQ(20u, I.start());EXPECT_EQ(29u, I.stop());EXPECT_EQ(2u, I.value());// Change value, now coalescing.I.setValue(3);ASSERT_TRUE(I.valid());EXPECT_EQ(20u, I.start());EXPECT_EQ(35u, I.stop());EXPECT_EQ(3u, I.value());// Close the gap, now coalescing.I.setValue(4);ASSERT_TRUE(I.valid());I.setStop(39);ASSERT_TRUE(I.valid());EXPECT_EQ(20u, I.start());EXPECT_EQ(45u, I.stop());EXPECT_EQ(4u, I.value());// advanceTo another node.I.advanceTo(200);ASSERT_TRUE(I.valid());EXPECT_EQ(200u, I.start());EXPECT_EQ(205u, I.stop());// Close the gap left, no coalescing.I.setStart(196);ASSERT_TRUE(I.valid());EXPECT_EQ(196u, I.start());EXPECT_EQ(205u, I.stop());EXPECT_EQ(20u, I.value());// Change value, no coalescing.I.setValue(0);ASSERT_TRUE(I.valid());EXPECT_EQ(196u, I.start());EXPECT_EQ(205u, I.stop());EXPECT_EQ(0u, I.value());// Change value, now coalescing.I.setValue(19);ASSERT_TRUE(I.valid());EXPECT_EQ(190u, I.start());EXPECT_EQ(205u, I.stop());EXPECT_EQ(19u, I.value());// Close the gap, now coalescing.I.setValue(18);ASSERT_TRUE(I.valid());I.setStart(186);ASSERT_TRUE(I.valid());EXPECT_EQ(180u, I.start());EXPECT_EQ(205u, I.stop());EXPECT_EQ(18u, I.value());// Erase from the front.I = map.begin();for (unsigned i = 0; i != 20; ++i) {I.erase();EXPECT_TRUE(I == map.begin());EXPECT_FALSE(map.empty());EXPECT_EQ(I.start(), map.start());EXPECT_EQ(995u, map.stop());}// Test clear() on branched map.map.clear();EXPECT_TRUE(map.empty());EXPECT_TRUE(map.begin() == map.end());}// Branched, high, non-coalescing tests.TEST(IntervalMapTest, Branched2) {UUMap::Allocator allocator;UUMap map(allocator);// Insert enough intervals to force a height >= 2 tree.for (unsigned i = 1; i < 1000; ++i)map.insert(10*i, 10*i+5, i);// Tree limits.EXPECT_FALSE(map.empty());EXPECT_EQ(10u, map.start());EXPECT_EQ(9995u, map.stop());// Tree lookup.for (unsigned i = 1; i < 1000; ++i) {EXPECT_EQ(0u, map.lookup(10*i-1));EXPECT_EQ(i, map.lookup(10*i));EXPECT_EQ(i, map.lookup(10*i+5));EXPECT_EQ(0u, map.lookup(10*i+6));}// Forward iteration.UUMap::iterator I = map.begin();for (unsigned i = 1; i < 1000; ++i) {ASSERT_TRUE(I.valid());EXPECT_EQ(10*i, I.start());EXPECT_EQ(10*i+5, I.stop());EXPECT_EQ(i, *I);++I;}EXPECT_FALSE(I.valid());EXPECT_TRUE(I == map.end());// Backwards iteration.for (unsigned i = 999; i; --i) {--I;ASSERT_TRUE(I.valid());EXPECT_EQ(10*i, I.start());EXPECT_EQ(10*i+5, I.stop());EXPECT_EQ(i, *I);}EXPECT_TRUE(I == map.begin());// Test advanceTo in same node.I.advanceTo(20);ASSERT_TRUE(I.valid());EXPECT_EQ(20u, I.start());EXPECT_EQ(25u, I.stop());// advanceTo sibling leaf node.I.advanceTo(200);ASSERT_TRUE(I.valid());EXPECT_EQ(200u, I.start());EXPECT_EQ(205u, I.stop());// advanceTo further.I.advanceTo(2000);ASSERT_TRUE(I.valid());EXPECT_EQ(2000u, I.start());EXPECT_EQ(2005u, I.stop());// advanceTo beyond end()I.advanceTo(20000);EXPECT_FALSE(I.valid());// end().advanceTo() is valid as long as x > map.stop()I.advanceTo(30000);EXPECT_FALSE(I.valid());// Test clear() on branched map.map.clear();EXPECT_TRUE(map.empty());EXPECT_TRUE(map.begin() == map.end());}// Random insertions, coalescing to a single interval.TEST(IntervalMapTest, RandomCoalescing) {UUMap::Allocator allocator;UUMap map(allocator);// This is a poor PRNG with maximal period:// x_n = 5 x_{n-1} + 1 mod 2^Nunsigned x = 100;for (unsigned i = 0; i != 4096; ++i) {map.insert(10*x, 10*x+9, 1);EXPECT_GE(10*x, map.start());EXPECT_LE(10*x+9, map.stop());x = (5*x+1)%4096;}// Map should be fully coalesced after that exercise.EXPECT_FALSE(map.empty());EXPECT_EQ(0u, map.start());EXPECT_EQ(40959u, map.stop());EXPECT_EQ(1, std::distance(map.begin(), map.end()));}TEST(IntervalMapTest, Overlaps) {UUMap::Allocator allocator;UUMap map(allocator);map.insert(10, 20, 0);map.insert(30, 40, 0);map.insert(50, 60, 0);EXPECT_FALSE(map.overlaps(0, 9));EXPECT_TRUE(map.overlaps(0, 10));EXPECT_TRUE(map.overlaps(0, 15));EXPECT_TRUE(map.overlaps(0, 25));EXPECT_TRUE(map.overlaps(0, 45));EXPECT_TRUE(map.overlaps(10, 45));EXPECT_TRUE(map.overlaps(30, 45));EXPECT_TRUE(map.overlaps(35, 36));EXPECT_TRUE(map.overlaps(40, 45));EXPECT_FALSE(map.overlaps(45, 45));EXPECT_TRUE(map.overlaps(60, 60));EXPECT_TRUE(map.overlaps(60, 66));EXPECT_FALSE(map.overlaps(66, 66));}TEST(IntervalMapTest, OverlapsHalfOpen) {UUHalfOpenMap::Allocator allocator;UUHalfOpenMap map(allocator);map.insert(10, 20, 0);map.insert(30, 40, 0);map.insert(50, 60, 0);EXPECT_FALSE(map.overlaps(0, 9));EXPECT_FALSE(map.overlaps(0, 10));EXPECT_TRUE(map.overlaps(0, 15));EXPECT_TRUE(map.overlaps(0, 25));EXPECT_TRUE(map.overlaps(0, 45));EXPECT_TRUE(map.overlaps(10, 45));EXPECT_TRUE(map.overlaps(30, 45));EXPECT_TRUE(map.overlaps(35, 36));EXPECT_FALSE(map.overlaps(40, 45));EXPECT_FALSE(map.overlaps(45, 46));EXPECT_FALSE(map.overlaps(60, 61));EXPECT_FALSE(map.overlaps(60, 66));EXPECT_FALSE(map.overlaps(66, 67));}TEST(IntervalMapOverlapsTest, SmallMaps) {typedef IntervalMapOverlaps<UUMap,UUMap> UUOverlaps;UUMap::Allocator allocator;UUMap mapA(allocator);UUMap mapB(allocator);// empty, empty.EXPECT_FALSE(UUOverlaps(mapA, mapB).valid());mapA.insert(1, 2, 3);// full, emptyEXPECT_FALSE(UUOverlaps(mapA, mapB).valid());// empty, fullEXPECT_FALSE(UUOverlaps(mapB, mapA).valid());mapB.insert(3, 4, 5);// full, full, non-overlappingEXPECT_FALSE(UUOverlaps(mapA, mapB).valid());EXPECT_FALSE(UUOverlaps(mapB, mapA).valid());// Add an overlapping segment.mapA.insert(4, 5, 6);UUOverlaps AB(mapA, mapB);ASSERT_TRUE(AB.valid());EXPECT_EQ(4u, AB.a().start());EXPECT_EQ(3u, AB.b().start());++AB;EXPECT_FALSE(AB.valid());UUOverlaps BA(mapB, mapA);ASSERT_TRUE(BA.valid());EXPECT_EQ(3u, BA.a().start());EXPECT_EQ(4u, BA.b().start());// advance past end.BA.advanceTo(6);EXPECT_FALSE(BA.valid());// advance an invalid iterator.BA.advanceTo(7);EXPECT_FALSE(BA.valid());}TEST(IntervalMapOverlapsTest, BigMaps) {typedef IntervalMapOverlaps<UUMap,UUMap> UUOverlaps;UUMap::Allocator allocator;UUMap mapA(allocator);UUMap mapB(allocator);// [0;4] [10;14] [20;24] ...for (unsigned n = 0; n != 100; ++n)mapA.insert(10*n, 10*n+4, n);// [5;6] [15;16] [25;26] ...for (unsigned n = 10; n != 20; ++n)mapB.insert(10*n+5, 10*n+6, n);// [208;209] [218;219] ...for (unsigned n = 20; n != 30; ++n)mapB.insert(10*n+8, 10*n+9, n);// insert some overlapping segments.mapB.insert(400, 400, 400);mapB.insert(401, 401, 401);mapB.insert(402, 500, 402);mapB.insert(600, 601, 402);UUOverlaps AB(mapA, mapB);ASSERT_TRUE(AB.valid());EXPECT_EQ(400u, AB.a().start());EXPECT_EQ(400u, AB.b().start());++AB;ASSERT_TRUE(AB.valid());EXPECT_EQ(400u, AB.a().start());EXPECT_EQ(401u, AB.b().start());++AB;ASSERT_TRUE(AB.valid());EXPECT_EQ(400u, AB.a().start());EXPECT_EQ(402u, AB.b().start());++AB;ASSERT_TRUE(AB.valid());EXPECT_EQ(410u, AB.a().start());EXPECT_EQ(402u, AB.b().start());++AB;ASSERT_TRUE(AB.valid());EXPECT_EQ(420u, AB.a().start());EXPECT_EQ(402u, AB.b().start());AB.skipB();ASSERT_TRUE(AB.valid());EXPECT_EQ(600u, AB.a().start());EXPECT_EQ(600u, AB.b().start());++AB;EXPECT_FALSE(AB.valid());// Test advanceTo.UUOverlaps AB2(mapA, mapB);AB2.advanceTo(410);ASSERT_TRUE(AB2.valid());EXPECT_EQ(410u, AB2.a().start());EXPECT_EQ(402u, AB2.b().start());// It is valid to advanceTo with any monotonic sequence.AB2.advanceTo(411);ASSERT_TRUE(AB2.valid());EXPECT_EQ(410u, AB2.a().start());EXPECT_EQ(402u, AB2.b().start());// Check reversed maps.UUOverlaps BA(mapB, mapA);ASSERT_TRUE(BA.valid());EXPECT_EQ(400u, BA.b().start());EXPECT_EQ(400u, BA.a().start());++BA;ASSERT_TRUE(BA.valid());EXPECT_EQ(400u, BA.b().start());EXPECT_EQ(401u, BA.a().start());++BA;ASSERT_TRUE(BA.valid());EXPECT_EQ(400u, BA.b().start());EXPECT_EQ(402u, BA.a().start());++BA;ASSERT_TRUE(BA.valid());EXPECT_EQ(410u, BA.b().start());EXPECT_EQ(402u, BA.a().start());++BA;ASSERT_TRUE(BA.valid());EXPECT_EQ(420u, BA.b().start());EXPECT_EQ(402u, BA.a().start());BA.skipA();ASSERT_TRUE(BA.valid());EXPECT_EQ(600u, BA.b().start());EXPECT_EQ(600u, BA.a().start());++BA;EXPECT_FALSE(BA.valid());// Test advanceTo.UUOverlaps BA2(mapB, mapA);BA2.advanceTo(410);ASSERT_TRUE(BA2.valid());EXPECT_EQ(410u, BA2.b().start());EXPECT_EQ(402u, BA2.a().start());BA2.advanceTo(411);ASSERT_TRUE(BA2.valid());EXPECT_EQ(410u, BA2.b().start());EXPECT_EQ(402u, BA2.a().start());}} // namespace
//===---- ADT/IntEqClassesTest.cpp - IntEqClasses unit tests ----*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/IntEqClasses.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(IntEqClasses, Simple) {IntEqClasses ec(10);ec.join(0, 1);ec.join(3, 2);ec.join(4, 5);ec.join(7, 6);EXPECT_EQ(0u, ec.findLeader(0));EXPECT_EQ(0u, ec.findLeader(1));EXPECT_EQ(2u, ec.findLeader(2));EXPECT_EQ(2u, ec.findLeader(3));EXPECT_EQ(4u, ec.findLeader(4));EXPECT_EQ(4u, ec.findLeader(5));EXPECT_EQ(6u, ec.findLeader(6));EXPECT_EQ(6u, ec.findLeader(7));EXPECT_EQ(8u, ec.findLeader(8));EXPECT_EQ(9u, ec.findLeader(9));// join two non-leaders.ec.join(1, 3);EXPECT_EQ(0u, ec.findLeader(0));EXPECT_EQ(0u, ec.findLeader(1));EXPECT_EQ(0u, ec.findLeader(2));EXPECT_EQ(0u, ec.findLeader(3));EXPECT_EQ(4u, ec.findLeader(4));EXPECT_EQ(4u, ec.findLeader(5));EXPECT_EQ(6u, ec.findLeader(6));EXPECT_EQ(6u, ec.findLeader(7));EXPECT_EQ(8u, ec.findLeader(8));EXPECT_EQ(9u, ec.findLeader(9));// join two leaders.ec.join(4, 8);EXPECT_EQ(0u, ec.findLeader(0));EXPECT_EQ(0u, ec.findLeader(1));EXPECT_EQ(0u, ec.findLeader(2));EXPECT_EQ(0u, ec.findLeader(3));EXPECT_EQ(4u, ec.findLeader(4));EXPECT_EQ(4u, ec.findLeader(5));EXPECT_EQ(6u, ec.findLeader(6));EXPECT_EQ(6u, ec.findLeader(7));EXPECT_EQ(4u, ec.findLeader(8));EXPECT_EQ(9u, ec.findLeader(9));// join mixed.ec.join(9, 1);EXPECT_EQ(0u, ec.findLeader(0));EXPECT_EQ(0u, ec.findLeader(1));EXPECT_EQ(0u, ec.findLeader(2));EXPECT_EQ(0u, ec.findLeader(3));EXPECT_EQ(4u, ec.findLeader(4));EXPECT_EQ(4u, ec.findLeader(5));EXPECT_EQ(6u, ec.findLeader(6));EXPECT_EQ(6u, ec.findLeader(7));EXPECT_EQ(4u, ec.findLeader(8));EXPECT_EQ(0u, ec.findLeader(9));// compressed map.ec.compress();EXPECT_EQ(3u, ec.getNumClasses());EXPECT_EQ(0u, ec[0]);EXPECT_EQ(0u, ec[1]);EXPECT_EQ(0u, ec[2]);EXPECT_EQ(0u, ec[3]);EXPECT_EQ(1u, ec[4]);EXPECT_EQ(1u, ec[5]);EXPECT_EQ(2u, ec[6]);EXPECT_EQ(2u, ec[7]);EXPECT_EQ(1u, ec[8]);EXPECT_EQ(0u, ec[9]);// uncompressed map.ec.uncompress();EXPECT_EQ(0u, ec.findLeader(0));EXPECT_EQ(0u, ec.findLeader(1));EXPECT_EQ(0u, ec.findLeader(2));EXPECT_EQ(0u, ec.findLeader(3));EXPECT_EQ(4u, ec.findLeader(4));EXPECT_EQ(4u, ec.findLeader(5));EXPECT_EQ(6u, ec.findLeader(6));EXPECT_EQ(6u, ec.findLeader(7));EXPECT_EQ(4u, ec.findLeader(8));EXPECT_EQ(0u, ec.findLeader(9));}} // end anonymous namespace
//===----------- ImmutableSetTest.cpp - ImmutableSet unit tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ImmutableSet.h"#include "gtest/gtest.h"using namespace llvm;namespace {class ImmutableSetTest : public testing::Test {protected:// for callback testsstatic char buffer[10];struct MyIter {int counter;char *ptr;MyIter() : counter(0), ptr(buffer) {for (unsigned i=0; i<sizeof(buffer);++i) buffer[i]='\0';}void operator()(char c) {*ptr++ = c;++counter;}};};char ImmutableSetTest::buffer[10];TEST_F(ImmutableSetTest, EmptyIntSetTest) {ImmutableSet<int>::Factory f;EXPECT_TRUE(f.getEmptySet() == f.getEmptySet());EXPECT_FALSE(f.getEmptySet() != f.getEmptySet());EXPECT_TRUE(f.getEmptySet().isEmpty());ImmutableSet<int> S = f.getEmptySet();EXPECT_EQ(0u, S.getHeight());EXPECT_TRUE(S.begin() == S.end());EXPECT_FALSE(S.begin() != S.end());}TEST_F(ImmutableSetTest, OneElemIntSetTest) {ImmutableSet<int>::Factory f;ImmutableSet<int> S = f.getEmptySet();ImmutableSet<int> S2 = f.add(S, 3);EXPECT_TRUE(S.isEmpty());EXPECT_FALSE(S2.isEmpty());EXPECT_FALSE(S == S2);EXPECT_TRUE(S != S2);EXPECT_FALSE(S.contains(3));EXPECT_TRUE(S2.contains(3));EXPECT_FALSE(S2.begin() == S2.end());EXPECT_TRUE(S2.begin() != S2.end());ImmutableSet<int> S3 = f.add(S, 2);EXPECT_TRUE(S.isEmpty());EXPECT_FALSE(S3.isEmpty());EXPECT_FALSE(S == S3);EXPECT_TRUE(S != S3);EXPECT_FALSE(S.contains(2));EXPECT_TRUE(S3.contains(2));EXPECT_FALSE(S2 == S3);EXPECT_TRUE(S2 != S3);EXPECT_FALSE(S2.contains(2));EXPECT_FALSE(S3.contains(3));}TEST_F(ImmutableSetTest, MultiElemIntSetTest) {ImmutableSet<int>::Factory f;ImmutableSet<int> S = f.getEmptySet();ImmutableSet<int> S2 = f.add(f.add(f.add(S, 3), 4), 5);ImmutableSet<int> S3 = f.add(f.add(f.add(S2, 9), 20), 43);ImmutableSet<int> S4 = f.add(S2, 9);EXPECT_TRUE(S.isEmpty());EXPECT_FALSE(S2.isEmpty());EXPECT_FALSE(S3.isEmpty());EXPECT_FALSE(S4.isEmpty());EXPECT_FALSE(S.contains(3));EXPECT_FALSE(S.contains(9));EXPECT_TRUE(S2.contains(3));EXPECT_TRUE(S2.contains(4));EXPECT_TRUE(S2.contains(5));EXPECT_FALSE(S2.contains(9));EXPECT_FALSE(S2.contains(0));EXPECT_TRUE(S3.contains(43));EXPECT_TRUE(S3.contains(20));EXPECT_TRUE(S3.contains(9));EXPECT_TRUE(S3.contains(3));EXPECT_TRUE(S3.contains(4));EXPECT_TRUE(S3.contains(5));EXPECT_FALSE(S3.contains(0));EXPECT_TRUE(S4.contains(9));EXPECT_TRUE(S4.contains(3));EXPECT_TRUE(S4.contains(4));EXPECT_TRUE(S4.contains(5));EXPECT_FALSE(S4.contains(20));EXPECT_FALSE(S4.contains(43));}TEST_F(ImmutableSetTest, RemoveIntSetTest) {ImmutableSet<int>::Factory f;ImmutableSet<int> S = f.getEmptySet();ImmutableSet<int> S2 = f.add(f.add(S, 4), 5);ImmutableSet<int> S3 = f.add(S2, 3);ImmutableSet<int> S4 = f.remove(S3, 3);EXPECT_TRUE(S3.contains(3));EXPECT_FALSE(S2.contains(3));EXPECT_FALSE(S4.contains(3));EXPECT_TRUE(S2 == S4);EXPECT_TRUE(S3 != S2);EXPECT_TRUE(S3 != S4);EXPECT_TRUE(S3.contains(4));EXPECT_TRUE(S3.contains(5));EXPECT_TRUE(S4.contains(4));EXPECT_TRUE(S4.contains(5));}TEST_F(ImmutableSetTest, IterLongSetTest) {ImmutableSet<long>::Factory f;ImmutableSet<long> S = f.getEmptySet();ImmutableSet<long> S2 = f.add(f.add(f.add(S, 0), 1), 2);ImmutableSet<long> S3 = f.add(f.add(f.add(S2, 3), 4), 5);int i = 0;for (ImmutableSet<long>::iterator I = S.begin(), E = S.end(); I != E; ++I) {i++;}ASSERT_EQ(0, i);i = 0;for (ImmutableSet<long>::iterator I = S2.begin(), E = S2.end(); I != E; ++I) {ASSERT_EQ(i, *I);i++;}ASSERT_EQ(3, i);i = 0;for (ImmutableSet<long>::iterator I = S3.begin(), E = S3.end(); I != E; I++) {ASSERT_EQ(i, *I);i++;}ASSERT_EQ(6, i);}}
//===----------- ImmutableMapTest.cpp - ImmutableMap unit tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ImmutableMap.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(ImmutableMapTest, EmptyIntMapTest) {ImmutableMap<int, int>::Factory f;EXPECT_TRUE(f.getEmptyMap() == f.getEmptyMap());EXPECT_FALSE(f.getEmptyMap() != f.getEmptyMap());EXPECT_TRUE(f.getEmptyMap().isEmpty());ImmutableMap<int, int> S = f.getEmptyMap();EXPECT_EQ(0u, S.getHeight());EXPECT_TRUE(S.begin() == S.end());EXPECT_FALSE(S.begin() != S.end());}TEST(ImmutableMapTest, MultiElemIntMapTest) {ImmutableMap<int, int>::Factory f;ImmutableMap<int, int> S = f.getEmptyMap();ImmutableMap<int, int> S2 = f.add(f.add(f.add(S, 3, 10), 4, 11), 5, 12);EXPECT_TRUE(S.isEmpty());EXPECT_FALSE(S2.isEmpty());EXPECT_EQ(nullptr, S.lookup(3));EXPECT_EQ(nullptr, S.lookup(9));EXPECT_EQ(10, *S2.lookup(3));EXPECT_EQ(11, *S2.lookup(4));EXPECT_EQ(12, *S2.lookup(5));EXPECT_EQ(5, S2.getMaxElement()->first);EXPECT_EQ(3U, S2.getHeight());}TEST(ImmutableMapTest, EmptyIntMapRefTest) {using int_int_map = ImmutableMapRef<int, int>;ImmutableMapRef<int, int>::FactoryTy f;EXPECT_TRUE(int_int_map::getEmptyMap(&f) == int_int_map::getEmptyMap(&f));EXPECT_FALSE(int_int_map::getEmptyMap(&f) != int_int_map::getEmptyMap(&f));EXPECT_TRUE(int_int_map::getEmptyMap(&f).isEmpty());int_int_map S = int_int_map::getEmptyMap(&f);EXPECT_EQ(0u, S.getHeight());EXPECT_TRUE(S.begin() == S.end());EXPECT_FALSE(S.begin() != S.end());}TEST(ImmutableMapTest, MultiElemIntMapRefTest) {ImmutableMapRef<int, int>::FactoryTy f;ImmutableMapRef<int, int> S = ImmutableMapRef<int, int>::getEmptyMap(&f);ImmutableMapRef<int, int> S2 = S.add(3, 10).add(4, 11).add(5, 12);EXPECT_TRUE(S.isEmpty());EXPECT_FALSE(S2.isEmpty());EXPECT_EQ(nullptr, S.lookup(3));EXPECT_EQ(nullptr, S.lookup(9));EXPECT_EQ(10, *S2.lookup(3));EXPECT_EQ(11, *S2.lookup(4));EXPECT_EQ(12, *S2.lookup(5));EXPECT_EQ(5, S2.getMaxElement()->first);EXPECT_EQ(3U, S2.getHeight());}TEST(ImmutableMapTest, MapOfMapRefsTest) {ImmutableMap<int, ImmutableMapRef<int, int>>::Factory f;EXPECT_TRUE(f.getEmptyMap() == f.getEmptyMap());}}
//===--------- ImmutableListTest.cpp - ImmutableList unit tests --*- C++ -*-==////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ImmutableList.h"#include "gtest/gtest.h"using namespace llvm;namespace {template <typename Fundamental> struct Wrapper : llvm::FoldingSetNode {Fundamental F;Wrapper(Fundamental F) : F(F) {}operator Fundamental() const { return F; }void Profile(FoldingSetNodeID &ID) const { ID.AddInteger(F); }};class ImmutableListTest : public testing::Test {};void concat(const ImmutableList<Wrapper<char>> &L, char *Buffer) {int Index = 0;for (ImmutableList<Wrapper<char>>::iterator It = L.begin(), End = L.end();It != End; ++It) {Buffer[Index++] = *It;}Buffer[Index] = '\0';}TEST_F(ImmutableListTest, EmptyIntListTest) {ImmutableList<Wrapper<int>>::Factory f;EXPECT_TRUE(f.getEmptyList() == f.getEmptyList());EXPECT_TRUE(f.getEmptyList().isEqual(f.getEmptyList()));EXPECT_TRUE(f.getEmptyList().isEmpty());ImmutableList<Wrapper<int>> L = f.getEmptyList();EXPECT_EQ(nullptr, L.getTail().getInternalPointer());EXPECT_TRUE(L.getTail().isEmpty());EXPECT_TRUE(L.begin() == L.end());}TEST_F(ImmutableListTest, OneElemIntListTest) {ImmutableList<Wrapper<int>>::Factory f;ImmutableList<Wrapper<int>> L = f.getEmptyList();ImmutableList<Wrapper<int>> L2 = f.add(3, L);EXPECT_TRUE(L.isEmpty());EXPECT_FALSE(L2.isEmpty());EXPECT_TRUE(L2.getTail().isEmpty());EXPECT_FALSE(L == L2);EXPECT_TRUE(L == L2.getTail());EXPECT_FALSE(L.isEqual(L2));EXPECT_TRUE(L.isEqual(L2.getTail()));EXPECT_FALSE(L2.begin() == L2.end());EXPECT_TRUE(L2.begin() != L2.end());EXPECT_FALSE(L.contains(3));EXPECT_EQ(3, L2.getHead());EXPECT_TRUE(L2.contains(3));ImmutableList<Wrapper<int>> L3 = f.add(2, L);EXPECT_TRUE(L.isEmpty());EXPECT_FALSE(L3.isEmpty());EXPECT_FALSE(L == L3);EXPECT_FALSE(L.contains(2));EXPECT_TRUE(L3.contains(2));EXPECT_EQ(2, L3.getHead());EXPECT_FALSE(L2 == L3);EXPECT_FALSE(L2.contains(2));}// We'll store references to objects of this type.struct Unmodifiable {Unmodifiable() = default;// We'll delete all of these special member functions to make sure no copy or// move happens during insertation.Unmodifiable(const Unmodifiable &) = delete;Unmodifiable(const Unmodifiable &&) = delete;Unmodifiable &operator=(const Unmodifiable &) = delete;Unmodifiable &operator=(const Unmodifiable &&) = delete;void doNothing() const {}void Profile(FoldingSetNodeID &ID) const { ID.AddPointer(this); }};// Mostly just a check whether ImmutableList::iterator can be instantiated// with a reference type as a template argument.TEST_F(ImmutableListTest, ReferenceStoringTest) {ImmutableList<const Unmodifiable &>::Factory f;Unmodifiable N;ImmutableList<const Unmodifiable &> L = f.create(N);for (ImmutableList<const Unmodifiable &>::iterator It = L.begin(),E = L.end();It != E; ++It) {It->doNothing();}}TEST_F(ImmutableListTest, CreatingIntListTest) {ImmutableList<Wrapper<int>>::Factory f;ImmutableList<Wrapper<int>> L = f.getEmptyList();ImmutableList<Wrapper<int>> L2 = f.create(3);EXPECT_FALSE(L2.isEmpty());EXPECT_TRUE(L2.getTail().isEmpty());EXPECT_EQ(3, L2.getHead());EXPECT_TRUE(L.isEqual(L2.getTail()));EXPECT_TRUE(L2.getTail().isEqual(L));}TEST_F(ImmutableListTest, MultiElemIntListTest) {ImmutableList<Wrapper<int>>::Factory f;ImmutableList<Wrapper<int>> L = f.getEmptyList();ImmutableList<Wrapper<int>> L2 = f.add(5, f.add(4, f.add(3, L)));ImmutableList<Wrapper<int>> L3 = f.add(43, f.add(20, f.add(9, L2)));ImmutableList<Wrapper<int>> L4 = f.add(9, L2);ImmutableList<Wrapper<int>> L5 = f.add(9, L2);EXPECT_TRUE(L.isEmpty());EXPECT_FALSE(L2.isEmpty());EXPECT_FALSE(L3.isEmpty());EXPECT_FALSE(L4.isEmpty());EXPECT_FALSE(L.contains(3));EXPECT_FALSE(L.contains(9));EXPECT_TRUE(L2.contains(3));EXPECT_TRUE(L2.contains(4));EXPECT_TRUE(L2.contains(5));EXPECT_FALSE(L2.contains(9));EXPECT_FALSE(L2.contains(0));EXPECT_EQ(5, L2.getHead());EXPECT_EQ(4, L2.getTail().getHead());EXPECT_EQ(3, L2.getTail().getTail().getHead());EXPECT_TRUE(L3.contains(43));EXPECT_TRUE(L3.contains(20));EXPECT_TRUE(L3.contains(9));EXPECT_TRUE(L3.contains(3));EXPECT_TRUE(L3.contains(4));EXPECT_TRUE(L3.contains(5));EXPECT_FALSE(L3.contains(0));EXPECT_EQ(43, L3.getHead());EXPECT_EQ(20, L3.getTail().getHead());EXPECT_EQ(9, L3.getTail().getTail().getHead());EXPECT_TRUE(L3.getTail().getTail().getTail() == L2);EXPECT_TRUE(L2 == L3.getTail().getTail().getTail());EXPECT_TRUE(L3.getTail().getTail().getTail().isEqual(L2));EXPECT_TRUE(L2.isEqual(L3.getTail().getTail().getTail()));EXPECT_TRUE(L4.contains(9));EXPECT_TRUE(L4.contains(3));EXPECT_TRUE(L4.contains(4));EXPECT_TRUE(L4.contains(5));EXPECT_FALSE(L4.contains(20));EXPECT_FALSE(L4.contains(43));EXPECT_TRUE(L4.isEqual(L4));EXPECT_TRUE(L4.isEqual(L5));EXPECT_TRUE(L5.isEqual(L4));EXPECT_TRUE(L5.isEqual(L5));}template <typename Fundamental>struct ExplicitCtorWrapper : public Wrapper<Fundamental> {explicit ExplicitCtorWrapper(Fundamental F) : Wrapper<Fundamental>(F) {}ExplicitCtorWrapper(const ExplicitCtorWrapper &) = delete;ExplicitCtorWrapper(ExplicitCtorWrapper &&) = default;ExplicitCtorWrapper &operator=(const ExplicitCtorWrapper &) = delete;ExplicitCtorWrapper &operator=(ExplicitCtorWrapper &&) = default;};TEST_F(ImmutableListTest, EmplaceIntListTest) {ImmutableList<ExplicitCtorWrapper<int>>::Factory f;ImmutableList<ExplicitCtorWrapper<int>> L = f.getEmptyList();ImmutableList<ExplicitCtorWrapper<int>> L2 = f.emplace(L, 3);ImmutableList<ExplicitCtorWrapper<int>> L3 =f.add(ExplicitCtorWrapper<int>(2), L2);ImmutableList<ExplicitCtorWrapper<int>> L4 =f.emplace(L3, ExplicitCtorWrapper<int>(1));ImmutableList<ExplicitCtorWrapper<int>> L5 =f.add(ExplicitCtorWrapper<int>(1), L3);EXPECT_FALSE(L2.isEmpty());EXPECT_TRUE(L2.getTail().isEmpty());EXPECT_EQ(3, L2.getHead());EXPECT_TRUE(L.isEqual(L2.getTail()));EXPECT_TRUE(L2.getTail().isEqual(L));EXPECT_FALSE(L3.isEmpty());EXPECT_FALSE(L2 == L3);EXPECT_EQ(2, L3.getHead());EXPECT_TRUE(L2 == L3.getTail());EXPECT_FALSE(L4.isEmpty());EXPECT_EQ(1, L4.getHead());EXPECT_TRUE(L3 == L4.getTail());EXPECT_TRUE(L4 == L5);EXPECT_TRUE(L3 == L5.getTail());}TEST_F(ImmutableListTest, CharListOrderingTest) {ImmutableList<Wrapper<char>>::Factory f;ImmutableList<Wrapper<char>> L = f.getEmptyList();ImmutableList<Wrapper<char>> L2 = f.add('i', f.add('e', f.add('a', L)));ImmutableList<Wrapper<char>> L3 = f.add('u', f.add('o', L2));char Buffer[10];concat(L3, Buffer);ASSERT_STREQ("uoiea", Buffer);}TEST_F(ImmutableListTest, LongListOrderingTest) {ImmutableList<Wrapper<long>>::Factory f;ImmutableList<Wrapper<long>> L = f.getEmptyList();ImmutableList<Wrapper<long>> L2 = f.add(3, f.add(4, f.add(5, L)));ImmutableList<Wrapper<long>> L3 = f.add(0, f.add(1, f.add(2, L2)));int i = 0;for (ImmutableList<Wrapper<long>>::iterator I = L.begin(), E = L.end();I != E; ++I) {i++;}ASSERT_EQ(0, i);i = 3;for (ImmutableList<Wrapper<long>>::iterator I = L2.begin(), E = L2.end();I != E; ++I) {ASSERT_EQ(i, *I);i++;}ASSERT_EQ(6, i);i = 0;for (ImmutableList<Wrapper<long>>::iterator I = L3.begin(), E = L3.end();I != E; ++I) {ASSERT_EQ(i, *I);i++;}ASSERT_EQ(6, i);}static_assert(std::is_trivially_copyable<ImmutableList<Wrapper<long>>>::value,"trivially copyable");} // namespace
//===- unittests/ADT/IListTest.cpp - ilist unit tests ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ilist.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/ilist_node.h"#include "gtest/gtest.h"#include <ostream>using namespace llvm;namespace {struct Node : ilist_node<Node> {int Value;Node() {}Node(int Value) : Value(Value) {}Node(const Node&) = default;~Node() { Value = -1; }};TEST(IListTest, Basic) {ilist<Node> List;List.push_back(new Node(1));EXPECT_EQ(1, List.back().Value);EXPECT_EQ(nullptr, List.getPrevNode(List.back()));EXPECT_EQ(nullptr, List.getNextNode(List.back()));List.push_back(new Node(2));EXPECT_EQ(2, List.back().Value);EXPECT_EQ(2, List.getNextNode(List.front())->Value);EXPECT_EQ(1, List.getPrevNode(List.back())->Value);const ilist<Node> &ConstList = List;EXPECT_EQ(2, ConstList.back().Value);EXPECT_EQ(2, ConstList.getNextNode(ConstList.front())->Value);EXPECT_EQ(1, ConstList.getPrevNode(ConstList.back())->Value);}TEST(IListTest, cloneFrom) {Node L1Nodes[] = {Node(0), Node(1)};Node L2Nodes[] = {Node(0), Node(1)};ilist<Node> L1, L2, L3;// Build L1 from L1Nodes.L1.push_back(&L1Nodes[0]);L1.push_back(&L1Nodes[1]);// Build L2 from L2Nodes, based on L1 nodes.L2.cloneFrom(L1, [&](const Node &N) { return &L2Nodes[N.Value]; });// Add a node to L3 to be deleted, and then rebuild L3 by copying L1.L3.push_back(new Node(7));L3.cloneFrom(L1, [](const Node &N) { return new Node(N); });EXPECT_EQ(2u, L1.size());EXPECT_EQ(&L1Nodes[0], &L1.front());EXPECT_EQ(&L1Nodes[1], &L1.back());EXPECT_EQ(2u, L2.size());EXPECT_EQ(&L2Nodes[0], &L2.front());EXPECT_EQ(&L2Nodes[1], &L2.back());EXPECT_EQ(2u, L3.size());EXPECT_EQ(0, L3.front().Value);EXPECT_EQ(1, L3.back().Value);// Don't free nodes on the stack.L1.clearAndLeakNodesUnsafely();L2.clearAndLeakNodesUnsafely();}TEST(IListTest, SpliceOne) {ilist<Node> List;List.push_back(new Node(1));// The single-element splice operation supports noops.List.splice(List.begin(), List, List.begin());EXPECT_EQ(1u, List.size());EXPECT_EQ(1, List.front().Value);EXPECT_TRUE(std::next(List.begin()) == List.end());// Altenative noop. Move the first element behind itself.List.push_back(new Node(2));List.push_back(new Node(3));List.splice(std::next(List.begin()), List, List.begin());EXPECT_EQ(3u, List.size());EXPECT_EQ(1, List.front().Value);EXPECT_EQ(2, std::next(List.begin())->Value);EXPECT_EQ(3, List.back().Value);}TEST(IListTest, SpliceSwap) {ilist<Node> L;Node N0(0);Node N1(1);L.insert(L.end(), &N0);L.insert(L.end(), &N1);EXPECT_EQ(0, L.front().Value);EXPECT_EQ(1, L.back().Value);L.splice(L.begin(), L, ++L.begin());EXPECT_EQ(1, L.front().Value);EXPECT_EQ(0, L.back().Value);L.clearAndLeakNodesUnsafely();}TEST(IListTest, SpliceSwapOtherWay) {ilist<Node> L;Node N0(0);Node N1(1);L.insert(L.end(), &N0);L.insert(L.end(), &N1);EXPECT_EQ(0, L.front().Value);EXPECT_EQ(1, L.back().Value);L.splice(L.end(), L, L.begin());EXPECT_EQ(1, L.front().Value);EXPECT_EQ(0, L.back().Value);L.clearAndLeakNodesUnsafely();}TEST(IListTest, UnsafeClear) {ilist<Node> List;// Before even allocating a sentinel.List.clearAndLeakNodesUnsafely();EXPECT_EQ(0u, List.size());// Empty list with sentinel.ilist<Node>::iterator E = List.end();List.clearAndLeakNodesUnsafely();EXPECT_EQ(0u, List.size());// The sentinel shouldn't change.EXPECT_TRUE(E == List.end());// List with contents.List.push_back(new Node(1));ASSERT_EQ(1u, List.size());Node *N = &*List.begin();EXPECT_EQ(1, N->Value);List.clearAndLeakNodesUnsafely();EXPECT_EQ(0u, List.size());ASSERT_EQ(1, N->Value);delete N;// List is still functional.List.push_back(new Node(5));List.push_back(new Node(6));ASSERT_EQ(2u, List.size());EXPECT_EQ(5, List.front().Value);EXPECT_EQ(6, List.back().Value);}struct Empty {};TEST(IListTest, HasObsoleteCustomizationTrait) {// Negative test for HasObsoleteCustomization.static_assert(!ilist_detail::HasObsoleteCustomization<Empty, Node>::value,"Empty has no customizations");}struct GetNext {Node *getNext(Node *);};TEST(IListTest, HasGetNextTrait) {static_assert(ilist_detail::HasGetNext<GetNext, Node>::value,"GetNext has a getNext(Node*)");static_assert(ilist_detail::HasObsoleteCustomization<GetNext, Node>::value,"Empty should be obsolete because of getNext()");// Negative test for HasGetNext.static_assert(!ilist_detail::HasGetNext<Empty, Node>::value,"Empty does not have a getNext(Node*)");}struct CreateSentinel {Node *createSentinel();};TEST(IListTest, HasCreateSentinelTrait) {static_assert(ilist_detail::HasCreateSentinel<CreateSentinel>::value,"CreateSentinel has a getNext(Node*)");static_assert(ilist_detail::HasObsoleteCustomization<CreateSentinel, Node>::value,"Empty should be obsolete because of createSentinel()");// Negative test for HasCreateSentinel.static_assert(!ilist_detail::HasCreateSentinel<Empty>::value,"Empty does not have a createSentinel()");}struct NodeWithCallback : ilist_node<NodeWithCallback> {int Value = 0;bool IsInList = false;bool WasTransferred = false;NodeWithCallback() = default;NodeWithCallback(int Value) : Value(Value) {}NodeWithCallback(const NodeWithCallback &) = delete;};} // end namespacenamespace llvm {// These nodes are stack-allocated for testing purposes, so don't let the ilist// own or delete them.template <> struct ilist_alloc_traits<NodeWithCallback> {static void deleteNode(NodeWithCallback *) {}};template <> struct ilist_callback_traits<NodeWithCallback> {void addNodeToList(NodeWithCallback *N) { N->IsInList = true; }void removeNodeFromList(NodeWithCallback *N) { N->IsInList = false; }template <class Iterator>void transferNodesFromList(ilist_callback_traits &Other, Iterator First,Iterator Last) {for (; First != Last; ++First) {First->WasTransferred = true;Other.removeNodeFromList(&*First);addNodeToList(&*First);}}};} // end namespace llvmnamespace {TEST(IListTest, addNodeToList) {ilist<NodeWithCallback> L1, L2;NodeWithCallback N(7);ASSERT_FALSE(N.IsInList);ASSERT_FALSE(N.WasTransferred);L1.insert(L1.begin(), &N);ASSERT_EQ(1u, L1.size());ASSERT_EQ(&N, &L1.front());ASSERT_TRUE(N.IsInList);ASSERT_FALSE(N.WasTransferred);L2.splice(L2.end(), L1);ASSERT_EQ(&N, &L2.front());ASSERT_TRUE(N.IsInList);ASSERT_TRUE(N.WasTransferred);L1.remove(&N);ASSERT_EQ(0u, L1.size());ASSERT_FALSE(N.IsInList);ASSERT_TRUE(N.WasTransferred);}TEST(IListTest, sameListSplice) {NodeWithCallback N1(1);NodeWithCallback N2(2);ASSERT_FALSE(N1.WasTransferred);ASSERT_FALSE(N2.WasTransferred);ilist<NodeWithCallback> L1;L1.insert(L1.end(), &N1);L1.insert(L1.end(), &N2);ASSERT_EQ(2u, L1.size());ASSERT_EQ(&N1, &L1.front());ASSERT_FALSE(N1.WasTransferred);ASSERT_FALSE(N2.WasTransferred);// Swap the nodes with splice inside the same list. Check that we get the// transfer callback.L1.splice(L1.begin(), L1, std::next(L1.begin()), L1.end());ASSERT_EQ(2u, L1.size());ASSERT_EQ(&N1, &L1.back());ASSERT_EQ(&N2, &L1.front());ASSERT_FALSE(N1.WasTransferred);ASSERT_TRUE(N2.WasTransferred);}struct PrivateNode : private ilist_node<PrivateNode> {friend struct llvm::ilist_detail::NodeAccess;int Value = 0;PrivateNode() = default;PrivateNode(int Value) : Value(Value) {}PrivateNode(const PrivateNode &) = delete;};TEST(IListTest, privateNode) {// Instantiate various APIs to be sure they're callable when ilist_node is// inherited privately.ilist<PrivateNode> L;PrivateNode N(7);L.insert(L.begin(), &N);++L.begin();(void)*L.begin();(void)(L.begin() == L.end());ilist<PrivateNode> L2;L2.splice(L2.end(), L);L2.remove(&N);}} // end namespace
//===- unittests/ADT/IListSentinelTest.cpp - ilist_sentinel unit tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ilist_node.h"#include "gtest/gtest.h"using namespace llvm;namespace {template <class T, class... Options> struct PickSentinel {typedef ilist_sentinel<typename ilist_detail::compute_node_options<T, Options...>::type>type;};class Node : public ilist_node<Node> {};class TrackingNode : public ilist_node<Node, ilist_sentinel_tracking<true>> {};typedef PickSentinel<Node>::type Sentinel;typedef PickSentinel<Node, ilist_sentinel_tracking<true>>::typeTrackingSentinel;typedef PickSentinel<Node, ilist_sentinel_tracking<false>>::typeNoTrackingSentinel;struct LocalAccess : ilist_detail::NodeAccess {using NodeAccess::getPrev;using NodeAccess::getNext;};TEST(IListSentinelTest, DefaultConstructor) {Sentinel S;EXPECT_EQ(&S, LocalAccess::getPrev(S));EXPECT_EQ(&S, LocalAccess::getNext(S));#if LLVM_ENABLE_ABI_BREAKING_CHECKSEXPECT_TRUE(S.isKnownSentinel());#elseEXPECT_FALSE(S.isKnownSentinel());#endifTrackingSentinel TS;NoTrackingSentinel NTS;EXPECT_TRUE(TS.isSentinel());EXPECT_TRUE(TS.isKnownSentinel());EXPECT_FALSE(NTS.isKnownSentinel());}TEST(IListSentinelTest, NormalNodeIsNotKnownSentinel) {Node N;EXPECT_EQ(nullptr, LocalAccess::getPrev(N));EXPECT_EQ(nullptr, LocalAccess::getNext(N));EXPECT_FALSE(N.isKnownSentinel());TrackingNode TN;EXPECT_FALSE(TN.isSentinel());}} // end namespace
//===- unittests/ADT/IListNodeTest.cpp - ilist_node unit tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ilist_node.h"#include "gtest/gtest.h"#include <type_traits>using namespace llvm;using namespace llvm::ilist_detail;namespace {struct Node;struct TagA {};struct TagB {};TEST(IListNodeTest, Options) {static_assert(std::is_same<compute_node_options<Node>::type,compute_node_options<Node, ilist_tag<void>>::type>::value,"default tag is void");static_assert(!std::is_same<compute_node_options<Node, ilist_tag<TagA>>::type,compute_node_options<Node, ilist_tag<void>>::type>::value,"default tag is void, different from TagA");static_assert(!std::is_same<compute_node_options<Node, ilist_tag<TagA>>::type,compute_node_options<Node, ilist_tag<TagB>>::type>::value,"TagA is not TagB");static_assert(std::is_same<compute_node_options<Node, ilist_sentinel_tracking<false>>::type,compute_node_options<Node, ilist_sentinel_tracking<false>,ilist_tag<void>>::type>::value,"default tag is void, even with sentinel tracking off");static_assert(std::is_same<compute_node_options<Node, ilist_sentinel_tracking<false>>::type,compute_node_options<Node, ilist_tag<void>,ilist_sentinel_tracking<false>>::type>::value,"order shouldn't matter");static_assert(std::is_same<compute_node_options<Node, ilist_sentinel_tracking<true>>::type,compute_node_options<Node, ilist_sentinel_tracking<true>,ilist_tag<void>>::type>::value,"default tag is void, even with sentinel tracking on");static_assert(std::is_same<compute_node_options<Node, ilist_sentinel_tracking<true>>::type,compute_node_options<Node, ilist_tag<void>,ilist_sentinel_tracking<true>>::type>::value,"order shouldn't matter");static_assert(std::is_same<compute_node_options<Node, ilist_sentinel_tracking<true>,ilist_tag<TagA>>::type,compute_node_options<Node, ilist_tag<TagA>,ilist_sentinel_tracking<true>>::type>::value,"order shouldn't matter with real tags");}} // end namespace
//===- unittests/ADT/IListNodeBaseTest.cpp - ilist_node_base unit tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ilist_node_base.h"#include "gtest/gtest.h"using namespace llvm;namespace {typedef ilist_node_base<false> RawNode;typedef ilist_node_base<true> TrackingNode;TEST(IListNodeBaseTest, DefaultConstructor) {RawNode A;EXPECT_EQ(nullptr, A.getPrev());EXPECT_EQ(nullptr, A.getNext());EXPECT_FALSE(A.isKnownSentinel());TrackingNode TA;EXPECT_EQ(nullptr, TA.getPrev());EXPECT_EQ(nullptr, TA.getNext());EXPECT_FALSE(TA.isKnownSentinel());EXPECT_FALSE(TA.isSentinel());}TEST(IListNodeBaseTest, setPrevAndNext) {RawNode A, B, C;A.setPrev(&B);EXPECT_EQ(&B, A.getPrev());EXPECT_EQ(nullptr, A.getNext());EXPECT_EQ(nullptr, B.getPrev());EXPECT_EQ(nullptr, B.getNext());EXPECT_EQ(nullptr, C.getPrev());EXPECT_EQ(nullptr, C.getNext());A.setNext(&C);EXPECT_EQ(&B, A.getPrev());EXPECT_EQ(&C, A.getNext());EXPECT_EQ(nullptr, B.getPrev());EXPECT_EQ(nullptr, B.getNext());EXPECT_EQ(nullptr, C.getPrev());EXPECT_EQ(nullptr, C.getNext());TrackingNode TA, TB, TC;TA.setPrev(&TB);EXPECT_EQ(&TB, TA.getPrev());EXPECT_EQ(nullptr, TA.getNext());EXPECT_EQ(nullptr, TB.getPrev());EXPECT_EQ(nullptr, TB.getNext());EXPECT_EQ(nullptr, TC.getPrev());EXPECT_EQ(nullptr, TC.getNext());TA.setNext(&TC);EXPECT_EQ(&TB, TA.getPrev());EXPECT_EQ(&TC, TA.getNext());EXPECT_EQ(nullptr, TB.getPrev());EXPECT_EQ(nullptr, TB.getNext());EXPECT_EQ(nullptr, TC.getPrev());EXPECT_EQ(nullptr, TC.getNext());}TEST(IListNodeBaseTest, isKnownSentinel) {// Without sentinel tracking.RawNode A, B;EXPECT_FALSE(A.isKnownSentinel());A.setPrev(&B);A.setNext(&B);EXPECT_EQ(&B, A.getPrev());EXPECT_EQ(&B, A.getNext());EXPECT_FALSE(A.isKnownSentinel());A.initializeSentinel();EXPECT_FALSE(A.isKnownSentinel());EXPECT_EQ(&B, A.getPrev());EXPECT_EQ(&B, A.getNext());// With sentinel tracking.TrackingNode TA, TB;EXPECT_FALSE(TA.isKnownSentinel());EXPECT_FALSE(TA.isSentinel());TA.setPrev(&TB);TA.setNext(&TB);EXPECT_EQ(&TB, TA.getPrev());EXPECT_EQ(&TB, TA.getNext());EXPECT_FALSE(TA.isKnownSentinel());EXPECT_FALSE(TA.isSentinel());TA.initializeSentinel();EXPECT_TRUE(TA.isKnownSentinel());EXPECT_TRUE(TA.isSentinel());EXPECT_EQ(&TB, TA.getPrev());EXPECT_EQ(&TB, TA.getNext());}} // end namespace
//===- unittests/ADT/IListIteratorTest.cpp - ilist_iterator unit tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/simple_ilist.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct Node : ilist_node<Node> {};TEST(IListIteratorTest, DefaultConstructor) {simple_ilist<Node>::iterator I;simple_ilist<Node>::reverse_iterator RI;simple_ilist<Node>::const_iterator CI;simple_ilist<Node>::const_reverse_iterator CRI;EXPECT_EQ(nullptr, I.getNodePtr());EXPECT_EQ(nullptr, CI.getNodePtr());EXPECT_EQ(nullptr, RI.getNodePtr());EXPECT_EQ(nullptr, CRI.getNodePtr());EXPECT_EQ(I, I);EXPECT_EQ(I, CI);EXPECT_EQ(CI, I);EXPECT_EQ(CI, CI);EXPECT_EQ(RI, RI);EXPECT_EQ(RI, CRI);EXPECT_EQ(CRI, RI);EXPECT_EQ(CRI, CRI);EXPECT_EQ(I, RI.getReverse());EXPECT_EQ(RI, I.getReverse());}TEST(IListIteratorTest, Empty) {simple_ilist<Node> L;// Check iterators of L.EXPECT_EQ(L.begin(), L.end());EXPECT_EQ(L.rbegin(), L.rend());// Reverse of end should be rend (since the sentinel sits on both sides).EXPECT_EQ(L.end(), L.rend().getReverse());EXPECT_EQ(L.rend(), L.end().getReverse());// Iterators shouldn't match default constructors.simple_ilist<Node>::iterator I;simple_ilist<Node>::reverse_iterator RI;EXPECT_NE(I, L.begin());EXPECT_NE(I, L.end());EXPECT_NE(RI, L.rbegin());EXPECT_NE(RI, L.rend());}TEST(IListIteratorTest, OneNodeList) {simple_ilist<Node> L;Node A;L.insert(L.end(), A);// Check address of reference.EXPECT_EQ(&A, &*L.begin());EXPECT_EQ(&A, &*L.rbegin());// Check that the handle matches.EXPECT_EQ(L.rbegin().getNodePtr(), L.begin().getNodePtr());// Check iteration.EXPECT_EQ(L.end(), ++L.begin());EXPECT_EQ(L.begin(), --L.end());EXPECT_EQ(L.rend(), ++L.rbegin());EXPECT_EQ(L.rbegin(), --L.rend());// Check conversions.EXPECT_EQ(L.rbegin(), L.begin().getReverse());EXPECT_EQ(L.begin(), L.rbegin().getReverse());}TEST(IListIteratorTest, TwoNodeList) {simple_ilist<Node> L;Node A, B;L.insert(L.end(), A);L.insert(L.end(), B);// Check order.EXPECT_EQ(&A, &*L.begin());EXPECT_EQ(&B, &*++L.begin());EXPECT_EQ(L.end(), ++++L.begin());EXPECT_EQ(&B, &*L.rbegin());EXPECT_EQ(&A, &*++L.rbegin());EXPECT_EQ(L.rend(), ++++L.rbegin());// Check conversions.EXPECT_EQ(++L.rbegin(), L.begin().getReverse());EXPECT_EQ(L.rbegin(), (++L.begin()).getReverse());EXPECT_EQ(++L.begin(), L.rbegin().getReverse());EXPECT_EQ(L.begin(), (++L.rbegin()).getReverse());}TEST(IListIteratorTest, CheckEraseForward) {simple_ilist<Node> L;Node A, B;L.insert(L.end(), A);L.insert(L.end(), B);// Erase nodes.auto I = L.begin();EXPECT_EQ(&A, &*I);L.remove(*I++);EXPECT_EQ(&B, &*I);L.remove(*I++);EXPECT_EQ(L.end(), I);}TEST(IListIteratorTest, CheckEraseReverse) {simple_ilist<Node> L;Node A, B;L.insert(L.end(), A);L.insert(L.end(), B);// Erase nodes.auto RI = L.rbegin();EXPECT_EQ(&B, &*RI);L.remove(*RI++);EXPECT_EQ(&A, &*RI);L.remove(*RI++);EXPECT_EQ(L.rend(), RI);}TEST(IListIteratorTest, ReverseConstructor) {simple_ilist<Node> L;const simple_ilist<Node> &CL = L;Node A, B;L.insert(L.end(), A);L.insert(L.end(), B);// Save typing.typedef simple_ilist<Node>::iterator iterator;typedef simple_ilist<Node>::reverse_iterator reverse_iterator;typedef simple_ilist<Node>::const_iterator const_iterator;typedef simple_ilist<Node>::const_reverse_iterator const_reverse_iterator;// Check conversion values.EXPECT_EQ(L.begin(), iterator(L.rend()));EXPECT_EQ(++L.begin(), iterator(++L.rbegin()));EXPECT_EQ(L.end(), iterator(L.rbegin()));EXPECT_EQ(L.rbegin(), reverse_iterator(L.end()));EXPECT_EQ(++L.rbegin(), reverse_iterator(++L.begin()));EXPECT_EQ(L.rend(), reverse_iterator(L.begin()));// Check const iterator constructors.EXPECT_EQ(CL.begin(), const_iterator(L.rend()));EXPECT_EQ(CL.begin(), const_iterator(CL.rend()));EXPECT_EQ(CL.rbegin(), const_reverse_iterator(L.end()));EXPECT_EQ(CL.rbegin(), const_reverse_iterator(CL.end()));// Confirm lack of implicit conversions.static_assert(!std::is_convertible<iterator, reverse_iterator>::value,"unexpected implicit conversion");static_assert(!std::is_convertible<reverse_iterator, iterator>::value,"unexpected implicit conversion");static_assert(!std::is_convertible<const_iterator, const_reverse_iterator>::value,"unexpected implicit conversion");static_assert(!std::is_convertible<const_reverse_iterator, const_iterator>::value,"unexpected implicit conversion");}} // end namespace
//===- unittests/ADT/IListBaseTest.cpp - ilist_base unit tests ------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ilist_base.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Test fixture.template <typename T> class IListBaseTest : public ::testing::Test {};// Test variants with the same test.typedef ::testing::Types<ilist_base<false>, ilist_base<true>>IListBaseTestTypes;TYPED_TEST_SUITE(IListBaseTest, IListBaseTestTypes, );TYPED_TEST(IListBaseTest, insertBeforeImpl) {typedef TypeParam list_base_type;typedef typename list_base_type::node_base_type node_base_type;node_base_type S, A, B;// [S] <-> [S]S.setPrev(&S);S.setNext(&S);// [S] <-> A <-> [S]list_base_type::insertBeforeImpl(S, A);EXPECT_EQ(&A, S.getPrev());EXPECT_EQ(&S, A.getPrev());EXPECT_EQ(&A, S.getNext());EXPECT_EQ(&S, A.getNext());// [S] <-> A <-> B <-> [S]list_base_type::insertBeforeImpl(S, B);EXPECT_EQ(&B, S.getPrev());EXPECT_EQ(&A, B.getPrev());EXPECT_EQ(&S, A.getPrev());EXPECT_EQ(&A, S.getNext());EXPECT_EQ(&B, A.getNext());EXPECT_EQ(&S, B.getNext());}TYPED_TEST(IListBaseTest, removeImpl) {typedef TypeParam list_base_type;typedef typename list_base_type::node_base_type node_base_type;node_base_type S, A, B;// [S] <-> A <-> B <-> [S]S.setPrev(&S);S.setNext(&S);list_base_type::insertBeforeImpl(S, A);list_base_type::insertBeforeImpl(S, B);// [S] <-> B <-> [S]list_base_type::removeImpl(A);EXPECT_EQ(&B, S.getPrev());EXPECT_EQ(&S, B.getPrev());EXPECT_EQ(&B, S.getNext());EXPECT_EQ(&S, B.getNext());EXPECT_EQ(nullptr, A.getPrev());EXPECT_EQ(nullptr, A.getNext());// [S] <-> [S]list_base_type::removeImpl(B);EXPECT_EQ(&S, S.getPrev());EXPECT_EQ(&S, S.getNext());EXPECT_EQ(nullptr, B.getPrev());EXPECT_EQ(nullptr, B.getNext());}TYPED_TEST(IListBaseTest, removeRangeImpl) {typedef TypeParam list_base_type;typedef typename list_base_type::node_base_type node_base_type;node_base_type S, A, B, C, D;// [S] <-> A <-> B <-> C <-> D <-> [S]S.setPrev(&S);S.setNext(&S);list_base_type::insertBeforeImpl(S, A);list_base_type::insertBeforeImpl(S, B);list_base_type::insertBeforeImpl(S, C);list_base_type::insertBeforeImpl(S, D);// [S] <-> A <-> D <-> [S]list_base_type::removeRangeImpl(B, D);EXPECT_EQ(&D, S.getPrev());EXPECT_EQ(&A, D.getPrev());EXPECT_EQ(&S, A.getPrev());EXPECT_EQ(&A, S.getNext());EXPECT_EQ(&D, A.getNext());EXPECT_EQ(&S, D.getNext());EXPECT_EQ(nullptr, B.getPrev());EXPECT_EQ(nullptr, C.getNext());}TYPED_TEST(IListBaseTest, removeRangeImplAllButSentinel) {typedef TypeParam list_base_type;typedef typename list_base_type::node_base_type node_base_type;node_base_type S, A, B;// [S] <-> A <-> B <-> [S]S.setPrev(&S);S.setNext(&S);list_base_type::insertBeforeImpl(S, A);list_base_type::insertBeforeImpl(S, B);// [S] <-> [S]list_base_type::removeRangeImpl(A, S);EXPECT_EQ(&S, S.getPrev());EXPECT_EQ(&S, S.getNext());EXPECT_EQ(nullptr, A.getPrev());EXPECT_EQ(nullptr, B.getNext());}TYPED_TEST(IListBaseTest, transferBeforeImpl) {typedef TypeParam list_base_type;typedef typename list_base_type::node_base_type node_base_type;node_base_type S1, S2, A, B, C, D, E;// [S1] <-> A <-> B <-> C <-> [S1]S1.setPrev(&S1);S1.setNext(&S1);list_base_type::insertBeforeImpl(S1, A);list_base_type::insertBeforeImpl(S1, B);list_base_type::insertBeforeImpl(S1, C);// [S2] <-> D <-> E <-> [S2]S2.setPrev(&S2);S2.setNext(&S2);list_base_type::insertBeforeImpl(S2, D);list_base_type::insertBeforeImpl(S2, E);// [S1] <-> C <-> [S1]list_base_type::transferBeforeImpl(D, A, C);EXPECT_EQ(&C, S1.getPrev());EXPECT_EQ(&S1, C.getPrev());EXPECT_EQ(&C, S1.getNext());EXPECT_EQ(&S1, C.getNext());// [S2] <-> A <-> B <-> D <-> E <-> [S2]EXPECT_EQ(&E, S2.getPrev());EXPECT_EQ(&D, E.getPrev());EXPECT_EQ(&B, D.getPrev());EXPECT_EQ(&A, B.getPrev());EXPECT_EQ(&S2, A.getPrev());EXPECT_EQ(&A, S2.getNext());EXPECT_EQ(&B, A.getNext());EXPECT_EQ(&D, B.getNext());EXPECT_EQ(&E, D.getNext());EXPECT_EQ(&S2, E.getNext());}} // end namespace
//===- llvm/unittest/ADT/HashingTest.cpp ----------------------------------===////// 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////===----------------------------------------------------------------------===////// Hashing.h unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/Hashing.h"#include "llvm/Support/DataTypes.h"#include "llvm/Support/HashBuilder.h"#include "gtest/gtest.h"#include <deque>#include <list>#include <map>#include <vector>namespace llvm {// Helper for test code to print hash codes.void PrintTo(const hash_code &code, std::ostream *os) {*os << static_cast<size_t>(code);}// Fake an object that is recognized as hashable data to test super large// objects.struct LargeTestInteger { uint64_t arr[8]; };struct NonPOD {uint64_t x, y;NonPOD(uint64_t x, uint64_t y) : x(x), y(y) {}friend hash_code hash_value(const NonPOD &obj) {return hash_combine(obj.x, obj.y);}};namespace hashing {namespace detail {template <> struct is_hashable_data<LargeTestInteger> : std::true_type {};} // namespace detail} // namespace hashing} // namespace llvmusing namespace llvm;namespace {enum TestEnumeration {TE_Foo = 42,TE_Bar = 43};TEST(HashingTest, HashValueBasicTest) {int x = 42, y = 43, c = 'x';void *p = nullptr;uint64_t i = 71;const unsigned ci = 71;volatile int vi = 71;const volatile int cvi = 71;uintptr_t addr = reinterpret_cast<uintptr_t>(&y);EXPECT_EQ(hash_value(42), hash_value(x));EXPECT_EQ(hash_value(42), hash_value(TE_Foo));EXPECT_NE(hash_value(42), hash_value(y));EXPECT_NE(hash_value(42), hash_value(TE_Bar));EXPECT_NE(hash_value(42), hash_value(p));EXPECT_EQ(hash_value(71), hash_value(i));EXPECT_EQ(hash_value(71), hash_value(ci));EXPECT_EQ(hash_value(71), hash_value(vi));EXPECT_EQ(hash_value(71), hash_value(cvi));EXPECT_EQ(hash_value(c), hash_value('x'));EXPECT_EQ(hash_value('4'), hash_value('0' + 4));EXPECT_EQ(hash_value(addr), hash_value(&y));}TEST(HashingTest, HashValueStdPair) {EXPECT_EQ(hash_combine(42, 43), hash_value(std::make_pair(42, 43)));EXPECT_NE(hash_combine(43, 42), hash_value(std::make_pair(42, 43)));EXPECT_NE(hash_combine(42, 43), hash_value(std::make_pair(42ull, 43ull)));EXPECT_NE(hash_combine(42, 43), hash_value(std::make_pair(42, 43ull)));EXPECT_NE(hash_combine(42, 43), hash_value(std::make_pair(42ull, 43)));// Note that pairs are implicitly flattened to a direct sequence of data and// hashed efficiently as a consequence.EXPECT_EQ(hash_combine(42, 43, 44),hash_value(std::make_pair(42, std::make_pair(43, 44))));EXPECT_EQ(hash_value(std::make_pair(42, std::make_pair(43, 44))),hash_value(std::make_pair(std::make_pair(42, 43), 44)));// Ensure that pairs which have padding bytes *inside* them don't get treated// this way.EXPECT_EQ(hash_combine('0', hash_combine(1ull, '2')),hash_value(std::make_pair('0', std::make_pair(1ull, '2'))));// Ensure that non-POD pairs don't explode the traits used.NonPOD obj1(1, 2), obj2(3, 4), obj3(5, 6);EXPECT_EQ(hash_combine(obj1, hash_combine(obj2, obj3)),hash_value(std::make_pair(obj1, std::make_pair(obj2, obj3))));}TEST(HashingTest, HashValueStdTuple) {EXPECT_EQ(hash_combine(), hash_value(std::make_tuple()));EXPECT_EQ(hash_combine(42), hash_value(std::make_tuple(42)));EXPECT_EQ(hash_combine(42, 'c'), hash_value(std::make_tuple(42, 'c')));EXPECT_NE(hash_combine(43, 42), hash_value(std::make_tuple(42, 43)));EXPECT_NE(hash_combine(42, 43), hash_value(std::make_tuple(42ull, 43ull)));EXPECT_NE(hash_combine(42, 43), hash_value(std::make_tuple(42, 43ull)));EXPECT_NE(hash_combine(42, 43), hash_value(std::make_tuple(42ull, 43)));}TEST(HashingTest, HashValueStdString) {std::string s = "Hello World!";EXPECT_EQ(hash_combine_range(s.c_str(), s.c_str() + s.size()), hash_value(s));EXPECT_EQ(hash_combine_range(s.c_str(), s.c_str() + s.size() - 1),hash_value(s.substr(0, s.size() - 1)));EXPECT_EQ(hash_combine_range(s.c_str() + 1, s.c_str() + s.size() - 1),hash_value(s.substr(1, s.size() - 2)));std::wstring ws = L"Hello Wide World!";EXPECT_EQ(hash_combine_range(ws.c_str(), ws.c_str() + ws.size()),hash_value(ws));EXPECT_EQ(hash_combine_range(ws.c_str(), ws.c_str() + ws.size() - 1),hash_value(ws.substr(0, ws.size() - 1)));EXPECT_EQ(hash_combine_range(ws.c_str() + 1, ws.c_str() + ws.size() - 1),hash_value(ws.substr(1, ws.size() - 2)));}template <typename T, size_t N> T *begin(T (&arr)[N]) { return arr; }template <typename T, size_t N> T *end(T (&arr)[N]) { return arr + N; }// Provide a dummy, hashable type designed for easy verification: its hash is// the same as its value.struct HashableDummy { size_t value; };hash_code hash_value(HashableDummy dummy) { return dummy.value; }TEST(HashingTest, HashCombineRangeBasicTest) {// Leave this uninitialized in the hope that valgrind will catch bad reads.int dummy;hash_code dummy_hash = hash_combine_range(&dummy, &dummy);EXPECT_NE(hash_code(0), dummy_hash);const int arr1[] = { 1, 2, 3 };hash_code arr1_hash = hash_combine_range(begin(arr1), end(arr1));EXPECT_NE(dummy_hash, arr1_hash);EXPECT_EQ(arr1_hash, hash_combine_range(begin(arr1), end(arr1)));const std::vector<int> vec(begin(arr1), end(arr1));EXPECT_EQ(arr1_hash, hash_combine_range(vec.begin(), vec.end()));const std::list<int> list(begin(arr1), end(arr1));EXPECT_EQ(arr1_hash, hash_combine_range(list.begin(), list.end()));const std::deque<int> deque(begin(arr1), end(arr1));EXPECT_EQ(arr1_hash, hash_combine_range(deque.begin(), deque.end()));const int arr2[] = { 3, 2, 1 };hash_code arr2_hash = hash_combine_range(begin(arr2), end(arr2));EXPECT_NE(dummy_hash, arr2_hash);EXPECT_NE(arr1_hash, arr2_hash);const int arr3[] = { 1, 1, 2, 3 };hash_code arr3_hash = hash_combine_range(begin(arr3), end(arr3));EXPECT_NE(dummy_hash, arr3_hash);EXPECT_NE(arr1_hash, arr3_hash);const int arr4[] = { 1, 2, 3, 3 };hash_code arr4_hash = hash_combine_range(begin(arr4), end(arr4));EXPECT_NE(dummy_hash, arr4_hash);EXPECT_NE(arr1_hash, arr4_hash);const size_t arr5[] = { 1, 2, 3 };const HashableDummy d_arr5[] = { {1}, {2}, {3} };hash_code arr5_hash = hash_combine_range(begin(arr5), end(arr5));hash_code d_arr5_hash = hash_combine_range(begin(d_arr5), end(d_arr5));EXPECT_EQ(arr5_hash, d_arr5_hash);}TEST(HashingTest, HashCombineRangeLengthDiff) {// Test that as only the length varies, we compute different hash codes for// sequences.std::map<size_t, size_t> code_to_size;std::vector<char> all_one_c(256, '\xff');for (unsigned Idx = 1, Size = all_one_c.size(); Idx < Size; ++Idx) {hash_code code = hash_combine_range(&all_one_c[0], &all_one_c[0] + Idx);std::map<size_t, size_t>::iteratorI = code_to_size.insert(std::make_pair(code, Idx)).first;EXPECT_EQ(Idx, I->second);}code_to_size.clear();std::vector<char> all_zero_c(256, '\0');for (unsigned Idx = 1, Size = all_zero_c.size(); Idx < Size; ++Idx) {hash_code code = hash_combine_range(&all_zero_c[0], &all_zero_c[0] + Idx);std::map<size_t, size_t>::iteratorI = code_to_size.insert(std::make_pair(code, Idx)).first;EXPECT_EQ(Idx, I->second);}code_to_size.clear();std::vector<unsigned> all_one_int(512, -1);for (unsigned Idx = 1, Size = all_one_int.size(); Idx < Size; ++Idx) {hash_code code = hash_combine_range(&all_one_int[0], &all_one_int[0] + Idx);std::map<size_t, size_t>::iteratorI = code_to_size.insert(std::make_pair(code, Idx)).first;EXPECT_EQ(Idx, I->second);}code_to_size.clear();std::vector<unsigned> all_zero_int(512, 0);for (unsigned Idx = 1, Size = all_zero_int.size(); Idx < Size; ++Idx) {hash_code code = hash_combine_range(&all_zero_int[0], &all_zero_int[0] + Idx);std::map<size_t, size_t>::iteratorI = code_to_size.insert(std::make_pair(code, Idx)).first;EXPECT_EQ(Idx, I->second);}}TEST(HashingTest, HashCombineRangeGoldenTest) {struct { const char *s; uint64_t hash; } golden_data[] = {#if SIZE_MAX == UINT64_MAX || SIZE_MAX == UINT32_MAX{ "a", 0xaeb6f9d5517c61f8ULL },{ "ab", 0x7ab1edb96be496b4ULL },{ "abc", 0xe38e60bf19c71a3fULL },{ "abcde", 0xd24461a66de97f6eULL },{ "abcdefgh", 0x4ef872ec411dec9dULL },{ "abcdefghijklm", 0xe8a865539f4eadfeULL },{ "abcdefghijklmnopqrstu", 0x261cdf85faaf4e79ULL },{ "abcdefghijklmnopqrstuvwxyzabcdef", 0x43ba70e4198e3b2aULL },{ "abcdefghijklmnopqrstuvwxyzabcdef""abcdefghijklmnopqrstuvwxyzghijkl""abcdefghijklmnopqrstuvwxyzmnopqr""abcdefghijklmnopqrstuvwxyzstuvwx""abcdefghijklmnopqrstuvwxyzyzabcd", 0xdcd57fb2afdf72beULL },{ "a", 0xaeb6f9d5517c61f8ULL },{ "aa", 0xf2b3b69a9736a1ebULL },{ "aaa", 0xf752eb6f07b1cafeULL },{ "aaaaa", 0x812bd21e1236954cULL },{ "aaaaaaaa", 0xff07a2cff08ac587ULL },{ "aaaaaaaaaaaaa", 0x84ac949d54d704ecULL },{ "aaaaaaaaaaaaaaaaaaaaa", 0xcb2c8fb6be8f5648ULL },{ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0xcc40ab7f164091b6ULL },{ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0xc58e174c1e78ffe9ULL },{ "z", 0x1ba160d7e8f8785cULL },{ "zz", 0x2c5c03172f1285d7ULL },{ "zzz", 0x9d2c4f4b507a2ac3ULL },{ "zzzzz", 0x0f03b9031735693aULL },{ "zzzzzzzz", 0xe674147c8582c08eULL },{ "zzzzzzzzzzzzz", 0x3162d9fa6938db83ULL },{ "zzzzzzzzzzzzzzzzzzzzz", 0x37b9a549e013620cULL },{ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", 0x8921470aff885016ULL },{ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz""zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz""zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz""zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz""zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", 0xf60fdcd9beb08441ULL },{ "a", 0xaeb6f9d5517c61f8ULL },{ "ab", 0x7ab1edb96be496b4ULL },{ "aba", 0x3edb049950884d0aULL },{ "ababa", 0x8f2de9e73a97714bULL },{ "abababab", 0xee14a29ddf0ce54cULL },{ "ababababababa", 0x38b3ddaada2d52b4ULL },{ "ababababababababababa", 0xd3665364219f2b85ULL },{ "abababababababababababababababab", 0xa75cd6afbf1bc972ULL },{ "abababababababababababababababab""abababababababababababababababab""abababababababababababababababab""abababababababababababababababab""abababababababababababababababab", 0x840192d129f7a22bULL }#else#error This test only supports 64-bit and 32-bit systems.#endif};for (unsigned i = 0; i < sizeof(golden_data)/sizeof(*golden_data); ++i) {StringRef str = golden_data[i].s;hash_code hash = hash_combine_range(str.begin(), str.end());#if 0 // Enable this to generate paste-able text for the above structure.std::string member_str = "\"" + str.str() + "\",";fprintf(stderr, " { %-35s 0x%016llxULL },\n",member_str.c_str(), static_cast<uint64_t>(hash));#endifEXPECT_EQ(static_cast<size_t>(golden_data[i].hash),static_cast<size_t>(hash));}}TEST(HashingTest, HashCombineBasicTest) {// Hashing a sequence of homogenous types matches range hashing.const int i1 = 42, i2 = 43, i3 = 123, i4 = 999, i5 = 0, i6 = 79;const int arr1[] = { i1, i2, i3, i4, i5, i6 };EXPECT_EQ(hash_combine_range(arr1, arr1 + 1), hash_combine(i1));EXPECT_EQ(hash_combine_range(arr1, arr1 + 2), hash_combine(i1, i2));EXPECT_EQ(hash_combine_range(arr1, arr1 + 3), hash_combine(i1, i2, i3));EXPECT_EQ(hash_combine_range(arr1, arr1 + 4), hash_combine(i1, i2, i3, i4));EXPECT_EQ(hash_combine_range(arr1, arr1 + 5),hash_combine(i1, i2, i3, i4, i5));EXPECT_EQ(hash_combine_range(arr1, arr1 + 6),hash_combine(i1, i2, i3, i4, i5, i6));// Hashing a sequence of heterogeneous types which *happen* to all produce the// same data for hashing produces the same as a range-based hash of the// fundamental values.const size_t s1 = 1024, s2 = 8888, s3 = 9000000;const HashableDummy d1 = { 1024 }, d2 = { 8888 }, d3 = { 9000000 };const size_t arr2[] = { s1, s2, s3 };EXPECT_EQ(hash_combine_range(begin(arr2), end(arr2)),hash_combine(s1, s2, s3));EXPECT_EQ(hash_combine(s1, s2, s3), hash_combine(s1, s2, d3));EXPECT_EQ(hash_combine(s1, s2, s3), hash_combine(s1, d2, s3));EXPECT_EQ(hash_combine(s1, s2, s3), hash_combine(d1, s2, s3));EXPECT_EQ(hash_combine(s1, s2, s3), hash_combine(d1, d2, s3));EXPECT_EQ(hash_combine(s1, s2, s3), hash_combine(d1, d2, d3));// Permuting values causes hashes to change.EXPECT_NE(hash_combine(i1, i1, i1), hash_combine(i1, i1, i2));EXPECT_NE(hash_combine(i1, i1, i1), hash_combine(i1, i2, i1));EXPECT_NE(hash_combine(i1, i1, i1), hash_combine(i2, i1, i1));EXPECT_NE(hash_combine(i1, i1, i1), hash_combine(i2, i2, i1));EXPECT_NE(hash_combine(i1, i1, i1), hash_combine(i2, i2, i2));EXPECT_NE(hash_combine(i2, i1, i1), hash_combine(i1, i1, i2));EXPECT_NE(hash_combine(i1, i1, i2), hash_combine(i1, i2, i1));EXPECT_NE(hash_combine(i1, i2, i1), hash_combine(i2, i1, i1));// Changing type w/o changing value causes hashes to change.EXPECT_NE(hash_combine(i1, i2, i3), hash_combine((char)i1, i2, i3));EXPECT_NE(hash_combine(i1, i2, i3), hash_combine(i1, (char)i2, i3));EXPECT_NE(hash_combine(i1, i2, i3), hash_combine(i1, i2, (char)i3));// This is array of uint64, but it should have the exact same byte pattern as// an array of LargeTestIntegers.const uint64_t bigarr[] = {0xaaaaaaaaababababULL, 0xacacacacbcbcbcbcULL, 0xccddeeffeeddccbbULL,0xdeadbeafdeadbeefULL, 0xfefefefededededeULL, 0xafafafafededededULL,0xffffeeeeddddccccULL, 0xaaaacbcbffffababULL,0xaaaaaaaaababababULL, 0xacacacacbcbcbcbcULL, 0xccddeeffeeddccbbULL,0xdeadbeafdeadbeefULL, 0xfefefefededededeULL, 0xafafafafededededULL,0xffffeeeeddddccccULL, 0xaaaacbcbffffababULL,0xaaaaaaaaababababULL, 0xacacacacbcbcbcbcULL, 0xccddeeffeeddccbbULL,0xdeadbeafdeadbeefULL, 0xfefefefededededeULL, 0xafafafafededededULL,0xffffeeeeddddccccULL, 0xaaaacbcbffffababULL};// Hash a preposterously large integer, both aligned with the buffer and// misaligned.const LargeTestInteger li = { {0xaaaaaaaaababababULL, 0xacacacacbcbcbcbcULL, 0xccddeeffeeddccbbULL,0xdeadbeafdeadbeefULL, 0xfefefefededededeULL, 0xafafafafededededULL,0xffffeeeeddddccccULL, 0xaaaacbcbffffababULL} };// Rotate the storage from 'li'.const LargeTestInteger l2 = { {0xacacacacbcbcbcbcULL, 0xccddeeffeeddccbbULL, 0xdeadbeafdeadbeefULL,0xfefefefededededeULL, 0xafafafafededededULL, 0xffffeeeeddddccccULL,0xaaaacbcbffffababULL, 0xaaaaaaaaababababULL} };const LargeTestInteger l3 = { {0xccddeeffeeddccbbULL, 0xdeadbeafdeadbeefULL, 0xfefefefededededeULL,0xafafafafededededULL, 0xffffeeeeddddccccULL, 0xaaaacbcbffffababULL,0xaaaaaaaaababababULL, 0xacacacacbcbcbcbcULL} };EXPECT_EQ(hash_combine_range(begin(bigarr), end(bigarr)),hash_combine(li, li, li));EXPECT_EQ(hash_combine_range(bigarr, bigarr + 9),hash_combine(bigarr[0], l2));EXPECT_EQ(hash_combine_range(bigarr, bigarr + 10),hash_combine(bigarr[0], bigarr[1], l3));EXPECT_EQ(hash_combine_range(bigarr, bigarr + 17),hash_combine(li, bigarr[0], l2));EXPECT_EQ(hash_combine_range(bigarr, bigarr + 18),hash_combine(li, bigarr[0], bigarr[1], l3));EXPECT_EQ(hash_combine_range(bigarr, bigarr + 18),hash_combine(bigarr[0], l2, bigarr[9], l3));EXPECT_EQ(hash_combine_range(bigarr, bigarr + 20),hash_combine(bigarr[0], l2, bigarr[9], l3, bigarr[18], bigarr[19]));}TEST(HashingTest, HashCombineArgs18) {// This tests that we can pass in up to 18 args.#define CHECK_SAME(...) \EXPECT_EQ(hash_combine(__VA_ARGS__), hash_combine(__VA_ARGS__))CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8, 9);CHECK_SAME(1, 2, 3, 4, 5, 6, 7, 8);CHECK_SAME(1, 2, 3, 4, 5, 6, 7);CHECK_SAME(1, 2, 3, 4, 5, 6);CHECK_SAME(1, 2, 3, 4, 5);CHECK_SAME(1, 2, 3, 4);CHECK_SAME(1, 2, 3);CHECK_SAME(1, 2);CHECK_SAME(1);#undef CHECK_SAME}struct StructWithHashBuilderSupport {char C;int I;template <typename HasherT, llvm::support::endianness Endianness>friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder,const StructWithHashBuilderSupport &Value) {HBuilder.add(Value.C, Value.I);}};TEST(HashingTest, HashWithHashBuilder) {StructWithHashBuilderSupport S{'c', 1};EXPECT_NE(static_cast<size_t>(llvm::hash_value(S)), static_cast<size_t>(0));}struct StructWithHashBuilderAndHashValueSupport {char C;int I;template <typename HasherT, llvm::support::endianness Endianness>friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder,const StructWithHashBuilderAndHashValueSupport &Value) {}friend hash_codehash_value(const StructWithHashBuilderAndHashValueSupport &Value) {return 0xbeef;}};TEST(HashingTest, HashBuilderAndHashValue) {StructWithHashBuilderAndHashValueSupport S{'c', 1};EXPECT_EQ(static_cast<size_t>(hash_value(S)), static_cast<size_t>(0xbeef));}} // namespace
//===- llvm/unittest/ADT/FunctionRefTest.cpp - function_ref unit tests ----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Ensure that there is a default constructor and we can test for a null// function_ref.TEST(FunctionRefTest, Null) {function_ref<int()> F;EXPECT_FALSE(F);auto L = [] { return 1; };F = L;EXPECT_TRUE(F);F = {};EXPECT_FALSE(F);}// Ensure that copies of a function_ref copy the underlying state rather than// causing one function_ref to chain to the next.TEST(FunctionRefTest, Copy) {auto A = [] { return 1; };auto B = [] { return 2; };function_ref<int()> X = A;function_ref<int()> Y = X;X = B;EXPECT_EQ(1, Y());}TEST(FunctionRefTest, BadCopy) {auto A = [] { return 1; };function_ref<int()> X;function_ref<int()> Y = A;function_ref<int()> Z = static_cast<const function_ref<int()> &&>(Y);X = Z;Y = nullptr;ASSERT_EQ(1, X());}// Test that overloads on function_refs are resolved as expected.std::string returns(StringRef) { return "not a function"; }std::string returns(function_ref<double()> F) { return "number"; }std::string returns(function_ref<StringRef()> F) { return "string"; }TEST(FunctionRefTest, SFINAE) {EXPECT_EQ("not a function", returns("boo!"));EXPECT_EQ("number", returns([] { return 42; }));EXPECT_EQ("string", returns([] { return "hello"; }));}} // namespace
//===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/FunctionExtras.h"#include "gtest/gtest.h"#include <memory>#include <type_traits>using namespace llvm;namespace {TEST(UniqueFunctionTest, Basic) {unique_function<int(int, int)> Sum = [](int A, int B) { return A + B; };EXPECT_EQ(Sum(1, 2), 3);unique_function<int(int, int)> Sum2 = std::move(Sum);EXPECT_EQ(Sum2(1, 2), 3);unique_function<int(int, int)> Sum3 = [](int A, int B) { return A + B; };Sum2 = std::move(Sum3);EXPECT_EQ(Sum2(1, 2), 3);Sum2 = unique_function<int(int, int)>([](int A, int B) { return A + B; });EXPECT_EQ(Sum2(1, 2), 3);// Explicit self-move test.*&Sum2 = std::move(Sum2);EXPECT_EQ(Sum2(1, 2), 3);Sum2 = unique_function<int(int, int)>();EXPECT_FALSE(Sum2);// Make sure we can forward through l-value reference parameters.unique_function<void(int &)> Inc = [](int &X) { ++X; };int X = 42;Inc(X);EXPECT_EQ(X, 43);// Make sure we can forward through r-value reference parameters with// move-only types.unique_function<int(std::unique_ptr<int> &&)> ReadAndDeallocByRef =[](std::unique_ptr<int> &&Ptr) {int V = *Ptr;Ptr.reset();return V;};std::unique_ptr<int> Ptr{new int(13)};EXPECT_EQ(ReadAndDeallocByRef(std::move(Ptr)), 13);EXPECT_FALSE((bool)Ptr);// Make sure we can pass a move-only temporary as opposed to a local variable.EXPECT_EQ(ReadAndDeallocByRef(std::unique_ptr<int>(new int(42))), 42);// Make sure we can pass a move-only type by-value.unique_function<int(std::unique_ptr<int>)> ReadAndDeallocByVal =[](std::unique_ptr<int> Ptr) {int V = *Ptr;Ptr.reset();return V;};Ptr.reset(new int(13));EXPECT_EQ(ReadAndDeallocByVal(std::move(Ptr)), 13);EXPECT_FALSE((bool)Ptr);EXPECT_EQ(ReadAndDeallocByVal(std::unique_ptr<int>(new int(42))), 42);}TEST(UniqueFunctionTest, Captures) {long A = 1, B = 2, C = 3, D = 4, E = 5;unique_function<long()> Tmp;unique_function<long()> C1 = [A]() { return A; };EXPECT_EQ(C1(), 1);Tmp = std::move(C1);EXPECT_EQ(Tmp(), 1);unique_function<long()> C2 = [A, B]() { return A + B; };EXPECT_EQ(C2(), 3);Tmp = std::move(C2);EXPECT_EQ(Tmp(), 3);unique_function<long()> C3 = [A, B, C]() { return A + B + C; };EXPECT_EQ(C3(), 6);Tmp = std::move(C3);EXPECT_EQ(Tmp(), 6);unique_function<long()> C4 = [A, B, C, D]() { return A + B + C + D; };EXPECT_EQ(C4(), 10);Tmp = std::move(C4);EXPECT_EQ(Tmp(), 10);unique_function<long()> C5 = [A, B, C, D, E]() { return A + B + C + D + E; };EXPECT_EQ(C5(), 15);Tmp = std::move(C5);EXPECT_EQ(Tmp(), 15);}TEST(UniqueFunctionTest, MoveOnly) {struct SmallCallable {std::unique_ptr<int> A{new int(1)};int operator()(int B) { return *A + B; }};unique_function<int(int)> Small = SmallCallable();EXPECT_EQ(Small(2), 3);unique_function<int(int)> Small2 = std::move(Small);EXPECT_EQ(Small2(2), 3);struct LargeCallable {std::unique_ptr<int> A{new int(1)};std::unique_ptr<int> B{new int(2)};std::unique_ptr<int> C{new int(3)};std::unique_ptr<int> D{new int(4)};std::unique_ptr<int> E{new int(5)};int operator()() { return *A + *B + *C + *D + *E; }};unique_function<int()> Large = LargeCallable();EXPECT_EQ(Large(), 15);unique_function<int()> Large2 = std::move(Large);EXPECT_EQ(Large2(), 15);}TEST(UniqueFunctionTest, CountForwardingCopies) {struct CopyCounter {int &CopyCount;CopyCounter(int &CopyCount) : CopyCount(CopyCount) {}CopyCounter(const CopyCounter &Arg) : CopyCount(Arg.CopyCount) {++CopyCount;}};unique_function<void(CopyCounter)> ByValF = [](CopyCounter) {};int CopyCount = 0;ByValF(CopyCounter(CopyCount));EXPECT_EQ(1, CopyCount);CopyCount = 0;{CopyCounter Counter{CopyCount};ByValF(Counter);}EXPECT_EQ(2, CopyCount);// Check that we don't generate a copy at all when we can bind a reference all// the way down, even if that reference could *in theory* allow copies.unique_function<void(const CopyCounter &)> ByRefF = [](const CopyCounter &) {};CopyCount = 0;ByRefF(CopyCounter(CopyCount));EXPECT_EQ(0, CopyCount);CopyCount = 0;{CopyCounter Counter{CopyCount};ByRefF(Counter);}EXPECT_EQ(0, CopyCount);// If we use a reference, we can make a stronger guarantee that *no* copy// occurs.struct Uncopyable {Uncopyable() = default;Uncopyable(const Uncopyable &) = delete;};unique_function<void(const Uncopyable &)> UncopyableF =[](const Uncopyable &) {};UncopyableF(Uncopyable());Uncopyable X;UncopyableF(X);}TEST(UniqueFunctionTest, CountForwardingMoves) {struct MoveCounter {int &MoveCount;MoveCounter(int &MoveCount) : MoveCount(MoveCount) {}MoveCounter(MoveCounter &&Arg) : MoveCount(Arg.MoveCount) { ++MoveCount; }};unique_function<void(MoveCounter)> ByValF = [](MoveCounter) {};int MoveCount = 0;ByValF(MoveCounter(MoveCount));EXPECT_EQ(1, MoveCount);MoveCount = 0;{MoveCounter Counter{MoveCount};ByValF(std::move(Counter));}EXPECT_EQ(2, MoveCount);// Check that when we use an r-value reference we get no spurious copies.unique_function<void(MoveCounter &&)> ByRefF = [](MoveCounter &&) {};MoveCount = 0;ByRefF(MoveCounter(MoveCount));EXPECT_EQ(0, MoveCount);MoveCount = 0;{MoveCounter Counter{MoveCount};ByRefF(std::move(Counter));}EXPECT_EQ(0, MoveCount);// If we use an r-value reference we can in fact make a stronger guarantee// with an unmovable type.struct Unmovable {Unmovable() = default;Unmovable(Unmovable &&) = delete;};unique_function<void(const Unmovable &)> UnmovableF = [](const Unmovable &) {};UnmovableF(Unmovable());Unmovable X;UnmovableF(X);}TEST(UniqueFunctionTest, Const) {// Can assign from const lambda.unique_function<int(int) const> Plus2 = [X(std::make_unique<int>(2))](int Y) {return *X + Y;};EXPECT_EQ(5, Plus2(3));// Can call through a const ref.const auto &Plus2Ref = Plus2;EXPECT_EQ(5, Plus2Ref(3));// Can move-construct and assign.unique_function<int(int) const> Plus2A = std::move(Plus2);EXPECT_EQ(5, Plus2A(3));unique_function<int(int) const> Plus2B;Plus2B = std::move(Plus2A);EXPECT_EQ(5, Plus2B(3));// Can convert to non-const function type, but not back.unique_function<int(int)> Plus2C = std::move(Plus2B);EXPECT_EQ(5, Plus2C(3));// Overloaded call operator correctly resolved.struct ChooseCorrectOverload {StringRef operator()() { return "non-const"; }StringRef operator()() const { return "const"; }};unique_function<StringRef()> ChooseMutable = ChooseCorrectOverload();ChooseCorrectOverload A;EXPECT_EQ("non-const", ChooseMutable());EXPECT_EQ("non-const", A());unique_function<StringRef() const> ChooseConst = ChooseCorrectOverload();const ChooseCorrectOverload &X = A;EXPECT_EQ("const", ChooseConst());EXPECT_EQ("const", X());}// Test that overloads on unique_functions are resolved as expected.std::string returns(StringRef) { return "not a function"; }std::string returns(unique_function<double()> F) { return "number"; }std::string returns(unique_function<StringRef()> F) { return "string"; }TEST(UniqueFunctionTest, SFINAE) {EXPECT_EQ("not a function", returns("boo!"));EXPECT_EQ("number", returns([] { return 42; }));EXPECT_EQ("string", returns([] { return "hello"; }));}// A forward declared type, and a templated type.class Incomplete;template <typename T> class Templated { T A; };// Check that we can define unique_function that have references to// incomplete types, even if those types are templated over an// incomplete type.TEST(UniqueFunctionTest, IncompleteTypes) {unique_function<void(Templated<Incomplete> &&)>IncompleteArgumentRValueReference;unique_function<void(Templated<Incomplete> &)>IncompleteArgumentLValueReference;unique_function<void(Templated<Incomplete> *)> IncompleteArgumentPointer;unique_function<Templated<Incomplete> &()> IncompleteResultLValueReference;unique_function<Templated<Incomplete> && ()> IncompleteResultRValueReference2;unique_function<Templated<Incomplete> *()> IncompleteResultPointer;}// Incomplete function returning an incomplete typeIncomplete incompleteFunction();const Incomplete incompleteFunctionConst();// Check that we can assign a callable to a unique_function when the// callable return value is incomplete.TEST(UniqueFunctionTest, IncompleteCallableType) {unique_function<Incomplete()> IncompleteReturnInCallable{incompleteFunction};unique_function<const Incomplete()> IncompleteReturnInCallableConst{incompleteFunctionConst};unique_function<const Incomplete()> IncompleteReturnInCallableConstConversion{incompleteFunction};}// Define the incomplete functionclass Incomplete {};Incomplete incompleteFunction() { return {}; }const Incomplete incompleteFunctionConst() { return {}; }} // anonymous namespace
//===- llvm/unittest/ADT/FoldingSetTest.cpp -------------------------------===////// 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////===----------------------------------------------------------------------===////// FoldingSet unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/FoldingSet.h"#include "gtest/gtest.h"#include <string>using namespace llvm;namespace {// Unaligned string test.TEST(FoldingSetTest, UnalignedStringTest) {SCOPED_TRACE("UnalignedStringTest");FoldingSetNodeID a, b;// An aligned string.std::string str1= "a test string";a.AddString(str1);// An unaligned string.std::string str2 = ">" + str1;b.AddString(str2.c_str() + 1);EXPECT_EQ(a.ComputeHash(), b.ComputeHash());}TEST(FoldingSetTest, LongLongComparison) {struct LongLongContainer : FoldingSetNode {unsigned long long A, B;LongLongContainer(unsigned long long A, unsigned long long B): A(A), B(B) {}void Profile(FoldingSetNodeID &ID) const {ID.AddInteger(A);ID.AddInteger(B);}};LongLongContainer C1((1ULL << 32) + 1, 1ULL);LongLongContainer C2(1ULL, (1ULL << 32) + 1);FoldingSet<LongLongContainer> Set;EXPECT_EQ(&C1, Set.GetOrInsertNode(&C1));EXPECT_EQ(&C2, Set.GetOrInsertNode(&C2));EXPECT_EQ(2U, Set.size());}struct TrivialPair : public FoldingSetNode {unsigned Key = 0;unsigned Value = 0;TrivialPair(unsigned K, unsigned V) : FoldingSetNode(), Key(K), Value(V) {}void Profile(FoldingSetNodeID &ID) const {ID.AddInteger(Key);ID.AddInteger(Value);}};TEST(FoldingSetTest, IDComparison) {FoldingSet<TrivialPair> Trivial;TrivialPair T(99, 42);Trivial.InsertNode(&T);void *InsertPos = nullptr;FoldingSetNodeID ID;T.Profile(ID);TrivialPair *N = Trivial.FindNodeOrInsertPos(ID, InsertPos);EXPECT_EQ(&T, N);EXPECT_EQ(nullptr, InsertPos);}TEST(FoldingSetTest, MissedIDComparison) {FoldingSet<TrivialPair> Trivial;TrivialPair S(100, 42);TrivialPair T(99, 42);Trivial.InsertNode(&T);void *InsertPos = nullptr;FoldingSetNodeID ID;S.Profile(ID);TrivialPair *N = Trivial.FindNodeOrInsertPos(ID, InsertPos);EXPECT_EQ(nullptr, N);EXPECT_NE(nullptr, InsertPos);}TEST(FoldingSetTest, RemoveNodeThatIsPresent) {FoldingSet<TrivialPair> Trivial;TrivialPair T(99, 42);Trivial.InsertNode(&T);EXPECT_EQ(Trivial.size(), 1U);bool WasThere = Trivial.RemoveNode(&T);EXPECT_TRUE(WasThere);EXPECT_EQ(0U, Trivial.size());}TEST(FoldingSetTest, RemoveNodeThatIsAbsent) {FoldingSet<TrivialPair> Trivial;TrivialPair T(99, 42);bool WasThere = Trivial.RemoveNode(&T);EXPECT_FALSE(WasThere);EXPECT_EQ(0U, Trivial.size());}TEST(FoldingSetTest, GetOrInsertInserting) {FoldingSet<TrivialPair> Trivial;TrivialPair T(99, 42);TrivialPair *N = Trivial.GetOrInsertNode(&T);EXPECT_EQ(&T, N);}TEST(FoldingSetTest, GetOrInsertGetting) {FoldingSet<TrivialPair> Trivial;TrivialPair T(99, 42);TrivialPair T2(99, 42);Trivial.InsertNode(&T);TrivialPair *N = Trivial.GetOrInsertNode(&T2);EXPECT_EQ(&T, N);}TEST(FoldingSetTest, InsertAtPos) {FoldingSet<TrivialPair> Trivial;void *InsertPos = nullptr;TrivialPair Finder(99, 42);FoldingSetNodeID ID;Finder.Profile(ID);Trivial.FindNodeOrInsertPos(ID, InsertPos);TrivialPair T(99, 42);Trivial.InsertNode(&T, InsertPos);EXPECT_EQ(1U, Trivial.size());}TEST(FoldingSetTest, EmptyIsTrue) {FoldingSet<TrivialPair> Trivial;EXPECT_TRUE(Trivial.empty());}TEST(FoldingSetTest, EmptyIsFalse) {FoldingSet<TrivialPair> Trivial;TrivialPair T(99, 42);Trivial.InsertNode(&T);EXPECT_FALSE(Trivial.empty());}TEST(FoldingSetTest, ClearOnEmpty) {FoldingSet<TrivialPair> Trivial;Trivial.clear();EXPECT_TRUE(Trivial.empty());}TEST(FoldingSetTest, ClearOnNonEmpty) {FoldingSet<TrivialPair> Trivial;TrivialPair T(99, 42);Trivial.InsertNode(&T);Trivial.clear();EXPECT_TRUE(Trivial.empty());}TEST(FoldingSetTest, CapacityLargerThanReserve) {FoldingSet<TrivialPair> Trivial;auto OldCapacity = Trivial.capacity();Trivial.reserve(OldCapacity + 1);EXPECT_GE(Trivial.capacity(), OldCapacity + 1);}TEST(FoldingSetTest, SmallReserveChangesNothing) {FoldingSet<TrivialPair> Trivial;auto OldCapacity = Trivial.capacity();Trivial.reserve(OldCapacity - 1);EXPECT_EQ(Trivial.capacity(), OldCapacity);}}
//===- llvm/unittest/ADT/FloatingPointMode.cpp ----------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/FloatingPointMode.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(FloatingPointModeTest, ParseDenormalFPAttributeComponent) {EXPECT_EQ(DenormalMode::IEEE, parseDenormalFPAttributeComponent("ieee"));EXPECT_EQ(DenormalMode::IEEE, parseDenormalFPAttributeComponent(""));EXPECT_EQ(DenormalMode::PreserveSign,parseDenormalFPAttributeComponent("preserve-sign"));EXPECT_EQ(DenormalMode::PositiveZero,parseDenormalFPAttributeComponent("positive-zero"));EXPECT_EQ(DenormalMode::Invalid, parseDenormalFPAttributeComponent("foo"));}TEST(FloatingPointModeTest, DenormalAttributeName) {EXPECT_EQ("ieee", denormalModeKindName(DenormalMode::IEEE));EXPECT_EQ("preserve-sign", denormalModeKindName(DenormalMode::PreserveSign));EXPECT_EQ("positive-zero", denormalModeKindName(DenormalMode::PositiveZero));EXPECT_EQ("", denormalModeKindName(DenormalMode::Invalid));}TEST(FloatingPointModeTest, ParseDenormalFPAttribute) {EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE),parseDenormalFPAttribute("ieee"));EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE),parseDenormalFPAttribute("ieee,ieee"));EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE),parseDenormalFPAttribute("ieee,"));EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE),parseDenormalFPAttribute(""));EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE),parseDenormalFPAttribute(","));EXPECT_EQ(DenormalMode(DenormalMode::PreserveSign, DenormalMode::PreserveSign),parseDenormalFPAttribute("preserve-sign"));EXPECT_EQ(DenormalMode(DenormalMode::PreserveSign, DenormalMode::PreserveSign),parseDenormalFPAttribute("preserve-sign,"));EXPECT_EQ(DenormalMode(DenormalMode::PreserveSign, DenormalMode::PreserveSign),parseDenormalFPAttribute("preserve-sign,preserve-sign"));EXPECT_EQ(DenormalMode(DenormalMode::PositiveZero, DenormalMode::PositiveZero),parseDenormalFPAttribute("positive-zero"));EXPECT_EQ(DenormalMode(DenormalMode::PositiveZero, DenormalMode::PositiveZero),parseDenormalFPAttribute("positive-zero,positive-zero"));EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::PositiveZero),parseDenormalFPAttribute("ieee,positive-zero"));EXPECT_EQ(DenormalMode(DenormalMode::PositiveZero, DenormalMode::IEEE),parseDenormalFPAttribute("positive-zero,ieee"));EXPECT_EQ(DenormalMode(DenormalMode::PreserveSign, DenormalMode::IEEE),parseDenormalFPAttribute("preserve-sign,ieee"));EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::PreserveSign),parseDenormalFPAttribute("ieee,preserve-sign"));EXPECT_EQ(DenormalMode(DenormalMode::Invalid, DenormalMode::Invalid),parseDenormalFPAttribute("foo"));EXPECT_EQ(DenormalMode(DenormalMode::Invalid, DenormalMode::Invalid),parseDenormalFPAttribute("foo,foo"));EXPECT_EQ(DenormalMode(DenormalMode::Invalid, DenormalMode::Invalid),parseDenormalFPAttribute("foo,bar"));}TEST(FloatingPointModeTest, RenderDenormalFPAttribute) {EXPECT_EQ(DenormalMode(DenormalMode::Invalid, DenormalMode::Invalid),parseDenormalFPAttribute("foo"));EXPECT_EQ("ieee,ieee",DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE).str());EXPECT_EQ(",",DenormalMode(DenormalMode::Invalid, DenormalMode::Invalid).str());EXPECT_EQ("preserve-sign,preserve-sign",DenormalMode(DenormalMode::PreserveSign, DenormalMode::PreserveSign).str());EXPECT_EQ("positive-zero,positive-zero",DenormalMode(DenormalMode::PositiveZero, DenormalMode::PositiveZero).str());EXPECT_EQ("ieee,preserve-sign",DenormalMode(DenormalMode::IEEE, DenormalMode::PreserveSign).str());EXPECT_EQ("preserve-sign,ieee",DenormalMode(DenormalMode::PreserveSign, DenormalMode::IEEE).str());EXPECT_EQ("preserve-sign,positive-zero",DenormalMode(DenormalMode::PreserveSign, DenormalMode::PositiveZero).str());}TEST(FloatingPointModeTest, DenormalModeIsSimple) {EXPECT_TRUE(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE).isSimple());EXPECT_FALSE(DenormalMode(DenormalMode::IEEE,DenormalMode::Invalid).isSimple());EXPECT_FALSE(DenormalMode(DenormalMode::PreserveSign,DenormalMode::PositiveZero).isSimple());}TEST(FloatingPointModeTest, DenormalModeIsValid) {EXPECT_TRUE(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE).isValid());EXPECT_FALSE(DenormalMode(DenormalMode::IEEE, DenormalMode::Invalid).isValid());EXPECT_FALSE(DenormalMode(DenormalMode::Invalid, DenormalMode::IEEE).isValid());EXPECT_FALSE(DenormalMode(DenormalMode::Invalid,DenormalMode::Invalid).isValid());}TEST(FloatingPointModeTest, DenormalModeConstructor) {EXPECT_EQ(DenormalMode(DenormalMode::Invalid, DenormalMode::Invalid),DenormalMode::getInvalid());EXPECT_EQ(DenormalMode(DenormalMode::IEEE, DenormalMode::IEEE),DenormalMode::getIEEE());EXPECT_EQ(DenormalMode(DenormalMode::PreserveSign, DenormalMode::PreserveSign),DenormalMode::getPreserveSign());EXPECT_EQ(DenormalMode(DenormalMode::PositiveZero, DenormalMode::PositiveZero),DenormalMode::getPositiveZero());}}
//===- unittests/ADT/FallibleIteratorTest.cpp - fallible_iterator.h tests -===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/fallible_iterator.h"#include "llvm/Testing/Support/Error.h"#include "gtest/gtest-spi.h"#include "gtest/gtest.h"#include <utility>#include <vector>using namespace llvm;namespace {using ItemValid = enum { ValidItem, InvalidItem };using LinkValid = enum { ValidLink, InvalidLink };class Item {public:Item(ItemValid V) : V(V) {}bool isValid() const { return V == ValidItem; }private:ItemValid V;};// A utility to mock "bad collections". It supports both invalid items,// where the dereference operator may return an Error, and bad links// where the inc/dec operations may return an Error.// Each element of the mock collection contains a pair of a (possibly broken)// item and link.using FallibleCollection = std::vector<std::pair<Item, LinkValid>>;class FallibleCollectionWalker {public:FallibleCollectionWalker(FallibleCollection &C, unsigned Idx): C(C), Idx(Idx) {}Item &operator*() { return C[Idx].first; }const Item &operator*() const { return C[Idx].first; }Error inc() {assert(Idx != C.size() && "Walking off end of (mock) collection");if (C[Idx].second == ValidLink) {++Idx;return Error::success();}return make_error<StringError>("cant get next object in (mock) collection",inconvertibleErrorCode());}Error dec() {assert(Idx != 0 && "Walking off start of (mock) collection");--Idx;if (C[Idx].second == ValidLink)return Error::success();return make_error<StringError>("cant get prev object in (mock) collection",inconvertibleErrorCode());}friend bool operator==(const FallibleCollectionWalker &LHS,const FallibleCollectionWalker &RHS) {assert(&LHS.C == &RHS.C && "Comparing iterators across collectionss.");return LHS.Idx == RHS.Idx;}private:FallibleCollection &C;unsigned Idx;};class FallibleCollectionWalkerWithStructDeref: public FallibleCollectionWalker {public:using FallibleCollectionWalker::FallibleCollectionWalker;Item *operator->() { return &this->operator*(); }const Item *operator->() const { return &this->operator*(); }};class FallibleCollectionWalkerWithFallibleDeref: public FallibleCollectionWalker {public:using FallibleCollectionWalker::FallibleCollectionWalker;Expected<Item &> operator*() {auto &I = FallibleCollectionWalker::operator*();if (!I.isValid())return make_error<StringError>("bad item", inconvertibleErrorCode());return I;}Expected<const Item &> operator*() const {const auto &I = FallibleCollectionWalker::operator*();if (!I.isValid())return make_error<StringError>("bad item", inconvertibleErrorCode());return I;}};TEST(FallibleIteratorTest, BasicSuccess) {// Check that a basic use-case involing successful iteration over a// "FallibleCollection" works.FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});FallibleCollectionWalker begin(C, 0);FallibleCollectionWalker end(C, 2);Error Err = Error::success();for (auto &Elem :make_fallible_range<FallibleCollectionWalker>(begin, end, Err))EXPECT_TRUE(Elem.isValid());cantFail(std::move(Err));}TEST(FallibleIteratorTest, BasicFailure) {// Check that a iteration failure (due to the InvalidLink state on element one// of the fallible collection) breaks out of the loop and raises an Error.FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, InvalidLink}});FallibleCollectionWalker begin(C, 0);FallibleCollectionWalker end(C, 2);Error Err = Error::success();for (auto &Elem :make_fallible_range<FallibleCollectionWalker>(begin, end, Err))EXPECT_TRUE(Elem.isValid());EXPECT_THAT_ERROR(std::move(Err), Failed()) << "Expected failure value";}TEST(FallibleIteratorTest, NoRedundantErrorCheckOnEarlyExit) {// Check that an early return from the loop body does not require a redundant// check of Err.FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});FallibleCollectionWalker begin(C, 0);FallibleCollectionWalker end(C, 2);Error Err = Error::success();for (auto &Elem :make_fallible_range<FallibleCollectionWalker>(begin, end, Err)) {(void)Elem;return;}// Err not checked, but should be ok because we exit from the loop// body.}#if LLVM_ENABLE_ABI_BREAKING_CHECKSTEST(FallibleIteratorTest, RegularLoopExitRequiresErrorCheck) {// Check that Err must be checked after a normal (i.e. not early) loop exit// by failing to check and expecting program death (due to the unchecked// error).EXPECT_DEATH({FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});FallibleCollectionWalker begin(C, 0);FallibleCollectionWalker end(C, 2);Error Err = Error::success();for (auto &Elem :make_fallible_range<FallibleCollectionWalker>(begin, end, Err))(void)Elem;},"Program aborted due to an unhandled Error:")<< "Normal (i.e. not early) loop exit should require an error check";}#endifTEST(FallibleIteratorTest, RawIncrementAndDecrementBehavior) {// Check the exact behavior of increment / decrement.FallibleCollection C({{ValidItem, ValidLink},{ValidItem, InvalidLink},{ValidItem, ValidLink},{ValidItem, InvalidLink}});{// One increment from begin succeeds.Error Err = Error::success();auto I = make_fallible_itr(FallibleCollectionWalker(C, 0), Err);++I;EXPECT_THAT_ERROR(std::move(Err), Succeeded());}{// Two increments from begin fail.Error Err = Error::success();auto I = make_fallible_itr(FallibleCollectionWalker(C, 0), Err);++I;EXPECT_THAT_ERROR(std::move(Err), Succeeded());++I;EXPECT_THAT_ERROR(std::move(Err), Failed()) << "Expected failure value";}{// One decement from element three succeeds.Error Err = Error::success();auto I = make_fallible_itr(FallibleCollectionWalker(C, 3), Err);--I;EXPECT_THAT_ERROR(std::move(Err), Succeeded());}{// One decement from element three succeeds.Error Err = Error::success();auto I = make_fallible_itr(FallibleCollectionWalker(C, 3), Err);--I;EXPECT_THAT_ERROR(std::move(Err), Succeeded());--I;EXPECT_THAT_ERROR(std::move(Err), Failed());}}TEST(FallibleIteratorTest, CheckStructDerefOperatorSupport) {// Check that the fallible_iterator wrapper forwards through to the// underlying iterator's structure dereference operator if present.FallibleCollection C({{ValidItem, ValidLink},{ValidItem, ValidLink},{InvalidItem, InvalidLink}});FallibleCollectionWalkerWithStructDeref begin(C, 0);{Error Err = Error::success();auto I = make_fallible_itr(begin, Err);EXPECT_TRUE(I->isValid());cantFail(std::move(Err));}{Error Err = Error::success();const auto I = make_fallible_itr(begin, Err);EXPECT_TRUE(I->isValid());cantFail(std::move(Err));}}TEST(FallibleIteratorTest, CheckDerefToExpectedSupport) {// Check that the fallible_iterator wrapper forwards value types, in// particular llvm::Expected, correctly.FallibleCollection C({{ValidItem, ValidLink},{InvalidItem, ValidLink},{ValidItem, ValidLink}});FallibleCollectionWalkerWithFallibleDeref begin(C, 0);FallibleCollectionWalkerWithFallibleDeref end(C, 3);Error Err = Error::success();auto I = make_fallible_itr(begin, Err);auto E = make_fallible_end(end);Expected<Item> V1 = *I;EXPECT_THAT_ERROR(V1.takeError(), Succeeded());++I;EXPECT_NE(I, E); // Implicitly check error.Expected<Item> V2 = *I;EXPECT_THAT_ERROR(V2.takeError(), Failed());++I;EXPECT_NE(I, E); // Implicitly check error.Expected<Item> V3 = *I;EXPECT_THAT_ERROR(V3.takeError(), Succeeded());++I;EXPECT_EQ(I, E);cantFail(std::move(Err));}} // namespace
//=== llvm/unittest/ADT/EquivalenceClassesTest.cpp - the structure tests --===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/EquivalenceClasses.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {TEST(EquivalenceClassesTest, NoMerges) {EquivalenceClasses<int> EqClasses;// Until we merged any sets, check that every element is only equivalent to// itself.for (int i = 0; i < 3; i++)for (int j = 0; j < 3; j++)if (i == j)EXPECT_TRUE(EqClasses.isEquivalent(i, j));elseEXPECT_FALSE(EqClasses.isEquivalent(i, j));}TEST(EquivalenceClassesTest, SimpleMerge1) {EquivalenceClasses<int> EqClasses;// Check that once we merge (A, B), (B, C), (C, D), then all elements belong// to one set.EqClasses.unionSets(0, 1);EqClasses.unionSets(1, 2);EqClasses.unionSets(2, 3);for (int i = 0; i < 4; ++i)for (int j = 0; j < 4; ++j)EXPECT_TRUE(EqClasses.isEquivalent(i, j));}TEST(EquivalenceClassesTest, SimpleMerge2) {EquivalenceClasses<int> EqClasses;// Check that once we merge (A, B), (C, D), (A, C), then all elements belong// to one set.EqClasses.unionSets(0, 1);EqClasses.unionSets(2, 3);EqClasses.unionSets(0, 2);for (int i = 0; i < 4; ++i)for (int j = 0; j < 4; ++j)EXPECT_TRUE(EqClasses.isEquivalent(i, j));}TEST(EquivalenceClassesTest, TwoSets) {EquivalenceClasses<int> EqClasses;// Form sets of odd and even numbers, check that we split them into these// two sets correcrly.for (int i = 0; i < 30; i += 2)EqClasses.unionSets(0, i);for (int i = 1; i < 30; i += 2)EqClasses.unionSets(1, i);for (int i = 0; i < 30; i++)for (int j = 0; j < 30; j++)if (i % 2 == j % 2)EXPECT_TRUE(EqClasses.isEquivalent(i, j));elseEXPECT_FALSE(EqClasses.isEquivalent(i, j));}// Type-parameterized tests: Run the same test cases with different element// types.template <typename T> class ParameterizedTest : public testing::Test {};TYPED_TEST_SUITE_P(ParameterizedTest);TYPED_TEST_P(ParameterizedTest, MultipleSets) {TypeParam EqClasses;// Split numbers from [0, 100) into sets so that values in the same set have// equal remainders (mod 17).for (int i = 0; i < 100; i++)EqClasses.unionSets(i % 17, i);for (int i = 0; i < 100; i++)for (int j = 0; j < 100; j++)if (i % 17 == j % 17)EXPECT_TRUE(EqClasses.isEquivalent(i, j));elseEXPECT_FALSE(EqClasses.isEquivalent(i, j));}namespace {// A dummy struct for testing EquivalenceClasses with a comparator.struct TestStruct {TestStruct(int value) : value(value) {}bool operator==(const TestStruct &other) const {return value == other.value;}int value;};// Comparator to be used in test case.struct TestStructComparator {bool operator()(const TestStruct &lhs, const TestStruct &rhs) const {return lhs.value < rhs.value;}};} // namespaceREGISTER_TYPED_TEST_SUITE_P(ParameterizedTest, MultipleSets);using ParamTypes =testing::Types<EquivalenceClasses<int>,EquivalenceClasses<TestStruct, TestStructComparator>>;INSTANTIATE_TYPED_TEST_SUITE_P(EquivalenceClassesTest, ParameterizedTest,ParamTypes, );} // llvm
//===- llvm/unittest/ADT/EnumeratedArrayTest.cpp ----------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// EnumeratedArray unit tests.////===----------------------------------------------------------------------===//#include "llvm/ADT/EnumeratedArray.h"#include "gtest/gtest.h"namespace llvm {//===--------------------------------------------------------------------===//// Test initialization and use of operator[] for both read and write.//===--------------------------------------------------------------------===//TEST(EnumeratedArray, InitAndIndex) {enum class Colors { Red, Blue, Green, Last = Green };EnumeratedArray<float, Colors, Colors::Last, size_t> Array1;Array1[Colors::Red] = 1.0;Array1[Colors::Blue] = 2.0;Array1[Colors::Green] = 3.0;EXPECT_EQ(Array1[Colors::Red], 1.0);EXPECT_EQ(Array1[Colors::Blue], 2.0);EXPECT_EQ(Array1[Colors::Green], 3.0);EnumeratedArray<bool, Colors> Array2(true);EXPECT_TRUE(Array2[Colors::Red]);EXPECT_TRUE(Array2[Colors::Blue]);EXPECT_TRUE(Array2[Colors::Green]);Array2[Colors::Red] = true;Array2[Colors::Blue] = false;Array2[Colors::Green] = true;EXPECT_TRUE(Array2[Colors::Red]);EXPECT_FALSE(Array2[Colors::Blue]);EXPECT_TRUE(Array2[Colors::Green]);}} // namespace llvm
//===- llvm/unittest/Support/EditDistanceTest.cpp - Edit distance tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/StringRef.h"#include "llvm/ADT/edit_distance.h"#include "gtest/gtest.h"#include <cstdlib>using namespace llvm;namespace {struct Result {unsigned NumMaps;unsigned EditDist;};} // namespacestatic Result editDistanceAndMaps(StringRef A, StringRef B,unsigned MaxEditDistance = 0) {unsigned NumMaps = 0;auto TrackMaps = [&](const char X) {++NumMaps;return X;};unsigned EditDist = llvm::ComputeMappedEditDistance(makeArrayRef(A.data(), A.size()), makeArrayRef(B.data(), B.size()),TrackMaps, true, MaxEditDistance);return {NumMaps, EditDist};}TEST(EditDistance, VerifyShortCircuit) {StringRef Hello = "Hello";StringRef HelloWorld = "HelloWorld";Result R = editDistanceAndMaps(Hello, HelloWorld, 5);EXPECT_EQ(R.EditDist, 5U);EXPECT_GT(R.NumMaps, 0U);R = editDistanceAndMaps(Hello, HelloWorld);EXPECT_EQ(R.EditDist, 5U);EXPECT_GT(R.NumMaps, 0U);R = editDistanceAndMaps(Hello, HelloWorld, 4);EXPECT_EQ(R.EditDist, 5U);EXPECT_EQ(R.NumMaps, 0U);R = editDistanceAndMaps(HelloWorld, Hello, 4);EXPECT_EQ(R.EditDist, 5U);EXPECT_EQ(R.NumMaps, 0U);R = editDistanceAndMaps(Hello, HelloWorld, 1);EXPECT_EQ(R.EditDist, 2U);EXPECT_EQ(R.NumMaps, 0U);R = editDistanceAndMaps(HelloWorld, Hello, 1);EXPECT_EQ(R.EditDist, 2U);EXPECT_EQ(R.NumMaps, 0U);}
//===- llvm/unittest/ADT/DirectedGraphTest.cpp ------------------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// This file defines concrete derivations of the directed-graph base classes// for testing purposes.////===----------------------------------------------------------------------===//#include "llvm/ADT/DirectedGraph.h"#include "llvm/ADT/GraphTraits.h"#include "llvm/ADT/SCCIterator.h"#include "llvm/ADT/SmallPtrSet.h"#include "gtest/gtest.h"namespace llvm {//===--------------------------------------------------------------------===//// Derived nodes, edges and graph types based on DirectedGraph.//===--------------------------------------------------------------------===//class DGTestNode;class DGTestEdge;using DGTestNodeBase = DGNode<DGTestNode, DGTestEdge>;using DGTestEdgeBase = DGEdge<DGTestNode, DGTestEdge>;using DGTestBase = DirectedGraph<DGTestNode, DGTestEdge>;class DGTestNode : public DGTestNodeBase {public:DGTestNode() = default;};class DGTestEdge : public DGTestEdgeBase {public:DGTestEdge() = delete;DGTestEdge(DGTestNode &N) : DGTestEdgeBase(N) {}};class DGTestGraph : public DGTestBase {public:DGTestGraph() = default;~DGTestGraph(){};};using EdgeListTy = SmallVector<DGTestEdge *, 2>;//===--------------------------------------------------------------------===//// GraphTraits specializations for the DGTest//===--------------------------------------------------------------------===//template <> struct GraphTraits<DGTestNode *> {using NodeRef = DGTestNode *;static DGTestNode *DGTestGetTargetNode(DGEdge<DGTestNode, DGTestEdge> *P) {return &P->getTargetNode();}// Provide a mapped iterator so that the GraphTrait-based implementations can// find the target nodes without having to explicitly go through the edges.using ChildIteratorType =mapped_iterator<DGTestNode::iterator, decltype(&DGTestGetTargetNode)>;using ChildEdgeIteratorType = DGTestNode::iterator;static NodeRef getEntryNode(NodeRef N) { return N; }static ChildIteratorType child_begin(NodeRef N) {return ChildIteratorType(N->begin(), &DGTestGetTargetNode);}static ChildIteratorType child_end(NodeRef N) {return ChildIteratorType(N->end(), &DGTestGetTargetNode);}static ChildEdgeIteratorType child_edge_begin(NodeRef N) {return N->begin();}static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }};template <>struct GraphTraits<DGTestGraph *> : public GraphTraits<DGTestNode *> {using nodes_iterator = DGTestGraph::iterator;static NodeRef getEntryNode(DGTestGraph *DG) { return *DG->begin(); }static nodes_iterator nodes_begin(DGTestGraph *DG) { return DG->begin(); }static nodes_iterator nodes_end(DGTestGraph *DG) { return DG->end(); }};//===--------------------------------------------------------------------===//// Test various modification and query functions.//===--------------------------------------------------------------------===//TEST(DirectedGraphTest, AddAndConnectNodes) {DGTestGraph DG;DGTestNode N1, N2, N3;DGTestEdge E1(N1), E2(N2), E3(N3);// Check that new nodes can be added successfully.EXPECT_TRUE(DG.addNode(N1));EXPECT_TRUE(DG.addNode(N2));EXPECT_TRUE(DG.addNode(N3));// Check that duplicate nodes are not added to the graph.EXPECT_FALSE(DG.addNode(N1));// Check that nodes can be connected using valid edges with no errors.EXPECT_TRUE(DG.connect(N1, N2, E2));EXPECT_TRUE(DG.connect(N2, N3, E3));EXPECT_TRUE(DG.connect(N3, N1, E1));// The graph looks like this now://// +---------------+// v |// N1 -> N2 -> N3 -+// Check that already connected nodes with the given edge are not connected// again (ie. edges are between nodes are not duplicated).EXPECT_FALSE(DG.connect(N3, N1, E1));// Check that there are 3 nodes in the graph.EXPECT_TRUE(DG.size() == 3);// Check that the added nodes can be found in the graph.EXPECT_NE(DG.findNode(N3), DG.end());// Check that nodes that are not part of the graph are not found.DGTestNode N4;EXPECT_EQ(DG.findNode(N4), DG.end());// Check that findIncommingEdgesToNode works correctly.EdgeListTy EL;EXPECT_TRUE(DG.findIncomingEdgesToNode(N1, EL));EXPECT_TRUE(EL.size() == 1);EXPECT_EQ(*EL[0], E1);}TEST(DirectedGraphTest, AddRemoveEdge) {DGTestGraph DG;DGTestNode N1, N2, N3;DGTestEdge E1(N1), E2(N2), E3(N3);DG.addNode(N1);DG.addNode(N2);DG.addNode(N3);DG.connect(N1, N2, E2);DG.connect(N2, N3, E3);DG.connect(N3, N1, E1);// The graph looks like this now://// +---------------+// v |// N1 -> N2 -> N3 -+// Check that there are 3 nodes in the graph.EXPECT_TRUE(DG.size() == 3);// Check that the target nodes of the edges are correct.EXPECT_EQ(E1.getTargetNode(), N1);EXPECT_EQ(E2.getTargetNode(), N2);EXPECT_EQ(E3.getTargetNode(), N3);// Remove the edge from N1 to N2.N1.removeEdge(E2);// The graph looks like this now://// N2 -> N3 -> N1// Check that there are no incoming edges to N2.EdgeListTy EL;EXPECT_FALSE(DG.findIncomingEdgesToNode(N2, EL));EXPECT_TRUE(EL.empty());// Put the edge from N1 to N2 back in place.N1.addEdge(E2);// Check that E2 is the only incoming edge to N2.EL.clear();EXPECT_TRUE(DG.findIncomingEdgesToNode(N2, EL));EXPECT_EQ(*EL[0], E2);}TEST(DirectedGraphTest, hasEdgeTo) {DGTestGraph DG;DGTestNode N1, N2, N3;DGTestEdge E1(N1), E2(N2), E3(N3), E4(N1);DG.addNode(N1);DG.addNode(N2);DG.addNode(N3);DG.connect(N1, N2, E2);DG.connect(N2, N3, E3);DG.connect(N3, N1, E1);DG.connect(N2, N1, E4);// The graph looks like this now://// +-----+// v |// N1 -> N2 -> N3// ^ |// +-----------+EXPECT_TRUE(N2.hasEdgeTo(N1));EXPECT_TRUE(N3.hasEdgeTo(N1));}TEST(DirectedGraphTest, AddRemoveNode) {DGTestGraph DG;DGTestNode N1, N2, N3;DGTestEdge E1(N1), E2(N2), E3(N3);DG.addNode(N1);DG.addNode(N2);DG.addNode(N3);DG.connect(N1, N2, E2);DG.connect(N2, N3, E3);DG.connect(N3, N1, E1);// The graph looks like this now://// +---------------+// v |// N1 -> N2 -> N3 -+// Check that there are 3 nodes in the graph.EXPECT_TRUE(DG.size() == 3);// Check that a node in the graph can be removed, but not more than once.EXPECT_TRUE(DG.removeNode(N1));EXPECT_EQ(DG.findNode(N1), DG.end());EXPECT_FALSE(DG.removeNode(N1));// The graph looks like this now://// N2 -> N3// Check that there are 2 nodes in the graph and only N2 is connected to N3.EXPECT_TRUE(DG.size() == 2);EXPECT_TRUE(N3.getEdges().empty());EdgeListTy EL;EXPECT_FALSE(DG.findIncomingEdgesToNode(N2, EL));EXPECT_TRUE(EL.empty());}TEST(DirectedGraphTest, SCC) {DGTestGraph DG;DGTestNode N1, N2, N3, N4;DGTestEdge E1(N1), E2(N2), E3(N3), E4(N4);DG.addNode(N1);DG.addNode(N2);DG.addNode(N3);DG.addNode(N4);DG.connect(N1, N2, E2);DG.connect(N2, N3, E3);DG.connect(N3, N1, E1);DG.connect(N3, N4, E4);// The graph looks like this now://// +---------------+// v |// N1 -> N2 -> N3 -+ N4// | ^// +--------+// Test that there are two SCCs:// 1. {N1, N2, N3}// 2. {N4}using NodeListTy = SmallPtrSet<DGTestNode *, 3>;SmallVector<NodeListTy, 4> ListOfSCCs;for (auto &SCC : make_range(scc_begin(&DG), scc_end(&DG)))ListOfSCCs.push_back(NodeListTy(SCC.begin(), SCC.end()));EXPECT_TRUE(ListOfSCCs.size() == 2);for (auto &SCC : ListOfSCCs) {if (SCC.size() > 1)continue;EXPECT_TRUE(SCC.size() == 1);EXPECT_TRUE(SCC.count(&N4) == 1);}for (auto &SCC : ListOfSCCs) {if (SCC.size() <= 1)continue;EXPECT_TRUE(SCC.size() == 3);EXPECT_TRUE(SCC.count(&N1) == 1);EXPECT_TRUE(SCC.count(&N2) == 1);EXPECT_TRUE(SCC.count(&N3) == 1);EXPECT_TRUE(SCC.count(&N4) == 0);}}} // namespace llvm
//=== llvm/unittest/ADT/DepthFirstIteratorTest.cpp - DFS iterator tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/DepthFirstIterator.h"#include "TestGraph.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {template <typename T> struct CountedSet {typedef typename SmallPtrSet<T, 4>::iterator iterator;SmallPtrSet<T, 4> S;int InsertVisited = 0;std::pair<iterator, bool> insert(const T &Item) {InsertVisited++;return S.insert(Item);}size_t count(const T &Item) const { return S.count(Item); }void completed(T) { }};template <typename T> class df_iterator_storage<CountedSet<T>, true> {public:df_iterator_storage(CountedSet<T> &VSet) : Visited(VSet) {}CountedSet<T> &Visited;};TEST(DepthFirstIteratorTest, ActuallyUpdateIterator) {typedef CountedSet<Graph<3>::NodeType *> StorageT;typedef df_iterator<Graph<3>, StorageT, true> DFIter;Graph<3> G;G.AddEdge(0, 1);G.AddEdge(0, 2);StorageT S;for (auto N : make_range(DFIter::begin(G, S), DFIter::end(G, S)))(void)N;EXPECT_EQ(3, S.InsertVisited);}}
//===- llvm/unittest/ADT/DenseSetTest.cpp - DenseSet unit tests --*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/DenseSet.h"#include "gtest/gtest.h"#include <type_traits>using namespace llvm;namespace {static_assert(std::is_const<std::remove_pointer<DenseSet<int>::const_iterator::pointer>::type>::value,"Iterator pointer type should be const");static_assert(std::is_const<std::remove_reference<DenseSet<int>::const_iterator::reference>::type>::value,"Iterator reference type should be const");// Test hashing with a set of only two entries.TEST(DenseSetTest, DoubleEntrySetTest) {llvm::DenseSet<unsigned> set(2);set.insert(0);set.insert(1);// Original failure was an infinite loop in this call:EXPECT_EQ(0u, set.count(2));}struct TestDenseSetInfo {static inline unsigned getEmptyKey() { return ~0; }static inline unsigned getTombstoneKey() { return ~0U - 1; }static unsigned getHashValue(const unsigned& Val) { return Val * 37U; }static unsigned getHashValue(const char* Val) {return (unsigned)(Val[0] - 'a') * 37U;}static bool isEqual(const unsigned& LHS, const unsigned& RHS) {return LHS == RHS;}static bool isEqual(const char* LHS, const unsigned& RHS) {return (unsigned)(LHS[0] - 'a') == RHS;}};// Test fixturetemplate <typename T> class DenseSetTest : public testing::Test {protected:T Set = GetTestSet();private:static T GetTestSet() {std::remove_const_t<T> Set;Set.insert(0);Set.insert(1);Set.insert(2);return Set;}};// Register these types for testing.typedef ::testing::Types<DenseSet<unsigned, TestDenseSetInfo>,const DenseSet<unsigned, TestDenseSetInfo>,SmallDenseSet<unsigned, 1, TestDenseSetInfo>,SmallDenseSet<unsigned, 4, TestDenseSetInfo>,const SmallDenseSet<unsigned, 4, TestDenseSetInfo>,SmallDenseSet<unsigned, 64, TestDenseSetInfo>>DenseSetTestTypes;TYPED_TEST_SUITE(DenseSetTest, DenseSetTestTypes, );TYPED_TEST(DenseSetTest, Constructor) {constexpr unsigned a[] = {1, 2, 4};TypeParam set(std::begin(a), std::end(a));EXPECT_EQ(3u, set.size());EXPECT_EQ(1u, set.count(1));EXPECT_EQ(1u, set.count(2));EXPECT_EQ(1u, set.count(4));}TYPED_TEST(DenseSetTest, InitializerList) {TypeParam set({1, 2, 1, 4});EXPECT_EQ(3u, set.size());EXPECT_EQ(1u, set.count(1));EXPECT_EQ(1u, set.count(2));EXPECT_EQ(1u, set.count(4));EXPECT_EQ(0u, set.count(3));}TYPED_TEST(DenseSetTest, InitializerListWithNonPowerOfTwoLength) {TypeParam set({1, 2, 3});EXPECT_EQ(3u, set.size());EXPECT_EQ(1u, set.count(1));EXPECT_EQ(1u, set.count(2));EXPECT_EQ(1u, set.count(3));}TYPED_TEST(DenseSetTest, ConstIteratorComparison) {TypeParam set({1});const TypeParam &cset = set;EXPECT_EQ(set.begin(), cset.begin());EXPECT_EQ(set.end(), cset.end());EXPECT_NE(set.end(), cset.begin());EXPECT_NE(set.begin(), cset.end());}TYPED_TEST(DenseSetTest, DefaultConstruction) {typename TypeParam::iterator I, J;typename TypeParam::const_iterator CI, CJ;EXPECT_EQ(I, J);EXPECT_EQ(CI, CJ);}TYPED_TEST(DenseSetTest, EmptyInitializerList) {TypeParam set({});EXPECT_EQ(0u, set.size());EXPECT_EQ(0u, set.count(0));}TYPED_TEST(DenseSetTest, FindAsTest) {auto &set = this->Set;// Size testsEXPECT_EQ(3u, set.size());// Normal lookup testsEXPECT_EQ(1u, set.count(1));EXPECT_EQ(0u, *set.find(0));EXPECT_EQ(1u, *set.find(1));EXPECT_EQ(2u, *set.find(2));EXPECT_TRUE(set.find(3) == set.end());// find_as() testsEXPECT_EQ(0u, *set.find_as("a"));EXPECT_EQ(1u, *set.find_as("b"));EXPECT_EQ(2u, *set.find_as("c"));EXPECT_TRUE(set.find_as("d") == set.end());}TYPED_TEST(DenseSetTest, EqualityComparisonTest) {TypeParam set1({1, 2, 3, 4});TypeParam set2({4, 3, 2, 1});TypeParam set3({2, 3, 4, 5});EXPECT_EQ(set1, set2);EXPECT_NE(set1, set3);}// Simple class that counts how many moves and copy happens when growing a mapstruct CountCopyAndMove {static int Move;static int Copy;int Value;CountCopyAndMove(int Value) : Value(Value) {}CountCopyAndMove(const CountCopyAndMove &RHS) {Value = RHS.Value;Copy++;}CountCopyAndMove &operator=(const CountCopyAndMove &RHS) {Value = RHS.Value;Copy++;return *this;}CountCopyAndMove(CountCopyAndMove &&RHS) {Value = RHS.Value;Move++;}CountCopyAndMove &operator=(const CountCopyAndMove &&RHS) {Value = RHS.Value;Move++;return *this;}};int CountCopyAndMove::Copy = 0;int CountCopyAndMove::Move = 0;} // anonymous namespacenamespace llvm {// Specialization required to insert a CountCopyAndMove into a DenseSet.template <> struct DenseMapInfo<CountCopyAndMove> {static inline CountCopyAndMove getEmptyKey() { return CountCopyAndMove(-1); };static inline CountCopyAndMove getTombstoneKey() {return CountCopyAndMove(-2);};static unsigned getHashValue(const CountCopyAndMove &Val) {return Val.Value;}static bool isEqual(const CountCopyAndMove &LHS,const CountCopyAndMove &RHS) {return LHS.Value == RHS.Value;}};}namespace {// Make sure reserve actually gives us enough buckets to insert N items// without increasing allocation size.TEST(DenseSetCustomTest, ReserveTest) {// Test a few different size, 48 is *not* a random choice: we need a value// that is 2/3 of a power of two to stress the grow() condition, and the power// of two has to be at least 64 because of minimum size allocation in the// DenseMa. 66 is a value just above the 64 default init.for (auto Size : {1, 2, 48, 66}) {DenseSet<CountCopyAndMove> Set;Set.reserve(Size);unsigned MemorySize = Set.getMemorySize();CountCopyAndMove::Copy = 0;CountCopyAndMove::Move = 0;for (int i = 0; i < Size; ++i)Set.insert(CountCopyAndMove(i));// Check that we didn't growEXPECT_EQ(MemorySize, Set.getMemorySize());// Check that move was called the expected number of timesEXPECT_EQ(Size, CountCopyAndMove::Move);// Check that no copy occurredEXPECT_EQ(0, CountCopyAndMove::Copy);}}TEST(DenseSetCustomTest, ConstTest) {// Test that const pointers work okay for count and find, even when the// underlying map is a non-const pointer.DenseSet<int *> Map;int A;int *B = &A;const int *C = &A;Map.insert(B);EXPECT_EQ(Map.count(B), 1u);EXPECT_EQ(Map.count(C), 1u);EXPECT_TRUE(Map.contains(B));EXPECT_TRUE(Map.contains(C));}}
//===- llvm/unittest/ADT/DenseMapMap.cpp - DenseMap unit tests --*- C++ -*-===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/DenseMap.h"#include "gtest/gtest.h"#include <map>#include <set>using namespace llvm;namespace {uint32_t getTestKey(int i, uint32_t *) { return i; }uint32_t getTestValue(int i, uint32_t *) { return 42 + i; }uint32_t *getTestKey(int i, uint32_t **) {static uint32_t dummy_arr1[8192];assert(i < 8192 && "Only support 8192 dummy keys.");return &dummy_arr1[i];}uint32_t *getTestValue(int i, uint32_t **) {static uint32_t dummy_arr1[8192];assert(i < 8192 && "Only support 8192 dummy keys.");return &dummy_arr1[i];}/// A test class that tries to check that construction and destruction/// occur correctly.class CtorTester {static std::set<CtorTester *> Constructed;int Value;public:explicit CtorTester(int Value = 0) : Value(Value) {EXPECT_TRUE(Constructed.insert(this).second);}CtorTester(uint32_t Value) : Value(Value) {EXPECT_TRUE(Constructed.insert(this).second);}CtorTester(const CtorTester &Arg) : Value(Arg.Value) {EXPECT_TRUE(Constructed.insert(this).second);}CtorTester &operator=(const CtorTester &) = default;~CtorTester() {EXPECT_EQ(1u, Constructed.erase(this));}operator uint32_t() const { return Value; }int getValue() const { return Value; }bool operator==(const CtorTester &RHS) const { return Value == RHS.Value; }};std::set<CtorTester *> CtorTester::Constructed;struct CtorTesterMapInfo {static inline CtorTester getEmptyKey() { return CtorTester(-1); }static inline CtorTester getTombstoneKey() { return CtorTester(-2); }static unsigned getHashValue(const CtorTester &Val) {return Val.getValue() * 37u;}static bool isEqual(const CtorTester &LHS, const CtorTester &RHS) {return LHS == RHS;}};CtorTester getTestKey(int i, CtorTester *) { return CtorTester(i); }CtorTester getTestValue(int i, CtorTester *) { return CtorTester(42 + i); }// Test fixture, with helper functions implemented by forwarding to global// function overloads selected by component types of the type parameter. This// allows all of the map implementations to be tested with shared// implementations of helper routines.template <typename T>class DenseMapTest : public ::testing::Test {protected:T Map;static typename T::key_type *const dummy_key_ptr;static typename T::mapped_type *const dummy_value_ptr;typename T::key_type getKey(int i = 0) {return getTestKey(i, dummy_key_ptr);}typename T::mapped_type getValue(int i = 0) {return getTestValue(i, dummy_value_ptr);}};template <typename T>typename T::key_type *const DenseMapTest<T>::dummy_key_ptr = nullptr;template <typename T>typename T::mapped_type *const DenseMapTest<T>::dummy_value_ptr = nullptr;// Register these types for testing.typedef ::testing::Types<DenseMap<uint32_t, uint32_t>,DenseMap<uint32_t *, uint32_t *>,DenseMap<CtorTester, CtorTester, CtorTesterMapInfo>,SmallDenseMap<uint32_t, uint32_t>,SmallDenseMap<uint32_t *, uint32_t *>,SmallDenseMap<CtorTester, CtorTester, 4,CtorTesterMapInfo>> DenseMapTestTypes;TYPED_TEST_SUITE(DenseMapTest, DenseMapTestTypes, );// Empty map testsTYPED_TEST(DenseMapTest, EmptyIntMapTest) {// Size testsEXPECT_EQ(0u, this->Map.size());EXPECT_TRUE(this->Map.empty());// Iterator testsEXPECT_TRUE(this->Map.begin() == this->Map.end());// Lookup testsEXPECT_FALSE(this->Map.count(this->getKey()));EXPECT_TRUE(this->Map.find(this->getKey()) == this->Map.end());EXPECT_EQ(typename TypeParam::mapped_type(),this->Map.lookup(this->getKey()));}// Constant map testsTYPED_TEST(DenseMapTest, ConstEmptyMapTest) {const TypeParam &ConstMap = this->Map;EXPECT_EQ(0u, ConstMap.size());EXPECT_TRUE(ConstMap.empty());EXPECT_TRUE(ConstMap.begin() == ConstMap.end());}// A map with a single entryTYPED_TEST(DenseMapTest, SingleEntryMapTest) {this->Map[this->getKey()] = this->getValue();// Size testsEXPECT_EQ(1u, this->Map.size());EXPECT_FALSE(this->Map.begin() == this->Map.end());EXPECT_FALSE(this->Map.empty());// Iterator teststypename TypeParam::iterator it = this->Map.begin();EXPECT_EQ(this->getKey(), it->first);EXPECT_EQ(this->getValue(), it->second);++it;EXPECT_TRUE(it == this->Map.end());// Lookup testsEXPECT_TRUE(this->Map.count(this->getKey()));EXPECT_TRUE(this->Map.find(this->getKey()) == this->Map.begin());EXPECT_EQ(this->getValue(), this->Map.lookup(this->getKey()));EXPECT_EQ(this->getValue(), this->Map[this->getKey()]);}// Test clear() methodTYPED_TEST(DenseMapTest, ClearTest) {this->Map[this->getKey()] = this->getValue();this->Map.clear();EXPECT_EQ(0u, this->Map.size());EXPECT_TRUE(this->Map.empty());EXPECT_TRUE(this->Map.begin() == this->Map.end());}// Test erase(iterator) methodTYPED_TEST(DenseMapTest, EraseTest) {this->Map[this->getKey()] = this->getValue();this->Map.erase(this->Map.begin());EXPECT_EQ(0u, this->Map.size());EXPECT_TRUE(this->Map.empty());EXPECT_TRUE(this->Map.begin() == this->Map.end());}// Test erase(value) methodTYPED_TEST(DenseMapTest, EraseTest2) {this->Map[this->getKey()] = this->getValue();this->Map.erase(this->getKey());EXPECT_EQ(0u, this->Map.size());EXPECT_TRUE(this->Map.empty());EXPECT_TRUE(this->Map.begin() == this->Map.end());}// Test insert() methodTYPED_TEST(DenseMapTest, InsertTest) {this->Map.insert(std::make_pair(this->getKey(), this->getValue()));EXPECT_EQ(1u, this->Map.size());EXPECT_EQ(this->getValue(), this->Map[this->getKey()]);}// Test copy constructor methodTYPED_TEST(DenseMapTest, CopyConstructorTest) {this->Map[this->getKey()] = this->getValue();TypeParam copyMap(this->Map);EXPECT_EQ(1u, copyMap.size());EXPECT_EQ(this->getValue(), copyMap[this->getKey()]);}// Test copy constructor method where SmallDenseMap isn't small.TYPED_TEST(DenseMapTest, CopyConstructorNotSmallTest) {for (int Key = 0; Key < 5; ++Key)this->Map[this->getKey(Key)] = this->getValue(Key);TypeParam copyMap(this->Map);EXPECT_EQ(5u, copyMap.size());for (int Key = 0; Key < 5; ++Key)EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);}// Test copying from a default-constructed map.TYPED_TEST(DenseMapTest, CopyConstructorFromDefaultTest) {TypeParam copyMap(this->Map);EXPECT_TRUE(copyMap.empty());}// Test copying from an empty map where SmallDenseMap isn't small.TYPED_TEST(DenseMapTest, CopyConstructorFromEmptyTest) {for (int Key = 0; Key < 5; ++Key)this->Map[this->getKey(Key)] = this->getValue(Key);this->Map.clear();TypeParam copyMap(this->Map);EXPECT_TRUE(copyMap.empty());}// Test assignment operator methodTYPED_TEST(DenseMapTest, AssignmentTest) {this->Map[this->getKey()] = this->getValue();TypeParam copyMap = this->Map;EXPECT_EQ(1u, copyMap.size());EXPECT_EQ(this->getValue(), copyMap[this->getKey()]);// test self-assignment.copyMap = static_cast<TypeParam &>(copyMap);EXPECT_EQ(1u, copyMap.size());EXPECT_EQ(this->getValue(), copyMap[this->getKey()]);}TYPED_TEST(DenseMapTest, AssignmentTestNotSmall) {for (int Key = 0; Key < 5; ++Key)this->Map[this->getKey(Key)] = this->getValue(Key);TypeParam copyMap = this->Map;EXPECT_EQ(5u, copyMap.size());for (int Key = 0; Key < 5; ++Key)EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);// test self-assignment.copyMap = static_cast<TypeParam &>(copyMap);EXPECT_EQ(5u, copyMap.size());for (int Key = 0; Key < 5; ++Key)EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);}// Test swap methodTYPED_TEST(DenseMapTest, SwapTest) {this->Map[this->getKey()] = this->getValue();TypeParam otherMap;this->Map.swap(otherMap);EXPECT_EQ(0u, this->Map.size());EXPECT_TRUE(this->Map.empty());EXPECT_EQ(1u, otherMap.size());EXPECT_EQ(this->getValue(), otherMap[this->getKey()]);this->Map.swap(otherMap);EXPECT_EQ(0u, otherMap.size());EXPECT_TRUE(otherMap.empty());EXPECT_EQ(1u, this->Map.size());EXPECT_EQ(this->getValue(), this->Map[this->getKey()]);// Make this more interesting by inserting 100 numbers into the map.for (int i = 0; i < 100; ++i)this->Map[this->getKey(i)] = this->getValue(i);this->Map.swap(otherMap);EXPECT_EQ(0u, this->Map.size());EXPECT_TRUE(this->Map.empty());EXPECT_EQ(100u, otherMap.size());for (int i = 0; i < 100; ++i)EXPECT_EQ(this->getValue(i), otherMap[this->getKey(i)]);this->Map.swap(otherMap);EXPECT_EQ(0u, otherMap.size());EXPECT_TRUE(otherMap.empty());EXPECT_EQ(100u, this->Map.size());for (int i = 0; i < 100; ++i)EXPECT_EQ(this->getValue(i), this->Map[this->getKey(i)]);}// A more complex iteration testTYPED_TEST(DenseMapTest, IterationTest) {bool visited[100];std::map<typename TypeParam::key_type, unsigned> visitedIndex;// Insert 100 numbers into the mapfor (int i = 0; i < 100; ++i) {visited[i] = false;visitedIndex[this->getKey(i)] = i;this->Map[this->getKey(i)] = this->getValue(i);}// Iterate over all numbers and mark each one found.for (typename TypeParam::iterator it = this->Map.begin();it != this->Map.end(); ++it)visited[visitedIndex[it->first]] = true;// Ensure every number was visited.for (int i = 0; i < 100; ++i)ASSERT_TRUE(visited[i]) << "Entry #" << i << " was never visited";}// const_iterator testTYPED_TEST(DenseMapTest, ConstIteratorTest) {// Check conversion from iterator to const_iterator.typename TypeParam::iterator it = this->Map.begin();typename TypeParam::const_iterator cit(it);EXPECT_TRUE(it == cit);// Check copying of const_iterators.typename TypeParam::const_iterator cit2(cit);EXPECT_TRUE(cit == cit2);}namespace {// Simple class that counts how many moves and copy happens when growing a mapstruct CountCopyAndMove {static int Move;static int Copy;CountCopyAndMove() {}CountCopyAndMove(const CountCopyAndMove &) { Copy++; }CountCopyAndMove &operator=(const CountCopyAndMove &) {Copy++;return *this;}CountCopyAndMove(CountCopyAndMove &&) { Move++; }CountCopyAndMove &operator=(const CountCopyAndMove &&) {Move++;return *this;}};int CountCopyAndMove::Copy = 0;int CountCopyAndMove::Move = 0;} // anonymous namespace// Test initializer list construction.TEST(DenseMapCustomTest, InitializerList) {DenseMap<int, int> M({{0, 0}, {0, 1}, {1, 2}});EXPECT_EQ(2u, M.size());EXPECT_EQ(1u, M.count(0));EXPECT_EQ(0, M[0]);EXPECT_EQ(1u, M.count(1));EXPECT_EQ(2, M[1]);}// Test initializer list construction.TEST(DenseMapCustomTest, EqualityComparison) {DenseMap<int, int> M1({{0, 0}, {1, 2}});DenseMap<int, int> M2({{0, 0}, {1, 2}});DenseMap<int, int> M3({{0, 0}, {1, 3}});EXPECT_EQ(M1, M2);EXPECT_NE(M1, M3);}// Test for the default minimum size of a DenseMapTEST(DenseMapCustomTest, DefaultMinReservedSizeTest) {// IF THIS VALUE CHANGE, please update InitialSizeTest, InitFromIterator, and// ReserveTest as well!const int ExpectedInitialBucketCount = 64;// Formula from DenseMap::getMinBucketToReserveForEntries()const int ExpectedMaxInitialEntries = ExpectedInitialBucketCount * 3 / 4 - 1;DenseMap<int, CountCopyAndMove> Map;// Will allocate 64 bucketsMap.reserve(1);unsigned MemorySize = Map.getMemorySize();CountCopyAndMove::Copy = 0;CountCopyAndMove::Move = 0;for (int i = 0; i < ExpectedMaxInitialEntries; ++i)Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,std::forward_as_tuple(i),std::forward_as_tuple()));// Check that we didn't growEXPECT_EQ(MemorySize, Map.getMemorySize());// Check that move was called the expected number of timesEXPECT_EQ(ExpectedMaxInitialEntries, CountCopyAndMove::Move);// Check that no copy occurredEXPECT_EQ(0, CountCopyAndMove::Copy);// Adding one extra element should grow the mapMap.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,std::forward_as_tuple(ExpectedMaxInitialEntries),std::forward_as_tuple()));// Check that we grewEXPECT_NE(MemorySize, Map.getMemorySize());// Check that move was called the expected number of times// This relies on move-construction elision, and cannot be reliably tested.// EXPECT_EQ(ExpectedMaxInitialEntries + 2, CountCopyAndMove::Move);// Check that no copy occurredEXPECT_EQ(0, CountCopyAndMove::Copy);}// Make sure creating the map with an initial size of N actually gives us enough// buckets to insert N items without increasing allocation size.TEST(DenseMapCustomTest, InitialSizeTest) {// Test a few different sizes, 48 is *not* a random choice: we need a value// that is 2/3 of a power of two to stress the grow() condition, and the power// of two has to be at least 64 because of minimum size allocation in the// DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the// 64 default init.for (auto Size : {1, 2, 48, 66}) {DenseMap<int, CountCopyAndMove> Map(Size);unsigned MemorySize = Map.getMemorySize();CountCopyAndMove::Copy = 0;CountCopyAndMove::Move = 0;for (int i = 0; i < Size; ++i)Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,std::forward_as_tuple(i),std::forward_as_tuple()));// Check that we didn't growEXPECT_EQ(MemorySize, Map.getMemorySize());// Check that move was called the expected number of timesEXPECT_EQ(Size, CountCopyAndMove::Move);// Check that no copy occurredEXPECT_EQ(0, CountCopyAndMove::Copy);}}// Make sure creating the map with a iterator range does not trigger grow()TEST(DenseMapCustomTest, InitFromIterator) {std::vector<std::pair<int, CountCopyAndMove>> Values;// The size is a random value greater than 64 (hardcoded DenseMap min init)const int Count = 65;for (int i = 0; i < Count; i++)Values.emplace_back(i, CountCopyAndMove());CountCopyAndMove::Move = 0;CountCopyAndMove::Copy = 0;DenseMap<int, CountCopyAndMove> Map(Values.begin(), Values.end());// Check that no move occurredEXPECT_EQ(0, CountCopyAndMove::Move);// Check that copy was called the expected number of timesEXPECT_EQ(Count, CountCopyAndMove::Copy);}// Make sure reserve actually gives us enough buckets to insert N items// without increasing allocation size.TEST(DenseMapCustomTest, ReserveTest) {// Test a few different size, 48 is *not* a random choice: we need a value// that is 2/3 of a power of two to stress the grow() condition, and the power// of two has to be at least 64 because of minimum size allocation in the// DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the// 64 default init.for (auto Size : {1, 2, 48, 66}) {DenseMap<int, CountCopyAndMove> Map;Map.reserve(Size);unsigned MemorySize = Map.getMemorySize();CountCopyAndMove::Copy = 0;CountCopyAndMove::Move = 0;for (int i = 0; i < Size; ++i)Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,std::forward_as_tuple(i),std::forward_as_tuple()));// Check that we didn't growEXPECT_EQ(MemorySize, Map.getMemorySize());// Check that move was called the expected number of timesEXPECT_EQ(Size, CountCopyAndMove::Move);// Check that no copy occurredEXPECT_EQ(0, CountCopyAndMove::Copy);}}// Make sure DenseMap works with StringRef keys.TEST(DenseMapCustomTest, StringRefTest) {DenseMap<StringRef, int> M;M["a"] = 1;M["b"] = 2;M["c"] = 3;EXPECT_EQ(3u, M.size());EXPECT_EQ(1, M.lookup("a"));EXPECT_EQ(2, M.lookup("b"));EXPECT_EQ(3, M.lookup("c"));EXPECT_EQ(0, M.lookup("q"));// Test the empty string, spelled various ways.EXPECT_EQ(0, M.lookup(""));EXPECT_EQ(0, M.lookup(StringRef()));EXPECT_EQ(0, M.lookup(StringRef("a", 0)));M[""] = 42;EXPECT_EQ(42, M.lookup(""));EXPECT_EQ(42, M.lookup(StringRef()));EXPECT_EQ(42, M.lookup(StringRef("a", 0)));}// Key traits that allows lookup with either an unsigned or char* key;// In the latter case, "a" == 0, "b" == 1 and so on.struct TestDenseMapInfo {static inline unsigned getEmptyKey() { return ~0; }static inline unsigned getTombstoneKey() { return ~0U - 1; }static unsigned getHashValue(const unsigned& Val) { return Val * 37U; }static unsigned getHashValue(const char* Val) {return (unsigned)(Val[0] - 'a') * 37U;}static bool isEqual(const unsigned& LHS, const unsigned& RHS) {return LHS == RHS;}static bool isEqual(const char* LHS, const unsigned& RHS) {return (unsigned)(LHS[0] - 'a') == RHS;}};// find_as() testsTEST(DenseMapCustomTest, FindAsTest) {DenseMap<unsigned, unsigned, TestDenseMapInfo> map;map[0] = 1;map[1] = 2;map[2] = 3;// Size testsEXPECT_EQ(3u, map.size());// Normal lookup testsEXPECT_EQ(1u, map.count(1));EXPECT_EQ(1u, map.find(0)->second);EXPECT_EQ(2u, map.find(1)->second);EXPECT_EQ(3u, map.find(2)->second);EXPECT_TRUE(map.find(3) == map.end());// find_as() testsEXPECT_EQ(1u, map.find_as("a")->second);EXPECT_EQ(2u, map.find_as("b")->second);EXPECT_EQ(3u, map.find_as("c")->second);EXPECT_TRUE(map.find_as("d") == map.end());}TEST(DenseMapCustomTest, SmallDenseMapInitializerList) {SmallDenseMap<int, int> M = {{0, 0}, {0, 1}, {1, 2}};EXPECT_EQ(2u, M.size());EXPECT_EQ(1u, M.count(0));EXPECT_EQ(0, M[0]);EXPECT_EQ(1u, M.count(1));EXPECT_EQ(2, M[1]);}struct ContiguousDenseMapInfo {static inline unsigned getEmptyKey() { return ~0; }static inline unsigned getTombstoneKey() { return ~0U - 1; }static unsigned getHashValue(const unsigned& Val) { return Val; }static bool isEqual(const unsigned& LHS, const unsigned& RHS) {return LHS == RHS;}};// Test that filling a small dense map with exactly the number of elements in// the map grows to have enough space for an empty bucket.TEST(DenseMapCustomTest, SmallDenseMapGrowTest) {SmallDenseMap<unsigned, unsigned, 32, ContiguousDenseMapInfo> map;// Add some number of elements, then delete a few to leave us some tombstones.// If we just filled the map with 32 elements we'd grow because of not enough// tombstones which masks the issue here.for (unsigned i = 0; i < 20; ++i)map[i] = i + 1;for (unsigned i = 0; i < 10; ++i)map.erase(i);for (unsigned i = 20; i < 32; ++i)map[i] = i + 1;// Size testsEXPECT_EQ(22u, map.size());// Try to find an element which doesn't exist. There was a bug in// SmallDenseMap which led to a map with num elements == small capacity not// having an empty bucket any more. Finding an element not in the map would// therefore never terminate.EXPECT_TRUE(map.find(32) == map.end());}TEST(DenseMapCustomTest, LargeSmallDenseMapCompaction) {SmallDenseMap<unsigned, unsigned, 128, ContiguousDenseMapInfo> map;// Fill to < 3/4 load.for (unsigned i = 0; i < 95; ++i)map[i] = i;// And erase, leaving behind tombstones.for (unsigned i = 0; i < 95; ++i)map.erase(i);// Fill further, so that less than 1/8 are empty, but still below 3/4 load.for (unsigned i = 95; i < 128; ++i)map[i] = i;EXPECT_EQ(33u, map.size());// Similar to the previous test, check for a non-existing element, as an// indirect check that tombstones have been removed.EXPECT_TRUE(map.find(0) == map.end());}TEST(DenseMapCustomTest, SmallDenseMapWithNumBucketsNonPowerOf2) {// Is not power of 2.const unsigned NumInitBuckets = 33;// Power of 2 less then NumInitBuckets.constexpr unsigned InlineBuckets = 4;// Constructor should not trigger assert.SmallDenseMap<int, int, InlineBuckets> map(NumInitBuckets);}TEST(DenseMapCustomTest, TryEmplaceTest) {DenseMap<int, std::unique_ptr<int>> Map;std::unique_ptr<int> P(new int(2));auto Try1 = Map.try_emplace(0, new int(1));EXPECT_TRUE(Try1.second);auto Try2 = Map.try_emplace(0, std::move(P));EXPECT_FALSE(Try2.second);EXPECT_EQ(Try1.first, Try2.first);EXPECT_NE(nullptr, P);}TEST(DenseMapCustomTest, ConstTest) {// Test that const pointers work okay for count and find, even when the// underlying map is a non-const pointer.DenseMap<int *, int> Map;int A;int *B = &A;const int *C = &A;Map.insert({B, 0});EXPECT_EQ(Map.count(B), 1u);EXPECT_EQ(Map.count(C), 1u);EXPECT_NE(Map.find(B), Map.end());EXPECT_NE(Map.find(C), Map.end());}struct IncompleteStruct;TEST(DenseMapCustomTest, OpaquePointerKey) {// Test that we can use a pointer to an incomplete type as a DenseMap key.// This is an important build time optimization, since many classes have// DenseMap members.DenseMap<IncompleteStruct *, int> Map;int Keys[3] = {0, 0, 0};IncompleteStruct *K1 = reinterpret_cast<IncompleteStruct *>(&Keys[0]);IncompleteStruct *K2 = reinterpret_cast<IncompleteStruct *>(&Keys[1]);IncompleteStruct *K3 = reinterpret_cast<IncompleteStruct *>(&Keys[2]);Map.insert({K1, 1});Map.insert({K2, 2});Map.insert({K3, 3});EXPECT_EQ(Map.count(K1), 1u);EXPECT_EQ(Map[K1], 1);EXPECT_EQ(Map[K2], 2);EXPECT_EQ(Map[K3], 3);Map.clear();EXPECT_EQ(Map.find(K1), Map.end());EXPECT_EQ(Map.find(K2), Map.end());EXPECT_EQ(Map.find(K3), Map.end());}} // namespacenamespace {struct A {A(int value) : value(value) {}int value;};struct B : public A {using A::A;};} // namespacenamespace llvm {template <typename T>struct DenseMapInfo<T, std::enable_if_t<std::is_base_of<A, T>::value>> {static inline T getEmptyKey() { return {static_cast<int>(~0)}; }static inline T getTombstoneKey() { return {static_cast<int>(~0U - 1)}; }static unsigned getHashValue(const T &Val) { return Val.value; }static bool isEqual(const T &LHS, const T &RHS) {return LHS.value == RHS.value;}};} // namespace llvmnamespace {TEST(DenseMapCustomTest, SFINAEMapInfo) {// Test that we can use a pointer to an incomplete type as a DenseMap key.// This is an important build time optimization, since many classes have// DenseMap members.DenseMap<B, int> Map;B Keys[3] = {{0}, {1}, {2}};Map.insert({Keys[0], 1});Map.insert({Keys[1], 2});Map.insert({Keys[2], 3});EXPECT_EQ(Map.count(Keys[0]), 1u);EXPECT_EQ(Map[Keys[0]], 1);EXPECT_EQ(Map[Keys[1]], 2);EXPECT_EQ(Map[Keys[2]], 3);Map.clear();EXPECT_EQ(Map.find(Keys[0]), Map.end());EXPECT_EQ(Map.find(Keys[1]), Map.end());EXPECT_EQ(Map.find(Keys[2]), Map.end());}} // namespace
//===- llvm/unittest/ADT/DeltaAlgorithmTest.cpp ---------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/DeltaAlgorithm.h"#include "gtest/gtest.h"#include <algorithm>#include <cstdarg>using namespace llvm;namespace std {std::ostream &operator<<(std::ostream &OS,const std::set<unsigned> &S) {OS << "{";for (std::set<unsigned>::const_iterator it = S.begin(),ie = S.end(); it != ie; ++it) {if (it != S.begin())OS << ",";OS << *it;}OS << "}";return OS;}}namespace {class FixedDeltaAlgorithm final : public DeltaAlgorithm {changeset_ty FailingSet;unsigned NumTests;protected:bool ExecuteOneTest(const changeset_ty &Changes) override {++NumTests;return std::includes(Changes.begin(), Changes.end(),FailingSet.begin(), FailingSet.end());}public:FixedDeltaAlgorithm(const changeset_ty &_FailingSet): FailingSet(_FailingSet),NumTests(0) {}unsigned getNumTests() const { return NumTests; }};std::set<unsigned> fixed_set(unsigned N, ...) {std::set<unsigned> S;va_list ap;va_start(ap, N);for (unsigned i = 0; i != N; ++i)S.insert(va_arg(ap, unsigned));va_end(ap);return S;}std::set<unsigned> range(unsigned Start, unsigned End) {std::set<unsigned> S;while (Start != End)S.insert(Start++);return S;}std::set<unsigned> range(unsigned N) {return range(0, N);}TEST(DeltaAlgorithmTest, Basic) {// P = {3,5,7} \in S// [0, 20) should minimize to {3,5,7} in a reasonable number of tests.std::set<unsigned> Fails = fixed_set(3, 3, 5, 7);FixedDeltaAlgorithm FDA(Fails);EXPECT_EQ(fixed_set(3, 3, 5, 7), FDA.Run(range(20)));EXPECT_GE(33U, FDA.getNumTests());// P = {3,5,7} \in S// [10, 20) should minimize to [10,20)EXPECT_EQ(range(10,20), FDA.Run(range(10,20)));// P = [0,4) \in S// [0, 4) should minimize to [0,4) in 11 tests.//// 11 = |{ {},// {0}, {1}, {2}, {3},// {1, 2, 3}, {0, 2, 3}, {0, 1, 3}, {0, 1, 2},// {0, 1}, {2, 3} }|FDA = FixedDeltaAlgorithm(range(10));EXPECT_EQ(range(4), FDA.Run(range(4)));EXPECT_EQ(11U, FDA.getNumTests());}}
//===- llvm/unittest/ADT/DAGDeltaAlgorithmTest.cpp ------------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/DAGDeltaAlgorithm.h"#include "gtest/gtest.h"#include <algorithm>#include <cstdarg>using namespace llvm;namespace {typedef DAGDeltaAlgorithm::edge_ty edge_ty;class FixedDAGDeltaAlgorithm : public DAGDeltaAlgorithm {changeset_ty FailingSet;unsigned NumTests;protected:bool ExecuteOneTest(const changeset_ty &Changes) override {++NumTests;return std::includes(Changes.begin(), Changes.end(),FailingSet.begin(), FailingSet.end());}public:FixedDAGDeltaAlgorithm(const changeset_ty &_FailingSet): FailingSet(_FailingSet),NumTests(0) {}unsigned getNumTests() const { return NumTests; }};std::set<unsigned> fixed_set(unsigned N, ...) {std::set<unsigned> S;va_list ap;va_start(ap, N);for (unsigned i = 0; i != N; ++i)S.insert(va_arg(ap, unsigned));va_end(ap);return S;}std::set<unsigned> range(unsigned Start, unsigned End) {std::set<unsigned> S;while (Start != End)S.insert(Start++);return S;}std::set<unsigned> range(unsigned N) {return range(0, N);}TEST(DAGDeltaAlgorithmTest, Basic) {std::vector<edge_ty> Deps;// Dependencies:// 1 - 3Deps.clear();Deps.push_back(std::make_pair(3, 1));// P = {3,5,7} \in S,// [0, 20),// should minimize to {1,3,5,7} in a reasonable number of tests.FixedDAGDeltaAlgorithm FDA(fixed_set(3, 3, 5, 7));EXPECT_EQ(fixed_set(4, 1, 3, 5, 7), FDA.Run(range(20), Deps));EXPECT_GE(46U, FDA.getNumTests());// Dependencies:// 0 - 1// \- 2 - 3// \- 4Deps.clear();Deps.push_back(std::make_pair(1, 0));Deps.push_back(std::make_pair(2, 0));Deps.push_back(std::make_pair(4, 0));Deps.push_back(std::make_pair(3, 2));// This is a case where we must hold required changes.//// P = {1,3} \in S,// [0, 5),// should minimize to {0,1,2,3} in a small number of tests.FixedDAGDeltaAlgorithm FDA2(fixed_set(2, 1, 3));EXPECT_EQ(fixed_set(4, 0, 1, 2, 3), FDA2.Run(range(5), Deps));EXPECT_GE(9U, FDA2.getNumTests());// This is a case where we should quickly prune part of the tree.//// P = {4} \in S,// [0, 5),// should minimize to {0,4} in a small number of tests.FixedDAGDeltaAlgorithm FDA3(fixed_set(1, 4));EXPECT_EQ(fixed_set(2, 0, 4), FDA3.Run(range(5), Deps));EXPECT_GE(6U, FDA3.getNumTests());}}
//===- llvm/unittest/ADT/CombinationGeneratorTest.cpp ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/CombinationGenerator.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/STLExtras.h"#include "llvm/ADT/STLForwardCompat.h"#include "llvm/ADT/iterator_range.h"#include "llvm/Support/ErrorHandling.h"#include "gmock/gmock.h"#include "gtest/gtest.h"#include <algorithm>#include <cstddef>#include <iterator>#include <tuple>#include <vector>using namespace llvm;namespace {TEST(CombinationGenerator, Square) {const std::vector<std::vector<int>> Choices{{0, 1}, {2, 3}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0, 2},{0, 3},{1, 2},{1, 3},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}TEST(CombinationGenerator, MiddleColumn) {const std::vector<std::vector<int>> Choices{{0}, {1, 2}, {3}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0, 1, 3},{0, 2, 3},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}TEST(CombinationGenerator, SideColumns) {const std::vector<std::vector<int>> Choices{{0, 1}, {2}, {3, 4}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0, 2, 3},{0, 2, 4},{1, 2, 3},{1, 2, 4},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}TEST(CombinationGenerator, LeftColumn) {const std::vector<std::vector<int>> Choices{{0, 1}, {2}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0, 2},{1, 2},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}TEST(CombinationGenerator, RightColumn) {const std::vector<std::vector<int>> Choices{{0}, {1, 2}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0, 1},{0, 2},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}TEST(CombinationGenerator, Column) {const std::vector<std::vector<int>> Choices{{0, 1}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0},{1},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}TEST(CombinationGenerator, Row) {const std::vector<std::vector<int>> Choices{{0}, {1}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0, 1},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}TEST(CombinationGenerator, Singleton) {const std::vector<std::vector<int>> Choices{{0}};std::vector<std::vector<int>> Variants;CombinationGenerator<int, std::vector<int>, 4> G(Choices);const size_t NumVariants = G.numCombinations();G.generate([&](ArrayRef<int> State) -> bool {Variants.emplace_back(State);return false; // keep going});const std::vector<std::vector<int>> ExpectedVariants{{0},};ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));}} // end anonymous namespace
//=== CoalescingBitVectorTest.cpp - CoalescingBitVector unit tests --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/CoalescingBitVector.h"#include "gtest/gtest.h"using namespace llvm;namespace {using UBitVec = CoalescingBitVector<unsigned>;using U64BitVec = CoalescingBitVector<uint64_t>;bool elementsMatch(const UBitVec &BV, std::initializer_list<unsigned> List) {if (!std::equal(BV.begin(), BV.end(), List.begin(), List.end())) {UBitVec::Allocator Alloc;UBitVec Expected(Alloc);Expected.set(List);dbgs() << "elementsMatch:\n"<< " Expected: ";Expected.print(dbgs());dbgs() << " Got: ";BV.print(dbgs());return false;}return true;}bool rangesMatch(iterator_range<UBitVec::const_iterator> R,std::initializer_list<unsigned> List) {return std::equal(R.begin(), R.end(), List.begin(), List.end());}TEST(CoalescingBitVectorTest, Set) {UBitVec::Allocator Alloc;UBitVec BV1(Alloc);UBitVec BV2(Alloc);BV1.set(0);EXPECT_TRUE(BV1.test(0));EXPECT_FALSE(BV1.test(1));BV2.set(BV1);EXPECT_TRUE(BV2.test(0));}TEST(CoalescingBitVectorTest, Count) {UBitVec::Allocator Alloc;UBitVec BV(Alloc);EXPECT_EQ(BV.count(), 0u);BV.set(0);EXPECT_EQ(BV.count(), 1u);BV.set({11, 12, 13, 14, 15});EXPECT_EQ(BV.count(), 6u);}TEST(CoalescingBitVectorTest, ClearAndEmpty) {UBitVec::Allocator Alloc;UBitVec BV(Alloc);EXPECT_TRUE(BV.empty());BV.set(1);EXPECT_FALSE(BV.empty());BV.clear();EXPECT_TRUE(BV.empty());}TEST(CoalescingBitVector, Copy) {UBitVec::Allocator Alloc;UBitVec BV1(Alloc);BV1.set(0);UBitVec BV2 = BV1;EXPECT_TRUE(elementsMatch(BV1, {0}));EXPECT_TRUE(elementsMatch(BV2, {0}));BV2.set(5);BV2 = BV1;EXPECT_TRUE(elementsMatch(BV1, {0}));EXPECT_TRUE(elementsMatch(BV2, {0}));}TEST(CoalescingBitVectorTest, Iterators) {UBitVec::Allocator Alloc;UBitVec BV(Alloc);BV.set({0, 1, 2});auto It = BV.begin();EXPECT_TRUE(It == BV.begin());EXPECT_EQ(*It, 0u);++It;EXPECT_EQ(*It, 1u);++It;EXPECT_EQ(*It, 2u);++It;EXPECT_TRUE(It == BV.end());EXPECT_TRUE(BV.end() == BV.end());It = BV.begin();EXPECT_TRUE(It == BV.begin());auto ItCopy = It++;EXPECT_TRUE(ItCopy == BV.begin());EXPECT_EQ(*ItCopy, 0u);EXPECT_EQ(*It, 1u);EXPECT_TRUE(elementsMatch(BV, {0, 1, 2}));BV.set({4, 5, 6});EXPECT_TRUE(elementsMatch(BV, {0, 1, 2, 4, 5, 6}));BV.set(3);EXPECT_TRUE(elementsMatch(BV, {0, 1, 2, 3, 4, 5, 6}));BV.set(10);EXPECT_TRUE(elementsMatch(BV, {0, 1, 2, 3, 4, 5, 6, 10}));// Should be able to reset unset bits.BV.reset(3);BV.reset(3);BV.reset(20000);BV.set({1000, 1001, 1002});EXPECT_TRUE(elementsMatch(BV, {0, 1, 2, 4, 5, 6, 10, 1000, 1001, 1002}));auto It1 = BV.begin();EXPECT_TRUE(It1 == BV.begin());EXPECT_TRUE(++It1 == ++BV.begin());EXPECT_TRUE(It1 != BV.begin());EXPECT_TRUE(It1 != BV.end());}TEST(CoalescingBitVectorTest, Reset) {UBitVec::Allocator Alloc;UBitVec BV(Alloc);BV.set(0);EXPECT_TRUE(BV.test(0));BV.reset(0);EXPECT_FALSE(BV.test(0));BV.clear();BV.set({1, 2, 3});BV.reset(1);EXPECT_TRUE(elementsMatch(BV, {2, 3}));BV.clear();BV.set({1, 2, 3});BV.reset(2);EXPECT_TRUE(elementsMatch(BV, {1, 3}));BV.clear();BV.set({1, 2, 3});BV.reset(3);EXPECT_TRUE(elementsMatch(BV, {1, 2}));}TEST(CoalescingBitVectorTest, Comparison) {UBitVec::Allocator Alloc;UBitVec BV1(Alloc);UBitVec BV2(Alloc);// Single interval.BV1.set({1, 2, 3});BV2.set({1, 2, 3});EXPECT_EQ(BV1, BV2);EXPECT_FALSE(BV1 != BV2);// Different number of intervals.BV1.clear();BV2.clear();BV1.set({1, 2, 3});EXPECT_NE(BV1, BV2);// Multiple intervals.BV1.clear();BV2.clear();BV1.set({1, 2, 11, 12});BV2.set({1, 2, 11, 12});EXPECT_EQ(BV1, BV2);BV2.reset(1);EXPECT_NE(BV1, BV2);BV2.set(1);BV2.reset(11);EXPECT_NE(BV1, BV2);}// A simple implementation of set union, used to double-check the human// "expected" answer.void simpleUnion(UBitVec &Union, const UBitVec &LHS,const UBitVec &RHS) {for (unsigned Bit : LHS)Union.test_and_set(Bit);for (unsigned Bit : RHS)Union.test_and_set(Bit);}TEST(CoalescingBitVectorTest, Union) {UBitVec::Allocator Alloc;// Check that after doing LHS |= RHS, LHS == Expected.auto unionIs = [&](std::initializer_list<unsigned> LHS,std::initializer_list<unsigned> RHS,std::initializer_list<unsigned> Expected) {UBitVec BV1(Alloc);BV1.set(LHS);UBitVec BV2(Alloc);BV2.set(RHS);UBitVec DoubleCheckedExpected(Alloc);simpleUnion(DoubleCheckedExpected, BV1, BV2);ASSERT_TRUE(elementsMatch(DoubleCheckedExpected, Expected));BV1 |= BV2;ASSERT_TRUE(elementsMatch(BV1, Expected));};// Check that "LHS |= RHS" and "RHS |= LHS" both produce the expected result.auto testUnionSymmetrically = [&](std::initializer_list<unsigned> LHS,std::initializer_list<unsigned> RHS,std::initializer_list<unsigned> Expected) {unionIs(LHS, RHS, Expected);unionIs(RHS, LHS, Expected);};// Empty LHS.testUnionSymmetrically({}, {1, 2, 3}, {1, 2, 3});// Empty RHS.testUnionSymmetrically({1, 2, 3}, {}, {1, 2, 3});// Full overlap.testUnionSymmetrically({1}, {1}, {1});testUnionSymmetrically({1, 2, 11, 12}, {1, 2, 11, 12}, {1, 2, 11, 12});// Sliding window: fix {2, 3, 4} as the LHS, and slide a window before/after// it. Repeat this swapping LHS and RHS.testUnionSymmetrically({2, 3, 4}, {1, 2, 3}, {1, 2, 3, 4});testUnionSymmetrically({2, 3, 4}, {2, 3, 4}, {2, 3, 4});testUnionSymmetrically({2, 3, 4}, {3, 4, 5}, {2, 3, 4, 5});testUnionSymmetrically({1, 2, 3}, {2, 3, 4}, {1, 2, 3, 4});testUnionSymmetrically({3, 4, 5}, {2, 3, 4}, {2, 3, 4, 5});// Multiple overlaps, but at least one of the overlaps forces us to split an// interval (and possibly both do). For ease of understanding, fix LHS to be// {1, 2, 11, 12}, but vary RHS.testUnionSymmetrically({1, 2, 11, 12}, {1}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {2}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {11}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {12}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {1, 11}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {1, 12}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {2, 11}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {2, 12}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {1, 2, 11}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {1, 2, 12}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {1, 11, 12}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {2, 11, 12}, {1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {0, 11, 12}, {0, 1, 2, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {3, 11, 12}, {1, 2, 3, 11, 12});testUnionSymmetrically({1, 2, 11, 12}, {1, 11, 13}, {1, 2, 11, 12, 13});testUnionSymmetrically({1, 2, 11, 12}, {1, 10, 11}, {1, 2, 10, 11, 12});// Partial overlap, but the existing interval covers future overlaps.testUnionSymmetrically({1, 2, 3, 4, 5, 6, 7, 8}, {2, 3, 4, 6, 7},{1, 2, 3, 4, 5, 6, 7, 8});testUnionSymmetrically({1, 2, 3, 4, 5, 6, 7, 8}, {2, 3, 7, 8, 9},{1, 2, 3, 4, 5, 6, 7, 8, 9});// More partial overlaps.testUnionSymmetrically({1, 2, 3, 4, 5}, {0, 1, 2, 4, 5, 6},{0, 1, 2, 3, 4, 5, 6});testUnionSymmetrically({2, 3}, {1, 2, 3, 4}, {1, 2, 3, 4});testUnionSymmetrically({3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4});testUnionSymmetrically({1, 2}, {1, 2, 3, 4}, {1, 2, 3, 4});testUnionSymmetrically({0, 1}, {1, 2, 3, 4}, {0, 1, 2, 3, 4});// Merge non-overlapping.testUnionSymmetrically({0, 1}, {2, 3}, {0, 1, 2, 3});testUnionSymmetrically({0, 3}, {1, 2}, {0, 1, 2, 3});}// A simple implementation of set intersection, used to double-check the// human "expected" answer.void simpleIntersection(UBitVec &Intersection, const UBitVec &LHS,const UBitVec &RHS) {for (unsigned Bit : LHS)if (RHS.test(Bit))Intersection.set(Bit);}TEST(CoalescingBitVectorTest, Intersection) {UBitVec::Allocator Alloc;// Check that after doing LHS &= RHS, LHS == Expected.auto intersectionIs = [&](std::initializer_list<unsigned> LHS,std::initializer_list<unsigned> RHS,std::initializer_list<unsigned> Expected) {UBitVec BV1(Alloc);BV1.set(LHS);UBitVec BV2(Alloc);BV2.set(RHS);UBitVec DoubleCheckedExpected(Alloc);simpleIntersection(DoubleCheckedExpected, BV1, BV2);ASSERT_TRUE(elementsMatch(DoubleCheckedExpected, Expected));BV1 &= BV2;ASSERT_TRUE(elementsMatch(BV1, Expected));};// Check that "LHS &= RHS" and "RHS &= LHS" both produce the expected result.auto testIntersectionSymmetrically = [&](std::initializer_list<unsigned> LHS,std::initializer_list<unsigned> RHS,std::initializer_list<unsigned> Expected) {intersectionIs(LHS, RHS, Expected);intersectionIs(RHS, LHS, Expected);};// Empty case, one-element case.testIntersectionSymmetrically({}, {}, {});testIntersectionSymmetrically({1}, {1}, {1});testIntersectionSymmetrically({1}, {2}, {});// Exact overlaps cases: single overlap and multiple overlaps.testIntersectionSymmetrically({1, 2}, {1, 2}, {1, 2});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 2, 11, 12}, {1, 2, 11, 12});// Sliding window: fix {2, 3, 4} as the LHS, and slide a window before/after// it.testIntersectionSymmetrically({2, 3, 4}, {1, 2, 3}, {2, 3});testIntersectionSymmetrically({2, 3, 4}, {2, 3, 4}, {2, 3, 4});testIntersectionSymmetrically({2, 3, 4}, {3, 4, 5}, {3, 4});// No overlap, but we have multiple intervals.testIntersectionSymmetrically({1, 2, 11, 12}, {3, 4, 13, 14}, {});// Multiple overlaps, but at least one of the overlaps forces us to split an// interval (and possibly both do). For ease of understanding, fix LHS to be// {1, 2, 11, 12}, but vary RHS.testIntersectionSymmetrically({1, 2, 11, 12}, {1}, {1});testIntersectionSymmetrically({1, 2, 11, 12}, {2}, {2});testIntersectionSymmetrically({1, 2, 11, 12}, {11}, {11});testIntersectionSymmetrically({1, 2, 11, 12}, {12}, {12});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 11}, {1, 11});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 12}, {1, 12});testIntersectionSymmetrically({1, 2, 11, 12}, {2, 11}, {2, 11});testIntersectionSymmetrically({1, 2, 11, 12}, {2, 12}, {2, 12});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 2, 11}, {1, 2, 11});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 2, 12}, {1, 2, 12});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 11, 12}, {1, 11, 12});testIntersectionSymmetrically({1, 2, 11, 12}, {2, 11, 12}, {2, 11, 12});testIntersectionSymmetrically({1, 2, 11, 12}, {0, 11, 12}, {11, 12});testIntersectionSymmetrically({1, 2, 11, 12}, {3, 11, 12}, {11, 12});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 11, 13}, {1, 11});testIntersectionSymmetrically({1, 2, 11, 12}, {1, 10, 11}, {1, 11});// Partial overlap, but the existing interval covers future overlaps.testIntersectionSymmetrically({1, 2, 3, 4, 5, 6, 7, 8}, {2, 3, 4, 6, 7},{2, 3, 4, 6, 7});}// A simple implementation of set intersection-with-complement, used to// double-check the human "expected" answer.void simpleIntersectionWithComplement(UBitVec &Intersection, const UBitVec &LHS,const UBitVec &RHS) {for (unsigned Bit : LHS)if (!RHS.test(Bit))Intersection.set(Bit);}TEST(CoalescingBitVectorTest, IntersectWithComplement) {UBitVec::Allocator Alloc;// Check that after doing LHS.intersectWithComplement(RHS), LHS == Expected.auto intersectionWithComplementIs =[&](std::initializer_list<unsigned> LHS,std::initializer_list<unsigned> RHS,std::initializer_list<unsigned> Expected) {UBitVec BV1(Alloc);BV1.set(LHS);UBitVec BV2(Alloc);BV2.set(RHS);UBitVec DoubleCheckedExpected(Alloc);simpleIntersectionWithComplement(DoubleCheckedExpected, BV1, BV2);ASSERT_TRUE(elementsMatch(DoubleCheckedExpected, Expected));BV1.intersectWithComplement(BV2);ASSERT_TRUE(elementsMatch(BV1, Expected));};// Empty case, one-element case.intersectionWithComplementIs({}, {}, {});intersectionWithComplementIs({1}, {1}, {});intersectionWithComplementIs({1}, {2}, {1});// Exact overlaps cases: single overlap and multiple overlaps.intersectionWithComplementIs({1, 2}, {1, 2}, {});intersectionWithComplementIs({1, 2, 11, 12}, {1, 2, 11, 12}, {});// Sliding window: fix {2, 3, 4} as the LHS, and slide a window before/after// it. Repeat this swapping LHS and RHS.intersectionWithComplementIs({2, 3, 4}, {1, 2, 3}, {4});intersectionWithComplementIs({2, 3, 4}, {2, 3, 4}, {});intersectionWithComplementIs({2, 3, 4}, {3, 4, 5}, {2});intersectionWithComplementIs({1, 2, 3}, {2, 3, 4}, {1});intersectionWithComplementIs({3, 4, 5}, {2, 3, 4}, {5});// No overlap, but we have multiple intervals.intersectionWithComplementIs({1, 2, 11, 12}, {3, 4, 13, 14}, {1, 2, 11, 12});// Multiple overlaps. For ease of understanding, fix LHS to be// {1, 2, 11, 12}, but vary RHS.intersectionWithComplementIs({1, 2, 11, 12}, {1}, {2, 11, 12});intersectionWithComplementIs({1, 2, 11, 12}, {2}, {1, 11, 12});intersectionWithComplementIs({1, 2, 11, 12}, {11}, {1, 2, 12});intersectionWithComplementIs({1, 2, 11, 12}, {12}, {1, 2, 11});intersectionWithComplementIs({1, 2, 11, 12}, {1, 11}, {2, 12});intersectionWithComplementIs({1, 2, 11, 12}, {1, 12}, {2, 11});intersectionWithComplementIs({1, 2, 11, 12}, {2, 11}, {1, 12});intersectionWithComplementIs({1, 2, 11, 12}, {2, 12}, {1, 11});intersectionWithComplementIs({1, 2, 11, 12}, {1, 2, 11}, {12});intersectionWithComplementIs({1, 2, 11, 12}, {1, 2, 12}, {11});intersectionWithComplementIs({1, 2, 11, 12}, {1, 11, 12}, {2});intersectionWithComplementIs({1, 2, 11, 12}, {2, 11, 12}, {1});intersectionWithComplementIs({1, 2, 11, 12}, {0, 11, 12}, {1, 2});intersectionWithComplementIs({1, 2, 11, 12}, {3, 11, 12}, {1, 2});intersectionWithComplementIs({1, 2, 11, 12}, {1, 11, 13}, {2, 12});intersectionWithComplementIs({1, 2, 11, 12}, {1, 10, 11}, {2, 12});// Partial overlap, but the existing interval covers future overlaps.intersectionWithComplementIs({1, 2, 3, 4, 5, 6, 7, 8}, {2, 3, 4, 6, 7},{1, 5, 8});}TEST(CoalescingBitVectorTest, FindLowerBound) {U64BitVec::Allocator Alloc;U64BitVec BV(Alloc);uint64_t BigNum1 = uint64_t(1) << 32;uint64_t BigNum2 = (uint64_t(1) << 33) + 1;EXPECT_TRUE(BV.find(BigNum1) == BV.end());BV.set(BigNum1);auto Find1 = BV.find(BigNum1);EXPECT_EQ(*Find1, BigNum1);BV.set(BigNum2);auto Find2 = BV.find(BigNum1);EXPECT_EQ(*Find2, BigNum1);auto Find3 = BV.find(BigNum2);EXPECT_EQ(*Find3, BigNum2);BV.reset(BigNum1);auto Find4 = BV.find(BigNum1);EXPECT_EQ(*Find4, BigNum2);BV.clear();BV.set({1, 2, 3});EXPECT_EQ(*BV.find(2), 2u);EXPECT_EQ(*BV.find(3), 3u);}TEST(CoalescingBitVectorTest, AdvanceToLowerBound) {U64BitVec::Allocator Alloc;U64BitVec BV(Alloc);uint64_t BigNum1 = uint64_t(1) << 32;uint64_t BigNum2 = (uint64_t(1) << 33) + 1;auto advFromBegin = [&](uint64_t To) -> U64BitVec::const_iterator {auto It = BV.begin();It.advanceToLowerBound(To);return It;};EXPECT_TRUE(advFromBegin(BigNum1) == BV.end());BV.set(BigNum1);auto Find1 = advFromBegin(BigNum1);EXPECT_EQ(*Find1, BigNum1);BV.set(BigNum2);auto Find2 = advFromBegin(BigNum1);EXPECT_EQ(*Find2, BigNum1);auto Find3 = advFromBegin(BigNum2);EXPECT_EQ(*Find3, BigNum2);BV.reset(BigNum1);auto Find4 = advFromBegin(BigNum1);EXPECT_EQ(*Find4, BigNum2);BV.clear();BV.set({1, 2, 3});EXPECT_EQ(*advFromBegin(2), 2u);EXPECT_EQ(*advFromBegin(3), 3u);auto It = BV.begin();It.advanceToLowerBound(0);EXPECT_EQ(*It, 1u);It.advanceToLowerBound(100);EXPECT_TRUE(It == BV.end());It.advanceToLowerBound(100);EXPECT_TRUE(It == BV.end());}TEST(CoalescingBitVectorTest, HalfOpenRange) {UBitVec::Allocator Alloc;{UBitVec BV(Alloc);BV.set({1, 2, 3});EXPECT_EQ(*BV.find(0), 1U); // find(Start) > StartEXPECT_TRUE(rangesMatch(BV.half_open_range(0, 5), {1, 2, 3}));EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 4), {1, 2, 3}));EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 3), {1, 2}));EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 3), {2}));EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 4), {2, 3}));EXPECT_TRUE(rangesMatch(BV.half_open_range(4, 5), {}));}{UBitVec BV(Alloc);BV.set({1, 2, 11, 12});EXPECT_TRUE(rangesMatch(BV.half_open_range(0, 15), {1, 2, 11, 12}));EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 13), {1, 2, 11, 12}));EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 12), {1, 2, 11}));EXPECT_TRUE(rangesMatch(BV.half_open_range(0, 5), {1, 2}));EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 5), {1, 2}));EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 5), {2}));EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 2), {1}));EXPECT_TRUE(rangesMatch(BV.half_open_range(13, 14), {}));EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 10), {2}));}{UBitVec BV(Alloc);BV.set({1});EXPECT_EQ(*BV.find(0), 1U); // find(Start) == EndEXPECT_TRUE(rangesMatch(BV.half_open_range(0, 1), {}));}{UBitVec BV(Alloc);BV.set({5});EXPECT_EQ(*BV.find(3), 5U); // find(Start) > EndEXPECT_TRUE(rangesMatch(BV.half_open_range(3, 4), {}));}}TEST(CoalescingBitVectorTest, Print) {std::string S;{raw_string_ostream OS(S);UBitVec::Allocator Alloc;UBitVec BV(Alloc);BV.set({1});BV.print(OS);BV.clear();BV.set({1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20});BV.print(OS);}EXPECT_EQ(S, "{[1]}""{[1][11, 20]}");}} // namespace
set(LLVM_LINK_COMPONENTSSupport)add_llvm_unittest(ADTTestsAnyTest.cppAPFixedPointTest.cppAPFloatTest.cppAPIntTest.cppAPSIntTest.cppArrayRefTest.cppBitFieldsTest.cppBitmaskEnumTest.cppBitVectorTest.cppBreadthFirstIteratorTest.cppBumpPtrListTest.cppCoalescingBitVectorTest.cppCombinationGeneratorTest.cppDAGDeltaAlgorithmTest.cppDeltaAlgorithmTest.cppDenseMapTest.cppDenseSetTest.cppDepthFirstIteratorTest.cppDirectedGraphTest.cppEditDistanceTest.cppEnumeratedArrayTest.cppEquivalenceClassesTest.cppFallibleIteratorTest.cppFloatingPointMode.cppFoldingSet.cppFunctionExtrasTest.cppFunctionRefTest.cppHashingTest.cppIListBaseTest.cppIListIteratorTest.cppIListNodeBaseTest.cppIListNodeTest.cppIListSentinelTest.cppIListTest.cppImmutableListTest.cppImmutableMapTest.cppImmutableSetTest.cppIntEqClassesTest.cppIntervalMapTest.cppIntrusiveRefCntPtrTest.cppIteratorTest.cppMappedIteratorTest.cppMapVectorTest.cppOptionalTest.cppPackedVectorTest.cppPointerEmbeddedIntTest.cppPointerIntPairTest.cppPointerSumTypeTest.cppPointerUnionTest.cppPostOrderIteratorTest.cppPriorityWorklistTest.cppRangeAdapterTest.cppSCCIteratorTest.cppSTLExtrasTest.cppSTLForwardCompatTest.cppScopeExitTest.cppSequenceTest.cppSetVectorTest.cppSimpleIListTest.cppSmallPtrSetTest.cppSmallSetTest.cppSmallStringTest.cppSmallVectorTest.cppSparseBitVectorTest.cppSparseMultiSetTest.cppSparseSetTest.cppStatisticTest.cppStringExtrasTest.cppStringMapTest.cppStringRefTest.cppStringSetTest.cppStringSwitchTest.cppTinyPtrVectorTest.cppTripleTest.cppTwineTest.cppTypeSwitchTest.cppTypeTraitsTest.cpp)target_link_libraries(ADTTests PRIVATE LLVMTestingSupport)add_dependencies(ADTTests intrinsics_gen)
//===- unittests/ADT/BumpPtrListTest.cpp - BumpPtrList unit tests ---------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/AllocatorList.h"#include "llvm/ADT/STLExtras.h"#include "gtest/gtest.h"using namespace llvm;namespace {struct CountsDestructors {static unsigned NumCalls;~CountsDestructors() { ++NumCalls; }};unsigned CountsDestructors::NumCalls = 0;struct MoveOnly {int V;explicit MoveOnly(int V) : V(V) {}MoveOnly() = delete;MoveOnly(MoveOnly &&X) { V = X.V; }MoveOnly(const MoveOnly &X) = delete;MoveOnly &operator=(MoveOnly &&X) = delete;MoveOnly &operator=(const MoveOnly &X) = delete;};struct EmplaceOnly {int V1, V2;explicit EmplaceOnly(int V1, int V2) : V1(V1), V2(V2) {}EmplaceOnly() = delete;EmplaceOnly(EmplaceOnly &&X) = delete;EmplaceOnly(const EmplaceOnly &X) = delete;EmplaceOnly &operator=(EmplaceOnly &&X) = delete;EmplaceOnly &operator=(const EmplaceOnly &X) = delete;};TEST(BumpPtrListTest, DefaultConstructor) {BumpPtrList<int> L;EXPECT_TRUE(L.empty());}TEST(BumpPtrListTest, pushPopBack) {// Build a list with push_back.BumpPtrList<int> L;int Ns[] = {1, 3, 9, 5, 7};for (const int N : Ns)L.push_back(N);// Use iterators to check contents.auto I = L.begin();for (int N : Ns)EXPECT_EQ(N, *I++);EXPECT_EQ(I, L.end());// Unbuild the list with pop_back.for (int N : llvm::reverse(Ns)) {EXPECT_EQ(N, L.back());L.pop_back();}EXPECT_TRUE(L.empty());}TEST(BumpPtrListTest, pushPopFront) {// Build a list with push_front.BumpPtrList<int> L;int Ns[] = {1, 3, 9, 5, 7};for (const int N : Ns)L.push_front(N);// Use reverse iterators to check contents.auto I = L.rbegin();for (int N : Ns)EXPECT_EQ(N, *I++);EXPECT_EQ(I, L.rend());// Unbuild the list with pop_front.for (int N : llvm::reverse(Ns)) {EXPECT_EQ(N, L.front());L.pop_front();}EXPECT_TRUE(L.empty());}TEST(BumpPtrListTest, pushBackMoveOnly) {BumpPtrList<MoveOnly> L;int Ns[] = {1, 3, 9, 5, 7};for (const int N : Ns) {L.push_back(MoveOnly(N));EXPECT_EQ(N, L.back().V);}// Instantiate with MoveOnly.while (!L.empty())L.pop_back();}TEST(BumpPtrListTest, pushFrontMoveOnly) {BumpPtrList<MoveOnly> L;int Ns[] = {1, 3, 9, 5, 7};for (const int N : Ns) {L.push_front(MoveOnly(N));EXPECT_EQ(N, L.front().V);}// Instantiate with MoveOnly.while (!L.empty())L.pop_front();}TEST(BumpPtrListTest, emplaceBack) {BumpPtrList<EmplaceOnly> L;int N1s[] = {1, 3, 9, 5, 7};int N2s[] = {7, 3, 1, 8, 2};for (int I = 0; I != 5; ++I) {L.emplace_back(N1s[I], N2s[I]);EXPECT_EQ(N1s[I], L.back().V1);EXPECT_EQ(N2s[I], L.back().V2);}// Instantiate with EmplaceOnly.while (!L.empty())L.pop_back();}TEST(BumpPtrListTest, emplaceFront) {BumpPtrList<EmplaceOnly> L;int N1s[] = {1, 3, 9, 5, 7};int N2s[] = {7, 3, 1, 8, 2};for (int I = 0; I != 5; ++I) {L.emplace_front(N1s[I], N2s[I]);EXPECT_EQ(N1s[I], L.front().V1);EXPECT_EQ(N2s[I], L.front().V2);}// Instantiate with EmplaceOnly.while (!L.empty())L.pop_front();}TEST(BumpPtrListTest, swap) {// Build two lists with different lifetimes and swap them.int N1s[] = {1, 3, 5, 7, 9};int N2s[] = {2, 4, 6, 8, 10};BumpPtrList<int> L1;L1.insert(L1.end(), std::begin(N1s), std::end(N1s));{BumpPtrList<int> L2;L2.insert(L2.end(), std::begin(N2s), std::end(N2s));// Swap the lists.L1.swap(L2);// Check L2's contents before it goes out of scope.auto I = L2.begin();for (int N : N1s)EXPECT_EQ(N, *I++);EXPECT_EQ(I, L2.end());}// Check L1's contents now that L2 is out of scope (with its allocation// blocks).auto I = L1.begin();for (int N : N2s)EXPECT_EQ(N, *I++);EXPECT_EQ(I, L1.end());}TEST(BumpPtrListTest, clear) {CountsDestructors::NumCalls = 0;CountsDestructors N;BumpPtrList<CountsDestructors> L;L.push_back(N);L.push_back(N);L.push_back(N);EXPECT_EQ(3u, L.size());EXPECT_EQ(0u, CountsDestructors::NumCalls);L.pop_back();EXPECT_EQ(1u, CountsDestructors::NumCalls);L.clear();EXPECT_EQ(3u, CountsDestructors::NumCalls);}TEST(BumpPtrListTest, move) {BumpPtrList<int> L1, L2;L1.push_back(1);L2.push_back(2);L1 = std::move(L2);EXPECT_EQ(1u, L1.size());EXPECT_EQ(2, L1.front());EXPECT_EQ(0u, L2.size());}TEST(BumpPtrListTest, moveCallsDestructors) {CountsDestructors::NumCalls = 0;BumpPtrList<CountsDestructors> L1, L2;L1.emplace_back();EXPECT_EQ(0u, CountsDestructors::NumCalls);L1 = std::move(L2);EXPECT_EQ(1u, CountsDestructors::NumCalls);}TEST(BumpPtrListTest, copy) {BumpPtrList<int> L1, L2;L1.push_back(1);L2.push_back(2);L1 = L2;EXPECT_EQ(1u, L1.size());EXPECT_EQ(2, L1.front());EXPECT_EQ(1u, L2.size());EXPECT_EQ(2, L2.front());}TEST(BumpPtrListTest, copyCallsDestructors) {CountsDestructors::NumCalls = 0;BumpPtrList<CountsDestructors> L1, L2;L1.emplace_back();EXPECT_EQ(0u, CountsDestructors::NumCalls);L1 = L2;EXPECT_EQ(1u, CountsDestructors::NumCalls);}TEST(BumpPtrListTest, resetAlloc) {// Resetting an empty list should work.BumpPtrList<int> L;// Resetting an empty list that has allocated should also work.L.resetAlloc();L.push_back(5);L.erase(L.begin());L.resetAlloc();// Resetting a non-empty list should crash.L.push_back(5);#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)EXPECT_DEATH(L.resetAlloc(), "Cannot reset allocator if not empty");#endif}} // end namespace
//=== llvm/unittest/ADT/BreadthFirstIteratorTest.cpp - BFS iterator tests -===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/BreadthFirstIterator.h"#include "TestGraph.h"#include "gtest/gtest.h"using namespace llvm;namespace llvm {TEST(BreadthFristIteratorTest, Basic) {typedef bf_iterator<Graph<4>> BFIter;Graph<4> G;G.AddEdge(0, 1);G.AddEdge(0, 2);G.AddEdge(1, 3);auto It = BFIter::begin(G);auto End = BFIter::end(G);EXPECT_EQ(It.getLevel(), 0U);EXPECT_EQ(*It, G.AccessNode(0));++It;EXPECT_EQ(It.getLevel(), 1U);EXPECT_EQ(*It, G.AccessNode(1));++It;EXPECT_EQ(It.getLevel(), 1U);EXPECT_EQ(*It, G.AccessNode(2));++It;EXPECT_EQ(It.getLevel(), 2U);EXPECT_EQ(*It, G.AccessNode(3));++It;EXPECT_EQ(It, End);}TEST(BreadthFristIteratorTest, Cycle) {typedef bf_iterator<Graph<4>> BFIter;Graph<4> G;G.AddEdge(0, 1);G.AddEdge(1, 0);G.AddEdge(1, 2);G.AddEdge(2, 1);G.AddEdge(2, 1);G.AddEdge(2, 3);G.AddEdge(3, 2);G.AddEdge(3, 1);G.AddEdge(3, 0);auto It = BFIter::begin(G);auto End = BFIter::end(G);EXPECT_EQ(It.getLevel(), 0U);EXPECT_EQ(*It, G.AccessNode(0));++It;EXPECT_EQ(It.getLevel(), 1U);EXPECT_EQ(*It, G.AccessNode(1));++It;EXPECT_EQ(It.getLevel(), 2U);EXPECT_EQ(*It, G.AccessNode(2));++It;EXPECT_EQ(It.getLevel(), 3U);EXPECT_EQ(*It, G.AccessNode(3));++It;EXPECT_EQ(It, End);}} // end namespace llvm
//===- llvm/unittest/ADT/BitmaskEnumTest.cpp - BitmaskEnum unit tests -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/BitmaskEnum.h"#include "gtest/gtest.h"using namespace llvm;namespace {enum Flags {F0 = 0,F1 = 1,F2 = 2,F3 = 4,F4 = 8,LLVM_MARK_AS_BITMASK_ENUM(F4)};TEST(BitmaskEnumTest, BitwiseOr) {Flags f = F1 | F2;EXPECT_EQ(3, f);f = f | F3;EXPECT_EQ(7, f);}TEST(BitmaskEnumTest, BitwiseOrEquals) {Flags f = F1;f |= F3;EXPECT_EQ(5, f);// |= should return a reference to the LHS.f = F2;(f |= F3) = F1;EXPECT_EQ(F1, f);}TEST(BitmaskEnumTest, BitwiseAnd) {Flags f = static_cast<Flags>(3) & F2;EXPECT_EQ(F2, f);f = (f | F3) & (F1 | F2 | F3);EXPECT_EQ(6, f);}TEST(BitmaskEnumTest, BitwiseAndEquals) {Flags f = F1 | F2 | F3;f &= F1 | F2;EXPECT_EQ(3, f);// &= should return a reference to the LHS.(f &= F1) = F3;EXPECT_EQ(F3, f);}TEST(BitmaskEnumTest, BitwiseXor) {Flags f = (F1 | F2) ^ (F2 | F3);EXPECT_EQ(5, f);f = f ^ F1;EXPECT_EQ(4, f);}TEST(BitmaskEnumTest, BitwiseXorEquals) {Flags f = (F1 | F2);f ^= (F2 | F4);EXPECT_EQ(9, f);// ^= should return a reference to the LHS.(f ^= F4) = F3;EXPECT_EQ(F3, f);}TEST(BitmaskEnumTest, ConstantExpression) {constexpr Flags f1 = ~F1;constexpr Flags f2 = F1 | F2;constexpr Flags f3 = F1 & F2;constexpr Flags f4 = F1 ^ F2;EXPECT_EQ(f1, ~F1);EXPECT_EQ(f2, F1 | F2);EXPECT_EQ(f3, F1 & F2);EXPECT_EQ(f4, F1 ^ F2);}TEST(BitmaskEnumTest, BitwiseNot) {Flags f = ~F1;EXPECT_EQ(14, f); // Largest value for f is 15.EXPECT_EQ(15, ~F0);}enum class FlagsClass {F0 = 0,F1 = 1,F2 = 2,F3 = 4,LLVM_MARK_AS_BITMASK_ENUM(F3)};TEST(BitmaskEnumTest, ScopedEnum) {FlagsClass f = (FlagsClass::F1 & ~FlagsClass::F0) | FlagsClass::F2;f |= FlagsClass::F3;EXPECT_EQ(7, static_cast<int>(f));}struct Container {enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, LLVM_MARK_AS_BITMASK_ENUM(F3) };static Flags getFlags() {Flags f = F0 | F1;f |= F2;return f;}};TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); }} // namespacenamespace foo {namespace bar {namespace {enum FlagsInNamespace {F0 = 0,F1 = 1,F2 = 2,F3 = 4,LLVM_MARK_AS_BITMASK_ENUM(F3)};} // namespace} // namespace foo} // namespace barnamespace {TEST(BitmaskEnumTest, EnumInNamespace) {foo::bar::FlagsInNamespace f = ~foo::bar::F0 & (foo::bar::F1 | foo::bar::F2);f |= foo::bar::F3;EXPECT_EQ(7, f);}} // namespace
//===- llvm/unittest/ADT/BitVectorTest.cpp - BitVector tests --------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/BitVector.h"#include "llvm/ADT/DenseSet.h"#include "llvm/ADT/SmallBitVector.h"#include "gtest/gtest.h"using namespace llvm;namespace {// Test fixturetemplate <typename T>class BitVectorTest : public ::testing::Test { };// Test both BitVector and SmallBitVector with the same suite of tests.typedef ::testing::Types<BitVector, SmallBitVector> BitVectorTestTypes;TYPED_TEST_SUITE(BitVectorTest, BitVectorTestTypes, );TYPED_TEST(BitVectorTest, TrivialOperation) {TypeParam Vec;EXPECT_EQ(0U, Vec.count());EXPECT_EQ(0U, Vec.size());EXPECT_FALSE(Vec.any());EXPECT_TRUE(Vec.all());EXPECT_TRUE(Vec.none());EXPECT_TRUE(Vec.empty());Vec.resize(5, true);EXPECT_EQ(5U, Vec.count());EXPECT_EQ(5U, Vec.size());EXPECT_TRUE(Vec.any());EXPECT_TRUE(Vec.all());EXPECT_FALSE(Vec.none());EXPECT_FALSE(Vec.empty());Vec.resize(11);EXPECT_EQ(5U, Vec.count());EXPECT_EQ(11U, Vec.size());EXPECT_TRUE(Vec.any());EXPECT_FALSE(Vec.all());EXPECT_FALSE(Vec.none());EXPECT_FALSE(Vec.empty());TypeParam Inv = Vec;Inv.flip();EXPECT_EQ(6U, Inv.count());EXPECT_EQ(11U, Inv.size());EXPECT_TRUE(Inv.any());EXPECT_FALSE(Inv.all());EXPECT_FALSE(Inv.none());EXPECT_FALSE(Inv.empty());EXPECT_FALSE(Inv == Vec);EXPECT_TRUE(Inv != Vec);Vec.flip();EXPECT_TRUE(Inv == Vec);EXPECT_FALSE(Inv != Vec);// Add some "interesting" data to Vec.Vec.resize(23, true);Vec.resize(25, false);Vec.resize(26, true);Vec.resize(29, false);Vec.resize(33, true);Vec.resize(57, false);unsigned Count = 0;for (unsigned i = Vec.find_first(); i != -1u; i = Vec.find_next(i)) {++Count;EXPECT_TRUE(Vec[i]);EXPECT_TRUE(Vec.test(i));}EXPECT_EQ(Count, Vec.count());EXPECT_EQ(Count, 23u);EXPECT_FALSE(Vec[0]);EXPECT_TRUE(Vec[32]);EXPECT_FALSE(Vec[56]);Vec.resize(61, false);TypeParam Copy = Vec;TypeParam Alt(3, false);Alt.resize(6, true);std::swap(Alt, Vec);EXPECT_TRUE(Copy == Alt);EXPECT_TRUE(Vec.size() == 6);EXPECT_TRUE(Vec.count() == 3);EXPECT_TRUE(Vec.find_first() == 3);std::swap(Copy, Vec);// Add some more "interesting" data.Vec.resize(68, true);Vec.resize(78, false);Vec.resize(89, true);Vec.resize(90, false);Vec.resize(91, true);Vec.resize(130, false);Count = 0;for (unsigned i = Vec.find_first(); i != -1u; i = Vec.find_next(i)) {++Count;EXPECT_TRUE(Vec[i]);EXPECT_TRUE(Vec.test(i));}EXPECT_EQ(Count, Vec.count());EXPECT_EQ(Count, 42u);EXPECT_FALSE(Vec[0]);EXPECT_TRUE(Vec[32]);EXPECT_FALSE(Vec[60]);EXPECT_FALSE(Vec[129]);Vec.flip(60);EXPECT_TRUE(Vec[60]);EXPECT_EQ(Count + 1, Vec.count());Vec.flip(60);EXPECT_FALSE(Vec[60]);EXPECT_EQ(Count, Vec.count());Vec.reset(32);EXPECT_FALSE(Vec[32]);EXPECT_EQ(Count - 1, Vec.count());Vec.set(32);EXPECT_TRUE(Vec[32]);EXPECT_EQ(Count, Vec.count());Vec.flip();EXPECT_EQ(Vec.size() - Count, Vec.count());Vec.reset();EXPECT_EQ(0U, Vec.count());EXPECT_EQ(130U, Vec.size());EXPECT_FALSE(Vec.any());EXPECT_FALSE(Vec.all());EXPECT_TRUE(Vec.none());EXPECT_FALSE(Vec.empty());Vec.flip();EXPECT_EQ(130U, Vec.count());EXPECT_EQ(130U, Vec.size());EXPECT_TRUE(Vec.any());EXPECT_TRUE(Vec.all());EXPECT_FALSE(Vec.none());EXPECT_FALSE(Vec.empty());Vec.resize(64);EXPECT_EQ(64U, Vec.count());EXPECT_EQ(64U, Vec.size());EXPECT_TRUE(Vec.any());EXPECT_TRUE(Vec.all());EXPECT_FALSE(Vec.none());EXPECT_FALSE(Vec.empty());Vec.flip();EXPECT_EQ(0U, Vec.count());EXPECT_EQ(64U, Vec.size());EXPECT_FALSE(Vec.any());EXPECT_FALSE(Vec.all());EXPECT_TRUE(Vec.none());EXPECT_FALSE(Vec.empty());Inv = TypeParam().flip();EXPECT_EQ(0U, Inv.count());EXPECT_EQ(0U, Inv.size());EXPECT_FALSE(Inv.any());EXPECT_TRUE(Inv.all());EXPECT_TRUE(Inv.none());EXPECT_TRUE(Inv.empty());Vec.clear();EXPECT_EQ(0U, Vec.count());EXPECT_EQ(0U, Vec.size());EXPECT_FALSE(Vec.any());EXPECT_TRUE(Vec.all());EXPECT_TRUE(Vec.none());EXPECT_TRUE(Vec.empty());}TYPED_TEST(BitVectorTest, Equality) {TypeParam A;TypeParam B;EXPECT_TRUE(A == B);A.resize(10);EXPECT_FALSE(A == B);B.resize(10);EXPECT_TRUE(A == B);A.set(5);EXPECT_FALSE(A == B);B.set(5);EXPECT_TRUE(A == B);A.resize(20);EXPECT_FALSE(A == B);B.resize(20);EXPECT_TRUE(A == B);}TYPED_TEST(BitVectorTest, SimpleFindOpsMultiWord) {TypeParam A;// Test finding next set and unset bits in a BitVector with multiple wordsA.resize(100);A.set(12);A.set(13);A.set(75);EXPECT_EQ(75, A.find_last());EXPECT_EQ(12, A.find_first());EXPECT_EQ(13, A.find_next(12));EXPECT_EQ(75, A.find_next(13));EXPECT_EQ(-1, A.find_next(75));EXPECT_EQ(-1, A.find_prev(12));EXPECT_EQ(12, A.find_prev(13));EXPECT_EQ(13, A.find_prev(75));EXPECT_EQ(75, A.find_prev(90));EXPECT_EQ(0, A.find_first_unset());EXPECT_EQ(99, A.find_last_unset());EXPECT_EQ(14, A.find_next_unset(11));EXPECT_EQ(14, A.find_next_unset(12));EXPECT_EQ(14, A.find_next_unset(13));EXPECT_EQ(16, A.find_next_unset(15));EXPECT_EQ(76, A.find_next_unset(74));EXPECT_EQ(76, A.find_next_unset(75));EXPECT_EQ(-1, A.find_next_unset(99));A.set(0, 100);EXPECT_EQ(100U, A.count());EXPECT_EQ(0, A.find_first());EXPECT_EQ(-1, A.find_first_unset());EXPECT_EQ(-1, A.find_last_unset());EXPECT_EQ(99, A.find_last());EXPECT_EQ(99, A.find_next(98));A.reset(0, 100);EXPECT_EQ(0U, A.count());EXPECT_EQ(-1, A.find_first());EXPECT_EQ(-1, A.find_last());EXPECT_EQ(0, A.find_first_unset());EXPECT_EQ(99, A.find_last_unset());EXPECT_EQ(99, A.find_next_unset(98));}// Test finding next set and unset bits in a BitVector/SmallBitVector within a// uintptr_t - check both 32-bit (Multi) and 64-bit (Small) targets.TYPED_TEST(BitVectorTest, SimpleFindOps64Bit) {TypeParam A;A.resize(57);A.set(12);A.set(13);A.set(47);EXPECT_EQ(47, A.find_last());EXPECT_EQ(12, A.find_first());EXPECT_EQ(13, A.find_next(12));EXPECT_EQ(47, A.find_next(13));EXPECT_EQ(-1, A.find_next(47));EXPECT_EQ(-1, A.find_prev(12));EXPECT_EQ(12, A.find_prev(13));EXPECT_EQ(13, A.find_prev(47));EXPECT_EQ(47, A.find_prev(56));EXPECT_EQ(0, A.find_first_unset());EXPECT_EQ(56, A.find_last_unset());EXPECT_EQ(14, A.find_next_unset(11));EXPECT_EQ(14, A.find_next_unset(12));EXPECT_EQ(14, A.find_next_unset(13));EXPECT_EQ(16, A.find_next_unset(15));EXPECT_EQ(48, A.find_next_unset(46));EXPECT_EQ(48, A.find_next_unset(47));EXPECT_EQ(-1, A.find_next_unset(56));}// Check if a SmallBitVector is in small mode. This check is used in tests// that run for both SmallBitVector and BitVector. This check doesn't apply// to BitVector so we provide an overload that returns true to get the tests// to compile.static bool SmallBitVectorIsSmallMode(const SmallBitVector &bv) {return bv.isSmall();}static bool SmallBitVectorIsSmallMode(const BitVector &) { return true; }// These tests are intended to exercise the single-word case of BitVector// and the small-mode case of SmallBitVector.TYPED_TEST(BitVectorTest, SimpleFindOpsSingleWord) {// Test finding in an empty BitVector.TypeParam A;ASSERT_TRUE(SmallBitVectorIsSmallMode(A));EXPECT_EQ(-1, A.find_first());EXPECT_EQ(-1, A.find_last());EXPECT_EQ(-1, A.find_first_unset());EXPECT_EQ(-1, A.find_last_unset());A.resize(20);ASSERT_TRUE(SmallBitVectorIsSmallMode(A));EXPECT_EQ(-1, A.find_first());EXPECT_EQ(-1, A.find_last());EXPECT_EQ(-1, A.find_next(5));EXPECT_EQ(-1, A.find_next(19));EXPECT_EQ(-1, A.find_prev(5));EXPECT_EQ(-1, A.find_prev(20));EXPECT_EQ(0, A.find_first_unset());EXPECT_EQ(19, A.find_last_unset());EXPECT_EQ(6, A.find_next_unset(5));EXPECT_EQ(-1, A.find_next_unset(19));A.set(3);A.set(4);A.set(16);ASSERT_TRUE(SmallBitVectorIsSmallMode(A));EXPECT_EQ(16, A.find_last());EXPECT_EQ(3, A.find_first());EXPECT_EQ(3, A.find_next(1));EXPECT_EQ(4, A.find_next(3));EXPECT_EQ(16, A.find_next(4));EXPECT_EQ(-1, A.find_next(16));EXPECT_EQ(-1, A.find_prev(3));EXPECT_EQ(3, A.find_prev(4));EXPECT_EQ(4, A.find_prev(16));EXPECT_EQ(16, A.find_prev(18));EXPECT_EQ(0, A.find_first_unset());EXPECT_EQ(19, A.find_last_unset());EXPECT_EQ(5, A.find_next_unset(3));EXPECT_EQ(5, A.find_next_unset(4));EXPECT_EQ(13, A.find_next_unset(12));EXPECT_EQ(17, A.find_next_unset(15));A.set();ASSERT_TRUE(SmallBitVectorIsSmallMode(A));EXPECT_EQ(0, A.find_first());EXPECT_EQ(19, A.find_last());EXPECT_EQ(6, A.find_next(5));EXPECT_EQ(-1, A.find_next(19));EXPECT_EQ(4, A.find_prev(5));EXPECT_EQ(19, A.find_prev(20));EXPECT_EQ(-1, A.find_first_unset());EXPECT_EQ(-1, A.find_last_unset());EXPECT_EQ(-1, A.find_next_unset(5));EXPECT_EQ(-1, A.find_next_unset(19));}TEST(BitVectorTest, FindInRangeMultiWord) {BitVector Vec;Vec.resize(200);Vec.set(3, 7);Vec.set(24, 35);Vec.set(50, 70);Vec.set(150);Vec.set(152);Vec.set(154);// find firstEXPECT_EQ(-1, Vec.find_first_in(0, 0));EXPECT_EQ(-1, Vec.find_first_in(24, 24));EXPECT_EQ(-1, Vec.find_first_in(7, 24));EXPECT_EQ(3, Vec.find_first_in(0, 10));EXPECT_EQ(4, Vec.find_first_in(4, 10));EXPECT_EQ(150, Vec.find_first_in(100, 200));EXPECT_EQ(152, Vec.find_first_in(151, 200));EXPECT_EQ(154, Vec.find_first_in(153, 200));EXPECT_EQ(-1, Vec.find_first_in(155, 200));Vec.set(199);EXPECT_EQ(199, Vec.find_first_in(199, 200));Vec.reset(199);// find lastEXPECT_EQ(-1, Vec.find_last_in(0, 0));EXPECT_EQ(-1, Vec.find_last_in(24, 24));EXPECT_EQ(-1, Vec.find_last_in(7, 24));EXPECT_EQ(6, Vec.find_last_in(0, 10));EXPECT_EQ(5, Vec.find_last_in(0, 6));EXPECT_EQ(154, Vec.find_last_in(100, 155));EXPECT_EQ(152, Vec.find_last_in(100, 154));EXPECT_EQ(150, Vec.find_last_in(100, 152));EXPECT_EQ(-1, Vec.find_last_in(100, 150));Vec.set(199);EXPECT_EQ(199, Vec.find_last_in(199, 200));Vec.reset(199);// find first unsetEXPECT_EQ(-1, Vec.find_first_unset_in(0, 0));EXPECT_EQ(-1, Vec.find_first_unset_in(23, 23));EXPECT_EQ(-1, Vec.find_first_unset_in(24, 35));EXPECT_EQ(0, Vec.find_first_unset_in(0, 10));EXPECT_EQ(1, Vec.find_first_unset_in(1, 10));EXPECT_EQ(7, Vec.find_first_unset_in(5, 25));EXPECT_EQ(151, Vec.find_first_unset_in(150, 200));EXPECT_EQ(151, Vec.find_first_unset_in(151, 200));EXPECT_EQ(153, Vec.find_first_unset_in(152, 200));EXPECT_EQ(153, Vec.find_first_unset_in(153, 200));EXPECT_EQ(155, Vec.find_first_unset_in(154, 200));EXPECT_EQ(155, Vec.find_first_unset_in(155, 200));EXPECT_EQ(199, Vec.find_first_unset_in(199, 200));// find last unsetEXPECT_EQ(-1, Vec.find_last_unset_in(0, 0));EXPECT_EQ(-1, Vec.find_last_unset_in(23, 23));EXPECT_EQ(-1, Vec.find_last_unset_in(24, 35));EXPECT_EQ(9, Vec.find_last_unset_in(0, 10));EXPECT_EQ(8, Vec.find_last_unset_in(0, 9));EXPECT_EQ(2, Vec.find_last_unset_in(0, 7));EXPECT_EQ(149, Vec.find_last_unset_in(100, 151));EXPECT_EQ(151, Vec.find_last_unset_in(100, 152));EXPECT_EQ(151, Vec.find_last_unset_in(100, 153));EXPECT_EQ(153, Vec.find_last_unset_in(100, 154));EXPECT_EQ(153, Vec.find_last_unset_in(100, 155));EXPECT_EQ(155, Vec.find_last_unset_in(100, 156));EXPECT_EQ(199, Vec.find_last_unset_in(199, 200));}TEST(BitVectorTest, FindInRangeSingleWord) {// When the bit vector contains only a single word, this is slightly different// than when the bit vector contains multiple words, because masks are applied// to the front and back of the same word. So make sure this works.BitVector Vec;Vec.resize(25);Vec.set(2, 4);Vec.set(6, 9);Vec.set(12, 15);Vec.set(19);Vec.set(21);Vec.set(23);// find firstEXPECT_EQ(-1, Vec.find_first_in(0, 0));EXPECT_EQ(-1, Vec.find_first_in(24, 24));EXPECT_EQ(-1, Vec.find_first_in(9, 12));EXPECT_EQ(2, Vec.find_first_in(0, 10));EXPECT_EQ(6, Vec.find_first_in(4, 10));EXPECT_EQ(19, Vec.find_first_in(18, 25));EXPECT_EQ(21, Vec.find_first_in(20, 25));EXPECT_EQ(23, Vec.find_first_in(22, 25));EXPECT_EQ(-1, Vec.find_first_in(24, 25));// find lastEXPECT_EQ(-1, Vec.find_last_in(0, 0));EXPECT_EQ(-1, Vec.find_last_in(24, 24));EXPECT_EQ(-1, Vec.find_last_in(9, 12));EXPECT_EQ(8, Vec.find_last_in(0, 10));EXPECT_EQ(3, Vec.find_last_in(0, 6));EXPECT_EQ(23, Vec.find_last_in(18, 25));EXPECT_EQ(21, Vec.find_last_in(18, 23));EXPECT_EQ(19, Vec.find_last_in(18, 21));EXPECT_EQ(-1, Vec.find_last_in(18, 19));// find first unsetEXPECT_EQ(-1, Vec.find_first_unset_in(0, 0));EXPECT_EQ(-1, Vec.find_first_unset_in(23, 23));EXPECT_EQ(-1, Vec.find_first_unset_in(6, 9));EXPECT_EQ(0, Vec.find_first_unset_in(0, 6));EXPECT_EQ(1, Vec.find_first_unset_in(1, 6));EXPECT_EQ(9, Vec.find_first_unset_in(7, 13));EXPECT_EQ(18, Vec.find_first_unset_in(18, 25));EXPECT_EQ(20, Vec.find_first_unset_in(19, 25));EXPECT_EQ(20, Vec.find_first_unset_in(20, 25));EXPECT_EQ(22, Vec.find_first_unset_in(21, 25));EXPECT_EQ(22, Vec.find_first_unset_in(22, 25));EXPECT_EQ(24, Vec.find_first_unset_in(23, 25));EXPECT_EQ(24, Vec.find_first_unset_in(24, 25));// find last unsetEXPECT_EQ(-1, Vec.find_last_unset_in(0, 0));EXPECT_EQ(-1, Vec.find_last_unset_in(23, 23));EXPECT_EQ(-1, Vec.find_last_unset_in(6, 9));EXPECT_EQ(5, Vec.find_last_unset_in(0, 6));EXPECT_EQ(4, Vec.find_last_unset_in(0, 5));EXPECT_EQ(1, Vec.find_last_unset_in(0, 4));EXPECT_EQ(11, Vec.find_last_unset_in(7, 13));EXPECT_EQ(24, Vec.find_last_unset_in(18, 25));EXPECT_EQ(22, Vec.find_last_unset_in(18, 24));EXPECT_EQ(22, Vec.find_last_unset_in(18, 23));EXPECT_EQ(20, Vec.find_last_unset_in(18, 22));EXPECT_EQ(20, Vec.find_last_unset_in(18, 21));EXPECT_EQ(18, Vec.find_last_unset_in(18, 20));EXPECT_EQ(18, Vec.find_last_unset_in(18, 19));}TYPED_TEST(BitVectorTest, CompoundAssignment) {TypeParam A;A.resize(10);A.set(4);A.set(7);TypeParam B;B.resize(50);B.set(5);B.set(18);A |= B;EXPECT_TRUE(A.test(4));EXPECT_TRUE(A.test(5));EXPECT_TRUE(A.test(7));EXPECT_TRUE(A.test(18));EXPECT_EQ(4U, A.count());EXPECT_EQ(50U, A.size());B.resize(10);B.set();B.reset(2);B.reset(7);A &= B;EXPECT_FALSE(A.test(2));EXPECT_FALSE(A.test(7));EXPECT_TRUE(A.test(4));EXPECT_TRUE(A.test(5));EXPECT_EQ(2U, A.count());EXPECT_EQ(50U, A.size());B.resize(100);B.set();A ^= B;EXPECT_TRUE(A.test(2));EXPECT_TRUE(A.test(7));EXPECT_EQ(98U, A.count());EXPECT_EQ(100U, A.size());}// Test SmallBitVector operations with mixed big/small representationsTYPED_TEST(BitVectorTest, MixedBigSmall) {{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Small &= Big;EXPECT_TRUE(Small.test(0));EXPECT_EQ(1u, Small.count());// FIXME BitVector and SmallBitVector behave differently here.// SmallBitVector resizes the LHS to max(LHS.size(), RHS.size())// but BitVector does not.// EXPECT_EQ(20u, Small.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Big &= Small;EXPECT_TRUE(Big.test(0));EXPECT_EQ(1u, Big.count());// FIXME BitVector and SmallBitVector behave differently here.// SmallBitVector resizes the LHS to max(LHS.size(), RHS.size())// but BitVector does not.// EXPECT_EQ(20u, Big.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Small |= Big;EXPECT_TRUE(Small.test(0));EXPECT_TRUE(Small.test(1));EXPECT_TRUE(Small.test(2));EXPECT_TRUE(Small.test(16));EXPECT_EQ(4u, Small.count());EXPECT_EQ(20u, Small.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Big |= Small;EXPECT_TRUE(Big.test(0));EXPECT_TRUE(Big.test(1));EXPECT_TRUE(Big.test(2));EXPECT_TRUE(Big.test(16));EXPECT_EQ(4u, Big.count());EXPECT_EQ(20u, Big.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Small ^= Big;EXPECT_TRUE(Small.test(1));EXPECT_TRUE(Small.test(2));EXPECT_TRUE(Small.test(16));EXPECT_EQ(3u, Small.count());EXPECT_EQ(20u, Small.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Big ^= Small;EXPECT_TRUE(Big.test(1));EXPECT_TRUE(Big.test(2));EXPECT_TRUE(Big.test(16));EXPECT_EQ(3u, Big.count());EXPECT_EQ(20u, Big.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Small.reset(Big);EXPECT_TRUE(Small.test(1));EXPECT_EQ(1u, Small.count());EXPECT_EQ(10u, Small.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);Big.set(2);Big.set(16);Big.reset(Small);EXPECT_TRUE(Big.test(2));EXPECT_TRUE(Big.test(16));EXPECT_EQ(2u, Big.count());EXPECT_EQ(20u, Big.size());}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(10);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);EXPECT_FALSE(Big == Small);EXPECT_FALSE(Small == Big);Big.set(1);EXPECT_TRUE(Big == Small);EXPECT_TRUE(Small == Big);}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(20);Small.resize(10);Small.set(0);Big.set(1);EXPECT_FALSE(Small.anyCommon(Big));EXPECT_FALSE(Big.anyCommon(Small));Big.set(0);EXPECT_TRUE(Small.anyCommon(Big));EXPECT_TRUE(Big.anyCommon(Small));}{TypeParam Big;TypeParam Small;Big.reserve(100);Big.resize(10);Small.resize(10);Small.set(0);Small.set(1);Big.set(0);EXPECT_TRUE(Small.test(Big));EXPECT_FALSE(Big.test(Small));Big.set(1);EXPECT_FALSE(Small.test(Big));EXPECT_FALSE(Big.test(Small));}}TYPED_TEST(BitVectorTest, ProxyIndex) {TypeParam Vec(3);EXPECT_TRUE(Vec.none());Vec[0] = Vec[1] = Vec[2] = true;EXPECT_EQ(Vec.size(), Vec.count());Vec[2] = Vec[1] = Vec[0] = false;EXPECT_TRUE(Vec.none());}TYPED_TEST(BitVectorTest, PortableBitMask) {TypeParam A;const uint32_t Mask1[] = { 0x80000000, 6, 5 };A.resize(10);A.setBitsInMask(Mask1, 1);EXPECT_EQ(10u, A.size());EXPECT_FALSE(A.test(0));A.resize(32);A.setBitsInMask(Mask1, 1);EXPECT_FALSE(A.test(0));EXPECT_TRUE(A.test(31));EXPECT_EQ(1u, A.count());A.resize(33);A.setBitsInMask(Mask1, 1);EXPECT_EQ(1u, A.count());A.setBitsInMask(Mask1, 2);EXPECT_EQ(1u, A.count());A.resize(34);A.setBitsInMask(Mask1, 2);EXPECT_EQ(2u, A.count());A.resize(65);A.setBitsInMask(Mask1, 3);EXPECT_EQ(4u, A.count());A.setBitsNotInMask(Mask1, 1);EXPECT_EQ(32u+3u, A.count());A.setBitsNotInMask(Mask1, 3);EXPECT_EQ(65u, A.count());A.resize(96);EXPECT_EQ(65u, A.count());A.clear();A.resize(128);A.setBitsNotInMask(Mask1, 3);EXPECT_EQ(96u-5u, A.count());A.clearBitsNotInMask(Mask1, 1);EXPECT_EQ(64-4u, A.count());}TYPED_TEST(BitVectorTest, BinOps) {TypeParam A;TypeParam B;A.resize(65);EXPECT_FALSE(A.anyCommon(B));EXPECT_FALSE(B.anyCommon(B));B.resize(64);A.set(64);EXPECT_FALSE(A.anyCommon(B));EXPECT_FALSE(B.anyCommon(A));B.set(63);EXPECT_FALSE(A.anyCommon(B));EXPECT_FALSE(B.anyCommon(A));A.set(63);EXPECT_TRUE(A.anyCommon(B));EXPECT_TRUE(B.anyCommon(A));B.resize(70);B.set(64);B.reset(63);A.resize(64);EXPECT_FALSE(A.anyCommon(B));EXPECT_FALSE(B.anyCommon(A));}typedef std::vector<std::pair<int, int>> RangeList;template <typename VecType>static inline VecType createBitVector(uint32_t Size,const RangeList &setRanges) {VecType V;V.resize(Size);for (auto &R : setRanges)V.set(R.first, R.second);return V;}TYPED_TEST(BitVectorTest, ShiftOpsSingleWord) {// Test that shift ops work when the desired shift amount is less// than one word.// 1. Case where the number of bits in the BitVector also fit into a single// word.TypeParam A = createBitVector<TypeParam>(12, {{2, 4}, {8, 10}});TypeParam B = A;EXPECT_EQ(4U, A.count());EXPECT_TRUE(A.test(2));EXPECT_TRUE(A.test(3));EXPECT_TRUE(A.test(8));EXPECT_TRUE(A.test(9));A >>= 1;EXPECT_EQ(createBitVector<TypeParam>(12, {{1, 3}, {7, 9}}), A);A <<= 1;EXPECT_EQ(B, A);A >>= 10;EXPECT_EQ(createBitVector<TypeParam>(12, {}), A);A = B;A <<= 10;EXPECT_EQ(createBitVector<TypeParam>(12, {}), A);// 2. Case where the number of bits in the BitVector do not fit into a single// word.// 31----------------------------------------------------------------------0// XXXXXXXX XXXXXXXX XXXXXXXX 00000111 | 11111110 00000000 00001111 11111111A = createBitVector<TypeParam>(40, {{0, 12}, {25, 35}});EXPECT_EQ(40U, A.size());EXPECT_EQ(22U, A.count());// 2a. Make sure that left shifting some 1 bits out of the vector works.// 31----------------------------------------------------------------------0// Before:// XXXXXXXX XXXXXXXX XXXXXXXX 00000111 | 11111110 00000000 00001111 11111111// After:// XXXXXXXX XXXXXXXX XXXXXXXX 11111100 | 00000000 00011111 11111110 00000000A <<= 9;EXPECT_EQ(createBitVector<TypeParam>(40, {{9, 21}, {34, 40}}), A);// 2b. Make sure that keeping the number of one bits unchanged works.// 31----------------------------------------------------------------------0// Before:// XXXXXXXX XXXXXXXX XXXXXXXX 11111100 | 00000000 00011111 11111110 00000000// After:// XXXXXXXX XXXXXXXX XXXXXXXX 00000011 | 11110000 00000000 01111111 11111000A >>= 6;EXPECT_EQ(createBitVector<TypeParam>(40, {{3, 15}, {28, 34}}), A);// 2c. Make sure that right shifting some 1 bits out of the vector works.// 31----------------------------------------------------------------------0// Before:// XXXXXXXX XXXXXXXX XXXXXXXX 00000011 | 11110000 00000000 01111111 11111000// After:// XXXXXXXX XXXXXXXX XXXXXXXX 00000000 | 00000000 11111100 00000000 00011111A >>= 10;EXPECT_EQ(createBitVector<TypeParam>(40, {{0, 5}, {18, 24}}), A);// 3. Big test.A = createBitVector<TypeParam>(300, {{1, 30}, {60, 95}, {200, 275}});A <<= 29;EXPECT_EQ(createBitVector<TypeParam>(300, {{1 + 29, 30 + 29}, {60 + 29, 95 + 29}, {200 + 29, 300}}),A);}TYPED_TEST(BitVectorTest, ShiftOpsMultiWord) {// Test that shift ops work when the desired shift amount is greater than or// equal to the size of a single word.auto A = createBitVector<TypeParam>(300, {{1, 30}, {60, 95}, {200, 275}});// Make a copy so we can re-use it later.auto B = A;// 1. Shift left by an exact multiple of the word size. This should invoke// only a memmove and no per-word bit operations.A <<= 64;auto Expected = createBitVector<TypeParam>(300, {{1 + 64, 30 + 64}, {60 + 64, 95 + 64}, {200 + 64, 300}});EXPECT_EQ(Expected, A);// 2. Shift left by a non multiple of the word size. This should invoke both// a memmove and per-word bit operations.A = B;A <<= 93;EXPECT_EQ(createBitVector<TypeParam>(300, {{1 + 93, 30 + 93}, {60 + 93, 95 + 93}, {200 + 93, 300}}),A);// 1. Shift right by an exact multiple of the word size. This should invoke// only a memmove and no per-word bit operations.A = B;A >>= 64;EXPECT_EQ(createBitVector<TypeParam>(300, {{0, 95 - 64}, {200 - 64, 275 - 64}}), A);// 2. Shift left by a non multiple of the word size. This should invoke both// a memmove and per-word bit operations.A = B;A >>= 93;EXPECT_EQ(createBitVector<TypeParam>(300, {{0, 95 - 93}, {200 - 93, 275 - 93}}), A);}TYPED_TEST(BitVectorTest, RangeOps) {TypeParam A;A.resize(256);A.reset();A.set(1, 255);EXPECT_FALSE(A.test(0));EXPECT_TRUE( A.test(1));EXPECT_TRUE( A.test(23));EXPECT_TRUE( A.test(254));EXPECT_FALSE(A.test(255));TypeParam B;B.resize(256);B.set();B.reset(1, 255);EXPECT_TRUE( B.test(0));EXPECT_FALSE(B.test(1));EXPECT_FALSE(B.test(23));EXPECT_FALSE(B.test(254));EXPECT_TRUE( B.test(255));TypeParam C;C.resize(3);C.reset();C.set(0, 1);EXPECT_TRUE(C.test(0));EXPECT_FALSE( C.test(1));EXPECT_FALSE( C.test(2));TypeParam D;D.resize(3);D.set();D.reset(0, 1);EXPECT_FALSE(D.test(0));EXPECT_TRUE( D.test(1));EXPECT_TRUE( D.test(2));TypeParam E;E.resize(128);E.reset();E.set(1, 33);EXPECT_FALSE(E.test(0));EXPECT_TRUE( E.test(1));EXPECT_TRUE( E.test(32));EXPECT_FALSE(E.test(33));TypeParam BufferOverrun;unsigned size = sizeof(unsigned long) * 8;BufferOverrun.resize(size);BufferOverrun.reset(0, size);BufferOverrun.set(0, size);}TYPED_TEST(BitVectorTest, CompoundTestReset) {TypeParam A(50, true);TypeParam B(50, false);TypeParam C(100, true);TypeParam D(100, false);EXPECT_FALSE(A.test(A));EXPECT_TRUE(A.test(B));EXPECT_FALSE(A.test(C));EXPECT_TRUE(A.test(D));EXPECT_FALSE(B.test(A));EXPECT_FALSE(B.test(B));EXPECT_FALSE(B.test(C));EXPECT_FALSE(B.test(D));EXPECT_TRUE(C.test(A));EXPECT_TRUE(C.test(B));EXPECT_FALSE(C.test(C));EXPECT_TRUE(C.test(D));A.reset(B);A.reset(D);EXPECT_TRUE(A.all());A.reset(A);EXPECT_TRUE(A.none());A.set();A.reset(C);EXPECT_TRUE(A.none());A.set();C.reset(A);EXPECT_EQ(50, C.find_first());C.reset(C);EXPECT_TRUE(C.none());}TYPED_TEST(BitVectorTest, MoveConstructor) {TypeParam A(10, true);TypeParam B(std::move(A));// Check that the move ctor leaves the moved-from object in a valid state.// The following line used to crash.A = B;TypeParam C(10, true);EXPECT_EQ(C, A);EXPECT_EQ(C, B);}TYPED_TEST(BitVectorTest, MoveAssignment) {TypeParam A(10, true);TypeParam B;B = std::move(A);// Check that move assignment leaves the moved-from object in a valid state.// The following line used to crash.A = B;TypeParam C(10, true);EXPECT_EQ(C, A);EXPECT_EQ(C, B);}template<class TypeParam>static void testEmpty(const TypeParam &A) {EXPECT_TRUE(A.empty());EXPECT_EQ((size_t)0, A.size());EXPECT_EQ((size_t)0, A.count());EXPECT_FALSE(A.any());EXPECT_TRUE(A.all());EXPECT_TRUE(A.none());EXPECT_EQ(-1, A.find_first());EXPECT_EQ(A, TypeParam());}/// Tests whether BitVector behaves well with Bits==nullptr, Capacity==0TYPED_TEST(BitVectorTest, EmptyVector) {TypeParam A;testEmpty(A);TypeParam B;B.reset();testEmpty(B);TypeParam C;C.clear();testEmpty(C);TypeParam D(A);testEmpty(D);TypeParam E;E = A;testEmpty(E);TypeParam F;E.reset(A);testEmpty(E);}TYPED_TEST(BitVectorTest, Iterators) {TypeParam Filled(10, true);EXPECT_NE(Filled.set_bits_begin(), Filled.set_bits_end());unsigned Counter = 0;for (unsigned Bit : Filled.set_bits())EXPECT_EQ(Bit, Counter++);TypeParam Empty;EXPECT_EQ(Empty.set_bits_begin(), Empty.set_bits_end());int BitCount = 0;for (unsigned Bit : Empty.set_bits()) {(void)Bit;BitCount++;}ASSERT_EQ(BitCount, 0);TypeParam ToFill(100, false);ToFill.set(0);EXPECT_NE(ToFill.set_bits_begin(), ToFill.set_bits_end());EXPECT_EQ(++ToFill.set_bits_begin(), ToFill.set_bits_end());EXPECT_EQ(*ToFill.set_bits_begin(), 0U);ToFill.reset(0);EXPECT_EQ(ToFill.set_bits_begin(), ToFill.set_bits_end());const unsigned List[] = {1, 10, 25, 99};for (unsigned Num : List)ToFill.set(Num);unsigned i = 0;for (unsigned Bit : ToFill.set_bits())EXPECT_EQ(List[i++], Bit);}TYPED_TEST(BitVectorTest, PushBack) {TypeParam Vec(10, false);EXPECT_EQ(-1, Vec.find_first());EXPECT_EQ(10U, Vec.size());EXPECT_EQ(0U, Vec.count());EXPECT_EQ(false, Vec.back());Vec.push_back(true);EXPECT_EQ(10, Vec.find_first());EXPECT_EQ(11U, Vec.size());EXPECT_EQ(1U, Vec.count());EXPECT_EQ(true, Vec.back());Vec.push_back(false);EXPECT_EQ(10, Vec.find_first());EXPECT_EQ(12U, Vec.size());EXPECT_EQ(1U, Vec.count());EXPECT_EQ(false, Vec.back());Vec.push_back(true);EXPECT_EQ(10, Vec.find_first());EXPECT_EQ(13U, Vec.size());EXPECT_EQ(2U, Vec.count());EXPECT_EQ(true, Vec.back());// Add a lot of values to cause reallocation.for (int i = 0; i != 100; ++i) {Vec.push_back(true);Vec.push_back(false);}EXPECT_EQ(10, Vec.find_first());EXPECT_EQ(213U, Vec.size());EXPECT_EQ(102U, Vec.count());}TYPED_TEST(BitVectorTest, PopBack) {TypeParam Vec(10, true);EXPECT_EQ(10U, Vec.size());EXPECT_EQ(10U, Vec.count());EXPECT_EQ(true, Vec.back());Vec.pop_back();EXPECT_EQ(9U, Vec.size());EXPECT_EQ(9U, Vec.count());EXPECT_EQ(true, Vec.back());Vec.push_back(false);EXPECT_EQ(10U, Vec.size());EXPECT_EQ(9U, Vec.count());EXPECT_EQ(false, Vec.back());Vec.pop_back();EXPECT_EQ(9U, Vec.size());EXPECT_EQ(9U, Vec.count());EXPECT_EQ(true, Vec.back());}TYPED_TEST(BitVectorTest, DenseSet) {DenseSet<TypeParam> Set;TypeParam A(10, true);auto I = Set.insert(A);EXPECT_EQ(true, I.second);TypeParam B(5, true);I = Set.insert(B);EXPECT_EQ(true, I.second);TypeParam C(20, false);C.set(19);I = Set.insert(C);EXPECT_EQ(true, I.second);#if LLVM_ENABLE_ABI_BREAKING_CHECKSTypeParam D;EXPECT_DEATH(Set.insert(D),"Empty/Tombstone value shouldn't be inserted into map!");#endifEXPECT_EQ(3U, Set.size());EXPECT_EQ(1U, Set.count(A));EXPECT_EQ(1U, Set.count(B));EXPECT_EQ(1U, Set.count(C));EXPECT_EQ(true, Set.erase(B));EXPECT_EQ(2U, Set.size());EXPECT_EQ(true, Set.erase(C));EXPECT_EQ(1U, Set.size());EXPECT_EQ(true, Set.erase(A));EXPECT_EQ(0U, Set.size());}/// Test that capacity doesn't affect hashing.TYPED_TEST(BitVectorTest, DenseMapHashing) {using DMI = DenseMapInfo<TypeParam>;{TypeParam A;A.resize(200);A.set(100);TypeParam B;B.resize(200);B.set(100);B.reserve(1000);EXPECT_EQ(DMI::getHashValue(A), DMI::getHashValue(B));}{TypeParam A;A.resize(20);A.set(10);TypeParam B;B.resize(20);B.set(10);B.reserve(1000);EXPECT_EQ(DMI::getHashValue(A), DMI::getHashValue(B));}}TEST(BitVectoryTest, Apply) {for (int i = 0; i < 2; ++i) {int j = i * 100 + 3;const BitVector x =createBitVector<BitVector>(j + 5, {{i, i + 1}, {j - 1, j}});const BitVector y = createBitVector<BitVector>(j + 5, {{i, j}});const BitVector z =createBitVector<BitVector>(j + 5, {{i + 1, i + 2}, {j, j + 1}});auto op0 = [](auto x) { return ~x; };BitVector expected0 = x;expected0.flip();BitVector out0(j - 2);BitVector::apply(op0, out0, x);EXPECT_EQ(out0, expected0);auto op1 = [](auto x, auto y) { return x & ~y; };BitVector expected1 = x;expected1.reset(y);BitVector out1;BitVector::apply(op1, out1, x, y);EXPECT_EQ(out1, expected1);auto op2 = [](auto x, auto y, auto z) { return (x ^ ~y) | z; };BitVector expected2 = y;expected2.flip();expected2 ^= x;expected2 |= z;BitVector out2(j + 5);BitVector::apply(op2, out2, x, y, z);EXPECT_EQ(out2, expected2);}}} // namespace
//===- llvm/unittests/ADT/BitFieldsTest.cpp - BitFields unit tests --------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Bitfields.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(BitfieldsTest, Example) {uint8_t Storage = 0;// Store and retrieve a single bit as bool.using Bool = Bitfield::Element<bool, 0, 1>;Bitfield::set<Bool>(Storage, true);EXPECT_EQ(Storage, 0b00000001);// ^EXPECT_EQ(Bitfield::get<Bool>(Storage), true);// Store and retrieve a 2 bit typed enum.// Note: enum underlying type must be unsigned.enum class SuitEnum : uint8_t { CLUBS, DIAMONDS, HEARTS, SPADES };// Note: enum maximum value needs to be passed in as last parameter.using Suit = Bitfield::Element<SuitEnum, 1, 2, SuitEnum::SPADES>;Bitfield::set<Suit>(Storage, SuitEnum::HEARTS);EXPECT_EQ(Storage, 0b00000101);// ^^EXPECT_EQ(Bitfield::get<Suit>(Storage), SuitEnum::HEARTS);// Store and retrieve a 5 bit value as unsigned.using Value = Bitfield::Element<unsigned, 3, 5>;Bitfield::set<Value>(Storage, 10);EXPECT_EQ(Storage, 0b01010101);// ^^^^^EXPECT_EQ(Bitfield::get<Value>(Storage), 10U);// Interpret the same 5 bit value as signed.using SignedValue = Bitfield::Element<int, 3, 5>;Bitfield::set<SignedValue>(Storage, -2);EXPECT_EQ(Storage, 0b11110101);// ^^^^^EXPECT_EQ(Bitfield::get<SignedValue>(Storage), -2);// Ability to efficiently test if a field is non zero.EXPECT_TRUE(Bitfield::test<Value>(Storage));// Alter Storage changes value.Storage = 0;EXPECT_EQ(Bitfield::get<Bool>(Storage), false);EXPECT_EQ(Bitfield::get<Suit>(Storage), SuitEnum::CLUBS);EXPECT_EQ(Bitfield::get<Value>(Storage), 0U);EXPECT_EQ(Bitfield::get<SignedValue>(Storage), 0);Storage = 255;EXPECT_EQ(Bitfield::get<Bool>(Storage), true);EXPECT_EQ(Bitfield::get<Suit>(Storage), SuitEnum::SPADES);EXPECT_EQ(Bitfield::get<Value>(Storage), 31U);EXPECT_EQ(Bitfield::get<SignedValue>(Storage), -1);}TEST(BitfieldsTest, FirstBit) {uint8_t Storage = 0;using FirstBit = Bitfield::Element<bool, 0, 1>;// Set trueBitfield::set<FirstBit>(Storage, true);EXPECT_EQ(Bitfield::get<FirstBit>(Storage), true);EXPECT_EQ(Storage, 0x1ULL);// Set falseBitfield::set<FirstBit>(Storage, false);EXPECT_EQ(Bitfield::get<FirstBit>(Storage), false);EXPECT_EQ(Storage, 0x0ULL);}TEST(BitfieldsTest, SecondBit) {uint8_t Storage = 0;using SecondBit = Bitfield::Element<bool, 1, 1>;// Set trueBitfield::set<SecondBit>(Storage, true);EXPECT_EQ(Bitfield::get<SecondBit>(Storage), true);EXPECT_EQ(Storage, 0x2ULL);// Set falseBitfield::set<SecondBit>(Storage, false);EXPECT_EQ(Bitfield::get<SecondBit>(Storage), false);EXPECT_EQ(Storage, 0x0ULL);}TEST(BitfieldsTest, LastBit) {uint8_t Storage = 0;using LastBit = Bitfield::Element<bool, 7, 1>;// Set trueBitfield::set<LastBit>(Storage, true);EXPECT_EQ(Bitfield::get<LastBit>(Storage), true);EXPECT_EQ(Storage, 0x80ULL);// Set falseBitfield::set<LastBit>(Storage, false);EXPECT_EQ(Bitfield::get<LastBit>(Storage), false);EXPECT_EQ(Storage, 0x0ULL);}TEST(BitfieldsTest, LastBitUint64) {uint64_t Storage = 0;using LastBit = Bitfield::Element<bool, 63, 1>;// Set trueBitfield::set<LastBit>(Storage, true);EXPECT_EQ(Bitfield::get<LastBit>(Storage), true);EXPECT_EQ(Storage, 0x8000000000000000ULL);// Set falseBitfield::set<LastBit>(Storage, false);EXPECT_EQ(Bitfield::get<LastBit>(Storage), false);EXPECT_EQ(Storage, 0x0ULL);}TEST(BitfieldsTest, Enum) {enum Enum : unsigned { Zero = 0, Two = 2, LAST = Two };uint8_t Storage = 0;using OrderingField = Bitfield::Element<Enum, 1, 2, LAST>;EXPECT_EQ(Bitfield::get<OrderingField>(Storage), Zero);Bitfield::set<OrderingField>(Storage, Two);EXPECT_EQ(Bitfield::get<OrderingField>(Storage), Two);EXPECT_EQ(Storage, 0b00000100);// value 2 in ^^}TEST(BitfieldsTest, EnumClass) {enum class Enum : unsigned { Zero = 0, Two = 2, LAST = Two };uint8_t Storage = 0;using OrderingField = Bitfield::Element<Enum, 1, 2, Enum::LAST>;EXPECT_EQ(Bitfield::get<OrderingField>(Storage), Enum::Zero);Bitfield::set<OrderingField>(Storage, Enum::Two);EXPECT_EQ(Bitfield::get<OrderingField>(Storage), Enum::Two);EXPECT_EQ(Storage, 0b00000100);// value 2 in ^^}TEST(BitfieldsTest, OneBitSigned) {uint8_t Storage = 0;using SignedField = Bitfield::Element<int, 1, 1>;EXPECT_EQ(Bitfield::get<SignedField>(Storage), 0);EXPECT_EQ(Storage, 0b00000000);// value 0 in ^Bitfield::set<SignedField>(Storage, -1);EXPECT_EQ(Bitfield::get<SignedField>(Storage), -1);EXPECT_EQ(Storage, 0b00000010);// value 1 in ^}TEST(BitfieldsTest, TwoBitSigned) {uint8_t Storage = 0;using SignedField = Bitfield::Element<int, 1, 2>;EXPECT_EQ(Bitfield::get<SignedField>(Storage), 0);EXPECT_EQ(Storage, 0b00000000);// value 0 in ^^Bitfield::set<SignedField>(Storage, 1);EXPECT_EQ(Bitfield::get<SignedField>(Storage), 1);EXPECT_EQ(Storage, 0b00000010);// value 1 in ^^Bitfield::set<SignedField>(Storage, -1);EXPECT_EQ(Bitfield::get<SignedField>(Storage), -1);EXPECT_EQ(Storage, 0b00000110);// value -1 in ^^Bitfield::set<SignedField>(Storage, -2);EXPECT_EQ(Bitfield::get<SignedField>(Storage), -2);EXPECT_EQ(Storage, 0b00000100);// value -2 in ^^}TEST(BitfieldsTest, isOverlapping) {// 01234567// A: --------// B: ---// C: ---// D: ---using A = Bitfield::Element<unsigned, 0, 8>;using B = Bitfield::Element<unsigned, 3, 3>;using C = Bitfield::Element<unsigned, 1, 3>;using D = Bitfield::Element<unsigned, 4, 3>;EXPECT_TRUE((Bitfield::isOverlapping<A, B>()));EXPECT_TRUE((Bitfield::isOverlapping<A, C>()));EXPECT_TRUE((Bitfield::isOverlapping<A, B>()));EXPECT_TRUE((Bitfield::isOverlapping<A, D>()));EXPECT_TRUE((Bitfield::isOverlapping<B, C>()));EXPECT_TRUE((Bitfield::isOverlapping<B, D>()));EXPECT_FALSE((Bitfield::isOverlapping<C, D>()));}TEST(BitfieldsTest, areContiguous) {using A = Bitfield::Element<unsigned, 0, 1>; // Next Bit:1using B = Bitfield::Element<unsigned, 1, 4>; // Next Bit:5using C = Bitfield::Element<unsigned, 5, 3>; // Next Bit:8EXPECT_TRUE((Bitfield::areContiguous<A, B>()));EXPECT_TRUE((Bitfield::areContiguous<A, B, C>()));EXPECT_FALSE((Bitfield::areContiguous<A, C>()));EXPECT_FALSE((Bitfield::areContiguous<A, A>()));EXPECT_FALSE((Bitfield::areContiguous<B, A>()));}TEST(BitfieldsTest, FullUint64) {uint64_t Storage = 0;using Value = Bitfield::Element<uint64_t, 0, 64>;Bitfield::set<Value>(Storage, -1ULL);EXPECT_EQ(Bitfield::get<Value>(Storage), -1ULL);Bitfield::set<Value>(Storage, 0ULL);EXPECT_EQ(Bitfield::get<Value>(Storage), 0ULL);}TEST(BitfieldsTest, FullInt64) {uint64_t Storage = 0;using Value = Bitfield::Element<int64_t, 0, 64>;Bitfield::set<Value>(Storage, -1);EXPECT_EQ(Bitfield::get<Value>(Storage), -1);Bitfield::set<Value>(Storage, 0);EXPECT_EQ(Bitfield::get<Value>(Storage), 0);}#ifdef EXPECT_DEBUG_DEATHTEST(BitfieldsTest, ValueTooBigBool) {uint64_t Storage = 0;using A = Bitfield::Element<unsigned, 0, 1>;Bitfield::set<A>(Storage, true);Bitfield::set<A>(Storage, false);EXPECT_DEBUG_DEATH(Bitfield::set<A>(Storage, 2), "value is too big");}TEST(BitfieldsTest, ValueTooBigInt) {uint64_t Storage = 0;using A = Bitfield::Element<unsigned, 0, 2>;Bitfield::set<A>(Storage, 3);EXPECT_DEBUG_DEATH(Bitfield::set<A>(Storage, 4), "value is too big");EXPECT_DEBUG_DEATH(Bitfield::set<A>(Storage, -1), "value is too big");}TEST(BitfieldsTest, ValueTooBigBounded) {uint8_t Storage = 0;using A = Bitfield::Element<int, 1, 2>;Bitfield::set<A>(Storage, 1);Bitfield::set<A>(Storage, 0);Bitfield::set<A>(Storage, -1);Bitfield::set<A>(Storage, -2);EXPECT_DEBUG_DEATH(Bitfield::set<A>(Storage, 2), "value is too big");EXPECT_DEBUG_DEATH(Bitfield::set<A>(Storage, -3), "value is too small");}#endif} // namespace
//===- llvm/unittest/ADT/ArrayRefTest.cpp - ArrayRef unit tests -----------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/ArrayRef.h"#include "llvm/Support/Allocator.h"#include "llvm/Support/raw_ostream.h"#include "gtest/gtest.h"#include <limits>#include <vector>using namespace llvm;// Check that the ArrayRef-of-pointer converting constructor only allows adding// cv qualifiers (not removing them, or otherwise changing the type)static_assert(std::is_convertible<ArrayRef<int *>, ArrayRef<const int *>>::value,"Adding const");static_assert(std::is_convertible<ArrayRef<int *>, ArrayRef<volatile int *>>::value,"Adding volatile");static_assert(!std::is_convertible<ArrayRef<int *>, ArrayRef<float *>>::value,"Changing pointer of one type to a pointer of another");static_assert(!std::is_convertible<ArrayRef<const int *>, ArrayRef<int *>>::value,"Removing const");static_assert(!std::is_convertible<ArrayRef<volatile int *>, ArrayRef<int *>>::value,"Removing volatile");// Check that we can't accidentally assign a temporary location to an ArrayRef.// (Unfortunately we can't make use of the same thing with constructors.)static_assert(!std::is_assignable<ArrayRef<int *>&, int *>::value,"Assigning from single prvalue element");static_assert(!std::is_assignable<ArrayRef<int *>&, int * &&>::value,"Assigning from single xvalue element");static_assert(std::is_assignable<ArrayRef<int *>&, int * &>::value,"Assigning from single lvalue element");static_assert(!std::is_assignable<ArrayRef<int *>&, std::initializer_list<int *>>::value,"Assigning from an initializer list");namespace {TEST(ArrayRefTest, AllocatorCopy) {BumpPtrAllocator Alloc;static const uint16_t Words1[] = { 1, 4, 200, 37 };ArrayRef<uint16_t> Array1 = makeArrayRef(Words1, 4);static const uint16_t Words2[] = { 11, 4003, 67, 64000, 13 };ArrayRef<uint16_t> Array2 = makeArrayRef(Words2, 5);ArrayRef<uint16_t> Array1c = Array1.copy(Alloc);ArrayRef<uint16_t> Array2c = Array2.copy(Alloc);EXPECT_TRUE(Array1.equals(Array1c));EXPECT_NE(Array1.data(), Array1c.data());EXPECT_TRUE(Array2.equals(Array2c));EXPECT_NE(Array2.data(), Array2c.data());// Check that copy can cope with uninitialized memory.struct NonAssignable {const char *Ptr;NonAssignable(const char *Ptr) : Ptr(Ptr) {}NonAssignable(const NonAssignable &RHS) = default;void operator=(const NonAssignable &RHS) { assert(RHS.Ptr != nullptr); }bool operator==(const NonAssignable &RHS) const { return Ptr == RHS.Ptr; }} Array3Src[] = {"hello", "world"};ArrayRef<NonAssignable> Array3Copy = makeArrayRef(Array3Src).copy(Alloc);EXPECT_EQ(makeArrayRef(Array3Src), Array3Copy);EXPECT_NE(makeArrayRef(Array3Src).data(), Array3Copy.data());}// This test is pure UB given the ArrayRef<> implementation.// You are not allowed to produce non-null pointers given null base pointer.TEST(ArrayRefTest, DISABLED_SizeTSizedOperations) {ArrayRef<char> AR(nullptr, std::numeric_limits<ptrdiff_t>::max());// Check that drop_back accepts size_t-sized numbers.EXPECT_EQ(1U, AR.drop_back(AR.size() - 1).size());// Check that drop_front accepts size_t-sized numbers.EXPECT_EQ(1U, AR.drop_front(AR.size() - 1).size());// Check that slice accepts size_t-sized numbers.EXPECT_EQ(1U, AR.slice(AR.size() - 1).size());EXPECT_EQ(AR.size() - 1, AR.slice(1, AR.size() - 1).size());}TEST(ArrayRefTest, DropBack) {static const int TheNumbers[] = {4, 8, 15, 16, 23, 42};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> AR2(TheNumbers, AR1.size() - 1);EXPECT_TRUE(AR1.drop_back().equals(AR2));}TEST(ArrayRefTest, DropFront) {static const int TheNumbers[] = {4, 8, 15, 16, 23, 42};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> AR2(&TheNumbers[2], AR1.size() - 2);EXPECT_TRUE(AR1.drop_front(2).equals(AR2));}TEST(ArrayRefTest, DropWhile) {static const int TheNumbers[] = {1, 3, 5, 8, 10, 11};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> Expected = AR1.drop_front(3);EXPECT_EQ(Expected, AR1.drop_while([](const int &N) { return N % 2 == 1; }));EXPECT_EQ(AR1, AR1.drop_while([](const int &N) { return N < 0; }));EXPECT_EQ(ArrayRef<int>(),AR1.drop_while([](const int &N) { return N > 0; }));}TEST(ArrayRefTest, DropUntil) {static const int TheNumbers[] = {1, 3, 5, 8, 10, 11};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> Expected = AR1.drop_front(3);EXPECT_EQ(Expected, AR1.drop_until([](const int &N) { return N % 2 == 0; }));EXPECT_EQ(ArrayRef<int>(),AR1.drop_until([](const int &N) { return N < 0; }));EXPECT_EQ(AR1, AR1.drop_until([](const int &N) { return N > 0; }));}TEST(ArrayRefTest, TakeBack) {static const int TheNumbers[] = {4, 8, 15, 16, 23, 42};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> AR2(AR1.end() - 1, 1);EXPECT_TRUE(AR1.take_back().equals(AR2));}TEST(ArrayRefTest, TakeFront) {static const int TheNumbers[] = {4, 8, 15, 16, 23, 42};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> AR2(AR1.data(), 2);EXPECT_TRUE(AR1.take_front(2).equals(AR2));}TEST(ArrayRefTest, TakeWhile) {static const int TheNumbers[] = {1, 3, 5, 8, 10, 11};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> Expected = AR1.take_front(3);EXPECT_EQ(Expected, AR1.take_while([](const int &N) { return N % 2 == 1; }));EXPECT_EQ(ArrayRef<int>(),AR1.take_while([](const int &N) { return N < 0; }));EXPECT_EQ(AR1, AR1.take_while([](const int &N) { return N > 0; }));}TEST(ArrayRefTest, TakeUntil) {static const int TheNumbers[] = {1, 3, 5, 8, 10, 11};ArrayRef<int> AR1(TheNumbers);ArrayRef<int> Expected = AR1.take_front(3);EXPECT_EQ(Expected, AR1.take_until([](const int &N) { return N % 2 == 0; }));EXPECT_EQ(AR1, AR1.take_until([](const int &N) { return N < 0; }));EXPECT_EQ(ArrayRef<int>(),AR1.take_until([](const int &N) { return N > 0; }));}TEST(ArrayRefTest, Equals) {static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8};ArrayRef<int> AR1(A1);EXPECT_TRUE(AR1.equals({1, 2, 3, 4, 5, 6, 7, 8}));EXPECT_FALSE(AR1.equals({8, 1, 2, 4, 5, 6, 6, 7}));EXPECT_FALSE(AR1.equals({2, 4, 5, 6, 6, 7, 8, 1}));EXPECT_FALSE(AR1.equals({0, 1, 2, 4, 5, 6, 6, 7}));EXPECT_FALSE(AR1.equals({1, 2, 42, 4, 5, 6, 7, 8}));EXPECT_FALSE(AR1.equals({42, 2, 3, 4, 5, 6, 7, 8}));EXPECT_FALSE(AR1.equals({1, 2, 3, 4, 5, 6, 7, 42}));EXPECT_FALSE(AR1.equals({1, 2, 3, 4, 5, 6, 7}));EXPECT_FALSE(AR1.equals({1, 2, 3, 4, 5, 6, 7, 8, 9}));ArrayRef<int> AR1a = AR1.drop_back();EXPECT_TRUE(AR1a.equals({1, 2, 3, 4, 5, 6, 7}));EXPECT_FALSE(AR1a.equals({1, 2, 3, 4, 5, 6, 7, 8}));ArrayRef<int> AR1b = AR1a.slice(2, 4);EXPECT_TRUE(AR1b.equals({3, 4, 5, 6}));EXPECT_FALSE(AR1b.equals({2, 3, 4, 5, 6}));EXPECT_FALSE(AR1b.equals({3, 4, 5, 6, 7}));}TEST(ArrayRefTest, EmptyEquals) {EXPECT_TRUE(ArrayRef<unsigned>() == ArrayRef<unsigned>());}TEST(ArrayRefTest, ConstConvert) {int buf[4];for (int i = 0; i < 4; ++i)buf[i] = i;static int *A[] = {&buf[0], &buf[1], &buf[2], &buf[3]};ArrayRef<const int *> a((ArrayRef<int *>(A)));a = ArrayRef<int *>(A);}static std::vector<int> ReturnTest12() { return {1, 2}; }static void ArgTest12(ArrayRef<int> A) {EXPECT_EQ(2U, A.size());EXPECT_EQ(1, A[0]);EXPECT_EQ(2, A[1]);}TEST(ArrayRefTest, InitializerList) {std::initializer_list<int> init_list = { 0, 1, 2, 3, 4 };ArrayRef<int> A = init_list;for (int i = 0; i < 5; ++i)EXPECT_EQ(i, A[i]);std::vector<int> B = ReturnTest12();A = B;EXPECT_EQ(1, A[0]);EXPECT_EQ(2, A[1]);ArgTest12({1, 2});}TEST(ArrayRefTest, EmptyInitializerList) {ArrayRef<int> A = {};EXPECT_TRUE(A.empty());A = {};EXPECT_TRUE(A.empty());}TEST(ArrayRefTest, makeArrayRef) {static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8};// No copy expected for non-const ArrayRef (true no-op)ArrayRef<int> AR1(A1);ArrayRef<int> &AR1Ref = makeArrayRef(AR1);EXPECT_EQ(&AR1, &AR1Ref);// A copy is expected for non-const ArrayRef (thin copy)const ArrayRef<int> AR2(A1);const ArrayRef<int> &AR2Ref = makeArrayRef(AR2);EXPECT_NE(&AR2Ref, &AR2);EXPECT_TRUE(AR2.equals(AR2Ref));}TEST(ArrayRefTest, OwningArrayRef) {static const int A1[] = {0, 1};OwningArrayRef<int> A(makeArrayRef(A1));OwningArrayRef<int> B(std::move(A));EXPECT_EQ(A.data(), nullptr);}TEST(ArrayRefTest, makeArrayRefFromStdArray) {std::array<int, 5> A1{{42, -5, 0, 1000000, -1000000}};ArrayRef<int> A2 = makeArrayRef(A1);EXPECT_EQ(A1.size(), A2.size());for (std::size_t i = 0; i < A1.size(); ++i) {EXPECT_EQ(A1[i], A2[i]);}}static_assert(std::is_trivially_copyable<ArrayRef<int>>::value,"trivially copyable");TEST(ArrayRefTest, makeMutableArrayRef) {int A = 0;auto AR = makeMutableArrayRef(A);EXPECT_EQ(AR.data(), &A);EXPECT_EQ(AR.size(), (size_t)1);AR[0] = 1;EXPECT_EQ(A, 1);int B[] = {0, 1, 2, 3};auto BR1 = makeMutableArrayRef(&B[0], 4);auto BR2 = makeMutableArrayRef(B);EXPECT_EQ(BR1.data(), &B[0]);EXPECT_EQ(BR1.size(), (size_t)4);EXPECT_EQ(BR2.data(), &B[0]);EXPECT_EQ(BR2.size(), (size_t)4);SmallVector<int> C1;SmallVectorImpl<int> &C2 = C1;C1.resize(5);auto CR1 = makeMutableArrayRef(C1);auto CR2 = makeMutableArrayRef(C2);EXPECT_EQ(CR1.data(), C1.data());EXPECT_EQ(CR1.size(), C1.size());EXPECT_EQ(CR2.data(), C2.data());EXPECT_EQ(CR2.size(), C2.size());std::vector<int> D;D.resize(5);auto DR = makeMutableArrayRef(D);EXPECT_EQ(DR.data(), D.data());EXPECT_EQ(DR.size(), D.size());std::array<int, 5> E;auto ER = makeMutableArrayRef(E);EXPECT_EQ(ER.data(), E.data());EXPECT_EQ(ER.size(), E.size());}} // end anonymous namespace
//===- llvm/unittest/Support/AnyTest.cpp - Any tests ---===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/Any.h"#include "gtest/gtest.h"#include <cstdlib>using namespace llvm;namespace {// Make sure we can construct, copy-construct, move-construct, and assign Any's.TEST(AnyTest, ConstructionAndAssignment) {llvm::Any A;llvm::Any B{7};llvm::Any C{8};llvm::Any D{"hello"};llvm::Any E{3.7};// An empty Any is not anything.EXPECT_FALSE(A.hasValue());EXPECT_FALSE(any_isa<int>(A));// An int is an int but not something else.EXPECT_TRUE(B.hasValue());EXPECT_TRUE(any_isa<int>(B));EXPECT_FALSE(any_isa<float>(B));EXPECT_TRUE(C.hasValue());EXPECT_TRUE(any_isa<int>(C));// A const char * is a const char * but not an int.EXPECT_TRUE(D.hasValue());EXPECT_TRUE(any_isa<const char *>(D));EXPECT_FALSE(any_isa<int>(D));// A double is a double but not a float.EXPECT_TRUE(E.hasValue());EXPECT_TRUE(any_isa<double>(E));EXPECT_FALSE(any_isa<float>(E));// After copy constructing from an int, the new item and old item are both// ints.llvm::Any F(B);EXPECT_TRUE(B.hasValue());EXPECT_TRUE(F.hasValue());EXPECT_TRUE(any_isa<int>(F));EXPECT_TRUE(any_isa<int>(B));// After move constructing from an int, the new item is an int and the old one// isn't.llvm::Any G(std::move(C));EXPECT_FALSE(C.hasValue());EXPECT_TRUE(G.hasValue());EXPECT_TRUE(any_isa<int>(G));EXPECT_FALSE(any_isa<int>(C));// After copy-assigning from an int, the new item and old item are both ints.A = F;EXPECT_TRUE(A.hasValue());EXPECT_TRUE(F.hasValue());EXPECT_TRUE(any_isa<int>(A));EXPECT_TRUE(any_isa<int>(F));// After move-assigning from an int, the new item and old item are both ints.B = std::move(G);EXPECT_TRUE(B.hasValue());EXPECT_FALSE(G.hasValue());EXPECT_TRUE(any_isa<int>(B));EXPECT_FALSE(any_isa<int>(G));}TEST(AnyTest, GoodAnyCast) {llvm::Any A;llvm::Any B{7};llvm::Any C{8};llvm::Any D{"hello"};llvm::Any E{'x'};// Check each value twice to make sure it isn't damaged by the cast.EXPECT_EQ(7, llvm::any_cast<int>(B));EXPECT_EQ(7, llvm::any_cast<int>(B));EXPECT_STREQ("hello", llvm::any_cast<const char *>(D));EXPECT_STREQ("hello", llvm::any_cast<const char *>(D));EXPECT_EQ('x', llvm::any_cast<char>(E));EXPECT_EQ('x', llvm::any_cast<char>(E));llvm::Any F(B);EXPECT_EQ(7, llvm::any_cast<int>(F));EXPECT_EQ(7, llvm::any_cast<int>(F));llvm::Any G(std::move(C));EXPECT_EQ(8, llvm::any_cast<int>(G));EXPECT_EQ(8, llvm::any_cast<int>(G));A = F;EXPECT_EQ(7, llvm::any_cast<int>(A));EXPECT_EQ(7, llvm::any_cast<int>(A));E = std::move(G);EXPECT_EQ(8, llvm::any_cast<int>(E));EXPECT_EQ(8, llvm::any_cast<int>(E));// Make sure we can any_cast from an rvalue and that it's properly destroyed// in the process.EXPECT_EQ(8, llvm::any_cast<int>(std::move(E)));EXPECT_TRUE(E.hasValue());// Make sure moving from pointers gives back pointers, and that we can modify// the underlying value through those pointers.EXPECT_EQ(7, *llvm::any_cast<int>(&A));int *N = llvm::any_cast<int>(&A);*N = 42;EXPECT_EQ(42, llvm::any_cast<int>(A));// Make sure that we can any_cast to a reference and this is considered a good// cast, resulting in an lvalue which can be modified.llvm::any_cast<int &>(A) = 43;EXPECT_EQ(43, llvm::any_cast<int>(A));}TEST(AnyTest, CopiesAndMoves) {struct TestType {TestType() = default;TestType(const TestType &Other): Copies(Other.Copies + 1), Moves(Other.Moves) {}TestType(TestType &&Other) : Copies(Other.Copies), Moves(Other.Moves + 1) {}int Copies = 0;int Moves = 0;};// One move to get TestType into the Any, and one move on the cast.TestType T1 = llvm::any_cast<TestType>(Any{TestType()});EXPECT_EQ(0, T1.Copies);EXPECT_EQ(2, T1.Moves);// One move to get TestType into the Any, and one copy on the cast.Any A{TestType()};TestType T2 = llvm::any_cast<TestType>(A);EXPECT_EQ(1, T2.Copies);EXPECT_EQ(1, T2.Moves);// One move to get TestType into the Any, and one move on the cast.TestType T3 = llvm::any_cast<TestType>(std::move(A));EXPECT_EQ(0, T3.Copies);EXPECT_EQ(2, T3.Moves);}TEST(AnyTest, BadAnyCast) {llvm::Any A;llvm::Any B{7};llvm::Any C{"hello"};llvm::Any D{'x'};#if !defined(NDEBUG) && GTEST_HAS_DEATH_TESTEXPECT_DEATH(llvm::any_cast<int>(A), "");EXPECT_DEATH(llvm::any_cast<float>(B), "");EXPECT_DEATH(llvm::any_cast<int *>(B), "");EXPECT_DEATH(llvm::any_cast<std::string>(C), "");EXPECT_DEATH(llvm::any_cast<unsigned char>(D), "");#endif}} // anonymous namespace
//===- llvm/unittest/ADT/APSIntTest.cpp - APSInt unit tests ---------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/APSInt.h"#include "gtest/gtest.h"using namespace llvm;namespace {TEST(APSIntTest, MoveTest) {APSInt A(32, true);EXPECT_TRUE(A.isUnsigned());APSInt B(128, false);A = B;EXPECT_FALSE(A.isUnsigned());APSInt C(B);EXPECT_FALSE(C.isUnsigned());APInt Wide(256, 0);const uint64_t *Bits = Wide.getRawData();APSInt D(std::move(Wide));EXPECT_TRUE(D.isUnsigned());EXPECT_EQ(Bits, D.getRawData()); // Verify that "Wide" was really moved.A = APSInt(64, true);EXPECT_TRUE(A.isUnsigned());Wide = APInt(128, 1);Bits = Wide.getRawData();A = std::move(Wide);EXPECT_TRUE(A.isUnsigned());EXPECT_EQ(Bits, A.getRawData()); // Verify that "Wide" was really moved.}TEST(APSIntTest, get) {EXPECT_TRUE(APSInt::get(7).isSigned());EXPECT_EQ(64u, APSInt::get(7).getBitWidth());EXPECT_EQ(7u, APSInt::get(7).getZExtValue());EXPECT_EQ(7, APSInt::get(7).getSExtValue());EXPECT_TRUE(APSInt::get(-7).isSigned());EXPECT_EQ(64u, APSInt::get(-7).getBitWidth());EXPECT_EQ(-7, APSInt::get(-7).getSExtValue());EXPECT_EQ(UINT64_C(0) - 7, APSInt::get(-7).getZExtValue());}TEST(APSIntTest, getUnsigned) {EXPECT_TRUE(APSInt::getUnsigned(7).isUnsigned());EXPECT_EQ(64u, APSInt::getUnsigned(7).getBitWidth());EXPECT_EQ(7u, APSInt::getUnsigned(7).getZExtValue());EXPECT_EQ(7, APSInt::getUnsigned(7).getSExtValue());EXPECT_TRUE(APSInt::getUnsigned(-7).isUnsigned());EXPECT_EQ(64u, APSInt::getUnsigned(-7).getBitWidth());EXPECT_EQ(-7, APSInt::getUnsigned(-7).getSExtValue());EXPECT_EQ(UINT64_C(0) - 7, APSInt::getUnsigned(-7).getZExtValue());}TEST(APSIntTest, getExtValue) {EXPECT_TRUE(APSInt(APInt(3, 7), true).isUnsigned());EXPECT_TRUE(APSInt(APInt(3, 7), false).isSigned());EXPECT_TRUE(APSInt(APInt(4, 7), true).isUnsigned());EXPECT_TRUE(APSInt(APInt(4, 7), false).isSigned());EXPECT_TRUE(APSInt(APInt(4, -7), true).isUnsigned());EXPECT_TRUE(APSInt(APInt(4, -7), false).isSigned());EXPECT_EQ(7, APSInt(APInt(3, 7), true).getExtValue());EXPECT_EQ(-1, APSInt(APInt(3, 7), false).getExtValue());EXPECT_EQ(7, APSInt(APInt(4, 7), true).getExtValue());EXPECT_EQ(7, APSInt(APInt(4, 7), false).getExtValue());EXPECT_EQ(9, APSInt(APInt(4, -7), true).getExtValue());EXPECT_EQ(-7, APSInt(APInt(4, -7), false).getExtValue());}TEST(APSIntTest, compareValues) {auto U = [](uint64_t V) { return APSInt::getUnsigned(V); };auto S = [](int64_t V) { return APSInt::get(V); };// Bit-width matches and is-signed.EXPECT_TRUE(APSInt::compareValues(S(7), S(8)) < 0);EXPECT_TRUE(APSInt::compareValues(S(8), S(7)) > 0);EXPECT_TRUE(APSInt::compareValues(S(7), S(7)) == 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(8)) < 0);EXPECT_TRUE(APSInt::compareValues(S(8), S(-7)) > 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(-7)) == 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(-8)) > 0);EXPECT_TRUE(APSInt::compareValues(S(-8), S(-7)) < 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(-7)) == 0);// Bit-width matches and not is-signed.EXPECT_TRUE(APSInt::compareValues(U(7), U(8)) < 0);EXPECT_TRUE(APSInt::compareValues(U(8), U(7)) > 0);EXPECT_TRUE(APSInt::compareValues(U(7), U(7)) == 0);// Bit-width matches and mixed signs.EXPECT_TRUE(APSInt::compareValues(U(7), S(8)) < 0);EXPECT_TRUE(APSInt::compareValues(U(8), S(7)) > 0);EXPECT_TRUE(APSInt::compareValues(U(7), S(7)) == 0);EXPECT_TRUE(APSInt::compareValues(U(8), S(-7)) > 0);// Bit-width mismatch and is-signed.EXPECT_TRUE(APSInt::compareValues(S(7).trunc(32), S(8)) < 0);EXPECT_TRUE(APSInt::compareValues(S(8).trunc(32), S(7)) > 0);EXPECT_TRUE(APSInt::compareValues(S(7).trunc(32), S(7)) == 0);EXPECT_TRUE(APSInt::compareValues(S(-7).trunc(32), S(8)) < 0);EXPECT_TRUE(APSInt::compareValues(S(8).trunc(32), S(-7)) > 0);EXPECT_TRUE(APSInt::compareValues(S(-7).trunc(32), S(-7)) == 0);EXPECT_TRUE(APSInt::compareValues(S(-7).trunc(32), S(-8)) > 0);EXPECT_TRUE(APSInt::compareValues(S(-8).trunc(32), S(-7)) < 0);EXPECT_TRUE(APSInt::compareValues(S(-7).trunc(32), S(-7)) == 0);EXPECT_TRUE(APSInt::compareValues(S(7), S(8).trunc(32)) < 0);EXPECT_TRUE(APSInt::compareValues(S(8), S(7).trunc(32)) > 0);EXPECT_TRUE(APSInt::compareValues(S(7), S(7).trunc(32)) == 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(8).trunc(32)) < 0);EXPECT_TRUE(APSInt::compareValues(S(8), S(-7).trunc(32)) > 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(-7).trunc(32)) == 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(-8).trunc(32)) > 0);EXPECT_TRUE(APSInt::compareValues(S(-8), S(-7).trunc(32)) < 0);EXPECT_TRUE(APSInt::compareValues(S(-7), S(-7).trunc(32)) == 0);// Bit-width mismatch and not is-signed.EXPECT_TRUE(APSInt::compareValues(U(7), U(8).trunc(32)) < 0);EXPECT_TRUE(APSInt::compareValues(U(8), U(7).trunc(32)) > 0);EXPECT_TRUE(APSInt::compareValues(U(7), U(7).trunc(32)) == 0);EXPECT_TRUE(APSInt::compareValues(U(7).trunc(32), U(8)) < 0);EXPECT_TRUE(APSInt::compareValues(U(8).trunc(32), U(7)) > 0);EXPECT_TRUE(APSInt::compareValues(U(7).trunc(32), U(7)) == 0);// Bit-width mismatch and mixed signs.EXPECT_TRUE(APSInt::compareValues(U(7).trunc(32), S(8)) < 0);EXPECT_TRUE(APSInt::compareValues(U(8).trunc(32), S(7)) > 0);EXPECT_TRUE(APSInt::compareValues(U(7).trunc(32), S(7)) == 0);EXPECT_TRUE(APSInt::compareValues(U(8).trunc(32), S(-7)) > 0);EXPECT_TRUE(APSInt::compareValues(U(7), S(8).trunc(32)) < 0);EXPECT_TRUE(APSInt::compareValues(U(8), S(7).trunc(32)) > 0);EXPECT_TRUE(APSInt::compareValues(U(7), S(7).trunc(32)) == 0);EXPECT_TRUE(APSInt::compareValues(U(8), S(-7).trunc(32)) > 0);}TEST(APSIntTest, FromString) {EXPECT_EQ(APSInt("1").getExtValue(), 1);EXPECT_EQ(APSInt("-1").getExtValue(), -1);EXPECT_EQ(APSInt("0").getExtValue(), 0);EXPECT_EQ(APSInt("56789").getExtValue(), 56789);EXPECT_EQ(APSInt("-1234").getExtValue(), -1234);}TEST(APSIntTest, FromStringBitWidth) {EXPECT_EQ(APSInt("0").getBitWidth(), 1U);EXPECT_EQ(APSInt("000").getBitWidth(), 1U);EXPECT_EQ(APSInt("1").getBitWidth(), 1U);EXPECT_EQ(APSInt("2").getBitWidth(), 2U);EXPECT_EQ(APSInt("3").getBitWidth(), 2U);EXPECT_EQ(APSInt("003").getBitWidth(), 2U);EXPECT_EQ(APSInt("15").getBitWidth(), 4U);EXPECT_EQ(APSInt("16").getBitWidth(), 5U);EXPECT_EQ(APSInt("17").getBitWidth(), 5U);EXPECT_EQ(APSInt("-0").getBitWidth(), 1U);EXPECT_EQ(APSInt("-000").getBitWidth(), 1U);EXPECT_EQ(APSInt("-1").getBitWidth(), 1U);EXPECT_EQ(APSInt("-2").getBitWidth(), 2U);EXPECT_EQ(APSInt("-3").getBitWidth(), 3U);EXPECT_EQ(APSInt("-003").getBitWidth(), 3U);EXPECT_EQ(APSInt("-5").getBitWidth(), 4U);EXPECT_EQ(APSInt("-15").getBitWidth(), 5U);EXPECT_EQ(APSInt("-16").getBitWidth(), 5U);EXPECT_EQ(APSInt("-17").getBitWidth(), 6U);}#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)TEST(APSIntTest, StringDeath) {EXPECT_DEATH((void)APSInt(""), "Invalid string length");EXPECT_DEATH((void)APSInt("1a"), "Invalid character in digit string");}#endifTEST(APSIntTest, SignedHighBit) {APSInt False(APInt(1, 0), false);APSInt True(APInt(1, 1), false);APSInt CharMin(APInt(8, 0), false);APSInt CharSmall(APInt(8, 0x13), false);APSInt CharBoundaryUnder(APInt(8, 0x7f), false);APSInt CharBoundaryOver(APInt(8, 0x80), false);APSInt CharLarge(APInt(8, 0xd9), false);APSInt CharMax(APInt(8, 0xff), false);EXPECT_FALSE(False.isNegative());EXPECT_TRUE(False.isNonNegative());EXPECT_FALSE(False.isStrictlyPositive());EXPECT_TRUE(True.isNegative());EXPECT_FALSE(True.isNonNegative());EXPECT_FALSE(True.isStrictlyPositive());EXPECT_FALSE(CharMin.isNegative());EXPECT_TRUE(CharMin.isNonNegative());EXPECT_FALSE(CharMin.isStrictlyPositive());EXPECT_FALSE(CharSmall.isNegative());EXPECT_TRUE(CharSmall.isNonNegative());EXPECT_TRUE(CharSmall.isStrictlyPositive());EXPECT_FALSE(CharBoundaryUnder.isNegative());EXPECT_TRUE(CharBoundaryUnder.isNonNegative());EXPECT_TRUE(CharBoundaryUnder.isStrictlyPositive());EXPECT_TRUE(CharBoundaryOver.isNegative());EXPECT_FALSE(CharBoundaryOver.isNonNegative());EXPECT_FALSE(CharBoundaryOver.isStrictlyPositive());EXPECT_TRUE(CharLarge.isNegative());EXPECT_FALSE(CharLarge.isNonNegative());EXPECT_FALSE(CharLarge.isStrictlyPositive());EXPECT_TRUE(CharMax.isNegative());EXPECT_FALSE(CharMax.isNonNegative());EXPECT_FALSE(CharMax.isStrictlyPositive());}TEST(APSIntTest, UnsignedHighBit) {APSInt False(APInt(1, 0));APSInt True(APInt(1, 1));APSInt CharMin(APInt(8, 0));APSInt CharSmall(APInt(8, 0x13));APSInt CharBoundaryUnder(APInt(8, 0x7f));APSInt CharBoundaryOver(APInt(8, 0x80));APSInt CharLarge(APInt(8, 0xd9));APSInt CharMax(APInt(8, 0xff));EXPECT_FALSE(False.isNegative());EXPECT_TRUE(False.isNonNegative());EXPECT_FALSE(False.isStrictlyPositive());EXPECT_FALSE(True.isNegative());EXPECT_TRUE(True.isNonNegative());EXPECT_TRUE(True.isStrictlyPositive());EXPECT_FALSE(CharMin.isNegative());EXPECT_TRUE(CharMin.isNonNegative());EXPECT_FALSE(CharMin.isStrictlyPositive());EXPECT_FALSE(CharSmall.isNegative());EXPECT_TRUE(CharSmall.isNonNegative());EXPECT_TRUE(CharSmall.isStrictlyPositive());EXPECT_FALSE(CharBoundaryUnder.isNegative());EXPECT_TRUE(CharBoundaryUnder.isNonNegative());EXPECT_TRUE(CharBoundaryUnder.isStrictlyPositive());EXPECT_FALSE(CharBoundaryOver.isNegative());EXPECT_TRUE(CharBoundaryOver.isNonNegative());EXPECT_TRUE(CharBoundaryOver.isStrictlyPositive());EXPECT_FALSE(CharLarge.isNegative());EXPECT_TRUE(CharLarge.isNonNegative());EXPECT_TRUE(CharLarge.isStrictlyPositive());EXPECT_FALSE(CharMax.isNegative());EXPECT_TRUE(CharMax.isNonNegative());EXPECT_TRUE(CharMax.isStrictlyPositive());}} // end anonymous namespace
//===- llvm/unittest/ADT/APInt.cpp - APInt unit tests ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/APInt.h"#include "llvm/ADT/ArrayRef.h"#include "llvm/ADT/SmallString.h"#include "llvm/ADT/Twine.h"#include "gtest/gtest.h"#include <array>using namespace llvm;namespace {TEST(APIntTest, ValueInit) {APInt Zero = APInt();EXPECT_TRUE(!Zero);EXPECT_TRUE(!Zero.zext(64));EXPECT_TRUE(!Zero.sext(64));}// Test that APInt shift left works when bitwidth > 64 and shiftamt == 0TEST(APIntTest, ShiftLeftByZero) {APInt One = APInt::getZero(65) + 1;APInt Shl = One.shl(0);EXPECT_TRUE(Shl[0]);EXPECT_FALSE(Shl[1]);}TEST(APIntTest, i64_ArithmeticRightShiftNegative) {const APInt neg_one(64, static_cast<uint64_t>(-1), true);EXPECT_EQ(neg_one, neg_one.ashr(7));}TEST(APIntTest, i128_NegativeCount) {APInt Minus3(128, static_cast<uint64_t>(-3), true);EXPECT_EQ(126u, Minus3.countLeadingOnes());EXPECT_EQ(-3, Minus3.getSExtValue());APInt Minus1(128, static_cast<uint64_t>(-1), true);EXPECT_EQ(0u, Minus1.countLeadingZeros());EXPECT_EQ(128u, Minus1.countLeadingOnes());EXPECT_EQ(128u, Minus1.getActiveBits());EXPECT_EQ(0u, Minus1.countTrailingZeros());EXPECT_EQ(128u, Minus1.countTrailingOnes());EXPECT_EQ(128u, Minus1.countPopulation());EXPECT_EQ(-1, Minus1.getSExtValue());}TEST(APIntTest, i33_Count) {APInt i33minus2(33, static_cast<uint64_t>(-2), true);EXPECT_EQ(0u, i33minus2.countLeadingZeros());EXPECT_EQ(32u, i33minus2.countLeadingOnes());EXPECT_EQ(33u, i33minus2.getActiveBits());EXPECT_EQ(1u, i33minus2.countTrailingZeros());EXPECT_EQ(32u, i33minus2.countPopulation());EXPECT_EQ(-2, i33minus2.getSExtValue());EXPECT_EQ(((uint64_t)-2)&((1ull<<33) -1), i33minus2.getZExtValue());}TEST(APIntTest, i61_Count) {APInt i61(61, 1 << 15);EXPECT_EQ(45u, i61.countLeadingZeros());EXPECT_EQ(0u, i61.countLeadingOnes());EXPECT_EQ(16u, i61.getActiveBits());EXPECT_EQ(15u, i61.countTrailingZeros());EXPECT_EQ(1u, i61.countPopulation());EXPECT_EQ(static_cast<int64_t>(1 << 15), i61.getSExtValue());EXPECT_EQ(static_cast<uint64_t>(1 << 15), i61.getZExtValue());i61.setBits(8, 19);EXPECT_EQ(42u, i61.countLeadingZeros());EXPECT_EQ(0u, i61.countLeadingOnes());EXPECT_EQ(19u, i61.getActiveBits());EXPECT_EQ(8u, i61.countTrailingZeros());EXPECT_EQ(11u, i61.countPopulation());EXPECT_EQ(static_cast<int64_t>((1 << 19) - (1 << 8)), i61.getSExtValue());EXPECT_EQ(static_cast<uint64_t>((1 << 19) - (1 << 8)), i61.getZExtValue());}TEST(APIntTest, i65_Count) {APInt i65(65, 0, true);EXPECT_EQ(65u, i65.countLeadingZeros());EXPECT_EQ(0u, i65.countLeadingOnes());EXPECT_EQ(0u, i65.getActiveBits());EXPECT_EQ(1u, i65.getActiveWords());EXPECT_EQ(65u, i65.countTrailingZeros());EXPECT_EQ(0u, i65.countPopulation());APInt i65minus(65, 0, true);i65minus.setBit(64);EXPECT_EQ(0u, i65minus.countLeadingZeros());EXPECT_EQ(1u, i65minus.countLeadingOnes());EXPECT_EQ(65u, i65minus.getActiveBits());EXPECT_EQ(64u, i65minus.countTrailingZeros());EXPECT_EQ(1u, i65minus.countPopulation());}TEST(APIntTest, i128_PositiveCount) {APInt u128max = APInt::getAllOnes(128);EXPECT_EQ(128u, u128max.countLeadingOnes());EXPECT_EQ(0u, u128max.countLeadingZeros());EXPECT_EQ(128u, u128max.getActiveBits());EXPECT_EQ(0u, u128max.countTrailingZeros());EXPECT_EQ(128u, u128max.countTrailingOnes());EXPECT_EQ(128u, u128max.countPopulation());APInt u64max(128, static_cast<uint64_t>(-1), false);EXPECT_EQ(64u, u64max.countLeadingZeros());EXPECT_EQ(0u, u64max.countLeadingOnes());EXPECT_EQ(64u, u64max.getActiveBits());EXPECT_EQ(0u, u64max.countTrailingZeros());EXPECT_EQ(64u, u64max.countTrailingOnes());EXPECT_EQ(64u, u64max.countPopulation());EXPECT_EQ((uint64_t)~0ull, u64max.getZExtValue());APInt zero(128, 0, true);EXPECT_EQ(128u, zero.countLeadingZeros());EXPECT_EQ(0u, zero.countLeadingOnes());EXPECT_EQ(0u, zero.getActiveBits());EXPECT_EQ(128u, zero.countTrailingZeros());EXPECT_EQ(0u, zero.countTrailingOnes());EXPECT_EQ(0u, zero.countPopulation());EXPECT_EQ(0u, zero.getSExtValue());EXPECT_EQ(0u, zero.getZExtValue());APInt one(128, 1, true);EXPECT_EQ(127u, one.countLeadingZeros());EXPECT_EQ(0u, one.countLeadingOnes());EXPECT_EQ(1u, one.getActiveBits());EXPECT_EQ(0u, one.countTrailingZeros());EXPECT_EQ(1u, one.countTrailingOnes());EXPECT_EQ(1u, one.countPopulation());EXPECT_EQ(1, one.getSExtValue());EXPECT_EQ(1u, one.getZExtValue());APInt s128(128, 2, true);EXPECT_EQ(126u, s128.countLeadingZeros());EXPECT_EQ(0u, s128.countLeadingOnes());EXPECT_EQ(2u, s128.getActiveBits());EXPECT_EQ(1u, s128.countTrailingZeros());EXPECT_EQ(0u, s128.countTrailingOnes());EXPECT_EQ(1u, s128.countPopulation());EXPECT_EQ(2, s128.getSExtValue());EXPECT_EQ(2u, s128.getZExtValue());// NOP Tests128.setBits(42, 42);EXPECT_EQ(126u, s128.countLeadingZeros());EXPECT_EQ(0u, s128.countLeadingOnes());EXPECT_EQ(2u, s128.getActiveBits());EXPECT_EQ(1u, s128.countTrailingZeros());EXPECT_EQ(0u, s128.countTrailingOnes());EXPECT_EQ(1u, s128.countPopulation());EXPECT_EQ(2, s128.getSExtValue());EXPECT_EQ(2u, s128.getZExtValue());s128.setBits(3, 32);EXPECT_EQ(96u, s128.countLeadingZeros());EXPECT_EQ(0u, s128.countLeadingOnes());EXPECT_EQ(32u, s128.getActiveBits());EXPECT_EQ(33u, s128.getMinSignedBits());EXPECT_EQ(1u, s128.countTrailingZeros());EXPECT_EQ(0u, s128.countTrailingOnes());EXPECT_EQ(30u, s128.countPopulation());EXPECT_EQ(static_cast<uint32_t>((~0u << 3) | 2), s128.getZExtValue());s128.setBits(62, 128);EXPECT_EQ(0u, s128.countLeadingZeros());EXPECT_EQ(66u, s128.countLeadingOnes());EXPECT_EQ(128u, s128.getActiveBits());EXPECT_EQ(63u, s128.getMinSignedBits());EXPECT_EQ(1u, s128.countTrailingZeros());EXPECT_EQ(0u, s128.countTrailingOnes());EXPECT_EQ(96u, s128.countPopulation());EXPECT_EQ(static_cast<int64_t>((3ull << 62) |static_cast<uint32_t>((~0u << 3) | 2)),s128.getSExtValue());}TEST(APIntTest, i256) {APInt s256(256, 15, true);EXPECT_EQ(252u, s256.countLeadingZeros());EXPECT_EQ(0u, s256.countLeadingOnes());EXPECT_EQ(4u, s256.getActiveBits());EXPECT_EQ(0u, s256.countTrailingZeros());EXPECT_EQ(4u, s256.countTrailingOnes());EXPECT_EQ(4u, s256.countPopulation());EXPECT_EQ(15, s256.getSExtValue());EXPECT_EQ(15u, s256.getZExtValue());s256.setBits(62, 66);EXPECT_EQ(190u, s256.countLeadingZeros());EXPECT_EQ(0u, s256.countLeadingOnes());EXPECT_EQ(66u, s256.getActiveBits());EXPECT_EQ(67u, s256.getMinSignedBits());EXPECT_EQ(0u, s256.countTrailingZeros());EXPECT_EQ(4u, s256.countTrailingOnes());EXPECT_EQ(8u, s256.countPopulation());s256.setBits(60, 256);EXPECT_EQ(0u, s256.countLeadingZeros());EXPECT_EQ(196u, s256.countLeadingOnes());EXPECT_EQ(256u, s256.getActiveBits());EXPECT_EQ(61u, s256.getMinSignedBits());EXPECT_EQ(0u, s256.countTrailingZeros());EXPECT_EQ(4u, s256.countTrailingOnes());EXPECT_EQ(200u, s256.countPopulation());EXPECT_EQ(static_cast<int64_t>((~0ull << 60) | 15), s256.getSExtValue());}TEST(APIntTest, i1) {const APInt neg_two(1, static_cast<uint64_t>(-2), true);const APInt neg_one(1, static_cast<uint64_t>(-1), true);const APInt zero(1, 0);const APInt one(1, 1);const APInt two(1, 2);EXPECT_EQ(0, neg_two.getSExtValue());EXPECT_EQ(-1, neg_one.getSExtValue());EXPECT_EQ(1u, neg_one.getZExtValue());EXPECT_EQ(0u, zero.getZExtValue());EXPECT_EQ(-1, one.getSExtValue());EXPECT_EQ(1u, one.getZExtValue());EXPECT_EQ(0u, two.getZExtValue());EXPECT_EQ(0, two.getSExtValue());// Basic equalities for 1-bit values.EXPECT_EQ(zero, two);EXPECT_EQ(zero, neg_two);EXPECT_EQ(one, neg_one);EXPECT_EQ(two, neg_two);// Min/max signed values.EXPECT_TRUE(zero.isMaxSignedValue());EXPECT_FALSE(one.isMaxSignedValue());EXPECT_FALSE(zero.isMinSignedValue());EXPECT_TRUE(one.isMinSignedValue());// Additions.EXPECT_EQ(two, one + one);EXPECT_EQ(zero, neg_one + one);EXPECT_EQ(neg_two, neg_one + neg_one);// Subtractions.EXPECT_EQ(neg_two, neg_one - one);EXPECT_EQ(two, one - neg_one);EXPECT_EQ(zero, one - one);// AndEXPECT_EQ(zero, zero & zero);EXPECT_EQ(zero, one & zero);EXPECT_EQ(zero, zero & one);EXPECT_EQ(one, one & one);EXPECT_EQ(zero, zero & zero);EXPECT_EQ(zero, neg_one & zero);EXPECT_EQ(zero, zero & neg_one);EXPECT_EQ(neg_one, neg_one & neg_one);// OrEXPECT_EQ(zero, zero | zero);EXPECT_EQ(one, one | zero);EXPECT_EQ(one, zero | one);EXPECT_EQ(one, one | one);EXPECT_EQ(zero, zero | zero);EXPECT_EQ(neg_one, neg_one | zero);EXPECT_EQ(neg_one, zero | neg_one);EXPECT_EQ(neg_one, neg_one | neg_one);// XorEXPECT_EQ(zero, zero ^ zero);EXPECT_EQ(one, one ^ zero);EXPECT_EQ(one, zero ^ one);EXPECT_EQ(zero, one ^ one);EXPECT_EQ(zero, zero ^ zero);EXPECT_EQ(neg_one, neg_one ^ zero);EXPECT_EQ(neg_one, zero ^ neg_one);EXPECT_EQ(zero, neg_one ^ neg_one);// Shifts.EXPECT_EQ(zero, one << one);EXPECT_EQ(one, one << zero);EXPECT_EQ(zero, one.shl(1));EXPECT_EQ(one, one.shl(0));EXPECT_EQ(zero, one.lshr(1));EXPECT_EQ(one, one.ashr(1));// Rotates.EXPECT_EQ(one, one.rotl(0));EXPECT_EQ(one, one.rotl(1));EXPECT_EQ(one, one.rotr(0));EXPECT_EQ(one, one.rotr(1));// Multiplies.EXPECT_EQ(neg_one, neg_one * one);EXPECT_EQ(neg_one, one * neg_one);EXPECT_EQ(one, neg_one * neg_one);EXPECT_EQ(one, one * one);// Divides.EXPECT_EQ(neg_one, one.sdiv(neg_one));EXPECT_EQ(neg_one, neg_one.sdiv(one));EXPECT_EQ(one, neg_one.sdiv(neg_one));EXPECT_EQ(one, one.sdiv(one));EXPECT_EQ(neg_one, one.udiv(neg_one));EXPECT_EQ(neg_one, neg_one.udiv(one));EXPECT_EQ(one, neg_one.udiv(neg_one));EXPECT_EQ(one, one.udiv(one));// Remainders.EXPECT_EQ(zero, neg_one.srem(one));EXPECT_EQ(zero, neg_one.urem(one));EXPECT_EQ(zero, one.srem(neg_one));// sdivrem{APInt q(8, 0);APInt r(8, 0);APInt one(8, 1);APInt two(8, 2);APInt nine(8, 9);APInt four(8, 4);EXPECT_EQ(nine.srem(two), one);EXPECT_EQ(nine.srem(-two), one);EXPECT_EQ((-nine).srem(two), -one);EXPECT_EQ((-nine).srem(-two), -one);APInt::sdivrem(nine, two, q, r);EXPECT_EQ(four, q);EXPECT_EQ(one, r);APInt::sdivrem(-nine, two, q, r);EXPECT_EQ(-four, q);EXPECT_EQ(-one, r);APInt::sdivrem(nine, -two, q, r);EXPECT_EQ(-four, q);EXPECT_EQ(one, r);APInt::sdivrem(-nine, -two, q, r);EXPECT_EQ(four, q);EXPECT_EQ(-one, r);}}TEST(APIntTest, compare) {std::array<APInt, 5> testVals{{APInt{16, 2},APInt{16, 1},APInt{16, 0},APInt{16, (uint64_t)-1, true},APInt{16, (uint64_t)-2, true},}};for (auto &arg1 : testVals)for (auto &arg2 : testVals) {auto uv1 = arg1.getZExtValue();auto uv2 = arg2.getZExtValue();auto sv1 = arg1.getSExtValue();auto sv2 = arg2.getSExtValue();EXPECT_EQ(uv1 < uv2, arg1.ult(arg2));EXPECT_EQ(uv1 <= uv2, arg1.ule(arg2));EXPECT_EQ(uv1 > uv2, arg1.ugt(arg2));EXPECT_EQ(uv1 >= uv2, arg1.uge(arg2));EXPECT_EQ(sv1 < sv2, arg1.slt(arg2));EXPECT_EQ(sv1 <= sv2, arg1.sle(arg2));EXPECT_EQ(sv1 > sv2, arg1.sgt(arg2));EXPECT_EQ(sv1 >= sv2, arg1.sge(arg2));EXPECT_EQ(uv1 < uv2, arg1.ult(uv2));EXPECT_EQ(uv1 <= uv2, arg1.ule(uv2));EXPECT_EQ(uv1 > uv2, arg1.ugt(uv2));EXPECT_EQ(uv1 >= uv2, arg1.uge(uv2));EXPECT_EQ(sv1 < sv2, arg1.slt(sv2));EXPECT_EQ(sv1 <= sv2, arg1.sle(sv2));EXPECT_EQ(sv1 > sv2, arg1.sgt(sv2));EXPECT_EQ(sv1 >= sv2, arg1.sge(sv2));}}TEST(APIntTest, compareWithRawIntegers) {EXPECT_TRUE(!APInt(8, 1).uge(256));EXPECT_TRUE(!APInt(8, 1).ugt(256));EXPECT_TRUE( APInt(8, 1).ule(256));EXPECT_TRUE( APInt(8, 1).ult(256));EXPECT_TRUE(!APInt(8, 1).sge(256));EXPECT_TRUE(!APInt(8, 1).sgt(256));EXPECT_TRUE( APInt(8, 1).sle(256));EXPECT_TRUE( APInt(8, 1).slt(256));EXPECT_TRUE(!(APInt(8, 0) == 256));EXPECT_TRUE( APInt(8, 0) != 256);EXPECT_TRUE(!(APInt(8, 1) == 256));EXPECT_TRUE( APInt(8, 1) != 256);auto uint64max = UINT64_MAX;auto int64max = INT64_MAX;auto int64min = INT64_MIN;auto u64 = APInt{128, uint64max};auto s64 = APInt{128, static_cast<uint64_t>(int64max), true};auto big = u64 + 1;EXPECT_TRUE( u64.uge(uint64max));EXPECT_TRUE(!u64.ugt(uint64max));EXPECT_TRUE( u64.ule(uint64max));EXPECT_TRUE(!u64.ult(uint64max));EXPECT_TRUE( u64.sge(int64max));EXPECT_TRUE( u64.sgt(int64max));EXPECT_TRUE(!u64.sle(int64max));EXPECT_TRUE(!u64.slt(int64max));EXPECT_TRUE( u64.sge(int64min));EXPECT_TRUE( u64.sgt(int64min));EXPECT_TRUE(!u64.sle(int64min));EXPECT_TRUE(!u64.slt(int64min));EXPECT_TRUE(u64 == uint64max);EXPECT_TRUE(u64 != int64max);EXPECT_TRUE(u64 != int64min);EXPECT_TRUE(!s64.uge(uint64max));EXPECT_TRUE(!s64.ugt(uint64max));EXPECT_TRUE( s64.ule(uint64max));EXPECT_TRUE( s64.ult(uint64max));EXPECT_TRUE( s64.sge(int64max));EXPECT_TRUE(!s64.sgt(int64max));EXPECT_TRUE( s64.sle(int64max));EXPECT_TRUE(!s64.slt(int64max));EXPECT_TRUE( s64.sge(int64min));EXPECT_TRUE( s64.sgt(int64min));EXPECT_TRUE(!s64.sle(int64min));EXPECT_TRUE(!s64.slt(int64min));EXPECT_TRUE(s64 != uint64max);EXPECT_TRUE(s64 == int64max);EXPECT_TRUE(s64 != int64min);EXPECT_TRUE( big.uge(uint64max));EXPECT_TRUE( big.ugt(uint64max));EXPECT_TRUE(!big.ule(uint64max));EXPECT_TRUE(!big.ult(uint64max));EXPECT_TRUE( big.sge(int64max));EXPECT_TRUE( big.sgt(int64max));EXPECT_TRUE(!big.sle(int64max));EXPECT_TRUE(!big.slt(int64max));EXPECT_TRUE( big.sge(int64min));EXPECT_TRUE( big.sgt(int64min));EXPECT_TRUE(!big.sle(int64min));EXPECT_TRUE(!big.slt(int64min));EXPECT_TRUE(big != uint64max);EXPECT_TRUE(big != int64max);EXPECT_TRUE(big != int64min);}TEST(APIntTest, compareWithInt64Min) {int64_t edge = INT64_MIN;int64_t edgeP1 = edge + 1;int64_t edgeM1 = INT64_MAX;auto a = APInt{64, static_cast<uint64_t>(edge), true};EXPECT_TRUE(!a.slt(edge));EXPECT_TRUE( a.sle(edge));EXPECT_TRUE(!a.sgt(edge));EXPECT_TRUE( a.sge(edge));EXPECT_TRUE( a.slt(edgeP1));EXPECT_TRUE( a.sle(edgeP1));EXPECT_TRUE(!a.sgt(edgeP1));EXPECT_TRUE(!a.sge(edgeP1));EXPECT_TRUE( a.slt(edgeM1));EXPECT_TRUE( a.sle(edgeM1));EXPECT_TRUE(!a.sgt(edgeM1));EXPECT_TRUE(!a.sge(edgeM1));}TEST(APIntTest, compareWithHalfInt64Max) {uint64_t edge = 0x4000000000000000;uint64_t edgeP1 = edge + 1;uint64_t edgeM1 = edge - 1;auto a = APInt{64, edge};EXPECT_TRUE(!a.ult(edge));EXPECT_TRUE( a.ule(edge));EXPECT_TRUE(!a.ugt(edge));EXPECT_TRUE( a.uge(edge));EXPECT_TRUE( a.ult(edgeP1));EXPECT_TRUE( a.ule(edgeP1));EXPECT_TRUE(!a.ugt(edgeP1));EXPECT_TRUE(!a.uge(edgeP1));EXPECT_TRUE(!a.ult(edgeM1));EXPECT_TRUE(!a.ule(edgeM1));EXPECT_TRUE( a.ugt(edgeM1));EXPECT_TRUE( a.uge(edgeM1));EXPECT_TRUE(!a.slt(edge));EXPECT_TRUE( a.sle(edge));EXPECT_TRUE(!a.sgt(edge));EXPECT_TRUE( a.sge(edge));EXPECT_TRUE( a.slt(edgeP1));EXPECT_TRUE( a.sle(edgeP1));EXPECT_TRUE(!a.sgt(edgeP1));EXPECT_TRUE(!a.sge(edgeP1));EXPECT_TRUE(!a.slt(edgeM1));EXPECT_TRUE(!a.sle(edgeM1));EXPECT_TRUE( a.sgt(edgeM1));EXPECT_TRUE( a.sge(edgeM1));}TEST(APIntTest, compareLargeIntegers) {// Make sure all the combinations of signed comparisons work with big ints.auto One = APInt{128, static_cast<uint64_t>(1), true};auto Two = APInt{128, static_cast<uint64_t>(2), true};auto MinusOne = APInt{128, static_cast<uint64_t>(-1), true};auto MinusTwo = APInt{128, static_cast<uint64_t>(-2), true};EXPECT_TRUE(!One.slt(One));EXPECT_TRUE(!Two.slt(One));EXPECT_TRUE(MinusOne.slt(One));EXPECT_TRUE(MinusTwo.slt(One));EXPECT_TRUE(One.slt(Two));EXPECT_TRUE(!Two.slt(Two));EXPECT_TRUE(MinusOne.slt(Two));EXPECT_TRUE(MinusTwo.slt(Two));EXPECT_TRUE(!One.slt(MinusOne));EXPECT_TRUE(!Two.slt(MinusOne));EXPECT_TRUE(!MinusOne.slt(MinusOne));EXPECT_TRUE(MinusTwo.slt(MinusOne));EXPECT_TRUE(!One.slt(MinusTwo));EXPECT_TRUE(!Two.slt(MinusTwo));EXPECT_TRUE(!MinusOne.slt(MinusTwo));EXPECT_TRUE(!MinusTwo.slt(MinusTwo));}TEST(APIntTest, binaryOpsWithRawIntegers) {// Single word check.uint64_t E1 = 0x2CA7F46BF6569915ULL;APInt A1(64, E1);EXPECT_EQ(A1 & E1, E1);EXPECT_EQ(A1 & 0, 0);EXPECT_EQ(A1 & 1, 1);EXPECT_EQ(A1 & 5, 5);EXPECT_EQ(A1 & UINT64_MAX, E1);EXPECT_EQ(A1 | E1, E1);EXPECT_EQ(A1 | 0, E1);EXPECT_EQ(A1 | 1, E1);EXPECT_EQ(A1 | 2, E1 | 2);EXPECT_EQ(A1 | UINT64_MAX, UINT64_MAX);EXPECT_EQ(A1 ^ E1, 0);EXPECT_EQ(A1 ^ 0, E1);EXPECT_EQ(A1 ^ 1, E1 ^ 1);EXPECT_EQ(A1 ^ 7, E1 ^ 7);EXPECT_EQ(A1 ^ UINT64_MAX, ~E1);// Multiword check.uint64_t N = 0xEB6EB136591CBA21ULL;APInt::WordType E2[4] = {N,0x7B9358BD6A33F10AULL,0x7E7FFA5EADD8846ULL,0x305F341CA00B613DULL};APInt A2(APInt::APINT_BITS_PER_WORD*4, E2);EXPECT_EQ(A2 & N, N);EXPECT_EQ(A2 & 0, 0);EXPECT_EQ(A2 & 1, 1);EXPECT_EQ(A2 & 5, 1);EXPECT_EQ(A2 & UINT64_MAX, N);EXPECT_EQ(A2 | N, A2);EXPECT_EQ(A2 | 0, A2);EXPECT_EQ(A2 | 1, A2);EXPECT_EQ(A2 | 2, A2 + 2);EXPECT_EQ(A2 | UINT64_MAX, A2 - N + UINT64_MAX);EXPECT_EQ(A2 ^ N, A2 - N);EXPECT_EQ(A2 ^ 0, A2);EXPECT_EQ(A2 ^ 1, A2 - 1);EXPECT_EQ(A2 ^ 7, A2 + 5);EXPECT_EQ(A2 ^ UINT64_MAX, A2 - N + ~N);}TEST(APIntTest, rvalue_arithmetic) {// Test all combinations of lvalue/rvalue lhs/rhs of add/sub// Lamdba to return an APInt by value, but also provide the raw value of the// allocated data.auto getRValue = [](const char *HexString, uint64_t const *&RawData) {APInt V(129, HexString, 16);RawData = V.getRawData();return V;};APInt One(129, "1", 16);APInt Two(129, "2", 16);APInt Three(129, "3", 16);APInt MinusOne = -One;const uint64_t *RawDataL = nullptr;const uint64_t *RawDataR = nullptr;{// 1 + 1 = 2APInt AddLL = One + One;EXPECT_EQ(AddLL, Two);APInt AddLR = One + getRValue("1", RawDataR);EXPECT_EQ(AddLR, Two);EXPECT_EQ(AddLR.getRawData(), RawDataR);APInt AddRL = getRValue("1", RawDataL) + One;EXPECT_EQ(AddRL, Two);EXPECT_EQ(AddRL.getRawData(), RawDataL);APInt AddRR = getRValue("1", RawDataL) + getRValue("1", RawDataR);EXPECT_EQ(AddRR, Two);EXPECT_EQ(AddRR.getRawData(), RawDataR);// LValue's and constantsAPInt AddLK = One + 1;EXPECT_EQ(AddLK, Two);APInt AddKL = 1 + One;EXPECT_EQ(AddKL, Two);// RValue's and constantsAPInt AddRK = getRValue("1", RawDataL) + 1;EXPECT_EQ(AddRK, Two);EXPECT_EQ(AddRK.getRawData(), RawDataL);APInt AddKR = 1 + getRValue("1", RawDataR);EXPECT_EQ(AddKR, Two);EXPECT_EQ(AddKR.getRawData(), RawDataR);}{// 0x0,FFFF...FFFF + 0x2 = 0x100...0001APInt AllOnes(129, "0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16);APInt HighOneLowOne(129, "100000000000000000000000000000001", 16);APInt AddLL = AllOnes + Two;EXPECT_EQ(AddLL, HighOneLowOne);APInt AddLR = AllOnes + getRValue("2", RawDataR);EXPECT_EQ(AddLR, HighOneLowOne);EXPECT_EQ(AddLR.getRawData(), RawDataR);APInt AddRL = getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataL) + Two;EXPECT_EQ(AddRL, HighOneLowOne);EXPECT_EQ(AddRL.getRawData(), RawDataL);APInt AddRR = getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataL) +getRValue("2", RawDataR);EXPECT_EQ(AddRR, HighOneLowOne);EXPECT_EQ(AddRR.getRawData(), RawDataR);// LValue's and constantsAPInt AddLK = AllOnes + 2;EXPECT_EQ(AddLK, HighOneLowOne);APInt AddKL = 2 + AllOnes;EXPECT_EQ(AddKL, HighOneLowOne);// RValue's and constantsAPInt AddRK = getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataL) + 2;EXPECT_EQ(AddRK, HighOneLowOne);EXPECT_EQ(AddRK.getRawData(), RawDataL);APInt AddKR = 2 + getRValue("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR);EXPECT_EQ(AddKR, HighOneLowOne);EXPECT_EQ(AddKR.getRawData(), RawDataR);}{// 2 - 1 = 1APInt SubLL = Two - One;EXPECT_EQ(SubLL, One);APInt SubLR = Two - getRValue("1", RawDataR);EXPECT_EQ(SubLR, One);EXPECT_EQ(SubLR.getRawData(), RawDataR);APInt SubRL = getRValue("2", RawDataL) - One;EXPECT_EQ(SubRL, One);EXPECT_EQ(SubRL.getRawData(), RawDataL);APInt SubRR = getRValue("2", RawDataL) - getRValue("1", RawDataR);EXPECT_EQ(SubRR, One);EXPECT_EQ(SubRR.getRawData(), RawDataR);// LValue's and constantsAPInt SubLK = Two - 1;EXPECT_EQ(SubLK, One);APInt SubKL = 2 - One;EXPECT_EQ(SubKL, One);// RValue's and constantsAPInt SubRK = getRValue("2", RawDataL) - 1;EXPECT_EQ(SubRK, One);EXPECT_EQ(SubRK.getRawData(), RawDataL);APInt SubKR = 2 - getRValue("1", RawDataR);EXPECT_EQ(SubKR, One);EXPECT_EQ(SubKR.getRawData(), RawDataR);}{// 0x100...0001 - 0x0,FFFF...FFFF = 0x2APInt AllOnes(129, "0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16);APInt HighOneLowOne(129, "100000000000000000000000000000001", 16);APInt SubLL = HighOneLowOne - AllOnes;EXPECT_EQ(SubLL, Two);APInt SubLR = HighOneLowOne -getRValue("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR);EXPECT_EQ(SubLR, Two);EXPECT_EQ(SubLR.getRawData(), RawDataR);APInt SubRL = getRValue("100000000000000000000000000000001", RawDataL) -AllOnes;EXPECT_EQ(SubRL, Two);EXPECT_EQ(SubRL.getRawData(), RawDataL);APInt SubRR = getRValue("100000000000000000000000000000001", RawDataL) -getRValue("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR);EXPECT_EQ(SubRR, Two);EXPECT_EQ(SubRR.getRawData(), RawDataR);// LValue's and constants// 0x100...0001 - 0x2 = 0x0,FFFF...FFFFAPInt SubLK = HighOneLowOne - 2;EXPECT_EQ(SubLK, AllOnes);// 2 - (-1) = 3APInt SubKL = 2 - MinusOne;EXPECT_EQ(SubKL, Three);// RValue's and constants// 0x100...0001 - 0x2 = 0x0,FFFF...FFFFAPInt SubRK = getRValue("100000000000000000000000000000001", RawDataL) - 2;EXPECT_EQ(SubRK, AllOnes);EXPECT_EQ(SubRK.getRawData(), RawDataL);APInt SubKR = 2 - getRValue("1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", RawDataR);EXPECT_EQ(SubKR, Three);EXPECT_EQ(SubKR.getRawData(), RawDataR);}}TEST(APIntTest, rvalue_bitwise) {// Test all combinations of lvalue/rvalue lhs/rhs of and/or/xor// Lamdba to return an APInt by value, but also provide the raw value of the// allocated data.auto getRValue = [](const char *HexString, uint64_t const *&RawData) {APInt V(129, HexString, 16);RawData = V.getRawData();return V;};APInt Ten(129, "A", 16);APInt Twelve(129, "C", 16);const uint64_t *RawDataL = nullptr;const uint64_t *RawDataR = nullptr;{// 12 & 10 = 8APInt AndLL = Ten & Twelve;EXPECT_EQ(AndLL, 0x8);APInt AndLR = Ten & getRValue("C", RawDataR);EXPECT_EQ(AndLR, 0x8);EXPECT_EQ(AndLR.getRawData(), RawDataR);APInt AndRL = getRValue("A", RawDataL) & Twelve;EXPECT_EQ(AndRL, 0x8);EXPECT_EQ(AndRL.getRawData(), RawDataL);APInt AndRR = getRValue("A", RawDataL) & getRValue("C", RawDataR);EXPECT_EQ(AndRR, 0x8);EXPECT_EQ(AndRR.getRawData(), RawDataR);// LValue's and constantsAPInt AndLK = Ten & 0xc;EXPECT_EQ(AndLK, 0x8);APInt AndKL = 0xa & Twelve;EXPECT_EQ(AndKL, 0x8);// RValue's and constantsAPInt AndRK = getRValue("A", RawDataL) & 0xc;EXPECT_EQ(AndRK, 0x8);EXPECT_EQ(AndRK.getRawData(), RawDataL);APInt AndKR = 0xa & getRValue("C", RawDataR);EXPECT_EQ(AndKR, 0x8);EXPECT_EQ(AndKR.getRawData(), RawDataR);}{// 12 | 10 = 14APInt OrLL = Ten | Twelve;EXPECT_EQ(OrLL, 0xe);APInt OrLR = Ten | getRValue("C", RawDataR);EXPECT_EQ(OrLR, 0xe);EXPECT_EQ(OrLR.getRawData(), RawDataR);APInt OrRL = getRValue("A", RawDataL) | Twelve;EXPECT_EQ(OrRL, 0xe);EXPECT_EQ(OrRL.getRawData(), RawDataL);APInt OrRR = getRValue("A", RawDataL) | getRValue("C", RawDataR);EXPECT_EQ(OrRR, 0xe);EXPECT_EQ(OrRR.getRawData(), RawDataR);// LValue's and constantsAPInt OrLK = Ten | 0xc;EXPECT_EQ(OrLK, 0xe);APInt OrKL = 0xa | Twelve;EXPECT_EQ(OrKL, 0xe);// RValue's and constantsAPInt OrRK = getRValue("A", RawDataL) | 0xc;EXPECT_EQ(OrRK, 0xe);EXPECT_EQ(OrRK.getRawData(), RawDataL);APInt OrKR = 0xa | getRValue("C", RawDataR);EXPECT_EQ(OrKR, 0xe);EXPECT_EQ(OrKR.getRawData(), RawDataR);}{// 12 ^ 10 = 6APInt XorLL = Ten ^ Twelve;EXPECT_EQ(XorLL, 0x6);APInt XorLR = Ten ^ getRValue("C", RawDataR);EXPECT_EQ(XorLR, 0x6);EXPECT_EQ(XorLR.getRawData(), RawDataR);APInt XorRL = getRValue("A", RawDataL) ^ Twelve;EXPECT_EQ(XorRL, 0x6);EXPECT_EQ(XorRL.getRawData(), RawDataL);APInt XorRR = getRValue("A", RawDataL) ^ getRValue("C", RawDataR);EXPECT_EQ(XorRR, 0x6);EXPECT_EQ(XorRR.getRawData(), RawDataR);// LValue's and constantsAPInt XorLK = Ten ^ 0xc;EXPECT_EQ(XorLK, 0x6);APInt XorKL = 0xa ^ Twelve;EXPECT_EQ(XorKL, 0x6);// RValue's and constantsAPInt XorRK = getRValue("A", RawDataL) ^ 0xc;EXPECT_EQ(XorRK, 0x6);EXPECT_EQ(XorRK.getRawData(), RawDataL);APInt XorKR = 0xa ^ getRValue("C", RawDataR);EXPECT_EQ(XorKR, 0x6);EXPECT_EQ(XorKR.getRawData(), RawDataR);}}TEST(APIntTest, rvalue_invert) {// Lamdba to return an APInt by value, but also provide the raw value of the// allocated data.auto getRValue = [](const char *HexString, uint64_t const *&RawData) {APInt V(129, HexString, 16);RawData = V.getRawData();return V;};APInt One(129, 1);APInt NegativeTwo(129, -2ULL, true);const uint64_t *RawData = nullptr;{// ~1 = -2APInt NegL = ~One;EXPECT_EQ(NegL, NegativeTwo);APInt NegR = ~getRValue("1", RawData);EXPECT_EQ(NegR, NegativeTwo);EXPECT_EQ(NegR.getRawData(), RawData);}}// Tests different div/rem varaints using scheme (a * b + c) / avoid testDiv(APInt a, APInt b, APInt c) {ASSERT_TRUE(a.uge(b)); // Must: a >= bASSERT_TRUE(a.ugt(c)); // Must: a > cauto p = a * b + c;auto q = p.udiv(a);auto r = p.urem(a);EXPECT_EQ(b, q);EXPECT_EQ(c, r);APInt::udivrem(p, a, q, r);EXPECT_EQ(b, q);EXPECT_EQ(c, r);q = p.sdiv(a);r = p.srem(a);EXPECT_EQ(b, q);EXPECT_EQ(c, r);APInt::sdivrem(p, a, q, r);EXPECT_EQ(b, q);EXPECT_EQ(c, r);if (b.ugt(c)) { // Test also symmetric caseq = p.udiv(b);r = p.urem(b);EXPECT_EQ(a, q);EXPECT_EQ(c, r);APInt::udivrem(p, b, q, r);EXPECT_EQ(a, q);EXPECT_EQ(c, r);q = p.sdiv(b);r = p.srem(b);EXPECT_EQ(a, q);EXPECT_EQ(c, r);APInt::sdivrem(p, b, q, r);EXPECT_EQ(a, q);EXPECT_EQ(c, r);}}TEST(APIntTest, divrem_big1) {// Tests KnuthDiv rare step D6testDiv({256, "1ffffffffffffffff", 16},{256, "1ffffffffffffffff", 16},{256, 0});}TEST(APIntTest, divrem_big2) {// Tests KnuthDiv rare step D6testDiv({1024, "112233ceff""cecece000000ffffffffffffffffffff""ffffffffffffffffffffffffffffffff""ffffffffffffffffffffffffffffffff""ffffffffffffffffffffffffffffff33", 16},{1024, "111111ffffffffffffffff""ffffffffffffffffffffffffffffffff""fffffffffffffffffffffffffffffccf""ffffffffffffffffffffffffffffff00", 16},{1024, 7919});}TEST(APIntTest, divrem_big3) {// Tests KnuthDiv case without shifttestDiv({256, "80000001ffffffffffffffff", 16},{256, "ffffffffffffff0000000", 16},{256, 4219});}TEST(APIntTest, divrem_big4) {// Tests heap allocation in divide() enfoced by huge numberstestDiv(APInt{4096, 5}.shl(2001),APInt{4096, 1}.shl(2000),APInt{4096, 4219*13});}TEST(APIntTest, divrem_big5) {// Tests one word divisor case of divide()testDiv(APInt{1024, 19}.shl(811),APInt{1024, 4356013}, // one wordAPInt{1024, 1});}TEST(APIntTest, divrem_big6) {// Tests some rare "borrow" cases in D4 steptestDiv(APInt{512, "ffffffffffffffff00000000000000000000000001", 16},APInt{512, "10000000000000001000000000000001", 16},APInt{512, "10000000000000000000000000000000", 16});}TEST(APIntTest, divrem_big7) {// Yet another test for KnuthDiv rare step D6.testDiv({224, "800000008000000200000005", 16},{224, "fffffffd", 16},{224, "80000000800000010000000f", 16});}void testDiv(APInt a, uint64_t b, APInt c) {auto p = a * b + c;APInt q;uint64_t r;// Unsigned division will only work if our original number wasn't negative.if (!a.isNegative()) {q = p.udiv(b);r = p.urem(b);EXPECT_EQ(a, q);EXPECT_EQ(c, r);APInt::udivrem(p, b, q, r);EXPECT_EQ(a, q);EXPECT_EQ(c, r);}q = p.sdiv(b);r = p.srem(b);EXPECT_EQ(a, q);if (c.isNegative())EXPECT_EQ(-c, -r); // Need to negate so the uint64_t compare will work.elseEXPECT_EQ(c, r);int64_t sr;APInt::sdivrem(p, b, q, sr);EXPECT_EQ(a, q);if (c.isNegative())EXPECT_EQ(-c, -sr); // Need to negate so the uint64_t compare will work.elseEXPECT_EQ(c, sr);}TEST(APIntTest, divremuint) {// Single word APInttestDiv(APInt{64, 9},2,APInt{64, 1});// Single word negative APInttestDiv(-APInt{64, 9},2,-APInt{64, 1});// Multiword dividend with only one significant word.testDiv(APInt{256, 9},2,APInt{256, 1});// Negative dividend.testDiv(-APInt{256, 9},2,-APInt{256, 1});// Multiword dividendtestDiv(APInt{1024, 19}.shl(811),4356013, // one wordAPInt{1024, 1});}TEST(APIntTest, divrem_simple) {// Test simple cases.APInt A(65, 2), B(65, 2);APInt Q, R;// X / XAPInt::sdivrem(A, B, Q, R);EXPECT_EQ(Q, APInt(65, 1));EXPECT_EQ(R, APInt(65, 0));APInt::udivrem(A, B, Q, R);EXPECT_EQ(Q, APInt(65, 1));EXPECT_EQ(R, APInt(65, 0));// 0 / XAPInt O(65, 0);APInt::sdivrem(O, B, Q, R);EXPECT_EQ(Q, APInt(65, 0));EXPECT_EQ(R, APInt(65, 0));APInt::udivrem(O, B, Q, R);EXPECT_EQ(Q, APInt(65, 0));EXPECT_EQ(R, APInt(65, 0));// X / 1APInt I(65, 1);APInt::sdivrem(A, I, Q, R);EXPECT_EQ(Q, A);EXPECT_EQ(R, APInt(65, 0));APInt::udivrem(A, I, Q, R);EXPECT_EQ(Q, A);EXPECT_EQ(R, APInt(65, 0));}TEST(APIntTest, fromString) {EXPECT_EQ(APInt(32, 0), APInt(32, "0", 2));EXPECT_EQ(APInt(32, 1), APInt(32, "1", 2));EXPECT_EQ(APInt(32, 2), APInt(32, "10", 2));EXPECT_EQ(APInt(32, 3), APInt(32, "11", 2));EXPECT_EQ(APInt(32, 4), APInt(32, "100", 2));EXPECT_EQ(APInt(32, 0), APInt(32, "+0", 2));EXPECT_EQ(APInt(32, 1), APInt(32, "+1", 2));EXPECT_EQ(APInt(32, 2), APInt(32, "+10", 2));EXPECT_EQ(APInt(32, 3), APInt(32, "+11", 2));EXPECT_EQ(APInt(32, 4), APInt(32, "+100", 2));EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 2));EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 2));EXPECT_EQ(APInt(32, uint64_t(-2LL)), APInt(32, "-10", 2));EXPECT_EQ(APInt(32, uint64_t(-3LL)), APInt(32, "-11", 2));EXPECT_EQ(APInt(32, uint64_t(-4LL)), APInt(32, "-100", 2));EXPECT_EQ(APInt(32, 0), APInt(32, "0", 8));EXPECT_EQ(APInt(32, 1), APInt(32, "1", 8));EXPECT_EQ(APInt(32, 7), APInt(32, "7", 8));EXPECT_EQ(APInt(32, 8), APInt(32, "10", 8));EXPECT_EQ(APInt(32, 15), APInt(32, "17", 8));EXPECT_EQ(APInt(32, 16), APInt(32, "20", 8));EXPECT_EQ(APInt(32, +0), APInt(32, "+0", 8));EXPECT_EQ(APInt(32, +1), APInt(32, "+1", 8));EXPECT_EQ(APInt(32, +7), APInt(32, "+7", 8));EXPECT_EQ(APInt(32, +8), APInt(32, "+10", 8));EXPECT_EQ(APInt(32, +15), APInt(32, "+17", 8));EXPECT_EQ(APInt(32, +16), APInt(32, "+20", 8));EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 8));EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 8));EXPECT_EQ(APInt(32, uint64_t(-7LL)), APInt(32, "-7", 8));EXPECT_EQ(APInt(32, uint64_t(-8LL)), APInt(32, "-10", 8));EXPECT_EQ(APInt(32, uint64_t(-15LL)), APInt(32, "-17", 8));EXPECT_EQ(APInt(32, uint64_t(-16LL)), APInt(32, "-20", 8));EXPECT_EQ(APInt(32, 0), APInt(32, "0", 10));EXPECT_EQ(APInt(32, 1), APInt(32, "1", 10));EXPECT_EQ(APInt(32, 9), APInt(32, "9", 10));EXPECT_EQ(APInt(32, 10), APInt(32, "10", 10));EXPECT_EQ(APInt(32, 19), APInt(32, "19", 10));EXPECT_EQ(APInt(32, 20), APInt(32, "20", 10));EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 10));EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 10));EXPECT_EQ(APInt(32, uint64_t(-9LL)), APInt(32, "-9", 10));EXPECT_EQ(APInt(32, uint64_t(-10LL)), APInt(32, "-10", 10));EXPECT_EQ(APInt(32, uint64_t(-19LL)), APInt(32, "-19", 10));EXPECT_EQ(APInt(32, uint64_t(-20LL)), APInt(32, "-20", 10));EXPECT_EQ(APInt(32, 0), APInt(32, "0", 16));EXPECT_EQ(APInt(32, 1), APInt(32, "1", 16));EXPECT_EQ(APInt(32, 15), APInt(32, "F", 16));EXPECT_EQ(APInt(32, 16), APInt(32, "10", 16));EXPECT_EQ(APInt(32, 31), APInt(32, "1F", 16));EXPECT_EQ(APInt(32, 32), APInt(32, "20", 16));EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 16));EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 16));EXPECT_EQ(APInt(32, uint64_t(-15LL)), APInt(32, "-F", 16));EXPECT_EQ(APInt(32, uint64_t(-16LL)), APInt(32, "-10", 16));EXPECT_EQ(APInt(32, uint64_t(-31LL)), APInt(32, "-1F", 16));EXPECT_EQ(APInt(32, uint64_t(-32LL)), APInt(32, "-20", 16));EXPECT_EQ(APInt(32, 0), APInt(32, "0", 36));EXPECT_EQ(APInt(32, 1), APInt(32, "1", 36));EXPECT_EQ(APInt(32, 35), APInt(32, "Z", 36));EXPECT_EQ(APInt(32, 36), APInt(32, "10", 36));EXPECT_EQ(APInt(32, 71), APInt(32, "1Z", 36));EXPECT_EQ(APInt(32, 72), APInt(32, "20", 36));EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 36));EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 36));EXPECT_EQ(APInt(32, uint64_t(-35LL)), APInt(32, "-Z", 36));EXPECT_EQ(APInt(32, uint64_t(-36LL)), APInt(32, "-10", 36));EXPECT_EQ(APInt(32, uint64_t(-71LL)), APInt(32, "-1Z", 36));EXPECT_EQ(APInt(32, uint64_t(-72LL)), APInt(32, "-20", 36));}TEST(APIntTest, SaturatingMath) {APInt AP_10 = APInt(8, 10);APInt AP_42 = APInt(8, 42);APInt AP_100 = APInt(8, 100);APInt AP_200 = APInt(8, 200);EXPECT_EQ(APInt(8, 100), AP_100.truncUSat(8));EXPECT_EQ(APInt(7, 100), AP_100.truncUSat(7));EXPECT_EQ(APInt(6, 63), AP_100.truncUSat(6));EXPECT_EQ(APInt(5, 31), AP_100.truncUSat(5));EXPECT_EQ(APInt(8, 200), AP_200.truncUSat(8));EXPECT_EQ(APInt(7, 127), AP_200.truncUSat(7));EXPECT_EQ(APInt(6, 63), AP_200.truncUSat(6));EXPECT_EQ(APInt(5, 31), AP_200.truncUSat(5));EXPECT_EQ(APInt(8, 42), AP_42.truncSSat(8));EXPECT_EQ(APInt(7, 42), AP_42.truncSSat(7));EXPECT_EQ(APInt(6, 31), AP_42.truncSSat(6));EXPECT_EQ(APInt(5, 15), AP_42.truncSSat(5));EXPECT_EQ(APInt(8, -56), AP_200.truncSSat(8));EXPECT_EQ(APInt(7, -56), AP_200.truncSSat(7));EXPECT_EQ(APInt(6, -32), AP_200.truncSSat(6));EXPECT_EQ(APInt(5, -16), AP_200.truncSSat(5));EXPECT_EQ(APInt(8, 200), AP_100.uadd_sat(AP_100));EXPECT_EQ(APInt(8, 255), AP_100.uadd_sat(AP_200));EXPECT_EQ(APInt(8, 255), APInt(8, 255).uadd_sat(APInt(8, 255)));EXPECT_EQ(APInt(8, 110), AP_10.sadd_sat(AP_100));EXPECT_EQ(APInt(8, 127), AP_100.sadd_sat(AP_100));EXPECT_EQ(APInt(8, -128), (-AP_100).sadd_sat(-AP_100));EXPECT_EQ(APInt(8, -128), APInt(8, -128).sadd_sat(APInt(8, -128)));EXPECT_EQ(APInt(8, 90), AP_100.usub_sat(AP_10));EXPECT_EQ(APInt(8, 0), AP_100.usub_sat(AP_200));EXPECT_EQ(APInt(8, 0), APInt(8, 0).usub_sat(APInt(8, 255)));EXPECT_EQ(APInt(8, -90), AP_10.ssub_sat(AP_100));EXPECT_EQ(APInt(8, 127), AP_100.ssub_sat(-AP_100));EXPECT_EQ(APInt(8, -128), (-AP_100).ssub_sat(AP_100));EXPECT_EQ(APInt(8, -128), APInt(8, -128).ssub_sat(APInt(8, 127)));EXPECT_EQ(APInt(8, 250), APInt(8, 50).umul_sat(APInt(8, 5)));EXPECT_EQ(APInt(8, 255), APInt(8, 50).umul_sat(APInt(8, 6)));EXPECT_EQ(APInt(8, 255), APInt(8, -128).umul_sat(APInt(8, 3)));EXPECT_EQ(APInt(8, 255), APInt(8, 3).umul_sat(APInt(8, -128)));EXPECT_EQ(APInt(8, 255), APInt(8, -128).umul_sat(APInt(8, -128)));EXPECT_EQ(APInt(8, 125), APInt(8, 25).smul_sat(APInt(8, 5)));EXPECT_EQ(APInt(8, 127), APInt(8, 25).smul_sat(APInt(8, 6)));EXPECT_EQ(APInt(8, 127), APInt(8, 127).smul_sat(APInt(8, 127)));EXPECT_EQ(APInt(8, -125), APInt(8, -25).smul_sat(APInt(8, 5)));EXPECT_EQ(APInt(8, -125), APInt(8, 25).smul_sat(APInt(8, -5)));EXPECT_EQ(APInt(8, 125), APInt(8, -25).smul_sat(APInt(8, -5)));EXPECT_EQ(APInt(8, 125), APInt(8, 25).smul_sat(APInt(8, 5)));EXPECT_EQ(APInt(8, -128), APInt(8, -25).smul_sat(APInt(8, 6)));EXPECT_EQ(APInt(8, -128), APInt(8, 25).smul_sat(APInt(8, -6)));EXPECT_EQ(APInt(8, 127), APInt(8, -25).smul_sat(APInt(8, -6)));EXPECT_EQ(APInt(8, 127), APInt(8, 25).smul_sat(APInt(8, 6)));EXPECT_EQ(APInt(8, 128), APInt(8, 4).ushl_sat(APInt(8, 5)));EXPECT_EQ(APInt(8, 255), APInt(8, 4).ushl_sat(APInt(8, 6)));EXPECT_EQ(APInt(8, 128), APInt(8, 1).ushl_sat(APInt(8, 7)));EXPECT_EQ(APInt(8, 255), APInt(8, 1).ushl_sat(APInt(8, 8)));EXPECT_EQ(APInt(8, 255), APInt(8, -128).ushl_sat(APInt(8, 2)));EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, 2)));EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, -2)));EXPECT_EQ(APInt(8, 64), APInt(8, 4).sshl_sat(APInt(8, 4)));EXPECT_EQ(APInt(8, 127), APInt(8, 4).sshl_sat(APInt(8, 5)));EXPECT_EQ(APInt(8, 127), APInt(8, 1).sshl_sat(APInt(8, 8)));EXPECT_EQ(APInt(8, -64), APInt(8, -4).sshl_sat(APInt(8, 4)));EXPECT_EQ(APInt(8, -128), APInt(8, -4).sshl_sat(APInt(8, 5)));EXPECT_EQ(APInt(8, -128), APInt(8, -4).sshl_sat(APInt(8, 6)));EXPECT_EQ(APInt(8, -128), APInt(8, -1).sshl_sat(APInt(8, 7)));EXPECT_EQ(APInt(8, -128), APInt(8, -1).sshl_sat(APInt(8, 8)));}TEST(APIntTest, FromArray) {EXPECT_EQ(APInt(32, uint64_t(1)), APInt(32, ArrayRef<uint64_t>(1)));}TEST(APIntTest, StringBitsNeeded2) {EXPECT_EQ(1U, APInt::getBitsNeeded( "0", 2));EXPECT_EQ(1U, APInt::getBitsNeeded( "1", 2));EXPECT_EQ(2U, APInt::getBitsNeeded( "10", 2));EXPECT_EQ(2U, APInt::getBitsNeeded( "11", 2));EXPECT_EQ(3U, APInt::getBitsNeeded("100", 2));EXPECT_EQ(1U, APInt::getBitsNeeded( "+0", 2));EXPECT_EQ(1U, APInt::getBitsNeeded( "+1", 2));EXPECT_EQ(2U, APInt::getBitsNeeded( "+10", 2));EXPECT_EQ(2U, APInt::getBitsNeeded( "+11", 2));EXPECT_EQ(3U, APInt::getBitsNeeded("+100", 2));EXPECT_EQ(2U, APInt::getBitsNeeded( "-0", 2));EXPECT_EQ(2U, APInt::getBitsNeeded( "-1", 2));EXPECT_EQ(3U, APInt::getBitsNeeded( "-10", 2));EXPECT_EQ(3U, APInt::getBitsNeeded( "-11", 2));EXPECT_EQ(4U, APInt::getBitsNeeded("-100", 2));}TEST(APIntTest, StringBitsNeeded8) {EXPECT_EQ(3U, APInt::getBitsNeeded( "0", 8));EXPECT_EQ(3U, APInt::getBitsNeeded( "7", 8));EXPECT_EQ(6U, APInt::getBitsNeeded("10", 8));EXPECT_EQ(6U, APInt::getBitsNeeded("17", 8));EXPECT_EQ(6U, APInt::getBitsNeeded("20", 8));EXPECT_EQ(3U, APInt::getBitsNeeded( "+0", 8));EXPECT_EQ(3U, APInt::getBitsNeeded( "+7", 8));EXPECT_EQ(6U, APInt::getBitsNeeded("+10", 8));EXPECT_EQ(6U, APInt::getBitsNeeded("+17", 8));EXPECT_EQ(6U, APInt::getBitsNeeded("+20", 8));EXPECT_EQ(4U, APInt::getBitsNeeded( "-0", 8));EXPECT_EQ(4U, APInt::getBitsNeeded( "-7", 8));EXPECT_EQ(7U, APInt::getBitsNeeded("-10", 8));EXPECT_EQ(7U, APInt::getBitsNeeded("-17", 8));EXPECT_EQ(7U, APInt::getBitsNeeded("-20", 8));}TEST(APIntTest, StringBitsNeeded10) {EXPECT_EQ(1U, APInt::getBitsNeeded( "0", 10));EXPECT_EQ(2U, APInt::getBitsNeeded( "3", 10));EXPECT_EQ(4U, APInt::getBitsNeeded( "9", 10));EXPECT_EQ(4U, APInt::getBitsNeeded("10", 10));EXPECT_EQ(5U, APInt::getBitsNeeded("19", 10));EXPECT_EQ(5U, APInt::getBitsNeeded("20", 10));EXPECT_EQ(1U, APInt::getBitsNeeded( "+0", 10));EXPECT_EQ(4U, APInt::getBitsNeeded( "+9", 10));EXPECT_EQ(4U, APInt::getBitsNeeded("+10", 10));EXPECT_EQ(5U, APInt::getBitsNeeded("+19", 10));EXPECT_EQ(5U, APInt::getBitsNeeded("+20", 10));EXPECT_EQ(2U, APInt::getBitsNeeded( "-0", 10));EXPECT_EQ(5U, APInt::getBitsNeeded( "-9", 10));EXPECT_EQ(5U, APInt::getBitsNeeded("-10", 10));EXPECT_EQ(6U, APInt::getBitsNeeded("-19", 10));EXPECT_EQ(6U, APInt::getBitsNeeded("-20", 10));EXPECT_EQ(1U, APInt::getBitsNeeded("-1", 10));EXPECT_EQ(2U, APInt::getBitsNeeded("-2", 10));EXPECT_EQ(3U, APInt::getBitsNeeded("-4", 10));EXPECT_EQ(4U, APInt::getBitsNeeded("-8", 10));EXPECT_EQ(5U, APInt::getBitsNeeded("-16", 10));EXPECT_EQ(6U, APInt::getBitsNeeded("-23", 10));EXPECT_EQ(6U, APInt::getBitsNeeded("-32", 10));EXPECT_EQ(7U, APInt::getBitsNeeded("-64", 10));EXPECT_EQ(8U, APInt::getBitsNeeded("-127", 10));EXPECT_EQ(8U, APInt::getBitsNeeded("-128", 10));EXPECT_EQ(9U, APInt::getBitsNeeded("-255", 10));EXPECT_EQ(9U, APInt::getBitsNeeded("-256", 10));EXPECT_EQ(10U, APInt::getBitsNeeded("-512", 10));EXPECT_EQ(11U, APInt::getBitsNeeded("-1024", 10));EXPECT_EQ(12U, APInt::getBitsNeeded("-1025", 10));}TEST(APIntTest, StringBitsNeeded16) {EXPECT_EQ(4U, APInt::getBitsNeeded( "0", 16));EXPECT_EQ(4U, APInt::getBitsNeeded( "F", 16));EXPECT_EQ(8U, APInt::getBitsNeeded("10", 16));EXPECT_EQ(8U, APInt::getBitsNeeded("1F", 16));EXPECT_EQ(8U, APInt::getBitsNeeded("20", 16));EXPECT_EQ(4U, APInt::getBitsNeeded( "+0", 16));EXPECT_EQ(4U, APInt::getBitsNeeded( "+F", 16));EXPECT_EQ(8U, APInt::getBitsNeeded("+10", 16));EXPECT_EQ(8U, APInt::getBitsNeeded("+1F", 16));EXPECT_EQ(8U, APInt::getBitsNeeded("+20", 16));EXPECT_EQ(5U, APInt::getBitsNeeded( "-0", 16));EXPECT_EQ(5U, APInt::getBitsNeeded( "-F", 16));EXPECT_EQ(9U, APInt::getBitsNeeded("-10", 16));EXPECT_EQ(9U, APInt::getBitsNeeded("-1F", 16));EXPECT_EQ(9U, APInt::getBitsNeeded("-20", 16));}TEST(APIntTest, toString) {SmallString<16> S;bool isSigned;APInt(8, 0).toString(S, 2, true, true);EXPECT_EQ(std::string(S), "0b0");S.clear();APInt(8, 0).toString(S, 8, true, true);EXPECT_EQ(std::string(S), "00");S.clear();APInt(8, 0).toString(S, 10, true, true);EXPECT_EQ(std::string(S), "0");S.clear();APInt(8, 0).toString(S, 16, true, true);EXPECT_EQ(std::string(S), "0x0");S.clear();APInt(8, 0).toString(S, 36, true, false);EXPECT_EQ(std::string(S), "0");S.clear();isSigned = false;APInt(8, 255, isSigned).toString(S, 2, isSigned, true);EXPECT_EQ(std::string(S), "0b11111111");S.clear();APInt(8, 255, isSigned).toString(S, 8, isSigned, true);EXPECT_EQ(std::string(S), "0377");S.clear();APInt(8, 255, isSigned).toString(S, 10, isSigned, true);EXPECT_EQ(std::string(S), "255");S.clear();APInt(8, 255, isSigned).toString(S, 16, isSigned, true);EXPECT_EQ(std::string(S), "0xFF");S.clear();APInt(8, 255, isSigned).toString(S, 36, isSigned, false);EXPECT_EQ(std::string(S), "73");S.clear();isSigned = true;APInt(8, 255, isSigned).toString(S, 2, isSigned, true);EXPECT_EQ(std::string(S), "-0b1");S.clear();APInt(8, 255, isSigned).toString(S, 8, isSigned, true);EXPECT_EQ(std::string(S), "-01");S.clear();APInt(8, 255, isSigned).toString(S, 10, isSigned, true);EXPECT_EQ(std::string(S), "-1");S.clear();APInt(8, 255, isSigned).toString(S, 16, isSigned, true);EXPECT_EQ(std::string(S), "-0x1");S.clear();APInt(8, 255, isSigned).toString(S, 36, isSigned, false);EXPECT_EQ(std::string(S), "-1");S.clear();}TEST(APIntTest, Log2) {EXPECT_EQ(APInt(15, 7).logBase2(), 2U);EXPECT_EQ(APInt(15, 7).ceilLogBase2(), 3U);EXPECT_EQ(APInt(15, 7).exactLogBase2(), -1);EXPECT_EQ(APInt(15, 8).logBase2(), 3U);EXPECT_EQ(APInt(15, 8).ceilLogBase2(), 3U);EXPECT_EQ(APInt(15, 8).exactLogBase2(), 3);EXPECT_EQ(APInt(15, 9).logBase2(), 3U);EXPECT_EQ(APInt(15, 9).ceilLogBase2(), 4U);EXPECT_EQ(APInt(15, 9).exactLogBase2(), -1);}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST(APIntTest, StringDeath) {EXPECT_DEATH((void)APInt(32, "", 0), "Invalid string length");EXPECT_DEATH((void)APInt(32, "0", 0), "Radix should be 2, 8, 10, 16, or 36!");EXPECT_DEATH((void)APInt(32, "", 10), "Invalid string length");EXPECT_DEATH((void)APInt(32, "-", 10), "String is only a sign, needs a value.");EXPECT_DEATH((void)APInt(1, "1234", 10), "Insufficient bit width");EXPECT_DEATH((void)APInt(32, "\0", 10), "Invalid string length");EXPECT_DEATH((void)APInt(32, StringRef("1\02", 3), 10), "Invalid character in digit string");EXPECT_DEATH((void)APInt(32, "1L", 10), "Invalid character in digit string");}#endif#endifTEST(APIntTest, mul_clear) {APInt ValA(65, -1ULL);APInt ValB(65, 4);APInt ValC(65, 0);ValC = ValA * ValB;ValA *= ValB;SmallString<16> StrA, StrC;ValA.toString(StrA, 10, false);ValC.toString(StrC, 10, false);EXPECT_EQ(std::string(StrA), std::string(StrC));}TEST(APIntTest, Rotate) {EXPECT_EQ(APInt(8, 1), APInt(8, 1).rotl(0));EXPECT_EQ(APInt(8, 2), APInt(8, 1).rotl(1));EXPECT_EQ(APInt(8, 4), APInt(8, 1).rotl(2));EXPECT_EQ(APInt(8, 16), APInt(8, 1).rotl(4));EXPECT_EQ(APInt(8, 1), APInt(8, 1).rotl(8));EXPECT_EQ(APInt(8, 16), APInt(8, 16).rotl(0));EXPECT_EQ(APInt(8, 32), APInt(8, 16).rotl(1));EXPECT_EQ(APInt(8, 64), APInt(8, 16).rotl(2));EXPECT_EQ(APInt(8, 1), APInt(8, 16).rotl(4));EXPECT_EQ(APInt(8, 16), APInt(8, 16).rotl(8));EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(33));EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(32, 33)));EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(33));EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(32, 33)));EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(33, 33)));EXPECT_EQ(APInt(32, (1 << 8)), APInt(32, 1).rotl(APInt(32, 40)));EXPECT_EQ(APInt(32, (1 << 30)), APInt(32, 1).rotl(APInt(31, 30)));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotl(APInt(31, 31)));EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotl(APInt(1, 0)));EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(1, 1)));EXPECT_EQ(APInt(32, 16), APInt(32, 1).rotl(APInt(3, 4)));EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotl(APInt(64, 64)));EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(64, 65)));EXPECT_EQ(APInt(7, 24), APInt(7, 3).rotl(APInt(7, 3)));EXPECT_EQ(APInt(7, 24), APInt(7, 3).rotl(APInt(7, 10)));EXPECT_EQ(APInt(7, 24), APInt(7, 3).rotl(APInt(5, 10)));EXPECT_EQ(APInt(7, 6), APInt(7, 3).rotl(APInt(12, 120)));EXPECT_EQ(APInt(8, 16), APInt(8, 16).rotr(0));EXPECT_EQ(APInt(8, 8), APInt(8, 16).rotr(1));EXPECT_EQ(APInt(8, 4), APInt(8, 16).rotr(2));EXPECT_EQ(APInt(8, 1), APInt(8, 16).rotr(4));EXPECT_EQ(APInt(8, 16), APInt(8, 16).rotr(8));EXPECT_EQ(APInt(8, 1), APInt(8, 1).rotr(0));EXPECT_EQ(APInt(8, 128), APInt(8, 1).rotr(1));EXPECT_EQ(APInt(8, 64), APInt(8, 1).rotr(2));EXPECT_EQ(APInt(8, 16), APInt(8, 1).rotr(4));EXPECT_EQ(APInt(8, 1), APInt(8, 1).rotr(8));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33)));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33)));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(33, 33)));EXPECT_EQ(APInt(32, (1 << 24)), APInt(32, 1).rotr(APInt(32, 40)));EXPECT_EQ(APInt(32, (1 << 2)), APInt(32, 1).rotr(APInt(31, 30)));EXPECT_EQ(APInt(32, (1 << 1)), APInt(32, 1).rotr(APInt(31, 31)));EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(1, 0)));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(1, 1)));EXPECT_EQ(APInt(32, (1 << 28)), APInt(32, 1).rotr(APInt(3, 4)));EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(64, 64)));EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(64, 65)));EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 3)));EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 10)));EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(5, 10)));EXPECT_EQ(APInt(7, 65), APInt(7, 3).rotr(APInt(12, 120)));APInt Big(256, "00004000800000000000000000003fff8000000000000003", 16);APInt Rot(256, "3fff80000000000000030000000000000000000040008000", 16);EXPECT_EQ(Rot, Big.rotr(144));EXPECT_EQ(APInt(32, 8), APInt(32, 1).rotl(Big));EXPECT_EQ(APInt(32, (1 << 29)), APInt(32, 1).rotr(Big));}TEST(APIntTest, Splat) {APInt ValA(8, 0x01);EXPECT_EQ(ValA, APInt::getSplat(8, ValA));EXPECT_EQ(APInt(64, 0x0101010101010101ULL), APInt::getSplat(64, ValA));APInt ValB(3, 5);EXPECT_EQ(APInt(4, 0xD), APInt::getSplat(4, ValB));EXPECT_EQ(APInt(15, 0xDB6D), APInt::getSplat(15, ValB));}TEST(APIntTest, tcDecrement) {// Test single word decrement.// No out borrow.{APInt::WordType singleWord = ~APInt::WordType(0) << (APInt::APINT_BITS_PER_WORD - 1);APInt::WordType carry = APInt::tcDecrement(&singleWord, 1);EXPECT_EQ(carry, APInt::WordType(0));EXPECT_EQ(singleWord, ~APInt::WordType(0) >> 1);}// With out borrow.{APInt::WordType singleWord = 0;APInt::WordType carry = APInt::tcDecrement(&singleWord, 1);EXPECT_EQ(carry, APInt::WordType(1));EXPECT_EQ(singleWord, ~APInt::WordType(0));}// Test multiword decrement.// No across word borrow, no out borrow.{APInt::WordType test[4] = {0x1, 0x1, 0x1, 0x1};APInt::WordType expected[4] = {0x0, 0x1, 0x1, 0x1};APInt::tcDecrement(test, 4);EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0);}// 1 across word borrow, no out borrow.{APInt::WordType test[4] = {0x0, 0xF, 0x1, 0x1};APInt::WordType expected[4] = {~APInt::WordType(0), 0xE, 0x1, 0x1};APInt::WordType carry = APInt::tcDecrement(test, 4);EXPECT_EQ(carry, APInt::WordType(0));EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0);}// 2 across word borrow, no out borrow.{APInt::WordType test[4] = {0x0, 0x0, 0xC, 0x1};APInt::WordType expected[4] = {~APInt::WordType(0), ~APInt::WordType(0), 0xB, 0x1};APInt::WordType carry = APInt::tcDecrement(test, 4);EXPECT_EQ(carry, APInt::WordType(0));EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0);}// 3 across word borrow, no out borrow.{APInt::WordType test[4] = {0x0, 0x0, 0x0, 0x1};APInt::WordType expected[4] = {~APInt::WordType(0), ~APInt::WordType(0), ~APInt::WordType(0), 0x0};APInt::WordType carry = APInt::tcDecrement(test, 4);EXPECT_EQ(carry, APInt::WordType(0));EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0);}// 3 across word borrow, with out borrow.{APInt::WordType test[4] = {0x0, 0x0, 0x0, 0x0};APInt::WordType expected[4] = {~APInt::WordType(0), ~APInt::WordType(0), ~APInt::WordType(0), ~APInt::WordType(0)};APInt::WordType carry = APInt::tcDecrement(test, 4);EXPECT_EQ(carry, APInt::WordType(1));EXPECT_EQ(APInt::tcCompare(test, expected, 4), 0);}}TEST(APIntTest, arrayAccess) {// Single word check.uint64_t E1 = 0x2CA7F46BF6569915ULL;APInt A1(64, E1);for (unsigned i = 0, e = 64; i < e; ++i) {EXPECT_EQ(bool(E1 & (1ULL << i)),A1[i]);}// Multiword check.APInt::WordType E2[4] = {0xEB6EB136591CBA21ULL,0x7B9358BD6A33F10AULL,0x7E7FFA5EADD8846ULL,0x305F341CA00B613DULL};APInt A2(APInt::APINT_BITS_PER_WORD*4, E2);for (unsigned i = 0; i < 4; ++i) {for (unsigned j = 0; j < APInt::APINT_BITS_PER_WORD; ++j) {EXPECT_EQ(bool(E2[i] & (1ULL << j)),A2[i*APInt::APINT_BITS_PER_WORD + j]);}}}TEST(APIntTest, LargeAPIntConstruction) {// Check that we can properly construct very large APInt. It is very// unlikely that people will ever do this, but it is a legal input,// so we should not crash on it.APInt A9(UINT32_MAX, 0);EXPECT_FALSE(A9.getBoolValue());}TEST(APIntTest, nearestLogBase2) {// Single word check.// Test round up.uint64_t I1 = 0x1800001;APInt A1(64, I1);EXPECT_EQ(A1.nearestLogBase2(), A1.ceilLogBase2());// Test round down.uint64_t I2 = 0x1000011;APInt A2(64, I2);EXPECT_EQ(A2.nearestLogBase2(), A2.logBase2());// Test ties round up.uint64_t I3 = 0x1800000;APInt A3(64, I3);EXPECT_EQ(A3.nearestLogBase2(), A3.ceilLogBase2());// Multiple word check.// Test round up.APInt::WordType I4[4] = {0x0, 0xF, 0x18, 0x0};APInt A4(APInt::APINT_BITS_PER_WORD*4, I4);EXPECT_EQ(A4.nearestLogBase2(), A4.ceilLogBase2());// Test round down.APInt::WordType I5[4] = {0x0, 0xF, 0x10, 0x0};APInt A5(APInt::APINT_BITS_PER_WORD*4, I5);EXPECT_EQ(A5.nearestLogBase2(), A5.logBase2());// Test ties round up.uint64_t I6[4] = {0x0, 0x0, 0x0, 0x18};APInt A6(APInt::APINT_BITS_PER_WORD*4, I6);EXPECT_EQ(A6.nearestLogBase2(), A6.ceilLogBase2());// Test BitWidth == 1 special cases.APInt A7(1, 1);EXPECT_EQ(A7.nearestLogBase2(), 0ULL);APInt A8(1, 0);EXPECT_EQ(A8.nearestLogBase2(), UINT32_MAX);// Test the zero case when we have a bit width large enough such// that the bit width is larger than UINT32_MAX-1.APInt A9(UINT32_MAX, 0);EXPECT_EQ(A9.nearestLogBase2(), UINT32_MAX);}TEST(APIntTest, IsSplat) {APInt A(32, 0x01010101);EXPECT_FALSE(A.isSplat(1));EXPECT_FALSE(A.isSplat(2));EXPECT_FALSE(A.isSplat(4));EXPECT_TRUE(A.isSplat(8));EXPECT_TRUE(A.isSplat(16));EXPECT_TRUE(A.isSplat(32));APInt B(24, 0xAAAAAA);EXPECT_FALSE(B.isSplat(1));EXPECT_TRUE(B.isSplat(2));EXPECT_TRUE(B.isSplat(4));EXPECT_TRUE(B.isSplat(8));EXPECT_TRUE(B.isSplat(24));APInt C(24, 0xABAAAB);EXPECT_FALSE(C.isSplat(1));EXPECT_FALSE(C.isSplat(2));EXPECT_FALSE(C.isSplat(4));EXPECT_FALSE(C.isSplat(8));EXPECT_TRUE(C.isSplat(24));APInt D(32, 0xABBAABBA);EXPECT_FALSE(D.isSplat(1));EXPECT_FALSE(D.isSplat(2));EXPECT_FALSE(D.isSplat(4));EXPECT_FALSE(D.isSplat(8));EXPECT_TRUE(D.isSplat(16));EXPECT_TRUE(D.isSplat(32));APInt E(32, 0);EXPECT_TRUE(E.isSplat(1));EXPECT_TRUE(E.isSplat(2));EXPECT_TRUE(E.isSplat(4));EXPECT_TRUE(E.isSplat(8));EXPECT_TRUE(E.isSplat(16));EXPECT_TRUE(E.isSplat(32));}TEST(APIntTest, isMask) {EXPECT_FALSE(APInt(32, 0x01010101).isMask());EXPECT_FALSE(APInt(32, 0xf0000000).isMask());EXPECT_FALSE(APInt(32, 0xffff0000).isMask());EXPECT_FALSE(APInt(32, 0xff << 1).isMask());for (int N : { 1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256 }) {EXPECT_FALSE(APInt(N, 0).isMask());APInt One(N, 1);for (int I = 1; I <= N; ++I) {APInt MaskVal = One.shl(I) - 1;EXPECT_TRUE(MaskVal.isMask());EXPECT_TRUE(MaskVal.isMask(I));}}}TEST(APIntTest, isShiftedMask) {EXPECT_FALSE(APInt(32, 0x01010101).isShiftedMask());EXPECT_TRUE(APInt(32, 0xf0000000).isShiftedMask());EXPECT_TRUE(APInt(32, 0xffff0000).isShiftedMask());EXPECT_TRUE(APInt(32, 0xff << 1).isShiftedMask());unsigned MaskIdx, MaskLen;EXPECT_FALSE(APInt(32, 0x01010101).isShiftedMask(MaskIdx, MaskLen));EXPECT_TRUE(APInt(32, 0xf0000000).isShiftedMask(MaskIdx, MaskLen));EXPECT_EQ(28, (int)MaskIdx);EXPECT_EQ(4, (int)MaskLen);EXPECT_TRUE(APInt(32, 0xffff0000).isShiftedMask(MaskIdx, MaskLen));EXPECT_EQ(16, (int)MaskIdx);EXPECT_EQ(16, (int)MaskLen);EXPECT_TRUE(APInt(32, 0xff << 1).isShiftedMask(MaskIdx, MaskLen));EXPECT_EQ(1, (int)MaskIdx);EXPECT_EQ(8, (int)MaskLen);for (int N : { 1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256 }) {EXPECT_FALSE(APInt(N, 0).isShiftedMask());EXPECT_FALSE(APInt(N, 0).isShiftedMask(MaskIdx, MaskLen));APInt One(N, 1);for (int I = 1; I < N; ++I) {APInt MaskVal = One.shl(I) - 1;EXPECT_TRUE(MaskVal.isShiftedMask());EXPECT_TRUE(MaskVal.isShiftedMask(MaskIdx, MaskLen));EXPECT_EQ(0, (int)MaskIdx);EXPECT_EQ(I, (int)MaskLen);}for (int I = 1; I < N - 1; ++I) {APInt MaskVal = One.shl(I);EXPECT_TRUE(MaskVal.isShiftedMask());EXPECT_TRUE(MaskVal.isShiftedMask(MaskIdx, MaskLen));EXPECT_EQ(I, (int)MaskIdx);EXPECT_EQ(1, (int)MaskLen);}for (int I = 1; I < N; ++I) {APInt MaskVal = APInt::getHighBitsSet(N, I);EXPECT_TRUE(MaskVal.isShiftedMask());EXPECT_TRUE(MaskVal.isShiftedMask(MaskIdx, MaskLen));EXPECT_EQ(N - I, (int)MaskIdx);EXPECT_EQ(I, (int)MaskLen);}}}TEST(APIntTest, isPowerOf2) {EXPECT_FALSE(APInt(5, 0x00).isPowerOf2());EXPECT_FALSE(APInt(32, 0x11).isPowerOf2());EXPECT_TRUE(APInt(17, 0x01).isPowerOf2());EXPECT_TRUE(APInt(32, (unsigned)(0xffu << 31)).isPowerOf2());for (int N : {1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256}) {EXPECT_FALSE(APInt(N, 0).isPowerOf2());EXPECT_TRUE(APInt::getSignedMinValue(N).isPowerOf2());APInt One(N, 1);for (int I = 1; I < N - 1; ++I) {EXPECT_TRUE(APInt::getOneBitSet(N, I).isPowerOf2());APInt MaskVal = One.shl(I);EXPECT_TRUE(MaskVal.isPowerOf2());}}}TEST(APIntTest, isNegatedPowerOf2) {EXPECT_FALSE(APInt(5, 0x00).isNegatedPowerOf2());EXPECT_TRUE(APInt(15, 0x7ffe).isNegatedPowerOf2());EXPECT_TRUE(APInt(16, 0xfffc).isNegatedPowerOf2());EXPECT_TRUE(APInt(32, 0xffffffff).isNegatedPowerOf2());for (int N : {1, 2, 3, 4, 7, 8, 16, 32, 64, 127, 128, 129, 256}) {EXPECT_FALSE(APInt(N, 0).isNegatedPowerOf2());EXPECT_TRUE(APInt::getAllOnes(N).isNegatedPowerOf2());EXPECT_TRUE(APInt::getSignedMinValue(N).isNegatedPowerOf2());EXPECT_TRUE((-APInt::getSignedMinValue(N)).isNegatedPowerOf2());APInt One(N, 1);for (int I = 1; I < N - 1; ++I) {EXPECT_FALSE(APInt::getOneBitSet(N, I).isNegatedPowerOf2());EXPECT_TRUE((-APInt::getOneBitSet(N, I)).isNegatedPowerOf2());APInt MaskVal = One.shl(I);EXPECT_TRUE((-MaskVal).isNegatedPowerOf2());APInt ShiftMaskVal = One.getHighBitsSet(N, I);EXPECT_TRUE(ShiftMaskVal.isNegatedPowerOf2());}}}// Test that self-move works with EXPENSIVE_CHECKS. It calls std::shuffle which// does self-move on some platforms.#ifdef EXPENSIVE_CHECKS#if defined(__clang__)// Disable the pragma warning from versions of Clang without -Wself-move#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wunknown-pragmas"// Disable the warning that triggers on exactly what is being tested.#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wself-move"#endifTEST(APIntTest, SelfMoveAssignment) {APInt X(32, 0xdeadbeef);X = std::move(X);EXPECT_EQ(32u, X.getBitWidth());EXPECT_EQ(0xdeadbeefULL, X.getLimitedValue());uint64_t Bits[] = {0xdeadbeefdeadbeefULL, 0xdeadbeefdeadbeefULL};APInt Y(128, Bits);Y = std::move(Y);EXPECT_EQ(128u, Y.getBitWidth());EXPECT_EQ(~0ULL, Y.getLimitedValue());const uint64_t *Raw = Y.getRawData();EXPECT_EQ(2u, Y.getNumWords());EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[0]);EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[1]);}#if defined(__clang__)#pragma clang diagnostic pop#pragma clang diagnostic pop#endif#endif // EXPENSIVE_CHECKSTEST(APIntTest, byteSwap) {EXPECT_EQ(0x00000000, APInt(16, 0x0000).byteSwap());EXPECT_EQ(0x0000010f, APInt(16, 0x0f01).byteSwap());EXPECT_EQ(0x00ff8000, APInt(24, 0x0080ff).byteSwap());EXPECT_EQ(0x117700ff, APInt(32, 0xff007711).byteSwap());EXPECT_EQ(0x228811aaffULL, APInt(40, 0xffaa118822ULL).byteSwap());EXPECT_EQ(0x050403020100ULL, APInt(48, 0x000102030405ULL).byteSwap());EXPECT_EQ(0xff050403020100ULL, APInt(56, 0x000102030405ffULL).byteSwap());EXPECT_EQ(0xff050403020100aaULL, APInt(64, 0xaa000102030405ffULL).byteSwap());for (unsigned N : {16, 24, 32, 48, 56, 64, 72, 80, 96, 112, 128, 248, 256,1024, 1032, 1040}) {for (unsigned I = 0; I < N; I += 8) {APInt X = APInt::getBitsSet(N, I, I + 8);APInt Y = APInt::getBitsSet(N, N - I - 8, N - I);EXPECT_EQ(Y, X.byteSwap());EXPECT_EQ(X, Y.byteSwap());}}}TEST(APIntTest, reverseBits) {EXPECT_EQ(1, APInt(1, 1).reverseBits());EXPECT_EQ(0, APInt(1, 0).reverseBits());EXPECT_EQ(3, APInt(2, 3).reverseBits());EXPECT_EQ(3, APInt(2, 3).reverseBits());EXPECT_EQ(0xb, APInt(4, 0xd).reverseBits());EXPECT_EQ(0xd, APInt(4, 0xb).reverseBits());EXPECT_EQ(0xf, APInt(4, 0xf).reverseBits());EXPECT_EQ(0x30, APInt(7, 0x6).reverseBits());EXPECT_EQ(0x5a, APInt(7, 0x2d).reverseBits());EXPECT_EQ(0x0f, APInt(8, 0xf0).reverseBits());EXPECT_EQ(0xf0, APInt(8, 0x0f).reverseBits());EXPECT_EQ(0x0f0f, APInt(16, 0xf0f0).reverseBits());EXPECT_EQ(0xf0f0, APInt(16, 0x0f0f).reverseBits());EXPECT_EQ(0x0f0f0f0f, APInt(32, 0xf0f0f0f0).reverseBits());EXPECT_EQ(0xf0f0f0f0, APInt(32, 0x0f0f0f0f).reverseBits());EXPECT_EQ(0x402880a0 >> 1, APInt(31, 0x05011402).reverseBits());EXPECT_EQ(0x0f0f0f0f, APInt(32, 0xf0f0f0f0).reverseBits());EXPECT_EQ(0xf0f0f0f0, APInt(32, 0x0f0f0f0f).reverseBits());EXPECT_EQ(0x0f0f0f0f0f0f0f0f, APInt(64, 0xf0f0f0f0f0f0f0f0).reverseBits());EXPECT_EQ(0xf0f0f0f0f0f0f0f0, APInt(64, 0x0f0f0f0f0f0f0f0f).reverseBits());for (unsigned N : { 1, 8, 16, 24, 31, 32, 33,63, 64, 65, 127, 128, 257, 1024 }) {for (unsigned I = 0; I < N; ++I) {APInt X = APInt::getOneBitSet(N, I);APInt Y = APInt::getOneBitSet(N, N - (I + 1));EXPECT_EQ(Y, X.reverseBits());EXPECT_EQ(X, Y.reverseBits());}}}TEST(APIntTest, insertBits) {APInt iSrc(31, 0x00123456);// Direct copy.APInt i31(31, 0x76543210ull);i31.insertBits(iSrc, 0);EXPECT_EQ(static_cast<int64_t>(0x00123456ull), i31.getSExtValue());// Single word src/dst insertion.APInt i63(63, 0x01234567FFFFFFFFull);i63.insertBits(iSrc, 4);EXPECT_EQ(static_cast<int64_t>(0x012345600123456Full), i63.getSExtValue());// Zero width insert is a noop.i31.insertBits(APInt::getZeroWidth(), 1);EXPECT_EQ(static_cast<int64_t>(0x00123456ull), i31.getSExtValue());// Insert single word src into one word of dst.APInt i120(120, UINT64_MAX, true);i120.insertBits(iSrc, 8);EXPECT_EQ(static_cast<int64_t>(0xFFFFFF80123456FFull), i120.getSExtValue());// Insert single word src into two words of dst.APInt i127(127, UINT64_MAX, true);i127.insertBits(iSrc, 48);EXPECT_EQ(i127.extractBits(64, 0).getZExtValue(), 0x3456FFFFFFFFFFFFull);EXPECT_EQ(i127.extractBits(63, 64).getZExtValue(), 0x7FFFFFFFFFFF8012ull);// Insert on word boundaries.APInt i128(128, 0);i128.insertBits(APInt(64, UINT64_MAX, true), 0);i128.insertBits(APInt(64, UINT64_MAX, true), 64);EXPECT_EQ(-1, i128.getSExtValue());APInt i256(256, UINT64_MAX, true);i256.insertBits(APInt(65, 0), 0);i256.insertBits(APInt(69, 0), 64);i256.insertBits(APInt(128, 0), 128);EXPECT_EQ(0u, i256.getSExtValue());APInt i257(257, 0);i257.insertBits(APInt(96, UINT64_MAX, true), 64);EXPECT_EQ(i257.extractBits(64, 0).getZExtValue(), 0x0000000000000000ull);EXPECT_EQ(i257.extractBits(64, 64).getZExtValue(), 0xFFFFFFFFFFFFFFFFull);EXPECT_EQ(i257.extractBits(64, 128).getZExtValue(), 0x00000000FFFFFFFFull);EXPECT_EQ(i257.extractBits(65, 192).getZExtValue(), 0x0000000000000000ull);// General insertion.APInt i260(260, UINT64_MAX, true);i260.insertBits(APInt(129, 1ull << 48), 15);EXPECT_EQ(i260.extractBits(64, 0).getZExtValue(), 0x8000000000007FFFull);EXPECT_EQ(i260.extractBits(64, 64).getZExtValue(), 0x0000000000000000ull);EXPECT_EQ(i260.extractBits(64, 128).getZExtValue(), 0xFFFFFFFFFFFF0000ull);EXPECT_EQ(i260.extractBits(64, 192).getZExtValue(), 0xFFFFFFFFFFFFFFFFull);EXPECT_EQ(i260.extractBits(4, 256).getZExtValue(), 0x000000000000000Full);}TEST(APIntTest, insertBitsUInt64) {// Tests cloned from insertBits but adapted to the numBits <= 64 constraintuint64_t iSrc = 0x00123456;// Direct copy.APInt i31(31, 0x76543210ull);i31.insertBits(iSrc, 0, 31);EXPECT_EQ(static_cast<int64_t>(0x00123456ull), i31.getSExtValue());// Single word src/dst insertion.APInt i63(63, 0x01234567FFFFFFFFull);i63.insertBits(iSrc, 4, 31);EXPECT_EQ(static_cast<int64_t>(0x012345600123456Full), i63.getSExtValue());// Insert single word src into one word of dst.APInt i120(120, UINT64_MAX, true);i120.insertBits(iSrc, 8, 31);EXPECT_EQ(static_cast<int64_t>(0xFFFFFF80123456FFull), i120.getSExtValue());// Insert single word src into two words of dst.APInt i127(127, UINT64_MAX, true);i127.insertBits(iSrc, 48, 31);EXPECT_EQ(i127.extractBits(64, 0).getZExtValue(), 0x3456FFFFFFFFFFFFull);EXPECT_EQ(i127.extractBits(63, 64).getZExtValue(), 0x7FFFFFFFFFFF8012ull);// Insert on word boundaries.APInt i128(128, 0);i128.insertBits(UINT64_MAX, 0, 64);i128.insertBits(UINT64_MAX, 64, 64);EXPECT_EQ(-1, i128.getSExtValue());APInt i256(256, UINT64_MAX, true);i256.insertBits(0, 0, 64);i256.insertBits(0, 64, 1);i256.insertBits(0, 64, 64);i256.insertBits(0, 128, 5);i256.insertBits(0, 128, 64);i256.insertBits(0, 192, 64);EXPECT_EQ(0u, i256.getSExtValue());APInt i257(257, 0);i257.insertBits(APInt(96, UINT64_MAX, true), 64);EXPECT_EQ(i257.extractBitsAsZExtValue(64, 0), 0x0000000000000000ull);EXPECT_EQ(i257.extractBitsAsZExtValue(64, 64), 0xFFFFFFFFFFFFFFFFull);EXPECT_EQ(i257.extractBitsAsZExtValue(64, 128), 0x00000000FFFFFFFFull);EXPECT_EQ(i257.extractBitsAsZExtValue(64, 192), 0x0000000000000000ull);EXPECT_EQ(i257.extractBitsAsZExtValue(1, 256), 0x0000000000000000ull);// General insertion.APInt i260(260, UINT64_MAX, true);i260.insertBits(APInt(129, 1ull << 48), 15);EXPECT_EQ(i260.extractBitsAsZExtValue(64, 0), 0x8000000000007FFFull);EXPECT_EQ(i260.extractBitsAsZExtValue(64, 64), 0x0000000000000000ull);EXPECT_EQ(i260.extractBitsAsZExtValue(64, 128), 0xFFFFFFFFFFFF0000ull);EXPECT_EQ(i260.extractBitsAsZExtValue(64, 192), 0xFFFFFFFFFFFFFFFFull);EXPECT_EQ(i260.extractBitsAsZExtValue(4, 256), 0x000000000000000Full);}TEST(APIntTest, extractBits) {APInt i32(32, 0x1234567);EXPECT_EQ(0x3456, i32.extractBits(16, 4));APInt i64(64, 0x01234567FFFFFFFFull);EXPECT_EQ(0xFFFFFFFF, i64.extractBits(32, 0));EXPECT_EQ(0xFFFFFFFF, i64.trunc(32));EXPECT_EQ(0x01234567, i64.extractBits(32, 32));EXPECT_EQ(0x01234567, i64.lshr(32).trunc(32));APInt i257(257, 0xFFFFFFFFFF0000FFull, true);EXPECT_EQ(0xFFu, i257.extractBits(16, 0));EXPECT_EQ(0xFFu, i257.lshr(0).trunc(16));EXPECT_EQ((0xFFu >> 1), i257.extractBits(16, 1));EXPECT_EQ((0xFFu >> 1), i257.lshr(1).trunc(16));EXPECT_EQ(-1, i257.extractBits(32, 64).getSExtValue());EXPECT_EQ(-1, i257.lshr(64).trunc(32).getSExtValue());EXPECT_EQ(-1, i257.extractBits(128, 128).getSExtValue());EXPECT_EQ(-1, i257.lshr(128).trunc(128).getSExtValue());EXPECT_EQ(-1, i257.extractBits(66, 191).getSExtValue());EXPECT_EQ(-1, i257.lshr(191).trunc(66).getSExtValue());EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFFF80007Full),i257.extractBits(128, 1).getSExtValue());EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFFF80007Full),i257.lshr(1).trunc(128).getSExtValue());EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFFF80007Full),i257.extractBits(129, 1).getSExtValue());EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFFF80007Full),i257.lshr(1).trunc(129).getSExtValue());EXPECT_EQ(APInt(48, 0),APInt(144, "281474976710655", 10).extractBits(48, 48));EXPECT_EQ(APInt(48, 0),APInt(144, "281474976710655", 10).lshr(48).trunc(48));EXPECT_EQ(APInt(48, 0x0000ffffffffffffull),APInt(144, "281474976710655", 10).extractBits(48, 0));EXPECT_EQ(APInt(48, 0x0000ffffffffffffull),APInt(144, "281474976710655", 10).lshr(0).trunc(48));EXPECT_EQ(APInt(48, 0x00007fffffffffffull),APInt(144, "281474976710655", 10).extractBits(48, 1));EXPECT_EQ(APInt(48, 0x00007fffffffffffull),APInt(144, "281474976710655", 10).lshr(1).trunc(48));}TEST(APIntTest, extractBitsAsZExtValue) {// Tests based on extractBitsAPInt i32(32, 0x1234567);EXPECT_EQ(0x3456u, i32.extractBitsAsZExtValue(16, 4));APInt i257(257, 0xFFFFFFFFFF0000FFull, true);EXPECT_EQ(0xFFu, i257.extractBitsAsZExtValue(16, 0));EXPECT_EQ((0xFFu >> 1), i257.extractBitsAsZExtValue(16, 1));EXPECT_EQ(0xFFFFFFFFull, i257.extractBitsAsZExtValue(32, 64));EXPECT_EQ(0xFFFFFFFFFFFFFFFFull, i257.extractBitsAsZExtValue(64, 128));EXPECT_EQ(0xFFFFFFFFFFFFFFFFull, i257.extractBitsAsZExtValue(64, 192));EXPECT_EQ(0xFFFFFFFFFFFFFFFFull, i257.extractBitsAsZExtValue(64, 191));EXPECT_EQ(0x3u, i257.extractBitsAsZExtValue(2, 255));EXPECT_EQ(0xFFFFFFFFFF80007Full, i257.extractBitsAsZExtValue(64, 1));EXPECT_EQ(0xFFFFFFFFFFFFFFFFull, i257.extractBitsAsZExtValue(64, 65));EXPECT_EQ(0xFFFFFFFFFF80007Full, i257.extractBitsAsZExtValue(64, 1));EXPECT_EQ(0xFFFFFFFFFFFFFFFFull, i257.extractBitsAsZExtValue(64, 65));EXPECT_EQ(0x1ull, i257.extractBitsAsZExtValue(1, 129));EXPECT_EQ(APInt(48, 0),APInt(144, "281474976710655", 10).extractBitsAsZExtValue(48, 48));EXPECT_EQ(APInt(48, 0x0000ffffffffffffull),APInt(144, "281474976710655", 10).extractBitsAsZExtValue(48, 0));EXPECT_EQ(APInt(48, 0x00007fffffffffffull),APInt(144, "281474976710655", 10).extractBitsAsZExtValue(48, 1));}TEST(APIntTest, getLowBitsSet) {APInt i128lo64 = APInt::getLowBitsSet(128, 64);EXPECT_EQ(0u, i128lo64.countLeadingOnes());EXPECT_EQ(64u, i128lo64.countLeadingZeros());EXPECT_EQ(64u, i128lo64.getActiveBits());EXPECT_EQ(0u, i128lo64.countTrailingZeros());EXPECT_EQ(64u, i128lo64.countTrailingOnes());EXPECT_EQ(64u, i128lo64.countPopulation());}TEST(APIntTest, getBitsSet) {APInt i64hi1lo1 = APInt::getBitsSet(64, 1, 63);EXPECT_EQ(0u, i64hi1lo1.countLeadingOnes());EXPECT_EQ(1u, i64hi1lo1.countLeadingZeros());EXPECT_EQ(63u, i64hi1lo1.getActiveBits());EXPECT_EQ(1u, i64hi1lo1.countTrailingZeros());EXPECT_EQ(0u, i64hi1lo1.countTrailingOnes());EXPECT_EQ(62u, i64hi1lo1.countPopulation());APInt i127hi1lo1 = APInt::getBitsSet(127, 1, 126);EXPECT_EQ(0u, i127hi1lo1.countLeadingOnes());EXPECT_EQ(1u, i127hi1lo1.countLeadingZeros());EXPECT_EQ(126u, i127hi1lo1.getActiveBits());EXPECT_EQ(1u, i127hi1lo1.countTrailingZeros());EXPECT_EQ(0u, i127hi1lo1.countTrailingOnes());EXPECT_EQ(125u, i127hi1lo1.countPopulation());}TEST(APIntTest, getBitsSetWithWrap) {APInt i64hi1lo1 = APInt::getBitsSetWithWrap(64, 1, 63);EXPECT_EQ(0u, i64hi1lo1.countLeadingOnes());EXPECT_EQ(1u, i64hi1lo1.countLeadingZeros());EXPECT_EQ(63u, i64hi1lo1.getActiveBits());EXPECT_EQ(1u, i64hi1lo1.countTrailingZeros());EXPECT_EQ(0u, i64hi1lo1.countTrailingOnes());EXPECT_EQ(62u, i64hi1lo1.countPopulation());APInt i127hi1lo1 = APInt::getBitsSetWithWrap(127, 1, 126);EXPECT_EQ(0u, i127hi1lo1.countLeadingOnes());EXPECT_EQ(1u, i127hi1lo1.countLeadingZeros());EXPECT_EQ(126u, i127hi1lo1.getActiveBits());EXPECT_EQ(1u, i127hi1lo1.countTrailingZeros());EXPECT_EQ(0u, i127hi1lo1.countTrailingOnes());EXPECT_EQ(125u, i127hi1lo1.countPopulation());APInt i64hi1lo1wrap = APInt::getBitsSetWithWrap(64, 63, 1);EXPECT_EQ(1u, i64hi1lo1wrap.countLeadingOnes());EXPECT_EQ(0u, i64hi1lo1wrap.countLeadingZeros());EXPECT_EQ(64u, i64hi1lo1wrap.getActiveBits());EXPECT_EQ(0u, i64hi1lo1wrap.countTrailingZeros());EXPECT_EQ(1u, i64hi1lo1wrap.countTrailingOnes());EXPECT_EQ(2u, i64hi1lo1wrap.countPopulation());APInt i127hi1lo1wrap = APInt::getBitsSetWithWrap(127, 126, 1);EXPECT_EQ(1u, i127hi1lo1wrap.countLeadingOnes());EXPECT_EQ(0u, i127hi1lo1wrap.countLeadingZeros());EXPECT_EQ(127u, i127hi1lo1wrap.getActiveBits());EXPECT_EQ(0u, i127hi1lo1wrap.countTrailingZeros());EXPECT_EQ(1u, i127hi1lo1wrap.countTrailingOnes());EXPECT_EQ(2u, i127hi1lo1wrap.countPopulation());APInt i32hiequallowrap = APInt::getBitsSetWithWrap(32, 10, 10);EXPECT_EQ(32u, i32hiequallowrap.countLeadingOnes());EXPECT_EQ(0u, i32hiequallowrap.countLeadingZeros());EXPECT_EQ(32u, i32hiequallowrap.getActiveBits());EXPECT_EQ(0u, i32hiequallowrap.countTrailingZeros());EXPECT_EQ(32u, i32hiequallowrap.countTrailingOnes());EXPECT_EQ(32u, i32hiequallowrap.countPopulation());}TEST(APIntTest, getHighBitsSet) {APInt i64hi32 = APInt::getHighBitsSet(64, 32);EXPECT_EQ(32u, i64hi32.countLeadingOnes());EXPECT_EQ(0u, i64hi32.countLeadingZeros());EXPECT_EQ(64u, i64hi32.getActiveBits());EXPECT_EQ(32u, i64hi32.countTrailingZeros());EXPECT_EQ(0u, i64hi32.countTrailingOnes());EXPECT_EQ(32u, i64hi32.countPopulation());}TEST(APIntTest, getBitsSetFrom) {APInt i64hi31 = APInt::getBitsSetFrom(64, 33);EXPECT_EQ(31u, i64hi31.countLeadingOnes());EXPECT_EQ(0u, i64hi31.countLeadingZeros());EXPECT_EQ(64u, i64hi31.getActiveBits());EXPECT_EQ(33u, i64hi31.countTrailingZeros());EXPECT_EQ(0u, i64hi31.countTrailingOnes());EXPECT_EQ(31u, i64hi31.countPopulation());}TEST(APIntTest, setLowBits) {APInt i64lo32(64, 0);i64lo32.setLowBits(32);EXPECT_EQ(0u, i64lo32.countLeadingOnes());EXPECT_EQ(32u, i64lo32.countLeadingZeros());EXPECT_EQ(32u, i64lo32.getActiveBits());EXPECT_EQ(0u, i64lo32.countTrailingZeros());EXPECT_EQ(32u, i64lo32.countTrailingOnes());EXPECT_EQ(32u, i64lo32.countPopulation());APInt i128lo64(128, 0);i128lo64.setLowBits(64);EXPECT_EQ(0u, i128lo64.countLeadingOnes());EXPECT_EQ(64u, i128lo64.countLeadingZeros());EXPECT_EQ(64u, i128lo64.getActiveBits());EXPECT_EQ(0u, i128lo64.countTrailingZeros());EXPECT_EQ(64u, i128lo64.countTrailingOnes());EXPECT_EQ(64u, i128lo64.countPopulation());APInt i128lo24(128, 0);i128lo24.setLowBits(24);EXPECT_EQ(0u, i128lo24.countLeadingOnes());EXPECT_EQ(104u, i128lo24.countLeadingZeros());EXPECT_EQ(24u, i128lo24.getActiveBits());EXPECT_EQ(0u, i128lo24.countTrailingZeros());EXPECT_EQ(24u, i128lo24.countTrailingOnes());EXPECT_EQ(24u, i128lo24.countPopulation());APInt i128lo104(128, 0);i128lo104.setLowBits(104);EXPECT_EQ(0u, i128lo104.countLeadingOnes());EXPECT_EQ(24u, i128lo104.countLeadingZeros());EXPECT_EQ(104u, i128lo104.getActiveBits());EXPECT_EQ(0u, i128lo104.countTrailingZeros());EXPECT_EQ(104u, i128lo104.countTrailingOnes());EXPECT_EQ(104u, i128lo104.countPopulation());APInt i128lo0(128, 0);i128lo0.setLowBits(0);EXPECT_EQ(0u, i128lo0.countLeadingOnes());EXPECT_EQ(128u, i128lo0.countLeadingZeros());EXPECT_EQ(0u, i128lo0.getActiveBits());EXPECT_EQ(128u, i128lo0.countTrailingZeros());EXPECT_EQ(0u, i128lo0.countTrailingOnes());EXPECT_EQ(0u, i128lo0.countPopulation());APInt i80lo79(80, 0);i80lo79.setLowBits(79);EXPECT_EQ(0u, i80lo79.countLeadingOnes());EXPECT_EQ(1u, i80lo79.countLeadingZeros());EXPECT_EQ(79u, i80lo79.getActiveBits());EXPECT_EQ(0u, i80lo79.countTrailingZeros());EXPECT_EQ(79u, i80lo79.countTrailingOnes());EXPECT_EQ(79u, i80lo79.countPopulation());}TEST(APIntTest, setHighBits) {APInt i64hi32(64, 0);i64hi32.setHighBits(32);EXPECT_EQ(32u, i64hi32.countLeadingOnes());EXPECT_EQ(0u, i64hi32.countLeadingZeros());EXPECT_EQ(64u, i64hi32.getActiveBits());EXPECT_EQ(32u, i64hi32.countTrailingZeros());EXPECT_EQ(0u, i64hi32.countTrailingOnes());EXPECT_EQ(32u, i64hi32.countPopulation());APInt i128hi64(128, 0);i128hi64.setHighBits(64);EXPECT_EQ(64u, i128hi64.countLeadingOnes());EXPECT_EQ(0u, i128hi64.countLeadingZeros());EXPECT_EQ(128u, i128hi64.getActiveBits());EXPECT_EQ(64u, i128hi64.countTrailingZeros());EXPECT_EQ(0u, i128hi64.countTrailingOnes());EXPECT_EQ(64u, i128hi64.countPopulation());APInt i128hi24(128, 0);i128hi24.setHighBits(24);EXPECT_EQ(24u, i128hi24.countLeadingOnes());EXPECT_EQ(0u, i128hi24.countLeadingZeros());EXPECT_EQ(128u, i128hi24.getActiveBits());EXPECT_EQ(104u, i128hi24.countTrailingZeros());EXPECT_EQ(0u, i128hi24.countTrailingOnes());EXPECT_EQ(24u, i128hi24.countPopulation());APInt i128hi104(128, 0);i128hi104.setHighBits(104);EXPECT_EQ(104u, i128hi104.countLeadingOnes());EXPECT_EQ(0u, i128hi104.countLeadingZeros());EXPECT_EQ(128u, i128hi104.getActiveBits());EXPECT_EQ(24u, i128hi104.countTrailingZeros());EXPECT_EQ(0u, i128hi104.countTrailingOnes());EXPECT_EQ(104u, i128hi104.countPopulation());APInt i128hi0(128, 0);i128hi0.setHighBits(0);EXPECT_EQ(0u, i128hi0.countLeadingOnes());EXPECT_EQ(128u, i128hi0.countLeadingZeros());EXPECT_EQ(0u, i128hi0.getActiveBits());EXPECT_EQ(128u, i128hi0.countTrailingZeros());EXPECT_EQ(0u, i128hi0.countTrailingOnes());EXPECT_EQ(0u, i128hi0.countPopulation());APInt i80hi1(80, 0);i80hi1.setHighBits(1);EXPECT_EQ(1u, i80hi1.countLeadingOnes());EXPECT_EQ(0u, i80hi1.countLeadingZeros());EXPECT_EQ(80u, i80hi1.getActiveBits());EXPECT_EQ(79u, i80hi1.countTrailingZeros());EXPECT_EQ(0u, i80hi1.countTrailingOnes());EXPECT_EQ(1u, i80hi1.countPopulation());APInt i32hi16(32, 0);i32hi16.setHighBits(16);EXPECT_EQ(16u, i32hi16.countLeadingOnes());EXPECT_EQ(0u, i32hi16.countLeadingZeros());EXPECT_EQ(32u, i32hi16.getActiveBits());EXPECT_EQ(16u, i32hi16.countTrailingZeros());EXPECT_EQ(0u, i32hi16.countTrailingOnes());EXPECT_EQ(16u, i32hi16.countPopulation());}TEST(APIntTest, setBitsFrom) {APInt i64from63(64, 0);i64from63.setBitsFrom(63);EXPECT_EQ(1u, i64from63.countLeadingOnes());EXPECT_EQ(0u, i64from63.countLeadingZeros());EXPECT_EQ(64u, i64from63.getActiveBits());EXPECT_EQ(63u, i64from63.countTrailingZeros());EXPECT_EQ(0u, i64from63.countTrailingOnes());EXPECT_EQ(1u, i64from63.countPopulation());}TEST(APIntTest, setAllBits) {APInt i32(32, 0);i32.setAllBits();EXPECT_EQ(32u, i32.countLeadingOnes());EXPECT_EQ(0u, i32.countLeadingZeros());EXPECT_EQ(32u, i32.getActiveBits());EXPECT_EQ(0u, i32.countTrailingZeros());EXPECT_EQ(32u, i32.countTrailingOnes());EXPECT_EQ(32u, i32.countPopulation());APInt i64(64, 0);i64.setAllBits();EXPECT_EQ(64u, i64.countLeadingOnes());EXPECT_EQ(0u, i64.countLeadingZeros());EXPECT_EQ(64u, i64.getActiveBits());EXPECT_EQ(0u, i64.countTrailingZeros());EXPECT_EQ(64u, i64.countTrailingOnes());EXPECT_EQ(64u, i64.countPopulation());APInt i96(96, 0);i96.setAllBits();EXPECT_EQ(96u, i96.countLeadingOnes());EXPECT_EQ(0u, i96.countLeadingZeros());EXPECT_EQ(96u, i96.getActiveBits());EXPECT_EQ(0u, i96.countTrailingZeros());EXPECT_EQ(96u, i96.countTrailingOnes());EXPECT_EQ(96u, i96.countPopulation());APInt i128(128, 0);i128.setAllBits();EXPECT_EQ(128u, i128.countLeadingOnes());EXPECT_EQ(0u, i128.countLeadingZeros());EXPECT_EQ(128u, i128.getActiveBits());EXPECT_EQ(0u, i128.countTrailingZeros());EXPECT_EQ(128u, i128.countTrailingOnes());EXPECT_EQ(128u, i128.countPopulation());}TEST(APIntTest, getLoBits) {APInt i32(32, 0xfa);i32.setHighBits(1);EXPECT_EQ(0xa, i32.getLoBits(4));APInt i128(128, 0xfa);i128.setHighBits(1);EXPECT_EQ(0xa, i128.getLoBits(4));}TEST(APIntTest, getHiBits) {APInt i32(32, 0xfa);i32.setHighBits(2);EXPECT_EQ(0xc, i32.getHiBits(4));APInt i128(128, 0xfa);i128.setHighBits(2);EXPECT_EQ(0xc, i128.getHiBits(4));}TEST(APIntTest, clearLowBits) {APInt i64hi32 = APInt::getAllOnes(64);i64hi32.clearLowBits(32);EXPECT_EQ(32u, i64hi32.countLeadingOnes());EXPECT_EQ(0u, i64hi32.countLeadingZeros());EXPECT_EQ(64u, i64hi32.getActiveBits());EXPECT_EQ(32u, i64hi32.countTrailingZeros());EXPECT_EQ(0u, i64hi32.countTrailingOnes());EXPECT_EQ(32u, i64hi32.countPopulation());APInt i128hi64 = APInt::getAllOnes(128);i128hi64.clearLowBits(64);EXPECT_EQ(64u, i128hi64.countLeadingOnes());EXPECT_EQ(0u, i128hi64.countLeadingZeros());EXPECT_EQ(128u, i128hi64.getActiveBits());EXPECT_EQ(64u, i128hi64.countTrailingZeros());EXPECT_EQ(0u, i128hi64.countTrailingOnes());EXPECT_EQ(64u, i128hi64.countPopulation());APInt i128hi24 = APInt::getAllOnes(128);i128hi24.clearLowBits(104);EXPECT_EQ(24u, i128hi24.countLeadingOnes());EXPECT_EQ(0u, i128hi24.countLeadingZeros());EXPECT_EQ(128u, i128hi24.getActiveBits());EXPECT_EQ(104u, i128hi24.countTrailingZeros());EXPECT_EQ(0u, i128hi24.countTrailingOnes());EXPECT_EQ(24u, i128hi24.countPopulation());APInt i128hi104 = APInt::getAllOnes(128);i128hi104.clearLowBits(24);EXPECT_EQ(104u, i128hi104.countLeadingOnes());EXPECT_EQ(0u, i128hi104.countLeadingZeros());EXPECT_EQ(128u, i128hi104.getActiveBits());EXPECT_EQ(24u, i128hi104.countTrailingZeros());EXPECT_EQ(0u, i128hi104.countTrailingOnes());EXPECT_EQ(104u, i128hi104.countPopulation());APInt i128hi0 = APInt::getAllOnes(128);i128hi0.clearLowBits(128);EXPECT_EQ(0u, i128hi0.countLeadingOnes());EXPECT_EQ(128u, i128hi0.countLeadingZeros());EXPECT_EQ(0u, i128hi0.getActiveBits());EXPECT_EQ(128u, i128hi0.countTrailingZeros());EXPECT_EQ(0u, i128hi0.countTrailingOnes());EXPECT_EQ(0u, i128hi0.countPopulation());APInt i80hi1 = APInt::getAllOnes(80);i80hi1.clearLowBits(79);EXPECT_EQ(1u, i80hi1.countLeadingOnes());EXPECT_EQ(0u, i80hi1.countLeadingZeros());EXPECT_EQ(80u, i80hi1.getActiveBits());EXPECT_EQ(79u, i80hi1.countTrailingZeros());EXPECT_EQ(0u, i80hi1.countTrailingOnes());EXPECT_EQ(1u, i80hi1.countPopulation());APInt i32hi16 = APInt::getAllOnes(32);i32hi16.clearLowBits(16);EXPECT_EQ(16u, i32hi16.countLeadingOnes());EXPECT_EQ(0u, i32hi16.countLeadingZeros());EXPECT_EQ(32u, i32hi16.getActiveBits());EXPECT_EQ(16u, i32hi16.countTrailingZeros());EXPECT_EQ(0u, i32hi16.countTrailingOnes());EXPECT_EQ(16u, i32hi16.countPopulation());}TEST(APIntTest, GCD) {using APIntOps::GreatestCommonDivisor;for (unsigned Bits : {1, 2, 32, 63, 64, 65}) {// Test some corner cases near zero.APInt Zero(Bits, 0), One(Bits, 1);EXPECT_EQ(GreatestCommonDivisor(Zero, Zero), Zero);EXPECT_EQ(GreatestCommonDivisor(Zero, One), One);EXPECT_EQ(GreatestCommonDivisor(One, Zero), One);EXPECT_EQ(GreatestCommonDivisor(One, One), One);if (Bits > 1) {APInt Two(Bits, 2);EXPECT_EQ(GreatestCommonDivisor(Zero, Two), Two);EXPECT_EQ(GreatestCommonDivisor(One, Two), One);EXPECT_EQ(GreatestCommonDivisor(Two, Two), Two);// Test some corner cases near the highest representable value.APInt Max(Bits, 0);Max.setAllBits();EXPECT_EQ(GreatestCommonDivisor(Zero, Max), Max);EXPECT_EQ(GreatestCommonDivisor(One, Max), One);EXPECT_EQ(GreatestCommonDivisor(Two, Max), One);EXPECT_EQ(GreatestCommonDivisor(Max, Max), Max);APInt MaxOver2 = Max.udiv(Two);EXPECT_EQ(GreatestCommonDivisor(MaxOver2, Max), One);// Max - 1 == Max / 2 * 2, because Max is odd.EXPECT_EQ(GreatestCommonDivisor(MaxOver2, Max - 1), MaxOver2);}}// Compute the 20th Mersenne prime.const unsigned BitWidth = 4450;APInt HugePrime = APInt::getLowBitsSet(BitWidth, 4423);// 9931 and 123456 are coprime.APInt A = HugePrime * APInt(BitWidth, 9931);APInt B = HugePrime * APInt(BitWidth, 123456);APInt C = GreatestCommonDivisor(A, B);EXPECT_EQ(C, HugePrime);}TEST(APIntTest, LogicalRightShift) {APInt i256(APInt::getHighBitsSet(256, 2));i256.lshrInPlace(1);EXPECT_EQ(1U, i256.countLeadingZeros());EXPECT_EQ(253U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256.lshrInPlace(62);EXPECT_EQ(63U, i256.countLeadingZeros());EXPECT_EQ(191U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256.lshrInPlace(65);EXPECT_EQ(128U, i256.countLeadingZeros());EXPECT_EQ(126U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256.lshrInPlace(64);EXPECT_EQ(192U, i256.countLeadingZeros());EXPECT_EQ(62U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256.lshrInPlace(63);EXPECT_EQ(255U, i256.countLeadingZeros());EXPECT_EQ(0U, i256.countTrailingZeros());EXPECT_EQ(1U, i256.countPopulation());// Ensure we handle large shifts of multi-word.const APInt neg_one(128, static_cast<uint64_t>(-1), true);EXPECT_EQ(0, neg_one.lshr(128));}TEST(APIntTest, ArithmeticRightShift) {APInt i72(APInt::getHighBitsSet(72, 1));i72.ashrInPlace(46);EXPECT_EQ(47U, i72.countLeadingOnes());EXPECT_EQ(25U, i72.countTrailingZeros());EXPECT_EQ(47U, i72.countPopulation());i72 = APInt::getHighBitsSet(72, 1);i72.ashrInPlace(64);EXPECT_EQ(65U, i72.countLeadingOnes());EXPECT_EQ(7U, i72.countTrailingZeros());EXPECT_EQ(65U, i72.countPopulation());APInt i128(APInt::getHighBitsSet(128, 1));i128.ashrInPlace(64);EXPECT_EQ(65U, i128.countLeadingOnes());EXPECT_EQ(63U, i128.countTrailingZeros());EXPECT_EQ(65U, i128.countPopulation());// Ensure we handle large shifts of multi-word.const APInt signmin32(APInt::getSignedMinValue(32));EXPECT_TRUE(signmin32.ashr(32).isAllOnes());// Ensure we handle large shifts of multi-word.const APInt umax32(APInt::getSignedMaxValue(32));EXPECT_EQ(0, umax32.ashr(32));// Ensure we handle large shifts of multi-word.const APInt signmin128(APInt::getSignedMinValue(128));EXPECT_TRUE(signmin128.ashr(128).isAllOnes());// Ensure we handle large shifts of multi-word.const APInt umax128(APInt::getSignedMaxValue(128));EXPECT_EQ(0, umax128.ashr(128));}TEST(APIntTest, LeftShift) {APInt i256(APInt::getLowBitsSet(256, 2));i256 <<= 1;EXPECT_EQ(253U, i256.countLeadingZeros());EXPECT_EQ(1U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256 <<= 62;EXPECT_EQ(191U, i256.countLeadingZeros());EXPECT_EQ(63U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256 <<= 65;EXPECT_EQ(126U, i256.countLeadingZeros());EXPECT_EQ(128U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256 <<= 64;EXPECT_EQ(62U, i256.countLeadingZeros());EXPECT_EQ(192U, i256.countTrailingZeros());EXPECT_EQ(2U, i256.countPopulation());i256 <<= 63;EXPECT_EQ(0U, i256.countLeadingZeros());EXPECT_EQ(255U, i256.countTrailingZeros());EXPECT_EQ(1U, i256.countPopulation());// Ensure we handle large shifts of multi-word.const APInt neg_one(128, static_cast<uint64_t>(-1), true);EXPECT_EQ(0, neg_one.shl(128));}TEST(APIntTest, isSubsetOf) {APInt i32_1(32, 1);APInt i32_2(32, 2);APInt i32_3(32, 3);EXPECT_FALSE(i32_3.isSubsetOf(i32_1));EXPECT_TRUE(i32_1.isSubsetOf(i32_3));EXPECT_FALSE(i32_2.isSubsetOf(i32_1));EXPECT_FALSE(i32_1.isSubsetOf(i32_2));EXPECT_TRUE(i32_3.isSubsetOf(i32_3));APInt i128_1(128, 1);APInt i128_2(128, 2);APInt i128_3(128, 3);EXPECT_FALSE(i128_3.isSubsetOf(i128_1));EXPECT_TRUE(i128_1.isSubsetOf(i128_3));EXPECT_FALSE(i128_2.isSubsetOf(i128_1));EXPECT_FALSE(i128_1.isSubsetOf(i128_2));EXPECT_TRUE(i128_3.isSubsetOf(i128_3));i128_1 <<= 64;i128_2 <<= 64;i128_3 <<= 64;EXPECT_FALSE(i128_3.isSubsetOf(i128_1));EXPECT_TRUE(i128_1.isSubsetOf(i128_3));EXPECT_FALSE(i128_2.isSubsetOf(i128_1));EXPECT_FALSE(i128_1.isSubsetOf(i128_2));EXPECT_TRUE(i128_3.isSubsetOf(i128_3));}TEST(APIntTest, sext) {EXPECT_EQ(0, APInt(1, 0).sext(64));EXPECT_EQ(~uint64_t(0), APInt(1, 1).sext(64));APInt i32_max(APInt::getSignedMaxValue(32).sext(63));EXPECT_EQ(i32_max, i32_max.sext(63));EXPECT_EQ(32U, i32_max.countLeadingZeros());EXPECT_EQ(0U, i32_max.countTrailingZeros());EXPECT_EQ(31U, i32_max.countPopulation());APInt i32_min(APInt::getSignedMinValue(32).sext(63));EXPECT_EQ(i32_min, i32_min.sext(63));EXPECT_EQ(32U, i32_min.countLeadingOnes());EXPECT_EQ(31U, i32_min.countTrailingZeros());EXPECT_EQ(32U, i32_min.countPopulation());APInt i32_neg1(APInt(32, ~uint64_t(0)).sext(63));EXPECT_EQ(i32_neg1, i32_neg1.sext(63));EXPECT_EQ(63U, i32_neg1.countLeadingOnes());EXPECT_EQ(0U, i32_neg1.countTrailingZeros());EXPECT_EQ(63U, i32_neg1.countPopulation());}TEST(APIntTest, trunc) {APInt val(32, 0xFFFFFFFF);EXPECT_EQ(0xFFFF, val.trunc(16));EXPECT_EQ(0xFFFFFFFF, val.trunc(32));}TEST(APIntTest, concat) {APInt Int1(4, 0x1ULL);APInt Int3(4, 0x3ULL);EXPECT_EQ(0x31, Int3.concat(Int1));EXPECT_EQ(APInt(12, 0x313), Int3.concat(Int1).concat(Int3));EXPECT_EQ(APInt(16, 0x3313), Int3.concat(Int3).concat(Int1).concat(Int3));APInt I64(64, 0x3ULL);EXPECT_EQ(I64, I64.concat(I64).lshr(64).trunc(64));APInt I65(65, 0x3ULL);APInt I0 = APInt::getZeroWidth();EXPECT_EQ(I65, I65.concat(I0));EXPECT_EQ(I65, I0.concat(I65));}TEST(APIntTest, multiply) {APInt i64(64, 1234);EXPECT_EQ(7006652, i64 * 5678);EXPECT_EQ(7006652, 5678 * i64);APInt i128 = APInt::getOneBitSet(128, 64);APInt i128_1234(128, 1234);i128_1234 <<= 64;EXPECT_EQ(i128_1234, i128 * 1234);EXPECT_EQ(i128_1234, 1234 * i128);APInt i96 = APInt::getOneBitSet(96, 64);i96 *= ~0ULL;EXPECT_EQ(32U, i96.countLeadingOnes());EXPECT_EQ(32U, i96.countPopulation());EXPECT_EQ(64U, i96.countTrailingZeros());}TEST(APIntTest, RoundingUDiv) {for (uint64_t Ai = 1; Ai <= 255; Ai++) {APInt A(8, Ai);APInt Zero(8, 0);EXPECT_EQ(0, APIntOps::RoundingUDiv(Zero, A, APInt::Rounding::UP));EXPECT_EQ(0, APIntOps::RoundingUDiv(Zero, A, APInt::Rounding::DOWN));EXPECT_EQ(0, APIntOps::RoundingUDiv(Zero, A, APInt::Rounding::TOWARD_ZERO));for (uint64_t Bi = 1; Bi <= 255; Bi++) {APInt B(8, Bi);{APInt Quo = APIntOps::RoundingUDiv(A, B, APInt::Rounding::UP);auto Prod = Quo.zext(16) * B.zext(16);EXPECT_TRUE(Prod.uge(Ai));if (Prod.ugt(Ai)) {EXPECT_TRUE(((Quo - 1).zext(16) * B.zext(16)).ult(Ai));}}{APInt Quo = A.udiv(B);EXPECT_EQ(Quo, APIntOps::RoundingUDiv(A, B, APInt::Rounding::TOWARD_ZERO));EXPECT_EQ(Quo, APIntOps::RoundingUDiv(A, B, APInt::Rounding::DOWN));}}}}TEST(APIntTest, RoundingSDiv) {for (int64_t Ai = -128; Ai <= 127; Ai++) {APInt A(8, Ai);if (Ai != 0) {APInt Zero(8, 0);EXPECT_EQ(0, APIntOps::RoundingSDiv(Zero, A, APInt::Rounding::UP));EXPECT_EQ(0, APIntOps::RoundingSDiv(Zero, A, APInt::Rounding::DOWN));EXPECT_EQ(0, APIntOps::RoundingSDiv(Zero, A, APInt::Rounding::TOWARD_ZERO));}for (int64_t Bi = -128; Bi <= 127; Bi++) {if (Bi == 0)continue;APInt B(8, Bi);APInt QuoTowardZero = A.sdiv(B);{APInt Quo = APIntOps::RoundingSDiv(A, B, APInt::Rounding::UP);if (A.srem(B).isNullValue()) {EXPECT_EQ(QuoTowardZero, Quo);} else if (A.isNegative() !=B.isNegative()) { // if the math quotient is negative.EXPECT_EQ(QuoTowardZero, Quo);} else {EXPECT_EQ(QuoTowardZero + 1, Quo);}}{APInt Quo = APIntOps::RoundingSDiv(A, B, APInt::Rounding::DOWN);if (A.srem(B).isNullValue()) {EXPECT_EQ(QuoTowardZero, Quo);} else if (A.isNegative() !=B.isNegative()) { // if the math quotient is negative.EXPECT_EQ(QuoTowardZero - 1, Quo);} else {EXPECT_EQ(QuoTowardZero, Quo);}}EXPECT_EQ(QuoTowardZero,APIntOps::RoundingSDiv(A, B, APInt::Rounding::TOWARD_ZERO));}}}TEST(APIntTest, umul_ov) {const std::pair<uint64_t, uint64_t> Overflows[] = {{0x8000000000000000, 2},{0x5555555555555556, 3},{4294967296, 4294967296},{4294967295, 4294967298},};const std::pair<uint64_t, uint64_t> NonOverflows[] = {{0x7fffffffffffffff, 2},{0x5555555555555555, 3},{4294967295, 4294967297},};bool Overflow;for (auto &X : Overflows) {APInt A(64, X.first);APInt B(64, X.second);(void)A.umul_ov(B, Overflow);EXPECT_TRUE(Overflow);}for (auto &X : NonOverflows) {APInt A(64, X.first);APInt B(64, X.second);(void)A.umul_ov(B, Overflow);EXPECT_FALSE(Overflow);}for (unsigned Bits = 1; Bits <= 5; ++Bits)for (unsigned A = 0; A != 1u << Bits; ++A)for (unsigned B = 0; B != 1u << Bits; ++B) {APInt N1 = APInt(Bits, A), N2 = APInt(Bits, B);APInt Narrow = N1.umul_ov(N2, Overflow);APInt Wide = N1.zext(2 * Bits) * N2.zext(2 * Bits);EXPECT_EQ(Wide.trunc(Bits), Narrow);EXPECT_EQ(Narrow.zext(2 * Bits) != Wide, Overflow);}}TEST(APIntTest, smul_ov) {for (unsigned Bits = 1; Bits <= 5; ++Bits)for (unsigned A = 0; A != 1u << Bits; ++A)for (unsigned B = 0; B != 1u << Bits; ++B) {bool Overflow;APInt N1 = APInt(Bits, A), N2 = APInt(Bits, B);APInt Narrow = N1.smul_ov(N2, Overflow);APInt Wide = N1.sext(2 * Bits) * N2.sext(2 * Bits);EXPECT_EQ(Wide.trunc(Bits), Narrow);EXPECT_EQ(Narrow.sext(2 * Bits) != Wide, Overflow);}}TEST(APIntTest, SolveQuadraticEquationWrap) {// Verify that "Solution" is the first non-negative integer that solves// Ax^2 + Bx + C = "0 or overflow", i.e. that it is a correct solution// as calculated by SolveQuadraticEquationWrap.auto Validate = [] (int A, int B, int C, unsigned Width, int Solution) {int Mask = (1 << Width) - 1;// Solution should be non-negative.EXPECT_GE(Solution, 0);auto OverflowBits = [] (int64_t V, unsigned W) {return V & -(1 << W);};int64_t Over0 = OverflowBits(C, Width);auto IsZeroOrOverflow = [&] (int X) {int64_t ValueAtX = A*X*X + B*X + C;int64_t OverX = OverflowBits(ValueAtX, Width);return (ValueAtX & Mask) == 0 || OverX != Over0;};auto EquationToString = [&] (const char *X_str) {return (Twine(A) + Twine(X_str) + Twine("^2 + ") + Twine(B) +Twine(X_str) + Twine(" + ") + Twine(C) + Twine(", bitwidth: ") +Twine(Width)).str();};auto IsSolution = [&] (const char *X_str, int X) {if (IsZeroOrOverflow(X))return ::testing::AssertionSuccess()<< X << " is a solution of " << EquationToString(X_str);return ::testing::AssertionFailure()<< X << " is not an expected solution of "<< EquationToString(X_str);};auto IsNotSolution = [&] (const char *X_str, int X) {if (!IsZeroOrOverflow(X))return ::testing::AssertionSuccess()<< X << " is not a solution of " << EquationToString(X_str);return ::testing::AssertionFailure()<< X << " is an unexpected solution of "<< EquationToString(X_str);};// This is the important part: make sure that there is no solution that// is less than the calculated one.if (Solution > 0) {for (int X = 1; X < Solution-1; ++X)EXPECT_PRED_FORMAT1(IsNotSolution, X);}// Verify that the calculated solution is indeed a solution.EXPECT_PRED_FORMAT1(IsSolution, Solution);};// Generate all possible quadratic equations with Width-bit wide integer// coefficients, get the solution from SolveQuadraticEquationWrap, and// verify that the solution is correct.auto Iterate = [&] (unsigned Width) {assert(1 < Width && Width < 32);int Low = -(1 << (Width-1));int High = (1 << (Width-1));for (int A = Low; A != High; ++A) {if (A == 0)continue;for (int B = Low; B != High; ++B) {for (int C = Low; C != High; ++C) {Optional<APInt> S = APIntOps::SolveQuadraticEquationWrap(APInt(Width, A), APInt(Width, B),APInt(Width, C), Width);if (S)Validate(A, B, C, Width, S->getSExtValue());}}}};// Test all widths in [2..6].for (unsigned i = 2; i <= 6; ++i)Iterate(i);}TEST(APIntTest, MultiplicativeInverseExaustive) {for (unsigned BitWidth = 1; BitWidth <= 16; ++BitWidth) {for (unsigned Value = 0; Value < (1u << BitWidth); ++Value) {APInt V = APInt(BitWidth, Value);APInt MulInv =V.zext(BitWidth + 1).multiplicativeInverse(APInt::getSignedMinValue(BitWidth + 1)).trunc(BitWidth);APInt One = V * MulInv;if (!V.isNullValue() && V.countTrailingZeros() == 0) {// Multiplicative inverse exists for all odd numbers.EXPECT_TRUE(One.isOneValue());} else {// Multiplicative inverse does not exist for even numbers (and 0).EXPECT_TRUE(MulInv.isNullValue());}}}}TEST(APIntTest, GetMostSignificantDifferentBit) {EXPECT_EQ(APIntOps::GetMostSignificantDifferentBit(APInt(8, 0), APInt(8, 0)),llvm::None);EXPECT_EQ(APIntOps::GetMostSignificantDifferentBit(APInt(8, 42), APInt(8, 42)),llvm::None);EXPECT_EQ(*APIntOps::GetMostSignificantDifferentBit(APInt(8, 0), APInt(8, 1)),0u);EXPECT_EQ(*APIntOps::GetMostSignificantDifferentBit(APInt(8, 0), APInt(8, 2)),1u);EXPECT_EQ(*APIntOps::GetMostSignificantDifferentBit(APInt(8, 0), APInt(8, 3)),1u);EXPECT_EQ(*APIntOps::GetMostSignificantDifferentBit(APInt(8, 1), APInt(8, 0)),0u);EXPECT_EQ(APIntOps::GetMostSignificantDifferentBit(APInt(8, 1), APInt(8, 1)),llvm::None);EXPECT_EQ(*APIntOps::GetMostSignificantDifferentBit(APInt(8, 1), APInt(8, 2)),1u);EXPECT_EQ(*APIntOps::GetMostSignificantDifferentBit(APInt(8, 1), APInt(8, 3)),1u);EXPECT_EQ(*APIntOps::GetMostSignificantDifferentBit(APInt(8, 42), APInt(8, 112)),6u);}TEST(APIntTest, GetMostSignificantDifferentBitExaustive) {auto GetHighestDifferentBitBruteforce =[](const APInt &V0, const APInt &V1) -> llvm::Optional<unsigned> {assert(V0.getBitWidth() == V1.getBitWidth() && "Must have same bitwidth");if (V0 == V1)return llvm::None; // Bitwise identical.// There is a mismatch. Let's find the most significant different bit.for (int Bit = V0.getBitWidth() - 1; Bit >= 0; --Bit) {if (V0[Bit] == V1[Bit])continue;return Bit;}llvm_unreachable("Must have found bit mismatch.");};for (unsigned BitWidth = 1; BitWidth <= 8; ++BitWidth) {for (unsigned V0 = 0; V0 < (1u << BitWidth); ++V0) {for (unsigned V1 = 0; V1 < (1u << BitWidth); ++V1) {APInt A = APInt(BitWidth, V0);APInt B = APInt(BitWidth, V1);auto Bit = APIntOps::GetMostSignificantDifferentBit(A, B);EXPECT_EQ(Bit, GetHighestDifferentBitBruteforce(A, B));if (!Bit)EXPECT_EQ(A, B);else {EXPECT_NE(A, B);for (unsigned NumLowBits = 0; NumLowBits <= BitWidth; ++NumLowBits) {APInt Adash = A;Adash.clearLowBits(NumLowBits);APInt Bdash = B;Bdash.clearLowBits(NumLowBits);// Clearing only low bits up to and including *Bit is sufficient// to make values equal.if (NumLowBits >= 1 + *Bit)EXPECT_EQ(Adash, Bdash);elseEXPECT_NE(Adash, Bdash);}}}}}}TEST(APIntTest, SignbitZeroChecks) {EXPECT_TRUE(APInt(8, -1).isNegative());EXPECT_FALSE(APInt(8, -1).isNonNegative());EXPECT_FALSE(APInt(8, -1).isStrictlyPositive());EXPECT_TRUE(APInt(8, -1).isNonPositive());EXPECT_FALSE(APInt(8, 0).isNegative());EXPECT_TRUE(APInt(8, 0).isNonNegative());EXPECT_FALSE(APInt(8, 0).isStrictlyPositive());EXPECT_TRUE(APInt(8, 0).isNonPositive());EXPECT_FALSE(APInt(8, 1).isNegative());EXPECT_TRUE(APInt(8, 1).isNonNegative());EXPECT_TRUE(APInt(8, 1).isStrictlyPositive());EXPECT_FALSE(APInt(8, 1).isNonPositive());}TEST(APIntTest, ZeroWidth) {// Zero width Constructors.auto ZW = APInt::getZeroWidth();EXPECT_EQ(0U, ZW.getBitWidth());EXPECT_EQ(0U, APInt(0, ArrayRef<uint64_t>({0, 1, 2})).getBitWidth());EXPECT_EQ(0U, APInt(0, "0", 10).getBitWidth());// Default constructor is single bit wide.EXPECT_EQ(1U, APInt().getBitWidth());// Copy ctor (move is down below).APInt ZW2(ZW);EXPECT_EQ(0U, ZW2.getBitWidth());// AssignmentZW = ZW2;EXPECT_EQ(0U, ZW.getBitWidth());// Methods like getLowBitsSet work with zero bits.EXPECT_EQ(0U, APInt::getLowBitsSet(0, 0).getBitWidth());EXPECT_EQ(0U, APInt::getSplat(0, ZW).getBitWidth());EXPECT_EQ(0U, APInt(4, 10).extractBits(0, 2).getBitWidth());// Logical operators.ZW |= ZW2;ZW &= ZW2;ZW ^= ZW2;ZW |= 42; // These ignore high bits of the literal.ZW &= 42;ZW ^= 42;EXPECT_EQ(1, ZW.isIntN(0));// Modulo Arithmetic. Divide/Rem aren't defined on division by zero, so they// aren't supported.ZW += ZW2;ZW -= ZW2;ZW *= ZW2;// Logical Shifts and rotates, the amount must be <= bitwidth.ZW <<= 0;ZW.lshrInPlace(0);(void)ZW.rotl(0);(void)ZW.rotr(0);// Comparisons.EXPECT_EQ(1, ZW == ZW);EXPECT_EQ(0, ZW != ZW);EXPECT_EQ(0, ZW.ult(ZW));// Mutations.ZW.setBitsWithWrap(0, 0);ZW.setBits(0, 0);ZW.clearAllBits();ZW.flipAllBits();// Leading, trailing, ctpop, etcEXPECT_EQ(0U, ZW.countLeadingZeros());EXPECT_EQ(0U, ZW.countLeadingOnes());EXPECT_EQ(0U, ZW.countPopulation());EXPECT_EQ(0U, ZW.reverseBits().getBitWidth());EXPECT_EQ(0U, ZW.getHiBits(0).getBitWidth());EXPECT_EQ(0U, ZW.getLoBits(0).getBitWidth());EXPECT_EQ(0, ZW.zext(4));EXPECT_EQ(0U, APInt(4, 3).trunc(0).getBitWidth());EXPECT_TRUE(ZW.isAllOnes());// Zero extension.EXPECT_EQ(0U, ZW.getZExtValue());SmallString<42> STR;ZW.toStringUnsigned(STR);EXPECT_EQ("0", STR);// Move ctor (keep at the end of the method since moves are destructive).APInt MZW1(std::move(ZW));EXPECT_EQ(0U, MZW1.getBitWidth());// Move AssignmentMZW1 = std::move(ZW2);EXPECT_EQ(0U, MZW1.getBitWidth());}TEST(APIntTest, ScaleBitMask) {EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x00), 8), APInt(8, 0x00));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x01), 8), APInt(8, 0x0F));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x02), 8), APInt(8, 0xF0));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x03), 8), APInt(8, 0xFF));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(8, 0x00), 4), APInt(4, 0x00));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(8, 0xFF), 4), APInt(4, 0x0F));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(8, 0xE4), 4), APInt(4, 0x0E));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(8, 0x00), 8), APInt(8, 0x00));EXPECT_EQ(APIntOps::ScaleBitMask(APInt::getNullValue(1024), 4096),APInt::getNullValue(4096));EXPECT_EQ(APIntOps::ScaleBitMask(APInt::getAllOnes(4096), 256),APInt::getAllOnes(256));EXPECT_EQ(APIntOps::ScaleBitMask(APInt::getOneBitSet(4096, 32), 256),APInt::getOneBitSet(256, 2));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x00), 8, true), APInt(8, 0x00));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x01), 8, true), APInt(8, 0x0F));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x02), 8, true), APInt(8, 0xF0));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(2, 0x03), 8, true), APInt(8, 0xFF));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(8, 0x00), 4, true), APInt(4, 0x00));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(8, 0xFF), 4, true), APInt(4, 0x0F));EXPECT_EQ(APIntOps::ScaleBitMask(APInt(8, 0xE4), 4, true), APInt(4, 0x08));}} // end anonymous namespace
//===- llvm/unittest/ADT/APFloat.cpp - APFloat unit tests ---------------------===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/APFloat.h"#include "llvm/ADT/APSInt.h"#include "llvm/ADT/Hashing.h"#include "llvm/ADT/SmallVector.h"#include "llvm/Support/Error.h"#include "llvm/Support/FormatVariadic.h"#include "gtest/gtest.h"#include <cmath>#include <ostream>#include <string>#include <tuple>using namespace llvm;static std::string convertToErrorFromString(StringRef Str) {llvm::APFloat F(0.0);auto StatusOrErr =F.convertFromString(Str, llvm::APFloat::rmNearestTiesToEven);EXPECT_TRUE(!StatusOrErr);return toString(StatusOrErr.takeError());}static double convertToDoubleFromString(StringRef Str) {llvm::APFloat F(0.0);auto StatusOrErr =F.convertFromString(Str, llvm::APFloat::rmNearestTiesToEven);EXPECT_FALSE(!StatusOrErr);consumeError(StatusOrErr.takeError());return F.convertToDouble();}static std::string convertToString(double d, unsigned Prec, unsigned Pad,bool Tr = true) {llvm::SmallVector<char, 100> Buffer;llvm::APFloat F(d);F.toString(Buffer, Prec, Pad, Tr);return std::string(Buffer.data(), Buffer.size());}namespace {TEST(APFloatTest, isSignaling) {// We test qNaN, -qNaN, +sNaN, -sNaN with and without payloads. *NOTE* The// positive/negative distinction is included only since the getQNaN/getSNaN// API provides the option.APInt payload = APInt::getOneBitSet(4, 2);EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), false).isSignaling());EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), true).isSignaling());EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), false, &payload).isSignaling());EXPECT_FALSE(APFloat::getQNaN(APFloat::IEEEsingle(), true, &payload).isSignaling());EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isSignaling());EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), true).isSignaling());EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), false, &payload).isSignaling());EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), true, &payload).isSignaling());}TEST(APFloatTest, next) {APFloat test(APFloat::IEEEquad(), APFloat::uninitialized);APFloat expected(APFloat::IEEEquad(), APFloat::uninitialized);// 1. Test Special Cases Values.//// Test all special values for nextUp and nextDown perscribed by IEEE-754R// 2008. These are:// 1. +inf// 2. -inf// 3. getLargest()// 4. -getLargest()// 5. getSmallest()// 6. -getSmallest()// 7. qNaN// 8. sNaN// 9. +0// 10. -0// nextUp(+inf) = +inf.test = APFloat::getInf(APFloat::IEEEquad(), false);expected = APFloat::getInf(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isInfinity());EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(+inf) = -nextUp(-inf) = -(-getLargest()) = getLargest()test = APFloat::getInf(APFloat::IEEEquad(), false);expected = APFloat::getLargest(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(-inf) = -getLargest()test = APFloat::getInf(APFloat::IEEEquad(), true);expected = APFloat::getLargest(APFloat::IEEEquad(), true);EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-inf) = -nextUp(+inf) = -(+inf) = -inf.test = APFloat::getInf(APFloat::IEEEquad(), true);expected = APFloat::getInf(APFloat::IEEEquad(), true);EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.isInfinity() && test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(getLargest()) = +inftest = APFloat::getLargest(APFloat::IEEEquad(), false);expected = APFloat::getInf(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isInfinity() && !test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(getLargest()) = -nextUp(-getLargest())// = -(-getLargest() + inc)// = getLargest() - inc.test = APFloat::getLargest(APFloat::IEEEquad(), false);expected = APFloat(APFloat::IEEEquad(),"0x1.fffffffffffffffffffffffffffep+16383");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(!test.isInfinity() && !test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(-getLargest()) = -getLargest() + inc.test = APFloat::getLargest(APFloat::IEEEquad(), true);expected = APFloat(APFloat::IEEEquad(),"-0x1.fffffffffffffffffffffffffffep+16383");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-getLargest()) = -nextUp(getLargest()) = -(inf) = -inf.test = APFloat::getLargest(APFloat::IEEEquad(), true);expected = APFloat::getInf(APFloat::IEEEquad(), true);EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.isInfinity() && test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(getSmallest()) = getSmallest() + inc.test = APFloat(APFloat::IEEEquad(), "0x0.0000000000000000000000000001p-16382");expected = APFloat(APFloat::IEEEquad(),"0x0.0000000000000000000000000002p-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(getSmallest()) = -nextUp(-getSmallest()) = -(-0) = +0.test = APFloat(APFloat::IEEEquad(), "0x0.0000000000000000000000000001p-16382");expected = APFloat::getZero(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.isPosZero());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(-getSmallest()) = -0.test = APFloat(APFloat::IEEEquad(), "-0x0.0000000000000000000000000001p-16382");expected = APFloat::getZero(APFloat::IEEEquad(), true);EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isNegZero());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-getSmallest()) = -nextUp(getSmallest()) = -getSmallest() - inc.test = APFloat(APFloat::IEEEquad(), "-0x0.0000000000000000000000000001p-16382");expected = APFloat(APFloat::IEEEquad(),"-0x0.0000000000000000000000000002p-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(qNaN) = qNaNtest = APFloat::getQNaN(APFloat::IEEEquad(), false);expected = APFloat::getQNaN(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(qNaN) = qNaNtest = APFloat::getQNaN(APFloat::IEEEquad(), false);expected = APFloat::getQNaN(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(sNaN) = qNaNtest = APFloat::getSNaN(APFloat::IEEEquad(), false);expected = APFloat::getQNaN(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(false), APFloat::opInvalidOp);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(sNaN) = qNaNtest = APFloat::getSNaN(APFloat::IEEEquad(), false);expected = APFloat::getQNaN(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(true), APFloat::opInvalidOp);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(+0) = +getSmallest()test = APFloat::getZero(APFloat::IEEEquad(), false);expected = APFloat::getSmallest(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(+0) = -nextUp(-0) = -getSmallest()test = APFloat::getZero(APFloat::IEEEquad(), false);expected = APFloat::getSmallest(APFloat::IEEEquad(), true);EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(-0) = +getSmallest()test = APFloat::getZero(APFloat::IEEEquad(), true);expected = APFloat::getSmallest(APFloat::IEEEquad(), false);EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-0) = -nextUp(0) = -getSmallest()test = APFloat::getZero(APFloat::IEEEquad(), true);expected = APFloat::getSmallest(APFloat::IEEEquad(), true);EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// 2. Binade Boundary Tests.// 2a. Test denormal <-> normal binade boundaries.// * nextUp(+Largest Denormal) -> +Smallest Normal.// * nextDown(-Largest Denormal) -> -Smallest Normal.// * nextUp(-Smallest Normal) -> -Largest Denormal.// * nextDown(+Smallest Normal) -> +Largest Denormal.// nextUp(+Largest Denormal) -> +Smallest Normal.test = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffffffffp-16382");expected = APFloat(APFloat::IEEEquad(),"0x1.0000000000000000000000000000p-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_FALSE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-Largest Denormal) -> -Smallest Normal.test = APFloat(APFloat::IEEEquad(),"-0x0.ffffffffffffffffffffffffffffp-16382");expected = APFloat(APFloat::IEEEquad(),"-0x1.0000000000000000000000000000p-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_FALSE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(-Smallest Normal) -> -LargestDenormal.test = APFloat(APFloat::IEEEquad(),"-0x1.0000000000000000000000000000p-16382");expected = APFloat(APFloat::IEEEquad(),"-0x0.ffffffffffffffffffffffffffffp-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(+Smallest Normal) -> +Largest Denormal.test = APFloat(APFloat::IEEEquad(),"+0x1.0000000000000000000000000000p-16382");expected = APFloat(APFloat::IEEEquad(),"+0x0.ffffffffffffffffffffffffffffp-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));// 2b. Test normal <-> normal binade boundaries.// * nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1.// * nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1.// * nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary.// * nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary.// nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1.test = APFloat(APFloat::IEEEquad(), "-0x1p+1");expected = APFloat(APFloat::IEEEquad(),"-0x1.ffffffffffffffffffffffffffffp+0");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1.test = APFloat(APFloat::IEEEquad(), "0x1p+1");expected = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffffffffp+0");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary.test = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffffffffp+0");expected = APFloat(APFloat::IEEEquad(), "0x1p+1");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary.test = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffffffffp+0");expected = APFloat(APFloat::IEEEquad(), "-0x1p+1");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// 2c. Test using next at binade boundaries with a direction away from the// binade boundary. Away from denormal <-> normal boundaries.//// This is to make sure that even though we are at a binade boundary, since// we are rounding away, we do not trigger the binade boundary code. Thus we// test:// * nextUp(-Largest Denormal) -> -Largest Denormal + inc.// * nextDown(+Largest Denormal) -> +Largest Denormal - inc.// * nextUp(+Smallest Normal) -> +Smallest Normal + inc.// * nextDown(-Smallest Normal) -> -Smallest Normal - inc.// nextUp(-Largest Denormal) -> -Largest Denormal + inc.test = APFloat(APFloat::IEEEquad(), "-0x0.ffffffffffffffffffffffffffffp-16382");expected = APFloat(APFloat::IEEEquad(),"-0x0.fffffffffffffffffffffffffffep-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(+Largest Denormal) -> +Largest Denormal - inc.test = APFloat(APFloat::IEEEquad(), "0x0.ffffffffffffffffffffffffffffp-16382");expected = APFloat(APFloat::IEEEquad(),"0x0.fffffffffffffffffffffffffffep-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(+Smallest Normal) -> +Smallest Normal + inc.test = APFloat(APFloat::IEEEquad(), "0x1.0000000000000000000000000000p-16382");expected = APFloat(APFloat::IEEEquad(),"0x1.0000000000000000000000000001p-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(!test.isDenormal());EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-Smallest Normal) -> -Smallest Normal - inc.test = APFloat(APFloat::IEEEquad(), "-0x1.0000000000000000000000000000p-16382");expected = APFloat(APFloat::IEEEquad(),"-0x1.0000000000000000000000000001p-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(!test.isDenormal());EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// 2d. Test values which cause our exponent to go to min exponent. This// is to ensure that guards in the code to check for min exponent// trigger properly.// * nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382// * nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) ->// -0x1p-16381// * nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16382// * nextDown(0x1p-16382) -> 0x1.ffffffffffffffffffffffffffffp-16382// nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382test = APFloat(APFloat::IEEEquad(), "-0x1p-16381");expected = APFloat(APFloat::IEEEquad(),"-0x1.ffffffffffffffffffffffffffffp-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) ->// -0x1p-16381test = APFloat(APFloat::IEEEquad(), "-0x1.ffffffffffffffffffffffffffffp-16382");expected = APFloat(APFloat::IEEEquad(), "-0x1p-16381");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16381test = APFloat(APFloat::IEEEquad(), "0x1.ffffffffffffffffffffffffffffp-16382");expected = APFloat(APFloat::IEEEquad(), "0x1p-16381");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(0x1p-16381) -> 0x1.ffffffffffffffffffffffffffffp-16382test = APFloat(APFloat::IEEEquad(), "0x1p-16381");expected = APFloat(APFloat::IEEEquad(),"0x1.ffffffffffffffffffffffffffffp-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.bitwiseIsEqual(expected));// 3. Now we test both denormal/normal computation which will not cause us// to go across binade boundaries. Specifically we test:// * nextUp(+Denormal) -> +Denormal.// * nextDown(+Denormal) -> +Denormal.// * nextUp(-Denormal) -> -Denormal.// * nextDown(-Denormal) -> -Denormal.// * nextUp(+Normal) -> +Normal.// * nextDown(+Normal) -> +Normal.// * nextUp(-Normal) -> -Normal.// * nextDown(-Normal) -> -Normal.// nextUp(+Denormal) -> +Denormal.test = APFloat(APFloat::IEEEquad(),"0x0.ffffffffffffffffffffffff000cp-16382");expected = APFloat(APFloat::IEEEquad(),"0x0.ffffffffffffffffffffffff000dp-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(+Denormal) -> +Denormal.test = APFloat(APFloat::IEEEquad(),"0x0.ffffffffffffffffffffffff000cp-16382");expected = APFloat(APFloat::IEEEquad(),"0x0.ffffffffffffffffffffffff000bp-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(-Denormal) -> -Denormal.test = APFloat(APFloat::IEEEquad(),"-0x0.ffffffffffffffffffffffff000cp-16382");expected = APFloat(APFloat::IEEEquad(),"-0x0.ffffffffffffffffffffffff000bp-16382");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-Denormal) -> -Denormaltest = APFloat(APFloat::IEEEquad(),"-0x0.ffffffffffffffffffffffff000cp-16382");expected = APFloat(APFloat::IEEEquad(),"-0x0.ffffffffffffffffffffffff000dp-16382");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(+Normal) -> +Normal.test = APFloat(APFloat::IEEEquad(),"0x1.ffffffffffffffffffffffff000cp-16000");expected = APFloat(APFloat::IEEEquad(),"0x1.ffffffffffffffffffffffff000dp-16000");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(!test.isDenormal());EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(+Normal) -> +Normal.test = APFloat(APFloat::IEEEquad(),"0x1.ffffffffffffffffffffffff000cp-16000");expected = APFloat(APFloat::IEEEquad(),"0x1.ffffffffffffffffffffffff000bp-16000");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(!test.isDenormal());EXPECT_TRUE(!test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextUp(-Normal) -> -Normal.test = APFloat(APFloat::IEEEquad(),"-0x1.ffffffffffffffffffffffff000cp-16000");expected = APFloat(APFloat::IEEEquad(),"-0x1.ffffffffffffffffffffffff000bp-16000");EXPECT_EQ(test.next(false), APFloat::opOK);EXPECT_TRUE(!test.isDenormal());EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));// nextDown(-Normal) -> -Normal.test = APFloat(APFloat::IEEEquad(),"-0x1.ffffffffffffffffffffffff000cp-16000");expected = APFloat(APFloat::IEEEquad(),"-0x1.ffffffffffffffffffffffff000dp-16000");EXPECT_EQ(test.next(true), APFloat::opOK);EXPECT_TRUE(!test.isDenormal());EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));}TEST(APFloatTest, FMA) {APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;{APFloat f1(14.5f);APFloat f2(-14.5f);APFloat f3(225.0f);f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);EXPECT_EQ(14.75f, f1.convertToFloat());}{APFloat Val2(2.0f);APFloat f1((float)1.17549435e-38F);APFloat f2((float)1.17549435e-38F);f1.divide(Val2, rdmd);f2.divide(Val2, rdmd);APFloat f3(12.0f);f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);EXPECT_EQ(12.0f, f1.convertToFloat());}// Test for correct zero sign when answer is exactly zero.// fma(1.0, -1.0, 1.0) -> +ve 0.{APFloat f1(1.0);APFloat f2(-1.0);APFloat f3(1.0);f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);EXPECT_TRUE(!f1.isNegative() && f1.isZero());}// Test for correct zero sign when answer is exactly zero and rounding towards// negative.// fma(1.0, -1.0, 1.0) -> +ve 0.{APFloat f1(1.0);APFloat f2(-1.0);APFloat f3(1.0);f1.fusedMultiplyAdd(f2, f3, APFloat::rmTowardNegative);EXPECT_TRUE(f1.isNegative() && f1.isZero());}// Test for correct (in this case -ve) sign when adding like signed zeros.// Test fma(0.0, -0.0, -0.0) -> -ve 0.{APFloat f1(0.0);APFloat f2(-0.0);APFloat f3(-0.0);f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);EXPECT_TRUE(f1.isNegative() && f1.isZero());}// Test -ve sign preservation when small negative results underflow.{APFloat f1(APFloat::IEEEdouble(), "-0x1p-1074");APFloat f2(APFloat::IEEEdouble(), "+0x1p-1074");APFloat f3(0.0);f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);EXPECT_TRUE(f1.isNegative() && f1.isZero());}// Test x87 extended precision case from http://llvm.org/PR20728.{APFloat M1(APFloat::x87DoubleExtended(), 1);APFloat M2(APFloat::x87DoubleExtended(), 1);APFloat A(APFloat::x87DoubleExtended(), 3);bool losesInfo = false;M1.fusedMultiplyAdd(M1, A, APFloat::rmNearestTiesToEven);M1.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_FALSE(losesInfo);EXPECT_EQ(4.0f, M1.convertToFloat());}// Regression test that failed an assertion.{APFloat f1(-8.85242279E-41f);APFloat f2(2.0f);APFloat f3(8.85242279E-41f);f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);EXPECT_EQ(-8.85242279E-41f, f1.convertToFloat());}// Test using only a single instance of APFloat.{APFloat F(1.5);F.fusedMultiplyAdd(F, F, APFloat::rmNearestTiesToEven);EXPECT_EQ(3.75, F.convertToDouble());}}TEST(APFloatTest, MinNum) {APFloat f1(1.0);APFloat f2(2.0);APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());EXPECT_EQ(1.0, minnum(f1, f2).convertToDouble());EXPECT_EQ(1.0, minnum(f2, f1).convertToDouble());EXPECT_EQ(1.0, minnum(f1, nan).convertToDouble());EXPECT_EQ(1.0, minnum(nan, f1).convertToDouble());}TEST(APFloatTest, MaxNum) {APFloat f1(1.0);APFloat f2(2.0);APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());EXPECT_EQ(2.0, maxnum(f1, f2).convertToDouble());EXPECT_EQ(2.0, maxnum(f2, f1).convertToDouble());EXPECT_EQ(1.0, maxnum(f1, nan).convertToDouble());EXPECT_EQ(1.0, maxnum(nan, f1).convertToDouble());}TEST(APFloatTest, Minimum) {APFloat f1(1.0);APFloat f2(2.0);APFloat zp(0.0);APFloat zn(-0.0);APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());EXPECT_EQ(1.0, minimum(f1, f2).convertToDouble());EXPECT_EQ(1.0, minimum(f2, f1).convertToDouble());EXPECT_EQ(-0.0, minimum(zp, zn).convertToDouble());EXPECT_EQ(-0.0, minimum(zn, zp).convertToDouble());EXPECT_TRUE(std::isnan(minimum(f1, nan).convertToDouble()));EXPECT_TRUE(std::isnan(minimum(nan, f1).convertToDouble()));}TEST(APFloatTest, Maximum) {APFloat f1(1.0);APFloat f2(2.0);APFloat zp(0.0);APFloat zn(-0.0);APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());EXPECT_EQ(2.0, maximum(f1, f2).convertToDouble());EXPECT_EQ(2.0, maximum(f2, f1).convertToDouble());EXPECT_EQ(0.0, maximum(zp, zn).convertToDouble());EXPECT_EQ(0.0, maximum(zn, zp).convertToDouble());EXPECT_TRUE(std::isnan(maximum(f1, nan).convertToDouble()));EXPECT_TRUE(std::isnan(maximum(nan, f1).convertToDouble()));}TEST(APFloatTest, Denormal) {APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;// Test single precision{const char *MinNormalStr = "1.17549435082228750797e-38";EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), MinNormalStr).isDenormal());EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), 0).isDenormal());APFloat Val2(APFloat::IEEEsingle(), 2);APFloat T(APFloat::IEEEsingle(), MinNormalStr);T.divide(Val2, rdmd);EXPECT_TRUE(T.isDenormal());}// Test double precision{const char *MinNormalStr = "2.22507385850720138309e-308";EXPECT_FALSE(APFloat(APFloat::IEEEdouble(), MinNormalStr).isDenormal());EXPECT_FALSE(APFloat(APFloat::IEEEdouble(), 0).isDenormal());APFloat Val2(APFloat::IEEEdouble(), 2);APFloat T(APFloat::IEEEdouble(), MinNormalStr);T.divide(Val2, rdmd);EXPECT_TRUE(T.isDenormal());}// Test Intel double-ext{const char *MinNormalStr = "3.36210314311209350626e-4932";EXPECT_FALSE(APFloat(APFloat::x87DoubleExtended(), MinNormalStr).isDenormal());EXPECT_FALSE(APFloat(APFloat::x87DoubleExtended(), 0).isDenormal());APFloat Val2(APFloat::x87DoubleExtended(), 2);APFloat T(APFloat::x87DoubleExtended(), MinNormalStr);T.divide(Val2, rdmd);EXPECT_TRUE(T.isDenormal());}// Test quadruple precision{const char *MinNormalStr = "3.36210314311209350626267781732175260e-4932";EXPECT_FALSE(APFloat(APFloat::IEEEquad(), MinNormalStr).isDenormal());EXPECT_FALSE(APFloat(APFloat::IEEEquad(), 0).isDenormal());APFloat Val2(APFloat::IEEEquad(), 2);APFloat T(APFloat::IEEEquad(), MinNormalStr);T.divide(Val2, rdmd);EXPECT_TRUE(T.isDenormal());}}TEST(APFloatTest, Zero) {EXPECT_EQ(0.0f, APFloat(0.0f).convertToFloat());EXPECT_EQ(-0.0f, APFloat(-0.0f).convertToFloat());EXPECT_TRUE(APFloat(-0.0f).isNegative());EXPECT_EQ(0.0, APFloat(0.0).convertToDouble());EXPECT_EQ(-0.0, APFloat(-0.0).convertToDouble());EXPECT_TRUE(APFloat(-0.0).isNegative());}TEST(APFloatTest, DecimalStringsWithoutNullTerminators) {// Make sure that we can parse strings without null terminators.// rdar://14323230.EXPECT_EQ(convertToDoubleFromString(StringRef("0.00", 3)), 0.0);EXPECT_EQ(convertToDoubleFromString(StringRef("0.01", 3)), 0.0);EXPECT_EQ(convertToDoubleFromString(StringRef("0.09", 3)), 0.0);EXPECT_EQ(convertToDoubleFromString(StringRef("0.095", 4)), 0.09);EXPECT_EQ(convertToDoubleFromString(StringRef("0.00e+3", 7)), 0.00);EXPECT_EQ(convertToDoubleFromString(StringRef("0e+3", 4)), 0.00);}TEST(APFloatTest, fromZeroDecimalString) {EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "00000.").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+00000.").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-00000.").convertToDouble());EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), ".00000").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.00000").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.00000").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0000.00000").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0000.00000").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0000.00000").convertToDouble());}TEST(APFloatTest, fromZeroDecimalSingleExponentString) {EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.e1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.e1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.e1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.e+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.e+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.e+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.e-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.e-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.e-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0e1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0e1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0e1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0e+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0e+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0e+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), ".0e-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+.0e-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-.0e-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0e1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0e1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0e1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0e+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0e+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0e+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0.0e-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0.0e-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0.0e-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "000.0000e1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+000.0000e+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-000.0000e+1").convertToDouble());}TEST(APFloatTest, fromZeroDecimalLargeExponentString) {EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e1234").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e1234").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e1234").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e+1234").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e+1234").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e+1234").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0e-1234").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0e-1234").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0e-1234").convertToDouble());EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), "000.0000e1234").convertToDouble());EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), "000.0000e-1234").convertToDouble());EXPECT_EQ(0.0, APFloat(APFloat::IEEEdouble(), StringRef("0e1234" "\0" "2", 6)).convertToDouble());}TEST(APFloatTest, fromZeroHexadecimalString) {EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0p1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0p+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0p-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.p1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.p+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.p+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.p-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.p-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.0p1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x.0p1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x.0p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.0p+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x.0p+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x.0p+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.0p-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x.0p-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x.0p-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.0p1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.0p1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.0p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.0p+1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.0p+1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.0p+1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.0p-1").convertToDouble());EXPECT_EQ(+0.0, APFloat(APFloat::IEEEdouble(), "+0x0.0p-1").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0.0p-1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x00000.p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0000.00000p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.00000p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p1").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0p1234").convertToDouble());EXPECT_EQ(-0.0, APFloat(APFloat::IEEEdouble(), "-0x0p1234").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x00000.p1234").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0000.00000p1234").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x.00000p1234").convertToDouble());EXPECT_EQ( 0.0, APFloat(APFloat::IEEEdouble(), "0x0.p1234").convertToDouble());}TEST(APFloatTest, fromDecimalString) {EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1").convertToDouble());EXPECT_EQ(2.0, APFloat(APFloat::IEEEdouble(), "2.").convertToDouble());EXPECT_EQ(0.5, APFloat(APFloat::IEEEdouble(), ".5").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1.0").convertToDouble());EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble(), "-2").convertToDouble());EXPECT_EQ(-4.0, APFloat(APFloat::IEEEdouble(), "-4.").convertToDouble());EXPECT_EQ(-0.5, APFloat(APFloat::IEEEdouble(), "-.5").convertToDouble());EXPECT_EQ(-1.5, APFloat(APFloat::IEEEdouble(), "-1.5").convertToDouble());EXPECT_EQ(1.25e12, APFloat(APFloat::IEEEdouble(), "1.25e12").convertToDouble());EXPECT_EQ(1.25e+12, APFloat(APFloat::IEEEdouble(), "1.25e+12").convertToDouble());EXPECT_EQ(1.25e-12, APFloat(APFloat::IEEEdouble(), "1.25e-12").convertToDouble());EXPECT_EQ(1024.0, APFloat(APFloat::IEEEdouble(), "1024.").convertToDouble());EXPECT_EQ(1024.05, APFloat(APFloat::IEEEdouble(), "1024.05000").convertToDouble());EXPECT_EQ(0.05, APFloat(APFloat::IEEEdouble(), ".05000").convertToDouble());EXPECT_EQ(2.0, APFloat(APFloat::IEEEdouble(), "2.").convertToDouble());EXPECT_EQ(2.0e2, APFloat(APFloat::IEEEdouble(), "2.e2").convertToDouble());EXPECT_EQ(2.0e+2, APFloat(APFloat::IEEEdouble(), "2.e+2").convertToDouble());EXPECT_EQ(2.0e-2, APFloat(APFloat::IEEEdouble(), "2.e-2").convertToDouble());EXPECT_EQ(2.05e2, APFloat(APFloat::IEEEdouble(), "002.05000e2").convertToDouble());EXPECT_EQ(2.05e+2, APFloat(APFloat::IEEEdouble(), "002.05000e+2").convertToDouble());EXPECT_EQ(2.05e-2, APFloat(APFloat::IEEEdouble(), "002.05000e-2").convertToDouble());EXPECT_EQ(2.05e12, APFloat(APFloat::IEEEdouble(), "002.05000e12").convertToDouble());EXPECT_EQ(2.05e+12, APFloat(APFloat::IEEEdouble(), "002.05000e+12").convertToDouble());EXPECT_EQ(2.05e-12, APFloat(APFloat::IEEEdouble(), "002.05000e-12").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1e").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "+1e").convertToDouble());EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-1e").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1.e").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "+1.e").convertToDouble());EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-1.e").convertToDouble());EXPECT_EQ(0.1, APFloat(APFloat::IEEEdouble(), ".1e").convertToDouble());EXPECT_EQ(0.1, APFloat(APFloat::IEEEdouble(), "+.1e").convertToDouble());EXPECT_EQ(-0.1, APFloat(APFloat::IEEEdouble(), "-.1e").convertToDouble());EXPECT_EQ(1.1, APFloat(APFloat::IEEEdouble(), "1.1e").convertToDouble());EXPECT_EQ(1.1, APFloat(APFloat::IEEEdouble(), "+1.1e").convertToDouble());EXPECT_EQ(-1.1, APFloat(APFloat::IEEEdouble(), "-1.1e").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1e+").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1e-").convertToDouble());EXPECT_EQ(0.1, APFloat(APFloat::IEEEdouble(), ".1e").convertToDouble());EXPECT_EQ(0.1, APFloat(APFloat::IEEEdouble(), ".1e+").convertToDouble());EXPECT_EQ(0.1, APFloat(APFloat::IEEEdouble(), ".1e-").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1.0e").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1.0e+").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "1.0e-").convertToDouble());// These are "carefully selected" to overflow the fast log-base// calculations in APFloat.cppEXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "99e99999").isInfinity());EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-99e99999").isInfinity());EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "1e-99999").isPosZero());EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-1e-99999").isNegZero());EXPECT_EQ(2.71828, convertToDoubleFromString("2.71828"));}TEST(APFloatTest, fromStringSpecials) {const fltSemantics &Sem = APFloat::IEEEdouble();const unsigned Precision = 53;const unsigned PayloadBits = Precision - 2;uint64_t PayloadMask = (uint64_t(1) << PayloadBits) - uint64_t(1);uint64_t NaNPayloads[] = {0,1,123,0xDEADBEEF,uint64_t(-2),uint64_t(1) << PayloadBits, // overflow bituint64_t(1) << (PayloadBits - 1), // signaling bituint64_t(1) << (PayloadBits - 2) // highest possible bit};// Convert payload integer to decimal string representation.std::string NaNPayloadDecStrings[array_lengthof(NaNPayloads)];for (size_t I = 0; I < array_lengthof(NaNPayloads); ++I)NaNPayloadDecStrings[I] = utostr(NaNPayloads[I]);// Convert payload integer to hexadecimal string representation.std::string NaNPayloadHexStrings[array_lengthof(NaNPayloads)];for (size_t I = 0; I < array_lengthof(NaNPayloads); ++I)NaNPayloadHexStrings[I] = "0x" + utohexstr(NaNPayloads[I]);// Fix payloads to expected result.for (uint64_t &Payload : NaNPayloads)Payload &= PayloadMask;// Signaling NaN must have a non-zero payload. In case a zero payload is// requested, a default arbitrary payload is set instead. Save this payload// for testing.const uint64_t SNaNDefaultPayload =APFloat::getSNaN(Sem).bitcastToAPInt().getZExtValue() & PayloadMask;// Negative sign prefix (or none - for positive).const char Signs[] = {0, '-'};// "Signaling" prefix (or none - for "Quiet").const char NaNTypes[] = {0, 's', 'S'};const StringRef NaNStrings[] = {"nan", "NaN"};for (StringRef NaNStr : NaNStrings)for (char TypeChar : NaNTypes) {bool Signaling = (TypeChar == 's' || TypeChar == 'S');for (size_t J = 0; J < array_lengthof(NaNPayloads); ++J) {uint64_t Payload = (Signaling && !NaNPayloads[J]) ? SNaNDefaultPayload: NaNPayloads[J];std::string &PayloadDec = NaNPayloadDecStrings[J];std::string &PayloadHex = NaNPayloadHexStrings[J];for (char SignChar : Signs) {bool Negative = (SignChar == '-');std::string TestStrings[5];size_t NumTestStrings = 0;std::string Prefix;if (SignChar)Prefix += SignChar;if (TypeChar)Prefix += TypeChar;Prefix += NaNStr;// Test without any paylod.if (!Payload)TestStrings[NumTestStrings++] = Prefix;// Test with the payload as a suffix.TestStrings[NumTestStrings++] = Prefix + PayloadDec;TestStrings[NumTestStrings++] = Prefix + PayloadHex;// Test with the payload inside parentheses.TestStrings[NumTestStrings++] = Prefix + '(' + PayloadDec + ')';TestStrings[NumTestStrings++] = Prefix + '(' + PayloadHex + ')';for (size_t K = 0; K < NumTestStrings; ++K) {StringRef TestStr = TestStrings[K];APFloat F(Sem);bool HasError = !F.convertFromString(TestStr, llvm::APFloat::rmNearestTiesToEven);EXPECT_FALSE(HasError);EXPECT_TRUE(F.isNaN());EXPECT_EQ(Signaling, F.isSignaling());EXPECT_EQ(Negative, F.isNegative());uint64_t PayloadResult =F.bitcastToAPInt().getZExtValue() & PayloadMask;EXPECT_EQ(Payload, PayloadResult);}}}}const StringRef InfStrings[] = {"inf", "INFINITY", "+Inf","-inf", "-INFINITY", "-Inf"};for (StringRef InfStr : InfStrings) {bool Negative = InfStr.front() == '-';APFloat F(Sem);bool HasError =!F.convertFromString(InfStr, llvm::APFloat::rmNearestTiesToEven);EXPECT_FALSE(HasError);EXPECT_TRUE(F.isInfinity());EXPECT_EQ(Negative, F.isNegative());uint64_t PayloadResult = F.bitcastToAPInt().getZExtValue() & PayloadMask;EXPECT_EQ(UINT64_C(0), PayloadResult);}}TEST(APFloatTest, fromToStringSpecials) {auto expects = [] (const char *first, const char *second) {std::string roundtrip = convertToString(convertToDoubleFromString(second), 0, 3);EXPECT_STREQ(first, roundtrip.c_str());};expects("+Inf", "+Inf");expects("+Inf", "INFINITY");expects("+Inf", "inf");expects("-Inf", "-Inf");expects("-Inf", "-INFINITY");expects("-Inf", "-inf");expects("NaN", "NaN");expects("NaN", "nan");expects("NaN", "-NaN");expects("NaN", "-nan");}TEST(APFloatTest, fromHexadecimalString) {EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble(), "0x1p0").convertToDouble());EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble(), "+0x1p0").convertToDouble());EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-0x1p0").convertToDouble());EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble(), "0x1p+0").convertToDouble());EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble(), "+0x1p+0").convertToDouble());EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-0x1p+0").convertToDouble());EXPECT_EQ( 1.0, APFloat(APFloat::IEEEdouble(), "0x1p-0").convertToDouble());EXPECT_EQ(+1.0, APFloat(APFloat::IEEEdouble(), "+0x1p-0").convertToDouble());EXPECT_EQ(-1.0, APFloat(APFloat::IEEEdouble(), "-0x1p-0").convertToDouble());EXPECT_EQ( 2.0, APFloat(APFloat::IEEEdouble(), "0x1p1").convertToDouble());EXPECT_EQ(+2.0, APFloat(APFloat::IEEEdouble(), "+0x1p1").convertToDouble());EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble(), "-0x1p1").convertToDouble());EXPECT_EQ( 2.0, APFloat(APFloat::IEEEdouble(), "0x1p+1").convertToDouble());EXPECT_EQ(+2.0, APFloat(APFloat::IEEEdouble(), "+0x1p+1").convertToDouble());EXPECT_EQ(-2.0, APFloat(APFloat::IEEEdouble(), "-0x1p+1").convertToDouble());EXPECT_EQ( 0.5, APFloat(APFloat::IEEEdouble(), "0x1p-1").convertToDouble());EXPECT_EQ(+0.5, APFloat(APFloat::IEEEdouble(), "+0x1p-1").convertToDouble());EXPECT_EQ(-0.5, APFloat(APFloat::IEEEdouble(), "-0x1p-1").convertToDouble());EXPECT_EQ( 3.0, APFloat(APFloat::IEEEdouble(), "0x1.8p1").convertToDouble());EXPECT_EQ(+3.0, APFloat(APFloat::IEEEdouble(), "+0x1.8p1").convertToDouble());EXPECT_EQ(-3.0, APFloat(APFloat::IEEEdouble(), "-0x1.8p1").convertToDouble());EXPECT_EQ( 3.0, APFloat(APFloat::IEEEdouble(), "0x1.8p+1").convertToDouble());EXPECT_EQ(+3.0, APFloat(APFloat::IEEEdouble(), "+0x1.8p+1").convertToDouble());EXPECT_EQ(-3.0, APFloat(APFloat::IEEEdouble(), "-0x1.8p+1").convertToDouble());EXPECT_EQ( 0.75, APFloat(APFloat::IEEEdouble(), "0x1.8p-1").convertToDouble());EXPECT_EQ(+0.75, APFloat(APFloat::IEEEdouble(), "+0x1.8p-1").convertToDouble());EXPECT_EQ(-0.75, APFloat(APFloat::IEEEdouble(), "-0x1.8p-1").convertToDouble());EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000.000p1").convertToDouble());EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000.000p1").convertToDouble());EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000.000p1").convertToDouble());EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000.000p+1").convertToDouble());EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000.000p+1").convertToDouble());EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000.000p+1").convertToDouble());EXPECT_EQ( 2048.0, APFloat(APFloat::IEEEdouble(), "0x1000.000p-1").convertToDouble());EXPECT_EQ(+2048.0, APFloat(APFloat::IEEEdouble(), "+0x1000.000p-1").convertToDouble());EXPECT_EQ(-2048.0, APFloat(APFloat::IEEEdouble(), "-0x1000.000p-1").convertToDouble());EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000p1").convertToDouble());EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000p1").convertToDouble());EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000p1").convertToDouble());EXPECT_EQ( 8192.0, APFloat(APFloat::IEEEdouble(), "0x1000p+1").convertToDouble());EXPECT_EQ(+8192.0, APFloat(APFloat::IEEEdouble(), "+0x1000p+1").convertToDouble());EXPECT_EQ(-8192.0, APFloat(APFloat::IEEEdouble(), "-0x1000p+1").convertToDouble());EXPECT_EQ( 2048.0, APFloat(APFloat::IEEEdouble(), "0x1000p-1").convertToDouble());EXPECT_EQ(+2048.0, APFloat(APFloat::IEEEdouble(), "+0x1000p-1").convertToDouble());EXPECT_EQ(-2048.0, APFloat(APFloat::IEEEdouble(), "-0x1000p-1").convertToDouble());EXPECT_EQ( 16384.0, APFloat(APFloat::IEEEdouble(), "0x10p10").convertToDouble());EXPECT_EQ(+16384.0, APFloat(APFloat::IEEEdouble(), "+0x10p10").convertToDouble());EXPECT_EQ(-16384.0, APFloat(APFloat::IEEEdouble(), "-0x10p10").convertToDouble());EXPECT_EQ( 16384.0, APFloat(APFloat::IEEEdouble(), "0x10p+10").convertToDouble());EXPECT_EQ(+16384.0, APFloat(APFloat::IEEEdouble(), "+0x10p+10").convertToDouble());EXPECT_EQ(-16384.0, APFloat(APFloat::IEEEdouble(), "-0x10p+10").convertToDouble());EXPECT_EQ( 0.015625, APFloat(APFloat::IEEEdouble(), "0x10p-10").convertToDouble());EXPECT_EQ(+0.015625, APFloat(APFloat::IEEEdouble(), "+0x10p-10").convertToDouble());EXPECT_EQ(-0.015625, APFloat(APFloat::IEEEdouble(), "-0x10p-10").convertToDouble());EXPECT_EQ(1.0625, APFloat(APFloat::IEEEdouble(), "0x1.1p0").convertToDouble());EXPECT_EQ(1.0, APFloat(APFloat::IEEEdouble(), "0x1p0").convertToDouble());EXPECT_EQ(convertToDoubleFromString("0x1p-150"),convertToDoubleFromString("+0x800000000000000001.p-221"));EXPECT_EQ(2251799813685248.5,convertToDoubleFromString("0x80000000000004000000.010p-28"));}TEST(APFloatTest, toString) {ASSERT_EQ("10", convertToString(10.0, 6, 3));ASSERT_EQ("1.0E+1", convertToString(10.0, 6, 0));ASSERT_EQ("10100", convertToString(1.01E+4, 5, 2));ASSERT_EQ("1.01E+4", convertToString(1.01E+4, 4, 2));ASSERT_EQ("1.01E+4", convertToString(1.01E+4, 5, 1));ASSERT_EQ("0.0101", convertToString(1.01E-2, 5, 2));ASSERT_EQ("0.0101", convertToString(1.01E-2, 4, 2));ASSERT_EQ("1.01E-2", convertToString(1.01E-2, 5, 1));ASSERT_EQ("0.78539816339744828", convertToString(0.78539816339744830961, 0, 3));ASSERT_EQ("4.9406564584124654E-324", convertToString(4.9406564584124654e-324, 0, 3));ASSERT_EQ("873.18340000000001", convertToString(873.1834, 0, 1));ASSERT_EQ("8.7318340000000001E+2", convertToString(873.1834, 0, 0));ASSERT_EQ("1.7976931348623157E+308", convertToString(1.7976931348623157E+308, 0, 0));ASSERT_EQ("10", convertToString(10.0, 6, 3, false));ASSERT_EQ("1.000000e+01", convertToString(10.0, 6, 0, false));ASSERT_EQ("10100", convertToString(1.01E+4, 5, 2, false));ASSERT_EQ("1.0100e+04", convertToString(1.01E+4, 4, 2, false));ASSERT_EQ("1.01000e+04", convertToString(1.01E+4, 5, 1, false));ASSERT_EQ("0.0101", convertToString(1.01E-2, 5, 2, false));ASSERT_EQ("0.0101", convertToString(1.01E-2, 4, 2, false));ASSERT_EQ("1.01000e-02", convertToString(1.01E-2, 5, 1, false));ASSERT_EQ("0.78539816339744828",convertToString(0.78539816339744830961, 0, 3, false));ASSERT_EQ("4.94065645841246540e-324",convertToString(4.9406564584124654e-324, 0, 3, false));ASSERT_EQ("873.18340000000001", convertToString(873.1834, 0, 1, false));ASSERT_EQ("8.73183400000000010e+02", convertToString(873.1834, 0, 0, false));ASSERT_EQ("1.79769313486231570e+308",convertToString(1.7976931348623157E+308, 0, 0, false));{SmallString<64> Str;APFloat UnnormalZero(APFloat::x87DoubleExtended(), APInt(80, {0, 1}));UnnormalZero.toString(Str);ASSERT_EQ("NaN", Str);}}TEST(APFloatTest, toInteger) {bool isExact = false;APSInt result(5, /*isUnsigned=*/true);EXPECT_EQ(APFloat::opOK,APFloat(APFloat::IEEEdouble(), "10").convertToInteger(result, APFloat::rmTowardZero, &isExact));EXPECT_TRUE(isExact);EXPECT_EQ(APSInt(APInt(5, 10), true), result);EXPECT_EQ(APFloat::opInvalidOp,APFloat(APFloat::IEEEdouble(), "-10").convertToInteger(result, APFloat::rmTowardZero, &isExact));EXPECT_FALSE(isExact);EXPECT_EQ(APSInt::getMinValue(5, true), result);EXPECT_EQ(APFloat::opInvalidOp,APFloat(APFloat::IEEEdouble(), "32").convertToInteger(result, APFloat::rmTowardZero, &isExact));EXPECT_FALSE(isExact);EXPECT_EQ(APSInt::getMaxValue(5, true), result);EXPECT_EQ(APFloat::opInexact,APFloat(APFloat::IEEEdouble(), "7.9").convertToInteger(result, APFloat::rmTowardZero, &isExact));EXPECT_FALSE(isExact);EXPECT_EQ(APSInt(APInt(5, 7), true), result);result.setIsUnsigned(false);EXPECT_EQ(APFloat::opOK,APFloat(APFloat::IEEEdouble(), "-10").convertToInteger(result, APFloat::rmTowardZero, &isExact));EXPECT_TRUE(isExact);EXPECT_EQ(APSInt(APInt(5, -10, true), false), result);EXPECT_EQ(APFloat::opInvalidOp,APFloat(APFloat::IEEEdouble(), "-17").convertToInteger(result, APFloat::rmTowardZero, &isExact));EXPECT_FALSE(isExact);EXPECT_EQ(APSInt::getMinValue(5, false), result);EXPECT_EQ(APFloat::opInvalidOp,APFloat(APFloat::IEEEdouble(), "16").convertToInteger(result, APFloat::rmTowardZero, &isExact));EXPECT_FALSE(isExact);EXPECT_EQ(APSInt::getMaxValue(5, false), result);}static APInt nanbitsFromAPInt(const fltSemantics &Sem, bool SNaN, bool Negative,uint64_t payload) {APInt appayload(64, payload);if (SNaN)return APFloat::getSNaN(Sem, Negative, &appayload).bitcastToAPInt();elsereturn APFloat::getQNaN(Sem, Negative, &appayload).bitcastToAPInt();}TEST(APFloatTest, makeNaN) {const struct {uint64_t expected;const fltSemantics &semantics;bool SNaN;bool Negative;uint64_t payload;} tests[] = {/* expected semantics SNaN Neg payload */{ 0x7fc00000ULL, APFloat::IEEEsingle(), false, false, 0x00000000ULL },{ 0xffc00000ULL, APFloat::IEEEsingle(), false, true, 0x00000000ULL },{ 0x7fc0ae72ULL, APFloat::IEEEsingle(), false, false, 0x0000ae72ULL },{ 0x7fffae72ULL, APFloat::IEEEsingle(), false, false, 0xffffae72ULL },{ 0x7fdaae72ULL, APFloat::IEEEsingle(), false, false, 0x00daae72ULL },{ 0x7fa00000ULL, APFloat::IEEEsingle(), true, false, 0x00000000ULL },{ 0xffa00000ULL, APFloat::IEEEsingle(), true, true, 0x00000000ULL },{ 0x7f80ae72ULL, APFloat::IEEEsingle(), true, false, 0x0000ae72ULL },{ 0x7fbfae72ULL, APFloat::IEEEsingle(), true, false, 0xffffae72ULL },{ 0x7f9aae72ULL, APFloat::IEEEsingle(), true, false, 0x001aae72ULL },{ 0x7ff8000000000000ULL, APFloat::IEEEdouble(), false, false, 0x0000000000000000ULL },{ 0xfff8000000000000ULL, APFloat::IEEEdouble(), false, true, 0x0000000000000000ULL },{ 0x7ff800000000ae72ULL, APFloat::IEEEdouble(), false, false, 0x000000000000ae72ULL },{ 0x7fffffffffffae72ULL, APFloat::IEEEdouble(), false, false, 0xffffffffffffae72ULL },{ 0x7ffdaaaaaaaaae72ULL, APFloat::IEEEdouble(), false, false, 0x000daaaaaaaaae72ULL },{ 0x7ff4000000000000ULL, APFloat::IEEEdouble(), true, false, 0x0000000000000000ULL },{ 0xfff4000000000000ULL, APFloat::IEEEdouble(), true, true, 0x0000000000000000ULL },{ 0x7ff000000000ae72ULL, APFloat::IEEEdouble(), true, false, 0x000000000000ae72ULL },{ 0x7ff7ffffffffae72ULL, APFloat::IEEEdouble(), true, false, 0xffffffffffffae72ULL },{ 0x7ff1aaaaaaaaae72ULL, APFloat::IEEEdouble(), true, false, 0x0001aaaaaaaaae72ULL },};for (const auto &t : tests) {ASSERT_EQ(t.expected, nanbitsFromAPInt(t.semantics, t.SNaN, t.Negative, t.payload));}}#ifdef GTEST_HAS_DEATH_TEST#ifndef NDEBUGTEST(APFloatTest, SemanticsDeath) {EXPECT_DEATH(APFloat(APFloat::IEEEquad(), 0).convertToDouble(),"Float semantics is not representable by IEEEdouble");EXPECT_DEATH(APFloat(APFloat::IEEEdouble(), 0).convertToFloat(),"Float semantics is not representable by IEEEsingle");}#endif#endifTEST(APFloatTest, StringDecimalError) {EXPECT_EQ("Invalid string length", convertToErrorFromString(""));EXPECT_EQ("String has no digits", convertToErrorFromString("+"));EXPECT_EQ("String has no digits", convertToErrorFromString("-"));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("\0", 1)));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("1\0", 2)));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("1" "\0" "2", 3)));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("1" "\0" "2e1", 5)));EXPECT_EQ("Invalid character in exponent", convertToErrorFromString(StringRef("1e\0", 3)));EXPECT_EQ("Invalid character in exponent", convertToErrorFromString(StringRef("1e1\0", 4)));EXPECT_EQ("Invalid character in exponent", convertToErrorFromString(StringRef("1e1" "\0" "2", 5)));EXPECT_EQ("Invalid character in significand", convertToErrorFromString("1.0f"));EXPECT_EQ("String contains multiple dots", convertToErrorFromString(".."));EXPECT_EQ("String contains multiple dots", convertToErrorFromString("..0"));EXPECT_EQ("String contains multiple dots", convertToErrorFromString("1.0.0"));}TEST(APFloatTest, StringDecimalSignificandError) {EXPECT_EQ("Significand has no digits", convertToErrorFromString( "."));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+."));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-."));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "e"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+e"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-e"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "e1"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+e1"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-e1"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( ".e1"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+.e1"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-.e1"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( ".e"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+.e"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-.e"));}TEST(APFloatTest, StringHexadecimalError) {EXPECT_EQ("Invalid string", convertToErrorFromString( "0x"));EXPECT_EQ("Invalid string", convertToErrorFromString("+0x"));EXPECT_EQ("Invalid string", convertToErrorFromString("-0x"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString( "0x0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("+0x0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("-0x0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString( "0x0."));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("+0x0."));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("-0x0."));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString( "0x.0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("+0x.0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("-0x.0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString( "0x0.0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("+0x0.0"));EXPECT_EQ("Hex strings require an exponent", convertToErrorFromString("-0x0.0"));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("0x\0", 3)));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("0x1\0", 4)));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("0x1" "\0" "2", 5)));EXPECT_EQ("Invalid character in significand", convertToErrorFromString(StringRef("0x1" "\0" "2p1", 7)));EXPECT_EQ("Invalid character in exponent", convertToErrorFromString(StringRef("0x1p\0", 5)));EXPECT_EQ("Invalid character in exponent", convertToErrorFromString(StringRef("0x1p1\0", 6)));EXPECT_EQ("Invalid character in exponent", convertToErrorFromString(StringRef("0x1p1" "\0" "2", 7)));EXPECT_EQ("Invalid character in exponent", convertToErrorFromString("0x1p0f"));EXPECT_EQ("String contains multiple dots", convertToErrorFromString("0x..p1"));EXPECT_EQ("String contains multiple dots", convertToErrorFromString("0x..0p1"));EXPECT_EQ("String contains multiple dots", convertToErrorFromString("0x1.0.0p1"));}TEST(APFloatTest, StringHexadecimalSignificandError) {EXPECT_EQ("Significand has no digits", convertToErrorFromString( "0x."));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+0x."));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-0x."));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "0xp"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+0xp"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-0xp"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "0xp+"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+0xp+"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-0xp+"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "0xp-"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+0xp-"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-0xp-"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "0x.p"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+0x.p"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-0x.p"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "0x.p+"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+0x.p+"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-0x.p+"));EXPECT_EQ("Significand has no digits", convertToErrorFromString( "0x.p-"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("+0x.p-"));EXPECT_EQ("Significand has no digits", convertToErrorFromString("-0x.p-"));}TEST(APFloatTest, StringHexadecimalExponentError) {EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1.p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1.p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1.p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1.p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1.p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1.p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1.p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1.p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1.p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x.1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x.1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x.1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x.1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x.1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x.1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x.1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x.1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x.1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1.1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1.1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1.1p"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1.1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1.1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1.1p+"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString( "0x1.1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("+0x1.1p-"));EXPECT_EQ("Exponent has no digits", convertToErrorFromString("-0x1.1p-"));}TEST(APFloatTest, exactInverse) {APFloat inv(0.0f);// Trivial operation.EXPECT_TRUE(APFloat(2.0).getExactInverse(&inv));EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(0.5)));EXPECT_TRUE(APFloat(2.0f).getExactInverse(&inv));EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(0.5f)));EXPECT_TRUE(APFloat(APFloat::IEEEquad(), "2.0").getExactInverse(&inv));EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::IEEEquad(), "0.5")));EXPECT_TRUE(APFloat(APFloat::PPCDoubleDouble(), "2.0").getExactInverse(&inv));EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::PPCDoubleDouble(), "0.5")));EXPECT_TRUE(APFloat(APFloat::x87DoubleExtended(), "2.0").getExactInverse(&inv));EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::x87DoubleExtended(), "0.5")));// FLT_MINEXPECT_TRUE(APFloat(1.17549435e-38f).getExactInverse(&inv));EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(8.5070592e+37f)));// Large float, inverse is a denormal.EXPECT_FALSE(APFloat(1.7014118e38f).getExactInverse(nullptr));// ZeroEXPECT_FALSE(APFloat(0.0).getExactInverse(nullptr));// Denormalized floatEXPECT_FALSE(APFloat(1.40129846e-45f).getExactInverse(nullptr));}TEST(APFloatTest, roundToIntegral) {APFloat T(-0.5), S(3.14), R(APFloat::getLargest(APFloat::IEEEdouble())), P(0.0);P = T;P.roundToIntegral(APFloat::rmTowardZero);EXPECT_EQ(-0.0, P.convertToDouble());P = T;P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_EQ(-1.0, P.convertToDouble());P = T;P.roundToIntegral(APFloat::rmTowardPositive);EXPECT_EQ(-0.0, P.convertToDouble());P = T;P.roundToIntegral(APFloat::rmNearestTiesToEven);EXPECT_EQ(-0.0, P.convertToDouble());P = S;P.roundToIntegral(APFloat::rmTowardZero);EXPECT_EQ(3.0, P.convertToDouble());P = S;P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_EQ(3.0, P.convertToDouble());P = S;P.roundToIntegral(APFloat::rmTowardPositive);EXPECT_EQ(4.0, P.convertToDouble());P = S;P.roundToIntegral(APFloat::rmNearestTiesToEven);EXPECT_EQ(3.0, P.convertToDouble());P = R;P.roundToIntegral(APFloat::rmTowardZero);EXPECT_EQ(R.convertToDouble(), P.convertToDouble());P = R;P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_EQ(R.convertToDouble(), P.convertToDouble());P = R;P.roundToIntegral(APFloat::rmTowardPositive);EXPECT_EQ(R.convertToDouble(), P.convertToDouble());P = R;P.roundToIntegral(APFloat::rmNearestTiesToEven);EXPECT_EQ(R.convertToDouble(), P.convertToDouble());P = APFloat::getZero(APFloat::IEEEdouble());P.roundToIntegral(APFloat::rmTowardZero);EXPECT_EQ(0.0, P.convertToDouble());P = APFloat::getZero(APFloat::IEEEdouble(), true);P.roundToIntegral(APFloat::rmTowardZero);EXPECT_EQ(-0.0, P.convertToDouble());P = APFloat::getNaN(APFloat::IEEEdouble());P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(std::isnan(P.convertToDouble()));P = APFloat::getInf(APFloat::IEEEdouble());P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(std::isinf(P.convertToDouble()) && P.convertToDouble() > 0.0);P = APFloat::getInf(APFloat::IEEEdouble(), true);P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(std::isinf(P.convertToDouble()) && P.convertToDouble() < 0.0);APFloat::opStatus St;P = APFloat::getNaN(APFloat::IEEEdouble());St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isNaN());EXPECT_FALSE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat::getNaN(APFloat::IEEEdouble(), true);St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isNaN());EXPECT_TRUE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat::getSNaN(APFloat::IEEEdouble());St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isNaN());EXPECT_FALSE(P.isSignaling());EXPECT_FALSE(P.isNegative());EXPECT_EQ(APFloat::opInvalidOp, St);P = APFloat::getSNaN(APFloat::IEEEdouble(), true);St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isNaN());EXPECT_FALSE(P.isSignaling());EXPECT_TRUE(P.isNegative());EXPECT_EQ(APFloat::opInvalidOp, St);P = APFloat::getInf(APFloat::IEEEdouble());St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isInfinity());EXPECT_FALSE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat::getInf(APFloat::IEEEdouble(), true);St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isInfinity());EXPECT_TRUE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat::getZero(APFloat::IEEEdouble(), false);St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isZero());EXPECT_FALSE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat::getZero(APFloat::IEEEdouble(), false);St = P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_TRUE(P.isZero());EXPECT_FALSE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat::getZero(APFloat::IEEEdouble(), true);St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_TRUE(P.isZero());EXPECT_TRUE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat::getZero(APFloat::IEEEdouble(), true);St = P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_TRUE(P.isZero());EXPECT_TRUE(P.isNegative());EXPECT_EQ(APFloat::opOK, St);P = APFloat(1E-100);St = P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_TRUE(P.isZero());EXPECT_FALSE(P.isNegative());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(1E-100);St = P.roundToIntegral(APFloat::rmTowardPositive);EXPECT_EQ(1.0, P.convertToDouble());EXPECT_FALSE(P.isNegative());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(-1E-100);St = P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_TRUE(P.isNegative());EXPECT_EQ(-1.0, P.convertToDouble());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(-1E-100);St = P.roundToIntegral(APFloat::rmTowardPositive);EXPECT_TRUE(P.isZero());EXPECT_TRUE(P.isNegative());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(10.0);St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_EQ(10.0, P.convertToDouble());EXPECT_EQ(APFloat::opOK, St);P = APFloat(10.5);St = P.roundToIntegral(APFloat::rmTowardZero);EXPECT_EQ(10.0, P.convertToDouble());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(10.5);St = P.roundToIntegral(APFloat::rmTowardPositive);EXPECT_EQ(11.0, P.convertToDouble());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(10.5);St = P.roundToIntegral(APFloat::rmTowardNegative);EXPECT_EQ(10.0, P.convertToDouble());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(10.5);St = P.roundToIntegral(APFloat::rmNearestTiesToAway);EXPECT_EQ(11.0, P.convertToDouble());EXPECT_EQ(APFloat::opInexact, St);P = APFloat(10.5);St = P.roundToIntegral(APFloat::rmNearestTiesToEven);EXPECT_EQ(10.0, P.convertToDouble());EXPECT_EQ(APFloat::opInexact, St);}TEST(APFloatTest, isInteger) {APFloat T(-0.0);EXPECT_TRUE(T.isInteger());T = APFloat(3.14159);EXPECT_FALSE(T.isInteger());T = APFloat::getNaN(APFloat::IEEEdouble());EXPECT_FALSE(T.isInteger());T = APFloat::getInf(APFloat::IEEEdouble());EXPECT_FALSE(T.isInteger());T = APFloat::getInf(APFloat::IEEEdouble(), true);EXPECT_FALSE(T.isInteger());T = APFloat::getLargest(APFloat::IEEEdouble());EXPECT_TRUE(T.isInteger());}TEST(DoubleAPFloatTest, isInteger) {APFloat F1(-0.0);APFloat F2(-0.0);llvm::detail::DoubleAPFloat T(APFloat::PPCDoubleDouble(), std::move(F1),std::move(F2));EXPECT_TRUE(T.isInteger());APFloat F3(3.14159);APFloat F4(-0.0);llvm::detail::DoubleAPFloat T2(APFloat::PPCDoubleDouble(), std::move(F3),std::move(F4));EXPECT_FALSE(T2.isInteger());APFloat F5(-0.0);APFloat F6(3.14159);llvm::detail::DoubleAPFloat T3(APFloat::PPCDoubleDouble(), std::move(F5),std::move(F6));EXPECT_FALSE(T3.isInteger());}TEST(APFloatTest, getLargest) {EXPECT_EQ(3.402823466e+38f, APFloat::getLargest(APFloat::IEEEsingle()).convertToFloat());EXPECT_EQ(1.7976931348623158e+308, APFloat::getLargest(APFloat::IEEEdouble()).convertToDouble());}TEST(APFloatTest, getSmallest) {APFloat test = APFloat::getSmallest(APFloat::IEEEsingle(), false);APFloat expected = APFloat(APFloat::IEEEsingle(), "0x0.000002p-126");EXPECT_FALSE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));test = APFloat::getSmallest(APFloat::IEEEsingle(), true);expected = APFloat(APFloat::IEEEsingle(), "-0x0.000002p-126");EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));test = APFloat::getSmallest(APFloat::IEEEquad(), false);expected = APFloat(APFloat::IEEEquad(), "0x0.0000000000000000000000000001p-16382");EXPECT_FALSE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));test = APFloat::getSmallest(APFloat::IEEEquad(), true);expected = APFloat(APFloat::IEEEquad(), "-0x0.0000000000000000000000000001p-16382");EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_TRUE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));}TEST(APFloatTest, getSmallestNormalized) {APFloat test = APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);APFloat expected = APFloat(APFloat::IEEEsingle(), "0x1p-126");EXPECT_FALSE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_FALSE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));test = APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);expected = APFloat(APFloat::IEEEsingle(), "-0x1p-126");EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_FALSE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));test = APFloat::getSmallestNormalized(APFloat::IEEEquad(), false);expected = APFloat(APFloat::IEEEquad(), "0x1p-16382");EXPECT_FALSE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_FALSE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));test = APFloat::getSmallestNormalized(APFloat::IEEEquad(), true);expected = APFloat(APFloat::IEEEquad(), "-0x1p-16382");EXPECT_TRUE(test.isNegative());EXPECT_TRUE(test.isFiniteNonZero());EXPECT_FALSE(test.isDenormal());EXPECT_TRUE(test.bitwiseIsEqual(expected));}TEST(APFloatTest, getZero) {struct {const fltSemantics *semantics;const bool sign;const unsigned long long bitPattern[2];const unsigned bitPatternLength;} const GetZeroTest[] = {{ &APFloat::IEEEhalf(), false, {0, 0}, 1},{ &APFloat::IEEEhalf(), true, {0x8000ULL, 0}, 1},{ &APFloat::IEEEsingle(), false, {0, 0}, 1},{ &APFloat::IEEEsingle(), true, {0x80000000ULL, 0}, 1},{ &APFloat::IEEEdouble(), false, {0, 0}, 1},{ &APFloat::IEEEdouble(), true, {0x8000000000000000ULL, 0}, 1},{ &APFloat::IEEEquad(), false, {0, 0}, 2},{ &APFloat::IEEEquad(), true, {0, 0x8000000000000000ULL}, 2},{ &APFloat::PPCDoubleDouble(), false, {0, 0}, 2},{ &APFloat::PPCDoubleDouble(), true, {0x8000000000000000ULL, 0}, 2},{ &APFloat::x87DoubleExtended(), false, {0, 0}, 2},{ &APFloat::x87DoubleExtended(), true, {0, 0x8000ULL}, 2},};const unsigned NumGetZeroTests = 12;for (unsigned i = 0; i < NumGetZeroTests; ++i) {APFloat test = APFloat::getZero(*GetZeroTest[i].semantics,GetZeroTest[i].sign);const char *pattern = GetZeroTest[i].sign? "-0x0p+0" : "0x0p+0";APFloat expected = APFloat(*GetZeroTest[i].semantics,pattern);EXPECT_TRUE(test.isZero());EXPECT_TRUE(GetZeroTest[i].sign? test.isNegative() : !test.isNegative());EXPECT_TRUE(test.bitwiseIsEqual(expected));for (unsigned j = 0, je = GetZeroTest[i].bitPatternLength; j < je; ++j) {EXPECT_EQ(GetZeroTest[i].bitPattern[j],test.bitcastToAPInt().getRawData()[j]);}}}TEST(APFloatTest, copySign) {EXPECT_TRUE(APFloat(-42.0).bitwiseIsEqual(APFloat::copySign(APFloat(42.0), APFloat(-1.0))));EXPECT_TRUE(APFloat(42.0).bitwiseIsEqual(APFloat::copySign(APFloat(-42.0), APFloat(1.0))));EXPECT_TRUE(APFloat(-42.0).bitwiseIsEqual(APFloat::copySign(APFloat(-42.0), APFloat(-1.0))));EXPECT_TRUE(APFloat(42.0).bitwiseIsEqual(APFloat::copySign(APFloat(42.0), APFloat(1.0))));}TEST(APFloatTest, convert) {bool losesInfo;APFloat test(APFloat::IEEEdouble(), "1.0");test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(1.0f, test.convertToFloat());EXPECT_FALSE(losesInfo);test = APFloat(APFloat::x87DoubleExtended(), "0x1p-53");test.add(APFloat(APFloat::x87DoubleExtended(), "1.0"), APFloat::rmNearestTiesToEven);test.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(1.0, test.convertToDouble());EXPECT_TRUE(losesInfo);test = APFloat(APFloat::IEEEquad(), "0x1p-53");test.add(APFloat(APFloat::IEEEquad(), "1.0"), APFloat::rmNearestTiesToEven);test.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(1.0, test.convertToDouble());EXPECT_TRUE(losesInfo);test = APFloat(APFloat::x87DoubleExtended(), "0xf.fffffffp+28");test.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(4294967295.0, test.convertToDouble());EXPECT_FALSE(losesInfo);test = APFloat::getSNaN(APFloat::IEEEsingle());APFloat::opStatus status = test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven, &losesInfo);// Conversion quiets the SNAN, so now 2 bits of the 64-bit significand should be set.APInt topTwoBits(64, 0x6000000000000000);EXPECT_TRUE(test.bitwiseIsEqual(APFloat::getQNaN(APFloat::x87DoubleExtended(), false, &topTwoBits)));EXPECT_FALSE(losesInfo);EXPECT_EQ(status, APFloat::opInvalidOp);test = APFloat::getQNaN(APFloat::IEEEsingle());APFloat X87QNaN = APFloat::getQNaN(APFloat::x87DoubleExtended());test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven,&losesInfo);EXPECT_TRUE(test.bitwiseIsEqual(X87QNaN));EXPECT_FALSE(losesInfo);test = APFloat::getSNaN(APFloat::x87DoubleExtended());test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven,&losesInfo);APFloat X87SNaN = APFloat::getSNaN(APFloat::x87DoubleExtended());EXPECT_TRUE(test.bitwiseIsEqual(X87SNaN));EXPECT_FALSE(losesInfo);test = APFloat::getQNaN(APFloat::x87DoubleExtended());test.convert(APFloat::x87DoubleExtended(), APFloat::rmNearestTiesToEven,&losesInfo);EXPECT_TRUE(test.bitwiseIsEqual(X87QNaN));EXPECT_FALSE(losesInfo);// The payload is lost in truncation, but we retain NaN by setting the quiet bit.APInt payload(52, 1);test = APFloat::getSNaN(APFloat::IEEEdouble(), false, &payload);status = test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0x7fc00000, test.bitcastToAPInt());EXPECT_TRUE(losesInfo);EXPECT_EQ(status, APFloat::opInvalidOp);// The payload is lost in truncation. QNaN remains QNaN.test = APFloat::getQNaN(APFloat::IEEEdouble(), false, &payload);status = test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0x7fc00000, test.bitcastToAPInt());EXPECT_TRUE(losesInfo);EXPECT_EQ(status, APFloat::opOK);// Test that subnormals are handled correctly in double to float conversiontest = APFloat(APFloat::IEEEdouble(), "0x0.0000010000000p-1022");test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0.0f, test.convertToFloat());EXPECT_TRUE(losesInfo);test = APFloat(APFloat::IEEEdouble(), "0x0.0000010000001p-1022");test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0.0f, test.convertToFloat());EXPECT_TRUE(losesInfo);test = APFloat(APFloat::IEEEdouble(), "-0x0.0000010000001p-1022");test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0.0f, test.convertToFloat());EXPECT_TRUE(losesInfo);test = APFloat(APFloat::IEEEdouble(), "0x0.0000020000000p-1022");test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0.0f, test.convertToFloat());EXPECT_TRUE(losesInfo);test = APFloat(APFloat::IEEEdouble(), "0x0.0000020000001p-1022");test.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0.0f, test.convertToFloat());EXPECT_TRUE(losesInfo);// Test subnormal conversion to bfloattest = APFloat(APFloat::IEEEsingle(), "0x0.01p-126");test.convert(APFloat::BFloat(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0.0f, test.convertToFloat());EXPECT_TRUE(losesInfo);test = APFloat(APFloat::IEEEsingle(), "0x0.02p-126");test.convert(APFloat::BFloat(), APFloat::rmNearestTiesToEven, &losesInfo);EXPECT_EQ(0x01, test.bitcastToAPInt());EXPECT_FALSE(losesInfo);test = APFloat(APFloat::IEEEsingle(), "0x0.01p-126");test.convert(APFloat::BFloat(), APFloat::rmNearestTiesToAway, &losesInfo);EXPECT_EQ(0x01, test.bitcastToAPInt());EXPECT_TRUE(losesInfo);}TEST(APFloatTest, PPCDoubleDouble) {APFloat test(APFloat::PPCDoubleDouble(), "1.0");EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]);EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]);// LDBL_MAXtest = APFloat(APFloat::PPCDoubleDouble(), "1.79769313486231580793728971405301e+308");EXPECT_EQ(0x7fefffffffffffffull, test.bitcastToAPInt().getRawData()[0]);EXPECT_EQ(0x7c8ffffffffffffeull, test.bitcastToAPInt().getRawData()[1]);// LDBL_MINtest = APFloat(APFloat::PPCDoubleDouble(), "2.00416836000897277799610805135016e-292");EXPECT_EQ(0x0360000000000000ull, test.bitcastToAPInt().getRawData()[0]);EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]);// PR30869{auto Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") +APFloat(APFloat::PPCDoubleDouble(), "1.0");EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics());Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") -APFloat(APFloat::PPCDoubleDouble(), "1.0");EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics());Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") *APFloat(APFloat::PPCDoubleDouble(), "1.0");EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics());Result = APFloat(APFloat::PPCDoubleDouble(), "1.0") /APFloat(APFloat::PPCDoubleDouble(), "1.0");EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics());int Exp;Result = frexp(APFloat(APFloat::PPCDoubleDouble(), "1.0"), Exp,APFloat::rmNearestTiesToEven);EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics());Result = scalbn(APFloat(APFloat::PPCDoubleDouble(), "1.0"), 1,APFloat::rmNearestTiesToEven);EXPECT_EQ(&APFloat::PPCDoubleDouble(), &Result.getSemantics());}}TEST(APFloatTest, isNegative) {APFloat t(APFloat::IEEEsingle(), "0x1p+0");EXPECT_FALSE(t.isNegative());t = APFloat(APFloat::IEEEsingle(), "-0x1p+0");EXPECT_TRUE(t.isNegative());EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isNegative());EXPECT_TRUE(APFloat::getInf(APFloat::IEEEsingle(), true).isNegative());EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isNegative());EXPECT_TRUE(APFloat::getZero(APFloat::IEEEsingle(), true).isNegative());EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isNegative());EXPECT_TRUE(APFloat::getNaN(APFloat::IEEEsingle(), true).isNegative());EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isNegative());EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), true).isNegative());}TEST(APFloatTest, isNormal) {APFloat t(APFloat::IEEEsingle(), "0x1p+0");EXPECT_TRUE(t.isNormal());EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isNormal());EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isNormal());EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isNormal());EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isNormal());EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isNormal());}TEST(APFloatTest, isFinite) {APFloat t(APFloat::IEEEsingle(), "0x1p+0");EXPECT_TRUE(t.isFinite());EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isFinite());EXPECT_TRUE(APFloat::getZero(APFloat::IEEEsingle(), false).isFinite());EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isFinite());EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isFinite());EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isFinite());}TEST(APFloatTest, isInfinity) {APFloat t(APFloat::IEEEsingle(), "0x1p+0");EXPECT_FALSE(t.isInfinity());EXPECT_TRUE(APFloat::getInf(APFloat::IEEEsingle(), false).isInfinity());EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isInfinity());EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isInfinity());EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isInfinity());EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isInfinity());}TEST(APFloatTest, isNaN) {APFloat t(APFloat::IEEEsingle(), "0x1p+0");EXPECT_FALSE(t.isNaN());EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isNaN());EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isNaN());EXPECT_TRUE(APFloat::getNaN(APFloat::IEEEsingle(), false).isNaN());EXPECT_TRUE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isNaN());EXPECT_FALSE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isNaN());}TEST(APFloatTest, isFiniteNonZero) {// Test positive/negative normal value.EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p+0").isFiniteNonZero());EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "-0x1p+0").isFiniteNonZero());// Test positive/negative denormal value.EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p-149").isFiniteNonZero());EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "-0x1p-149").isFiniteNonZero());// Test +/- Infinity.EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), false).isFiniteNonZero());EXPECT_FALSE(APFloat::getInf(APFloat::IEEEsingle(), true).isFiniteNonZero());// Test +/- Zero.EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), false).isFiniteNonZero());EXPECT_FALSE(APFloat::getZero(APFloat::IEEEsingle(), true).isFiniteNonZero());// Test +/- qNaN. +/- dont mean anything with qNaN but paranoia can't hurt in// this instance.EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), false).isFiniteNonZero());EXPECT_FALSE(APFloat::getNaN(APFloat::IEEEsingle(), true).isFiniteNonZero());// Test +/- sNaN. +/- dont mean anything with sNaN but paranoia can't hurt in// this instance.EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), false).isFiniteNonZero());EXPECT_FALSE(APFloat::getSNaN(APFloat::IEEEsingle(), true).isFiniteNonZero());}TEST(APFloatTest, add) {// Test Special Cases against each other and normal values.APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat SNaN = APFloat(APFloat::IEEEsingle(), "snan123");APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0");APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0");APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false);APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true);APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false);APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true);APFloat PSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);APFloat MSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact;struct {APFloat x;APFloat y;const char *result;int status;int category;} SpecialCaseTests[] = {{ PInf, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PZero, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MZero, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PZero, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MZero, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PZero, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PZero, PLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PZero, MLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PZero, PSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PZero, MSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PZero, PSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PZero, MSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MZero, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MZero, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MZero, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MZero, PLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MZero, MLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MZero, PSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MZero, MSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MZero, PSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MZero, MSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ SNaN, PInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, QNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PNormalValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PNormalValue, PZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PNormalValue, "0x1p+1", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, PSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, MSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, PSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, MSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MNormalValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MNormalValue, PZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MNormalValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, MNormalValue, "-0x1p+1", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, PSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, MSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, PSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, MSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PLargestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PLargestValue, PZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, PLargestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ PLargestValue, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, PSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, MSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, PSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, MSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MLargestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MLargestValue, PZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, MLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, PSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, MSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, PSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, MSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestValue, PZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestValue, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, PSmallestValue, "0x1p-148", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, PSmallestNormalized, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MSmallestNormalized, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestValue, PZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestValue, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, MSmallestValue, "-0x1p-148", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PSmallestNormalized, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MSmallestNormalized, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestNormalized, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestNormalized, PZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestNormalized, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, PSmallestValue, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MSmallestValue, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PSmallestNormalized, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestNormalized, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestNormalized, PZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestNormalized, PNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, MNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, PLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, MLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, PSmallestValue, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MSmallestValue, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, MSmallestNormalized, "-0x1p-125", APFloat::opOK, APFloat::fcNormal }};for (size_t i = 0; i < array_lengthof(SpecialCaseTests); ++i) {APFloat x(SpecialCaseTests[i].x);APFloat y(SpecialCaseTests[i].y);APFloat::opStatus status = x.add(y, APFloat::rmNearestTiesToEven);APFloat result(APFloat::IEEEsingle(), SpecialCaseTests[i].result);EXPECT_TRUE(result.bitwiseIsEqual(x));EXPECT_EQ(SpecialCaseTests[i].status, (int)status);EXPECT_EQ(SpecialCaseTests[i].category, (int)x.getCategory());}}TEST(APFloatTest, subtract) {// Test Special Cases against each other and normal values.APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat SNaN = APFloat(APFloat::IEEEsingle(), "snan123");APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0");APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0");APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false);APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true);APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false);APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true);APFloat PSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);APFloat MSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact;struct {APFloat x;APFloat y;const char *result;int status;int category;} SpecialCaseTests[] = {{ PInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PZero, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MZero, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PZero, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MZero, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PZero, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PZero, PLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PZero, MLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PZero, PSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PZero, MSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PZero, PSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PZero, MSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MZero, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MZero, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MZero, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MZero, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MZero, PLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MZero, MLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MZero, PSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MZero, MSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MZero, PSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MZero, MSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ SNaN, PInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, QNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PNormalValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PNormalValue, PZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MZero, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, MNormalValue, "0x1p+1", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, PSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, MSmallestValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, PSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PNormalValue, MSmallestNormalized, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MNormalValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MNormalValue, PZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MZero, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MNormalValue, PNormalValue, "-0x1p+1", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, PSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, MSmallestValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, PSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MNormalValue, MSmallestNormalized, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PLargestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PLargestValue, PZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MZero, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, MLargestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ PLargestValue, PSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, MSmallestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, PSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PLargestValue, MSmallestNormalized, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MLargestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MLargestValue, PZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MZero, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, PLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, PSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, MSmallestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, PSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MLargestValue, MSmallestNormalized, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestValue, PZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MZero, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestValue, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, MSmallestValue, "0x1p-148", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PSmallestNormalized, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MSmallestNormalized, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestValue, PZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MZero, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestValue, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestValue, PSmallestValue, "-0x1p-148", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, PSmallestNormalized, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MSmallestNormalized, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestNormalized, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestNormalized, PZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MZero, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestNormalized, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ PSmallestNormalized, PSmallestValue, "0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MSmallestValue, "0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, MSmallestNormalized, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestNormalized, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestNormalized, PZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MZero, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestNormalized, PNormalValue, "-0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, MNormalValue, "0x1p+0", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, PLargestValue, "-0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, MLargestValue, "0x1.fffffep+127", APFloat::opInexact, APFloat::fcNormal },{ MSmallestNormalized, PSmallestValue, "-0x1.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MSmallestValue, "-0x1.fffffcp-127", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PSmallestNormalized, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero }};for (size_t i = 0; i < array_lengthof(SpecialCaseTests); ++i) {APFloat x(SpecialCaseTests[i].x);APFloat y(SpecialCaseTests[i].y);APFloat::opStatus status = x.subtract(y, APFloat::rmNearestTiesToEven);APFloat result(APFloat::IEEEsingle(), SpecialCaseTests[i].result);EXPECT_TRUE(result.bitwiseIsEqual(x));EXPECT_EQ(SpecialCaseTests[i].status, (int)status);EXPECT_EQ(SpecialCaseTests[i].category, (int)x.getCategory());}}TEST(APFloatTest, multiply) {// Test Special Cases against each other and normal values.APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat SNaN = APFloat(APFloat::IEEEsingle(), "snan123");APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0");APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0");APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false);APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true);APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false);APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true);APFloat PSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);APFloat MSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);APFloat MaxQuad(APFloat::IEEEquad(),"0x1.ffffffffffffffffffffffffffffp+16383");APFloat MinQuad(APFloat::IEEEquad(),"0x0.0000000000000000000000000001p-16382");APFloat NMinQuad(APFloat::IEEEquad(),"-0x0.0000000000000000000000000001p-16382");const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact;const int UnderflowStatus = APFloat::opUnderflow | APFloat::opInexact;struct {APFloat x;APFloat y;const char *result;int status;int category;APFloat::roundingMode roundingMode = APFloat::rmNearestTiesToEven;} SpecialCaseTests[] = {{ PInf, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ SNaN, PInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, QNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PNormalValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PNormalValue, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, PLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, PSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, PSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MNormalValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MNormalValue, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MNormalValue, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PLargestValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MLargestValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PSmallestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MSmallestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PSmallestNormalized, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MSmallestNormalized, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PLargestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PLargestValue, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, PLargestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ PLargestValue, MLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ PLargestValue, PSmallestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MSmallestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, PSmallestNormalized, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MSmallestNormalized, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MLargestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MLargestValue, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, PLargestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, MLargestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, PSmallestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MSmallestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, PSmallestNormalized, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MSmallestNormalized, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestValue, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestValue, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestValue, PNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PLargestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MLargestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestValue, MSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestValue, PSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestValue, MSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestValue, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestValue, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestValue, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestValue, PNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PLargestValue, "-0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MLargestValue, "0x1.fffffep-22", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestValue, MSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestValue, PSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestValue, MSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestNormalized, PInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestNormalized, MInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PSmallestNormalized, PZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, MZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestNormalized, PNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PLargestValue, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MLargestValue, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestNormalized, MSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestNormalized, PSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestNormalized, MSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestNormalized, PInf, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestNormalized, MInf, "inf", APFloat::opOK, APFloat::fcInfinity },{ MSmallestNormalized, PZero, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, MZero, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestNormalized, PNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PLargestValue, "-0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MLargestValue, "0x1.fffffep+1", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PSmallestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestNormalized, MSmallestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestNormalized, PSmallestNormalized, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestNormalized, MSmallestNormalized, "0x0p+0", UnderflowStatus, APFloat::fcZero },{MaxQuad, MinQuad, "0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmNearestTiesToEven},{MaxQuad, MinQuad, "0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmTowardPositive},{MaxQuad, MinQuad, "0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmTowardNegative},{MaxQuad, MinQuad, "0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmTowardZero},{MaxQuad, MinQuad, "0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmNearestTiesToAway},{MaxQuad, NMinQuad, "-0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmNearestTiesToEven},{MaxQuad, NMinQuad, "-0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmTowardPositive},{MaxQuad, NMinQuad, "-0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmTowardNegative},{MaxQuad, NMinQuad, "-0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmTowardZero},{MaxQuad, NMinQuad, "-0x1.ffffffffffffffffffffffffffffp-111", APFloat::opOK,APFloat::fcNormal, APFloat::rmNearestTiesToAway},{MaxQuad, MaxQuad, "inf", OverflowStatus, APFloat::fcInfinity,APFloat::rmNearestTiesToEven},{MaxQuad, MaxQuad, "inf", OverflowStatus, APFloat::fcInfinity,APFloat::rmTowardPositive},{MaxQuad, MaxQuad, "0x1.ffffffffffffffffffffffffffffp+16383",APFloat::opInexact, APFloat::fcNormal, APFloat::rmTowardNegative},{MaxQuad, MaxQuad, "0x1.ffffffffffffffffffffffffffffp+16383",APFloat::opInexact, APFloat::fcNormal, APFloat::rmTowardZero},{MaxQuad, MaxQuad, "inf", OverflowStatus, APFloat::fcInfinity,APFloat::rmNearestTiesToAway},{MinQuad, MinQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToEven},{MinQuad, MinQuad, "0x0.0000000000000000000000000001p-16382",UnderflowStatus, APFloat::fcNormal, APFloat::rmTowardPositive},{MinQuad, MinQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardNegative},{MinQuad, MinQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardZero},{MinQuad, MinQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToAway},{MinQuad, NMinQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToEven},{MinQuad, NMinQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardPositive},{MinQuad, NMinQuad, "-0x0.0000000000000000000000000001p-16382",UnderflowStatus, APFloat::fcNormal, APFloat::rmTowardNegative},{MinQuad, NMinQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardZero},{MinQuad, NMinQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToAway},};for (size_t i = 0; i < array_lengthof(SpecialCaseTests); ++i) {APFloat x(SpecialCaseTests[i].x);APFloat y(SpecialCaseTests[i].y);APFloat::opStatus status = x.multiply(y, SpecialCaseTests[i].roundingMode);APFloat result(x.getSemantics(), SpecialCaseTests[i].result);EXPECT_TRUE(result.bitwiseIsEqual(x));EXPECT_EQ(SpecialCaseTests[i].status, (int)status);EXPECT_EQ(SpecialCaseTests[i].category, (int)x.getCategory());}}TEST(APFloatTest, divide) {// Test Special Cases against each other and normal values.APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat SNaN = APFloat(APFloat::IEEEsingle(), "snan123");APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0");APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0");APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false);APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true);APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false);APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true);APFloat PSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);APFloat MSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);APFloat MaxQuad(APFloat::IEEEquad(),"0x1.ffffffffffffffffffffffffffffp+16383");APFloat MinQuad(APFloat::IEEEquad(),"0x0.0000000000000000000000000001p-16382");APFloat NMinQuad(APFloat::IEEEquad(),"-0x0.0000000000000000000000000001p-16382");const int OverflowStatus = APFloat::opOverflow | APFloat::opInexact;const int UnderflowStatus = APFloat::opUnderflow | APFloat::opInexact;struct {APFloat x;APFloat y;const char *result;int status;int category;APFloat::roundingMode roundingMode = APFloat::rmNearestTiesToEven;} SpecialCaseTests[] = {{ PInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PZero, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MZero, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, PSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ PInf, MSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PZero, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MZero, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PNormalValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MNormalValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PLargestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MLargestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestValue, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestValue, "inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, PSmallestNormalized, "-inf", APFloat::opOK, APFloat::fcInfinity },{ MInf, MSmallestNormalized, "inf", APFloat::opOK, APFloat::fcInfinity },{ PZero, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ SNaN, PInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, QNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PNormalValue, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, PLargestValue, "0x1p-128", UnderflowStatus, APFloat::fcNormal },{ PNormalValue, MLargestValue, "-0x1p-128", UnderflowStatus, APFloat::fcNormal },{ PNormalValue, PSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ PNormalValue, MSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ PNormalValue, PSmallestNormalized, "0x1p+126", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MSmallestNormalized, "-0x1p+126", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MNormalValue, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MNormalValue, PNormalValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MNormalValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PLargestValue, "-0x1p-128", UnderflowStatus, APFloat::fcNormal },{ MNormalValue, MLargestValue, "0x1p-128", UnderflowStatus, APFloat::fcNormal },{ MNormalValue, PSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ MNormalValue, MSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ MNormalValue, PSmallestNormalized, "-0x1p+126", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MSmallestNormalized, "0x1p+126", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PLargestValue, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PLargestValue, PNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, PLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, PSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ PLargestValue, MSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ PLargestValue, PSmallestNormalized, "inf", OverflowStatus, APFloat::fcInfinity },{ PLargestValue, MSmallestNormalized, "-inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MLargestValue, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MLargestValue, PNormalValue, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MNormalValue, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, PLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, PSmallestValue, "-inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, MSmallestValue, "inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, PSmallestNormalized, "-inf", OverflowStatus, APFloat::fcInfinity },{ MLargestValue, MSmallestNormalized, "inf", OverflowStatus, APFloat::fcInfinity },{ PSmallestValue, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PSmallestValue, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestValue, PNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestValue, MLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestValue, PSmallestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MSmallestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PSmallestNormalized, "0x1p-23", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MSmallestNormalized, "-0x1p-23", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MSmallestValue, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestValue, PNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestValue, MLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestValue, PSmallestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MSmallestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PSmallestNormalized, "-0x1p-23", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MSmallestNormalized, "0x1p-23", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, PZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PSmallestNormalized, MZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestNormalized, PNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestNormalized, MLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ PSmallestNormalized, PSmallestValue, "0x1p+23", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MSmallestValue, "-0x1p+23", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PSmallestNormalized, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MSmallestNormalized, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, PZero, "-inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MSmallestNormalized, MZero, "inf", APFloat::opDivByZero, APFloat::fcInfinity },{ MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestNormalized, PNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PLargestValue, "-0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestNormalized, MLargestValue, "0x0p+0", UnderflowStatus, APFloat::fcZero },{ MSmallestNormalized, PSmallestValue, "-0x1p+23", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MSmallestValue, "0x1p+23", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PSmallestNormalized, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MSmallestNormalized, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{MaxQuad, NMinQuad, "-inf", OverflowStatus, APFloat::fcInfinity,APFloat::rmNearestTiesToEven},{MaxQuad, NMinQuad, "-0x1.ffffffffffffffffffffffffffffp+16383",APFloat::opInexact, APFloat::fcNormal, APFloat::rmTowardPositive},{MaxQuad, NMinQuad, "-inf", OverflowStatus, APFloat::fcInfinity,APFloat::rmTowardNegative},{MaxQuad, NMinQuad, "-0x1.ffffffffffffffffffffffffffffp+16383",APFloat::opInexact, APFloat::fcNormal, APFloat::rmTowardZero},{MaxQuad, NMinQuad, "-inf", OverflowStatus, APFloat::fcInfinity,APFloat::rmNearestTiesToAway},{MinQuad, MaxQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToEven},{MinQuad, MaxQuad, "0x0.0000000000000000000000000001p-16382",UnderflowStatus, APFloat::fcNormal, APFloat::rmTowardPositive},{MinQuad, MaxQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardNegative},{MinQuad, MaxQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardZero},{MinQuad, MaxQuad, "0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToAway},{NMinQuad, MaxQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToEven},{NMinQuad, MaxQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardPositive},{NMinQuad, MaxQuad, "-0x0.0000000000000000000000000001p-16382",UnderflowStatus, APFloat::fcNormal, APFloat::rmTowardNegative},{NMinQuad, MaxQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmTowardZero},{NMinQuad, MaxQuad, "-0", UnderflowStatus, APFloat::fcZero,APFloat::rmNearestTiesToAway},};for (size_t i = 0; i < array_lengthof(SpecialCaseTests); ++i) {APFloat x(SpecialCaseTests[i].x);APFloat y(SpecialCaseTests[i].y);APFloat::opStatus status = x.divide(y, SpecialCaseTests[i].roundingMode);APFloat result(x.getSemantics(), SpecialCaseTests[i].result);EXPECT_TRUE(result.bitwiseIsEqual(x));EXPECT_EQ(SpecialCaseTests[i].status, (int)status);EXPECT_EQ(SpecialCaseTests[i].category, (int)x.getCategory());}}TEST(APFloatTest, operatorOverloads) {// This is mostly testing that these operator overloads compile.APFloat One = APFloat(APFloat::IEEEsingle(), "0x1p+0");APFloat Two = APFloat(APFloat::IEEEsingle(), "0x2p+0");EXPECT_TRUE(Two.bitwiseIsEqual(One + One));EXPECT_TRUE(One.bitwiseIsEqual(Two - One));EXPECT_TRUE(Two.bitwiseIsEqual(One * Two));EXPECT_TRUE(One.bitwiseIsEqual(Two / Two));}TEST(APFloatTest, Comparisons) {enum {MNan, MInf, MBig, MOne, MZer, PZer, POne, PBig, PInf, PNan, NumVals};APFloat Vals[NumVals] = {APFloat::getNaN(APFloat::IEEEsingle(), true),APFloat::getInf(APFloat::IEEEsingle(), true),APFloat::getLargest(APFloat::IEEEsingle(), true),APFloat(APFloat::IEEEsingle(), "-0x1p+0"),APFloat::getZero(APFloat::IEEEsingle(), true),APFloat::getZero(APFloat::IEEEsingle(), false),APFloat(APFloat::IEEEsingle(), "0x1p+0"),APFloat::getLargest(APFloat::IEEEsingle(), false),APFloat::getInf(APFloat::IEEEsingle(), false),APFloat::getNaN(APFloat::IEEEsingle(), false),};using Relation = void (*)(const APFloat &, const APFloat &);Relation LT = [](const APFloat &LHS, const APFloat &RHS) {EXPECT_FALSE(LHS == RHS);EXPECT_TRUE(LHS != RHS);EXPECT_TRUE(LHS < RHS);EXPECT_FALSE(LHS > RHS);EXPECT_TRUE(LHS <= RHS);EXPECT_FALSE(LHS >= RHS);};Relation EQ = [](const APFloat &LHS, const APFloat &RHS) {EXPECT_TRUE(LHS == RHS);EXPECT_FALSE(LHS != RHS);EXPECT_FALSE(LHS < RHS);EXPECT_FALSE(LHS > RHS);EXPECT_TRUE(LHS <= RHS);EXPECT_TRUE(LHS >= RHS);};Relation GT = [](const APFloat &LHS, const APFloat &RHS) {EXPECT_FALSE(LHS == RHS);EXPECT_TRUE(LHS != RHS);EXPECT_FALSE(LHS < RHS);EXPECT_TRUE(LHS > RHS);EXPECT_FALSE(LHS <= RHS);EXPECT_TRUE(LHS >= RHS);};Relation UN = [](const APFloat &LHS, const APFloat &RHS) {EXPECT_FALSE(LHS == RHS);EXPECT_TRUE(LHS != RHS);EXPECT_FALSE(LHS < RHS);EXPECT_FALSE(LHS > RHS);EXPECT_FALSE(LHS <= RHS);EXPECT_FALSE(LHS >= RHS);};Relation Relations[NumVals][NumVals] = {// -N -I -B -1 -0 +0 +1 +B +I +N/* MNan */ {UN, UN, UN, UN, UN, UN, UN, UN, UN, UN},/* MInf */ {UN, EQ, LT, LT, LT, LT, LT, LT, LT, UN},/* MBig */ {UN, GT, EQ, LT, LT, LT, LT, LT, LT, UN},/* MOne */ {UN, GT, GT, EQ, LT, LT, LT, LT, LT, UN},/* MZer */ {UN, GT, GT, GT, EQ, EQ, LT, LT, LT, UN},/* PZer */ {UN, GT, GT, GT, EQ, EQ, LT, LT, LT, UN},/* POne */ {UN, GT, GT, GT, GT, GT, EQ, LT, LT, UN},/* PBig */ {UN, GT, GT, GT, GT, GT, GT, EQ, LT, UN},/* PInf */ {UN, GT, GT, GT, GT, GT, GT, GT, EQ, UN},/* PNan */ {UN, UN, UN, UN, UN, UN, UN, UN, UN, UN},};for (unsigned I = 0; I < NumVals; ++I)for (unsigned J = 0; J < NumVals; ++J)Relations[I][J](Vals[I], Vals[J]);}TEST(APFloatTest, abs) {APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat PQNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat MQNaN = APFloat::getNaN(APFloat::IEEEsingle(), true);APFloat PSNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false);APFloat MSNaN = APFloat::getSNaN(APFloat::IEEEsingle(), true);APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0");APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0");APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false);APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true);APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false);APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true);APFloat PSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);APFloat MSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);EXPECT_TRUE(PInf.bitwiseIsEqual(abs(PInf)));EXPECT_TRUE(PInf.bitwiseIsEqual(abs(MInf)));EXPECT_TRUE(PZero.bitwiseIsEqual(abs(PZero)));EXPECT_TRUE(PZero.bitwiseIsEqual(abs(MZero)));EXPECT_TRUE(PQNaN.bitwiseIsEqual(abs(PQNaN)));EXPECT_TRUE(PQNaN.bitwiseIsEqual(abs(MQNaN)));EXPECT_TRUE(PSNaN.bitwiseIsEqual(abs(PSNaN)));EXPECT_TRUE(PSNaN.bitwiseIsEqual(abs(MSNaN)));EXPECT_TRUE(PNormalValue.bitwiseIsEqual(abs(PNormalValue)));EXPECT_TRUE(PNormalValue.bitwiseIsEqual(abs(MNormalValue)));EXPECT_TRUE(PLargestValue.bitwiseIsEqual(abs(PLargestValue)));EXPECT_TRUE(PLargestValue.bitwiseIsEqual(abs(MLargestValue)));EXPECT_TRUE(PSmallestValue.bitwiseIsEqual(abs(PSmallestValue)));EXPECT_TRUE(PSmallestValue.bitwiseIsEqual(abs(MSmallestValue)));EXPECT_TRUE(PSmallestNormalized.bitwiseIsEqual(abs(PSmallestNormalized)));EXPECT_TRUE(PSmallestNormalized.bitwiseIsEqual(abs(MSmallestNormalized)));}TEST(APFloatTest, neg) {APFloat One = APFloat(APFloat::IEEEsingle(), "1.0");APFloat NegOne = APFloat(APFloat::IEEEsingle(), "-1.0");APFloat Zero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat NegZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat Inf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat NegInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat NegQNaN = APFloat::getNaN(APFloat::IEEEsingle(), true);EXPECT_TRUE(NegOne.bitwiseIsEqual(neg(One)));EXPECT_TRUE(One.bitwiseIsEqual(neg(NegOne)));EXPECT_TRUE(NegZero.bitwiseIsEqual(neg(Zero)));EXPECT_TRUE(Zero.bitwiseIsEqual(neg(NegZero)));EXPECT_TRUE(NegInf.bitwiseIsEqual(neg(Inf)));EXPECT_TRUE(Inf.bitwiseIsEqual(neg(NegInf)));EXPECT_TRUE(NegInf.bitwiseIsEqual(neg(Inf)));EXPECT_TRUE(Inf.bitwiseIsEqual(neg(NegInf)));EXPECT_TRUE(NegQNaN.bitwiseIsEqual(neg(QNaN)));EXPECT_TRUE(QNaN.bitwiseIsEqual(neg(NegQNaN)));EXPECT_TRUE(NegOne.bitwiseIsEqual(-One));EXPECT_TRUE(One.bitwiseIsEqual(-NegOne));EXPECT_TRUE(NegZero.bitwiseIsEqual(-Zero));EXPECT_TRUE(Zero.bitwiseIsEqual(-NegZero));EXPECT_TRUE(NegInf.bitwiseIsEqual(-Inf));EXPECT_TRUE(Inf.bitwiseIsEqual(-NegInf));EXPECT_TRUE(NegInf.bitwiseIsEqual(-Inf));EXPECT_TRUE(Inf.bitwiseIsEqual(-NegInf));EXPECT_TRUE(NegQNaN.bitwiseIsEqual(-QNaN));EXPECT_TRUE(QNaN.bitwiseIsEqual(-NegQNaN));}TEST(APFloatTest, ilogb) {EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), false)));EXPECT_EQ(-1074, ilogb(APFloat::getSmallest(APFloat::IEEEdouble(), true)));EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1024")));EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1023")));EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1023")));EXPECT_EQ(-51, ilogb(APFloat(APFloat::IEEEdouble(), "0x1p-51")));EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1023")));EXPECT_EQ(-2, ilogb(APFloat(APFloat::IEEEdouble(), "0x0.ffffp-1")));EXPECT_EQ(-1023, ilogb(APFloat(APFloat::IEEEdouble(), "0x1.fffep-1023")));EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble(), false)));EXPECT_EQ(1023, ilogb(APFloat::getLargest(APFloat::IEEEdouble(), true)));EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+0")));EXPECT_EQ(0, ilogb(APFloat(APFloat::IEEEsingle(), "-0x1p+0")));EXPECT_EQ(42, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p+42")));EXPECT_EQ(-42, ilogb(APFloat(APFloat::IEEEsingle(), "0x1p-42")));EXPECT_EQ(APFloat::IEK_Inf,ilogb(APFloat::getInf(APFloat::IEEEsingle(), false)));EXPECT_EQ(APFloat::IEK_Inf,ilogb(APFloat::getInf(APFloat::IEEEsingle(), true)));EXPECT_EQ(APFloat::IEK_Zero,ilogb(APFloat::getZero(APFloat::IEEEsingle(), false)));EXPECT_EQ(APFloat::IEK_Zero,ilogb(APFloat::getZero(APFloat::IEEEsingle(), true)));EXPECT_EQ(APFloat::IEK_NaN,ilogb(APFloat::getNaN(APFloat::IEEEsingle(), false)));EXPECT_EQ(APFloat::IEK_NaN,ilogb(APFloat::getSNaN(APFloat::IEEEsingle(), false)));EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle(), false)));EXPECT_EQ(127, ilogb(APFloat::getLargest(APFloat::IEEEsingle(), true)));EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle(), false)));EXPECT_EQ(-149, ilogb(APFloat::getSmallest(APFloat::IEEEsingle(), true)));EXPECT_EQ(-126,ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false)));EXPECT_EQ(-126,ilogb(APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true)));}TEST(APFloatTest, scalbn) {const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p+0").bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+0"), 0, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p+42").bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+0"), 42, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "0x1p-42").bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+0"), -42, RM)));APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat QPNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat QMNaN = APFloat::getNaN(APFloat::IEEEsingle(), true);APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle(), false);EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0, RM)));EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0, RM)));EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(PZero, 0, RM)));EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(MZero, 0, RM)));EXPECT_TRUE(QPNaN.bitwiseIsEqual(scalbn(QPNaN, 0, RM)));EXPECT_TRUE(QMNaN.bitwiseIsEqual(scalbn(QMNaN, 0, RM)));EXPECT_FALSE(scalbn(SNaN, 0, RM).isSignaling());APFloat ScalbnSNaN = scalbn(SNaN, 1, RM);EXPECT_TRUE(ScalbnSNaN.isNaN() && !ScalbnSNaN.isSignaling());// Make sure highest bit of payload is preserved.const APInt Payload(64, (UINT64_C(1) << 50) |(UINT64_C(1) << 49) |(UINT64_C(1234) << 32) |1);APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble(), false,&Payload);APFloat QuietPayload = scalbn(SNaNWithPayload, 1, RM);EXPECT_TRUE(QuietPayload.isNaN() && !QuietPayload.isSignaling());EXPECT_EQ(Payload, QuietPayload.bitcastToAPInt().getLoBits(51));EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+0"), 128, RM)));EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "-0x1p+0"), 128, RM)));EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p+127"), 1, RM)));EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p-127"), -127, RM)));EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "-0x1p-127"), -127, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEsingle(), "-0x1p-149").bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "-0x1p-127"), -22, RM)));EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle(), "0x1p-126"), -24, RM)));APFloat SmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble(), false);APFloat NegSmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble(), true);APFloat LargestF64 = APFloat::getLargest(APFloat::IEEEdouble(), false);APFloat NegLargestF64 = APFloat::getLargest(APFloat::IEEEdouble(), true);APFloat SmallestNormalizedF64= APFloat::getSmallestNormalized(APFloat::IEEEdouble(), false);APFloat NegSmallestNormalizedF64= APFloat::getSmallestNormalized(APFloat::IEEEdouble(), true);APFloat LargestDenormalF64(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1023");APFloat NegLargestDenormalF64(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1023");EXPECT_TRUE(SmallestF64.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble(), "0x1p-1074"), 0, RM)));EXPECT_TRUE(NegSmallestF64.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble(), "-0x1p-1074"), 0, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p+1023").bitwiseIsEqual(scalbn(SmallestF64, 2097, RM)));EXPECT_TRUE(scalbn(SmallestF64, -2097, RM).isPosZero());EXPECT_TRUE(scalbn(SmallestF64, -2098, RM).isPosZero());EXPECT_TRUE(scalbn(SmallestF64, -2099, RM).isPosZero());EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p+1022").bitwiseIsEqual(scalbn(SmallestF64, 2096, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p+1023").bitwiseIsEqual(scalbn(SmallestF64, 2097, RM)));EXPECT_TRUE(scalbn(SmallestF64, 2098, RM).isInfinity());EXPECT_TRUE(scalbn(SmallestF64, 2099, RM).isInfinity());// Test for integer overflows when adding to exponent.EXPECT_TRUE(scalbn(SmallestF64, -INT_MAX, RM).isPosZero());EXPECT_TRUE(scalbn(LargestF64, INT_MAX, RM).isInfinity());EXPECT_TRUE(LargestDenormalF64.bitwiseIsEqual(scalbn(LargestDenormalF64, 0, RM)));EXPECT_TRUE(NegLargestDenormalF64.bitwiseIsEqual(scalbn(NegLargestDenormalF64, 0, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1022").bitwiseIsEqual(scalbn(LargestDenormalF64, 1, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1021").bitwiseIsEqual(scalbn(NegLargestDenormalF64, 2, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep+1").bitwiseIsEqual(scalbn(LargestDenormalF64, 1024, RM)));EXPECT_TRUE(scalbn(LargestDenormalF64, -1023, RM).isPosZero());EXPECT_TRUE(scalbn(LargestDenormalF64, -1024, RM).isPosZero());EXPECT_TRUE(scalbn(LargestDenormalF64, -2048, RM).isPosZero());EXPECT_TRUE(scalbn(LargestDenormalF64, 2047, RM).isInfinity());EXPECT_TRUE(scalbn(LargestDenormalF64, 2098, RM).isInfinity());EXPECT_TRUE(scalbn(LargestDenormalF64, 2099, RM).isInfinity());EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-2").bitwiseIsEqual(scalbn(LargestDenormalF64, 1021, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1").bitwiseIsEqual(scalbn(LargestDenormalF64, 1022, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep+0").bitwiseIsEqual(scalbn(LargestDenormalF64, 1023, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep+1023").bitwiseIsEqual(scalbn(LargestDenormalF64, 2046, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p+974").bitwiseIsEqual(scalbn(SmallestF64, 2048, RM)));APFloat RandomDenormalF64(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp+51");EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-972").bitwiseIsEqual(scalbn(RandomDenormalF64, -1023, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1").bitwiseIsEqual(scalbn(RandomDenormalF64, -52, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-2").bitwiseIsEqual(scalbn(RandomDenormalF64, -53, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp+0").bitwiseIsEqual(scalbn(RandomDenormalF64, -51, RM)));EXPECT_TRUE(scalbn(RandomDenormalF64, -2097, RM).isPosZero());EXPECT_TRUE(scalbn(RandomDenormalF64, -2090, RM).isPosZero());EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1p-1073").bitwiseIsEqual(scalbn(NegLargestF64, -2097, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1p-1024").bitwiseIsEqual(scalbn(NegLargestF64, -2048, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p-1073").bitwiseIsEqual(scalbn(LargestF64, -2097, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p-1074").bitwiseIsEqual(scalbn(LargestF64, -2098, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1p-1074").bitwiseIsEqual(scalbn(NegLargestF64, -2098, RM)));EXPECT_TRUE(scalbn(NegLargestF64, -2099, RM).isNegZero());EXPECT_TRUE(scalbn(LargestF64, 1, RM).isInfinity());EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p+0").bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble(), "0x1p+52"), -52, RM)));EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p-103").bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble(), "0x1p-51"), -52, RM)));}TEST(APFloatTest, frexp) {const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;APFloat PZero = APFloat::getZero(APFloat::IEEEdouble(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEdouble(), true);APFloat One(1.0);APFloat MOne(-1.0);APFloat Two(2.0);APFloat MTwo(-2.0);APFloat LargestDenormal(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1023");APFloat NegLargestDenormal(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1023");APFloat Smallest = APFloat::getSmallest(APFloat::IEEEdouble(), false);APFloat NegSmallest = APFloat::getSmallest(APFloat::IEEEdouble(), true);APFloat Largest = APFloat::getLargest(APFloat::IEEEdouble(), false);APFloat NegLargest = APFloat::getLargest(APFloat::IEEEdouble(), true);APFloat PInf = APFloat::getInf(APFloat::IEEEdouble(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEdouble(), true);APFloat QPNaN = APFloat::getNaN(APFloat::IEEEdouble(), false);APFloat QMNaN = APFloat::getNaN(APFloat::IEEEdouble(), true);APFloat SNaN = APFloat::getSNaN(APFloat::IEEEdouble(), false);// Make sure highest bit of payload is preserved.const APInt Payload(64, (UINT64_C(1) << 50) |(UINT64_C(1) << 49) |(UINT64_C(1234) << 32) |1);APFloat SNaNWithPayload = APFloat::getSNaN(APFloat::IEEEdouble(), false,&Payload);APFloat SmallestNormalized= APFloat::getSmallestNormalized(APFloat::IEEEdouble(), false);APFloat NegSmallestNormalized= APFloat::getSmallestNormalized(APFloat::IEEEdouble(), true);int Exp;APFloat Frac(APFloat::IEEEdouble());Frac = frexp(PZero, Exp, RM);EXPECT_EQ(0, Exp);EXPECT_TRUE(Frac.isPosZero());Frac = frexp(MZero, Exp, RM);EXPECT_EQ(0, Exp);EXPECT_TRUE(Frac.isNegZero());Frac = frexp(One, Exp, RM);EXPECT_EQ(1, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p-1").bitwiseIsEqual(Frac));Frac = frexp(MOne, Exp, RM);EXPECT_EQ(1, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1p-1").bitwiseIsEqual(Frac));Frac = frexp(LargestDenormal, Exp, RM);EXPECT_EQ(-1022, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.ffffffffffffep-1").bitwiseIsEqual(Frac));Frac = frexp(NegLargestDenormal, Exp, RM);EXPECT_EQ(-1022, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1.ffffffffffffep-1").bitwiseIsEqual(Frac));Frac = frexp(Smallest, Exp, RM);EXPECT_EQ(-1073, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p-1").bitwiseIsEqual(Frac));Frac = frexp(NegSmallest, Exp, RM);EXPECT_EQ(-1073, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1p-1").bitwiseIsEqual(Frac));Frac = frexp(Largest, Exp, RM);EXPECT_EQ(1024, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.fffffffffffffp-1").bitwiseIsEqual(Frac));Frac = frexp(NegLargest, Exp, RM);EXPECT_EQ(1024, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "-0x1.fffffffffffffp-1").bitwiseIsEqual(Frac));Frac = frexp(PInf, Exp, RM);EXPECT_EQ(INT_MAX, Exp);EXPECT_TRUE(Frac.isInfinity() && !Frac.isNegative());Frac = frexp(MInf, Exp, RM);EXPECT_EQ(INT_MAX, Exp);EXPECT_TRUE(Frac.isInfinity() && Frac.isNegative());Frac = frexp(QPNaN, Exp, RM);EXPECT_EQ(INT_MIN, Exp);EXPECT_TRUE(Frac.isNaN());Frac = frexp(QMNaN, Exp, RM);EXPECT_EQ(INT_MIN, Exp);EXPECT_TRUE(Frac.isNaN());Frac = frexp(SNaN, Exp, RM);EXPECT_EQ(INT_MIN, Exp);EXPECT_TRUE(Frac.isNaN() && !Frac.isSignaling());Frac = frexp(SNaNWithPayload, Exp, RM);EXPECT_EQ(INT_MIN, Exp);EXPECT_TRUE(Frac.isNaN() && !Frac.isSignaling());EXPECT_EQ(Payload, Frac.bitcastToAPInt().getLoBits(51));Frac = frexp(APFloat(APFloat::IEEEdouble(), "0x0.ffffp-1"), Exp, RM);EXPECT_EQ(-1, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.fffep-1").bitwiseIsEqual(Frac));Frac = frexp(APFloat(APFloat::IEEEdouble(), "0x1p-51"), Exp, RM);EXPECT_EQ(-50, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1p-1").bitwiseIsEqual(Frac));Frac = frexp(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp+51"), Exp, RM);EXPECT_EQ(52, Exp);EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac));}TEST(APFloatTest, mod) {{APFloat f1(APFloat::IEEEdouble(), "1.5");APFloat f2(APFloat::IEEEdouble(), "1.0");APFloat expected(APFloat::IEEEdouble(), "0.5");EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "0.5");APFloat f2(APFloat::IEEEdouble(), "1.0");APFloat expected(APFloat::IEEEdouble(), "0.5");EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "0x1.3333333333333p-2"); // 0.3APFloat f2(APFloat::IEEEdouble(), "0x1.47ae147ae147bp-7"); // 0.01APFloat expected(APFloat::IEEEdouble(),"0x1.47ae147ae1471p-7"); // 0.009999999999999983EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "0x1p64"); // 1.8446744073709552e19APFloat f2(APFloat::IEEEdouble(), "1.5");APFloat expected(APFloat::IEEEdouble(), "1.0");EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "0x1p1000");APFloat f2(APFloat::IEEEdouble(), "0x1p-1000");APFloat expected(APFloat::IEEEdouble(), "0.0");EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "0.0");APFloat f2(APFloat::IEEEdouble(), "1.0");APFloat expected(APFloat::IEEEdouble(), "0.0");EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "1.0");APFloat f2(APFloat::IEEEdouble(), "0.0");EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp);EXPECT_TRUE(f1.isNaN());}{APFloat f1(APFloat::IEEEdouble(), "0.0");APFloat f2(APFloat::IEEEdouble(), "0.0");EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp);EXPECT_TRUE(f1.isNaN());}{APFloat f1 = APFloat::getInf(APFloat::IEEEdouble(), false);APFloat f2(APFloat::IEEEdouble(), "1.0");EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp);EXPECT_TRUE(f1.isNaN());}{APFloat f1(APFloat::IEEEdouble(), "-4.0");APFloat f2(APFloat::IEEEdouble(), "-2.0");APFloat expected(APFloat::IEEEdouble(), "-0.0");EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "-4.0");APFloat f2(APFloat::IEEEdouble(), "2.0");APFloat expected(APFloat::IEEEdouble(), "-0.0");EXPECT_EQ(f1.mod(f2), APFloat::opOK);EXPECT_TRUE(f1.bitwiseIsEqual(expected));}}TEST(APFloatTest, remainder) {// Test Special Cases against each other and normal values.APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);APFloat SNaN = APFloat(APFloat::IEEEsingle(), "snan123");APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0");APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0");APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false);APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true);APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false);APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true);APFloat PSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);APFloat MSmallestNormalized =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);APFloat PVal1(APFloat::IEEEsingle(), "0x1.fffffep+126");APFloat MVal1(APFloat::IEEEsingle(), "-0x1.fffffep+126");APFloat PVal2(APFloat::IEEEsingle(), "0x1.fffffep-126");APFloat MVal2(APFloat::IEEEsingle(), "-0x1.fffffep-126");APFloat PVal3(APFloat::IEEEsingle(), "0x1p-125");APFloat MVal3(APFloat::IEEEsingle(), "-0x1p-125");APFloat PVal4(APFloat::IEEEsingle(), "0x1p+127");APFloat MVal4(APFloat::IEEEsingle(), "-0x1p+127");APFloat PVal5(APFloat::IEEEsingle(), "1.5");APFloat MVal5(APFloat::IEEEsingle(), "-1.5");APFloat PVal6(APFloat::IEEEsingle(), "1");APFloat MVal6(APFloat::IEEEsingle(), "-1");struct {APFloat x;APFloat y;const char *result;int status;int category;} SpecialCaseTests[] = {{ PInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PInf, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MInf, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PZero, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PZero, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MZero, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MZero, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },{ SNaN, PInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, QNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, PSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ SNaN, MSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PInf, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MInf, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PNormalValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, PLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, MLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PNormalValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PNormalValue, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, PInf, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MInf, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MNormalValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MNormalValue, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, PLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, MLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MNormalValue, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MNormalValue, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, PInf, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, MInf, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ PLargestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PLargestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PLargestValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PLargestValue, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, PInf, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, MInf, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },{ MLargestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MLargestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MLargestValue, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MLargestValue, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, PInf, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MInf, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestValue, PNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PLargestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MLargestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestValue, PSmallestNormalized, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestValue, MSmallestNormalized, "0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PInf, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MInf, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestValue, PNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PLargestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MLargestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestValue, PSmallestNormalized, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ MSmallestValue, MSmallestNormalized, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PInf, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MInf, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestNormalized, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ PSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ PSmallestNormalized, PNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PLargestValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, MLargestValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },{ PSmallestNormalized, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PSmallestNormalized, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, PInf, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MInf, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestNormalized, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },{ MSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },{ MSmallestNormalized, PNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PLargestValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, MLargestValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },{ MSmallestNormalized, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MSmallestNormalized, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, PVal1, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, MVal1, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, PVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, MVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, PVal4, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },{ PVal1, MVal4, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },{ PVal1, PVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, MVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, PVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal1, MVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, PVal1, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, MVal1, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, PVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, MVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, PVal4, "0x1p+103", APFloat::opOK, APFloat::fcNormal },{ MVal1, MVal4, "0x1p+103", APFloat::opOK, APFloat::fcNormal },{ MVal1, PVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, MVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, PVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal1, MVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal2, PVal1, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, MVal1, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, PVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal2, MVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal2, PVal3, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, MVal3, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, PVal4, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, MVal4, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, PVal5, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, MVal5, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, PVal6, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal2, MVal6, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, PVal1, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, MVal1, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, PVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal2, MVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal2, PVal3, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, MVal3, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, PVal4, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, MVal4, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, PVal5, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, MVal5, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, PVal6, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ MVal2, MVal6, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },{ PVal3, PVal1, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal3, MVal1, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal3, PVal2, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PVal3, MVal2, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ PVal3, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal3, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal3, PVal4, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal3, MVal4, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal3, PVal5, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal3, MVal5, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal3, PVal6, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal3, MVal6, "0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, PVal1, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, MVal1, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, PVal2, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MVal3, MVal2, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },{ MVal3, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal3, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal3, PVal4, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, MVal4, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, PVal5, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, MVal5, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, PVal6, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ MVal3, MVal6, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },{ PVal4, PVal1, "0x1p+103", APFloat::opOK, APFloat::fcNormal },{ PVal4, MVal1, "0x1p+103", APFloat::opOK, APFloat::fcNormal },{ PVal4, PVal2, "0x0.002p-126", APFloat::opOK, APFloat::fcNormal },{ PVal4, MVal2, "0x0.002p-126", APFloat::opOK, APFloat::fcNormal },{ PVal4, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal4, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal4, PVal4, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal4, MVal4, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal4, PVal5, "0.5", APFloat::opOK, APFloat::fcNormal },{ PVal4, MVal5, "0.5", APFloat::opOK, APFloat::fcNormal },{ PVal4, PVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal4, MVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal4, PVal1, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },{ MVal4, MVal1, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },{ MVal4, PVal2, "-0x0.002p-126", APFloat::opOK, APFloat::fcNormal },{ MVal4, MVal2, "-0x0.002p-126", APFloat::opOK, APFloat::fcNormal },{ MVal4, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal4, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal4, PVal4, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal4, MVal4, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal4, PVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },{ MVal4, MVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },{ MVal4, PVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal4, MVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal5, PVal1, "1.5", APFloat::opOK, APFloat::fcNormal },{ PVal5, MVal1, "1.5", APFloat::opOK, APFloat::fcNormal },{ PVal5, PVal2, "0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },{ PVal5, MVal2, "0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },{ PVal5, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal5, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal5, PVal4, "1.5", APFloat::opOK, APFloat::fcNormal },{ PVal5, MVal4, "1.5", APFloat::opOK, APFloat::fcNormal },{ PVal5, PVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal5, MVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal5, PVal6, "-0.5", APFloat::opOK, APFloat::fcNormal },{ PVal5, MVal6, "-0.5", APFloat::opOK, APFloat::fcNormal },{ MVal5, PVal1, "-1.5", APFloat::opOK, APFloat::fcNormal },{ MVal5, MVal1, "-1.5", APFloat::opOK, APFloat::fcNormal },{ MVal5, PVal2, "-0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },{ MVal5, MVal2, "-0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },{ MVal5, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal5, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal5, PVal4, "-1.5", APFloat::opOK, APFloat::fcNormal },{ MVal5, MVal4, "-1.5", APFloat::opOK, APFloat::fcNormal },{ MVal5, PVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal5, MVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal5, PVal6, "0.5", APFloat::opOK, APFloat::fcNormal },{ MVal5, MVal6, "0.5", APFloat::opOK, APFloat::fcNormal },{ PVal6, PVal1, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PVal6, MVal1, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PVal6, PVal2, "0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },{ PVal6, MVal2, "0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },{ PVal6, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal6, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal6, PVal4, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PVal6, MVal4, "0x1p+0", APFloat::opOK, APFloat::fcNormal },{ PVal6, PVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },{ PVal6, MVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },{ PVal6, PVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ PVal6, MVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal6, PVal1, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MVal6, MVal1, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MVal6, PVal2, "-0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },{ MVal6, MVal2, "-0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },{ MVal6, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal6, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal6, PVal4, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MVal6, MVal4, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },{ MVal6, PVal5, "0.5", APFloat::opOK, APFloat::fcNormal },{ MVal6, MVal5, "0.5", APFloat::opOK, APFloat::fcNormal },{ MVal6, PVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },{ MVal6, MVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },};for (size_t i = 0; i < array_lengthof(SpecialCaseTests); ++i) {APFloat x(SpecialCaseTests[i].x);APFloat y(SpecialCaseTests[i].y);APFloat::opStatus status = x.remainder(y);APFloat result(x.getSemantics(), SpecialCaseTests[i].result);EXPECT_TRUE(result.bitwiseIsEqual(x));EXPECT_EQ(SpecialCaseTests[i].status, (int)status);EXPECT_EQ(SpecialCaseTests[i].category, (int)x.getCategory());}{APFloat f1(APFloat::IEEEdouble(), "0x1.3333333333333p-2"); // 0.3APFloat f2(APFloat::IEEEdouble(), "0x1.47ae147ae147bp-7"); // 0.01APFloat expected(APFloat::IEEEdouble(), "-0x1.4p-56");EXPECT_EQ(APFloat::opOK, f1.remainder(f2));EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "0x1p64"); // 1.8446744073709552e19APFloat f2(APFloat::IEEEdouble(), "1.5");APFloat expected(APFloat::IEEEdouble(), "-0.5");EXPECT_EQ(APFloat::opOK, f1.remainder(f2));EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "0x1p1000");APFloat f2(APFloat::IEEEdouble(), "0x1p-1000");APFloat expected(APFloat::IEEEdouble(), "0.0");EXPECT_EQ(APFloat::opOK, f1.remainder(f2));EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1 = APFloat::getInf(APFloat::IEEEdouble(), false);APFloat f2(APFloat::IEEEdouble(), "1.0");EXPECT_EQ(f1.remainder(f2), APFloat::opInvalidOp);EXPECT_TRUE(f1.isNaN());}{APFloat f1(APFloat::IEEEdouble(), "-4.0");APFloat f2(APFloat::IEEEdouble(), "-2.0");APFloat expected(APFloat::IEEEdouble(), "-0.0");EXPECT_EQ(APFloat::opOK, f1.remainder(f2));EXPECT_TRUE(f1.bitwiseIsEqual(expected));}{APFloat f1(APFloat::IEEEdouble(), "-4.0");APFloat f2(APFloat::IEEEdouble(), "2.0");APFloat expected(APFloat::IEEEdouble(), "-0.0");EXPECT_EQ(APFloat::opOK, f1.remainder(f2));EXPECT_TRUE(f1.bitwiseIsEqual(expected));}}TEST(APFloatTest, PPCDoubleDoubleAddSpecial) {using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t,APFloat::fltCategory, APFloat::roundingMode>;DataType Data[] = {// (1 + 0) + (-1 + 0) = fcZerostd::make_tuple(0x3ff0000000000000ull, 0, 0xbff0000000000000ull, 0,APFloat::fcZero, APFloat::rmNearestTiesToEven),// LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = fcInfinitystd::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull,0x7948000000000000ull, 0ull, APFloat::fcInfinity,APFloat::rmNearestTiesToEven),// TODO: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when// semPPCDoubleDoubleLegacy is gone.// LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 -// 160))) = fcNormalstd::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull,0x7947ffffffffffffull, 0x75effffffffffffeull,APFloat::fcNormal, APFloat::rmNearestTiesToEven),// LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = fcInfinitystd::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull,0x7fefffffffffffffull, 0x7c8ffffffffffffeull,APFloat::fcInfinity, APFloat::rmNearestTiesToEven),// NaN + (1 + 0) = fcNaNstd::make_tuple(0x7ff8000000000000ull, 0, 0x3ff0000000000000ull, 0,APFloat::fcNaN, APFloat::rmNearestTiesToEven),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2];APFloat::fltCategory Expected;APFloat::roundingMode RM;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected, RM) = Tp;{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.add(A2, RM);EXPECT_EQ(Expected, A1.getCategory())<< formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1],Op2[0], Op2[1]).str();}{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A2.add(A1, RM);EXPECT_EQ(Expected, A2.getCategory())<< formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op2[0], Op2[1],Op1[0], Op1[1]).str();}}}TEST(APFloatTest, PPCDoubleDoubleAdd) {using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t,uint64_t, APFloat::roundingMode>;DataType Data[] = {// (1 + 0) + (1e-105 + 0) = (1 + 1e-105)std::make_tuple(0x3ff0000000000000ull, 0, 0x3960000000000000ull, 0,0x3ff0000000000000ull, 0x3960000000000000ull,APFloat::rmNearestTiesToEven),// (1 + 0) + (1e-106 + 0) = (1 + 1e-106)std::make_tuple(0x3ff0000000000000ull, 0, 0x3950000000000000ull, 0,0x3ff0000000000000ull, 0x3950000000000000ull,APFloat::rmNearestTiesToEven),// (1 + 1e-106) + (1e-106 + 0) = (1 + 1e-105)std::make_tuple(0x3ff0000000000000ull, 0x3950000000000000ull,0x3950000000000000ull, 0, 0x3ff0000000000000ull,0x3960000000000000ull, APFloat::rmNearestTiesToEven),// (1 + 0) + (epsilon + 0) = (1 + epsilon)std::make_tuple(0x3ff0000000000000ull, 0, 0x0000000000000001ull, 0,0x3ff0000000000000ull, 0x0000000000000001ull,APFloat::rmNearestTiesToEven),// TODO: change 0xf950000000000000 to 0xf940000000000000, when// semPPCDoubleDoubleLegacy is gone.// (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX +// 1.11111... << (1023 - 52)std::make_tuple(0x7fefffffffffffffull, 0xf950000000000000ull,0x7c90000000000000ull, 0, 0x7fefffffffffffffull,0x7c8ffffffffffffeull, APFloat::rmNearestTiesToEven),// TODO: change 0xf950000000000000 to 0xf940000000000000, when// semPPCDoubleDoubleLegacy is gone.// (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX +// 1.11111... << (1023 - 52)std::make_tuple(0x7c90000000000000ull, 0, 0x7fefffffffffffffull,0xf950000000000000ull, 0x7fefffffffffffffull,0x7c8ffffffffffffeull, APFloat::rmNearestTiesToEven),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2], Expected[2];APFloat::roundingMode RM;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp;{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.add(A2, RM);EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0])<< formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1],Op2[0], Op2[1]).str();EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1])<< formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op1[0], Op1[1],Op2[0], Op2[1]).str();}{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A2.add(A1, RM);EXPECT_EQ(Expected[0], A2.bitcastToAPInt().getRawData()[0])<< formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op2[0], Op2[1],Op1[0], Op1[1]).str();EXPECT_EQ(Expected[1], A2.bitcastToAPInt().getRawData()[1])<< formatv("({0:x} + {1:x}) + ({2:x} + {3:x})", Op2[0], Op2[1],Op1[0], Op1[1]).str();}}}TEST(APFloatTest, PPCDoubleDoubleSubtract) {using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t,uint64_t, APFloat::roundingMode>;DataType Data[] = {// (1 + 0) - (-1e-105 + 0) = (1 + 1e-105)std::make_tuple(0x3ff0000000000000ull, 0, 0xb960000000000000ull, 0,0x3ff0000000000000ull, 0x3960000000000000ull,APFloat::rmNearestTiesToEven),// (1 + 0) - (-1e-106 + 0) = (1 + 1e-106)std::make_tuple(0x3ff0000000000000ull, 0, 0xb950000000000000ull, 0,0x3ff0000000000000ull, 0x3950000000000000ull,APFloat::rmNearestTiesToEven),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2], Expected[2];APFloat::roundingMode RM;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp;APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.subtract(A2, RM);EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0])<< formatv("({0:x} + {1:x}) - ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0],Op2[1]).str();EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1])<< formatv("({0:x} + {1:x}) - ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0],Op2[1]).str();}}TEST(APFloatTest, PPCDoubleDoubleMultiplySpecial) {using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t,APFloat::fltCategory, APFloat::roundingMode>;DataType Data[] = {// fcNaN * fcNaN = fcNaNstd::make_tuple(0x7ff8000000000000ull, 0, 0x7ff8000000000000ull, 0,APFloat::fcNaN, APFloat::rmNearestTiesToEven),// fcNaN * fcZero = fcNaNstd::make_tuple(0x7ff8000000000000ull, 0, 0, 0, APFloat::fcNaN,APFloat::rmNearestTiesToEven),// fcNaN * fcInfinity = fcNaNstd::make_tuple(0x7ff8000000000000ull, 0, 0x7ff0000000000000ull, 0,APFloat::fcNaN, APFloat::rmNearestTiesToEven),// fcNaN * fcNormal = fcNaNstd::make_tuple(0x7ff8000000000000ull, 0, 0x3ff0000000000000ull, 0,APFloat::fcNaN, APFloat::rmNearestTiesToEven),// fcInfinity * fcInfinity = fcInfinitystd::make_tuple(0x7ff0000000000000ull, 0, 0x7ff0000000000000ull, 0,APFloat::fcInfinity, APFloat::rmNearestTiesToEven),// fcInfinity * fcZero = fcNaNstd::make_tuple(0x7ff0000000000000ull, 0, 0, 0, APFloat::fcNaN,APFloat::rmNearestTiesToEven),// fcInfinity * fcNormal = fcInfinitystd::make_tuple(0x7ff0000000000000ull, 0, 0x3ff0000000000000ull, 0,APFloat::fcInfinity, APFloat::rmNearestTiesToEven),// fcZero * fcZero = fcZerostd::make_tuple(0, 0, 0, 0, APFloat::fcZero,APFloat::rmNearestTiesToEven),// fcZero * fcNormal = fcZerostd::make_tuple(0, 0, 0x3ff0000000000000ull, 0, APFloat::fcZero,APFloat::rmNearestTiesToEven),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2];APFloat::fltCategory Expected;APFloat::roundingMode RM;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected, RM) = Tp;{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.multiply(A2, RM);EXPECT_EQ(Expected, A1.getCategory())<< formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1],Op2[0], Op2[1]).str();}{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A2.multiply(A1, RM);EXPECT_EQ(Expected, A2.getCategory())<< formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op2[0], Op2[1],Op1[0], Op1[1]).str();}}}TEST(APFloatTest, PPCDoubleDoubleMultiply) {using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t,uint64_t, APFloat::roundingMode>;DataType Data[] = {// 1/3 * 3 = 1.0std::make_tuple(0x3fd5555555555555ull, 0x3c75555555555556ull,0x4008000000000000ull, 0, 0x3ff0000000000000ull, 0,APFloat::rmNearestTiesToEven),// (1 + epsilon) * (1 + 0) = fcZerostd::make_tuple(0x3ff0000000000000ull, 0x0000000000000001ull,0x3ff0000000000000ull, 0, 0x3ff0000000000000ull,0x0000000000000001ull, APFloat::rmNearestTiesToEven),// (1 + epsilon) * (1 + epsilon) = 1 + 2 * epsilonstd::make_tuple(0x3ff0000000000000ull, 0x0000000000000001ull,0x3ff0000000000000ull, 0x0000000000000001ull,0x3ff0000000000000ull, 0x0000000000000002ull,APFloat::rmNearestTiesToEven),// -(1 + epsilon) * (1 + epsilon) = -1std::make_tuple(0xbff0000000000000ull, 0x0000000000000001ull,0x3ff0000000000000ull, 0x0000000000000001ull,0xbff0000000000000ull, 0, APFloat::rmNearestTiesToEven),// (0.5 + 0) * (1 + 2 * epsilon) = 0.5 + epsilonstd::make_tuple(0x3fe0000000000000ull, 0, 0x3ff0000000000000ull,0x0000000000000002ull, 0x3fe0000000000000ull,0x0000000000000001ull, APFloat::rmNearestTiesToEven),// (0.5 + 0) * (1 + epsilon) = 0.5std::make_tuple(0x3fe0000000000000ull, 0, 0x3ff0000000000000ull,0x0000000000000001ull, 0x3fe0000000000000ull, 0,APFloat::rmNearestTiesToEven),// __LDBL_MAX__ * (1 + 1 << 106) = infstd::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull,0x3ff0000000000000ull, 0x3950000000000000ull,0x7ff0000000000000ull, 0, APFloat::rmNearestTiesToEven),// __LDBL_MAX__ * (1 + 1 << 107) > __LDBL_MAX__, but not inf, yes =_=|||std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull,0x3ff0000000000000ull, 0x3940000000000000ull,0x7fefffffffffffffull, 0x7c8fffffffffffffull,APFloat::rmNearestTiesToEven),// __LDBL_MAX__ * (1 + 1 << 108) = __LDBL_MAX__std::make_tuple(0x7fefffffffffffffull, 0x7c8ffffffffffffeull,0x3ff0000000000000ull, 0x3930000000000000ull,0x7fefffffffffffffull, 0x7c8ffffffffffffeull,APFloat::rmNearestTiesToEven),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2], Expected[2];APFloat::roundingMode RM;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp;{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.multiply(A2, RM);EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0])<< formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1],Op2[0], Op2[1]).str();EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1])<< formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op1[0], Op1[1],Op2[0], Op2[1]).str();}{APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A2.multiply(A1, RM);EXPECT_EQ(Expected[0], A2.bitcastToAPInt().getRawData()[0])<< formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op2[0], Op2[1],Op1[0], Op1[1]).str();EXPECT_EQ(Expected[1], A2.bitcastToAPInt().getRawData()[1])<< formatv("({0:x} + {1:x}) * ({2:x} + {3:x})", Op2[0], Op2[1],Op1[0], Op1[1]).str();}}}TEST(APFloatTest, PPCDoubleDoubleDivide) {using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t,uint64_t, APFloat::roundingMode>;// TODO: Only a sanity check for now. Add more edge cases when the// double-double algorithm is implemented.DataType Data[] = {// 1 / 3 = 1/3std::make_tuple(0x3ff0000000000000ull, 0, 0x4008000000000000ull, 0,0x3fd5555555555555ull, 0x3c75555555555556ull,APFloat::rmNearestTiesToEven),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2], Expected[2];APFloat::roundingMode RM;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1], RM) = Tp;APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.divide(A2, RM);EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0])<< formatv("({0:x} + {1:x}) / ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0],Op2[1]).str();EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1])<< formatv("({0:x} + {1:x}) / ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0],Op2[1]).str();}}TEST(APFloatTest, PPCDoubleDoubleRemainder) {using DataType =std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t>;DataType Data[] = {// remainder(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)std::make_tuple(0x4008000000000000ull, 0x3cb8000000000000ull,0x3ff4000000000000ull, 0x3ca4000000000000ull,0x3fe0000000000000ull, 0x3c90000000000000ull),// remainder(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (-0.5 - 0.5 << 53)std::make_tuple(0x4008000000000000ull, 0x3cb8000000000000ull,0x3ffc000000000000ull, 0x3cac000000000000ull,0xbfe0000000000000ull, 0xbc90000000000000ull),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2], Expected[2];std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1]) = Tp;APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.remainder(A2);EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0])<< formatv("remainder({0:x} + {1:x}), ({2:x} + {3:x}))", Op1[0], Op1[1],Op2[0], Op2[1]).str();EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1])<< formatv("remainder(({0:x} + {1:x}), ({2:x} + {3:x}))", Op1[0],Op1[1], Op2[0], Op2[1]).str();}}TEST(APFloatTest, PPCDoubleDoubleMod) {using DataType =std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t>;DataType Data[] = {// mod(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)std::make_tuple(0x4008000000000000ull, 0x3cb8000000000000ull,0x3ff4000000000000ull, 0x3ca4000000000000ull,0x3fe0000000000000ull, 0x3c90000000000000ull),// mod(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (1.25 + 1.25 << 53)// 0xbc98000000000000 doesn't seem right, but it's what we currently have.// TODO: investigatestd::make_tuple(0x4008000000000000ull, 0x3cb8000000000000ull,0x3ffc000000000000ull, 0x3cac000000000000ull,0x3ff4000000000001ull, 0xbc98000000000000ull),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2], Expected[2];std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected[0], Expected[1]) = Tp;APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));A1.mod(A2);EXPECT_EQ(Expected[0], A1.bitcastToAPInt().getRawData()[0])<< formatv("fmod(({0:x} + {1:x}), ({2:x} + {3:x}))", Op1[0], Op1[1],Op2[0], Op2[1]).str();EXPECT_EQ(Expected[1], A1.bitcastToAPInt().getRawData()[1])<< formatv("fmod(({0:x} + {1:x}), ({2:x} + {3:x}))", Op1[0], Op1[1],Op2[0], Op2[1]).str();}}TEST(APFloatTest, PPCDoubleDoubleFMA) {// Sanity check for now.APFloat A(APFloat::PPCDoubleDouble(), "2");A.fusedMultiplyAdd(APFloat(APFloat::PPCDoubleDouble(), "3"),APFloat(APFloat::PPCDoubleDouble(), "4"),APFloat::rmNearestTiesToEven);EXPECT_EQ(APFloat::cmpEqual,APFloat(APFloat::PPCDoubleDouble(), "10").compare(A));}TEST(APFloatTest, PPCDoubleDoubleRoundToIntegral) {{APFloat A(APFloat::PPCDoubleDouble(), "1.5");A.roundToIntegral(APFloat::rmNearestTiesToEven);EXPECT_EQ(APFloat::cmpEqual,APFloat(APFloat::PPCDoubleDouble(), "2").compare(A));}{APFloat A(APFloat::PPCDoubleDouble(), "2.5");A.roundToIntegral(APFloat::rmNearestTiesToEven);EXPECT_EQ(APFloat::cmpEqual,APFloat(APFloat::PPCDoubleDouble(), "2").compare(A));}}TEST(APFloatTest, PPCDoubleDoubleCompare) {using DataType =std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, APFloat::cmpResult>;DataType Data[] = {// (1 + 0) = (1 + 0)std::make_tuple(0x3ff0000000000000ull, 0, 0x3ff0000000000000ull, 0,APFloat::cmpEqual),// (1 + 0) < (1.00...1 + 0)std::make_tuple(0x3ff0000000000000ull, 0, 0x3ff0000000000001ull, 0,APFloat::cmpLessThan),// (1.00...1 + 0) > (1 + 0)std::make_tuple(0x3ff0000000000001ull, 0, 0x3ff0000000000000ull, 0,APFloat::cmpGreaterThan),// (1 + 0) < (1 + epsilon)std::make_tuple(0x3ff0000000000000ull, 0, 0x3ff0000000000001ull,0x0000000000000001ull, APFloat::cmpLessThan),// NaN != NaNstd::make_tuple(0x7ff8000000000000ull, 0, 0x7ff8000000000000ull, 0,APFloat::cmpUnordered),// (1 + 0) != NaNstd::make_tuple(0x3ff0000000000000ull, 0, 0x7ff8000000000000ull, 0,APFloat::cmpUnordered),// Inf = Infstd::make_tuple(0x7ff0000000000000ull, 0, 0x7ff0000000000000ull, 0,APFloat::cmpEqual),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2];APFloat::cmpResult Expected;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected) = Tp;APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));EXPECT_EQ(Expected, A1.compare(A2))<< formatv("compare(({0:x} + {1:x}), ({2:x} + {3:x}))", Op1[0], Op1[1],Op2[0], Op2[1]).str();}}TEST(APFloatTest, PPCDoubleDoubleBitwiseIsEqual) {using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, bool>;DataType Data[] = {// (1 + 0) = (1 + 0)std::make_tuple(0x3ff0000000000000ull, 0, 0x3ff0000000000000ull, 0, true),// (1 + 0) != (1.00...1 + 0)std::make_tuple(0x3ff0000000000000ull, 0, 0x3ff0000000000001ull, 0,false),// NaN = NaNstd::make_tuple(0x7ff8000000000000ull, 0, 0x7ff8000000000000ull, 0, true),// NaN != NaN with a different bit patternstd::make_tuple(0x7ff8000000000000ull, 0, 0x7ff8000000000000ull,0x3ff0000000000000ull, false),// Inf = Infstd::make_tuple(0x7ff0000000000000ull, 0, 0x7ff0000000000000ull, 0, true),};for (auto Tp : Data) {uint64_t Op1[2], Op2[2];bool Expected;std::tie(Op1[0], Op1[1], Op2[0], Op2[1], Expected) = Tp;APFloat A1(APFloat::PPCDoubleDouble(), APInt(128, 2, Op1));APFloat A2(APFloat::PPCDoubleDouble(), APInt(128, 2, Op2));EXPECT_EQ(Expected, A1.bitwiseIsEqual(A2))<< formatv("({0:x} + {1:x}) = ({2:x} + {3:x})", Op1[0], Op1[1], Op2[0],Op2[1]).str();}}TEST(APFloatTest, PPCDoubleDoubleHashValue) {uint64_t Data1[] = {0x3ff0000000000001ull, 0x0000000000000001ull};uint64_t Data2[] = {0x3ff0000000000001ull, 0};// The hash values are *hopefully* different.EXPECT_NE(hash_value(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Data1))),hash_value(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Data2))));}TEST(APFloatTest, PPCDoubleDoubleChangeSign) {uint64_t Data[] = {0x400f000000000000ull, 0xbcb0000000000000ull,};APFloat Float(APFloat::PPCDoubleDouble(), APInt(128, 2, Data));{APFloat Actual =APFloat::copySign(Float, APFloat(APFloat::IEEEdouble(), "1"));EXPECT_EQ(0x400f000000000000ull, Actual.bitcastToAPInt().getRawData()[0]);EXPECT_EQ(0xbcb0000000000000ull, Actual.bitcastToAPInt().getRawData()[1]);}{APFloat Actual =APFloat::copySign(Float, APFloat(APFloat::IEEEdouble(), "-1"));EXPECT_EQ(0xc00f000000000000ull, Actual.bitcastToAPInt().getRawData()[0]);EXPECT_EQ(0x3cb0000000000000ull, Actual.bitcastToAPInt().getRawData()[1]);}}TEST(APFloatTest, PPCDoubleDoubleFactories) {{uint64_t Data[] = {0, 0,};EXPECT_EQ(APInt(128, 2, Data),APFloat::getZero(APFloat::PPCDoubleDouble()).bitcastToAPInt());}{uint64_t Data[] = {0x7fefffffffffffffull, 0x7c8ffffffffffffeull,};EXPECT_EQ(APInt(128, 2, Data),APFloat::getLargest(APFloat::PPCDoubleDouble()).bitcastToAPInt());}{uint64_t Data[] = {0x0000000000000001ull, 0,};EXPECT_EQ(APInt(128, 2, Data),APFloat::getSmallest(APFloat::PPCDoubleDouble()).bitcastToAPInt());}{uint64_t Data[] = {0x0360000000000000ull, 0};EXPECT_EQ(APInt(128, 2, Data),APFloat::getSmallestNormalized(APFloat::PPCDoubleDouble()).bitcastToAPInt());}{uint64_t Data[] = {0x8000000000000000ull, 0x0000000000000000ull,};EXPECT_EQ(APInt(128, 2, Data),APFloat::getZero(APFloat::PPCDoubleDouble(), true).bitcastToAPInt());}{uint64_t Data[] = {0xffefffffffffffffull, 0xfc8ffffffffffffeull,};EXPECT_EQ(APInt(128, 2, Data),APFloat::getLargest(APFloat::PPCDoubleDouble(), true).bitcastToAPInt());}{uint64_t Data[] = {0x8000000000000001ull, 0x0000000000000000ull,};EXPECT_EQ(APInt(128, 2, Data),APFloat::getSmallest(APFloat::PPCDoubleDouble(), true).bitcastToAPInt());}{uint64_t Data[] = {0x8360000000000000ull, 0x0000000000000000ull,};EXPECT_EQ(APInt(128, 2, Data),APFloat::getSmallestNormalized(APFloat::PPCDoubleDouble(), true).bitcastToAPInt());}EXPECT_TRUE(APFloat::getSmallest(APFloat::PPCDoubleDouble()).isSmallest());EXPECT_TRUE(APFloat::getLargest(APFloat::PPCDoubleDouble()).isLargest());}TEST(APFloatTest, PPCDoubleDoubleIsDenormal) {EXPECT_TRUE(APFloat::getSmallest(APFloat::PPCDoubleDouble()).isDenormal());EXPECT_FALSE(APFloat::getLargest(APFloat::PPCDoubleDouble()).isDenormal());EXPECT_FALSE(APFloat::getSmallestNormalized(APFloat::PPCDoubleDouble()).isDenormal());{// (4 + 3) is not normalizeduint64_t Data[] = {0x4010000000000000ull, 0x4008000000000000ull,};EXPECT_TRUE(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Data)).isDenormal());}}TEST(APFloatTest, PPCDoubleDoubleScalbn) {// 3.0 + 3.0 << 53uint64_t Input[] = {0x4008000000000000ull, 0x3cb8000000000000ull,};APFloat Result =scalbn(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Input)), 1,APFloat::rmNearestTiesToEven);// 6.0 + 6.0 << 53EXPECT_EQ(0x4018000000000000ull, Result.bitcastToAPInt().getRawData()[0]);EXPECT_EQ(0x3cc8000000000000ull, Result.bitcastToAPInt().getRawData()[1]);}TEST(APFloatTest, PPCDoubleDoubleFrexp) {// 3.0 + 3.0 << 53uint64_t Input[] = {0x4008000000000000ull, 0x3cb8000000000000ull,};int Exp;// 0.75 + 0.75 << 53APFloat Result =frexp(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Input)), Exp,APFloat::rmNearestTiesToEven);EXPECT_EQ(2, Exp);EXPECT_EQ(0x3fe8000000000000ull, Result.bitcastToAPInt().getRawData()[0]);EXPECT_EQ(0x3c98000000000000ull, Result.bitcastToAPInt().getRawData()[1]);}TEST(APFloatTest, x87Largest) {APFloat MaxX87Val = APFloat::getLargest(APFloat::x87DoubleExtended());EXPECT_TRUE(MaxX87Val.isLargest());}TEST(APFloatTest, x87Next) {APFloat F(APFloat::x87DoubleExtended(), "-1.0");F.next(false);EXPECT_TRUE(ilogb(F) == -1);}TEST(APFloatTest, ToDouble) {APFloat DPosZero(0.0);APFloat DPosZeroToDouble(DPosZero.convertToDouble());EXPECT_TRUE(DPosZeroToDouble.isPosZero());APFloat DNegZero(-0.0);APFloat DNegZeroToDouble(DNegZero.convertToDouble());EXPECT_TRUE(DNegZeroToDouble.isNegZero());APFloat DOne(1.0);EXPECT_EQ(1.0, DOne.convertToDouble());APFloat DPosLargest = APFloat::getLargest(APFloat::IEEEdouble(), false);EXPECT_EQ(std::numeric_limits<double>::max(), DPosLargest.convertToDouble());APFloat DNegLargest = APFloat::getLargest(APFloat::IEEEdouble(), true);EXPECT_EQ(-std::numeric_limits<double>::max(), DNegLargest.convertToDouble());APFloat DPosSmallest =APFloat::getSmallestNormalized(APFloat::IEEEdouble(), false);EXPECT_EQ(std::numeric_limits<double>::min(), DPosSmallest.convertToDouble());APFloat DNegSmallest =APFloat::getSmallestNormalized(APFloat::IEEEdouble(), true);EXPECT_EQ(-std::numeric_limits<double>::min(),DNegSmallest.convertToDouble());APFloat DSmallestDenorm = APFloat::getSmallest(APFloat::IEEEdouble(), false);EXPECT_EQ(std::numeric_limits<double>::denorm_min(),DSmallestDenorm.convertToDouble());APFloat DLargestDenorm(APFloat::IEEEdouble(), "0x0.FFFFFFFFFFFFFp-1022");EXPECT_EQ(/*0x0.FFFFFFFFFFFFFp-1022*/ 2.225073858507201e-308,DLargestDenorm.convertToDouble());APFloat DPosInf = APFloat::getInf(APFloat::IEEEdouble());EXPECT_EQ(std::numeric_limits<double>::infinity(), DPosInf.convertToDouble());APFloat DNegInf = APFloat::getInf(APFloat::IEEEdouble(), true);EXPECT_EQ(-std::numeric_limits<double>::infinity(),DNegInf.convertToDouble());APFloat DQNaN = APFloat::getQNaN(APFloat::IEEEdouble());EXPECT_TRUE(std::isnan(DQNaN.convertToDouble()));APFloat FPosZero(0.0F);APFloat FPosZeroToDouble(FPosZero.convertToDouble());EXPECT_TRUE(FPosZeroToDouble.isPosZero());APFloat FNegZero(-0.0F);APFloat FNegZeroToDouble(FNegZero.convertToDouble());EXPECT_TRUE(FNegZeroToDouble.isNegZero());APFloat FOne(1.0F);EXPECT_EQ(1.0, FOne.convertToDouble());APFloat FPosLargest = APFloat::getLargest(APFloat::IEEEsingle(), false);EXPECT_EQ(std::numeric_limits<float>::max(), FPosLargest.convertToDouble());APFloat FNegLargest = APFloat::getLargest(APFloat::IEEEsingle(), true);EXPECT_EQ(-std::numeric_limits<float>::max(), FNegLargest.convertToDouble());APFloat FPosSmallest =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);EXPECT_EQ(std::numeric_limits<float>::min(), FPosSmallest.convertToDouble());APFloat FNegSmallest =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);EXPECT_EQ(-std::numeric_limits<float>::min(), FNegSmallest.convertToDouble());APFloat FSmallestDenorm = APFloat::getSmallest(APFloat::IEEEsingle(), false);EXPECT_EQ(std::numeric_limits<float>::denorm_min(),FSmallestDenorm.convertToDouble());APFloat FLargestDenorm(APFloat::IEEEdouble(), "0x0.FFFFFEp-126");EXPECT_EQ(/*0x0.FFFFFEp-126*/ 1.1754942106924411e-38,FLargestDenorm.convertToDouble());APFloat FPosInf = APFloat::getInf(APFloat::IEEEsingle());EXPECT_EQ(std::numeric_limits<double>::infinity(), FPosInf.convertToDouble());APFloat FNegInf = APFloat::getInf(APFloat::IEEEsingle(), true);EXPECT_EQ(-std::numeric_limits<double>::infinity(),FNegInf.convertToDouble());APFloat FQNaN = APFloat::getQNaN(APFloat::IEEEsingle());EXPECT_TRUE(std::isnan(FQNaN.convertToDouble()));APFloat HPosZero = APFloat::getZero(APFloat::IEEEhalf());APFloat HPosZeroToDouble(HPosZero.convertToDouble());EXPECT_TRUE(HPosZeroToDouble.isPosZero());APFloat HNegZero = APFloat::getZero(APFloat::IEEEhalf(), true);APFloat HNegZeroToDouble(HNegZero.convertToDouble());EXPECT_TRUE(HNegZeroToDouble.isNegZero());APFloat HOne(APFloat::IEEEhalf(), "1.0");EXPECT_EQ(1.0, HOne.convertToDouble());APFloat HPosLargest = APFloat::getLargest(APFloat::IEEEhalf(), false);EXPECT_EQ(65504.0, HPosLargest.convertToDouble());APFloat HNegLargest = APFloat::getLargest(APFloat::IEEEhalf(), true);EXPECT_EQ(-65504.0, HNegLargest.convertToDouble());APFloat HPosSmallest =APFloat::getSmallestNormalized(APFloat::IEEEhalf(), false);EXPECT_EQ(/*0x1.p-14*/ 6.103515625e-05, HPosSmallest.convertToDouble());APFloat HNegSmallest =APFloat::getSmallestNormalized(APFloat::IEEEhalf(), true);EXPECT_EQ(/*-0x1.p-14*/ -6.103515625e-05, HNegSmallest.convertToDouble());APFloat HSmallestDenorm = APFloat::getSmallest(APFloat::IEEEhalf(), false);EXPECT_EQ(/*0x1.p-24*/ 5.960464477539063e-08,HSmallestDenorm.convertToDouble());APFloat HLargestDenorm(APFloat::IEEEhalf(), "0x1.FFCp-14");EXPECT_EQ(/*0x1.FFCp-14*/ 0.00012201070785522461,HLargestDenorm.convertToDouble());APFloat HPosInf = APFloat::getInf(APFloat::IEEEhalf());EXPECT_EQ(std::numeric_limits<double>::infinity(), HPosInf.convertToDouble());APFloat HNegInf = APFloat::getInf(APFloat::IEEEhalf(), true);EXPECT_EQ(-std::numeric_limits<double>::infinity(),HNegInf.convertToDouble());APFloat HQNaN = APFloat::getQNaN(APFloat::IEEEhalf());EXPECT_TRUE(std::isnan(HQNaN.convertToDouble()));APFloat BPosZero = APFloat::getZero(APFloat::IEEEhalf());APFloat BPosZeroToDouble(BPosZero.convertToDouble());EXPECT_TRUE(BPosZeroToDouble.isPosZero());APFloat BNegZero = APFloat::getZero(APFloat::IEEEhalf(), true);APFloat BNegZeroToDouble(BNegZero.convertToDouble());EXPECT_TRUE(BNegZeroToDouble.isNegZero());APFloat BOne(APFloat::BFloat(), "1.0");EXPECT_EQ(1.0, BOne.convertToDouble());APFloat BPosLargest = APFloat::getLargest(APFloat::BFloat(), false);EXPECT_EQ(/*0x1.FEp127*/ 3.3895313892515355e+38,BPosLargest.convertToDouble());APFloat BNegLargest = APFloat::getLargest(APFloat::BFloat(), true);EXPECT_EQ(/*-0x1.FEp127*/ -3.3895313892515355e+38,BNegLargest.convertToDouble());APFloat BPosSmallest =APFloat::getSmallestNormalized(APFloat::BFloat(), false);EXPECT_EQ(/*0x1.p-126*/ 1.1754943508222875e-38,BPosSmallest.convertToDouble());APFloat BNegSmallest =APFloat::getSmallestNormalized(APFloat::BFloat(), true);EXPECT_EQ(/*-0x1.p-126*/ -1.1754943508222875e-38,BNegSmallest.convertToDouble());APFloat BSmallestDenorm = APFloat::getSmallest(APFloat::BFloat(), false);EXPECT_EQ(/*0x1.p-133*/ 9.183549615799121e-41,BSmallestDenorm.convertToDouble());APFloat BLargestDenorm(APFloat::BFloat(), "0x1.FCp-127");EXPECT_EQ(/*0x1.FCp-127*/ 1.1663108012064884e-38,BLargestDenorm.convertToDouble());APFloat BPosInf = APFloat::getInf(APFloat::BFloat());EXPECT_EQ(std::numeric_limits<double>::infinity(), BPosInf.convertToDouble());APFloat BNegInf = APFloat::getInf(APFloat::BFloat(), true);EXPECT_EQ(-std::numeric_limits<double>::infinity(),BNegInf.convertToDouble());APFloat BQNaN = APFloat::getQNaN(APFloat::BFloat());EXPECT_TRUE(std::isnan(BQNaN.convertToDouble()));}TEST(APFloatTest, ToFloat) {APFloat FPosZero(0.0F);APFloat FPosZeroToFloat(FPosZero.convertToFloat());EXPECT_TRUE(FPosZeroToFloat.isPosZero());APFloat FNegZero(-0.0F);APFloat FNegZeroToFloat(FNegZero.convertToFloat());EXPECT_TRUE(FNegZeroToFloat.isNegZero());APFloat FOne(1.0F);EXPECT_EQ(1.0F, FOne.convertToFloat());APFloat FPosLargest = APFloat::getLargest(APFloat::IEEEsingle(), false);EXPECT_EQ(std::numeric_limits<float>::max(), FPosLargest.convertToFloat());APFloat FNegLargest = APFloat::getLargest(APFloat::IEEEsingle(), true);EXPECT_EQ(-std::numeric_limits<float>::max(), FNegLargest.convertToFloat());APFloat FPosSmallest =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);EXPECT_EQ(std::numeric_limits<float>::min(), FPosSmallest.convertToFloat());APFloat FNegSmallest =APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);EXPECT_EQ(-std::numeric_limits<float>::min(), FNegSmallest.convertToFloat());APFloat FSmallestDenorm = APFloat::getSmallest(APFloat::IEEEsingle(), false);EXPECT_EQ(std::numeric_limits<float>::denorm_min(),FSmallestDenorm.convertToFloat());APFloat FLargestDenorm(APFloat::IEEEsingle(), "0x1.FFFFFEp-126");EXPECT_EQ(/*0x1.FFFFFEp-126*/ 2.3509885615147286e-38F,FLargestDenorm.convertToFloat());APFloat FPosInf = APFloat::getInf(APFloat::IEEEsingle());EXPECT_EQ(std::numeric_limits<float>::infinity(), FPosInf.convertToFloat());APFloat FNegInf = APFloat::getInf(APFloat::IEEEsingle(), true);EXPECT_EQ(-std::numeric_limits<float>::infinity(), FNegInf.convertToFloat());APFloat FQNaN = APFloat::getQNaN(APFloat::IEEEsingle());EXPECT_TRUE(std::isnan(FQNaN.convertToFloat()));APFloat HPosZero = APFloat::getZero(APFloat::IEEEhalf());APFloat HPosZeroToFloat(HPosZero.convertToFloat());EXPECT_TRUE(HPosZeroToFloat.isPosZero());APFloat HNegZero = APFloat::getZero(APFloat::IEEEhalf(), true);APFloat HNegZeroToFloat(HNegZero.convertToFloat());EXPECT_TRUE(HNegZeroToFloat.isNegZero());APFloat HOne(APFloat::IEEEhalf(), "1.0");EXPECT_EQ(1.0F, HOne.convertToFloat());APFloat HPosLargest = APFloat::getLargest(APFloat::IEEEhalf(), false);EXPECT_EQ(/*0x1.FFCp15*/ 65504.0F, HPosLargest.convertToFloat());APFloat HNegLargest = APFloat::getLargest(APFloat::IEEEhalf(), true);EXPECT_EQ(/*-0x1.FFCp15*/ -65504.0F, HNegLargest.convertToFloat());APFloat HPosSmallest =APFloat::getSmallestNormalized(APFloat::IEEEhalf(), false);EXPECT_EQ(/*0x1.p-14*/ 6.103515625e-05F, HPosSmallest.convertToFloat());APFloat HNegSmallest =APFloat::getSmallestNormalized(APFloat::IEEEhalf(), true);EXPECT_EQ(/*-0x1.p-14*/ -6.103515625e-05F, HNegSmallest.convertToFloat());APFloat HSmallestDenorm = APFloat::getSmallest(APFloat::IEEEhalf(), false);EXPECT_EQ(/*0x1.p-24*/ 5.960464477539063e-08F,HSmallestDenorm.convertToFloat());APFloat HLargestDenorm(APFloat::IEEEhalf(), "0x1.FFCp-14");EXPECT_EQ(/*0x1.FFCp-14*/ 0.00012201070785522461F,HLargestDenorm.convertToFloat());APFloat HPosInf = APFloat::getInf(APFloat::IEEEhalf());EXPECT_EQ(std::numeric_limits<float>::infinity(), HPosInf.convertToFloat());APFloat HNegInf = APFloat::getInf(APFloat::IEEEhalf(), true);EXPECT_EQ(-std::numeric_limits<float>::infinity(), HNegInf.convertToFloat());APFloat HQNaN = APFloat::getQNaN(APFloat::IEEEhalf());EXPECT_TRUE(std::isnan(HQNaN.convertToFloat()));APFloat BPosZero = APFloat::getZero(APFloat::BFloat());APFloat BPosZeroToDouble(BPosZero.convertToFloat());EXPECT_TRUE(BPosZeroToDouble.isPosZero());APFloat BNegZero = APFloat::getZero(APFloat::BFloat(), true);APFloat BNegZeroToDouble(BNegZero.convertToFloat());EXPECT_TRUE(BNegZeroToDouble.isNegZero());APFloat BOne(APFloat::BFloat(), "1.0");EXPECT_EQ(1.0F, BOne.convertToFloat());APFloat BPosLargest = APFloat::getLargest(APFloat::BFloat(), false);EXPECT_EQ(/*0x1.FEp127*/ 3.3895313892515355e+38F,BPosLargest.convertToFloat());APFloat BNegLargest = APFloat::getLargest(APFloat::BFloat(), true);EXPECT_EQ(/*-0x1.FEp127*/ -3.3895313892515355e+38F,BNegLargest.convertToFloat());APFloat BPosSmallest =APFloat::getSmallestNormalized(APFloat::BFloat(), false);EXPECT_EQ(/*0x1.p-126*/ 1.1754943508222875e-38F,BPosSmallest.convertToFloat());APFloat BNegSmallest =APFloat::getSmallestNormalized(APFloat::BFloat(), true);EXPECT_EQ(/*-0x1.p-126*/ -1.1754943508222875e-38F,BNegSmallest.convertToFloat());APFloat BSmallestDenorm = APFloat::getSmallest(APFloat::BFloat(), false);EXPECT_EQ(/*0x1.p-133*/ 9.183549615799121e-41F,BSmallestDenorm.convertToFloat());APFloat BLargestDenorm(APFloat::BFloat(), "0x1.FCp-127");EXPECT_EQ(/*0x1.FCp-127*/ 1.1663108012064884e-38F,BLargestDenorm.convertToFloat());APFloat BPosInf = APFloat::getInf(APFloat::BFloat());EXPECT_EQ(std::numeric_limits<float>::infinity(), BPosInf.convertToFloat());APFloat BNegInf = APFloat::getInf(APFloat::BFloat(), true);EXPECT_EQ(-std::numeric_limits<float>::infinity(), BNegInf.convertToFloat());APFloat BQNaN = APFloat::getQNaN(APFloat::BFloat());EXPECT_TRUE(std::isnan(BQNaN.convertToFloat()));}}
//===- unittests/ADT/FixedPointTest.cpp -- fixed point number tests -----===////// 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////===----------------------------------------------------------------------===//#include "llvm/ADT/APFixedPoint.h"#include "llvm/ADT/APFloat.h"#include "llvm/ADT/APSInt.h"#include "gtest/gtest.h"using llvm::APFixedPoint;using llvm::APFloat;using llvm::APInt;using llvm::APSInt;using llvm::FixedPointSemantics;namespace {FixedPointSemantics Saturated(FixedPointSemantics Sema) {Sema.setSaturated(true);return Sema;}FixedPointSemantics getSAccumSema() {return FixedPointSemantics(/*width=*/16, /*scale=*/7, /*isSigned=*/true,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getAccumSema() {return FixedPointSemantics(/*width=*/32, /*scale=*/15, /*isSigned=*/true,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getLAccumSema() {return FixedPointSemantics(/*width=*/64, /*scale=*/31, /*isSigned=*/true,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getSFractSema() {return FixedPointSemantics(/*width=*/8, /*scale=*/7, /*isSigned=*/true,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getFractSema() {return FixedPointSemantics(/*width=*/16, /*scale=*/15, /*isSigned=*/true,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getLFractSema() {return FixedPointSemantics(/*width=*/32, /*scale=*/31, /*isSigned=*/true,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getUSAccumSema() {return FixedPointSemantics(/*width=*/16, /*scale=*/8, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getUAccumSema() {return FixedPointSemantics(/*width=*/32, /*scale=*/16, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getULAccumSema() {return FixedPointSemantics(/*width=*/64, /*scale=*/32, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getUSFractSema() {return FixedPointSemantics(/*width=*/8, /*scale=*/8, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getUFractSema() {return FixedPointSemantics(/*width=*/16, /*scale=*/16, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getULFractSema() {return FixedPointSemantics(/*width=*/32, /*scale=*/32, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/false);}FixedPointSemantics getPadUSAccumSema() {return FixedPointSemantics(/*width=*/16, /*scale=*/7, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/true);}FixedPointSemantics getPadUAccumSema() {return FixedPointSemantics(/*width=*/32, /*scale=*/15, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/true);}FixedPointSemantics getPadULAccumSema() {return FixedPointSemantics(/*width=*/64, /*scale=*/31, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/true);}FixedPointSemantics getPadUSFractSema() {return FixedPointSemantics(/*width=*/8, /*scale=*/7, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/true);}FixedPointSemantics getPadUFractSema() {return FixedPointSemantics(/*width=*/16, /*scale=*/15, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/true);}FixedPointSemantics getPadULFractSema() {return FixedPointSemantics(/*width=*/32, /*scale=*/31, /*isSigned=*/false,/*isSaturated=*/false,/*hasUnsignedPadding=*/true);}void CheckUnpaddedMax(const FixedPointSemantics &Sema) {ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(),APSInt::getMaxValue(Sema.getWidth(), !Sema.isSigned()));}void CheckPaddedMax(const FixedPointSemantics &Sema) {ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(),APSInt::getMaxValue(Sema.getWidth(), !Sema.isSigned()) >> 1);}void CheckMin(const FixedPointSemantics &Sema) {ASSERT_EQ(APFixedPoint::getMin(Sema).getValue(),APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned()));}TEST(FixedPointTest, getMax) {CheckUnpaddedMax(getSAccumSema());CheckUnpaddedMax(getAccumSema());CheckUnpaddedMax(getLAccumSema());CheckUnpaddedMax(getUSAccumSema());CheckUnpaddedMax(getUAccumSema());CheckUnpaddedMax(getULAccumSema());CheckUnpaddedMax(getSFractSema());CheckUnpaddedMax(getFractSema());CheckUnpaddedMax(getLFractSema());CheckUnpaddedMax(getUSFractSema());CheckUnpaddedMax(getUFractSema());CheckUnpaddedMax(getULFractSema());CheckPaddedMax(getPadUSAccumSema());CheckPaddedMax(getPadUAccumSema());CheckPaddedMax(getPadULAccumSema());CheckPaddedMax(getPadUSFractSema());CheckPaddedMax(getPadUFractSema());CheckPaddedMax(getPadULFractSema());}TEST(FixedPointTest, getMin) {CheckMin(getSAccumSema());CheckMin(getAccumSema());CheckMin(getLAccumSema());CheckMin(getUSAccumSema());CheckMin(getUAccumSema());CheckMin(getULAccumSema());CheckMin(getSFractSema());CheckMin(getFractSema());CheckMin(getLFractSema());CheckMin(getUSFractSema());CheckMin(getUFractSema());CheckMin(getULFractSema());CheckMin(getPadUSAccumSema());CheckMin(getPadUAccumSema());CheckMin(getPadULAccumSema());CheckMin(getPadUSFractSema());CheckMin(getPadUFractSema());CheckMin(getPadULFractSema());}void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) {unsigned Scale = Sema.getScale();// Value with a fractionAPFixedPoint ValWithFract(APInt(Sema.getWidth(),(IntPart << Scale) + (1ULL << (Scale - 1)),Sema.isSigned()),Sema);ASSERT_EQ(ValWithFract.getIntPart(), IntPart);// Just fractionAPFixedPoint JustFract(APInt(Sema.getWidth(), (1ULL << (Scale - 1)), Sema.isSigned()), Sema);ASSERT_EQ(JustFract.getIntPart(), 0);// Whole numberAPFixedPoint WholeNum(APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema);ASSERT_EQ(WholeNum.getIntPart(), IntPart);// Negativeif (Sema.isSigned()) {APFixedPoint Negative(APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema);ASSERT_EQ(Negative.getIntPart(), IntPart);}}void CheckIntPartMin(const FixedPointSemantics &Sema, int64_t Expected) {EXPECT_TRUE(APSInt::compareValues(APFixedPoint::getMin(Sema).getIntPart(),APSInt::get(Expected)) == 0);}void CheckIntPartMax(const FixedPointSemantics &Sema, uint64_t Expected) {EXPECT_TRUE(APSInt::compareValues(APFixedPoint::getMax(Sema).getIntPart(),APSInt::getUnsigned(Expected)) == 0);}TEST(FixedPoint, getIntPart) {// Normal valuesCheckIntPart(getSAccumSema(), 2);CheckIntPart(getAccumSema(), 2);CheckIntPart(getLAccumSema(), 2);CheckIntPart(getUSAccumSema(), 2);CheckIntPart(getUAccumSema(), 2);CheckIntPart(getULAccumSema(), 2);// ZeroCheckIntPart(getSAccumSema(), 0);CheckIntPart(getAccumSema(), 0);CheckIntPart(getLAccumSema(), 0);CheckIntPart(getUSAccumSema(), 0);CheckIntPart(getUAccumSema(), 0);CheckIntPart(getULAccumSema(), 0);CheckIntPart(getSFractSema(), 0);CheckIntPart(getFractSema(), 0);CheckIntPart(getLFractSema(), 0);CheckIntPart(getUSFractSema(), 0);CheckIntPart(getUFractSema(), 0);CheckIntPart(getULFractSema(), 0);// MinCheckIntPartMin(getSAccumSema(), -256);CheckIntPartMin(getAccumSema(), -65536);CheckIntPartMin(getLAccumSema(), -4294967296);CheckIntPartMin(getSFractSema(), -1);CheckIntPartMin(getFractSema(), -1);CheckIntPartMin(getLFractSema(), -1);// MaxCheckIntPartMax(getSAccumSema(), 255);CheckIntPartMax(getAccumSema(), 65535);CheckIntPartMax(getLAccumSema(), 4294967295);CheckIntPartMax(getUSAccumSema(), 255);CheckIntPartMax(getUAccumSema(), 65535);CheckIntPartMax(getULAccumSema(), 4294967295);CheckIntPartMax(getSFractSema(), 0);CheckIntPartMax(getFractSema(), 0);CheckIntPartMax(getLFractSema(), 0);CheckIntPartMax(getUSFractSema(), 0);CheckIntPartMax(getUFractSema(), 0);CheckIntPartMax(getULFractSema(), 0);// Padded// Normal ValuesCheckIntPart(getPadUSAccumSema(), 2);CheckIntPart(getPadUAccumSema(), 2);CheckIntPart(getPadULAccumSema(), 2);// ZeroCheckIntPart(getPadUSAccumSema(), 0);CheckIntPart(getPadUAccumSema(), 0);CheckIntPart(getPadULAccumSema(), 0);CheckIntPart(getPadUSFractSema(), 0);CheckIntPart(getPadUFractSema(), 0);CheckIntPart(getPadULFractSema(), 0);// MaxCheckIntPartMax(getPadUSAccumSema(), 255);CheckIntPartMax(getPadUAccumSema(), 65535);CheckIntPartMax(getPadULAccumSema(), 4294967295);CheckIntPartMax(getPadUSFractSema(), 0);CheckIntPartMax(getPadUFractSema(), 0);CheckIntPartMax(getPadULFractSema(), 0);}TEST(FixedPoint, compare) {// Equality// With fractional part (2.5)// Across sizesASSERT_EQ(APFixedPoint(320, getSAccumSema()),APFixedPoint(81920, getAccumSema()));ASSERT_EQ(APFixedPoint(320, getSAccumSema()),APFixedPoint(5368709120, getLAccumSema()));ASSERT_EQ(APFixedPoint(0, getSAccumSema()), APFixedPoint(0, getLAccumSema()));// Across types (0.5)ASSERT_EQ(APFixedPoint(64, getSAccumSema()),APFixedPoint(64, getSFractSema()));ASSERT_EQ(APFixedPoint(16384, getAccumSema()),APFixedPoint(16384, getFractSema()));ASSERT_EQ(APFixedPoint(1073741824, getLAccumSema()),APFixedPoint(1073741824, getLFractSema()));// Across widths and types (0.5)ASSERT_EQ(APFixedPoint(64, getSAccumSema()),APFixedPoint(16384, getFractSema()));ASSERT_EQ(APFixedPoint(64, getSAccumSema()),APFixedPoint(1073741824, getLFractSema()));// Across saturationASSERT_EQ(APFixedPoint(320, getSAccumSema()),APFixedPoint(81920, Saturated(getAccumSema())));// Across signsASSERT_EQ(APFixedPoint(320, getSAccumSema()),APFixedPoint(640, getUSAccumSema()));ASSERT_EQ(APFixedPoint(-320, getSAccumSema()),APFixedPoint(-81920, getAccumSema()));// Across paddingASSERT_EQ(APFixedPoint(320, getSAccumSema()),APFixedPoint(320, getPadUSAccumSema()));ASSERT_EQ(APFixedPoint(640, getUSAccumSema()),APFixedPoint(320, getPadUSAccumSema()));// Less thanASSERT_LT(APFixedPoint(-1, getSAccumSema()), APFixedPoint(0, getAccumSema()));ASSERT_LT(APFixedPoint(-1, getSAccumSema()),APFixedPoint(0, getUAccumSema()));ASSERT_LT(APFixedPoint(0, getSAccumSema()), APFixedPoint(1, getAccumSema()));ASSERT_LT(APFixedPoint(0, getSAccumSema()), APFixedPoint(1, getUAccumSema()));ASSERT_LT(APFixedPoint(0, getUSAccumSema()), APFixedPoint(1, getAccumSema()));ASSERT_LT(APFixedPoint(0, getUSAccumSema()),APFixedPoint(1, getUAccumSema()));// Greater thanASSERT_GT(APFixedPoint(0, getAccumSema()), APFixedPoint(-1, getSAccumSema()));ASSERT_GT(APFixedPoint(0, getUAccumSema()),APFixedPoint(-1, getSAccumSema()));ASSERT_GT(APFixedPoint(1, getAccumSema()), APFixedPoint(0, getSAccumSema()));ASSERT_GT(APFixedPoint(1, getUAccumSema()), APFixedPoint(0, getSAccumSema()));ASSERT_GT(APFixedPoint(1, getAccumSema()), APFixedPoint(0, getUSAccumSema()));ASSERT_GT(APFixedPoint(1, getUAccumSema()),APFixedPoint(0, getUSAccumSema()));}// Check that a fixed point value in one sema is the same in another semavoid CheckUnsaturatedConversion(FixedPointSemantics Src,FixedPointSemantics Dst, int64_t TestVal) {int64_t ScaledVal = TestVal;bool IsNegative = ScaledVal < 0;if (IsNegative)ScaledVal = -ScaledVal;if (Dst.getScale() > Src.getScale()) {ScaledVal <<= (Dst.getScale() - Src.getScale());} else {ScaledVal >>= (Src.getScale() - Dst.getScale());}if (IsNegative)ScaledVal = -ScaledVal;APFixedPoint Fixed(TestVal, Src);APFixedPoint Expected(ScaledVal, Dst);ASSERT_EQ(Fixed.convert(Dst), Expected);}// Check the value in a given fixed point sema overflows to the saturated min// for another semavoid CheckSaturatedConversionMin(FixedPointSemantics Src,FixedPointSemantics Dst, int64_t TestVal) {APFixedPoint Fixed(TestVal, Src);ASSERT_EQ(Fixed.convert(Dst), APFixedPoint::getMin(Dst));}// Check the value in a given fixed point sema overflows to the saturated max// for another semavoid CheckSaturatedConversionMax(FixedPointSemantics Src,FixedPointSemantics Dst, int64_t TestVal) {APFixedPoint Fixed(TestVal, Src);ASSERT_EQ(Fixed.convert(Dst), APFixedPoint::getMax(Dst));}// Check one signed _Accum sema converted to other sema for different values.void CheckSignedAccumConversionsAgainstOthers(FixedPointSemantics Src,int64_t OneVal) {int64_t NormalVal = (OneVal * 2) + (OneVal / 2); // 2.5int64_t HalfVal = (OneVal / 2); // 0.5// +Accums to AccumsCheckUnsaturatedConversion(Src, getSAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getLAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getUSAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getUAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getULAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getPadUSAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getPadUAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getPadULAccumSema(), NormalVal);// -Accums to AccumsCheckUnsaturatedConversion(Src, getSAccumSema(), -NormalVal);CheckUnsaturatedConversion(Src, getAccumSema(), -NormalVal);CheckUnsaturatedConversion(Src, getLAccumSema(), -NormalVal);CheckSaturatedConversionMin(Src, Saturated(getUSAccumSema()), -NormalVal);CheckSaturatedConversionMin(Src, Saturated(getUAccumSema()), -NormalVal);CheckSaturatedConversionMin(Src, Saturated(getULAccumSema()), -NormalVal);CheckSaturatedConversionMin(Src, Saturated(getPadUSAccumSema()), -NormalVal);CheckSaturatedConversionMin(Src, Saturated(getPadUAccumSema()), -NormalVal);CheckSaturatedConversionMin(Src, Saturated(getPadULAccumSema()), -NormalVal);// +Accums to FractsCheckUnsaturatedConversion(Src, getSFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getLFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getUSFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getUFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getULFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getPadUSFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getPadUFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getPadULFractSema(), HalfVal);// -Accums to FractsCheckUnsaturatedConversion(Src, getSFractSema(), -HalfVal);CheckUnsaturatedConversion(Src, getFractSema(), -HalfVal);CheckUnsaturatedConversion(Src, getLFractSema(), -HalfVal);CheckSaturatedConversionMin(Src, Saturated(getUSFractSema()), -HalfVal);CheckSaturatedConversionMin(Src, Saturated(getUFractSema()), -HalfVal);CheckSaturatedConversionMin(Src, Saturated(getULFractSema()), -HalfVal);CheckSaturatedConversionMin(Src, Saturated(getPadUSFractSema()), -HalfVal);CheckSaturatedConversionMin(Src, Saturated(getPadUFractSema()), -HalfVal);CheckSaturatedConversionMin(Src, Saturated(getPadULFractSema()), -HalfVal);// 0 to AccumsCheckUnsaturatedConversion(Src, getSAccumSema(), 0);CheckUnsaturatedConversion(Src, getAccumSema(), 0);CheckUnsaturatedConversion(Src, getLAccumSema(), 0);CheckUnsaturatedConversion(Src, getUSAccumSema(), 0);CheckUnsaturatedConversion(Src, getUAccumSema(), 0);CheckUnsaturatedConversion(Src, getULAccumSema(), 0);CheckUnsaturatedConversion(Src, getPadUSAccumSema(), 0);CheckUnsaturatedConversion(Src, getPadUAccumSema(), 0);CheckUnsaturatedConversion(Src, getPadULAccumSema(), 0);// 0 to FractsCheckUnsaturatedConversion(Src, getSFractSema(), 0);CheckUnsaturatedConversion(Src, getFractSema(), 0);CheckUnsaturatedConversion(Src, getLFractSema(), 0);CheckUnsaturatedConversion(Src, getUSFractSema(), 0);CheckUnsaturatedConversion(Src, getUFractSema(), 0);CheckUnsaturatedConversion(Src, getULFractSema(), 0);CheckUnsaturatedConversion(Src, getPadUSFractSema(), 0);CheckUnsaturatedConversion(Src, getPadUFractSema(), 0);CheckUnsaturatedConversion(Src, getPadULFractSema(), 0);}// Check one unsigned _Accum sema converted to other sema for different// values.void CheckUnsignedAccumConversionsAgainstOthers(FixedPointSemantics Src,int64_t OneVal) {int64_t NormalVal = (OneVal * 2) + (OneVal / 2); // 2.5int64_t HalfVal = (OneVal / 2); // 0.5// +UAccums to AccumsCheckUnsaturatedConversion(Src, getSAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getLAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getUSAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getUAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getULAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getPadUSAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getPadUAccumSema(), NormalVal);CheckUnsaturatedConversion(Src, getPadULAccumSema(), NormalVal);// +UAccums to FractsCheckUnsaturatedConversion(Src, getSFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getLFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getUSFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getUFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getULFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getPadUSFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getPadUFractSema(), HalfVal);CheckUnsaturatedConversion(Src, getPadULFractSema(), HalfVal);}TEST(FixedPoint, AccumConversions) {// Normal conversionsCheckSignedAccumConversionsAgainstOthers(getSAccumSema(), 128);CheckUnsignedAccumConversionsAgainstOthers(getUSAccumSema(), 256);CheckSignedAccumConversionsAgainstOthers(getAccumSema(), 32768);CheckUnsignedAccumConversionsAgainstOthers(getUAccumSema(), 65536);CheckSignedAccumConversionsAgainstOthers(getLAccumSema(), 2147483648);CheckUnsignedAccumConversionsAgainstOthers(getULAccumSema(), 4294967296);CheckUnsignedAccumConversionsAgainstOthers(getPadUSAccumSema(), 128);CheckUnsignedAccumConversionsAgainstOthers(getPadUAccumSema(), 32768);CheckUnsignedAccumConversionsAgainstOthers(getPadULAccumSema(), 2147483648);}TEST(FixedPoint, AccumConversionOverflow) {// To SAccum max limit (65536)CheckSaturatedConversionMax(getLAccumSema(), Saturated(getAccumSema()),140737488355328);CheckSaturatedConversionMax(getLAccumSema(), Saturated(getUAccumSema()),140737488355328);CheckSaturatedConversionMax(getLAccumSema(), Saturated(getPadUAccumSema()),140737488355328);CheckSaturatedConversionMax(getULAccumSema(), Saturated(getAccumSema()),281474976710656);CheckSaturatedConversionMax(getULAccumSema(), Saturated(getUAccumSema()),281474976710656);CheckSaturatedConversionMax(getULAccumSema(), Saturated(getPadUAccumSema()),281474976710656);CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getAccumSema()),140737488355328);CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getUAccumSema()),140737488355328);CheckSaturatedConversionMax(getPadULAccumSema(),Saturated(getPadUAccumSema()), 140737488355328);// To SAccum min limit (-65536)CheckSaturatedConversionMin(getLAccumSema(), Saturated(getAccumSema()),-140737488355328);CheckSaturatedConversionMin(getLAccumSema(), Saturated(getUAccumSema()),-140737488355328);CheckSaturatedConversionMin(getLAccumSema(), Saturated(getPadUAccumSema()),-140737488355328);}TEST(FixedPoint, SAccumConversionOverflow) {// To SAccum max limit (256)CheckSaturatedConversionMax(getAccumSema(), Saturated(getSAccumSema()),8388608);CheckSaturatedConversionMax(getAccumSema(), Saturated(getUSAccumSema()),8388608);CheckSaturatedConversionMax(getAccumSema(), Saturated(getPadUSAccumSema()),8388608);CheckSaturatedConversionMax(getUAccumSema(), Saturated(getSAccumSema()),16777216);CheckSaturatedConversionMax(getUAccumSema(), Saturated(getUSAccumSema()),16777216);CheckSaturatedConversionMax(getUAccumSema(), Saturated(getPadUSAccumSema()),16777216);CheckSaturatedConversionMax(getLAccumSema(), Saturated(getSAccumSema()),549755813888);CheckSaturatedConversionMax(getLAccumSema(), Saturated(getUSAccumSema()),549755813888);CheckSaturatedConversionMax(getLAccumSema(), Saturated(getPadUSAccumSema()),549755813888);CheckSaturatedConversionMax(getULAccumSema(), Saturated(getSAccumSema()),1099511627776);CheckSaturatedConversionMax(getULAccumSema(), Saturated(getUSAccumSema()),1099511627776);CheckSaturatedConversionMax(getULAccumSema(), Saturated(getPadUSAccumSema()),1099511627776);CheckSaturatedConversionMax(getPadUAccumSema(), Saturated(getSAccumSema()),8388608);CheckSaturatedConversionMax(getPadUAccumSema(), Saturated(getUSAccumSema()),8388608);CheckSaturatedConversionMax(getPadUAccumSema(),Saturated(getPadUSAccumSema()), 8388608);CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getSAccumSema()),549755813888);CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getUSAccumSema()),549755813888);CheckSaturatedConversionMax(getPadULAccumSema(),Saturated(getPadUSAccumSema()), 549755813888);// To SAccum min limit (-256)CheckSaturatedConversionMin(getAccumSema(), Saturated(getSAccumSema()),-8388608);CheckSaturatedConversionMin(getAccumSema(), Saturated(getUSAccumSema()),-8388608);CheckSaturatedConversionMin(getAccumSema(), Saturated(getPadUSAccumSema()),-8388608);CheckSaturatedConversionMin(getLAccumSema(), Saturated(getSAccumSema()),-549755813888);CheckSaturatedConversionMin(getLAccumSema(), Saturated(getUSAccumSema()),-549755813888);CheckSaturatedConversionMin(getLAccumSema(), Saturated(getPadUSAccumSema()),-549755813888);}TEST(FixedPoint, GetValueSignAfterConversion) {APFixedPoint Fixed(255 << 7, getSAccumSema());ASSERT_TRUE(Fixed.getValue().isSigned());APFixedPoint UFixed = Fixed.convert(getUSAccumSema());ASSERT_TRUE(UFixed.getValue().isUnsigned());ASSERT_EQ(UFixed.getValue(), APSInt::getUnsigned(255 << 8).extOrTrunc(16));}TEST(FixedPoint, ModularWrapAround) {// Positive to negativeAPFixedPoint Val = APFixedPoint(1ULL << 7, getSAccumSema());ASSERT_EQ(Val.convert(getLFractSema()).getValue(), -(1ULL << 31));Val = APFixedPoint(1ULL << 23, getAccumSema());ASSERT_EQ(Val.convert(getSAccumSema()).getValue(), -(1ULL << 15));Val = APFixedPoint(1ULL << 47, getLAccumSema());ASSERT_EQ(Val.convert(getAccumSema()).getValue(), -(1ULL << 31));// Negative to positiveVal = APFixedPoint(/*-1.5*/ -192, getSAccumSema());ASSERT_EQ(Val.convert(getLFractSema()).getValue(), 1ULL << 30);Val = APFixedPoint(-(257 << 15), getAccumSema());ASSERT_EQ(Val.convert(getSAccumSema()).getValue(), 255 << 7);Val = APFixedPoint(-(65537ULL << 31), getLAccumSema());ASSERT_EQ(Val.convert(getAccumSema()).getValue(), 65535 << 15);// Signed to unsignedVal = APFixedPoint(-(1 << 7), getSAccumSema());ASSERT_EQ(Val.convert(getUSAccumSema()).getValue(), 255 << 8);Val = APFixedPoint(-(1 << 15), getAccumSema());ASSERT_EQ(Val.convert(getUAccumSema()).getValue(), 65535ULL << 16);Val = APFixedPoint(-(1ULL << 31), getLAccumSema());ASSERT_EQ(Val.convert(getULAccumSema()).getValue().getZExtValue(),4294967295ULL << 32);}enum OvfKind { MinSat, MaxSat };void CheckFloatToFixedConversion(APFloat &Val, const FixedPointSemantics &Sema,int64_t ExpectedNonSat) {bool Ovf;ASSERT_EQ(APFixedPoint::getFromFloatValue(Val, Sema, &Ovf).getValue(),ExpectedNonSat);ASSERT_EQ(Ovf, false);ASSERT_EQ(APFixedPoint::getFromFloatValue(Val, Saturated(Sema), &Ovf).getValue(),ExpectedNonSat);ASSERT_EQ(Ovf, false);}void CheckFloatToFixedConversion(APFloat &Val, const FixedPointSemantics &Sema,OvfKind ExpectedOvf) {bool Ovf;(void)APFixedPoint::getFromFloatValue(Val, Sema, &Ovf);ASSERT_EQ(Ovf, true);ASSERT_EQ(APFixedPoint::getFromFloatValue(Val, Saturated(Sema), &Ovf).getValue(),(ExpectedOvf == MinSat ? APFixedPoint::getMin(Sema): APFixedPoint::getMax(Sema)).getValue());ASSERT_EQ(Ovf, false);}TEST(FixedPoint, FloatToFixed) {APFloat Val(0.0f);// Simple exact fractionVal = APFloat(0.75f);CheckFloatToFixedConversion(Val, getSAccumSema(), 3ULL << 5);CheckFloatToFixedConversion(Val, getAccumSema(), 3ULL << 13);CheckFloatToFixedConversion(Val, getLAccumSema(), 3ULL << 29);CheckFloatToFixedConversion(Val, getUSAccumSema(), 3ULL << 6);CheckFloatToFixedConversion(Val, getUAccumSema(), 3ULL << 14);CheckFloatToFixedConversion(Val, getULAccumSema(), 3ULL << 30);CheckFloatToFixedConversion(Val, getSFractSema(), 3ULL << 5);CheckFloatToFixedConversion(Val, getFractSema(), 3ULL << 13);CheckFloatToFixedConversion(Val, getLFractSema(), 3ULL << 29);CheckFloatToFixedConversion(Val, getUSFractSema(), 3ULL << 6);CheckFloatToFixedConversion(Val, getUFractSema(), 3ULL << 14);CheckFloatToFixedConversion(Val, getULFractSema(), 3ULL << 30);// Simple negative exact fractionVal = APFloat(-0.75f);CheckFloatToFixedConversion(Val, getSAccumSema(), -3ULL << 5);CheckFloatToFixedConversion(Val, getAccumSema(), -3ULL << 13);CheckFloatToFixedConversion(Val, getLAccumSema(), -3ULL << 29);CheckFloatToFixedConversion(Val, getUSAccumSema(), MinSat);CheckFloatToFixedConversion(Val, getUAccumSema(), MinSat);CheckFloatToFixedConversion(Val, getULAccumSema(), MinSat);CheckFloatToFixedConversion(Val, getSFractSema(), -3ULL << 5);CheckFloatToFixedConversion(Val, getFractSema(), -3ULL << 13);CheckFloatToFixedConversion(Val, getLFractSema(), -3ULL << 29);CheckFloatToFixedConversion(Val, getUSFractSema(), MinSat);CheckFloatToFixedConversion(Val, getUFractSema(), MinSat);CheckFloatToFixedConversion(Val, getULFractSema(), MinSat);// Highly precise fractionVal = APFloat(0.999999940395355224609375f);CheckFloatToFixedConversion(Val, getSAccumSema(), 0x7FULL);CheckFloatToFixedConversion(Val, getAccumSema(), 0x7FFFULL);CheckFloatToFixedConversion(Val, getLAccumSema(), 0xFFFFFFULL << 7);CheckFloatToFixedConversion(Val, getUSAccumSema(), 0xFFULL);CheckFloatToFixedConversion(Val, getUAccumSema(), 0xFFFFULL);CheckFloatToFixedConversion(Val, getULAccumSema(), 0xFFFFFFULL << 8);CheckFloatToFixedConversion(Val, getSFractSema(), 0x7FULL);CheckFloatToFixedConversion(Val, getFractSema(), 0x7FFFULL);CheckFloatToFixedConversion(Val, getLFractSema(), 0xFFFFFFULL << 7);CheckFloatToFixedConversion(Val, getUSFractSema(), 0xFFULL);CheckFloatToFixedConversion(Val, getUFractSema(), 0xFFFFULL);CheckFloatToFixedConversion(Val, getULFractSema(), 0xFFFFFFULL << 8);// Integral and fractionVal = APFloat(17.99609375f);CheckFloatToFixedConversion(Val, getSAccumSema(), 0x11FFULL >> 1);CheckFloatToFixedConversion(Val, getAccumSema(), 0x11FFULL << 7);CheckFloatToFixedConversion(Val, getLAccumSema(), 0x11FFULL << 23);CheckFloatToFixedConversion(Val, getUSAccumSema(), 0x11FFULL);CheckFloatToFixedConversion(Val, getUAccumSema(), 0x11FFULL << 8);CheckFloatToFixedConversion(Val, getULAccumSema(), 0x11FFULL << 24);CheckFloatToFixedConversion(Val, getSFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getLFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getUSFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getUFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getULFractSema(), MaxSat);// Negative integral and fractionVal = APFloat(-17.99609375f);CheckFloatToFixedConversion(Val, getSAccumSema(), -0x11FELL >> 1);CheckFloatToFixedConversion(Val, getAccumSema(), -0x11FFULL << 7);CheckFloatToFixedConversion(Val, getLAccumSema(), -0x11FFULL << 23);CheckFloatToFixedConversion(Val, getUSAccumSema(), MinSat);CheckFloatToFixedConversion(Val, getUAccumSema(), MinSat);CheckFloatToFixedConversion(Val, getULAccumSema(), MinSat);CheckFloatToFixedConversion(Val, getSFractSema(), MinSat);CheckFloatToFixedConversion(Val, getFractSema(), MinSat);CheckFloatToFixedConversion(Val, getLFractSema(), MinSat);CheckFloatToFixedConversion(Val, getUSFractSema(), MinSat);CheckFloatToFixedConversion(Val, getUFractSema(), MinSat);CheckFloatToFixedConversion(Val, getULFractSema(), MinSat);// Very large valueVal = APFloat(1.0e38f);CheckFloatToFixedConversion(Val, getSAccumSema(), MaxSat);CheckFloatToFixedConversion(Val, getAccumSema(), MaxSat);CheckFloatToFixedConversion(Val, getLAccumSema(), MaxSat);CheckFloatToFixedConversion(Val, getUSAccumSema(), MaxSat);CheckFloatToFixedConversion(Val, getUAccumSema(), MaxSat);CheckFloatToFixedConversion(Val, getULAccumSema(), MaxSat);CheckFloatToFixedConversion(Val, getSFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getLFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getUSFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getUFractSema(), MaxSat);CheckFloatToFixedConversion(Val, getULFractSema(), MaxSat);// Very small valueVal = APFloat(1.0e-38f);CheckFloatToFixedConversion(Val, getSAccumSema(), 0);CheckFloatToFixedConversion(Val, getAccumSema(), 0);CheckFloatToFixedConversion(Val, getLAccumSema(), 0);CheckFloatToFixedConversion(Val, getUSAccumSema(), 0);CheckFloatToFixedConversion(Val, getUAccumSema(), 0);CheckFloatToFixedConversion(Val, getULAccumSema(), 0);CheckFloatToFixedConversion(Val, getSFractSema(), 0);CheckFloatToFixedConversion(Val, getFractSema(), 0);CheckFloatToFixedConversion(Val, getLFractSema(), 0);CheckFloatToFixedConversion(Val, getUSFractSema(), 0);CheckFloatToFixedConversion(Val, getUFractSema(), 0);CheckFloatToFixedConversion(Val, getULFractSema(), 0);// Half conversionVal = APFloat(0.99951171875f);bool Ignored;Val.convert(APFloat::IEEEhalf(), APFloat::rmNearestTiesToEven, &Ignored);CheckFloatToFixedConversion(Val, getSAccumSema(), 0x7FULL);CheckFloatToFixedConversion(Val, getAccumSema(), 0x7FFULL << 4);CheckFloatToFixedConversion(Val, getLAccumSema(), 0x7FFULL << 20);CheckFloatToFixedConversion(Val, getUSAccumSema(), 0xFFULL);CheckFloatToFixedConversion(Val, getUAccumSema(), 0xFFEULL << 4);CheckFloatToFixedConversion(Val, getULAccumSema(), 0xFFEULL << 20);CheckFloatToFixedConversion(Val, getSFractSema(), 0x7FULL);CheckFloatToFixedConversion(Val, getFractSema(), 0x7FFULL << 4);CheckFloatToFixedConversion(Val, getLFractSema(), 0x7FFULL << 20);CheckFloatToFixedConversion(Val, getUSFractSema(), 0xFFULL);CheckFloatToFixedConversion(Val, getUFractSema(), 0xFFEULL << 4);CheckFloatToFixedConversion(Val, getULFractSema(), 0xFFEULL << 20);}void CheckFixedToFloatConversion(int64_t Val, const FixedPointSemantics &Sema,float Result) {APFixedPoint FXVal(Val, Sema);APFloat APRes(Result);ASSERT_EQ(FXVal.convertToFloat(APFloat::IEEEsingle()), APRes);}void CheckFixedToHalfConversion(int64_t Val, const FixedPointSemantics &Sema,float Result) {APFixedPoint FXVal(Val, Sema);APFloat APRes(Result);bool Ignored;APRes.convert(APFloat::IEEEhalf(), APFloat::rmNearestTiesToEven, &Ignored);ASSERT_EQ(FXVal.convertToFloat(APFloat::IEEEhalf()), APRes);}TEST(FixedPoint, FixedToFloat) {int64_t Val = 0x1ULL;CheckFixedToFloatConversion(Val, getSAccumSema(), 0.0078125f);CheckFixedToFloatConversion(Val, getFractSema(), 0.000030517578125f);CheckFixedToFloatConversion(Val, getAccumSema(), 0.000030517578125f);CheckFixedToFloatConversion(Val, getLFractSema(),0.0000000004656612873077392578125f);CheckFixedToFloatConversion(Val, getUSAccumSema(), 0.00390625f);CheckFixedToFloatConversion(Val, getUFractSema(), 0.0000152587890625f);CheckFixedToFloatConversion(Val, getUAccumSema(), 0.0000152587890625f);CheckFixedToFloatConversion(Val, getULFractSema(),0.00000000023283064365386962890625f);Val = 0x7FULL;CheckFixedToFloatConversion(Val, getSAccumSema(), 0.9921875f);CheckFixedToFloatConversion(Val, getFractSema(), 0.003875732421875f);CheckFixedToFloatConversion(Val, getAccumSema(), 0.003875732421875f);CheckFixedToFloatConversion(Val, getLFractSema(),0.0000000591389834880828857421875f);CheckFixedToFloatConversion(Val, getUSAccumSema(), 0.49609375f);CheckFixedToFloatConversion(Val, getUFractSema(), 0.0019378662109375f);CheckFixedToFloatConversion(Val, getUAccumSema(), 0.0019378662109375f);CheckFixedToFloatConversion(Val, getULFractSema(),0.00000002956949174404144287109375f);Val = -0x1ULL;CheckFixedToFloatConversion(Val, getSAccumSema(), -0.0078125f);CheckFixedToFloatConversion(Val, getFractSema(), -0.000030517578125f);CheckFixedToFloatConversion(Val, getAccumSema(), -0.000030517578125f);CheckFixedToFloatConversion(Val, getLFractSema(),-0.0000000004656612873077392578125f);CheckFixedToFloatConversion(-0x80ULL, getSAccumSema(), -1.0f);CheckFixedToFloatConversion(-0x8000ULL, getFractSema(), -1.0f);CheckFixedToFloatConversion(-0x8000ULL, getAccumSema(), -1.0f);CheckFixedToFloatConversion(-0x80000000ULL, getLFractSema(), -1.0f);Val = 0xAFAULL;CheckFixedToFloatConversion(Val, getSAccumSema(), 21.953125f);CheckFixedToFloatConversion(Val, getFractSema(), 0.08575439453125f);CheckFixedToFloatConversion(Val, getAccumSema(), 0.08575439453125f);CheckFixedToFloatConversion(Val, getLFractSema(),0.000001308508217334747314453125f);CheckFixedToFloatConversion(Val, getUSAccumSema(), 10.9765625f);CheckFixedToFloatConversion(Val, getUFractSema(), 0.042877197265625f);CheckFixedToFloatConversion(Val, getUAccumSema(), 0.042877197265625f);CheckFixedToFloatConversion(Val, getULFractSema(),0.0000006542541086673736572265625f);Val = -0xAFAULL;CheckFixedToFloatConversion(Val, getSAccumSema(), -21.953125f);CheckFixedToFloatConversion(Val, getFractSema(), -0.08575439453125f);CheckFixedToFloatConversion(Val, getAccumSema(), -0.08575439453125f);CheckFixedToFloatConversion(Val, getLFractSema(),-0.000001308508217334747314453125f);Val = 0x40000080ULL;CheckFixedToFloatConversion(Val, getAccumSema(), 32768.00390625f);CheckFixedToFloatConversion(Val, getLFractSema(),0.500000059604644775390625f);CheckFixedToFloatConversion(Val, getUAccumSema(), 16384.001953125f);CheckFixedToFloatConversion(Val, getULFractSema(),0.2500000298023223876953125f);Val = 0x40000040ULL;CheckFixedToFloatConversion(Val, getAccumSema(), 32768.0f);CheckFixedToFloatConversion(Val, getLFractSema(), 0.5f);CheckFixedToFloatConversion(Val, getUAccumSema(), 16384.0f);CheckFixedToFloatConversion(Val, getULFractSema(), 0.25f);Val = 0x7FF0ULL;CheckFixedToHalfConversion(Val, getAccumSema(), 0.99951171875f);CheckFixedToHalfConversion(Val, getLFractSema(), 0.000015251338481903076171875f);CheckFixedToHalfConversion(Val, getUAccumSema(), 0.499755859375f);CheckFixedToHalfConversion(Val, getULFractSema(), 0.0000076256692409515380859375f);}} // namespace
; RUN: llc -verify-machineinstrs -regalloc=simple -O0 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O1 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O2 < %s; Rotate bits in a 8-byte integer n right for k bitsdefine i8 @rotate(i8 %n, i8 %k) {entry:%k.1 = urem i8 %k, 8%n.lo = lshr i8 %n, %k.1%m = sub i8 8, %k.1%n.hi = shl i8 %n, %m%n.1 = or i8 %n.hi, %n.loret i8 %n.1}define i8 @main() {%c = call i8 @rotate(i8 251, i8 3)ret i8 %c}
; RUN: llc -verify-machineinstrs -regalloc=simple -O0 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O1 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O2 < %sdefine i32 @max(i32 %a, i32 %b) {entry:%i = icmp sge i32 %a, %bbr i1 %i, label %then, label %elsethen:br label %endelse:br label %endend:%c = phi i32 [%a, %then], [%b, %else]ret i32 %c}define i32 @main() {%c = call i32 @max(i32 15, i32 24)ret i32 %c}
; RUN: llc -verify-machineinstrs -regalloc=simple -O0 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O1 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O2 < %sdefine i32 @fib(i32 %n) {entry:%base = icmp sle i32 %n, 2br i1 %base, label %then, label %elsethen:ret i32 1else:%n.1 = sub i32 %n, 1%n.2 = sub i32 %n, 2%f.1 = call i32 @fib(i32 %n.1)%f.2 = call i32 @fib(i32 %n.2)%f = add i32 %f.1, %f.2ret i32 %f}define i32 @main() {%c = call i32 @fib(i32 12)ret i32 %c}
; RUN: llc -verify-machineinstrs -regalloc=simple -O0 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O1 < %s; RUN: llc -verify-machineinstrs -regalloc=simple -O2 < %sdefine i1 @even(i32 %n) {entry:%z = icmp eq i32 %n, 0br i1 %z, label %true, label %nonzerononzero:%p = icmp sgt i32 %n, 0br i1 %p, label %positive, label %negativepositive:%n.1 = sub i32 %n, 1%even.1 = call i1 @even(i32 %n.1)br i1 %even.1, label %false, label %truenegative:%n.2 = add i32 %n, 1%even.2 = call i1 @even(i32 %n.2)br i1 %even.2, label %false, label %truetrue:ret i1 1false:ret i1 0}define i1 @main() {%c = call i1 @even(i32 -11)ret i1 %c}
//===- TargetPassConfig.cpp - Target independent code generation passes ---===////// 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////===----------------------------------------------------------------------===////// This file defines interfaces to access the target independent code// generation passes provided by the LLVM backend.////===---------------------------------------------------------------------===//#include "llvm/CodeGen/TargetPassConfig.h"#include "llvm/ADT/DenseMap.h"#include "llvm/ADT/SmallVector.h"#include "llvm/ADT/StringRef.h"#include "llvm/Analysis/BasicAliasAnalysis.h"#include "llvm/Analysis/CFLAndersAliasAnalysis.h"#include "llvm/Analysis/CFLSteensAliasAnalysis.h"#include "llvm/Analysis/CallGraphSCCPass.h"#include "llvm/Analysis/ScopedNoAliasAA.h"#include "llvm/Analysis/TargetTransformInfo.h"#include "llvm/Analysis/TypeBasedAliasAnalysis.h"#include "llvm/CodeGen/BasicBlockSectionsProfileReader.h"#include "llvm/CodeGen/CSEConfigBase.h"#include "llvm/CodeGen/MachineFunctionPass.h"#include "llvm/CodeGen/MachinePassRegistry.h"#include "llvm/CodeGen/Passes.h"#include "llvm/CodeGen/RegAllocRegistry.h"#include "llvm/IR/IRPrintingPasses.h"#include "llvm/IR/LegacyPassManager.h"#include "llvm/IR/PassInstrumentation.h"#include "llvm/IR/Verifier.h"#include "llvm/InitializePasses.h"#include "llvm/MC/MCAsmInfo.h"#include "llvm/MC/MCTargetOptions.h"#include "llvm/Pass.h"#include "llvm/Support/CodeGen.h"#include "llvm/Support/CommandLine.h"#include "llvm/Support/Compiler.h"#include "llvm/Support/Debug.h"#include "llvm/Support/Discriminator.h"#include "llvm/Support/ErrorHandling.h"#include "llvm/Support/SaveAndRestore.h"#include "llvm/Support/Threading.h"#include "llvm/Target/CGPassBuilderOption.h"#include "llvm/Target/TargetMachine.h"#include "llvm/Transforms/Scalar.h"#include "llvm/Transforms/Utils.h"#include <cassert>#include <string>using namespace llvm;static cl::opt<bool>EnableIPRA("enable-ipra", cl::init(false), cl::Hidden,cl::desc("Enable interprocedural register allocation ""to reduce load/store at procedure calls."));static cl::opt<bool> DisablePostRASched("disable-post-ra", cl::Hidden,cl::desc("Disable Post Regalloc Scheduler"));static cl::opt<bool> DisableBranchFold("disable-branch-fold", cl::Hidden,cl::desc("Disable branch folding"));static cl::opt<bool> DisableTailDuplicate("disable-tail-duplicate", cl::Hidden,cl::desc("Disable tail duplication"));static cl::opt<bool> DisableEarlyTailDup("disable-early-taildup", cl::Hidden,cl::desc("Disable pre-register allocation tail duplication"));static cl::opt<bool> DisableBlockPlacement("disable-block-placement",cl::Hidden, cl::desc("Disable probability-driven block placement"));static cl::opt<bool> EnableBlockPlacementStats("enable-block-placement-stats",cl::Hidden, cl::desc("Collect probability-driven block placement stats"));static cl::opt<bool> DisableSSC("disable-ssc", cl::Hidden,cl::desc("Disable Stack Slot Coloring"));static cl::opt<bool> DisableMachineDCE("disable-machine-dce", cl::Hidden,cl::desc("Disable Machine Dead Code Elimination"));static cl::opt<bool> DisableEarlyIfConversion("disable-early-ifcvt", cl::Hidden,cl::desc("Disable Early If-conversion"));static cl::opt<bool> DisableMachineLICM("disable-machine-licm", cl::Hidden,cl::desc("Disable Machine LICM"));static cl::opt<bool> DisableMachineCSE("disable-machine-cse", cl::Hidden,cl::desc("Disable Machine Common Subexpression Elimination"));static cl::opt<cl::boolOrDefault> OptimizeRegAlloc("optimize-regalloc", cl::Hidden,cl::desc("Enable optimized register allocation compilation path."));static cl::opt<bool> DisablePostRAMachineLICM("disable-postra-machine-licm",cl::Hidden,cl::desc("Disable Machine LICM"));static cl::opt<bool> DisableMachineSink("disable-machine-sink", cl::Hidden,cl::desc("Disable Machine Sinking"));static cl::opt<bool> DisablePostRAMachineSink("disable-postra-machine-sink",cl::Hidden,cl::desc("Disable PostRA Machine Sinking"));static cl::opt<bool> DisableLSR("disable-lsr", cl::Hidden,cl::desc("Disable Loop Strength Reduction Pass"));static cl::opt<bool> DisableConstantHoisting("disable-constant-hoisting",cl::Hidden, cl::desc("Disable ConstantHoisting"));static cl::opt<bool> DisableCGP("disable-cgp", cl::Hidden,cl::desc("Disable Codegen Prepare"));static cl::opt<bool> DisableCopyProp("disable-copyprop", cl::Hidden,cl::desc("Disable Copy Propagation pass"));static cl::opt<bool> DisablePartialLibcallInlining("disable-partial-libcall-inlining",cl::Hidden, cl::desc("Disable Partial Libcall Inlining"));static cl::opt<bool> EnableImplicitNullChecks("enable-implicit-null-checks",cl::desc("Fold null checks into faulting memory operations"),cl::init(false), cl::Hidden);static cl::opt<bool> DisableMergeICmps("disable-mergeicmps",cl::desc("Disable MergeICmps Pass"),cl::init(false), cl::Hidden);static cl::opt<bool> PrintLSR("print-lsr-output", cl::Hidden,cl::desc("Print LLVM IR produced by the loop-reduce pass"));static cl::opt<bool> PrintISelInput("print-isel-input", cl::Hidden,cl::desc("Print LLVM IR input to isel pass"));static cl::opt<bool> PrintGCInfo("print-gc", cl::Hidden,cl::desc("Dump garbage collector data"));static cl::opt<cl::boolOrDefault>VerifyMachineCode("verify-machineinstrs", cl::Hidden,cl::desc("Verify generated machine code"));static cl::opt<cl::boolOrDefault>DebugifyAndStripAll("debugify-and-strip-all-safe", cl::Hidden,cl::desc("Debugify MIR before and Strip debug after ""each pass except those known to be unsafe ""when debug info is present"));static cl::opt<cl::boolOrDefault> DebugifyCheckAndStripAll("debugify-check-and-strip-all-safe", cl::Hidden,cl::desc("Debugify MIR before, by checking and stripping the debug info after, ""each pass except those known to be unsafe when debug info is ""present"));// Enable or disable the MachineOutliner.static cl::opt<RunOutliner> EnableMachineOutliner("enable-machine-outliner", cl::desc("Enable the machine outliner"),cl::Hidden, cl::ValueOptional, cl::init(RunOutliner::TargetDefault),cl::values(clEnumValN(RunOutliner::AlwaysOutline, "always","Run on all functions guaranteed to be beneficial"),clEnumValN(RunOutliner::NeverOutline, "never","Disable all outlining"),// Sentinel value for unspecified option.clEnumValN(RunOutliner::AlwaysOutline, "", "")));// Disable the pass to fix unwind information. Whether the pass is included in// the pipeline is controlled via the target options, this option serves as// manual override.static cl::opt<bool> DisableCFIFixup("disable-cfi-fixup", cl::Hidden,cl::desc("Disable the CFI fixup pass"));// Enable or disable FastISel. Both options are needed, because// FastISel is enabled by default with -fast, and we wish to be// able to enable or disable fast-isel independently from -O0.static cl::opt<cl::boolOrDefault>EnableFastISelOption("fast-isel", cl::Hidden,cl::desc("Enable the \"fast\" instruction selector"));static cl::opt<cl::boolOrDefault> EnableGlobalISelOption("global-isel", cl::Hidden,cl::desc("Enable the \"global\" instruction selector"));// FIXME: remove this after switching to NPM or GlobalISel, whichever gets there// first...static cl::opt<bool>PrintAfterISel("print-after-isel", cl::init(false), cl::Hidden,cl::desc("Print machine instrs after ISel"));static cl::opt<GlobalISelAbortMode> EnableGlobalISelAbort("global-isel-abort", cl::Hidden,cl::desc("Enable abort calls when \"global\" instruction selection ""fails to lower/select an instruction"),cl::values(clEnumValN(GlobalISelAbortMode::Disable, "0", "Disable the abort"),clEnumValN(GlobalISelAbortMode::Enable, "1", "Enable the abort"),clEnumValN(GlobalISelAbortMode::DisableWithDiag, "2","Disable the abort but emit a diagnostic on failure")));// An option that disables inserting FS-AFDO discriminators before emit.// This is mainly for debugging and tuning purpose.static cl::opt<bool>FSNoFinalDiscrim("fs-no-final-discrim", cl::init(false), cl::Hidden,cl::desc("Do not insert FS-AFDO discriminators before ""emit."));// Disable MIRProfileLoader before RegAlloc. This is for for debugging and// tuning purpose.static cl::opt<bool> DisableRAFSProfileLoader("disable-ra-fsprofile-loader", cl::init(false), cl::Hidden,cl::desc("Disable MIRProfileLoader before RegAlloc"));// Disable MIRProfileLoader before BloackPlacement. This is for for debugging// and tuning purpose.static cl::opt<bool> DisableLayoutFSProfileLoader("disable-layout-fsprofile-loader", cl::init(false), cl::Hidden,cl::desc("Disable MIRProfileLoader before BlockPlacement"));// Specify FSProfile file name.static cl::opt<std::string>FSProfileFile("fs-profile-file", cl::init(""), cl::value_desc("filename"),cl::desc("Flow Sensitive profile file name."), cl::Hidden);// Specify Remapping file for FSProfile.static cl::opt<std::string> FSRemappingFile("fs-remapping-file", cl::init(""), cl::value_desc("filename"),cl::desc("Flow Sensitive profile remapping file name."), cl::Hidden);// Temporary option to allow experimenting with MachineScheduler as a post-RA// scheduler. Targets can "properly" enable this with// substitutePass(&PostRASchedulerID, &PostMachineSchedulerID).// Targets can return true in targetSchedulesPostRAScheduling() and// insert a PostRA scheduling pass wherever it wants.static cl::opt<bool> MISchedPostRA("misched-postra", cl::Hidden,cl::desc("Run MachineScheduler post regalloc (independent of preRA sched)"));// Experimental option to run live interval analysis early.static cl::opt<bool> EarlyLiveIntervals("early-live-intervals", cl::Hidden,cl::desc("Run live interval analysis earlier in the pipeline"));// Experimental option to use CFL-AA in codegenstatic cl::opt<CFLAAType> UseCFLAA("use-cfl-aa-in-codegen", cl::init(CFLAAType::None), cl::Hidden,cl::desc("Enable the new, experimental CFL alias analysis in CodeGen"),cl::values(clEnumValN(CFLAAType::None, "none", "Disable CFL-AA"),clEnumValN(CFLAAType::Steensgaard, "steens","Enable unification-based CFL-AA"),clEnumValN(CFLAAType::Andersen, "anders","Enable inclusion-based CFL-AA"),clEnumValN(CFLAAType::Both, "both","Enable both variants of CFL-AA")));/// Option names for limiting the codegen pipeline./// Those are used in error reporting and we didn't want/// to duplicate their names all over the place.static const char StartAfterOptName[] = "start-after";static const char StartBeforeOptName[] = "start-before";static const char StopAfterOptName[] = "stop-after";static const char StopBeforeOptName[] = "stop-before";static cl::opt<std::string>StartAfterOpt(StringRef(StartAfterOptName),cl::desc("Resume compilation after a specific pass"),cl::value_desc("pass-name"), cl::init(""), cl::Hidden);static cl::opt<std::string>StartBeforeOpt(StringRef(StartBeforeOptName),cl::desc("Resume compilation before a specific pass"),cl::value_desc("pass-name"), cl::init(""), cl::Hidden);static cl::opt<std::string>StopAfterOpt(StringRef(StopAfterOptName),cl::desc("Stop compilation after a specific pass"),cl::value_desc("pass-name"), cl::init(""), cl::Hidden);static cl::opt<std::string>StopBeforeOpt(StringRef(StopBeforeOptName),cl::desc("Stop compilation before a specific pass"),cl::value_desc("pass-name"), cl::init(""), cl::Hidden);/// Enable the machine function splitter pass.static cl::opt<bool> EnableMachineFunctionSplitter("enable-split-machine-functions", cl::Hidden,cl::desc("Split out cold blocks from machine functions based on profile ""information."));/// Disable the expand reductions pass for testing.static cl::opt<bool> DisableExpandReductions("disable-expand-reductions", cl::init(false), cl::Hidden,cl::desc("Disable the expand reduction intrinsics pass from running"));/// Disable the select optimization pass.static cl::opt<bool> DisableSelectOptimize("disable-select-optimize", cl::init(true), cl::Hidden,cl::desc("Disable the select-optimization pass from running"));/// Allow standard passes to be disabled by command line options. This supports/// simple binary flags that either suppress the pass or do nothing./// i.e. -disable-mypass=false has no effect./// These should be converted to boolOrDefault in order to use applyOverride.static IdentifyingPassPtr applyDisable(IdentifyingPassPtr PassID,bool Override) {if (Override)return IdentifyingPassPtr();return PassID;}/// Allow standard passes to be disabled by the command line, regardless of who/// is adding the pass.////// StandardID is the pass identified in the standard pass pipeline and provided/// to addPass(). It may be a target-specific ID in the case that the target/// directly adds its own pass, but in that case we harmlessly fall through.////// TargetID is the pass that the target has configured to override StandardID.////// StandardID may be a pseudo ID. In that case TargetID is the name of the real/// pass to run. This allows multiple options to control a single pass depending/// on where in the pipeline that pass is added.static IdentifyingPassPtr overridePass(AnalysisID StandardID,IdentifyingPassPtr TargetID) {if (StandardID == &PostRASchedulerID)return applyDisable(TargetID, DisablePostRASched);if (StandardID == &BranchFolderPassID)return applyDisable(TargetID, DisableBranchFold);if (StandardID == &TailDuplicateID)return applyDisable(TargetID, DisableTailDuplicate);if (StandardID == &EarlyTailDuplicateID)return applyDisable(TargetID, DisableEarlyTailDup);if (StandardID == &MachineBlockPlacementID)return applyDisable(TargetID, DisableBlockPlacement);if (StandardID == &StackSlotColoringID)return applyDisable(TargetID, DisableSSC);if (StandardID == &DeadMachineInstructionElimID)return applyDisable(TargetID, DisableMachineDCE);if (StandardID == &EarlyIfConverterID)return applyDisable(TargetID, DisableEarlyIfConversion);if (StandardID == &EarlyMachineLICMID)return applyDisable(TargetID, DisableMachineLICM);if (StandardID == &MachineCSEID)return applyDisable(TargetID, DisableMachineCSE);if (StandardID == &MachineLICMID)return applyDisable(TargetID, DisablePostRAMachineLICM);if (StandardID == &MachineSinkingID)return applyDisable(TargetID, DisableMachineSink);if (StandardID == &PostRAMachineSinkingID)return applyDisable(TargetID, DisablePostRAMachineSink);if (StandardID == &MachineCopyPropagationID)return applyDisable(TargetID, DisableCopyProp);return TargetID;}// Find the FSProfile file name. The internal option takes the precedence// before getting from TargetMachine.static std::string getFSProfileFile(const TargetMachine *TM) {if (!FSProfileFile.empty())return FSProfileFile.getValue();const Optional<PGOOptions> &PGOOpt = TM->getPGOOption();if (PGOOpt == None || PGOOpt->Action != PGOOptions::SampleUse)return std::string();return PGOOpt->ProfileFile;}// Find the Profile remapping file name. The internal option takes the// precedence before getting from TargetMachine.static std::string getFSRemappingFile(const TargetMachine *TM) {if (!FSRemappingFile.empty())return FSRemappingFile.getValue();const Optional<PGOOptions> &PGOOpt = TM->getPGOOption();if (PGOOpt == None || PGOOpt->Action != PGOOptions::SampleUse)return std::string();return PGOOpt->ProfileRemappingFile;}//===---------------------------------------------------------------------===///// TargetPassConfig//===---------------------------------------------------------------------===//INITIALIZE_PASS(TargetPassConfig, "targetpassconfig","Target Pass Configuration", false, false)char TargetPassConfig::ID = 0;namespace {struct InsertedPass {AnalysisID TargetPassID;IdentifyingPassPtr InsertedPassID;InsertedPass(AnalysisID TargetPassID, IdentifyingPassPtr InsertedPassID): TargetPassID(TargetPassID), InsertedPassID(InsertedPassID) {}Pass *getInsertedPass() const {assert(InsertedPassID.isValid() && "Illegal Pass ID!");if (InsertedPassID.isInstance())return InsertedPassID.getInstance();Pass *NP = Pass::createPass(InsertedPassID.getID());assert(NP && "Pass ID not registered");return NP;}};} // end anonymous namespacenamespace llvm {extern cl::opt<bool> EnableFSDiscriminator;class PassConfigImpl {public:// List of passes explicitly substituted by this target. Normally this is// empty, but it is a convenient way to suppress or replace specific passes// that are part of a standard pass pipeline without overridding the entire// pipeline. This mechanism allows target options to inherit a standard pass's// user interface. For example, a target may disable a standard pass by// default by substituting a pass ID of zero, and the user may still enable// that standard pass with an explicit command line option.DenseMap<AnalysisID,IdentifyingPassPtr> TargetPasses;/// Store the pairs of <AnalysisID, AnalysisID> of which the second pass/// is inserted after each instance of the first one.SmallVector<InsertedPass, 4> InsertedPasses;};} // end namespace llvm// Out of line virtual method.TargetPassConfig::~TargetPassConfig() {delete Impl;}static const PassInfo *getPassInfo(StringRef PassName) {if (PassName.empty())return nullptr;const PassRegistry &PR = *PassRegistry::getPassRegistry();const PassInfo *PI = PR.getPassInfo(PassName);if (!PI)report_fatal_error(Twine('\"') + Twine(PassName) +Twine("\" pass is not registered."));return PI;}static AnalysisID getPassIDFromName(StringRef PassName) {const PassInfo *PI = getPassInfo(PassName);return PI ? PI->getTypeInfo() : nullptr;}static std::pair<StringRef, unsigned>getPassNameAndInstanceNum(StringRef PassName) {StringRef Name, InstanceNumStr;std::tie(Name, InstanceNumStr) = PassName.split(',');unsigned InstanceNum = 0;if (!InstanceNumStr.empty() && InstanceNumStr.getAsInteger(10, InstanceNum))report_fatal_error("invalid pass instance specifier " + PassName);return std::make_pair(Name, InstanceNum);}void TargetPassConfig::setStartStopPasses() {StringRef StartBeforeName;std::tie(StartBeforeName, StartBeforeInstanceNum) =getPassNameAndInstanceNum(StartBeforeOpt);StringRef StartAfterName;std::tie(StartAfterName, StartAfterInstanceNum) =getPassNameAndInstanceNum(StartAfterOpt);StringRef StopBeforeName;std::tie(StopBeforeName, StopBeforeInstanceNum)= getPassNameAndInstanceNum(StopBeforeOpt);StringRef StopAfterName;std::tie(StopAfterName, StopAfterInstanceNum)= getPassNameAndInstanceNum(StopAfterOpt);StartBefore = getPassIDFromName(StartBeforeName);StartAfter = getPassIDFromName(StartAfterName);StopBefore = getPassIDFromName(StopBeforeName);StopAfter = getPassIDFromName(StopAfterName);if (StartBefore && StartAfter)report_fatal_error(Twine(StartBeforeOptName) + Twine(" and ") +Twine(StartAfterOptName) + Twine(" specified!"));if (StopBefore && StopAfter)report_fatal_error(Twine(StopBeforeOptName) + Twine(" and ") +Twine(StopAfterOptName) + Twine(" specified!"));Started = (StartAfter == nullptr) && (StartBefore == nullptr);}CGPassBuilderOption llvm::getCGPassBuilderOption() {CGPassBuilderOption Opt;#define SET_OPTION(Option) \if (Option.getNumOccurrences()) \Opt.Option = Option;SET_OPTION(EnableFastISelOption)SET_OPTION(EnableGlobalISelAbort)SET_OPTION(EnableGlobalISelOption)SET_OPTION(EnableIPRA)SET_OPTION(OptimizeRegAlloc)SET_OPTION(VerifyMachineCode)#define SET_BOOLEAN_OPTION(Option) Opt.Option = Option;SET_BOOLEAN_OPTION(EarlyLiveIntervals)SET_BOOLEAN_OPTION(EnableBlockPlacementStats)SET_BOOLEAN_OPTION(EnableImplicitNullChecks)SET_BOOLEAN_OPTION(EnableMachineOutliner)SET_BOOLEAN_OPTION(MISchedPostRA)SET_BOOLEAN_OPTION(UseCFLAA)SET_BOOLEAN_OPTION(DisableMergeICmps)SET_BOOLEAN_OPTION(DisableLSR)SET_BOOLEAN_OPTION(DisableConstantHoisting)SET_BOOLEAN_OPTION(DisableCGP)SET_BOOLEAN_OPTION(DisablePartialLibcallInlining)SET_BOOLEAN_OPTION(DisableSelectOptimize)SET_BOOLEAN_OPTION(PrintLSR)SET_BOOLEAN_OPTION(PrintISelInput)SET_BOOLEAN_OPTION(PrintGCInfo)return Opt;}static void registerPartialPipelineCallback(PassInstrumentationCallbacks &PIC,LLVMTargetMachine &LLVMTM) {StringRef StartBefore;StringRef StartAfter;StringRef StopBefore;StringRef StopAfter;unsigned StartBeforeInstanceNum = 0;unsigned StartAfterInstanceNum = 0;unsigned StopBeforeInstanceNum = 0;unsigned StopAfterInstanceNum = 0;std::tie(StartBefore, StartBeforeInstanceNum) =getPassNameAndInstanceNum(StartBeforeOpt);std::tie(StartAfter, StartAfterInstanceNum) =getPassNameAndInstanceNum(StartAfterOpt);std::tie(StopBefore, StopBeforeInstanceNum) =getPassNameAndInstanceNum(StopBeforeOpt);std::tie(StopAfter, StopAfterInstanceNum) =getPassNameAndInstanceNum(StopAfterOpt);if (StartBefore.empty() && StartAfter.empty() && StopBefore.empty() &&StopAfter.empty())return;std::tie(StartBefore, std::ignore) =LLVMTM.getPassNameFromLegacyName(StartBefore);std::tie(StartAfter, std::ignore) =LLVMTM.getPassNameFromLegacyName(StartAfter);std::tie(StopBefore, std::ignore) =LLVMTM.getPassNameFromLegacyName(StopBefore);std::tie(StopAfter, std::ignore) =LLVMTM.getPassNameFromLegacyName(StopAfter);if (!StartBefore.empty() && !StartAfter.empty())report_fatal_error(Twine(StartBeforeOptName) + Twine(" and ") +Twine(StartAfterOptName) + Twine(" specified!"));if (!StopBefore.empty() && !StopAfter.empty())report_fatal_error(Twine(StopBeforeOptName) + Twine(" and ") +Twine(StopAfterOptName) + Twine(" specified!"));PIC.registerShouldRunOptionalPassCallback([=, EnableCurrent = StartBefore.empty() && StartAfter.empty(),EnableNext = Optional<bool>(), StartBeforeCount = 0u,StartAfterCount = 0u, StopBeforeCount = 0u,StopAfterCount = 0u](StringRef P, Any) mutable {bool StartBeforePass = !StartBefore.empty() && P.contains(StartBefore);bool StartAfterPass = !StartAfter.empty() && P.contains(StartAfter);bool StopBeforePass = !StopBefore.empty() && P.contains(StopBefore);bool StopAfterPass = !StopAfter.empty() && P.contains(StopAfter);// Implement -start-after/-stop-afterif (EnableNext) {EnableCurrent = *EnableNext;EnableNext.reset();}// Using PIC.registerAfterPassCallback won't work because if this// callback returns false, AfterPassCallback is also skipped.if (StartAfterPass && StartAfterCount++ == StartAfterInstanceNum) {assert(!EnableNext && "Error: assign to EnableNext more than once");EnableNext = true;}if (StopAfterPass && StopAfterCount++ == StopAfterInstanceNum) {assert(!EnableNext && "Error: assign to EnableNext more than once");EnableNext = false;}if (StartBeforePass && StartBeforeCount++ == StartBeforeInstanceNum)EnableCurrent = true;if (StopBeforePass && StopBeforeCount++ == StopBeforeInstanceNum)EnableCurrent = false;return EnableCurrent;});}void llvm::registerCodeGenCallback(PassInstrumentationCallbacks &PIC,LLVMTargetMachine &LLVMTM) {// Register a callback for disabling passes.PIC.registerShouldRunOptionalPassCallback([](StringRef P, Any) {#define DISABLE_PASS(Option, Name) \if (Option && P.contains(#Name)) \return false;DISABLE_PASS(DisableBlockPlacement, MachineBlockPlacementPass)DISABLE_PASS(DisableBranchFold, BranchFolderPass)DISABLE_PASS(DisableCopyProp, MachineCopyPropagationPass)DISABLE_PASS(DisableEarlyIfConversion, EarlyIfConverterPass)DISABLE_PASS(DisableEarlyTailDup, EarlyTailDuplicatePass)DISABLE_PASS(DisableMachineCSE, MachineCSEPass)DISABLE_PASS(DisableMachineDCE, DeadMachineInstructionElimPass)DISABLE_PASS(DisableMachineLICM, EarlyMachineLICMPass)DISABLE_PASS(DisableMachineSink, MachineSinkingPass)DISABLE_PASS(DisablePostRAMachineLICM, MachineLICMPass)DISABLE_PASS(DisablePostRAMachineSink, PostRAMachineSinkingPass)DISABLE_PASS(DisablePostRASched, PostRASchedulerPass)DISABLE_PASS(DisableSSC, StackSlotColoringPass)DISABLE_PASS(DisableTailDuplicate, TailDuplicatePass)return true;});registerPartialPipelineCallback(PIC, LLVMTM);}// Out of line constructor provides default values for pass options and// registers all common codegen passes.TargetPassConfig::TargetPassConfig(LLVMTargetMachine &TM, PassManagerBase &pm): ImmutablePass(ID), PM(&pm), TM(&TM) {Impl = new PassConfigImpl();// Register all target independent codegen passes to activate their PassIDs,// including this pass itself.initializeCodeGen(*PassRegistry::getPassRegistry());// Also register alias analysis passes required by codegen passes.initializeBasicAAWrapperPassPass(*PassRegistry::getPassRegistry());initializeAAResultsWrapperPassPass(*PassRegistry::getPassRegistry());if (EnableIPRA.getNumOccurrences())TM.Options.EnableIPRA = EnableIPRA;else {// If not explicitly specified, use target default.TM.Options.EnableIPRA |= TM.useIPRA();}if (TM.Options.EnableIPRA)setRequiresCodeGenSCCOrder();if (EnableGlobalISelAbort.getNumOccurrences())TM.Options.GlobalISelAbort = EnableGlobalISelAbort;setStartStopPasses();}CodeGenOpt::Level TargetPassConfig::getOptLevel() const {return TM->getOptLevel();}/// Insert InsertedPassID pass after TargetPassID.void TargetPassConfig::insertPass(AnalysisID TargetPassID,IdentifyingPassPtr InsertedPassID) {assert(((!InsertedPassID.isInstance() &&TargetPassID != InsertedPassID.getID()) ||(InsertedPassID.isInstance() &&TargetPassID != InsertedPassID.getInstance()->getPassID())) &&"Insert a pass after itself!");Impl->InsertedPasses.emplace_back(TargetPassID, InsertedPassID);}/// createPassConfig - Create a pass configuration object to be used by/// addPassToEmitX methods for generating a pipeline of CodeGen passes.////// Targets may override this to extend TargetPassConfig.TargetPassConfig *LLVMTargetMachine::createPassConfig(PassManagerBase &PM) {return new TargetPassConfig(*this, PM);}TargetPassConfig::TargetPassConfig(): ImmutablePass(ID) {report_fatal_error("Trying to construct TargetPassConfig without a target ""machine. Scheduling a CodeGen pass without a target ""triple set?");}bool TargetPassConfig::willCompleteCodeGenPipeline() {return StopBeforeOpt.empty() && StopAfterOpt.empty();}bool TargetPassConfig::hasLimitedCodeGenPipeline() {return !StartBeforeOpt.empty() || !StartAfterOpt.empty() ||!willCompleteCodeGenPipeline();}std::stringTargetPassConfig::getLimitedCodeGenPipelineReason(const char *Separator) {if (!hasLimitedCodeGenPipeline())return std::string();std::string Res;static cl::opt<std::string> *PassNames[] = {&StartAfterOpt, &StartBeforeOpt,&StopAfterOpt, &StopBeforeOpt};static const char *OptNames[] = {StartAfterOptName, StartBeforeOptName,StopAfterOptName, StopBeforeOptName};bool IsFirst = true;for (int Idx = 0; Idx < 4; ++Idx)if (!PassNames[Idx]->empty()) {if (!IsFirst)Res += Separator;IsFirst = false;Res += OptNames[Idx];}return Res;}// Helper to verify the analysis is really immutable.void TargetPassConfig::setOpt(bool &Opt, bool Val) {assert(!Initialized && "PassConfig is immutable");Opt = Val;}void TargetPassConfig::substitutePass(AnalysisID StandardID,IdentifyingPassPtr TargetID) {Impl->TargetPasses[StandardID] = TargetID;}IdentifyingPassPtr TargetPassConfig::getPassSubstitution(AnalysisID ID) const {DenseMap<AnalysisID, IdentifyingPassPtr>::const_iteratorI = Impl->TargetPasses.find(ID);if (I == Impl->TargetPasses.end())return ID;return I->second;}bool TargetPassConfig::isPassSubstitutedOrOverridden(AnalysisID ID) const {IdentifyingPassPtr TargetID = getPassSubstitution(ID);IdentifyingPassPtr FinalPtr = overridePass(ID, TargetID);return !FinalPtr.isValid() || FinalPtr.isInstance() ||FinalPtr.getID() != ID;}/// Add a pass to the PassManager if that pass is supposed to be run. If the/// Started/Stopped flags indicate either that the compilation should start at/// a later pass or that it should stop after an earlier pass, then do not add/// the pass. Finally, compare the current pass against the StartAfter/// and StopAfter options and change the Started/Stopped flags accordingly.void TargetPassConfig::addPass(Pass *P) {assert(!Initialized && "PassConfig is immutable");// Cache the Pass ID here in case the pass manager finds this pass is// redundant with ones already scheduled / available, and deletes it.// Fundamentally, once we add the pass to the manager, we no longer own it// and shouldn't reference it.AnalysisID PassID = P->getPassID();if (StartBefore == PassID && StartBeforeCount++ == StartBeforeInstanceNum)Started = true;if (StopBefore == PassID && StopBeforeCount++ == StopBeforeInstanceNum)Stopped = true;if (Started && !Stopped) {if (AddingMachinePasses) {// Construct banner message before PM->add() as that may delete the pass.std::string Banner =std::string("After ") + std::string(P->getPassName());addMachinePrePasses();PM->add(P);addMachinePostPasses(Banner);} else {PM->add(P);}// Add the passes after the pass P if there is any.for (const auto &IP : Impl->InsertedPasses)if (IP.TargetPassID == PassID)addPass(IP.getInsertedPass());} else {delete P;}if (StopAfter == PassID && StopAfterCount++ == StopAfterInstanceNum)Stopped = true;if (StartAfter == PassID && StartAfterCount++ == StartAfterInstanceNum)Started = true;if (Stopped && !Started)report_fatal_error("Cannot stop compilation after pass that is not run");}/// Add a CodeGen pass at this point in the pipeline after checking for target/// and command line overrides.////// addPass cannot return a pointer to the pass instance because is internal the/// PassManager and the instance we create here may already be freed.AnalysisID TargetPassConfig::addPass(AnalysisID PassID) {IdentifyingPassPtr TargetID = getPassSubstitution(PassID);IdentifyingPassPtr FinalPtr = overridePass(PassID, TargetID);if (!FinalPtr.isValid())return nullptr;Pass *P;if (FinalPtr.isInstance())P = FinalPtr.getInstance();else {P = Pass::createPass(FinalPtr.getID());if (!P)llvm_unreachable("Pass ID not registered");}AnalysisID FinalID = P->getPassID();addPass(P); // Ends the lifetime of P.return FinalID;}void TargetPassConfig::printAndVerify(const std::string &Banner) {addPrintPass(Banner);addVerifyPass(Banner);}void TargetPassConfig::addPrintPass(const std::string &Banner) {if (PrintAfterISel)PM->add(createMachineFunctionPrinterPass(dbgs(), Banner));}void TargetPassConfig::addVerifyPass(const std::string &Banner) {bool Verify = VerifyMachineCode == cl::BOU_TRUE;#ifdef EXPENSIVE_CHECKSif (VerifyMachineCode == cl::BOU_UNSET)Verify = TM->isMachineVerifierClean();#endifif (Verify)PM->add(createMachineVerifierPass(Banner));}void TargetPassConfig::addDebugifyPass() {PM->add(createDebugifyMachineModulePass());}void TargetPassConfig::addStripDebugPass() {PM->add(createStripDebugMachineModulePass(/*OnlyDebugified=*/true));}void TargetPassConfig::addCheckDebugPass() {PM->add(createCheckDebugMachineModulePass());}void TargetPassConfig::addMachinePrePasses(bool AllowDebugify) {if (AllowDebugify && DebugifyIsSafe &&(DebugifyAndStripAll == cl::BOU_TRUE ||DebugifyCheckAndStripAll == cl::BOU_TRUE))addDebugifyPass();}void TargetPassConfig::addMachinePostPasses(const std::string &Banner) {if (DebugifyIsSafe) {if (DebugifyCheckAndStripAll == cl::BOU_TRUE) {addCheckDebugPass();addStripDebugPass();} else if (DebugifyAndStripAll == cl::BOU_TRUE)addStripDebugPass();}addVerifyPass(Banner);}/// Add common target configurable passes that perform LLVM IR to IR transforms/// following machine independent optimization.void TargetPassConfig::addIRPasses() {// Before running any passes, run the verifier to determine if the input// coming from the front-end and/or optimizer is valid.if (!DisableVerify)addPass(createVerifierPass());if (getOptLevel() != CodeGenOpt::None) {switch (UseCFLAA) {case CFLAAType::Steensgaard:addPass(createCFLSteensAAWrapperPass());break;case CFLAAType::Andersen:addPass(createCFLAndersAAWrapperPass());break;case CFLAAType::Both:addPass(createCFLAndersAAWrapperPass());addPass(createCFLSteensAAWrapperPass());break;default:break;}// Basic AliasAnalysis support.// Add TypeBasedAliasAnalysis before BasicAliasAnalysis so that// BasicAliasAnalysis wins if they disagree. This is intended to help// support "obvious" type-punning idioms.addPass(createTypeBasedAAWrapperPass());addPass(createScopedNoAliasAAWrapperPass());addPass(createBasicAAWrapperPass());// Run loop strength reduction before anything else.if (!DisableLSR) {addPass(createCanonicalizeFreezeInLoopsPass());addPass(createLoopStrengthReducePass());if (PrintLSR)addPass(createPrintFunctionPass(dbgs(),"\n\n*** Code after LSR ***\n"));}// The MergeICmpsPass tries to create memcmp calls by grouping sequences of// loads and compares. ExpandMemCmpPass then tries to expand those calls// into optimally-sized loads and compares. The transforms are enabled by a// target lowering hook.if (!DisableMergeICmps)addPass(createMergeICmpsLegacyPass());addPass(createExpandMemCmpPass());}// Run GC lowering passes for builtin collectors// TODO: add a pass insertion point hereaddPass(&GCLoweringID);addPass(&ShadowStackGCLoweringID);addPass(createLowerConstantIntrinsicsPass());// For MachO, lower @llvm.global_dtors into @llvm_global_ctors with// __cxa_atexit() calls to avoid emitting the deprecated __mod_term_func.if (TM->getTargetTriple().isOSBinFormatMachO() &&TM->Options.LowerGlobalDtorsViaCxaAtExit)addPass(createLowerGlobalDtorsLegacyPass());// Make sure that no unreachable blocks are instruction selected.addPass(createUnreachableBlockEliminationPass());// Prepare expensive constants for SelectionDAG.if (getOptLevel() != CodeGenOpt::None && !DisableConstantHoisting)addPass(createConstantHoistingPass());if (getOptLevel() != CodeGenOpt::None)addPass(createReplaceWithVeclibLegacyPass());if (getOptLevel() != CodeGenOpt::None && !DisablePartialLibcallInlining)addPass(createPartiallyInlineLibCallsPass());// Expand vector predication intrinsics into standard IR instructions.// This pass has to run before ScalarizeMaskedMemIntrin and ExpandReduction// passes since it emits those kinds of intrinsics.addPass(createExpandVectorPredicationPass());// Add scalarization of target's unsupported masked memory intrinsics pass.// the unsupported intrinsic will be replaced with a chain of basic blocks,// that stores/loads element one-by-one if the appropriate mask bit is set.addPass(createScalarizeMaskedMemIntrinLegacyPass());// Expand reduction intrinsics into shuffle sequences if the target wants to.// Allow disabling it for testing purposes.if (!DisableExpandReductions)addPass(createExpandReductionsPass());if (getOptLevel() != CodeGenOpt::None)addPass(createTLSVariableHoistPass());// Convert conditional moves to conditional jumps when profitable.if (getOptLevel() != CodeGenOpt::None && !DisableSelectOptimize)addPass(createSelectOptimizePass());}/// Turn exception handling constructs into something the code generators can/// handle.void TargetPassConfig::addPassesToHandleExceptions() {const MCAsmInfo *MCAI = TM->getMCAsmInfo();assert(MCAI && "No MCAsmInfo");switch (MCAI->getExceptionHandlingType()) {case ExceptionHandling::SjLj:// SjLj piggy-backs on dwarf for this bit. The cleanups done apply to both// Dwarf EH prepare needs to be run after SjLj prepare. Otherwise,// catch info can get misplaced when a selector ends up more than one block// removed from the parent invoke(s). This could happen when a landing// pad is shared by multiple invokes and is also a target of a normal// edge from elsewhere.addPass(createSjLjEHPreparePass(TM));LLVM_FALLTHROUGH;case ExceptionHandling::DwarfCFI:case ExceptionHandling::ARM:case ExceptionHandling::AIX:addPass(createDwarfEHPass(getOptLevel()));break;case ExceptionHandling::WinEH:// We support using both GCC-style and MSVC-style exceptions on Windows, so// add both preparation passes. Each pass will only actually run if it// recognizes the personality function.addPass(createWinEHPass());addPass(createDwarfEHPass(getOptLevel()));break;case ExceptionHandling::Wasm:// Wasm EH uses Windows EH instructions, but it does not need to demote PHIs// on catchpads and cleanuppads because it does not outline them into// funclets. Catchswitch blocks are not lowered in SelectionDAG, so we// should remove PHIs there.addPass(createWinEHPass(/*DemoteCatchSwitchPHIOnly=*/false));addPass(createWasmEHPass());break;case ExceptionHandling::None:addPass(createLowerInvokePass());// The lower invoke pass may create unreachable code. Remove it.addPass(createUnreachableBlockEliminationPass());break;}}/// Add pass to prepare the LLVM IR for code generation. This should be done/// before exception handling preparation passes.void TargetPassConfig::addCodeGenPrepare() {if (getOptLevel() != CodeGenOpt::None && !DisableCGP)addPass(createCodeGenPreparePass());}/// Add common passes that perform LLVM IR to IR transforms in preparation for/// instruction selection.void TargetPassConfig::addISelPrepare() {addPreISel();// Force codegen to run according to the callgraph.if (requiresCodeGenSCCOrder())addPass(new DummyCGSCCPass);// Add both the safe stack and the stack protection passes: each of them will// only protect functions that have corresponding attributes.addPass(createSafeStackPass());addPass(createStackProtectorPass());if (PrintISelInput)addPass(createPrintFunctionPass(dbgs(), "\n\n*** Final LLVM Code input to ISel ***\n"));// All passes which modify the LLVM IR are now complete; run the verifier// to ensure that the IR is valid.if (!DisableVerify)addPass(createVerifierPass());}bool TargetPassConfig::addCoreISelPasses() {// Enable FastISel with -fast-isel, but allow that to be overridden.TM->setO0WantsFastISel(EnableFastISelOption != cl::BOU_FALSE);// Determine an instruction selector.enum class SelectorType { SelectionDAG, FastISel, GlobalISel };SelectorType Selector;if (EnableFastISelOption == cl::BOU_TRUE)Selector = SelectorType::FastISel;else if (EnableGlobalISelOption == cl::BOU_TRUE ||(TM->Options.EnableGlobalISel &&EnableGlobalISelOption != cl::BOU_FALSE))Selector = SelectorType::GlobalISel;else if (TM->getOptLevel() == CodeGenOpt::None && TM->getO0WantsFastISel())Selector = SelectorType::FastISel;elseSelector = SelectorType::SelectionDAG;// Set consistently TM->Options.EnableFastISel and EnableGlobalISel.if (Selector == SelectorType::FastISel) {TM->setFastISel(true);TM->setGlobalISel(false);} else if (Selector == SelectorType::GlobalISel) {TM->setFastISel(false);TM->setGlobalISel(true);}// FIXME: Injecting into the DAGISel pipeline seems to cause issues with// analyses needing to be re-run. This can result in being unable to// schedule passes (particularly with 'Function Alias Analysis// Results'). It's not entirely clear why but AFAICT this seems to be// due to one FunctionPassManager not being able to use analyses from a// previous one. As we're injecting a ModulePass we break the usual// pass manager into two. GlobalISel with the fallback path disabled// and -run-pass seem to be unaffected. The majority of GlobalISel// testing uses -run-pass so this probably isn't too bad.SaveAndRestore<bool> SavedDebugifyIsSafe(DebugifyIsSafe);if (Selector != SelectorType::GlobalISel || !isGlobalISelAbortEnabled())DebugifyIsSafe = false;// Add instruction selector passes.if (Selector == SelectorType::GlobalISel) {SaveAndRestore<bool> SavedAddingMachinePasses(AddingMachinePasses, true);if (addIRTranslator())return true;addPreLegalizeMachineIR();if (addLegalizeMachineIR())return true;// Before running the register bank selector, ask the target if it// wants to run some passes.addPreRegBankSelect();if (addRegBankSelect())return true;addPreGlobalInstructionSelect();if (addGlobalInstructionSelect())return true;// Pass to reset the MachineFunction if the ISel failed.addPass(createResetMachineFunctionPass(reportDiagnosticWhenGlobalISelFallback(), isGlobalISelAbortEnabled()));// Provide a fallback path when we do not want to abort on// not-yet-supported input.if (!isGlobalISelAbortEnabled() && addInstSelector())return true;} else if (addInstSelector())return true;// Expand pseudo-instructions emitted by ISel. Don't run the verifier before// FinalizeISel.addPass(&FinalizeISelID);// Print the instruction selected machine code...printAndVerify("After Instruction Selection");return false;}bool TargetPassConfig::addISelPasses() {if (TM->useEmulatedTLS())addPass(createLowerEmuTLSPass());addPass(createPreISelIntrinsicLoweringPass());PM->add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));addIRPasses();addCodeGenPrepare();addPassesToHandleExceptions();addISelPrepare();return addCoreISelPasses();}/// -regalloc=... command line option.static FunctionPass *useDefaultRegisterAllocator() { return nullptr; }static cl::opt<RegisterRegAlloc::FunctionPassCtor, false,RegisterPassParser<RegisterRegAlloc>>RegAlloc("regalloc", cl::Hidden, cl::init(&useDefaultRegisterAllocator),cl::desc("Register allocator to use"));/// Add the complete set of target-independent postISel code generator passes.////// This can be read as the standard order of major LLVM CodeGen stages. Stages/// with nontrivial configuration or multiple passes are broken out below in/// add%Stage routines.////// Any TargetPassConfig::addXX routine may be overriden by the Target. The/// addPre/Post methods with empty header implementations allow injecting/// target-specific fixups just before or after major stages. Additionally,/// targets have the flexibility to change pass order within a stage by/// overriding default implementation of add%Stage routines below. Each/// technique has maintainability tradeoffs because alternate pass orders are/// not well supported. addPre/Post works better if the target pass is easily/// tied to a common pass. But if it has subtle dependencies on multiple passes,/// the target should override the stage instead.////// TODO: We could use a single addPre/Post(ID) hook to allow pass injection/// before/after any target-independent pass. But it's currently overkill.void TargetPassConfig::addMachinePasses() {AddingMachinePasses = true;// Add passes that optimize machine instructions in SSA form.if (getOptLevel() != CodeGenOpt::None) {addMachineSSAOptimization();} else {// If the target requests it, assign local variables to stack slots relative// to one another and simplify frame index references where possible.addPass(&LocalStackSlotAllocationID);}if (TM->Options.EnableIPRA)addPass(createRegUsageInfoPropPass());// Run pre-ra passes.addPreRegAlloc();// Debugifying the register allocator passes seems to provoke some// non-determinism that affects CodeGen and there doesn't seem to be a point// where it becomes safe again so stop debugifying here.DebugifyIsSafe = false;// Add a FSDiscriminator pass right before RA, so that we could get// more precise SampleFDO profile for RA.if (EnableFSDiscriminator) {addPass(createMIRAddFSDiscriminatorsPass(sampleprof::FSDiscriminatorPass::Pass1));const std::string ProfileFile = getFSProfileFile(TM);if (!ProfileFile.empty() && !DisableRAFSProfileLoader)addPass(createMIRProfileLoaderPass(ProfileFile, getFSRemappingFile(TM),sampleprof::FSDiscriminatorPass::Pass1));}// Run register allocation and passes that are tightly coupled with it,// including phi elimination and scheduling.if (getOptimizeRegAlloc())addOptimizedRegAlloc();elseaddFastRegAlloc();// Run post-ra passes.addPostRegAlloc();addPass(&RemoveRedundantDebugValuesID);addPass(&FixupStatepointCallerSavedID);// Insert prolog/epilog code. Eliminate abstract frame index references...if (getOptLevel() != CodeGenOpt::None) {addPass(&PostRAMachineSinkingID);addPass(&ShrinkWrapID);}// Prolog/Epilog inserter needs a TargetMachine to instantiate. But only// do so if it hasn't been disabled, substituted, or overridden.if (!isPassSubstitutedOrOverridden(&PrologEpilogCodeInserterID))addPass(createPrologEpilogInserterPass());/// Add passes that optimize machine instructions after register allocation.if (getOptLevel() != CodeGenOpt::None)addMachineLateOptimization();// Expand pseudo instructions before second scheduling pass.addPass(&ExpandPostRAPseudosID);// Run pre-sched2 passes.addPreSched2();if (EnableImplicitNullChecks)addPass(&ImplicitNullChecksID);// Second pass scheduler.// Let Target optionally insert this pass by itself at some other// point.if (getOptLevel() != CodeGenOpt::None &&!TM->targetSchedulesPostRAScheduling()) {if (MISchedPostRA)addPass(&PostMachineSchedulerID);elseaddPass(&PostRASchedulerID);}// GCif (addGCPasses()) {if (PrintGCInfo)addPass(createGCInfoPrinter(dbgs()));}// Basic block placement.if (getOptLevel() != CodeGenOpt::None)addBlockPlacement();// Insert before XRay Instrumentation.addPass(&FEntryInserterID);addPass(&XRayInstrumentationID);addPass(&PatchableFunctionID);if (EnableFSDiscriminator && !FSNoFinalDiscrim)// Add FS discriminators here so that all the instruction duplicates// in different BBs get their own discriminators. With this, we can "sum"// the SampleFDO counters instead of using MAX. This will improve the// SampleFDO profile quality.addPass(createMIRAddFSDiscriminatorsPass(sampleprof::FSDiscriminatorPass::PassLast));addPreEmitPass();if (TM->Options.EnableIPRA)// Collect register usage information and produce a register mask of// clobbered registers, to be used to optimize call sites.addPass(createRegUsageInfoCollector());// FIXME: Some backends are incompatible with running the verifier after// addPreEmitPass. Maybe only pass "false" here for those targets?addPass(&FuncletLayoutID);addPass(&StackMapLivenessID);addPass(&LiveDebugValuesID);if (TM->Options.EnableMachineOutliner && getOptLevel() != CodeGenOpt::None &&EnableMachineOutliner != RunOutliner::NeverOutline) {bool RunOnAllFunctions =(EnableMachineOutliner == RunOutliner::AlwaysOutline);bool AddOutliner =RunOnAllFunctions || TM->Options.SupportsDefaultOutlining;if (AddOutliner)addPass(createMachineOutlinerPass(RunOnAllFunctions));}// Machine function splitter uses the basic block sections feature. Both// cannot be enabled at the same time. Basic block sections takes precedence.// FIXME: In principle, BasicBlockSection::Labels and splitting can used// together. Update this check once we have addressed any issues.if (TM->getBBSectionsType() != llvm::BasicBlockSection::None) {if (TM->getBBSectionsType() == llvm::BasicBlockSection::List) {addPass(llvm::createBasicBlockSectionsProfileReaderPass(TM->getBBSectionsFuncListBuf()));}addPass(llvm::createBasicBlockSectionsPass());} else if (TM->Options.EnableMachineFunctionSplitter ||EnableMachineFunctionSplitter) {addPass(createMachineFunctionSplitterPass());}if (!DisableCFIFixup && TM->Options.EnableCFIFixup)addPass(createCFIFixup());// Add passes that directly emit MI after all other MI passes.addPreEmitPass2();AddingMachinePasses = false;}/// Add passes that optimize machine instructions in SSA form.void TargetPassConfig::addMachineSSAOptimization() {// Pre-ra tail duplication.addPass(&EarlyTailDuplicateID);// Optimize PHIs before DCE: removing dead PHI cycles may make more// instructions dead.addPass(&OptimizePHIsID);// This pass merges large allocas. StackSlotColoring is a different pass// which merges spill slots.addPass(&StackColoringID);// If the target requests it, assign local variables to stack slots relative// to one another and simplify frame index references where possible.addPass(&LocalStackSlotAllocationID);// With optimization, dead code should already be eliminated. However// there is one known exception: lowered code for arguments that are only// used by tail calls, where the tail calls reuse the incoming stack// arguments directly (see t11 in test/CodeGen/X86/sibcall.ll).addPass(&DeadMachineInstructionElimID);// Allow targets to insert passes that improve instruction level parallelism,// like if-conversion. Such passes will typically need dominator trees and// loop info, just like LICM and CSE below.addILPOpts();addPass(&EarlyMachineLICMID);addPass(&MachineCSEID);addPass(&MachineSinkingID);addPass(&PeepholeOptimizerID);// Clean-up the dead code that may have been generated by peephole// rewriting.addPass(&DeadMachineInstructionElimID);}//===---------------------------------------------------------------------===///// Register Allocation Pass Configuration//===---------------------------------------------------------------------===//bool TargetPassConfig::getOptimizeRegAlloc() const {switch (OptimizeRegAlloc) {case cl::BOU_UNSET: return getOptLevel() != CodeGenOpt::None;case cl::BOU_TRUE: return true;case cl::BOU_FALSE: return false;}llvm_unreachable("Invalid optimize-regalloc state");}/// A dummy default pass factory indicates whether the register allocator is/// overridden on the command line.static llvm::once_flag InitializeDefaultRegisterAllocatorFlag;static RegisterRegAllocdefaultRegAlloc("default","pick register allocator based on -O option",useDefaultRegisterAllocator);static void initializeDefaultRegisterAllocatorOnce() {if (!RegisterRegAlloc::getDefault())RegisterRegAlloc::setDefault(RegAlloc);}/// Instantiate the default register allocator pass for this target for either/// the optimized or unoptimized allocation path. This will be added to the pass/// manager by addFastRegAlloc in the unoptimized case or addOptimizedRegAlloc/// in the optimized case.////// A target that uses the standard regalloc pass order for fast or optimized/// allocation may still override this for per-target regalloc/// selection. But -regalloc=... always takes precedence.FunctionPass *TargetPassConfig::createTargetRegisterAllocator(bool Optimized) {if (Optimized)return createGreedyRegisterAllocator();elsereturn createFastRegisterAllocator();}/// Find and instantiate the register allocation pass requested by this target/// at the current optimization level. Different register allocators are/// defined as separate passes because they may require different analysis.////// This helper ensures that the regalloc= option is always available,/// even for targets that override the default allocator.////// FIXME: When MachinePassRegistry register pass IDs instead of function ptrs,/// this can be folded into addPass.FunctionPass *TargetPassConfig::createRegAllocPass(bool Optimized) {// Initialize the global default.llvm::call_once(InitializeDefaultRegisterAllocatorFlag,initializeDefaultRegisterAllocatorOnce);RegisterRegAlloc::FunctionPassCtor Ctor = RegisterRegAlloc::getDefault();if (Ctor != useDefaultRegisterAllocator)return Ctor();// With no -regalloc= override, ask the target for a regalloc pass.return createTargetRegisterAllocator(Optimized);}bool TargetPassConfig::isCustomizedRegAlloc() {return RegAlloc !=(RegisterRegAlloc::FunctionPassCtor)&useDefaultRegisterAllocator;}bool TargetPassConfig::addRegAssignAndRewriteFast() {if (RegAlloc != (RegisterRegAlloc::FunctionPassCtor)&useDefaultRegisterAllocator &&RegAlloc != (RegisterRegAlloc::FunctionPassCtor)&createFastRegisterAllocator)report_fatal_error("Must use fast (default) register allocator for unoptimized regalloc.");addPass(createRegAllocPass(false));// Allow targets to change the register assignments after// fast register allocation.addPostFastRegAllocRewrite();return true;}bool TargetPassConfig::addRegAssignAndRewriteOptimized() {// Add the selected register allocation pass.addPass(createRegAllocPass(true));// Allow targets to change the register assignments before rewriting.addPreRewrite();// Finally rewrite virtual registers.addPass(&VirtRegRewriterID);// Regalloc scoring for ML-driven eviction - noop except when learning a new// eviction policy.addPass(createRegAllocScoringPass());return true;}/// Return true if the default global register allocator is in use and/// has not be overriden on the command line with '-regalloc=...'bool TargetPassConfig::usingDefaultRegAlloc() const {return RegAlloc.getNumOccurrences() == 0;}/// Add the minimum set of target-independent passes that are required for/// register allocation. No coalescing or scheduling.void TargetPassConfig::addFastRegAlloc() {addPass(&PHIEliminationID);addPass(&TwoAddressInstructionPassID);addRegAssignAndRewriteFast();}/// Add standard target-independent passes that are tightly coupled with/// optimized register allocation, including coalescing, machine instruction/// scheduling, and register allocation itself.void TargetPassConfig::addOptimizedRegAlloc() {addPass(&DetectDeadLanesID);addPass(&ProcessImplicitDefsID);// LiveVariables currently requires pure SSA form.//// FIXME: Once TwoAddressInstruction pass no longer uses kill flags,// LiveVariables can be removed completely, and LiveIntervals can be directly// computed. (We still either need to regenerate kill flags after regalloc, or// preferably fix the scavenger to not depend on them).// FIXME: UnreachableMachineBlockElim is a dependant pass of LiveVariables.// When LiveVariables is removed this has to be removed/moved either.// Explicit addition of UnreachableMachineBlockElim allows stopping before or// after it with -stop-before/-stop-after.addPass(&UnreachableMachineBlockElimID);addPass(&LiveVariablesID);// Edge splitting is smarter with machine loop info.addPass(&MachineLoopInfoID);addPass(&PHIEliminationID);// Eventually, we want to run LiveIntervals before PHI elimination.if (EarlyLiveIntervals)addPass(&LiveIntervalsID);addPass(&TwoAddressInstructionPassID);addPass(&RegisterCoalescerID);// The machine scheduler may accidentally create disconnected components// when moving subregister definitions around, avoid this by splitting them to// separate vregs before. Splitting can also improve reg. allocation quality.addPass(&RenameIndependentSubregsID);// PreRA instruction scheduling.addPass(&MachineSchedulerID);if (addRegAssignAndRewriteOptimized()) {// Perform stack slot coloring and post-ra machine LICM.addPass(&StackSlotColoringID);// Allow targets to expand pseudo instructions depending on the choice of// registers before MachineCopyPropagation.addPostRewrite();// Copy propagate to forward register uses and try to eliminate COPYs that// were not coalesced.addPass(&MachineCopyPropagationID);// Run post-ra machine LICM to hoist reloads / remats.//// FIXME: can this move into MachineLateOptimization?addPass(&MachineLICMID);}}//===---------------------------------------------------------------------===///// Post RegAlloc Pass Configuration//===---------------------------------------------------------------------===///// Add passes that optimize machine instructions after register allocation.void TargetPassConfig::addMachineLateOptimization() {// Branch folding must be run after regalloc and prolog/epilog insertion.addPass(&BranchFolderPassID);// Tail duplication.// Note that duplicating tail just increases code size and degrades// performance for targets that require Structured Control Flow.// In addition it can also make CFG irreducible. Thus we disable it.if (!TM->requiresStructuredCFG())addPass(&TailDuplicateID);// Copy propagation.addPass(&MachineCopyPropagationID);}/// Add standard GC passes.bool TargetPassConfig::addGCPasses() {addPass(&GCMachineCodeAnalysisID);return true;}/// Add standard basic block placement passes.void TargetPassConfig::addBlockPlacement() {if (EnableFSDiscriminator) {addPass(createMIRAddFSDiscriminatorsPass(sampleprof::FSDiscriminatorPass::Pass2));const std::string ProfileFile = getFSProfileFile(TM);if (!ProfileFile.empty() && !DisableLayoutFSProfileLoader)addPass(createMIRProfileLoaderPass(ProfileFile, getFSRemappingFile(TM),sampleprof::FSDiscriminatorPass::Pass2));}if (addPass(&MachineBlockPlacementID)) {// Run a separate pass to collect block placement statistics.if (EnableBlockPlacementStats)addPass(&MachineBlockPlacementStatsID);}}//===---------------------------------------------------------------------===///// GlobalISel Configuration//===---------------------------------------------------------------------===//bool TargetPassConfig::isGlobalISelAbortEnabled() const {return TM->Options.GlobalISelAbort == GlobalISelAbortMode::Enable;}bool TargetPassConfig::reportDiagnosticWhenGlobalISelFallback() const {return TM->Options.GlobalISelAbort == GlobalISelAbortMode::DisableWithDiag;}bool TargetPassConfig::isGISelCSEEnabled() const {return true;}std::unique_ptr<CSEConfigBase> TargetPassConfig::getCSEConfig() const {return std::make_unique<CSEConfigBase>();}
//===-- CodeGen.cpp -------------------------------------------------------===////// 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////===----------------------------------------------------------------------===////// This file implements the common initialization routines for the// CodeGen library.////===----------------------------------------------------------------------===//#include "llvm-c/Initialization.h"#include "llvm/InitializePasses.h"#include "llvm/PassRegistry.h"using namespace llvm;/// initializeCodeGen - Initialize all passes linked into the CodeGen library.void llvm::initializeCodeGen(PassRegistry &Registry) {initializeAtomicExpandPass(Registry);initializeBasicBlockSectionsPass(Registry);initializeBranchFolderPassPass(Registry);initializeBranchRelaxationPass(Registry);initializeCFGuardLongjmpPass(Registry);initializeCFIFixupPass(Registry);initializeCFIInstrInserterPass(Registry);initializeCheckDebugMachineModulePass(Registry);initializeCodeGenPreparePass(Registry);initializeDeadMachineInstructionElimPass(Registry);initializeDebugifyMachineModulePass(Registry);initializeDetectDeadLanesPass(Registry);initializeDwarfEHPrepareLegacyPassPass(Registry);initializeEarlyIfConverterPass(Registry);initializeEarlyIfPredicatorPass(Registry);initializeEarlyMachineLICMPass(Registry);initializeEarlyTailDuplicatePass(Registry);initializeExpandMemCmpPassPass(Registry);initializeExpandPostRAPass(Registry);initializeFEntryInserterPass(Registry);initializeFinalizeISelPass(Registry);initializeFinalizeMachineBundlesPass(Registry);initializeFixupStatepointCallerSavedPass(Registry);initializeFuncletLayoutPass(Registry);initializeGCMachineCodeAnalysisPass(Registry);initializeGCModuleInfoPass(Registry);initializeHardwareLoopsPass(Registry);initializeIfConverterPass(Registry);initializeImplicitNullChecksPass(Registry);initializeIndirectBrExpandPassPass(Registry);initializeInterleavedLoadCombinePass(Registry);initializeInterleavedAccessPass(Registry);initializeJMCInstrumenterPass(Registry);initializeLiveDebugValuesPass(Registry);initializeLiveDebugVariablesPass(Registry);initializeLiveIntervalsPass(Registry);initializeLiveRangeShrinkPass(Registry);initializeLiveStacksPass(Registry);initializeLiveVariablesPass(Registry);initializeLocalStackSlotPassPass(Registry);initializeLowerGlobalDtorsLegacyPassPass(Registry);initializeLowerIntrinsicsPass(Registry);initializeMIRAddFSDiscriminatorsPass(Registry);initializeMIRCanonicalizerPass(Registry);initializeMIRNamerPass(Registry);initializeMIRProfileLoaderPassPass(Registry);initializeMachineBlockFrequencyInfoPass(Registry);initializeMachineBlockPlacementPass(Registry);initializeMachineBlockPlacementStatsPass(Registry);initializeMachineCSEPass(Registry);initializeMachineCombinerPass(Registry);initializeMachineCopyPropagationPass(Registry);initializeMachineCycleInfoPrinterPassPass(Registry);initializeMachineCycleInfoWrapperPassPass(Registry);initializeMachineDominatorTreePass(Registry);initializeMachineFunctionPrinterPassPass(Registry);initializeMachineLICMPass(Registry);initializeMachineLoopInfoPass(Registry);initializeMachineModuleInfoWrapperPassPass(Registry);initializeMachineOptimizationRemarkEmitterPassPass(Registry);initializeMachineOutlinerPass(Registry);initializeMachinePipelinerPass(Registry);initializeModuloScheduleTestPass(Registry);initializeMachinePostDominatorTreePass(Registry);initializeMachineRegionInfoPassPass(Registry);initializeMachineSchedulerPass(Registry);initializeMachineSinkingPass(Registry);initializeMachineVerifierPassPass(Registry);initializeOptimizePHIsPass(Registry);initializePEIPass(Registry);initializePHIEliminationPass(Registry);initializePatchableFunctionPass(Registry);initializePeepholeOptimizerPass(Registry);initializePostMachineSchedulerPass(Registry);initializePostRAHazardRecognizerPass(Registry);initializePostRAMachineSinkingPass(Registry);initializePostRASchedulerPass(Registry);initializePreISelIntrinsicLoweringLegacyPassPass(Registry);initializeProcessImplicitDefsPass(Registry);initializeRABasicPass(Registry);initializeRAGreedyPass(Registry);initializeRegAllocFastPass(Registry);initializeRegUsageInfoCollectorPass(Registry);initializeRegUsageInfoPropagationPass(Registry);initializeRegisterCoalescerPass(Registry);initializeRemoveRedundantDebugValuesPass(Registry);initializeRenameIndependentSubregsPass(Registry);initializeSafeStackLegacyPassPass(Registry);initializeSelectOptimizePass(Registry);initializeShadowStackGCLoweringPass(Registry);initializeShrinkWrapPass(Registry);initializeSjLjEHPreparePass(Registry);initializeSlotIndexesPass(Registry);initializeStackColoringPass(Registry);initializeStackMapLivenessPass(Registry);initializeStackProtectorPass(Registry);initializeStackSlotColoringPass(Registry);initializeStripDebugMachineModulePass(Registry);initializeTailDuplicatePass(Registry);initializeTargetPassConfigPass(Registry);initializeTwoAddressInstructionPassPass(Registry);initializeTypePromotionPass(Registry);initializeUnpackMachineBundlesPass(Registry);initializeUnreachableBlockElimLegacyPassPass(Registry);initializeUnreachableMachineBlockElimPass(Registry);initializeVirtRegMapPass(Registry);initializeVirtRegRewriterPass(Registry);initializeWasmEHPreparePass(Registry);initializeWinEHPreparePass(Registry);initializeXRayInstrumentationPass(Registry);}void LLVMInitializeCodeGen(LLVMPassRegistryRef R) {initializeCodeGen(*unwrap(R));}
//===- llvm/InitializePasses.h - Initialize All Passes ----------*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// This file contains the declarations for the pass initialization routines// for the entire LLVM project.////===----------------------------------------------------------------------===//#ifndef LLVM_INITIALIZEPASSES_H#define LLVM_INITIALIZEPASSES_Hnamespace llvm {class PassRegistry;/// Initialize all passes linked into the TransformUtils library.void initializeCore(PassRegistry&);/// Initialize all passes linked into the TransformUtils library.void initializeTransformUtils(PassRegistry&);/// Initialize all passes linked into the ScalarOpts library.void initializeScalarOpts(PassRegistry&);/// Initialize all passes linked into the ObjCARCOpts library.void initializeObjCARCOpts(PassRegistry&);/// Initialize all passes linked into the Vectorize library.void initializeVectorization(PassRegistry&);/// Initialize all passes linked into the InstCombine library.void initializeInstCombine(PassRegistry&);/// Initialize all passes linked into the AggressiveInstCombine library.void initializeAggressiveInstCombine(PassRegistry&);/// Initialize all passes linked into the IPO library.void initializeIPO(PassRegistry&);/// Initialize all passes linked into the Instrumentation library.void initializeInstrumentation(PassRegistry&);/// Initialize all passes linked into the Analysis library.void initializeAnalysis(PassRegistry&);/// Initialize all passes linked into the CodeGen library.void initializeCodeGen(PassRegistry&);/// Initialize all passes linked into the GlobalISel library.void initializeGlobalISel(PassRegistry&);/// Initialize all passes linked into the CodeGen library.void initializeTarget(PassRegistry&);void initializeAAEvalLegacyPassPass(PassRegistry&);void initializeAAResultsWrapperPassPass(PassRegistry&);void initializeADCELegacyPassPass(PassRegistry&);void initializeAddDiscriminatorsLegacyPassPass(PassRegistry&);void initializeAddFSDiscriminatorsPass(PassRegistry &);void initializeAggressiveInstCombinerLegacyPassPass(PassRegistry&);void initializeAliasSetPrinterPass(PassRegistry&);void initializeAlignmentFromAssumptionsPass(PassRegistry&);void initializeAlwaysInlinerLegacyPassPass(PassRegistry&);void initializeAssumeSimplifyPassLegacyPassPass(PassRegistry &);void initializeAssumeBuilderPassLegacyPassPass(PassRegistry &);void initializeAnnotation2MetadataLegacyPass(PassRegistry &);void initializeAnnotationRemarksLegacyPass(PassRegistry &);void initializeOpenMPOptCGSCCLegacyPassPass(PassRegistry &);void initializeAssumptionCacheTrackerPass(PassRegistry&);void initializeAtomicExpandPass(PassRegistry&);void initializeAttributorLegacyPassPass(PassRegistry&);void initializeAttributorCGSCCLegacyPassPass(PassRegistry &);void initializeBasicBlockSectionsProfileReaderPass(PassRegistry &);void initializeBasicBlockSectionsPass(PassRegistry &);void initializeBDCELegacyPassPass(PassRegistry&);void initializeBarrierNoopPass(PassRegistry&);void initializeBasicAAWrapperPassPass(PassRegistry&);void initializeBlockExtractorLegacyPassPass(PassRegistry &);void initializeBlockFrequencyInfoWrapperPassPass(PassRegistry&);void initializeBoundsCheckingLegacyPassPass(PassRegistry&);void initializeBranchFolderPassPass(PassRegistry&);void initializeBranchProbabilityInfoWrapperPassPass(PassRegistry&);void initializeBranchRelaxationPass(PassRegistry&);void initializeBreakCriticalEdgesPass(PassRegistry&);void initializeBreakFalseDepsPass(PassRegistry&);void initializeCanonicalizeFreezeInLoopsPass(PassRegistry &);void initializeCFGOnlyPrinterLegacyPassPass(PassRegistry&);void initializeCFGOnlyViewerLegacyPassPass(PassRegistry&);void initializeCFGPrinterLegacyPassPass(PassRegistry&);void initializeCFGSimplifyPassPass(PassRegistry&);void initializeCFGuardPass(PassRegistry&);void initializeCFGuardLongjmpPass(PassRegistry&);void initializeCFGViewerLegacyPassPass(PassRegistry&);void initializeCFIFixupPass(PassRegistry&);void initializeCFIInstrInserterPass(PassRegistry&);void initializeCFLAndersAAWrapperPassPass(PassRegistry&);void initializeCFLSteensAAWrapperPassPass(PassRegistry&);void initializeCallGraphDOTPrinterPass(PassRegistry&);void initializeCallGraphPrinterLegacyPassPass(PassRegistry&);void initializeCallGraphViewerPass(PassRegistry&);void initializeCallGraphWrapperPassPass(PassRegistry&);void initializeCallSiteSplittingLegacyPassPass(PassRegistry&);void initializeCalledValuePropagationLegacyPassPass(PassRegistry &);void initializeCheckDebugMachineModulePass(PassRegistry &);void initializeCodeGenPreparePass(PassRegistry&);void initializeConstantHoistingLegacyPassPass(PassRegistry&);void initializeConstantMergeLegacyPassPass(PassRegistry&);void initializeConstraintEliminationPass(PassRegistry &);void initializeCorrelatedValuePropagationPass(PassRegistry&);void initializeCostModelAnalysisPass(PassRegistry&);void initializeCrossDSOCFIPass(PassRegistry&);void initializeCycleInfoWrapperPassPass(PassRegistry &);void initializeDAEPass(PassRegistry&);void initializeDAHPass(PassRegistry&);void initializeDCELegacyPassPass(PassRegistry&);void initializeDFAJumpThreadingLegacyPassPass(PassRegistry &);void initializeDSELegacyPassPass(PassRegistry&);void initializeDataFlowSanitizerLegacyPassPass(PassRegistry &);void initializeDeadMachineInstructionElimPass(PassRegistry&);void initializeDebugifyMachineModulePass(PassRegistry &);void initializeDelinearizationPass(PassRegistry&);void initializeDemandedBitsWrapperPassPass(PassRegistry&);void initializeDependenceAnalysisPass(PassRegistry&);void initializeDependenceAnalysisWrapperPassPass(PassRegistry&);void initializeDetectDeadLanesPass(PassRegistry&);void initializeDivRemPairsLegacyPassPass(PassRegistry&);void initializeDomOnlyPrinterWrapperPassPass(PassRegistry &);void initializeDomOnlyViewerWrapperPassPass(PassRegistry &);void initializeDomPrinterWrapperPassPass(PassRegistry &);void initializeDomViewerWrapperPassPass(PassRegistry &);void initializeDominanceFrontierWrapperPassPass(PassRegistry&);void initializeDominatorTreeWrapperPassPass(PassRegistry&);void initializeDwarfEHPrepareLegacyPassPass(PassRegistry &);void initializeEarlyCSELegacyPassPass(PassRegistry&);void initializeEarlyCSEMemSSALegacyPassPass(PassRegistry&);void initializeEarlyIfConverterPass(PassRegistry&);void initializeEarlyIfPredicatorPass(PassRegistry &);void initializeEarlyMachineLICMPass(PassRegistry&);void initializeEarlyTailDuplicatePass(PassRegistry&);void initializeEdgeBundlesPass(PassRegistry&);void initializeEHContGuardCatchretPass(PassRegistry &);void initializeEliminateAvailableExternallyLegacyPassPass(PassRegistry&);void initializeExpandMemCmpPassPass(PassRegistry&);void initializeExpandPostRAPass(PassRegistry&);void initializeExpandReductionsPass(PassRegistry&);void initializeExpandVectorPredicationPass(PassRegistry &);void initializeMakeGuardsExplicitLegacyPassPass(PassRegistry&);void initializeExternalAAWrapperPassPass(PassRegistry&);void initializeFEntryInserterPass(PassRegistry&);void initializeFinalizeISelPass(PassRegistry&);void initializeFinalizeMachineBundlesPass(PassRegistry&);void initializeFixIrreduciblePass(PassRegistry &);void initializeFixupStatepointCallerSavedPass(PassRegistry&);void initializeFlattenCFGLegacyPassPass(PassRegistry &);void initializeFloat2IntLegacyPassPass(PassRegistry&);void initializeForceFunctionAttrsLegacyPassPass(PassRegistry&);void initializeForwardControlFlowIntegrityPass(PassRegistry&);void initializeFuncletLayoutPass(PassRegistry&);void initializeFunctionSpecializationLegacyPassPass(PassRegistry &);void initializeGCMachineCodeAnalysisPass(PassRegistry&);void initializeGCModuleInfoPass(PassRegistry&);void initializeGVNHoistLegacyPassPass(PassRegistry&);void initializeGVNLegacyPassPass(PassRegistry&);void initializeGVNSinkLegacyPassPass(PassRegistry&);void initializeGlobalDCELegacyPassPass(PassRegistry&);void initializeGlobalMergePass(PassRegistry&);void initializeGlobalOptLegacyPassPass(PassRegistry&);void initializeGlobalSplitPass(PassRegistry&);void initializeGlobalsAAWrapperPassPass(PassRegistry&);void initializeGuardWideningLegacyPassPass(PassRegistry&);void initializeHardwareLoopsPass(PassRegistry&);void initializeMIRProfileLoaderPassPass(PassRegistry &);void initializeMemProfilerLegacyPassPass(PassRegistry &);void initializeHotColdSplittingLegacyPassPass(PassRegistry&);void initializeIPSCCPLegacyPassPass(PassRegistry&);void initializeIRCELegacyPassPass(PassRegistry&);void initializeIROutlinerLegacyPassPass(PassRegistry&);void initializeIRSimilarityIdentifierWrapperPassPass(PassRegistry&);void initializeIRTranslatorPass(PassRegistry&);void initializeIVUsersWrapperPassPass(PassRegistry&);void initializeIfConverterPass(PassRegistry&);void initializeImmutableModuleSummaryIndexWrapperPassPass(PassRegistry&);void initializeImplicitNullChecksPass(PassRegistry&);void initializeIndVarSimplifyLegacyPassPass(PassRegistry&);void initializeIndirectBrExpandPassPass(PassRegistry&);void initializeInferAddressSpacesPass(PassRegistry&);void initializeInferFunctionAttrsLegacyPassPass(PassRegistry&);void initializeInjectTLIMappingsLegacyPass(PassRegistry &);void initializeInlineCostAnalysisPass(PassRegistry&);void initializeInstCountLegacyPassPass(PassRegistry &);void initializeInstNamerPass(PassRegistry&);void initializeInstSimplifyLegacyPassPass(PassRegistry &);void initializeInstructionCombiningPassPass(PassRegistry&);void initializeInstructionSelectPass(PassRegistry&);void initializeInterleavedAccessPass(PassRegistry&);void initializeInterleavedLoadCombinePass(PassRegistry &);void initializeInternalizeLegacyPassPass(PassRegistry&);void initializeIntervalPartitionPass(PassRegistry&);void initializeJMCInstrumenterPass(PassRegistry&);void initializeJumpThreadingPass(PassRegistry&);void initializeLCSSAVerificationPassPass(PassRegistry&);void initializeLCSSAWrapperPassPass(PassRegistry&);void initializeLazyBlockFrequencyInfoPassPass(PassRegistry&);void initializeLazyBranchProbabilityInfoPassPass(PassRegistry&);void initializeLazyMachineBlockFrequencyInfoPassPass(PassRegistry&);void initializeLazyValueInfoPrinterPass(PassRegistry&);void initializeLazyValueInfoWrapperPassPass(PassRegistry&);void initializeLegacyDivergenceAnalysisPass(PassRegistry&);void initializeLegacyLICMPassPass(PassRegistry&);void initializeLegacyLoopSinkPassPass(PassRegistry&);void initializeLegalizerPass(PassRegistry&);void initializeGISelCSEAnalysisWrapperPassPass(PassRegistry &);void initializeGISelKnownBitsAnalysisPass(PassRegistry &);void initializeLibCallsShrinkWrapLegacyPassPass(PassRegistry&);void initializeLintLegacyPassPass(PassRegistry &);void initializeLiveDebugValuesPass(PassRegistry&);void initializeLiveDebugVariablesPass(PassRegistry&);void initializeLiveIntervalsPass(PassRegistry&);void initializeLiveRangeShrinkPass(PassRegistry&);void initializeLiveRegMatrixPass(PassRegistry&);void initializeLiveStacksPass(PassRegistry&);void initializeLiveVariablesPass(PassRegistry &);void initializeLoadStoreOptPass(PassRegistry &);void initializeLoadStoreVectorizerLegacyPassPass(PassRegistry&);void initializeLoaderPassPass(PassRegistry&);void initializeLocalStackSlotPassPass(PassRegistry&);void initializeLocalizerPass(PassRegistry&);void initializeLoopAccessLegacyAnalysisPass(PassRegistry&);void initializeLoopDataPrefetchLegacyPassPass(PassRegistry&);void initializeLoopDeletionLegacyPassPass(PassRegistry&);void initializeLoopDistributeLegacyPass(PassRegistry&);void initializeLoopExtractorLegacyPassPass(PassRegistry &);void initializeLoopGuardWideningLegacyPassPass(PassRegistry&);void initializeLoopFuseLegacyPass(PassRegistry&);void initializeLoopIdiomRecognizeLegacyPassPass(PassRegistry&);void initializeLoopInfoWrapperPassPass(PassRegistry&);void initializeLoopInstSimplifyLegacyPassPass(PassRegistry&);void initializeLoopInterchangeLegacyPassPass(PassRegistry &);void initializeLoopFlattenLegacyPassPass(PassRegistry&);void initializeLoopLoadEliminationPass(PassRegistry&);void initializeLoopPassPass(PassRegistry&);void initializeLoopPredicationLegacyPassPass(PassRegistry&);void initializeLoopRerollLegacyPassPass(PassRegistry &);void initializeLoopRotateLegacyPassPass(PassRegistry&);void initializeLoopSimplifyCFGLegacyPassPass(PassRegistry&);void initializeLoopSimplifyPass(PassRegistry&);void initializeLoopStrengthReducePass(PassRegistry&);void initializeLoopUnrollAndJamPass(PassRegistry&);void initializeLoopUnrollPass(PassRegistry&);void initializeLoopUnswitchPass(PassRegistry&);void initializeLoopVectorizePass(PassRegistry&);void initializeLoopVersioningLICMLegacyPassPass(PassRegistry &);void initializeLoopVersioningLegacyPassPass(PassRegistry &);void initializeLowerAtomicLegacyPassPass(PassRegistry&);void initializeLowerConstantIntrinsicsPass(PassRegistry&);void initializeLowerEmuTLSPass(PassRegistry&);void initializeLowerExpectIntrinsicPass(PassRegistry&);void initializeLowerGlobalDtorsLegacyPassPass(PassRegistry &);void initializeLowerGuardIntrinsicLegacyPassPass(PassRegistry&);void initializeLowerWidenableConditionLegacyPassPass(PassRegistry&);void initializeLowerIntrinsicsPass(PassRegistry&);void initializeLowerInvokeLegacyPassPass(PassRegistry&);void initializeLowerSwitchLegacyPassPass(PassRegistry &);void initializeLowerMatrixIntrinsicsLegacyPassPass(PassRegistry &);void initializeLowerMatrixIntrinsicsMinimalLegacyPassPass(PassRegistry &);void initializeMIRAddFSDiscriminatorsPass(PassRegistry &);void initializeMIRCanonicalizerPass(PassRegistry &);void initializeMIRNamerPass(PassRegistry &);void initializeMIRPrintingPassPass(PassRegistry&);void initializeMachineBlockFrequencyInfoPass(PassRegistry&);void initializeMachineBlockPlacementPass(PassRegistry&);void initializeMachineBlockPlacementStatsPass(PassRegistry&);void initializeMachineBranchProbabilityInfoPass(PassRegistry&);void initializeMachineCSEPass(PassRegistry&);void initializeMachineCombinerPass(PassRegistry&);void initializeMachineCopyPropagationPass(PassRegistry&);void initializeMachineCycleInfoPrinterPassPass(PassRegistry &);void initializeMachineCycleInfoWrapperPassPass(PassRegistry &);void initializeMachineDominanceFrontierPass(PassRegistry&);void initializeMachineDominatorTreePass(PassRegistry&);void initializeMachineFunctionPrinterPassPass(PassRegistry&);void initializeMachineFunctionSplitterPass(PassRegistry &);void initializeMachineLICMPass(PassRegistry&);void initializeMachineLoopInfoPass(PassRegistry&);void initializeMachineModuleInfoWrapperPassPass(PassRegistry &);void initializeMachineOptimizationRemarkEmitterPassPass(PassRegistry&);void initializeMachineOutlinerPass(PassRegistry&);void initializeMachinePipelinerPass(PassRegistry&);void initializeMachinePostDominatorTreePass(PassRegistry&);void initializeMachineRegionInfoPassPass(PassRegistry&);void initializeMachineSchedulerPass(PassRegistry&);void initializeMachineSinkingPass(PassRegistry&);void initializeMachineTraceMetricsPass(PassRegistry&);void initializeMachineVerifierPassPass(PassRegistry&);void initializeMemCpyOptLegacyPassPass(PassRegistry&);void initializeMemDepPrinterPass(PassRegistry&);void initializeMemDerefPrinterPass(PassRegistry&);void initializeMemoryDependenceWrapperPassPass(PassRegistry&);void initializeMemorySSAPrinterLegacyPassPass(PassRegistry&);void initializeMemorySSAWrapperPassPass(PassRegistry&);void initializeMergeFunctionsLegacyPassPass(PassRegistry&);void initializeMergeICmpsLegacyPassPass(PassRegistry &);void initializeMergedLoadStoreMotionLegacyPassPass(PassRegistry&);void initializeMetaRenamerPass(PassRegistry&);void initializeModuleDebugInfoLegacyPrinterPass(PassRegistry &);void initializeModuleMemProfilerLegacyPassPass(PassRegistry &);void initializeModuleSummaryIndexWrapperPassPass(PassRegistry&);void initializeModuloScheduleTestPass(PassRegistry&);void initializeMustExecutePrinterPass(PassRegistry&);void initializeMustBeExecutedContextPrinterPass(PassRegistry&);void initializeNaryReassociateLegacyPassPass(PassRegistry&);void initializeNewGVNLegacyPassPass(PassRegistry&);void initializeObjCARCAAWrapperPassPass(PassRegistry&);void initializeObjCARCAPElimPass(PassRegistry&);void initializeObjCARCContractLegacyPassPass(PassRegistry &);void initializeObjCARCExpandPass(PassRegistry&);void initializeObjCARCOptLegacyPassPass(PassRegistry &);void initializeOptimizationRemarkEmitterWrapperPassPass(PassRegistry&);void initializeOptimizePHIsPass(PassRegistry&);void initializePAEvalPass(PassRegistry&);void initializePEIPass(PassRegistry&);void initializePHIEliminationPass(PassRegistry&);void initializePartialInlinerLegacyPassPass(PassRegistry&);void initializePartiallyInlineLibCallsLegacyPassPass(PassRegistry&);void initializePatchableFunctionPass(PassRegistry&);void initializePeepholeOptimizerPass(PassRegistry&);void initializePhiValuesWrapperPassPass(PassRegistry&);void initializePhysicalRegisterUsageInfoPass(PassRegistry&);void initializePlaceBackedgeSafepointsImplPass(PassRegistry&);void initializePlaceSafepointsPass(PassRegistry&);void initializePostDomOnlyPrinterWrapperPassPass(PassRegistry &);void initializePostDomOnlyViewerWrapperPassPass(PassRegistry &);void initializePostDomPrinterWrapperPassPass(PassRegistry &);void initializePostDomViewerWrapperPassPass(PassRegistry &);void initializePostDominatorTreeWrapperPassPass(PassRegistry&);void initializePostMachineSchedulerPass(PassRegistry&);void initializePostOrderFunctionAttrsLegacyPassPass(PassRegistry&);void initializePostRAHazardRecognizerPass(PassRegistry&);void initializePostRAMachineSinkingPass(PassRegistry&);void initializePostRASchedulerPass(PassRegistry&);void initializePreISelIntrinsicLoweringLegacyPassPass(PassRegistry&);void initializePredicateInfoPrinterLegacyPassPass(PassRegistry&);void initializePrintFunctionPassWrapperPass(PassRegistry&);void initializePrintModulePassWrapperPass(PassRegistry&);void initializeProcessImplicitDefsPass(PassRegistry&);void initializeProfileSummaryInfoWrapperPassPass(PassRegistry&);void initializePromoteLegacyPassPass(PassRegistry&);void initializePruneEHPass(PassRegistry&);void initializeRABasicPass(PassRegistry&);void initializePseudoProbeInserterPass(PassRegistry &);void initializeRAGreedyPass(PassRegistry&);void initializeReachingDefAnalysisPass(PassRegistry&);void initializeReassociateLegacyPassPass(PassRegistry&);void initializeRedundantDbgInstEliminationPass(PassRegistry&);void initializeRegAllocEvictionAdvisorAnalysisPass(PassRegistry &);void initializeRegAllocFastPass(PassRegistry&);void initializeRegAllocScoringPass(PassRegistry &);void initializeRegBankSelectPass(PassRegistry&);void initializeRegToMemLegacyPass(PassRegistry&);void initializeRegUsageInfoCollectorPass(PassRegistry&);void initializeRegUsageInfoPropagationPass(PassRegistry&);void initializeRegionInfoPassPass(PassRegistry&);void initializeRegionOnlyPrinterPass(PassRegistry&);void initializeRegionOnlyViewerPass(PassRegistry&);void initializeRegionPrinterPass(PassRegistry&);void initializeRegionViewerPass(PassRegistry&);void initializeRegisterCoalescerPass(PassRegistry&);void initializeRemoveRedundantDebugValuesPass(PassRegistry&);void initializeRenameIndependentSubregsPass(PassRegistry&);void initializeReplaceWithVeclibLegacyPass(PassRegistry &);void initializeResetMachineFunctionPass(PassRegistry&);void initializeReversePostOrderFunctionAttrsLegacyPassPass(PassRegistry&);void initializeRewriteStatepointsForGCLegacyPassPass(PassRegistry &);void initializeRewriteSymbolsLegacyPassPass(PassRegistry&);void initializeSCCPLegacyPassPass(PassRegistry&);void initializeSCEVAAWrapperPassPass(PassRegistry&);void initializeSLPVectorizerPass(PassRegistry&);void initializeSROALegacyPassPass(PassRegistry&);void initializeSafeStackLegacyPassPass(PassRegistry&);void initializeSafepointIRVerifierPass(PassRegistry&);void initializeSelectOptimizePass(PassRegistry &);void initializeScalarEvolutionWrapperPassPass(PassRegistry&);void initializeScalarizeMaskedMemIntrinLegacyPassPass(PassRegistry &);void initializeScalarizerLegacyPassPass(PassRegistry&);void initializeScavengerTestPass(PassRegistry&);void initializeScopedNoAliasAAWrapperPassPass(PassRegistry&);void initializeSeparateConstOffsetFromGEPLegacyPassPass(PassRegistry &);void initializeShadowStackGCLoweringPass(PassRegistry&);void initializeShrinkWrapPass(PassRegistry&);void initializeSimpleInlinerPass(PassRegistry&);void initializeSimpleLoopUnswitchLegacyPassPass(PassRegistry&);void initializeSingleLoopExtractorPass(PassRegistry&);void initializeSinkingLegacyPassPass(PassRegistry&);void initializeSjLjEHPreparePass(PassRegistry&);void initializeSlotIndexesPass(PassRegistry&);void initializeSpeculativeExecutionLegacyPassPass(PassRegistry&);void initializeSpillPlacementPass(PassRegistry&);void initializeStackColoringPass(PassRegistry&);void initializeStackMapLivenessPass(PassRegistry&);void initializeStackProtectorPass(PassRegistry&);void initializeStackSafetyGlobalInfoWrapperPassPass(PassRegistry &);void initializeStackSafetyInfoWrapperPassPass(PassRegistry &);void initializeStackSlotColoringPass(PassRegistry&);void initializeStraightLineStrengthReduceLegacyPassPass(PassRegistry &);void initializeStripDeadDebugInfoPass(PassRegistry&);void initializeStripDeadPrototypesLegacyPassPass(PassRegistry&);void initializeStripDebugDeclarePass(PassRegistry&);void initializeStripDebugMachineModulePass(PassRegistry &);void initializeStripGCRelocatesLegacyPass(PassRegistry &);void initializeStripNonDebugSymbolsPass(PassRegistry&);void initializeStripNonLineTableDebugLegacyPassPass(PassRegistry &);void initializeStripSymbolsPass(PassRegistry&);void initializeStructurizeCFGLegacyPassPass(PassRegistry &);void initializeTailCallElimPass(PassRegistry&);void initializeTailDuplicatePass(PassRegistry&);void initializeTargetLibraryInfoWrapperPassPass(PassRegistry&);void initializeTargetPassConfigPass(PassRegistry&);void initializeTargetTransformInfoWrapperPassPass(PassRegistry&);void initializeTLSVariableHoistLegacyPassPass(PassRegistry &);void initializeTwoAddressInstructionPassPass(PassRegistry&);void initializeTypeBasedAAWrapperPassPass(PassRegistry&);void initializeTypePromotionPass(PassRegistry&);void initializeUnifyFunctionExitNodesLegacyPassPass(PassRegistry &);void initializeUnifyLoopExitsLegacyPassPass(PassRegistry &);void initializeUnpackMachineBundlesPass(PassRegistry&);void initializeUnreachableBlockElimLegacyPassPass(PassRegistry&);void initializeUnreachableMachineBlockElimPass(PassRegistry&);void initializeVectorCombineLegacyPassPass(PassRegistry&);void initializeVerifierLegacyPassPass(PassRegistry&);void initializeVirtRegMapPass(PassRegistry&);void initializeVirtRegRewriterPass(PassRegistry&);void initializeWarnMissedTransformationsLegacyPass(PassRegistry &);void initializeWasmEHPreparePass(PassRegistry&);void initializeWinEHPreparePass(PassRegistry&);void initializeWriteBitcodePassPass(PassRegistry&);void initializeWriteThinLTOBitcodePass(PassRegistry&);void initializeXRayInstrumentationPass(PassRegistry&);} // end namespace llvm#endif // LLVM_INITIALIZEPASSES_H
//===-- Passes.h - Target independent code generation passes ----*- C++ -*-===////// 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////===----------------------------------------------------------------------===////// This file defines interfaces to access the target independent code generation// passes provided by the LLVM backend.////===----------------------------------------------------------------------===//#ifndef LLVM_CODEGEN_PASSES_H#define LLVM_CODEGEN_PASSES_H#include "llvm/Support/CodeGen.h"#include "llvm/Support/Discriminator.h"#include "llvm/CodeGen/RegAllocCommon.h"#include <functional>#include <string>namespace llvm {class FunctionPass;class MachineFunction;class MachineFunctionPass;class ModulePass;class Pass;class TargetMachine;class raw_ostream;} // End llvm namespace// List of target independent CodeGen pass IDs.namespace llvm {/// AtomicExpandPass - At IR level this pass replace atomic instructions with/// __atomic_* library calls, or target specific instruction which implement the/// same semantics in a way which better fits the target backend.FunctionPass *createAtomicExpandPass();/// createUnreachableBlockEliminationPass - The LLVM code generator does not/// work well with unreachable basic blocks (what live ranges make sense for a/// block that cannot be reached?). As such, a code generator should either/// not instruction select unreachable blocks, or run this pass as its/// last LLVM modifying pass to clean up blocks that are not reachable from/// the entry block.FunctionPass *createUnreachableBlockEliminationPass();/// createBasicBlockSections Pass - This pass assigns sections to machine/// basic blocks and is enabled with -fbasic-block-sections.MachineFunctionPass *createBasicBlockSectionsPass();/// createMachineFunctionSplitterPass - This pass splits machine functions/// using profile information.MachineFunctionPass *createMachineFunctionSplitterPass();/// MachineFunctionPrinter pass - This pass prints out the machine function to/// the given stream as a debugging tool.MachineFunctionPass *createMachineFunctionPrinterPass(raw_ostream &OS,const std::string &Banner ="");/// MIRPrinting pass - this pass prints out the LLVM IR into the given stream/// using the MIR serialization format.MachineFunctionPass *createPrintMIRPass(raw_ostream &OS);/// This pass resets a MachineFunction when it has the FailedISel property/// as if it was just created./// If EmitFallbackDiag is true, the pass will emit a/// DiagnosticInfoISelFallback for every MachineFunction it resets./// If AbortOnFailedISel is true, abort compilation instead of resetting.MachineFunctionPass *createResetMachineFunctionPass(bool EmitFallbackDiag,bool AbortOnFailedISel);/// createCodeGenPreparePass - Transform the code to expose more pattern/// matching during instruction selection.FunctionPass *createCodeGenPreparePass();/// AtomicExpandID -- Lowers atomic operations in terms of either cmpxchg/// load-linked/store-conditional loops.extern char &AtomicExpandID;/// MachineLoopInfo - This pass is a loop analysis pass.extern char &MachineLoopInfoID;/// MachineDominators - This pass is a machine dominators analysis pass.extern char &MachineDominatorsID;/// MachineDominanaceFrontier - This pass is a machine dominators analysis.extern char &MachineDominanceFrontierID;/// MachineRegionInfo - This pass computes SESE regions for machine functions.extern char &MachineRegionInfoPassID;/// EdgeBundles analysis - Bundle machine CFG edges.extern char &EdgeBundlesID;/// LiveVariables pass - This pass computes the set of blocks in which each/// variable is life and sets machine operand kill flags.extern char &LiveVariablesID;/// PHIElimination - This pass eliminates machine instruction PHI nodes/// by inserting copy instructions. This destroys SSA information, but is the/// desired input for some register allocators. This pass is "required" by/// these register allocator like this: AU.addRequiredID(PHIEliminationID);extern char &PHIEliminationID;/// LiveIntervals - This analysis keeps track of the live ranges of virtual/// and physical registers.extern char &LiveIntervalsID;/// LiveStacks pass. An analysis keeping track of the liveness of stack slots.extern char &LiveStacksID;/// TwoAddressInstruction - This pass reduces two-address instructions to/// use two operands. This destroys SSA information but it is desired by/// register allocators.extern char &TwoAddressInstructionPassID;/// ProcessImpicitDefs pass - This pass removes IMPLICIT_DEFs.extern char &ProcessImplicitDefsID;/// RegisterCoalescer - This pass merges live ranges to eliminate copies.extern char &RegisterCoalescerID;/// MachineScheduler - This pass schedules machine instructions.extern char &MachineSchedulerID;/// PostMachineScheduler - This pass schedules machine instructions postRA.extern char &PostMachineSchedulerID;/// SpillPlacement analysis. Suggest optimal placement of spill code between/// basic blocks.extern char &SpillPlacementID;/// ShrinkWrap pass. Look for the best place to insert save and restore// instruction and update the MachineFunctionInfo with that information.extern char &ShrinkWrapID;/// LiveRangeShrink pass. Move instruction close to its definition to shrink/// the definition's live range.extern char &LiveRangeShrinkID;/// Greedy register allocator.extern char &RAGreedyID;/// Basic register allocator.extern char &RABasicID;/// VirtRegRewriter pass. Rewrite virtual registers to physical registers as/// assigned in VirtRegMap.extern char &VirtRegRewriterID;FunctionPass *createVirtRegRewriter(bool ClearVirtRegs = true);/// UnreachableMachineBlockElimination - This pass removes unreachable/// machine basic blocks.extern char &UnreachableMachineBlockElimID;/// DeadMachineInstructionElim - This pass removes dead machine instructions.extern char &DeadMachineInstructionElimID;/// This pass adds dead/undef flags after analyzing subregister lanes.extern char &DetectDeadLanesID;/// This pass perform post-ra machine sink for COPY instructions.extern char &PostRAMachineSinkingID;/// This pass adds flow sensitive discriminators.extern char &MIRAddFSDiscriminatorsID;/// This pass reads flow sensitive profile.extern char &MIRProfileLoaderPassID;/// FastRegisterAllocation Pass - This pass register allocates as fast as/// possible. It is best suited for debug code where live ranges are short.///FunctionPass *createFastRegisterAllocator();FunctionPass *createFastRegisterAllocator(RegClassFilterFunc F,bool ClearVirtRegs);/// BasicRegisterAllocation Pass - This pass implements a degenerate global/// register allocator using the basic regalloc framework.///FunctionPass *createBasicRegisterAllocator();FunctionPass *createBasicRegisterAllocator(RegClassFilterFunc F);/// Greedy register allocation pass - This pass implements a global register/// allocator for optimized builds.///FunctionPass *createGreedyRegisterAllocator();FunctionPass *createGreedyRegisterAllocator(RegClassFilterFunc F);/// PBQPRegisterAllocation Pass - This pass implements the Partitioned Boolean/// Quadratic Prograaming (PBQP) based register allocator.///FunctionPass *createDefaultPBQPRegisterAllocator();/// PrologEpilogCodeInserter - This pass inserts prolog and epilog code,/// and eliminates abstract frame references.extern char &PrologEpilogCodeInserterID;MachineFunctionPass *createPrologEpilogInserterPass();/// ExpandPostRAPseudos - This pass expands pseudo instructions after/// register allocation.extern char &ExpandPostRAPseudosID;/// PostRAHazardRecognizer - This pass runs the post-ra hazard/// recognizer.extern char &PostRAHazardRecognizerID;/// PostRAScheduler - This pass performs post register allocation/// scheduling.extern char &PostRASchedulerID;/// BranchFolding - This pass performs machine code CFG based/// optimizations to delete branches to branches, eliminate branches to/// successor blocks (creating fall throughs), and eliminating branches over/// branches.extern char &BranchFolderPassID;/// BranchRelaxation - This pass replaces branches that need to jump further/// than is supported by a branch instruction.extern char &BranchRelaxationPassID;/// MachineFunctionPrinterPass - This pass prints out MachineInstr's.extern char &MachineFunctionPrinterPassID;/// MIRPrintingPass - this pass prints out the LLVM IR using the MIR/// serialization format.extern char &MIRPrintingPassID;/// TailDuplicate - Duplicate blocks with unconditional branches/// into tails of their predecessors.extern char &TailDuplicateID;/// Duplicate blocks with unconditional branches into tails of their/// predecessors. Variant that works before register allocation.extern char &EarlyTailDuplicateID;/// MachineTraceMetrics - This pass computes critical path and CPU resource/// usage in an ensemble of traces.extern char &MachineTraceMetricsID;/// EarlyIfConverter - This pass performs if-conversion on SSA form by/// inserting cmov instructions.extern char &EarlyIfConverterID;/// EarlyIfPredicator - This pass performs if-conversion on SSA form by/// predicating if/else block and insert select at the join point.extern char &EarlyIfPredicatorID;/// This pass performs instruction combining using trace metrics to estimate/// critical-path and resource depth.extern char &MachineCombinerID;/// StackSlotColoring - This pass performs stack coloring and merging./// It merges disjoint allocas to reduce the stack size.extern char &StackColoringID;/// IfConverter - This pass performs machine code if conversion.extern char &IfConverterID;FunctionPass *createIfConverter(std::function<bool(const MachineFunction &)> Ftor);/// MachineBlockPlacement - This pass places basic blocks based on branch/// probabilities.extern char &MachineBlockPlacementID;/// MachineBlockPlacementStats - This pass collects statistics about the/// basic block placement using branch probabilities and block frequency/// information.extern char &MachineBlockPlacementStatsID;/// GCLowering Pass - Used by gc.root to perform its default lowering/// operations.FunctionPass *createGCLoweringPass();/// GCLowering Pass - Used by gc.root to perform its default lowering/// operations.extern char &GCLoweringID;/// ShadowStackGCLowering - Implements the custom lowering mechanism/// used by the shadow stack GC. Only runs on functions which opt in to/// the shadow stack collector.FunctionPass *createShadowStackGCLoweringPass();/// ShadowStackGCLowering - Implements the custom lowering mechanism/// used by the shadow stack GC.extern char &ShadowStackGCLoweringID;/// GCMachineCodeAnalysis - Target-independent pass to mark safe points/// in machine code. Must be added very late during code generation, just/// prior to output, and importantly after all CFG transformations (such as/// branch folding).extern char &GCMachineCodeAnalysisID;/// Creates a pass to print GC metadata.///FunctionPass *createGCInfoPrinter(raw_ostream &OS);/// MachineCSE - This pass performs global CSE on machine instructions.extern char &MachineCSEID;/// MIRCanonicalizer - This pass canonicalizes MIR by renaming vregs/// according to the semantics of the instruction as well as hoists/// code.extern char &MIRCanonicalizerID;/// ImplicitNullChecks - This pass folds null pointer checks into nearby/// memory operations.extern char &ImplicitNullChecksID;/// This pass performs loop invariant code motion on machine instructions.extern char &MachineLICMID;/// This pass performs loop invariant code motion on machine instructions./// This variant works before register allocation. \see MachineLICMID.extern char &EarlyMachineLICMID;/// MachineSinking - This pass performs sinking on machine instructions.extern char &MachineSinkingID;/// MachineCopyPropagation - This pass performs copy propagation on/// machine instructions.extern char &MachineCopyPropagationID;MachineFunctionPass *createMachineCopyPropagationPass(bool UseCopyInstr);/// PeepholeOptimizer - This pass performs peephole optimizations -/// like extension and comparison eliminations.extern char &PeepholeOptimizerID;/// OptimizePHIs - This pass optimizes machine instruction PHIs/// to take advantage of opportunities created during DAG legalization.extern char &OptimizePHIsID;/// StackSlotColoring - This pass performs stack slot coloring.extern char &StackSlotColoringID;/// This pass lays out funclets contiguously.extern char &FuncletLayoutID;/// This pass inserts the XRay instrumentation sleds if they are supported by/// the target platform.extern char &XRayInstrumentationID;/// This pass inserts FEntry callsextern char &FEntryInserterID;/// This pass implements the "patchable-function" attribute.extern char &PatchableFunctionID;/// createStackProtectorPass - This pass adds stack protectors to functions.///FunctionPass *createStackProtectorPass();/// createMachineVerifierPass - This pass verifies cenerated machine code/// instructions for correctness.///FunctionPass *createMachineVerifierPass(const std::string& Banner);/// createDwarfEHPass - This pass mulches exception handling code into a form/// adapted to code generation. Required if using dwarf exception handling.FunctionPass *createDwarfEHPass(CodeGenOpt::Level OptLevel);/// createWinEHPass - Prepares personality functions used by MSVC on Windows,/// in addition to the Itanium LSDA based personalities.FunctionPass *createWinEHPass(bool DemoteCatchSwitchPHIOnly = false);/// createSjLjEHPreparePass - This pass adapts exception handling code to use/// the GCC-style builtin setjmp/longjmp (sjlj) to handling EH control flow.///FunctionPass *createSjLjEHPreparePass(const TargetMachine *TM);/// createWasmEHPass - This pass adapts exception handling code to use/// WebAssembly's exception handling scheme.FunctionPass *createWasmEHPass();/// LocalStackSlotAllocation - This pass assigns local frame indices to stack/// slots relative to one another and allocates base registers to access them/// when it is estimated by the target to be out of range of normal frame/// pointer or stack pointer index addressing.extern char &LocalStackSlotAllocationID;/// This pass expands pseudo-instructions, reserves registers and adjusts/// machine frame information.extern char &FinalizeISelID;/// UnpackMachineBundles - This pass unpack machine instruction bundles.extern char &UnpackMachineBundlesID;FunctionPass *createUnpackMachineBundles(std::function<bool(const MachineFunction &)> Ftor);/// FinalizeMachineBundles - This pass finalize machine instruction/// bundles (created earlier, e.g. during pre-RA scheduling).extern char &FinalizeMachineBundlesID;/// StackMapLiveness - This pass analyses the register live-out set of/// stackmap/patchpoint intrinsics and attaches the calculated information to/// the intrinsic for later emission to the StackMap.extern char &StackMapLivenessID;/// RemoveRedundantDebugValues pass.extern char &RemoveRedundantDebugValuesID;/// LiveDebugValues passextern char &LiveDebugValuesID;/// createJumpInstrTables - This pass creates jump-instruction tables.ModulePass *createJumpInstrTablesPass();/// InterleavedAccess Pass - This pass identifies and matches interleaved/// memory accesses to target specific intrinsics.///FunctionPass *createInterleavedAccessPass();/// InterleavedLoadCombines Pass - This pass identifies interleaved loads and/// combines them into wide loads detectable by InterleavedAccessPass///FunctionPass *createInterleavedLoadCombinePass();/// LowerEmuTLS - This pass generates __emutls_[vt].xyz variables for all/// TLS variables for the emulated TLS model.///ModulePass *createLowerEmuTLSPass();/// This pass lowers the \@llvm.load.relative and \@llvm.objc.* intrinsics to/// instructions. This is unsafe to do earlier because a pass may combine the/// constant initializer into the load, which may result in an overflowing/// evaluation.ModulePass *createPreISelIntrinsicLoweringPass();/// GlobalMerge - This pass merges internal (by default) globals into structs/// to enable reuse of a base pointer by indexed addressing modes./// It can also be configured to focus on size optimizations only.///Pass *createGlobalMergePass(const TargetMachine *TM, unsigned MaximalOffset,bool OnlyOptimizeForSize = false,bool MergeExternalByDefault = false);/// This pass splits the stack into a safe stack and an unsafe stack to/// protect against stack-based overflow vulnerabilities.FunctionPass *createSafeStackPass();/// This pass detects subregister lanes in a virtual register that are used/// independently of other lanes and splits them into separate virtual/// registers.extern char &RenameIndependentSubregsID;/// This pass is executed POST-RA to collect which physical registers are/// preserved by given machine function.FunctionPass *createRegUsageInfoCollector();/// Return a MachineFunction pass that identifies call sites/// and propagates register usage information of callee to caller/// if available with PysicalRegisterUsageInfo pass.FunctionPass *createRegUsageInfoPropPass();/// This pass performs software pipelining on machine instructions.extern char &MachinePipelinerID;/// This pass frees the memory occupied by the MachineFunction.FunctionPass *createFreeMachineFunctionPass();/// This pass performs outlining on machine instructions directly before/// printing assembly.ModulePass *createMachineOutlinerPass(bool RunOnAllFunctions = true);/// This pass expands the experimental reduction intrinsics into sequences of/// shuffles.FunctionPass *createExpandReductionsPass();// This pass replaces intrinsics operating on vector operands with calls to// the corresponding function in a vector library (e.g., SVML, libmvec).FunctionPass *createReplaceWithVeclibLegacyPass();/// This pass expands the vector predication intrinsics into unpredicated/// instructions with selects or just the explicit vector length into the/// predicate mask.FunctionPass *createExpandVectorPredicationPass();// This pass expands memcmp() to load/stores.FunctionPass *createExpandMemCmpPass();/// Creates Break False Dependencies pass. \see BreakFalseDeps.cppFunctionPass *createBreakFalseDeps();// This pass expands indirectbr instructions.FunctionPass *createIndirectBrExpandPass();/// Creates CFI Fixup pass. \see CFIFixup.cppFunctionPass *createCFIFixup();/// Creates CFI Instruction Inserter pass. \see CFIInstrInserter.cppFunctionPass *createCFIInstrInserter();/// Creates CFGuard longjmp target identification pass./// \see CFGuardLongjmp.cppFunctionPass *createCFGuardLongjmpPass();/// Creates EHContGuard catchret target identification pass./// \see EHContGuardCatchret.cppFunctionPass *createEHContGuardCatchretPass();/// Create Hardware Loop pass. \see HardwareLoops.cppFunctionPass *createHardwareLoopsPass();/// This pass inserts pseudo probe annotation for callsite profiling.FunctionPass *createPseudoProbeInserter();/// Create IR Type Promotion pass. \see TypePromotion.cppFunctionPass *createTypePromotionPass();/// Add Flow Sensitive Discriminators. PassNum specifies the/// sequence number of this pass (starting from 1).FunctionPass *createMIRAddFSDiscriminatorsPass(sampleprof::FSDiscriminatorPass P);/// Read Flow Sensitive Profile.FunctionPass *createMIRProfileLoaderPass(std::string File,std::string RemappingFile,sampleprof::FSDiscriminatorPass P);/// Creates MIR Debugify pass. \see MachineDebugify.cppModulePass *createDebugifyMachineModulePass();/// Creates MIR Strip Debug pass. \see MachineStripDebug.cpp/// If OnlyDebugified is true then it will only strip debug info if it was/// added by a Debugify pass. The module will be left unchanged if the debug/// info was generated by another source such as clang.ModulePass *createStripDebugMachineModulePass(bool OnlyDebugified);/// Creates MIR Check Debug pass. \see MachineCheckDebugify.cppModulePass *createCheckDebugMachineModulePass();/// The pass fixups statepoint machine instruction to replace usage of/// caller saved registers with stack slots.extern char &FixupStatepointCallerSavedID;/// The pass transforms load/store <256 x i32> to AMX load/store intrinsics/// or split the data to two <128 x i32>.FunctionPass *createX86LowerAMXTypePass();/// The pass insert tile config intrinsics for AMX fast register allocation.FunctionPass *createX86PreAMXConfigPass();/// The pass transforms amx intrinsics to scalar operation if the function has/// optnone attribute or it is O0.FunctionPass *createX86LowerAMXIntrinsicsPass();/// When learning an eviction policy, extract score(reward) information,/// otherwise this does nothingFunctionPass *createRegAllocScoringPass();/// JMC instrument pass.ModulePass *createJMCInstrumenterPass();/// This pass converts conditional moves to conditional jumps when profitable.FunctionPass *createSelectOptimizePass();} // End llvm namespace#endif