ЛДР псевдоинструкция - PullRequest
       20

ЛДР псевдоинструкция

0 голосов
/ 29 января 2020

когда я создаю код сборки ARM из C кода с помощью g cc -S, я получаю вариант инструкции LDR, которого я не знаю. В частности, я получаю инструкцию "ldr r3, .L5", где ".L5" - метка, определенная компилятором. Мне не понятно, почему я не получаю псевдоинструкцию "ldr r3, = .L5", которая должна быть единственным способом загрузки произвольного числа в регистр.

Подробнее:

  1. Я начинаю с этого C кода (имя файла: sum_squares_ C. c):
int sum;

int main(){
    sum = 0;
    for(int i=1; i<=n; i++){
            sum = sum + i*i;
    }
}

Затем на Raspeberry PI я компилирую с помощью «g cc -O0 -S sum_squares_ C. c» с версией компилятора g cc (Raspbian 8.3.0-6 + rpi1 ) 8.3.0.

Выводится этот код ARM (инструкция «ldr r3, .L5» находится в 7-й строке после метки «main»):

    .arch armv6
    .eabi_attribute 28, 1
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 2
    .eabi_attribute 30, 6
    .eabi_attribute 34, 1
    .eabi_attribute 18, 4
    .file   "sum_squares_C.c"
    .text
    .global n
    .data
    .align  2
    .type   n, %object
    .size   n, 4
n:
    .word   1
    .comm   sum,4,4
    .text
    .align  2
    .global main
    .arch armv6
    .syntax unified
    .arm
    .fpu vfp
    .type   main, %function
main:
    @ args = 0, pretend = 0, frame = 8
    @ frame_needed = 1, uses_anonymous_args = 0
    @ link register save eliminated.
    str fp, [sp, #-4]!
    add fp, sp, #0
    sub sp, sp, #12
    ldr r3, .L5
    mov r2, #0
    str r2, [r3]
    mov r3, #1
    str r3, [fp, #-8]
    b   .L2
.L3:
    ldr r3, [fp, #-8]
    ldr r2, [fp, #-8]
    mul r2, r2, r3
    ldr r3, .L5
    ldr r3, [r3]
    add r3, r2, r3
    ldr r2, .L5
    str r3, [r2]
    ldr r3, [fp, #-8]
    add r3, r3, #1
    str r3, [fp, #-8]
.L2:
    ldr r3, .L5+4
    ldr r3, [r3]
    ldr r2, [fp, #-8]
    cmp r2, r3
    ble .L3
    mov r3, #0
    mov r0, r3
    add sp, fp, #0
    @ sp needed
    ldr fp, [sp], #4
    bx  lr
.L6:
    .align  2
.L5:
    .word   sum
    .word   n
    .size   main, .-main
    .ident  "GCC: (Raspbian 8.3.0-6+rpi1) 8.3.0"
    .section    .note.GNU-stack,"",%progbits

Мне кажется, что g cc использует инструкцию "ldr r3, .L5" как эквивалентную "ldr r3, = .L5". Это правильно? Где я могу найти определение синтаксиса этой инструкции? Можно ли заставить g cc не использовать эту инструкцию, а использовать "ldr r3, = .L5" (мне это нужно из соображений обучения)?

Спасибо! Francesco

Ответы [ 2 ]

1 голос
/ 29 января 2020

, чтобы сохранить набор текста и принять на себя риск, который человек может использовать

ldr r3,=sum
ldr r3,[r3]

, как указано в другом примере, ассемблер создаст в машинном коде эквивалент того, что человек мог набрать без адреса = трюк

ldr r3,address_of_sum (without the =)
ldr r3,[r3]
...
address_of_sum: .word sum

и этот первый ldr (не псевдо, поскольку он переводит непосредственно в известную инструкцию, один к одному) - это относительная нагрузка ap c (при условии, что она может достигать).

Оба эти параметра указаны спецификацией ассемблера c, поскольку язык ассемблера определяется ассемблером, а не целью.

Ярлык = addresss не поддерживается всеми ассемблерами arm и должен использоваться с осторожностью при определенных значениях оно не превращается в слово в пуле с относительной нагрузкой ap c.

для вопросов, подобных этому, сначала изучите разборку, в большинстве случаев ответьте на ваш вопрос, еще лучше сначала изучите разборку под вопросом сборка. сборка, сгенерированная компилятором, не так проста для чтения и отслеживания, как разборка, особенно если она связана. Также легче учиться на оптимизированном коде, чем на неоптимизированном, так как большая часть кода - это переменная стека (или в данном случае глобальная).

ldr r3,=0x1000
ldr r3,=0x1234
b .

00000000 <.text>:
   0:   e3a03a01    mov r3, #4096   ; 0x1000
   4:   e51f3000    ldr r3, [pc, #-0]   ; c <.text+0xc>
   8:   eafffffe    b   8 <.text+0x8>
   c:   00001234    andeq   r1, r0, r4, lsr r2

в одном случае, когда он может генерировать mov, где он не может, то он выделяет из пула и помещает туда значение, тогда делает относительную нагрузку ap c. Теперь да, когда вы читаете вывод таким образом, вам нужно видеть / понимать / игнорировать разборку andeq, эту строку мы смотрим на значение 0x00001234 и видим сгенерированную инструкцию.

Вы не всегда должны предполагать, что трюк = address работать, если вы решите попробовать различные инструменты, теперь он работает для gnu, если он может найти пул, если он не может, тогда вам нужно либо просто набрать текст самостоятельно, либо добавить .pool или любой другой псевдокод, который делает то же самое для помогите ассемблеру найти место для этого значения по мере необходимости.

Я бы ожидал, что ассемблер всегда будет помещать это (= адрес) в пул для внешней ссылки, но технически возможно, чтобы цепочка инструментов поместила заполнитель там и пусть компоновщик заполнит его либо mov, либо добавит соседний элемент и поместит туда значение, как binutils, с bl для внешней ссылки.

gas:

ldr r3,=sum
b .

00000000 <.text>:
   0:   e51f3000    ldr r3, [pc, #-0]   ; 8 <.text+0x8>
   4:   eafffffe    b   4 <.text+0x4>
   8:   00000000    andeq   r0, r0, r0

компоновщик заполнит адрес позже, как при выводе вашего компилятора. теперь разборка -0 очень интересная, почти забавная.

1 голос
/ 29 января 2020

ldr r3, .L5 загружает слово с адреса .L5 в r3. На этикетке .L5 указан адрес переменной sum. Таким образом, адрес sum загружается в r3.

ldr r3, =.L5, адрес .L5 загружается в r3. Затем программе потребуется разыменовать ее снова, чтобы получить адрес sum. Нет причин делать это.

Когда вы используете ldr r3, =.L5, ассемблер сохраняет где-то адрес .L5, а затем загружается с этого адреса. Итак, это:

    ldr r3, =.L5
    ...
.L5:
    .word sum

- это то же самое, что и это:

    ldr r3, .address_of_L5
    ...
.L5:
    .word sum
    ...
.address_of_L5:
    .word .L5

Как видите, компилятор уже сделал это для sum. Вместо того, чтобы писать эту сборку:

    ldr r3, =sum

, компилятор написал:

    ldr r3, .L5
    ...
.L5:
    .word sum

, что именно то, что ассемблер сделал бы в любом случае. Я не знаю, почему компилятор хочет сделать это вместо ассемблера.

Мне не понятно, почему я не получаю псевдоинструкцию "ldr r3, = .L5", которая должен быть единственным способом загрузки произвольного числа в регистр.

Обратите внимание, что это не единственный способ загрузить произвольное число в регистр. Это даже не реальный способ загрузить произвольное число в регистр. Это псевдоинструкция (как вы знаете): это не то, что на самом деле может делать процессор, это то, что ассемблер может «скомпилировать» для вашего удобства.

...