Add tests for C <setjmp.h> and Modula-2 Semaphores.

Fix PowerPC ncg so setjmp() returns the correct value.  I got unlucky
when ncg picked r3 for "uses REG"; this destroyed the return value in
r3 and caused the new test to fail.
This commit is contained in:
George Koehler 2018-01-03 14:51:14 -05:00
parent 26de4c1ab1
commit d6938108a6
4 changed files with 230 additions and 9 deletions

View file

@ -85,9 +85,8 @@ REGISTERS
lr, ctr : SPR.
cr0 : CR.
/* The stacking rules and the splitting coercions can't
* allocate registers. We use r12 in the splitting coercions,
* and these scratch registers in the stacking rules.
/* The stacking rules can't allocate registers. We use these
* scratch registers to stack tokens.
*/
#define RSCRATCH r0
#define FSCRATCH f0
@ -2192,15 +2191,21 @@ PATTERNS
pat lpb /* LB -> argument base */
leaving adp EM_BSIZE
/* "gto" must preserve the function result for "lfr", so
* longjmp() can pass the return value to setjmp().
* - See lang/cem/libcc.ansi/setjmp/setjmp.e
*
* Must preserve r3 and r4, so no "uses REG".
* PowerPC can't add r0 + constant. Use r12.
*/
pat gto /* longjmp */
with STACK
uses REG
gen
move {LABEL, $1}, %a
move {IND_RC_W, %a, 8}, fp
move {IND_RC_W, %a, 4}, sp
move {IND_RC_W, %a, 0}, %a
mtspr ctr, %a
move {LABEL, $1}, r12
move {IND_RC_W, r12, 8}, fp
move {IND_RC_W, r12, 4}, sp
move {IND_RC_W, r12, 0}, r12
mtspr ctr, r12
bctr.
pat lor $1==0 /* Load local base */

View file

@ -22,6 +22,7 @@ definerule("plat_testsuite",
"tests/plat/m2/ConvTest_mod.mod",
"tests/plat/m2/NestProc_mod.mod",
"tests/plat/m2/OpenArray_mod.mod",
"tests/plat/m2/SemaTest_mod.mod",
"tests/plat/m2/Set100_mod.mod",
"tests/plat/m2/StringTest_mod.mod"
)

View file

@ -0,0 +1,157 @@
(*
* Generates some integer sequences. Each generator is a process that
* yields integers to the main process. ACK switches processes by
* saving and restoring the stack. It uses _lor_ and _str_ to save
* and restore the local base and frame pointer.
*)
MODULE SemaTest;
FROM Semaphores IMPORT Sema, NewSema, Down, Up, StartProcess;
FROM Storage IMPORT ALLOCATE;
FROM Test IMPORT fail, finished;
TYPE
Generator = POINTER TO GeneratorRecord;
GeneratorRecord = RECORD
resume: Sema; (* up when resuming generator *)
yield: Sema; (* up when yielding value *)
value: INTEGER;
END;
VAR
curgen: Generator; (* current generator *)
startLock: Sema; (* down when booting generator *)
startProc: PROC;
startSelf: Generator;
PROCEDURE BootGenerator;
VAR pr: PROC; self: Generator;
BEGIN
pr := startProc;
self := startSelf;
Up(startLock);
Down(self^.resume); (* wait for first Resume *)
pr();
END BootGenerator;
PROCEDURE StartGenerator(gen: Generator; pr: PROC);
BEGIN
gen^.resume := NewSema(0);
gen^.yield := NewSema(0);
Down(startLock);
startProc := pr;
startSelf := gen;
StartProcess(BootGenerator, 8192);
END StartGenerator;
PROCEDURE Resume(gen: Generator): INTEGER;
VAR self: Generator;
BEGIN
self := curgen;
curgen := gen;
Up(gen^.resume);
Down(gen^.yield); (* wait for Yield *)
curgen := self;
RETURN gen^.value
END Resume;
PROCEDURE Yield(i: INTEGER);
VAR self: Generator;
BEGIN
self := curgen;
self^.value := i;
Up(self^.yield); (* curgen becomes invalid *)
Down(self^.resume); (* wait for Resume *)
END Yield;
PROCEDURE YieldHalfOf(i: INTEGER);
BEGIN
Yield(i DIV 2);
END YieldHalfOf;
PROCEDURE Triangular;
(* Yields the triangular numbers, http://oeis.org/A000217 *)
VAR n: INTEGER;
BEGIN
n := 0;
LOOP
YieldHalfOf(n * (n + 1));
INC(n);
END;
END Triangular;
PROCEDURE Pentagonal;
(* Yields the pentagonal numbers, http://oeis.org/A000326 *)
VAR n: INTEGER;
BEGIN
n := 0;
LOOP
YieldHalfOf(n * (3 * n - 1));
INC(n);
END;
END Pentagonal;
PROCEDURE Odious;
(* Yields the odius numbers, http://oeis.org/A000069 *)
VAR b, i, n: INTEGER;
BEGIN
n := 1;
LOOP
(* b := count bits in n *)
b := 0;
i := n;
WHILE i # 0 DO
INC(b, i MOD 2);
i := i DIV 2;
END;
IF (b MOD 2) = 1 THEN
Yield(n);
END;
INC(n);
END;
END Odious;
TYPE
Triple = ARRAY[1..3] OF INTEGER;
PROCEDURE T(i1, i2, i3: INTEGER): Triple;
VAR t: Triple;
BEGIN
t[1] := i1; t[2] := i2; t[3] := i3; RETURN t
END T;
CONST
two28 = 268435456D; (* 0x1000_0000 *)
VAR
a: ARRAY [0..9] OF Triple;
tri, pen, odi: Generator;
i, g1, g2, g3: INTEGER;
BEGIN
startLock := NewSema(1);
ALLOCATE(tri, SIZE(GeneratorRecord));
ALLOCATE(pen, SIZE(GeneratorRecord));
ALLOCATE(odi, SIZE(GeneratorRecord));
StartGenerator(tri, Triangular);
StartGenerator(pen, Pentagonal);
StartGenerator(odi, Odious);
a[0] := T( 0, 0, 1);
a[1] := T( 1, 1, 2);
a[2] := T( 3, 5, 4);
a[3] := T( 6, 12, 7);
a[4] := T(10, 22, 8);
a[5] := T(15, 35, 11);
a[6] := T(21, 51, 13);
a[7] := T(28, 70, 14);
a[8] := T(36, 92, 16);
a[9] := T(45, 117, 19);
FOR i := 0 TO INTEGER(9) DO
g1 := Resume(tri);
g2 := Resume(pen);
g3 := Resume(odi);
IF g1 # a[i][1] THEN fail(1D * two28 + LONG(a[i][1])) END;
IF g2 # a[i][2] THEN fail(2D * two28 + LONG(a[i][2])) END;
IF g3 # a[i][3] THEN fail(3D * two28 + LONG(a[i][3])) END;
END;
finished;
END SemaTest.

58
tests/plat/setjmp_c.c Normal file
View file

@ -0,0 +1,58 @@
#include <setjmp.h>
#include "test.h"
/*
* Sets i = 2 * i for each i in nums, until i == 0, but stops if
* 2 * i >= 1000.
*
* Uses setjmp() and longjmp() in libc. For ACK's libc, the back end
* must provides EM's _gto_, and _gto_ must preserve the function
* return area.
*/
int nums1[] = { 79, 245, 164, 403, 0};
const int expect1[] = {158, 490, 328, 806, 0};
int nums2[] = {20, 221, 411, 643, 48, 272, 448, 0};
const int expect2[] = {40, 442, 822, 643, 48, 272, 448, 0};
int nums3[] = {371, 265, 500, 124, 117, 0};
const int expect3[] = {742, 530, 500, 124, 117, 0};
int docount = 0;
int twice(int i, jmp_buf esc) {
if (i >= 500)
longjmp(esc, i);
return 2 * i;
}
void donums(int *nums, jmp_buf esc) {
int *p;
docount++;
for (p = nums; *p != 0; p++) {
*p = twice(*p, esc);
}
}
int cknums(int *nums, const int *expect) {
jmp_buf env;
int ret;
ret = setjmp(env);
if (ret == 0)
donums(nums, env);
for (;;) {
ASSERT(*nums == *expect);
if (*expect == 0)
break;
nums++;
expect++;
}
return ret;
}
int main(void) {
ASSERT(cknums(nums1, expect1) == 0);
ASSERT(cknums(nums2, expect2) == 643);
ASSERT(cknums(nums3, expect3) == 500);
ASSERT(docount == 3);
finished();
}