void GenerateMachineCode()

in libraries/emitters/src/IRAssemblyWriter.cpp [106:238]


    void GenerateMachineCode(llvm::raw_ostream& os, IRModuleEmitter& moduleEmitter, ModuleOutputFormat outputFormat, const MachineCodeOutputOptions& ellOptions)
    {
        llvm::Module& module = *(moduleEmitter.GetLLVMModule());

        llvm::LLVMContext context;
        context.setDiscardValueNames(false); // Don't throw away names of non-global values

        std::stringstream sstr;
        llvm::raw_os_ostream out(sstr);
        auto verifyResult = llvm::verifyModule(module, &out);
        if (verifyResult)
        {
            auto errorString = sstr.str();
            if (ellOptions.verifyModule)
            {
                throw EmitterException(EmitterError::unexpected, "Module verification failed.\n\n" + errorString);
            }
            else
            {
                Log() << "Warning: Module failed verification\n\n"
                      << errorString << EOL;
            }
        }

        // Set the triple for the module, and retrieve it as a Triple object
        auto targetTripleStr = ellOptions.targetDevice.triple.empty() ? llvm::sys::getDefaultTargetTriple() : ellOptions.targetDevice.triple;
        module.setTargetTriple(llvm::Triple::normalize(targetTripleStr));

        // Get the target-specific parser.
        std::string error;
        const llvm::Target* target = llvm::TargetRegistry::lookupTarget(module.getTargetTriple(), error);
        if (!target)
        {
            throw EmitterException(EmitterError::unexpected, std::string("Couldn't create target ") + error);
        }

        llvm::TargetOptions targetOptions = MakeTargetOptions();
        targetOptions.MCOptions.AsmVerbose = ellOptions.verboseOutput;
        targetOptions.FloatABIType = ellOptions.floatABI;
        targetOptions.AllowFPOpFusion = ellOptions.floatFusionMode;
        targetOptions.UnsafeFPMath = ellOptions.unsafeFPMath ? 1 : 0;
        targetOptions.NoInfsFPMath = ellOptions.noInfsFPMath ? 1 : 0;
        targetOptions.NoNaNsFPMath = ellOptions.noNaNsFPMath ? 1 : 0;
        targetOptions.NoSignedZerosFPMath = ellOptions.noSignedZerosFPMath ? 1 : 0;

        OutputRelocationModel relocModel = ellOptions.relocModel;
        llvm::CodeModel::Model codeModel = llvm::CodeModel::Small; // If this code gets run during JIT, we may have to change to medium/large

        std::unique_ptr<llvm::TargetMachine> targetMachine(target->createTargetMachine(module.getTargetTriple(),
                                                                                       ellOptions.targetDevice.cpu,
                                                                                       ellOptions.targetDevice.features,
                                                                                       targetOptions,
                                                                                       relocModel,
                                                                                       codeModel,
                                                                                       ellOptions.optimizationLevel));

        if (!targetMachine)
        {
            throw EmitterException(EmitterError::unexpected, "Unable to allocate target machine");
        }

        // Build up all of the passes that we want to apply to the module
        llvm::legacy::PassManager passManager;

        // Get a targetLibraryInfo describing the library functions available for this triple,
        // and any special processing we might want to do. For instance, if we want to
        // disable all builtin library functions, do this: `targetLibraryInfo.disableAllFunctions();`
        llvm::TargetLibraryInfoImpl targetLibraryInfo(llvm::Triple(module.getTargetTriple()));

        // ...and add it to the pass manager, so various optimizations can be done
        passManager.add(new llvm::TargetLibraryInfoWrapperPass(targetLibraryInfo));

        // Set the data layout of the module to match the target machine
        module.setDataLayout(targetMachine->createDataLayout());

        // Override function attributes based on cpu and features
        if (!ellOptions.targetDevice.cpu.empty() || !ellOptions.targetDevice.features.empty())
        {
            SetFunctionAttributes(ellOptions.targetDevice.cpu, ellOptions.targetDevice.features, module);
        }

        // Set up passes to emit code to a memory stream
        llvm::SmallVector<char, 0> buffer;
        llvm::raw_svector_ostream bufferedStream(buffer);
        if (IsMachineCodeFormat(outputFormat))
        {
            MachineCodeType fileType = MachineCodeType::CGFT_Null;
            switch (outputFormat)
            {
            case ModuleOutputFormat::assembly:
                fileType = MachineCodeType::CGFT_AssemblyFile;
                break;
            case ModuleOutputFormat::objectCode:
                fileType = MachineCodeType::CGFT_ObjectFile;
                break;
            default:
                throw EmitterException(EmitterError::notSupported);
            }

            if (targetMachine->addPassesToEmitFile(passManager, bufferedStream, nullptr, fileType, ellOptions.verifyModule))
            {
                throw EmitterException(EmitterError::unexpected, "target does not support generation of this file type!");
            }
        }

        // Finally, run the passes to emit code to the straem
        passManager.run(module); // run() returns a bool indicating if the module was modified (true if it was)

        if (!IsMachineCodeFormat(outputFormat))
        {
            switch (outputFormat)
            {
            case ModuleOutputFormat::bitcode:
                llvm::WriteBitcodeToFile(module, os);
                break;
            case ModuleOutputFormat::ir:
                module.print(os, nullptr);
                break;
            default:
                throw EmitterException(EmitterError::notSupported);
            }
        }
        else
        {
            // Write memory buffer to our output stream
            os << buffer;
        }

        if (moduleEmitter.GetDiagnosticHandler().HadError())
        {
            throw EmitterException(EmitterError::unexpected, "Error compiling module");
        }
    }