Skip to content

Instantly share code, notes, and snippets.

@vext01
Last active May 12, 2022 14:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vext01/12ee4e7220458bc6e3bb67b07bd9b1bd to your computer and use it in GitHub Desktop.
Save vext01/12ee4e7220458bc6e3bb67b07bd9b1bd to your computer and use it in GitHub Desktop.
Stackmap legalisation

Stackmap legalisation

Goal

The goal is to fix llvm/llvm-project#21657

TLDR: Passing illegally-typed live variable operands to llvm.experimental.stackmap (at -O1 or above) will make LLVM crash by an assertion failure.

The issue is that stackmap operands are emitted directly to target nodes in the selection DAG, which means their operands don't get legalised.

The diff below introduces a (non-target) stackmap DAG node, so that the stackmap node and its operands can take part in regular legalisations.

Progress

It works for some kinds of live variable, e.g. the failing example in the github issue now (appears to) work:

$ cat reg.ll
declare void @llvm.experimental.stackmap(i64, i32, ...)

define dso_local i32 @main(i32 %argc, i8** %argv) {
entry:
  %boom = icmp eq i32 %argc, 1
  call void (i64, i32, ...) @llvm.experimental.stackmap(i64 0, i32 0, i1 %boom)
  ret i32 0
}

Gives the stackmaps:

$ llvm-readelf a.out
LLVM StackMap Version: 3
Num Functions: 1
  Function address: 4400, stack size: 8, callsite record count: 1
Num Constants: 0
Num Records: 1
  Record ID: 0, instruction offset: 10
    1 locations:
      #1: Register R#0, size: 1
    0 live-outs: [ ]

And memory operands seem to work too:

declare void @llvm.experimental.stackmap(i64, i32, ...)

define dso_local i32 @main(i32 %argc, i8** %argv) {
entry:
  ;%boom = icmp eq i32 %argc, 1
  %a = alloca i32
  call void (i64, i32, ...) @llvm.experimental.stackmap(i64 0, i32 0, i32 %argc, i32* %a)
  ret i32 0
}

Gives:

LLVM StackMap Version: 3
Num Functions: 1
  Function address: 4400, stack size: 24, callsite record count: 1
Num Constants: 0
Num Records: 1
  Record ID: 0, instruction offset: 8
    2 locations:
      #1: Register R#5, size: 4
      #2: Direct R#6 + -4, size: 8
    0 live-outs: [ ]

The problem

Constant operands don't appear to work.

declare void @llvm.experimental.stackmap(i64, i32, ...)

define dso_local i32 @main(i32 %argc, i8** %argv) {
entry:
  call void (i64, i32, ...) @llvm.experimental.stackmap(i64 0, i32 0, i16 2)
  ret i32 0
}

Gives:

warning: overriding the module target triple with x86_64-unknown-linux-gnu [-Woverride-module]
Stackmap instr:  STACKMAP 0, 0, 2, implicit-def dead early-clobber $r11
Constant operand: implicit-def dead early-clobber $r11
clang-15: /home/vext01/research/ykllvm/llvm/lib/CodeGen/StackMaps.cpp:220: const llvm::MachineOperand* llvm::StackMaps::parseOperand(llvm::MachineInstr::const_mop_iterator, llvm::MachineInstr::const_mop_iterator, llvm::StackMaps::LocationVec&, llvm::StackMaps::LiveOutVec&) const: Assertion `MOI->isImm() && "Expected constant operand."' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /home/vext01/research/ykllvm/build/bin/clang-15 -cc1 -triple x86_64-unknown-linux-gnu -emit-obj --mrelax-relocations -disable-free -clear-ast-before-backend -main-file-name const.ll -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -mllvm -treat-scalable-fixed-error-as-warning -debugger-tuning=gdb -fcoverage-compilation-dir=/home/vext01/research/ykllvm -resource-dir /home/vext01/research/ykllvm/build/lib/clang/15.0.0 -O2 -fdebug-compilation-dir=/home/vext01/research/ykllvm -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/const-d8a0ee.o -x ir const.ll
1.	Code generation
2.	Running pass 'Function Pass Manager' on module 'const.ll'.
3.	Running pass 'X86 Assembly Printer' on function '@main'
 #0 0x000055dd46dc7270 PrintStackTraceSignalHandler(void*) Signals.cpp:0:0
 #1 0x000055dd46dc4abc SignalHandler(int) Signals.cpp:0:0
 #2 0x00007f76cc797140 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x14140)
 #3 0x00007f76cc27ace1 raise ./signal/../sysdeps/unix/sysv/linux/raise.c:51:1
 #4 0x00007f76cc264537 abort ./stdlib/abort.c:81:7
 #5 0x00007f76cc26440f get_sysdep_segment_value ./intl/loadmsgcat.c:509:8
 #6 0x00007f76cc26440f _nl_load_domain ./intl/loadmsgcat.c:970:34
 #7 0x00007f76cc273662 (/lib/x86_64-linux-gnu/libc.so.6+0x34662)
 #8 0x000055dd46197140 llvm::StackMaps::parseOperand(llvm::MachineOperand const*, llvm::MachineOperand const*, llvm::SmallVector<llvm::StackMaps::Location, 8u>&, llvm::SmallVector<llvm::StackMaps::LiveOutReg, 8u>&) const (/home/vext01/research/ykllvm/build/bin/clang-15+0x2ec8140)
 #9 0x000055dd46199549 llvm::StackMaps::recordStackMapOpers(llvm::MCSymbol const&, llvm::MachineInstr const&, unsigned long, llvm::MachineOperand const*, llvm::MachineOperand const*, bool) (/home/vext01/research/ykllvm/build/bin/clang-15+0x2eca549)
#10 0x000055dd4619ab3d llvm::StackMaps::recordStackMap(llvm::MCSymbol const&, llvm::MachineInstr const&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x2ecbb3d)
#11 0x000055dd4572a60e llvm::X86AsmPrinter::LowerSTACKMAP(llvm::MachineInstr const&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x245b60e)
#12 0x000055dd45730a03 llvm::X86AsmPrinter::emitInstruction(llvm::MachineInstr const*) (/home/vext01/research/ykllvm/build/bin/clang-15+0x2461a03)
#13 0x000055dd47bd544f llvm::AsmPrinter::emitFunctionBody() (/home/vext01/research/ykllvm/build/bin/clang-15+0x490644f)
#14 0x000055dd4572244b llvm::X86AsmPrinter::runOnMachineFunction(llvm::MachineFunction&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x245344b)
#15 0x000055dd45f702c0 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x2ca12c0)
#16 0x000055dd46490e34 llvm::FPPassManager::runOnFunction(llvm::Function&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x31c1e34)
#17 0x000055dd46491051 llvm::FPPassManager::runOnModule(llvm::Module&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x31c2051)
#18 0x000055dd46491ce5 llvm::legacy::PassManagerImpl::run(llvm::Module&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x31c2ce5)
#19 0x000055dd4714124e clang::EmitBackendOutput(clang::DiagnosticsEngine&, clang::HeaderSearchOptions const&, clang::CodeGenOptions const&, clang::TargetOptions const&, clang::LangOptions const&, llvm::StringRef, llvm::Module*, clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream> >) (/home/vext01/research/ykllvm/build/bin/clang-15+0x3e7224e)
#20 0x000055dd47f87312 clang::CodeGenAction::ExecuteAction() (/home/vext01/research/ykllvm/build/bin/clang-15+0x4cb8312)
#21 0x000055dd47873f99 clang::FrontendAction::Execute() (/home/vext01/research/ykllvm/build/bin/clang-15+0x45a4f99)
#22 0x000055dd477fda86 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/home/vext01/research/ykllvm/build/bin/clang-15+0x452ea86)
#23 0x000055dd47935c48 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/home/vext01/research/ykllvm/build/bin/clang-15+0x4666c48)
#24 0x000055dd444d256a cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/home/vext01/research/ykllvm/build/bin/clang-15+0x120356a)
#25 0x000055dd444cde84 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&) driver.cpp:0:0
#26 0x000055dd4442408a main (/home/vext01/research/ykllvm/build/bin/clang-15+0x115508a)
#27 0x00007f76cc265d0a __libc_start_main ./csu/../csu/libc-start.c:308:16
#28 0x000055dd444cd82a _start (/home/vext01/research/ykllvm/build/bin/clang-15+0x11fe82a)
clang-15: error: unable to execute command: Aborted
clang-15: error: clang frontend command failed due to signal (use -v to see invocation)
clang version 15.0.0 (https://vext01@github.com/vext01/llvm-project.git 11c4199f6bb49be3aacc6e84cb49e073612f5f68)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/vext01/research/ykllvm/build/bin
clang-15: note: diagnostic msg: Error generating preprocessed source(s) - no preprocessable inputs.

As the debug prints show, the constant live variable operand has been put into a register:

Stackmap instr:  STACKMAP 0, 0, 2, implicit-def dead early-clobber $r11
Constant operand: implicit-def dead early-clobber $r11

This seems to happen at some point after the selection stage. The post-select, pre-scheduling DAG (llc -view-sched-dags) looks OK to me: https://theunixzoo.co.uk/random/sm-const-dag.png

Note that operand 2 of the stackmap node is a constant. But by code-gen it has changed to a register operand, and LLVM dislikes this.

What happened?

Other questions

  • Why is the stackmap wrapped in a CALLSEQ when building the initial DAG?
  • What are the red glue edges for? They are only briefly mentioned in passing in the docs.
@vext01
Copy link
Author

vext01 commented May 12, 2022

As pointed out by Tim, setOperationAction(ISD::STACKMAP, MVT::Any, Custom); can go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment