llvm.gcroot trouble with non-i8* allocas

I’m allocating { i8*, i32 } on the stack, and would like to add this as a GC root, but I’m having trouble figuring this out.

This works as expected:

declare void @llvm.gcroot(i8** %ptrloc, i8* %metadata)

define i8* @bar(i8* %x) gc “shadow-stack” {
%objptr = alloca i8*
call void @llvm.gcroot(i8** %objptr, i8* null)
store i8* %x, i8** %objptr
%v = load i8*, i8** %objptr
ret i8* %v

However, when I have this:

define { i8*, i32 } @foo({ i8*, i32 } %x) gc “shadow-stack” {
%objptr = alloca { i8*, i32 }
%rootptr = bitcast { i8*, i32 }* %objptr to i8**
call void @llvm.gcroot(i8** %rootptr, i8* null)
store { i8*, i32 } %x, { i8*, i32 }* %objptr
%v = load { i8*, i32 }, { i8*, i32 }* %objptr
ret { i8*, i32 } %v

llc says: llvm.gcroot parameter #1 must either be a pointer alloca, or argument #2 must be a non-null constant.
call void @llvm.gcroot(i8** %rootptr, i8* null)

…and if I do as it asks and add a metadata then I get a crash:

@info = constant i8 1

define { i8*, i32 } @quux({ i8*, i32 } %x) gc “shadow-stack” {
%objptr = alloca { i8*, i32 }
%rootptr = bitcast { i8*, i32 }* %objptr to i8**
call void @llvm.gcroot(i8** %rootptr, i8* @info)
store { i8*, i32 } %x, { i8*, i32 }* %objptr
%v = load { i8*, i32 }, { i8*, i32 }* %objptr
ret { i8*, i32 } %v

leads llc into this:

Assertion failed: isa(Val) && “cast() argument of incompatible type!”, file C:\Users\nikod\src\llvm\include\llvm/Support/Casting.h, line 236
Wrote crash dump file “C:\Users\nikod\AppData\Local\Temp\llc.exe-7b1546.dmp”
#0 0x01a26809 HandleAbort c:\users\nikod\src\llvm\lib\support\windows\signals.inc:405:0
#1 0x75804188 (C:\WINDOWS\System32\ucrtbase.dll+0xa4188)
#2 0x75805422 (C:\WINDOWS\System32\ucrtbase.dll+0xa5422)
#3 0x75806dc5 (C:\WINDOWS\System32\ucrtbase.dll+0xa6dc5)
#4 0x75806ce8 (C:\WINDOWS\System32\ucrtbase.dll+0xa6ce8)
#5 0x7580618b (C:\WINDOWS\System32\ucrtbase.dll+0xa618b)
#6 0x75806e26 (C:\WINDOWS\System32\ucrtbase.dll+0xa6e26)
#7 0x013786a6 InsertRootInitializers c:\users\nikod\src\llvm\lib\codegen\gcrootlowering.cpp:172:0
#8 0x01378d97 anonymous namespace'::LowerIntrinsics::PerformDefaultLowering c:\users\nikod\src\llvm\lib\codegen\gcrootlowering.cpp:249:0 #9 0x01379986 anonymous namespace’::LowerIntrinsics::runOnFunction c:\users\nikod\src\llvm\lib\codegen\gcrootlowering.cpp:195:0
#10 0x016a343c llvm::FPPassManager::runOnFunction(class llvm::Function &) c:\users\nikod\src\llvm\lib\ir\legacypassmanager.cpp:1513:0
#11 0x016a35e4 llvm::FPPassManager::runOnModule(class llvm::Module &) c:\users\nikod\src\llvm\lib\ir\legacypassmanager.cpp:1533:0
#12 0x016a382c `anonymous namespace’::MPPassManager::runOnModule c:\users\nikod\src\llvm\lib\ir\legacypassmanager.cpp:1590:0
#13 0x016a2e0e llvm::legacy::PassManagerImpl::run(class llvm::Module &) c:\users\nikod\src\llvm\lib\ir\legacypassmanager.cpp:1693:0
#14 0x01000a62 compileModule c:\users\nikod\src\llvm\tools\llc\llc.cpp:534:0
#15 0x010059ca main c:\users\nikod\src\llvm\tools\llc\llc.cpp:289:0
#16 0x01be99e9 _scrt_common_main_seh f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:253:0
#17 0x77178744 (C:\WINDOWS\System32\KERNEL32.DLL+0x18744)
#18 0x7737582d (C:\WINDOWS\SYSTEM32\ntdll.dll+0x6582d)
#19 0x773757fd (C:\WINDOWS\SYSTEM32\ntdll.dll+0x657fd)

(This is the same assertion failure that my full code produces, and which lead me to trying out things with minimal case and llc.)

What I can see in the debugger isn’t immediately illuminating. Any clues? Am I missing the obvious here?

This is:

LLVM (http://llvm.org/):
LLVM version 4.0.1
DEBUG build with assertions.
Default target: i686-pc-windows-msvc
Host CPU: skylake


– nikodemus

Solved by using alloca i8*, i32 2 which the system seems happy enough with, and bitcasting to the actual type for rest of the code after llvm.gcroot.

Not entirely sure if this is a terrible workaround or exactly the way gcroot is supposed to be used…

At this point gcroot is essentially deprecated. It hasn't been officially deprecated and probably won't be in the near future, but the only practical user/contributor to LLVM GC support is using statepoint and the related infrastructure. Not saying you need to change your approach, but might be something you want to keep in mind.