如何解决在组装中检查输入字符最干净的方法是在0〜9之间
问题是在RISC-V中将字符串转换为int
如果存在不为0〜9的字符,请立即返回-1
但是我想知道是否有任何方法可以通过使用最小指令来检查它
我的方法是将48和57(对应于ASCII中的0〜9)放入临时寄存器,
并使用2个分支,首先检查 = 48
但是它使用了太多指令,并且需要额外的临时寄存器来存储48和57。还有其他有效的方法吗?
解决方法
是的,因为您仍然必须减去'0'
,所以要这样做,然后对c <= 9
或c < 10
进行无符号比较。有关范围检查技巧,请参见What is the idea behind ^= 32,that converts lowercase letters to upper and vice versa?。
我们可以在C语言中进行此操作,然后查看其编译方式,以此作为紧凑型RISC-V实现的起点。这个C的结构类似于NASM Assembly convert input to integer?中的asm,希望GCC或clang将使用类似的循环结构。如果您手动翻译它,则可能需要这种循环结构,或对其进行调整以在有序RISC-V上进行更好的软件流水线处理,尤其是隐藏负载使用延迟。这种循环结构在现代的x86上非常有用,在该x86上,OoO投机执行程序隐藏了分支和负载使用延迟。
// C intentionally written exactly like hand-written asm
// Translate this to asm by hand,including the loop structure.
// or compile it if you want more bloated asm.
unsigned str_to_uint(const unsigned char *p) {
unsigned dig = *p - '0';
unsigned total = dig; // peel first iter,optimize away the + 0 * 10
if (total < 10) // <10 can share a constant with *10
goto loop_entry;
else // fall through to the uncommon case of no valid digits
return 0;
do {
total = total*10 + dig;
loop_entry: // branch target = loop entry point
dig = *++p - '0';
} while(dig < 10);
return total;
}
在第一次迭代中,我使用taked分支跳过了total * 10 + dig
,因此我们最好将其作为循环的入口,以最大程度地减少总代码量。
另一个选择是将另一个循环迭代剥离到循环顶部。当使用-O3
或-O2
进行编译时,这就是GCC和clang所选择的。使用-Os
时,gcc将其优化为一个循环,其底部为j
,中间为btgu
。 (Godbolt compiler explorer)。我不知道要尝试的任何-march=
RISC-V拱形或调谐选项。
因此,如果您希望在代码大小和效率之间取得良好的平衡(尤其是对于常见的1或2位数字的情况),则应该手动“编译”它。
GCC使用(x<<3) + (x<<1)
乘以10; clang使用mul
(并且循环内的确在mul
和bltu
循环分支之间共享一个常数。不幸的是,循环外clang与9
比较,例如{{1} },因此它需要两个常量(RISC-V是否有一个9 < total
bge
比较?IDK,TODO /编辑是否欢迎忽略此优化)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。