- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试编写一些高性能汇编函数作为练习,并且在运行程序时遇到了一个奇怪的段错误,但在 valgrind 或 nemiver 中没有。
基本上不应该运行的 cmov,带有越界地址,即使条件始终为假,也会使我出现段错误
我有一个快速和一个慢速版本。缓慢的一直有效。除非我在 adb 或 nemiver 上运行,否则快速的工作,除非它收到一个非 ascii 字符,否则它会严重崩溃。
ascii_flags 只是一个 128 字节的数组(末尾有一点空间)包含所有 ASCII 字符(字母、数字、可打印等)上的标志。
这有效:
ft_isprint:
xor EAX, EAX ; empty EAX
test EDI, ~127 ; check for non-ascii (>127) input
jnz .error
mov EAX, [rel ascii_flags + EDI] ; load ascii table if input fits
and EAX, 0b00001000 ; get specific bit
.error:
ret
ft_isprint:
xor EAX, EAX ; empty EAX
test EDI, ~127 ; check for non-ascii (>127) input
cmovz EAX, [rel ascii_flags + EDI] ; load ascii table if input fits
and EAX, flag_print ; get specific bit
ret
ft_isprint:
mov RAX, 128 ; load default index
test RDI, ~127 ; check for non-ascii (>127) input
cmovz RAX, RDI ; if none are found, load correct index
mov AL, byte [ascii_flags + RAX] ; dereference index into least sig. byte
and RAX, flag_print ; get specific bit (and zeros rest of RAX)
ret
ft_isprint_branch:
test RDI, ~127 ; check for non-ascii (>127) input
jnz .out_of_bounds ; if non-ascii, jump to error handling
mov AL, byte [ascii_flags + RDI] ; dereference index into least sig. byte
and RAX, flag_print ; get specific bit (and zeros rest of RAX)
ret
.out_of_bounds:
xor RAX, RAX ; zeros return value
ret
ft_isprint_compact:
xor RAX, RAX ; zeros return value preemptively
test RDI, ~127 ; check for non-ascii (>127) input
jnz .out_of_bounds ; if non-ascii was found, skip dereferenciation
mov AL, byte [ascii_flags + RDI] ; dereference index into least sig. byte
and RAX, flag_print ; get specific bit
.out_of_bounds:
ret
ft_isprint_compact:
sub EDI, 32 ; substract 32 from input, to overflow any value < ' '
xor EAX, EAX ; set return value to 0
cmp EDI, 94 ; check if input <= '~' - 32
setbe AL ; if so, set return value to 1
ret
ft_isprint_branch:
xor EAX, EAX ; set return value to 0
cmp EDI, 127 ; check for non-ascii (>127) input
ja .out_of_bounds ; if non-ascii was found, skip dereferenciation
mov AL, byte [rel ascii_flags + EDI] ; dereference index into least sig. byte
.out_of_bounds:
ret
ft_isprint:
mov EAX, 128 ; load default index
cmp EDI, EAX ; check if ascii
cmovae EDI, EAX ; replace with 128 if outside 0..127
; cmov also zero-extends EDI into RDI
; movzx EAX, byte [ascii_flags + RDI] ; alternative to two following instruction if masking is removed
mov AL, byte [ascii_flags + RDI] ; load table entry
and EAX, flag_print ; apply mask to get correct bit and zero rest of EAX
ret
-O3 a.out
1 cond 153185, 2 branch 238341 3 no_table 145436
1 cond 148928, 3 branch 248954 2 no_table 116629
2 cond 149599, 1 branch 226222 3 no_table 117428
2 cond 117258, 3 branch 241118 1 no_table 147053
3 cond 117635, 1 branch 228209 2 no_table 147263
3 cond 146212, 2 branch 220900 1 no_table 147377
-O3 main.c
1 cond 132964, 2 branch 157963 3 no_table 131826
1 cond 133697, 3 branch 159629 2 no_table 105961
2 cond 133825, 1 branch 139360 3 no_table 108185
2 cond 113039, 3 branch 162261 1 no_table 142454
3 cond 106407, 1 branch 133979 2 no_table 137602
3 cond 134306, 2 branch 148205 1 no_table 141934
-O0 a.out
1 cond 255904, 2 branch 320505 3 no_table 257241
1 cond 262288, 3 branch 325310 2 no_table 249576
2 cond 247948, 1 branch 340220 3 no_table 250163
2 cond 256020, 3 branch 415632 1 no_table 256492
3 cond 250690, 1 branch 316983 2 no_table 257726
3 cond 249331, 2 branch 325226 1 no_table 250227
-O0 main.c
1 cond 225019, 2 branch 224297 3 no_table 229554
1 cond 235607, 3 branch 199806 2 no_table 226286
2 cond 226739, 1 branch 210179 3 no_table 238690
2 cond 237532, 3 branch 223877 1 no_table 234103
3 cond 225485, 1 branch 201246 2 no_table 230591
3 cond 228824, 2 branch 202015 1 no_table 226788
最佳答案
cmov
是一个 ALU 选择操作,它总是在检查条件之前读取两个源 .使用内存源不会改变这一点。如果条件为假,它不像一个 ARM 谓词指令,它就像一个 NOP。 cmovz eax, [mem]
也无条件地写入 EAX,无论条件如何,都将零扩展到 RAX。
就大部分 CPU 而言(乱序调度器等),cmovcc reg, [mem]
处理方式与 adc reg, [mem]
完全一样: 3 输入 1 输出 ALU 指令 . ( adc
写入标志,与 cmov
不同,但不要紧。)微融合内存源操作数是一个单独的 uop,恰好是同一 x86 指令的一部分。这也是 ISA 规则的运作方式。
真的,这是 cmovz
更合适的助记符作为 selectz
x86 的唯一条件加载 (不会在错误地址上出错,只是可能运行缓慢)是:
#PF
页面错误异常,这不可避免地会发生)慢)。在某些 CPU 上,这种快速处理会导致 Meltdown 攻击。 >.< 见 http://blog.stuffedcow.net/2018/05/meltdown-microarchitecture/ .rep lodsd
RCX=0 或 1。(不快也不高效,但微码分支是特殊的,不能从英特尔 CPU 上的分支预测中受益。参见 What setup does REP do?。Andy Glew 提到了微码分支错误预测,但我认为这些与正常分支未命中,因为似乎有固定成本。)vpmaskmovd/q
/AVX1 vmaskmovps/pd
.对于掩码为 0 的元素,错误会被抑制。即使来自合法地址的全 0 掩码的掩码加载也需要~200 周期的微码辅助和基址+索引寻址模式。)见 section 12.9 CONDITIONAL SIMD PACKED LOADS AND STORES和英特尔优化手册中的表 C-8。 (在 Skylake 上,使用全零掩码存储到非法地址也需要帮助。)maskmovdqu
仅存储(并具有 NT 提示)。只有具有 dword/qword(而不是字节)元素的类似 AVX 指令具有加载形式。cmov
您无条件使用的地址,选择从哪个地址加载。例如如果你有
0
从其他地方加载,那会起作用。但是你必须在寄存器中计算表索引,而不是使用寻址模式,所以你可以
cmov
最后的地址。
table + 128
加载.
[rel]
仅在寻址模式中不涉及寄存器(RIP 除外)时才有效。 RIP 相对寻址替换了 2 种冗余方式之一(以 32 位代码)来编码
[disp32]
.它使用较短的非 SIB 编码,而 ModRM+SIB 仍然可以编码绝对
[disp32]
没有寄存器。 (对于像
[fs: 16]
这样的地址很有用,用于相对于带有段基的线程本地存储的小偏移量。)
default rel
在您的文件顶部 .
[symbol]
将是 RIP 相对的,但
[symbol + rax]
惯于。不幸的是,NASM 和 YASM 默认为
default abs
.
[reg + disp32]
是一种在与位置相关的代码中索引静态数据的非常有效的方法,只是不要自欺欺人地认为它可以与 RIP 相关。见
32-bit absolute addresses no longer allowed in x86-64 Linux? .
[rel ascii_flags + EDI]
也很奇怪,因为
您在 x86-64 代码中以寻址模式使用 32 位寄存器 .通常没有理由花费地址大小前缀来将地址截断为 32 位。
[disp32 + edi]
而不是
mov esi,edi
或零扩展的东西。如果您是故意这样做的,请务必说明您使用 32 位寻址模式的原因。
cmov
在索引上将为您零扩展到 64 位。
[disp32]
寻址模式比 RIP-relative 差(长 1 个字节),但
[reg + disp32]
当位置相关代码和 32 位绝对地址没问题时,寻址模式非常好。 (例如 x86-64 Linux,但不是 OS X,其中可执行文件总是映射到低 32 位之外。)请注意它不是
rel
.
; position-dependent version taking advantage of 32-bit absolute [reg + disp32] addressing
; not usable in shared libraries, only non-PIE executables.
ft_isprint:
mov eax, 128 ; offset of dummy entry for "not ASCII"
cmp edi, eax ; check if ascii
cmovae edi, eax ; replace with 128 if outside 0..127
; cmov also zero-extends EDI into RDI
movzx eax, byte [ascii_flags + rdi] ; load table entry
and al, flag_print ; mask the desired flag
; if the caller is only going to read / test AL anyway, might as well save bytes here
ret
如果您的表中的任何现有条目具有与您想要的高输入相同的标志 ,例如也许条目
0
你永远不会在隐式长度的字符串中看到,你仍然可以异或零 EAX 并将你的表保持在 128 个字节,而不是 129 个字节。
test r32, imm32
占用的代码字节比您需要的多 .
~127 = 0xFFFFFF80
将适合符号扩展字节,但不是
TEST r/m32, sign-extended-imm8
编码。
cmp
有这样的编码,不过,基本上就像所有其他立即指令一样。
cmp edi, 127
检查 127 以上的未签名/
cmovbe eax, edi
或
cmova edi, eax
.这节省了 3 个字节的代码大小。或者我们可以使用
cmp reg,reg
节省 4 个字节使用
128
我们用于表索引。
and al, imm8
仅为 2 个字节,而
and r/m32, sign-extended-imm8
为 3 个字节.只要调用者只读取 AL,它在任何 CPU 上都不会变慢。在 Sandybridge 之前的 Intel CPU 上,在 AND 到 AL 之后读取 EAX 可能会导致部分寄存器停顿/减速。如果我没记错的话,Sandybridge 不会为读-修改-写操作重命名部分寄存器,而且 IvB 和更高版本根本不会重命名 low8 部分寄存器。
mov al, [table]
而不是
movzx
保存另一个代码字节。较早的
mov eax, 128
已经打破了对 EAX 旧值的任何错误依赖,因此它不应该有性能下降。但是
movzx
不是一个坏主意。
关于assembly - 由于从越界内存中跳过 cmov,难以调试 SEGV,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54050188/
我在 _WebTryThreadLock 中偶尔会看到像这样的崩溃报告: Exception Type: SIGSEGV Exception Codes: SEGV_ACCERR a
vector *v =new vector[size]; for(i=0;i>(size/*, vector(1024)*/); 然后使用它 for(auto v& : vv) { v.pus
我有以下代码,它被 SEGV 信号杀死。使用调试器表明它被 main() 中的第一个 sem_init() 杀死。如果我注释掉第一个 sem_init() ,第二个会导致同样的问题。我试图弄清楚是什么
我目前正在调试我们的一个 C++ 应用程序中的段错误,这让我很难过,因为在发生段错误时没有生成核心文件。 在阅读和检查 ulimits 等之后,我可以重现核心文件未生成的情况。它似乎以某种方式与线程有
我在 strace 下运行了一个用汇编语言编写的简单程序,该程序仅执行 SYS_exit。 _start: mov rax, 0x3C mov rdi, 0x0 syscall
我在热门编码网站 codechef 上提交名为“Money Transformation”的问题时遇到问题。每次我提交解决方案时,我都会收到运行时错误名称 SIG SEGV。经过一些搜索在谷歌上我发现
Getting Segmentation error ,请在下面的c代码逻辑中帮助问题。程序因信号 SIGSEGV、段错误而终止。 /* * For your reference: * * Si
我有一个经过良好测试且没有崩溃的代码库(主要是 C++)。大多。代码的一部分——不可替代、难以维护或改进并链接到一个二进制库*——导致所有崩溃。这些不会经常发生,但一旦发生,整个程序就会崩溃。
我已经安装了一个处理程序(例如,crashHandler()),它具有一些文件输出功能。它是一个使用 crashHandler() 注册 SIGSEGV 的 linux 线程。需要写入文件,因为它将堆
我在 ubuntu 11.10 上安装了 vim-gnome 并且可以正常工作。使用 vundle 并安装了 seeral 软件包,一切正常。 我已经安装了 command-t,但在编译 C 扩展之前
我正在尝试编写一些高性能汇编函数作为练习,并且在运行程序时遇到了一个奇怪的段错误,但在 valgrind 或 nemiver 中没有。 基本上不应该运行的 cmov,带有越界地址,即使条件始终为假,也
我试图让 ASAN 使用一个程序,但我所做的任何事情都会导致 ASAN:DEADLYSIGNAL ,所以我尝试缩小范围并使用只有几个编译器选项的小型测试程序,只是为了看看它是否可以工作: $ cat
将 vim 与 pathogen 和 rails.vim 一起使用时,有时会导致 vim 崩溃。这是我的崩溃控制台日志文件。我怎样才能防止这种情况发生? Process: vim [1
我有一个应用程序可以扫描文件并收集有关文件的元数据。一个功能是获取文件的文件大小。为此,我使用了 winapi 函数 GetFileSizeEx(Handle, PLARGE_INTEGER) 。参数
我正在尝试调整结构的 vector 元素的大小,它会导致 segv。但是当我为一些小结构单独做它时它工作得很好。我很想知道它是如何将内存分配给结构的,其中有一个可以调整大小的 vector 元素。下面
试图在我的小电脑上构建一个矩阵,数组变大,内存容量用完,然后我得到了一个segv,我想知道C++中是否有一种方法可以在不跳出的情况下检测segv并然后继续? unsigned m = 10000; u
上下文化,在编译我的代码后,我收到了一个 SEGV 信号,这与未经授权的内存访问有关。考虑到源代码并且代码在最近的更改之前可以正常工作。为什么我会收到此信号? Note: Motif's type r
我收到错误 SEGV on unknown address 运行我的程序时。我很确定它来自 fgets() 但我不太清楚为什么。在此之前,我使用 scanf() 并且运行良好。输入很好地进入了数组,但
以下程序在使用 g++ 退出后出现段错误: #include #include
我正在构建一个包含大量 Android 源代码的库。如果为 Linux x86 编译,该库工作正常。我正在使用独立的工具链构建它。我之前在其他库中已经成功地做到了这一点,所以我觉得我的设置很好。无论如
我是一名优秀的程序员,十分优秀!