Skip to content

Commit 02acf9d

Browse files
committed
Warn when assembly doesn't appear to generate symbols
1 parent 8ce7fca commit 02acf9d

File tree

5 files changed

+96
-2
lines changed

5 files changed

+96
-2
lines changed

ui/frontend/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ node_modules
1919
!Header.tsx
2020
!HelpExample.tsx
2121
!Notifications.tsx
22+
!Output/Assembly.tsx
2223
!Output/OutputPrism.tsx
2324
!Output/PaneWithCode.tsx
2425
!Output/PaneWithMir.tsx

ui/frontend/Output.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { changeFocus } from './reducers/output/meta';
44
import { Focus } from './types';
55

66
import Execute from './Output/Execute';
7+
import Assembly from './Output/Assembly';
78
import Gist from './Output/Gist';
89
import SimplePane from './Output/SimplePane';
910
import PaneWithMir from './Output/PaneWithMir';
@@ -73,7 +74,7 @@ const Output: React.FC = () => {
7374
{focus === Focus.Clippy && <SimplePane {...clippy} kind="clippy" />}
7475
{focus === Focus.Miri && <SimplePane {...miri} kind="miri" />}
7576
{focus === Focus.MacroExpansion && <SimplePane {...macroExpansion} kind="macro-expansion" />}
76-
{focus === Focus.Asm && <PaneWithCode {...assembly} kind="asm" />}
77+
{focus === Focus.Asm && <Assembly />}
7778
{focus === Focus.LlvmIr && <PaneWithCode {...llvmIr} kind="llvm-ir" />}
7879
{focus === Focus.Mir && <PaneWithMir {...mir} kind="mir" />}
7980
{focus === Focus.Hir && <PaneWithMir {...hir} kind="hir" />}

ui/frontend/Output/Assembly.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
3+
import { useAppSelector } from '../hooks';
4+
import * as selectors from '../selectors';
5+
import PaneWithCode from './PaneWithCode';
6+
import Section from './Section';
7+
8+
const Assembly: React.FC = () => {
9+
const assembly = useAppSelector((state) => state.output.assembly);
10+
const isAssemblyInProgress = useAppSelector(selectors.isAssemblyInProgressSelector);
11+
const hasAssemblySymbols = useAppSelector(selectors.hasAssemblySymbolsSelector);
12+
13+
const warnAboutNoSymbols = !isAssemblyInProgress && !hasAssemblySymbols;
14+
15+
return (
16+
<PaneWithCode {...assembly} kind="asm">
17+
{warnAboutNoSymbols ? (
18+
<Section kind="warning" label="Warnings">
19+
No symbols detected — they may have been optimized away.
20+
{'\n'}
21+
Add the <code>#[unsafe(no_mangle)]</code> attribute to
22+
{'\n'}
23+
functions you want to see assembly for. Generic functions
24+
{'\n'}
25+
only generate assembly when concrete types are provided.
26+
</Section>
27+
) : null}
28+
</PaneWithCode>
29+
);
30+
};
31+
32+
export default Assembly;

ui/frontend/selectors/index.spec.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import reducer from '../reducers';
22
import { editCode } from '../reducers/code';
3-
import { hasMainFunctionSelector } from './index';
3+
import { hasAssemblySymbolsSelector, hasMainFunctionSelector } from './index';
44

55
const buildState = (code: string) => reducer(undefined, editCode(code));
66

@@ -64,3 +64,51 @@ describe('checking for a main function', () => {
6464
expect(doMainFunctionSelector('fn main(/* comment */) {')).toBe(true);
6565
});
6666
});
67+
68+
const doHasAssemblySymbolSelector = (code: string) => {
69+
const state = reducer(
70+
{ output: { assembly: { code, requestsInProgress: 0 } } },
71+
{ type: 'test' },
72+
);
73+
return hasAssemblySymbolsSelector(state);
74+
};
75+
76+
describe('checking for symbols in assembly output', () => {
77+
test('empty code has no symbols', () => {
78+
expect(doHasAssemblySymbolSelector('')).toBe(false);
79+
});
80+
81+
test('instructions are not symbols', () => {
82+
// x86_64
83+
expect(doHasAssemblySymbolSelector(' movl %edi, 4(%rsp)')).toBe(false);
84+
// arm
85+
expect(doHasAssemblySymbolSelector(' sub sp, sp, #32')).toBe(false);
86+
});
87+
88+
test('mangled symbols are symbols', () => {
89+
expect(doHasAssemblySymbolSelector('_ZN10playground3add17h903bea7e047dfb9fE:')).toBe(true);
90+
});
91+
92+
test('unmangled symbols are symbols', () => {
93+
expect(doHasAssemblySymbolSelector('playground::add:')).toBe(true);
94+
});
95+
96+
test('unmangled symbols from traits are symbols', () => {
97+
expect(
98+
doHasAssemblySymbolSelector(
99+
'<rand::rngs::reseeding::ReseedingCore<R,Rsdr> as rand_core::block::BlockRngCore>::generate:',
100+
),
101+
).toBe(true);
102+
});
103+
104+
test('symbols with comments are symbols', () => {
105+
// x86_64
106+
expect(doHasAssemblySymbolSelector('add: # @add')).toBe(
107+
true,
108+
);
109+
// arm
110+
expect(doHasAssemblySymbolSelector('add: // @add')).toBe(
111+
true,
112+
);
113+
});
114+
});

ui/frontend/selectors/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,18 @@ export const compileRequestPayloadSelector = createSelector(
518518
}),
519519
);
520520

521+
export const isAssemblyInProgressSelector = createSelector(
522+
(state: State) => state.output.assembly,
523+
asm => asm.requestsInProgress > 0,
524+
);
525+
526+
const ASSEMBLY_SYMBOLS_RE = /^[_a-zA-Z0-9<>, ]+:/m;
527+
528+
export const hasAssemblySymbolsSelector = createSelector(
529+
(state: State) => state.output.assembly,
530+
asm => !!asm.code?.match(ASSEMBLY_SYMBOLS_RE),
531+
);
532+
521533
export const themeSelector = createSelector(
522534
(state: State) => state,
523535
(state) => state.configuration.theme,

0 commit comments

Comments
 (0)