Skip to content

Commit 6fbfdfd

Browse files
aratajewigcbot
authored andcommitted
Refactor SPIR-V extension support to follow LLVM TableGen pattern
Refactor the SPIR-V supported extensions infrastructure to follow the standard LLVM approach: a .td file defines declarative data, a TableGen backend generates a data-only .inc file, and a hand-written header provides the query logic that consumes it.
1 parent a701802 commit 6fbfdfd

11 files changed

Lines changed: 618 additions & 295 deletions

File tree

IGC/AdaptorOCL/Utils/IGCCSPIRVSupportTblGen/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ target_link_libraries(igcc-spirv-support-tblgen PRIVATE
3535
LLVMSupport
3636
LLVMTableGen
3737
)
38+
39+
add_subdirectory(test)

IGC/AdaptorOCL/Utils/IGCCSPIRVSupportTblGen/IGCCSPIRVSupportTblGen.cpp

Lines changed: 131 additions & 280 deletions
Large diffs are not rendered by default.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#=========================== begin_copyright_notice ============================
2+
#
3+
# Copyright (C) 2026 Intel Corporation
4+
#
5+
# SPDX-License-Identifier: MIT
6+
#
7+
#============================ end_copyright_notice =============================
8+
9+
if(NOT IGC_OPTION__ENABLE_LIT_TESTS)
10+
return()
11+
endif()
12+
13+
set(SPIRV_TBLGEN_TEST_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
14+
set(SPIRV_TBLGEN_TEST_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
15+
set(SPIRV_TBLGEN_LIT_CONFIG_FILE ${SPIRV_TBLGEN_TEST_BINARY_DIR}/lit.site.cfg.py)
16+
set(SPIRV_EXTENSIONS_INC_DIR
17+
${CMAKE_CURRENT_SOURCE_DIR}/../../../ocl_igc_interface/extensions)
18+
19+
igc_configure_lit_site_cfg(
20+
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
21+
${SPIRV_TBLGEN_LIT_CONFIG_FILE}
22+
MAIN_CONFIG
23+
${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py
24+
)
25+
26+
set(SPIRV_TBLGEN_TEST_DEPENDS
27+
FileCheck
28+
not
29+
igcc-spirv-support-tblgen
30+
)
31+
32+
file(GLOB_RECURSE _tests "${SPIRV_TBLGEN_TEST_SOURCE_DIR}/*.td")
33+
34+
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17")
35+
igc_add_lit_target(check-spirv-tblgen "${SPIRV_TBLGEN_TEST_BINARY_DIR}"
36+
"Running the SPIR-V extension support tblgen regression tests"
37+
DEPENDS ${SPIRV_TBLGEN_TEST_DEPENDS} ${_tests}
38+
SOURCES ${_tests} ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py
39+
)
40+
else()
41+
add_lit_testsuite(check-spirv-tblgen
42+
"Running the SPIR-V extension support tblgen regression tests"
43+
${SPIRV_TBLGEN_TEST_BINARY_DIR}
44+
DEPENDS
45+
${SPIRV_TBLGEN_TEST_DEPENDS}
46+
)
47+
endif()
48+
49+
set_target_properties(check-spirv-tblgen
50+
PROPERTIES
51+
EXCLUDE_FROM_DEFAULT_BUILD OFF
52+
EXCLUDE_FROM_ALL OFF
53+
)
54+
55+
set_target_properties(check-spirv-tblgen PROPERTIES FOLDER "Tests/LIT Tests")
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*========================== begin_copyright_notice ============================
2+
3+
Copyright (C) 2026 Intel Corporation
4+
5+
SPDX-License-Identifier: MIT
6+
7+
============================= end_copyright_notice ===========================*/
8+
9+
// Predicate-flattening regression test for IGCCSPIRVSupportTblGen.
10+
// Each extension exercises one or more PlatformSupport TD shapes; the CHECK
11+
// lines pin the exact Pred:: template emitted into the .inc.
12+
//
13+
// CHECK-DAG is used because the generator emits extensions in TableGen's
14+
// internal record order, not source order. Each pattern is unique enough
15+
// that DAG matching is unambiguous.
16+
//
17+
// RUN: igcc-spirv-support-tblgen -gen-igcc-spirv-extension-support-header \
18+
// RUN: -I %spirv_extensions_inc %s | FileCheck %s
19+
20+
include "SPIRVExtensions_Common.td"
21+
22+
// Fake platform/core symbols. Their string names flow straight through the
23+
// generator into the emitted Pred:: template arguments — they don't need to
24+
// be real PRODUCT_FAMILY / GFXCORE_FAMILY enumerators because the generator
25+
// never resolves them and FileCheck only inspects the textual output.
26+
def CORE_A : CoreFamily<"CORE_A">;
27+
def CORE_B : CoreFamily<"CORE_B">;
28+
def PLAT_X : Platform<"PLAT_X">;
29+
def PLAT_Y : Platform<"PLAT_Y">;
30+
def PLAT_Z : Platform<"PLAT_Z">;
31+
32+
def XYGroup : PlatformGroup<[PLAT_X, PLAT_Y]>;
33+
34+
// --- AllPlatformSupport singleton ---------------------------------------
35+
def Ext_All : Extension<"Ext_All",
36+
[ Capability<"Cap_All"> ],
37+
"https://example.com/Ext_All",
38+
AllPlatformSupport>;
39+
// CHECK-DAG: {"Ext_All", "https://example.com/Ext_All", Pred::AllPlatformSupport, nullptr,
40+
// CHECK-DAG: {"Cap_All", Pred::AllPlatformSupport, nullptr,
41+
42+
// --- isCoreChildOf ------------------------------------------------------
43+
def Ext_CoreChild : Extension<"Ext_CoreChild",
44+
[ Capability<"Cap_CoreChild"> ],
45+
"https://example.com/Ext_CoreChild",
46+
isCoreChildOf<CORE_A>>;
47+
// CHECK-DAG: {"Ext_CoreChild", "https://example.com/Ext_CoreChild", Pred::isCoreChildOf<CORE_A>, nullptr,
48+
// CHECK-DAG: {"Cap_CoreChild", Pred::isCoreChildOf<CORE_A>, nullptr,
49+
50+
// --- ExactCoreFamily ----------------------------------------------------
51+
def Ext_ExactCore : Extension<"Ext_ExactCore",
52+
[ Capability<"Cap_ExactCore"> ],
53+
"https://example.com/Ext_ExactCore",
54+
ExactCoreFamily<CORE_B>>;
55+
// CHECK-DAG: {"Ext_ExactCore", "https://example.com/Ext_ExactCore", Pred::ExactCoreFamily<CORE_B>, nullptr,
56+
// CHECK-DAG: {"Cap_ExactCore", Pred::ExactCoreFamily<CORE_B>, nullptr,
57+
58+
// --- isProductChildOf ---------------------------------------------------
59+
def Ext_ProductChild : Extension<"Ext_ProductChild",
60+
[ Capability<"Cap_ProductChild"> ],
61+
"https://example.com/Ext_ProductChild",
62+
isProductChildOf<PLAT_X>>;
63+
// CHECK-DAG: {"Ext_ProductChild", "https://example.com/Ext_ProductChild", Pred::isProductChildOf<PLAT_X>, nullptr,
64+
// CHECK-DAG: {"Cap_ProductChild", Pred::isProductChildOf<PLAT_X>, nullptr,
65+
66+
// --- ExactPlatform ------------------------------------------------------
67+
def Ext_ExactPlatform : Extension<"Ext_ExactPlatform",
68+
[ Capability<"Cap_ExactPlatform"> ],
69+
"https://example.com/Ext_ExactPlatform",
70+
ExactPlatform<PLAT_Y>>;
71+
// CHECK-DAG: {"Ext_ExactPlatform", "https://example.com/Ext_ExactPlatform", Pred::ExactPlatform<PLAT_Y>, nullptr,
72+
// CHECK-DAG: {"Cap_ExactPlatform", Pred::ExactPlatform<PLAT_Y>, nullptr,
73+
74+
// --- isInGroup (variadic over group members) ----------------------------
75+
def Ext_InGroup : Extension<"Ext_InGroup",
76+
[ Capability<"Cap_InGroup"> ],
77+
"https://example.com/Ext_InGroup",
78+
isInGroup<XYGroup>>;
79+
// CHECK-DAG: {"Ext_InGroup", "https://example.com/Ext_InGroup", Pred::isInGroup<PLAT_X, PLAT_Y>, nullptr,
80+
// CHECK-DAG: {"Cap_InGroup", Pred::isInGroup<PLAT_X, PLAT_Y>, nullptr,
81+
82+
// --- AnyOf, AllOf, Not (composed) ---------------------------------------
83+
def Ext_Composed : Extension<"Ext_Composed",
84+
[ Capability<"Cap_Composed"> ],
85+
"https://example.com/Ext_Composed",
86+
AllOf<[ isCoreChildOf<CORE_A>,
87+
Not<ExactPlatform<PLAT_Z>>,
88+
AnyOf<[ ExactPlatform<PLAT_X>, ExactPlatform<PLAT_Y> ]> ]>>;
89+
// CHECK-DAG: {"Ext_Composed", "https://example.com/Ext_Composed", Pred::AllOf<Pred::isCoreChildOf<CORE_A>, Pred::Not<Pred::ExactPlatform<PLAT_Z>>, Pred::AnyOf<Pred::ExactPlatform<PLAT_X>, Pred::ExactPlatform<PLAT_Y>>>, nullptr,
90+
// CHECK-DAG: {"Cap_Composed", Pred::AllOf<Pred::isCoreChildOf<CORE_A>, Pred::Not<Pred::ExactPlatform<PLAT_Z>>, Pred::AnyOf<Pred::ExactPlatform<PLAT_X>, Pred::ExactPlatform<PLAT_Y>>>, nullptr,
91+
92+
// --- NotSupported singleton ---------------------------------------------
93+
// NotSupported collapses to nullptr in BOTH the extension column and the
94+
// capability column (emitTierColumn special-cases it). The capability
95+
// inherits NotSupported from the extension via InheritFromExtension default.
96+
def Ext_None : Extension<"Ext_None",
97+
[ Capability<"Cap_None"> ],
98+
"https://example.com/Ext_None",
99+
NotSupported>;
100+
// CHECK-DAG: {"Ext_None", "https://example.com/Ext_None", nullptr, nullptr,
101+
// CHECK-DAG: {"Cap_None", nullptr, nullptr,
102+
103+
// --- InheritFromCapabilities aggregation --------------------------------
104+
// Extension column collapses to nullptr (signalling aggregate mode);
105+
// per-capability columns carry the real predicates.
106+
def Ext_Aggregate : Extension<"Ext_Aggregate",
107+
[ Capability<"Cap_Aggr_A", isCoreChildOf<CORE_A>>,
108+
Capability<"Cap_Aggr_B", ExactPlatform<PLAT_Z>> ],
109+
"https://example.com/Ext_Aggregate",
110+
InheritFromCapabilities>;
111+
// CHECK-DAG: {"Ext_Aggregate", "https://example.com/Ext_Aggregate", nullptr, nullptr,
112+
// CHECK-DAG: {"Cap_Aggr_A", Pred::isCoreChildOf<CORE_A>, nullptr,
113+
// CHECK-DAG: {"Cap_Aggr_B", Pred::ExactPlatform<PLAT_Z>, nullptr,
114+
115+
// --- Experimental tier (separate column) --------------------------------
116+
// Capability ID is "0" because the extension has no production support
117+
// (formatCapabilityId requires both Ext.ProductionSupport and
118+
// Cap.ProductionSupport to be non-null before reporting a real ID).
119+
def Ext_Exp_experimental : ExperimentalExtension<"Ext_Exp",
120+
[ Capability<"Cap_Exp"> ],
121+
"https://example.com/Ext_Exp",
122+
ExactPlatform<PLAT_X>>;
123+
// CHECK-DAG: {"Ext_Exp", "https://example.com/Ext_Exp", nullptr, Pred::ExactPlatform<PLAT_X>,
124+
// CHECK-DAG: {"Cap_Exp", nullptr, Pred::ExactPlatform<PLAT_X>, 0},
125+
126+
// --- Dual definition: production + experimental on the same name --------
127+
def Ext_Dual : Extension<"Ext_Dual",
128+
[ Capability<"Cap_Dual"> ],
129+
"https://example.com/Ext_Dual",
130+
isCoreChildOf<CORE_A>>;
131+
def Ext_Dual_experimental : ExperimentalExtension<"Ext_Dual",
132+
[ Capability<"Cap_Dual"> ],
133+
"https://example.com/Ext_Dual",
134+
isCoreChildOf<CORE_B>>;
135+
// CHECK-DAG: {"Ext_Dual", "https://example.com/Ext_Dual", Pred::isCoreChildOf<CORE_A>, Pred::isCoreChildOf<CORE_B>,
136+
// CHECK-DAG: {"Cap_Dual", Pred::isCoreChildOf<CORE_A>, Pred::isCoreChildOf<CORE_B>,
137+
138+
// --- Capability ID handling --------------------------------------------
139+
// Default (Cap.Id == -1): the generator emits a static_cast over
140+
// spv::Capability<Name> from the vendored SPIRV-Headers, so the consumer
141+
// compiles iff <Name> exists in spv::.
142+
// CapabilityWithManualId<Name, Id>: the generator emits the literal ID
143+
// followed by 'u'. Use this for capabilities not yet in the headers.
144+
// Manual and default IDs can be mixed within one extension.
145+
def Ext_IdMix : Extension<"Ext_IdMix",
146+
[ Capability<"Cap_IdDefault">,
147+
CapabilityWithManualId<"Cap_IdManual", 4242> ],
148+
"https://example.com/Ext_IdMix",
149+
AllPlatformSupport>;
150+
// CHECK-DAG: {"Cap_IdDefault", Pred::AllPlatformSupport, nullptr, static_cast<uint32_t>(spv::CapabilityCap_IdDefault)},
151+
// CHECK-DAG: {"Cap_IdManual", Pred::AllPlatformSupport, nullptr, 4242u},
152+
153+
// Manual ID on an experimental-only extension still collapses to "0",
154+
// because formatCapabilityId gates the ID on production support being
155+
// present at BOTH the extension and capability levels.
156+
def Ext_IdExp_experimental : ExperimentalExtension<"Ext_IdExp",
157+
[ CapabilityWithManualId<"Cap_IdExp", 7777> ],
158+
"https://example.com/Ext_IdExp",
159+
AllPlatformSupport>;
160+
// CHECK-DAG: {"Cap_IdExp", nullptr, Pred::AllPlatformSupport, 0},
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# ========================== begin_copyright_notice ============================
2+
#
3+
# Copyright (C) 2026 Intel Corporation
4+
#
5+
# SPDX-License-Identifier: MIT
6+
#
7+
# =========================== end_copyright_notice =============================
8+
9+
# -*- Python -*-
10+
11+
import lit.formats
12+
import os
13+
14+
from lit.llvm import llvm_config
15+
from lit.llvm.subst import ToolSubst
16+
17+
config.name = 'spirv-extension-support-tblgen'
18+
19+
config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
20+
21+
config.suffixes = ['.td']
22+
23+
config.excludes = ['CMakeLists.txt']
24+
25+
config.test_source_root = os.path.dirname(__file__)
26+
27+
config.test_exec_root = os.path.join(config.test_run_dir, 'test_output')
28+
29+
llvm_config.use_default_substitutions()
30+
31+
tool_dirs = [
32+
config.spirv_tblgen_dir,
33+
config.llvm_tools_dir]
34+
35+
tools = [ToolSubst('not'),
36+
ToolSubst('igcc-spirv-support-tblgen')]
37+
38+
llvm_config.add_tool_substitutions(tools, tool_dirs)
39+
40+
# Path used by tests to -I the shared SPIRVExtensions_Common.td.
41+
config.substitutions.append(
42+
('%spirv_extensions_inc', config.spirv_extensions_inc))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# ========================== begin_copyright_notice ============================
2+
#
3+
# Copyright (C) 2026 Intel Corporation
4+
#
5+
# SPDX-License-Identifier: MIT
6+
#
7+
# =========================== end_copyright_notice =============================
8+
9+
@LIT_SITE_CFG_IN_HEADER@
10+
11+
import sys
12+
13+
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
14+
config.lit_tools_dir = "@LLVM_TOOLS_DIR@"
15+
config.host_triple = "@LLVM_HOST_TRIPLE@"
16+
config.target_triple = "@TARGET_TRIPLE@"
17+
config.host_arch = "@HOST_ARCH@"
18+
config.python_executable = "@PYTHON_EXECUTABLE@"
19+
config.test_run_dir = "@CMAKE_CURRENT_BINARY_DIR@"
20+
config.spirv_tblgen_dir = "$<$<TARGET_EXISTS:igcc-spirv-support-tblgen>:$<TARGET_FILE_DIR:igcc-spirv-support-tblgen>>"
21+
config.spirv_extensions_inc = "@SPIRV_EXTENSIONS_INC_DIR@"
22+
23+
try:
24+
config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params
25+
except KeyError:
26+
e = sys.exc_info()[1]
27+
key, = e.args
28+
lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
29+
30+
import lit.llvm
31+
lit.llvm.initialize(lit_config, config)
32+
33+
lit_config.load_config(config, "@SPIRV_TBLGEN_TEST_SOURCE_DIR@/lit.cfg.py")

IGC/AdaptorOCL/ocl_igc_interface/extensions/CMakeLists.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,21 @@ set(CPP_HEADER_FILENAME "SPIRVExtensionsSupport")
2727
# CPP Headers Generation
2828
#=============================================================================
2929

30-
# Generate C++ header for SPIR-V extensions
30+
# Generate data .inc file for SPIR-V extensions (included by the hand-written
31+
# SPIRVExtensionsSupport.h header in this source directory).
3132
set(TABLEGEN_OUTPUT)
3233
set(LLVM_TARGET_DEFINITIONS ${SPIRV_TD_INPUT_FILE})
3334
set(IGCC_LIB_INTERFACES_CPP_TABLEGEN_EXE "${IGCC_LIB_INTERFACES_TABLEGEN_EXE}")
34-
tablegen(IGCC_LIB_INTERFACES_CPP ${CPP_HEADER_FILENAME}.h
35+
tablegen(IGCC_LIB_INTERFACES_CPP ${CPP_HEADER_FILENAME}.inc
3536
-gen-igcc-spirv-extension-support-header)
3637
add_public_tablegen_target(IGCCSPIRVExtensionsCpp)
3738

3839
add_custom_target(${CPP_HEADER_FILENAME}Gen
39-
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CPP_HEADER_FILENAME}.h)
40+
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CPP_HEADER_FILENAME}.inc)
4041
add_dependencies(${CPP_HEADER_FILENAME}Gen IGCCSPIRVExtensionsCpp)
4142

43+
# The hand-written .h lives in the source tree; the .inc it includes is in the
44+
# binary tree. Both directories must be on the include path for consumers.
45+
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
46+
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
47+

0 commit comments

Comments
 (0)