LINUX.ORG.RU

О хвостовой рекурсии

 


3

4

Или с чем ее едят и как ее готовят? Набрел на статью в Википедии, ознакомился, сделал сравнительный замер времени выполнения для приведенного там примера. Результаты впечатлили, особенно если учесть, что тестировал я это для Scala с включенной оптимизацией.пиляторы

Собственно вопросов два.

1. Хотелось бы получше разобраться в устройстве хвосто-рекурсивных функций и их вызовов, дабы объяснить человеку, далекому от этой темы - в чем там магия?

2. Какие еще популярные ЯП и компиляторы поддерживают оптимизацию данного вида рекурсии?

Всем спасибо.

Ответ на: комментарий от drBatty

компилятор тоже НЕ может ничего определить. Ему сказали - «это целое», он считает это целым. Как и процессор.

Почему же не может? Может. Он выводит эту информацию на основе аннотаций типов и применений ф-й.

А процессор вывести эту информацию не может. Ты можешь применять любые операции к любым значениям. Можешь перемножить числа как беззнаковые - а затем их же перемножить как знаковые. По твоей логике тип ВНЕЗАПНО поменялся?

непонятно. Это не просто «некоторые процедуры», а специальные процедуры, предназначенные например для умножения.

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

Называть их можешь как хочешь, но твоя X1 даст правильный целый знаковый результат только для целых знаковых операндов.

А вот и нет. Если применить ее к беззнаковым операндам она даст _точно такой же_ результат. Это уже ты интерпретируешь его как правильный или нет. Процессор об этом ничего не знает.

Сказал ты «это целое», и НИКТО не станет проверять, целое оно или дробное. Ни процессор, ни компилятор.

Компилятор как раз и занимается тем, что проверяет это.

Пойми простую вещь: типы не определяют, их назначают.

Тип терма зависит от семантики программы. Ты ничего не назначаешь, назначает компилятор.

ну может у твоего процессора и так.

У твоего тоже.

А вот мой знает довольно много типов чисел.

Процессор вообще никаких чисел не знает.

Да, я знаю, _можно_ подсунуть процессору не тот тип

Ээээ... Каким образом? Вот у тебя есть команда ADD, как ей можно подставить «не тот тип»? Да и что в данном случае значит «не тот тип»?

компилятору тоже можно(с тем же результатом).

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

Вообще давай проще поступим, вот у нас есть ADD AX, BX. какие типы у AX, BX и результата?

anonymous
()
Ответ на: комментарий от drBatty

т.е. единственная ценность тсо в том, что оно иногда меняет call на jmp? И всё?

Да. Благодаря этому она экономит гигабайты памяти.

anonymous
()
Ответ на: комментарий от yyk

мы можем сразу расположить эти параметры в начале области стека, которую получили при вызове текущей функции и прыгнуть к началу следующей.

Так и происходит.

Во всех остальных случаях мы вынуждены будем либо дополнительно копировать рассчитанные параметры

Без либо. Под расчет промежуточного результата в любом случае выделяется память.

anonymous
()
Ответ на: комментарий от anonymous

Точно наркоман. Не уверен, что даже Кнут поможет. Слишком много ты книг прочитал, написанных теми, кто в жизни не написал ни одной реальной программы, а потому ушел в теоретический атсрал, не имеющий с программированием ничего общего.

anonymous
()
Ответ на: Наркоман? от anonymous

Если есть различные операции для различных типов, значит есть и типы.

А у процессора нет операций для различный типов. У него есть операции только для машинных слов.

а, ВНЕЗАПНО, тем, предусмотрены ли соответствующие интерпретации или нет

Тип значения определяется тем, какие операции можно применять к этому значению (интерфейс, грубо говоря). Фактически, набор операций = тип. В случае процессора к чему угодно применяется что угодно и никто мне не запретит. Иными словами, существует один единственный набор операций. А значит - один единственный тип.

а, ВНЕЗАПНО, тем, предусмотрены ли соответствующие интерпретации или нет

Наконец, до тебя дошло. Тип есть только тогда, когда мы интерпретируем байтики (или термы). Компилятор термы интерпретирует - соответственно, для компилятора есть типы. А процессор ничего не интерпретирует, он просто применяет хуйню Х к последовательности байтов Y - никак ее не интерпретируя. И как мы интерпретируем (либо можем интерпретировать) эту последовательность процессор неебет.

anonymous
()
Ответ на: комментарий от anonymous

А в чем должен помочь Кнут? Он вполне со мной согласен. В процессоре нету никаких типов. Не заложено туда (в процессор) соответствующей информации. Не может тип интерпретировать нечто как целое, или как флоат, или еще как-либо. Обратное доказать легко - достаточно привести пример, как процессор интерпретирует Х в качестве типа Y. До тех пор, пока такое докзаательство не предоставлено я пользуюсь бритвой Оккама. Поскольку работа процессора впонле объяснима без интерпретации им его работы с точки зрения типов - я полагаю, что такой интерпретации в нем и нет. Так же как нет бога, макаронного монстра и невидимого розового единорога внутри чайника Рассела.

anonymous
()
Ответ на: комментарий от anonymous

Без либо. Под расчет промежуточного результата в любом случае выделяется память.

Да, вот только в случае «нормальной» передачи параметров эта память может «выделяется» на стеке прямо в том порядке, как эти параметры будут передаваться вызываемой функции. Более того, если локальные переменные, в которых сохраняются передаваемые значения, только для этой задачи и используются - это прямая обязанность компилятора именно в таком порядке их и расположить ;)

yyk ★★★★★
()
Ответ на: комментарий от anonymous

можно, если расчёт этих самых параметров не будет зависеть от тех данных, которые они «затрут»

Ладно, это всё «гадания на кофейной гуще» - надо на практике попробовать :)

yyk ★★★★★
()
Ответ на: комментарий от anonymous

У процессора есть операции для различных типов. Более того, операции над некоторыми типами могут делать отдельные модули. FPU например. Да, процессор не проверяет. Но не наличием проверок определяется тип.

Компилятор в таком случае точно так же ничего не «интерпретирует»(ИИ не изобрели еще) - он просто делает ряд формальных проверок(«просто применяет хуйню Х к» хуйне Y).

И да, мы тут вроде раньше не общались(хотя кто нас, анонимусов, разберет).

anonymous
()
Ответ на: комментарий от anonymous

У процессора есть операции для различных типов.

Эти операции применяются к одному и тому же типу - последовательности машинных слов.

Еще раз, тип - это набор операций, которые мы можем применить к значению. Раз к любому значению процессор может применять любые операции, то тип один - Any.

Вот с компилятором все сложнее - если я объявил, что х имеет тип int, то хоть усрись, а я к нему уже не смогу применить функцию для char'ов например.

anonymous
()
Ответ на: комментарий от yyk

можно, если расчёт этих самых параметров не будет зависеть от тех данных, которые они «затрут»

с чего они должны что-то затереть, если специально для них выделена память при вызове?

Ладно, это всё «гадания на кофейной гуще» - надо на практике попробовать :)

На практике языки с ТСО существуют и в них стек не растет. Чего тут проверять? :)

anonymous
()
Ответ на: комментарий от anonymous

«Последовательность машинных слов» - это не тип, а способ представления. Тип определяется интерпретацией этих самых слов. Вызывая операцию, например, для числе с плавающей точкой, ты говоришь - вот тут у меня данные типа «число с плавающей точкой»(причем в таком-то формате). То, что процессор тебе верит на слово, никак не устраняет типы.

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

anonymous
()
Ответ на: комментарий от yyk

ручками может и не получится, а компилятор почему не сможет?

там не в том дело, дело в сдвиге.

И да, я совсем забыл про передачу параметров через регистры :( (надо на sbcl-е проверить) - может (особенно для 64-разрядного) передача параметров через стек будет только в очень крайних случаях?

ну дык в том и дело - результаты-то получаются в регистрах, какой смысл их загонять в стек, а потом вытаскивать, если от call мы избавились? А если избавились от call, поменяв на jmp, то зачем нам jmp? так и получается inline. И стек тут вообще не причём оказывается, разве что сам исходный фрейм, который используется как обычная временная память. в том-то и профит TCO.

drBatty ★★
()
Ответ на: комментарий от anonymous

И вообще не ясно, почему ты сравниваешь компилятор и процессор.

Любой современный процессор является весьма навороченным компилятором, если чо.

anonymous
()
Ответ на: комментарий от tailgunner

Раз к любому значению процессор может применять любые операции, то тип один - Any.

А как же word, byte? :)

Можно рассматривать это как один полиморфный тип - битовый вектор. Как в С--.

encyrtid ★★★★★
()
Ответ на: комментарий от tailgunner

А как же word, byte? :)

Ну так по адресу операции применяемой к байту может и машинное слово лежать. Короче тип - один, а как его назвать - дело десятое :)

anonymous
()
Ответ на: комментарий от anonymous

с чего они должны что-то затереть, если специально для них выделена память при вызове?

мы про «писанину» в «начало стека» при ТСО - там могут находиться «полезные данные»

На практике языки с ТСО существуют и в них стек не растет. Чего тут проверять? :)

Вот и интересует - каким макаром сие действо осуществляется при передачи пачки параметров, явно не влезающих в «свободные» регистры

yyk ★★★★★
()
Ответ на: комментарий от encyrtid

Можно рассматривать это как один полиморфный тип - битовый вектор. Как в С--.

ЕМНИП, в Си-- типов таки несколько - слова разных размеров.

tailgunner ★★★★★
()
Ответ на: комментарий от drBatty

ну дык в том и дело - результаты-то получаются в регистрах, какой смысл их загонять в стек, а потом вытаскивать, если от call мы избавились? А если избавились от call, поменяв на jmp, то зачем нам jmp? так и получается inline. И стек тут вообще не причём оказывается, разве что сам исходный фрейм, который используется как обычная временная память. в том-то и профит TCO.

Ты еблан. Тебе уже двадцать раз объяснили как работает ТСО и в чем ее профит, а ты до сих витаешь в каких-то своих фантазиях, не имеющих ничего общего с реальностью.

anonymous
()
Ответ на: комментарий от yyk

Вот и интересует - каким макаром сие действо осуществляется при передачи пачки параметров, явно не влезающих в «свободные» регистры

Еще один, вы заебали уже. Представь себе, что регистров вообще нет. На практике сперва делается инлайнинг, потом ТСО, и уже только после этого РА. То есть когда мы делаем ТСО то все лежит на стеке, в регистры мы еще ничего не клали. ТСО ничего не знает о регистрах.

Для особо одаренных пример:

(define (yoba x y) (yoba (+ x y) (- x y)))
здесь на стеке надо выделить место под х, у, и две временные переменные. При первом вызове выделяется стекфрейм на 4 переменные, при дальнейших хвостовых вызовах ничего больше не выделяется, а будет перезаписываться старый стекфрейм. ИРЛ сперва будет сделан инлайнинг, переменных станет больше, но потом при RA граф стянется опять до 4, котоыре потом лягут на регистры. Но это к ТСО никак не онтосится.

anonymous
()
Ответ на: комментарий от yyk

Вот и интересует - каким макаром сие действо осуществляется при передачи пачки параметров, явно не влезающих в «свободные» регистры

Сдвигом пачки параметров на начало stack frame, например.

anonymous
()
Ответ на: комментарий от tailgunner

Можно рассматривать это как один полиморфный тип - битовый вектор. Как в С--.

ЕМНИП, в Си-- типов таки несколько - слова разных размеров.

Таки нет, пункт 2.4 спецификации:

There are just two types in C--:

  • A k-bit value has type bitsk; for example, a four-byte word is specifed as bits32. Values may be stored in variables, passed as parameters, returned as results, and fetched from and stored in memory.
  • A Boolean value has type bool. Booleans govern control ow in conditionals. Boolean values are not first class: they cannot be stored in variables or passed to procedures.
encyrtid ★★★★★
()
Ответ на: комментарий от anonymous

А процессор вывести эту информацию не может. Ты можешь применять любые операции к любым значениям. Можешь перемножить числа как беззнаковые - а затем их же перемножить как знаковые. По твоей логике тип ВНЕЗАПНО поменялся?

так и есть - тип в ассемблере задаётся при операции, если ты про знаковость. А вот тип double в int ВНЕЗАПНО перейти не может, ибо операции над double производятся в _других_ регистрах, не в РОНах. Потому мой компилятор и рушит TCO выделяя свой кусок фрейма стека, для преобразования int->double. Из-за этого семантика ломается, и TCO становится невыгодным с т.з. gcc. А байты памяти - да, они имеют вообще говоря «тип» void*, и там тип какой получится. Но и тут есть исключение - стек, из/в которого/в который нельзя вынимать иначе, чем машинными словами.

Компилятор как раз и занимается тем, что проверяет это.

ага. на этапе компиляции. Ассемблер тоже проверяет на этапе ассемблирования на пример DWORD PTR, а в рантайме никто никого не проверяет, а просто выполняет готовые инструкции.

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

имеет. Если ты не программируешь на ассемблере, то тебе вообще ничего там «не важно», ну и что? А на самом деле, именно это-то и ВАЖНО, какую операцию к чему применить.

Тип терма зависит от семантики программы. Ты ничего не назначаешь, назначает компилятор.

в сишечке тип зависит от объявления. Именно я его и пишу. int не может внезапно стать unsigned, вне зависимости от семантики.

Ээээ... Каким образом? Вот у тебя есть команда ADD, как ей можно подставить «не тот тип»? Да и что в данном случае значит «не тот тип»?

можно подставить 32х битной команде add адрес памяти, в котором лежит 64х битное double. Команда add радостно сложит число с половинкой double, получив НЁХ.

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

ну и пиши программы на своих ЯП, оптимизированных под ТВОИ CPU. У меня таких CPU нет, потому мне, как и остальным кодерам IRL твои ЯП НЕ НУЖНЫ. Смирись, и жди появления LISP процессоров (говорят - в разработке уже 30+ лет).

Вообще давай проще поступим, вот у нас есть ADD AX, BX. какие типы у AX, BX и результата?

это 8086? тип - целое знаковое 16 бит длинной. А может и беззнаковое - в данном случае эта команда работает одинаково хорошо, потому не две команды, а одна (за то два набора ветвлений, для знаковых результатов(jl,jg) и для беззнаковых(ja,jb))

drBatty ★★
()
Ответ на: комментарий от anonymous

на стеке надо выделить место под х, у, и две временные переменные

вот эти две временные переменные при «нормальном» вызове могли бы «аллоцироваться» сразу на «местах» параметров для этого самого «нормального вызова», а при ТСО их придётся перемещать на место х и у - не? Т.е. экономим на call/ret, но получаем пачку mov-ов (я, как и просили, забыл про регистры)?

yyk ★★★★★
()
Ответ на: комментарий от anonymous

Если есть различные операции для различных типов, значит есть и типы.

А у процессора нет операций для различный типов. У него есть операции только для машинных слов.

ты точно упоролся... команда x86 IMUL это по твоему ЧТО? Извини, в википедию рыльцем ткнуть не могу, википедики про утиную типизацию знают, а про IMUL - не в курсе. Ищи сам описание ЭТОЙ команды, а то ведь скажешь, что мои пруфлинки не авторитетны.

В случае процессора к чему угодно применяется что угодно и никто мне не запретит.

потому-то дуракам и не дают стеклянного МПХ - ибо он и его расколет, и руки порежет, и губы оцарапает.

drBatty ★★
()
Ответ на: комментарий от drBatty

Потому мой компилятор и рушит TCO выделяя свой кусок фрейма стека, для преобразования int->double.

Ну так gcc просто тупой. На самом деле он мог выделить начало фрейма стека под аргументы хвостовых вызовов, а все промежуточные значения разместить следом. Поскольку x86 - не стековая машина, так делать можно. И даже довольно легко. Никаких дополнительных накладных расходов на такое не потребуется. Правда, с текущей архитектурой gcc это сложновато - хвостовые вызовы определяются в одном месте, а ABI lowering происходит совсем в другом, и обмениваться информацией эти два прохода не могут. Теоретически, LLVM от этой проблемы свободен. На практике он пока таких оптимизаций тоже не делает.

anonymous
()
Ответ на: комментарий от anonymous

А в чем должен помочь Кнут? Он вполне со мной согласен.

ага. Согласен. Потому на протяжении пяти томов выводит 100500 разных ТИПОВ, которые ещё и организует в запутанные структуры данных. И всё это - ВНЕЗАПНО на ассемблере. Где якобы никаких типов и нет...

Обратное доказать легко - достаточно привести пример, как процессор интерпретирует Х в качестве типа Y.

OK. какой тип имеет

x;
?

А какой

x = y * z;
??

Никакой? Тогда в C/C++/java/js/php/... ТИПОВ НЕТ!!!11

Поскольку работа процессора впонле объяснима без интерпретации им его работы с точки зрения типов - я полагаю

дяденька, твоё упоротое «понимание» и так всем ясно из твоего же высказывания:

А процессор ничего не интерпретирует, он просто применяет хню Х к последовательности байтов Y - никак ее не интерпретируя.

т.е. ничего ты не понимаешь...

drBatty ★★
()
Ответ на: комментарий от drBatty

так и есть - тип в ассемблере задаётся при операции

В чем это проявляется? Ну то есть какие у нас основания полагать, что какой-то там тип действительно задается, а не проводится обычная процедура над обычными машинными словами?

ага. на этапе компиляции.

Ну да.

Ассемблер тоже проверяет на этапе ассемблирования на пример DWORD PTR

А при чем тут ассемблер если мы о процессоре?

а в рантайме никто никого не проверяет, а просто выполняет готовые инструкции.

Именно. То есть в рантайме у процессора типов нет.

А на самом деле, именно это-то и ВАЖНО, какую операцию к чему применить.

вот операция деления некорректная если второй операнд - 0. Значит есть специальный тип «ноль» и «не ноль», так?

в сишечке тип зависит от объявления. Именно я его и пишу. int не может внезапно стать unsigned, вне зависимости от семантики.

Я и говорю - тип определяется независимо от тебя в соответствии с семантикой программы. Объявления - часть программы, если что.

можно подставить 32х битной команде add адрес памяти, в котором лежит 64х битное double. Команда add радостно сложит число с половинкой double, получив НЁХ.

Но ведь команда вполне корректно отработает и даст вполне корректный результат. Что что-то получилось не так - это ты знаешь. А процессор не знает. Значит у тебя в голове типы есть, а у процессора - нет. Потому он и не знает.

ну и пиши программы на своих ЯП, оптимизированных под ТВОИ CPU. У меня таких CPU нет, потому мне, как и остальным кодерам IRL твои ЯП НЕ НУЖНЫ. Смирись, и жди появления LISP процессоров (говорят - в разработке уже 30+ лет).

Да вобщем-то твой ассемблер нужен кому-то немногим более, чем лисп. Но я говорил не о лиспе.

это 8086? тип - целое знаковое 16 бит длинной.

С чего ты взял? Может это 16-битное число с плавающей точкой? Или вовсе булевое значение? Или char?

anonymous
()
Ответ на: комментарий от drBatty

Я вообще не понимаю, о чем спор. Тип - это абстрактное понятие, и не более того.

anonymous
()
Ответ на: комментарий от yyk

вот эти две временные переменные при «нормальном» вызове могли бы «аллоцироваться» сразу на «местах» параметров для этого самого «нормального вызова», а при ТСО их придётся перемещать на место х и у - не?

Нет, конечно. Как ты их туда аллоцируешь? У процессора нету такой операции, которая берет два аргумента, что-то с ними делает и кладет куда-то в третье место. То есть какие-нибудь частные случаи типа pushadd, может, и есть, но в общем случае ничего не выйдет.

anonymous
()
Ответ на: комментарий от anonymous

Раз к любому значению процессор может применять любые операции, то тип один - Any.

во первых - НЕ может. Сложи мне как целые два double из сопроцессора.

Только про память мне тут не надо - неужели твой говноЯП не может записать в память Over9000 int'ов, а потом прочитать оттуда же Over9000 double?

Вот с компилятором все сложнее - если я объявил, что х имеет тип int

вот с CPU точно также, если я сложил eax с eax, то хоть усрись, но результат и операнды целые числа в 32 бита.

drBatty ★★
()
Ответ на: комментарий от anonymous

Любой современный процессор является весьма навороченным компилятором, если чо.

скорее интерпретатором, хоть и аппаратным.

drBatty ★★
()
Ответ на: комментарий от anonymous

У процессора результат любой операции всегда будет в регистре. То, что ты видишь как ассемблер x86 - это язык высокого уровня, который процессором компилируется в RISC, который, естественно, чистая load/store архитектура. Так что в общем случае без разницы, как именно писать аргументы в stack frame (и куда именно в stack frame).

anonymous
()
Ответ на: комментарий от drBatty

ты точно упоролся... команда x86 IMUL это по твоему ЧТО?

Некоторая процедура, выполняемая над машинными словами. А по-твоему что?

Ищи сам описание ЭТОЙ команды, а то ведь скажешь, что мои пруфлинки не авторитетны.

Нашел. Описание подтвердило - это процедура, выполняемая над машинными словами.

anonymous
()
Ответ на: комментарий от drBatty

скорее интерпретатором, хоть и аппаратным.

Нет, именно компилятором. Он очень много чего интересного с кодом делает - бьет на basic block-и, делает хитрый instruction selection (а не просто микрокодовую трансляцию, как это было в середине 80х), делает свой register allocation. Жырновато для интерпретатора-то.

anonymous
()
Ответ на: комментарий от anonymous

Ну так gcc просто тупой. На самом деле он мог выделить начало фрейма стека под аргументы хвостовых вызовов, а все промежуточные значения разместить следом. Поскольку x86 - не стековая машина, так делать можно. И даже довольно легко. Никаких дополнительных накладных расходов на такое не потребуется.

вот только мне не очень ясно, будет-ли в этом профит?

drBatty ★★
()
Ответ на: комментарий от drBatty

во первых - НЕ может. Сложи мне как целые два double из сопроцессора.

Ты имеешь ввиду применить процедуру ADD к регистрам сопроцессора? Но у этой процедуры нет доступа к этим регистрам, так?

И при чем тут типы, я не понял?

вот с CPU точно также, если я сложил eax с eax, то хоть усрись, но результат и операнды целые числа в 32 бита.

Почему? Вот у меня в памяти в [AX] лежит вещественное число и в [BX] лежит вещественное число и я делаю ADD [AX], [BX]. Операнды - вещественные числа, очевидно. Результат? Ну вот смотрю на результат и вижу там 2 знаковых бита (порядок мантисса) 20 битов мантиссы и 8 порядка. Так что результат тоже вполне вещественный.

anonymous
()
Ответ на: комментарий от drBatty

вот только мне не очень ясно, будет-ли в этом профит?

Еще какой. Тебе уже несколько страниц это талдычат: экономия далеко не резиновой памяти стека, как следствие - улучшение cache locality, что особенно полезно для C++-кода, развернутого из темплейтов, ну и отсутствие ненужных RET-ов, каждый из которых - это несколько операций чтения из памяти.

Для типичного Си-кода выгода будет тоже, но больше всего выиграет C++.

anonymous
()
Ответ на: комментарий от encyrtid

ЕМНИП, в Си-- типов таки несколько - слова разных размеров.

Таки нет

Из более ранней статьи: http://www.cminusminus.org/download/pal-ifl.ps.gz

«C-- has a very weak type system, recognising only the following types:
- word1, word2, word4, word8
- float4, float8»

Может, к моменту спецификации что-то изменилось, но (из той же статьи):

«С-- takes a very simple, concrete approach: each data type has a fixed language-specified size. So, for example, word4 is a 4-byte word, while word8 is an 8-byte word» - насколько я понимаю, это значит, что bitsk - разные типы для разных значений k.

И даже в той цитате, что ты привел: «There are just two types in C--» это уже 2 типа :)

А еще в C-- есть массивы.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

И в том же LLVM знаковые и беззнаковые типы не различаются.

anonymous
()
Ответ на: комментарий от anonymous

В чем это проявляется? Ну то есть какие у нас основания полагать, что какой-то там тип действительно задается, а не проводится обычная процедура над обычными машинными словами?

ну например команда imul обрабатывает старший бит аргументов как знак, потому как в знаковом типе старший бит == знак числа. Потому это не обычная процедура (mul), которая тупо множит все биты, а специальная процедура, которая старшие биты не множит, а XOR'ит (ибо минус на минус даёт плюс и т.д.)

А при чем тут ассемблер если мы о процессоре?

а причём тут компилятор, если мы о процессоре? процессору плевать, чей код выполнять, ассемблера или компилятора.

Именно. То есть в рантайме у процессора типов нет.

есть. только они жёстко заданы, и не проверяются. А задаются/проверяются они на этапе компиляции или написания программы на ассемблере. Ясное дело, быстрее и проще доверить эту тупую ТИПОВУЮ рутину компилятору, чем ручками прописывать.

вот операция деления некорректная если второй операнд - 0. Значит есть специальный тип «ноль» и «не ноль», так?

Скорее есть «специальный тип» НЁХ. Его по разному называют. Считай его «результатом» деления на ноль. С этим «типом» нельзя никаких операций производить.

Я и говорю - тип определяется независимо от тебя в соответствии с семантикой программы. Объявления - часть программы, если что.

ну в CPU всё точно также, я могу записать что-то в N байт в eax, и получить целое 32х битное число. И оно никогда не станет double. Разве что я загружу его в память, а потом сконвертирую в double.

Но ведь команда вполне корректно отработает и даст вполне корректный результат. Что что-то получилось не так - это ты знаешь. А процессор не знает. Значит у тебя в голове типы есть, а у процессора - нет. Потому он и не знает.

если ты объявил x целым, но записал туда ASCIIZ «XYZ», то для твоего компилятора оно будет целым. И если ты его скажем распечатаешь, получишь что угодно, но только не XYZ. Получается, что тип ASCIIZ только в твоей голове?

Да вобщем-то твой ассемблер нужен кому-то немногим более, чем лисп. Но я говорил не о лиспе.

хрен вас, анонов разберёшь... А ассемблер я-бы уже давно забыл, если-бы не некоторые тонкие моменты, которые только в ассемблерном листинге и видны. Естественно, я использую компилятор.

С чего ты взял? Может это 16-битное число с плавающей точкой? Или вовсе булевое значение? Или char?

значит ты выбрал не ту команду. ADD AX,DX складывает два целых 16и битных числа.

drBatty ★★
()
Ответ на: комментарий от anonymous

Я именно про это и говорил.

Ёшкин кот, всё надо на пальцах... Мы что-то там посчитали, значение в регистре, нам его надо сохранить. Сохраняем во временную переменную (на стеке). И так 48 раз. Теперь надо вызвать функцию. В случае ТСО надо скопировать пачку значений с одного места в стеке в другое. В случае обычного вызова эти значения УЖЕ могут (должны?) находится в заданном физическом порядке для простого вызова функции. Ну? Мы не можем СРАЗУ при ТСО сохранять временные значения куда надо - там «входящие параметры».

Всё, устал...

yyk ★★★★★
()
Ответ на: комментарий от anonymous

процессора результат любой операции всегда будет в регистре.

откуда такая уверенность, что [esp+0x20] это НЕ регистр, а кусочек памяти, для доступа к которому надо прибавить 32 к esp, и что-то там вынуть из памяти? А вот по времени выходит - регистр...

drBatty ★★
()
Ответ на: комментарий от yyk

Сохраняем во временную переменную (на стеке). И так 48 раз. Теперь надо вызвать функцию. В случае ТСО надо скопировать пачку значений с одного места в стеке в другое.

Нет, в случае TCO эти значения надо сразу класть в начало фрейма.

Ну? Мы не можем СРАЗУ при ТСО сохранять временные значения куда надо - там «входящие параметры».

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

anonymous
()
Ответ на: комментарий от drBatty

Опять ты про свой высокоуровневый язык. Не интересно. Внутри компилятор это в регистр положит.

anonymous
()
Ответ на: комментарий от anonymous

Нашел. Описание подтвердило - это процедура, выполняемая над машинными словами.

какая и над КАКИМИ? Над любыми что-ли? Как понимать результат обработки скажем двух строк?

drBatty ★★
()
Ответ на: комментарий от anonymous

Жырновато для интерпретатора-то.

продвинутый интерпретатор. попробуй написать Over9000 inc eax, оно тупо будет выполнять Over9000 inc eax...

drBatty ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.