diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 54271d3dd0491..af87651833d88 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -108,7 +108,7 @@ fn to_llvm_relocation_model(relocation_model: RelocModel) -> llvm::RelocModel { } } -fn to_llvm_code_model(code_model: Option) -> llvm::CodeModel { +pub(crate) fn to_llvm_code_model(code_model: Option) -> llvm::CodeModel { match code_model { Some(CodeModel::Tiny) => llvm::CodeModel::Tiny, Some(CodeModel::Small) => llvm::CodeModel::Small, diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index e9a001bab7540..d7ab1e2327519 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -1,4 +1,5 @@ use crate::attributes; +use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::coverageinfo; use crate::debuginfo; @@ -181,6 +182,13 @@ pub unsafe fn create_module( } } + // Linking object files with different code models is undefined behavior + // because the compiler would have to generate additional code (to span + // longer jumps) if a larger code model is used with a smaller one. + // + // See https://reviews.llvm.org/D52322 and https://reviews.llvm.org/D52323. + llvm::LLVMRustSetModuleCodeModel(llmod, to_llvm_code_model(sess.code_model())); + // If skipping the PLT is enabled, we need to add some module metadata // to ensure intrinsic calls don't use it. if !sess.needs_plt() { diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 7beb4fc897472..c7ebd3f0684fa 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -2129,6 +2129,7 @@ extern "C" { pub fn LLVMRustUnsetComdat(V: &Value); pub fn LLVMRustSetModulePICLevel(M: &Module); pub fn LLVMRustSetModulePIELevel(M: &Module); + pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); pub fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; pub fn LLVMRustModuleBufferPtr(p: &ModuleBuffer) -> *const u8; pub fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 41b14714842fd..3ad3c5353b7b0 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -1163,6 +1163,14 @@ extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { unwrap(M)->setPIELevel(PIELevel::Level::Large); } +extern "C" void LLVMRustSetModuleCodeModel(LLVMModuleRef M, + LLVMRustCodeModel Model) { + auto CM = fromRust(Model); + if (!CM.hasValue()) + return; + unwrap(M)->setCodeModel(*CM); +} + // Here you'll find an implementation of ThinLTO as used by the Rust compiler // right now. This ThinLTO support is only enabled on "recent ish" versions of // LLVM, and otherwise it's just blanket rejected from other compilers. diff --git a/src/test/codegen/codemodels.rs b/src/test/codegen/codemodels.rs new file mode 100644 index 0000000000000..d6cb73b8b8a16 --- /dev/null +++ b/src/test/codegen/codemodels.rs @@ -0,0 +1,18 @@ +// revisions: NOMODEL MODEL-SMALL MODEL-KERNEL MODEL-MEDIUM MODEL-LARGE +//[NOMODEL] compile-flags: +//[MODEL-SMALL] compile-flags: -C code-model=small +//[MODEL-KERNEL] compile-flags: -C code-model=kernel +//[MODEL-MEDIUM] compile-flags: -C code-model=medium +//[MODEL-LARGE] compile-flags: -C code-model=large + +#![crate_type = "lib"] + +// MODEL-SMALL: !llvm.module.flags = !{{{.*}}} +// MODEL-SMALL: !{{[0-9]+}} = !{i32 1, !"Code Model", i32 1} +// MODEL-KERNEL: !llvm.module.flags = !{{{.*}}} +// MODEL-KERNEL: !{{[0-9]+}} = !{i32 1, !"Code Model", i32 2} +// MODEL-MEDIUM: !llvm.module.flags = !{{{.*}}} +// MODEL-MEDIUM: !{{[0-9]+}} = !{i32 1, !"Code Model", i32 3} +// MODEL-LARGE: !llvm.module.flags = !{{{.*}}} +// MODEL-LARGE: !{{[0-9]+}} = !{i32 1, !"Code Model", i32 4} +// NOMODEL-NOT: Code Model