/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/jitlayers.h
1 035 строк
40 KB
Keno Fischer
cancellation: Wire up compute cancellation (#62604)
04 авг 2026, 18:06
Не верифицирован
04 авг 2026, 18:06
5566b23
Код
Авторство
О чём код?
// This file is a part of Julia. License is MIT: https://julialang.org/license #include "llvm-version.h" #include "llvm/ADT/SmallSet.h" #include <llvm/ADT/MapVector.h> #include <llvm/ADT/StringSet.h> #include <llvm/Support/AllocatorBase.h> #include <llvm/IR/LLVMContext.h> #include <llvm/IR/Constants.h> #include <llvm/IR/Module.h> #include <llvm/IR/Value.h> #include <llvm/IR/Attributes.h> #include <llvm/IR/PassManager.h> #include <llvm/IR/LegacyPassManager.h> #include <llvm/IR/PassTimingInfo.h> #include <llvm/ExecutionEngine/Orc/IRCompileLayer.h> #include <llvm/ExecutionEngine/Orc/IRTransformLayer.h> #include <llvm/ExecutionEngine/JITEventListener.h> #include <llvm/Passes/PassBuilder.h> #if JL_LLVM_VERSION >= 220000 # include <llvm/Plugins/PassPlugin.h> #else # include <llvm/Passes/PassPlugin.h> #endif #include <llvm/Passes/StandardInstrumentations.h> #include <llvm/Target/TargetMachine.h> #include "julia_assert.h" #include "julia.h" #include "julia_internal.h" #include "platform.h" #include "llvm-codegen-shared.h" #include "objcache.h" #include <stack> #include <queue> #include <tuple> // 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). // // JITLink is now used on all platforms by default. The support for RuntimeDyld // will be removed when we need the ability to manipulate JITLink LinkGraphs. // // Of the supported profilers, only OProfile has not been ported to JITLink. #if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_) # define HAS_SANITIZER #endif #ifndef JL_USE_OPROFILE_JITEVENTS #define JL_USE_JITLINK #endif # include <llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h> # include <llvm/ExecutionEngine/RTDyldMemoryManager.h> # include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h> using namespace llvm; inline int jl_is_timing_passes = 0; inline int jl_is_timing_trace = 0; inline unsigned jl_timing_trace_granularity = 500; inline std::string jl_timing_trace_file; inline LLVMOrcThreadSafeContextRef wrap(const orc::ThreadSafeContext *P) JL_NOTSAFEPOINT; inline LLVMOrcThreadSafeModuleRef wrap(const orc::ThreadSafeModule *P) JL_NOTSAFEPOINT; DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeContext, LLVMOrcThreadSafeContextRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeModule, LLVMOrcThreadSafeModuleRef) void addTargetPasses(legacy::PassManagerBase *PM, const Triple &triple, TargetIRAnalysis analysis) JL_NOTSAFEPOINT; GlobalVariable *jl_emit_RTLD_DEFAULT_var(Module *M) JL_NOTSAFEPOINT; DataLayout jl_create_datalayout(TargetMachine &TM) JL_NOTSAFEPOINT; // N.B.: kept in sync with Compiler/src/effects.jl (encode_effects); 0 means // "no recorded effects" and is treated conservatively. inline bool effects_ipo_reset_safe(uint32_t effects) JL_NOTSAFEPOINT { return effects != 0 && ((effects >> 3) & 0x03u) == 0u && // is_effect_free ((effects >> 15) & 0x03u) == 0u; // is_reset_safe } // Translate Julia's inferred ipo_purity_bits into LLVM function attributes. // Optimistic attrs (memory(argmem: read), readnone on gcstack) are added for // pre-GC passes; LateLowerGCFrame widens them before safepoint analysis. // Applied to the CallInst, and also to the callee declaration if present. inline void add_fn_attrs_for_effects(CallInst *CI, uint32_t effects) JL_NOTSAFEPOINT { if (effects == 0) return; bool is_consistent = (effects & 0x07u) == 0u; bool is_effect_free = ((effects >> 3) & 0x03u) == 0u; bool is_nothrow = (effects >> 5) & 0x01u; bool is_terminates = (effects >> 6) & 0x01u; bool is_notaskstate = (effects >> 7) & 0x01u; AttrBuilder attrs(CI->getContext()); attrs.addAttribute("julia.safepoint"); if (is_nothrow) attrs.addAttribute(Attribute::NoUnwind); if (is_terminates) attrs.addAttribute(Attribute::MustProgress); if (is_nothrow && is_terminates) attrs.addAttribute(Attribute::WillReturn); // True if the signature contains a pointer that isn't a Julia-internal gcstack slot. // Only when false can we safely emit memory(argmem: read). bool has_user_ptr = true; if (is_consistent && is_effect_free) { FunctionType *ft = CI->getFunctionType(); has_user_ptr = ft->getReturnType()->isPointerTy(); if (!has_user_ptr) { for (unsigned i = 0; i < ft->getNumParams(); i++) { if (ft->getParamType(i)->isPointerTy()) { if (CI->getParamAttr(i, "gcstack").isValid()) { if (is_notaskstate) CI->addParamAttr(i, Attribute::ReadNone); continue; } has_user_ptr = true; break; } } } if (!has_user_ptr) attrs.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Ref)); } CI->setAttributes(CI->getAttributes().addFnAttributes(CI->getContext(), attrs)); // Also apply to the callee declaration. if (auto *F = dyn_cast_or_null<Function>(CI->getCalledFunction())) { if (F->isDeclaration()) { F->addFnAttrs(attrs); // N.B. We do not emit `speculatable` here: it allows SimplifyCFG // to hoist calls past branches unconditionally, which can cause // regressions when pure-but-expensive functions (e.g. exp()) get // speculated onto hot paths. if (is_nothrow && !F->hasUWTable()) F->addFnAttr(Attribute::get(F->getContext(), Attribute::UWTable, uint64_t(llvm::UWTableKind::Async))); if (!has_user_ptr && is_notaskstate) { for (unsigned i = 0; i < F->arg_size(); i++) { if (F->hasParamAttribute(i, "gcstack")) F->addParamAttr(i, Attribute::ReadNone); } } } } } struct OptimizationOptions { bool lower_intrinsics; bool dump_native; bool tls_getters; bool external_use; bool llvm_only; bool always_inline; bool enable_early_simplifications; bool enable_early_optimizations; bool enable_scalar_optimizations; bool enable_loop_optimizations; bool enable_vector_pipeline; bool remove_ni; bool cleanup; bool warn_missed_transformations; bool sanitize_memory; bool sanitize_thread; bool sanitize_address; static constexpr OptimizationOptions defaults( bool lower_intrinsics=true, bool dump_native=false, bool tls_getters=false, bool external_use=false, bool llvm_only=false, bool always_inline=true, bool enable_early_simplifications=true, bool enable_early_optimizations=true, bool enable_scalar_optimizations=true, bool enable_loop_optimizations=true, bool enable_vector_pipeline=true, bool remove_ni=true, bool cleanup=true, bool warn_missed_transformations=false, #ifdef _COMPILER_MSAN_ENABLED_ bool sanitize_memory=true, #else bool sanitize_memory=false, #endif #ifdef _COMPILER_TSAN_ENABLED_ bool sanitize_thread=true, #else bool sanitize_thread=false, #endif #ifdef _COMPILER_ASAN_ENABLED_ bool sanitize_address=true #else bool sanitize_address=false #endif ) JL_NOTSAFEPOINT { return {lower_intrinsics, dump_native, tls_getters, external_use, llvm_only, always_inline, enable_early_simplifications, enable_early_optimizations, enable_scalar_optimizations, enable_loop_optimizations, enable_vector_pipeline, remove_ni, cleanup, warn_missed_transformations, sanitize_memory, sanitize_thread, sanitize_address}; } }; struct PrintOptions { bool print_before_all = false; bool print_after_all = false; bool print_module_scope = false; // TODO: Add print_changed support using LLVM's ChangeReporter (a text diff). // See https://llvm.org/doxygen/classllvm_1_1ChangeReporter.html SmallVector<std::string, 1> print_before; // specific pass names (comma-separated or repeated) SmallVector<std::string, 1> print_after; // specific pass names (comma-separated or repeated) SmallVector<std::string, 1> filter_print_funcs; // filter for function names (comma-separated or repeated) std::string error; // error messages from parsing raw_ostream *out = nullptr; // output stream (default: errs()) PrintOptions() JL_NOTSAFEPOINT = default; ~PrintOptions() JL_NOTSAFEPOINT = default; PrintOptions(const PrintOptions &) JL_NOTSAFEPOINT = default; PrintOptions(PrintOptions &&) JL_NOTSAFEPOINT = default; PrintOptions &operator=(const PrintOptions &) JL_NOTSAFEPOINT = default; PrintOptions &operator=(PrintOptions &&) JL_NOTSAFEPOINT = default; static PrintOptions defaults() JL_NOTSAFEPOINT { return PrintOptions(); } }; // Parse LLVM-style option string into PrintOptions void parseLLVMOptions(const char *options, PrintOptions &out) JL_NOTSAFEPOINT; struct NewPM { std::unique_ptr<TargetMachine> TM; OptimizationLevel O; OptimizationOptions options; PrintOptions print_options; TimePassesHandler TimePasses; NewPM(std::unique_ptr<TargetMachine> TM, OptimizationLevel O, OptimizationOptions options = OptimizationOptions::defaults(), PrintOptions print_options = PrintOptions::defaults()) JL_NOTSAFEPOINT; ~NewPM() JL_NOTSAFEPOINT; void run(Module &M) JL_NOTSAFEPOINT; void printTimers() JL_NOTSAFEPOINT; #ifdef USE_TRACY void registerTracyCallbacks(PassInstrumentationCallbacks &PIC) JL_NOTSAFEPOINT; #endif }; struct AnalysisManagers { LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModuleAnalysisManager MAM; AnalysisManagers(PassBuilder &PB) JL_NOTSAFEPOINT; AnalysisManagers(TargetMachine &TM, PassBuilder &PB, OptimizationLevel O) JL_NOTSAFEPOINT; ~AnalysisManagers() JL_NOTSAFEPOINT; }; OptimizationLevel getOptLevel(int optlevel) JL_NOTSAFEPOINT; struct jl_locked_stream { ios_t *stream = nullptr; std::mutex mutex; struct lock { std::unique_lock<std::mutex> lck; ios_t *&stream; lock(std::mutex &mutex, ios_t *&stream) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER : lck(mutex), stream(stream) {} lock(lock&) = delete; lock(lock&&) JL_NOTSAFEPOINT = default; ~lock() JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT = default; ios_t *&operator*() JL_NOTSAFEPOINT { return stream; } explicit operator bool() JL_NOTSAFEPOINT { return !!stream; } operator ios_t *() JL_NOTSAFEPOINT { return stream; } operator JL_STREAM *() JL_NOTSAFEPOINT { return (JL_STREAM*)stream; } }; jl_locked_stream() JL_NOTSAFEPOINT = default; ~jl_locked_stream() JL_NOTSAFEPOINT = default; lock operator*() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER { return lock(mutex, stream); } }; // jl_codeinst_funcs_t holds the results of compiling a CodeInstance, which can // produce one, two, or zero entrypoints. The `invoke_api` field determines // what the CodeInstance's `invoke` should be set to, and whether `invoke` and // `specptr` are compiled functions. // // JL_INVOKE_ARGS // specptr: jl_fptr_args_t convention // JL_INVOKE_CONST // (no compiled functions) // JL_INVOKE_SPARAM // specptr: jl_fptr_sparam_t convention // JL_INVOKE_INTERPRETED // (not produced by compilation) // JL_INVOKE_SPECSIG // invoke: jfptr_* wrapper around specptr // specptr: specsig function template <typename T> struct jl_codeinst_funcs_t { jl_invoke_api_t invoke_api; T invoke; T specptr; jl_codeinst_funcs_t() JL_NOTSAFEPOINT = default; jl_codeinst_funcs_t &operator=(const jl_codeinst_funcs_t&) JL_NOTSAFEPOINT = default; jl_codeinst_funcs_t(const jl_codeinst_funcs_t &) JL_NOTSAFEPOINT = default; jl_codeinst_funcs_t(jl_codeinst_funcs_t &&) JL_NOTSAFEPOINT = default; ~jl_codeinst_funcs_t() JL_NOTSAFEPOINT = default; }; using jl_llvm_functions_t = jl_codeinst_funcs_t<Function *>; struct jl_returninfo_t { llvm::FunctionCallee decl; llvm::AttributeList attrs; enum CallingConv { Boxed = 0, Register, SRet, Union, Ghosts } cc; size_t union_bytes; size_t union_align; size_t union_minalign; unsigned return_roots; bool all_roots; uint32_t effects = 0; // ipo_purity_bits, applied to CallInst and Function }; struct jl_codegen_call_target_t { llvm::Function *decl; bool external_linkage; // whether codegen would like this edge to be externally-available bool private_linkage; // whether codegen would like this edge to be internally-available // external = ExternalLinkage (similar to "extern") // private = InternalLinkage (similar to "static") // external+private = AvailableExternallyLinkage+ExternalLinkage or ExternalLinkage (similar to "static inline") // neither = unused }; // reification of a call to jl_jit_abi_convert, so that it isn't necessary to parse the Modules to recover this info struct cfunc_decl_t { jl_abi_t abi; llvm::GlobalVariable *cfuncdata; }; std::unique_ptr<Module> jl_create_llvm_module(StringRef name, LLVMContext &ctx, const DataLayout &DL, const Triple &triple, Module *source = nullptr) JL_NOTSAFEPOINT; typedef std::list<std::tuple<std::string, std::string, unsigned int>> CallFrames; class jl_name_counter_t { public: template<class... Ts> std::string operator()(Ts... args) JL_NOTSAFEPOINT { std::string name; raw_string_ostream s{name}; (s << ... << args); unsigned n = counter[name]++; s << n; return name; } jl_name_counter_t() JL_NOTSAFEPOINT = default; jl_name_counter_t(jl_name_counter_t &&) JL_NOTSAFEPOINT = default; ~jl_name_counter_t() JL_NOTSAFEPOINT = default; private: StringMap<unsigned> counter; }; struct jl_linker_info_t { DenseMap<jl_code_instance_t *, jl_codeinst_funcs_t<orc::SymbolStringPtr>> ci_funcs; DenseMap<std::pair<jl_code_instance_t *, jl_invoke_api_t>, orc::SymbolStringPtr> call_targets; DenseMap<void *, orc::SymbolStringPtr> global_targets; }; struct jl_emitted_output_t { std::unique_ptr<LLVMContext> ctx; std::unique_ptr<Module> module; std::unique_ptr<jl_linker_info_t> linker_info; jl_emitted_output_t() JL_NOTSAFEPOINT = default; jl_emitted_output_t(jl_emitted_output_t &&) JL_NOTSAFEPOINT = default; jl_emitted_output_t &operator=(jl_emitted_output_t &&) JL_NOTSAFEPOINT = default; ~jl_emitted_output_t() JL_NOTSAFEPOINT = default; }; // A jl_codegen_output_t is the target for LLVM IR generation, containing a // reference to the destination LLVM module and the metadata for linking it into // the current session or a system image. Many code instances can be emitted to // a single codegen output. class jl_codegen_output_t { private: Module &M; jl_name_counter_t names; public: LLVMContext &get_context() JL_NOTSAFEPOINT { return M.getContext(); } Module &get_module() JL_NOTSAFEPOINT { return M; } StringRef strip_linux(StringRef name) JL_NOTSAFEPOINT; std::string make_name(jl_symbol_prefix_t type, jl_invoke_api_t api, StringRef orig_name) JL_NOTSAFEPOINT; std::string make_name(StringRef prefix, StringRef orig_name) JL_NOTSAFEPOINT; std::string make_name(StringRef orig_name) JL_NOTSAFEPOINT; StringRef get_call_target(jl_code_instance_t *ci, bool specsig, bool always_inline) JL_CANSAFEPOINT; // Discard all the context that will be invalidated when we compile the // module. The context and module will be moved to the jl_emitted_output_t. jl_emitted_output_t finish(std::unique_ptr<LLVMContext> ctx, std::unique_ptr<Module> mod, orc::SymbolStringPool &SSP) JL_NOTSAFEPOINT; public: // outputs DenseMap<std::pair<jl_code_instance_t *, jl_invoke_api_t>, jl_codegen_call_target_t> call_targets; DenseMap<jl_code_instance_t *, jl_llvm_functions_t> ci_funcs; SmallVector<std::pair<jl_code_instance_t *, GlobalVariable *>, 0> external_fns; SmallVector<cfunc_decl_t,0> cfuncs; std::map<void*, GlobalVariable*> global_targets; jl_array_t *temporary_roots = nullptr; SmallSet<jl_value_t *, 8> temporary_roots_set; std::map<jl_datatype_t*, DIType*> ditypes; std::map<jl_datatype_t*, Type*> llvmtypes; DenseMap<Constant*, GlobalVariable*> mergedConstants; // Map from symbol name (in a certain library) to its GV in sysimg and the // DL handle address in the current session. typedef StringMap<GlobalVariable *> SymMapGV; StringMap<std::pair<GlobalVariable*,SymMapGV>> libMapGV; SymMapGV symMapDefault; // These symMaps are Windows-only SymMapGV symMapExe; SymMapGV symMapDll; SymMapGV symMapDlli; // 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<AttributeList, std::map< std::tuple<GlobalVariable*, FunctionType*, CallingConv::ID>, GlobalVariable*>> allPltMap; SmallVector<std::unique_ptr<Module>, 0> llvmcall_modules; // inputs const DataLayout &DL; Triple TargetTriple; const jl_cgparams_t *params = &jl_default_cgparams; bool external_linkage = false; bool imaging_mode = true; bool safepoint_on_entry = true; bool use_swiftcc = true; jl_codegen_output_t(Module &M) JL_NOTSAFEPOINT : M(M), DL(M.getDataLayout()), TargetTriple(M.getTargetTriple()) { if (TargetTriple.isRISCV()) use_swiftcc = false; } jl_codegen_output_t(jl_codegen_output_t &&) JL_NOTSAFEPOINT = default; ~jl_codegen_output_t() JL_NOTSAFEPOINT = default; }; const char *jl_generate_ccallable(jl_codegen_output_t &out, jl_value_t *nameval, jl_value_t *declrt, jl_value_t *sigt) JL_CANSAFEPOINT; std::optional<jl_llvm_functions_t> jl_emit_code( jl_codegen_output_t &out, jl_method_instance_t *mi, jl_code_info_t *src, jl_value_t *abi_at, jl_value_t *abi_rt, jl_code_instance_t *codeinst = nullptr) JL_CANSAFEPOINT; std::optional<jl_llvm_functions_t> jl_emit_codeinst( jl_codegen_output_t &out, jl_code_instance_t *codeinst, jl_code_info_t *src) JL_CANSAFEPOINT; jl_llvm_functions_t jl_emit_codedecls( jl_codegen_output_t &out, jl_code_instance_t *codeinst) JL_CANSAFEPOINT; jl_code_info_t *jl_get_method_ir(jl_code_instance_t *ci) JL_CANSAFEPOINT; void emit_always_inline(jl_codegen_output_t &out, unique_function<jl_code_info_t *(jl_code_instance_t *)> get_src) JL_CANSAFEPOINT; void emit_llvmcall_modules(jl_codegen_output_t &out) JL_NOTSAFEPOINT; enum CompilationPolicy { Default = 0, Extern = 1, }; Function *jl_cfunction_object(jl_value_t *f, jl_value_t *rt, jl_tupletype_t *argt, jl_codegen_output_t &out); extern "C" JL_DLLEXPORT_CODEGEN void *jl_jit_abi_convert(jl_task_t *ct, jl_abi_t from_abi, _Atomic(void*) *fptr, _Atomic(size_t) *last_world, void *data); std::string emit_abi_dispatcher(jl_codegen_output_t &out, jl_abi_t from_abi, jl_code_instance_t *codeinst, Value *invoke) JL_CANSAFEPOINT; std::string emit_abi_converter(jl_codegen_output_t &out, jl_abi_t from_abi, jl_code_instance_t *codeinst, Value *target, bool target_specsig) JL_CANSAFEPOINT; std::string emit_abi_constreturn(jl_codegen_output_t &out, jl_abi_t from_abi, jl_value_t *rettype_const) JL_CANSAFEPOINT; std::string emit_abi_constreturn(jl_codegen_output_t &out, bool specsig, jl_code_instance_t *codeinst) JL_CANSAFEPOINT; Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptrName, jl_codegen_output_t &out) JL_CANSAFEPOINT; void emit_specsig_to_fptr1( Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, jl_codegen_output_t &out, Value *target) JL_CANSAFEPOINT; Function *emit_specsig_to_fptr1(jl_codegen_output_t &out, jl_code_instance_t *ci, Value *func) JL_CANSAFEPOINT; Function *get_or_emit_fptr1(StringRef Name, Module *M) JL_NOTSAFEPOINT; void jl_init_function(Function *F, const jl_codegen_output_t ¶ms) JL_NOTSAFEPOINT; jl_returninfo_t get_specsig_function(jl_codegen_output_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, ArrayRef<const char *> ArgNames = {}, unsigned nreq = 0) JL_CANSAFEPOINT; void add_named_global(StringRef name, void *addr) JL_NOTSAFEPOINT; Constant *literal_pointer_val_slot(jl_codegen_output_t &out, jl_value_t *p) JL_CANSAFEPOINT; static inline Constant *literal_static_pointer_val(const void *p, Type *T) JL_NOTSAFEPOINT { // 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) JL_NOTSAFEPOINT { return jl_is_method(li->def.method) ? jl_symbol_name(li->def.method->name) : "top-level scope"; } static inline jl_value_t *get_ci_abi(jl_code_instance_t *ci JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { if (jl_typeof(ci->def) == (jl_value_t*)jl_abioverride_type) return ((jl_abi_override_t*)ci->def)->abi; return jl_get_ci_mi(ci)->specTypes; } template <size_t offset = 0> class MaxAlignedAllocImpl : public AllocatorBase<MaxAlignedAllocImpl<offset>> { public: MaxAlignedAllocImpl() JL_NOTSAFEPOINT = default; static Align alignment(size_t Size) JL_NOTSAFEPOINT { // Define the maximum alignment we expect to require, from offset bytes off // the returned pointer, this is >= alignof(std::max_align_t), which is too // small often to actually use. const size_t MaxAlignment = JL_CACHE_BYTE_ALIGNMENT; if (Size <= offset) return Align(1); return Align(std::min((size_t)llvm::PowerOf2Ceil(Size - offset), MaxAlignment)); } LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size, Align Alignment) { Align MaxAlign = alignment(Size); assert(Alignment < MaxAlign); (void)Alignment; return jl_gc_perm_alloc(Size, 0, MaxAlign.value(), offset); } inline LLVM_ATTRIBUTE_RETURNS_NONNULL void * Allocate(size_t Size, size_t Alignment) { return Allocate(Size, Align(Alignment)); } // Pull in base class overloads. using AllocatorBase<MaxAlignedAllocImpl>::Allocate; void Deallocate(const void *Ptr, size_t Size, size_t /*Alignment*/) { abort(); } // Pull in base class overloads. using AllocatorBase<MaxAlignedAllocImpl>::Deallocate; private: }; using MaxAlignedAlloc = MaxAlignedAllocImpl<>; using CompilerResultT = Expected<std::unique_ptr<llvm::MemoryBuffer>>; using OptimizerResultT = Expected<orc::ThreadSafeModule>; using SharedBytesT = StringSet<MaxAlignedAllocImpl<sizeof(StringSet<>::MapEntryTy)>>; using CISymbolPtr = jl_codeinst_funcs_t<orc::SymbolStringPtr>; using CISymbolMap = DenseMap<jl_code_instance_t *, CISymbolPtr>; class JLMaterializationUnit; class JLTrampolineMaterializationUnit; struct JITObjectInfo { std::unique_ptr<MemoryBuffer> BackingBuffer; std::unique_ptr<object::ObjectFile> Object; StringMap<uint64_t> SectionLoadAddresses; std::unique_ptr<jl_linker_info_t> LinkerInfo; }; class JLDebuginfoPlugin : public orc::ObjectLinkingLayer::Plugin { std::mutex PluginMutex; std::map<orc::MaterializationResponsibility *, std::unique_ptr<JITObjectInfo>> PendingObjs; public: void notifyMaterializingWithInfo(orc::MaterializationResponsibility &MR, jitlink::LinkGraph &G, MemoryBufferRef InputObject, std::unique_ptr<jl_linker_info_t> LinkerInfo) JL_NOTSAFEPOINT; Error notifyEmitted(orc::MaterializationResponsibility &MR) override JL_CANSAFEPOINT_ENTER_LEAVE; // NOLINT[julia-first-decl-annotations] Error notifyFailed(orc::MaterializationResponsibility &MR) override; Error notifyRemovingResources(orc::JITDylib &JD, orc::ResourceKey K) override; void notifyTransferringResources(orc::JITDylib &JD, orc::ResourceKey DstKey, orc::ResourceKey SrcKey) override; void modifyPassConfig(orc::MaterializationResponsibility &MR, jitlink::LinkGraph &, jitlink::PassConfiguration &PassConfig) override; }; class JuliaOJIT { friend JLMaterializationUnit; friend JLTrampolineMaterializationUnit; private: // any verification the user wants to do when adding an OwningResource to the pool template <typename AnyT> static void verifyResource(AnyT &resource) JL_NOTSAFEPOINT { } static void verifyResource(orc::ThreadSafeContext &context) JL_NOTSAFEPOINT { #if JL_LLVM_VERSION < 210000 assert(context.getContext()); #else context.withContextDo([](LLVMContext *ctx) { assert(ctx); }); #endif } public: typedef orc::ObjectLinkingLayer ObjLayerT; typedef orc::IRCompileLayer CompileLayerT; typedef orc::IRTransformLayer JITPointersLayerT; typedef orc::IRTransformLayer OptimizeLayerT; typedef object::OwningBinary<object::ObjectFile> OwningObj; template <typename ResourceT, size_t max = 0, typename BackingT = std::stack<ResourceT, std::conditional_t<max == 0, SmallVector<ResourceT, 0>, SmallVector<ResourceT, max> > > > struct ResourcePool { public: ResourcePool(std::function<ResourceT()> creator) JL_NOTSAFEPOINT : creator(std::move(creator)), mutex(std::make_unique<WNMutex>()) {} ResourcePool(ResourcePool&) = delete; ResourcePool(ResourcePool&&) JL_NOTSAFEPOINT = default; ~ResourcePool() JL_NOTSAFEPOINT = default; class OwningResource { public: OwningResource(ResourcePool &pool, ResourceT resource) JL_NOTSAFEPOINT // _ENTER : pool(pool), resource(std::move(resource)) {} OwningResource(const OwningResource &) = delete; OwningResource &operator=(const OwningResource &) = delete; OwningResource(OwningResource &&other) JL_NOTSAFEPOINT : pool(other.pool), resource(std::move(other.resource)) { other.resource.reset(); } OwningResource &operator=(OwningResource &&) JL_NOTSAFEPOINT = default; ~OwningResource() JL_NOTSAFEPOINT { // _LEAVE if (resource) { verifyResource(*resource); pool.release(std::move(*resource)); } } ResourceT release() JL_NOTSAFEPOINT { ResourceT res(std::move(*resource)); resource.reset(); return res; } void reset(ResourceT res) JL_NOTSAFEPOINT { *resource = std::move(res); } ResourceT &operator*() JL_NOTSAFEPOINT { return *resource; } ResourceT *operator->() JL_NOTSAFEPOINT { return get(); } ResourceT *get() JL_NOTSAFEPOINT { return resource.getPointer(); } const ResourceT &operator*() const JL_NOTSAFEPOINT { return *resource; } const ResourceT *operator->() const JL_NOTSAFEPOINT { return get(); } const ResourceT *get() const JL_NOTSAFEPOINT { return resource.getPointer(); } explicit operator bool() const JL_NOTSAFEPOINT { return resource; } private: ResourcePool &pool; std::optional<ResourceT> resource; }; OwningResource operator*() JL_NOTSAFEPOINT { return OwningResource(*this, acquire()); } OwningResource get() { return **this; } ResourceT acquire() JL_NOTSAFEPOINT { // _ENTER std::unique_lock<std::mutex> lock(mutex->mutex); if (!pool.empty()) { return pop(pool); } if (!max || created < max) { created++; return creator(); } mutex->empty.wait(lock, [&](){ return !pool.empty(); }); assert(!pool.empty() && "Expected resource pool to have a value!"); return pop(pool); } void release(ResourceT &&resource) JL_NOTSAFEPOINT { // _LEAVE std::lock_guard<std::mutex> lock(mutex->mutex); pool.push(std::move(resource)); mutex->empty.notify_one(); } private: template<typename T, typename Container> static ResourceT pop(std::queue<T, Container> &pool) JL_NOTSAFEPOINT { ResourceT top = std::move(pool.front()); pool.pop(); return top; } template<typename PoolT> static ResourceT pop(PoolT &pool) JL_NOTSAFEPOINT { ResourceT top = std::move(pool.top()); pool.pop(); return top; } std::function<ResourceT()> creator; size_t created = 0; BackingT pool; struct WNMutex { std::mutex mutex; std::condition_variable empty; }; std::unique_ptr<WNMutex> mutex; }; typedef ResourcePool<orc::ThreadSafeContext, 0, std::queue<orc::ThreadSafeContext>> ContextPoolT; struct DLSymOptimizer; struct OptimizerT; struct JITPointersT; public: JuliaOJIT() JL_NOTSAFEPOINT; ~JuliaOJIT() JL_NOTSAFEPOINT; void enableJITDebuggingSupport() JL_CANSAFEPOINT; void enableIntelJITEventListener() JL_NOTSAFEPOINT; void enableOProfileJITEventListener() JL_NOTSAFEPOINT; void enablePerfJITEventListener() JL_NOTSAFEPOINT; orc::SymbolStringPtr mangle(StringRef Name) JL_NOTSAFEPOINT; void addGlobalMapping(StringRef Name, uint64_t Addr) JL_NOTSAFEPOINT; void addOutput(jl_emitted_output_t O) JL_NOTSAFEPOINT; // Methods mainly for the C API Error addExternalModule(orc::JITDylib &JD, orc::ThreadSafeModule TSM, bool ShouldOptimize = false) JL_NOTSAFEPOINT; Error addObjectFile(orc::JITDylib &JD, std::unique_ptr<MemoryBuffer> Obj) JL_NOTSAFEPOINT; orc::IRCompileLayer &getIRCompileLayer() JL_NOTSAFEPOINT { return CompileLayer; }; orc::ExecutionSession &getExecutionSession() JL_NOTSAFEPOINT { return ES; } orc::JITDylib &createJITDylib(StringRef NamePrefix) JL_NOTSAFEPOINT; Expected<llvm::orc::ExecutorSymbolDef> findJDSymbol(orc::JITDylib &JD, StringRef Name, bool ExportedSymbolsOnly) JL_CANSAFEPOINT; SmallVector<uint64_t> findSymbols(ArrayRef<StringRef> Names) JL_CANSAFEPOINT; uint64_t getGlobalValueAddress(StringRef Name) JL_CANSAFEPOINT; uint64_t getFunctionAddress(StringRef Name) JL_CANSAFEPOINT; void publishCIs(ArrayRef<jl_code_instance_t *> CIs, bool Wait=false) JL_CANSAFEPOINT; void registerCI(jl_code_instance_t *CI) JL_NOTSAFEPOINT; // When a CodeInstance is garbage collected, we must remove any existing // entries in CISymbols, to prevent invokes to a new CodeInstance with the // same address from being linked to old symbol. void unregisterCI(jl_code_instance_t *CI) JL_NOTSAFEPOINT; orc::ThreadSafeContext makeContext() JL_NOTSAFEPOINT; const DataLayout& getDataLayout() const JL_NOTSAFEPOINT; // TargetMachine pass-through methods std::unique_ptr<TargetMachine> cloneTargetMachine() const JL_NOTSAFEPOINT; const Triple& getTargetTriple() const JL_NOTSAFEPOINT; StringRef getTargetFeatureString() const JL_NOTSAFEPOINT; StringRef getTargetCPU() const JL_NOTSAFEPOINT; const TargetOptions &getTargetOptions() const JL_NOTSAFEPOINT; const Target &getTarget() const JL_NOTSAFEPOINT; TargetIRAnalysis getTargetIRAnalysis() const JL_NOTSAFEPOINT; size_t getTotalBytes() const JL_NOTSAFEPOINT; void addBytes(size_t bytes) JL_NOTSAFEPOINT; void printTimers() JL_NOTSAFEPOINT; const char *objCacheDisabledNotice() JL_CANSAFEPOINT_ENTER_LEAVE { return OCache.disabledNotice(); } jl_locked_stream &get_dump_emitted_mi_name_stream() JL_NOTSAFEPOINT { return dump_emitted_mi_name_stream; } jl_locked_stream &get_dump_compiles_stream() JL_NOTSAFEPOINT { return dump_compiles_stream; } jl_locked_stream &get_dump_llvm_opt_stream() JL_NOTSAFEPOINT { return dump_llvm_opt_stream; } std::string getMangledName(StringRef Name) JL_NOTSAFEPOINT; std::string getMangledName(const GlobalValue *GV) JL_NOTSAFEPOINT; // Note that this is a potential safepoint due to jl_get_library_ and jl_dlsym calls // and must be called from inside safe-regions due to internal use of locks void optimizeDLSyms(Module &M) JL_CANSAFEPOINT_ENTER_LEAVE; void shutdown() JL_NOTSAFEPOINT; protected: // Choose globally unique names for the functions defined by the given CI // and register the mapping in CISymbols. CISymbolPtr makeUniqueCIName(jl_code_instance_t *CI, const CISymbolPtr &Funcs) JL_NOTSAFEPOINT; // void registerJITOutput(MemoryBufferRef Obj, const jl_linker_info_t &Info); // Rename LinkGraph symbols to match the previously chosen names and // register debug info for defined symbols. Returns true on success, and // false after calling MR.failMaterialization(). bool linkOutput(orc::MaterializationResponsibility &MR, MemoryBufferRef ObjBuf, jitlink::LinkGraph &G, std::unique_ptr<jl_linker_info_t> Info) JL_CANSAFEPOINT_ENTER_LEAVE; // Return a symbol that should be linked to the call target. The origin of // this symbol depends on the code instance: // - If the call target is for a specialized function defined by a CI added // to the JIT, return the symbol that was registered by makeUniqueCIName. // - If the CI already exists and has code that matches the expected calling // convention, generate a symbol for it and cache it in CISymbols. // - If the CI exists but the code has the wrong calling convention (a // specialized function is expected but only a jlcall exists, or neither // exists and we should go through jl_invoke), emit the trampoline into a // new module and return a symbol for it. orc::SymbolStringPtr linkCallTarget(orc::MaterializationResponsibility &MR, jl_code_instance_t *CI, jl_invoke_api_t API, const DenseMap<jl_code_instance_t *, jl_code_instance_t *> &EquivMap) JL_NOTSAFEPOINT; // If the provided CodeInstance is neither compiled nor has an ORC symbol in // CISymbols, look for a compatible CodeInstance in the MethodInstance's // cache that does. Returns the original CodeInstance if none exists. jl_code_instance_t *findCompatibleCI(jl_code_instance_t *CI) JL_NOTSAFEPOINT; // Create an ORC symbol and entry in CISymbols for the CI's specptr, // returning a pointer into CISymbols or NULL if the CI is not compiled. CISymbolPtr *linkCISymbol(jl_code_instance_t *CI) JL_NOTSAFEPOINT; void optimizeModule(Module &M) JL_CANSAFEPOINT_ENTER_LEAVE; std::unique_ptr<MemoryBuffer> compileModule(Module &M) JL_NOTSAFEPOINT; private: const std::unique_ptr<TargetMachine> TM; const DataLayout DL; orc::ExecutionSession ES; orc::JITDylib &SessionJD; orc::JITDylib &GlobalJD; orc::JITDylib &JD; std::mutex SharedBytesMutex{}; SharedBytesT SharedBytes; // LinkerMutex protects CISymbols, Names std::mutex LinkerMutex; // CISymbols maps CodeInstance pointers to their ORC symbols. If a // CodeInstance is eligible for garbage collection, it must be removed from // this map first, with unregisterCI. CISymbolMap CISymbols; jl_name_counter_t Names; std::unique_ptr<DLSymOptimizer> DLSymOpt; //Compilation streams jl_locked_stream dump_emitted_mi_name_stream; jl_locked_stream dump_compiles_stream; jl_locked_stream dump_llvm_opt_stream; std::mutex llvm_printing_mutex{}; SmallVector<std::function<void()>, 0> PrintLLVMTimers; ObjCache OCache; _Atomic(size_t) jit_bytes_size{0}; _Atomic(size_t) jitcounter{0}; const std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr; ObjLayerT ObjectLayer; CompileLayerT CompileLayer; std::unique_ptr<JITPointersT> JITPointers; JITPointersLayerT JITPointersLayer; std::unique_ptr<OptimizerT> Optimizers; OptimizeLayerT OptimizeLayer; std::shared_ptr<JLDebuginfoPlugin> DebuginfoPlugin; }; extern JuliaOJIT *jl_ExecutionEngine; void fixupTM(TargetMachine &TM) JL_NOTSAFEPOINT; void optimizeDLSyms(Module &M) JL_CANSAFEPOINT_ENTER_LEAVE; static inline const char *jl_symbol_prefix(jl_symbol_prefix_t type, jl_invoke_api_t api) JL_NOTSAFEPOINT { switch (type) { case JL_SYMBOL_INVOKE_DEF: switch (api) { case JL_INVOKE_SPECSIG: return JL_SYM_INVOKE_SPECSIG; default: jl_unreachable(); }; case JL_SYMBOL_INVOKE_IMG: switch (api) { case JL_INVOKE_SPECSIG: return JL_SYM_INVOKE_IMG_SPECSIG; default: jl_unreachable(); } case JL_SYMBOL_SPECPTR_DEF: switch (api) { case JL_INVOKE_ARGS: return JL_SYM_SPECPTR_ARGS; case JL_INVOKE_CONST: return JL_SYM_SPECPTR_CONST; case JL_INVOKE_SPARAM: return JL_SYM_SPECPTR_SPARAM; case JL_INVOKE_SPECSIG: return JL_SYM_SPECPTR_SPECSIG; default: jl_unreachable(); }; case JL_SYMBOL_SPECPTR_PROTO: switch (api) { case JL_INVOKE_ARGS: return JL_SYM_PROTO_ARGS; case JL_INVOKE_SPECSIG: return JL_SYM_PROTO_SPECSIG; default: jl_unreachable(); } case JL_SYMBOL_SPECPTR_IMG: switch (api) { case JL_INVOKE_ARGS: return JL_SYM_SPECPTR_IMG_ARGS; case JL_INVOKE_SPARAM: return JL_SYM_SPECPTR_IMG_SPARAM; case JL_INVOKE_SPECSIG: return JL_SYM_SPECPTR_IMG_SPECSIG; default: jl_unreachable(); } default: jl_unreachable(); } } // NewPM #include "passes.h" #if JL_LLVM_VERSION >= 180000 CodeGenOptLevel CodeGenOptLevelFor(int optlevel) JL_NOTSAFEPOINT; #else CodeGenOpt::Level CodeGenOptLevelFor(int optlevel) JL_NOTSAFEPOINT; #endif void jl_jit_add_bytes(size_t bytes) JL_NOTSAFEPOINT; void jl_register_jit_object(const object::ObjectFile &Object, std::function<uint64_t(const StringRef &)> getLoadAddress, const jl_linker_info_t &Info) JL_CANSAFEPOINT_ENTER_LEAVE;