123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128 |
- /*
- * Copyright 2017, 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 <algorithm>
- #include <iostream>
- #include <string>
- #include "clang/AST/APValue.h"
- #include "slang_assert.h"
- #include "slang_rs_export_foreach.h"
- #include "slang_rs_export_func.h"
- #include "slang_rs_export_reduce.h"
- #include "slang_rs_export_type.h"
- #include "slang_rs_export_var.h"
- #include "slang_rs_reflection.h"
- #include "slang_rs_reflection_state.h"
- #include "bcinfo/MetadataExtractor.h"
- namespace slang {
- static bool equal(const clang::APValue &a, const clang::APValue &b) {
- if (a.getKind() != b.getKind())
- return false;
- switch (a.getKind()) {
- case clang::APValue::Float:
- return a.getFloat().bitwiseIsEqual(b.getFloat());
- case clang::APValue::Int:
- return a.getInt() == b.getInt();
- case clang::APValue::Vector: {
- unsigned NumElements = a.getVectorLength();
- if (NumElements != b.getVectorLength())
- return false;
- for (unsigned i = 0; i < NumElements; ++i) {
- if (!equal(a.getVectorElt(i), b.getVectorElt(i)))
- return false;
- }
- return true;
- }
- default:
- slangAssert(false && "unexpected APValue kind");
- return false;
- }
- }
- ReflectionState::~ReflectionState() {
- slangAssert(mState==S_Initial || mState==S_ClosedJava64 || mState==S_Bad);
- delete mStringSet;
- }
- void ReflectionState::openJava32(size_t NumFiles) {
- if (kDisabled)
- return;
- slangAssert(mState==S_Initial);
- mState = S_OpenJava32;
- mStringSet = new llvm::StringSet<>;
- mFiles.BeginCollecting(NumFiles);
- }
- void ReflectionState::closeJava32() {
- if (kDisabled)
- return;
- slangAssert(mState==S_OpenJava32 && (mForEachOpen < 0) && !mOutputClassOpen && (mRecordsState != RS_Open));
- mState = S_ClosedJava32;
- mRSC = nullptr;
- }
- void ReflectionState::openJava64() {
- if (kDisabled)
- return;
- slangAssert(mState==S_ClosedJava32);
- mState = S_OpenJava64;
- mFiles.BeginUsing();
- }
- void ReflectionState::closeJava64() {
- if (kDisabled)
- return;
- slangAssert(mState==S_OpenJava64 && (mForEachOpen < 0) && !mOutputClassOpen && (mRecordsState != RS_Open));
- mState = S_ClosedJava64;
- mRSC = nullptr;
- }
- llvm::StringRef ReflectionState::canon(const std::string &String) {
- slangAssert(isCollecting());
- // NOTE: llvm::StringSet does not permit the empty string as a member
- return String.empty() ? llvm::StringRef() : mStringSet->insert(String).first->getKey();
- }
- std::string ReflectionState::getUniqueTypeName(const RSExportType *T) {
- return RSReflectionJava::GetTypeName(T, RSReflectionJava::TypeNamePseudoC);
- }
- void ReflectionState::nextFile(const RSContext *RSC,
- const std::string &PackageName,
- const std::string &RSSourceFileName) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- mRSC = RSC;
- slangAssert(mRecordsState != RS_Open);
- mRecordsState = RS_Initial;
- if (isCollecting()) {
- File &file = mFiles.CollectNext();
- file.mPackageName = PackageName;
- file.mRSSourceFileName = RSSourceFileName;
- }
- if (isUsing()) {
- File &file = mFiles.UseNext();
- slangAssert(file.mRSSourceFileName == RSSourceFileName);
- if (file.mPackageName != PackageName)
- mRSC->ReportError("in file '%0' Java package name is '%1' for 32-bit targets "
- "but '%2' for 64-bit targets")
- << RSSourceFileName << file.mPackageName << PackageName;
- }
- }
- void ReflectionState::dump() {
- const size_t NumFiles = mFiles.Size();
- for (int i = 0; i < NumFiles; ++i) {
- const File &file = mFiles[i];
- std::cout << "file = \"" << file.mRSSourceFileName << "\", "
- << "package = \"" << file.mPackageName << "\"" << std::endl;
- // NOTE: "StringMap iteration order, however, is not guaranteed to
- // be deterministic". So sort before dumping.
- typedef const llvm::StringMap<File::Record>::MapEntryTy *RecordsEntryTy;
- std::vector<RecordsEntryTy> Records;
- Records.reserve(file.mRecords.size());
- for (auto I = file.mRecords.begin(), E = file.mRecords.end(); I != E; I++)
- Records.push_back(&(*I));
- std::sort(Records.begin(), Records.end(),
- [](RecordsEntryTy a, RecordsEntryTy b) { return a->getKey().compare(b->getKey())==-1; });
- for (auto Record : Records) {
- const auto &Val = Record->getValue();
- std::cout << " (Record) name=\"" << Record->getKey().str() << "\""
- << " allocSize=" << Val.mAllocSize
- << " postPadding=" << Val.mPostPadding
- << " ordinary=" << Val.mOrdinary
- << " matchedByName=" << Val.mMatchedByName
- << std::endl;
- const size_t NumFields = Val.mFieldCount;
- for (int fieldIdx = 0; fieldIdx < NumFields; ++fieldIdx) {
- const auto &field = Val.mFields[fieldIdx];
- std::cout << " (Field) name=\"" << field.mName << "\" ("
- << field.mPrePadding << ", \"" << field.mType.str()
- << "\"(" << field.mStoreSize << ")@" << field.mOffset
- << ", " << field.mPostPadding << ")" << std::endl;
- }
- }
- const size_t NumVars = file.mVariables.Size();
- for (int varIdx = 0; varIdx < NumVars; ++varIdx) {
- const auto &var = file.mVariables[varIdx];
- std::cout << " (Var) name=\"" << var.mName << "\" type=\"" << var.mType.str()
- << "\" const=" << var.mIsConst << " initialized=" << (var.mInitializerCount != 0)
- << " allocSize=" << var.mAllocSize << std::endl;
- }
- for (int feIdx = 0; feIdx < file.mForEachCount; ++feIdx) {
- const auto &fe = file.mForEaches[feIdx];
- std::cout << " (ForEach) ordinal=" << feIdx << " state=";
- switch (fe.mState) {
- case File::ForEach::S_Initial:
- std::cout << "initial" << std::endl;
- continue;
- case File::ForEach::S_Collected:
- std::cout << "collected";
- break;
- case File::ForEach::S_UseMatched:
- std::cout << "usematched";
- break;
- default:
- std::cout << fe.mState;
- break;
- }
- std::cout << " name=\"" << fe.mName << "\" kernel=" << fe.mIsKernel
- << " hasOut=" << fe.mHasOut << " out=\"" << fe.mOut.str()
- << "\" metadata=0x" << std::hex << fe.mSignatureMetadata << std::dec
- << std::endl;
- const size_t NumIns = fe.mIns.Size();
- for (int insIdx = 0; insIdx < NumIns; ++insIdx)
- std::cout << " (In) " << fe.mIns[insIdx].str() << std::endl;
- const size_t NumParams = fe.mParams.Size();
- for (int paramsIdx = 0; paramsIdx < NumParams; ++paramsIdx)
- std::cout << " (Param) " << fe.mParams[paramsIdx].str() << std::endl;
- }
- for (auto feBad : mForEachesBad) {
- std::cout << " (ForEachBad) ordinal=" << feBad->getOrdinal()
- << " name=\"" << feBad->getName() << "\""
- << std::endl;
- }
- const size_t NumInvokables = file.mInvokables.Size();
- for (int invIdx = 0; invIdx < NumInvokables; ++invIdx) {
- const auto &inv = file.mInvokables[invIdx];
- std::cout << " (Invokable) name=\"" << inv.mName << "\"" << std::endl;
- const size_t NumParams = inv.mParamCount;
- for (int paramsIdx = 0; paramsIdx < NumParams; ++paramsIdx)
- std::cout << " (Param) " << inv.mParams[paramsIdx].str() << std::endl;
- }
- const size_t NumReduces = file.mReduces.Size();
- for (int redIdx = 0; redIdx < NumReduces; ++redIdx) {
- const auto &red = file.mReduces[redIdx];
- std::cout << " (Reduce) name=\"" << red.mName
- << "\" result=\"" << red.mResult.str()
- << "\" exportable=" << red.mIsExportable
- << std::endl;
- const size_t NumIns = red.mAccumInCount;
- for (int insIdx = 0; insIdx < NumIns; ++insIdx)
- std::cout << " (In) " << red.mAccumIns[insIdx].str() << std::endl;
- }
- }
- }
- // ForEach /////////////////////////////////////////////////////////////////////////////////////
- void ReflectionState::beginForEaches(size_t Count) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- if (isCollecting()) {
- auto &file = mFiles.Current();
- file.mForEaches = new File::ForEach[Count];
- file.mForEachCount = Count;
- }
- if (isUsing()) {
- slangAssert(mForEachesBad.empty());
- mNumForEachesMatchedByOrdinal = 0;
- }
- }
- // Keep this in sync with RSReflectionJava::genExportForEach().
- void ReflectionState::beginForEach(const RSExportForEach *EF) {
- slangAssert(!isClosed() && (mForEachOpen < 0));
- if (!isActive())
- return;
- const bool IsKernel = EF->isKernelStyle();
- const std::string& Name = EF->getName();
- const unsigned Ordinal = EF->getOrdinal();
- const size_t InCount = EF->getInTypes().size();
- const size_t ParamCount = EF->params_count();
- const RSExportType *OET = EF->getOutType();
- if (OET && !IsKernel) {
- slangAssert(OET->getClass() == RSExportType::ExportClassPointer);
- OET = static_cast<const RSExportPointerType *>(OET)->getPointeeType();
- }
- const std::string OutType = (OET ? getUniqueTypeName(OET) : "");
- const bool HasOut = (EF->hasOut() || EF->hasReturn());
- mForEachOpen = Ordinal;
- mForEachFatal = true; // we'll set this to false if everything looks ok
- auto &file = mFiles.Current();
- auto &foreaches = file.mForEaches;
- if (isCollecting()) {
- slangAssert(Ordinal < file.mForEachCount);
- auto &foreach = foreaches[Ordinal];
- slangAssert(foreach.mState == File::ForEach::S_Initial);
- foreach.mState = File::ForEach::S_Collected;
- foreach.mName = Name;
- foreach.mIns.BeginCollecting(InCount);
- foreach.mParams.BeginCollecting(ParamCount);
- foreach.mOut = canon(OutType);
- foreach.mHasOut = HasOut;
- foreach.mSignatureMetadata = 0;
- foreach.mIsKernel = IsKernel;
- }
- if (isUsing()) {
- if (Ordinal >= file.mForEachCount) {
- mForEachesBad.push_back(EF);
- return;
- }
- auto &foreach = foreaches[Ordinal];
- slangAssert(foreach.mState == File::ForEach::S_Collected);
- foreach.mState = File::ForEach::S_UseMatched;
- ++mNumForEachesMatchedByOrdinal;
- if (foreach.mName != Name) {
- // Order matters because it determines slot number
- mForEachesBad.push_back(EF);
- return;
- }
- // At this point, we have matching ordinal and matching name.
- if (foreach.mIsKernel != IsKernel) {
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has __attribute__((kernel)) for %select{32|64}1-bit targets "
- "but not for %select{64|32}1-bit targets")
- << Name << IsKernel;
- return;
- }
- if ((foreach.mHasOut != HasOut) || !foreach.mOut.equals(OutType)) {
- // There are several different patterns we need to handle:
- // (1) Two different non-void* output types
- // (2) One non-void* output type, one void* output type
- // (3) One non-void* output type, one no-output
- // (4) One void* output type, one no-output
- if (foreach.mHasOut && HasOut) {
- if (foreach.mOut.size() && OutType.size()) {
- // (1) Two different non-void* output types
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has output type '%1' for 32-bit targets "
- "but output type '%2' for 64-bit targets")
- << Name << foreach.mOut.str() << OutType;
- } else {
- // (2) One non-void* return type, one void* output type
- const bool hasTyped64 = OutType.size();
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has output type '%1' for %select{32|64}2-bit targets "
- "but has untyped output for %select{64|32}2-bit targets")
- << Name << (foreach.mOut.str() + OutType) << hasTyped64;
- }
- } else {
- const std::string CombinedOutType = (foreach.mOut.str() + OutType);
- if (CombinedOutType.size()) {
- // (3) One non-void* output type, one no-output
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has output type '%1' for %select{32|64}2-bit targets "
- "but no output for %select{64|32}2-bit targets")
- << Name << CombinedOutType << HasOut;
- } else {
- // (4) One void* output type, one no-output
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has untyped output for %select{32|64}1-bit targets "
- "but no output for %select{64|32}1-bit targets")
- << Name << HasOut;
- }
- }
- }
- bool BadCount = false;
- if (foreach.mIns.Size() != InCount) {
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has %1 input%s1 for 32-bit targets "
- "but %2 input%s2 for 64-bit targets")
- << Name << unsigned(foreach.mIns.Size()) << unsigned(InCount);
- BadCount = true;
- }
- if (foreach.mParams.Size() != ParamCount) {
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has %1 usrData parameter%s1 for 32-bit targets "
- "but %2 usrData parameter%s2 for 64-bit targets")
- << Name << unsigned(foreach.mParams.Size()) << unsigned(ParamCount);
- BadCount = true;
- }
- if (BadCount)
- return;
- foreach.mIns.BeginUsing();
- foreach.mParams.BeginUsing();
- }
- mForEachFatal = false;
- }
- void ReflectionState::addForEachIn(const RSExportForEach *EF, const RSExportType *Type) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- slangAssert(mForEachOpen == EF->getOrdinal());
- // Type may be nullptr in the case of void*. See RSExportForEach::Create().
- if (Type && !EF->isKernelStyle()) {
- slangAssert(Type->getClass() == RSExportType::ExportClassPointer);
- Type = static_cast<const RSExportPointerType *>(Type)->getPointeeType();
- }
- const std::string TypeName = (Type ? getUniqueTypeName(Type) : std::string());
- auto &ins = mFiles.Current().mForEaches[EF->getOrdinal()].mIns;
- if (isCollecting()) {
- ins.CollectNext() = canon(TypeName);
- }
- if (isUsing()) {
- if (mForEachFatal)
- return;
- if (!ins.UseNext().equals(TypeName)) {
- if (ins.Current().size() && TypeName.size()) {
- mRSC->ReportError(EF->getLocation(),
- "%ordinal0 input of foreach kernel '%1' "
- "has type '%2' for 32-bit targets "
- "but type '%3' for 64-bit targets")
- << unsigned(ins.CurrentIdx() + 1)
- << EF->getName()
- << ins.Current().str()
- << TypeName;
- } else {
- const bool hasType64 = TypeName.size();
- mRSC->ReportError(EF->getLocation(),
- "%ordinal0 input of foreach kernel '%1' "
- "has type '%2' for %select{32|64}3-bit targets "
- "but is untyped for %select{64|32}3-bit targets")
- << unsigned(ins.CurrentIdx() + 1)
- << EF->getName()
- << (ins.Current().str() + TypeName)
- << hasType64;
- }
- }
- }
- }
- void ReflectionState::addForEachParam(const RSExportForEach *EF, const RSExportType *Type) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- slangAssert(mForEachOpen == EF->getOrdinal());
- const std::string TypeName = getUniqueTypeName(Type);
- auto ¶ms = mFiles.Current().mForEaches[EF->getOrdinal()].mParams;
- if (isCollecting()) {
- params.CollectNext() = canon(TypeName);
- }
- if (isUsing()) {
- if (mForEachFatal)
- return;
- if (!params.UseNext().equals(TypeName)) {
- mRSC->ReportError(EF->getLocation(),
- "%ordinal0 usrData parameter of foreach kernel '%1' "
- "has type '%2' for 32-bit targets "
- "but type '%3' for 64-bit targets")
- << unsigned(params.CurrentIdx() + 1)
- << EF->getName()
- << params.Current().str()
- << TypeName;
- }
- }
- }
- void ReflectionState::addForEachSignatureMetadata(const RSExportForEach *EF, unsigned Metadata) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- slangAssert(mForEachOpen == EF->getOrdinal());
- // These are properties in the metadata that we need to check.
- const unsigned SpecialParameterBits = bcinfo::MD_SIG_X|bcinfo::MD_SIG_Y|bcinfo::MD_SIG_Z|bcinfo::MD_SIG_Ctxt;
- #ifndef __DISABLE_ASSERTS
- {
- // These are properties in the metadata that we already check in
- // some other way.
- const unsigned BoringBits = bcinfo::MD_SIG_In|bcinfo::MD_SIG_Out|bcinfo::MD_SIG_Usr|bcinfo::MD_SIG_Kernel;
- slangAssert((Metadata & ~(SpecialParameterBits | BoringBits)) == 0);
- }
- #endif
- auto &mSignatureMetadata = mFiles.Current().mForEaches[EF->getOrdinal()].mSignatureMetadata;
- if (isCollecting()) {
- mSignatureMetadata = Metadata;
- }
- if (isUsing()) {
- if (mForEachFatal)
- return;
- if ((mSignatureMetadata & SpecialParameterBits) != (Metadata & SpecialParameterBits)) {
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' has different special parameters "
- "for 32-bit targets than for 64-bit targets")
- << EF->getName();
- }
- }
- }
- void ReflectionState::endForEach() {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- slangAssert(mForEachOpen >= 0);
- if (isUsing() && !mForEachFatal) {
- slangAssert(mFiles.Current().mForEaches[mForEachOpen].mIns.isFinished());
- slangAssert(mFiles.Current().mForEaches[mForEachOpen].mParams.isFinished());
- }
- mForEachOpen = -1;
- }
- void ReflectionState::endForEaches() {
- slangAssert(mForEachOpen < 0);
- if (!isUsing())
- return;
- const auto &file = mFiles.Current();
- if (!mForEachesBad.empty()) {
- std::sort(mForEachesBad.begin(), mForEachesBad.end(),
- [](const RSExportForEach *a, const RSExportForEach *b) { return a->getOrdinal() < b->getOrdinal(); });
- // Note that after the sort, all kernels that are bad because of
- // name mismatch precede all kernels that are bad because of
- // too-high ordinal.
- // 32-bit and 64-bit compiles need to see foreach kernels in the
- // same order, because of slot number assignment. Once we see the
- // first name mismatch in the sequence of foreach kernels, it
- // doesn't make sense to issue further diagnostics regarding
- // foreach kernels except those that still happen to match by name
- // and ordinal (we already handled those diagnostics between
- // beginForEach() and endForEach()).
- bool ForEachesOrderFatal = false;
- for (const RSExportForEach *EF : mForEachesBad) {
- if (EF->getOrdinal() >= file.mForEachCount) {
- mRSC->ReportError(EF->getLocation(),
- "foreach kernel '%0' is only present for 64-bit targets")
- << EF->getName();
- } else {
- mRSC->ReportError(EF->getLocation(),
- "%ordinal0 foreach kernel is '%1' for 32-bit targets "
- "but '%2' for 64-bit targets")
- << (EF->getOrdinal() + 1)
- << mFiles.Current().mForEaches[EF->getOrdinal()].mName
- << EF->getName();
- ForEachesOrderFatal = true;
- break;
- }
- }
- mForEachesBad.clear();
- if (ForEachesOrderFatal)
- return;
- }
- if (mNumForEachesMatchedByOrdinal == file.mForEachCount)
- return;
- for (unsigned ord = 0; ord < file.mForEachCount; ord++) {
- const auto &fe = file.mForEaches[ord];
- if (fe.mState == File::ForEach::S_Collected) {
- mRSC->ReportError("in file '%0' foreach kernel '%1' is only present for 32-bit targets")
- << file.mRSSourceFileName << fe.mName;
- }
- }
- }
- // Invokable ///////////////////////////////////////////////////////////////////////////////////
- // Keep this in sync with RSReflectionJava::genExportFunction().
- void ReflectionState::declareInvokable(const RSExportFunc *EF) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- const std::string& Name = EF->getName(/*Mangle=*/false);
- const size_t ParamCount = EF->getNumParameters();
- auto &invokables = mFiles.Current().mInvokables;
- if (isCollecting()) {
- auto &invokable = invokables.CollectNext();
- invokable.mName = Name;
- invokable.mParamCount = ParamCount;
- if (EF->hasParam()) {
- unsigned FieldIdx = 0;
- invokable.mParams = new llvm::StringRef[ParamCount];
- for (RSExportFunc::const_param_iterator I = EF->params_begin(),
- E = EF->params_end();
- I != E; I++, FieldIdx++) {
- invokable.mParams[FieldIdx] = canon(getUniqueTypeName((*I)->getType()));
- }
- }
- }
- if (isUsing()) {
- if (mInvokablesOrderFatal)
- return;
- if (invokables.isFinished()) {
- // This doesn't actually break reflection, but that's a
- // coincidence of the fact that we reflect during the 64-bit
- // compilation pass rather than the 32-bit compilation pass, and
- // of the fact that the "extra" invokable(s) are at the end.
- mRSC->ReportError(EF->getLocation(),
- "invokable function '%0' is only present for 64-bit targets")
- << Name;
- return;
- }
- auto &invokable = invokables.UseNext();
- if (invokable.mName != Name) {
- // Order matters because it determines slot number
- mRSC->ReportError(EF->getLocation(),
- "%ordinal0 invokable function is '%1' for 32-bit targets "
- "but '%2' for 64-bit targets")
- << unsigned(invokables.CurrentIdx() + 1)
- << invokable.mName
- << Name;
- mInvokablesOrderFatal = true;
- return;
- }
- if (invokable.mParamCount != ParamCount) {
- mRSC->ReportError(EF->getLocation(),
- "invokable function '%0' has %1 parameter%s1 for 32-bit targets "
- "but %2 parameter%s2 for 64-bit targets")
- << Name << unsigned(invokable.mParamCount) << unsigned(ParamCount);
- return;
- }
- if (EF->hasParam()) {
- unsigned FieldIdx = 0;
- for (RSExportFunc::const_param_iterator I = EF->params_begin(),
- E = EF->params_end();
- I != E; I++, FieldIdx++) {
- const std::string Type = getUniqueTypeName((*I)->getType());
- if (!invokable.mParams[FieldIdx].equals(Type)) {
- mRSC->ReportError(EF->getLocation(),
- "%ordinal0 parameter of invokable function '%1' "
- "has type '%2' for 32-bit targets "
- "but type '%3' for 64-bit targets")
- << (FieldIdx + 1)
- << Name
- << invokable.mParams[FieldIdx].str()
- << Type;
- }
- }
- }
- }
- }
- void ReflectionState::endInvokables() {
- if (!isUsing() || mInvokablesOrderFatal)
- return;
- auto &invokables = mFiles.Current().mInvokables;
- while (!invokables.isFinished()) {
- const auto &invokable = invokables.UseNext();
- mRSC->ReportError("in file '%0' invokable function '%1' is only present for 32-bit targets")
- << mFiles.Current().mRSSourceFileName << invokable.mName;
- }
- }
- // Record //////////////////////////////////////////////////////////////////////////////////////
- void ReflectionState::beginRecords() {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- slangAssert(mRecordsState != RS_Open);
- mRecordsState = RS_Open;
- mNumRecordsMatchedByName = 0;
- }
- void ReflectionState::endRecords() {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- slangAssert(mRecordsState == RS_Open);
- mRecordsState = RS_Closed;
- if (isUsing()) {
- const File &file = mFiles.Current();
- if (mNumRecordsMatchedByName == file.mRecords.size())
- return;
- // NOTE: "StringMap iteration order, however, is not guaranteed to
- // be deterministic". So sort by name before reporting.
- // Alternatively, if we record additional information, we could
- // sort by source location or by order in which we discovered the
- // need to export.
- std::vector<llvm::StringRef> Non64RecordNames;
- for (auto I = file.mRecords.begin(), E = file.mRecords.end(); I != E; I++)
- if (!I->getValue().mMatchedByName && I->getValue().mOrdinary)
- Non64RecordNames.push_back(I->getKey());
- std::sort(Non64RecordNames.begin(), Non64RecordNames.end(),
- [](llvm::StringRef a, llvm::StringRef b) { return a.compare(b)==-1; });
- for (auto N : Non64RecordNames)
- mRSC->ReportError("in file '%0' structure '%1' is exported only for 32-bit targets")
- << file.mRSSourceFileName << N.str();
- }
- }
- void ReflectionState::declareRecord(const RSExportRecordType *ERT, bool Ordinary) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- slangAssert(mRecordsState == RS_Open);
- auto &records = mFiles.Current().mRecords;
- if (isCollecting()) {
- // Keep struct/field layout in sync with
- // RSReflectionJava::genPackVarOfType() and
- // RSReflectionJavaElementBuilder::genAddElement()
- // Save properties of record
- const size_t FieldCount = ERT->fields_size();
- File::Record::Field *Fields = new File::Record::Field[FieldCount];
- size_t Pos = 0; // Relative position of field within record
- unsigned FieldIdx = 0;
- for (RSExportRecordType::const_field_iterator I = ERT->fields_begin(), E = ERT->fields_end();
- I != E; I++, FieldIdx++) {
- const RSExportRecordType::Field *FieldExport = *I;
- size_t FieldOffset = FieldExport->getOffsetInParent();
- const RSExportType *T = FieldExport->getType();
- size_t FieldStoreSize = T->getStoreSize();
- size_t FieldAllocSize = T->getAllocSize();
- slangAssert(FieldOffset >= Pos);
- slangAssert(FieldAllocSize >= FieldStoreSize);
- auto &FieldState = Fields[FieldIdx];
- FieldState.mName = FieldExport->getName();
- FieldState.mType = canon(getUniqueTypeName(T));
- FieldState.mPrePadding = FieldOffset - Pos;
- FieldState.mPostPadding = FieldAllocSize - FieldStoreSize;
- FieldState.mOffset = FieldOffset;
- FieldState.mStoreSize = FieldStoreSize;
- Pos = FieldOffset + FieldAllocSize;
- }
- slangAssert(ERT->getAllocSize() >= Pos);
- // Insert record into map
- slangAssert(records.find(ERT->getName()) == records.end());
- File::Record &record = records[ERT->getName()];
- record.mFields = Fields;
- record.mFieldCount = FieldCount;
- record.mPostPadding = ERT->getAllocSize() - Pos;
- record.mAllocSize = ERT->getAllocSize();
- record.mOrdinary = Ordinary;
- record.mMatchedByName = false;
- }
- if (isUsing()) {
- if (!Ordinary)
- return;
- const auto RIT = records.find(ERT->getName());
- if (RIT == records.end()) {
- // This doesn't actually break reflection, but that's a
- // coincidence of the fact that we reflect during the 64-bit
- // compilation pass rather than the 32-bit compilation pass, so
- // a record that's only classified as exported during the 64-bit
- // compilation pass doesn't cause any problems.
- mRSC->ReportError(ERT->getLocation(), "structure '%0' is exported only for 64-bit targets")
- << ERT->getName();
- return;
- }
- File::Record &record = RIT->getValue();
- record.mMatchedByName = true;
- ++mNumRecordsMatchedByName;
- slangAssert(record.mOrdinary);
- if (ERT->fields_size() != record.mFieldCount) {
- mRSC->ReportError(ERT->getLocation(),
- "exported structure '%0' has %1 field%s1 for 32-bit targets "
- "but %2 field%s2 for 64-bit targets")
- << ERT->getName() << unsigned(record.mFieldCount) << unsigned(ERT->fields_size());
- return;
- }
- // Note that we are deliberately NOT comparing layout properties
- // (such as Field offsets and sizes, or Record allocation size);
- // we need to tolerate layout differences between 32-bit
- // compilation and 64-bit compilation.
- unsigned FieldIdx = 0;
- for (RSExportRecordType::const_field_iterator I = ERT->fields_begin(), E = ERT->fields_end();
- I != E; I++, FieldIdx++) {
- const RSExportRecordType::Field &FieldExport = **I;
- const File::Record::Field &FieldState = record.mFields[FieldIdx];
- if (FieldState.mName != FieldExport.getName()) {
- mRSC->ReportError(ERT->getLocation(),
- "%ordinal0 field of exported structure '%1' "
- "is '%2' for 32-bit targets "
- "but '%3' for 64-bit targets")
- << (FieldIdx + 1) << ERT->getName() << FieldState.mName << FieldExport.getName();
- return;
- }
- const std::string FieldExportType = getUniqueTypeName(FieldExport.getType());
- if (!FieldState.mType.equals(FieldExportType)) {
- mRSC->ReportError(ERT->getLocation(),
- "field '%0' of exported structure '%1' "
- "has type '%2' for 32-bit targets "
- "but type '%3' for 64-bit targets")
- << FieldState.mName << ERT->getName() << FieldState.mType.str() << FieldExportType;
- }
- }
- }
- }
- ReflectionState::Record32
- ReflectionState::getRecord32(const RSExportRecordType *ERT) {
- if (isUsing()) {
- const auto &Records = mFiles.Current().mRecords;
- const auto RIT = Records.find(ERT->getName());
- if (RIT != Records.end())
- return Record32(&RIT->getValue());
- }
- return Record32();
- }
- // Reduce //////////////////////////////////////////////////////////////////////////////////////
- void ReflectionState::declareReduce(const RSExportReduce *ER, bool IsExportable) {
- slangAssert(!isClosed());
- if (!isActive())
- return;
- auto &reduces = mFiles.Current().mReduces;
- if (isCollecting()) {
- auto &reduce = reduces.CollectNext();
- reduce.mName = ER->getNameReduce();
- const auto &InTypes = ER->getAccumulatorInTypes();
- const size_t InTypesSize = InTypes.size();
- reduce.mAccumInCount = InTypesSize;
- reduce.mAccumIns = new llvm::StringRef[InTypesSize];
- unsigned InTypesIdx = 0;
- for (const auto &InType : InTypes)
- reduce.mAccumIns[InTypesIdx++] = canon(getUniqueTypeName(InType));
- reduce.mResult = canon(getUniqueTypeName(ER->getResultType()));
- reduce.mIsExportable = IsExportable;
- }
- if (isUsing()) {
- if (mReducesOrderFatal)
- return;
- const std::string& Name = ER->getNameReduce();
- if (reduces.isFinished()) {
- // This doesn't actually break reflection, but that's a
- // coincidence of the fact that we reflect during the 64-bit
- // compilation pass rather than the 32-bit compilation pass, and
- // of the fact that the "extra" reduction kernel(s) are at the
- // end.
- mRSC->ReportError(ER->getLocation(),
- "reduction kernel '%0' is only present for 64-bit targets")
- << Name;
- return;
- }
- auto &reduce = reduces.UseNext();
- if (reduce.mName != Name) {
- // Order matters because it determines slot number. We might be
- // able to tolerate certain cases if we ignore non-exportable
- // kernels in the two sequences (32-bit and 64-bit) -- non-exportable
- // kernels do not take up slot numbers.
- mRSC->ReportError(ER->getLocation(),
- "%ordinal0 reduction kernel is '%1' for 32-bit targets "
- "but '%2' for 64-bit targets")
- << unsigned(reduces.CurrentIdx() + 1)
- << reduce.mName
- << Name;
- mReducesOrderFatal = true;
- return;
- }
- // If at least one of the two kernels (32-bit or 64-bit) is not
- // exporable, then there will be no reflection for that kernel,
- // and so any mismatch in result type or in inputs is irrelevant.
- // However, we may make more kernels exportable in the future.
- // Therefore, we'll forbid mismatches anyway.
- if (reduce.mIsExportable != IsExportable) {
- mRSC->ReportError(ER->getLocation(),
- "reduction kernel '%0' is reflected in Java only for %select{32|64}1-bit targets")
- << reduce.mName
- << IsExportable;
- }
- const std::string ResultType = getUniqueTypeName(ER->getResultType());
- if (!reduce.mResult.equals(ResultType)) {
- mRSC->ReportError(ER->getLocation(),
- "reduction kernel '%0' has result type '%1' for 32-bit targets "
- "but result type '%2' for 64-bit targets")
- << reduce.mName << reduce.mResult.str() << ResultType;
- }
- const auto &InTypes = ER->getAccumulatorInTypes();
- if (reduce.mAccumInCount != InTypes.size()) {
- mRSC->ReportError(ER->getLocation(),
- "reduction kernel '%0' has %1 input%s1 for 32-bit targets "
- "but %2 input%s2 for 64-bit targets")
- << Name << unsigned(reduce.mAccumInCount) << unsigned(InTypes.size());
- return;
- }
- unsigned FieldIdx = 0;
- for (const auto &InType : InTypes) {
- const std::string InTypeName = getUniqueTypeName(InType);
- const llvm::StringRef StateInTypeName = reduce.mAccumIns[FieldIdx++];
- if (!StateInTypeName.equals(InTypeName)) {
- mRSC->ReportError(ER->getLocation(),
- "%ordinal0 input of reduction kernel '%1' "
- "has type '%2' for 32-bit targets "
- "but type '%3' for 64-bit targets")
- << FieldIdx
- << Name
- << StateInTypeName.str()
- << InTypeName;
- }
- }
- }
- }
- void ReflectionState::endReduces() {
- if (!isUsing() || mReducesOrderFatal)
- return;
- auto &reduces = mFiles.Current().mReduces;
- while (!reduces.isFinished()) {
- const auto &reduce = reduces.UseNext();
- mRSC->ReportError("in file '%0' reduction kernel '%1' is only present for 32-bit targets")
- << mFiles.Current().mRSSourceFileName << reduce.mName;
- }
- }
- // Variable ////////////////////////////////////////////////////////////////////////////////////
- // Keep this in sync with initialization handling in
- // RSReflectionJava::genScriptClassConstructor().
- ReflectionState::Val32 ReflectionState::declareVariable(const RSExportVar *EV) {
- slangAssert(!isClosed());
- if (!isActive())
- return NoVal32();
- auto &variables = mFiles.Current().mVariables;
- if (isCollecting()) {
- auto &variable = variables.CollectNext();
- variable.mName = EV->getName();
- variable.mType = canon(getUniqueTypeName(EV->getType()));
- variable.mAllocSize = EV->getType()->getAllocSize();
- variable.mIsConst = EV->isConst();
- if (!EV->getInit().isUninit()) {
- variable.mInitializerCount = 1;
- variable.mInitializers = new clang::APValue[1];
- variable.mInitializers[0] = EV->getInit();
- } else if (EV->getArraySize()) {
- variable.mInitializerCount = EV->getNumInits();
- variable.mInitializers = new clang::APValue[variable.mInitializerCount];
- for (size_t i = 0; i < variable.mInitializerCount; ++i)
- variable.mInitializers[i] = EV->getInitArray(i);
- } else {
- variable.mInitializerCount = 0;
- }
- return NoVal32();
- }
- /*-- isUsing() -----------------------------------------------------------*/
- slangAssert(isUsing());
- if (mVariablesOrderFatal)
- return NoVal32();
- if (variables.isFinished()) {
- // This doesn't actually break reflection, but that's a
- // coincidence of the fact that we reflect during the 64-bit
- // compilation pass rather than the 32-bit compilation pass, and
- // of the fact that the "extra" variable(s) are at the end.
- mRSC->ReportError(EV->getLocation(), "global variable '%0' is only present for 64-bit targets")
- << EV->getName();
- return NoVal32();
- }
- const auto &variable = variables.UseNext();
- if (variable.mName != EV->getName()) {
- // Order matters because it determines slot number
- mRSC->ReportError(EV->getLocation(),
- "%ordinal0 global variable is '%1' for 32-bit targets "
- "but '%2' for 64-bit targets")
- << unsigned(variables.CurrentIdx() + 1)
- << variable.mName
- << EV->getName();
- mVariablesOrderFatal = true;
- return NoVal32();
- }
- const std::string TypeName = getUniqueTypeName(EV->getType());
- if (!variable.mType.equals(TypeName)) {
- mRSC->ReportError(EV->getLocation(),
- "global variable '%0' has type '%1' for 32-bit targets "
- "but type '%2' for 64-bit targets")
- << EV->getName()
- << variable.mType.str()
- << TypeName;
- return NoVal32();
- }
- if (variable.mIsConst != EV->isConst()) {
- mRSC->ReportError(EV->getLocation(),
- "global variable '%0' has inconsistent 'const' qualification "
- "between 32-bit targets and 64-bit targets")
- << EV->getName();
- return NoVal32();
- }
- // NOTE: Certain syntactically different but semantically
- // equivalent initialization patterns are unnecessarily rejected
- // as errors.
- //
- // Background:
- //
- // . A vector initialized with a scalar value is treated
- // by reflection as if all elements of the vector are
- // initialized with the scalar value.
- // . A vector may be initialized with a vector of greater
- // length; reflection ignores the extra initializers.
- // . If only the beginning of a vector is explicitly
- // initialized, reflection treats it as if trailing elements are
- // initialized to zero (by issuing explicit assignments to those
- // trailing elements).
- // . If only the beginning of an array is explicitly initialized,
- // reflection treats it as if trailing elements are initialized
- // to zero (by Java rules for newly-created arrays).
- //
- // Unnecessarily rejected as errors:
- //
- // . One compile initializes a vector with a scalar, and
- // another initializes it with a vector whose elements
- // are the scalar, as in
- //
- // int2 x =
- // #ifdef __LP64__
- // 1
- // #else
- // { 1, 1 }
- // #endif
- //
- // . Compiles initialize a vector with vectors of different
- // lengths, but the initializers agree up to the length
- // of the variable being initialized, as in
- //
- // int2 x = { 1, 2
- // #ifdef __LP64__
- // 3
- // #else
- // 4
- // #endif
- // };
- //
- // . Two compiles agree with the initializer for a vector or
- // array, except that one has some number of explicit trailing
- // zeroes, as in
- //
- // int x[4] = { 3, 2, 1
- // #ifdef __LP64__
- // , 0
- // #endif
- // };
- bool MismatchedInitializers = false;
- if (!EV->getInit().isUninit()) {
- // Use phase has a scalar initializer.
- // Make sure that Collect phase had a matching scalar initializer.
- if ((variable.mInitializerCount != 1) ||
- !equal(variable.mInitializers[0], EV->getInit()))
- MismatchedInitializers = true;
- } else if (EV->getArraySize()) {
- const size_t UseSize = EV->getNumInits();
- if (variable.mInitializerCount != UseSize)
- MismatchedInitializers = true;
- else {
- for (int i = 0; i < UseSize; ++i)
- if (!equal(variable.mInitializers[i], EV->getInitArray(i))) {
- MismatchedInitializers = true;
- break;
- }
- }
- } else if (variable.mInitializerCount != 0) {
- // Use phase does not have a scalar initializer, variable is not
- // an array, and Collect phase has an initializer. This is an error.
- MismatchedInitializers = true;
- }
- if (MismatchedInitializers) {
- mRSC->ReportError(EV->getLocation(),
- "global variable '%0' is initialized differently for 32-bit targets "
- "than for 64-bit targets")
- << EV->getName();
- return NoVal32();
- }
- return Val32(true, variable.mAllocSize);
- }
- void ReflectionState::endVariables() {
- if (!isUsing() || mVariablesOrderFatal)
- return;
- auto &variables = mFiles.Current().mVariables;
- while (!variables.isFinished()) {
- const auto &variable = variables.UseNext();
- mRSC->ReportError("in file '%0' global variable '%1' is only present for 32-bit targets")
- << mFiles.Current().mRSSourceFileName << variable.mName;
- }
- }
- } // namespace slang
|