Search in sources :

Example 1 with CompilerPlugin

use of org.robovm.compiler.plugin.CompilerPlugin in project robovm by robovm.

the class Linker method link.

public void link(Set<Clazz> classes) throws IOException {
    for (CompilerPlugin plugin : config.getCompilerPlugins()) {
        plugin.beforeLinker(config, this, classes);
    }
    Arch arch = config.getArch();
    OS os = config.getOs();
    Set<Clazz> linkClasses = new TreeSet<Clazz>(classes);
    config.getLogger().info("Linking %d classes (%s %s %s)", linkClasses.size(), os, arch, config.isDebug() ? "debug" : "release");
    ModuleBuilder mb = new ModuleBuilder();
    mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", os.getFamily(), arch)));
    mb.addInclude(getClass().getClassLoader().getResource("header.ll"));
    mb.addGlobal(new Global("_bcRuntimeData", runtimeDataToBytes()));
    ArrayConstantBuilder staticLibs = new ArrayConstantBuilder(I8_PTR);
    for (Config.Lib lib : config.getLibs()) {
        String p = lib.getValue();
        if (p.endsWith(".a")) {
            p = new File(p).getName();
            String libName = p.substring(0, p.length() - 2);
            if (libName.startsWith("lib")) {
                libName = libName.substring(3);
            }
            staticLibs.add(mb.getString(libName));
        }
    }
    staticLibs.add(new NullConstant(Type.I8_PTR));
    mb.addGlobal(new Global("_bcStaticLibs", new ConstantGetelementptr(mb.newGlobal(staticLibs.build()).ref(), 0, 0)));
    HashTableGenerator<String, Constant> bcpHashGen = new HashTableGenerator<String, Constant>(new ModifiedUtf8HashFunction());
    HashTableGenerator<String, Constant> cpHashGen = new HashTableGenerator<String, Constant>(new ModifiedUtf8HashFunction());
    int classCount = 0;
    Map<ClazzInfo, TypeInfo> typeInfos = new HashMap<ClazzInfo, TypeInfo>();
    for (Clazz clazz : linkClasses) {
        TypeInfo typeInfo = new TypeInfo();
        typeInfo.clazz = clazz;
        typeInfo.id = classCount++;
        typeInfos.put(clazz.getClazzInfo(), typeInfo);
        StructureConstant infoErrorStruct = createClassInfoErrorStruct(mb, clazz.getClazzInfo());
        Global info = null;
        if (infoErrorStruct == null) {
            info = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), external, I8_PTR, false);
        } else {
            typeInfo.error = true;
            info = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), infoErrorStruct);
        }
        mb.addGlobal(info);
        if (clazz.isInBootClasspath()) {
            bcpHashGen.put(clazz.getInternalName(), new ConstantBitcast(info.ref(), I8_PTR));
        } else {
            cpHashGen.put(clazz.getInternalName(), new ConstantBitcast(info.ref(), I8_PTR));
        }
    }
    mb.addGlobal(new Global("_bcBootClassesHash", new ConstantGetelementptr(mb.newGlobal(bcpHashGen.generate(), true).ref(), 0, 0)));
    mb.addGlobal(new Global("_bcClassesHash", new ConstantGetelementptr(mb.newGlobal(cpHashGen.generate(), true).ref(), 0, 0)));
    ArrayConstantBuilder bootClasspathValues = new ArrayConstantBuilder(I8_PTR);
    ArrayConstantBuilder classpathValues = new ArrayConstantBuilder(I8_PTR);
    List<Path> allPaths = new ArrayList<Path>();
    allPaths.addAll(config.getClazzes().getPaths());
    allPaths.addAll(config.getResourcesPaths());
    for (Path path : allPaths) {
        String entryName = null;
        if (config.isSkipInstall() && config.getTarget().canLaunchInPlace()) {
            entryName = path.getFile().getAbsolutePath();
        } else {
            entryName = config.getTarget().getInstallRelativeArchivePath(path);
        }
        if (path.isInBootClasspath()) {
            bootClasspathValues.add(mb.getString(entryName));
        } else {
            classpathValues.add(mb.getString(entryName));
        }
    }
    bootClasspathValues.add(new NullConstant(Type.I8_PTR));
    classpathValues.add(new NullConstant(Type.I8_PTR));
    mb.addGlobal(new Global("_bcBootclasspath", new ConstantGetelementptr(mb.newGlobal(bootClasspathValues.build()).ref(), 0, 0)));
    mb.addGlobal(new Global("_bcClasspath", new ConstantGetelementptr(mb.newGlobal(classpathValues.build()).ref(), 0, 0)));
    if (config.getMainClass() != null) {
        mb.addGlobal(new Global("_bcMainClass", mb.getString(config.getMainClass())));
    }
    ModuleBuilder[] mbs = new ModuleBuilder[config.getThreads() + 1];
    FunctionRef[] stubRefs = new FunctionRef[mbs.length];
    ArrayConstantBuilder stubRefsArray = new ArrayConstantBuilder(I8_PTR);
    mbs[0] = mb;
    for (int i = 1; i < mbs.length; i++) {
        mbs[i] = new ModuleBuilder();
        mbs[i].addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", os.getFamily(), arch)));
        mbs[i].addInclude(getClass().getClassLoader().getResource("header.ll"));
        Function fn = new FunctionBuilder("_stripped_method" + i, new FunctionType(VOID, ENV_PTR)).linkage(external).build();
        call(fn, BC_THROW_NO_SUCH_METHOD_ERROR, fn.getParameterRef(0), mbs[i].getString("Method has been stripped out of the executable"));
        fn.add(new Unreachable());
        mbs[i].addFunction(fn);
        mb.addFunctionDeclaration(new FunctionDeclaration(fn.ref()));
        stubRefs[i] = fn.ref();
        stubRefsArray.add(new ConstantBitcast(fn.ref(), I8_PTR));
    }
    stubRefsArray.add(new NullConstant(I8_PTR));
    mb.addGlobal(new Global("_bcStrippedMethodStubs", stubRefsArray.build()));
    Random rnd = new Random();
    buildTypeInfos(typeInfos);
    Set<String> checkcasts = new HashSet<>();
    Set<String> instanceofs = new HashSet<>();
    Set<String> invokes = new HashSet<>();
    for (Clazz clazz : linkClasses) {
        ClazzInfo ci = clazz.getClazzInfo();
        checkcasts.addAll(ci.getCheckcasts());
        instanceofs.addAll(ci.getInstanceofs());
        invokes.addAll(ci.getInvokes());
    }
    Set<String> reachableMethods = new HashSet<>();
    for (Triple<String, String, String> node : config.getDependencyGraph().findReachableMethods()) {
        reachableMethods.add(node.getLeft() + "." + node.getMiddle() + node.getRight());
    }
    int totalMethodCount = 0;
    int reachableMethodCount = 0;
    for (Clazz clazz : linkClasses) {
        int mbIdx = rnd.nextInt(mbs.length - 1) + 1;
        ClazzInfo ci = clazz.getClazzInfo();
        // symbols errors.
        for (MethodInfo mi : ci.getMethods()) {
            if (!mi.isAbstract()) {
                totalMethodCount++;
                if (!reachableMethods.contains(clazz.getInternalName() + "." + mi.getName() + mi.getDesc())) {
                    createStrippedMethodStub(stubRefs[mbIdx], mbs[mbIdx], clazz, mi);
                } else {
                    reachableMethodCount++;
                }
            }
        }
        TypeInfo typeInfo = typeInfos.get(ci);
        if (typeInfo.error) {
            // Add an empty TypeInfo
            mb.addGlobal(new Global(Symbols.typeInfoSymbol(clazz.getInternalName()), new StructureConstantBuilder().add(new IntegerConstant(typeInfo.id)).add(new IntegerConstant(0)).add(new IntegerConstant(-1)).add(new IntegerConstant(0)).add(new IntegerConstant(0)).build()));
        } else {
            int[] classIds = new int[typeInfo.classTypes.length];
            for (int i = 0; i < typeInfo.classTypes.length; i++) {
                classIds[i] = typeInfo.classTypes[i].id;
            }
            int[] interfaceIds = new int[typeInfo.interfaceTypes.length];
            for (int i = 0; i < typeInfo.interfaceTypes.length; i++) {
                interfaceIds[i] = typeInfo.interfaceTypes[i].id;
            }
            mb.addGlobal(new Global(Symbols.typeInfoSymbol(clazz.getInternalName()), new StructureConstantBuilder().add(new IntegerConstant(typeInfo.id)).add(new IntegerConstant((typeInfo.classTypes.length - 1) * 4 + 5 * 4)).add(new IntegerConstant(-1)).add(new IntegerConstant(typeInfo.classTypes.length)).add(new IntegerConstant(typeInfo.interfaceTypes.length)).add(new ArrayConstantBuilder(I32).add(classIds).build()).add(new ArrayConstantBuilder(I32).add(interfaceIds).build()).build()));
            if (!config.isDebug() && !ci.isInterface() && !ci.isFinal() && typeInfo.children.isEmpty()) {
                // which doesn't do any lookup.
                for (MethodInfo mi : ci.getMethods()) {
                    String name = mi.getName();
                    if (!name.equals("<clinit>") && !name.equals("<init>") && !mi.isPrivate() && !mi.isStatic() && !mi.isFinal() && !mi.isAbstract()) {
                        if (invokes.contains(clazz.getInternalName() + "." + name + mi.getDesc())) {
                            if (reachableMethods.contains(clazz.getInternalName() + "." + name + mi.getDesc())) {
                                mbs[mbIdx].addFunction(createLookup(mbs[mbIdx], ci, mi));
                            }
                        }
                    }
                }
            }
        }
        if (checkcasts.contains(clazz.getInternalName())) {
            mbs[mbIdx].addFunction(createCheckcast(mbs[mbIdx], clazz, typeInfo));
        }
        if (instanceofs.contains(clazz.getInternalName())) {
            mbs[mbIdx].addFunction(createInstanceof(mbs[mbIdx], clazz, typeInfo));
        }
    }
    config.getLogger().info("%d methods out of %d included in the executable", reachableMethodCount, totalMethodCount);
    List<File> objectFiles = new ArrayList<File>();
    generateMachineCode(config, mbs, objectFiles);
    for (Clazz clazz : linkClasses) {
        objectFiles.add(config.getOFile(clazz));
    }
    /*
         * Assemble the lines files for all linked classes into the module.
         */
    for (Clazz clazz : linkClasses) {
        File f = config.getLinesOFile(clazz);
        if (f.exists() && f.length() > 0) {
            objectFiles.add(f);
        }
    }
    config.getTarget().build(objectFiles);
}
Also used : HashMap(java.util.HashMap) CompilerPlugin(org.robovm.compiler.plugin.CompilerPlugin) Config(org.robovm.compiler.config.Config) NullConstant(org.robovm.compiler.llvm.NullConstant) StructureConstant(org.robovm.compiler.llvm.StructureConstant) IntegerConstant(org.robovm.compiler.llvm.IntegerConstant) Constant(org.robovm.compiler.llvm.Constant) ArrayConstant(org.robovm.compiler.llvm.ArrayConstant) ConstantBitcast(org.robovm.compiler.llvm.ConstantBitcast) ArrayList(java.util.ArrayList) Global(org.robovm.compiler.llvm.Global) HashTableGenerator(org.robovm.compiler.hash.HashTableGenerator) ModifiedUtf8HashFunction(org.robovm.compiler.hash.ModifiedUtf8HashFunction) ArrayConstantBuilder(org.robovm.compiler.llvm.ArrayConstantBuilder) ModifiedUtf8HashFunction(org.robovm.compiler.hash.ModifiedUtf8HashFunction) Function(org.robovm.compiler.llvm.Function) FunctionDeclaration(org.robovm.compiler.llvm.FunctionDeclaration) Random(java.util.Random) Unreachable(org.robovm.compiler.llvm.Unreachable) TreeSet(java.util.TreeSet) Clazz(org.robovm.compiler.clazz.Clazz) FunctionRef(org.robovm.compiler.llvm.FunctionRef) HashSet(java.util.HashSet) Path(org.robovm.compiler.clazz.Path) OS(org.robovm.compiler.config.OS) FunctionType(org.robovm.compiler.llvm.FunctionType) Arch(org.robovm.compiler.config.Arch) NullConstant(org.robovm.compiler.llvm.NullConstant) ClazzInfo(org.robovm.compiler.clazz.ClazzInfo) StructureConstantBuilder(org.robovm.compiler.llvm.StructureConstantBuilder) IntegerConstant(org.robovm.compiler.llvm.IntegerConstant) StructureConstant(org.robovm.compiler.llvm.StructureConstant) MethodInfo(org.robovm.compiler.clazz.MethodInfo) File(java.io.File) ConstantGetelementptr(org.robovm.compiler.llvm.ConstantGetelementptr)

Example 2 with CompilerPlugin

use of org.robovm.compiler.plugin.CompilerPlugin 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();
}
Also used : Ret(org.robovm.compiler.llvm.Ret) Set(java.util.Set) HashSet(java.util.HashSet) CompilerPlugin(org.robovm.compiler.plugin.CompilerPlugin) ConstantBitcast(org.robovm.compiler.llvm.ConstantBitcast) Global(org.robovm.compiler.llvm.Global) Invoke(org.robovm.compiler.trampoline.Invoke) Function(org.robovm.compiler.llvm.Function) Instanceof(org.robovm.compiler.trampoline.Instanceof) ArrayList(java.util.ArrayList) List(java.util.List) JimpleBody(soot.jimple.JimpleBody) Checkcast(org.robovm.compiler.trampoline.Checkcast) Invokevirtual(org.robovm.compiler.trampoline.Invokevirtual) FunctionRef(org.robovm.compiler.llvm.FunctionRef) HashSet(java.util.HashSet) JReturnVoidStmt(soot.jimple.internal.JReturnVoidStmt) Trampoline(org.robovm.compiler.trampoline.Trampoline) ClazzInfo(org.robovm.compiler.clazz.ClazzInfo) SootClass(soot.SootClass) Invokeinterface(org.robovm.compiler.trampoline.Invokeinterface) Triple(org.apache.commons.lang3.tuple.Triple) CodeGenFileType(org.robovm.llvm.binding.CodeGenFileType) BooleanType(soot.BooleanType) StructureType(org.robovm.compiler.llvm.StructureType) PointerType(org.robovm.compiler.llvm.PointerType) ShortType(soot.ShortType) ByteType(soot.ByteType) DoubleType(soot.DoubleType) FloatType(soot.FloatType) IntType(soot.IntType) CharType(soot.CharType) LongType(soot.LongType) RefLikeType(soot.RefLikeType) Type(org.robovm.compiler.llvm.Type) PrimType(soot.PrimType) VoidType(soot.VoidType) Alias(org.robovm.compiler.llvm.Alias) SootMethod(soot.SootMethod) SootField(soot.SootField) MethodInfo(org.robovm.compiler.clazz.MethodInfo) OutputStreamWriter(java.io.OutputStreamWriter)

Example 3 with CompilerPlugin

use of org.robovm.compiler.plugin.CompilerPlugin in project robovm by robovm.

the class ClassCompiler method generateMachineCode.

private static void generateMachineCode(Config config, Clazz clazz, byte[] llData, List<String> cCode) throws IOException {
    if (config.isDumpIntermediates()) {
        File llFile = config.getLlFile(clazz);
        llFile.getParentFile().mkdirs();
        FileUtils.writeByteArrayToFile(llFile, llData);
        File cFile = config.getCFile(clazz);
        if (cCode.isEmpty()) {
            cFile.delete();
        } else {
            FileUtils.writeLines(cFile, "ascii", cCode);
        }
    }
    File oFile = config.getOFile(clazz);
    try (Context context = new Context()) {
        try (Module module = Module.parseIR(context, llData, clazz.getClassName())) {
            if (!cCode.isEmpty()) {
                int size = 0;
                for (String s : cCode) {
                    size += s.length();
                }
                StringBuilder sb = new StringBuilder(size);
                for (String s : cCode) {
                    sb.append(s);
                }
                try (Module m2 = Module.parseClangString(context, sb.toString(), clazz.getClassName() + ".c", config.getClangTriple())) {
                    module.link(m2);
                    for (org.robovm.llvm.Function f1 : m2.getFunctions()) {
                        String name = f1.getName();
                        org.robovm.llvm.Function f2 = module.getFunctionByName(name);
                        if (Symbols.isBridgeCSymbol(name) || Symbols.isCallbackCSymbol(name) || Symbols.isCallbackInnerCSymbol(name)) {
                            f2.setLinkage(org.robovm.llvm.binding.Linkage.PrivateLinkage);
                            if (Symbols.isCallbackInnerCSymbol(name)) {
                                // TODO: We should also always inline the bridge functions but for some reason
                                // that makes the RoboVM tests hang indefinitely.
                                f2.removeAttribute(Attribute.NoInlineAttribute);
                                f2.addAttribute(Attribute.AlwaysInlineAttribute);
                            }
                        }
                    }
                }
            }
            try (PassManager passManager = createPassManager(config)) {
                passManager.run(module);
            }
            if (config.isDumpIntermediates()) {
                File bcFile = config.getBcFile(clazz);
                bcFile.getParentFile().mkdirs();
                module.writeBitcode(bcFile);
            }
            String triple = config.getTriple();
            Target target = Target.lookupTarget(triple);
            try (TargetMachine targetMachine = target.createTargetMachine(triple, config.getArch().getLlvmCpu(), null, config.isDebug() ? CodeGenOptLevel.CodeGenLevelNone : null, RelocMode.RelocPIC, null)) {
                targetMachine.setAsmVerbosityDefault(true);
                targetMachine.setFunctionSections(true);
                targetMachine.setDataSections(true);
                targetMachine.getOptions().setNoFramePointerElim(true);
                // NOTE: Doesn't have any effect on x86. See #503.
                targetMachine.getOptions().setPositionIndependentExecutable(!config.isDebug());
                ByteArrayOutputStream output = new ByteArrayOutputStream(256 * 1024);
                targetMachine.emit(module, output, CodeGenFileType.AssemblyFile);
                byte[] asm = output.toByteArray();
                output.reset();
                patchAsmWithFunctionSizes(config, clazz, new ByteArrayInputStream(asm), output);
                asm = output.toByteArray();
                if (config.isDumpIntermediates()) {
                    File sFile = config.getSFile(clazz);
                    sFile.getParentFile().mkdirs();
                    FileUtils.writeByteArrayToFile(sFile, asm);
                }
                oFile.getParentFile().mkdirs();
                ByteArrayOutputStream oFileBytes = new ByteArrayOutputStream();
                targetMachine.assemble(asm, clazz.getClassName(), oFileBytes);
                new HfsCompressor().compress(oFile, oFileBytes.toByteArray(), config);
                for (CompilerPlugin plugin : config.getCompilerPlugins()) {
                    plugin.afterObjectFile(config, clazz, oFile);
                }
                /*
                     * Read out line number info from the .o file if any and
                     * assemble into a separate .o file.
                     */
                String symbolPrefix = config.getOs().getFamily() == OS.Family.darwin ? "_" : "";
                symbolPrefix += Symbols.EXTERNAL_SYMBOL_PREFIX;
                ModuleBuilder linesMb = null;
                try (ObjectFile objectFile = ObjectFile.load(oFile)) {
                    for (Symbol symbol : objectFile.getSymbols()) {
                        if (symbol.getSize() > 0 && symbol.getName().startsWith(symbolPrefix)) {
                            List<LineInfo> lineInfos = objectFile.getLineInfos(symbol);
                            if (!lineInfos.isEmpty()) {
                                Collections.sort(lineInfos, new Comparator<LineInfo>() {

                                    public int compare(LineInfo o1, LineInfo o2) {
                                        return Long.compare(o1.getAddress(), o2.getAddress());
                                    }
                                });
                                // The base address of the method which will be used to calculate offsets into the method
                                long baseAddress = symbol.getAddress();
                                // The first line number in the method. All other line numbers in the table will be deltas against this.
                                int firstLineNumber = lineInfos.get(0).getLineNumber();
                                // Calculate the max address and line number offsets
                                long maxAddressOffset = 0;
                                long maxLineOffset = 0;
                                for (LineInfo lineInfo : lineInfos) {
                                    maxAddressOffset = Math.max(maxAddressOffset, lineInfo.getAddress() - baseAddress);
                                    maxLineOffset = Math.max(maxLineOffset, lineInfo.getLineNumber() - firstLineNumber);
                                }
                                // Calculate the number of bytes needed to represent the highest offsets.
                                // Either 1, 2 or 4 bytes will be used.
                                int addressOffsetSize = (maxAddressOffset & ~0xff) == 0 ? 1 : ((maxAddressOffset & ~0xffff) == 0 ? 2 : 4);
                                int lineOffsetSize = (maxLineOffset & ~0xff) == 0 ? 1 : ((maxLineOffset & ~0xffff) == 0 ? 2 : 4);
                                // The size of the address offsets table. We skip the first LineInfo as its offset is always 0.
                                int addressOffsetTableSize = addressOffsetSize * (lineInfos.size() - 1);
                                // Pad size of address offset table to make sure line offsets are aligned properly 
                                int addressOffsetPadding = (lineOffsetSize - (addressOffsetTableSize & (lineOffsetSize - 1))) & (lineOffsetSize - 1);
                                addressOffsetTableSize += addressOffsetPadding;
                                // The first 32 bits of the line number info contains the number of line numbers
                                // minus the first. The 4 most significant bits are used to store the number of
                                // bytes needed by each entry in each table.
                                int flags = 0;
                                flags = addressOffsetSize - 1;
                                flags <<= 2;
                                flags |= lineOffsetSize - 1;
                                flags <<= 28;
                                flags |= (lineInfos.size() - 1) & 0x0fffffff;
                                StructureConstantBuilder builder = new StructureConstantBuilder();
                                builder.add(new IntegerConstant(flags)).add(new IntegerConstant(firstLineNumber));
                                for (LineInfo lineInfo : lineInfos.subList(1, lineInfos.size())) {
                                    if (addressOffsetSize == 1) {
                                        builder.add(new IntegerConstant((byte) (lineInfo.getAddress() - baseAddress)));
                                    } else if (addressOffsetSize == 2) {
                                        builder.add(new IntegerConstant((short) (lineInfo.getAddress() - baseAddress)));
                                    } else {
                                        builder.add(new IntegerConstant((int) (lineInfo.getAddress() - baseAddress)));
                                    }
                                }
                                // Padding
                                for (int i = 0; i < addressOffsetPadding; i++) {
                                    builder.add(new IntegerConstant((byte) 0));
                                }
                                for (LineInfo lineInfo : lineInfos.subList(1, lineInfos.size())) {
                                    if (lineOffsetSize == 1) {
                                        builder.add(new IntegerConstant((byte) (lineInfo.getLineNumber() - firstLineNumber)));
                                    } else if (lineOffsetSize == 2) {
                                        builder.add(new IntegerConstant((short) (lineInfo.getLineNumber() - firstLineNumber)));
                                    } else {
                                        builder.add(new IntegerConstant((int) (lineInfo.getLineNumber() - firstLineNumber)));
                                    }
                                }
                                // Extract the method's name and descriptor from the symbol and
                                // build the linetable symbol name.
                                String methodName = symbol.getName().substring(symbol.getName().lastIndexOf('.') + 1);
                                methodName = methodName.substring(0, methodName.indexOf('('));
                                String methodDesc = symbol.getName().substring(symbol.getName().lastIndexOf('('));
                                String linetableSymbol = Symbols.linetableSymbol(clazz.getInternalName(), methodName, methodDesc);
                                if (linesMb == null) {
                                    linesMb = new ModuleBuilder();
                                }
                                linesMb.addGlobal(new Global(linetableSymbol, builder.build(), true));
                            }
                        }
                    }
                }
                if (linesMb != null) {
                    byte[] linesData = linesMb.build().toString().getBytes("UTF-8");
                    if (config.isDumpIntermediates()) {
                        File linesLlFile = config.getLinesLlFile(clazz);
                        linesLlFile.getParentFile().mkdirs();
                        FileUtils.writeByteArrayToFile(linesLlFile, linesData);
                    }
                    try (Module linesModule = Module.parseIR(context, linesData, clazz.getClassName() + ".lines")) {
                        File linesOFile = config.getLinesOFile(clazz);
                        ByteArrayOutputStream linesOBytes = new ByteArrayOutputStream();
                        targetMachine.emit(linesModule, linesOBytes, CodeGenFileType.ObjectFile);
                        new HfsCompressor().compress(linesOFile, linesOBytes.toByteArray(), config);
                    }
                } else {
                    // Make sure there's no stale lines.o file lingering
                    File linesOFile = config.getLinesOFile(clazz);
                    if (linesOFile.exists()) {
                        linesOFile.delete();
                    }
                }
            }
        }
    } catch (Throwable t) {
        if (oFile.exists()) {
            oFile.delete();
        }
        if (t instanceof IOException) {
            throw (IOException) t;
        }
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        }
        if (t instanceof Error) {
            throw (Error) t;
        }
        throw new CompilerException(t);
    }
}
Also used : CompilerPlugin(org.robovm.compiler.plugin.CompilerPlugin) Symbol(org.robovm.llvm.Symbol) LineInfo(org.robovm.llvm.LineInfo) PassManager(org.robovm.llvm.PassManager) Global(org.robovm.compiler.llvm.Global) HfsCompressor(org.robovm.compiler.util.io.HfsCompressor) Target(org.robovm.llvm.Target) ObjectFile(org.robovm.llvm.ObjectFile) Context(org.robovm.llvm.Context) ByteArrayOutputStream(java.io.ByteArrayOutputStream) IOException(java.io.IOException) StructureConstantBuilder(org.robovm.compiler.llvm.StructureConstantBuilder) PackedStructureConstantBuilder(org.robovm.compiler.llvm.PackedStructureConstantBuilder) IntegerConstant(org.robovm.compiler.llvm.IntegerConstant) TargetMachine(org.robovm.llvm.TargetMachine) ByteArrayInputStream(java.io.ByteArrayInputStream) Module(org.robovm.llvm.Module) ObjectFile(org.robovm.llvm.ObjectFile) File(java.io.File)

Aggregations

Global (org.robovm.compiler.llvm.Global)3 CompilerPlugin (org.robovm.compiler.plugin.CompilerPlugin)3 File (java.io.File)2 ArrayList (java.util.ArrayList)2 HashSet (java.util.HashSet)2 ClazzInfo (org.robovm.compiler.clazz.ClazzInfo)2 MethodInfo (org.robovm.compiler.clazz.MethodInfo)2 ConstantBitcast (org.robovm.compiler.llvm.ConstantBitcast)2 Function (org.robovm.compiler.llvm.Function)2 FunctionRef (org.robovm.compiler.llvm.FunctionRef)2 IntegerConstant (org.robovm.compiler.llvm.IntegerConstant)2 StructureConstantBuilder (org.robovm.compiler.llvm.StructureConstantBuilder)2 ByteArrayInputStream (java.io.ByteArrayInputStream)1 ByteArrayOutputStream (java.io.ByteArrayOutputStream)1 IOException (java.io.IOException)1 OutputStreamWriter (java.io.OutputStreamWriter)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Random (java.util.Random)1 Set (java.util.Set)1