Адрес загрузки MIPS la не всегда использует регистр $ 1?

#mips #cpu-registers

#mips #регистры процессора

Вопрос:

Пожалуйста, обратитесь к части редактирования для моего объяснения.

Это немного длинно и сложно проиллюстрировать. Но я ценю, что вы уделили время чтению этого. Пожалуйста, потерпите меня.

Предположим, у меня есть это:

 .data
    str1: .asciiz "A"
    str2: .asciiz "1"
    myInt:
          .word 42      # allocate an integer word: 42
    myChar:
          .word 'Q'     # allocate a char word

    .text    
    .align 2
    .globl main

main:
    lw      $t0, myInt          # load myInt into register $t0

    lw      $t3, str1           # load str1 into register $t3

    lw      $t4, str2           #load str2 into register $t4

    la      $a0, str1           # load address str1

    la      $a1, str2           # load address str2
  

Тогда в SPIM сегмент пользовательского текста равен

 User Text Segment [00400000]..[00440000]
[00400000] 8fa40000  lw $4, 0($29)            ; 183: lw $a0 0($sp) # argc 
[00400004] 27a50004  addiu $5, $29, 4         ; 184: addiu $a1 $sp 4 # argv 
[00400008] 24a60004  addiu $6, $5, 4          ; 185: addiu $a2 $a1 4 # envp 
[0040000c] 00041080  sll $2, $4, 2            ; 186: sll $v0 $a0 2 
[00400010] 00c23021  addu $6, $6, $2          ; 187: addu $a2 $a2 $v0 
[00400014] 0c100009  jal 0x00400024 [main]    ; 188: jal main 
[00400018] 00000000  nop                      ; 189: nop 
[0040001c] 3402000a  ori $2, $0, 10           ; 191: li $v0 10 
[00400020] 0000000c  syscall                  ; 192: syscall # syscall 10 (exit) 
[00400024] 3c011001  lui $1, 4097             ; 23: lw $t0, myInt # load myInt into register $t0 
[00400028] 8c280004  lw $8, 4($1)             
[0040002c] 3c011001  lui $1, 4097             ; 25: lw $t3, str1 # load str1 into register $t3 
[00400030] 8c2b0000  lw $11, 0($1)            
[00400034] 3c011001  lui $1, 4097             ; 27: lw $t4, str2 #load str2 into register $t4 
[00400038] 8c2c0002  lw $12, 2($1)            
[0040003c] 3c041001  lui $4, 4097 [str1]      ; 29: la $a0, str1 # load address str1 
[00400040] 3c011001  lui $1, 4097 [str2]      ; 31: la $a1, str2 # load address str2 
[00400044] 34250002  ori $5, $1, 2 [str2]   
  

Я понимаю, что lw является псевдокодом, поэтому его нужно разбить на две инструкции. Я понимаю эту часть. Мы используем адрес входа сегмента данных в качестве «базового указателя» и относительно доступа к другим данным (включая первые данные).

Я также заметил, что адреса загрузки str1 и str2 использовали два разных регистра: $ 4 и $ 1. $ 4 — это $ a0. Почему это?

Если я поменяю местами последние две инструкции, в SPIM я увижу

 ...        
[0040003c] 3c011001  lui $1, 4097 [str2]      ; 31: la $a1, str2 # load address str2 
[00400040] 34250002  ori $5, $1, 2 [str2]     
[00400044] 3c041001  lui $4, 4097 [str1]      ; 32: la $a0, str1 # load address str1
  

Итак, почему адрес загрузки такой странный? Почему str2 использовал $ 1???
Как я могу объяснить, чем отличаются lui $ 1, 4097 [str2] и lui $ 4, 4097 [str1]?

PS: Может кто-нибудь также объяснить мне, зачем нам нужна скобка [str2]?

lui, $ 1, 4097, [str2] только вводит адрес ввода сегмента данных в регистр $ 1. То есть 0x10010000 .

Большое вам спасибо!


Редактировать

Я переписал весь скрипт, чтобы упростить ситуацию.

Скрипт:http://pastebin.com/BHh89iqt Текстовый сегмент: http://pastebin.com/t2eDEs1f

Let me remind you that we write in pseudo instructions, rather than true MIPS machine code. That is, «lw», «jal», «addi», etc are all pseudo instructions.

For example, lw (load word) is broken down into two machine instructions (look at the text segement):

 lui $1, 4097             ; 23: lw $t0, myInt # load myInt into register $t0 
lw $8, 4($1)
  

MIPS является 32-разрядным, поэтому мы разбиваем его на две инструкции. Общее количество обращений к 32-разрядному адресу приведет к набору команд 43 бита.. вот почему мы разбиваем на 2 части.
Метка — это адрес памяти, указывающий на то, что мы назначили.

MIPS может считывать инструкции только вида lw $rt, смещение ($ rs). Таким образом, большинство инструкций загрузки следуют этому подходу и используют $at для преобразования псевдоинструкций, которые включают метки, в машинные инструкции MIPS.

Для lw это довольно просто. Для адреса загрузки la это немного сложно. Обратите внимание на последние четыре инструкции по загрузке адреса. MIPS преобразует их в это:

 [0040003c] 3c041001  lui $4, 4097 [str1]      ; 27: la $a0, str1 # load address str2 
[00400040] 3c011001  lui $1, 4097 [str2]      ; 28: la $a0, str2 # load address str1 
[00400044] 34240002  ori $4, $1, 2 [str2]     
[00400048] 3c011001  lui $1, 4097 [str2]      ; 30: la $a0, str2 # load address str2 
[0040004c] 34240002  ori $4, $1, 2 [str2]     
[00400050] 3c041001  lui $4, 4097 [str1]      ; 31: la $a0, str1 # load address str1 
  

$ 4 относится к $ a0. Если вы посмотрите на инструкции, я поменял местами первые две инструкции загрузки, и результатом являются последние две инструкции.
Я специально сделал это, чтобы проиллюстрировать странное поведение: перед заменой lui использует $ 4 для хранения адреса str1, но если я захочу загрузить адрес str2, я буду использовать $ at, а затем применю смещение.

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

И все же это тоже странно, потому что как компилятор узнает, в каком байте прекратить печать строки? (если мы хотим напечатать строку …)

Мое предположение: нулевой символ для завершения печати.

В любом случае. Я думаю, это просто соглашение, которое использует MIPS.


Вторая правка

На самом деле, если вы просто добавите новые данные поверх str1, вы увидите, что мое объяснение правильное.

Новый скрипт:http://pastebin.com/8DuzFrk0

Новый текстовый сегмент:http://pastebin.com/YXbvzc4z

Я добавил myCharB только в начало сегмента данных.

 [0040003c] 3c011001  lui $1, 4097 [str1]      ; 29: la $a0, str1 #
load address str2
[00400040] 34240004  ori $4, $1, 4 [str1]
[00400044] 3c011001  lui $1, 4097 [str2]      ; 30: la $a0, str2 #
load address str1
[00400048] 34240006  ori $4, $1, 6 [str2] 
  

Комментарии:

1. Я полагаю, что у меня есть ответ на этот вопрос. Пожалуйста, ссылается на редактирование выше.

2. Вторая правка подтверждает мое объяснение.

Ответ №1:

Я также заметил, что адреса загрузки str1 и str2 использовали два разных регистра: $ 4 и $ 1. $ 4 равно $ a0. Почему это?

Ну, кого это волнует? xD Это внутренняя реализация SPIM, и можно свободно использовать любой регистр, если он не нарушает MIPS ABI. Я просто предлагаю вам не слишком полагаться на псевдо-инструкции, чтобы убедиться, какие регистры изменились / какие значения они содержат. Также обычно LW не является псевдо-инструкцией, но в том виде, в котором вы ее используете.

Может кто-нибудь также объяснить мне, зачем нам нужна скобка [str2]?

Вам не нужны никакие скобки. Это просто информация SPIM для программиста, чтобы показать, что эта инструкция загружает адрес str2. Это не часть сборки.

lui, $ 1, 4097, [str2] только вводит адрес ввода сегмента данных в регистр $ 1. То есть 0x10010000

Ну, на самом деле он загружает только верхнюю половину слова в размере $ 1. Просто так получилось, что нижняя половина слова — это простые нули. Имейте в виду, что LUI не изменяет нижнюю половину слова, поэтому вы должны убедиться, что он содержит нужное вам значение (сброс регистра или использование LI).

И все же это тоже странно, потому что как компилятор узнает, в каком байте прекратить печать строки? (если мы хотим напечатать строку …)

Завершается нулем, как вы правильно догадались.

Я думаю, это просто соглашение, которое использует MIPS.

Это намного старше, чем MIPS. И MIPS ничего не определяет по этому поводу, как и любая другая архитектура. Это обработка данных, и она определена на верхнем уровне, таком как ОС. В этом случае это соглашение SPIM для своих собственных системных вызовов. В любом случае строки с нулевым завершением довольно распространены. Язык программирования C использует so для строк.

Комментарии:

1. m0skt0 Большое вам спасибо. Это действительно помогает, когда кто-то подходит и говорит несколько слов. Ваше время ценно, и большое вам спасибо. Вы на самом деле исправили мою ошибку: Раньше я думал, что LUI заменяет верхние 16 бит $ r_des на верхние 16 бит imme, а нижние 16 бит — на нули.

2. Всегда пожалуйста. LUI не трогает нижнюю половину слова, но LI обнуляет верхнюю половину слова. Для этого вы должны сначала использовать LI, а затем LUI 🙂 И я знаю, что это звучит глупо, но в следующий раз постарайтесь не переусердствовать с вопросами, потому что это может заставить людей не хотеть тратить столько времени на чтение всего вопроса. Хотя просто предложение.

3. Привет, m0skit0. Большое вам спасибо. Я расширил, потому что ответа не было, и я выяснил большую часть материала, наблюдая. Большое вам спасибо. Я удалил свой предыдущий комментарий. Я был немного расстроен, когда получил отрицательный отзыв о моем собственном редактировании. Спасибо! Мне действительно понравилось делать эту вещь MIPS: D

4. Вы утверждаете, что LUI объединяет новые высокие 16 бит в существующее значение. MIPS работает не так. li $t0, 0x123456 выполняется сборка (с clang -target mips -c foo.s ) в lui $8, 0x12 / ori $8, $8, 0x3456 . (Вы можете проверить с помощью llvm-objdump -d foo.o ). Если бы это слилось с оригиналом $8 , это было бы перенаправлением на любой мусор, который там был. (И имеет ложную зависимость для суперскалярных / вышедших из строя исполнительных процессоров.) Вот почему lui определяется как доступный только для записи в адрес назначения, устанавливая для него значение imm16 << 16 . Если бы вы использовали ori $t0, $zero, low_half / lui $t0, high_half , вы бы обнулили нижнюю половину

5. Смотрите любую ссылку на набор инструкций MIPS, например inst.eecs.berkeley.edu /~cs61c/resources/MIPS_help.html (хотя это не полная ссылка, например, не учитывающая bltz и bltzal среди прочих, это правильно для lui )