Skip to content

Commit 4f64a1b

Browse files
committed
Make sure pecos-qir Window's C stubs are consistent with runtime.rs via tests
1 parent a3ba135 commit 4f64a1b

File tree

3 files changed

+339
-78
lines changed

3 files changed

+339
-78
lines changed

crates/pecos-qir/src/platform/windows.rs

Lines changed: 11 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ use std::fs;
66
use std::path::Path;
77
use std::process::Command;
88

9+
#[path = "windows_stub_gen.rs"]
10+
mod stub_gen;
11+
912
/// Handle Windows-specific QIR compilation
1013
pub struct WindowsCompiler;
1114

@@ -108,11 +111,11 @@ impl WindowsCompiler {
108111
let stub_obj_path = parent_dir.join("qir_runtime_stub.o");
109112

110113
// Write DEF file for exporting symbols
111-
fs::write(&def_file_path, Self::generate_def_file())
114+
fs::write(&def_file_path, &Self::generate_def_file())
112115
.map_err(|e| PecosError::Processing(format!("Failed to write DEF file: {e}")))?;
113116

114117
// Write C stub implementation
115-
fs::write(&stub_c_path, Self::generate_c_stub())
118+
fs::write(&stub_c_path, &Self::generate_c_stub())
116119
.map_err(|e| PecosError::Processing(format!("Failed to write stub .c file: {e}")))?;
117120

118121
// Compile the C stub
@@ -170,84 +173,14 @@ impl WindowsCompiler {
170173
Ok(())
171174
}
172175

173-
/// Generate DEF file content
174-
fn generate_def_file() -> &'static str {
175-
r"EXPORTS
176-
qir_runtime_reset
177-
qir_runtime_get_binary_commands
178-
qir_runtime_free_binary_commands
179-
__quantum__qis__rz__body
180-
__quantum__qis__r1xy__body
181-
__quantum__qis__h__body
182-
__quantum__qis__x__body
183-
__quantum__qis__y__body
184-
__quantum__qis__z__body
185-
__quantum__qis__cx__body
186-
__quantum__qis__cz__body
187-
__quantum__qis__szz__body
188-
__quantum__qis__rzz__body
189-
__quantum__qis__m__body
190-
__quantum__qis__reset__body
191-
__quantum__rt__qubit_allocate
192-
__quantum__rt__result_allocate
193-
__quantum__rt__qubit_release
194-
__quantum__rt__result_release
195-
__quantum__rt__message
196-
__quantum__rt__record
197-
__quantum__rt__result_record_output
198-
main @1 NONAME ; Export main function from QIR program
199-
"
176+
/// Generate DEF file content dynamically
177+
fn generate_def_file() -> String {
178+
stub_gen::generate_def_file()
200179
}
201180

202-
/// Generate C stub implementation
203-
fn generate_c_stub() -> &'static str {
204-
r"#include <stdlib.h>
205-
#include <stdint.h>
206-
207-
// Define a minimal binary command structure
208-
typedef struct {
209-
int command_count;
210-
unsigned char* data;
211-
size_t data_size;
212-
} BinaryCommands;
213-
214-
// Static data for commands - empty but valid
215-
static unsigned char empty_data[] = {0};
216-
static BinaryCommands empty_commands = {0, empty_data, 1};
217-
218-
// Required Windows DLL entry point
219-
__declspec(dllexport) int _DllMainCRTStartup(void* hinst, unsigned long reason, void* reserved) {
220-
return 1;
221-
}
222-
223-
// QIR runtime API stubs
224-
__declspec(dllexport) void qir_runtime_reset() {}
225-
__declspec(dllexport) void* qir_runtime_get_binary_commands() { return &empty_commands; }
226-
__declspec(dllexport) void qir_runtime_free_binary_commands(void* cmds) {}
227-
228-
// QIR quantum instruction set stubs
229-
__declspec(dllexport) void __quantum__qis__rz__body(double angle, int qubit) {}
230-
__declspec(dllexport) void __quantum__qis__r1xy__body(double angle, int qubit) {}
231-
__declspec(dllexport) void __quantum__qis__h__body(int qubit) {}
232-
__declspec(dllexport) void __quantum__qis__x__body(int qubit) {}
233-
__declspec(dllexport) void __quantum__qis__y__body(int qubit) {}
234-
__declspec(dllexport) void __quantum__qis__z__body(int qubit) {}
235-
__declspec(dllexport) void __quantum__qis__cx__body(int control, int target) {}
236-
__declspec(dllexport) void __quantum__qis__cz__body(int control, int target) {}
237-
__declspec(dllexport) void __quantum__qis__szz__body(int q1, int q2) {}
238-
__declspec(dllexport) void __quantum__qis__rzz__body(double angle, int q1, int q2) {}
239-
__declspec(dllexport) int __quantum__qis__m__body(int qubit) { return 0; }
240-
__declspec(dllexport) void __quantum__qis__reset__body(int qubit) {}
241-
242-
// QIR runtime stubs
243-
__declspec(dllexport) int __quantum__rt__qubit_allocate() { return 0; }
244-
__declspec(dllexport) int __quantum__rt__result_allocate() { return 0; }
245-
__declspec(dllexport) void __quantum__rt__qubit_release(int qubit) {}
246-
__declspec(dllexport) void __quantum__rt__result_release(int result) {}
247-
__declspec(dllexport) void __quantum__rt__message(const char* msg) {}
248-
__declspec(dllexport) void __quantum__rt__record(const char* msg) {}
249-
__declspec(dllexport) void __quantum__rt__result_record_output(int result) {}
250-
"
181+
/// Generate C stub implementation dynamically
182+
fn generate_c_stub() -> String {
183+
stub_gen::generate_c_stub()
251184
}
252185

253186
/// Get Windows system libraries for linking
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
//! Utility to generate Windows stub files dynamically
2+
3+
/// Information about an exported function
4+
#[derive(Debug, Clone)]
5+
pub struct ExportedFunction {
6+
pub name: &'static str,
7+
pub return_type: &'static str,
8+
pub params: &'static [(&'static str, &'static str)], // (type, name)
9+
}
10+
11+
impl ExportedFunction {
12+
/// Generate C stub implementation
13+
fn generate_c_stub(&self) -> String {
14+
let params_str = if self.params.is_empty() {
15+
"void".to_string()
16+
} else {
17+
self.params
18+
.iter()
19+
.map(|(typ, name)| format!("{typ} {name}"))
20+
.collect::<Vec<_>>()
21+
.join(", ")
22+
};
23+
24+
let body = match self.return_type {
25+
"int" | "usize" | "u32" => "{ return 0; }",
26+
"void*" => "{ return &empty_commands; }",
27+
_ => "{}",
28+
};
29+
30+
format!(
31+
"__declspec(dllexport) {} {}({}) {}",
32+
self.return_type, self.name, params_str, body
33+
)
34+
}
35+
36+
/// Generate DEF file entry
37+
fn generate_def_entry(&self) -> String {
38+
// Special case for main function
39+
if self.name == "main" {
40+
format!(
41+
" {} @1 NONAME ; Export main function from QIR program",
42+
self.name
43+
)
44+
} else {
45+
format!(" {}", self.name)
46+
}
47+
}
48+
}
49+
50+
/// Get the list of exported functions
51+
/// This list must be kept in sync with runtime.rs
52+
pub const EXPORTED_FUNCTIONS: &[ExportedFunction] = &[
53+
// QIR runtime API
54+
ExportedFunction {
55+
name: "qir_runtime_reset",
56+
return_type: "void",
57+
params: &[],
58+
},
59+
ExportedFunction {
60+
name: "qir_runtime_get_binary_commands",
61+
return_type: "void*",
62+
params: &[],
63+
},
64+
ExportedFunction {
65+
name: "qir_runtime_free_binary_commands",
66+
return_type: "void",
67+
params: &[("void*", "cmds")],
68+
},
69+
// Quantum instruction set
70+
ExportedFunction {
71+
name: "__quantum__qis__rz__body",
72+
return_type: "void",
73+
params: &[("double", "theta"), ("int", "qubit")],
74+
},
75+
ExportedFunction {
76+
name: "__quantum__qis__r1xy__body",
77+
return_type: "void",
78+
params: &[("double", "theta"), ("double", "phi"), ("int", "qubit")],
79+
},
80+
ExportedFunction {
81+
name: "__quantum__qis__h__body",
82+
return_type: "void",
83+
params: &[("int", "qubit")],
84+
},
85+
ExportedFunction {
86+
name: "__quantum__qis__x__body",
87+
return_type: "void",
88+
params: &[("int", "qubit")],
89+
},
90+
ExportedFunction {
91+
name: "__quantum__qis__y__body",
92+
return_type: "void",
93+
params: &[("int", "qubit")],
94+
},
95+
ExportedFunction {
96+
name: "__quantum__qis__z__body",
97+
return_type: "void",
98+
params: &[("int", "qubit")],
99+
},
100+
ExportedFunction {
101+
name: "__quantum__qis__cx__body",
102+
return_type: "void",
103+
params: &[("int", "control"), ("int", "target")],
104+
},
105+
ExportedFunction {
106+
name: "__quantum__qis__cz__body",
107+
return_type: "void",
108+
params: &[("int", "control"), ("int", "target")],
109+
},
110+
ExportedFunction {
111+
name: "__quantum__qis__szz__body",
112+
return_type: "void",
113+
params: &[("int", "q1"), ("int", "q2")],
114+
},
115+
ExportedFunction {
116+
name: "__quantum__qis__rzz__body",
117+
return_type: "void",
118+
params: &[("double", "theta"), ("int", "q1"), ("int", "q2")],
119+
},
120+
ExportedFunction {
121+
name: "__quantum__qis__m__body",
122+
return_type: "int",
123+
params: &[("int", "qubit"), ("int", "result")],
124+
},
125+
ExportedFunction {
126+
name: "__quantum__qis__reset__body",
127+
return_type: "void",
128+
params: &[("int", "qubit")],
129+
},
130+
// Runtime management
131+
ExportedFunction {
132+
name: "__quantum__rt__qubit_allocate",
133+
return_type: "int",
134+
params: &[],
135+
},
136+
ExportedFunction {
137+
name: "__quantum__rt__result_allocate",
138+
return_type: "int",
139+
params: &[],
140+
},
141+
ExportedFunction {
142+
name: "__quantum__rt__qubit_release",
143+
return_type: "void",
144+
params: &[("int", "qubit")],
145+
},
146+
ExportedFunction {
147+
name: "__quantum__rt__result_release",
148+
return_type: "void",
149+
params: &[("int", "result")],
150+
},
151+
ExportedFunction {
152+
name: "__quantum__rt__message",
153+
return_type: "void",
154+
params: &[("const char*", "msg")],
155+
},
156+
ExportedFunction {
157+
name: "__quantum__rt__record",
158+
return_type: "void",
159+
params: &[("const char*", "data")],
160+
},
161+
ExportedFunction {
162+
name: "__quantum__rt__result_record_output",
163+
return_type: "void",
164+
params: &[("int", "result"), ("const char*", "name")],
165+
},
166+
// Main function (exported from QIR program, not runtime)
167+
ExportedFunction {
168+
name: "main",
169+
return_type: "void",
170+
params: &[],
171+
},
172+
];
173+
174+
/// Generate Windows DEF file content
175+
pub fn generate_def_file() -> String {
176+
let exports: Vec<String> = EXPORTED_FUNCTIONS
177+
.iter()
178+
.map(ExportedFunction::generate_def_entry)
179+
.collect();
180+
181+
format!("EXPORTS\n{}\n", exports.join("\n"))
182+
}
183+
184+
/// Generate Windows C stub content
185+
pub fn generate_c_stub() -> String {
186+
// Filter out main (it's defined in the QIR program)
187+
let stub_functions: Vec<String> = EXPORTED_FUNCTIONS
188+
.iter()
189+
.filter(|f| f.name != "main")
190+
.map(ExportedFunction::generate_c_stub)
191+
.collect();
192+
193+
format!(
194+
r"#include <stdlib.h>
195+
#include <stdint.h>
196+
197+
// Define a minimal binary command structure
198+
typedef struct {{
199+
int command_count;
200+
unsigned char* data;
201+
size_t data_size;
202+
}} BinaryCommands;
203+
204+
// Static data for commands - empty but valid
205+
static unsigned char empty_data[] = {{0}};
206+
static BinaryCommands empty_commands = {{0, empty_data, 1}};
207+
208+
// Required Windows DLL entry point
209+
__declspec(dllexport) int _DllMainCRTStartup(void* hinst, unsigned long reason, void* reserved) {{
210+
return 1;
211+
}}
212+
213+
// QIR runtime stubs
214+
{}
215+
",
216+
stub_functions.join("\n")
217+
)
218+
}

0 commit comments

Comments
 (0)