Skip to content

Commit 93f4773

Browse files
authored
[SYCL] Fix mechanism to throw exception when placeholder accessor passed to a command (#10110)
Current mechanism for checking whether an exception should be thrown if a placeholder accessor was passed to a command was not working properly under certain conditions, either by not throwing when required, or throwing when unrequired. Additionally, following clarifications in KhronosGroup/SYCL-Docs#434, this PR makes default-constructed accessors to stop being placeholders. --------- Signed-off-by: Maronas, Marcos <[email protected]>
1 parent d430bef commit 93f4773

File tree

2 files changed

+79
-15
lines changed

2 files changed

+79
-15
lines changed

sycl/source/handler.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,22 +122,23 @@ event handler::finalize() {
122122

123123
// According to 4.7.6.9 of SYCL2020 spec, if a placeholder accessor is passed
124124
// to a command without being bound to a command group, an exception should
125-
// be thrown. There should be as many requirements as unique accessors,
126-
// otherwise some of the accessors are unbound, and thus we throw.
125+
// be thrown.
127126
{
128-
// A counter is not good enough since we can have the same accessor several
129-
// times as arg
130-
std::unordered_set<void *> accessors;
131127
for (const auto &arg : MArgs) {
132128
if (arg.MType != detail::kernel_param_kind_t::kind_accessor)
133129
continue;
134130

135-
accessors.insert(arg.MPtr);
131+
detail::Requirement *AccImpl =
132+
static_cast<detail::Requirement *>(arg.MPtr);
133+
if (AccImpl->MIsPlaceH) {
134+
auto It = std::find(CGData.MRequirements.begin(),
135+
CGData.MRequirements.end(), AccImpl);
136+
if (It == CGData.MRequirements.end())
137+
throw sycl::exception(make_error_code(errc::kernel_argument),
138+
"placeholder accessor must be bound by calling "
139+
"handler::require() before it can be used.");
140+
}
136141
}
137-
if (accessors.size() > CGData.MRequirements.size())
138-
throw sycl::exception(make_error_code(errc::kernel_argument),
139-
"placeholder accessor must be bound by calling "
140-
"handler::require() before it can be used.");
141142
}
142143

143144
const auto &type = getType();

sycl/test-e2e/Basic/accessor/accessor.cpp

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ int main() {
696696
acc(b);
697697

698698
q.submit([&](sycl::handler &cgh) {
699-
// we do NOT call .require(acc) without which we should throw a
699+
// We do NOT call .require(acc) without which we should throw a
700700
// synchronous exception with errc::kernel_argument
701701
cgh.parallel_for<class ph1>(r,
702702
[=](sycl::id<1> index) { acc[index] = 0; });
@@ -727,7 +727,7 @@ int main() {
727727
AccT acc(b);
728728

729729
q.submit([&](sycl::handler &cgh) {
730-
// we do NOT call .require(acc) without which we should throw a
730+
// We do NOT call .require(acc) without which we should throw a
731731
// synchronous exception with errc::kernel_argument
732732
// The difference with the previous test is that the use of acc
733733
// is usually optimized away for this particular scenario, but the
@@ -762,14 +762,14 @@ int main() {
762762

763763
q.submit([&](sycl::handler &cgh) {
764764
AccT acc2(b, cgh);
765-
// we do NOT call .require(acc) without which we should throw a
765+
// We do NOT call .require(acc) without which we should throw a
766766
// synchronous exception with errc::kernel_argument
767767
// The particularity of this test is that it passes to a command
768768
// one bound accessor and one unbound accessor. In the past, this
769769
// has led to throw the wrong exception.
770770
cgh.single_task<class ph3>([=] {
771-
volatile int x = acc[0];
772-
volatile int y = acc2[0];
771+
int x = acc[0];
772+
int y = acc2[0];
773773
});
774774
});
775775
q.wait_and_throw();
@@ -784,6 +784,40 @@ int main() {
784784
}
785785
}
786786

787+
// placeholder accessor exception (4) // SYCL2020 4.7.6.9
788+
{
789+
sycl::queue q;
790+
// host device executes kernels via a different method and there
791+
// is no good way to throw an exception at this time.
792+
sycl::range<1> r(4);
793+
sycl::buffer<int, 1> b(r);
794+
try {
795+
using AccT = sycl::accessor<int, 1, sycl::access::mode::read_write,
796+
sycl::access::target::device,
797+
sycl::access::placeholder::true_t>;
798+
AccT acc(b);
799+
800+
q.submit([&](sycl::handler &cgh) {
801+
AccT acc2(b, cgh);
802+
// Pass placeholder accessor to command, but having required a different
803+
// accessor in the command. In past versions, we used to compare the
804+
// number of accessors with the number of requirements, and if they
805+
// matched, we did not throw, allowing this scenario that shouldn't be
806+
// allowed.
807+
cgh.single_task<class ph4>([=] { int x = acc[0]; });
808+
});
809+
q.wait_and_throw();
810+
assert(false && "we should not be here, missing exception");
811+
} catch (sycl::exception &e) {
812+
std::cout << "exception received: " << e.what() << std::endl;
813+
assert(e.code() == sycl::errc::kernel_argument && "incorrect error code");
814+
} catch (...) {
815+
std::cout << "Some other exception (line " << __LINE__ << ")"
816+
<< std::endl;
817+
return 1;
818+
}
819+
}
820+
787821
// SYCL2020 4.9.4.1: calling require() on empty accessor should throw
788822
{
789823
sycl::queue q;
@@ -1430,5 +1464,34 @@ int main() {
14301464
}
14311465
}
14321466

1467+
// default constructed accessor is not a placeholder
1468+
{
1469+
AccT acc;
1470+
assert(!acc.is_placeholder());
1471+
sycl::queue q;
1472+
bool result;
1473+
{
1474+
sycl::buffer<bool, 1> Buf{&result, sycl::range<1>{1}};
1475+
// As a non-placeholder accessor, make sure no exception is thrown when
1476+
// passed to a command. However, we cannot access it, because there's
1477+
// no underlying storage.
1478+
try {
1479+
q.submit([&](sycl::handler &cgh) {
1480+
sycl::accessor res_acc{Buf, cgh};
1481+
cgh.single_task<class def_ctor>(
1482+
[=] { res_acc[0] = acc.is_placeholder(); });
1483+
});
1484+
q.wait_and_throw();
1485+
} catch (sycl::exception &e) {
1486+
assert("Unexpected exception");
1487+
} catch (...) {
1488+
std::cout << "Some other unexpected exception (line " << __LINE__ << ")"
1489+
<< std::endl;
1490+
return 1;
1491+
}
1492+
}
1493+
assert(!result);
1494+
}
1495+
14331496
std::cout << "Test passed" << std::endl;
14341497
}

0 commit comments

Comments
 (0)