-
Notifications
You must be signed in to change notification settings - Fork 754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SYCL][E2E] Add logic to react to REQUIRED
/UNSUPPORTED
in build-only
#16725
base: sycl
Are you sure you want to change the base?
Conversation
This pr is the logic from #16637, but only with support for |
sycl/test-e2e/TriStateExpr.py
Outdated
self.assertTrue( | ||
E2EExpr.evaluate("!rt_feature", {"rt_feature"}, True, True) | ||
) | ||
self.assertTrue( | ||
E2EExpr.evaluate("!!rt_feature", {"rt_feature"}, True, True) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, there are far too many tests with {"rt_feature"}
when run time features are to be ignored. We don't expect the set of "set" features to contain them, so why are we testing that scenario mostly?
I can understand having a couple of tests for completeness/to document behavior, but having too many is counterproductive and might confuse the reader about the purpose of this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of deleting these, I just made the available features list empty. Does that work? otherwise I can delete them.
My intention with these was to show that ignore values propagate up the expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quick initial review, will look more indepth once andrei's feedback is addressed
sycl/test-e2e/format.py
Outdated
if self.getMissingRequiredFeaturesFromList(features, test.requires, build_only_mode=True): | ||
continue | ||
if self.getMatchedFromList(features, test.unsupported, build_only_mode=True): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these functions are called get
but are being used as has
it seems, should we rename?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasMissingRequiredFeatures
and hasUnsupportedFeatures
? or what were you thinking. One thing to note that brought some confusion in the past is the fact that these dont report the specific features that were missing to pass the expression, rather it returns the clauses that did not pass.
Also should note that getMissingRequiredFeaturesFromList
is named after a function of the same name in the lit implementation. As for getMatchedFromList
that one doesnt have an exact corresponding function, but there is getUnsupportedFeatures
which is similar, but does not have an argument to pass in the features available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we ever use the return value's contents or is it just empty or not? if its only empty or not i think we should rename to has...
, otherwise current is fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One small caveat, the getMatchedFromList
function is also used for determining XFAILs, so im not entirely sure hasUnsupportedFeatures
would be entirely accurate.
This
False
implies that we're processingUNSUPPORTED
, we need to modify the name of this function to indicate that now. Doeslit.Test
have some name that we can copy?
@aelovikov-intel tagging you here, because I think these comments are asking for the same thing, going to mark the other as resolved and we can continue discussion here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, keeping it close to lit.Test
or whatever the names came from is more important than renaming just because we don't use the result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One small caveat, the getMatchedFromList function is also used for determining XFAILs, so im not entirely sure hasUnsupportedFeatures would be entirely accurate.
In this case, I'd avoid hardcoding False
in the body and pass it as an additional parameter. Yes, it's kind of useless, but it would make interface more consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah im not super familiar with the implementation of lit so if the function names are standard then its fine
sycl/test-e2e/lit.cfg.py
Outdated
target = config.backend_to_target[be] | ||
features.add(target) | ||
config.sycl_triples.add(target) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backend is the underlying runtime that sycl is implemented on, right? Isn't it possible to have the same backend support multiple targets?. For example using OpenCL on AMD or NVIDIA (theoretically of course since practically they would have to support the extensions sycl requires).
I think if backend_to_target
would be backend_default_target
then it leaves the design open to add an override via some option like --param backend_targets_<be>=<target1>,<target2>
.
Or maybe this is what architectures
is for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if
backend_to_target
would bebackend_default_target
then it leaves the design open to add an override via some option like--param backend_targets_<be>=<target1>,<target2>
.
Or maybe this is whatarchitectures
is for?
Yeah, something like that could be possible to do by overriding/changing the dictionary used for the mappings. Although I do still prefer the x_to_y
naming scheme for these mapping dictionaries, since it groups them together with consistent naming, and I don't think it necessarily stops us from redefining them in the future through parameters like you suggest.
sycl/test-e2e/lit.cfg.py
Outdated
config.target_to_triple = { | ||
"target-spir": "spir64", | ||
"target-nvidia":"nvptx64-nvidia-cuda", | ||
"target-amd":"amdgcn-amd-amdhsa", | ||
"target-native_cpu":"native_cpu", | ||
} | ||
config.triple_to_target = {v:k for k,v in config.target_to_triple.items()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between a "target" and the "triple" it corresponds to, given there's a 1:1 mapping between them? Is target just a short name for the triple? Is it an alias to allow changing the target triples without updating all the tests that refer to it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Short name was part of the motivation (at least for me). Also, Nick mentioned that there is some work to have two different spir triples (spir64 and spirv, I think).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor naming suggestion, otherwise looks great.
@@ -2,8 +2,7 @@ | |||
// It tests if the target triples can be specified with any order. | |||
// The test is repeated for per_kernel device code splitting. | |||
// | |||
// REQUIRES: cuda || hip || native_cpu | |||
// REQUIRES: build-and-run-mode | |||
// REQUIRES: target-nvidia || target-amd || target-native_cpu |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// REQUIRES: target-nvidia || target-amd || target-native_cpu | |
// REQUIRES: (target-nvidia || target-amd || target-native_cpu) && target-spir |
"false", | ||
} | ||
|
||
def __init__(self, string, variables, build_only_mode, findal_unknown_value): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def __init__(self, string, variables, build_only_mode, findal_unknown_value): | |
def __init__(self, string, variables, build_only_mode, final_unknown_value): |
here and everywhere below.
|
||
|
||
class TestE2EExpr(unittest.TestCase): | ||
def test_basic(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see this:
BuildOnly = True
BuildAndRun = False
RequiresDirective = True
UnsupportedOrXFAILDirective = False
self.assertTrue(E2EExpr.evaluate(<expr>, <features>, BuildOnly, RequiresDirective)
And now that I spelled it out, it can be even better (C++-like syntax, names to be improved):
auto BuildOnlyRequires = [](auto expr, auto features) { ... };
...
self.assertTrue(BuildOnlyRequires("linux", {"linux", "rt_feature"}))
self.assertFalse(BuildOnlyUnsupported("rt_feature", {}))
Also, since we want these name improvements in the tests, we might as well want to propagate them all the way to the interfaces somehow.
) | ||
self.assertTrue( | ||
E2EExpr.evaluate( | ||
"rt_feature", {"rt_feature"}, True, final_unknown_value=True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we assert/raise an error instead?
) | ||
self.assertTrue( | ||
E2EExpr.evaluate( | ||
"linux && !(windows || rt_feature)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have hard time mapping that to a possible REQURIES
directive. I think changing "windows" to something non-OS might help, but even then I'm not sure what would be the best example. Same for the next one.
) | ||
self.assertFalse( | ||
E2EExpr.evaluate( | ||
"linux && (windows && rt_feature)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change "windows" to something that can make sense in real world. Same in the next one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no high level flags from me, will leave in-depth review to andrei/others who are more familiar with lit's internals
Adds logic for evaluating
REQUIRES
/UNSUPPORTED
statements onbuild-only
mode by "ignoring" features in these statements that do not affect the compilation (Everything other than OS, triple, and SDK/libraries).More precisely, the ability to ignore features is implemented by extending the boolean expressions to use a third boolean state -
ignore
. If a particular sub-expression includes anignore
, and if its result could be changed by setting thatignore
to eithertrue
orfalse
, then the result of that sub-expression is set toignore
. For exampleignore || true = true
, butignore || false = ignore
, this is because in the first example there would be no way to set the ignore such that the result is anything other thantrue
, while in the second example the result is dependent on the "actual" value of theignore
.If the resulting value of a
REQUIRES
predicate isignore
we interpret that astrue
(The requirements were met), on the other hand forUNSUPPORTED
predicates we would interpretignore
asfalse
instead (The unsupported criteria was not met).The triples that can be used for a given test are then selected by evaluating the
REQUIRES
/UNSUPPORTED
with the set of features + the target feature corresponding to each triple (mirroring the way we select devices), while ignoring all features that do not affect compilation.Similarly to how
XFAIL
is handled when using multiple devices if we are compiling for multiple triples, and a single triple is marked asXFAIL
, then it is treated as unsupported instead.The available target triples in
build-only
mode is determined through the use of thesycl_triples
lit param (i.e.,--param sycl_triples=spir;amd
). This is currently set to onlyspir
, as the changes in test markup included in this pr do not take into account building fornvptx
andamdgcn
triples. Inrun-only
andfull
mode the available triples are determined via the available devices.