Skip to content

Commit ed80ea3

Browse files
authored
LLVM: simplify target initialization and support more targets (#16437)
The LLVM bindings only expose the targets that the Crystal compiler itself supports, and each target copy-pastes the LibLLVM bindings and LLVM init methods which should use a macro. I introduce a `LibLLVM::ALL_TARGETS` constant that's only used in macros to check if a known target has been built and generate the bindings and init methods accordingly. The `@@initialized_` class variables are now an atomic, and the dead `@@initialized` has been removed.
1 parent 2fd8534 commit ed80ea3

File tree

5 files changed

+68
-139
lines changed

5 files changed

+68
-139
lines changed

scripts/generate_llvm_version_info.cr

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,7 @@
55
# LLVM installation different from the one bundled with Crystal.
66

77
require "c/libloaderapi"
8-
9-
# The list of supported targets are hardcoded in:
10-
# https://github.com/llvm/llvm-project/blob/main/llvm/CMakeLists.txt
11-
LLVM_ALL_TARGETS = %w(
12-
AArch64
13-
AMDGPU
14-
ARM
15-
AVR
16-
BPF
17-
Hexagon
18-
Lanai
19-
LoongArch
20-
Mips
21-
MSP430
22-
NVPTX
23-
PowerPC
24-
RISCV
25-
Sparc
26-
SPIRV
27-
SystemZ
28-
VE
29-
WebAssembly
30-
X86
31-
XCore
32-
ARC
33-
CSKY
34-
DirectX
35-
M68k
36-
Xtensa
37-
)
8+
require "llvm/lib_llvm/config"
389

3910
def find_dll_in_env_path
4011
ENV["PATH"]?.try &.split(Process::PATH_DELIMITER, remove_empty: true) do |path|
@@ -62,7 +33,7 @@ begin
6233
patch = uninitialized LibC::UInt
6334
llvm_get_version.call(pointerof(major), pointerof(minor), pointerof(patch))
6435

65-
targets_built = LLVM_ALL_TARGETS.select do |target|
36+
targets_built = LibLLVM::ALL_TARGETS.select do |target|
6637
LibC.GetProcAddress(dll, "LLVMInitialize#{target}Target") && LibC.GetProcAddress(dll, "LLVMInitialize#{target}TargetInfo")
6738
end
6839

src/llvm.cr

Lines changed: 22 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ require "./llvm/**"
22
require "c/string"
33

44
module LLVM
5-
@@initialized = false
6-
75
# Returns the runtime version of LLVM.
86
#
97
# Starting with LLVM 16, this method returns the version as reported by
@@ -20,85 +18,25 @@ module LLVM
2018
{% end %}
2119
end
2220

23-
def self.init_x86 : Nil
24-
return if @@initialized_x86
25-
@@initialized_x86 = true
26-
27-
{% if LibLLVM::BUILT_TARGETS.includes?(:x86) %}
28-
LibLLVM.initialize_x86_target_info
29-
LibLLVM.initialize_x86_target
30-
LibLLVM.initialize_x86_target_mc
31-
LibLLVM.initialize_x86_asm_printer
32-
LibLLVM.initialize_x86_asm_parser
33-
LibLLVM.link_in_mc_jit
34-
{% else %}
35-
raise "ERROR: LLVM was built without X86 target"
36-
{% end %}
37-
end
38-
39-
def self.init_aarch64 : Nil
40-
return if @@initialized_aarch64
41-
@@initialized_aarch64 = true
42-
43-
{% if LibLLVM::BUILT_TARGETS.includes?(:aarch64) %}
44-
LibLLVM.initialize_aarch64_target_info
45-
LibLLVM.initialize_aarch64_target
46-
LibLLVM.initialize_aarch64_target_mc
47-
LibLLVM.initialize_aarch64_asm_printer
48-
LibLLVM.initialize_aarch64_asm_parser
49-
LibLLVM.link_in_mc_jit
50-
{% else %}
51-
raise "ERROR: LLVM was built without AArch64 target"
52-
{% end %}
53-
end
54-
55-
def self.init_arm : Nil
56-
return if @@initialized_arm
57-
@@initialized_arm = true
58-
59-
{% if LibLLVM::BUILT_TARGETS.includes?(:arm) %}
60-
LibLLVM.initialize_arm_target_info
61-
LibLLVM.initialize_arm_target
62-
LibLLVM.initialize_arm_target_mc
63-
LibLLVM.initialize_arm_asm_printer
64-
LibLLVM.initialize_arm_asm_parser
65-
LibLLVM.link_in_mc_jit
66-
{% else %}
67-
raise "ERROR: LLVM was built without ARM target"
68-
{% end %}
69-
end
21+
{% for target in LibLLVM::ALL_TARGETS %}
22+
{% name = target.downcase.id %}
23+
@@initialized_{{name}} = Atomic(Bool).new(false)
7024

71-
def self.init_avr : Nil
72-
return if @@initialized_avr
73-
@@initialized_avr = true
74-
75-
{% if LibLLVM::BUILT_TARGETS.includes?(:avr) %}
76-
LibLLVM.initialize_avr_target_info
77-
LibLLVM.initialize_avr_target
78-
LibLLVM.initialize_avr_target_mc
79-
LibLLVM.initialize_avr_asm_printer
80-
LibLLVM.initialize_avr_asm_parser
81-
LibLLVM.link_in_mc_jit
82-
{% else %}
83-
raise "ERROR: LLVM was built without AVR target"
84-
{% end %}
85-
end
25+
def self.init_{{name}} : Nil
26+
return if @@initialized_{{name}}.swap(true)
8627

87-
def self.init_webassembly : Nil
88-
return if @@initialized_webassembly
89-
@@initialized_webassembly = true
90-
91-
{% if LibLLVM::BUILT_TARGETS.includes?(:webassembly) %}
92-
LibLLVM.initialize_webassembly_target_info
93-
LibLLVM.initialize_webassembly_target
94-
LibLLVM.initialize_webassembly_target_mc
95-
LibLLVM.initialize_webassembly_asm_printer
96-
LibLLVM.initialize_webassembly_asm_parser
97-
LibLLVM.link_in_mc_jit
98-
{% else %}
99-
raise "ERROR: LLVM was built without WebAssembly target"
100-
{% end %}
101-
end
28+
\{% if LibLLVM::BUILT_TARGETS.includes?({{name.symbolize}}) %}
29+
LibLLVM.initialize_{{name}}_target_info
30+
LibLLVM.initialize_{{name}}_target
31+
LibLLVM.initialize_{{name}}_target_mc
32+
LibLLVM.initialize_{{name}}_asm_printer
33+
LibLLVM.initialize_{{name}}_asm_parser
34+
LibLLVM.link_in_mc_jit
35+
\{% else %}
36+
raise "ERROR: LLVM was built without {{target.id}} target"
37+
\{% end %}
38+
end
39+
{% end %}
10240

10341
def self.init_native_target : Nil
10442
{% if flag?(:i386) || flag?(:x86_64) %}
@@ -117,10 +55,11 @@ module LLVM
11755
end
11856

11957
def self.init_all_targets : Nil
120-
{% for target in %i(x86 aarch64 arm avr webassembly) %}
121-
{% if LibLLVM::BUILT_TARGETS.includes?(target) %}
122-
init_{{ target.id }}
123-
{% end %}
58+
{% for target in LibLLVM::ALL_TARGETS %}
59+
{% name = target.downcase.id %}
60+
\{% if LibLLVM::BUILT_TARGETS.includes?({{name.symbolize}}) %}
61+
init_{{name}}
62+
\{% end %}
12463
{% end %}
12564
end
12665

src/llvm/lib_llvm.cr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,5 @@ lib LibLLVM
8787
alias SizeT = LibC::SizeT
8888
end
8989

90+
require "./lib_llvm/config"
9091
require "./lib_llvm/**"

src/llvm/lib_llvm/config.cr

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# The list of supported targets are hardcoded in:
2+
# https://github.com/llvm/llvm-project/blob/main/llvm/CMakeLists.txt
3+
4+
lib LibLLVM
5+
ALL_TARGETS = [
6+
# default targets (as of LLVM 21)
7+
"AArch64",
8+
"AMDGPU",
9+
"ARM",
10+
"AVR",
11+
"BPF",
12+
"Hexagon",
13+
"Lanai",
14+
"LoongArch",
15+
"MSP430",
16+
"Mips",
17+
"NVPTX",
18+
"PowerPC",
19+
"RISCV",
20+
"SPIRV",
21+
"Sparc",
22+
"SystemZ",
23+
"VE",
24+
"WebAssembly",
25+
"X86",
26+
"XCore",
27+
28+
# experimental targets (as of LLVM 21)
29+
"ARC",
30+
"CSKY",
31+
"DirectX",
32+
"M68k",
33+
"Xtensa",
34+
]
35+
end

src/llvm/lib_llvm/target.cr

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,14 @@ require "./types"
33
lib LibLLVM
44
type TargetDataRef = Void*
55

6-
fun initialize_aarch64_target_info = LLVMInitializeAArch64TargetInfo
7-
fun initialize_aarch64_target = LLVMInitializeAArch64Target
8-
fun initialize_aarch64_target_mc = LLVMInitializeAArch64TargetMC
9-
fun initialize_aarch64_asm_printer = LLVMInitializeAArch64AsmPrinter
10-
fun initialize_aarch64_asm_parser = LLVMInitializeAArch64AsmParser
11-
fun initialize_arm_target_info = LLVMInitializeARMTargetInfo
12-
fun initialize_arm_target = LLVMInitializeARMTarget
13-
fun initialize_arm_target_mc = LLVMInitializeARMTargetMC
14-
fun initialize_arm_asm_printer = LLVMInitializeARMAsmPrinter
15-
fun initialize_arm_asm_parser = LLVMInitializeARMAsmParser
16-
fun initialize_avr_target_info = LLVMInitializeAVRTargetInfo
17-
fun initialize_avr_target = LLVMInitializeAVRTarget
18-
fun initialize_avr_target_mc = LLVMInitializeAVRTargetMC
19-
fun initialize_avr_asm_printer = LLVMInitializeAVRAsmPrinter
20-
fun initialize_avr_asm_parser = LLVMInitializeAVRAsmParser
21-
fun initialize_webassembly_target_info = LLVMInitializeWebAssemblyTargetInfo
22-
fun initialize_webassembly_target = LLVMInitializeWebAssemblyTarget
23-
fun initialize_webassembly_target_mc = LLVMInitializeWebAssemblyTargetMC
24-
fun initialize_webassembly_asm_printer = LLVMInitializeWebAssemblyAsmPrinter
25-
fun initialize_webassembly_asm_parser = LLVMInitializeWebAssemblyAsmParser
26-
fun initialize_x86_target_info = LLVMInitializeX86TargetInfo
27-
fun initialize_x86_target = LLVMInitializeX86Target
28-
fun initialize_x86_target_mc = LLVMInitializeX86TargetMC
29-
fun initialize_x86_asm_printer = LLVMInitializeX86AsmPrinter
30-
fun initialize_x86_asm_parser = LLVMInitializeX86AsmParser
6+
{% for target in ALL_TARGETS %}
7+
{% name = target.downcase.id %}
8+
fun initialize_{{name}}_target_info = LLVMInitialize{{target.id}}TargetInfo
9+
fun initialize_{{name}}_target = LLVMInitialize{{target.id}}Target
10+
fun initialize_{{name}}_target_mc = LLVMInitialize{{target.id}}TargetMC
11+
fun initialize_{{name}}_asm_printer = LLVMInitialize{{target.id}}AsmPrinter
12+
fun initialize_{{name}}_asm_parser = LLVMInitialize{{target.id}}AsmParser
13+
{% end %}
3114

3215
fun set_module_data_layout = LLVMSetModuleDataLayout(m : ModuleRef, dl : TargetDataRef)
3316

0 commit comments

Comments
 (0)