이 지침의 차이점이 무엇인지 알고 싶습니다.
MOV AX, [TABLE-ADDR]
과
LEA AX, [TABLE-ADDR]
이 지침의 차이점이 무엇인지 알고 싶습니다.
MOV AX, [TABLE-ADDR]
과
LEA AX, [TABLE-ADDR]
답변:
LEA
로드 유효 주소를 의미MOV
로드 값을 의미즉, LEA
주소를 지정할 항목에 대한 포인터를로드하는 반면 MOV는 해당 주소의 실제 값을로드합니다.
LEA
사소한 주소 계산을 수행하고 결과를 [나중에 사용하기 위해] 저장하도록하는 것이 목적입니다 .
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
관련된 상수가있는 경우 MOV
(어셈블러의 상수 계산을 통해) 때로는 가장 간단한 사용 사례와 겹치는 것처럼 보일 수 있습니다 LEA
. 여러 기본 주소 등으로 여러 부분으로 구성된 계산이있는 경우 유용합니다.
LAHF
: FLAGS를 AH 레지스터에로드 합니다. CLR의 CIL에서 (높은 수준의 스택 기반 추상 기계 인 경우, 로드 라는 용어 는 명목 스택에 값을 넣는 것을 의미하며 일반적으로 l
...이고, s
등가는 반대입니다). 이 노트 : cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html )는 실제로 구별이 적용되는 아키텍처가 있음을 제안합니다.
NASM 구문에서 :
mov eax, var == lea eax, [var] ; i.e. mov r32, imm32
lea eax, [var+16] == mov eax, var+16
lea eax, [eax*4] == shl eax, 2 ; but without setting flags
MASM 구문에서 OFFSET var
로드 대신 mov-immediate를 얻는 데 사용하십시오 .
mov eax, var
로드와 동일 하며 레이블을 즉시 상수로 mov eax, [var]
사용 mov eax, OFFSET var
하려면 사용해야합니다.
lea
RIP 상대 주소 지정을위한 64 비트 모드를 제외하고는 더 나쁜 선택입니다. mov r32, imm32
더 많은 포트에서 실행됩니다. lea eax, [edx*4]
그렇지 않으면 하나의 명령으로 수행 할 수없는 복사 및 시프트입니다.하지만 동일한 레지스터에서 LEA는을 [eax*4]
요구 하기 때문에 더 많은 바이트를 사용 합니다 disp32=0
. (그러나 시프트와 다른 포트에서 실행됩니다.) agner.org/optimize 및 stackoverflow.com/tags/x86/info를 참조하십시오 .
명령 MOV reg, addr은 주소 addr에 저장된 변수를 register reg로 읽는 것을 의미합니다. LEA reg, addr 명령어는 주소에 저장된 변수가 아닌 주소를 레지스터 reg로 읽는 것을 의미합니다.
MOV 명령어의 다른 형태는 MOV reg, immdata입니다. 즉, 즉시 데이터 (즉, 상수) immdata를 레지스터 reg로 읽습니다. LEA reg의 addr, addr가 상수 (즉, 고정 오프셋) 인 경우 LEA 명령은 본질적으로 즉각적인 데이터와 동일한 상수를로드하는 동등한 MOV reg, immdata 명령과 정확히 동일합니다.
리터럴 만 지정하면 차이가 없습니다. LEA는 더 많은 능력을 가지고 있으며 여기에서 읽을 수 있습니다.
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
leal TextLabel, LabelFromBssSegment
당신이 smth를 얻을 수 없습니다 . 처럼 .bss .lcomm LabelFromBssSegment, 4
, movl $TextLabel, LabelFromBssSegment
그렇지 않습니까?
lea
레지스터 대상이 필요 하기 때문에 소스와 메모리 대상을 mov
가질 수 있기 때문 imm32
입니다. 물론이 제한은 GNU 어셈블러에만 국한되지는 않습니다.
MOV AX, [TABLE-ADDR]
입니다. 따라서 큰 차이가 있습니다. 동등한 명령은mov ax, OFFSET table_addr
사용 된 어셈블러에 따라 다릅니다.
mov ax,table_addr
MASM에서는
mov ax,word ptr[table_addr]
따라서 table_addr
오프셋이 아닌 에서 첫 번째 바이트를로드합니다 table_addr
. 대신 사용해야합니다
mov ax,offset table_addr
또는
lea ax,table_addr
같은 방식으로 작동합니다.
lea
table_addr
로컬 변수 인 경우 버전도 잘 작동합니다.
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
이전 답변 중 어느 것도 내 혼란의 바닥에 도달하지 못했기 때문에 내 자신을 추가하고 싶습니다.
내가 누락 된 것은 lea
작업이 괄호 사용을 처리하는 방식과 다르게 처리한다는 것입니다 mov
.
C를 생각해보십시오 . long
내가 호출 하는 배열이 있다고 가정 해 봅시다 array
. 이제 식은 array[i]
주소 array + i * sizeof(long)
[1]의 메모리에서 값을로드하여 역 참조를 수행합니다 .
반면에, 표현을 고려하십시오 &array[i]
. 여기에는 여전히 하위 표현식이 포함되어 array[i]
있지만 역 참조는 수행되지 않습니다! 의 의미 array[i]
가 변경되었습니다. 더 이상 지연을 수행하는 것이 아니라 대신 일종의 사양 으로 작동하여 &
찾고있는 메모리 주소를 알려줍니다 . 원한다면 &
, 역 참조를 "취소" 한다고 생각할 수도 있습니다.
두 가지 유스 케이스는 여러면에서 유사하기 때문에 구문을 공유 array[i]
하지만 &
구문의 해석 방법 에 대한 변경 유무 . 가 없으면 &
역 참조이며 실제로 배열에서 읽습니다. 로는 &
그렇지 않습니다. 값 array + i * sizeof(long)
은 여전히 계산되지만 역 참조되지는 않습니다.
상황은 매우 유사하다 mov
와 lea
. 을 사용하면 mov
에는 발생하지 않는 역 참조가 발생합니다 lea
. 이것은 두 가지 모두에서 발생하는 괄호 사용에도 불구하고 있습니다. 예를 들어, movq (%r8), %r9
와 leaq (%r8), %r9
. 로 mov
괄호는 "역 참조"를 의미합니다. 와 함께 lea
하지 않습니다. 이 array[i]
없는 경우 "역 참조"만 의미 하는 것과 비슷합니다 &
.
예를 들면 다음과 같습니다.
코드를 고려하십시오
movq (%rdi, %rsi, 8), %rbp
이것은 메모리 위치의 값을 %rdi + %rsi * 8
레지스터로 로드합니다 %rbp
. 즉, 레지스터 %rdi
의 값과 레지스터의 값을 가져옵니다 %rsi
. 후자에 8을 곱한 다음 전자에 더하십시오. 이 위치에서 값을 찾아 레지스터에 배치하십시오 %rbp
.
이 코드는 C 선에 대응하고 x = array[i];
, array
해진다 %rdi
과 i
하게 %rsi
하고 x
해진다 %rbp
. 이 8
어레이에 포함 된 데이터 유형의 길이이다.
이제 다음을 사용하는 유사한 코드를 고려하십시오 lea
.
leaq (%rdi, %rsi, 8), %rbp
의 사용이 movq
역 참조 에 해당하는 것처럼, leaq
여기서 의 사용은 역 참조 가 아님에 해당합니다 . 이 조립 라인은 C 라인에 해당합니다 x = &array[i];
. 역 참조에서 단순히 위치 지정으로 &
의 의미 를 변경 한다는 점을 상기하십시오 array[i]
. 마찬가지로, 참조를 사용하면 역 참조에서 위치 지정으로 leaq
의 의미 가 변경 (%rdi, %rsi, 8)
됩니다.
이 코드 줄의 의미는 다음과 같습니다. 레지스터 %rdi
의 값과 레지스터 의 값을 가져옵니다 %rsi
. 후자에 8을 곱한 다음 전자에 더하십시오. 이 값을 레지스터에 넣습니다 %rbp
. 메모리에서로드되는 것이 아니라 산술 연산 만 수행됩니다 [2].
주 내 설명 사이의 유일한 차이 있음 leaq
과 movq
즉이 movq
역 참조를 수행하고 leaq
있지 않습니다. 사실, leaq
설명 을 작성하기 위해 기본적으로의 설명을 복사하여 붙여 넣은 movq
다음 "이 위치에서 값 찾기"를 제거했습니다.
요약하면 : movq
대는 leaq
그들과 같이 괄호를 사용하여 치료하기 때문에 까다 롭습니다 (%rsi)
와 (%rdi, %rsi, 8)
다르게. 에서 movq
(그리고 다른 모든 명령을 제외하고는 lea
있는 반면)이 괄호는 진짜 역 참조를 나타내는 leaq
그렇지 않은 순수하게 편리한 구문입니다.
[1] array
의 배열 인 long
경우 식은 array[i]
주소에서 값을로드 한다고합니다 array + i * sizeof(long)
. 이것은 사실이지만 해결해야 할 미묘함이 있습니다. C 코드를 작성하면
long x = array[5];
이것은 입력과 동일 하지 않습니다
long x = *(array + 5 * sizeof(long));
그것은 이전의 진술에 근거 해야하는 것처럼 보이지만 그렇지 않습니다.
진행중인 것은 C 포인터 추가에 트릭이 있다는 것입니다. p
type 값을 가리키는 포인터가 있다고 가정 해보십시오 T
. 이 표현 p + i
은 " 더하기 바이트 에서의 위치"를 의미 하지 않습니다 . 대신, 표현은 실제로 " 플러스 바이트 에서의 위치"를 의미합니다 .p
i
p + i
p
i * sizeof(T)
이것의 편리함은 "다음 값"을 얻기 위해 p + 1
대신에 작성해야한다는 것입니다 p + 1 * sizeof(T)
.
이것은 C 코드 long x = array[5];
가 실제로
long x = *(array + 5)
C 자동으로 곱 때문 5
으로 sizeof(long)
.
따라서이 StackOverflow 질문의 맥락에서 이것이 어떻게 관련이 있습니까? "주소 array + i * sizeof(long)
" 라고 말할 때 " "가 C 표현식으로 해석 된다는 의미 는 아닙니다array + i * sizeof(long)
. 나는 sizeof(long)
대답을보다 명확하게하기 위해 스스로 곱셈을 하고 있지만 그로 인해이 표현은 C로 읽혀서는 안된다는 것을 이해합니다. C 구문을 사용하는 일반적인 수학처럼.
[2] 참고 : 모든 lea
작업은 산술 연산이므로 인수가 실제로 유효한 주소를 참조 할 필요는 없습니다. 이러한 이유로 종종 역 참조되지 않는 값에 대해 순수한 산술을 수행하는 데 사용됩니다. 예를 들어, cc
와 -O2
최적화를 번역
long f(long x) {
return x * 5;
}
다음과 같이 (관련없는 줄이 제거되었습니다) :
f:
leaq (%rdi, %rdi, 4), %rax # set %rax to %rdi + %rdi * 4
ret
&
연산자는 좋은 비유입니다. LEA가 특별한 경우 인 반면 MOV는 메모리 또는 레지스터 피연산자를 사용할 수있는 다른 모든 명령어와 같습니다. 예를 들어 add (%rdi), %eax
MOV와 같은 주소 지정 모드를 사용하여 메모리를 지정합니다. 또한 관련 : 주소 / 포인터가 아닌 값에 LEA 사용? LEA는 주소 계산을 위해 CPU의 하드웨어 지원을 사용하여 임의의 계산을 수행하는 방법입니다.
%rdi
"-이것은 이상하게 표현되었습니다. 레지스터 의 값 을rdi
사용해야 함을 의미합니다 . "at"을 사용하면 메모리 역 참조가없는 것으로 나타납니다.
%rdi
또는"값 " 에서를 %rdi
". "레지스터의 값 %rdi
"은 길지만 훌륭하며 레지스터 대 메모리를 이해하는 데 어려움을 겪고있는 누군가를 도울 수 있습니다.
기본적으로 ... "계산 후 REG로 이동 ... ..."다른 목적으로도 좋은 것 같습니다 :)
값이 포인터라는 것을 잊어 버린 경우 코드 최적화 / 최소화에 사용할 수 있습니다.
MOV EBX , 1
MOV ECX , 2
;//with 1 instruction you got result of 2 registers in 3rd one ...
LEA EAX , [EBX+ECX+5]
EAX = 8
독창성 :
MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
lea
시프트 앤 ADD 명령 인 것을 사용하는 메모리 피연산자 시스템 인코딩 구문 하드웨어 이미 / M + + disp0 SIB / 32분의 8 ModR를 해독하는 방법을 알고 있기 때문에.
다른 답변에 명시된 바와 같이 :
MOV
대괄호 안의 주소 에서 데이터를 가져 와서 해당 데이터 를 대상 피연산자에 배치합니다 .LEA
대괄호 안의 주소 계산 을 수행하고 계산 된 주소 를 대상 피연산자에 배치합니다. 실제로 메모리로 이동하여 데이터를 가져 오지 않고 발생합니다. 이 작업 LEA
은 "유효 주소"를 계산하는 것입니다.메모리는 여러 가지 방법으로 처리 될 수 있기 때문에 (아래 예 참조), LEA
명시 적 ADD
또는 MUL
명령 (또는 동등한) 을 사용하지 않고 레지스터를 함께 추가하거나 곱하기 위해 사용되는 경우가 있습니다 .
모두가 Intel 구문으로 예제를 보여 주므로 AT & T 구문에 대한 예가 있습니다.
MOVL 16(%ebp), %eax /* put long at ebp+16 into eax */
LEAL 16(%ebp), %eax /* add 16 to ebp and store in eax */
MOVQ (%rdx,%rcx,8), %rax /* put qword at rcx*8 + rdx into rax */
LEAQ (%rdx,%rcx,8), %rax /* put value of "rcx*8 + rdx" into rax */
MOVW 5(%bp,%si), %ax /* put word at si + bp + 5 into ax */
LEAW 5(%bp,%si), %ax /* put value of "si + bp + 5" into ax */
MOVQ 16(%rip), %rax /* put qword at rip + 16 into rax */
LEAQ 16(%rip), %rax /* add 16 to instruction pointer and store in rax */
MOVL label(,1), %eax /* put long at label into eax */
LEAL label(,1), %eax /* put the address of the label into eax */
lea label, %eax
절대 [disp32]
주소 지정 모드를 원하지 않습니다 . mov $label, %eax
대신 사용하십시오 . 예, 작동하지만 효율성이 떨어집니다 (더 큰 기계 코드 및 더 적은 실행 단위로 실행). AT & T에 대해 언급 했으므로 주소 / 포인터가 아닌 값에 LEA 사용하기? AT & T를 사용하고 있으며 저의 대답에는 다른 AT & T 예제가 있습니다.
예를 들어 이것을 이해하자.
mov eax, [ebx] 및
lea eax, [ebx] ebx의 값이 0x400000이라고 가정합니다. 그런 다음 mov는 주소 0x400000으로 이동하여 4 바이트의 데이터를 eax 레지스터에 복사하지만 lea는 주소 0x400000을 eax에 복사합니다. 따라서 각 경우에 eax의 각 명령 값을 실행 한 후에는 메모리 0x400000에 30이 있다고 가정합니다.
eax = 30 (mov의 경우) eax = 0x400000 (lea의 경우) mov 정의의 경우 mov는 rm32에서 대상 (mov dest rm32)으로 데이터를 복사하고 lea (load 유효 주소)는 주소를 대상 (mov dest rm32)으로 복사합니다. ).
MOV는 LEA [label]과 동일한 작업을 수행 할 수 있지만 MOV 명령에는 명령 자체 내의 유효 주소가 즉시 상수 (어셈블러에서 미리 계산 됨)로 포함됩니다. LEA는 PC 기준을 사용하여 명령 실행 중 유효 주소를 계산합니다.
lea [label
에서는 무의미한 바이트 낭비와 더 컴팩트 한 방식 mov
이므로 말하고있는 조건을 지정해야합니다. 또한 일부 어셈블러의 [label]
경우 RIP 상대 주소 지정 모드에 적합한 구문이 아닙니다. 그러나 그렇습니다. GNU 어셈블러에서 함수 또는 레이블의 주소를 레지스터에로드하는 방법에 대해 자세히 설명합니다.
그 차이는 미묘하지만 중요합니다. MOV 명령어는 'MOVe'이며 실제로 TABLE-ADDR 레이블이 나타내는 주소의 복사본입니다. LEA 명령어는 간접 명령어 인 '로드 유효 주소'입니다. 즉, TABLE-ADDR은로드 할 주소가있는 메모리 위치를 가리 킵니다.
LEA를 효과적으로 사용하는 것은 강력한 명령어이므로 C와 같은 언어로 포인터를 사용하는 것과 같습니다.