编译原理Lab4-使用LightIR框架自动产生cminus-f语言的LLVM IR

news2024/11/28 19:01:09
  • [[#实验框架|实验框架]]
  • [[#实验过程|实验过程]]
    • [[#实验过程#全局变量的设计|全局变量的设计]]
    • [[#实验过程#1ASTProgram|1ASTProgram]]
    • [[#实验过程#2ASTNum|2ASTNum]]
    • [[#实验过程#3ASTVarDeclaration|3ASTVarDeclaration]]
    • [[#实验过程#4ASTFunDeclaration|4ASTFunDeclaration]]
    • [[#实验过程#5ASTParam|5ASTParam]]
    • [[#实验过程#6ASTCompoundStmt|6ASTCompoundStmt]]
    • [[#实验过程#7ASTExpressionStmt|7ASTExpressionStmt]]
    • [[#实验过程#8ASTSelectionStmt|8ASTSelectionStmt]]
    • [[#实验过程#9ASTIterationStmt|9ASTIterationStmt]]
    • [[#实验过程#10ASTReturnStmt|10ASTReturnStmt]]
    • [[#实验过程#11ASTVar|11ASTVar]]
    • [[#实验过程#12ASTAssignExpression|12ASTAssignExpression]]
    • [[#实验过程#13ASTSimpleExpression|13ASTSimpleExpression]]
    • [[#实验过程#14ASTAdditiveExpression|14ASTAdditiveExpression]]
    • [[#实验过程#15ASTTerm|15ASTTerm]]
    • [[#实验过程#16ASTCall|16ASTCall]]
  • [[#编译|编译]]
    • [[#编译#结果验证|结果验证]]
  • [[#实验总结|实验总结]]
  • [[#参考|参考]]

实验要求

需要使用 LightIR 框架自动产生 cminus-f 语言的LLVM IR。
要编写的代码是:修改 src/cminusfc/cminusf_builder.cpp ,其内含各个 visit 函数,我们需要补全这些 visit 的函数。来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序
要掌握 logging 工具,仔细看 Light IR 文档(主要看 C++ APIs 部分),仔细看cminus-f 的语法与语义

实验难点

  • 函数没有返回值,都是void类型,对于一些有计算结果的代码不好处理。但是实验有规定不能修改实验代码,用全局变量来存储返回值也是一个不错的选择,关键在于很正确的调用这些函数并且逻辑不出现错误,这样才能正确的传递信息
  • 在生成代码的时候需要对不符合要求的数值进行数据转换。

实验框架

本次实验使用了由C++编写的 LightIR 来生成 LLVM IR。为了便于大家进行实验,该框架自动完成了语法树到 C++ 上的抽象语法树的转换。 我们可以使用访问者模式来设计抽象语法树中的算法。大家可以参考打印抽象语法树的算法, 以及运行 test_ast 来理解访问者模式下算法的执行流程。

在include/cminusf_builder.hpp中,我还定义了一个用于存储作用域的类Scope。它的作用是辅助我们在遍历语法树时,管理不同作用域中的变量。它提供了以下接口:

// 进入一个新的作用域
void enter();
// 退出一个作用域
void exit();
// 往当前作用域插入新的名字->值映射
bool push(std::string name, Value *val);
// 根据名字,寻找到值
Value* find(std::string name);
// 判断当前是否在全局作用域内
bool in_global();

你们需要根据语义合理调用enter与exit,并且在变量声明和使用时正确调用push与find。在类CminusfBuilder中,有一个Scope类型的成员变量scope,它在初始化时已经将input、output等函数加入了作用域中。因此,你们在进行名字查找时不需要顾虑是否需要对特殊函数进行特殊操作。

实验过程

将实验仓库克隆到本地
打开本地的工作目录,在命令行中输入
git clone https://gitee.com/你的gitee用户名/cminus_compiler-2022-fall.git

全局变量的设计

#include "cminusf_builder.hpp"
#include "logging.hpp"
// use these macros to get constant value
// 得到常数值的表示,方便后面多次用到
#define CONST_INT(num) \
    ConstantInt::get(num, module.get())

#define CONST_FP(num) \
    ConstantFP::get(num, module.get())
    
// You can define global variables here
// to store state
Value *ret;               //存储返回值
std::vector<Type *> Ints; //储存参数的类型,以确定函数的类型
int return_flag = 0;       //全局变量标识当前模块是否已经有return语句

1ASTProgram

ASTProgram 语义规则为program -> declaration-list,遍历program下层的declaration-list中的声明即可

void CminusfBuilder::visit(ASTProgram &node) //遍历program下层的declaration-list中的声明
//program -> declaration-list
{
    for (auto decl : node.declarations)

    {
        decl->accept(*this);
    }
}

2ASTNum

ASTNum判断node的类型(整型或浮点型),将将相应的value值存入,并将结果赋给ret,这里的ret是一个 全局变量。

void CminusfBuilder::visit(ASTNum &node) //判断node的类型(整型或浮点型),将将相应的value值存入,并将地址赋给ret

{

    if(return_flag)

        return;

    if (node.type == TYPE_INT)

    {

        ret = CONST_INT(node.i_val);

    }

    else if (node.type == TYPE_FLOAT)

    {

        ret = CONST_FP(node.f_val);

    }

}

3ASTVarDeclaration

语义规则为var-declaration -> type-specifier ID ; ∣ type-specifier ID [INTEGER ],变量要么是整型或浮点型,要么是数组。 首先使用scope.in_global判断是否为全局变量,然后根据node.num是否为空判断是否为数组变量 (若不为空则为数组变量),接着根据node.type判断是整型还是浮点型,最后将变量名和变量值 压入scope中。

void CminusfBuilder::visit(ASTVarDeclaration &node) //变量声明
//var-declaration -> type-specifier ID ; ∣ type-specifier ID [INTEGER ]
{
    if(return_flag)
        return;
    auto TyInt32 = Type::get_int32_type(module.get());
    auto TyFloat = Type::get_float_type(module.get());
    if (!scope.in_global()) //局部
    {
        if (node.num != nullptr) //数组(指针非空
        {
            if(!node.num->i_val)
            {
            	Value * call_error = scope.find("neg_idx_except");//数组定义是大小为零时,打印报错信息
            	builder->create_call(call_error,{});
            }
            if (node.type == TYPE_INT) //整型数组
            {
                auto *arrayType = ArrayType::get(TyInt32, node.num->i_val);
                auto Local_IntArrayAlloca = builder->create_alloca(arrayType); //为数组分配空间
                scope.push(node.id, Local_IntArrayAlloca);
            }
            else if (node.type == TYPE_FLOAT) //浮点型数组
            {
                auto *arrayType = ArrayType::get(TyFloat, node.num->i_val);
                auto Local_FloatArrayAlloca = builder->create_alloca(arrayType); //为数组分配空间
                scope.push(node.id, Local_FloatArrayAlloca);
            }
        }
        else //变量
        {
            if (node.type == TYPE_INT) //整型变量
            {
                auto Local_IntAlloca = builder->create_alloca(TyInt32); //为变量分配空间
                scope.push(node.id, Local_IntAlloca);
            }
            else if (node.type == TYPE_FLOAT) //浮点型变量
            {
                auto Local_FloatAlloca = builder->create_alloca(TyFloat); //为变量分配空间
                scope.push(node.id, Local_FloatAlloca);
            }
        }
    }
    else //全局
    {
        if (node.num != nullptr ) //数组(指针非空
        {
            if(!node.num->i_val)
            {
            	Value * call_error = scope.find("neg_idx_except");
            	builder->create_call(call_error,{});
            }
            if (node.type == TYPE_INT) //整型数组
            {
                auto *arrayType = ArrayType::get(TyInt32, node.num->i_val);
                auto initializer = ConstantZero::get(arrayType, module.get());
                auto Globle_IntArrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer); //为数组分配空间
                scope.push(node.id, Globle_IntArrayAlloca);
            }
            else if (node.type == TYPE_FLOAT) //浮点型数组
            {
                auto *arrayType = ArrayType::get(TyFloat, node.num->i_val);
                auto initializer = ConstantZero::get(arrayType, module.get()); //初始值赋为零
                auto Globle_FloatArrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer); //为数组分配空间
                scope.push(node.id, Globle_FloatArrayAlloca);
            }
        }
        else //变量
        {
            if (node.type == TYPE_INT) //整型变量
            {
                auto initializer = ConstantZero::get(TyInt32, module.get());
                auto Globle_IntAlloca = GlobalVariable::create(node.id, module.get(), TyInt32, false, initializer); //为变量分配空间
                scope.push(node.id, Globle_IntAlloca);
            }
            else if (node.type == TYPE_FLOAT) //浮点型变量
            {
                auto initializer = ConstantZero::get(TyFloat, module.get());
                auto Globle_FloatAlloca = GlobalVariable::create(node.id, module.get(), TyFloat, false, initializer); //为变量分配空间
                scope.push(node.id, Globle_FloatAlloca);
            }
        }
    }
}

4ASTFunDeclaration

语义规则为fun-declaration → type-specifier ID ( params ) compound-stmt 首先要用create构造函数,那么就要得到函数的类型和参数列表,根据node.type得到函数类型, 然后遍历node.params,对于每一个param,得到其参数类型。若param为数组,则开辟数组空间,若param为整型或浮点型变量,则开辟单个变量的空间,然后把参数store下来。最后处理 compound_stmt。

void CminusfBuilder::visit(ASTFunDeclaration &node) //函数声明

//fun-declaration → type-specifier ID ( params ) compound-stmt

{

    scope.enter(); //进入函数的作用域

    Type *TYPE32 = Type::get_int32_type(module.get());

    Type *TyFloat = Type::get_float_type(module.get());

    Type *TYPEV = Type::get_void_type(module.get());

    Type *TYPEARRAY_32 = PointerType::get_int32_ptr_type(module.get());

    Type *funType;

    Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());

    Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());

    //判断新声明的function的返回值类型

    if (node.type == TYPE_FLOAT)

        funType = TyFloat;

    else if(node.type == TYPE_VOID)

    {

        funType = TYPEV;

    }

    else

    {

    	funType = TYPE32;

    }

    // 函数参数的vector

    std::vector<Value *> args;

    if (node.params.size() > 0) //参数列表非空

    {

        for (auto param : node.params) //遍历Fundeclaration下的param节点,得到参数类型

        {

            param->accept(*this); //得到参数类型

        }

        auto fun = Function::create(FunctionType::get(funType, Ints), node.id, module.get()); //由函数类型定义函数

        auto bb = BasicBlock::create(module.get(), "entry", fun); // BB的名字在生成中无所谓,但是可以方便阅读

        builder->set_insert_point(bb);

        scope.exit(); //先退出当前作用域

        scope.push(node.id, fun); //函数名放进作用域

        scope.enter(); //再次进入函数的作用域

        for (auto param : node.params) //遍历Fundeclaration下的param节点,根据参数的类型分配空间

        {

            if (param->isarray) //数组

            {

                if (param->type == TYPE_INT) //整型数组

                {

                    auto pAlloca = builder->create_alloca(TYPEARRAY_INT_32); //在内存中分配空间

                    scope.push(param->id, pAlloca);

                }

                else if (param->type == TYPE_FLOAT)  //浮点型数组

                {

                    auto pAlloca = builder->create_alloca(TYPEARRAY_FLOAT_32);

                    scope.push(param->id, pAlloca);

                }

            }

            else if (param->type == TYPE_INT) //整型

            {

                auto pAlloca = builder->create_alloca(TYPE32);

                scope.push(param->id, pAlloca);

            }

            else if (param->type == TYPE_FLOAT) //浮点型

            {

                auto pAlloca = builder->create_alloca(TyFloat);

                scope.push(param->id, pAlloca);

            }

        }

        for (auto arg = fun->arg_begin(); arg != fun->arg_end(); arg++) //获取形参

        {

            args.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素

        }

        int i = 0;

        for (auto param : node.params) //将参数store下来

        {

            auto pAlloca = scope.find(param->id);

            if (pAlloca == nullptr)

                exit(0);

            else

                builder->create_store(args[i++], pAlloca);

            Ints.pop_back(); //清空向量

        }

    }

    else //参数列表为空

    {

        auto fun = Function::create(FunctionType::get(funType, Ints), node.id, module.get()); //由函数类型定义函数

        auto bb = BasicBlock::create(module.get(), "entry", fun);// BB的名字在生成中无所谓,但是可以方便阅读

        builder->set_insert_point(bb);

        scope.exit(); //先退出当前作用域

        scope.push(node.id, fun); //函数名放进作用域

        scope.enter(); //再次进入函数的作用域

    }

    node.compound_stmt->accept(*this); //执行compound-stmt

    if(return_flag == 0) //当前模块没有return

    {

        auto return_type = builder->get_insert_block()->get_parent()->get_return_type();

        if(return_type->is_void_type())

            builder->create_void_ret();

        else if(return_type->is_integer_type())

            builder->create_ret(CONST_INT(0));

        else

            builder->create_ret(CONST_FP(0));

    }

    return_flag = 0;

    scope.exit();

}

5ASTParam

语义规则为param -> type-specifier ID | type-specifier ID [],对于每一个传进来的参数node,将 其类型存入到全局变量Ints中。 若为数组类型则将数组类型push进Ints(参数列表)中,若为变量类型则将变量类型push进Ints (参数列表)中。

void CminusfBuilder::visit(ASTParam &node) //对于每一个传进来的参数node,将其类型存入到全局变量Ints中 //参数

//param -> type-specifier ID | type-specifier ID []
{
    Type *TYPE32 = Type::get_int32_type(module.get());
    Type *TyFloat = Type::get_float_type(module.get());
    Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());
    Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());
    //返回参数类型并分配空间
    if (node.isarray) //数组参数
    {
        if (node.type == TYPE_INT)
        {
            Ints.push_back(TYPEARRAY_INT_32);
        }
        else if (node.type == TYPE_FLOAT)
        {
            Ints.push_back(TYPEARRAY_FLOAT_32);
        }
    }
    else if (node.type == TYPE_INT) //整型
    {
        Ints.push_back(TYPE32);
    }
    else if (node.type == TYPE_FLOAT) //浮点型
    {
        Ints.push_back(TyFloat);
    }
    return;
}

6ASTCompoundStmt

语义规则为compound-stmt -> { local-declarations statement-list }。 处理每个局部声明和每个语句,然后退出作用域

void CminusfBuilder::visit(ASTCompoundStmt &node) //复合语句
// compound-stmt -> { local-declarations statement-list }
{
    if(return_flag)
        return;
    scope.enter(); //先进入到新的作用域,可能有局部变量
    for (auto loc_decl : node.local_declarations) //遍历所有local_declarations
    {
        loc_decl->accept(*this);
    }
    for (auto stmt : node.statement_list) //遍历所有statement-list
    {
        stmt->accept(*this);
    }
    scope.exit();
}

7ASTExpressionStmt

语义规则为expression-stmt→expression ; ∣ ;expression→assign-expression ∣ simple expression 若expression存在(即不为空),则处理。

void CminusfBuilder::visit(ASTExpressionStmt &node) //对expression结点调用accept //表达式语句

//expression-stmt→expression ; ∣ ;

//expression→assign-expression ∣ simple-expression

{

    if(return_flag)

        return;

    if (node.expression != nullptr)

    {

        node.expression->accept(*this);

    }

}

8ASTSelectionStmt

首先accpet判断语句,若为float则用fcmp与0比较,若为int则用icmp与0比较,若为指针则将地址 中的值load出来赋给ret后再进行比较。 然后判断是否存在else语句,若存在则创建true块和false块,若在块中不存在return,则都去到 next块,若不存在则只创建true块和next块,如果true块中没有return则去到next块。

void CminusfBuilder::visit(ASTSelectionStmt &node) //if
//selection-stmt→ ​if ( expression ) statement∣ if ( expression ) statement else statement​
{
    if(return_flag)
        return;
    Type *TYPE32 = Type::get_int32_type(module.get());
    node.expression->accept(*this);
    if (ret->get_type()->is_pointer_type()) //指针
        ret = builder->create_load(ret);
    if (ret->get_type()->is_float_type())
        ret = builder->create_fcmp_ne(ret, CONST_FP(0));
    else if (ret->get_type() == TYPE32)
        ret = builder->create_icmp_ne(ret, CONST_INT(0));
    //currentFunction
    auto currentFunc = builder->get_insert_block()->get_parent();
    auto trueBB = BasicBlock::create(module.get(), "", currentFunc); //创建true分支
    BasicBlock *falseBB;
    BasicBlock *nextBB;
    BranchInst *br;
    int insertedflag = 0;
    if (node.else_statement != nullptr) //有else
    {
        falseBB = BasicBlock::create(module.get(), "", currentFunc);
        br = builder->create_cond_br(ret, trueBB, falseBB);
        //falseBB
        builder->set_insert_point(falseBB);
        node.else_statement->accept(*this);
        if (builder->get_insert_block()->get_terminator() == nullptr)
        { // no return inside the block
            insertedflag = 1;
            nextBB = BasicBlock::create(module.get(), "", currentFunc);
            builder->create_br(nextBB);
        }
        return_flag = 0;
        //tureBB
        builder->set_insert_point(trueBB);
        node.if_statement->accept(*this);
        if (builder->get_insert_block()->get_terminator() == nullptr)
        { // no return inside the block
            if (insertedflag == 0)
            {
                insertedflag = 1;
                nextBB = BasicBlock::create(module.get(), "", currentFunc);
            }
            builder->create_br(nextBB);
        }
        return_flag = !insertedflag;
        //nextBB
        if (insertedflag == 1)
        {
            builder->set_insert_point(nextBB);
        }
    }
    else //无else
    {
        //tureBB
        nextBB = BasicBlock::create(module.get(), "", currentFunc);
        br = builder->create_cond_br(ret, trueBB, nextBB);
        builder->set_insert_point(trueBB);
        node.if_statement->accept(*this);
        if (return_flag == 0)
        {
            builder->create_br(nextBB);
        }
        return_flag = 0;
        //nextBB
        builder->set_insert_point(nextBB);
    }
}

9ASTIterationStmt

语义规则为iteration-stmt→while ( expression ) statement
首先创建loopJudge, loopBody和out块, 然后进入loopJudge块,accept判断语句,获得判断语句 的结果,若判断结果为int,则用icmp与0比较,若判断结果为float,则用fcmp与0比较,若为真, 则进入loopBody块,如果loopBody块中没有return,就再次进入loopJudge块;若为假,就进入 out块。

void CminusfBuilder::visit(ASTIterationStmt &node)//迭代语句 while
//iteration-stmt→while ( expression ) statement
{
    if(return_flag)
        return;
    Type *TYPE32 = Type::get_int32_type(module.get());
    //currentFunction
    auto currentFunc = builder->get_insert_block()->get_parent();
    auto loopJudge = BasicBlock::create(module.get(), "", currentFunc);
    auto loopBody = BasicBlock::create(module.get(), "", currentFunc);
    auto out = BasicBlock::create(module.get(), "", currentFunc);
    if (builder->get_insert_block()->get_terminator() == nullptr)
        builder->create_br(loopJudge);
    //loopJudge BB
    builder->set_insert_point(loopJudge);
    node.expression->accept(*this);
    if (ret->get_type()->is_pointer_type())
        ret = builder->create_load(ret);
    if (ret->get_type()->is_float_type())
        ret = builder->create_fcmp_ne(ret, CONST_FP(0));
    else if (ret->get_type() == TYPE32)
        ret = builder->create_icmp_ne(ret, CONST_INT(0));
    auto br = builder->create_cond_br(ret, loopBody, out);
    //loopBody BB
    builder->set_insert_point(loopBody);
    node.statement->accept(*this);
    if (builder->get_insert_block()->get_terminator() == nullptr)
        builder->create_br(loopJudge);
    return_flag = 0;
    //outloop BB
    builder->set_insert_point(out);
}

10ASTReturnStmt

语义规则为return-stmt→return ; ∣ return expression ;
首先得到返回值类型,若为void则创建void返回,若为指针则将地址中的值load出来然后返回,若 为整型或浮点型则做类型匹配,最后用create_ret对返回值进行返回。

void CminusfBuilder::visit(ASTReturnStmt &node) //return语句 //强制转换

//return-stmt→return ; ∣ return expression ;

{

    if(return_flag)

        return;

    Type *TYPE32 = Type::get_int32_type(module.get());

    Type *TYPE1 = Type::get_int1_type(module.get());

    Type *TyFloat = Type::get_float_type(module.get());

    auto return_type = builder->get_insert_block()->get_parent()->get_return_type();

    if (node.expression == nullptr) //空指针

    {

        if (!return_type->is_void_type())

            printf("return_type is not void, but expression is empty\n");

        builder->create_void_ret();

    }

    else

    {

        node.expression->accept(*this);

        if (return_type->is_void_type()) //void型

        {

            printf("return_type is void, but expression is not empty\n");

            builder->create_void_ret();

            return;

        }

        if (ret->get_type()->is_pointer_type()) //ret为指针型

            ret = builder->create_load(ret);

        if (return_type == TYPE32) //转化为int型

        {

            if (ret->get_type() == TYPE1) //布尔型

                ret = builder->create_zext(ret, TYPE32);

            else if (ret->get_type() == TyFloat)

                ret = builder->create_fptosi(ret, TYPE32);

        }

        if (return_type == TyFloat) //转化为float型

        {

            if (ret->get_type()->is_integer_type())

                ret = builder->create_sitofp(ret, TyFloat);

        }

        builder->create_ret(ret);

    }

    return_flag = 1;

}

11ASTVar

语义规则为var→ID ∣ ID [ expression] var
在cminus-f中比较简单,既可以是整型也可以是取了下标后的数组。

void CminusfBuilder::visit(ASTVar &node) //变量

//var→ID ∣ ID [ expression]

{

    if(return_flag)

        return;

    Type *FloatPtrType = Type::get_float_ptr_type(module.get());

    Type *Int32PtrType = Type::get_int32_ptr_type(module.get());

    Type *TYPE32 = Type::get_int32_type(module.get());

    Type *TYPE1 = Type::get_int1_type(module.get());

    //currentFunction

    auto currentFunc = builder->get_insert_block()->get_parent();

    auto var = scope.find(node.id);

    if (var)

    {

        if (node.expression != nullptr) //数组

        {

            node.expression->accept(*this);

            Value *num = ret;

            //转化为int

            if (num->get_type()->is_pointer_type())

                num = builder->create_load(num);

            if (num->get_type() == TYPE1)

                num = builder->create_zext(num, TYPE32);

            else if (num->get_type()->is_float_type())

                num = builder->create_fptosi(num, TYPE32);

            //if num < 0; enter exphandBB

            auto exphandBB = BasicBlock::create(module.get(), "", currentFunc);

            auto normalBB = BasicBlock::create(module.get(), "", currentFunc);

            auto outBB = BasicBlock::create(module.get(), "", currentFunc);

            auto flagnum = builder->create_icmp_ge(num, CONST_INT(0));

            auto br = builder->create_cond_br(flagnum, normalBB, exphandBB);

            //normalBB

            builder->set_insert_point(normalBB);

            if (var->get_type()->get_pointer_element_type()->is_pointer_type())

            {

                //var is an array that sub func get from main func

                auto var_load = builder->create_load(var);

                var = builder->create_gep(var_load, {num});

            }

            else if (var->get_type()->get_pointer_element_type()->is_array_type())

            {

                //var is an id of array,get address of id[num]

                var = builder->create_gep(var, {CONST_INT(0), num});

            }

            else

            {

                printf("id is a float or int, but expression is not empty\n");

            }

            ret = var;

            builder->create_br(outBB);

            //exphandBB

            builder->set_insert_point(exphandBB);

            Value * call_error = scope.find("neg_idx_except");

            builder->create_call(call_error, {});

            builder->create_br(outBB);

            //outBB

            builder->set_insert_point(outBB);

        }

        else

        {

            if (!(var->get_type()->get_pointer_element_type()->is_float_type()) && !(var->get_type()->get_pointer_element_type()->is_integer_type()) && !(var->get_type()->get_pointer_element_type()->is_array_type()))

            {

                var = builder->create_load(var);

            }

            ret = var;

        }

    }

    else

    {

        printf("cannot find the var\n");

        return;

    }

}

12ASTAssignExpression

语义规则为assign-expression→var = expression
首先accept拿到表达式左边var的地址,然后accept拿到表达式右边expression的值,匹配左值和 右值的类型,把左值store到右值的地址中。

void CminusfBuilder::visit(ASTAssignExpression &node) //赋值 //强制转换

//assign-expression→var = expression

{

    if(return_flag)

        return;

    Type *TYPE32 = Type::get_int32_type(module.get());

    Type *TYPE1 = Type::get_int1_type(module.get());

    Type *TYPEFLOAT = Type::get_float_type(module.get());

    node.var.get()->accept(*this);

    Value *var = ret;

    node.expression.get()->accept(*this);

    if (var->get_type()->get_pointer_element_type()->is_float_type()) //指向的是float型

    {

        if (ret->get_type()->is_pointer_type())

            ret = builder->create_load(ret);

        if (ret->get_type()->is_integer_type())

            ret = builder->create_sitofp(ret, TYPEFLOAT);

        builder->create_store(ret, var);

    }

    else //指向的是int型

    {

        if (ret->get_type()->is_pointer_type())

            ret = builder->create_load(ret);

        if (ret->get_type() == TYPE1)

            ret = builder->create_zext(ret, TYPE32);

        else if (ret->get_type()->is_float_type())

            ret = builder->create_fptosi(ret, TYPE32);

        builder->create_store(ret, var);

    }

}

13ASTSimpleExpression

语义规则为simple-expression -> additive-expression relop additive- expression | additive expression

首先accept左边,再accept右边,然后判断左右两边的类型,若至少有一个为float,则把两个都转 成float,再判断op类型,左边和右边进行比较,将结果作为整数保存。

void CminusfBuilder::visit(ASTSimpleExpression &node) //简单表达式

//simple-expression -> additive-expression relop additive- expression | additive-expression

{

    if(return_flag)

        return;

    Type *Int32Type = Type::get_int32_type(module.get());

    Type *FloatType = Type::get_float_type(module.get());

    Type *Int1Type = Type::get_int1_type(module.get());

    //simple-expression -> additive-expression

    if (!node.additive_expression_r) //简单加法表达式,通过accept调用下一层级

    {

        node.additive_expression_l->accept(*this);

    }

    //simple-expression -> additive-expression relop additive- expression

    else //关系表达式,运算结果为整型1 或者 0

    {

        //获取左值和右值

        Value *AdditiveLoad_l;

        Value *AdditiveLoad_r;

        Value *icmp;

        node.additive_expression_l->accept(*this);

        if (ret->get_type()->is_pointer_type())

            AdditiveLoad_l = builder->create_load(ret);

        else

            AdditiveLoad_l = ret;

        node.additive_expression_r->accept(*this);

        if (ret->get_type()->is_pointer_type())

            AdditiveLoad_r = builder->create_load(ret);

        else

            AdditiveLoad_r = ret;

        int flag = 0; //标志是否为浮点数

        if (AdditiveLoad_l->get_type()->is_float_type()) //l是浮点数

        {

            flag = 1;

            if (AdditiveLoad_r->get_type()->is_integer_type())

                AdditiveLoad_r = builder->create_sitofp(AdditiveLoad_r, FloatType);

        }

        else

        {

            if (AdditiveLoad_r->get_type()->is_float_type()) //r是浮点数

            {

                flag = 1;

                AdditiveLoad_l = builder->create_sitofp(AdditiveLoad_l, FloatType);

            }

            else

            {

                flag = 0;

                if (AdditiveLoad_l->get_type() == Int1Type)

                    AdditiveLoad_l = builder->create_zext(AdditiveLoad_l, Int32Type);

                if (AdditiveLoad_r->get_type() == Int1Type)

                    AdditiveLoad_r = builder->create_zext(AdditiveLoad_r, Int32Type);

            }

        }

        if (flag == 1) //float型

        {

            switch (node.op)

            {

            case OP_GE:

                icmp = builder->create_fcmp_ge(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_GT:

                icmp = builder->create_fcmp_gt(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_LE:

                icmp = builder->create_fcmp_le(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_LT:

                icmp = builder->create_fcmp_lt(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_EQ:

                icmp = builder->create_fcmp_eq(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_NEQ:

                icmp = builder->create_fcmp_ne(AdditiveLoad_l, AdditiveLoad_r);

                break;

            default:

                break;

            }

        }

        else //int型

        {

            switch (node.op)

            {

            case OP_GE:

                icmp = builder->create_icmp_ge(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_GT:

                icmp = builder->create_icmp_gt(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_LE:

                icmp = builder->create_icmp_le(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_LT:

                icmp = builder->create_icmp_lt(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_EQ:

                icmp = builder->create_icmp_eq(AdditiveLoad_l, AdditiveLoad_r);

                break;

            case OP_NEQ:

                icmp = builder->create_icmp_ne(AdditiveLoad_l, AdditiveLoad_r);

                break;

            default:

                break;

            }

        }

        ret = icmp;

    }

}

14ASTAdditiveExpression

语义规则为additive-expression -> additive-expression addop term | term
首先accept additive_expression和term,然后判断二者类型,若至少有一个为float,则将两个都 转成float,然后进行计算

void CminusfBuilder::visit(ASTAdditiveExpression &node)

//additive-expression -> additive-expression addop term | term

{

    if(return_flag)

        return;

    Type *Int32Type = Type::get_int32_type(module.get());

    Type *Int1Type = Type::get_int1_type(module.get());

    Type *FloatType = Type::get_float_type(module.get());

    Value *AdditiveExpression;

    Value *Term;

    Value *icmp;

    //additive-expression -> term

    if (node.additive_expression == nullptr) //如果只是简单的项,转到下一层

    {

        node.term->accept(*this);

    }

    //additive-expression -> additive-expression addop term

    else

    {

        node.additive_expression->accept(*this);

        if (ret->get_type()->is_pointer_type())

            AdditiveExpression = builder->create_load(ret);

        else

            AdditiveExpression = ret;

        node.term->accept(*this);

        if (ret->get_type()->is_pointer_type())

            Term = builder->create_load(ret);

        else

            Term = ret;

        int flag = 0;

        if (AdditiveExpression->get_type()->is_float_type()) //如果是浮点数相加

        {

            flag = 1;

            if (Term->get_type()->is_integer_type())

                Term = builder->create_sitofp(Term, FloatType);

        }

        else

        {

            if (Term->get_type()->is_float_type()) //term为float型

            {

                flag = 1;

                AdditiveExpression = builder->create_sitofp(AdditiveExpression, FloatType);

            }

            else

            {

                flag = 0;

                if (AdditiveExpression->get_type() == Int1Type)

                    AdditiveExpression = builder->create_zext(AdditiveExpression, Int32Type);

                if (Term->get_type() == Int1Type)

                    Term = builder->create_zext(Term, Int32Type);

            }

        }

        if (flag == 1) //float型

        {

            if (node.op == OP_PLUS)

            {

                icmp = builder->create_fadd(AdditiveExpression, Term);

            }

            else

            {

                icmp = builder->create_fsub(AdditiveExpression, Term);

            }

        }

        else //int型

        {

            if (node.op == OP_PLUS)

            {

                icmp = builder->create_iadd(AdditiveExpression, Term);

            }

            else

            {

                icmp = builder->create_isub(AdditiveExpression, Term);

            }

        }

        ret = icmp;

    }

}

15ASTTerm

语义规则为term -> term mulop factor | factor
大致和ASTAdditiveExpression差不多。

void CminusfBuilder::visit(ASTTerm &node)

//term -> term mulop factor | factor

{

    if(return_flag)

        return;

    Type *Int32Type = Type::get_int32_type(module.get());

    Type *Int1Type = Type::get_int1_type(module.get());

    Type *FloatType = Type::get_float_type(module.get());

    Value *Term;

    Value *Factor;

    Value *icmp;

    //term -> factor

    if (!node.term)

    {

        node.factor->accept(*this);

    }

    //term -> term mulop factor

    else

    {

        node.term->accept(*this);

        if (ret->get_type()->is_pointer_type())

            Term = builder->create_load(ret);

        else

            Term = ret;

        node.factor->accept(*this);

        if (ret->get_type()->is_pointer_type())

            Factor = builder->create_load(ret);

        else

            Factor = ret;

        int flag = 0;

        if (Term->get_type()->is_float_type())

        {

            flag = 1;

            if (Factor->get_type()->is_integer_type())

                Factor = builder->create_sitofp(Factor, FloatType);

        }

        else

        {

            if (Factor->get_type()->is_float_type())

            {

                flag = 1;

                Term = builder->create_sitofp(Term, FloatType);

            }

            else

            {

                flag = 0;

                if (Factor->get_type() == Int1Type)

                    Factor = builder->create_zext(Factor, Int32Type);

                if (Term->get_type() == Int1Type)

                    Term = builder->create_zext(Term, Int32Type);

            }

        }

        if (flag == 1)

        {

            if (node.op == OP_MUL)

            {

                icmp = builder->create_fmul(Term, Factor);

            }

            else

            {

                icmp = builder->create_fdiv(Term, Factor);

            }

        }

        else

        {

            if (node.op == OP_MUL)

            {

                icmp = builder->create_imul(Term, Factor);

            }

            else

            {

                icmp = builder->create_isdiv(Term, Factor);

            }

        }

        ret = icmp;

    }

}

16ASTCall

首先scope.find找到对应的函数,get_type获取函数类型,然后遍历node.args,检查参数类型是 否匹配,若形参为int或float且形参与实参类型不匹配,则将实参转换为形参类型,压入function (参数列表)。

void CminusfBuilder::visit(ASTCall &node)

{

    //根据名字寻找到对应的值

    if(return_flag)

        return; 

    Value *value;

    value = scope.find(node.id); //调用scope.find()找ID对应的值

    if (value == nullptr) //是不是函数名

    {

        printf("cannot find the fun\n");

        return;

    }

    auto fun = value->get_type();

    if (!fun->is_function_type()) //检查函数类型

        return;

    auto callfun = static_cast<FunctionType *>(fun);

    Value *value_args;

    int i = 0;

    std::vector<Value *> function;

    Type *Int32Type = Type::get_int32_type(module.get());

    Type *FloatType = Type::get_float_type(module.get());

    Type *Int32PtrType = Type::get_int32_ptr_type(module.get());

    Type *Int1Type = Type::get_int1_type(module.get());

    for (auto Args : node.args) //检查参数类型是否匹配

    {

        auto arg_type = callfun->get_param_type(i);

        i++;

        Args->accept(*this);

        if (ret->get_type() == Int1Type) //如果ret是布尔型,ret先转换成32位整型

        {

            ret = builder->create_zext(ret, Int32Type);

        }

        if (arg_type == Int32Type) //要求的参数为整型

        {

            if (ret->get_type()->is_pointer_type())

                ret = builder->create_load(ret);

            else if (ret->get_type() == FloatType)

                ret = builder->create_fptosi(ret, Int32Type);

            value_args = ret;

        }

        else if (arg_type == FloatType) //要求的参数为浮点数

        {

            if (ret->get_type()->is_pointer_type())

                ret = builder->create_load(ret);

            else if (ret->get_type() == Int32Type)

                ret = builder->create_sitofp(ret, FloatType);

            value_args = ret;

        }

        else //要求的参数为指针

        {

            if (ret->get_type() == Int32Type || ret->get_type() == FloatType)

                return;

            value_args = ret;

        }

        function.push_back(value_args);

    }

    if (i != callfun->get_num_of_args())

    {

        printf("\t the num of arg error\n");

        return;

    }

    //call,get into sub func

    ret = builder->create_call(value, function);

}

编译

mkdir build
cd build
cmake … -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall$ mkdir build
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall$ cd build
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ cmake .. -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found FLEX: /usr/bin/flex (found version "2.6.4") 
-- Found BISON: /usr/bin/bison (found version "3.5.1") 
-- Found LLVM 10.0.0
-- Using LLVMConfig.cmake in: /usr/lib/llvm-10/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /home/sunny2004/lab1/cminus_compiler-2023-fall/build


make -j

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ make -j
[  1%] [FLEX][lex] Building scanner with flex 2.6.4
Scanning dependencies of target cminus_io
Scanning dependencies of target OP_lib
Scanning dependencies of target common
[  3%] [BISON][syntax] Building parser with bison 3.5.1
Scanning dependencies of target IR_lib
[  5%] [FLEX][lex] Building scanner with flex 2.6.4
lexical_analyzer.l:60: warning, 无法匹配规则
syntax_analyzer.y[  7%] Building C object src/io/CMakeFiles/cminus_io.dir/io.c.o
lexical_analyzer.l:53: warning, 无法匹配规则
: 警告: 1 项偏移/归约冲突 [-Wconflicts-sr]
[ 18%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/ConstPropagation.cpp.o
[  9%] Building C object src/common/CMakeFiles/common.dir/syntax_tree.c.o
[ 11%] Building CXX object src/common/CMakeFiles/common.dir/logging.cpp.o
[ 13%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/Dominators.cpp.o
Scanning dependencies of target flex
[ 15%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Value.cpp.o
[ 16%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/BasicBlock.cpp.o
[ 20%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Constant.cpp.o
[ 22%] Building CXX object src/common/CMakeFiles/common.dir/ast.cpp.o
[ 24%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Function.cpp.o
[ 26%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Type.cpp.o
[ 28%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/Mem2Reg.cpp.o
[ 30%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/User.cpp.o
[ 32%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/LoopSearch.cpp.o
[ 33%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/ActiveVars.cpp.o
[ 35%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/LoopInvHoist.cpp.o
[ 37%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/GlobalVariable.cpp.o
[ 39%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Module.cpp.o
[ 41%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Instruction.cpp.o
[ 43%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/IRprinter.cpp.o
[ 45%] Building C object src/lexer/CMakeFiles/flex.dir/lex.yy.c.o
[ 47%] Linking C static library ../../libcminus_io.a
lexical_analyzer.l: In function ‘analyzer’:
lexical_analyzer.l:92:5: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
lexical_analyzer.l: At top level:
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/lexer/lex.yy.c:1320:17: warning: ‘yyunput’ defined but not used [-Wunused-function]
     static void yyunput (int c, char * yy_bp )
                 ^
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/lexer/lex.yy.c:1363:16: warning: ‘input’ defined but not used [-Wunused-function]
     static int input  (void)
                ^
Scanning dependencies of target syntax
[ 47%] Built target cminus_io
[ 49%] Building C object src/parser/CMakeFiles/syntax.dir/syntax_analyzer.c.o
[ 50%] Building C object src/parser/CMakeFiles/syntax.dir/lexical_analyzer.c.o
[ 52%] Linking C static library ../../libflex.a
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/parser/lexical_analyzer.c:1301:17: warning: ‘yyunput’ defined but not used [-Wunused-function]
     static void yyunput (int c, char * yy_bp )
                 ^
syntax_analyzer.y: In function ‘parse’:
syntax_analyzer.y:180:5: warning: implicit declaration of function ‘yyrestart’ [-Wimplicit-function-declaration]
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/parser/lexical_analyzer.c:1344:16: warning: ‘input’ defined but not used [-Wunused-function]
     static int input  (void)
                ^
[ 52%] Built target flex
Scanning dependencies of target lexer
[ 54%] Linking C static library ../../libsyntax.a
[ 56%] Building C object tests/lab1/CMakeFiles/lexer.dir/main.c.o
[ 56%] Built target syntax
[ 58%] Linking C executable ../../lexer
[ 58%] Built target lexer
[ 60%] Linking CXX static library ../../libIR_lib.a
[ 62%] Linking CXX static library ../../libcommon.a
[ 62%] Built target IR_lib
Scanning dependencies of target stu_while_generator
Scanning dependencies of target stu_if_generator
Scanning dependencies of target stu_assign_generator
Scanning dependencies of target stu_fun_generator
Scanning dependencies of target gcd_array_generator
[ 64%] Building CXX object tests/lab3/CMakeFiles/stu_while_generator.dir/stu_cpp/while_generator.cpp.o
[ 66%] Building CXX object tests/lab3/CMakeFiles/stu_if_generator.dir/stu_cpp/if_generator.cpp.o
[ 69%] Building CXX object tests/lab3/CMakeFiles/stu_fun_generator.dir/stu_cpp/fun_generator.cpp.o
[ 71%] Building CXX object tests/lab3/CMakeFiles/gcd_array_generator.dir/ta_gcd/gcd_array_generator.cpp.o
[ 67%] Building CXX object tests/lab3/CMakeFiles/stu_assign_generator.dir/stu_cpp/assign_generator.cpp.o
[ 71%] Built target common
Scanning dependencies of target test_logging
Scanning dependencies of target test_ast
Scanning dependencies of target parser
[ 73%] Building CXX object tests/CMakeFiles/test_ast.dir/test_ast.cpp.o
[ 75%] Building CXX object tests/CMakeFiles/test_logging.dir/test_logging.cpp.o
[ 77%] Building C object tests/lab2/CMakeFiles/parser.dir/main.c.o
[ 79%] Linking CXX executable ../../parser
[ 79%] Built target parser
[ 81%] Linking CXX executable ../test_logging
[ 81%] Built target test_logging
[ 83%] Linking CXX executable ../test_ast
[ 84%] Linking CXX executable ../../stu_while_generator
[ 86%] Linking CXX executable ../../stu_if_generator
[ 88%] Linking CXX executable ../../stu_assign_generator
[ 88%] Built target test_ast
[ 90%] Linking CXX executable ../../stu_fun_generator
[ 92%] Linking CXX executable ../../gcd_array_generator
[ 92%] Built target stu_if_generator
[ 94%] Linking CXX static library ../../libOP_lib.a
[ 94%] Built target stu_while_generator
[ 94%] Built target stu_assign_generator
[ 94%] Built target stu_fun_generator
[ 94%] Built target gcd_array_generator
[ 94%] Built target OP_lib
Scanning dependencies of target cminusfc
[ 96%] Building CXX object src/cminusfc/CMakeFiles/cminusfc.dir/cminusfc.cpp.o
[ 98%] Building CXX object src/cminusfc/CMakeFiles/cminusfc.dir/cminusf_builder.cpp.o
[100%] Linking CXX executable ../../cminusfc
[100%] Built target cminusfc
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ 

make install(需要管理员权限)

root@sunny2004-VirtualBox:/home/sunny2004/lab1/cminus_compiler-2023-fall/build# make install
[  5%] Built target flex
[ 15%] Built target syntax
[ 18%] Built target cminus_io
[ 26%] Built target common
[ 47%] Built target IR_lib
[ 60%] Built target OP_lib
[ 66%] Built target cminusfc
[ 69%] Built target test_logging
[ 73%] Built target test_ast
[ 77%] Built target lexer
[ 81%] Built target parser
[ 84%] Built target stu_while_generator
[ 88%] Built target stu_if_generator
[ 92%] Built target stu_assign_generator
[ 96%] Built target stu_fun_generator
[100%] Built target gcd_array_generator
Install the project...
-- Install configuration: "Debug"
-- Installing: /usr/local/lib/libcminus_io.a
-- Installing: /usr/local/bin/cminusfc
root@sunny2004-VirtualBox:/home/sunny2004/lab1/cminus_compiler-2023-fall/build#

结果验证

运行 python lab4_test.py

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab4$ python lab4_test.py
===========TEST START===========
Case 01:	Success
Case 02:	Success
Case 03:	Success
Case 04:	Success
Case 05:	Success
Case 06:	Success
Case 07:	Success
Case 08:	Success
Case 09:	Success
Case 10:	Success
Case 11:	Success
Case 12:	Success
============TEST END============

实验总结

本次实验主要的难点在于读懂实验需求,以及对代码的特殊细节进行处理,刚开始做的时候非常困惑,查阅了非常多的资料,包括实验本身给的文档、各类头文件,也查阅了别人的代码和注释,参照了lab3 中给出的gcd_generator,才勉强写出代码。大量时间用在阅读和理解上。 在debug的过程中尝试了多种思路,包括使用log等等,但还是很难debug,参照别人的代码时也发现了 自己考虑问题不全面,接口不熟悉等一些问题,希望以后这样的实验还是能与同学多交流吧

参考

编译原理:cminus_compiler-2021-fall Lab4_编译原理实验 lab4-CSDN博客
还有一个:岳麓山大小姐的
https://blog.csdn.net/qq_45795586/article/details/122593206

上面
这个很管用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1365339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

概率论与数理统计 知识点+课后习题

文章目录 &#x1f496; [学习资源整合](https://www.cnblogs.com/duisheng/p/17872980.html)&#x1f4da; 总复习&#x1f4d5; 知识点⭐ 常用分布的数学期望和方差 &#x1f4d9; 选择题&#x1f4d9; 填空题&#x1f4d9; 大题1. 概率2. 概率3. 概率4. P5. 概率6. 概率密度…

读元宇宙改变一切笔记01_起源

1. 元宇宙是我们下一个生存之地 1.1. 1968年&#xff0c;只有不到10%的美国家庭拥有彩色电视&#xff0c;但当年票房排名第二位的电影《2001&#xff1a;太空漫游》&#xff08;2001: A Space Odyssey&#xff09;设想了这样的未来 1.1.1. 斯坦利库布里克(Stanley Kubrick) …

react-router-domV6.21.1版本结合ant design mobile的TabBar标签栏和Popup弹出层实现移动端路由配置

react-router-demo react-router-dom在V6版本之后更换了很多的API名称&#xff0c;在ant design mobile的TabBar配置中还是之前的旧版本&#xff0c;比如使用了switch组件等。我们在这里使用新版本的react-router-dom进行react移动端的配置 首先使用npm下载最新版的react-rout…

【前端设计】小球loading动画

欢迎来到前端设计专栏&#xff0c;本专栏收藏了一些好看且实用的前端作品&#xff0c;使用简单的html、css语法打造创意有趣的作品&#xff0c;为网站加入更多高级创意的元素。 html <!DOCTYPE html> <html lang"en"> <head><meta charset&quo…

YOLOv5改进 | 主干篇 | CSWinTransformer交叉形窗口网络改进特征融合层

一、本文介绍 本文给大家带来的改进机制是CSWin Transformer,其基于Transformer架构,创新性地引入了交叉形窗口自注意力机制,用于有效地并行处理图像的水平和垂直条带,形成交叉形窗口以提高计算效率。它还提出了局部增强位置编码(LePE),更好地处理局部位置信息,我将其…

File-一个IO流中至关重要的类

File类 概述&#xff1a;文件和目录路径名的抽象表示&#xff0c;File 对象就代表一个路径 对与File而言&#xff0c;其封装的并不是一个真正存在的文件&#xff0c;仅仅是一个路径名而已。它可以是存在的&#xff0c;也可以是不存在的。将来要通过具体的操作把这个路径的内容…

构建高效PythonWeb:GraphQL+Sanic

1.1 简介&#xff1a;在当今快速发展的技术时代&#xff0c;Web应用的性能和灵活性变得越来越重要。在众多技术中&#xff0c;GraphQL和Sanic以其独特的优势脱颖而出。GraphQL&#xff0c;作为一个强大的数据查询语言&#xff0c;为前端和后端之间的通信提供了极大的灵活性。而…

HCIA-Datacom题库(自己整理分类的)_09_Telent协议【13道题】

一、单选 1.某公司网络管理员希望能够远程管理分支机构的网络设备&#xff0c;则下面哪个协议会被用到&#xff1f; RSTP CIDR Telnet VLSM 2.以下哪种远程登录方式最安全&#xff1f; Telnet Stelnet v100 Stelnet v2 Stelnet v1 解析&#xff1a; Telnet 明文传输…

Oracle-expdp备份变慢问题分析

问题背景&#xff1a; 应用有一个每日跑批之前的备份作业&#xff0c;通过expdp备份应用的用户数据&#xff0c;数据量大概为600G左右&#xff0c;正常情况下可以在20分钟内跑完&#xff0c;但最近expdp备份完成时间却突然猛涨到要2小时32分才能备份完&#xff0c;导致后续的跑…

使用 MONAI 加载和保存各种格式的医学图像

本教程属于实战&#xff0c;手把手教你加载各种医学图像数据&#xff08;nii.gz, .dcm, .png等&#xff09;。并学会查看医学图像数据的元数据&#xff08;shape, affine, orientation&#xff09;。学会使用monai全方位了解你的数据&#xff0c;并把它用于之后的深度学习训练。…

最新ChatGPT网站系统源码+详细搭建部署教程+Midjourney绘画AI绘画

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

数据版本控制利器LakeFS的介绍,以及其使用方法,与其它工具结合案例

LakeFS介绍 LakeFS 是一个开源的数据湖版本控制系统&#xff0c;可以帮助用户管理和控制数据湖中的数据版本。以下是LakeFS的一些主要用处和功能介绍&#xff1a; 数据版本控制&#xff1a;LakeFS 提供了类似于 Git 的版本控制功能&#xff0c;可以跟踪和管理数据湖中的数据版…

智能合约:3分钟开发ERC20 token(2)

0.前言 上一节我们讲到了开发智能合约的准备工作&#xff0c;以及在线编程平台remix 智能合约&#xff08;1&#xff09; 这一节讲解如何开发、发行一个代币&#xff0c;并具备包括代币铸造mint&#xff0c;转账transfer和销毁burn功能&#xff0c;并确保合约拥有者owner的权限…

HEVC 视频扩展安装包,WIN10上看苹果的照片格式

苹果手机照片放电脑上&#xff0c;后缀名称.HEIC&#xff0c;打开居然要下载 HEVC 视频扩展包才能查看&#xff0c;网盘找到一个记录一下&#xff0c;安装后就可以查看苹果手机照片了。 链接&#xff1a;https://pan.baidu.com/s/13ye9_6brXD32jWAjfS47gQ?pwd36k5 提取码&am…

vue结合Cesium加载gltf模型

Cesium支持什么格式&#xff1f; Cesium支持的格式包括&#xff1a;3D模型格式&#xff08;如COLLADA、gITF、OBJ&#xff09;、影像格式&#xff08;如JPEG、PNG、GeoTIFF&#xff09;、地形格式&#xff08;如STL、Heightmap&#xff09;、矢量数据格式&#xff08;如GeoJSON…

[AutoSar]基础部分 RTE 05 Port的实例化和初始化

目录 关键词平台说明一、端口类型二、端口的实例化2.1 创建application port2.2 实例化 三、初始化 关键词 嵌入式、C语言、autosar、Rte 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 一、端口类型 如下图所示&am…

java 生成一个当前时间的时间搓

开发过程中 用时间搓数值格式存储 会更加精准 那么 我们在一些日常增删查改中就可以用时间搓来记录操作时间 就一行代码 long timestamp System.currentTimeMillis();他就能生成当前时间的时间搓 运行结果如下 然后 我们可以在 http://shijianchuo.wiicha.com/ 上进行转换查…

YOLOv5改进 | 注意力篇 | ACmix注意力与卷积混合的模型(轻量化注意力机制)

一、本文介绍 本文给大家带来的改进机制是ACmix自注意力机制的改进版本,它的核心思想是,传统卷积操作和自注意力模块的大部分计算都可以通过1x1的卷积来实现。ACmix首先使用1x1卷积对输入特征图进行投影,生成一组中间特征,然后根据不同的范式,即自注意力和卷积方式,分别…

【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax类图

【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax概述 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax快速入门 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax类图 【大数据进阶第三阶段之Datax学习笔记】使用…

2024--Django平台开发-Django知识点(四)

1.知识回顾 创建项目&#xff1a;新项目、别人项目、新版版、老版本 项目目录&#xff08;v1.0版本&#xff09; 路由系统 常见路由编写加粗样式 /index/ 函数 /index/<str:v1> 函数 re_path(ryy/(\d{4})-(\d{2})-(\d{2})/, views.yy), re_path(ryy/(?…