diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 3cc17cc78..e97f1c493 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -232,7 +232,7 @@ public class WasmTarget implements TeaVMTarget { context.addIntrinsic(new StructureIntrinsic(classGenerator)); context.addIntrinsic(new FunctionIntrinsic(classGenerator)); context.addIntrinsic(new WasmRuntimeIntrinsic()); - context.addIntrinsic(new AllocatorIntrinsic()); + context.addIntrinsic(new AllocatorIntrinsic(classGenerator)); context.addIntrinsic(new PlatformIntrinsic()); context.addIntrinsic(new PlatformClassIntrinsic()); context.addIntrinsic(new PlatformObjectIntrinsic(classGenerator)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index af5d0c6d1..2495f5958 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -21,23 +21,16 @@ import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmType; -import org.teavm.backend.wasm.model.expression.WasmCall; -import org.teavm.backend.wasm.model.expression.WasmConditional; -import org.teavm.backend.wasm.model.expression.WasmExpression; -import org.teavm.backend.wasm.model.expression.WasmInt32Constant; -import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; -import org.teavm.backend.wasm.model.expression.WasmIntBinary; -import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; -import org.teavm.backend.wasm.model.expression.WasmIntType; -import org.teavm.backend.wasm.model.expression.WasmLoadInt32; +import org.teavm.model.BasicBlock; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderSource; import org.teavm.model.ElementModifier; -import org.teavm.model.FieldReference; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; +import org.teavm.model.Program; import org.teavm.model.ValueType; -import org.teavm.runtime.RuntimeClass; +import org.teavm.model.instructions.InitClassInstruction; +import org.teavm.model.lowlevel.ClassInitializerTransformer; public class WasmGenerator { private Decompiler decompiler; @@ -56,6 +49,16 @@ public class WasmGenerator { public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) { ClassHolder cls = classSource.get(methodReference.getClassName()); MethodHolder method = cls.getMethod(methodReference.getDescriptor()); + Program program = bodyMethod.getProgram(); + + if (needsClinitCall(method) && classGenerator.hasClinit(method.getOwnerName())) { + BasicBlock entryBlock = program.basicBlockAt(0); + InitClassInstruction initInsn = new InitClassInstruction(); + initInsn.setClassName(bodyMethod.getOwnerName()); + entryBlock.getInstructions().add(0, initInsn); + } + + new ClassInitializerTransformer().transform(program); RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod); WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference)); @@ -75,19 +78,6 @@ public class WasmGenerator { function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType())); } - if (needsClinitCall(method) && classGenerator.hasClinit(method.getOwnerName())) { - int index = classGenerator.getClassPointer(ValueType.object(method.getOwnerName())) - + classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "flags")); - WasmExpression initFlag = new WasmLoadInt32(4, new WasmInt32Constant(index), WasmInt32Subtype.INT32); - initFlag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, initFlag, - new WasmInt32Constant(RuntimeClass.INITIALIZED)); - - WasmConditional conditional = new WasmConditional(initFlag); - conditional.getThenBlock().getBody().add(new WasmCall( - WasmMangling.mangleInitializer(method.getOwnerName()))); - function.getBody().add(conditional); - } - WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function, methodReference, firstVariable); methodAst.getBody().acceptVisitor(visitor); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java index ae1119532..16fadfa39 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java @@ -16,15 +16,35 @@ package org.teavm.backend.wasm.intrinsics; import java.util.stream.Collectors; +import org.teavm.ast.ConstantExpr; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.WasmRuntime; +import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; +import org.teavm.backend.wasm.model.expression.WasmIntBinary; +import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmLoadInt32; +import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; import org.teavm.runtime.Allocator; +import org.teavm.runtime.RuntimeClass; public class AllocatorIntrinsic implements WasmIntrinsic { + private static final FieldReference flagsField = new FieldReference(RuntimeClass.class.getName(), "flags"); + private WasmClassGenerator classGenerator; + private int flagsFieldOffset; + + public AllocatorIntrinsic(WasmClassGenerator classGenerator) { + this.classGenerator = classGenerator; + flagsFieldOffset = classGenerator.getFieldOffset(flagsField); + } + @Override public boolean isApplicable(MethodReference methodReference) { if (!methodReference.getClassName().equals(Allocator.class.getName())) { @@ -33,6 +53,7 @@ public class AllocatorIntrinsic implements WasmIntrinsic { switch (methodReference.getName()) { case "fillZero": case "moveMemoryBlock": + case "isInitialized": return true; default: return false; @@ -44,14 +65,23 @@ public class AllocatorIntrinsic implements WasmIntrinsic { switch (invocation.getMethod().getName()) { case "fillZero": case "moveMemoryBlock": { - MethodReference delegateMetod = new MethodReference(WasmRuntime.class.getName(), + MethodReference delegateMethod = new MethodReference(WasmRuntime.class.getName(), invocation.getMethod().getDescriptor()); - WasmCall call = new WasmCall(WasmMangling.mangleMethod(delegateMetod)); + WasmCall call = new WasmCall(WasmMangling.mangleMethod(delegateMethod)); call.getArguments().addAll(invocation.getArguments().stream() .map(manager::generate) .collect(Collectors.toList())); return call; } + case "isInitialized": { + ConstantExpr argument = (ConstantExpr) invocation.getArguments().get(0); + ValueType type = (ValueType) argument.getValue(); + int pointer = classGenerator.getClassPointer(type) + flagsFieldOffset; + WasmExpression flags = new WasmLoadInt32(4, new WasmInt32Constant(pointer), WasmInt32Subtype.INT32); + WasmExpression flag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, flags, + new WasmInt32Constant(RuntimeClass.INITIALIZED)); + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, flag, new WasmInt32Constant(0)); + } default: throw new IllegalArgumentException(invocation.getMethod().toString()); } diff --git a/core/src/main/java/org/teavm/model/lowlevel/ClassInitializerTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/ClassInitializerTransformer.java new file mode 100644 index 000000000..18199001d --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/ClassInitializerTransformer.java @@ -0,0 +1,113 @@ +/* + * Copyright 2016 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.model.lowlevel; + +import java.util.ArrayList; +import java.util.List; +import org.teavm.model.BasicBlock; +import org.teavm.model.Incoming; +import org.teavm.model.Instruction; +import org.teavm.model.MethodReference; +import org.teavm.model.Phi; +import org.teavm.model.Program; +import org.teavm.model.ValueType; +import org.teavm.model.Variable; +import org.teavm.model.instructions.BranchingCondition; +import org.teavm.model.instructions.BranchingInstruction; +import org.teavm.model.instructions.ClassConstantInstruction; +import org.teavm.model.instructions.InitClassInstruction; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.JumpInstruction; +import org.teavm.model.util.ProgramUtils; +import org.teavm.runtime.Allocator; + +public class ClassInitializerTransformer { + public void transform(Program program) { + int[] basicBlockMap = new int[program.basicBlockCount()]; + for (int i = 0; i < basicBlockMap.length; ++i) { + basicBlockMap[i] = i; + } + + for (int i = 0; i < basicBlockMap.length; ++i) { + BasicBlock block = program.basicBlockAt(i); + List instructions = block.getInstructions(); + for (int j = 0; j < instructions.size(); ++j) { + Instruction instruction = instructions.get(j); + if (instruction instanceof InitClassInstruction) { + String className = ((InitClassInstruction) instruction).getClassName(); + + BasicBlock continueBlock = program.createBasicBlock(); + List instructionsToMove = instructions.subList(j + 1, instructions.size()); + List instructionsToMoveCopy = new ArrayList<>(instructionsToMove); + instructionsToMove.clear(); + continueBlock.getInstructions().addAll(instructionsToMoveCopy); + continueBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); + + BasicBlock initBlock = program.createBasicBlock(); + instructions.remove(j); + initBlock.getInstructions().add(instruction); + JumpInstruction jumpToContinue = new JumpInstruction(); + jumpToContinue.setTarget(continueBlock); + initBlock.getInstructions().add(jumpToContinue); + + createInitCheck(program, block, className, continueBlock, initBlock); + + basicBlockMap[i] = continueBlock.getIndex(); + block = continueBlock; + instructions = block.getInstructions(); + j = 0; + } + } + } + + for (int i = 0; i < basicBlockMap.length; ++i) { + BasicBlock block = program.basicBlockAt(i); + for (Phi phi : block.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + int source = incoming.getSource().getIndex(); + BasicBlock mappedSource = program.basicBlockAt(basicBlockMap[source]); + incoming.setSource(mappedSource); + } + } + } + } + + private void createInitCheck(Program program, BasicBlock block, String className, BasicBlock continueBlock, + BasicBlock initBlock) { + Variable clsVariable = program.createVariable(); + Variable initializedVariable = program.createVariable(); + + ClassConstantInstruction clsConstant = new ClassConstantInstruction(); + clsConstant.setReceiver(clsVariable); + clsConstant.setConstant(ValueType.object(className)); + block.getInstructions().add(clsConstant); + + InvokeInstruction checkInitialized = new InvokeInstruction(); + checkInitialized.setType(InvocationType.SPECIAL); + checkInitialized.setMethod(new MethodReference(Allocator.class, "isInitialized", + Class.class, boolean.class)); + checkInitialized.getArguments().add(clsVariable); + checkInitialized.setReceiver(initializedVariable); + block.getInstructions().add(checkInitialized); + + BranchingInstruction branching = new BranchingInstruction(BranchingCondition.EQUAL); + branching.setOperand(initializedVariable); + branching.setConsequent(continueBlock); + branching.setAlternative(initBlock); + block.getInstructions().add(branching); + } +} diff --git a/core/src/main/java/org/teavm/runtime/Allocator.java b/core/src/main/java/org/teavm/runtime/Allocator.java index e5578ae36..251045878 100644 --- a/core/src/main/java/org/teavm/runtime/Allocator.java +++ b/core/src/main/java/org/teavm/runtime/Allocator.java @@ -53,4 +53,6 @@ public final class Allocator { public static native void fillZero(Address address, int count); public static native void moveMemoryBlock(Address source, Address target, int count); + + public static native boolean isInitialized(Class cls); }