/// Transform AccessedStorage from a callee into the caller context. If this is /// uniquely identified local storage, then return an invalid storage object. /// /// For correctness, AccessEnforcementOpts relies on all Argument access to /// either be mapped into the caller's context or marked as an unidentified /// access at the call site. /// /// Note: This does *not* map the storage index into the caller function's index /// range. (When the storage value doesn't need to be remapped, it returns the /// original storage value.) It's simpler to set the storage index later when it /// is actually added to the function's storageAccessSet. static StorageAccessInfo transformCalleeStorage(const StorageAccessInfo &storage, FullApplySite fullApply) { switch (storage.getKind()) { case AccessedStorage::Box: case AccessedStorage::Stack: // Do not merge local storage. return StorageAccessInfo(AccessedStorage(), storage); case AccessedStorage::Global: // Global accesses is universal. return storage; case AccessedStorage::Class: { // If the object's value is an argument, translate it into a value on the // caller side. SILValue obj = storage.getObjectProjection().getObject(); if (auto *arg = dyn_cast<SILFunctionArgument>(obj)) { SILValue argVal = getCallerArg(fullApply, arg->getIndex()); if (argVal) { auto &proj = storage.getObjectProjection().getProjection(); // Remap the argument source value and inherit the old storage info. return StorageAccessInfo(AccessedStorage(argVal, proj), storage); } } // Otherwise, continue to reference the value in the callee because we don't // have any better placeholder for a callee-defined object. return storage; } case AccessedStorage::Argument: { // Transitively search for the storage base in the caller. SILValue argVal = getCallerArg(fullApply, storage.getParamIndex()); if (argVal) { // Remap the argument source value and inherit the old storage info. auto calleeStorage = findAccessedStorageNonNested(argVal); if (calleeStorage) return StorageAccessInfo(calleeStorage, storage); } // If the argument can't be transformed, demote it to an unidentified // access. return StorageAccessInfo( AccessedStorage(storage.getValue(), AccessedStorage::Unidentified), storage); } case AccessedStorage::Nested: llvm_unreachable("Unexpected nested access"); case AccessedStorage::Yield: // Continue to hold on to yields from the callee because we don't have // any better placeholder in the callee. return storage; case AccessedStorage::Unidentified: // For unidentified storage, continue to reference the value in the callee // because we don't have any better placeholder for a callee-defined object. return storage; } }
/// Transform AccessedStorage from a callee into the caller context. If this is /// uniquely identified local storage, then return an invalid storage object. /// /// For correctness, AccessEnforcementOpts relies on all Argument access to /// either be mapped into the caller's context or marked as an unidentified /// access at the call site. /// /// Note: This does *not* map the storage index into the caller function's index /// range. (When the storage value doesn't need to be remapped, it returns the /// original storage value.) It's simpler to set the storage index later when it /// is actually added to the function's storageAccessSet. static StorageAccessInfo transformCalleeStorage(const StorageAccessInfo &storage, FullApplySite fullApply) { switch (storage.getKind()) { case AccessedStorage::Box: case AccessedStorage::Stack: // Do not merge local storage. return StorageAccessInfo(AccessedStorage(), storage); case AccessedStorage::Global: // Global accesses is universal. return storage; case AccessedStorage::Class: { // If the object's value is an argument, translate it into a value on the // caller side. SILValue obj = storage.getObjectProjection().getObject(); if (auto *arg = dyn_cast<SILFunctionArgument>(obj)) { SILValue argVal = getCallerArg(fullApply, arg->getIndex()); if (argVal) { auto *instr = storage.getObjectProjection().getInstr(); // Remap the argument source value and inherit the old storage info. return StorageAccessInfo(AccessedStorage(argVal, instr), storage); } } // Otherwise, continue to reference the value in the callee because we don't // have any better placeholder for a callee-defined object. return storage; } case AccessedStorage::Argument: { // Transitively search for the storage base in the caller. SILValue argVal = getCallerArg(fullApply, storage.getParamIndex()); if (argVal) { // Remap the argument source value and inherit the old storage info. auto calleeStorage = findAccessedStorageNonNested(argVal); if (calleeStorage) return StorageAccessInfo(calleeStorage, storage); } // If the argument can't be transformed, demote it to an unidentified // access. // // This is an untested bailout. It is only reachable if the call graph // contains an edge that getCallerArg is unable to analyze OR if // findAccessedStorageNonNested returns an invalid SILValue, which won't // pass SIL verification. // // FIXME: In case argVal is invalid, support Unidentified access for invalid // values. This would also be useful for partially invalidating results. return StorageAccessInfo( AccessedStorage(argVal, AccessedStorage::Unidentified), storage); } case AccessedStorage::Nested: llvm_unreachable("Unexpected nested access"); case AccessedStorage::Yield: // Continue to hold on to yields from the callee because we don't have // any better placeholder in the callee. return storage; case AccessedStorage::Unidentified: // For unidentified storage, continue to reference the value in the callee // because we don't have any better placeholder for a callee-defined object. return storage; } llvm_unreachable("unhandled kind"); }