Просмотр исходного кода

m2make: adapt to the current compiler (short-circuit, --extra, dogfood build)

- M2make.mod: relaxed string helpers now that both compilers
  short-circuit AND/OR (Len/Cmp/Cat/Sub/Lower use ordinary index
  guards); flat pools documented as a style choice. Added --extra
  <path>: m2 mode links shim + extra C file (a tool's own shim).
- build_m2.sh: dogfoods the previous b_v3/M2make when present
  (--extra m2make_os.c -I runtime/syslib, the fixpoint shape); first
  run bootstraps. Re-running is a self-host check for the tool.
- README: --extra, -I resolution, corrected constraint list.
- Verified: m2make 18/18, compiler 137/137, fixpoint OK
  (2,156,095 bytes).
Eric Streit 1 неделя назад
Родитель
Сommit
0031cfa65b
4 измененных файлов с 159 добавлено и 80 удалено
  1. 53 0
      docs/summary_m2make-adapt.md
  2. 39 46
      tools/m2make/M2make.mod
  3. 34 18
      tools/m2make/README.md
  4. 33 16
      tools/m2make/build_m2.sh

+ 53 - 0
docs/summary_m2make-adapt.md

@@ -0,0 +1,53 @@
+# Step: M2make adapted to the current compiler
+
+Tag `v3-m2make-adapt`. M2make suite **18/18**; compiler suite
+**137/137**; fixpoint **OK** (2,156,095 bytes).
+
+## Why
+
+`tools/m2make/M2make.mod` is a dual-target build front-end (GNU
+Modula-2 **and** V3).  Its source and README still carried
+workarounds for compiler limits that V3 has since outgrown, and its
+self-build did not use the driver it produces.
+
+## What changed
+
+1. **Source re-probed and relaxed.** Every "both compilers must
+   accept" bullet was re-tested against the current `gm2 -fiso` and
+   V3:
+   - `AND`/`OR` **short-circuit** in both now, so the string helpers
+     dropped their `done`-flag / index-stop gymnastics for ordinary
+     guarded loops (`Len`, `Cmp`, `Cat`, `Sub`, `Lower`).
+   - nested arrays and nested-element open-array actuals work; the
+     flat pools are now documented as a style choice, not a limit.
+   - `LONGINT` function results compile; only `VAL(INTEGER, long)`
+     (64→32) is still rejected, so `LONGINT` stays a `VAR`
+     out-parameter across the `M2makeOS` boundary.
+2. **`--extra <path>` option.** `m2` mode links the C shim plus an
+   extra C file (`cc <image>.s <shim> [<extra>] -o <out> -lm`), so a
+   tool shipping its own C shim (M2make's `m2make_os.c`) can drive
+   its own build.  Usage text updated.
+3. **`build_m2.sh` dogfoods.** It sets the previous `b_v3/M2make`
+   aside, and if one exists it drives the build itself
+   (`M2make -c m2 --extra m2make_os.c -I runtime/syslib …`); the
+   first run (no driver) falls back to the hand-spelled session.
+   Re-running the script is thus a small self-host check for the
+   tool.  (The previous driver predates `--extra`, so the very first
+   dogfood run after this change bootstraps once.)
+4. **README refreshed** — the `--extra` flag, the `-I` module
+   resolution shape (same as `bootstrap/fixpoint.sh`), and the
+   corrected constraint list.
+
+## Verified
+
+- `tools/m2make/tests/run_tests.sh` → **18/18** (both compilers build
+  M2make; ordering, cycle detection, real gm2/m2 builds, up-to-date
+  skips, and the cross-driver case).
+- `build_m2.sh` clean build (bootstrap path), then a **dogfood** run
+  driven by the freshly built `b_v3/M2make`.
+- Compiler suite **137/137**; `bootstrap/fixpoint.sh` **FIXPOINT OK**.
+
+## Files
+
+`tools/m2make/M2make.mod` (string helpers, `--extra`, usage),
+`tools/m2make/build_m2.sh`, `tools/m2make/README.md`.

+ 39 - 46
tools/m2make/M2make.mod

@@ -31,8 +31,8 @@ TYPE
 
 VAR
   nMods: CARDINAL;
-  (* Flat pools (V3 cannot pass nested-array elements as open-array
-     actuals, nor double-index; all access is single-indexed here). *)
+  (* Flat 1D pools: every table is a single indexed array (a style
+     choice -- both compilers handle nested arrays now). *)
   namePool: ARRAY [0..16383] OF CHAR;
   srcPool: ARRAY [0..65535] OF CHAR;
   defPool: ARRAY [0..65535] OF CHAR;
@@ -49,6 +49,7 @@ VAR
   dryRun, verbose, useGm2: BOOLEAN;
   outGiven: BOOLEAN;
   mainPath, outName, m2bin, shimPath: Path;
+  extraC: Path;  (* extra C file linked in m2 mode (may be empty) *)
   nDirs: CARDINAL;
   progName: Name;
   wantProg: BOOLEAN;
@@ -64,19 +65,14 @@ VAR
 (* ---------------- zeroed (NUL) string utilities ---------------- *)
 
 PROCEDURE Len(s: ARRAY OF CHAR): CARDINAL;
-(* NUL scan that never reads past HIGH(s): V3 evaluates AND operands
-   fully (no short-circuit) and traps out-of-range access, and string
-   literals carry no NUL padding, so the scan must stop by index. *)
-VAR i, hi: CARDINAL;
+(* NUL scan.  Both compilers short-circuit AND/OR now, so the index
+   guard protects the access; the bound is the string's HIGH + 1
+   (V3 sting literals carry no NUL padding, fixed buffers do). *)
+VAR i: CARDINAL;
 BEGIN
-  hi := HIGH(s);
   i := 0;
-  WHILE i < hi DO
-    IF s[i] = CHR(0) THEN RETURN i END;
-    INC(i)
-  END;
-  IF s[hi] = CHR(0) THEN RETURN hi END;
-  RETURN hi + 1
+  WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO INC(i) END;
+  RETURN i
 END Len;
 
 PROCEDURE Zero(VAR s: ARRAY OF CHAR);
@@ -98,7 +94,7 @@ END Copy;
 
 PROCEDURE Cmp(a, b: ARRAY OF CHAR): INTEGER;
 (* Length-aware comparison: also correct when one side is a string
-   literal (no NUL padding under V3).  No AND-access in conditions. *)
+   literal (no NUL padding under V3). *)
 VAR i, na, nb: CARDINAL;
 BEGIN
   na := Len(a);
@@ -116,56 +112,44 @@ BEGIN
 END Cmp;
 
 PROCEDURE Cat(a, b: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
-VAR i, k: CARDINAL; done: BOOLEAN;
+(* dst := a + b (NUL-terminated).  Short-circuit AND keeps the index
+   guard ahead of every access. *)
+VAR i, k: CARDINAL;
 BEGIN
   Zero(dst);
-  k := 0;
-  i := 0;
-  done := FALSE;
-  WHILE (NOT done) AND (i <= HIGH(a)) AND (k <= HIGH(dst)) DO
-    IF a[i] = CHR(0) THEN done := TRUE
-    ELSE dst[k] := a[i]; INC(k); INC(i)
-    END
+  k := 0; i := 0;
+  WHILE (i <= HIGH(a)) AND (k <= HIGH(dst)) AND (a[i] # CHR(0)) DO
+    dst[k] := a[i]; INC(k); INC(i)
   END;
   i := 0;
-  done := FALSE;
-  WHILE (NOT done) AND (i <= HIGH(b)) AND (k <= HIGH(dst)) DO
-    IF b[i] = CHR(0) THEN done := TRUE
-    ELSE dst[k] := b[i]; INC(k); INC(i)
-    END
+  WHILE (i <= HIGH(b)) AND (k <= HIGH(dst)) AND (b[i] # CHR(0)) DO
+    dst[k] := b[i]; INC(k); INC(i)
   END
 END Cat;
 
 PROCEDURE Sub(src: ARRAY OF CHAR; start, count: CARDINAL;
               VAR dst: ARRAY OF CHAR);
-VAR j: CARDINAL; done: BOOLEAN;
+VAR j: CARDINAL;
 BEGIN
   Zero(dst);
   j := 0;
-  done := FALSE;
-  WHILE (NOT done) AND (j < count) AND (start + j <= HIGH(src))
-    AND (j <= HIGH(dst)) DO
-    IF src[start + j] = CHR(0) THEN done := TRUE
-    ELSE dst[j] := src[start + j]; INC(j)
-    END
+  WHILE (j < count) AND (start + j <= HIGH(src)) AND (j <= HIGH(dst))
+    AND (src[start + j] # CHR(0)) DO
+    dst[j] := src[start + j]; INC(j)
   END
 END Sub;
 
 PROCEDURE Lower(src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
-VAR i: CARDINAL; ch: CHAR; done: BOOLEAN;
+VAR i: CARDINAL; ch: CHAR;
 BEGIN
   Zero(dst);
   i := 0;
-  done := FALSE;
-  WHILE (NOT done) AND (i <= HIGH(src)) AND (i <= HIGH(dst)) DO
-    IF src[i] = CHR(0) THEN done := TRUE
-    ELSE
-      ch := src[i];
-      IF (ch >= "A") AND (ch <= "Z") THEN
-        ch := CHR(ORD(ch) - ORD("A") + ORD("a"))
-      END;
-      dst[i] := ch; INC(i)
-    END
+  WHILE (i <= HIGH(src)) AND (i <= HIGH(dst)) AND (src[i] # CHR(0)) DO
+    ch := src[i];
+    IF (ch >= "A") AND (ch <= "Z") THEN
+      ch := CHR(ORD(ch) - ORD("A") + ORD("a"))
+    END;
+    dst[i] := ch; INC(i)
   END
 END Lower;
 
@@ -297,7 +281,7 @@ END Die;
 PROCEDURE Usage;
 BEGIN
   Out("Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]"); OutLn;
-  Out("       [--m2bin path] [--shim path] main.mod"); OutLn
+  Out("       [--m2bin path] [--shim path] [--extra path] main.mod"); OutLn
 END Usage;
 
 (* ---------------- module table ---------------- *)
@@ -823,6 +807,7 @@ BEGIN
   AppendSeg(cmd, progName);
   AppendSeg(cmd, ".s ");
   AppendTok(cmd, shimPath);
+  IF Len(extraC) > 0 THEN AppendTok(cmd, extraC) END;
   AppendSeg(cmd, " -o ");
   AppendTok(cmd, out);
   AppendSeg(cmd, " -lm");
@@ -1034,6 +1019,14 @@ BEGIN
       END;
       INC(na);
       Copy(s, shimPath)
+    ELSIF Cmp(s, "--extra") = 0 THEN
+      ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
+      IF NOT ok THEN
+        Out("M2make: --extra expects a path"); OutLn;
+        M2makeOS.ExitNow(2)
+      END;
+      INC(na);
+      Copy(s, extraC)
     ELSIF (Cmp(s, "-h") = 0) OR (Cmp(s, "--help") = 0) THEN
       Usage;
       M2makeOS.ExitNow(0)

+ 34 - 18
tools/m2make/README.md

@@ -9,7 +9,7 @@ to do), 1 build failure (incl. import cycles), 2 usage error.
 
 ```sh
 M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
-       [--m2bin path] [--shim path] main.mod
+       [--m2bin path] [--shim path] [--extra path] main.mod
 ```
 
 - `-n`: dry run (print the build order and the command only).
@@ -21,10 +21,16 @@ M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
   as declared in the main file).
 - `--m2bin path`: M2 driver binary (default `M2`).
 - `--shim path`: C runtime shim for `m2` mode (default `shim.c`).
+- `--extra path`: extra C file linked in `m2` mode (after the shim;
+  e.g. a tool's own `m2make_os.c`).
 
 `m2` mode runs one session — `<m2bin> <ordered .def/.mod files>`
-then `qbe` then `cc <image>.s <shim> -o <out> -lm` — and gates on
-M2's `Parsed correctly` verdict (M2 exits 0 even on failure).
+then `qbe` then `cc <image>.s <shim> [<extra>] -o <out> -lm` — and
+gates on M2's `Parsed correctly` verdict (M2 exits 0 even on
+failure).  Modules the session needs but that are not named on the
+command line are resolved from the `-I` directories (so
+`-I runtime/syslib` finds `SysShim`/`FileIO`): the same shape the
+compiler's own `bootstrap/fixpoint.sh` uses.
 `gm2` mode recompiles only stale units (`gm2 -fiso -c`, objects
 land in the cwd as `<Base>.o`) and links the main file with the
 objects in topological order.
@@ -43,29 +49,39 @@ non-portable lives in the per-compiler layer:
 
 ```sh
 ./build_gm2.sh   # gm2 -fiso -> b_gm2/M2make
-./build_m2.sh    # M2 session + qbe + cc -> b_v3/M2make
+./build_m2.sh    # -> b_v3/M2make  (bootstraps, then dogfoods)
 ./tests/run_tests.sh
 ```
 
+`build_m2.sh` builds M2make with the V3 compiler.  If a previous
+`b_v3/M2make` exists it drives the build itself
+(`M2make -c m2 --extra m2make_os.c -I runtime/syslib …`); on the
+first run (no driver yet) the session is spelled out by hand.
+Re-running it is therefore a small self-host check for the tool.
+
 ## Source conventions (both compilers must accept the code)
 
-`M2make.mod` is restricted to the intersection the two compilers
-actually implement (all learned the hard way, via probes):
+`M2make.mod` targets the intersection of GNU Modula-2 and V3.  The
+list below was re-probed against the current compilers; several old
+restrictions are gone (both V3 and gm2 now accept them):
 
-- No `_` in identifiers, no `0C` (use `CHR(0)`), empty parameter
-  lists need a space (`PROCEDURE P () : T`).
-- Declaration before use (V3 is single-pass; gm2 tolerates
-  forward references).
+- No `_` in identifiers (still required by V3's classic lexer);
+  empty parameter lists need a space (`PROCEDURE P () : T`).
+- Declaration before use (V3 is single-pass; gm2 tolerates forward
+  references).
 - All strings NUL-terminated; own `Len/Copy/Cmp/Cat` on top of
   indexed access (the two `FileIO` string layers differ).
-- Flat 1D arrays only: V3 rejects `a[i][j]` loads (233) and
-  miscompiles nested-array elements passed as open-array actuals
-  (double load → segfault).
-- Never rely on `AND`/`OR` short-circuit: V3 evaluates both sides
-  and traps out-of-range indexing, so guards must dominate
-  accesses structurally.
-- `LONGINT` only via `VAR` out-parameters (V3 rejects `LONGINT`
-  function results in calls); never print negative `INTEGER`s.
+- `AND`/`OR` **short-circuit** in both compilers now, so ordinary
+  index guards (`(i <= HIGH(s)) AND (s[i] # …)`) are safe.  (The
+  helpers still compute `Len` up front where that reads more
+  clearly.)
+- Flat 1D arrays are used throughout; V3 now handles nested arrays
+  and nested-element open-array actuals, so this is a style choice
+  rather than a restriction.
+- `LONGINT` results from calls compile, but `VAL(INTEGER, longval)`
+  (64→32) is still rejected by V3, so `LONGINT` crosses the
+  `M2makeOS` boundary only as a `VAR` out-parameter; negative
+  `INTEGER` printing is still avoided.
 - No `ExitCode` global (gm2 has none): all exits via
   `M2makeOS.ExitNow`. No `SYSTEM`/`Storage` in portable code.
 

+ 33 - 16
tools/m2make/build_m2.sh

@@ -1,30 +1,47 @@
 #!/bin/sh
-# Builds M2make with the V3 M2 compiler itself.
+# Builds M2make with the V3 M2 compiler.
 # Usage: ./build_m2.sh   (from the tools/m2make directory)
 # Output: b_v3/M2make (a V3-built binary)
 #
-# Session: SysShim + FileIO (runtime/syslib) + M2makeOS + M2make,
-# then qbe + cc with the runtime shim and m2make_os.c.
+# Two paths:
+#   * If a V3-built M2make already exists (b_v3/M2make, e.g. from a
+#     previous run), it drives the build -- M2make resolves the
+#     SysShim/FileIO modules via -I runtime/syslib and orders the
+#     session itself (the dogfooding path; same shape as
+#     bootstrap/fixpoint.sh).
+#   * Otherwise the session is spelled out by hand (bootstrap path).
 set -e
 cd "$(dirname "$0")"
 M2=../../compiler/M2
 SYS=../../runtime/syslib
-rm -rf b_v3
+
+# keep the old driver (if any) aside so it can drive this build
+rm -rf b_v3.old
+if [ -x b_v3/M2make ]; then mv b_v3 b_v3.old; fi
 mkdir -p b_v3
 cp M2make.mod M2makeOS.def m2make_os.c b_v3/
 cp M2makeOS_v3.mod b_v3/M2makeOS.mod
 cd b_v3
 mkdir -p gen_ssa
-echo "=== Compiling (M2 session) ==="
-# NB: M2 exits 0 even when it reports "Incorrect source", so gate on
-# the verdict text instead of the exit code.
-"../$M2" \
-    "../$SYS/SysShim.def" "../$SYS/SysShim.mod" \
-    "../$SYS/FileIO.def" "../$SYS/FileIO.mod" \
-    M2makeOS.def M2makeOS.mod M2make.mod \
-    2>&1 | tee m2.log | grep -q Parsed || exit 1
-echo "=== Assembling and linking ==="
-qbe -o gen_ssa/M2make.s gen_ssa/M2make.ssa || exit 1
-cc gen_ssa/M2make.s "../$SYS/shim.c" m2make_os.c \
-    -o M2make -lm || exit 1
+
+if [ -x ../b_v3.old/M2make ]; then
+  echo "=== Building with the previous b_v3/M2make (dogfood) ==="
+  ../b_v3.old/M2make -c m2 --m2bin "../$M2" --shim "../$SYS/shim.c" \
+      --extra m2make_os.c -I "../$SYS" -o M2make M2make.mod || exit 1
+else
+  echo "=== Compiling (M2 session; bootstrap path) ==="
+  # NB: M2 exits 0 even when it reports "Incorrect source", so gate on
+  # the verdict text instead of the exit code.
+  "../$M2" \
+      "../$SYS/SysShim.def" "../$SYS/SysShim.mod" \
+      "../$SYS/FileIO.def" "../$SYS/FileIO.mod" \
+      M2makeOS.def M2makeOS.mod M2make.mod \
+      2>&1 | tee m2.log | grep -q Parsed || exit 1
+  echo "=== Assembling and linking ==="
+  qbe -o gen_ssa/M2make.s gen_ssa/M2make.ssa || exit 1
+  cc gen_ssa/M2make.s "../$SYS/shim.c" m2make_os.c \
+      -o M2make -lm || exit 1
+fi
+cd ..
+rm -rf b_v3.old
 echo "=== b_v3/M2make built ==="