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
|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#ifndef _HW_INTRINSIC_XARCH_H_
#define _HW_INTRINSIC_XARCH_H_
#ifdef FEATURE_HW_INTRINSICS
enum HWIntrinsicCategory : unsigned int
{
// Simple SIMD intrinsics
// - take Vector128/256<T> parameters
// - return a Vector128/256<T>
// - the codegen of overloads can be determined by intrinsicID and base type of returned vector
HW_Category_SimpleSIMD,
// IsSupported Property
// - each ISA class has an "IsSupported" property
HW_Category_IsSupportedProperty,
// IMM intrinsics
// - some SIMD intrinsics requires immediate value (i.e. imm8) to generate instruction
HW_Category_IMM,
// Scalar intrinsics
// - operate over general purpose registers, like crc32, lzcnt, popcnt, etc.
HW_Category_Scalar,
// SIMD scalar
// - operate over vector registers(XMM), but just compute on the first element
HW_Category_SIMDScalar,
// Memory access intrinsics
// - e.g., Avx.Load, Avx.Store, Sse.LoadAligned
HW_Category_MemoryLoad,
HW_Category_MemoryStore,
// Helper intrinsics
// - do not directly correspond to a instruction, such as Avx.SetAllVector256
HW_Category_Helper,
// Special intrinsics
// - have to be addressed specially
HW_Category_Special
};
enum HWIntrinsicFlag : unsigned int
{
HW_Flag_NoFlag = 0,
// Commutative
// - if a binary-op intrinsic is commutative (e.g., Add, Multiply), its op1 can be contained
HW_Flag_Commutative = 0x1,
// Full range IMM intrinsic
// - the immediate value is valid on the full range of imm8 (0-255)
HW_Flag_FullRangeIMM = 0x2,
// Generic
// - must throw NotSupportException if the type argument is not numeric type
HW_Flag_OneTypeGeneric = 0x4,
// Two-type Generic
// - the intrinsic has two type parameters
HW_Flag_TwoTypeGeneric = 0x8,
// NoCodeGen
// - should be transformed in the compiler front-end, cannot reach CodeGen
HW_Flag_NoCodeGen = 0x10,
// Unfixed SIMD-size
// - overloaded on multiple vector sizes (SIMD size in the table is unreliable)
HW_Flag_UnfixedSIMDSize = 0x20,
// Complex overload
// - the codegen of overloads cannot be determined by intrinsicID and base type
HW_Flag_ComplexOverloads = 0x40,
// Multi-instruction
// - that one intrinsic can generate multiple instructions
HW_Flag_MultiIns = 0x80,
// NoContainment
// the intrinsic cannot be contained
HW_Flag_NoContainment = 0x100,
// Copy Upper bits
// some SIMD scalar intrinsics need the semantics of copying upper bits from the source operand
HW_Flag_CopyUpperBits = 0x200,
// Select base type using the first argument type
HW_Flag_BaseTypeFromFirstArg = 0x400,
// Indicates compFloatingPointUsed does not need to be set.
HW_Flag_NoFloatingPointUsed = 0x800,
// Maybe IMM
// the intrinsic has either imm or Vector overloads
HW_Flag_MaybeIMM = 0x1000,
// NoJmpTable IMM
// the imm intrinsic does not need jumptable fallback when it gets non-const argument
HW_Flag_NoJmpTableIMM = 0x2000,
// 64-bit intrinsics
// Intrinsics that operate over 64-bit general purpose registers are not supported on 32-bit platform
HW_Flag_64BitOnly = 0x4000,
HW_Flag_SecondArgMaybe64Bit = 0x8000,
// Select base type using the second argument type
HW_Flag_BaseTypeFromSecondArg = 0x10000,
// Special codegen
// the intrinsics need special rules in CodeGen,
// but may be table-driven in the front-end
HW_Flag_SpecialCodeGen = 0x20000,
// No Read/Modify/Write Semantics
// the intrinsic doesn't have read/modify/write semantics in two/three-operand form.
HW_Flag_NoRMWSemantics = 0x40000,
// Special import
// the intrinsics need special rules in importer,
// but may be table-driven in the back-end
HW_Flag_SpecialImport = 0x80000,
};
struct HWIntrinsicInfo
{
NamedIntrinsic id;
const char* name;
InstructionSet isa;
int ival;
unsigned simdSize;
int numArgs;
instruction ins[10];
HWIntrinsicCategory category;
HWIntrinsicFlag flags;
static const HWIntrinsicInfo& lookup(NamedIntrinsic id);
static NamedIntrinsic lookupId(const char* className, const char* methodName);
static InstructionSet lookupIsa(const char* className);
static unsigned lookupSimdSize(Compiler* comp, NamedIntrinsic id, CORINFO_SIG_INFO* sig);
static int lookupNumArgs(const GenTreeHWIntrinsic* node);
static GenTree* lookupLastOp(const GenTreeHWIntrinsic* node);
static bool isImmOp(NamedIntrinsic id, const GenTree* op);
static int lookupImmUpperBound(NamedIntrinsic id);
static bool isFullyImplementedIsa(InstructionSet isa);
static bool isScalarIsa(InstructionSet isa);
// Member lookup
static NamedIntrinsic lookupId(NamedIntrinsic id)
{
return lookup(id).id;
}
static const char* lookupName(NamedIntrinsic id)
{
return lookup(id).name;
}
static InstructionSet lookupIsa(NamedIntrinsic id)
{
return lookup(id).isa;
}
static int lookupIval(NamedIntrinsic id)
{
return lookup(id).ival;
}
static unsigned lookupSimdSize(NamedIntrinsic id)
{
return lookup(id).simdSize;
}
static int lookupNumArgs(NamedIntrinsic id)
{
return lookup(id).numArgs;
}
static instruction lookupIns(NamedIntrinsic id, var_types type)
{
assert((type >= TYP_BYTE) && (type <= TYP_DOUBLE));
return lookup(id).ins[type - TYP_BYTE];
}
static HWIntrinsicCategory lookupCategory(NamedIntrinsic id)
{
return lookup(id).category;
}
static HWIntrinsicFlag lookupFlags(NamedIntrinsic id)
{
return lookup(id).flags;
}
};
#endif // FEATURE_HW_INTRINSICS
#endif // _HW_INTRINSIC_XARCH_H_
|