C: support array bound checking

This commit is contained in:
Alexey Andreev 2019-10-04 19:15:22 +03:00
parent 6f50eefaf9
commit 94a96f571b
10 changed files with 313 additions and 165 deletions

View File

@ -106,12 +106,13 @@ import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.lowlevel.CheckInstructionTransformation;
import org.teavm.model.lowlevel.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer;
import org.teavm.model.lowlevel.ExportDependencyListener;
import org.teavm.model.lowlevel.LowLevelNullCheckFilter;
import org.teavm.model.lowlevel.NullCheckTransformation;
import org.teavm.model.lowlevel.ShadowStackTransformer;
import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.ClassPatch;
import org.teavm.model.transformation.NullCheckInsertion;
import org.teavm.model.util.AsyncMethodFinder;
@ -150,7 +151,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private ClassInitializerTransformer classInitializerTransformer;
private ShadowStackTransformer shadowStackTransformer;
private NullCheckInsertion nullCheckInsertion;
private NullCheckTransformation nullCheckTransformation;
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
private CheckInstructionTransformation checkTransformation;
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
private int minHeapSize = 4 * 1024 * 1024;
private int maxHeapSize = 128 * 1024 * 1024;
@ -222,7 +224,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
classInitializerTransformer = new ClassInitializerTransformer();
shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed);
nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics));
nullCheckTransformation = new NullCheckTransformation();
checkTransformation = new CheckInstructionTransformation();
controller.addVirtualMethods(VIRTUAL_METHODS::contains);
}
@ -267,6 +269,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class,
"throwArrayIndexOutOfBoundsException", void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class))
.propagate(0, NullPointerException.class.getName())
.use();
@ -324,6 +328,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
@Override
public void beforeOptimizations(Program program, MethodReader method) {
nullCheckInsertion.transformProgram(program, method.getReference());
boundCheckInsertion.transformProgram(program, method.getReference());
}
@Override
@ -331,7 +336,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program);
if (!longjmpUsed) {
nullCheckTransformation.apply(program, method.getResultType());
checkTransformation.apply(program, method.getResultType());
}
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
.apply(program, method.getReference());
@ -394,8 +399,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
if (incremental) {
configHeaderWriter.println("#define TEAVM_INCREMENTAL 1");
}
if (longjmpUsed) {
configHeaderWriter.println("#define TEAVM_USE_SETJMP 1");
if (!longjmpUsed) {
configHeaderWriter.println("#define TEAVM_USE_SETJMP 0");
}
if (vmAssertions) {
configHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1");

View File

@ -1423,7 +1423,37 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
@Override
public void visit(BoundCheckExpr expr) {
if (expr.getArray() == null && !expr.isLower()) {
expr.getIndex().acceptVisitor(this);
return;
}
boolean needParenthesis = false;
if (needsCallSiteId()) {
needParenthesis = true;
withCallSite();
}
String functionName;
if (expr.getArray() == null) {
functionName = "teavm_checkLowerBound";
} else if (!expr.isLower()) {
functionName = "teavm_checkUpperBound";
} else {
functionName = "teavm_checkBounds";
}
writer.print(functionName);
writer.print("(");
expr.getIndex().acceptVisitor(this);
if (expr.getArray() != null) {
writer.print(", ");
visitReference(expr.getArray());
}
writer.print(")");
if (needParenthesis) {
writer.print(")");
}
}
private IntrinsicContext intrinsicContext = new IntrinsicContext() {

View File

@ -0,0 +1,221 @@
/*
* 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.model.lowlevel;
import org.teavm.model.BasicBlock;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.BinaryInstruction;
import org.teavm.model.instructions.BinaryOperation;
import org.teavm.model.instructions.BoundCheckInstruction;
import org.teavm.model.instructions.BranchingCondition;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.NumericOperandType;
import org.teavm.model.util.BasicBlockSplitter;
import org.teavm.runtime.ExceptionHandling;
public class CheckInstructionTransformation {
private BasicBlock returnBlock;
private BasicBlock next;
public void apply(Program program, ValueType returnType) {
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
returnBlock = null;
int count = program.basicBlockCount();
for (int i = 0; i < count; ++i) {
next = program.basicBlockAt(i);
BasicBlock block;
while (next != null) {
block = next;
next = null;
for (Instruction instruction : block) {
if (instruction instanceof NullCheckInstruction) {
replaceNullCheck(splitter, program, (NullCheckInstruction) instruction);
} else if (instruction instanceof BoundCheckInstruction) {
replaceBoundCheck(splitter, program, (BoundCheckInstruction) instruction);
}
}
}
}
if (returnBlock != null) {
ExitInstruction fakeExit = new ExitInstruction();
if (returnType != ValueType.VOID) {
Variable fakeReturnVar = program.createVariable();
createFakeReturnValue(returnBlock, fakeReturnVar, returnType);
fakeExit.setValueToReturn(fakeReturnVar);
}
returnBlock.add(fakeExit);
}
splitter.fixProgram();
}
private void replaceNullCheck(BasicBlockSplitter splitter, Program program, NullCheckInstruction nullCheck) {
BasicBlock block = nullCheck.getBasicBlock();
BasicBlock continueBlock = splitter.split(block, nullCheck);
BasicBlock throwBlock = program.createBasicBlock();
InvokeInstruction throwNPE = new InvokeInstruction();
throwNPE.setType(InvocationType.SPECIAL);
throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
void.class));
throwNPE.setLocation(nullCheck.getLocation());
throwBlock.add(throwNPE);
jumpToReturn(program, nullCheck, throwBlock);
BranchingInstruction jumpIfNull = new BranchingInstruction(BranchingCondition.NULL);
jumpIfNull.setOperand(nullCheck.getValue());
jumpIfNull.setConsequent(throwBlock);
jumpIfNull.setAlternative(continueBlock);
jumpIfNull.setLocation(nullCheck.getLocation());
nullCheck.replace(jumpIfNull);
AssignInstruction assign = new AssignInstruction();
assign.setAssignee(nullCheck.getValue());
assign.setReceiver(nullCheck.getReceiver());
assign.setLocation(nullCheck.getLocation());
continueBlock.addFirst(assign);
next = continueBlock;
}
private void replaceBoundCheck(BasicBlockSplitter splitter, Program program, BoundCheckInstruction boundCheck) {
BasicBlock block = boundCheck.getBasicBlock();
BasicBlock continueBlock = splitter.split(block, boundCheck);
BasicBlock throwBlock = program.createBasicBlock();
BasicBlock ifPositiveBlock = continueBlock;
if (boundCheck.isLower() && boundCheck.getArray() != null) {
ifPositiveBlock = program.createBasicBlock();
}
InvokeInstruction throwAIOOBE = new InvokeInstruction();
throwAIOOBE.setType(InvocationType.SPECIAL);
throwAIOOBE.setMethod(new MethodReference(ExceptionHandling.class, "throwArrayIndexOutOfBoundsException",
void.class));
throwAIOOBE.setLocation(boundCheck.getLocation());
throwBlock.add(throwAIOOBE);
jumpToReturn(program, boundCheck, throwBlock);
if (boundCheck.isLower()) {
BranchingInstruction jumpIfLess = new BranchingInstruction(BranchingCondition.LESS);
jumpIfLess.setOperand(boundCheck.getIndex());
jumpIfLess.setConsequent(throwBlock);
jumpIfLess.setAlternative(ifPositiveBlock);
jumpIfLess.setLocation(boundCheck.getLocation());
boundCheck.replace(jumpIfLess);
}
if (boundCheck.getArray() != null) {
ArrayLengthInstruction arrayLength = new ArrayLengthInstruction();
arrayLength.setArray(boundCheck.getArray());
arrayLength.setReceiver(program.createVariable());
arrayLength.setLocation(boundCheck.getLocation());
BinaryInstruction compare = new BinaryInstruction(BinaryOperation.COMPARE, NumericOperandType.INT);
compare.setFirstOperand(boundCheck.getIndex());
compare.setSecondOperand(arrayLength.getReceiver());
compare.setReceiver(program.createVariable());
compare.setLocation(boundCheck.getLocation());
BranchingInstruction jumpIfGreater = new BranchingInstruction(BranchingCondition.GREATER_OR_EQUAL);
jumpIfGreater.setOperand(compare.getReceiver());
jumpIfGreater.setConsequent(throwBlock);
jumpIfGreater.setAlternative(continueBlock);
jumpIfGreater.setLocation(boundCheck.getLocation());
if (boundCheck.isLower()) {
ifPositiveBlock.add(jumpIfGreater);
} else {
boundCheck.replace(jumpIfGreater);
}
jumpIfGreater.insertPrevious(arrayLength);
jumpIfGreater.insertPrevious(compare);
}
AssignInstruction assign = new AssignInstruction();
assign.setAssignee(boundCheck.getIndex());
assign.setReceiver(boundCheck.getReceiver());
assign.setLocation(boundCheck.getLocation());
continueBlock.addFirst(assign);
}
private void jumpToReturn(Program program, Instruction instruction, BasicBlock throwBlock) {
if (returnBlock == null) {
returnBlock = program.createBasicBlock();
}
JumpInstruction jumpToFakeReturn = new JumpInstruction();
jumpToFakeReturn.setTarget(returnBlock);
jumpToFakeReturn.setLocation(instruction.getLocation());
throwBlock.add(jumpToFakeReturn);
}
private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case INTEGER:
case CHARACTER:
IntegerConstantInstruction intZero = new IntegerConstantInstruction();
intZero.setReceiver(variable);
block.add(intZero);
return;
case LONG:
LongConstantInstruction longZero = new LongConstantInstruction();
longZero.setReceiver(variable);
block.add(longZero);
return;
case FLOAT:
FloatConstantInstruction floatZero = new FloatConstantInstruction();
floatZero.setReceiver(variable);
block.add(floatZero);
return;
case DOUBLE:
DoubleConstantInstruction doubleZero = new DoubleConstantInstruction();
doubleZero.setReceiver(variable);
block.add(doubleZero);
return;
}
}
NullConstantInstruction nullConstant = new NullConstantInstruction();
nullConstant.setReceiver(variable);
block.add(nullConstant);
}
}

View File

@ -40,6 +40,7 @@ import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.BinaryBranchingCondition;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BoundCheckInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
@ -283,7 +284,7 @@ public class ExceptionHandlingShadowStackContributor {
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction
|| insn instanceof NullCheckInstruction) {
|| insn instanceof NullCheckInstruction || insn instanceof BoundCheckInstruction) {
return true;
} else if (insn instanceof InvokeInstruction) {
return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod());

View File

@ -1,142 +0,0 @@
/*
* 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.model.lowlevel;
import org.teavm.model.BasicBlock;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.BranchingCondition;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.util.BasicBlockSplitter;
import org.teavm.runtime.ExceptionHandling;
public class NullCheckTransformation {
public void apply(Program program, ValueType returnType) {
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
BasicBlock returnBlock = null;
int count = program.basicBlockCount();
for (int i = 0; i < count; ++i) {
BasicBlock next = program.basicBlockAt(i);
BasicBlock block;
while (next != null) {
block = next;
next = null;
for (Instruction instruction : block) {
if (!(instruction instanceof NullCheckInstruction)) {
continue;
}
NullCheckInstruction nullCheck = (NullCheckInstruction) instruction;
BasicBlock continueBlock = splitter.split(block, nullCheck);
BasicBlock throwBlock = program.createBasicBlock();
InvokeInstruction throwNPE = new InvokeInstruction();
throwNPE.setType(InvocationType.SPECIAL);
throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
void.class));
throwNPE.setLocation(nullCheck.getLocation());
throwBlock.add(throwNPE);
if (returnBlock == null) {
returnBlock = program.createBasicBlock();
}
JumpInstruction jumpToFakeReturn = new JumpInstruction();
jumpToFakeReturn.setTarget(returnBlock);
jumpToFakeReturn.setLocation(nullCheck.getLocation());
throwBlock.add(jumpToFakeReturn);
BranchingInstruction jumpIfNull = new BranchingInstruction(BranchingCondition.NULL);
jumpIfNull.setOperand(nullCheck.getValue());
jumpIfNull.setConsequent(throwBlock);
jumpIfNull.setAlternative(continueBlock);
jumpIfNull.setLocation(nullCheck.getLocation());
nullCheck.replace(jumpIfNull);
AssignInstruction assign = new AssignInstruction();
assign.setAssignee(nullCheck.getValue());
assign.setReceiver(nullCheck.getReceiver());
assign.setLocation(nullCheck.getLocation());
continueBlock.addFirst(assign);
next = continueBlock;
break;
}
}
}
if (returnBlock != null) {
ExitInstruction fakeExit = new ExitInstruction();
if (returnType != ValueType.VOID) {
Variable fakeReturnVar = program.createVariable();
createFakeReturnValue(returnBlock, fakeReturnVar, returnType);
fakeExit.setValueToReturn(fakeReturnVar);
}
returnBlock.add(fakeExit);
}
splitter.fixProgram();
}
private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case INTEGER:
case CHARACTER:
IntegerConstantInstruction intZero = new IntegerConstantInstruction();
intZero.setReceiver(variable);
block.add(intZero);
return;
case LONG:
LongConstantInstruction longZero = new LongConstantInstruction();
longZero.setReceiver(variable);
block.add(longZero);
return;
case FLOAT:
FloatConstantInstruction floatZero = new FloatConstantInstruction();
floatZero.setReceiver(variable);
block.add(floatZero);
return;
case DOUBLE:
DoubleConstantInstruction doubleZero = new DoubleConstantInstruction();
doubleZero.setReceiver(variable);
block.add(doubleZero);
return;
}
}
NullConstantInstruction nullConstant = new NullConstantInstruction();
nullConstant.setReceiver(variable);
block.add(nullConstant);
}
}

View File

@ -126,6 +126,12 @@ public final class ExceptionHandling {
throw new NullPointerException();
}
@Unmanaged
@Export(name = "teavm_throwArrayIndexOutOfBoundsException")
public static void throwArrayIndexOutOfBoundsException() {
throw new ArrayIndexOutOfBoundsException();
}
@Unmanaged
public static int callStackSize() {
Address stackFrame = ShadowStack.getStackTop();

View File

@ -5,7 +5,6 @@
#include "definitions.h"
#include "heapdump.h"
#include "memory.h"
#include "exceptions.h"
#if TEAVM_MEMORY_TRACE
#include "heaptrace.h"
@ -53,6 +52,10 @@ typedef struct TeaVM_String {
extern char* teavm_beforeClasses;
extern void* teavm_throwClassCastException();
extern void teavm_throwNullPointerException();
extern void teavm_throwArrayIndexOutOfBoundsException();
#define TEAVM_PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - teavm_beforeClasses) >> 3))
#define TEAVM_UNPACK_CLASS(cls) ((TeaVM_Class*) (teavm_beforeClasses + ((cls) << 3)))
#define TEAVM_CLASS_OF(obj) (TEAVM_UNPACK_CLASS(((TeaVM_Object*) (obj))->header))

View File

@ -1,5 +1,6 @@
#pragma once
#include "definitions.h"
#include "core.h"
#include <stdint.h>
#include <stddef.h>
@ -7,9 +8,6 @@
#include <setjmp.h>
#endif
extern void* teavm_throwClassCastException();
extern void teavm_throwNullPointerException();
#if TEAVM_USE_SETJMP
#define TEAVM_JUMP_SUPPORTED 1
#define TEAVM_TRY \
@ -35,18 +33,7 @@ extern void teavm_throwNullPointerException();
#define TEAVM_JUMP_TO_FRAME(frame, id) \
teavm_stackTop = (TeaVM_StackFrame*) (frame); \
longjmp(*teavm_stackTop->jmpTarget, id)
inline static void* teavm_nullCheck(void* o) {
if (o == NULL) {
teavm_throwNullPointerException();
#if TEAVM_UNIX
__builtin_unreachable();
#endif
#if TEAVM_WINDOWS
__assume(0);
#endif
}
return o;
}
#if TEAVM_UNIX
#define TEAVM_UNREACHABLE __builtin_unreachable();
@ -58,6 +45,38 @@ extern void teavm_throwNullPointerException();
#define TEAVM_UNREACHABLE return;
#endif
inline static void* teavm_nullCheck(void* o) {
if (o == NULL) {
teavm_throwNullPointerException();
TEAVM_UNREACHABLE
}
return o;
}
inline static int32_t teavm_checkBounds(int32_t index, void* array) {
if (index < 0 || index >= TEAVM_ARRAY_LENGTH(array)) {
teavm_throwArrayIndexOutOfBoundsException();
TEAVM_UNREACHABLE
}
return index;
}
inline static int32_t teavm_checkLowerBound(int32_t index) {
if (index < 0) {
teavm_throwArrayIndexOutOfBoundsException();
TEAVM_UNREACHABLE
}
return index;
}
inline static int32_t teavm_checkUpperBound(int32_t index, void* array) {
if (index >= TEAVM_ARRAY_LENGTH(array)) {
teavm_throwArrayIndexOutOfBoundsException();
TEAVM_UNREACHABLE
}
return index;
}
#else
#define TEAVM_JUMP_SUPPORTED 0
#define TEAVM_JUMP_TO_FRAME(frame, id)

View File

@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include "definitions.h"
#if TEAVM_MEMORY_TRACE

View File

@ -4,6 +4,10 @@
#include "string.h"
#include "definitions.h"
#if TEAVM_USE_SETJMP
#include <setjmp.h>
#endif
typedef struct {
TeaVM_String* value;
} TeaVM_StringPtr;