[devel] re-writing GNU C extensions (part0)
Ivan Zakharyaschev
imz на altlinux.org
Пн Янв 11 11:48:36 MSK 2016
Пишу преобразователь C-кода, который бы переписывал некоторые GNU
extensions. Это делается для того, чтобы такие программы можно было
компилировать компилятором, который не поддерживает GNU extensions.
Например, clang-ом. (Реализуется на библиотеке
<http://hackage.haskell.org/package/language-c>.)
Сейчас хочу предварительно, пока преобразователь ещё не полностью
готов, показать заготовку, т.е. интерфейс этой программы. В следующих
сообщениях будут описываться работающие преобразования.
(Это сообщение сохранено как
[ann0.md](http://hub.darcs.net/imz/cuglify/browse/ann0.md).)
Вопросы:
========
Может быть, кто-нибудь может быстро дать совет, который пригодится в
этой работе:
как встроить её в пересборку Sisyphus? (На данном этапе это ценно
только для тестирования возможностей language-c на реальном коде из
Sisyphus -- не ломается ли на чём-то, т.е. не очень интересно для
широкого круга разработчиков. До проведения такой проверки у меня пока
руки не дошли. Думаю, это реализовать будет несложно и проверить
пересборку по крайней мере избранных пакетов.)
Это поспособствует потенциальной переносимости Sisyphus на платформы
без gcc.
Ещё интересуют любые замеченные ошибки в работе, если вдруг кто-то
будет пробовать.
Описание
========
Разные варианты программы-заготовки живут в файле examples/Process.hs
в разных ветках изменённой language-c:
1. <http://hub.darcs.net/imz/language-c_process_analyze-silently>,
2. <http://hub.darcs.net/imz/language-c_process_analyze-printAST>,
3. <http://hub.darcs.net/imz/language-c_process_analyze-generate>.
[Вот](http://hub.darcs.net/imz/cuglify/browse/run-tests.sh) скриптик
для тестирования Process.hs, который демонстрирует идею:
если запустить программу с теми же опциями, что cpp/gcc, то её
поведение должно быть в некотором смысле эквивалентно cpp/gcc.
(Сравниваются коды возврата и демонстрируется diff между
выводом `gcc -E` и нашего `examples/Process`.)
Что умеют эти варианты examples/Process:
----------------------------------------
1. молча парсит и анализирует входной C-файл и завершается с успехом
или неуспехом, т.е. грубо говоря (не)успех ожидается в тех же случаях,
что у `gcc -c`
2. парсит и анализирует входной C-файл, а потом в случае успеха
анализа печатает C-код по своему внутреннему AST, т.е. должен быть
заменой `cpp`/`gcc -E`
3. парсит и анализирует входной C-файл, а потом (в случае успеха,
конечно) генерирует нечто похожее на C-код по своему внутреннему
семантическому представлению
(Может быть, где-то реальное поведение не соответствует заявленному
выше. Тогда это ошибка, которая требует исправления. Я пока ещё
внимательно не проверял все случаи.)
Как массово проверять разные варианты examples/Process и зачем:
---------------------------------------------------------------
1 интересно встраивать в процесс пересборки так: (если пакет успешно
пересобирается в Sisyphus, то) перед всяким вызовом gcc молча вызывать
Process и отваливаться в случае неуспеха. (Подменять cpp и gcc для
других языков не надо, потому что cpp может натравливаться на разный
код, а Process рассчитан на код, который C семантически.)
2 интересно встроить вместо этапа препроцессинга. (Чтобы проверить
печаталку Process на годность.)
3 в таком виде никуда встраивать не планируется, потому что генератор
просто вываливает код для всех встреченных глобальных семантических
объектов неважно в каком порядке. Это интересно для изучения работы
генератора человеком. Использовать генератор всё равно планируется,
чтобы сгенерировать только кусочки переписанного кода, а не всю
программу.
Примеры работы на ex-nested_undef.c
-----------------------------------
(на других примерах -- см. в конце письма):
### 1.
PROCESSING ex-nested_undef.c...
* with gcc -c:
0
* with ../language-c_process_analyze-silently/examples/Process:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
0
* diff against gcc -E:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
--- /dev/fd/63 2016-01-09 05:35:09.788220253 +0300
+++ /dev/fd/62 2016-01-09 05:35:09.789220179 +0300
@@ -1,12 +0,0 @@
-# 1 "ex-nested_undef.c"
-# 1 "<command-line>"
-# 1 "ex-nested_undef.c"
-void f(int a) {
- int b = 5;
- int g(int x) {
- return b + x + a;
- }
- g(0);
- h(1);
- g(2);
-}
TEST OK on ex-nested_undef.c
### 2.
PROCESSING ex-nested_undef.c...
* with gcc -c:
0
* with ../language-c_process_analyze-printAST/examples/Process:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
void f(int a)
{
int b = 5;
int g(int x)
{
return b + x + a;
}
g(0);
h(1);
g(2);
}
0
* diff against gcc -E:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
--- /dev/fd/63 2016-01-09 05:23:34.198134568 +0300
+++ /dev/fd/62 2016-01-09 05:23:34.199134493 +0300
@@ -1,9 +1,8 @@
-# 1 "ex-nested_undef.c"
-# 1 "<command-line>"
-# 1 "ex-nested_undef.c"
-void f(int a) {
+void f(int a)
+{
int b = 5;
- int g(int x) {
+ int g(int x)
+ {
return b + x + a;
}
g(0);
TEST OK on ex-nested_undef.c
### 3.
PROCESSING ex-nested_undef.c...
* with gcc -c:
0
* with ../language-c_process_analyze-generate/examples/Process:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
void f(int a)
{
int b = 5;
int g(int x)
{
return b + x + a;
}
g(0);
h(1);
g(2);
}
void * __builtin_extract_return_addr(void *);
static const char __FUNCTION__[];
int __builtin___snprintf_chk(char *,
int,
int,
int,
char * const, ...);
int __builtin___vsprintf_chk(char *,
int,
int,
char * const,
va_list);
void * __builtin___memcpy_chk(void *, void * const, int, int);
char * __builtin___stpcpy_chk(char * const, char * const, int);
char * __builtin___strcat_chk(char * const, char * const, int);
char * __builtin___strcpy_chk(char * const, char * const, int);
int __builtin___sprintf_chk(char *, int, int, char * const, ...);
void * __builtin_return_address(unsigned int);
int __builtin_va_arg_pack();
void * __builtin___memmove_chk(void *, void * const, int, int);
int __builtin___vsnprintf_chk(char *,
int,
int,
int,
char * const,
va_list);
char * __builtin___strncat_chk(char * const,
char * const,
int,
int);
char * __builtin___strncpy_chk(char * const,
char * const,
int,
int);
void * __builtin___mempcpy_chk(void *, void * const, int, int);
void * __builtin___memset_chk(void *, int, int, int);
int __builtin_constant_p(__ty_any);
void __builtin_va_start(va_list, void *);
void * __builtin_frame_address(unsigned int);
void __builtin_va_end(va_list);
void * __builtin_alloca(int);
int __builtin_object_size(void *, int);
void __builtin_va_copy(va_list, va_list);
char * __builtin_strncat(char * const, char * const, int);
double __builtin_copysign(double, double);
void * __builtin_memcpy(void *, void * const, int);
double __builtin_fabs(double);
float __builtin_fabsf(float);
long double __builtin_fabsl(long double);
int __builtin_strspn(char * const, char * const);
char * __builtin_strncpy(char * const, char * const, int);
int __builtin_strcmp(char * const, char * const);
int __builtin_strcspn(char * const, char * const);
char * __builtin_strpbrk(char * const, char * const);
void __builtin_prefetch(void * const);
char * __builtin_strchr(char * const, int);
static const char __PRETTY_FUNCTION__[];
double __builtin_huge_val();
int __builtin_clz(unsigned int);
float __builtin_huge_valf();
long double __builtin_huge_vall();
long __builtin_expect(long, long);
double __builtin_inf();
float __builtin_inff();
long double __builtin_infl();
static const char __func__[];
void __builtin_bzero(void *, int);
int __builtin_va_arg_pack_len();
0
* diff against gcc -E:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
--- /dev/fd/63 2016-01-09 05:33:00.477902543 +0300
+++ /dev/fd/62 2016-01-09 05:33:00.477902543 +0300
@@ -1,12 +1,79 @@
-# 1 "ex-nested_undef.c"
-# 1 "<command-line>"
-# 1 "ex-nested_undef.c"
-void f(int a) {
+void f(int a)
+{
int b = 5;
- int g(int x) {
+ int g(int x)
+ {
return b + x + a;
}
g(0);
h(1);
g(2);
}
+void * __builtin_extract_return_addr(void *);
+static const char __FUNCTION__[];
+int __builtin___snprintf_chk(char *,
+ int,
+ int,
+ int,
+ char * const, ...);
+int __builtin___vsprintf_chk(char *,
+ int,
+ int,
+ char * const,
+ va_list);
+void * __builtin___memcpy_chk(void *, void * const, int, int);
+char * __builtin___stpcpy_chk(char * const, char * const, int);
+char * __builtin___strcat_chk(char * const, char * const, int);
+char * __builtin___strcpy_chk(char * const, char * const, int);
+int __builtin___sprintf_chk(char *, int, int, char * const, ...);
+void * __builtin_return_address(unsigned int);
+int __builtin_va_arg_pack();
+void * __builtin___memmove_chk(void *, void * const, int, int);
+int __builtin___vsnprintf_chk(char *,
+ int,
+ int,
+ int,
+ char * const,
+ va_list);
+char * __builtin___strncat_chk(char * const,
+ char * const,
+ int,
+ int);
+char * __builtin___strncpy_chk(char * const,
+ char * const,
+ int,
+ int);
+void * __builtin___mempcpy_chk(void *, void * const, int, int);
+void * __builtin___memset_chk(void *, int, int, int);
+int __builtin_constant_p(__ty_any);
+void __builtin_va_start(va_list, void *);
+void * __builtin_frame_address(unsigned int);
+void __builtin_va_end(va_list);
+void * __builtin_alloca(int);
+int __builtin_object_size(void *, int);
+void __builtin_va_copy(va_list, va_list);
+char * __builtin_strncat(char * const, char * const, int);
+double __builtin_copysign(double, double);
+void * __builtin_memcpy(void *, void * const, int);
+double __builtin_fabs(double);
+float __builtin_fabsf(float);
+long double __builtin_fabsl(long double);
+int __builtin_strspn(char * const, char * const);
+char * __builtin_strncpy(char * const, char * const, int);
+int __builtin_strcmp(char * const, char * const);
+int __builtin_strcspn(char * const, char * const);
+char * __builtin_strpbrk(char * const, char * const);
+void __builtin_prefetch(void * const);
+char * __builtin_strchr(char * const, int);
+static const char __PRETTY_FUNCTION__[];
+double __builtin_huge_val();
+int __builtin_clz(unsigned int);
+float __builtin_huge_valf();
+long double __builtin_huge_vall();
+long __builtin_expect(long, long);
+double __builtin_inf();
+float __builtin_inff();
+long double __builtin_infl();
+static const char __func__[];
+void __builtin_bzero(void *, int);
+int __builtin_va_arg_pack_len();
TEST OK on ex-nested_undef.c
Appendix. Как воспроизвести:
============================
(Последний раздел. Дальше можно не читать. Кстати, готовые варианты
исполняемого файла examples/Process кто-то может взять, чтобы не
воспроизводить, в vb2:/home/imz/public/cuglify-WIP/ .)
Инструменты и зависимости:
--------------------------
Побольше всего, чтобы поменьше пересобирать cabal-ом:
# apt-get install ghc7.6.1-cabal-install ghc7.6.1 ghc7.6.1-darcs
ghc7.6.1-language-c ghc7.6.1-happy ghc7.6.1-alex ghc7.6.1-haskell-src
ghc7.6.1-mtl
Устанавливаем новую версию cabal-install с поддержкой sandboxes:
(TODO: упаковать в Sisyphus. См. также <http://altlinux.org/Haskell>)
$ cabal update
$ cabal install cabal-install
$ ln -s ~/.cabal/bin/cabal ~/bin -v
«/home/imz/bin/cabal» -> «/home/imz/.cabal/bin/cabal»
$
После этого нужно, чтобы shell не вызывал старый cabal (можно
по-простому заново зайти).
Скачиваем несколько вариантов кода
----------------------------------
(Чтобы в итоге собрать разные варианты, делаем, например, так:)
$ mkdir cuglify-WIP
$ cd cuglify-WIP
$ darcs clone
http://hub.darcs.net/imz/language-c_process_analyze-silently
$ darcs clone
http://hub.darcs.net/imz/language-c_process_analyze-printAST
$ darcs clone
http://hub.darcs.net/imz/language-c_process_analyze-generate
Собираем библиотеку
-------------------
Несовместимых изменений в разных вариантах библиотеки нет, поэтому
можно скомпилировать и установить в sandbox одну на всех (самую полную):
[imz на vb2 cuglify-WIP]$ cd language-c_process_analyze-generate/
[imz на vb2 language-c_process_analyze-generate]$ darcs pull
http://hub.darcs.net/imz/language-c_WIP -p tmp
[imz на vb2 language-c_process_analyze-generate]$ cabal sandbox
--sandbox=/storage/imz/CABAL-SANDBOX-cuglify init
[imz на vb2 language-c_process_analyze-generate]$ cabal install
[imz на vb2 language-c_process_analyze-generate]$ cd ..
### Замечание (о проверке компилятором полноты реализации):
При компиляции Export.hs из библиотеки предупреждения
компилятора рассказывают нам, экспорт каких конструкций ещё не
реализован. (Я пока не думал толком, можно ли это требование закодировать
более
явно на Haskell, а не полагаться на определённый стиль написания
функций и предупреждения компилятора.) Вот например:
[28 of 39] Compiling Language.C.Analysis.Export (
src/Language/C/Analysis/Export.hs,
dist/dist-sandbox-36d68e1d/build/Language/C/Analysis/Export.o )
src/Language/C/Analysis/Export.hs:236:39: Warning:
Defined but not used: `g_tags'
src/Language/C/Analysis/Export.hs:236:46: Warning:
Defined but not used: `g_typedefs'
src/Language/C/Analysis/Export.hs:241:1: Warning:
Pattern match(es) are non-exhaustive
In an equation for `exportIdentDecl':
Patterns not matched: EnumeratorDef _
Компилируем все варианты нашего examples/Process
------------------------------------------------
### 1.
[imz на vb2 cuglify-WIP]$ cd language-c_process_analyze-silently/
[imz на vb2 language-c_process_analyze-silently]$ darcs pull
http://hub.darcs.net/imz/language-c_WIP -p tmp
[imz на vb2 language-c_process_analyze-silently]$ cabal sandbox
--sandbox=/storage/imz/CABAL-SANDBOX-cuglify init
[imz на vb2 language-c_process_analyze-silently]$ cabal exec -- make -C
examples -j Process
[imz на vb2 language-c_process_analyze-silently]$ cd ..
### 2.
[imz на vb2 cuglify-WIP]$ cd language-c_process_analyze-printAST/
[imz на vb2 language-c_process_analyze-printAST]$ darcs pull
http://hub.darcs.net/imz/language-c_WIP -p tmp
[imz на vb2 language-c_process_analyze-printAST]$ cabal sandbox
--sandbox=/storage/imz/CABAL-SANDBOX-cuglify init
[imz на vb2 language-c_process_analyze-printAST]$ cabal exec -- make -C
examples -j Process
make: Вход в каталог
`/home/imz/public/cuglify-WIP/language-c_process_analyze-printAST/examples'
ghc -Wall -package language-c-0.4.8 --make -O Process.hs
[1 of 1] Compiling Main ( Process.hs, Process.o )
Process.hs:3:1: Warning:
The import of `Data.List' is redundant
except perhaps to import instances from `Data.List'
To import instances alone, use: import Data.List()
Process.hs:14:1: Warning:
The import of `System.Console.GetOpt' is redundant
except perhaps to import instances from `System.Console.GetOpt'
To import instances alone, use: import System.Console.GetOpt()
Process.hs:16:1: Warning:
The import of `System.Exit' is redundant
except perhaps to import instances from `System.Exit'
To import instances alone, use: import System.Exit()
Linking Process ...
make: Выход из каталога
`/home/imz/public/cuglify-WIP/language-c_process_analyze-printAST/examples'
[imz на vb2 language-c_process_analyze-printAST]$ cd ..
### 3.
[imz на vb2 cuglify-WIP]$ cd language-c_process_analyze-generate/
[imz на vb2 language-c_process_analyze-generate]$ darcs pull
http://hub.darcs.net/imz/language-c_WIP -p tmp
[imz на vb2 language-c_process_analyze-generate]$ cabal sandbox
--sandbox=/storage/imz/CABAL-SANDBOX-cuglify init
[imz на vb2 language-c_process_analyze-generate]$ cabal exec -- make -C
examples -j Process
make: Вход в каталог
`/home/imz/public/cuglify-WIP/language-c_process_analyze-generate/examples'
ghc -Wall -package language-c-0.4.8 --make -O Process.hs
[1 of 1] Compiling Main ( Process.hs, Process.o )
Process.hs:3:1: Warning:
The import of `Data.List' is redundant
except perhaps to import instances from `Data.List'
To import instances alone, use: import Data.List()
Process.hs:15:1: Warning:
The import of `System.Console.GetOpt' is redundant
except perhaps to import instances from `System.Console.GetOpt'
To import instances alone, use: import System.Console.GetOpt()
Process.hs:17:1: Warning:
The import of `System.Exit' is redundant
except perhaps to import instances from `System.Exit'
To import instances alone, use: import System.Exit()
Linking Process ...
make: Выход из каталога
`/home/imz/public/cuglify-WIP/language-c_process_analyze-generate/examples'
[imz на vb2 language-c_process_analyze-generate]$ cd ..
Тестируем разные варианты
-------------------------
[imz на vb2 cuglify-WIP]$ darcs clone http://hub.darcs.net/imz/cuglify
--set-scripts-executable
[imz на vb2 cuglify-WIP]$ cd cuglify/
### Смотрим прохождение тестов (коротко):
[imz на vb2 cuglify]$ ./run-tests.sh
../language-c_process_analyze-silently/examples/Process 2> /dev/null
TEST OK on ex-nested.c
TEST OK on ex-nested_typemismatch.c
TEST OK on ex-nested_undef.c
TEST OK on ex-nested-with-id-collisions.c
[imz на vb2 cuglify]$ ./run-tests.sh
../language-c_process_analyze-printAST/examples/Process 2> /dev/null
TEST OK on ex-nested.c
TEST OK on ex-nested_typemismatch.c
TEST OK on ex-nested_undef.c
TEST OK on ex-nested-with-id-collisions.c
[imz на vb2 cuglify]$ ./run-tests.sh
../language-c_process_analyze-generate/examples/Process 2> /dev/null
TEST OK on ex-nested.c
TEST OK on ex-nested_typemismatch.c
TEST OK on ex-nested_undef.c
TEST OK on ex-nested-with-id-collisions.c
### Пример того, что происходит (за кулисами):
[imz на vb2 cuglify]$ ./run-tests.sh
../language-c_process_analyze-printAST/examples/Process
PROCESSING ex-nested.c...
* with gcc -c:
0
* with ../language-c_process_analyze-printAST/examples/Process:
void f(int a)
{
int b = 5;
int g(int x)
{
return b + x + a;
}
g(0);
g(1);
g(2);
}
0
* diff against gcc -E:
--- /dev/fd/63 2016-01-09 05:23:34.041146328 +0300
+++ /dev/fd/62 2016-01-09 05:23:34.042146253 +0300
@@ -1,9 +1,8 @@
-# 1 "ex-nested.c"
-# 1 "<command-line>"
-# 1 "ex-nested.c"
-void f(int a) {
+void f(int a)
+{
int b = 5;
- int g(int x) {
+ int g(int x)
+ {
return b + x + a;
}
g(0);
TEST OK on ex-nested.c
PROCESSING ex-nested_typemismatch.c...
* with gcc -c:
ex-nested_typemismatch.c: In function ‘f’:
ex-nested_typemismatch.c:7:4: error: called object ‘b’ is not a
function
1
* with ../language-c_process_analyze-printAST/examples/Process:
Process: user error (ex-nested_typemismatch.c:7: (column 3) [ERROR]
>>> AST invariant violated
attempt to call non-function of type int
)
1
TEST OK on ex-nested_typemismatch.c
PROCESSING ex-nested_undef.c...
* with gcc -c:
0
* with ../language-c_process_analyze-printAST/examples/Process:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
void f(int a)
{
int b = 5;
int g(int x)
{
return b + x + a;
}
g(0);
h(1);
g(2);
}
0
* diff against gcc -E:
ex-nested_undef.c:7: (column 3) [WARNING] >>> AST invariant violated
unknown function: h
--- /dev/fd/63 2016-01-09 05:23:34.198134568 +0300
+++ /dev/fd/62 2016-01-09 05:23:34.199134493 +0300
@@ -1,9 +1,8 @@
-# 1 "ex-nested_undef.c"
-# 1 "<command-line>"
-# 1 "ex-nested_undef.c"
-void f(int a) {
+void f(int a)
+{
int b = 5;
- int g(int x) {
+ int g(int x)
+ {
return b + x + a;
}
g(0);
TEST OK on ex-nested_undef.c
PROCESSING ex-nested-with-id-collisions.c...
* with gcc -c:
ex-nested-with-id-collisions.c: In function ‘g’:
ex-nested-with-id-collisions.c:10:5: warning: return makes integer
from pointer without a cast [enabled by default]
0
* with ../language-c_process_analyze-printAST/examples/Process:
void f(int x);
void g(int x);
void h(int x);
int b = 11;
void f(int a)
{
int b = 5;
int g(int x)
{
return f + x + a;
}
g(0);
g(1);
g(2);
}
void f(int y);
void g(int y);
void h(int y);
0
* diff against gcc -E:
--- /dev/fd/63 2016-01-09 05:23:34.309126253 +0300
+++ /dev/fd/62 2016-01-09 05:23:34.310126178 +0300
@@ -1,23 +1,18 @@
-# 1 "ex-nested-with-id-collisions.c"
-# 1 "<command-line>"
-# 1 "ex-nested-with-id-collisions.c"
void f(int x);
void g(int x);
void h(int x);
-
int b = 11;
-
-void f(int a) {
+void f(int a)
+{
int b = 5;
- int g(int x) {
+ int g(int x)
+ {
return f + x + a;
}
-
g(0);
g(1);
g(2);
}
-
void f(int y);
void g(int y);
void h(int y);
TEST OK on ex-nested-with-id-collisions.c
[imz на vb2 cuglify]$
Best regards.
Ivan
Подробная информация о списке рассылки Devel