[CA]Part B:2 确认指令解析后,变量是怎么处理的?

uint32_t Instruction::parse_field(
    QString &field_token,          // 传入的当前字段字符串,用于解析
    const QString &arg,            // 模板字符串,定义指令需要的字段类型
    Address inst_addr,             // 当前指令的地址,用于计算相对偏移量
    RelocExpressionList *reloc,    // 可选的重定位表达式列表
    const QString &filename,       // 源代码文件名
    unsigned int line,             // 当前行号,用于错误信息
    Modifier pseudo_mod,           // 修饰符,通常用于伪指令
    uint64_t initial_immediate_value // 初始立即数的值
) {
    uint32_t inst_code = 0; // 最终生成的机器码
    for (QChar ao : arg) {  // 遍历模板字符串中的每个字段描述符
        bool need_reloc = false; // 是否需要重定位
        uint a = ao.toLatin1();  // 将字段描述符转换为 ASCII
        if (!a) { continue; }    // 如果为空字符,跳过

        field_token = field_token.trimmed(); // 去除当前字段字符串的空格
        const ArgumentDesc *adesc = arg_desc_by_code[a]; // 根据描述符查找字段信息
        if (adesc == nullptr) {  // 如果字段描述符未定义
            if (!field_token.count()) { // 当前字段为空
                throw ParseError("empty argument encountered"); // 抛出解析错误
            }
            if (field_token.at(0) != ao) { // 如果字段首字符与描述符不匹配
                throw ParseError("argument does not match instruction template"); // 抛出错误
            }
            field_token = field_token.mid(1); // 跳过该字符
            continue;
        }

        // 如果字段是立即数,则根据伪指令修饰符应用修饰
        const Modifier effective_mod = (adesc->is_imm()) ? pseudo_mod : Modifier::NONE;

        uint64_t val = 0; // 解析的字段值
        uint chars_taken = 0; // 已解析的字符数量

        // 根据字段类型解析值
        switch (adesc->kind) {
        case 'g': // 寄存器类型
            val += parse_reg_from_string(field_token, &chars_taken); 
            break;
        case 'p': // 偏移地址类型
        case 'a': // 地址类型
            val -= inst_addr.get_raw(); // 计算相对地址
            FALLTROUGH
        case 'o': // 偏移立即数类型
        case 'n': // 普通立即数类型
            val += initial_immediate_value; // 加上初始值
            if (!parse_immediate_value(
                    field_token, inst_addr, reloc, filename, line, need_reloc, adesc, effective_mod,
                    val, chars_taken)) { // 解析失败时抛出错误
                throw ParseError(
                    QString("field_token %1 is not a valid immediate value").arg(field_token));
            }
            break;
        case 'E': // CSR(控制与状态寄存器)类型
            val = parse_csr_address(field_token, chars_taken); 
            break;
        }
        
        // 如果未解析任何字符,抛出错误
        if (chars_taken <= 0) { 
            printf("[Debug] Parsing field_token: %s, template arg: %s\n", 
                   field_token.toStdString().c_str(), arg.toStdString().c_str());
            throw ParseError("argument parse error"); 
        }

        // 如果有修饰符,则修饰立即数值
        if (effective_mod != Modifier::NONE) {
            val = modify_pseudoinst_imm(effective_mod, val);
        } else if (!adesc->is_value_in_field_range(val)) { // 检查值是否在字段允许范围内
            throw ParseError("argument range exceed");
        }

        // // 根据解析的值设置机器码的相应字段
        // inst_code |= adesc->arg.encode(val);
        //printf("[Debug] Inst code before: 0x%x\n", inst_code);
        inst_code |= adesc->arg.encode(val);
        //printf("[Debug] Inst code after: 0x%x\n", inst_code);

        // 去掉已解析的字段部分
        field_token = field_token.mid(chars_taken);
    }

    // 如果字段未完全解析,抛出错误
    if (field_token.trimmed() != "") { 
        throw ParseError("excessive characters in argument"); 
    }

    return inst_code; // 返回生成的机器码
}

当我们执行指令时,需要将指令从文本表示(例如 vadd.vv x1, x2, x3)转换为机器语言形式(例如 0x12345678)。这部分代码的目标就是将指令中的每个部分解析成相应的机器码字段,并把它们组合成完整的机器指令。

逐步解析

  1. 函数的输入参数

    • field_token:这是指令中的一个字段,它可能是寄存器名、立即数等。比如在指令 vadd.vv x1, x2, x3 中,x1, x2, x3 就是字段。
    • arg:这表示该字段的类型。例如,它可能是寄存器、立即数、地址等。
    • inst_addr:当前指令的地址,用于计算相对地址(例如加载指令时的偏移)。
    • reloc:用于存储重定位信息,可能用于某些指令解析中需要调整地址的情况。
    • filenameline:提供源文件和行号的信息,通常用于调试和错误报告。
    • pseudo_mod:伪指令的修饰符。某些指令可能是伪指令,它们需要特殊处理(例如 nopli)。
    • initial_immediate_value:指令中的初始立即数值。比如,某些指令中会使用一个立即数(例如 addi x1, x2, 10 中的 10)。
  2. 解析过程

    • 遍历模板字符串:每个指令字段都有一个模板字符串(例如 d, s, t 等),这些字符指示字段的类型。arg 就是这个模板,每次循环会处理一个字段描述符(如 gan 等)。
    • 去除空格field_token.trimmed() 去除当前字段的空格,确保我们处理的字段是干净的。
  3. 解析字段

    • 对于每个字段描述符(如 gan),通过 arg_desc_by_code[a] 查找它的描述信息。
    • 寄存器(g:如果字段描述符是 g,意味着该字段应该是一个寄存器。通过 parse_reg_from_string 函数解析寄存器名,转换为对应的寄存器编号。
    • 立即数或偏移量(o, n, a, p:这些字段类型需要通过 parse_immediate_value 来解析。它们可能是数字、立即数,或者与当前指令地址相关的偏移量。
    • CSR(控制与状态寄存器):字段描述符为 E 时,解析的是 CSR 地址,通常用于控制寄存器。
  4. 修饰符和验证

    • 修饰符:如果字段是立即数类型,并且该字段有伪指令修饰符(如 slli -> XLEN-8),那么在解析后会应用这个修饰符来调整值。
    • 范围检查:每个字段都有一个有效值范围(例如寄存器编号 0-31)。通过 is_value_in_field_range 函数确保解析的值在合法范围内。如果超出范围,会抛出错误。
  5. 更新指令码

    • 每解析一个字段,就通过 inst_code |= adesc->arg.encode(val) 更新指令码。这里使用了按位 OR 操作,将解析出的值合并到指令码的正确位置。
  6. 解析完成后检查

    • 最后,检查字段是否已完全解析。如果 field_token 还有剩余内容,表示解析有误,会抛出错误。

在这段代码中,模板字符串(如 arg 参数)是用来定义指令中各个字段的类型和结构的。它通常是一个由多个字母组成的字符串,其中每个字母代表一个字段的类型或角色。具体来说,这些字母用于表示不同类型的指令字段,例如寄存器、立即数、偏移地址等。

每个字母的含义

在指令解析过程中,模板字符串中的字母对应了指令的各个字段。例如:

  • g:表示寄存器类型的字段,通常是通用寄存器(如 x1, x2)。
  • ap:表示偏移地址相关的字段,通常是指令中的偏移量或地址。
  • n:表示立即数类型的字段,这通常是指令中的数字或常量值。
  • o:也表示立即数类型的字段,可能与偏移量有关。
  • E:表示控制和状态寄存器(CSR)类型的字段。
  • >:通常表示位移字段,表示立即数或寄存器的位移量。

如何使用这些字母

  • 每个字母会被解析为具体的字段类型,然后根据模板对指令中的实际字段进行解析。举个例子,在指令 addi x1, x2, 10 中,模板字符串可能是 "d,s,j",表示:

    • d:是目标寄存器(x1)。
    • s:是源寄存器(x2)。
    • j:是立即数(10)。
  • 解析过程中,代码会根据这个模板检查指令的各个部分,并根据字母对应的类型(如寄存器、立即数等)进行解析。

举个例子

假设我们有一个模板字符串 "g,o,n",它表示一个指令有三个字段:

  1. g:寄存器类型,表示指令中的寄存器字段。
  2. o:立即数类型,表示一个数字或常量。
  3. n:偏移量或另一个立即数。

在指令解析过程中,我们会依次解析每个字段,确保它们的格式符合预期,并且转换成机器码。

总结

这些字母的作用是:

  • 定义指令中的字段类型。
  • 指示如何解析每个字段(例如,寄存器、立即数、偏移量等)。
  • 通过模板字符串来规范指令的解析过程,确保指令按照正确的格式转换成机器码。

 代码中,是否已有足够的模板字符串用于RVV计算?

RVV指令中的字段要求

RVV指令的典型格式和字段可能包括以下内容:

  • 寄存器字段:表示向量寄存器,如 v0v31
  • 立即数或标量值:RVV指令常常需要操作一个寄存器和一个立即数(标量值)。
  • 操作符:表示向量指令要执行的操作,如加法、乘法等。
  • 向量长度:RVV指令通常包括设置向量长度(vsetvl)的操作。

检查现有的模板字符串

代码中定义的模板字符串包括以下几种类型:

  • g:代表寄存器类型。
  • n:代表普通立即数类型。
  • o:代表偏移立即数类型,通常用于存储或加载指令。
  • ap:可能用于表示地址或偏移量。

这些模板字段已经覆盖了大部分指令字段,但对于 RVV 指令,特别是向量寄存器和向量长度等,可能需要一些额外的模板来正确解析。

RVV指令中的具体需求

对于 RVV 指令,我们可能需要处理以下情况:

  1. 向量寄存器:在指令中,需要解析向量寄存器,通常会用类似 v0v31 的寄存器。现有的模板字符串 g 适用于通用寄存器,但需要确保它能正确处理 v0v31 这样的向量寄存器。
  2. 向量长度设置:指令如 vsetvl 会设置向量的长度,这可能涉及到新的模板字段来处理向量长度。
  3. 向量计算:RVV指令执行的操作(如 vadd.vvvmul.vv 等)需要明确地识别源寄存器和目标寄存器,以及操作符(如加法、乘法等)。现有的 gn 字段应该足以处理这些情况。

 

static const ArgumentDesc arg_desc_list[] = {
    // Destination register (rd)
    ArgumentDesc('d', 'g', 0, 0x1f, { { { 5, 7 } }, 0 }),
    // Source register 1 (rs1/rs)
    ArgumentDesc('s', 'g', 0, 0x1f, { { { 5, 15 } }, 0 }),
    // Source register 2 (rs2/rt)
    ArgumentDesc('t', 'g', 0, 0x1f, { { { 5, 20 } }, 0 }),
    // I-type immediate for arithmetic instructions (12bits)
    ArgumentDesc('j', 'n', -0x800, 0x7ff, { { { 12, 20 } }, 0 }),
    // Shift for bit shift instructions (5bits)
    ArgumentDesc('>', 'n', 0, 0x1f, { { { 5, 20 } }, 0 }),
    // Address offset immediate (20bits), encoded in multiples of 2 bytes
    ArgumentDesc('a', 'a', -0x80000, 0x7ffff, { { { 10, 21 }, { 1, 20 }, { 8, 12 }, { 1, 31 } }, 1 }),
    // U-type immediate for LUI and AUIPC (20bits)
    ArgumentDesc('u', 'n', 0, 0xfffff000, { { { 20, 12 } }, 0 }),
    // B-type immediate for branches (12 bits)
    ArgumentDesc('p', 'p', -0x1000, 0x0fff, { { { 4, 8 }, { 6, 25 }, { 1, 7 }, { 1, 31 } }, 1 }),
    // Offset immediate for load instructions (12 bits)
    ArgumentDesc('o', 'o', -0x800, 0x7ff, { { { 12, 20 } }, 0 }),
    // Offset immediate for store instructions (12 bits)
    ArgumentDesc('q', 'o', -0x800, 0x7ff, { { { 5, 7 }, { 7, 25 } }, 0 }),
    // 5-bit CSR value immediate
    // (https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=opcodes/riscv-opc.c;h=7e95f645c5c5fe0a7c93c64c2f1719efaec67972;hb=HEAD#l928)
    ArgumentDesc('Z', 'n', 0, 0x1f, { { { 5, 15 } }, 0 }),
    // 12-bit CSR address
    // (https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=opcodes/riscv-opc.c;h=7e95f645c5c5fe0a7c93c64c2f1719efaec67972;hb=HEAD#l928)
    ArgumentDesc('E', 'E', 0, 0xfff, { { { 12, 20 } }, 0 }),
};

static const ArgumentDesc *arg_desc_by_code[(int)('z' + 1)];

static bool fill_argdesbycode() {
    for (const auto &desc : arg_desc_list) {
        arg_desc_by_code[(uint)(unsigned char)desc.name] = &desc;
    }
    return true;
}

bool argdesbycode_filled = fill_argdesbycode();

这段代码的作用是定义并初始化指令解析过程中所使用的 指令字段描述符,并根据这些描述符将字段信息映射到一个查找表 arg_desc_by_code 中。具体来说,它是用来描述每个指令字段的格式和解析范围的。

详细解释:

1. ArgumentDesc 结构体:

ArgumentDesc 是一个结构体,它定义了指令字段的描述,包括:

  • name:字段的名称,通常是一个字符(例如 d, s, t 等),它标识了字段的类型(例如寄存器、立即数、偏移量等)。
  • kind:字段的种类,可能的值包括:
    • 'g':表示通用寄存器(例如 x0x31)。
    • 'n':表示普通的立即数类型(例如一个数字)。
    • 'a''p':表示地址相关的字段或偏移量。
    • 'o':偏移量立即数,通常用于存储/加载指令。
    • 'E':表示 CSR(控制和状态寄存器)类型。
  • minmax:用于指定该字段值的最小值和最大值,用于验证该字段的值是否在允许的范围内。
  • arg:这部分是用来定义如何对该字段进行编码和解码的。它使用了 BitArg 类型来描述位段的偏移和长度。
2. arg_desc_list 数组:

arg_desc_list 是一个 ArgumentDesc 类型的数组,定义了多种不同类型的字段描述符。每个字段描述符对应一种类型的字段,像是寄存器字段、立即数字段等。例如:

  • ArgumentDesc('d', 'g', 0, 0x1f, { { { 5, 7 } }, 0 }):表示目标寄存器字段(rd),它的值范围是 0 到 31,字段在指令中的位置由位段 5-7 决定。
  • ArgumentDesc('j', 'n', -0x800, 0x7ff, { { { 12, 20 } }, 0 }):表示立即数字段,值的范围是 -2048 到 2047,字段位置在指令中的 12-20 位。
3. arg_desc_by_code 数组:

arg_desc_by_code 是一个长度为 123 的数组(因为它包含从字符 'a''z' 对应的 123 个字符),用于通过字段的字符名称(如 ds)来查找对应的字段描述符。每个字符会映射到一个 ArgumentDesc 结构体的指针,这样可以快速通过字段字符来找到其定义的描述符。

  • 例如,arg_desc_by_code['d'] 会返回目标寄存器(rd)的字段描述符。
4. fill_argdesbycode 函数:

fill_argdesbycode 函数的作用是填充 arg_desc_by_code 数组,将每个字段描述符的 name(即字段的字符,如 d, s)作为索引,将其对应的 ArgumentDesc 结构体指针存入 arg_desc_by_code 中。

5. argdesbycode_filled 变量:

argdesbycode_filled 是一个全局变量,表示 arg_desc_by_code 数组是否已经被填充。初始化时会调用 fill_argdesbycode() 函数来填充该数组。通过这个变量可以确保在指令解析过程中查找表已经准备好。

作用总结:

这段代码主要是为了 快速查找指令字段描述符,它将每个指令字段(如寄存器字段、立即数字段等)的详细信息(如范围、类型、位段位置等)存储在 ArgumentDesc 结构体中,并使用 arg_desc_by_code 数组将这些描述符与指令中的字段字符(如 ds)关联起来。

在指令解析过程中,当遇到一个字段字符时,可以通过 arg_desc_by_code 快速找到该字段的详细描述,并根据描述符来解析该字段的值(如寄存器编号、立即数、地址等)。这对于高效处理指令解析非常重要。

对RVV指令的影响:

对于 RVV 指令,如果需要新的字段类型(如向量寄存器、向量长度等),可以在这个 arg_desc_list 数组中增加新的字段描述符,例如:

  • 向量寄存器字段:可能需要增加 v0v31 的处理,可以用类似 g 字段类型来处理向量寄存器。
  • 向量长度字段:可以使用新的类型标识符(例如 v 或类似的类型)来处理与向量长度相关的字段。

1. 向量寄存器字段的处理

为了处理向量寄存器字段(如 v0v31),我们可以扩展现有的 g 字段类型来支持 v 字段。你需要添加一个新的 ArgumentDesc 结构来表示向量寄存器,定义如何解析寄存器编号。

向量寄存器字段实现:

我们可以用 v 字段表示向量寄存器,类似于 g 字段表示通用寄存器。你可以在 arg_desc_list 中新增一项

寄存器解析:

现在我们需要修改 parse_reg_from_string 函数,以便它能够识别向量寄存器 v0v31

2. 向量长度字段的处理

对于向量长度字段(如 vsetvl),你可以使用新的类型标识符(例如 v 或类似的类型)来处理与向量长度相关的字段。为了处理向量长度,可以定义一个新的字段描述符来解析它。

向量长度字段实现:

我们可以用 vlen 或类似的名称来定义一个新的字段描述符,用来表示向量长度

这行代码的意思是:

  • l:表示向量长度字段(字段名称)。
  • n:字段类型,表示它是一个普通的立即数类型(通常用于表示向量长度的值)。
  • 00x1f:值的最小值和最大值(例如向量长度可以从 0 到 31)。
  • { { { 5, 15 } }, 0 }:字段在指令中的位置。

你可以根据实际需求调整向量长度的字段值范围和位段。

3. 字段名称和描述符的比喻通俗关系

  • g:表示 通用寄存器,它对应的是指令中用来存储和操作数据的寄存器字段,例如 x0x31
  • v:表示 向量寄存器,它对应的是指令中用来存储和操作向量数据的寄存器字段,例如 v0v31
  • n:表示 普通立即数,通常用于表示常数值或操作符中的标量值,像加法或乘法指令的立即数。
  • l:表示 向量长度,用于特定的向量指令(如 vsetvl)来指定向量的长度。

这些字段名称(如 g, v, n, l)是指令中各个字段的简化标识,描述符则包含了该字段的详细解析规则(如值的范围、位段位置等)。

4. 总结

  • 向量寄存器字段:通过增加一个 v 类型的字段描述符,允许解析向量寄存器 v0v31
  • 向量长度字段:通过定义一个新的字段(如 l),用于表示向量长度,适用于 vsetvl 等指令。
 输出1:说明进入了判断语句,
eric@Eric:~/Sample3/qtrvsim$ ./target/qtrvsim_cli --asm /home/eric/Pro3/qtrvsim/tests/test1.S
[INFO]  machine.BranchPredictor:        Initialized branch predictor: None
是向量寄存器是向量寄存器是向量寄存器是向量寄存器是向量寄存器是向量寄存器是向量寄存器是向量寄存器是向量寄存器是向量寄存器Machine stopped on BREAK exception.
eric@Eric:~/Sample3/qtrvsim$ 
// 检查是否是向量寄存器 (v0 - v31)
    else if (str.at(0) == 'v') {
        //printf("是向量寄存器");
        int res = 0;
        int ctk = 1;  // 跳过 "v"
        for (; ctk < str.size(); ctk += 1) {
            auto c = str.at(ctk);
            if (c >= '0' && c <= '9') {
                res *= 10;
                res += c.unicode() - '0';
            } else {
                break;
            }
        }
        if (ctk == 1 || res > 31) {  // 如果 "v" 后没有数字或超出范围
            return -1;
        } else {
            *chars_taken = ctk;
            return res;  // 返回向量寄存器编号
        }
    } 
    // 检查是否是特殊寄存器
1. 判断字符串是否是向量寄存器
  • str.at(0) == 'v':首先检查字符串的第一个字符是否是 'v',即是否是一个向量寄存器(如 v0v1、...、v31)。
2. 提取并解析寄存器编号
  • int res = 0;:用来存储解析出的寄存器编号。
  • int ctk = 1;:变量 ctk 用来遍历字符串,从第二个字符开始(跳过 'v' 字符)。
  • for (; ctk < str.size(); ctk++) { ... }:循环遍历字符串的每个字符,将数字部分拼接起来,直到遇到非数字字符或字符串结束为止。
3. 判断是否是有效的向量寄存器编号
  • if (ctk == 1 || res > 31)
    • ctk == 1:如果 ctk 等于 1,说明 v 后面没有任何数字,表示这不是一个有效的向量寄存器(比如 v 就是单独的字符串)。
    • res > 31:如果 res 大于 31,说明向量寄存器编号超出了有效范围(只能是 v0v31)。
    如果是无效的寄存器名,返回 -1,表示解析失败。
4. 成功解析寄存器编号
  • *chars_taken = ctk;:记录已经解析的字符数。这个值可以用来在后续处理中跳过已经解析的部分。
  • return res;:返回解析出来的寄存器编号,例如 01、...、31
5. 返回错误
  • 如果字符串既不是有效的向量寄存器,返回 -1

用途和作用:

  • 解析寄存器编号:这段代码的主要目的是识别和解析向量寄存器(v0v31),将其转换为对应的寄存器编号(031)。
  • 输入验证:同时它也在验证输入是否合法。如果输入字符串不符合有效的向量寄存器格式(例如 v 后没有数字,或者数字不在 0 到 31 之间),它会返回 -1,表示无效寄存器。
  • 进一步操作:这个解析操作通常会在指令解析的上下文中使用,确保能够正确处理 v0v31 这样的向量寄存器。如果解析失败,指令解析过程可能会抛出错误或跳过这条指令。

通俗总结:

这段代码就是负责确认输入的寄存器名是否是合法的向量寄存器名(如 v0v31),并提取出其中的寄存器编号供后续操作使用。如果解析失败,返回 -1,表示无效的寄存器名。

 

你说得对,虽然向量寄存器的命名是 v0v31,但实际上它们和普通的通用寄存器(如 x0x31)非常类似,只是在使用时需要特别区分。在实际的指令解析过程中,向量寄存器和通用寄存器共享相同的寄存器字段(例如 g 类型字段),不同之处在于如何在内存中组织和访问这些寄存器。

向量寄存器与普通寄存器的区别

  1. 寄存器命名和识别:如你所提到的,向量寄存器使用 v0v31 作为命名,而普通寄存器通常使用 x0x31。这就意味着,虽然它们在寄存器的命名上有区别,但是它们在硬件实现上可能共享相同的物理寄存器。

  2. 字段划分:虽然向量寄存器在名字上与普通寄存器有所不同,但在处理时,可以把它们当作普通寄存器来操作。在访问时,需要划分寄存器内部的不同字段。例如,向量寄存器可能具有多个元素(例如浮点数、整数、或是其他数据类型的多个值),并通过特定的操作(例如向量化指令)来访问这些元素。与普通寄存器不同的是,向量寄存器通常会支持并行处理多个数据项。

  3. 寄存器字段的划分:在处理指令时,向量寄存器的字段划分可能是关键。通过这些字段的划分,向量寄存器可以实现并行计算或向量化计算。而在普通寄存器的情况下,通常只包含一个标量值。在实际的指令集实现中,向量寄存器会有专门的操作来控制和访问这些向量元素。

代码中如何处理

在你的代码中,寄存器的解析部分确实是统一处理的,无论是普通寄存器还是向量寄存器,都通过类似的方式进行解析。通过字母(如 g)来表示寄存器类型,接着通过不同的字段描述符(如 5, 7)来确定它的特定位置。

当你解析一个指令时,parse_reg_from_string() 会根据给定的寄存器名称(如 v0x1)来解析其寄存器编号。这些编号之后会被传递到指令中去,指定需要操作的寄存器。

结合你的代码,如何修改和扩展

  1. 字段划分

    • 对于向量寄存器字段(例如 v0v31),你可以在 ArgumentDesc 中增加一个新的类型标识符,比如 v,用来指示该寄存器为向量寄存器。
  2. 向量寄存器的解析

    • 对于向量寄存器的解析,你可以扩展现有的解析函数,让它既能解析 x0x31 这样的普通寄存器,也能解析 v0v31 这样的向量寄存器。解析的结果可能仍然是一个寄存器编号,但在后续的操作中,你需要根据具体情况来处理向量寄存器的字段。
  3. 向量长度的处理

    • 对于向量长度,考虑为每个向量寄存器提供额外的字段,这样你就可以在后续的指令处理中使用这些信息来决定向量的长度,并实现向量化操作。
  4. 在指令执行时进行区分

    • 在执行向量化指令时,你需要根据字段划分来访问向量寄存器的各个元素,并应用相应的运算。可以根据 v 字段类型在执行过程中对寄存器进行适当的解码和处理

未完见下一篇 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值