Skip to content

Commit 4585e7d

Browse files
committed
lua4jvm: Implement VARIABLE_TRACING pass and re-enable upvalue typing
This should make local functions efficient enough for numerical code! While testing said numerical code, it turned out that lua4jvm's support for using ints for numerical code was, especially mixed with doubles, quite buggy. The bugs are now fixed.
1 parent 88033d3 commit 4585e7d

28 files changed

+236
-64
lines changed

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/LuaVm.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ public LuaModule compile(String chunk) {
101101
public LuaFunction load(LuaModule module, LuaTable env) {
102102
// Instantiate the module
103103
var type = LuaType.function(
104-
// TODO _ENV mutability tracking
105-
List.of(new UpvalueTemplate(module.env(), LuaType.TABLE)),
104+
// TODO _ENV mutability tracking - we need LuaContext, which is a bit tricky here...
105+
List.of(new UpvalueTemplate(module.env(), LuaType.UNKNOWN, true)),
106106
List.of(),
107107
module.root(),
108108
module.name(),

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/CompilerPass.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ public enum CompilerPass {
88
IR_GEN,
99

1010
/**
11-
* In this phase, variables are traced to determine their mutability and
12-
* other properties. TODO not yet implemented
11+
* Return tracking, to determine if lua4jvm has to insert empty returns.
12+
*/
13+
RETURN_TRACKING,
14+
15+
/**
16+
* Variable flagging, based on e.g. their mutability.
1317
*/
1418
VARIABLE_TRACING,
1519

1620
/**
17-
* In analysis phase, types that can be statically inferred are inferred
21+
* In analysis pass, types that can be statically inferred are inferred
1822
* to generate better code later.
1923
*/
2024
TYPE_ANALYSIS,

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/FunctionCompiler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ public static MethodHandle callTarget(LuaType[] argTypes, LuaFunction function,
6363

6464
// Compile and load the function code, or use something that is already cached
6565
var compiledFunc = function.type().specializations().computeIfAbsent(cacheKey, t -> {
66-
CompilerPass.setCurrent(CompilerPass.TYPE_ANALYSIS);
6766
var ctx = LuaContext.forFunction(function.owner(), function.type(), truncateReturn, argTypes);
6867

6968
CompilerPass.setCurrent(CompilerPass.CODEGEN);

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/IrCompiler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,8 @@ public IrNode visitStringConcat(StringConcatContext ctx) {
448448
@Override
449449
public IrNode visitNumberLiteral(NumberLiteralContext ctx) {
450450
var value = Double.valueOf(ctx.Numeral().getText());
451-
return new LuaConstant(value.intValue() == value ? value.intValue() : value);
451+
// Use Math.rint() to handle very large doubles safely
452+
return Math.rint(value) == value ? new LuaConstant(value.intValue()) : new LuaConstant(value);
452453
}
453454

454455
@Override

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/LuaContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,13 @@ public static LuaContext forFunction(LuaVm vm, LuaType.Function type, boolean tr
4545
ctx.setFlag(arg, VariableFlag.ASSIGNED); // JVM assigns arguments to these
4646
}
4747

48+
// Do variable flagging BEFORE type analysis, we need that mutability information
49+
type.body().flagVariables(ctx);
50+
4851
// Compute types of local variables and the return type
52+
CompilerPass.setCurrent(CompilerPass.TYPE_ANALYSIS);
4953
type.body().outputType(ctx);
54+
CompilerPass.setCurrent(null);
5055
return ctx;
5156
}
5257

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/compiler/VariableFlag.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public enum VariableFlag {
1717
/**
1818
* Variable is mutable; that is, it is assigned to at least twice.
1919
*/
20-
MUTABLE(CompilerPass.TYPE_ANALYSIS)
20+
MUTABLE(CompilerPass.VARIABLE_TRACING)
2121
;
2222

2323
/**

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ffi/JavaFunction.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ public Target matchToArgs(LuaType[] argTypes, String intrinsicId) {
8282
if (target.intrinsicId != null && !target.intrinsicId.equals(intrinsicId)) {
8383
continue; // Intrinsic not allowed by caller
8484
}
85-
if (checkArgs(target, argTypes) == MatchResult.SUCCESS) {
85+
var result = checkArgs(target, argTypes);
86+
if (result == MatchResult.SUCCESS || result == MatchResult.INT_DOUBLE_CAST_NEEDED) {
87+
// Linker calls MethodHandle#cast(...), which casts ints to doubles if needed
8688
return target;
8789
}
8890
}
@@ -99,7 +101,8 @@ private enum MatchResult {
99101
SUCCESS,
100102
TOO_FEW_ARGS,
101103
ARG_TYPE_MISMATCH,
102-
VARARGS_TYPE_MISMATCH
104+
VARARGS_TYPE_MISMATCH,
105+
INT_DOUBLE_CAST_NEEDED
103106
}
104107

105108
private MatchResult checkArgs(Target target, LuaType[] argTypes) {
@@ -113,12 +116,18 @@ private MatchResult checkArgs(Target target, LuaType[] argTypes) {
113116
}
114117

115118
// Check types of arguments
119+
var intDoubleCast = false;
116120
for (var i = 0; i < requiredArgs; i++) {
117121
var arg = target.arguments.get(i);
118122
if (!arg.type.isAssignableFrom(argTypes[i])) {
119123
// Allow nil instead of expected type if nullability is allowed
120-
if (!arg.nullable ||!argTypes[i].equals(LuaType.NIL)) {
121-
return MatchResult.ARG_TYPE_MISMATCH;
124+
if (!arg.nullable || !argTypes[i].equals(LuaType.NIL)) {
125+
if (argTypes[i].equals(LuaType.INTEGER) && arg.type.equals(LuaType.FLOAT)) {
126+
// We'll need to cast ints to doubles using MethodHandle magic
127+
intDoubleCast = true;
128+
} else {
129+
return MatchResult.ARG_TYPE_MISMATCH;
130+
}
122131
}
123132
}
124133
}
@@ -133,6 +142,10 @@ private MatchResult checkArgs(Target target, LuaType[] argTypes) {
133142
}
134143
}
135144

145+
if (intDoubleCast) {
146+
return MatchResult.INT_DOUBLE_CAST_NEEDED;
147+
}
148+
136149
return MatchResult.SUCCESS;
137150
}
138151
}

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/DebugInfoNode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ public boolean hasReturn() {
3131
return node.hasReturn();
3232
}
3333

34+
@Override
35+
public void flagVariables(LuaContext ctx) {
36+
node.flagVariables(ctx);
37+
}
38+
3439
@Override
3540
public IrNode concreteNode() {
3641
return node.concreteNode();

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/IrNode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ default boolean hasReturn() {
1414
return false;
1515
}
1616

17+
default void flagVariables(LuaContext ctx) {
18+
// No-op
19+
}
20+
1721
default IrNode concreteNode() {
1822
return this;
1923
}

lua4jvm/src/main/java/fi/benjami/code4jvm/lua/ir/LuaBlock.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,11 @@ public boolean hasReturn() {
3939
return false;
4040
}
4141

42+
@Override
43+
public void flagVariables(LuaContext ctx) {
44+
for (var node : nodes) {
45+
node.flagVariables(ctx);
46+
}
47+
}
48+
4249
}

0 commit comments

Comments
 (0)