| |
| |
| //===- Interfaces.cpp - C Interface for MLIR Interfaces -------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "mlir-c/Interfaces.h" |
| |
| #include "mlir/CAPI/IR.h" |
| #include "mlir/CAPI/Interfaces.h" |
| #include "mlir/CAPI/Support.h" |
| #include "mlir/CAPI/Wrap.h" |
| #include "mlir/IR/ValueRange.h" |
| #include "mlir/Interfaces/InferTypeOpInterface.h" |
| #include "mlir/Interfaces/SideEffectInterfaces.h" |
| #include "llvm/ADT/ScopeExit.h" |
| #include <optional> |
| |
| using namespace mlir; |
| |
| namespace { |
| |
| std::optional<RegisteredOperationName> |
| getRegisteredOperationName(MlirContext context, MlirStringRef opName) { |
| StringRef name(opName.data, opName.length); |
| std::optional<RegisteredOperationName> info = |
| RegisteredOperationName::lookup(name, unwrap(context)); |
| return info; |
| } |
| |
| std::optional<Location> maybeGetLocation(MlirLocation location) { |
| std::optional<Location> maybeLocation; |
| if (!mlirLocationIsNull(location)) |
| maybeLocation = unwrap(location); |
| return maybeLocation; |
| } |
| |
| SmallVector<Value> unwrapOperands(intptr_t nOperands, MlirValue *operands) { |
| SmallVector<Value> unwrappedOperands; |
| (void)unwrapList(nOperands, operands, unwrappedOperands); |
| return unwrappedOperands; |
| } |
| |
| DictionaryAttr unwrapAttributes(MlirAttribute attributes) { |
| DictionaryAttr attributeDict; |
| if (!mlirAttributeIsNull(attributes)) |
| attributeDict = llvm::cast<DictionaryAttr>(unwrap(attributes)); |
| return attributeDict; |
| } |
| |
| SmallVector<std::unique_ptr<Region>> unwrapRegions(intptr_t nRegions, |
| MlirRegion *regions) { |
| // Create a vector of unique pointers to regions and make sure they are not |
| // deleted when exiting the scope. This is a hack caused by C++ API expecting |
| // an list of unique pointers to regions (without ownership transfer |
| // semantics) and C API making ownership transfer explicit. |
| SmallVector<std::unique_ptr<Region>> unwrappedRegions; |
| unwrappedRegions.reserve(nRegions); |
| for (intptr_t i = 0; i < nRegions; ++i) |
| unwrappedRegions.emplace_back(unwrap(*(regions + i))); |
| llvm::scope_exit cleaner([&]() { |
| for (auto ®ion : unwrappedRegions) |
| region.release(); |
| }); |
| return unwrappedRegions; |
| } |
| |
| } // namespace |
| |
| bool mlirOperationImplementsInterface(MlirOperation operation, |
| MlirTypeID interfaceTypeID) { |
| std::optional<RegisteredOperationName> info = |
| unwrap(operation)->getRegisteredInfo(); |
| return info && info->hasInterface(unwrap(interfaceTypeID)); |
| } |
| |
| bool mlirOperationImplementsInterfaceStatic(MlirStringRef operationName, |
| MlirContext context, |
| MlirTypeID interfaceTypeID) { |
| std::optional<RegisteredOperationName> info = RegisteredOperationName::lookup( |
| StringRef(operationName.data, operationName.length), unwrap(context)); |
| return info && info->hasInterface(unwrap(interfaceTypeID)); |
| } |
| |
| MlirTypeID mlirInferTypeOpInterfaceTypeID() { |
| return wrap(InferTypeOpInterface::getInterfaceID()); |
| } |
| |
| MlirLogicalResult mlirInferTypeOpInterfaceInferReturnTypes( |
| MlirStringRef opName, MlirContext context, MlirLocation location, |
| intptr_t nOperands, MlirValue *operands, MlirAttribute attributes, |
| void *properties, intptr_t nRegions, MlirRegion *regions, |
| MlirTypesCallback callback, void *userData) { |
| StringRef name(opName.data, opName.length); |
| std::optional<RegisteredOperationName> info = |
| getRegisteredOperationName(context, opName); |
| if (!info) |
| return mlirLogicalResultFailure(); |
| |
| std::optional<Location> maybeLocation = maybeGetLocation(location); |
| SmallVector<Value> unwrappedOperands = unwrapOperands(nOperands, operands); |
| DictionaryAttr attributeDict = unwrapAttributes(attributes); |
| SmallVector<std::unique_ptr<Region>> unwrappedRegions = |
| unwrapRegions(nRegions, regions); |
| |
| SmallVector<Type> inferredTypes; |
| // The C API passes an opaque void*; we trust the caller to pass the correct |
| // properties type for this operation. |
| // TODO: Create a C API that's more type-safe. |
| PropertyRef propertyRef = |
| properties ? PropertyRef(info->getOpPropertiesTypeID(), properties) |
| : PropertyRef(); |
| if (failed(info->getInterface<InferTypeOpInterface>()->inferReturnTypes( |
| unwrap(context), maybeLocation, unwrappedOperands, attributeDict, |
| propertyRef, unwrappedRegions, inferredTypes))) |
| return mlirLogicalResultFailure(); |
| |
| SmallVector<MlirType> wrappedInferredTypes; |
| wrappedInferredTypes.reserve(inferredTypes.size()); |
| for (Type t : inferredTypes) |
| wrappedInferredTypes.push_back(wrap(t)); |
| callback(wrappedInferredTypes.size(), wrappedInferredTypes.data(), userData); |
| return mlirLogicalResultSuccess(); |
| } |
| |
| MlirTypeID mlirInferShapedTypeOpInterfaceTypeID() { |
| return wrap(InferShapedTypeOpInterface::getInterfaceID()); |
| } |
| |
| MlirLogicalResult mlirInferShapedTypeOpInterfaceInferReturnTypes( |
| MlirStringRef opName, MlirContext context, MlirLocation location, |
| intptr_t nOperands, MlirValue *operands, MlirAttribute attributes, |
| void *properties, intptr_t nRegions, MlirRegion *regions, |
| MlirShapedTypeComponentsCallback callback, void *userData) { |
| std::optional<RegisteredOperationName> info = |
| getRegisteredOperationName(context, opName); |
| if (!info) |
| return mlirLogicalResultFailure(); |
| |
| std::optional<Location> maybeLocation = maybeGetLocation(location); |
| SmallVector<Value> unwrappedOperands = unwrapOperands(nOperands, operands); |
| DictionaryAttr attributeDict = unwrapAttributes(attributes); |
| SmallVector<std::unique_ptr<Region>> unwrappedRegions = |
| unwrapRegions(nRegions, regions); |
| |
| SmallVector<ShapedTypeComponents> inferredTypeComponents; |
| // The C API passes an opaque void*; we trust the caller to pass the correct |
| // properties type for this operation. |
| PropertyRef propertyRef = |
| properties ? PropertyRef(info->getOpPropertiesTypeID(), properties) |
| : PropertyRef(); |
| if (failed(info->getInterface<InferShapedTypeOpInterface>() |
| ->inferReturnTypeComponents( |
| unwrap(context), maybeLocation, |
| mlir::ValueRange(llvm::ArrayRef(unwrappedOperands)), |
| attributeDict, propertyRef, unwrappedRegions, |
| inferredTypeComponents))) |
| return mlirLogicalResultFailure(); |
| |
| bool hasRank; |
| intptr_t rank; |
| const int64_t *shapeData; |
| for (const ShapedTypeComponents &t : inferredTypeComponents) { |
| if (t.hasRank()) { |
| hasRank = true; |
| rank = t.getDims().size(); |
| shapeData = t.getDims().data(); |
| } else { |
| hasRank = false; |
| rank = 0; |
| shapeData = nullptr; |
| } |
| callback(hasRank, rank, shapeData, wrap(t.getElementType()), |
| wrap(t.getAttribute()), userData); |
| } |
| return mlirLogicalResultSuccess(); |
| } |
| |
| //===---------------------------------------------------------------------===// |
| // ConditionallySpeculatable |
| //===---------------------------------------------------------------------===// |
| |
| MlirTypeID mlirConditionallySpeculatableOpInterfaceTypeID() { |
| return wrap(ConditionallySpeculatable::getInterfaceID()); |
| } |
| |
| /// Fallback model for the ConditionallySpeculatable interface that uses C API |
| /// callbacks. |
| class ConditionallySpeculatableOpInterfaceFallbackModel |
| : public mlir::ConditionallySpeculatable::FallbackModel< |
| ConditionallySpeculatableOpInterfaceFallbackModel> { |
| public: |
| /// Sets the callbacks that this FallbackModel will use. |
| /// NB: the callbacks can only be set through this method as the |
| /// RegisteredOperationName::attachInterface mechanism default-constructs |
| /// the FallbackModel without being able to provide arguments. |
| void |
| setCallbacks(MlirConditionallySpeculatableOpInterfaceCallbacks callbacks) { |
| this->callbacks = callbacks; |
| } |
| |
| ~ConditionallySpeculatableOpInterfaceFallbackModel() { |
| if (callbacks.destruct) |
| callbacks.destruct(callbacks.userData); |
| } |
| |
| static TypeID getInterfaceID() { |
| return ConditionallySpeculatable::getInterfaceID(); |
| } |
| |
| static bool classof(const mlir::ConditionallySpeculatable::Concept *op) { |
| // Enable casting back to the FallbackModel from the Interface. This is |
| // necessary as attachInterface(...) default-constructs the FallbackModel |
| // without being able to pass in the callbacks and returns just the Concept. |
| return true; |
| } |
| |
| Speculation::Speculatability getSpeculatability(Operation *op) const { |
| assert(callbacks.getSpeculatability && |
| "getSpeculatability callback not set"); |
| |
| switch (callbacks.getSpeculatability(wrap(op), callbacks.userData)) { |
| case MlirSpeculatabilityNotSpeculatable: |
| return Speculation::NotSpeculatable; |
| case MlirSpeculatabilitySpeculatable: |
| return Speculation::Speculatable; |
| case MlirSpeculatabilityRecursivelySpeculatable: |
| return Speculation::RecursivelySpeculatable; |
| } |
| llvm_unreachable("unknown speculatability"); |
| } |
| |
| private: |
| MlirConditionallySpeculatableOpInterfaceCallbacks callbacks; |
| }; |
| |
| /// Attach a ConditionallySpeculatable FallbackModel to the given named op. |
| /// The FallbackModel uses the provided callbacks to implement the interface. |
| void mlirConditionallySpeculatableOpInterfaceAttachFallbackModel( |
| MlirContext ctx, MlirStringRef opName, |
| MlirConditionallySpeculatableOpInterfaceCallbacks callbacks) { |
| // Look up the operation definition in the context. |
| std::optional<RegisteredOperationName> opInfo = |
| RegisteredOperationName::lookup(unwrap(opName), unwrap(ctx)); |
| |
| assert(opInfo.has_value() && "operation not found in context"); |
| |
| // NB: the following default-constructs the FallbackModel _without_ being able |
| // to provide arguments. |
| opInfo->attachInterface<ConditionallySpeculatableOpInterfaceFallbackModel>(); |
| // Cast to get the underlying FallbackModel and set the callbacks. |
| auto *model = cast<ConditionallySpeculatableOpInterfaceFallbackModel>( |
| opInfo |
| ->getInterface<ConditionallySpeculatableOpInterfaceFallbackModel>()); |
| assert(model && |
| "Failed to get ConditionallySpeculatableOpInterfaceFallbackModel"); |
| model->setCallbacks(callbacks); |
| } |
| |
| MlirSpeculatability mlirConditionallySpeculatableOpInterfaceGetSpeculatability( |
| MlirOperation operation) { |
| auto iface = dyn_cast<ConditionallySpeculatable>(unwrap(operation)); |
| assert(iface && "operation does not implement ConditionallySpeculatable"); |
| |
| switch (iface.getSpeculatability()) { |
| case Speculation::NotSpeculatable: |
| return MlirSpeculatabilityNotSpeculatable; |
| case Speculation::Speculatable: |
| return MlirSpeculatabilitySpeculatable; |
| case Speculation::RecursivelySpeculatable: |
| return MlirSpeculatabilityRecursivelySpeculatable; |
| } |
| llvm_unreachable("unknown speculatability"); |
| } |
| |
| //===---------------------------------------------------------------------===// |
| // MemoryEffectOpInterface |
| //===---------------------------------------------------------------------===// |
| |
| MlirMemoryEffect mlirMemoryEffectsAllocateGet() { |
| return wrap( |
| static_cast<MemoryEffects::Effect *>(MemoryEffects::Allocate::get())); |
| } |
| |
| MlirMemoryEffect mlirMemoryEffectsFreeGet() { |
| return wrap(static_cast<MemoryEffects::Effect *>(MemoryEffects::Free::get())); |
| } |
| |
| MlirMemoryEffect mlirMemoryEffectsReadGet() { |
| return wrap(static_cast<MemoryEffects::Effect *>(MemoryEffects::Read::get())); |
| } |
| |
| MlirMemoryEffect mlirMemoryEffectsWriteGet() { |
| return wrap( |
| static_cast<MemoryEffects::Effect *>(MemoryEffects::Write::get())); |
| } |
| |
| MlirTypeID mlirMemoryEffectGetEffectID(MlirMemoryEffect effect) { |
| return wrap(unwrap(effect)->getEffectID()); |
| } |
| |
| MlirSideEffectResource mlirSideEffectsDefaultResourceGet() { |
| return wrap(static_cast<SideEffects::Resource *>( |
| SideEffects::DefaultResource::get())); |
| } |
| |
| MlirMemoryEffectInstance mlirMemoryEffectInstanceCreate( |
| MlirMemoryEffect effect, MlirAttribute parameters, int stage, |
| bool effectOnFullRegion, MlirSideEffectResource resource) { |
| return wrap(new MemoryEffects::EffectInstance( |
| unwrap(effect), unwrap(parameters), stage, effectOnFullRegion, |
| unwrap(resource))); |
| } |
| |
| MlirMemoryEffectInstance mlirMemoryEffectInstanceCreateForOpOperand( |
| MlirMemoryEffect effect, MlirOpOperand opOperand, MlirAttribute parameters, |
| int stage, bool effectOnFullRegion, MlirSideEffectResource resource) { |
| return wrap(new MemoryEffects::EffectInstance( |
| unwrap(effect), unwrap(opOperand), unwrap(parameters), stage, |
| effectOnFullRegion, unwrap(resource))); |
| } |
| |
| MlirMemoryEffectInstance mlirMemoryEffectInstanceCreateForOpResult( |
| MlirMemoryEffect effect, MlirValue result, MlirAttribute parameters, |
| int stage, bool effectOnFullRegion, MlirSideEffectResource resource) { |
| return wrap(new MemoryEffects::EffectInstance( |
| unwrap(effect), cast<OpResult>(unwrap(result)), unwrap(parameters), stage, |
| effectOnFullRegion, unwrap(resource))); |
| } |
| |
| MlirMemoryEffectInstance mlirMemoryEffectInstanceCreateForBlockArgument( |
| MlirMemoryEffect effect, MlirValue blockArgument, MlirAttribute parameters, |
| int stage, bool effectOnFullRegion, MlirSideEffectResource resource) { |
| return wrap(new MemoryEffects::EffectInstance( |
| unwrap(effect), cast<BlockArgument>(unwrap(blockArgument)), |
| unwrap(parameters), stage, effectOnFullRegion, unwrap(resource))); |
| } |
| |
| MlirMemoryEffectInstance mlirMemoryEffectInstanceCreateForSymbol( |
| MlirMemoryEffect effect, MlirAttribute symbol, MlirAttribute parameters, |
| int stage, bool effectOnFullRegion, MlirSideEffectResource resource) { |
| return wrap(new MemoryEffects::EffectInstance( |
| unwrap(effect), cast<SymbolRefAttr>(unwrap(symbol)), unwrap(parameters), |
| stage, effectOnFullRegion, unwrap(resource))); |
| } |
| |
| void mlirMemoryEffectInstanceDestroy(MlirMemoryEffectInstance instance) { |
| delete unwrap(instance); |
| } |
| |
| MlirMemoryEffectInstance |
| mlirMemoryEffectInstanceClone(MlirMemoryEffectInstance instance) { |
| return wrap(new MemoryEffects::EffectInstance(*unwrap(instance))); |
| } |
| |
| MlirMemoryEffect |
| mlirMemoryEffectInstanceGetEffect(MlirMemoryEffectInstance instance) { |
| return wrap(unwrap(instance)->getEffect()); |
| } |
| |
| MlirSideEffectResource |
| mlirMemoryEffectInstanceGetResource(MlirMemoryEffectInstance instance) { |
| return wrap(unwrap(instance)->getResource()); |
| } |
| |
| int mlirMemoryEffectInstanceGetStage(MlirMemoryEffectInstance instance) { |
| return unwrap(instance)->getStage(); |
| } |
| |
| bool mlirMemoryEffectInstanceGetEffectOnFullRegion( |
| MlirMemoryEffectInstance instance) { |
| return unwrap(instance)->getEffectOnFullRegion(); |
| } |
| |
| MlirAttribute |
| mlirMemoryEffectInstanceGetParameters(MlirMemoryEffectInstance instance) { |
| return wrap(unwrap(instance)->getParameters()); |
| } |
| |
| MlirValue mlirMemoryEffectInstanceGetValue(MlirMemoryEffectInstance instance) { |
| return wrap(unwrap(instance)->getValue()); |
| } |
| |
| MlirAttribute |
| mlirMemoryEffectInstanceGetSymbolRef(MlirMemoryEffectInstance instance) { |
| return wrap(unwrap(instance)->getSymbolRef()); |
| } |
| |
| MlirTypeID mlirMemoryEffectsOpInterfaceTypeID() { |
| return wrap(MemoryEffectOpInterface::getInterfaceID()); |
| } |
| |
| /// Fallback model for the MemoryEffectsOpInterface that uses C API callbacks. |
| class MemoryEffectOpInterfaceFallbackModel |
| : public mlir::MemoryEffectOpInterface::FallbackModel< |
| MemoryEffectOpInterfaceFallbackModel> { |
| public: |
| /// Sets the callbacks that this FallbackModel will use. |
| /// NB: the callbacks can only be set through this method as the |
| /// RegisteredOperationName::attachInterface mechanism default-constructs |
| /// the FallbackModel without being able to provide arguments. |
| void setCallbacks(MlirMemoryEffectsOpInterfaceCallbacks callbacks) { |
| this->callbacks = callbacks; |
| } |
| |
| ~MemoryEffectOpInterfaceFallbackModel() { |
| if (callbacks.destruct) |
| callbacks.destruct(callbacks.userData); |
| } |
| |
| static TypeID getInterfaceID() { |
| return MemoryEffectOpInterface::getInterfaceID(); |
| } |
| |
| static bool classof(const mlir::MemoryEffectOpInterface::Concept *op) { |
| // Enable casting back to the FallbackModel from the Interface. This is |
| // necessary as attachInterface(...) default-constructs the FallbackModel |
| // without being able to pass in the callbacks and returns just the Concept. |
| return true; |
| } |
| |
| void |
| getEffects(Operation *op, |
| SmallVectorImpl<MemoryEffects::EffectInstance> &effects) const { |
| assert(callbacks.getEffects && "getEffects callback not set"); |
| callbacks.getEffects( |
| wrap(op), |
| [](intptr_t numEffects, MlirMemoryEffectInstance *effectInstances, |
| void *userData) { |
| auto *unwrappedEffects = |
| static_cast<SmallVectorImpl<MemoryEffects::EffectInstance> *>( |
| userData); |
| unwrappedEffects->reserve(unwrappedEffects->size() + numEffects); |
| for (intptr_t i = 0; i < numEffects; ++i) |
| unwrappedEffects->push_back(*unwrap(effectInstances[i])); |
| }, |
| &effects, callbacks.userData); |
| } |
| |
| private: |
| MlirMemoryEffectsOpInterfaceCallbacks callbacks; |
| }; |
| |
| /// Attach a MemoryEffectsOpInterface FallbackModel to the given named op. |
| /// The FallbackModel uses the provided callbacks to implement the interface. |
| void mlirMemoryEffectsOpInterfaceAttachFallbackModel( |
| MlirContext ctx, MlirStringRef opName, |
| MlirMemoryEffectsOpInterfaceCallbacks callbacks) { |
| // Look up the operation definition in the context |
| std::optional<RegisteredOperationName> opInfo = |
| RegisteredOperationName::lookup(unwrap(opName), unwrap(ctx)); |
| |
| assert(opInfo.has_value() && "operation not found in context"); |
| |
| // NB: the following default-constructs the FallbackModel _without_ being able |
| // to provide arguments. |
| opInfo->attachInterface<MemoryEffectOpInterfaceFallbackModel>(); |
| // Cast to get the underlying FallbackModel and set the callbacks. |
| auto *model = cast<MemoryEffectOpInterfaceFallbackModel>( |
| opInfo->getInterface<MemoryEffectOpInterfaceFallbackModel>()); |
| assert(model && "Failed to get MemoryEffectOpInterfaceFallbackModel"); |
| model->setCallbacks(callbacks); |
| } |
| |
| void mlirMemoryEffectsOpInterfaceGetEffects( |
| MlirOperation operation, MlirMemoryEffectInstancesCallback callback, |
| void *userData) { |
| auto iface = dyn_cast<MemoryEffectOpInterface>(unwrap(operation)); |
| assert(iface && "operation does not implement MemoryEffectOpInterface"); |
| |
| SmallVector<MemoryEffects::EffectInstance> effects; |
| iface.getEffects(effects); |
| SmallVector<MlirMemoryEffectInstance> wrappedEffects; |
| wrappedEffects.reserve(effects.size()); |
| for (MemoryEffects::EffectInstance &effect : effects) |
| wrappedEffects.push_back(wrap(&effect)); |
| callback(wrappedEffects.size(), wrappedEffects.data(), userData); |
| } |