Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
// https://github.com/ghc/ghc/blob/e204431e5a5e2fd16da52b04bda2798f16c51344/rts/Interpreter.c#L1184
case bci_PUSH8: {
int off = BCO_NEXT;
Sp_subB(1);
*(StgWord8*)Sp = *(StgWord8*)(Sp_plusB(off+1));
goto nextInsn;
}
case bci_PUSH16: {
int off = BCO_NEXT;
Sp_subB(2);
*(StgWord16*)Sp = *(StgWord16*)(Sp_plusB(off+2));
goto nextInsn;
}
case bci_PUSH32: {
int off = BCO_NEXT;
Sp_subB(4);
*(StgWord32*)Sp = *(StgWord32*)(Sp_plusB(off+4));
goto nextInsn;
}
case bci_PUSH8_W: {
int off = BCO_NEXT;
*(StgWord*)(Sp_minusW(1)) = *(StgWord8*)(Sp_plusB(off));
Sp_subW(1);
goto nextInsn;
}
case bci_PUSH16_W: {
int off = BCO_NEXT;
*(StgWord*)(Sp_minusW(1)) = *(StgWord16*)(Sp_plusB(off));
Sp_subW(1);
goto nextInsn;
}
case bci_PUSH32_W: {
int off = BCO_NEXT;
*(StgWord*)(Sp_minusW(1)) = *(StgWord32*)(Sp_plusB(off));
Sp_subW(1);
goto nextInsn;
}
> The linker lives in rts/Linker.c and is responsible for handling runtime loading of code into a Haskell process. This is something of a big blob of unpleasant code, and see DynamicGhcPrograms for information about efforts to reduce our dependence on this linker.
Итак, тут у нас стековая машина из хачкеля, которая вродекак отвечает за динамическую загрузку какого-то говна.
Что мы видим? Обычные синонимы к херне из uint8_t int8_t uint16_t int16_t и так далее.
А у нас тут есть херня такая, что для bci_PUSH8 мы делаем Sp_subB(1) т.е. уменьшаем стек на 1, потом кастуем: *(StgWord8*)Sp = *(StgWord8*)(Sp_plusB(off+1));
Ну в общем какую-то херь в стек записываем из какого-то говна, но при этом стекпоинтер уменьшаем на 1. Т.е. стек у нас нихера не выровнен. И если потом будет хуйня bci_PUSH16: *(StgWord16*)Sp = *(StgWord16*)(Sp_plusB(off+2));
Которая кастанет невыровненную хуйню в *uint16_t и потом разыменует даже. А это UB
У нас было два пакетика GHC Core, семьдесят пять ампул C--, 5 пакетиков Spineless Tagless G-Machine или STG, солонка, наполовину наполненная LLVM, и целое море разноцветных бекендов, интерпретаторов, а так же пинта стекового языка, и 12 пузырьков ghci. Не то, чтобы всё это было категорически необходимо в языке программирования, но если уж начал обеспечивать студентов темами для masters theses, то к делу надо подходить серьёзно. Единственное, что вызывало у меня опасение — это стековые языки. Ничто в мире не бывает более беспомощным, безответственным и порочным, чем шитый код. Я знал, что рано или поздно мы перейдем и на эту дрянь.
Регистры иногда заканчиваются, надо где-то сохранять их содержимое. Да и в подпрограммах содержимое используемых регистров тоже надобно схоронить, а в конце восстановить. Так что без стека только совсем примитивные программы обходились.
Недавно смотрел документацию по «Итаниуму» (помним, скорбим). У него была фишка, именуемая ротацией регистров. При упоминании в программе нумерных регистров PR16...PR63, GR32...GR127 и FR32...FR127 реальное обращение происходит не к регистру с указанным номером (N), а к регистру с номером N+RRB (по модулю количества регистров), где RRB –— специальный регистр, значение которого увеличивается на единицу после инструкции, передающей управление на начало цикла. Т. е. если на первой итерации цикла RRB=0, то при упоминании PR16 данные читаются из PR16, на второй итерации цикла RRB=1 и при упоминании PR16 данные читаются из PR17, на третьей итерации RRB=2 и при упоминании PR16 данные читаются из PR18 и так далее. Аппаратный кольцевой буфер!
В «x86» вращаемых банков регистров нет, именно поэтому я за «x86».
> В «x86» вращаемых банков регистров нет, именно поэтому я за «x86».
Зато в x86 можно циклически сдвигать какой-нибудь регистр, и брать от него кусочек поменьше. В x86-64 например 64-битный регистр rax и его 8-битные кусочки ah и al.
> А я за «Ассемблер», потому что в нём нет ни кастов, ни UB.
В ассемблере есть (точнее, могут быть) недокументированные инструкции, которые хуй знает что делают. Вполне можно считать это UB-ом. А еще, если взять процессор версии X, в котором инструкция A (т.е. некоторая некорректная последовательность байт, которая не может быть декодирована как инструкция) не поддерживается (при попытке ее исполнения будет брошено исключение), то в каком-то более новом процессоре версии X+1 могут додуматься в такой способ кодировать некоторые инструкции, и тогда это уже не будет приводить к броску исключения.
Но ведь это не ассемблер, а машинные коды. Разве ассемблер (программа) может скомпилировать какой-то исходный код в бинарник с невалидными инструкциями (если погромист не додумался напрямую байтики писать через db, конечно)?
Ну нет. Это implementation-defined - я могу предсказать результат прочитав даташит на конкретный проц.
З.Ы. Хотя... вызов недокументированной инструкции на старом проце всё-таки UB. Хуй знает что она там делала. Вроде не гарантируется, что все неизвестные инструкции должны кидать исключение?
> Ну нет. Это implementation-defined - я могу предсказать результат прочитав даташит на конкретный проц.
В даташите может быть ничего не написано по поводу этой инструкции. Может даже так оказаться, что при попытке выполнения этой инструкции, процессор сгорает нахрен.
Other use of branch hint prefixes and/or other undefined opcodes with Intel 64 or IA-32 instructions is reserved; such use may cause unpredictable behavior.
Other use of the 66H prefix is reserved; such use may cause unpredictable behavior.
The address-size override prefix (67H) allows programs to switch between 16- and 32-bit addressing. <...> Using this prefix and/or other undefined opcodes when operands for the instruction do not reside in memory is reserved; such use may cause unpredictable behavior.
> unpredictable behavior.
изобрел процессор который при использовании недокументированного префикса подымает напряжение на VRM до 12 вольт и самоуничтожается
ни один копилятор или асемблер такую инструкцуию не генерит, так что вирус-убийцу процов пришлось писать на машинных колдах
> Суть метода заключается в создании условий для возникновения непредвиденных искажений данных при вычислениях в SGX, от которых не защищает применение шифрования и аутентификации памяти в анклаве. Для внесения искажений, оказалось, можно использовать штатные программные интерфейсы для управления частотой и напряжением, обычно применяемые для снижения энергопотребления во время простоя системы и активации максимальной производительности при выполнении интенсивных работ. Характеристики частоты и напряжения охватывают весь чип, в том числе влияют на выполнение вычислений в изолированном анклаве.
> Изменяя напряжение, можно добиться возникновения условий, при которых заряда оказывается недостаточно для регенерации ячейки памяти внутри CPU, и её значение меняется. Ключевым отличием от атаки RowHammer является то, что RowHammer позволяет изменить содержимое отдельных битов в памяти DRAM, путём цикличного чтения данных из соседних ячеек, в то время как Plundervolt позволяет добиться изменения битов внутри CPU, когда данные уже загружены из памяти для выполнения вычислений. Подобная особенность позволяет обойти применяемые в SGX механизмы контроля целостности и шифрования данных в памяти, так как значения в памяти остаются корректными, но могут исказиться при операциях с ними до того, как результат будет записан в память.
>изобрел процессор который при использовании недокументированного префикса подымает напряжение на VRM до 12 вольт и самоуничтожается
Новая уязвимость у Интел процессоров позволяет управлять напряжением на камне +/- 0.5В, что легко может привести к его выходу из строя https://www.linux.org.ru/forum/talks/15845897
Компания «Intel» пиздит идеи с говнокода.
Программы с «классическим» — сишко-крестовым — UB использовать нельзя: их поведение не определено где угодно (оно может «правильно» работать на каких-то платформах, но рассчитывать на это категорически нельзя).
Чтобы поведение какой-то конструкции было implementation-defined, оно должно быть задокументировано для всех платформ:
implementation-defined behavior
behavior, for a well-formed program construct and correct data, that depends on the implementation and
that each implementation documents
§3.12, [defns.impl.defined]
В результате имеем, что неподдерживаемые инструкции ассемблера — это и не UB, и не ID: с одной стороны, на платформах без поддержки данной инструкции поведение в принципе не определено (т. е. уже не ID), а с другой — на «хороших» платформах — точно задокументировано (не UB).
22.15 Undefined opcodes All new instructions defined for IA-32 processors use binary encodings that were reserved on earlier-generation processors. Attempting to execute a reserved opcode always results in an invalid-opcode (#UD) exception being generated.
6.15 Exception and interrupt reference
Interrupt 6 - Invalid Opcode Exception (#UD) <...> The opcodes D6 and F1 are undefined opcodes reserved by the Intel 64 and IA-32 architectures. These opcodes, even through undefined, do not generate an invalid opcode exception.
Интел сам себе противоречит... Все опкоды равны, но некоторые равнее других.
На ГК нужен какой-нибудь валидатор русского языка. Стоит выдавать сообщение "ошибка компиляции комментария", если комментарий претендует на информативность, но написан в стиле вореций.
Хм, или тупо скриптом Vindicar'а скрывать всех петухов, чтобы невалидное говно вида "вжрукк" не читать.
Стековые языки не обязательно реализуются в шитом коде, например S" SP-FORTH" компилицца в нативный код, а 'RETRO_FORTH в байткод, просто шитый код легче декомпилить.
И доебусь! А ещё я могу доебатся и до своих слов, ведь байткод можно считать частным случаем свёрнутого косвенного шитого кода, тогда, например, 'RETRO_FORTH тоже компилица в шитый код.
Шитый код компактный, быстрый, а его интерпретатор реализуется парой инструкций. Вот минимальный адресный интерпретатор прямого шитого кода с примерами подпрограмм:
; esi -- указатель инструкций
; ebp -- стек развраьов
; esp -- стек банных
macro next { ; сам адресный интерпретатор, этим макросом должны заканчиватся низкоуровневые определения
lodsd
jmp eax
}
macro rpush x { ; запушить x в стек развратов
sub ebp, 4
mov dword [ebp], x
}
macro rpop x { ; попнуть из стека развратов в x
mov dword x, [ebp]
add ebp, 4
}
; примеры низкоуровневых определений:
false:
push 0
next
state:
push var_state
next
var_state dd ?
store_: ; x addr !
pop ebx
pop dword [ebx]
next
forth_call: ; помещается в начале "высокоуровневых" определений
rpush esi
pop esi
next
exit: ; возврат в вызывающую подпрограмму
rpop esi
next
left_bracket: ; пример "высокоуровневого" определения
call forth_call
dd false, state, store_, exit
А в косвенном шитом коде в высокоуровневых определениях только адреса, и они могут хранится в секции с данными, а само ядро маленькое и может целиком поместиться в кешу. А вот пример адресного интерпретатора косвенного шитого кода, он не сложнее и не больше чем у прямого:
macro next {
lodsd
jmp [eax]
}
forth_call: ; адресс помещается в начале "высокоуровневых" определений
rpush esi
mov esi, eax
add esi, 4
next
; пример низкоуровневого определения
exit dd @f
@@:
rpop esi
next
; пример "высокоуровневого" определения:
left_bracket dd forth_call, false, state_, store, exit
А в подпрограмном шитом коде адресный интерпретатор не нужен, программа состоит из цепочки call'ов, и не сложно перейти от него к обычному нативному коду.
Хуёвый ты оппонент, тогда я пока временно за баткод:
В байкоде только КОП занимает один байт, но у него ещё могут быть аргументы, как в твоём примере у push (и в шитом коде тоже могут быть оргументы). Сам код получается компактный, но для его декодирования нужна как минимум одна таблица как минимум на 256 адресов (можно и меньше, но придётся добавлять условия в интерпретаторе байткола), вот примитивный интерпретатор бацкода:
; esi -- укахатель инструкций
; ebx -- адрес таблицы
; esp -- стек банных
; ebp -- стек развратов
next equ jmp next_
next_:
xor eax, eax
lodsb
jmp dword [ebx + eax * 4] ; у джумпа же так можно?
; пример реализации самих команд:
call_:
rpush esi
pop esi
next
exit:
rpop esi
next
push_:
lodsd
push eax
next
add_:
pop eax
add [esp], eax
next
; пример пожпрограссы в бакоде (PUSH_, ADD_, EXIT это сами байткоды
my_sub:
db PUSH_
dd 2
db PUSH_
dd 2
db ADD_, EXIT
Как видно интерпретатор байткода сложнее чем у прямого или косвенного шитого кода, ему требуется дополнительная память для таблицы. Но его главное преимущество в том, что сам сконпелированный ьайткод лучше переносим чем шитый код.
Интересная как у вас беседа, правда кажется что твой собеседник ничерта не понимает.
А я так понял из твоего объяснения что шитый код довольно сильно привязан к рахитектуре и нихуя не переносим. Чем же он тогда лучше чем обычная компиляция?
Только тем что "генерацию" шитого кода (кстати, а почему он так назван?) можно отвязать его "интерпретатора" (который на самом деле постамбула)
Косвеный шитый код можно переносить, но это сложнее чем переносить баткод, надо генерить объектный файл, а потом с ним линковатся. А форт язык простой и компилица быстро, в нём обычно всё в исходниках хранится/переносица и объектные файлы не нужны.
Там обычно всё в исходниках, даже части самой системы могут в исходниках хранится.
Если сильно хочеца генерь образ для gforth или просто бинарник. Вот только деконпелируется он просто, надо брать систему которая умеет в нативный код и оптимизации (типа SP-FORTH).
А вот бенчмарк разных техник интерпретации шитого кода: http://www.complang.tuwien.ac.at/forth/threading/Во многих случаях обычно быстрее прямой. Баткода там, к сожалению нет, но наверное за него можно считать switch threading.
> как шитокод может быть переносимым.
Токены можно легко переносить, а чтоб перенести косвенный шиткод надо генерить объектные файлы, и при загрузке просто подправить нужные адреса. Из форт-систем я знаю, что только GForth умеет делать переносимые образы:
На счёт других языков конпилирующихся в шиткоды я не знаю.
Кстати, прямой шиткод тоже можно переносить как косвенный, надо просто в начале всех определений ещё вставить платформо-специфичный кусочек.
> генерится чисто для скорости, а переносица все равно сырцы?
Тип того, в стандарте нет ничего про перенос сконпелированых программ, в каждой системе может быть по своему, а миожет и вообще не быть. А ещё шиткод может выигрывать по размеру у нативного, в какои-нибудь зачуханом контроллере это может оч важно.
> compile once, run everywhere не полу?
Token threading
> шиткод может выигрывать по размеру у нативного, в какои-нибудь зачуханом
это когда вместо
call pituh
call pituh
call pituh
у меня просто
pituh
pituh
pituh
?
я пока не очень понимаю как тут работает префетчер и декодер у cpu.. кажется что плохо работает.
в обычном коде пока вызыввают первого петуха, второй уже может с другой стнацуей вычисляться (привет мельтдаун)
а как быть с шитокодом? что же это, никакого спекулятивного выполнения, никакого мельдауна?
Ну там же есть кеш косвенных вызовов. Вспомнит, куда обычно с этой строки прыгали. Так что небольшие горячие циклы не должны прям сильно тупить. А для остального кода и пофиг.
З.Ы. Кстати поэтому косвенный jmp обычно в конце каждой примитивной команды накопипащен. А не один на всех.
Что это за хуйня "*(StgWord8*)Sp" по-вашему? Типы StgWord8 StgWord16 StgWord32 определены в
Что мы видим? Обычные синонимы к херне из uint8_t int8_t uint16_t int16_t и так далее.
А у нас тут есть херня такая, что для bci_PUSH8 мы делаем Sp_subB(1) т.е. уменьшаем стек на 1, потом кастуем: *(StgWord8*)Sp = *(StgWord8*)(Sp_plusB(off+1));
Ну в общем какую-то херь в стек записываем из какого-то говна, но при этом стекпоинтер уменьшаем на 1. Т.е. стек у нас нихера не выровнен. И если потом будет хуйня bci_PUSH16: *(StgWord16*)Sp = *(StgWord16*)(Sp_plusB(off+2));
Которая кастанет невыровненную хуйню в *uint16_t и потом разыменует даже. А это UB
И кто вас блядь учил писать так макросы? Макросы принято писать КАПСОМ!
да и у юнисков бывает так что есть функция foo и апнисано: "вронинг! эта функция может быть мокросом"
Именно потому я за "Haskell"
можно всю память выделить статически и там шарудить
в няшной тоже можно, но надо убедица что кал-конвеншен там через регистры а не через
я когда был мелким пиздюком и писал под tiny (когда вся мапять в одном сегменте 64К) а API биоса было на регистрах, и мне стек вообще не нужен был
На фортране и бейсике отлично обходились без этих ваших автоматических переменных.
В «x86» вращаемых банков регистров нет, именно поэтому я за «x86».
Зато в x86 можно циклически сдвигать какой-нибудь регистр, и брать от него кусочек поменьше. В x86-64 например 64-битный регистр rax и его 8-битные кусочки ah и al.
выделил сразу кусок и управляй ею
хуже того: в асембреле у тебя не было никакой кучи ввиду аотстуствия ее менеджера
вся пмять твоя
> в асембреле у тебя не было никакой кучи
В виндах и линупсах есть. И ничто не машало использовать билиботеку/написать свой для ДОЧ'а.
В ассемблере есть (точнее, могут быть) недокументированные инструкции, которые хуй знает что делают. Вполне можно считать это UB-ом. А еще, если взять процессор версии X, в котором инструкция A (т.е. некоторая некорректная последовательность байт, которая не может быть декодирована как инструкция) не поддерживается (при попытке ее исполнения будет брошено исключение), то в каком-то более новом процессоре версии X+1 могут додуматься в такой способ кодировать некоторые инструкции, и тогда это уже не будет приводить к броску исключения.
Например
то да
а так же не смогу писнуть с CR3 таблу страниц бо их тоже не было в 286 (хотя протектед мод там и был)_
З.Ы. Хотя... вызов недокументированной инструкции на старом проце всё-таки UB. Хуй знает что она там делала. Вроде не гарантируется, что все неизвестные инструкции должны кидать исключение?
В даташите может быть ничего не написано по поводу этой инструкции. Может даже так оказаться, что при попытке выполнения этой инструкции, процессор сгорает нахрен.
невалидная инсрукция это INVALID OPCODE.
Вроде на интеле находили недокументированные опкоды которые что-то делают и не кидают.
Впрочем, про UB тоже не написано.
The opcodes D6 and F1 are undefined opcodes reserved by the Intel 64 and IA-32 architectures.
Но надо ещё поискать как обстоят дела с недокументированными сочетаниями префиксов.
Other use of the 66H prefix is reserved; such use may cause unpredictable behavior.
The address-size override prefix (67H) allows programs to switch between 16- and 32-bit addressing. <...> Using this prefix and/or other undefined opcodes when operands for the instruction do not reside in memory is reserved; such use may cause unpredictable behavior.
изобрел процессор который при использовании недокументированного префикса подымает напряжение на VRM до 12 вольт и самоуничтожается
ни один копилятор или асемблер такую инструкцуию не генерит, так что вирус-убийцу процов пришлось писать на машинных колдах
Интел кажется что-то такое тоже изобрел
https://www.opennet.ru/opennews/art.shtml?num=52011
> Суть метода заключается в создании условий для возникновения непредвиденных искажений данных при вычислениях в SGX, от которых не защищает применение шифрования и аутентификации памяти в анклаве. Для внесения искажений, оказалось, можно использовать штатные программные интерфейсы для управления частотой и напряжением, обычно применяемые для снижения энергопотребления во время простоя системы и активации максимальной производительности при выполнении интенсивных работ. Характеристики частоты и напряжения охватывают весь чип, в том числе влияют на выполнение вычислений в изолированном анклаве.
> Изменяя напряжение, можно добиться возникновения условий, при которых заряда оказывается недостаточно для регенерации ячейки памяти внутри CPU, и её значение меняется. Ключевым отличием от атаки RowHammer является то, что RowHammer позволяет изменить содержимое отдельных битов в памяти DRAM, путём цикличного чтения данных из соседних ячеек, в то время как Plundervolt позволяет добиться изменения битов внутри CPU, когда данные уже загружены из памяти для выполнения вычислений. Подобная особенность позволяет обойти применяемые в SGX механизмы контроля целостности и шифрования данных в памяти, так как значения в памяти остаются корректными, но могут исказиться при операциях с ними до того, как результат будет записан в память.
Новая уязвимость у Интел процессоров позволяет управлять напряжением на камне +/- 0.5В, что легко может привести к его выходу из строя
https://www.linux.org.ru/forum/talks/15845897
Компания «Intel» пиздит идеи с говнокода.
Чтобы поведение какой-то конструкции было implementation-defined, оно должно быть задокументировано для всех платформ:
§3.12, [defns.impl.defined]
В результате имеем, что неподдерживаемые инструкции ассемблера — это и не UB, и не ID: с одной стороны, на платформах без поддержки данной инструкции поведение в принципе не определено (т. е. уже не ID), а с другой — на «хороших» платформах — точно задокументировано (не UB).
All new instructions defined for IA-32 processors use binary encodings that were reserved on earlier-generation processors. Attempting to execute a reserved opcode always results in an invalid-opcode (#UD) exception being generated.
6.15 Exception and interrupt reference
Interrupt 6 - Invalid Opcode Exception (#UD)
<...> The opcodes D6 and F1 are undefined opcodes reserved by the Intel 64 and IA-32 architectures. These opcodes, even through undefined, do not generate an invalid opcode exception.
Интел сам себе противоречит... Все опкоды равны, но некоторые равнее других.
На касты там больше похожи movzx и movsx.
Хм, или тупо скриптом Vindicar'а скрывать всех петухов, чтобы невалидное говно вида "вжрукк" не читать.
праздравляю
dwarf
dixed
типа ллвм?
Шитый код компактный, быстрый, а его интерпретатор реализуется парой инструкций. Вот минимальный адресный интерпретатор прямого шитого кода с примерами подпрограмм:
А в косвенном шитом коде в высокоуровневых определениях только адреса, и они могут хранится в секции с данными, а само ядро маленькое и может целиком поместиться в кешу. А вот пример адресного интерпретатора косвенного шитого кода, он не сложнее и не больше чем у прямого:А в подпрограмном шитом коде адресный интерпретатор не нужен, программа состоит из цепочки call'ов, и не сложно перейти от него к обычному нативному коду.
Теперь ваш ход.
каждый оп в таком коде занимает один байт. Мало того что это сильно упрощает декодер, так еще и создает компактный код.
Возьмем стековую машину где надо сложить 2 и 2
push 2
push 2
add
всё.
В байкоде только КОП занимает один байт, но у него ещё могут быть аргументы, как в твоём примере у push (и в шитом коде тоже могут быть оргументы). Сам код получается компактный, но для его декодирования нужна как минимум одна таблица как минимум на 256 адресов (можно и меньше, но придётся добавлять условия в интерпретаторе байткола), вот примитивный интерпретатор бацкода:Как видно интерпретатор байткода сложнее чем у прямого или косвенного шитого кода, ему требуется дополнительная память для таблицы. Но его главное преимущество в том, что сам сконпелированный ьайткод лучше переносим чем шитый код.
У меня усё.
А я так понял из твоего объяснения что шитый код довольно сильно привязан к рахитектуре и нихуя не переносим. Чем же он тогда лучше чем обычная компиляция?
Только тем что "генерацию" шитого кода (кстати, а почему он так назван?) можно отвязать его "интерпретатора" (который на самом деле постамбула)
Если сильно хочеца генерь образ для gforth или просто бинарник. Вот только деконпелируется он просто, надо брать систему которая умеет в нативный код и оптимизации (типа SP-FORTH).
http://www.complang.tuwien.ac.at/forth/threaded-code.html
там есть примеры переносимых реализаций интерпретаторов, по ним кстати видно какая сишка убогая при своей-то низкоуровневости.
А вот бенчмарк разных техник интерпретации шитого кода:
http://www.complang.tuwien.ac.at/forth/threading/Во многих случаях обычно быстрее прямой. Баткода там, к сожалению нет, но наверное за него можно считать switch threading.
http://govnokod.ru/user/26089/codes
http://govnokod.ru/user/25786/codes
http://govnokod.ru/user/26137/codes
http://govnokod.ru/user/26135/codes
Даже если там не
call 123 а просто 123: у одного проца адреса выравниваются так, у дргого этак.
шитокод надо гегенинирить под каждую платхорму отдельно.
или это ок? или это как .pyc для питона: генерится чисто для скорости, а переносица все равно сырцы?
то-есть как у джавоклассов: compile once, test run everywhere не полу?
Токены можно легко переносить, а чтоб перенести косвенный шиткод надо генерить объектные файлы, и при загрузке просто подправить нужные адреса. Из форт-систем я знаю, что только GForth умеет делать переносимые образы:
На счёт других языков конпилирующихся в шиткоды я не знаю.
Кстати, прямой шиткод тоже можно переносить как косвенный, надо просто в начале всех определений ещё вставить платформо-специфичный кусочек.
> генерится чисто для скорости, а переносица все равно сырцы?
Тип того, в стандарте нет ничего про перенос сконпелированых программ, в каждой системе может быть по своему, а миожет и вообще не быть. А ещё шиткод может выигрывать по размеру у нативного, в какои-нибудь зачуханом контроллере это может оч важно.
> compile once, run everywhere не полу?
Token threading
много времени тратится на парсинг, и его результаты жалко терять.
Это конечно не так быстро работаит, но можно джытнуть. Но джиту же нужно сначала сипать в страницу а потом выключать ей w и включать x, да?
это когда вместо
call pituh
call pituh
call pituh
у меня просто
pituh
pituh
pituh
?
я пока не очень понимаю как тут работает префетчер и декодер у cpu.. кажется что плохо работает.
в обычном коде пока вызыввают первого петуха, второй уже может с другой стнацуей вычисляться (привет мельтдаун)
а как быть с шитокодом? что же это, никакого спекулятивного выполнения, никакого мельдауна?
З.Ы. Кстати поэтому косвенный jmp обычно в конце каждой примитивной команды накопипащен. А не один на всех.