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
5852void 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
114101To use this module, just do: **`features = import('features')`**. The
115102following functions will then be available as methods on the object
116103with the name `features`. You can, of course, replace the name `features`
117104with 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
178185Example:
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