Страницы

Поиск по вопросам

Показаны сообщения с ярлыком компилятор. Показать все сообщения
Показаны сообщения с ярлыком компилятор. Показать все сообщения

воскресенье, 29 марта 2020 г.

Чем отличается компилятор от интерпретатора?

#javascript #компилятор #интерпретатор


JavaScript к какому языку относится? интерпретируемый либо комплируемый?
    


Ответы

Ответ 1



Интерпретатор - программа которая выполняет исходный код по инструкциям(строчно). Компилятор - программа которая анализирует и переводит исходный код в машинный язык программирования и выполняет его.

Ответ 2



JavaScript относится к динамически транслируемым языкам (JIT - Just-In-Time). Т.е. он сначала запускается в интерпретируемом режиме, а потом компилируется в нативный код (т.е. код, исполняемый непосредственно процессором). Вообще разница между компилятором и интерпретатором довольно размыта, но в целом можно считать что основной задачей компилятора является трансляция программы в ассемблер или сразу исполняемый код целевого процессора. Задачей интерпретатора является трансляция в промежуточный код для исполнения виртуальной машиной. Динамические (jit) компиляторы являются некоторой смесью этих двух видов трансляции.

воскресенье, 8 марта 2020 г.

Компилятор для С поддерживающий стандарт С11 для Windows 7

#c #windows_7 #компилятор #c11


Какой компилятор языка С поддерживает стандарт С11 и может быть установлен в операционной
системе Microsoft Windows Домашняя расширенная SP1 ?
    


Ответы

Ответ 1



Компилятор gcc, который портирован под Windows в проекте MinGW-w64. При компиляции нужно указывать флаг, какой конкретно использовать стандарт: gcc -std=c11 Подробнее, про поддерживаемые стандарты gcc можно посмотреть тут: https://gcc.gnu.org/onlinedocs/gcc/Standards.html

понедельник, 24 февраля 2020 г.

Может ли транслятор работать без интерпретатора или компилятора?

#компилятор #интерпретатор #транслятор


Может ли транслятор работать без интерпретатора или компилятора и почему?
    


Ответы

Ответ 1



Если придерживаться самых распространенных определений транслятора, интерпретатора и компилятора, то да. Транслятор - переводчик с какого-то языка на другой язык. Компилятор - переводчик на некий низкоуровневый язык, в форму подлежащую исполнению. Например в машинный код Интерпретатор - штука которая программу сразу исполняет. Например на некоторой виртуальной машине, а виртуальная машина уже на лету компилирует в машинный код. То есть штука, которая нашу программу переводит скажем с паскаля на javascript, это транслятор, но не компилятор. Если конечно наша машина не предназначена для исполнения сразу javascript. Вообще все эти три понятия на практике очень взаимосвязаны и переплетаются.

Ответ 2



Да, может работать. Транслятор - это техническое средство, выполняющее трансляцию программы. То есть переводит текст программы с одного языка на другой, не более. Компилятор - это тот же транслятор, только переводит код с языка высокого уровня на язык низкого уровня. Поскольку компилятор, это транслятор, то первая часть вопроса не имеет смысла. Интерпретатор исполняет код программы, для транслирования он не нужен.

пятница, 14 февраля 2020 г.

Где я могу задать ключи для компиляции в Visual Studio?

#c_sharp #visual_studio #компиляция #компилятор


Прочитал на хабре, что можно уменьшить время компиляции, за счет ключа параллельной
компиляции /MP. В настройках не вижу таких пунктов. Поиск в гугле не дал результатов.
Используется Visual Studio 2017.
UPD. В частности интересует C# под Windows Forms.
    


Ответы

Ответ 1



Параметр /MP служит для ускорения компиляции нативных проектов (C++ или C), в C# проектах этот параметр не поддерживается, да и в целом не нужен. Документация: ссылка.

четверг, 13 февраля 2020 г.

Как создать простой компилятор для простого языка (например, brainfuck)?

#g++ #компилятор #brainfuck #cpp #ubuntu


В общем хочу написать компилятор brainfuck или своего собственного языка (простого).
Как это сделать?   
P.S.
  Интерпретатор (для brainfuck) я осилил сам, как сделать компилятор я даже не догадываюсь.
Желательно найти литературу.    


Ответы

Ответ 1



для brainfuck я когда то сам писал компилятор. Лучше для начала написать траслятор, который переведет код в с/с++/java/любой другой любимый язык. Это очень просто. Вот пример траслятора в Java. После того, как получится написать такое, никто не мешает написать похожее для ассеблера (для fasm или masm). Последней ступенькой будет генерирование сразу исполнимого файла. О том, что нужно знать ассемблер, я думаю вопросов нет. И ещё немного интересного материала - http://habrahabr.ru/blogs/development/113339/

Ответ 2



Есть такая книжка, Marc-André Cournoyer, «How To Create Your Own Freaking Awesome Programming Language» (по ссылке — продается, но ищущий название и слово "pdf" всегда найдет и где взять менее цивлизованно, ЕВПОЧЯ). Показывают основы на пальцах, этакая «Драконья Книга для самых маленьких». Начинают с лексера и парсера, затем интерпретатор, и затем — компилятор под LLVM и, на всякий случай, собственную игрушечную виртуальную машину. Все, правда, на Ruby, но общие идеи от языка не зависят. Так что если есть интерес — можете поискать и посмотреть, рекомендую.

Ответ 3



Было уже. Давайте создадим компилятор

Работа компилятора: формулировка некоторых понятий

#компиляция #компилятор


У меня не совсем качественная подготовка по английскому языку, да и hashcode.ru не
являет сервисом, который помогает переводить что-то, но меня в первую очередь интересует
грамотная формулировка понятий.
Вот есть абзац:

The lexical parser analyses the source code and breaks it up into lexical tokens,
removing on the way all comments and white-spaces between the tokens. The syntax parser
groups the tokens into syntax constructs, and builds an equivalent    internal    abstract
   syntax    data    structure. The semantic analyser walks through the abstract syntax
and determines whether all semantic rules have been obeyed. The  code  generator, 
based  on the abstract  syntax  once again, produces the final equivalent code.

Я его перевёл вот так:

Лексический парсер анализирует исходный код, разбивает его на так называемые лексические
«токены», удаляет все комментарии и лишние пробелы. Синтаксический парсер группирует
эти полученные «токены» в синтаксические конструкции и создает эквивалентные внутренние
абстрактные синтаксические структуры данных. Семантический анализ снова «проходит»
по абстрактным синтаксическим структурам и проверяет соблюдение всех правил. Генератор
кода, снова основываясь на абстрактных синтаксических структурах, создаёт конечный
эквивалентный код.

У меня вопрос вот: что значит в данном контексте "abstract syntax", это одно и то
же, что и  "an equivalent   internal   abstract   syntax   data   structure", т.е.
это синонимы и что это означает в реальности?    


Ответы

Ответ 1



Я бы сказал, что здесь понятия abstract syntax и abstract syntax data structure коррелируют, однако, обозначают разные вещи. Abstract syntax - некое описание используемого парсером грамматики (которое, как пример, может быть задано в стандартных LL, LR формах, или, например, в форме БНФ). Abstract syntax data structure - структура данных, необходимая для поддержания набора правил грамматики. Примером такой структуры может являться тривиальное дерево разбора, разумеется, что в случае более сложных парсеров, сложность используемых структур возрастает. Если интересно, то могу порекомендовать неплохой референс по теории компиляторов вообще, там можно найти большое количество примеров и более формальные определения соответсвующих понятий.

среда, 29 января 2020 г.

Сравнение возможностей компиляторов MASM и FASM [закрыт]

#ассемблер #компилятор #masm32 #fasm


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 4 года назад.
                                                                                
           
                
        
Подскажите, пожалуйста, что можно сделать на MASM и нельзя (или очень трудно) на
FASM и наоборот.     


Ответы

Ответ 1



FASM предоставляет более развитую систему макросов, что облегчает восприятие кода и ускоряет процесс разработки. Хорошая статья по сабжу: http://www.insidepro.com/kk/108/108r.shtml

Ответ 2



Скажем так, MASM - это компилятор от крупной и вполне себе так серьезной фирмы Microsoft, а FASM - это компилятор, созданный энтузиастом, причем, насколько я знаю, только одним( и причем на Assembler`е ) =) Думаю, это о многом говорит вам. Но не подумайте, что я против энтузиазма, я наоборот за него - у энтузиастов порой получается все сделать даже лучше и качественнее, потому что они, как правило, одержимы не прибылью, как крупные компании, а скорее желанием получать удовольствие от своего дела. Но именно тут суть в том, что FASM был создан ОДНИМ человеком, что, в общем-то сложно, а MASM - подразделением Microsoft.

вторник, 28 января 2020 г.

Почему ОТКРЫТАЯ функция из родительского класса становится недоступной в ОТКРЫТО унаследованном классе

#cpp #qt #gcc #компилятор #стандарт


если в производном классе имеется функция с тем же именем, но различной сигнатурой?

Пример.
Создаем класс TwoStageMap, открыто унаследованного от QMap:

template>
class TwoStageMap : public QMap
{
public:
    void insert(const F &fkey, const S &skey, const T &value1); //TODO: return an
iterator of what?

    void insert(const QPair &pair, const T &value1);

    T value(const F &fkey, const S &skey);

    T value(const QPair &pair);

};


При попытки обратиться к value() с сигнатурой из производного класса (TwoStageMap)
все прекрасно работает:

TwoStageMap> tsMap;

tsMap.insert(31, "October", "Halloween");
tsMap.insert(31, "December", "New Year's Eve");
tsMap.insert(25, "December", "Xmas");
tsMap.insert({25, "October"}, "Canna");

qDebug() << "Day1:" << tsMap.value1(31, "December");


, но как только попытаемся вызвать value() самого QMap (чтобы получить внутренний
контейнер под первым ключем), tsMap.value(31);, то тут же получаем ошибку:


  no matching member function for call to 'value'


При переименовании функции, например, в value1() проблема исчезает.
Вопрос - это ошибка компилятора или стандарт языка?
Компирятор gcc x86 64bit



Реализация класса, если кто хочет его использовать/попробовать:

template
void TwoStageMap::insert(const F &fkey, const S &skey, const T &value)
{
    Cont innerMap;
        if (QMap::contains(fkey)) {
            innerMap = QMap::value(fkey);
        }

    innerMap.insert(skey, value);
    QMap::insert(fkey, innerMap);

    //TODO: return an iterator of what?
}

template
void TwoStageMap::insert(const QPair &pair, const T &value)
{
    return insert(pair.first, pair.second, value);
}

template
T TwoStageMap::value(const F &fkey, const S &skey)
{
    auto innerMap = QMap::value(fkey);
    return innerMap.value(skey);
}

template
T TwoStageMap::value(const QPair &pair)
{
    return value1(pair.first, pair.second);
}




PS 
В примере Вызов функции_члена шаблонного базового класса из функции производного
шаблонного класса ситуация с невидимостью неквалифицированного имени функции шаблонного
родительского класса, с этим, как раз, вопросов нет, у меня, как можно видеть, идет
обращение через QMap::, здесь же немного другой случай, а именно, вопрос в
том, что мешает компилятору распознать перегружанную функцию с другой сигнатурой, используя
т.н. "искажение имен"? Если это стандарт языка, то вопрос, скорее к Комитету
    


Ответы

Ответ 1



При вызове метода класса по имени без квалификатора tsMap.value(31); для построения списка перегрузок будет осуществлен поиск имени без квалификатора в области видимости класса TwoStageMap согласно спецификации обращения к членам класса: 6.4.5 Class member access [basic.lookup.classref] 2 If the id-expression in a class member access (8.5.1.5) is an unqualified-id, and the type of the object expression is of a class type C , the unqualified-id is looked up in the scope of class C. Этот поиск будет завершен при нахождении имени value в классе TwoStageMap, так как поиск имени без квалификатора должен завершаться сразу при нахождении первого объявления, согласно спецификации поиска имени без квалификатора: 6.4.1 Unqualified name lookup [basic.lookup.unqual] 1 In all the cases listed in 6.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. Таким образом метод value из базового класса QMap в списке перегрузок будет отсутствовать. Чтобы это исправить можно Внести этот метод в область видимости класса TwoStageMap добавив using QMap::value; При вызове использовать имя с квалификатором: tsMap.QMap::value(31);

Ответ 2



Если я правильно понял проблему, то решается эта проблема с помощью using. #include #include class Base { public: void func(int _v) { std::cout << __FUNCTION__ << std::endl; } }; class Derived : public Base { public: using Base::func; void func(const std::string &_str) { std::cout << __FUNCTION__ << std::endl; } }; int main(int argc, char *argv[]) { Derived d; d.func("text"); d.func(1); return 0; }

Ответ 3



Не важно какой класс вы напишете. Допустим вы написали такой простой класс: class Your_class { protected: int n{ 3 }; public: int value(int n) const { return n + 3; } }; Если я наследую ваш класс, то я наследую все, что не является закрытым членами вашего класса: class My_class : public Your_class { }; В таком виде мой класс имеет функцию_член и обьект n вашего класса. Но как только я добавлю в класс: class My_class : public Your_class { public: bool value(const std::string& s) { return n == s.length(); } }; Этим я выражаю, что моя value это совсем другая функция(мне не нужен ваш вариант). И экземпляры моего класса не будут иметь возможность пользоваться одноименной функцией базового вашего класса. Для обеспечения этой возможности я смогу написать другую функцию, которая вызовит value вашего класса, или же сделать так, как описан в другом ответе. Точно также, если я добавляю в класс свой обьект n, то экземпляры моего класса будут пользоваться только этим обьектом. Так что, функция=член в производном классе с таким же именем, что и функция_ член в базовом, не является ее перегрузкой, а является ее заменой

Ассемблерный эквивалент определения нового типа?

#компилятор #ассемблер #cpp


class MyClass
{
MyClass();
int a;
int b;
void MyMethod();
};

Всегда мучал вопрос что делает синтаксический анализатор компилятора языка высокого
уровня когда видит такую конструкцию? Записывает описание типа с адресами методов и
размерами/типами переменных в какую-то таблицу внутри файла с программой? 
Что происходит при создании объекта класса в стэке?
MyClass Object;

Из этой таблицы извлекается размеры и типы переменных им назначаются соответствующие
адреса, адрес конструктора помещается в стэк вызовов и далее происходит инициализация
в зависимости от того что написано в конструкторе?
Где можно почитать подробнее про этот процесс?    


Ответы

Ответ 1



В целом, все очень зависит от компилятора. Некоторые умные компиляторы могут даже выбросить сам класс, если в нем нет необходимости. Но общие принципы все таки есть. Начнем Когда компилятор видит определение класса, он просто его разбирает. А вот когда нужно создать экземпляр класса, вот здесь начинается самое интересное. Компилятор, просмотрев описание класса, рассчитывает, сколько нужно под него памяти. В описанном в вопросе классе - минимум 8 байт (здесь и дальше я буду говорить в контексте 32битных платформ x86). По факту - может выделиться больше - например, если класс виртуальный, то ещё от 4 байт для указателя на таблицу виртуальных функций. То есть, в общем виде, new для класса - это просто alloc+memset (а если конструктор не тривиальный, то и вызов конструктора). Для классов без виртуальных методов память обычно выделяется как под структуру с соответствующими полями. Для виртуальных может быть ещё минимум одно поле. А что же такое "методы", они же функции класса? это самые обычные функции, просто у них есть ещё один (хотя никто не запрещает компилятору использовать больше, но обычно это один), который по факту является указателем на выделенную ранее память. В методе этот параметр выглядит как this. Как происходит обращение к полям: Для каждого поля компилятор рассчитывает смещение относительно this. Например, имеем: MyClass m; m.a = 10; m.b = 20; На псевдокоде это так mov [this+0], 10 mov [this+4], 20 смещение +4, потому что размер int равен 4. Но компилятор может заняться выравниванием и по факту второе поле может оказаться по смещению 8. Вызов методов: А такой же как и вызов обычных функций. Только, как я писал выше - добавляем ещё один параметр - указатель на класс. Адрес метода компилятор знает. Вызов виртуальных методов: С ними интереснее. Для этого применяется таблица методов (похоже, что лучше этого пока не придумали. Почитать детальнее можно здесь.) Компилятор по имени функции берет ее индекс в таблице. А когда нужно в коде сделать вызов, то это будет так mov eax, [this+8] ; адрес таблицы методов. mov eax, [eax + номер_метода]; загрузили адрес push параметр push this call[eax] ; вызываем функцию по адресу Но компилятор может схитрить. Если он может определить, какой именно метод нужно вызвать, то он может вставить вызов напрямую. Более того, компилятор может не вставлять даже параметра this, если внутри метода оно не используется. Я думаю, что понятно, что таблицы виртуальных методов создаются по одной на каждый класс, а не на каждый объект. Создание объектов на стеке а здесь ничего особого. В классической реализации "выделить память на стеке" - это просто изменит указатель вершины стека. Так как стек растет сверху вниз, то это вычитание размера с регистра, хранящего вершину стека. В си даже есть такая функция - alloca (в visual studio может называться _alloca), которая работает как malloc, но выделяет на стеке. абстрактные методы Эти методы есть в таблице виртуальных методов, но указывают на специальную функцию, которая отображает сообщение о том, что такие методы нельзя вызывать. всякое странное В результирующем коде обычно уже нет никаких имен методов и полей. Есть только адреса и смещения. И типов также уже нет. А вот если отладчику нужно показать пользователю данные, то он получает от компилятора специальный map файл, где все это расписано. Именно поэтому, если отлаживать релизный код, то отладчик часто не может даже привязать код к бинарному коду - у него просто нет этой информации. А угадать очень сложно. Но иногда компиляторы, особенно если они делают отладочный код, могут добавлять дополнительные поля, что бы проверять, что код не делает ничего страшного. Например добавлять реальный тип объекта и сравнивать его при надобности. А бывает, программист хочет использовать rtti, тут уж нужно подобавлять в каком то виде данных.

Ответ 2



Как минимум, происходят три вещи: На стеке выделяется кусок такого размера, чтобы туда гарантированно влез объект. Где-то запоминается тот факт, что при выходе за пределы блока нужно вызвать деструктор. Управление передаётся конструктору. Никакие размеры и типы внутренних переменных ни из какой таблицы точно не загружаются. Вычисление адресов внутренних переменных происходит во время компиляции кода, который к ним реально обращается. Код, создающий объект, как это ни странно, всего лишь создаёт объект, и не делает ничего другого.

Ответ 3



Можно сгенерировать ассемблерный листинг из исходника и посмотреть. Если коротко, то компилятор выделяет в стеке память, необходимую для размещения объекта, затем вызывает конструктор и передает ему через this адрес ранее выделенной памяти. Как-то так.

пятница, 24 января 2020 г.

Компилятор и интерпретатор. В чем разница?

#компилятор #интерпретатор


Да, да. Это очередной вопрос о разнице между компилятором и интерпретатором ЯП.
Только ответы, которые обычно даются меня не удовлетворяют.

Обычно говорят, компилятор переводит код на каком либо ЯП в исполняемый и уже при
выполнении код сразу выполняется непосредственно процессором. В случае интерпретатора,
в процессе выполнения программы специальная программа интерпретатор считывает исходный
код программы построчно и выполняет его. Шаг за шагом. При этом говорится, что мол
компиляторы не так-то просто еще написать.

Собственно вопрос вот в чем. Если в итоге программа выполняется, то какая в сущности
разница выполняется сразу машинный код, или сначала интерпретатор что-то делает с исходным
кодом, а уже потом выполняет его - в итоге-то процессор выполняет такие же инструкции
машинные. Какая тогда разница? Явно же не в том, что компилятор сохраняет машинный
код в файл заранее, а интерпрететор нет)
Ну допустим я понимаю, что при интерпретации программы с if-ами какая-то часть никогда
может быть и не выполнена, а следовательно не переведена в машинный код. Но, опять
же, наверное не в этом проблема, что сложно перевести все if-ы в машинный код заранее,
то есть скомпилировать код.

Короче, в чем еще отличия и сложности реализации компилятора в отличии от интерпретатора?
    


Ответы

Ответ 1



Отличий два: Интерпретатор не занимается генерацией машинного кода. Вместо этого он вызывает для каждой интерпретируемой команды специальную функцию (являющуюся частью кода интерпретатора), которая и делают всю работу. Компилятор же (как обычный, так и JIT) сначала генерирует машинный код, который затем скармливается процессору для непосредственного исполнения. Компилятор делает всю работу единожды (при сборке программы), а интерпретатор — каждый раз при чтении очередной инструкции. То есть при компилировании накладные расходы выполнения меньше, а следовательно, выше скорость работы конечного кода. Теперь касательно сложности реализации компилятора. Интерпретатор просто берёт и выполняет очередное выражение программы; а как выполнит — тут же забывает про него (разумеется, предварительно сохранив результат). Компилятор же вынужден мыслить более глобально: тут и оптимизации, и межмодульный импорт/экспорт сущностей (ведь программа может состоять из множества файлов исходных кодов). В придачу, компилятор должен придерживаться определённых соглашений и стандартов для взаимодействия с другими инструментами (компоновщиком, к примеру); интерпретатор же является «вещью в себе», делающей всю работу самостоятельно.

Ответ 2



"компилятором и интерпретатором" - не совсем корректный вопрос. Вещи абсолютно разные. Компилятор - преобразует исходный код в исполняемый (машинный, который будет исполняться процессором или какой-нибудь промежуточный, который будет исполняться виртуальной исполняющей средой). Интерпретатор же исполняем исходный код. Видите разницу ? Компилятор преобразует(компилирует), а интерпретатор исполняет(интерпретирует). Возможно вы хотели иначе поставить вопрос: в чем отличие компилируемых яп от интерпретируемых. Разница очевидна: вторые можно исполнять как есть и для это не нужно дополнительных инструментов. Ну в общем попробую подытожить: в конечном счете да, в конечном счете мы имеем изменение состояния процессора, реального физического процессора и к этому сводится исполнение любой программы. Но различия ведь есть: 1) компилируемые языки требуют специальных инструментов (компиляторов), конечный продукт частично защищен и сложно получить исходный код, конечный продукт более компактен , так как состоит из бинарных инструкций, а не из человеко понятных - слов. Конечный продукт исполняется быстрее - ибо он описан инструкция, которые напрямую поддерживаются целевой платформой. 2) интерпретируемый язык исполняется как есть - не требует доп. инструментов для доведения до финального состояния, не защищен - любой может украсть и изменить.... Я понимаю, что написал очевидные вещи. Я просто не понимаю что вы вообще кроме этого хотите услышать. Если про сложность, то компилятор и интерпретатор могут быть одинаково сложны в написании. Если язык сложный, то в любом случае его будет сложно разбирать на лексемы, не важно для какой цели для исполнения или компиляции. Ну лишь тот момент, что перед комплятором всегда будет стоять задача приведения инструкций к какой-то канонической, придуманной не вами форме

суббота, 11 января 2020 г.

Почему объектные файлы не кроссплатформенны?

#c #gcc #компиляция #компилятор


По определению, объектный код - набор инструкций для определённой архитектуры процессора.
Возьмём компилятор GCC. Если компилировать один и тот же код из-под разных платформ(Linux/Windows
etc.), но на одном железе, на выходе получим с виду одинаковые *.o файлы, которые не
будут кросплатформенны. Почему?

Чтобы исключить разность реализаций библиотек, возьмём код без них, что-то вроде:

//main.c
int main()
{
    return 5+3;
}
//main.c EOF

    


Ответы

Ответ 1



По определению, объектный код - набор инструкций для определённой архитектуры процессора. Это правда. Но НЕ вся правда. Кроме собственно инструкций процессора он содержит таблицу внешних ссылок, таблицу перемещаемых адресов и т.д. Эти таблицы используются загрузчиком/линкером при подготовки объектного модуля к выполнению. Ни сами таблицы, ни метод настройки объектного модуля не стандартизованы. В силу этого, объектный модуль, подготовленный для исполнения на одной платформе, не может быть выполнен на другой. Можно было бы попытаться написать не очень сложный транслятор одного формата в другой, но проблема практически неразрешима, т.к. различается не только сам формат объектного модуля, но и формат вызовов внешних функций (структура стека) и даже формат вызова функций ОС. Дело дохлое...

пятница, 10 января 2020 г.

Заработает ли Intel C++ Compiler на AMD процессоре?

#cpp #компилятор


Кто нибудь устанавливал его не на процессоры семейства Intel?
    


Ответы

Ответ 1



AMD и Intel базируются на одной архитектуре x86 или amd64. Хотя в новейших процессорах у них и могут быть разночтения в наборе команд, большая часть оных одинакова. Разумеется никто в здравом уме не станет использовать такие инструкции, которые не позволят запускать компилятор на процессоре конкурента. Это потеря рынка равноценна экономическому самоубийству, учитывая тот факт, что компилятор от Intel, мягко говоря, не на ведущих ролях. Таким образом ответ на вопрос - да, заработает и по другому быть не может. Генерирует ли компилятор от Intel такой код, что он будет быстрее на Intel? Лучше этот вопрос адресовать разработчикам от Intel, но вероятность этого есть, хотя и небольшая.

Ответ 2



ICC включает в свои исполняемые файлы проверку - на интел проце пойдет со всеми оптимизациями, а на не-интеле пойдет вообще без. Если эту проверку вырубить, врубить только допустимые аппаратные расширения типа sse2 есть вероятность, что вполне хороший код получится. Как ее отрубать я где-то видел, посмотрите в генту-вики.

Visual Studio 2012, C#, Compiler Options

#c_sharp #компилятор


Привет. Как скомпилировать проект C# с оптимизацией по размеру, и как скомпилировать
с оптимизацией по скорости? Спасибо
    


Ответы

Ответ 1



Среди ключей компилятора C# есть лишь один, относящийся к оптимизации: /optimize. Таким образом, вы не можете управлять оптимизацией, вы можете лишь включить или выключить её. Это относится не только к Visual Studio 2012, но и к версиям с Visual Studio .NET 2003 вплоть до текущей Visual Studio 2015.

четверг, 9 января 2020 г.

Чем отличаются стандарты c++14 и gnu++14?

#cpp #linux #компилятор #clang


Есть ли существенные отличия между двумя стандартами c++14 и gnu++14 (расширение
GNU)? Имеет ли смысл для компиляции под линуксом придерживаться именно 2-ого варианта?
    


Ответы

Ответ 1



Отличие между c++14 и gnu++14 в том, что в первом случае компилятор старается соответствовать стандарту, а во втором включает различные расширения. Если Вы пишете приложение только под линукс - то можно не задумываться о том, какой именно ключик выбирать. Если же приложение пишется так, что есть небольшая вероятность, что оно будет компилироваться и другими компиляторами (и другие платформы), то лучше указывать std=c++14. Если это приложение просто лабораторная работа, то также лучше использовать std=c++14 - в этом случае больше шансов, что у преподавателя в visual studio оно скомпилируется и можно будет получить свою оценку.

понедельник, 6 января 2020 г.

Проблема с автосвойствами. Ошибка компилятора CS0840

#c_sharp #компилятор


Я использую VS 2012


  error CS0840: Pisos.Rectangle.Area.get должен декларировать тело, так
  как оно не отмечено как абстрактное или внешнее. Автоматически
  реализованные свойства должны определять функции доступа get и set.


class Program
{
    static void Main()
    {
        Rectangle rectangle = new Rectangle(10, 20);
        double a = rectangle.AreaCalc(rectangle.side1, rectangle.side2);
        double b = rectangle.PerimeterCalc(rectangle.side1, rectangle.side2);

        Console.WriteLine(a);
        Console.WriteLine(b);

        Console.ReadKey();
    }
}

class Rectangle
{
    public double side1, side2;

    public Rectangle(double side1, double side2)
    {
        this.side1 = side1;
        this.side2 = side2;
    }

    double Area { get; }
    double Perimeter { get; }

    public double AreaCalc(double side1, double side2)
    {
        double Ar = side1 * side2;
        return Ar;
    }

    public double PerimeterCalc(double side1, double side2)
    {
        double Per = 2 * (side1 + side2);
        return Per;
    }
}

    


Ответы

Ответ 1



В C# версии младше 6 автосвойства только для чтения (содержат только get) не поддерживаются. Варианта два: Используйте VS 2015 (в ней используется компилятор для C# 6). Добавьте private set. Это более правильный вариант, учитывая, что, судя по коду, эти свойства не являются readonly свойствами.

воскресенье, 5 января 2020 г.

gcc naked атрибут

#c #компилятор #gcc #g++


Приветствую всех.
А я к вам снова с вопросом:)
Ковыряю gcc, к своему сожалению обнаружил что для архитектур i386 amd64 не поддерживается
naked атрибут.
Ну и собственно вопросы:

Есть ли возможность обойти это досадное недоразумение (может я проглядел какие-нибудь
специфичные для этих архитектур атрибуты)
Может кто-то знает - есть ли в отсутствии этого атрибута какой-то злой или добрый
умысел со стороны разработчиков? (короче - почему атрибут не поддерживается?)

Большое спасибо за ваши ответы.    


Ответы

Ответ 1



Может быть обычная лень тому причина. Equivalent of __declspec( naked ) in gcc/g++ Naked functions in gcc/g++ - здесь есть обходные пути.

четверг, 2 января 2020 г.

Странные (лишние) инструкции после компиляции

#c_sharp #ассемблер #компиляция #компилятор


Игрался с замером скорости доступа к L1,L2,L3 кэша процессора средствами C# и случайно
наткнулся на странное поведение компилятора (vs2017, х86, со включенной оптимизацией).

Приведу адаптированный кусок кода:

fixed (uint** array = new uint*[256])
{
    var p = array;
    uint iters = 1024;

    for (uint i = 0; i < iters; i++)
        p = (uint**)*p;
}


На выходе, цикл компилится абсолютно корректно:

011E0884  xor         edx,edx              //uint i = 0
011E0886  mov         eax,dword ptr [eax]  //p = (uint**)*p;
011E0888  inc         edx                  //i++
011E0889  cmp         edx,400h             //i < 1024 
011E088F  jb          011E0886


Но если изменить тип переменной iters, начинается магия:

fixed (uint** array = new uint*[256])
{
    var p = array;
    ulong iters = 1024; //  <---  отличие в этой строке

    for (uint i = 0; i < iters; i++)
        p = (uint**)*p;
}


В этом случае цикл компилится вот в такое:

00BA0888  xor         edi,edi              //uint i = 0
00BA088A  mov         esi,dword ptr [esi]  //p = (uint**)*p;
00BA088C  inc         edi                  //i++
00BA088D  xor         eax,eax      // Д
00BA088F  test        eax,eax      // И
00BA0891  ja          00BA089D     // Ч
00BA0893  jb          00BA088A     // Ь
00BA0895  cmp         edi,400h             //i < 1024 
00BA089B  jb          00BA088A


Вопросы:


Почему после изменения типа переменной iters, которая оптимизатором заменяется на
константу, и которой по сути вообще нет, появляются лишние бестолковые инструкции?
В чем сакральный смысл помеченных четырех инструкций? Сперва обнуляем eax. Потом
проверяем, а не ноль ли там случаем. И потом эти джампы, которые, насколько я понимаю,
никогда не сработают... Это баг или фича?

    


Ответы

Ответ 1



Похоже что оптимизатор не осилил убрать смесь каста uint в ulong и последующего сравнения. Каст был из (edi) в (eax, edi), и выглядит как заполнение eax нулём (через xor). 00BA088C inc edi //i++ // каст uint i в ulong. Результат в паре eax, edi 00BA088D xor eax,eax // поразрядное сравнение двух ulong // старший разряд 00BA088F test eax,eax // вместо cmp eax, 0 00BA0891 ja 00BA089D 00BA0893 jb 00BA088A // младший разряд 00BA0895 cmp edi,400h 00BA089B jb 00BA088A Т.е. вроде как оптимизатор мог догадаться, что верхний разряд можно не сравнивать, но не догадался. Оптимизатор x86 старый, не ждите от него слишком многого :)

Как реализована рекурсия на уровне компилятора (если так будет правильно сказать)?

#память #рекурсия #компилятор


Допустим, есть рекурсивная функция, и в ней объявлены локальные переменные. Но, кроме
них, функция еще работает с глобальными переменными (меняет их как-нибудь).
Вопрос: как сохраняются значения локальных и глобальных переменных от одного рекурсивного
вызова к другому и что с ними происходит, так сказать, при "возвращении назад", т.е.
когда рекурсивная функция дошла до конца и передала управление вызвавшему ему? 
Спасибо.    


Ответы

Ответ 1



"На уровне компилятора" никак. Этим интерпретатор занимается... "от одного рекурсивного вызова к другому" глобальные не сохраняются, на то они и глобальные... Если на одном шаге изменяете, на следующем будет уже изменённая. Передаваемые функции параметры сохраняются в стеке, "когда рекурсивная функция дошла до конца" верхние элементы стека передаются "вызвавшему", стек уменьшается. Перенес комментарий Isaev в ответ

Ответ 2



Проще всего с глобальными переменными. Они не сохраняют свои значения между вызовами процедур, на то они и глобальные. То есть все процедуры имеют доступ к одним и теми же глобальным переменным, и если вызванная процедура поменяла значение такой переменной, это видно в вызывающей процедуре. Теперь по поводу локальных переменных (и параметров). Они располагаются в так называемом activation record'е данной функции. Каждая функция знает, явно или неявно, свой activation record, и производит доступ к локальным переменным через него. При начале работы функции создаётся её индивидуальный activation record, поэтому при возвращении из (рекурсивного или нет) вызова функция всё так же работает со своими переменными. При вызове другой функции выполнение переключается на новый activation record, а при возврате активируется прежний. Для языков, произошедших от C, обычное место для activation record'а — стек. Это сделано так, потому что вызванная функция заканчивает обработку строго перед возвратом в вызывающую функцию, и таким образом activation record'ы подчиняются дисциплине «последний пришёл — первый ушёл». Для этого случая activation record обычно называют stack frame. Активизация stack frame'а обычно сводится к (сохранению и) изменению значения в специальном регистре, указывающем на текущий frame. Часто переменные располагаются в регистрах процессора для ускорения доступа. В этом случае при смене activation record'а «живые» переменные всё равно должны быть сохранены в некоторой области памяти, и восстановлены, когда activation record снова активизируется. Для языков, в которых части функции могут пережить её выполнение (сопрограммы, лямбда-функции, continuation passing style и т. п.) activation record не всегда может находится в стеке, и им управляет рантайм-билиотека. Обычное их размещение в этом случае — free store (heap). Некоторые языки (например, Pascal), позволяют вложенные функции, так что внутренняя функция имеет доступ к переменным внешней функции. В этом случае activation record соджержит т. наз. static chain — указатель на activation record внешней функции. Обратите внимание, что «внешняя функция» имеется в виду в лексическом смысле (функция, содержащая внутреннюю функцию, а не вызвавшая её), из-за возможной рекурсии во вложенных функциях. (И имеется в виду activation record самого ближнего экземпляра внешней функции.) Указатель на свой activation record, соответственно, называют dynamic chain. В языках без вложенных функций (наподобие C) static chain не требуется.

вторник, 31 декабря 2019 г.

Приложение создающее другое приложение

#c_sharp #windows #winforms #файлы #компилятор


Приветствую. Не знаю в какую сторону копать и возможно ли такое вообще. Суть задачи
такова : есть приложение написанное на c# в windowsforms. В приложении пользователь
задает некоторые параметры, указывает пути к картинке ect и после выполнения всех настроек
родительское приложение собирает в папке рядом с собой еще одно exe программу но значительно
урезанную согласно установкам пользователя. Можно ли такое сделать если да то в какую
сторону рыть, чем гугол мучать ?
    


Ответы

Ответ 1



Нашел небольшой пример по теме. Если кому будет интересно вот код с динамической компиляцией (exe создается из exe) : using System; using System.CodeDom.Compiler; using System.Collections.Generic; using Microsoft.CSharp; namespace ConsoleCompiler { internal class Program { private static void Main(string[] args) { // Source code для компиляции string source = @" namespace Foo { public class Bar { static void Main(string[] args) { Bar.SayHello(); System.Console.ReadKey(); } public static void SayHello() { System.Console.WriteLine(""Hello World""); } } }"; // Настройки компиляции Dictionary providerOptions = new Dictionary { {"CompilerVersion", "v3.5"} }; CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions); CompilerParameters compilerParams = new CompilerParameters { OutputAssembly = "D:\\Foo.EXE", GenerateExecutable = true }; // Компиляция CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source); // Выводим информацию об ошибках Console.WriteLine("Number of Errors: {0}", results.Errors.Count); foreach (CompilerError err in results.Errors) { Console.WriteLine("ERROR {0}", err.ErrorText); } Console.ReadKey(); } } } Сам вопрос можно закрывать.

Ответ 2



Компиляция нового приложения - довольно тяжелый вариант, из разряда "из пушки по воробьям". Особенно "весело" в такой программе будет экранировать константы в коде... Можно поступить проще. Для начала, можно просто отдавать копировать текущую программу (или лежащую рядом), но с измененным конфигом. Это намного проще всех остальных вариантов. Если же принципиально нужно забить настройки в код, то можно сделать вот так: Готовим библиотеку с точкой входа, которая будет принимать все нужные настройки: public sealed class Program { public string Foo { get; set; } public int Bar { get; set; } public void Run() { // ... } } Создаем выражение на создание объекта с зашитыми настройками: var configExpr = Expression.MemberInit( Expression.New(typeof(Program)), Expression.Bind(typeof(Program).GetProperty("Foo"), Expression.Constant("Hello, world!")), Expression.Bind(typeof(Program).GetProperty("Bar"), Expression.Constant(42))); Создаем программу-загрузчик через Reflection.Emit: var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Loader"), AssemblyBuilderAccess.Save); var moduleBuilder = assemblyBuilder.DefineDynamicModule("Main", "Loader.exe"); var type = moduleBuilder.DefineType("Loader", TypeAttributes.Abstract | TypeAttributes.Sealed); var method = type.DefineMethod("Main", MethodAttributes.Static); Expression.Lambda(Expression.Call(configExpr, "Run", null)).CompileToMethod(method); type.CreateType(); assemblyBuilder.SetEntryPoint(method, PEFileKinds.WindowApplication); assemblyBuilder.Save("Loader.exe"); Как видно, код получился заметно короче чем вариант с использованием CodeDOM. И безопаснее.

вторник, 24 декабря 2019 г.

Как удалить все строковые литералы?

#c #строки #компилятор


Я пишу программу для микроконтроллера, и в нём использую библиотеку для работы с
матрицами Meschach. Библиотека не оптимизирована для работы с микроконтроллерами. В
частности, там есть множество строковых литералов, предназначенных для отправки в стандартный
выводной поток. Он мне не нужен, а литералы занимают слишком много места в исполняемом
файле. Можно ли их убрать? В конфигурационном файле библиотеки я не нашёл макроопределений,
позволяющих отключить стандартный вывод.

Под термином "убрать" я понимаю следующее:


либо убрать все вызовы printf, чтобы литералы стали неиспользуемыми и оптимизатор
их бы выбросил;
либо превратить строки вроде char *a = "aaaa"; в char *a = "";
применить какие-то настройки компилятора, которые могли бы убрать все строки;


Компилятор: IAR Embedded Workbench for ARM 7.50.

Какие есть варианты?
    


Ответы

Ответ 1



sed -e '/printf/ d' ВашаПрограмма.c > Результат.c По комментарию TS: 1) "работает только на Linux" - кто Вам мешает поставить sed под виндой ?! Это же СПО, а не Photoshop за 75 килорублей :-) 2) "удалять также и строковые литералы, а также одновременно работать с множеством файлов." - вот крохотный скрипт для bash: #! /bin/bash # # Скрипт просматривает вс файлы *.c в текущем каталоге и # выполняет следующие действия: # 1. Удаляет все строки, содержащие 'printf' # 2. "Схлопывает все текстовые литералы: "abcd" -> "" # # Что именно делается, задано в файле команд команд sed, # который называется process.txt # # Результат обработки записывается в файл с дополнительным суффиксом new. for prog_file in *.c do echo Обрабатывается файл $prog_file sed -f process.txt $prog_file > $prog_file.new done Файл команд process.txt для sed выглядит таким образом: /printf/ d s/".\+"/""/ Первая команда удаляет строки, содержащие 'printf', а вторая - "схлапывает" текстовые литералы. Со второй командой есть некоторая проблема... Если текстовый литерал: Занимает несколько строк На одной строке расположено несколько литералов Содержит символы \" то всё это работать не будет. Ну я уже не стал так заморачиваться - в рамках регулярных выражений эти задачи решить невозможно.