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

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

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

Оберон я открыл для себя около года назад. За это время я немало написал на Обеорне, ощутил достоинства этого языка, составил учебную программу обучения программированию на основе Оберона, через неё уже успешно прошло 4 человека.
Сначала я использовал 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) {
}

/* --- */
```

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