199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
1 | |
Арифметические операции над небольшими целыми числами в процессоре SPARC11.09.2016, 14:11. Просмотров 2995. Ответов 57
Метки нет Все метки)
(
Здравствуйте, форумчане!
Есть ли среди вас знатоки архитектуры SPARC? Если да, то просветите меня, пожалуйста, по такому вопросу. Как в процессоре SPARC выполняются простые арифметические действия над целыми числами размера меньшего, чем стандартное машинное слово этого процессора (32 или 64 бит)? Допустим, нам надо выполнить какую-то простую операцию над байтом или 16-битным полусловом. Мне хотелось бы понять, каким набором машинных инструкций эта операция над байтом или полусловом длиной в 16 бит будет воплощена? Особенно мне интересно, как осуществляется контроль за переполнением разрядной сетки в этом случае. В случае архитектуры x86 особых вопросов не возникает - как и в любой другой CISC-архитектуре код операции (поле КОП) в команде один и тот же независимо от размера данных, а собственно размер операндов задаётся специальными битами признаков внутри самой команды. Проблемы контроля переполнения данных в x86 (как, впрочем, и в других CISC-архитектурах) тоже нет. Фиксируется переполнение той разрядной сетки, которая указана в поле длины операндов. Т. е. если длина данных равняется одному байту - в регистре флагов фиксируется выход значения за границы одного байта, если операнды представляют собой 16-битные полуслова - фиксируется выход за пределы 16-битного представления целых чисел, если длина операндов равна 32-битному слову, результат должен помещаться в 32 бита (в противном случае будут установлены соответствующие флаги в регистре флагов). То же самое относится к 64-разрядным целым числам. Хотелось бы понять, как те же самые проблемы (хранения небольших целых чисел размера меньшего длины машинного слова, манипулирования этими маленькими числами, контроля над переполнением данных при арифметических операциях с их участием) решаются в архитектуре SPARC. Для примера можно было бы рассмотреть какое-то простое арифметическое действие (например, сложение или вычитание) над двумя числами размером с байт или 16-битное полуслово и привести элементарную программку, которая его выполняет. Был бы очень благодарен тем, кто мне ответит.
0
|
|
11.09.2016, 14:11 | |
Арифметические опреции над целыми числами
Арифметические операции над числами Арифметические операции над числами |
|
1576 / 809 / 146
Регистрация: 13.06.2015
Сообщений: 2,939
|
|
11.09.2016, 17:27 | 2 |
Это делается путём "вырезания" операндов из машинного слова операцией and со сдвигом в младшие разряды, затем проведение нужного действия, контроль битов переполнения (он будут левее "вырезанных" операндов) с их последующим занулением (опять and) и "врезание" результата обратно в машинное слово сдвигом и операцией or.
P.S. С архитектурой SPARC не работал, это просто общие принципы по которым всегда это делают.
1
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
||||||||||||||||||||||||||
05.10.2016, 05:06 [ТС] | 4 | |||||||||||||||||||||||||
Прошу прощение за задержку с ответом, просто был долго в отъезде.
Kukuxumushu, это всё верно и я это понимаю. Это способ работы с машинными словами произвольной длины при условии, что процессор поддерживает работу только со словами одного единственного размера (допустим, 64 бит) и не имеет команд (или режимов) для работы со словами меньшей длины (допустим, 32, 16 или 8 бит). По сути ведь это не что иное, как программная эмуляция работы команд, выполняющих действия над операндами меньшей длины, при отсутствии таких команд в процессоре. Смысл этой эмуляции сводится к тому, что мы обнуляем старшие "лишние" биты в регистрах, хранящих операнды, выполняем арифметическое действие (сложение, вычитание или умножение), а потом проверяем, не появились ли в этих старших, обнулённых ранее битах единицы (выполнить эту проверку можно операцией or). Если единицы появились, значит, произошло переполнение. Но ведь в процессоре Intel, единственном процессоре, ассемблер которого я немного знаю, никакой необходимости в такой имитации нет. Там ведь для каждой арифметической команды или команды побитовой логики предусмотрены различные режимы, позволяющие работать со словами произвольного размера - от байта до 64-битного слова. Допустим, для того чтобы сложить два байта, размещённых в регистрах, нам достаточно выполнить такую инструкцию
Никакой необходимости во всей этой химии с предварительным обнулением старших битов в регистре, хранящем машинное слово избыточной длины, а потом с проверкой, не появились ли единички в этих старших битах после выполнения операции (что позволяет выполнить контроль переполнения), в x86 нет, так как x86 позволяет работать со словами разной длины напрямую. Я и хотел спросить, а как всё это делается в процессоре SPARC.
0
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
07.10.2016, 00:50 [ТС] | 5 |
И всё же очень хотелось бы получить ответ на вопрос, заданный мною в исходном сообщении. Если есть здесь знатоки низкоуровневого программирования процессора SPARC или RISC-процессоров вообще — отзовитесь, пожалуйста.
0
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
07.10.2016, 04:13 [ТС] | 7 |
Т. е. никаких команд, позволяющих работать напрямую с байтами и полусловами в 16 бит, процессор SPARC не имеет и контроль переполнения целочисленных арифметических операций на уровне байт, полуслов и т. п. в нём напрочь отсутствует? Невозможно, допустим, сложить 2 беззнаковых целых числа размером в байт и проверить на аппаратном уровне через флаги переполнения (или что-то другое в этом роде), уместился ли результат в байт или превзошёл его?
Всё это можно сделать только над словами в 32 бит для архитектуры sparc v8 или над словами 32 и 64 бит в случае архитектуры sparc v9? Работу над байтами и 16-битовыми полусловами приходится эмулировать программно через 32 или 64-битные регистры, а потом таким же программным путём (с помощью операций побитовой логики) определять, произошло переполнение байта или полуслова в 16 бит или нет? Это всё действительно так?
0
|
Ушел с форума
![]() 13991 / 7003 / 815
Регистрация: 11.11.2010
Сообщений: 12,598
|
|
07.10.2016, 08:53 | 8 |
JohnyWalker,
а кто мешает поместить данные размером в байт в 32-разрядный регистр и сдвинуть содержимое влево на 24 разряда или данные размером в слово помещенные в 32-разрядный регистр сдвигаются на 16 разрядов. При сложении/вычитании будут взведены/сброшены флаги переноса/переполнения
1
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
07.10.2016, 22:16 [ТС] | 10 |
Mikl___, разумно. Не пришёл в голову сразу этот вариант. А казалось бы, он сойдёт и для случая сложения, и для случая вычитания, как знаковых, так и беззнаковых чисел. Не проверял, но похоже, что это так. И действительно, будут сброшены/взведены флаги, которые можно будет после этого проанализировать каким-нибудь jump'ом. Просто меня в данном случае интересовал несколько иной вопрос — действительно ли в RISC-процессорах отсутствует хоть какая-то возможность выполнения арифметических операций на уровне байт и полуслов в 16 бит и невозможно контролировать на этом уровне переполнение и перенос (то, что программная имитация всего этого возможна, я понимаю, но речь сейчас не о том). Хотел разобраться с этим вопросом на примере SPARC'ов. Вроде это действительно так, но хотелось бы получить чёткий и исчерпывающий ответ.
0
|
Ушел с форума
![]() 13991 / 7003 / 815
Регистрация: 11.11.2010
Сообщений: 12,598
|
|
08.10.2016, 03:47 | 11 |
shmkv,
если 9-ый бит результата = 1, значит произошел перенос, а переполнение это
OF = CF xor SOP1 xor SOP2 xor SRESULT где CF - значение флага переноса, SOP1 - знак 1-ого операнда, SOP2 - знак второго операнда, SRESULT - знак результата. Подробности есть в https://www.cyberforum.ru/asse... 05284.html
2
|
![]() ![]() |
|
21.11.2016, 19:34 | 13 |
Общий принцип я расписывал в Int vs int fast - как проверить производительность?
На sparc начиная с v9 имеется, грубо говоря, единственная операция сложения - add. Она работает на 64-битными операндами (коими являются регистр либо 13-битный литерал). Поэтому ответ на вопрос кроется не столько в операции сложения, сколько в том месте, кто формирует операнды для операции сложения. Если нужен какой-то контроль за битами переполнения, то нужно следит за тем, чтобы в старших битах было сформировано нужное значение. Если этот контроль не нужен, то значения в старших битах безразличны (поскольку они не влияю на младшие биты результата) Добавлено через 2 минуты В простейшем случае аргументы сложения лежат в памяти. Для их чтения используется операция ldh. Она читает 16 бит из памяти и кладёт их в младшие 16 бит регистра. А оставшиеся 48 бит заполняет нулями. Если я ничего не путаю, то ест ещё операция ldsh вроде бы, которая старшие 48 бит заполняет знаком (значением 15-го бита, если считать с нуля) Добавлено через 4 минуты Что-то я немного протупил. тут ведь вопрос касался о переполнении в 8-м и 16-м бите, а не в старшем. Я с ходу не могу сказать, завтра систему команд почитаю, если доберусь до неё
1
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
||||||
21.11.2016, 20:28 [ТС] | 14 | |||||
Давайте, было бы очень интересно! Посмотрите, что об этом пишут в учебниках и справочниках по SPARC.
А заодно, если Вы не против, можно было бы поступить так. Вы бы написали маленькую программу на ассемблере SPARC, проводящую подобную манипуляцию над числами и анализирующую случай переполнения, а я бы написал какую-нибудь идиотскую программу на C, выполняющую простейшие операции над целыми числами различного размера, со знаком и без. Что-то в духе
0
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
22.11.2016, 05:59 [ТС] | 16 |
Evg, а цель этого эксперимента очень проста. Просто мне хочется посмотреть на код, который породит компилятор для всех этих манипуляций над целыми числами разного размера. Понятно, что код компилятора над числами будет достаточно рационален — никакой глупости заставлять делать процессор он не будет, всё же компиляторы пишут не идиоты, а люди, хорошо разбирающиеся в машине)) Т. е. этот код будет учитывать все возможности данного процессора: он не будет заставлять его выполнять ненужные, бесполезные, лишние операции - лишние пересылки данных, преобразования типов, бесполезные сравнения, когда всё это можно сделать более простым и коротким путём с меньшим числом команд, но компилятор и не сможет заставить процессор сделать невозможное. Если не умеет данный процессор работать с байтами и шестнадцатиразрядными целыми, то компилятор никак его не заставит это делать — все вычисления всё равно будут производиться над 64-битными операндами, операции над байтами и 16-битными целыми будут проводиться через 64-разрядные целые. Т. е. компилятор сделает максимум того, что можно сделать (его код будет не сильно хуже того, что сможет написать в данном случае на ассемблере человек), но не более того. Мне и хочется своими глазами взглянуть на этот код, на этот ассемблерный листинг, он мне очень многое даст для понимания. Вот и вся цель данного эксперимента. А заодно я скомпилирую свою тестовую программу под платформу x86 (с помощью gcc или Visual Studio) и сравню код для платформы Intel с кодом для SPARC. Очень наглядное сравнение возможностей и организации системы команд двух процессоров.
Ну и конечно очень хотелось бы увидеть вручную составленную на ассемблере для SPARC'а демонстрационную программу, иллюстрирующую все эти вещи. Может, в справочниках и учебниках по SPARC'у такие программы и есть — можно было бы просто скопипастить их сюда. Добавлено через 18 минут Да, чистый C в том виде, в каком он описан в стандартах и изложен в учебниках не предполагает ничего, что позволяло бы контролировать переполнение — таких операций там просто нет. Но вот, возможно, есть какие-то нестандартные расширения языка, которые всё же позволяют это делать (в виде ли библиотечных функций, макросов или даже базовых средств самой реализации языка). Хорошо, если такие проверки реализованы через нижний аппаратный уровень машины (допустим, в случае x86 задействуют флаги OF и CF, а в случае SPARC те возможности данного процессора, какие у него для этого есть), а не чисто алгоритмическим путём (в духе, c = a + b; все три числа беззнаковые; раз c < a || c < b , значит, произошло переполнение целого при сложении). Не уверен точно, но вроде я такую возможность некоторых реализаций языка C где-то видел, что-то мне такое вроде бы попадалось. Добавлено через 7 часов 55 минут Вот что удалось разыскать по поводу встроенных в сишный компилятор арифметических действий, выполняющих проверку переполнения https://gcc.gnu.org/onlinedocs... ltins.html. Так и не понял, правда, что представляют собой эти функции. Это обычные библиотечные функции, входящие в glibc, это какие-то сложные макросы, разворачиваемые препроцессором, или же это встроенные в сам компилятор (не в библиотеки и не в .h-файлы!) операции, распознаваемые им и приводящие к генерации кода? Т. е. это не библиотечные функции и не макросы, а именно базовые синтаксические конструкции самого компилятора? Как вы думаете? Интересно, в компиляторе Oracle Developer Studio есть что-то подобное или нет? Или в компиляторе MS Visual Studio? Любопытно, как всё это работает — опирается на инструкции процессора, позволяющие проверить переполнение арифметических операций, например, путём проверки соответствующих флагов (OF и СF в x86), или же всё это воплощено платформо-независимым способом, не опираясь на железо (допустим, путём сравнения между собой операндов и результата)?
0
|
![]() ![]() |
|
22.11.2016, 14:35 | 17 |
В посте #13 я написал, что будет сделано. Но если тебе это так важно, то позже покажу код из-под компилятора
Этого я тоже не понимаю. Чем вручную написанная программа принципиально отличается от ассемблерного кода, рождённого компилятором? Из того, что я видел в жизни - такие вещи всегда реализуются на ассемблерных вставках. Возможно, они накрыты каким-то библиотечным интерфейсом. Я в общем-то не знаком с библиотеками, специализированными для математичеких рассчётов, но если найдёшь какую-нибудь подобную, то на уровне интерфейсов они почти наверняка будут выглядеть одинаково для всех архитектур, но внутри будут иметь собственные кишки Это не функции. По синтаксису они выглядят как функции, но по сути это встроенные в язык операции. Они могли бы вместо синтаксиса функции ввести какую-нибудь закорючку (типа +, -, *, /), но беда в том, что все закорючки уже давно задействованы Я не то чтоб думаю, я точно знаю, что это именно так. Наверняка есть, но выглядят по другому. В природе такого понятия как "чистый Си" не существует. У каждого компилятора есть собственные расширения Для тех архитектур, где есть аппаратная поддержка, там должны использоваться встроенные операции. Для тех, у кого в процессоре таких инструкций нет и для тех архитектур, для которых разработчики компилятора пока это не поддержали - там будет использоваться эмуляция. Собственно, все builtin'ы в gcc построены по такому принципу Я, честно говоря, не совсем понял, в какой версии gcc появились эти builtin'ы. В официальной документации от gcc-6.2 я что-то не нашёл этих описаний. Видимо, это ссылка из самой свежей версии, которая пока ещё в процессе разработки. Т.е. много лет они жили без этого, но по каким-то причинам решили наконец встроить в язык Добавлено через 2 часа 57 минут Исходник: C short a, b, c; void foo (void) { a = b - c; } Код
foo: save %sp, -96, %sp sethi %hi(b), %g1 or %g1, %lo(b), %g1 lduh [%g1], %g1 mov %g1, %g2 sethi %hi(c), %g1 or %g1, %lo(c), %g1 lduh [%g1], %g1 sub %g2, %g1, %g1 mov %g1, %g2 sethi %hi(a), %g1 or %g1, %lo(a), %g1 sth %g2, [%g1] restore jmp %o7+8 nop Код
foo: sethi %hi(b), %g1 lduh [%g1+%lo(b)], %g2 sethi %hi(c), %g1 lduh [%g1+%lo(c)], %g1 sub %g2, %g1, %g2 sethi %hi(a), %g1 jmp %o7+8 sth %g2, [%g1+%lo(a)] Код
foo: save %sp,-96,%sp ! block 1 .L17: ! File t.c: ! 1 short a, b, c; ! 2 void foo (void) { a = b - c; } sethi %hi(b),%o0 ldsh [%o0+%lo(b)],%o1 sethi %hi(c),%o0 ldsh [%o0+%lo(c)],%o0 sub %o1,%o0,%o1 sethi %hi(a),%o0 sth %o1,[%o0+%lo(a)] jmp %i7+8 restore ! block 2 .L16: jmp %i7+8 restore Код
foo: /* 000000 2 */ sethi %hi(b),%o5 /* 0x0004 */ sethi %hi(c),%o4 /* 0x0008 */ ldsh [%o5+%lo(b)],%o3 /* 0x000c */ sethi %hi(a),%o2 /* 0x0010 */ ldsh [%o4+%lo(c)],%o5 /* 0x0014 */ sub %o3,%o5,%o1 /* 0x0018 */ retl ! Result = /* 0x001c */ sth %o1,[%o2+%lo(a)] Добавлено через 2 часа 0 минут На sparc'е есть флаги для 32-го и 64-го битов, но нету для 8-го и 16-го. Каких-то специальных операций для эмуляции нет. Т.е. для 8-го и 16-го бита все флаги переноса нужно считать вручную Добавлено через 1 минуту Точнее так: для v8 есть только флаги для 32-го бита (поскольку машина была ещё 32-разрядная). А для v9 есть 32 и 64 (причём 32 сделано для поддержки совместимости с v8)
1
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
22.11.2016, 19:40 [ТС] | 18 |
Evg, большое спасибо за такой полный и исчерпывающий ответ! По поводу этих builtin'ов — вот что сходу удалось нагуглить:
https://news.ycombinator.com/item?id=8766210 https://gcc.gnu.org/gcc-5/changes.html Т. е. они, похоже, появились только в пятой версии gcc. MinGW, основанный на gcc 4.9.3, эти функции не опознаёт и отказывается программу с их участием напрочь компилировать. Я правильно понимаю, что для использования встроенных функций компилятора gcc (builtins), не нужно в исходник программы добавлять никаких заголовочников (файлов *.h)? Добавлено через 31 минуту Посмотрел у себя в Debian'е и с удивлением обнаружил, что там тоже стоит четвёртая версия gcc — gcc 4.9.2, т. е. даже ещё более старый gcc, чем в MinGW под Windows (MinGW использует версию 4.9.3), хотя инсталляция Debian'а свеженькая — 8.6, Jessie, буквально на днях устанавливал. Даже не знаю, как и где эти builtin'ы скомпилировать и посмотреть, как они работают. Добавлено через 20 минут Любопытно, почему разработчики дистрибутивов, будь то MinGW для Windows или даже последние дистрибутивы Linux, так упорно не хотят переходить на новые версии gcc. Или они их считают недостаточно зрелыми и надёжными из-за того, что в них много ошибок, или эти новые версии плохо совместимы с прежними старыми версиями того же компилятора, что порождает трудности при работе с ним, или же этот новый компилятор вводит всякие строгости и ограничения в синтаксисе C и C++, а программисты Linux привыкли к более свободному синтаксису более старых версий gcc, допускающему больше вольностей в объявлениях и преобразованиях типов, и не хотят поэтому переходить на новый gcc. В общем, я даже не знаю, что там на самом деле. У тебя, Evg, какие-то мысли есть по этому поводу? Добавлено через 3 часа 52 минуты Да, по поводу этих встроенных функций с проверкой переполнения... Посмотрел сейчас, есть они в документации на gcc 6.2.0. Вот они: http://gcc.gnu.org/onlinedocs/... eddest=538 . Добавлено через 4 минуты Ну и собственно ответ на мой вопрос, используют ли они возможности аппаратуры процессора для фиксации переполнения или всё делают чисто программным путём. The compiler will attempt to use hardware instructions to implement these built-in functions where possible, like conditional jump on overflow after addition, conditional jump on carry etc. Т. е. всё, как ты мне и написал.
0
|
![]() ![]() |
|
22.11.2016, 19:47 | 19 |
Не по теме: Странно, утром смотрел в документацию от 6-го и ничего не нашёл, а сейчас вижу, что есть. Видимо, при поиске ошибся Да Ну вот собрали люди дистрибутив при помощи gcc-4.9.2, потратили кучу времени на его отладку. А теперь выходит gcc-4.9.3, в котором может быть ничего полезного-то и не было (например, исправили ошибки для какой-нибудь левой архитектуры или поправили документацию). А оно нужно ради этого весь этот процесс начинать по новой? Если речь идёт о переходе с gcc-4 на gcc-5, то такой переход зачастую нетривиален. И не каждая софтина его переживёт. Очень часто при обновлении версии компилятора синтаксис делается более строгим. Т.е. в старых компиляторах допускалось то, что по стандарту должно вызывать ошибку (и при этом в реальности не было критичным), а на новой версии это запрещается. А в софт уже успело проникнуть. Новые версии выдают гораздо больше предупреждений, в результате чего не соберётся софт, в котором предупреждения рассматриваются как ошибки (опция -Werror). Да и зачем людям переходить на новую gcc, если им ни одной из новых свойств не нужно? В итоге при переходе кроме геморроя ничего не получат
1
|
199 / 86 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
22.11.2016, 20:37 [ТС] | 20 |
Ну, в общем я что-то такое и предполагал. Я вот думаю, может, разработчики всех этих компиляторов, и gcc здесь не исключение, со строгостью синтаксиса перегибают палку. Ведь C изначально создавался как язык с не очень строгим синтаксисом. Простой удобный язык с уклоном в сторону машины с кучей вольностей в вопросе приведения типов (и немножко в вопросе объявлений). В этом смысле он прямая противоположность Паскалю. Зачем его уродовать всеми этими строгостями, сообщениями об ошибках со стороны компилятора, warning'ами и пр.? Какой смысл запрещать людям делать то, к чему они привыкли и что им нравится? Раз уж стандарт развивается в такую сторону (хотя какая от этого польза для C?), сделали бы дополнительную опцию компилятора, что-то вроде -strict или -strictstd, и компилятор с включённой этой опцией всё бы запрещал. А без неё бы всё разрешалось, как и прежде. В результате огромное сообщество не самых худших и не самых глупых программистов работает над этим gcc, делает огромную работу, а люди этим пользоваться не хотят. И не пользуются... Уже вышла шестая версия gcc, а основная масса программистов из мира свободного софта сидит на последних версиях четвёртого. Я догадывался, что эти ограничивающие нововведения стандарта вызывают больше проблем. Но это так, мысли вслух...
Добавлено через 16 минут У меня такой ещё вопрос по сути. Можно ли в gcc сделать кросскомпиляцию? Допустим, у меня рабочая платформа x86 или x86_64. Могу ли я при этом получить на выходе компилятора код под SPARC, который на моей машине, естественно, не запустится, но будет вполне работоспособен и будучи скопирован на компьютер с процессором SPARC прекрасно на нём заработает? При этом хотелось бы иметь возможность получать и ассемблерный листинг (с помощью ключа -S), и объектный файл (т. е. файл с расширением .o), и окончательный загрузочный исполняемый модуль. Можно ли это сделать с помощью gcc, идущем в обычном дистрибутиве Linux, не устанавливая никаких дополнительных пакетов, просто указав компилятору в командной строке какую-то опцию? Или всё это несложно сделать, но нужно устанавливать дополнительные специальные пакеты из дистрибутива, позволяющие такую кросскомпиляцию выполнять? Или это всё, если и возможно, то очень сложно, — нужно каким-то нетривиальным образом скачивать и устанавливать дополнительное ПО, а потом его хитрым и сложным образом настраивать? В общем, могу я на своём Intel'е, имея дистрибутив Debian, легко и просто компилировать программы под SPARC? Или это всё, если и возможно, то очень сложно?
0
|
22.11.2016, 20:37 | |
Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь. Написать программу калькулятор, выполняющую арифметические действия над целыми и вещественными числами
Длинная арифметика: арифметические операции над числами Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |