This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/88391] New: Request - attribute for adding tags to functions
- From: "david at westcontrol dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Thu, 06 Dec 2018 13:39:37 +0000
- Subject: [Bug c/88391] New: Request - attribute for adding tags to functions
- Auto-submitted: auto-generated
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88391
Bug ID: 88391
Summary: Request - attribute for adding tags to functions
Product: gcc
Version: unknown
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c
Assignee: unassigned at gcc dot gnu.org
Reporter: david at westcontrol dot com
Target Milestone: ---
Sometimes it would be very useful to have "tags" on functions, which can be
used to restrict the kinds of functions that can call them or be called by
them. I can think of two use-cases from the embedded world, and one more
general case:
1. A function that can only be called when interrupts are disabled, has special
powers (such as direct access to locked or atomic data, at least on single-core
systems) and special responsibilities (it should be fast, should not block,
etc.). It should only be called by a function that disables interrupts.
2. A function that needs to run from ram should not call other functions that
are not also in ram. This might be for programming code flash, or perhaps to
minimise speed variation in a real-time system with slow external flash.
3. A function that should only be called when you have a certain lock or
resource.
There are many other uses possible uses. I think that a few general attributes
could allow this to be handled in the C (and C++) front-end, with the compiler
issuing warnings or errors when the "tag" rules are broken. It would not
affect code generation, name mangling, etc. - it's just a mechanism to help
spot incorrect code. I am not suggesting that we have attributes that actually
disable interrupts, or place code in ram (though these might be nice!) - all
embedded targets already have mechanisms for that, such as "section"
attributes, and we don't need to duplicate functionality that can be handled by
a macro or two.
My suggestions for function attributes are:
1. __attribute__((tagged("tagname")))
Mark a function as being tagged with "tagname" (or multiple tag names).
2. __attribute__((caller_tag("tagname")))
Mark a function as only being callable by a function with this tag.
3. __attribute__((callee_tag("tagname")))
Mark a function as only being allowed to call functions with this tag.
4. __attribute__((caller_notag("tagname")))
Mark a function as not being callable by a function with this tag.
5. __attribute__((callee_notag("tagname")))
Mark a function as not being allowed to call functions with this tag.
There would also need to be a statement attribute:
__attribute__((tag("tagname")))
Mark the statement as having this tag (and therefore being allowed to call
matching "caller_tag" functions, and not allowed to call matching
"caller_notag" functions).
Example 1
=========
__attribute__((caller_tag("irq_disabled"), tagged("irq_disabled")))
void criticalFunction(void)
{
// Some code that should never be interrupted
}
void normalFunction(void) {
// Normal code
disableInterrupts();
__attribute__((tag("irq_disabled"))) criticalFunction();
enableInterrupts();
}
Example 2
=========
__attribute__((tagged("in_ram")), callee_tag("in_ram")))
__attribute__((section("ramcode")))
void flashCommand(uint32_t cmd)
{
// Write command to flash programming peripheral
}
__attribute__((tagged("in_ram")), callee_tag("in_ram")))
__attribute__((section("ramcode")))
void flashErase(void)
{
flashCommand(0x12345678);
}
void updateSoftware(void) {
downloadNewImage();
flashErase();
flashProgram();
}
Example 3
=========
__attribute__((caller_notag("got_lock")))
void getLock(void)
{
mtx_lock(&mutex);
}
__attribute__((caller_tag("got_lock")))
void releaseLock(void)
{
mtx_unlock(&mutex);
}
__attribute__((caller_tag("got_lock"), tagged("got_lock")))
void accessData(void)
{
// Use critical data
}
void normalCode(void)
{
// Collect data
getLock();
__attribute__((tag("got_lock")) {
accessData();
releaseLock(); // Still tag "got_lock"
}
}
One big question is the compiler target library. In small embedded systems,
apparently simple operations like "x = y / z;" may result in a library call if
the processor does not have hardware instructions for the operation. It is
important in "callee_tag" functions that any such library calls also issue a
warning.