想象你住进了一栋混合宿舍楼:计算机系、金融系、土木系的学生各有各的“黑话”。为了让大家都能用厨房、储物间和活动室,学生会制定了一本《通用生活公约》,把“烧水、借柜子、开会”这些公共事务写得清清楚楚。这本公约,就是 MLIR 里的 Standard Dialect(标准方言,缩写 std)。 MLIR 是一个给 AI 编译器用的“多语言社区”,方言(Dialect)就是各专业的行话手册,而 Standard Dialect 是最早诞生的那本总公约:它统一描述了算术、内存、函数调用等最基础的操作。后来社区壮大了,总公约被拆分成《厨房手册》(arith 方言)、《储物间手册》(memref 方言)、《活动室手册》(func 方言)等更专业的小册子。虽然大部分条款搬了家,但读懂最初的这本总公约,依然是理解整个 MLIR 世界的最佳起点——就像老校区的校史馆,旧却不朽。
Standard Dialect 是理解 MLIR 操作体系与编译流程演进的第一把钥匙。掌握它,你才能看懂如今 arith、memref、func 等方言分工的来龙去脉,在阅读老代码与设计新编译器 pass 时游刃有余。
可视化演示
\u4e09\u9636\u6bb5\u5c55\u793aStandard Dialect\u64cd\u4f5c\u5206\u7c7b\u3001\u65b9\u8a00\u62c6\u5206\u8fc1\u79fb\u6811\u4e0eMemRef\u7c7b\u578b\u7ed3\u6784\uff0c\u63ed\u793aMLIR\u65b9\u8a00\u4f53\u7cfb\u7684\u6f14\u8fdb\u5386\u7a0b\u4e0e\u7ec6\u7c92\u5ea6\u62c6\u5206\u673a\u5236\u3002
本节配有交互动效(进入视口后自动加载,约 3KB)。
Standard Dialect\u64cd\u4f5c\u5206\u7c7b \u7b97\u672f\u64cd\u4f5c\uff1aaddi/subi/muli/divi \u5185\u5b58\u64cd\u4f5c\uff1aload/store/memref std.addi(a,b) \u65e9\u671f\u65b9\u8a00\u5305\u542b\u5168\u90e8\u901a\u7528\u8ba1\u7b97\u64cd\u4f5c
方言定位
2019 年,MLIR 这栋“宿舍楼”刚刚动工时,学生会翻遍了所有专业的行话,发现彼此根本没法统一。于是他们出了一本最朴素的《通用生活公约》:不管你是计算机系还是土木系,“把水烧开”就写“把水烧开”,“借储物柜”就写“借储物柜”。条款不多,但覆盖了所有公共空间,是整栋楼最基础的共同语言。
这本公约在 MLIR 里就叫 Standard Dialect(标准方言,缩写 std)。它是 MLIR 最早的内置方言,收录了一批“谁都要用”的通用操作:整数加法、内存读写、跳转、函数调用。它的设计原则是“通用”——不绑定任何硬件、不偏向任何上层框架,就像集体宿舍里的普通话,任何专业的同学都能听懂。
从体系上看,std 属于“操作集合”型方言:每个操作都有操作名、操作数、结果、属性和类型约束。正是这套“操作五要素”的骨架,后来被所有方言继承。理解它的构造方式,你就拿到了读懂 MLIR 任何一个方言的钥匙。
就像老宿舍的《通用生活公约》至今还挂在楼道公告栏上,std 的条款句式今天仍被各本分册沿用。它是 MLIR 宿舍的“地基公约”,新手册可以比它更厚、更专,但谁也绕不开它当年打下的底子。
- 集合左侧符号
std表示标准方言这个整体,它不是一个孤立的指令,而是一批操作的集合。 - 集合与竖线竖线
\mid读作“使得”,把集合分成左右两半:左边是成员,右边是成员必须满足的条件。 - 右侧准入条件“通用操作”指算术、内存、控制流这类不依赖具体硬件、各领域都用得上的操作。
历史演进
宿舍人越来越多,《通用生活公约》从 30 页涨到 300 页。想查“怎么用微波炉”得翻 20 分钟。于是学生会把公约拆了:厨房条款进《厨房手册》,储物条款进《储物手册》,活动室条款进《活动室手册》。每本手册只管一件事,查起来又快又准。
MLIR 里这场“拆公约”运动发生在 2020 年前后:std 方言中一大批操作迁移到职责更单一的专业方言中。std.addi 这类算术操作去了 arith 和 math 方言;std.load、std.store、std.alloca 去了 memref;std.call、std.return 去了 func;std.br、std.cond_br 去了 cf。每个方言只负责一个领域,编译优化时能“按手册精确查找”。
迁移不等于删除。为了兼容存量代码,std 中旧操作仍然保留可用,但新代码被推荐直接写在更细粒度的方言里。这种“细粒度化”是整个 MLIR 生态的大趋势——从一本厚书拆成一排薄书,让工具链各司其职、并行演进。
旧条约虽然进了档案室,但新手册的目录、编号、行文方式全都继承了它——这就是为什么读懂 std 仍然是理解整个 MLIR 方言世界的必修课,档案虽旧,却是所有新手册的“族谱”。
- 迁移的起点
std在箭头左侧,表示旧操作是从标准方言这个“总集合”里搬出去的。 - 演进箭头
\xrightarrow{\text{演进}}表示“随时间搬运”的方向,从通用总公约指向各专业手册。 - 目标方言集合大括号里列出五个新家——arith/math 负责算术,memref 负责内存,func/cf 负责函数与分支。
算术运算
《厨房手册》的第一章讲“加热”:烧水、热饭、烤面包,本质都是把温度从一个值变成另一个值。而总公约里对应的只有一条:“对两个数值做加法”。条款短,但放之四海皆准——这就是通用算术操作的气质。
在 std 方言中,算术操作就是这个“数值变换条目”。std.addi 做整数加法,std.muli 做整数乘法,还有 std.subi(减法)、std.divi(除法);浮点版本则叫 std.addf、std.mulf。它们把两个相同类型的操作数变成一个同类型结果——规则简单得像烧水时把热量从火传到壶里,输入输出温度永远同一种刻度。
算术操作与类型强绑定:i32 的加法,输入输出必须都是 i32,不允许“整型 + 浮点”混搭。编译器靠这套类型签名做合法性检查,让错误在编译早期就暴露出来。如今这些算术操作主要在 arith 方言中安家,但函数签名的规则与 std 时期完全一致。
就像“烧水”条款从总公约抄进厨房手册后动作没变,std.addi 变成 arith.addi 也只是换了归属、没换语义。算术就是算术,无论手册封面怎么变,一加一永远等于二。
- 操作名
std.addi表示“标准方言中的整数加法操作”,add是加法,i是整数(integer)。 - 输入类型对括号里的
(\texttt{i32}, \texttt{i32})表示两个操作数都必须是 32 位整数类型。 - 结果类型箭头
\to右侧的\texttt{i32}表示结果的类型与操作数一致,保证“同种刻度进、同种刻度出”。
内存操作
宿舍的储物间曾是公共区域的“老大难”:东西随手放,回头找不到。公约规定了一套流程——先到管理处登记一个储物柜(alloca),把东西放进去(store),需要用的时候再取出来(load)。后来这套流程被原样搬进了《储物间手册》,成为 memref 方言的看家本领。
在 std 方言中,这套流程对应三个内存操作。std.alloca 在栈上申请一块空间,返回一个 memref 类型的“储物柜钥匙”;std.store 把值写进内存,相当于把东西放进柜子;std.load 把值读出来,相当于开柜取物。memref 是 MLIR 描述内存的核心类型,它自带形状、元素类型和存储布局,就像储物柜上贴的“长、宽、高、承重、编号”标签,一应俱全。
内存操作后来整体迁移到 memref 方言,成为现代 MLIR 中几乎所有数组、张量、缓冲操作的基石。你在向量化、张量核优化等一系列 pass 中,都会频繁看到 memref.load 与 memref.store 的身影。
从 std 到 memref,变的只是手册封面,不变的是一套“先申请、再读写、带索引”的储物间礼仪。今天你写任何涉及数组的 MLIR 代码,其实都在沿用这本公约当年定下的规矩。
- 读取签名
load需要“内存区域 memref”和“下标 index”两个输入,产出一个值。 - 写入签名
store需要“值、内存区域、下标”三个输入,写完不产生返回值,记作空集\emptyset。 - 笛卡尔积含义公式里的
\times表示多个输入按顺序组合,“钥匙 × 格口号”才是一次完整的取物动作。
控制流调用
宿舍楼有 10 层,公约里写着“怎么去食堂”:如果下雨,走室内通道;否则走天台。还写着“社团例会要去哪间活动室报到”。这些“怎么走、去哪开会”的条款,后来分别进了《楼宇指南》(cf 方言)和《活动室手册》(func 方言)。
std 方言中的控制流操作负责“程序走路”。std.br 是无条件跳转,像“不管怎样都去 3 楼”;std.cond_br 按 1 位整数 i1(只能存 0 或 1)决定跳向哪个基本块,像“下雨走室内,不下雨走天台”;std.call 调用一个函数,像“敲门进活动室开会”;std.return 则交回结果,像“会议结束,把结论带出来”。
std.call 和 std.return 之后迁移到 func 方言,std.br、std.cond_br 迁移到 cf 方言。它们共同支撑起 MLIR 程序中函数与基本块的组织结构,是编译器做 CFG(控制流图)分析时的主角——优化器顺着这些“走路箭头”判断哪条路更热、哪条路可以合并。
今天的新代码已经很少直接写 std 的控制流操作,但每一份 IR 里都能看到它们当年设计的影子——就像楼宇指南里的箭头,大多还是老公约箭头改绘的,方向没变,只是笔画更清楚。
- 条件类型
\texttt{i1}是 1 位整数,只有 0 和 1 两个取值,对应“假”与“真”,决定走哪条路。 - 两个跳转目标
true\_block与false\_block是基本块标签,分别表示“条件为真去 A 块、为假去 B 块”。 - 空结果
\emptyset表示控制流操作只改变程序的执行位置,本身不产生新的 SSA 值。
操作格式
为什么拆出来的手册都那么好查?因为总公约定下了一个“条款句式”:每条公约 = 居民编号 + 条款名 + 涉及的物件 + 物件规格。各分册都严格遵守这个句式,谁也不会写歪。管理宿舍靠句式,管理编译器的中间表示也靠句式。
MLIR 操作就有一套统一的“句式”,叫操作格式。一条操作写出来长这样:%r = "std.addi"(%a, %b) : (i32, i32) -> i32。其中 %r 是结果名,std.addi 是操作名,(%a, %b) 是操作数,冒号后面是类型签名。任何方言的操作都长这个模样,读起来像念公约一样顺口。
这个句式背后是 SSA(静态单赋值)形式:每个值在整个程序中只被定义一次。%r 一旦在加法中产生,就不会被改写;想再算一次,就新建一个 %r2。SSA 让数据依赖关系一目了然,是现代编译器做数据流分析的基石,也是 MLIR 所有方言默认遵循的规矩。
无论手册怎么拆分,“条款句式”从不变形——这就是为什么你可以在一个完全陌生的方言里,凭格式立刻读懂每一条操作。格式统一,是 MLIR 多方言社区能和平共处的第一契约。
- SSA 结果
\%r是这个操作产生的新值,\%前缀表示它是 SSA 变量,整个程序里只被定义这一次。 - 操作名与操作数
\text{std.op}是操作名,(\%a, \%b)是被消费的操作数,像公约里的“物件”清单。 - 类型签名冒号后的
(\texttt{i32}, \texttt{i32}) \to \texttt{i32}声明了输入类型和输出类型,是编译器的“验货单”。
现代价值
有人问:公约都拆了,还留着档案室里的原版做什么?答案很简单:它是整栋楼的“施工蓝图原型”。想理解为什么厨房在 1 楼东侧、储物间在 B1,得先看最早的平面图。同理,想理解 MLIR 今天为什么长成这样,得先看 std。
今天,std 方言不再是生产环境的首选方言,但它的现代价值恰恰藏在“历史”与“参照”里。第一,它是学习 MLIR 操作体系的最佳起点:arith、memref、func、cf 的操作都能在 std 中找到“祖辈”,从祖辈入手,新方言的每个动作都显得熟悉。第二,它记录了方言演进的完整轨迹:读老代码、维护旧 pass 时,没有 std 知识寸步难行,满屏 std.load 会像天书一样劝退新人。
第三,std 的设计思路——通用操作 + 明确类型 + SSA 约束——是所有新方言的模板。MLIR 团队在开发新方言时,仍会参考 std 的 TableGen 定义方式,确保新操作与整个体系“语法兼容”。可以说,每个新方言的脸上,都隐约透着 std 的五官。
总公约不需要再贴到楼道里,但每个学生会在制定新规则前,都会先翻翻那本泛黄的档案——这就是标准方言在今天的价值。它不是被遗忘的过去,而是理解现在、设计未来的参照系。
- 全称量词
\forall读作“对于每一个”,表示这句话对标准方言里的所有操作都成立。 - 存在量词
\exists读作“存在”,保证每个旧操作都至少有一个明确的新归属。 - 等价搬迁
\rightsquigarrow表示“搬迁且语义不变”,旧操作搬家后行为保持一致,方便工具链逐步切换。
AI 应用场景
把深度学习框架算子翻译成 MLIR
阶段:AI 编译器前端代码生成与算子落地
解决的问题:从 PyTorch/TensorFlow 导出计算图后,逐算子手写转换繁琐且易错。传统做法是每个算子写一套专属转换逻辑,维护成本极高;借助标准方言的通用操作,可以先统一映射到 std.addi、std.load 这类通用条款上,再交给后续 pass 逐步优化和拆分。
具体案例:某编译器团队把 ONNX 的 Add 算子先映射为 std.addi(后按官方建议改用 arith.addi),配合类型转换 pass 统一 i32/i64,在一周内完成了 20 个基础算子的落地。正是 std 的“通用操作”定位,让这种批量化映射成为可能。
读懂并升级历史 MLIR pass
阶段:编译优化调试与代码维护
解决的问题:仓库里还跑着 2020 年前的量化 pass,打印出的 IR 全是 std.load、std.store 和 std.alloca。不懂 std 方言,就无法判断这些 pass 是否正确,也无从把它们迁移到 memref 新接口。掌握 std 知识如同拿到了旧蓝图的注记,能快速定位问题、安全迁移。
具体案例:升级老版本量化 pass 时,工程师批量把 std.load 替换为 memref.load,并编写脚本校验替换后的 IR 是否满足 SSA 规范(每个值只定义一次、使用前已定义)。正是对 std 时代的操作语义足够熟悉,迁移才没有引入新的数据流错误。
给新人讲清楚方言体系
阶段:团队培训与知识沉淀
解决的问题:新同学面对 arith、memref、func、cf 一堆方言一脸茫然,不知道从哪学起。传统方式是逐方言罗列操作,信息过载;用 std 作为“上古总纲”先建立整体心智模型,再逐个展开各专业手册,学习曲线平缓得多。
具体案例:某编译器团队在内部培训中,先用一页纸画出“std 总公约 → 四本分册”的演进图,再让新同学动手把一段老式 std IR 迁移成新方言 IR。三天内,新人就能独立阅读中等复杂度的 pass 代码。
Python 演示
# -*- coding: utf-8 -*-# 演示 MLIR 标准方言(std)到细粒度方言的操作迁移, 以及 SSA 形式的合法性检查 # 旧 std 操作 -> 新方言操作 的迁移字典STD_MIGRATION = { "std.addi": "arith.addi", "std.muli": "arith.muli", "std.constant": "arith.constant", "std.alloca": "memref.alloca", "std.load": "memref.load", "std.store": "memref.store", "std.br": "cf.br", "std.cond_br": "cf.cond_br", "std.call": "func.call", "std.return": "func.return",} # 一段"宿舍总公约"时代的旧式 std 方言代码OLD_IR = """func @sum(%n: i32) -> i32 { %c0 = "std.constant"() {value = 0 : i32} : () -> i32 %arr = "std.alloca"() : () -> memref<10xi32> "std.store"(%c0, %arr) : (i32, memref<10xi32>) -> () %v = "std.load"(%arr) : (memref<10xi32>) -> i32 %r = "std.addi"(%v, %c0) : (i32, i32) -> i32 "std.return"(%r) : (i32) -> ()}""" def parse_operands(line): """提取一行操作中的 SSA 操作数(形如 %name)""" if '(' not in line or ')' not in line: return [] head = line.split('(', 1)[1].split(')', 1)[0] tokens = [t.strip() for t in head.split(',')] return [t for t in tokens if t.startswith('%') and ':' not in t and ' ' not in t] def check_ssa(ir): """SSA 检查: 每个值只定义一次, 且使用前必须已经定义""" defined = set() errors = [] for line in ir.strip().splitlines(): line = line.strip().rstrip('{').strip() body = line.split('//', 1)[0].strip() if '=' in body: lhs = body.split('=', 1)[0].strip() if lhs in defined: errors.append("redefined value: " + lhs) defined.add(lhs) for op in parse_operands(body): if op not in defined: errors.append("use before def: " + op) return errors def migrate(ir): """把 std 操作整体迁移到新方言, 返回新的 IR 与迁移报告""" lines = ir.strip().splitlines() migrated, report = [], [] for line in lines: new_line = line for old, new in STD_MIGRATION.items(): if old in new_line: new_line = new_line.replace(old, new) report.append(old + " -> " + new) break migrated.append(new_line) return "\n".join(migrated), report def main(): print("===== 1. SSA validity check (legacy std) =====") errors = check_ssa(OLD_IR) if errors: for e in errors: print(" FAIL:", e) else: print(" PASS: every value is defined before use.") print() print("===== 2. std migration to fine-grained dialects =====") new_ir, report = migrate(OLD_IR) for old, new in report: print(" %-14s -> %s" % (old, new)) print() print("===== 3. migrated IR =====") print(new_ir) if __name__ == "__main__": main()
STD_MIGRATION = {迁移字典:把旧的 std 操作名映射到新方言操作名,相当于“总公约拆分对照表”。tokens = [t.strip() for t in head.split(',')]把操作数列表按逗号切开并去掉首尾空格,得到一个个操作数。if '=' in body:判断这一行是否是“定义型”操作:带等号说明左边是 SSA 结果的唯一定义点。lhs = body.split('=', 1)[0].strip()取等号左侧的内容,就是本次被定义的值。for op in parse_operands(body):遍历本行使用到的每个操作数,逐一检查它们是否已经定义。if op not in defined:发现“未定义先使用”,立即记录一条 SSA 违规。for old, new in STD_MIGRATION.items():遍历迁移表,为当前这一行查找匹配的旧操作。new_line = new_line.replace(old, new)把旧操作名原地替换成新方言名,完成“搬家”。
常见误区
误区既然 std 的操作都迁移走了,那标准方言就是废弃代码,新人不该浪费时间学它。
正解迁移不等于废弃。MLIR 官方为兼容保留了旧操作,很多老项目的 IR 和 pass 里仍大量出现 std 操作。更重要的是,std 是 arith、memref、func、cf 的“母本”——就像宿舍总公约虽然被拆,但每本分册都继承了它的条款句式。学 std 不是学死代码,而是学一门方言该有的骨架。
为何常见:看到文档里的 “migrated“ 就脑补成 ”deleted”,把演进误读成淘汰,忽略了 MLIR 一贯的向后兼容策略。
误区名字不同就是完全不同的操作,语义、类型规则、实现都得重新学一遍。
正解迁移的本质是搬运,不是重写。std.addi 变成 arith.addi 时,操作数、结果、类型约束、执行语义几乎一致,只是隶属方言变了。就像“烧水”条款从总公约抄进厨房手册,烧的还是水,只是归属更清晰。理解这一点,你读新方言代码时会非常轻松。
为何常见:把“方言名”误当成操作语义的一部分,忽视了 MLIR 操作迁移“语义不变”的兼容原则。
误区标准方言这么“标准”,应该是离机器指令最近的底层方言,优化就看它了。
正解MLIR 是多级中间表示,std 只是高层的通用操作层,离机器指令还很远。真正的底层是 LLVM 方言和各类 target 方言。宿舍公约定“怎么开会”,但不会规定用哪间教室的哪把椅子——std 管通用语义,底层映射另有人负责。
为何常见:望文生义,把“标准”理解成“贴近硬件的事实标准”,混淆了“通用”与“底层”两个维度。
误区SSA 要求每个值只能赋值一次,那循环里的 `i = i + 1` 怎么写?这限制太死。
正解编译器会把 i = i + 1 改写成 %i1 = addi %i0, 1、%i2 = addi %i1, 1……每次迭代生成一个新值。SSA 的“一个定义点”不是禁止更新,而是把每次更新变成一个新值,让数据流一目了然。就像宿舍物品换新标签就算新物品,旧标签依然可追溯。
为何常见:拿命令式语言的“变量可变”习惯去套 SSA,没有理解“定义点”与“值”的关系。
误区官方拆分手册那天,std 里的旧操作应该马上不可用了吧?所有代码都得连夜改。
正解为保证编译生态平稳,旧操作在过渡期继续可用,只是新代码被建议直接写新方言。这是“软迁移”而非“硬删除”。老版公约在过渡期仍然有效,新楼入住后才逐步回收。实际工程中,老 IR 和旧 pass 往往还能跑很多年。
为何常见:把文档层面的演进等同于运行时能力的即时移除,忽略了编译器项目的兼容性设计常识。
面试题
Q1. MLIR 中 Standard Dialect 的核心定位是什么?
- A.专为GPU硬件定制的底层指令集
- B.最早的通用操作内置方言之一← 正确答案
- C.用于描述神经网络层的专用方言
- D.负责最终机器码生成的目标方言
答案:B。std 是 MLIR 最早的内置方言,提供算术、内存、控制流等通用操作,不绑定任何硬件或框架。A 描述的是 target 方言,C 是领域专用方言,D 是后端方言,均与 std 的“通用”定位不符。
Q2. 下列哪个操作原本属于 std 方言,后来迁移到了 arith 方言?
- A.std.alloca
- B.std.cond_br
- C.std.addi← 正确答案
- D.std.call
答案:C。std.addi 是整数加法,属于算术操作,归宿是 arith 方言。alloca 迁移到 memref,cond_br 迁移到 cf,call 迁移到 func。这正好对应“总公约拆分成专业手册”的演进路径。
Q3. 关于 std.load 与 std.store 的说法,正确的是?
- A.load负责写入而store负责读取
- B.两者都操作memref类型的内存区域← 正确答案
- C.两者只支持浮点数一种类型
- D.两者在MLIR历史中从未出现过
答案:B。load 从内存读值,store 把值写入内存,两者都作用于 memref 类型的内存区域,且同时支持整数与浮点。A 方向说反了,C 类型范围说窄了,D 与历史事实矛盾。
Q4. 标准方言中的操作被拆分的根本原因是什么?
- A.设计存在缺陷需要彻底重写
- B.为了更细粒度的职责划分← 正确答案
- C.MLIR项目已经停止维护
- D.所有操作被硬件指令完全取代
答案:B。拆分是为了让每个方言职责单一——arith 管算术、memref 管内存、func/cf 管函数与控制流,便于复用与优化。这不是推倒重写,更不是项目停摆,而是生态走向成熟的标志。
Q5. MLIR 中 SSA 形式的核心要求是什么?
- A.每个值只允许被定义一次← 正确答案
- B.所有变量都必须分配内存储存
- C.每个操作的结果类型必须相同
- D.操作数中禁止出现任何常数值
答案:A。SSA(静态单赋值)要求每个值在整个程序中只有一个定义点,编译器据此做数据流分析。B 不是 SSA 的要求,C 与类型签名规则无关,D 显然是错的——常量也可以作为操作数出现。
Q6. 下列哪组迁移关系是正确的?
- A.std.call迁移到memref.call
- B.std.cond_br迁移到cf.cond_br← 正确答案
- C.std.addi迁移到func.addi
- D.std.load迁移到arith.load
答案:B。cond_br 属于控制流操作,正确归宿是 cf 方言。而 call 应迁到 func.call,addi 应迁到 arith.addi,load 应迁到 memref.load。A、C、D 都把操作送错了“专业手册”。
Q7. 标准方言如今对编译器工程师的主要价值是什么?
- A.直接生成高效的最终机器码
- B.理解操作体系的起点与参照← 正确答案
- C.替代所有target方言的位置
- D.提供完整神经网络算子库
答案:B。std 是理解 MLIR 操作体系的起点:arith/memref/func/cf 都能在 std 中找到祖先,读老代码也离不开它。它不生成机器码,不替代 target 方言,也不提供神经网络算子库。
Q8. 下列哪些操作属于 std 方言的通用操作类别?
- A.通用算术运算类操作← 正确答案
- B.通用内存操作类指令← 正确答案
- C.控制流与函数调用操作← 正确答案
- D.专项音视频编解码指令
答案:ABC。std 的通用操作覆盖算术、内存、控制流与函数调用三大类,是任何程序都需要的“公共条款”。D 是特定领域的专用指令,不属于通用操作集合——就像宿舍公约不会规定怎么剪视频。
Q9. 关于 std 方言历史演进的说法,正确的有哪些?
- A.部分操作迁移到arith与math方言← 正确答案
- B.内存操作迁移到memref方言← 正确答案
- C.旧操作被立即删除以保证整洁
- D.迁移体现方言细粒度化趋势← 正确答案
答案:ABD。算术操作迁往 arith/math,内存操作迁往 memref,整体呈现“职责细分”的趋势。但旧操作并未被立即删除,而是保留兼容,所以 C 错误。软迁移而非硬删除,是编译器生态演进的基本原则。
Q10. 一条 MLIR 操作的格式中,通常包含哪些要素?
- A.操作名称要素← 正确答案
- B.操作数与结果← 正确答案
- C.属性与类型约束← 正确答案
- D.硬件缓存容量信息
答案:ABC。操作格式由操作名、操作数、结果、属性、类型签名组成,这是 std 定下的“条款句式”。D 的硬件缓存容量属于目标硬件信息,与操作本身的格式无关,不属于通用操作格式要素。