Android:assembler_test.cc是如何进行测试的

上一篇条件跳转的范围不够了怎么办?扩容!讲述了跳转在汇编层面跳转指令是如何生成机器码的,以及当条件跳转的13位跳转空间不够的时候,是如何依托auipc和jalr来实现32位的条件跳转的。接下来我们来分析一下安卓中如何使用test.cc对编写的assembler进行测试。此处仅对扩容跳转进行测试。

测试源码

TEST_F(AssemblerRiscv64Test, LongBeq) {
  riscv64::Riscv64Label label;
  __ Beq(riscv64::A0, riscv64::A1, &label); // 条件跳转,但是跳转的范围超过了13位的有效表示数字
  constexpr uint32_t kAdduCount1 = (1u << 12) + 1;
  for (uint32_t i = 0; i != kAdduCount1; ++i) {
    __ Add(riscv64::ZERO, riscv64::ZERO, riscv64::ZERO);
  }
  __ Bind(&label);
  constexpr uint32_t kAdduCount2 = (1u << 12) + 1;
  for (uint32_t i = 0; i != kAdduCount2; ++i) {
    __ Add(riscv64::ZERO, riscv64::ZERO, riscv64::ZERO);
  }
  __ Beq(riscv64::A2, riscv64::A3, &label);
// 以上是需要调用assembler来实现机器码的生成,接下来要生成string类型的字符串,以供工具链生成机器码并用来和assembler生成的机器码进行对比

  uint32_t offset_forward = 2 + kAdduCount1;  // 2: account for auipc and jalr.
  // offset_forward表示的是向后跳转的offset, "auipc t2, 0x"获得pc的地址位auipc的起始地址,jalr跳转的是pc+offset的地址,所以想要获得在jalr结束处的pc地址,就要加上auipc和jalr自身的偏移量,此时+2表示为加上两条指令的数量
  offset_forward <<= 2;	// 将指令的数量左移两位也就是*4,得到了指令数量对应的大小
  offset_forward += (offset_forward & 0x800) << 1;  // Account for sign extension in jalr.
// 为了防止最高位为1的情况,该部分介绍依然位于文章开头的链接中

  uint32_t offset_back = -(kAdduCount2 + 1);  // 1: account for bne.
  // offset_back表示向前跳转,+1是因为auipc是获得了auipc处的pc地址,在jalr进行跳转的时候,需要将pc地址挪到bne开始处然后加偏移量才是真正的偏移量
  offset_back <<= 2;
  offset_back += (offset_back & 0x800) << 1;  // Account for sign extension in jalr.

  /*reg AT =T2*/
  std::ostringstream oss;
  oss <<
      "bne a0, a1, 1f\n"
     // beq被替换成三条指令进行测试,在编译器中是无法对汇编指令自动进行优化和扩容的,所以在测试的时候需要将扩容好的汇编指令输入
      "auipc t2, 0x" << std::hex << High20Bits(offset_forward) << "\n"
      "Jalr zero, 0x" << std::hex << SignExtend64<riscv64::kIImm12Bits>(offset_forward) << "(t2)\n"
      "1:\n" <<
      // 对于跳转指令来说,只能给其label的跳转,而直接给跳转指令偏移量的跳转是无法进行跳转的,所以此处用三个label替代了原来的一个label
      RepeatInsn(kAdduCount1, "add zero, zero, zero\n") <<
      "2:\n" <<
      RepeatInsn(kAdduCount2, "add zero, zero, zero\n") <<
      "bne a2, a3, 3f\n"
      "auipc t2, 0x" << std::hex << High20Bits(offset_back) << "\n"
      "Jalr zero, 0x" << std::hex << SignExtend64<riscv64::kIImm12Bits>(offset_back) << "(t2)\n"
      "3:\n";
  std::string expected = oss.str();
  DriverStr(expected, "LongBeq");
}

测试方法

在进行完测试代码的编写之后,直接编译utils文件即可,对单独代码的测试可以加上./out/host/linux-x86/nativetest/art_compiler_host_tests/assembler_riscv64_test --gtest_filter=*LongBeq
测试效果如图:
在这里插入图片描述

如果出错,如何查找问题

在移植的过程中无论多么简单的问题,想要一次成功是不可能的。如何寻找问题,是关键。当buffer中的机器码和gcc的机器码不一致的时候,会在测试执行的过程中直接输出与两个机器码的对比情况,这个就很烦,二进制代码谁看得懂?

这个问题,谷歌也帮你做好了。在art/compiler/utils/assembler_test_base.h中有这样两条指令,当测试出错的时候,可以将两种情况的汇编指令打印出。将kKeepDisassembledFiles设置为true,然后在tmpnam_处设置路径。
在这里插入图片描述
接着就可以查看两种编译器生成的汇编到底不同在哪里,当时是因为gcc处给string赋值的时候,jalr的跳转没有将寄存器设置为0寄存器。
在这里插入图片描述
流程大致是以上流程,在mips的基础之上,为了方便条件跳转的测试,将测试代码进行了改进。


void LongBranchCondTwoRegsHelper(void (riscv64::Riscv64Assembler::*f)(riscv64::GpuRegister,
                                                                  riscv64::GpuRegister,
                                                                  riscv64::Riscv64Label*),
                               const std::string& instr_name) { 
  std::string opposite_name = riscv64::Riscv64Assembler::Branch::OppositeCondition(instr_name);
  // 对instr_name取反,该函数为自己定义,但是在定义的时候需要注意,需要将OppositeCondition声明为static
  // 因为该函数位于branch类中,但是在调用的时候并没有生成类的对象来调用,而是直接调用了该函数,所以需要将该函数定义为static
  riscv64::Riscv64Label label;
  (Base::GetAssembler()->*f)(riscv64::A0, riscv64::A1, &label);
  // 此时调用模板类,这样可以方便不同类型的传参
  // constexpr uint32_t kAdduCount1 = (1u << 20) + 1;
  constexpr uint32_t kAdduCount1 = (1u << 20) + 0xfdf;
  for (uint32_t i = 0; i != kAdduCount1; ++i) {
    __ Add(riscv64::ZERO, riscv64::ZERO, riscv64::ZERO);
  }
  __ Bind(&label);
  constexpr uint32_t kAdduCount2 = (1u << 20) + 0xfdf;
  for (uint32_t i = 0; i != kAdduCount2; ++i) {
    __ Add(riscv64::ZERO, riscv64::ZERO, riscv64::ZERO);
  }
  (Base::GetAssembler()->*f)(riscv64::A0, riscv64::A1, &label);

  uint32_t offset_forward = 2 + kAdduCount1;  // 2: account for auipc and jalr.
  offset_forward <<= 2;
  offset_forward += (offset_forward & 0x800) << 1;  // Account for sign extension in jalr.

  uint32_t offset_back = -(kAdduCount2 + 1);
  offset_back <<= 2;
  offset_back += (offset_back & 0x800) << 1;  // Account for sign extension in jalr.
  
  std::ostringstream oss;
  oss <<
      opposite_name << " a0, a1, 1f\n" << 
      "auipc t2, 0x" << std::hex << High20Bits(offset_forward) << "\n"
      "jalr zero, 0x" << std::hex << SignExtend64<12>(offset_forward) << "(t2)\n" <<
      "1:\n" <<
      RepeatInsn(kAdduCount1, "add zero, zero, zero\n") <<
      "2:\n" <<
      RepeatInsn(kAdduCount2, "add zero, zero, zero\n") <<
      opposite_name << " a0, a1, 3f\n" << 
      "auipc t2, 0x" << std::hex << High20Bits(offset_back) << "\n"
      "jalr zero, 0x" << std::hex << SignExtend64<12>(offset_back) << "(t2)\n" << 
      "3:\n" ;
  std::string expected = oss.str();
  std::string LongCondBranch = "Long" + instr_name;
  // longCondBranch只是工具链生成的二进制文件的名字,用以区分不用测试生成的文件
  DriverStr(expected, LongCondBranch);
}


TEST_F(AssemblerRiscv64Test, LongBeq){
  LongBranchCondTwoRegsHelper(&riscv64::Riscv64Assembler::Beq, "beq");
}


TEST_F(AssemblerRiscv64Test, LongBne){
  LongBranchCondTwoRegsHelper(&riscv64::Riscv64Assembler::Bne, "bne");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值