Skip to content

Commit cb6de04

Browse files
[OpaquePointers] Rewrite the type scavenger. (#1957)
This is a massive change, which consists of the following pieces that are not so easily seperable (why this is a large, single commit instead of many smaller ones): Use typed pointer types in the external interface of the scavenger. Use target extension types in lieu of DeferredType to represent types not yet known. These types are now target("typevar", N), where N is some integer. Use TypedPointerType in lieu of DeducedType. This makes multi-level pointer types simpler to use. Replace std::pair<unsigned, DeducedType> with a more generalized TypeRule class, which simplifies some of the rules for the typing procedure. Make computePointerElementType and correctUseTypes both rely on the same list of type rules, removing duplication. Add support for vectors of pointers, arrays of pointers, and function pointers in the type scavenger. Allow global variables to have scavenged types. Allow return types to not be i8*. Add more documentation of the type scavenger process. More tests! Miscellaneous type rule fixes. Overall, the logic of the type scavenger largely remains unchanged (albeit with increased functionality), but the way that logic is expressed has changed very significantly, and the easier functionality for handling a few cases causes shifting of expected bitcast placements in existing tests.
1 parent f990940 commit cb6de04

File tree

19 files changed

+1098
-558
lines changed

19 files changed

+1098
-558
lines changed

lib/SPIRV/SPIRVTypeScavenger.cpp

Lines changed: 756 additions & 382 deletions
Large diffs are not rendered by default.

lib/SPIRV/SPIRVTypeScavenger.h

Lines changed: 146 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -41,52 +41,138 @@
4141
#ifndef SPIRVTYPESCAVENGER_H
4242
#define SPIRVTYPESCAVENGER_H
4343

44+
#include "llvm/ADT/IntEqClasses.h"
4445
#include "llvm/ADT/PointerUnion.h"
4546
#include "llvm/IR/Instructions.h"
4647
#include "llvm/IR/Module.h"
4748
#include "llvm/IR/ValueMap.h"
4849

4950
using namespace llvm;
5051

51-
/// This class allows for the recovery of pointer element types from LLVM
52-
/// opaque pointer types.
52+
/// This class allows for the recovery of typed pointer types from LLVM opaque
53+
/// pointer types. A detailed description of how this algorithm works may be
54+
/// found in the file comment of SPIRVTypeScavenger.cpp.
5355
class SPIRVTypeScavenger {
54-
/// A representation of a pointer whose type will be determined by uses. This
55-
/// will include every value that needs to be assigned the same type.
56-
struct DeferredType {
57-
std::vector<Value *> Values;
56+
/// The mapping from type variables to concrete types.
57+
std::vector<Type *> TypeVariables;
58+
59+
/// The structure storing which type variables have been unified.
60+
IntEqClasses UnifiedTypeVars;
61+
62+
/// Replace all ptr types found within T with new type variables.
63+
Type *allocateTypeVariable(Type *T);
64+
65+
/// Replace all type variables found within T with their concrete types. If
66+
/// the type variable doesn't have a concrete type yet, the type variable will
67+
/// be retained.
68+
Type *substituteTypeVariables(Type *T);
69+
70+
/// Try to resolve all type variables into concrete types using knowledge that
71+
/// T1 and T2 have to be the same type. If T1 and T2 cannot be made the same
72+
/// type, return false (and callers will know they need to insert synthetic
73+
/// bitcasts to guarantee equality).
74+
bool unifyType(Type *T1, Type *T2);
75+
76+
/// This stores the Value -> corrected type mapping for the module. It is
77+
/// expected that all instructions, arguments, and global values will appear
78+
/// in this mapping, while constants are not expected to be listed here.
79+
ValueMap<Value *, Type *> DeducedTypes;
80+
81+
/// Store associated type variables for certain instructions. In the case
82+
/// where a return value has an association with an operand, it's necessary
83+
/// that the type variable used to generate type rules be the same for all
84+
/// invocations of getTypeRules. This variable allows storage of such
85+
/// variables.
86+
ValueMap<Value *, Type *> AssociatedTypeVariables;
87+
88+
/// A type rule, which expresses that the given operand of a User must have
89+
/// the given type (which may contain type variables).
90+
struct TypeRule {
91+
unsigned OpNo;
92+
bool LhsIndirect;
93+
bool RhsIndirect;
94+
PointerUnion<Type *, Use *> Target;
95+
TypeRule(unsigned A, bool AIndirect, Type *B, bool BIndirect)
96+
: OpNo(A), LhsIndirect(AIndirect), RhsIndirect(BIndirect), Target(B) {}
97+
TypeRule(unsigned A, bool AIndirect, Use *B, bool BIndirect)
98+
: OpNo(A), LhsIndirect(AIndirect), RhsIndirect(BIndirect), Target(B) {}
99+
100+
/// Establishes typeof(operand) == concrete type
101+
static TypeRule is(unsigned OpIndex, Type *Ty) {
102+
return TypeRule(OpIndex, false, Ty, false);
103+
}
104+
/// Establishes typeof(operand) == concrete type
105+
static TypeRule is(Use &U, Type *Ty) {
106+
return TypeRule::is(U.getOperandNo(), Ty);
107+
}
108+
/// Establishes typeof(operand) == typeof(operand)
109+
static TypeRule is(User &U, unsigned Op1, unsigned Op2) {
110+
return TypeRule(Op1, false, &U.getOperandUse(Op2), false);
111+
}
112+
/// Establishes typedptr(typeof(operand)) == typedptr(typeof(operand))
113+
/// (this is useful when the address spaces do not need to match).
114+
static TypeRule isIndirect(User &U, unsigned Op1, unsigned Op2) {
115+
return TypeRule(Op1, true, &U.getOperandUse(Op2), true);
116+
}
117+
/// Establishes typeof(operand) == typedptr(concrete type)
118+
static TypeRule pointsTo(Use &U, Type *Ty) {
119+
return TypeRule(U.getOperandNo(), false, Ty, true);
120+
}
121+
/// Establishes typeof(operand) == typedptr(concrete type)
122+
static TypeRule pointsTo(User &U, unsigned OpIndex, Type *Ty) {
123+
return TypeRule::pointsTo(U.getOperandUse(OpIndex), Ty);
124+
}
125+
/// Establishes typeof(mem operand) == typedptr(typeof(val operand))
126+
static TypeRule pointsTo(User &U, unsigned MemIndex, unsigned ValIndex) {
127+
return TypeRule(MemIndex, false, &U.getOperandUse(ValIndex), true);
128+
}
129+
/// Establishes typeof(operand) == typedptr(typeof(return))
130+
static TypeRule pointsToReturn(User &U, unsigned OpIndex) {
131+
return TypeRule(RETURN_OPERAND, true, &U.getOperandUse(OpIndex), false);
132+
}
133+
/// Establishes typeof(return) == concrete type
134+
static TypeRule returns(Type *Ty) {
135+
return TypeRule(RETURN_OPERAND, false, Ty, false);
136+
}
137+
/// Establishes typeof(return) == typedptr(concrete type)
138+
static TypeRule returnsPointerTo(Type *Ty) {
139+
return TypeRule(RETURN_OPERAND, false, Ty, true);
140+
}
141+
/// Establishes typeof(return) == typeof(operand)
142+
static TypeRule propagates(Use &U) {
143+
return TypeRule(RETURN_OPERAND, false, &U, false);
144+
}
145+
/// Establishes typeof(return) == typeof(operand)
146+
static TypeRule propagates(User &U, unsigned OpIndex) {
147+
return TypeRule::propagates(U.getOperandUse(OpIndex));
148+
}
149+
/// Establishes typedptr(typeof(return)) == typedptr(typeof(operand))
150+
static TypeRule propagatesIndirect(Use &U) {
151+
return TypeRule(RETURN_OPERAND, true, &U, true);
152+
}
153+
/// Establishes typedptr(typeof(return)) == typedptr(typeof(operand))
154+
static TypeRule propagatesIndirect(User &U, unsigned OpIndex) {
155+
return TypeRule::propagatesIndirect(U.getOperandUse(OpIndex));
156+
}
58157
};
59158

60-
/// This is called when a deferred type is fixed to a known use.
61-
void fixType(DeferredType &Deferred, Type *AssignedType);
62-
63-
/// This merges two deferred types into one deferred type.
64-
void mergeType(DeferredType *A, DeferredType *B);
65-
66-
/// A representation of the possible states of a type internal to this pass:
67-
/// it may be either
68-
/// * Something with a fixed LLVM type (this is a Type *)
69-
/// * A type whose pointer element type is yet unknown (DeferredType *)
70-
/// * A multi-level pointer type (this is a Value *, whose type is what this
71-
/// pointer type will point to). The latter should only exist for
72-
/// pointer operands of memory operations that return ptr.
73-
typedef PointerUnion<Type *, DeferredType *, Value *> DeducedType;
74-
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeducedType Ty) {
75-
if (auto *AsTy = dyn_cast<Type *>(Ty))
76-
return OS << *AsTy;
77-
if (auto *AsDeferred = dyn_cast<DeferredType *>(Ty))
78-
return OS << "deferred type for " << *AsDeferred->Values[0];
79-
if (auto *AsValue = dyn_cast<Value *>(Ty))
80-
return OS << "points to " << *AsValue;
81-
return OS;
82-
}
159+
/// This is a value that allows the ability to express the type of a value as
160+
/// a whole in a typing rule.
161+
static constexpr unsigned RETURN_OPERAND = ~0U;
162+
163+
/// Turn a type rule into an operand and a type to check for. If the type of
164+
/// the operand and the type to check against cannot be unified, then a
165+
/// bitcast will need to be inserted for the use.
166+
std::pair<Use &, Type *> getTypeCheck(Instruction &I, const TypeRule &Rule);
83167

84-
/// Compute the pointer element type of a value, solely based on its
85-
/// definition. The value must be pointer-valued.
86-
DeducedType computePointerElementType(Value *V);
168+
/// Retrieve the list of typing rules for an instruction.
169+
void getTypeRules(Instruction &I, SmallVectorImpl<TypeRule> &Rules);
87170

88-
/// This stores the Value -> pointer element type mapping for the module.
89-
ValueMap<Value *, DeducedType> DeducedTypes;
171+
/// Get the best guess for the type of the value, applying any type rules to
172+
/// the return value of an instruction that exist. The return type may refer
173+
/// to type variables that have yet to be resolved, if the type rules are
174+
/// insufficient to establish a typed pointer type for the instruction.
175+
Type *getTypeAfterRules(Value *V);
90176

91177
/// Enforce that the pointer element types of all operands of the instruction
92178
/// matches the type that the instruction itself requires. If a pointer
@@ -99,17 +185,21 @@ class SPIRVTypeScavenger {
99185
/// analysis on the module.
100186
void deduceFunctionType(Function &F);
101187

102-
/// This computes the known types of a call to an LLVM intrinsic or specific
103-
/// well-known function name. Returns true if the call filled in type
104-
/// information.
105-
///
106-
/// The ArgTys parameter contains a list of known type uses for the parameters
107-
/// of the function call. Each element is a pair, with the first being the
108-
/// operand number, and the second indicating either a known type or an
109-
/// unknown type variable (DeferredType).
110-
bool
111-
typeIntrinsicCall(CallBase &CB,
112-
SmallVectorImpl<std::pair<unsigned, DeducedType>> &ArgTys);
188+
/// This computes known type rules of a call to an LLVM intrinsic or specific
189+
/// well-known function name. Returns true if the call was known to this
190+
/// function.
191+
bool typeIntrinsicCall(CallBase &CB, SmallVectorImpl<TypeRule> &TypeRules);
192+
193+
/// Get the type rules for checking argument and return value compatibility
194+
/// for the function type being called. This is meant to help unify cases
195+
/// for indirect function calls.
196+
void typeFunctionParams(CallBase &CB, FunctionType *FT, unsigned ArgStart,
197+
bool IncludeRet,
198+
SmallVectorImpl<TypeRule> &TypeRules);
199+
200+
/// Compute the type of a global variable or global alias, based on the type
201+
/// of the initializer (which may be null for global variables).
202+
void typeGlobalValue(GlobalValue &GV, Constant *Init);
113203

114204
/// Compute pointer element types for all pertinent values in the module.
115205
void typeModule(Module &M);
@@ -119,24 +209,17 @@ class SPIRVTypeScavenger {
119209
std::vector<Value *> VisitStack;
120210

121211
public:
122-
explicit SPIRVTypeScavenger(Module &M) { typeModule(M); }
123-
124-
/// This type represents the type that a pointer element type of a type. If it
125-
/// is a Type value, then the pointee type represents a pointer to that type.
126-
/// If it is a Value value, then the pointee type is the type of that value
127-
/// (which should be a pointer-typed value.)
128-
typedef PointerUnion<Type *, Value *> PointeeType;
129-
130-
/// Get the pointer element type of the value.
131-
/// If the type is a multi-level pointer, then PointeeType will be a Value
132-
/// whose pointee type can be recursively queried through this method.
133-
/// Otherwise, it will be a pointer to the Type returned by this method.
134-
PointeeType getPointerElementType(Value *V);
135-
136-
/// Get the pointer element type of an argument of the given function. Since
137-
/// this type is guaranteed to not be a multi-level pointer type, the result
138-
/// is an LLVM type instead of a PointeeType.
139-
Type *getArgumentPointerElementType(Function *F, unsigned ArgNo);
212+
explicit SPIRVTypeScavenger(Module &M) : UnifiedTypeVars(1024) {
213+
typeModule(M);
214+
}
215+
216+
/// Get the type of the value, with pointer types replaced with
217+
/// TypedPointerType types instead.
218+
Type *getScavengedType(Value *V);
219+
220+
/// Get the deduced function type for a function, with pointer types replaced
221+
/// with TypedPointerTypes (maybe including type variables).
222+
FunctionType *getFunctionType(Function *F);
140223
};
141224

142225
#endif // SPIRVTYPESCAVENGER_H

lib/SPIRV/SPIRVUtil.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -818,11 +818,12 @@ bool getParameterTypes(Function *F, SmallVectorImpl<Type *> &ArgTys,
818818
assert(!HasSret && &Arg == F->getArg(0) &&
819819
"sret parameter should only appear on the first argument");
820820
HasSret = true;
821+
unsigned AS = Arg.getType()->getPointerAddressSpace();
821822
if (auto *STy = dyn_cast<StructType>(Ty))
822823
ArgTys.push_back(
823-
TypedPointerType::get(GetStructType(STy->getName()), 0));
824+
TypedPointerType::get(GetStructType(STy->getName()), AS));
824825
else
825-
ArgTys.push_back(TypedPointerType::get(Ty, 0));
826+
ArgTys.push_back(TypedPointerType::get(Ty, AS));
826827
} else {
827828
ArgTys.push_back(Arg.getType());
828829
}
@@ -893,6 +894,11 @@ bool getParameterTypes(Function *F, SmallVectorImpl<Type *> &ArgTys,
893894
DemangledSuccessfully = false;
894895
} else if (ArgTy->isTargetExtTy() || !DemangledTy)
895896
DemangledTy = ArgTy;
897+
if (auto *TPT = dyn_cast<TypedPointerType>(DemangledTy))
898+
if (ArgTy->isPointerTy() &&
899+
TPT->getAddressSpace() != ArgTy->getPointerAddressSpace())
900+
DemangledTy = TypedPointerType::get(TPT->getElementType(),
901+
ArgTy->getPointerAddressSpace());
896902
*ArgIter++ = DemangledTy;
897903
}
898904
return DemangledSuccessfully;

0 commit comments

Comments
 (0)