forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHexagonConstExtenders.cpp
2023 lines (1857 loc) · 70.6 KB
/
HexagonConstExtenders.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//===- HexagonConstExtenders.cpp ------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "HexagonInstrInfo.h"
#include "HexagonRegisterInfo.h"
#include "HexagonSubtarget.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#include <set>
#include <utility>
#include <vector>
#define DEBUG_TYPE "hexagon-cext-opt"
using namespace llvm;
static cl::opt<unsigned> CountThreshold("hexagon-cext-threshold",
cl::init(3), cl::Hidden, cl::ZeroOrMore,
cl::desc("Minimum number of extenders to trigger replacement"));
static cl::opt<unsigned> ReplaceLimit("hexagon-cext-limit", cl::init(0),
cl::Hidden, cl::ZeroOrMore, cl::desc("Maximum number of replacements"));
namespace llvm {
void initializeHexagonConstExtendersPass(PassRegistry&);
FunctionPass *createHexagonConstExtenders();
}
static int32_t adjustUp(int32_t V, uint8_t A, uint8_t O) {
assert(isPowerOf2_32(A));
int32_t U = (V & -A) + O;
return U >= V ? U : U+A;
}
static int32_t adjustDown(int32_t V, uint8_t A, uint8_t O) {
assert(isPowerOf2_32(A));
int32_t U = (V & -A) + O;
return U <= V ? U : U-A;
}
namespace {
struct OffsetRange {
// The range of values between Min and Max that are of form Align*N+Offset,
// for some integer N. Min and Max are required to be of that form as well,
// except in the case of an empty range.
int32_t Min = INT_MIN, Max = INT_MAX;
uint8_t Align = 1;
uint8_t Offset = 0;
OffsetRange() = default;
OffsetRange(int32_t L, int32_t H, uint8_t A, uint8_t O = 0)
: Min(L), Max(H), Align(A), Offset(O) {}
OffsetRange &intersect(OffsetRange A) {
if (Align < A.Align)
std::swap(*this, A);
// Align >= A.Align.
if (Offset >= A.Offset && (Offset - A.Offset) % A.Align == 0) {
Min = adjustUp(std::max(Min, A.Min), Align, Offset);
Max = adjustDown(std::min(Max, A.Max), Align, Offset);
} else {
// Make an empty range.
Min = 0;
Max = -1;
}
// Canonicalize empty ranges.
if (Min > Max)
std::tie(Min, Max, Align) = std::make_tuple(0, -1, 1);
return *this;
}
OffsetRange &shift(int32_t S) {
Min += S;
Max += S;
Offset = (Offset+S) % Align;
return *this;
}
OffsetRange &extendBy(int32_t D) {
// If D < 0, extend Min, otherwise extend Max.
assert(D % Align == 0);
if (D < 0)
Min = (INT_MIN-D < Min) ? Min+D : INT_MIN;
else
Max = (INT_MAX-D > Max) ? Max+D : INT_MAX;
return *this;
}
bool empty() const {
return Min > Max;
}
bool contains(int32_t V) const {
return Min <= V && V <= Max && (V-Offset) % Align == 0;
}
bool operator==(const OffsetRange &R) const {
return Min == R.Min && Max == R.Max && Align == R.Align;
}
bool operator!=(const OffsetRange &R) const {
return !operator==(R);
}
bool operator<(const OffsetRange &R) const {
if (Min != R.Min)
return Min < R.Min;
if (Max != R.Max)
return Max < R.Max;
return Align < R.Align;
}
static OffsetRange zero() { return {0, 0, 1}; }
};
struct RangeTree {
struct Node {
Node(const OffsetRange &R) : MaxEnd(R.Max), Range(R) {}
unsigned Height = 1;
unsigned Count = 1;
int32_t MaxEnd;
const OffsetRange &Range;
Node *Left = nullptr, *Right = nullptr;
};
Node *Root = nullptr;
void add(const OffsetRange &R) {
Root = add(Root, R);
}
void erase(const Node *N) {
Root = remove(Root, N);
delete N;
}
void order(SmallVectorImpl<Node*> &Seq) const {
order(Root, Seq);
}
SmallVector<Node*,8> nodesWith(int32_t P, bool CheckAlign = true) {
SmallVector<Node*,8> Nodes;
nodesWith(Root, P, CheckAlign, Nodes);
return Nodes;
}
void dump() const;
~RangeTree() {
SmallVector<Node*,8> Nodes;
order(Nodes);
for (Node *N : Nodes)
delete N;
}
private:
void dump(const Node *N) const;
void order(Node *N, SmallVectorImpl<Node*> &Seq) const;
void nodesWith(Node *N, int32_t P, bool CheckA,
SmallVectorImpl<Node*> &Seq) const;
Node *add(Node *N, const OffsetRange &R);
Node *remove(Node *N, const Node *D);
Node *rotateLeft(Node *Lower, Node *Higher);
Node *rotateRight(Node *Lower, Node *Higher);
unsigned height(Node *N) {
return N != nullptr ? N->Height : 0;
}
Node *update(Node *N) {
assert(N != nullptr);
N->Height = 1 + std::max(height(N->Left), height(N->Right));
if (N->Left)
N->MaxEnd = std::max(N->MaxEnd, N->Left->MaxEnd);
if (N->Right)
N->MaxEnd = std::max(N->MaxEnd, N->Right->MaxEnd);
return N;
}
Node *rebalance(Node *N) {
assert(N != nullptr);
int32_t Balance = height(N->Right) - height(N->Left);
if (Balance < -1)
return rotateRight(N->Left, N);
if (Balance > 1)
return rotateLeft(N->Right, N);
return N;
}
};
struct Loc {
MachineBasicBlock *Block = nullptr;
MachineBasicBlock::iterator At;
Loc(MachineBasicBlock *B, MachineBasicBlock::iterator It)
: Block(B), At(It) {
if (B->end() == It) {
Pos = -1;
} else {
assert(It->getParent() == B);
Pos = std::distance(B->begin(), It);
}
}
bool operator<(Loc A) const {
if (Block != A.Block)
return Block->getNumber() < A.Block->getNumber();
if (A.Pos == -1)
return Pos != A.Pos;
return Pos != -1 && Pos < A.Pos;
}
private:
int Pos = 0;
};
struct HexagonConstExtenders : public MachineFunctionPass {
static char ID;
HexagonConstExtenders() : MachineFunctionPass(ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<MachineDominatorTree>();
AU.addPreserved<MachineDominatorTree>();
MachineFunctionPass::getAnalysisUsage(AU);
}
StringRef getPassName() const override {
return "Hexagon constant-extender optimization";
}
bool runOnMachineFunction(MachineFunction &MF) override;
private:
struct Register {
Register() = default;
Register(llvm::Register R, unsigned S) : Reg(R), Sub(S) {}
Register(const MachineOperand &Op)
: Reg(Op.getReg()), Sub(Op.getSubReg()) {}
Register &operator=(const MachineOperand &Op) {
if (Op.isReg()) {
Reg = Op.getReg();
Sub = Op.getSubReg();
} else if (Op.isFI()) {
Reg = llvm::Register::index2StackSlot(Op.getIndex());
}
return *this;
}
bool isVReg() const {
return Reg != 0 && !Reg.isStack() && Reg.isVirtual();
}
bool isSlot() const { return Reg != 0 && Reg.isStack(); }
operator MachineOperand() const {
if (isVReg())
return MachineOperand::CreateReg(Reg, /*Def*/false, /*Imp*/false,
/*Kill*/false, /*Dead*/false, /*Undef*/false,
/*EarlyClobber*/false, Sub);
if (Reg.isStack()) {
int FI = llvm::Register::stackSlot2Index(Reg);
return MachineOperand::CreateFI(FI);
}
llvm_unreachable("Cannot create MachineOperand");
}
bool operator==(Register R) const { return Reg == R.Reg && Sub == R.Sub; }
bool operator!=(Register R) const { return !operator==(R); }
bool operator<(Register R) const {
// For std::map.
return Reg < R.Reg || (Reg == R.Reg && Sub < R.Sub);
}
llvm::Register Reg;
unsigned Sub = 0;
};
struct ExtExpr {
// A subexpression in which the extender is used. In general, this
// represents an expression where adding D to the extender will be
// equivalent to adding D to the expression as a whole. In other
// words, expr(add(##V,D) = add(expr(##V),D).
// The original motivation for this are the io/ur addressing modes,
// where the offset is extended. Consider the io example:
// In memw(Rs+##V), the ##V could be replaced by a register Rt to
// form the rr mode: memw(Rt+Rs<<0). In such case, however, the
// register Rt must have exactly the value of ##V. If there was
// another instruction memw(Rs+##V+4), it would need a different Rt.
// Now, if Rt was initialized as "##V+Rs<<0", both of these
// instructions could use the same Rt, just with different offsets.
// Here it's clear that "initializer+4" should be the same as if
// the offset 4 was added to the ##V in the initializer.
// The only kinds of expressions that support the requirement of
// commuting with addition are addition and subtraction from ##V.
// Include shifting the Rs to account for the ur addressing mode:
// ##Val + Rs << S
// ##Val - Rs
Register Rs;
unsigned S = 0;
bool Neg = false;
ExtExpr() = default;
ExtExpr(Register RS, bool NG, unsigned SH) : Rs(RS), S(SH), Neg(NG) {}
// Expression is trivial if it does not modify the extender.
bool trivial() const {
return Rs.Reg == 0;
}
bool operator==(const ExtExpr &Ex) const {
return Rs == Ex.Rs && S == Ex.S && Neg == Ex.Neg;
}
bool operator!=(const ExtExpr &Ex) const {
return !operator==(Ex);
}
bool operator<(const ExtExpr &Ex) const {
if (Rs != Ex.Rs)
return Rs < Ex.Rs;
if (S != Ex.S)
return S < Ex.S;
return !Neg && Ex.Neg;
}
};
struct ExtDesc {
MachineInstr *UseMI = nullptr;
unsigned OpNum = -1u;
// The subexpression in which the extender is used (e.g. address
// computation).
ExtExpr Expr;
// Optional register that is assigned the value of Expr.
Register Rd;
// Def means that the output of the instruction may differ from the
// original by a constant c, and that the difference can be corrected
// by adding/subtracting c in all users of the defined register.
bool IsDef = false;
MachineOperand &getOp() {
return UseMI->getOperand(OpNum);
}
const MachineOperand &getOp() const {
return UseMI->getOperand(OpNum);
}
};
struct ExtRoot {
union {
const ConstantFP *CFP; // MO_FPImmediate
const char *SymbolName; // MO_ExternalSymbol
const GlobalValue *GV; // MO_GlobalAddress
const BlockAddress *BA; // MO_BlockAddress
int64_t ImmVal; // MO_Immediate, MO_TargetIndex,
// and MO_ConstantPoolIndex
} V;
unsigned Kind; // Same as in MachineOperand.
unsigned char TF; // TargetFlags.
ExtRoot(const MachineOperand &Op);
bool operator==(const ExtRoot &ER) const {
return Kind == ER.Kind && V.ImmVal == ER.V.ImmVal;
}
bool operator!=(const ExtRoot &ER) const {
return !operator==(ER);
}
bool operator<(const ExtRoot &ER) const;
};
struct ExtValue : public ExtRoot {
int32_t Offset;
ExtValue(const MachineOperand &Op);
ExtValue(const ExtDesc &ED) : ExtValue(ED.getOp()) {}
ExtValue(const ExtRoot &ER, int32_t Off) : ExtRoot(ER), Offset(Off) {}
bool operator<(const ExtValue &EV) const;
bool operator==(const ExtValue &EV) const {
return ExtRoot(*this) == ExtRoot(EV) && Offset == EV.Offset;
}
bool operator!=(const ExtValue &EV) const {
return !operator==(EV);
}
explicit operator MachineOperand() const;
};
using IndexList = SetVector<unsigned>;
using ExtenderInit = std::pair<ExtValue, ExtExpr>;
using AssignmentMap = std::map<ExtenderInit, IndexList>;
using LocDefList = std::vector<std::pair<Loc, IndexList>>;
const HexagonSubtarget *HST = nullptr;
const HexagonInstrInfo *HII = nullptr;
const HexagonRegisterInfo *HRI = nullptr;
MachineDominatorTree *MDT = nullptr;
MachineRegisterInfo *MRI = nullptr;
std::vector<ExtDesc> Extenders;
std::vector<unsigned> NewRegs;
bool isStoreImmediate(unsigned Opc) const;
bool isRegOffOpcode(unsigned ExtOpc) const ;
unsigned getRegOffOpcode(unsigned ExtOpc) const;
unsigned getDirectRegReplacement(unsigned ExtOpc) const;
OffsetRange getOffsetRange(Register R, const MachineInstr &MI) const;
OffsetRange getOffsetRange(const ExtDesc &ED) const;
OffsetRange getOffsetRange(Register Rd) const;
void recordExtender(MachineInstr &MI, unsigned OpNum);
void collectInstr(MachineInstr &MI);
void collect(MachineFunction &MF);
void assignInits(const ExtRoot &ER, unsigned Begin, unsigned End,
AssignmentMap &IMap);
void calculatePlacement(const ExtenderInit &ExtI, const IndexList &Refs,
LocDefList &Defs);
Register insertInitializer(Loc DefL, const ExtenderInit &ExtI);
bool replaceInstrExact(const ExtDesc &ED, Register ExtR);
bool replaceInstrExpr(const ExtDesc &ED, const ExtenderInit &ExtI,
Register ExtR, int32_t &Diff);
bool replaceInstr(unsigned Idx, Register ExtR, const ExtenderInit &ExtI);
bool replaceExtenders(const AssignmentMap &IMap);
unsigned getOperandIndex(const MachineInstr &MI,
const MachineOperand &Op) const;
const MachineOperand &getPredicateOp(const MachineInstr &MI) const;
const MachineOperand &getLoadResultOp(const MachineInstr &MI) const;
const MachineOperand &getStoredValueOp(const MachineInstr &MI) const;
friend struct PrintRegister;
friend struct PrintExpr;
friend struct PrintInit;
friend struct PrintIMap;
friend raw_ostream &operator<< (raw_ostream &OS,
const struct PrintRegister &P);
friend raw_ostream &operator<< (raw_ostream &OS, const struct PrintExpr &P);
friend raw_ostream &operator<< (raw_ostream &OS, const struct PrintInit &P);
friend raw_ostream &operator<< (raw_ostream &OS, const ExtDesc &ED);
friend raw_ostream &operator<< (raw_ostream &OS, const ExtRoot &ER);
friend raw_ostream &operator<< (raw_ostream &OS, const ExtValue &EV);
friend raw_ostream &operator<< (raw_ostream &OS, const OffsetRange &OR);
friend raw_ostream &operator<< (raw_ostream &OS, const struct PrintIMap &P);
};
using HCE = HexagonConstExtenders;
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const OffsetRange &OR) {
if (OR.Min > OR.Max)
OS << '!';
OS << '[' << OR.Min << ',' << OR.Max << "]a" << unsigned(OR.Align)
<< '+' << unsigned(OR.Offset);
return OS;
}
struct PrintRegister {
PrintRegister(HCE::Register R, const HexagonRegisterInfo &I)
: Rs(R), HRI(I) {}
HCE::Register Rs;
const HexagonRegisterInfo &HRI;
};
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const PrintRegister &P) {
if (P.Rs.Reg != 0)
OS << printReg(P.Rs.Reg, &P.HRI, P.Rs.Sub);
else
OS << "noreg";
return OS;
}
struct PrintExpr {
PrintExpr(const HCE::ExtExpr &E, const HexagonRegisterInfo &I)
: Ex(E), HRI(I) {}
const HCE::ExtExpr &Ex;
const HexagonRegisterInfo &HRI;
};
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const PrintExpr &P) {
OS << "## " << (P.Ex.Neg ? "- " : "+ ");
if (P.Ex.Rs.Reg != 0)
OS << printReg(P.Ex.Rs.Reg, &P.HRI, P.Ex.Rs.Sub);
else
OS << "__";
OS << " << " << P.Ex.S;
return OS;
}
struct PrintInit {
PrintInit(const HCE::ExtenderInit &EI, const HexagonRegisterInfo &I)
: ExtI(EI), HRI(I) {}
const HCE::ExtenderInit &ExtI;
const HexagonRegisterInfo &HRI;
};
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const PrintInit &P) {
OS << '[' << P.ExtI.first << ", "
<< PrintExpr(P.ExtI.second, P.HRI) << ']';
return OS;
}
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const HCE::ExtDesc &ED) {
assert(ED.OpNum != -1u);
const MachineBasicBlock &MBB = *ED.getOp().getParent()->getParent();
const MachineFunction &MF = *MBB.getParent();
const auto &HRI = *MF.getSubtarget<HexagonSubtarget>().getRegisterInfo();
OS << "bb#" << MBB.getNumber() << ": ";
if (ED.Rd.Reg != 0)
OS << printReg(ED.Rd.Reg, &HRI, ED.Rd.Sub);
else
OS << "__";
OS << " = " << PrintExpr(ED.Expr, HRI);
if (ED.IsDef)
OS << ", def";
return OS;
}
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const HCE::ExtRoot &ER) {
switch (ER.Kind) {
case MachineOperand::MO_Immediate:
OS << "imm:" << ER.V.ImmVal;
break;
case MachineOperand::MO_FPImmediate:
OS << "fpi:" << *ER.V.CFP;
break;
case MachineOperand::MO_ExternalSymbol:
OS << "sym:" << *ER.V.SymbolName;
break;
case MachineOperand::MO_GlobalAddress:
OS << "gad:" << ER.V.GV->getName();
break;
case MachineOperand::MO_BlockAddress:
OS << "blk:" << *ER.V.BA;
break;
case MachineOperand::MO_TargetIndex:
OS << "tgi:" << ER.V.ImmVal;
break;
case MachineOperand::MO_ConstantPoolIndex:
OS << "cpi:" << ER.V.ImmVal;
break;
case MachineOperand::MO_JumpTableIndex:
OS << "jti:" << ER.V.ImmVal;
break;
default:
OS << "???:" << ER.V.ImmVal;
break;
}
return OS;
}
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const HCE::ExtValue &EV) {
OS << HCE::ExtRoot(EV) << " off:" << EV.Offset;
return OS;
}
struct PrintIMap {
PrintIMap(const HCE::AssignmentMap &M, const HexagonRegisterInfo &I)
: IMap(M), HRI(I) {}
const HCE::AssignmentMap &IMap;
const HexagonRegisterInfo &HRI;
};
LLVM_ATTRIBUTE_UNUSED
raw_ostream &operator<< (raw_ostream &OS, const PrintIMap &P) {
OS << "{\n";
for (const std::pair<const HCE::ExtenderInit, HCE::IndexList> &Q : P.IMap) {
OS << " " << PrintInit(Q.first, P.HRI) << " -> {";
for (unsigned I : Q.second)
OS << ' ' << I;
OS << " }\n";
}
OS << "}\n";
return OS;
}
}
INITIALIZE_PASS_BEGIN(HexagonConstExtenders, "hexagon-cext-opt",
"Hexagon constant-extender optimization", false, false)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
INITIALIZE_PASS_END(HexagonConstExtenders, "hexagon-cext-opt",
"Hexagon constant-extender optimization", false, false)
static unsigned ReplaceCounter = 0;
char HCE::ID = 0;
#ifndef NDEBUG
LLVM_DUMP_METHOD void RangeTree::dump() const {
dbgs() << "Root: " << Root << '\n';
if (Root)
dump(Root);
}
LLVM_DUMP_METHOD void RangeTree::dump(const Node *N) const {
dbgs() << "Node: " << N << '\n';
dbgs() << " Height: " << N->Height << '\n';
dbgs() << " Count: " << N->Count << '\n';
dbgs() << " MaxEnd: " << N->MaxEnd << '\n';
dbgs() << " Range: " << N->Range << '\n';
dbgs() << " Left: " << N->Left << '\n';
dbgs() << " Right: " << N->Right << "\n\n";
if (N->Left)
dump(N->Left);
if (N->Right)
dump(N->Right);
}
#endif
void RangeTree::order(Node *N, SmallVectorImpl<Node*> &Seq) const {
if (N == nullptr)
return;
order(N->Left, Seq);
Seq.push_back(N);
order(N->Right, Seq);
}
void RangeTree::nodesWith(Node *N, int32_t P, bool CheckA,
SmallVectorImpl<Node*> &Seq) const {
if (N == nullptr || N->MaxEnd < P)
return;
nodesWith(N->Left, P, CheckA, Seq);
if (N->Range.Min <= P) {
if ((CheckA && N->Range.contains(P)) || (!CheckA && P <= N->Range.Max))
Seq.push_back(N);
nodesWith(N->Right, P, CheckA, Seq);
}
}
RangeTree::Node *RangeTree::add(Node *N, const OffsetRange &R) {
if (N == nullptr)
return new Node(R);
if (N->Range == R) {
N->Count++;
return N;
}
if (R < N->Range)
N->Left = add(N->Left, R);
else
N->Right = add(N->Right, R);
return rebalance(update(N));
}
RangeTree::Node *RangeTree::remove(Node *N, const Node *D) {
assert(N != nullptr);
if (N != D) {
assert(N->Range != D->Range && "N and D should not be equal");
if (D->Range < N->Range)
N->Left = remove(N->Left, D);
else
N->Right = remove(N->Right, D);
return rebalance(update(N));
}
// We got to the node we need to remove. If any of its children are
// missing, simply replace it with the other child.
if (N->Left == nullptr || N->Right == nullptr)
return (N->Left == nullptr) ? N->Right : N->Left;
// Find the rightmost child of N->Left, remove it and plug it in place
// of N.
Node *M = N->Left;
while (M->Right)
M = M->Right;
M->Left = remove(N->Left, M);
M->Right = N->Right;
return rebalance(update(M));
}
RangeTree::Node *RangeTree::rotateLeft(Node *Lower, Node *Higher) {
assert(Higher->Right == Lower);
// The Lower node is on the right from Higher. Make sure that Lower's
// balance is greater to the right. Otherwise the rotation will create
// an unbalanced tree again.
if (height(Lower->Left) > height(Lower->Right))
Lower = rotateRight(Lower->Left, Lower);
assert(height(Lower->Left) <= height(Lower->Right));
Higher->Right = Lower->Left;
update(Higher);
Lower->Left = Higher;
update(Lower);
return Lower;
}
RangeTree::Node *RangeTree::rotateRight(Node *Lower, Node *Higher) {
assert(Higher->Left == Lower);
// The Lower node is on the left from Higher. Make sure that Lower's
// balance is greater to the left. Otherwise the rotation will create
// an unbalanced tree again.
if (height(Lower->Left) < height(Lower->Right))
Lower = rotateLeft(Lower->Right, Lower);
assert(height(Lower->Left) >= height(Lower->Right));
Higher->Left = Lower->Right;
update(Higher);
Lower->Right = Higher;
update(Lower);
return Lower;
}
HCE::ExtRoot::ExtRoot(const MachineOperand &Op) {
// Always store ImmVal, since it's the field used for comparisons.
V.ImmVal = 0;
if (Op.isImm())
; // Keep 0. Do not use Op.getImm() for value here (treat 0 as the root).
else if (Op.isFPImm())
V.CFP = Op.getFPImm();
else if (Op.isSymbol())
V.SymbolName = Op.getSymbolName();
else if (Op.isGlobal())
V.GV = Op.getGlobal();
else if (Op.isBlockAddress())
V.BA = Op.getBlockAddress();
else if (Op.isCPI() || Op.isTargetIndex() || Op.isJTI())
V.ImmVal = Op.getIndex();
else
llvm_unreachable("Unexpected operand type");
Kind = Op.getType();
TF = Op.getTargetFlags();
}
bool HCE::ExtRoot::operator< (const HCE::ExtRoot &ER) const {
if (Kind != ER.Kind)
return Kind < ER.Kind;
switch (Kind) {
case MachineOperand::MO_Immediate:
case MachineOperand::MO_TargetIndex:
case MachineOperand::MO_ConstantPoolIndex:
case MachineOperand::MO_JumpTableIndex:
return V.ImmVal < ER.V.ImmVal;
case MachineOperand::MO_FPImmediate: {
const APFloat &ThisF = V.CFP->getValueAPF();
const APFloat &OtherF = ER.V.CFP->getValueAPF();
return ThisF.bitcastToAPInt().ult(OtherF.bitcastToAPInt());
}
case MachineOperand::MO_ExternalSymbol:
return StringRef(V.SymbolName) < StringRef(ER.V.SymbolName);
case MachineOperand::MO_GlobalAddress:
// Do not use GUIDs, since they depend on the source path. Moving the
// source file to a different directory could cause different GUID
// values for a pair of given symbols. These symbols could then compare
// "less" in one directory, but "greater" in another.
assert(!V.GV->getName().empty() && !ER.V.GV->getName().empty());
return V.GV->getName() < ER.V.GV->getName();
case MachineOperand::MO_BlockAddress: {
const BasicBlock *ThisB = V.BA->getBasicBlock();
const BasicBlock *OtherB = ER.V.BA->getBasicBlock();
assert(ThisB->getParent() == OtherB->getParent());
const Function &F = *ThisB->getParent();
return std::distance(F.begin(), ThisB->getIterator()) <
std::distance(F.begin(), OtherB->getIterator());
}
}
return V.ImmVal < ER.V.ImmVal;
}
HCE::ExtValue::ExtValue(const MachineOperand &Op) : ExtRoot(Op) {
if (Op.isImm())
Offset = Op.getImm();
else if (Op.isFPImm() || Op.isJTI())
Offset = 0;
else if (Op.isSymbol() || Op.isGlobal() || Op.isBlockAddress() ||
Op.isCPI() || Op.isTargetIndex())
Offset = Op.getOffset();
else
llvm_unreachable("Unexpected operand type");
}
bool HCE::ExtValue::operator< (const HCE::ExtValue &EV) const {
const ExtRoot &ER = *this;
if (!(ER == ExtRoot(EV)))
return ER < EV;
return Offset < EV.Offset;
}
HCE::ExtValue::operator MachineOperand() const {
switch (Kind) {
case MachineOperand::MO_Immediate:
return MachineOperand::CreateImm(V.ImmVal + Offset);
case MachineOperand::MO_FPImmediate:
assert(Offset == 0);
return MachineOperand::CreateFPImm(V.CFP);
case MachineOperand::MO_ExternalSymbol:
assert(Offset == 0);
return MachineOperand::CreateES(V.SymbolName, TF);
case MachineOperand::MO_GlobalAddress:
return MachineOperand::CreateGA(V.GV, Offset, TF);
case MachineOperand::MO_BlockAddress:
return MachineOperand::CreateBA(V.BA, Offset, TF);
case MachineOperand::MO_TargetIndex:
return MachineOperand::CreateTargetIndex(V.ImmVal, Offset, TF);
case MachineOperand::MO_ConstantPoolIndex:
return MachineOperand::CreateCPI(V.ImmVal, Offset, TF);
case MachineOperand::MO_JumpTableIndex:
assert(Offset == 0);
return MachineOperand::CreateJTI(V.ImmVal, TF);
default:
llvm_unreachable("Unhandled kind");
}
}
bool HCE::isStoreImmediate(unsigned Opc) const {
switch (Opc) {
case Hexagon::S4_storeirbt_io:
case Hexagon::S4_storeirbf_io:
case Hexagon::S4_storeirht_io:
case Hexagon::S4_storeirhf_io:
case Hexagon::S4_storeirit_io:
case Hexagon::S4_storeirif_io:
case Hexagon::S4_storeirb_io:
case Hexagon::S4_storeirh_io:
case Hexagon::S4_storeiri_io:
return true;
default:
break;
}
return false;
}
bool HCE::isRegOffOpcode(unsigned Opc) const {
switch (Opc) {
case Hexagon::L2_loadrub_io:
case Hexagon::L2_loadrb_io:
case Hexagon::L2_loadruh_io:
case Hexagon::L2_loadrh_io:
case Hexagon::L2_loadri_io:
case Hexagon::L2_loadrd_io:
case Hexagon::L2_loadbzw2_io:
case Hexagon::L2_loadbzw4_io:
case Hexagon::L2_loadbsw2_io:
case Hexagon::L2_loadbsw4_io:
case Hexagon::L2_loadalignh_io:
case Hexagon::L2_loadalignb_io:
case Hexagon::L2_ploadrubt_io:
case Hexagon::L2_ploadrubf_io:
case Hexagon::L2_ploadrbt_io:
case Hexagon::L2_ploadrbf_io:
case Hexagon::L2_ploadruht_io:
case Hexagon::L2_ploadruhf_io:
case Hexagon::L2_ploadrht_io:
case Hexagon::L2_ploadrhf_io:
case Hexagon::L2_ploadrit_io:
case Hexagon::L2_ploadrif_io:
case Hexagon::L2_ploadrdt_io:
case Hexagon::L2_ploadrdf_io:
case Hexagon::S2_storerb_io:
case Hexagon::S2_storerh_io:
case Hexagon::S2_storerf_io:
case Hexagon::S2_storeri_io:
case Hexagon::S2_storerd_io:
case Hexagon::S2_pstorerbt_io:
case Hexagon::S2_pstorerbf_io:
case Hexagon::S2_pstorerht_io:
case Hexagon::S2_pstorerhf_io:
case Hexagon::S2_pstorerft_io:
case Hexagon::S2_pstorerff_io:
case Hexagon::S2_pstorerit_io:
case Hexagon::S2_pstorerif_io:
case Hexagon::S2_pstorerdt_io:
case Hexagon::S2_pstorerdf_io:
case Hexagon::A2_addi:
return true;
default:
break;
}
return false;
}
unsigned HCE::getRegOffOpcode(unsigned ExtOpc) const {
// If there exists an instruction that takes a register and offset,
// that corresponds to the ExtOpc, return it, otherwise return 0.
using namespace Hexagon;
switch (ExtOpc) {
case A2_tfrsi: return A2_addi;
default:
break;
}
const MCInstrDesc &D = HII->get(ExtOpc);
if (D.mayLoad() || D.mayStore()) {
uint64_t F = D.TSFlags;
unsigned AM = (F >> HexagonII::AddrModePos) & HexagonII::AddrModeMask;
switch (AM) {
case HexagonII::Absolute:
case HexagonII::AbsoluteSet:
case HexagonII::BaseLongOffset:
switch (ExtOpc) {
case PS_loadrubabs:
case L4_loadrub_ap:
case L4_loadrub_ur: return L2_loadrub_io;
case PS_loadrbabs:
case L4_loadrb_ap:
case L4_loadrb_ur: return L2_loadrb_io;
case PS_loadruhabs:
case L4_loadruh_ap:
case L4_loadruh_ur: return L2_loadruh_io;
case PS_loadrhabs:
case L4_loadrh_ap:
case L4_loadrh_ur: return L2_loadrh_io;
case PS_loadriabs:
case L4_loadri_ap:
case L4_loadri_ur: return L2_loadri_io;
case PS_loadrdabs:
case L4_loadrd_ap:
case L4_loadrd_ur: return L2_loadrd_io;
case L4_loadbzw2_ap:
case L4_loadbzw2_ur: return L2_loadbzw2_io;
case L4_loadbzw4_ap:
case L4_loadbzw4_ur: return L2_loadbzw4_io;
case L4_loadbsw2_ap:
case L4_loadbsw2_ur: return L2_loadbsw2_io;
case L4_loadbsw4_ap:
case L4_loadbsw4_ur: return L2_loadbsw4_io;
case L4_loadalignh_ap:
case L4_loadalignh_ur: return L2_loadalignh_io;
case L4_loadalignb_ap:
case L4_loadalignb_ur: return L2_loadalignb_io;
case L4_ploadrubt_abs: return L2_ploadrubt_io;
case L4_ploadrubf_abs: return L2_ploadrubf_io;
case L4_ploadrbt_abs: return L2_ploadrbt_io;
case L4_ploadrbf_abs: return L2_ploadrbf_io;
case L4_ploadruht_abs: return L2_ploadruht_io;
case L4_ploadruhf_abs: return L2_ploadruhf_io;
case L4_ploadrht_abs: return L2_ploadrht_io;
case L4_ploadrhf_abs: return L2_ploadrhf_io;
case L4_ploadrit_abs: return L2_ploadrit_io;
case L4_ploadrif_abs: return L2_ploadrif_io;
case L4_ploadrdt_abs: return L2_ploadrdt_io;
case L4_ploadrdf_abs: return L2_ploadrdf_io;
case PS_storerbabs:
case S4_storerb_ap:
case S4_storerb_ur: return S2_storerb_io;
case PS_storerhabs:
case S4_storerh_ap:
case S4_storerh_ur: return S2_storerh_io;
case PS_storerfabs:
case S4_storerf_ap:
case S4_storerf_ur: return S2_storerf_io;
case PS_storeriabs:
case S4_storeri_ap:
case S4_storeri_ur: return S2_storeri_io;
case PS_storerdabs:
case S4_storerd_ap:
case S4_storerd_ur: return S2_storerd_io;
case S4_pstorerbt_abs: return S2_pstorerbt_io;
case S4_pstorerbf_abs: return S2_pstorerbf_io;
case S4_pstorerht_abs: return S2_pstorerht_io;
case S4_pstorerhf_abs: return S2_pstorerhf_io;
case S4_pstorerft_abs: return S2_pstorerft_io;
case S4_pstorerff_abs: return S2_pstorerff_io;
case S4_pstorerit_abs: return S2_pstorerit_io;
case S4_pstorerif_abs: return S2_pstorerif_io;
case S4_pstorerdt_abs: return S2_pstorerdt_io;
case S4_pstorerdf_abs: return S2_pstorerdf_io;
default:
break;
}
break;
case HexagonII::BaseImmOffset:
if (!isStoreImmediate(ExtOpc))
return ExtOpc;
break;
default:
break;
}
}
return 0;
}
unsigned HCE::getDirectRegReplacement(unsigned ExtOpc) const {
switch (ExtOpc) {
case Hexagon::A2_addi: return Hexagon::A2_add;
case Hexagon::A2_andir: return Hexagon::A2_and;
case Hexagon::A2_combineii: return Hexagon::A4_combineri;
case Hexagon::A2_orir: return Hexagon::A2_or;
case Hexagon::A2_paddif: return Hexagon::A2_paddf;
case Hexagon::A2_paddit: return Hexagon::A2_paddt;
case Hexagon::A2_subri: return Hexagon::A2_sub;
case Hexagon::A2_tfrsi: return TargetOpcode::COPY;
case Hexagon::A4_cmpbeqi: return Hexagon::A4_cmpbeq;
case Hexagon::A4_cmpbgti: return Hexagon::A4_cmpbgt;
case Hexagon::A4_cmpbgtui: return Hexagon::A4_cmpbgtu;
case Hexagon::A4_cmpheqi: return Hexagon::A4_cmpheq;
case Hexagon::A4_cmphgti: return Hexagon::A4_cmphgt;
case Hexagon::A4_cmphgtui: return Hexagon::A4_cmphgtu;
case Hexagon::A4_combineii: return Hexagon::A4_combineir;
case Hexagon::A4_combineir: return TargetOpcode::REG_SEQUENCE;
case Hexagon::A4_combineri: return TargetOpcode::REG_SEQUENCE;
case Hexagon::A4_rcmpeqi: return Hexagon::A4_rcmpeq;
case Hexagon::A4_rcmpneqi: return Hexagon::A4_rcmpneq;
case Hexagon::C2_cmoveif: return Hexagon::A2_tfrpf;
case Hexagon::C2_cmoveit: return Hexagon::A2_tfrpt;
case Hexagon::C2_cmpeqi: return Hexagon::C2_cmpeq;
case Hexagon::C2_cmpgti: return Hexagon::C2_cmpgt;
case Hexagon::C2_cmpgtui: return Hexagon::C2_cmpgtu;
case Hexagon::C2_muxii: return Hexagon::C2_muxir;
case Hexagon::C2_muxir: return Hexagon::C2_mux;
case Hexagon::C2_muxri: return Hexagon::C2_mux;
case Hexagon::C4_cmpltei: return Hexagon::C4_cmplte;
case Hexagon::C4_cmplteui: return Hexagon::C4_cmplteu;
case Hexagon::C4_cmpneqi: return Hexagon::C4_cmpneq;
case Hexagon::M2_accii: return Hexagon::M2_acci; // T -> T
/* No M2_macsin */
case Hexagon::M2_macsip: return Hexagon::M2_maci; // T -> T
case Hexagon::M2_mpysin: return Hexagon::M2_mpyi;
case Hexagon::M2_mpysip: return Hexagon::M2_mpyi;