229 lines
7.5 KiB
C++
229 lines
7.5 KiB
C++
/*
|
|
* 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
|