LLVM 22.0.0git
LegalizerInfo.cpp
Go to the documentation of this file.
1//===- lib/CodeGen/GlobalISel/LegalizerInfo.cpp - Legalizer ---------------===//
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// Implement an interface to specify and query how an illegal operation on a
10// given type should be expanded.
11//
12//===----------------------------------------------------------------------===//
13
21#include "llvm/MC/MCInstrDesc.h"
22#include "llvm/MC/MCInstrInfo.h"
23#include "llvm/Support/Debug.h"
25#include <algorithm>
26
27using namespace llvm;
28using namespace LegalizeActions;
29
30#define DEBUG_TYPE "legalizer-info"
31
33 "disable-gisel-legality-check",
34 cl::desc("Don't verify that MIR is fully legal between GlobalISel passes"),
36
38 "verbose-gisel-verify-legalizer-info",
39 cl::desc("Print more information to dbgs about GlobalISel legalizer rules "
40 "being verified"),
42
44 switch (Action) {
45 case Legal:
46 OS << "Legal";
47 break;
48 case NarrowScalar:
49 OS << "NarrowScalar";
50 break;
51 case WidenScalar:
52 OS << "WidenScalar";
53 break;
54 case FewerElements:
55 OS << "FewerElements";
56 break;
57 case MoreElements:
58 OS << "MoreElements";
59 break;
60 case Bitcast:
61 OS << "Bitcast";
62 break;
63 case Lower:
64 OS << "Lower";
65 break;
66 case Libcall:
67 OS << "Libcall";
68 break;
69 case Custom:
70 OS << "Custom";
71 break;
72 case Unsupported:
73 OS << "Unsupported";
74 break;
75 case NotFound:
76 OS << "NotFound";
77 break;
78 case UseLegacyRules:
79 OS << "UseLegacyRules";
80 break;
81 }
82 return OS;
83}
84
86 OS << "Opcode=" << Opcode << ", Tys={";
87 for (const auto &Type : Types) {
88 OS << Type << ", ";
89 }
90 OS << "}, MMOs={";
91 for (const auto &MMODescr : MMODescrs) {
92 OS << MMODescr.MemoryTy << ", ";
93 }
94 OS << "}";
95
96 return OS;
97}
98
99#ifndef NDEBUG
100// Make sure the rule won't (trivially) loop forever.
101static bool hasNoSimpleLoops(const LegalizeRule &Rule, const LegalityQuery &Q,
102 const std::pair<unsigned, LLT> &Mutation) {
103 switch (Rule.getAction()) {
104 case Legal:
105 case Custom:
106 case Lower:
107 case MoreElements:
108 case FewerElements:
109 case Libcall:
110 break;
111 default:
112 return Q.Types[Mutation.first] != Mutation.second;
113 }
114 return true;
115}
116
117// Make sure the returned mutation makes sense for the match type.
118static bool mutationIsSane(const LegalizeRule &Rule,
119 const LegalityQuery &Q,
120 std::pair<unsigned, LLT> Mutation) {
121 // If the user wants a custom mutation, then we can't really say much about
122 // it. Return true, and trust that they're doing the right thing.
123 if (Rule.getAction() == Custom || Rule.getAction() == Legal)
124 return true;
125
126 // Skip null mutation.
127 if (!Mutation.second.isValid())
128 return true;
129
130 const unsigned TypeIdx = Mutation.first;
131 const LLT OldTy = Q.Types[TypeIdx];
132 const LLT NewTy = Mutation.second;
133
134 switch (Rule.getAction()) {
135 case FewerElements:
136 if (!OldTy.isVector())
137 return false;
138 [[fallthrough]];
139 case MoreElements: {
140 // MoreElements can go from scalar to vector.
141 const ElementCount OldElts = OldTy.isVector() ?
143 if (NewTy.isVector()) {
144 if (Rule.getAction() == FewerElements) {
145 // Make sure the element count really decreased.
146 if (ElementCount::isKnownGE(NewTy.getElementCount(), OldElts))
147 return false;
148 } else {
149 // Make sure the element count really increased.
150 if (ElementCount::isKnownLE(NewTy.getElementCount(), OldElts))
151 return false;
152 }
153 } else if (Rule.getAction() == MoreElements)
154 return false;
155
156 // Make sure the element type didn't change.
157 return NewTy.getScalarType() == OldTy.getScalarType();
158 }
159 case NarrowScalar:
160 case WidenScalar: {
161 if (OldTy.isVector()) {
162 // Number of elements should not change.
163 if (!NewTy.isVector() ||
164 OldTy.getElementCount() != NewTy.getElementCount())
165 return false;
166 } else {
167 // Both types must be vectors
168 if (NewTy.isVector())
169 return false;
170 }
171
172 if (Rule.getAction() == NarrowScalar) {
173 // Make sure the size really decreased.
174 if (NewTy.getScalarSizeInBits() >= OldTy.getScalarSizeInBits())
175 return false;
176 } else {
177 // Make sure the size really increased.
178 if (NewTy.getScalarSizeInBits() <= OldTy.getScalarSizeInBits())
179 return false;
180 }
181
182 return true;
183 }
184 case Bitcast: {
185 return OldTy != NewTy && OldTy.getSizeInBits() == NewTy.getSizeInBits();
186 }
187 default:
188 return true;
189 }
190}
191#endif
192
194 LLVM_DEBUG(dbgs() << "Applying legalizer ruleset to: "; Query.print(dbgs());
195 dbgs() << "\n");
196 if (Rules.empty()) {
197 LLVM_DEBUG(dbgs() << ".. fallback to legacy rules (no rules defined)\n");
198 return {LegalizeAction::UseLegacyRules, 0, LLT{}};
199 }
200 for (const LegalizeRule &Rule : Rules) {
201 if (Rule.match(Query)) {
202 LLVM_DEBUG(dbgs() << ".. match\n");
203 std::pair<unsigned, LLT> Mutation = Rule.determineMutation(Query);
204 LLVM_DEBUG(dbgs() << ".. .. " << Rule.getAction() << ", "
205 << Mutation.first << ", " << Mutation.second << "\n");
206 assert(mutationIsSane(Rule, Query, Mutation) &&
207 "legality mutation invalid for match");
208 assert(hasNoSimpleLoops(Rule, Query, Mutation) && "Simple loop detected");
209 return {Rule.getAction(), Mutation.first, Mutation.second};
210 } else
211 LLVM_DEBUG(dbgs() << ".. no match\n");
212 }
213 LLVM_DEBUG(dbgs() << ".. unsupported\n");
214 return {LegalizeAction::Unsupported, 0, LLT{}};
215}
216
217bool LegalizeRuleSet::verifyTypeIdxsCoverage(unsigned NumTypeIdxs) const {
218#ifndef NDEBUG
219 if (Rules.empty()) {
221 LLVM_DEBUG(dbgs() << ".. type index coverage check SKIPPED: "
222 << "no rules defined\n");
223 }
224 return true;
225 }
226 const int64_t FirstUncovered = TypeIdxsCovered.find_first_unset();
227 if (FirstUncovered < 0) {
229 LLVM_DEBUG(dbgs() << ".. type index coverage check SKIPPED:"
230 " user-defined predicate detected\n");
231 }
232 return true;
233 }
234 const bool AllCovered = (FirstUncovered >= NumTypeIdxs);
235 if (NumTypeIdxs > 0) {
237 LLVM_DEBUG(dbgs() << ".. the first uncovered type index: "
238 << FirstUncovered << ", "
239 << (AllCovered ? "OK" : "FAIL") << "\n");
240 }
241 }
242 return AllCovered;
243#else
244 return true;
245#endif
246}
247
248bool LegalizeRuleSet::verifyImmIdxsCoverage(unsigned NumImmIdxs) const {
249#ifndef NDEBUG
250 if (Rules.empty()) {
252 LLVM_DEBUG(dbgs() << ".. imm index coverage check SKIPPED: "
253 << "no rules defined\n");
254 }
255 return true;
256 }
257 const int64_t FirstUncovered = ImmIdxsCovered.find_first_unset();
258 if (FirstUncovered < 0) {
260 LLVM_DEBUG(dbgs() << ".. imm index coverage check SKIPPED:"
261 " user-defined predicate detected\n");
262 }
263 return true;
264 }
265 const bool AllCovered = (FirstUncovered >= NumImmIdxs);
267 LLVM_DEBUG(dbgs() << ".. the first uncovered imm index: " << FirstUncovered
268 << ", " << (AllCovered ? "OK" : "FAIL") << "\n");
269 }
270 return AllCovered;
271#else
272 return true;
273#endif
274}
275
276/// Helper function to get LLT for the given type index.
278 const MachineRegisterInfo &MRI, unsigned OpIdx,
279 unsigned TypeIdx) {
280 assert(TypeIdx < MI.getNumOperands() && "Unexpected TypeIdx");
281 // G_UNMERGE_VALUES has variable number of operands, but there is only
282 // one source type and one destination type as all destinations must be the
283 // same type. So, get the last operand if TypeIdx == 1.
284 if (MI.getOpcode() == TargetOpcode::G_UNMERGE_VALUES && TypeIdx == 1)
285 return MRI.getType(MI.getOperand(MI.getNumOperands() - 1).getReg());
286 return MRI.getType(MI.getOperand(OpIdx).getReg());
287}
288
289unsigned LegalizerInfo::getOpcodeIdxForOpcode(unsigned Opcode) const {
290 assert(Opcode >= FirstOp && Opcode <= LastOp && "Unsupported opcode");
291 return Opcode - FirstOp;
292}
293
294unsigned LegalizerInfo::getActionDefinitionsIdx(unsigned Opcode) const {
295 unsigned OpcodeIdx = getOpcodeIdxForOpcode(Opcode);
296 if (unsigned Alias = RulesForOpcode[OpcodeIdx].getAlias()) {
298 LLVM_DEBUG(dbgs() << ".. opcode " << Opcode << " is aliased to " << Alias
299 << "\n");
300 }
301 OpcodeIdx = getOpcodeIdxForOpcode(Alias);
302 assert(RulesForOpcode[OpcodeIdx].getAlias() == 0 && "Cannot chain aliases");
303 }
304
305 return OpcodeIdx;
306}
307
308const LegalizeRuleSet &
310 unsigned OpcodeIdx = getActionDefinitionsIdx(Opcode);
311 return RulesForOpcode[OpcodeIdx];
312}
313
315 unsigned OpcodeIdx = getActionDefinitionsIdx(Opcode);
316 auto &Result = RulesForOpcode[OpcodeIdx];
317 assert(!Result.isAliasedByAnother() && "Modifying this opcode will modify aliases");
318 return Result;
319}
320
322 std::initializer_list<unsigned> Opcodes) {
323 unsigned Representative = *Opcodes.begin();
324
325 assert(Opcodes.size() >= 2 &&
326 "Initializer list must have at least two opcodes");
327
328 for (unsigned Op : llvm::drop_begin(Opcodes))
329 aliasActionDefinitions(Representative, Op);
330
331 auto &Return = getActionDefinitionsBuilder(Representative);
332 Return.setIsAliasedByAnother();
333 return Return;
334}
335
337 unsigned OpcodeFrom) {
338 assert(OpcodeTo != OpcodeFrom && "Cannot alias to self");
339 assert(OpcodeTo >= FirstOp && OpcodeTo <= LastOp && "Unsupported opcode");
340 const unsigned OpcodeFromIdx = getOpcodeIdxForOpcode(OpcodeFrom);
341 RulesForOpcode[OpcodeFromIdx].aliasTo(OpcodeTo);
342}
343
347 if (Step.Action != LegalizeAction::UseLegacyRules) {
348 return Step;
349 }
350
351 return getLegacyLegalizerInfo().getAction(Query);
352}
353
356 const MachineRegisterInfo &MRI) const {
358 SmallBitVector SeenTypes(8);
359 ArrayRef<MCOperandInfo> OpInfo = MI.getDesc().operands();
360 // FIXME: probably we'll need to cache the results here somehow?
361 for (unsigned i = 0; i < MI.getDesc().getNumOperands(); ++i) {
362 if (!OpInfo[i].isGenericType())
363 continue;
364
365 // We must only record actions once for each TypeIdx; otherwise we'd
366 // try to legalize operands multiple times down the line.
367 unsigned TypeIdx = OpInfo[i].getGenericTypeIndex();
368 if (SeenTypes[TypeIdx])
369 continue;
370
371 SeenTypes.set(TypeIdx);
372
373 LLT Ty = getTypeFromTypeIdx(MI, MRI, i, TypeIdx);
374 Types.push_back(Ty);
375 }
376
378 for (const auto &MMO : MI.memoperands())
379 MemDescrs.push_back({*MMO});
380
381 return getAction({MI.getOpcode(), Types, MemDescrs});
382}
383
385 const MachineRegisterInfo &MRI) const {
386 return getAction(MI, MRI).Action == Legal;
387}
388
390 const MachineRegisterInfo &MRI) const {
391 auto Action = getAction(MI, MRI).Action;
392 // If the action is custom, it may not necessarily modify the instruction,
393 // so we have to assume it's legal.
394 return Action == Legal || Action == Custom;
395}
396
398 return SmallTy.isByteSized() ? TargetOpcode::G_SEXT : TargetOpcode::G_ZEXT;
399}
400
401/// \pre Type indices of every opcode form a dense set starting from 0.
402void LegalizerInfo::verify(const MCInstrInfo &MII) const {
403#ifndef NDEBUG
404 std::vector<unsigned> FailedOpcodes;
405 for (unsigned Opcode = FirstOp; Opcode <= LastOp; ++Opcode) {
406 const MCInstrDesc &MCID = MII.get(Opcode);
407 const unsigned NumTypeIdxs = std::accumulate(
408 MCID.operands().begin(), MCID.operands().end(), 0U,
409 [](unsigned Acc, const MCOperandInfo &OpInfo) {
410 return OpInfo.isGenericType()
411 ? std::max(OpInfo.getGenericTypeIndex() + 1U, Acc)
412 : Acc;
413 });
414 const unsigned NumImmIdxs = std::accumulate(
415 MCID.operands().begin(), MCID.operands().end(), 0U,
416 [](unsigned Acc, const MCOperandInfo &OpInfo) {
417 return OpInfo.isGenericImm()
418 ? std::max(OpInfo.getGenericImmIndex() + 1U, Acc)
419 : Acc;
420 });
422 LLVM_DEBUG(dbgs() << MII.getName(Opcode) << " (opcode " << Opcode
423 << "): " << NumTypeIdxs << " type ind"
424 << (NumTypeIdxs == 1 ? "ex" : "ices") << ", "
425 << NumImmIdxs << " imm ind"
426 << (NumImmIdxs == 1 ? "ex" : "ices") << "\n");
427 }
428 const LegalizeRuleSet &RuleSet = getActionDefinitions(Opcode);
429 if (!RuleSet.verifyTypeIdxsCoverage(NumTypeIdxs))
430 FailedOpcodes.push_back(Opcode);
431 else if (!RuleSet.verifyImmIdxsCoverage(NumImmIdxs))
432 FailedOpcodes.push_back(Opcode);
433 }
434 if (!FailedOpcodes.empty()) {
435 errs() << "The following opcodes have ill-defined legalization rules:";
436 for (unsigned Opcode : FailedOpcodes)
437 errs() << " " << MII.getName(Opcode);
438 errs() << "\n";
439
440 report_fatal_error("ill-defined LegalizerInfo, try "
441 "-debug-only=legalizer-info and "
442 "-verbose-gisel-verify-legalizer-info for details");
443 }
444#endif
445}
446
447#ifndef NDEBUG
448// FIXME: This should be in the MachineVerifier, but it can't use the
449// LegalizerInfo as it's currently in the separate GlobalISel library.
450// Note that RegBankSelected property already checked in the verifier
451// has the same layering problem, but we only use inline methods so
452// end up not needing to link against the GlobalISel library.
454 if (const LegalizerInfo *MLI = MF.getSubtarget().getLegalizerInfo()) {
455 const MachineRegisterInfo &MRI = MF.getRegInfo();
456 for (const MachineBasicBlock &MBB : MF)
457 for (const MachineInstr &MI : MBB)
458 if (isPreISelGenericOpcode(MI.getOpcode()) &&
459 !MLI->isLegalOrCustom(MI, MRI))
460 return &MI;
461 }
462 return nullptr;
463}
464#endif
unsigned const MachineRegisterInfo * MRI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
IRTranslator LLVM IR MI
cl::opt< bool > VerboseVerifyLegalizerInfo("verbose-gisel-verify-legalizer-info", cl::desc("Print more information to dbgs about GlobalISel legalizer rules " "being verified"), cl::Hidden)
static bool hasNoSimpleLoops(const LegalizeRule &Rule, const LegalityQuery &Q, const std::pair< unsigned, LLT > &Mutation)
static LLT getTypeFromTypeIdx(const MachineInstr &MI, const MachineRegisterInfo &MRI, unsigned OpIdx, unsigned TypeIdx)
Helper function to get LLT for the given type index.
static bool mutationIsSane(const LegalizeRule &Rule, const LegalityQuery &Q, std::pair< unsigned, LLT > Mutation)
Interface for Targets to specify which operations they can successfully select and how the others sho...
Implement a low-level type suitable for MachineInstr level instruction selection.
MachineInstr unsigned OpIdx
PowerPC VSX FMA Mutation
This file implements the SmallBitVector class.
#define LLVM_DEBUG(...)
Definition Debug.h:114
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:41
static constexpr ElementCount getFixed(ScalarTy MinVal)
Definition TypeSize.h:309
constexpr unsigned getScalarSizeInBits() const
constexpr bool isVector() const
constexpr bool isByteSized() const
constexpr TypeSize getSizeInBits() const
Returns the total size of the type. Must only be called on sized types.
constexpr ElementCount getElementCount() const
constexpr LLT getScalarType() const
LLVM_ABI LegacyLegalizeActionStep getAction(const LegalityQuery &Query) const
LLVM_ABI bool verifyImmIdxsCoverage(unsigned NumImmIdxs) const
Check if there is no imm index which is obviously not handled by the LegalizeRuleSet in any way at al...
LLVM_ABI bool verifyTypeIdxsCoverage(unsigned NumTypeIdxs) const
Check if there is no type index which is obviously not handled by the LegalizeRuleSet in any way at a...
LLVM_ABI LegalizeActionStep apply(const LegalityQuery &Query) const
Apply the ruleset to the given LegalityQuery.
A single rule in a legalizer info ruleset.
LegalizeAction getAction() const
const LegalizeRuleSet & getActionDefinitions(unsigned Opcode) const
Get the action definitions for the given opcode.
LegalizeRuleSet & getActionDefinitionsBuilder(unsigned Opcode)
Get the action definition builder for the given opcode.
const LegacyLegalizerInfo & getLegacyLegalizerInfo() const
virtual unsigned getExtOpcodeForWideningConstant(LLT SmallTy) const
Return the opcode (SEXT/ZEXT/ANYEXT) that should be performed while widening a constant of type Small...
bool isLegalOrCustom(const LegalityQuery &Query) const
void aliasActionDefinitions(unsigned OpcodeTo, unsigned OpcodeFrom)
void verify(const MCInstrInfo &MII) const
Perform simple self-diagnostic and assert if there is anything obviously wrong with the actions set u...
unsigned getOpcodeIdxForOpcode(unsigned Opcode) const
bool isLegal(const LegalityQuery &Query) const
unsigned getActionDefinitionsIdx(unsigned Opcode) const
LegalizeActionStep getAction(const LegalityQuery &Query) const
Determine what action should be taken to legalize the described instruction.
Describe properties that are true of each instruction in the target description file.
Interface to description of machine instruction set.
Definition MCInstrInfo.h:27
const MCInstrDesc & get(unsigned Opcode) const
Return the machine instruction descriptor that corresponds to the specified instruction opcode.
Definition MCInstrInfo.h:64
StringRef getName(unsigned Opcode) const
Returns the name for the instructions with the given opcode.
Definition MCInstrInfo.h:71
This holds information about one operand of a machine instruction, indicating the register class for ...
Definition MCInstrDesc.h:86
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Representation of each machine instruction.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallBitVector & set()
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
virtual const LegalizerInfo * getLegalizerInfo() const
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:45
static constexpr bool isKnownLE(const FixedOrScalableQuantity &LHS, const FixedOrScalableQuantity &RHS)
Definition TypeSize.h:230
static constexpr bool isKnownGE(const FixedOrScalableQuantity &LHS, const FixedOrScalableQuantity &RHS)
Definition TypeSize.h:237
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
@ FewerElements
The (vector) operation should be implemented by splitting it into sub-vectors where the operation is ...
@ Libcall
The operation should be implemented as a call to some kind of runtime support library.
@ Unsupported
This operation is completely unsupported on the target.
@ Lower
The operation itself must be expressed in terms of simpler actions on this target.
@ UseLegacyRules
Fall back onto the old rules.
@ WidenScalar
The operation should be implemented in terms of a wider scalar base-type.
@ Bitcast
Perform the operation on a different, but equivalently sized type.
@ NarrowScalar
The operation should be synthesized from multiple instructions acting on a narrower scalar base-type.
@ Custom
The target wants to do something special with this combination of operand and type.
@ NotFound
Sentinel value for when no action was found in the specified table.
@ MoreElements
The (vector) operation should be implemented by widening the input vector and ignoring the lanes adde...
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:310
bool isPreISelGenericOpcode(unsigned Opcode)
Check whether the given Opcode is a generic opcode that is not supposed to appear after ISel.
LLVM_ABI cl::opt< bool > DisableGISelLegalityCheck
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:167
const MachineInstr * machineFunctionIsIllegal(const MachineFunction &MF)
Checks that MIR is fully legal, returns an illegal instruction if it's not, nullptr otherwise.
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
DWARFExpression::Operation Op
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
The LegalityQuery object bundles together all the information that's needed to decide whether a given...
ArrayRef< MemDesc > MMODescrs
Operations which require memory can use this to place requirements on the memory type for each MMO.
ArrayRef< LLT > Types
LLVM_ABI raw_ostream & print(raw_ostream &OS) const
The result of a query.
LegalizeAction Action
The action to take or the final answer.