]>
Commit | Line | Data |
---|---|---|
e1599b0c XL |
1 | // This defines the ia32 target for UEFI systems as described in the UEFI specification. See the |
2 | // uefi-base module for generic UEFI options. On ia32 systems | |
3 | // UEFI systems always run in protected-mode, have the interrupt-controller pre-configured and | |
4 | // force a single-CPU execution. | |
5 | // The cdecl ABI is used. It differs from the stdcall or fastcall ABI. | |
6 | // "i686-unknown-windows" is used to get the minimal subset of windows-specific features. | |
7 | ||
8 | use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; | |
9 | ||
10 | pub fn target() -> TargetResult { | |
11 | let mut base = super::uefi_base::opts(); | |
12 | base.cpu = "pentium4".to_string(); | |
13 | base.max_atomic_width = Some(64); | |
14 | ||
15 | // We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to | |
16 | // enable these CPU features explicitly before their first use, otherwise their instructions | |
17 | // will trigger an exception. Rust does not inject any code that enables AVX/MMX/SSE | |
18 | // instruction sets, so this must be done by the firmware. However, existing firmware is known | |
19 | // to leave these uninitialized, thus triggering exceptions if we make use of them. Which is | |
20 | // why we avoid them and instead use soft-floats. This is also what GRUB and friends did so | |
21 | // far. | |
22 | // If you initialize FP units yourself, you can override these flags with custom linker | |
23 | // arguments, thus giving you access to full MMX/SSE acceleration. | |
24 | base.features = "-mmx,-sse,+soft-float".to_string(); | |
25 | ||
26 | // UEFI mirrors the calling-conventions used on windows. In case of i686 this means small | |
27 | // structs will be returned as int. This shouldn't matter much, since the restrictions placed | |
28 | // by the UEFI specifications forbid any ABI to return structures. | |
29 | base.abi_return_struct_as_int = true; | |
30 | ||
31 | // Use -GNU here, because of the reason below: | |
32 | // Backgound and Problem: | |
33 | // If we use i686-unknown-windows, the LLVM IA32 MSVC generates compiler intrinsic | |
34 | // _alldiv, _aulldiv, _allrem, _aullrem, _allmul, which will cause undefined symbol. | |
60c5eb7d | 35 | // A real issue is __aulldiv() is referred by __udivdi3() - udivmod_inner!(), from |
e1599b0c XL |
36 | // https://github.com/rust-lang-nursery/compiler-builtins. |
37 | // As result, rust-lld generates link error finally. | |
38 | // Root-cause: | |
39 | // In rust\src\llvm-project\llvm\lib\Target\X86\X86ISelLowering.cpp, | |
40 | // we have below code to use MSVC intrinsics. It assumes MSVC target | |
41 | // will link MSVC library. But that is NOT true in UEFI environment. | |
42 | // UEFI does not link any MSVC or GCC standard library. | |
43 | // if (Subtarget.isTargetKnownWindowsMSVC() || | |
44 | // Subtarget.isTargetWindowsItanium()) { | |
45 | // // Setup Windows compiler runtime calls. | |
46 | // setLibcallName(RTLIB::SDIV_I64, "_alldiv"); | |
47 | // setLibcallName(RTLIB::UDIV_I64, "_aulldiv"); | |
48 | // setLibcallName(RTLIB::SREM_I64, "_allrem"); | |
49 | // setLibcallName(RTLIB::UREM_I64, "_aullrem"); | |
50 | // setLibcallName(RTLIB::MUL_I64, "_allmul"); | |
51 | // setLibcallCallingConv(RTLIB::SDIV_I64, CallingConv::X86_StdCall); | |
52 | // setLibcallCallingConv(RTLIB::UDIV_I64, CallingConv::X86_StdCall); | |
53 | // setLibcallCallingConv(RTLIB::SREM_I64, CallingConv::X86_StdCall); | |
54 | // setLibcallCallingConv(RTLIB::UREM_I64, CallingConv::X86_StdCall); | |
55 | // setLibcallCallingConv(RTLIB::MUL_I64, CallingConv::X86_StdCall); | |
56 | // } | |
57 | // The compiler intrisics should be implemented by compiler-builtins. | |
58 | // Unfortunately, compiler-builtins has not provided those intrinsics yet. Such as: | |
59 | // i386/divdi3.S | |
60 | // i386/lshrdi3.S | |
61 | // i386/moddi3.S | |
62 | // i386/muldi3.S | |
63 | // i386/udivdi3.S | |
64 | // i386/umoddi3.S | |
65 | // Possible solution: | |
66 | // 1. Eliminate Intrinsics generation. | |
67 | // 1.1 Choose differnt target to bypass isTargetKnownWindowsMSVC(). | |
68 | // 1.2 Remove the "Setup Windows compiler runtime calls" in LLVM | |
69 | // 2. Implement Intrinsics. | |
70 | // We evaluated all options. | |
71 | // #2 is hard because we need implement the intrinsics (_aulldiv) generated | |
72 | // from the other intrinscis (__udivdi3) implementation with the same | |
73 | // functionality (udivmod_inner). If we let _aulldiv() call udivmod_inner!(), | |
74 | // then we are in loop. We may have to find another way to implement udivmod_inner!(). | |
75 | // #1.2 may break the existing usage. | |
76 | // #1.1 seems the simplest solution today. | |
77 | // The IA32 -gnu calling convention is same as the one defined in UEFI specification. | |
78 | // It uses cdecl, EAX/ECX/EDX as volatile register, and EAX/EDX as return value. | |
79 | // We also checked the LLVM X86TargetLowering, the differences between -gnu and -msvc | |
80 | // is fmodf(f32), longjmp() and TLS. None of them impacts the UEFI code. | |
81 | // As a result, we choose -gnu for i686 version before those intrisics are implemented in | |
82 | // compiler-builtins. After compiler-builtins implements all required intrinsics, we may | |
83 | // remove -gnu and use the default one. | |
84 | Ok(Target { | |
85 | llvm_target: "i686-unknown-windows-gnu".to_string(), | |
86 | target_endian: "little".to_string(), | |
87 | target_pointer_width: "32".to_string(), | |
88 | target_c_int_width: "32".to_string(), | |
89 | data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(), | |
90 | target_os: "uefi".to_string(), | |
91 | target_env: "".to_string(), | |
92 | target_vendor: "unknown".to_string(), | |
93 | arch: "x86".to_string(), | |
94 | linker_flavor: LinkerFlavor::Lld(LldFlavor::Link), | |
95 | ||
96 | options: base, | |
97 | }) | |
98 | } |