Skip to content

Commit 7768ba2

Browse files
committed
DOC: Improve the overview, functions signatures
1 parent 1f8351f commit 7768ba2

File tree

1 file changed

+184
-123
lines changed

1 file changed

+184
-123
lines changed

docs/markdown/Features-module.md

Lines changed: 184 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,24 @@
22

33
## Overview
44

5-
Dealing with numerous CPU features through C and C++ compilers is a challenging task,
6-
especially when aiming to support massive amount of CPU features for various architectures and multiple compilers
7-
Additionally, supporting both baseline features and additional features dispatched at runtime presents another dilemma.
5+
Dealing with a numerous of CPU features within C and C++ compilers poses intricate challenges,
6+
particularly when endeavoring to support an extensive set of CPU features across diverse
7+
architectures and multiple compilers. Furthermore, the conundrum of accommodating
8+
both fundamental features and supplementary features dispatched at runtime
9+
further complicates matters.
810

9-
Another issue that may arise is simplifying the implementations of generic interfaces while keeping the dirty work laid
10-
on the build system rather than using nested namespaces or recursive sources, relying on pragma or compiler targets attributes
11-
on top of complicated precompiled macros or meta templates, which can make debugging and maintenance difficult.
11+
In addition, the task of streamlining generic interface implementations often leads to the necessity
12+
of intricate solutions, which can obscure code readability and hinder debugging and maintenance.
13+
Nested namespaces, recursive sources, complex precompiled macros, or intricate meta templates
14+
are commonly employed but can result in convoluted code structures.
1215

13-
While this module doesn't force you to follow a specific approach, it instead paves the way to count on a
14-
practical multi-targets solution that can make managing CPU features easier and more reliable.
16+
Enter the proposed module, which offers a simple pragmatic multi-target solution to
17+
facilitate the management of CPU features with heightened ease and reliability.
1518

16-
In a nutshell, this module helps you deliver the following concept:
19+
In essence, this module introduces the following core principle:
1720

1821
```C
19-
// Brings the headers files of enabled CPU features
22+
// Include header files for enabled CPU features
2023
#ifdef HAVE_SSE
2124
#include <xmmintrin.h>
2225
#endif
@@ -32,20 +35,10 @@ In a nutshell, this module helps you deliver the following concept:
3235
#ifdef HAVE_SSE41
3336
#include <smmintrin.h>
3437
#endif
35-
#ifdef HAVE_POPCNT
36-
#ifdef _MSC_VER
37-
#include <nmmintrin.h>
38-
#else
39-
#include <popcntintrin.h>
40-
#endif
41-
#endif
42-
#ifdef HAVE_AVX
43-
#include <immintrin.h>
44-
#endif
45-
4638
#ifdef HAVE_NEON
4739
#include <arm_neon.h>
4840
#endif
41+
// ... (similar blocks for other features)
4942

5043
// MTARGETS_CURRENT defined as compiler argument via `features.multi_targets()`
5144
#ifdef MTARGETS_CURRENT
@@ -55,19 +48,13 @@ In a nutshell, this module helps you deliver the following concept:
5548
#define TARGET_SFX(X) X
5649
#endif
5750

51+
// Core function utilizing feature-specific implementations
5852
void TARGET_SFX(my_kernal)(const float *src, float *dst)
5953
{
60-
#ifdef HAVE_AVX512F
61-
// defeintions for implied features alawys present
62-
// no matter the compiler is
63-
#ifndef HAVE_AVX2
64-
#error "Alawys defined"
65-
#endif
66-
#elif defined(HAVE_AVX2) && defined(HAVE_FMA3)
67-
#ifndef HAVE_AVX
68-
#error "Alawys defined"
69-
#endif
70-
#elif defined(HAVE_SSE41)
54+
// Feature-based branching
55+
#if defined(HAVE_SSE41)
56+
// Definitions for implied features always present,
57+
// regardless of the compiler used.
7158
#ifndef HAVE_SSSE3
7259
#error "Alawys defined"
7360
#endif
@@ -93,87 +80,107 @@ void TARGET_SFX(my_kernal)(const float *src, float *dst)
9380
}
9481
```
9582
96-
From the above code we can deduce the following:
97-
- The code is written on top features based definitions rather than counting clusters or
98-
features groups which gives the code more readability and flexibility.
83+
Key Takeaways from the Code:
9984
100-
- Avoid using compiler built-in defeintions no matters the enabled arguments allows you
101-
to easily manage the enabled/disabled features and to deal with any kind of compiler or features.
102-
Since compilers like MSVC for example doesn't provides defeintions for all CPU features.
85+
- The code employs feature-centric definitions, enhancing readability
86+
and flexibility while sidestepping the need for feature grouping.
10387
104-
- The code is not aware of how its going to be build it, that gives the code a great prodiblity to
105-
manage the generated objects which allow raising the baseline features at any time
106-
or reduce and increase the additional dispatched features without changing the code.
88+
- Notably, compiler-built definitions are circumvented, thereby affording
89+
seamless management of enabled/disabled features and accommodating diverse compilers and feature sets.
90+
Notably, this accommodates compilers like MSVC, which might lack definitions for specific CPU features.
10791
108-
- Allow building a single source multiple of times simplifying the implementations
109-
of generic interfaces.
92+
- The code remains agnostic about its build process, granting it remarkable versatility
93+
in managing generated objects. This empowers the ability to elevate baseline features at will,
94+
adjust additional dispatched features, and effect changes without necessitating code modifications.
11095
96+
- The architecture permits the construction of a singular source, which can be compiled multiple times.
97+
This strategic approach simplifies the implementation of generic interfaces, streamlining the development process.
11198
112-
## Usage
11399
100+
## Usage
114101
To use this module, just do: **`features = import('features')`**. The
115102
following functions will then be available as methods on the object
116103
with the name `features`. You can, of course, replace the name `features`
117104
with anything else.
118105
119106
### features.new()
107+
108+
**Signature**
120109
```meson
121-
features.new(string, int,
122-
implies: FeatureOject | FeatureObject[] = [],
123-
group: string | string[] = [],
124-
detect: string | {} | (string | {})[] = [],
125-
args: string | {} | (string | {})[] = [],
126-
test_code: string | File = '',
127-
extra_tests: {string: string | file} = {},
128-
disable: string = ''
129-
) -> FeatureObject
110+
FeatureObject features.new(
111+
# Positional arguments:
112+
str,
113+
int,
114+
# Keyword arguments:
115+
implies : FeatureOject | list[FeatureObject] = [],
116+
group : str | list[str] = [],
117+
args : str | dict[str] | list[str | dict[str]] = [],
118+
detect : str | dict[str] | list[str | dict[str]] = [],
119+
test_code : str | File = '',
120+
extra_tests : dict[str | file] = {},
121+
disable : str = ''
122+
)
130123
```
131124

132-
This function plays a crucial role in the Features Module as it creates
133-
a new `FeatureObject` instance that is essential for the functioning of
134-
other methods within the module.
135-
136-
It takes two required positional arguments. The first one is the name of the feature,
137-
and the second is the interest level of the feature, which is used by
138-
the sort operation and priority of conflicting arguments.
139-
The rest of the kwargs arguments are explained as follows:
140-
141-
* `implies` **FeatureOject | FeatureObject[] = []**: One or an array of features objects
142-
representing predecessor features.
143-
144-
* `group` **string | string[] = []**: Optional one or an array of extra features names
145-
to be added as extra definitions that can passed to source.
146-
147-
* `args` **string | {} | (string | {})[] = []**: Optional one or an array of compiler
148-
arguments that are required to be enabled for this feature.
149-
Each argument can be a string or a dictionary that holds four items that allow dealing
150-
with the conflicts of the arguments of implied features:
151-
- `val` **string**: string, the compiler argument.
152-
- `match` **string | empty**: regex to match the arguments of implied features
153-
that need to be filtered or erased.
154-
- `mfilter` **string | empty**: regex to find certain strings from the matched arguments
155-
to be combined with `val`. If the value of `mfilter` is empty or undefined,
156-
any matches triggered by the value of `match` will not be combined with `val`.
157-
- `mjoin` **string | empty**: a separator used to join all the filtered arguments.
158-
If it's empty or undefined, the filtered arguments will be joined without a separator.
159-
160-
* `detect` **string | {} | (string | {})[] = []**: Optional one or an array of features names
161-
that required to be detect on runtime. If no features sepecfied then the values of `group`
162-
will be used if its provides otherwise the name of the feature will be used instead.
163-
Similar to `args`, each feature name can be a string or a dictionary that holds four items
164-
that allow dealing with the conflicts of the of implied features names.
165-
See `features.multi_targets()` or `features.test()` for more clearfications.
166-
167-
* `test_code` **string | File = ''**: Optional C/C++ code or the path to the source
168-
that needs to be tested against the compiler to consider this feature is supported.
169-
170-
* `extra_tests` **{string: string | file} = {}**: Optional dictionary holds extra tests where
171-
the key represents the test name, which is also added as a compiler definition if the test succeeded,
172-
and the value is C/C++ code or the path to the source that needs to be tested against the compiler.
173-
174-
* `disable` **string = ''**: Optional string to consider this feature disabled.
175-
176-
Returns a new instance of `FeatureObject`.
125+
The `features.new()` function plays a pivotal role within the Features Module.
126+
It creates a fresh mutable instance of FeatureObject, an essential component
127+
for facilitating the functionality of other methods within the module.
128+
129+
This function requires two positional arguments. The first argument pertains to the feature's name,
130+
while the second one involves the interest level of the feature. This interest level governs sorting
131+
operations and aids in resolving conflicts among arguments with differing priorities.
132+
133+
Additional keyword arguments are elaborated as follows:
134+
135+
* `implies` **FeatureOject | list[FeatureObject]**:
136+
An optional single feature object or an array of feature objects
137+
representing predecessor features. It is noteworthy that two features can imply each other.
138+
Such mutual implication can prove beneficial in addressing compatibility concerns with compilers or hardware.
139+
For instance, while some compilers might require enabling both `AVX2` and `FMA3` simultaneously,
140+
others may permit independent activation.
141+
142+
* `group` **str | list[str]**:
143+
An optional single feature name or an array of additional feature names. These names are appended as
144+
supplementary definitions that can be passed to the source.
145+
146+
* `args` **str | dict[str] | list[str | dict[str]] = []**:
147+
An optional single compiler argument or an array of compiler arguments that must be enabled for
148+
the corresponding feature. Each argument can be a string or a dictionary containing four elements.
149+
These elements handle conflicts arising from arguments of implied features or when concatenating two features:
150+
- `val` **str**:
151+
The compiler argument.
152+
- `match` **str | empty**:
153+
A regular expression to match arguments of implied features that necessitate filtering or removal.
154+
- `mfilter` **str | empty**:
155+
A regular expression to identify specific strings from matched arguments.
156+
These strings are combined with `val`. If `mfilter` is empty or undefined,
157+
matched arguments from `match` will not be combined with `val`.
158+
- `mjoin` **str | empty**:
159+
A separator used to join all filtered arguments.
160+
If undefined or empty, filtered arguments are joined without a separator.
161+
162+
* `detect` **str | dict[str] | list[str | dict[str]] = [] = []**:
163+
An optional single feature name or an array of feature names to be detected at runtime.
164+
If no feature names are specified, the values from the `group` will be used.
165+
If the `group` doesn't provide values, the feature's name is employed instead.
166+
Similar to args, each feature name can be a string or a dictionary with four
167+
elements to manage conflicts of implied feature names.
168+
Refer to `features.multi_targets()` or `features.test()` for further clarity.
169+
170+
* `test_code` **str | File = ''**:
171+
An optional block of C/C++ code or the path to a source file for testing against the compiler.
172+
Successful compilation indicates feature support.
173+
174+
* `extra_tests` **dict[str | file] = {}**:
175+
An optional dictionary containing extra tests. The keys represent test names,
176+
and the associated values are C/C++ code or paths to source files.
177+
Successful tests lead to compiler definitions based on the test names.
178+
179+
* `disable` **str = ''**:
180+
An optional string to mark a feature as disabled.
181+
If the string is empty or undefined, the feature is considered not disabled.
182+
183+
The function returns a new instance of `FeatureObject`.
177184

178185
Example:
179186

@@ -211,49 +218,103 @@ ASIMDFHM = features.new(
211218
args: {'val': '-march=armv8.2-a+fp16fml', 'match': '-march=.*', 'mfilter': '\+.*'}
212219
)
213220
```
214-
215221
### features.test()
222+
**Signature**
216223
```meson
217-
features.test(FeatureObject...,
218-
anyfet: bool = false,
219-
force_args: string | string[] | empty = empty,
220-
compiler: Compiler | empty = empty,
221-
cached: bool = true,
222-
) -> {}
224+
[bool, Dict] features.test(
225+
# Positional arguments:
226+
FeatureObject...
227+
# Keyword arguments:
228+
anyfet : bool = false,
229+
force_args : str | list[str] | empty = empty,
230+
compiler : Compiler | empty = empty,
231+
cached : bool = true
232+
)
223233
```
224234

225-
Test a one or set of features against the compiler and returns a dictionary
226-
contains all required information that needed for building a source that
227-
requires these features.
235+
Test one or a list of features against the compiler and retrieve a dictionary containing
236+
essential information required for compiling a source that depends on these features.
237+
238+
A feature is deemed supported if it fulfills the following criteria:
239+
240+
- It is not marked as disabled.
241+
- The compiler accommodates the features arguments.
242+
- Successful compilation of the designated test file or code.
243+
- The implied features are supported by the compiler, aligning with
244+
the criteria mentioned above.
245+
246+
This function requires at least one feature object as positional argument,
247+
and additional keyword arguments are elaborated as follows:
248+
249+
- `anyfet` **bool = false**:
250+
If set to true, the returned dictionary will encompass information regarding
251+
the maximum features available that are supported by the compiler.
252+
This extends beyond the specified features and includes their implied features.
253+
- `force_args` **str | list[str] | empty = empty**:
254+
An optional single compiler argument or an array of compiler arguments to be
255+
employed instead of the designated features' arguments when testing the
256+
test code against the compiler. This can be useful for detecting host features.
257+
- `compiler` **Compiler | empty = empty**:
258+
A Compiler object to be tested against. If not defined,
259+
the function will default to the standard C or C++ compiler.
260+
- `cached`: **bool = true**:
261+
Enable or disable the cache. By default, the cache is enabled,
262+
enhancing efficiency by storing previous results.
263+
264+
This function returns a list of two items. The first item is a boolean value,
265+
set to true if the compiler supports the feature and false if it does not.
266+
The second item is a dictionary that encapsulates the test results, outlined as follows:
267+
268+
**structure**
269+
```meson
270+
{
271+
'target_name' : str,
272+
'prevalent_features' : list[str],
273+
'features' : list[str],
274+
'args' : list[str],
275+
'detect' : list[str],
276+
'defines' : list[str],
277+
'undefines' : list[str],
278+
'is_supported' : bool,
279+
'is_disabled' : bool,
280+
'fail_reason' : str
281+
}
282+
```
228283

229284
### features.multi_targets()
285+
**Signature**
230286
```meson
231-
features.multi_targets(string, (
232-
str | File | CustomTarget | CustomTargetIndex |
233-
GeneratedList | StructuredSources | ExtractedObjects |
234-
BuildTarget
235-
)...,
236-
dispatch: (FeatureObject | FeatureObject[])[] = [],
237-
baseline: empty | FeatureObject[] = empty,
238-
prefix: string = '',
239-
compiler: empty | compiler = empty,
240-
cached: bool = True
241-
) [{}[], StaticLibrary[]]
287+
TargetsObject features.multi_targets(
288+
# Positional arguments:
289+
str,
290+
(
291+
str | File | CustomTarget | CustomTargetIndex |
292+
GeneratedList | StructuredSources | ExtractedObjects |
293+
BuildTarget
294+
)...,
295+
# Keyword arguments:
296+
dispatch : FeatureObject | list[FeatureObject] = [],
297+
baseline : empty | list[FeatureObject] = empty,
298+
prefix : str = '',
299+
keep_sort : bool = false,
300+
compiler : empty | compiler = empty,
301+
cached : bool = True,
302+
**known_stlib_kwargs
303+
)
242304
```
243305

244-
245306
### features.sort()
307+
**Signature**
246308
```meson
247-
features.sort(FeatureObject..., reverse: bool = false) : FeatureObject[]
248309
```
249310

250311
### features.implicit()
312+
**Signature**
251313
```meson
252-
features.implicit(FeatureObject...) : FeatureObject[]
253314
```
254315

255316
### features.implicit_c()
317+
**Signature**
256318
```meson
257-
features.implicit_c(FeatureObject...) : FeatureObject[]
258319
```
259320

0 commit comments

Comments
 (0)