1 | //===--- ReplaceWithVecLibTest.cpp - replace-with-veclib unit tests -------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/CodeGen/ReplaceWithVeclib.h" |
10 | #include "llvm/Analysis/TargetLibraryInfo.h" |
11 | #include "llvm/AsmParser/Parser.h" |
12 | #include "llvm/IR/LLVMContext.h" |
13 | #include "llvm/IR/Module.h" |
14 | #include "llvm/Passes/PassBuilder.h" |
15 | #include "llvm/Support/SourceMgr.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | using namespace llvm; |
19 | |
20 | /// NOTE: Assertions must be enabled for these tests to run. |
21 | #ifndef NDEBUG |
22 | |
23 | namespace { |
24 | |
25 | static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { |
26 | SMDiagnostic Err; |
27 | std::unique_ptr<Module> Mod = parseAssemblyString(AsmString: IR, Err, Context&: C); |
28 | if (!Mod) |
29 | Err.print(ProgName: "ReplaceWithVecLibTest" , S&: errs()); |
30 | return Mod; |
31 | } |
32 | |
33 | /// Runs ReplaceWithVecLib with different TLIIs that have custom VecDescs. This |
34 | /// allows checking that the pass won't crash when the function to replace (from |
35 | /// the input IR) does not match the replacement function (derived from the |
36 | /// VecDesc mapping). |
37 | /// |
38 | class ReplaceWithVecLibTest : public ::testing::Test { |
39 | |
40 | std::string getLastLine(std::string Out) { |
41 | // remove any trailing '\n' |
42 | if (!Out.empty() && *(Out.cend() - 1) == '\n') |
43 | Out.pop_back(); |
44 | |
45 | size_t LastNL = Out.find_last_of(c: '\n'); |
46 | return (LastNL == std::string::npos) ? Out : Out.substr(pos: LastNL + 1); |
47 | } |
48 | |
49 | protected: |
50 | LLVMContext Ctx; |
51 | |
52 | /// Creates TLII using the given \p VD, and then runs the ReplaceWithVeclib |
53 | /// pass. The pass should not crash even when the replacement function |
54 | /// (derived from the \p VD mapping) does not match the function to be |
55 | /// replaced (from the input \p IR). |
56 | /// |
57 | /// \returns the last line of the standard error to be compared for |
58 | /// correctness. |
59 | std::string run(const VecDesc &VD, const char *IR) { |
60 | // Create TLII and register it with FAM so it's preserved when |
61 | // ReplaceWithVeclib pass runs. |
62 | TargetLibraryInfoImpl TLII = TargetLibraryInfoImpl(Triple()); |
63 | TLII.addVectorizableFunctions(Fns: {VD}); |
64 | FunctionAnalysisManager FAM; |
65 | FAM.registerPass(PassBuilder: [&TLII]() { return TargetLibraryAnalysis(TLII); }); |
66 | |
67 | // Register and run the pass on the 'foo' function from the input IR. |
68 | FunctionPassManager FPM; |
69 | FPM.addPass(Pass: ReplaceWithVeclib()); |
70 | std::unique_ptr<Module> M = parseIR(C&: Ctx, IR); |
71 | PassBuilder PB; |
72 | PB.registerFunctionAnalyses(FAM); |
73 | |
74 | // Enable debugging and capture std error |
75 | bool DebugFlagPrev = llvm::DebugFlag; |
76 | llvm::DebugFlag = true; |
77 | testing::internal::CaptureStderr(); |
78 | FPM.run(IR&: *M->getFunction(Name: "foo" ), AM&: FAM); |
79 | llvm::DebugFlag = DebugFlagPrev; |
80 | return getLastLine(Out: testing::internal::GetCapturedStderr()); |
81 | } |
82 | }; |
83 | |
84 | } // end anonymous namespace |
85 | |
86 | static const char *IR = R"IR( |
87 | define <vscale x 4 x float> @foo(<vscale x 4 x float> %in){ |
88 | %call = call <vscale x 4 x float> @llvm.powi.f32.i32(<vscale x 4 x float> %in, i32 3) |
89 | ret <vscale x 4 x float> %call |
90 | } |
91 | |
92 | declare <vscale x 4 x float> @llvm.powi.f32.i32(<vscale x 4 x float>, i32) #0 |
93 | )IR" ; |
94 | |
95 | // The VFABI prefix in TLI describes signature which is matching the powi |
96 | // intrinsic declaration. |
97 | TEST_F(ReplaceWithVecLibTest, TestValidMapping) { |
98 | VecDesc CorrectVD = {"llvm.powi.f32.i32" , "_ZGVsMxvu_powi" , |
99 | ElementCount::getScalable(MinVal: 4), /*Masked*/ true, |
100 | "_ZGVsMxvu" }; |
101 | EXPECT_EQ(run(CorrectVD, IR), |
102 | "Instructions replaced with vector libraries: 1" ); |
103 | } |
104 | |
105 | // The VFABI prefix in TLI describes signature which is not matching the powi |
106 | // intrinsic declaration. |
107 | TEST_F(ReplaceWithVecLibTest, TestInvalidMapping) { |
108 | VecDesc IncorrectVD = {"llvm.powi.f32.i32" , "_ZGVsMxvv_powi" , |
109 | ElementCount::getScalable(MinVal: 4), /*Masked*/ true, |
110 | "_ZGVsMxvv" }; |
111 | EXPECT_EQ(run(IncorrectVD, IR), |
112 | "replace-with-veclib: Will not replace: llvm.powi.f32.i32. Wrong " |
113 | "type at index 1: i32" ); |
114 | } |
115 | #endif |