787 lines
33 KiB
C++
787 lines
33 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_export_reduce.h"
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
|
|
#include "slang_assert.h"
|
|
#include "slang_rs_context.h"
|
|
#include "slang_rs_export_type.h"
|
|
#include "slang_rs_object_ref_count.h"
|
|
#include "slang_rs_special_kernel_param.h"
|
|
#include "slang_version.h"
|
|
|
|
#include "bcinfo/MetadataExtractor.h"
|
|
|
|
namespace slang {
|
|
|
|
const char RSExportReduce::KeyReduce[] = "reduce";
|
|
const char RSExportReduce::KeyInitializer[] = "initializer";
|
|
const char RSExportReduce::KeyAccumulator[] = "accumulator";
|
|
const char RSExportReduce::KeyCombiner[] = "combiner";
|
|
const char RSExportReduce::KeyOutConverter[] = "outconverter";
|
|
const char RSExportReduce::KeyHalter[] = "halter";
|
|
|
|
bool RSExportReduce::matchName(const llvm::StringRef &Candidate) const {
|
|
return
|
|
Candidate.equals(mNameInitializer) ||
|
|
Candidate.equals(mNameAccumulator) ||
|
|
Candidate.equals(mNameCombiner) ||
|
|
Candidate.equals(mNameOutConverter) ||
|
|
Candidate.equals(mNameHalter);
|
|
}
|
|
|
|
RSExportReduce *RSExportReduce::Create(RSContext *Context,
|
|
const clang::SourceLocation Location,
|
|
const llvm::StringRef &NameReduce,
|
|
const llvm::StringRef &NameInitializer,
|
|
const llvm::StringRef &NameAccumulator,
|
|
const llvm::StringRef &NameCombiner,
|
|
const llvm::StringRef &NameOutConverter,
|
|
const llvm::StringRef &NameHalter) {
|
|
slangAssert(Context);
|
|
RSExportReduce *RNE = new RSExportReduce(Context,
|
|
Location,
|
|
NameReduce,
|
|
NameInitializer,
|
|
NameAccumulator,
|
|
NameCombiner,
|
|
NameOutConverter,
|
|
NameHalter);
|
|
|
|
return RNE;
|
|
}
|
|
|
|
const char *RSExportReduce::getKey(FnIdent Kind) {
|
|
switch (Kind) {
|
|
default:
|
|
slangAssert(!"Unknown FnIdent");
|
|
// and fall through
|
|
case FN_IDENT_INITIALIZER:
|
|
return KeyInitializer;
|
|
case FN_IDENT_ACCUMULATOR:
|
|
return KeyAccumulator;
|
|
case FN_IDENT_COMBINER:
|
|
return KeyCombiner;
|
|
case FN_IDENT_OUT_CONVERTER:
|
|
return KeyOutConverter;
|
|
case FN_IDENT_HALTER:
|
|
return KeyHalter;
|
|
}
|
|
}
|
|
|
|
// This data is needed during analyzeTranslationUnit() but not afterwards.
|
|
// Breaking it out into a struct makes it easy for analyzeTranslationUnit()
|
|
// to call a number of helper functions that all need access to this data.
|
|
struct RSExportReduce::StateOfAnalyzeTranslationUnit {
|
|
|
|
typedef std::function<std::string (const char *Key, const std::string &Name)> DiagnosticDescriptionType;
|
|
|
|
StateOfAnalyzeTranslationUnit(
|
|
RSContext &anRSContext,
|
|
clang::Preprocessor &aPP,
|
|
clang::ASTContext &anASTContext,
|
|
const DiagnosticDescriptionType &aDiagnosticDescription) :
|
|
|
|
RSC(anRSContext),
|
|
PP(aPP),
|
|
ASTC(anASTContext),
|
|
DiagnosticDescription(aDiagnosticDescription),
|
|
|
|
Ok(true),
|
|
|
|
FnInitializer(nullptr),
|
|
FnAccumulator(nullptr),
|
|
FnCombiner(nullptr),
|
|
FnOutConverter(nullptr),
|
|
FnHalter(nullptr),
|
|
|
|
FnInitializerParam(nullptr),
|
|
FnInitializerParamTy(),
|
|
|
|
FnAccumulatorOk(true),
|
|
FnAccumulatorParamFirst(nullptr),
|
|
FnAccumulatorParamFirstTy(),
|
|
FnAccumulatorIndexOfFirstSpecialParameter(0),
|
|
|
|
FnOutConverterOk(true),
|
|
FnOutConverterParamFirst(nullptr),
|
|
FnOutConverterParamFirstTy()
|
|
{ }
|
|
|
|
/*-- Convenience ------------------------------------------*/
|
|
|
|
RSContext &RSC;
|
|
clang::Preprocessor &PP;
|
|
clang::ASTContext &ASTC;
|
|
const DiagnosticDescriptionType DiagnosticDescription;
|
|
|
|
/*-- Actual state -----------------------------------------*/
|
|
|
|
bool Ok;
|
|
|
|
clang::FunctionDecl *FnInitializer;
|
|
clang::FunctionDecl *FnAccumulator;
|
|
clang::FunctionDecl *FnCombiner;
|
|
clang::FunctionDecl *FnOutConverter;
|
|
clang::FunctionDecl *FnHalter;
|
|
|
|
clang::ParmVarDecl *FnInitializerParam;
|
|
clang::QualType FnInitializerParamTy;
|
|
|
|
bool FnAccumulatorOk;
|
|
clang::ParmVarDecl *FnAccumulatorParamFirst;
|
|
clang::QualType FnAccumulatorParamFirstTy;
|
|
size_t FnAccumulatorIndexOfFirstSpecialParameter;
|
|
|
|
bool FnOutConverterOk; // also true if no outconverter
|
|
clang::ParmVarDecl *FnOutConverterParamFirst;
|
|
clang::QualType FnOutConverterParamFirstTy;
|
|
};
|
|
|
|
// does update S.Ok
|
|
clang::FunctionDecl *RSExportReduce::lookupFunction(StateOfAnalyzeTranslationUnit &S,
|
|
const char *Kind, const llvm::StringRef &Name) {
|
|
if (Name.empty())
|
|
return nullptr;
|
|
|
|
clang::TranslationUnitDecl *TUDecl = getRSContext()->getASTContext().getTranslationUnitDecl();
|
|
slangAssert(TUDecl);
|
|
|
|
clang::FunctionDecl *Ret = nullptr;
|
|
const clang::IdentifierInfo *II = S.PP.getIdentifierInfo(Name);
|
|
if (II) {
|
|
for (auto Decl : TUDecl->lookup(II)) {
|
|
clang::FunctionDecl *FDecl = Decl->getAsFunction();
|
|
if (!FDecl || !FDecl->isThisDeclarationADefinition())
|
|
continue;
|
|
if (Ret) {
|
|
S.RSC.ReportError(mLocation,
|
|
"duplicate function definition for '%0(%1)' for '#pragma rs %2(%3)' (%4, %5)")
|
|
<< Kind << Name << KeyReduce << mNameReduce
|
|
<< Ret->getLocation().printToString(S.PP.getSourceManager())
|
|
<< FDecl->getLocation().printToString(S.PP.getSourceManager());
|
|
S.Ok = false;
|
|
return nullptr;
|
|
}
|
|
Ret = FDecl;
|
|
}
|
|
}
|
|
if (!Ret) {
|
|
// Either the identifier lookup failed, or we never found the function definition.
|
|
S.RSC.ReportError(mLocation,
|
|
"could not find function definition for '%0(%1)' for '#pragma rs %2(%3)'")
|
|
<< Kind << Name << KeyReduce << mNameReduce;
|
|
S.Ok = false;
|
|
return nullptr;
|
|
}
|
|
if (Ret) {
|
|
// Must have internal linkage
|
|
if (Ret->getFormalLinkage() != clang::InternalLinkage) {
|
|
S.RSC.ReportError(Ret->getLocation(), "%0 must be static")
|
|
<< S.DiagnosticDescription(Kind, Name);
|
|
S.Ok = false;
|
|
}
|
|
}
|
|
if (Ret == nullptr)
|
|
S.Ok = false;
|
|
return Ret;
|
|
}
|
|
|
|
// updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk
|
|
void RSExportReduce::notOk(StateOfAnalyzeTranslationUnit &S, FnIdent Kind) {
|
|
S.Ok = false;
|
|
if (Kind == FN_IDENT_ACCUMULATOR) {
|
|
S.FnAccumulatorOk = false;
|
|
} else if (Kind == FN_IDENT_OUT_CONVERTER) {
|
|
S.FnOutConverterOk = false;
|
|
}
|
|
}
|
|
|
|
// updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk
|
|
void RSExportReduce::checkVoidReturn(StateOfAnalyzeTranslationUnit &S,
|
|
FnIdent Kind, clang::FunctionDecl *Fn) {
|
|
slangAssert(Fn);
|
|
const clang::QualType ReturnTy = Fn->getReturnType().getCanonicalType();
|
|
if (!ReturnTy->isVoidType()) {
|
|
S.RSC.ReportError(Fn->getLocation(),
|
|
"%0 must return void not '%1'")
|
|
<< S.DiagnosticDescription(getKey(Kind), Fn->getName()) << ReturnTy.getAsString();
|
|
notOk(S, Kind);
|
|
}
|
|
}
|
|
|
|
// updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk
|
|
void RSExportReduce::checkPointeeConstQualified(StateOfAnalyzeTranslationUnit &S,
|
|
FnIdent Kind, const llvm::StringRef &Name,
|
|
const clang::ParmVarDecl *Param, bool ExpectedQualification) {
|
|
const clang::QualType ParamQType = Param->getType();
|
|
slangAssert(ParamQType->isPointerType());
|
|
const clang::QualType PointeeQType = ParamQType->getPointeeType();
|
|
if (PointeeQType.isConstQualified() != ExpectedQualification) {
|
|
S.RSC.ReportError(Param->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must%3 point to const-qualified type")
|
|
<< S.DiagnosticDescription(getKey(Kind), Name)
|
|
<< Param->getName() << ParamQType.getAsString()
|
|
<< (ExpectedQualification ? "" : " not");
|
|
notOk(S, Kind);
|
|
}
|
|
}
|
|
|
|
// Process "void mNameInitializer(compType *accum)"
|
|
void RSExportReduce::analyzeInitializer(StateOfAnalyzeTranslationUnit &S) {
|
|
if (!S.FnInitializer) // initializer is always optional
|
|
return;
|
|
|
|
// Must return void
|
|
checkVoidReturn(S, FN_IDENT_INITIALIZER, S.FnInitializer);
|
|
|
|
// Must have exactly one parameter
|
|
if (S.FnInitializer->getNumParams() != 1) {
|
|
S.RSC.ReportError(S.FnInitializer->getLocation(),
|
|
"%0 must take exactly 1 parameter (found %1)")
|
|
<< S.DiagnosticDescription(KeyInitializer, mNameInitializer)
|
|
<< S.FnInitializer->getNumParams();
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
// Parameter must not be a special parameter
|
|
S.FnInitializerParam = S.FnInitializer->getParamDecl(0);
|
|
if (isSpecialKernelParameter(S.FnInitializerParam->getName())) {
|
|
S.RSC.ReportError(S.FnInitializer->getLocation(),
|
|
"%0 cannot take special parameter '%1'")
|
|
<< S.DiagnosticDescription(KeyInitializer, mNameInitializer)
|
|
<< S.FnInitializerParam->getName();
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
// Parameter must be of pointer type
|
|
S.FnInitializerParamTy = S.FnInitializerParam->getType().getCanonicalType();
|
|
if (!S.FnInitializerParamTy->isPointerType()) {
|
|
S.RSC.ReportError(S.FnInitializer->getLocation(),
|
|
"%0 parameter '%1' must be of pointer type not '%2'")
|
|
<< S.DiagnosticDescription(KeyInitializer, mNameInitializer)
|
|
<< S.FnInitializerParam->getName() << S.FnInitializerParamTy.getAsString();
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
// Parameter must not point to const-qualified
|
|
checkPointeeConstQualified(S, FN_IDENT_INITIALIZER, mNameInitializer, S.FnInitializerParam, false);
|
|
}
|
|
|
|
// Process "void mNameAccumulator(compType *accum, in1Type in1, …, inNType inN[, specialarguments])"
|
|
void RSExportReduce::analyzeAccumulator(StateOfAnalyzeTranslationUnit &S) {
|
|
slangAssert(S.FnAccumulator);
|
|
|
|
// Must return void
|
|
checkVoidReturn(S, FN_IDENT_ACCUMULATOR, S.FnAccumulator);
|
|
|
|
// Must have initial parameter of same type as initializer parameter
|
|
// (if there is an initializer), followed by at least 1 input
|
|
|
|
if (S.FnAccumulator->getNumParams() < 2) {
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 must take at least 2 parameters")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator);
|
|
S.Ok = S.FnAccumulatorOk = false;
|
|
return;
|
|
}
|
|
|
|
S.FnAccumulatorParamFirst = S.FnAccumulator->getParamDecl(0);
|
|
S.FnAccumulatorParamFirstTy = S.FnAccumulatorParamFirst->getType().getCanonicalType();
|
|
|
|
// First parameter must be of pointer type
|
|
if (!S.FnAccumulatorParamFirstTy->isPointerType()) {
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 parameter '%1' must be of pointer type not '%2'")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
|
|
S.Ok = S.FnAccumulatorOk = false;
|
|
return;
|
|
}
|
|
|
|
// If there is an initializer with a pointer-typed parameter (as
|
|
// opposed to an initializer with a bad parameter list), then
|
|
// accumulator first parameter must be of same type as initializer
|
|
// parameter
|
|
if (S.FnInitializer &&
|
|
!S.FnInitializerParamTy.isNull() &&
|
|
S.FnInitializerParamTy->isPointerType() &&
|
|
!S.FnAccumulator->getASTContext().hasSameUnqualifiedType(
|
|
S.FnInitializerParamTy->getPointeeType().getCanonicalType(),
|
|
S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType())) {
|
|
// <accumulator> parameter '<baz>' (type '<tbaz>') and initializer <goo>() parameter '<gaz>' (type '<tgaz>')
|
|
// must be pointers to the same type
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
|
|
" must be pointers to the same type")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString()
|
|
<< KeyInitializer << mNameInitializer
|
|
<< S.FnInitializerParam->getName() << S.FnInitializerParamTy.getAsString();
|
|
S.Ok = S.FnAccumulatorOk = false;
|
|
}
|
|
|
|
if (S.FnAccumulatorOk && S.FnAccumulatorParamFirstTy->getPointeeType()->isFunctionType()) {
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not be pointer to function type")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
|
|
S.Ok = S.FnAccumulatorOk = false;
|
|
}
|
|
|
|
if (S.FnAccumulatorOk && S.FnAccumulatorParamFirstTy->getPointeeType()->isIncompleteType()) {
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not be pointer to incomplete type")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
|
|
S.Ok = S.FnAccumulatorOk = false;
|
|
}
|
|
|
|
if (S.FnAccumulatorOk &&
|
|
HasRSObjectType(S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType().getTypePtr())) {
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not be pointer to data containing an object type")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
|
|
S.Ok = S.FnAccumulatorOk = false;
|
|
}
|
|
|
|
// Parameter must not point to const-qualified
|
|
checkPointeeConstQualified(S, FN_IDENT_ACCUMULATOR, mNameAccumulator, S.FnAccumulatorParamFirst, false);
|
|
|
|
// Analyze special parameters
|
|
S.Ok &= (S.FnAccumulatorOk &= processSpecialKernelParameters(
|
|
&S.RSC,
|
|
std::bind(S.DiagnosticDescription, KeyAccumulator, mNameAccumulator),
|
|
S.FnAccumulator,
|
|
&S.FnAccumulatorIndexOfFirstSpecialParameter,
|
|
&mAccumulatorSignatureMetadata));
|
|
|
|
// Must have at least an accumulator and an input.
|
|
// If we get here we know there are at least 2 arguments; so the only problem case is
|
|
// where we have an accumulator followed immediately by a special parameter.
|
|
if (S.FnAccumulatorIndexOfFirstSpecialParameter < 2) {
|
|
slangAssert(S.FnAccumulatorIndexOfFirstSpecialParameter < S.FnAccumulator->getNumParams());
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 must have at least 1 input ('%1' is a special parameter)")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< S.FnAccumulator->getParamDecl(S.FnAccumulatorIndexOfFirstSpecialParameter)->getName();
|
|
S.Ok = S.FnAccumulatorOk = false;
|
|
return;
|
|
}
|
|
|
|
if (S.FnAccumulatorOk) {
|
|
mAccumulatorSignatureMetadata |= bcinfo::MD_SIG_In;
|
|
mAccumulatorTypeSize = S.ASTC.getTypeSizeInChars(S.FnAccumulatorParamFirstTy->getPointeeType()).getQuantity();
|
|
for (size_t ParamIdx = 1; ParamIdx < S.FnAccumulatorIndexOfFirstSpecialParameter; ++ParamIdx) {
|
|
const clang::ParmVarDecl *const Param = S.FnAccumulator->getParamDecl(ParamIdx);
|
|
mAccumulatorIns.push_back(Param);
|
|
const clang::QualType ParamQType = Param->getType().getCanonicalType();
|
|
const clang::Type *ParamType = ParamQType.getTypePtr();
|
|
|
|
RSExportType *ParamEType = nullptr;
|
|
if (ParamQType->isPointerType()) {
|
|
S.RSC.ReportError(Param->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not be a pointer")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< Param->getName() << ParamQType.getAsString();
|
|
S.Ok = false;
|
|
} else if (HasRSObjectType(ParamType)) {
|
|
S.RSC.ReportError(Param->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not contain an object type")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< Param->getName() << ParamQType.getAsString();
|
|
S.Ok = false;
|
|
} else if (RSExportType::ValidateType(&S.RSC, S.ASTC, ParamQType, Param, Param->getLocStart(),
|
|
S.RSC.getTargetAPI(),
|
|
false /* IsFilterscript */,
|
|
true /* IsExtern */)) {
|
|
// TODO: Better diagnostics on validation or creation failure?
|
|
ParamEType = RSExportType::Create(&S.RSC, ParamType, NotLegacyKernelArgument);
|
|
S.Ok &= (ParamEType != nullptr);
|
|
} else {
|
|
S.Ok = false;
|
|
}
|
|
mAccumulatorInTypes.push_back(ParamEType); // possibly nullptr
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process "void combinename(compType *accum, const compType *val)"
|
|
void RSExportReduce::analyzeCombiner(StateOfAnalyzeTranslationUnit &S) {
|
|
if (S.FnCombiner) {
|
|
// Must return void
|
|
checkVoidReturn(S, FN_IDENT_COMBINER, S.FnCombiner);
|
|
|
|
// Must have exactly two parameters, of same type as first accumulator parameter
|
|
|
|
if (S.FnCombiner->getNumParams() != 2) {
|
|
S.RSC.ReportError(S.FnCombiner->getLocation(),
|
|
"%0 must take exactly 2 parameters (found %1)")
|
|
<< S.DiagnosticDescription(KeyCombiner, mNameCombiner)
|
|
<< S.FnCombiner->getNumParams();
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) {
|
|
// We're already in an error situation. We could compare
|
|
// against the initializer parameter type instead of the first
|
|
// accumulator parameter type (we'd have to check for the
|
|
// availability of a parameter type there, too), but it does not
|
|
// seem worth the effort.
|
|
//
|
|
// Likewise, we could compare the two combiner parameter types
|
|
// against each other.
|
|
slangAssert(!S.Ok);
|
|
return;
|
|
}
|
|
|
|
for (int ParamIdx = 0; ParamIdx < 2; ++ParamIdx) {
|
|
const clang::ParmVarDecl *const FnCombinerParam = S.FnCombiner->getParamDecl(ParamIdx);
|
|
const clang::QualType FnCombinerParamTy = FnCombinerParam->getType().getCanonicalType();
|
|
if (!FnCombinerParamTy->isPointerType() ||
|
|
!S.FnCombiner->getASTContext().hasSameUnqualifiedType(
|
|
S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
|
|
FnCombinerParamTy->getPointeeType().getCanonicalType())) {
|
|
// <combiner> parameter '<baz>' (type '<tbaz>')
|
|
// and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type
|
|
S.RSC.ReportError(S.FnCombiner->getLocation(),
|
|
"%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
|
|
" must be pointers to the same type")
|
|
<< S.DiagnosticDescription(KeyCombiner, mNameCombiner)
|
|
<< FnCombinerParam->getName() << FnCombinerParamTy.getAsString()
|
|
<< KeyAccumulator << mNameAccumulator
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
|
|
S.Ok = false;
|
|
} else {
|
|
// Check const-qualification
|
|
checkPointeeConstQualified(S, FN_IDENT_COMBINER, mNameCombiner, FnCombinerParam, ParamIdx==1);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Ensure accumulator properties permit omission of combiner.
|
|
|
|
if (!S.FnAccumulatorOk) {
|
|
// Couldn't fully analyze accumulator, so cannot see whether it permits omission of combiner.
|
|
return;
|
|
}
|
|
|
|
if (mAccumulatorIns.size() != 1 ||
|
|
S.FnAccumulatorIndexOfFirstSpecialParameter != S.FnAccumulator->getNumParams())
|
|
{
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 must have exactly 1 input"
|
|
" and no special parameters in order for the %1 to be omitted")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< KeyCombiner;
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
const clang::ParmVarDecl *const FnAccumulatorParamInput = S.FnAccumulator->getParamDecl(1);
|
|
const clang::QualType FnAccumulatorParamInputTy = FnAccumulatorParamInput->getType().getCanonicalType();
|
|
if (!S.FnAccumulator->getASTContext().hasSameUnqualifiedType(
|
|
S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
|
|
FnAccumulatorParamInputTy.getCanonicalType())) {
|
|
S.RSC.ReportError(S.FnAccumulator->getLocation(),
|
|
"%0 parameter '%1' (type '%2')"
|
|
" must be pointer to the type of parameter '%3' (type '%4')"
|
|
" in order for the %5 to be omitted")
|
|
<< S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString()
|
|
<< FnAccumulatorParamInput->getName() << FnAccumulatorParamInputTy.getAsString()
|
|
<< KeyCombiner;
|
|
S.Ok = false;
|
|
}
|
|
}
|
|
|
|
// Process "void outconvertname(resultType *result, const compType *accum)"
|
|
void RSExportReduce::analyzeOutConverter(StateOfAnalyzeTranslationUnit &S) {
|
|
if (!S.FnOutConverter) // outconverter is always optional
|
|
return;
|
|
|
|
// Must return void
|
|
checkVoidReturn(S, FN_IDENT_OUT_CONVERTER, S.FnOutConverter);
|
|
|
|
// Must have exactly two parameters
|
|
if (S.FnOutConverter->getNumParams() != 2) {
|
|
S.RSC.ReportError(S.FnOutConverter->getLocation(),
|
|
"%0 must take exactly 2 parameters (found %1)")
|
|
<< S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
|
|
<< S.FnOutConverter->getNumParams();
|
|
S.Ok = S.FnOutConverterOk = false;
|
|
return;
|
|
}
|
|
|
|
// Parameters must not be special and must be of pointer type;
|
|
// and second parameter must match first accumulator parameter
|
|
for (int ParamIdx = 0; ParamIdx < 2; ++ParamIdx) {
|
|
clang::ParmVarDecl *const FnOutConverterParam = S.FnOutConverter->getParamDecl(ParamIdx);
|
|
|
|
if (isSpecialKernelParameter(FnOutConverterParam->getName())) {
|
|
S.RSC.ReportError(S.FnOutConverter->getLocation(),
|
|
"%0 cannot take special parameter '%1'")
|
|
<< S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
|
|
<< FnOutConverterParam->getName();
|
|
S.Ok = S.FnOutConverterOk = false;
|
|
continue;
|
|
}
|
|
|
|
const clang::QualType FnOutConverterParamTy = FnOutConverterParam->getType().getCanonicalType();
|
|
|
|
if (!FnOutConverterParamTy->isPointerType()) {
|
|
S.RSC.ReportError(S.FnOutConverter->getLocation(),
|
|
"%0 parameter '%1' must be of pointer type not '%2'")
|
|
<< S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
|
|
<< FnOutConverterParam->getName() << FnOutConverterParamTy.getAsString();
|
|
S.Ok = S.FnOutConverterOk = false;
|
|
continue;
|
|
}
|
|
|
|
// Check const-qualification
|
|
checkPointeeConstQualified(S, FN_IDENT_OUT_CONVERTER, mNameOutConverter, FnOutConverterParam, ParamIdx==1);
|
|
|
|
if (ParamIdx == 0) {
|
|
S.FnOutConverterParamFirst = FnOutConverterParam;
|
|
S.FnOutConverterParamFirstTy = FnOutConverterParamTy;
|
|
continue;
|
|
}
|
|
|
|
if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) {
|
|
// We're already in an error situation. We could compare
|
|
// against the initializer parameter type instead of the first
|
|
// accumulator parameter type (we'd have to check for the
|
|
// availability of a parameter type there, too), but it does not
|
|
// seem worth the effort.
|
|
slangAssert(!S.Ok);
|
|
continue;
|
|
}
|
|
|
|
if (!S.FnOutConverter->getASTContext().hasSameUnqualifiedType(
|
|
S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
|
|
FnOutConverterParamTy->getPointeeType().getCanonicalType())) {
|
|
// <outconverter> parameter '<baz>' (type '<tbaz>')
|
|
// and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type
|
|
S.RSC.ReportError(S.FnOutConverter->getLocation(),
|
|
"%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
|
|
" must be pointers to the same type")
|
|
<< S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
|
|
<< FnOutConverterParam->getName() << FnOutConverterParamTy.getAsString()
|
|
<< KeyAccumulator << mNameAccumulator
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
|
|
S.Ok = S.FnOutConverterOk = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process "bool haltername(const compType *accum)"
|
|
void RSExportReduce::analyzeHalter(StateOfAnalyzeTranslationUnit &S) {
|
|
if (!S.FnHalter) // halter is always optional
|
|
return;
|
|
|
|
// Must return bool
|
|
const clang::QualType ReturnTy = S.FnHalter->getReturnType().getCanonicalType();
|
|
if (!ReturnTy->isBooleanType()) {
|
|
S.RSC.ReportError(S.FnHalter->getLocation(),
|
|
"%0 must return bool not '%1'")
|
|
<< S.DiagnosticDescription(KeyHalter, mNameHalter) << ReturnTy.getAsString();
|
|
S.Ok = false;
|
|
}
|
|
|
|
// Must have exactly one parameter
|
|
if (S.FnHalter->getNumParams() != 1) {
|
|
S.RSC.ReportError(S.FnHalter->getLocation(),
|
|
"%0 must take exactly 1 parameter (found %1)")
|
|
<< S.DiagnosticDescription(KeyHalter, mNameHalter)
|
|
<< S.FnHalter->getNumParams();
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
// Parameter must not be a special parameter
|
|
const clang::ParmVarDecl *const FnHalterParam = S.FnHalter->getParamDecl(0);
|
|
if (isSpecialKernelParameter(FnHalterParam->getName())) {
|
|
S.RSC.ReportError(S.FnHalter->getLocation(),
|
|
"%0 cannot take special parameter '%1'")
|
|
<< S.DiagnosticDescription(KeyHalter, mNameHalter)
|
|
<< FnHalterParam->getName();
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
// Parameter must be same type as first accumulator parameter
|
|
|
|
if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) {
|
|
// We're already in an error situation. We could compare against
|
|
// the initializer parameter type or the first combiner parameter
|
|
// type instead of the first accumulator parameter type (we'd have
|
|
// to check for the availability of a parameter type there, too),
|
|
// but it does not seem worth the effort.
|
|
slangAssert(!S.Ok);
|
|
return;
|
|
}
|
|
|
|
const clang::QualType FnHalterParamTy = FnHalterParam->getType().getCanonicalType();
|
|
if (!FnHalterParamTy->isPointerType() ||
|
|
!S.FnHalter->getASTContext().hasSameUnqualifiedType(
|
|
S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
|
|
FnHalterParamTy->getPointeeType().getCanonicalType())) {
|
|
// <halter> parameter '<baz>' (type '<tbaz>')
|
|
// and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type
|
|
S.RSC.ReportError(S.FnHalter->getLocation(),
|
|
"%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
|
|
" must be pointers to the same type")
|
|
<< S.DiagnosticDescription(KeyHalter, mNameHalter)
|
|
<< FnHalterParam->getName() << FnHalterParamTy.getAsString()
|
|
<< KeyAccumulator << mNameAccumulator
|
|
<< S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
|
|
S.Ok = false;
|
|
return;
|
|
}
|
|
|
|
// Parameter must point to const-qualified
|
|
checkPointeeConstQualified(S, FN_IDENT_HALTER, mNameHalter, FnHalterParam, true);
|
|
}
|
|
|
|
void RSExportReduce::analyzeResultType(StateOfAnalyzeTranslationUnit &S) {
|
|
if (!(S.FnAccumulatorOk && S.FnOutConverterOk)) {
|
|
// No idea what the result type is
|
|
slangAssert(!S.Ok);
|
|
return;
|
|
}
|
|
|
|
struct ResultInfoType {
|
|
const clang::QualType QType;
|
|
clang::VarDecl *const Decl;
|
|
const char *FnKey;
|
|
const std::string &FnName;
|
|
std::function<std::string ()> UnlessOutConverter;
|
|
} ResultInfo =
|
|
S.FnOutConverter
|
|
? ResultInfoType({ S.FnOutConverterParamFirstTy, S.FnOutConverterParamFirst,
|
|
KeyOutConverter, mNameOutConverter,
|
|
[]() { return std::string(""); }})
|
|
: ResultInfoType({ S.FnAccumulatorParamFirstTy, S.FnAccumulatorParamFirst,
|
|
KeyAccumulator, mNameAccumulator,
|
|
[]() { return std::string(" unless ") + KeyOutConverter + " is provided"; }});
|
|
const clang::QualType PointeeQType = ResultInfo.QType->getPointeeType();
|
|
|
|
if (PointeeQType->isPointerType()) {
|
|
S.RSC.ReportError(ResultInfo.Decl->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not point to a pointer%3")
|
|
<< S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
|
|
<< ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
|
|
<< ResultInfo.UnlessOutConverter();
|
|
} else if (PointeeQType->isIncompleteType()) {
|
|
S.RSC.ReportError(ResultInfo.Decl->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not point to an incomplete type%3")
|
|
<< S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
|
|
<< ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
|
|
<< ResultInfo.UnlessOutConverter();
|
|
} else if (HasRSObjectType(PointeeQType.getTypePtr())) {
|
|
S.RSC.ReportError(ResultInfo.Decl->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not point to data containing an object type%3")
|
|
<< S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
|
|
<< ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
|
|
<< ResultInfo.UnlessOutConverter();
|
|
} else if (RSExportType::ValidateType(&S.RSC, S.ASTC, PointeeQType,
|
|
ResultInfo.Decl, ResultInfo.Decl->getLocStart(),
|
|
S.RSC.getTargetAPI(),
|
|
false /* IsFilterscript */,
|
|
true /* IsExtern */)) {
|
|
// TODO: Better diagnostics on validation or creation failure?
|
|
if ((mResultType = RSExportType::Create(&S.RSC, PointeeQType.getTypePtr(),
|
|
NotLegacyKernelArgument, ResultInfo.Decl)) != nullptr) {
|
|
const RSExportType *CheckType = mResultType;
|
|
const char *ArrayErrorPhrase = "";
|
|
if (mResultType->getClass() == RSExportType::ExportClassConstantArray) {
|
|
CheckType = static_cast<const RSExportConstantArrayType *>(mResultType)->getElementType();
|
|
ArrayErrorPhrase = "n array of";
|
|
}
|
|
switch (CheckType->getClass()) {
|
|
case RSExportType::ExportClassMatrix:
|
|
// Not supported for now -- what does a matrix result type mean?
|
|
S.RSC.ReportError(ResultInfo.Decl->getLocation(),
|
|
"%0 parameter '%1' (type '%2') must not point to a%3 matrix type%4")
|
|
<< S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
|
|
<< ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
|
|
<< ArrayErrorPhrase
|
|
<< ResultInfo.UnlessOutConverter();
|
|
mResultType = nullptr;
|
|
break;
|
|
default:
|
|
// All's well
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mResultType)
|
|
S.RSC.insertExportReduceResultType(mResultType);
|
|
else
|
|
S.Ok = false;
|
|
}
|
|
|
|
bool RSExportReduce::analyzeTranslationUnit() {
|
|
|
|
RSContext &RSC = *getRSContext();
|
|
clang::Preprocessor &PP = RSC.getPreprocessor();
|
|
|
|
StateOfAnalyzeTranslationUnit S(
|
|
RSC, PP, RSC.getASTContext(),
|
|
[&PP, this] (const char *Key, const std::string &Name) {
|
|
std::ostringstream Description;
|
|
Description
|
|
<< Key << " " << Name << "()"
|
|
<< " for '#pragma rs " << KeyReduce << "(" << mNameReduce << ")'"
|
|
<< " (" << mLocation.printToString(PP.getSourceManager()) << ")";
|
|
return Description.str();
|
|
});
|
|
|
|
S.FnInitializer = lookupFunction(S, KeyInitializer, mNameInitializer);
|
|
S.FnAccumulator = lookupFunction(S, KeyAccumulator, mNameAccumulator);
|
|
S.FnCombiner = lookupFunction(S, KeyCombiner, mNameCombiner);
|
|
S.FnOutConverter = lookupFunction(S, KeyOutConverter, mNameOutConverter);
|
|
S.FnHalter = lookupFunction(S, KeyHalter, mNameHalter);
|
|
|
|
if (!S.Ok)
|
|
return false;
|
|
|
|
analyzeInitializer(S);
|
|
analyzeAccumulator(S);
|
|
analyzeCombiner(S);
|
|
analyzeOutConverter(S);
|
|
analyzeHalter(S);
|
|
analyzeResultType(S);
|
|
|
|
return S.Ok;
|
|
}
|
|
|
|
} // namespace slang
|