| /* |
| * Copyright 2016, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "RSAllocationUtils.h" |
| |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/GlobalVariable.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include "cxxabi.h" |
| |
| #include <sstream> |
| #include <unordered_map> |
| |
| #define DEBUG_TYPE "rs2spirv-rs-allocation-utils" |
| |
| using namespace llvm; |
| |
| namespace rs2spirv { |
| |
| bool isRSAllocation(const GlobalVariable &GV) { |
| auto *PT = cast<PointerType>(GV.getType()); |
| DEBUG(PT->dump()); |
| |
| auto *VT = PT->getElementType(); |
| DEBUG(VT->dump()); |
| std::string TypeName; |
| raw_string_ostream RSO(TypeName); |
| VT->print(RSO); |
| RSO.str(); // Force flush. |
| DEBUG(dbgs() << "TypeName: " << TypeName << '\n'); |
| |
| return TypeName.find("struct.rs_allocation") != std::string::npos; |
| } |
| |
| bool getRSAllocationInfo(Module &M, SmallVectorImpl<RSAllocationInfo> &Allocs) { |
| DEBUG(dbgs() << "getRSAllocationInfo\n"); |
| for (auto &GV : M.globals()) { |
| if (GV.isDeclaration() || !isRSAllocation(GV)) |
| continue; |
| |
| Allocs.push_back({'%' + GV.getName().str(), None, &GV, -1}); |
| } |
| |
| return true; |
| } |
| |
| // Collect Allocation access calls into the Calls |
| // Also update Allocs with assigned ID. |
| // After calling this function, Allocs would contain the mapping from |
| // GV name to the corresponding ID. |
| bool getRSAllocAccesses(SmallVectorImpl<RSAllocationInfo> &Allocs, |
| SmallVectorImpl<RSAllocationCallInfo> &Calls) { |
| DEBUG(dbgs() << "getRSGEATCalls\n"); |
| DEBUG(dbgs() << "\n\n~~~~~~~~~~~~~~~~~~~~~\n\n"); |
| |
| std::unordered_map<const Value *, const GlobalVariable *> Mapping; |
| int id_assigned = 0; |
| |
| for (auto &A : Allocs) { |
| auto *GV = A.GlobalVar; |
| std::vector<User *> WorkList(GV->user_begin(), GV->user_end()); |
| size_t Idx = 0; |
| |
| while (Idx < WorkList.size()) { |
| auto *U = WorkList[Idx]; |
| DEBUG(dbgs() << "Visiting "); |
| DEBUG(U->dump()); |
| ++Idx; |
| auto It = Mapping.find(U); |
| if (It != Mapping.end()) { |
| if (It->second == GV) { |
| continue; |
| } else { |
| errs() << "Duplicate global mapping discovered!\n"; |
| errs() << "\nGlobal: "; |
| GV->print(errs()); |
| errs() << "\nExisting mapping: "; |
| It->second->print(errs()); |
| errs() << "\nUser: "; |
| U->print(errs()); |
| errs() << '\n'; |
| |
| return false; |
| } |
| } |
| |
| Mapping[U] = GV; |
| DEBUG(dbgs() << "New mapping: "); |
| DEBUG(U->print(dbgs())); |
| DEBUG(dbgs() << " -> " << GV->getName() << '\n'); |
| |
| if (auto *FCall = dyn_cast<CallInst>(U)) { |
| if (auto *F = FCall->getCalledFunction()) { |
| const auto FName = F->getName(); |
| DEBUG(dbgs() << "Discovered function call to : " << FName << '\n'); |
| // Treat memcpy as moves for the purpose of this analysis |
| if (FName.startswith("llvm.memcpy")) { |
| assert(FCall->getNumArgOperands() > 0); |
| Value *CopyDest = FCall->getArgOperand(0); |
| // We are interested in the users of the dest operand of |
| // memcpy here |
| Value *LocalCopy = CopyDest->stripPointerCasts(); |
| User *NewU = dyn_cast<User>(LocalCopy); |
| assert(NewU); |
| WorkList.push_back(NewU); |
| continue; |
| } |
| |
| char *demangled = __cxxabiv1::__cxa_demangle( |
| FName.str().c_str(), nullptr, nullptr, nullptr); |
| if (!demangled) |
| continue; |
| const StringRef DemangledNameRef(demangled); |
| DEBUG(dbgs() << "Demangled name: " << DemangledNameRef << '\n'); |
| |
| const StringRef GEAPrefix = "rsGetElementAt_"; |
| const StringRef SEAPrefix = "rsSetElementAt_"; |
| const StringRef DIMXPrefix = "rsAllocationGetDimX"; |
| assert(GEAPrefix.size() == SEAPrefix.size()); |
| |
| const bool IsGEA = DemangledNameRef.startswith(GEAPrefix); |
| const bool IsSEA = DemangledNameRef.startswith(SEAPrefix); |
| const bool IsDIMX = DemangledNameRef.startswith(DIMXPrefix); |
| |
| assert(IsGEA || IsSEA || IsDIMX); |
| if (!A.hasID()) { |
| A.assignID(id_assigned++); |
| } |
| |
| if (IsGEA || IsSEA) { |
| DEBUG(dbgs() << "Found rsAlloc function!\n"); |
| |
| const auto Kind = |
| IsGEA ? RSAllocAccessKind::GEA : RSAllocAccessKind::SEA; |
| |
| const auto RSElementTy = |
| DemangledNameRef.drop_front(GEAPrefix.size()); |
| |
| Calls.push_back({A, FCall, Kind, RSElementTy.str()}); |
| continue; |
| } else if (DemangledNameRef.startswith(GEAPrefix.drop_back()) || |
| DemangledNameRef.startswith(SEAPrefix.drop_back())) { |
| errs() << "Untyped accesses to global rs_allocations are not " |
| "supported.\n"; |
| return false; |
| } else if (IsDIMX) { |
| DEBUG(dbgs() << "Found rsAllocationGetDimX function!\n"); |
| const auto Kind = RSAllocAccessKind::DIMX; |
| Calls.push_back({A, FCall, Kind, ""}); |
| } |
| } |
| } |
| |
| // TODO: Consider using set-like container to reduce computational |
| // complexity. |
| for (auto *NewU : U->users()) |
| if (std::find(WorkList.begin(), WorkList.end(), NewU) == WorkList.end()) |
| WorkList.push_back(NewU); |
| } |
| } |
| |
| std::unordered_map<const GlobalVariable *, std::string> GVAccessTypes; |
| |
| for (auto &Access : Calls) { |
| auto AccessElemTyIt = GVAccessTypes.find(Access.RSAlloc.GlobalVar); |
| if (AccessElemTyIt != GVAccessTypes.end() && |
| AccessElemTyIt->second != Access.RSElementTy) { |
| errs() << "Could not infere element type for: "; |
| Access.RSAlloc.GlobalVar->print(errs()); |
| errs() << '\n'; |
| return false; |
| } else if (AccessElemTyIt == GVAccessTypes.end()) { |
| GVAccessTypes.emplace(Access.RSAlloc.GlobalVar, Access.RSElementTy); |
| Access.RSAlloc.RSElementType = Access.RSElementTy; |
| } |
| } |
| |
| DEBUG(dbgs() << "\n\n~~~~~~~~~~~~~~~~~~~~~\n\n"); |
| return true; |
| } |
| |
| bool solidifyRSAllocAccess(Module &M, RSAllocationCallInfo CallInfo) { |
| DEBUG(dbgs() << "solidifyRSAllocAccess " << CallInfo.RSAlloc.VarName << '\n'); |
| auto *FCall = CallInfo.FCall; |
| auto *Fun = FCall->getCalledFunction(); |
| assert(Fun); |
| |
| StringRef FName; |
| if (CallInfo.Kind == RSAllocAccessKind::DIMX) |
| FName = "rsAllocationGetDimX"; |
| else |
| FName = Fun->getName(); |
| |
| std::ostringstream OSS; |
| OSS << "__rsov_" << FName.str(); |
| // Make up uint32_t F(uint32_t) |
| Type *UInt32Ty = IntegerType::get(M.getContext(), 32); |
| auto *NewFT = FunctionType::get(UInt32Ty, ArrayRef<Type *>(UInt32Ty), false); |
| |
| auto *NewF = Function::Create(NewFT, // Fun->getFunctionType(), |
| Function::ExternalLinkage, OSS.str(), &M); |
| FCall->setCalledFunction(NewF); |
| FCall->setArgOperand(0, ConstantInt::get(UInt32Ty, 0, false)); |
| NewF->setAttributes(Fun->getAttributes()); |
| |
| DEBUG(M.dump()); |
| |
| return true; |
| } |
| |
| } // namespace rs2spirv |