"XXX is not a function" - dead code elemination issue?
See original GitHub issueProlog
I have forked the teavm-libgdx project and updated TeaVM + libgdx. The Invaders demo works but I have problem with Super Jumper demo.
Issue
Many methods simply disappear from the Matrix4
class in output code. This results in error:
cbgm_Matrix4_tmpMat.$setToTranslation is not a function
Indeed, cbgm_Matrix4_tmpMat
has only few methods:
- idt
- mul
- set
- setToLookAt
- setToOrtho
- setToOrtho2D
- ctor
Here’s a screenshots presenting both the exception and list of methods:
There is Matrix4Emulator class which is supposed to replace whole original Matrix4
class from libgdx.
The replacing code is a bit different from original but I’ve println-ed all removals and adds of methods/fields, all logs are OK.
The replaceClass()
method:
https://github.com/Namek/teavm-libgdx/blob/master---repro_issues/core/src/main/java/org/teavm/libgdx/plugin/OverlayTransformer.java#L83-L109
Important bits:
public OverlayTransformer(){
Reflections reflections = new Reflections("org.teavm.libgdx.emu");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(Emulate.class);
for(Class<?> type : annotated){
Emulate em = type.getAnnotation(Emulate.class);
Class<?> toEmulate = em.value();
emulations.put(toEmulate.getTypeName(), type);
}
annotated = reflections.getTypesAnnotatedWith(Replace.class);
for(Class<?> type : annotated){
Replace em = type.getAnnotation(Replace.class);
Class<?> toEmulate = em.value();
replacements.put(toEmulate.getTypeName(), type);
}
}
private void replaceClass(final ClassHolder cls, final ClassReader emuCls) {
ClassRefsRenamer renamer = new ClassRefsRenamer(referenceCache, preimage ->
preimage.equals(emuCls.getName()) ? cls.getName() : preimage);
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
cls.removeField(field);
}
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
cls.removeMethod(method);
}
for (FieldReader field : emuCls.getFields()) {
cls.addField(ModelUtils.copyField(field));
}
for (MethodReader method : emuCls.getMethods()) {
cls.addMethod(renamer.rename(ModelUtils.copyMethod(method)));
}
}
Discussion
If that’s dead code elemination then it definitely is wrong about the setToTranslation
method because it’s used in the same class, in the method setToLookAt
(visible on screenshot).
What can I do about it? If that’s DCE, as a workaround, can I enforce not deleting methods?
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:7 (4 by maintainers)
Top GitHub Comments
It’s not a bug of TeaVM, it’s expected behaviour. Somewhere after 0.5.x I added a bit more restrictive behaviour: when type gets into field, it’s filtered against field type. This was performance optimization of DCE. Unfortunately, this broken some code, which has bugs, but which worked fine with 0.5.x. And OverlayTransformer is the code that contained bug. The behaviour was following. Consider you have code:
this code is turned into field declaration
and
<clinit>
method (which is created by Java compiler internally for all class initializers):After processing with OverlayTransformer you get following code:
however,
tmpMat
is still Matrix4Emulator. When DCE calculates the set of possible real types oftmpMat
, it processes this line and matches real type (Matrix4) against declared type (Matrix4Emulator), and since these types are different (and Matrix4Emulator is not a supertype for Matrix4), DCE filters out this type. DCE ends up with empty set of types oftmpMat
, which means that any virtual call to any method ontmpMat
has not effect and does not include any method into reachable set.OverlayTransformer
Just found out myself the lacking
renamer
. I ended up with:That worked. Thanks!
Is it bug?
I don’t really understand the exact path of DCE removing this method while still linking to it. Just to make sure before closing the issue, is it a bug of TeaVM or it is not?