From b9cbf7487c06cdf5d44de86dcb4210263507388b Mon Sep 17 00:00:00 2001 From: Filip Krikava Date: Sun, 1 Sep 2024 17:09:05 +0000 Subject: [PATCH] Spotless --- client/rsh/test6.R | 5 +- external/R | 2 +- .../java/org/prlprg/bc2c/BC2CCompiler.java | 936 +++++++++--------- .../src/main/java/org/prlprg/bc2c/CFile.java | 56 +- .../main/java/org/prlprg/bc2c/CFunction.java | 72 +- .../java/org/prlprg/bc2c/CompiledModule.java | 3 +- .../java/org/prlprg/service/JITService.java | 93 +- .../org/prlprg/service/NativeClosure.java | 3 +- .../java/org/prlprg/service/RshCompiler.java | 99 +- .../src/main/java/org/prlprg/util/Either.java | 84 +- .../org/prlprg/util/ThrowingRunnable.java | 2 +- .../org/prlprg/bc2c/BC2CCompilerTest.java | 568 +++++------ .../src/test/java/org/prlprg/server/Main.java | 149 ++- .../src/test/java/org/prlprg/util/GNUR.java | 14 +- .../test/resources/org/prlprg/bc2c/Makefile | 34 +- 15 files changed, 1062 insertions(+), 1058 deletions(-) diff --git a/client/rsh/test6.R b/client/rsh/test6.R index 01b3aad46..15a6b90f6 100644 --- a/client/rsh/test6.R +++ b/client/rsh/test6.R @@ -4,7 +4,10 @@ f <- function (n) { c(1,2) + n } -rsh <- rsh:::rsh_cmpfun(f) +rsh <- rsh:::rsh_cmpfun(f, options=list(optimize=3)) f(1) == c(2,3) stopifnot(f(1) == c(2,3)) + +g <- function() .External2("Rsh_call_trampoline", 1) +g() diff --git a/external/R b/external/R index ee40a42e6..5fb4b8dc0 160000 --- a/external/R +++ b/external/R @@ -1 +1 @@ -Subproject commit ee40a42e64777ba6e05c8a578d93fda729e0f94c +Subproject commit 5fb4b8dc05bb8f38e1e34267a0a99f08eebbb905 diff --git a/server/src/main/java/org/prlprg/bc2c/BC2CCompiler.java b/server/src/main/java/org/prlprg/bc2c/BC2CCompiler.java index f4e4ec180..09635ca3c 100644 --- a/server/src/main/java/org/prlprg/bc2c/BC2CCompiler.java +++ b/server/src/main/java/org/prlprg/bc2c/BC2CCompiler.java @@ -3,526 +3,518 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; - -import org.jetbrains.annotations.NotNull; import org.prlprg.bc.*; import org.prlprg.sexp.*; -record Constant(int id, SEXP value) { -} +record Constant(int id, SEXP value) {} -record Value(String expr, boolean protect) { -} +record Value(String expr, boolean protect) {} class ByteCodeStack { - private static final String PROTECT_EXPR = "PROTECT(%s)"; - private static final String UNPROTECT_EXPR = "UNPROTECT(%d)"; + private static final String PROTECT_EXPR = "PROTECT(%s)"; + private static final String UNPROTECT_EXPR = "UNPROTECT(%d)"; - private int max = 0; - private final Stack protects = new Stack<>(); + private int max = 0; + private final Stack protects = new Stack<>(); - public String push(String expr, boolean protect) { - protects.push(0); - int curr = protects.size(); - max = Math.max(max, curr); + public String push(String expr, boolean protect) { + protects.push(0); + int curr = protects.size(); + max = Math.max(max, curr); - return set(curr - 1, expr, protect); - } + return set(curr - 1, expr, protect); + } - public String pop(int n) { - var unprotect = 0; - for (int i = 0; i < n; i++) { - unprotect += protects.pop(); - } - - if (unprotect > 0) { - return UNPROTECT_EXPR.formatted(unprotect); - } else { - return ""; - } + public String pop(int n) { + var unprotect = 0; + for (int i = 0; i < n; i++) { + unprotect += protects.pop(); } - public String curr(int n) { - return register(currIdx(n)); + if (unprotect > 0) { + return UNPROTECT_EXPR.formatted(unprotect); + } else { + return ""; } + } - public int currIdx(int n) { - int curr = protects.size() - 1; + public String curr(int n) { + return register(currIdx(n)); + } - assert curr + n >= 0 && curr + n <= max - : "Invalid offset: %d (curr: %d, max: %d)".formatted(n, curr, max); + public int currIdx(int n) { + int curr = protects.size() - 1; - return curr + n; - } - - public int max() { - return max; - } + assert curr + n >= 0 && curr + n <= max + : "Invalid offset: %d (curr: %d, max: %d)".formatted(n, curr, max); - public boolean isEmpty() { - return protects.isEmpty(); - } + return curr + n; + } - public String set(int i, String expr, boolean protect) { - assert i >= 0 && i <= max : "Invalid index: %d (max: %d)".formatted(i, max); + public int max() { + return max; + } - if (protect) { - expr = protect(i, expr); - } + public boolean isEmpty() { + return protects.isEmpty(); + } - return "%s = %s;".formatted(register(i), expr); - } + public String set(int i, String expr, boolean protect) { + assert i >= 0 && i <= max : "Invalid index: %d (max: %d)".formatted(i, max); - private String protect(int i, String expr) { - protects.set(i, protects.get(i) + 1); - return PROTECT_EXPR.formatted(expr); + if (protect) { + expr = protect(i, expr); } - protected String register(int idx) { - assert idx >= 0; - return "_" + idx; - } + return "%s = %s;".formatted(register(i), expr); + } - protected Optional registerInitialization() { - if (max == 0) { - return Optional.empty(); - } + private String protect(int i, String expr) { + protects.set(i, protects.get(i) + 1); + return PROTECT_EXPR.formatted(expr); + } - var line = - IntStream.range(0, max) - .mapToObj("_%d"::formatted) - .collect(Collectors.joining(", ", "Value ", ";")); + protected String register(int idx) { + assert idx >= 0; + return "_" + idx; + } - return Optional.of(line); + protected Optional registerInitialization() { + if (max == 0) { + return Optional.empty(); } -} -record CompiledClosure(String name, VectorSXP constantPool) { + var line = + IntStream.range(0, max) + .mapToObj("_%d"::formatted) + .collect(Collectors.joining(", ", "Value ", ";")); + + return Optional.of(line); + } } +record CompiledClosure(String name, VectorSXP constantPool) {} + class CModule { - private final List funs = new ArrayList<>(); + private final List funs = new ArrayList<>(); - CFunction createFun(String returnType, String name, String args) { - var fun = new CFunction(returnType, name, args); - funs.add(fun); - return fun; - } + CFunction createFun(String returnType, String name, String args) { + var fun = new CFunction(returnType, name, args); + funs.add(fun); + return fun; + } - CompiledClosure compileClosure(Bc bc) { - var bcHash = Math.abs(bc.hashCode()); - var randomSuffix = UUID.randomUUID().toString().substring(0, 8); - var name = "f_" + bcHash + "_" + randomSuffix; + CompiledClosure compileClosure(Bc bc) { + var bcHash = Math.abs(bc.hashCode()); + var randomSuffix = UUID.randomUUID().toString().substring(0, 8); + var name = "f_" + bcHash + "_" + randomSuffix; - var compiler = new ClosureCompiler(this, name, bc); - var constants = compiler.compile(); + var compiler = new ClosureCompiler(this, name, bc); + var constants = compiler.compile(); - return new CompiledClosure(name, constants); - } + return new CompiledClosure(name, constants); + } - public Iterable funs() { - return funs; - } + public Iterable funs() { + return funs; + } } public class BC2CCompiler { - private final CModule module = new CModule(); - private final Bc bc; + private final CModule module = new CModule(); + private final Bc bc; - public BC2CCompiler(Bc bc) { - this.bc = bc; - } + public BC2CCompiler(Bc bc) { + this.bc = bc; + } - public CompiledModule finish() { - var compiledClosure = module.compileClosure(bc); + public CompiledModule finish() { + var compiledClosure = module.compileClosure(bc); - var file = new CFile(); - file.addInclude("runtime.h"); - module.funs().forEach(fun -> file.addFun(fun, true)); + var file = new CFile(); + file.addInclude("runtime.h"); + module.funs().forEach(fun -> file.addFun(fun, true)); - return new CompiledModule(file, compiledClosure.name(), compiledClosure.constantPool()); - } + return new CompiledModule(file, compiledClosure.name(), compiledClosure.constantPool()); + } } // TODO: extract labels and cells into its own classes class ClosureCompiler { - private static final String NAME_ENV = "ENV"; - private static final String NAME_CP = "CP"; - private static final Value VAL_NULL = new Value("Rsh_NilValue", false); - private static final String VAL_TRUE = "VAL_TRUE"; - private static final String VAL_FALSE = "VAL_FALSE"; - - private final Bc bc; - private final Map constants = new LinkedHashMap<>(); - private final ByteCodeStack stack = new ByteCodeStack(); - private final Set labels = new HashSet<>(); - private final Set cells = new HashSet<>(); - private int extraConstPoolIdx; - - protected CModule module; - protected CFunction fun; - protected CCode body; - - public ClosureCompiler(CModule module, String name, Bc bc) { - this.bc = bc; - this.module = module; - this.fun = module.createFun("SEXP", name, "SEXP %s, SEXP %s".formatted(NAME_ENV, NAME_CP)); - this.body = fun.add(); - this.extraConstPoolIdx = bc.consts().size() + 1; - } - - public VectorSXP compile() { - beforeCompile(); - - var code = bc.code(); - for (int i = 0; i < code.size(); i++) { - compile(code.get(i), i); - } - - afterCompile(); - - return SEXPs.vec(constants()); - } - - private void beforeCompile() { - fillLabels(); - } - - private void afterCompile() { - compileCells(); - compileRegisters(); - } - - private void fillLabels() { - bc.code().forEach(x -> x.labels().forEach(l -> labels.add(l.target()))); - } - - public List constants() { - return List.copyOf(constants.values().stream().map(Constant::value).toList()); - } - - private void compile(BcInstr instr, int instrIdx) { - body.comment("begin: " + instr); - if (labels.contains(instrIdx)) { - body.line("%s:".formatted(label(instrIdx))); - } - - switch (instr) { - case BcInstr.SetVar(var idx) -> compileSetVar(idx); - case BcInstr.SetVar2(var idx) -> compileSetVar2(idx); - case BcInstr.LdConst(var idx) -> compileLd(constantVAL(idx)); - case BcInstr.LdTrue() -> compileLd(VAL_TRUE); - case BcInstr.LdFalse() -> compileLd(VAL_FALSE); - case BcInstr.GetVar(var idx) -> compileGetVar(idx); - case BcInstr.Add(var idx) -> compileArith(idx, "ADD_OP"); - case BcInstr.Sub(var idx) -> compileArith(idx, "SUB_OP"); - case BcInstr.Mul(var idx) -> compileArith(idx, "MUL_OP"); - case BcInstr.Div(var idx) -> compileArith(idx, "DIV_OP"); - case BcInstr.Expt(var idx) -> compileArith(idx, "POW_OP"); - case BcInstr.Lt(var idx) -> compileRelop(idx, "LT_OP"); - case BcInstr.Le(var idx) -> compileRelop(idx, "LE_OP"); - case BcInstr.Gt(var idx) -> compileRelop(idx, "GT_OP"); - case BcInstr.Ge(var idx) -> compileRelop(idx, "GE_OP"); - case BcInstr.Eq(var idx) -> compileRelop(idx, "EQ_OP"); - case BcInstr.Ne(var idx) -> compileRelop(idx, "NE_OP"); - case BcInstr.Exp(var idx) -> compileMath1(idx, "EXP_OP"); - case BcInstr.Sqrt(var idx) -> compileMath1(idx, "SQRT_OP"); - case BcInstr.UPlus(var idx) -> compileUnary(idx, "UPLUS_OP"); - case BcInstr.UMinus(var idx) -> compileUnary(idx, "UMINUS_OP"); - case BcInstr.And(var idx) -> compileLogic(idx, "AND_OP"); - case BcInstr.Or(var idx) -> compileLogic(idx, "OR_OP"); - case BcInstr.Not(var idx) -> compileNot(idx); - case BcInstr.Return() -> compileReturn(); - case BcInstr.Pop() -> pop(1); - case BcInstr.GetBuiltin(var idx) -> compileGetBuiltin(idx); - case BcInstr.PushConstArg(var idx) -> compilePushConstArg(idx); - case BcInstr.CallBuiltin(var idx) -> compileCall(idx); - case BcInstr.Call(var idx) -> compileCall(idx); - case BcInstr.PushArg() -> compilePushArg(); - case BcInstr.SetTag(var idx) -> compileSetTag(idx); - case BcInstr.BrIfNot(var call, var label) -> compileBrIfNot(call, label); - case BcInstr.Goto(var label) -> compileGoto(label); - case BcInstr.Invisible() -> compileInvisible(); - case BcInstr.LdNull() -> compileLdNull(); - case BcInstr.GetFun(var idx) -> compileGetFun(idx); - case BcInstr.MakeClosure(var idx) -> compileMakeClosure(idx); - case BcInstr.CheckFun() -> compileCheckFun(); - case BcInstr.MakeProm(var idx) -> compileMakeProm(idx); - - default -> throw new UnsupportedOperationException(instr + ": not supported"); - } - body.comment("end: " + instr); - body.nl(); - } - - private void compileMakeProm(ConstPool.Idx idx) { - body.line("Rsh_make_prom(%s, &%s, &%s, %s, %s);".formatted( - stack.curr(-2), - stack.curr(-1), - stack.curr(0), - constantSXP(idx), - NAME_ENV) - ); - } - - private void compileCheckFun() { - body.line("Rsh_check_fun(%s);".formatted(stack.curr(0))); - initCallFrame(); - } - - private void compileMakeClosure(ConstPool.Idx idx) { - var cls = bc.consts().get(idx); - - if (cls.get(1) instanceof BCodeSXP closureBody) { - var compiledClosure = module.compileClosure(closureBody.bc()); - var cpConst = createExtraConstant(compiledClosure.constantPool()); - push("Rsh_native_closure(%s, &%s, %s, %s)".formatted(constantSXP(idx), compiledClosure.name(), constantSXP(cpConst), NAME_ENV), false); - } else { - throw new UnsupportedOperationException("Unsupported body: " + body); - } - } - - private void compileCall(ConstPool.Idx idx) { - var call = constantSXP(idx); - var fun = stack.curr(-2); - var args = stack.curr(-1); - - // FIXME: how do we signal back the whether the value needs protection? - var c = "Rsh_call(%s, %s, %s, %s)".formatted(call, fun, args, NAME_ENV); - // we are going to pop 4 elements from the stack - all the until the beginning of the call frame - popPush(3, c, false); - } - - private void compileGetFun(ConstPool.Idx idx) { - push("Rsh_getFun(%s, %s)".formatted(constantSXP(idx), NAME_ENV), false); - initCallFrame(); - } - - private void compileLdNull() { - visible(true); - push(VAL_NULL); - } - - private void compileInvisible() { - visible(false); - } - - private void compileGoto(BcLabel label) { - body.line("goto %s;".formatted(label(label.target()))); - } - - private void compileBrIfNot(ConstPool.Idx call, BcLabel label) { - var curr = stack.curr(0); - var unprotect = stack.pop(1); - body.line( - "if (!Rsh_is_true(%s, %s, %s)) { %s; goto %s; }" - .formatted(curr, constantSXP(call), NAME_ENV, unprotect, label(label.target()))); - body.line(unprotect + ";"); - } - - private void compileSetTag(ConstPool.Idx idx) { - body.line( - """ + private static final String NAME_ENV = "ENV"; + private static final String NAME_CP = "CP"; + private static final Value VAL_NULL = new Value("Rsh_NilValue", false); + private static final String VAL_TRUE = "VAL_TRUE"; + private static final String VAL_FALSE = "VAL_FALSE"; + + private final Bc bc; + private final Map constants = new LinkedHashMap<>(); + private final ByteCodeStack stack = new ByteCodeStack(); + private final Set labels = new HashSet<>(); + private final Set cells = new HashSet<>(); + private int extraConstPoolIdx; + + protected CModule module; + protected CFunction fun; + protected CCode body; + + public ClosureCompiler(CModule module, String name, Bc bc) { + this.bc = bc; + this.module = module; + this.fun = module.createFun("SEXP", name, "SEXP %s, SEXP %s".formatted(NAME_ENV, NAME_CP)); + this.body = fun.add(); + this.extraConstPoolIdx = bc.consts().size() + 1; + } + + public VectorSXP compile() { + beforeCompile(); + + var code = bc.code(); + for (int i = 0; i < code.size(); i++) { + compile(code.get(i), i); + } + + afterCompile(); + + return SEXPs.vec(constants()); + } + + private void beforeCompile() { + fillLabels(); + } + + private void afterCompile() { + compileCells(); + compileRegisters(); + } + + private void fillLabels() { + bc.code().forEach(x -> x.labels().forEach(l -> labels.add(l.target()))); + } + + public List constants() { + return List.copyOf(constants.values().stream().map(Constant::value).toList()); + } + + private void compile(BcInstr instr, int instrIdx) { + body.comment("begin: " + instr); + if (labels.contains(instrIdx)) { + body.line("%s:".formatted(label(instrIdx))); + } + + switch (instr) { + case BcInstr.SetVar(var idx) -> compileSetVar(idx); + case BcInstr.SetVar2(var idx) -> compileSetVar2(idx); + case BcInstr.LdConst(var idx) -> compileLd(constantVAL(idx)); + case BcInstr.LdTrue() -> compileLd(VAL_TRUE); + case BcInstr.LdFalse() -> compileLd(VAL_FALSE); + case BcInstr.GetVar(var idx) -> compileGetVar(idx); + case BcInstr.Add(var idx) -> compileArith(idx, "ADD_OP"); + case BcInstr.Sub(var idx) -> compileArith(idx, "SUB_OP"); + case BcInstr.Mul(var idx) -> compileArith(idx, "MUL_OP"); + case BcInstr.Div(var idx) -> compileArith(idx, "DIV_OP"); + case BcInstr.Expt(var idx) -> compileArith(idx, "POW_OP"); + case BcInstr.Lt(var idx) -> compileRelop(idx, "LT_OP"); + case BcInstr.Le(var idx) -> compileRelop(idx, "LE_OP"); + case BcInstr.Gt(var idx) -> compileRelop(idx, "GT_OP"); + case BcInstr.Ge(var idx) -> compileRelop(idx, "GE_OP"); + case BcInstr.Eq(var idx) -> compileRelop(idx, "EQ_OP"); + case BcInstr.Ne(var idx) -> compileRelop(idx, "NE_OP"); + case BcInstr.Exp(var idx) -> compileMath1(idx, "EXP_OP"); + case BcInstr.Sqrt(var idx) -> compileMath1(idx, "SQRT_OP"); + case BcInstr.UPlus(var idx) -> compileUnary(idx, "UPLUS_OP"); + case BcInstr.UMinus(var idx) -> compileUnary(idx, "UMINUS_OP"); + case BcInstr.And(var idx) -> compileLogic(idx, "AND_OP"); + case BcInstr.Or(var idx) -> compileLogic(idx, "OR_OP"); + case BcInstr.Not(var idx) -> compileNot(idx); + case BcInstr.Return() -> compileReturn(); + case BcInstr.Pop() -> pop(1); + case BcInstr.GetBuiltin(var idx) -> compileGetBuiltin(idx); + case BcInstr.PushConstArg(var idx) -> compilePushConstArg(idx); + case BcInstr.CallBuiltin(var idx) -> compileCall(idx); + case BcInstr.Call(var idx) -> compileCall(idx); + case BcInstr.PushArg() -> compilePushArg(); + case BcInstr.SetTag(var idx) -> compileSetTag(idx); + case BcInstr.BrIfNot(var call, var label) -> compileBrIfNot(call, label); + case BcInstr.Goto(var label) -> compileGoto(label); + case BcInstr.Invisible() -> compileInvisible(); + case BcInstr.LdNull() -> compileLdNull(); + case BcInstr.GetFun(var idx) -> compileGetFun(idx); + case BcInstr.MakeClosure(var idx) -> compileMakeClosure(idx); + case BcInstr.CheckFun() -> compileCheckFun(); + case BcInstr.MakeProm(var idx) -> compileMakeProm(idx); + + default -> throw new UnsupportedOperationException(instr + ": not supported"); + } + body.comment("end: " + instr); + body.nl(); + } + + private void compileMakeProm(ConstPool.Idx idx) { + body.line( + "Rsh_make_prom(%s, &%s, &%s, %s, %s);" + .formatted(stack.curr(-2), stack.curr(-1), stack.curr(0), constantSXP(idx), NAME_ENV)); + } + + private void compileCheckFun() { + body.line("Rsh_check_fun(%s);".formatted(stack.curr(0))); + initCallFrame(); + } + + private void compileMakeClosure(ConstPool.Idx idx) { + var cls = bc.consts().get(idx); + + if (cls.get(1) instanceof BCodeSXP closureBody) { + var compiledClosure = module.compileClosure(closureBody.bc()); + var cpConst = createExtraConstant(compiledClosure.constantPool()); + push( + "Rsh_native_closure(%s, &%s, %s, %s)" + .formatted(constantSXP(idx), compiledClosure.name(), constantSXP(cpConst), NAME_ENV), + false); + } else { + throw new UnsupportedOperationException("Unsupported body: " + body); + } + } + + private void compileCall(ConstPool.Idx idx) { + var call = constantSXP(idx); + var fun = stack.curr(-2); + var args = stack.curr(-1); + + // FIXME: how do we signal back the whether the value needs protection? + var c = "Rsh_call(%s, %s, %s, %s)".formatted(call, fun, args, NAME_ENV); + // we are going to pop 4 elements from the stack - all the until the beginning of the call frame + popPush(3, c, false); + } + + private void compileGetFun(ConstPool.Idx idx) { + push("Rsh_getFun(%s, %s)".formatted(constantSXP(idx), NAME_ENV), false); + initCallFrame(); + } + + private void compileLdNull() { + visible(true); + push(VAL_NULL); + } + + private void compileInvisible() { + visible(false); + } + + private void compileGoto(BcLabel label) { + body.line("goto %s;".formatted(label(label.target()))); + } + + private void compileBrIfNot(ConstPool.Idx call, BcLabel label) { + var curr = stack.curr(0); + var unprotect = stack.pop(1); + body.line( + "if (!Rsh_is_true(%s, %s, %s)) { %s; goto %s; }" + .formatted(curr, constantSXP(call), NAME_ENV, unprotect, label(label.target()))); + body.line(unprotect + ";"); + } + + private void compileSetTag(ConstPool.Idx idx) { + body.line( + """ if (TYPEOF(%s) != SPECIALSXP) { RSH_SET_TAG(%s, %s); }""" - .formatted(stack.curr(-2), stack.curr(0), constantVAL(idx))); - } - - private void compilePushArg() { - body.line( - "RSH_LIST_APPEND(%s, %s, %s);".formatted(stack.curr(-2), stack.curr(-1), stack.curr(0))); - pop(1); - } - - private void compileRegisters() { - var code = stack.registerInitialization(); - if (code.isPresent()) { - var sec = fun.insertAbove(body); - sec.line(code.get()); - } - } - - private void compileCells() { - if (cells.isEmpty()) { - return; - } - - var sec = fun.insertAbove(body); - var line = - cells.stream() - .map("C%d = R_NilValue"::formatted) - .collect(Collectors.joining(", ", "BCell ", ";")); - sec.line(line); - } - - private void compilePushConstArg(ConstPool.Idx idx) { - body.line( - "RSH_LIST_APPEND(%s, %s, %s);".formatted(stack.curr(-1), stack.curr(0), constantVAL(idx))); - } - - private void compileGetBuiltin(ConstPool.Idx idx) { - var name = bc.consts().get(idx).name(); - push("Rsh_get_builtin(\"%s\")".formatted(name), false); - initCallFrame(); - } - - private void initCallFrame() { - push(VAL_NULL); - push(VAL_NULL); - } - - private void compileGetVar(ConstPool.Idx idx) { - push( - "Rsh_get_var(%s, %s, FALSE, FALSE, &%s)".formatted(constantSXP(idx), NAME_ENV, cell(idx)), - false); - } - - private void compileSetVar(ConstPool.Idx idx) { - body.line( - "Rsh_set_var(%s, %s, %s, &%s);" - .formatted(constantSXP(idx), stack.curr(0), NAME_ENV, cell(idx))); - } - - private void compileSetVar2(ConstPool.Idx idx) { - body.line( - "Rsh_set_var2(%s, %s, %s);" - .formatted(constantSXP(idx), stack.curr(0), NAME_ENV)); - } - - private void compileReturn() { - pop(1); - assert stack.isEmpty() : "Stack not empty (%d)".formatted(stack.currIdx(0)); - body.line("return Rsh_return(%s);".formatted(stack.curr(1))); - } - - // FIXME: refactor - private void compileMath1(ConstPool.Idx idx, String op) { - var call = constantSXP(idx); - var arg = stack.curr(0); - popPush(1, "Rsh_math1(%s, %s, %s, %s)".formatted(call, op, arg, NAME_ENV), false); - } - - private void compileUnary(ConstPool.Idx idx, String op) { - var call = constantSXP(idx); - var arg = stack.curr(0); - popPush(1, "Rsh_unary(%s, %s, %s, %s)".formatted(call, op, arg, NAME_ENV), false); - } - - private void compileNot(ConstPool.Idx idx) { - var call = constantSXP(idx); - var arg = stack.curr(0); - popPush(1, "Rsh_not(%s, %s, %s)".formatted(call, arg, NAME_ENV), false); - } - - private void compileArith(ConstPool.Idx idx, String op) { - var call = constantSXP(idx); - var lhs = stack.curr(-1); - var rhs = stack.curr(0); - popPush(2, "Rsh_arith(%s, %s, %s, %s, %s)".formatted(call, op, lhs, rhs, NAME_ENV), false); - } - - private void compileLogic(ConstPool.Idx idx, String op) { - var call = constantSXP(idx); - var lhs = stack.curr(-1); - var rhs = stack.curr(0); - popPush(2, "Rsh_logic(%s, %s, %s, %s, %s)".formatted(call, op, lhs, rhs, NAME_ENV), false); - } - - private void compileRelop(ConstPool.Idx idx, String op) { - var call = constantSXP(idx); - var lhs = stack.curr(-1); - var rhs = stack.curr(0); - popPush(2, "Rsh_relop(%s, %s, %s, %s, %s)".formatted(call, op, lhs, rhs, NAME_ENV), false); - } - - private void compileLd(String constant) { - push(constant, false); - } - - // API - - private void pop(int n) { - var unprotect = stack.pop(n); - if (!unprotect.isEmpty()) { - body.line(unprotect + ";"); - } - } - - private String push(String expr, boolean protect) { - body.line(stack.push(expr, protect)); - return stack.curr(0); - } - - private String push(String expr) { - return push(expr, true); - } - - private void push(Value value) { - push(value.expr(), value.protect()); - } - - public void popPush(int n, String expr, boolean protect) { - set(stack.currIdx(-n + 1), expr, false); - pop(n); - push(stack.curr(1), protect); - } - - private void set(int i, String expr, boolean protect) { - body.line(stack.set(i, expr, protect)); - } - - private String constantSXP(ConstPool.Idx idx) { - var c = getConstant(idx); - return constantSXP(c); - } - - private String constantSXP(Constant c) { - return "Rsh_const(%s, %d)".formatted(NAME_CP, c.id()); - } - - private String constantVAL(ConstPool.Idx idx) { - var c = getConstant(idx); - - var f = - switch (c.value()) { - case IntSXP v when v.size() == 1 -> "Rsh_const_int"; - case RealSXP v when v.size() == 1 -> "Rsh_const_dbl"; - case LglSXP v when v.size() == 1 -> "Rsh_const_lgl"; - case SEXP _ -> "Rsh_const_sxp"; - }; - - return "%s(%s, %d)".formatted(f, NAME_CP, c.id()); - } - - private Constant getConstant(ConstPool.Idx idx) { - return constants.computeIfAbsent( - idx.idx(), - ignored -> { - var next = constants.size(); - return new Constant(next, bc.consts().get(idx)); - }); - } - - private Constant createExtraConstant(SEXP v) { - var next = constants.size(); - var c = new Constant(next, v); - constants.put(extraConstPoolIdx++, c); - return c; - } - - private String label(int instrIndex) { - labels.add(instrIndex); - return "L%d".formatted(instrIndex); - } - - private String cell(ConstPool.Idx idx) { - var id = getConstant(idx).id(); - cells.add(id); - return "C%d".formatted(id); - } - - private void visible(boolean visible) { - body.line("R_Visible = %s;".formatted(visible ? "TRUE" : "FALSE")); - } + .formatted(stack.curr(-2), stack.curr(0), constantVAL(idx))); + } + + private void compilePushArg() { + body.line( + "RSH_LIST_APPEND(%s, %s, %s);".formatted(stack.curr(-2), stack.curr(-1), stack.curr(0))); + pop(1); + } + + private void compileRegisters() { + var code = stack.registerInitialization(); + if (code.isPresent()) { + var sec = fun.insertAbove(body); + sec.line(code.get()); + } + } + + private void compileCells() { + if (cells.isEmpty()) { + return; + } + + var sec = fun.insertAbove(body); + var line = + cells.stream() + .map("C%d = R_NilValue"::formatted) + .collect(Collectors.joining(", ", "BCell ", ";")); + sec.line(line); + } + + private void compilePushConstArg(ConstPool.Idx idx) { + body.line( + "RSH_LIST_APPEND(%s, %s, %s);".formatted(stack.curr(-1), stack.curr(0), constantVAL(idx))); + } + + private void compileGetBuiltin(ConstPool.Idx idx) { + var name = bc.consts().get(idx).name(); + push("Rsh_get_builtin(\"%s\")".formatted(name), false); + initCallFrame(); + } + + private void initCallFrame() { + push(VAL_NULL); + push(VAL_NULL); + } + + private void compileGetVar(ConstPool.Idx idx) { + push( + "Rsh_get_var(%s, %s, FALSE, FALSE, &%s)".formatted(constantSXP(idx), NAME_ENV, cell(idx)), + false); + } + + private void compileSetVar(ConstPool.Idx idx) { + body.line( + "Rsh_set_var(%s, %s, %s, &%s);" + .formatted(constantSXP(idx), stack.curr(0), NAME_ENV, cell(idx))); + } + + private void compileSetVar2(ConstPool.Idx idx) { + body.line("Rsh_set_var2(%s, %s, %s);".formatted(constantSXP(idx), stack.curr(0), NAME_ENV)); + } + + private void compileReturn() { + pop(1); + assert stack.isEmpty() : "Stack not empty (%d)".formatted(stack.currIdx(0)); + body.line("return Rsh_return(%s);".formatted(stack.curr(1))); + } + + // FIXME: refactor + private void compileMath1(ConstPool.Idx idx, String op) { + var call = constantSXP(idx); + var arg = stack.curr(0); + popPush(1, "Rsh_math1(%s, %s, %s, %s)".formatted(call, op, arg, NAME_ENV), false); + } + + private void compileUnary(ConstPool.Idx idx, String op) { + var call = constantSXP(idx); + var arg = stack.curr(0); + popPush(1, "Rsh_unary(%s, %s, %s, %s)".formatted(call, op, arg, NAME_ENV), false); + } + + private void compileNot(ConstPool.Idx idx) { + var call = constantSXP(idx); + var arg = stack.curr(0); + popPush(1, "Rsh_not(%s, %s, %s)".formatted(call, arg, NAME_ENV), false); + } + + private void compileArith(ConstPool.Idx idx, String op) { + var call = constantSXP(idx); + var lhs = stack.curr(-1); + var rhs = stack.curr(0); + popPush(2, "Rsh_arith(%s, %s, %s, %s, %s)".formatted(call, op, lhs, rhs, NAME_ENV), false); + } + + private void compileLogic(ConstPool.Idx idx, String op) { + var call = constantSXP(idx); + var lhs = stack.curr(-1); + var rhs = stack.curr(0); + popPush(2, "Rsh_logic(%s, %s, %s, %s, %s)".formatted(call, op, lhs, rhs, NAME_ENV), false); + } + + private void compileRelop(ConstPool.Idx idx, String op) { + var call = constantSXP(idx); + var lhs = stack.curr(-1); + var rhs = stack.curr(0); + popPush(2, "Rsh_relop(%s, %s, %s, %s, %s)".formatted(call, op, lhs, rhs, NAME_ENV), false); + } + + private void compileLd(String constant) { + push(constant, false); + } + + // API + + private void pop(int n) { + var unprotect = stack.pop(n); + if (!unprotect.isEmpty()) { + body.line(unprotect + ";"); + } + } + + private String push(String expr, boolean protect) { + body.line(stack.push(expr, protect)); + return stack.curr(0); + } + + private String push(String expr) { + return push(expr, true); + } + + private void push(Value value) { + push(value.expr(), value.protect()); + } + + public void popPush(int n, String expr, boolean protect) { + set(stack.currIdx(-n + 1), expr, false); + pop(n); + push(stack.curr(1), protect); + } + + private void set(int i, String expr, boolean protect) { + body.line(stack.set(i, expr, protect)); + } + + private String constantSXP(ConstPool.Idx idx) { + var c = getConstant(idx); + return constantSXP(c); + } + + private String constantSXP(Constant c) { + return "Rsh_const(%s, %d)".formatted(NAME_CP, c.id()); + } + + private String constantVAL(ConstPool.Idx idx) { + var c = getConstant(idx); + + var f = + switch (c.value()) { + case IntSXP v when v.size() == 1 -> "Rsh_const_int"; + case RealSXP v when v.size() == 1 -> "Rsh_const_dbl"; + case LglSXP v when v.size() == 1 -> "Rsh_const_lgl"; + case SEXP _ -> "Rsh_const_sxp"; + }; + + return "%s(%s, %d)".formatted(f, NAME_CP, c.id()); + } + + private Constant getConstant(ConstPool.Idx idx) { + return constants.computeIfAbsent( + idx.idx(), + ignored -> { + var next = constants.size(); + return new Constant(next, bc.consts().get(idx)); + }); + } + + private Constant createExtraConstant(SEXP v) { + var next = constants.size(); + var c = new Constant(next, v); + constants.put(extraConstPoolIdx++, c); + return c; + } + + private String label(int instrIndex) { + labels.add(instrIndex); + return "L%d".formatted(instrIndex); + } + + private String cell(ConstPool.Idx idx) { + var id = getConstant(idx).id(); + cells.add(id); + return "C%d".formatted(id); + } + + private void visible(boolean visible) { + body.line("R_Visible = %s;".formatted(visible ? "TRUE" : "FALSE")); + } } diff --git a/server/src/main/java/org/prlprg/bc2c/CFile.java b/server/src/main/java/org/prlprg/bc2c/CFile.java index 4a924a578..2c414a993 100644 --- a/server/src/main/java/org/prlprg/bc2c/CFile.java +++ b/server/src/main/java/org/prlprg/bc2c/CFile.java @@ -7,34 +7,34 @@ import java.util.List; public class CFile { - private final List funs = new ArrayList<>(); - private final List forwards = new ArrayList<>(); - private final List includes = new ArrayList<>(); - - public void writeTo(Writer w) { - var pw = new PrintWriter(w); - - includes.forEach(x -> pw.println("#include <" + x + ">")); - pw.println(); - forwards.stream().map(CFunction::getDeclaration).forEach(x -> pw.println(x + ";")); - - funs.forEach(x -> x.writeTo(pw)); - } - - public String toString() { - var w = new StringWriter(); - writeTo(new PrintWriter(w)); - return w.toString(); - } - - public void addFun(CFunction fun, boolean forwardDeclare) { - funs.add(fun); - if (forwardDeclare) { - forwards.add(fun); - } + private final List funs = new ArrayList<>(); + private final List forwards = new ArrayList<>(); + private final List includes = new ArrayList<>(); + + public void writeTo(Writer w) { + var pw = new PrintWriter(w); + + includes.forEach(x -> pw.println("#include <" + x + ">")); + pw.println(); + forwards.stream().map(CFunction::getDeclaration).forEach(x -> pw.println(x + ";")); + + funs.forEach(x -> x.writeTo(pw)); + } + + public String toString() { + var w = new StringWriter(); + writeTo(new PrintWriter(w)); + return w.toString(); + } + + public void addFun(CFunction fun, boolean forwardDeclare) { + funs.add(fun); + if (forwardDeclare) { + forwards.add(fun); } + } - public void addInclude(String include) { - includes.add(include); - } + public void addInclude(String include) { + includes.add(include); + } } diff --git a/server/src/main/java/org/prlprg/bc2c/CFunction.java b/server/src/main/java/org/prlprg/bc2c/CFunction.java index 479e676bd..457f7cbdc 100644 --- a/server/src/main/java/org/prlprg/bc2c/CFunction.java +++ b/server/src/main/java/org/prlprg/bc2c/CFunction.java @@ -6,48 +6,48 @@ import java.util.List; public class CFunction { - private final String returnType; - private final String name; - private final String parameters; - private final List sections = new ArrayList<>(); + private final String returnType; + private final String name; + private final String parameters; + private final List sections = new ArrayList<>(); - CFunction(String returnType, String name, String parameters) { - this.returnType = returnType; - this.name = name; - this.parameters = parameters; - } + CFunction(String returnType, String name, String parameters) { + this.returnType = returnType; + this.name = name; + this.parameters = parameters; + } - public CCode add() { - var s = new CCode(); - sections.add(s); - return s; - } + public CCode add() { + var s = new CCode(); + sections.add(s); + return s; + } - public void writeTo(Writer w) { - var pw = new PrintWriter(w); - pw.format("%s {", getDeclaration()); + public void writeTo(Writer w) { + var pw = new PrintWriter(w); + pw.format("%s {", getDeclaration()); + pw.println(); + for (int i = 0; i < sections.size(); i++) { + sections.get(i).writeTo(w); + if (i < sections.size() - 1) { pw.println(); - for (int i = 0; i < sections.size(); i++) { - sections.get(i).writeTo(w); - if (i < sections.size() - 1) { - pw.println(); - } - } - pw.println("}"); - pw.flush(); + } } + pw.println("}"); + pw.flush(); + } - public CCode insertAbove(CCode sec) { - var i = sections.indexOf(sec); - if (i == -1) { - throw new IllegalArgumentException("Section " + sec + " does not exist in fun " + this); - } - var s = new CCode(); - sections.add(i, s); - return s; + public CCode insertAbove(CCode sec) { + var i = sections.indexOf(sec); + if (i == -1) { + throw new IllegalArgumentException("Section " + sec + " does not exist in fun " + this); } + var s = new CCode(); + sections.add(i, s); + return s; + } - public String getDeclaration() { - return String.format("%s %s(%s)", returnType, name, parameters); - } + public String getDeclaration() { + return String.format("%s %s(%s)", returnType, name, parameters); + } } diff --git a/server/src/main/java/org/prlprg/bc2c/CompiledModule.java b/server/src/main/java/org/prlprg/bc2c/CompiledModule.java index 50e441668..26aa19455 100644 --- a/server/src/main/java/org/prlprg/bc2c/CompiledModule.java +++ b/server/src/main/java/org/prlprg/bc2c/CompiledModule.java @@ -3,5 +3,4 @@ import org.prlprg.sexp.SEXP; import org.prlprg.sexp.VectorSXP; -public record CompiledModule(CFile file, String topLevelFunName, VectorSXP constantPool) { -} +public record CompiledModule(CFile file, String topLevelFunName, VectorSXP constantPool) {} diff --git a/server/src/main/java/org/prlprg/service/JITService.java b/server/src/main/java/org/prlprg/service/JITService.java index cd7ccff51..7ac4e62dd 100644 --- a/server/src/main/java/org/prlprg/service/JITService.java +++ b/server/src/main/java/org/prlprg/service/JITService.java @@ -1,56 +1,63 @@ package org.prlprg.service; import com.google.common.io.Files; - import java.io.File; import java.nio.charset.Charset; import java.util.logging.Logger; - import org.prlprg.RSession; import org.prlprg.bc.BCCompiler; import org.prlprg.bc2c.BC2CCompiler; import org.prlprg.sexp.CloSXP; public class JITService { - private final RSession rsession; - - private static final Logger logger = Logger.getLogger(JITService.class.getName()); - - public JITService(RSession rsession) { - this.rsession = rsession; - } - - public NativeClosure execute(String name, CloSXP closure, int bcOptimization, int ccOptimization) - throws Exception { - logger.fine("Compiling closure: " + name + "\n" + closure + "(bcOpt=" + bcOptimization + ", ccOpt=" + ccOptimization + ")\n"); - - var bcCompiler = new BCCompiler(closure, rsession); - bcCompiler.setOptimizationLevel(bcOptimization); - var bc = bcCompiler.compile().get(); - var bc2cCompiler = new BC2CCompiler(bc); - var module = bc2cCompiler.finish(); - // var input = new File("/tmp/jit.c"); - var input = File.createTempFile("cfile", ".c"); - var f = Files.newWriter(input, Charset.defaultCharset()); - module.file().writeTo(f); - // var output = new File("/tmp/jit.o"); - var output = File.createTempFile("ofile", ".o"); - - RshCompiler.getInstance(ccOptimization) - .createBuilder(input.getPath(), output.getPath()) - .flag("-c") - .compile(); - - var res = Files.asByteSource(output).read(); - - // if (!input.delete()) { - // throw new IOException("Unable to delete file" + input); - // } - - // if (!output.delete()) { - // throw new IOException("Unable to delete file" + input); - // } - - return new NativeClosure(res, module.topLevelFunName(), module.constantPool()); - } + private final RSession rsession; + + private static final Logger logger = Logger.getLogger(JITService.class.getName()); + + public JITService(RSession rsession) { + this.rsession = rsession; + } + + public NativeClosure execute(String name, CloSXP closure, int bcOptimization, int ccOptimization) + throws Exception { + logger.fine( + "Compiling closure: " + + name + + "\n" + + closure + + "(bcOpt=" + + bcOptimization + + ", ccOpt=" + + ccOptimization + + ")\n"); + + var bcCompiler = new BCCompiler(closure, rsession); + bcCompiler.setOptimizationLevel(bcOptimization); + var bc = bcCompiler.compile().get(); + var bc2cCompiler = new BC2CCompiler(bc); + var module = bc2cCompiler.finish(); + // var input = new File("/tmp/jit.c"); + var input = File.createTempFile("cfile", ".c"); + var f = Files.newWriter(input, Charset.defaultCharset()); + module.file().writeTo(f); + // var output = new File("/tmp/jit.o"); + var output = File.createTempFile("ofile", ".o"); + + RshCompiler.getInstance(ccOptimization) + .createBuilder(input.getPath(), output.getPath()) + .flag("-c") + .compile(); + + var res = Files.asByteSource(output).read(); + + // if (!input.delete()) { + // throw new IOException("Unable to delete file" + input); + // } + + // if (!output.delete()) { + // throw new IOException("Unable to delete file" + input); + // } + + return new NativeClosure(res, module.topLevelFunName(), module.constantPool()); + } } diff --git a/server/src/main/java/org/prlprg/service/NativeClosure.java b/server/src/main/java/org/prlprg/service/NativeClosure.java index 753fb9ef8..82248fb7e 100644 --- a/server/src/main/java/org/prlprg/service/NativeClosure.java +++ b/server/src/main/java/org/prlprg/service/NativeClosure.java @@ -3,5 +3,4 @@ import org.prlprg.sexp.SEXP; import org.prlprg.sexp.VectorSXP; -public record NativeClosure(byte[] code, String name, VectorSXP constantPool) { -} +public record NativeClosure(byte[] code, String name, VectorSXP constantPool) {} diff --git a/server/src/main/java/org/prlprg/service/RshCompiler.java b/server/src/main/java/org/prlprg/service/RshCompiler.java index 31e9abd3d..cc070cdff 100644 --- a/server/src/main/java/org/prlprg/service/RshCompiler.java +++ b/server/src/main/java/org/prlprg/service/RshCompiler.java @@ -1,69 +1,68 @@ package org.prlprg.service; -import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.logging.Level; import java.util.logging.Logger; - import org.prlprg.util.cc.CCCompilationBuilder; public class RshCompiler { - private static final Logger logger = Logger.getLogger(RshCompiler.class.getName()); - - // TODO: this is just temporary - // what we need is to keep this in the resources, versioned by R version - // and upon server instantiation, copy it to some temp directory - // and precompile the header file (if needed) - private static final Path RSH_INCLUDE_PATH = Paths.get("../client/rsh/src/bc2c").normalize().toAbsolutePath(); - private static final Path R_INCLUDE_PATH = Paths.get("../external/R/include").normalize().toAbsolutePath(); + private static final Logger logger = Logger.getLogger(RshCompiler.class.getName()); - // TODO: which ones are needed? - private static final List COMMON_COMPILER_FLAGS = - List.of( - "-I" + RSH_INCLUDE_PATH, - "-I" + R_INCLUDE_PATH, - "-fpic", - "-fno-plt", - "-fexceptions", - "-fstack-clash-protection", - "-fcf-protection", - "-flto=auto", - "-ffat-lto-objects", - "-pedantic", - "-Wformat", - "-Werror=format-security", - "-Wall", - "-Wno-unused-but-set-variable", - "-Wno-comment", - "-DRSH"); + // TODO: this is just temporary + // what we need is to keep this in the resources, versioned by R version + // and upon server instantiation, copy it to some temp directory + // and precompile the header file (if needed) + private static final Path RSH_INCLUDE_PATH = + Paths.get("../client/rsh/src/bc2c").normalize().toAbsolutePath(); + private static final Path R_INCLUDE_PATH = + Paths.get("../external/R/include").normalize().toAbsolutePath(); - private final List compilerFlags; + // TODO: which ones are needed? + private static final List COMMON_COMPILER_FLAGS = + List.of( + "-I" + RSH_INCLUDE_PATH, + "-I" + R_INCLUDE_PATH, + "-fpic", + "-fno-plt", + "-fexceptions", + "-fstack-clash-protection", + "-fcf-protection", + "-flto=auto", + "-ffat-lto-objects", + "-pedantic", + "-Wformat", + "-Werror=format-security", + "-Wall", + "-Wno-unused-but-set-variable", + "-Wno-comment", + "-DRSH"); - public RshCompiler(List compilerFlags) { - this.compilerFlags = compilerFlags; - } + private final List compilerFlags; - public static RshCompiler getInstance(int optimizationLevel) { - var flags = new ArrayList<>(COMMON_COMPILER_FLAGS); + public RshCompiler(List compilerFlags) { + this.compilerFlags = compilerFlags; + } - if (optimizationLevel == 0) { - flags.add("-g3"); - flags.add("-ggdb"); - flags.add("-Wno-unused-function"); - } else { - flags.add("-DNDEBUG"); - flags.add("-g"); - } - flags.add("-O" + optimizationLevel); + public static RshCompiler getInstance(int optimizationLevel) { + var flags = new ArrayList<>(COMMON_COMPILER_FLAGS); - // TODO: cache - return new RshCompiler(flags); + if (optimizationLevel == 0) { + flags.add("-g3"); + flags.add("-ggdb"); + flags.add("-Wno-unused-function"); + } else { + flags.add("-DNDEBUG"); + flags.add("-g"); } + flags.add("-O" + optimizationLevel); - public CCCompilationBuilder createBuilder(String input, String output) { - return new CCCompilationBuilder(input, output).flags(compilerFlags); - } + // TODO: cache + return new RshCompiler(flags); + } + + public CCCompilationBuilder createBuilder(String input, String output) { + return new CCCompilationBuilder(input, output).flags(compilerFlags); + } } diff --git a/server/src/main/java/org/prlprg/util/Either.java b/server/src/main/java/org/prlprg/util/Either.java index 5344a3b4f..ea521e5fd 100644 --- a/server/src/main/java/org/prlprg/util/Either.java +++ b/server/src/main/java/org/prlprg/util/Either.java @@ -4,63 +4,63 @@ @SuppressWarnings("MissingJavadoc") public sealed interface Either permits Left, Right { - static Either left(L left) { - return new Left<>(left); - } + static Either left(L left) { + return new Left<>(left); + } - static Either right(R right) { - return new Right<>(right); - } + static Either right(R right) { + return new Right<>(right); + } - boolean isLeft(); + boolean isLeft(); - boolean isRight(); + boolean isRight(); - L getLeft(); + L getLeft(); - R getRight(); + R getRight(); } record Left(L left) implements Either { - @Override - public boolean isLeft() { - return true; - } + @Override + public boolean isLeft() { + return true; + } - @Override - public boolean isRight() { - return false; - } + @Override + public boolean isRight() { + return false; + } - @Override - public L getLeft() { - return left; - } + @Override + public L getLeft() { + return left; + } - @Override - public R getRight() { - throw new NoSuchElementException("This either contains left value"); - } + @Override + public R getRight() { + throw new NoSuchElementException("This either contains left value"); + } } record Right(R right) implements Either { - @Override - public boolean isLeft() { - return false; - } + @Override + public boolean isLeft() { + return false; + } - @Override - public boolean isRight() { - return true; - } + @Override + public boolean isRight() { + return true; + } - @Override - public L getLeft() { - throw new NoSuchElementException("This either contains right value"); - } + @Override + public L getLeft() { + throw new NoSuchElementException("This either contains right value"); + } - @Override - public R getRight() { - return right; - } + @Override + public R getRight() { + return right; + } } diff --git a/server/src/main/java/org/prlprg/util/ThrowingRunnable.java b/server/src/main/java/org/prlprg/util/ThrowingRunnable.java index 46755c066..8ea982594 100644 --- a/server/src/main/java/org/prlprg/util/ThrowingRunnable.java +++ b/server/src/main/java/org/prlprg/util/ThrowingRunnable.java @@ -15,7 +15,7 @@ default void run() { try { runWithException(); } catch (RuntimeException e) { - throw e; + throw e; } catch (Exception e) { throw new RuntimeException(e); } diff --git a/server/src/test/java/org/prlprg/bc2c/BC2CCompilerTest.java b/server/src/test/java/org/prlprg/bc2c/BC2CCompilerTest.java index e0b7e37b7..934f8fcdd 100644 --- a/server/src/test/java/org/prlprg/bc2c/BC2CCompilerTest.java +++ b/server/src/test/java/org/prlprg/bc2c/BC2CCompilerTest.java @@ -6,7 +6,6 @@ import java.io.IOException; import java.util.Objects; import java.util.function.Consumer; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; @@ -22,143 +21,150 @@ @Execution(ExecutionMode.CONCURRENT) public class BC2CCompilerTest extends AbstractGNURBasedTest { - @Test - public void testReturn() throws Exception { - verify("42", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); - } - - // @Test - // public void testGetVar() throws Exception { - // verify("f <- function (x) { x }; f(42)", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); - // } - - @Test - public void testSetVar() throws Exception { - verify("x <- 42; x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); - } - - // @Test - // public void testGetAndSetVar() throws Exception { - // verify( - // """ - // function (x) { y <- x; y } - // """, - // "list(x=42)", - // (RealSXP v) -> { - // assertEquals(42.0, v.asReal(0)); - // }); - // } - - @Test - public void testSetVar2() throws Exception { - verify("y <- 42; x <- y; x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); - } - - @Test - public void testAdd() throws Exception { - verify("x <- 42; x + 21", (RealSXP v) -> assertEquals(63.0, v.asReal(0))); - - verify("x <- 42L; x + 21L", (IntSXP v) -> assertEquals(63, v.asInt(0))); - - verify("x <- 42L; x + 21", (RealSXP v) -> assertEquals(63.0, v.asReal(0))); - - verify( - "x <- 42; x + c(1, 2)", - (RealSXP v) -> { - assertEquals(2, v.size()); - assertEquals(43.0, v.asReal(0)); - assertEquals(44.0, v.asReal(1)); - }); - - verify( - "x <- c(42, 43); x + c(1, 2)", - (RealSXP v) -> { - assertEquals(2, v.size()); - assertEquals(43.0, v.asReal(0)); - assertEquals(45.0, v.asReal(1)); - }); - } - - @Test - public void testRealScalarArith() throws Exception { - verify("x <- 42; x + 21", (RealSXP v) -> assertEquals(63.0, v.asReal(0))); - verify("x <- 42; x - 21", (RealSXP v) -> assertEquals(21.0, v.asReal(0))); - verify("x <- 42; x * 2", (RealSXP v) -> assertEquals(84.0, v.asReal(0))); - verify("x <- 42; x / 2", (RealSXP v) -> assertEquals(21.0, v.asReal(0))); - verify("x <- 42; x ^ 2", (RealSXP v) -> assertEquals(1764.0, v.asReal(0))); - } - - @Test - public void testIntScalarArith() throws Exception { - verify("x <- 42L; x + 21L", (IntSXP v) -> assertEquals(63, v.asInt(0))); - verify("x <- 42L; x - 21L", (IntSXP v) -> assertEquals(21, v.asInt(0))); - verify("x <- 42L; x * 2L", (IntSXP v) -> assertEquals(84, v.asInt(0))); - verify("x <- 42L; x / 2L", (RealSXP v) -> assertEquals(21.0, v.asReal(0))); - } - - // TODO: do some property based testing including NA using R as an oracle - - @Test - public void testArithBuiltins() throws Exception { - verify("x <- 42; x %% 5", (RealSXP v) -> assertEquals(2.0, v.asReal(0))); - verify("x <- 42; x %/% 5", (RealSXP v) -> assertEquals(8.0, v.asReal(0))); - } - - @Test - public void testMath1Builtins() throws Exception { - verify("sqrt(4)", (RealSXP v) -> assertEquals(2.0, v.asReal(0))); - verify("exp(0)", (RealSXP v) -> assertEquals(1.0, v.asReal(0))); - } - - @Test - public void testUnaryBuiltins() throws Exception { - verify("x <- 42; +x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); - verify("x <- 42; -x", (RealSXP v) -> assertEquals(-42.0, v.asReal(0))); - verify("x <- -42; +x", (RealSXP v) -> assertEquals(-42.0, v.asReal(0))); - verify("x <- -42; -x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); - verify("x <- 42L; +x", (IntSXP v) -> assertEquals(42, v.asInt(0))); - verify("x <- 42L; -x", (IntSXP v) -> assertEquals(-42, v.asInt(0))); - verify("x <- -42L; +x", (IntSXP v) -> assertEquals(-42, v.asInt(0))); - verify("x <- -42L; -x", (IntSXP v) -> assertEquals(42, v.asInt(0))); - verify("x <- c(1, -2); -x", (RealSXP v) -> assertArrayEquals(new Double[]{-1.0, 2.0}, v.coerceTo(Double.class))); - } - - @Test - public void testScalarCompare() throws Exception { - verify("x <- 42; x < 100", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - verify("x <- 42; x > 100", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); - verify("x <- 42; x <= 42", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - verify("x <- 42; x >= 42", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - verify("x <- 42; x == 42", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - verify("x <- 42; x == 100", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); - verify("x <- 42; x != 42", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); - verify("x <- 42; x != 100", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - } - - @Test - public void testBooleanOperators() throws Exception { - verify("x <- TRUE; y <- FALSE; x & y", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); - verify("x <- TRUE; y <- FALSE; x | y", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - verify("x <- TRUE; !x", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); - verify("x <- 42; !!x", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - verify("x <- c(T,F,T,F); y <- c(T,T,F,F); x | y", (LglSXP v) -> assertArrayEquals(new Logical[]{Logical.TRUE, Logical.TRUE, Logical.TRUE, Logical.FALSE}, v.coerceTo(Logical.class))); - } - - @Test - public void testClosure() throws Exception { - verify( - """ + @Test + public void testReturn() throws Exception { + verify("42", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); + } + + // @Test + // public void testGetVar() throws Exception { + // verify("f <- function (x) { x }; f(42)", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); + // } + + @Test + public void testSetVar() throws Exception { + verify("x <- 42; x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); + } + + // @Test + // public void testGetAndSetVar() throws Exception { + // verify( + // """ + // function (x) { y <- x; y } + // """, + // "list(x=42)", + // (RealSXP v) -> { + // assertEquals(42.0, v.asReal(0)); + // }); + // } + + @Test + public void testSetVar2() throws Exception { + verify("y <- 42; x <- y; x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); + } + + @Test + public void testAdd() throws Exception { + verify("x <- 42; x + 21", (RealSXP v) -> assertEquals(63.0, v.asReal(0))); + + verify("x <- 42L; x + 21L", (IntSXP v) -> assertEquals(63, v.asInt(0))); + + verify("x <- 42L; x + 21", (RealSXP v) -> assertEquals(63.0, v.asReal(0))); + + verify( + "x <- 42; x + c(1, 2)", + (RealSXP v) -> { + assertEquals(2, v.size()); + assertEquals(43.0, v.asReal(0)); + assertEquals(44.0, v.asReal(1)); + }); + + verify( + "x <- c(42, 43); x + c(1, 2)", + (RealSXP v) -> { + assertEquals(2, v.size()); + assertEquals(43.0, v.asReal(0)); + assertEquals(45.0, v.asReal(1)); + }); + } + + @Test + public void testRealScalarArith() throws Exception { + verify("x <- 42; x + 21", (RealSXP v) -> assertEquals(63.0, v.asReal(0))); + verify("x <- 42; x - 21", (RealSXP v) -> assertEquals(21.0, v.asReal(0))); + verify("x <- 42; x * 2", (RealSXP v) -> assertEquals(84.0, v.asReal(0))); + verify("x <- 42; x / 2", (RealSXP v) -> assertEquals(21.0, v.asReal(0))); + verify("x <- 42; x ^ 2", (RealSXP v) -> assertEquals(1764.0, v.asReal(0))); + } + + @Test + public void testIntScalarArith() throws Exception { + verify("x <- 42L; x + 21L", (IntSXP v) -> assertEquals(63, v.asInt(0))); + verify("x <- 42L; x - 21L", (IntSXP v) -> assertEquals(21, v.asInt(0))); + verify("x <- 42L; x * 2L", (IntSXP v) -> assertEquals(84, v.asInt(0))); + verify("x <- 42L; x / 2L", (RealSXP v) -> assertEquals(21.0, v.asReal(0))); + } + + // TODO: do some property based testing including NA using R as an oracle + + @Test + public void testArithBuiltins() throws Exception { + verify("x <- 42; x %% 5", (RealSXP v) -> assertEquals(2.0, v.asReal(0))); + verify("x <- 42; x %/% 5", (RealSXP v) -> assertEquals(8.0, v.asReal(0))); + } + + @Test + public void testMath1Builtins() throws Exception { + verify("sqrt(4)", (RealSXP v) -> assertEquals(2.0, v.asReal(0))); + verify("exp(0)", (RealSXP v) -> assertEquals(1.0, v.asReal(0))); + } + + @Test + public void testUnaryBuiltins() throws Exception { + verify("x <- 42; +x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); + verify("x <- 42; -x", (RealSXP v) -> assertEquals(-42.0, v.asReal(0))); + verify("x <- -42; +x", (RealSXP v) -> assertEquals(-42.0, v.asReal(0))); + verify("x <- -42; -x", (RealSXP v) -> assertEquals(42.0, v.asReal(0))); + verify("x <- 42L; +x", (IntSXP v) -> assertEquals(42, v.asInt(0))); + verify("x <- 42L; -x", (IntSXP v) -> assertEquals(-42, v.asInt(0))); + verify("x <- -42L; +x", (IntSXP v) -> assertEquals(-42, v.asInt(0))); + verify("x <- -42L; -x", (IntSXP v) -> assertEquals(42, v.asInt(0))); + verify( + "x <- c(1, -2); -x", + (RealSXP v) -> assertArrayEquals(new Double[] {-1.0, 2.0}, v.coerceTo(Double.class))); + } + + @Test + public void testScalarCompare() throws Exception { + verify("x <- 42; x < 100", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + verify("x <- 42; x > 100", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); + verify("x <- 42; x <= 42", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + verify("x <- 42; x >= 42", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + verify("x <- 42; x == 42", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + verify("x <- 42; x == 100", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); + verify("x <- 42; x != 42", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); + verify("x <- 42; x != 100", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + } + + @Test + public void testBooleanOperators() throws Exception { + verify("x <- TRUE; y <- FALSE; x & y", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); + verify("x <- TRUE; y <- FALSE; x | y", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + verify("x <- TRUE; !x", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); + verify("x <- 42; !!x", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + verify( + "x <- c(T,F,T,F); y <- c(T,T,F,F); x | y", + (LglSXP v) -> + assertArrayEquals( + new Logical[] {Logical.TRUE, Logical.TRUE, Logical.TRUE, Logical.FALSE}, + v.coerceTo(Logical.class))); + } + + @Test + public void testClosure() throws Exception { + verify( + """ y <- 21 f <- function (x) { x + y } f(42) """, - (RealSXP v) -> assertEquals(63.0, v.asReal(0))); - } + (RealSXP v) -> assertEquals(63.0, v.asReal(0))); + } - @Test - public void testNestedClosures() throws Exception { - verify( - """ + @Test + public void testNestedClosures() throws Exception { + verify( + """ a <- 1 f <- function(z) { c <- 2 @@ -173,24 +179,24 @@ public void testNestedClosures() throws Exception { } f(10)(30) """, - (RealSXP v) -> assertEquals(66.0, v.asReal(0))); - } - - @Test - public void testCall() throws Exception { - verify( - "timestamp()", - (StrSXP v) -> { - assertEquals(1, v.size()); - assertTrue(v.get(0).startsWith("##------")); - assertTrue(v.get(0).endsWith("------##")); - }); - } - - @Test - public void testSumIn0Loop() throws Exception { - verify( - """ + (RealSXP v) -> assertEquals(66.0, v.asReal(0))); + } + + @Test + public void testCall() throws Exception { + verify( + "timestamp()", + (StrSXP v) -> { + assertEquals(1, v.size()); + assertTrue(v.get(0).startsWith("##------")); + assertTrue(v.get(0).endsWith("------##")); + }); + } + + @Test + public void testSumIn0Loop() throws Exception { + verify( + """ n <- 100 s <- 0 i <- 0 @@ -200,136 +206,138 @@ public void testSumIn0Loop() throws Exception { } s """, - (RealSXP v) -> assertEquals(4950.0, v.asReal(0))); + (RealSXP v) -> assertEquals(4950.0, v.asReal(0))); + } + + @Test + public void testNA() throws Exception { + verify("x <- TRUE; y <- x; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); + verify("x <- FALSE; y <- x; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); + verify("x <- NA; y <- x; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + verify("y <- NA_integer_; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); + } + + @Test + public void testPromise() throws Exception { + verify("f <- function(x) x + 1; y <- 2; f(y*2)", (RealSXP v) -> assertEquals(5, v.asInt(0))); + } + + // @Test + // public void testList(TestInfo info) throws Exception { + // compileAndCall( + // """ + // function (x) { list(1,2,3,x=x) } + // """, + // "list(x=4)", + // (VecSXP v) -> { + // assertArrayEquals(new Double[] {1.0, 2.0, 3.0, 4.0}, v.coerceTo(Double.class)); + // assertEquals("x", v.names().get(3)); + // }); + // } + // + // @Test + // public void testEq(TestInfo info) throws Exception { + // compileAndCall( + // """ + // function (x) { x == 1 } + // """, + // "list(x=1)", + // (LglSXP v) -> { + // assertEquals(SEXPs.TRUE, v); + // }); + // } + // + // @Test + // public void testIfElse(TestInfo info) throws Exception { + // compileAndCall( + // """ + // function (x) { if (x == 1) 1 else if (x == 2) 2 else 3 } + // """, + // "list(x=2)", + // (RealSXP v) -> { + // assertEquals(2.0, v.asReal(0)); + // assertEquals(1, v.size()); + // }); + // } + + record TestArtifact(Either result, File tempDir) { + public void destroy() throws IOException { + Files.deleteRecursively(tempDir.toPath()); } - - @Test - public void testNA() throws Exception { - verify("x <- TRUE; y <- x; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); - verify("x <- FALSE; y <- x; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.FALSE, v)); - verify("x <- NA; y <- x; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - verify("y <- NA_integer_; is.na(y)", (LglSXP v) -> assertEquals(SEXPs.TRUE, v)); - } - - @Test - public void testPromise() throws Exception { - verify("f <- function(x) x + 1; y <- 2; f(y*2)", (RealSXP v) -> assertEquals(5, v.asInt(0))); + } + + TestArtifact compileAndCall(String code) throws Exception { + // this has to be the same as in the test/resources/.../Makefile + var prefix = "test"; + + var tempDir = Files.createTempDirectory("test-bc2cc").toFile(); + var cFile = new File(tempDir, prefix + ".c"); + var cpFile = new File(tempDir, prefix + ".RDS"); + var soFile = new File(tempDir, prefix + ".so"); + var rFile = new File(tempDir, prefix + ".R"); + + Files.clearDirectory(tempDir.toPath()); + + var funCode = "function() {" + code + "}"; + var closure = (CloSXP) R.eval(funCode); + var ast2bc = new BCCompiler(closure, rsession); + + // FIXME: just for now as we do not support guards + ast2bc.setOptimizationLevel(3); + var bc = + ast2bc + .compile() + .orElseThrow(() -> new RuntimeException("Compilation did not produce byte code")); + + try { + var bc2c = new BC2CCompiler(bc); + var module = bc2c.finish(); + + RDSWriter.writeFile(cpFile, module.constantPool()); + + Files.writeString(cFile.toPath(), module.file().toString()); + + RshCompiler.getInstance(0) + .createBuilder(cFile.getPath(), soFile.getPath()) + .flag("-shared") + .flag("-DRSH_TESTS") + .compile(); + + String testDriver = + "dyn.load('%s')\n".formatted(soFile.getAbsolutePath()) + + "cp <- readRDS('%s')\n".formatted(cpFile.getAbsolutePath()) + + "env <- new.env()\n" + + "parent.env(env) <- globalenv()\n" + + "invisible(.Call('Rsh_initialize_runtime'))\n" + + "res <- .Call('%s', env, cp)\n".formatted(module.topLevelFunName()) + + "dyn.unload('%s')\n".formatted(soFile.getAbsolutePath()) + + "res\n"; + + Files.writeString(rFile.toPath(), testDriver); + + var res = R.eval("source('%s', local=F)$value".formatted(rFile.getAbsolutePath())); + + return new TestArtifact<>(Either.right((T) res), tempDir); + } catch (Exception e) { + return new TestArtifact<>(Either.left(e), tempDir); } - // @Test - // public void testList(TestInfo info) throws Exception { - // compileAndCall( - // """ - // function (x) { list(1,2,3,x=x) } - // """, - // "list(x=4)", - // (VecSXP v) -> { - // assertArrayEquals(new Double[] {1.0, 2.0, 3.0, 4.0}, v.coerceTo(Double.class)); - // assertEquals("x", v.names().get(3)); - // }); - // } - // - // @Test - // public void testEq(TestInfo info) throws Exception { - // compileAndCall( - // """ - // function (x) { x == 1 } - // """, - // "list(x=1)", - // (LglSXP v) -> { - // assertEquals(SEXPs.TRUE, v); - // }); - // } - // - // @Test - // public void testIfElse(TestInfo info) throws Exception { - // compileAndCall( - // """ - // function (x) { if (x == 1) 1 else if (x == 2) 2 else 3 } - // """, - // "list(x=2)", - // (RealSXP v) -> { - // assertEquals(2.0, v.asReal(0)); - // assertEquals(1, v.size()); - // }); - // } - - record TestArtifact(Either result, File tempDir) { - public void destroy() throws IOException { - Files.deleteRecursively(tempDir.toPath()); - } - } - - TestArtifact compileAndCall(String code) throws Exception { - // this has to be the same as in the test/resources/.../Makefile - var prefix = "test"; - - var tempDir = Files.createTempDirectory("test-bc2cc").toFile(); - var cFile = new File(tempDir, prefix + ".c"); - var cpFile = new File(tempDir, prefix + ".RDS"); - var soFile = new File(tempDir, prefix + ".so"); - var rFile = new File(tempDir, prefix + ".R"); - - Files.clearDirectory(tempDir.toPath()); - - var funCode = "function() {" + code + "}"; - var closure = (CloSXP) R.eval(funCode); - var ast2bc = new BCCompiler(closure, rsession); - - // FIXME: just for now as we do not support guards - ast2bc.setOptimizationLevel(3); - var bc = - ast2bc - .compile() - .orElseThrow(() -> new RuntimeException("Compilation did not produce byte code")); - - try { - var bc2c = new BC2CCompiler(bc); - var module = bc2c.finish(); - - RDSWriter.writeFile(cpFile, module.constantPool()); - - Files.writeString(cFile.toPath(), module.file().toString()); - - RshCompiler.getInstance(0) - .createBuilder(cFile.getPath(), soFile.getPath()) - .flag("-shared") - .flag("-DRSH_TESTS") - .compile(); - - String testDriver = - "dyn.load('%s')\n".formatted(soFile.getAbsolutePath()) - + "cp <- readRDS('%s')\n".formatted(cpFile.getAbsolutePath()) - + "env <- new.env()\n" - + "parent.env(env) <- globalenv()\n" - + "invisible(.Call('Rsh_initialize_runtime'))\n" - + "res <- .Call('%s', env, cp)\n".formatted(module.topLevelFunName()) - + "dyn.unload('%s')\n".formatted(soFile.getAbsolutePath()) - + "res\n"; - - Files.writeString(rFile.toPath(), testDriver); - - var res = R.eval("source('%s', local=F)$value".formatted(rFile.getAbsolutePath())); - - return new TestArtifact<>(Either.right((T) res), tempDir); - } catch (Exception e) { - return new TestArtifact<>(Either.left(e), tempDir); - } - } - - void verify(String code, Consumer validator) throws Exception { - TestArtifact artifact = compileAndCall(code); - try { - if (artifact.result.isLeft()) { - throw artifact.result.getLeft(); - } else { - validator.accept(artifact.result.getRight()); - artifact.destroy(); - } - } catch (Throwable e) { - var makeFile = new File(artifact.tempDir, "Makefile"); - - Files.copyURL(Objects.requireNonNull(getClass().getResource("Makefile")), makeFile.toPath()); - throw new RuntimeException("Test failed - compilation dir: " + artifact.tempDir.getAbsolutePath(), e); - } + } + + void verify(String code, Consumer validator) throws Exception { + TestArtifact artifact = compileAndCall(code); + try { + if (artifact.result.isLeft()) { + throw artifact.result.getLeft(); + } else { + validator.accept(artifact.result.getRight()); + artifact.destroy(); + } + } catch (Throwable e) { + var makeFile = new File(artifact.tempDir, "Makefile"); + + Files.copyURL(Objects.requireNonNull(getClass().getResource("Makefile")), makeFile.toPath()); + throw new RuntimeException( + "Test failed - compilation dir: " + artifact.tempDir.getAbsolutePath(), e); } + } } diff --git a/server/src/test/java/org/prlprg/server/Main.java b/server/src/test/java/org/prlprg/server/Main.java index c3c149a5a..ae04ed5c9 100644 --- a/server/src/test/java/org/prlprg/server/Main.java +++ b/server/src/test/java/org/prlprg/server/Main.java @@ -1,7 +1,6 @@ package org.prlprg.server; import com.google.protobuf.ByteString; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -9,7 +8,6 @@ import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; - import org.prlprg.RSession; import org.prlprg.rds.RDSReader; import org.prlprg.rds.RDSWriter; @@ -21,90 +19,89 @@ import org.prlprg.service.JITService; import org.prlprg.sexp.CloSXP; import org.prlprg.sexp.SEXP; -import org.prlprg.sexp.SEXPs; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; public class Main { - static { - var logger = Logger.getLogger("org.prlprg"); - logger.setLevel(Level.ALL); - var consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.ALL); - logger.addHandler(consoleHandler); - } - - private static final Logger logger = Logger.getLogger(Main.class.getName()); - private final RSession rsession; - private final JITService jit; - - public Main() { - rsession = new TestRSession(); - jit = new JITService(rsession); - } - - void run() { - try (ZContext context = new ZContext()) { - ZMQ.Socket socket = context.createSocket(SocketType.REP); - socket.bind("tcp://*:5555"); - logger.info("Listening on " + socket.getLastEndpoint()); - - while (!Thread.currentThread().isInterrupted()) { - Request request = Request.parseFrom(socket.recv(0)); - logger.info("Got a request: " + request); - - switch (request.getPayloadCase()) { - case COMPILE -> { - var response = compile(request.getCompile()); - socket.send(response.toByteArray()); - } - default -> { - logger.severe("Unknown request type: " + request); - } - } - } - } catch (Exception e) { - e.printStackTrace(System.err); - System.exit(1); + static { + var logger = Logger.getLogger("org.prlprg"); + logger.setLevel(Level.ALL); + var consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.ALL); + logger.addHandler(consoleHandler); + } + + private static final Logger logger = Logger.getLogger(Main.class.getName()); + private final RSession rsession; + private final JITService jit; + + public Main() { + rsession = new TestRSession(); + jit = new JITService(rsession); + } + + void run() { + try (ZContext context = new ZContext()) { + ZMQ.Socket socket = context.createSocket(SocketType.REP); + socket.bind("tcp://*:5555"); + logger.info("Listening on " + socket.getLastEndpoint()); + + while (!Thread.currentThread().isInterrupted()) { + Request request = Request.parseFrom(socket.recv(0)); + logger.info("Got a request: " + request); + + switch (request.getPayloadCase()) { + case COMPILE -> { + var response = compile(request.getCompile()); + socket.send(response.toByteArray()); + } + default -> { + logger.severe("Unknown request type: " + request); + } } + } + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(1); } - - private CompileResponse compile(CompileRequest compile) { - try { - var name = compile.getName(); - var closure = deserialize(new ByteArrayInputStream(compile.getClosure().toByteArray())); - var nativeClosure = jit.execute(name, closure, compile.getBcOptimization(), compile.getCcOptimization()); - - var result = - CompiledFunction.newBuilder() - .setName(nativeClosure.name()) - .setNativeCode(ByteString.copyFrom(nativeClosure.code())) - .setConstants( - ByteString.copyFrom(serialize(nativeClosure.constantPool()))); - - var response = CompileResponse.newBuilder().setResult(result); - - return response.build(); - } catch (Exception e) { - logger.severe("Unable to process request: " + e.getMessage()); - var response = CompileResponse.newBuilder().setFailure(e.getMessage()); - return response.build(); - } + } + + private CompileResponse compile(CompileRequest compile) { + try { + var name = compile.getName(); + var closure = deserialize(new ByteArrayInputStream(compile.getClosure().toByteArray())); + var nativeClosure = + jit.execute(name, closure, compile.getBcOptimization(), compile.getCcOptimization()); + + var result = + CompiledFunction.newBuilder() + .setName(nativeClosure.name()) + .setNativeCode(ByteString.copyFrom(nativeClosure.code())) + .setConstants(ByteString.copyFrom(serialize(nativeClosure.constantPool()))); + + var response = CompileResponse.newBuilder().setResult(result); + + return response.build(); + } catch (Exception e) { + logger.severe("Unable to process request: " + e.getMessage()); + var response = CompileResponse.newBuilder().setFailure(e.getMessage()); + return response.build(); } + } - private CloSXP deserialize(InputStream input) throws IOException { - return (CloSXP) RDSReader.readStream(rsession, input); - } + private CloSXP deserialize(InputStream input) throws IOException { + return (CloSXP) RDSReader.readStream(rsession, input); + } - private byte[] serialize(SEXP data) throws IOException { - var output = new ByteArrayOutputStream(); - RDSWriter.writeStream(output, data); - return output.toByteArray(); - } + private byte[] serialize(SEXP data) throws IOException { + var output = new ByteArrayOutputStream(); + RDSWriter.writeStream(output, data); + return output.toByteArray(); + } - public static void main(String[] args) { - new Main().run(); - } + public static void main(String[] args) { + new Main().run(); + } } diff --git a/server/src/test/java/org/prlprg/util/GNUR.java b/server/src/test/java/org/prlprg/util/GNUR.java index 96f97c8ff..f3e78b05a 100644 --- a/server/src/test/java/org/prlprg/util/GNUR.java +++ b/server/src/test/java/org/prlprg/util/GNUR.java @@ -143,13 +143,13 @@ public static GNUR spawn(RSession session) { version = versionStr.substring("R version ".length()).split(" ", 2)[0]; if (!version.equals(session.version())) { throw new RuntimeException( - "R version can't be used for compiler tests: expected version " - + session.version() - + " but found " - + version - + " (R_BIN = " - + R_BIN - + ")"); + "R version can't be used for compiler tests: expected version " + + session.version() + + " but found " + + version + + " (R_BIN = " + + R_BIN + + ")"); } } else if (versionStr.startsWith("R Under development (unstable)")) { // OK -- this should be a bundled version diff --git a/server/src/test/resources/org/prlprg/bc2c/Makefile b/server/src/test/resources/org/prlprg/bc2c/Makefile index 6b20f8d83..d9e13b4e5 100644 --- a/server/src/test/resources/org/prlprg/bc2c/Makefile +++ b/server/src/test/resources/org/prlprg/bc2c/Makefile @@ -3,23 +3,23 @@ R_HOME := $(RSH_HOME)/external/R R := $(R_HOME)/bin/R CC := gcc CFLAGS := -I$(RSH_HOME)/client/rsh/src/bc2c \ - -I$(R_HOME)/include \ - -fpic \ - -fno-plt \ - -fexceptions \ - -fstack-clash-protection \ - -fcf-protection \ - -flto=auto \ - -ffat-lto-objects \ - -Wformat \ - -Werror=format-security \ - -Wall \ - -Winline \ - -Wno-unused-but-set-variable \ - -Wno-comments \ - -pedantic \ - -shared \ - -DRSH_TESTS + -I$(R_HOME)/include \ + -fpic \ + -fno-plt \ + -fexceptions \ + -fstack-clash-protection \ + -fcf-protection \ + -flto=auto \ + -ffat-lto-objects \ + -Wformat \ + -Werror=format-security \ + -Wall \ + -Winline \ + -Wno-unused-but-set-variable \ + -Wno-comments \ + -pedantic \ + -shared \ + -DRSH_TESTS DRIVER := test.R SOURCE := test.c