Skip to content

Commit 43495f3

Browse files
committed
Scalarize tuples in the Lua backend
1 parent a72f375 commit 43495f3

6 files changed

Lines changed: 444 additions & 25 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -874,12 +874,11 @@ public LuaCompilationUnit transformProgToLua() {
874874

875875
ImAttrType.setWurstClassType(null);
876876
int stage;
877-
if (containsGenericNewCall() || containsTypeClassDispatch()) {
878-
// Both operations need the concrete type argument, which erasure does not keep. Only
879-
// the paths reaching them are specialised: the full elimination used for Jass is
880-
// followed there by class elimination, and leaves state this backend cannot consume.
881-
beginPhase(2, "Specialize generics for generic construction and type class dispatch");
882-
new EliminateGenerics(getImTranslator(), getImProg()).transformGenericNewOnly();
877+
boolean specializeTupleValueTypes = containsTupleTypeArgument();
878+
if (containsGenericNewCall() || containsTypeClassDispatch() || specializeTupleValueTypes) {
879+
beginPhase(2, "Specialize generics for Lua-only concrete operations");
880+
new EliminateGenerics(getImTranslator(), getImProg())
881+
.transformGenericNewOnly(specializeTupleValueTypes);
883882
timeTaker.endPhase();
884883
}
885884
if (runArgs.isNoDebugMessages()) {
@@ -919,6 +918,12 @@ public LuaCompilationUnit transformProgToLua() {
919918
getImProg().flatten(imTranslator2);
920919
EliminateLocalTypes.eliminateLocalTypesProg(getImProg(), imTranslator2);
921920

921+
timeTaker.beginPhase("eliminate tuples");
922+
getImProg().flatten(imTranslator2);
923+
EliminateTuples.eliminateTuplesProg(getImProg(), imTranslator2);
924+
imTranslator2.assertProperties(AssertProperty.NOTUPLES);
925+
timeTaker.endPhase();
926+
922927
optimizer.removeGarbage();
923928
imProg.flatten(imTranslator);
924929
timeTaker.endPhase();
@@ -996,4 +1001,19 @@ public void visit(ImTypeVarDispatch dispatch) {
9961001
});
9971002
return found[0];
9981003
}
1004+
1005+
/** Tuple type arguments need monomorphisation before tuples can become scalar storage. */
1006+
private boolean containsTupleTypeArgument() {
1007+
boolean[] found = {false};
1008+
getImProg().accept(new de.peeeq.wurstscript.jassIm.Element.DefaultVisitor() {
1009+
@Override
1010+
public void visit(ImTypeArgument argument) {
1011+
if (TypesHelper.typeContainsTuples(argument.getType())) {
1012+
found[0] = true;
1013+
}
1014+
super.visit(argument);
1015+
}
1016+
});
1017+
return found[0];
1018+
}
9991019
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import de.peeeq.wurstscript.translation.imtojass.ImAttrType;
1515
import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher;
1616
import de.peeeq.wurstscript.translation.lua.translation.RemoveGarbage;
17+
import de.peeeq.wurstscript.types.TypesHelper;
1718
import io.vavr.control.Either;
1819
import org.eclipse.jdt.annotation.Nullable;
1920
import org.jetbrains.annotations.NotNull;
@@ -29,6 +30,7 @@ public class EliminateGenerics {
2930
private final ImTranslator translator;
3031
private final ImProg prog;
3132
private boolean genericNewOnly;
33+
private boolean specializeTupleValueTypes;
3234
private final Deque<GenericUse> genericsUses = new ArrayDeque<>();
3335
/**
3436
* Call sites already rewritten to a specialisation.
@@ -112,12 +114,21 @@ public void transform() {
112114
}
113115

114116
/**
115-
* Lua normally erases new generics. Generic construction is the one operation which needs the
116-
* concrete type, so only specialize functions on paths leading to {@code wurstNewInstance}. All other
117-
* generic calls and classes keep the Lua backend's normal erased representation.
117+
* Lua normally erases generics. Generic construction and scalar storage for tuple type arguments
118+
* are the operations which need the concrete type, so only specialize paths leading to those
119+
* operations. All other generic calls and classes keep the Lua backend's erased representation.
118120
*/
119121
public void transformGenericNewOnly() {
122+
transformGenericNewOnly(false);
123+
}
124+
125+
public void transformGenericNewOnly(boolean specializeTupleValueTypes) {
120126
genericNewOnly = true;
127+
this.specializeTupleValueTypes = specializeTupleValueTypes;
128+
if (specializeTupleValueTypes) {
129+
addMemberTypeArguments();
130+
identifyGenericGlobals();
131+
}
121132
collectUnspecializedGenericClassMethods();
122133
// Specialising a constructor makes its result type concrete, which is what lets a method
123134
// call on that result resolve. Repeat until a pass finds nothing new; collection is
@@ -133,6 +144,14 @@ public void transformGenericNewOnly() {
133144
assertNoReachableGenericNewMarkers();
134145
bindSpecialisedMethodsToTheAllocatedClass();
135146
settleRemainingDispatches();
147+
if (specializeTupleValueTypes) {
148+
for (Map.Entry<ImFunction, GenericTypes> entry :
149+
new ArrayList<>(specializedFunctionGenerics.entrySet())) {
150+
if (genericTypesContainTuple(entry.getValue())) {
151+
rewriteGenericGlobals(entry.getKey(), entry.getValue());
152+
}
153+
}
154+
}
136155
}
137156

138157
/**
@@ -290,6 +309,26 @@ public void visit(ImMemberAccess memberAccess) {
290309
super.visit(memberAccess);
291310
collectGenericNewUse(memberAccess);
292311
}
312+
313+
@Override
314+
public void visit(ImVarAccess access) {
315+
super.visit(access);
316+
if (specializedContextContainsTuple(access)
317+
&& globalToClass.containsKey(access.getVar())) {
318+
recordGenericGlobalUse(access, access.getVar());
319+
genericsUses.add(new GenericGlobalAccess(access));
320+
}
321+
}
322+
323+
@Override
324+
public void visit(ImVarArrayAccess access) {
325+
super.visit(access);
326+
if (specializedContextContainsTuple(access)
327+
&& globalToClass.containsKey(access.getVar())) {
328+
recordGenericGlobalUse(access, access.getVar());
329+
genericsUses.add(new GenericGlobalArrayAccess(access));
330+
}
331+
}
293332
});
294333
}
295334

@@ -304,7 +343,8 @@ private void collectGenericNewUse(ImFunctionCall call) {
304343
return;
305344
}
306345
if (!call.getTypeArguments().isEmpty()
307-
&& functionNeedsSpecialization(call.getFunc(), Collections.newSetFromMap(new IdentityHashMap<>()))) {
346+
&& (shouldSpecializeTupleArguments(call.getTypeArguments())
347+
|| functionNeedsSpecialization(call.getFunc(), Collections.newSetFromMap(new IdentityHashMap<>())))) {
308348
if (!typeArgumentsContainTypeVariable(call.getTypeArguments())) {
309349
genericsUses.add(new GenericImFunctionCall(call));
310350
}
@@ -347,8 +387,9 @@ private void collectCallThroughGenericReceiver(ImFunctionCall call) {
347387
|| typeArgumentsContainTypeVariable(classType.getTypeArguments())) {
348388
return;
349389
}
350-
if (!functionNeedsSpecialization(call.getFunc(),
351-
Collections.newSetFromMap(new IdentityHashMap<>()))) {
390+
if (!shouldSpecializeTupleArguments(classType.getTypeArguments())
391+
&& !functionNeedsSpecialization(call.getFunc(),
392+
Collections.newSetFromMap(new IdentityHashMap<>()))) {
352393
return;
353394
}
354395
genericsUses.add(new GenericClassFunctionCall(call, owningClass,
@@ -393,7 +434,8 @@ private void collectGenericNewUse(ImAlloc alloc) {
393434
ImClassType clazz = alloc.getClazz();
394435
if (clazz.getTypeArguments().isEmpty()
395436
|| typeArgumentsContainTypeVariable(clazz.getTypeArguments())
396-
|| !isConstructionOnlyInstantiation(clazz.getClassDef())) {
437+
|| (!shouldSpecializeTupleArguments(clazz.getTypeArguments())
438+
&& !isConstructionOnlyInstantiation(clazz.getClassDef()))) {
397439
return;
398440
}
399441
genericsUses.add(new GenericClazzUse(alloc));
@@ -468,7 +510,7 @@ private void collectGenericNewUse(ImMemberAccess memberAccess) {
468510
// A class that has already been specialised has nothing left to select, and asking the
469511
// receiver to adapt to it fails outright: the receiver is still typed by the generic class
470512
// the specialised one was copied from, which is not a superclass of it.
471-
if (owningClass.getTypeVariables().isEmpty() || !isConstructionOnlyInstantiation(owningClass)) {
513+
if (owningClass.getTypeVariables().isEmpty()) {
472514
return;
473515
}
474516
if (memberAccess.getTypeArguments().isEmpty()) {
@@ -479,6 +521,10 @@ private void collectGenericNewUse(ImMemberAccess memberAccess) {
479521
|| typeArgumentsContainTypeVariable(memberAccess.getTypeArguments())) {
480522
return;
481523
}
524+
if (!shouldSpecializeTupleArguments(memberAccess.getTypeArguments())
525+
&& !isConstructionOnlyInstantiation(owningClass)) {
526+
return;
527+
}
482528
genericsUses.add(new GenericMemberAccess(memberAccess));
483529
}
484530

@@ -487,7 +533,8 @@ private void collectGenericNewUse(ImMethodCall call) {
487533
return;
488534
}
489535
ImMethod method = call.getMethod();
490-
if (!methodNeedsSpecialization(method,
536+
if (!shouldSpecializeTupleArguments(call.getTypeArguments())
537+
&& !methodNeedsSpecialization(method,
491538
Collections.newSetFromMap(new IdentityHashMap<>()),
492539
Collections.newSetFromMap(new IdentityHashMap<>()))) {
493540
return;
@@ -550,6 +597,29 @@ private boolean typeArgumentsContainTypeVariable(ImTypeArguments typeArguments)
550597
return false;
551598
}
552599

600+
private boolean typeArgumentsContainTuple(Iterable<ImTypeArgument> typeArguments) {
601+
for (ImTypeArgument typeArgument : typeArguments) {
602+
if (TypesHelper.typeContainsTuples(typeArgument.getType())) {
603+
return true;
604+
}
605+
}
606+
return false;
607+
}
608+
609+
private boolean shouldSpecializeTupleArguments(ImTypeArguments typeArguments) {
610+
return specializeTupleValueTypes && typeArgumentsContainTuple(typeArguments);
611+
}
612+
613+
private boolean genericTypesContainTuple(GenericTypes generics) {
614+
return typeArgumentsContainTuple(generics.getTypeArguments());
615+
}
616+
617+
private boolean specializedContextContainsTuple(Element element) {
618+
ImFunction function = enclosingFunction(element);
619+
GenericTypes generics = function == null ? null : specializedFunctionGenerics.get(function);
620+
return specializeTupleValueTypes && generics != null && genericTypesContainTuple(generics);
621+
}
622+
553623
private boolean functionNeedsSpecialization(ImFunction function, Set<ImFunction> visited) {
554624
return functionNeedsSpecialization(function, visited,
555625
Collections.newSetFromMap(new IdentityHashMap<>()));
@@ -1307,6 +1377,10 @@ private ImFunction specializeFunction(ImFunction f, GenericTypes generics) {
13071377
rewriteGenerics(newF, generics, typeVars);
13081378
}
13091379

1380+
if (genericNewOnly && specializeTupleValueTypes && genericTypesContainTuple(generics)) {
1381+
rewriteGenericGlobals(newF, generics);
1382+
}
1383+
13101384
// Fix calls inside this specialized function so they also point to specialized callees
13111385
if (genericNewOnly) {
13121386
collectGenericNewUses(newF);
@@ -1320,6 +1394,36 @@ private ImFunction specializeFunction(ImFunction f, GenericTypes generics) {
13201394
return newF;
13211395
}
13221396

1397+
private void rewriteGenericGlobals(ImFunction function, GenericTypes generics) {
1398+
function.accept(new Element.DefaultVisitor() {
1399+
@Override
1400+
public void visit(ImVarAccess access) {
1401+
super.visit(access);
1402+
access.setVar(specializedGlobal(access.getVar()));
1403+
}
1404+
1405+
@Override
1406+
public void visit(ImVarArrayAccess access) {
1407+
super.visit(access);
1408+
access.setVar(specializedGlobal(access.getVar()));
1409+
}
1410+
1411+
private ImVar specializedGlobal(ImVar original) {
1412+
ImClass owner = globalToClass.get(original);
1413+
if (owner == null) {
1414+
return original;
1415+
}
1416+
GenericTypes concrete = normalizeToClassArity(generics, owner,
1417+
"specialized function " + function.getName());
1418+
if (concrete == null || concrete.containsTypeVariable()) {
1419+
return original;
1420+
}
1421+
ImVar result = ensureSpecializedGlobal(original, owner, concrete);
1422+
return result == null ? original : result;
1423+
}
1424+
});
1425+
}
1426+
13231427
/**
13241428
* creates a specialized version of this method
13251429
*/
@@ -1398,6 +1502,9 @@ private ImFunction specializeClassFunction(ImFunction function, ImClass owningCl
13981502
newImplementation.getTypeVariables().removeAll();
13991503
newImplementation.setName(function.getName() + "_specialized");
14001504
rewriteGenerics(newImplementation, generics, typeVariables);
1505+
if (specializeTupleValueTypes && genericTypesContainTuple(generics)) {
1506+
rewriteGenericGlobals(newImplementation, generics);
1507+
}
14011508
collectGenericNewUses(newImplementation);
14021509
return newImplementation;
14031510
}

0 commit comments

Comments
 (0)