/*
* 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());
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