// This file is a part of Julia. License is MIT: https://julialang.org/license #include #include #include #include #include "llvm/IR/LegacyPassManager.h" #include #include #include #include "julia_assert.h" #include "debug-registry.h" // As of LLVM 13, there are two runtime JIT linker implementations, the older // RuntimeDyld (used via orc::RTDyldObjectLinkingLayer) and the newer JITLink // (used via orc::ObjectLinkingLayer). // // JITLink is not only more flexible (which isn't of great importance for us, as // we do only single-threaded in-process codegen), but crucially supports using // the Small code model, where the linker needs to fix up relocations between // object files that end up far apart in address space. RuntimeDyld can't do // that and relies on the Large code model instead, which is broken on // aarch64-darwin (macOS on ARM64), and not likely to ever be supported there // (see https://bugs.llvm.org/show_bug.cgi?id=52029). // // However, JITLink is a relatively young library and lags behind in platform // and feature support (e.g. Windows, JITEventListeners for various profilers, // etc.). Thus, we currently only use JITLink where absolutely required, that is, // for Mac/aarch64. #if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) # if JL_LLVM_VERSION < 130000 # pragma message("On aarch64-darwin, LLVM version >= 13 is required for JITLink; fallback suffers from occasional segfaults") # endif # define JL_USE_JITLINK #endif #ifdef JL_USE_JITLINK # include #else # include # include #endif using namespace llvm; extern "C" jl_cgparams_t jl_default_cgparams; extern TargetMachine *jl_TargetMachine; extern bool imaging_mode; void addTargetPasses(legacy::PassManagerBase *PM, TargetMachine *TM); void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, bool lower_intrinsics=true, bool dump_native=false); void addMachinePasses(legacy::PassManagerBase *PM, TargetMachine *TM, int optlevel); void jl_finalize_module(std::unique_ptr m); void jl_merge_module(Module *dest, std::unique_ptr src); Module *jl_create_llvm_module(StringRef name); GlobalVariable *jl_emit_RTLD_DEFAULT_var(Module *M); typedef struct _jl_llvm_functions_t { std::string functionObject; // jlcall llvm Function name std::string specFunctionObject; // specialized llvm Function name } jl_llvm_functions_t; struct jl_returninfo_t { llvm::Function *decl; enum CallingConv { Boxed = 0, Register, SRet, Union, Ghosts } cc; size_t union_bytes; size_t union_align; size_t union_minalign; unsigned return_roots; }; typedef std::vector> jl_codegen_call_targets_t; typedef std::tuple, jl_llvm_functions_t> jl_compile_result_t; typedef struct _jl_codegen_params_t { typedef StringMap SymMapGV; // outputs jl_codegen_call_targets_t workqueue; std::map globals; std::map ditypes; std::map llvmtypes; DenseMap mergedConstants; // Map from symbol name (in a certain library) to its GV in sysimg and the // DL handle address in the current session. StringMap> libMapGV; #ifdef _OS_WINDOWS_ SymMapGV symMapExe; SymMapGV symMapDll; SymMapGV symMapDlli; #endif SymMapGV symMapDefault; // Map from distinct callee's to its GOT entry. // In principle the attribute, function type and calling convention // don't need to be part of the key but it seems impossible to forward // all the arguments without writing assembly directly. // This doesn't matter too much in reality since a single function is usually // not called with multiple signatures. DenseMap, GlobalVariable*>> allPltMap; Module *_shared_module = NULL; Module *shared_module(LLVMContext &context) { if (!_shared_module) _shared_module = jl_create_llvm_module("globals"); return _shared_module; } // inputs size_t world = 0; const jl_cgparams_t *params = &jl_default_cgparams; bool cache = false; } jl_codegen_params_t; jl_compile_result_t jl_emit_code( jl_method_instance_t *mi, jl_code_info_t *src, jl_value_t *jlrettype, jl_codegen_params_t ¶ms); jl_compile_result_t jl_emit_codeinst( jl_code_instance_t *codeinst, jl_code_info_t *src, jl_codegen_params_t ¶ms); enum CompilationPolicy { Default = 0, Extern = 1, ImagingMode = 2 }; void jl_compile_workqueue( std::map &emitted, jl_codegen_params_t ¶ms, CompilationPolicy policy); Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_tupletype_t *argt, jl_codegen_params_t ¶ms); void add_named_global(StringRef name, void *addr); static inline Constant *literal_static_pointer_val(const void *p, Type *T) { // this function will emit a static pointer into the generated code // the generated code will only be valid during the current session, // and thus, this should typically be avoided in new API's #if defined(_P64) return ConstantExpr::getIntToPtr(ConstantInt::get(Type::getInt64Ty(T->getContext()), (uint64_t)p), T); #else return ConstantExpr::getIntToPtr(ConstantInt::get(Type::getInt32Ty(T->getContext()), (uint32_t)p), T); #endif } static const inline char *name_from_method_instance(jl_method_instance_t *li) { return jl_is_method(li->def.method) ? jl_symbol_name(li->def.method->name) : "top-level scope"; } void jl_init_jit(void); typedef JITSymbol JL_JITSymbol; // The type that is similar to SymbolInfo on LLVM 4.0 is actually // `JITEvaluatedSymbol`. However, we only use this type when a JITSymbol // is expected. typedef JITSymbol JL_SymbolInfo; using CompilerResultT = Expected>; class JuliaOJIT { struct CompilerT : public orc::IRCompileLayer::IRCompiler { CompilerT(JuliaOJIT *pjit) : IRCompiler(orc::IRSymbolMapper::ManglingOptions{}), jit(*pjit) {} virtual CompilerResultT operator()(Module &M) override; private: JuliaOJIT &jit; }; // Custom object emission notification handler for the JuliaOJIT template void registerObject(const ObjT &Obj, const LoadResult &LO); public: #ifdef JL_USE_JITLINK typedef orc::ObjectLinkingLayer ObjLayerT; #else typedef orc::RTDyldObjectLinkingLayer ObjLayerT; #endif typedef orc::IRCompileLayer CompileLayerT; typedef object::OwningBinary OwningObj; JuliaOJIT(TargetMachine &TM, LLVMContext *Ctx); void enableJITDebuggingSupport(); #ifndef JL_USE_JITLINK // JITLink doesn't support old JITEventListeners (yet). void RegisterJITEventListener(JITEventListener *L); #endif orc::SymbolStringPtr mangle(StringRef Name); void addGlobalMapping(StringRef Name, uint64_t Addr); void addModule(std::unique_ptr M); JL_JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly); JL_JITSymbol findUnmangledSymbol(StringRef Name); uint64_t getGlobalValueAddress(StringRef Name); uint64_t getFunctionAddress(StringRef Name); StringRef getFunctionAtAddress(uint64_t Addr, jl_code_instance_t *codeinst); const DataLayout& getDataLayout() const; const Triple& getTargetTriple() const; size_t getTotalBytes() const; JITDebugInfoRegistry &getDebugInfoRegistry() JL_NOTSAFEPOINT { return DebugRegistry; } private: std::string getMangledName(StringRef Name); std::string getMangledName(const GlobalValue *GV); TargetMachine &TM; const DataLayout DL; // Should be big enough that in the common case, The // object fits in its entirety SmallVector ObjBufferSV; raw_svector_ostream ObjStream; legacy::PassManager PM0; // per-optlevel pass managers legacy::PassManager PM1; legacy::PassManager PM2; legacy::PassManager PM3; TargetMachine *TMs[4]; MCContext *Ctx; orc::ThreadSafeContext TSCtx; orc::ExecutionSession ES; orc::JITDylib &GlobalJD; orc::JITDylib &JD; JITDebugInfoRegistry DebugRegistry; #ifndef JL_USE_JITLINK std::shared_ptr MemMgr; #endif ObjLayerT ObjectLayer; CompileLayerT CompileLayer; DenseMap ReverseLocalSymbolTable; }; extern JuliaOJIT *jl_ExecutionEngine; Pass *createLowerPTLSPass(bool imaging_mode); Pass *createCombineMulAddPass(); Pass *createFinalLowerGCPass(); Pass *createLateLowerGCFramePass(); Pass *createLowerExcHandlersPass(); Pass *createGCInvariantVerifierPass(bool Strong); Pass *createPropagateJuliaAddrspaces(); Pass *createRemoveJuliaAddrspacesPass(); Pass *createRemoveNIPass(); Pass *createJuliaLICMPass(); Pass *createMultiVersioningPass(); Pass *createAllocOptPass(); Pass *createDemoteFloat16Pass(); Pass *createCPUFeaturesPass(); // Whether the Function is an llvm or julia intrinsic. static inline bool isIntrinsicFunction(Function *F) { return F->isIntrinsic() || F->getName().startswith("julia."); } CodeGenOpt::Level CodeGenOptLevelFor(int optlevel);