Почему MIPS G CC перезагружает $ 28 из 16 ($ fp) после вызова функции (указатель GOT), не сохранив сначала 16 ($ fp)? - PullRequest
1 голос
/ 05 апреля 2020

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

Я пишу простой код для теста.

#include <stdio.h>

int funadd(int a, int b){
  int x = 0;

  x = a + b;

  return x;
}

int fun(int a, int b){
  int y = 17;
  int returnvalue = 0;

  returnvalue = funadd(a, b);
  returnvalue = returnvalue - y;

  return returnvalue;
}

int main(){

  int a = 32;
  int b = 24;

  int c = 0;

  c = fun(a, b);

  printf("%d\n", c);

  return c;

}

После сборки:

    .file   1 "testfuncall.c"
    .section .mdebug.abi32
    .previous
    .nan    legacy
    .module fp=xx
    .module nooddspreg
    .abicalls
    .text
    .align  2
    .globl  funadd
    .set    nomips16
    .set    nomicromips
    .ent    funadd
    .type   funadd, @function
funadd:
    .frame  $fp,24,$31      # vars= 8, regs= 1/0, args= 0, gp= 8
    .mask   0x40000000,-4
    .fmask  0x00000000,0
    .set    noreorder
    .set    nomacro
    addiu   $sp,$sp,-24
    sw  $fp,20($sp)
    move    $fp,$sp
    sw  $4,24($fp)
    sw  $5,28($fp)
    sw  $0,8($fp)
    lw  $3,24($fp)
    lw  $2,28($fp)
    addu    $2,$3,$2
    sw  $2,8($fp)
    lw  $2,8($fp)
    move    $sp,$fp
    lw  $fp,20($sp)
    addiu   $sp,$sp,24
    jr  $31
    nop

    .set    macro
    .set    reorder
    .end    funadd
    .size   funadd, .-funadd
    .align  2
    .globl  fun
    .set    nomips16
    .set    nomicromips
    .ent    fun
    .type   fun, @function
fun:
    .frame  $fp,40,$31      # vars= 8, regs= 2/0, args= 16, gp= 8
    .mask   0xc0000000,-4
    .fmask  0x00000000,0
    .set    noreorder
    .cpload $25
    .set    nomacro
    addiu   $sp,$sp,-40
    sw  $31,36($sp)
    sw  $fp,32($sp)
    move    $fp,$sp
    .cprestore  16
    sw  $4,40($fp)
    sw  $5,44($fp)
    li  $2,17           # 0x11
    sw  $2,24($fp)
    sw  $0,28($fp)
    lw  $5,44($fp)
    lw  $4,40($fp)
    lw  $2,%got(funadd)($28)
    move    $25,$2
    .reloc  1f,R_MIPS_JALR,funadd
1:  jalr    $25
    nop

    lw  $28,16($fp)
    sw  $2,28($fp)
    lw  $3,28($fp)
    lw  $2,24($fp)
    subu    $2,$3,$2
    sw  $2,28($fp)
    lw  $2,28($fp)
    move    $sp,$fp
    lw  $31,36($sp)
    lw  $fp,32($sp)
    addiu   $sp,$sp,40
    jr  $31
    nop

    .set    macro
    .set    reorder
    .end    fun
    .size   fun, .-fun
    .rdata
    .align  2
$LC0:
    .ascii  "%d\012\000"
    .text
    .align  2
    .globl  main
    .set    nomips16
    .set    nomicromips
    .ent    main
    .type   main, @function
main:
    .frame  $fp,48,$31      # vars= 16, regs= 2/0, args= 16, gp= 8
    .mask   0xc0000000,-4
    .fmask  0x00000000,0
    .set    noreorder
    .cpload $25
    .set    nomacro
    addiu   $sp,$sp,-48
    sw  $31,44($sp)
    sw  $fp,40($sp)
    move    $fp,$sp
    .cprestore  16
    li  $2,32           # 0x20
    sw  $2,24($fp)
    li  $2,24           # 0x18
    sw  $2,28($fp)
    sw  $0,32($fp)
    lw  $5,28($fp)
    lw  $4,24($fp)
    lw  $2,%got(fun)($28)
    move    $25,$2
    .reloc  1f,R_MIPS_JALR,fun
1:  jalr    $25
    nop

    lw  $28,16($fp)
    sw  $2,32($fp)
    lw  $5,32($fp)
    lw  $2,%got($LC0)($28)
    addiu   $4,$2,%lo($LC0)
    lw  $2,%call16(printf)($28)
    move    $25,$2
    .reloc  1f,R_MIPS_JALR,printf
1:  jalr    $25
    nop

    lw  $28,16($fp)
    lw  $2,32($fp)
    move    $sp,$fp
    lw  $31,44($sp)
    lw  $fp,40($sp)
    addiu   $sp,$sp,48
    jr  $31
    nop

    .set    macro
    .set    reorder
    .end    main
    .size   main, .-main
    .ident  "GCC: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516"

Я понимаю, что после каждого вызова функции есть инструкция lw $28,16($fp). Но я не вижу ни одного кода, который бы сначала сохранял значение в вызывающем или вызывающем объекте.

Я могу прочитать сборку MIPS. Я знаю, что lw является словом загрузки и как $ fp и $ sp являются указателем кадра и указателем стека.

Я просто не могу понять, как имеет смысл загружать что-либо из 16($fp); кажется, что есть неинициализированное пространство.

Я знаю, $28 - это $gp, и я вижу, что он используется в качестве указателя GOT для загрузки адресов функций перед вызовами, но, похоже, ничего не инициализирует этот регистр до используется в функциях.

Требует ли соглашение о вызовах MIPS, чтобы $28 уже указывал на GOT при вводе функции?

Ответы [ 2 ]

2 голосов
/ 05 апреля 2020
lw $28,16($fp)

lw - это инструкция «загрузить слово» - она ​​загружает слово (4 байта или 32 бита) из памяти в регистр. $28 является регистром назначения (также иногда называемым $gp), а 16($fp) является адресом для загрузки с - 16 байтов в кадр ($fp является регистром указателя кадра, и 16 добавляется к чтобы получить адрес для загрузки).

"Фрейм" обычно используется для хранения локальных переменных функции - когда функция запускается, она выделяет кадр в стеке, вычитая константу из $sp, затем сохраняет значение $fp вызывающего абонента где-то там и копирует $sp в $fp, чтобы он указывал на этот вновь выделенный кадр. Затем он читает и записывает локальные данные в / из фрейма с инструкциями загрузки (l) и сохранения (s).

Если вы скомпилировали с оптимизацией, G CC будет хранить локальные данные в регистрах когда это возможно, вместо того, чтобы тратить огромное количество инструкций, хранящих / перезагружающих их в стек. И получал бы доступ к памяти стека относительно указателя стека, вместо того чтобы тратить инструкцию на установку $fp в качестве традиционного указателя кадра. Неоптимизированный код не похож на то, что человек написал бы вручную, но оптимизированный код иногда делает.

1 голос
/ 06 апреля 2020

Мне кажется, я знаю почему.

Я автоматически проигнорировал .cprestore 16, потому что считаю его бесполезным. Но оказывается, что он генерирует реальные инструкции, а не только метаданные, связанные с регистром $gp.

.cprestore 16 расширится до sw $gp,16(sp). Используется вместе с .cpload $25 и другим необходимым кодом. В частности, вы можете прочитать ссылку https://www.linux-mips.org/wiki/PIC_code

...