Rollup merge of #132319 - Zalathar:add-module-flag, r=jieyouxu

cg_llvm: Clean up FFI calls for setting module flags

This is a combination of several inter-related changes to how module flags are set:

- Remove some unnecessary code for setting an `"LTOPostLink"` flag, which has been obsolete since LLVM 17.
- Define our own enum instead of relying on enum values defined by LLVM's unstable C++ API.
- Use safe wrapper functions to set module flags, instead of direct `unsafe` calls.
- Consistently pass pointer/length strings instead of C strings.
- Remove or shrink some `unsafe` blocks.
This commit is contained in:
Matthias Krüger 2024-10-29 18:38:59 +01:00 committed by GitHub
commit 2707cd670c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 245 additions and 237 deletions

View File

@ -25,7 +25,6 @@
use crate::back::write::{ use crate::back::write::{
self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode, self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode,
}; };
use crate::common::AsCCharPtr;
use crate::errors::{ use crate::errors::{
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro, DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro,
}; };
@ -602,23 +601,9 @@ pub(crate) fn run_pass_manager(
// This code is based off the code found in llvm's LTO code generator: // This code is based off the code found in llvm's LTO code generator:
// llvm/lib/LTO/LTOCodeGenerator.cpp // llvm/lib/LTO/LTOCodeGenerator.cpp
debug!("running the pass manager"); debug!("running the pass manager");
unsafe {
if !llvm::LLVMRustHasModuleFlag(
module.module_llvm.llmod(),
"LTOPostLink".as_c_char_ptr(),
11,
) {
llvm::LLVMRustAddModuleFlagU32(
module.module_llvm.llmod(),
llvm::LLVMModFlagBehavior::Error,
c"LTOPostLink".as_ptr(),
1,
);
}
let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO }; let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
let opt_level = config.opt_level.unwrap_or(config::OptLevel::No); let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
write::llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage)?; unsafe { write::llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage) }?;
}
debug!("lto done"); debug!("lto done");
Ok(()) Ok(())
} }

View File

@ -210,133 +210,111 @@ pub(crate) unsafe fn create_module<'ll>(
// If skipping the PLT is enabled, we need to add some module metadata // If skipping the PLT is enabled, we need to add some module metadata
// to ensure intrinsic calls don't use it. // to ensure intrinsic calls don't use it.
if !sess.needs_plt() { if !sess.needs_plt() {
let avoid_plt = c"RtLibUseGOT".as_ptr(); llvm::add_module_flag_u32(llmod, llvm::ModuleFlagMergeBehavior::Warning, "RtLibUseGOT", 1);
unsafe {
llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
}
} }
// Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.) // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.)
if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() { if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() {
let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr(); llvm::add_module_flag_u32(
unsafe {
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Override, llvm::ModuleFlagMergeBehavior::Override,
canonical_jump_tables, "CFI Canonical Jump Tables",
1, 1,
); );
} }
}
// If we're normalizing integers with CFI, ensure LLVM generated functions do the same. // If we're normalizing integers with CFI, ensure LLVM generated functions do the same.
// See https://github.com/llvm/llvm-project/pull/104826 // See https://github.com/llvm/llvm-project/pull/104826
if sess.is_sanitizer_cfi_normalize_integers_enabled() { if sess.is_sanitizer_cfi_normalize_integers_enabled() {
let cfi_normalize_integers = c"cfi-normalize-integers".as_ptr(); llvm::add_module_flag_u32(
unsafe {
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Override, llvm::ModuleFlagMergeBehavior::Override,
cfi_normalize_integers, "cfi-normalize-integers",
1, 1,
); );
} }
}
// Enable LTO unit splitting if specified or if CFI is enabled. (See // Enable LTO unit splitting if specified or if CFI is enabled. (See
// https://reviews.llvm.org/D53891.) // https://reviews.llvm.org/D53891.)
if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() {
let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr(); llvm::add_module_flag_u32(
unsafe {
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Override, llvm::ModuleFlagMergeBehavior::Override,
enable_split_lto_unit, "EnableSplitLTOUnit",
1, 1,
); );
} }
}
// Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.) // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
if sess.is_sanitizer_kcfi_enabled() { if sess.is_sanitizer_kcfi_enabled() {
let kcfi = c"kcfi".as_ptr(); llvm::add_module_flag_u32(llmod, llvm::ModuleFlagMergeBehavior::Override, "kcfi", 1);
unsafe {
llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
}
// Add "kcfi-offset" module flag with -Z patchable-function-entry (See // Add "kcfi-offset" module flag with -Z patchable-function-entry (See
// https://reviews.llvm.org/D141172). // https://reviews.llvm.org/D141172).
let pfe = let pfe =
PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry); PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry);
if pfe.prefix() > 0 { if pfe.prefix() > 0 {
let kcfi_offset = c"kcfi-offset".as_ptr(); llvm::add_module_flag_u32(
unsafe {
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Override, llvm::ModuleFlagMergeBehavior::Override,
kcfi_offset, "kcfi-offset",
pfe.prefix().into(), pfe.prefix().into(),
); );
} }
} }
}
// Control Flow Guard is currently only supported by the MSVC linker on Windows. // Control Flow Guard is currently only supported by the MSVC linker on Windows.
if sess.target.is_like_msvc { if sess.target.is_like_msvc {
unsafe {
match sess.opts.cg.control_flow_guard { match sess.opts.cg.control_flow_guard {
CFGuard::Disabled => {} CFGuard::Disabled => {}
CFGuard::NoChecks => { CFGuard::NoChecks => {
// Set `cfguard=1` module flag to emit metadata only. // Set `cfguard=1` module flag to emit metadata only.
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Warning, llvm::ModuleFlagMergeBehavior::Warning,
c"cfguard".as_ptr() as *const _, "cfguard",
1, 1,
) );
} }
CFGuard::Checks => { CFGuard::Checks => {
// Set `cfguard=2` module flag to emit metadata and checks. // Set `cfguard=2` module flag to emit metadata and checks.
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Warning, llvm::ModuleFlagMergeBehavior::Warning,
c"cfguard".as_ptr() as *const _, "cfguard",
2, 2,
) );
}
} }
} }
} }
if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
if sess.target.arch == "aarch64" { if sess.target.arch == "aarch64" {
unsafe { llvm::add_module_flag_u32(
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Min, llvm::ModuleFlagMergeBehavior::Min,
c"branch-target-enforcement".as_ptr(), "branch-target-enforcement",
bti.into(), bti.into(),
); );
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Min, llvm::ModuleFlagMergeBehavior::Min,
c"sign-return-address".as_ptr(), "sign-return-address",
pac_ret.is_some().into(), pac_ret.is_some().into(),
); );
let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Min, llvm::ModuleFlagMergeBehavior::Min,
c"sign-return-address-all".as_ptr(), "sign-return-address-all",
pac_opts.leaf.into(), pac_opts.leaf.into(),
); );
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Min, llvm::ModuleFlagMergeBehavior::Min,
c"sign-return-address-with-bkey".as_ptr(), "sign-return-address-with-bkey",
u32::from(pac_opts.key == PAuthKey::B), u32::from(pac_opts.key == PAuthKey::B),
); );
}
} else { } else {
bug!( bug!(
"branch-protection used on non-AArch64 target; \ "branch-protection used on non-AArch64 target; \
@ -347,59 +325,46 @@ pub(crate) unsafe fn create_module<'ll>(
// Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang).
if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection { if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
unsafe { llvm::add_module_flag_u32(
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Override, llvm::ModuleFlagMergeBehavior::Override,
c"cf-protection-branch".as_ptr(), "cf-protection-branch",
1, 1,
); );
} }
}
if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection { if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
unsafe { llvm::add_module_flag_u32(
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Override, llvm::ModuleFlagMergeBehavior::Override,
c"cf-protection-return".as_ptr(), "cf-protection-return",
1, 1,
); );
} }
}
if sess.opts.unstable_opts.virtual_function_elimination { if sess.opts.unstable_opts.virtual_function_elimination {
unsafe { llvm::add_module_flag_u32(
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Error, llvm::ModuleFlagMergeBehavior::Error,
c"Virtual Function Elim".as_ptr(), "Virtual Function Elim",
1, 1,
); );
} }
}
// Set module flag to enable Windows EHCont Guard (/guard:ehcont). // Set module flag to enable Windows EHCont Guard (/guard:ehcont).
if sess.opts.unstable_opts.ehcont_guard { if sess.opts.unstable_opts.ehcont_guard {
unsafe { llvm::add_module_flag_u32(llmod, llvm::ModuleFlagMergeBehavior::Warning, "ehcontguard", 1);
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Warning,
c"ehcontguard".as_ptr() as *const _,
1,
)
}
} }
match sess.opts.unstable_opts.function_return { match sess.opts.unstable_opts.function_return {
FunctionReturn::Keep => {} FunctionReturn::Keep => {}
FunctionReturn::ThunkExtern => unsafe { FunctionReturn::ThunkExtern => {
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Override, llvm::ModuleFlagMergeBehavior::Override,
c"function_return_thunk_extern".as_ptr(), "function_return_thunk_extern",
1, 1,
) );
}, }
} }
match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support()) match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support())
@ -407,15 +372,12 @@ pub(crate) unsafe fn create_module<'ll>(
// Set up the small-data optimization limit for architectures that use // Set up the small-data optimization limit for architectures that use
// an LLVM module flag to control this. // an LLVM module flag to control this.
(Some(threshold), SmallDataThresholdSupport::LlvmModuleFlag(flag)) => { (Some(threshold), SmallDataThresholdSupport::LlvmModuleFlag(flag)) => {
let flag = SmallCStr::new(flag.as_ref()); llvm::add_module_flag_u32(
unsafe {
llvm::LLVMRustAddModuleFlagU32(
llmod, llmod,
llvm::LLVMModFlagBehavior::Error, llvm::ModuleFlagMergeBehavior::Error,
flag.as_c_str().as_ptr(), &flag,
threshold as u32, threshold as u32,
) );
}
} }
_ => (), _ => (),
}; };
@ -449,33 +411,29 @@ pub(crate) unsafe fn create_module<'ll>(
// If llvm_abiname is empty, emit nothing. // If llvm_abiname is empty, emit nothing.
let llvm_abiname = &sess.target.options.llvm_abiname; let llvm_abiname = &sess.target.options.llvm_abiname;
if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() { if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() {
unsafe { llvm::add_module_flag_str(
llvm::LLVMRustAddModuleFlagString(
llmod, llmod,
llvm::LLVMModFlagBehavior::Error, llvm::ModuleFlagMergeBehavior::Error,
c"target-abi".as_ptr(), "target-abi",
llvm_abiname.as_c_char_ptr(), llvm_abiname,
llvm_abiname.len(),
); );
} }
}
// Add module flags specified via -Z llvm_module_flag // Add module flags specified via -Z llvm_module_flag
for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag { for (key, value, merge_behavior) in &sess.opts.unstable_opts.llvm_module_flag {
let key = format!("{key}\0"); let merge_behavior = match merge_behavior.as_str() {
let behavior = match behavior.as_str() { "error" => llvm::ModuleFlagMergeBehavior::Error,
"error" => llvm::LLVMModFlagBehavior::Error, "warning" => llvm::ModuleFlagMergeBehavior::Warning,
"warning" => llvm::LLVMModFlagBehavior::Warning, "require" => llvm::ModuleFlagMergeBehavior::Require,
"require" => llvm::LLVMModFlagBehavior::Require, "override" => llvm::ModuleFlagMergeBehavior::Override,
"override" => llvm::LLVMModFlagBehavior::Override, "append" => llvm::ModuleFlagMergeBehavior::Append,
"append" => llvm::LLVMModFlagBehavior::Append, "appendunique" => llvm::ModuleFlagMergeBehavior::AppendUnique,
"appendunique" => llvm::LLVMModFlagBehavior::AppendUnique, "max" => llvm::ModuleFlagMergeBehavior::Max,
"max" => llvm::LLVMModFlagBehavior::Max, "min" => llvm::ModuleFlagMergeBehavior::Min,
"min" => llvm::LLVMModFlagBehavior::Min,
// We already checked this during option parsing // We already checked this during option parsing
_ => unreachable!(), _ => unreachable!(),
}; };
unsafe { llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_c_char_ptr(), *value) } llvm::add_module_flag_u32(llmod, merge_behavior, key, *value);
} }
llmod llmod

View File

@ -91,9 +91,7 @@ pub(crate) fn new(llmod: &'ll llvm::Module) -> Self {
} }
pub(crate) fn finalize(&self, sess: &Session) { pub(crate) fn finalize(&self, sess: &Session) {
unsafe { unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) };
llvm::LLVMRustDIBuilderFinalize(self.builder);
if !sess.target.is_like_msvc { if !sess.target.is_like_msvc {
// Debuginfo generation in LLVM by default uses a higher // Debuginfo generation in LLVM by default uses a higher
// version of dwarf than macOS currently understands. We can // version of dwarf than macOS currently understands. We can
@ -101,36 +99,32 @@ pub(crate) fn finalize(&self, sess: &Session) {
// for macOS to understand. For more info see #11352 // for macOS to understand. For more info see #11352
// This can be overridden using --llvm-opts -dwarf-version,N. // This can be overridden using --llvm-opts -dwarf-version,N.
// Android has the same issue (#22398) // Android has the same issue (#22398)
let dwarf_version = sess let dwarf_version =
.opts sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version);
.unstable_opts llvm::add_module_flag_u32(
.dwarf_version
.unwrap_or(sess.target.default_dwarf_version);
llvm::LLVMRustAddModuleFlagU32(
self.llmod, self.llmod,
llvm::LLVMModFlagBehavior::Warning, llvm::ModuleFlagMergeBehavior::Warning,
c"Dwarf Version".as_ptr(), "Dwarf Version",
dwarf_version, dwarf_version,
); );
} else { } else {
// Indicate that we want CodeView debug information on MSVC // Indicate that we want CodeView debug information on MSVC
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
self.llmod, self.llmod,
llvm::LLVMModFlagBehavior::Warning, llvm::ModuleFlagMergeBehavior::Warning,
c"CodeView".as_ptr(), "CodeView",
1, 1,
) );
} }
// Prevent bitcode readers from deleting the debug info. // Prevent bitcode readers from deleting the debug info.
llvm::LLVMRustAddModuleFlagU32( llvm::add_module_flag_u32(
self.llmod, self.llmod,
llvm::LLVMModFlagBehavior::Warning, llvm::ModuleFlagMergeBehavior::Warning,
c"Debug Info Version".as_ptr(), "Debug Info Version",
llvm::LLVMRustDebugMetadataVersion(), unsafe { llvm::LLVMRustDebugMetadataVersion() },
); );
} }
}
} }
/// Creates any deferred debug metadata nodes /// Creates any deferred debug metadata nodes

View File

@ -85,7 +85,7 @@ pub enum LLVMMachineType {
ARM = 0x01c0, ARM = 0x01c0,
} }
/// LLVM's Module::ModFlagBehavior, defined in llvm/include/llvm/IR/Module.h. /// Must match the layout of `LLVMRustModuleFlagMergeBehavior`.
/// ///
/// When merging modules (e.g. during LTO), their metadata flags are combined. Conflicts are /// When merging modules (e.g. during LTO), their metadata flags are combined. Conflicts are
/// resolved according to the merge behaviors specified here. Flags differing only in merge /// resolved according to the merge behaviors specified here. Flags differing only in merge
@ -93,9 +93,13 @@ pub enum LLVMMachineType {
/// ///
/// In order for Rust-C LTO to work, we must specify behaviors compatible with Clang. Notably, /// In order for Rust-C LTO to work, we must specify behaviors compatible with Clang. Notably,
/// 'Error' and 'Warning' cannot be mixed for a given flag. /// 'Error' and 'Warning' cannot be mixed for a given flag.
///
/// There is a stable LLVM-C version of this enum (`LLVMModuleFlagBehavior`),
/// but as of LLVM 19 it does not support all of the enum values in the unstable
/// C++ API.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
#[repr(C)] #[repr(C)]
pub enum LLVMModFlagBehavior { pub enum ModuleFlagMergeBehavior {
Error = 1, Error = 1,
Warning = 2, Warning = 2,
Require = 3, Require = 3,
@ -1829,21 +1833,21 @@ pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar(
/// "compatible" means depends on the merge behaviors involved. /// "compatible" means depends on the merge behaviors involved.
pub fn LLVMRustAddModuleFlagU32( pub fn LLVMRustAddModuleFlagU32(
M: &Module, M: &Module,
merge_behavior: LLVMModFlagBehavior, MergeBehavior: ModuleFlagMergeBehavior,
name: *const c_char, Name: *const c_char,
value: u32, NameLen: size_t,
Value: u32,
); );
pub fn LLVMRustAddModuleFlagString( pub fn LLVMRustAddModuleFlagString(
M: &Module, M: &Module,
merge_behavior: LLVMModFlagBehavior, MergeBehavior: ModuleFlagMergeBehavior,
name: *const c_char, Name: *const c_char,
value: *const c_char, NameLen: size_t,
value_len: size_t, Value: *const c_char,
ValueLen: size_t,
); );
pub fn LLVMRustHasModuleFlag(M: &Module, name: *const c_char, len: size_t) -> bool;
pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>; pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>;
pub fn LLVMRustDIBuilderDispose<'a>(Builder: &'a mut DIBuilder<'a>); pub fn LLVMRustDIBuilderDispose<'a>(Builder: &'a mut DIBuilder<'a>);

View File

@ -352,3 +352,32 @@ fn drop(&mut self) {
} }
} }
} }
pub(crate) fn add_module_flag_u32(
module: &Module,
merge_behavior: ModuleFlagMergeBehavior,
key: &str,
value: u32,
) {
unsafe {
LLVMRustAddModuleFlagU32(module, merge_behavior, key.as_c_char_ptr(), key.len(), value);
}
}
pub(crate) fn add_module_flag_str(
module: &Module,
merge_behavior: ModuleFlagMergeBehavior,
key: &str,
value: &str,
) {
unsafe {
LLVMRustAddModuleFlagString(
module,
merge_behavior,
key.as_c_char_ptr(),
key.len(),
value.as_c_char_ptr(),
value.len(),
);
}
}

View File

@ -853,25 +853,63 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }
extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
extern "C" void LLVMRustAddModuleFlagU32(LLVMModuleRef M, // FFI equivalent of LLVM's `llvm::Module::ModFlagBehavior`.
Module::ModFlagBehavior MergeBehavior, // Must match the layout of
const char *Name, uint32_t Value) { // `rustc_codegen_llvm::llvm::ffi::ModuleFlagMergeBehavior`.
unwrap(M)->addModuleFlag(MergeBehavior, Name, Value); //
// There is a stable LLVM-C version of this enum (`LLVMModuleFlagBehavior`),
// but as of LLVM 19 it does not support all of the enum values in the unstable
// C++ API.
enum class LLVMRustModuleFlagMergeBehavior {
Error = 1,
Warning = 2,
Require = 3,
Override = 4,
Append = 5,
AppendUnique = 6,
Max = 7,
Min = 8,
};
static Module::ModFlagBehavior
fromRust(LLVMRustModuleFlagMergeBehavior Behavior) {
switch (Behavior) {
case LLVMRustModuleFlagMergeBehavior::Error:
return Module::ModFlagBehavior::Error;
case LLVMRustModuleFlagMergeBehavior::Warning:
return Module::ModFlagBehavior::Warning;
case LLVMRustModuleFlagMergeBehavior::Require:
return Module::ModFlagBehavior::Require;
case LLVMRustModuleFlagMergeBehavior::Override:
return Module::ModFlagBehavior::Override;
case LLVMRustModuleFlagMergeBehavior::Append:
return Module::ModFlagBehavior::Append;
case LLVMRustModuleFlagMergeBehavior::AppendUnique:
return Module::ModFlagBehavior::AppendUnique;
case LLVMRustModuleFlagMergeBehavior::Max:
return Module::ModFlagBehavior::Max;
case LLVMRustModuleFlagMergeBehavior::Min:
return Module::ModFlagBehavior::Min;
}
report_fatal_error("bad LLVMRustModuleFlagMergeBehavior");
}
extern "C" void
LLVMRustAddModuleFlagU32(LLVMModuleRef M,
LLVMRustModuleFlagMergeBehavior MergeBehavior,
const char *Name, size_t NameLen, uint32_t Value) {
unwrap(M)->addModuleFlag(fromRust(MergeBehavior), StringRef(Name, NameLen),
Value);
} }
extern "C" void LLVMRustAddModuleFlagString( extern "C" void LLVMRustAddModuleFlagString(
LLVMModuleRef M, Module::ModFlagBehavior MergeBehavior, const char *Name, LLVMModuleRef M, LLVMRustModuleFlagMergeBehavior MergeBehavior,
const char *Value, size_t ValueLen) { const char *Name, size_t NameLen, const char *Value, size_t ValueLen) {
unwrap(M)->addModuleFlag( unwrap(M)->addModuleFlag(
MergeBehavior, Name, fromRust(MergeBehavior), StringRef(Name, NameLen),
MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen))); MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen)));
} }
extern "C" bool LLVMRustHasModuleFlag(LLVMModuleRef M, const char *Name,
size_t Len) {
return unwrap(M)->getModuleFlag(StringRef(Name, Len)) != nullptr;
}
extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind, extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind,
LLVMMetadataRef MD) { LLVMMetadataRef MD) {
unwrap<GlobalObject>(Global)->addMetadata(Kind, *unwrap<MDNode>(MD)); unwrap<GlobalObject>(Global)->addMetadata(Kind, *unwrap<MDNode>(MD));