MOV와 LEA의 차이점은 무엇입니까?


138

이 지침의 차이점이 무엇인지 알고 싶습니다.

MOV AX, [TABLE-ADDR]

LEA AX, [TABLE-ADDR]


8
고마워 닉. 우선, 나는 그 링크를 조사 하여이 질문에 대한 답을 찾지 못했습니다. 여기에서 특정 정보를 찾고 있었으므로 제공 한 링크의 토론은 본질적으로 더 일반적입니다.
naveen

3
나는 오래 전에 @Nick의 dup을 찬성했지만 지금은 vtc'd입니다. 성찰 해 볼 때 나는 너무 성급해서 이제 나빈과 함께 a) 다른 질문은 "차이점"에 답하지 않고 b) 이것은 유용한 질문이다. 내 실수로 naveen에게 사과-VTC를 취소 할 수만 있다면 ...
Ruben Bartelink


관련 : 주소 / 포인터가 아닌 값에 LEA 사용? 임의의 수학을위한 LEA의 다른 용도에 대해 이야기합니다.
Peter Cordes

답변:


168
  • 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. 여러 기본 주소 등으로 여러 부분으로 구성된 계산이있는 경우 유용합니다.


6
명확한 설명에 대해 +1 감사하며 다른 질문에 답변 하는 데 도움이되었습니다 .
legends2k

lea는 이름에 "로드"가 있고 메모리 위치를 계산하는 모든 입력이 즉각적인 값이거나 레지스터이기 때문에 계산 된 주소를 레지스터에 "로드"한다고 혼동합니다. AFAICT lea는 계산 만 수행합니다. 로딩은 메모리에 닿는 것을 의미합니까?
Joseph Garvin

2
@josephGarvin IIRC fetch라는 용어는 그 측면에 적용됩니다. 로드는 레지스터의 값을 처음부터 무언가로 바꾸는 방법입니다. 예 LAHF: FLAGS를 AH 레지스터에로드 합니다. CLR의 CIL에서 (높은 수준의 스택 기반 추상 기계 인 경우, 로드 라는 용어 는 명목 스택에 값을 넣는 것을 의미하며 일반적으로 l...이고, s등가는 반대입니다). 이 노트 : cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html )는 실제로 구별이 적용되는 아키텍처가 있음을 제안합니다.
Ruben Bartelink


45

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를 얻는 데 사용하십시오 .


3
NASM 구문에서만. MASM 구문에서는 mov eax, var로드와 동일 하며 레이블을 즉시 상수로 mov eax, [var]사용 mov eax, OFFSET var하려면 사용해야합니다.
Peter Cordes

1
명확하고 간단하며 확인하려는 내용을 보여줍니다. 감사.
JayArby

1
이러한 모든 예에서 leaRIP 상대 주소 지정을위한 64 비트 모드를 제외하고는 더 나쁜 선택입니다. mov r32, imm32더 많은 포트에서 실행됩니다. lea eax, [edx*4]그렇지 않으면 하나의 명령으로 수행 할 수없는 복사 및 시프트입니다.하지만 동일한 레지스터에서 LEA는을 [eax*4]요구 하기 때문에 더 많은 바이트를 사용 합니다 disp32=0. (그러나 시프트와 다른 포트에서 실행됩니다.) agner.org/optimizestackoverflow.com/tags/x86/info를 참조하십시오 .
Peter Cordes

29

명령 MOV reg, addr은 주소 addr에 저장된 변수를 register reg로 읽는 것을 의미합니다. LEA reg, addr 명령어는 주소에 저장된 변수가 아닌 주소를 레지스터 reg로 읽는 것을 의미합니다.

MOV 명령어의 다른 형태는 MOV reg, immdata입니다. 즉, 즉시 데이터 (즉, 상수) immdata를 레지스터 reg로 읽습니다. LEA reg의 addr, addr가 상수 (즉, 고정 오프셋) 인 경우 LEA 명령은 본질적으로 즉각적인 데이터와 동일한 상수를로드하는 동등한 MOV reg, immdata 명령과 정확히 동일합니다.


11

리터럴 만 지정하면 차이가 없습니다. LEA는 더 많은 능력을 가지고 있으며 여기에서 읽을 수 있습니다.

http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136


GNU 어셈블러에서 .bss 세그먼트의 레이블에 관해서는 사실이 아니라는 것을 제외하고는 추측합니다. AFAIR 당신은 정말로 leal TextLabel, LabelFromBssSegment당신이 smth를 얻을 수 없습니다 . 처럼 .bss .lcomm LabelFromBssSegment, 4, movl $TextLabel, LabelFromBssSegment그렇지 않습니까?
JSmyth

@ JSmyth : lea레지스터 대상이 필요 하기 때문에 소스와 메모리 대상을 mov가질 수 있기 때문 imm32입니다. 물론이 제한은 GNU 어셈블러에만 국한되지는 않습니다.
Peter Cordes

1
또한이 대답은 기본적으로 잘못된 것 MOV AX, [TABLE-ADDR]입니다. 따라서 큰 차이가 있습니다. 동등한 명령은mov ax, OFFSET table_addr
Peter Cordes

10

사용 된 어셈블러에 따라 다릅니다.

mov ax,table_addr

MASM에서는

mov ax,word ptr[table_addr]

따라서 table_addr오프셋이 아닌 에서 첫 번째 바이트를로드합니다 table_addr. 대신 사용해야합니다

mov ax,offset table_addr

또는

lea ax,table_addr

같은 방식으로 작동합니다.

leatable_addr로컬 변수 인 경우 버전도 잘 작동합니다.

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

고마워요. 단지 하나 이상의 답변으로 표시 할 수 없다는 것입니다. (
naveen

5
x86 명령어 MOV와 LEA의 차이점은 가장 확실하게 어셈블러에 의존하지 않습니다.
IJ 케네디

4

이전 답변 중 어느 것도 내 혼란의 바닥에 도달하지 못했기 때문에 내 자신을 추가하고 싶습니다.

내가 누락 된 것은 lea작업이 괄호 사용을 처리하는 방식과 다르게 처리한다는 것입니다 mov.

C를 생각해보십시오 . long내가 호출 하는 배열이 있다고 가정 해 봅시다 array. 이제 식은 array[i]주소 array + i * sizeof(long)[1]의 메모리에서 값을로드하여 역 참조를 수행합니다 .

반면에, 표현을 고려하십시오 &array[i]. 여기에는 여전히 하위 표현식이 포함되어 array[i]있지만 역 참조는 수행되지 않습니다! 의 의미 array[i]가 변경되었습니다. 더 이상 지연을 수행하는 것이 아니라 대신 일종의 사양 으로 작동하여 &찾고있는 메모리 주소를 알려줍니다 . 원한다면 &, 역 참조를 "취소" 한다고 생각할 수도 있습니다.

두 가지 유스 케이스는 여러면에서 유사하기 때문에 구문을 공유 array[i]하지만 &구문의 해석 방법 에 대한 변경 유무 . 가 없으면 &역 참조이며 실제로 배열에서 읽습니다. 로는 &그렇지 않습니다. 값 array + i * sizeof(long)은 여전히 ​​계산되지만 역 참조되지는 않습니다.

상황은 매우 유사하다 movlea. 을 사용하면 mov에는 발생하지 않는 역 참조가 발생합니다 lea. 이것은 두 가지 모두에서 발생하는 괄호 사용에도 불구하고 있습니다. 예를 들어, movq (%r8), %r9leaq (%r8), %r9. 로 mov괄호는 "역 참조"를 의미합니다. 와 함께 lea하지 않습니다. 이 array[i]없는 경우 "역 참조"만 의미 하는 것과 비슷합니다 &.

예를 들면 다음과 같습니다.

코드를 고려하십시오

movq (%rdi, %rsi, 8), %rbp

이것은 메모리 위치의 값을 %rdi + %rsi * 8레지스터로 로드합니다 %rbp. 즉, 레지스터 %rdi의 값과 레지스터의 값을 가져옵니다 %rsi. 후자에 8을 곱한 다음 전자에 더하십시오. 이 위치에서 값을 찾아 레지스터에 배치하십시오 %rbp.

이 코드는 C 선에 대응하고 x = array[i];, array해진다 %rdii하게 %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].

주 내 설명 사이의 유일한 차이 있음 leaqmovq즉이 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 포인터 추가에 트릭이 있다는 것입니다. ptype 값을 가리키는 포인터가 있다고 가정 해보십시오 T. 이 표현 p + i은 " 더하기 바이트 에서의 위치"를 의미 하지 않습니다 . 대신, 표현은 실제로 " 플러스 바이트 에서의 위치"를 의미합니다 .pip + i pi * 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

1
예, 다른 설명보다 더 자세하게 설명하면 C의 &연산자는 좋은 비유입니다. LEA가 특별한 경우 인 반면 MOV는 메모리 또는 레지스터 피연산자를 사용할 수있는 다른 모든 명령어와 같습니다. 예를 들어 add (%rdi), %eaxMOV와 같은 주소 지정 모드를 사용하여 메모리를 지정합니다. 또한 관련 : 주소 / 포인터가 아닌 값에 LEA 사용? LEA는 주소 계산을 위해 CPU의 하드웨어 지원을 사용하여 임의의 계산을 수행하는 방법입니다.
피터 코데

"에서 가치를 얻는다 %rdi"-이것은 이상하게 표현되었습니다. 레지스터 의 값 rdi 사용해야 함을 의미합니다 . "at"을 사용하면 메모리 역 참조가없는 것으로 나타납니다.
ecm

@PeterCordes 감사합니다! 나는 그것이 특별한 경우라는 점을 대답에 덧붙였다.
Quelklef

1
@ecm 좋은 지적; 나는 그것을 알아 차리지 못했다. 나는 지금 그것을 변경했습니다, 감사합니다! :)
Quelklef

참고로, 수정 문제 ECM 지적 것을 짧은 말씨이 포함됩니다 : "값 %rdi 또는"값 " 에서를 %rdi ". "레지스터의 값 %rdi"은 길지만 훌륭하며 레지스터 대 메모리를 이해하는 데 어려움을 겪고있는 누군가를 도울 수 있습니다.
Peter Cordes

2

기본적으로 ... "계산 후 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를 해독하는 방법을 알고 있기 때문에.
Peter Cordes

1

다른 답변에 명시된 바와 같이 :

  • 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 예제가 있습니다.
Peter Cordes

1

예를 들어 이것을 이해하자.

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)으로 복사합니다. ).


0

LEA (Load Effective Address)는 시프트 및 추가 명령어입니다. 하드웨어가 어드레싱 모드를 디코딩하고 계산할 수 있기 때문에 8086에 추가되었습니다.


0

MOV는 LEA [label]과 동일한 작업을 수행 할 수 있지만 MOV 명령에는 명령 자체 내의 유효 주소가 즉시 상수 (어셈블러에서 미리 계산 됨)로 포함됩니다. LEA는 PC 기준을 사용하여 명령 실행 중 유효 주소를 계산합니다.


64 비트 모드 (PC 기준 주소 지정이 새로운 경우)에만 적용됩니다. 다른 모드 lea [label에서는 무의미한 바이트 낭비와 더 컴팩트 한 방식 mov이므로 말하고있는 조건을 지정해야합니다. 또한 일부 어셈블러의 [label]경우 RIP 상대 주소 지정 모드에 적합한 구문이 아닙니다. 그러나 그렇습니다. GNU 어셈블러에서 함수 또는 레이블의 주소를 레지스터에로드하는 방법에 대해 자세히 설명합니다.
Peter Cordes

-1

그 차이는 미묘하지만 중요합니다. MOV 명령어는 'MOVe'이며 실제로 TABLE-ADDR 레이블이 나타내는 주소의 복사본입니다. LEA 명령어는 간접 명령어 인 '로드 유효 주소'입니다. 즉, TABLE-ADDR은로드 할 주소가있는 메모리 위치를 가리 킵니다.

LEA를 효과적으로 사용하는 것은 강력한 명령어이므로 C와 같은 언어로 포인터를 사용하는 것과 같습니다.


7
나는이 답변이 혼란 스럽다고 생각합니다. "LEA 명령어는 간접 명령어 인 '로드 유효 주소'입니다. 즉, TABLE-ADDR은로드 할 주소가있는 메모리 위치를 가리 킵니다." 실제로 LEA는 주소 내용이 아닌 주소를로드합니다. 나는 실제로 어떤 상황에서는 MOV와 LEA가 겹칠 수 있고 똑같은 일을 할 수 있다는 확신을 심어줄 필요가 있다고 생각한다
Bill Forster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.