Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add playground RIR tab with raw and ssa processed RIR #2096

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 75 additions & 22 deletions compiler/qsc/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub mod qsharp {
}

pub mod qir {
use qsc_codegen::qir::fir_to_qir;
use qsc_codegen::qir::{fir_to_qir, fir_to_rir};

use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
use qsc_frontend::{
Expand Down Expand Up @@ -87,33 +87,100 @@ pub mod qir {
))]
})
}

pub fn get_rir(
sources: SourceMap,
language_features: LanguageFeatures,
capabilities: TargetCapabilityFlags,
mut package_store: PackageStore,
dependencies: &Dependencies,
) -> Result<Vec<String>, Vec<Error>> {
let (package_id, fir_store, entry, compute_properties) = compile_to_fir(
sources,
language_features,
capabilities,
&mut package_store,
dependencies,
)?;

let (raw, ssa) = fir_to_rir(&fir_store, capabilities, Some(compute_properties), &entry)
.map_err(|e| {
let source_package_id = match e.span() {
Some(span) => span.package,
None => package_id,
};
let source_package = package_store
.get(source_package_id)
.expect("package should be in store");
vec![Error::PartialEvaluation(WithSource::from_map(
&source_package.sources,
e,
))]
})?;
Ok(vec![raw.to_string(), ssa.to_string()])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why convert the tuple to a vec here? You could just return the tuple. Were you fighting JavaScript?

Copy link
Collaborator Author

@idavis idavis Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I wanted to return a tuple, but the wasm bindings wouldn't let me, so instead of having to turn a tuple into vec, I just kept it in a vec to start

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also may be returning more instances of RIR to show pass steps which would be a vec eventually.

}

pub fn get_qir(
sources: SourceMap,
language_features: LanguageFeatures,
capabilities: TargetCapabilityFlags,
mut package_store: PackageStore,
dependencies: &Dependencies,
) -> Result<String, Vec<Error>> {
let (package_id, fir_store, entry, compute_properties) = compile_to_fir(
sources,
language_features,
capabilities,
&mut package_store,
dependencies,
)?;

fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry).map_err(|e| {
let source_package_id = match e.span() {
Some(span) => span.package,
None => package_id,
};
let source_package = package_store
.get(source_package_id)
.expect("package should be in store");
vec![Error::PartialEvaluation(WithSource::from_map(
&source_package.sources,
e,
))]
})
}

fn compile_to_fir(
sources: SourceMap,
language_features: LanguageFeatures,
capabilities: TargetCapabilityFlags,
package_store: &mut PackageStore,
dependencies: &[(qsc_hir::hir::PackageId, Option<std::sync::Arc<str>>)],
) -> Result<
(
qsc_hir::hir::PackageId,
qsc_fir::fir::PackageStore,
ProgramEntry,
qsc_rca::PackageStoreComputeProperties,
),
Vec<Error>,
> {
if capabilities == TargetCapabilityFlags::all() {
return Err(vec![Error::UnsupportedRuntimeCapabilities]);
}

let (unit, errors) = crate::compile::compile(
&package_store,
&*package_store,
idavis marked this conversation as resolved.
Show resolved Hide resolved
dependencies,
sources,
PackageType::Exe,
capabilities,
language_features,
);

// Ensure it compiles before trying to add it to the store.
if !errors.is_empty() {
return Err(errors.iter().map(|e| Error::Compile(e.clone())).collect());
}

let package_id = package_store.insert(unit);
let (fir_store, fir_package_id) = qsc_passes::lower_hir_to_fir(&package_store, package_id);
let (fir_store, fir_package_id) = qsc_passes::lower_hir_to_fir(&*package_store, package_id);
idavis marked this conversation as resolved.
Show resolved Hide resolved
let package = fir_store.get(fir_package_id);
let entry = ProgramEntry {
exec_graph: package.entry_exec_graph.clone(),
Expand All @@ -125,7 +192,6 @@ pub mod qir {
)
.into(),
};

let compute_properties = PassContext::run_fir_passes_on_fir(
&fir_store,
fir_package_id,
Expand All @@ -140,19 +206,6 @@ pub mod qir {
.map(|e| Error::Pass(WithSource::from_map(&source_package.sources, e.clone())))
.collect::<Vec<_>>()
})?;

fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry).map_err(|e| {
let source_package_id = match e.span() {
Some(span) => span.package,
None => package_id,
};
let source_package = package_store
.get(source_package_id)
.expect("package should be in store");
vec![Error::PartialEvaluation(WithSource::from_map(
&source_package.sources,
e,
))]
})
Ok((package_id, fir_store, entry, compute_properties))
}
}
14 changes: 13 additions & 1 deletion compiler/qsc_codegen/src/qir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use qsc_partial_eval::{partially_evaluate, ProgramEntry};
use qsc_rca::PackageStoreComputeProperties;
use qsc_rir::{
passes::check_and_transform,
rir::{self, ConditionCode},
rir::{self, ConditionCode, Program},
utils::get_all_block_successors,
};

Expand All @@ -37,6 +37,18 @@ pub fn hir_to_qir(
fir_to_qir(&fir_store, capabilities, compute_properties, entry)
}

pub fn fir_to_rir(
fir_store: &qsc_fir::fir::PackageStore,
capabilities: TargetCapabilityFlags,
compute_properties: Option<PackageStoreComputeProperties>,
entry: &ProgramEntry,
) -> Result<(Program, Program), qsc_partial_eval::Error> {
let mut program = get_rir_from_compilation(fir_store, compute_properties, entry, capabilities)?;
let orig = program.clone();
check_and_transform(&mut program);
Ok((orig, program))
}

pub fn fir_to_qir(
fir_store: &qsc_fir::fir::PackageStore,
capabilities: TargetCapabilityFlags,
Expand Down
11 changes: 11 additions & 0 deletions npm/qsharp/src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export interface ICompiler {
profile?: TargetProfile,
): Promise<string>;

getRir(program: ProgramConfig): Promise<string[]>;

run(
program: ProgramConfig,
expr: string,
Expand Down Expand Up @@ -162,6 +164,14 @@ export class Compiler implements ICompiler {
);
}

async getRir(program: ProgramConfig): Promise<string[]> {
const config = toWasmProgramConfig(
program,
program.profile || "adaptive_ri",
);
return this.wasm.get_rir(config);
}

async run(
program: ProgramConfig,
expr: string,
Expand Down Expand Up @@ -319,6 +329,7 @@ export const compilerProtocol: ServiceProtocol<ICompiler, QscEventData> = {
checkCode: "request",
getAst: "request",
getHir: "request",
getRir: "request",
getQir: "request",
getEstimates: "request",
getCircuit: "request",
Expand Down
8 changes: 8 additions & 0 deletions npm/qsharp/ux/qsharp-ux.css
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ html {
white-space: pre;
}

.rir-output {
height: 40vh;
min-height: 400px;
width: 100%;
resize: none;
white-space: pre;
}

.qir-output {
height: 40vh;
min-height: 400px;
Expand Down
15 changes: 11 additions & 4 deletions playground/src/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export function Editor(props: {
profile: TargetProfile;
setAst: (ast: string) => void;
setHir: (hir: string) => void;
setRir: (rir: string[]) => void;
setQir: (qir: string) => void;
activeTab: ActiveTab;
languageService: ILanguageServiceWorker;
Expand Down Expand Up @@ -159,7 +160,7 @@ export function Editor(props: {
);
}
const codeGenTimeout = 1000; // ms
if (props.activeTab === "qir-tab") {
if (props.activeTab === "qir-tab" || props.activeTab === "rir-tab") {
let timedOut = false;
const compiler = props.compiler_worker_factory();
const compilerTimeout = setTimeout(() => {
Expand All @@ -168,9 +169,15 @@ export function Editor(props: {
compiler.terminate();
}, codeGenTimeout);
try {
const qir = await compiler.getQir(config);
clearTimeout(compilerTimeout);
props.setQir(qir);
if (props.activeTab === "rir-tab") {
const ir = await compiler.getRir(config);
clearTimeout(compilerTimeout);
props.setRir(ir);
} else {
const ir = await compiler.getQir(config);
clearTimeout(compilerTimeout);
props.setQir(ir);
}
} catch (e: any) {
if (timedOut) {
props.setQir("timed out");
Expand Down
4 changes: 4 additions & 0 deletions playground/src/kata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ function LessonElem(props: Props & { section: KataSection }) {
profile={getProfile()}
setAst={() => ({})}
setHir={() => ({})}
setRir={() => ({})}
setQir={() => ({})}
activeTab="results-tab"
languageService={props.languageService}
Expand All @@ -124,6 +125,7 @@ function LessonElem(props: Props & { section: KataSection }) {
onShotError={(diag?: VSDiagnostic) => setShotError(diag)}
ast=""
hir=""
rir={["", ""]}
qir=""
activeTab="results-tab"
setActiveTab={() => undefined}
Expand Down Expand Up @@ -176,6 +178,7 @@ function ExerciseElem(props: Props & { section: KataSection }) {
profile={getProfile()}
setAst={() => ({})}
setHir={() => ({})}
setRir={() => ({})}
setQir={() => ({})}
activeTab="results-tab"
languageService={props.languageService}
Expand All @@ -187,6 +190,7 @@ function ExerciseElem(props: Props & { section: KataSection }) {
onShotError={(diag?: VSDiagnostic) => setShotError(diag)}
ast=""
hir=""
rir={["", ""]}
qir=""
activeTab="results-tab"
setActiveTab={() => undefined}
Expand Down
10 changes: 9 additions & 1 deletion playground/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ md.use((mk as any).default, {
}); // Not sure why it's not using the default export automatically :-/
setRenderer((input: string) => md.render(input));

export type ActiveTab = "results-tab" | "ast-tab" | "hir-tab" | "qir-tab";
export type ActiveTab =
| "results-tab"
| "ast-tab"
| "hir-tab"
| "rir-tab"
| "qir-tab";

const basePath = (window as any).qscBasePath || "";
const monacoPath = basePath + "libs/monaco/vs";
Expand Down Expand Up @@ -104,6 +109,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {

const [ast, setAst] = useState<string>("");
const [hir, setHir] = useState<string>("");
const [rir, setRir] = useState<string[]>(["", ""]);
const [qir, setQir] = useState<string>("");
const [activeTab, setActiveTab] = useState<ActiveTab>("results-tab");

Expand Down Expand Up @@ -177,6 +183,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {
profile={getProfile()}
setAst={setAst}
setHir={setHir}
setRir={setRir}
setQir={setQir}
activeTab={activeTab}
languageService={languageService}
Expand All @@ -187,6 +194,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) {
onShotError={(diag?: VSDiagnostic) => setShotError(diag)}
ast={ast}
hir={hir}
rir={rir}
qir={qir}
activeTab={activeTab}
setActiveTab={setActiveTab}
Expand Down
18 changes: 18 additions & 0 deletions playground/src/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const tabArray: Array<[ActiveTab, string]> = [
["results-tab", "RESULTS"],
["ast-tab", "AST"],
["hir-tab", "HIR"],
["rir-tab", "RIR"],
["qir-tab", "QIR"],
];

Expand All @@ -28,6 +29,21 @@ function HirTab(props: { hir: string; activeTab: ActiveTab }) {
) : null;
}

function RirTab(props: { rir: string[]; activeTab: ActiveTab }) {
const raw = props.rir[0];
const ssa = props.rir[1];
return props.activeTab === "rir-tab" ? (
<div>
<textarea readonly class="rir-output">
{raw}
</textarea>
<textarea readonly class="rir-output">
{ssa}
</textarea>
</div>
) : null;
}

function QirTab(props: { qir: string; activeTab: ActiveTab }) {
return props.activeTab === "qir-tab" ? (
<textarea readonly class="qir-output">
Expand All @@ -43,6 +59,7 @@ export function OutputTabs(props: {
kataMode?: boolean;
ast: string;
hir: string;
rir: string[];
qir: string;
activeTab: ActiveTab;
setActiveTab: (tab: ActiveTab) => void;
Expand All @@ -64,6 +81,7 @@ export function OutputTabs(props: {
<ResultsTab {...props} />
<AstTab {...props} />
<HirTab {...props} />
<RirTab {...props} />
<QirTab {...props} />
</div>
);
Expand Down
15 changes: 15 additions & 0 deletions wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,21 @@ pub fn get_hir(
Ok(package.to_string())
}

#[wasm_bindgen]
pub fn get_rir(program: ProgramConfig) -> Result<Vec<String>, String> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an extra step, but for readability and type checking, please define a struct (which will be converted to a JS object) for the return type instead of a "tuple" (or JS array). Just makes it so much easier to use from the JS side.

Currently we use the serializable_type! macro. generate_docs/IDocFile in this file is an example you can follow. (It is a bit verbose, I plan to improve it in the future, but right now that's the pattern we use all over this module).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I avoided that as it is likely that there will be additional RIR returned such that we have a vec of RIR representing compilation/optimization steps so we can view the iteration of the compilation. We have other examples where we are wrapping complex types and returning arrays of them. It seemed overkill to do so when the value is just a string.

let (source_map, capabilities, language_features, store, deps) =
into_qsc_args(program, None).map_err(compile_errors_into_qsharp_errors_json)?;

qsc::codegen::qir::get_rir(
source_map,
language_features,
capabilities,
store,
&deps[..],
)
.map_err(interpret_errors_into_qsharp_errors_json)
}

struct CallbackReceiver<F>
where
F: FnMut(&str),
Expand Down
Loading