Skip to content

Commit a3afdc6

Browse files
Copilotgtong-nvslangbot
authored
Fix parseCommandLineArguments not populating TargetDesc format and profile fields (#8967)
`IGlobalSession::parseCommandLineArguments` was creating `TargetDesc` objects with correct `compilerOptionEntries` but leaving `format` and `profile` fields uninitialized (zero). Multiple targets appeared identical despite having different formats (SPIRV vs DXIL) and profiles (sm_5_0 vs sm_6_0). ## Changes - **source/slang/slang-global-session.cpp**: Populate `format` and `profile` from `TargetRequest` in the target iteration loop: ```cpp tdesc.format = (SlangCompileTarget)target->getTarget(); tdesc.profile = (SlangProfileID)target->getOptionSet().getProfile().raw; ``` - **tools/slang-unit-test/unit-test-parse-command-line-args.cpp**: Add test verifying multiple targets with different formats and profiles are parsed correctly. ## Note `parseCommandLineArguments` expects arguments **without** the program name (unlike standard `main(argc, argv)`). Users should pass `argc-1, argv+1` when adapting from main's arguments. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Multiple targets are not parsed correctly with slang::IGlobalSession::parseCommandLineArguments</issue_title> > <issue_description># Issue Description > When parsing command line arguments via `slang::IGlobalSession::parseCommandLineArguments`, the `slang::SessionDesc` object contain the right amount of targets, but the targets are all the same. > > # Reproducer Code > [Here is a link to a repository that reproduces the issue.](https://github.com/sporacid/shader-slang-issue) > > I've included my `CMakeUserPresets.json` file, you just have to clone [vcpkg](https://github.com/microsoft/vcpkg) and change the `VCPKG_ROOT` environment variable. > > You can invoke the program with stantard `slangc` command line. I've used: > > ``` > -target spirv -o mesh.spv -profile sm_5_0 -target dxil -o mesh.dxil -profile sm_6_0 -- mesh.slang > ``` > > # Expected Behavior > I would have expected to have the first target as `SPIR-V` with `sm_5_0` and the second target as `DXIL` with `sm_6_0`. I would also have expected the fields of `slang::TargetDesc` to be filled instead of looking into the compiler option entries. When I execute the program, I get this output. > > ``` > target count: 2 > target: 0 > format: 0 > profile: 0 > target option: 10 > profile option: 196608 > target: 1 > format: 0 > profile: 0 > target option: 10 > profile option: 196608 > ``` > > # Actual Behavior > The `slang::TargetDesc::format` and `slang::TargetDesc::profile` fields are not set to the right values. The format and profile compiler option entries for both targets are set to `DXIL` and `sm_6_0`. > > # Environment > - Slang Version: 2025.5 > - OS: Windows 11 Version 10.0.26100 Build 26100 > - GPU/Driver version: NVIDIA 576.52 > - Any related environment variables: None > > # Additional context > I'm not compiling the code, simply parsing its syntax to generate C++ bindings for slang types.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > <comment_new><author>@gtong-nv</author><body> > Looking at the `Session::parseCommandLineArguments implementation in source/slang/slang-global-session.cpp (lines 1107-1116)`: > ``` > for (auto target : tempReq->getLinkage()->targets) > { > slang::TargetDesc tdesc; > SerializedOptionsData& targetOptionData = outData->options[optionDataIndex]; > optionDataIndex++; > tempReq->getTargetOptionSet(target).serialize(&targetOptionData); > tdesc.compilerOptionEntryCount = (uint32_t)targetOptionData.entries.getCount(); > tdesc.compilerOptionEntries = targetOptionData.entries.getBuffer(); > outData->targets.add(tdesc); > } > ``` > The problem: When creating each `TargetDesc`, the code: > Initializes `tdesc` with default values (format=0, profile=0) > Only sets the `compilerOptionEntries` field > Never populates the format and profile fields of the TargetDesc struct</body></comment_new> > </comments> > </details> - Fixes #8963 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/shader-slang/slang/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: gtong-nv <[email protected]> Co-authored-by: Gangzheng Tong <[email protected]> Co-authored-by: slangbot <[email protected]> Co-authored-by: slangbot <[email protected]>
1 parent f8e97d9 commit a3afdc6

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

source/slang/slang-global-session.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,8 @@ SLANG_NO_THROW SlangResult SLANG_MCALL Session::parseCommandLineArguments(
11121112
tempReq->getTargetOptionSet(target).serialize(&targetOptionData);
11131113
tdesc.compilerOptionEntryCount = (uint32_t)targetOptionData.entries.getCount();
11141114
tdesc.compilerOptionEntries = targetOptionData.entries.getBuffer();
1115+
tdesc.format = (SlangCompileTarget)target->getTarget();
1116+
tdesc.profile = (SlangProfileID)target->getOptionSet().getProfile().raw;
11151117
outData->targets.add(tdesc);
11161118
}
11171119
outDesc->compilerOptionEntryCount = (uint32_t)optionData.entries.getCount();
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// unit-test-parse-command-line-args.cpp
2+
3+
#include "slang-com-helper.h"
4+
#include "slang-com-ptr.h"
5+
#include "slang.h"
6+
#include "unit-test/slang-unit-test.h"
7+
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
11+
namespace
12+
{
13+
14+
using namespace Slang;
15+
16+
struct ParseCommandLineArgsTestContext
17+
{
18+
ParseCommandLineArgsTestContext(UnitTestContext* context)
19+
: m_unitTestContext(context)
20+
{
21+
}
22+
23+
SlangResult runTests()
24+
{
25+
slang::IGlobalSession* slangSession = m_unitTestContext->slangGlobalSession;
26+
27+
// Test multiple targets with different formats and profiles
28+
// Note: parseCommandLineArguments expects arguments WITHOUT the program name
29+
const char* argv[] = {
30+
"-target",
31+
"spirv",
32+
"-o",
33+
"out1.spv",
34+
"-profile",
35+
"sm_5_0",
36+
"-target",
37+
"dxil",
38+
"-o",
39+
"out2.dxil",
40+
"-profile",
41+
"sm_6_0",
42+
"--",
43+
"shader.slang"};
44+
int argc = SLANG_COUNT_OF(argv);
45+
46+
slang::SessionDesc sessionDesc{};
47+
ComPtr<ISlangUnknown> allocation;
48+
49+
SLANG_RETURN_ON_FAIL(slangSession->parseCommandLineArguments(
50+
argc,
51+
argv,
52+
&sessionDesc,
53+
allocation.writeRef()));
54+
55+
const auto findCompilerOptionEntry =
56+
[](const slang::TargetDesc& targetDesc,
57+
slang::CompilerOptionName optionName) -> const slang::CompilerOptionEntry*
58+
{
59+
if (!targetDesc.compilerOptionEntries)
60+
return nullptr;
61+
62+
for (SlangInt i = 0; i < targetDesc.compilerOptionEntryCount; ++i)
63+
{
64+
const auto& entry = targetDesc.compilerOptionEntries[i];
65+
if (entry.name == optionName)
66+
return &entry;
67+
}
68+
return nullptr;
69+
};
70+
71+
// Verify we have 2 targets
72+
SLANG_CHECK(sessionDesc.targetCount == 2);
73+
if (sessionDesc.targetCount != 2)
74+
return SLANG_FAIL;
75+
76+
// Verify first target is SPIRV with sm_5_0
77+
const auto& target0 = sessionDesc.targets[0];
78+
SLANG_CHECK(target0.format == SLANG_SPIRV);
79+
if (target0.format != SLANG_SPIRV)
80+
return SLANG_FAIL;
81+
82+
auto profile_sm_5_0 = slangSession->findProfile("sm_5_0");
83+
SLANG_CHECK(target0.profile == profile_sm_5_0);
84+
if (target0.profile != profile_sm_5_0)
85+
return SLANG_FAIL;
86+
87+
const auto* targetOption0 =
88+
findCompilerOptionEntry(target0, slang::CompilerOptionName::Target);
89+
SLANG_CHECK(targetOption0 != nullptr);
90+
if (!targetOption0)
91+
return SLANG_FAIL;
92+
SLANG_CHECK(targetOption0->value.intValue0 == static_cast<int32_t>(target0.format));
93+
if (targetOption0->value.intValue0 != static_cast<int32_t>(target0.format))
94+
return SLANG_FAIL;
95+
96+
const auto* profileOption0 =
97+
findCompilerOptionEntry(target0, slang::CompilerOptionName::Profile);
98+
SLANG_CHECK(profileOption0 != nullptr);
99+
if (!profileOption0)
100+
return SLANG_FAIL;
101+
SLANG_CHECK(profileOption0->value.intValue0 == static_cast<int32_t>(profile_sm_5_0));
102+
if (profileOption0->value.intValue0 != static_cast<int32_t>(profile_sm_5_0))
103+
return SLANG_FAIL;
104+
105+
// Verify second target is DXIL with sm_6_0
106+
const auto& target1 = sessionDesc.targets[1];
107+
SLANG_CHECK(target1.format == SLANG_DXIL);
108+
if (target1.format != SLANG_DXIL)
109+
return SLANG_FAIL;
110+
111+
auto profile_sm_6_0 = slangSession->findProfile("sm_6_0");
112+
SLANG_CHECK(target1.profile == profile_sm_6_0);
113+
if (target1.profile != profile_sm_6_0)
114+
return SLANG_FAIL;
115+
116+
const auto* targetOption1 =
117+
findCompilerOptionEntry(target1, slang::CompilerOptionName::Target);
118+
SLANG_CHECK(targetOption1 != nullptr);
119+
if (!targetOption1)
120+
return SLANG_FAIL;
121+
SLANG_CHECK(targetOption1->value.intValue0 == static_cast<int32_t>(target1.format));
122+
if (targetOption1->value.intValue0 != static_cast<int32_t>(target1.format))
123+
return SLANG_FAIL;
124+
125+
const auto* profileOption1 =
126+
findCompilerOptionEntry(target1, slang::CompilerOptionName::Profile);
127+
SLANG_CHECK(profileOption1 != nullptr);
128+
if (!profileOption1)
129+
return SLANG_FAIL;
130+
SLANG_CHECK(profileOption1->value.intValue0 == static_cast<int32_t>(profile_sm_6_0));
131+
if (profileOption1->value.intValue0 != static_cast<int32_t>(profile_sm_6_0))
132+
return SLANG_FAIL;
133+
134+
return SLANG_OK;
135+
}
136+
137+
UnitTestContext* m_unitTestContext;
138+
};
139+
140+
} // namespace
141+
142+
SLANG_UNIT_TEST(parseCommandLineArgs)
143+
{
144+
ParseCommandLineArgsTestContext context(unitTestContext);
145+
146+
const auto result = context.runTests();
147+
148+
SLANG_CHECK(SLANG_SUCCEEDED(result));
149+
}

0 commit comments

Comments
 (0)