You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When generating the resolver function for function multiversioning, a mechanism is needed to retrieve the environment information.
To achieve this goal, several steps need to be taken:
Collect the required extensions for a particular function.
Transform these required extensions into a platform-dependent form.
Query whether the environment fulfills these requirements during runtime.
Step 1 is handled by the compiler, while step 3 must follow the necessary steps from the platform during runtime.
This RFC aims to propose how the compiler and runtime function can tackle problem 2.
Here is a example
__attribute__((target_clones("default", "arch=rv64gcv"))) int bar() {
return 1;
}
In this example, there are two versions of function bar. One for default, another for "rv64gcv".
If environment fullfills the requirement, then bar could use the version arch=rv64gcv. Otherwise, It invokes with default version.
This process be controlled by the ifunc resolver function.
ptr bar.resolver() {
if (isFullFill(...))
return "bar.arch=rv64gcv";
return bar.default;
}
The isFullFill should available during the program runtime.
The version arch=rv64gcv require
i, m, a, f, d, c, v, zicsr, zifencei, zve32f, zve32x, zve64d, zve64f, zve64x, zvl128b, zvl32b, zvl64b,
The problem 2 is about where to maintain the relationship between extension names and platform-dependent probe forms.
Here are three possible approach to achieve goal.
Encode all required extensions into a string format, then let the platform implement its own probe approach based on the string inside the runtime function. This approach maintains the relationship between extension names and platform-dependent probe forms inside the runtime function.
ptr bar.resolver() {
if (isFullFill("i;m;a;f;d;c;v;zicsr;zifencei;zve32f;zve32x;zve64d;zve64f;zve64x;zvl128b;zvl32b;zvl64b"))
return bar.arch=rv64gcv;
return bar.default;
}
bool isFullFill(char *ReqExts) {
if (isLinux())
return doLinuxRISCVExtensionProbe(ReqExts);
if (isFreeBSD())
return doFreeBSDRISCVExtensionProbe(ReqExts);
// Other platform
....
return false;
}
Pros
Human readable
Relatively high portability
Provides a uniform interface for all platforms
Cons
Requires extra effort for string processing in the runtime function.
Encode all required extensions into a compiler-defined key, then let the platform implement its own probe approach inside the runtime. This approach maintains the relationship between the compiler-defined key for extensions and the platform-dependent probe form inside the runtime function.
// Assume compiler define
// i -> 1
// m -> 2
...
ptr bar.resolver() {
if (isFullFill([1, 2, 3, 8, ...], length))
return bar.arch=rv64gcv;
return bar.default;
}
bool isFullFill(int *ReqExts, length) {
if (isLinux())
return doLinuxRISCVExtensionProbe(ReqExts, length);
if (isFreeBSD())
return doFreeBSDRISCVExtensionProbe(ReqExts, length);
// Other platform
....
return false;
}
Pros
Doesn't require string processing during runtime
Provides a uniform interface for all platforms
Cons
Requires maintaining the relationship between the compiler-defined key for extensions and the concrete extension names inside runtime function.
Define a different runtime function for each platform and construct any necessary information during compilation time if necessary for the platform. This approach maintains the relationship between extension names and platform-dependent probe forms inside the compiler.
// If compiler compile for linux, then use bar.resolver.linux
ptr bar.resolver.linux() {
if (isFullFillLinux(LinuxProbeObject))
return bar.arch=rv64gcv;
return bar.default;
}
ptr bar.resolver.freebsd() {
if (isFullFillFreeBSD(FreeBSDProbeObject))
return bar.arch=rv64gcv;
return bar.default;
}
// Other platform bar.resolver
...
bool isFullFillLinux(LinuxProbeObject Obj) {
return doLinuxProbe(Obj);
}
bool isFullFillFreeBSD(FreeBSDProbeObject Obj) {
return doFreeBSDProbe(Obj);
}
// Other platform isFullFill
...
Pros
Relatively simple implementation for the runtime function
Cons
Does not provide a uniform interface for all platforms
The text was updated successfully, but these errors were encountered:
You've gone down the route of 2, which makes sense to me.
One thing you've glossed over is part of the third stage: each function version is for a specific set of features, but nothing requires that each set of features is a strict super-/sub-set of those for other versions of the same function. Does it matter that there's not a defined order for choosing between the two sets?
For example:
The default version of f just has I, no extensions.
One version of x is for when you have I and the C extension.
One version of x is for when you have I and the F extension.
The CPU you're running on implements I, C and F - which version of x do you choose?
Maybe it's fine to leave it undefined? Maybe it's fine to be reasonably nondeterministic? I'm not sure. What do you think?
When generating the resolver function for function multiversioning, a mechanism is needed to retrieve the environment information.
To achieve this goal, several steps need to be taken:
Step 1 is handled by the compiler, while step 3 must follow the necessary steps from the platform during runtime.
This RFC aims to propose how the compiler and runtime function can tackle problem 2.
Here is a example
In this example, there are two versions of function
bar
. One for default, another for "rv64gcv".If environment fullfills the requirement, then
bar
could use the versionarch=rv64gcv
. Otherwise, It invokes withdefault
version.This process be controlled by the
ifunc
resolver function.The version
arch=rv64gcv
requireThe problem 2 is about where to maintain the relationship between extension names and platform-dependent probe forms.
Here are three possible approach to achieve goal.
The text was updated successfully, but these errors were encountered: