Fix bugs in C backend to make more tests pass

This commit is contained in:
Alexey Andreev 2018-04-21 00:55:43 +03:00
parent 4990dbe8e4
commit 8f0320e217
17 changed files with 223 additions and 51 deletions

View File

@ -65,12 +65,18 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
for (BasicBlock block : program.getBasicBlocks()) {
for (Instruction instruction : block) {
Variable receiver;
MarkerKind kind;
if (instruction instanceof InvokeInstruction) {
MethodReference methodRef = ((InvokeInstruction) instruction).getMethod();
MethodReader method = innerSource.resolve(methodRef);
if (method == null || !isMarker(method)) {
if (method == null) {
continue;
}
kind = isMarker(method);
if (kind == null) {
continue;
}
if (!method.hasModifier(ElementModifier.STATIC)) {
diagnostics.error(new CallLocation(containingMethod, instruction.getLocation()),
"Method '{{m0}}' is marked with '{{c1}}' and should be static",
@ -87,9 +93,14 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
} else if (instruction instanceof GetFieldInstruction) {
FieldReference fieldRef = ((GetFieldInstruction) instruction).getField();
FieldReader field = innerSource.resolve(fieldRef);
if (field == null || !isMarker(field)) {
if (field == null) {
continue;
}
kind = isMarker(field);
if (kind == null) {
continue;
}
if (!field.hasModifier(ElementModifier.STATIC)) {
diagnostics.error(new CallLocation(containingMethod, instruction.getLocation()),
"Field '{{f0}}' is marked with '{{c1}}' and should be static",
@ -113,7 +124,7 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
} else {
IntegerConstantInstruction trueResult = new IntegerConstantInstruction();
trueResult.setReceiver(receiver);
trueResult.setConstant(1);
trueResult.setConstant(kind == MarkerKind.TRUE ? 1 : 0);
trueResult.setLocation(instruction.getLocation());
instruction.replace(trueResult);
}
@ -130,21 +141,26 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
}
}
private boolean isMarker(MemberReader member) {
private MarkerKind isMarker(MemberReader member) {
AnnotationReader annot = member.getAnnotations().get(PlatformMarker.class.getName());
if (annot == null) {
return false;
return null;
}
AnnotationValue value = annot.getValue("value");
if (value == null) {
return true;
return MarkerKind.TRUE;
}
String tagToMatch = value.getString();
for (String tag : tags) {
if (tag.equals(tagToMatch)) {
return true;
return MarkerKind.TRUE;
}
}
return false;
return MarkerKind.FALSE;
}
enum MarkerKind {
TRUE,
FALSE
}
}

View File

@ -242,7 +242,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
return toLowerCaseSystem(codePoint);
}
@Import(module = "runtime", name = "tolower")
@Import(module = "runtime", name = "towlower")
private static native int toLowerCaseSystem(int codePoint);
public static char toUpperCase(char ch) {
@ -258,7 +258,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
return toUpperCaseSystem(codePoint);
}
@Import(module = "runtime", name = "toupper")
@Import(module = "runtime", name = "towupper")
private static native int toUpperCaseSystem(int codePoint);
public static int digit(char ch, int radix) {

View File

@ -512,7 +512,11 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
@SuppressWarnings("unchecked")
public T[] getEnumConstants() {
return isEnum() ? (T[]) Platform.getEnumConstants(platformClass).clone() : null;
if (!isEnum()) {
return null;
}
Platform.initClass(platformClass);
return (T[]) Platform.getEnumConstants(platformClass).clone();
}
@SuppressWarnings("unchecked")

View File

@ -32,6 +32,6 @@ class TConsoleOutputStreamStderr extends TOutputStream {
writeImpl(b);
}
@Import(name = "putchar", module = "runtime")
@Import(name = "putwchar", module = "runtime")
static native void writeImpl(int b);
}

View File

@ -222,15 +222,15 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
}
@JSBody(params = "v", script = "return isNaN(v);")
@Import(module = "runtime", name = "isNaN")
@Import(module = "runtime", name = "isnan")
public static native boolean isNaN(double v);
@JSBody(script = "return NaN;")
@Import(module = "runtime", name = "getNaN")
@Import(module = "runtime", name = "TeaVM_getNaN")
private static native double getNaN();
@JSBody(params = "v", script = "return !isFinite(v);")
@Import(module = "runtime", name = "isInfinite")
@Import(module = "runtime", name = "isinf")
public static native boolean isInfinite(double v);
public static long doubleToRawLongBits(double value) {

View File

@ -90,7 +90,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
}
@JSBody(params = "v", script = "return isNaN(v);")
@Import(module = "runtime", name = "isNaN")
@Import(module = "runtime", name = "isnan")
public static native boolean isNaN(float v);
public static boolean isInfinite(float v) {
@ -98,11 +98,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
}
@JSBody(params = "v", script = "return isFinite(v);")
@Import(module = "runtime", name = "isFinite")
@Import(module = "runtime", name = "isfinite")
private static native boolean isFinite(float v);
@JSBody(script = "return NaN;")
@Import(module = "runtime", name = "getNaN")
@Import(module = "runtime", name = "TeaVM_getNaN")
private static native float getNaN();
public static float parseFloat(TString string) throws TNumberFormatException {

View File

@ -15,6 +15,7 @@
*/
package org.teavm.classlib.java.util;
import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.lang.TMath;
import org.teavm.classlib.java.lang.TObject;
@ -39,7 +40,7 @@ public class TRandom extends TObject implements TSerializable {
}
protected int next(int bits) {
return (int) (random() * (1L << TMath.min(32, bits)));
return (int) (nextDouble() * (1L << TMath.min(32, bits)));
}
public void nextBytes(byte[] bytes) {
@ -53,7 +54,7 @@ public class TRandom extends TObject implements TSerializable {
}
public int nextInt(int n) {
return (int) (random() * n);
return (int) (nextDouble() * n);
}
public long nextLong() {
@ -65,13 +66,20 @@ public class TRandom extends TObject implements TSerializable {
}
public float nextFloat() {
return (float) random();
return (float) nextDouble();
}
public double nextDouble() {
return random();
if (PlatformDetector.isC()) {
return crand();
} else {
return random();
}
}
@Import(name = "TeaVM_rand")
private static native double crand();
/**
* Generate a random number with Gaussian distribution:
* centered around 0 with a standard deviation of 1.0.

View File

@ -352,6 +352,7 @@ public class CTarget implements TeaVMTarget {
Set<? extends ValueType> types) {
writer.println("int main(int argc, char** argv) {").indent();
writer.println("TeaVM_beforeInit();");
writer.println("initHeap(" + minHeapSize + ");");
generateVirtualTableHeaders(context, writer, types);
generateStringPoolHeaders(context, writer);
@ -417,6 +418,6 @@ public class CTarget implements TeaVMTarget {
@Override
public String[] getPlatformTags() {
return new String[] { PlatformMarkers.C, PlatformMarkers.WEBASSEMBLY };
return new String[] { PlatformMarkers.C, PlatformMarkers.LOW_LEVEL };
}
}

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.c.generators.Generator;
@ -36,6 +37,7 @@ import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
@ -276,11 +278,20 @@ public class ClassGenerator {
String name = context.getNames().forClassInstance(type);
vtableForwardWriter.print("static ").print(structName).print(" ").print(name).println(";");
ClassReader cls = className != null ? context.getClassSource().get(className) : null;
String enumConstants;
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) {
enumConstants = writeEnumConstants(cls, name);
} else {
enumConstants = "NULL";
}
vtableWriter.print("static alignas(8) ").print(structName).print(" ").print(name).println(" = {").indent();
if (className != null) {
vtableWriter.println(".parent = {").indent();
generateRuntimeClassInitializer(type);
generateRuntimeClassInitializer(type, enumConstants);
vtableWriter.outdent().println("},");
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className);
@ -301,19 +312,34 @@ public class ClassGenerator {
}
}
} else {
generateRuntimeClassInitializer(type);
generateRuntimeClassInitializer(type, enumConstants);
}
vtableWriter.outdent().println("};");
}
private void generateRuntimeClassInitializer(ValueType type) {
private String writeEnumConstants(ClassReader cls, String baseName) {
List<FieldReader> fields = cls.getFields().stream()
.filter(f -> f.hasModifier(ElementModifier.ENUM))
.collect(Collectors.toList());
String name = baseName + "_enumConstants";
vtableWriter.print("static void* " + name + "[" + (fields.size() + 1) + "] = { ");
vtableWriter.print("(void*) (intptr_t) " + fields.size());
for (FieldReader field : fields) {
vtableWriter.print(", ").print("&" + context.getNames().forStaticField(field.getReference()));
}
vtableWriter.println(" };");
return name;
}
private void generateRuntimeClassInitializer(ValueType type, String enumConstants) {
String sizeExpr;
int tag;
String parent;
String itemTypeExpr;
int flags = 0;
String layout = "NULL";
String initFunction = "NULL";
if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName();
@ -323,13 +349,17 @@ public class ClassGenerator {
className = RuntimeObject.class.getName();
}
if (needsData(cls)) {
if (cls != null && needsData(cls)) {
String structName = context.getNames().forClass(className);
sizeExpr = "(int32_t) (intptr_t) ALIGN(sizeof(" + structName + "), sizeof(void*))";
} else {
sizeExpr = "0";
}
tag = tagRegistry.getRanges(className).get(0).lower;
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) {
flags |= RuntimeClass.ENUM;
}
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
tag = ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0;
parent = cls != null && cls.getParent() != null && types.contains(ValueType.object(cls.getParent()))
? "&" + context.getNames().forClassInstance(ValueType.object(cls.getParent()))
@ -337,6 +367,10 @@ public class ClassGenerator {
itemTypeExpr = "NULL";
int layoutOffset = classLayoutOffsets.getOrDefault(className, -1);
layout = layoutOffset >= 0 ? "classLayouts + " + layoutOffset : "NULL";
if (cls != null && needsInitializer(cls)) {
initFunction = context.getNames().forClassInitializer(className);
}
} else if (type instanceof ValueType.Array) {
parent = "&" + context.getNames().forClassInstance(ValueType.object("java.lang.Object"));
tag = tagRegistry.getRanges("java.lang.Object").get(0).lower;
@ -382,7 +416,9 @@ public class ClassGenerator {
vtableWriter.print(".").print(classFieldName("isSupertypeOf")).println(" = &" + superTypeFunction + ",");
vtableWriter.print(".").print(classFieldName("parent")).println(" = " + parent + ",");
vtableWriter.print(".").print(classFieldName("enumValues")).println(" = NULL,");
vtableWriter.print(".").print(classFieldName("layout")).println(" = " + layout);
vtableWriter.print(".").print(classFieldName("layout")).println(" = " + layout + ",");
vtableWriter.print(".").print(classFieldName("enumValues")).println(" = " + enumConstants + ",");
vtableWriter.print(".").print(classFieldName("init")).println(" = " + initFunction);
}
private void generateVirtualTableStructure(ClassReader cls) {
@ -392,13 +428,15 @@ public class ClassGenerator {
vtableStructuresWriter.println("JavaClass parent;");
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(cls.getName());
for (VirtualTableEntry entry : virtualTable.getEntries().values()) {
String methodName = context.getNames().forVirtualMethod(
new MethodReference(cls.getName(), entry.getMethod()));
vtableStructuresWriter.printType(entry.getMethod().getResultType())
.print(" (*").print(methodName).print(")(");
codeGenerator.generateMethodParameters(vtableStructuresWriter, entry.getMethod(), false, false);
vtableStructuresWriter.println(");");
if (virtualTable != null) {
for (VirtualTableEntry entry : virtualTable.getEntries().values()) {
String methodName = context.getNames().forVirtualMethod(
new MethodReference(cls.getName(), entry.getMethod()));
vtableStructuresWriter.printType(entry.getMethod().getResultType())
.print(" (*").print(methodName).print(")(");
codeGenerator.generateMethodParameters(vtableStructuresWriter, entry.getMethod(), false, false);
vtableStructuresWriter.println(");");
}
}
vtableStructuresWriter.outdent().print("} ").print(name).println(";");
@ -517,7 +555,7 @@ public class ClassGenerator {
}
}
private boolean needsInitializer(ClassHolder cls) {
private boolean needsInitializer(ClassReader cls) {
return !context.getCharacteristics().isStaticInit(cls.getName())
&& !context.getCharacteristics().isStructure(cls.getName())
&& cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null;

View File

@ -266,13 +266,48 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
int index = context.getStringPool().getStringIndex((String) value);
writer.print("(stringPool + " + index + ")");
} else if (value instanceof Integer) {
writer.print("INT32_C(" + value + ")");
int i = (Integer) value;
long v = i;
if (i < 0) {
writer.print("-");
v = -i;
}
writer.print("INT32_C(");
writeLongConstant(v);
writer.print(")");
} else if (value instanceof Long) {
writer.print("INT64_C(" + value + ")");
long v = (Long) value;
if (v < 0) {
writer.print("-");
v = -v;
}
writer.print("INT64_C(");
writeLongConstant(v);
writer.print(")");
} else if (value instanceof Float) {
writer.print(value + "f");
float f = (Float) value;
if (Float.isInfinite(f)) {
if (f < 0) {
writer.print("-");
}
writer.print("INFINITY");
} else if (Float.isNaN(f)) {
writer.print("NAN");
} else {
writer.print(f + "f");
}
} else if (value instanceof Double) {
writer.print(value.toString());
double d = (Double) value;
if (Double.isInfinite(d)) {
if (d < 0) {
writer.print("-");
}
writer.print("INFINITY");
} else if (Double.isNaN(d)) {
writer.print("NAN");
} else {
writer.print(value.toString());
}
} else if (value instanceof Boolean) {
writer.print((Boolean) value ? "1" : "0");
} else if (value instanceof ValueType) {
@ -280,6 +315,14 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
}
}
private void writeLongConstant(long v) {
if (v == Long.MIN_VALUE) {
writer.print("0x8000000000000000");
return;
}
writer.print(String.valueOf(v));
}
@Override
public void visit(VariableExpr expr) {
if (expr.getIndex() == 0) {

View File

@ -24,26 +24,40 @@ public class PlatformClassMetadataIntrinsic implements Intrinsic {
private static final String PLATFORM_CLASS_METADATA = "org.teavm.platform.PlatformClassMetadata";
private static final FieldReference ARRAY_TYPE_FIELD = new FieldReference(
RuntimeClass.class.getName(), "arrayType");
private static final FieldReference SUPERCLASS_FIELD = new FieldReference(
RuntimeClass.class.getName(), "parent");
@Override
public boolean canHandle(MethodReference method) {
if (!method.getClassName().equals(PLATFORM_CLASS_METADATA)) {
return false;
}
return method.getName().equals("getArrayItem");
switch (method.getName()) {
case "getArrayItem":
case "getSuperclass":
return true;
}
return false;
}
@Override
public void apply(IntrinsicContext context, InvocationExpr invocation) {
switch (invocation.getMethod().getName()) {
case "getArrayItem":
context.writer().print("FIELD(");
context.emit(invocation.getArguments().get(0));
context.writer().print(",");
context.writer().print(context.names().forClass(ARRAY_TYPE_FIELD.getClassName())).print(", ");
context.writer().print(context.names().forMemberField(ARRAY_TYPE_FIELD));
context.writer().print(")");
printFieldAccess(context, invocation, ARRAY_TYPE_FIELD);
break;
case "getSuperclass":
printFieldAccess(context, invocation, SUPERCLASS_FIELD);
break;
}
}
private void printFieldAccess(IntrinsicContext context, InvocationExpr invocation, FieldReference field) {
context.writer().print("FIELD(");
context.emit(invocation.getArguments().get(0));
context.writer().print(",");
context.writer().print(context.names().forClass(field.getClassName())).print(", ");
context.writer().print(context.names().forMemberField(field));
context.writer().print(")");
}
}

View File

@ -42,8 +42,6 @@ public class PlatformIntrinsic implements Intrinsic {
case "asJavaClass":
context.emit(invocation.getArguments().get(0));
break;
case "getName":
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2018 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.Function;
import org.teavm.interop.Unmanaged;
public abstract class InitFunction extends Function {
@Unmanaged
public abstract void run();
}

View File

@ -43,6 +43,7 @@ public class RuntimeClass extends RuntimeObject {
public RuntimeClass itemType;
public RuntimeClass arrayType;
public IsSupertypeFunction isSupertypeOf;
public InitFunction init;
public RuntimeClass parent;
public Address enumValues;
public Address layout;

View File

@ -2,7 +2,9 @@
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <uchar.h>
#include <wchar.h>
#include <time.h>
#include <math.h>
@ -87,6 +89,18 @@ static int32_t gc_regionSize = INT32_C(32768);
static int32_t gc_regionMaxCount = INT32_C(0);
static int64_t gc_availableBytes = INT64_C(0);
static double TeaVM_rand() {
return rand() / ((double) RAND_MAX + 1);
}
static inline float TeaVM_getNaN() {
return NAN;
}
static void TeaVM_beforeInit() {
srand(time(NULL));
}
#ifdef __GNUC__
static void initHeap(int64_t heapSize) {
long workSize = heapSize / 16;

View File

@ -112,8 +112,16 @@ public final class Platform {
@PluggableDependency(PlatformGenerator.class)
@InjectedBy(PlatformGenerator.class)
@DelegateTo("initClassLowLevel")
public static native void initClass(PlatformClass cls);
@Unmanaged
private static void initClassLowLevel(RuntimeClass cls) {
if (cls.init != null) {
cls.init.run();
}
}
@InjectedBy(PlatformGenerator.class)
@PluggableDependency(PlatformGenerator.class)
public static native PlatformClass classFromResource(ClassResource resource);

View File

@ -15,17 +15,20 @@
*/
package org.teavm.platform;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
public interface PlatformClassMetadata extends JSObject {
@JSProperty("item")
@Unmanaged
PlatformClass getArrayItem();
@JSProperty
PlatformSequence<PlatformClass> getSupertypes();
@JSProperty
@Unmanaged
PlatformClass getSuperclass();
@JSProperty