DPC++ device frontend never terminates on imgui_internal.h
- Dominant language
- LLVM
- Stars
- 1.5k
- Forks
- 854
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 137
Description
### Describe the bug
Just including `imgui_internal.h` (Dear ImGui v1.92.9b) spins "forever", 100% CPU, memory flat at 54 MB.
| compiler | |
| --- | --- |
| oneAPI 2025.1 / .2 / .3 | ok ~1.5 s |
| v6.2.0 | ok 0.9 s |
| v7.0.0 | killed at 300 s |
| nightly-2026-08-17 | killed at 90 s, i.e. not fixed upstream |
### To reproduce
```sh
git clone --depth 1 --branch v1.92.9b https://github.com/ocornut/imgui.git
echo '#include "imgui_internal.h"' > repro.cpp
clang++ -fsycl -fsycl-device-only -fsyntax-only -Iimgui repro.cpp
```
Device pass only. `-fsycl-is-host` and plain C++ 0.2 s, preprocessing unaffected.
Up to v7.0.0 one cc1 flag on the host triple suffices, no SYCL headers: `clang++ -fsyntax-only -Xclang -fsycl-is-device -Iimgui repro.cpp`.
Newer nightlies reject a non-SPIR-V triple there, so use `-fsycl -fsycl-device-only` for cross-version runs.
Including `imgui.h` alone is fine. Needs the dense mutually-referential graph of `ImGuiContext`/`ImGuiWindow`.
Attaching files failed, minimal self-contained (no `#include`) repro by reducing the header, ill-formed by design (`-ferror-limit=0`): 0.1 s v6.2.0 vs 2.0 s v7.0.0, 20x slowdown (not hanging):
```cpp
typedef signed short ImS16;
typedef unsigned short ImU16;
typedef signed int ImS32;
typedef unsigned int ImU32;
typedef signed long long ImS64;
typedef unsigned long long ImU64;
struct ImDrawChannel;
struct ImDrawCmd;
struct ImDrawData;
struct ImDrawList;
struct ImDrawListSharedData;
struct ImDrawListSplitter;
struct ImDrawVert;
struct ImFont;
struct ImFontAtlas;
struct ImFontAtlasBuilder;
struct ImFontConfig;
struct ImFontGlyph;
struct ImFontGlyphRangesBuilder;
struct ImFontLoader;
struct ImTextureData;
struct ImTextureRect;
struct ImGuiSelectionExternalStorage;
struct ImGuiSelectionRequest;
struct ImGuiSizeCallbackData;
struct ImGuiStorage;
enum ImGuiDir : int;
enum ImGuiKey : int;
enum ImGuiMouseSource : int;
enum ImGuiSortDirection : ImU8;
typedef int ImGuiCol;
typedef int ImGuiListClipperFlags;
typedef unsigned short ImWchar16;
template struct ImVector {
int Size;
int Capacity;
T *Data;
ImVec2 WindowTitleAlign;
ImGuiDir WindowMenuButtonPosition;
float ChildRounding;
float ChildBorderSize;
float PopupRounding;
ImVec2 DisplayWindowPadding;
ImVec2 DisplaySafeAreaPadding;
float MouseCursorScale;
bool AntiAliasedLines;
bool AntiAliasedLinesUseTex;
bool AntiAliasedFill;
float CurveTessellationTol;
float CircleTessellationMaxError;
ImVec4 Colors[ImGuiCol_COUNT];
float HoverStationaryDelay;
float HoverDelayShort;
float HoverDelayNormal;
ImGuiHoveredFlags HoverFlagsForTooltipMouse;
ImGuiHoveredFlags HoverFlagsForTooltipNav;
ImFontAtlas *Fonts;
ImFont *FontDefault;
void SetStatus(ImTextureStatus status) {
Status = status;
if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame &&
Pixels != nullptr)
Status = ImTextureStatus_WantCreate;
}
ImFont *DstFont;
const ImFontLoader *FontLoader;
void *FontLoaderData;
ImFontAtlasFlags_NoBakedLines = 1 << 2,
};
struct ImFontAtlas {
ImFontAtlas();
~ImFontAtlas();
ImFont *AddFont(const ImFontConfig *font_cfg);
ImFont *AddFontDefault(const ImFontConfig *font_cfg = 0);
ImVector TexList;
bool RendererHasTextures;
bool TexIsBuilt;
ImVec2 TexUvWhitePixel;
ImVector Fonts;
ImVector Sources;
ImVec4 TexUvLines[(32) + 1];
int TexNextUniqueID;
int FontNextUniqueID;
ImVector DrawListSharedDatas;
ImFontAtlasBuilder *Builder;
const ImFontLoader *FontLoader;
const char *FontLoaderName;
void *FontLoaderData;
int RefCount;
ImGuiContext *OwnerContext;
ImVector IndexAdvanceX;
float FallbackAdvanceX;
float Size;
float RasterizerDensity;
ImVector IndexLookup;
ImVector Glyphs;
int FallbackGlyphIndex;
float Ascent, Descent;
unsigned int MetricsTotalSurface : 26;
unsigned int WantDestroy : 1;
unsigned int LoadNoFallback : 1;
unsigned int LoadNoRenderOnLayout : 1;
int LastUsedFrame;
ImGuiID BakedId;
ImFont *OwnerFont;
};
struct ImFont {
ImFontBaked *LastBaked;
ImFontAtlas *OwnerAtlas;
ImFontFlags Flags;
float CurrentRasterizerDensity;
ImVector Sources;
}
}
;
struct ImRect {
ImVec2 Min;
ImVec2 Max;
ImVector TempBuffer;
ImVector DrawLists;
ImVector FontAtlases;
ImFont *Font;
ImVector ItemWidthStack;
ImVector TextWrapPosStack;
};
struct ImGuiWindow {
ImGuiWindowFlags Flags;
ImRect OuterRectClipped;
ImRect InnerRect;
ImRect InnerClipRect;
ImRect WorkRect;
ImRect ParentWorkRect;
ImRect ClipRect;
ImGuiWindow *ParentWindow;
ImGuiWindow *ParentWindowInBeginStack;
ImGuiWindow *RootWindow;
ImGuiWindow *RootWindowPopupTree;
};
ImGuiWindow *OuterWindow;
```
### Environment
- OS: independent
- Target device and vendor: N/A, frontend only
- DPC++ version: bisected
```
last good: nightly-2025-09-24 3276ddd75dae5563f336737daddcbb151484702b
first bad: nightly-2025-09-30 0ff1a5c2b4e4bc56799ec2dd17a89c3c57608890
```
### Additional context
Hypothesis from Claude:
`checkSYCLType()` in clang/lib/Sema/SemaSYCL.cpp, from `SemaSYCL::checkSYCLDeviceVarDecl`, device mode only. Walks all fields, follows pointers/arrays into the pointee's fields. `Visited` passed **by value** => memoizes per DFS path, not globally => enumerates simple paths instead of visiting each type once. See [#17313](https://github.com/intel/llvm/issues/17313) (open, wants checkSYCLType merged into `Sema::checkTypeSupport`); mentions no perf impact, but same function.
Exponential in both versions: on v6.2.0, nesting `struct W { ImGuiContext* a; ImGuiContext* b; };`
N deep multiplies time by ~1.8/level (0.7, 0.9, 1.3, 2.0, 3.7 s). So not a new loop — something in the Sept 2025 window weakened dedup enough to push ImGui's graph past any usable threshold;
v6.2.0 needs ~9 extra doublings to reach v7.0.0.
Contributor guide
Research direction
Start by reproducing the device-only command with imgui_internal.h and the reduced self-contained example, then inspect checkSYCLType() in clang/lib/Sema/SemaSYCL.cpp and its use from SemaSYCL::checkSYCLDeviceVarDecl. Compare behavior around the bisected nightly commits and the existing context in #17313. Done means the frontend no longer becomes unusably slow on this include while preserving the relevant device checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100