From a0b2bd9d86ea61827c1cb3cf792a35e336d09dea Mon Sep 17 00:00:00 2001 From: LiXiaoQi Date: Mon, 10 Oct 2022 09:39:12 +0800 Subject: [PATCH] finish part1: hand write .ll file --- include/lightir/Type.h | 32 +- tests/2-ir-gen-warmup/c_cases/.gitignore | 1 + tests/2-ir-gen-warmup/stu_ll/assign_hand.ll | 14 + tests/2-ir-gen-warmup/stu_ll/fun_hand.ll | 9 + tests/2-ir-gen-warmup/stu_ll/if_hand.ll | 11 + tests/2-ir-gen-warmup/stu_ll/while_hand.ll | 25 ++ tests/2-ir-gen-warmup/ta_gcd/.gitignore | 1 + .../ta_gcd/gcd_array_generator.cpp | 331 +++++++++--------- 8 files changed, 252 insertions(+), 172 deletions(-) create mode 100644 tests/2-ir-gen-warmup/c_cases/.gitignore create mode 100644 tests/2-ir-gen-warmup/ta_gcd/.gitignore diff --git a/include/lightir/Type.h b/include/lightir/Type.h index 945e807..5cb808f 100644 --- a/include/lightir/Type.h +++ b/include/lightir/Type.h @@ -11,7 +11,8 @@ class ArrayType; class PointerType; class FloatType; -class Type { +class Type +{ public: enum TypeID { VoidTyID, // Void @@ -73,11 +74,12 @@ class Type { std::string print(); private: - TypeID tid_; + TypeID tid_; Module *m_; }; -class IntegerType : public Type { +class IntegerType : public Type +{ public: explicit IntegerType(unsigned num_bits, Module *m); @@ -89,7 +91,8 @@ class IntegerType : public Type { unsigned num_bits_; }; -class FunctionType : public Type { +class FunctionType : public Type +{ public: FunctionType(Type *result, std::vector params); @@ -100,17 +103,18 @@ class FunctionType : public Type { unsigned get_num_of_args() const; - Type *get_param_type(unsigned i) const; + Type *get_param_type(unsigned i) const; std::vector::iterator param_begin() { return args_.begin(); } std::vector::iterator param_end() { return args_.end(); } - Type *get_return_type() const; + Type *get_return_type() const; private: - Type *result_; + Type *result_; std::vector args_; }; -class ArrayType : public Type { +class ArrayType : public Type +{ public: ArrayType(Type *contained, unsigned num_elements); @@ -118,15 +122,16 @@ class ArrayType : public Type { static ArrayType *get(Type *contained, unsigned num_elements); - Type *get_element_type() const { return contained_; } + Type *get_element_type() const { return contained_; } unsigned get_num_of_elements() const { return num_elements_; } private: - Type *contained_; // The element type of the array. + Type *contained_; // The element type of the array. unsigned num_elements_; // Number of elements in the array. }; -class PointerType : public Type { +class PointerType : public Type +{ public: PointerType(Type *contained); Type *get_element_type() const { return contained_; } @@ -137,7 +142,8 @@ class PointerType : public Type { Type *contained_; // The element type of the ptr. }; -class FloatType : public Type { +class FloatType : public Type +{ public: FloatType(Module *m); static FloatType *get(Module *m); @@ -145,4 +151,4 @@ class FloatType : public Type { private: }; -#endif // SYSYC_TYPE_H \ No newline at end of file +#endif // SYSYC_TYPE_H diff --git a/tests/2-ir-gen-warmup/c_cases/.gitignore b/tests/2-ir-gen-warmup/c_cases/.gitignore new file mode 100644 index 0000000..fd64565 --- /dev/null +++ b/tests/2-ir-gen-warmup/c_cases/.gitignore @@ -0,0 +1 @@ +*.ll diff --git a/tests/2-ir-gen-warmup/stu_ll/assign_hand.ll b/tests/2-ir-gen-warmup/stu_ll/assign_hand.ll index e69de29..5e427ea 100644 --- a/tests/2-ir-gen-warmup/stu_ll/assign_hand.ll +++ b/tests/2-ir-gen-warmup/stu_ll/assign_hand.ll @@ -0,0 +1,14 @@ +define dso_local i32 @main() #0 { + %a = alloca [10 x i32], align 4 + ; ptr is the pointer with type i32* + %ptr = getelementptr [10 x i32], [10 x i32]* %a, i64 0, i64 0 + %a0 = getelementptr i32, i32* %ptr, i64 0 + %a1 = getelementptr i32, i32* %ptr, i64 1 + store i32 10, i32* %a0 + %v1 = load i32, i32* %a0 + %v2 = mul i32 %v1, 2 + store i32 %v2, i32* %a1 + %r = load i32, i32* %a1 + ret i32 %r + +} diff --git a/tests/2-ir-gen-warmup/stu_ll/fun_hand.ll b/tests/2-ir-gen-warmup/stu_ll/fun_hand.ll index e69de29..e776379 100644 --- a/tests/2-ir-gen-warmup/stu_ll/fun_hand.ll +++ b/tests/2-ir-gen-warmup/stu_ll/fun_hand.ll @@ -0,0 +1,9 @@ +define dso_local i32 @callee(i32 %0) #0 { + %r = mul i32 %0, 2 + ret i32 %r +} + +define dso_local i32 @main() #0 { + %r = call i32 @callee(i32 110) + ret i32 %r +} diff --git a/tests/2-ir-gen-warmup/stu_ll/if_hand.ll b/tests/2-ir-gen-warmup/stu_ll/if_hand.ll index e69de29..305d087 100644 --- a/tests/2-ir-gen-warmup/stu_ll/if_hand.ll +++ b/tests/2-ir-gen-warmup/stu_ll/if_hand.ll @@ -0,0 +1,11 @@ +define i32 @main(){ + %a_ptr = alloca float + store float 0x40163851E0000000, float* %a_ptr + %a = load float, float* %a_ptr + %cond = fcmp ugt float %a, 1.000000e+00 + br i1 %cond, label %1, label %2 +1: + ret i32 233 +2: + ret i32 0 +} diff --git a/tests/2-ir-gen-warmup/stu_ll/while_hand.ll b/tests/2-ir-gen-warmup/stu_ll/while_hand.ll index e69de29..12e19a1 100644 --- a/tests/2-ir-gen-warmup/stu_ll/while_hand.ll +++ b/tests/2-ir-gen-warmup/stu_ll/while_hand.ll @@ -0,0 +1,25 @@ +define dso_local i32 @main() #0 { + %pa = alloca i32 + %pi = alloca i32 + store i32 10, i32* %pa + store i32 0, i32* %pi + br label %loop_head +loop_head: + %i = load i32, i32* %pi + %cond = icmp slt i32 %i, 10 + br i1 %cond, label %loop_body, label %over +loop_body: + %i_1 = add i32 %i, 1 + store i32 %i_1, i32* %pi + + %newi = load i32, i32* %pi + %a = load i32, i32* %pa + %a_i = add i32 %a, %newi + store i32 %a_i, i32* %pa + br label %loop_head + + +over: + %r = load i32, i32* %pa + ret i32 %r +} diff --git a/tests/2-ir-gen-warmup/ta_gcd/.gitignore b/tests/2-ir-gen-warmup/ta_gcd/.gitignore new file mode 100644 index 0000000..fd64565 --- /dev/null +++ b/tests/2-ir-gen-warmup/ta_gcd/.gitignore @@ -0,0 +1 @@ +*.ll diff --git a/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp b/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp index f8f63a8..8b49c52 100644 --- a/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp +++ b/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp @@ -8,163 +8,176 @@ #include #include -#ifdef DEBUG // 用于调试信息,大家可以在编译过程中通过" -DDEBUG"来开启这一选项 -#define DEBUG_OUTPUT std::cout << __LINE__ << std::endl; // 输出行号的简单示例 -#else -#define DEBUG_OUTPUT -#endif - -#define CONST_INT(num) ConstantInt::get(num, module) - -#define CONST_FP(num) ConstantFP::get(num, module) // 得到常数值的表示,方便后面多次用到 - -int main() { - auto module = new Module("Cminus code"); // module name是什么无关紧要 - auto builder = new IRBuilder(nullptr, module); - Type *Int32Type = Type::get_int32_type(module); - - // 全局数组,x,y - auto *arrayType = ArrayType::get(Int32Type, 1); - auto initializer = ConstantZero::get(Int32Type, module); - auto x = GlobalVariable::create("x", module, arrayType, false, initializer);// 参数解释: 名字name,所属module,全局变量类型type, - auto y = GlobalVariable::create("y", module, arrayType, false, initializer);// 是否是常量定义(cminus中没有常量概念,应全都是false),初始化常量(ConstantZero类) - - // gcd函数 - // 函数参数类型的vector - std::vector Ints(2, Int32Type); - - //通过返回值类型与参数类型列表得到函数类型 - auto gcdFunTy = FunctionType::get(Int32Type, Ints); - - // 由函数类型得到函数 - auto gcdFun = Function::create(gcdFunTy, - "gcd", module); - - // BB的名字在生成中无所谓,但是可以方便阅读 - auto bb = BasicBlock::create(module, "entry", gcdFun); - - builder->set_insert_point(bb); // 一个BB的开始,将当前插入指令点的位置设在bb - - auto retAlloca = builder->create_alloca(Int32Type); // 在内存中分配返回值的位置 - auto uAlloca = builder->create_alloca(Int32Type); // 在内存中分配参数u的位置 - auto vAlloca = builder->create_alloca(Int32Type); // 在内存中分配参数v的位置 - - std::vector args; // 获取gcd函数的形参,通过Function中的iterator - for (auto arg = gcdFun->arg_begin(); arg != gcdFun->arg_end(); arg++) { - args.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素 - } - - builder->create_store(args[0], uAlloca); // 将参数u store下来 - builder->create_store(args[1], vAlloca); // 将参数v store下来 - - auto vLoad = builder->create_load(vAlloca); // 将参数v load上来 - auto icmp = builder->create_icmp_eq(vLoad, CONST_INT(0)); // v和0的比较,注意ICMPEQ - - auto trueBB = BasicBlock::create(module, "trueBB", gcdFun); // true分支 - auto falseBB = BasicBlock::create(module, "falseBB", gcdFun); // false分支 - auto retBB = BasicBlock::create( - module, "", gcdFun); // return分支,提前create,以便true分支可以br - - auto br = builder->create_cond_br(icmp, trueBB, falseBB); // 条件BR - DEBUG_OUTPUT // 调试的时候故意留下来的,以醒目地提醒你这个调试用的宏定义方法 - builder->set_insert_point(trueBB); // if true; 分支的开始需要SetInsertPoint设置 - auto uLoad = builder->create_load(uAlloca); - builder->create_store(uLoad, retAlloca); - builder->create_br(retBB); // br retBB - - builder->set_insert_point(falseBB); // if false - uLoad = builder->create_load(uAlloca); - vLoad = builder->create_load(vAlloca); - auto div = builder->create_isdiv(uLoad, vLoad); // SDIV - div with S flag - auto mul = builder->create_imul(div, vLoad); // MUL - mul - auto sub = builder->create_isub(uLoad, mul); // the same - auto call = builder->create_call(gcdFun, {vLoad, sub}); // 创建call指令 - // {vLoad, sub} - 参数array - builder->create_store(call, retAlloca); - builder->create_br(retBB); // br retBB - - builder->set_insert_point(retBB); // ret分支 - auto retLoad = builder->create_load(retAlloca); - builder->create_ret(retLoad); - - // funArray函数 - auto Int32PtrType = Type::get_int32_ptr_type(module); // 单个参数的类型,指针 - std::vector IntPtrs(2, Int32PtrType); // 参数列表类型 - auto funArrayFunType = FunctionType::get(Int32Type, IntPtrs); // 函数类型 - auto funArrayFun = Function::create(funArrayFunType, "funArray", module); - bb = BasicBlock::create(module, "entry", funArrayFun); - builder->set_insert_point(bb); - auto upAlloca = builder->create_alloca(Int32PtrType); // u的存放 - auto vpAlloca = builder->create_alloca(Int32PtrType); // v的存放 - auto aAlloca = builder->create_alloca(Int32Type); // a的存放 - auto bAlloca = builder->create_alloca(Int32Type); // b的存放 - auto tempAlloca = builder->create_alloca(Int32Type); // temp的存放 - - std::vector args1; //获取funArrayFun函数的形参,通过Function中的iterator - for (auto arg = funArrayFun->arg_begin(); arg != funArrayFun->arg_end(); arg++) { - args1.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素 - } - builder->create_store(args1[0], upAlloca); // 将参数u store下来 - builder->create_store(args1[1], vpAlloca); // 将参数v store下来 - - auto u0pLoad = builder->create_load(upAlloca); // 读取u - auto u0GEP = builder->create_gep(u0pLoad, {CONST_INT(0)}); // GEP: 获取u[0]地址 - auto u0Load = builder->create_load(u0GEP); // 从u[0]地址 读取u[0] - builder->create_store(u0Load, aAlloca); // 将u[0] 写入 a - auto v0pLoad = builder->create_load(vpAlloca); // 同上 - auto v0GEP = builder->create_gep(v0pLoad, {CONST_INT(0)}); - auto v0Load = builder->create_load(v0GEP); - builder->create_store(v0Load, bAlloca); - - auto aLoad = builder->create_load(aAlloca); - auto bLoad = builder->create_load(bAlloca); - icmp = builder->create_icmp_lt(aLoad, bLoad); - trueBB = BasicBlock::create(module, "trueBB", funArrayFun); - falseBB = BasicBlock::create(module, "falseBB", funArrayFun); - builder->create_cond_br(icmp, trueBB, falseBB); - - builder->set_insert_point(trueBB); - builder->create_store(aLoad, tempAlloca); - builder->create_store(bLoad, aAlloca); - auto tempLoad = builder->create_load(tempAlloca); - builder->create_store(tempLoad, bAlloca); - builder->create_br(falseBB); // 注意在下一个BB之前要Br一下 - - builder->set_insert_point(falseBB); - aLoad = builder->create_load(aAlloca); - bLoad = builder->create_load(bAlloca); - call = builder->create_call(gcdFun, {aLoad, bLoad}); - builder->create_ret(call); - - - // main函数 - auto mainFun = Function::create(FunctionType::get(Int32Type, {}), - "main", module); - bb = BasicBlock::create(module, "entry", mainFun); - // BasicBlock的名字在生成中无所谓,但是可以方便阅读 - builder->set_insert_point(bb); - - retAlloca = builder->create_alloca(Int32Type); - builder->create_store(CONST_INT(0), retAlloca); // 默认 ret 0 - - auto x0GEP = builder->create_gep(x, {CONST_INT(0), CONST_INT(0)}); // GEP: 这里为什么是{0, 0}呢? (实验报告相关) - builder->create_store(CONST_INT(90), x0GEP); - auto y0GEP = builder->create_gep(y, {CONST_INT(0), CONST_INT(0)}); // GEP: 这里为什么是{0, 0}呢? (实验报告相关) - builder->create_store(CONST_INT(18), y0GEP); - - x0GEP = builder->create_gep(x, {CONST_INT(0), CONST_INT(0)}); - y0GEP = builder->create_gep(y, {CONST_INT(0), CONST_INT(0)}); - call = builder->create_call(funArrayFun, {x0GEP, y0GEP}); // 为什么这里传的是{x0GEP, y0GEP}呢? - - builder->create_ret(call); - // 尽管已经有很多注释,但可能还是会遇到很多bug - // 所以强烈建议配置AutoComplete,效率会大大提高! - // 如果猜不到某个IR指令对应的C++的函数,建议把指令翻译成英语然后在method列表中搜索一下。 - // 最后,这个例子只涉及到了一些基本的指令生成, - // 对于额外的指令,包括数组,在之后的实验中可能需要大家自己搜索一下思考一下, - // 还有涉及到的C++语法,可以及时提问或者向大家提供指导哦! - // 对于这个例子里的代码风格/用法,如果有好的建议也欢迎提出! - std::cout << module->print(); - delete module; - return 0; +/* #ifdef DEBUG // 用于调试信息,大家可以在编译过程中通过" -DDEBUG"来开启这一选项 + * #define DEBUG_OUTPUT std::cout << __LINE__ << std::endl; // + * 输出行号的简单示例 #else #define DEBUG_OUTPUT #endif */ + +#define DEBUG_OUTPUT std::cout << __LINE__ << std::endl; // 输出行号的简单示例 + +#define CONST_INT(num) ConstantInt::get(num, mod) + +#define CONST_FP(num) \ + ConstantFP::get(num, mod) // 得到常数值的表示,方便后面多次用到 + +int main() +{ + auto mod = new Module("Cminus code"); // module name是什么无关紧要 + auto builder = new IRBuilder(nullptr, mod); + Type *Int32Type = Type::get_int32_type(mod); + + // 全局数组,x,y + auto *arrayType = ArrayType::get(Int32Type, 1); + auto initializer = ConstantZero::get(Int32Type, mod); + // 参数解释: 名字name,所属module,全局变量类型type, + auto x = GlobalVariable::create("x", mod, arrayType, false, initializer); + // 是否是常量定义(cminus中没有常量概念,应全都是false),初始化常量(ConstantZero类) + auto y = GlobalVariable::create("y", mod, arrayType, false, initializer); + + // gcd函数 + // 函数参数类型的vector + std::vector Ints(2, Int32Type); + + // 通过返回值类型与参数类型列表得到函数类型 + auto gcdFunTy = FunctionType::get(Int32Type, Ints); + + // 由函数类型得到函数 + auto gcdFun = Function::create(gcdFunTy, "gcd", mod); + + // BB的名字在生成中无所谓,但是可以方便阅读 + auto bb = BasicBlock::create(mod, "entry", gcdFun); + + builder->set_insert_point(bb); // 一个BB的开始,将当前插入指令点的位置设在bb + + auto retAlloca = + builder->create_alloca(Int32Type); // 在内存中分配返回值的位置 + auto uAlloca = builder->create_alloca(Int32Type); // 在内存中分配参数u的位置 + auto vAlloca = builder->create_alloca(Int32Type); // 在内存中分配参数v的位置 + + std::vector args; // 获取gcd函数的形参,通过Function中的iterator + for (auto arg = gcdFun->arg_begin(); arg != gcdFun->arg_end(); arg++) { + args.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素 + } + + builder->create_store(args[0], uAlloca); // 将参数u store下来 + builder->create_store(args[1], vAlloca); // 将参数v store下来 + + auto vLoad = builder->create_load(vAlloca); // 将参数v load上来 + auto icmp = + builder->create_icmp_eq(vLoad, CONST_INT(0)); // v和0的比较,注意ICMPEQ + + auto trueBB = BasicBlock::create(mod, "trueBB", gcdFun); // true分支 + auto falseBB = BasicBlock::create(mod, "falseBB", gcdFun); // false分支 + auto retBB = BasicBlock::create( + mod, "", gcdFun); // return分支,提前create,以便true分支可以br + + auto br = builder->create_cond_br(icmp, trueBB, falseBB); // 条件BR + DEBUG_OUTPUT // 调试的时候故意留下来的,以醒目地提醒你这个调试用的宏定义方法 + builder->set_insert_point( + trueBB); // if true; 分支的开始需要SetInsertPoint设置 + auto uLoad = builder->create_load(uAlloca); + builder->create_store(uLoad, retAlloca); + builder->create_br(retBB); // br retBB + + builder->set_insert_point(falseBB); // if false + uLoad = builder->create_load(uAlloca); + vLoad = builder->create_load(vAlloca); + auto div = builder->create_isdiv(uLoad, vLoad); // SDIV - div with S flag + auto mul = builder->create_imul(div, vLoad); // MUL - mul + auto sub = builder->create_isub(uLoad, mul); // the same + auto call = builder->create_call(gcdFun, {vLoad, sub}); // 创建call指令 + // {vLoad, sub} - 参数array + builder->create_store(call, retAlloca); + builder->create_br(retBB); // br retBB + + builder->set_insert_point(retBB); // ret分支 + auto retLoad = builder->create_load(retAlloca); + builder->create_ret(retLoad); + + // funArray函数 + auto Int32PtrType = Type::get_int32_ptr_type(mod); // 单个参数的类型,指针 + std::vector IntPtrs(2, Int32PtrType); // 参数列表类型 + auto funArrayFunType = FunctionType::get(Int32Type, IntPtrs); // 函数类型 + auto funArrayFun = Function::create(funArrayFunType, "funArray", mod); + bb = BasicBlock::create(mod, "entry", funArrayFun); + builder->set_insert_point(bb); + auto upAlloca = builder->create_alloca(Int32PtrType); // u的存放 + auto vpAlloca = builder->create_alloca(Int32PtrType); // v的存放 + auto aAlloca = builder->create_alloca(Int32Type); // a的存放 + auto bAlloca = builder->create_alloca(Int32Type); // b的存放 + auto tempAlloca = builder->create_alloca(Int32Type); // temp的存放 + + std::vector + args1; //获取funArrayFun函数的形参,通过Function中的iterator + for (auto arg = funArrayFun->arg_begin(); arg != funArrayFun->arg_end(); + arg++) { + args1.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素 + } + builder->create_store(args1[0], upAlloca); // 将参数u store下来 + builder->create_store(args1[1], vpAlloca); // 将参数v store下来 + + auto u0pLoad = builder->create_load(upAlloca); // 读取u + auto u0GEP = + builder->create_gep(u0pLoad, {CONST_INT(0)}); // GEP: 获取u[0]地址 + auto u0Load = builder->create_load(u0GEP); // 从u[0]地址 读取u[0] + builder->create_store(u0Load, aAlloca); // 将u[0] 写入 a + auto v0pLoad = builder->create_load(vpAlloca); // 同上 + auto v0GEP = builder->create_gep(v0pLoad, {CONST_INT(0)}); + auto v0Load = builder->create_load(v0GEP); + builder->create_store(v0Load, bAlloca); + + auto aLoad = builder->create_load(aAlloca); + auto bLoad = builder->create_load(bAlloca); + icmp = builder->create_icmp_lt(aLoad, bLoad); + trueBB = BasicBlock::create(mod, "trueBB", funArrayFun); + falseBB = BasicBlock::create(mod, "falseBB", funArrayFun); + builder->create_cond_br(icmp, trueBB, falseBB); + + builder->set_insert_point(trueBB); + builder->create_store(aLoad, tempAlloca); + builder->create_store(bLoad, aAlloca); + auto tempLoad = builder->create_load(tempAlloca); + builder->create_store(tempLoad, bAlloca); + builder->create_br(falseBB); // 注意在下一个BB之前要Br一下 + + builder->set_insert_point(falseBB); + aLoad = builder->create_load(aAlloca); + bLoad = builder->create_load(bAlloca); + call = builder->create_call(gcdFun, {aLoad, bLoad}); + builder->create_ret(call); + + // main函数 + auto mainFun = + Function::create(FunctionType::get(Int32Type, {}), "main", mod); + bb = BasicBlock::create(mod, "entry", mainFun); + // BasicBlock的名字在生成中无所谓,但是可以方便阅读 + builder->set_insert_point(bb); + + retAlloca = builder->create_alloca(Int32Type); + builder->create_store(CONST_INT(0), retAlloca); // 默认 ret 0 + // + // GEP: 这里为什么是{0, 0}呢? (实验报告相关) + auto x0GEP = builder->create_gep(x, {CONST_INT(0), CONST_INT(0)}); + builder->create_store(CONST_INT(90), x0GEP); + // + // GEP: 这里为什么是{0, 0}呢? (实验报告相关) + auto y0GEP = builder->create_gep(y, {CONST_INT(0), CONST_INT(0)}); + builder->create_store(CONST_INT(18), y0GEP); + + x0GEP = builder->create_gep(x, {CONST_INT(0), CONST_INT(0)}); + y0GEP = builder->create_gep(y, {CONST_INT(0), CONST_INT(0)}); + // + // 为什么这里传的是{x0GEP, y0GEP}呢? + call = builder->create_call(funArrayFun, {x0GEP, y0GEP}); + + builder->create_ret(call); + // 尽管已经有很多注释,但可能还是会遇到很多bug + // 所以强烈建议配置AutoComplete,效率会大大提高! + // 如果猜不到某个IR指令对应的C++的函数,建议把指令翻译成英语然后在method列表中搜索一下。 + // 最后,这个例子只涉及到了一些基本的指令生成, + // 对于额外的指令,包括数组,在之后的实验中可能需要大家自己搜索一下思考一下, + // 还有涉及到的C++语法,可以及时提问或者向大家提供指导哦! + // 对于这个例子里的代码风格/用法,如果有好的建议也欢迎提出! + std::cout << mod->print(); + delete mod; + return 0; } -- GitLab