OberonCore
https://forum.oberoncore.ru/

Ошибка в компиляторе OO2C
https://forum.oberoncore.ru/viewtopic.php?f=30&t=5656
Страница 1 из 2

Автор:  kekc_leader [ Понедельник, 04 Апрель, 2016 05:09 ]
Заголовок сообщения:  Ошибка в компиляторе OO2C

Уважаемые товарищи!

Я нашёл ошибку в компиляторе OO2C, прошу вас помочь мне её устранить. Уж извините, что так много написал.

Но сначала немного о себе (это к ошибке не особо относится).

Оберон я открыл для себя около года назад. За это время я немало написал на Обеорне, ощутил достоинства этого языка, составил учебную программу обучения программированию на основе Оберона, через неё уже успешно прошло 4 человека. :mrgreen:
Сначала я использовал XDS, но пришлось от него отказаться, потому что он не работал на некоторых системах, затем - VOC. От него тоже пришлось отказаться, потому что я не смог портировать его на Windows без Cygwin, затем я стал использовать OO2C (большое спасибо Алексею Ильину за то, что он освежил этот проект несколько лет назад). Хочу сказать, что на всём протяжении освоения Оберона я пытался использовать и Blackbox, но он отталкивает своей излишней привязанностью к Windows, так что с ним пока не получилось.

Теперь об ошибке в OO2C.

Писал я себе спокойно программу, как вдруг началась какая-то чертовщина - добавляешь в текст процедуры Out.Int(i, 0); и процедура начинает работать совсем по-другому. Изучив генерирующиеся си-файлы я понял, что это какая-то ошибка оптимизации в компиляторе. Дальше встала задача выдрать этот кусок, но так, чтобы «не повредить» ошибку. Оказалось, что это не так просто - достаточно убрать что-то лишнее и всё внезапно начинает работать правильно, но в итоге получилось. Вот код модуля (55 строк):
Код:
MODULE BugFix;
IMPORT Out, SYSTEM;
TYPE
  Bitmap = POINTER TO BitmapDesc;
  BitmapDesc = RECORD
    w, h: LONGINT;
    surface: BOOLEAN
  END;
VAR B: Bitmap;

PROCEDURE PutPixelQuick*(x, y: LONGINT);
BEGIN
  IF x >= 0 THEN
    Out.String('  PUT'); Out.Int(x, 3); Out.Int(y, 3); Out.Ln
  END
END PutPixelQuick;

PROCEDURE Line*(bmp: Bitmap; x1, y1, x2, y2: LONGINT);
VAR x, y, i, dx, dy, sx, sy, e: LONGINT; vert: BOOLEAN;
BEGIN
  dx := ABS(x1 - x2); dy := ABS(y1 - y2);
  IF x2 > x1 THEN sx := 1 ELSE sx := -1 END;
  IF y2 > y1 THEN sy := 1 ELSE sy := -1 END;
  x := x1; y := y1; vert := dy > dx;
  IF vert THEN i := dx; dx := dy; dy := i END;
  e := 2 * dy - dx;
  (*Out.Bool(vert);*) (* Magically fixes everything *)
  FOR i := 0 TO dx DO
    (*Out.Int(i, 2); Out.String('=i ');*) (* Makes 6-4 / 7-3 *)

    (* Removing "bmp.w" and "bmp.h" checks here makes all correct.
       Changing "bmp.*" to "100" or to constants or to global
       variables magically fixes everything too. *)
    IF (x >= 0) & (y >= 0) &
       (x < bmp.w) & (y < bmp.h) THEN
      PutPixelQuick(x, y)
    END;
    IF e >= 0 THEN
      IF vert THEN INC(x, sx) ELSE INC(y, sy) END;
      DEC(e, 2 * dx)
    END;
    IF vert THEN INC(y, sy) ELSE INC(x, sx) END;
    INC(e, 2 * dy)
  END
END Line;

BEGIN
  NEW(B); B.w := 100; B.h := 100;
  Out.String('LINE (0; 0) TO (7; 7):'); Out.Ln;
  Line(B, 0, 0, 7, 7); Out.Ln;
  Out.String('LINE (0; 0) TO (3; 7):'); Out.Ln;
  Line(B, 0, 0, 3, 7); Out.Ln;
  Out.String('LINE (0; 0) TO (7; 3):'); Out.Ln;
  Line(B, 0, 0, 7, 3)
END BugFix.

Ошибка происходит при компиляции процедуры Line.
Назначение процедуры - отрисовать отрезок из точки (x1; y1) в (x2; y2).
Пояснения:
1. Отрезок всегда рисуется из точки №1 в точку №2, какая из них где бы ни находилась.
2. dx и dy - это расстояния (неотрицательные) между данными точками по X и по Y соответственно.
3. sx и sy - это направление (-1 или +1) движения от точки №1 к точке №2 (например, если точка №1 левее, то sx = 1, иначе sx = -1)
4. x и y - это точка «на которой мы сейчас находимся», проходя по прорисовываемому отрезку.
4. vert - это булевская переменная, которая истинна в случае, если наклонный отрезок более вытянут по вертикали, чем по горизонтали.
5. В случае, если vert - истина, отрезок следует рисовать, пробегая его сверху вниз (или снизу вверх), а не слева направо, чтобы не было разрывов между точками. Однако в этой реализации алгоритма данный нюанс реализован таким образом: во-первых, перед циклом, в случае если vert - истина, переменные dx и dy меняются местами, а во-вторых, внутри цикла (в двух местах) в зависимости от значения vert, изменяется значение либо x, либо y:
Код:
IF vert THEN INC(x, sx) ELSE INC(y, sy) END;

5. В нескольких местах можно заметить, что dx или dy умножается на 2. Это делается для того, чтобы можно было обойтись без деления пополам (чтобы избежать округления).

Если скомпилировать данный модуль с помощью OO2C (oo2c -M BugFix), то отрезок из (0; 0) в (7; 3) будет отрисован неправильно:
Код:
  PUT  0  0
  PUT  1  0
  PUT  2  1
  PUT  2  2
  PUT  3  3
  PUT  3  4
  PUT  4  5
  PUT  4  6

Во первых, он явно идёт в точку (3; 7), а не (7; 3), а во-вторых, он топчется два шага на нуле и в результате доходит только до (4; 6). (На самом деле, дело не в том, что он топчется на нуле, там вообще как-то по-странному всё сбивается.)

Дёргаем ошибку за разные места:
1. Если раскомментировать эту строчку:
Код:
(*Out.Bool(vert);*) (* Magically fixes everything *)
то происходит собственно чертовщина, всё начинает работать как часы.
2. Если закомментировать её обратно, но раскомментировать эту:
Код:
(*Out.Int(i, 2); Out.String('=i ');*) (* Makes 6-4 / 7-3 *)
то ошибка превращается в другую. Теперь проблемный отрезок рисуется правильно, зато другой портится - он строится не в (3; 7), как ему было сказано, а в (6; 4). То есть ошибка типа симметричная, но какого чёрта? мы всего-навсего вывели на экран содержимое переменной "i".
3. Если закомментировать всё обратно, но убрать из IF'а эту часть условия:
Код:
(x < bmp.w) & (y < bmp.h)
то всё опять работает.
4. И самая эпика. Если вернуть проверку из предыдущего пункта обратно, но только вместо bmp.w и bmp.h использовать две обычные глобальные переменные (заранее проинициализированные в главном BEGIN'е)...
Код:
VAR bmpw, bmph: LONGINT;
...
... & (x < bmpw) & (y < bmph) THEN ...
то ошибка тоже пропадает! Если использовать константные значения или ещё что-то, но ошибки не будет. Она появляется только если w и h находятся внутри POINTER TO RECORD.

А такой классный компилятор. Только теперь, получается, ошибку может выдать в любой программе в неожиданном месте. Что теперь с ним делать? Я искал как в нём отключить оптимизацию или что-нибудь, но не факт, что это возможно.

Генирируемый код на Си выглядит довольно сложно. У него там
register OOC_INT32 i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12;
а потом бессмысленная чехарда с регистрами по туда-сюда.

BugFix.c модуля, приведённого выше:
Код:
#include <BugFix.d>
#include <__oo2c.h>
#include <setjmp.h>

void BugFix__PutPixelQuick(OOC_INT32 x, OOC_INT32 y) {
  register OOC_INT32 i0,i1;

  i0 = x;
  i1 = i0>=0;
  if (!i1) goto l4;
  Out__String((OOC_CHAR8*)"  PUT", 6);
  Out__Int(i0, 3);
  i0 = y;
  Out__Int(i0, 3);
  Out__Ln();
l4:
  return;
  ;
}

void BugFix__Line(BugFix__Bitmap bmp, OOC_INT32 x1, OOC_INT32 y1, OOC_INT32 x2, OOC_INT32 y2) {
  register OOC_INT32 i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12;
  OOC_INT32 dx;
  OOC_INT32 dy;
  OOC_INT32 sx;
  OOC_INT32 sy;
  OOC_INT32 x;
  OOC_INT32 y;
  OOC_CHAR8 vert;
  OOC_INT32 i;
  OOC_INT32 e;

  i0 = x1;
  i1 = x2;
  i2 = y2;
  i3 = y1;
  i4 = _abs((i0-i1));
  dx = i4;
  i5 = _abs((i3-i2));
  dy = i5;
  i1 = i1>i0;
  if (i1) goto l3;
  sx = (-1);
  i1=(-1);
  goto l4;
l3:
  sx = 1;
  i1=1;
l4:
  i2 = i2>i3;
  if (i2) goto l7;
  sy = (-1);
  i2=(-1);
  goto l8;
l7:
  sy = 1;
  i2=1;
l8:
  x = i0;
  y = i3;
  vert = (i5>i4);
  if ((i5>i4)) goto l11;
  i6=i5;i7=i4;
  goto l12;
l11:
  dx = i5;
  dy = i4;
  i6=i4;i7=i5;
l12:
  i6 = 2*i6;
  i8 = i6-i7;
  e = i8;
  i = 0;
  i9 = 0<=i7;
  if (!i9) goto l46;
  i9 = 2*i7;
  i10 = (OOC_INT32)bmp;
  i11=0;
l15_loop:
  i12 = i0>=0;
  if (i12) goto l18;
  i12=0u;
  goto l20;
l18:
  i12 = i3>=0;
 
l20:
  if (i12) goto l22;
  i12=0u;
  goto l24;
l22:
  i12 = *(OOC_INT32*)(_check_pointer(i10, 1066));
  i12 = i0<i12;
 
l24:
  if (i12) goto l26;
  i12=0u;
  goto l28;
l26:
  i12 = *(OOC_INT32*)((_check_pointer(i10, 1080))+4);
  i12 = i3<i12;
 
l28:
  if (!i12) goto l30;
  BugFix__PutPixelQuick(i0, i3);
l30:
  i12 = i8>=0;
  if (!i12) goto l38;
  if ((i5>i4)) goto l35;
  i3 = i3+i2;
  y = i3;
 
  goto l36;
l35:
  i0 = i0+i1;
  x = i0;
 
l36:
  i8 = i8-i9;
 
l38:
  if ((i5>i4)) goto l40;
  i0 = i0+i1;
  x = i0;
 
  goto l41;
l40:
  i3 = i3+i2;
  y = i3;
 
l41:
  i4 = i8+i6;
  e = i4;
  i5 = i11+1;
  i = i5;
  i8 = i5<=i7;
  if (!i8) goto l46;
  i8=i4;i11=i5;
  goto l15_loop;
l46:
  return;
  ;
}

void OOC_BugFix_init(void) {
  register OOC_INT32 i0;

  i0 = (OOC_INT32)RT0__NewObject(_td_BugFix__Bitmap.baseTypes[0]);
  BugFix__B = (BugFix__Bitmap)i0;
  *(OOC_INT32*)(_check_pointer(i0, 1339)) = 100;
  *(OOC_INT32*)((_check_pointer(i0, 1351))+4) = 100;
  Out__String((OOC_CHAR8*)"LINE (0; 0) TO (7; 7):", 23);
  Out__Ln();
  i0 = (OOC_INT32)BugFix__B;
  BugFix__Line((BugFix__Bitmap)i0, 0, 0, 7, 7);
  Out__Ln();
  Out__String((OOC_CHAR8*)"LINE (0; 0) TO (3; 7):", 23);
  Out__Ln();
  i0 = (OOC_INT32)BugFix__B;
  BugFix__Line((BugFix__Bitmap)i0, 0, 0, 3, 7);
  Out__Ln();
  Out__String((OOC_CHAR8*)"LINE (0; 0) TO (7; 3):", 23);
  Out__Ln();
  i0 = (OOC_INT32)BugFix__B;
  BugFix__Line((BugFix__Bitmap)i0, 0, 0, 7, 3);
  return;
  ;
}

void OOC_BugFix_destroy(void) {
}

/* --- */

Спасибо за внимание.

Автор:  Alexander Shiryaev [ Понедельник, 04 Апрель, 2016 05:19 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Попробуйте отключить оптимизации компилятора C.
--cflags "-O0"

Автор:  kekc_leader [ Понедельник, 04 Апрель, 2016 05:24 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Alexander Shiryaev писал(а):
Попробуйте отключить оптимизации компилятора C.
--cflags "-O0"
Не подскажите, как через oo2c передать параметр компилятору Си? Кажется, надо создавать какой-то config-файл...

Автор:  Alexander Shiryaev [ Понедельник, 04 Апрель, 2016 05:26 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

oo2c --cflags "-O0" ...

Автор:  kekc_leader [ Понедельник, 04 Апрель, 2016 05:30 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Не помогло (ничего не изменилось).

Автор:  Kemet [ Понедельник, 04 Апрель, 2016 06:42 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

ошибка проявляется в Windpws и Linux версии?

Автор:  kekc_leader [ Понедельник, 04 Апрель, 2016 12:25 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Kemet писал(а):
ошибка проявляется в Windpws и Linux версии?
Только что проверил в Винде - ошибка появляется в обоих версиях.
Кстати, раз она и на Винде, могу закачать уже откомпилированный OO2C под Windows, чтоб можно было «пощупать» ошибку.

Автор:  Kemet [ Понедельник, 04 Апрель, 2016 12:47 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

kekc_leader,
компилятор 32 или 64 бита, оригинальный с sf.net или кем-то правленый?
Мы когда-то использовали OO2C и таких ошибок точно не было.

Автор:  Alexander Shiryaev [ Понедельник, 04 Апрель, 2016 14:44 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

kekc_leader писал(а):
Хочу сказать, что на всём протяжении освоения Оберона я пытался использовать и Blackbox, но он отталкивает своей излишней привязанностью к Windows, так что с ним пока не получилось.

(Не-GUI) часть BlackBox сейчас нормально работает и на других операционных системах. http://gitlab.molpit.org/oberon/blackbox-freenix

Автор:  Alexander Shiryaev [ Понедельник, 04 Апрель, 2016 15:04 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Alexander Shiryaev писал(а):
kekc_leader писал(а):
Хочу сказать, что на всём протяжении освоения Оберона я пытался использовать и Blackbox, но он отталкивает своей излишней привязанностью к Windows, так что с ним пока не получилось.

(Не-GUI) часть BlackBox сейчас нормально работает и на других операционных системах. http://gitlab.molpit.org/oberon/blackbox-freenix

Код:
./run-BlackBox <<DATA
ConsCompiler.Compile('', 'BugFix.Mod')
Kernel.LoadMod('BugFix')
DATA

Код:
LINE (0; 0) TO (7; 7):
  PUT 0 0
  PUT 1 1
  PUT 2 2
  PUT 3 3
  PUT 4 4
  PUT 5 5
  PUT 6 6
  PUT 7 7

LINE (0; 0) TO (3; 7):
  PUT 0 0
  PUT 0 1
  PUT 1 2
  PUT 1 3
  PUT 2 4
  PUT 2 5
  PUT 3 6
  PUT 3 7

LINE (0; 0) TO (7; 3):
  PUT 0 0
  PUT 1 0
  PUT 2 1
  PUT 3 1
  PUT 4 2
  PUT 5 2
  PUT 6 3
  PUT 7 3

Автор:  Александр Ильин [ Понедельник, 04 Апрель, 2016 16:56 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

kekc_leader писал(а):
большое спасибо Алексею Ильину
Не слышали о таком...

Автор:  Илья Ермаков [ Понедельник, 04 Апрель, 2016 17:47 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Alexander Shiryaev писал(а):
(Не-GUI) часть BlackBox сейчас нормально работает и на других операционных системах. http://gitlab.molpit.org/oberon/blackbox-freenix


Ну так уже и ГУИ-часть-то работает там нормально, вроде?

Мы серверные вещи для отладки, чтоб полазить по дампам и проч., иногда запускаем графически.

Автор:  prospero78 [ Понедельник, 04 Апрель, 2016 17:59 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Илья Ермаков писал(а):
Мы серверные вещи для отладки, чтоб полазить по дампам и проч., иногда запускаем графически.

Терпимо, если чекбоксы не использовать.

Автор:  Alexander Shiryaev [ Понедельник, 04 Апрель, 2016 18:50 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Илья Ермаков писал(а):
Ну так уже и ГУИ-часть-то работает там нормально, вроде?

Для кого-то может и так сойдёт, но вообще глюки есть.

Автор:  kekc_leader [ Понедельник, 04 Апрель, 2016 20:30 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Kemet писал(а):
kekc_leader,
компилятор 32 или 64 бита, оригинальный с sf.net или кем-то правленый?
Мы когда-то использовали OO2C и таких ошибок точно не было.
Это 32-разрядная версия, взятая с https://github.com/AlexIljin/oo2c. Я продолжительное время использую OO2C и тоже такую ошибку вижу впервые.

Alexander Shiryaev писал(а):
(Не-GUI) часть BlackBox сейчас нормально работает и на других операционных системах. http://gitlab.molpit.org/oberon/blackbox-freenix
О, может быть подойдёт. Где можно найти развёрнутые инструкции? (например, как в этой версии собрать исполняемый файл)

Александр Ильин писал(а):
kekc_leader писал(а):
большое спасибо Алексею Ильину
Не слышали о таком...
Перепутал))

Когда я этот пример скомпилировал через консольный Blackbox, он вместо пробелов вывел какие-то другие символы (с кодом 143), причём только пробелы, которые идут из Out.Int. Что это? (Используется эмулятор терминала XFCE.)

Вложения:
Комментарий к файлу: Странные пробелы
strange-spaces.png
strange-spaces.png [ 19.58 КБ | Просмотров: 12869 ]

Автор:  Alexander Shiryaev [ Понедельник, 04 Апрель, 2016 21:53 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

kekc_leader писал(а):
Alexander Shiryaev писал(а):
(Не-GUI) часть BlackBox сейчас нормально работает и на других операционных системах. http://gitlab.molpit.org/oberon/blackbox-freenix
О, может быть подойдёт. Где можно найти развёрнутые инструкции? (например, как в этой версии собрать исполняемый файл)

Там всё почти так же, как в Windows.
Исполняемый файл собрать нельзя, только библиотеку .so

kekc_leader писал(а):
Когда я этот пример скомпилировал через консольный Blackbox, он вместо пробелов вывел какие-то другие символы (с кодом 143), причём только пробелы, которые идут из Out.Int. Что это? (Используется эмулятор терминала XFCE.)

Да, надо будет поразбираться откуда берётся символ 8FX. Он появляется при использовании модуля Out.

Автор:  Alexander Shiryaev [ Понедельник, 04 Апрель, 2016 22:06 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Код:
MODULE Out;
(**
   project   = "BlackBox"
   organization   = "www.oberon.ch"
   contributors   = "Oberon microsystems"
   version   = "System/Rsrc/About"
   copyright   = "System/Rsrc/About"
   license   = "Docu/BB-License"
   changes   = ""
   issues   = ""

**)

   IMPORT Views, TextModels, TextMappers, TextViews, StdLog;

   CONST digitspace = 08FX;

   VAR
      buf: TextModels.Model;
      out: TextMappers.Formatter;

   PROCEDURE Open*;
   BEGIN
      StdLog.Open
   END Open;

   PROCEDURE Char* (ch: CHAR);
   BEGIN
      out.WriteChar(ch);
      StdLog.text.Append(buf); Views.RestoreDomain(StdLog.text.Domain())
   END Char;

   PROCEDURE Ln*;
   BEGIN
      out.WriteLn;
      StdLog.text.Append(buf); Views.RestoreDomain(StdLog.text.Domain());
      TextViews.ShowRange(StdLog.text, StdLog.text.Length(), StdLog.text.Length(), TextViews.any)
   END Ln;

   PROCEDURE String* (str: ARRAY OF CHAR);
   BEGIN
      out.WriteString(str);
      StdLog.text.Append(buf); Views.RestoreDomain(StdLog.text.Domain())
   END String;

   PROCEDURE Int* (i: LONGINT; n: INTEGER);
   BEGIN
      out.WriteIntForm(i, 10, n, digitspace, FALSE);
      StdLog.text.Append(buf); Views.RestoreDomain(StdLog.text.Domain())
   END Int;

   PROCEDURE Real* (x: REAL; n: INTEGER);
   BEGIN
      out.WriteRealForm(x, 16, n, 0, digitspace);
      StdLog.text.Append(buf); Views.RestoreDomain(StdLog.text.Domain())
   END Real;

BEGIN
   buf := TextModels.CloneOf(StdLog.buf); out.ConnectTo(buf)
END Out.

Это digitspace

Автор:  Роман М. [ Вторник, 05 Апрель, 2016 00:08 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Это всё замечательно, но не отвечает на изначальный вопрос по теме.

Автор:  Иван Денисов [ Вторник, 05 Апрель, 2016 06:57 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Роман М. писал(а):
Это всё замечательно, но не отвечает на изначальный вопрос по теме.

Но ведь автор вопроса не зря задавал вопрос очень обширно и с большой предысторией :)
Суть ответа — используйте Компонентный Паскаль и BlackBox. А людей, кто бы использовал oo2c сегодня мало... однако сам пользовался H2O, которая на нем сделана.

Заметил, что если vert сделать глобальной, то тоже корректно считается.

Автор:  Иван Денисов [ Вторник, 05 Апрель, 2016 07:52 ]
Заголовок сообщения:  Re: Ошибка в компиляторе OO2C

Вот суть ошибки по анализу кода на С.

Вот так хорошо (вынес vert в глобальные):
Код:
l30:
  i10 = i6>=0;
  i11 = BugFix__vert;
  if (!i10) goto l38;
  if (i11) goto l35;
  i3 = i3+i2;


А вот так плохо:
Код:
l30:
  i12 = i8>=0;
  if (!i12) goto l38;
  if ((i5>i4)) goto l35;
  i3 = i3+i2;
  y = i3;


Вместо переменной компилятор подставляет выражение i5>i4.

Страница 1 из 2 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/