mirror of
https://github.com/JuliaLang/julia.git
synced 2026-05-28 03:10:33 +08:00
988 lines
38 KiB
C++
988 lines
38 KiB
C++
// This file is a part of Julia. License is MIT: https://julialang.org/license
|
|
|
|
#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>
|
|
#include <llvm/Passes/PassPlugin.h>
|
|
#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 "llvm-version.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;
|
|
|
|
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;
|
|
|
|
// 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 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 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, 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;
|
|
};
|
|
|
|
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 JL_NOTSAFEPOINT_ENTER = default;
|
|
~jl_locked_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE = default;
|
|
|
|
lock operator*() JL_NOTSAFEPOINT {
|
|
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() { return M.getContext(); }
|
|
Module &get_module() { return M; }
|
|
|
|
StringRef strip_linux(StringRef name);
|
|
std::string make_name(jl_symbol_prefix_t type, jl_invoke_api_t api,
|
|
StringRef orig_name);
|
|
std::string make_name(StringRef prefix, StringRef orig_name);
|
|
std::string make_name(StringRef orig_name);
|
|
|
|
StringRef get_call_target(jl_code_instance_t *ci, bool specsig, bool always_inline);
|
|
|
|
// 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)
|
|
: 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);
|
|
|
|
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);
|
|
|
|
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_llvm_functions_t jl_emit_codedecls(
|
|
jl_codegen_output_t &out,
|
|
jl_code_instance_t *codeinst);
|
|
|
|
jl_code_info_t *jl_get_method_ir(jl_code_instance_t *ci);
|
|
void emit_always_inline(jl_codegen_output_t &out,
|
|
unique_function<jl_code_info_t *(jl_code_instance_t *)> get_src);
|
|
void emit_llvmcall_modules(jl_codegen_output_t &out);
|
|
|
|
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);
|
|
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);
|
|
std::string emit_abi_constreturn(jl_codegen_output_t &out, jl_abi_t from_abi, jl_value_t *rettype_const);
|
|
std::string emit_abi_constreturn(jl_codegen_output_t &out, bool specsig, jl_code_instance_t *codeinst);
|
|
|
|
Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptrName, jl_codegen_output_t &out) JL_NOTSAFEPOINT;
|
|
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,
|
|
size_t nargs,
|
|
jl_codegen_output_t &out,
|
|
Value *target) JL_NOTSAFEPOINT;
|
|
Function *emit_specsig_to_fptr1(jl_codegen_output_t &out, jl_code_instance_t *ci,
|
|
Value *func) JL_NOTSAFEPOINT;
|
|
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);
|
|
|
|
void add_named_global(StringRef name, void *addr) JL_NOTSAFEPOINT;
|
|
|
|
Constant *literal_pointer_val_slot(jl_codegen_output_t &out, jl_value_t *p);
|
|
|
|
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)
|
|
{
|
|
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;
|
|
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();
|
|
void enableIntelJITEventListener() JL_NOTSAFEPOINT;
|
|
void enableOProfileJITEventListener() JL_NOTSAFEPOINT;
|
|
void enablePerfJITEventListener() JL_NOTSAFEPOINT;
|
|
|
|
orc::SymbolStringPtr mangle(StringRef Name) JL_NOTSAFEPOINT;
|
|
StringRef demangle(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);
|
|
SmallVector<uint64_t> findSymbols(ArrayRef<StringRef> Names);
|
|
uint64_t getGlobalValueAddress(StringRef Name);
|
|
uint64_t getFunctionAddress(StringRef Name);
|
|
|
|
void publishCIs(ArrayRef<jl_code_instance_t *> CIs, bool Wait=false);
|
|
|
|
void registerCI(jl_code_instance_t *CI);
|
|
// 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);
|
|
|
|
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;
|
|
|
|
jl_locked_stream &get_dump_emitted_mi_name_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER {
|
|
return dump_emitted_mi_name_stream;
|
|
}
|
|
jl_locked_stream &get_dump_compiles_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER {
|
|
return dump_compiles_stream;
|
|
}
|
|
jl_locked_stream &get_dump_llvm_opt_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER {
|
|
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
|
|
// but may be called from inside safe-regions due to jit compilation locks
|
|
void optimizeDLSyms(Module &M) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER;
|
|
|
|
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_NOTSAFEPOINT;
|
|
|
|
// 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) 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_NOTSAFEPOINT;
|
|
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;
|
|
|
|
_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);
|
|
|
|
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
|