wasm gc: reduce size of class metadata and metadata initializer

This commit is contained in:
Alexey Andreev 2024-09-14 18:24:54 +02:00
parent a503333c1b
commit e5d6603503
4 changed files with 146 additions and 30 deletions

View File

@ -32,6 +32,7 @@ public final class WasmGCClassFlags {
public static final int VARARGS = 1 << 13; public static final int VARARGS = 1 << 13;
public static final int VOLATILE = 1 << 14; public static final int VOLATILE = 1 << 14;
public static final int PRIMITIVE = 1 << 15; public static final int PRIMITIVE = 1 << 15;
public static final int ARRAY = 1 << 16;
public static final int PRIMITIVE_KIND_SHIFT = 16; public static final int PRIMITIVE_KIND_SHIFT = 16;
public static final int PRIMITIVE_BOOLEAN = 0; public static final int PRIMITIVE_BOOLEAN = 0;

View File

@ -121,12 +121,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private List<WasmExpression> initializerFunctionStatements = new ArrayList<>(); private List<WasmExpression> initializerFunctionStatements = new ArrayList<>();
private WasmFunction createPrimitiveClassFunction; private WasmFunction createPrimitiveClassFunction;
private WasmFunction createArrayClassFunction; private WasmFunction createArrayClassFunction;
private WasmFunction fillRegularClassFunction;
private final WasmGCSupertypeFunctionGenerator supertypeGenerator; private final WasmGCSupertypeFunctionGenerator supertypeGenerator;
private final WasmGCNewArrayFunctionGenerator newArrayGenerator; private final WasmGCNewArrayFunctionGenerator newArrayGenerator;
private String arrayDataFieldName; private String arrayDataFieldName;
private int classTagOffset; private int classTagOffset;
private int classFlagsOffset; private int classFlagsOffset = -1;
private int classNameOffset; private int classNameOffset;
private int classSimpleNameOffset; private int classSimpleNameOffset;
private int classCanonicalNameOffset; private int classCanonicalNameOffset;
@ -149,7 +150,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private WasmFunctionType arrayLengthType; private WasmFunctionType arrayLengthType;
private List<WasmStructure> nonInitializedStructures = new ArrayList<>(); private List<WasmStructure> nonInitializedStructures = new ArrayList<>();
private WasmArray objectArrayType; private WasmArray objectArrayType;
private WasmArray enumConstantArray;
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
ClassHierarchy hierarchy, ClassHierarchy hierarchy,
@ -242,7 +242,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
@Override @Override
public void contributeToInitializer(WasmFunction function) { public void contributeToInitializer(WasmFunction function) {
var classClass = standardClasses.classClass();
function.getBody().addAll(initializerFunctionStatements); function.getBody().addAll(initializerFunctionStatements);
initializerFunctionStatements.clear(); initializerFunctionStatements.clear();
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
@ -261,8 +260,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
new WasmFunctionReference(newArrayFunction))); new WasmFunctionReference(newArrayFunction)));
} }
} }
function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET,
new WasmGetGlobal(classClass.pointer)));
} }
for (var consumer : staticFieldInitializers) { for (var consumer : staticFieldInitializers) {
consumer.accept(function); consumer.accept(function);
@ -497,14 +494,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
standardClasses.classClass().getStructure().init(); standardClasses.classClass().getStructure().init();
var ranges = tagRegistry.getRanges(name); var ranges = tagRegistry.getRanges(name);
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0); int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag))); int flags = cls != null ? getFlags(cls) : 0;
target.add(new WasmCall(getFillRegularClassFunction(), new WasmGetGlobal(classInfo.pointer),
new WasmInt32Constant(tag), new WasmInt32Constant(flags)));
var metadataReq = metadataRequirements.getInfo(name); var metadataReq = metadataRequirements.getInfo(name);
if (metadataReq.name()) { if (metadataReq.name()) {
var namePtr = strings.getStringConstant(name).global; var namePtr = strings.getStringConstant(name).global;
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr))); target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
} }
if (cls != null) { if (cls != null) {
target.add(setClassField(classInfo, classFlagsOffset, new WasmInt32Constant(getFlags(cls))));
if (metadataReq.simpleName() && cls.getSimpleName() != null) { if (metadataReq.simpleName() && cls.getSimpleName() != null) {
var namePtr = strings.getStringConstant(cls.getSimpleName()).global; var namePtr = strings.getStringConstant(cls.getSimpleName()).global;
target.add(setClassField(classInfo, classSimpleNameOffset, new WasmGetGlobal(namePtr))); target.add(setClassField(classInfo, classSimpleNameOffset, new WasmGetGlobal(namePtr)));
@ -1091,36 +1089,52 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fields.add(wasmField); fields.add(wasmField);
} }
if (className.equals("java.lang.Class")) { if (className.equals("java.lang.Class")) {
var cls = classSource.get("java.lang.Class");
classFlagsOffset = fields.size(); classFlagsOffset = fields.size();
fields.add(createClassField(WasmType.INT32.asStorage(), "lowerIndex")); fields.add(createClassField(WasmType.INT32.asStorage(), "flags"));
classTagOffset = fields.size(); classTagOffset = fields.size();
fields.add(createClassField(WasmType.INT32.asStorage(), "upperIndex")); fields.add(createClassField(WasmType.INT32.asStorage(), "id"));
if (metadataRequirements.hasSuperclass()) {
classParentOffset = fields.size(); classParentOffset = fields.size();
fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "parent")); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "parent"));
}
classArrayItemOffset = fields.size(); classArrayItemOffset = fields.size();
fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "arrayItem")); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "arrayItem"));
classArrayOffset = fields.size(); classArrayOffset = fields.size();
fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "array")); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "array"));
if (metadataRequirements.hasIsAssignable()) {
classSupertypeFunctionOffset = fields.size(); classSupertypeFunctionOffset = fields.size();
fields.add(createClassField(supertypeGenerator.getFunctionType().getReference().asStorage(), fields.add(createClassField(supertypeGenerator.getFunctionType().getReference().asStorage(),
"isSupertype")); "isSupertype"));
}
if (metadataRequirements.hasArrayNewInstance()) {
classNewArrayOffset = fields.size(); classNewArrayOffset = fields.size();
fields.add(createClassField(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage(), fields.add(createClassField(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage(),
"createArrayInstance")); "createArrayInstance"));
}
if (metadataRequirements.hasEnclosingClass()) {
classEnclosingClassOffset = fields.size(); classEnclosingClassOffset = fields.size();
fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "enclosingClass")); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "enclosingClass"));
}
if (metadataRequirements.hasDeclaringClass()) {
classDeclaringClassOffset = fields.size(); classDeclaringClassOffset = fields.size();
fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "declaringClass")); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "declaringClass"));
}
if (metadataRequirements.hasName()) {
classNameOffset = fields.size(); classNameOffset = fields.size();
fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "name")); fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "name"));
}
if (metadataRequirements.hasSimpleName()) {
classSimpleNameOffset = fields.size(); classSimpleNameOffset = fields.size();
fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "simpleName")); fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "simpleName"));
}
if (cls != null && cls.getMethod(new MethodDescriptor("getCanonicalName", String.class)) != null) {
classCanonicalNameOffset = fields.size(); classCanonicalNameOffset = fields.size();
fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "canonicalName")); fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "canonicalName"));
}
cloneOffset = fields.size(); cloneOffset = fields.size();
fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(), fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(),
standardClasses.objectClass().getType()).getReference().asStorage(), "clone")); standardClasses.objectClass().getType()).getReference().asStorage(), "clone"));
fields.add(createClassField(WasmType.INT32.asStorage(), "enumConstantCount"));
if (metadataRequirements.hasEnumConstants()) { if (metadataRequirements.hasEnumConstants()) {
enumConstantsFunctionOffset = fields.size(); enumConstantsFunctionOffset = fields.size();
var enumArrayType = getClassInfo(ValueType.arrayOf(ValueType.object("java.lang.Enum"))).getType(); var enumArrayType = getClassInfo(ValueType.arrayOf(ValueType.object("java.lang.Enum"))).getType();
@ -1214,7 +1228,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
function.add(nameVar); function.add(nameVar);
function.add(kindVar); function.add(kindVar);
standardClasses.classClass().getStructure().getFields().size(); standardClasses.classClass().getStructure().init();
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
CLASS_FIELD_OFFSET,
new WasmGetGlobal(standardClasses.classClass().pointer)
));
var flagsExpr = new WasmIntBinary( var flagsExpr = new WasmIntBinary(
WasmIntType.INT32, WasmIntType.INT32,
WasmIntBinaryOperation.SHL, WasmIntBinaryOperation.SHL,
@ -1248,6 +1268,54 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return function; return function;
} }
private WasmFunction getFillRegularClassFunction() {
if (fillRegularClassFunction == null) {
fillRegularClassFunction = createFillRegularClassFunction();
}
return fillRegularClassFunction;
}
private WasmFunction createFillRegularClassFunction() {
var functionType = functionTypes.of(
null,
standardClasses.classClass().getType(),
WasmType.INT32,
WasmType.INT32
);
var function = new WasmFunction(functionType);
module.functions.add(function);
function.setName(names.topLevel("teavm@fillRegularClass"));
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var idVar = new WasmLocal(standardClasses.classClass().getType(), "id");
var flagsVar = new WasmLocal(standardClasses.classClass().getType(), "flags");
function.add(targetVar);
function.add(idVar);
function.add(flagsVar);
standardClasses.classClass().getStructure().init();
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
CLASS_FIELD_OFFSET,
new WasmGetGlobal(standardClasses.classClass().pointer)
));
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classTagOffset,
new WasmGetLocal(idVar)
));
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classFlagsOffset,
new WasmGetLocal(flagsVar)
));
return function;
}
private WasmFunction getCreateArrayClassFunction() { private WasmFunction getCreateArrayClassFunction() {
if (createArrayClassFunction == null) { if (createArrayClassFunction == null) {
createArrayClassFunction = createCreateArrayClassFunction(); createArrayClassFunction = createCreateArrayClassFunction();
@ -1271,6 +1339,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
function.add(itemVar); function.add(itemVar);
standardClasses.classClass().getStructure().init(); standardClasses.classClass().getStructure().init();
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
CLASS_FIELD_OFFSET,
new WasmGetGlobal(standardClasses.classClass().pointer)
));
function.getBody().add(new WasmStructSet( function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(), standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar), new WasmGetLocal(targetVar),

View File

@ -174,7 +174,6 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
return functionType; return functionType;
} }
private WasmExpression getClassField(WasmExpression instance, int fieldIndex) { private WasmExpression getClassField(WasmExpression instance, int fieldIndex) {
return new WasmStructGet( return new WasmStructGet(
classGenerator.standardClasses.classClass().getStructure(), classGenerator.standardClasses.classClass().getStructure(),

View File

@ -47,15 +47,24 @@ public class ClassMetadataRequirements {
private boolean hasArrayGet; private boolean hasArrayGet;
private boolean hasArrayLength; private boolean hasArrayLength;
private boolean hasEnumConstants; private boolean hasEnumConstants;
private boolean hasSuperclass;
private boolean hasIsAssignable;
private boolean hasNewInstance;
private boolean hasEnclosingClass;
private boolean hasDeclaringClass;
private boolean hasSimpleName;
private boolean hasName;
public ClassMetadataRequirements(DependencyInfo dependencyInfo) { public ClassMetadataRequirements(DependencyInfo dependencyInfo) {
MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD); MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD);
if (getNameMethod != null) { if (getNameMethod != null) {
hasName = true;
addClassesRequiringName(requirements, getNameMethod.getVariable(0).getClassValueNode().getTypes()); addClassesRequiringName(requirements, getNameMethod.getVariable(0).getClassValueNode().getTypes());
} }
MethodDependencyInfo getSimpleNameMethod = dependencyInfo.getMethod(GET_SIMPLE_NAME_METHOD); MethodDependencyInfo getSimpleNameMethod = dependencyInfo.getMethod(GET_SIMPLE_NAME_METHOD);
if (getSimpleNameMethod != null) { if (getSimpleNameMethod != null) {
hasSimpleName = true;
String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes(); String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes();
addClassesRequiringName(requirements, classNames); addClassesRequiringName(requirements, classNames);
for (String className : classNames) { for (String className : classNames) {
@ -67,6 +76,7 @@ public class ClassMetadataRequirements {
var getSuperclassMethod = dependencyInfo.getMethod(GET_SUPERCLASS_METHOD); var getSuperclassMethod = dependencyInfo.getMethod(GET_SUPERCLASS_METHOD);
if (getSuperclassMethod != null) { if (getSuperclassMethod != null) {
hasSuperclass = true;
var classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes(); var classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes();
for (var className : classNames) { for (var className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).superclass = true; requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).superclass = true;
@ -75,6 +85,7 @@ public class ClassMetadataRequirements {
var isAssignableMethod = dependencyInfo.getMethod(IS_ASSIGNABLE_METHOD); var isAssignableMethod = dependencyInfo.getMethod(IS_ASSIGNABLE_METHOD);
if (isAssignableMethod != null) { if (isAssignableMethod != null) {
hasIsAssignable = true;
var classNames = isAssignableMethod.getVariable(0).getClassValueNode().getTypes(); var classNames = isAssignableMethod.getVariable(0).getClassValueNode().getTypes();
for (var className : classNames) { for (var className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).isAssignable = true; requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).isAssignable = true;
@ -83,6 +94,7 @@ public class ClassMetadataRequirements {
MethodDependencyInfo getDeclaringClassMethod = dependencyInfo.getMethod(GET_DECLARING_CLASS_METHOD); MethodDependencyInfo getDeclaringClassMethod = dependencyInfo.getMethod(GET_DECLARING_CLASS_METHOD);
if (getDeclaringClassMethod != null) { if (getDeclaringClassMethod != null) {
hasDeclaringClass = true;
String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes(); String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes();
for (String className : classNames) { for (String className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).declaringClass = true; requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).declaringClass = true;
@ -91,6 +103,7 @@ public class ClassMetadataRequirements {
MethodDependencyInfo getEnclosingClassMethod = dependencyInfo.getMethod(GET_ENCLOSING_CLASS_METHOD); MethodDependencyInfo getEnclosingClassMethod = dependencyInfo.getMethod(GET_ENCLOSING_CLASS_METHOD);
if (getEnclosingClassMethod != null) { if (getEnclosingClassMethod != null) {
hasEnclosingClass = true;
String[] classNames = getEnclosingClassMethod.getVariable(0).getClassValueNode().getTypes(); String[] classNames = getEnclosingClassMethod.getVariable(0).getClassValueNode().getTypes();
for (String className : classNames) { for (String className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enclosingClass = true; requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enclosingClass = true;
@ -99,6 +112,7 @@ public class ClassMetadataRequirements {
var newArrayMethod = dependencyInfo.getMethod(NEW_ARRAY); var newArrayMethod = dependencyInfo.getMethod(NEW_ARRAY);
if (newArrayMethod != null) { if (newArrayMethod != null) {
hasNewInstance = true;
var classNames = newArrayMethod.getVariable(1).getClassValueNode().getTypes(); var classNames = newArrayMethod.getVariable(1).getClassValueNode().getTypes();
for (var className : classNames) { for (var className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).newArray = true; requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).newArray = true;
@ -172,6 +186,34 @@ public class ClassMetadataRequirements {
return hasEnumConstants; return hasEnumConstants;
} }
public boolean hasSuperclass() {
return hasSuperclass;
}
public boolean hasIsAssignable() {
return hasIsAssignable;
}
public boolean hasArrayNewInstance() {
return hasNewInstance;
}
public boolean hasEnclosingClass() {
return hasEnclosingClass;
}
public boolean hasDeclaringClass() {
return hasDeclaringClass;
}
public boolean hasSimpleName() {
return hasSimpleName;
}
public boolean hasName() {
return hasName;
}
private void addClassesRequiringName(Map<ValueType, ClassInfo> target, String[] source) { private void addClassesRequiringName(Map<ValueType, ClassInfo> target, String[] source) {
for (String typeName : source) { for (String typeName : source) {
target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true; target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true;