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
99
100
101
102
103
104
105
106
|
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
// Call a C driver program, which calls a Simple-generated .o, and self-checks.
// Expects GCC from the default CLI, compiles C, compiles Simple, links and runs.
public abstract class TestC {
public static final String OS = System.getProperty("os.name");
public static final String CPU = System.getProperty("os.arch");
public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "Win64" : "SystemV";
public static final String CPU_PORT = switch( CPU ) {
case "amd64" -> "x86_64_v2";
default -> throw Utils.TODO("Map Yer CPU Port Here");
};
public static void run( String file, int spills ) throws IOException { run(file,"",spills); }
public static void run( String file, String expected, int spills ) throws IOException {
run("src/test/java/com/seaofnodes/simple/progs",file,expected,spills);
}
// Compile and run a simple program
public static void run( String dir, String file, String expected, int spills ) throws IOException {
// Files
String cfile = dir+"/"+file+".c" ;
String sfile = dir+"/"+file+".smp";
String efile = "build/objs/"+file;
// Compile and export Simple
String src = Files.readString(Path.of(sfile));
run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills);
}
public static void run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException {
String bin = efile+xtn;
String obj = bin+".o";
String exe = OS.startsWith("Windows") ? bin+".exe" : bin;
// Compile simple, emit ELF
CodeGen code = new CodeGen(src).driver( CPU_PORT, simple_conv, obj);
String result = gcc(obj, c_conv, cfile, false, exe );
assertEquals(expected,result);
// Allocation quality not degraded
int delta = spills>>3;
if( delta==0 ) delta = 1;
if( spills != -1 )
assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
}
public static String gcc( String obj, String c_conv, String cfile, boolean stdin, String... args ) throws IOException {
// Compile the C program. Compiling code and constants in the low
// 2Gig. Pointers are 64b BUT since always in the low 2G all the
// high bits are zero - and Simple code can be emitted treating
// pointers as 4bytes.
var params = new Ary<>(String.class);
params.add("gcc");
if( cfile!=null ) params.add(cfile); // Associated C driver, usually has a `main`
params.addAll(new String[] {
obj,
"-lm", // Picks up 'sqrt' for newtonFloat tests to compare
"-g",
"-o",
args[0],
});
// Calling convention for C calls, if any
if( cfile!=null ) {
params.add("-D");
params.add("CALL_CONV="+c_conv);
}
// Run GCC to link (optionally compile C driver code)
Process gcc = new ProcessBuilder(params.asAry()).redirectErrorStream(true).start();
int exit;
try {
boolean normal = gcc.waitFor(2, TimeUnit.SECONDS);
exit = normal ? gcc.exitValue() : -1; // no exit???
} catch( InterruptedException e ) {
throw new IOException("interrupted");
}
String result = new String(gcc.getInputStream().readAllBytes());
if( exit!=0 ) {
System.err.println("gcc error code: "+exit);
System.err.println(result);
}
assertEquals( 0, exit );
//assertTrue(result.isEmpty()); // No data in error stream
// Execute results
ProcessBuilder smp = new ProcessBuilder(args);
if( stdin ) smp.redirectInput(ProcessBuilder.Redirect.INHERIT);
Process p = smp.start();
try { exit = (byte)p.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); }
result = new String(p.getInputStream().readAllBytes());
if( exit!=0 )
System.err.println("exec exit code: "+exit);
return result;
}
}
|