bool RSExportForEach::validateSpecialFuncDecl(int targetAPI, clang::DiagnosticsEngine *DiagEngine, clang::FunctionDecl const *FD) { slangAssert(DiagEngine && FD); bool valid = true; const clang::ASTContext &C = FD->getASTContext(); if (isRootRSFunc(FD)) { unsigned int numParams = FD->getNumParams(); if (numParams == 0) { // Graphics root function, so verify that it returns an int if (FD->getResultType().getCanonicalType() != C.IntTy) { DiagEngine->Report( clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()), DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, "root(void) is required to return " "an int for graphics usage")); valid = false; } } else if ((targetAPI < SLANG_ICS_TARGET_API) && (numParams == 1)) { // Legacy graphics root function // This has already been validated in isRSForEachFunc(). } else { slangAssert(false && "Should not call validateSpecialFuncDecl() on compute root()"); } } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) { if (FD->getNumParams() != 0) { DiagEngine->Report( clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()), DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, "%0(void) is required to have no " "parameters")) << FD->getName(); valid = false; } if (FD->getResultType().getCanonicalType() != C.VoidTy) { DiagEngine->Report( clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()), DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, "%0(void) is required to have a void " "return type")) << FD->getName(); valid = false; } } else { slangAssert(false && "must be called on root, init or .rs.dtor function!"); } return valid; }
bool RSSpecialFunc::validateSpecialFuncDecl(unsigned int targetAPI, slang::RSContext *Context, clang::FunctionDecl const *FD) { slangAssert(Context && FD); bool valid = true; const clang::ASTContext &C = FD->getASTContext(); const clang::QualType &IntType = FD->getASTContext().IntTy; if (isGraphicsRootRSFunc(targetAPI, FD)) { if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) { // Legacy graphics root function const clang::ParmVarDecl *PVD = FD->getParamDecl(0); clang::QualType QT = PVD->getType().getCanonicalType(); if (QT != IntType) { Context->ReportError(PVD->getLocation(), "invalid parameter type for legacy " "graphics root() function: %0") << PVD->getType(); valid = false; } } // Graphics root function, so verify that it returns an int if (FD->getReturnType().getCanonicalType() != IntType) { Context->ReportError(FD->getLocation(), "root() is required to return " "an int for graphics usage"); valid = false; } } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) { if (FD->getNumParams() != 0) { Context->ReportError(FD->getLocation(), "%0(void) is required to have no " "parameters") << FD->getName(); valid = false; } if (FD->getReturnType().getCanonicalType() != C.VoidTy) { Context->ReportError(FD->getLocation(), "%0(void) is required to have a void " "return type") << FD->getName(); valid = false; } } else { slangAssert(false && "must be called on root, init or .rs.dtor function!"); } return valid; }
bool RSContext::processExportFunc(const clang::FunctionDecl *FD) { slangAssert(!FD->getName().empty() && "Function name should not be empty"); if (!FD->isThisDeclarationADefinition()) { return true; } if (FD->getStorageClass() != clang::SC_None) { fprintf(stderr, "RSContext::processExportFunc : cannot export extern or " "static function '%s'\n", FD->getName().str().c_str()); return false; } if (RSExportForEach::isSpecialRSFunc(mTargetAPI, FD)) { // Do not reflect specialized functions like init, dtor, or graphics root. return RSExportForEach::validateSpecialFuncDecl(mTargetAPI, this, FD); } else if (RSExportForEach::isRSForEachFunc(mTargetAPI, this, FD)) { RSExportForEach *EFE = RSExportForEach::Create(this, FD); if (EFE == NULL) return false; else mExportForEach.push_back(EFE); return true; } RSExportFunc *EF = RSExportFunc::Create(this, FD); if (EF == NULL) return false; else mExportFuncs.push_back(EF); return true; }
bool RSContext::reflectToJava(const std::string &OutputPathBase, const std::string &OutputPackageName, const std::string &RSPackageName, const std::string &InputFileName, const std::string &OutputBCFileName, std::string *RealPackageName) { if (RealPackageName != NULL) RealPackageName->clear(); const std::string &PackageName = ((OutputPackageName.empty()) ? mReflectJavaPackageName : OutputPackageName); slangAssert(!PackageName.empty()); // Copy back the really applied package name RealPackageName->assign(PackageName); if (!RSPackageName.empty()) { mRSPackageName = RSPackageName; } RSReflection *R = new RSReflection(this, mGeneratedFileNames); bool ret = R->reflect(OutputPathBase, PackageName, mRSPackageName, InputFileName, OutputBCFileName); if (!ret) fprintf(stderr, "RSContext::reflectToJava : failed to do reflection " "(%s)\n", R->getLastError()); delete R; return ret; }
clang::ModuleLoadResult Slang::loadModule( clang::SourceLocation ImportLoc, clang::ModuleIdPath Path, clang::Module::NameVisibilityKind Visibility, bool IsInclusionDirective) { slangAssert(0 && "Not implemented"); return clang::ModuleLoadResult(); }
string RSReflectionBase::genInitValue(const clang::APValue &Val, bool asBool) { stringstream tmp; switch (Val.getKind()) { case clang::APValue::Int: { llvm::APInt api = Val.getInt(); if(asBool) { tmp << ((api.getSExtValue() == 0) ? "false" : "true"); } else { // TODO: Handle unsigned possibly for C++ API. tmp << api.getSExtValue(); if (api.getBitWidth() > 32) { tmp << "L"; } } break; } case clang::APValue::Float: { llvm::APFloat apf = Val.getFloat(); llvm::SmallString<30> s; apf.toString(s); tmp << s.c_str(); if (&apf.getSemantics() == &llvm::APFloat::IEEEsingle) { if (s.count('.') == 0) { tmp << ".f"; } else { tmp << "f"; } } break; } case clang::APValue::ComplexInt: case clang::APValue::ComplexFloat: case clang::APValue::LValue: case clang::APValue::Vector: { slangAssert(false && "Primitive type cannot have such kind of initializer"); break; } default: { slangAssert(false && "Unknown kind of initializer"); } } return tmp.str(); }
// 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); } }
string RSReflectionBase::genInitValue(const clang::APValue &Val, bool asBool) { stringstream tmp; switch (Val.getKind()) { case clang::APValue::Int: { llvm::APInt api = Val.getInt(); if(asBool) { tmp << ((api.getSExtValue() == 0) ? "false" : "true"); } else { tmp << api.getSExtValue(); if (api.getBitWidth() > 32) { tmp << "L"; } } break; } case clang::APValue::Float: { llvm::APFloat apf = Val.getFloat(); if (&apf.getSemantics() == &llvm::APFloat::IEEEsingle) { tmp << apf.convertToFloat() << "f"; } else { tmp << apf.convertToDouble(); } break; } case clang::APValue::ComplexInt: case clang::APValue::ComplexFloat: case clang::APValue::LValue: case clang::APValue::Vector: { slangAssert(false && "Primitive type cannot have such kind of initializer"); break; } default: { slangAssert(false && "Unknown kind of initializer"); } } return tmp.str(); }
RSExportType *RSExportElement::Create(RSContext *Context, const clang::Type *T, const ElementInfo *EI) { // Create RSExportType corresponded to the @T first and then verify llvm::StringRef TypeName; RSExportType *ET = NULL; if (!Initialized) Init(); slangAssert(EI != NULL && "Element info not found"); if (!RSExportType::NormalizeType(T, TypeName, Context->getDiagnostics(), NULL)) return NULL; switch (T->getTypeClass()) { case clang::Type::Builtin: case clang::Type::Pointer: { slangAssert(EI->vsize == 1 && "Element not a primitive class (please " "check your macro)"); RSExportPrimitiveType *EPT = RSExportPrimitiveType::Create(Context, T, TypeName, EI->normalized); // Verify slangAssert(EI->type == EPT->getType() && "Element has unexpected type"); ET = EPT; break; } case clang::Type::ExtVector: { slangAssert(EI->vsize > 1 && "Element not a vector class (please check " "your macro)"); RSExportVectorType *EVT = RSExportVectorType::Create(Context, static_cast<const clang::ExtVectorType*>( T->getCanonicalTypeInternal() .getTypePtr()), TypeName, EI->normalized); // Verify slangAssert(EI->type == EVT->getType() && "Element has unexpected type"); slangAssert(EI->vsize == EVT->getNumElement() && "Element has unexpected " "size of vector"); ET = EVT; break; } default: { // TODO(zonr): warn that type is not exportable fprintf(stderr, "RSExportElement::Create : type '%s' is not exportable\n", T->getTypeClassName()); break; } } return ET; }
const char *RSReflectionBase::getVectorAccessor(unsigned Index) { static const char *VectorAccessorMap[] = { /* 0 */ "x", /* 1 */ "y", /* 2 */ "z", /* 3 */ "w", }; slangAssert((Index < (sizeof(VectorAccessorMap) / sizeof(const char*))) && "Out-of-bound index to access vector member"); return VectorAccessorMap[Index]; }
// Encase the Bitcode in a wrapper containing RS version information. void Backend::WrapBitcode(llvm::raw_string_ostream &Bitcode) { bcinfo::AndroidBitcodeWrapper wrapper; size_t actualWrapperLen = bcinfo::writeAndroidBitcodeWrapper( &wrapper, Bitcode.str().length(), getTargetAPI(), SlangVersion::CURRENT, mCodeGenOpts.OptimizationLevel); slangAssert(actualWrapperLen > 0); // Write out the bitcode wrapper. FormattedOutStream.write(reinterpret_cast<char*>(&wrapper), actualWrapperLen); // Write out the actual encoded bitcode. FormattedOutStream << Bitcode.str(); }
RSExportVar::RSExportVar(RSContext *Context, const clang::VarDecl *VD, const RSExportType *ET) : RSExportable(Context, RSExportable::EX_VAR), mName(VD->getName().data(), VD->getName().size()), mET(ET), mIsConst(false) { // mInit - Evaluate initializer expression const clang::Expr *Initializer = VD->getAnyInitializer(); if (Initializer != NULL) { switch (ET->getClass()) { case RSExportType::ExportClassPrimitive: case RSExportType::ExportClassVector: { Initializer->Evaluate(mInit, Context->getASTContext()); break; } case RSExportType::ExportClassPointer: { if (Initializer->isNullPointerConstant (Context->getASTContext(), clang::Expr::NPC_ValueDependentIsNotNull) ) mInit.Val = clang::APValue(llvm::APSInt(1)); else Initializer->Evaluate(mInit, Context->getASTContext()); break; } case RSExportType::ExportClassRecord: { // No action fprintf(stderr, "RSExportVar::RSExportVar : Reflection of initializer " "to variable '%s' (of type '%s') is unsupported " "currently.\n", mName.c_str(), ET->getName().c_str()); break; } default: { slangAssert(false && "Unknown class of type"); } } } // mIsConst - Is it a constant? clang::QualType QT = VD->getTypeSourceInfo()->getType(); if (!QT.isNull()) { mIsConst = QT.isConstQualified(); } return; }
// 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); } }
// 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; }
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; } }
bool RSContext::processExportVar(const clang::VarDecl *VD) { slangAssert(!VD->getName().empty() && "Variable name should not be empty"); // TODO(zonr): some check on variable RSExportType *ET = RSExportType::CreateFromDecl(this, VD); if (!ET) return false; RSExportVar *EV = new RSExportVar(this, VD, ET); if (EV == NULL) return false; else mExportVars.push_back(EV); return true; }
bool RSExportFunc::checkParameterPacketType(llvm::StructType *ParamTy) const { if (ParamTy == NULL) return !hasParam(); else if (!hasParam()) return false; slangAssert(mParamPacketType != NULL); const RSExportRecordType *ERT = mParamPacketType; // must have same number of elements if (ERT->getFields().size() != ParamTy->getNumElements()) return false; const llvm::StructLayout *ParamTySL = getRSContext()->getTargetData()->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 = RSExportType::GetTypeAllocSize(F->getType()); size_t T2Size = getRSContext()->getTargetData()->getTypeAllocSize(T2); if (T1Size != T2Size) return false; } return true; }
bool RSContext::processExportType(const llvm::StringRef &Name) { clang::TranslationUnitDecl *TUDecl = mCtx.getTranslationUnitDecl(); slangAssert(TUDecl != NULL && "Translation unit declaration (top-level " "declaration) is null object"); const clang::IdentifierInfo *II = mPP.getIdentifierInfo(Name); if (II == NULL) // TODO(zonr): alert identifier @Name mark as an exportable type cannot be // found return false; clang::DeclContext::lookup_const_result R = TUDecl->lookup(II); RSExportType *ET = NULL; for (clang::DeclContext::lookup_const_iterator I = R.begin(), E = R.end(); I != E; I++) { clang::NamedDecl *const ND = *I; const clang::Type *T = NULL; switch (ND->getKind()) { case clang::Decl::Typedef: { T = static_cast<const clang::TypedefDecl*>( ND)->getCanonicalDecl()->getUnderlyingType().getTypePtr(); break; } case clang::Decl::Record: { T = static_cast<const clang::RecordDecl*>(ND)->getTypeForDecl(); break; } default: { // unsupported, skip break; } } if (T != NULL) ET = RSExportType::Create(this, T); } return (ET != NULL); }
static inline llvm::tool_output_file *OpenOutputFile(const char *OutputFile, unsigned Flags, std::string* Error, clang::Diagnostic* Diag) { slangAssert((OutputFile != NULL) && (Error != NULL) && (Diag != NULL) && "Invalid parameter!"); if (SlangUtils::CreateDirectoryWithParents( llvm::sys::path::parent_path(OutputFile), Error)) { llvm::tool_output_file *F = new llvm::tool_output_file(OutputFile, *Error, Flags); if (F != NULL) return F; } // Report error here. Diag->Report(clang::diag::err_fe_error_opening) << OutputFile << *Error; return NULL; }
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; }
RSContext::RSContext(clang::Preprocessor &PP, clang::ASTContext &Ctx, const clang::TargetInfo &Target, PragmaList *Pragmas, unsigned int TargetAPI, std::vector<std::string> *GeneratedFileNames) : mPP(PP), mCtx(Ctx), mTarget(Target), mPragmas(Pragmas), mTargetAPI(TargetAPI), mGeneratedFileNames(GeneratedFileNames), mTargetData(NULL), mLLVMContext(llvm::getGlobalContext()), mLicenseNote(NULL), mRSPackageName("android.renderscript"), version(0), mMangleCtx(Ctx.createMangleContext()) { slangAssert(mGeneratedFileNames && "Must supply GeneratedFileNames"); // For #pragma rs export_type PP.AddPragmaHandler( "rs", RSPragmaHandler::CreatePragmaExportTypeHandler(this)); // For #pragma rs java_package_name PP.AddPragmaHandler( "rs", RSPragmaHandler::CreatePragmaJavaPackageNameHandler(this)); // For #pragma rs set_reflect_license PP.AddPragmaHandler( "rs", RSPragmaHandler::CreatePragmaReflectLicenseHandler(this)); // For #pragma version PP.AddPragmaHandler(RSPragmaHandler::CreatePragmaVersionHandler(this)); // Prepare target data mTargetData = new llvm::TargetData(Target.getTargetDescription()); return; }
// 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 } } }
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->getDiagnostics(), FD)) { return NULL; } F = new RSExportFunc(Context, Name, FD); // Initialize mParamPacketType if (FD->getNumParams() <= 0) { F->mParamPacketType = NULL; } else { clang::ASTContext &Ctx = Context->getASTContext(); std::string Id(DUMMY_RS_TYPE_NAME_PREFIX"helper_func_param:"); Id.append(F->getName()).append(DUMMY_RS_TYPE_NAME_POSTFIX); 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(), NULL, /* BitWidth = */ NULL, /* Mutable = */ false, /* HasInit = */ false); RD->addDecl(FD); } RD->completeDefinition(); clang::QualType T = Ctx.getTagDeclType(RD); slangAssert(!T.isNull()); RSExportType *ET = RSExportType::Create(Context, T.getTypePtr()); if (ET == NULL) { 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 NULL; } slangAssert((ET->getClass() == RSExportType::ExportClassRecord) && "Parameter packet must be a record"); F->mParamPacketType = static_cast<RSExportRecordType *>(ET); } return F; }
RSExportVar::RSExportVar(RSContext *Context, const clang::VarDecl *VD, const RSExportType *ET) : RSExportable(Context, RSExportable::EX_VAR), mName(VD->getName().data(), VD->getName().size()), mET(ET), mIsConst(false), mArraySize(0), mNumInits(0) { // mInit - Evaluate initializer expression const clang::Expr *Initializer = VD->getAnyInitializer(); if (Initializer != NULL) { switch (ET->getClass()) { case RSExportType::ExportClassPrimitive: case RSExportType::ExportClassVector: { Initializer->EvaluateAsRValue(mInit, Context->getASTContext()); break; } case RSExportType::ExportClassPointer: { if (Initializer->isNullPointerConstant(Context->getASTContext(), clang::Expr::NPC_ValueDependentIsNotNull)) { mInit.Val = clang::APValue(llvm::APSInt(1)); } else { if (!Initializer->EvaluateAsRValue(mInit, Context->getASTContext())) { ReportVarError(Context, Initializer->getExprLoc(), "initializer is not an R-value"); } } break; } case RSExportType::ExportClassConstantArray: { const clang::InitListExpr *IList = static_cast<const clang::InitListExpr*>(Initializer); if (!IList) { ReportVarError(Context, VD->getLocation(), "Unable to find initializer list"); break; } const RSExportConstantArrayType *ECAT = static_cast<const RSExportConstantArrayType*>(ET); mArraySize = ECAT->getSize(); mNumInits = IList->getNumInits(); for (unsigned int i = 0; i < mNumInits; i++) { clang::Expr::EvalResult tempInit; if (!IList->getInit(i)->EvaluateAsRValue(tempInit, Context->getASTContext())) { ReportVarError(Context, IList->getInit(i)->getExprLoc(), "initializer is not an R-value"); } mInitArray.push_back(tempInit); } break; } case RSExportType::ExportClassMatrix: case RSExportType::ExportClassRecord: { ReportVarError(Context, VD->getLocation(), "Reflection of initializer to variable '%0' (of type " "'%1') is unsupported currently.") << mName << ET->getName(); break; } default: { slangAssert(false && "Unknown class of type"); } } } // mIsConst - Is it a constant? clang::QualType QT = VD->getTypeSourceInfo()->getType(); if (!QT.isNull()) { mIsConst = QT.isConstQualified(); } return; }
void Backend::HandleTranslationUnit(clang::ASTContext &Ctx) { HandleTranslationUnitPre(Ctx); mGen->HandleTranslationUnit(Ctx); // Here, we complete a translation unit (whole translation unit is now in LLVM // IR). Now, interact with LLVM backend to generate actual machine code (asm // or machine code, whatever.) // Silently ignore if we weren't initialized for some reason. if (!mpModule) return; llvm::Module *M = mGen->ReleaseModule(); if (!M) { // The module has been released by IR gen on failures, do not double free. mpModule = NULL; return; } slangAssert(mpModule == M && "Unexpected module change during LLVM IR generation"); // Insert #pragma information into metadata section of module if (!mPragmas->empty()) { llvm::NamedMDNode *PragmaMetadata = mpModule->getOrInsertNamedMetadata(Slang::PragmaMetadataName); for (PragmaList::const_iterator I = mPragmas->begin(), E = mPragmas->end(); I != E; I++) { llvm::SmallVector<llvm::Value*, 2> Pragma; // Name goes first Pragma.push_back(llvm::MDString::get(mLLVMContext, I->first)); // And then value Pragma.push_back(llvm::MDString::get(mLLVMContext, I->second)); // Create MDNode and insert into PragmaMetadata PragmaMetadata->addOperand( llvm::MDNode::get(mLLVMContext, Pragma)); } } HandleTranslationUnitPost(mpModule); // Create passes for optimization and code emission // Create and run per-function passes CreateFunctionPasses(); if (mPerFunctionPasses) { mPerFunctionPasses->doInitialization(); for (llvm::Module::iterator I = mpModule->begin(), E = mpModule->end(); I != E; I++) if (!I->isDeclaration()) mPerFunctionPasses->run(*I); mPerFunctionPasses->doFinalization(); } // Create and run module passes CreateModulePasses(); if (mPerModulePasses) mPerModulePasses->run(*mpModule); switch (mOT) { case Slang::OT_Assembly: case Slang::OT_Object: { if (!CreateCodeGenPasses()) return; mCodeGenPasses->doInitialization(); for (llvm::Module::iterator I = mpModule->begin(), E = mpModule->end(); I != E; I++) if (!I->isDeclaration()) mCodeGenPasses->run(*I); mCodeGenPasses->doFinalization(); break; } case Slang::OT_LLVMAssembly: { llvm::PassManager *LLEmitPM = new llvm::PassManager(); LLEmitPM->add(llvm::createPrintModulePass(&FormattedOutStream)); LLEmitPM->run(*mpModule); break; } case Slang::OT_Bitcode: { llvm::PassManager *BCEmitPM = new llvm::PassManager(); std::string BCStr; llvm::raw_string_ostream Bitcode(BCStr); unsigned int TargetAPI = getTargetAPI(); switch (TargetAPI) { case SLANG_HC_TARGET_API: case SLANG_HC_MR1_TARGET_API: case SLANG_HC_MR2_TARGET_API: { // Pre-ICS targets must use the LLVM 2.9 BitcodeWriter BCEmitPM->add(llvm_2_9::createBitcodeWriterPass(Bitcode)); break; } case SLANG_ICS_TARGET_API: case SLANG_ICS_MR1_TARGET_API: { // ICS targets must use the LLVM 2.9_func BitcodeWriter BCEmitPM->add(llvm_2_9_func::createBitcodeWriterPass(Bitcode)); break; } default: { if (TargetAPI < SLANG_MINIMUM_TARGET_API || TargetAPI > SLANG_MAXIMUM_TARGET_API) { slangAssert(false && "Invalid target API value"); } // Switch to the 3.2 BitcodeWriter by default, and don't use // LLVM's included BitcodeWriter at all (for now). BCEmitPM->add(llvm_3_2::createBitcodeWriterPass(Bitcode)); //BCEmitPM->add(llvm::createBitcodeWriterPass(Bitcode)); break; } } BCEmitPM->run(*mpModule); WrapBitcode(Bitcode); break; } case Slang::OT_Nothing: { return; } default: { slangAssert(false && "Unknown output type"); } } FormattedOutStream.flush(); return; }
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; }
// 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); }
// 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 "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; } }
void RSASTReplace::VisitSwitchCase(clang::SwitchCase *SC) { slangAssert(false && "Both case and default have specialized handlers"); VisitStmt(SC); }