Skip to content

Conversation

@soburi
Copy link
Member

@soburi soburi commented Jan 7, 2025

Problem

Currently, DeviceTree macros face limitations in setting flexible compile-time conditions. Specifically, COND_CODE_1 evaluates whether a given argument is 1 or not, but deriving this 1 using only macros can be challenging in certain cases.

Proposed Solution (Overview)

Introduce the capability to perform "calculations" using the preprocessor, allowing for flexible condition evaluation. The following example demonstrates the proposed functionality:

/* (1 + 2 == 9) is not true, so the 3rd argument will remains. */
COND_CODE_1(
    EXPR_IS_EQ(EXPR_ADD(EXPR_HEX(0x1), EXPR_HEX(0x2)), EXPR_HEX(0x9)),
    (zassert_unreachable("unreachable")),
    (zassert_true(1))
);

Proposed Solution (Details)

To address the problem, implement 32-bit arithmetic calculations with macros. Numbers will be represented as lists of 0 and 1 arguments (referred to as "bit args"). Using these bit args, the following operations will be provided:

  • Comparison operations:
    • EQ(= ,equal)
    • GT(>, greater than)
    • LT(<, less than)
    • GTE(>=, greater than equal)
    • LTE(<=, less than equal)
  • Bitwise operations:
    • OR(|)
    • AND(&)
    • XOR(^)
    • NOT(~)
  • Arithmetic operations:
    • ADD(+, addition)
    • SUB(-, subtraction)
    • MUL(*, multiplication)
    • DIV(/, division)
    • MOD(%, modulus)

This enables complex conditional expressions to be evaluated entirely within the preprocessor.
In other words, it is a proposal to implement a simple computer using only macros.

  • Integration with DeviceTree

    This functionality is designed to work alongside DeviceTree macros.
    My suggestion is to process devicetree_generated.h and automatically generate definitions that
    correspond to decimal numbers that appear; it is possible to include values ​​that can be obtained
    through the DeviceTree API in the processing. I think this covers the main use cases.

Considerations

  1. Performance

    Multiplication and division are inherently slower operations. For performance reasons, we may need to be avoided in CI environments.

  2. Limitations of Number Representations

    Due to the preprocessor's constraints, decimal and hexadecimal literals cannot be handled directly. They must first be converted into the internal bit args representation.
    Lookup tables containing up to 4096 entries for decimal and hexadecimal numbers have been defined to facilitate this. However, this approach has limitations in computation resources.

Internally, generating hexadecimal literals from bit args is easy, so outputting results in hexadecimal is unrestricted.

The limitations can be summarized as follows:

Format Input Support Output Support
bit args
Hexadecimal Table-dependent
Decimal Table-dependent Table-dependent

Benefits

This feature enables flexible conditional branching and complex calculations at compile time, enhancing the power and versatility of DeviceTree macros.

  • note
    Changes to some common parts have been grouped into a separate PR.
    I will rebase as appropriate.

#84191
#84190

@soburi soburi added the RFC Request For Comments: want input from the community label Jan 7, 2025
@soburi soburi changed the title Preprocessor-Based Arithmetic and Condition Evaluation in Zephyr [RFC] Preprocessor-Based Arithmetic and Condition Evaluation in Zephyr Jan 7, 2025
@soburi soburi force-pushed the preprocessor_calculation branch 3 times, most recently from da0d0c0 to 683e72b Compare January 19, 2025 02:45
@soburi soburi removed the RFC Request For Comments: want input from the community label Jan 19, 2025
@soburi soburi force-pushed the preprocessor_calculation branch from 683e72b to 8b93c98 Compare January 19, 2025 04:04
@soburi soburi added RFC Request For Comments: want input from the community DNM This PR should not be merged (Do Not Merge) labels Jan 19, 2025
@soburi soburi marked this pull request as ready for review January 19, 2025 07:17
@soburi soburi changed the title [RFC] Preprocessor-Based Arithmetic and Condition Evaluation in Zephyr [RFC] Preprocessor-Based Arithmetic and Condition Evaluation Jan 19, 2025
@bjarki-andreasen
Copy link
Contributor

bjarki-andreasen commented Jan 20, 2025

We currently have a bit of this implemented, but spread out in IS_EQ(), UTIL_INC(), UTIL_ADD etc. It would make sense to add the missing expressions, and move the existing expressions to the same namespace, like EXPR_EQ(), EXPR_OR() etc.

It will add many thousands of lines of code, but if we hide these macros behind a header like zephyr/expr/mul.h, zephyr/expr/div.h, zephyr/expr/eq.h etc. and not include these in zephyr/kernel.h or similar, it should not impact build times unless the macros are explicitly needed/included :)

@teburd
Copy link
Contributor

teburd commented Jan 21, 2025

I have concerns, though not likely worth -1ing over as we have already been going this direction for some time.

What happens if there's a typo or mistake in the macro? What kind of errors are reported by gcc? How do you go about debugging these easily?

Basically I'd like to see what the debugging story is for this sort of thing. Our biggest hurdle today for new people are macros which are not very easy to debug. This leads to our yearly developer survey showing DT/Kconfig/macros being a hurdle to using Zephyr. I find myself avoiding these things in Zephyr as I've personally burned hours debugging macros, and I'm constantly using and developing Zephyr as a maintainer!

At what point are we going to be concerned about our developer experience in Zephyr with all of these things?

@bjarki-andreasen
Copy link
Contributor

I have concerns, though not likely worth -1ing over as we have already been going this direction for some time.

What happens if there's a typo or mistake in the macro? What kind of errors are reported by gcc? How do you go about debugging these easily?

Basically I'd like to see what the debugging story is for this sort of thing. Our biggest hurdle today for new people are macros which are not very easy to debug. This leads to our yearly developer survey showing DT/Kconfig/macros being a hurdle to using Zephyr. I find myself avoiding these things in Zephyr as I've personally burned hours debugging macros, and I'm constantly using and developing Zephyr as a maintainer!

At what point are we going to be concerned about our developer experience in Zephyr with all of these things?

I do agree many macros in zephyr are near impossible to follow "intuitively", stuff like having to use IS_EQ() in a COND_CODE_1() because it has to expand to a constant with the value 1 is not exactly obvious, or the last to args of COND_CODE_1() having to be in brackets etc. etc. Both of these quirks cost me hours to :) But what to do?

@carlescufi
Copy link
Member

Architecture WG:

  • @bjarki-andreasen states that we already have some of those, but scattered around, and it would be good to have them in one place, until we have a better solution
  • @gmarull asks what the compiler output will be in case of error, because with Devicetree we already have a serious issue with this.
  • @nordicjm tells us that there was an earlier attempt at this, which at the time was rejected because of its complexity: Include: Simple preprocessing math util macros #75380

@teburd
Copy link
Contributor

teburd commented Jan 21, 2025

I have concerns, though not likely worth -1ing over as we have already been going this direction for some time.
What happens if there's a typo or mistake in the macro? What kind of errors are reported by gcc? How do you go about debugging these easily?
Basically I'd like to see what the debugging story is for this sort of thing. Our biggest hurdle today for new people are macros which are not very easy to debug. This leads to our yearly developer survey showing DT/Kconfig/macros being a hurdle to using Zephyr. I find myself avoiding these things in Zephyr as I've personally burned hours debugging macros, and I'm constantly using and developing Zephyr as a maintainer!
At what point are we going to be concerned about our developer experience in Zephyr with all of these things?

I do agree many macros in zephyr are near impossible to follow "intuitively", stuff like having to use IS_EQ() in a COND_CODE_1() because it has to expand to a constant with the value 1 is not exactly obvious, or the last to args of COND_CODE_1() having to be in brackets etc. etc. Both of these quirks cost me hours to :) But what to do?

The answer is straightforward but likely many will not like where it goes. Not using the C preprocessor for this stuff. Going a step further... encoding more information in this not-C-preprocessor, enough such that typos and errors don't get punted to the next build step until at long last you get a linker error that all goes back to a typo you had in some metadata file describing you wanting to enable a hardware peripheral and its driver. Basically re-look at all this stuff from orbit rather from the anthill on the ground trying to solve the next micro problem like this that comes along.

@bjarki-andreasen
Copy link
Contributor

I have concerns, though not likely worth -1ing over as we have already been going this direction for some time.
What happens if there's a typo or mistake in the macro? What kind of errors are reported by gcc? How do you go about debugging these easily?
Basically I'd like to see what the debugging story is for this sort of thing. Our biggest hurdle today for new people are macros which are not very easy to debug. This leads to our yearly developer survey showing DT/Kconfig/macros being a hurdle to using Zephyr. I find myself avoiding these things in Zephyr as I've personally burned hours debugging macros, and I'm constantly using and developing Zephyr as a maintainer!
At what point are we going to be concerned about our developer experience in Zephyr with all of these things?

I do agree many macros in zephyr are near impossible to follow "intuitively", stuff like having to use IS_EQ() in a COND_CODE_1() because it has to expand to a constant with the value 1 is not exactly obvious, or the last to args of COND_CODE_1() having to be in brackets etc. etc. Both of these quirks cost me hours to :) But what to do?

The answer is straightforward but likely many will not like where it goes. Not using the C preprocessor for this stuff. Going a step further... encoding more information in this not-C-preprocessor, enough such that typos and errors don't get punted to the next build step until at long last you get a linker error that all goes back to a typo you had in some metadata file describing you wanting to enable a hardware peripheral and its driver. Basically re-look at all this stuff from orbit rather from the anthill on the ground trying to solve the next micro problem like this that comes along.

Well, the "what we don't want" is straightforward, no C-preprocessor, the "what to replace it with", that's the answer I'm interested in :) The preprocessor is solving the not having to store every node, child, prop, etc. in C structures (like Linux), which I'm fairly sure is not an option. What else can we do?

@pdgendt
Copy link
Contributor

pdgendt commented Jan 22, 2025

I've been intrigued by https://gcc-python-plugin.readthedocs.io/en/latest/index.html to create some custom pre-processor shenanigans, but I think it's GCC only.

@soburi soburi force-pushed the preprocessor_calculation branch from b897c00 to ccef878 Compare February 11, 2025 20:28
@soburi soburi marked this pull request as draft February 11, 2025 20:36
@soburi soburi force-pushed the preprocessor_calculation branch from ccef878 to b6390fe Compare February 11, 2025 22:47
@soburi soburi force-pushed the preprocessor_calculation branch 2 times, most recently from 13a8cd4 to 392c4b8 Compare March 8, 2025 07:45
soburi added 2 commits March 16, 2025 10:30
Gets the specified number of elements from the beginning of the list.
This is the symmetircal operation as `GET_ARGS_LESS_N`.

Signed-off-by: TOKITA Hiroshi <[email protected]>
Add `GET_ARGS_FIRST_N` tests.

Signed-off-by: TOKITA Hiroshi <[email protected]>
@soburi soburi force-pushed the preprocessor_calculation branch 2 times, most recently from 47c8488 to ac2adf0 Compare March 16, 2025 02:10
soburi and others added 5 commits March 16, 2025 23:58
Generates decimal definitions from devicetree_generated.h
for use with the EXPR utils that will add.

Signed-off-by: TOKITA Hiroshi <[email protected]>
This commit introduces a set of macros (EXPR utilities)
designed for efficient compile-time evaluation of arithmetic,
bitwise, and logical operations.

This allows for flexible numeric processing even at compile time.
Combined with the DeviceTree macro, calculations can be performed
from the dt value.

Co-authored-by: Joel Hirsbrunner <[email protected]>
Signed-off-by: TOKITA Hiroshi <[email protected]>
We will re-implement IS_EQ, UTIL_INC, UTIL_DEC, and UTIL_X2,
which were implemented as LUTs.

Since it is still necessary to include large headers,
we will also extract these into util_expr_macro.h and include them
explicitly.

Signed-off-by: TOKITA Hiroshi <[email protected]>
Add test for `UTIL_INC`, `UTIL_DEC`, `UTIL_X2`
`IS_LT`, `IS_GT`, `IS_LE`, and `IS_GE`.

Co-authored-by: Joel Hirsbrunner <[email protected]>
Signed-off-by: TOKITA Hiroshi <[email protected]>
Add tests for EXPR utilities

Co-authored-by: Joel Hirsbrunner <[email protected]>
Signed-off-by: TOKITA Hiroshi <[email protected]>
@soburi soburi force-pushed the preprocessor_calculation branch from ac2adf0 to c92e8b3 Compare March 16, 2025 15:01
@57300 57300 removed their request for review April 1, 2025 18:57
@github-actions
Copy link

github-actions bot commented Jun 1, 2025

This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time.

@github-actions github-actions bot added the Stale label Jun 1, 2025
@github-actions github-actions bot closed this Jun 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: Base OS Base OS Library (lib/os) area: Build System area: Devicetree area: Testsuite Testsuite area: Utilities DNM This PR should not be merged (Do Not Merge) RFC Request For Comments: want input from the community Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants