# doc **Repository Path**: advanced_os_group1/doc ## Basic Information - **Project Name**: doc - **Description**: 2023年秋季学期《高级操作系统》课程大作业第1组项目文档 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2023-12-07 - **Last Updated**: 2024-05-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Arkcompiler对AssemblerRiscv64 R型指令的支持 郝淼、陶思成、李龙昊 ## 摘要 ### 任务简介 我们的任务是为`arkcompiler_ets_runtime`的`assembler`中,实现对`RISCV`架构`R型`指令的支持。 `arkcompiler`是华为推出的首个完全自主研发的编译器平台,专门为软件厂商研发的统一编程平台,包含编译器、工具链、运行时等关键部件。该编译器支持多种编程语言、多种芯片平台的联合编译与运行。`arkcompiler` 支持将`javascript`、`typescript`翻译为方舟字节码`arkcompiler bytecode` ,为了让机器能够正确的执行对应的方舟字节码,方舟字节码可运行在方舟运行时`arkcompiler_ ets_runtime`上,我们仍需要将方舟字节码翻译为机器指令,才能交由底层硬件执行,而这就是`arkcompiler_ets_runtime`的作用之一,方舟运行时包含的编译器会经过词法、语法分析等操作后生成最终的机器指令码,交由底层硬件执行。不同架构的硬件有不同的指令码,而我们要实现的`AssemblerRiscv64`,就是用于生成`RISCV`架构的机器指令。 ![arkcompiler](slide/pic/arkcompiler.png) #### RISCV R型指令 `RISCV`中的指令结构相对比较统一,共有六种类型的指令,如下图所示: ![](slide/pic/riscv-all-inst.jpg) 而我们需要实现的,是`RISCV`中的15条`R型`指令,包含常见的加减运算、位运算和左移右移。具体指令如下图所示: ![rv32i-inst](slide/pic/rv32i-inst.png) ![rv64i-inst](slide/pic/rv64i-inst.png) ### 任务完成情况 | 大作业具体要求 | 完成情况 | | ------------------------------------------------- | :----------------------------------------------------------- | | 完成arkcompiler环境搭建和源码编译 | 成功在`docker`中运行整个`arkcompiler`项目,能够将TS、JS代码编译生成最终的.abc可执行文件 | | 完成 Assembler类各个接口的实现并提交pr至riscv-sig | 实现了`RISCV` 相关的` Assembler`类的框架,并实现了其中关于`RISCV` R型指令的接口,已将代码通过`pr`方式提交到`riscv-sig` | | 完成相应测试用例并提交pr至riscv-sig | 实现了`AssemblerRiscv64Test` 测例运行框架,编写了riscv 全部R型指令的测例。修改了`ninja`编译依赖,能够成功编译测例文件并运行。 | ### 任务分工 **郝淼**:搭建`assembler`、`AssemblerRiscv64Test`框架;PPT展示汇报 **陶思成**: 实现`RISCV`中R型相关指令对应的`assembler`函数;编写`RV64`R型指令测例;PPT制作 **李龙昊**:修改`BUILD.gn`支持运行相关测例;编写`RV32`R型指令测例;文档撰写 ### 提交方式 代码位于我们的[仓库](https://gitee.com/advanced_os_group1/arkcompiler_ets_runtime.git)中,具体实现的功能已通过`pull request`的方式合并到[官方仓库](https://gitee.com/riscv-sig/arkcompiler_ets_runtime.git)。 ## 任务实现 ### 环境部署 我们的实验环境基于ark运行环境,使用如下命令可下载源码并完成环境相关下载: ~~~bash repo init -u https://gitee.com/riscv-sig/manifest.git -b weekly -m default_weekly_0905.xml -g ohos:standard --no-repo-verify repo sync -c repo forall -c "git lfs pull" ./build/prebuilts_download.sh ~~~ 完成后在根目录下使用如下指令可以开始编译测例: ```bash build.py --product-name rk3568 --build-target ark_compiler_unittest ``` 生成的测例位于: ```bash ./out/rk3568/clang_x64/tests/unittest/arkcompiler/ets_runtime/AssemblerTest ``` `AssemblerTest`为二进制文件,可直接通过命令行运行。 ### 实验方法设计 #### 框架搭建 在设计之初,我们注意到要实现的`assembler`在先前已有`x86`、`aarch64`两种架构的实现,因此在搭建`AssemblerRiscv64`时,我们尽可能的让实现的`RISCV`相关框架与另两种架构的实现框架相同,方便上层开发者的调用。基于这种理念,我们定义了包括`AssemblerRiscv64`在内的众多类,他们的命名规范与调用逻辑与另两种架构基本相同。 ```c++ //assembler_riscv64.h namespace panda::ecmascript::riscv64 { class Register { public: Register(RegisterId reg) : reg_(reg) {}; inline RegisterId GetId() const { return reg_; } private: RegisterId reg_; }; class AssemblerRiscv64 : public Assembler { public: explicit AssemblerRiscv64(Chunk *chunk) : Assembler(chunk) { } // Integer Register-Register Operations (R-types) void Add(const Register &rd, const Register &rs1, const Register &rs2); void Addw(const Register &rd, const Register &rs1, const Register &rs2); ··· // other R-type Instructions private: // R_TYPE field defines inline uint32_t Rd(uint32_t id){ return (id << R_TYPE_rd_LOWBITS) & R_TYPE_rd_MASK; } inline uint32_t Rs1(uint32_t id){ return (id << R_TYPE_rs1_LOWBITS) & R_TYPE_rs1_MASK; } inline uint32_t Rs2(uint32_t id){ return (id << R_TYPE_rs2_LOWBITS) & R_TYPE_rs2_MASK; } }; } // namespace panda::ecmascript::riscv64 ``` #### 接口实现 `RISCV`架构中特定类型的指令的结构统一,下图为我们实现的`riscv32` R型指令: ![rv32i-inst](slide/pic/rv32i-inst.png) 这些指令有着相同的位结构,因此我们使用了宏定义模板的方法来实现这些指令对应的接口。 我们首先使用宏定义的方法定义指令模板函数: ```c++ // assembler_riscv64_constants.h #define EMIT_R_TYPE_INST(INSTNAME, INSTID) \ void AssemblerRiscv64::INSTNAME(const Register &rd, const Register &rs1, const Register &rs2) \ { \ uint32_t rd_id = Rd(rd.GetId()); \ uint32_t rs1_id = Rs1(rs1.GetId()); \ uint32_t rs2_id = Rs2(rs2.GetId()); \ uint32_t code = rd_id | rs1_id | rs2_id | INSTID; \ EmitU32(code); \ } ``` 通过宏定义的方法为不同指令套用模板,我们已经将指令中除去寄存器ID以外的其他指令位写好,因此在模板函数中只需要通过或运算寄存器的方法生成完整的指令。 ```c // assembler_riscv64_constants.h #define EMIT_INSTS \ EMIT_R_TYPE_INSTS(EMIT_R_TYPE_INST) \ #define EMIT_R_TYPE_INSTS(V) \ V( Add, ADD) \ V(Addw, ADDW) \ ··· // other instructions enum RegisterId : uint8_t { X0, X1, SP, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, ZERO = X0, RA = X1, GP = X3, ···// all 32 registers‘ aliases INVALID_REG = 0xFF, }; enum AddSubOpFunct { ADD = 0x00000033, ADDW = 0x0000003b, ··· // other instructions Op Funct }; ``` 最终在命名空间中调用宏定义,生成对应的`AssemblerRiscv64`接口。 ```c // assembler_riscv64.cpp namespace panda::ecmascript::riscv64 { using namespace panda::ecmascript::base; EMIT_INSTS } // panda::ecmascript::riscv64 ``` 使用上述方法,就可以实现`Assembler`中对应的`R型指令`接口。 #### 测例实现 在测例部分,我们仿照另两种架构的实现,实现了riscv的测例类:`AssemblerRiscv64Test`,并编写了对应的测例,采用对拍的方式,调用我们的接口,查看生成的机器码是否与给定的预期结果相同,判断所写接口的正确性。 ```c++ // assembler_riscv64_test.cpp #define __ masm. HWTEST_F_L0(AssemblerRiscv64Test, AddSub) { std::string expectResult( "00000000:002080b3 \tadd\tra, ra, sp\n" "00000004:003100b3 \tadd\tra, sp, gp\n" ··· // other results ); AssemblerRiscv64 masm(chunk_); __ Add(Register(RA), Register(RA), Register(SP)); __ Add(Register(RA), Register(SP), Register(GP)); ··· // other test std::ostringstream oss; DisassembleChunk(TARGET_RISCV64, &masm, oss); ASSERT_EQ(oss.str(), expectResult); } ``` #### 实验结果 对于10条`rv32` R型指令和5条 `rv64`,我们组均进行了正确性测试,指令均没有问题,下图为实验结果。 all-test ### 代码文件说明 ``` /arkcompiler/ets_runtime ├─ ecmascript # 方舟ArkTS运行时实现,包括ECMAScript标准库、解释器、内存管理等 │ ├─ compiler # 编译器 │ │ ├─ assembler │ │ │ ├─ riscv64 │ │ │ │ ├─ assembler_riscv64_constants.h # 定义了模板函数,用宏定义实现了具体的接口 │ │ │ │ ├─ assembler_riscv64.cpp # 调用宏定义 │ │ │ │ ├─ assembler_riscv64.h # 定义了AssemblerRiscv64、Immediate、Register等可能用到的类 │ │ │ ├─ tests │ │ │ │ ├─ assembler_riscv64_test.cpp # 实现了riscv assembler的对应测例 ``` ### 代码验证说明 代码运行方法已跟随上述**环境部署**说明。 ## 实验中遇到的问题和解决方法 ### 单元测试相关问题 在尝试编译全部的单元测例时,发现使用`ninja`编译过程中,会运行对应的单元测试,如果单元测试不能成功运行,`ninja`默认编译出错,停止编译。可能由于环境问题,某些`javascript`相关的单元测试在本地不能成功的运行,就会造成测例不能正确的编译完成。而这些单元测试,并不是我们测试的重点,对于它的正确性,我们并不关心。为此,我们添加了新的测例的`group`,在编译时,仅编译`assembler`相关的测例,解决了上述问题,还大大节约了编译的时间成本。 ## 参考文献 [arkcompiler文档](https://gitee.com/openharmony/community/blob/master/sig/sig_compileruntime/sig_compileruntime_cn.md) [riscv指令集手册](https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf)