forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMveEmitter.cpp
2224 lines (1965 loc) · 83.9 KB
/
MveEmitter.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
//===- MveEmitter.cpp - Generate arm_mve.h for use with clang -*- C++ -*-=====//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This set of linked tablegen backends is responsible for emitting the bits
// and pieces that implement <arm_mve.h>, which is defined by the ACLE standard
// and provides a set of types and functions for (more or less) direct access
// to the MVE instruction set, including the scalar shifts as well as the
// vector instructions.
//
// MVE's standard intrinsic functions are unusual in that they have a system of
// polymorphism. For example, the function vaddq() can behave like vaddq_u16(),
// vaddq_f32(), vaddq_s8(), etc., depending on the types of the vector
// arguments you give it.
//
// This constrains the implementation strategies. The usual approach to making
// the user-facing functions polymorphic would be to either use
// __attribute__((overloadable)) to make a set of vaddq() functions that are
// all inline wrappers on the underlying clang builtins, or to define a single
// vaddq() macro which expands to an instance of _Generic.
//
// The inline-wrappers approach would work fine for most intrinsics, except for
// the ones that take an argument required to be a compile-time constant,
// because if you wrap an inline function around a call to a builtin, the
// constant nature of the argument is not passed through.
//
// The _Generic approach can be made to work with enough effort, but it takes a
// lot of machinery, because of the design feature of _Generic that even the
// untaken branches are required to pass all front-end validity checks such as
// type-correctness. You can work around that by nesting further _Generics all
// over the place to coerce things to the right type in untaken branches, but
// what you get out is complicated, hard to guarantee its correctness, and
// worst of all, gives _completely unreadable_ error messages if the user gets
// the types wrong for an intrinsic call.
//
// Therefore, my strategy is to introduce a new __attribute__ that allows a
// function to be mapped to a clang builtin even though it doesn't have the
// same name, and then declare all the user-facing MVE function names with that
// attribute, mapping each one directly to the clang builtin. And the
// polymorphic ones have __attribute__((overloadable)) as well. So once the
// compiler has resolved the overload, it knows the internal builtin ID of the
// selected function, and can check the immediate arguments against that; and
// if the user gets the types wrong in a call to a polymorphic intrinsic, they
// get a completely clear error message showing all the declarations of that
// function in the header file and explaining why each one doesn't fit their
// call.
//
// The downside of this is that if every clang builtin has to correspond
// exactly to a user-facing ACLE intrinsic, then you can't save work in the
// frontend by doing it in the header file: CGBuiltin.cpp has to do the entire
// job of converting an ACLE intrinsic call into LLVM IR. So the Tablegen
// description for an MVE intrinsic has to contain a full description of the
// sequence of IRBuilder calls that clang will need to make.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/StringToOffsetTable.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
using namespace llvm;
namespace {
class EmitterBase;
class Result;
// -----------------------------------------------------------------------------
// A system of classes to represent all the types we'll need to deal with in
// the prototypes of intrinsics.
//
// Query methods include finding out the C name of a type; the "LLVM name" in
// the sense of a C++ code snippet that can be used in the codegen function;
// the suffix that represents the type in the ACLE intrinsic naming scheme
// (e.g. 's32' represents int32_t in intrinsics such as vaddq_s32); whether the
// type is floating-point related (hence should be under #ifdef in the MVE
// header so that it isn't included in integer-only MVE mode); and the type's
// size in bits. Not all subtypes support all these queries.
class Type {
public:
enum class TypeKind {
// Void appears as a return type (for store intrinsics, which are pure
// side-effect). It's also used as the parameter type in the Tablegen
// when an intrinsic doesn't need to come in various suffixed forms like
// vfooq_s8,vfooq_u16,vfooq_f32.
Void,
// Scalar is used for ordinary int and float types of all sizes.
Scalar,
// Vector is used for anything that occupies exactly one MVE vector
// register, i.e. {uint,int,float}NxM_t.
Vector,
// MultiVector is used for the {uint,int,float}NxMxK_t types used by the
// interleaving load/store intrinsics v{ld,st}{2,4}q.
MultiVector,
// Predicate is used by all the predicated intrinsics. Its C
// representation is mve_pred16_t (which is just an alias for uint16_t).
// But we give more detail here, by indicating that a given predicate
// instruction is logically regarded as a vector of i1 containing the
// same number of lanes as the input vector type. So our Predicate type
// comes with a lane count, which we use to decide which kind of <n x i1>
// we'll invoke the pred_i2v IR intrinsic to translate it into.
Predicate,
// Pointer is used for pointer types (obviously), and comes with a flag
// indicating whether it's a pointer to a const or mutable instance of
// the pointee type.
Pointer,
};
private:
const TypeKind TKind;
protected:
Type(TypeKind K) : TKind(K) {}
public:
TypeKind typeKind() const { return TKind; }
virtual ~Type() = default;
virtual bool requiresFloat() const = 0;
virtual bool requiresMVE() const = 0;
virtual unsigned sizeInBits() const = 0;
virtual std::string cName() const = 0;
virtual std::string llvmName() const {
PrintFatalError("no LLVM type name available for type " + cName());
}
virtual std::string acleSuffix(std::string) const {
PrintFatalError("no ACLE suffix available for this type");
}
};
enum class ScalarTypeKind { SignedInt, UnsignedInt, Float };
inline std::string toLetter(ScalarTypeKind kind) {
switch (kind) {
case ScalarTypeKind::SignedInt:
return "s";
case ScalarTypeKind::UnsignedInt:
return "u";
case ScalarTypeKind::Float:
return "f";
}
llvm_unreachable("Unhandled ScalarTypeKind enum");
}
inline std::string toCPrefix(ScalarTypeKind kind) {
switch (kind) {
case ScalarTypeKind::SignedInt:
return "int";
case ScalarTypeKind::UnsignedInt:
return "uint";
case ScalarTypeKind::Float:
return "float";
}
llvm_unreachable("Unhandled ScalarTypeKind enum");
}
class VoidType : public Type {
public:
VoidType() : Type(TypeKind::Void) {}
unsigned sizeInBits() const override { return 0; }
bool requiresFloat() const override { return false; }
bool requiresMVE() const override { return false; }
std::string cName() const override { return "void"; }
static bool classof(const Type *T) { return T->typeKind() == TypeKind::Void; }
std::string acleSuffix(std::string) const override { return ""; }
};
class PointerType : public Type {
const Type *Pointee;
bool Const;
public:
PointerType(const Type *Pointee, bool Const)
: Type(TypeKind::Pointer), Pointee(Pointee), Const(Const) {}
unsigned sizeInBits() const override { return 32; }
bool requiresFloat() const override { return Pointee->requiresFloat(); }
bool requiresMVE() const override { return Pointee->requiresMVE(); }
std::string cName() const override {
std::string Name = Pointee->cName();
// The syntax for a pointer in C is different when the pointee is
// itself a pointer. The MVE intrinsics don't contain any double
// pointers, so we don't need to worry about that wrinkle.
assert(!isa<PointerType>(Pointee) && "Pointer to pointer not supported");
if (Const)
Name = "const " + Name;
return Name + " *";
}
std::string llvmName() const override {
return "llvm::PointerType::getUnqual(" + Pointee->llvmName() + ")";
}
const Type *getPointeeType() const { return Pointee; }
static bool classof(const Type *T) {
return T->typeKind() == TypeKind::Pointer;
}
};
// Base class for all the types that have a name of the form
// [prefix][numbers]_t, like int32_t, uint16x8_t, float32x4x2_t.
//
// For this sub-hierarchy we invent a cNameBase() method which returns the
// whole name except for the trailing "_t", so that Vector and MultiVector can
// append an extra "x2" or whatever to their element type's cNameBase(). Then
// the main cName() query method puts "_t" on the end for the final type name.
class CRegularNamedType : public Type {
using Type::Type;
virtual std::string cNameBase() const = 0;
public:
std::string cName() const override { return cNameBase() + "_t"; }
};
class ScalarType : public CRegularNamedType {
ScalarTypeKind Kind;
unsigned Bits;
std::string NameOverride;
public:
ScalarType(const Record *Record) : CRegularNamedType(TypeKind::Scalar) {
Kind = StringSwitch<ScalarTypeKind>(Record->getValueAsString("kind"))
.Case("s", ScalarTypeKind::SignedInt)
.Case("u", ScalarTypeKind::UnsignedInt)
.Case("f", ScalarTypeKind::Float);
Bits = Record->getValueAsInt("size");
NameOverride = std::string(Record->getValueAsString("nameOverride"));
}
unsigned sizeInBits() const override { return Bits; }
ScalarTypeKind kind() const { return Kind; }
std::string suffix() const { return toLetter(Kind) + utostr(Bits); }
std::string cNameBase() const override {
return toCPrefix(Kind) + utostr(Bits);
}
std::string cName() const override {
if (NameOverride.empty())
return CRegularNamedType::cName();
return NameOverride;
}
std::string llvmName() const override {
if (Kind == ScalarTypeKind::Float) {
if (Bits == 16)
return "HalfTy";
if (Bits == 32)
return "FloatTy";
if (Bits == 64)
return "DoubleTy";
PrintFatalError("bad size for floating type");
}
return "Int" + utostr(Bits) + "Ty";
}
std::string acleSuffix(std::string overrideLetter) const override {
return "_" + (overrideLetter.size() ? overrideLetter : toLetter(Kind))
+ utostr(Bits);
}
bool isInteger() const { return Kind != ScalarTypeKind::Float; }
bool requiresFloat() const override { return !isInteger(); }
bool requiresMVE() const override { return false; }
bool hasNonstandardName() const { return !NameOverride.empty(); }
static bool classof(const Type *T) {
return T->typeKind() == TypeKind::Scalar;
}
};
class VectorType : public CRegularNamedType {
const ScalarType *Element;
unsigned Lanes;
public:
VectorType(const ScalarType *Element, unsigned Lanes)
: CRegularNamedType(TypeKind::Vector), Element(Element), Lanes(Lanes) {}
unsigned sizeInBits() const override { return Lanes * Element->sizeInBits(); }
unsigned lanes() const { return Lanes; }
bool requiresFloat() const override { return Element->requiresFloat(); }
bool requiresMVE() const override { return true; }
std::string cNameBase() const override {
return Element->cNameBase() + "x" + utostr(Lanes);
}
std::string llvmName() const override {
return "llvm::FixedVectorType::get(" + Element->llvmName() + ", " +
utostr(Lanes) + ")";
}
static bool classof(const Type *T) {
return T->typeKind() == TypeKind::Vector;
}
};
class MultiVectorType : public CRegularNamedType {
const VectorType *Element;
unsigned Registers;
public:
MultiVectorType(unsigned Registers, const VectorType *Element)
: CRegularNamedType(TypeKind::MultiVector), Element(Element),
Registers(Registers) {}
unsigned sizeInBits() const override {
return Registers * Element->sizeInBits();
}
unsigned registers() const { return Registers; }
bool requiresFloat() const override { return Element->requiresFloat(); }
bool requiresMVE() const override { return true; }
std::string cNameBase() const override {
return Element->cNameBase() + "x" + utostr(Registers);
}
// MultiVectorType doesn't override llvmName, because we don't expect to do
// automatic code generation for the MVE intrinsics that use it: the {vld2,
// vld4, vst2, vst4} family are the only ones that use these types, so it was
// easier to hand-write the codegen for dealing with these structs than to
// build in lots of extra automatic machinery that would only be used once.
static bool classof(const Type *T) {
return T->typeKind() == TypeKind::MultiVector;
}
};
class PredicateType : public CRegularNamedType {
unsigned Lanes;
public:
PredicateType(unsigned Lanes)
: CRegularNamedType(TypeKind::Predicate), Lanes(Lanes) {}
unsigned sizeInBits() const override { return 16; }
std::string cNameBase() const override { return "mve_pred16"; }
bool requiresFloat() const override { return false; };
bool requiresMVE() const override { return true; }
std::string llvmName() const override {
return "llvm::FixedVectorType::get(Builder.getInt1Ty(), " + utostr(Lanes) +
")";
}
static bool classof(const Type *T) {
return T->typeKind() == TypeKind::Predicate;
}
};
// -----------------------------------------------------------------------------
// Class to facilitate merging together the code generation for many intrinsics
// by means of varying a few constant or type parameters.
//
// Most obviously, the intrinsics in a single parametrised family will have
// code generation sequences that only differ in a type or two, e.g. vaddq_s8
// and vaddq_u16 will look the same apart from putting a different vector type
// in the call to CGM.getIntrinsic(). But also, completely different intrinsics
// will often code-generate in the same way, with only a different choice of
// _which_ IR intrinsic they lower to (e.g. vaddq_m_s8 and vmulq_m_s8), but
// marshalling the arguments and return values of the IR intrinsic in exactly
// the same way. And others might differ only in some other kind of constant,
// such as a lane index.
//
// So, when we generate the IR-building code for all these intrinsics, we keep
// track of every value that could possibly be pulled out of the code and
// stored ahead of time in a local variable. Then we group together intrinsics
// by textual equivalence of the code that would result if _all_ those
// parameters were stored in local variables. That gives us maximal sets that
// can be implemented by a single piece of IR-building code by changing
// parameter values ahead of time.
//
// After we've done that, we do a second pass in which we only allocate _some_
// of the parameters into local variables, by tracking which ones have the same
// values as each other (so that a single variable can be reused) and which
// ones are the same across the whole set (so that no variable is needed at
// all).
//
// Hence the class below. Its allocParam method is invoked during code
// generation by every method of a Result subclass (see below) that wants to
// give it the opportunity to pull something out into a switchable parameter.
// It returns a variable name for the parameter, or (if it's being used in the
// second pass once we've decided that some parameters don't need to be stored
// in variables after all) it might just return the input expression unchanged.
struct CodeGenParamAllocator {
// Accumulated during code generation
std::vector<std::string> *ParamTypes = nullptr;
std::vector<std::string> *ParamValues = nullptr;
// Provided ahead of time in pass 2, to indicate which parameters are being
// assigned to what. This vector contains an entry for each call to
// allocParam expected during code gen (which we counted up in pass 1), and
// indicates the number of the parameter variable that should be returned, or
// -1 if this call shouldn't allocate a parameter variable at all.
//
// We rely on the recursive code generation working identically in passes 1
// and 2, so that the same list of calls to allocParam happen in the same
// order. That guarantees that the parameter numbers recorded in pass 1 will
// match the entries in this vector that store what EmitterBase::EmitBuiltinCG
// decided to do about each one in pass 2.
std::vector<int> *ParamNumberMap = nullptr;
// Internally track how many things we've allocated
unsigned nparams = 0;
std::string allocParam(StringRef Type, StringRef Value) {
unsigned ParamNumber;
if (!ParamNumberMap) {
// In pass 1, unconditionally assign a new parameter variable to every
// value we're asked to process.
ParamNumber = nparams++;
} else {
// In pass 2, consult the map provided by the caller to find out which
// variable we should be keeping things in.
int MapValue = (*ParamNumberMap)[nparams++];
if (MapValue < 0)
return std::string(Value);
ParamNumber = MapValue;
}
// If we've allocated a new parameter variable for the first time, store
// its type and value to be retrieved after codegen.
if (ParamTypes && ParamTypes->size() == ParamNumber)
ParamTypes->push_back(std::string(Type));
if (ParamValues && ParamValues->size() == ParamNumber)
ParamValues->push_back(std::string(Value));
// Unimaginative naming scheme for parameter variables.
return "Param" + utostr(ParamNumber);
}
};
// -----------------------------------------------------------------------------
// System of classes that represent all the intermediate values used during
// code-generation for an intrinsic.
//
// The base class 'Result' can represent a value of the LLVM type 'Value', or
// sometimes 'Address' (for loads/stores, including an alignment requirement).
//
// In the case where the Tablegen provides a value in the codegen dag as a
// plain integer literal, the Result object we construct here will be one that
// returns true from hasIntegerConstantValue(). This allows the generated C++
// code to use the constant directly in contexts which can take a literal
// integer, such as Builder.CreateExtractValue(thing, 1), without going to the
// effort of calling llvm::ConstantInt::get() and then pulling the constant
// back out of the resulting llvm:Value later.
class Result {
public:
// Convenient shorthand for the pointer type we'll be using everywhere.
using Ptr = std::shared_ptr<Result>;
private:
Ptr Predecessor;
std::string VarName;
bool VarNameUsed = false;
unsigned Visited = 0;
public:
virtual ~Result() = default;
using Scope = std::map<std::string, Ptr>;
virtual void genCode(raw_ostream &OS, CodeGenParamAllocator &) const = 0;
virtual bool hasIntegerConstantValue() const { return false; }
virtual uint32_t integerConstantValue() const { return 0; }
virtual bool hasIntegerValue() const { return false; }
virtual std::string getIntegerValue(const std::string &) {
llvm_unreachable("non-working Result::getIntegerValue called");
}
virtual std::string typeName() const { return "Value *"; }
// Mostly, when a code-generation operation has a dependency on prior
// operations, it's because it uses the output values of those operations as
// inputs. But there's one exception, which is the use of 'seq' in Tablegen
// to indicate that operations have to be performed in sequence regardless of
// whether they use each others' output values.
//
// So, the actual generation of code is done by depth-first search, using the
// prerequisites() method to get a list of all the other Results that have to
// be computed before this one. That method divides into the 'predecessor',
// set by setPredecessor() while processing a 'seq' dag node, and the list
// returned by 'morePrerequisites', which each subclass implements to return
// a list of the Results it uses as input to whatever its own computation is
// doing.
virtual void morePrerequisites(std::vector<Ptr> &output) const {}
std::vector<Ptr> prerequisites() const {
std::vector<Ptr> ToRet;
if (Predecessor)
ToRet.push_back(Predecessor);
morePrerequisites(ToRet);
return ToRet;
}
void setPredecessor(Ptr p) {
// If the user has nested one 'seq' node inside another, and this
// method is called on the return value of the inner 'seq' (i.e.
// the final item inside it), then we can't link _this_ node to p,
// because it already has a predecessor. Instead, walk the chain
// until we find the first item in the inner seq, and link that to
// p, so that nesting seqs has the obvious effect of linking
// everything together into one long sequential chain.
Result *r = this;
while (r->Predecessor)
r = r->Predecessor.get();
r->Predecessor = p;
}
// Each Result will be assigned a variable name in the output code, but not
// all those variable names will actually be used (e.g. the return value of
// Builder.CreateStore has void type, so nobody will want to refer to it). To
// prevent annoying compiler warnings, we track whether each Result's
// variable name was ever actually mentioned in subsequent statements, so
// that it can be left out of the final generated code.
std::string varname() {
VarNameUsed = true;
return VarName;
}
void setVarname(const StringRef s) { VarName = std::string(s); }
bool varnameUsed() const { return VarNameUsed; }
// Emit code to generate this result as a Value *.
virtual std::string asValue() {
return varname();
}
// Code generation happens in multiple passes. This method tracks whether a
// Result has yet been visited in a given pass, without the need for a
// tedious loop in between passes that goes through and resets a 'visited'
// flag back to false: you just set Pass=1 the first time round, and Pass=2
// the second time.
bool needsVisiting(unsigned Pass) {
bool ToRet = Visited < Pass;
Visited = Pass;
return ToRet;
}
};
// Result subclass that retrieves one of the arguments to the clang builtin
// function. In cases where the argument has pointer type, we call
// EmitPointerWithAlignment and store the result in a variable of type Address,
// so that load and store IR nodes can know the right alignment. Otherwise, we
// call EmitScalarExpr.
//
// There are aggregate parameters in the MVE intrinsics API, but we don't deal
// with them in this Tablegen back end: they only arise in the vld2q/vld4q and
// vst2q/vst4q family, which is few enough that we just write the code by hand
// for those in CGBuiltin.cpp.
class BuiltinArgResult : public Result {
public:
unsigned ArgNum;
bool AddressType;
bool Immediate;
BuiltinArgResult(unsigned ArgNum, bool AddressType, bool Immediate)
: ArgNum(ArgNum), AddressType(AddressType), Immediate(Immediate) {}
void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {
OS << (AddressType ? "EmitPointerWithAlignment" : "EmitScalarExpr")
<< "(E->getArg(" << ArgNum << "))";
}
std::string typeName() const override {
return AddressType ? "Address" : Result::typeName();
}
// Emit code to generate this result as a Value *.
std::string asValue() override {
if (AddressType)
return "(" + varname() + ".emitRawPointer(*this))";
return Result::asValue();
}
bool hasIntegerValue() const override { return Immediate; }
std::string getIntegerValue(const std::string &IntType) override {
return "GetIntegerConstantValue<" + IntType + ">(E->getArg(" +
utostr(ArgNum) + "), getContext())";
}
};
// Result subclass for an integer literal appearing in Tablegen. This may need
// to be turned into an llvm::Result by means of llvm::ConstantInt::get(), or
// it may be used directly as an integer, depending on which IRBuilder method
// it's being passed to.
class IntLiteralResult : public Result {
public:
const ScalarType *IntegerType;
uint32_t IntegerValue;
IntLiteralResult(const ScalarType *IntegerType, uint32_t IntegerValue)
: IntegerType(IntegerType), IntegerValue(IntegerValue) {}
void genCode(raw_ostream &OS,
CodeGenParamAllocator &ParamAlloc) const override {
OS << "llvm::ConstantInt::get("
<< ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName())
<< ", ";
OS << ParamAlloc.allocParam(IntegerType->cName(), utostr(IntegerValue))
<< ")";
}
bool hasIntegerConstantValue() const override { return true; }
uint32_t integerConstantValue() const override { return IntegerValue; }
};
// Result subclass representing a cast between different integer types. We use
// our own ScalarType abstraction as the representation of the target type,
// which gives both size and signedness.
class IntCastResult : public Result {
public:
const ScalarType *IntegerType;
Ptr V;
IntCastResult(const ScalarType *IntegerType, Ptr V)
: IntegerType(IntegerType), V(V) {}
void genCode(raw_ostream &OS,
CodeGenParamAllocator &ParamAlloc) const override {
OS << "Builder.CreateIntCast(" << V->varname() << ", "
<< ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName()) << ", "
<< ParamAlloc.allocParam("bool",
IntegerType->kind() == ScalarTypeKind::SignedInt
? "true"
: "false")
<< ")";
}
void morePrerequisites(std::vector<Ptr> &output) const override {
output.push_back(V);
}
};
// Result subclass representing a cast between different pointer types.
class PointerCastResult : public Result {
public:
const PointerType *PtrType;
Ptr V;
PointerCastResult(const PointerType *PtrType, Ptr V)
: PtrType(PtrType), V(V) {}
void genCode(raw_ostream &OS,
CodeGenParamAllocator &ParamAlloc) const override {
OS << "Builder.CreatePointerCast(" << V->asValue() << ", "
<< ParamAlloc.allocParam("llvm::Type *", PtrType->llvmName()) << ")";
}
void morePrerequisites(std::vector<Ptr> &output) const override {
output.push_back(V);
}
};
// Result subclass representing a call to an IRBuilder method. Each IRBuilder
// method we want to use will have a Tablegen record giving the method name and
// describing any important details of how to call it, such as whether a
// particular argument should be an integer constant instead of an llvm::Value.
class IRBuilderResult : public Result {
public:
StringRef CallPrefix;
std::vector<Ptr> Args;
std::set<unsigned> AddressArgs;
std::map<unsigned, std::string> IntegerArgs;
IRBuilderResult(StringRef CallPrefix, const std::vector<Ptr> &Args,
const std::set<unsigned> &AddressArgs,
const std::map<unsigned, std::string> &IntegerArgs)
: CallPrefix(CallPrefix), Args(Args), AddressArgs(AddressArgs),
IntegerArgs(IntegerArgs) {}
void genCode(raw_ostream &OS,
CodeGenParamAllocator &ParamAlloc) const override {
OS << CallPrefix;
const char *Sep = "";
for (unsigned i = 0, e = Args.size(); i < e; ++i) {
Ptr Arg = Args[i];
auto it = IntegerArgs.find(i);
OS << Sep;
Sep = ", ";
if (it != IntegerArgs.end()) {
if (Arg->hasIntegerConstantValue())
OS << "static_cast<" << it->second << ">("
<< ParamAlloc.allocParam(it->second,
utostr(Arg->integerConstantValue()))
<< ")";
else if (Arg->hasIntegerValue())
OS << ParamAlloc.allocParam(it->second,
Arg->getIntegerValue(it->second));
} else {
OS << Arg->varname();
}
}
OS << ")";
}
void morePrerequisites(std::vector<Ptr> &output) const override {
for (unsigned i = 0, e = Args.size(); i < e; ++i) {
Ptr Arg = Args[i];
if (IntegerArgs.find(i) != IntegerArgs.end())
continue;
output.push_back(Arg);
}
}
};
// Result subclass representing making an Address out of a Value.
class AddressResult : public Result {
public:
Ptr Arg;
const Type *Ty;
unsigned Align;
AddressResult(Ptr Arg, const Type *Ty, unsigned Align)
: Arg(Arg), Ty(Ty), Align(Align) {}
void genCode(raw_ostream &OS,
CodeGenParamAllocator &ParamAlloc) const override {
OS << "Address(" << Arg->varname() << ", " << Ty->llvmName()
<< ", CharUnits::fromQuantity(" << Align << "))";
}
std::string typeName() const override {
return "Address";
}
void morePrerequisites(std::vector<Ptr> &output) const override {
output.push_back(Arg);
}
};
// Result subclass representing a call to an IR intrinsic, which we first have
// to look up using an Intrinsic::ID constant and an array of types.
class IRIntrinsicResult : public Result {
public:
std::string IntrinsicID;
std::vector<const Type *> ParamTypes;
std::vector<Ptr> Args;
IRIntrinsicResult(StringRef IntrinsicID,
const std::vector<const Type *> &ParamTypes,
const std::vector<Ptr> &Args)
: IntrinsicID(std::string(IntrinsicID)), ParamTypes(ParamTypes),
Args(Args) {}
void genCode(raw_ostream &OS,
CodeGenParamAllocator &ParamAlloc) const override {
std::string IntNo = ParamAlloc.allocParam(
"Intrinsic::ID", "Intrinsic::" + IntrinsicID);
OS << "Builder.CreateCall(CGM.getIntrinsic(" << IntNo;
if (!ParamTypes.empty()) {
OS << ", {";
const char *Sep = "";
for (auto T : ParamTypes) {
OS << Sep << ParamAlloc.allocParam("llvm::Type *", T->llvmName());
Sep = ", ";
}
OS << "}";
}
OS << "), {";
const char *Sep = "";
for (auto Arg : Args) {
OS << Sep << Arg->asValue();
Sep = ", ";
}
OS << "})";
}
void morePrerequisites(std::vector<Ptr> &output) const override {
output.insert(output.end(), Args.begin(), Args.end());
}
};
// Result subclass that specifies a type, for use in IRBuilder operations such
// as CreateBitCast that take a type argument.
class TypeResult : public Result {
public:
const Type *T;
TypeResult(const Type *T) : T(T) {}
void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {
OS << T->llvmName();
}
std::string typeName() const override {
return "llvm::Type *";
}
};
// -----------------------------------------------------------------------------
// Class that describes a single ACLE intrinsic.
//
// A Tablegen record will typically describe more than one ACLE intrinsic, by
// means of setting the 'list<Type> Params' field to a list of multiple
// parameter types, so as to define vaddq_{s8,u8,...,f16,f32} all in one go.
// We'll end up with one instance of ACLEIntrinsic for *each* parameter type,
// rather than a single one for all of them. Hence, the constructor takes both
// a Tablegen record and the current value of the parameter type.
class ACLEIntrinsic {
// Structure documenting that one of the intrinsic's arguments is required to
// be a compile-time constant integer, and what constraints there are on its
// value. Used when generating Sema checking code.
struct ImmediateArg {
enum class BoundsType { ExplicitRange, UInt };
BoundsType boundsType;
int64_t i1, i2;
StringRef ExtraCheckType, ExtraCheckArgs;
const Type *ArgType;
};
// For polymorphic intrinsics, FullName is the explicit name that uniquely
// identifies this variant of the intrinsic, and ShortName is the name it
// shares with at least one other intrinsic.
std::string ShortName, FullName;
// Name of the architecture extension, used in the Clang builtin name
StringRef BuiltinExtension;
// A very small number of intrinsics _only_ have a polymorphic
// variant (vuninitializedq taking an unevaluated argument).
bool PolymorphicOnly;
// Another rarely-used flag indicating that the builtin doesn't
// evaluate its argument(s) at all.
bool NonEvaluating;
// True if the intrinsic needs only the C header part (no codegen, semantic
// checks, etc). Used for redeclaring MVE intrinsics in the arm_cde.h header.
bool HeaderOnly;
const Type *ReturnType;
std::vector<const Type *> ArgTypes;
std::map<unsigned, ImmediateArg> ImmediateArgs;
Result::Ptr Code;
std::map<std::string, std::string> CustomCodeGenArgs;
// Recursive function that does the internals of code generation.
void genCodeDfs(Result::Ptr V, std::list<Result::Ptr> &Used,
unsigned Pass) const {
if (!V->needsVisiting(Pass))
return;
for (Result::Ptr W : V->prerequisites())
genCodeDfs(W, Used, Pass);
Used.push_back(V);
}
public:
const std::string &shortName() const { return ShortName; }
const std::string &fullName() const { return FullName; }
StringRef builtinExtension() const { return BuiltinExtension; }
const Type *returnType() const { return ReturnType; }
const std::vector<const Type *> &argTypes() const { return ArgTypes; }
bool requiresFloat() const {
if (ReturnType->requiresFloat())
return true;
for (const Type *T : ArgTypes)
if (T->requiresFloat())
return true;
return false;
}
bool requiresMVE() const {
return ReturnType->requiresMVE() ||
any_of(ArgTypes, [](const Type *T) { return T->requiresMVE(); });
}
bool polymorphic() const { return ShortName != FullName; }
bool polymorphicOnly() const { return PolymorphicOnly; }
bool nonEvaluating() const { return NonEvaluating; }
bool headerOnly() const { return HeaderOnly; }
// External entry point for code generation, called from EmitterBase.
void genCode(raw_ostream &OS, CodeGenParamAllocator &ParamAlloc,
unsigned Pass) const {
assert(!headerOnly() && "Called genCode for header-only intrinsic");
if (!hasCode()) {
for (auto kv : CustomCodeGenArgs)
OS << " " << kv.first << " = " << kv.second << ";\n";
OS << " break; // custom code gen\n";
return;
}
std::list<Result::Ptr> Used;
genCodeDfs(Code, Used, Pass);
unsigned varindex = 0;
for (Result::Ptr V : Used)
if (V->varnameUsed())
V->setVarname("Val" + utostr(varindex++));
for (Result::Ptr V : Used) {
OS << " ";
if (V == Used.back()) {
assert(!V->varnameUsed());
OS << "return "; // FIXME: what if the top-level thing is void?
} else if (V->varnameUsed()) {
std::string Type = V->typeName();
OS << V->typeName();
if (!StringRef(Type).ends_with("*"))
OS << " ";
OS << V->varname() << " = ";
}
V->genCode(OS, ParamAlloc);
OS << ";\n";
}
}
bool hasCode() const { return Code != nullptr; }
static std::string signedHexLiteral(const llvm::APInt &iOrig) {
llvm::APInt i = iOrig.trunc(64);
SmallString<40> s;
i.toString(s, 16, true, true);
return std::string(s);
}
std::string genSema() const {
assert(!headerOnly() && "Called genSema for header-only intrinsic");
std::vector<std::string> SemaChecks;
for (const auto &kv : ImmediateArgs) {
const ImmediateArg &IA = kv.second;
llvm::APInt lo(128, 0), hi(128, 0);
switch (IA.boundsType) {
case ImmediateArg::BoundsType::ExplicitRange:
lo = IA.i1;
hi = IA.i2;
break;
case ImmediateArg::BoundsType::UInt:
lo = 0;
hi = llvm::APInt::getMaxValue(IA.i1).zext(128);
break;
}
std::string Index = utostr(kv.first);
// Emit a range check if the legal range of values for the
// immediate is smaller than the _possible_ range of values for
// its type.
unsigned ArgTypeBits = IA.ArgType->sizeInBits();
llvm::APInt ArgTypeRange = llvm::APInt::getMaxValue(ArgTypeBits).zext(128);
llvm::APInt ActualRange = (hi-lo).trunc(64).sext(128);
if (ActualRange.ult(ArgTypeRange))
SemaChecks.push_back("SemaRef.BuiltinConstantArgRange(TheCall, " +
Index + ", " + signedHexLiteral(lo) + ", " +
signedHexLiteral(hi) + ")");
if (!IA.ExtraCheckType.empty()) {
std::string Suffix;
if (!IA.ExtraCheckArgs.empty()) {
std::string tmp;
StringRef Arg = IA.ExtraCheckArgs;
if (Arg == "!lanesize") {
tmp = utostr(IA.ArgType->sizeInBits());
Arg = tmp;
}
Suffix = (Twine(", ") + Arg).str();
}
SemaChecks.push_back((Twine("SemaRef.BuiltinConstantArg") +
IA.ExtraCheckType + "(TheCall, " + Index +
Suffix + ")")
.str());
}
assert(!SemaChecks.empty());
}
if (SemaChecks.empty())
return "";
return join(std::begin(SemaChecks), std::end(SemaChecks),
" ||\n ") +
";\n";
}
ACLEIntrinsic(EmitterBase &ME, Record *R, const Type *Param);
};
// -----------------------------------------------------------------------------
// The top-level class that holds all the state from analyzing the entire
// Tablegen input.
class EmitterBase {
protected:
// EmitterBase holds a collection of all the types we've instantiated.
VoidType Void;
std::map<std::string, std::unique_ptr<ScalarType>> ScalarTypes;
std::map<std::tuple<ScalarTypeKind, unsigned, unsigned>,
std::unique_ptr<VectorType>>
VectorTypes;
std::map<std::pair<std::string, unsigned>, std::unique_ptr<MultiVectorType>>
MultiVectorTypes;
std::map<unsigned, std::unique_ptr<PredicateType>> PredicateTypes;
std::map<std::string, std::unique_ptr<PointerType>> PointerTypes;
// And all the ACLEIntrinsic instances we've created.
std::map<std::string, std::unique_ptr<ACLEIntrinsic>> ACLEIntrinsics;
public:
// Methods to create a Type object, or return the right existing one from the
// maps stored in this object.
const VoidType *getVoidType() { return &Void; }
const ScalarType *getScalarType(StringRef Name) {
return ScalarTypes[std::string(Name)].get();
}
const ScalarType *getScalarType(Record *R) {
return getScalarType(R->getName());
}
const VectorType *getVectorType(const ScalarType *ST, unsigned Lanes) {
std::tuple<ScalarTypeKind, unsigned, unsigned> key(ST->kind(),
ST->sizeInBits(), Lanes);
if (VectorTypes.find(key) == VectorTypes.end())
VectorTypes[key] = std::make_unique<VectorType>(ST, Lanes);
return VectorTypes[key].get();
}