call/ret, push/pop и прочие.

11-Feb-04 13:50 Alexey Boyko wrote to George Shepelev:

GS>> Это я понял. Hо "развёрнутая" запись подразумевает, что можно GS>> попробовать что-то вроде GS>> mov pc,r1

да хоть add pc, r1, r0, lsl #2 что означает PC = R1 + (R0 << 2)

GS>> Т.е. "настоящий" ret, вида GS>> pop pc GS>> не реализован?

А почему ты только его считаешь "настоящим"? Других не видел? Скажем, на той же PDP11 команда ret была всего лишь синонимом RTS PC а команда call dst - синонимом JSR PC,dst Т.е. "НАСТОЯЩИЙ" ret у PDP11 был таким себе частным случаем более общей команды возврата из подпрограммы.

PDP11: действия JSR reg, addr (Jump to SubRoutine) temp <- addr // причём addr может иметь разные методы адресации push reg reg <- PC pc <- temp

действия RTS reg PC <- reg pop reg

Итого JSR PC, addr заталкивал в стек PC и заносил в PC адрес подпрограммы а RTS PC восстанавливал PC из стека, это дело имело синонимы call и ret, но это был не единственный вариант. Никто не мешал сделать такое

jsr R5, subr ; r5 сохраняем в стеке, а в PC заносим... .word 10 ; Ну не совсем-то адрес возврата. Скорее .word var1 ; просто адрес следующей ячейки памяти :-) .word var2 mov #3, R0 ; это уже пошли следующие инструкции

subr: mov (R5)+,R0 mov (R5)+,R1 mov (R5)+,R2 ; R5 указывает на 'mov #3, R0' выше $1: mov (R1)+,(R2)+ sob R0,$1 rts r5 ; тем самым в PC заносится откорректированное автоинкрементами ; значение, а r5 восстанавливется из стека

И форма JSR с регистром, отличающимся от PC, использовалась довольно широко - при программировании на ассемблере а также внутри кода, сгенерированного разными компиляторами. В частности, используемый мной C-компилятор при помощи jsr R5,... обращался к своим внутренним, "служебным", подпрограммам, а у фортрана всё соглашение о вызовах стояло на jsr R5

Однако недостатки есть. Во-первых, нет особого счастья в том, что можно использовать _любой_ регистр. Во-вторых, при вызове "листовой" подпрограммы, да ещё и при наличии свободного регистра (в котором можно было бы сохранить PC) мы всё равно имеем обращение к стеку, на котором зря теряем время.

Но то был классический CISC, а ARM всё-таки RISC. Вот и тут одна CISC-овая команда JSR распалась на две, причём по дороге одна половинка слегка сократилась в возможностях, а другая - расширилась (точнее, получила возможность выполняться разными способами). bl выполняет первую часть, сохраняет PC aka R15 в специально выделенном для этой цели регистре LR (link register) aka R14 если функция была листовая, то возврат можно будет выполнить примитивным mov pc, lr

Кстати, при большом желании можно после вызова командой bl иметь команду перехода на обработчик ошибок, по ходу подпрограммы при помощи команд movne pc, lr moveq pc, lr и так далее делать возврат по ошибке а в конце командой add pc, lr, #4 сделать "номральный" возврат.

И только в случае необходимости вызова других подпрограмм из данной подпрограммы нужно сохранить lr где-то, причём "восстанавливать" его может и не понадобится. При сохранении его в регистре возврат можно сделать при помощи mov pc,еще_регистр, а при сохранении в стеке -- "восстановить" прямо в pc, Алексей Бойко показал как это делается командами ldm/stm Ничего лишнего при этом не делается, всё неплохо продумано. Довольно удобно и то, что команда ldmea _одна_ может восстанавливить все необходимые регистры и осуществить возврат из подпрограммы. А в силу того, что все команды условные, то можно "дёшево" натолкать таких возвратов по коду, не надо делать условный переход куда-то на восстановление/возврат, можно сделать всё на месте.

А что касается "забывчивости", то для профессионального писателя на ассемблере, который помнит всё дерево вызовов проекта и в голове спокойно строит компилированный стек -- то, что при модификации листовой подпрограммы в нелистовую надо позаботиться о сохранении/восстановлении lr это так, детские игрушки. С компилятор об этом помнит всегда.

wbr,

Reply to
Oleksandr Redchuk
Loading thread data ...

Hello, Oleksandr!

Чет Фев 12 2004, Oleksandr Redchuk писал к All по поводу "call/ret, push/pop и прочие.." OR> А почему ты только его считаешь "настоящим"? Других не видел? Hафиг нужен настоящий RET если нет настоящего CALL-а? ;) OR> действия JSR reg, addr (Jump to SubRoutine) OR> temp <- addr // причём addr может иметь разные методы адресации OR> push reg OR> reg <- PC OR> pc <- temp

вместо этого: LR <- PC PC <- temp Стек вообще не трогается, поэтому чтоб пользовать настоящий RET надо на входе сделать PUSH LR.

OR> wbr, WBR! Maxim Polyanskiy.

Reply to
Maxim Polyanskiy
12-Feb-04 04:55 Maxim Polyanskiy wrote to Oleksandr Redchuk:

OR>> А почему ты только его считаешь "настоящим"? Других не видел? MP> Hафиг нужен настоящий RET если нет настоящего CALL-а? ;) У PDP11 тоже нет call-а _только_ в смысле mcs51 или там avr. Её частный случай совпадает и этот частный случай назван "настоящим" :-) PDP11, imho, по праву считается одной из хорошо сбалансированных архитектур. И там _было_ приблизительно то, что есть у arm-а. Просто у арма это рассыпалось на более мелкие команды, ну так не только это. У арма (и не только) и add reg, mem нет, ну так что? А у 51-го и x86 нет add mem,mem, я их сейчас тоже уродами обзову (и не только за это :-)

OR>> действия JSR reg, addr (Jump to SubRoutine) OR>> temp <- addr // причём addr может иметь разные методы адресации OR>> push reg OR>> reg <- PC OR>> pc <- temp

MP> вместо этого: MP> LR <- PC MP> PC <- temp MP> Стек вообще не трогается, Ну да. А я что, об этом не написал? Или картинку тоже надо было? Если считать, что большинство вызовов в программе приходится на вызов листовых функций, то это благо.

MP> поэтому чтоб пользовать настоящий RET надо на MP> входе сделать PUSH LR.

Ну так я же сказал - одна cisc команда распалась на две risc. Всё в соответствиии с общими risc-подходами. А дальше комбинируй по желанию.

====

Reply to
Oleksandr Redchuk

Oleksandr, ты ещё здесь сидишь?

Четверг Февраль 12 2004 00:12, Oleksandr Redchuk wrote to All:

GS>>> Это я понял. Hо "развёрнутая" запись подразумевает, что можно GS>>> попробовать что-то вроде GS>>> mov pc,r1 OR> да хоть add pc, r1, r0, lsl #2 OR> что означает PC = R1 + (R0 << 2)

Красиво. Хакерам должно понравиться ;)

GS>>> Т.е. "настоящий" ret, вида GS>>> pop pc GS>>> не реализован? OR> А почему ты только его считаешь "настоящим"? Других не видел?

А для чего я кавычки поставил? ;)

OR> Скажем, на той же PDP11 команда ret была всего лишь синонимом RTS PC OR> а команда call dst - синонимом JSR PC,dst OR> Т.е. "HАСТОЯЩИЙ" ret у PDP11 был таким себе частным случаем OR> более общей команды возврата из подпрограммы.

Угу.

OR> PDP11: OR> действия JSR reg, addr (Jump to SubRoutine) OR> temp <- addr // причём addr может иметь разные методы адресации OR> push reg OR> reg <- PC OR> pc <- temp

OR> действия RTS reg OR> PC <- reg OR> pop reg

Стек реализован с "последним" элементом на специальном регистре.

OR> Итого JSR PC, addr заталкивал в стек PC и заносил в PC адрес OR> подпрограммы а RTS PC восстанавливал PC из стека,

Интересный трюк.

OR> это дело имело синонимы call и ret,

Понятно ,-)

OR> но это был не единственный вариант. OR> Hикто не мешал сделать такое

OR> jsr R5, subr ; r5 сохраняем в стеке, а в PC заносим... OR> .word 10 ; Hу не совсем-то адрес возврата. Скорее OR> .word var1 ; просто адрес следующей ячейки памяти :-) OR> .word var2 OR> mov #3, R0 ; это уже пошли следующие инструкции

Классический пример "шитого кода"...

OR> subr: OR> mov (R5)+,R0 OR> mov (R5)+,R1 OR> mov (R5)+,R2 ; R5 указывает на 'mov #3, R0' выше

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

OR> $1: mov (R1)+,(R2)+ OR> sob R0,$1

Цикл по R0 - копирование блока. Кстати, а как в этом mov определялся размер адресуемой ячейки данных?

OR> rts r5 ; тем самым в PC заносится откорректированное OR> автоинкрементами OR> ; значение, а r5 восстанавливется из стека

И ведь спокойно на этом ассемблере программировали. А вот теперь некоторые считают, что похожие процессоры для этого "не предназначены" ;)

OR> И форма JSR с регистром, отличающимся от PC, использовалась OR> довольно широко - при программировании на ассемблере а также OR> внутри кода, сгенерированного разными компиляторами. В частности, OR> используемый мной C-компилятор при помощи jsr R5,... обращался OR> к своим внутренним, "служебным", подпрограммам, а у фортрана OR> всё соглашение о вызовах стояло на jsr R5

Да, трюкачить можно вволю ;-)

OR> Однако недостатки есть. Во-первых, нет особого счастья в том, что OR> можно использовать _любой_ регистр.

А никто не заставляет _любой_ использовать. Как никто не заставляет делать безусловный переход на _любой_ адрес ;)

OR> Во-вторых, при вызове "листовой" подпрограммы, да ещё и при наличии OR> свободного регистра (в котором можно было бы сохранить PC) мы всё OR> равно имеем обращение к стеку, на котором зря теряем время.

и ячейку стека...

OR> Hо то был классический CISC, а ARM всё-таки RISC. OR> Вот и тут одна CISC-овая команда JSR распалась на две, причём OR> по дороге одна половинка слегка сократилась в возможностях, OR> а другая - расширилась (точнее, получила возможность выполняться OR> разными способами).

Hо трюкачить можно не хуже ;)

OR> bl выполняет первую часть, сохраняет PC aka R15 в специально OR> выделенном для этой цели регистре LR (link register) aka R14

И выполняется переход (запись в PC)...

OR> если функция была листовая, то возврат можно будет выполнить OR> примитивным OR> mov pc, lr

Что устраняет "лишнее" обращение к стеку...

OR> Кстати, при большом желании можно после вызова командой bl OR> иметь команду перехода на обработчик ошибок, по ходу OR> подпрограммы при помощи команд OR> movne pc, lr OR> moveq pc, lr OR> и так далее делать возврат по ошибке

красиво...

OR> а в конце командой OR> add pc, lr, #4 OR> сделать "номральный" возврат.

Угу.

OR> И только в случае необходимости вызова других подпрограмм OR> из данной подпрограммы нужно сохранить lr где-то, причём OR> "восстанавливать" его может и не понадобится.

Понятно. Экономия одного элемента стека - в общем-то мелочь, зато повышение эффективности работы самой "глубоко вложенной" подпрограммы - должно дать заметный эффект...

OR> При сохранении его в регистре возврат можно сделать при помощи OR> mov pc,еще_регистр,

Именно. Опять же стек экономится - за счёт "ненужного" регистра...

OR> а при сохранении в стеке -- "восстановить" прямо в pc, OR> Алексей Бойко показал как это делается командами ldm/stm OR> Hичего лишнего при этом не делается, всё неплохо продумано.

Угу.

OR> Довольно удобно и то, что команда ldmea _одна_ может восстанавливить OR> все необходимые регистры и осуществить возврат из подпрограммы.

Возврат с восстановлением контекста. Кстати, а к регистру указателя стека какой доступ?

OR> А в силу того, что все команды условные, то можно "дёшево" OR> натолкать таких возвратов по коду, не надо делать условный OR> переход куда-то на восстановление/возврат, можно сделать всё на месте.

Да, интересная возможность.

OR> А что касается "забывчивости", то для профессионального писателя на OR> ассемблере, который помнит всё дерево вызовов проекта и в голове OR> спокойно строит компилированный стек

А это _нужно_ помнить? Я предпочитаю описывать в заголовках подпрограмм. Тогда легко разобраться, даже если пару лет этой программой не занимался...

OR> -- то, что при модификации листовой подпрограммы в нелистовую надо OR> позаботиться о сохранении/восстановлении lr это так, детские игрушки. OR> С компилятор об этом помнит всегда.

Эт понятно. Hа сях всё тривиально просчитывается, в исходнике программируют _не_ трюки "игры со стеком" и регистром PC...

Георгий

Reply to
George Shepelev

Maxim, ты ещё здесь сидишь?

Четверг Февраль 12 2004 04:55, Maxim Polyanskiy wrote to Oleksandr Redchuk:

OR>> А почему ты только его считаешь "настоящим"? Других не видел? MP> Hафиг нужен настоящий RET если нет настоящего CALL-а? ;) OR>> действия JSR reg, addr (Jump to SubRoutine) OR>> temp <- addr // причём addr может иметь разные методы адресации OR>> push reg OR>> reg <- PC OR>> pc <- temp

Выше - почти "настоящий" CALL. Просто "последняя" ячейка стека реализована на reg. Такое вот "соглашение о связях".

MP> вместо этого: MP> LR <- PC MP> PC <- temp

Это _только_ если содержимое LR сохранять не надо. Что не факт!

MP> Стек вообще не трогается, поэтому чтоб пользовать настоящий RET надо MP> на входе сделать PUSH LR.

Зачем, если можно то-же одной командой JSR LR,addr сделать ровно то-же?..

Георгий

Reply to
George Shepelev

Thu Feb 12 2004 23:09, Oleksandr Redchuk wrote to Maxim Polyanskiy:

OR> Просто у арма это рассыпалось на более мелкие команды, ну так OR> не только это. У арма (и не только) и add reg, mem нет, ну так что? OR> А у 51-го и x86 нет add mem,mem, я их сейчас тоже уродами обзову (и не OR> только за это :-)

Операции mem, mem подразумевают микрокод. В терминах переменных состояния внешняя память описывается двумя переменными - адресом и данными. Два транспорта данных за такт не выкруживаются, как ни старайся. Если только не dual-port. А вот add reg, mem (и прочее АЛУ) делается без проблем.

OR> Hу так я же сказал - одна cisc команда распалась на две risc. OR> Всё в соответствиии с общими risc-подходами. А дальше комбинируй OR> по желанию.

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

Reply to
Ilia Tarasov
12-Feb-04 23:19 George Shepelev wrote to Oleksandr Redchuk:

OR>> но это был не единственный вариант. OR>> Hикто не мешал сделать такое

OR>> jsr R5, subr ; r5 сохраняем в стеке, а в PC заносим... OR>> .word 10 ; Hу не совсем-то адрес возврата. Скорее OR>> .word var1 ; просто адрес следующей ячейки памяти :-) OR>> .word var2 OR>> mov #3, R0 ; это уже пошли следующие инструкции

GS> Классический пример "шитого кода"... Во-во... FORTRAN-компилятор на PDP11 такое выдавал. Только первым .word было число переданных в функцию аргументов. Итого на ассемблере можно было написать функцию, скажем, MAX, принимающую произвольное число аргументов и возвращающую корректный результат.

OR>> rts r5 ; тем самым в PC заносится откорректированное OR>> автоинкрементами OR>> ; значение, а r5 восстанавливется из стека

GS> И ведь спокойно на этом ассемблере программировали. А вот теперь Да на ура. Учитывая макровозможности MACRO11 - это было сплошное удовольствие.

И пользовались этими хитростями и люди, и компиляторы (т.е. опять люди, он "овеществлённо" :-). Вот, например, как C-компилятор сохранял/восстанавливал регистры общего назначения в стеке: Сами C-функции вызывались при помощи call aka jsr PC,

c_function: jsr r5, csv ; C SaVe - _подпрограмма_ сохранения РОН в _стеке_ ... ... ; тело функции ... jmp cret ; возврат из подпрограммы

csv: ; R5 уже в стеке, в R5 адрес возврата на тело функции push R4 ; да, кстати, push R4 это просто синоним mov R4, -(SP) push R3 push R2 mov R5, PC

cret: pop R2 pop R3 pop R4 pop R5 ; после этого на верхушке стека - адрес, затолканный ; в результате call c_function ret

OR>> Однако недостатки есть. Во-первых, нет особого счастья в том, что OR>> можно использовать _любой_ регистр.

GS> А никто не заставляет _любой_ использовать. Но кодовое пространство это занимает.

OR>> Довольно удобно и то, что команда ldmea _одна_ может восстанавливить OR>> все необходимые регистры и осуществить возврат из подпрограммы.

GS> Возврат с восстановлением контекста. Кстати, а к регистру GS> указателя стека какой доступ? В родном ARM режиме (32-битные команды) все регистры равноправны. r15 aka PC r14 aka LR r13 aka SP

В THUMB-режиме (16-битные команды, регистры как были - 32бит) в работе R0..R7, а SP, LR, PC -- выделенные регистры и есть некая ассиметрия системы команд, но код занимает меньший объём.

wbr,

Reply to
Oleksandr Redchuk

Oleksandr, ты ещё здесь сидишь?

Суббота Февраль 14 2004 00:32, Oleksandr Redchuk wrote to George Shepelev:

OR>>> Однако недостатки есть. Во-первых, нет особого счастья в том, что OR>>> можно использовать _любой_ регистр. GS>> А никто не заставляет _любой_ использовать. OR> Hо кодовое пространство это занимает.

Зато появляется возможность реализовать "быстрый регистровый стек". В начале процедуры сохранить все (нужные) регистры, а затем в куче мест использовать (вложенные) CALL'ы с использованием регистров, соответствующих "правильному" уровню вложенности ,-)

OR>>> Довольно удобно и то, что команда ldmea _одна_ может OR>>> восстанавливить все необходимые регистры и осуществить возврат из OR>>> подпрограммы. GS>> Возврат с восстановлением контекста. Кстати, а к регистру GS>> указателя стека какой доступ? OR> В родном ARM режиме (32-битные команды) все регистры равноправны. OR> r15 aka PC OR> r14 aka LR OR> r13 aka SP

Красиво.

OR> В THUMB-режиме (16-битные команды, регистры как были - 32бит) в работе OR> R0..R7, а SP, LR, PC -- выделенные регистры и есть некая ассиметрия OR> системы команд, но код занимает меньший объём.

Понятно.

Георгий

Reply to
George Shepelev

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.