Remove call to initializer from wasm generator, put it into IR transformer. Add intrinsic to check whether class has been initializer. Add IR transformer that guards call to initializer with this intrinsic

This commit is contained in:
Alexey Andreev 2016-09-06 23:15:12 +03:00
parent eaf31c1309
commit f7e39e6a4d
5 changed files with 162 additions and 27 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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());
}

View File

@ -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<Instruction> 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<Instruction> instructionsToMove = instructions.subList(j + 1, instructions.size());
List<Instruction> 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);
}
}

View File

@ -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);
}