南大-ICS2021 PA1~PA2.2 学习笔记记录

news2024/11/26 0:52:48

文章目录

  • 代码github网址
  • ICS2021其他博客
  • 基础设施: 简易调试器
  • 表达式求值
    • 词法分析
    • 递归求值
    • 如何测试自己的代码
  • 监视点的实现
    • 扩展表达式求值的功能
    • 实现监视点
  • 阅读源码 2
    • 译码
    • 执行
    • 用RTL表示指令行为
    • 实现常用的库函数
    • 实现常用的库函数

代码github网址

https://github.com/xiao-tai/ics2021

ICS2021其他博客

南大-ICS2021 PA2.3 学习笔记&记录
南大-ICS2021 PA3.1 学习笔记&记录
南大ICS2021–实现库函数vsnprintf

基础设施: 简易调试器

这些都还比较简单实现, 具体可以查看nemu/src/sdb/sdb.c中的

  • cmd_c: 启动执行

  • cmd_q: 退出NEMU

  • cmd_si: 单步执行

  • cmd_x: 打印内存

  • cmd_p: 进行表达式求值, 具体实现看下节

表达式求值

主要文件为src/monitor/sdb/expr.c,具体可以查看我的github

词法分析

对于一下表达式:

"0x80100000+   ($a0 +5)*4 - *(  $t1 + 8) + number"

我们需要将各个单元(也就是token)给识别出来, 使用正则表达式可以方便的匹配出一些复杂的pattern, 代码框架中的init_regex()函数负责将定义的正则表达式进行验证, 函数在此处报错通常是因为使用了非法的正则表达式

在这里插入图片描述
本人所用到的正则表达式如下图

在这里插入图片描述

然后, 给定一个表达式, 通过make_token()来识别token类型, 将识别结果保存在一个tokens[i]中, 将对应的字符串保存在str[32]属性中, 类型保存在type属性中, 以上述表达式为例,

tokens[0].str = 0x80100000, tokens[0].type = TK_HEX_NUM, 以此类推, 这样一个表达式所有的taoken都识别完毕了.

递归求值

这里就是涉及到算法的知识了, 需要用到分治法, 这类方法在排序上也有一个经典的应用–快速排序算法. 表达式求值使用上, 我们需要先对短表达式求值, 再对长表达式求值, 所以要使用递归. 这里需要考虑括号的问题和运算符优先级的问题.

eval(p, q) {
  if (p > q) {
    /* Bad expression */
  }
  else if (p == q) {
    /* Single token.
     * For now this token should be a number.
     * Return the value of the number.
     */
  }
  else if (check_parentheses(p, q) == true) {
    /* The expression is surrounded by a matched pair of parentheses.
     * If that is the case, just throw away the parentheses.
     */
    return eval(p + 1, q - 1);
  }
  else {
    op = the position of 主运算符 in the token expression;
    val1 = eval(p, op - 1);
    val2 = eval(op + 1, q);

    switch (op_type) {
      case '+': return val1 + val2;
      case '-': /* ... */
      case '*': /* ... */
      case '/': /* ... */
      default: assert(0);
    }
  }
}

对于一个长表达式, 需要先找出主运算符, 本人计算主运算符逻辑是

  • 遍历p和q之间所有的运算符

  • 优先级高的运算符放到后面进行if判断, 因为主运算符是最后一步才进行运算的

// 获取主运算符的位置
int get_position(int p, int q) {
    int pos = 0;
    int num = 0;
    bool plus_or_sub = false;
    bool mul_or_div = false;
    bool minus = false;
    bool deref = false;
    for(int i = p; i <= q; i++) {
        if(tokens[i].type == '(')
            num++;
        if(tokens[i].type == ')')
            num--;

        if(num != 0)
            continue;
        if(tokens[i].type == TK_EQ) {
            return i;
        }
        // The lower the priority, the higher the judge will be
        if(tokens[i].type == '+' || tokens[i].type == '-') {
            pos = i;
            plus_or_sub = true;
            continue;
        }        
        if((tokens[i].type == '*' || tokens[i].type == '/') && !plus_or_sub) {
            pos = i;
            mul_or_div = true;
            continue;
        }
        if(tokens[i].type == TK_MINUS && !plus_or_sub && !mul_or_div && !minus) {
            pos = i;
            minus = true;
        }
        if(tokens[i].type == TK_DEREF && !plus_or_sub && !mul_or_div && !deref) {
            pos = i;
            deref = true;
        }
    }
    return pos;
}

如何测试自己的代码

写一个随机生成表达式的程序, 要求合法, 其实就是将生成的表达式, 例如1 + 2 + 3传入一个定义好的文件中

#include <stdio.h>

int main() {
    unsigned result = 1 + 2 + 3;
    printf("%u\n", result);
}

监视点的实现

扩展表达式求值的功能

在递归求解表达式前, 要区分*是乘法运算符, 还是解引用, 所以在进行递归前, 先做一次判断, 以区分负号和减号, 乘号和解引用运算符:

for (int i = 0; i < nr_token; i++) {
    if(tokens[i].type == '-') {
        if(i == 0 || (tokens[i-1].type != ')' && tokens[i-1].type != TK_NUM))
            tokens[i].type = TK_MINUS;
    }
    if(tokens[i].type == '*') {
    if(i == 0 || (tokens[i-1].type != ')' && tokens[i-1].type != TK_NUM))
        tokens[i].type = TK_DEREF;
    }
}

实现监视点

使用两个链表维护一个监视点池, head负责维护使用中的监视点结构, free_负责维护空闲的监视点结构. 监视点的机构体如下:

typedef struct watchpoint {
  int NO;
  struct watchpoint *next;
  // 额外添加两个成员, 用于后续的扫描
  char exp[32]; //表达式
  uint32_t res;
} WP;

这时还需要增加监视点和删除监视点这两个函数, 同时还需要有扫描所有监视点的函数:

  • 增加监视点(new_wp): 申请一个空闲监视点, 并将表达式记录下来

  • 删除监视点(free_wp(int no)): 通过监视点编号删除监视点, 将其表达式设置为’\0’, 即代表该监视点不参与监视

  • 扫描监视点(check_all_wp()): 每当cpu_exec()执行一条指令时, 都会调用一次trace_and_difftest(), 所以在其中扫描一次所有监视点, 对其表达式进行求值, 如果结果发生变化, 就打印出来, 并将nemu_state.state变量设置为NEMU_STOP

具体见src/monitor/sdb/watchpoint.c

阅读源码 2

译码

译码上, 框架代码进行了多层抽象.

取指令的时候会把指令记录到s->isa.instr.val, 首先匹配opecode字段, 再匹配func3字段, 再匹配func7字段, 这样isa_fetch_decode()会返回唯一的idx, 用于表示执行的指令.

但是我们还不知道操作对象(比如立即数是多少, 使用哪个寄存器), 使用译码辅助函数

// 宏定义解开的样子
def_DHelper(name) = void decode_name(Decode *s)

每个译码辅助函数负责进行一种类型的操作数译码, 把指令中的操作数信息分别记录在译码信息sdest成员, src1成员和src2成员中, 它们分别代表目的操作数和两个源操作数. nemu/include/cpu/decode.h中还定义了三个宏id_dest, id_src1id_src2, 用于方便地访问它们.

同时为了更好的实现操作数译码和指令译码的解耦, 使用译码操作数辅助函数

def_DopHelper(name) = 
    void decode_op_name(Decode *s, Operand *op, word_t val, bool flag)
  • decode_op_i: 通过译码的指令获得立即数

  • decode_op_r: 通过译码的指令获得寄存器

执行

fetch_decode()中得到的执行辅助函数记录到s->EHelper

在这里插入图片描述

执行辅助函数统一通过宏def_EHelper, 具体通过RTL指令来进行真正的执行

def_EHelper(name) = static inline void exec_name(Decode *s) 

每个执行辅助函数都需要有一个标识该指令的ID以及一个表格辅助函数与之相对应, 这一点是通过一系列宏定义来实现的. 在nemu/src/isa/$ISA/include/isa-all-instr.h中定义用于表示指令列表的宏INSTR_LIST, 它定义了NEMU支持的所有指令.

用RTL表示指令行为

在NEMU中, RTL寄存器只有以下这些

  • 不同ISA的通用寄存器(在nemu/src/isa/$ISA/include/isa-def.h中定义)
  • 临时寄存器s0, s1, s2t0(在nemu/include/rtl/rtl.h中定义)
  • 零寄存器rz(在nemu/include/rtl/rtl.h中定义), 它的值总是0

进入nemu/build/obj-riscv32-nemu-interpreter/src/cpu/cpu-exec.i查看具体的执行情况, g_exec_table如下:

static const void *g_exec_table[TOTAL_INSTR] = {
    [EXEC_ID_lui] = exec_lui,
    [EXEC_ID_lb] = exec_lb,
    [EXEC_ID_lbu] = exec_lbu,
    [EXEC_ID_lh] = exec_lh,
    [EXEC_ID_lhu] = exec_lhu,
    [EXEC_ID_lw] = exec_lw,
    [EXEC_ID_sw] = exec_sw,
    [EXEC_ID_sh] = exec_sh,
    [EXEC_ID_sb] = exec_sb,
    //.....
}

各个函数的具体实现位于src/isa/riscv32/instr/compute.h中, 通过以下指令查找得到:

xiaoxTai:nemu$ grep -r "rtl_add" .

./src/isa/riscv32/instr/compute.h:  rtl_addi(s, ddest, dsrc1, id_src2->imm);
./src/isa/riscv32/instr/compute.h:  rtl_add(s, ddest, dsrc1, dsrc2);

在这里插入图片描述

所以实现一个新指令的步骤如下:

  1. nemu/src/isa/riscv32/instr/decode.c中添加正确的模式匹配规则
  2. 用RTL实现正确的执行辅助函数, 在./src/isa/riscv32/instr/compute.h或者instr文件夹下其他的头文件
  3. nemu/src/isa/riscv32/include/isa-all-instr.h中把指令添加到INSTR_LIST
  4. 必要时在nemu/src/isa/riscv32/include/isa-exec.h添加相应的头文件, 言外之意就是第二步写在哪个头文件下, 就把那个头文件, 加到这个isa-exec.h

在这里插入图片描述

接下来就是通过运行程序, 检查还有哪些指令没有实现, 安装好交叉编译环境, 通过执行

make ARCH=riscv32-nemu ALL=dummy run

运行结果打印的信息会提示你还有什么指令没有实现, 当然要通过在am-kernels/tests/cpu-tests/build/dummy-$ISA-nemu.txt进行搜索

实现常用的库函数

相关库函数的具体实现, 也是面试中出现频率较高的问题了, 主要在abstract-machine/klib/src/

  • strcpy, memset等函数在string.c

  • sprintf()stdio.c下, 这个函数实现的讲解见我的另一篇博客

  • atoi, malloc在stdlib.c下
    果打印的信息会提示你还有什么指令没有实现, 当然要通过在am-kernels/tests/cpu-tests/build/dummy-$ISA-nemu.txt进行搜索

实现常用的库函数

相关库函数的具体实现, 也是面试中出现频率较高的问题了, 主要在abstract-machine/klib/src/

  • strcpy, memset等函数在string.c

  • sprintf()stdio.c下, 这个函数实现的讲解见我的另一篇博客南大ICS2021–实现库函数vsnprintf

  • atoi, malloc在stdlib.c下

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

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

相关文章

一主一从读写分离

目录 介绍 一主一从 原理 准备 配置主从复制 验证主从复制 一主一从读写分离 安装MyCat schema.xml配置 server.xml配置 测试 介绍 读写分离,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作&#xff0c;从数据库提供读操作&am…

Springboot中多线程数据库操作下的事务一致性问题的解决方案

文章目录 1 代码实现1.1 正常情况1.2 异常情况总结 1 代码实现 1.1 正常情况 我们采用手动开启事务的方式 public void add(CountDownLatch countDownLatch) {executor.submit(() -> {TransactionStatus transaction dataSourceTransactionManager.getTransaction(transa…

【HTML】HTML学习之引入CSS样式表

1、CSS样式规则 选择器{属性1:属性值1; 属性2:属性值2; 属性3:属性值3;}2、HTML引入CSS样式表 2.1、行内式 行内式也称为内联样式&#xff0c;是通过标签的style属性来设置元素的样式&#xff0c;其基本语法格式如下: <标签名 style"属性1:属性值1; 属性2:属性值2;…

Proxy/Skeleton

设计模式之&#xff08;十二&#xff09;代理模式_skeleton proxy 模式-CSDN博客 在RMI中&#xff0c;客户端可以通过一个桩&#xff08;Stub&#xff09;对象与远程主机上的业务对象进行通信&#xff0c;由于桩对象和远程业务对象接口的一致&#xff0c;因此对于客户端而言&am…

Maven的一些相关知识【重修】《包括私服搭建!》

mvnrepository.com Maven 下载jar包的位置&#xff01; 【该部分有教程】 这是什么nb代码投稿视频-这是什么nb代码视频分享-哔哩哔哩视频

python之matplotlib (6 等高线和热力图)

等高线 import numpy as np import matplotlib.pyplot as pltdef f(x,y):return (1-x/2x**5y**3)*np.exp(-x**2-y**2) n256 xnp.linspace(-3,3,n) yx X,Ynp.meshgrid(x,y) plt.contourf(X,Y,f(X,Y),8,alpha0.75,cmapviridis) plt.colorbar() Cplt.contour(X,Y,f(X,Y),8,colors…

第64期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

免费图形化nginx管理工具nginxWebUI

nginxWebUI是一款图形化管理nginx配置得工具, 可以使用网页来快速配置nginx的各项功能, 包括http协议转发, tcp协议转发, 反向代理, 负载均衡, 静态html服务器, ssl证书自动申请、续签、配置等, 配置好后可一建生成nginx.conf文件, 同时可控制nginx使用此文件进行启动与重载, 完…

Linux基础软件-软件安装

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux进阶部分又分了很多小的部分,我们刚讲完了Linux日常运维。讲的那些东西都算是系统自带的&#xff0c;但是Linux作为一个…

mklink 命令详解

mklink 命令详解 在命令提示符中输入 mklink 可以查看相关的运行命令。 创建符号链接。MKLINK [[/D] | [/H] | [/J]] Link Target/D 创建目录符号链接。默认为文件符号链接。/H 创建硬链接而非符号链接。/J 创建目录联接。Link 指定新的符号链接名称。Targ…

前端3d动画-----平移 transform: translate3d()

必须加这个属性&#xff1a;transform-style: preserve-3d; perspective: 900px; 设置了景深才能感到近大远小的感觉 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible&q…

CTF中的换表类Crypto题目

目录 [安洵杯 2019]JustBase[SWPUCTF 2021 新生赛]traditional字符替换解密 [BJDCTF 2020]base??字符替换 --》 base64解密 [安洵杯 2019]JustBase VGhlIGdlbxvZ#kgbYgdGhlIEVhcnRoJ#Mgc#VyZmFjZSBpcyBkb!pbmF)ZWQgYnkgdGhlIHBhcnRpY#VsYXIgcHJvcGVydGllcyBvZiB#YXRlci$gUHJ…

图神经网络教程3——循环图神经网络-2

目录 计算下游输出 序列图数据的扩展 图长短期记忆网络 循环转换在RGNN应用于图分类时的作用 数据集 算法 结果和讨论 门控循环单元 优缺点 前文索引 本篇是GRNN的第二篇文章&#xff0c;点击此处可到达第一篇文章的位置。 计算下游输出 一旦我们以图中的每个顶点为…

webm格式怎么转换成mp4?7个有效方法将webm转mp4

在数字媒体的浩瀚宇宙中&#xff0c;视频格式的多样性犹如繁星点点&#xff0c;既点亮了创意的火花&#xff0c;也铺设了内容分享的广阔道路。每一种视频格式都承载着其独特的技术优势与设计初衷&#xff0c;WebM便是其中一颗璀璨的新星&#xff0c;专为优化网络传输而生。它凭…

Unity | Shader基础知识(第二十二集:两次渲染)

目录 一、前言 二、“渲染两次” 三、本次成品介绍 四、第一次渲染代码 五、第二次渲染代码 六、截止目前的所有代码 七、调整代码 八、总结 一、前言 之前一直讲的shader文件中&#xff0c;都只写了一次CG代码。 为了大家对这部分的整体理解&#xff0c;我们这次渲…

微服务的保护

一、雪崩问题及解决方案 1.雪崩问题 微服务之间&#xff0c;一个微服务依赖多个其他的微服务。当一个微服务A依赖的一个微服务B出错时&#xff0c;微服务A会被阻塞&#xff0c;但其他不依赖于B的微服务不会受影响。 当有多个微服务依赖于B时&#xff0c;服务器支持的线程和并…

使用策略模式代替多个ifelse

传统的多个 public class OrderServiceImpl implements IOrderService {Overridepublic String handle(OrderDTO dto) {String type dto.getType();if ("1".equals(type)) {return "处理普通订单";} else if ("2".equals(type)) {return "…

PMP–知识卡片--产品管理知识体系

产品管理是公司为管理一个产品或者产品线的产品计划、产品市场和产品生命周期所采用的组织架构。产品管理是一个典型的强矩阵的管理方式。产品管理是企业或组织在产品生命周期中对产品规划、开发、生产、营销、销售和支持等环节进行管理的业务活动。 项目经理和产品有着直接、间…

xmind 2024下载,安装目录更改为其他盘

下载 最新版官网地址 更改目录

网络编程Day9_IO多路复用 20240821

运行1个服务器和2个客户端实现效果&#xff1a; 服务器和2个客户端互相聊天&#xff0c;服务器和客户端都需要使用select模型去实现 服务器要监视2个客户端是否连接&#xff0c;2个客户端是否发来消息以及服务器自己的标准输入流 客户端要监视服务器是否发来消息以及客户端自…