Перейти к содержанию
    

AVI-crak

Участник
  • Постов

    367
  • Зарегистрирован

  • Посещение

Репутация

0 Обычный

Информация о AVI-crak

  • Звание
    Местный
    Местный

Контакты

  • Сайт
    Array

Посетители профиля

3 164 просмотра профиля
  1. u64=(u128)u64*u64>>64;

    Нашлось решение. https://godbolt.org/z/G3M64K7nz Для cortex без вставки не обойтись, вообще ни как, чисто физически. #if (defined __ARM_ARCH && __ARM_FEATURE_DSP) Но вставка умудрялась встраиваться народном cortex m3 (тот самый stn32f103), а не должна - там не такой инструкции. Оказалось что подобные длинные и страшные инструкции для cortex - это часть DSP. И встречаются подобные инструкции даже в очень древних архитектурах - и всегда в списках DSP. Однако компания STM, а за ней и все остальные - дружно подложили жирную свинью. Инструкция u64=u32+u32+u32*u32 находится в общем разделе - попробуй догадайся. Для х86-64 ещё веселей - дополняется проверка на существование типа uint128_t, и тогда вся функция выполняется одной инструкцией.
  2. u64=(u128)u64*u64>>64;

    Не хочу запускать рекурсию, просто прочитай ещё раз.
  3. u64=(u128)u64*u64>>64;

    Например ARM выполняет честную арифметику с плавающей точкой, с округлением к нулю. Всё что не влезает в итоговый результат - просто отбрасывается. x86-64 учитывает результат намного глубже, а в части операций производит округление к единице. Если вычислений слишком много - то ошибка накапливается в младших знаках, и её уже становится видно.
  4. u64=(u128)u64*u64>>64;

    Расширенным поиском нашлось очень много материала на данную тему, в том числе очень старые темы. В целом ситуация странная. С очень давних времён была написана функция с ошибкой, которая вошла во все компиляторы без исключения. Создано огромное количество программного обеспечения, и даже аппаратные решения в новых процессорах подогнаны под ошибочные результаты. А всё потому, что если сделать правильно - то отвалится чуть-ли не половина созданного ранее. Дабы сделать плавный переход на верные результаты - практически все компиляторы исключили нативную поддержку чисел выше int64_t. Для того чтобы общество само разобралось чего кому нужно, и чтоб ненужное и глюкавое отвалилось само собой. Плавный переход затянулся на 12 лет. Числа большой разрядности необходимы для программной эмуляции long double : мантисса 64 бита, степень 15 бит, знак 1 бит. Когда-то очень давно для интела появилась аппаратная поддержка double, так вот внутри оно обрабатывалось как long double, и не очень честно. uint64_t mult64toH128(uint64_t val1, uint64_t val2, int32_t* ord) { uint32_t c1c0 = (uint64_t) (val1 & 0xffffffff) * (val2 & 0xffffffff) >> 32; uint64_t a2a1 = (val1 & 0xffffffff) * (val2 >> 32) ; uint64_t b2b1 = (a2a1 & 0xffffffff)+ c1c0 + (val1 >> 32) * (val2 & 0xffffffff); uint64_t c3c2 = (a2a1 >> 32) + (b2b1 >> 32) + (val1 >> 32) * (val2 >> 32) ; *ord += c3c2 >> 63; if((c3c2 >> 63) == 0){ c3c2 <<= 1; c3c2 |= (b2b1 >> 31) & 1; }; return c3c2; } Шланг 16,0,0 делает простой и быстрый код для кортекса М4 и выше. push {r4, lr} umull r12, lr, r2, r0 ldr r4, [sp, #8] umull r12, r0, r3, r0 umaal lr, r12, r2, r1 umaal r0, r12, r3, r1 ldr r1, [r4] lsr.w r3, lr, #31 lsl.w r2, r12, #1 cmp.w r12, #0 add.w r1, r1, r12, lsr #31 itt pl orrpl.w r12, r2, r0, lsr #31 orrpl.w r0, r3, r0, lsl #1 str r1, [r4] mov r1, r12 pop {r4, pc} Команда umaal работает так: u64 = u32+u32+u32*u32, при этом два первых операнда находятся в выходных регистрах. Так вот, GCC отказывается применять эту команду, хотя прекрасно знает о её существовании. А всё потому что очень боится переполнения, и боится не без причины. Дело в том что аппаратная поддержка double выполняется на том-же куске кремния что и простое умножение. Однако если простое умножение ограничено в разрядности, то умное от интел имеет более высокую разрядность и автоматическое округление. Ситуации когда из длинного FFFFF.... получаются нули - не столь уж и редки. Описать ситуации исключения переполнения для double и простого умножения - для GCC оказалось непосильной задачей. А потому там просто пометка - низя. Похоже ждать у моря погоды придётся очень долго. Даже раст отказался от поддержки int128_t - напоролся на те-же грабли.
  5. u64=(u128)u64*u64>>64;

    Пока ещё не обрабатываются, и куча дополнительных условий без обработки. На данный момент у меня борьба с последним битом double. В общем оригинальный православный sscanf(), так-же как ptintf() - содержит в себе полноценную таблицу множителей на все степени двойки. 40килобайт - это от жирной таблицы. У мня есть желание избавится от таблицы, и упаковаться в размер меньше 1 килобайта. Фетиш такой, под старость лет.
  6. u64=(u128)u64*u64>>64;

    К вопросу - зачем всё это нужно. https://godbolt.org/z/8jbzqEeed
  7. u64=(u128)u64*u64>>64;

    Ок, переключился на ARM GCC 9.2.1, вижу umlal, но не понимаю отчего так код разбух. У меня на асме получалось всего 18 команд, и я хотел-бы увидеть то-же от GCC. Использовать вставки на ассемблере в подобных вещах считаю недопустимым, а иным способом ситуацию исправить невозможно. Тогда просто буду ждать у моря погоды, когда оно само исправится.
  8. u64=(u128)u64*u64>>64;

    Как у вас это получилось? Потому как мой вариант компилируется так-же как в онлан сервисе. 20000E6E push {r4, r5, r6, lr} 20000E70 umull r6, r5, r0, r2 20000E74 ldr r4, [sp, #16] 20000E76 umull r0, r6, r0, r3 20000E7A adds r5, r5, r0 20000E7C umull r2, r0, r2, r1 20000E80 umull r3, r1, r3, r1 20000E84 adc.w r6, r6, #0 20000E88 adds r2, r2, r5 20000E8A adc.w r0, r0, #0 20000E8E adds r3, r3, r6 20000E90 adc.w r1, r1, #0 20000E94 adds r0, r0, r3 20000E96 adc.w r1, r1, #0 20000E9A cmp r1, #0 20000E9C blt.n 0x20000ea8 <mult64toH128+58> 20000E9E adds r0, r0, r0 20000EA0 adcs r1, r1 20000EA2 orr.w r0, r0, r2, lsr #31 20000EA6 pop {r4, r5, r6, pc} 20000EA8 ldr r3, [r4, #0] 20000EAA adds r3, #1 20000EAC str r3, [r4, #0] 20000EAE b.n 0x20000ea6 <mult64toH128+56> Эта функция используется вместо встроенной лапши на 6 килобайт.
  9. u64=(u128)u64*u64>>64;

    А так вообще пропало умножение u64=u32*u32. Моя функция "uint64_t mult64toH128(uint64_t val1, uint64_t val2, int32_t* nr)" используется для замены встроенной функции умножения чисел long double. Там по дефолту такая дикая ересь добавляется на 6 килобайт, и всё это тормозит как стадо егэшников на экзамене. Логика умножения long double простая как тапок: u64=(u128)u64*u64>>64;. Если число получается больше или равным (1<<63) - тогда к сумме степеней (двоичное со знаком) первого и второго числа добавляется единица. В оригинале там степень использует число без знака с диким легаси в обработке, мне такого не надо. Полное описание в первом сообщении, с картинками.😁
  10. u64=(u128)u64*u64>>64;

    Вот о чём я говорил чуть выше "((uint32_t) (val2 >> 32))" - val2 в этом преобразовании уже имеет размер uint32_t, соответственно результат ноль, и сокращение одной операции умножения. Операций умножения должно быть 4, иначе фигня получится. Тут очень просто, все биты первого числа перемножаются на все биты второго, со смещением и последующим сложением. Столбиком (ненавижу это слово).
  11. u64=(u128)u64*u64>>64;

    Очень просто, для него все операции имеют входные и выходные операнды размером с регистр. Нужно дополнительно указывать варианты событий с иным результатом. В моём случае это дополнительное (uint64_t), без него будет выполнено обычное умножение u32=u32*u32.
  12. u64=(u128)u64*u64>>64;

    Помогите кто чем может, уже вторую неделю головой об стену, и каждый раз ничего хорошего не получается. От этой функции зависит общая скорость. Для того чтобы меньше путаться, я дал имена переменным согласно позиции в числе uint128. uint64_t mult64toH128(uint64_t val1, uint64_t val2, int32_t* nr) { uint64_t c1c0 = (uint64_t) (val1 & 0xffffffff) * (val2 & 0xffffffff); uint64_t a2a1 = (uint64_t) (val1 & 0xffffffff) * (val2 >> 32) + (c1c0 >> 32); uint64_t b2b1 = (uint64_t) (val1 >> 32) * (val2 & 0xffffffff) + (a2a1 & 0xffffffff); uint64_t c3c2 = (uint64_t) (val1 >> 32) * (val2 >> 32) + (a2a1 >> 32); c3c2 = (uint64_t) c3c2 + (b2b1 >> 32); if((c3c2 >> 63) == 0){ c3c2 <<= 1; c3c2 |= (b2b1 >> 31) & 1; }else *nr += 1; return c3c2; } Эта функция выдаёт примерно такой код (на ARM): mult64toH128: Топорное использование umull - умножение без накопления u64=u32*u32, и только потом добавляет u32. ARM и другие архитектуры имеют команды u64 +=u32*u32 (UMLAL для ARM). В этом случае например "(c1c0 >> 32)" нужно поместить в "a2a1", или в наглую использовать "(c1c0 >> 32)" по месту создания и очистить свободный регистр. Я считаю что команда записи нуля и умножение с накоплением дают в сумме меньший код. На чистой асме у меня получилось написать компактно (18 команд), тесты на скорость показали ускорение. Но заставить GCC использовать "UMLAL" у меня не получается. Встроенные функции дают невообразимо длинную лапшу, использовать нельзя.
  13. Время свободной шины BUSTURN - FSMC целиком блокируется на несколько тактов, линия данных отключается, но емкости дорожек и контактов достаточно для удержания уровня. в режиме NAND возможно аппаратное управление контактом дисплея "команда". Надо просто добавить немного логики.
  14. Хочу знать как вам удалось приобрести сиё чудо, и главное - где???
×
×
  • Создать...