[TL;DR: следующие инструкции байт-кода JVM, похоже, не работают:
iconst_0
istore 6
...sequential
iinc 6 1
jsr L42
...
; L42
iload 6
ifeq L53 ; Always branches!!!
astore 8
iinc 6 -1
; L53
LDC 100
ISUB ; ERROR, returnAddress is at the top of the stack
Тестовый .class можно найти здесь (с немного более сложной логикой). Если вы хотите узнать больше о том, почему я вижу эти инструкции, продолжайте читать.]
Я пишу компилятор пробелов, ориентированный на байт-код JVM. Несмотря на то, что это эзотерический язык, Whitespace описывает интересный набор инструкций по ассемблеру для стековой машины, который хорошо отображается на JVM.
Пробелы имеют метки, которые являются целями для перехода (goto/jump-if-zero/jump-if-negative) и вызовов функций. Соответствующие инструкции (с именами, данными мной, в спецификации они даны как комбинации пробела, табуляции и новой строки):
mark <label>
: устанавливает метку для следующей инструкцииjump[-if-neg|-if-zero] <label>
: безусловный или условный переход к заданной меткеcall <label>
: вызвать функцию, на которую указывает меткаend <label>
: завершает функцию, возвращаясь к вызывающей стороне.
Мой компилятор выводит всю программу Whitespace в основном методе класса. Самый простой способ реализовать call
и end
— использовать коды операций JSR
и RET
, предназначенные для реализации подпрограмм. После операции JSR
стек будет содержать ссылку returnAddress
, которую следует сохранить в переменной для последующего использования в end
.
Однако, поскольку mark
может быть либо call
, либо jump
, стек может содержать или не содержать ссылку returnAddress
. Я решил использовать логическую переменную (бит вызова по адресу 6), чтобы сохранить, как была достигнута отметка, а затем проверить, следует ли сохранять вершину стека в локальную переменную (адрес возврата, по адресу 8). Реализация каждой инструкции выглядит следующим образом:
; ... initialization
iconst_0
istore 6 ; local variable #6 holds the call bit
# call
iinc 6 1 ; sets the call bit
jsr Lxxx ; jumps to the given label, pushing a returnAddress to the stack
# mark
; Lxxx
iload 6 ; loads the call bit
ifeq Lxxx-end ; SHOULD jump to mark's end if the call bit is not set
; call bit is set: mark was call-ed and returnAddress is in the stack
astore 8 ; stores returnAddress to local variable #8
iinc 6 -1 ; resets the call bit
; Lxxx-end
# end
ret 8 ; returns using the stored returnAddress
Проблема: ifeq
ВСЕГДА ветки. Я также пытался изменить логику на обратную (call-bit -> jump-bit, ifeq->ifne) и даже просто переключиться на ifne
(что было бы неправильно).. ., но if всегда переходит в конец. После вызова returnAddress
остается в стеке, и следующая операция прерывается.
Я использовал анализатор ASM для просмотра стека для отладки всего этого, но только что подтвердил это поведение и не могу найти, что я делаю неправильно. Мое единственное подозрение состоит в том, что в iinc
или в ifeq
есть нечто большее, чем может вообразить моя тщеславная философия. Я признаю, что я только прочитал набор инструкций страницу и соответствующую документацию ASM для этого проекта, но я надеюсь, что кто-то может предложить решение из головы.
В этой папке находятся соответствующие файлы, включая исполняемый класс и исходный пробел , а также результаты анализа javap -c
и ASM.
ifne
, но во втором фрагменте с идентичным кодом вы переходите с помощьюifeq
. Это намеренно?ifeq
кажется правильным кодом операции для того, что вы хотите, так почему же его нет в первом фрагменте? - person Erwin Bolwidt   schedule 03.04.2015ifeq
? Возможно, ваш инкремент/декремент не возвращает его обратно к нулю. - person Erwin Bolwidt   schedule 03.04.2015