如何给无法修改的类增加一个父类?mlir某机制

文章探讨了在编程中如何在不可修改的库中通过模板和Trait机制实现类的继承,特别是在MLIR库中的Op和Interface设计。重点介绍了Conv2dOp与Trait的关系,以及BackinferInterface和其Trait的继承结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.从这个问题说起

如果我们使用了一个库,这个库的代码是无法修改的,其中包含一个类class A。

我们在代码中定义了一个类B,如何让A继承自我的写的B呢?

二.奇怪的代码 :

class Conv2dOp : public ::mlir::Op<Conv2dOp, ::mlir::OpTrait::ZeroRegions>

在mlir中,当我们定义了一个Op,如Conv2d。通过tablegen,可以生成以上的代码。

上面的代码看上去比较奇怪,父类mlir::Op<>中有很多模板。

template <typename ConcreteType, template <typename T> class... Traits>

class Op : public OpState, public Traits<ConcreteType>... {

我们追溯一下,看看mlir::Op的定义。在Op的定义中,我们在子类(Conv2dOp中定义的Trait就变成了Op的父类)

通过这种模板的方式 , 我们将自己定义的类,传递到了mlir库中。 

三.mlir中的interface 

在了解了这个前置知识后, 我们来看一个更复杂的内容,mlir中的interface的实现

1.使用interface

td定义:

class Ka_Op<string mnemonic, list<Trait> traits = []> :

  Ka_BaseOp<mnemonic, !listconcat(traits,

    [DeclareOpInterfaceMethods<BackinferInterface>]

    )>;

使用:

if (auto bk = llvm::dyn_cast<BackinferInterface>(innerOp)) {

  bk.backinfer()

}

如上面所示, 当我们在td中定义一个Backinterface后,然后在op中”DeclareOpInterfaceMethods“在Op中,则可以在C++代码中将op转成BackinferInterface,并调用基接口

那么背后,mlir库为我们做了什么呢

2.Conv2d与Trait

class Conv2dOp : public ::mlir::Op<Conv2dOp, 省略很多, ::ka_compiler::BackinferInterface::Trait>

在”奇怪的代码“中我们讲过, 通过这种方式,Op可以继承自BackinferInterface::Trait

3.BackinferInterface::Trait的定义

class BackinferInterface : public ::mlir::OpInterface<BackinferInterface, detail::BackinferInterfaceInterfaceTraits> {

public:

  看这里---> struct Trait : public detail::BackinferInterfaceTrait<ConcreteOp> {};

  bool backinfer(llvm::DenseMap<mlir::Value, ka_compiler::RankedAxisData> & inputs, const llvm::DenseMap<mlir::Value, ka_compiler::RankedAxisData> & outputs);

};

可以看到Trait 继承自BackinferInterfaceTrait<ConcreteOp> 。我们再看BackinferInterfaceTrait<ConcreteOp> 的定义

4.BackinferInterfaceTraint<ConcreteOp>

struct BackinferInterfaceTrait : public ::mlir::OpInterface<BackinferInterface, detail::BackinferInterfaceInterfaceTraits>::Trait<ConcreteOp> {

};

可以看到BackinferInterfaceTrait 继承自OpInterface,并将BackinferInterface和通过模板的形式传递给了父类OpInterface

接下来看OpInterface

5.OpInterface

class OpInterface

    public detail::Interface<ConcreteType, Operation *, Traits,

                               Op<ConcreteType>, OpTrait::TraitBase> {

OpInterface继承自Interface,并将Op和Traint传递给了父类Interface

我们再看Interface

6.Interface

template <typename ConcreteType, typename ValueT, typename Traits,

          typename BaseType,

          template <typenametemplate <typenameclassclass BaseTrait>

class Interface : public BaseType {

可以看到Interface继承自BaseType ,而BaseType 则是由OpInterface的定义中传递 的Op<ConcreteType>,

ConcreteType也就是BackinferInterface 也就是说Interface最终继承了BackinferInterface 类

四。结论

以上追溯过程十分复杂,那么能不能简单理解 一下呢?

一个类,如果拥有了某种trait(继承),最终的结果是这个类将通过继承此Trait的方式获得Trait的能力

参考资源链接:[构建编译器:MLIR入门教程](https://wenku.youkuaiyun.com/doc/799rqn6yui?utm_source=wenku_answer2doc_content) 在探索编译器开发和优化时,MLIR(多级中间表示)是一个强大的工具,它允许开发者定义和操作特定领域的方言(Dialects)。为了深入理解如何使用MLIR定义一个新的方言并实现数组DSL的结构化优化,推荐阅读《构建编译器:MLIR入门教程》。 首先,我们需要了解MLIR的基本组成。MLIR的核心概念包括操作(Operations)、区域(Regions)和方言(Dialects)。方言是MLIR的扩展机制,它允许开发者定义特定领域的操作和表示。例如,在定义一个数组DSL方言时,我们可能会引入数组相关的操作,如数组初始化、访问和变换等。 定义方言后,我们需要创建一系列操作来表示DSL的语义。在MLIR中,操作是构建块,它们可以包含属性和操作数,并且可以按照需要将它们组织成区域。区域是操作列表的容器,它们允许复杂控制流和数据流结构的构建。 接下来,我们可以利用MLIR的转换(Transformation)框架来实现结构化优化。MLIR鼓励编写针对操作接口(Op Interfaces)的转换,这意味着优化可以对整个结构进行处理,而不是单独处理每个操作。这为编写更高效的编译器前端和后端提供了更大的灵活性。 例如,如果我们想要对数组DSL进行优化,我们可能会编写转换来优化数组索引计算,或者将多个连续的数组操作合并成一个更高效的单个操作。这些转换将被定义为操作接口,这样它们就可以在MLIR的任何方言中使用。 最后,我们需要理解如何将高级方言逐步降低到更低层次的方言,直到可以转换为LLVM IR,这是向硬件目标编译的常见步骤。在这个过程中,可能会涉及到方言转换、操作的折叠、循环展开等技术。 《构建编译器:MLIR入门教程》详细介绍了这些概念,并提供了一个实际的例子来展示如何实现一个简单的玩具语言,其中涵盖了从定义操作到方言转换的整个流程。通过学习这个教程,你将能够掌握MLIR的基础知识,并能够将这些知识应用于构建自己的编译器基础设施中。 参考资源链接:[构建编译器:MLIR入门教程](https://wenku.youkuaiyun.com/doc/799rqn6yui?utm_source=wenku_answer2doc_content)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值