AND 명령어 (Bitwise AND), ANDS
Summary
정의: 두 오퍼랜드의 비트 단위 논리곱(AND) 연산을 수행하여 결과를 레지스터에 저장하는 명령어. 조건 플래그 갱신 여부에 따라 AND와 ANDS 두 가지 변형이 존재함.
기본 문법
AND Xd, Xn, Xm // 레지스터 간 AND
AND Xd, Xn, #imm // 즉치값(비트마스크)과 AND
AND Xd, Xn, Xm, {shift #amount} // shift 적용 후 AND
Xd: 결과 저장 레지스터Xn: 첫 번째 오퍼랜드Xm또는#imm: 두 번째 오퍼랜드
32비트 버전: W 레지스터 사용 시 동일한 문법으로 32비트 연산 수행 (AND Wd, Wn, Wm)
AND vs ANDS 차이
| 니모닉 | 플래그 갱신 | 용도 |
|---|---|---|
AND |
갱신 안 함 | 순수 비트 연산, 값 저장만 필요할 때 |
ANDS |
NZCV 플래그 갱신 | 연산 결과로 조건 분기를 하고 싶을 때 |
ANDS 플래그 규칙:
- N: 결과의 최상위 비트 (부호 비트)
- Z: 결과가 0이면 1
- C: 항상 0으로 클리어
- V: 항상 0으로 클리어
참고: TST 명령어는 사실 ANDS의 별칭(alias)으로, 결과를 저장할 필요 없이 플래그만 확인할 때 ANDS XZR, Xn, Xm 형태로 내부 처리됨.
즉치값(Immediate) 사용 시 제약 사항 — 자주 안 보이는 이유
핵심: AND 명령어의 즉치값(#imm)은 일반 정수가 아니라 “bitmask immediate”라는 특수한 인코딩 규칙을 따름.
제약 조건:
- 즉치값은 반복되는 비트 패턴(rotated run of 1s)이어야 함
- 예:
0xFF,0xFFFF,0x0F0F0F0F같은 패턴은 가능 - 예:
0x12345678같은 임의 값은 인코딩 불가능
이유: ARM64 명령어는 고정 32비트 길이이므로, 64비트 즉치값을 명령어 안에 직접 담을 공간이 없음. 그래서 “규칙적인 비트 패턴”만 압축 인코딩하여 담을 수 있도록 설계됨.
결과적 영향: 컴파일러는 임의의 마스크 값을 AND하려 할 때 즉치값 인코딩이 불가능하면, 별도로 MOV(또는 MOVZ/MOVK)로 레지스터에 값을 로드한 뒤 레지스터-레지스터 AND를 사용함. 이 때문에 실제 어셈블리에서 “AND + 즉치값” 조합보다 “MOV + AND” 조합이 더 자주 관찰됨 → 질문하신 “자주 볼 것 같은데 자주 못 봄” 현상의 핵심 원인.
예제 코드
기본 AND 연산:
.global _start
.align 2
.text
_start:
mov x0, #0xFF // x0 = 0x000000FF
mov x1, #0x0F // x1 = 0x0000000F
and x2, x0, x1 // x2 = 0xFF & 0x0F = 0x0F
mov x0, #0
mov x16, #1
svc #0x80
ANDS를 이용한 조건 분기 (비트 검사):
.text
_start:
mov x0, #0b1010 // x0 = 10 (이진수)
ands xzr, x0, #0b0001 // 최하위 비트 검사, 결과는 버림(xzr)
b.eq even_number // 결과가 0이면(Z=1) 짝수
// 홀수 처리
mov x1, #1
b end
even_number:
mov x1, #0
end:
mov x0, #0
mov x16, #1
svc #0x80
Shift 적용 예시:
and x2, x0, x1, lsl #4 // x1을 4비트 왼쪽 시프트 후 x0와 AND
실전 활용 패턴
| 용도 | 예시 |
|---|---|
| 특정 비트 마스킹 | and x0, x0, #0xFF (하위 8비트만 추출) |
| 정렬 확인 (Alignment) | ands xzr, x0, #0xF → 16바이트 정렬 여부 검사 |
| 플래그 비트 검사 | tst x0, #(1<<3) (특정 비트 set 여부 확인) |
| 짝수/홀수 판별 | tst x0, #1 |
MOV+AND 조합 실제 예시 (컴파일러 생성 패턴)
임의 마스크 값 사용 시 컴파일러가 생성하는 전형적 패턴:
mov x1, #0x5678
movk x1, #0x1234, lsl #16 // 임의의 32비트 마스크 값 구성
and x0, x0, x1 // 레지스터 간 AND 수행
결론: AND 자체는 기본적인 논리 연산이라 개념적으로는 친숙하지만, 즉치값 인코딩 제약으로 인해 실제 바이너리 분석 시 “AND 단독 명령”보다 “MOV/MOVK + AND” 조합, 또는 플래그 검사 목적의 TST(ANDS의 별칭) 형태로 더 자주 관찰되는 명령어임.