Компилятор Oberon-07

High-level languages programming questions
  • 0CodErr wrote:Процедурные типы и переменные процедурных типов тоже попадают в этот список.
    Если это лишнее — убрать не долго
    Да, это конечно, лишнее.
    0CodErr wrote: Комментарии сейчас могут быть дважды вложенными
    Нормально, на практике бОльшая глубина не требуется.

    При беглом осмотре нашел только один недочет: кроме суффикса "H" (INTEGER), шестнадцатиричные константы могут иметь суффикс "X" (CHAR), последние не подсвечиваются, но должны подсвечиваться как "строки".

    В остальном -- отлично!

    Сейчас пишу транслятор промежуточного байт-кода в ассемблер FASM, заодно и опробую HyppoEdit.
    Байт-код не сложный, но обширный -- около 180 инструкций. Пока обработано только 30. Первые результаты несколько неоднозначные.
    Такой код:

    Code: Select all

    PROCEDURE test (n: INTEGER);
    VAR
        a: INTEGER;
    BEGIN
        WHILE n > 0 DO
            a := 7FFFFFFFH;
            WHILE a > 0 DO
                a := a - 1
            END;
            n := n - 1
        END
    END test;
    выполняется в 3 раза быстрее, чем скомпилированный текущим (старым) компилятором, но в 6-7 раз медленнее, чем Delphi7. Но тут понятно, Delphi хорошо оптимизирует: процедура-лист, две переменные -- два регистра. Мои компиляторы честно загружают и сохраняют переменные при каждом обращении. В более сложных случаях разрыв, наверно, будет меньше. Сделать полноценное распределение регистров, конечно, сложно, но можно будет попробовать выявить в программе места, в которых распределить регистры сравнительно просто (вроде этой процедуры), и транслировать такие процедуры по особым правилам.
  • akron1 wrote:выполняется в 3 раза быстрее, чем скомпилированный текущим (старым) компилятором, но в 6-7 раз медленнее, чем Delphi7
    Ну прогресс ведь уже есть — это главное!
    Какое, кстати, внутреннее соглашение вызова(если не указано явно [stdcall] | [winapi]| [cdecl])?
    akron1 wrote:шестнадцатиричные константы могут иметь суффикс "X" (CHAR), последние не подсвечиваются, но должны подсвечиваться как "строки".
    Пробовал такой регуляркой:

    Code: Select all

    <Regexp text="[0-9][0-9A-F]*X" lead="[^\w]"/>
    Вот здесь:

    Code: Select all

          <Style id="char" name="Char" text="1" bold="0" italic="0" underline="0" clr="Strings" bkclr="#FFFFFFFF">
            <Blocks>
              <Block open=""" close="""/>
              <Regexp text="[0-9][0-9A-F]*X" lead="[^\w]"/>
            </Blocks>
          </Style>
    
    Проверял её в https://regex101.com/ и она там рабочая.
    Но в HippoEDIT она не заработала. Вообще у меня не самая новая версия программы, может просто ещё тег Regexp не поддерживался.
    Ни в одной синтаксической схеме, поставляемой с моей версией, не присутствует ни один тег Regexp.
    В то же время в файлах отсюда https://www.hippoedit.com/syntax_files.php?lang=ru такой тег присутствует во многих местах, в частности в asm_arm_spec.xml:

    Code: Select all

          <Style id="number" extend="true">
            <Blocks>
              <!--  0xFFFF -->
              <Regexp text="0x[0-9A-Fa-f]+\>" lead="[^\w]"/>
              <!-- Binary: 0b1010 -->
              <Regexp text="0b[01]+\>" lead="[^\w]"/>
              <!-- Octal: 01234567 -->
              <Regexp text="0[0-7]\>" lead="[^\w]"/>
              <!-- Decimal  are built-in -->
            </Blocks>
          </Style>
    0CodErr wrote:Вставка шаблонов кода Code templates по Ctrl+Enter.
    Как оказалось, клавишу Ctrl нажимать не всегда обязательно, в большинстве случаев достаточно Enter.
    Есть ещё Ctrl+Пробелautocomplete(автоподстановка по первым введённым буквам).

    Забыл сказать, что если выбрать опцию 'Capture output', тогда то, что компилятор выводит обычно в консоль, будет выведено прямо в редакторе в панель Output.
    Потом его ещё вроде можно регулярками распарсить(Output Pattern).
    Spoiler:
    capture_output_hippo_edit.PNG
    capture_output_hippo_edit.PNG (76.13 KiB)
    Viewed 13854 times
  • 0CodErr wrote:Какое, кстати, внутреннее соглашение вызова(если не указано явно [stdcall] | [winapi]| [cdecl])?
    По умолчанию stdcall.
    Есть нюансы: массивы и записи всегда передаются по ссылке, но если перед формальным параметром-записью указано VAR, то на самом деле там будет передан не один параметр, а два: тип записи и адрес:

    Code: Select all

    PROCEDURE (VAR r: REC);
    procedure (typeof(r), adr(r)); stdcall
    Если формальный параметр -- открытый массив (VAR или нет -- не важно), то кроме адреса передаются длины по всем измерениям:

    Code: Select all

    PROCEDURE (a: ARRAY OF ... );
    procedure (adr(a), len(a, 0), len(a, 1)...); stdcall
    В новом компиляторе я изменил порядок передачи адреса и длин:

    Code: Select all

    PROCEDURE (a: ARRAY OF ... );
    procedure (len(a, 0), len(a, 1)..., adr(a)); stdcall
    Это оказалось удобнее для индексации массива и передачи в другую процедуру. Не знаю, почему я сразу так не сделал... Наверно, долго не обдумавал и просто взял первое что пришло.
  • Может лучше уж опционально транслировать в LLVM ?

    В FASM-никакой выгоды, на 1й взгляд.
  • Новый компилятор отличается от старого не только "более лучшим" качеством результирующего кода, а также исходного (в последнее время я пишу значительно лучше). Отличается архитектура: есть четкое разделение на фронт-энд (исходный код -> промежуточное представление) и бэк-энд (промежуточное представление -> целевой код). Разделение настолько четкое, что сейчас это физически две отдельные программы. Предполагается, что таких бэк-эндов будет несколько, в принципе, можно будет сделать бэк-энд LLVM. Но пока я транслирую в FASM. Часто говорят, что трансляция ЯВУ в ассемблер не имеет никаких преимуществ. Но я с этим не согласен: это позволяет легко контролировать правильность трансляции, первое время не заниматься опкодами и вычислением меток, а потом можно без проблем заменить ассемблерный код на машинный. Как это и было в первый раз. Может быть, LLVM выдаст более эффективный машинный код, но ассемблер x86 -- это единственный низкоуровневый язык который я кое-как знаю. Всё остальное мне нужно сначала прочитать, осмыслить и только потом приступать к трансляции. В общем, пока цель -- FASM, потом FASM -> машинный код, а потом посмотрим...
  • akron1 wrote:Байт-код не сложный, но обширный -- около 180 инструкций.
    А можно, если это не секрет, узнать, почему так много?
  • На самом деле, многие инструкции очень похожи, отличаются знаком операции, порядком и типом операндов.
    Например, операции сравнения: их шесть ">", "<", ">=", "<=", "=", "#". Вроде немного...

    Но во-первых, они могуть применяться к разным типам операндов: целым, вещественным и строковым. Для каждого типа нужна своя операция сравнения. Таким образом, ужЕ получается 18 операций.

    Во-вторых, операнды могут быть константами и переменными (выражениями). Возможны три случая:
    1) оба операнда переменные (выражения)
    2) левый операнд переменная, правый -- константа
    3) правый операнд переменная, левый -- константа
    Если оба операнда -- константы, то это вычисляется при компиляции и код не генерируется. Каждый из этих случаев транслируется в машинный код по своим правилам.

    18*3 = 54

    То же самое можно сказать про арифметические операции: "+", "-" и т. д. А еще есть встроенные в язык процедуры: INC, ODD, LSL ... Вот так и получается ~180.

    Конечно, некоторые инструкции полностью совпадают:

    a > 5: больше(выраж, конст)
    5 < a: меньше(конст, выраж)

    Они одинаково транслируются в машинный код, но на этапе трансляции в промежуточный код, удобнее считать их различными. Пока, во всяком случае, а потом, может я их объединю.
  • akron1 wrote:...
    Ага, теперь всё понял. Благодарю за развёрнутый ответ.