ksco 的工作日志
245 subscribers
168 photos
10 videos
4 files
84 links
内容主要取决于我正在做的东西,目前主要是模拟器 / DBT 之类的散乱话题。
Download Telegram
昨天给 3A6000 装机然后安装了 AOSC OS,今天在上面做了一整天 box64 开发。不得不夸的是,不管是硬件还是系统,体验都是无可挑剔的。
🤔1
LoongArch 的 LBT 扩展也相当好玩,为了方便之后对比手动模拟 eflags 和直接使用 LBT 扩展的性能差距,我们同时实现了这两套方案。我已经等不及实现足够的指令后 benchmark 一下了
🤯1
真漂亮
LoongArch VSCode 折腾小计

最近因为需要在 LoongArch 上写一些 C 代码,所以折腾了一下 VSCode 环境。

本体下载:

https://github.com/Xiao-Tao/vscode-LA64/releases

在 Loongson 群里搜到的群友打包的 code-oss,解压就能用,版本是 1.80.1。

VSCode 插件:

因为没有 marketplace,所以需要手动下载 VSIX 文件来安装。

- GitLens:

https://github.com/gitkraken/vscode-gitlens/releases/tag/v13.0.0

需要下载旧版本,新版本不兼容。我用的是 13.0.0,再新一点的可能也能用,但我懒得试了。

- LoongArch Assembly:

https://github.com/FreeFlyingSheep/loongarch-assembly/releases

汇编语法高亮。

- C/C++ IntelliSense

https://github.com/microsoft/vscode-cpptools/releases

直接下载最新版的 cpptools-linux.vsix (x86_64)安装即可正常使用(需要安装 latx)。
Forwarded from sterprim | SIGTERM
^ box64 小伙伴把 libwrap + elfloader 拿出来,移植到了他的 RV 模拟器上
box64 中对于 Self-Modifying Code 的处理:
所有的 guest 代码页都被设置为了 read-only,所以当有指令试图写入到 guest 代码页的时候,就会当场触发一个 SIGSEGV。

box64 的全局 signalhandler 接收到 SIGSEGV 信号后,首先会搜索出事的 pc 是否在某个 dynablock 中,如果确实是来自 dynablock,就会检查 si_addr 是否属于 guest 代码页,两者都吻合,就会 --

1. 把那个 dynaloblock 设置为 dirty;
2. 然后将相应的 guest 代码页设置为可写;
3. 从 ucontext 里面把 x86 状态恢复出来[1];
4. siglongjump 回 run_code() ,使用解释器去执行 SMC 代码;
5. 解释器会尽快退出执行,跳转回 DynaRec。

对于任何标记为 dirty 的 dynablock,DynaRec 会检查 CRC 来确定是不是真的脏了,是的话就重建,否则就重新标记为 clean。

[1] box64 中每一个 x86 的寄存器都被一一映射到了 host 寄存器上,且 codegen 会保证对于每一条指令,生成的代码对于 x86 寄存器写入一定在内存写入之后,所以不用担心, ucontext 里面的状态一定是正确的。

这就是 x86 模拟器支持 SMC 所需要付出的代价。

RISC 则要容易得多,比如对于 RISC-V 模拟器来说,可以选择直接 intercept fence.i 和 __clear_cache() 然后更新相应的 dynablock 即可。
👏2🎃1
https://github.com/FEX-Emu/FEX/blob/main/docs/DeferredSignals.md

FEX relies on guest signal handlers returning via sigreturn to handle stacked deferred signals, so a longjmp would interfere with this

🤨 所以 “Classical signal masking” 到底是哪里不好。。
22:00 开会,21:50 的时候我家猫在他的跑步机上拉了一大泡,然后在上面猛跑把屎甩的到处都是,这会刚收拾完。
都五岁了之前从来没有乱拉过,这是怎么了 🤔️
🤔1
写了一种很新的 codegen
今天 GitHub 给我推了一个很有意思的东西 fwsGonzo/libriscv,从名字也能看出个大概,这是一个用户空间的 RISC-V 模拟器库,提供了常规的解释器实现。

但除此之外,它还有一个很有趣的东西:开启某个编译期选项后,libriscv 就会在运行 guest 程序前扫描它的所有代码,把可以被安全翻译的一些片段翻译成 plain C 代码,然后调用本地的 C 编译器编译后放到代码缓存中,供后续的运行时使用。

例如,下面的代码序列的前 4 条指令就可以被安全地翻译:

1:
li a0, 100
beqz a0, 1f
addi a0, a0, -1
j 1b
1:
ret


但最后的 ret 则无法翻译,因为 ra 的值是编译期未知的,所以扫描器就会终止当前的代码块,跳过这条指令,然后再开始翻译下一段。

以上是背景。

在扫描器开始工作之前,其实还有一遍可选的扫描,这个是今天的主题。

这遍可选的扫描会去尝试寻找 auipc gp,imm 指令以及它后面紧跟着的 addi[w] gp,gp,imm 。很明显这两条指令是在给 gp 赋值。然后 libriscv 会把要赋的值算出来,保存起来,这遍扫描就完成了。

有人可能一眼就看出来了,这是在找 load_gp

0000000000023572 <load_gp>:
  23572:       0028e197 auipc   gp,0x28e
  23576:       30618193 addi   gp,gp,774 # 2b1878 <__global_pointer$>
  2357a:       8082     ret
      ...


按照 RISC-V 的 ABI, gp 寄存器是 global pointer ,始终指向 .data 段中的一个固定的地方,用于做链接器的松弛。尽管这个设定非常的嵌入式,但编译器通常也不会把 gp 寄存器挪作他用。所以对于遵循 ABI 的绝大多数 RISC-V 程序而言, gp 寄存器打从程序运行的一开始,其中保存的便是一个常量。

既然我们已经知道了这个常量的值,那么每当有指令需要读取 gp 寄存器时,我们就可以安全地替换成字面量。例如对于 addi a0,gp,32 ,通常情况下,生成的 C 代码是:

GPRs[a0] = GPRs[gp] + 32;


但开启这个优化后,生成的代码就变成了:

GPRs[a0] = 0x2b1878 + 32;


很明显,后者更优。
80 块的自助,物价真的便宜了
🤩4
?(都 bcc 到我这儿来了,不会是超级 AOE 吧
突然接了个 numpy rvv 1.0 的活,今天搭建了一下开发环境。因为没有可用的 rvv 1.0 硬件,所以最后使用的方案是 x86 archlinux + qemu user 8.2 + riscv archlinux rootfs。

VSCode Remote 直接通过 x86 连到 rootfs 的项目目录下,rootfs 里面配置好 clang、python、meson 以及 virtualenv 之类的开发环境,最后获得了一个接近原生的开发体验。

archriscv 的包版本都很新,clang 直接就有 rvv 1.0 instrinsic 的支持,完全不用折腾工具链。唯一的缺点是 qemu user 的性能确实不太够看,风扇狂转但编译并不快。
🎉3🥰2🤔1
原来 latx 中的 x 是这个意思
🤔1😐1😇1🤷1
ksco 的工作日志
突然接了个 numpy rvv 1.0 的活,今天搭建了一下开发环境。因为没有可用的 rvv 1.0 硬件,所以最后使用的方案是 x86 archlinux + qemu user 8.2 + riscv archlinux rootfs。 VSCode Remote 直接通过 x86 连到 rootfs 的项目目录下,rootfs 里面配置好 clang、python、meson 以及 virtualenv 之类的开发环境,最后获得了一个接近原生的开发体验。 archriscv 的包版本都很新,clang…
继续 numpy,因为需要支持最新 rvv intrinsic 的编译器,但 gcc 需要等 14 才有,所以切换到了 clang 17。切换过去后编译完运行测试发现某个测例失败,最后整理出来下面这段代码:


import numpy as np

code = 'g' # long double
fnan = np.array(np.nan, dtype=code)
fone = np.array(1.0, dtype=code)
with np.errstate(all='raise'):
print(np.floor_divide(fnan, fone))


在 clang 编译的 numpy 上,运行这段代码会报错: FloatingPointError: invalid value encountered in floor_divide ,而 gcc 编译器则可以输出预期的 nan

于是怀疑是编译器的问题,尝试构造一个最小复现,然后就在 Python 和 C 代码里逛了一晚上,最后终于找到了 floor_divide 调用的底层函数 npy_divmodl() ,又一通精简后,构造出如下复现代码:


bool test(long double a, long double b)
{
long double m = fmodl(a, b);
return isless(m, (long double)0);
}

int main(void) {
fetestexcept(FE_INVALID);
long double a = NAN, b = 1.0;
printf("%d\n", test(a, b));
if(fetestexcept(FE_INVALID)) printf("raised\n");
return 0;
}


这段代码如果用 clang rv64 编译运行会输出 raised,用 gcc rv64 则不会,经过和 clang x64 交叉验证,发现 clang rv64 是有问题的。
🤯4
macOS Preview 怎么这么菜,M2 打开 v-intrinsic-spec.pdf 随便搜点东西都直接卡死,也就 4000 页而已。
👌1
ksco 的工作日志
Photo
没活了
🤯1🆒1