Skip to content

Commit

Permalink
ios13_postfix#5: compiler, support for offsetOf in Structs (#431)
Browse files Browse the repository at this point in the history
* * jetbrains intellij plugin for gradle version updated

* * intellij maven pom reworked into stand-alone. as mvn:version is not able to update parent version number

* * added sonatype repo for snapshot dependencies

* * eclipse plugin's maven.pom switched into standalone (without) as tyho plugin is not able update parent's version during release

* * release script reworked to properly pick up new idea artifact and update it gradle version

* * Idea: release script updated to use property to specify the dependencies. otherview version:commit failed to pick up non existing dependencies (for future version)

* * added code to synthesize `offsetOf` method in struct classes
  • Loading branch information
dkimitsa authored and Tom-Ski committed Jan 10, 2020
1 parent 944af95 commit f6b1a27
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ private StructureType getStructType(SootClass clazz, boolean checkEmpty) {
}

// find out if regular structure is simple wrapper around 8, 16 or 32 int
// (this is needed to find out if this structure will be retured by value on arm7 cpus)
// (this is needed to find out if this structure will be returned by value on arm7 CPUs)
boolean singleIntStruct = result.length == 1 && result[0] instanceof IntegerType && ((IntegerType)result[0]).getBits() <= 32;
if (!singleIntStruct) {
attributes |= StructureType.ATTR_NOT_SINGLE_INT_STRUCT;
Expand Down Expand Up @@ -990,7 +990,20 @@ public Type getStructMemberType(SootMethod method) {

return memberType;
}


/**
* returns own members offsets in bytes from start of struct
*/
public int[] getStructMemberOffsets(StructureType structType) {
// get offset of each struct member by calling llvm api
int membersCount = structType.getTypeCount() - structType.getOwnMembersOffset();
int offset = structType.getOwnMembersOffset(); // inherited struct (if any) goes as member 0, own members starts 1
int[] offsets = new int[membersCount];
for (int idx = 0; idx < membersCount; idx++)
offsets[idx] = config.getDataLayout().getOffsetOfElement(structType, offset + idx);
return offsets;
}

protected SootMethod createFakeStructRetMethod(SootMethod originalMethod) {
// Create a new method with the same parameters but a @StructRet parameter inserted first
@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,9 @@ private void compile(Clazz clazz, OutputStream out) throws IOException {
// method that will provide meta flags for runtime to help finding out if stret is required
SootMethod stretMeta = new SootMethod(STRUCT_ATTRIBUTES_METHOD, Collections.EMPTY_LIST, IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE);
sootClass.addMethod(stretMeta);
// method that returns offset of struct member
SootMethod offset = new SootMethod("offsetOf", Collections.singletonList(IntType.v()), IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE);
sootClass.addMethod(offset);
}

mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", config.getOs().getFamily(), config.getArch())));
Expand Down Expand Up @@ -877,8 +880,8 @@ private void compile(Clazz clazz, OutputStream out) throws IOException {
function = method(method);
} else if (hasGlobalValueAnnotation(method)) {
function = globalValueMethod(method);
} else if (isStruct(sootClass) && ("_sizeOf".equals(name)
|| "sizeOf".equals(name) || STRUCT_ATTRIBUTES_METHOD.equals(name) || hasStructMemberAnnotation(method))) {
} else if (isStruct(sootClass) && ("_sizeOf".equals(name) || "sizeOf".equals(name) ||
"offsetOf".equals(name) || STRUCT_ATTRIBUTES_METHOD.equals(name) || hasStructMemberAnnotation(method))) {
function = structMember(method);
} else if (method.isNative()) {
function = nativeMethod(method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public class Functions {
public static final FunctionRef BC_THROW_INCOMPATIBLE_CLASS_CHANGE_ERROR = new FunctionRef("_bcThrowIncompatibleClassChangeError", new FunctionType(VOID, ENV_PTR, I8_PTR));
public static final FunctionRef BC_THROW_ABSTRACT_METHOD_ERROR = new FunctionRef("_bcThrowAbstractMethodError", new FunctionType(VOID, ENV_PTR, I8_PTR));
public static final FunctionRef BC_THROW_CLASS_CAST_EXCEPTION_ARRAY = new FunctionRef("_bcThrowClassCastExceptionArray", new FunctionType(VOID, ENV_PTR, CLASS_PTR, OBJECT_PTR));

public static final FunctionRef BC_THROW_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = new FunctionRef("_bcThrowArrayIndexOutOfBoundsException", new FunctionType(VOID, ENV_PTR, I32, I32));

public static final FunctionRef BC_NEW_BOOLEAN_ARRAY = new FunctionRef("_bcNewBooleanArray", new FunctionType(OBJECT_PTR, ENV_PTR, I32));
public static final FunctionRef BC_NEW_BYTE_ARRAY = new FunctionRef("_bcNewByteArray", new FunctionType(OBJECT_PTR, ENV_PTR, I32));
public static final FunctionRef BC_NEW_CHAR_ARRAY = new FunctionRef("_bcNewCharArray", new FunctionType(OBJECT_PTR, ENV_PTR, I32));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,21 @@
import org.robovm.compiler.Bro.MarshalerFlags;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.llvm.BasicBlockRef;
import org.robovm.compiler.llvm.Bitcast;
import org.robovm.compiler.llvm.Function;
import org.robovm.compiler.llvm.Getelementptr;
import org.robovm.compiler.llvm.IntegerConstant;
import org.robovm.compiler.llvm.Inttoptr;
import org.robovm.compiler.llvm.Label;
import org.robovm.compiler.llvm.Load;
import org.robovm.compiler.llvm.PackedStructureType;
import org.robovm.compiler.llvm.PointerType;
import org.robovm.compiler.llvm.Ret;
import org.robovm.compiler.llvm.StructureType;
import org.robovm.compiler.llvm.Switch;
import org.robovm.compiler.llvm.Type;
import org.robovm.compiler.llvm.Unreachable;
import org.robovm.compiler.llvm.Value;
import org.robovm.compiler.llvm.Variable;
import org.robovm.compiler.llvm.VariableRef;
Expand All @@ -43,6 +47,9 @@
import soot.SootMethod;
import soot.VoidType;

import java.util.HashMap;
import java.util.Map;

/**
* @author niklas
*
Expand All @@ -68,6 +75,8 @@ public void reset(Clazz clazz) {
protected Function doCompile(ModuleBuilder moduleBuilder, SootMethod method) {
if ("_sizeOf".equals(method.getName()) || "sizeOf".equals(method.getName())) {
return structSizeOf(moduleBuilder, method);
} else if ("offsetOf".equals(method.getName())) {
return structOffsetOf(moduleBuilder, method);
} else if (STRUCT_ATTRIBUTES_METHOD.equals(method.getName())) {
return stretMeta(moduleBuilder, method);
} else {
Expand All @@ -82,6 +91,39 @@ private Function structSizeOf(ModuleBuilder moduleBuilder, SootMethod method) {
return fn;
}

private Function structOffsetOf(ModuleBuilder moduleBuilder, SootMethod method) {
Function fn = createMethodFunction(method);
moduleBuilder.addFunction(fn);

int[] offsets = getStructMemberOffsets(structType);
if (offsets.length > 0 ) {
// function code -- basic switch of returns
Label[] switchLabels = new Label[offsets.length];
Map<IntegerConstant, BasicBlockRef> targets = new HashMap<IntegerConstant, BasicBlockRef>();
for (int idx = 0; idx < offsets.length; idx++) {
targets.put(new IntegerConstant(idx), fn.newBasicBlockRef(switchLabels[idx] = new Label(idx)));
}

Value idxValue = fn.getParameterRef(1); // 'env' is parameter 0
Label def = new Label(-1);
fn.add(new Switch(idxValue, fn.newBasicBlockRef(def), targets));

// cases
for (int idx = 0; idx < offsets.length; idx++) {
fn.newBasicBlock(switchLabels[idx]);
fn.add(new Ret(new IntegerConstant(offsets[idx])));
}

// default case -- array out of bounds exception
fn.newBasicBlock(def);
}
Functions.call(fn, Functions.BC_THROW_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION, fn.getParameterRef(0),
new IntegerConstant(offsets.length), fn.getParameterRef(1));
fn.add(new Unreachable());

return fn;
}

private Function stretMeta(ModuleBuilder moduleBuilder, SootMethod method) {
Function fn = createMethodFunction(method);
moduleBuilder.addFunction(fn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,13 @@ private SootMethod resolveMethod(SootClass clazz, String name, String desc) {
method.setDeclared(true);
return method;
}
if (name.equals("offsetOf") && isStruct(clazz)) {
method = new SootMethod("offsetOf", Collections.singletonList(IntType.v()), IntType.v(),
Modifier.PUBLIC | Modifier.STATIC);
method.setDeclaringClass(clazz);
method.setDeclared(true);
return method;
}

SootClass c = !clazz.isInterface() && clazz.hasSuperclass() ? clazz.getSuperclass() : null;
while (c != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,15 @@ Integer doWithType(TargetMachine targetMachine, org.robovm.llvm.Type type) {
}
});
}


public int getOffsetOfElement(StructureType type, int idx) {
return runTypeQuery(type, new TypeCallback<Integer>() {
Integer doWithType(TargetMachine targetMachine, org.robovm.llvm.Type type) {
return (int) targetMachine.getDataLayout().getOffsetOfElement(type, idx);
}
});
}

private static abstract class TypeCallback<T> {
abstract T doWithType(TargetMachine targetMachine, org.robovm.llvm.Type type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.io.File;
import java.io.IOException;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

/**
Expand Down Expand Up @@ -111,6 +112,15 @@ public void testPackedStructures() {
assertEquals(config.getDataLayout().getStoreSize(packed), 12);
}

@Test
public void testStructMemberOffsets() {
StructMemberMethodCompiler compiler = new StructMemberMethodCompiler(config);

StructureType packed = compiler.getStructType(toSootClass(StructForOffsetsPacked.class));
int[] offsets = compiler.getStructMemberOffsets(packed);
assertArrayEquals(new int[]{0, 1, 3}, offsets);
}

@Test
public void testBlockMember() {
Clazz clz = toClazz(StructWithBlock.class);
Expand Down Expand Up @@ -181,6 +191,13 @@ public static class StructWithBlock extends Struct<StructWithBlock> {
@StructMember(0) public native @Block Runnable getA();
}

@Packed(1)
public static class StructForOffsetsPacked extends Struct<StructForOffsetsPacked> {
@StructMember(0) public native byte getA();
@StructMember(1) public native short getB();
@StructMember(2) public native int getC();
}

public static class MockHome extends Home {
public MockHome(File homeDir) {
super(homeDir, false);
Expand Down
5 changes: 5 additions & 0 deletions compiler/llvm/src/main/java/org/robovm/llvm/DataLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public long getTypeStoreSize(Type type) {
return LLVM.StoreSizeOfType(ref, type.ref).longValue();
}

public long getOffsetOfElement(Type type, int idx) {
checkDisposed();
return LLVM.OffsetOfElement(ref, type.ref, idx).longValue();
}

@Override
public int hashCode() {
final int prime = 31;
Expand Down

0 comments on commit f6b1a27

Please sign in to comment.