mlir toy 教程(1)

本文翻译自MLIR 官网 TOY教程,附带个人理解与批注,内容仅用于个人学习与记录

Chapter 1: Toy Language and AST

第一章:Toy语言与AST

注:ast为抽象语法树,这里需要有编译原理的基础,至少要知道各个组件是做什么的

This tutorial will be illustrated with a toy language that we’ll call “Toy” (naming is hard…). Toy is a tensor-based language that allows you to define functions, perform some math computation, and print results.
该教程会使用一个叫做"toy"的编程语言作为示例。toy是一个tensor-based的语言,允许用户定义函数,执行一些数学计算并输出结果。

注:tensor-based顾名思义,就是一套用于张量运算的语言,张量运算在深度学习中有大量使用,MLIR的诞生就是为了优化张量运算而来的

Given that we want to keep things simple, the codegen will be limited to tensors of rank <= 2, and the only datatype in Toy is a 64-bit floating point type (aka ‘double’ in C parlance). As such, all values are implicitly double precision, Values are immutable (i.e. every operation returns a newly allocated value), and deallocation is automatically managed. But enough with the long description; nothing is better than walking through an example to get a better understanding:

为了使用于学习的示例简单易懂,codegen会限制为一个rank <=2的tensors,并且toy的数据类型只有fp64类型(c语言中的double类型)。如此一来,所有的值类型都是隐式的表示为双精度类型。
所有的值类型都是不可变的(immutable)(例如每个操作都会返回一个新申请的变量),并且变量会被自动回收。多说无益,自己动手搞一遍比啥都强。(真理)

注:codegen用中文来解释,就是代码生成的意思,也是一种开发中常用的技术手段,可以根据固定的格式生成源代码,比如caffe里面使用的哪个protobuf

看下面一段代码:

def main() {
  # Define a variable `a` with shape <2, 3>, initialized with the literal value.
  # 定义一个形状为<2,3>的变量a,并对其用字面值初始化
  # The shape is inferred from the supplied literal.
  # 变量的形状可以通过字面值推倒出来(这句不是废话,见下面)
  var a = [[1, 2, 3], [4, 5, 6]];

  # b is identical to a, the literal tensor is implicitly reshaped: defining new
  # variables is the way to reshape tensors (element count must match).
  # b和a纬度一样,不过它的形状会被隐式的推到出来。将一个tensor进行reshape操作的办法之一就是定义一个新变量(注意元素数量要匹配上)
  var b<2, 3> = [1, 2, 3, 4, 5, 6];

  # transpose() and print() are the only builtin, the following will transpose
  # transpose()print() 操作是builtin的方法,接下来的操作会对a和b进行transpose操作,并执行一个element-wise的乘法,在打印最后的结果
  # a and b and perform a element-wise multiplication before printing the result.
  print(transpose(a) * transpose(b));
}

注:builtin中文是内建的,一般是系统提供的高性能函数或系统自带的函数文中指的是后者

AST

将上面代码的ast得到的结果dump出来,如下:

Module:
  Function 
    Proto 'multiply_transpose' @test/Examples/Toy/Ch1/ast.toy:4:1'
    Params: [a, b]
    Block {
      Return
        BinOp: * @test/Examples/Toy/Ch1/ast.toy:5:25
          Call 'transpose' [ @test/Examples/Toy/Ch1/ast.toy:5:10
            var: a @test/Examples/Toy/Ch1/ast.toy:5:20
          ]
          Call 'transpose' [ @test/Examples/Toy/Ch1/ast.toy:5:25
            var: b @test/Examples/Toy/Ch1/ast.toy:5:35
          ]
    } // Block
  Function 
    Proto 'main' @test/Examples/Toy/Ch1/ast.toy:8:1'
    Params: []
    Block {
      VarDecl a<> @test/Examples/Toy/Ch1/ast.toy:11:3
        Literal: <2, 3>[ <3>[ 1.000000e+00, 2.000000e+00, 3.000000e+00], <3>[ 4.000000e+00, 5.000000e+00, 6.000000e+00]] @test/Examples/Toy/Ch1/ast.toy:11:11
      VarDecl b<2, 3> @test/Examples/Toy/Ch1/ast.toy:15:3
        Literal: <6>[ 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00] @test/Examples/Toy/Ch1/ast.toy:15:17
      VarDecl c<> @test/Examples/Toy/Ch1/ast.toy:19:3
        Call 'multiply_transpose' [ @test/Examples/Toy/Ch1/ast.toy:19:11
          var: a @test/Examples/Toy/Ch1/ast.toy:19:30
          var: b @test/Examples/Toy/Ch1/ast.toy:19:33
        ]
      VarDecl d<> @test/Examples/Toy/Ch1/ast.toy:22:3
        Call 'multiply_transpose' [ @test/Examples/Toy/Ch1/ast.toy:22:11
          var: b @test/Examples/Toy/Ch1/ast.toy:22:30
          var: a @test/Examples/Toy/Ch1/ast.toy:22:33
        ]
      VarDecl e<> @test/Examples/Toy/Ch1/ast.toy:25:3
        Call 'multiply_transpose' [ @test/Examples/Toy/Ch1/ast.toy:25:11
          var: b @test/Examples/Toy/Ch1/ast.toy:25:30
          var: c @test/Examples/Toy/Ch1/ast.toy:25:33
        ]
      VarDecl f<> @test/Examples/Toy/Ch1/ast.toy:28:3
        Call 'multiply_transpose' [ @test/Examples/Toy/Ch1/ast.toy:28:11
          Call 'transpose' [ @test/Examples/Toy/Ch1/ast.toy:28:30
            var: a @test/Examples/Toy/Ch1/ast.toy:28:40
          ]
          var: c @test/Examples/Toy/Ch1/ast.toy:28:44
        ]
    } // Block

注:上的抽象语法树的生成的编译命令如下:


首先进入到llvm-project/build的目录下面,这个build的目录里面已经把教程中的所有可执行程序都跑编译好了。
然后执行

bin/toyc-ch1 ../mlir/test/Examples/Toy/Ch1/ast.toy -emit=ast

会得到上面的抽象语法树内容

输出的ast,可以观察得到一个层级形式的结构。
最外为Module层,在Module层中包括两个Function层,在Function层中Proto描述了函数的名称以及所在位置;Param描述了参数信息;Block描述了函数中具体实现的内容。
在函数的内容中包括如VarDecl,Literal以及var等类型,后面再详细讨论。

其中ast.toy的内容如下:

# RUN: toyc-ch1 %s -emit=ast 2>&1 | FileCheck %s

# User defined generic function that operates on unknown shaped arguments.
def multiply_transpose(a, b) {
  return transpose(a) * transpose(b);
}

def main() {
  # Define a variable `a` with shape <2, 3>, initialized with the literal value.
  # The shape is inferred from the supplied literal.
  var a = [[1, 2, 3], [4, 5, 6]];
  # b is identical to a, the literal array is implicitly reshaped: defining new
  # variables is the way to reshape arrays (element count in literal must match
  # the size of specified shape).
  var b<2, 3> = [1, 2, 3, 4, 5, 6];

  # This call will specialize `multiply_transpose` with <2, 3> for both
  # arguments and deduce a return type of <2, 2> in initialization of `c`.
  var c = multiply_transpose(a, b);
  # A second call to `multiply_transpose` with <2, 3> for both arguments will
  # reuse the previously specialized and inferred version and return `<2, 2>`
  var d = multiply_transpose(b, a);
  # A new call with `<2, 2>` for both dimension will trigger another
  # specialization of `multiply_transpose`.
  var e = multiply_transpose(b, c);
  # Finally, calling into `multiply_transpose` with incompatible shape will
  # trigger a shape inference error.
  var f = multiply_transpose(transpose(a), c);
}


# CHECK: Module:
# CHECK-NEXT:     Function
# CHECK-NEXT:       Proto 'multiply_transpose' @{{.*}}ast.toy:4:1
# CHECK-NEXT:       Params: [a, b]
# CHECK-NEXT:       Block {
# CHECK-NEXT:         Return
# CHECK-NEXT:           BinOp: * @{{.*}}ast.toy:5:25
# CHECK-NEXT:             Call 'transpose' [ @{{.*}}ast.toy:5:10
# CHECK-NEXT:               var: a @{{.*}}ast.toy:5:20
# CHECK-NEXT:             ]
# CHECK-NEXT:             Call 'transpose' [ @{{.*}}ast.toy:5:25
# CHECK-NEXT:               var: b @{{.*}}ast.toy:5:35
# CHECK-NEXT:             ]
# CHECK-NEXT:       } // Block
# CHECK-NEXT:     Function
# CHECK-NEXT:       Proto 'main' @{{.*}}ast.toy:8:1
# CHECK-NEXT:       Params: []
# CHECK-NEXT:       Block {
# CHECK-NEXT:         VarDecl a<> @{{.*}}ast.toy:11:3
# CHECK-NEXT:           Literal: <2, 3>[ <3>[ 1.000000e+00, 2.000000e+00, 3.000000e+00], <3>[ 4.000000e+00, 5.000000e+00, 6.000000e+00]] @{{.*}}ast.toy:11:11
# CHECK-NEXT:         VarDecl b<2, 3> @{{.*}}ast.toy:15:3
# CHECK-NEXT:           Literal: <6>[ 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00] @{{.*}}ast.toy:15:17
# CHECK-NEXT:         VarDecl c<> @{{.*}}ast.toy:19:3
# CHECK-NEXT:           Call 'multiply_transpose' [ @{{.*}}ast.toy:19:11
# CHECK-NEXT:             var: a @{{.*}}ast.toy:19:30
# CHECK-NEXT:             var: b @{{.*}}ast.toy:19:33
# CHECK-NEXT:           ]
# CHECK-NEXT:         VarDecl d<> @{{.*}}ast.toy:22:3
# CHECK-NEXT:           Call 'multiply_transpose' [ @{{.*}}ast.toy:22:11
# CHECK-NEXT:             var: b @{{.*}}ast.toy:22:30
# CHECK-NEXT:             var: a @{{.*}}ast.toy:22:33
# CHECK-NEXT:           ]
# CHECK-NEXT:         VarDecl e<> @{{.*}}ast.toy:25:3
# CHECK-NEXT:           Call 'multiply_transpose' [ @{{.*}}ast.toy:25:11
# CHECK-NEXT:             var: b @{{.*}}ast.toy:25:30
# CHECK-NEXT:             var: c @{{.*}}ast.toy:25:33
# CHECK-NEXT:           ]
# CHECK-NEXT:         VarDecl f<> @{{.*}}ast.toy:28:3
# CHECK-NEXT:           Call 'multiply_transpose' [ @{{.*}}ast.toy:28:11
# CHECK-NEXT:             Call 'transpose' [ @{{.*}}ast.toy:28:30
# CHECK-NEXT:               var: a @{{.*}}ast.toy:28:40
# CHECK-NEXT:             ]
# CHECK-NEXT:             var: c @{{.*}}ast.toy:28:44
# CHECK-NEXT:           ]


The code for the lexer is fairly straightforward; it is all in a single header: examples/toy/Ch1/include/toy/Lexer.h. The parser can be found in examples/toy/Ch1/include/toy/Parser.h; it is a recursive descent parser. If you are not familiar with such a Lexer/Parser, these are very similar to the LLVM Kaleidoscope equivalent that are detailed in the first two chapters of the Kaleidoscope Tutorial .

代码的lexer很直观,全部都放在一个头文件里面。parser可以在对应位置找到,它是一个递归下降的parser。如果对lexer和parser不熟悉,建议你看看Kaleidoscope教程。(带货)

注:lexer是词法分析器,parser是语法分析器,都是编译原理中的基本概念,上面提到的教程是llvm提供的一个教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值