174 lines
5.2 KiB
C++
174 lines
5.2 KiB
C++
|
/*
|
||
|
* Copyright 2010, 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 "slang_rs_export_func.h"
|
||
|
|
||
|
#include <string>
|
||
|
|
||
|
#include "clang/AST/ASTContext.h"
|
||
|
#include "clang/AST/Decl.h"
|
||
|
|
||
|
#include "llvm/IR/DataLayout.h"
|
||
|
#include "llvm/IR/DerivedTypes.h"
|
||
|
|
||
|
#include "slang_assert.h"
|
||
|
#include "slang_rs_context.h"
|
||
|
|
||
|
namespace slang {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
// Ensure that the exported function is actually valid
|
||
|
static bool ValidateFuncDecl(slang::RSContext *Context,
|
||
|
const clang::FunctionDecl *FD) {
|
||
|
slangAssert(Context && FD);
|
||
|
const clang::ASTContext &C = FD->getASTContext();
|
||
|
if (FD->getReturnType().getCanonicalType() != C.VoidTy) {
|
||
|
Context->ReportError(
|
||
|
FD->getLocation(),
|
||
|
"invokable non-static functions are required to return void");
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
RSExportFunc *RSExportFunc::Create(RSContext *Context,
|
||
|
const clang::FunctionDecl *FD) {
|
||
|
llvm::StringRef Name = FD->getName();
|
||
|
RSExportFunc *F;
|
||
|
|
||
|
slangAssert(!Name.empty() && "Function must have a name");
|
||
|
|
||
|
if (!ValidateFuncDecl(Context, FD)) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
F = new RSExportFunc(Context, Name, FD);
|
||
|
|
||
|
// Initialize mParamPacketType
|
||
|
if (FD->getNumParams() <= 0) {
|
||
|
F->mParamPacketType = nullptr;
|
||
|
} else {
|
||
|
clang::ASTContext &Ctx = Context->getASTContext();
|
||
|
|
||
|
std::string Id = CreateDummyName("helper_func_param", F->getName());
|
||
|
|
||
|
clang::RecordDecl *RD =
|
||
|
clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
|
||
|
Ctx.getTranslationUnitDecl(),
|
||
|
clang::SourceLocation(),
|
||
|
clang::SourceLocation(),
|
||
|
&Ctx.Idents.get(Id));
|
||
|
|
||
|
for (unsigned i = 0; i < FD->getNumParams(); i++) {
|
||
|
const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
|
||
|
llvm::StringRef ParamName = PVD->getName();
|
||
|
|
||
|
if (PVD->hasDefaultArg())
|
||
|
fprintf(stderr, "Note: parameter '%s' in function '%s' has default "
|
||
|
"value which is not supported\n",
|
||
|
ParamName.str().c_str(),
|
||
|
F->getName().c_str());
|
||
|
|
||
|
clang::FieldDecl *FD =
|
||
|
clang::FieldDecl::Create(Ctx,
|
||
|
RD,
|
||
|
clang::SourceLocation(),
|
||
|
clang::SourceLocation(),
|
||
|
PVD->getIdentifier(),
|
||
|
PVD->getOriginalType(),
|
||
|
nullptr,
|
||
|
/* BitWidth = */ nullptr,
|
||
|
/* Mutable = */ false,
|
||
|
/* HasInit = */ clang::ICIS_NoInit);
|
||
|
RD->addDecl(FD);
|
||
|
}
|
||
|
|
||
|
RD->completeDefinition();
|
||
|
|
||
|
clang::QualType T = Ctx.getTagDeclType(RD);
|
||
|
slangAssert(!T.isNull());
|
||
|
|
||
|
RSExportType *ET =
|
||
|
RSExportType::Create(Context, T.getTypePtr(), NotLegacyKernelArgument);
|
||
|
|
||
|
if (ET == nullptr) {
|
||
|
fprintf(stderr, "Failed to export the function %s. There's at least one "
|
||
|
"parameter whose type is not supported by the "
|
||
|
"reflection\n", F->getName().c_str());
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
|
||
|
"Parameter packet must be a record");
|
||
|
|
||
|
F->mParamPacketType = static_cast<RSExportRecordType *>(ET);
|
||
|
}
|
||
|
|
||
|
return F;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
RSExportFunc::checkParameterPacketType(llvm::StructType *ParamTy) const {
|
||
|
if (ParamTy == nullptr)
|
||
|
return !hasParam();
|
||
|
else if (!hasParam())
|
||
|
return false;
|
||
|
|
||
|
slangAssert(mParamPacketType != nullptr);
|
||
|
|
||
|
const RSExportRecordType *ERT = mParamPacketType;
|
||
|
// must have same number of elements
|
||
|
if (ERT->getFields().size() != ParamTy->getNumElements())
|
||
|
return false;
|
||
|
|
||
|
const llvm::StructLayout *ParamTySL =
|
||
|
getRSContext()->getDataLayout().getStructLayout(ParamTy);
|
||
|
|
||
|
unsigned Index = 0;
|
||
|
for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
|
||
|
FE = ERT->fields_end(); FI != FE; FI++, Index++) {
|
||
|
const RSExportRecordType::Field *F = *FI;
|
||
|
|
||
|
llvm::Type *T1 = F->getType()->getLLVMType();
|
||
|
llvm::Type *T2 = ParamTy->getTypeAtIndex(Index);
|
||
|
|
||
|
// Fast check
|
||
|
if (T1 == T2)
|
||
|
continue;
|
||
|
|
||
|
// Check offset
|
||
|
size_t T1Offset = F->getOffsetInParent();
|
||
|
size_t T2Offset = ParamTySL->getElementOffset(Index);
|
||
|
|
||
|
if (T1Offset != T2Offset)
|
||
|
return false;
|
||
|
|
||
|
// Check size
|
||
|
size_t T1Size = F->getType()->getAllocSize();
|
||
|
size_t T2Size = getRSContext()->getDataLayout().getTypeAllocSize(T2);
|
||
|
|
||
|
if (T1Size != T2Size)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace slang
|