1# 实现描述 2 3重要说明:目前 AbcKit 支持 JS、ArkTS 和静态 ArkTS,但**对静态 ArkTS 的支持处于试验阶段**。 4编译后的 JS 和 ArkTS 存储在"动态"方舟字节码格式的文件中,静态 ArkTS 存储在"静态"方舟字节码格式的文件中。 5AbcKit 根据字节码的格式使用"动态"或"静态"的方式去处理文件。 6 7请先查看 [cookbook](mini_cookbook.md) 以了解用户视角的API。 8 91. [两种类型的字节码文件](#两种类型的-abc-文件) 102. [C API 和 C++ API](#c-api-和-c-api) 113. [按组件的控制流](#按组件的控制流) 124. [按源文件的控制流](#按源文件的控制流) 135. [动态和静态文件格式的处理](#动态和静态文件格式之间的分发) 146. [数据结构(上下文)和不透明指针](#数据结构上下文和不透明指针) 157. [数据结构(上下文)实现](#数据结构上下文实现) 168. [头文件命名冲突](#头文件命名冲突) 179. [字节码<-->中间表示](#字节码--中间表示) 18 19## 两种类型的 abc 文件 20 21**AbcKit 支持两种类型的字节码文件**:动态和静态。 22根据字节码文件类型可使用以下两种类型的组件: 23 241. `panda::panda_file` 和 `ark::panda_file` 252. `panda::abc2program` 和 `ark::abc2program` 263. `panda::pandasm` 和 `ark::pandasm` 274. IR 构建器:`libabckit::IrBuilderDynamic` 和 `ark::compiler::IrBuilder` 285. 代码生成:`libabckit::CodeGenDynamic` 和 `libabckit::CodeGenStatic` 29 30注意:`panda::` 是动态运行时命名空间,`ark::` 是静态运行时命名空间。 31 32只有一个静态运行时编译器用于 AbcKit 图表示:`ark::compiler::Graph`。 33 34## C API 和 C++ API 35 36AbcKit 提供两种 API:C API 和 C++ API。 37 38### C API 39 40所有 C API 都存储在 `./include/c` 文件夹中,目录结构如下: 41 42``` 43include/c/ 44├── abckit.h // 入口点 API 45├── api_version.h // API 版本定义 46├── declarations.h // 基础声明 47├── metadata_core.h // 用于语言无关元数据检查/转换的 API 48├── extensions 49│ ├── arkts 50│ │ └── metadata_arkts.h // 用于语言特定(ArkTS 和静态 ArkTS)元数据检查/转换的 API 51│ └── js 52│ └── metadata_js.h // 用于语言特定(JS)元数据检查/转换的 API 53├── ir_core.h // 用于语言无关图检查/转换的 API 54├── isa 55│ ├── isa_dynamic.h // 用于语言特定(JS 和 ArkTS)图检查/转换的 API 56│ └── isa_static.h // 用于语言特定(静态 ArkTS)图检查/转换的 API(此头文件现在已隐藏) 57├── statuses.h // 错误代码列表 58``` 59 60### C++ API 61 62C++ API 存储在 `./include/cpp` 文件夹中,目录结构如下: 63 64``` 65include/cpp/ 66├── abckit_cpp.h // C++ API 入口点 67├── headers/ 68│ ├── file.h // 文件操作 69│ ├── graph.h // 图操作 70│ ├── basic_block.h // 基本块操作 71│ ├── instruction.h // 指令操作 72│ ├── literal.h // 字面量操作 73│ ├── value.h // 值操作 74│ ├── type.h // 类型操作 75│ ├── dynamic_isa.h // 动态 ISA 操作 76│ ├── config.h // 配置 77│ ├── utils.h // 工具函数 78│ ├── base_classes.h // 基础类 79│ ├── base_concepts.h // 手动实现 concepts 特性 80│ ├── core/ // 语言无关 API 81│ ├── arkts/ // ArkTS 特定 API 82│ └── js/ // JS 特定 API 83``` 84 85C API 是纯 C 函数,实现存储在 `./src/` 文件夹中并用 C++ 编写。C++ API 提供了更高级的面向对象接口。 86 87## 按组件的控制流 88 891. 调用 `openAbc` 时: 90 1. 将 `abc` 文件读入 `panda_file` 91 2. 使用 `abc2program` 将 `panda_file` 转换为 `pandasm` 922. Abckit 元数据 API 获取/处理 `pandasm` 程序信息 933. 当调用 `createGraphFromFunction` 时: 94 1. `pandasm` 程序信息被转换成 `panda_file`(因为当前的 IR 构建器只支持 `panda_file` 输入) 95 2. IR 构建器为函数构建控制流图: `ark::compiler::Graph` 964. Abckit Graph API 获取/处理 `ark::compiler::Graph` 975. 当调用 `functionSetGraph` 时,代码生成器使用 `ark::compiler::Graph` 生成 `pandasm` 格式的字节码并替换函数的原始字节码 986. 当调用 `writeAbc` 时,转换后的 `pandasm` 程序被写到 abc 文件中 99 100``` 101 ────────────────────────────────────────────────────────────────────────────────────────────────── 102 | /\ 103 \/ | 104x.abc────>(ark/panda)::panda_file───>(ark/panda)::abc2program────>(ark/panda)::pandasm────>(ark/panda)::panda_file───>(ark/panda)::ir_builder────>ark::compiler::Graph──>(ark/panda::)codegen 105 | /\ | /\ | 106 | | | | | 107 | | | | | 108 (abckit metadata API) | | \/ 109 ──────────>(ark/panda)::RuntimeIface───────────────>(abckit IR API) 110``` 111 112## 按源文件的控制流 113 1141. 声明 C API 1152. 上述 API 的 C++ 实现主要在动态和静态运行时之间进行分发 1163. 运行时特定实现: 117 1. 动态运行时实现 118 2. 静态运行时实现 1194. API 接收并返回指向不透明 `AbckitXXX` 结构的指针 120 121``` 122 123 |───────────────────────────────────────────| 124 | 4. 数据结构(上下文)(./src) | 125 | metadata_inspect_impl.h | 126 | ir_impl.h | 127 |───────────────────────────────────────────| 128 /\ 129 | 130 \/ 131─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 132 /\ 133 | 134 \/ 135 |──────────────────────────────────────| |───────────────────────────────────────────| |─────────────────────────────────────────────────────────────────| 136 | 1. API 声明 (./include/c) | | 2. API 实现 (./src/) | | 3.1 动态运行时实现 (./src/adapter_dynamic/) | 137 | abckit.h | | abckit_impl.cpp | | abckit_dynamic.cpp | 138 | metadata_core.h | | metadata_(inspect|modify)_impl.cpp | | metadata_(inspect|modify)_dynamic.cpp | 139 | ir_core.h | | ir_impl.cpp | | | 140 | extensions/arkts/metadata_arkts.h |───────>| metadata_arkts_(inspect|modify)_impl.cpp |───────>| | 141 | extensions/js/metadata_js.h | | metadata_js_(inspect|modify)_impl.cpp | | | 142 | isa/isa_dynamic.h | | isa_dynamic_impl.cpp | | | 143 | isa/isa_static.h | | isa_static_impl.cpp | | | 144 |──────────────────────────────────────| |───────────────────────────────────────────| |─────────────────────────────────────────────────────────────────| 145 | 146 \/ 147 |───────────────────────────────────────────────────────────────| 148 | 3.2 静态运行时实现 (./src/adapter_static/) | 149 | abckit_static.cpp | 150 | metadata_(inspect|modify)_static.cpp | 151 | ir_static.cpp | 152 |───────────────────────────────────────────────────────────────| 153 154``` 155 156## 动态和静态文件格式的处理 157 158许多 API 能够根据 `abc` 文件的源语言处理动态和静态文件格式。 159运行时特定实现存储在 `./src/adapter_dynamic/` 和 `./src/adapter_static/` 中 160 161例如, `functionGetName()` API的实现(`./src/metadata_inspect_impl.cpp`): 162 163```cpp 164 switch (function->module->target) { 165 case ABCKIT_TARGET_JS: 166 case ABCKIT_TARGET_ARK_TS_V1: 167 return FunctionGetNameDynamic(function); 168 case ABCKIT_TARGET_ARK_TS_V2: 169 return FunctionGetNameStatic(function); 170 } 171``` 172 173根据函数的源语言调用两个函数之一: 174 175- `FunctionGetNameDynamic()` 适用于动态字节码,代码位于: `./src/adapter_dynamic/metadata_inspect_dynamic.cpp` 176- `FunctionGetNameStatic()` 适用于静态字节码,代码位于: `./src/adapter_static/metadata_inspect_static.cpp` 177 178## 数据结构(上下文)和不透明指针 179 180Abckit C API接收并返回指向不透明 `AbckitXXX` 结构的指针。 181用户有这些结构的前向声明类型,实现对用户隐藏并存储在 `./src/metadata_inspect_impl.h` 中。 182因此,用户只能从 API 接收指针并将其传递给另一个 API,不能手动修改。 183 184例如,这是来自 `./include/c/metadata_core.h` 的前向类型声明: 185 186``` 187typedef struct AbckitLiteral AbckitLiteral; 188``` 189 190这是来自 `./src/metadata_inspect_impl.h` 的实现: 191 192``` 193struct AbckitLiteral { 194 AbckitFile *file; 195 libabckit::pandasm_Literal* val; 196}; 197``` 198 199## 数据结构(上下文)实现 200 201### 元数据 202 203在 `openAbc` API 调用时,abckit 执行以下步骤: 204 2051. 使用 `panda_file` 打开 `abc` 文件 2062. 使用 `abc2program` 将打开的 panda 文件转换为 `pandasm` 程序 2073. **贪心遍历**所有 `pandasm` 结构并创建相应的 `AbckitXXX` 结构 208 209所有 `AbckitXXX` 数据结构的实现都存储在 `metadata_inspect_impl.h` 和 `ir_core.h` 中。 210顶层数据结构是 `AbckitFile`,用户在 `openAbc` 调用后接收 `AbckitFile*` 指针。 211 212`AbckitXXX` 元数据结构具有"树结构",与源程序结构匹配,例如: 213 2141. `AbckitFile` 拥有 `unique_ptr<AbckitCoreModule>` 的 vector(每个模块通常对应一个源文件) 2152. `AbckitCoreModule` 拥有 `unique_ptr<AbckitCoreNamespace>`(顶层命名空间)、 216 `unique_ptr<AbckitCoreClass>`(顶层类)、`unique_ptr<AbckitCoreFunction>`(顶层函数)的 vector 2173. `AbckitNamespace` 拥有 `unique_ptr<AbckitCoreNamespace>`(命名空间中嵌套的命名空间)、 218 `unique_ptr<AbckitCoreClass>`(命名空间中嵌套的类)、`unique_ptr<AbckitCoreFunction>`(顶层命名空间函数)的 vector 2194. `AbckitCoreClass` 拥有 `unique_ptr<AbckitCoreFunction>`(类方法)的 vector 2205. `AbckitCoreFunction` 拥有 `unique_ptr<AbckitCoreFunction>`(嵌套在其他函数中的函数)的 vector 221 222### 图 223 224在 `createGraphFromFunction` API 调用时,abckit 执行以下步骤: 225 2261. 从 `pandasm` 函数生成 `panda_file`(这是必需的,因为当前 `ir_builder` 只支持 `panda_file` 输入) 2272. 使用 `IrBuilder` 构建 `ark::compiler::Graph` 2283. **贪心遍历 `ark::compiler::Graph` 并创建相应的 `AbckitXXX` 结构** 229 230`AbckitXXX` 图结构是:`AbckitGraph`、`AbckitBasicBlock`、`AbckitInst` 和 `AbckitIrInterface`。 231对于每个 `ark::compiler::Graph` 基本块和指令,创建 `AbckitBasicBlock` 和 `AbckitInst`。 232`AbckitGraph` 包含这样的映射: 233 234```cpp 235std::unordered_map<ark::compiler::BasicBlock *, AbckitBasicBlock *> implToBB; 236std::unordered_map<ark::compiler::Inst *, AbckitInst *> implToInst; 237``` 238 239因此我们可以从内部图实现获取相关的 `AbckitXXX` 结构。 240 241## 头文件命名冲突 242 243**重要的实现限制:** libabckit 包含来自动态和静态运行时的头文件, 244因此,在构建期间,clang 必须提供两个运行时文件夹的包含路径(`-I`)。 245但是两个运行时中有很多具有相同名称的文件和文件夹,这会导致 `#include` 的命名冲突。 246这就是 AbcKit 中没有文件同时包含两个运行时头文件的原因: 247- 有 `./src/adapter_dynamic/` 文件夹用于包含动态运行时头文件的文件 248- 有 `./src/adapter_static/` 文件夹用于包含静态运行时头文件的文件 249 250### 包装器(Wrappers) 251 252但对于某些情况,我们需要在单个文件中处理两个运行时,例如: 253 254- 从 `panda::panda_file::Function` 生成 `ark::compiler::Graph` 时 255- 或者从 `ark::compiler::Graph` 生成 `panda::pandasm::Function` 时 256 257对于这种情况,我们使用存储在`./src/wrappers/` 中的**包装器**来处理两个运行时。 258在下图中(箭头显示包含方向),您可以看到: 259 260- `metadata_inspect_dynamic.cpp` 包含动态运行时头文件,并且也使用静态图(通过 `graph_wrapper.h`) 261- `graph_wrapper.cpp` 包含静态运行时头文件,并且也使用动态 `panda_file`(通过 `abcfile_wrapper.h`) 262 263``` 264 metadata_inspect_dynamic.cpp ──>graph_wrapper.h<────| 265 | | 266 \/ | 267DynamicRuntime |<───graph_wrapper.cpp──>StaticRuntime 268 /\ | 269 | | 270 abcfile_wrapper.cpp────────────>abcfile_wrapper.h<───| 271``` 272 273按照上图箭头,可以看到没有文件同时包含静态和动态运行时的头文件 274 275## 字节码 <──> 中间表示 (IR) 276 277abckit 使用 `ark::compiler::Graph` 作为内部图表示, 278因此动态和静态 `pandasm` 字节码都被转换为单个 `IR` 279 280字节码与 IR 的转换方法与字节码优化器相同, 281IR 构建器将字节码转换为 IR,代码生成器将 IR 转换为字节码。 282 283- 有两个 IR 构建器:`libabckit::IrBuilderDynamic` 和 `ark::compiler::IrBuilder` 284- 两个字节码优化器:`libabckit::CodeGenDynamic` 和 `libabckit::CodeGenStatic` 285- 两个运行时接口:`libabckit::AbckitRuntimeAdapterDynamic` 和 `libabckit::AbckitRuntimeAdapterStatic` 286 287运行时接口是静态编译器的一部分,需要抽象出 IR 构建器 288IR 构建器的每个用户都应该提供自己的运行时接口(根据编译器的设计)。 289 290IR 接口(`AbckitIrInterface`)源自字节码优化器,需要存储 `panda_file` 实体名称和偏移量之间的关系。 291IR 接口的实例: 292 2931. 当函数的字节码转换为 IR 时创建 2942. 存储在 `AbckitGraph` 实例中 2953. 在 abckit API 实现中使用,以从指令的立即偏移量获取 `panda_file` 实体 296 297### IR 构建器 298 299对于静态字节码,abckit 重用静态运行时的 IrBuilder。 300 301对于动态字节码,abckit 使用动态运行时 IrBuilder 的分支,并进行多种更改。 302对于动态字节码,大多数指令仅转换为内部调用(例如 `Intrinsic.callthis0`)。 303 304### 代码生成 305 306对于静态字节码,abckit 基于静态运行时的字节码优化器和代码生成器做了多处修改。 307对于动态字节码,abckit 基于动态运行时的字节码优化器和代码生成器做了多处修改。 308 309### 编译器传递 310 311在图创建和字节码生成期间,应用了额外的图清理和优化, 312因此如果您这样做:`graph1`->`bytecode`->`graph2` 而不进行任何额外更改, 313`graph1` **可能与** `graph2` 存在差异。