-
Notifications
You must be signed in to change notification settings - Fork 222
/
SPIRVTypeScavenger.h
225 lines (198 loc) · 10.1 KB
/
SPIRVTypeScavenger.h
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
//===- SPIRVTypeScavenger.h - Recover pointer types in opaque pointer IR --===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal with the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimers in the documentation
// and/or other materials provided with the distribution.
// Neither the names of The Khronos Group, nor the names of its
// contributors may be used to endorse or promote products derived from this
// Software without specific prior written permission.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
// THE SOFTWARE.
//
//===----------------------------------------------------------------------===//
//
// This file implements the necessary logic to recover pointer types from LLVM
// IR for the output SPIR-V file after LLVM IR completes its transition to
// opaque pointers.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRVTYPESCAVENGER_H
#define SPIRVTYPESCAVENGER_H
#include "llvm/ADT/IntEqClasses.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ValueMap.h"
using namespace llvm;
/// This class allows for the recovery of typed pointer types from LLVM opaque
/// pointer types. A detailed description of how this algorithm works may be
/// found in the file comment of SPIRVTypeScavenger.cpp.
class SPIRVTypeScavenger {
/// The mapping from type variables to concrete types.
std::vector<Type *> TypeVariables;
/// The structure storing which type variables have been unified.
IntEqClasses UnifiedTypeVars;
/// Replace all ptr types found within T with new type variables.
Type *allocateTypeVariable(Type *T);
/// Replace all type variables found within T with their concrete types. If
/// the type variable doesn't have a concrete type yet, the type variable will
/// be retained.
Type *substituteTypeVariables(Type *T);
/// Try to resolve all type variables into concrete types using knowledge that
/// T1 and T2 have to be the same type. If T1 and T2 cannot be made the same
/// type, return false (and callers will know they need to insert synthetic
/// bitcasts to guarantee equality).
bool unifyType(Type *T1, Type *T2);
/// This stores the Value -> corrected type mapping for the module. It is
/// expected that all instructions, arguments, and global values will appear
/// in this mapping, while constants are not expected to be listed here.
ValueMap<Value *, Type *> DeducedTypes;
/// Store associated type variables for certain instructions. In the case
/// where a return value has an association with an operand, it's necessary
/// that the type variable used to generate type rules be the same for all
/// invocations of getTypeRules. This variable allows storage of such
/// variables.
ValueMap<Value *, Type *> AssociatedTypeVariables;
/// A type rule, which expresses that the given operand of a User must have
/// the given type (which may contain type variables).
struct TypeRule {
unsigned OpNo;
bool LhsIndirect;
bool RhsIndirect;
PointerUnion<Type *, Use *> Target;
TypeRule(unsigned A, bool AIndirect, Type *B, bool BIndirect)
: OpNo(A), LhsIndirect(AIndirect), RhsIndirect(BIndirect), Target(B) {}
TypeRule(unsigned A, bool AIndirect, Use *B, bool BIndirect)
: OpNo(A), LhsIndirect(AIndirect), RhsIndirect(BIndirect), Target(B) {}
/// Establishes typeof(operand) == concrete type
static TypeRule is(unsigned OpIndex, Type *Ty) {
return TypeRule(OpIndex, false, Ty, false);
}
/// Establishes typeof(operand) == concrete type
static TypeRule is(Use &U, Type *Ty) {
return TypeRule::is(U.getOperandNo(), Ty);
}
/// Establishes typeof(operand) == typeof(operand)
static TypeRule is(User &U, unsigned Op1, unsigned Op2) {
return TypeRule(Op1, false, &U.getOperandUse(Op2), false);
}
/// Establishes typedptr(typeof(operand)) == typedptr(typeof(operand))
/// (this is useful when the address spaces do not need to match).
static TypeRule isIndirect(User &U, unsigned Op1, unsigned Op2) {
return TypeRule(Op1, true, &U.getOperandUse(Op2), true);
}
/// Establishes typeof(operand) == typedptr(concrete type)
static TypeRule pointsTo(Use &U, Type *Ty) {
return TypeRule(U.getOperandNo(), false, Ty, true);
}
/// Establishes typeof(operand) == typedptr(concrete type)
static TypeRule pointsTo(User &U, unsigned OpIndex, Type *Ty) {
return TypeRule::pointsTo(U.getOperandUse(OpIndex), Ty);
}
/// Establishes typeof(mem operand) == typedptr(typeof(val operand))
static TypeRule pointsTo(User &U, unsigned MemIndex, unsigned ValIndex) {
return TypeRule(MemIndex, false, &U.getOperandUse(ValIndex), true);
}
/// Establishes typeof(operand) == typedptr(typeof(return))
static TypeRule pointsToReturn(User &U, unsigned OpIndex) {
return TypeRule(RETURN_OPERAND, true, &U.getOperandUse(OpIndex), false);
}
/// Establishes typeof(return) == concrete type
static TypeRule returns(Type *Ty) {
return TypeRule(RETURN_OPERAND, false, Ty, false);
}
/// Establishes typeof(return) == typedptr(concrete type)
static TypeRule returnsPointerTo(Type *Ty) {
return TypeRule(RETURN_OPERAND, false, Ty, true);
}
/// Establishes typeof(return) == typeof(operand)
static TypeRule propagates(Use &U) {
return TypeRule(RETURN_OPERAND, false, &U, false);
}
/// Establishes typeof(return) == typeof(operand)
static TypeRule propagates(User &U, unsigned OpIndex) {
return TypeRule::propagates(U.getOperandUse(OpIndex));
}
/// Establishes typedptr(typeof(return)) == typedptr(typeof(operand))
static TypeRule propagatesIndirect(Use &U) {
return TypeRule(RETURN_OPERAND, true, &U, true);
}
/// Establishes typedptr(typeof(return)) == typedptr(typeof(operand))
static TypeRule propagatesIndirect(User &U, unsigned OpIndex) {
return TypeRule::propagatesIndirect(U.getOperandUse(OpIndex));
}
};
/// This is a value that allows the ability to express the type of a value as
/// a whole in a typing rule.
static constexpr unsigned RETURN_OPERAND = ~0U;
/// Turn a type rule into an operand and a type to check for. If the type of
/// the operand and the type to check against cannot be unified, then a
/// bitcast will need to be inserted for the use.
std::pair<Use &, Type *> getTypeCheck(Instruction &I, const TypeRule &Rule);
/// Retrieve the list of typing rules for an instruction.
void getTypeRules(Instruction &I, SmallVectorImpl<TypeRule> &Rules);
/// Get the best guess for the type of the value, applying any type rules to
/// the return value of an instruction that exist. The return type may refer
/// to type variables that have yet to be resolved, if the type rules are
/// insufficient to establish a typed pointer type for the instruction.
Type *getTypeAfterRules(Value *V);
/// Enforce that the pointer element types of all operands of the instruction
/// matches the type that the instruction itself requires. If a pointer
/// element type of one of the operands is deferred, this will type the use
/// correctly.
void correctUseTypes(Instruction &I);
/// This assigns known pointer element types for the parameters of a function.
/// This method should be called for all functions before doing any type
/// analysis on the module.
void deduceFunctionType(Function &F);
/// This computes known type rules of a call to an LLVM intrinsic or specific
/// well-known function name. Returns true if the call was known to this
/// function.
bool typeIntrinsicCall(CallBase &CB, SmallVectorImpl<TypeRule> &TypeRules);
/// Get the type rules for checking argument and return value compatibility
/// for the function type being called. This is meant to help unify cases
/// for indirect function calls.
void typeFunctionParams(CallBase &CB, FunctionType *FT, unsigned ArgStart,
bool IncludeRet,
SmallVectorImpl<TypeRule> &TypeRules);
/// Compute the type of a global variable or global alias, based on the type
/// of the initializer (which may be null for global variables).
void typeGlobalValue(GlobalValue &GV, Constant *Init);
/// Compute pointer element types for all pertinent values in the module.
void typeModule(Module &M);
/// This stores a list of instructions whose pointer element types are
/// currently being investigated, to avoid the possibility of infinite cycles.
std::vector<Value *> VisitStack;
public:
explicit SPIRVTypeScavenger(Module &M) : UnifiedTypeVars(1024) {
typeModule(M);
}
/// Get the type of the value, with pointer types replaced with
/// TypedPointerType types instead.
Type *getScavengedType(Value *V);
/// Get the deduced function type for a function, with pointer types replaced
/// with TypedPointerTypes (maybe including type variables).
FunctionType *getFunctionType(Function *F);
};
#endif // SPIRVTYPESCAVENGER_H