Введение

Привет, Хабр! Сегодня я хочу поделиться своими наблюдениями и опытом по работе с различными ассемблерами. Я сам пишу на языке C и относительно редко касался темы ассемблера. Но недавно решил восполнить этот пробел в знаниях и посмотреть на различные ассемблеры. В данной статье мы не будем рассматривать ARM, AVR и другие микроконтроллерные архитектуры, а сосредоточимся исключительно на компьютерных ассемблерах. Давайте не будем судить строго, ведь это скорее исследовательский опыт, чем глубокое погружение.

Введение в ассемблеры

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

Классика жанра: NASM и GAS

NASM (Netwide Assembler) — это свободный ассемблер для архитектуры Intel x86, известный своей простотой и мощью. Он поддерживает 16-, 32- и 64-разрядные программы и предлагает богатый набор макросов. NASM часто используется для написания высокопроизводительных участков кода, драйверов устройств и операционных систем.

  • Автор: Simon Tatham и Julian Hall.

  • Репозиторий: NASM GitHub

  • Инструкции: NASM поддерживает порядка 700+ инструкций, включая различные расширения и специфические команды.

GAS (GNU Assembler) — ассемблер проекта GNU, используемый компилятором GCC. Это кроссплатформенный инструмент, поддерживающий множество архитектур. GAS отличается мощными возможностями по работе с макросами и директивами, что делает его незаменимым инструментом для разработки системного программного обеспечения.

  • Автор: Проект GNU.

  • Репозиторий: GAS в составе GNU Binutils

  • Инструкции: Поддерживает более 1000 инструкций, так как покрывает множество процессорных архитектур

ReLax и AsmX

Relax — не является традиционным ассемблером, но его автор позиционирует его как язык программирования, который компилируется в байт‑код. Сложно назвать его полноценным компилятором, так как он больше похож на виртуальную машину.

AsmX — это язык программирования на основе NodeJS. AsmX G2 является вторым поколением этой платформы. AsmX пытается предложить ассемблер для разработчиков, знакомых с веб‑технологиями.

Relax и AsmX: Первые впечатления

Relax, несмотря на заявления о компиляции, вызвал у меня больше вопросов, чем ответов. Отсутствует понятная документация, а сам процесс компиляции неясен. Хоть автор и позиционирует Relax как компилятор, по факту его реализация больше напоминает байт‑машину. Я не нашел информации о конкретных инструкциях компиляции, что показалось мне странным, ведь продукт должен быть готов к использованию. На страницах GitHub‑репозиториев RVM и IRasm присутствует лицензия, но документации по использованию языка я не нашел.

AsmX, в отличие от Relax, обладает более структурированным подходом. На GitHub доступна документация, позволяющая разобраться с установкой и использованием. AsmX Foundation реализовали довольно необычную систему, которая больше похожа на JIT‑компилятор, но не является классической виртуальной машиной. Впервые я сталкиваюсь с подобным подходом, и это можно считать своего рода ноу‑хау. Автор в README‑файлах репозиториев подробно описывает обновления и новые возможности языка.

Оценка Relax и AsmX

Relax:

  • Плюсы:

    • Автор пытается создать свой язык программирования на C++.

    • Написан на C++.

  • Минусы:

    • Отсутствие технической документации.

    • Сложно понять, что можно писать на Relax.

AsmX:

  • Плюсы:

    • Доступна техническая документация.

    • Установку и использование легко понять благодаря мануалам.

  • Минусы:

    • Написан на NodeJS.

Таблицы сравнения

Сравнение синтаксиса комментариев в различных ассемблерах

Для удобства чтения и сопровождения кода, ассемблеры предоставляют возможность добавлять комментарии — текст, который игнорируется компилятором. Рассмотрим, как это делается в разных ассемблерах:

GAS

NASM

ReLax

AsmX

AsmX G2

#

;

;

#

;;

Как видно из таблицы, большинство ассемблеров используют символ # или ; для обозначения комментариев. Однако AsmX G2 использует два символа ;; для комментариев.

Сравнение наличия и определения точки входа

Точка входа — это место в программе, с которого начинается её выполнение. В различных ассемблерах подход к определению точки входа может различаться. Рассмотрим, как это реализовано в каждом из ассемблеров.

GAS

Нужно определять вручную (например, main)

NASM

Нужно определять вручную (например, _start)

ReLax

MainClass.Main

AsmX

Не имеет явной точки входа

AsmX G2

main

В GAS и NASM разработчику необходимо явно указать точку входа, что дает гибкость, но требует дополнительной настройки. ReLax использует объектно‑ориентированный подход, где точка входа — это метод MainClass.Main. AsmX, в свою очередь, не имеет явно определенной точки входа, что может быть необычно для традиционных ассемблеров. AsmX G2 возвращается к более традиционному подходу, используя main как точку входа, что может облегчить переход для разработчиков, привыкших к C‑подобным языкам.

Наличие инструкций SIMD в разных ассемблерах

Теперь давайте посмотрим, как обстоят дела с поддержкой SIMD‑инструкций (Single Instruction, Multiple Data) в разных ассемблерах. Эти инструкции играют важную роль в высокопроизводительных вычислениях, например, в мультимедийных приложениях или обработке больших массивов данных. В таблице ниже представлены поддерживаемые наборы SIMD‑инструкций, такие как MMX, AVX, SSE и другие.

Assembler

MMX

AVX

SSE

AVX2

SSE2

SSE3

SSE4

AVX-512

SSE4.1

SSE4.2

GAS

+

+

+

+

+

+

+

+

+

+

NASM

+

+

+

+

+

+

+

+

-

-

ReLax

-

-

-

-

-

-

-

-

-

-

AsmX

-

-

-

-

-

-

-

-

-

-

AsmX G2

+

-

+

-

-

-

-

-

-

-

  • + означает, что ассемблер поддерживает соответствующий набор инструкций SIMD.

  • - означает, что ассемблер не поддерживает соответствующий набор инструкций SIMD.

Анализ поддержки SIMD-инструкций

  • GAS и NASM показывают полную или почти полную поддержку современных SIMD инструкций, что делает их предпочтительными для разработки высокопроизводительных приложений. Однако, NASM не поддерживает SSE4.1 и SSE4.2, что может быть ограничением для некоторых специфических задач.

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

  • AsmX G2 поддерживает базовые MMX и SSE, что может быть достаточно для некоторых задач, но отсутствие поддержки более новых инструкций, таких как AVX или AVX-512, может ограничить его использование в современных вычислениях.

Итоги

Если вам нужны мощные SIMD‑инструкции для работы с мультимедийными данными или высокопроизводительными вычислениями, GAS и NASM — это ваши лучшие друзья. GAS лидирует по поддержке SIMD, включая самые современные наборы инструкций, такие как AVX-512. NASM также хорош, но с небольшими ограничениями. ReLax и AsmX не подходят для программ, требующих инструкций SIMD. AsmX G2 может быть использован для программ, требующих только MMX и SSE.

Важно отметить, что поддержка инструкций SIMD может зависеть от конкретной версии ассемблера и целевой платформы. Перед использованием SIMD инструкций, рекомендуется ознакомиться с документацией выбранного ассемблера.

Пример кода для вычисления квадрата числа

square(x) = x^2

Для сравнения я написал простую программу на разных ассемблерах: вычисление квадрата числа. Это не «Hello, World!» (он слишком прост), но и не факториал (он слишком сложен для первого знакомства с ассемблером).

Программа на разных ассемблерах

GAS:

.section .data
number: .int 500
result: .int 0

.section .text
.global main

main:
    movl number, %eax   # Загрузить число в регистр eax
    imull %eax, %eax    # Вычислить квадрат числа
    movl %eax, result   # Сохранить результат в переменную result

    # Завершение программы
    movl $1, %eax       # Системный вызов для выхода
    xorl %ebx, %ebx     # Код возврата 0
    int $0x80           # Вызов ядра

NASM:

section .data
number dd 500
result dd 0

section .text
global _start

_start:
    mov eax, [number]   ; Загрузить число в регистр eax
    imul eax, eax       ; Вычислить квадрат числа
    mov [result], eax   ; Сохранить результат в переменную result

    ; Завершение программы
    mov eax, 1          ; Системный вызов для выхода
    xor ebx, ebx        ; Код возврата 0
    int 0x80            ; Вызов ядра

AsmX:

@mul 500 500          # Вычислить квадрат числа

AsmX G2:

@function main {    ;; Точка входа C/C++
    @mul 500 500    ;; Вычислить квадрат числа
}

Бенчмарк: Вычисление квадрата числа

Я решил посмотреть, как ведут себя эти ассемблеры с точки зрения производительности и потребления памяти, написав простую программу для вычисления квадрата числа. Для этого я запустил программы и замерил время выполнения и использование памяти.

Assembler

memory

min

avg

max

GAS

1024kb

0.38s

0.4s

0.587s

NASM

256kb

0.36s

0.38s

0.505s

AsmX

?????

0.17s

0.21s

0.262s

AsmX G2

?????

0.15s

0.19s

0.259s

К сожалению, провести бенчмарки для Relax оказалось невозможным из‑за отсутствия возможности запуска.

Примечание: Бенчмарки носят ориентировочный характер и могут варьироваться в зависимости от аппаратного обеспечения и конфигурации системы.

Итоги

  • GAS и NASM — это классические ассемблеры, прекрасно подходящие для работы с языками программирования C/C++ и другими.

  • AsmX — интересный проект, который может быть полезен для бэкенд и фронтенд разработчиков, благодаря использованию NodeJS.

  • NASM показал себя более экономичным в плане потребления памяти по сравнению с GAS.

  • AsmX G2 имеет синтаксис, напоминающий смесь C++ и ассемблера.

  • Бенчмарки удивили нас, демонстрируя высокую производительность AsmX и AsmX G2. Однако, из‑за отсутствия информации о памяти для этих ассемблеров, сравнение не может быть полным.

Спасибо за внимание! Надеюсь, статья была полезной и интересной. Пишите в комментариях о вашем опыте работы с ассемблерами и какие ещё малоизвестные инструменты стоит рассмотреть.

Комментарии (15)


  1. Arenoros
    13.09.2024 14:33
    +2

    мне больше всего fasm нравился, когда в универе нужно было что то писать, в проде ни когда не приходилось что то писать чисто на asm


  1. Number571
    13.09.2024 14:33
    +3

    AsmX Foundation реализовали довольно необычную систему, которая больше похожа на JIT‑компилятор, но не является классической виртуальной машиной. Впервые я сталкиваюсь с подобным подходом, и это можно считать своего рода ноу‑хау

    Ассемблер на интерпретаторе JS-кода - это и вправду ноу-хау, потому что смысла в этом = 0.

    Ассемблер — это низкоуровневый язык программирования, который напрямую взаимодействует с аппаратным обеспечением.

    AsmX подходит под данное определение? Вопрос конечно риторический. С учётом того, как автор данной статьи восхищается AsmX - не удивлюсь, что он есть также и автор данного ассемблера. Более подробно про AsmX можно почитать в статье: Обзор языка программирования AsmX.


    1. Alex679 Автор
      13.09.2024 14:33

      Спасибо за ваш комментарий!
      Вы абсолютно правы, что традиционный ассемблер - это низкоуровневый язык, напрямую взаимодействующий с аппаратным обеспечением.

      AsmX действительно не является классическим ассемблером в привычном понимании этого термина, так как он не взаимодействует напрямую с аппаратным обеспечением. Это ближе к концепции высокоуровневой абстракции или виртуальной платформы, где ассемблерный синтаксис используется для управления кодом в окружении, которое не является "железом" в традиционном смысле. Однако, это не означает, что в этом подходе нет своего рода уникальности или инновации (или даже ноу-хау), особенно если он позволяет решать задачи, которые стандартные виртуальные машины или JIT-компиляторы не закрывают.

      Что касается ReLax, я не сказал бы, что он меня "не смутил". Это действительно виртуальная машина, и она, как вы правильно заметили, не является ассемблером в классическом понимании. Однако, оба этих проекта, насколько я понимаю, предлагают свои решения для специфичных задач, и здесь стоит корректно различать архитектурные подходы, которые они используют, хотя и позиционирование некоторых терминов может быть спорным.

      Оба проекта, AsmX и ReLax, хоть и используют терминологию ассемблера, не являются классическими ассемблерами в традиционном понимании этого слова. Они скорее представляют собой высокоуровневые абстракции.

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


      1. SerafimArts
        13.09.2024 14:33

        Однако, это не означает, что в этом подходе нет своего рода уникальности или инновации (или даже ноу-хау), особенно если он позволяет решать задачи, которые стандартные виртуальные машины или JIT-компиляторы не закрывают.

        И какие задачи он позволяет решать, если даже обычный высокоуровневый JS будет быстрее и читабельнее?

        Извиняюсь за циничность, но почему в статью попала эта детсадовская поделка уровня BolgenOS (а учитывая код - никак иначе это назвать нельзя, ну блин, парсер на регулярках, без построения AST, использующий самый грубый и медленный алгоритм разбора -- каждый шаг матчить новое значение -- это даже не смешно), а не, например, LLVM IR, который на выходе может выдавать тот же бинарь под веб или любую существующую платформу. Или почему не WASM, который на порядки быстрее и эффективнее этой поделки? Или почему не AssemblyScript? Или почему... А я напоминаю, что перечисляю только ассемблерные языки на которых можно написать что-то под веб, которые в разы популярнее (28 звёздочек у этого AsmX с 1 контрибьютером, против 3к+ звёздочек только у одной репы спеки WASM, и 7.5к у транспайлера из С/С++), производительнее и качественнее.

        Да миллион же всяких более популярных решений. Тот же C# во что собирается? Правильно, в CLR, который фактически является таким же высокоуровневым ассемблером и поддерживает JIT, не?

        Какое это блин "ноу-хау"? Это просто шутка, которая слишком сильно затянулась, раз кто-то это пытается оценить серьёзно.

        Поддерживаю @Number571, выглядит всё так, что вы и есть автор этой поделки, иначе я даже не знаю как ещё объяснить подобное...


        1. Alex679 Автор
          13.09.2024 14:33

          Спасибо за ваш комментарий! Ваше мнение весьма ценно.
          Благодарю за ваше мнение и за то, что поделились информацией о различных ассемблерных языках и технологиях, таких как WASM, AssemblyScript и LLVM IR.

          Сравнивать AsmX с такими проектами, как LLVM IR, не совсем корректно. Это как сравнивать самодельный деревянный велосипед с гоночным автомобилем. Оба средства передвижения, но классы разные.

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

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

          Не исключено, что AsmX был создан школьником. Это не необходимо оценивать как нечто плохое или незначительное. Наоборот, это может быть примером интереса и увлеченности молодого человека.

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


  1. SIISII
    13.09.2024 14:33

    В данной статье мы не будем рассматривать ARM, AVR и другие микроконтроллерные архитектуры, а сосредоточимся исключительно на компьютерных ассемблерах.

    В порядке придирок.

    ARM, AVR -- это, конечно, архитектуры, но не обязательно микроконтроллерные, если говорить об ARM (есть и ПК, и полноценные серверы, да и смартфоны не на микроконтроллерах реализованы). "Персоналочная" архитектура, неофициально обозначаемая как x86 (официально -- IA-32 для 32-разрядных процессоров и AMD64 / Intel64 для 64-разрядных) -- это тоже архитектура (и, в принципе, может быть и микроконтроллерной: технических препятствий нет). Архитектура определяет, как "выглядит" процессор (или вычислительная система в целом) с точки зрения программиста -- в первую очередь, какова его система команд. Эта тема в статье затрагивается, но весьма несильно, что неудивительно: для глубокого погружения в систему команд даже IA-32, без всего, что появилось в 64-разрядных процессорах, потребуется толстенный талмуд.

    Но в целом автор рассуждает не о системе команд, а о ассемблерах в узком смысле слова -- о трансляторах языка ассемблера. В спойлере "Программы на разных ассемблерах" можно увидеть примеры одного и того же кода для разных трансляторов.

    По сути, эта статья даёт обзор синтаксиса нескольких трансляторов для одного исходного языка, и её название формально является совершенно корректным. Но реальное употребление слова "ассемблер" в жизни куда шире и даже, скорей, обозначает именно язык, а не транслятор (более того, нередко встречается словосочетание "компилятор ассемблера", что не очень-то правильно). Как по мне, лучше б название было изменить на что-нибудь вроде "Знакомимся с трансляторами ассемблера для ПК" -- тогда сразу станет ясно, о чём пойдёт речь.


    1. Alex679 Автор
      13.09.2024 14:33

      Спасибо за ваш комментарий! Спасибо за вашу идею, критику!


  1. tolich_the_shadow
    13.09.2024 14:33

    Это не трансляторы ассемблера, а ассемблеры. Ассемблер - не язык, а транслятор мнемокода.


    1. SIISII
      13.09.2024 14:33

      Я выше уже откомментировал название статьи. Раньше там стояло просто "ассемблеры" и формально это было корректно ("ассемблер" -- транслятор, а транслируемый им язык -- "язык ассемблера", assembly language). Но в реальном использовании этого слова под ассемблером почти всегда понимают именно язык, а отнюдь не транслятор. Явное упоминание последнего в названии статьи сразу говорит, что речь пойдёт именно о трансляторах, а не о языке как таковом. Кстати, термин "мнемокод", кажется, давным-давно уже не используется...


  1. andy_p
    13.09.2024 14:33

    Было бы интересно почитать про сравнение дисассемблеров.


    1. Yuri0128
      13.09.2024 14:33

      С AsmX? Или с Relax? Ну да, автор - есть тема новой статьи!


  1. Yuri0128
    13.09.2024 14:33

    У меня статья вызвала некоторое недоумение:

    ну сравнивать нечто типа "виртуального assembly language" с реальным несколько писец как некорректно;

    сам уровень статьи (ну песочница же ведь);

    некоторые заявления автора как в статье так и в комментах, причем они иногда не коррелируют;

    ну и вишенка - когда-то давным давно я писал на assembly language c компиляцией TASM Borland - так размер в памяти был как-то сильно много меньше при сильно более сложной программе (а для умножения в 3 строки - там наверное до пары десятков байт) и время исполнения было ну буквально единицы тактов. Откудова у автора взялося 256KB на NASM-е? То ли он оптимизацию не делал то еще что-то. И откудова 0,38 секунды времени исполнения 6 строчек в assembly language? Может я что-то не доганяю. Ведь вся программка влезет в кэш первого уровня - там просто моментом все исполнится. Снова я чего-то не догоняю... Старею видать...


    1. SerafimArts
      13.09.2024 14:33

      А то что псевдо-виртуальная машина, написанная на JS и исполняющаяся в браузере оказалось в 2 раза быстрее этих "6 строчек" - вас не смутила?)


      1. Yuri0128
        13.09.2024 14:33

        Не... Если чиста ассемблер на 6 строчек исполняется 0,38 секунды, то все остальное уже точно не смущает :).... В том числе и JS интерпретатор, который оказалси быстрее...


  1. ivankprod
    13.09.2024 14:33

    Очень странные результаты тестов, скорее всего, некорректные.