Skip to content

Commit 330fa7d

Browse files
[TargetLowering] Deduplicate choosing InlineAsm constraint between ISels (#67057)
Given a list of constraints for InlineAsm (ex. "imr") I'm looking to modify the order in which they are chosen. Before doing so, I noticed a fair amount of logic is duplicated between SelectionDAGISel and GlobalISel for this. That is because SelectionDAGISel is also trying to lower immediates during selection. If we detangle these concerns into: 1. choose the preferred constraint 2. attempt to lower that constraint Then we can slide down the list of constraints until we find one that can be lowered. That allows the implementation to be shared between instruction selection frameworks. This makes it so that later I might only need to adjust the priority of constraints in one place, and have both selectors behave the same.
1 parent 9310baa commit 330fa7d

31 files changed

+142
-184
lines changed

llvm/include/llvm/CodeGen/TargetLowering.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4849,6 +4849,15 @@ class TargetLowering : public TargetLoweringBase {
48494849
/// Given a constraint, return the type of constraint it is for this target.
48504850
virtual ConstraintType getConstraintType(StringRef Constraint) const;
48514851

4852+
using ConstraintPair = std::pair<StringRef, TargetLowering::ConstraintType>;
4853+
using ConstraintGroup = SmallVector<ConstraintPair>;
4854+
/// Given an OpInfo with list of constraints codes as strings, return a
4855+
/// sorted Vector of pairs of constraint codes and their types in priority of
4856+
/// what we'd prefer to lower them as. This may contain immediates that
4857+
/// cannot be lowered, but it is meant to be a machine agnostic order of
4858+
/// preferences.
4859+
ConstraintGroup getConstraintPreferences(AsmOperandInfo &OpInfo) const;
4860+
48524861
/// Given a physical register constraint (e.g. {edx}), return the register
48534862
/// number and the register class for the register.
48544863
///
@@ -4882,7 +4891,7 @@ class TargetLowering : public TargetLoweringBase {
48824891

48834892
/// Lower the specified operand into the Ops vector. If it is invalid, don't
48844893
/// add anything to Ops.
4885-
virtual void LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint,
4894+
virtual void LowerAsmOperandForConstraint(SDValue Op, StringRef Constraint,
48864895
std::vector<SDValue> &Ops,
48874896
SelectionDAG &DAG) const;
48884897

llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp

Lines changed: 12 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -133,71 +133,6 @@ static void getRegistersForValue(MachineFunction &MF,
133133
}
134134
}
135135

136-
/// Return an integer indicating how general CT is.
137-
static unsigned getConstraintGenerality(TargetLowering::ConstraintType CT) {
138-
switch (CT) {
139-
case TargetLowering::C_Immediate:
140-
case TargetLowering::C_Other:
141-
case TargetLowering::C_Unknown:
142-
return 0;
143-
case TargetLowering::C_Register:
144-
return 1;
145-
case TargetLowering::C_RegisterClass:
146-
return 2;
147-
case TargetLowering::C_Memory:
148-
case TargetLowering::C_Address:
149-
return 3;
150-
}
151-
llvm_unreachable("Invalid constraint type");
152-
}
153-
154-
static void chooseConstraint(TargetLowering::AsmOperandInfo &OpInfo,
155-
const TargetLowering *TLI) {
156-
assert(OpInfo.Codes.size() > 1 && "Doesn't have multiple constraint options");
157-
unsigned BestIdx = 0;
158-
TargetLowering::ConstraintType BestType = TargetLowering::C_Unknown;
159-
int BestGenerality = -1;
160-
161-
// Loop over the options, keeping track of the most general one.
162-
for (unsigned i = 0, e = OpInfo.Codes.size(); i != e; ++i) {
163-
TargetLowering::ConstraintType CType =
164-
TLI->getConstraintType(OpInfo.Codes[i]);
165-
166-
// Indirect 'other' or 'immediate' constraints are not allowed.
167-
if (OpInfo.isIndirect && !(CType == TargetLowering::C_Memory ||
168-
CType == TargetLowering::C_Register ||
169-
CType == TargetLowering::C_RegisterClass))
170-
continue;
171-
172-
// If this is an 'other' or 'immediate' constraint, see if the operand is
173-
// valid for it. For example, on X86 we might have an 'rI' constraint. If
174-
// the operand is an integer in the range [0..31] we want to use I (saving a
175-
// load of a register), otherwise we must use 'r'.
176-
if (CType == TargetLowering::C_Other ||
177-
CType == TargetLowering::C_Immediate) {
178-
assert(OpInfo.Codes[i].size() == 1 &&
179-
"Unhandled multi-letter 'other' constraint");
180-
// FIXME: prefer immediate constraints if the target allows it
181-
}
182-
183-
// Things with matching constraints can only be registers, per gcc
184-
// documentation. This mainly affects "g" constraints.
185-
if (CType == TargetLowering::C_Memory && OpInfo.hasMatchingInput())
186-
continue;
187-
188-
// This constraint letter is more general than the previous one, use it.
189-
int Generality = getConstraintGenerality(CType);
190-
if (Generality > BestGenerality) {
191-
BestType = CType;
192-
BestIdx = i;
193-
BestGenerality = Generality;
194-
}
195-
}
196-
197-
OpInfo.ConstraintCode = OpInfo.Codes[BestIdx];
198-
OpInfo.ConstraintType = BestType;
199-
}
200-
201136
static void computeConstraintToUse(const TargetLowering *TLI,
202137
TargetLowering::AsmOperandInfo &OpInfo) {
203138
assert(!OpInfo.Codes.empty() && "Must have at least one constraint");
@@ -207,7 +142,18 @@ static void computeConstraintToUse(const TargetLowering *TLI,
207142
OpInfo.ConstraintCode = OpInfo.Codes[0];
208143
OpInfo.ConstraintType = TLI->getConstraintType(OpInfo.ConstraintCode);
209144
} else {
210-
chooseConstraint(OpInfo, TLI);
145+
TargetLowering::ConstraintGroup G = TLI->getConstraintPreferences(OpInfo);
146+
if (G.empty())
147+
return;
148+
// FIXME: prefer immediate constraints if the target allows it
149+
unsigned BestIdx = 0;
150+
for (const unsigned E = G.size();
151+
BestIdx < E && (G[BestIdx].second == TargetLowering::C_Other ||
152+
G[BestIdx].second == TargetLowering::C_Immediate);
153+
++BestIdx)
154+
;
155+
OpInfo.ConstraintCode = G[BestIdx].first;
156+
OpInfo.ConstraintType = G[BestIdx].second;
211157
}
212158

213159
// 'X' matches anything.

llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5379,11 +5379,12 @@ SDValue TargetLowering::LowerAsmOutputForConstraint(
53795379
/// Lower the specified operand into the Ops vector.
53805380
/// If it is invalid, don't add anything to Ops.
53815381
void TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
5382-
std::string &Constraint,
5382+
StringRef Constraint,
53835383
std::vector<SDValue> &Ops,
53845384
SelectionDAG &DAG) const {
53855385

5386-
if (Constraint.length() > 1) return;
5386+
if (Constraint.size() > 1)
5387+
return;
53875388

53885389
char ConstraintLetter = Constraint[0];
53895390
switch (ConstraintLetter) {
@@ -5702,20 +5703,27 @@ TargetLowering::ParseConstraints(const DataLayout &DL,
57025703
return ConstraintOperands;
57035704
}
57045705

5705-
/// Return an integer indicating how general CT is.
5706-
static unsigned getConstraintGenerality(TargetLowering::ConstraintType CT) {
5706+
/// Return a number indicating our preference for chosing a type of constraint
5707+
/// over another, for the purpose of sorting them. Immediates are almost always
5708+
/// preferrable (when they can be emitted). A higher return value means a
5709+
/// stronger preference for one constraint type relative to another.
5710+
/// FIXME: We should prefer registers over memory but doing so may lead to
5711+
/// unrecoverable register exhaustion later.
5712+
/// https://github.com/llvm/llvm-project/issues/20571
5713+
static unsigned getConstraintPiority(TargetLowering::ConstraintType CT) {
57075714
switch (CT) {
57085715
case TargetLowering::C_Immediate:
57095716
case TargetLowering::C_Other:
5710-
case TargetLowering::C_Unknown:
5711-
return 0;
5712-
case TargetLowering::C_Register:
5713-
return 1;
5714-
case TargetLowering::C_RegisterClass:
5715-
return 2;
5717+
return 4;
57165718
case TargetLowering::C_Memory:
57175719
case TargetLowering::C_Address:
57185720
return 3;
5721+
case TargetLowering::C_RegisterClass:
5722+
return 2;
5723+
case TargetLowering::C_Register:
5724+
return 1;
5725+
case TargetLowering::C_Unknown:
5726+
return 0;
57195727
}
57205728
llvm_unreachable("Invalid constraint type");
57215729
}
@@ -5813,59 +5821,51 @@ TargetLowering::ConstraintWeight
58135821
/// 2) Otherwise, pick the most general constraint present. This prefers
58145822
/// 'm' over 'r', for example.
58155823
///
5816-
static void ChooseConstraint(TargetLowering::AsmOperandInfo &OpInfo,
5817-
const TargetLowering &TLI,
5818-
SDValue Op, SelectionDAG *DAG) {
5819-
assert(OpInfo.Codes.size() > 1 && "Doesn't have multiple constraint options");
5820-
unsigned BestIdx = 0;
5821-
TargetLowering::ConstraintType BestType = TargetLowering::C_Unknown;
5822-
int BestGenerality = -1;
5824+
TargetLowering::ConstraintGroup TargetLowering::getConstraintPreferences(
5825+
TargetLowering::AsmOperandInfo &OpInfo) const {
5826+
ConstraintGroup Ret;
58235827

5824-
// Loop over the options, keeping track of the most general one.
5825-
for (unsigned i = 0, e = OpInfo.Codes.size(); i != e; ++i) {
5826-
TargetLowering::ConstraintType CType =
5827-
TLI.getConstraintType(OpInfo.Codes[i]);
5828+
Ret.reserve(OpInfo.Codes.size());
5829+
for (StringRef Code : OpInfo.Codes) {
5830+
TargetLowering::ConstraintType CType = getConstraintType(Code);
58285831

58295832
// Indirect 'other' or 'immediate' constraints are not allowed.
58305833
if (OpInfo.isIndirect && !(CType == TargetLowering::C_Memory ||
58315834
CType == TargetLowering::C_Register ||
58325835
CType == TargetLowering::C_RegisterClass))
58335836
continue;
58345837

5835-
// If this is an 'other' or 'immediate' constraint, see if the operand is
5836-
// valid for it. For example, on X86 we might have an 'rI' constraint. If
5837-
// the operand is an integer in the range [0..31] we want to use I (saving a
5838-
// load of a register), otherwise we must use 'r'.
5839-
if ((CType == TargetLowering::C_Other ||
5840-
CType == TargetLowering::C_Immediate) && Op.getNode()) {
5841-
assert(OpInfo.Codes[i].size() == 1 &&
5842-
"Unhandled multi-letter 'other' constraint");
5843-
std::vector<SDValue> ResultOps;
5844-
TLI.LowerAsmOperandForConstraint(Op, OpInfo.Codes[i],
5845-
ResultOps, *DAG);
5846-
if (!ResultOps.empty()) {
5847-
BestType = CType;
5848-
BestIdx = i;
5849-
break;
5850-
}
5851-
}
5852-
58535838
// Things with matching constraints can only be registers, per gcc
58545839
// documentation. This mainly affects "g" constraints.
58555840
if (CType == TargetLowering::C_Memory && OpInfo.hasMatchingInput())
58565841
continue;
58575842

5858-
// This constraint letter is more general than the previous one, use it.
5859-
int Generality = getConstraintGenerality(CType);
5860-
if (Generality > BestGenerality) {
5861-
BestType = CType;
5862-
BestIdx = i;
5863-
BestGenerality = Generality;
5864-
}
5843+
Ret.emplace_back(Code, CType);
58655844
}
58665845

5867-
OpInfo.ConstraintCode = OpInfo.Codes[BestIdx];
5868-
OpInfo.ConstraintType = BestType;
5846+
std::sort(Ret.begin(), Ret.end(), [](ConstraintPair a, ConstraintPair b) {
5847+
return getConstraintPiority(a.second) > getConstraintPiority(b.second);
5848+
});
5849+
5850+
return Ret;
5851+
}
5852+
5853+
/// If we have an immediate, see if we can lower it. Return true if we can,
5854+
/// false otherwise.
5855+
static bool lowerImmediateIfPossible(TargetLowering::ConstraintPair &P,
5856+
SDValue Op, SelectionDAG *DAG,
5857+
const TargetLowering &TLI) {
5858+
5859+
assert((P.second == TargetLowering::C_Other ||
5860+
P.second == TargetLowering::C_Immediate) &&
5861+
"need immediate or other");
5862+
5863+
if (!Op.getNode())
5864+
return false;
5865+
5866+
std::vector<SDValue> ResultOps;
5867+
TLI.LowerAsmOperandForConstraint(Op, P.first, ResultOps, *DAG);
5868+
return !ResultOps.empty();
58695869
}
58705870

58715871
/// Determines the constraint code and constraint type to use for the specific
@@ -5880,7 +5880,20 @@ void TargetLowering::ComputeConstraintToUse(AsmOperandInfo &OpInfo,
58805880
OpInfo.ConstraintCode = OpInfo.Codes[0];
58815881
OpInfo.ConstraintType = getConstraintType(OpInfo.ConstraintCode);
58825882
} else {
5883-
ChooseConstraint(OpInfo, *this, Op, DAG);
5883+
ConstraintGroup G = getConstraintPreferences(OpInfo);
5884+
if (G.empty())
5885+
return;
5886+
5887+
unsigned BestIdx = 0;
5888+
for (const unsigned E = G.size();
5889+
BestIdx < E && (G[BestIdx].second == TargetLowering::C_Other ||
5890+
G[BestIdx].second == TargetLowering::C_Immediate);
5891+
++BestIdx)
5892+
if (lowerImmediateIfPossible(G[BestIdx], Op, DAG, *this))
5893+
break;
5894+
5895+
OpInfo.ConstraintCode = G[BestIdx].first;
5896+
OpInfo.ConstraintType = G[BestIdx].second;
58845897
}
58855898

58865899
// 'X' matches anything.

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10310,12 +10310,12 @@ EVT AArch64TargetLowering::getAsmOperandValueType(const DataLayout &DL,
1031010310
/// LowerAsmOperandForConstraint - Lower the specified operand into the Ops
1031110311
/// vector. If it is invalid, don't add anything to Ops.
1031210312
void AArch64TargetLowering::LowerAsmOperandForConstraint(
10313-
SDValue Op, std::string &Constraint, std::vector<SDValue> &Ops,
10313+
SDValue Op, StringRef Constraint, std::vector<SDValue> &Ops,
1031410314
SelectionDAG &DAG) const {
1031510315
SDValue Result;
1031610316

1031710317
// Currently only support length 1 constraints.
10318-
if (Constraint.length() != 1)
10318+
if (Constraint.size() != 1)
1031910319
return;
1032010320

1032110321
char ConstraintLetter = Constraint[0];

llvm/lib/Target/AArch64/AArch64ISelLowering.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,7 @@ class AArch64TargetLowering : public TargetLowering {
11681168

11691169
const char *LowerXConstraint(EVT ConstraintVT) const override;
11701170

1171-
void LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint,
1171+
void LowerAsmOperandForConstraint(SDValue Op, StringRef Constraint,
11721172
std::vector<SDValue> &Ops,
11731173
SelectionDAG &DAG) const override;
11741174

llvm/lib/Target/AMDGPU/SIISelLowering.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13872,7 +13872,7 @@ static uint64_t clearUnusedBits(uint64_t Val, unsigned Size) {
1387213872
}
1387313873

1387413874
void SITargetLowering::LowerAsmOperandForConstraint(SDValue Op,
13875-
std::string &Constraint,
13875+
StringRef Constraint,
1387613876
std::vector<SDValue> &Ops,
1387713877
SelectionDAG &DAG) const {
1387813878
if (isImmConstraint(Constraint)) {
@@ -13921,8 +13921,7 @@ bool SITargetLowering::getAsmOperandConstVal(SDValue Op, uint64_t &Val) const {
1392113921
return false;
1392213922
}
1392313923

13924-
bool SITargetLowering::checkAsmConstraintVal(SDValue Op,
13925-
const std::string &Constraint,
13924+
bool SITargetLowering::checkAsmConstraintVal(SDValue Op, StringRef Constraint,
1392613925
uint64_t Val) const {
1392713926
if (Constraint.size() == 1) {
1392813927
switch (Constraint[0]) {

llvm/lib/Target/AMDGPU/SIISelLowering.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,13 +468,11 @@ class SITargetLowering final : public AMDGPUTargetLowering {
468468
getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
469469
StringRef Constraint, MVT VT) const override;
470470
ConstraintType getConstraintType(StringRef Constraint) const override;
471-
void LowerAsmOperandForConstraint(SDValue Op,
472-
std::string &Constraint,
471+
void LowerAsmOperandForConstraint(SDValue Op, StringRef Constraint,
473472
std::vector<SDValue> &Ops,
474473
SelectionDAG &DAG) const override;
475474
bool getAsmOperandConstVal(SDValue Op, uint64_t &Val) const;
476-
bool checkAsmConstraintVal(SDValue Op,
477-
const std::string &Constraint,
475+
bool checkAsmConstraintVal(SDValue Op, StringRef Constraint,
478476
uint64_t Val) const;
479477
bool checkAsmConstraintValA(SDValue Op,
480478
uint64_t Val,

llvm/lib/Target/ARM/ARMISelLowering.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@
111111
#include <iterator>
112112
#include <limits>
113113
#include <optional>
114-
#include <string>
115114
#include <tuple>
116115
#include <utility>
117116
#include <vector>
@@ -20244,14 +20243,14 @@ bool ARMTargetLowering::ExpandInlineAsm(CallInst *CI) const {
2024420243
return false;
2024520244

2024620245
InlineAsm *IA = cast<InlineAsm>(CI->getCalledOperand());
20247-
std::string AsmStr = IA->getAsmString();
20246+
StringRef AsmStr = IA->getAsmString();
2024820247
SmallVector<StringRef, 4> AsmPieces;
2024920248
SplitString(AsmStr, AsmPieces, ";\n");
2025020249

2025120250
switch (AsmPieces.size()) {
2025220251
default: return false;
2025320252
case 1:
20254-
AsmStr = std::string(AsmPieces[0]);
20253+
AsmStr = AsmPieces[0];
2025520254
AsmPieces.clear();
2025620255
SplitString(AsmStr, AsmPieces, " \t,");
2025720256

@@ -20431,13 +20430,14 @@ RCPair ARMTargetLowering::getRegForInlineAsmConstraint(
2043120430
/// LowerAsmOperandForConstraint - Lower the specified operand into the Ops
2043220431
/// vector. If it is invalid, don't add anything to Ops.
2043320432
void ARMTargetLowering::LowerAsmOperandForConstraint(SDValue Op,
20434-
std::string &Constraint,
20435-
std::vector<SDValue>&Ops,
20433+
StringRef Constraint,
20434+
std::vector<SDValue> &Ops,
2043620435
SelectionDAG &DAG) const {
2043720436
SDValue Result;
2043820437

2043920438
// Currently only support length 1 constraints.
20440-
if (Constraint.length() != 1) return;
20439+
if (Constraint.size() != 1)
20440+
return;
2044120441

2044220442
char ConstraintLetter = Constraint[0];
2044320443
switch (ConstraintLetter) {

llvm/lib/Target/ARM/ARMISelLowering.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ class VectorType;
530530
/// vector. If it is invalid, don't add anything to Ops. If hasMemory is
531531
/// true it means one of the asm constraint of the inline asm instruction
532532
/// being processed is 'm'.
533-
void LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint,
533+
void LowerAsmOperandForConstraint(SDValue Op, StringRef Constraint,
534534
std::vector<SDValue> &Ops,
535535
SelectionDAG &DAG) const override;
536536

0 commit comments

Comments
 (0)