From 1e411b888fa0ffde516b7dfeec5eafde5b7c58df Mon Sep 17 00:00:00 2001 From: Jeremy Kemp Date: Mon, 31 Aug 2020 23:12:10 +0100 Subject: [PATCH] Memory consistency model optionality (#907) * Test minimum memory consistency capabilities for a device reporting >= 3.0. Skip tests where unsupported memory consistency capabilities are being requested. * Pass nullptr as program build option. Allows the CTS framework to select an appropriate CL C version. * Removed redundant braces. --- test_conformance/c11_atomics/common.h | 105 +++++++++++++++++- test_conformance/c11_atomics/main.cpp | 60 ++++++++++ test_conformance/c11_atomics/test_atomics.cpp | 21 ++++ 3 files changed, 184 insertions(+), 2 deletions(-) diff --git a/test_conformance/c11_atomics/common.h b/test_conformance/c11_atomics/common.h index c45e1aa5..a69feb06 100644 --- a/test_conformance/c11_atomics/common.h +++ b/test_conformance/c11_atomics/common.h @@ -65,6 +65,8 @@ extern bool gUseHostPtr; // use malloc/free instead of clSVMAlloc/clSVMFree extern bool gDebug; // print OpenCL kernel code extern int gInternalIterations; // internal test iterations for atomic operation, sufficient to verify atomicity extern int gMaxDeviceThreads; // maximum number of threads executed on OCL device +extern cl_device_atomic_capabilities gAtomicMemCap, + gAtomicFenceCap; // atomic memory and fence capabilities for this device extern const char *get_memory_order_type_name(TExplicitMemoryOrderType orderType); extern const char *get_memory_scope_type_name(TExplicitMemoryScopeType scopeType); @@ -281,6 +283,88 @@ public: else return 0; } + + int CheckCapabilities(TExplicitMemoryScopeType memoryScope, + TExplicitMemoryOrderType memoryOrder) + { + /* + Differentiation between atomic fence and other atomic operations + does not need to occur here. + + The initialisation of this test checks that the minimum required + capabilities are supported by this device. + + The following switches allow the test to skip if optional capabilites + are not supported by the device. + */ + switch (memoryScope) + { + case MEMORY_SCOPE_EMPTY: { + break; + } + case MEMORY_SCOPE_WORK_GROUP: { + if ((gAtomicMemCap & CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP) == 0) + { + return TEST_SKIPPED_ITSELF; + } + break; + } + case MEMORY_SCOPE_DEVICE: { + if ((gAtomicMemCap & CL_DEVICE_ATOMIC_SCOPE_DEVICE) == 0) + { + return TEST_SKIPPED_ITSELF; + } + break; + } + case MEMORY_SCOPE_ALL_SVM_DEVICES: { + if ((gAtomicMemCap & CL_DEVICE_ATOMIC_SCOPE_ALL_DEVICES) == 0) + { + return TEST_SKIPPED_ITSELF; + } + break; + } + default: { + log_info("Invalid memory scope\n"); + break; + } + } + + switch (memoryOrder) + { + case MEMORY_ORDER_EMPTY: { + break; + } + case MEMORY_ORDER_RELAXED: { + if ((gAtomicMemCap & CL_DEVICE_ATOMIC_ORDER_RELAXED) == 0) + { + return TEST_SKIPPED_ITSELF; + } + break; + } + case MEMORY_ORDER_ACQUIRE: + case MEMORY_ORDER_RELEASE: + case MEMORY_ORDER_ACQ_REL: { + if ((gAtomicMemCap & CL_DEVICE_ATOMIC_ORDER_ACQ_REL) == 0) + { + return TEST_SKIPPED_ITSELF; + } + break; + } + case MEMORY_ORDER_SEQ_CST: { + if ((gAtomicMemCap & CL_DEVICE_ATOMIC_ORDER_SEQ_CST) == 0) + { + return TEST_SKIPPED_ITSELF; + } + break; + } + default: { + log_info("Invalid memory order\n"); + break; + } + } + + return 0; + } virtual bool SVMDataBufferAllSVMConsistent() {return false;} bool UseSVM() {return _useSVM;} void StartValue(HostDataType startValue) {_startValue = startValue;} @@ -339,6 +423,7 @@ class CBasicTestMemOrderScope : public CBasicTest public: using CBasicTest::LocalMemory; using CBasicTest::MaxGroupSize; + using CBasicTest::CheckCapabilities; CBasicTestMemOrderScope(TExplicitAtomicType dataType, bool useSVM = false) : CBasicTest(dataType, useSVM) { } @@ -389,6 +474,10 @@ public: MaxGroupSize(16); // increase number of groups by forcing smaller group size else MaxGroupSize(0); // group size limited by device capabilities + + if (CheckCapabilities(MemoryScope(), MemoryOrder()) == TEST_SKIPPED_ITSELF) + return 0; // skip test - not applicable + return CBasicTest::ExecuteSingleTest(deviceID, context, queue); } virtual int ExecuteForEachParameterSet(cl_device_id deviceID, cl_context context, cl_command_queue queue) @@ -470,6 +559,8 @@ public: using CBasicTestMemOrderScope::MemoryScope; using CBasicTestMemOrderScope::MemoryOrderStr; using CBasicTestMemOrderScope::MemoryScopeStr; + using CBasicTest::CheckCapabilities; + CBasicTestMemOrder2Scope(TExplicitAtomicType dataType, bool useSVM = false) : CBasicTestMemOrderScope(dataType, useSVM) { } @@ -517,6 +608,15 @@ public: MemoryOrder(memoryOrder[oi]); MemoryOrder2(memoryOrder[o2i]); MemoryScope(memoryScope[si]); + + if (CheckCapabilities(MemoryScope(), MemoryOrder()) + == TEST_SKIPPED_ITSELF) + continue; // skip test - not applicable + + if (CheckCapabilities(MemoryScope(), MemoryOrder2()) + == TEST_SKIPPED_ITSELF) + continue; // skip test - not applicable + EXECUTE_TEST(error, (CBasicTest::ExecuteForEachParameterSet(deviceID, context, queue))); } } @@ -855,8 +955,9 @@ int CBasicTest::ExecuteSingleTest(cl_device_id dev // Set up the kernel code programSource = PragmaHeader(deviceID)+ProgramHeader(numDestItems)+FunctionCode()+KernelCode(numDestItems); programLine = programSource.c_str(); - if(create_single_kernel_helper_with_build_options(context, &program, &kernel, 1, &programLine, "test_atomic_kernel", - gOldAPI ? "" : "-cl-std=CL2.0")) + if (create_single_kernel_helper_with_build_options( + context, &program, &kernel, 1, &programLine, "test_atomic_kernel", + gOldAPI ? "" : nullptr)) { return -1; } diff --git a/test_conformance/c11_atomics/main.cpp b/test_conformance/c11_atomics/main.cpp index 52084732..41b253a0 100644 --- a/test_conformance/c11_atomics/main.cpp +++ b/test_conformance/c11_atomics/main.cpp @@ -26,6 +26,8 @@ bool gUseHostPtr = false; // use malloc/free with CL_MEM_USE_HOST_PTR instead of bool gDebug = false; // always print OpenCL kernel code int gInternalIterations = 10000; // internal test iterations for atomic operation, sufficient to verify atomicity int gMaxDeviceThreads = 1024; // maximum number of threads executed on OCL device +cl_device_atomic_capabilities gAtomicMemCap, + gAtomicFenceCap; // atomic memory and fence capabilities for this device extern int test_atomic_init(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements); extern int test_atomic_store(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements); @@ -108,6 +110,7 @@ const int test_num = ARRAY_SIZE( test_list ); test_status InitCL(cl_device_id device) { auto version = get_device_cl_version(device); auto expected_min_version = Version(2, 0); + if (version < expected_min_version) { version_expected_info("Test", "OpenCL", @@ -115,6 +118,63 @@ test_status InitCL(cl_device_id device) { version.to_string().c_str()); return TEST_SKIP; } + + if (version >= Version(3, 0)) + { + cl_int error; + + error = clGetDeviceInfo(device, CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES, + sizeof(gAtomicMemCap), &gAtomicMemCap, NULL); + if (error != CL_SUCCESS) + { + print_error(error, "Unable to get atomic memory capabilities\n"); + return TEST_FAIL; + } + + error = + clGetDeviceInfo(device, CL_DEVICE_ATOMIC_FENCE_CAPABILITIES, + sizeof(gAtomicFenceCap), &gAtomicFenceCap, NULL); + if (error != CL_SUCCESS) + { + print_error(error, "Unable to get atomic fence capabilities\n"); + return TEST_FAIL; + } + + if ((gAtomicFenceCap + & (CL_DEVICE_ATOMIC_ORDER_RELAXED | CL_DEVICE_ATOMIC_ORDER_ACQ_REL + | CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP)) + == 0) + { + log_info( + "Minimum atomic fence capabilities unsupported by device\n"); + return TEST_FAIL; + } + + if ((gAtomicMemCap + & (CL_DEVICE_ATOMIC_ORDER_RELAXED + | CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP)) + == 0) + { + log_info( + "Minimum atomic memory capabilities unsupported by device\n"); + return TEST_FAIL; + } + } + else + { + // OpenCL 2.x device, default to all capabilities + gAtomicMemCap = CL_DEVICE_ATOMIC_ORDER_RELAXED + | CL_DEVICE_ATOMIC_ORDER_ACQ_REL | CL_DEVICE_ATOMIC_ORDER_SEQ_CST + | CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP | CL_DEVICE_ATOMIC_SCOPE_DEVICE + | CL_DEVICE_ATOMIC_SCOPE_ALL_DEVICES; + + gAtomicFenceCap = CL_DEVICE_ATOMIC_ORDER_RELAXED + | CL_DEVICE_ATOMIC_ORDER_ACQ_REL | CL_DEVICE_ATOMIC_ORDER_SEQ_CST + | CL_DEVICE_ATOMIC_SCOPE_WORK_ITEM + | CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP | CL_DEVICE_ATOMIC_SCOPE_DEVICE + | CL_DEVICE_ATOMIC_SCOPE_ALL_DEVICES; + } + return TEST_PASS; } diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index f12f9554..c1e153be 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -29,7 +29,9 @@ class CBasicTestStore : public CBasicTestMemOrderScope::OldValueCheck; using CBasicTestMemOrderScope::MemoryOrder; + using CBasicTestMemOrderScope::MemoryScope; using CBasicTestMemOrderScope::MemoryOrderScopeStr; + using CBasicTest::CheckCapabilities; CBasicTestStore(TExplicitAtomicType dataType, bool useSVM) : CBasicTestMemOrderScope(dataType, useSVM) { OldValueCheck(false); @@ -43,6 +45,10 @@ public: if(MemoryOrder() == MEMORY_ORDER_ACQUIRE || MemoryOrder() == MEMORY_ORDER_ACQ_REL) return 0; //skip test - not applicable + + if (CheckCapabilities(MemoryScope(), MemoryOrder()) == TEST_SKIPPED_ITSELF) + return 0; // skip test - not applicable + return CBasicTestMemOrderScope::ExecuteSingleTest(deviceID, context, queue); } virtual std::string ProgramCore() @@ -198,7 +204,9 @@ class CBasicTestLoad : public CBasicTestMemOrderScope::OldValueCheck; using CBasicTestMemOrderScope::MemoryOrder; + using CBasicTestMemOrderScope::MemoryScope; using CBasicTestMemOrderScope::MemoryOrderScopeStr; + using CBasicTest::CheckCapabilities; CBasicTestLoad(TExplicitAtomicType dataType, bool useSVM) : CBasicTestMemOrderScope(dataType, useSVM) { OldValueCheck(false); @@ -212,6 +220,10 @@ public: if(MemoryOrder() == MEMORY_ORDER_RELEASE || MemoryOrder() == MEMORY_ORDER_ACQ_REL) return 0; //skip test - not applicable + + if (CheckCapabilities(MemoryScope(), MemoryOrder()) == TEST_SKIPPED_ITSELF) + return 0; // skip test - not applicable + return CBasicTestMemOrderScope::ExecuteSingleTest(deviceID, context, queue); } virtual std::string ProgramCore() @@ -435,9 +447,11 @@ public: using CBasicTestMemOrder2Scope::MemoryOrder; using CBasicTestMemOrder2Scope::MemoryOrder2; using CBasicTestMemOrder2Scope::MemoryOrderScope; + using CBasicTestMemOrder2Scope::MemoryScope; using CBasicTestMemOrder2Scope::DataType; using CBasicTestMemOrder2Scope::Iterations; using CBasicTestMemOrder2Scope::IterationsStr; + using CBasicTest::CheckCapabilities; CBasicTestCompareStrong(TExplicitAtomicType dataType, bool useSVM) : CBasicTestMemOrder2Scope(dataType, useSVM) { StartValue(123456); @@ -451,6 +465,13 @@ public: if((MemoryOrder() == MEMORY_ORDER_RELAXED && MemoryOrder2() != MEMORY_ORDER_RELAXED) || (MemoryOrder() != MEMORY_ORDER_SEQ_CST && MemoryOrder2() == MEMORY_ORDER_SEQ_CST)) return 0; // failure argument shall be no stronger than the success + + if (CheckCapabilities(MemoryScope(), MemoryOrder()) == TEST_SKIPPED_ITSELF) + return 0; // skip test - not applicable + + if (CheckCapabilities(MemoryScope(), MemoryOrder2()) == TEST_SKIPPED_ITSELF) + return 0; // skip test - not applicable + return CBasicTestMemOrder2Scope::ExecuteSingleTest(deviceID, context, queue); } virtual std::string ProgramCore()