Skip to content

Commit bee977e

Browse files
author
John Stuart
committed
[GlobalISel] An optimizing MIR builder
It optimizes instructions while building them. It looks at the constness and values of its parameters to optimize them. It might build copies. In some cases partial constness or values are sufficient to optimize. The only context relied on are constants and their values. The builder will never use known bits to optimize. It always relies on legality before building. It uses undefness for optimization. The CSEMIRBuilder can build maybe illegal build vectors pass the legalizer without any means to verify. It is inconsistent whether to optimize fixed-length vectors. It never optimizes scalable vectors. The win of the new approach are separation of concern and correctness. TODO: move constant folding out of CSEMIRBuilder I put the new builder only into the post legalizer combiner to keep the patch small and show the demand for a legal builder. I like the GIConstant. It keeps the builder smaller and simpler.
1 parent 99b1a2a commit bee977e

File tree

18 files changed

+1420
-419
lines changed

18 files changed

+1420
-419
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===-- llvm/CodeGen/GlobalISel/OptMIRBuilder.h --*- C++ -*-==---------------//
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+
/// \file This file implements a legal version of MachineIRBuilder which
9+
/// optimizes insts while building.
10+
//===----------------------------------------------------------------------===//
11+
#ifndef LLVM_CODEGEN_GLOBALISEL_OPTMIRBUILDER_H
12+
#define LLVM_CODEGEN_GLOBALISEL_OPTMIRBUILDER_H
13+
14+
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
15+
16+
namespace llvm {
17+
18+
class LegalizerInfo;
19+
struct LegalityQuery;
20+
21+
/// OptMIRBuilder optimizes instructions while building them. It
22+
/// checks its operands whether they are constant or undef. It never
23+
/// checks whether an operand is defined by G_FSINCOS. It checks
24+
/// operands and registers for G_IMPLICIT_DEF, G_CONSTANT,
25+
/// G_BUILD_VECTOR, and G_SPLAT_VECTOR and nothing else.
26+
/// Based on undef, the constants and their values, it folds
27+
/// instructions into constants, undef, or other instructions. For
28+
/// optmizations and constant folding it relies on GIConstant.
29+
/// It can fold G_MUL into G_ADD and G_SUB. Before folding
30+
/// it always queries the legalizer. When it fails to fold, it
31+
/// delegates the building to the CSEMIRBuilder. It is the users
32+
/// responsibility to only attempt to build legal instructions pass
33+
/// the legalizer. OptMIRBuilder can safely be used in optimization
34+
/// passes pass the legalizer.
35+
class OptMIRBuilder : public CSEMIRBuilder {
36+
const LegalizerInfo *LI;
37+
const bool IsPrelegalize;
38+
39+
/// Legality tests.
40+
bool isPrelegalize() const;
41+
bool isLegal(const LegalityQuery &Query) const;
42+
bool isConstantLegal(LLT);
43+
bool isLegalOrBeforeLegalizer(const LegalityQuery &Query) const;
44+
bool isConstantLegalOrBeforeLegalizer(LLT);
45+
46+
/// Returns true if the register \p R is defined by G_IMPLICIT_DEF.
47+
bool isUndef(Register R) const;
48+
49+
/// Builds 0 - X.
50+
MachineInstrBuilder buildNegation(const DstOp &, const SrcOp &);
51+
52+
// Constants.
53+
MachineInstrBuilder buildGIConstant(const DstOp &DstOp, const GIConstant &);
54+
55+
/// Integer.
56+
MachineInstrBuilder optimizeAdd(unsigned Opc, ArrayRef<DstOp> DstOps,
57+
ArrayRef<SrcOp> SrcOps,
58+
std::optional<unsigned> Flag = std::nullopt);
59+
MachineInstrBuilder optimizeSub(unsigned Opc, ArrayRef<DstOp> DstOps,
60+
ArrayRef<SrcOp> SrcOps,
61+
std::optional<unsigned> Flag = std::nullopt);
62+
MachineInstrBuilder optimizeMul(unsigned Opc, ArrayRef<DstOp> DstOps,
63+
ArrayRef<SrcOp> SrcOps,
64+
std::optional<unsigned> Flag = std::nullopt);
65+
66+
public:
67+
OptMIRBuilder(MachineFunction &MF, GISelCSEInfo *CSEInfo,
68+
GISelChangeObserver &Observer, const LegalizerInfo *LI,
69+
bool IsPrelegalize)
70+
: LI(LI), IsPrelegalize(IsPrelegalize) {
71+
setMF(MF);
72+
setCSEInfo(CSEInfo);
73+
setChangeObserver(Observer);
74+
};
75+
76+
MachineInstrBuilder
77+
buildInstr(unsigned Opc, ArrayRef<DstOp> DstOps, ArrayRef<SrcOp> SrcOps,
78+
std::optional<unsigned> Flag = std::nullopt) override;
79+
};
80+
81+
} // namespace llvm
82+
83+
#endif // LLVM_CODEGEN_GLOBALISEL_OPTMIRBUILDER_H

llvm/include/llvm/CodeGen/GlobalISel/Utils.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,8 +635,28 @@ class GIConstant {
635635
/// Returns the value, if this constant is a scalar.
636636
APInt getScalarValue() const;
637637

638+
/// Returns the value, if this constant is a scalable vector.
639+
APInt getSplatValue() const;
640+
641+
/// Returns the values, if this constant is a fixed vector.
642+
ArrayRef<APInt> getAsArrayRef() const;
643+
638644
static std::optional<GIConstant> getConstant(Register Const,
639645
const MachineRegisterInfo &MRI);
646+
647+
/// Returns a new constant where add(this, x) was applied.
648+
GIConstant add(const GIConstant &) const;
649+
650+
/// Returns a new constant where sub(this, x) was applied.
651+
GIConstant sub(const GIConstant &) const;
652+
653+
/// Returns a new constant where mul(this, x) was applied.
654+
GIConstant mul(const GIConstant &) const;
655+
656+
bool isZero() const;
657+
bool isOne() const;
658+
bool isTwo() const;
659+
bool isAllOnes() const;
640660
};
641661

642662
/// An floating-point-like constant.

llvm/lib/CodeGen/GlobalISel/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_llvm_component_library(LLVMGlobalISel
2626
Localizer.cpp
2727
LostDebugLocObserver.cpp
2828
MachineIRBuilder.cpp
29+
OptMIRBuilder.cpp
2930
RegBankSelect.cpp
3031
Utils.cpp
3132

llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,15 +199,12 @@ MachineInstrBuilder CSEMIRBuilder::buildInstr(unsigned Opc,
199199
}
200200
break;
201201
}
202-
case TargetOpcode::G_ADD:
203202
case TargetOpcode::G_PTR_ADD:
204203
case TargetOpcode::G_AND:
205204
case TargetOpcode::G_ASHR:
206205
case TargetOpcode::G_LSHR:
207-
case TargetOpcode::G_MUL:
208206
case TargetOpcode::G_OR:
209207
case TargetOpcode::G_SHL:
210-
case TargetOpcode::G_SUB:
211208
case TargetOpcode::G_XOR:
212209
case TargetOpcode::G_UDIV:
213210
case TargetOpcode::G_SDIV:

llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,12 @@ MachineInstrBuilder MachineIRBuilder::buildConstant(const DstOp &Res,
321321
assert(EltTy.getScalarSizeInBits() == Val.getBitWidth() &&
322322
"creating constant with the wrong size");
323323

324-
assert(!Ty.isScalableVector() &&
325-
"unexpected scalable vector in buildConstant");
324+
if (Ty.isScalableVector()) {
325+
auto Const = buildInstr(TargetOpcode::G_CONSTANT)
326+
.addDef(getMRI()->createGenericVirtualRegister(EltTy))
327+
.addCImm(&Val);
328+
return buildSplatVector(Res, Const);
329+
}
326330

327331
if (Ty.isFixedVector()) {
328332
auto Const = buildInstr(TargetOpcode::G_CONSTANT)
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
//===-- llvm/CodeGen/GlobalISel/OptMIRBuilder.cpp -----------------*- C++-*-==//
2+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
//===----------------------------------------------------------------------===//
7+
/// \file
8+
/// This file implements the OptMIRBuilder class which optimizes as it builds
9+
/// instructions.
10+
//===----------------------------------------------------------------------===//
11+
//
12+
13+
#include "llvm/CodeGen/GlobalISel/OptMIRBuilder.h"
14+
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
15+
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
16+
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
17+
#include "llvm/CodeGen/GlobalISel/Utils.h"
18+
#include "llvm/CodeGen/TargetOpcodes.h"
19+
20+
using namespace llvm;
21+
22+
bool OptMIRBuilder::isPrelegalize() const { return IsPrelegalize; }
23+
24+
bool OptMIRBuilder::isLegal(const LegalityQuery &Query) const {
25+
assert(LI != nullptr && "legalizer info is not available");
26+
return LI->isLegal(Query);
27+
}
28+
29+
bool OptMIRBuilder::isConstantLegal(LLT Ty) {
30+
if (Ty.isScalar())
31+
return isLegal({TargetOpcode::G_CONSTANT, {Ty}});
32+
33+
LLT EltTy = Ty.getElementType();
34+
if (Ty.isFixedVector())
35+
return isLegal({TargetOpcode::G_BUILD_VECTOR, {Ty, EltTy}}) &&
36+
isLegal({TargetOpcode::G_CONSTANT, {EltTy}});
37+
38+
// scalable vector
39+
assert(Ty.isScalableVector() && "Unexpected LLT");
40+
return isLegal({TargetOpcode::G_SPLAT_VECTOR, {Ty, EltTy}}) &&
41+
isLegal({TargetOpcode::G_CONSTANT, {EltTy}});
42+
}
43+
44+
bool OptMIRBuilder::isLegalOrBeforeLegalizer(const LegalityQuery &Query) const {
45+
return isPrelegalize() || isLegal(Query);
46+
}
47+
48+
bool OptMIRBuilder::isConstantLegalOrBeforeLegalizer(LLT Ty) {
49+
return isPrelegalize() || isConstantLegal(Ty);
50+
}
51+
52+
bool OptMIRBuilder::isUndef(Register Reg) const {
53+
const MachineInstr *MI = getMRI()->getVRegDef(Reg);
54+
return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF;
55+
}
56+
57+
MachineInstrBuilder OptMIRBuilder::buildNegation(const DstOp &DstOp,
58+
const SrcOp &SrcOp) {
59+
LLT DstTy = DstOp.getLLTTy(*getMRI());
60+
61+
auto Zero = buildConstant(DstTy, 0);
62+
return buildSub(DstOp, Zero, SrcOp);
63+
}
64+
65+
MachineInstrBuilder OptMIRBuilder::buildGIConstant(const DstOp &DstOp,
66+
const GIConstant &Const) {
67+
LLT DstTy = DstOp.getLLTTy(*getMRI());
68+
69+
switch (Const.getKind()) {
70+
case GIConstant::GIConstantKind::Scalar:
71+
return buildConstant(DstOp, Const.getScalarValue());
72+
case GIConstant::GIConstantKind::FixedVector:
73+
return buildBuildVectorConstant(DstOp, Const.getAsArrayRef());
74+
case GIConstant::GIConstantKind::ScalableVector: {
75+
auto Constant =
76+
buildConstant(DstTy.getElementType(), Const.getSplatValue());
77+
return buildSplatVector(DstOp, Constant);
78+
}
79+
}
80+
}
81+
82+
MachineInstrBuilder OptMIRBuilder::optimizeAdd(unsigned Opc,
83+
ArrayRef<DstOp> DstOps,
84+
ArrayRef<SrcOp> SrcOps,
85+
std::optional<unsigned> Flag) {
86+
assert(SrcOps.size() == 2 && "Invalid sources");
87+
assert(DstOps.size() == 1 && "Invalid dsts");
88+
89+
LLT DstTy = DstOps[0].getLLTTy(*getMRI());
90+
91+
if (isUndef(SrcOps[1].getReg()) || isUndef(SrcOps[0].getReg()))
92+
return buildUndef(DstTy);
93+
94+
std::optional<GIConstant> RHS =
95+
GIConstant::getConstant(SrcOps[1].getReg(), *getMRI());
96+
if (!RHS)
97+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
98+
99+
if (RHS->isZero())
100+
return buildCopy(DstOps[0], SrcOps[0]);
101+
102+
if (!isConstantLegalOrBeforeLegalizer(DstTy))
103+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
104+
105+
std::optional<GIConstant> LHS =
106+
GIConstant::getConstant(SrcOps[0].getReg(), *getMRI());
107+
if (!LHS)
108+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
109+
110+
GIConstant Add = LHS->add(*RHS);
111+
112+
return buildGIConstant(DstOps[0], Add);
113+
}
114+
115+
MachineInstrBuilder OptMIRBuilder::optimizeSub(unsigned Opc,
116+
ArrayRef<DstOp> DstOps,
117+
ArrayRef<SrcOp> SrcOps,
118+
std::optional<unsigned> Flag) {
119+
assert(SrcOps.size() == 2 && "Invalid sources");
120+
assert(DstOps.size() == 1 && "Invalid dsts");
121+
122+
LLT DstTy = DstOps[0].getLLTTy(*getMRI());
123+
124+
if (isUndef(SrcOps[1].getReg()) || isUndef(SrcOps[0].getReg()))
125+
return buildUndef(DstTy);
126+
127+
std::optional<GIConstant> RHS =
128+
GIConstant::getConstant(SrcOps[1].getReg(), *getMRI());
129+
if (!RHS)
130+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
131+
132+
if (RHS->isZero())
133+
return buildCopy(DstOps[0], SrcOps[0]);
134+
135+
if (!isConstantLegalOrBeforeLegalizer(DstTy))
136+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
137+
138+
std::optional<GIConstant> LHS =
139+
GIConstant::getConstant(SrcOps[0].getReg(), *getMRI());
140+
if (!LHS)
141+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
142+
143+
GIConstant Sub = LHS->sub(*RHS);
144+
145+
return buildGIConstant(DstOps[0], Sub);
146+
}
147+
148+
MachineInstrBuilder OptMIRBuilder::optimizeMul(unsigned Opc,
149+
ArrayRef<DstOp> DstOps,
150+
ArrayRef<SrcOp> SrcOps,
151+
std::optional<unsigned> Flag) {
152+
assert(SrcOps.size() == 2 && "Invalid sources");
153+
assert(DstOps.size() == 1 && "Invalid dsts");
154+
155+
LLT DstTy = DstOps[0].getLLTTy(*getMRI());
156+
157+
if ((isUndef(SrcOps[1].getReg()) || isUndef(SrcOps[0].getReg())) &&
158+
isConstantLegalOrBeforeLegalizer(DstTy))
159+
return buildConstant(DstTy, 0);
160+
161+
std::optional<GIConstant> RHS =
162+
GIConstant::getConstant(SrcOps[1].getReg(), *getMRI());
163+
if (!RHS)
164+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
165+
166+
if (RHS->isZero() && isConstantLegalOrBeforeLegalizer(DstTy))
167+
return buildConstant(DstTy, 0);
168+
169+
if (RHS->isOne())
170+
return buildCopy(DstOps[0], SrcOps[0]);
171+
172+
if (RHS->isAllOnes() && isConstantLegalOrBeforeLegalizer(DstTy) &&
173+
isLegalOrBeforeLegalizer({TargetOpcode::G_SUB, {DstTy}}))
174+
return buildNegation(DstOps[0], SrcOps[0]);
175+
176+
if (RHS->isTwo() && isLegalOrBeforeLegalizer({TargetOpcode::G_ADD, {DstTy}}))
177+
return buildAdd(DstOps[0], SrcOps[0], SrcOps[0]);
178+
179+
if (!isConstantLegalOrBeforeLegalizer(DstTy))
180+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
181+
182+
std::optional<GIConstant> LHS =
183+
GIConstant::getConstant(SrcOps[0].getReg(), *getMRI());
184+
if (!LHS)
185+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
186+
187+
GIConstant Mul = LHS->mul(*RHS);
188+
189+
return buildGIConstant(DstOps[0], Mul);
190+
}
191+
192+
MachineInstrBuilder OptMIRBuilder::buildInstr(unsigned Opc,
193+
ArrayRef<DstOp> DstOps,
194+
ArrayRef<SrcOp> SrcOps,
195+
std::optional<unsigned> Flag) {
196+
switch (Opc) {
197+
case TargetOpcode::G_ADD:
198+
return optimizeAdd(Opc, DstOps, SrcOps, Flag);
199+
case TargetOpcode::G_SUB:
200+
return optimizeSub(Opc, DstOps, SrcOps, Flag);
201+
case TargetOpcode::G_MUL:
202+
return optimizeMul(Opc, DstOps, SrcOps, Flag);
203+
default:
204+
return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
205+
}
206+
}

0 commit comments

Comments
 (0)