From 8e08cf6dcd57241c5ee91fc4a04a90e2c9a30509 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 16 May 2019 16:53:40 +0300 Subject: [PATCH] C: improve generation of string pool for incremental mode --- .../java/org/teavm/backend/c/CTarget.java | 65 +++++----- .../backend/c/generate/CallSiteGenerator.java | 2 +- .../backend/c/generate/ClassGenerator.java | 64 +++++++--- .../backend/c/generate/CodeGeneratorUtil.java | 2 +- .../backend/c/generate/SimpleStringPool.java | 64 +--------- .../teavm/backend/c/generate/StringPool.java | 2 +- .../c/generate/StringPoolGenerator.java | 37 ++++-- .../PlatformClassMetadataIntrinsic.java | 1 + .../generate/LowLevelNameProvider.java | 8 +- .../org/teavm/runtime/CallSiteLocation.java | 6 +- .../org/teavm/runtime/ExceptionHandling.java | 12 +- .../java/org/teavm/runtime/RuntimeClass.java | 2 +- .../org/teavm/runtime/RuntimeObjectPtr.java | 22 ++++ .../java/org/teavm/runtime/StringPtr.java | 22 ++++ .../resources/org/teavm/backend/c/runtime.c | 2 +- .../resources/org/teavm/backend/c/runtime.h | 21 ++-- .../org/teavm/backend/c/stringhash.c | 115 ++++++++++++++++++ .../platform/plugin/MetadataCIntrinsic.java | 8 +- .../plugin/ResourceReadCIntrinsic.java | 7 ++ .../c/incremental/IncrementalCBuilder.java | 5 +- 20 files changed, 313 insertions(+), 154 deletions(-) create mode 100644 core/src/main/java/org/teavm/runtime/RuntimeObjectPtr.java create mode 100644 core/src/main/java/org/teavm/runtime/StringPtr.java create mode 100644 core/src/main/resources/org/teavm/backend/c/stringhash.c diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index c8789196e..a78899568 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -47,7 +47,6 @@ import org.teavm.backend.c.generate.NameProvider; import org.teavm.backend.c.generate.OutputFileUtil; import org.teavm.backend.c.generate.SimpleIncludeManager; import org.teavm.backend.c.generate.SimpleStringPool; -import org.teavm.backend.c.generate.StringPool; import org.teavm.backend.c.generate.StringPoolGenerator; import org.teavm.backend.c.generators.ArrayGenerator; import org.teavm.backend.c.generators.Generator; @@ -145,15 +144,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private Set asyncMethods; private boolean incremental; private boolean lineNumbersGenerated; - private StringPool stringPool; - - public CTarget() { - this(new SimpleStringPool()); - } - - public CTarget(StringPool stringPool) { - this.stringPool = stringPool; - } + private SimpleStringPool stringPool; public void setMinHeapSize(int minHeapSize) { this.minHeapSize = minHeapSize; @@ -333,6 +324,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { List generators = new ArrayList<>(); generators.add(new ArrayGenerator()); + stringPool = new SimpleStringPool(); GenerationContext context = new GenerationContext(vtableProvider, characteristics, controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes, intrinsics, generators, asyncMethods::contains, buildTarget, incremental); @@ -363,9 +355,12 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { generateSpecialFunctions(context, runtimeWriter); OutputFileUtil.write(runtimeWriter, "runtime.c", buildTarget); OutputFileUtil.write(runtimeHeaderWriter, "runtime.h", buildTarget); + BufferedCodeWriter stringhashWriter = new BufferedCodeWriter(false); + emitResource(stringhashWriter, "stringhash.c"); + OutputFileUtil.write(stringhashWriter, "stringhash.c", buildTarget); generateCallSites(buildTarget, context, classes.getClassNames()); - generateStrings(buildTarget, stringPool); + generateStrings(buildTarget, context); List types = classGenerator.getTypes().stream() .filter(c -> ClassGenerator.needsVirtualTable(characteristics, c)) @@ -374,7 +369,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { generateAllFile(classes, types, buildTarget); } - private void emitResource(CodeWriter writer, String resourceName) { ClassLoader classLoader = CTarget.class.getClassLoader(); try (BufferedReader reader = new BufferedReader(new InputStreamReader( @@ -407,6 +401,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { String name = ClassGenerator.fileName(className); OutputFileUtil.write(writer, name + ".c", buildTarget); OutputFileUtil.write(headerWriter, name + ".h", buildTarget); + if (incremental) { + stringPool.reset(); + } } for (ValueType type : classGenerator.getTypes()) { @@ -419,6 +416,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { String name = ClassGenerator.fileName(type); OutputFileUtil.write(writer, name + ".c", buildTarget); OutputFileUtil.write(headerWriter, name + ".h", buildTarget); + if (incremental) { + stringPool.reset(); + } } } @@ -495,16 +495,28 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { + "*) ((void**) frame)[3]) + id)"); } - private void generateStrings(BuildTarget buildTarget, StringPool stringPool) throws IOException { + private void generateStrings(BuildTarget buildTarget, GenerationContext context) throws IOException { BufferedCodeWriter writer = new BufferedCodeWriter(false); + IncludeManager includes = new SimpleIncludeManager(writer); + includes.init("strings.c"); BufferedCodeWriter headerWriter = new BufferedCodeWriter(false); headerWriter.println("#pragma once"); headerWriter.println("#include \"runtime.h\""); - headerWriter.println("extern TeaVM_String teavm_stringPool[];"); + headerWriter.println("extern void teavm_initStringPool();"); + if (!incremental) { + headerWriter.println("extern TeaVM_String* teavm_stringPool[];"); + headerWriter.println("#define TEAVM_GET_STRING(i) teavm_stringPool[i]"); - writer.println("#include \"strings.h\""); - new StringPoolGenerator(writer).generate(stringPool.getStrings()); + writer.println("#include \"strings.h\""); + StringPoolGenerator poolGenerator = new StringPoolGenerator(context, "teavm_stringPool"); + poolGenerator.generate(writer); + writer.println("void teavm_initStringPool() {").indent(); + poolGenerator.generateStringPoolHeaders(writer, includes); + writer.outdent().println("}"); + } else { + writer.println("void teavm_initStringPool() {}"); + } OutputFileUtil.write(writer, "strings.c", buildTarget); OutputFileUtil.write(headerWriter, "strings.h", buildTarget); @@ -652,10 +664,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { writer.println("teavm_beforeInit();"); writer.println("teavm_initHeap(" + minHeapSize + ");"); generateVirtualTableHeaders(context, writer, types); - generateStringPoolHeaders(context, writer); - for (String className : classes.getClassNames()) { - includes.includeClass(className); - writer.println(context.getNames().forClassSystemInitializer(className) + "();"); + writer.println("teavm_initStringPool();"); + for (ValueType type : types) { + includes.includeType(type); + writer.println(context.getNames().forClassSystemInitializer(type) + "();"); } writer.println("teavm_afterInitClasses();"); generateStaticInitializerCalls(context, writer, includes, classes); @@ -705,19 +717,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { writer.outdent().println("}"); } - private void generateStringPoolHeaders(GenerationContext context, CodeWriter writer) { - String stringClassName = context.getNames().forClassInstance(ValueType.object("java.lang.String")); - writer.print("int32_t stringHeader = TEAVM_PACK_CLASS(&" + stringClassName + ") | "); - CodeGeneratorUtil.writeIntValue(writer, RuntimeObject.GC_MARKED); - writer.println(";"); - - int size = context.getStringPool().getStrings().size(); - writer.println("for (int i = 0; i < " + size + "; ++i) {").indent(); - writer.println("TeaVM_Object *s = (TeaVM_Object*) (teavm_stringPool + i);"); - writer.println("if (s != NULL) s->header = stringHeader;"); - writer.outdent().println("}"); - } - private void generateFiberStart(GenerationContext context, CodeWriter writer, IncludeManager includes) { String startName = context.getNames().forMethod(new MethodReference(Fiber.class, "startMain", String[].class, void.class)); diff --git a/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java index 5c5331054..81852480b 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java @@ -183,6 +183,6 @@ public class CallSiteGenerator { } private String getStringExpr(String s) { - return s != null ? "teavm_stringPool + " + context.getStringPool().getStringIndex(s) : "NULL"; + return s != null ? "&TEAVM_GET_STRING(" + context.getStringPool().getStringIndex(s) + ")" : "NULL"; } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 60680532a..b364133e4 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -84,7 +84,6 @@ public class ClassGenerator { private CodeWriter headerWriter; private IncludeManager includes; private IncludeManager headerIncludes; - private boolean stackDefined; public ClassGenerator(GenerationContext context, ClassReaderSource unprocessedClassSource, TagRegistry tagRegistry, Decompiler decompiler) { @@ -165,18 +164,10 @@ public class ClassGenerator { }; public void generateClass(CodeWriter writer, CodeWriter headerWriter, ClassHolder cls) { - stackDefined = false; - init(writer, headerWriter, fileName(cls.getName())); - - codeGenerator = new CodeGenerator(context, codeWriter, includes); - - String sysInitializerName = context.getNames().forClassSystemInitializer(cls.getName()); - headerWriter.println("extern void " + sysInitializerName + "();"); - writer.println("void " + sysInitializerName + "() {").indent(); - initWriter = writer.fragment(); - writer.outdent().println("}"); - includes.includeClass(cls.getName()); + ValueType type = ValueType.object(cls.getName()); + init(writer, headerWriter, fileName(cls.getName()), type); + generateStringPoolDecl(type); generateClassStructure(cls); generateClassStaticFields(cls); generateClassMethods(cls); @@ -184,6 +175,7 @@ public class ClassGenerator { generateVirtualTable(ValueType.object(cls.getName())); generateStaticGCRoots(cls.getName()); generateLayoutArray(cls.getName()); + generateStringPool(type); } private void generateCallSites(List callSites, String callSitesName) { @@ -193,12 +185,14 @@ public class ClassGenerator { } public void generateType(CodeWriter writer, CodeWriter headerWriter, ValueType type) { - init(writer, headerWriter, fileName(type)); + init(writer, headerWriter, fileName(type), type); + generateStringPoolDecl(type); includes.includeType(type); generateVirtualTable(type); + generateStringPool(type); } - private void init(CodeWriter writer, CodeWriter headerWriter, String fileName) { + private void init(CodeWriter writer, CodeWriter headerWriter, String fileName, ValueType type) { staticGcRoots = null; classLayout = null; @@ -211,6 +205,38 @@ public class ClassGenerator { headerIncludes = new SimpleIncludeManager(headerWriter); headerIncludes.init(fileName + ".h"); headerIncludes.includePath("runtime.h"); + + codeGenerator = new CodeGenerator(context, codeWriter, includes); + + String sysInitializerName = context.getNames().forClassSystemInitializer(type); + headerWriter.println("extern void " + sysInitializerName + "();"); + writer.println("void " + sysInitializerName + "() {").indent(); + initWriter = writer.fragment(); + writer.outdent().println("}"); + includes.includeType(type); + } + + private void generateStringPoolDecl(ValueType type) { + if (!context.isIncremental()) { + return; + } + + String poolName = "strings_" + context.getNames().forClassInstance(type); + codeWriter.println("TeaVM_String* " + poolName + "[];"); + codeWriter.println("#define TEAVM_GET_STRING(i) " + poolName + "[i]"); + } + + private void generateStringPool(ValueType type) { + if (!context.isIncremental()) { + return; + } + + codeWriter.println("#undef TEAVM_GET_STRING"); + + String poolName = "strings_" + context.getNames().forClassInstance(type); + StringPoolGenerator poolGenerator = new StringPoolGenerator(context, poolName); + poolGenerator.generate(codeWriter); + poolGenerator.generateStringPoolHeaders(initWriter, includes); } public Set getTypes() { @@ -242,17 +268,17 @@ public class ClassGenerator { } else { callSitesName = "NULL"; } - if (stackDefined) { - codeWriter.println("#undef TEAVM_ALLOC_STACK"); - } codeWriter.println("#define TEAVM_ALLOC_STACK(size) TEAVM_ALLOC_STACK_DEF(size, " + callSitesName + ")"); - stackDefined = true; } generateMethodForwardDeclaration(method); RegularMethodNode methodNode = decompiler.decompileRegular(method); codeGenerator.generateMethod(methodNode); + + if (context.isIncremental()) { + codeWriter.println("#undef TEAVM_ALLOC_STACK"); + } } } @@ -579,7 +605,7 @@ public class ClassGenerator { codeWriter.println(".flags = " + flags + ","); codeWriter.println(".tag = " + tag + ","); codeWriter.println(".canary = 0,"); - codeWriter.println(".name = (TeaVM_Object*) (teavm_stringPool + " + nameRef + "),"); + codeWriter.println(".name = (TeaVM_Object**) &TEAVM_GET_STRING(" + nameRef + "),"); codeWriter.println(".simpleName = NULL,"); codeWriter.println(".arrayType = " + arrayTypeExpr + ","); codeWriter.println(".itemType = " + itemTypeExpr + ","); diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGeneratorUtil.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGeneratorUtil.java index ee561d374..6e0bb3a7f 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGeneratorUtil.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGeneratorUtil.java @@ -43,7 +43,7 @@ public final class CodeGeneratorUtil { } else if (value instanceof String) { includes.includePath("strings.h"); int index = context.getStringPool().getStringIndex((String) value); - writer.print("(teavm_stringPool + " + index + ")"); + writer.print("TEAVM_GET_STRING(" + index + ")"); } else if (value instanceof Integer) { writeIntValue(writer, (Integer) value); } else if (value instanceof Long) { diff --git a/core/src/main/java/org/teavm/backend/c/generate/SimpleStringPool.java b/core/src/main/java/org/teavm/backend/c/generate/SimpleStringPool.java index 12ef09175..ffd847963 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/SimpleStringPool.java +++ b/core/src/main/java/org/teavm/backend/c/generate/SimpleStringPool.java @@ -25,81 +25,25 @@ public class SimpleStringPool implements StringPool { private ObjectIntMap stringIndexes = new ObjectIntHashMap<>(); private final List strings = new ArrayList<>(); private final List readonlyStrings = Collections.unmodifiableList(strings); - private final List freeIndexes = new ArrayList<>(); - private FreeIndex firstFreeIndex; - private FreeIndex lastFreeIndex; - private ObjectIntMap lastStringIndexes = new ObjectIntHashMap<>(); @Override public int getStringIndex(String string) { int index = stringIndexes.getOrDefault(string, -1); if (index < 0) { - index = lastStringIndexes.getOrDefault(string, -1); - if (index >= 0) { - removeFreeIndex(index); - strings.set(index, string); - } else if (firstFreeIndex != null) { - index = firstFreeIndex.value; - freeIndexes.set(index, null); - firstFreeIndex = firstFreeIndex.next; - if (firstFreeIndex == null) { - lastFreeIndex = null; - } - strings.set(index, string); - } else { - index = strings.size(); - strings.add(string); - } + index = strings.size(); + strings.add(string); stringIndexes.put(string, index); } return index; } - private void removeFreeIndex(int index) { - FreeIndex freeIndex = freeIndexes.get(index); - if (freeIndex == null) { - return; - } - freeIndexes.set(index, null); - - if (freeIndex.previous != null) { - freeIndex.previous.next = freeIndex.next; - } else { - firstFreeIndex = freeIndex.next; - } - if (freeIndex.next != null) { - freeIndex.next.previous = freeIndex.previous; - } else { - lastFreeIndex = freeIndex.previous; - } - } - @Override - public List getStrings() { + public List getStrings() { return readonlyStrings; } public void reset() { - for (int i = freeIndexes.size(); i < strings.size(); ++i) { - FreeIndex freeIndex = new FreeIndex(); - freeIndexes.add(freeIndex); - if (lastFreeIndex != null) { - freeIndex.previous = lastFreeIndex; - lastFreeIndex.next = freeIndex; - } - lastFreeIndex = freeIndex; - } - lastStringIndexes.clear(); - lastStringIndexes.putAll(stringIndexes); + strings.clear(); stringIndexes.clear(); - for (int i = 0; i < strings.size(); ++i) { - strings.set(i, null); - } - } - - static class FreeIndex { - int value; - FreeIndex previous; - FreeIndex next; } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/StringPool.java b/core/src/main/java/org/teavm/backend/c/generate/StringPool.java index 52b17f27b..cf50dd76b 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/StringPool.java +++ b/core/src/main/java/org/teavm/backend/c/generate/StringPool.java @@ -20,5 +20,5 @@ import java.util.List; public interface StringPool { int getStringIndex(String string); - List getStrings(); + List getStrings(); } diff --git a/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java index f4c2d31d4..992abdcc2 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java @@ -16,16 +16,21 @@ package org.teavm.backend.c.generate; import java.util.List; +import org.teavm.model.ValueType; +import org.teavm.runtime.RuntimeObject; public class StringPoolGenerator { - private CodeWriter writer; + private GenerationContext context; + private String poolVariable; - public StringPoolGenerator(CodeWriter writer) { - this.writer = writer; + public StringPoolGenerator(GenerationContext context, String poolVariable) { + this.context = context; + this.poolVariable = poolVariable; } - public void generate(List strings) { - writer.println("TeaVM_String teavm_stringPool[" + strings.size() + "] = {").indent(); + public void generate(CodeWriter writer) { + List strings = context.getStringPool().getStrings(); + writer.println("TeaVM_String* " + poolVariable + "[" + strings.size() + "] = {").indent(); for (int i = 0; i < strings.size(); ++i) { String s = strings.get(i); if (s == null) { @@ -35,7 +40,7 @@ public class StringPoolGenerator { String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING"; writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ","); if (codes) { - generateNumericStringLiteral(s); + generateNumericStringLiteral(writer, s); } else { writer.print("u"); generateSimpleStringLiteral(writer, s); @@ -50,6 +55,24 @@ public class StringPoolGenerator { writer.outdent().println("};"); } + public void generateStringPoolHeaders(CodeWriter writer, IncludeManager includes) { + includes.includeClass("java.lang.String"); + String stringClassName = context.getNames().forClassInstance(ValueType.object("java.lang.String")); + writer.print("int32_t stringHeader = TEAVM_PACK_CLASS(&" + stringClassName + ") | "); + CodeGeneratorUtil.writeIntValue(writer, RuntimeObject.GC_MARKED); + writer.println(";"); + + int size = context.getStringPool().getStrings().size(); + writer.println("for (int i = 0; i < " + size + "; ++i) {").indent(); + writer.println("TeaVM_String *s = " + poolVariable + "[i];"); + writer.println("if (s != NULL) {").indent(); + writer.println("s = teavm_registerString(s);"); + writer.println("((TeaVM_Object*) s)->header = stringHeader;"); + writer.println(poolVariable + "[i] = s;"); + writer.outdent().println("}"); + writer.outdent().println("}"); + } + private boolean hasBadCharacters(String string) { for (int i = 0; i < string.length(); ++i) { char c = string.charAt(i); @@ -112,7 +135,7 @@ public class StringPoolGenerator { } } - private void generateNumericStringLiteral(String string) { + private void generateNumericStringLiteral(CodeWriter writer, String string) { for (int i = 0; i < string.length(); ++i) { if (i > 0) { writer.print(", "); diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformClassMetadataIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformClassMetadataIntrinsic.java index 3b2556901..3bb7fd4d2 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformClassMetadataIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformClassMetadataIntrinsic.java @@ -53,6 +53,7 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic { printFieldAccess(context, invocation, SUPERCLASS_FIELD); break; case "getName": + context.writer().print("*"); printFieldAccess(context, invocation, NAME_FIELD); break; } diff --git a/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java b/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java index 22d30cb7a..2424ce06c 100644 --- a/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java +++ b/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java @@ -46,7 +46,7 @@ public abstract class LowLevelNameProvider { protected Map classNames = new HashMap<>(); protected Map classInitializerNames = new HashMap<>(); protected Map classClassNames = new HashMap<>(); - protected Map classSystemInitializerNames = new HashMap<>(); + protected Map classSystemInitializerNames = new HashMap<>(); protected Map classInstanceNames = new HashMap<>(); protected Map supertypeNames = new HashMap<>(); @@ -109,9 +109,9 @@ public abstract class LowLevelNameProvider { return classInitializerNames.computeIfAbsent(className, k -> pickUnoccupied("initclass_" + suggestForClass(k))); } - public String forClassSystemInitializer(String className) { - return classSystemInitializerNames.computeIfAbsent(className, k -> pickUnoccupied("sysinitclass_" - + suggestForClass(k))); + public String forClassSystemInitializer(ValueType type) { + return classSystemInitializerNames.computeIfAbsent(type, k -> pickUnoccupied("sysinitclass_" + + suggestForType(k))); } public String forClassClass(String className) { diff --git a/core/src/main/java/org/teavm/runtime/CallSiteLocation.java b/core/src/main/java/org/teavm/runtime/CallSiteLocation.java index c0a505cc5..6c3bfacf6 100644 --- a/core/src/main/java/org/teavm/runtime/CallSiteLocation.java +++ b/core/src/main/java/org/teavm/runtime/CallSiteLocation.java @@ -22,8 +22,8 @@ import org.teavm.interop.Unmanaged; @Unmanaged @StaticInit public class CallSiteLocation extends Structure { - public String fileName; - public String className; - public String methodName; + public StringPtr fileName; + public StringPtr className; + public StringPtr methodName; public int lineNumber; } diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index 6da49863b..1bcef45c3 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -39,13 +39,13 @@ public final class ExceptionHandling { if (location.className == null || location.methodName == null) { Console.printString("(Unknown method)"); } else { - Console.printString(location.className); + Console.printString(location.className.value); Console.printString("."); - Console.printString(location.methodName); + Console.printString(location.methodName.value); } Console.printString("("); if (location.fileName != null && location.lineNumber >= 0) { - Console.printString(location.fileName); + Console.printString(location.fileName.value); Console.printString(":"); Console.printInt(location.lineNumber); } @@ -120,8 +120,10 @@ public final class ExceptionHandling { int callSiteId = ShadowStack.getCallSiteId(stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSiteLocation location = callSite.location; - StackTraceElement element = createElement(location != null ? location.className : "", - location != null ? location.methodName : "", location != null ? location.fileName : null, + StackTraceElement element = createElement( + location != null && location.className != null ? location.className.value : "", + location != null && location.methodName != null ? location.methodName.value : "", + location != null && location.fileName != null ? location.fileName.value : null, location != null ? location.lineNumber : -1); target[index++] = element; stackFrame = ShadowStack.getNextStackFrame(stackFrame); diff --git a/core/src/main/java/org/teavm/runtime/RuntimeClass.java b/core/src/main/java/org/teavm/runtime/RuntimeClass.java index 47f0f461e..65ce88254 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeClass.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeClass.java @@ -39,7 +39,7 @@ public class RuntimeClass extends RuntimeObject { public int flags; public int tag; public int canary; - public RuntimeObject name; + public RuntimeObjectPtr name; public RuntimeClass itemType; public RuntimeClass arrayType; public IsSupertypeFunction isSupertypeOf; diff --git a/core/src/main/java/org/teavm/runtime/RuntimeObjectPtr.java b/core/src/main/java/org/teavm/runtime/RuntimeObjectPtr.java new file mode 100644 index 000000000..68f2c7422 --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/RuntimeObjectPtr.java @@ -0,0 +1,22 @@ +/* + * Copyright 2019 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.runtime; + +import org.teavm.interop.Structure; + +public class RuntimeObjectPtr extends Structure { + RuntimeObject value; +} diff --git a/core/src/main/java/org/teavm/runtime/StringPtr.java b/core/src/main/java/org/teavm/runtime/StringPtr.java new file mode 100644 index 000000000..4a4fcfffe --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/StringPtr.java @@ -0,0 +1,22 @@ +/* + * Copyright 2019 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.runtime; + +import org.teavm.interop.Structure; + +public class StringPtr extends Structure { + String value; +} diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.c b/core/src/main/resources/org/teavm/backend/c/runtime.c index 9b0a8d92b..821baa01e 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime.c @@ -44,7 +44,7 @@ TeaVM_ResourceMapEntry* teavm_lookupResource(TeaVM_ResourceMap *map, TeaVM_Strin if (map->entries[index].key == NULL) { return NULL; } - if (teavm_equals(map->entries[index].key, string)) { + if (teavm_equals(*map->entries[index].key, string)) { return &map->entries[index]; } } diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.h b/core/src/main/resources/org/teavm/backend/c/runtime.h index 67da31bea..44991ad1d 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.h +++ b/core/src/main/resources/org/teavm/backend/c/runtime.h @@ -29,7 +29,7 @@ typedef struct TeaVM_Class { int32_t flags; int32_t tag; int32_t canary; - TeaVM_Object* name; + TeaVM_Object** name; struct TeaVM_Class* itemType; struct TeaVM_Class* arrayType; int32_t (*isSupertypeOf)(struct TeaVM_Class*); @@ -134,12 +134,7 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) { #define TEAVM_ADDRESS_ADD(address, offset) ((char *) (address) + (offset)) #define TEAVM_STRUCTURE_ADD(structure, address, offset) (((structure*) (address)) + offset) -#define TEAVM_NULL_STRING { \ - .characters = NULL, \ - .hashCode = 0 \ -} - -#define TEAVM_STRING(length, hash, s) { \ +#define TEAVM_STRING(length, hash, s) &(TeaVM_String) { \ .characters = (TeaVM_Array*) & (struct { TeaVM_Array hdr; char16_t data[(length) + 1]; }) { \ .hdr = { .size = length }, \ .data = s \ @@ -147,7 +142,7 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) { .hashCode = INT32_C(hash) \ } -#define TEAVM_STRING_FROM_CODES(length, hash, ...) { \ +#define TEAVM_STRING_FROM_CODES(length, hash, ...) &(TeaVM_String) { \ .characters = (TeaVM_Array*) & (struct { TeaVM_Array hdr; char16_t data[(length) + 1]; }) { \ .hdr = { .size = length }, \ .data = { __VA_ARGS__ } \ @@ -177,7 +172,7 @@ typedef struct { } TeaVM_ResourceArray; typedef struct { - TeaVM_String* key; + TeaVM_String** key; void* value; } TeaVM_ResourceMapEntry; @@ -247,4 +242,10 @@ extern void teavm_printInt(int32_t i); extern TeaVM_Array* teavm_parseArguments(int argc, char** argv); -extern void teavm_registerStaticGcRoots(void***, int count); \ No newline at end of file +extern void teavm_registerStaticGcRoots(void***, int count); + +extern TeaVM_String* teavm_registerString(TeaVM_String*); + +static inline TeaVM_Object* teavm_dereferenceNullable(TeaVM_Object** o) { + return o != NULL ? *o : NULL; +} \ No newline at end of file diff --git a/core/src/main/resources/org/teavm/backend/c/stringhash.c b/core/src/main/resources/org/teavm/backend/c/stringhash.c new file mode 100644 index 000000000..5ee8325ee --- /dev/null +++ b/core/src/main/resources/org/teavm/backend/c/stringhash.c @@ -0,0 +1,115 @@ +#include "runtime.h" +#include +#include +#include +#include +#include + +#define TEAVM_HASHTABLE_ENTRIES 512 + +typedef struct TeaVM_HashtableEntry { + TeaVM_String* data; + int32_t hash; + struct TeaVM_HashtableEntry* next; +} TeaVM_HashtableEntry; + +typedef struct TeaVM_HashtableEntrySet { + TeaVM_HashtableEntry data[TEAVM_HASHTABLE_ENTRIES]; + int32_t size; + struct TeaVM_HashtableEntrySet* next; +} TeaVM_HashtableEntrySet; + +static TeaVM_HashtableEntry** teavm_stringHashtable = NULL; +static TeaVM_HashtableEntrySet* teavm_stringHashtableData = NULL; +static int32_t teavm_stringHashtableSize = 0; +static int32_t teavm_stringHashtableFill = 0; +static int32_t teavm_stringHashtableThreshold = 0; + +static void teavm_updateStringHashtableThreshold() { + teavm_stringHashtableThreshold = (int32_t) (0.6f * teavm_stringHashtableSize) - INT32_C(1); +} + +static TeaVM_HashtableEntry* teavm_stringHashtableNewEntry() { + TeaVM_HashtableEntrySet* data = teavm_stringHashtableData; + if (data == NULL || data->size == TEAVM_HASHTABLE_ENTRIES) { + data = malloc(sizeof(TeaVM_HashtableEntrySet)); + data->next = teavm_stringHashtableData; + data->size = 0; + teavm_stringHashtableData = data; + } + return &data->data[data->size++]; +} + +static void teavm_putStringIntoHashtable(TeaVM_String* str, int32_t hash) { + int32_t index = (uint32_t) hash % teavm_stringHashtableSize; + if (teavm_stringHashtable[index] == NULL) { + teavm_stringHashtableFill++; + } + TeaVM_HashtableEntry* entry = teavm_stringHashtableNewEntry(); + entry->next = teavm_stringHashtable[index]; + entry->hash = hash; + entry->data = str; + teavm_stringHashtable[index] = entry; +} + +static void teavm_rehashStrings() { + TeaVM_HashtableEntry** oldHashtable = teavm_stringHashtable; + TeaVM_HashtableEntrySet* oldHashtableData = teavm_stringHashtableData; + int32_t oldHashtableSize = teavm_stringHashtableSize; + + teavm_stringHashtableSize = teavm_stringHashtableSize * INT32_C(2); + teavm_updateStringHashtableThreshold(); + teavm_stringHashtable = malloc(sizeof(TeaVM_HashtableEntry*) * teavm_stringHashtableSize); + memset(teavm_stringHashtable, 0, sizeof(TeaVM_HashtableEntry*) * teavm_stringHashtableSize); + teavm_stringHashtableData = NULL; + + for (int32_t i = 0; i < oldHashtableSize; ++i) { + TeaVM_HashtableEntry* entry = oldHashtable[i]; + while (entry != NULL) { + teavm_putStringIntoHashtable(entry->data, entry->hash); + entry = entry->next; + } + } + + free(oldHashtable); + while (oldHashtableData != NULL) { + TeaVM_HashtableEntrySet* next = oldHashtableData->next; + free(oldHashtableData); + oldHashtableData = next; + } +} + +TeaVM_String* teavm_registerString(TeaVM_String* str) { + if (teavm_stringHashtable == NULL) { + teavm_stringHashtableSize = 256; + teavm_updateStringHashtableThreshold(); + teavm_stringHashtable = malloc(sizeof(TeaVM_HashtableEntry*) * teavm_stringHashtableSize); + memset(teavm_stringHashtable, 0, sizeof(TeaVM_HashtableEntry*) * teavm_stringHashtableSize); + } + + int32_t hash = teavm_hashCode(str); + int32_t index = (uint32_t) hash % teavm_stringHashtableSize; + TeaVM_HashtableEntry* entry = teavm_stringHashtable[index]; + while (entry != NULL) { + if (entry->hash == hash && teavm_equals(entry->data, str)) { + return entry->data; + } + entry = entry->next; + } + + if (teavm_stringHashtable[index] == NULL) { + if (teavm_stringHashtableFill >= teavm_stringHashtableThreshold) { + teavm_rehashStrings(); + int32_t index = (uint32_t) hash % teavm_stringHashtableSize; + } + teavm_stringHashtableFill++; + } + + entry = teavm_stringHashtableNewEntry(); + entry->next = teavm_stringHashtable[index]; + entry->hash = hash; + entry->data = str; + teavm_stringHashtable[index] = entry; + + return str; +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java index ae8f6d3ec..6f3d1a95b 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java @@ -65,7 +65,7 @@ class MetadataCIntrinsic implements Generator { } else if (value instanceof String) { int stringIndex = context.stringPool().getStringIndex((String) value); context.includes().includePath("strings.h"); - context.writerBefore().print("(TeaVM_Object*) (teavm_stringPool + " + stringIndex + ")"); + context.writerBefore().print("(TeaVM_Object**) &TEAVM_GET_STRING(" + stringIndex + ")"); } else if (value instanceof Boolean) { context.writerBefore().print((Boolean) value ? "1" : "0"); } else if (value instanceof Integer) { @@ -164,7 +164,7 @@ class MetadataCIntrinsic implements Generator { } else if (Resource.class.isAssignableFrom(cls)) { return "void*"; } else if (cls == String.class) { - return "TeaVM_Object*"; + return "TeaVM_Object**"; } else { throw new IllegalArgumentException("Don't know how to write resource type " + cls); } @@ -237,8 +237,8 @@ class MetadataCIntrinsic implements Generator { if (key == null) { context.writerBefore().print("{ NULL, NULL }"); } else { - context.writerBefore().print("{ teavm_stringPool + " - + context.stringPool().getStringIndex(key) + ", "); + context.writerBefore().print("{ &TEAVM_GET_STRING(" + + context.stringPool().getStringIndex(key) + "), "); writeValue(context, resourceMap.get(key)); context.writerBefore().print("}"); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java index f78d983d8..25c8aeebd 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java @@ -59,10 +59,17 @@ public class ResourceReadCIntrinsic implements Intrinsic { String resourceName = "resources/" + context.escapeFileName(invocation.getMethod().getClassName()) + ".h"; context.includes().includePath(resourceName); + boolean isString = invocation.getMethod().getReturnType().isObject("java.lang.String"); + if (isString) { + context.writer().print("teavm_dereferenceNullable("); + } context.writer().print("TEAVM_FIELD("); context.emit(invocation.getArguments().get(0)); context.writer().print(", ").print(context.names().forClass(invocation.getMethod().getClassName())); context.writer().print(", ").print(name).print(")"); + if (isString) { + context.writer().print(")"); + } } private void applyForResourceMap(IntrinsicContext context, InvocationExpr invocation) { diff --git a/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java b/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java index c23b63678..0b4a21c2f 100644 --- a/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java +++ b/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java @@ -33,7 +33,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import org.teavm.backend.c.CTarget; -import org.teavm.backend.c.generate.SimpleStringPool; import org.teavm.cache.InMemoryMethodNodeCache; import org.teavm.cache.InMemoryProgramCache; import org.teavm.cache.InMemorySymbolTable; @@ -75,7 +74,6 @@ public class IncrementalCBuilder { private MemoryCachedClassReaderSource classSource; private ReferenceCache referenceCache = new ReferenceCache(); - private SimpleStringPool stringPool = new SimpleStringPool(); private InMemorySymbolTable symbolTable = new InMemorySymbolTable(); private InMemorySymbolTable fileSymbolTable = new InMemorySymbolTable(); private InMemorySymbolTable variableSymbolTable = new InMemorySymbolTable(); @@ -309,7 +307,7 @@ public class IncrementalCBuilder { classSource.setProvider(name -> PreOptimizingClassHolderSource.optimize(classPathMapper, name)); long startTime = System.currentTimeMillis(); - CTarget cTarget = new CTarget(stringPool); + CTarget cTarget = new CTarget(); TeaVM vm = new TeaVMBuilder(cTarget) .setReferenceCache(referenceCache) @@ -369,7 +367,6 @@ public class IncrementalCBuilder { astCache.discard(); programCache.discard(); buildTarget.reset(); - stringPool.reset(); cancelRequested = false; }