1use pliron::{std_deps::sync::LazyLock, utils::table::HMap};
7use thiserror::Error;
8
9use crate::llvm_sys::core::llvm_enum_attribute_kind;
10
11#[derive(Clone, Copy, PartialEq, Eq, Debug)]
13pub enum LlvmAttrValueKind {
14 Enum,
16 Int,
18 Type,
20 ConstantRange,
22 ConstantRangeList,
24}
25
26const LLVM_ENUM_ATTRIBUTES: &[(&str, LlvmAttrValueKind)] = {
45 use LlvmAttrValueKind::*;
46 &[
47 ("align", Int),
48 ("alignstack", Int),
49 ("allocalign", Enum),
50 ("allockind", Int),
51 ("allocptr", Enum),
52 ("allocsize", Int),
53 ("alwaysinline", Enum),
54 ("builtin", Enum),
55 ("byref", Type),
56 ("byval", Type),
57 ("captures", Int),
58 ("cold", Enum),
59 ("convergent", Enum),
60 ("coro_elide_safe", Enum),
61 ("coro_only_destroy_when_complete", Enum),
62 ("dead_on_return", Int),
63 ("dead_on_unwind", Enum),
64 ("denormal_fpenv", Int),
65 ("dereferenceable", Int),
66 ("dereferenceable_or_null", Int),
67 ("disable_sanitizer_instrumentation", Enum),
68 ("elementtype", Type),
69 ("flatten", Enum),
70 ("fn_ret_thunk_extern", Enum),
71 ("hot", Enum),
72 ("hybrid_patchable", Enum),
73 ("immarg", Enum),
74 ("inalloca", Type),
75 ("initializes", ConstantRangeList),
76 ("inlinehint", Enum),
77 ("inreg", Enum),
78 ("jumptable", Enum),
79 ("memory", Int),
80 ("minsize", Enum),
81 ("mustprogress", Enum),
82 ("naked", Enum),
83 ("nest", Enum),
84 ("noalias", Enum),
85 ("nobuiltin", Enum),
86 ("nocallback", Enum),
87 ("nocf_check", Enum),
88 ("nocreateundeforpoison", Enum),
89 ("nodivergencesource", Enum),
90 ("noduplicate", Enum),
91 ("noext", Enum),
92 ("nofpclass", Int),
93 ("nofree", Enum),
94 ("noimplicitfloat", Enum),
95 ("noinline", Enum),
96 ("noipa", Enum),
97 ("nomerge", Enum),
98 ("nonlazybind", Enum),
99 ("nonnull", Enum),
100 ("nooutline", Enum),
101 ("noprofile", Enum),
102 ("norecurse", Enum),
103 ("noredzone", Enum),
104 ("noreturn", Enum),
105 ("nosanitize_bounds", Enum),
106 ("nosanitize_coverage", Enum),
107 ("nosync", Enum),
108 ("noundef", Enum),
109 ("nounwind", Enum),
110 ("null_pointer_is_valid", Enum),
111 ("optdebug", Enum),
112 ("optforfuzzing", Enum),
113 ("optnone", Enum),
114 ("optsize", Enum),
115 ("preallocated", Type),
116 ("presplitcoroutine", Enum),
117 ("range", ConstantRange),
118 ("readnone", Enum),
119 ("readonly", Enum),
120 ("returned", Enum),
121 ("returns_twice", Enum),
122 ("safestack", Enum),
123 ("sanitize_address", Enum),
124 ("sanitize_alloc_token", Enum),
125 ("sanitize_hwaddress", Enum),
126 ("sanitize_memory", Enum),
127 ("sanitize_memtag", Enum),
128 ("sanitize_numerical_stability", Enum),
129 ("sanitize_realtime", Enum),
130 ("sanitize_realtime_blocking", Enum),
131 ("sanitize_thread", Enum),
132 ("sanitize_type", Enum),
133 ("shadowcallstack", Enum),
134 ("signext", Enum),
135 ("skipprofile", Enum),
136 ("speculatable", Enum),
137 ("speculative_load_hardening", Enum),
138 ("sret", Type),
139 ("ssp", Enum),
140 ("sspreq", Enum),
141 ("sspstrong", Enum),
142 ("strictfp", Enum),
143 ("swiftasync", Enum),
144 ("swifterror", Enum),
145 ("swiftself", Enum),
146 ("uwtable", Int),
147 ("vscale_range", Int),
148 ("willreturn", Enum),
149 ("writable", Enum),
150 ("writeonly", Enum),
151 ("zeroext", Enum),
152 ]
153};
154
155static LLVM_ENUM_ATTRIBUTE_KIND_INFO: LazyLock<HMap<u32, (&'static str, LlvmAttrValueKind)>> =
157 LazyLock::new(|| {
158 LLVM_ENUM_ATTRIBUTES
159 .iter()
160 .filter_map(|(name, value_kind)| {
161 llvm_enum_attribute_kind(name).map(|kind| (kind, (*name, *value_kind)))
162 })
163 .collect()
164 });
165
166pub fn llvm_enum_attribute_info(kind: u32) -> Option<(&'static str, LlvmAttrValueKind)> {
168 LLVM_ENUM_ATTRIBUTE_KIND_INFO.get(&kind).copied()
169}
170
171#[derive(Error, Debug)]
173pub enum ToLlvmAttrErr {
174 #[error(
175 "LLVM attribute \"{name}\" cannot hold the given value; expected value kind {value_kind:?}"
176 )]
177 ValueKindMismatch {
178 name: String,
180 value_kind: LlvmAttrValueKind,
182 },
183}
184
185pub mod from_llvm_ir {
187 use pliron::{context::Context, result::Result};
188
189 use crate::{
190 from_llvm_ir::ConversionContext,
191 llvm_attrs::{LlvmAttrValue, LlvmAttributesAttr},
192 llvm_sys::core::{
193 LLVMAttribute, llvm_get_enum_attribute_kind, llvm_get_enum_attribute_value,
194 llvm_get_string_attribute_kind, llvm_get_string_attribute_value,
195 llvm_get_type_attribute_value, llvm_is_enum_attribute, llvm_is_string_attribute,
196 llvm_is_type_attribute,
197 },
198 };
199
200 use super::{LlvmAttrValueKind, llvm_enum_attribute_info};
201
202 pub(crate) fn convert_llvm_attributes(
206 ctx: &mut Context,
207 cctx: &mut ConversionContext,
208 attrs: Vec<LLVMAttribute>,
209 ) -> Result<LlvmAttributesAttr> {
210 let mut converted = LlvmAttributesAttr::new();
211 for attr in attrs {
212 if llvm_is_string_attribute(attr) {
213 converted.set(
214 llvm_get_string_attribute_kind(attr),
215 LlvmAttrValue::Str(llvm_get_string_attribute_value(attr)),
216 );
217 continue;
218 }
219
220 let kind = llvm_get_enum_attribute_kind(attr);
221 let Some((name, value_kind)) = llvm_enum_attribute_info(kind) else {
222 log::warn!("Dropping LLVM attribute with unknown kind id {kind}");
223 continue;
224 };
225
226 let value = if llvm_is_type_attribute(attr) {
227 LlvmAttrValue::Type(crate::from_llvm_ir::convert_type(
228 ctx,
229 cctx,
230 llvm_get_type_attribute_value(attr),
231 )?)
232 } else if llvm_is_enum_attribute(attr) {
233 if value_kind == LlvmAttrValueKind::Int {
234 LlvmAttrValue::Int(llvm_get_enum_attribute_value(attr))
235 } else {
236 LlvmAttrValue::Unit
237 }
238 } else {
239 log::warn!("Dropping LLVM attribute \"{name}\", whose value is unsupported");
241 continue;
242 };
243 converted.set(name, value);
244 }
245 Ok(converted)
246 }
247}
248
249pub mod to_llvm_ir {
251 use pliron::{context::Context, input_err_noloc, result::Result};
252
253 use crate::{
254 llvm_attrs::{LlvmAttrValue, LlvmAttributesAttr},
255 llvm_sys::core::{
256 LLVM_ATTRIBUTE_FUNCTION_INDEX, LLVMAttribute, LLVMAttributeIndex, LLVMContext,
257 LLVMValue, llvm_create_enum_attribute, llvm_create_string_attribute,
258 llvm_create_type_attribute,
259 },
260 to_llvm_ir::TypeConversionContext,
261 };
262
263 use super::{
264 LlvmAttrValueKind, ToLlvmAttrErr, llvm_enum_attribute_info, llvm_enum_attribute_kind,
265 };
266
267 pub(crate) fn add_function_attributes(
273 ctx: &Context,
274 llvm_ctx: &LLVMContext,
275 tcctx: &mut TypeConversionContext,
276 value: LLVMValue,
277 attrs: &LlvmAttributesAttr,
278 add_attribute: fn(LLVMValue, LLVMAttributeIndex, LLVMAttribute),
279 ) -> Result<()> {
280 for (name, attr_value) in attrs.iter() {
281 if let LlvmAttrValue::Str(s) = attr_value {
283 let attr = llvm_create_string_attribute(llvm_ctx, name, s);
284 add_attribute(value, LLVM_ATTRIBUTE_FUNCTION_INDEX, attr);
285 continue;
286 }
287
288 let info = llvm_enum_attribute_kind(name).and_then(|kind| {
290 llvm_enum_attribute_info(kind).map(|(_, value_kind)| (kind, value_kind))
291 });
292 let Some((kind, value_kind)) = info else {
293 log::warn!("Dropping LLVM attribute \"{name}\", unknown to this LLVM version");
294 continue;
295 };
296
297 let attr = match (value_kind, attr_value) {
299 (LlvmAttrValueKind::Enum, LlvmAttrValue::Unit) => {
300 llvm_create_enum_attribute(llvm_ctx, kind, 0)
301 }
302 (LlvmAttrValueKind::Int, LlvmAttrValue::Int(i)) => {
303 llvm_create_enum_attribute(llvm_ctx, kind, *i)
304 }
305 (LlvmAttrValueKind::Type, LlvmAttrValue::Type(ty)) => {
306 let llvm_ty = crate::to_llvm_ir::convert_type(ctx, llvm_ctx, tcctx, *ty)?;
307 llvm_create_type_attribute(llvm_ctx, kind, llvm_ty)
308 }
309 (LlvmAttrValueKind::ConstantRange | LlvmAttrValueKind::ConstantRangeList, _) => {
310 log::warn!("Dropping LLVM attribute \"{name}\", whose value is unsupported");
311 continue;
312 }
313 _ => {
314 return input_err_noloc!(ToLlvmAttrErr::ValueKindMismatch {
315 name: name.to_string(),
316 value_kind,
317 });
318 }
319 };
320 add_attribute(value, LLVM_ATTRIBUTE_FUNCTION_INDEX, attr);
321 }
322 Ok(())
323 }
324}