Skip to content

Commit 204d6d3

Browse files
stellaraccidentjammm
authored andcommitted
Port rocisa to Windows.
* Adds some missing headers that the Windows STL is stricter about. * Separates `run` and `demangle` from `helper.hpp` into a new translation unit and conditionally provides Windows or POSIX-ey versions. Adds some RAII safety and error handling above and beyond the contributed original Windows implementation. * Works around an interaction between nanobind and the Windows ABI not being able to differentiate between generated member function trampolines that differ only by const-ness. * Note that this is just one part of a longer patch sequence that has been tested on Windows but will not compile in isolation. Co-Authored-By: Aaryaman Vasishta <[email protected]>
1 parent cf3a6ae commit 204d6d3

File tree

5 files changed

+277
-67
lines changed

5 files changed

+277
-67
lines changed

tensilelite/rocisa/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ nanobind_add_module(rocisa NOMINSIZE NB_SUPPRESS_WARNINGS
6868
${CMAKE_CURRENT_SOURCE_DIR}/rocisa/src/container.cpp
6969
${CMAKE_CURRENT_SOURCE_DIR}/rocisa/src/count.cpp
7070
${CMAKE_CURRENT_SOURCE_DIR}/rocisa/src/enum.cpp
71+
${CMAKE_CURRENT_SOURCE_DIR}/rocisa/src/helper.cpp
7172
${CMAKE_CURRENT_SOURCE_DIR}/rocisa/src/label.cpp
7273
${CMAKE_CURRENT_SOURCE_DIR}/rocisa/src/macro.cpp
7374
${ROCISAINST_SOURCE}

tensilelite/rocisa/rocisa/include/helper.hpp

+5-65
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@
2121
*
2222
* ************************************************************************ */
2323
#pragma once
24+
#include <array>
2425
#include <stdexcept>
2526
#include <string>
26-
#include <sys/wait.h>
27-
#include <unistd.h>
2827
#include <vector>
29-
#ifdef __GNUG__
30-
#include <cxxabi.h>
31-
#endif
3228

3329
using IsaVersion = std::array<int, 3>;
3430

31+
std::pair<int, std::string>
32+
run(const std::vector<char*>& cmd, const std::string& input, bool debug = false);
33+
std::string demangle(const char* name);
34+
3535
inline std::string getGfxNameTuple(const IsaVersion& isaVersion)
3636
{
3737
/*Converts an ISA version to a gfx architecture name.
@@ -47,52 +47,6 @@ inline std::string getGfxNameTuple(const IsaVersion& isaVersion)
4747
+ int_to_hex[isaVersion[2]];
4848
}
4949

50-
inline std::pair<int, std::string>
51-
run(const std::vector<char*>& cmd, const std::string& input, bool debug = false)
52-
{
53-
int p[2];
54-
pid_t pid;
55-
if(pipe(p) == -1)
56-
{
57-
throw std::runtime_error("cmd failed!");
58-
}
59-
60-
pid = fork();
61-
if(pid == 0)
62-
{
63-
close(p[1]);
64-
dup2(p[0], STDIN_FILENO);
65-
if(!debug)
66-
{
67-
dup2(p[0], STDERR_FILENO);
68-
dup2(p[0], STDOUT_FILENO);
69-
}
70-
close(p[0]);
71-
execvp(cmd[0], cmd.data());
72-
perror("execvp");
73-
exit(1);
74-
}
75-
else
76-
{
77-
close(p[0]);
78-
write(p[1], input.c_str(), input.size());
79-
close(p[1]);
80-
char buf[128] = {0};
81-
std::string result;
82-
read(p[0], buf, 128);
83-
result += buf;
84-
int rcode;
85-
waitpid(pid, &rcode, 0);
86-
if(WIFEXITED(rcode))
87-
{
88-
rcode = WEXITSTATUS(rcode);
89-
}
90-
return {rcode, result};
91-
}
92-
// Should not go here.
93-
return {0, "0"};
94-
}
95-
9650
template <typename T>
9751
bool checkInList(const T& a, const std::vector<T> b)
9852
{
@@ -104,17 +58,3 @@ bool checkNotInList(const T& a, const std::vector<T> b)
10458
{
10559
return std::find(b.begin(), b.end(), a) == b.end();
10660
}
107-
108-
inline std::string demangle(const char* name)
109-
{
110-
std::string result = name;
111-
#ifdef __GNUG__
112-
int status = -1;
113-
char* demangled = abi::__cxa_demangle(name, nullptr, nullptr, &status);
114-
result = (status == 0) ? demangled : name;
115-
free(demangled);
116-
#else
117-
#error "Windows not supported"
118-
#endif
119-
return result;
120-
}

tensilelite/rocisa/rocisa/include/label.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
*
2222
* ************************************************************************ */
2323
#pragma once
24+
#include <algorithm>
2425
#include <map>
2526
#include <stdexcept>
27+
#include <string>
2628
#include <time.h>
2729

2830
#define LABEL_NAME_LENGTH 17

tensilelite/rocisa/rocisa/src/code.cpp

+14-2
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,13 @@ void init_code(nb::module_ m)
152152
.def("setInlineAsmPrintMode", &rocisa::Module::setInlineAsmPrintMode)
153153
.def("addSpaceLine", &rocisa::Module::addSpaceLine)
154154
.def("add", &rocisa::Module::add, nb::arg("item"), nb::arg("pos") = -1)
155-
.def("addItems", &rocisa::Module::addItems)
155+
// nanobind cannot reliably bind the same method signatures that differ
156+
// only by const-ness, so indirectly bind via a lambda. This manifests
157+
// as a mangled name clash on Windows.
158+
.def("addItems", [](rocisa::Module& self,
159+
const std::vector<std::shared_ptr<rocisa::Item>>& items) {
160+
self.addItems(items);
161+
})
156162
.def("appendModule", &rocisa::Module::appendModule)
157163
.def("addModuleAsFlatItems", &rocisa::Module::addModuleAsFlatItems)
158164
.def("findIndex", &rocisa::Module::findIndex)
@@ -166,7 +172,13 @@ void init_code(nb::module_ m)
166172
.def("count", &rocisa::Module::count)
167173
.def("getItem", &rocisa::Module::getItem)
168174
.def("setItem", &rocisa::Module::setItem)
169-
.def("setItems", &rocisa::Module::setItems)
175+
// nanobind cannot reliably bind the same method signatures that differ
176+
// only by const-ness, so indirectly bind via a lambda. This manifests
177+
// as a mangled name clash on Windows.
178+
.def("setItems", [](rocisa::Module& self,
179+
const std::vector<std::shared_ptr<rocisa::Item>>& items) {
180+
self.setItems(items);
181+
})
170182
.def("items", &rocisa::Module::items)
171183
.def("itemsSize", &rocisa::Module::itemsSize)
172184
.def("replaceItem", &rocisa::Module::replaceItem)
+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/* ************************************************************************
2+
* Copyright (C) 2025 Advanced Micro Devices, Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*
22+
* ************************************************************************ */
23+
24+
#include "helper.hpp"
25+
26+
// This file includes platform specific helpers that differ between POSIX and
27+
// Windows. The alternatives are switched at a whole-file level. Please do
28+
// not use inline/fine-grained ifdefs.
29+
30+
#ifdef WIN32
31+
32+
#include <windows.h>
33+
#include <dbghelp.h>
34+
35+
// Linker hint to ensure dbghelp.lib is linked.
36+
#pragma comment(lib, "dbghelp.lib")
37+
38+
std::pair<int, std::string>
39+
run(const std::vector<char*>& cmd, const std::string& input, bool debug)
40+
{
41+
struct State {
42+
State() {
43+
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
44+
}
45+
~State() {
46+
if (hChildStd_OUT_Rd) CloseHandle(hChildStd_OUT_Rd);
47+
if (hChildStd_OUT_Wr) CloseHandle(hChildStd_OUT_Wr);
48+
if (hChildStd_IN_Rd) CloseHandle(hChildStd_IN_Rd);
49+
if (hChildStd_IN_Wr) CloseHandle(hChildStd_IN_Wr);
50+
if (piProcInfo.hProcess) CloseHandle(piProcInfo.hProcess);
51+
if (piProcInfo.hThread) CloseHandle(piProcInfo.hThread);
52+
}
53+
HANDLE hChildStd_OUT_Rd = NULL;
54+
HANDLE hChildStd_OUT_Wr = NULL;
55+
HANDLE hChildStd_IN_Rd = NULL;
56+
HANDLE hChildStd_IN_Wr = NULL;
57+
58+
PROCESS_INFORMATION piProcInfo;
59+
};
60+
State state;
61+
62+
// Windows implementation using CreateProcess and pipes.
63+
SECURITY_ATTRIBUTES saAttr;
64+
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
65+
saAttr.bInheritHandle = TRUE;
66+
saAttr.lpSecurityDescriptor = NULL;
67+
68+
// Create a pipe for the child process's STDOUT.
69+
if (!CreatePipe(&state.hChildStd_OUT_Rd, &state.hChildStd_OUT_Wr, &saAttr, 0))
70+
throw std::runtime_error("Stdout pipe creation failed");
71+
// Ensure the read handle is NOT inherited.
72+
if (!SetHandleInformation(state.hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
73+
throw std::runtime_error("Stdout SetHandleInformation failed");
74+
75+
// Create a pipe for the child process's STDIN.
76+
if (!CreatePipe(&state.hChildStd_IN_Rd, &state.hChildStd_IN_Wr, &saAttr, 0))
77+
throw std::runtime_error("Stdin pipe creation failed");
78+
if (!SetHandleInformation(state.hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
79+
throw std::runtime_error("Stdin SetHandleInformation failed");
80+
81+
// Build a command-line string from the vector of arguments.
82+
std::string commandLine;
83+
for (size_t i = 0; i < cmd.size(); ++i)
84+
{
85+
if (i > 0)
86+
commandLine += " ";
87+
std::string arg(cmd[i]);
88+
// Quote the argument if it contains spaces.
89+
// Note that this can still fail to quote properly on more esoteric
90+
// things like embedded quotation marks, etc. Since it is being used
91+
// to invoke controlled tools, we do not expect this and do not guard
92+
// against it.
93+
if (arg.find(' ') != std::string::npos)
94+
commandLine += "\"" + arg + "\"";
95+
else
96+
commandLine += arg;
97+
}
98+
// Create a mutable command-line buffer as required by CreateProcess.
99+
std::vector<char> cmdLineMutable(commandLine.begin(), commandLine.end());
100+
cmdLineMutable.push_back('\0');
101+
102+
STARTUPINFOA siStartInfo;
103+
ZeroMemory(&siStartInfo, sizeof(STARTUPINFOA));
104+
siStartInfo.cb = sizeof(STARTUPINFOA);
105+
siStartInfo.hStdInput = state.hChildStd_IN_Rd;
106+
if (!debug)
107+
{
108+
siStartInfo.hStdOutput = state.hChildStd_OUT_Wr;
109+
siStartInfo.hStdError = state.hChildStd_OUT_Wr;
110+
}
111+
else
112+
{
113+
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
114+
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
115+
}
116+
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
117+
118+
if (!CreateProcessA(
119+
NULL,
120+
cmdLineMutable.data(),
121+
NULL,
122+
NULL,
123+
TRUE,
124+
0,
125+
NULL,
126+
NULL,
127+
&siStartInfo,
128+
&state.piProcInfo))
129+
{
130+
std::string message("CreateProcess failed: ");
131+
message.append(commandLine);
132+
throw std::runtime_error(std::move(message));
133+
}
134+
135+
// Write the input string to the child's STDIN.
136+
DWORD dwWritten = 0;
137+
if (!WriteFile(state.hChildStd_IN_Wr, input.c_str(),
138+
static_cast<DWORD>(input.size()), &dwWritten, NULL))
139+
{
140+
std::string message("Failed to write to child's stdin: ");
141+
message.append(commandLine);
142+
throw std::runtime_error(std::move(message));
143+
}
144+
CloseHandle(state.hChildStd_IN_Wr);
145+
state.hChildStd_IN_Wr = NULL;
146+
CloseHandle(state.hChildStd_IN_Rd);
147+
state.hChildStd_IN_Rd = NULL;
148+
149+
std::string result;
150+
if (!debug)
151+
{
152+
const int BUFSIZE = 128;
153+
char buffer[BUFSIZE];
154+
DWORD dwRead = 0;
155+
while (true)
156+
{
157+
BOOL bRead = ReadFile(state.hChildStd_OUT_Rd, buffer, BUFSIZE, &dwRead, NULL);
158+
if(!bRead || dwRead == 0)
159+
break;
160+
result.append(buffer, dwRead);
161+
}
162+
}
163+
CloseHandle(state.hChildStd_OUT_Wr);
164+
state.hChildStd_OUT_Wr = NULL;
165+
CloseHandle(state.hChildStd_OUT_Rd);
166+
state.hChildStd_OUT_Rd = NULL;
167+
168+
// Wait for the child process to finish.
169+
WaitForSingleObject(state.piProcInfo.hProcess, INFINITE);
170+
DWORD exitCode = 0;
171+
if (!GetExitCodeProcess(state.piProcInfo.hProcess, &exitCode))
172+
exitCode = static_cast<DWORD>(-1);
173+
174+
return { static_cast<int>(exitCode), result };
175+
}
176+
177+
std::string demangle(const char* name)
178+
{
179+
std::string result = name;
180+
char demangledName[1024] = {0};
181+
// UNDNAME_COMPLETE flag produces the full undecorated name.
182+
if (UnDecorateSymbolName(name, demangledName, sizeof(demangledName), UNDNAME_COMPLETE))
183+
{
184+
result = demangledName;
185+
}
186+
return result;
187+
}
188+
189+
#else
190+
// POSIX implementation.
191+
#include <sys/wait.h>
192+
#include <unistd.h>
193+
#ifdef __GNUG__
194+
#include <cxxabi.h>
195+
#endif
196+
197+
198+
std::pair<int, std::string>
199+
run(const std::vector<char*>& cmd, const std::string& input, bool debug)
200+
{
201+
int p[2];
202+
pid_t pid;
203+
if(pipe(p) == -1)
204+
{
205+
throw std::runtime_error("cmd failed!");
206+
}
207+
208+
pid = fork();
209+
if(pid == 0)
210+
{
211+
close(p[1]);
212+
dup2(p[0], STDIN_FILENO);
213+
if(!debug)
214+
{
215+
dup2(p[0], STDERR_FILENO);
216+
dup2(p[0], STDOUT_FILENO);
217+
}
218+
close(p[0]);
219+
execvp(cmd[0], cmd.data());
220+
perror("execvp");
221+
exit(1);
222+
}
223+
else
224+
{
225+
close(p[0]);
226+
write(p[1], input.c_str(), input.size());
227+
close(p[1]);
228+
char buf[128] = {0};
229+
std::string result;
230+
read(p[0], buf, 128);
231+
result += buf;
232+
int rcode;
233+
waitpid(pid, &rcode, 0);
234+
if(WIFEXITED(rcode))
235+
{
236+
rcode = WEXITSTATUS(rcode);
237+
}
238+
return {rcode, result};
239+
}
240+
// Should not go here.
241+
return {0, "0"};
242+
}
243+
244+
std::string demangle(const char* name)
245+
{
246+
std::string result = name;
247+
int status = -1;
248+
char* demangled = abi::__cxa_demangle(name, nullptr, nullptr, &status);
249+
result = (status == 0) ? demangled : name;
250+
free(demangled);
251+
return result;
252+
}
253+
254+
#endif // POSIX
255+

0 commit comments

Comments
 (0)