xref: /llvm-project/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Legality.cpp (revision 083369fd99ef1094c1058b8112712d3589a0e8fa)
1 //===- Legality.cpp -------------------------------------------------------===//
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/Legality.h"
10 #include "llvm/SandboxIR/Instruction.h"
11 #include "llvm/SandboxIR/Operator.h"
12 #include "llvm/SandboxIR/Utils.h"
13 #include "llvm/SandboxIR/Value.h"
14 #include "llvm/Support/Debug.h"
15 #include "llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h"
16 
17 namespace llvm::sandboxir {
18 
19 #define DEBUG_TYPE "SBVec:Legality"
20 
21 #ifndef NDEBUG
22 void LegalityResult::dump() const {
23   print(dbgs());
24   dbgs() << "\n";
25 }
26 #endif // NDEBUG
27 
28 std::optional<ResultReason>
29 LegalityAnalysis::notVectorizableBasedOnOpcodesAndTypes(
30     ArrayRef<Value *> Bndl) {
31   auto *I0 = cast<Instruction>(Bndl[0]);
32   auto Opcode = I0->getOpcode();
33   // If they have different opcodes, then we cannot form a vector (for now).
34   if (any_of(drop_begin(Bndl), [Opcode](Value *V) {
35         return cast<Instruction>(V)->getOpcode() != Opcode;
36       }))
37     return ResultReason::DiffOpcodes;
38 
39   // If not the same scalar type, Pack. This will accept scalars and vectors as
40   // long as the element type is the same.
41   Type *ElmTy0 = VecUtils::getElementType(Utils::getExpectedType(I0));
42   if (any_of(drop_begin(Bndl), [ElmTy0](Value *V) {
43         return VecUtils::getElementType(Utils::getExpectedType(V)) != ElmTy0;
44       }))
45     return ResultReason::DiffTypes;
46 
47   // TODO: Allow vectorization of instrs with different flags as long as we
48   // change them to the least common one.
49   // For now pack if differnt FastMathFlags.
50   if (isa<FPMathOperator>(I0)) {
51     FastMathFlags FMF0 = cast<Instruction>(Bndl[0])->getFastMathFlags();
52     if (any_of(drop_begin(Bndl), [FMF0](auto *V) {
53           return cast<Instruction>(V)->getFastMathFlags() != FMF0;
54         }))
55       return ResultReason::DiffMathFlags;
56   }
57 
58   // TODO: Allow vectorization by using common flags.
59   // For now Pack if they don't have the same wrap flags.
60   bool CanHaveWrapFlags =
61       isa<OverflowingBinaryOperator>(I0) || isa<TruncInst>(I0);
62   if (CanHaveWrapFlags) {
63     bool NUW0 = I0->hasNoUnsignedWrap();
64     bool NSW0 = I0->hasNoSignedWrap();
65     if (any_of(drop_begin(Bndl), [NUW0, NSW0](auto *V) {
66           return cast<Instruction>(V)->hasNoUnsignedWrap() != NUW0 ||
67                  cast<Instruction>(V)->hasNoSignedWrap() != NSW0;
68         })) {
69       return ResultReason::DiffWrapFlags;
70     }
71   }
72 
73   // Now we need to do further checks for specific opcodes.
74   switch (Opcode) {
75   case Instruction::Opcode::ZExt:
76   case Instruction::Opcode::SExt:
77   case Instruction::Opcode::FPToUI:
78   case Instruction::Opcode::FPToSI:
79   case Instruction::Opcode::FPExt:
80   case Instruction::Opcode::PtrToInt:
81   case Instruction::Opcode::IntToPtr:
82   case Instruction::Opcode::SIToFP:
83   case Instruction::Opcode::UIToFP:
84   case Instruction::Opcode::Trunc:
85   case Instruction::Opcode::FPTrunc:
86   case Instruction::Opcode::BitCast: {
87     // We have already checked that they are of the same opcode.
88     assert(all_of(Bndl,
89                   [Opcode](Value *V) {
90                     return cast<Instruction>(V)->getOpcode() == Opcode;
91                   }) &&
92            "Different opcodes, should have early returned!");
93     // But for these opcodes we should also check the operand type.
94     Type *FromTy0 = Utils::getExpectedType(I0->getOperand(0));
95     if (any_of(drop_begin(Bndl), [FromTy0](Value *V) {
96           return Utils::getExpectedType(cast<User>(V)->getOperand(0)) !=
97                  FromTy0;
98         }))
99       return ResultReason::DiffTypes;
100     return std::nullopt;
101   }
102   case Instruction::Opcode::FCmp:
103   case Instruction::Opcode::ICmp: {
104     // We need the same predicate..
105     auto Pred0 = cast<CmpInst>(I0)->getPredicate();
106     bool Same = all_of(Bndl, [Pred0](Value *V) {
107       return cast<CmpInst>(V)->getPredicate() == Pred0;
108     });
109     if (Same)
110       return std::nullopt;
111     return ResultReason::DiffOpcodes;
112   }
113   case Instruction::Opcode::Select:
114   case Instruction::Opcode::FNeg:
115   case Instruction::Opcode::Add:
116   case Instruction::Opcode::FAdd:
117   case Instruction::Opcode::Sub:
118   case Instruction::Opcode::FSub:
119   case Instruction::Opcode::Mul:
120   case Instruction::Opcode::FMul:
121   case Instruction::Opcode::FRem:
122   case Instruction::Opcode::UDiv:
123   case Instruction::Opcode::SDiv:
124   case Instruction::Opcode::FDiv:
125   case Instruction::Opcode::URem:
126   case Instruction::Opcode::SRem:
127   case Instruction::Opcode::Shl:
128   case Instruction::Opcode::LShr:
129   case Instruction::Opcode::AShr:
130   case Instruction::Opcode::And:
131   case Instruction::Opcode::Or:
132   case Instruction::Opcode::Xor:
133     return std::nullopt;
134   case Instruction::Opcode::Load:
135     if (VecUtils::areConsecutive<LoadInst>(Bndl, SE, DL))
136       return std::nullopt;
137     return ResultReason::NotConsecutive;
138   case Instruction::Opcode::Store:
139     if (VecUtils::areConsecutive<StoreInst>(Bndl, SE, DL))
140       return std::nullopt;
141     return ResultReason::NotConsecutive;
142   case Instruction::Opcode::PHI:
143     return ResultReason::Unimplemented;
144   case Instruction::Opcode::Opaque:
145     return ResultReason::Unimplemented;
146   case Instruction::Opcode::Br:
147   case Instruction::Opcode::Ret:
148   case Instruction::Opcode::AddrSpaceCast:
149   case Instruction::Opcode::InsertElement:
150   case Instruction::Opcode::InsertValue:
151   case Instruction::Opcode::ExtractElement:
152   case Instruction::Opcode::ExtractValue:
153   case Instruction::Opcode::ShuffleVector:
154   case Instruction::Opcode::Call:
155   case Instruction::Opcode::GetElementPtr:
156   case Instruction::Opcode::Switch:
157     return ResultReason::Unimplemented;
158   case Instruction::Opcode::VAArg:
159   case Instruction::Opcode::Freeze:
160   case Instruction::Opcode::Fence:
161   case Instruction::Opcode::Invoke:
162   case Instruction::Opcode::CallBr:
163   case Instruction::Opcode::LandingPad:
164   case Instruction::Opcode::CatchPad:
165   case Instruction::Opcode::CleanupPad:
166   case Instruction::Opcode::CatchRet:
167   case Instruction::Opcode::CleanupRet:
168   case Instruction::Opcode::Resume:
169   case Instruction::Opcode::CatchSwitch:
170   case Instruction::Opcode::AtomicRMW:
171   case Instruction::Opcode::AtomicCmpXchg:
172   case Instruction::Opcode::Alloca:
173   case Instruction::Opcode::Unreachable:
174     return ResultReason::Infeasible;
175   }
176 
177   return std::nullopt;
178 }
179 
180 #ifndef NDEBUG
181 static void dumpBndl(ArrayRef<Value *> Bndl) {
182   for (auto *V : Bndl)
183     dbgs() << *V << "\n";
184 }
185 #endif // NDEBUG
186 
187 const LegalityResult &LegalityAnalysis::canVectorize(ArrayRef<Value *> Bndl) {
188   // If Bndl contains values other than instructions, we need to Pack.
189   if (any_of(Bndl, [](auto *V) { return !isa<Instruction>(V); })) {
190     LLVM_DEBUG(dbgs() << "Not vectorizing: Not Instructions:\n";
191                dumpBndl(Bndl););
192     return createLegalityResult<Pack>(ResultReason::NotInstructions);
193   }
194 
195   if (auto ReasonOpt = notVectorizableBasedOnOpcodesAndTypes(Bndl))
196     return createLegalityResult<Pack>(*ReasonOpt);
197 
198   // TODO: Check for existing vectors containing values in Bndl.
199 
200   // TODO: Check with scheduler.
201 
202   return createLegalityResult<Widen>();
203 }
204 } // namespace llvm::sandboxir
205