summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_target
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_target')
-rw-r--r--compiler/rustc_target/Cargo.toml14
-rw-r--r--compiler/rustc_target/README.md6
-rw-r--r--compiler/rustc_target/src/abi/call/aarch64.rs86
-rw-r--r--compiler/rustc_target/src/abi/call/amdgpu.rs35
-rw-r--r--compiler/rustc_target/src/abi/call/arm.rs97
-rw-r--r--compiler/rustc_target/src/abi/call/avr.rs59
-rw-r--r--compiler/rustc_target/src/abi/call/bpf.rs31
-rw-r--r--compiler/rustc_target/src/abi/call/hexagon.rs30
-rw-r--r--compiler/rustc_target/src/abi/call/m68k.rs30
-rw-r--r--compiler/rustc_target/src/abi/call/mips.rs51
-rw-r--r--compiler/rustc_target/src/abi/call/mips64.rs167
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs734
-rw-r--r--compiler/rustc_target/src/abi/call/msp430.rs39
-rw-r--r--compiler/rustc_target/src/abi/call/nvptx.rs33
-rw-r--r--compiler/rustc_target/src/abi/call/nvptx64.rs64
-rw-r--r--compiler/rustc_target/src/abi/call/powerpc.rs30
-rw-r--r--compiler/rustc_target/src/abi/call/powerpc64.rs141
-rw-r--r--compiler/rustc_target/src/abi/call/riscv.rs348
-rw-r--r--compiler/rustc_target/src/abi/call/s390x.rs57
-rw-r--r--compiler/rustc_target/src/abi/call/sparc.rs51
-rw-r--r--compiler/rustc_target/src/abi/call/sparc64.rs226
-rw-r--r--compiler/rustc_target/src/abi/call/wasm.rs83
-rw-r--r--compiler/rustc_target/src/abi/call/x86.rs117
-rw-r--r--compiler/rustc_target/src/abi/call/x86_64.rs248
-rw-r--r--compiler/rustc_target/src/abi/call/x86_win64.rs40
-rw-r--r--compiler/rustc_target/src/abi/mod.rs1558
-rw-r--r--compiler/rustc_target/src/asm/aarch64.rs200
-rw-r--r--compiler/rustc_target/src/asm/arm.rs340
-rw-r--r--compiler/rustc_target/src/asm/avr.rs197
-rw-r--r--compiler/rustc_target/src/asm/bpf.rs118
-rw-r--r--compiler/rustc_target/src/asm/hexagon.rs95
-rw-r--r--compiler/rustc_target/src/asm/mips.rs135
-rw-r--r--compiler/rustc_target/src/asm/mod.rs976
-rw-r--r--compiler/rustc_target/src/asm/msp430.rs81
-rw-r--r--compiler/rustc_target/src/asm/nvptx.rs50
-rw-r--r--compiler/rustc_target/src/asm/powerpc.rs204
-rw-r--r--compiler/rustc_target/src/asm/riscv.rs185
-rw-r--r--compiler/rustc_target/src/asm/s390x.rs107
-rw-r--r--compiler/rustc_target/src/asm/spirv.rs47
-rw-r--r--compiler/rustc_target/src/asm/wasm.rs47
-rw-r--r--compiler/rustc_target/src/asm/x86.rs492
-rw-r--r--compiler/rustc_target/src/json.rs91
-rw-r--r--compiler/rustc_target/src/lib.rs88
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_darwin.rs30
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_ios.rs36
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs32
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_ios_sim.rs38
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_tvos.rs18
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_watchos_sim.rs38
-rw-r--r--compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs18
-rw-r--r--compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs21
-rw-r--r--compiler/rustc_target/src/spec/aarch64_fuchsia.rs15
-rw-r--r--compiler/rustc_target/src/spec/aarch64_kmc_solid_asp3.rs19
-rw-r--r--compiler/rustc_target/src/spec/aarch64_linux_android.rs25
-rw-r--r--compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs26
-rw-r--r--compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld78
-rw-r--r--compiler/rustc_target/src/spec/aarch64_pc_windows_gnullvm.rs16
-rw-r--r--compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs15
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs18
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs15
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs23
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs17
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs14
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs15
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_none.rs29
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs30
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs11
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_redox.rs14
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs20
-rw-r--r--compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs14
-rw-r--r--compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs11
-rw-r--r--compiler/rustc_target/src/spec/abi.rs175
-rw-r--r--compiler/rustc_target/src/spec/abi/tests.rs27
-rw-r--r--compiler/rustc_target/src/spec/android_base.rs15
-rw-r--r--compiler/rustc_target/src/spec/apple_base.rs143
-rw-r--r--compiler/rustc_target/src/spec/apple_sdk_base.rs113
-rw-r--r--compiler/rustc_target/src/spec/arm64_32_apple_watchos.rs28
-rw-r--r--compiler/rustc_target/src/spec/arm_linux_androideabi.rs18
-rw-r--r--compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs17
-rw-r--r--compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs17
-rw-r--r--compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs22
-rw-r--r--compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs22
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabi.rs27
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs28
-rw-r--r--compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs23
-rw-r--r--compiler/rustc_target/src/spec/armv5te_unknown_linux_uclibceabi.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs38
-rw-r--r--compiler/rustc_target/src/spec/armv7_apple_ios.rs18
-rw-r--r--compiler/rustc_target/src/spec/armv7_linux_androideabi.rs27
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs20
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs21
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs26
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs25
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs23
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabihf.rs24
-rw-r--r--compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs17
-rw-r--r--compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabi.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabihf.rs19
-rw-r--r--compiler/rustc_target/src/spec/armv7a_none_eabi.rs40
-rw-r--r--compiler/rustc_target/src/spec/armv7a_none_eabihf.rs32
-rw-r--r--compiler/rustc_target/src/spec/armv7k_apple_watchos.rs28
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabi.rs26
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabihf.rs27
-rw-r--r--compiler/rustc_target/src/spec/armv7s_apple_ios.rs16
-rw-r--r--compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs7
-rw-r--r--compiler/rustc_target/src/spec/avr_gnu_base.rs27
-rw-r--r--compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs5
-rw-r--r--compiler/rustc_target/src/spec/bpf_base.rs25
-rw-r--r--compiler/rustc_target/src/spec/bpfeb_unknown_none.rs12
-rw-r--r--compiler/rustc_target/src/spec/bpfel_unknown_none.rs12
-rw-r--r--compiler/rustc_target/src/spec/crt_objects.rs157
-rw-r--r--compiler/rustc_target/src/spec/dragonfly_base.rs14
-rw-r--r--compiler/rustc_target/src/spec/freebsd_base.rs15
-rw-r--r--compiler/rustc_target/src/spec/fuchsia_base.rs38
-rw-r--r--compiler/rustc_target/src/spec/haiku_base.rs11
-rw-r--r--compiler/rustc_target/src/spec/hermit_base.rs21
-rw-r--r--compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs30
-rw-r--r--compiler/rustc_target/src/spec/i386_apple_ios.rs22
-rw-r--r--compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs8
-rw-r--r--compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs8
-rw-r--r--compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs8
-rw-r--r--compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs8
-rw-r--r--compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs8
-rw-r--r--compiler/rustc_target/src/spec/i686_apple_darwin.rs28
-rw-r--r--compiler/rustc_target/src/spec/i686_linux_android.rs26
-rw-r--r--compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs24
-rw-r--r--compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs32
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_freebsd.rs20
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_haiku.rs20
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs20
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs34
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_netbsd.rs20
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_openbsd.rs20
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_uefi.rs89
-rw-r--r--compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs23
-rw-r--r--compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs17
-rw-r--r--compiler/rustc_target/src/spec/i686_wrs_vxworks.rs20
-rw-r--r--compiler/rustc_target/src/spec/illumos_base.rs59
-rw-r--r--compiler/rustc_target/src/spec/l4re_base.rs14
-rw-r--r--compiler/rustc_target/src/spec/linux_base.rs15
-rw-r--r--compiler/rustc_target/src/spec/linux_gnu_base.rs5
-rw-r--r--compiler/rustc_target/src/spec/linux_kernel_base.rs19
-rw-r--r--compiler/rustc_target/src/spec/linux_musl_base.rs16
-rw-r--r--compiler/rustc_target/src/spec/linux_uclibc_base.rs5
-rw-r--r--compiler/rustc_target/src/spec/m68k_unknown_linux_gnu.rs15
-rw-r--r--compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs26
-rw-r--r--compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs22
-rw-r--r--compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs22
-rw-r--r--compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs20
-rw-r--r--compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs16
-rw-r--r--compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs20
-rw-r--r--compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs17
-rw-r--r--compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs20
-rw-r--r--compiler/rustc_target/src/spec/mipsel_sony_psp.rs34
-rw-r--r--compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld45
-rw-r--r--compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs19
-rw-r--r--compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs16
-rw-r--r--compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs19
-rw-r--r--compiler/rustc_target/src/spec/mipsel_unknown_none.rs27
-rw-r--r--compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs20
-rw-r--r--compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs19
-rw-r--r--compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs22
-rw-r--r--compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs20
-rw-r--r--compiler/rustc_target/src/spec/mod.rs2585
-rw-r--r--compiler/rustc_target/src/spec/msp430_none_elf.rs59
-rw-r--r--compiler/rustc_target/src/spec/msvc_base.rs24
-rw-r--r--compiler/rustc_target/src/spec/netbsd_base.rs16
-rw-r--r--compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs53
-rw-r--r--compiler/rustc_target/src/spec/openbsd_base.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs17
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs21
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs17
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs17
-rw-r--r--compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs23
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs21
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_openbsd.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs16
-rw-r--r--compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs22
-rw-r--r--compiler/rustc_target/src/spec/redox_base.rs17
-rw-r--r--compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs18
-rw-r--r--compiler/rustc_target/src/spec/riscv32gc_unknown_linux_musl.rs18
-rw-r--r--compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs24
-rw-r--r--compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs25
-rw-r--r--compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs24
-rw-r--r--compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs23
-rw-r--r--compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs36
-rw-r--r--compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs25
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_freebsd.rs18
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs18
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_linux_musl.rs18
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs26
-rw-r--r--compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs25
-rw-r--r--compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs23
-rw-r--r--compiler/rustc_target/src/spec/s390x_unknown_linux_musl.rs24
-rw-r--r--compiler/rustc_target/src/spec/solaris_base.rs16
-rw-r--r--compiler/rustc_target/src/spec/solid_base.rs13
-rw-r--r--compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs17
-rw-r--r--compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs17
-rw-r--r--compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs18
-rw-r--r--compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs18
-rw-r--r--compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs24
-rw-r--r--compiler/rustc_target/src/spec/tests/tests_impl.rs135
-rw-r--r--compiler/rustc_target/src/spec/thumb_base.rs60
-rw-r--r--compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs69
-rw-r--r--compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs23
-rw-r--r--compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs28
-rw-r--r--compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs18
-rw-r--r--compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs27
-rw-r--r--compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs36
-rw-r--r--compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs18
-rw-r--r--compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs26
-rw-r--r--compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs23
-rw-r--r--compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs29
-rw-r--r--compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs21
-rw-r--r--compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs19
-rw-r--r--compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs25
-rw-r--r--compiler/rustc_target/src/spec/uefi_msvc_base.rs51
-rw-r--r--compiler/rustc_target/src/spec/vxworks_base.rs21
-rw-r--r--compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs34
-rw-r--r--compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs64
-rw-r--r--compiler/rustc_target/src/spec/wasm32_wasi.rs112
-rw-r--r--compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs49
-rw-r--r--compiler/rustc_target/src/spec/wasm_base.rs129
-rw-r--r--compiler/rustc_target/src/spec/windows_gnu_base.rs91
-rw-r--r--compiler/rustc_target/src/spec/windows_gnullvm_base.rs40
-rw-r--r--compiler/rustc_target/src/spec/windows_msvc_base.rs34
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs34
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs11
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_darwin.rs30
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_ios.rs21
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs23
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_tvos.rs18
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs35
-rw-r--r--compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs84
-rw-r--r--compiler/rustc_target/src/spec/x86_64_fuchsia.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_linux_android.rs21
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_solaris.rs21
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs20
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs18
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs16
-rw-r--r--compiler/rustc_target/src/spec/x86_64_sun_solaris.rs20
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs21
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs21
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs20
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs25
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs25
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs25
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs24
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_none.rs40
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs28
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_redox.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs36
-rw-r--r--compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs16
-rw-r--r--compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs20
-rw-r--r--compiler/rustc_target/src/tests.rs57
272 files changed, 16668 insertions, 0 deletions
diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml
new file mode 100644
index 000000000..162376af4
--- /dev/null
+++ b/compiler/rustc_target/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "rustc_target"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+bitflags = "1.2.1"
+tracing = "0.1"
+serde_json = "1.0.59"
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_span = { path = "../rustc_span" }
+rustc_index = { path = "../rustc_index" }
diff --git a/compiler/rustc_target/README.md b/compiler/rustc_target/README.md
new file mode 100644
index 000000000..ca72a89da
--- /dev/null
+++ b/compiler/rustc_target/README.md
@@ -0,0 +1,6 @@
+`rustc_target` contains some very low-level details that are
+specific to different compilation targets and so forth.
+
+For more information about how rustc works, see the [rustc dev guide].
+
+[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/
diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs
new file mode 100644
index 000000000..4613a459c
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/aarch64.rs
@@ -0,0 +1,86 @@
+use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
+use crate::abi::{HasDataLayout, TyAbiInterface};
+
+fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
+ let size = arg.layout.size;
+
+ // Ensure we have at most four uniquely addressable members.
+ if size > unit.size.checked_mul(4, cx).unwrap() {
+ return None;
+ }
+
+ let valid_unit = match unit.kind {
+ RegKind::Integer => false,
+ RegKind::Float => true,
+ RegKind::Vector => size.bits() == 64 || size.bits() == 128,
+ };
+
+ valid_unit.then_some(Uniform { unit, total: size })
+ })
+}
+
+fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !ret.layout.is_aggregate() {
+ ret.extend_integer_width_to(32);
+ return;
+ }
+ if let Some(uniform) = is_homogeneous_aggregate(cx, ret) {
+ ret.cast_to(uniform);
+ return;
+ }
+ let size = ret.layout.size;
+ let bits = size.bits();
+ if bits <= 128 {
+ ret.cast_to(Uniform { unit: Reg::i64(), total: size });
+ return;
+ }
+ ret.make_indirect();
+}
+
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !arg.layout.is_aggregate() {
+ arg.extend_integer_width_to(32);
+ return;
+ }
+ if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
+ arg.cast_to(uniform);
+ return;
+ }
+ let size = arg.layout.size;
+ let bits = size.bits();
+ if bits <= 128 {
+ arg.cast_to(Uniform { unit: Reg::i64(), total: size });
+ return;
+ }
+ arg.make_indirect();
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/amdgpu.rs b/compiler/rustc_target/src/abi/call/amdgpu.rs
new file mode 100644
index 000000000..9be97476c
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/amdgpu.rs
@@ -0,0 +1,35 @@
+use crate::abi::call::{ArgAbi, FnAbi};
+use crate::abi::{HasDataLayout, TyAbiInterface};
+
+fn classify_ret<'a, Ty, C>(_cx: &C, ret: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ ret.extend_integer_width_to(32);
+}
+
+fn classify_arg<'a, Ty, C>(_cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ arg.extend_integer_width_to(32);
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/abi/call/arm.rs
new file mode 100644
index 000000000..e66c2132b
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/arm.rs
@@ -0,0 +1,97 @@
+use crate::abi::call::{ArgAbi, Conv, FnAbi, Reg, RegKind, Uniform};
+use crate::abi::{HasDataLayout, TyAbiInterface};
+use crate::spec::HasTargetSpec;
+
+fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
+ let size = arg.layout.size;
+
+ // Ensure we have at most four uniquely addressable members.
+ if size > unit.size.checked_mul(4, cx).unwrap() {
+ return None;
+ }
+
+ let valid_unit = match unit.kind {
+ RegKind::Integer => false,
+ RegKind::Float => true,
+ RegKind::Vector => size.bits() == 64 || size.bits() == 128,
+ };
+
+ valid_unit.then_some(Uniform { unit, total: size })
+ })
+}
+
+fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, vfp: bool)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !ret.layout.is_aggregate() {
+ ret.extend_integer_width_to(32);
+ return;
+ }
+
+ if vfp {
+ if let Some(uniform) = is_homogeneous_aggregate(cx, ret) {
+ ret.cast_to(uniform);
+ return;
+ }
+ }
+
+ let size = ret.layout.size;
+ let bits = size.bits();
+ if bits <= 32 {
+ ret.cast_to(Uniform { unit: Reg::i32(), total: size });
+ return;
+ }
+ ret.make_indirect();
+}
+
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, vfp: bool)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !arg.layout.is_aggregate() {
+ arg.extend_integer_width_to(32);
+ return;
+ }
+
+ if vfp {
+ if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
+ arg.cast_to(uniform);
+ return;
+ }
+ }
+
+ let align = arg.layout.align.abi.bytes();
+ let total = arg.layout.size;
+ arg.cast_to(Uniform { unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, total });
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout + HasTargetSpec,
+{
+ // If this is a target with a hard-float ABI, and the function is not explicitly
+ // `extern "aapcs"`, then we must use the VFP registers for homogeneous aggregates.
+ let vfp = cx.target_spec().llvm_target.ends_with("hf")
+ && fn_abi.conv != Conv::ArmAapcs
+ && !fn_abi.c_variadic;
+
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret, vfp);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg, vfp);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/avr.rs b/compiler/rustc_target/src/abi/call/avr.rs
new file mode 100644
index 000000000..c1f7a1e3a
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/avr.rs
@@ -0,0 +1,59 @@
+//! LLVM-frontend specific AVR calling convention implementation.
+//!
+//! # Current calling convention ABI
+//!
+//! Inherited from Clang's `clang::DefaultABIInfo` implementation - self described
+//! as
+//!
+//! > the default implementation for ABI specific details. This implementation
+//! > provides information which results in
+//! > self-consistent and sensible LLVM IR generation, but does not
+//! > conform to any particular ABI.
+//! >
+//! > - Doxygen Doxumentation of `clang::DefaultABIInfo`
+//!
+//! This calling convention may not match AVR-GCC in all cases.
+//!
+//! In the future, an AVR-GCC compatible argument classification ABI should be
+//! adopted in both Rust and Clang.
+//!
+//! *NOTE*: Currently, this module implements the same calling convention
+//! that clang with AVR currently does - the default, simple, unspecialized
+//! ABI implementation available to all targets. This ABI is not
+//! binary-compatible with AVR-GCC. Once LLVM [PR46140](https://bugs.llvm.org/show_bug.cgi?id=46140)
+//! is completed, this module should be updated to match so that both Clang
+//! and Rust emit code to the same AVR-GCC compatible ABI.
+//!
+//! In particular, both Clang and Rust may not have the same semantics
+//! when promoting arguments to indirect references as AVR-GCC. It is important
+//! to note that the core AVR ABI implementation within LLVM itself is ABI
+//! compatible with AVR-GCC - Rust and AVR-GCC only differ in the small amount
+//! of compiler frontend specific calling convention logic implemented here.
+
+use crate::abi::call::{ArgAbi, FnAbi};
+
+fn classify_ret_ty<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() {
+ ret.make_indirect();
+ }
+}
+
+fn classify_arg_ty<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() {
+ arg.make_indirect();
+ }
+}
+
+pub fn compute_abi_info<Ty>(fty: &mut FnAbi<'_, Ty>) {
+ if !fty.ret.is_ignore() {
+ classify_ret_ty(&mut fty.ret);
+ }
+
+ for arg in &mut fty.args {
+ if arg.is_ignore() {
+ continue;
+ }
+
+ classify_arg_ty(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/bpf.rs b/compiler/rustc_target/src/abi/call/bpf.rs
new file mode 100644
index 000000000..466c52553
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/bpf.rs
@@ -0,0 +1,31 @@
+// see https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/BPF/BPFCallingConv.td
+use crate::abi::call::{ArgAbi, FnAbi};
+
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() || ret.layout.size.bits() > 64 {
+ ret.make_indirect();
+ } else {
+ ret.extend_integer_width_to(32);
+ }
+}
+
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() || arg.layout.size.bits() > 64 {
+ arg.make_indirect();
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+}
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/hexagon.rs b/compiler/rustc_target/src/abi/call/hexagon.rs
new file mode 100644
index 000000000..8028443b8
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/hexagon.rs
@@ -0,0 +1,30 @@
+use crate::abi::call::{ArgAbi, FnAbi};
+
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 {
+ ret.make_indirect();
+ } else {
+ ret.extend_integer_width_to(32);
+ }
+}
+
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 {
+ arg.make_indirect();
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+}
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/m68k.rs b/compiler/rustc_target/src/abi/call/m68k.rs
new file mode 100644
index 000000000..58fdc00b6
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/m68k.rs
@@ -0,0 +1,30 @@
+use crate::abi::call::{ArgAbi, FnAbi};
+
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() {
+ ret.make_indirect();
+ } else {
+ ret.extend_integer_width_to(32);
+ }
+}
+
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() {
+ arg.make_indirect_byval();
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+}
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/mips.rs b/compiler/rustc_target/src/abi/call/mips.rs
new file mode 100644
index 000000000..cc4431976
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/mips.rs
@@ -0,0 +1,51 @@
+use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform};
+use crate::abi::{HasDataLayout, Size};
+
+fn classify_ret<Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size)
+where
+ C: HasDataLayout,
+{
+ if !ret.layout.is_aggregate() {
+ ret.extend_integer_width_to(32);
+ } else {
+ ret.make_indirect();
+ *offset += cx.data_layout().pointer_size;
+ }
+}
+
+fn classify_arg<Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
+where
+ C: HasDataLayout,
+{
+ let dl = cx.data_layout();
+ let size = arg.layout.size;
+ let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi;
+
+ if arg.layout.is_aggregate() {
+ arg.cast_to(Uniform { unit: Reg::i32(), total: size });
+ if !offset.is_aligned(align) {
+ arg.pad_with(Reg::i32());
+ }
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+
+ *offset = offset.align_to(align) + size.align_to(align);
+}
+
+pub fn compute_abi_info<Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>)
+where
+ C: HasDataLayout,
+{
+ let mut offset = Size::ZERO;
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret, &mut offset);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg, &mut offset);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/mips64.rs b/compiler/rustc_target/src/abi/call/mips64.rs
new file mode 100644
index 000000000..cd54167aa
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/mips64.rs
@@ -0,0 +1,167 @@
+use crate::abi::call::{
+ ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, Reg, Uniform,
+};
+use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
+
+fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) {
+ // Always sign extend u32 values on 64-bit mips
+ if let abi::Abi::Scalar(scalar) = arg.layout.abi {
+ if let abi::Int(i, signed) = scalar.primitive() {
+ if !signed && i.size().bits() == 32 {
+ if let PassMode::Direct(ref mut attrs) = arg.mode {
+ attrs.ext(ArgExtension::Sext);
+ return;
+ }
+ }
+ }
+ }
+
+ arg.extend_integer_width_to(bits);
+}
+
+fn float_reg<'a, Ty, C>(cx: &C, ret: &ArgAbi<'a, Ty>, i: usize) -> Option<Reg>
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ match ret.layout.field(cx, i).abi {
+ abi::Abi::Scalar(scalar) => match scalar.primitive() {
+ abi::F32 => Some(Reg::f32()),
+ abi::F64 => Some(Reg::f64()),
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !ret.layout.is_aggregate() {
+ extend_integer_width_mips(ret, 64);
+ return;
+ }
+
+ let size = ret.layout.size;
+ let bits = size.bits();
+ if bits <= 128 {
+ // Unlike other architectures which return aggregates in registers, MIPS n64 limits the
+ // use of float registers to structures (not unions) containing exactly one or two
+ // float fields.
+
+ if let abi::FieldsShape::Arbitrary { .. } = ret.layout.fields {
+ if ret.layout.fields.count() == 1 {
+ if let Some(reg) = float_reg(cx, ret, 0) {
+ ret.cast_to(reg);
+ return;
+ }
+ } else if ret.layout.fields.count() == 2 {
+ if let Some(reg0) = float_reg(cx, ret, 0) {
+ if let Some(reg1) = float_reg(cx, ret, 1) {
+ ret.cast_to(CastTarget::pair(reg0, reg1));
+ return;
+ }
+ }
+ }
+ }
+
+ // Cast to a uniform int structure
+ ret.cast_to(Uniform { unit: Reg::i64(), total: size });
+ } else {
+ ret.make_indirect();
+ }
+}
+
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !arg.layout.is_aggregate() {
+ extend_integer_width_mips(arg, 64);
+ return;
+ }
+
+ let dl = cx.data_layout();
+ let size = arg.layout.size;
+ let mut prefix = [None; 8];
+ let mut prefix_index = 0;
+
+ match arg.layout.fields {
+ abi::FieldsShape::Primitive => unreachable!(),
+ abi::FieldsShape::Array { .. } => {
+ // Arrays are passed indirectly
+ arg.make_indirect();
+ return;
+ }
+ abi::FieldsShape::Union(_) => {
+ // Unions and are always treated as a series of 64-bit integer chunks
+ }
+ abi::FieldsShape::Arbitrary { .. } => {
+ // Structures are split up into a series of 64-bit integer chunks, but any aligned
+ // doubles not part of another aggregate are passed as floats.
+ let mut last_offset = Size::ZERO;
+
+ for i in 0..arg.layout.fields.count() {
+ let field = arg.layout.field(cx, i);
+ let offset = arg.layout.fields.offset(i);
+
+ // We only care about aligned doubles
+ if let abi::Abi::Scalar(scalar) = field.abi {
+ if let abi::F64 = scalar.primitive() {
+ if offset.is_aligned(dl.f64_align.abi) {
+ // Insert enough integers to cover [last_offset, offset)
+ assert!(last_offset.is_aligned(dl.f64_align.abi));
+ for _ in 0..((offset - last_offset).bits() / 64)
+ .min((prefix.len() - prefix_index) as u64)
+ {
+ prefix[prefix_index] = Some(Reg::i64());
+ prefix_index += 1;
+ }
+
+ if prefix_index == prefix.len() {
+ break;
+ }
+
+ prefix[prefix_index] = Some(Reg::f64());
+ prefix_index += 1;
+ last_offset = offset + Reg::f64().size;
+ }
+ }
+ }
+ }
+ }
+ };
+
+ // Extract first 8 chunks as the prefix
+ let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
+ arg.cast_to(CastTarget {
+ prefix,
+ rest: Uniform { unit: Reg::i64(), total: rest_size },
+ attrs: ArgAttributes {
+ regular: ArgAttribute::default(),
+ arg_ext: ArgExtension::None,
+ pointee_size: Size::ZERO,
+ pointee_align: None,
+ },
+ });
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
new file mode 100644
index 000000000..577126a95
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -0,0 +1,734 @@
+use crate::abi::{self, Abi, Align, FieldsShape, Size};
+use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
+use crate::spec::{self, HasTargetSpec};
+use rustc_span::Symbol;
+use std::fmt;
+
+mod aarch64;
+mod amdgpu;
+mod arm;
+mod avr;
+mod bpf;
+mod hexagon;
+mod m68k;
+mod mips;
+mod mips64;
+mod msp430;
+mod nvptx;
+mod nvptx64;
+mod powerpc;
+mod powerpc64;
+mod riscv;
+mod s390x;
+mod sparc;
+mod sparc64;
+mod wasm;
+mod x86;
+mod x86_64;
+mod x86_win64;
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum PassMode {
+ /// Ignore the argument.
+ ///
+ /// The argument is either uninhabited or a ZST.
+ Ignore,
+ /// Pass the argument directly.
+ ///
+ /// The argument has a layout abi of `Scalar`, `Vector` or in rare cases `Aggregate`.
+ Direct(ArgAttributes),
+ /// Pass a pair's elements directly in two arguments.
+ ///
+ /// The argument has a layout abi of `ScalarPair`.
+ Pair(ArgAttributes, ArgAttributes),
+ /// Pass the argument after casting it, to either
+ /// a single uniform or a pair of registers.
+ Cast(CastTarget),
+ /// Pass the argument indirectly via a hidden pointer.
+ /// The `extra_attrs` value, if any, is for the extra data (vtable or length)
+ /// which indicates that it refers to an unsized rvalue.
+ /// `on_stack` defines that the the value should be passed at a fixed
+ /// stack offset in accordance to the ABI rather than passed using a
+ /// pointer. This corresponds to the `byval` LLVM argument attribute.
+ Indirect { attrs: ArgAttributes, extra_attrs: Option<ArgAttributes>, on_stack: bool },
+}
+
+// Hack to disable non_upper_case_globals only for the bitflags! and not for the rest
+// of this module
+pub use attr_impl::ArgAttribute;
+
+#[allow(non_upper_case_globals)]
+#[allow(unused)]
+mod attr_impl {
+ // The subset of llvm::Attribute needed for arguments, packed into a bitfield.
+ bitflags::bitflags! {
+ #[derive(Default, HashStable_Generic)]
+ pub struct ArgAttribute: u16 {
+ const NoAlias = 1 << 1;
+ const NoCapture = 1 << 2;
+ const NonNull = 1 << 3;
+ const ReadOnly = 1 << 4;
+ const InReg = 1 << 5;
+ // Due to past miscompiles in LLVM, we use a separate attribute for
+ // &mut arguments, so that the codegen backend can decide whether
+ // or not to actually emit the attribute. It can also be controlled
+ // with the `-Zmutable-noalias` debugging option.
+ const NoAliasMutRef = 1 << 6;
+ const NoUndef = 1 << 7;
+ }
+ }
+}
+
+/// Sometimes an ABI requires small integers to be extended to a full or partial register. This enum
+/// defines if this extension should be zero-extension or sign-extension when necessary. When it is
+/// not necessary to extend the argument, this enum is ignored.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum ArgExtension {
+ None,
+ Zext,
+ Sext,
+}
+
+/// A compact representation of LLVM attributes (at least those relevant for this module)
+/// that can be manipulated without interacting with LLVM's Attribute machinery.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct ArgAttributes {
+ pub regular: ArgAttribute,
+ pub arg_ext: ArgExtension,
+ /// The minimum size of the pointee, guaranteed to be valid for the duration of the whole call
+ /// (corresponding to LLVM's dereferenceable and dereferenceable_or_null attributes).
+ pub pointee_size: Size,
+ pub pointee_align: Option<Align>,
+}
+
+impl ArgAttributes {
+ pub fn new() -> Self {
+ ArgAttributes {
+ regular: ArgAttribute::default(),
+ arg_ext: ArgExtension::None,
+ pointee_size: Size::ZERO,
+ pointee_align: None,
+ }
+ }
+
+ pub fn ext(&mut self, ext: ArgExtension) -> &mut Self {
+ assert!(
+ self.arg_ext == ArgExtension::None || self.arg_ext == ext,
+ "cannot set {:?} when {:?} is already set",
+ ext,
+ self.arg_ext
+ );
+ self.arg_ext = ext;
+ self
+ }
+
+ pub fn set(&mut self, attr: ArgAttribute) -> &mut Self {
+ self.regular |= attr;
+ self
+ }
+
+ pub fn contains(&self, attr: ArgAttribute) -> bool {
+ self.regular.contains(attr)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum RegKind {
+ Integer,
+ Float,
+ Vector,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct Reg {
+ pub kind: RegKind,
+ pub size: Size,
+}
+
+macro_rules! reg_ctor {
+ ($name:ident, $kind:ident, $bits:expr) => {
+ pub fn $name() -> Reg {
+ Reg { kind: RegKind::$kind, size: Size::from_bits($bits) }
+ }
+ };
+}
+
+impl Reg {
+ reg_ctor!(i8, Integer, 8);
+ reg_ctor!(i16, Integer, 16);
+ reg_ctor!(i32, Integer, 32);
+ reg_ctor!(i64, Integer, 64);
+ reg_ctor!(i128, Integer, 128);
+
+ reg_ctor!(f32, Float, 32);
+ reg_ctor!(f64, Float, 64);
+}
+
+impl Reg {
+ pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
+ let dl = cx.data_layout();
+ match self.kind {
+ RegKind::Integer => match self.size.bits() {
+ 1 => dl.i1_align.abi,
+ 2..=8 => dl.i8_align.abi,
+ 9..=16 => dl.i16_align.abi,
+ 17..=32 => dl.i32_align.abi,
+ 33..=64 => dl.i64_align.abi,
+ 65..=128 => dl.i128_align.abi,
+ _ => panic!("unsupported integer: {:?}", self),
+ },
+ RegKind::Float => match self.size.bits() {
+ 32 => dl.f32_align.abi,
+ 64 => dl.f64_align.abi,
+ _ => panic!("unsupported float: {:?}", self),
+ },
+ RegKind::Vector => dl.vector_align(self.size).abi,
+ }
+ }
+}
+
+/// An argument passed entirely registers with the
+/// same kind (e.g., HFA / HVA on PPC64 and AArch64).
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct Uniform {
+ pub unit: Reg,
+
+ /// The total size of the argument, which can be:
+ /// * equal to `unit.size` (one scalar/vector),
+ /// * a multiple of `unit.size` (an array of scalar/vectors),
+ /// * if `unit.kind` is `Integer`, the last element
+ /// can be shorter, i.e., `{ i64, i64, i32 }` for
+ /// 64-bit integers with a total size of 20 bytes.
+ pub total: Size,
+}
+
+impl From<Reg> for Uniform {
+ fn from(unit: Reg) -> Uniform {
+ Uniform { unit, total: unit.size }
+ }
+}
+
+impl Uniform {
+ pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
+ self.unit.align(cx)
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct CastTarget {
+ pub prefix: [Option<Reg>; 8],
+ pub rest: Uniform,
+ pub attrs: ArgAttributes,
+}
+
+impl From<Reg> for CastTarget {
+ fn from(unit: Reg) -> CastTarget {
+ CastTarget::from(Uniform::from(unit))
+ }
+}
+
+impl From<Uniform> for CastTarget {
+ fn from(uniform: Uniform) -> CastTarget {
+ CastTarget {
+ prefix: [None; 8],
+ rest: uniform,
+ attrs: ArgAttributes {
+ regular: ArgAttribute::default(),
+ arg_ext: ArgExtension::None,
+ pointee_size: Size::ZERO,
+ pointee_align: None,
+ },
+ }
+ }
+}
+
+impl CastTarget {
+ pub fn pair(a: Reg, b: Reg) -> CastTarget {
+ CastTarget {
+ prefix: [Some(a), None, None, None, None, None, None, None],
+ rest: Uniform::from(b),
+ attrs: ArgAttributes {
+ regular: ArgAttribute::default(),
+ arg_ext: ArgExtension::None,
+ pointee_size: Size::ZERO,
+ pointee_align: None,
+ },
+ }
+ }
+
+ pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size {
+ let mut size = self.rest.total;
+ for i in 0..self.prefix.iter().count() {
+ match self.prefix[i] {
+ Some(v) => size += Size { raw: v.size.bytes() },
+ None => {}
+ }
+ }
+ return size;
+ }
+
+ pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
+ self.prefix
+ .iter()
+ .filter_map(|x| x.map(|reg| reg.align(cx)))
+ .fold(cx.data_layout().aggregate_align.abi.max(self.rest.align(cx)), |acc, align| {
+ acc.max(align)
+ })
+ }
+}
+
+/// Return value from the `homogeneous_aggregate` test function.
+#[derive(Copy, Clone, Debug)]
+pub enum HomogeneousAggregate {
+ /// Yes, all the "leaf fields" of this struct are passed in the
+ /// same way (specified in the `Reg` value).
+ Homogeneous(Reg),
+
+ /// There are no leaf fields at all.
+ NoData,
+}
+
+/// Error from the `homogeneous_aggregate` test function, indicating
+/// there are distinct leaf fields passed in different ways,
+/// or this is uninhabited.
+#[derive(Copy, Clone, Debug)]
+pub struct Heterogeneous;
+
+impl HomogeneousAggregate {
+ /// If this is a homogeneous aggregate, returns the homogeneous
+ /// unit, else `None`.
+ pub fn unit(self) -> Option<Reg> {
+ match self {
+ HomogeneousAggregate::Homogeneous(reg) => Some(reg),
+ HomogeneousAggregate::NoData => None,
+ }
+ }
+
+ /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
+ /// the same `struct`. Only succeeds if only one of them has any data,
+ /// or both units are identical.
+ fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
+ match (self, other) {
+ (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
+
+ (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
+ if a != b {
+ return Err(Heterogeneous);
+ }
+ Ok(self)
+ }
+ }
+ }
+}
+
+impl<'a, Ty> TyAndLayout<'a, Ty> {
+ fn is_aggregate(&self) -> bool {
+ match self.abi {
+ Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
+ Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
+ }
+ }
+
+ /// Returns `Homogeneous` if this layout is an aggregate containing fields of
+ /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
+ /// special-cased in ABIs.
+ ///
+ /// Note: We generally ignore fields of zero-sized type when computing
+ /// this value (see #56877).
+ ///
+ /// This is public so that it can be used in unit tests, but
+ /// should generally only be relevant to the ABI details of
+ /// specific targets.
+ pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
+ where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ {
+ match self.abi {
+ Abi::Uninhabited => Err(Heterogeneous),
+
+ // The primitive for this algorithm.
+ Abi::Scalar(scalar) => {
+ let kind = match scalar.primitive() {
+ abi::Int(..) | abi::Pointer => RegKind::Integer,
+ abi::F32 | abi::F64 => RegKind::Float,
+ };
+ Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
+ }
+
+ Abi::Vector { .. } => {
+ assert!(!self.is_zst());
+ Ok(HomogeneousAggregate::Homogeneous(Reg {
+ kind: RegKind::Vector,
+ size: self.size,
+ }))
+ }
+
+ Abi::ScalarPair(..) | Abi::Aggregate { .. } => {
+ // Helper for computing `homogeneous_aggregate`, allowing a custom
+ // starting offset (used below for handling variants).
+ let from_fields_at =
+ |layout: Self,
+ start: Size|
+ -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
+ let is_union = match layout.fields {
+ FieldsShape::Primitive => {
+ unreachable!("aggregates can't have `FieldsShape::Primitive`")
+ }
+ FieldsShape::Array { count, .. } => {
+ assert_eq!(start, Size::ZERO);
+
+ let result = if count > 0 {
+ layout.field(cx, 0).homogeneous_aggregate(cx)?
+ } else {
+ HomogeneousAggregate::NoData
+ };
+ return Ok((result, layout.size));
+ }
+ FieldsShape::Union(_) => true,
+ FieldsShape::Arbitrary { .. } => false,
+ };
+
+ let mut result = HomogeneousAggregate::NoData;
+ let mut total = start;
+
+ for i in 0..layout.fields.count() {
+ if !is_union && total != layout.fields.offset(i) {
+ return Err(Heterogeneous);
+ }
+
+ let field = layout.field(cx, i);
+
+ result = result.merge(field.homogeneous_aggregate(cx)?)?;
+
+ // Keep track of the offset (without padding).
+ let size = field.size;
+ if is_union {
+ total = total.max(size);
+ } else {
+ total += size;
+ }
+ }
+
+ Ok((result, total))
+ };
+
+ let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
+
+ match &self.variants {
+ abi::Variants::Single { .. } => {}
+ abi::Variants::Multiple { variants, .. } => {
+ // Treat enum variants like union members.
+ // HACK(eddyb) pretend the `enum` field (discriminant)
+ // is at the start of every variant (otherwise the gap
+ // at the start of all variants would disqualify them).
+ //
+ // NB: for all tagged `enum`s (which include all non-C-like
+ // `enum`s with defined FFI representation), this will
+ // match the homogeneous computation on the equivalent
+ // `struct { tag; union { variant1; ... } }` and/or
+ // `union { struct { tag; variant1; } ... }`
+ // (the offsets of variant fields should be identical
+ // between the two for either to be a homogeneous aggregate).
+ let variant_start = total;
+ for variant_idx in variants.indices() {
+ let (variant_result, variant_total) =
+ from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
+
+ result = result.merge(variant_result)?;
+ total = total.max(variant_total);
+ }
+ }
+ }
+
+ // There needs to be no padding.
+ if total != self.size {
+ Err(Heterogeneous)
+ } else {
+ match result {
+ HomogeneousAggregate::Homogeneous(_) => {
+ assert_ne!(total, Size::ZERO);
+ }
+ HomogeneousAggregate::NoData => {
+ assert_eq!(total, Size::ZERO);
+ }
+ }
+ Ok(result)
+ }
+ }
+ }
+ }
+}
+
+/// Information about how to pass an argument to,
+/// or return a value from, a function, under some ABI.
+#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct ArgAbi<'a, Ty> {
+ pub layout: TyAndLayout<'a, Ty>,
+
+ /// Dummy argument, which is emitted before the real argument.
+ pub pad: Option<Reg>,
+
+ pub mode: PassMode,
+}
+
+impl<'a, Ty> ArgAbi<'a, Ty> {
+ pub fn new(
+ cx: &impl HasDataLayout,
+ layout: TyAndLayout<'a, Ty>,
+ scalar_attrs: impl Fn(&TyAndLayout<'a, Ty>, abi::Scalar, Size) -> ArgAttributes,
+ ) -> Self {
+ let mode = match layout.abi {
+ Abi::Uninhabited => PassMode::Ignore,
+ Abi::Scalar(scalar) => PassMode::Direct(scalar_attrs(&layout, scalar, Size::ZERO)),
+ Abi::ScalarPair(a, b) => PassMode::Pair(
+ scalar_attrs(&layout, a, Size::ZERO),
+ scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)),
+ ),
+ Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()),
+ Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()),
+ };
+ ArgAbi { layout, pad: None, mode }
+ }
+
+ fn indirect_pass_mode(layout: &TyAndLayout<'a, Ty>) -> PassMode {
+ let mut attrs = ArgAttributes::new();
+
+ // For non-immediate arguments the callee gets its own copy of
+ // the value on the stack, so there are no aliases. It's also
+ // program-invisible so can't possibly capture
+ attrs
+ .set(ArgAttribute::NoAlias)
+ .set(ArgAttribute::NoCapture)
+ .set(ArgAttribute::NonNull)
+ .set(ArgAttribute::NoUndef);
+ attrs.pointee_size = layout.size;
+ // FIXME(eddyb) We should be doing this, but at least on
+ // i686-pc-windows-msvc, it results in wrong stack offsets.
+ // attrs.pointee_align = Some(layout.align.abi);
+
+ let extra_attrs = layout.is_unsized().then_some(ArgAttributes::new());
+
+ PassMode::Indirect { attrs, extra_attrs, on_stack: false }
+ }
+
+ pub fn make_indirect(&mut self) {
+ match self.mode {
+ PassMode::Direct(_) | PassMode::Pair(_, _) => {}
+ PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: false } => return,
+ _ => panic!("Tried to make {:?} indirect", self.mode),
+ }
+
+ self.mode = Self::indirect_pass_mode(&self.layout);
+ }
+
+ pub fn make_indirect_byval(&mut self) {
+ self.make_indirect();
+ match self.mode {
+ PassMode::Indirect { attrs: _, extra_attrs: _, ref mut on_stack } => {
+ *on_stack = true;
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ pub fn extend_integer_width_to(&mut self, bits: u64) {
+ // Only integers have signedness
+ if let Abi::Scalar(scalar) = self.layout.abi {
+ if let abi::Int(i, signed) = scalar.primitive() {
+ if i.size().bits() < bits {
+ if let PassMode::Direct(ref mut attrs) = self.mode {
+ if signed {
+ attrs.ext(ArgExtension::Sext)
+ } else {
+ attrs.ext(ArgExtension::Zext)
+ };
+ }
+ }
+ }
+ }
+ }
+
+ pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) {
+ self.mode = PassMode::Cast(target.into());
+ }
+
+ pub fn pad_with(&mut self, reg: Reg) {
+ self.pad = Some(reg);
+ }
+
+ pub fn is_indirect(&self) -> bool {
+ matches!(self.mode, PassMode::Indirect { .. })
+ }
+
+ pub fn is_sized_indirect(&self) -> bool {
+ matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ })
+ }
+
+ pub fn is_unsized_indirect(&self) -> bool {
+ matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ })
+ }
+
+ pub fn is_ignore(&self) -> bool {
+ matches!(self.mode, PassMode::Ignore)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum Conv {
+ // General language calling conventions, for which every target
+ // should have its own backend (e.g. LLVM) support.
+ C,
+ Rust,
+
+ /// For things unlikely to be called, where smaller caller codegen is
+ /// preferred over raw speed.
+ /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
+ RustCold,
+
+ // Target-specific calling conventions.
+ ArmAapcs,
+ CCmseNonSecureCall,
+
+ Msp430Intr,
+
+ PtxKernel,
+
+ X86Fastcall,
+ X86Intr,
+ X86Stdcall,
+ X86ThisCall,
+ X86VectorCall,
+
+ X86_64SysV,
+ X86_64Win64,
+
+ AmdGpuKernel,
+ AvrInterrupt,
+ AvrNonBlockingInterrupt,
+}
+
+/// Metadata describing how the arguments to a native function
+/// should be passed in order to respect the native ABI.
+///
+/// I will do my best to describe this structure, but these
+/// comments are reverse-engineered and may be inaccurate. -NDM
+#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct FnAbi<'a, Ty> {
+ /// The LLVM types of each argument.
+ pub args: Vec<ArgAbi<'a, Ty>>,
+
+ /// LLVM return type.
+ pub ret: ArgAbi<'a, Ty>,
+
+ pub c_variadic: bool,
+
+ /// The count of non-variadic arguments.
+ ///
+ /// Should only be different from args.len() when c_variadic is true.
+ /// This can be used to know whether an argument is variadic or not.
+ pub fixed_count: usize,
+
+ pub conv: Conv,
+
+ pub can_unwind: bool,
+}
+
+/// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI.
+#[derive(Copy, Clone, Debug, HashStable_Generic)]
+pub enum AdjustForForeignAbiError {
+ /// Target architecture doesn't support "foreign" (i.e. non-Rust) ABIs.
+ Unsupported { arch: Symbol, abi: spec::abi::Abi },
+}
+
+impl fmt::Display for AdjustForForeignAbiError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Unsupported { arch, abi } => {
+ write!(f, "target architecture {:?} does not support `extern {}` ABI", arch, abi)
+ }
+ }
+ }
+}
+
+impl<'a, Ty> FnAbi<'a, Ty> {
+ pub fn adjust_for_foreign_abi<C>(
+ &mut self,
+ cx: &C,
+ abi: spec::abi::Abi,
+ ) -> Result<(), AdjustForForeignAbiError>
+ where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout + HasTargetSpec,
+ {
+ if abi == spec::abi::Abi::X86Interrupt {
+ if let Some(arg) = self.args.first_mut() {
+ arg.make_indirect_byval();
+ }
+ return Ok(());
+ }
+
+ match &cx.target_spec().arch[..] {
+ "x86" => {
+ let flavor = if let spec::abi::Abi::Fastcall { .. }
+ | spec::abi::Abi::Vectorcall { .. } = abi
+ {
+ x86::Flavor::FastcallOrVectorcall
+ } else {
+ x86::Flavor::General
+ };
+ x86::compute_abi_info(cx, self, flavor);
+ }
+ "x86_64" => match abi {
+ spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),
+ spec::abi::Abi::Win64 { .. } => x86_win64::compute_abi_info(self),
+ _ => {
+ if cx.target_spec().is_like_windows {
+ x86_win64::compute_abi_info(self)
+ } else {
+ x86_64::compute_abi_info(cx, self)
+ }
+ }
+ },
+ "aarch64" => aarch64::compute_abi_info(cx, self),
+ "amdgpu" => amdgpu::compute_abi_info(cx, self),
+ "arm" => arm::compute_abi_info(cx, self),
+ "avr" => avr::compute_abi_info(self),
+ "m68k" => m68k::compute_abi_info(self),
+ "mips" => mips::compute_abi_info(cx, self),
+ "mips64" => mips64::compute_abi_info(cx, self),
+ "powerpc" => powerpc::compute_abi_info(self),
+ "powerpc64" => powerpc64::compute_abi_info(cx, self),
+ "s390x" => s390x::compute_abi_info(cx, self),
+ "msp430" => msp430::compute_abi_info(self),
+ "sparc" => sparc::compute_abi_info(cx, self),
+ "sparc64" => sparc64::compute_abi_info(cx, self),
+ "nvptx" => nvptx::compute_abi_info(self),
+ "nvptx64" => {
+ if cx.target_spec().adjust_abi(abi) == spec::abi::Abi::PtxKernel {
+ nvptx64::compute_ptx_kernel_abi_info(cx, self)
+ } else {
+ nvptx64::compute_abi_info(self)
+ }
+ }
+ "hexagon" => hexagon::compute_abi_info(self),
+ "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
+ "wasm32" | "wasm64" => {
+ if cx.target_spec().adjust_abi(abi) == spec::abi::Abi::Wasm {
+ wasm::compute_wasm_abi_info(self)
+ } else {
+ wasm::compute_c_abi_info(cx, self)
+ }
+ }
+ "asmjs" => wasm::compute_c_abi_info(cx, self),
+ "bpf" => bpf::compute_abi_info(self),
+ arch => {
+ return Err(AdjustForForeignAbiError::Unsupported {
+ arch: Symbol::intern(arch),
+ abi,
+ });
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/msp430.rs b/compiler/rustc_target/src/abi/call/msp430.rs
new file mode 100644
index 000000000..0ba73657b
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/msp430.rs
@@ -0,0 +1,39 @@
+// Reference: MSP430 Embedded Application Binary Interface
+// https://www.ti.com/lit/an/slaa534a/slaa534a.pdf
+
+use crate::abi::call::{ArgAbi, FnAbi};
+
+// 3.5 Structures or Unions Passed and Returned by Reference
+//
+// "Structures (including classes) and unions larger than 32 bits are passed and
+// returned by reference. To pass a structure or union by reference, the caller
+// places its address in the appropriate location: either in a register or on
+// the stack, according to its position in the argument list. (..)"
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() && ret.layout.size.bits() > 32 {
+ ret.make_indirect();
+ } else {
+ ret.extend_integer_width_to(16);
+ }
+}
+
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() && arg.layout.size.bits() > 32 {
+ arg.make_indirect();
+ } else {
+ arg.extend_integer_width_to(16);
+ }
+}
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/nvptx.rs b/compiler/rustc_target/src/abi/call/nvptx.rs
new file mode 100644
index 000000000..428dd95bb
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/nvptx.rs
@@ -0,0 +1,33 @@
+// Reference: PTX Writer's Guide to Interoperability
+// https://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability
+
+use crate::abi::call::{ArgAbi, FnAbi};
+
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() && ret.layout.size.bits() > 32 {
+ ret.make_indirect();
+ } else {
+ ret.extend_integer_width_to(32);
+ }
+}
+
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() && arg.layout.size.bits() > 32 {
+ arg.make_indirect();
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+}
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs
new file mode 100644
index 000000000..fc16f1c97
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/nvptx64.rs
@@ -0,0 +1,64 @@
+use crate::abi::call::{ArgAbi, FnAbi, PassMode, Reg, Size, Uniform};
+use crate::abi::{HasDataLayout, TyAbiInterface};
+
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 {
+ ret.make_indirect();
+ }
+}
+
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 {
+ arg.make_indirect();
+ }
+}
+
+fn classify_arg_kernel<'a, Ty, C>(_cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if matches!(arg.mode, PassMode::Pair(..)) && (arg.layout.is_adt() || arg.layout.is_tuple()) {
+ let align_bytes = arg.layout.align.abi.bytes();
+
+ let unit = match align_bytes {
+ 1 => Reg::i8(),
+ 2 => Reg::i16(),
+ 4 => Reg::i32(),
+ 8 => Reg::i64(),
+ 16 => Reg::i128(),
+ _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"),
+ };
+ arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) });
+ }
+}
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+}
+
+pub fn compute_ptx_kernel_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.layout.is_unit() && !fn_abi.ret.layout.is_never() {
+ panic!("Kernels should not return anything other than () or !");
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg_kernel(cx, arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/powerpc.rs b/compiler/rustc_target/src/abi/call/powerpc.rs
new file mode 100644
index 000000000..27a5c6d2f
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/powerpc.rs
@@ -0,0 +1,30 @@
+use crate::abi::call::{ArgAbi, FnAbi};
+
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if ret.layout.is_aggregate() {
+ ret.make_indirect();
+ } else {
+ ret.extend_integer_width_to(32);
+ }
+}
+
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ if arg.layout.is_aggregate() {
+ arg.make_indirect();
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+}
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs
new file mode 100644
index 000000000..c22ef9c8f
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/powerpc64.rs
@@ -0,0 +1,141 @@
+// FIXME:
+// Alignment of 128 bit types is not currently handled, this will
+// need to be fixed when PowerPC vector support is added.
+
+use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
+use crate::abi::{Endian, HasDataLayout, TyAbiInterface};
+use crate::spec::HasTargetSpec;
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum ABI {
+ ELFv1, // original ABI used for powerpc64 (big-endian)
+ ELFv2, // newer ABI used for powerpc64le and musl (both endians)
+}
+use ABI::*;
+
+fn is_homogeneous_aggregate<'a, Ty, C>(
+ cx: &C,
+ arg: &mut ArgAbi<'a, Ty>,
+ abi: ABI,
+) -> Option<Uniform>
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
+ // ELFv1 only passes one-member aggregates transparently.
+ // ELFv2 passes up to eight uniquely addressable members.
+ if (abi == ELFv1 && arg.layout.size > unit.size)
+ || arg.layout.size > unit.size.checked_mul(8, cx).unwrap()
+ {
+ return None;
+ }
+
+ let valid_unit = match unit.kind {
+ RegKind::Integer => false,
+ RegKind::Float => true,
+ RegKind::Vector => arg.layout.size.bits() == 128,
+ };
+
+ valid_unit.then_some(Uniform { unit, total: arg.layout.size })
+ })
+}
+
+fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, abi: ABI)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !ret.layout.is_aggregate() {
+ ret.extend_integer_width_to(64);
+ return;
+ }
+
+ // The ELFv1 ABI doesn't return aggregates in registers
+ if abi == ELFv1 {
+ ret.make_indirect();
+ return;
+ }
+
+ if let Some(uniform) = is_homogeneous_aggregate(cx, ret, abi) {
+ ret.cast_to(uniform);
+ return;
+ }
+
+ let size = ret.layout.size;
+ let bits = size.bits();
+ if bits <= 128 {
+ let unit = if cx.data_layout().endian == Endian::Big {
+ Reg { kind: RegKind::Integer, size }
+ } else if bits <= 8 {
+ Reg::i8()
+ } else if bits <= 16 {
+ Reg::i16()
+ } else if bits <= 32 {
+ Reg::i32()
+ } else {
+ Reg::i64()
+ };
+
+ ret.cast_to(Uniform { unit, total: size });
+ return;
+ }
+
+ ret.make_indirect();
+}
+
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi: ABI)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !arg.layout.is_aggregate() {
+ arg.extend_integer_width_to(64);
+ return;
+ }
+
+ if let Some(uniform) = is_homogeneous_aggregate(cx, arg, abi) {
+ arg.cast_to(uniform);
+ return;
+ }
+
+ let size = arg.layout.size;
+ let (unit, total) = if size.bits() <= 64 {
+ // Aggregates smaller than a doubleword should appear in
+ // the least-significant bits of the parameter doubleword.
+ (Reg { kind: RegKind::Integer, size }, size)
+ } else {
+ // Aggregates larger than a doubleword should be padded
+ // at the tail to fill out a whole number of doublewords.
+ let reg_i64 = Reg::i64();
+ (reg_i64, size.align_to(reg_i64.align(cx)))
+ };
+
+ arg.cast_to(Uniform { unit, total });
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout + HasTargetSpec,
+{
+ let abi = if cx.target_spec().env == "musl" {
+ ELFv2
+ } else {
+ match cx.data_layout().endian {
+ Endian::Big => ELFv1,
+ Endian::Little => ELFv2,
+ }
+ };
+
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret, abi);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg, abi);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs
new file mode 100644
index 000000000..752b44f64
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/riscv.rs
@@ -0,0 +1,348 @@
+// Reference: RISC-V ELF psABI specification
+// https://github.com/riscv/riscv-elf-psabi-doc
+//
+// Reference: Clang RISC-V ELF psABI lowering code
+// https://github.com/llvm/llvm-project/blob/8e780252a7284be45cf1ba224cabd884847e8e92/clang/lib/CodeGen/TargetInfo.cpp#L9311-L9773
+
+use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
+use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
+use crate::spec::HasTargetSpec;
+
+#[derive(Copy, Clone)]
+enum RegPassKind {
+ Float(Reg),
+ Integer(Reg),
+ Unknown,
+}
+
+#[derive(Copy, Clone)]
+enum FloatConv {
+ FloatPair(Reg, Reg),
+ Float(Reg),
+ MixedPair(Reg, Reg),
+}
+
+#[derive(Copy, Clone)]
+struct CannotUseFpConv;
+
+fn is_riscv_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool {
+ match arg.layout.abi {
+ Abi::Vector { .. } => true,
+ _ => arg.layout.is_aggregate(),
+ }
+}
+
+fn should_use_fp_conv_helper<'a, Ty, C>(
+ cx: &C,
+ arg_layout: &TyAndLayout<'a, Ty>,
+ xlen: u64,
+ flen: u64,
+ field1_kind: &mut RegPassKind,
+ field2_kind: &mut RegPassKind,
+) -> Result<(), CannotUseFpConv>
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+{
+ match arg_layout.abi {
+ Abi::Scalar(scalar) => match scalar.primitive() {
+ abi::Int(..) | abi::Pointer => {
+ if arg_layout.size.bits() > xlen {
+ return Err(CannotUseFpConv);
+ }
+ match (*field1_kind, *field2_kind) {
+ (RegPassKind::Unknown, _) => {
+ *field1_kind = RegPassKind::Integer(Reg {
+ kind: RegKind::Integer,
+ size: arg_layout.size,
+ });
+ }
+ (RegPassKind::Float(_), RegPassKind::Unknown) => {
+ *field2_kind = RegPassKind::Integer(Reg {
+ kind: RegKind::Integer,
+ size: arg_layout.size,
+ });
+ }
+ _ => return Err(CannotUseFpConv),
+ }
+ }
+ abi::F32 | abi::F64 => {
+ if arg_layout.size.bits() > flen {
+ return Err(CannotUseFpConv);
+ }
+ match (*field1_kind, *field2_kind) {
+ (RegPassKind::Unknown, _) => {
+ *field1_kind =
+ RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
+ }
+ (_, RegPassKind::Unknown) => {
+ *field2_kind =
+ RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
+ }
+ _ => return Err(CannotUseFpConv),
+ }
+ }
+ },
+ Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv),
+ Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields {
+ FieldsShape::Primitive => {
+ unreachable!("aggregates can't have `FieldsShape::Primitive`")
+ }
+ FieldsShape::Union(_) => {
+ if !arg_layout.is_zst() {
+ return Err(CannotUseFpConv);
+ }
+ }
+ FieldsShape::Array { count, .. } => {
+ for _ in 0..count {
+ let elem_layout = arg_layout.field(cx, 0);
+ should_use_fp_conv_helper(
+ cx,
+ &elem_layout,
+ xlen,
+ flen,
+ field1_kind,
+ field2_kind,
+ )?;
+ }
+ }
+ FieldsShape::Arbitrary { .. } => {
+ match arg_layout.variants {
+ abi::Variants::Multiple { .. } => return Err(CannotUseFpConv),
+ abi::Variants::Single { .. } => (),
+ }
+ for i in arg_layout.fields.index_by_increasing_offset() {
+ let field = arg_layout.field(cx, i);
+ should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?;
+ }
+ }
+ },
+ }
+ Ok(())
+}
+
+fn should_use_fp_conv<'a, Ty, C>(
+ cx: &C,
+ arg: &TyAndLayout<'a, Ty>,
+ xlen: u64,
+ flen: u64,
+) -> Option<FloatConv>
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+{
+ let mut field1_kind = RegPassKind::Unknown;
+ let mut field2_kind = RegPassKind::Unknown;
+ if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() {
+ return None;
+ }
+ match (field1_kind, field2_kind) {
+ (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)),
+ (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)),
+ (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)),
+ (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)),
+ _ => None,
+ }
+}
+
+fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+{
+ if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) {
+ match conv {
+ FloatConv::Float(f) => {
+ arg.cast_to(f);
+ }
+ FloatConv::FloatPair(l, r) => {
+ arg.cast_to(CastTarget::pair(l, r));
+ }
+ FloatConv::MixedPair(l, r) => {
+ arg.cast_to(CastTarget::pair(l, r));
+ }
+ }
+ return false;
+ }
+
+ let total = arg.layout.size;
+
+ // "Scalars wider than 2✕XLEN are passed by reference and are replaced in
+ // the argument list with the address."
+ // "Aggregates larger than 2✕XLEN bits are passed by reference and are
+ // replaced in the argument list with the address, as are C++ aggregates
+ // with nontrivial copy constructors, destructors, or vtables."
+ if total.bits() > 2 * xlen {
+ // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN.
+ if is_riscv_aggregate(arg) {
+ arg.make_indirect();
+ }
+ return true;
+ }
+
+ let xlen_reg = match xlen {
+ 32 => Reg::i32(),
+ 64 => Reg::i64(),
+ _ => unreachable!("Unsupported XLEN: {}", xlen),
+ };
+ if is_riscv_aggregate(arg) {
+ if total.bits() <= xlen {
+ arg.cast_to(xlen_reg);
+ } else {
+ arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) });
+ }
+ return false;
+ }
+
+ // "When passed in registers, scalars narrower than XLEN bits are widened
+ // according to the sign of their type up to 32 bits, then sign-extended to
+ // XLEN bits."
+ extend_integer_width(arg, xlen);
+ false
+}
+
+fn classify_arg<'a, Ty, C>(
+ cx: &C,
+ arg: &mut ArgAbi<'a, Ty>,
+ xlen: u64,
+ flen: u64,
+ is_vararg: bool,
+ avail_gprs: &mut u64,
+ avail_fprs: &mut u64,
+) where
+ Ty: TyAbiInterface<'a, C> + Copy,
+{
+ if !is_vararg {
+ match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
+ Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {
+ *avail_fprs -= 1;
+ arg.cast_to(f);
+ return;
+ }
+ Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => {
+ *avail_fprs -= 2;
+ arg.cast_to(CastTarget::pair(l, r));
+ return;
+ }
+ Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => {
+ *avail_gprs -= 1;
+ *avail_fprs -= 1;
+ arg.cast_to(CastTarget::pair(l, r));
+ return;
+ }
+ _ => (),
+ }
+ }
+
+ let total = arg.layout.size;
+ let align = arg.layout.align.abi.bits();
+
+ // "Scalars wider than 2✕XLEN are passed by reference and are replaced in
+ // the argument list with the address."
+ // "Aggregates larger than 2✕XLEN bits are passed by reference and are
+ // replaced in the argument list with the address, as are C++ aggregates
+ // with nontrivial copy constructors, destructors, or vtables."
+ if total.bits() > 2 * xlen {
+ // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN.
+ if is_riscv_aggregate(arg) {
+ arg.make_indirect();
+ }
+ if *avail_gprs >= 1 {
+ *avail_gprs -= 1;
+ }
+ return;
+ }
+
+ let double_xlen_reg = match xlen {
+ 32 => Reg::i64(),
+ 64 => Reg::i128(),
+ _ => unreachable!("Unsupported XLEN: {}", xlen),
+ };
+
+ let xlen_reg = match xlen {
+ 32 => Reg::i32(),
+ 64 => Reg::i64(),
+ _ => unreachable!("Unsupported XLEN: {}", xlen),
+ };
+
+ if total.bits() > xlen {
+ let align_regs = align > xlen;
+ if is_riscv_aggregate(arg) {
+ arg.cast_to(Uniform {
+ unit: if align_regs { double_xlen_reg } else { xlen_reg },
+ total: Size::from_bits(xlen * 2),
+ });
+ }
+ if align_regs && is_vararg {
+ *avail_gprs -= *avail_gprs % 2;
+ }
+ if *avail_gprs >= 2 {
+ *avail_gprs -= 2;
+ } else {
+ *avail_gprs = 0;
+ }
+ return;
+ } else if is_riscv_aggregate(arg) {
+ arg.cast_to(xlen_reg);
+ if *avail_gprs >= 1 {
+ *avail_gprs -= 1;
+ }
+ return;
+ }
+
+ // "When passed in registers, scalars narrower than XLEN bits are widened
+ // according to the sign of their type up to 32 bits, then sign-extended to
+ // XLEN bits."
+ if *avail_gprs >= 1 {
+ extend_integer_width(arg, xlen);
+ *avail_gprs -= 1;
+ }
+}
+
+fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) {
+ if let Abi::Scalar(scalar) = arg.layout.abi {
+ if let abi::Int(i, _) = scalar.primitive() {
+ // 32-bit integers are always sign-extended
+ if i.size().bits() == 32 && xlen > 32 {
+ if let PassMode::Direct(ref mut attrs) = arg.mode {
+ attrs.ext(ArgExtension::Sext);
+ return;
+ }
+ }
+ }
+ }
+
+ arg.extend_integer_width_to(xlen);
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout + HasTargetSpec,
+{
+ let flen = match &cx.target_spec().llvm_abiname[..] {
+ "ilp32f" | "lp64f" => 32,
+ "ilp32d" | "lp64d" => 64,
+ _ => 0,
+ };
+ let xlen = cx.data_layout().pointer_size.bits();
+
+ let mut avail_gprs = 8;
+ let mut avail_fprs = 8;
+
+ if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) {
+ avail_gprs -= 1;
+ }
+
+ for (i, arg) in fn_abi.args.iter_mut().enumerate() {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(
+ cx,
+ arg,
+ xlen,
+ flen,
+ i >= fn_abi.fixed_count,
+ &mut avail_gprs,
+ &mut avail_fprs,
+ );
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/s390x.rs b/compiler/rustc_target/src/abi/call/s390x.rs
new file mode 100644
index 000000000..13706e8c2
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/s390x.rs
@@ -0,0 +1,57 @@
+// FIXME: The assumes we're using the non-vector ABI, i.e., compiling
+// for a pre-z13 machine or using -mno-vx.
+
+use crate::abi::call::{ArgAbi, FnAbi, Reg};
+use crate::abi::{HasDataLayout, TyAbiInterface};
+
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 {
+ ret.extend_integer_width_to(64);
+ } else {
+ ret.make_indirect();
+ }
+}
+
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 {
+ arg.extend_integer_width_to(64);
+ return;
+ }
+
+ if arg.layout.is_single_fp_element(cx) {
+ match arg.layout.size.bytes() {
+ 4 => arg.cast_to(Reg::f32()),
+ 8 => arg.cast_to(Reg::f64()),
+ _ => arg.make_indirect(),
+ }
+ } else {
+ match arg.layout.size.bytes() {
+ 1 => arg.cast_to(Reg::i8()),
+ 2 => arg.cast_to(Reg::i16()),
+ 4 => arg.cast_to(Reg::i32()),
+ 8 => arg.cast_to(Reg::i64()),
+ _ => arg.make_indirect(),
+ }
+ }
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/sparc.rs b/compiler/rustc_target/src/abi/call/sparc.rs
new file mode 100644
index 000000000..cc4431976
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/sparc.rs
@@ -0,0 +1,51 @@
+use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform};
+use crate::abi::{HasDataLayout, Size};
+
+fn classify_ret<Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size)
+where
+ C: HasDataLayout,
+{
+ if !ret.layout.is_aggregate() {
+ ret.extend_integer_width_to(32);
+ } else {
+ ret.make_indirect();
+ *offset += cx.data_layout().pointer_size;
+ }
+}
+
+fn classify_arg<Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
+where
+ C: HasDataLayout,
+{
+ let dl = cx.data_layout();
+ let size = arg.layout.size;
+ let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi;
+
+ if arg.layout.is_aggregate() {
+ arg.cast_to(Uniform { unit: Reg::i32(), total: size });
+ if !offset.is_aligned(align) {
+ arg.pad_with(Reg::i32());
+ }
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+
+ *offset = offset.align_to(align) + size.align_to(align);
+}
+
+pub fn compute_abi_info<Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>)
+where
+ C: HasDataLayout,
+{
+ let mut offset = Size::ZERO;
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret, &mut offset);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg, &mut offset);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs
new file mode 100644
index 000000000..cc3a0a699
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/sparc64.rs
@@ -0,0 +1,226 @@
+// FIXME: This needs an audit for correctness and completeness.
+
+use crate::abi::call::{
+ ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, Uniform,
+};
+use crate::abi::{self, HasDataLayout, Scalar, Size, TyAbiInterface, TyAndLayout};
+
+#[derive(Clone, Debug)]
+pub struct Sdata {
+ pub prefix: [Option<Reg>; 8],
+ pub prefix_index: usize,
+ pub last_offset: Size,
+ pub has_float: bool,
+ pub arg_attribute: ArgAttribute,
+}
+
+fn arg_scalar<C>(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata
+where
+ C: HasDataLayout,
+{
+ let dl = cx.data_layout();
+
+ if !scalar.primitive().is_float() {
+ return data;
+ }
+
+ data.has_float = true;
+
+ if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset {
+ if data.prefix_index == data.prefix.len() {
+ return data;
+ }
+ data.prefix[data.prefix_index] = Some(Reg::i32());
+ data.prefix_index += 1;
+ data.last_offset = data.last_offset + Reg::i32().size;
+ }
+
+ for _ in 0..((offset - data.last_offset).bits() / 64)
+ .min((data.prefix.len() - data.prefix_index) as u64)
+ {
+ data.prefix[data.prefix_index] = Some(Reg::i64());
+ data.prefix_index += 1;
+ data.last_offset = data.last_offset + Reg::i64().size;
+ }
+
+ if data.last_offset < offset {
+ if data.prefix_index == data.prefix.len() {
+ return data;
+ }
+ data.prefix[data.prefix_index] = Some(Reg::i32());
+ data.prefix_index += 1;
+ data.last_offset = data.last_offset + Reg::i32().size;
+ }
+
+ if data.prefix_index == data.prefix.len() {
+ return data;
+ }
+
+ if scalar.primitive() == abi::F32 {
+ data.arg_attribute = ArgAttribute::InReg;
+ data.prefix[data.prefix_index] = Some(Reg::f32());
+ data.last_offset = offset + Reg::f32().size;
+ } else {
+ data.prefix[data.prefix_index] = Some(Reg::f64());
+ data.last_offset = offset + Reg::f64().size;
+ }
+ data.prefix_index += 1;
+ return data;
+}
+
+fn arg_scalar_pair<C>(
+ cx: &C,
+ scalar1: &Scalar,
+ scalar2: &Scalar,
+ mut offset: Size,
+ mut data: Sdata,
+) -> Sdata
+where
+ C: HasDataLayout,
+{
+ data = arg_scalar(cx, &scalar1, offset, data);
+ match (scalar1.primitive(), scalar2.primitive()) {
+ (abi::F32, _) => offset += Reg::f32().size,
+ (_, abi::F64) => offset += Reg::f64().size,
+ (abi::Int(i, _signed), _) => offset += i.size(),
+ (abi::Pointer, _) => offset += Reg::i64().size,
+ _ => {}
+ }
+
+ if (offset.raw % 4) != 0 && scalar2.primitive().is_float() {
+ offset.raw += 4 - (offset.raw % 4);
+ }
+ data = arg_scalar(cx, &scalar2, offset, data);
+ return data;
+}
+
+fn parse_structure<'a, Ty, C>(
+ cx: &C,
+ layout: TyAndLayout<'a, Ty>,
+ mut data: Sdata,
+ mut offset: Size,
+) -> Sdata
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if let abi::FieldsShape::Union(_) = layout.fields {
+ return data;
+ }
+
+ match layout.abi {
+ abi::Abi::Scalar(scalar) => {
+ data = arg_scalar(cx, &scalar, offset, data);
+ }
+ abi::Abi::Aggregate { .. } => {
+ for i in 0..layout.fields.count() {
+ if offset < layout.fields.offset(i) {
+ offset = layout.fields.offset(i);
+ }
+ data = parse_structure(cx, layout.field(cx, i), data.clone(), offset);
+ }
+ }
+ _ => {
+ if let abi::Abi::ScalarPair(scalar1, scalar2) = &layout.abi {
+ data = arg_scalar_pair(cx, scalar1, scalar2, offset, data);
+ }
+ }
+ }
+
+ return data;
+}
+
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !arg.layout.is_aggregate() {
+ arg.extend_integer_width_to(64);
+ return;
+ }
+
+ let total = arg.layout.size;
+ if total > in_registers_max {
+ arg.make_indirect();
+ return;
+ }
+
+ match arg.layout.fields {
+ abi::FieldsShape::Primitive => unreachable!(),
+ abi::FieldsShape::Array { .. } => {
+ // Arrays are passed indirectly
+ arg.make_indirect();
+ return;
+ }
+ abi::FieldsShape::Union(_) => {
+ // Unions and are always treated as a series of 64-bit integer chunks
+ }
+ abi::FieldsShape::Arbitrary { .. } => {
+ // Structures with floating point numbers need special care.
+
+ let mut data = parse_structure(
+ cx,
+ arg.layout,
+ Sdata {
+ prefix: [None; 8],
+ prefix_index: 0,
+ last_offset: Size::ZERO,
+ has_float: false,
+ arg_attribute: ArgAttribute::default(),
+ },
+ Size { raw: 0 },
+ );
+
+ if data.has_float {
+ // Structure { float, int, int } doesn't like to be handled like
+ // { float, long int }. Other way around it doesn't mind.
+ if data.last_offset < arg.layout.size
+ && (data.last_offset.raw % 8) != 0
+ && data.prefix_index < data.prefix.len()
+ {
+ data.prefix[data.prefix_index] = Some(Reg::i32());
+ data.prefix_index += 1;
+ data.last_offset += Reg::i32().size;
+ }
+
+ let mut rest_size = arg.layout.size - data.last_offset;
+ if (rest_size.raw % 8) != 0 && data.prefix_index < data.prefix.len() {
+ data.prefix[data.prefix_index] = Some(Reg::i32());
+ rest_size = rest_size - Reg::i32().size;
+ }
+
+ arg.cast_to(CastTarget {
+ prefix: data.prefix,
+ rest: Uniform { unit: Reg::i64(), total: rest_size },
+ attrs: ArgAttributes {
+ regular: data.arg_attribute,
+ arg_ext: ArgExtension::None,
+ pointee_size: Size::ZERO,
+ pointee_align: None,
+ },
+ });
+ return;
+ }
+ }
+ }
+
+ arg.cast_to(Uniform { unit: Reg::i64(), total });
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.is_ignore() {
+ classify_arg(cx, &mut fn_abi.ret, Size { raw: 32 });
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg, Size { raw: 16 });
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs
new file mode 100644
index 000000000..3237cde10
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/wasm.rs
@@ -0,0 +1,83 @@
+use crate::abi::call::{ArgAbi, FnAbi, Uniform};
+use crate::abi::{HasDataLayout, TyAbiInterface};
+
+fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if val.layout.is_aggregate() {
+ if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) {
+ let size = val.layout.size;
+ if unit.size == size {
+ val.cast_to(Uniform { unit, total: size });
+ return true;
+ }
+ }
+ }
+ false
+}
+
+fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ ret.extend_integer_width_to(32);
+ if ret.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, ret) {
+ ret.make_indirect();
+ }
+}
+
+fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ arg.extend_integer_width_to(32);
+ if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) {
+ arg.make_indirect_byval();
+ }
+}
+
+/// The purpose of this ABI is to match the C ABI (aka clang) exactly.
+pub fn compute_c_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(cx, &mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(cx, arg);
+ }
+}
+
+/// The purpose of this ABI is for matching the WebAssembly standard. This
+/// intentionally diverges from the C ABI and is specifically crafted to take
+/// advantage of LLVM's support of multiple returns in WebAssembly.
+pub fn compute_wasm_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ if !fn_abi.ret.is_ignore() {
+ classify_ret(&mut fn_abi.ret);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg(arg);
+ }
+
+ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+ ret.extend_integer_width_to(32);
+ }
+
+ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
+ arg.extend_integer_width_to(32);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs
new file mode 100644
index 000000000..c7d59baf9
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/x86.rs
@@ -0,0 +1,117 @@
+use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind};
+use crate::abi::{HasDataLayout, TyAbiInterface};
+use crate::spec::HasTargetSpec;
+
+#[derive(PartialEq)]
+pub enum Flavor {
+ General,
+ FastcallOrVectorcall,
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout + HasTargetSpec,
+{
+ if !fn_abi.ret.is_ignore() {
+ if fn_abi.ret.layout.is_aggregate() {
+ // Returning a structure. Most often, this will use
+ // a hidden first argument. On some platforms, though,
+ // small structs are returned as integers.
+ //
+ // Some links:
+ // https://www.angelcode.com/dev/callconv/callconv.html
+ // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
+ let t = cx.target_spec();
+ if t.abi_return_struct_as_int {
+ // According to Clang, everyone but MSVC returns single-element
+ // float aggregates directly in a floating-point register.
+ if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) {
+ match fn_abi.ret.layout.size.bytes() {
+ 4 => fn_abi.ret.cast_to(Reg::f32()),
+ 8 => fn_abi.ret.cast_to(Reg::f64()),
+ _ => fn_abi.ret.make_indirect(),
+ }
+ } else {
+ match fn_abi.ret.layout.size.bytes() {
+ 1 => fn_abi.ret.cast_to(Reg::i8()),
+ 2 => fn_abi.ret.cast_to(Reg::i16()),
+ 4 => fn_abi.ret.cast_to(Reg::i32()),
+ 8 => fn_abi.ret.cast_to(Reg::i64()),
+ _ => fn_abi.ret.make_indirect(),
+ }
+ }
+ } else {
+ fn_abi.ret.make_indirect();
+ }
+ } else {
+ fn_abi.ret.extend_integer_width_to(32);
+ }
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ if arg.layout.is_aggregate() {
+ arg.make_indirect_byval();
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+ }
+
+ if flavor == Flavor::FastcallOrVectorcall {
+ // Mark arguments as InReg like clang does it,
+ // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
+
+ // Clang reference: lib/CodeGen/TargetInfo.cpp
+ // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs()
+
+ // IsSoftFloatABI is only set to true on ARM platforms,
+ // which in turn can't be x86?
+
+ let mut free_regs = 2;
+
+ for arg in &mut fn_abi.args {
+ let attrs = match arg.mode {
+ PassMode::Ignore
+ | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
+ continue;
+ }
+ PassMode::Direct(ref mut attrs) => attrs,
+ PassMode::Pair(..)
+ | PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ }
+ | PassMode::Cast(_) => {
+ unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
+ }
+ };
+
+ // At this point we know this must be a primitive of sorts.
+ let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
+ assert_eq!(unit.size, arg.layout.size);
+ if unit.kind == RegKind::Float {
+ continue;
+ }
+
+ let size_in_regs = (arg.layout.size.bits() + 31) / 32;
+
+ if size_in_regs == 0 {
+ continue;
+ }
+
+ if size_in_regs > free_regs {
+ break;
+ }
+
+ free_regs -= size_in_regs;
+
+ if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
+ attrs.set(ArgAttribute::InReg);
+ }
+
+ if free_regs == 0 {
+ break;
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs
new file mode 100644
index 000000000..a52e01a49
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/x86_64.rs
@@ -0,0 +1,248 @@
+// The classification code for the x86_64 ABI is taken from the clay language
+// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp
+
+use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind};
+use crate::abi::{self, Abi, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
+
+/// Classification of "eightbyte" components.
+// N.B., the order of the variants is from general to specific,
+// such that `unify(a, b)` is the "smaller" of `a` and `b`.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+enum Class {
+ Int,
+ Sse,
+ SseUp,
+}
+
+#[derive(Clone, Copy, Debug)]
+struct Memory;
+
+// Currently supported vector size (AVX-512).
+const LARGEST_VECTOR_SIZE: usize = 512;
+const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
+
+fn classify_arg<'a, Ty, C>(
+ cx: &C,
+ arg: &ArgAbi<'a, Ty>,
+) -> Result<[Option<Class>; MAX_EIGHTBYTES], Memory>
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ fn classify<'a, Ty, C>(
+ cx: &C,
+ layout: TyAndLayout<'a, Ty>,
+ cls: &mut [Option<Class>],
+ off: Size,
+ ) -> Result<(), Memory>
+ where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+ {
+ if !off.is_aligned(layout.align.abi) {
+ if !layout.is_zst() {
+ return Err(Memory);
+ }
+ return Ok(());
+ }
+
+ let mut c = match layout.abi {
+ Abi::Uninhabited => return Ok(()),
+
+ Abi::Scalar(scalar) => match scalar.primitive() {
+ abi::Int(..) | abi::Pointer => Class::Int,
+ abi::F32 | abi::F64 => Class::Sse,
+ },
+
+ Abi::Vector { .. } => Class::Sse,
+
+ Abi::ScalarPair(..) | Abi::Aggregate { .. } => {
+ for i in 0..layout.fields.count() {
+ let field_off = off + layout.fields.offset(i);
+ classify(cx, layout.field(cx, i), cls, field_off)?;
+ }
+
+ match &layout.variants {
+ abi::Variants::Single { .. } => {}
+ abi::Variants::Multiple { variants, .. } => {
+ // Treat enum variants like union members.
+ for variant_idx in variants.indices() {
+ classify(cx, layout.for_variant(cx, variant_idx), cls, off)?;
+ }
+ }
+ }
+
+ return Ok(());
+ }
+ };
+
+ // Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
+ let first = (off.bytes() / 8) as usize;
+ let last = ((off.bytes() + layout.size.bytes() - 1) / 8) as usize;
+ for cls in &mut cls[first..=last] {
+ *cls = Some(cls.map_or(c, |old| old.min(c)));
+
+ // Everything after the first Sse "eightbyte"
+ // component is the upper half of a register.
+ if c == Class::Sse {
+ c = Class::SseUp;
+ }
+ }
+
+ Ok(())
+ }
+
+ let n = ((arg.layout.size.bytes() + 7) / 8) as usize;
+ if n > MAX_EIGHTBYTES {
+ return Err(Memory);
+ }
+
+ let mut cls = [None; MAX_EIGHTBYTES];
+ classify(cx, arg.layout, &mut cls, Size::ZERO)?;
+ if n > 2 {
+ if cls[0] != Some(Class::Sse) {
+ return Err(Memory);
+ }
+ if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) {
+ return Err(Memory);
+ }
+ } else {
+ let mut i = 0;
+ while i < n {
+ if cls[i] == Some(Class::SseUp) {
+ cls[i] = Some(Class::Sse);
+ } else if cls[i] == Some(Class::Sse) {
+ i += 1;
+ while i != n && cls[i] == Some(Class::SseUp) {
+ i += 1;
+ }
+ } else {
+ i += 1;
+ }
+ }
+ }
+
+ Ok(cls)
+}
+
+fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg> {
+ if *i >= cls.len() {
+ return None;
+ }
+
+ match cls[*i] {
+ None => None,
+ Some(Class::Int) => {
+ *i += 1;
+ Some(if size.bytes() < 8 { Reg { kind: RegKind::Integer, size } } else { Reg::i64() })
+ }
+ Some(Class::Sse) => {
+ let vec_len =
+ 1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count();
+ *i += vec_len;
+ Some(if vec_len == 1 {
+ match size.bytes() {
+ 4 => Reg::f32(),
+ _ => Reg::f64(),
+ }
+ } else {
+ Reg { kind: RegKind::Vector, size: Size::from_bytes(8) * (vec_len as u64) }
+ })
+ }
+ Some(c) => unreachable!("reg_component: unhandled class {:?}", c),
+ }
+}
+
+fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
+ let mut i = 0;
+ let lo = reg_component(cls, &mut i, size).unwrap();
+ let offset = Size::from_bytes(8) * (i as u64);
+ let mut target = CastTarget::from(lo);
+ if size > offset {
+ if let Some(hi) = reg_component(cls, &mut i, size - offset) {
+ target = CastTarget::pair(lo, hi);
+ }
+ }
+ assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
+ target
+}
+
+const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
+const MAX_SSE_REGS: usize = 8; // XMM0-7
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ let mut int_regs = MAX_INT_REGS;
+ let mut sse_regs = MAX_SSE_REGS;
+
+ let mut x86_64_arg_or_ret = |arg: &mut ArgAbi<'a, Ty>, is_arg: bool| {
+ let mut cls_or_mem = classify_arg(cx, arg);
+
+ if is_arg {
+ if let Ok(cls) = cls_or_mem {
+ let mut needed_int = 0;
+ let mut needed_sse = 0;
+ for c in cls {
+ match c {
+ Some(Class::Int) => needed_int += 1,
+ Some(Class::Sse) => needed_sse += 1,
+ _ => {}
+ }
+ }
+ match (int_regs.checked_sub(needed_int), sse_regs.checked_sub(needed_sse)) {
+ (Some(left_int), Some(left_sse)) => {
+ int_regs = left_int;
+ sse_regs = left_sse;
+ }
+ _ => {
+ // Not enough registers for this argument, so it will be
+ // passed on the stack, but we only mark aggregates
+ // explicitly as indirect `byval` arguments, as LLVM will
+ // automatically put immediates on the stack itself.
+ if arg.layout.is_aggregate() {
+ cls_or_mem = Err(Memory);
+ }
+ }
+ }
+ }
+ }
+
+ match cls_or_mem {
+ Err(Memory) => {
+ if is_arg {
+ arg.make_indirect_byval();
+ } else {
+ // `sret` parameter thus one less integer register available
+ arg.make_indirect();
+ // NOTE(eddyb) return is handled first, so no registers
+ // should've been used yet.
+ assert_eq!(int_regs, MAX_INT_REGS);
+ int_regs -= 1;
+ }
+ }
+ Ok(ref cls) => {
+ // split into sized chunks passed individually
+ if arg.layout.is_aggregate() {
+ let size = arg.layout.size;
+ arg.cast_to(cast_target(cls, size))
+ } else {
+ arg.extend_integer_width_to(32);
+ }
+ }
+ }
+ };
+
+ if !fn_abi.ret.is_ignore() {
+ x86_64_arg_or_ret(&mut fn_abi.ret, false);
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ x86_64_arg_or_ret(arg, true);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/call/x86_win64.rs b/compiler/rustc_target/src/abi/call/x86_win64.rs
new file mode 100644
index 000000000..2aad641b1
--- /dev/null
+++ b/compiler/rustc_target/src/abi/call/x86_win64.rs
@@ -0,0 +1,40 @@
+use crate::abi::call::{ArgAbi, FnAbi, Reg};
+use crate::abi::Abi;
+
+// Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing
+
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
+ let fixup = |a: &mut ArgAbi<'_, Ty>| {
+ match a.layout.abi {
+ Abi::Uninhabited => {}
+ Abi::ScalarPair(..) | Abi::Aggregate { .. } => match a.layout.size.bits() {
+ 8 => a.cast_to(Reg::i8()),
+ 16 => a.cast_to(Reg::i16()),
+ 32 => a.cast_to(Reg::i32()),
+ 64 => a.cast_to(Reg::i64()),
+ _ => a.make_indirect(),
+ },
+ Abi::Vector { .. } => {
+ // FIXME(eddyb) there should be a size cap here
+ // (probably what clang calls "illegal vectors").
+ }
+ Abi::Scalar(_) => {
+ if a.layout.size.bytes() > 8 {
+ a.make_indirect();
+ } else {
+ a.extend_integer_width_to(32);
+ }
+ }
+ }
+ };
+
+ if !fn_abi.ret.is_ignore() {
+ fixup(&mut fn_abi.ret);
+ }
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ fixup(arg);
+ }
+}
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
new file mode 100644
index 000000000..92ce4d91d
--- /dev/null
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -0,0 +1,1558 @@
+pub use Integer::*;
+pub use Primitive::*;
+
+use crate::json::{Json, ToJson};
+use crate::spec::Target;
+
+use std::convert::{TryFrom, TryInto};
+use std::fmt;
+use std::iter::Step;
+use std::num::NonZeroUsize;
+use std::ops::{Add, AddAssign, Deref, Mul, RangeInclusive, Sub};
+use std::str::FromStr;
+
+use rustc_data_structures::intern::Interned;
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_macros::HashStable_Generic;
+
+pub mod call;
+
+/// Parsed [Data layout](https://llvm.org/docs/LangRef.html#data-layout)
+/// for a target, which contains everything needed to compute layouts.
+pub struct TargetDataLayout {
+ pub endian: Endian,
+ pub i1_align: AbiAndPrefAlign,
+ pub i8_align: AbiAndPrefAlign,
+ pub i16_align: AbiAndPrefAlign,
+ pub i32_align: AbiAndPrefAlign,
+ pub i64_align: AbiAndPrefAlign,
+ pub i128_align: AbiAndPrefAlign,
+ pub f32_align: AbiAndPrefAlign,
+ pub f64_align: AbiAndPrefAlign,
+ pub pointer_size: Size,
+ pub pointer_align: AbiAndPrefAlign,
+ pub aggregate_align: AbiAndPrefAlign,
+
+ /// Alignments for vector types.
+ pub vector_align: Vec<(Size, AbiAndPrefAlign)>,
+
+ pub instruction_address_space: AddressSpace,
+
+ /// Minimum size of #[repr(C)] enums (default I32 bits)
+ pub c_enum_min_size: Integer,
+}
+
+impl Default for TargetDataLayout {
+ /// Creates an instance of `TargetDataLayout`.
+ fn default() -> TargetDataLayout {
+ let align = |bits| Align::from_bits(bits).unwrap();
+ TargetDataLayout {
+ endian: Endian::Big,
+ i1_align: AbiAndPrefAlign::new(align(8)),
+ i8_align: AbiAndPrefAlign::new(align(8)),
+ i16_align: AbiAndPrefAlign::new(align(16)),
+ i32_align: AbiAndPrefAlign::new(align(32)),
+ i64_align: AbiAndPrefAlign { abi: align(32), pref: align(64) },
+ i128_align: AbiAndPrefAlign { abi: align(32), pref: align(64) },
+ f32_align: AbiAndPrefAlign::new(align(32)),
+ f64_align: AbiAndPrefAlign::new(align(64)),
+ pointer_size: Size::from_bits(64),
+ pointer_align: AbiAndPrefAlign::new(align(64)),
+ aggregate_align: AbiAndPrefAlign { abi: align(0), pref: align(64) },
+ vector_align: vec![
+ (Size::from_bits(64), AbiAndPrefAlign::new(align(64))),
+ (Size::from_bits(128), AbiAndPrefAlign::new(align(128))),
+ ],
+ instruction_address_space: AddressSpace::DATA,
+ c_enum_min_size: Integer::I32,
+ }
+ }
+}
+
+impl TargetDataLayout {
+ pub fn parse(target: &Target) -> Result<TargetDataLayout, String> {
+ // Parse an address space index from a string.
+ let parse_address_space = |s: &str, cause: &str| {
+ s.parse::<u32>().map(AddressSpace).map_err(|err| {
+ format!("invalid address space `{}` for `{}` in \"data-layout\": {}", s, cause, err)
+ })
+ };
+
+ // Parse a bit count from a string.
+ let parse_bits = |s: &str, kind: &str, cause: &str| {
+ s.parse::<u64>().map_err(|err| {
+ format!("invalid {} `{}` for `{}` in \"data-layout\": {}", kind, s, cause, err)
+ })
+ };
+
+ // Parse a size string.
+ let size = |s: &str, cause: &str| parse_bits(s, "size", cause).map(Size::from_bits);
+
+ // Parse an alignment string.
+ let align = |s: &[&str], cause: &str| {
+ if s.is_empty() {
+ return Err(format!("missing alignment for `{}` in \"data-layout\"", cause));
+ }
+ let align_from_bits = |bits| {
+ Align::from_bits(bits).map_err(|err| {
+ format!("invalid alignment for `{}` in \"data-layout\": {}", cause, err)
+ })
+ };
+ let abi = parse_bits(s[0], "alignment", cause)?;
+ let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?;
+ Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? })
+ };
+
+ let mut dl = TargetDataLayout::default();
+ let mut i128_align_src = 64;
+ for spec in target.data_layout.split('-') {
+ let spec_parts = spec.split(':').collect::<Vec<_>>();
+
+ match &*spec_parts {
+ ["e"] => dl.endian = Endian::Little,
+ ["E"] => dl.endian = Endian::Big,
+ [p] if p.starts_with('P') => {
+ dl.instruction_address_space = parse_address_space(&p[1..], "P")?
+ }
+ ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?,
+ ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?,
+ ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?,
+ [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
+ dl.pointer_size = size(s, p)?;
+ dl.pointer_align = align(a, p)?;
+ }
+ [s, ref a @ ..] if s.starts_with('i') => {
+ let Ok(bits) = s[1..].parse::<u64>() else {
+ size(&s[1..], "i")?; // For the user error.
+ continue;
+ };
+ let a = align(a, s)?;
+ match bits {
+ 1 => dl.i1_align = a,
+ 8 => dl.i8_align = a,
+ 16 => dl.i16_align = a,
+ 32 => dl.i32_align = a,
+ 64 => dl.i64_align = a,
+ _ => {}
+ }
+ if bits >= i128_align_src && bits <= 128 {
+ // Default alignment for i128 is decided by taking the alignment of
+ // largest-sized i{64..=128}.
+ i128_align_src = bits;
+ dl.i128_align = a;
+ }
+ }
+ [s, ref a @ ..] if s.starts_with('v') => {
+ let v_size = size(&s[1..], "v")?;
+ let a = align(a, s)?;
+ if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
+ v.1 = a;
+ continue;
+ }
+ // No existing entry, add a new one.
+ dl.vector_align.push((v_size, a));
+ }
+ _ => {} // Ignore everything else.
+ }
+ }
+
+ // Perform consistency checks against the Target information.
+ if dl.endian != target.endian {
+ return Err(format!(
+ "inconsistent target specification: \"data-layout\" claims \
+ architecture is {}-endian, while \"target-endian\" is `{}`",
+ dl.endian.as_str(),
+ target.endian.as_str(),
+ ));
+ }
+
+ let target_pointer_width: u64 = target.pointer_width.into();
+ if dl.pointer_size.bits() != target_pointer_width {
+ return Err(format!(
+ "inconsistent target specification: \"data-layout\" claims \
+ pointers are {}-bit, while \"target-pointer-width\" is `{}`",
+ dl.pointer_size.bits(),
+ target.pointer_width
+ ));
+ }
+
+ dl.c_enum_min_size = Integer::from_size(Size::from_bits(target.c_enum_min_bits))?;
+
+ Ok(dl)
+ }
+
+ /// Returns exclusive upper bound on object size.
+ ///
+ /// The theoretical maximum object size is defined as the maximum positive `isize` value.
+ /// This ensures that the `offset` semantics remain well-defined by allowing it to correctly
+ /// index every address within an object along with one byte past the end, along with allowing
+ /// `isize` to store the difference between any two pointers into an object.
+ ///
+ /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer
+ /// to represent object size in bits. It would need to be 1 << 61 to account for this, but is
+ /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable
+ /// address space on 64-bit ARMv8 and x86_64.
+ #[inline]
+ pub fn obj_size_bound(&self) -> u64 {
+ match self.pointer_size.bits() {
+ 16 => 1 << 15,
+ 32 => 1 << 31,
+ 64 => 1 << 47,
+ bits => panic!("obj_size_bound: unknown pointer bit size {}", bits),
+ }
+ }
+
+ #[inline]
+ pub fn ptr_sized_integer(&self) -> Integer {
+ match self.pointer_size.bits() {
+ 16 => I16,
+ 32 => I32,
+ 64 => I64,
+ bits => panic!("ptr_sized_integer: unknown pointer bit size {}", bits),
+ }
+ }
+
+ #[inline]
+ pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
+ for &(size, align) in &self.vector_align {
+ if size == vec_size {
+ return align;
+ }
+ }
+ // Default to natural alignment, which is what LLVM does.
+ // That is, use the size, rounded up to a power of 2.
+ AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap())
+ }
+}
+
+pub trait HasDataLayout {
+ fn data_layout(&self) -> &TargetDataLayout;
+}
+
+impl HasDataLayout for TargetDataLayout {
+ #[inline]
+ fn data_layout(&self) -> &TargetDataLayout {
+ self
+ }
+}
+
+/// Endianness of the target, which must match cfg(target-endian).
+#[derive(Copy, Clone, PartialEq)]
+pub enum Endian {
+ Little,
+ Big,
+}
+
+impl Endian {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Self::Little => "little",
+ Self::Big => "big",
+ }
+ }
+}
+
+impl fmt::Debug for Endian {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl FromStr for Endian {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "little" => Ok(Self::Little),
+ "big" => Ok(Self::Big),
+ _ => Err(format!(r#"unknown endian: "{}""#, s)),
+ }
+ }
+}
+
+impl ToJson for Endian {
+ fn to_json(&self) -> Json {
+ self.as_str().to_json()
+ }
+}
+
+/// Size of a type in bytes.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
+pub struct Size {
+ raw: u64,
+}
+
+// This is debug-printed a lot in larger structs, don't waste too much space there
+impl fmt::Debug for Size {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Size({} bytes)", self.bytes())
+ }
+}
+
+impl Size {
+ pub const ZERO: Size = Size { raw: 0 };
+
+ /// Rounds `bits` up to the next-higher byte boundary, if `bits` is
+ /// not a multiple of 8.
+ pub fn from_bits(bits: impl TryInto<u64>) -> Size {
+ let bits = bits.try_into().ok().unwrap();
+ // Avoid potential overflow from `bits + 7`.
+ Size { raw: bits / 8 + ((bits % 8) + 7) / 8 }
+ }
+
+ #[inline]
+ pub fn from_bytes(bytes: impl TryInto<u64>) -> Size {
+ let bytes: u64 = bytes.try_into().ok().unwrap();
+ Size { raw: bytes }
+ }
+
+ #[inline]
+ pub fn bytes(self) -> u64 {
+ self.raw
+ }
+
+ #[inline]
+ pub fn bytes_usize(self) -> usize {
+ self.bytes().try_into().unwrap()
+ }
+
+ #[inline]
+ pub fn bits(self) -> u64 {
+ #[cold]
+ fn overflow(bytes: u64) -> ! {
+ panic!("Size::bits: {} bytes in bits doesn't fit in u64", bytes)
+ }
+
+ self.bytes().checked_mul(8).unwrap_or_else(|| overflow(self.bytes()))
+ }
+
+ #[inline]
+ pub fn bits_usize(self) -> usize {
+ self.bits().try_into().unwrap()
+ }
+
+ #[inline]
+ pub fn align_to(self, align: Align) -> Size {
+ let mask = align.bytes() - 1;
+ Size::from_bytes((self.bytes() + mask) & !mask)
+ }
+
+ #[inline]
+ pub fn is_aligned(self, align: Align) -> bool {
+ let mask = align.bytes() - 1;
+ self.bytes() & mask == 0
+ }
+
+ #[inline]
+ pub fn checked_add<C: HasDataLayout>(self, offset: Size, cx: &C) -> Option<Size> {
+ let dl = cx.data_layout();
+
+ let bytes = self.bytes().checked_add(offset.bytes())?;
+
+ if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None }
+ }
+
+ #[inline]
+ pub fn checked_mul<C: HasDataLayout>(self, count: u64, cx: &C) -> Option<Size> {
+ let dl = cx.data_layout();
+
+ let bytes = self.bytes().checked_mul(count)?;
+ if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None }
+ }
+
+ /// Truncates `value` to `self` bits and then sign-extends it to 128 bits
+ /// (i.e., if it is negative, fill with 1's on the left).
+ #[inline]
+ pub fn sign_extend(self, value: u128) -> u128 {
+ let size = self.bits();
+ if size == 0 {
+ // Truncated until nothing is left.
+ return 0;
+ }
+ // Sign-extend it.
+ let shift = 128 - size;
+ // Shift the unsigned value to the left, then shift back to the right as signed
+ // (essentially fills with sign bit on the left).
+ (((value << shift) as i128) >> shift) as u128
+ }
+
+ /// Truncates `value` to `self` bits.
+ #[inline]
+ pub fn truncate(self, value: u128) -> u128 {
+ let size = self.bits();
+ if size == 0 {
+ // Truncated until nothing is left.
+ return 0;
+ }
+ let shift = 128 - size;
+ // Truncate (shift left to drop out leftover values, shift right to fill with zeroes).
+ (value << shift) >> shift
+ }
+
+ #[inline]
+ pub fn signed_int_min(&self) -> i128 {
+ self.sign_extend(1_u128 << (self.bits() - 1)) as i128
+ }
+
+ #[inline]
+ pub fn signed_int_max(&self) -> i128 {
+ i128::MAX >> (128 - self.bits())
+ }
+
+ #[inline]
+ pub fn unsigned_int_max(&self) -> u128 {
+ u128::MAX >> (128 - self.bits())
+ }
+}
+
+// Panicking addition, subtraction and multiplication for convenience.
+// Avoid during layout computation, return `LayoutError` instead.
+
+impl Add for Size {
+ type Output = Size;
+ #[inline]
+ fn add(self, other: Size) -> Size {
+ Size::from_bytes(self.bytes().checked_add(other.bytes()).unwrap_or_else(|| {
+ panic!("Size::add: {} + {} doesn't fit in u64", self.bytes(), other.bytes())
+ }))
+ }
+}
+
+impl Sub for Size {
+ type Output = Size;
+ #[inline]
+ fn sub(self, other: Size) -> Size {
+ Size::from_bytes(self.bytes().checked_sub(other.bytes()).unwrap_or_else(|| {
+ panic!("Size::sub: {} - {} would result in negative size", self.bytes(), other.bytes())
+ }))
+ }
+}
+
+impl Mul<Size> for u64 {
+ type Output = Size;
+ #[inline]
+ fn mul(self, size: Size) -> Size {
+ size * self
+ }
+}
+
+impl Mul<u64> for Size {
+ type Output = Size;
+ #[inline]
+ fn mul(self, count: u64) -> Size {
+ match self.bytes().checked_mul(count) {
+ Some(bytes) => Size::from_bytes(bytes),
+ None => panic!("Size::mul: {} * {} doesn't fit in u64", self.bytes(), count),
+ }
+ }
+}
+
+impl AddAssign for Size {
+ #[inline]
+ fn add_assign(&mut self, other: Size) {
+ *self = *self + other;
+ }
+}
+
+impl Step for Size {
+ #[inline]
+ fn steps_between(start: &Self, end: &Self) -> Option<usize> {
+ u64::steps_between(&start.bytes(), &end.bytes())
+ }
+
+ #[inline]
+ fn forward_checked(start: Self, count: usize) -> Option<Self> {
+ u64::forward_checked(start.bytes(), count).map(Self::from_bytes)
+ }
+
+ #[inline]
+ fn forward(start: Self, count: usize) -> Self {
+ Self::from_bytes(u64::forward(start.bytes(), count))
+ }
+
+ #[inline]
+ unsafe fn forward_unchecked(start: Self, count: usize) -> Self {
+ Self::from_bytes(u64::forward_unchecked(start.bytes(), count))
+ }
+
+ #[inline]
+ fn backward_checked(start: Self, count: usize) -> Option<Self> {
+ u64::backward_checked(start.bytes(), count).map(Self::from_bytes)
+ }
+
+ #[inline]
+ fn backward(start: Self, count: usize) -> Self {
+ Self::from_bytes(u64::backward(start.bytes(), count))
+ }
+
+ #[inline]
+ unsafe fn backward_unchecked(start: Self, count: usize) -> Self {
+ Self::from_bytes(u64::backward_unchecked(start.bytes(), count))
+ }
+}
+
+/// Alignment of a type in bytes (always a power of two).
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
+pub struct Align {
+ pow2: u8,
+}
+
+// This is debug-printed a lot in larger structs, don't waste too much space there
+impl fmt::Debug for Align {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Align({} bytes)", self.bytes())
+ }
+}
+
+impl Align {
+ pub const ONE: Align = Align { pow2: 0 };
+ pub const MAX: Align = Align { pow2: 29 };
+
+ #[inline]
+ pub fn from_bits(bits: u64) -> Result<Align, String> {
+ Align::from_bytes(Size::from_bits(bits).bytes())
+ }
+
+ #[inline]
+ pub fn from_bytes(align: u64) -> Result<Align, String> {
+ // Treat an alignment of 0 bytes like 1-byte alignment.
+ if align == 0 {
+ return Ok(Align::ONE);
+ }
+
+ #[cold]
+ fn not_power_of_2(align: u64) -> String {
+ format!("`{}` is not a power of 2", align)
+ }
+
+ #[cold]
+ fn too_large(align: u64) -> String {
+ format!("`{}` is too large", align)
+ }
+
+ let mut bytes = align;
+ let mut pow2: u8 = 0;
+ while (bytes & 1) == 0 {
+ pow2 += 1;
+ bytes >>= 1;
+ }
+ if bytes != 1 {
+ return Err(not_power_of_2(align));
+ }
+ if pow2 > Self::MAX.pow2 {
+ return Err(too_large(align));
+ }
+
+ Ok(Align { pow2 })
+ }
+
+ #[inline]
+ pub fn bytes(self) -> u64 {
+ 1 << self.pow2
+ }
+
+ #[inline]
+ pub fn bits(self) -> u64 {
+ self.bytes() * 8
+ }
+
+ /// Computes the best alignment possible for the given offset
+ /// (the largest power of two that the offset is a multiple of).
+ ///
+ /// N.B., for an offset of `0`, this happens to return `2^64`.
+ #[inline]
+ pub fn max_for_offset(offset: Size) -> Align {
+ Align { pow2: offset.bytes().trailing_zeros() as u8 }
+ }
+
+ /// Lower the alignment, if necessary, such that the given offset
+ /// is aligned to it (the offset is a multiple of the alignment).
+ #[inline]
+ pub fn restrict_for_offset(self, offset: Size) -> Align {
+ self.min(Align::max_for_offset(offset))
+ }
+}
+
+/// A pair of alignments, ABI-mandated and preferred.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(HashStable_Generic)]
+pub struct AbiAndPrefAlign {
+ pub abi: Align,
+ pub pref: Align,
+}
+
+impl AbiAndPrefAlign {
+ #[inline]
+ pub fn new(align: Align) -> AbiAndPrefAlign {
+ AbiAndPrefAlign { abi: align, pref: align }
+ }
+
+ #[inline]
+ pub fn min(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign {
+ AbiAndPrefAlign { abi: self.abi.min(other.abi), pref: self.pref.min(other.pref) }
+ }
+
+ #[inline]
+ pub fn max(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign {
+ AbiAndPrefAlign { abi: self.abi.max(other.abi), pref: self.pref.max(other.pref) }
+ }
+}
+
+/// Integers, also used for enum discriminants.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, HashStable_Generic)]
+pub enum Integer {
+ I8,
+ I16,
+ I32,
+ I64,
+ I128,
+}
+
+impl Integer {
+ #[inline]
+ pub fn size(self) -> Size {
+ match self {
+ I8 => Size::from_bytes(1),
+ I16 => Size::from_bytes(2),
+ I32 => Size::from_bytes(4),
+ I64 => Size::from_bytes(8),
+ I128 => Size::from_bytes(16),
+ }
+ }
+
+ pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAndPrefAlign {
+ let dl = cx.data_layout();
+
+ match self {
+ I8 => dl.i8_align,
+ I16 => dl.i16_align,
+ I32 => dl.i32_align,
+ I64 => dl.i64_align,
+ I128 => dl.i128_align,
+ }
+ }
+
+ /// Finds the smallest Integer type which can represent the signed value.
+ #[inline]
+ pub fn fit_signed(x: i128) -> Integer {
+ match x {
+ -0x0000_0000_0000_0080..=0x0000_0000_0000_007f => I8,
+ -0x0000_0000_0000_8000..=0x0000_0000_0000_7fff => I16,
+ -0x0000_0000_8000_0000..=0x0000_0000_7fff_ffff => I32,
+ -0x8000_0000_0000_0000..=0x7fff_ffff_ffff_ffff => I64,
+ _ => I128,
+ }
+ }
+
+ /// Finds the smallest Integer type which can represent the unsigned value.
+ #[inline]
+ pub fn fit_unsigned(x: u128) -> Integer {
+ match x {
+ 0..=0x0000_0000_0000_00ff => I8,
+ 0..=0x0000_0000_0000_ffff => I16,
+ 0..=0x0000_0000_ffff_ffff => I32,
+ 0..=0xffff_ffff_ffff_ffff => I64,
+ _ => I128,
+ }
+ }
+
+ /// Finds the smallest integer with the given alignment.
+ pub fn for_align<C: HasDataLayout>(cx: &C, wanted: Align) -> Option<Integer> {
+ let dl = cx.data_layout();
+
+ for candidate in [I8, I16, I32, I64, I128] {
+ if wanted == candidate.align(dl).abi && wanted.bytes() == candidate.size().bytes() {
+ return Some(candidate);
+ }
+ }
+ None
+ }
+
+ /// Find the largest integer with the given alignment or less.
+ pub fn approximate_align<C: HasDataLayout>(cx: &C, wanted: Align) -> Integer {
+ let dl = cx.data_layout();
+
+ // FIXME(eddyb) maybe include I128 in the future, when it works everywhere.
+ for candidate in [I64, I32, I16] {
+ if wanted >= candidate.align(dl).abi && wanted.bytes() >= candidate.size().bytes() {
+ return candidate;
+ }
+ }
+ I8
+ }
+
+ // FIXME(eddyb) consolidate this and other methods that find the appropriate
+ // `Integer` given some requirements.
+ #[inline]
+ fn from_size(size: Size) -> Result<Self, String> {
+ match size.bits() {
+ 8 => Ok(Integer::I8),
+ 16 => Ok(Integer::I16),
+ 32 => Ok(Integer::I32),
+ 64 => Ok(Integer::I64),
+ 128 => Ok(Integer::I128),
+ _ => Err(format!("rust does not support integers with {} bits", size.bits())),
+ }
+ }
+}
+
+/// Fundamental unit of memory access and layout.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum Primitive {
+ /// The `bool` is the signedness of the `Integer` type.
+ ///
+ /// One would think we would not care about such details this low down,
+ /// but some ABIs are described in terms of C types and ISAs where the
+ /// integer arithmetic is done on {sign,zero}-extended registers, e.g.
+ /// a negative integer passed by zero-extension will appear positive in
+ /// the callee, and most operations on it will produce the wrong values.
+ Int(Integer, bool),
+ F32,
+ F64,
+ Pointer,
+}
+
+impl Primitive {
+ pub fn size<C: HasDataLayout>(self, cx: &C) -> Size {
+ let dl = cx.data_layout();
+
+ match self {
+ Int(i, _) => i.size(),
+ F32 => Size::from_bits(32),
+ F64 => Size::from_bits(64),
+ Pointer => dl.pointer_size,
+ }
+ }
+
+ pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAndPrefAlign {
+ let dl = cx.data_layout();
+
+ match self {
+ Int(i, _) => i.align(dl),
+ F32 => dl.f32_align,
+ F64 => dl.f64_align,
+ Pointer => dl.pointer_align,
+ }
+ }
+
+ // FIXME(eddyb) remove, it's trivial thanks to `matches!`.
+ #[inline]
+ pub fn is_float(self) -> bool {
+ matches!(self, F32 | F64)
+ }
+
+ // FIXME(eddyb) remove, it's completely unused.
+ #[inline]
+ pub fn is_int(self) -> bool {
+ matches!(self, Int(..))
+ }
+
+ #[inline]
+ pub fn is_ptr(self) -> bool {
+ matches!(self, Pointer)
+ }
+}
+
+/// Inclusive wrap-around range of valid values, that is, if
+/// start > end, it represents `start..=MAX`,
+/// followed by `0..=end`.
+///
+/// That is, for an i8 primitive, a range of `254..=2` means following
+/// sequence:
+///
+/// 254 (-2), 255 (-1), 0, 1, 2
+///
+/// This is intended specifically to mirror LLVM’s `!range` metadata semantics.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub struct WrappingRange {
+ pub start: u128,
+ pub end: u128,
+}
+
+impl WrappingRange {
+ pub fn full(size: Size) -> Self {
+ Self { start: 0, end: size.unsigned_int_max() }
+ }
+
+ /// Returns `true` if `v` is contained in the range.
+ #[inline(always)]
+ pub fn contains(&self, v: u128) -> bool {
+ if self.start <= self.end {
+ self.start <= v && v <= self.end
+ } else {
+ self.start <= v || v <= self.end
+ }
+ }
+
+ /// Returns `self` with replaced `start`
+ #[inline(always)]
+ pub fn with_start(mut self, start: u128) -> Self {
+ self.start = start;
+ self
+ }
+
+ /// Returns `self` with replaced `end`
+ #[inline(always)]
+ pub fn with_end(mut self, end: u128) -> Self {
+ self.end = end;
+ self
+ }
+
+ /// Returns `true` if `size` completely fills the range.
+ #[inline]
+ pub fn is_full_for(&self, size: Size) -> bool {
+ let max_value = size.unsigned_int_max();
+ debug_assert!(self.start <= max_value && self.end <= max_value);
+ self.start == (self.end.wrapping_add(1) & max_value)
+ }
+}
+
+impl fmt::Debug for WrappingRange {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.start > self.end {
+ write!(fmt, "(..={}) | ({}..)", self.end, self.start)?;
+ } else {
+ write!(fmt, "{}..={}", self.start, self.end)?;
+ }
+ Ok(())
+ }
+}
+
+/// Information about one scalar component of a Rust type.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+#[derive(HashStable_Generic)]
+pub enum Scalar {
+ Initialized {
+ value: Primitive,
+
+ // FIXME(eddyb) always use the shortest range, e.g., by finding
+ // the largest space between two consecutive valid values and
+ // taking everything else as the (shortest) valid range.
+ valid_range: WrappingRange,
+ },
+ Union {
+ /// Even for unions, we need to use the correct registers for the kind of
+ /// values inside the union, so we keep the `Primitive` type around. We
+ /// also use it to compute the size of the scalar.
+ /// However, unions never have niches and even allow undef,
+ /// so there is no `valid_range`.
+ value: Primitive,
+ },
+}
+
+impl Scalar {
+ #[inline]
+ pub fn is_bool(&self) -> bool {
+ matches!(
+ self,
+ Scalar::Initialized {
+ value: Int(I8, false),
+ valid_range: WrappingRange { start: 0, end: 1 }
+ }
+ )
+ }
+
+ /// Get the primitive representation of this type, ignoring the valid range and whether the
+ /// value is allowed to be undefined (due to being a union).
+ pub fn primitive(&self) -> Primitive {
+ match *self {
+ Scalar::Initialized { value, .. } | Scalar::Union { value } => value,
+ }
+ }
+
+ pub fn align(self, cx: &impl HasDataLayout) -> AbiAndPrefAlign {
+ self.primitive().align(cx)
+ }
+
+ pub fn size(self, cx: &impl HasDataLayout) -> Size {
+ self.primitive().size(cx)
+ }
+
+ #[inline]
+ pub fn to_union(&self) -> Self {
+ Self::Union { value: self.primitive() }
+ }
+
+ #[inline]
+ pub fn valid_range(&self, cx: &impl HasDataLayout) -> WrappingRange {
+ match *self {
+ Scalar::Initialized { valid_range, .. } => valid_range,
+ Scalar::Union { value } => WrappingRange::full(value.size(cx)),
+ }
+ }
+
+ #[inline]
+ /// Allows the caller to mutate the valid range. This operation will panic if attempted on a union.
+ pub fn valid_range_mut(&mut self) -> &mut WrappingRange {
+ match self {
+ Scalar::Initialized { valid_range, .. } => valid_range,
+ Scalar::Union { .. } => panic!("cannot change the valid range of a union"),
+ }
+ }
+
+ /// Returns `true` if all possible numbers are valid, i.e `valid_range` covers the whole layout
+ #[inline]
+ pub fn is_always_valid<C: HasDataLayout>(&self, cx: &C) -> bool {
+ match *self {
+ Scalar::Initialized { valid_range, .. } => valid_range.is_full_for(self.size(cx)),
+ Scalar::Union { .. } => true,
+ }
+ }
+
+ /// Returns `true` if this type can be left uninit.
+ #[inline]
+ pub fn is_uninit_valid(&self) -> bool {
+ match *self {
+ Scalar::Initialized { .. } => false,
+ Scalar::Union { .. } => true,
+ }
+ }
+}
+
+/// Describes how the fields of a type are located in memory.
+#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum FieldsShape {
+ /// Scalar primitives and `!`, which never have fields.
+ Primitive,
+
+ /// All fields start at no offset. The `usize` is the field count.
+ Union(NonZeroUsize),
+
+ /// Array/vector-like placement, with all fields of identical types.
+ Array { stride: Size, count: u64 },
+
+ /// Struct-like placement, with precomputed offsets.
+ ///
+ /// Fields are guaranteed to not overlap, but note that gaps
+ /// before, between and after all the fields are NOT always
+ /// padding, and as such their contents may not be discarded.
+ /// For example, enum variants leave a gap at the start,
+ /// where the discriminant field in the enum layout goes.
+ Arbitrary {
+ /// Offsets for the first byte of each field,
+ /// ordered to match the source definition order.
+ /// This vector does not go in increasing order.
+ // FIXME(eddyb) use small vector optimization for the common case.
+ offsets: Vec<Size>,
+
+ /// Maps source order field indices to memory order indices,
+ /// depending on how the fields were reordered (if at all).
+ /// This is a permutation, with both the source order and the
+ /// memory order using the same (0..n) index ranges.
+ ///
+ /// Note that during computation of `memory_index`, sometimes
+ /// it is easier to operate on the inverse mapping (that is,
+ /// from memory order to source order), and that is usually
+ /// named `inverse_memory_index`.
+ ///
+ // FIXME(eddyb) build a better abstraction for permutations, if possible.
+ // FIXME(camlorn) also consider small vector optimization here.
+ memory_index: Vec<u32>,
+ },
+}
+
+impl FieldsShape {
+ #[inline]
+ pub fn count(&self) -> usize {
+ match *self {
+ FieldsShape::Primitive => 0,
+ FieldsShape::Union(count) => count.get(),
+ FieldsShape::Array { count, .. } => count.try_into().unwrap(),
+ FieldsShape::Arbitrary { ref offsets, .. } => offsets.len(),
+ }
+ }
+
+ #[inline]
+ pub fn offset(&self, i: usize) -> Size {
+ match *self {
+ FieldsShape::Primitive => {
+ unreachable!("FieldsShape::offset: `Primitive`s have no fields")
+ }
+ FieldsShape::Union(count) => {
+ assert!(
+ i < count.get(),
+ "tried to access field {} of union with {} fields",
+ i,
+ count
+ );
+ Size::ZERO
+ }
+ FieldsShape::Array { stride, count } => {
+ let i = u64::try_from(i).unwrap();
+ assert!(i < count);
+ stride * i
+ }
+ FieldsShape::Arbitrary { ref offsets, .. } => offsets[i],
+ }
+ }
+
+ #[inline]
+ pub fn memory_index(&self, i: usize) -> usize {
+ match *self {
+ FieldsShape::Primitive => {
+ unreachable!("FieldsShape::memory_index: `Primitive`s have no fields")
+ }
+ FieldsShape::Union(_) | FieldsShape::Array { .. } => i,
+ FieldsShape::Arbitrary { ref memory_index, .. } => memory_index[i].try_into().unwrap(),
+ }
+ }
+
+ /// Gets source indices of the fields by increasing offsets.
+ #[inline]
+ pub fn index_by_increasing_offset<'a>(&'a self) -> impl Iterator<Item = usize> + 'a {
+ let mut inverse_small = [0u8; 64];
+ let mut inverse_big = vec![];
+ let use_small = self.count() <= inverse_small.len();
+
+ // We have to write this logic twice in order to keep the array small.
+ if let FieldsShape::Arbitrary { ref memory_index, .. } = *self {
+ if use_small {
+ for i in 0..self.count() {
+ inverse_small[memory_index[i] as usize] = i as u8;
+ }
+ } else {
+ inverse_big = vec![0; self.count()];
+ for i in 0..self.count() {
+ inverse_big[memory_index[i] as usize] = i as u32;
+ }
+ }
+ }
+
+ (0..self.count()).map(move |i| match *self {
+ FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i,
+ FieldsShape::Arbitrary { .. } => {
+ if use_small {
+ inverse_small[i] as usize
+ } else {
+ inverse_big[i] as usize
+ }
+ }
+ })
+ }
+}
+
+/// An identifier that specifies the address space that some operation
+/// should operate on. Special address spaces have an effect on code generation,
+/// depending on the target and the address spaces it implements.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct AddressSpace(pub u32);
+
+impl AddressSpace {
+ /// The default address space, corresponding to data space.
+ pub const DATA: Self = AddressSpace(0);
+}
+
+/// Describes how values of the type are passed by target ABIs,
+/// in terms of categories of C types there are ABI rules for.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum Abi {
+ Uninhabited,
+ Scalar(Scalar),
+ ScalarPair(Scalar, Scalar),
+ Vector {
+ element: Scalar,
+ count: u64,
+ },
+ Aggregate {
+ /// If true, the size is exact, otherwise it's only a lower bound.
+ sized: bool,
+ },
+}
+
+impl Abi {
+ /// Returns `true` if the layout corresponds to an unsized type.
+ #[inline]
+ pub fn is_unsized(&self) -> bool {
+ match *self {
+ Abi::Uninhabited | Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false,
+ Abi::Aggregate { sized } => !sized,
+ }
+ }
+
+ /// Returns `true` if this is a single signed integer scalar
+ #[inline]
+ pub fn is_signed(&self) -> bool {
+ match self {
+ Abi::Scalar(scal) => match scal.primitive() {
+ Primitive::Int(_, signed) => signed,
+ _ => false,
+ },
+ _ => panic!("`is_signed` on non-scalar ABI {:?}", self),
+ }
+ }
+
+ /// Returns `true` if this is an uninhabited type
+ #[inline]
+ pub fn is_uninhabited(&self) -> bool {
+ matches!(*self, Abi::Uninhabited)
+ }
+
+ /// Returns `true` is this is a scalar type
+ #[inline]
+ pub fn is_scalar(&self) -> bool {
+ matches!(*self, Abi::Scalar(_))
+ }
+}
+
+rustc_index::newtype_index! {
+ pub struct VariantIdx {
+ derive [HashStable_Generic]
+ }
+}
+
+#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum Variants<'a> {
+ /// Single enum variants, structs/tuples, unions, and all non-ADTs.
+ Single { index: VariantIdx },
+
+ /// Enum-likes with more than one inhabited variant: each variant comes with
+ /// a *discriminant* (usually the same as the variant index but the user can
+ /// assign explicit discriminant values). That discriminant is encoded
+ /// as a *tag* on the machine. The layout of each variant is
+ /// a struct, and they all have space reserved for the tag.
+ /// For enums, the tag is the sole field of the layout.
+ Multiple {
+ tag: Scalar,
+ tag_encoding: TagEncoding,
+ tag_field: usize,
+ variants: IndexVec<VariantIdx, Layout<'a>>,
+ },
+}
+
+#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum TagEncoding {
+ /// The tag directly stores the discriminant, but possibly with a smaller layout
+ /// (so converting the tag to the discriminant can require sign extension).
+ Direct,
+
+ /// Niche (values invalid for a type) encoding the discriminant:
+ /// Discriminant and variant index coincide.
+ /// The variant `dataful_variant` contains a niche at an arbitrary
+ /// offset (field `tag_field` of the enum), which for a variant with
+ /// discriminant `d` is set to
+ /// `(d - niche_variants.start).wrapping_add(niche_start)`.
+ ///
+ /// For example, `Option<(usize, &T)>` is represented such that
+ /// `None` has a null pointer for the second tuple field, and
+ /// `Some` is the identity function (with a non-null reference).
+ Niche {
+ dataful_variant: VariantIdx,
+ niche_variants: RangeInclusive<VariantIdx>,
+ niche_start: u128,
+ },
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct Niche {
+ pub offset: Size,
+ pub value: Primitive,
+ pub valid_range: WrappingRange,
+}
+
+impl Niche {
+ pub fn from_scalar<C: HasDataLayout>(cx: &C, offset: Size, scalar: Scalar) -> Option<Self> {
+ let Scalar::Initialized { value, valid_range } = scalar else { return None };
+ let niche = Niche { offset, value, valid_range };
+ if niche.available(cx) > 0 { Some(niche) } else { None }
+ }
+
+ pub fn available<C: HasDataLayout>(&self, cx: &C) -> u128 {
+ let Self { value, valid_range: v, .. } = *self;
+ let size = value.size(cx);
+ assert!(size.bits() <= 128);
+ let max_value = size.unsigned_int_max();
+
+ // Find out how many values are outside the valid range.
+ let niche = v.end.wrapping_add(1)..v.start;
+ niche.end.wrapping_sub(niche.start) & max_value
+ }
+
+ pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
+ assert!(count > 0);
+
+ let Self { value, valid_range: v, .. } = *self;
+ let size = value.size(cx);
+ assert!(size.bits() <= 128);
+ let max_value = size.unsigned_int_max();
+
+ let niche = v.end.wrapping_add(1)..v.start;
+ let available = niche.end.wrapping_sub(niche.start) & max_value;
+ if count > available {
+ return None;
+ }
+
+ // Extend the range of valid values being reserved by moving either `v.start` or `v.end` bound.
+ // Given an eventual `Option<T>`, we try to maximize the chance for `None` to occupy the niche of zero.
+ // This is accomplished by preferring enums with 2 variants(`count==1`) and always taking the shortest path to niche zero.
+ // Having `None` in niche zero can enable some special optimizations.
+ //
+ // Bound selection criteria:
+ // 1. Select closest to zero given wrapping semantics.
+ // 2. Avoid moving past zero if possible.
+ //
+ // In practice this means that enums with `count > 1` are unlikely to claim niche zero, since they have to fit perfectly.
+ // If niche zero is already reserved, the selection of bounds are of little interest.
+ let move_start = |v: WrappingRange| {
+ let start = v.start.wrapping_sub(count) & max_value;
+ Some((start, Scalar::Initialized { value, valid_range: v.with_start(start) }))
+ };
+ let move_end = |v: WrappingRange| {
+ let start = v.end.wrapping_add(1) & max_value;
+ let end = v.end.wrapping_add(count) & max_value;
+ Some((start, Scalar::Initialized { value, valid_range: v.with_end(end) }))
+ };
+ let distance_end_zero = max_value - v.end;
+ if v.start > v.end {
+ // zero is unavailable because wrapping occurs
+ move_end(v)
+ } else if v.start <= distance_end_zero {
+ if count <= v.start {
+ move_start(v)
+ } else {
+ // moved past zero, use other bound
+ move_end(v)
+ }
+ } else {
+ let end = v.end.wrapping_add(count) & max_value;
+ let overshot_zero = (1..=v.end).contains(&end);
+ if overshot_zero {
+ // moved past zero, use other bound
+ move_start(v)
+ } else {
+ move_end(v)
+ }
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Hash, HashStable_Generic)]
+pub struct LayoutS<'a> {
+ /// Says where the fields are located within the layout.
+ pub fields: FieldsShape,
+
+ /// Encodes information about multi-variant layouts.
+ /// Even with `Multiple` variants, a layout still has its own fields! Those are then
+ /// shared between all variants. One of them will be the discriminant,
+ /// but e.g. generators can have more.
+ ///
+ /// To access all fields of this layout, both `fields` and the fields of the active variant
+ /// must be taken into account.
+ pub variants: Variants<'a>,
+
+ /// The `abi` defines how this data is passed between functions, and it defines
+ /// value restrictions via `valid_range`.
+ ///
+ /// Note that this is entirely orthogonal to the recursive structure defined by
+ /// `variants` and `fields`; for example, `ManuallyDrop<Result<isize, isize>>` has
+ /// `Abi::ScalarPair`! So, even with non-`Aggregate` `abi`, `fields` and `variants`
+ /// have to be taken into account to find all fields of this layout.
+ pub abi: Abi,
+
+ /// The leaf scalar with the largest number of invalid values
+ /// (i.e. outside of its `valid_range`), if it exists.
+ pub largest_niche: Option<Niche>,
+
+ pub align: AbiAndPrefAlign,
+ pub size: Size,
+}
+
+impl<'a> LayoutS<'a> {
+ pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
+ let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
+ let size = scalar.size(cx);
+ let align = scalar.align(cx);
+ LayoutS {
+ variants: Variants::Single { index: VariantIdx::new(0) },
+ fields: FieldsShape::Primitive,
+ abi: Abi::Scalar(scalar),
+ largest_niche,
+ size,
+ align,
+ }
+ }
+}
+
+impl<'a> fmt::Debug for LayoutS<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // This is how `Layout` used to print before it become
+ // `Interned<LayoutS>`. We print it like this to avoid having to update
+ // expected output in a lot of tests.
+ let LayoutS { size, align, abi, fields, largest_niche, variants } = self;
+ f.debug_struct("Layout")
+ .field("size", size)
+ .field("align", align)
+ .field("abi", abi)
+ .field("fields", fields)
+ .field("largest_niche", largest_niche)
+ .field("variants", variants)
+ .finish()
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
+#[rustc_pass_by_value]
+pub struct Layout<'a>(pub Interned<'a, LayoutS<'a>>);
+
+impl<'a> fmt::Debug for Layout<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // See comment on `<LayoutS as Debug>::fmt` above.
+ self.0.0.fmt(f)
+ }
+}
+
+impl<'a> Layout<'a> {
+ pub fn fields(self) -> &'a FieldsShape {
+ &self.0.0.fields
+ }
+
+ pub fn variants(self) -> &'a Variants<'a> {
+ &self.0.0.variants
+ }
+
+ pub fn abi(self) -> Abi {
+ self.0.0.abi
+ }
+
+ pub fn largest_niche(self) -> Option<Niche> {
+ self.0.0.largest_niche
+ }
+
+ pub fn align(self) -> AbiAndPrefAlign {
+ self.0.0.align
+ }
+
+ pub fn size(self) -> Size {
+ self.0.0.size
+ }
+}
+
+/// The layout of a type, alongside the type itself.
+/// Provides various type traversal APIs (e.g., recursing into fields).
+///
+/// Note that the layout is NOT guaranteed to always be identical
+/// to that obtained from `layout_of(ty)`, as we need to produce
+/// layouts for which Rust types do not exist, such as enum variants
+/// or synthetic fields of enums (i.e., discriminants) and fat pointers.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)]
+pub struct TyAndLayout<'a, Ty> {
+ pub ty: Ty,
+ pub layout: Layout<'a>,
+}
+
+impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
+ type Target = &'a LayoutS<'a>;
+ fn deref(&self) -> &&'a LayoutS<'a> {
+ &self.layout.0.0
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum PointerKind {
+ /// Most general case, we know no restrictions to tell LLVM.
+ SharedMutable,
+
+ /// `&T` where `T` contains no `UnsafeCell`, is `dereferenceable`, `noalias` and `readonly`.
+ Frozen,
+
+ /// `&mut T` which is `dereferenceable` and `noalias` but not `readonly`.
+ UniqueBorrowed,
+
+ /// `&mut !Unpin`, which is `dereferenceable` but neither `noalias` nor `readonly`.
+ UniqueBorrowedPinned,
+
+ /// `Box<T>`, which is `noalias` (even on return types, unlike the above) but neither `readonly`
+ /// nor `dereferenceable`.
+ UniqueOwned,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct PointeeInfo {
+ pub size: Size,
+ pub align: Align,
+ pub safe: Option<PointerKind>,
+ pub address_space: AddressSpace,
+}
+
+/// Used in `might_permit_raw_init` to indicate the kind of initialisation
+/// that is checked to be valid
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum InitKind {
+ Zero,
+ Uninit,
+}
+
+/// Trait that needs to be implemented by the higher-level type representation
+/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
+pub trait TyAbiInterface<'a, C>: Sized {
+ fn ty_and_layout_for_variant(
+ this: TyAndLayout<'a, Self>,
+ cx: &C,
+ variant_index: VariantIdx,
+ ) -> TyAndLayout<'a, Self>;
+ fn ty_and_layout_field(this: TyAndLayout<'a, Self>, cx: &C, i: usize) -> TyAndLayout<'a, Self>;
+ fn ty_and_layout_pointee_info_at(
+ this: TyAndLayout<'a, Self>,
+ cx: &C,
+ offset: Size,
+ ) -> Option<PointeeInfo>;
+ fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
+ fn is_never(this: TyAndLayout<'a, Self>) -> bool;
+ fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
+ fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
+}
+
+impl<'a, Ty> TyAndLayout<'a, Ty> {
+ pub fn for_variant<C>(self, cx: &C, variant_index: VariantIdx) -> Self
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::ty_and_layout_for_variant(self, cx, variant_index)
+ }
+
+ pub fn field<C>(self, cx: &C, i: usize) -> Self
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::ty_and_layout_field(self, cx, i)
+ }
+
+ pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo>
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::ty_and_layout_pointee_info_at(self, cx, offset)
+ }
+
+ pub fn is_single_fp_element<C>(self, cx: &C) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ C: HasDataLayout,
+ {
+ match self.abi {
+ Abi::Scalar(scalar) => scalar.primitive().is_float(),
+ Abi::Aggregate { .. } => {
+ if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
+ self.field(cx, 0).is_single_fp_element(cx)
+ } else {
+ false
+ }
+ }
+ _ => false,
+ }
+ }
+
+ pub fn is_adt<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_adt(self)
+ }
+
+ pub fn is_never<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_never(self)
+ }
+
+ pub fn is_tuple<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_tuple(self)
+ }
+
+ pub fn is_unit<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_unit(self)
+ }
+}
+
+impl<'a, Ty> TyAndLayout<'a, Ty> {
+ /// Returns `true` if the layout corresponds to an unsized type.
+ pub fn is_unsized(&self) -> bool {
+ self.abi.is_unsized()
+ }
+
+ /// Returns `true` if the type is a ZST and not unsized.
+ pub fn is_zst(&self) -> bool {
+ match self.abi {
+ Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false,
+ Abi::Uninhabited => self.size.bytes() == 0,
+ Abi::Aggregate { sized } => sized && self.size.bytes() == 0,
+ }
+ }
+
+ /// Determines if this type permits "raw" initialization by just transmuting some
+ /// memory into an instance of `T`.
+ ///
+ /// `init_kind` indicates if the memory is zero-initialized or left uninitialized.
+ ///
+ /// This code is intentionally conservative, and will not detect
+ /// * zero init of an enum whose 0 variant does not allow zero initialization
+ /// * making uninitialized types who have a full valid range (ints, floats, raw pointers)
+ /// * Any form of invalid value being made inside an array (unless the value is uninhabited)
+ ///
+ /// A strict form of these checks that uses const evaluation exists in
+ /// `rustc_const_eval::might_permit_raw_init`, and a tracking issue for making these checks
+ /// stricter is <https://github.com/rust-lang/rust/issues/66151>.
+ ///
+ /// FIXME: Once all the conservatism is removed from here, and the checks are ran by default,
+ /// we can use the const evaluation checks always instead.
+ pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind) -> bool
+ where
+ Self: Copy,
+ Ty: TyAbiInterface<'a, C>,
+ C: HasDataLayout,
+ {
+ let scalar_allows_raw_init = move |s: Scalar| -> bool {
+ match init_kind {
+ InitKind::Zero => {
+ // The range must contain 0.
+ s.valid_range(cx).contains(0)
+ }
+ InitKind::Uninit => {
+ // The range must include all values.
+ s.is_always_valid(cx)
+ }
+ }
+ };
+
+ // Check the ABI.
+ let valid = match self.abi {
+ Abi::Uninhabited => false, // definitely UB
+ Abi::Scalar(s) => scalar_allows_raw_init(s),
+ Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
+ Abi::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s),
+ Abi::Aggregate { .. } => true, // Fields are checked below.
+ };
+ if !valid {
+ // This is definitely not okay.
+ return false;
+ }
+
+ // If we have not found an error yet, we need to recursively descend into fields.
+ match &self.fields {
+ FieldsShape::Primitive | FieldsShape::Union { .. } => {}
+ FieldsShape::Array { .. } => {
+ // FIXME(#66151): For now, we are conservative and do not check arrays by default.
+ }
+ FieldsShape::Arbitrary { offsets, .. } => {
+ for idx in 0..offsets.len() {
+ if !self.field(cx, idx).might_permit_raw_init(cx, init_kind) {
+ // We found a field that is unhappy with this kind of initialization.
+ return false;
+ }
+ }
+ }
+ }
+
+ // FIXME(#66151): For now, we are conservative and do not check `self.variants`.
+ true
+ }
+}
diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs
new file mode 100644
index 000000000..62a0f9fb0
--- /dev/null
+++ b/compiler/rustc_target/src/asm/aarch64.rs
@@ -0,0 +1,200 @@
+use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::{RelocModel, Target};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ AArch64 AArch64InlineAsmRegClass {
+ reg,
+ vreg,
+ vreg_low16,
+ preg,
+ }
+}
+
+impl AArch64InlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ match self {
+ Self::reg => &['w', 'x'],
+ Self::vreg | Self::vreg_low16 => &['b', 'h', 's', 'd', 'q', 'v'],
+ Self::preg => &[],
+ }
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ match self {
+ Self::reg => match ty.size().bits() {
+ 64 => None,
+ _ => Some(('w', "w0")),
+ },
+ Self::vreg | Self::vreg_low16 => match ty.size().bits() {
+ 8 => Some(('b', "b0")),
+ 16 => Some(('h', "h0")),
+ 32 => Some(('s', "s0")),
+ 64 => Some(('d', "d0")),
+ 128 => Some(('q', "q0")),
+ _ => None,
+ },
+ Self::preg => None,
+ }
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ match self {
+ Self::reg => Some(('x', "x0")),
+ Self::vreg | Self::vreg_low16 => Some(('v', "v0")),
+ Self::preg => None,
+ }
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg => types! { _: I8, I16, I32, I64, F32, F64; },
+ Self::vreg | Self::vreg_low16 => types! {
+ neon: I8, I16, I32, I64, F32, F64,
+ VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1),
+ VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
+ },
+ Self::preg => &[],
+ }
+ }
+}
+
+pub fn target_reserves_x18(target: &Target) -> bool {
+ target.os == "android" || target.os == "fuchsia" || target.is_like_osx || target.is_like_windows
+}
+
+fn reserved_x18(
+ _arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ _target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ if target_reserves_x18(target) {
+ Err("x18 is a reserved register on this target")
+ } else {
+ Ok(())
+ }
+}
+
+def_regs! {
+ AArch64 AArch64InlineAsmReg AArch64InlineAsmRegClass {
+ x0: reg = ["x0", "w0"],
+ x1: reg = ["x1", "w1"],
+ x2: reg = ["x2", "w2"],
+ x3: reg = ["x3", "w3"],
+ x4: reg = ["x4", "w4"],
+ x5: reg = ["x5", "w5"],
+ x6: reg = ["x6", "w6"],
+ x7: reg = ["x7", "w7"],
+ x8: reg = ["x8", "w8"],
+ x9: reg = ["x9", "w9"],
+ x10: reg = ["x10", "w10"],
+ x11: reg = ["x11", "w11"],
+ x12: reg = ["x12", "w12"],
+ x13: reg = ["x13", "w13"],
+ x14: reg = ["x14", "w14"],
+ x15: reg = ["x15", "w15"],
+ x16: reg = ["x16", "w16"],
+ x17: reg = ["x17", "w17"],
+ x18: reg = ["x18", "w18"] % reserved_x18,
+ x20: reg = ["x20", "w20"],
+ x21: reg = ["x21", "w21"],
+ x22: reg = ["x22", "w22"],
+ x23: reg = ["x23", "w23"],
+ x24: reg = ["x24", "w24"],
+ x25: reg = ["x25", "w25"],
+ x26: reg = ["x26", "w26"],
+ x27: reg = ["x27", "w27"],
+ x28: reg = ["x28", "w28"],
+ x30: reg = ["x30", "w30", "lr", "wlr"],
+ v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0", "z0"],
+ v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1", "z1"],
+ v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2", "z2"],
+ v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3", "z3"],
+ v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4", "z4"],
+ v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5", "z5"],
+ v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6", "z6"],
+ v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7", "z7"],
+ v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8", "z8"],
+ v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9", "z9"],
+ v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10", "z10"],
+ v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11", "z11"],
+ v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12", "z12"],
+ v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13", "z13"],
+ v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14", "z14"],
+ v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15", "z15"],
+ v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16", "z16"],
+ v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17", "z17"],
+ v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18", "z18"],
+ v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19", "z19"],
+ v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20", "z20"],
+ v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21", "z21"],
+ v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22", "z22"],
+ v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23", "z23"],
+ v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24", "z24"],
+ v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25", "z25"],
+ v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26", "z26"],
+ v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27", "z27"],
+ v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28", "z28"],
+ v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"],
+ v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"],
+ v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"],
+ p0: preg = ["p0"],
+ p1: preg = ["p1"],
+ p2: preg = ["p2"],
+ p3: preg = ["p3"],
+ p4: preg = ["p4"],
+ p5: preg = ["p5"],
+ p6: preg = ["p6"],
+ p7: preg = ["p7"],
+ p8: preg = ["p8"],
+ p9: preg = ["p9"],
+ p10: preg = ["p10"],
+ p11: preg = ["p11"],
+ p12: preg = ["p12"],
+ p13: preg = ["p13"],
+ p14: preg = ["p14"],
+ p15: preg = ["p15"],
+ ffr: preg = ["ffr"],
+ #error = ["x19", "w19"] =>
+ "x19 is used internally by LLVM and cannot be used as an operand for inline asm",
+ #error = ["x29", "w29", "fp", "wfp"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ #error = ["sp", "wsp"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["xzr", "wzr"] =>
+ "the zero register cannot be used as an operand for inline asm",
+ }
+}
+
+impl AArch64InlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ modifier: Option<char>,
+ ) -> fmt::Result {
+ let (prefix, index) = if (self as u32) < Self::v0 as u32 {
+ (modifier.unwrap_or('x'), self as u32 - Self::x0 as u32)
+ } else {
+ (modifier.unwrap_or('v'), self as u32 - Self::v0 as u32)
+ };
+ assert!(index < 32);
+ write!(out, "{}{}", prefix, index)
+ }
+}
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
new file mode 100644
index 000000000..0db3eb6fc
--- /dev/null
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -0,0 +1,340 @@
+use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::{RelocModel, Target};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_macros::HashStable_Generic;
+use rustc_span::{sym, Symbol};
+use std::fmt;
+
+def_reg_class! {
+ Arm ArmInlineAsmRegClass {
+ reg,
+ sreg,
+ sreg_low16,
+ dreg,
+ dreg_low16,
+ dreg_low8,
+ qreg,
+ qreg_low8,
+ qreg_low4,
+ }
+}
+
+impl ArmInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ match self {
+ Self::qreg | Self::qreg_low8 | Self::qreg_low4 => &['e', 'f'],
+ _ => &[],
+ }
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg => types! { _: I8, I16, I32, F32; },
+ Self::sreg | Self::sreg_low16 => types! { vfp2: I32, F32; },
+ Self::dreg_low16 | Self::dreg_low8 => types! {
+ vfp2: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2);
+ },
+ Self::dreg => types! {
+ d32: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2);
+ },
+ Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! {
+ neon: VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4);
+ },
+ }
+ }
+}
+
+// This uses the same logic as useR7AsFramePointer in LLVM
+fn frame_pointer_is_r7(target_features: &FxHashSet<Symbol>, target: &Target) -> bool {
+ target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode))
+}
+
+fn frame_pointer_r11(
+ arch: InlineAsmArch,
+ reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ is_clobber: bool,
+) -> Result<(), &'static str> {
+ not_thumb1(arch, reloc_model, target_features, target, is_clobber)?;
+
+ if !frame_pointer_is_r7(target_features, target) {
+ Err("the frame pointer (r11) cannot be used as an operand for inline asm")
+ } else {
+ Ok(())
+ }
+}
+
+fn frame_pointer_r7(
+ _arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ if frame_pointer_is_r7(target_features, target) {
+ Err("the frame pointer (r7) cannot be used as an operand for inline asm")
+ } else {
+ Ok(())
+ }
+}
+
+fn not_thumb1(
+ _arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ _target: &Target,
+ is_clobber: bool,
+) -> Result<(), &'static str> {
+ if !is_clobber
+ && target_features.contains(&sym::thumb_mode)
+ && !target_features.contains(&sym::thumb2)
+ {
+ Err("high registers (r8+) can only be used as clobbers in Thumb-1 code")
+ } else {
+ Ok(())
+ }
+}
+
+fn reserved_r9(
+ arch: InlineAsmArch,
+ reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ is_clobber: bool,
+) -> Result<(), &'static str> {
+ not_thumb1(arch, reloc_model, target_features, target, is_clobber)?;
+
+ match reloc_model {
+ RelocModel::Rwpi | RelocModel::RopiRwpi => {
+ Err("the RWPI static base register (r9) cannot be used as an operand for inline asm")
+ }
+ _ => Ok(()),
+ }
+}
+
+def_regs! {
+ Arm ArmInlineAsmReg ArmInlineAsmRegClass {
+ r0: reg = ["r0", "a1"],
+ r1: reg = ["r1", "a2"],
+ r2: reg = ["r2", "a3"],
+ r3: reg = ["r3", "a4"],
+ r4: reg = ["r4", "v1"],
+ r5: reg = ["r5", "v2"],
+ r7: reg = ["r7", "v4"] % frame_pointer_r7,
+ r8: reg = ["r8", "v5"] % not_thumb1,
+ r9: reg = ["r9", "v6", "rfp"] % reserved_r9,
+ r10: reg = ["r10", "sl"] % not_thumb1,
+ r11: reg = ["r11", "fp"] % frame_pointer_r11,
+ r12: reg = ["r12", "ip"] % not_thumb1,
+ r14: reg = ["r14", "lr"] % not_thumb1,
+ s0: sreg, sreg_low16 = ["s0"],
+ s1: sreg, sreg_low16 = ["s1"],
+ s2: sreg, sreg_low16 = ["s2"],
+ s3: sreg, sreg_low16 = ["s3"],
+ s4: sreg, sreg_low16 = ["s4"],
+ s5: sreg, sreg_low16 = ["s5"],
+ s6: sreg, sreg_low16 = ["s6"],
+ s7: sreg, sreg_low16 = ["s7"],
+ s8: sreg, sreg_low16 = ["s8"],
+ s9: sreg, sreg_low16 = ["s9"],
+ s10: sreg, sreg_low16 = ["s10"],
+ s11: sreg, sreg_low16 = ["s11"],
+ s12: sreg, sreg_low16 = ["s12"],
+ s13: sreg, sreg_low16 = ["s13"],
+ s14: sreg, sreg_low16 = ["s14"],
+ s15: sreg, sreg_low16 = ["s15"],
+ s16: sreg = ["s16"],
+ s17: sreg = ["s17"],
+ s18: sreg = ["s18"],
+ s19: sreg = ["s19"],
+ s20: sreg = ["s20"],
+ s21: sreg = ["s21"],
+ s22: sreg = ["s22"],
+ s23: sreg = ["s23"],
+ s24: sreg = ["s24"],
+ s25: sreg = ["s25"],
+ s26: sreg = ["s26"],
+ s27: sreg = ["s27"],
+ s28: sreg = ["s28"],
+ s29: sreg = ["s29"],
+ s30: sreg = ["s30"],
+ s31: sreg = ["s31"],
+ d0: dreg, dreg_low16, dreg_low8 = ["d0"],
+ d1: dreg, dreg_low16, dreg_low8 = ["d1"],
+ d2: dreg, dreg_low16, dreg_low8 = ["d2"],
+ d3: dreg, dreg_low16, dreg_low8 = ["d3"],
+ d4: dreg, dreg_low16, dreg_low8 = ["d4"],
+ d5: dreg, dreg_low16, dreg_low8 = ["d5"],
+ d6: dreg, dreg_low16, dreg_low8 = ["d6"],
+ d7: dreg, dreg_low16, dreg_low8 = ["d7"],
+ d8: dreg, dreg_low16 = ["d8"],
+ d9: dreg, dreg_low16 = ["d9"],
+ d10: dreg, dreg_low16 = ["d10"],
+ d11: dreg, dreg_low16 = ["d11"],
+ d12: dreg, dreg_low16 = ["d12"],
+ d13: dreg, dreg_low16 = ["d13"],
+ d14: dreg, dreg_low16 = ["d14"],
+ d15: dreg, dreg_low16 = ["d15"],
+ d16: dreg = ["d16"],
+ d17: dreg = ["d17"],
+ d18: dreg = ["d18"],
+ d19: dreg = ["d19"],
+ d20: dreg = ["d20"],
+ d21: dreg = ["d21"],
+ d22: dreg = ["d22"],
+ d23: dreg = ["d23"],
+ d24: dreg = ["d24"],
+ d25: dreg = ["d25"],
+ d26: dreg = ["d26"],
+ d27: dreg = ["d27"],
+ d28: dreg = ["d28"],
+ d29: dreg = ["d29"],
+ d30: dreg = ["d30"],
+ d31: dreg = ["d31"],
+ q0: qreg, qreg_low8, qreg_low4 = ["q0"],
+ q1: qreg, qreg_low8, qreg_low4 = ["q1"],
+ q2: qreg, qreg_low8, qreg_low4 = ["q2"],
+ q3: qreg, qreg_low8, qreg_low4 = ["q3"],
+ q4: qreg, qreg_low8 = ["q4"],
+ q5: qreg, qreg_low8 = ["q5"],
+ q6: qreg, qreg_low8 = ["q6"],
+ q7: qreg, qreg_low8 = ["q7"],
+ q8: qreg = ["q8"],
+ q9: qreg = ["q9"],
+ q10: qreg = ["q10"],
+ q11: qreg = ["q11"],
+ q12: qreg = ["q12"],
+ q13: qreg = ["q13"],
+ q14: qreg = ["q14"],
+ q15: qreg = ["q15"],
+ #error = ["r6", "v3"] =>
+ "r6 is used internally by LLVM and cannot be used as an operand for inline asm",
+ #error = ["r13", "sp"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["r15", "pc"] =>
+ "the program pointer cannot be used as an operand for inline asm",
+ }
+}
+
+impl ArmInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ modifier: Option<char>,
+ ) -> fmt::Result {
+ // Only qreg is allowed to have modifiers. This should have been
+ // validated already by now.
+ if let Some(modifier) = modifier {
+ let index = self as u32 - Self::q0 as u32;
+ assert!(index < 16);
+ let index = index * 2 + (modifier == 'f') as u32;
+ write!(out, "d{}", index)
+ } else {
+ out.write_str(self.name())
+ }
+ }
+
+ pub fn overlapping_regs(self, mut cb: impl FnMut(ArmInlineAsmReg)) {
+ cb(self);
+
+ macro_rules! reg_conflicts {
+ (
+ $(
+ $q:ident : $d0:ident $d1:ident : $s0:ident $s1:ident $s2:ident $s3:ident
+ ),*;
+ $(
+ $q_high:ident : $d0_high:ident $d1_high:ident
+ ),*;
+ ) => {
+ match self {
+ $(
+ Self::$q => {
+ cb(Self::$d0);
+ cb(Self::$d1);
+ cb(Self::$s0);
+ cb(Self::$s1);
+ cb(Self::$s2);
+ cb(Self::$s3);
+ }
+ Self::$d0 => {
+ cb(Self::$q);
+ cb(Self::$s0);
+ cb(Self::$s1);
+ }
+ Self::$d1 => {
+ cb(Self::$q);
+ cb(Self::$s2);
+ cb(Self::$s3);
+ }
+ Self::$s0 | Self::$s1 => {
+ cb(Self::$q);
+ cb(Self::$d0);
+ }
+ Self::$s2 | Self::$s3 => {
+ cb(Self::$q);
+ cb(Self::$d1);
+ }
+ )*
+ $(
+ Self::$q_high => {
+ cb(Self::$d0_high);
+ cb(Self::$d1_high);
+ }
+ Self::$d0_high | Self::$d1_high => {
+ cb(Self::$q_high);
+ }
+ )*
+ _ => {},
+ }
+ };
+ }
+
+ // ARM's floating-point register file is interesting in that it can be
+ // viewed as 16 128-bit registers, 32 64-bit registers or 32 32-bit
+ // registers. Because these views overlap, the registers of different
+ // widths will conflict (e.g. d0 overlaps with s0 and s1, and q1
+ // overlaps with d2 and d3).
+ //
+ // See section E1.3.1 of the ARM Architecture Reference Manual for
+ // ARMv8-A for more details.
+ reg_conflicts! {
+ q0 : d0 d1 : s0 s1 s2 s3,
+ q1 : d2 d3 : s4 s5 s6 s7,
+ q2 : d4 d5 : s8 s9 s10 s11,
+ q3 : d6 d7 : s12 s13 s14 s15,
+ q4 : d8 d9 : s16 s17 s18 s19,
+ q5 : d10 d11 : s20 s21 s22 s23,
+ q6 : d12 d13 : s24 s25 s26 s27,
+ q7 : d14 d15 : s28 s29 s30 s31;
+ q8 : d16 d17,
+ q9 : d18 d19,
+ q10 : d20 d21,
+ q11 : d22 d23,
+ q12 : d24 d25,
+ q13 : d26 d27,
+ q14 : d28 d29,
+ q15 : d30 d31;
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs
new file mode 100644
index 000000000..9a96a61f5
--- /dev/null
+++ b/compiler/rustc_target/src/asm/avr.rs
@@ -0,0 +1,197 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ Avr AvrInlineAsmRegClass {
+ reg,
+ reg_upper,
+ reg_pair,
+ reg_iw,
+ reg_ptr,
+ }
+}
+
+impl AvrInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] {
+ match self {
+ Self::reg_pair | Self::reg_iw | Self::reg_ptr => &['h', 'l'],
+ _ => &[],
+ }
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg => types! { _: I8; },
+ Self::reg_upper => types! { _: I8; },
+ Self::reg_pair => types! { _: I16; },
+ Self::reg_iw => types! { _: I16; },
+ Self::reg_ptr => types! { _: I16; },
+ }
+ }
+}
+
+def_regs! {
+ Avr AvrInlineAsmReg AvrInlineAsmRegClass {
+ r2: reg = ["r2"],
+ r3: reg = ["r3"],
+ r4: reg = ["r4"],
+ r5: reg = ["r5"],
+ r6: reg = ["r6"],
+ r7: reg = ["r7"],
+ r8: reg = ["r8"],
+ r9: reg = ["r9"],
+ r10: reg = ["r10"],
+ r11: reg = ["r11"],
+ r12: reg = ["r12"],
+ r13: reg = ["r13"],
+ r14: reg = ["r14"],
+ r15: reg = ["r15"],
+ r16: reg, reg_upper = ["r16"],
+ r17: reg, reg_upper = ["r17"],
+ r18: reg, reg_upper = ["r18"],
+ r19: reg, reg_upper = ["r19"],
+ r20: reg, reg_upper = ["r20"],
+ r21: reg, reg_upper = ["r21"],
+ r22: reg, reg_upper = ["r22"],
+ r23: reg, reg_upper = ["r23"],
+ r24: reg, reg_upper = ["r24"],
+ r25: reg, reg_upper = ["r25"],
+ r26: reg, reg_upper = ["r26", "XL"],
+ r27: reg, reg_upper = ["r27", "XH"],
+ r30: reg, reg_upper = ["r30", "ZL"],
+ r31: reg, reg_upper = ["r31", "ZH"],
+
+ r3r2: reg_pair = ["r3r2"],
+ r5r4: reg_pair = ["r5r4"],
+ r7r6: reg_pair = ["r7r6"],
+ r9r8: reg_pair = ["r9r8"],
+ r11r10: reg_pair = ["r11r10"],
+ r13r12: reg_pair = ["r13r12"],
+ r15r14: reg_pair = ["r15r14"],
+ r17r16: reg_pair = ["r17r16"],
+ r19r18: reg_pair = ["r19r18"],
+ r21r20: reg_pair = ["r21r20"],
+ r23r22: reg_pair = ["r23r22"],
+
+ r25r24: reg_iw, reg_pair = ["r25r24"],
+
+ X: reg_ptr, reg_iw, reg_pair = ["r27r26", "X"],
+ Z: reg_ptr, reg_iw, reg_pair = ["r31r30", "Z"],
+
+ #error = ["Y", "YL", "YH"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ #error = ["SP", "SPL", "SPH"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["r0", "r1", "r1r0"] =>
+ "r0 and r1 are not available due to an issue in LLVM",
+ }
+}
+
+macro_rules! emit_pairs {
+ (
+ $self:ident $modifier:ident,
+ $($pair:ident $name:literal $hi:literal $lo:literal,)*
+ ) => {
+ match ($self, $modifier) {
+ $(
+ (AvrInlineAsmReg::$pair, Some('h')) => $hi,
+ (AvrInlineAsmReg::$pair, Some('l')) => $lo,
+ (AvrInlineAsmReg::$pair, _) => $name,
+ )*
+ _ => $self.name(),
+ }
+ };
+}
+
+impl AvrInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ modifier: Option<char>,
+ ) -> fmt::Result {
+ let name = emit_pairs! {
+ self modifier,
+ Z "Z" "ZH" "ZL",
+ X "X" "XH" "XL",
+ r25r24 "r25:r24" "r25" "r24",
+ r23r22 "r23:r22" "r23" "r22",
+ r21r20 "r21:r20" "r21" "r20",
+ r19r18 "r19:r18" "r19" "r18",
+ r17r16 "r17:r16" "r17" "r16",
+ r15r14 "r15:r14" "r15" "r14",
+ r13r12 "r13:r12" "r13" "r12",
+ r11r10 "r11:r10" "r11" "r10",
+ r9r8 "r9:r8" "r9" "r8",
+ r7r6 "r7:r6" "r7" "r6",
+ r5r4 "r5:r4" "r5" "r4",
+ r3r2 "r3:r2" "r3" "r2",
+ };
+ out.write_str(name)
+ }
+
+ pub fn overlapping_regs(self, mut cb: impl FnMut(AvrInlineAsmReg)) {
+ cb(self);
+
+ macro_rules! reg_conflicts {
+ (
+ $(
+ $pair:ident : $hi:ident $lo:ident,
+ )*
+ ) => {
+ match self {
+ $(
+ Self::$pair => {
+ cb(Self::$hi);
+ cb(Self::$lo);
+ }
+ Self::$hi => {
+ cb(Self::$pair);
+ }
+ Self::$lo => {
+ cb(Self::$pair);
+ }
+ )*
+ }
+ };
+ }
+
+ reg_conflicts! {
+ Z : r31 r30,
+ X : r27 r26,
+ r25r24 : r25 r24,
+ r23r22 : r23 r22,
+ r21r20 : r21 r20,
+ r19r18 : r19 r18,
+ r17r16 : r17 r16,
+ r15r14 : r15 r14,
+ r13r12 : r13 r12,
+ r11r10 : r11 r10,
+ r9r8 : r9 r8,
+ r7r6 : r7 r6,
+ r5r4 : r5 r4,
+ r3r2 : r3 r2,
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs
new file mode 100644
index 000000000..3b03766a0
--- /dev/null
+++ b/compiler/rustc_target/src/asm/bpf.rs
@@ -0,0 +1,118 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ Bpf BpfInlineAsmRegClass {
+ reg,
+ wreg,
+ }
+}
+
+impl BpfInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg => types! { _: I8, I16, I32, I64; },
+ Self::wreg => types! { alu32: I8, I16, I32; },
+ }
+ }
+}
+
+def_regs! {
+ Bpf BpfInlineAsmReg BpfInlineAsmRegClass {
+ r0: reg = ["r0"],
+ r1: reg = ["r1"],
+ r2: reg = ["r2"],
+ r3: reg = ["r3"],
+ r4: reg = ["r4"],
+ r5: reg = ["r5"],
+ r6: reg = ["r6"],
+ r7: reg = ["r7"],
+ r8: reg = ["r8"],
+ r9: reg = ["r9"],
+ w0: wreg = ["w0"],
+ w1: wreg = ["w1"],
+ w2: wreg = ["w2"],
+ w3: wreg = ["w3"],
+ w4: wreg = ["w4"],
+ w5: wreg = ["w5"],
+ w6: wreg = ["w6"],
+ w7: wreg = ["w7"],
+ w8: wreg = ["w8"],
+ w9: wreg = ["w9"],
+
+ #error = ["r10", "w10"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ }
+}
+
+impl BpfInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ _modifier: Option<char>,
+ ) -> fmt::Result {
+ out.write_str(self.name())
+ }
+
+ pub fn overlapping_regs(self, mut cb: impl FnMut(BpfInlineAsmReg)) {
+ cb(self);
+
+ macro_rules! reg_conflicts {
+ (
+ $(
+ $r:ident : $w:ident
+ ),*
+ ) => {
+ match self {
+ $(
+ Self::$r => {
+ cb(Self::$w);
+ }
+ Self::$w => {
+ cb(Self::$r);
+ }
+ )*
+ }
+ };
+ }
+
+ reg_conflicts! {
+ r0 : w0,
+ r1 : w1,
+ r2 : w2,
+ r3 : w3,
+ r4 : w4,
+ r5 : w5,
+ r6 : w6,
+ r7 : w7,
+ r8 : w8,
+ r9 : w9
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs
new file mode 100644
index 000000000..d20270ac9
--- /dev/null
+++ b/compiler/rustc_target/src/asm/hexagon.rs
@@ -0,0 +1,95 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ Hexagon HexagonInlineAsmRegClass {
+ reg,
+ }
+}
+
+impl HexagonInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg => types! { _: I8, I16, I32, F32; },
+ }
+ }
+}
+
+def_regs! {
+ Hexagon HexagonInlineAsmReg HexagonInlineAsmRegClass {
+ r0: reg = ["r0"],
+ r1: reg = ["r1"],
+ r2: reg = ["r2"],
+ r3: reg = ["r3"],
+ r4: reg = ["r4"],
+ r5: reg = ["r5"],
+ r6: reg = ["r6"],
+ r7: reg = ["r7"],
+ r8: reg = ["r8"],
+ r9: reg = ["r9"],
+ r10: reg = ["r10"],
+ r11: reg = ["r11"],
+ r12: reg = ["r12"],
+ r13: reg = ["r13"],
+ r14: reg = ["r14"],
+ r15: reg = ["r15"],
+ r16: reg = ["r16"],
+ r17: reg = ["r17"],
+ r18: reg = ["r18"],
+ r20: reg = ["r20"],
+ r21: reg = ["r21"],
+ r22: reg = ["r22"],
+ r23: reg = ["r23"],
+ r24: reg = ["r24"],
+ r25: reg = ["r25"],
+ r26: reg = ["r26"],
+ r27: reg = ["r27"],
+ r28: reg = ["r28"],
+ #error = ["r19"] =>
+ "r19 is used internally by LLVM and cannot be used as an operand for inline asm",
+ #error = ["r29", "sp"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["r30", "fr"] =>
+ "the frame register cannot be used as an operand for inline asm",
+ #error = ["r31", "lr"] =>
+ "the link register cannot be used as an operand for inline asm",
+ }
+}
+
+impl HexagonInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ _modifier: Option<char>,
+ ) -> fmt::Result {
+ out.write_str(self.name())
+ }
+
+ pub fn overlapping_regs(self, mut _cb: impl FnMut(HexagonInlineAsmReg)) {}
+}
diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs
new file mode 100644
index 000000000..4e7c2eb1b
--- /dev/null
+++ b/compiler/rustc_target/src/asm/mips.rs
@@ -0,0 +1,135 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ Mips MipsInlineAsmRegClass {
+ reg,
+ freg,
+ }
+}
+
+impl MipsInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match (self, arch) {
+ (Self::reg, InlineAsmArch::Mips64) => types! { _: I8, I16, I32, I64, F32, F64; },
+ (Self::reg, _) => types! { _: I8, I16, I32, F32; },
+ (Self::freg, _) => types! { _: F32, F64; },
+ }
+ }
+}
+
+// The reserved registers are somewhat taken from
+// <https://github.com/llvm/llvm-project/blob/deb8f8bcf31540c657716ea5242183b0792702a1/llvm/lib/Target/Mips/MipsRegisterInfo.cpp#L150>.
+def_regs! {
+ Mips MipsInlineAsmReg MipsInlineAsmRegClass {
+ r2: reg = ["$2"],
+ r3: reg = ["$3"],
+ r4: reg = ["$4"],
+ r5: reg = ["$5"],
+ r6: reg = ["$6"],
+ r7: reg = ["$7"],
+ // FIXME: Reserve $t0, $t1 if in mips16 mode.
+ r8: reg = ["$8"],
+ r9: reg = ["$9"],
+ r10: reg = ["$10"],
+ r11: reg = ["$11"],
+ r12: reg = ["$12"],
+ r13: reg = ["$13"],
+ r14: reg = ["$14"],
+ r15: reg = ["$15"],
+ r16: reg = ["$16"],
+ r17: reg = ["$17"],
+ r18: reg = ["$18"],
+ r19: reg = ["$19"],
+ r20: reg = ["$20"],
+ r21: reg = ["$21"],
+ r22: reg = ["$22"],
+ r23: reg = ["$23"],
+ r24: reg = ["$24"],
+ r25: reg = ["$25"],
+ f0: freg = ["$f0"],
+ f1: freg = ["$f1"],
+ f2: freg = ["$f2"],
+ f3: freg = ["$f3"],
+ f4: freg = ["$f4"],
+ f5: freg = ["$f5"],
+ f6: freg = ["$f6"],
+ f7: freg = ["$f7"],
+ f8: freg = ["$f8"],
+ f9: freg = ["$f9"],
+ f10: freg = ["$f10"],
+ f11: freg = ["$f11"],
+ f12: freg = ["$f12"],
+ f13: freg = ["$f13"],
+ f14: freg = ["$f14"],
+ f15: freg = ["$f15"],
+ f16: freg = ["$f16"],
+ f17: freg = ["$f17"],
+ f18: freg = ["$f18"],
+ f19: freg = ["$f19"],
+ f20: freg = ["$f20"],
+ f21: freg = ["$f21"],
+ f22: freg = ["$f22"],
+ f23: freg = ["$f23"],
+ f24: freg = ["$f24"],
+ f25: freg = ["$f25"],
+ f26: freg = ["$f26"],
+ f27: freg = ["$f27"],
+ f28: freg = ["$f28"],
+ f29: freg = ["$f29"],
+ f30: freg = ["$f30"],
+ f31: freg = ["$f31"],
+ #error = ["$0"] =>
+ "constant zero cannot be used as an operand for inline asm",
+ #error = ["$1"] =>
+ "reserved for assembler (Assembler Temp)",
+ #error = ["$26"] =>
+ "OS-reserved register cannot be used as an operand for inline asm",
+ #error = ["$27"] =>
+ "OS-reserved register cannot be used as an operand for inline asm",
+ #error = ["$28"] =>
+ "the global pointer cannot be used as an operand for inline asm",
+ #error = ["$29"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["$30"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ #error = ["$31"] =>
+ "the return address register cannot be used as an operand for inline asm",
+ }
+}
+
+impl MipsInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ _modifier: Option<char>,
+ ) -> fmt::Result {
+ out.write_str(self.name())
+ }
+}
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
new file mode 100644
index 000000000..65d2cd64b
--- /dev/null
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -0,0 +1,976 @@
+use crate::spec::Target;
+use crate::{abi::Size, spec::RelocModel};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+use std::str::FromStr;
+
+macro_rules! def_reg_class {
+ ($arch:ident $arch_regclass:ident {
+ $(
+ $class:ident,
+ )*
+ }) => {
+ #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, HashStable_Generic)]
+ #[allow(non_camel_case_types)]
+ pub enum $arch_regclass {
+ $($class,)*
+ }
+
+ impl $arch_regclass {
+ pub fn name(self) -> rustc_span::Symbol {
+ match self {
+ $(Self::$class => rustc_span::symbol::sym::$class,)*
+ }
+ }
+
+ pub fn parse(name: rustc_span::Symbol) -> Result<Self, &'static str> {
+ match name {
+ $(
+ rustc_span::sym::$class => Ok(Self::$class),
+ )*
+ _ => Err("unknown register class"),
+ }
+ }
+ }
+
+ pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap<
+ super::InlineAsmRegClass,
+ rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>,
+ > {
+ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+ use super::InlineAsmRegClass;
+ let mut map = FxHashMap::default();
+ $(
+ map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default());
+ )*
+ map
+ }
+ }
+}
+
+macro_rules! def_regs {
+ ($arch:ident $arch_reg:ident $arch_regclass:ident {
+ $(
+ $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?,
+ )*
+ $(
+ #error = [$($bad_reg:literal),+] => $error:literal,
+ )*
+ }) => {
+ #[allow(unreachable_code)]
+ #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, HashStable_Generic)]
+ #[allow(non_camel_case_types)]
+ pub enum $arch_reg {
+ $($reg,)*
+ }
+
+ impl $arch_reg {
+ pub fn name(self) -> &'static str {
+ match self {
+ $(Self::$reg => $reg_name,)*
+ }
+ }
+
+ pub fn reg_class(self) -> $arch_regclass {
+ match self {
+ $(Self::$reg => $arch_regclass::$class,)*
+ }
+ }
+
+ pub fn parse(name: &str) -> Result<Self, &'static str> {
+ match name {
+ $(
+ $($alias)|* | $reg_name => Ok(Self::$reg),
+ )*
+ $(
+ $($bad_reg)|* => Err($error),
+ )*
+ _ => Err("unknown register"),
+ }
+ }
+
+ pub fn validate(self,
+ _arch: super::InlineAsmArch,
+ _reloc_model: crate::spec::RelocModel,
+ _target_features: &rustc_data_structures::fx::FxHashSet<Symbol>,
+ _target: &crate::spec::Target,
+ _is_clobber: bool,
+ ) -> Result<(), &'static str> {
+ match self {
+ $(
+ Self::$reg => {
+ $($filter(
+ _arch,
+ _reloc_model,
+ _target_features,
+ _target,
+ _is_clobber
+ )?;)?
+ Ok(())
+ }
+ )*
+ }
+ }
+ }
+
+ pub(super) fn fill_reg_map(
+ _arch: super::InlineAsmArch,
+ _reloc_model: crate::spec::RelocModel,
+ _target_features: &rustc_data_structures::fx::FxHashSet<Symbol>,
+ _target: &crate::spec::Target,
+ _map: &mut rustc_data_structures::fx::FxHashMap<
+ super::InlineAsmRegClass,
+ rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>,
+ >,
+ ) {
+ #[allow(unused_imports)]
+ use super::{InlineAsmReg, InlineAsmRegClass};
+ $(
+ if $($filter(_arch, _reloc_model, _target_features, _target, false).is_ok() &&)? true {
+ if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) {
+ set.insert(InlineAsmReg::$arch($arch_reg::$reg));
+ }
+ $(
+ if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) {
+ set.insert(InlineAsmReg::$arch($arch_reg::$reg));
+ }
+ )*
+ }
+ )*
+ }
+ }
+}
+
+macro_rules! types {
+ (
+ $(_ : $($ty:expr),+;)?
+ $($feature:ident: $($ty2:expr),+;)*
+ ) => {
+ {
+ use super::InlineAsmType::*;
+ &[
+ $($(
+ ($ty, None),
+ )*)?
+ $($(
+ ($ty2, Some(rustc_span::sym::$feature)),
+ )*)*
+ ]
+ }
+ };
+}
+
+mod aarch64;
+mod arm;
+mod avr;
+mod bpf;
+mod hexagon;
+mod mips;
+mod msp430;
+mod nvptx;
+mod powerpc;
+mod riscv;
+mod s390x;
+mod spirv;
+mod wasm;
+mod x86;
+
+pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass};
+pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass};
+pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass};
+pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass};
+pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass};
+pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass};
+pub use msp430::{Msp430InlineAsmReg, Msp430InlineAsmRegClass};
+pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass};
+pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass};
+pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass};
+pub use s390x::{S390xInlineAsmReg, S390xInlineAsmRegClass};
+pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass};
+pub use wasm::{WasmInlineAsmReg, WasmInlineAsmRegClass};
+pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass};
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)]
+pub enum InlineAsmArch {
+ X86,
+ X86_64,
+ Arm,
+ AArch64,
+ RiscV32,
+ RiscV64,
+ Nvptx64,
+ Hexagon,
+ Mips,
+ Mips64,
+ PowerPC,
+ PowerPC64,
+ S390x,
+ SpirV,
+ Wasm32,
+ Wasm64,
+ Bpf,
+ Avr,
+ Msp430,
+}
+
+impl FromStr for InlineAsmArch {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<InlineAsmArch, ()> {
+ match s {
+ "x86" => Ok(Self::X86),
+ "x86_64" => Ok(Self::X86_64),
+ "arm" => Ok(Self::Arm),
+ "aarch64" => Ok(Self::AArch64),
+ "riscv32" => Ok(Self::RiscV32),
+ "riscv64" => Ok(Self::RiscV64),
+ "nvptx64" => Ok(Self::Nvptx64),
+ "powerpc" => Ok(Self::PowerPC),
+ "powerpc64" => Ok(Self::PowerPC64),
+ "hexagon" => Ok(Self::Hexagon),
+ "mips" => Ok(Self::Mips),
+ "mips64" => Ok(Self::Mips64),
+ "s390x" => Ok(Self::S390x),
+ "spirv" => Ok(Self::SpirV),
+ "wasm32" => Ok(Self::Wasm32),
+ "wasm64" => Ok(Self::Wasm64),
+ "bpf" => Ok(Self::Bpf),
+ "avr" => Ok(Self::Avr),
+ "msp430" => Ok(Self::Msp430),
+ _ => Err(()),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
+pub enum InlineAsmReg {
+ X86(X86InlineAsmReg),
+ Arm(ArmInlineAsmReg),
+ AArch64(AArch64InlineAsmReg),
+ RiscV(RiscVInlineAsmReg),
+ Nvptx(NvptxInlineAsmReg),
+ PowerPC(PowerPCInlineAsmReg),
+ Hexagon(HexagonInlineAsmReg),
+ Mips(MipsInlineAsmReg),
+ S390x(S390xInlineAsmReg),
+ SpirV(SpirVInlineAsmReg),
+ Wasm(WasmInlineAsmReg),
+ Bpf(BpfInlineAsmReg),
+ Avr(AvrInlineAsmReg),
+ Msp430(Msp430InlineAsmReg),
+ // Placeholder for invalid register constraints for the current target
+ Err,
+}
+
+impl InlineAsmReg {
+ pub fn name(self) -> &'static str {
+ match self {
+ Self::X86(r) => r.name(),
+ Self::Arm(r) => r.name(),
+ Self::AArch64(r) => r.name(),
+ Self::RiscV(r) => r.name(),
+ Self::PowerPC(r) => r.name(),
+ Self::Hexagon(r) => r.name(),
+ Self::Mips(r) => r.name(),
+ Self::S390x(r) => r.name(),
+ Self::Bpf(r) => r.name(),
+ Self::Avr(r) => r.name(),
+ Self::Msp430(r) => r.name(),
+ Self::Err => "<reg>",
+ }
+ }
+
+ pub fn reg_class(self) -> InlineAsmRegClass {
+ match self {
+ Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()),
+ Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()),
+ Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()),
+ Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()),
+ Self::PowerPC(r) => InlineAsmRegClass::PowerPC(r.reg_class()),
+ Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()),
+ Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()),
+ Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()),
+ Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()),
+ Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()),
+ Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()),
+ Self::Err => InlineAsmRegClass::Err,
+ }
+ }
+
+ pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> {
+ // FIXME: use direct symbol comparison for register names
+ // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`.
+ let name = name.as_str();
+ Ok(match arch {
+ InlineAsmArch::X86 | InlineAsmArch::X86_64 => Self::X86(X86InlineAsmReg::parse(name)?),
+ InlineAsmArch::Arm => Self::Arm(ArmInlineAsmReg::parse(name)?),
+ InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmReg::parse(name)?),
+ InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
+ Self::RiscV(RiscVInlineAsmReg::parse(name)?)
+ }
+ InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmReg::parse(name)?),
+ InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {
+ Self::PowerPC(PowerPCInlineAsmReg::parse(name)?)
+ }
+ InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmReg::parse(name)?),
+ InlineAsmArch::Mips | InlineAsmArch::Mips64 => {
+ Self::Mips(MipsInlineAsmReg::parse(name)?)
+ }
+ InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse(name)?),
+ InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse(name)?),
+ InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {
+ Self::Wasm(WasmInlineAsmReg::parse(name)?)
+ }
+ InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmReg::parse(name)?),
+ InlineAsmArch::Avr => Self::Avr(AvrInlineAsmReg::parse(name)?),
+ InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmReg::parse(name)?),
+ })
+ }
+
+ pub fn validate(
+ self,
+ arch: InlineAsmArch,
+ reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ is_clobber: bool,
+ ) -> Result<(), &'static str> {
+ match self {
+ Self::X86(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::Arm(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::AArch64(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::RiscV(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::PowerPC(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::Hexagon(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::Mips(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::S390x(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::Bpf(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::Avr(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::Msp430(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
+ Self::Err => unreachable!(),
+ }
+ }
+
+ // NOTE: This function isn't used at the moment, but is needed to support
+ // falling back to an external assembler.
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ arch: InlineAsmArch,
+ modifier: Option<char>,
+ ) -> fmt::Result {
+ match self {
+ Self::X86(r) => r.emit(out, arch, modifier),
+ Self::Arm(r) => r.emit(out, arch, modifier),
+ Self::AArch64(r) => r.emit(out, arch, modifier),
+ Self::RiscV(r) => r.emit(out, arch, modifier),
+ Self::PowerPC(r) => r.emit(out, arch, modifier),
+ Self::Hexagon(r) => r.emit(out, arch, modifier),
+ Self::Mips(r) => r.emit(out, arch, modifier),
+ Self::S390x(r) => r.emit(out, arch, modifier),
+ Self::Bpf(r) => r.emit(out, arch, modifier),
+ Self::Avr(r) => r.emit(out, arch, modifier),
+ Self::Msp430(r) => r.emit(out, arch, modifier),
+ Self::Err => unreachable!("Use of InlineAsmReg::Err"),
+ }
+ }
+
+ pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) {
+ match self {
+ Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))),
+ Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))),
+ Self::AArch64(_) => cb(self),
+ Self::RiscV(_) => cb(self),
+ Self::PowerPC(r) => r.overlapping_regs(|r| cb(Self::PowerPC(r))),
+ Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))),
+ Self::Mips(_) => cb(self),
+ Self::S390x(_) => cb(self),
+ Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))),
+ Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))),
+ Self::Msp430(_) => cb(self),
+ Self::Err => unreachable!("Use of InlineAsmReg::Err"),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
+pub enum InlineAsmRegClass {
+ X86(X86InlineAsmRegClass),
+ Arm(ArmInlineAsmRegClass),
+ AArch64(AArch64InlineAsmRegClass),
+ RiscV(RiscVInlineAsmRegClass),
+ Nvptx(NvptxInlineAsmRegClass),
+ PowerPC(PowerPCInlineAsmRegClass),
+ Hexagon(HexagonInlineAsmRegClass),
+ Mips(MipsInlineAsmRegClass),
+ S390x(S390xInlineAsmRegClass),
+ SpirV(SpirVInlineAsmRegClass),
+ Wasm(WasmInlineAsmRegClass),
+ Bpf(BpfInlineAsmRegClass),
+ Avr(AvrInlineAsmRegClass),
+ Msp430(Msp430InlineAsmRegClass),
+ // Placeholder for invalid register constraints for the current target
+ Err,
+}
+
+impl InlineAsmRegClass {
+ pub fn name(self) -> Symbol {
+ match self {
+ Self::X86(r) => r.name(),
+ Self::Arm(r) => r.name(),
+ Self::AArch64(r) => r.name(),
+ Self::RiscV(r) => r.name(),
+ Self::Nvptx(r) => r.name(),
+ Self::PowerPC(r) => r.name(),
+ Self::Hexagon(r) => r.name(),
+ Self::Mips(r) => r.name(),
+ Self::S390x(r) => r.name(),
+ Self::SpirV(r) => r.name(),
+ Self::Wasm(r) => r.name(),
+ Self::Bpf(r) => r.name(),
+ Self::Avr(r) => r.name(),
+ Self::Msp430(r) => r.name(),
+ Self::Err => rustc_span::symbol::sym::reg,
+ }
+ }
+
+ /// Returns a suggested register class to use for this type. This is called
+ /// when `supported_types` fails to give a better error
+ /// message to the user.
+ pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> {
+ match self {
+ Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86),
+ Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm),
+ Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64),
+ Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV),
+ Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx),
+ Self::PowerPC(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::PowerPC),
+ Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon),
+ Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips),
+ Self::S390x(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::S390x),
+ Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV),
+ Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm),
+ Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf),
+ Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr),
+ Self::Msp430(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Msp430),
+ Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
+ }
+ }
+
+ /// Returns a suggested template modifier to use for this type and an
+ /// example of a register named formatted with it.
+ ///
+ /// Such suggestions are useful if a type smaller than the full register
+ /// size is used and a modifier can be used to point to the subregister of
+ /// the correct size.
+ pub fn suggest_modifier(
+ self,
+ arch: InlineAsmArch,
+ ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ match self {
+ Self::X86(r) => r.suggest_modifier(arch, ty),
+ Self::Arm(r) => r.suggest_modifier(arch, ty),
+ Self::AArch64(r) => r.suggest_modifier(arch, ty),
+ Self::RiscV(r) => r.suggest_modifier(arch, ty),
+ Self::Nvptx(r) => r.suggest_modifier(arch, ty),
+ Self::PowerPC(r) => r.suggest_modifier(arch, ty),
+ Self::Hexagon(r) => r.suggest_modifier(arch, ty),
+ Self::Mips(r) => r.suggest_modifier(arch, ty),
+ Self::S390x(r) => r.suggest_modifier(arch, ty),
+ Self::SpirV(r) => r.suggest_modifier(arch, ty),
+ Self::Wasm(r) => r.suggest_modifier(arch, ty),
+ Self::Bpf(r) => r.suggest_modifier(arch, ty),
+ Self::Avr(r) => r.suggest_modifier(arch, ty),
+ Self::Msp430(r) => r.suggest_modifier(arch, ty),
+ Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
+ }
+ }
+
+ /// Returns the default modifier for this register and an example of a
+ /// register named formatted with it.
+ ///
+ /// This is only needed when the register class can suggest a modifier, so
+ /// that the user can be shown how to get the default behavior without a
+ /// warning.
+ pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ match self {
+ Self::X86(r) => r.default_modifier(arch),
+ Self::Arm(r) => r.default_modifier(arch),
+ Self::AArch64(r) => r.default_modifier(arch),
+ Self::RiscV(r) => r.default_modifier(arch),
+ Self::Nvptx(r) => r.default_modifier(arch),
+ Self::PowerPC(r) => r.default_modifier(arch),
+ Self::Hexagon(r) => r.default_modifier(arch),
+ Self::Mips(r) => r.default_modifier(arch),
+ Self::S390x(r) => r.default_modifier(arch),
+ Self::SpirV(r) => r.default_modifier(arch),
+ Self::Wasm(r) => r.default_modifier(arch),
+ Self::Bpf(r) => r.default_modifier(arch),
+ Self::Avr(r) => r.default_modifier(arch),
+ Self::Msp430(r) => r.default_modifier(arch),
+ Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
+ }
+ }
+
+ /// Returns a list of supported types for this register class, each with an
+ /// options target feature required to use this type.
+ pub fn supported_types(
+ self,
+ arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::X86(r) => r.supported_types(arch),
+ Self::Arm(r) => r.supported_types(arch),
+ Self::AArch64(r) => r.supported_types(arch),
+ Self::RiscV(r) => r.supported_types(arch),
+ Self::Nvptx(r) => r.supported_types(arch),
+ Self::PowerPC(r) => r.supported_types(arch),
+ Self::Hexagon(r) => r.supported_types(arch),
+ Self::Mips(r) => r.supported_types(arch),
+ Self::S390x(r) => r.supported_types(arch),
+ Self::SpirV(r) => r.supported_types(arch),
+ Self::Wasm(r) => r.supported_types(arch),
+ Self::Bpf(r) => r.supported_types(arch),
+ Self::Avr(r) => r.supported_types(arch),
+ Self::Msp430(r) => r.supported_types(arch),
+ Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
+ }
+ }
+
+ pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> {
+ Ok(match arch {
+ InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
+ Self::X86(X86InlineAsmRegClass::parse(name)?)
+ }
+ InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(name)?),
+ InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmRegClass::parse(name)?),
+ InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
+ Self::RiscV(RiscVInlineAsmRegClass::parse(name)?)
+ }
+ InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(name)?),
+ InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {
+ Self::PowerPC(PowerPCInlineAsmRegClass::parse(name)?)
+ }
+ InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmRegClass::parse(name)?),
+ InlineAsmArch::Mips | InlineAsmArch::Mips64 => {
+ Self::Mips(MipsInlineAsmRegClass::parse(name)?)
+ }
+ InlineAsmArch::S390x => Self::S390x(S390xInlineAsmRegClass::parse(name)?),
+ InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(name)?),
+ InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {
+ Self::Wasm(WasmInlineAsmRegClass::parse(name)?)
+ }
+ InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(name)?),
+ InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(name)?),
+ InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(name)?),
+ })
+ }
+
+ /// Returns the list of template modifiers that can be used with this
+ /// register class.
+ pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] {
+ match self {
+ Self::X86(r) => r.valid_modifiers(arch),
+ Self::Arm(r) => r.valid_modifiers(arch),
+ Self::AArch64(r) => r.valid_modifiers(arch),
+ Self::RiscV(r) => r.valid_modifiers(arch),
+ Self::Nvptx(r) => r.valid_modifiers(arch),
+ Self::PowerPC(r) => r.valid_modifiers(arch),
+ Self::Hexagon(r) => r.valid_modifiers(arch),
+ Self::Mips(r) => r.valid_modifiers(arch),
+ Self::S390x(r) => r.valid_modifiers(arch),
+ Self::SpirV(r) => r.valid_modifiers(arch),
+ Self::Wasm(r) => r.valid_modifiers(arch),
+ Self::Bpf(r) => r.valid_modifiers(arch),
+ Self::Avr(r) => r.valid_modifiers(arch),
+ Self::Msp430(r) => r.valid_modifiers(arch),
+ Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
+ }
+ }
+
+ /// Returns whether registers in this class can only be used as clobbers
+ /// and not as inputs/outputs.
+ pub fn is_clobber_only(self, arch: InlineAsmArch) -> bool {
+ self.supported_types(arch).is_empty()
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
+pub enum InlineAsmRegOrRegClass {
+ Reg(InlineAsmReg),
+ RegClass(InlineAsmRegClass),
+}
+
+impl InlineAsmRegOrRegClass {
+ pub fn reg_class(self) -> InlineAsmRegClass {
+ match self {
+ Self::Reg(r) => r.reg_class(),
+ Self::RegClass(r) => r,
+ }
+ }
+}
+
+impl fmt::Display for InlineAsmRegOrRegClass {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Reg(r) => write!(f, "\"{}\"", r.name()),
+ Self::RegClass(r) => write!(f, "{}", r.name()),
+ }
+ }
+}
+
+/// Set of types which can be used with a particular register class.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum InlineAsmType {
+ I8,
+ I16,
+ I32,
+ I64,
+ I128,
+ F32,
+ F64,
+ VecI8(u64),
+ VecI16(u64),
+ VecI32(u64),
+ VecI64(u64),
+ VecI128(u64),
+ VecF32(u64),
+ VecF64(u64),
+}
+
+impl InlineAsmType {
+ pub fn is_integer(self) -> bool {
+ matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128)
+ }
+
+ pub fn size(self) -> Size {
+ Size::from_bytes(match self {
+ Self::I8 => 1,
+ Self::I16 => 2,
+ Self::I32 => 4,
+ Self::I64 => 8,
+ Self::I128 => 16,
+ Self::F32 => 4,
+ Self::F64 => 8,
+ Self::VecI8(n) => n * 1,
+ Self::VecI16(n) => n * 2,
+ Self::VecI32(n) => n * 4,
+ Self::VecI64(n) => n * 8,
+ Self::VecI128(n) => n * 16,
+ Self::VecF32(n) => n * 4,
+ Self::VecF64(n) => n * 8,
+ })
+ }
+}
+
+impl fmt::Display for InlineAsmType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ Self::I8 => f.write_str("i8"),
+ Self::I16 => f.write_str("i16"),
+ Self::I32 => f.write_str("i32"),
+ Self::I64 => f.write_str("i64"),
+ Self::I128 => f.write_str("i128"),
+ Self::F32 => f.write_str("f32"),
+ Self::F64 => f.write_str("f64"),
+ Self::VecI8(n) => write!(f, "i8x{}", n),
+ Self::VecI16(n) => write!(f, "i16x{}", n),
+ Self::VecI32(n) => write!(f, "i32x{}", n),
+ Self::VecI64(n) => write!(f, "i64x{}", n),
+ Self::VecI128(n) => write!(f, "i128x{}", n),
+ Self::VecF32(n) => write!(f, "f32x{}", n),
+ Self::VecF64(n) => write!(f, "f64x{}", n),
+ }
+ }
+}
+
+/// Returns the full set of allocatable registers for a given architecture.
+///
+/// The registers are structured as a map containing the set of allocatable
+/// registers in each register class. A particular register may be allocatable
+/// from multiple register classes, in which case it will appear multiple times
+/// in the map.
+// NOTE: This function isn't used at the moment, but is needed to support
+// falling back to an external assembler.
+pub fn allocatable_registers(
+ arch: InlineAsmArch,
+ reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &crate::spec::Target,
+) -> FxHashMap<InlineAsmRegClass, FxHashSet<InlineAsmReg>> {
+ match arch {
+ InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
+ let mut map = x86::regclass_map();
+ x86::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Arm => {
+ let mut map = arm::regclass_map();
+ arm::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::AArch64 => {
+ let mut map = aarch64::regclass_map();
+ aarch64::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
+ let mut map = riscv::regclass_map();
+ riscv::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Nvptx64 => {
+ let mut map = nvptx::regclass_map();
+ nvptx::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {
+ let mut map = powerpc::regclass_map();
+ powerpc::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Hexagon => {
+ let mut map = hexagon::regclass_map();
+ hexagon::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Mips | InlineAsmArch::Mips64 => {
+ let mut map = mips::regclass_map();
+ mips::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::S390x => {
+ let mut map = s390x::regclass_map();
+ s390x::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::SpirV => {
+ let mut map = spirv::regclass_map();
+ spirv::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {
+ let mut map = wasm::regclass_map();
+ wasm::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Bpf => {
+ let mut map = bpf::regclass_map();
+ bpf::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Avr => {
+ let mut map = avr::regclass_map();
+ avr::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ InlineAsmArch::Msp430 => {
+ let mut map = msp430::regclass_map();
+ msp430::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
+ map
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
+pub enum InlineAsmClobberAbi {
+ X86,
+ X86_64Win,
+ X86_64SysV,
+ Arm,
+ AArch64,
+ AArch64NoX18,
+ RiscV,
+}
+
+impl InlineAsmClobberAbi {
+ /// Parses a clobber ABI for the given target, or returns a list of supported
+ /// clobber ABIs for the target.
+ pub fn parse(
+ arch: InlineAsmArch,
+ target: &Target,
+ name: Symbol,
+ ) -> Result<Self, &'static [&'static str]> {
+ let name = name.as_str();
+ match arch {
+ InlineAsmArch::X86 => match name {
+ "C" | "system" | "efiapi" | "cdecl" | "stdcall" | "fastcall" => {
+ Ok(InlineAsmClobberAbi::X86)
+ }
+ _ => Err(&["C", "system", "efiapi", "cdecl", "stdcall", "fastcall"]),
+ },
+ InlineAsmArch::X86_64 => match name {
+ "C" | "system" if !target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64SysV),
+ "C" | "system" if target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64Win),
+ "win64" | "efiapi" => Ok(InlineAsmClobberAbi::X86_64Win),
+ "sysv64" => Ok(InlineAsmClobberAbi::X86_64SysV),
+ _ => Err(&["C", "system", "efiapi", "win64", "sysv64"]),
+ },
+ InlineAsmArch::Arm => match name {
+ "C" | "system" | "efiapi" | "aapcs" => Ok(InlineAsmClobberAbi::Arm),
+ _ => Err(&["C", "system", "efiapi", "aapcs"]),
+ },
+ InlineAsmArch::AArch64 => match name {
+ "C" | "system" | "efiapi" => Ok(if aarch64::target_reserves_x18(target) {
+ InlineAsmClobberAbi::AArch64NoX18
+ } else {
+ InlineAsmClobberAbi::AArch64
+ }),
+ _ => Err(&["C", "system", "efiapi"]),
+ },
+ InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name {
+ "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::RiscV),
+ _ => Err(&["C", "system", "efiapi"]),
+ },
+ _ => Err(&[]),
+ }
+ }
+
+ /// Returns the set of registers which are clobbered by this ABI.
+ pub fn clobbered_regs(self) -> &'static [InlineAsmReg] {
+ macro_rules! clobbered_regs {
+ ($arch:ident $arch_reg:ident {
+ $(
+ $reg:ident,
+ )*
+ }) => {
+ &[
+ $(InlineAsmReg::$arch($arch_reg::$reg),)*
+ ]
+ };
+ }
+ match self {
+ InlineAsmClobberAbi::X86 => clobbered_regs! {
+ X86 X86InlineAsmReg {
+ ax, cx, dx,
+
+ xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
+
+ k0, k1, k2, k3, k4, k5, k6, k7,
+
+ mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
+ st0, st1, st2, st3, st4, st5, st6, st7,
+ }
+ },
+ InlineAsmClobberAbi::X86_64SysV => clobbered_regs! {
+ X86 X86InlineAsmReg {
+ ax, cx, dx, si, di, r8, r9, r10, r11,
+
+ xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
+ xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
+ zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
+ zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
+
+ k0, k1, k2, k3, k4, k5, k6, k7,
+
+ mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
+ st0, st1, st2, st3, st4, st5, st6, st7,
+ tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7,
+ }
+ },
+ InlineAsmClobberAbi::X86_64Win => clobbered_regs! {
+ X86 X86InlineAsmReg {
+ // rdi and rsi are callee-saved on windows
+ ax, cx, dx, r8, r9, r10, r11,
+
+ // xmm6-xmm15 are callee-saved on windows, but we need to
+ // mark them as clobbered anyways because the upper portions
+ // of ymm6-ymm15 are volatile.
+ xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
+ xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
+ zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
+ zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
+
+ k0, k1, k2, k3, k4, k5, k6, k7,
+
+ mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
+ st0, st1, st2, st3, st4, st5, st6, st7,
+ tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7,
+ }
+ },
+ InlineAsmClobberAbi::AArch64 => clobbered_regs! {
+ AArch64 AArch64InlineAsmReg {
+ x0, x1, x2, x3, x4, x5, x6, x7,
+ x8, x9, x10, x11, x12, x13, x14, x15,
+ x16, x17, x18, x30,
+
+ // Technically the low 64 bits of v8-v15 are preserved, but
+ // we have no way of expressing this using clobbers.
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15,
+ v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31,
+
+ p0, p1, p2, p3, p4, p5, p6, p7,
+ p8, p9, p10, p11, p12, p13, p14, p15,
+ ffr,
+
+ }
+ },
+ InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! {
+ AArch64 AArch64InlineAsmReg {
+ x0, x1, x2, x3, x4, x5, x6, x7,
+ x8, x9, x10, x11, x12, x13, x14, x15,
+ x16, x17, x30,
+
+ // Technically the low 64 bits of v8-v15 are preserved, but
+ // we have no way of expressing this using clobbers.
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15,
+ v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31,
+
+ p0, p1, p2, p3, p4, p5, p6, p7,
+ p8, p9, p10, p11, p12, p13, p14, p15,
+ ffr,
+
+ }
+ },
+ InlineAsmClobberAbi::Arm => clobbered_regs! {
+ Arm ArmInlineAsmReg {
+ // r9 is either platform-reserved or callee-saved. Either
+ // way we don't need to clobber it.
+ r0, r1, r2, r3, r12, r14,
+
+ // The finest-grained register variant is used here so that
+ // partial uses of larger registers are properly handled.
+ s0, s1, s2, s3, s4, s5, s6, s7,
+ s8, s9, s10, s11, s12, s13, s14, s15,
+ // s16-s31 are callee-saved
+ d16, d17, d18, d19, d20, d21, d22, d23,
+ d24, d25, d26, d27, d28, d29, d30, d31,
+ }
+ },
+ InlineAsmClobberAbi::RiscV => clobbered_regs! {
+ RiscV RiscVInlineAsmReg {
+ // ra
+ x1,
+ // t0-t2
+ x5, x6, x7,
+ // a0-a7
+ x10, x11, x12, x13, x14, x15, x16, x17,
+ // t3-t6
+ x28, x29, x30, x31,
+ // ft0-ft7
+ f0, f1, f2, f3, f4, f5, f6, f7,
+ // fa0-fa7
+ f10, f11, f12, f13, f14, f15, f16, f17,
+ // ft8-ft11
+ f28, f29, f30, f31,
+
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15,
+ v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31,
+ }
+ },
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs
new file mode 100644
index 000000000..a27d6390a
--- /dev/null
+++ b/compiler/rustc_target/src/asm/msp430.rs
@@ -0,0 +1,81 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ Msp430 Msp430InlineAsmRegClass {
+ reg,
+ }
+}
+
+impl Msp430InlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match (self, arch) {
+ (Self::reg, _) => types! { _: I8, I16; },
+ }
+ }
+}
+
+// The reserved registers are taken from:
+// https://github.com/llvm/llvm-project/blob/36cb29cbbe1b22dcd298ad65e1fabe899b7d7249/llvm/lib/Target/MSP430/MSP430RegisterInfo.cpp#L73.
+def_regs! {
+ Msp430 Msp430InlineAsmReg Msp430InlineAsmRegClass {
+ r5: reg = ["r5"],
+ r6: reg = ["r6"],
+ r7: reg = ["r7"],
+ r8: reg = ["r8"],
+ r9: reg = ["r9"],
+ r10: reg = ["r10"],
+ r11: reg = ["r11"],
+ r12: reg = ["r12"],
+ r13: reg = ["r13"],
+ r14: reg = ["r14"],
+ r15: reg = ["r15"],
+
+ #error = ["r0", "pc"] =>
+ "the program counter cannot be used as an operand for inline asm",
+ #error = ["r1", "sp"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["r2", "sr"] =>
+ "the status register cannot be used as an operand for inline asm",
+ #error = ["r3", "cg"] =>
+ "the constant generator cannot be used as an operand for inline asm",
+ #error = ["r4", "fp"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ }
+}
+
+impl Msp430InlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ _modifier: Option<char>,
+ ) -> fmt::Result {
+ out.write_str(self.name())
+ }
+}
diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs
new file mode 100644
index 000000000..8e1e91e7c
--- /dev/null
+++ b/compiler/rustc_target/src/asm/nvptx.rs
@@ -0,0 +1,50 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+
+def_reg_class! {
+ Nvptx NvptxInlineAsmRegClass {
+ reg16,
+ reg32,
+ reg64,
+ }
+}
+
+impl NvptxInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg16 => types! { _: I8, I16; },
+ Self::reg32 => types! { _: I8, I16, I32, F32; },
+ Self::reg64 => types! { _: I8, I16, I32, F32, I64, F64; },
+ }
+ }
+}
+
+def_regs! {
+ // Registers in PTX are declared in the assembly.
+ // There are no predefined registers that one can use.
+ Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass {}
+}
diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs
new file mode 100644
index 000000000..d3ccb3035
--- /dev/null
+++ b/compiler/rustc_target/src/asm/powerpc.rs
@@ -0,0 +1,204 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ PowerPC PowerPCInlineAsmRegClass {
+ reg,
+ reg_nonzero,
+ freg,
+ cr,
+ xer,
+ }
+}
+
+impl PowerPCInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg | Self::reg_nonzero => {
+ if arch == InlineAsmArch::PowerPC {
+ types! { _: I8, I16, I32; }
+ } else {
+ types! { _: I8, I16, I32, I64; }
+ }
+ }
+ Self::freg => types! { _: F32, F64; },
+ Self::cr | Self::xer => &[],
+ }
+ }
+}
+
+def_regs! {
+ PowerPC PowerPCInlineAsmReg PowerPCInlineAsmRegClass {
+ r0: reg = ["r0", "0"],
+ r3: reg, reg_nonzero = ["r3", "3"],
+ r4: reg, reg_nonzero = ["r4", "4"],
+ r5: reg, reg_nonzero = ["r5", "5"],
+ r6: reg, reg_nonzero = ["r6", "6"],
+ r7: reg, reg_nonzero = ["r7", "7"],
+ r8: reg, reg_nonzero = ["r8", "8"],
+ r9: reg, reg_nonzero = ["r9", "9"],
+ r10: reg, reg_nonzero = ["r10", "10"],
+ r11: reg, reg_nonzero = ["r11", "11"],
+ r12: reg, reg_nonzero = ["r12", "12"],
+ r14: reg, reg_nonzero = ["r14", "14"],
+ r15: reg, reg_nonzero = ["r15", "15"],
+ r16: reg, reg_nonzero = ["r16", "16"],
+ r17: reg, reg_nonzero = ["r17", "17"],
+ r18: reg, reg_nonzero = ["r18", "18"],
+ r19: reg, reg_nonzero = ["r19", "19"],
+ r20: reg, reg_nonzero = ["r20", "20"],
+ r21: reg, reg_nonzero = ["r21", "21"],
+ r22: reg, reg_nonzero = ["r22", "22"],
+ r23: reg, reg_nonzero = ["r23", "23"],
+ r24: reg, reg_nonzero = ["r24", "24"],
+ r25: reg, reg_nonzero = ["r25", "25"],
+ r26: reg, reg_nonzero = ["r26", "26"],
+ r27: reg, reg_nonzero = ["r27", "27"],
+ r28: reg, reg_nonzero = ["r28", "28"],
+ f0: freg = ["f0", "fr0"],
+ f1: freg = ["f1", "fr1"],
+ f2: freg = ["f2", "fr2"],
+ f3: freg = ["f3", "fr3"],
+ f4: freg = ["f4", "fr4"],
+ f5: freg = ["f5", "fr5"],
+ f6: freg = ["f6", "fr6"],
+ f7: freg = ["f7", "fr7"],
+ f8: freg = ["f8", "fr8"],
+ f9: freg = ["f9", "fr9"],
+ f10: freg = ["f10", "fr10"],
+ f11: freg = ["f11", "fr11"],
+ f12: freg = ["f12", "fr12"],
+ f13: freg = ["f13", "fr13"],
+ f14: freg = ["f14", "fr14"],
+ f15: freg = ["f15", "fr15"],
+ f16: freg = ["f16", "fr16"],
+ f17: freg = ["f17", "fr17"],
+ f18: freg = ["f18", "fr18"],
+ f19: freg = ["f19", "fr19"],
+ f20: freg = ["f20", "fr20"],
+ f21: freg = ["f21", "fr21"],
+ f22: freg = ["f22", "fr22"],
+ f23: freg = ["f23", "fr23"],
+ f24: freg = ["f24", "fr24"],
+ f25: freg = ["f25", "fr25"],
+ f26: freg = ["f26", "fr26"],
+ f27: freg = ["f27", "fr27"],
+ f28: freg = ["f28", "fr28"],
+ f29: freg = ["f29", "fr29"],
+ f30: freg = ["f30", "fr30"],
+ f31: freg = ["f31", "fr31"],
+ cr: cr = ["cr"],
+ cr0: cr = ["cr0"],
+ cr1: cr = ["cr1"],
+ cr2: cr = ["cr2"],
+ cr3: cr = ["cr3"],
+ cr4: cr = ["cr4"],
+ cr5: cr = ["cr5"],
+ cr6: cr = ["cr6"],
+ cr7: cr = ["cr7"],
+ xer: xer = ["xer"],
+ #error = ["r1", "1", "sp"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["r2", "2"] =>
+ "r2 is a system reserved register and cannot be used as an operand for inline asm",
+ #error = ["r13", "13"] =>
+ "r13 is a system reserved register and cannot be used as an operand for inline asm",
+ #error = ["r29", "29"] =>
+ "r29 is used internally by LLVM and cannot be used as an operand for inline asm",
+ #error = ["r30", "30"] =>
+ "r30 is used internally by LLVM and cannot be used as an operand for inline asm",
+ #error = ["r31", "31", "fp"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ #error = ["lr"] =>
+ "the link register cannot be used as an operand for inline asm",
+ #error = ["ctr"] =>
+ "the counter register cannot be used as an operand for inline asm",
+ #error = ["vrsave"] =>
+ "the vrsave register cannot be used as an operand for inline asm",
+ }
+}
+
+impl PowerPCInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ _modifier: Option<char>,
+ ) -> fmt::Result {
+ macro_rules! do_emit {
+ (
+ $($(($reg:ident, $value:literal)),*;)*
+ ) => {
+ out.write_str(match self {
+ $($(Self::$reg => $value,)*)*
+ })
+ };
+ }
+ // Strip off the leading prefix.
+ do_emit! {
+ (r0, "0"), (r3, "3"), (r4, "4"), (r5, "5"), (r6, "6"), (r7, "7");
+ (r8, "8"), (r9, "9"), (r10, "10"), (r11, "11"), (r12, "12"), (r14, "14"), (r15, "15");
+ (r16, "16"), (r17, "17"), (r18, "18"), (r19, "19"), (r20, "20"), (r21, "21"), (r22, "22"), (r23, "23");
+ (r24, "24"), (r25, "25"), (r26, "26"), (r27, "27"), (r28, "28");
+ (f0, "0"), (f1, "1"), (f2, "2"), (f3, "3"), (f4, "4"), (f5, "5"), (f6, "6"), (f7, "7");
+ (f8, "8"), (f9, "9"), (f10, "10"), (f11, "11"), (f12, "12"), (f13, "13"), (f14, "14"), (f15, "15");
+ (f16, "16"), (f17, "17"), (f18, "18"), (f19, "19"), (f20, "20"), (f21, "21"), (f22, "22"), (f23, "23");
+ (f24, "24"), (f25, "25"), (f26, "26"), (f27, "27"), (f28, "28"), (f29, "29"), (f30, "30"), (f31, "31");
+ (cr, "cr");
+ (cr0, "0"), (cr1, "1"), (cr2, "2"), (cr3, "3"), (cr4, "4"), (cr5, "5"), (cr6, "6"), (cr7, "7");
+ (xer, "xer");
+ }
+ }
+
+ pub fn overlapping_regs(self, mut cb: impl FnMut(PowerPCInlineAsmReg)) {
+ macro_rules! reg_conflicts {
+ (
+ $(
+ $full:ident : $($field:ident)*
+ ),*;
+ ) => {
+ match self {
+ $(
+ Self::$full => {
+ cb(Self::$full);
+ $(cb(Self::$field);)*
+ }
+ $(Self::$field)|* => {
+ cb(Self::$full);
+ cb(self);
+ }
+ )*
+ r => cb(r),
+ }
+ };
+ }
+ reg_conflicts! {
+ cr : cr0 cr1 cr2 cr3 cr4 cr5 cr6 cr7;
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs
new file mode 100644
index 000000000..e41bdc9a5
--- /dev/null
+++ b/compiler/rustc_target/src/asm/riscv.rs
@@ -0,0 +1,185 @@
+use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::{RelocModel, Target};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_macros::HashStable_Generic;
+use rustc_span::{sym, Symbol};
+use std::fmt;
+
+def_reg_class! {
+ RiscV RiscVInlineAsmRegClass {
+ reg,
+ freg,
+ vreg,
+ }
+}
+
+impl RiscVInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg => {
+ if arch == InlineAsmArch::RiscV64 {
+ types! { _: I8, I16, I32, I64, F32, F64; }
+ } else {
+ types! { _: I8, I16, I32, F32; }
+ }
+ }
+ Self::freg => types! { f: F32; d: F64; },
+ Self::vreg => &[],
+ }
+ }
+}
+
+fn not_e(
+ _arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ _target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ if target_features.contains(&sym::e) {
+ Err("register can't be used with the `e` target feature")
+ } else {
+ Ok(())
+ }
+}
+
+def_regs! {
+ RiscV RiscVInlineAsmReg RiscVInlineAsmRegClass {
+ x1: reg = ["x1", "ra"],
+ x5: reg = ["x5", "t0"],
+ x6: reg = ["x6", "t1"],
+ x7: reg = ["x7", "t2"],
+ x10: reg = ["x10", "a0"],
+ x11: reg = ["x11", "a1"],
+ x12: reg = ["x12", "a2"],
+ x13: reg = ["x13", "a3"],
+ x14: reg = ["x14", "a4"],
+ x15: reg = ["x15", "a5"],
+ x16: reg = ["x16", "a6"] % not_e,
+ x17: reg = ["x17", "a7"] % not_e,
+ x18: reg = ["x18", "s2"] % not_e,
+ x19: reg = ["x19", "s3"] % not_e,
+ x20: reg = ["x20", "s4"] % not_e,
+ x21: reg = ["x21", "s5"] % not_e,
+ x22: reg = ["x22", "s6"] % not_e,
+ x23: reg = ["x23", "s7"] % not_e,
+ x24: reg = ["x24", "s8"] % not_e,
+ x25: reg = ["x25", "s9"] % not_e,
+ x26: reg = ["x26", "s10"] % not_e,
+ x27: reg = ["x27", "s11"] % not_e,
+ x28: reg = ["x28", "t3"] % not_e,
+ x29: reg = ["x29", "t4"] % not_e,
+ x30: reg = ["x30", "t5"] % not_e,
+ x31: reg = ["x31", "t6"] % not_e,
+ f0: freg = ["f0", "ft0"],
+ f1: freg = ["f1", "ft1"],
+ f2: freg = ["f2", "ft2"],
+ f3: freg = ["f3", "ft3"],
+ f4: freg = ["f4", "ft4"],
+ f5: freg = ["f5", "ft5"],
+ f6: freg = ["f6", "ft6"],
+ f7: freg = ["f7", "ft7"],
+ f8: freg = ["f8", "fs0"],
+ f9: freg = ["f9", "fs1"],
+ f10: freg = ["f10", "fa0"],
+ f11: freg = ["f11", "fa1"],
+ f12: freg = ["f12", "fa2"],
+ f13: freg = ["f13", "fa3"],
+ f14: freg = ["f14", "fa4"],
+ f15: freg = ["f15", "fa5"],
+ f16: freg = ["f16", "fa6"],
+ f17: freg = ["f17", "fa7"],
+ f18: freg = ["f18", "fs2"],
+ f19: freg = ["f19", "fs3"],
+ f20: freg = ["f20", "fs4"],
+ f21: freg = ["f21", "fs5"],
+ f22: freg = ["f22", "fs6"],
+ f23: freg = ["f23", "fs7"],
+ f24: freg = ["f24", "fs8"],
+ f25: freg = ["f25", "fs9"],
+ f26: freg = ["f26", "fs10"],
+ f27: freg = ["f27", "fs11"],
+ f28: freg = ["f28", "ft8"],
+ f29: freg = ["f29", "ft9"],
+ f30: freg = ["f30", "ft10"],
+ f31: freg = ["f31", "ft11"],
+ v0: vreg = ["v0"],
+ v1: vreg = ["v1"],
+ v2: vreg = ["v2"],
+ v3: vreg = ["v3"],
+ v4: vreg = ["v4"],
+ v5: vreg = ["v5"],
+ v6: vreg = ["v6"],
+ v7: vreg = ["v7"],
+ v8: vreg = ["v8"],
+ v9: vreg = ["v9"],
+ v10: vreg = ["v10"],
+ v11: vreg = ["v11"],
+ v12: vreg = ["v12"],
+ v13: vreg = ["v13"],
+ v14: vreg = ["v14"],
+ v15: vreg = ["v15"],
+ v16: vreg = ["v16"],
+ v17: vreg = ["v17"],
+ v18: vreg = ["v18"],
+ v19: vreg = ["v19"],
+ v20: vreg = ["v20"],
+ v21: vreg = ["v21"],
+ v22: vreg = ["v22"],
+ v23: vreg = ["v23"],
+ v24: vreg = ["v24"],
+ v25: vreg = ["v25"],
+ v26: vreg = ["v26"],
+ v27: vreg = ["v27"],
+ v28: vreg = ["v28"],
+ v29: vreg = ["v29"],
+ v30: vreg = ["v30"],
+ v31: vreg = ["v31"],
+ #error = ["x9", "s1"] =>
+ "s1 is used internally by LLVM and cannot be used as an operand for inline asm",
+ #error = ["x8", "s0", "fp"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ #error = ["x2", "sp"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["x3", "gp"] =>
+ "the global pointer cannot be used as an operand for inline asm",
+ #error = ["x4", "tp"] =>
+ "the thread pointer cannot be used as an operand for inline asm" ,
+ #error = ["x0", "zero"] =>
+ "the zero register cannot be used as an operand for inline asm",
+ }
+}
+
+impl RiscVInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ _modifier: Option<char>,
+ ) -> fmt::Result {
+ out.write_str(self.name())
+ }
+}
diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs
new file mode 100644
index 000000000..0a50064f5
--- /dev/null
+++ b/compiler/rustc_target/src/asm/s390x.rs
@@ -0,0 +1,107 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ S390x S390xInlineAsmRegClass {
+ reg,
+ freg,
+ }
+}
+
+impl S390xInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match (self, arch) {
+ (Self::reg, _) => types! { _: I8, I16, I32, I64; },
+ (Self::freg, _) => types! { _: F32, F64; },
+ }
+ }
+}
+
+def_regs! {
+ S390x S390xInlineAsmReg S390xInlineAsmRegClass {
+ r0: reg = ["r0"],
+ r1: reg = ["r1"],
+ r2: reg = ["r2"],
+ r3: reg = ["r3"],
+ r4: reg = ["r4"],
+ r5: reg = ["r5"],
+ r6: reg = ["r6"],
+ r7: reg = ["r7"],
+ r8: reg = ["r8"],
+ r9: reg = ["r9"],
+ r10: reg = ["r10"],
+ r12: reg = ["r12"],
+ r13: reg = ["r13"],
+ r14: reg = ["r14"],
+ f0: freg = ["f0"],
+ f1: freg = ["f1"],
+ f2: freg = ["f2"],
+ f3: freg = ["f3"],
+ f4: freg = ["f4"],
+ f5: freg = ["f5"],
+ f6: freg = ["f6"],
+ f7: freg = ["f7"],
+ f8: freg = ["f8"],
+ f9: freg = ["f9"],
+ f10: freg = ["f10"],
+ f11: freg = ["f11"],
+ f12: freg = ["f12"],
+ f13: freg = ["f13"],
+ f14: freg = ["f14"],
+ f15: freg = ["f15"],
+ #error = ["r11"] =>
+ "The frame pointer cannot be used as an operand for inline asm",
+ #error = ["r15"] =>
+ "The stack pointer cannot be used as an operand for inline asm",
+ #error = [
+ "c0", "c1", "c2", "c3",
+ "c4", "c5", "c6", "c7",
+ "c8", "c9", "c10", "c11",
+ "c12", "c13", "c14", "c15"
+ ] =>
+ "control registers are reserved by the kernel and cannot be used as operands for inline asm",
+ #error = [
+ "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7",
+ "a8", "a9", "a10", "a11",
+ "a12", "a13", "a14", "a15"
+ ] =>
+ "access registers are not supported and cannot be used as operands for inline asm",
+ }
+}
+
+impl S390xInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ _modifier: Option<char>,
+ ) -> fmt::Result {
+ write!(out, "%{}", self.name())
+ }
+}
diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs
new file mode 100644
index 000000000..31073da10
--- /dev/null
+++ b/compiler/rustc_target/src/asm/spirv.rs
@@ -0,0 +1,47 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+
+def_reg_class! {
+ SpirV SpirVInlineAsmRegClass {
+ reg,
+ }
+}
+
+impl SpirVInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg => {
+ types! { _: I8, I16, I32, I64, F32, F64; }
+ }
+ }
+ }
+}
+
+def_regs! {
+ // SPIR-V is SSA-based, it does not have registers.
+ SpirV SpirVInlineAsmReg SpirVInlineAsmRegClass {}
+}
diff --git a/compiler/rustc_target/src/asm/wasm.rs b/compiler/rustc_target/src/asm/wasm.rs
new file mode 100644
index 000000000..f095b7c6e
--- /dev/null
+++ b/compiler/rustc_target/src/asm/wasm.rs
@@ -0,0 +1,47 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+
+def_reg_class! {
+ Wasm WasmInlineAsmRegClass {
+ local,
+ }
+}
+
+impl WasmInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+ &[]
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::local => {
+ types! { _: I8, I16, I32, I64, F32, F64; }
+ }
+ }
+ }
+}
+
+def_regs! {
+ // WebAssembly doesn't have registers.
+ Wasm WasmInlineAsmReg WasmInlineAsmRegClass {}
+}
diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs
new file mode 100644
index 000000000..238c36509
--- /dev/null
+++ b/compiler/rustc_target/src/asm/x86.rs
@@ -0,0 +1,492 @@
+use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::{RelocModel, Target};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+ X86 X86InlineAsmRegClass {
+ reg,
+ reg_abcd,
+ reg_byte,
+ xmm_reg,
+ ymm_reg,
+ zmm_reg,
+ kreg,
+ kreg0,
+ mmx_reg,
+ x87_reg,
+ tmm_reg,
+ }
+}
+
+impl X86InlineAsmRegClass {
+ pub fn valid_modifiers(self, arch: super::InlineAsmArch) -> &'static [char] {
+ match self {
+ Self::reg => {
+ if arch == InlineAsmArch::X86_64 {
+ &['l', 'x', 'e', 'r']
+ } else {
+ &['x', 'e']
+ }
+ }
+ Self::reg_abcd => {
+ if arch == InlineAsmArch::X86_64 {
+ &['l', 'h', 'x', 'e', 'r']
+ } else {
+ &['l', 'h', 'x', 'e']
+ }
+ }
+ Self::reg_byte => &[],
+ Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'],
+ Self::kreg | Self::kreg0 => &[],
+ Self::mmx_reg | Self::x87_reg => &[],
+ Self::tmm_reg => &[],
+ }
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> {
+ match self {
+ Self::reg | Self::reg_abcd if ty.size().bits() == 8 => Some(Self::reg_byte),
+ _ => None,
+ }
+ }
+
+ pub fn suggest_modifier(
+ self,
+ arch: InlineAsmArch,
+ ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ match self {
+ Self::reg => match ty.size().bits() {
+ 16 => Some(('x', "ax")),
+ 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")),
+ _ => None,
+ },
+ Self::reg_abcd => match ty.size().bits() {
+ 16 => Some(('x', "ax")),
+ 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")),
+ _ => None,
+ },
+ Self::reg_byte => None,
+ Self::xmm_reg => None,
+ Self::ymm_reg => match ty.size().bits() {
+ 256 => None,
+ _ => Some(('x', "xmm0")),
+ },
+ Self::zmm_reg => match ty.size().bits() {
+ 512 => None,
+ 256 => Some(('y', "ymm0")),
+ _ => Some(('x', "xmm0")),
+ },
+ Self::kreg | Self::kreg0 => None,
+ Self::mmx_reg | Self::x87_reg => None,
+ Self::tmm_reg => None,
+ }
+ }
+
+ pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ match self {
+ Self::reg | Self::reg_abcd => {
+ if arch == InlineAsmArch::X86_64 {
+ Some(('r', "rax"))
+ } else {
+ Some(('e', "eax"))
+ }
+ }
+ Self::reg_byte => None,
+ Self::xmm_reg => Some(('x', "xmm0")),
+ Self::ymm_reg => Some(('y', "ymm0")),
+ Self::zmm_reg => Some(('z', "zmm0")),
+ Self::kreg | Self::kreg0 => None,
+ Self::mmx_reg | Self::x87_reg => None,
+ Self::tmm_reg => None,
+ }
+ }
+
+ pub fn supported_types(
+ self,
+ arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+ match self {
+ Self::reg | Self::reg_abcd => {
+ if arch == InlineAsmArch::X86_64 {
+ types! { _: I16, I32, I64, F32, F64; }
+ } else {
+ types! { _: I16, I32, F32; }
+ }
+ }
+ Self::reg_byte => types! { _: I8; },
+ Self::xmm_reg => types! {
+ sse: I32, I64, F32, F64,
+ VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
+ },
+ Self::ymm_reg => types! {
+ avx: I32, I64, F32, F64,
+ VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2),
+ VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4);
+ },
+ Self::zmm_reg => types! {
+ avx512f: I32, I64, F32, F64,
+ VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2),
+ VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4),
+ VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF32(16), VecF64(8);
+ },
+ Self::kreg => types! {
+ avx512f: I8, I16;
+ avx512bw: I32, I64;
+ },
+ Self::kreg0 => &[],
+ Self::mmx_reg | Self::x87_reg => &[],
+ Self::tmm_reg => &[],
+ }
+ }
+}
+
+fn x86_64_only(
+ arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ _target_features: &FxHashSet<Symbol>,
+ _target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ match arch {
+ InlineAsmArch::X86 => Err("register is only available on x86_64"),
+ InlineAsmArch::X86_64 => Ok(()),
+ _ => unreachable!(),
+ }
+}
+
+fn high_byte(
+ arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ _target_features: &FxHashSet<Symbol>,
+ _target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ match arch {
+ InlineAsmArch::X86_64 => Err("high byte registers cannot be used as an operand on x86_64"),
+ _ => Ok(()),
+ }
+}
+
+fn rbx_reserved(
+ arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ _target_features: &FxHashSet<Symbol>,
+ _target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ match arch {
+ InlineAsmArch::X86 => Ok(()),
+ InlineAsmArch::X86_64 => {
+ Err("rbx is used internally by LLVM and cannot be used as an operand for inline asm")
+ }
+ _ => unreachable!(),
+ }
+}
+
+fn esi_reserved(
+ arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ _target_features: &FxHashSet<Symbol>,
+ _target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ match arch {
+ InlineAsmArch::X86 => {
+ Err("esi is used internally by LLVM and cannot be used as an operand for inline asm")
+ }
+ InlineAsmArch::X86_64 => Ok(()),
+ _ => unreachable!(),
+ }
+}
+
+def_regs! {
+ X86 X86InlineAsmReg X86InlineAsmRegClass {
+ ax: reg, reg_abcd = ["ax", "eax", "rax"],
+ bx: reg, reg_abcd = ["bx", "ebx", "rbx"] % rbx_reserved,
+ cx: reg, reg_abcd = ["cx", "ecx", "rcx"],
+ dx: reg, reg_abcd = ["dx", "edx", "rdx"],
+ si: reg = ["si", "esi", "rsi"] % esi_reserved,
+ di: reg = ["di", "edi", "rdi"],
+ r8: reg = ["r8", "r8w", "r8d"] % x86_64_only,
+ r9: reg = ["r9", "r9w", "r9d"] % x86_64_only,
+ r10: reg = ["r10", "r10w", "r10d"] % x86_64_only,
+ r11: reg = ["r11", "r11w", "r11d"] % x86_64_only,
+ r12: reg = ["r12", "r12w", "r12d"] % x86_64_only,
+ r13: reg = ["r13", "r13w", "r13d"] % x86_64_only,
+ r14: reg = ["r14", "r14w", "r14d"] % x86_64_only,
+ r15: reg = ["r15", "r15w", "r15d"] % x86_64_only,
+ al: reg_byte = ["al"],
+ ah: reg_byte = ["ah"] % high_byte,
+ bl: reg_byte = ["bl"],
+ bh: reg_byte = ["bh"] % high_byte,
+ cl: reg_byte = ["cl"],
+ ch: reg_byte = ["ch"] % high_byte,
+ dl: reg_byte = ["dl"],
+ dh: reg_byte = ["dh"] % high_byte,
+ sil: reg_byte = ["sil"] % x86_64_only,
+ dil: reg_byte = ["dil"] % x86_64_only,
+ r8b: reg_byte = ["r8b"] % x86_64_only,
+ r9b: reg_byte = ["r9b"] % x86_64_only,
+ r10b: reg_byte = ["r10b"] % x86_64_only,
+ r11b: reg_byte = ["r11b"] % x86_64_only,
+ r12b: reg_byte = ["r12b"] % x86_64_only,
+ r13b: reg_byte = ["r13b"] % x86_64_only,
+ r14b: reg_byte = ["r14b"] % x86_64_only,
+ r15b: reg_byte = ["r15b"] % x86_64_only,
+ xmm0: xmm_reg = ["xmm0"],
+ xmm1: xmm_reg = ["xmm1"],
+ xmm2: xmm_reg = ["xmm2"],
+ xmm3: xmm_reg = ["xmm3"],
+ xmm4: xmm_reg = ["xmm4"],
+ xmm5: xmm_reg = ["xmm5"],
+ xmm6: xmm_reg = ["xmm6"],
+ xmm7: xmm_reg = ["xmm7"],
+ xmm8: xmm_reg = ["xmm8"] % x86_64_only,
+ xmm9: xmm_reg = ["xmm9"] % x86_64_only,
+ xmm10: xmm_reg = ["xmm10"] % x86_64_only,
+ xmm11: xmm_reg = ["xmm11"] % x86_64_only,
+ xmm12: xmm_reg = ["xmm12"] % x86_64_only,
+ xmm13: xmm_reg = ["xmm13"] % x86_64_only,
+ xmm14: xmm_reg = ["xmm14"] % x86_64_only,
+ xmm15: xmm_reg = ["xmm15"] % x86_64_only,
+ ymm0: ymm_reg = ["ymm0"],
+ ymm1: ymm_reg = ["ymm1"],
+ ymm2: ymm_reg = ["ymm2"],
+ ymm3: ymm_reg = ["ymm3"],
+ ymm4: ymm_reg = ["ymm4"],
+ ymm5: ymm_reg = ["ymm5"],
+ ymm6: ymm_reg = ["ymm6"],
+ ymm7: ymm_reg = ["ymm7"],
+ ymm8: ymm_reg = ["ymm8"] % x86_64_only,
+ ymm9: ymm_reg = ["ymm9"] % x86_64_only,
+ ymm10: ymm_reg = ["ymm10"] % x86_64_only,
+ ymm11: ymm_reg = ["ymm11"] % x86_64_only,
+ ymm12: ymm_reg = ["ymm12"] % x86_64_only,
+ ymm13: ymm_reg = ["ymm13"] % x86_64_only,
+ ymm14: ymm_reg = ["ymm14"] % x86_64_only,
+ ymm15: ymm_reg = ["ymm15"] % x86_64_only,
+ zmm0: zmm_reg = ["zmm0"],
+ zmm1: zmm_reg = ["zmm1"],
+ zmm2: zmm_reg = ["zmm2"],
+ zmm3: zmm_reg = ["zmm3"],
+ zmm4: zmm_reg = ["zmm4"],
+ zmm5: zmm_reg = ["zmm5"],
+ zmm6: zmm_reg = ["zmm6"],
+ zmm7: zmm_reg = ["zmm7"],
+ zmm8: zmm_reg = ["zmm8"] % x86_64_only,
+ zmm9: zmm_reg = ["zmm9"] % x86_64_only,
+ zmm10: zmm_reg = ["zmm10"] % x86_64_only,
+ zmm11: zmm_reg = ["zmm11"] % x86_64_only,
+ zmm12: zmm_reg = ["zmm12"] % x86_64_only,
+ zmm13: zmm_reg = ["zmm13"] % x86_64_only,
+ zmm14: zmm_reg = ["zmm14"] % x86_64_only,
+ zmm15: zmm_reg = ["zmm15"] % x86_64_only,
+ zmm16: zmm_reg = ["zmm16", "xmm16", "ymm16"] % x86_64_only,
+ zmm17: zmm_reg = ["zmm17", "xmm17", "ymm17"] % x86_64_only,
+ zmm18: zmm_reg = ["zmm18", "xmm18", "ymm18"] % x86_64_only,
+ zmm19: zmm_reg = ["zmm19", "xmm19", "ymm19"] % x86_64_only,
+ zmm20: zmm_reg = ["zmm20", "xmm20", "ymm20"] % x86_64_only,
+ zmm21: zmm_reg = ["zmm21", "xmm21", "ymm21"] % x86_64_only,
+ zmm22: zmm_reg = ["zmm22", "xmm22", "ymm22"] % x86_64_only,
+ zmm23: zmm_reg = ["zmm23", "xmm23", "ymm23"] % x86_64_only,
+ zmm24: zmm_reg = ["zmm24", "xmm24", "ymm24"] % x86_64_only,
+ zmm25: zmm_reg = ["zmm25", "xmm25", "ymm25"] % x86_64_only,
+ zmm26: zmm_reg = ["zmm26", "xmm26", "ymm26"] % x86_64_only,
+ zmm27: zmm_reg = ["zmm27", "xmm27", "ymm27"] % x86_64_only,
+ zmm28: zmm_reg = ["zmm28", "xmm28", "ymm28"] % x86_64_only,
+ zmm29: zmm_reg = ["zmm29", "xmm29", "ymm29"] % x86_64_only,
+ zmm30: zmm_reg = ["zmm30", "xmm30", "ymm30"] % x86_64_only,
+ zmm31: zmm_reg = ["zmm31", "xmm31", "ymm31"] % x86_64_only,
+ k0: kreg0 = ["k0"],
+ k1: kreg = ["k1"],
+ k2: kreg = ["k2"],
+ k3: kreg = ["k3"],
+ k4: kreg = ["k4"],
+ k5: kreg = ["k5"],
+ k6: kreg = ["k6"],
+ k7: kreg = ["k7"],
+ mm0: mmx_reg = ["mm0"],
+ mm1: mmx_reg = ["mm1"],
+ mm2: mmx_reg = ["mm2"],
+ mm3: mmx_reg = ["mm3"],
+ mm4: mmx_reg = ["mm4"],
+ mm5: mmx_reg = ["mm5"],
+ mm6: mmx_reg = ["mm6"],
+ mm7: mmx_reg = ["mm7"],
+ st0: x87_reg = ["st(0)", "st"],
+ st1: x87_reg = ["st(1)"],
+ st2: x87_reg = ["st(2)"],
+ st3: x87_reg = ["st(3)"],
+ st4: x87_reg = ["st(4)"],
+ st5: x87_reg = ["st(5)"],
+ st6: x87_reg = ["st(6)"],
+ st7: x87_reg = ["st(7)"],
+ tmm0: tmm_reg = ["tmm0"] % x86_64_only,
+ tmm1: tmm_reg = ["tmm1"] % x86_64_only,
+ tmm2: tmm_reg = ["tmm2"] % x86_64_only,
+ tmm3: tmm_reg = ["tmm3"] % x86_64_only,
+ tmm4: tmm_reg = ["tmm4"] % x86_64_only,
+ tmm5: tmm_reg = ["tmm5"] % x86_64_only,
+ tmm6: tmm_reg = ["tmm6"] % x86_64_only,
+ tmm7: tmm_reg = ["tmm7"] % x86_64_only,
+ #error = ["bp", "bpl", "ebp", "rbp"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ #error = ["sp", "spl", "esp", "rsp"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["ip", "eip", "rip"] =>
+ "the instruction pointer cannot be used as an operand for inline asm",
+ }
+}
+
+impl X86InlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ arch: InlineAsmArch,
+ modifier: Option<char>,
+ ) -> fmt::Result {
+ let reg_default_modifier = match arch {
+ InlineAsmArch::X86 => 'e',
+ InlineAsmArch::X86_64 => 'r',
+ _ => unreachable!(),
+ };
+ if self as u32 <= Self::dx as u32 {
+ let root = ['a', 'b', 'c', 'd'][self as usize - Self::ax as usize];
+ match modifier.unwrap_or(reg_default_modifier) {
+ 'l' => write!(out, "{}l", root),
+ 'h' => write!(out, "{}h", root),
+ 'x' => write!(out, "{}x", root),
+ 'e' => write!(out, "e{}x", root),
+ 'r' => write!(out, "r{}x", root),
+ _ => unreachable!(),
+ }
+ } else if self as u32 <= Self::di as u32 {
+ let root = self.name();
+ match modifier.unwrap_or(reg_default_modifier) {
+ 'l' => write!(out, "{}l", root),
+ 'x' => write!(out, "{}", root),
+ 'e' => write!(out, "e{}", root),
+ 'r' => write!(out, "r{}", root),
+ _ => unreachable!(),
+ }
+ } else if self as u32 <= Self::r15 as u32 {
+ let root = self.name();
+ match modifier.unwrap_or(reg_default_modifier) {
+ 'l' => write!(out, "{}b", root),
+ 'x' => write!(out, "{}w", root),
+ 'e' => write!(out, "{}d", root),
+ 'r' => out.write_str(root),
+ _ => unreachable!(),
+ }
+ } else if self as u32 <= Self::r15b as u32 {
+ out.write_str(self.name())
+ } else if self as u32 <= Self::xmm15 as u32 {
+ let prefix = modifier.unwrap_or('x');
+ let index = self as u32 - Self::xmm0 as u32;
+ write!(out, "{}{}", prefix, index)
+ } else if self as u32 <= Self::ymm15 as u32 {
+ let prefix = modifier.unwrap_or('y');
+ let index = self as u32 - Self::ymm0 as u32;
+ write!(out, "{}{}", prefix, index)
+ } else if self as u32 <= Self::zmm31 as u32 {
+ let prefix = modifier.unwrap_or('z');
+ let index = self as u32 - Self::zmm0 as u32;
+ write!(out, "{}{}", prefix, index)
+ } else {
+ out.write_str(self.name())
+ }
+ }
+
+ pub fn overlapping_regs(self, mut cb: impl FnMut(X86InlineAsmReg)) {
+ macro_rules! reg_conflicts {
+ (
+ $(
+ $w:ident : $l:ident $h:ident
+ ),*;
+ $(
+ $w2:ident : $l2:ident
+ ),*;
+ $(
+ $x:ident : $y:ident : $z:ident
+ ),*;
+ ) => {
+ match self {
+ $(
+ Self::$w => {
+ cb(Self::$w);
+ cb(Self::$l);
+ cb(Self::$h);
+ }
+ Self::$l => {
+ cb(Self::$w);
+ cb(Self::$l);
+ }
+ Self::$h => {
+ cb(Self::$w);
+ cb(Self::$h);
+ }
+ )*
+ $(
+ Self::$w2 | Self::$l2 => {
+ cb(Self::$w2);
+ cb(Self::$l2);
+ }
+ )*
+ $(
+ Self::$x | Self::$y | Self::$z => {
+ cb(Self::$x);
+ cb(Self::$y);
+ cb(Self::$z);
+ }
+ )*
+ r => cb(r),
+ }
+ };
+ }
+
+ // XMM*, YMM* and ZMM* are all different views of the same register.
+ //
+ // See section 15.5 of the combined Intel® 64 and IA-32 Architectures
+ // Software Developer’s Manual for more details.
+ //
+ // We don't need to specify conflicts for [x,y,z]mm[16-31] since these
+ // registers are only available with AVX-512, so we just specify them
+ // as aliases directly.
+ reg_conflicts! {
+ ax : al ah,
+ bx : bl bh,
+ cx : cl ch,
+ dx : dl dh;
+ si : sil,
+ di : dil,
+ r8 : r8b,
+ r9 : r9b,
+ r10 : r10b,
+ r11 : r11b,
+ r12 : r12b,
+ r13 : r13b,
+ r14 : r14b,
+ r15 : r15b;
+ xmm0 : ymm0 : zmm0,
+ xmm1 : ymm1 : zmm1,
+ xmm2 : ymm2 : zmm2,
+ xmm3 : ymm3 : zmm3,
+ xmm4 : ymm4 : zmm4,
+ xmm5 : ymm5 : zmm5,
+ xmm6 : ymm6 : zmm6,
+ xmm7 : ymm7 : zmm7,
+ xmm8 : ymm8 : zmm8,
+ xmm9 : ymm9 : zmm9,
+ xmm10 : ymm10 : zmm10,
+ xmm11 : ymm11 : zmm11,
+ xmm12 : ymm12 : zmm12,
+ xmm13 : ymm13 : zmm13,
+ xmm14 : ymm14 : zmm14,
+ xmm15 : ymm15 : zmm15;
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/json.rs b/compiler/rustc_target/src/json.rs
new file mode 100644
index 000000000..b5d926352
--- /dev/null
+++ b/compiler/rustc_target/src/json.rs
@@ -0,0 +1,91 @@
+use std::borrow::Cow;
+use std::collections::BTreeMap;
+
+pub use serde_json::Value as Json;
+use serde_json::{Map, Number};
+
+pub trait ToJson {
+ fn to_json(&self) -> Json;
+}
+
+impl ToJson for Json {
+ fn to_json(&self) -> Json {
+ self.clone()
+ }
+}
+
+macro_rules! to_json_impl_num {
+ ($($t:ty), +) => (
+ $(impl ToJson for $t {
+ fn to_json(&self) -> Json {
+ Json::Number(Number::from(*self))
+ }
+ })+
+ )
+}
+
+to_json_impl_num! { isize, i8, i16, i32, i64, usize, u8, u16, u32, u64 }
+
+impl ToJson for bool {
+ fn to_json(&self) -> Json {
+ Json::Bool(*self)
+ }
+}
+
+impl ToJson for str {
+ fn to_json(&self) -> Json {
+ Json::String(self.to_owned())
+ }
+}
+
+impl ToJson for String {
+ fn to_json(&self) -> Json {
+ Json::String(self.to_owned())
+ }
+}
+
+impl<'a> ToJson for Cow<'a, str> {
+ fn to_json(&self) -> Json {
+ Json::String(self.to_string())
+ }
+}
+
+impl<A: ToJson> ToJson for [A] {
+ fn to_json(&self) -> Json {
+ Json::Array(self.iter().map(|elt| elt.to_json()).collect())
+ }
+}
+
+impl<A: ToJson> ToJson for Vec<A> {
+ fn to_json(&self) -> Json {
+ Json::Array(self.iter().map(|elt| elt.to_json()).collect())
+ }
+}
+
+impl<'a, A: ToJson> ToJson for Cow<'a, [A]>
+where
+ [A]: ToOwned,
+{
+ fn to_json(&self) -> Json {
+ Json::Array(self.iter().map(|elt| elt.to_json()).collect())
+ }
+}
+
+impl<T: ToString, A: ToJson> ToJson for BTreeMap<T, A> {
+ fn to_json(&self) -> Json {
+ let mut d = Map::new();
+ for (key, value) in self {
+ d.insert(key.to_string(), value.to_json());
+ }
+ Json::Object(d)
+ }
+}
+
+impl<A: ToJson> ToJson for Option<A> {
+ fn to_json(&self) -> Json {
+ match *self {
+ None => Json::Null,
+ Some(ref value) => value.to_json(),
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
new file mode 100644
index 000000000..59dbea705
--- /dev/null
+++ b/compiler/rustc_target/src/lib.rs
@@ -0,0 +1,88 @@
+//! Some stuff used by rustc that doesn't have many dependencies
+//!
+//! Originally extracted from rustc::back, which was nominally the
+//! compiler 'backend', though LLVM is rustc's backend, so rustc_target
+//! is really just odds-and-ends relating to code gen and linking.
+//! This crate mostly exists to make rustc smaller, so we might put
+//! more 'stuff' here in the future. It does not have a dependency on
+//! LLVM.
+
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(assert_matches)]
+#![feature(associated_type_bounds)]
+#![feature(exhaustive_patterns)]
+#![feature(let_else)]
+#![feature(min_specialization)]
+#![feature(never_type)]
+#![feature(rustc_attrs)]
+#![feature(step_trait)]
+
+use std::iter::FromIterator;
+use std::path::{Path, PathBuf};
+
+#[macro_use]
+extern crate rustc_macros;
+
+#[macro_use]
+extern crate tracing;
+
+pub mod abi;
+pub mod asm;
+pub mod json;
+pub mod spec;
+
+#[cfg(test)]
+mod tests;
+
+/// Requirements for a `StableHashingContext` to be used in this crate.
+/// This is a hack to allow using the `HashStable_Generic` derive macro
+/// instead of implementing everything in `rustc_middle`.
+pub trait HashStableContext {}
+
+/// The name of rustc's own place to organize libraries.
+///
+/// Used to be `rustc`, now the default is `rustlib`.
+const RUST_LIB_DIR: &str = "rustlib";
+
+/// Returns a `rustlib` path for this particular target, relative to the provided sysroot.
+///
+/// For example: `target_sysroot_path("/usr", "x86_64-unknown-linux-gnu")` =>
+/// `"lib*/rustlib/x86_64-unknown-linux-gnu"`.
+pub fn target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
+ let libdir = find_libdir(sysroot);
+ PathBuf::from_iter([
+ Path::new(libdir.as_ref()),
+ Path::new(RUST_LIB_DIR),
+ Path::new(target_triple),
+ ])
+}
+
+/// The name of the directory rustc expects libraries to be located.
+fn find_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
+ // FIXME: This is a quick hack to make the rustc binary able to locate
+ // Rust libraries in Linux environments where libraries might be installed
+ // to lib64/lib32. This would be more foolproof by basing the sysroot off
+ // of the directory where `librustc_driver` is located, rather than
+ // where the rustc binary is.
+ // If --libdir is set during configuration to the value other than
+ // "lib" (i.e., non-default), this value is used (see issue #16552).
+
+ #[cfg(target_pointer_width = "64")]
+ const PRIMARY_LIB_DIR: &str = "lib64";
+
+ #[cfg(target_pointer_width = "32")]
+ const PRIMARY_LIB_DIR: &str = "lib32";
+
+ const SECONDARY_LIB_DIR: &str = "lib";
+
+ match option_env!("CFG_LIBDIR_RELATIVE") {
+ None | Some("lib") => {
+ if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() {
+ PRIMARY_LIB_DIR.into()
+ } else {
+ SECONDARY_LIB_DIR.into()
+ }
+ }
+ Some(libdir) => libdir.into(),
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs
new file mode 100644
index 000000000..9d36e37d7
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs
@@ -0,0 +1,30 @@
+use crate::spec::{FramePointer, LinkerFlavor, SanitizerSet, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::apple_base::opts("macos");
+ base.cpu = "apple-a14".into();
+ base.max_atomic_width = Some(128);
+
+ // FIXME: The leak sanitizer currently fails the tests, see #88132.
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD;
+
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-arch", "arm64"]);
+ base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove());
+
+ // Clang automatically chooses a more specific target based on
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // correctly, we do too.
+ let llvm_target = super::apple_base::macos_llvm_target("arm64");
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ mcount: "\u{1}mcount".into(),
+ frame_pointer: FramePointer::NonLeaf,
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs
new file mode 100644
index 000000000..beb904239
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs
@@ -0,0 +1,36 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{FramePointer, Target, TargetOptions};
+
+pub fn target() -> Target {
+ // Clang automatically chooses a more specific target based on
+ // IPHONEOS_DEPLOYMENT_TARGET.
+ // This is required for the target to pick the right
+ // MACH-O commands, so we do too.
+ let arch = "arm64";
+ let llvm_target = super::apple_base::ios_llvm_target(arch);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+neon,+fp-armv8,+apple-a7".into(),
+ max_atomic_width: Some(128),
+ forces_embed_bitcode: true,
+ frame_pointer: FramePointer::NonLeaf,
+ // Taken from a clang build on Xcode 11.4.1.
+ // These arguments are not actually invoked - they just have
+ // to look right to pass App Store validation.
+ bitcode_llvm_cmdline: "-triple\0\
+ arm64-apple-ios11.0.0\0\
+ -emit-obj\0\
+ -disable-llvm-passes\0\
+ -target-abi\0\
+ darwinpcs\0\
+ -Os\0"
+ .into(),
+ ..opts("ios", Arch::Arm64)
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs
new file mode 100644
index 000000000..1dad07a9a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs
@@ -0,0 +1,32 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{FramePointer, LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let llvm_target = "arm64-apple-ios14.0-macabi";
+
+ let mut base = opts("ios", Arch::Arm64_macabi);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-target", llvm_target]);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+neon,+fp-armv8,+apple-a12".into(),
+ max_atomic_width: Some(128),
+ forces_embed_bitcode: true,
+ frame_pointer: FramePointer::NonLeaf,
+ // Taken from a clang build on Xcode 11.4.1.
+ // These arguments are not actually invoked - they just have
+ // to look right to pass App Store validation.
+ bitcode_llvm_cmdline: "-triple\0\
+ arm64-apple-ios14.0-macabi\0\
+ -emit-obj\0\
+ -disable-llvm-passes\0\
+ -Os\0"
+ .into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios_sim.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios_sim.rs
new file mode 100644
index 000000000..b4e135f66
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_apple_ios_sim.rs
@@ -0,0 +1,38 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{FramePointer, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("ios", Arch::Arm64_sim);
+
+ // Clang automatically chooses a more specific target based on
+ // IPHONEOS_DEPLOYMENT_TARGET.
+ // This is required for the simulator target to pick the right
+ // MACH-O commands, so we do too.
+ let arch = "arm64";
+ let llvm_target = super::apple_base::ios_sim_llvm_target(arch);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+neon,+fp-armv8,+apple-a7".into(),
+ max_atomic_width: Some(128),
+ forces_embed_bitcode: true,
+ frame_pointer: FramePointer::NonLeaf,
+ // Taken from a clang build on Xcode 11.4.1.
+ // These arguments are not actually invoked - they just have
+ // to look right to pass App Store validation.
+ bitcode_llvm_cmdline: "-triple\0\
+ arm64-apple-ios14.0-simulator\0\
+ -emit-obj\0\
+ -disable-llvm-passes\0\
+ -target-abi\0\
+ darwinpcs\0\
+ -Os\0"
+ .into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
new file mode 100644
index 000000000..2e31d16dc
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
@@ -0,0 +1,18 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{FramePointer, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "arm64-apple-tvos".into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+neon,+fp-armv8,+apple-a7".into(),
+ max_atomic_width: Some(128),
+ forces_embed_bitcode: true,
+ frame_pointer: FramePointer::NonLeaf,
+ ..opts("tvos", Arch::Arm64)
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/aarch64_apple_watchos_sim.rs
new file mode 100644
index 000000000..3059f4214
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_apple_watchos_sim.rs
@@ -0,0 +1,38 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{FramePointer, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("watchos", Arch::Arm64_sim);
+
+ // Clang automatically chooses a more specific target based on
+ // WATCHOS_DEPLOYMENT_TARGET.
+ // This is required for the simulator target to pick the right
+ // MACH-O commands, so we do too.
+ let arch = "arm64";
+ let llvm_target = super::apple_base::watchos_sim_llvm_target(arch);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+neon,+fp-armv8,+apple-a7".into(),
+ max_atomic_width: Some(128),
+ forces_embed_bitcode: true,
+ frame_pointer: FramePointer::NonLeaf,
+ // Taken from a clang build on Xcode 11.4.1.
+ // These arguments are not actually invoked - they just have
+ // to look right to pass App Store validation.
+ bitcode_llvm_cmdline: "-triple\0\
+ arm64-apple-watchos5.0-simulator\0\
+ -emit-obj\0\
+ -disable-llvm-passes\0\
+ -target-abi\0\
+ darwinpcs\0\
+ -Os\0"
+ .into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs
new file mode 100644
index 000000000..9bce82a19
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs
@@ -0,0 +1,18 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64_be-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+outline-atomics".into(),
+ max_atomic_width: Some(128),
+ mcount: "\u{1}_mcount".into(),
+ endian: Endian::Big,
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs
new file mode 100644
index 000000000..c9ceb55dd
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs
@@ -0,0 +1,21 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.max_atomic_width = Some(128);
+
+ Target {
+ llvm_target: "aarch64_be-unknown-linux-gnu_ilp32".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ abi: "ilp32".into(),
+ features: "+outline-atomics".into(),
+ mcount: "\u{1}_mcount".into(),
+ endian: Endian::Big,
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs
new file mode 100644
index 000000000..4634433c4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs
@@ -0,0 +1,15 @@
+use crate::spec::{SanitizerSet, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-fuchsia".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(128),
+ supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI,
+ ..super::fuchsia_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_kmc_solid_asp3.rs b/compiler/rustc_target/src/spec/aarch64_kmc_solid_asp3.rs
new file mode 100644
index 000000000..6ea9ae266
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_kmc_solid_asp3.rs
@@ -0,0 +1,19 @@
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = super::solid_base::opts("asp3");
+ Target {
+ llvm_target: "aarch64-unknown-none".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ linker: Some("aarch64-kmc-elf-gcc".into()),
+ features: "+neon,+fp-armv8".into(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(128),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs
new file mode 100644
index 000000000..c85f7f62a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs
@@ -0,0 +1,25 @@
+use crate::spec::{SanitizerSet, Target, TargetOptions};
+
+// See https://developer.android.com/ndk/guides/abis.html#arm64-v8a
+// for target ABI requirements.
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-linux-android".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(128),
+ // As documented in https://developer.android.com/ndk/guides/cpu-features.html
+ // the neon (ASIMD) and FP must exist on all android aarch64 targets.
+ features: "+neon,+fp-armv8".into(),
+ supported_sanitizers: SanitizerSet::CFI
+ | SanitizerSet::HWADDRESS
+ | SanitizerSet::MEMTAG
+ | SanitizerSet::SHADOWCALLSTACK
+ | SanitizerSet::ADDRESS,
+ ..super::android_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs
new file mode 100644
index 000000000..1b7161fbb
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs
@@ -0,0 +1,26 @@
+use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelroLevel, Target, TargetOptions};
+
+const LINKER_SCRIPT: &str = include_str!("./aarch64_nintendo_switch_freestanding_linker_script.ld");
+
+/// A base target for Nintendo Switch devices using a pure LLVM toolchain.
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-unknown-none".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ link_script: Some(LINKER_SCRIPT.into()),
+ os: "horizon".into(),
+ max_atomic_width: Some(128),
+ panic_strategy: PanicStrategy::Abort,
+ position_independent_executables: true,
+ dynamic_linking: true,
+ executables: true,
+ relro_level: RelroLevel::Off,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld
new file mode 100644
index 000000000..f3441e659
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld
@@ -0,0 +1,78 @@
+OUTPUT_FORMAT(elf64-littleaarch64)
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+
+PHDRS
+{
+ text PT_LOAD FLAGS(5);
+ rodata PT_LOAD FLAGS(4);
+ data PT_LOAD FLAGS(6);
+ bss PT_LOAD FLAGS(6);
+ dynamic PT_DYNAMIC;
+}
+
+SECTIONS
+{
+ . = 0;
+
+ .text : ALIGN(0x1000) {
+ HIDDEN(__text_start = .);
+ KEEP(*(.text.jmp))
+
+ . = 0x80;
+
+ *(.text .text.*)
+ *(.plt .plt.*)
+ }
+
+ /* Read-only sections */
+
+ . = ALIGN(0x1000);
+
+ .module_name : { *(.module_name) } :rodata
+
+ .rodata : { *(.rodata .rodata.*) } :rodata
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym .dynsym.*) }
+ .dynstr : { *(.dynstr .dynstr.*) }
+ .rela.dyn : { *(.rela.dyn) }
+
+ .eh_frame : {
+ HIDDEN(__eh_frame_start = .);
+ *(.eh_frame .eh_frame.*)
+ HIDDEN(__eh_frame_end = .);
+ }
+
+ .eh_frame_hdr : {
+ HIDDEN(__eh_frame_hdr_start = .);
+ *(.eh_frame_hdr .eh_frame_hdr.*)
+ HIDDEN(__eh_frame_hdr_end = .);
+ }
+
+ /* Read-write sections */
+
+ . = ALIGN(0x1000);
+
+ .data : {
+ *(.data .data.*)
+ *(.got .got.*)
+ *(.got.plt .got.plt.*)
+ } :data
+
+ .dynamic : {
+ HIDDEN(__dynamic_start = .);
+ *(.dynamic)
+ }
+
+ /* BSS section */
+
+ . = ALIGN(0x1000);
+
+ .bss : {
+ HIDDEN(__bss_start = .);
+ *(.bss .bss.*)
+ *(COMMON)
+ . = ALIGN(8);
+ HIDDEN(__bss_end = .);
+ } :bss
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/aarch64_pc_windows_gnullvm.rs
new file mode 100644
index 000000000..59c6a95c2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_pc_windows_gnullvm.rs
@@ -0,0 +1,16 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::windows_gnullvm_base::opts();
+ base.max_atomic_width = Some(64);
+ base.features = "+neon,+fp-armv8".into();
+ base.linker = Some("aarch64-w64-mingw32-clang".into());
+
+ Target {
+ llvm_target: "aarch64-pc-windows-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs
new file mode 100644
index 000000000..856ec4fb0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs
@@ -0,0 +1,15 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::windows_msvc_base::opts();
+ base.max_atomic_width = Some(64);
+ base.features = "+neon,+fp-armv8".into();
+
+ Target {
+ llvm_target: "aarch64-pc-windows-msvc".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs
new file mode 100644
index 000000000..2f39c4862
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs
@@ -0,0 +1,18 @@
+use crate::spec::{SanitizerSet, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-unknown-freebsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(128),
+ supported_sanitizers: SanitizerSet::ADDRESS
+ | SanitizerSet::CFI
+ | SanitizerSet::MEMORY
+ | SanitizerSet::THREAD,
+ ..super::freebsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs
new file mode 100644
index 000000000..1d7269c8d
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs
@@ -0,0 +1,15 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::hermit_base::opts();
+ base.max_atomic_width = Some(128);
+ base.features = "+strict-align,+neon,+fp-armv8".into();
+
+ Target {
+ llvm_target: "aarch64-unknown-hermit".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs
new file mode 100644
index 000000000..3006044d5
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs
@@ -0,0 +1,23 @@
+use crate::spec::{SanitizerSet, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+outline-atomics".into(),
+ mcount: "\u{1}_mcount".into(),
+ max_atomic_width: Some(128),
+ supported_sanitizers: SanitizerSet::ADDRESS
+ | SanitizerSet::CFI
+ | SanitizerSet::LEAK
+ | SanitizerSet::MEMORY
+ | SanitizerSet::MEMTAG
+ | SanitizerSet::THREAD
+ | SanitizerSet::HWADDRESS,
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs
new file mode 100644
index 000000000..63023df1d
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs
@@ -0,0 +1,17 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-unknown-linux-gnu_ilp32".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ abi: "ilp32".into(),
+ features: "+outline-atomics".into(),
+ max_atomic_width: Some(128),
+ mcount: "\u{1}_mcount".into(),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs
new file mode 100644
index 000000000..002d0dac2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs
@@ -0,0 +1,14 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.max_atomic_width = Some(128);
+
+ Target {
+ llvm_target: "aarch64-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions { mcount: "\u{1}_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs
new file mode 100644
index 000000000..703f75022
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs
@@ -0,0 +1,15 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-unknown-netbsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ mcount: "__mcount".into(),
+ max_atomic_width: Some(128),
+ ..super::netbsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
new file mode 100644
index 000000000..d3fd7051a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
@@ -0,0 +1,29 @@
+// Generic AArch64 target for bare-metal code - Floating point enabled
+//
+// Can be used in conjunction with the `target-feature` and
+// `target-cpu` compiler flags to opt-in more hardware-specific
+// features.
+//
+// For example, `-C target-cpu=cortex-a53`.
+
+use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let opts = TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ features: "+strict-align,+neon,+fp-armv8".into(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(128),
+ panic_strategy: PanicStrategy::Abort,
+ ..Default::default()
+ };
+ Target {
+ llvm_target: "aarch64-unknown-none".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: opts,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
new file mode 100644
index 000000000..6316abe1b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
@@ -0,0 +1,30 @@
+// Generic AArch64 target for bare-metal code - Floating point disabled
+//
+// Can be used in conjunction with the `target-feature` and
+// `target-cpu` compiler flags to opt-in more hardware-specific
+// features.
+//
+// For example, `-C target-cpu=cortex-a53`.
+
+use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let opts = TargetOptions {
+ abi: "softfloat".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ features: "+strict-align,-neon,-fp-armv8".into(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(128),
+ panic_strategy: PanicStrategy::Abort,
+ ..Default::default()
+ };
+ Target {
+ llvm_target: "aarch64-unknown-none".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: opts,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs
new file mode 100644
index 000000000..3d99040f0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs
@@ -0,0 +1,11 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-unknown-openbsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions { max_atomic_width: Some(128), ..super::openbsd_base::opts() },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs b/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs
new file mode 100644
index 000000000..6c9be4c8e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs
@@ -0,0 +1,14 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::redox_base::opts();
+ base.max_atomic_width = Some(128);
+
+ Target {
+ llvm_target: "aarch64-unknown-redox".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs b/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs
new file mode 100644
index 000000000..162b091b2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs
@@ -0,0 +1,20 @@
+// This defines the aarch64 target for UEFI systems as described in the UEFI specification. See the
+// uefi-base module for generic UEFI options.
+
+use super::uefi_msvc_base;
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = uefi_msvc_base::opts();
+
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Msvc, &["/machine:arm64"]);
+
+ Target {
+ llvm_target: "aarch64-unknown-windows".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs
new file mode 100644
index 000000000..54247fd93
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs
@@ -0,0 +1,14 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::windows_uwp_msvc_base::opts();
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "aarch64-pc-windows-msvc".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs
new file mode 100644
index 000000000..e118553df
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs
@@ -0,0 +1,11 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions { max_atomic_width: Some(128), ..super::vxworks_base::opts() },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs
new file mode 100644
index 000000000..337554dc9
--- /dev/null
+++ b/compiler/rustc_target/src/spec/abi.rs
@@ -0,0 +1,175 @@
+use std::fmt;
+
+use rustc_macros::HashStable_Generic;
+
+#[cfg(test)]
+mod tests;
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
+pub enum Abi {
+ // Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
+ // hashing tests. These are used in many places, so giving them stable values reduces test
+ // churn. The specific values are meaningless.
+ Rust,
+ C { unwind: bool },
+ Cdecl { unwind: bool },
+ Stdcall { unwind: bool },
+ Fastcall { unwind: bool },
+ Vectorcall { unwind: bool },
+ Thiscall { unwind: bool },
+ Aapcs { unwind: bool },
+ Win64 { unwind: bool },
+ SysV64 { unwind: bool },
+ PtxKernel,
+ Msp430Interrupt,
+ X86Interrupt,
+ AmdGpuKernel,
+ EfiApi,
+ AvrInterrupt,
+ AvrNonBlockingInterrupt,
+ CCmseNonSecureCall,
+ Wasm,
+ System { unwind: bool },
+ RustIntrinsic,
+ RustCall,
+ PlatformIntrinsic,
+ Unadjusted,
+ RustCold,
+}
+
+#[derive(Copy, Clone)]
+pub struct AbiData {
+ abi: Abi,
+
+ /// Name of this ABI as we like it called.
+ name: &'static str,
+}
+
+#[allow(non_upper_case_globals)]
+const AbiDatas: &[AbiData] = &[
+ AbiData { abi: Abi::Rust, name: "Rust" },
+ AbiData { abi: Abi::C { unwind: false }, name: "C" },
+ AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" },
+ AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" },
+ AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" },
+ AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" },
+ AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" },
+ AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" },
+ AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" },
+ AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" },
+ AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" },
+ AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" },
+ AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" },
+ AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" },
+ AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" },
+ AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" },
+ AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" },
+ AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" },
+ AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" },
+ AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" },
+ AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" },
+ AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" },
+ AbiData { abi: Abi::AmdGpuKernel, name: "amdgpu-kernel" },
+ AbiData { abi: Abi::EfiApi, name: "efiapi" },
+ AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt" },
+ AbiData { abi: Abi::AvrNonBlockingInterrupt, name: "avr-non-blocking-interrupt" },
+ AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call" },
+ AbiData { abi: Abi::Wasm, name: "wasm" },
+ AbiData { abi: Abi::System { unwind: false }, name: "system" },
+ AbiData { abi: Abi::System { unwind: true }, name: "system-unwind" },
+ AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic" },
+ AbiData { abi: Abi::RustCall, name: "rust-call" },
+ AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic" },
+ AbiData { abi: Abi::Unadjusted, name: "unadjusted" },
+ AbiData { abi: Abi::RustCold, name: "rust-cold" },
+];
+
+/// Returns the ABI with the given name (if any).
+pub fn lookup(name: &str) -> Option<Abi> {
+ AbiDatas.iter().find(|abi_data| name == abi_data.name).map(|&x| x.abi)
+}
+
+pub fn all_names() -> Vec<&'static str> {
+ AbiDatas.iter().map(|d| d.name).collect()
+}
+
+impl Abi {
+ /// Default ABI chosen for `extern fn` declarations without an explicit ABI.
+ pub const FALLBACK: Abi = Abi::C { unwind: false };
+
+ #[inline]
+ pub fn index(self) -> usize {
+ // N.B., this ordering MUST match the AbiDatas array above.
+ // (This is ensured by the test indices_are_correct().)
+ use Abi::*;
+ let i = match self {
+ // Cross-platform ABIs
+ Rust => 0,
+ C { unwind: false } => 1,
+ C { unwind: true } => 2,
+ // Platform-specific ABIs
+ Cdecl { unwind: false } => 3,
+ Cdecl { unwind: true } => 4,
+ Stdcall { unwind: false } => 5,
+ Stdcall { unwind: true } => 6,
+ Fastcall { unwind: false } => 7,
+ Fastcall { unwind: true } => 8,
+ Vectorcall { unwind: false } => 9,
+ Vectorcall { unwind: true } => 10,
+ Thiscall { unwind: false } => 11,
+ Thiscall { unwind: true } => 12,
+ Aapcs { unwind: false } => 13,
+ Aapcs { unwind: true } => 14,
+ Win64 { unwind: false } => 15,
+ Win64 { unwind: true } => 16,
+ SysV64 { unwind: false } => 17,
+ SysV64 { unwind: true } => 18,
+ PtxKernel => 19,
+ Msp430Interrupt => 20,
+ X86Interrupt => 21,
+ AmdGpuKernel => 22,
+ EfiApi => 23,
+ AvrInterrupt => 24,
+ AvrNonBlockingInterrupt => 25,
+ CCmseNonSecureCall => 26,
+ Wasm => 27,
+ // Cross-platform ABIs
+ System { unwind: false } => 28,
+ System { unwind: true } => 29,
+ RustIntrinsic => 30,
+ RustCall => 31,
+ PlatformIntrinsic => 32,
+ Unadjusted => 33,
+ RustCold => 34,
+ };
+ debug_assert!(
+ AbiDatas
+ .iter()
+ .enumerate()
+ .find(|(_, AbiData { abi, .. })| *abi == self)
+ .map(|(index, _)| index)
+ .expect("abi variant has associated data")
+ == i,
+ "Abi index did not match `AbiDatas` ordering"
+ );
+ i
+ }
+
+ #[inline]
+ pub fn data(self) -> &'static AbiData {
+ &AbiDatas[self.index()]
+ }
+
+ pub fn name(self) -> &'static str {
+ self.data().name
+ }
+}
+
+impl fmt::Display for Abi {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ abi => write!(f, "\"{}\"", abi.name()),
+ }
+ }
+}
diff --git a/compiler/rustc_target/src/spec/abi/tests.rs b/compiler/rustc_target/src/spec/abi/tests.rs
new file mode 100644
index 000000000..8bea5e5ef
--- /dev/null
+++ b/compiler/rustc_target/src/spec/abi/tests.rs
@@ -0,0 +1,27 @@
+use super::*;
+
+#[allow(non_snake_case)]
+#[test]
+fn lookup_Rust() {
+ let abi = lookup("Rust");
+ assert!(abi.is_some() && abi.unwrap().data().name == "Rust");
+}
+
+#[test]
+fn lookup_cdecl() {
+ let abi = lookup("cdecl");
+ assert!(abi.is_some() && abi.unwrap().data().name == "cdecl");
+}
+
+#[test]
+fn lookup_baz() {
+ let abi = lookup("baz");
+ assert!(abi.is_none());
+}
+
+#[test]
+fn indices_are_correct() {
+ for (i, abi_data) in AbiDatas.iter().enumerate() {
+ assert_eq!(i, abi_data.abi.index());
+ }
+}
diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs
new file mode 100644
index 000000000..dc06597db
--- /dev/null
+++ b/compiler/rustc_target/src/spec/android_base.rs
@@ -0,0 +1,15 @@
+use crate::spec::TargetOptions;
+
+pub fn opts() -> TargetOptions {
+ let mut base = super::linux_base::opts();
+ base.os = "android".into();
+ base.default_dwarf_version = 2;
+ base.position_independent_executables = true;
+ base.has_thread_local = false;
+ // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
+ // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution
+ // was to always emit `uwtable`).
+ base.default_uwtable = true;
+ base.crt_static_respected = false;
+ base
+}
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
new file mode 100644
index 000000000..15e4fb9be
--- /dev/null
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -0,0 +1,143 @@
+use std::{borrow::Cow, env};
+
+use crate::spec::{cvs, FramePointer, LldFlavor, SplitDebuginfo, TargetOptions};
+
+pub fn opts(os: &'static str) -> TargetOptions {
+ // ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6
+ // either the linker will complain if it is used or the binary will end up
+ // segfaulting at runtime when run on 10.6. Rust by default supports macOS
+ // 10.7+, but there is a standard environment variable,
+ // MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
+ // versions of macOS. For example compiling on 10.10 with
+ // MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
+ // warnings about the usage of ELF TLS.
+ //
+ // Here we detect what version is being requested, defaulting to 10.7. ELF
+ // TLS is flagged as enabled if it looks to be supported. The architecture
+ // only matters for default deployment target which is 11.0 for ARM64 and
+ // 10.7 for everything else.
+ let has_thread_local = macos_deployment_target("x86_64") >= (10, 7);
+
+ TargetOptions {
+ os: os.into(),
+ vendor: "apple".into(),
+ // macOS has -dead_strip, which doesn't rely on function_sections
+ function_sections: false,
+ dynamic_linking: true,
+ linker_is_gnu: false,
+ families: cvs!["unix"],
+ is_like_osx: true,
+ default_dwarf_version: 2,
+ frame_pointer: FramePointer::Always,
+ has_rpath: true,
+ dll_suffix: ".dylib".into(),
+ archive_format: "darwin".into(),
+ has_thread_local,
+ abi_return_struct_as_int: true,
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ lld_flavor: LldFlavor::Ld64,
+
+ // The historical default for macOS targets is to run `dsymutil` which
+ // generates a packed version of debuginfo split from the main file.
+ split_debuginfo: SplitDebuginfo::Packed,
+
+ // This environment variable is pretty magical but is intended for
+ // producing deterministic builds. This was first discovered to be used
+ // by the `ar` tool as a way to control whether or not mtime entries in
+ // the archive headers were set to zero or not. It appears that
+ // eventually the linker got updated to do the same thing and now reads
+ // this environment variable too in recent versions.
+ //
+ // For some more info see the commentary on #47086
+ link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
+
+ ..Default::default()
+ }
+}
+
+fn deployment_target(var_name: &str) -> Option<(u32, u32)> {
+ let deployment_target = env::var(var_name).ok();
+ deployment_target
+ .as_ref()
+ .and_then(|s| s.split_once('.'))
+ .and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok())
+}
+
+fn macos_default_deployment_target(arch: &str) -> (u32, u32) {
+ if arch == "arm64" { (11, 0) } else { (10, 7) }
+}
+
+fn macos_deployment_target(arch: &str) -> (u32, u32) {
+ deployment_target("MACOSX_DEPLOYMENT_TARGET")
+ .unwrap_or_else(|| macos_default_deployment_target(arch))
+}
+
+pub fn macos_llvm_target(arch: &str) -> String {
+ let (major, minor) = macos_deployment_target(arch);
+ format!("{}-apple-macosx{}.{}.0", arch, major, minor)
+}
+
+pub fn macos_link_env_remove() -> Vec<Cow<'static, str>> {
+ let mut env_remove = Vec::with_capacity(2);
+ // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
+ // may occur when we're linking a custom build script while targeting iOS for example.
+ if let Ok(sdkroot) = env::var("SDKROOT") {
+ if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform") {
+ env_remove.push("SDKROOT".into())
+ }
+ }
+ // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
+ // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
+ // although this is apparently ignored when using the linker at "/usr/bin/ld".
+ env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
+ env_remove
+}
+
+fn ios_deployment_target() -> (u32, u32) {
+ deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
+}
+
+pub fn ios_llvm_target(arch: &str) -> String {
+ // Modern iOS tooling extracts information about deployment target
+ // from LC_BUILD_VERSION. This load command will only be emitted when
+ // we build with a version specific `llvm_target`, with the version
+ // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode
+ // to pick it up (since std and core are still built with the fallback
+ // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION).
+ let (major, minor) = ios_deployment_target();
+ format!("{}-apple-ios{}.{}.0", arch, major, minor)
+}
+
+pub fn ios_lld_platform_version() -> String {
+ let (major, minor) = ios_deployment_target();
+ format!("{}.{}", major, minor)
+}
+
+pub fn ios_sim_llvm_target(arch: &str) -> String {
+ let (major, minor) = ios_deployment_target();
+ format!("{}-apple-ios{}.{}.0-simulator", arch, major, minor)
+}
+
+fn tvos_deployment_target() -> (u32, u32) {
+ deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
+}
+
+pub fn tvos_lld_platform_version() -> String {
+ let (major, minor) = tvos_deployment_target();
+ format!("{}.{}", major, minor)
+}
+
+fn watchos_deployment_target() -> (u32, u32) {
+ deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
+}
+
+pub fn watchos_lld_platform_version() -> String {
+ let (major, minor) = watchos_deployment_target();
+ format!("{}.{}", major, minor)
+}
+
+pub fn watchos_sim_llvm_target(arch: &str) -> String {
+ let (major, minor) = watchos_deployment_target();
+ format!("{}-apple-watchos{}.{}.0-simulator", arch, major, minor)
+}
diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs
new file mode 100644
index 000000000..d77558f0f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs
@@ -0,0 +1,113 @@
+use crate::spec::{cvs, LinkArgs, LinkerFlavor, LldFlavor, TargetOptions};
+use std::borrow::Cow;
+
+use Arch::*;
+#[allow(non_camel_case_types)]
+#[derive(Copy, Clone)]
+pub enum Arch {
+ Armv7,
+ Armv7k,
+ Armv7s,
+ Arm64,
+ Arm64_32,
+ I386,
+ X86_64,
+ X86_64_macabi,
+ Arm64_macabi,
+ Arm64_sim,
+}
+
+fn target_arch_name(arch: Arch) -> &'static str {
+ match arch {
+ Armv7 => "armv7",
+ Armv7k => "armv7k",
+ Armv7s => "armv7s",
+ Arm64 | Arm64_macabi | Arm64_sim => "arm64",
+ Arm64_32 => "arm64_32",
+ I386 => "i386",
+ X86_64 | X86_64_macabi => "x86_64",
+ }
+}
+
+fn target_abi(arch: Arch) -> &'static str {
+ match arch {
+ Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | X86_64 => "",
+ X86_64_macabi | Arm64_macabi => "macabi",
+ Arm64_sim => "sim",
+ }
+}
+
+fn target_cpu(arch: Arch) -> &'static str {
+ match arch {
+ Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
+ Armv7k => "cortex-a8",
+ Armv7s => "cortex-a9",
+ Arm64 => "apple-a7",
+ Arm64_32 => "apple-s4",
+ I386 => "yonah",
+ X86_64 => "core2",
+ X86_64_macabi => "core2",
+ Arm64_macabi => "apple-a12",
+ Arm64_sim => "apple-a12",
+ }
+}
+
+fn link_env_remove(arch: Arch) -> Cow<'static, [Cow<'static, str>]> {
+ match arch {
+ Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | X86_64 | Arm64_sim => {
+ cvs!["MACOSX_DEPLOYMENT_TARGET"]
+ }
+ X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
+ }
+}
+
+fn pre_link_args(os: &'static str, arch: Arch) -> LinkArgs {
+ let mut args = LinkArgs::new();
+
+ let target_abi = target_abi(arch);
+
+ let platform_name = match target_abi {
+ "sim" => format!("{}-simulator", os),
+ "macabi" => "mac-catalyst".to_string(),
+ _ => os.to_string(),
+ };
+
+ let platform_version = match os.as_ref() {
+ "ios" => super::apple_base::ios_lld_platform_version(),
+ "tvos" => super::apple_base::tvos_lld_platform_version(),
+ "watchos" => super::apple_base::watchos_lld_platform_version(),
+ _ => unreachable!(),
+ };
+
+ let arch_str = target_arch_name(arch);
+
+ if target_abi != "macabi" {
+ args.insert(LinkerFlavor::Gcc, vec!["-arch".into(), arch_str.into()]);
+ }
+
+ args.insert(
+ LinkerFlavor::Lld(LldFlavor::Ld64),
+ vec![
+ "-arch".into(),
+ arch_str.into(),
+ "-platform_version".into(),
+ platform_name.into(),
+ platform_version.clone().into(),
+ platform_version.into(),
+ ],
+ );
+
+ args
+}
+
+pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
+ TargetOptions {
+ abi: target_abi(arch).into(),
+ cpu: target_cpu(arch).into(),
+ dynamic_linking: false,
+ pre_link_args: pre_link_args(os, arch),
+ link_env_remove: link_env_remove(arch),
+ has_thread_local: false,
+ ..super::apple_base::opts(os)
+ }
+}
diff --git a/compiler/rustc_target/src/spec/arm64_32_apple_watchos.rs b/compiler/rustc_target/src/spec/arm64_32_apple_watchos.rs
new file mode 100644
index 000000000..7b23fe1c4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/arm64_32_apple_watchos.rs
@@ -0,0 +1,28 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("watchos", Arch::Arm64_32);
+ Target {
+ llvm_target: "arm64_32-apple-watchos".into(),
+ pointer_width: 32,
+ data_layout: "e-m:o-p:32:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ features: "+neon,+fp-armv8,+apple-a7".into(),
+ max_atomic_width: Some(64),
+ forces_embed_bitcode: true,
+ // These arguments are not actually invoked - they just have
+ // to look right to pass App Store validation.
+ bitcode_llvm_cmdline: "-triple\0\
+ arm64_32-apple-watchos5.0.0\0\
+ -emit-obj\0\
+ -disable-llvm-passes\0\
+ -target-abi\0\
+ darwinpcs\0\
+ -Os\0"
+ .into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/arm_linux_androideabi.rs b/compiler/rustc_target/src/spec/arm_linux_androideabi.rs
new file mode 100644
index 000000000..bbf1fa315
--- /dev/null
+++ b/compiler/rustc_target/src/spec/arm_linux_androideabi.rs
@@ -0,0 +1,18 @@
+use crate::spec::{SanitizerSet, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "arm-linux-androideabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ // https://developer.android.com/ndk/guides/abis.html#armeabi
+ features: "+strict-align,+v5te".into(),
+ supported_sanitizers: SanitizerSet::ADDRESS,
+ max_atomic_width: Some(32),
+ ..super::android_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs
new file mode 100644
index 000000000..c0f1827ad
--- /dev/null
+++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs
@@ -0,0 +1,17 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "arm-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+strict-align,+v6".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs
new file mode 100644
index 000000000..79b8958c2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs
@@ -0,0 +1,17 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "arm-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ features: "+strict-align,+v6,+vfp2,-d32".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs
new file mode 100644
index 000000000..3ef441d6a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs
@@ -0,0 +1,22 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it
+ // to determine the calling convention and float ABI, and it doesn't
+ // support the "musleabi" value.
+ llvm_target: "arm-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ // Most of these settings are copied from the arm_unknown_linux_gnueabi
+ // target.
+ features: "+strict-align,+v6".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}mcount".into(),
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs
new file mode 100644
index 000000000..eb6660d4c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs
@@ -0,0 +1,22 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ // It's important we use "gnueabihf" and not "musleabihf" here. LLVM
+ // uses it to determine the calling convention and float ABI, and it
+ // doesn't support the "musleabihf" value.
+ llvm_target: "arm-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // Most of these settings are copied from the arm_unknown_linux_gnueabihf
+ // target.
+ features: "+strict-align,+v6,+vfp2,-d32".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}mcount".into(),
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
new file mode 100644
index 000000000..511693abe
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
@@ -0,0 +1,27 @@
+// Targets the Big endian Cortex-R4/R5 processor (ARMv7-R)
+
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armebv7r-unknown-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ endian: Endian::Big,
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ relocation_model: RelocModel::Static,
+ panic_strategy: PanicStrategy::Abort,
+ max_atomic_width: Some(32),
+ emit_debug_gdb_scripts: false,
+ // GCC and Clang default to 8 for arm-none here
+ c_enum_min_bits: 8,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
new file mode 100644
index 000000000..5df4a0a15
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
@@ -0,0 +1,28 @@
+// Targets the Cortex-R4F/R5F processor (ARMv7-R)
+
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armebv7r-unknown-none-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ endian: Endian::Big,
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ relocation_model: RelocModel::Static,
+ panic_strategy: PanicStrategy::Abort,
+ features: "+vfp3,-d32,-fp16".into(),
+ max_atomic_width: Some(32),
+ emit_debug_gdb_scripts: false,
+ // GCC and Clang default to 8 for arm-none here
+ c_enum_min_bits: 8,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs
new file mode 100644
index 000000000..1de63a920
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv4t-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+soft-float,+strict-align".into(),
+ // Atomic operations provided by compiler-builtins
+ max_atomic_width: Some(32),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ has_thumb_interworking: true,
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs
new file mode 100644
index 000000000..b94056310
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv5te-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+soft-float,+strict-align".into(),
+ // Atomic operations provided by compiler-builtins
+ max_atomic_width: Some(32),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ has_thumb_interworking: true,
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs
new file mode 100644
index 000000000..2530971b5
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs
@@ -0,0 +1,23 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ // FIXME: this comment below does not seem applicable?
+ // It's important we use "gnueabihf" and not "musleabihf" here. LLVM
+ // uses it to determine the calling convention and float ABI, and LLVM
+ // doesn't support the "musleabihf" value.
+ llvm_target: "armv5te-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+soft-float,+strict-align".into(),
+ // Atomic operations provided by compiler-builtins
+ max_atomic_width: Some(32),
+ mcount: "\u{1}mcount".into(),
+ has_thumb_interworking: true,
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv5te_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_uclibceabi.rs
new file mode 100644
index 000000000..a51be10a3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv5te_unknown_linux_uclibceabi.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv5te-unknown-linux-uclibcgnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+soft-float,+strict-align".into(),
+ // Atomic operations provided by compiler-builtins
+ max_atomic_width: Some(32),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ has_thumb_interworking: true,
+ ..super::linux_uclibc_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs
new file mode 100644
index 000000000..b7cfccc8b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv6-unknown-freebsd-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // FIXME: change env to "gnu" when cfg_target_abi becomes stable
+ env: "gnueabihf".into(),
+ features: "+v6,+vfp2,-d32".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ ..super::freebsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs
new file mode 100644
index 000000000..6e26f686f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv6-unknown-netbsdelf-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // FIXME: remove env when cfg_target_abi becomes stable
+ env: "eabihf".into(),
+ features: "+v6,+vfp2,-d32".into(),
+ max_atomic_width: Some(64),
+ mcount: "__mcount".into(),
+ ..super::netbsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
new file mode 100644
index 000000000..1bba39393
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
@@ -0,0 +1,38 @@
+use crate::spec::{cvs, LinkerFlavor, RelocModel, Target, TargetOptions};
+
+/// A base target for Nintendo 3DS devices using the devkitARM toolchain.
+///
+/// Requires the devkitARM toolchain for 3DS targets on the host system.
+
+pub fn target() -> Target {
+ let pre_link_args = TargetOptions::link_args(
+ LinkerFlavor::Gcc,
+ &["-specs=3dsx.specs", "-mtune=mpcore", "-mfloat-abi=hard", "-mtp=soft"],
+ );
+
+ Target {
+ llvm_target: "armv6k-none-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ os: "horizon".into(),
+ env: "newlib".into(),
+ vendor: "nintendo".into(),
+ abi: "eabihf".into(),
+ linker_flavor: LinkerFlavor::Gcc,
+ cpu: "mpcore".into(),
+ families: cvs!["unix"],
+ linker: Some("arm-none-eabi-gcc".into()),
+ relocation_model: RelocModel::Static,
+ features: "+vfp2".into(),
+ pre_link_args,
+ exe_suffix: ".elf".into(),
+ no_default_libraries: false,
+ // There are some issues in debug builds with this enabled in certain programs.
+ has_thread_local: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_apple_ios.rs b/compiler/rustc_target/src/spec/armv7_apple_ios.rs
new file mode 100644
index 000000000..57fd74a36
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_apple_ios.rs
@@ -0,0 +1,18 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let llvm_target = super::apple_base::ios_llvm_target("armv7");
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 32,
+ data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ features: "+v7,+vfp3,+neon".into(),
+ max_atomic_width: Some(64),
+ ..opts("ios", Arch::Armv7)
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs
new file mode 100644
index 000000000..38c117a49
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs
@@ -0,0 +1,27 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, Target, TargetOptions};
+
+// This target if is for the baseline of the Android v7a ABI
+// in thumb mode. It's named armv7-* instead of thumbv7-*
+// for historical reasons. See the thumbv7neon variant for
+// enabling NEON.
+
+// See https://developer.android.com/ndk/guides/abis.html#v7a
+// for target ABI requirements.
+
+pub fn target() -> Target {
+ let mut base = super::android_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-march=armv7-a"]);
+ Target {
+ llvm_target: "armv7-none-linux-android".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+v7,+thumb-mode,+thumb2,+vfp3,-d32,-neon".into(),
+ supported_sanitizers: SanitizerSet::ADDRESS,
+ max_atomic_width: Some(64),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs
new file mode 100644
index 000000000..bc37b62de
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7-unknown-freebsd-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // FIXME: change env to "gnu" when cfg_target_abi becomes stable
+ env: "gnueabihf".into(),
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ ..super::freebsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs
new file mode 100644
index 000000000..903042d7e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs
@@ -0,0 +1,20 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for glibc Linux on ARMv7 without thumb-mode, NEON or
+// hardfloat.
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+v7,+thumb2,+soft-float,-neon".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs
new file mode 100644
index 000000000..e39ea49a0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs
@@ -0,0 +1,21 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for glibc Linux on ARMv7 without NEON or
+// thumb-mode. See the thumbv7neon variant for enabling both.
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // Info about features at https://wiki.debian.org/ArmHardFloatPort
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}__gnu_mcount_nc".into(),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs
new file mode 100644
index 000000000..7dae85773
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs
@@ -0,0 +1,26 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for musl Linux on ARMv7 without thumb-mode, NEON or
+// hardfloat.
+
+pub fn target() -> Target {
+ // Most of these settings are copied from the armv7_unknown_linux_gnueabi
+ // target.
+ Target {
+ // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it
+ // to determine the calling convention and float ABI, and it doesn't
+ // support the "musleabi" value.
+ llvm_target: "armv7-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+v7,+thumb2,+soft-float,-neon".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}mcount".into(),
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs
new file mode 100644
index 000000000..ba83964bf
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs
@@ -0,0 +1,25 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for musl Linux on ARMv7 without thumb-mode or NEON.
+
+pub fn target() -> Target {
+ Target {
+ // It's important we use "gnueabihf" and not "musleabihf" here. LLVM
+ // uses it to determine the calling convention and float ABI, and LLVM
+ // doesn't support the "musleabihf" value.
+ llvm_target: "armv7-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ // Most of these settings are copied from the armv7_unknown_linux_gnueabihf
+ // target.
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}mcount".into(),
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs
new file mode 100644
index 000000000..171f67070
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs
@@ -0,0 +1,23 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for uclibc Linux on ARMv7 without NEON,
+// thumb-mode or hardfloat.
+
+pub fn target() -> Target {
+ let base = super::linux_uclibc_base::opts();
+ Target {
+ llvm_target: "armv7-unknown-linux-gnueabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ features: "+v7,+thumb2,+soft-float,-neon".into(),
+ cpu: "generic".into(),
+ max_atomic_width: Some(64),
+ mcount: "_mcount".into(),
+ abi: "eabi".into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabihf.rs
new file mode 100644
index 000000000..d3e95a657
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabihf.rs
@@ -0,0 +1,24 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for uclibc Linux on ARMv7 without NEON or
+// thumb-mode. See the thumbv7neon variant for enabling both.
+
+pub fn target() -> Target {
+ let base = super::linux_uclibc_base::opts();
+ Target {
+ llvm_target: "armv7-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ // Info about features at https://wiki.debian.org/ArmHardFloatPort
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
+ cpu: "generic".into(),
+ max_atomic_width: Some(64),
+ mcount: "_mcount".into(),
+ abi: "eabihf".into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs
new file mode 100644
index 000000000..c89ae2483
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7-unknown-netbsdelf-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // FIXME: remove env when cfg_target_abi becomes stable
+ env: "eabihf".into(),
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
+ max_atomic_width: Some(64),
+ mcount: "__mcount".into(),
+ ..super::netbsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs
new file mode 100644
index 000000000..c1ab90172
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs
@@ -0,0 +1,17 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // Info about features at https://wiki.debian.org/ArmHardFloatPort
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
+ max_atomic_width: Some(64),
+ ..super::vxworks_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabi.rs b/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabi.rs
new file mode 100644
index 000000000..b49dc650b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabi.rs
@@ -0,0 +1,19 @@
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = super::solid_base::opts("asp3");
+ Target {
+ llvm_target: "armv7a-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ linker: Some("arm-kmc-eabi-gcc".into()),
+ features: "+v7,+soft-float,+thumb2,-neon".into(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(64),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabihf.rs
new file mode 100644
index 000000000..7d30238e8
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabihf.rs
@@ -0,0 +1,19 @@
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = super::solid_base::opts("asp3");
+ Target {
+ llvm_target: "armv7a-none-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ linker: Some("arm-kmc-eabi-gcc".into()),
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(64),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
new file mode 100644
index 000000000..cb5cbe158
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
@@ -0,0 +1,40 @@
+// Generic ARMv7-A target for bare-metal code - floating point disabled
+//
+// This is basically the `armv7-unknown-linux-gnueabi` target with some changes
+// (listed below) to bring it closer to the bare-metal `thumb` & `aarch64`
+// targets:
+//
+// - `TargetOptions.features`: added `+strict-align`. rationale: unaligned
+// memory access is disabled on boot on these cores
+// - linker changed to LLD. rationale: C is not strictly needed to build
+// bare-metal binaries (the `gcc` linker has the advantage that it knows where C
+// libraries and crt*.o are but it's not much of an advantage here); LLD is also
+// faster
+// - `panic_strategy` set to `abort`. rationale: matches `thumb` targets
+// - `relocation-model` set to `static`; also no PIE, no relro and no dynamic
+// linking. rationale: matches `thumb` targets
+
+use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let opts = TargetOptions {
+ abi: "eabi".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(64),
+ panic_strategy: PanicStrategy::Abort,
+ emit_debug_gdb_scripts: false,
+ c_enum_min_bits: 8,
+ ..Default::default()
+ };
+ Target {
+ llvm_target: "armv7a-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: opts,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
new file mode 100644
index 000000000..fb5dd2e75
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
@@ -0,0 +1,32 @@
+// Generic ARMv7-A target for bare-metal code - floating point enabled (assumes
+// FPU is present and emits FPU instructions)
+//
+// This is basically the `armv7-unknown-linux-gnueabihf` target with some
+// changes (list in `armv7a_none_eabi.rs`) to bring it closer to the bare-metal
+// `thumb` & `aarch64` targets.
+
+use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let opts = TargetOptions {
+ abi: "eabihf".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".into(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(64),
+ panic_strategy: PanicStrategy::Abort,
+ emit_debug_gdb_scripts: false,
+ // GCC and Clang default to 8 for arm-none here
+ c_enum_min_bits: 8,
+ ..Default::default()
+ };
+ Target {
+ llvm_target: "armv7a-none-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: opts,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7k_apple_watchos.rs b/compiler/rustc_target/src/spec/armv7k_apple_watchos.rs
new file mode 100644
index 000000000..af5d1c2ff
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7k_apple_watchos.rs
@@ -0,0 +1,28 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("watchos", Arch::Armv7k);
+ Target {
+ llvm_target: "armv7k-apple-watchos".into(),
+ pointer_width: 32,
+ data_layout: "e-m:o-p:32:32-Fi8-i64:64-a:0:32-n32-S128".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ features: "+v7,+vfp4,+neon".into(),
+ max_atomic_width: Some(64),
+ forces_embed_bitcode: true,
+ // These arguments are not actually invoked - they just have
+ // to look right to pass App Store validation.
+ bitcode_llvm_cmdline: "-triple\0\
+ armv7k-apple-watchos3.0.0\0\
+ -emit-obj\0\
+ -disable-llvm-passes\0\
+ -target-abi\0\
+ darwinpcs\0\
+ -Os\0"
+ .into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
new file mode 100644
index 000000000..5f1da09b3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
@@ -0,0 +1,26 @@
+// Targets the Little-endian Cortex-R4/R5 processor (ARMv7-R)
+
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7r-unknown-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabi".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ relocation_model: RelocModel::Static,
+ panic_strategy: PanicStrategy::Abort,
+ max_atomic_width: Some(32),
+ emit_debug_gdb_scripts: false,
+ // GCC and Clang default to 8 for arm-none here
+ c_enum_min_bits: 8,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
new file mode 100644
index 000000000..0038ed0df
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
@@ -0,0 +1,27 @@
+// Targets the Little-endian Cortex-R4F/R5F processor (ARMv7-R)
+
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7r-unknown-none-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ relocation_model: RelocModel::Static,
+ panic_strategy: PanicStrategy::Abort,
+ features: "+vfp3,-d32,-fp16".into(),
+ max_atomic_width: Some(32),
+ emit_debug_gdb_scripts: false,
+ // GCC and Clang default to 8 for arm-none here
+ c_enum_min_bits: 8,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs
new file mode 100644
index 000000000..cc17265b2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs
@@ -0,0 +1,16 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7s-apple-ios".into(),
+ pointer_width: 32,
+ data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ features: "+v7,+vfp4,+neon".into(),
+ max_atomic_width: Some(64),
+ ..opts("ios", Arch::Armv7s)
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
new file mode 100644
index 000000000..b4cf2c5ee
--- /dev/null
+++ b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
@@ -0,0 +1,7 @@
+use super::{wasm32_unknown_emscripten, LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut target = wasm32_unknown_emscripten::target();
+ target.add_post_link_args(LinkerFlavor::Em, &["-sWASM=0", "--memory-init-file", "0"]);
+ target
+}
diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs
new file mode 100644
index 000000000..1d441e558
--- /dev/null
+++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs
@@ -0,0 +1,27 @@
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+/// A base target for AVR devices using the GNU toolchain.
+///
+/// Requires GNU avr-gcc and avr-binutils on the host system.
+/// FIXME: Remove the second parameter when const string concatenation is possible.
+pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target {
+ Target {
+ arch: "avr".into(),
+ data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".into(),
+ llvm_target: "avr-unknown-unknown".into(),
+ pointer_width: 16,
+ options: TargetOptions {
+ c_int_width: "16".into(),
+ cpu: target_cpu.into(),
+ exe_suffix: ".elf".into(),
+
+ linker: Some("avr-gcc".into()),
+ eh_frame_header: false,
+ pre_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &[mmcu]),
+ late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]),
+ max_atomic_width: Some(0),
+ atomic_cas: false,
+ ..TargetOptions::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs
new file mode 100644
index 000000000..6c16b03cc
--- /dev/null
+++ b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs
@@ -0,0 +1,5 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ super::avr_gnu_base::target("atmega328", "-mmcu=atmega328")
+}
diff --git a/compiler/rustc_target/src/spec/bpf_base.rs b/compiler/rustc_target/src/spec/bpf_base.rs
new file mode 100644
index 000000000..3c4da6f88
--- /dev/null
+++ b/compiler/rustc_target/src/spec/bpf_base.rs
@@ -0,0 +1,25 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, TargetOptions};
+
+pub fn opts(endian: Endian) -> TargetOptions {
+ TargetOptions {
+ allow_asm: true,
+ endian,
+ linker_flavor: LinkerFlavor::BpfLinker,
+ atomic_cas: false,
+ dynamic_linking: true,
+ no_builtins: true,
+ panic_strategy: PanicStrategy::Abort,
+ position_independent_executables: true,
+ // Disable MergeFunctions since:
+ // - older kernels don't support bpf-to-bpf calls
+ // - on newer kernels, userspace still needs to relocate before calling
+ // BPF_PROG_LOAD and not all BPF libraries do that yet
+ merge_functions: MergeFunctions::Disabled,
+ obj_is_bitcode: true,
+ requires_lto: false,
+ singlethread: true,
+ max_atomic_width: Some(64),
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/bpfeb_unknown_none.rs b/compiler/rustc_target/src/spec/bpfeb_unknown_none.rs
new file mode 100644
index 000000000..174ddfa50
--- /dev/null
+++ b/compiler/rustc_target/src/spec/bpfeb_unknown_none.rs
@@ -0,0 +1,12 @@
+use crate::spec::Target;
+use crate::{abi::Endian, spec::bpf_base};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "bpfeb".into(),
+ data_layout: "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
+ pointer_width: 64,
+ arch: "bpf".into(),
+ options: bpf_base::opts(Endian::Big),
+ }
+}
diff --git a/compiler/rustc_target/src/spec/bpfel_unknown_none.rs b/compiler/rustc_target/src/spec/bpfel_unknown_none.rs
new file mode 100644
index 000000000..7625e7b0e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/bpfel_unknown_none.rs
@@ -0,0 +1,12 @@
+use crate::spec::Target;
+use crate::{abi::Endian, spec::bpf_base};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "bpfel".into(),
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
+ pointer_width: 64,
+ arch: "bpf".into(),
+ options: bpf_base::opts(Endian::Little),
+ }
+}
diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs
new file mode 100644
index 000000000..52ac3622e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/crt_objects.rs
@@ -0,0 +1,157 @@
+//! Object files providing support for basic runtime facilities and added to the produced binaries
+//! at the start and at the end of linking.
+//!
+//! Table of CRT objects for popular toolchains.
+//! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc.
+//! See <https://dev.gentoo.org/~vapier/crt.txt> for some more details.
+//!
+//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi |
+//! |----------------------|------------------------|------------------------|------------------|-------------------|--------------|
+//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 |
+//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
+//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 |
+//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 |
+//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
+//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - |
+//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - |
+//! | wasi-reactor-exe | N/A | N/A | N/A | N/A | crt1-reactor |
+//!
+//! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi |
+//! |-----------------------|---------------|---------------|----------------|--------|------|
+//! | dynamic-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - |
+//! | dynamic-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - |
+//! | static-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - |
+//! | static-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - |
+//! | dynamic-dylib | crtendS, crtn | crtendS, crtn | crtend_so | crtend | - |
+//! | static-dylib (gcc) | crtend, crtn | crtendS, crtn | crtend_so | crtend | - |
+//! | static-dylib (clang) | crtendS, crtn | N/A | crtend_so | crtend | - |
+//!
+//! Use cases for rustc linking the CRT objects explicitly:
+//! - rustc needs to add its own Rust-specific objects (mingw is the example)
+//! - gcc wrapper cannot be used for some reason and linker like ld or lld is used directly.
+//! - gcc wrapper pulls wrong CRT objects (e.g. from glibc when we are targeting musl).
+//!
+//! In general it is preferable to rely on the target's native toolchain to pull the objects.
+//! However, for some targets (musl, mingw) rustc historically provides a more self-contained
+//! installation not requiring users to install the native target's toolchain.
+//! In that case rustc distributes the objects as a part of the target's Rust toolchain
+//! and falls back to linking with them manually.
+//! Unlike native toolchains, rustc only currently adds the libc's objects during linking,
+//! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710)
+//! when linking in self-contained mode.
+
+use crate::json::{Json, ToJson};
+use crate::spec::LinkOutputKind;
+use std::borrow::Cow;
+use std::collections::BTreeMap;
+use std::str::FromStr;
+
+pub type CrtObjects = BTreeMap<LinkOutputKind, Vec<Cow<'static, str>>>;
+
+pub(super) fn new(obj_table: &[(LinkOutputKind, &[&'static str])]) -> CrtObjects {
+ obj_table.iter().map(|(z, k)| (*z, k.iter().map(|b| (*b).into()).collect())).collect()
+}
+
+pub(super) fn all(obj: &'static str) -> CrtObjects {
+ new(&[
+ (LinkOutputKind::DynamicNoPicExe, &[obj]),
+ (LinkOutputKind::DynamicPicExe, &[obj]),
+ (LinkOutputKind::StaticNoPicExe, &[obj]),
+ (LinkOutputKind::StaticPicExe, &[obj]),
+ (LinkOutputKind::DynamicDylib, &[obj]),
+ (LinkOutputKind::StaticDylib, &[obj]),
+ ])
+}
+
+pub(super) fn pre_musl_fallback() -> CrtObjects {
+ new(&[
+ (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o", "crtbegin.o"]),
+ (LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o", "crtbeginS.o"]),
+ (LinkOutputKind::StaticNoPicExe, &["crt1.o", "crti.o", "crtbegin.o"]),
+ (LinkOutputKind::StaticPicExe, &["rcrt1.o", "crti.o", "crtbeginS.o"]),
+ (LinkOutputKind::DynamicDylib, &["crti.o", "crtbeginS.o"]),
+ (LinkOutputKind::StaticDylib, &["crti.o", "crtbeginS.o"]),
+ ])
+}
+
+pub(super) fn post_musl_fallback() -> CrtObjects {
+ new(&[
+ (LinkOutputKind::DynamicNoPicExe, &["crtend.o", "crtn.o"]),
+ (LinkOutputKind::DynamicPicExe, &["crtendS.o", "crtn.o"]),
+ (LinkOutputKind::StaticNoPicExe, &["crtend.o", "crtn.o"]),
+ (LinkOutputKind::StaticPicExe, &["crtendS.o", "crtn.o"]),
+ (LinkOutputKind::DynamicDylib, &["crtendS.o", "crtn.o"]),
+ (LinkOutputKind::StaticDylib, &["crtendS.o", "crtn.o"]),
+ ])
+}
+
+pub(super) fn pre_mingw_fallback() -> CrtObjects {
+ new(&[
+ (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]),
+ (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]),
+ (LinkOutputKind::StaticNoPicExe, &["crt2.o", "rsbegin.o"]),
+ (LinkOutputKind::StaticPicExe, &["crt2.o", "rsbegin.o"]),
+ (LinkOutputKind::DynamicDylib, &["dllcrt2.o", "rsbegin.o"]),
+ (LinkOutputKind::StaticDylib, &["dllcrt2.o", "rsbegin.o"]),
+ ])
+}
+
+pub(super) fn post_mingw_fallback() -> CrtObjects {
+ all("rsend.o")
+}
+
+pub(super) fn pre_mingw() -> CrtObjects {
+ all("rsbegin.o")
+}
+
+pub(super) fn post_mingw() -> CrtObjects {
+ all("rsend.o")
+}
+
+pub(super) fn pre_wasi_fallback() -> CrtObjects {
+ // Use crt1-command.o instead of crt1.o to enable support for new-style
+ // commands. See https://reviews.llvm.org/D81689 for more info.
+ new(&[
+ (LinkOutputKind::DynamicNoPicExe, &["crt1-command.o"]),
+ (LinkOutputKind::DynamicPicExe, &["crt1-command.o"]),
+ (LinkOutputKind::StaticNoPicExe, &["crt1-command.o"]),
+ (LinkOutputKind::StaticPicExe, &["crt1-command.o"]),
+ (LinkOutputKind::WasiReactorExe, &["crt1-reactor.o"]),
+ ])
+}
+
+pub(super) fn post_wasi_fallback() -> CrtObjects {
+ new(&[])
+}
+
+/// Which logic to use to determine whether to fall back to the "self-contained" mode or not.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum CrtObjectsFallback {
+ Musl,
+ Mingw,
+ Wasm,
+}
+
+impl FromStr for CrtObjectsFallback {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<CrtObjectsFallback, ()> {
+ Ok(match s {
+ "musl" => CrtObjectsFallback::Musl,
+ "mingw" => CrtObjectsFallback::Mingw,
+ "wasm" => CrtObjectsFallback::Wasm,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl ToJson for CrtObjectsFallback {
+ fn to_json(&self) -> Json {
+ match *self {
+ CrtObjectsFallback::Musl => "musl",
+ CrtObjectsFallback::Mingw => "mingw",
+ CrtObjectsFallback::Wasm => "wasm",
+ }
+ .to_json()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs
new file mode 100644
index 000000000..de2be7817
--- /dev/null
+++ b/compiler/rustc_target/src/spec/dragonfly_base.rs
@@ -0,0 +1,14 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "dragonfly".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ has_rpath: true,
+ position_independent_executables: true,
+ relro_level: RelroLevel::Full,
+ default_dwarf_version: 2,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs
new file mode 100644
index 000000000..8c141aaae
--- /dev/null
+++ b/compiler/rustc_target/src/spec/freebsd_base.rs
@@ -0,0 +1,15 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "freebsd".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ has_rpath: true,
+ position_independent_executables: true,
+ relro_level: RelroLevel::Full,
+ abi_return_struct_as_int: true,
+ default_dwarf_version: 2,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs
new file mode 100644
index 000000000..df1e3275f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/fuchsia_base.rs
@@ -0,0 +1,38 @@
+use crate::spec::{crt_objects, cvs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let pre_link_args = TargetOptions::link_args(
+ LinkerFlavor::Ld,
+ &[
+ "--build-id",
+ "--hash-style=gnu",
+ "-z",
+ "max-page-size=4096",
+ "-z",
+ "now",
+ "-z",
+ "rodynamic",
+ "-z",
+ "separate-loadable-segments",
+ "--pack-dyn-relocs=relr",
+ ],
+ );
+
+ TargetOptions {
+ os: "fuchsia".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ pre_link_args,
+ pre_link_objects: crt_objects::new(&[
+ (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]),
+ (LinkOutputKind::DynamicPicExe, &["Scrt1.o"]),
+ (LinkOutputKind::StaticNoPicExe, &["Scrt1.o"]),
+ (LinkOutputKind::StaticPicExe, &["Scrt1.o"]),
+ ]),
+ position_independent_executables: true,
+ has_thread_local: true,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs
new file mode 100644
index 000000000..8ab874410
--- /dev/null
+++ b/compiler/rustc_target/src/spec/haiku_base.rs
@@ -0,0 +1,11 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "haiku".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ relro_level: RelroLevel::Full,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs
new file mode 100644
index 000000000..562ccef7e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/hermit_base.rs
@@ -0,0 +1,21 @@
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel};
+
+pub fn opts() -> TargetOptions {
+ let pre_link_args = TargetOptions::link_args(
+ LinkerFlavor::Ld,
+ &["--build-id", "--hash-style=gnu", "--Bstatic"],
+ );
+
+ TargetOptions {
+ os: "hermit".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ has_thread_local: true,
+ pre_link_args,
+ panic_strategy: PanicStrategy::Abort,
+ position_independent_executables: true,
+ static_position_independent_executables: true,
+ tls_model: TlsModel::InitialExec,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
new file mode 100644
index 000000000..cc2c78c69
--- /dev/null
+++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
@@ -0,0 +1,30 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "hexagonv60".into();
+ base.max_atomic_width = Some(32);
+ // FIXME: HVX length defaults are per-CPU
+ base.features = "-small-data,+hvx-length128b".into();
+
+ base.crt_static_default = false;
+ base.has_rpath = true;
+ base.linker_is_gnu = false;
+ base.dynamic_linking = true;
+
+ base.c_enum_min_bits = 8;
+
+ Target {
+ llvm_target: "hexagon-unknown-linux-musl".into(),
+ pointer_width: 32,
+ data_layout: concat!(
+ "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32",
+ ":32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32",
+ ":32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048",
+ ":2048:2048"
+ )
+ .into(),
+ arch: "hexagon".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i386_apple_ios.rs b/compiler/rustc_target/src/spec/i386_apple_ios.rs
new file mode 100644
index 000000000..8b6266c58
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i386_apple_ios.rs
@@ -0,0 +1,22 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("ios", Arch::I386);
+ let llvm_target = super::apple_base::ios_sim_llvm_target("i386");
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 32,
+ data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:128-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(64),
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ stack_probes: StackProbeType::Call,
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs
new file mode 100644
index 000000000..801a88933
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs
@@ -0,0 +1,8 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::i686_unknown_linux_gnu::target();
+ base.cpu = "i386".into();
+ base.llvm_target = "i386-unknown-linux-gnu".into();
+ base
+}
diff --git a/compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs
new file mode 100644
index 000000000..a11fbecc3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs
@@ -0,0 +1,8 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::i686_unknown_linux_gnu::target();
+ base.cpu = "i486".into();
+ base.llvm_target = "i486-unknown-linux-gnu".into();
+ base
+}
diff --git a/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs
new file mode 100644
index 000000000..befb0f89f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs
@@ -0,0 +1,8 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::i686_pc_windows_msvc::target();
+ base.cpu = "pentium".into();
+ base.llvm_target = "i586-pc-windows-msvc".into();
+ base
+}
diff --git a/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs
new file mode 100644
index 000000000..b699a7af1
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs
@@ -0,0 +1,8 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::i686_unknown_linux_gnu::target();
+ base.cpu = "pentium".into();
+ base.llvm_target = "i586-unknown-linux-gnu".into();
+ base
+}
diff --git a/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs
new file mode 100644
index 000000000..55a26eb00
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs
@@ -0,0 +1,8 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::i686_unknown_linux_musl::target();
+ base.cpu = "pentium".into();
+ base.llvm_target = "i586-unknown-linux-musl".into();
+ base
+}
diff --git a/compiler/rustc_target/src/spec/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/i686_apple_darwin.rs
new file mode 100644
index 000000000..1718bd77b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_apple_darwin.rs
@@ -0,0 +1,28 @@
+use crate::spec::{FramePointer, LinkerFlavor, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::apple_base::opts("macos");
+ base.cpu = "yonah".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove());
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.frame_pointer = FramePointer::Always;
+
+ // Clang automatically chooses a more specific target based on
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // correctly, we do too.
+ let arch = "i686";
+ let llvm_target = super::apple_base::macos_llvm_target(&arch);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 32,
+ data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:128-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: TargetOptions { mcount: "\u{1}mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_linux_android.rs b/compiler/rustc_target/src/spec/i686_linux_android.rs
new file mode 100644
index 000000000..bdaf5c990
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_linux_android.rs
@@ -0,0 +1,26 @@
+use crate::spec::{SanitizerSet, StackProbeType, Target, TargetOptions};
+
+// See https://developer.android.com/ndk/guides/abis.html#x86
+// for target ABI requirements.
+
+pub fn target() -> Target {
+ let mut base = super::android_base::opts();
+
+ base.max_atomic_width = Some(64);
+
+ // https://developer.android.com/ndk/guides/abis.html#x86
+ base.cpu = "pentiumpro".into();
+ base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".into();
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "i686-linux-android".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: TargetOptions { supported_sanitizers: SanitizerSet::ADDRESS, ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs
new file mode 100644
index 000000000..631865439
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs
@@ -0,0 +1,24 @@
+use crate::spec::{FramePointer, LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::windows_gnu_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.frame_pointer = FramePointer::Always; // Required for backtraces
+ base.linker = Some("i686-w64-mingw32-gcc".into());
+
+ // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
+ // space available to x86 Windows binaries on x86_64.
+ base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pe", "--large-address-aware"]);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-Wl,--large-address-aware"]);
+
+ Target {
+ llvm_target: "i686-pc-windows-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs
new file mode 100644
index 000000000..f4ceaa1ca
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs
@@ -0,0 +1,32 @@
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::windows_msvc_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+
+ base.add_pre_link_args(
+ LinkerFlavor::Msvc,
+ &[
+ // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
+ // space available to x86 Windows binaries on x86_64.
+ "/LARGEADDRESSAWARE",
+ // Ensure the linker will only produce an image if it can also produce a table of
+ // the image's safe exception handlers.
+ // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers
+ "/SAFESEH",
+ ],
+ );
+ // Workaround for #95429
+ base.has_thread_local = false;
+
+ Target {
+ llvm_target: "i686-pc-windows-msvc".into(),
+ pointer_width: 32,
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:128-n8:16:32-a:0:32-S32"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs
new file mode 100644
index 000000000..aff284bf2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::freebsd_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-Wl,-znotext"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "i686-unknown-freebsd".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs
new file mode 100644
index 000000000..87aa74e40
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::haiku_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "i686-unknown-haiku".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs
new file mode 100644
index 000000000..765803d16
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "i686-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs
new file mode 100644
index 000000000..d94928043
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs
@@ -0,0 +1,34 @@
+use crate::spec::{FramePointer, LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-Wl,-melf_i386"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ // The unwinder used by i686-unknown-linux-musl, the LLVM libunwind
+ // implementation, apparently relies on frame pointers existing... somehow.
+ // It's not clear to me why nor where this dependency is introduced, but the
+ // test suite does not pass with frame pointers eliminated and it passes
+ // with frame pointers present.
+ //
+ // If you think that this is no longer necessary, then please feel free to
+ // ignore! If it still passes the test suite and the bots then sounds good
+ // to me.
+ //
+ // This may or may not be related to this bug:
+ // https://llvm.org/bugs/show_bug.cgi?id=30879
+ base.frame_pointer = FramePointer::Always;
+
+ Target {
+ llvm_target: "i686-unknown-linux-musl".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs
new file mode 100644
index 000000000..8de698b51
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::netbsd_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "i686-unknown-netbsdelf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: TargetOptions { mcount: "__mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs
new file mode 100644
index 000000000..7f25a1a16
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::openbsd_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-fuse-ld=lld"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "i686-unknown-openbsd".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/i686_unknown_uefi.rs
new file mode 100644
index 000000000..a2e42c5e6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_unknown_uefi.rs
@@ -0,0 +1,89 @@
+// This defines the ia32 target for UEFI systems as described in the UEFI specification. See the
+// uefi-base module for generic UEFI options. On ia32 systems
+// UEFI systems always run in protected-mode, have the interrupt-controller pre-configured and
+// force a single-CPU execution.
+// The cdecl ABI is used. It differs from the stdcall or fastcall ABI.
+// "i686-unknown-windows" is used to get the minimal subset of windows-specific features.
+
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::uefi_msvc_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+
+ // We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
+ // enable these CPU features explicitly before their first use, otherwise their instructions
+ // will trigger an exception. Rust does not inject any code that enables AVX/MMX/SSE
+ // instruction sets, so this must be done by the firmware. However, existing firmware is known
+ // to leave these uninitialized, thus triggering exceptions if we make use of them. Which is
+ // why we avoid them and instead use soft-floats. This is also what GRUB and friends did so
+ // far.
+ // If you initialize FP units yourself, you can override these flags with custom linker
+ // arguments, thus giving you access to full MMX/SSE acceleration.
+ base.features = "-mmx,-sse,+soft-float".into();
+
+ // Use -GNU here, because of the reason below:
+ // Background and Problem:
+ // If we use i686-unknown-windows, the LLVM IA32 MSVC generates compiler intrinsic
+ // _alldiv, _aulldiv, _allrem, _aullrem, _allmul, which will cause undefined symbol.
+ // A real issue is __aulldiv() is referred by __udivdi3() - udivmod_inner!(), from
+ // https://github.com/rust-lang-nursery/compiler-builtins.
+ // As result, rust-lld generates link error finally.
+ // Root-cause:
+ // In rust\src\llvm-project\llvm\lib\Target\X86\X86ISelLowering.cpp,
+ // we have below code to use MSVC intrinsics. It assumes MSVC target
+ // will link MSVC library. But that is NOT true in UEFI environment.
+ // UEFI does not link any MSVC or GCC standard library.
+ // if (Subtarget.isTargetKnownWindowsMSVC() ||
+ // Subtarget.isTargetWindowsItanium()) {
+ // // Setup Windows compiler runtime calls.
+ // setLibcallName(RTLIB::SDIV_I64, "_alldiv");
+ // setLibcallName(RTLIB::UDIV_I64, "_aulldiv");
+ // setLibcallName(RTLIB::SREM_I64, "_allrem");
+ // setLibcallName(RTLIB::UREM_I64, "_aullrem");
+ // setLibcallName(RTLIB::MUL_I64, "_allmul");
+ // setLibcallCallingConv(RTLIB::SDIV_I64, CallingConv::X86_StdCall);
+ // setLibcallCallingConv(RTLIB::UDIV_I64, CallingConv::X86_StdCall);
+ // setLibcallCallingConv(RTLIB::SREM_I64, CallingConv::X86_StdCall);
+ // setLibcallCallingConv(RTLIB::UREM_I64, CallingConv::X86_StdCall);
+ // setLibcallCallingConv(RTLIB::MUL_I64, CallingConv::X86_StdCall);
+ // }
+ // The compiler intrinsics should be implemented by compiler-builtins.
+ // Unfortunately, compiler-builtins has not provided those intrinsics yet. Such as:
+ // i386/divdi3.S
+ // i386/lshrdi3.S
+ // i386/moddi3.S
+ // i386/muldi3.S
+ // i386/udivdi3.S
+ // i386/umoddi3.S
+ // Possible solution:
+ // 1. Eliminate Intrinsics generation.
+ // 1.1 Choose different target to bypass isTargetKnownWindowsMSVC().
+ // 1.2 Remove the "Setup Windows compiler runtime calls" in LLVM
+ // 2. Implement Intrinsics.
+ // We evaluated all options.
+ // #2 is hard because we need implement the intrinsics (_aulldiv) generated
+ // from the other intrinsics (__udivdi3) implementation with the same
+ // functionality (udivmod_inner). If we let _aulldiv() call udivmod_inner!(),
+ // then we are in loop. We may have to find another way to implement udivmod_inner!().
+ // #1.2 may break the existing usage.
+ // #1.1 seems the simplest solution today.
+ // The IA32 -gnu calling convention is same as the one defined in UEFI specification.
+ // It uses cdecl, EAX/ECX/EDX as volatile register, and EAX/EDX as return value.
+ // We also checked the LLVM X86TargetLowering, the differences between -gnu and -msvc
+ // is fmodf(f32), longjmp() and TLS. None of them impacts the UEFI code.
+ // As a result, we choose -gnu for i686 version before those intrinsics are implemented in
+ // compiler-builtins. After compiler-builtins implements all required intrinsics, we may
+ // remove -gnu and use the default one.
+ Target {
+ llvm_target: "i686-unknown-windows-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .into(),
+ arch: "x86".into(),
+
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs
new file mode 100644
index 000000000..d52810d2f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs
@@ -0,0 +1,23 @@
+use crate::spec::{FramePointer, LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::windows_uwp_gnu_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.frame_pointer = FramePointer::Always; // Required for backtraces
+
+ // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
+ // space available to x86 Windows binaries on x86_64.
+ base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pe", "--large-address-aware"]);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-Wl,--large-address-aware"]);
+
+ Target {
+ llvm_target: "i686-pc-windows-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs
new file mode 100644
index 000000000..4c657fe90
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs
@@ -0,0 +1,17 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::windows_uwp_msvc_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "i686-pc-windows-msvc".into(),
+ pointer_width: 32,
+ data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:128-n8:16:32-a:0:32-S32"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs
new file mode 100644
index 000000000..f62404e82
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::vxworks_base::opts();
+ base.cpu = "pentium4".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "i686-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ f64:32:64-f80:32-n8:16:32-S128"
+ .into(),
+ arch: "x86".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs
new file mode 100644
index 000000000..77e000474
--- /dev/null
+++ b/compiler/rustc_target/src/spec/illumos_base.rs
@@ -0,0 +1,59 @@
+use crate::spec::{cvs, FramePointer, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let late_link_args = TargetOptions::link_args(
+ LinkerFlavor::Gcc,
+ &[
+ // The illumos libc contains a stack unwinding implementation, as
+ // does libgcc_s. The latter implementation includes several
+ // additional symbols that are not always in base libc. To force
+ // the consistent use of just one unwinder, we ensure libc appears
+ // after libgcc_s in the NEEDED list for the resultant binary by
+ // ignoring any attempts to add it as a dynamic dependency until the
+ // very end.
+ // FIXME: This should be replaced by a more complete and generic
+ // mechanism for controlling the order of library arguments passed
+ // to the linker.
+ "-lc",
+ // LLVM will insert calls to the stack protector functions
+ // "__stack_chk_fail" and "__stack_chk_guard" into code in native
+ // object files. Some platforms include these symbols directly in
+ // libc, but at least historically these have been provided in
+ // libssp.so on illumos and Solaris systems.
+ "-lssp",
+ ],
+ );
+
+ TargetOptions {
+ os: "illumos".into(),
+ dynamic_linking: true,
+ has_rpath: true,
+ families: cvs!["unix"],
+ is_like_solaris: true,
+ linker_is_gnu: false,
+ limit_rdylib_exports: false, // Linker doesn't support this
+ frame_pointer: FramePointer::Always,
+ eh_frame_header: false,
+ late_link_args,
+
+ // While we support ELF TLS, rust requires a way to register
+ // cleanup handlers (in C, this would be something along the lines of:
+ // void register_callback(void (*fn)(void *), void *arg);
+ // (see src/libstd/sys/unix/fast_thread_local.rs) that is currently
+ // missing in illumos. For now at least, we must fallback to using
+ // pthread_{get,set}specific.
+ //has_thread_local: true,
+
+ // FIXME: Currently, rust is invoking cc to link, which ends up
+ // causing these to get included twice. We should eventually transition
+ // to having rustc invoke ld directly, in which case these will need to
+ // be uncommented.
+ //
+ // We want XPG6 behavior from libc and libm. See standards(5)
+ //pre_link_objects_exe: vec![
+ // "/usr/lib/amd64/values-Xc.o".into(),
+ // "/usr/lib/amd64/values-xpg6.o".into(),
+ //],
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs
new file mode 100644
index 000000000..a08756861
--- /dev/null
+++ b/compiler/rustc_target/src/spec/l4re_base.rs
@@ -0,0 +1,14 @@
+use crate::spec::{cvs, LinkerFlavor, PanicStrategy, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "l4re".into(),
+ env: "uclibc".into(),
+ linker_flavor: LinkerFlavor::L4Bender,
+ panic_strategy: PanicStrategy::Abort,
+ linker: Some("l4-bender".into()),
+ linker_is_gnu: false,
+ families: cvs!["unix"],
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs
new file mode 100644
index 000000000..f4fce3b40
--- /dev/null
+++ b/compiler/rustc_target/src/spec/linux_base.rs
@@ -0,0 +1,15 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "linux".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ has_rpath: true,
+ position_independent_executables: true,
+ relro_level: RelroLevel::Full,
+ has_thread_local: true,
+ crt_static_respected: true,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/linux_gnu_base.rs b/compiler/rustc_target/src/spec/linux_gnu_base.rs
new file mode 100644
index 000000000..8d6b3f185
--- /dev/null
+++ b/compiler/rustc_target/src/spec/linux_gnu_base.rs
@@ -0,0 +1,5 @@
+use crate::spec::TargetOptions;
+
+pub fn opts() -> TargetOptions {
+ TargetOptions { env: "gnu".into(), ..super::linux_base::opts() }
+}
diff --git a/compiler/rustc_target/src/spec/linux_kernel_base.rs b/compiler/rustc_target/src/spec/linux_kernel_base.rs
new file mode 100644
index 000000000..0f5d85205
--- /dev/null
+++ b/compiler/rustc_target/src/spec/linux_kernel_base.rs
@@ -0,0 +1,19 @@
+use crate::spec::TargetOptions;
+use crate::spec::{FramePointer, PanicStrategy, RelocModel, RelroLevel, StackProbeType};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ env: "gnu".into(),
+ disable_redzone: true,
+ panic_strategy: PanicStrategy::Abort,
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ stack_probes: StackProbeType::Call,
+ frame_pointer: FramePointer::Always,
+ position_independent_executables: true,
+ needs_plt: true,
+ relro_level: RelroLevel::Full,
+ relocation_model: RelocModel::Static,
+
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs
new file mode 100644
index 000000000..207a87ab0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/linux_musl_base.rs
@@ -0,0 +1,16 @@
+use crate::spec::crt_objects::{self, CrtObjectsFallback};
+use crate::spec::TargetOptions;
+
+pub fn opts() -> TargetOptions {
+ let mut base = super::linux_base::opts();
+
+ base.env = "musl".into();
+ base.pre_link_objects_fallback = crt_objects::pre_musl_fallback();
+ base.post_link_objects_fallback = crt_objects::post_musl_fallback();
+ base.crt_objects_fallback = Some(CrtObjectsFallback::Musl);
+
+ // These targets statically link libc by default
+ base.crt_static_default = true;
+
+ base
+}
diff --git a/compiler/rustc_target/src/spec/linux_uclibc_base.rs b/compiler/rustc_target/src/spec/linux_uclibc_base.rs
new file mode 100644
index 000000000..4ba480ffe
--- /dev/null
+++ b/compiler/rustc_target/src/spec/linux_uclibc_base.rs
@@ -0,0 +1,5 @@
+use crate::spec::TargetOptions;
+
+pub fn opts() -> TargetOptions {
+ TargetOptions { env: "uclibc".into(), ..super::linux_base::opts() }
+}
diff --git a/compiler/rustc_target/src/spec/m68k_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/m68k_unknown_linux_gnu.rs
new file mode 100644
index 000000000..ebd74012d
--- /dev/null
+++ b/compiler/rustc_target/src/spec/m68k_unknown_linux_gnu.rs
@@ -0,0 +1,15 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "m68k-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16".into(),
+ arch: "m68k".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs
new file mode 100644
index 000000000..3c6ef52c6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs
@@ -0,0 +1,26 @@
+/// A target tuple for OpenWrt MIPS64 targets
+///
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "mips64r2".into();
+ base.features = "+mips64r2,+soft-float".into();
+ base.max_atomic_width = Some(64);
+ base.crt_static_default = false;
+
+ Target {
+ // LLVM doesn't recognize "muslabi64" yet.
+ llvm_target: "mips64-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(),
+ arch: "mips64".into(),
+ options: TargetOptions {
+ abi: "abi64".into(),
+ endian: Endian::Big,
+ mcount: "_mcount".into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs
new file mode 100644
index 000000000..fc5dbd114
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs
@@ -0,0 +1,22 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mips64-unknown-linux-gnuabi64".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(),
+ arch: "mips64".into(),
+ options: TargetOptions {
+ abi: "abi64".into(),
+ endian: Endian::Big,
+ // NOTE(mips64r2) matches C toolchain
+ cpu: "mips64r2".into(),
+ features: "+mips64r2".into(),
+ max_atomic_width: Some(64),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs
new file mode 100644
index 000000000..465e97a02
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs
@@ -0,0 +1,22 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "mips64r2".into();
+ base.features = "+mips64r2".into();
+ base.max_atomic_width = Some(64);
+ Target {
+ // LLVM doesn't recognize "muslabi64" yet.
+ llvm_target: "mips64-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(),
+ arch: "mips64".into(),
+ options: TargetOptions {
+ abi: "abi64".into(),
+ endian: Endian::Big,
+ mcount: "_mcount".into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs
new file mode 100644
index 000000000..e0d5f6f57
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs
@@ -0,0 +1,20 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mips64el-unknown-linux-gnuabi64".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(),
+ arch: "mips64".into(),
+ options: TargetOptions {
+ abi: "abi64".into(),
+ // NOTE(mips64r2) matches C toolchain
+ cpu: "mips64r2".into(),
+ features: "+mips64r2".into(),
+ max_atomic_width: Some(64),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs
new file mode 100644
index 000000000..75575eb7e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs
@@ -0,0 +1,16 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "mips64r2".into();
+ base.features = "+mips64r2".into();
+ base.max_atomic_width = Some(64);
+ Target {
+ // LLVM doesn't recognize "muslabi64" yet.
+ llvm_target: "mips64el-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(),
+ arch: "mips64".into(),
+ options: TargetOptions { abi: "abi64".into(), mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs
new file mode 100644
index 000000000..8df8b0b4c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs
@@ -0,0 +1,20 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mips-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+ options: TargetOptions {
+ endian: Endian::Big,
+ cpu: "mips32r2".into(),
+ features: "+mips32r2,+fpxx,+nooddspreg".into(),
+ max_atomic_width: Some(32),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs
new file mode 100644
index 000000000..c2846313a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "mips32r2".into();
+ base.features = "+mips32r2,+soft-float".into();
+ base.max_atomic_width = Some(32);
+ base.crt_static_default = false;
+ Target {
+ llvm_target: "mips-unknown-linux-musl".into(),
+ pointer_width: 32,
+ data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs
new file mode 100644
index 000000000..c59bb5fdd
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs
@@ -0,0 +1,20 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mips-unknown-linux-uclibc".into(),
+ pointer_width: 32,
+ data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+ options: TargetOptions {
+ endian: Endian::Big,
+ cpu: "mips32r2".into(),
+ features: "+mips32r2,+soft-float".into(),
+ max_atomic_width: Some(32),
+ mcount: "_mcount".into(),
+
+ ..super::linux_uclibc_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
new file mode 100644
index 000000000..cfc8ec21c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
@@ -0,0 +1,34 @@
+use crate::spec::{cvs, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, LldFlavor, RelocModel};
+
+// The PSP has custom linker requirements.
+const LINKER_SCRIPT: &str = include_str!("./mipsel_sony_psp_linker_script.ld");
+
+pub fn target() -> Target {
+ let pre_link_args = TargetOptions::link_args(LinkerFlavor::Ld, &["--emit-relocs", "--nmagic"]);
+
+ Target {
+ llvm_target: "mipsel-sony-psp".into(),
+ pointer_width: 32,
+ data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+
+ options: TargetOptions {
+ os: "psp".into(),
+ vendor: "sony".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ cpu: "mips2".into(),
+ linker: Some("rust-lld".into()),
+ relocation_model: RelocModel::Static,
+
+ // PSP FPU only supports single precision floats.
+ features: "+single-float".into(),
+
+ // PSP does not support trap-on-condition instructions.
+ llvm_args: cvs!["-mno-check-zero-division"],
+ pre_link_args,
+ link_script: Some(LINKER_SCRIPT.into()),
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld b/compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld
new file mode 100644
index 000000000..9eb35ad9f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld
@@ -0,0 +1,45 @@
+ENTRY(module_start)
+SECTIONS
+{
+ /* PRX format requires text to begin at 0 */
+ .text 0 : { *(.text .text.*) }
+
+ /* Sort stubs for convenient ordering */
+ .sceStub.text : { *(.sceStub.text) *(SORT(.sceStub.text.*)) }
+
+ /* PSP import library stub sections. Bundles together `.lib.stub.entry.*`
+ * sections for better `--gc-sections` support. */
+ .lib.stub.top : { *(.lib.stub.top) }
+ .lib.stub : { *(.lib.stub) *(.lib.stub.entry.*) }
+ .lib.stub.btm : { *(.lib.stub.btm) }
+
+ /* Keep these sections around, even though they may appear unused to the linker */
+ .lib.ent.top : { KEEP(*(.lib.ent.top)) }
+ .lib.ent : { KEEP(*(.lib.ent)) }
+ .lib.ent.btm : { KEEP(*(.lib.ent.btm)) }
+
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+
+ /* Add symbols for LLVM's libunwind */
+ __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
+ __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
+ .eh_frame :
+ {
+ __eh_frame_start = .;
+ KEEP(*(.eh_frame))
+ __eh_frame_end = .;
+ }
+
+ /* These are explicitly listed to avoid being merged into .rodata */
+ .rodata.sceResident : { *(.rodata.sceResident) *(.rodata.sceResident.*) }
+ .rodata.sceModuleInfo : { *(.rodata.sceModuleInfo) }
+ /* Sort NIDs for convenient ordering */
+ .rodata.sceNid : { *(.rodata.sceNid) *(SORT(.rodata.sceNid.*)) }
+
+ .rodata : { *(.rodata .rodata.*) }
+ .data : { *(.data .data.*) }
+ .gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) }
+ .bss : { *(.bss .bss.*) }
+
+ /DISCARD/ : { *(.rel.sceStub.text .MIPS.abiflags .reginfo) }
+}
diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs
new file mode 100644
index 000000000..01346e71a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mipsel-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+
+ options: TargetOptions {
+ cpu: "mips32r2".into(),
+ features: "+mips32r2,+fpxx,+nooddspreg".into(),
+ max_atomic_width: Some(32),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs
new file mode 100644
index 000000000..0e8f1a2c8
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs
@@ -0,0 +1,16 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "mips32r2".into();
+ base.features = "+mips32r2,+soft-float".into();
+ base.max_atomic_width = Some(32);
+ base.crt_static_default = false;
+ Target {
+ llvm_target: "mipsel-unknown-linux-musl".into(),
+ pointer_width: 32,
+ data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+ options: TargetOptions { mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs
new file mode 100644
index 000000000..834207458
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mipsel-unknown-linux-uclibc".into(),
+ pointer_width: 32,
+ data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+
+ options: TargetOptions {
+ cpu: "mips32r2".into(),
+ features: "+mips32r2,+soft-float".into(),
+ max_atomic_width: Some(32),
+ mcount: "_mcount".into(),
+
+ ..super::linux_uclibc_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
new file mode 100644
index 000000000..fe2aa2de8
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
@@ -0,0 +1,27 @@
+//! Bare MIPS32r2, little endian, softfloat, O32 calling convention
+//!
+//! Can be used for MIPS M4K core (e.g. on PIC32MX devices)
+
+use crate::spec::{LinkerFlavor, LldFlavor, RelocModel};
+use crate::spec::{PanicStrategy, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mipsel-unknown-none".into(),
+ pointer_width: 32,
+ data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ cpu: "mips32r2".into(),
+ features: "+mips32r2,+soft-float,+noabicalls".into(),
+ max_atomic_width: Some(32),
+ linker: Some("rust-lld".into()),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ emit_debug_gdb_scripts: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs
new file mode 100644
index 000000000..1e066b271
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs
@@ -0,0 +1,20 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mipsisa32r6-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+ options: TargetOptions {
+ endian: Endian::Big,
+ cpu: "mips32r6".into(),
+ features: "+mips32r6".into(),
+ max_atomic_width: Some(32),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs
new file mode 100644
index 000000000..4785929c1
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mipsisa32r6el-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(),
+ arch: "mips".into(),
+
+ options: TargetOptions {
+ cpu: "mips32r6".into(),
+ features: "+mips32r6".into(),
+ max_atomic_width: Some(32),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs
new file mode 100644
index 000000000..766ac7680
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs
@@ -0,0 +1,22 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mipsisa64r6-unknown-linux-gnuabi64".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(),
+ arch: "mips64".into(),
+ options: TargetOptions {
+ abi: "abi64".into(),
+ endian: Endian::Big,
+ // NOTE(mips64r6) matches C toolchain
+ cpu: "mips64r6".into(),
+ features: "+mips64r6".into(),
+ max_atomic_width: Some(64),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs
new file mode 100644
index 000000000..d2b07c654
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs
@@ -0,0 +1,20 @@
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "mipsisa64r6el-unknown-linux-gnuabi64".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(),
+ arch: "mips64".into(),
+ options: TargetOptions {
+ abi: "abi64".into(),
+ // NOTE(mips64r6) matches C toolchain
+ cpu: "mips64r6".into(),
+ features: "+mips64r6".into(),
+ max_atomic_width: Some(64),
+ mcount: "_mcount".into(),
+
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
new file mode 100644
index 000000000..f7abeafd3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -0,0 +1,2585 @@
+//! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131)
+//!
+//! Rust targets a wide variety of usecases, and in the interest of flexibility,
+//! allows new target triples to be defined in configuration files. Most users
+//! will not need to care about these, but this is invaluable when porting Rust
+//! to a new platform, and allows for an unprecedented level of control over how
+//! the compiler works.
+//!
+//! # Using custom targets
+//!
+//! A target triple, as passed via `rustc --target=TRIPLE`, will first be
+//! compared against the list of built-in targets. This is to ease distributing
+//! rustc (no need for configuration files) and also to hold these built-in
+//! targets as immutable and sacred. If `TRIPLE` is not one of the built-in
+//! targets, rustc will check if a file named `TRIPLE` exists. If it does, it
+//! will be loaded as the target configuration. If the file does not exist,
+//! rustc will search each directory in the environment variable
+//! `RUST_TARGET_PATH` for a file named `TRIPLE.json`. The first one found will
+//! be loaded. If no file is found in any of those directories, a fatal error
+//! will be given.
+//!
+//! Projects defining their own targets should use
+//! `--target=path/to/my-awesome-platform.json` instead of adding to
+//! `RUST_TARGET_PATH`.
+//!
+//! # Defining a new target
+//!
+//! Targets are defined using [JSON](https://json.org/). The `Target` struct in
+//! this module defines the format the JSON file should take, though each
+//! underscore in the field names should be replaced with a hyphen (`-`) in the
+//! JSON file. Some fields are required in every target specification, such as
+//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`,
+//! `arch`, and `os`. In general, options passed to rustc with `-C` override
+//! the target's settings, though `target-feature` and `link-args` will *add*
+//! to the list specified by the target, rather than replace.
+
+use crate::abi::Endian;
+use crate::json::{Json, ToJson};
+use crate::spec::abi::{lookup as lookup_abi, Abi};
+use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use rustc_span::symbol::{sym, Symbol};
+use serde_json::Value;
+use std::borrow::Cow;
+use std::collections::BTreeMap;
+use std::convert::TryFrom;
+use std::hash::{Hash, Hasher};
+use std::iter::FromIterator;
+use std::ops::{Deref, DerefMut};
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+use std::{fmt, io};
+
+use rustc_macros::HashStable_Generic;
+
+pub mod abi;
+pub mod crt_objects;
+
+mod android_base;
+mod apple_base;
+mod apple_sdk_base;
+mod avr_gnu_base;
+mod bpf_base;
+mod dragonfly_base;
+mod freebsd_base;
+mod fuchsia_base;
+mod haiku_base;
+mod hermit_base;
+mod illumos_base;
+mod l4re_base;
+mod linux_base;
+mod linux_gnu_base;
+mod linux_kernel_base;
+mod linux_musl_base;
+mod linux_uclibc_base;
+mod msvc_base;
+mod netbsd_base;
+mod openbsd_base;
+mod redox_base;
+mod solaris_base;
+mod solid_base;
+mod thumb_base;
+mod uefi_msvc_base;
+mod vxworks_base;
+mod wasm_base;
+mod windows_gnu_base;
+mod windows_gnullvm_base;
+mod windows_msvc_base;
+mod windows_uwp_gnu_base;
+mod windows_uwp_msvc_base;
+
+#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum LinkerFlavor {
+ Em,
+ Gcc,
+ L4Bender,
+ Ld,
+ Msvc,
+ Lld(LldFlavor),
+ PtxLinker,
+ BpfLinker,
+}
+
+#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum LldFlavor {
+ Wasm,
+ Ld64,
+ Ld,
+ Link,
+}
+
+impl LldFlavor {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ LldFlavor::Wasm => "wasm",
+ LldFlavor::Ld64 => "darwin",
+ LldFlavor::Ld => "gnu",
+ LldFlavor::Link => "link",
+ }
+ }
+
+ fn from_str(s: &str) -> Option<Self> {
+ Some(match s {
+ "darwin" => LldFlavor::Ld64,
+ "gnu" => LldFlavor::Ld,
+ "link" => LldFlavor::Link,
+ "wasm" => LldFlavor::Wasm,
+ _ => return None,
+ })
+ }
+}
+
+impl ToJson for LldFlavor {
+ fn to_json(&self) -> Json {
+ self.as_str().to_json()
+ }
+}
+
+impl ToJson for LinkerFlavor {
+ fn to_json(&self) -> Json {
+ self.desc().to_json()
+ }
+}
+macro_rules! flavor_mappings {
+ ($((($($flavor:tt)*), $string:expr),)*) => (
+ impl LinkerFlavor {
+ pub const fn one_of() -> &'static str {
+ concat!("one of: ", $($string, " ",)*)
+ }
+
+ pub fn from_str(s: &str) -> Option<Self> {
+ Some(match s {
+ $($string => $($flavor)*,)*
+ _ => return None,
+ })
+ }
+
+ pub fn desc(&self) -> &str {
+ match *self {
+ $($($flavor)* => $string,)*
+ }
+ }
+ }
+ )
+}
+
+flavor_mappings! {
+ ((LinkerFlavor::Em), "em"),
+ ((LinkerFlavor::Gcc), "gcc"),
+ ((LinkerFlavor::L4Bender), "l4-bender"),
+ ((LinkerFlavor::Ld), "ld"),
+ ((LinkerFlavor::Msvc), "msvc"),
+ ((LinkerFlavor::PtxLinker), "ptx-linker"),
+ ((LinkerFlavor::BpfLinker), "bpf-linker"),
+ ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"),
+ ((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"),
+ ((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"),
+ ((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"),
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
+pub enum PanicStrategy {
+ Unwind,
+ Abort,
+}
+
+impl PanicStrategy {
+ pub fn desc(&self) -> &str {
+ match *self {
+ PanicStrategy::Unwind => "unwind",
+ PanicStrategy::Abort => "abort",
+ }
+ }
+
+ pub const fn desc_symbol(&self) -> Symbol {
+ match *self {
+ PanicStrategy::Unwind => sym::unwind,
+ PanicStrategy::Abort => sym::abort,
+ }
+ }
+
+ pub const fn all() -> [Symbol; 2] {
+ [Self::Abort.desc_symbol(), Self::Unwind.desc_symbol()]
+ }
+}
+
+impl ToJson for PanicStrategy {
+ fn to_json(&self) -> Json {
+ match *self {
+ PanicStrategy::Abort => "abort".to_json(),
+ PanicStrategy::Unwind => "unwind".to_json(),
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Hash)]
+pub enum RelroLevel {
+ Full,
+ Partial,
+ Off,
+ None,
+}
+
+impl RelroLevel {
+ pub fn desc(&self) -> &str {
+ match *self {
+ RelroLevel::Full => "full",
+ RelroLevel::Partial => "partial",
+ RelroLevel::Off => "off",
+ RelroLevel::None => "none",
+ }
+ }
+}
+
+impl FromStr for RelroLevel {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<RelroLevel, ()> {
+ match s {
+ "full" => Ok(RelroLevel::Full),
+ "partial" => Ok(RelroLevel::Partial),
+ "off" => Ok(RelroLevel::Off),
+ "none" => Ok(RelroLevel::None),
+ _ => Err(()),
+ }
+ }
+}
+
+impl ToJson for RelroLevel {
+ fn to_json(&self) -> Json {
+ match *self {
+ RelroLevel::Full => "full".to_json(),
+ RelroLevel::Partial => "partial".to_json(),
+ RelroLevel::Off => "off".to_json(),
+ RelroLevel::None => "None".to_json(),
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Hash)]
+pub enum MergeFunctions {
+ Disabled,
+ Trampolines,
+ Aliases,
+}
+
+impl MergeFunctions {
+ pub fn desc(&self) -> &str {
+ match *self {
+ MergeFunctions::Disabled => "disabled",
+ MergeFunctions::Trampolines => "trampolines",
+ MergeFunctions::Aliases => "aliases",
+ }
+ }
+}
+
+impl FromStr for MergeFunctions {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<MergeFunctions, ()> {
+ match s {
+ "disabled" => Ok(MergeFunctions::Disabled),
+ "trampolines" => Ok(MergeFunctions::Trampolines),
+ "aliases" => Ok(MergeFunctions::Aliases),
+ _ => Err(()),
+ }
+ }
+}
+
+impl ToJson for MergeFunctions {
+ fn to_json(&self) -> Json {
+ match *self {
+ MergeFunctions::Disabled => "disabled".to_json(),
+ MergeFunctions::Trampolines => "trampolines".to_json(),
+ MergeFunctions::Aliases => "aliases".to_json(),
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum RelocModel {
+ Static,
+ Pic,
+ Pie,
+ DynamicNoPic,
+ Ropi,
+ Rwpi,
+ RopiRwpi,
+}
+
+impl FromStr for RelocModel {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<RelocModel, ()> {
+ Ok(match s {
+ "static" => RelocModel::Static,
+ "pic" => RelocModel::Pic,
+ "pie" => RelocModel::Pie,
+ "dynamic-no-pic" => RelocModel::DynamicNoPic,
+ "ropi" => RelocModel::Ropi,
+ "rwpi" => RelocModel::Rwpi,
+ "ropi-rwpi" => RelocModel::RopiRwpi,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl ToJson for RelocModel {
+ fn to_json(&self) -> Json {
+ match *self {
+ RelocModel::Static => "static",
+ RelocModel::Pic => "pic",
+ RelocModel::Pie => "pie",
+ RelocModel::DynamicNoPic => "dynamic-no-pic",
+ RelocModel::Ropi => "ropi",
+ RelocModel::Rwpi => "rwpi",
+ RelocModel::RopiRwpi => "ropi-rwpi",
+ }
+ .to_json()
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum CodeModel {
+ Tiny,
+ Small,
+ Kernel,
+ Medium,
+ Large,
+}
+
+impl FromStr for CodeModel {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<CodeModel, ()> {
+ Ok(match s {
+ "tiny" => CodeModel::Tiny,
+ "small" => CodeModel::Small,
+ "kernel" => CodeModel::Kernel,
+ "medium" => CodeModel::Medium,
+ "large" => CodeModel::Large,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl ToJson for CodeModel {
+ fn to_json(&self) -> Json {
+ match *self {
+ CodeModel::Tiny => "tiny",
+ CodeModel::Small => "small",
+ CodeModel::Kernel => "kernel",
+ CodeModel::Medium => "medium",
+ CodeModel::Large => "large",
+ }
+ .to_json()
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum TlsModel {
+ GeneralDynamic,
+ LocalDynamic,
+ InitialExec,
+ LocalExec,
+}
+
+impl FromStr for TlsModel {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<TlsModel, ()> {
+ Ok(match s {
+ // Note the difference "general" vs "global" difference. The model name is "general",
+ // but the user-facing option name is "global" for consistency with other compilers.
+ "global-dynamic" => TlsModel::GeneralDynamic,
+ "local-dynamic" => TlsModel::LocalDynamic,
+ "initial-exec" => TlsModel::InitialExec,
+ "local-exec" => TlsModel::LocalExec,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl ToJson for TlsModel {
+ fn to_json(&self) -> Json {
+ match *self {
+ TlsModel::GeneralDynamic => "global-dynamic",
+ TlsModel::LocalDynamic => "local-dynamic",
+ TlsModel::InitialExec => "initial-exec",
+ TlsModel::LocalExec => "local-exec",
+ }
+ .to_json()
+ }
+}
+
+/// Everything is flattened to a single enum to make the json encoding/decoding less annoying.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub enum LinkOutputKind {
+ /// Dynamically linked non position-independent executable.
+ DynamicNoPicExe,
+ /// Dynamically linked position-independent executable.
+ DynamicPicExe,
+ /// Statically linked non position-independent executable.
+ StaticNoPicExe,
+ /// Statically linked position-independent executable.
+ StaticPicExe,
+ /// Regular dynamic library ("dynamically linked").
+ DynamicDylib,
+ /// Dynamic library with bundled libc ("statically linked").
+ StaticDylib,
+ /// WASI module with a lifetime past the _initialize entry point
+ WasiReactorExe,
+}
+
+impl LinkOutputKind {
+ fn as_str(&self) -> &'static str {
+ match self {
+ LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe",
+ LinkOutputKind::DynamicPicExe => "dynamic-pic-exe",
+ LinkOutputKind::StaticNoPicExe => "static-nopic-exe",
+ LinkOutputKind::StaticPicExe => "static-pic-exe",
+ LinkOutputKind::DynamicDylib => "dynamic-dylib",
+ LinkOutputKind::StaticDylib => "static-dylib",
+ LinkOutputKind::WasiReactorExe => "wasi-reactor-exe",
+ }
+ }
+
+ pub(super) fn from_str(s: &str) -> Option<LinkOutputKind> {
+ Some(match s {
+ "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe,
+ "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe,
+ "static-nopic-exe" => LinkOutputKind::StaticNoPicExe,
+ "static-pic-exe" => LinkOutputKind::StaticPicExe,
+ "dynamic-dylib" => LinkOutputKind::DynamicDylib,
+ "static-dylib" => LinkOutputKind::StaticDylib,
+ "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe,
+ _ => return None,
+ })
+ }
+}
+
+impl fmt::Display for LinkOutputKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<StaticCow<str>>>;
+
+#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
+pub enum SplitDebuginfo {
+ /// Split debug-information is disabled, meaning that on supported platforms
+ /// you can find all debug information in the executable itself. This is
+ /// only supported for ELF effectively.
+ ///
+ /// * Windows - not supported
+ /// * macOS - don't run `dsymutil`
+ /// * ELF - `.dwarf_*` sections
+ Off,
+
+ /// Split debug-information can be found in a "packed" location separate
+ /// from the final artifact. This is supported on all platforms.
+ ///
+ /// * Windows - `*.pdb`
+ /// * macOS - `*.dSYM` (run `dsymutil`)
+ /// * ELF - `*.dwp` (run `rust-llvm-dwp`)
+ Packed,
+
+ /// Split debug-information can be found in individual object files on the
+ /// filesystem. The main executable may point to the object files.
+ ///
+ /// * Windows - not supported
+ /// * macOS - supported, scattered object files
+ /// * ELF - supported, scattered `*.dwo` or `*.o` files (see `SplitDwarfKind`)
+ Unpacked,
+}
+
+impl SplitDebuginfo {
+ fn as_str(&self) -> &'static str {
+ match self {
+ SplitDebuginfo::Off => "off",
+ SplitDebuginfo::Packed => "packed",
+ SplitDebuginfo::Unpacked => "unpacked",
+ }
+ }
+}
+
+impl FromStr for SplitDebuginfo {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<SplitDebuginfo, ()> {
+ Ok(match s {
+ "off" => SplitDebuginfo::Off,
+ "unpacked" => SplitDebuginfo::Unpacked,
+ "packed" => SplitDebuginfo::Packed,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl ToJson for SplitDebuginfo {
+ fn to_json(&self) -> Json {
+ self.as_str().to_json()
+ }
+}
+
+impl fmt::Display for SplitDebuginfo {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum StackProbeType {
+ /// Don't emit any stack probes.
+ None,
+ /// It is harmless to use this option even on targets that do not have backend support for
+ /// stack probes as the failure mode is the same as if no stack-probe option was specified in
+ /// the first place.
+ Inline,
+ /// Call `__rust_probestack` whenever stack needs to be probed.
+ Call,
+ /// Use inline option for LLVM versions later than specified in `min_llvm_version_for_inline`
+ /// and call `__rust_probestack` otherwise.
+ InlineOrCall { min_llvm_version_for_inline: (u32, u32, u32) },
+}
+
+impl StackProbeType {
+ fn from_json(json: &Json) -> Result<Self, String> {
+ let object = json.as_object().ok_or_else(|| "expected a JSON object")?;
+ let kind = object
+ .get("kind")
+ .and_then(|o| o.as_str())
+ .ok_or_else(|| "expected `kind` to be a string")?;
+ match kind {
+ "none" => Ok(StackProbeType::None),
+ "inline" => Ok(StackProbeType::Inline),
+ "call" => Ok(StackProbeType::Call),
+ "inline-or-call" => {
+ let min_version = object
+ .get("min-llvm-version-for-inline")
+ .and_then(|o| o.as_array())
+ .ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?;
+ let mut iter = min_version.into_iter().map(|v| {
+ let int = v.as_u64().ok_or_else(
+ || "expected `min-llvm-version-for-inline` values to be integers",
+ )?;
+ u32::try_from(int)
+ .map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32")
+ });
+ let min_llvm_version_for_inline = (
+ iter.next().unwrap_or(Ok(11))?,
+ iter.next().unwrap_or(Ok(0))?,
+ iter.next().unwrap_or(Ok(0))?,
+ );
+ Ok(StackProbeType::InlineOrCall { min_llvm_version_for_inline })
+ }
+ _ => Err(String::from(
+ "`kind` expected to be one of `none`, `inline`, `call` or `inline-or-call`",
+ )),
+ }
+ }
+}
+
+impl ToJson for StackProbeType {
+ fn to_json(&self) -> Json {
+ Json::Object(match self {
+ StackProbeType::None => {
+ [(String::from("kind"), "none".to_json())].into_iter().collect()
+ }
+ StackProbeType::Inline => {
+ [(String::from("kind"), "inline".to_json())].into_iter().collect()
+ }
+ StackProbeType::Call => {
+ [(String::from("kind"), "call".to_json())].into_iter().collect()
+ }
+ StackProbeType::InlineOrCall { min_llvm_version_for_inline: (maj, min, patch) } => [
+ (String::from("kind"), "inline-or-call".to_json()),
+ (
+ String::from("min-llvm-version-for-inline"),
+ Json::Array(vec![maj.to_json(), min.to_json(), patch.to_json()]),
+ ),
+ ]
+ .into_iter()
+ .collect(),
+ })
+ }
+}
+
+bitflags::bitflags! {
+ #[derive(Default, Encodable, Decodable)]
+ pub struct SanitizerSet: u8 {
+ const ADDRESS = 1 << 0;
+ const LEAK = 1 << 1;
+ const MEMORY = 1 << 2;
+ const THREAD = 1 << 3;
+ const HWADDRESS = 1 << 4;
+ const CFI = 1 << 5;
+ const MEMTAG = 1 << 6;
+ const SHADOWCALLSTACK = 1 << 7;
+ }
+}
+
+impl SanitizerSet {
+ /// Return sanitizer's name
+ ///
+ /// Returns none if the flags is a set of sanitizers numbering not exactly one.
+ pub fn as_str(self) -> Option<&'static str> {
+ Some(match self {
+ SanitizerSet::ADDRESS => "address",
+ SanitizerSet::CFI => "cfi",
+ SanitizerSet::LEAK => "leak",
+ SanitizerSet::MEMORY => "memory",
+ SanitizerSet::MEMTAG => "memtag",
+ SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
+ SanitizerSet::THREAD => "thread",
+ SanitizerSet::HWADDRESS => "hwaddress",
+ _ => return None,
+ })
+ }
+}
+
+/// Formats a sanitizer set as a comma separated list of sanitizers' names.
+impl fmt::Display for SanitizerSet {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut first = true;
+ for s in *self {
+ let name = s.as_str().unwrap_or_else(|| panic!("unrecognized sanitizer {:?}", s));
+ if !first {
+ f.write_str(", ")?;
+ }
+ f.write_str(name)?;
+ first = false;
+ }
+ Ok(())
+ }
+}
+
+impl IntoIterator for SanitizerSet {
+ type Item = SanitizerSet;
+ type IntoIter = std::vec::IntoIter<SanitizerSet>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ [
+ SanitizerSet::ADDRESS,
+ SanitizerSet::CFI,
+ SanitizerSet::LEAK,
+ SanitizerSet::MEMORY,
+ SanitizerSet::MEMTAG,
+ SanitizerSet::SHADOWCALLSTACK,
+ SanitizerSet::THREAD,
+ SanitizerSet::HWADDRESS,
+ ]
+ .iter()
+ .copied()
+ .filter(|&s| self.contains(s))
+ .collect::<Vec<_>>()
+ .into_iter()
+ }
+}
+
+impl<CTX> HashStable<CTX> for SanitizerSet {
+ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
+ self.bits().hash_stable(ctx, hasher);
+ }
+}
+
+impl ToJson for SanitizerSet {
+ fn to_json(&self) -> Json {
+ self.into_iter()
+ .map(|v| Some(v.as_str()?.to_json()))
+ .collect::<Option<Vec<_>>>()
+ .unwrap_or_default()
+ .to_json()
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum FramePointer {
+ /// Forces the machine code generator to always preserve the frame pointers.
+ Always,
+ /// Forces the machine code generator to preserve the frame pointers except for the leaf
+ /// functions (i.e. those that don't call other functions).
+ NonLeaf,
+ /// Allows the machine code generator to omit the frame pointers.
+ ///
+ /// This option does not guarantee that the frame pointers will be omitted.
+ MayOmit,
+}
+
+impl FromStr for FramePointer {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Self, ()> {
+ Ok(match s {
+ "always" => Self::Always,
+ "non-leaf" => Self::NonLeaf,
+ "may-omit" => Self::MayOmit,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl ToJson for FramePointer {
+ fn to_json(&self) -> Json {
+ match *self {
+ Self::Always => "always",
+ Self::NonLeaf => "non-leaf",
+ Self::MayOmit => "may-omit",
+ }
+ .to_json()
+ }
+}
+
+/// Controls use of stack canaries.
+#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
+pub enum StackProtector {
+ /// Disable stack canary generation.
+ None,
+
+ /// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see
+ /// llvm/docs/LangRef.rst). This triggers stack canary generation in
+ /// functions which contain an array of a byte-sized type with more than
+ /// eight elements.
+ Basic,
+
+ /// On LLVM, mark all generated LLVM functions with the `sspstrong`
+ /// attribute (see llvm/docs/LangRef.rst). This triggers stack canary
+ /// generation in functions which either contain an array, or which take
+ /// the address of a local variable.
+ Strong,
+
+ /// Generate stack canaries in all functions.
+ All,
+}
+
+impl StackProtector {
+ fn as_str(&self) -> &'static str {
+ match self {
+ StackProtector::None => "none",
+ StackProtector::Basic => "basic",
+ StackProtector::Strong => "strong",
+ StackProtector::All => "all",
+ }
+ }
+}
+
+impl FromStr for StackProtector {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<StackProtector, ()> {
+ Ok(match s {
+ "none" => StackProtector::None,
+ "basic" => StackProtector::Basic,
+ "strong" => StackProtector::Strong,
+ "all" => StackProtector::All,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl fmt::Display for StackProtector {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+macro_rules! supported_targets {
+ ( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
+ $(mod $module;)+
+
+ /// List of supported targets
+ pub const TARGETS: &[&str] = &[$($($triple),+),+];
+
+ fn load_builtin(target: &str) -> Option<Target> {
+ let mut t = match target {
+ $( $($triple)|+ => $module::target(), )+
+ _ => return None,
+ };
+ t.is_builtin = true;
+ debug!("got builtin target: {:?}", t);
+ Some(t)
+ }
+
+ #[cfg(test)]
+ mod tests {
+ mod tests_impl;
+
+ // Cannot put this into a separate file without duplication, make an exception.
+ $(
+ #[test] // `#[test]`
+ fn $module() {
+ tests_impl::test_target(super::$module::target());
+ }
+ )+
+ }
+ };
+}
+
+supported_targets! {
+ ("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu),
+ ("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32),
+ ("i686-unknown-linux-gnu", i686_unknown_linux_gnu),
+ ("i586-unknown-linux-gnu", i586_unknown_linux_gnu),
+ ("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu),
+ ("mips-unknown-linux-gnu", mips_unknown_linux_gnu),
+ ("mips64-unknown-linux-gnuabi64", mips64_unknown_linux_gnuabi64),
+ ("mips64el-unknown-linux-gnuabi64", mips64el_unknown_linux_gnuabi64),
+ ("mipsisa32r6-unknown-linux-gnu", mipsisa32r6_unknown_linux_gnu),
+ ("mipsisa32r6el-unknown-linux-gnu", mipsisa32r6el_unknown_linux_gnu),
+ ("mipsisa64r6-unknown-linux-gnuabi64", mipsisa64r6_unknown_linux_gnuabi64),
+ ("mipsisa64r6el-unknown-linux-gnuabi64", mipsisa64r6el_unknown_linux_gnuabi64),
+ ("mipsel-unknown-linux-gnu", mipsel_unknown_linux_gnu),
+ ("powerpc-unknown-linux-gnu", powerpc_unknown_linux_gnu),
+ ("powerpc-unknown-linux-gnuspe", powerpc_unknown_linux_gnuspe),
+ ("powerpc-unknown-linux-musl", powerpc_unknown_linux_musl),
+ ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu),
+ ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl),
+ ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu),
+ ("powerpc64le-unknown-linux-musl", powerpc64le_unknown_linux_musl),
+ ("s390x-unknown-linux-gnu", s390x_unknown_linux_gnu),
+ ("s390x-unknown-linux-musl", s390x_unknown_linux_musl),
+ ("sparc-unknown-linux-gnu", sparc_unknown_linux_gnu),
+ ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu),
+ ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi),
+ ("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf),
+ ("arm-unknown-linux-musleabi", arm_unknown_linux_musleabi),
+ ("arm-unknown-linux-musleabihf", arm_unknown_linux_musleabihf),
+ ("armv4t-unknown-linux-gnueabi", armv4t_unknown_linux_gnueabi),
+ ("armv5te-unknown-linux-gnueabi", armv5te_unknown_linux_gnueabi),
+ ("armv5te-unknown-linux-musleabi", armv5te_unknown_linux_musleabi),
+ ("armv5te-unknown-linux-uclibceabi", armv5te_unknown_linux_uclibceabi),
+ ("armv7-unknown-linux-gnueabi", armv7_unknown_linux_gnueabi),
+ ("armv7-unknown-linux-gnueabihf", armv7_unknown_linux_gnueabihf),
+ ("thumbv7neon-unknown-linux-gnueabihf", thumbv7neon_unknown_linux_gnueabihf),
+ ("thumbv7neon-unknown-linux-musleabihf", thumbv7neon_unknown_linux_musleabihf),
+ ("armv7-unknown-linux-musleabi", armv7_unknown_linux_musleabi),
+ ("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf),
+ ("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu),
+ ("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl),
+ ("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl),
+ ("i686-unknown-linux-musl", i686_unknown_linux_musl),
+ ("i586-unknown-linux-musl", i586_unknown_linux_musl),
+ ("mips-unknown-linux-musl", mips_unknown_linux_musl),
+ ("mipsel-unknown-linux-musl", mipsel_unknown_linux_musl),
+ ("mips64-unknown-linux-muslabi64", mips64_unknown_linux_muslabi64),
+ ("mips64el-unknown-linux-muslabi64", mips64el_unknown_linux_muslabi64),
+ ("hexagon-unknown-linux-musl", hexagon_unknown_linux_musl),
+
+ ("mips-unknown-linux-uclibc", mips_unknown_linux_uclibc),
+ ("mipsel-unknown-linux-uclibc", mipsel_unknown_linux_uclibc),
+
+ ("i686-linux-android", i686_linux_android),
+ ("x86_64-linux-android", x86_64_linux_android),
+ ("arm-linux-androideabi", arm_linux_androideabi),
+ ("armv7-linux-androideabi", armv7_linux_androideabi),
+ ("thumbv7neon-linux-androideabi", thumbv7neon_linux_androideabi),
+ ("aarch64-linux-android", aarch64_linux_android),
+
+ ("x86_64-unknown-none-linuxkernel", x86_64_unknown_none_linuxkernel),
+
+ ("aarch64-unknown-freebsd", aarch64_unknown_freebsd),
+ ("armv6-unknown-freebsd", armv6_unknown_freebsd),
+ ("armv7-unknown-freebsd", armv7_unknown_freebsd),
+ ("i686-unknown-freebsd", i686_unknown_freebsd),
+ ("powerpc-unknown-freebsd", powerpc_unknown_freebsd),
+ ("powerpc64-unknown-freebsd", powerpc64_unknown_freebsd),
+ ("powerpc64le-unknown-freebsd", powerpc64le_unknown_freebsd),
+ ("riscv64gc-unknown-freebsd", riscv64gc_unknown_freebsd),
+ ("x86_64-unknown-freebsd", x86_64_unknown_freebsd),
+
+ ("x86_64-unknown-dragonfly", x86_64_unknown_dragonfly),
+
+ ("aarch64-unknown-openbsd", aarch64_unknown_openbsd),
+ ("i686-unknown-openbsd", i686_unknown_openbsd),
+ ("sparc64-unknown-openbsd", sparc64_unknown_openbsd),
+ ("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
+ ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
+
+ ("aarch64-unknown-netbsd", aarch64_unknown_netbsd),
+ ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf),
+ ("armv7-unknown-netbsd-eabihf", armv7_unknown_netbsd_eabihf),
+ ("i686-unknown-netbsd", i686_unknown_netbsd),
+ ("powerpc-unknown-netbsd", powerpc_unknown_netbsd),
+ ("sparc64-unknown-netbsd", sparc64_unknown_netbsd),
+ ("x86_64-unknown-netbsd", x86_64_unknown_netbsd),
+
+ ("i686-unknown-haiku", i686_unknown_haiku),
+ ("x86_64-unknown-haiku", x86_64_unknown_haiku),
+
+ ("aarch64-apple-darwin", aarch64_apple_darwin),
+ ("x86_64-apple-darwin", x86_64_apple_darwin),
+ ("i686-apple-darwin", i686_apple_darwin),
+
+ ("aarch64-fuchsia", aarch64_fuchsia),
+ ("x86_64-fuchsia", x86_64_fuchsia),
+
+ ("avr-unknown-gnu-atmega328", avr_unknown_gnu_atmega328),
+
+ ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc),
+
+ ("aarch64-unknown-redox", aarch64_unknown_redox),
+ ("x86_64-unknown-redox", x86_64_unknown_redox),
+
+ ("i386-apple-ios", i386_apple_ios),
+ ("x86_64-apple-ios", x86_64_apple_ios),
+ ("aarch64-apple-ios", aarch64_apple_ios),
+ ("armv7-apple-ios", armv7_apple_ios),
+ ("armv7s-apple-ios", armv7s_apple_ios),
+ ("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi),
+ ("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi),
+ ("aarch64-apple-ios-sim", aarch64_apple_ios_sim),
+ ("aarch64-apple-tvos", aarch64_apple_tvos),
+ ("x86_64-apple-tvos", x86_64_apple_tvos),
+
+ ("armv7k-apple-watchos", armv7k_apple_watchos),
+ ("arm64_32-apple-watchos", arm64_32_apple_watchos),
+ ("x86_64-apple-watchos-sim", x86_64_apple_watchos_sim),
+ ("aarch64-apple-watchos-sim", aarch64_apple_watchos_sim),
+
+ ("armebv7r-none-eabi", armebv7r_none_eabi),
+ ("armebv7r-none-eabihf", armebv7r_none_eabihf),
+ ("armv7r-none-eabi", armv7r_none_eabi),
+ ("armv7r-none-eabihf", armv7r_none_eabihf),
+
+ ("x86_64-pc-solaris", x86_64_pc_solaris),
+ ("x86_64-sun-solaris", x86_64_sun_solaris),
+ ("sparcv9-sun-solaris", sparcv9_sun_solaris),
+
+ ("x86_64-unknown-illumos", x86_64_unknown_illumos),
+
+ ("x86_64-pc-windows-gnu", x86_64_pc_windows_gnu),
+ ("i686-pc-windows-gnu", i686_pc_windows_gnu),
+ ("i686-uwp-windows-gnu", i686_uwp_windows_gnu),
+ ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu),
+
+ ("aarch64-pc-windows-gnullvm", aarch64_pc_windows_gnullvm),
+ ("x86_64-pc-windows-gnullvm", x86_64_pc_windows_gnullvm),
+
+ ("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc),
+ ("aarch64-uwp-windows-msvc", aarch64_uwp_windows_msvc),
+ ("x86_64-pc-windows-msvc", x86_64_pc_windows_msvc),
+ ("x86_64-uwp-windows-msvc", x86_64_uwp_windows_msvc),
+ ("i686-pc-windows-msvc", i686_pc_windows_msvc),
+ ("i686-uwp-windows-msvc", i686_uwp_windows_msvc),
+ ("i586-pc-windows-msvc", i586_pc_windows_msvc),
+ ("thumbv7a-pc-windows-msvc", thumbv7a_pc_windows_msvc),
+ ("thumbv7a-uwp-windows-msvc", thumbv7a_uwp_windows_msvc),
+
+ ("asmjs-unknown-emscripten", asmjs_unknown_emscripten),
+ ("wasm32-unknown-emscripten", wasm32_unknown_emscripten),
+ ("wasm32-unknown-unknown", wasm32_unknown_unknown),
+ ("wasm32-wasi", wasm32_wasi),
+ ("wasm64-unknown-unknown", wasm64_unknown_unknown),
+
+ ("thumbv6m-none-eabi", thumbv6m_none_eabi),
+ ("thumbv7m-none-eabi", thumbv7m_none_eabi),
+ ("thumbv7em-none-eabi", thumbv7em_none_eabi),
+ ("thumbv7em-none-eabihf", thumbv7em_none_eabihf),
+ ("thumbv8m.base-none-eabi", thumbv8m_base_none_eabi),
+ ("thumbv8m.main-none-eabi", thumbv8m_main_none_eabi),
+ ("thumbv8m.main-none-eabihf", thumbv8m_main_none_eabihf),
+
+ ("armv7a-none-eabi", armv7a_none_eabi),
+ ("armv7a-none-eabihf", armv7a_none_eabihf),
+
+ ("msp430-none-elf", msp430_none_elf),
+
+ ("aarch64-unknown-hermit", aarch64_unknown_hermit),
+ ("x86_64-unknown-hermit", x86_64_unknown_hermit),
+
+ ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf),
+ ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf),
+ ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf),
+ ("riscv32imc-esp-espidf", riscv32imc_esp_espidf),
+ ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf),
+ ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf),
+ ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu),
+ ("riscv32gc-unknown-linux-musl", riscv32gc_unknown_linux_musl),
+ ("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf),
+ ("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf),
+ ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu),
+ ("riscv64gc-unknown-linux-musl", riscv64gc_unknown_linux_musl),
+
+ ("aarch64-unknown-none", aarch64_unknown_none),
+ ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat),
+
+ ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx),
+
+ ("x86_64-unknown-uefi", x86_64_unknown_uefi),
+ ("i686-unknown-uefi", i686_unknown_uefi),
+ ("aarch64-unknown-uefi", aarch64_unknown_uefi),
+
+ ("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda),
+
+ ("i686-wrs-vxworks", i686_wrs_vxworks),
+ ("x86_64-wrs-vxworks", x86_64_wrs_vxworks),
+ ("armv7-wrs-vxworks-eabihf", armv7_wrs_vxworks_eabihf),
+ ("aarch64-wrs-vxworks", aarch64_wrs_vxworks),
+ ("powerpc-wrs-vxworks", powerpc_wrs_vxworks),
+ ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe),
+ ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks),
+
+ ("aarch64-kmc-solid_asp3", aarch64_kmc_solid_asp3),
+ ("armv7a-kmc-solid_asp3-eabi", armv7a_kmc_solid_asp3_eabi),
+ ("armv7a-kmc-solid_asp3-eabihf", armv7a_kmc_solid_asp3_eabihf),
+
+ ("mipsel-sony-psp", mipsel_sony_psp),
+ ("mipsel-unknown-none", mipsel_unknown_none),
+ ("thumbv4t-none-eabi", thumbv4t_none_eabi),
+
+ ("aarch64_be-unknown-linux-gnu", aarch64_be_unknown_linux_gnu),
+ ("aarch64-unknown-linux-gnu_ilp32", aarch64_unknown_linux_gnu_ilp32),
+ ("aarch64_be-unknown-linux-gnu_ilp32", aarch64_be_unknown_linux_gnu_ilp32),
+
+ ("bpfeb-unknown-none", bpfeb_unknown_none),
+ ("bpfel-unknown-none", bpfel_unknown_none),
+
+ ("armv6k-nintendo-3ds", armv6k_nintendo_3ds),
+
+ ("aarch64-nintendo-switch-freestanding", aarch64_nintendo_switch_freestanding),
+
+ ("armv7-unknown-linux-uclibceabi", armv7_unknown_linux_uclibceabi),
+ ("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf),
+
+ ("x86_64-unknown-none", x86_64_unknown_none),
+
+ ("mips64-openwrt-linux-musl", mips64_openwrt_linux_musl),
+}
+
+/// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]>
+macro_rules! cvs {
+ () => {
+ ::std::borrow::Cow::Borrowed(&[])
+ };
+ ($($x:expr),+ $(,)?) => {
+ ::std::borrow::Cow::Borrowed(&[
+ $(
+ ::std::borrow::Cow::Borrowed($x),
+ )*
+ ])
+ };
+}
+
+pub(crate) use cvs;
+
+/// Warnings encountered when parsing the target `json`.
+///
+/// Includes fields that weren't recognized and fields that don't have the expected type.
+#[derive(Debug, PartialEq)]
+pub struct TargetWarnings {
+ unused_fields: Vec<String>,
+ incorrect_type: Vec<String>,
+}
+
+impl TargetWarnings {
+ pub fn empty() -> Self {
+ Self { unused_fields: Vec::new(), incorrect_type: Vec::new() }
+ }
+
+ pub fn warning_messages(&self) -> Vec<String> {
+ let mut warnings = vec![];
+ if !self.unused_fields.is_empty() {
+ warnings.push(format!(
+ "target json file contains unused fields: {}",
+ self.unused_fields.join(", ")
+ ));
+ }
+ if !self.incorrect_type.is_empty() {
+ warnings.push(format!(
+ "target json file contains fields whose value doesn't have the correct json type: {}",
+ self.incorrect_type.join(", ")
+ ));
+ }
+ warnings
+ }
+}
+
+/// Everything `rustc` knows about how to compile for a specific target.
+///
+/// Every field here must be specified, and has no default value.
+#[derive(PartialEq, Clone, Debug)]
+pub struct Target {
+ /// Target triple to pass to LLVM.
+ pub llvm_target: StaticCow<str>,
+ /// Number of bits in a pointer. Influences the `target_pointer_width` `cfg` variable.
+ pub pointer_width: u32,
+ /// Architecture to use for ABI considerations. Valid options include: "x86",
+ /// "x86_64", "arm", "aarch64", "mips", "powerpc", "powerpc64", and others.
+ pub arch: StaticCow<str>,
+ /// [Data layout](https://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM.
+ pub data_layout: StaticCow<str>,
+ /// Optional settings with defaults.
+ pub options: TargetOptions,
+}
+
+pub trait HasTargetSpec {
+ fn target_spec(&self) -> &Target;
+}
+
+impl HasTargetSpec for Target {
+ #[inline]
+ fn target_spec(&self) -> &Target {
+ self
+ }
+}
+
+type StaticCow<T> = Cow<'static, T>;
+
+/// Optional aspects of a target specification.
+///
+/// This has an implementation of `Default`, see each field for what the default is. In general,
+/// these try to take "minimal defaults" that don't assume anything about the runtime they run in.
+///
+/// `TargetOptions` as a separate structure is mostly an implementation detail of `Target`
+/// construction, all its fields logically belong to `Target` and available from `Target`
+/// through `Deref` impls.
+#[derive(PartialEq, Clone, Debug)]
+pub struct TargetOptions {
+ /// Whether the target is built-in or loaded from a custom target specification.
+ pub is_builtin: bool,
+
+ /// Used as the `target_endian` `cfg` variable. Defaults to little endian.
+ pub endian: Endian,
+ /// Width of c_int type. Defaults to "32".
+ pub c_int_width: StaticCow<str>,
+ /// OS name to use for conditional compilation (`target_os`). Defaults to "none".
+ /// "none" implies a bare metal target without `std` library.
+ /// A couple of targets having `std` also use "unknown" as an `os` value,
+ /// but they are exceptions.
+ pub os: StaticCow<str>,
+ /// Environment name to use for conditional compilation (`target_env`). Defaults to "".
+ pub env: StaticCow<str>,
+ /// ABI name to distinguish multiple ABIs on the same OS and architecture. For instance, `"eabi"`
+ /// or `"eabihf"`. Defaults to "".
+ pub abi: StaticCow<str>,
+ /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown".
+ pub vendor: StaticCow<str>,
+ /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed
+ /// on the command line. Defaults to `LinkerFlavor::Gcc`.
+ pub linker_flavor: LinkerFlavor,
+
+ /// Linker to invoke
+ pub linker: Option<StaticCow<str>>,
+
+ /// LLD flavor used if `lld` (or `rust-lld`) is specified as a linker
+ /// without clarifying its flavor in any way.
+ pub lld_flavor: LldFlavor,
+
+ /// Linker arguments that are passed *before* any user-defined libraries.
+ pub pre_link_args: LinkArgs,
+ /// Objects to link before and after all other object code.
+ pub pre_link_objects: CrtObjects,
+ pub post_link_objects: CrtObjects,
+ /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the
+ /// target's native gcc and fall back to the "self-contained" mode and pull them manually.
+ /// See `crt_objects.rs` for some more detailed documentation.
+ pub pre_link_objects_fallback: CrtObjects,
+ pub post_link_objects_fallback: CrtObjects,
+ /// Which logic to use to determine whether to fall back to the "self-contained" mode or not.
+ pub crt_objects_fallback: Option<CrtObjectsFallback>,
+
+ /// Linker arguments that are unconditionally passed after any
+ /// user-defined but before post-link objects. Standard platform
+ /// libraries that should be always be linked to, usually go here.
+ pub late_link_args: LinkArgs,
+ /// Linker arguments used in addition to `late_link_args` if at least one
+ /// Rust dependency is dynamically linked.
+ pub late_link_args_dynamic: LinkArgs,
+ /// Linker arguments used in addition to `late_link_args` if all Rust
+ /// dependencies are statically linked.
+ pub late_link_args_static: LinkArgs,
+ /// Linker arguments that are unconditionally passed *after* any
+ /// user-defined libraries.
+ pub post_link_args: LinkArgs,
+ /// Optional link script applied to `dylib` and `executable` crate types.
+ /// This is a string containing the script, not a path. Can only be applied
+ /// to linkers where `linker_is_gnu` is true.
+ pub link_script: Option<StaticCow<str>>,
+
+ /// Environment variables to be set for the linker invocation.
+ pub link_env: StaticCow<[(StaticCow<str>, StaticCow<str>)]>,
+ /// Environment variables to be removed for the linker invocation.
+ pub link_env_remove: StaticCow<[StaticCow<str>]>,
+
+ /// Extra arguments to pass to the external assembler (when used)
+ pub asm_args: StaticCow<[StaticCow<str>]>,
+
+ /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults
+ /// to "generic".
+ pub cpu: StaticCow<str>,
+ /// Default target features to pass to LLVM. These features will *always* be
+ /// passed, and cannot be disabled even via `-C`. Corresponds to `llc
+ /// -mattr=$features`.
+ pub features: StaticCow<str>,
+ /// Whether dynamic linking is available on this target. Defaults to false.
+ pub dynamic_linking: bool,
+ /// If dynamic linking is available, whether only cdylibs are supported.
+ pub only_cdylib: bool,
+ /// Whether executables are available on this target. Defaults to true.
+ pub executables: bool,
+ /// Relocation model to use in object file. Corresponds to `llc
+ /// -relocation-model=$relocation_model`. Defaults to `Pic`.
+ pub relocation_model: RelocModel,
+ /// Code model to use. Corresponds to `llc -code-model=$code_model`.
+ /// Defaults to `None` which means "inherited from the base LLVM target".
+ pub code_model: Option<CodeModel>,
+ /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec"
+ /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang.
+ pub tls_model: TlsModel,
+ /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false.
+ pub disable_redzone: bool,
+ /// Frame pointer mode for this target. Defaults to `MayOmit`.
+ pub frame_pointer: FramePointer,
+ /// Emit each function in its own section. Defaults to true.
+ pub function_sections: bool,
+ /// String to prepend to the name of every dynamic library. Defaults to "lib".
+ pub dll_prefix: StaticCow<str>,
+ /// String to append to the name of every dynamic library. Defaults to ".so".
+ pub dll_suffix: StaticCow<str>,
+ /// String to append to the name of every executable.
+ pub exe_suffix: StaticCow<str>,
+ /// String to prepend to the name of every static library. Defaults to "lib".
+ pub staticlib_prefix: StaticCow<str>,
+ /// String to append to the name of every static library. Defaults to ".a".
+ pub staticlib_suffix: StaticCow<str>,
+ /// Values of the `target_family` cfg set for this target.
+ ///
+ /// Common options are: "unix", "windows". Defaults to no families.
+ ///
+ /// See <https://doc.rust-lang.org/reference/conditional-compilation.html#target_family>.
+ pub families: StaticCow<[StaticCow<str>]>,
+ /// Whether the target toolchain's ABI supports returning small structs as an integer.
+ pub abi_return_struct_as_int: bool,
+ /// Whether the target toolchain is like macOS's. Only useful for compiling against iOS/macOS,
+ /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false.
+ pub is_like_osx: bool,
+ /// Whether the target toolchain is like Solaris's.
+ /// Only useful for compiling against Illumos/Solaris,
+ /// as they have a different set of linker flags. Defaults to false.
+ pub is_like_solaris: bool,
+ /// Whether the target is like Windows.
+ /// This is a combination of several more specific properties represented as a single flag:
+ /// - The target uses a Windows ABI,
+ /// - uses PE/COFF as a format for object code,
+ /// - uses Windows-style dllexport/dllimport for shared libraries,
+ /// - uses import libraries and .def files for symbol exports,
+ /// - executables support setting a subsystem.
+ pub is_like_windows: bool,
+ /// Whether the target is like MSVC.
+ /// This is a combination of several more specific properties represented as a single flag:
+ /// - The target has all the properties from `is_like_windows`
+ /// (for in-tree targets "is_like_msvc ⇒ is_like_windows" is ensured by a unit test),
+ /// - has some MSVC-specific Windows ABI properties,
+ /// - uses a link.exe-like linker,
+ /// - uses CodeView/PDB for debuginfo and natvis for its visualization,
+ /// - uses SEH-based unwinding,
+ /// - supports control flow guard mechanism.
+ pub is_like_msvc: bool,
+ /// Whether a target toolchain is like WASM.
+ pub is_like_wasm: bool,
+ /// Default supported version of DWARF on this platform.
+ /// Useful because some platforms (osx, bsd) only want up to DWARF2.
+ pub default_dwarf_version: u32,
+ /// Whether the linker support GNU-like arguments such as -O. Defaults to true.
+ pub linker_is_gnu: bool,
+ /// The MinGW toolchain has a known issue that prevents it from correctly
+ /// handling COFF object files with more than 2<sup>15</sup> sections. Since each weak
+ /// symbol needs its own COMDAT section, weak linkage implies a large
+ /// number sections that easily exceeds the given limit for larger
+ /// codebases. Consequently we want a way to disallow weak linkage on some
+ /// platforms.
+ pub allows_weak_linkage: bool,
+ /// Whether the linker support rpaths or not. Defaults to false.
+ pub has_rpath: bool,
+ /// Whether to disable linking to the default libraries, typically corresponds
+ /// to `-nodefaultlibs`. Defaults to true.
+ pub no_default_libraries: bool,
+ /// Dynamically linked executables can be compiled as position independent
+ /// if the default relocation model of position independent code is not
+ /// changed. This is a requirement to take advantage of ASLR, as otherwise
+ /// the functions in the executable are not randomized and can be used
+ /// during an exploit of a vulnerability in any code.
+ pub position_independent_executables: bool,
+ /// Executables that are both statically linked and position-independent are supported.
+ pub static_position_independent_executables: bool,
+ /// Determines if the target always requires using the PLT for indirect
+ /// library calls or not. This controls the default value of the `-Z plt` flag.
+ pub needs_plt: bool,
+ /// Either partial, full, or off. Full RELRO makes the dynamic linker
+ /// resolve all symbols at startup and marks the GOT read-only before
+ /// starting the program, preventing overwriting the GOT.
+ pub relro_level: RelroLevel,
+ /// Format that archives should be emitted in. This affects whether we use
+ /// LLVM to assemble an archive or fall back to the system linker, and
+ /// currently only "gnu" is used to fall into LLVM. Unknown strings cause
+ /// the system linker to be used.
+ pub archive_format: StaticCow<str>,
+ /// Is asm!() allowed? Defaults to true.
+ pub allow_asm: bool,
+ /// Whether the runtime startup code requires the `main` function be passed
+ /// `argc` and `argv` values.
+ pub main_needs_argc_argv: bool,
+
+ /// Flag indicating whether #[thread_local] is available for this target.
+ pub has_thread_local: bool,
+ // This is mainly for easy compatibility with emscripten.
+ // If we give emcc .o files that are actually .bc files it
+ // will 'just work'.
+ pub obj_is_bitcode: bool,
+ /// Whether the target requires that emitted object code includes bitcode.
+ pub forces_embed_bitcode: bool,
+ /// Content of the LLVM cmdline section associated with embedded bitcode.
+ pub bitcode_llvm_cmdline: StaticCow<str>,
+
+ /// Don't use this field; instead use the `.min_atomic_width()` method.
+ pub min_atomic_width: Option<u64>,
+
+ /// Don't use this field; instead use the `.max_atomic_width()` method.
+ pub max_atomic_width: Option<u64>,
+
+ /// Whether the target supports atomic CAS operations natively
+ pub atomic_cas: bool,
+
+ /// Panic strategy: "unwind" or "abort"
+ pub panic_strategy: PanicStrategy,
+
+ /// Whether or not linking dylibs to a static CRT is allowed.
+ pub crt_static_allows_dylibs: bool,
+ /// Whether or not the CRT is statically linked by default.
+ pub crt_static_default: bool,
+ /// Whether or not crt-static is respected by the compiler (or is a no-op).
+ pub crt_static_respected: bool,
+
+ /// The implementation of stack probes to use.
+ pub stack_probes: StackProbeType,
+
+ /// The minimum alignment for global symbols.
+ pub min_global_align: Option<u64>,
+
+ /// Default number of codegen units to use in debug mode
+ pub default_codegen_units: Option<u64>,
+
+ /// Whether to generate trap instructions in places where optimization would
+ /// otherwise produce control flow that falls through into unrelated memory.
+ pub trap_unreachable: bool,
+
+ /// This target requires everything to be compiled with LTO to emit a final
+ /// executable, aka there is no native linker for this target.
+ pub requires_lto: bool,
+
+ /// This target has no support for threads.
+ pub singlethread: bool,
+
+ /// Whether library functions call lowering/optimization is disabled in LLVM
+ /// for this target unconditionally.
+ pub no_builtins: bool,
+
+ /// The default visibility for symbols in this target should be "hidden"
+ /// rather than "default"
+ pub default_hidden_visibility: bool,
+
+ /// Whether a .debug_gdb_scripts section will be added to the output object file
+ pub emit_debug_gdb_scripts: bool,
+
+ /// Whether or not to unconditionally `uwtable` attributes on functions,
+ /// typically because the platform needs to unwind for things like stack
+ /// unwinders.
+ pub requires_uwtable: bool,
+
+ /// Whether or not to emit `uwtable` attributes on functions if `-C force-unwind-tables`
+ /// is not specified and `uwtable` is not required on this target.
+ pub default_uwtable: bool,
+
+ /// Whether or not SIMD types are passed by reference in the Rust ABI,
+ /// typically required if a target can be compiled with a mixed set of
+ /// target features. This is `true` by default, and `false` for targets like
+ /// wasm32 where the whole program either has simd or not.
+ pub simd_types_indirect: bool,
+
+ /// Pass a list of symbol which should be exported in the dylib to the linker.
+ pub limit_rdylib_exports: bool,
+
+ /// If set, have the linker export exactly these symbols, instead of using
+ /// the usual logic to figure this out from the crate itself.
+ pub override_export_symbols: Option<StaticCow<[StaticCow<str>]>>,
+
+ /// Determines how or whether the MergeFunctions LLVM pass should run for
+ /// this target. Either "disabled", "trampolines", or "aliases".
+ /// The MergeFunctions pass is generally useful, but some targets may need
+ /// to opt out. The default is "aliases".
+ ///
+ /// Workaround for: <https://github.com/rust-lang/rust/issues/57356>
+ pub merge_functions: MergeFunctions,
+
+ /// Use platform dependent mcount function
+ pub mcount: StaticCow<str>,
+
+ /// LLVM ABI name, corresponds to the '-mabi' parameter available in multilib C compilers
+ pub llvm_abiname: StaticCow<str>,
+
+ /// Whether or not RelaxElfRelocation flag will be passed to the linker
+ pub relax_elf_relocations: bool,
+
+ /// Additional arguments to pass to LLVM, similar to the `-C llvm-args` codegen option.
+ pub llvm_args: StaticCow<[StaticCow<str>]>,
+
+ /// Whether to use legacy .ctors initialization hooks rather than .init_array. Defaults
+ /// to false (uses .init_array).
+ pub use_ctors_section: bool,
+
+ /// Whether the linker is instructed to add a `GNU_EH_FRAME` ELF header
+ /// used to locate unwinding information is passed
+ /// (only has effect if the linker is `ld`-like).
+ pub eh_frame_header: bool,
+
+ /// Is true if the target is an ARM architecture using thumb v1 which allows for
+ /// thumb and arm interworking.
+ pub has_thumb_interworking: bool,
+
+ /// How to handle split debug information, if at all. Specifying `None` has
+ /// target-specific meaning.
+ pub split_debuginfo: SplitDebuginfo,
+
+ /// The sanitizers supported by this target
+ ///
+ /// Note that the support here is at a codegen level. If the machine code with sanitizer
+ /// enabled can generated on this target, but the necessary supporting libraries are not
+ /// distributed with the target, the sanitizer should still appear in this list for the target.
+ pub supported_sanitizers: SanitizerSet,
+
+ /// If present it's a default value to use for adjusting the C ABI.
+ pub default_adjusted_cabi: Option<Abi>,
+
+ /// Minimum number of bits in #[repr(C)] enum. Defaults to 32.
+ pub c_enum_min_bits: u64,
+
+ /// Whether or not the DWARF `.debug_aranges` section should be generated.
+ pub generate_arange_section: bool,
+
+ /// Whether the target supports stack canary checks. `true` by default,
+ /// since this is most common among tier 1 and tier 2 targets.
+ pub supports_stack_protector: bool,
+}
+
+/// Add arguments for the given flavor and also for its "twin" flavors
+/// that have a compatible command line interface.
+fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'static str]) {
+ let mut insert = |flavor| {
+ link_args.entry(flavor).or_default().extend(args.iter().copied().map(Cow::Borrowed))
+ };
+ insert(flavor);
+ match flavor {
+ LinkerFlavor::Ld => insert(LinkerFlavor::Lld(LldFlavor::Ld)),
+ LinkerFlavor::Msvc => insert(LinkerFlavor::Lld(LldFlavor::Link)),
+ LinkerFlavor::Lld(LldFlavor::Wasm) => {}
+ LinkerFlavor::Lld(lld_flavor) => {
+ panic!("add_link_args: use non-LLD flavor for {:?}", lld_flavor)
+ }
+ LinkerFlavor::Gcc
+ | LinkerFlavor::Em
+ | LinkerFlavor::L4Bender
+ | LinkerFlavor::BpfLinker
+ | LinkerFlavor::PtxLinker => {}
+ }
+}
+
+impl TargetOptions {
+ fn link_args(flavor: LinkerFlavor, args: &[&'static str]) -> LinkArgs {
+ let mut link_args = LinkArgs::new();
+ add_link_args(&mut link_args, flavor, args);
+ link_args
+ }
+
+ fn add_pre_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) {
+ add_link_args(&mut self.pre_link_args, flavor, args);
+ }
+
+ fn add_post_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) {
+ add_link_args(&mut self.post_link_args, flavor, args);
+ }
+}
+
+impl Default for TargetOptions {
+ /// Creates a set of "sane defaults" for any target. This is still
+ /// incomplete, and if used for compilation, will certainly not work.
+ fn default() -> TargetOptions {
+ TargetOptions {
+ is_builtin: false,
+ endian: Endian::Little,
+ c_int_width: "32".into(),
+ os: "none".into(),
+ env: "".into(),
+ abi: "".into(),
+ vendor: "unknown".into(),
+ linker_flavor: LinkerFlavor::Gcc,
+ linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.into()),
+ lld_flavor: LldFlavor::Ld,
+ pre_link_args: LinkArgs::new(),
+ post_link_args: LinkArgs::new(),
+ link_script: None,
+ asm_args: cvs![],
+ cpu: "generic".into(),
+ features: "".into(),
+ dynamic_linking: false,
+ only_cdylib: false,
+ executables: true,
+ relocation_model: RelocModel::Pic,
+ code_model: None,
+ tls_model: TlsModel::GeneralDynamic,
+ disable_redzone: false,
+ frame_pointer: FramePointer::MayOmit,
+ function_sections: true,
+ dll_prefix: "lib".into(),
+ dll_suffix: ".so".into(),
+ exe_suffix: "".into(),
+ staticlib_prefix: "lib".into(),
+ staticlib_suffix: ".a".into(),
+ families: cvs![],
+ abi_return_struct_as_int: false,
+ is_like_osx: false,
+ is_like_solaris: false,
+ is_like_windows: false,
+ is_like_msvc: false,
+ is_like_wasm: false,
+ default_dwarf_version: 4,
+ linker_is_gnu: true,
+ allows_weak_linkage: true,
+ has_rpath: false,
+ no_default_libraries: true,
+ position_independent_executables: false,
+ static_position_independent_executables: false,
+ needs_plt: false,
+ relro_level: RelroLevel::None,
+ pre_link_objects: Default::default(),
+ post_link_objects: Default::default(),
+ pre_link_objects_fallback: Default::default(),
+ post_link_objects_fallback: Default::default(),
+ crt_objects_fallback: None,
+ late_link_args: LinkArgs::new(),
+ late_link_args_dynamic: LinkArgs::new(),
+ late_link_args_static: LinkArgs::new(),
+ link_env: cvs![],
+ link_env_remove: cvs![],
+ archive_format: "gnu".into(),
+ main_needs_argc_argv: true,
+ allow_asm: true,
+ has_thread_local: false,
+ obj_is_bitcode: false,
+ forces_embed_bitcode: false,
+ bitcode_llvm_cmdline: "".into(),
+ min_atomic_width: None,
+ max_atomic_width: None,
+ atomic_cas: true,
+ panic_strategy: PanicStrategy::Unwind,
+ crt_static_allows_dylibs: false,
+ crt_static_default: false,
+ crt_static_respected: false,
+ stack_probes: StackProbeType::None,
+ min_global_align: None,
+ default_codegen_units: None,
+ trap_unreachable: true,
+ requires_lto: false,
+ singlethread: false,
+ no_builtins: false,
+ default_hidden_visibility: false,
+ emit_debug_gdb_scripts: true,
+ requires_uwtable: false,
+ default_uwtable: false,
+ simd_types_indirect: true,
+ limit_rdylib_exports: true,
+ override_export_symbols: None,
+ merge_functions: MergeFunctions::Aliases,
+ mcount: "mcount".into(),
+ llvm_abiname: "".into(),
+ relax_elf_relocations: false,
+ llvm_args: cvs![],
+ use_ctors_section: false,
+ eh_frame_header: true,
+ has_thumb_interworking: false,
+ split_debuginfo: SplitDebuginfo::Off,
+ supported_sanitizers: SanitizerSet::empty(),
+ default_adjusted_cabi: None,
+ c_enum_min_bits: 32,
+ generate_arange_section: true,
+ supports_stack_protector: true,
+ }
+ }
+}
+
+/// `TargetOptions` being a separate type is basically an implementation detail of `Target` that is
+/// used for providing defaults. Perhaps there's a way to merge `TargetOptions` into `Target` so
+/// this `Deref` implementation is no longer necessary.
+impl Deref for Target {
+ type Target = TargetOptions;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.options
+ }
+}
+impl DerefMut for Target {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.options
+ }
+}
+
+impl Target {
+ /// Given a function ABI, turn it into the correct ABI for this target.
+ pub fn adjust_abi(&self, abi: Abi) -> Abi {
+ match abi {
+ Abi::C { .. } => self.default_adjusted_cabi.unwrap_or(abi),
+ Abi::System { unwind } if self.is_like_windows && self.arch == "x86" => {
+ Abi::Stdcall { unwind }
+ }
+ Abi::System { unwind } => Abi::C { unwind },
+ Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false },
+ Abi::EfiApi => Abi::C { unwind: false },
+
+ // See commentary in `is_abi_supported`.
+ Abi::Stdcall { .. } | Abi::Thiscall { .. } if self.arch == "x86" => abi,
+ Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => Abi::C { unwind },
+ Abi::Fastcall { .. } if self.arch == "x86" => abi,
+ Abi::Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi,
+ Abi::Fastcall { unwind } | Abi::Vectorcall { unwind } => Abi::C { unwind },
+
+ abi => abi,
+ }
+ }
+
+ /// Returns a None if the UNSUPPORTED_CALLING_CONVENTIONS lint should be emitted
+ pub fn is_abi_supported(&self, abi: Abi) -> Option<bool> {
+ use Abi::*;
+ Some(match abi {
+ Rust
+ | C { .. }
+ | System { .. }
+ | RustIntrinsic
+ | RustCall
+ | PlatformIntrinsic
+ | Unadjusted
+ | Cdecl { .. }
+ | EfiApi
+ | RustCold => true,
+ X86Interrupt => ["x86", "x86_64"].contains(&&self.arch[..]),
+ Aapcs { .. } => "arm" == self.arch,
+ CCmseNonSecureCall => ["arm", "aarch64"].contains(&&self.arch[..]),
+ Win64 { .. } | SysV64 { .. } => self.arch == "x86_64",
+ PtxKernel => self.arch == "nvptx64",
+ Msp430Interrupt => self.arch == "msp430",
+ AmdGpuKernel => self.arch == "amdgcn",
+ AvrInterrupt | AvrNonBlockingInterrupt => self.arch == "avr",
+ Wasm => ["wasm32", "wasm64"].contains(&&self.arch[..]),
+ Thiscall { .. } => self.arch == "x86",
+ // On windows these fall-back to platform native calling convention (C) when the
+ // architecture is not supported.
+ //
+ // This is I believe a historical accident that has occurred as part of Microsoft
+ // striving to allow most of the code to "just" compile when support for 64-bit x86
+ // was added and then later again, when support for ARM architectures was added.
+ //
+ // This is well documented across MSDN. Support for this in Rust has been added in
+ // #54576. This makes much more sense in context of Microsoft's C++ than it does in
+ // Rust, but there isn't much leeway remaining here to change it back at the time this
+ // comment has been written.
+ //
+ // Following are the relevant excerpts from the MSDN documentation.
+ //
+ // > The __vectorcall calling convention is only supported in native code on x86 and
+ // x64 processors that include Streaming SIMD Extensions 2 (SSE2) and above.
+ // > ...
+ // > On ARM machines, __vectorcall is accepted and ignored by the compiler.
+ //
+ // -- https://docs.microsoft.com/en-us/cpp/cpp/vectorcall?view=msvc-160
+ //
+ // > On ARM and x64 processors, __stdcall is accepted and ignored by the compiler;
+ //
+ // -- https://docs.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-160
+ //
+ // > In most cases, keywords or compiler switches that specify an unsupported
+ // > convention on a particular platform are ignored, and the platform default
+ // > convention is used.
+ //
+ // -- https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions
+ Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } if self.is_like_windows => true,
+ // Outside of Windows we want to only support these calling conventions for the
+ // architectures for which these calling conventions are actually well defined.
+ Stdcall { .. } | Fastcall { .. } if self.arch == "x86" => true,
+ Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => true,
+ // Return a `None` for other cases so that we know to emit a future compat lint.
+ Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => return None,
+ })
+ }
+
+ /// Minimum integer size in bits that this target can perform atomic
+ /// operations on.
+ pub fn min_atomic_width(&self) -> u64 {
+ self.min_atomic_width.unwrap_or(8)
+ }
+
+ /// Maximum integer size in bits that this target can perform atomic
+ /// operations on.
+ pub fn max_atomic_width(&self) -> u64 {
+ self.max_atomic_width.unwrap_or_else(|| self.pointer_width.into())
+ }
+
+ /// Loads a target descriptor from a JSON object.
+ pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> {
+ // While ugly, this code must remain this way to retain
+ // compatibility with existing JSON fields and the internal
+ // expected naming of the Target and TargetOptions structs.
+ // To ensure compatibility is retained, the built-in targets
+ // are round-tripped through this code to catch cases where
+ // the JSON parser is not updated to match the structs.
+
+ let mut obj = match obj {
+ Value::Object(obj) => obj,
+ _ => return Err("Expected JSON object for target")?,
+ };
+
+ let mut get_req_field = |name: &str| {
+ obj.remove(name)
+ .and_then(|j| j.as_str().map(str::to_string))
+ .ok_or_else(|| format!("Field {} in target specification is required", name))
+ };
+
+ let mut base = Target {
+ llvm_target: get_req_field("llvm-target")?.into(),
+ pointer_width: get_req_field("target-pointer-width")?
+ .parse::<u32>()
+ .map_err(|_| "target-pointer-width must be an integer".to_string())?,
+ data_layout: get_req_field("data-layout")?.into(),
+ arch: get_req_field("arch")?.into(),
+ options: Default::default(),
+ };
+
+ let mut incorrect_type = vec![];
+
+ macro_rules! key {
+ ($key_name:ident) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(s) = obj.remove(&name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) {
+ base.$key_name = s;
+ }
+ } );
+ ($key_name:ident = $json_name:expr) => ( {
+ let name = $json_name;
+ if let Some(s) = obj.remove(name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) {
+ base.$key_name = s;
+ }
+ } );
+ ($key_name:ident, bool) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) {
+ base.$key_name = s;
+ }
+ } );
+ ($key_name:ident, u64) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(s) = obj.remove(&name).and_then(|j| Json::as_u64(&j)) {
+ base.$key_name = s;
+ }
+ } );
+ ($key_name:ident, u32) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) {
+ if s < 1 || s > 5 {
+ return Err("Not a valid DWARF version number".into());
+ }
+ base.$key_name = s as u32;
+ }
+ } );
+ ($key_name:ident, Option<u64>) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) {
+ base.$key_name = Some(s);
+ }
+ } );
+ ($key_name:ident, MergeFunctions) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s.parse::<MergeFunctions>() {
+ Ok(mergefunc) => base.$key_name = mergefunc,
+ _ => return Some(Err(format!("'{}' is not a valid value for \
+ merge-functions. Use 'disabled', \
+ 'trampolines', or 'aliases'.",
+ s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, RelocModel) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s.parse::<RelocModel>() {
+ Ok(relocation_model) => base.$key_name = relocation_model,
+ _ => return Some(Err(format!("'{}' is not a valid relocation model. \
+ Run `rustc --print relocation-models` to \
+ see the list of supported values.", s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, CodeModel) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s.parse::<CodeModel>() {
+ Ok(code_model) => base.$key_name = Some(code_model),
+ _ => return Some(Err(format!("'{}' is not a valid code model. \
+ Run `rustc --print code-models` to \
+ see the list of supported values.", s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, TlsModel) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s.parse::<TlsModel>() {
+ Ok(tls_model) => base.$key_name = tls_model,
+ _ => return Some(Err(format!("'{}' is not a valid TLS model. \
+ Run `rustc --print tls-models` to \
+ see the list of supported values.", s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, PanicStrategy) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s {
+ "unwind" => base.$key_name = PanicStrategy::Unwind,
+ "abort" => base.$key_name = PanicStrategy::Abort,
+ _ => return Some(Err(format!("'{}' is not a valid value for \
+ panic-strategy. Use 'unwind' or 'abort'.",
+ s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, RelroLevel) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s.parse::<RelroLevel>() {
+ Ok(level) => base.$key_name = level,
+ _ => return Some(Err(format!("'{}' is not a valid value for \
+ relro-level. Use 'full', 'partial, or 'off'.",
+ s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, SplitDebuginfo) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s.parse::<SplitDebuginfo>() {
+ Ok(level) => base.$key_name = level,
+ _ => return Some(Err(format!("'{}' is not a valid value for \
+ split-debuginfo. Use 'off' or 'dsymutil'.",
+ s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, list) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(j) = obj.remove(&name) {
+ if let Some(v) = j.as_array() {
+ base.$key_name = v.iter()
+ .map(|a| a.as_str().unwrap().to_string().into())
+ .collect();
+ } else {
+ incorrect_type.push(name)
+ }
+ }
+ } );
+ ($key_name:ident, opt_list) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(j) = obj.remove(&name) {
+ if let Some(v) = j.as_array() {
+ base.$key_name = Some(v.iter()
+ .map(|a| a.as_str().unwrap().to_string().into())
+ .collect());
+ } else {
+ incorrect_type.push(name)
+ }
+ }
+ } );
+ ($key_name:ident, optional) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(o) = obj.remove(&name) {
+ base.$key_name = o
+ .as_str()
+ .map(|s| s.to_string().into());
+ }
+ } );
+ ($key_name:ident, LldFlavor) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ if let Some(flavor) = LldFlavor::from_str(&s) {
+ base.$key_name = flavor;
+ } else {
+ return Some(Err(format!(
+ "'{}' is not a valid value for lld-flavor. \
+ Use 'darwin', 'gnu', 'link' or 'wasm.",
+ s)))
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, LinkerFlavor) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match LinkerFlavor::from_str(s) {
+ Some(linker_flavor) => base.$key_name = linker_flavor,
+ _ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \
+ Use {}", s, LinkerFlavor::one_of()))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, StackProbeType) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| match StackProbeType::from_json(&o) {
+ Ok(v) => {
+ base.$key_name = v;
+ Some(Ok(()))
+ },
+ Err(s) => Some(Err(
+ format!("`{:?}` is not a valid value for `{}`: {}", o, name, s)
+ )),
+ }).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, SanitizerSet) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(o) = obj.remove(&name) {
+ if let Some(a) = o.as_array() {
+ for s in a {
+ base.$key_name |= match s.as_str() {
+ Some("address") => SanitizerSet::ADDRESS,
+ Some("cfi") => SanitizerSet::CFI,
+ Some("leak") => SanitizerSet::LEAK,
+ Some("memory") => SanitizerSet::MEMORY,
+ Some("memtag") => SanitizerSet::MEMTAG,
+ Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
+ Some("thread") => SanitizerSet::THREAD,
+ Some("hwaddress") => SanitizerSet::HWADDRESS,
+ Some(s) => return Err(format!("unknown sanitizer {}", s)),
+ _ => return Err(format!("not a string: {:?}", s)),
+ };
+ }
+ } else {
+ incorrect_type.push(name)
+ }
+ }
+ Ok::<(), String>(())
+ } );
+
+ ($key_name:ident, crt_objects_fallback) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match s.parse::<CrtObjectsFallback>() {
+ Ok(fallback) => base.$key_name = Some(fallback),
+ _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \
+ Use 'musl', 'mingw' or 'wasm'", s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, link_objects) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(val) = obj.remove(&name) {
+ let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
+ JSON object with fields per CRT object kind.", name))?;
+ let mut args = CrtObjects::new();
+ for (k, v) in obj {
+ let kind = LinkOutputKind::from_str(&k).ok_or_else(|| {
+ format!("{}: '{}' is not a valid value for CRT object kind. \
+ Use '(dynamic,static)-(nopic,pic)-exe' or \
+ '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k)
+ })?;
+
+ let v = v.as_array().ok_or_else(||
+ format!("{}.{}: expected a JSON array", name, k)
+ )?.iter().enumerate()
+ .map(|(i,s)| {
+ let s = s.as_str().ok_or_else(||
+ format!("{}.{}[{}]: expected a JSON string", name, k, i))?;
+ Ok(s.to_string().into())
+ })
+ .collect::<Result<Vec<_>, String>>()?;
+
+ args.insert(kind, v);
+ }
+ base.$key_name = args;
+ }
+ } );
+ ($key_name:ident, link_args) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(val) = obj.remove(&name) {
+ let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
+ JSON object with fields per linker-flavor.", name))?;
+ let mut args = LinkArgs::new();
+ for (k, v) in obj {
+ let flavor = LinkerFlavor::from_str(&k).ok_or_else(|| {
+ format!("{}: '{}' is not a valid value for linker-flavor. \
+ Use 'em', 'gcc', 'ld' or 'msvc'", name, k)
+ })?;
+
+ let v = v.as_array().ok_or_else(||
+ format!("{}.{}: expected a JSON array", name, k)
+ )?.iter().enumerate()
+ .map(|(i,s)| {
+ let s = s.as_str().ok_or_else(||
+ format!("{}.{}[{}]: expected a JSON string", name, k, i))?;
+ Ok(s.to_string().into())
+ })
+ .collect::<Result<Vec<_>, String>>()?;
+
+ args.insert(flavor, v);
+ }
+ base.$key_name = args;
+ }
+ } );
+ ($key_name:ident, env) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ if let Some(o) = obj.remove(&name) {
+ if let Some(a) = o.as_array() {
+ for o in a {
+ if let Some(s) = o.as_str() {
+ let p = s.split('=').collect::<Vec<_>>();
+ if p.len() == 2 {
+ let k = p[0].to_string();
+ let v = p[1].to_string();
+ base.$key_name.to_mut().push((k.into(), v.into()));
+ }
+ }
+ }
+ } else {
+ incorrect_type.push(name)
+ }
+ }
+ } );
+ ($key_name:ident, Option<Abi>) => ( {
+ let name = (stringify!($key_name)).replace("_", "-");
+ obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+ match lookup_abi(s) {
+ Some(abi) => base.$key_name = Some(abi),
+ _ => return Some(Err(format!("'{}' is not a valid value for abi", s))),
+ }
+ Some(Ok(()))
+ })).unwrap_or(Ok(()))
+ } );
+ ($key_name:ident, TargetFamilies) => ( {
+ if let Some(value) = obj.remove("target-family") {
+ if let Some(v) = value.as_array() {
+ base.$key_name = v.iter()
+ .map(|a| a.as_str().unwrap().to_string().into())
+ .collect();
+ } else if let Some(v) = value.as_str() {
+ base.$key_name = vec![v.to_string().into()].into();
+ }
+ }
+ } );
+ }
+
+ if let Some(j) = obj.remove("target-endian") {
+ if let Some(s) = j.as_str() {
+ base.endian = s.parse()?;
+ } else {
+ incorrect_type.push("target-endian".into())
+ }
+ }
+
+ if let Some(fp) = obj.remove("frame-pointer") {
+ if let Some(s) = fp.as_str() {
+ base.frame_pointer = s
+ .parse()
+ .map_err(|()| format!("'{}' is not a valid value for frame-pointer", s))?;
+ } else {
+ incorrect_type.push("frame-pointer".into())
+ }
+ }
+
+ key!(is_builtin, bool);
+ key!(c_int_width = "target-c-int-width");
+ key!(os);
+ key!(env);
+ key!(abi);
+ key!(vendor);
+ key!(linker_flavor, LinkerFlavor)?;
+ key!(linker, optional);
+ key!(lld_flavor, LldFlavor)?;
+ key!(pre_link_objects, link_objects);
+ key!(post_link_objects, link_objects);
+ key!(pre_link_objects_fallback, link_objects);
+ key!(post_link_objects_fallback, link_objects);
+ key!(crt_objects_fallback, crt_objects_fallback)?;
+ key!(pre_link_args, link_args);
+ key!(late_link_args, link_args);
+ key!(late_link_args_dynamic, link_args);
+ key!(late_link_args_static, link_args);
+ key!(post_link_args, link_args);
+ key!(link_script, optional);
+ key!(link_env, env);
+ key!(link_env_remove, list);
+ key!(asm_args, list);
+ key!(cpu);
+ key!(features);
+ key!(dynamic_linking, bool);
+ key!(only_cdylib, bool);
+ key!(executables, bool);
+ key!(relocation_model, RelocModel)?;
+ key!(code_model, CodeModel)?;
+ key!(tls_model, TlsModel)?;
+ key!(disable_redzone, bool);
+ key!(function_sections, bool);
+ key!(dll_prefix);
+ key!(dll_suffix);
+ key!(exe_suffix);
+ key!(staticlib_prefix);
+ key!(staticlib_suffix);
+ key!(families, TargetFamilies);
+ key!(abi_return_struct_as_int, bool);
+ key!(is_like_osx, bool);
+ key!(is_like_solaris, bool);
+ key!(is_like_windows, bool);
+ key!(is_like_msvc, bool);
+ key!(is_like_wasm, bool);
+ key!(default_dwarf_version, u32);
+ key!(linker_is_gnu, bool);
+ key!(allows_weak_linkage, bool);
+ key!(has_rpath, bool);
+ key!(no_default_libraries, bool);
+ key!(position_independent_executables, bool);
+ key!(static_position_independent_executables, bool);
+ key!(needs_plt, bool);
+ key!(relro_level, RelroLevel)?;
+ key!(archive_format);
+ key!(allow_asm, bool);
+ key!(main_needs_argc_argv, bool);
+ key!(has_thread_local, bool);
+ key!(obj_is_bitcode, bool);
+ key!(forces_embed_bitcode, bool);
+ key!(bitcode_llvm_cmdline);
+ key!(max_atomic_width, Option<u64>);
+ key!(min_atomic_width, Option<u64>);
+ key!(atomic_cas, bool);
+ key!(panic_strategy, PanicStrategy)?;
+ key!(crt_static_allows_dylibs, bool);
+ key!(crt_static_default, bool);
+ key!(crt_static_respected, bool);
+ key!(stack_probes, StackProbeType)?;
+ key!(min_global_align, Option<u64>);
+ key!(default_codegen_units, Option<u64>);
+ key!(trap_unreachable, bool);
+ key!(requires_lto, bool);
+ key!(singlethread, bool);
+ key!(no_builtins, bool);
+ key!(default_hidden_visibility, bool);
+ key!(emit_debug_gdb_scripts, bool);
+ key!(requires_uwtable, bool);
+ key!(default_uwtable, bool);
+ key!(simd_types_indirect, bool);
+ key!(limit_rdylib_exports, bool);
+ key!(override_export_symbols, opt_list);
+ key!(merge_functions, MergeFunctions)?;
+ key!(mcount = "target-mcount");
+ key!(llvm_abiname);
+ key!(relax_elf_relocations, bool);
+ key!(llvm_args, list);
+ key!(use_ctors_section, bool);
+ key!(eh_frame_header, bool);
+ key!(has_thumb_interworking, bool);
+ key!(split_debuginfo, SplitDebuginfo)?;
+ key!(supported_sanitizers, SanitizerSet)?;
+ key!(default_adjusted_cabi, Option<Abi>)?;
+ key!(c_enum_min_bits, u64);
+ key!(generate_arange_section, bool);
+ key!(supports_stack_protector, bool);
+
+ if base.is_builtin {
+ // This can cause unfortunate ICEs later down the line.
+ return Err("may not set is_builtin for targets not built-in".into());
+ }
+ // Each field should have been read using `Json::remove` so any keys remaining are unused.
+ let remaining_keys = obj.keys();
+ Ok((
+ base,
+ TargetWarnings { unused_fields: remaining_keys.cloned().collect(), incorrect_type },
+ ))
+ }
+
+ /// Load a built-in target
+ pub fn expect_builtin(target_triple: &TargetTriple) -> Target {
+ match *target_triple {
+ TargetTriple::TargetTriple(ref target_triple) => {
+ load_builtin(target_triple).expect("built-in target")
+ }
+ TargetTriple::TargetJson { .. } => {
+ panic!("built-in targets doens't support target-paths")
+ }
+ }
+ }
+
+ /// Search for a JSON file specifying the given target triple.
+ ///
+ /// If none is found in `$RUST_TARGET_PATH`, look for a file called `target.json` inside the
+ /// sysroot under the target-triple's `rustlib` directory. Note that it could also just be a
+ /// bare filename already, so also check for that. If one of the hardcoded targets we know
+ /// about, just return it directly.
+ ///
+ /// The error string could come from any of the APIs called, including filesystem access and
+ /// JSON decoding.
+ pub fn search(
+ target_triple: &TargetTriple,
+ sysroot: &Path,
+ ) -> Result<(Target, TargetWarnings), String> {
+ use std::env;
+ use std::fs;
+
+ fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> {
+ let contents = fs::read_to_string(path).map_err(|e| e.to_string())?;
+ let obj = serde_json::from_str(&contents).map_err(|e| e.to_string())?;
+ Target::from_json(obj)
+ }
+
+ match *target_triple {
+ TargetTriple::TargetTriple(ref target_triple) => {
+ // check if triple is in list of built-in targets
+ if let Some(t) = load_builtin(target_triple) {
+ return Ok((t, TargetWarnings::empty()));
+ }
+
+ // search for a file named `target_triple`.json in RUST_TARGET_PATH
+ let path = {
+ let mut target = target_triple.to_string();
+ target.push_str(".json");
+ PathBuf::from(target)
+ };
+
+ let target_path = env::var_os("RUST_TARGET_PATH").unwrap_or_default();
+
+ for dir in env::split_paths(&target_path) {
+ let p = dir.join(&path);
+ if p.is_file() {
+ return load_file(&p);
+ }
+ }
+
+ // Additionally look in the sysroot under `lib/rustlib/<triple>/target.json`
+ // as a fallback.
+ let rustlib_path = crate::target_rustlib_path(&sysroot, &target_triple);
+ let p = PathBuf::from_iter([
+ Path::new(sysroot),
+ Path::new(&rustlib_path),
+ Path::new("target.json"),
+ ]);
+ if p.is_file() {
+ return load_file(&p);
+ }
+
+ Err(format!("Could not find specification for target {:?}", target_triple))
+ }
+ TargetTriple::TargetJson { ref contents, .. } => {
+ let obj = serde_json::from_str(contents).map_err(|e| e.to_string())?;
+ Target::from_json(obj)
+ }
+ }
+ }
+}
+
+impl ToJson for Target {
+ fn to_json(&self) -> Json {
+ let mut d = serde_json::Map::new();
+ let default: TargetOptions = Default::default();
+
+ macro_rules! target_val {
+ ($attr:ident) => {{
+ let name = (stringify!($attr)).replace("_", "-");
+ d.insert(name, self.$attr.to_json());
+ }};
+ }
+
+ macro_rules! target_option_val {
+ ($attr:ident) => {{
+ let name = (stringify!($attr)).replace("_", "-");
+ if default.$attr != self.$attr {
+ d.insert(name, self.$attr.to_json());
+ }
+ }};
+ ($attr:ident, $key_name:expr) => {{
+ let name = $key_name;
+ if default.$attr != self.$attr {
+ d.insert(name.into(), self.$attr.to_json());
+ }
+ }};
+ (link_args - $attr:ident) => {{
+ let name = (stringify!($attr)).replace("_", "-");
+ if default.$attr != self.$attr {
+ let obj = self
+ .$attr
+ .iter()
+ .map(|(k, v)| (k.desc().to_string(), v.clone()))
+ .collect::<BTreeMap<_, _>>();
+ d.insert(name, obj.to_json());
+ }
+ }};
+ (env - $attr:ident) => {{
+ let name = (stringify!($attr)).replace("_", "-");
+ if default.$attr != self.$attr {
+ let obj = self
+ .$attr
+ .iter()
+ .map(|&(ref k, ref v)| format!("{k}={v}"))
+ .collect::<Vec<_>>();
+ d.insert(name, obj.to_json());
+ }
+ }};
+ }
+
+ target_val!(llvm_target);
+ d.insert("target-pointer-width".to_string(), self.pointer_width.to_string().to_json());
+ target_val!(arch);
+ target_val!(data_layout);
+
+ target_option_val!(is_builtin);
+ target_option_val!(endian, "target-endian");
+ target_option_val!(c_int_width, "target-c-int-width");
+ target_option_val!(os);
+ target_option_val!(env);
+ target_option_val!(abi);
+ target_option_val!(vendor);
+ target_option_val!(linker_flavor);
+ target_option_val!(linker);
+ target_option_val!(lld_flavor);
+ target_option_val!(pre_link_objects);
+ target_option_val!(post_link_objects);
+ target_option_val!(pre_link_objects_fallback);
+ target_option_val!(post_link_objects_fallback);
+ target_option_val!(crt_objects_fallback);
+ target_option_val!(link_args - pre_link_args);
+ target_option_val!(link_args - late_link_args);
+ target_option_val!(link_args - late_link_args_dynamic);
+ target_option_val!(link_args - late_link_args_static);
+ target_option_val!(link_args - post_link_args);
+ target_option_val!(link_script);
+ target_option_val!(env - link_env);
+ target_option_val!(link_env_remove);
+ target_option_val!(asm_args);
+ target_option_val!(cpu);
+ target_option_val!(features);
+ target_option_val!(dynamic_linking);
+ target_option_val!(only_cdylib);
+ target_option_val!(executables);
+ target_option_val!(relocation_model);
+ target_option_val!(code_model);
+ target_option_val!(tls_model);
+ target_option_val!(disable_redzone);
+ target_option_val!(frame_pointer);
+ target_option_val!(function_sections);
+ target_option_val!(dll_prefix);
+ target_option_val!(dll_suffix);
+ target_option_val!(exe_suffix);
+ target_option_val!(staticlib_prefix);
+ target_option_val!(staticlib_suffix);
+ target_option_val!(families, "target-family");
+ target_option_val!(abi_return_struct_as_int);
+ target_option_val!(is_like_osx);
+ target_option_val!(is_like_solaris);
+ target_option_val!(is_like_windows);
+ target_option_val!(is_like_msvc);
+ target_option_val!(is_like_wasm);
+ target_option_val!(default_dwarf_version);
+ target_option_val!(linker_is_gnu);
+ target_option_val!(allows_weak_linkage);
+ target_option_val!(has_rpath);
+ target_option_val!(no_default_libraries);
+ target_option_val!(position_independent_executables);
+ target_option_val!(static_position_independent_executables);
+ target_option_val!(needs_plt);
+ target_option_val!(relro_level);
+ target_option_val!(archive_format);
+ target_option_val!(allow_asm);
+ target_option_val!(main_needs_argc_argv);
+ target_option_val!(has_thread_local);
+ target_option_val!(obj_is_bitcode);
+ target_option_val!(forces_embed_bitcode);
+ target_option_val!(bitcode_llvm_cmdline);
+ target_option_val!(min_atomic_width);
+ target_option_val!(max_atomic_width);
+ target_option_val!(atomic_cas);
+ target_option_val!(panic_strategy);
+ target_option_val!(crt_static_allows_dylibs);
+ target_option_val!(crt_static_default);
+ target_option_val!(crt_static_respected);
+ target_option_val!(stack_probes);
+ target_option_val!(min_global_align);
+ target_option_val!(default_codegen_units);
+ target_option_val!(trap_unreachable);
+ target_option_val!(requires_lto);
+ target_option_val!(singlethread);
+ target_option_val!(no_builtins);
+ target_option_val!(default_hidden_visibility);
+ target_option_val!(emit_debug_gdb_scripts);
+ target_option_val!(requires_uwtable);
+ target_option_val!(default_uwtable);
+ target_option_val!(simd_types_indirect);
+ target_option_val!(limit_rdylib_exports);
+ target_option_val!(override_export_symbols);
+ target_option_val!(merge_functions);
+ target_option_val!(mcount, "target-mcount");
+ target_option_val!(llvm_abiname);
+ target_option_val!(relax_elf_relocations);
+ target_option_val!(llvm_args);
+ target_option_val!(use_ctors_section);
+ target_option_val!(eh_frame_header);
+ target_option_val!(has_thumb_interworking);
+ target_option_val!(split_debuginfo);
+ target_option_val!(supported_sanitizers);
+ target_option_val!(c_enum_min_bits);
+ target_option_val!(generate_arange_section);
+ target_option_val!(supports_stack_protector);
+
+ if let Some(abi) = self.default_adjusted_cabi {
+ d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
+ }
+
+ Json::Object(d)
+ }
+}
+
+/// Either a target triple string or a path to a JSON file.
+#[derive(Clone, Debug)]
+pub enum TargetTriple {
+ TargetTriple(String),
+ TargetJson {
+ /// Warning: This field may only be used by rustdoc. Using it anywhere else will lead to
+ /// inconsistencies as it is discarded during serialization.
+ path_for_rustdoc: PathBuf,
+ triple: String,
+ contents: String,
+ },
+}
+
+// Use a manual implementation to ignore the path field
+impl PartialEq for TargetTriple {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Self::TargetTriple(l0), Self::TargetTriple(r0)) => l0 == r0,
+ (
+ Self::TargetJson { path_for_rustdoc: _, triple: l_triple, contents: l_contents },
+ Self::TargetJson { path_for_rustdoc: _, triple: r_triple, contents: r_contents },
+ ) => l_triple == r_triple && l_contents == r_contents,
+ _ => false,
+ }
+ }
+}
+
+// Use a manual implementation to ignore the path field
+impl Hash for TargetTriple {
+ fn hash<H: Hasher>(&self, state: &mut H) -> () {
+ match self {
+ TargetTriple::TargetTriple(triple) => {
+ 0u8.hash(state);
+ triple.hash(state)
+ }
+ TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => {
+ 1u8.hash(state);
+ triple.hash(state);
+ contents.hash(state)
+ }
+ }
+ }
+}
+
+// Use a manual implementation to prevent encoding the target json file path in the crate metadata
+impl<S: Encoder> Encodable<S> for TargetTriple {
+ fn encode(&self, s: &mut S) {
+ match self {
+ TargetTriple::TargetTriple(triple) => s.emit_enum_variant(0, |s| s.emit_str(triple)),
+ TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => s
+ .emit_enum_variant(1, |s| {
+ s.emit_str(triple);
+ s.emit_str(contents)
+ }),
+ }
+ }
+}
+
+impl<D: Decoder> Decodable<D> for TargetTriple {
+ fn decode(d: &mut D) -> Self {
+ match d.read_usize() {
+ 0 => TargetTriple::TargetTriple(d.read_str().to_owned()),
+ 1 => TargetTriple::TargetJson {
+ path_for_rustdoc: PathBuf::new(),
+ triple: d.read_str().to_owned(),
+ contents: d.read_str().to_owned(),
+ },
+ _ => {
+ panic!("invalid enum variant tag while decoding `TargetTriple`, expected 0..2");
+ }
+ }
+ }
+}
+
+impl TargetTriple {
+ /// Creates a target triple from the passed target triple string.
+ pub fn from_triple(triple: &str) -> Self {
+ TargetTriple::TargetTriple(triple.into())
+ }
+
+ /// Creates a target triple from the passed target path.
+ pub fn from_path(path: &Path) -> Result<Self, io::Error> {
+ let canonicalized_path = path.canonicalize()?;
+ let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| {
+ io::Error::new(
+ io::ErrorKind::InvalidInput,
+ format!("target path {:?} is not a valid file: {}", canonicalized_path, err),
+ )
+ })?;
+ let triple = canonicalized_path
+ .file_stem()
+ .expect("target path must not be empty")
+ .to_str()
+ .expect("target path must be valid unicode")
+ .to_owned();
+ Ok(TargetTriple::TargetJson { path_for_rustdoc: canonicalized_path, triple, contents })
+ }
+
+ /// Returns a string triple for this target.
+ ///
+ /// If this target is a path, the file name (without extension) is returned.
+ pub fn triple(&self) -> &str {
+ match *self {
+ TargetTriple::TargetTriple(ref triple)
+ | TargetTriple::TargetJson { ref triple, .. } => triple,
+ }
+ }
+
+ /// Returns an extended string triple for this target.
+ ///
+ /// If this target is a path, a hash of the path is appended to the triple returned
+ /// by `triple()`.
+ pub fn debug_triple(&self) -> String {
+ use std::collections::hash_map::DefaultHasher;
+
+ match self {
+ TargetTriple::TargetTriple(triple) => triple.to_owned(),
+ TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents: content } => {
+ let mut hasher = DefaultHasher::new();
+ content.hash(&mut hasher);
+ let hash = hasher.finish();
+ format!("{}-{}", triple, hash)
+ }
+ }
+ }
+}
+
+impl fmt::Display for TargetTriple {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.debug_triple())
+ }
+}
diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs
new file mode 100644
index 000000000..6b09386ae
--- /dev/null
+++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs
@@ -0,0 +1,59 @@
+use crate::spec::{cvs, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "msp430-none-elf".into(),
+ pointer_width: 16,
+ data_layout: "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16".into(),
+ arch: "msp430".into(),
+
+ options: TargetOptions {
+ c_int_width: "16".into(),
+
+ // The LLVM backend currently can't generate object files. To
+ // workaround this LLVM generates assembly files which then we feed
+ // to gcc to get object files. For this reason we have a hard
+ // dependency on this specific gcc.
+ asm_args: cvs!["-mcpu=msp430"],
+ linker: Some("msp430-elf-gcc".into()),
+ linker_is_gnu: false,
+
+ // There are no atomic CAS instructions available in the MSP430
+ // instruction set, and the LLVM backend doesn't currently support
+ // compiler fences so the Atomic* API is missing on this target.
+ // When the LLVM backend gains support for compile fences uncomment
+ // the `singlethread: true` line and set `max_atomic_width` to
+ // `Some(16)`.
+ max_atomic_width: Some(0),
+ atomic_cas: false,
+ // singlethread: true,
+
+ // Because these devices have very little resources having an
+ // unwinder is too onerous so we default to "abort" because the
+ // "unwind" strategy is very rare.
+ panic_strategy: PanicStrategy::Abort,
+
+ // Similarly, one almost always never wants to use relocatable
+ // code because of the extra costs it involves.
+ relocation_model: RelocModel::Static,
+
+ // Right now we invoke an external assembler and this isn't
+ // compatible with multiple codegen units, and plus we probably
+ // don't want to invoke that many gcc instances.
+ default_codegen_units: Some(1),
+
+ // Since MSP430 doesn't meaningfully support faulting on illegal
+ // instructions, LLVM generates a call to abort() function instead
+ // of a trap instruction. Such calls are 4 bytes long, and that is
+ // too much overhead for such small target.
+ trap_unreachable: false,
+
+ // See the thumb_base.rs file for an explanation of this value
+ emit_debug_gdb_scripts: false,
+
+ eh_frame_header: false,
+
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs
new file mode 100644
index 000000000..edb30b72b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/msvc_base.rs
@@ -0,0 +1,24 @@
+use crate::spec::{LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ // Suppress the verbose logo and authorship debugging output, which would needlessly
+ // clog any log files.
+ let pre_link_args = TargetOptions::link_args(LinkerFlavor::Msvc, &["/NOLOGO"]);
+
+ TargetOptions {
+ linker_flavor: LinkerFlavor::Msvc,
+ is_like_windows: true,
+ is_like_msvc: true,
+ lld_flavor: LldFlavor::Link,
+ linker_is_gnu: false,
+ pre_link_args,
+ abi_return_struct_as_int: true,
+ emit_debug_gdb_scripts: false,
+
+ // Currently this is the only supported method of debuginfo on MSVC
+ // where `*.pdb` files show up next to the final artifact.
+ split_debuginfo: SplitDebuginfo::Packed,
+
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs
new file mode 100644
index 000000000..be94ea234
--- /dev/null
+++ b/compiler/rustc_target/src/spec/netbsd_base.rs
@@ -0,0 +1,16 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "netbsd".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ no_default_libraries: false,
+ has_rpath: true,
+ position_independent_executables: true,
+ relro_level: RelroLevel::Full,
+ use_ctors_section: true,
+ default_dwarf_version: 2,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
new file mode 100644
index 000000000..1c5b68001
--- /dev/null
+++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
@@ -0,0 +1,53 @@
+use crate::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ arch: "nvptx64".into(),
+ data_layout: "e-i64:64-i128:128-v16:16-v32:32-n16:32:64".into(),
+ llvm_target: "nvptx64-nvidia-cuda".into(),
+ pointer_width: 64,
+
+ options: TargetOptions {
+ os: "cuda".into(),
+ vendor: "nvidia".into(),
+ linker_flavor: LinkerFlavor::PtxLinker,
+ // The linker can be installed from `crates.io`.
+ linker: Some("rust-ptx-linker".into()),
+ linker_is_gnu: false,
+
+ // With `ptx-linker` approach, it can be later overridden via link flags.
+ cpu: "sm_30".into(),
+
+ // FIXME: create tests for the atomics.
+ max_atomic_width: Some(64),
+
+ // Unwinding on CUDA is neither feasible nor useful.
+ panic_strategy: PanicStrategy::Abort,
+
+ // Needed to use `dylib` and `bin` crate types and the linker.
+ dynamic_linking: true,
+
+ // Avoid using dylib because it contain metadata not supported
+ // by LLVM NVPTX backend.
+ only_cdylib: true,
+
+ // Let the `ptx-linker` to handle LLVM lowering into MC / assembly.
+ obj_is_bitcode: true,
+
+ // Convenient and predicable naming scheme.
+ dll_prefix: "".into(),
+ dll_suffix: ".ptx".into(),
+ exe_suffix: ".ptx".into(),
+
+ // Disable MergeFunctions LLVM optimisation pass because it can
+ // produce kernel functions that call other kernel functions.
+ // This behavior is not supported by PTX ISA.
+ merge_functions: MergeFunctions::Disabled,
+
+ // The LLVM backend does not support stack canaries for this target
+ supports_stack_protector: false,
+
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs
new file mode 100644
index 000000000..e7db14e05
--- /dev/null
+++ b/compiler/rustc_target/src/spec/openbsd_base.rs
@@ -0,0 +1,16 @@
+use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "openbsd".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ has_rpath: true,
+ abi_return_struct_as_int: true,
+ position_independent_executables: true,
+ frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
+ relro_level: RelroLevel::Full,
+ default_dwarf_version: 2,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs
new file mode 100644
index 000000000..803453c4a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::freebsd_base::opts();
+ base.cpu = "ppc64".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "powerpc64-unknown-freebsd".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs
new file mode 100644
index 000000000..5413c4f33
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs
@@ -0,0 +1,21 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.cpu = "ppc64".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ // ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO
+ // for now. https://github.com/rust-lang/rust/pull/43170#issuecomment-315411474
+ base.relro_level = RelroLevel::Partial;
+
+ Target {
+ llvm_target: "powerpc64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs
new file mode 100644
index 000000000..159335eb6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "ppc64".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "powerpc64-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs
new file mode 100644
index 000000000..b7420d232
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::vxworks_base::opts();
+ base.cpu = "ppc64".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "powerpc64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { endian: Endian::Big, ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs
new file mode 100644
index 000000000..a3d180043
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs
@@ -0,0 +1,16 @@
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::freebsd_base::opts();
+ base.cpu = "ppc64le".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "powerpc64le-unknown-freebsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i64:64-n32:64".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs
new file mode 100644
index 000000000..e18ff3be4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs
@@ -0,0 +1,16 @@
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.cpu = "ppc64le".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "powerpc64le-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs
new file mode 100644
index 000000000..b84943d23
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs
@@ -0,0 +1,16 @@
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "ppc64le".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "powerpc64le-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(),
+ arch: "powerpc64".into(),
+ options: TargetOptions { mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs
new file mode 100644
index 000000000..516b2de37
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs
@@ -0,0 +1,23 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::freebsd_base::opts();
+ // Extra hint to linker that we are generating secure-PLT code.
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "--target=powerpc-unknown-freebsd13.0"]);
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-freebsd13.0".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: TargetOptions {
+ endian: Endian::Big,
+ features: "+secure-plt".into(),
+ relocation_model: RelocModel::Pic,
+ mcount: "_mcount".into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs
new file mode 100644
index 000000000..6686a0bbf
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs
@@ -0,0 +1,16 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs
new file mode 100644
index 000000000..6a250f4b5
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs
@@ -0,0 +1,21 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-mspe"]);
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-linux-gnuspe".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: TargetOptions {
+ abi: "spe".into(),
+ endian: Endian::Big,
+ mcount: "_mcount".into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs
new file mode 100644
index 000000000..34200c679
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs
@@ -0,0 +1,16 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-linux-musl".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs
new file mode 100644
index 000000000..60661ef9b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs
@@ -0,0 +1,16 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::netbsd_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-netbsd".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "__mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_openbsd.rs
new file mode 100644
index 000000000..ad2c3d40f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_openbsd.rs
@@ -0,0 +1,16 @@
+use crate::abi::Endian;
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::openbsd_base::opts();
+ base.endian = Endian::Big;
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-openbsd".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs
new file mode 100644
index 000000000..3f24966e0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs
@@ -0,0 +1,16 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::vxworks_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "--secure-plt"]);
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: TargetOptions { endian: Endian::Big, features: "+secure-plt".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs
new file mode 100644
index 000000000..0f04f41f9
--- /dev/null
+++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs
@@ -0,0 +1,22 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::vxworks_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-mspe", "--secure-plt"]);
+ base.max_atomic_width = Some(32);
+
+ Target {
+ llvm_target: "powerpc-unknown-linux-gnuspe".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-n32".into(),
+ arch: "powerpc".into(),
+ options: TargetOptions {
+ abi: "spe".into(),
+ endian: Endian::Big,
+ // feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2
+ features: "+secure-plt,+msync".into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs
new file mode 100644
index 000000000..468fe4785
--- /dev/null
+++ b/compiler/rustc_target/src/spec/redox_base.rs
@@ -0,0 +1,17 @@
+use crate::spec::{cvs, RelroLevel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "redox".into(),
+ env: "relibc".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ has_rpath: true,
+ position_independent_executables: true,
+ relro_level: RelroLevel::Full,
+ has_thread_local: true,
+ crt_static_default: true,
+ crt_static_respected: true,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs
new file mode 100644
index 000000000..bffd377bc
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs
@@ -0,0 +1,18 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv32-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ arch: "riscv32".into(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv32".into(),
+ features: "+m,+a,+f,+d,+c".into(),
+ llvm_abiname: "ilp32d".into(),
+ max_atomic_width: Some(32),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_musl.rs
new file mode 100644
index 000000000..c9f3acffb
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_musl.rs
@@ -0,0 +1,18 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv32-unknown-linux-musl".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ arch: "riscv32".into(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv32".into(),
+ features: "+m,+a,+f,+d,+c".into(),
+ llvm_abiname: "ilp32d".into(),
+ max_atomic_width: Some(32),
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
new file mode 100644
index 000000000..232139db6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
@@ -0,0 +1,24 @@
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ llvm_target: "riscv32".into(),
+ pointer_width: 32,
+ arch: "riscv32".into(),
+
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ cpu: "generic-rv32".into(),
+ max_atomic_width: Some(0),
+ atomic_cas: false,
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
new file mode 100644
index 000000000..3e5d2887f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
@@ -0,0 +1,25 @@
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ llvm_target: "riscv32".into(),
+ pointer_width: 32,
+ arch: "riscv32".into(),
+
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ cpu: "generic-rv32".into(),
+ max_atomic_width: Some(0),
+ atomic_cas: false,
+ features: "+m".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
new file mode 100644
index 000000000..99317b9f1
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
@@ -0,0 +1,24 @@
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ llvm_target: "riscv32".into(),
+ pointer_width: 32,
+ arch: "riscv32".into(),
+
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ cpu: "generic-rv32".into(),
+ max_atomic_width: Some(32),
+ features: "+m,+a,+c".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
new file mode 100644
index 000000000..a5de645c9
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
@@ -0,0 +1,23 @@
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ llvm_target: "riscv32".into(),
+ pointer_width: 32,
+ arch: "riscv32".into(),
+
+ options: TargetOptions {
+ os: "xous".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ cpu: "generic-rv32".into(),
+ max_atomic_width: Some(32),
+ features: "+m,+a,+c".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
new file mode 100644
index 000000000..03baef65c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
@@ -0,0 +1,36 @@
+use crate::spec::{cvs, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, PanicStrategy, RelocModel};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ llvm_target: "riscv32".into(),
+ pointer_width: 32,
+ arch: "riscv32".into(),
+
+ options: TargetOptions {
+ families: cvs!["unix"],
+ os: "espidf".into(),
+ env: "newlib".into(),
+ vendor: "espressif".into(),
+ linker_flavor: LinkerFlavor::Gcc,
+ linker: Some("riscv32-esp-elf-gcc".into()),
+ cpu: "generic-rv32".into(),
+
+ // While the RiscV32IMC architecture does not natively support atomics, ESP-IDF does support
+ // the __atomic* and __sync* GCC builtins, so setting `max_atomic_width` to `Some(64)`
+ // and `atomic_cas` to `true` will cause the compiler to emit libcalls to these builtins.
+ //
+ // Support for atomics is necessary for the Rust STD library, which is supported by the ESP-IDF framework.
+ max_atomic_width: Some(64),
+ atomic_cas: true,
+
+ features: "+m,+c".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
new file mode 100644
index 000000000..bf510d204
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
@@ -0,0 +1,25 @@
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ llvm_target: "riscv32".into(),
+ pointer_width: 32,
+ arch: "riscv32".into(),
+
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ cpu: "generic-rv32".into(),
+ max_atomic_width: Some(0),
+ atomic_cas: false,
+ features: "+m,+c".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_freebsd.rs
new file mode 100644
index 000000000..0539eca6c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_freebsd.rs
@@ -0,0 +1,18 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv64-unknown-freebsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ arch: "riscv64".into(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv64".into(),
+ features: "+m,+a,+f,+d,+c".into(),
+ llvm_abiname: "lp64d".into(),
+ max_atomic_width: Some(64),
+ ..super::freebsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs
new file mode 100644
index 000000000..7d1bf228c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs
@@ -0,0 +1,18 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ arch: "riscv64".into(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv64".into(),
+ features: "+m,+a,+f,+d,+c".into(),
+ llvm_abiname: "lp64d".into(),
+ max_atomic_width: Some(64),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_musl.rs
new file mode 100644
index 000000000..f04f8a48b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_musl.rs
@@ -0,0 +1,18 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv64-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ arch: "riscv64".into(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv64".into(),
+ features: "+m,+a,+f,+d,+c".into(),
+ llvm_abiname: "lp64d".into(),
+ max_atomic_width: Some(64),
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
new file mode 100644
index 000000000..03b3cfd1e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
@@ -0,0 +1,26 @@
+use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ llvm_target: "riscv64".into(),
+ pointer_width: 64,
+ arch: "riscv64".into(),
+
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ llvm_abiname: "lp64d".into(),
+ cpu: "generic-rv64".into(),
+ max_atomic_width: Some(64),
+ features: "+m,+a,+f,+d,+c".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ code_model: Some(CodeModel::Medium),
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
new file mode 100644
index 000000000..2a94c9dd2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
@@ -0,0 +1,25 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ llvm_target: "riscv64".into(),
+ pointer_width: 64,
+ arch: "riscv64".into(),
+
+ options: TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ cpu: "generic-rv64".into(),
+ max_atomic_width: Some(64),
+ features: "+m,+a,+c".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ code_model: Some(CodeModel::Medium),
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs
new file mode 100644
index 000000000..8757bbed8
--- /dev/null
+++ b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs
@@ -0,0 +1,23 @@
+use crate::abi::Endian;
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.endian = Endian::Big;
+ // z10 is the oldest CPU supported by LLVM
+ base.cpu = "z10".into();
+ // FIXME: The data_layout string below and the ABI implementation in
+ // cabi_s390x.rs are for now hard-coded to assume the no-vector ABI.
+ // Pass the -vector feature string to LLVM to respect this assumption.
+ base.features = "-vector".into();
+ base.max_atomic_width = Some(64);
+ base.min_global_align = Some(16);
+
+ Target {
+ llvm_target: "s390x-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64".into(),
+ arch: "s390x".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_musl.rs
new file mode 100644
index 000000000..4c855271a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/s390x_unknown_linux_musl.rs
@@ -0,0 +1,24 @@
+use crate::abi::Endian;
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.endian = Endian::Big;
+ // z10 is the oldest CPU supported by LLVM
+ base.cpu = "z10".into();
+ // FIXME: The data_layout string below and the ABI implementation in
+ // cabi_s390x.rs are for now hard-coded to assume the no-vector ABI.
+ // Pass the -vector feature string to LLVM to respect this assumption.
+ base.features = "-vector".into();
+ base.max_atomic_width = Some(64);
+ base.min_global_align = Some(16);
+ base.static_position_independent_executables = true;
+
+ Target {
+ llvm_target: "s390x-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64".into(),
+ arch: "s390x".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs
new file mode 100644
index 000000000..b7e8e8cf7
--- /dev/null
+++ b/compiler/rustc_target/src/spec/solaris_base.rs
@@ -0,0 +1,16 @@
+use crate::spec::{cvs, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "solaris".into(),
+ dynamic_linking: true,
+ has_rpath: true,
+ families: cvs!["unix"],
+ is_like_solaris: true,
+ linker_is_gnu: false,
+ limit_rdylib_exports: false, // Linker doesn't support this
+ eh_frame_header: false,
+
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/solid_base.rs b/compiler/rustc_target/src/spec/solid_base.rs
new file mode 100644
index 000000000..c585a6cd5
--- /dev/null
+++ b/compiler/rustc_target/src/spec/solid_base.rs
@@ -0,0 +1,13 @@
+use super::FramePointer;
+use crate::spec::TargetOptions;
+
+pub fn opts(kernel: &str) -> TargetOptions {
+ TargetOptions {
+ os: format!("solid_{}", kernel).into(),
+ vendor: "kmc".into(),
+ executables: false,
+ frame_pointer: FramePointer::NonLeaf,
+ has_thread_local: true,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs
new file mode 100644
index 000000000..39efd8f30
--- /dev/null
+++ b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.endian = Endian::Big;
+ base.cpu = "v9".into();
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "sparc64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64-S128".into(),
+ arch: "sparc64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs
new file mode 100644
index 000000000..836ab0e37
--- /dev/null
+++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::netbsd_base::opts();
+ base.cpu = "v9".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "sparc64-unknown-netbsd".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64-S128".into(),
+ arch: "sparc64".into(),
+ options: TargetOptions { endian: Endian::Big, mcount: "__mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs
new file mode 100644
index 000000000..4a192df39
--- /dev/null
+++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs
@@ -0,0 +1,18 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::openbsd_base::opts();
+ base.endian = Endian::Big;
+ base.cpu = "v9".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "sparc64-unknown-openbsd".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64-S128".into(),
+ arch: "sparc64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs
new file mode 100644
index 000000000..ea4fafa4b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs
@@ -0,0 +1,18 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.endian = Endian::Big;
+ base.cpu = "v9".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-mv8plus"]);
+
+ Target {
+ llvm_target: "sparc-unknown-linux-gnu".into(),
+ pointer_width: 32,
+ data_layout: "E-m:e-p:32:32-i64:64-f128:64-n32-S64".into(),
+ arch: "sparc".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs
new file mode 100644
index 000000000..aac09181a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs
@@ -0,0 +1,24 @@
+use crate::abi::Endian;
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::solaris_base::opts();
+ base.endian = Endian::Big;
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // llvm calls this "v9"
+ base.cpu = "v9".into();
+ base.vendor = "sun".into();
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "sparcv9-sun-solaris".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i64:64-n32:64-S128".into(),
+ // Use "sparc64" instead of "sparcv9" here, since the former is already
+ // used widely in the source base. If we ever needed ABI
+ // differentiation from the sparc64, we could, but that would probably
+ // just be confusing.
+ arch: "sparc64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs
new file mode 100644
index 000000000..1db6db78b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs
@@ -0,0 +1,135 @@
+use super::super::*;
+use std::assert_matches::assert_matches;
+
+// Test target self-consistency and JSON encoding/decoding roundtrip.
+pub(super) fn test_target(target: Target) {
+ target.check_consistency();
+ assert_eq!(Target::from_json(target.to_json()).map(|(j, _)| j), Ok(target));
+}
+
+impl Target {
+ fn check_consistency(&self) {
+ assert_eq!(self.is_like_osx, self.vendor == "apple");
+ assert_eq!(self.is_like_solaris, self.os == "solaris" || self.os == "illumos");
+ assert_eq!(self.is_like_windows, self.os == "windows" || self.os == "uefi");
+ assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64");
+ assert!(self.is_like_windows || !self.is_like_msvc);
+
+ // Check that default linker flavor and lld flavor are compatible
+ // with some other key properties.
+ assert_eq!(self.is_like_osx, matches!(self.lld_flavor, LldFlavor::Ld64));
+ assert_eq!(self.is_like_msvc, matches!(self.lld_flavor, LldFlavor::Link));
+ assert_eq!(self.is_like_wasm, matches!(self.lld_flavor, LldFlavor::Wasm));
+ assert_eq!(self.os == "l4re", matches!(self.linker_flavor, LinkerFlavor::L4Bender));
+ assert_eq!(self.os == "emscripten", matches!(self.linker_flavor, LinkerFlavor::Em));
+ assert_eq!(self.arch == "bpf", matches!(self.linker_flavor, LinkerFlavor::BpfLinker));
+ assert_eq!(self.arch == "nvptx64", matches!(self.linker_flavor, LinkerFlavor::PtxLinker));
+
+ for args in [
+ &self.pre_link_args,
+ &self.late_link_args,
+ &self.late_link_args_dynamic,
+ &self.late_link_args_static,
+ &self.post_link_args,
+ ] {
+ for (&flavor, flavor_args) in args {
+ assert!(!flavor_args.is_empty());
+ // Check that flavors mentioned in link args are compatible with the default flavor.
+ match (self.linker_flavor, self.lld_flavor) {
+ (
+ LinkerFlavor::Ld | LinkerFlavor::Lld(LldFlavor::Ld) | LinkerFlavor::Gcc,
+ LldFlavor::Ld,
+ ) => {
+ assert_matches!(
+ flavor,
+ LinkerFlavor::Ld | LinkerFlavor::Lld(LldFlavor::Ld) | LinkerFlavor::Gcc
+ )
+ }
+ (LinkerFlavor::Gcc, LldFlavor::Ld64) => {
+ assert_matches!(
+ flavor,
+ LinkerFlavor::Lld(LldFlavor::Ld64) | LinkerFlavor::Gcc
+ )
+ }
+ (LinkerFlavor::Msvc | LinkerFlavor::Lld(LldFlavor::Link), LldFlavor::Link) => {
+ assert_matches!(
+ flavor,
+ LinkerFlavor::Msvc | LinkerFlavor::Lld(LldFlavor::Link)
+ )
+ }
+ (LinkerFlavor::Lld(LldFlavor::Wasm) | LinkerFlavor::Gcc, LldFlavor::Wasm) => {
+ assert_matches!(
+ flavor,
+ LinkerFlavor::Lld(LldFlavor::Wasm) | LinkerFlavor::Gcc
+ )
+ }
+ (LinkerFlavor::L4Bender, LldFlavor::Ld) => {
+ assert_matches!(flavor, LinkerFlavor::L4Bender)
+ }
+ (LinkerFlavor::Em, LldFlavor::Wasm) => {
+ assert_matches!(flavor, LinkerFlavor::Em)
+ }
+ (LinkerFlavor::BpfLinker, LldFlavor::Ld) => {
+ assert_matches!(flavor, LinkerFlavor::BpfLinker)
+ }
+ (LinkerFlavor::PtxLinker, LldFlavor::Ld) => {
+ assert_matches!(flavor, LinkerFlavor::PtxLinker)
+ }
+ flavors => unreachable!("unexpected flavor combination: {:?}", flavors),
+ }
+
+ // Check that link args for cc and non-cc versions of flavors are consistent.
+ let check_noncc = |noncc_flavor| {
+ if let Some(noncc_args) = args.get(&noncc_flavor) {
+ for arg in flavor_args {
+ if let Some(suffix) = arg.strip_prefix("-Wl,") {
+ assert!(noncc_args.iter().any(|a| a == suffix));
+ }
+ }
+ }
+ };
+ match self.linker_flavor {
+ LinkerFlavor::Gcc => match self.lld_flavor {
+ LldFlavor::Ld => {
+ check_noncc(LinkerFlavor::Ld);
+ check_noncc(LinkerFlavor::Lld(LldFlavor::Ld));
+ }
+ LldFlavor::Wasm => check_noncc(LinkerFlavor::Lld(LldFlavor::Wasm)),
+ LldFlavor::Ld64 | LldFlavor::Link => {}
+ },
+ _ => {}
+ }
+ }
+
+ // Check that link args for lld and non-lld versions of flavors are consistent.
+ assert_eq!(args.get(&LinkerFlavor::Ld), args.get(&LinkerFlavor::Lld(LldFlavor::Ld)));
+ assert_eq!(
+ args.get(&LinkerFlavor::Msvc),
+ args.get(&LinkerFlavor::Lld(LldFlavor::Link)),
+ );
+ }
+
+ assert!(
+ (self.pre_link_objects_fallback.is_empty()
+ && self.post_link_objects_fallback.is_empty())
+ || self.crt_objects_fallback.is_some()
+ );
+
+ // If your target really needs to deviate from the rules below,
+ // except it and document the reasons.
+ // Keep the default "unknown" vendor instead.
+ assert_ne!(self.vendor, "");
+ if !self.can_use_os_unknown() {
+ // Keep the default "none" for bare metal targets instead.
+ assert_ne!(self.os, "unknown");
+ }
+ }
+
+ // Add your target to the whitelist if it has `std` library
+ // and you certainly want "unknown" for the OS name.
+ fn can_use_os_unknown(&self) -> bool {
+ self.llvm_target == "wasm32-unknown-unknown"
+ || self.llvm_target == "wasm64-unknown-unknown"
+ || (self.env == "sgx" && self.vendor == "fortanix")
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs
new file mode 100644
index 000000000..049142b89
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumb_base.rs
@@ -0,0 +1,60 @@
+// These `thumbv*` targets cover the ARM Cortex-M family of processors which are widely used in
+// microcontrollers. Namely, all these processors:
+//
+// - Cortex-M0
+// - Cortex-M0+
+// - Cortex-M1
+// - Cortex-M3
+// - Cortex-M4(F)
+// - Cortex-M7(F)
+// - Cortex-M23
+// - Cortex-M33
+//
+// We have opted for these instead of one target per processor (e.g., `cortex-m0`, `cortex-m3`,
+// etc) because the differences between some processors like the cortex-m0 and cortex-m1 are almost
+// non-existent from the POV of codegen so it doesn't make sense to have separate targets for them.
+// And if differences exist between two processors under the same target, rustc flags can be used to
+// optimize for one processor or the other.
+//
+// Also, we have not chosen a single target (`arm-none-eabi`) like GCC does because this makes
+// difficult to integrate Rust code and C code. Targeting the Cortex-M4 requires different gcc flags
+// than the ones you would use for the Cortex-M0 and with a single target it'd be impossible to
+// differentiate one processor from the other.
+//
+// About arm vs thumb in the name. The Cortex-M devices only support the Thumb instruction set,
+// which is more compact (higher code density), and not the ARM instruction set. That's why LLVM
+// triples use thumb instead of arm. We follow suit because having thumb in the name let us
+// differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of
+// build scripts / gcc flags.
+
+use crate::spec::TargetOptions;
+use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
+
+pub fn opts() -> TargetOptions {
+ // See rust-lang/rfcs#1645 for a discussion about these defaults
+ TargetOptions {
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ // In most cases, LLD is good enough
+ linker: Some("rust-lld".into()),
+ // Because these devices have very little resources having an unwinder is too onerous so we
+ // default to "abort" because the "unwind" strategy is very rare.
+ panic_strategy: PanicStrategy::Abort,
+ // Similarly, one almost always never wants to use relocatable code because of the extra
+ // costs it involves.
+ relocation_model: RelocModel::Static,
+ // When this section is added a volatile load to its start address is also generated. This
+ // volatile load is a footgun as it can end up loading an invalid memory address, depending
+ // on how the user set up their linker scripts. This section adds pretty printer for stuff
+ // like std::Vec, which is not that used in no-std context, so it's best to left it out
+ // until we figure a way to add the pretty printers without requiring a volatile load cf.
+ // rust-lang/rust#44993.
+ emit_debug_gdb_scripts: false,
+ // LLVM is eager to trash the link register when calling `noreturn` functions, which
+ // breaks debugging. Preserve LR by default to prevent that from happening.
+ frame_pointer: FramePointer::Always,
+ // ARM supports multiple ABIs for enums, the linux one matches the default of 32 here
+ // but any arm-none or thumb-none target will be defaulted to 8 on GCC and clang
+ c_enum_min_bits: 8,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs
new file mode 100644
index 000000000..7125d141a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs
@@ -0,0 +1,69 @@
+//! Targets the ARMv4T, with code as `t32` code by default.
+//!
+//! Primarily of use for the GBA, but usable with other devices too.
+//!
+//! Please ping @Lokathor if changes are needed.
+//!
+//! This target profile assumes that you have the ARM binutils in your path
+//! (specifically the linker, `arm-none-eabi-ld`). They can be obtained for free
+//! for all major OSes from the ARM developer's website, and they may also be
+//! available in your system's package manager. Unfortunately, the standard
+//! linker that Rust uses (`lld`) only supports as far back as `ARMv5TE`, so we
+//! must use the GNU `ld` linker.
+//!
+//! **Important:** This target profile **does not** specify a linker script. You
+//! just get the default link script when you build a binary for this target.
+//! The default link script is very likely wrong, so you should use
+//! `-Clink-arg=-Tmy_script.ld` to override that with a correct linker script.
+
+use crate::spec::{
+ cvs, FramePointer, LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions,
+};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv4t-none-eabi".into(),
+ pointer_width: 32,
+ arch: "arm".into(),
+ /* Data layout args are '-' separated:
+ * little endian
+ * stack is 64-bit aligned (EABI)
+ * pointers are 32-bit
+ * i64 must be 64-bit aligned (EABI)
+ * mangle names with ELF style
+ * native integers are 32-bit
+ * All other elements are default
+ */
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ linker_flavor: LinkerFlavor::Ld,
+ linker: Some("arm-none-eabi-ld".into()),
+
+ // extra args passed to the external assembler (assuming `arm-none-eabi-as`):
+ // * activate t32/a32 interworking
+ // * use arch ARMv4T
+ // * use little-endian
+ asm_args: cvs!["-mthumb-interwork", "-march=armv4t", "-mlittle-endian",],
+
+ // minimum extra features, these cannot be disabled via -C
+ features: "+soft-float,+strict-align".into(),
+
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ // suggested from thumb_base, rust-lang/rust#44993.
+ emit_debug_gdb_scripts: false,
+ // suggested from thumb_base, with no-os gcc/clang use 8-bit enums
+ c_enum_min_bits: 8,
+ frame_pointer: FramePointer::MayOmit,
+
+ main_needs_argc_argv: false,
+
+ // don't have atomic compare-and-swap
+ atomic_cas: false,
+ has_thumb_interworking: true,
+
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs
new file mode 100644
index 000000000..2546ab9b7
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs
@@ -0,0 +1,23 @@
+// Targets the Cortex-M0, Cortex-M0+ and Cortex-M1 processors (ARMv6-M architecture)
+
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv6m-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabi".into(),
+ // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them
+ // with +strict-align.
+ features: "+strict-align".into(),
+ // There are no atomic CAS instructions available in the instruction set of the ARMv6-M
+ // architecture
+ atomic_cas: false,
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs
new file mode 100644
index 000000000..4d09d3a4d
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs
@@ -0,0 +1,28 @@
+use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::windows_msvc_base::opts();
+ // Prevent error LNK2013: BRANCH24(T) fixup overflow
+ // The LBR optimization tries to eliminate branch islands,
+ // but if the displacement is larger than can fit
+ // in the instruction, this error will occur. The linker
+ // should be smart enough to insert branch islands only
+ // where necessary, but this is not the observed behavior.
+ // Disabling the LBR optimization works around the issue.
+ base.add_pre_link_args(LinkerFlavor::Msvc, &["/OPT:NOLBR"]);
+
+ Target {
+ llvm_target: "thumbv7a-pc-windows-msvc".into(),
+ pointer_width: 32,
+ data_layout: "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ features: "+vfp3,+neon".into(),
+ max_atomic_width: Some(64),
+ // FIXME(jordanrh): use PanicStrategy::Unwind when SEH is
+ // implemented for windows/arm in LLVM
+ panic_strategy: PanicStrategy::Abort,
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs
new file mode 100644
index 000000000..65c2f5a70
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs
@@ -0,0 +1,18 @@
+use crate::spec::{PanicStrategy, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv7a-pc-windows-msvc".into(),
+ pointer_width: 32,
+ data_layout: "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ features: "+vfp3,+neon".into(),
+ max_atomic_width: Some(64),
+ // FIXME(jordanrh): use PanicStrategy::Unwind when SEH is
+ // implemented for windows/arm in LLVM
+ panic_strategy: PanicStrategy::Abort,
+ ..super::windows_uwp_msvc_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs
new file mode 100644
index 000000000..000e5f2d3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs
@@ -0,0 +1,27 @@
+// Targets the Cortex-M4 and Cortex-M7 processors (ARMv7E-M)
+//
+// This target assumes that the device doesn't have a FPU (Floating Point Unit) and lowers all the
+// floating point operations to software routines (intrinsics).
+//
+// As such, this target uses the "soft" calling convention (ABI) where floating point values are
+// passed to/from subroutines via general purpose registers (R0, R1, etc.).
+//
+// To opt-in to hardware accelerated floating point operations, you can use, for example,
+// `-C target-feature=+vfp4` or `-C target-cpu=cortex-m4`.
+
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv7em-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabi".into(),
+ max_atomic_width: Some(32),
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs
new file mode 100644
index 000000000..39a72564e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs
@@ -0,0 +1,36 @@
+// Targets the Cortex-M4F and Cortex-M7F processors (ARMv7E-M)
+//
+// This target assumes that the device does have a FPU (Floating Point Unit) and lowers all (single
+// precision) floating point operations to hardware instructions.
+//
+// Additionally, this target uses the "hard" floating convention (ABI) where floating point values
+// are passed to/from subroutines via FPU registers (S0, S1, D0, D1, etc.).
+//
+// To opt into double precision hardware support, use the `-C target-feature=+fp64` flag.
+
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv7em-none-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // `+vfp4` is the lowest common denominator between the Cortex-M4 (vfp4-16) and the
+ // Cortex-M7 (vfp5)
+ // `-d32` both the Cortex-M4 and the Cortex-M7 only have 16 double-precision registers
+ // available
+ // `-fp64` The Cortex-M4 only supports single precision floating point operations
+ // whereas in the Cortex-M7 double precision is optional
+ //
+ // Reference:
+ // ARMv7-M Architecture Reference Manual - A2.5 The optional floating-point extension
+ features: "+vfp4,-d32,-fp64".into(),
+ max_atomic_width: Some(32),
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs
new file mode 100644
index 000000000..ab25cde66
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs
@@ -0,0 +1,18 @@
+// Targets the Cortex-M3 processor (ARMv7-M)
+
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv7m-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabi".into(),
+ max_atomic_width: Some(32),
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs
new file mode 100644
index 000000000..4cad9e183
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs
@@ -0,0 +1,26 @@
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
+
+// This target if is for the Android v7a ABI in thumb mode with
+// NEON unconditionally enabled and, therefore, with 32 FPU registers
+// enabled as well. See section A2.6.2 on page A2-56 in
+// https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf
+
+// See https://developer.android.com/ndk/guides/abis.html#v7a
+// for target ABI requirements.
+
+pub fn target() -> Target {
+ let mut base = super::android_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-march=armv7-a"]);
+ Target {
+ llvm_target: "armv7-none-linux-android".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabi".into(),
+ features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".into(),
+ max_atomic_width: Some(64),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs
new file mode 100644
index 000000000..28c81340a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs
@@ -0,0 +1,23 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for glibc Linux on ARMv7 with thumb mode enabled
+// (for consistency with Android and Debian-based distributions)
+// and with NEON unconditionally enabled and, therefore, with 32 FPU
+// registers enabled as well. See section A2.6.2 on page A2-56 in
+// https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "armv7-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // Info about features at https://wiki.debian.org/ArmHardFloatPort
+ features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".into(),
+ max_atomic_width: Some(64),
+ ..super::linux_gnu_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs
new file mode 100644
index 000000000..2c375ab22
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs
@@ -0,0 +1,29 @@
+use crate::spec::{Target, TargetOptions};
+
+// This target is for musl Linux on ARMv7 with thumb mode enabled
+// (for consistency with Android and Debian-based distributions)
+// and with NEON unconditionally enabled and, therefore, with 32 FPU
+// registers enabled as well. See section A2.6.2 on page A2-56 in
+// https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf
+
+pub fn target() -> Target {
+ Target {
+ // It's important we use "gnueabihf" and not "musleabihf" here. LLVM
+ // uses it to determine the calling convention and float ABI, and LLVM
+ // doesn't support the "musleabihf" value.
+ llvm_target: "armv7-unknown-linux-gnueabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ // Most of these settings are copied from the thumbv7neon_unknown_linux_gnueabihf
+ // target.
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".into(),
+ max_atomic_width: Some(64),
+ mcount: "\u{1}mcount".into(),
+ ..super::linux_musl_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs
new file mode 100644
index 000000000..756b1834c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs
@@ -0,0 +1,21 @@
+// Targets the Cortex-M23 processor (Baseline ARMv8-M)
+
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv8m.base-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabi".into(),
+ // ARMv8-M baseline doesn't support unaligned loads/stores so we disable them
+ // with +strict-align.
+ features: "+strict-align".into(),
+ max_atomic_width: Some(32),
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs
new file mode 100644
index 000000000..4b6268546
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs
@@ -0,0 +1,19 @@
+// Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile),
+// without the Floating Point extension.
+
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv8m.main-none-eabi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabi".into(),
+ max_atomic_width: Some(32),
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs
new file mode 100644
index 000000000..86c25f9e4
--- /dev/null
+++ b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs
@@ -0,0 +1,25 @@
+// Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile),
+// with the Floating Point extension.
+
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "thumbv8m.main-none-eabihf".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+ arch: "arm".into(),
+
+ options: TargetOptions {
+ abi: "eabihf".into(),
+ // If the Floating Point extension is implemented in the Cortex-M33
+ // processor, the Cortex-M33 Technical Reference Manual states that
+ // the FPU uses the FPv5 architecture, single-precision instructions
+ // and 16 D registers.
+ // These parameters map to the following LLVM features.
+ features: "+fp-armv8,-fp64,-d32".into(),
+ max_atomic_width: Some(32),
+ ..super::thumb_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs
new file mode 100644
index 000000000..aee8eb2e3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs
@@ -0,0 +1,51 @@
+// This defines a base target-configuration for native UEFI systems. The UEFI specification has
+// quite detailed sections on the ABI of all the supported target architectures. In almost all
+// cases it simply follows what Microsoft Windows does. Hence, whenever in doubt, see the MSDN
+// documentation.
+// UEFI uses COFF/PE32+ format for binaries. All binaries must be statically linked. No dynamic
+// linker is supported. As native to COFF, binaries are position-dependent, but will be relocated
+// by the loader if the pre-chosen memory location is already in use.
+// UEFI forbids running code on anything but the boot-CPU. No interrupts are allowed other than
+// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
+// code runs in the same environment, no process separation is supported.
+
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let mut base = super::msvc_base::opts();
+
+ base.add_pre_link_args(
+ LinkerFlavor::Msvc,
+ &[
+ // Non-standard subsystems have no default entry-point in PE+ files. We have to define
+ // one. "efi_main" seems to be a common choice amongst other implementations and the
+ // spec.
+ "/entry:efi_main",
+ // COFF images have a "Subsystem" field in their header, which defines what kind of
+ // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION,
+ // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION,
+ // which is very likely the most common option. Individual projects can override this
+ // with custom linker flags.
+ // The subsystem-type only has minor effects on the application. It defines the memory
+ // regions the application is loaded into (runtime-drivers need to be put into
+ // reserved areas), as well as whether a return from the entry-point is treated as
+ // exit (default for applications).
+ "/subsystem:efi_application",
+ ],
+ );
+
+ TargetOptions {
+ os: "uefi".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Link),
+ disable_redzone: true,
+ exe_suffix: ".efi".into(),
+ allows_weak_linkage: false,
+ panic_strategy: PanicStrategy::Abort,
+ // LLVM does not emit inline assembly because the LLVM target does not get considered as…
+ // "Windows".
+ stack_probes: StackProbeType::Call,
+ singlethread: true,
+ linker: Some("rust-lld".into()),
+ ..base
+ }
+}
diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs
new file mode 100644
index 000000000..aa4784b63
--- /dev/null
+++ b/compiler/rustc_target/src/spec/vxworks_base.rs
@@ -0,0 +1,21 @@
+use crate::spec::{cvs, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ TargetOptions {
+ os: "vxworks".into(),
+ env: "gnu".into(),
+ vendor: "wrs".into(),
+ linker: Some("wr-c++".into()),
+ exe_suffix: ".vxe".into(),
+ dynamic_linking: true,
+ families: cvs!["unix"],
+ has_rpath: true,
+ has_thread_local: true,
+ crt_static_default: true,
+ crt_static_respected: true,
+ crt_static_allows_dylibs: true,
+ // VxWorks needs to implement this to support profiling
+ mcount: "_mcount".into(),
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
new file mode 100644
index 000000000..c7e7d2210
--- /dev/null
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
@@ -0,0 +1,34 @@
+use super::{cvs, wasm_base};
+use super::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ // Reset flags for non-Em flavors back to empty to satisfy sanity checking tests.
+ let pre_link_args = LinkArgs::new();
+ let post_link_args = TargetOptions::link_args(
+ LinkerFlavor::Em,
+ &["-sABORTING_MALLOC=0", "-Wl,--fatal-warnings"],
+ );
+
+ let opts = TargetOptions {
+ os: "emscripten".into(),
+ linker_flavor: LinkerFlavor::Em,
+ // emcc emits two files - a .js file to instantiate the wasm and supply platform
+ // functionality, and a .wasm file.
+ exe_suffix: ".js".into(),
+ linker: None,
+ pre_link_args,
+ post_link_args,
+ relocation_model: RelocModel::Pic,
+ panic_strategy: PanicStrategy::Unwind,
+ no_default_libraries: false,
+ families: cvs!["unix", "wasm"],
+ ..wasm_base::options()
+ };
+ Target {
+ llvm_target: "wasm32-unknown-emscripten".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-S128-ni:1:10:20".into(),
+ arch: "wasm32".into(),
+ options: opts,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
new file mode 100644
index 000000000..4e2927dd9
--- /dev/null
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
@@ -0,0 +1,64 @@
+//! A "bare wasm" target representing a WebAssembly output that makes zero
+//! assumptions about its environment.
+//!
+//! The `wasm32-unknown-unknown` target is intended to encapsulate use cases
+//! that do not rely on any imported functionality. The binaries generated are
+//! entirely self-contained by default when using the standard library. Although
+//! the standard library is available, most of it returns an error immediately
+//! (e.g. trying to create a TCP stream or something like that).
+//!
+//! This target is more or less managed by the Rust and WebAssembly Working
+//! Group nowadays at <https://github.com/rustwasm>.
+
+use super::wasm_base;
+use super::{LinkerFlavor, LldFlavor, Target};
+use crate::spec::abi::Abi;
+
+pub fn target() -> Target {
+ let mut options = wasm_base::options();
+ options.os = "unknown".into();
+ options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
+
+ // This is a default for backwards-compatibility with the original
+ // definition of this target oh-so-long-ago. Once the "wasm" ABI is
+ // stable and the wasm-bindgen project has switched to using it then there's
+ // no need for this and it can be removed.
+ //
+ // Currently this is the reason that this target's ABI is mismatched with
+ // clang's ABI. This means that, in the limit, you can't merge C and Rust
+ // code on this target due to this ABI mismatch.
+ options.default_adjusted_cabi = Some(Abi::Wasm);
+
+ options.add_pre_link_args(
+ LinkerFlavor::Lld(LldFlavor::Wasm),
+ &[
+ // For now this target just never has an entry symbol no matter the output
+ // type, so unconditionally pass this.
+ "--no-entry",
+ // Rust really needs a way for users to specify exports and imports in
+ // the source code. --export-dynamic isn't the right tool for this job,
+ // however it does have the side effect of automatically exporting a lot
+ // of symbols, which approximates what people want when compiling for
+ // wasm32-unknown-unknown expect, so use it for now.
+ "--export-dynamic",
+ ],
+ );
+ options.add_pre_link_args(
+ LinkerFlavor::Gcc,
+ &[
+ // Make sure clang uses LLD as its linker and is configured appropriately
+ // otherwise
+ "--target=wasm32-unknown-unknown",
+ "-Wl,--no-entry",
+ "-Wl,--export-dynamic",
+ ],
+ );
+
+ Target {
+ llvm_target: "wasm32-unknown-unknown".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(),
+ arch: "wasm32".into(),
+ options,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs
new file mode 100644
index 000000000..280457d68
--- /dev/null
+++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs
@@ -0,0 +1,112 @@
+//! The `wasm32-wasi` target is a new and still (as of April 2019) an
+//! experimental target. The definition in this file is likely to be tweaked
+//! over time and shouldn't be relied on too much.
+//!
+//! The `wasi` target is a proposal to define a standardized set of syscalls
+//! that WebAssembly files can interoperate with. This set of syscalls is
+//! intended to empower WebAssembly binaries with native capabilities such as
+//! filesystem access, network access, etc.
+//!
+//! You can see more about the proposal at <https://wasi.dev>.
+//!
+//! The Rust target definition here is interesting in a few ways. We want to
+//! serve two use cases here with this target:
+//!
+//! * First, we want Rust usage of the target to be as hassle-free as possible,
+//! ideally avoiding the need to configure and install a local wasm32-wasi
+//! toolchain.
+//!
+//! * Second, one of the primary use cases of LLVM's new wasm backend and the
+//! wasm support in LLD is that any compiled language can interoperate with
+//! any other. To that the `wasm32-wasi` target is the first with a viable C
+//! standard library and sysroot common definition, so we want Rust and C/C++
+//! code to interoperate when compiled to `wasm32-unknown-unknown`.
+//!
+//! You'll note, however, that the two goals above are somewhat at odds with one
+//! another. To attempt to solve both use cases in one go we define a target
+//! that (ab)uses the `crt-static` target feature to indicate which one you're
+//! in.
+//!
+//! ## No interop with C required
+//!
+//! By default the `crt-static` target feature is enabled, and when enabled
+//! this means that the bundled version of `libc.a` found in `liblibc.rlib`
+//! is used. This isn't intended really for interoperation with a C because it
+//! may be the case that Rust's bundled C library is incompatible with a
+//! foreign-compiled C library. In this use case, though, we use `rust-lld` and
+//! some copied crt startup object files to ensure that you can download the
+//! wasi target for Rust and you're off to the races, no further configuration
+//! necessary.
+//!
+//! All in all, by default, no external dependencies are required. You can
+//! compile `wasm32-wasi` binaries straight out of the box. You can't, however,
+//! reliably interoperate with C code in this mode (yet).
+//!
+//! ## Interop with C required
+//!
+//! For the second goal we repurpose the `target-feature` flag, meaning that
+//! you'll need to do a few things to have C/Rust code interoperate.
+//!
+//! 1. All Rust code needs to be compiled with `-C target-feature=-crt-static`,
+//! indicating that the bundled C standard library in the Rust sysroot will
+//! not be used.
+//!
+//! 2. If you're using rustc to build a linked artifact then you'll need to
+//! specify `-C linker` to a `clang` binary that supports
+//! `wasm32-wasi` and is configured with the `wasm32-wasi` sysroot. This
+//! will cause Rust code to be linked against the libc.a that the specified
+//! `clang` provides.
+//!
+//! 3. If you're building a staticlib and integrating Rust code elsewhere, then
+//! compiling with `-C target-feature=-crt-static` is all you need to do.
+//!
+//! You can configure the linker via Cargo using the
+//! `CARGO_TARGET_WASM32_WASI_LINKER` env var. Be sure to also set
+//! `CC_wasm32-wasi` if any crates in the dependency graph are using the `cc`
+//! crate.
+//!
+//! ## Remember, this is all in flux
+//!
+//! The wasi target is **very** new in its specification. It's likely going to
+//! be a long effort to get it standardized and stable. We'll be following it as
+//! best we can with this target. Don't start relying on too much here unless
+//! you know what you're getting in to!
+
+use super::wasm_base;
+use super::{crt_objects, LinkerFlavor, LldFlavor, Target};
+
+pub fn target() -> Target {
+ let mut options = wasm_base::options();
+
+ options.os = "wasi".into();
+ options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
+ options.add_pre_link_args(LinkerFlavor::Gcc, &["--target=wasm32-wasi"]);
+
+ options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback();
+ options.post_link_objects_fallback = crt_objects::post_wasi_fallback();
+
+ // Right now this is a bit of a workaround but we're currently saying that
+ // the target by default has a static crt which we're taking as a signal
+ // for "use the bundled crt". If that's turned off then the system's crt
+ // will be used, but this means that default usage of this target doesn't
+ // need an external compiler but it's still interoperable with an external
+ // compiler if configured correctly.
+ options.crt_static_default = true;
+ options.crt_static_respected = true;
+
+ // Allow `+crt-static` to create a "cdylib" output which is just a wasm file
+ // without a main function.
+ options.crt_static_allows_dylibs = true;
+
+ // WASI's `sys::args::init` function ignores its arguments; instead,
+ // `args::args()` makes the WASI API calls itself.
+ options.main_needs_argc_argv = false;
+
+ Target {
+ llvm_target: "wasm32-wasi".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(),
+ arch: "wasm32".into(),
+ options,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
new file mode 100644
index 000000000..5211f7707
--- /dev/null
+++ b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
@@ -0,0 +1,49 @@
+//! A "bare wasm" target representing a WebAssembly output that makes zero
+//! assumptions about its environment.
+//!
+//! The `wasm64-unknown-unknown` target is intended to encapsulate use cases
+//! that do not rely on any imported functionality. The binaries generated are
+//! entirely self-contained by default when using the standard library. Although
+//! the standard library is available, most of it returns an error immediately
+//! (e.g. trying to create a TCP stream or something like that).
+
+use super::wasm_base;
+use super::{LinkerFlavor, LldFlavor, Target};
+
+pub fn target() -> Target {
+ let mut options = wasm_base::options();
+ options.os = "unknown".into();
+ options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
+
+ options.add_pre_link_args(
+ LinkerFlavor::Lld(LldFlavor::Wasm),
+ &[
+ // For now this target just never has an entry symbol no matter the output
+ // type, so unconditionally pass this.
+ "--no-entry",
+ "-mwasm64",
+ ],
+ );
+ options.add_pre_link_args(
+ LinkerFlavor::Gcc,
+ &[
+ // Make sure clang uses LLD as its linker and is configured appropriately
+ // otherwise
+ "--target=wasm64-unknown-unknown",
+ "-Wl,--no-entry",
+ ],
+ );
+
+ // Any engine that implements wasm64 will surely implement the rest of these
+ // features since they were all merged into the official spec by the time
+ // wasm64 was designed.
+ options.features = "+bulk-memory,+mutable-globals,+sign-ext,+nontrapping-fptoint".into();
+
+ Target {
+ llvm_target: "wasm64-unknown-unknown".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(),
+ arch: "wasm64".into(),
+ options,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs
new file mode 100644
index 000000000..9216d3e7b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/wasm_base.rs
@@ -0,0 +1,129 @@
+use super::crt_objects::CrtObjectsFallback;
+use super::{cvs, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
+
+pub fn options() -> TargetOptions {
+ macro_rules! args {
+ ($prefix:literal) => {
+ &[
+ // By default LLD only gives us one page of stack (64k) which is a
+ // little small. Default to a larger stack closer to other PC platforms
+ // (1MB) and users can always inject their own link-args to override this.
+ concat!($prefix, "-z"),
+ concat!($prefix, "stack-size=1048576"),
+ // By default LLD's memory layout is:
+ //
+ // 1. First, a blank page
+ // 2. Next, all static data
+ // 3. Finally, the main stack (which grows down)
+ //
+ // This has the unfortunate consequence that on stack overflows you
+ // corrupt static data and can cause some exceedingly weird bugs. To
+ // help detect this a little sooner we instead request that the stack is
+ // placed before static data.
+ //
+ // This means that we'll generate slightly larger binaries as references
+ // to static data will take more bytes in the ULEB128 encoding, but
+ // stack overflow will be guaranteed to trap as it underflows instead of
+ // corrupting static data.
+ concat!($prefix, "--stack-first"),
+ // FIXME we probably shouldn't pass this but instead pass an explicit list
+ // of symbols we'll allow to be undefined. We don't currently have a
+ // mechanism of knowing, however, which symbols are intended to be imported
+ // from the environment and which are intended to be imported from other
+ // objects linked elsewhere. This is a coarse approximation but is sure to
+ // hide some bugs and frustrate someone at some point, so we should ideally
+ // work towards a world where we can explicitly list symbols that are
+ // supposed to be imported and have all other symbols generate errors if
+ // they remain undefined.
+ concat!($prefix, "--allow-undefined"),
+ // Rust code should never have warnings, and warnings are often
+ // indicative of bugs, let's prevent them.
+ concat!($prefix, "--fatal-warnings"),
+ // LLD only implements C++-like demangling, which doesn't match our own
+ // mangling scheme. Tell LLD to not demangle anything and leave it up to
+ // us to demangle these symbols later. Currently rustc does not perform
+ // further demangling, but tools like twiggy and wasm-bindgen are intended
+ // to do so.
+ concat!($prefix, "--no-demangle"),
+ ]
+ };
+ }
+
+ let mut pre_link_args = TargetOptions::link_args(LinkerFlavor::Lld(LldFlavor::Wasm), args!(""));
+ super::add_link_args(&mut pre_link_args, LinkerFlavor::Gcc, args!("-Wl,"));
+
+ TargetOptions {
+ is_like_wasm: true,
+ families: cvs!["wasm"],
+
+ // we allow dynamic linking, but only cdylibs. Basically we allow a
+ // final library artifact that exports some symbols (a wasm module) but
+ // we don't allow intermediate `dylib` crate types
+ dynamic_linking: true,
+ only_cdylib: true,
+
+ // relatively self-explanatory!
+ exe_suffix: ".wasm".into(),
+ dll_prefix: "".into(),
+ dll_suffix: ".wasm".into(),
+ eh_frame_header: false,
+
+ max_atomic_width: Some(64),
+
+ // Unwinding doesn't work right now, so the whole target unconditionally
+ // defaults to panic=abort. Note that this is guaranteed to change in
+ // the future once unwinding is implemented. Don't rely on this as we're
+ // basically guaranteed to change it once WebAssembly supports
+ // exceptions.
+ panic_strategy: PanicStrategy::Abort,
+
+ // Wasm doesn't have atomics yet, so tell LLVM that we're in a single
+ // threaded model which will legalize atomics to normal operations.
+ singlethread: true,
+
+ // no dynamic linking, no need for default visibility!
+ default_hidden_visibility: true,
+
+ // Symbol visibility takes care of this for the WebAssembly.
+ // Additionally the only known linker, LLD, doesn't support the script
+ // arguments just yet
+ limit_rdylib_exports: false,
+
+ // we use the LLD shipped with the Rust toolchain by default
+ linker: Some("rust-lld".into()),
+ lld_flavor: LldFlavor::Wasm,
+ linker_is_gnu: false,
+
+ pre_link_args,
+
+ crt_objects_fallback: Some(CrtObjectsFallback::Wasm),
+
+ // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
+ // PIC code is implemented this has quite a drastic effect if it stays
+ // at the default, `pic`. In an effort to keep wasm binaries as minimal
+ // as possible we're defaulting to `static` for now, but the hope is
+ // that eventually we can ship a `pic`-compatible standard library which
+ // works with `static` as well (or works with some method of generating
+ // non-relative calls and such later on).
+ relocation_model: RelocModel::Static,
+
+ // When the atomics feature is activated then these two keys matter,
+ // otherwise they're basically ignored by the standard library. In this
+ // mode, however, the `#[thread_local]` attribute works (i.e.
+ // `has_thread_local`) and we need to get it to work by specifying
+ // `local-exec` as that's all that's implemented in LLVM today for wasm.
+ has_thread_local: true,
+ tls_model: TlsModel::LocalExec,
+
+ // gdb scripts don't work on wasm blobs
+ emit_debug_gdb_scripts: false,
+
+ // There's more discussion of this at
+ // https://bugs.llvm.org/show_bug.cgi?id=52442 but the general result is
+ // that this isn't useful for wasm and has tricky issues with
+ // representation, so this is disabled.
+ generate_arange_section: false,
+
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs
new file mode 100644
index 000000000..90e0af3e3
--- /dev/null
+++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs
@@ -0,0 +1,91 @@
+use crate::spec::crt_objects::{self, CrtObjectsFallback};
+use crate::spec::{cvs, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let mut pre_link_args = TargetOptions::link_args(
+ LinkerFlavor::Ld,
+ &[
+ // Enable ASLR
+ "--dynamicbase",
+ // ASLR will rebase it anyway so leaving that option enabled only leads to confusion
+ "--disable-auto-image-base",
+ ],
+ );
+ super::add_link_args(
+ &mut pre_link_args,
+ LinkerFlavor::Gcc,
+ &[
+ // Tell GCC to avoid linker plugins, because we are not bundling
+ // them with Windows installer, and Rust does its own LTO anyways.
+ "-fno-use-linker-plugin",
+ "-Wl,--dynamicbase",
+ "-Wl,--disable-auto-image-base",
+ ],
+ );
+
+ // Order of `late_link_args*` was found through trial and error to work with various
+ // mingw-w64 versions (not tested on the CI). It's expected to change from time to time.
+ let mingw_libs = &[
+ "-lmsvcrt",
+ "-lmingwex",
+ "-lmingw32",
+ "-lgcc", // alas, mingw* libraries above depend on libgcc
+ // mingw's msvcrt is a weird hybrid import library and static library.
+ // And it seems that the linker fails to use import symbols from msvcrt
+ // that are required from functions in msvcrt in certain cases. For example
+ // `_fmode` that is used by an implementation of `__p__fmode` in x86_64.
+ // The library is purposely listed twice to fix that.
+ //
+ // See https://github.com/rust-lang/rust/pull/47483 for some more details.
+ "-lmsvcrt",
+ "-luser32",
+ "-lkernel32",
+ ];
+ let mut late_link_args = TargetOptions::link_args(LinkerFlavor::Ld, mingw_libs);
+ super::add_link_args(&mut late_link_args, LinkerFlavor::Gcc, mingw_libs);
+ // If any of our crates are dynamically linked then we need to use
+ // the shared libgcc_s-dw2-1.dll. This is required to support
+ // unwinding across DLL boundaries.
+ let dynamic_unwind_libs = &["-lgcc_s"];
+ let mut late_link_args_dynamic =
+ TargetOptions::link_args(LinkerFlavor::Ld, dynamic_unwind_libs);
+ super::add_link_args(&mut late_link_args_dynamic, LinkerFlavor::Gcc, dynamic_unwind_libs);
+ // If all of our crates are statically linked then we can get away
+ // with statically linking the libgcc unwinding code. This allows
+ // binaries to be redistributed without the libgcc_s-dw2-1.dll
+ // dependency, but unfortunately break unwinding across DLL
+ // boundaries when unwinding across FFI boundaries.
+ let static_unwind_libs = &["-lgcc_eh", "-l:libpthread.a"];
+ let mut late_link_args_static = TargetOptions::link_args(LinkerFlavor::Ld, static_unwind_libs);
+ super::add_link_args(&mut late_link_args_static, LinkerFlavor::Gcc, static_unwind_libs);
+
+ TargetOptions {
+ os: "windows".into(),
+ env: "gnu".into(),
+ vendor: "pc".into(),
+ // FIXME(#13846) this should be enabled for windows
+ function_sections: false,
+ linker: Some("gcc".into()),
+ dynamic_linking: true,
+ dll_prefix: "".into(),
+ dll_suffix: ".dll".into(),
+ exe_suffix: ".exe".into(),
+ families: cvs!["windows"],
+ is_like_windows: true,
+ allows_weak_linkage: false,
+ pre_link_args,
+ pre_link_objects: crt_objects::pre_mingw(),
+ post_link_objects: crt_objects::post_mingw(),
+ pre_link_objects_fallback: crt_objects::pre_mingw_fallback(),
+ post_link_objects_fallback: crt_objects::post_mingw_fallback(),
+ crt_objects_fallback: Some(CrtObjectsFallback::Mingw),
+ late_link_args,
+ late_link_args_dynamic,
+ late_link_args_static,
+ abi_return_struct_as_int: true,
+ emit_debug_gdb_scripts: false,
+ requires_uwtable: true,
+ eh_frame_header: false,
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
new file mode 100644
index 000000000..bae007dc9
--- /dev/null
+++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
@@ -0,0 +1,40 @@
+use crate::spec::{cvs, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ // We cannot use `-nodefaultlibs` because compiler-rt has to be passed
+ // as a path since it's not added to linker search path by the default.
+ // There were attemts to make it behave like libgcc (so one can just use -l<name>)
+ // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440
+ let pre_link_args =
+ TargetOptions::link_args(LinkerFlavor::Gcc, &["-nolibc", "--unwindlib=none"]);
+ // Order of `late_link_args*` does not matter with LLD.
+ let late_link_args = TargetOptions::link_args(
+ LinkerFlavor::Gcc,
+ &["-lmingw32", "-lmingwex", "-lmsvcrt", "-lkernel32", "-luser32"],
+ );
+
+ TargetOptions {
+ os: "windows".into(),
+ env: "gnu".into(),
+ vendor: "pc".into(),
+ abi: "llvm".into(),
+ linker: Some("clang".into()),
+ dynamic_linking: true,
+ dll_prefix: "".into(),
+ dll_suffix: ".dll".into(),
+ exe_suffix: ".exe".into(),
+ families: cvs!["windows"],
+ is_like_windows: true,
+ allows_weak_linkage: false,
+ pre_link_args,
+ late_link_args,
+ abi_return_struct_as_int: true,
+ emit_debug_gdb_scripts: false,
+ requires_uwtable: true,
+ eh_frame_header: false,
+ no_default_libraries: false,
+ has_thread_local: true,
+
+ ..Default::default()
+ }
+}
diff --git a/compiler/rustc_target/src/spec/windows_msvc_base.rs b/compiler/rustc_target/src/spec/windows_msvc_base.rs
new file mode 100644
index 000000000..21062c337
--- /dev/null
+++ b/compiler/rustc_target/src/spec/windows_msvc_base.rs
@@ -0,0 +1,34 @@
+use crate::spec::{cvs, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let base = super::msvc_base::opts();
+
+ TargetOptions {
+ os: "windows".into(),
+ env: "msvc".into(),
+ vendor: "pc".into(),
+ dynamic_linking: true,
+ dll_prefix: "".into(),
+ dll_suffix: ".dll".into(),
+ exe_suffix: ".exe".into(),
+ staticlib_prefix: "".into(),
+ staticlib_suffix: ".lib".into(),
+ families: cvs!["windows"],
+ crt_static_allows_dylibs: true,
+ crt_static_respected: true,
+ requires_uwtable: true,
+ // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
+ // as there's been trouble in the past of linking the C++ standard
+ // library required by LLVM. This likely needs to happen one day, but
+ // in general Windows is also a more controlled environment than
+ // Unix, so it's not necessarily as critical that this be implemented.
+ //
+ // Note that there are also some licensing worries about statically
+ // linking some libraries which require a specific agreement, so it may
+ // not ever be possible for us to pass this flag.
+ no_default_libraries: false,
+ has_thread_local: true,
+
+ ..base
+ }
+}
diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
new file mode 100644
index 000000000..fa69b919c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
@@ -0,0 +1,34 @@
+use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let base = super::windows_gnu_base::opts();
+
+ // FIXME: This should be updated for the exception machinery changes from #67502
+ // and inherit from `windows_gnu_base`, at least partially.
+ let mingw_libs = &[
+ "-lwinstorecompat",
+ "-lruntimeobject",
+ "-lsynchronization",
+ "-lvcruntime140_app",
+ "-lucrt",
+ "-lwindowsapp",
+ "-lmingwex",
+ "-lmingw32",
+ ];
+ let mut late_link_args = TargetOptions::link_args(LinkerFlavor::Ld, mingw_libs);
+ super::add_link_args(&mut late_link_args, LinkerFlavor::Gcc, mingw_libs);
+ // Reset the flags back to empty until the FIXME above is addressed.
+ let late_link_args_dynamic = LinkArgs::new();
+ let late_link_args_static = LinkArgs::new();
+
+ TargetOptions {
+ abi: "uwp".into(),
+ vendor: "uwp".into(),
+ limit_rdylib_exports: false,
+ late_link_args,
+ late_link_args_dynamic,
+ late_link_args_static,
+
+ ..base
+ }
+}
diff --git a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs
new file mode 100644
index 000000000..f2573fc2d
--- /dev/null
+++ b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs
@@ -0,0 +1,11 @@
+use crate::spec::{LinkerFlavor, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+ let mut opts = super::windows_msvc_base::opts();
+
+ opts.abi = "uwp".into();
+ opts.vendor = "uwp".into();
+ opts.add_pre_link_args(LinkerFlavor::Msvc, &["/APPCONTAINER", "mincore.lib"]);
+
+ opts
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs
new file mode 100644
index 000000000..dbd26899c
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs
@@ -0,0 +1,30 @@
+use crate::spec::TargetOptions;
+use crate::spec::{FramePointer, LinkerFlavor, SanitizerSet, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::apple_base::opts("macos");
+ base.cpu = "core2".into();
+ base.max_atomic_width = Some(128); // core2 support cmpxchg16b
+ base.frame_pointer = FramePointer::Always;
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-arch", "x86_64"]);
+ base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove());
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.supported_sanitizers =
+ SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::THREAD;
+
+ // Clang automatically chooses a more specific target based on
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // correctly, we do too.
+ let arch = "x86_64";
+ let llvm_target = super::apple_base::macos_llvm_target(&arch);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: arch.into(),
+ options: TargetOptions { mcount: "\u{1}mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs
new file mode 100644
index 000000000..5e64ed0cf
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs
@@ -0,0 +1,21 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("ios", Arch::X86_64);
+ let llvm_target = super::apple_base::ios_sim_llvm_target("x86_64");
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(64),
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ stack_probes: StackProbeType::Call,
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
new file mode 100644
index 000000000..2122bcd37
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
@@ -0,0 +1,23 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let llvm_target = "x86_64-apple-ios13.0-macabi";
+
+ let mut base = opts("ios", Arch::X86_64_macabi);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-target", llvm_target]);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(64),
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ stack_probes: StackProbeType::Call,
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs
new file mode 100644
index 000000000..a848c5a0a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs
@@ -0,0 +1,18 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("tvos", Arch::X86_64);
+ Target {
+ llvm_target: "x86_64-apple-tvos".into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".into(),
+ arch: "x86_64".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(64),
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ stack_probes: StackProbeType::Call,
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs
new file mode 100644
index 000000000..4dff3c2f2
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs
@@ -0,0 +1,35 @@
+use super::apple_sdk_base::{opts, Arch};
+use crate::spec::{StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = opts("watchos", Arch::X86_64);
+
+ let arch = "x86_64";
+ let llvm_target = super::apple_base::watchos_sim_llvm_target(arch);
+
+ Target {
+ llvm_target: llvm_target.into(),
+ pointer_width: 64,
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: TargetOptions {
+ max_atomic_width: Some(64),
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ stack_probes: StackProbeType::Call,
+ forces_embed_bitcode: true,
+ // Taken from a clang build on Xcode 11.4.1.
+ // These arguments are not actually invoked - they just have
+ // to look right to pass App Store validation.
+ bitcode_llvm_cmdline: "-triple\0\
+ x86_64-apple-watchos5.0-simulator\0\
+ -emit-obj\0\
+ -disable-llvm-passes\0\
+ -target-abi\0\
+ darwinpcs\0\
+ -Os\0"
+ .into(),
+ ..base
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
new file mode 100644
index 000000000..9d597ea2e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
@@ -0,0 +1,84 @@
+use std::borrow::Cow;
+
+use crate::spec::cvs;
+
+use super::{LinkerFlavor, LldFlavor, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let pre_link_args = TargetOptions::link_args(
+ LinkerFlavor::Ld,
+ &[
+ "-e",
+ "elf_entry",
+ "-Bstatic",
+ "--gc-sections",
+ "-z",
+ "text",
+ "-z",
+ "norelro",
+ "--no-undefined",
+ "--error-unresolved-symbols",
+ "--no-undefined-version",
+ "-Bsymbolic",
+ "--export-dynamic",
+ // The following symbols are needed by libunwind, which is linked after
+ // libstd. Make sure they're included in the link.
+ "-u",
+ "__rust_abort",
+ "-u",
+ "__rust_c_alloc",
+ "-u",
+ "__rust_c_dealloc",
+ "-u",
+ "__rust_print_err",
+ "-u",
+ "__rust_rwlock_rdlock",
+ "-u",
+ "__rust_rwlock_unlock",
+ "-u",
+ "__rust_rwlock_wrlock",
+ ],
+ );
+
+ const EXPORT_SYMBOLS: &[&str] = &[
+ "sgx_entry",
+ "HEAP_BASE",
+ "HEAP_SIZE",
+ "RELA",
+ "RELACOUNT",
+ "ENCLAVE_SIZE",
+ "CFGDATA_BASE",
+ "DEBUG",
+ "EH_FRM_HDR_OFFSET",
+ "EH_FRM_HDR_LEN",
+ "EH_FRM_OFFSET",
+ "EH_FRM_LEN",
+ "TEXT_BASE",
+ "TEXT_SIZE",
+ ];
+ let opts = TargetOptions {
+ os: "unknown".into(),
+ env: "sgx".into(),
+ vendor: "fortanix".into(),
+ abi: "fortanix".into(),
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ max_atomic_width: Some(64),
+ cpu: "x86-64".into(),
+ features: "+rdrnd,+rdseed,+lvi-cfi,+lvi-load-hardening".into(),
+ llvm_args: cvs!["--x86-experimental-lvi-inline-asm-hardening"],
+ position_independent_executables: true,
+ pre_link_args,
+ override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(Cow::from).collect()),
+ relax_elf_relocations: true,
+ ..Default::default()
+ };
+ Target {
+ llvm_target: "x86_64-elf".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: opts,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs
new file mode 100644
index 000000000..4f88fc350
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs
@@ -0,0 +1,19 @@
+use crate::spec::{SanitizerSet, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::fuchsia_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
+
+ Target {
+ llvm_target: "x86_64-fuchsia".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs
new file mode 100644
index 000000000..6d19cf265
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs
@@ -0,0 +1,21 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::android_base::opts();
+ base.cpu = "x86-64".into();
+ // https://developer.android.com/ndk/guides/abis.html#86-64
+ base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "x86_64-linux-android".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: TargetOptions { supported_sanitizers: SanitizerSet::ADDRESS, ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
new file mode 100644
index 000000000..0550b221f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
@@ -0,0 +1,21 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::solaris_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.cpu = "x86-64".into();
+ base.vendor = "pc".into();
+ base.max_atomic_width = Some(64);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
+
+ Target {
+ llvm_target: "x86_64-pc-solaris".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs
new file mode 100644
index 000000000..59a8cffca
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::windows_gnu_base::opts();
+ base.cpu = "x86-64".into();
+ // Use high-entropy 64 bit address space for ASLR
+ base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pep", "--high-entropy-va"]);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-Wl,--high-entropy-va"]);
+ base.max_atomic_width = Some(64);
+ base.linker = Some("x86_64-w64-mingw32-gcc".into());
+
+ Target {
+ llvm_target: "x86_64-pc-windows-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
new file mode 100644
index 000000000..d3909b389
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
@@ -0,0 +1,18 @@
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::windows_gnullvm_base::opts();
+ base.cpu = "x86-64".into();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.max_atomic_width = Some(64);
+ base.linker = Some("x86_64-w64-mingw32-clang".into());
+
+ Target {
+ llvm_target: "x86_64-pc-windows-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs
new file mode 100644
index 000000000..081806aa6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs
@@ -0,0 +1,16 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::windows_msvc_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "x86_64-pc-windows-msvc".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs
new file mode 100644
index 000000000..cbe87589a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::solaris_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ base.cpu = "x86-64".into();
+ base.vendor = "sun".into();
+ base.max_atomic_width = Some(64);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "x86_64-pc-solaris".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs
new file mode 100644
index 000000000..746f64781
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs
@@ -0,0 +1,19 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::dragonfly_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "x86_64-unknown-dragonfly".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
new file mode 100644
index 000000000..b30784ed6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
@@ -0,0 +1,21 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::freebsd_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.supported_sanitizers =
+ SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::MEMORY | SanitizerSet::THREAD;
+
+ Target {
+ llvm_target: "x86_64-unknown-freebsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs
new file mode 100644
index 000000000..d6d033629
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs
@@ -0,0 +1,21 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::haiku_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ // This option is required to build executables on Haiku x86_64
+ base.position_independent_executables = true;
+
+ Target {
+ llvm_target: "x86_64-unknown-haiku".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs
new file mode 100644
index 000000000..d31530161
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs
@@ -0,0 +1,19 @@
+use crate::spec::{StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::hermit_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.features = "+rdrnd,+rdseed".into();
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "x86_64-unknown-hermit".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
new file mode 100644
index 000000000..9f19c3a2b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, Target};
+
+pub fn target() -> Target {
+ let mut base = super::illumos_base::opts();
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-std=c99"]);
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
+
+ Target {
+ // LLVM does not currently have a separate illumos target,
+ // so we still pass Solaris to it
+ llvm_target: "x86_64-pc-solaris".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs
new file mode 100644
index 000000000..78189a0c0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs
@@ -0,0 +1,19 @@
+use crate::spec::{PanicStrategy, Target};
+
+pub fn target() -> Target {
+ let mut base = super::l4re_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.crt_static_allows_dylibs = false;
+ base.dynamic_linking = false;
+ base.panic_strategy = PanicStrategy::Abort;
+
+ Target {
+ llvm_target: "x86_64-unknown-l4re-uclibc".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
new file mode 100644
index 000000000..956be0353
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
@@ -0,0 +1,25 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.static_position_independent_executables = true;
+ base.supported_sanitizers = SanitizerSet::ADDRESS
+ | SanitizerSet::CFI
+ | SanitizerSet::LEAK
+ | SanitizerSet::MEMORY
+ | SanitizerSet::THREAD;
+
+ Target {
+ llvm_target: "x86_64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs
new file mode 100644
index 000000000..140882747
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs
@@ -0,0 +1,25 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::linux_gnu_base::opts();
+ base.cpu = "x86-64".into();
+ base.abi = "x32".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-mx32"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.has_thread_local = false;
+ // BUG(GabrielMajeri): disabling the PLT on x86_64 Linux with x32 ABI
+ // breaks code gen. See LLVM bug 36743
+ base.needs_plt = true;
+
+ Target {
+ llvm_target: "x86_64-unknown-linux-gnux32".into(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+ i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs
new file mode 100644
index 000000000..87e7784d1
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs
@@ -0,0 +1,25 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.static_position_independent_executables = true;
+ base.supported_sanitizers = SanitizerSet::ADDRESS
+ | SanitizerSet::CFI
+ | SanitizerSet::LEAK
+ | SanitizerSet::MEMORY
+ | SanitizerSet::THREAD;
+
+ Target {
+ llvm_target: "x86_64-unknown-linux-musl".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
new file mode 100644
index 000000000..d3a67619a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
@@ -0,0 +1,24 @@
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::netbsd_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.supported_sanitizers = SanitizerSet::ADDRESS
+ | SanitizerSet::CFI
+ | SanitizerSet::LEAK
+ | SanitizerSet::MEMORY
+ | SanitizerSet::THREAD;
+
+ Target {
+ llvm_target: "x86_64-unknown-netbsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: TargetOptions { mcount: "__mcount".into(), ..base },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
new file mode 100644
index 000000000..809fd642d
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
@@ -0,0 +1,40 @@
+// Generic x86-64 target for bare-metal code - Floating point disabled
+//
+// Can be used in conjunction with the `target-feature` and
+// `target-cpu` compiler flags to opt-in more hardware-specific
+// features.
+
+use super::{
+ CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType,
+ Target, TargetOptions,
+};
+
+pub fn target() -> Target {
+ let opts = TargetOptions {
+ cpu: "x86-64".into(),
+ max_atomic_width: Some(64),
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ stack_probes: StackProbeType::Call,
+ position_independent_executables: true,
+ static_position_independent_executables: true,
+ relro_level: RelroLevel::Full,
+ relocation_model: RelocModel::Pic,
+ linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+ linker: Some("rust-lld".into()),
+ features:
+ "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
+ .into(),
+ disable_redzone: true,
+ panic_strategy: PanicStrategy::Abort,
+ code_model: Some(CodeModel::Kernel),
+ ..Default::default()
+ };
+ Target {
+ llvm_target: "x86_64-unknown-none-elf".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: opts,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs
new file mode 100644
index 000000000..593345a5f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs
@@ -0,0 +1,28 @@
+// This defines the amd64 target for the Linux Kernel. See the linux-kernel-base module for
+// generic Linux kernel options.
+
+use crate::spec::{CodeModel, LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::linux_kernel_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.features =
+ "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float".into();
+ base.code_model = Some(CodeModel::Kernel);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+
+ Target {
+ // FIXME: Some dispute, the linux-on-clang folks think this should use
+ // "Linux". We disagree because running *on* Linux is nothing like
+ // running *as" linux, and historically the "os" component as has always
+ // been used to mean the "on" part.
+ llvm_target: "x86_64-unknown-none-elf".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
new file mode 100644
index 000000000..f50c6bcee
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
@@ -0,0 +1,19 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::openbsd_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "x86_64-unknown-openbsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs
new file mode 100644
index 000000000..668ae9054
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs
@@ -0,0 +1,19 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::redox_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+
+ Target {
+ llvm_target: "x86_64-unknown-redox".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs
new file mode 100644
index 000000000..a7ae17839
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs
@@ -0,0 +1,36 @@
+// This defines the amd64 target for UEFI systems as described in the UEFI specification. See the
+// uefi-base module for generic UEFI options. On x86_64 systems (mostly called "x64" in the spec)
+// UEFI systems always run in long-mode, have the interrupt-controller pre-configured and force a
+// single-CPU execution.
+// The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with
+// LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features.
+
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::uefi_msvc_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+
+ // We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
+ // enable these CPU features explicitly before their first use, otherwise their instructions
+ // will trigger an exception. Rust does not inject any code that enables AVX/MMX/SSE
+ // instruction sets, so this must be done by the firmware. However, existing firmware is known
+ // to leave these uninitialized, thus triggering exceptions if we make use of them. Which is
+ // why we avoid them and instead use soft-floats. This is also what GRUB and friends did so
+ // far.
+ //
+ // If you initialize FP units yourself, you can override these flags with custom linker
+ // arguments, thus giving you access to full MMX/SSE acceleration.
+ base.features = "-mmx,-sse,+soft-float".into();
+
+ Target {
+ llvm_target: "x86_64-unknown-windows".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs
new file mode 100644
index 000000000..76d2013cf
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs
@@ -0,0 +1,19 @@
+use crate::spec::{LinkerFlavor, Target};
+
+pub fn target() -> Target {
+ let mut base = super::windows_uwp_gnu_base::opts();
+ base.cpu = "x86-64".into();
+ // Use high-entropy 64 bit address space for ASLR
+ base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pep", "--high-entropy-va"]);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-Wl,--high-entropy-va"]);
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "x86_64-pc-windows-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs
new file mode 100644
index 000000000..b2769350b
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs
@@ -0,0 +1,16 @@
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::windows_uwp_msvc_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+
+ Target {
+ llvm_target: "x86_64-pc-windows-msvc".into(),
+ pointer_width: 64,
+ data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs
new file mode 100644
index 000000000..129897495
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs
@@ -0,0 +1,20 @@
+use crate::spec::{LinkerFlavor, StackProbeType, Target};
+
+pub fn target() -> Target {
+ let mut base = super::vxworks_base::opts();
+ base.cpu = "x86-64".into();
+ base.max_atomic_width = Some(64);
+ base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
+ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
+ base.stack_probes = StackProbeType::Call;
+ base.disable_redzone = true;
+
+ Target {
+ llvm_target: "x86_64-unknown-linux-gnu".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
+ arch: "x86_64".into(),
+ options: base,
+ }
+}
diff --git a/compiler/rustc_target/src/tests.rs b/compiler/rustc_target/src/tests.rs
new file mode 100644
index 000000000..76375170d
--- /dev/null
+++ b/compiler/rustc_target/src/tests.rs
@@ -0,0 +1,57 @@
+use crate::spec::Target;
+
+#[test]
+fn report_unused_fields() {
+ let json = serde_json::from_str(
+ r#"
+ {
+ "arch": "powerpc64",
+ "data-layout": "e-m:e-i64:64-n32:64",
+ "llvm-target": "powerpc64le-elf",
+ "target-pointer-width": "64",
+ "code-mode": "foo"
+ }
+ "#,
+ )
+ .unwrap();
+ let warnings = Target::from_json(json).unwrap().1;
+ assert_eq!(warnings.warning_messages().len(), 1);
+ assert!(warnings.warning_messages().join("\n").contains("code-mode"));
+}
+
+#[test]
+fn report_incorrect_json_type() {
+ let json = serde_json::from_str(
+ r#"
+ {
+ "arch": "powerpc64",
+ "data-layout": "e-m:e-i64:64-n32:64",
+ "llvm-target": "powerpc64le-elf",
+ "target-pointer-width": "64",
+ "link-env-remove": "foo"
+ }
+ "#,
+ )
+ .unwrap();
+ let warnings = Target::from_json(json).unwrap().1;
+ assert_eq!(warnings.warning_messages().len(), 1);
+ assert!(warnings.warning_messages().join("\n").contains("link-env-remove"));
+}
+
+#[test]
+fn no_warnings_for_valid_target() {
+ let json = serde_json::from_str(
+ r#"
+ {
+ "arch": "powerpc64",
+ "data-layout": "e-m:e-i64:64-n32:64",
+ "llvm-target": "powerpc64le-elf",
+ "target-pointer-width": "64",
+ "link-env-remove": ["foo"]
+ }
+ "#,
+ )
+ .unwrap();
+ let warnings = Target::from_json(json).unwrap().1;
+ assert_eq!(warnings.warning_messages().len(), 0);
+}