mirror of
https://github.com/LLVMParty/llvm-nanobind
synced 2026-06-21 13:43:38 +00:00
Attribute add_memory
This commit is contained in:
@@ -294,6 +294,7 @@ This section captures guard preconditions for wrapper classes other than
|
||||
- `return_attributes` for return-value attributes.
|
||||
- `param_attributes(i)` for parameter attributes, using a 0-based Python index.
|
||||
- Use `llvm.Attribute.enum/type/string(...)` or `slot.add("noreturn")`.
|
||||
- Use `llvm.Attribute.memory(ctx, "none")` or `slot.add_memory("read")` for `memory(...)` effects without raw encoded integers.
|
||||
- `block_address` requires block ownership by that function.
|
||||
Prefer `bb.block_address()` when the function can be inferred.
|
||||
- Parent navigation:
|
||||
|
||||
@@ -149,6 +149,141 @@ static unsigned require_enum_attribute_kind(const std::string &name) {
|
||||
return kind_id;
|
||||
}
|
||||
|
||||
static std::string trim_copy(const std::string &text) {
|
||||
size_t start = 0;
|
||||
while (start < text.size() &&
|
||||
std::isspace(static_cast<unsigned char>(text[start])))
|
||||
++start;
|
||||
size_t end = text.size();
|
||||
while (end > start && std::isspace(static_cast<unsigned char>(text[end - 1])))
|
||||
--end;
|
||||
return text.substr(start, end - start);
|
||||
}
|
||||
|
||||
static std::string lower_copy(std::string text) {
|
||||
for (char &c : text)
|
||||
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
return text;
|
||||
}
|
||||
|
||||
static std::string normalize_memory_keyword(const std::string &text) {
|
||||
std::string result;
|
||||
for (char c : trim_copy(text)) {
|
||||
unsigned char uc = static_cast<unsigned char>(c);
|
||||
if (std::isspace(uc) || c == '_' || c == '-')
|
||||
continue;
|
||||
result.push_back(static_cast<char>(std::tolower(uc)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint64_t memory_access_bits(const std::string &access) {
|
||||
std::string key = normalize_memory_keyword(access);
|
||||
if (key == "none" || key == "nomodref" || key == "noaccess")
|
||||
return 0;
|
||||
if (key == "read" || key == "ref" || key == "readonly")
|
||||
return 1;
|
||||
if (key == "write" || key == "mod" || key == "writeonly")
|
||||
return 2;
|
||||
if (key == "readwrite" || key == "modref")
|
||||
return 3;
|
||||
throw LLVMAssertionError("Unknown memory access effect: " + access);
|
||||
}
|
||||
|
||||
static bool memory_effects_has_errno_mem() {
|
||||
unsigned major = 0;
|
||||
LLVMGetVersion(&major, nullptr, nullptr);
|
||||
return major >= 21;
|
||||
}
|
||||
|
||||
static uint64_t encode_all_memory_locations(uint64_t access) {
|
||||
uint64_t encoded = access | (access << 2);
|
||||
if (memory_effects_has_errno_mem()) {
|
||||
// LLVM 21 added ErrnoMem between InaccessibleMem and Other.
|
||||
encoded |= (access << 4) | (access << 6);
|
||||
} else {
|
||||
encoded |= access << 4;
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
struct EncodedMemoryLocationEffect {
|
||||
uint64_t encoded = 0;
|
||||
unsigned seen_bit = 0;
|
||||
};
|
||||
|
||||
static EncodedMemoryLocationEffect
|
||||
encode_memory_location_effect(const std::string &location, uint64_t access) {
|
||||
std::string key = normalize_memory_keyword(location);
|
||||
if (key == "argmem" || key == "arg" || key == "argument" ||
|
||||
key == "argumentmem") {
|
||||
return {access, 1u << 0};
|
||||
}
|
||||
if (key == "inaccessiblemem" || key == "inaccessible") {
|
||||
return {access << 2, 1u << 1};
|
||||
}
|
||||
if (key == "errnomem" || key == "errno") {
|
||||
if (!memory_effects_has_errno_mem()) {
|
||||
throw LLVMAssertionError(
|
||||
"memory effect location 'errnomem' requires LLVM 21+");
|
||||
}
|
||||
return {access << 4, 1u << 2};
|
||||
}
|
||||
if (key == "other") {
|
||||
if (memory_effects_has_errno_mem())
|
||||
return {access << 6, 1u << 3};
|
||||
return {access << 4, 1u << 2};
|
||||
}
|
||||
throw LLVMAssertionError("Unknown memory effect location: " + location);
|
||||
}
|
||||
|
||||
static uint64_t encode_memory_effects(const std::string &effects) {
|
||||
std::string spec = trim_copy(effects);
|
||||
if (spec.empty())
|
||||
throw LLVMAssertionError("memory effects cannot be empty");
|
||||
|
||||
std::string lowered = lower_copy(spec);
|
||||
if (lowered.size() >= 8 && lowered.compare(0, 7, "memory(") == 0 &&
|
||||
spec.back() == ')') {
|
||||
spec = trim_copy(spec.substr(7, spec.size() - 8));
|
||||
}
|
||||
|
||||
if (spec.find(':') == std::string::npos) {
|
||||
return encode_all_memory_locations(memory_access_bits(spec));
|
||||
}
|
||||
|
||||
uint64_t encoded = 0;
|
||||
unsigned seen_locations = 0;
|
||||
size_t pos = 0;
|
||||
while (pos <= spec.size()) {
|
||||
size_t comma = spec.find(',', pos);
|
||||
std::string part = trim_copy(spec.substr(
|
||||
pos, comma == std::string::npos ? std::string::npos : comma - pos));
|
||||
if (part.empty())
|
||||
throw LLVMAssertionError("empty memory effect component in: " + effects);
|
||||
|
||||
size_t colon = part.find(':');
|
||||
if (colon == std::string::npos) {
|
||||
throw LLVMAssertionError(
|
||||
"memory location effects must use 'location: access': " + part);
|
||||
}
|
||||
|
||||
std::string location = trim_copy(part.substr(0, colon));
|
||||
std::string access_name = trim_copy(part.substr(colon + 1));
|
||||
EncodedMemoryLocationEffect effect =
|
||||
encode_memory_location_effect(location, memory_access_bits(access_name));
|
||||
if (seen_locations & effect.seen_bit)
|
||||
throw LLVMAssertionError("duplicate memory effect location: " + location);
|
||||
seen_locations |= effect.seen_bit;
|
||||
encoded |= effect.encoded;
|
||||
|
||||
if (comma == std::string::npos)
|
||||
break;
|
||||
pos = comma + 1;
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Diagnostic Information
|
||||
// =============================================================================
|
||||
@@ -4285,6 +4420,10 @@ struct LLVMAttributeAccessorWrapper {
|
||||
add(wrapped);
|
||||
}
|
||||
|
||||
void add_memory(const std::string &effects = "none") {
|
||||
add("memory", encode_memory_effects(effects));
|
||||
}
|
||||
|
||||
void add_type(const std::string &name, const LLVMTypeWrapper &type) {
|
||||
check_valid();
|
||||
type.check_valid();
|
||||
@@ -15217,6 +15356,25 @@ Examples:
|
||||
Attribute.enum(ctx, "noreturn")
|
||||
Attribute.enum(ctx, "align", 16)
|
||||
|
||||
<sub>C API: LLVMCreateEnumAttribute</sub>)")
|
||||
.def_static(
|
||||
"memory",
|
||||
[](LLVMContextWrapper &ctx, const std::string &effects) {
|
||||
return ctx.create_enum_attribute("memory",
|
||||
encode_memory_effects(effects));
|
||||
},
|
||||
"context"_a, "effects"_a = "none",
|
||||
R"(Create a memory(...) enum attribute.
|
||||
|
||||
The effects string accepts "none", "read", "write", "readwrite", or
|
||||
location-specific entries such as "argmem: read". LLVM-version-specific memory
|
||||
locations such as errnomem are encoded when supported by the linked LLVM.
|
||||
|
||||
Examples:
|
||||
Attribute.memory(ctx, "none")
|
||||
Attribute.memory(ctx, "read")
|
||||
Attribute.memory(ctx, "argmem: read")
|
||||
|
||||
<sub>C API: LLVMCreateEnumAttribute</sub>)")
|
||||
.def_static(
|
||||
"type",
|
||||
@@ -15314,6 +15472,20 @@ Examples:
|
||||
attrs.add("align", 16)
|
||||
|
||||
<sub>C API: LLVMAddAttributeAtIndex / LLVMAddCallSiteAttribute</sub>)")
|
||||
.def("add_memory", &LLVMAttributeAccessorWrapper::add_memory,
|
||||
"effects"_a = "none",
|
||||
R"(Add a memory(...) enum attribute.
|
||||
|
||||
The effects string accepts "none", "read", "write", "readwrite", or
|
||||
location-specific entries such as "argmem: read". LLVM-version-specific memory
|
||||
locations such as errnomem are encoded when supported by the linked LLVM.
|
||||
|
||||
Examples:
|
||||
attrs.add_memory("none")
|
||||
attrs.add_memory("read")
|
||||
attrs.add_memory("argmem: read")
|
||||
|
||||
<sub>C API: LLVMCreateEnumAttribute, LLVMAddAttributeAtIndex / LLVMAddCallSiteAttribute</sub>)")
|
||||
.def("add_type", &LLVMAttributeAccessorWrapper::add_type, "name"_a,
|
||||
"type"_a,
|
||||
R"(Add a type attribute by name.)")
|
||||
|
||||
@@ -74,6 +74,59 @@ def test_attribute_factories_and_function_accessors():
|
||||
assert mod.verify(), mod.verification_error
|
||||
|
||||
|
||||
def test_memory_attribute_helpers():
|
||||
with llvm.create_context() as ctx:
|
||||
with ctx.create_module("memory_attrs") as mod:
|
||||
void = ctx.types.void
|
||||
fn_ty = ctx.types.function(void, [])
|
||||
fn = mod.add_function("f", fn_ty)
|
||||
|
||||
memory_none = llvm.Attribute.memory(ctx, "none")
|
||||
assert memory_none.value == 0
|
||||
fn.attributes.add(memory_none)
|
||||
memory_attr = fn.attributes.get("memory")
|
||||
assert memory_attr is not None
|
||||
assert memory_attr.value == 0
|
||||
assert "memory(none)" in str(mod)
|
||||
|
||||
fn.attributes.remove("memory")
|
||||
fn.attributes.add_memory("read")
|
||||
memory_attr = fn.attributes.get("memory")
|
||||
assert memory_attr is not None
|
||||
assert memory_attr.value == llvm.Attribute.memory(ctx, "read").value
|
||||
assert "memory(read)" in str(mod)
|
||||
|
||||
fn.attributes.remove("memory")
|
||||
fn.attributes.add_memory("argmem: read")
|
||||
memory_attr = fn.attributes.get("memory")
|
||||
assert memory_attr is not None
|
||||
assert memory_attr.value == 1
|
||||
assert "memory(argmem: read)" in str(mod)
|
||||
|
||||
fn.attributes.remove("memory")
|
||||
complex_effects = (
|
||||
"memory(argmem: read, inaccessiblemem: write, other: readwrite)"
|
||||
)
|
||||
fn.attributes.add_memory(complex_effects)
|
||||
memory_attr = fn.attributes.get("memory")
|
||||
assert memory_attr is not None
|
||||
assert (
|
||||
memory_attr.value == llvm.Attribute.memory(ctx, complex_effects).value
|
||||
)
|
||||
ir = str(mod)
|
||||
assert "argmem: read" in ir
|
||||
assert "inaccessiblemem: write" in ir
|
||||
|
||||
try:
|
||||
fn.attributes.add_memory("argmem: frobnicate")
|
||||
except llvm.LLVMAssertionError as exc:
|
||||
assert "Unknown memory access effect" in str(exc)
|
||||
else:
|
||||
raise AssertionError("expected invalid memory access error")
|
||||
|
||||
assert mod.verify(), mod.verification_error
|
||||
|
||||
|
||||
def test_callsite_attribute_accessors():
|
||||
with llvm.create_context() as ctx:
|
||||
with ctx.create_module("callsite_attr_accessors") as mod:
|
||||
@@ -123,6 +176,9 @@ if __name__ == "__main__":
|
||||
test_attribute_factories_and_function_accessors()
|
||||
print("test_attribute_factories_and_function_accessors: PASSED")
|
||||
|
||||
test_memory_attribute_helpers()
|
||||
print("test_memory_attribute_helpers: PASSED")
|
||||
|
||||
test_callsite_attribute_accessors()
|
||||
print("test_callsite_attribute_accessors: PASSED")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user