diff options
author | Vyacheslav Levytskyy <vyacheslav.levytskyy@intel.com> | 2024-07-11 07:16:29 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-11 07:16:29 +0200 |
commit | dbd00a5968d6c823d686714c91f2b4fcfd03797a (patch) | |
tree | 4580cc18a94806f0e6e32e5104029a09c8497548 | |
parent | 4710e0f498cb661ca17c99cb174616102fcad923 (diff) | |
download | llvm-dbd00a5968d6c823d686714c91f2b4fcfd03797a.zip llvm-dbd00a5968d6c823d686714c91f2b4fcfd03797a.tar.gz llvm-dbd00a5968d6c823d686714c91f2b4fcfd03797a.tar.bz2 |
[SPIRV] Improve type inference of operand presented by opaque pointers and aggregate types (#98035)
This PR improves type inference of operand presented by opaque pointers
and aggregate types:
* tries to restore original function return type for aggregate types so
that it's possible to deduce a correct type during emit-intrinsics step
(see llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll for the
reproducer of the previously existed issue when spirv-val found a
mismatch between object and ptr types in OpStore due to the incorrect
aggregate types tracing),
* explores untyped pointer operands of store to deduce correct pointee
types,
* creates an extension type to track pointee types from emit-intrinsics
step and further instead of direct and naive usage of TypePointerType
that led previously to crashes due to ban of creation of Value of
TypePointerType type,
* tracks instructions with uncomplete type information and tries to
improve their type info after pass calculated types for all machine
functions (it doesn't traverse a code but rather checks only those
instructions which were tracked as uncompleted),
* address more cases of removing unnecessary bitcasts (see, for example,
changes in test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll where
`CHECK-SPIRV-NEXT` in LIT checks show absence of unneeded bitcasts and
unmangled/mangled versions have proper typing now with equivalent type
info),
* address more cases of well known types or relations between types
within instructions (see, for example, atomic*.ll test cases and
Event-related test cases for improved SPIR-V code generated by the
Backend),
* fix the issue of removing unneeded ptrcast instructions in
pre-legalizer pass that led to creation of new assign-type instructions
with the same argument as source in ptrcast and caused errors in type
inference (the reproducer `complex.ll` test case is added to the PR).
20 files changed, 871 insertions, 256 deletions
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp index 286bdb9..1609576 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp @@ -169,21 +169,9 @@ using namespace InstructionSet; // TableGen records //===----------------------------------------------------------------------===// -/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using -/// the provided \p DemangledCall and specified \p Set. -/// -/// The lookup follows the following algorithm, returning the first successful -/// match: -/// 1. Search with the plain demangled name (expecting a 1:1 match). -/// 2. Search with the prefix before or suffix after the demangled name -/// signyfying the type of the first argument. -/// -/// \returns Wrapper around the demangled call and found builtin definition. -static std::unique_ptr<const SPIRV::IncomingCall> -lookupBuiltin(StringRef DemangledCall, - SPIRV::InstructionSet::InstructionSet Set, - Register ReturnRegister, const SPIRVType *ReturnType, - const SmallVectorImpl<Register> &Arguments) { +namespace SPIRV { +/// Parses the name part of the demangled builtin call. +std::string lookupBuiltinNameHelper(StringRef DemangledCall) { const static std::string PassPrefix = "(anonymous namespace)::"; std::string BuiltinName; // Itanium Demangler result may have "(anonymous namespace)::" prefix @@ -215,6 +203,27 @@ lookupBuiltin(StringRef DemangledCall, BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R")); } + return BuiltinName; +} +} // namespace SPIRV + +/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using +/// the provided \p DemangledCall and specified \p Set. +/// +/// The lookup follows the following algorithm, returning the first successful +/// match: +/// 1. Search with the plain demangled name (expecting a 1:1 match). +/// 2. Search with the prefix before or suffix after the demangled name +/// signyfying the type of the first argument. +/// +/// \returns Wrapper around the demangled call and found builtin definition. +static std::unique_ptr<const SPIRV::IncomingCall> +lookupBuiltin(StringRef DemangledCall, + SPIRV::InstructionSet::InstructionSet Set, + Register ReturnRegister, const SPIRVType *ReturnType, + const SmallVectorImpl<Register> &Arguments) { + std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall); + SmallVector<StringRef, 10> BuiltinArgumentTypes; StringRef BuiltinArgs = DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')')); @@ -2610,9 +2619,6 @@ Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall, // Unable to recognize SPIRV type name. return nullptr; - if (BaseType->isVoidTy()) - BaseType = Type::getInt8Ty(Ctx); - // Handle "typeN*" or "type vector[N]*". TypeStr.consume_back("*"); @@ -2621,7 +2627,8 @@ Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall, TypeStr.getAsInteger(10, VecElts); if (VecElts > 0) - BaseType = VectorType::get(BaseType, VecElts, false); + BaseType = VectorType::get( + BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false); return BaseType; } diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h index 68bff60..d07fc7c 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h @@ -19,6 +19,8 @@ namespace llvm { namespace SPIRV { +/// Parses the name part of the demangled builtin call. +std::string lookupBuiltinNameHelper(StringRef DemangledCall); /// Lowers a builtin function call using the provided \p DemangledCall skeleton /// and external instruction \p Set. /// diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index 566eafd..d9864ab 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -46,6 +46,10 @@ using namespace llvm; namespace llvm { +namespace SPIRV { +#define GET_BuiltinGroup_DECL +#include "SPIRVGenTables.inc" +} // namespace SPIRV void initializeSPIRVEmitIntrinsicsPass(PassRegistry &); } // namespace llvm @@ -69,22 +73,38 @@ class SPIRVEmitIntrinsics DenseSet<Instruction *> AggrStores; SPIRV::InstructionSet::InstructionSet InstrSet; + // a register of Instructions that don't have a complete type definition + SmallPtrSet<Value *, 8> UncompleteTypeInfo; + SmallVector<Instruction *> PostprocessWorklist; + + // well known result types of builtins + enum WellKnownTypes { Event }; + // deduce element type of untyped pointers Type *deduceElementType(Value *I, bool UnknownElemTypeI8); - Type *deduceElementTypeHelper(Value *I); - Type *deduceElementTypeHelper(Value *I, std::unordered_set<Value *> &Visited); + Type *deduceElementTypeHelper(Value *I, bool UnknownElemTypeI8); + Type *deduceElementTypeHelper(Value *I, std::unordered_set<Value *> &Visited, + bool UnknownElemTypeI8); Type *deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand, - std::unordered_set<Value *> &Visited); + bool UnknownElemTypeI8); + Type *deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand, + std::unordered_set<Value *> &Visited, + bool UnknownElemTypeI8); Type *deduceElementTypeByUsersDeep(Value *Op, - std::unordered_set<Value *> &Visited); + std::unordered_set<Value *> &Visited, + bool UnknownElemTypeI8); + void maybeAssignPtrType(Type *&Ty, Value *I, Type *RefTy, + bool UnknownElemTypeI8); // deduce nested types of composites - Type *deduceNestedTypeHelper(User *U); + Type *deduceNestedTypeHelper(User *U, bool UnknownElemTypeI8); Type *deduceNestedTypeHelper(User *U, Type *Ty, - std::unordered_set<Value *> &Visited); + std::unordered_set<Value *> &Visited, + bool UnknownElemTypeI8); // deduce Types of operands of the Instruction if possible - void deduceOperandElementType(Instruction *I); + void deduceOperandElementType(Instruction *I, Instruction *AskOp = 0, + Type *AskTy = 0, CallInst *AssignCI = 0); void preprocessCompositeConstants(IRBuilder<> &B); void preprocessUndefs(IRBuilder<> &B); @@ -151,6 +171,7 @@ public: bool runOnModule(Module &M) override; bool runOnFunction(Function &F); + bool postprocessTypes(); void getAnalysisUsage(AnalysisUsage &AU) const override { ModulePass::getAnalysisUsage(AU); @@ -223,6 +244,41 @@ static inline void reportFatalOnTokenType(const Instruction *I) { false); } +static bool IsKernelArgInt8(Function *F, StoreInst *SI) { + return SI && F->getCallingConv() == CallingConv::SPIR_KERNEL && + isPointerTy(SI->getValueOperand()->getType()) && + isa<Argument>(SI->getValueOperand()); +} + +// Maybe restore original function return type. +static inline Type *restoreMutatedType(SPIRVGlobalRegistry *GR, Instruction *I, + Type *Ty) { + CallInst *CI = dyn_cast<CallInst>(I); + if (!CI || CI->isIndirectCall() || CI->isInlineAsm() || + !CI->getCalledFunction() || CI->getCalledFunction()->isIntrinsic()) + return Ty; + if (Type *OriginalTy = GR->findMutated(CI->getCalledFunction())) + return OriginalTy; + return Ty; +} + +// Reconstruct type with nested element types according to deduced type info. +// Return nullptr if no detailed type info is available. +static inline Type *reconstructType(SPIRVGlobalRegistry *GR, Value *Op) { + Type *Ty = Op->getType(); + if (!isUntypedPointerTy(Ty)) + return Ty; + // try to find the pointee type + if (Type *NestedTy = GR->findDeducedElementType(Op)) + return getTypedPointerWrapper(NestedTy, getPointerAddressSpace(Ty)); + // not a pointer according to the type info (e.g., Event object) + CallInst *CI = GR->findAssignPtrTypeInstr(Op); + if (!CI) + return nullptr; + MetadataAsValue *MD = cast<MetadataAsValue>(CI->getArgOperand(1)); + return cast<ConstantAsMetadata>(MD->getMetadata())->getType(); +} + void SPIRVEmitIntrinsics::buildAssignType(IRBuilder<> &B, Type *Ty, Value *Arg) { Value *OfType = PoisonValue::get(Ty); @@ -263,15 +319,26 @@ void SPIRVEmitIntrinsics::updateAssignType(CallInst *AssignCI, Value *Arg, // Set element pointer type to the given value of ValueTy and tries to // specify this type further (recursively) by Operand value, if needed. +Type * +SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand, + bool UnknownElemTypeI8) { + std::unordered_set<Value *> Visited; + return deduceElementTypeByValueDeep(ValueTy, Operand, Visited, + UnknownElemTypeI8); +} + Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep( - Type *ValueTy, Value *Operand, std::unordered_set<Value *> &Visited) { + Type *ValueTy, Value *Operand, std::unordered_set<Value *> &Visited, + bool UnknownElemTypeI8) { Type *Ty = ValueTy; if (Operand) { if (auto *PtrTy = dyn_cast<PointerType>(Ty)) { - if (Type *NestedTy = deduceElementTypeHelper(Operand, Visited)) - Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); + if (Type *NestedTy = + deduceElementTypeHelper(Operand, Visited, UnknownElemTypeI8)) + Ty = getTypedPointerWrapper(NestedTy, PtrTy->getAddressSpace()); } else { - Ty = deduceNestedTypeHelper(dyn_cast<User>(Operand), Ty, Visited); + Ty = deduceNestedTypeHelper(dyn_cast<User>(Operand), Ty, Visited, + UnknownElemTypeI8); } } return Ty; @@ -279,12 +346,12 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep( // Traverse User instructions to deduce an element pointer type of the operand. Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep( - Value *Op, std::unordered_set<Value *> &Visited) { + Value *Op, std::unordered_set<Value *> &Visited, bool UnknownElemTypeI8) { if (!Op || !isPointerTy(Op->getType())) return nullptr; - if (auto PType = dyn_cast<TypedPointerType>(Op->getType())) - return PType->getElementType(); + if (auto ElemTy = getPointeeType(Op->getType())) + return ElemTy; // maybe we already know operand's element type if (Type *KnownTy = GR->findDeducedElementType(Op)) @@ -292,7 +359,7 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep( for (User *OpU : Op->users()) { if (Instruction *Inst = dyn_cast<Instruction>(OpU)) { - if (Type *Ty = deduceElementTypeHelper(Inst, Visited)) + if (Type *Ty = deduceElementTypeHelper(Inst, Visited, UnknownElemTypeI8)) return Ty; } } @@ -314,13 +381,27 @@ static Type *getPointeeTypeByCallInst(StringRef DemangledName, // Deduce and return a successfully deduced Type of the Instruction, // or nullptr otherwise. -Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(Value *I) { +Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(Value *I, + bool UnknownElemTypeI8) { std::unordered_set<Value *> Visited; - return deduceElementTypeHelper(I, Visited); + return deduceElementTypeHelper(I, Visited, UnknownElemTypeI8); +} + +void SPIRVEmitIntrinsics::maybeAssignPtrType(Type *&Ty, Value *Op, Type *RefTy, + bool UnknownElemTypeI8) { + if (isUntypedPointerTy(RefTy)) { + if (!UnknownElemTypeI8) + return; + if (auto *I = dyn_cast<Instruction>(Op)) { + UncompleteTypeInfo.insert(I); + PostprocessWorklist.push_back(I); + } + } + Ty = RefTy; } Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( - Value *I, std::unordered_set<Value *> &Visited) { + Value *I, std::unordered_set<Value *> &Visited, bool UnknownElemTypeI8) { // allow to pass nullptr as an argument if (!I) return nullptr; @@ -338,34 +419,41 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( Type *Ty = nullptr; // look for known basic patterns of type inference if (auto *Ref = dyn_cast<AllocaInst>(I)) { - Ty = Ref->getAllocatedType(); + maybeAssignPtrType(Ty, I, Ref->getAllocatedType(), UnknownElemTypeI8); } else if (auto *Ref = dyn_cast<GetElementPtrInst>(I)) { Ty = Ref->getResultElementType(); } else if (auto *Ref = dyn_cast<GlobalValue>(I)) { Ty = deduceElementTypeByValueDeep( Ref->getValueType(), - Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr, Visited); + Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr, Visited, + UnknownElemTypeI8); } else if (auto *Ref = dyn_cast<AddrSpaceCastInst>(I)) { - Ty = deduceElementTypeHelper(Ref->getPointerOperand(), Visited); + Type *RefTy = deduceElementTypeHelper(Ref->getPointerOperand(), Visited, + UnknownElemTypeI8); + maybeAssignPtrType(Ty, I, RefTy, UnknownElemTypeI8); } else if (auto *Ref = dyn_cast<BitCastInst>(I)) { if (Type *Src = Ref->getSrcTy(), *Dest = Ref->getDestTy(); isPointerTy(Src) && isPointerTy(Dest)) - Ty = deduceElementTypeHelper(Ref->getOperand(0), Visited); + Ty = deduceElementTypeHelper(Ref->getOperand(0), Visited, + UnknownElemTypeI8); } else if (auto *Ref = dyn_cast<AtomicCmpXchgInst>(I)) { Value *Op = Ref->getNewValOperand(); - Ty = deduceElementTypeByValueDeep(Op->getType(), Op, Visited); + if (isPointerTy(Op->getType())) + Ty = deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8); } else if (auto *Ref = dyn_cast<AtomicRMWInst>(I)) { Value *Op = Ref->getValOperand(); - Ty = deduceElementTypeByValueDeep(Op->getType(), Op, Visited); + if (isPointerTy(Op->getType())) + Ty = deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8); } else if (auto *Ref = dyn_cast<PHINode>(I)) { for (unsigned i = 0; i < Ref->getNumIncomingValues(); i++) { - Ty = deduceElementTypeByUsersDeep(Ref->getIncomingValue(i), Visited); + Ty = deduceElementTypeByUsersDeep(Ref->getIncomingValue(i), Visited, + UnknownElemTypeI8); if (Ty) break; } } else if (auto *Ref = dyn_cast<SelectInst>(I)) { for (Value *Op : {Ref->getTrueValue(), Ref->getFalseValue()}) { - Ty = deduceElementTypeByUsersDeep(Op, Visited); + Ty = deduceElementTypeByUsersDeep(Op, Visited, UnknownElemTypeI8); if (Ty) break; } @@ -384,10 +472,12 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( if (Function *CalledF = CI->getCalledFunction()) { std::string DemangledName = getOclOrSpirvBuiltinDemangledName(CalledF->getName()); + if (DemangledName.length() > 0) + DemangledName = SPIRV::lookupBuiltinNameHelper(DemangledName); auto AsArgIt = ResTypeByArg.find(DemangledName); if (AsArgIt != ResTypeByArg.end()) { Ty = deduceElementTypeHelper(CI->getArgOperand(AsArgIt->second), - Visited); + Visited, UnknownElemTypeI8); } } } @@ -404,13 +494,15 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( // Re-create a type of the value if it has untyped pointer fields, also nested. // Return the original value type if no corrections of untyped pointer // information is found or needed. -Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(User *U) { +Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(User *U, + bool UnknownElemTypeI8) { std::unordered_set<Value *> Visited; - return deduceNestedTypeHelper(U, U->getType(), Visited); + return deduceNestedTypeHelper(U, U->getType(), Visited, UnknownElemTypeI8); } Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper( - User *U, Type *OrigTy, std::unordered_set<Value *> &Visited) { + User *U, Type *OrigTy, std::unordered_set<Value *> &Visited, + bool UnknownElemTypeI8) { if (!U) return OrigTy; @@ -432,10 +524,12 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper( Type *Ty = OpTy; if (Op) { if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) { - if (Type *NestedTy = deduceElementTypeHelper(Op, Visited)) + if (Type *NestedTy = + deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8)) Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); } else { - Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited); + Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited, + UnknownElemTypeI8); } } Tys.push_back(Ty); @@ -451,10 +545,12 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper( Type *OpTy = ArrTy->getElementType(); Type *Ty = OpTy; if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) { - if (Type *NestedTy = deduceElementTypeHelper(Op, Visited)) + if (Type *NestedTy = + deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8)) Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); } else { - Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited); + Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited, + UnknownElemTypeI8); } if (Ty != OpTy) { Type *NewTy = ArrayType::get(Ty, ArrTy->getNumElements()); @@ -467,10 +563,12 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper( Type *OpTy = VecTy->getElementType(); Type *Ty = OpTy; if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) { - if (Type *NestedTy = deduceElementTypeHelper(Op, Visited)) - Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); + if (Type *NestedTy = + deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8)) + Ty = getTypedPointerWrapper(NestedTy, PtrTy->getAddressSpace()); } else { - Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited); + Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited, + UnknownElemTypeI8); } if (Ty != OpTy) { Type *NewTy = VectorType::get(Ty, VecTy->getElementCount()); @@ -484,16 +582,38 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper( } Type *SPIRVEmitIntrinsics::deduceElementType(Value *I, bool UnknownElemTypeI8) { - if (Type *Ty = deduceElementTypeHelper(I)) + if (Type *Ty = deduceElementTypeHelper(I, UnknownElemTypeI8)) return Ty; - return UnknownElemTypeI8 ? IntegerType::getInt8Ty(I->getContext()) : nullptr; + if (!UnknownElemTypeI8) + return nullptr; + if (auto *Instr = dyn_cast<Instruction>(I)) { + UncompleteTypeInfo.insert(Instr); + PostprocessWorklist.push_back(Instr); + } + return IntegerType::getInt8Ty(I->getContext()); +} + +static inline Type *getAtomicElemTy(SPIRVGlobalRegistry *GR, Instruction *I, + Value *PointerOperand) { + Type *PointeeTy = GR->findDeducedElementType(PointerOperand); + if (PointeeTy && !isUntypedPointerTy(PointeeTy)) + return nullptr; + auto *PtrTy = dyn_cast<PointerType>(I->getType()); + if (!PtrTy) + return I->getType(); + if (Type *NestedTy = GR->findDeducedElementType(I)) + return getTypedPointerWrapper(NestedTy, PtrTy->getAddressSpace()); + return nullptr; } // If the Instruction has Pointer operands with unresolved types, this function // tries to deduce them. If the Instruction has Pointer operands with known // types which differ from expected, this function tries to insert a bitcast to // resolve the issue. -void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) { +void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I, + Instruction *AskOp, + Type *AskTy, + CallInst *AskCI) { SmallVector<std::pair<Value *, unsigned>> Ops; Type *KnownElemTy = nullptr; // look for known basic patterns of type inference @@ -506,6 +626,51 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) { if (isPointerTy(Op->getType())) Ops.push_back(std::make_pair(Op, i)); } + } else if (auto *Ref = dyn_cast<AddrSpaceCastInst>(I)) { + KnownElemTy = GR->findDeducedElementType(I); + if (!KnownElemTy) + return; + Ops.push_back(std::make_pair(Ref->getPointerOperand(), 0)); + } else if (auto *Ref = dyn_cast<GetElementPtrInst>(I)) { + KnownElemTy = Ref->getSourceElementType(); + if (isUntypedPointerTy(KnownElemTy)) + return; + Type *PointeeTy = GR->findDeducedElementType(Ref->getPointerOperand()); + if (PointeeTy && !isUntypedPointerTy(PointeeTy)) + return; + Ops.push_back(std::make_pair(Ref->getPointerOperand(), + GetElementPtrInst::getPointerOperandIndex())); + } else if (auto *Ref = dyn_cast<LoadInst>(I)) { + KnownElemTy = I->getType(); + if (isUntypedPointerTy(KnownElemTy)) + return; + Type *PointeeTy = GR->findDeducedElementType(Ref->getPointerOperand()); + if (PointeeTy && !isUntypedPointerTy(PointeeTy)) + return; + Ops.push_back(std::make_pair(Ref->getPointerOperand(), + LoadInst::getPointerOperandIndex())); + } else if (auto *Ref = dyn_cast<StoreInst>(I)) { + if (IsKernelArgInt8(Ref->getParent()->getParent(), Ref)) + return; + if (!(KnownElemTy = reconstructType(GR, Ref->getValueOperand()))) + return; + Type *PointeeTy = GR->findDeducedElementType(Ref->getPointerOperand()); + if (PointeeTy && !isUntypedPointerTy(PointeeTy)) + return; + Ops.push_back(std::make_pair(Ref->getPointerOperand(), + StoreInst::getPointerOperandIndex())); + } else if (auto *Ref = dyn_cast<AtomicCmpXchgInst>(I)) { + KnownElemTy = getAtomicElemTy(GR, I, Ref->getPointerOperand()); + if (!KnownElemTy) + return; + Ops.push_back(std::make_pair(Ref->getPointerOperand(), + AtomicCmpXchgInst::getPointerOperandIndex())); + } else if (auto *Ref = dyn_cast<AtomicRMWInst>(I)) { + KnownElemTy = getAtomicElemTy(GR, I, Ref->getPointerOperand()); + if (!KnownElemTy) + return; + Ops.push_back(std::make_pair(Ref->getPointerOperand(), + AtomicRMWInst::getPointerOperandIndex())); } else if (auto *Ref = dyn_cast<SelectInst>(I)) { if (!isPointerTy(I->getType()) || !(KnownElemTy = GR->findDeducedElementType(I))) @@ -565,6 +730,32 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) { KnownElemTy = ElemTy; // src will rewrite dest if both are defined Ops.push_back(std::make_pair(Op, i)); } + } else if (Grp == SPIRV::Atomic || Grp == SPIRV::AtomicFloating) { + if (CI->arg_size() < 2) + return; + Value *Op = CI->getArgOperand(0); + if (!isPointerTy(Op->getType())) + return; + switch (Opcode) { + case SPIRV::OpAtomicLoad: + case SPIRV::OpAtomicCompareExchangeWeak: + case SPIRV::OpAtomicCompareExchange: + case SPIRV::OpAtomicExchange: + case SPIRV::OpAtomicIAdd: + case SPIRV::OpAtomicISub: + case SPIRV::OpAtomicOr: + case SPIRV::OpAtomicXor: + case SPIRV::OpAtomicAnd: + case SPIRV::OpAtomicUMin: + case SPIRV::OpAtomicUMax: + case SPIRV::OpAtomicSMin: + case SPIRV::OpAtomicSMax: { + KnownElemTy = getAtomicElemTy(GR, I, Op); + if (!KnownElemTy) + return; + Ops.push_back(std::make_pair(Op, 0)); + } break; + } } } } @@ -578,17 +769,18 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) { IRBuilder<> B(Ctx); for (auto &OpIt : Ops) { Value *Op = OpIt.first; - if (Op->use_empty()) + if (Op->use_empty() || (AskOp && Op != AskOp)) continue; - Type *Ty = GR->findDeducedElementType(Op); + Type *Ty = AskOp ? AskTy : GR->findDeducedElementType(Op); if (Ty == KnownElemTy) continue; - Value *OpTyVal = Constant::getNullValue(KnownElemTy); + Value *OpTyVal = PoisonValue::get(KnownElemTy); Type *OpTy = Op->getType(); - if (!Ty) { + if (!Ty || AskTy || isUntypedPointerTy(Ty) || + UncompleteTypeInfo.contains(Op)) { GR->addDeducedElementType(Op, KnownElemTy); // check if there is existing Intrinsic::spv_assign_ptr_type instruction - CallInst *AssignCI = GR->findAssignPtrTypeInstr(Op); + CallInst *AssignCI = AskCI ? AskCI : GR->findAssignPtrTypeInstr(Op); if (AssignCI == nullptr) { Instruction *User = dyn_cast<Instruction>(Op->use_begin()->get()); setInsertPointSkippingPhis(B, User ? User->getNextNode() : I); @@ -719,7 +911,7 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) { I->replaceUsesOfWith(Op, CI); KeepInst = true; AggrConsts[CI] = AggrConst; - AggrConstTypes[CI] = deduceNestedTypeHelper(AggrConst); + AggrConstTypes[CI] = deduceNestedTypeHelper(AggrConst, false); } } if (!KeepInst) @@ -864,8 +1056,9 @@ void SPIRVEmitIntrinsics::replacePointerOperandWithPtrCast( Pointer = BC->getOperand(0); // Do not emit spv_ptrcast if Pointer's element type is ExpectedElementType - Type *PointerElemTy = deduceElementTypeHelper(Pointer); - if (PointerElemTy == ExpectedElementType) + Type *PointerElemTy = deduceElementTypeHelper(Pointer, false); + if (PointerElemTy == ExpectedElementType || + isEquivalentTypes(PointerElemTy, ExpectedElementType)) return; setInsertPointSkippingPhis(B, I); @@ -930,15 +1123,19 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I, IRBuilder<> &B) { // Handle basic instructions: StoreInst *SI = dyn_cast<StoreInst>(I); - if (SI && F->getCallingConv() == CallingConv::SPIR_KERNEL && - isPointerTy(SI->getValueOperand()->getType()) && - isa<Argument>(SI->getValueOperand())) { + if (IsKernelArgInt8(F, SI)) { return replacePointerOperandWithPtrCast( I, SI->getValueOperand(), IntegerType::getInt8Ty(F->getContext()), 0, B); } else if (SI) { - return replacePointerOperandWithPtrCast( - I, SI->getPointerOperand(), SI->getValueOperand()->getType(), 1, B); + Value *Op = SI->getValueOperand(); + Type *OpTy = Op->getType(); + if (auto *OpI = dyn_cast<Instruction>(Op)) + OpTy = restoreMutatedType(GR, OpI, OpTy); + if (OpTy == Op->getType()) + OpTy = deduceElementTypeByValueDeep(OpTy, Op, false); + return replacePointerOperandWithPtrCast(I, SI->getPointerOperand(), OpTy, 1, + B); } else if (LoadInst *LI = dyn_cast<LoadInst>(I)) { return replacePointerOperandWithPtrCast(I, LI->getPointerOperand(), LI->getType(), 0, B); @@ -978,7 +1175,7 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I, } else { for (User *U : CalledArg->users()) { if (Instruction *Inst = dyn_cast<Instruction>(U)) { - if ((ElemTy = deduceElementTypeHelper(Inst)) != nullptr) + if ((ElemTy = deduceElementTypeHelper(Inst, false)) != nullptr) break; } } @@ -1012,7 +1209,7 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I, if (!ExpectedType && !DemangledName.empty()) ExpectedType = SPIRV::parseBuiltinCallArgumentBaseType( DemangledName, OpIdx, I->getContext()); - if (!ExpectedType) + if (!ExpectedType || ExpectedType->isVoidTy()) continue; if (ExpectedType->isTargetExtTy()) @@ -1182,7 +1379,7 @@ void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV, // Deduce element type and store results in Global Registry. // Result is ignored, because TypedPointerType is not supported // by llvm IR general logic. - deduceElementTypeHelper(&GV); + deduceElementTypeHelper(&GV, false); Constant *Init = GV.getInitializer(); Type *Ty = isAggrConstForceInt32(Init) ? B.getInt32Ty() : Init->getType(); Constant *Const = isAggrConstForceInt32(Init) ? B.getInt32(1) : Init; @@ -1216,9 +1413,39 @@ bool SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(Instruction *I, void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I, IRBuilder<> &B) { + // TODO: extend the list of functions with known result types + static StringMap<unsigned> ResTypeWellKnown = { + {"async_work_group_copy", WellKnownTypes::Event}, + {"async_work_group_strided_copy", WellKnownTypes::Event}, + {"__spirv_GroupAsyncCopy", WellKnownTypes::Event}}; + reportFatalOnTokenType(I); + + bool IsKnown = false; + if (auto *CI = dyn_cast<CallInst>(I)) { + if (!CI->isIndirectCall() && !CI->isInlineAsm() && + CI->getCalledFunction() && !CI->getCalledFunction()->isIntrinsic()) { + Function *CalledF = CI->getCalledFunction(); + std::string DemangledName = + getOclOrSpirvBuiltinDemangledName(CalledF->getName()); + if (DemangledName.length() > 0) + DemangledName = SPIRV::lookupBuiltinNameHelper(DemangledName); + auto ResIt = ResTypeWellKnown.find(DemangledName); + if (ResIt != ResTypeWellKnown.end()) { + IsKnown = true; + setInsertPointAfterDef(B, I); + switch (ResIt->second) { + case WellKnownTypes::Event: + buildAssignType(B, TargetExtType::get(I->getContext(), "spirv.Event"), + I); + break; + } + } + } + } + Type *Ty = I->getType(); - if (!Ty->isVoidTy() && !isPointerTy(Ty) && requireAssignType(I)) { + if (!IsKnown && !Ty->isVoidTy() && !isPointerTy(Ty) && requireAssignType(I)) { setInsertPointAfterDef(B, I); Type *TypeToAssign = Ty; if (auto *II = dyn_cast<IntrinsicInst>(I)) { @@ -1230,6 +1457,7 @@ void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I, TypeToAssign = It->second; } } + TypeToAssign = restoreMutatedType(GR, I, TypeToAssign); buildAssignType(B, TypeToAssign, I); } for (const auto &Op : I->operands()) { @@ -1343,7 +1571,7 @@ Type *SPIRVEmitIntrinsics::deduceFunParamElementType( return KnownTy; // try to deduce from the operand itself Visited.clear(); - if (Type *Ty = deduceElementTypeHelper(OpArg, Visited)) + if (Type *Ty = deduceElementTypeHelper(OpArg, Visited, false)) return Ty; // search in actual parameter's users for (User *OpU : OpArg->users()) { @@ -1351,7 +1579,7 @@ Type *SPIRVEmitIntrinsics::deduceFunParamElementType( if (!Inst || Inst == CI) continue; Visited.clear(); - if (Type *Ty = deduceElementTypeHelper(Inst, Visited)) + if (Type *Ty = deduceElementTypeHelper(Inst, Visited, false)) return Ty; } // check if it's a formal parameter of the outer function @@ -1480,12 +1708,39 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) { return true; } +// Try to deduce a better type for pointers to untyped ptr. +bool SPIRVEmitIntrinsics::postprocessTypes() { + bool Changed = false; + if (!GR) + return Changed; + for (auto IB = PostprocessWorklist.rbegin(), IE = PostprocessWorklist.rend(); + IB != IE; ++IB) { + CallInst *AssignCI = GR->findAssignPtrTypeInstr(*IB); + Type *KnownTy = GR->findDeducedElementType(*IB); + if (!KnownTy || !AssignCI || !isa<Instruction>(AssignCI->getArgOperand(0))) + continue; + Instruction *I = cast<Instruction>(AssignCI->getArgOperand(0)); + for (User *U : I->users()) { + Instruction *Inst = dyn_cast<Instruction>(U); + if (!Inst || isa<IntrinsicInst>(Inst)) + continue; + deduceOperandElementType(Inst, I, KnownTy, AssignCI); + if (KnownTy != GR->findDeducedElementType(I)) { + Changed = true; + break; + } + } + } + return Changed; +} + bool SPIRVEmitIntrinsics::runOnModule(Module &M) { bool Changed = false; - for (auto &F : M) { + UncompleteTypeInfo.clear(); + PostprocessWorklist.clear(); + for (auto &F : M) Changed |= runOnFunction(F); - } for (auto &F : M) { // check if function parameter types are set @@ -1497,6 +1752,8 @@ bool SPIRVEmitIntrinsics::runOnModule(Module &M) { } } + Changed |= postprocessTypes(); + return Changed; } diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h index a45e1cc..0e26b38 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h @@ -51,6 +51,10 @@ class SPIRVGlobalRegistry { // Maps Functions to their calls (in a form of the machine instruction, // OpFunctionCall) that happened before the definition is available DenseMap<const Function *, SmallPtrSet<MachineInstr *, 8>> ForwardCalls; + // map a Function to its original return type before the clone function was + // created during substitution of aggregate arguments + // (see `SPIRVPrepareFunctions::removeAggregateTypesFromSignature()`) + DenseMap<Value *, Type *> MutatedAggRet; // Look for an equivalent of the newType in the map. Return the equivalent // if it's found, otherwise insert newType to the map and return the type. @@ -163,6 +167,16 @@ public: return It == AssignPtrTypeInstr.end() ? nullptr : It->second; } + // A registry of mutated values + // (see `SPIRVPrepareFunctions::removeAggregateTypesFromSignature()`): + // - Add a record. + void addMutated(Value *Val, Type *Ty) { MutatedAggRet[Val] = Ty; } + // - Find a record. + Type *findMutated(const Value *Val) { + auto It = MutatedAggRet.find(Val); + return It == MutatedAggRet.end() ? nullptr : It->second; + } + // Deduced element types of untyped pointers and composites: // - Add a record to the map of deduced element types. void addDeducedElementType(Value *Val, Type *Ty) { DeducedElTys[Val] = Ty; } diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 9be736c..04def5e 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -2190,7 +2190,7 @@ bool SPIRVInstructionSelector::selectGlobalValue( // FIXME: don't use MachineIRBuilder here, replace it with BuildMI. MachineIRBuilder MIRBuilder(I); const GlobalValue *GV = I.getOperand(1).getGlobal(); - Type *GVType = GR.getDeducedGlobalValueType(GV); + Type *GVType = toTypedPointer(GR.getDeducedGlobalValueType(GV)); SPIRVType *PointerBaseType; if (GVType->isArrayTy()) { SPIRVType *ArrayElementType = diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp index 6c7c3af..e775f8c 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -138,7 +138,8 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { s16, s32, s64, v2s16, v2s32, v2s64, v3s16, v3s32, v3s64, v4s16, v4s32, v4s64, v8s16, v8s32, v8s64, v16s16, v16s32, v16s64}; - auto allFloatAndIntScalars = allIntScalars; + auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1, + p2, p3, p4, p5, p6}; auto allPtrs = {p0, p1, p2, p3, p4, p5, p6}; auto allWritablePtrs = {p0, p1, p3, p4, p5, p6}; @@ -238,7 +239,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { .legalForCartesianProduct(allFloatScalars, allWritablePtrs); getActionDefinitionsBuilder(G_ATOMICRMW_XCHG) - .legalForCartesianProduct(allFloatAndIntScalars, allWritablePtrs); + .legalForCartesianProduct(allFloatAndIntScalarsAndPtrs, allWritablePtrs); getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG_WITH_SUCCESS).lower(); // TODO: add proper legalization rules. diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp index 0ea2f17..099557a 100644 --- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp @@ -151,6 +151,20 @@ foldConstantsIntoIntrinsics(MachineFunction &MF, MI->eraseFromParent(); } +static MachineInstr *findAssignTypeInstr(Register Reg, + MachineRegisterInfo *MRI) { + for (MachineRegisterInfo::use_instr_iterator I = MRI->use_instr_begin(Reg), + IE = MRI->use_instr_end(); + I != IE; ++I) { + MachineInstr *UseMI = &*I; + if ((isSpvIntrinsic(*UseMI, Intrinsic::spv_assign_ptr_type) || + isSpvIntrinsic(*UseMI, Intrinsic::spv_assign_type)) && + UseMI->getOperand(1).getReg() == Reg) + return UseMI; + } + return nullptr; +} + static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR, MachineIRBuilder MIB) { // Get access to information about available extensions @@ -177,9 +191,12 @@ static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR, BaseTy, MI, *MF.getSubtarget<SPIRVSubtarget>().getInstrInfo(), addressSpaceToStorageClass(MI.getOperand(4).getImm(), *ST)); - // If the bitcast would be redundant, replace all uses with the source + // If the ptrcast would be redundant, replace all uses with the source // register. if (GR->getSPIRVTypeForVReg(Source) == AssignedPtrType) { + // Erase Def's assign type instruction if we are going to replace Def. + if (MachineInstr *AssignMI = findAssignTypeInstr(Def, MIB.getMRI())) + ToErase.push_back(AssignMI); MIB.getMRI()->replaceRegWith(Def, Source); } else { GR->assignSPIRVTypeToVReg(AssignedPtrType, Def, MF); @@ -224,8 +241,8 @@ static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR, case TargetOpcode::G_GLOBAL_VALUE: { MIB.setInsertPt(*MI->getParent(), MI); const GlobalValue *Global = MI->getOperand(1).getGlobal(); - Type *ElementTy = GR->getDeducedGlobalValueType(Global); - auto *Ty = TypedPointerType::get(toTypedPointer(ElementTy), + Type *ElementTy = toTypedPointer(GR->getDeducedGlobalValueType(Global)); + auto *Ty = TypedPointerType::get(ElementTy, Global->getType()->getAddressSpace()); SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB); break; diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp index 7bee87d..29b8f8f 100644 --- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp @@ -536,6 +536,11 @@ SPIRVPrepareFunctions::removeAggregateTypesFromSignature(Function *F) { CI->mutateFunctionType(NewF->getFunctionType()); U->replaceUsesOfWith(F, NewF); } + + // register the mutation + if (RetType != F->getReturnType()) + TM.getSubtarget<SPIRVSubtarget>(*F).getSPIRVGlobalRegistry()->addMutated( + NewF, F->getReturnType()); return NewF; } diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h index 12725d6..c757af6 100644 --- a/llvm/lib/Target/SPIRV/SPIRVUtils.h +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h @@ -108,7 +108,7 @@ Type *parseBasicTypeName(StringRef &TypeName, LLVMContext &Ctx); // True if this is an instance of TypedPointerType. inline bool isTypedPointerTy(const Type *T) { - return T->getTypeID() == Type::TypedPointerTyID; + return T && T->getTypeID() == Type::TypedPointerTyID; } // True if this is an instance of PointerType. @@ -153,7 +153,61 @@ inline Type *reconstructFunctionType(Function *F) { return FunctionType::get(F->getReturnType(), ArgTys, F->isVarArg()); } +#define TYPED_PTR_TARGET_EXT_NAME "spirv.$TypedPointerType" +inline Type *getTypedPointerWrapper(Type *ElemTy, unsigned AS) { + return TargetExtType::get(ElemTy->getContext(), TYPED_PTR_TARGET_EXT_NAME, + {ElemTy}, {AS}); +} + +inline bool isTypedPointerWrapper(TargetExtType *ExtTy) { + return ExtTy->getName() == TYPED_PTR_TARGET_EXT_NAME && + ExtTy->getNumIntParameters() == 1 && + ExtTy->getNumTypeParameters() == 1; +} + +inline Type *applyWrappers(Type *Ty) { + if (auto *ExtTy = dyn_cast<TargetExtType>(Ty)) { + if (isTypedPointerWrapper(ExtTy)) + return TypedPointerType::get(applyWrappers(ExtTy->getTypeParameter(0)), + ExtTy->getIntParameter(0)); + } else if (auto *VecTy = dyn_cast<VectorType>(Ty)) { + Type *ElemTy = VecTy->getElementType(); + Type *NewElemTy = ElemTy->isTargetExtTy() ? applyWrappers(ElemTy) : ElemTy; + if (NewElemTy != ElemTy) + return VectorType::get(NewElemTy, VecTy->getElementCount()); + } + return Ty; +} + +inline Type *getPointeeType(Type *Ty) { + if (auto PType = dyn_cast<TypedPointerType>(Ty)) + return PType->getElementType(); + else if (auto *ExtTy = dyn_cast<TargetExtType>(Ty)) + if (isTypedPointerWrapper(ExtTy)) + return applyWrappers(ExtTy->getTypeParameter(0)); + return nullptr; +} + +inline bool isUntypedEquivalentToTyExt(Type *Ty1, Type *Ty2) { + if (!isUntypedPointerTy(Ty1) || !Ty2) + return false; + if (auto *ExtTy = dyn_cast<TargetExtType>(Ty2)) + if (isTypedPointerWrapper(ExtTy) && + ExtTy->getTypeParameter(0) == + IntegerType::getInt8Ty(Ty1->getContext()) && + ExtTy->getIntParameter(0) == cast<PointerType>(Ty1)->getAddressSpace()) + return true; + return false; +} + +inline bool isEquivalentTypes(Type *Ty1, Type *Ty2) { + return isUntypedEquivalentToTyExt(Ty1, Ty2) || + isUntypedEquivalentToTyExt(Ty2, Ty1); +} + inline Type *toTypedPointer(Type *Ty) { + if (Type *NewTy = applyWrappers(Ty); NewTy != Ty) + return NewTy; return isUntypedPointerTy(Ty) ? TypedPointerType::get(IntegerType::getInt8Ty(Ty->getContext()), getPointerAddressSpace(Ty)) diff --git a/llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll b/llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll new file mode 100644 index 0000000..9e91854 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll @@ -0,0 +1,46 @@ +; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK-DAG: %[[#FloatTy:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#StructTy:]] = OpTypeStruct %[[#FloatTy]] +; CHECK-DAG: %[[#ArrayTy:]] = OpTypeArray %[[#StructTy]] %[[#]] +; CHECK-DAG: %[[#Struct7Ty:]] = OpTypeStruct %[[#ArrayTy]] +; CHECK-DAG: %[[#Void:]] = OpTypeVoid +; CHECK-DAG: %[[#PtrStructTy:]] = OpTypePointer Generic %[[#StructTy]] +; CHECK-DAG: %[[#PtrStruct7Ty:]] = OpTypePointer Generic %[[#Struct7Ty]] +; CHECK-DAG: %[[#FunTy:]] = OpTypeFunction %[[#Void]] %[[#PtrStructTy]] %[[#PtrStruct7Ty]] +; CHECK-DAG: %[[#Const1:]] = OpConstant %[[#FloatTy]] 1 +; CHECK-DAG: %[[#FPtrStructTy:]] = OpTypePointer Function %[[#StructTy]] +; CHECK-DAG: %[[#Spec1:]] = OpSpecConstantComposite %[[#StructTy]] %[[#Const1]] +; CHECK-DAG: %[[#Spec2:]] = OpSpecConstantComposite %[[#ArrayTy]] %[[#Spec1]] %[[#Spec1]] +; CHECK-DAG: %[[#Spec3:]] = OpSpecConstantComposite %[[#Struct7Ty]] %[[#Spec2]] +; CHECK: %[[#FunDef:]] = OpFunction %[[#Void]] None %[[#FunTy]] +; CHECK: %[[#Arg1:]] = OpFunctionParameter %[[#PtrStructTy]] +; CHECK: %[[#Arg2:]] = OpFunctionParameter %[[#PtrStruct7Ty]] +; CHECK: %[[#]] = OpVariable %[[#FPtrStructTy]] Function +; CHECK: OpStore %[[#Arg1]] %[[#Spec1]] +; CHECK: OpStore %[[#Arg2]] %[[#Spec3]] +; CHECK: OpFunctionEnd + +%Struct = type <{ float }> +%Struct7 = type [2 x %Struct] +%Nested = type { %Struct7 } + +define spir_kernel void @foo(ptr addrspace(4) %arg1, ptr addrspace(4) %arg2) { +entry: + %var = alloca %Struct + %r1 = call %Struct @_Z29__spirv_SpecConstantComposite_1(float 1.0) + store %Struct %r1, ptr addrspace(4) %arg1 + %r2 = call %Struct7 @_Z29__spirv_SpecConstantComposite_2(%Struct %r1, %Struct %r1) + %r3 = call %Nested @_Z29__spirv_SpecConstantComposite_3(%Struct7 %r2) + store %Nested %r3, ptr addrspace(4) %arg2 + + ret void +} + +declare %Struct @_Z29__spirv_SpecConstantComposite_1(float) +declare %Struct7 @_Z29__spirv_SpecConstantComposite_2(%Struct, %Struct) +declare %Nested @_Z29__spirv_SpecConstantComposite_3(%Struct7) diff --git a/llvm/test/CodeGen/SPIRV/instructions/atomic-ptr.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic-ptr.ll new file mode 100644 index 0000000..86e9be1 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/atomic-ptr.ll @@ -0,0 +1,38 @@ +; The goal of the test case is to ensure that the Backend doesn't crash on the stage +; of type inference. Result SPIR-V is not expected to be valid from the perspective +; of spirv-val in this case, because there is a difference of accepted return types +; between atomicrmw and OpAtomicExchange. + +; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s + +; CHECK-DAG: %[[#LongTy:]] = OpTypeInt 64 0 +; CHECK-DAG: %[[#PtrLongTy:]] = OpTypePointer CrossWorkgroup %[[#LongTy]] +; CHECK-DAG: %[[#IntTy:]] = OpTypeInt 32 0 +; CHECK-DAG: %[[#Scope:]] = OpConstant %[[#IntTy]] 1 +; CHECK-DAG: %[[#MemSem:]] = OpConstant %[[#IntTy]] 8 +; CHECK-DAG: %[[#PtrPtrLongTy:]] = OpTypePointer CrossWorkgroup %[[#PtrLongTy]] + +; CHECK: OpFunction +; CHECK: %[[#Arg1:]] = OpFunctionParameter %[[#PtrPtrLongTy]] +; CHECK: %[[#Arg2:]] = OpFunctionParameter %[[#PtrLongTy]] +; CHECK: OpAtomicExchange %[[#PtrLongTy]] %[[#Arg1]] %[[#Scope]] %[[#MemSem]] %[[#Arg2]] +; CHECK: OpFunctionEnd + +define dso_local spir_func void @test1(ptr addrspace(1) %arg1, ptr addrspace(1) byval(i64) %arg_ptr) { +entry: + %r = atomicrmw xchg ptr addrspace(1) %arg1, ptr addrspace(1) %arg_ptr acq_rel + ret void +} + +; CHECK: OpFunction +; CHECK: %[[#Arg3:]] = OpFunctionParameter %[[#PtrLongTy]] +; CHECK: %[[#Arg4:]] = OpFunctionParameter %[[#LongTy]] +; CHECK: OpAtomicExchange %[[#LongTy]] %[[#Arg3]] %[[#Scope]] %[[#MemSem]] %[[#Arg4]] +; CHECK: OpFunctionEnd + +define dso_local spir_func void @test2(ptr addrspace(1) %arg1, i64 %arg_ptr) { +entry: + %r = atomicrmw xchg ptr addrspace(1) %arg1, i64 %arg_ptr acq_rel + ret void +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/atomic.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll index 8a19fc7..1ccbd5a 100644 --- a/llvm/test/CodeGen/SPIRV/instructions/atomic.ll +++ b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll @@ -15,18 +15,19 @@ ; CHECK-DAG: OpName [[XOR:%.*]] "test_xor" ; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0 +; CHECK-DAG: [[PtrI32Ty:%.*]] = OpTypePointer Function [[I32Ty]] ; CHECK-DAG: [[I64Ty:%.*]] = OpTypeInt 64 0 +; CHECK-DAG: [[PtrI64Ty:%.*]] = OpTypePointer Generic [[I64Ty]] ;; Device scope is encoded with constant 1 ; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1 ;; "monotonic" maps to the relaxed memory semantics, encoded with constant 0 ; CHECK-DAG: [[RELAXED:%.*]] = OpConstantNull [[I32Ty]] ; CHECK: [[ADD]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_add(i32* %ptr, i32 %val) { @@ -35,11 +36,10 @@ define i32 @test_add(i32* %ptr, i32 %val) { } ; CHECK: [[SUB]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_sub(i32* %ptr, i32 %val) { @@ -48,11 +48,10 @@ define i32 @test_sub(i32* %ptr, i32 %val) { } ; CHECK: [[MIN]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_min(i32* %ptr, i32 %val) { @@ -61,11 +60,10 @@ define i32 @test_min(i32* %ptr, i32 %val) { } ; CHECK: [[MAX]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_max(i32* %ptr, i32 %val) { @@ -74,11 +72,10 @@ define i32 @test_max(i32* %ptr, i32 %val) { } ; CHECK: [[UMIN]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_umin(i32* %ptr, i32 %val) { @@ -87,11 +84,10 @@ define i32 @test_umin(i32* %ptr, i32 %val) { } ; CHECK: [[UMAX]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_umax(i32* %ptr, i32 %val) { @@ -100,11 +96,10 @@ define i32 @test_umax(i32* %ptr, i32 %val) { } ; CHECK: [[AND]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_and(i32* %ptr, i32 %val) { @@ -113,11 +108,10 @@ define i32 @test_and(i32* %ptr, i32 %val) { } ; CHECK: [[OR]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_or(i32* %ptr, i32 %val) { @@ -126,11 +120,10 @@ define i32 @test_or(i32* %ptr, i32 %val) { } ; CHECK: [[XOR]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_xor(i32* %ptr, i32 %val) { @@ -139,13 +132,15 @@ define i32 @test_xor(i32* %ptr, i32 %val) { } ; CHECK: OpFunction -; CHECK: [[Arg1:%.*]] = OpFunctionParameter -; CHECK: [[Arg2:%.*]] = OpFunctionParameter -; CHECK: OpAtomicSMin [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]] -; CHECK: OpAtomicSMax [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]] -; CHECK: OpAtomicUMin [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]] -; CHECK: OpAtomicUMax [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]] -; CHECK: OpFunctionEnd +; CHECK-NEXT: [[Arg1:%.*]] = OpFunctionParameter [[PtrI64Ty]] +; CHECK-NEXT: [[Arg2:%.*]] = OpFunctionParameter [[I64Ty]] +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpAtomicSMin [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]] +; CHECK-NEXT: OpAtomicSMax [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]] +; CHECK-NEXT: OpAtomicUMin [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]] +; CHECK-NEXT: OpAtomicUMax [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd define dso_local spir_kernel void @test_wrappers(ptr addrspace(4) %arg, i64 %val) { %r1 = call spir_func i64 @__spirv_AtomicSMin(ptr addrspace(4) %arg, i32 1, i32 0, i64 %val) %r2 = call spir_func i64 @__spirv_AtomicSMax(ptr addrspace(4) %arg, i32 1, i32 0, i64 %val) diff --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll index 950dfe4..d0c4531 100644 --- a/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll +++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll @@ -12,17 +12,17 @@ ; CHECK-DAG: OpName [[XOR:%.*]] "test_xor" ; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0 +; CHECK-DAG: [[PtrI32Ty:%.*]] = OpTypePointer Function [[I32Ty]] ;; Device scope is encoded with constant 1 ; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1 ;; "acq_rel" maps to the constant 8 ; CHECK-DAG: [[ACQREL:%.*]] = OpConstant [[I32Ty]] 8 ; CHECK: [[ADD]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_add(i32* %ptr, i32 %val) { @@ -31,11 +31,10 @@ define i32 @test_add(i32* %ptr, i32 %val) { } ; CHECK: [[SUB]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_sub(i32* %ptr, i32 %val) { @@ -44,11 +43,10 @@ define i32 @test_sub(i32* %ptr, i32 %val) { } ; CHECK: [[MIN]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_min(i32* %ptr, i32 %val) { @@ -57,11 +55,10 @@ define i32 @test_min(i32* %ptr, i32 %val) { } ; CHECK: [[MAX]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_max(i32* %ptr, i32 %val) { @@ -70,11 +67,10 @@ define i32 @test_max(i32* %ptr, i32 %val) { } ; CHECK: [[UMIN]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_umin(i32* %ptr, i32 %val) { @@ -83,11 +79,10 @@ define i32 @test_umin(i32* %ptr, i32 %val) { } ; CHECK: [[UMAX]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_umax(i32* %ptr, i32 %val) { @@ -96,11 +91,10 @@ define i32 @test_umax(i32* %ptr, i32 %val) { } ; CHECK: [[AND]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_and(i32* %ptr, i32 %val) { @@ -109,11 +103,10 @@ define i32 @test_and(i32* %ptr, i32 %val) { } ; CHECK: [[OR]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_or(i32* %ptr, i32 %val) { @@ -122,11 +115,10 @@ define i32 @test_or(i32* %ptr, i32 %val) { } ; CHECK: [[XOR]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_xor(i32* %ptr, i32 %val) { diff --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll index f142e01..fc1d6da 100644 --- a/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll +++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll @@ -12,17 +12,17 @@ ; CHECK-DAG: OpName [[XOR:%.*]] "test_xor" ; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0 +; CHECK-DAG: [[PtrI32Ty:%.*]] = OpTypePointer Function [[I32Ty]] ;; Device scope is encoded with constant 1 ; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1 ;; "sequentially consistent" maps to constant 16 ; CHECK-DAG: [[SEQ:%.*]] = OpConstant [[I32Ty]] 16 ; CHECK: [[ADD]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_add(i32* %ptr, i32 %val) { @@ -31,11 +31,10 @@ define i32 @test_add(i32* %ptr, i32 %val) { } ; CHECK: [[SUB]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_sub(i32* %ptr, i32 %val) { @@ -44,11 +43,10 @@ define i32 @test_sub(i32* %ptr, i32 %val) { } ; CHECK: [[MIN]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_min(i32* %ptr, i32 %val) { @@ -57,11 +55,10 @@ define i32 @test_min(i32* %ptr, i32 %val) { } ; CHECK: [[MAX]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_max(i32* %ptr, i32 %val) { @@ -70,11 +67,10 @@ define i32 @test_max(i32* %ptr, i32 %val) { } ; CHECK: [[UMIN]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_umin(i32* %ptr, i32 %val) { @@ -83,11 +79,10 @@ define i32 @test_umin(i32* %ptr, i32 %val) { } ; CHECK: [[UMAX]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_umax(i32* %ptr, i32 %val) { @@ -96,11 +91,10 @@ define i32 @test_umax(i32* %ptr, i32 %val) { } ; CHECK: [[AND]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_and(i32* %ptr, i32 %val) { @@ -109,11 +103,10 @@ define i32 @test_and(i32* %ptr, i32 %val) { } ; CHECK: [[OR]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_or(i32* %ptr, i32 %val) { @@ -122,11 +115,10 @@ define i32 @test_or(i32* %ptr, i32 %val) { } ; CHECK: [[XOR]] = OpFunction [[I32Ty]] -; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter -; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]] +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]] ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]] -; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] ; CHECK-NEXT: OpReturnValue [[R]] ; CHECK-NEXT: OpFunctionEnd define i32 @test_xor(i32* %ptr, i32 %val) { diff --git a/llvm/test/CodeGen/SPIRV/pointers/complex.ll b/llvm/test/CodeGen/SPIRV/pointers/complex.ll new file mode 100644 index 0000000..6253ef2 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/pointers/complex.ll @@ -0,0 +1,74 @@ +; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK-DAG: OpName %[[#Foo:]] "foo" +; CHECK-DAG: OpName %[[#CSQRT:]] "csqrt" +; CHECK-DAG: %[[#FloatTy:]] = OpTypeFloat 64 +; CHECK-DAG: %[[#StructTy:]] = OpTypeStruct %[[#FloatTy]] %[[#FloatTy]] +; CHECK-DAG: %[[#GPtrStructTy:]] = OpTypePointer Generic %[[#StructTy]] +; CHECK-DAG: %[[#FPtrStructTy:]] = OpTypePointer Function %[[#StructTy]] +; CHECK-DAG: %[[#WrapTy:]] = OpTypeStruct %[[#StructTy]] +; CHECK-DAG: %[[#Int64Ty:]] = OpTypeInt 64 0 +; CHECK-DAG: %[[#Int32Ty:]] = OpTypeInt 32 0 +; CHECK-DAG: %[[#C1:]] = OpConstant %[[#Int32Ty]] 1 +; CHECK-DAG: %[[#ArrayTy:]] = OpTypeArray %[[#Int64Ty]] %[[#C1]] +; CHECK-DAG: %[[#SArrayTy:]] = OpTypeStruct %[[#ArrayTy]] +; CHECK-DAG: %[[#SSArrayTy:]] = OpTypeStruct %[[#SArrayTy]] +; CHECK-DAG: %[[#CWPtrComplexTy:]] = OpTypePointer CrossWorkgroup %[[#WrapTy]] +; CHECK-DAG: %[[#IdTy:]] = OpTypePointer Function %[[#SSArrayTy]] +; CHECK: %[[#Foo]] = OpFunction +; CHECK: OpFunctionParameter %[[#CWPtrComplexTy]] +; CHECK: OpFunctionParameter %[[#IdTy]] +; CHECK: %[[#CSQRT]] = OpFunction +; CHECK: OpFunctionParameter %[[#GPtrStructTy]] +; CHECK: OpFunctionParameter %[[#FPtrStructTy]] + +%"class.id" = type { %"class.array" } +%"class.array" = type { [1 x i64] } +%"class.complex" = type { { double, double } } + +define weak_odr dso_local spir_kernel void @foo(ptr addrspace(1) align 8 %_arg_buf_out1_access, ptr byval(%"class.id") align 8 %_arg_buf_out1_access3) { +entry: + %tmp.i.i = alloca { double, double }, align 8 + %byval-temp.i.i = alloca { double, double }, align 8 + %idxvalue = load i64, ptr %_arg_buf_out1_access3, align 8 + %add.ptr.i = getelementptr inbounds %"class.complex", ptr addrspace(1) %_arg_buf_out1_access, i64 %idxvalue + %tmp.ascast.i.i = addrspacecast ptr %tmp.i.i to ptr addrspace(4) + %byval-temp.imagp.i.i = getelementptr inbounds i8, ptr %byval-temp.i.i, i64 8 + store double -1.000000e+00, ptr %byval-temp.i.i, align 8 + store double 0.000000e+00, ptr %byval-temp.imagp.i.i, align 8 + call spir_func void @csqrt(ptr addrspace(4) dead_on_unwind writable sret({ double, double }) align 8 %tmp.ascast.i.i, ptr nonnull byval({ double, double }) align 8 %byval-temp.i.i) + %tmp.ascast.real.i.i = load double, ptr %tmp.i.i, align 8 + %tmp.ascast.imagp.i.i = getelementptr inbounds i8, ptr %tmp.i.i, i64 8 + %tmp.ascast.imag.i.i = load double, ptr %tmp.ascast.imagp.i.i, align 8 + store double %tmp.ascast.real.i.i, ptr addrspace(1) %add.ptr.i, align 8 + %dest = getelementptr inbounds i8, ptr addrspace(1) %add.ptr.i, i64 8 + store double %tmp.ascast.imag.i.i, ptr addrspace(1) %dest, align 8 + ret void +} + +define weak dso_local spir_func void @csqrt(ptr addrspace(4) dead_on_unwind noalias writable sret({ double, double }) align 8 %agg.result, ptr byval({ double, double }) align 8 %z) { +entry: + %tmp = alloca { double, double }, align 8 + %byval-temp = alloca { double, double }, align 8 + %tmp.ascast = addrspacecast ptr %tmp to ptr addrspace(4) + %z.ascast.real = load double, ptr %z, align 8 + %z.ascast.imagp = getelementptr inbounds i8, ptr %z, i64 8 + %z.ascast.imag = load double, ptr %z.ascast.imagp, align 8 + %byval-temp.imagp = getelementptr inbounds i8, ptr %byval-temp, i64 8 + store double %z.ascast.real, ptr %byval-temp, align 8 + store double %z.ascast.imag, ptr %byval-temp.imagp, align 8 + call spir_func void @__devicelib_csqrt(ptr addrspace(4) dead_on_unwind writable sret({ double, double }) align 8 %tmp.ascast, ptr nonnull byval({ double, double }) align 8 %byval-temp) #7 + %tmp.ascast.real = load double, ptr %tmp, align 8 + %tmp.ascast.imagp = getelementptr inbounds i8, ptr %tmp, i64 8 + %tmp.ascast.imag = load double, ptr %tmp.ascast.imagp, align 8 + %agg.result.imagp = getelementptr inbounds i8, ptr addrspace(4) %agg.result, i64 8 + store double %tmp.ascast.real, ptr addrspace(4) %agg.result, align 8 + store double %tmp.ascast.imag, ptr addrspace(4) %agg.result.imagp, align 8 + ret void +} + +declare extern_weak dso_local spir_func void @__devicelib_csqrt(ptr addrspace(4) dead_on_unwind writable sret({ double, double }) align 8, ptr byval({ double, double }) align 8) local_unnamed_addr #3 diff --git a/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll index b039f80..f060a97 100644 --- a/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll +++ b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll @@ -18,11 +18,8 @@ ; CHECK-SPIRV-DAG: %[[TyFunPtrLong:.*]] = OpTypeFunction %[[TyVoid]] %[[TyPtrLong]] ; CHECK-SPIRV-DAG: %[[TyGenPtrPtrLong:.*]] = OpTypePointer Generic %[[TyGenPtrLong]] ; CHECK-SPIRV-DAG: %[[TyFunGenPtrLongLong:.*]] = OpTypeFunction %[[TyVoid]] %[[TyGenPtrLong]] %[[TyLong]] -; CHECK-SPIRV-DAG: %[[TyChar:.*]] = OpTypeInt 8 0 -; CHECK-SPIRV-DAG: %[[TyGenPtrChar:.*]] = OpTypePointer Generic %[[TyChar]] -; CHECK-SPIRV-DAG: %[[TyGenPtrPtrChar:.*]] = OpTypePointer Generic %[[TyGenPtrChar]] -; CHECK-SPIRV-DAG: %[[TyFunPtrGenPtrChar:.*]] = OpTypePointer Function %[[TyGenPtrChar]] ; CHECK-SPIRV-DAG: %[[Const3:.*]] = OpConstant %[[TyLong]] 3 +; CHECK-SPIRV-DAG: %[[TyFunPtrGenPtrLong:.*]] = OpTypePointer Function %[[TyGenPtrLong]] ; CHECK-SPIRV: %[[FunTest]] = OpFunction %[[TyVoid]] None %[[TyFunPtrLong]] ; CHECK-SPIRV: %[[ArgCum]] = OpFunctionParameter %[[TyPtrLong]] @@ -41,10 +38,9 @@ ; CHECK-SPIRV: %[[StubObj]] = OpFunctionParameter %[[TyGenPtrLong]] ; CHECK-SPIRV: %[[MemOrder]] = OpFunctionParameter %[[TyLong]] -; CHECK-SPIRV: %[[ObjectAddr:.*]] = OpVariable %[[TyFunPtrGenPtrChar]] Function -; CHECK-SPIRV-NEXT: %[[ToGeneric:.*]] = OpPtrCastToGeneric %[[TyGenPtrPtrChar]] %[[ObjectAddr]] -; CHECK-SPIRV-NEXT: %[[Casted:.*]] = OpBitcast %[[TyGenPtrPtrLong]] %[[ToGeneric]] -; CHECK-SPIRV-NEXT: OpStore %[[Casted]] %[[StubObj]] +; CHECK-SPIRV: %[[ObjectAddr:.*]] = OpVariable %[[TyFunPtrGenPtrLong]] Function +; CHECK-SPIRV-NEXT: %[[ToGeneric:.*]] = OpPtrCastToGeneric %[[TyGenPtrPtrLong]] %[[ObjectAddr]] +; CHECK-SPIRV-NEXT: OpStore %[[ToGeneric]] %[[StubObj]] ; CHECK-SPIRV: %[[FooFunc]] = OpFunction %[[TyVoid]] None %[[TyFunGenPtrLongLong]] ; CHECK-SPIRV: %[[FooObj]] = OpFunctionParameter %[[TyGenPtrLong]] diff --git a/llvm/test/CodeGen/SPIRV/pointers/type-deduce-sycl-stub.ll b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-sycl-stub.ll new file mode 100644 index 0000000..008a474 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-sycl-stub.ll @@ -0,0 +1,127 @@ +; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=all --translator-compatibility-mode --avoid-spirv-capabilities=Shader %s -o - -filetype=obj | spirv-val %} + +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=all --translator-compatibility-mode --avoid-spirv-capabilities=Shader %s -o - -filetype=obj | spirv-val %} + +; CHECK-SPIRV-DAG: OpName %[[#F:]] "finish" +; CHECK-SPIRV-DAG: OpName %[[#FH:]] "finish_helper" +; CHECK-SPIRV-DAG: OpName %[[#S:]] "start" +; CHECK-SPIRV-DAG: OpName %[[#SH:]] "start_helper" + +; CHECK-SPIRV-DAG: %[[#Long:]] = OpTypeInt 64 0 +; CHECK-SPIRV-DAG: %[[#FPtrLong:]] = OpTypePointer Function %[[#Long]] +; CHECK-SPIRV-DAG: %[[#GPtrLong:]] = OpTypePointer Generic %[[#Long]] +; CHECK-SPIRV-DAG: %[[#C3:]] = OpConstant %[[#]] 3 +; CHECK-SPIRV-DAG: %[[#Array3:]] = OpTypeArray %[[#Long]] %[[#C3]] +; CHECK-SPIRV-DAG: %[[#PtrArray3:]] = OpTypePointer Generic %[[#Array3]] +; CHECK-SPIRV-DAG: %[[#FPtrPtrArray3:]] = OpTypePointer Function %[[#PtrArray3]] +; CHECK-SPIRV-DAG: %[[#GPtrPtrArray3:]] = OpTypePointer Generic %[[#PtrArray3]] + +; CHECK-SPIRV: %[[#FH]] = OpFunction +; CHECK-SPIRV: %[[#Arg1:]] = OpFunctionParameter %[[#PtrArray3]] +; CHECK-SPIRV: %[[#Arg2:]] = OpFunctionParameter %[[#Long]] +; CHECK-SPIRV: %[[#GrpIdAddr:]] = OpVariable %[[#FPtrPtrArray3]] Function +; CHECK-SPIRV: %[[#WIId:]] = OpVariable %[[#FPtrLong]] Function +; CHECK-SPIRV: %[[#GenGrpIdAddr:]] = OpPtrCastToGeneric %[[#GPtrPtrArray3]] %[[#GrpIdAddr]] +; CHECK-SPIRV: %[[#GenWIId:]] = OpPtrCastToGeneric %[[#GPtrLong]] %[[#WIId]] +; CHECK-SPIRV: OpStore %[[#GenGrpIdAddr]] %[[#Arg1]] +; CHECK-SPIRV: OpStore %[[#GenWIId]] %[[#Arg2]] +; CHECK-SPIRV: OpReturn +; CHECK-SPIRV: OpFunctionEnd + +@__spirv_BuiltInWorkgroupId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32 +@__spirv_BuiltInGlobalLinearId = external dso_local local_unnamed_addr addrspace(1) constant i64, align 8 +@__spirv_BuiltInWorkgroupSize = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32 + +define weak_odr dso_local spir_kernel void @foo() { +entry: + call spir_func void @start() + call spir_func void @finish() + ret void +} + +define dso_local spir_func void @start() { +entry: + %GroupID = alloca [3 x i64], align 8 + %call.i = tail call spir_func signext i8 @__spirv_SpecConstant(i32 noundef -9145239, i8 noundef signext 0) + %cmp.i.not = icmp eq i8 %call.i, 0 + br i1 %cmp.i.not, label %return, label %if.end + +if.end: ; preds = %entry + %GroupID.ascast = addrspacecast ptr %GroupID to ptr addrspace(4) + %r0 = load <3 x i64>, ptr addrspace(1) @__spirv_BuiltInWorkgroupId, align 32 + %r1 = extractelement <3 x i64> %r0, i64 0 + store i64 %r1, ptr %GroupID, align 8 + %arrayinit.element = getelementptr inbounds i8, ptr %GroupID, i64 8 + %r2 = extractelement <3 x i64> %r0, i64 1 + store i64 %r2, ptr %arrayinit.element, align 8 + %arrayinit.element1 = getelementptr inbounds i8, ptr %GroupID, i64 16 + %r3 = extractelement <3 x i64> %r0, i64 2 + store i64 %r3, ptr %arrayinit.element1, align 8 + %r4 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalLinearId, align 8 + %r5 = load i64, ptr addrspace(1) @__spirv_BuiltInWorkgroupSize, align 32 + %r6 = load i64, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @__spirv_BuiltInWorkgroupSize, i64 8), align 8 + %mul = mul i64 %r5, %r6 + %r7 = load i64, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @__spirv_BuiltInWorkgroupSize, i64 16), align 16 + %mul2 = mul i64 %mul, %r7 + %conv = trunc i64 %mul2 to i32 + call spir_func void @start_helper(ptr addrspace(4) noundef %GroupID.ascast, i64 noundef %r4, i32 noundef %conv) + br label %return + +return: ; preds = %if.end, %entry + ret void +} + +define dso_local spir_func void @finish() { +entry: + %GroupID = alloca [3 x i64], align 8 + %call.i = tail call spir_func signext i8 @__spirv_SpecConstant(i32 noundef -9145239, i8 noundef signext 0) + %cmp.i.not = icmp eq i8 %call.i, 0 + br i1 %cmp.i.not, label %return, label %if.end + +if.end: ; preds = %entry + %GroupID.ascast = addrspacecast ptr %GroupID to ptr addrspace(4) + %r0 = load <3 x i64>, ptr addrspace(1) @__spirv_BuiltInWorkgroupId, align 32 + %r1 = extractelement <3 x i64> %r0, i64 0 + store i64 %r1, ptr %GroupID, align 8 + %arrayinit.element = getelementptr inbounds i8, ptr %GroupID, i64 8 + %r2 = extractelement <3 x i64> %r0, i64 1 + store i64 %r2, ptr %arrayinit.element, align 8 + %arrayinit.element1 = getelementptr inbounds i8, ptr %GroupID, i64 16 + %r3 = extractelement <3 x i64> %r0, i64 2 + store i64 %r3, ptr %arrayinit.element1, align 8 + %r4 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalLinearId, align 8 + call spir_func void @finish_helper(ptr addrspace(4) noundef %GroupID.ascast, i64 noundef %r4) + br label %return + +return: ; preds = %if.end, %entry + ret void +} + +define dso_local spir_func void @start_helper(ptr addrspace(4) noundef %group_id, i64 noundef %wi_id, i32 noundef %wg_size) { +entry: + %group_id.addr = alloca ptr addrspace(4), align 8 + %wi_id.addr = alloca i64, align 8 + %wg_size.addr = alloca i32, align 4 + %group_id.addr.ascast = addrspacecast ptr %group_id.addr to ptr addrspace(4) + %wi_id.addr.ascast = addrspacecast ptr %wi_id.addr to ptr addrspace(4) + %wg_size.addr.ascast = addrspacecast ptr %wg_size.addr to ptr addrspace(4) + store ptr addrspace(4) %group_id, ptr addrspace(4) %group_id.addr.ascast, align 8 + store i64 %wi_id, ptr addrspace(4) %wi_id.addr.ascast, align 8 + store i32 %wg_size, ptr addrspace(4) %wg_size.addr.ascast, align 4 + ret void +} + +define dso_local spir_func void @finish_helper(ptr addrspace(4) noundef %group_id, i64 noundef %wi_id) { +entry: + %group_id.addr = alloca ptr addrspace(4), align 8 + %wi_id.addr = alloca i64, align 8 + %group_id.addr.ascast = addrspacecast ptr %group_id.addr to ptr addrspace(4) + %wi_id.addr.ascast = addrspacecast ptr %wi_id.addr to ptr addrspace(4) + store ptr addrspace(4) %group_id, ptr addrspace(4) %group_id.addr.ascast, align 8 + store i64 %wi_id, ptr addrspace(4) %wi_id.addr.ascast, align 8 + ret void +} + +declare dso_local spir_func signext i8 @__spirv_SpecConstant(i32 noundef, i8 noundef signext) diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll index 8f3f71c..54b2c78 100644 --- a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll +++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll @@ -19,26 +19,17 @@ ; Mangling -; CHECK-SPIRV: OpFunction -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]] -; CHECK-SPIRV: OpFunctionEnd - -; CHECK-SPIRV: OpFunction -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]] -; CHECK-SPIRV: OpFunctionEnd +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]] +; CHECK-SPIRV: OpFunctionEnd define spir_kernel void @test1(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) { entry: @@ -59,6 +50,15 @@ entry: ret void } +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]] +; CHECK-SPIRV: OpFunctionEnd + define spir_kernel void @test2(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) { entry: %var = alloca i32 @@ -88,26 +88,26 @@ declare spir_func ptr @_Z10to_privatePv(ptr addrspace(4)) ; No mangling -; CHECK-SPIRV: OpFunction -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]] -; CHECK-SPIRV: OpFunctionEnd - -; CHECK-SPIRV: OpFunction -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]] -; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]] -; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]] -; CHECK-SPIRV: OpFunctionEnd +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]] +; CHECK-SPIRV: OpFunctionEnd + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]] +; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]] +; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]] +; CHECK-SPIRV: OpFunctionEnd define spir_kernel void @test3(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) { entry: diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll index 96d6016..efb99dc 100644 --- a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll +++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll @@ -14,12 +14,10 @@ ; CHECK-SPIRV-DAG: %[[#GenPtrEventTy:]] = OpTypePointer Generic %[[#EventTy]] ; CHECK-SPIRV-DAG: %[[#FunPtrEventTy:]] = OpTypePointer Function %[[#EventTy]] ; CHECK-SPIRV: OpFunction -; CHECK-SPIRV: %[[#Var:]] = OpVariable %[[#]] Function +; CHECK-SPIRV: %[[#Var:]] = OpVariable %[[#FunPtrEventTy]] Function ; CHECK-SPIRV: %[[#ResEvent:]] = OpGroupAsyncCopy %[[#EventTy]] %[[#Scope]] %[[#Null]] %[[#Null]] %[[#Num]] %[[#Stride]] %[[#Null]] -; CHECK-SPIRV: %[[#VarPtrEvent:]] = OpBitcast %[[#FunPtrEventTy]] %[[#Var]] -; CHECK-SPIRV: OpStore %[[#VarPtrEvent]] %[[#ResEvent]] -; CHECK-SPIRV: %[[#VarPtrEvent2:]] = OpBitcast %[[#FunPtrEventTy]] %[[#Var]] -; CHECK-SPIRV: %[[#PtrEventGen:]] = OpPtrCastToGeneric %[[#]] %[[#VarPtrEvent2]] +; CHECK-SPIRV: OpStore %[[#Var]] %[[#ResEvent]] +; CHECK-SPIRV: %[[#PtrEventGen:]] = OpPtrCastToGeneric %[[#GenPtrEventTy]] %[[#Var]] ; CHECK-SPIRV: OpGroupWaitEvents %[[#Scope]] %[[#Num]] %[[#PtrEventGen]] ; CHECK-SPIRV: OpFunctionEnd diff --git a/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll b/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll index df11565..fcb6191 100644 --- a/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll +++ b/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll @@ -53,13 +53,13 @@ declare dso_local spir_func target("spirv.Event") @_Z22__spirv_GroupAsyncCopyjPU ; CHECK: %[[#BarArg1:]] = OpFunctionParameter %[[#TyPtrSV4_W]] ; CHECK: %[[#BarArg2:]] = OpFunctionParameter %[[#TyPtrSV4_CW]] ; CHECK: %[[#EventVarBar:]] = OpVariable %[[#TyStructPtr]] Function +; CHECK: %[[#EventVarBarCasted2:]] = OpBitcast %[[#TyEventPtr]] %[[#EventVarBar]] ; CHECK: %[[#SrcBar:]] = OpInBoundsPtrAccessChain %[[#TyPtrSV4_CW]] %[[#BarArg2]] %[[#]] ; CHECK-DAG: %[[#BarArg1Casted:]] = OpBitcast %[[#TyPtrV4_W]] %[[#BarArg1]] ; CHECK-DAG: %[[#SrcBarCasted:]] = OpBitcast %[[#TyPtrV4_CW]] %[[#SrcBar]] ; CHECK: %[[#ResBar:]] = OpGroupAsyncCopy %[[#TyEvent]] %[[#]] %[[#BarArg1Casted]] %[[#SrcBarCasted]] %[[#]] %[[#]] %[[#ConstEvent]] ; CHECK: %[[#EventVarBarCasted:]] = OpBitcast %[[#TyEventPtr]] %[[#EventVarBar]] ; CHECK: OpStore %[[#EventVarBarCasted]] %[[#ResBar]] -; CHECK: %[[#EventVarBarCasted2:]] = OpBitcast %[[#TyEventPtr]] %[[#EventVarBar]] ; CHECK: %[[#EventVarBarGen:]] = OpPtrCastToGeneric %[[#TyEventPtrGen]] %[[#EventVarBarCasted2]] ; CHECK: OpGroupWaitEvents %[[#]] %[[#]] %[[#EventVarBarGen]] ; CHECK: OpFunctionEnd |