我有一个初始化的字符串“ Hello,World!”。我想从中提取第一个字符(即“ H”)并将其比较一个在运行时传递到寄存器中的字符。
我尝试比较“ Hello,World!”的第一个字符。通过以下代码用“ H”表示:
"box"{
"quotations": [
{
"lifeAssured": [
"Hieu Vo"
],
}
]
}
但是,此代码终止而不跳转到while($row = $result->fetch_assoc()) {
$total = empty($row['total']) ? '-' : preg_replace('/[^0-9]+/','',$row['total']);
echo "<tr>";
echo "<td>" . $total . "</td>";
echo "</tr>";
}
标签。此外,程序的退出状态为global start
section .data
msg: db "Hello, World!", 10, 0
section .text
start:
mov rdx, msg
mov rdi, [rdx]
mov rsi, 'H'
cmp rdi, rsi
je equal
mov rax, 0x2000001
mov rdi, [rdx]
syscall
equal:
mov rax, 0x2000001
mov rdi, 58
syscall
,它是equal
的ASCII码。这使我尝试将72
传递给H
而不是72
,但这也导致程序终止而没有跳转到rsi
标签。
如何正确比较“世界你好!”中的第一个字符带有传递到寄存器的字符?
答案 0 :(得分:3)
您和@Rafael的答案使您的代码过于复杂化。
通常,您绝对不想在绝对地址的64位立即数上使用mov rdi, msg
。 (请参见Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array)
使用default rel
并使用cmp byte [msg], 'H'
。或者,如果您想要RDI中的指针以便可以在循环中递增,请使用lea rdi, [rel msg]
。
您的分支之间唯一不同的是RDI值。您无需复制RAX设置或syscall
,只需在RDI中获得正确的值,然后使分支彼此重新连接即可。 (或无分支地进行。)
@Rafael的答案出于某种原因仍在从字符串中加载8个字节,例如问题中的两个加载。大概是sys_exit
,它忽略了高字节,只设置了低字节的进程退出状态,但是为了好玩,让我们假装我们实际上希望所有8个字节都为syscall加载,而只比较低字节。>
default rel ; use RIP-relative addressing modes by default for [label]
global start
section .rodata ;; read-only data usually belongs in .rodata
msg: db "Hello, World!", 10, 0
section .text
start:
mov rdi, [msg] ; 8 byte load from a RIP-relative address
mov ecx, 'H'
cmp dil, cl ; compare the low byte of RDI (dil) with the low byte of RCX (cl)
jne .notequal
;; fall through on equal
mov edi, 58
.notequal: ; .labels are local labels in NASM
; mov rdi, [rdx] ; still loaded from before; we didn't destroy it.
mov eax, 0x2000001
syscall
尽可能避免写入AH / BH / CH / DH。它对RAX / RBX / RCX / RDX的旧值有错误的依赖性,或者如果您稍后读取完整的寄存器,则可能导致部分寄存器合并停顿。 @Rafael的答案不是那样,但是mov ah, 'H'
取决于某些CPU上AL的负载。请参见Why doesn't GCC use partial registers?和How exactly do partial registers on Haswell/Skylake perform? Writing AL seems to have a false dependency on RAX, and AH is inconsistent-mov ah, 'H'
对Haswell / Skylake上AH的旧值有错误的依赖性,即使AH与RAX分开重命名也是如此。但是AL不是,所以是的,这很可能对负载有错误的依赖性,从而使其无法并行运行,并使cmp
延迟一个周期。
无论如何,这里的TL:DR是不需要的话,不要乱写AH / BH / CH / DH。读取它们通常没问题,但可能会导致更糟的延迟。请注意,cmp dil, ah
是不可编码的,因为DIL仅可使用REX前缀访问,而AH仅可在没有REX前缀的情况下访问。
我选择RCX而不是RSI,因为CL不需要REX前缀,但是由于我们需要查看RDI的低字节(dil),因此无论如何在cmp上都需要一个REX前缀。我本可以使用mov cl, 'H'
来保存代码大小,因为错误地依赖RCX的旧值可能没有问题。
顺便说一句,cmp dil, 'H'
和cmp dil, cl
一样好。
或者,如果我们将零扩展的字节加载到完整的RDI中,则可以使用cmp edi, 'H'
而不是其低8版本。 (零扩展加载是在现代x86-64上处理字节和16位整数的正常/建议方法。合并到旧寄存器值的低字节通常会降低性能,这会降低性能。是Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register?的原因。)
我们可以选择CMOV而不是分支。对于代码大小和性能,这有时更好,有时更好。
版本2,仅实际加载1个字节:
start:
movzx edi, byte [msg] ; 1 byte load, zero extended to 4 (and implicitly to 8)
mov eax, 58 ; ASCII ':'
cmp edi, 'H'
cmove edi, eax ; edi = (edi == 'H') ? 58 : edi
; rdi = 58 or the first byte,
; unlike in the other version where it had 8 bytes of string data here
mov eax, 0x2000001
syscall
(此版本看起来短很多,但是大多数额外的行是空格,注释和标签。优化为cmp
即刻使这4条指令,而不是前5条mov eax
/ syscall
,但两者相等。
答案 1 :(得分:1)
我将并排解释更改(希望更容易理解):
global start
section .data
msg: db "Hello, World!", 10, 0
section .text
start:
mov rdx, msg
mov al, [rdx] ; moves one byte from msg, H to al, the 8-bit lower part of ax
mov ah, 'H' ; move constant 'H' to the 8-bit upper part of ax
cmp al, ah ; compares H with H
je equal ; yes, they are equal, so go to address at equal
mov rax, 0x2000001
mov rdi, [rdx]
syscall
equal: ; here we are
mov rax, 0x2000001
mov rdi, 58
syscall
如果您不了解al
,ah
,ax
的使用/提及,请参阅General-Purpose Registers。