From b0435958a0cf91f6d72aae299ec18baddeab026b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonne=20Ha=C3=9F?= Date: Sun, 31 May 2020 23:01:18 +0200 Subject: [PATCH] Disable LLVM Global Isel It's not enabled by default on x86, but on other targets it is. At least until https://reviews.llvm.org/D80898 is released, it doesn't like us generating a value of a zero sized type We may need to fix doing that, but until then this workarounds the issue. See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270 for more background. --- spec/std/llvm/aarch64_spec.cr | 1 + spec/std/llvm/arm_abi_spec.cr | 1 + spec/std/llvm/x86_64_abi_spec.cr | 1 + spec/std/llvm/x86_abi_spec.cr | 1 + src/compiler/crystal/codegen/target.cr | 9 ++- src/llvm/ext/llvm_ext.cc | 84 +++++++++++++++++++++++++- src/llvm/jit_compiler.cr | 2 +- src/llvm/lib_llvm_ext.cr | 3 + src/llvm/target_machine.cr | 4 ++ 9 files changed, 102 insertions(+), 4 deletions(-) diff --git a/spec/std/llvm/aarch64_spec.cr b/spec/std/llvm/aarch64_spec.cr index ee30468f0d2..b6ce65ea278 100644 --- a/spec/std/llvm/aarch64_spec.cr +++ b/spec/std/llvm/aarch64_spec.cr @@ -9,6 +9,7 @@ private def abi triple = "aarch64-unknown-linux-gnu" target = LLVM::Target.from_triple(triple) machine = target.create_target_machine(triple) + machine.enable_global_isel = false LLVM::ABI::AArch64.new(machine) end diff --git a/spec/std/llvm/arm_abi_spec.cr b/spec/std/llvm/arm_abi_spec.cr index 1bc7d32d559..98ae9b588a4 100644 --- a/spec/std/llvm/arm_abi_spec.cr +++ b/spec/std/llvm/arm_abi_spec.cr @@ -9,6 +9,7 @@ private def abi triple = "arm-unknown-linux-gnueabihf" target = LLVM::Target.from_triple(triple) machine = target.create_target_machine(triple) + machine.enable_global_isel = false LLVM::ABI::ARM.new(machine) end diff --git a/spec/std/llvm/x86_64_abi_spec.cr b/spec/std/llvm/x86_64_abi_spec.cr index e9dcc70423c..2e2514e209d 100644 --- a/spec/std/llvm/x86_64_abi_spec.cr +++ b/spec/std/llvm/x86_64_abi_spec.cr @@ -9,6 +9,7 @@ private def abi triple = LLVM.default_target_triple.gsub(/^(.+?)-/, "x86_64-") target = LLVM::Target.from_triple(triple) machine = target.create_target_machine(triple) + machine.enable_global_isel = false LLVM::ABI::X86_64.new(machine) end diff --git a/spec/std/llvm/x86_abi_spec.cr b/spec/std/llvm/x86_abi_spec.cr index e3ae341756b..5f69be12df8 100644 --- a/spec/std/llvm/x86_abi_spec.cr +++ b/spec/std/llvm/x86_abi_spec.cr @@ -13,6 +13,7 @@ private def abi {% end %} target = LLVM::Target.from_triple(triple) machine = target.create_target_machine(triple) + machine.enable_global_isel = false LLVM::ABI::X86.new(machine) end diff --git a/src/compiler/crystal/codegen/target.cr b/src/compiler/crystal/codegen/target.cr index 4e469fef9d4..1a9c7727571 100644 --- a/src/compiler/crystal/codegen/target.cr +++ b/src/compiler/crystal/codegen/target.cr @@ -143,7 +143,14 @@ class Crystal::Codegen::Target opt_level = release ? LLVM::CodeGenOptLevel::Aggressive : LLVM::CodeGenOptLevel::None target = LLVM::Target.from_triple(self.to_s) - target.create_target_machine(self.to_s, cpu: cpu, features: features, opt_level: opt_level, code_model: code_model).not_nil! + machine = target.create_target_machine(self.to_s, cpu: cpu, features: features, opt_level: opt_level, code_model: code_model).not_nil! + # We need to disable global isel until https://reviews.llvm.org/D80898 is released, + # or we fixed generating values for 0 sized types. + # When removing this, also remove it from the ABI specs and jit compiler. + # See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270 + # for background info + machine.enable_global_isel = false + machine end def to_s(io : IO) : Nil diff --git a/src/llvm/ext/llvm_ext.cc b/src/llvm/ext/llvm_ext.cc index b418dabb571..760653d5717 100644 --- a/src/llvm/ext/llvm_ext.cc +++ b/src/llvm/ext/llvm_ext.cc @@ -9,6 +9,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include using namespace llvm; @@ -475,10 +481,84 @@ char *LLVMExtBasicBlockName(LLVMBasicBlockRef BB) { #endif } +static TargetMachine *unwrap(LLVMTargetMachineRef P) { + return reinterpret_cast(P); +} + +void LLVMExtTargetMachineEnableGlobalIsel(LLVMTargetMachineRef T, LLVMBool Enable) { + unwrap(T)->setGlobalISel(Enable); +} + +// Copy paste of https://github.com/llvm/llvm-project/blob/dace8224f38a31636a02fe9c2af742222831f70c/llvm/lib/ExecutionEngine/ExecutionEngineBindings.cpp#L160-L214 +// but with a parameter to set global isel state +LLVMBool LLVMExtCreateMCJITCompilerForModule( + LLVMExecutionEngineRef *OutJIT, LLVMModuleRef M, + LLVMMCJITCompilerOptions *PassedOptions, size_t SizeOfPassedOptions, + LLVMBool EnableGlobalISel, + char **OutError) { + LLVMMCJITCompilerOptions options; + // If the user passed a larger sized options struct, then they were compiled + // against a newer LLVM. Tell them that something is wrong. + if (SizeOfPassedOptions > sizeof(options)) { + *OutError = strdup( + "Refusing to use options struct that is larger than my own; assuming " + "LLVM library mismatch."); + return 1; + } + + + // Defend against the user having an old version of the API by ensuring that + // any fields they didn't see are cleared. We must defend against fields being + // set to the bitwise equivalent of zero, and assume that this means "do the + // default" as if that option hadn't been available. + LLVMInitializeMCJITCompilerOptions(&options, sizeof(options)); + memcpy(&options, PassedOptions, SizeOfPassedOptions); + + + TargetOptions targetOptions; + targetOptions.EnableFastISel = options.EnableFastISel; + targetOptions.EnableGlobalISel = EnableGlobalISel; + std::unique_ptr Mod(unwrap(M)); + + if (Mod) + // Set function attribute "frame-pointer" based on + // NoFramePointerElim. + for (auto &F : *Mod) { + auto Attrs = F.getAttributes(); + StringRef Value = options.NoFramePointerElim ? "all" : "none"; + Attrs = Attrs.addAttribute(F.getContext(), AttributeList::FunctionIndex, + "frame-pointer", Value); + F.setAttributes(Attrs); + } + + + std::string Error; + EngineBuilder builder(std::move(Mod)); + builder.setEngineKind(EngineKind::JIT) + .setErrorStr(&Error) + .setOptLevel((CodeGenOpt::Level)options.OptLevel) + .setTargetOptions(targetOptions); + bool JIT; + if (Optional CM = unwrap(options.CodeModel, JIT)) + builder.setCodeModel(*CM); + if (options.MCJMM) + builder.setMCJITMemoryManager( + std::unique_ptr(unwrap(options.MCJMM))); + + TargetMachine* tm = builder.selectTarget(); + tm->setGlobalISel(EnableGlobalISel); + + if (ExecutionEngine *JIT = builder.create(tm)) { + *OutJIT = wrap(JIT); + return 0; + } + *OutError = strdup(Error.c_str()); + return 1; +} + LLVMMetadataRef LLVMExtDIBuilderGetOrCreateArraySubrange( DIBuilderRef Dref, uint64_t Lo, uint64_t Count) { return wrap(Dref->getOrCreateSubrange(Lo, Count)); -} - + } } diff --git a/src/llvm/jit_compiler.cr b/src/llvm/jit_compiler.cr index 8fb45068b99..b881e06ee8d 100644 --- a/src/llvm/jit_compiler.cr +++ b/src/llvm/jit_compiler.cr @@ -5,7 +5,7 @@ class LLVM::JITCompiler mod.take_ownership { raise "Can't create two JIT compilers for the same module" } # if LibLLVM.create_jit_compiler_for_module(out @unwrap, mod, 3, out error) != 0 - if LibLLVM.create_mc_jit_compiler_for_module(out @unwrap, mod, nil, 0, out error) != 0 + if LibLLVMExt.create_mc_jit_compiler_for_module(out @unwrap, mod, nil, 0, false, out error) != 0 raise LLVM.string_and_dispose(error) end diff --git a/src/llvm/lib_llvm_ext.cr b/src/llvm/lib_llvm_ext.cr index b7bae30dcd4..7c29cf3f6c2 100644 --- a/src/llvm/lib_llvm_ext.cr +++ b/src/llvm/lib_llvm_ext.cr @@ -162,4 +162,7 @@ lib LibLLVMExt fun normalize_target_triple = LLVMExtNormalizeTargetTriple(triple : Char*) : Char* fun basic_block_name = LLVMExtBasicBlockName(basic_block : LibLLVM::BasicBlockRef) : Char* fun di_builder_get_or_create_array_subrange = LLVMExtDIBuilderGetOrCreateArraySubrange(builder : DIBuilder, lo : UInt64, count : UInt64) : Metadata + + fun target_machine_enable_global_isel = LLVMExtTargetMachineEnableGlobalIsel(machine : LibLLVM::TargetMachineRef, enable : Bool) + fun create_mc_jit_compiler_for_module = LLVMExtCreateMCJITCompilerForModule(jit : LibLLVM::ExecutionEngineRef*, m : LibLLVM::ModuleRef, options : LibLLVM::JITCompilerOptions*, options_length : UInt32, enable_global_isel : Bool, error : UInt8**) : Int32 end diff --git a/src/llvm/target_machine.cr b/src/llvm/target_machine.cr index e9c8b793aa7..a0583d16586 100644 --- a/src/llvm/target_machine.cr +++ b/src/llvm/target_machine.cr @@ -32,6 +32,10 @@ class LLVM::TargetMachine emit_to_file llvm_mod, filename, LLVM::CodeGenFileType::AssemblyFile end + def enable_global_isel=(enable : Bool) + LibLLVMExt.target_machine_enable_global_isel(self, enable) + end + private def emit_to_file(llvm_mod, filename, type) status = LibLLVM.target_machine_emit_to_file(self, llvm_mod, filename, type, out error_msg) unless status == 0