1//===- SandboxVectorizer.cpp - Vectorizer based on Sandbox IR -------------===//
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/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizer.h"
10#include "llvm/Analysis/TargetTransformInfo.h"
11#include "llvm/IR/Module.h"
12#include "llvm/SandboxIR/Constant.h"
13#include "llvm/Support/CommandLine.h"
14#include "llvm/Support/Regex.h"
15#include "llvm/Transforms/Vectorize/SandboxVectorizer/Debug.h"
16#include "llvm/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizerPassBuilder.h"
17
18using namespace llvm;
19
20static cl::opt<bool>
21 PrintPassPipeline("sbvec-print-pass-pipeline", cl::init(Val: false), cl::Hidden,
22 cl::desc("Prints the pass pipeline and returns."));
23
24/// A magic string for the default pass pipeline.
25static const char *DefaultPipelineMagicStr = "*";
26
27static cl::opt<std::string> UserDefinedPassPipeline(
28 "sbvec-passes", cl::init(Val: DefaultPipelineMagicStr), cl::Hidden,
29 cl::desc("Comma-separated list of vectorizer passes. If not set "
30 "we run the predefined pipeline."));
31
32// This option is useful for bisection debugging.
33// For example you may use it to figure out which filename is the one causing a
34// miscompile. You can specify a regex for the filename like: "/[a-m][^/]*"
35// which will enable any file name starting with 'a' to 'm' and disable the
36// rest. If the miscompile goes away, then we try "/[n-z][^/]*" for the other
37// half of the range, from 'n' to 'z'. If we can reproduce the miscompile then
38// we can keep looking in [n-r] and [s-z] and so on, in a binary-search fashion.
39//
40// Please note that we are using [^/]* and not .* to make sure that we are
41// matching the actual filename and not some other directory in the path.
42cl::opt<std::string> AllowFiles(
43 "sbvec-allow-files", cl::init(Val: ".*"), cl::Hidden,
44 cl::desc("Run the vectorizer only on file paths that match any in the "
45 "list of comma-separated regex's."));
46static constexpr const char AllowFilesDelim = ',';
47
48SandboxVectorizerPass::SandboxVectorizerPass() : FPM("fpm") {
49 if (UserDefinedPassPipeline == DefaultPipelineMagicStr) {
50 // TODO: Add passes to the default pipeline. It currently contains:
51 // - Seed collection, which creates seed regions and runs the pipeline
52 // - Bottom-up Vectorizer pass that starts from a seed
53 // - Accept or revert IR state pass
54 FPM.setPassPipeline(
55 Pipeline: "seed-collection<tr-save,bottom-up-vec,tr-accept-or-revert>",
56 CreatePass: sandboxir::SandboxVectorizerPassBuilder::createFunctionPass);
57 } else {
58 // Create the user-defined pipeline.
59 FPM.setPassPipeline(
60 Pipeline: UserDefinedPassPipeline,
61 CreatePass: sandboxir::SandboxVectorizerPassBuilder::createFunctionPass);
62 }
63}
64
65SandboxVectorizerPass::SandboxVectorizerPass(SandboxVectorizerPass &&) =
66 default;
67
68SandboxVectorizerPass::~SandboxVectorizerPass() = default;
69
70PreservedAnalyses SandboxVectorizerPass::run(Function &F,
71 FunctionAnalysisManager &AM) {
72 TTI = &AM.getResult<TargetIRAnalysis>(IR&: F);
73 AA = &AM.getResult<AAManager>(IR&: F);
74 SE = &AM.getResult<ScalarEvolutionAnalysis>(IR&: F);
75
76 bool Changed = runImpl(F);
77 if (!Changed)
78 return PreservedAnalyses::all();
79
80 PreservedAnalyses PA;
81 PA.preserveSet<CFGAnalyses>();
82 return PA;
83}
84
85bool SandboxVectorizerPass::allowFile(const std::string &SrcFilePath) {
86 // Iterate over all files in AllowFiles separated by `AllowFilesDelim`.
87 size_t DelimPos = 0;
88 do {
89 size_t LastPos = DelimPos != 0 ? DelimPos + 1 : DelimPos;
90 DelimPos = AllowFiles.find(c: AllowFilesDelim, pos: LastPos);
91 auto FileNameToMatch = AllowFiles.substr(pos: LastPos, n: DelimPos - LastPos);
92 if (FileNameToMatch.empty())
93 return false;
94 // Note: This only runs when debugging so its OK not to reuse the regex.
95 Regex FileNameRegex(".*" + FileNameToMatch + "$");
96 assert(FileNameRegex.isValid() && "Bad regex!");
97 if (FileNameRegex.match(String: SrcFilePath))
98 return true;
99 } while (DelimPos != std::string::npos);
100 return false;
101}
102
103bool SandboxVectorizerPass::runImpl(Function &LLVMF) {
104 if (Ctx == nullptr)
105 Ctx = std::make_unique<sandboxir::Context>(args&: LLVMF.getContext());
106
107 if (PrintPassPipeline) {
108 FPM.printPipeline(OS&: outs());
109 return false;
110 }
111
112 // This is used for debugging.
113 if (LLVM_UNLIKELY(AllowFiles != ".*")) {
114 const auto &SrcFilePath = LLVMF.getParent()->getSourceFileName();
115 if (!allowFile(SrcFilePath))
116 return false;
117 }
118
119 // If the target claims to have no vector registers early return.
120 if (!TTI->getNumberOfRegisters(ClassID: TTI->getRegisterClassForType(Vector: true))) {
121 LLVM_DEBUG(dbgs() << DEBUG_PREFIX
122 << "Target has no vector registers, return.\n");
123 return false;
124 }
125 LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Analyzing " << LLVMF.getName()
126 << ".\n");
127 // Early return if the attribute NoImplicitFloat is used.
128 if (LLVMF.hasFnAttribute(Kind: Attribute::NoImplicitFloat)) {
129 LLVM_DEBUG(dbgs() << DEBUG_PREFIX
130 << "NoImplicitFloat attribute, return.\n");
131 return false;
132 }
133
134 // Create SandboxIR for LLVMF and run BottomUpVec on it.
135 sandboxir::Function &F = *Ctx->createFunction(F: &LLVMF);
136 sandboxir::Analyses A(*AA, *SE, *TTI);
137 bool Change = FPM.runOnFunction(F, A);
138 // Given that sandboxir::Context `Ctx` is defined at a pass-level scope, the
139 // maps from LLVM IR to Sandbox IR may go stale as later passes remove LLVM IR
140 // objects. To avoid issues caused by this clear the context's state.
141 // NOTE: The alternative would be to define Ctx and FPM within runOnFunction()
142 Ctx->clear();
143 return Change;
144}
145