programing

ARM C 호출 규칙에 저장할 레지스터는 무엇입니까?

copysource 2022. 8. 27. 23:40
반응형

ARM C 호출 규칙에 저장할 레지스터는 무엇입니까?

내가 마지막으로 코드 암 어셈블러를 한지 꽤 됐는데 세부 사항이 좀 녹슬었다.암에서 C 함수를 호출하면 r0-r3과 lr만 저장하면 되는 거죠?

C 함수가 다른 레지스터를 사용하는 경우 스택에 저장하고 복원하는 역할을 합니까?즉, 컴파일러는 C 함수를 위해 이 작업을 수행하기 위한 코드를 생성합니다.

예를 들어 어셈블러 함수로 r10을 사용하는 경우 스택이나 메모리에 값을 푸시하고 C 콜 후에 pop/restore를 할 필요가 없습니다.

이것은 arm-eabi-gcc 4.3.0용입니다.

컴파일 대상 플랫폼의 ABI에 따라 달라집니다.Linux 에서는 오래된 ARM ABI와 새로운 ARM ABI의 2개가 있습니다.AFAIK, 새로운 것은 ARM의 AAPCS입니다.완전한 EABI 정의는 현재 ARM의 Infocenter에 있습니다.

AAPCS의 § 5.1.1:

  • r0-r3은 인수 및 스크래치 레지스터이며 r0-r1은 결과 레지스터이기도 합니다.
  • r4-r8은 착신측 저장 레지스터입니다.
  • r9는 착신측 저장 레지스터일 수도 있고 아닐 수도 있습니다(AAPCS의 일부 바리안트에서는 특수 레지스터입니다).
  • r10-r11은 착신측 저장 레지스터입니다.
  • r12-r15는 특수 레지스터입니다.

착신측 보존 레지스터는, 착신측(발신측이 레지스터를 보존하는 경우)에 의해서 보존할 필요가 있습니다.따라서, 이것이 사용하고 있는 ABI 의 경우, 다른 함수를 호출하기 전에 r10 을 보존할 필요는 없습니다(다른 함수는 r10 을 보존합니다).

편집: 사용하는 컴파일러에 따라 차이가 없습니다.특히 gcc는 여러 ABI용으로 설정할 수 있으며 명령줄에서 변경할 수도 있습니다.생성되는 프롤로그/에필로그 코드를 살펴보는 것은 그다지 유용하지 않습니다.왜냐하면 각 함수에 맞게 조정되어 컴파일러는 레지스터를 저장하는 다른 방법(예를 들어 함수 중간에 저장하는 것)을 사용할 수 있기 때문입니다.


용어: "callee-save"는 "비휘발성" 또는 "call-preserved"의 동의어입니다.착신측과 발신자가 저장한 레지스터란 무엇입니까?
함수 콜을 발신할 때는, r4-r11 의 값(아마도 r9 를 제외)은, r0-r3(call-clobered/volatile)이 아니고, 그 후에도 존재하는 것을 전제로 할 수 있습니다.

64비트 ARM의 경우, A64(ARM 64비트아키텍처용 프로시저 콜 표준)

A64 명령어 세트에는 31개의 범용(정수) 레지스터가 표시되며, 이러한 레지스터는 r0-r30이라고 라벨이 붙어 있습니다.64비트 컨텍스트에서는 보통 이러한 레지스터는 x0-x30이라는 이름을 사용하여 참조됩니다.32비트 컨텍스트에서는 레지스터는 w0-w30을 사용하여 지정됩니다.또한 스택 포인터 레지스터 SP는 제한된 수의 명령으로 사용할 수 있습니다.

  • 스택 포인터 SP
  • r30 LR 링크 레지스터
  • r29 FP 프레임 포인터
  • r19…r28 콜리가 저장한 레지스터
  • r18 플랫폼 등록부(필요한 경우), 그 이외의 경우 임시 등록부.
  • r17 IP1 두 번째 프로시저 내 콜 임시 레지스터(콜 베니어 및 PLT 코드로 사용 가능).그 외의 경우는, 임시 레지스터로서 사용할 수 있습니다.
  • r16 IP0 첫 번째 프로시저 내 콜 스크래치 레지스터(콜 베니어 및 PLT 코드로 사용 가능).그 외의 경우는, 임시 레지스터로서 사용할 수 있습니다.
  • r9…r15 임시 레지스터
  • r8 간접결과 위치대장
  • r0…r7 파라미터/결과 레지스터

첫 번째 8개의 레지스터 r0-r7은 인수 값을 서브루틴에 전달하고 함수에서 결과 값을 반환하기 위해 사용됩니다.또, 루틴내의 중간치를 보관 유지하기 위해서도 사용할 수 있습니다(단, 일반적으로 서브루틴 콜간의 경우만).

레지스터 r16(IP0)r17(IP1)은 링커에 의해 루틴과 호출되는 서브루틴 사이의 스크래치 레지스터로 사용될 수 있습니다.서브루틴 콜간의 중간치를 유지하기 위해서, 루틴내에서 사용할 수도 있습니다.

register r18의 역할은 플랫폼에 따라 다릅니다.플랫폼 ABI가 프로시저 간 상태(예를 들어 스레드 컨텍스트)를 전송하기 위해 전용 범용 레지스터를 필요로 하는 경우, 그 목적을 위해 이 레지스터를 사용해야 합니다.플랫폼 ABI에 이러한 요건이 없는 경우 추가 임시 레지스터로서 r18을 사용해야 합니다.플랫폼 ABI 사양은 이 레지스터의 사용을 문서화해야 합니다.

SIMD

ARM 64비트아키텍처에는 32개의 레지스터 v0-v31도 있어 SIMD 및 부동소수점 조작으로 사용할 수 있습니다.레지스터의 정확한 이름이 액세스 크기를 나타내도록 변경됩니다.

주의: AArch32와는 달리 AArch64에서는 SIMD 및 부동소수점 레지스터의 128비트 및 64비트 뷰는 좁은 뷰에서 여러 레지스터와 겹치지 않으므로 q1, d1 s1은 모두 레지스터 뱅크 내의 동일한 엔트리를 참조합니다.

첫 번째 8개의 레지스터 v0-v7은 인수 값을 서브루틴에 전달하고 함수에서 결과 값을 반환하는 데 사용됩니다.또, 루틴내의 중간치를 보관 유지하기 위해서도 사용할 수 있습니다(단, 일반적으로 서브루틴 콜간의 경우만).

레지스터 v8-v15는 서브루틴콜을 통해 콜처에 의해 유지되어야 합니다.나머지 레지스터(v0-v7, v16-v31)는 유지할 필요가 없습니다(또는 발신자가 보존할 필요가 있습니다).또한 v8-v15에 저장되어 있는 각 값의 하위 64비트만 보존하면 됩니다.큰 값을 유지하는 것은 발신자의 책임입니다.

CesarB와 Pavel의 답변은 AAPCS의 견적을 제공했지만 아직 해결되지 않은 문제가 남아 있습니다.착신측이 r9를 저장합니까?r12는 어떻습니까?r14는요?또한, 답변은 매우 일반적이며, 요청대로 암 eabi 툴 체인에만 한정되지 않았습니다.다음은 콜리 저장 레지스터와 저장되지 않은 레지스터를 알아내는 실용적인 방법입니다.

다음 C 코드에는 레지스터 r0-r12 및 r14의 변경을 주장하는 인라인어셈블리 블록이 포함되어 있습니다.컴파일러는 ABI에 필요한 레지스터를 저장하는 코드를 생성합니다.

void foo() {
  asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}

명령줄 사용arm-eabi-gcc-4.7 -O2 -S -o - foo.c사용하시는 플랫폼의 스위치를 추가합니다(예:-mcpu=arm7tdmi예를 들어)를 참조해 주세요.명령은 생성된 어셈블리 코드를 STDOUT에 인쇄합니다.다음과 같은 경우가 있습니다.

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    bx  lr

컴파일러가 생성한 코드는 r4-r11을 저장하고 복원합니다.컴파일러는 r0-r3, r12를 저장하지 않습니다.r14(에일리어스 lr)를 복원하는 것은 순전히 우연입니다.그 경험상 출구 코드는 저장된 lr을 r0에 로드한 후 "bx lr" 대신 "bx r0"을 실행할 수도 있습니다.둘 중 하나를 추가함으로써-mcpu=arm7tdmi -mno-thumb-interwork또는 를 사용하여-mcpu=cortex-m4 -mthumb다음과 같은 약간 다른 어셈블리 코드를 얻을 수 있습니다.

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}

다시 r4-r11이 저장 및 복원됩니다.그러나 r14(일명 lr)는 복원되지 않습니다.

요약:

  • r0-r3은 착신측에서 인식되지 않습니다.
  • r4-r11은 착신측으로 인식됩니다.
  • r12(호출 IP)는 착신측에서 인식되지 않습니다.
  • r13(모듈 SP)은 착신측으로 설정되어 있습니다.
  • r14(called lr)는 착신측이 아닙니다.
  • r15(syslog pc)는 프로그램카운터로 함수 호출 전에 lr 값으로 설정됩니다.

이것은 적어도 arm-eabi-gcc의 디폴트값의 경우 유지됩니다.결과에 영향을 줄 수 있는 명령줄 스위치(특히 -mabi 스위치)가 있습니다.

32비트 ARM 콜규칙은 AAPCS에 의해 지정됩니다.

From AAPCS, §5.1.1 Core registers:

  • r0-r3은 인수 및 스크래치 레지스터이며 r0-r1은 결과 레지스터이기도 합니다.
  • r4-r8은 착신측 저장 레지스터입니다.
  • r9는 착신측 저장 레지스터일 수도 있고 아닐 수도 있습니다(AAPCS의 일부 바리안트에서는 특수 레지스터입니다).
  • r10-r11은 착신측 저장 레지스터입니다.
  • r12-r15는 특수 레지스터입니다.

AAPCS에서 VFP 등록 사용 규칙 5.1.2.1:

  • s16–s31(d8–d15, q4–q7)을 보존해야 합니다.
  • s0–s15(d0–d7, q0–q3)d16–d31(q8–q15)은 유지할 필요가 없습니다.

원본 투고:
암투씨컨벤션


64비트 ARM 콜규칙은 AAPCS64에 의해 지정됩니다.

범용 레지스터 section specifies what registers need be preserved.

  • r0-r7은 파라미터/결과 레지스터입니다.
  • r9-r15는 임시 레지스터입니다.
  • r19-r28은 착신측 레지스터입니다.
  • 다른 모든 것(r8, r16-r18, r29, r30, SP)은 특별한 의미를 가지며 일부는 임시 레지스터로 취급될 수 있습니다.

SIMD 부동 소수점 레지스터는 Neon 및 부동 소수점 레지스터를 지정합니다.

적어도 Cortex M3 아키텍처에서는 함수 호출과 인터럽트에 대한 차이가 있습니다.

인터럽트가 발생하면 R0-R3,R12,LR,PC가 스택에 자동으로 푸시되고 IRQ 자동 POP이 반환됩니다.IRQ 루틴에서 다른 레지스터를 사용하는 경우 수동으로 해당 레지스터를 스택에 푸시/팝해야 합니다.

이 자동 PUSH 및 POP은 함수 호출(점프 명령)용으로 만들어지지 않았다고 생각합니다.관례상 R0-R3은 인수, 결과 또는 스크래치 레지스터로만 사용할 수 있으므로 함수 반환 후 나중에 사용할 값이 없으므로 함수 호출 전에 저장할 필요가 없습니다.단, 인터럽트와 마찬가지로 다른 모든 CPU 레지스터를 기능에서 사용할 경우 이를 저장해야 합니다.

언급URL : https://stackoverflow.com/questions/261419/what-registers-to-save-in-the-arm-c-calling-convention

반응형