use of org.robovm.compiler.llvm.Function in project robovm by robovm.
the class CallbackMethodCompiler method compileCallback.
private Function compileCallback(ModuleBuilder moduleBuilder, SootMethod method) {
Function callbackFn = null;
FunctionType nativeFnType = null;
boolean useCWrapper = requiresCWrapper(method);
if (useCWrapper) {
// The C wrapper is the function which is called by native code. It
// handles structs passed/returned by value. It calls an LLVM function
// which has the same signature but all structs passed/returned by value
// replaced by pointers (i8*).
FunctionRef callbackCWrapperRef = getCallbackCWrapperRef(method, Symbols.callbackCSymbol(method));
getCWrapperFunctions().add(createCallbackCWrapper(callbackCWrapperRef.getType(), callbackCWrapperRef.getName(), Symbols.callbackInnerCSymbol(method)));
moduleBuilder.addFunctionDeclaration(new FunctionDeclaration(callbackCWrapperRef));
Type callbackRetType = callbackCWrapperRef.getType().getReturnType() instanceof StructureType ? I8_PTR : callbackCWrapperRef.getType().getReturnType();
Type[] callbackParamTypes = new Type[callbackCWrapperRef.getType().getParameterTypes().length];
for (int i = 0; i < callbackParamTypes.length; i++) {
Type t = callbackCWrapperRef.getType().getParameterTypes()[i];
if (t instanceof StructureType) {
t = I8_PTR;
}
callbackParamTypes[i] = t;
}
moduleBuilder.addAlias(new Alias(Symbols.callbackPtrSymbol(method), Linkage._private, new ConstantBitcast(callbackCWrapperRef, I8_PTR)));
callbackFn = new FunctionBuilder(Symbols.callbackInnerCSymbol(method), new FunctionType(callbackRetType, callbackParamTypes)).build();
nativeFnType = callbackCWrapperRef.getType();
} else {
FunctionType callbackFnType = getCallbackFunctionType(method, false);
callbackFn = FunctionBuilder.callback(method, callbackFnType);
moduleBuilder.addAlias(new Alias(Symbols.callbackPtrSymbol(method), Linkage._private, new ConstantBitcast(callbackFn.ref(), I8_PTR)));
nativeFnType = callbackFnType;
}
moduleBuilder.addFunction(callbackFn);
String targetName = method.isSynchronized() ? Symbols.synchronizedWrapperSymbol(method) : Symbols.methodSymbol(method);
FunctionRef targetFn = new FunctionRef(targetName, getFunctionType(method));
// Increase the attach count for the current thread (attaches the thread
// if not attached)
Value env = call(callbackFn, BC_ATTACH_THREAD_FROM_CALLBACK);
BasicBlockRef bbSuccess = callbackFn.newBasicBlockRef(new Label("success"));
BasicBlockRef bbFailure = callbackFn.newBasicBlockRef(new Label("failure"));
pushCallbackFrame(callbackFn, env);
trycatchAllEnter(callbackFn, env, bbSuccess, bbFailure);
callbackFn.newBasicBlock(bbSuccess.getLabel());
List<MarshaledArg> marshaledArgs = new ArrayList<MarshaledArg>();
ArrayList<Value> args = new ArrayList<Value>();
args.add(env);
if (!method.isStatic()) {
MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, MarshalSite.RECEIVER));
MarshaledArg marshaledArg = new MarshaledArg();
marshaledArg.paramIndex = MarshalSite.RECEIVER;
marshaledArgs.add(marshaledArg);
Value arg = callbackFn.getParameterRef(0);
String targetClassName = getInternalName(method.getDeclaringClass());
arg = marshalNativeToObject(callbackFn, marshalerMethod, marshaledArg, env, targetClassName, arg, MarshalerFlags.CALL_TYPE_CALLBACK);
args.add(arg);
}
for (int i = 0, argIdx = 0; i < method.getParameterCount(); i++, argIdx++) {
if (!method.isStatic() && argIdx == 0) {
argIdx++;
}
Value arg = callbackFn.getParameterRef(argIdx);
soot.Type type = method.getParameterType(i);
if (needsMarshaler(type)) {
MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, i));
String targetClassName = getInternalName(type);
if (arg.getType() instanceof PrimitiveType) {
arg = marshalNativeToValueObject(callbackFn, marshalerMethod, env, targetClassName, arg, MarshalerFlags.CALL_TYPE_CALLBACK);
} else {
MarshaledArg marshaledArg = new MarshaledArg();
marshaledArg.paramIndex = i;
marshaledArgs.add(marshaledArg);
Type nativeType = nativeFnType.getParameterTypes()[argIdx];
if (nativeType instanceof StructureType) {
// Struct passed by value on the stack. Make a heap copy of the data and marshal that.
DataLayout dataLayout = config.getDataLayout();
Value heapCopy = call(callbackFn, BC_COPY_STRUCT, env, arg, new IntegerConstant(dataLayout.getAllocSize(nativeType)));
arg = marshalNativeToObject(callbackFn, marshalerMethod, marshaledArg, env, targetClassName, heapCopy, MarshalerFlags.CALL_TYPE_CALLBACK);
} else {
arg = marshalNativeToObject(callbackFn, marshalerMethod, marshaledArg, env, targetClassName, arg, MarshalerFlags.CALL_TYPE_CALLBACK);
}
}
} else {
arg = marshalNativeToPrimitive(callbackFn, method, i, arg);
}
args.add(arg);
}
Value result = call(callbackFn, targetFn, args);
// Call Marshaler.updateNative() for each object that was marshaled before
// the call.
updateNative(method, callbackFn, env, MarshalerFlags.CALL_TYPE_CALLBACK, marshaledArgs);
// Marshal the returned value to a native value before returning
if (needsMarshaler(method.getReturnType())) {
MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method));
Type nativeType = callbackFn.getType().getReturnType();
if (nativeType instanceof PrimitiveType) {
result = marshalValueObjectToNative(callbackFn, marshalerMethod, nativeType, env, result, MarshalerFlags.CALL_TYPE_CALLBACK);
} else {
result = marshalObjectToNative(callbackFn, marshalerMethod, null, nativeType, env, result, MarshalerFlags.CALL_TYPE_CALLBACK);
}
} else {
result = marshalPrimitiveToNative(callbackFn, method, result);
}
trycatchLeave(callbackFn, env);
popCallbackFrame(callbackFn, env);
call(callbackFn, BC_DETACH_THREAD_FROM_CALLBACK, env);
callbackFn.add(new Ret(result));
callbackFn.newBasicBlock(bbFailure.getLabel());
trycatchLeave(callbackFn, env);
popCallbackFrame(callbackFn, env);
Value ex = call(callbackFn, BC_EXCEPTION_CLEAR, env);
// Call Marshaler.updateNative() for each object that was marshaled before
// the call.
updateNative(method, callbackFn, env, MarshalerFlags.CALL_TYPE_CALLBACK, marshaledArgs);
call(callbackFn, BC_DETACH_THREAD_FROM_CALLBACK, env);
call(callbackFn, BC_THROW, env, ex);
callbackFn.add(new Unreachable());
return callbackFn;
}
use of org.robovm.compiler.llvm.Function in project robovm by robovm.
the class ClassCompiler method createLdcClassWrapper.
private Function createLdcClassWrapper() {
Function fn = FunctionBuilder.ldcExternal(sootClass);
Value info = getInfoStruct(fn, sootClass);
Value result = call(fn, LDC_CLASS_WRAPPER, fn.getParameterRef(0), info);
fn.add(new Ret(result));
return fn;
}
use of org.robovm.compiler.llvm.Function in project robovm by robovm.
the class ClassCompiler method compileMethod.
private Function compileMethod(AbstractMethodCompiler methodCompiler, SootMethod method) {
Function fn = methodCompiler.compile(mb, method);
for (Trampoline t : methodCompiler.getTrampolines()) {
List<SootMethod> l = trampolines.get(t);
if (l == null) {
l = new ArrayList<>();
trampolines.put(t, l);
}
l.add(method);
}
catches.addAll(methodCompiler.getCatches());
return fn;
}
use of org.robovm.compiler.llvm.Function in project robovm by robovm.
the class ClassCompiler method createClassInitWrapperFunction.
private Function createClassInitWrapperFunction(FunctionRef targetFn) {
Function fn = FunctionBuilder.clinitWrapper(targetFn);
Value info = getInfoStruct(fn, sootClass);
Variable infoHeader = fn.newVariable(new PointerType(new StructureType(I8_PTR, I32)));
fn.add(new Bitcast(infoHeader, info, infoHeader.getType()));
Variable infoHeaderFlags = fn.newVariable(new PointerType(I32));
fn.add(new Getelementptr(infoHeaderFlags, infoHeader.ref(), 0, 1));
Variable flags = fn.newVariable(I32);
fn.add(new Load(flags, infoHeaderFlags.ref()));
Variable initializedFlag = fn.newVariable(I32);
fn.add(new And(initializedFlag, flags.ref(), new IntegerConstant(CI_INITIALIZED)));
Variable initialized = fn.newVariable(I1);
fn.add(new Icmp(initialized, Icmp.Condition.eq, initializedFlag.ref(), new IntegerConstant(CI_INITIALIZED)));
Label trueLabel = new Label();
Label falseLabel = new Label();
fn.add(new Br(initialized.ref(), fn.newBasicBlockRef(trueLabel), fn.newBasicBlockRef(falseLabel)));
fn.newBasicBlock(trueLabel);
Value result = call(fn, targetFn, fn.getParameterRefs());
fn.add(new Ret(result));
fn.newBasicBlock(falseLabel);
call(fn, BC_INITIALIZE_CLASS, fn.getParameterRef(0), info);
fn.add(new Br(fn.newBasicBlockRef(trueLabel)));
return fn;
}
use of org.robovm.compiler.llvm.Function in project robovm by robovm.
the class ClassCompiler method compile.
private void compile(Clazz clazz, OutputStream out) throws IOException {
javaMethodCompiler.reset(clazz);
bridgeMethodCompiler.reset(clazz);
callbackMethodCompiler.reset(clazz);
nativeMethodCompiler.reset(clazz);
structMemberMethodCompiler.reset(clazz);
globalValueMethodCompiler.reset(clazz);
ClazzInfo ci = clazz.resetClazzInfo();
mb = new ModuleBuilder();
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.beforeClass(config, clazz, mb);
}
sootClass = clazz.getSootClass();
trampolines = new HashMap<>();
catches = new HashSet<String>();
classFields = getClassFields(config.getOs(), config.getArch(), sootClass);
instanceFields = getInstanceFields(config.getOs(), config.getArch(), sootClass);
classType = getClassType(config.getOs(), config.getArch(), sootClass);
instanceType = getInstanceType(config.getOs(), config.getArch(), sootClass);
attributesEncoder.encode(mb, sootClass);
// will never be initialized.
if (!sootClass.declaresMethodByName("<clinit>") && hasConstantValueTags(classFields)) {
SootMethod clinit = new SootMethod("<clinit>", Collections.EMPTY_LIST, VoidType.v(), Modifier.STATIC);
JimpleBody body = Jimple.v().newBody(clinit);
clinit.setActiveBody(body);
body.getUnits().add(new JReturnVoidStmt());
this.sootClass.addMethod(clinit);
}
if (isStruct(sootClass)) {
SootMethod _sizeOf = new SootMethod("_sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PROTECTED | Modifier.NATIVE);
sootClass.addMethod(_sizeOf);
SootMethod sizeOf = new SootMethod("sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE);
sootClass.addMethod(sizeOf);
}
mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", config.getOs().getFamily(), config.getArch())));
mb.addInclude(getClass().getClassLoader().getResource("header.ll"));
mb.addFunction(createLdcClass());
mb.addFunction(createLdcClassWrapper());
Function allocator = createAllocator();
mb.addFunction(allocator);
mb.addFunction(createClassInitWrapperFunction(allocator.ref()));
for (SootField f : sootClass.getFields()) {
Function getter = createFieldGetter(f, classFields, classType, instanceFields, instanceType);
Function setter = createFieldSetter(f, classFields, classType, instanceFields, instanceType);
mb.addFunction(getter);
mb.addFunction(setter);
if (f.isStatic() && !f.isPrivate()) {
mb.addFunction(createClassInitWrapperFunction(getter.ref()));
if (!f.isFinal()) {
mb.addFunction(createClassInitWrapperFunction(setter.ref()));
}
}
}
// After this point no changes to methods/fields may be done by CompilerPlugins.
ci.initClassInfo();
for (SootMethod method : sootClass.getMethods()) {
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.beforeMethod(config, clazz, method, mb);
}
String name = method.getName();
Function function = null;
if (hasBridgeAnnotation(method)) {
function = bridgeMethod(method);
} else if (hasGlobalValueAnnotation(method)) {
function = globalValueMethod(method);
} else if (isStruct(sootClass) && ("_sizeOf".equals(name) || "sizeOf".equals(name) || hasStructMemberAnnotation(method))) {
function = structMember(method);
} else if (method.isNative()) {
function = nativeMethod(method);
} else if (!method.isAbstract()) {
function = method(method);
}
if (hasCallbackAnnotation(method)) {
callbackMethod(method);
}
if (!name.equals("<clinit>") && !name.equals("<init>") && !method.isPrivate() && !method.isStatic() && !Modifier.isFinal(method.getModifiers()) && !Modifier.isFinal(sootClass.getModifiers())) {
createLookupFunction(method);
}
if (method.isStatic() && !name.equals("<clinit>")) {
String fnName = method.isSynchronized() ? Symbols.synchronizedWrapperSymbol(method) : Symbols.methodSymbol(method);
FunctionRef fn = new FunctionRef(fnName, getFunctionType(method));
mb.addFunction(createClassInitWrapperFunction(fn));
}
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
if (function != null) {
compilerPlugin.afterMethod(config, clazz, method, mb, function);
}
}
}
for (Trampoline trampoline : trampolines.keySet()) {
Set<String> deps = new HashSet<String>();
Set<Triple<String, String, String>> mDeps = new HashSet<>();
trampolineResolver.compile(mb, clazz, trampoline, deps, mDeps);
for (SootMethod m : trampolines.get(trampoline)) {
MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
mi.addClassDependencies(deps, false);
mi.addInvokeMethodDependencies(mDeps, false);
}
}
/*
* Add method dependencies from overriding methods to the overridden
* super method(s). These will be reversed by the DependencyGraph to
* create edges from the super/interface method to the overriding
* method.
*/
Map<SootMethod, Set<SootMethod>> overriddenMethods = getOverriddenMethods(this.sootClass);
for (SootMethod from : overriddenMethods.keySet()) {
MethodInfo mi = ci.getMethod(from.getName(), getDescriptor(from));
for (SootMethod to : overriddenMethods.get(from)) {
mi.addSuperMethodDependency(getInternalName(to.getDeclaringClass()), to.getName(), getDescriptor(to), false);
}
}
/*
* Edge case. A method in a superclass might satisfy an interface method
* in the interfaces implemented by this class. See e.g. the abstract
* class HashMap$HashIterator which doesn't implement Iterator but has
* the hasNext() and other methods. We add a dependency from the current
* class to the super method to ensure it's included if the current
* class is linked in.
*/
if (sootClass.hasSuperclass()) {
for (SootClass interfaze : getImmediateInterfaces(sootClass)) {
for (SootMethod m : interfaze.getMethods()) {
if (!m.isStatic()) {
try {
this.sootClass.getMethod(m.getName(), m.getParameterTypes());
} catch (RuntimeException e) {
/*
* Not found. Find the implementation in
* superclasses.
*/
SootMethod superMethod = null;
for (SootClass sc = sootClass.getSuperclass(); sc.hasSuperclass(); sc = sc.getSuperclass()) {
try {
SootMethod candidate = sc.getMethod(m.getName(), m.getParameterTypes());
if (!candidate.isStatic()) {
superMethod = candidate;
break;
}
} catch (RuntimeException e2) {
// Not found.
}
}
if (superMethod != null) {
ci.addSuperMethodDependency(getInternalName(superMethod.getDeclaringClass()), superMethod.getName(), getDescriptor(superMethod), false);
}
}
}
}
}
}
Global classInfoStruct = null;
try {
if (!sootClass.isInterface()) {
config.getVTableCache().get(sootClass);
}
classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), Linkage.weak, createClassInfoStruct());
} catch (IllegalArgumentException e) {
// VTable throws this if any of the superclasses of the class is actually an interface.
// Shouldn't happen frequently but the DRLVM test suite has some tests for this.
// The Linker will take care of making sure the class cannot be loaded at runtime.
classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), I8_PTR, true);
}
mb.addGlobal(classInfoStruct);
/*
* Emit an internal i8* alias for the info struct which MethodCompiler
* can use when referencing this info struct in exception landing pads
* in methods in the same class. See #1007.
*/
mb.addAlias(new Alias(classInfoStruct.getName() + "_i8ptr", Linkage._private, new ConstantBitcast(classInfoStruct.ref(), I8_PTR)));
Function infoFn = FunctionBuilder.infoStruct(sootClass);
infoFn.add(new Ret(new ConstantBitcast(classInfoStruct.ref(), I8_PTR_PTR)));
mb.addFunction(infoFn);
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.afterClass(config, clazz, mb);
}
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
mb.build().write(writer);
writer.flush();
ci.setCatchNames(catches);
// Make sure no class or interface has zero dependencies
ci.addClassDependency("java/lang/Object", false);
if (sootClass.hasSuperclass() && !sootClass.isInterface()) {
ci.addClassDependency(getInternalName(sootClass.getSuperclass()), false);
}
for (SootClass iface : sootClass.getInterfaces()) {
ci.addClassDependency(getInternalName(iface), false);
}
for (SootField f : sootClass.getFields()) {
addClassDependencyIfNeeded(clazz, f.getType(), false);
}
for (SootMethod m : sootClass.getMethods()) {
MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
addClassDependencyIfNeeded(clazz, mi, m.getReturnType(), false);
@SuppressWarnings("unchecked") List<soot.Type> paramTypes = (List<soot.Type>) m.getParameterTypes();
for (soot.Type type : paramTypes) {
addClassDependencyIfNeeded(clazz, mi, type, false);
}
}
ci.addClassDependencies(attributesEncoder.getDependencies(), false);
ci.addClassDependencies(catches, false);
for (Trampoline t : trampolines.keySet()) {
if (t instanceof Checkcast) {
ci.addCheckcast(t.getTarget());
} else if (t instanceof Instanceof) {
ci.addInstanceof(t.getTarget());
} else if (t instanceof Invokevirtual || t instanceof Invokeinterface) {
ci.addInvoke(t.getTarget() + "." + ((Invoke) t).getMethodName() + ((Invoke) t).getMethodDesc());
}
}
clazz.saveClazzInfo();
}
Aggregations