summaryrefslogtreecommitdiff
path: root/chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java
blob: dd20b7467d75e980dbcdb811764dedf2ecf3625b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.seaofnodes.simple;

import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.isa.eval.EvalArm64;
import com.seaofnodes.isa.eval.EvalRisc5;
import org.junit.Test;
import static org.junit.Assert.*;

public class ConstructionTest {
    private static final String CONSTRUCT = """
        struct Box {
            int x=7;
            int[] data;
            new Box = { int n ->
                data=new int[2];
                x=n;
                data[0]=x+1;
            };
            val get = { -> x+data[0]; };
        };
        val a=new Box(arg);
        val b=new Box(arg+10);
        return a.get()*100+b.get();
        """;

    @Test public void testConstructorCalls() throws java.io.IOException {
        var code=new CodeGen(CONSTRUCT).driver(CodeGen.Phase.FinalizeIR);
        assertEquals("525",Eval2.eval(code,2));
        for( String cpu : new String[]{"arm","riscv"} ) {
            code=new CodeGen(CONSTRUCT).driver(cpu,"SystemV",null);
            byte[] memory=new byte[1<<20];
            byte[] bits=code._encoding.bits();
            System.arraycopy(bits,0,memory,0,bits.length);
            if( cpu.equals("arm") ) {
                EvalArm64 eval=new EvalArm64(memory,1<<16);
                eval.regs[0]=2;
                assertEquals(0,eval.step(10000));
                assertEquals(525,eval.regs[0]);
            } else {
                EvalRisc5 eval=new EvalRisc5(memory,1<<16);
                eval.regs[10]=2;
                assertEquals(0,eval.step(10000));
                assertEquals(525,eval.regs[10]);
            }
        }
        // Field updates are local SSA values, including the old x used after
        // updating y and x. Only constructor exits should write these fields.
        String counter="struct Counter { int x; int y; int z; new Counter={int n -> while(x<n) { int old=x; y=x+1; x=y+1; z=old; }}; }; val c=new Counter(arg); return c.x*100+c.z;";
        var loop=new CodeGen(counter)
            .driver(CodeGen.Phase.LocalSched);
        assertEquals("0",Eval2.eval(loop,0));
        assertEquals("2018",Eval2.eval(loop,19));
        loop._stop.walk(n -> {
            if( n instanceof com.seaofnodes.simple.node.StoreNode st )
                assertEquals("Constructor Stores belong outside the loop",0,st.cfg0().loopDepth());
            return null;
        });
        var defaults=new CodeGen("struct Buffer { int[] data=new int[1]; }; val a=new Buffer; val b=new Buffer; a.data[0]=7; return b.data[0];")
            .driver(CodeGen.Phase.TypeCheck);
        assertEquals("0",Eval2.eval(defaults,0));
        java.nio.file.Path dir=java.nio.file.Path.of("build/objs");
        java.nio.file.Files.createDirectories(dir);
        java.nio.file.Files.writeString(dir.resolve("construction.smp"),"val construct={int arg ->"+CONSTRUCT+"}; val count={int arg ->"+counter+"};");
        java.nio.file.Files.writeString(dir.resolve("construction.c"),"""
            extern long long construct(long long);
            extern long long count(long long);
            int main(void) { return construct(2)!=525 || construct(0)!=121 || count(19)!=2018 || count(0)!=0; }
            """);
        TestC.run("build/objs","construction","",-1);
    }

    @Test public void testCompletionBeforePublication() {
        String src="struct Box { int[] data; new Box={int n -> if(n) data=new int[2];}; }; return new Box(arg);";
        try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(); }
        catch( Parser.ParseException e ) {
            assertEquals("'Box' is not fully initialized, field 'data' needs to be set in a constructor",e.getMessage());
        }
        for( String early : new String[]{
            "val use={!Box b -> b.x;}; struct Box { int x; new Box={->use(self);}; }; return new Box;",
            "struct Box { Box? next; new Box={->next=self;}; }; return new Box;",
            "struct Wrap { Box? item; }; struct Box { new Box={->new Wrap { item=self; };}; }; return new Box;"
        } ) {
            try { new CodeGen(early).driver(CodeGen.Phase.TypeCheck); fail(); }
            catch( Parser.ParseException e ) {
                assertEquals("Cannot publish self before the constructor finishes",e.getMessage());
            }
        }
    }

    @Test public void testForwardMethod() {
        var code=new CodeGen("val run={!Box b -> b.get();}; struct Box { int x=7; val get={->x;}; }; return run(new Box);")
            .driver(CodeGen.Phase.FinalizeIR);
        assertEquals("7",Eval2.eval(code,0));
        code=new CodeGen("struct Byte { new Byte={int n -> self.x=n; self.x+=3;}; u8 x; }; return new Byte(255).x;")
            .driver(CodeGen.Phase.FinalizeIR);
        assertEquals("2",Eval2.eval(code,0));
    }
}