Skip to content

Commit c8355db

Browse files
committed
web: surface optimizer transforms in the tracer instruction list
Show the tailcall and inline transform annotations inline in the tracer's instruction list, so the optimizer passes applied to each instruction are visible as you step through the trace.
1 parent c00252e commit c8355db

2 files changed

Lines changed: 99 additions & 4 deletions

File tree

‎packages/web/src/theme/ProgramExample/TraceDrawer.css‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,14 @@
270270
border-left: 3px solid #8250df;
271271
}
272272

273+
/* Inline boundary banner: same purple tint, dashed edge to echo the
274+
virtual-activation badge (no real call occurred). */
275+
.call-info-inline {
276+
background: rgba(130, 80, 223, 0.12);
277+
color: var(--ifm-color-content);
278+
border-left: 3px dashed #8250df;
279+
}
280+
273281
.call-stack-tailcall,
274282
.call-stack-inline {
275283
margin-left: 4px;
@@ -489,6 +497,35 @@
489497
color: var(--ifm-color-content);
490498
}
491499

500+
/* Instructions rewritten by the optimizer (transform contexts): a purple
501+
inset accent + tint so the spliced/rewritten region reads as a block in
502+
the list. The inset shadow avoids shifting the row layout, and the
503+
two-class .opcode-item.active rule still wins for the active row. */
504+
.opcode-item-inline,
505+
.opcode-item-tailcall {
506+
box-shadow: inset 3px 0 0 rgba(130, 80, 223, 0.7);
507+
background: rgba(130, 80, 223, 0.06);
508+
}
509+
510+
/* Per-instruction transform marker, right-aligned in the row. */
511+
.opcode-transform-tag {
512+
margin-left: auto;
513+
padding: 0 5px;
514+
border-radius: 8px;
515+
font-size: 10px;
516+
font-weight: 600;
517+
white-space: nowrap;
518+
background: rgba(130, 80, 223, 0.15);
519+
color: var(--ifm-color-content);
520+
border: 1px solid rgba(130, 80, 223, 0.45);
521+
}
522+
523+
/* Inline markers read as "virtual" (dashed), matching the call-stack badge. */
524+
.opcode-transform-inline {
525+
border-style: dashed;
526+
font-style: italic;
527+
}
528+
492529
/* Instruction object footer - user-resizable, scrolls internally */
493530
.instruction-object-panel {
494531
position: relative;

‎packages/web/src/theme/ProgramExample/TraceDrawer.tsx‎

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,18 @@ function TraceDrawerContent(): JSX.Element {
252252
[programsTrace, formatPcToInstruction, currentStep],
253253
);
254254

255+
// Compiler transform tags per trace step, so the instruction list can
256+
// mark which instructions the optimizer rewrote (e.g. the spliced
257+
// instructions of an inlined body) without stepping onto each one.
258+
const transformsByStep = useMemo<string[][]>(
259+
() =>
260+
trace.map((s) => {
261+
const fi = formatPcToInstruction.get(s.pc);
262+
return fi ? extractTransformFromInstruction(fi) : [];
263+
}),
264+
[trace, formatPcToInstruction],
265+
);
266+
255267
// Resolve argument values for call stack frames
256268
const argCacheRef = useRef<Map<number, ResolvedArg[]>>(new Map());
257269

@@ -721,9 +733,11 @@ function TraceDrawerContent(): JSX.Element {
721733
{currentCallInfo && (
722734
<div
723735
className={`call-info-bar ${
724-
currentCallInfo.isTailCall
725-
? "call-info-tailcall"
726-
: `call-info-${currentCallInfo.kind}`
736+
currentCallInfo.isInline
737+
? "call-info-inline"
738+
: currentCallInfo.isTailCall
739+
? "call-info-tailcall"
740+
: `call-info-${currentCallInfo.kind}`
727741
}`}
728742
>
729743
{formatCallBanner(currentCallInfo)}
@@ -737,6 +751,7 @@ function TraceDrawerContent(): JSX.Element {
737751
trace={trace}
738752
currentStep={currentStep}
739753
onStepClick={setCurrentStep}
754+
transformsByStep={transformsByStep}
740755
/>
741756
</div>
742757

@@ -848,12 +863,15 @@ interface OpcodeListProps {
848863
trace: TraceStep[];
849864
currentStep: number;
850865
onStepClick: (index: number) => void;
866+
/** Compiler transform tags per trace step (same index as `trace`). */
867+
transformsByStep: string[][];
851868
}
852869

853870
function OpcodeList({
854871
trace,
855872
currentStep,
856873
onStepClick,
874+
transformsByStep,
857875
}: OpcodeListProps): JSX.Element {
858876
// Render the full instruction list; the panel scrolls internally.
859877
// Keep the active step scrolled into view as the trace advances.
@@ -867,18 +885,47 @@ function OpcodeList({
867885
<div className="opcode-list">
868886
{trace.map((step, index) => {
869887
const isActive = index === currentStep;
888+
const transforms = transformsByStep[index] ?? [];
889+
const isInline = transforms.includes("inline");
890+
const isTailCall = transforms.includes("tailcall");
891+
const className = [
892+
"opcode-item",
893+
isActive ? "active" : "",
894+
isInline ? "opcode-item-inline" : "",
895+
isTailCall ? "opcode-item-tailcall" : "",
896+
]
897+
.filter(Boolean)
898+
.join(" ");
870899
return (
871900
<div
872901
key={index}
873902
ref={isActive ? activeRef : undefined}
874-
className={`opcode-item ${isActive ? "active" : ""}`}
903+
className={className}
875904
onClick={() => onStepClick(index)}
876905
>
877906
<span className="opcode-index">{index + 1}</span>
878907
<span className="opcode-pc">
879908
0x{step.pc.toString(16).padStart(4, "0")}
880909
</span>
881910
<code className="opcode-name">{step.opcode}</code>
911+
{isInline && (
912+
<span
913+
className="opcode-transform-tag opcode-transform-inline"
914+
title={'transform: ["inline"] — spliced from the inlined body'}
915+
>
916+
⧉ inline
917+
</span>
918+
)}
919+
{isTailCall && (
920+
<span
921+
className="opcode-transform-tag opcode-transform-tailcall"
922+
title={
923+
'transform: ["tailcall"] — tail-call optimized back-edge'
924+
}
925+
>
926+
⮌ tailcall
927+
</span>
928+
)}
882929
</div>
883930
);
884931
})}
@@ -1065,6 +1112,17 @@ function formatCallBanner(info: CallInfo): string {
10651112
if (info.isTailCall) {
10661113
return `Tail call: ${name} (frame reused)`;
10671114
}
1115+
if (info.isInline) {
1116+
// No call actually occurs — the body was spliced in at compile time.
1117+
switch (info.kind) {
1118+
case "invoke":
1119+
return `Inlined ${name}${params} (no call — body spliced in)`;
1120+
case "return":
1121+
return `End of inlined ${name}()`;
1122+
case "revert":
1123+
return `Reverted in inlined ${name}()`;
1124+
}
1125+
}
10681126
switch (info.kind) {
10691127
case "invoke": {
10701128
const prefix = info.callType === "create" ? "Creating" : "Calling";

0 commit comments

Comments
 (0)