393 lines
14 KiB
C++
393 lines
14 KiB
C++
/*
|
|
* Copyright 2015, 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_foreach_lowering.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "slang_rs_context.h"
|
|
#include "slang_rs_export_foreach.h"
|
|
|
|
namespace slang {
|
|
|
|
namespace {
|
|
|
|
const char KERNEL_LAUNCH_FUNCTION_NAME[] = "rsForEach";
|
|
const char KERNEL_LAUNCH_FUNCTION_NAME_WITH_OPTIONS[] = "rsForEachWithOptions";
|
|
const char INTERNAL_LAUNCH_FUNCTION_NAME[] =
|
|
"_Z17rsForEachInternaliP14rs_script_calliiP13rs_allocation";
|
|
|
|
} // anonymous namespace
|
|
|
|
RSForEachLowering::RSForEachLowering(RSContext* ctxt)
|
|
: mCtxt(ctxt), mASTCtxt(ctxt->getASTContext()) {}
|
|
|
|
// Check if the passed-in expr references a kernel function in the following
|
|
// pattern in the AST.
|
|
//
|
|
// ImplicitCastExpr 'void *' <BitCast>
|
|
// `-ImplicitCastExpr 'int (*)(int)' <FunctionToPointerDecay>
|
|
// `-DeclRefExpr 'int (int)' Function 'foo' 'int (int)'
|
|
const clang::FunctionDecl* RSForEachLowering::matchFunctionDesignator(
|
|
clang::Expr* expr) {
|
|
clang::ImplicitCastExpr* ToVoidPtr =
|
|
clang::dyn_cast<clang::ImplicitCastExpr>(expr);
|
|
if (ToVoidPtr == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
clang::ImplicitCastExpr* Decay =
|
|
clang::dyn_cast<clang::ImplicitCastExpr>(ToVoidPtr->getSubExpr());
|
|
|
|
if (Decay == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
clang::DeclRefExpr* DRE =
|
|
clang::dyn_cast<clang::DeclRefExpr>(Decay->getSubExpr());
|
|
|
|
if (DRE == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
const clang::FunctionDecl* FD =
|
|
clang::dyn_cast<clang::FunctionDecl>(DRE->getDecl());
|
|
|
|
if (FD == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
return FD;
|
|
}
|
|
|
|
// Checks if the call expression is a legal rsForEach call by looking for the
|
|
// following pattern in the AST. On success, returns the first argument that is
|
|
// a FunctionDecl of a kernel function.
|
|
//
|
|
// CallExpr 'void'
|
|
// |
|
|
// |-ImplicitCastExpr 'void (*)(void *, ...)' <FunctionToPointerDecay>
|
|
// | `-DeclRefExpr 'void (void *, ...)' 'rsForEach' 'void (void *, ...)'
|
|
// |
|
|
// |-ImplicitCastExpr 'void *' <BitCast>
|
|
// | `-ImplicitCastExpr 'int (*)(int)' <FunctionToPointerDecay>
|
|
// | `-DeclRefExpr 'int (int)' Function 'foo' 'int (int)'
|
|
// |
|
|
// |-ImplicitCastExpr 'rs_allocation':'rs_allocation' <LValueToRValue>
|
|
// | `-DeclRefExpr 'rs_allocation':'rs_allocation' lvalue ParmVar 'in' 'rs_allocation':'rs_allocation'
|
|
// |
|
|
// `-ImplicitCastExpr 'rs_allocation':'rs_allocation' <LValueToRValue>
|
|
// `-DeclRefExpr 'rs_allocation':'rs_allocation' lvalue ParmVar 'out' 'rs_allocation':'rs_allocation'
|
|
const clang::FunctionDecl* RSForEachLowering::matchKernelLaunchCall(
|
|
clang::CallExpr* CE, int* slot, bool* hasOptions) {
|
|
const clang::Decl* D = CE->getCalleeDecl();
|
|
const clang::FunctionDecl* FD = clang::dyn_cast<clang::FunctionDecl>(D);
|
|
|
|
if (FD == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
const clang::StringRef& funcName = FD->getName();
|
|
|
|
if (funcName.equals(KERNEL_LAUNCH_FUNCTION_NAME)) {
|
|
*hasOptions = false;
|
|
} else if (funcName.equals(KERNEL_LAUNCH_FUNCTION_NAME_WITH_OPTIONS)) {
|
|
*hasOptions = true;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mInsideKernel) {
|
|
mCtxt->ReportError(CE->getExprLoc(),
|
|
"Invalid kernel launch call made from inside another kernel.");
|
|
return nullptr;
|
|
}
|
|
|
|
clang::Expr* arg0 = CE->getArg(0);
|
|
const clang::FunctionDecl* kernel = matchFunctionDesignator(arg0);
|
|
|
|
if (kernel == nullptr) {
|
|
mCtxt->ReportError(arg0->getExprLoc(),
|
|
"Invalid kernel launch call. "
|
|
"Expects a function designator for the first argument.");
|
|
return nullptr;
|
|
}
|
|
|
|
// Verifies that kernel is indeed a "kernel" function.
|
|
*slot = mCtxt->getForEachSlotNumber(kernel);
|
|
if (*slot == -1) {
|
|
mCtxt->ReportError(CE->getExprLoc(),
|
|
"%0 applied to function %1 defined without \"kernel\" attribute")
|
|
<< funcName << kernel->getName();
|
|
return nullptr;
|
|
}
|
|
|
|
return kernel;
|
|
}
|
|
|
|
// Create an AST node for the declaration of rsForEachInternal
|
|
clang::FunctionDecl* RSForEachLowering::CreateForEachInternalFunctionDecl() {
|
|
clang::DeclContext* DC = mASTCtxt.getTranslationUnitDecl();
|
|
clang::SourceLocation Loc;
|
|
|
|
llvm::StringRef SR(INTERNAL_LAUNCH_FUNCTION_NAME);
|
|
clang::IdentifierInfo& II = mASTCtxt.Idents.get(SR);
|
|
clang::DeclarationName N(&II);
|
|
|
|
clang::FunctionProtoType::ExtProtoInfo EPI;
|
|
|
|
const clang::QualType& AllocTy = mCtxt->getAllocationType();
|
|
clang::QualType AllocPtrTy = mASTCtxt.getPointerType(AllocTy);
|
|
|
|
clang::QualType ScriptCallTy = mCtxt->getScriptCallType();
|
|
const clang::QualType ScriptCallPtrTy = mASTCtxt.getPointerType(ScriptCallTy);
|
|
|
|
clang::QualType ParamTypes[] = {
|
|
mASTCtxt.IntTy, // int slot
|
|
ScriptCallPtrTy, // rs_script_call_t* launch_options
|
|
mASTCtxt.IntTy, // int numOutput
|
|
mASTCtxt.IntTy, // int numInputs
|
|
AllocPtrTy // rs_allocation* allocs
|
|
};
|
|
|
|
clang::QualType T = mASTCtxt.getFunctionType(
|
|
mASTCtxt.VoidTy, // Return type
|
|
ParamTypes, // Parameter types
|
|
EPI);
|
|
|
|
clang::FunctionDecl* FD = clang::FunctionDecl::Create(
|
|
mASTCtxt, DC, Loc, Loc, N, T, nullptr, clang::SC_Extern);
|
|
|
|
static constexpr unsigned kNumParams = sizeof(ParamTypes) / sizeof(ParamTypes[0]);
|
|
clang::ParmVarDecl *ParamDecls[kNumParams];
|
|
for (unsigned I = 0; I != kNumParams; ++I) {
|
|
ParamDecls[I] = clang::ParmVarDecl::Create(mASTCtxt, FD, Loc,
|
|
Loc, nullptr, ParamTypes[I], nullptr, clang::SC_None, nullptr);
|
|
// Implicit means that this declaration was created by the compiler, and
|
|
// not part of the actual source code.
|
|
ParamDecls[I]->setImplicit();
|
|
}
|
|
FD->setParams(llvm::makeArrayRef(ParamDecls, kNumParams));
|
|
|
|
// Implicit means that this declaration was created by the compiler, and
|
|
// not part of the actual source code.
|
|
FD->setImplicit();
|
|
|
|
return FD;
|
|
}
|
|
|
|
// Create an expression like the following that references the rsForEachInternal to
|
|
// replace the callee in the original call expression that references rsForEach.
|
|
//
|
|
// ImplicitCastExpr 'void (*)(int, rs_script_call_t*, int, int, rs_allocation*)' <FunctionToPointerDecay>
|
|
// `-DeclRefExpr 'void' Function '_Z17rsForEachInternaliP14rs_script_calliiP13rs_allocation' 'void (int, rs_script_call_t*, int, int, rs_allocation*)'
|
|
clang::Expr* RSForEachLowering::CreateCalleeExprForInternalForEach() {
|
|
clang::FunctionDecl* FDNew = CreateForEachInternalFunctionDecl();
|
|
|
|
const clang::QualType FDNewType = FDNew->getType();
|
|
|
|
clang::DeclRefExpr* refExpr = clang::DeclRefExpr::Create(
|
|
mASTCtxt, clang::NestedNameSpecifierLoc(), clang::SourceLocation(), FDNew,
|
|
false, clang::SourceLocation(), FDNewType, clang::VK_RValue);
|
|
|
|
clang::Expr* calleeNew = clang::ImplicitCastExpr::Create(
|
|
mASTCtxt, mASTCtxt.getPointerType(FDNewType),
|
|
clang::CK_FunctionToPointerDecay, refExpr, nullptr, clang::VK_RValue);
|
|
|
|
return calleeNew;
|
|
}
|
|
|
|
// This visit method checks (via pattern matching) if the call expression is to
|
|
// rsForEach, and the arguments satisfy the restrictions on the
|
|
// rsForEach API. If so, replace the call with a rsForEachInternal call
|
|
// with the first argument replaced by the slot number of the kernel function
|
|
// referenced in the original first argument.
|
|
//
|
|
// See comments to the helper methods defined above for details.
|
|
void RSForEachLowering::VisitCallExpr(clang::CallExpr* CE) {
|
|
int slot;
|
|
bool hasOptions;
|
|
const clang::FunctionDecl* kernel = matchKernelLaunchCall(CE, &slot, &hasOptions);
|
|
if (kernel == nullptr) {
|
|
return;
|
|
}
|
|
|
|
slangAssert(slot >= 0);
|
|
|
|
const unsigned numArgsOrig = CE->getNumArgs();
|
|
|
|
clang::QualType resultType = kernel->getReturnType().getCanonicalType();
|
|
const unsigned numOutputsExpected = resultType->isVoidType() ? 0 : 1;
|
|
|
|
const unsigned numInputsExpected = RSExportForEach::getNumInputs(mCtxt->getTargetAPI(), kernel);
|
|
|
|
// Verifies that rsForEach takes the right number of input and output allocations.
|
|
// TODO: Check input/output allocation types match kernel function expectation.
|
|
const unsigned numAllocations = numArgsOrig - (hasOptions ? 2 : 1);
|
|
if (numInputsExpected + numOutputsExpected != numAllocations) {
|
|
mCtxt->ReportError(
|
|
CE->getExprLoc(),
|
|
"Number of input and output allocations unexpected for kernel function %0")
|
|
<< kernel->getName();
|
|
return;
|
|
}
|
|
|
|
clang::Expr* calleeNew = CreateCalleeExprForInternalForEach();
|
|
CE->setCallee(calleeNew);
|
|
|
|
const clang::CanQualType IntTy = mASTCtxt.IntTy;
|
|
const unsigned IntTySize = mASTCtxt.getTypeSize(IntTy);
|
|
const llvm::APInt APIntSlot(IntTySize, slot);
|
|
const clang::Expr* arg0 = CE->getArg(0);
|
|
const clang::SourceLocation Loc(arg0->getLocStart());
|
|
clang::Expr* IntSlotNum =
|
|
clang::IntegerLiteral::Create(mASTCtxt, APIntSlot, IntTy, Loc);
|
|
CE->setArg(0, IntSlotNum);
|
|
|
|
/*
|
|
The last few arguments to rsForEach or rsForEachWithOptions are allocations.
|
|
Creates a new compound literal of an array initialized with those values, and
|
|
passes it to rsForEachInternal as the last (the 5th) argument.
|
|
|
|
For example, rsForEach(foo, ain1, ain2, aout) would be translated into
|
|
rsForEachInternal(
|
|
1, // Slot number for kernel
|
|
NULL, // Launch options
|
|
2, // Number of input allocations
|
|
1, // Number of output allocations
|
|
(rs_allocation[]){ain1, ain2, aout) // Input and output allocations
|
|
);
|
|
|
|
The AST for the rs_allocation array looks like following:
|
|
|
|
ImplicitCastExpr 0x99575670 'struct rs_allocation *' <ArrayToPointerDecay>
|
|
`-CompoundLiteralExpr 0x99575648 'struct rs_allocation [3]' lvalue
|
|
`-InitListExpr 0x99575590 'struct rs_allocation [3]'
|
|
|-ImplicitCastExpr 0x99574b38 'rs_allocation':'struct rs_allocation' <LValueToRValue>
|
|
| `-DeclRefExpr 0x99574a08 'rs_allocation':'struct rs_allocation' lvalue ParmVar 0x9942c408 'ain1' 'rs_allocation':'struct rs_allocation'
|
|
|-ImplicitCastExpr 0x99574b50 'rs_allocation':'struct rs_allocation' <LValueToRValue>
|
|
| `-DeclRefExpr 0x99574a30 'rs_allocation':'struct rs_allocation' lvalue ParmVar 0x9942c478 'ain2' 'rs_allocation':'struct rs_allocation'
|
|
`-ImplicitCastExpr 0x99574b68 'rs_allocation':'struct rs_allocation' <LValueToRValue>
|
|
`-DeclRefExpr 0x99574a58 'rs_allocation':'struct rs_allocation' lvalue ParmVar 0x9942c478 'aout' 'rs_allocation':'struct rs_allocation'
|
|
*/
|
|
|
|
const clang::QualType& AllocTy = mCtxt->getAllocationType();
|
|
const llvm::APInt APIntNumAllocs(IntTySize, numAllocations);
|
|
clang::QualType AllocArrayTy = mASTCtxt.getConstantArrayType(
|
|
AllocTy,
|
|
APIntNumAllocs,
|
|
clang::ArrayType::ArraySizeModifier::Normal,
|
|
0 // index type qualifiers
|
|
);
|
|
|
|
const int allocArgIndexEnd = numArgsOrig - 1;
|
|
int allocArgIndexStart = allocArgIndexEnd;
|
|
|
|
clang::Expr** args = CE->getArgs();
|
|
|
|
clang::SourceLocation lparenloc;
|
|
clang::SourceLocation rparenloc;
|
|
|
|
if (numAllocations > 0) {
|
|
allocArgIndexStart = hasOptions ? 2 : 1;
|
|
lparenloc = args[allocArgIndexStart]->getExprLoc();
|
|
rparenloc = args[allocArgIndexEnd]->getExprLoc();
|
|
}
|
|
|
|
clang::InitListExpr* init = new (mASTCtxt) clang::InitListExpr(
|
|
mASTCtxt,
|
|
lparenloc,
|
|
llvm::ArrayRef<clang::Expr*>(args + allocArgIndexStart, numAllocations),
|
|
rparenloc);
|
|
init->setType(AllocArrayTy);
|
|
|
|
clang::TypeSourceInfo* ti = mASTCtxt.getTrivialTypeSourceInfo(AllocArrayTy);
|
|
clang::CompoundLiteralExpr* CLE = new (mASTCtxt) clang::CompoundLiteralExpr(
|
|
lparenloc,
|
|
ti,
|
|
AllocArrayTy,
|
|
clang::VK_LValue, // A compound literal is an l-value in C.
|
|
init,
|
|
false // Not file scope
|
|
);
|
|
|
|
const clang::QualType AllocPtrTy = mASTCtxt.getPointerType(AllocTy);
|
|
|
|
clang::ImplicitCastExpr* Decay = clang::ImplicitCastExpr::Create(
|
|
mASTCtxt,
|
|
AllocPtrTy,
|
|
clang::CK_ArrayToPointerDecay,
|
|
CLE,
|
|
nullptr, // C++ cast path
|
|
clang::VK_RValue
|
|
);
|
|
|
|
CE->setNumArgs(mASTCtxt, 5);
|
|
|
|
CE->setArg(4, Decay);
|
|
|
|
// Sets the new arguments for NULL launch option (if the user does not set one),
|
|
// the number of outputs, and the number of inputs.
|
|
|
|
if (!hasOptions) {
|
|
const llvm::APInt APIntZero(IntTySize, 0);
|
|
clang::Expr* IntNull =
|
|
clang::IntegerLiteral::Create(mASTCtxt, APIntZero, IntTy, Loc);
|
|
clang::QualType ScriptCallTy = mCtxt->getScriptCallType();
|
|
const clang::QualType ScriptCallPtrTy = mASTCtxt.getPointerType(ScriptCallTy);
|
|
clang::CStyleCastExpr* Cast =
|
|
clang::CStyleCastExpr::Create(mASTCtxt,
|
|
ScriptCallPtrTy,
|
|
clang::VK_RValue,
|
|
clang::CK_NullToPointer,
|
|
IntNull,
|
|
nullptr,
|
|
mASTCtxt.getTrivialTypeSourceInfo(ScriptCallPtrTy),
|
|
clang::SourceLocation(),
|
|
clang::SourceLocation());
|
|
CE->setArg(1, Cast);
|
|
}
|
|
|
|
const llvm::APInt APIntNumOutput(IntTySize, numOutputsExpected);
|
|
clang::Expr* IntNumOutput =
|
|
clang::IntegerLiteral::Create(mASTCtxt, APIntNumOutput, IntTy, Loc);
|
|
CE->setArg(2, IntNumOutput);
|
|
|
|
const llvm::APInt APIntNumInputs(IntTySize, numInputsExpected);
|
|
clang::Expr* IntNumInputs =
|
|
clang::IntegerLiteral::Create(mASTCtxt, APIntNumInputs, IntTy, Loc);
|
|
CE->setArg(3, IntNumInputs);
|
|
}
|
|
|
|
void RSForEachLowering::VisitStmt(clang::Stmt* S) {
|
|
for (clang::Stmt* Child : S->children()) {
|
|
if (Child) {
|
|
Visit(Child);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RSForEachLowering::handleForEachCalls(clang::FunctionDecl* FD,
|
|
unsigned int targetAPI) {
|
|
slangAssert(FD && FD->hasBody());
|
|
|
|
mInsideKernel = FD->hasAttr<clang::RenderScriptKernelAttr>();
|
|
VisitStmt(FD->getBody());
|
|
}
|
|
|
|
} // namespace slang
|