Search in sources :

Example 1 with ObjectFile

use of org.robovm.llvm.ObjectFile 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

ByteArrayInputStream (java.io.ByteArrayInputStream)1 ByteArrayOutputStream (java.io.ByteArrayOutputStream)1 File (java.io.File)1 IOException (java.io.IOException)1 Global (org.robovm.compiler.llvm.Global)1 IntegerConstant (org.robovm.compiler.llvm.IntegerConstant)1 PackedStructureConstantBuilder (org.robovm.compiler.llvm.PackedStructureConstantBuilder)1 StructureConstantBuilder (org.robovm.compiler.llvm.StructureConstantBuilder)1 CompilerPlugin (org.robovm.compiler.plugin.CompilerPlugin)1 HfsCompressor (org.robovm.compiler.util.io.HfsCompressor)1 Context (org.robovm.llvm.Context)1 LineInfo (org.robovm.llvm.LineInfo)1 Module (org.robovm.llvm.Module)1 ObjectFile (org.robovm.llvm.ObjectFile)1 PassManager (org.robovm.llvm.PassManager)1 Symbol (org.robovm.llvm.Symbol)1 Target (org.robovm.llvm.Target)1 TargetMachine (org.robovm.llvm.TargetMachine)1