Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix segmentation fault when compiling va_arg intrinsic #4336

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

zyedidia
Copy link
Contributor

@zyedidia zyedidia commented Feb 27, 2023

The following code currently causes a segmentation fault in LDC when compiled:

alias va_list = void*;

pragma(LDC_va_arg) T va_arg(T)(va_list ap);

int foo(va_list ap) {
    return va_arg!(int)(ap);
}

This PR fixes that.

Note: the following code still causes a segfault during compilation, but I think this is a bug in LLVM, not LDC.

alias va_list = void*;

pragma(LDC_va_arg) T va_arg(T)(va_list ap);

string foo(va_list ap) {
    return va_arg!(string)(ap);
}

I will report that separately to the LLVM project.

Also, it seems like the D runtime that LDC uses does not make use of the va_arg intrinsic, and instead uses custom code for each architecture (from DMD I assume). What is the reason for this? Using some custom implementation instead of relying on the LLVM intrinsic seems much more prone to bugs. Perhaps some followup work to this PR would be to convert D runtime over to using the va_arg intrinsic for LDC instead of the (hacky?) one from DMD.

@zyedidia
Copy link
Contributor Author

Looks like this needs a little more work on aarch64 hosts? I'm not really able to debug that at the moment.

@kinke
Copy link
Member

kinke commented Feb 27, 2023

From what I've seen, that intrinsic is basically useless, and we need to do it manually in druntime. Under examples, https://llvm.org/docs/LangRef.html#va-arg-instruction states:

Note that the code generator does not yet fully support va_arg on many targets. Also, it does not currently support va_arg with aggregate types on any target.

A slice like string is an aggregate.

@dnadlinger
Copy link
Member

Yep, LLVM doesn't implement the target C ABI (the lowering is done in Clang); consequently, the intrinsic is also useless, as it would be missing the requisite translation of types according to the respective ABI details.

@zyedidia
Copy link
Contributor Author

That is unfortunate. It would still be good for LDC to support emitting the intrinsic, even if LLVM doesn't handle it well. Either that or the LDC intrinsic for it should not exist, and shouldn't be listed on the wiki here: https://wiki.dlang.org/LDC-specific_language_changes#LDC_va_.2A_for_variadic_argument_handling_intrinsics. For my target platforms (riscv64-unknown-elf and aarch64-none-elf), the LLVM va_arg instruction appears to work, but maybe I should just use the D runtime implementation because LLVM va_arg is half-baked.

@kinke
Copy link
Member

kinke commented Feb 27, 2023

Well years ago when I last looked at varargs stuff, the va_arg intrinsic was supported just fine by LDC, but as said, not usable. I'd have to check why it's now apparently segfaulting.

@kinke
Copy link
Member

kinke commented Feb 27, 2023

Ah, so the other 3 va_* intrinsics are intrinsic functions, but va_arg is an IR instruction (but a D function declaration). So we're nowadays e.g. hitting an assertion for

ldc/gen/llvmhelpers.cpp

Lines 1571 to 1573 in 981c58e

const auto llValue =
fdecl->llvmInternal != LLVMva_arg ? DtoCallee(fdecl) : nullptr;
return new DFuncValue(fdecl, llValue);

Besides skipping the IR declaration of the D function, we should probably handle va_arg (emitting the instruction instead of a call) in

ldc/gen/toir.cpp

Lines 713 to 723 in 981c58e

// handle magic inline asm
if (auto ve = e->e1->isVarExp()) {
if (auto fd = ve->var->isFuncDeclaration()) {
if (fd->llvmInternal == LLVMinline_asm) {
return DtoInlineAsmExpr(e->loc, fd, e->arguments, sretPointer);
}
if (fd->llvmInternal == LLVMinline_ir) {
return DtoInlineIRExpr(e->loc, fd, e->arguments, sretPointer);
}
}
}

@kinke
Copy link
Member

kinke commented Feb 27, 2023

This seemingly does the job:

diff --git a/gen/toir.cpp b/gen/toir.cpp
index 6a2c00052e..421b7d0e1a 100644
--- a/gen/toir.cpp
+++ b/gen/toir.cpp
@@ -719,6 +719,12 @@ public:
         if (fd->llvmInternal == LLVMinline_ir) {
           return DtoInlineIRExpr(e->loc, fd, e->arguments, sretPointer);
         }
+        if (fd->llvmInternal == LLVMva_arg) {
+          DValue *result = nullptr;
+          const auto success = DtoLowerMagicIntrinsic(p, fd, e, result);
+          assert(success);
+          return result;
+        }
       }
     }
 

Little test file:

import core.stdc.stdarg : va_list;

pragma(LDC_va_arg) T va_arg(T)(ref va_list ap);

int foo(...) {
    return va_arg!int(_argptr);
}

void main() {
    assert(foo(123) == 123);
}

Compiling with -mtriple=i686-linux-gnu is fine (haven't tried running it), and for riscv64-unknown-elf too. With an LLVM with enabled assertions, the LLVM limitations become obvious quite quickly:

  • -mtriple=x86_64-linux-gnu:
    ldc2: /home/mkinkelin/dev/llvm-project/llvm/lib/CodeGen/RegAllocFast.cpp:961: void {anonymous}::RegAllocFast::useVirtReg(llvm::MachineInstr&, unsigned int, llvm::Register): Assertion `(!MO.isKill() || LRI->LastUse == &MI) && "Invalid kill flag"' failed.
    
  • -mtriple=aarch64-none-elf:
    ldc2: /home/mkinkelin/dev/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:8212: llvm::SDValue llvm::AArch64TargetLowering::LowerVAARG(llvm::SDValue, llvm::SelectionDAG&) const: Assertion `Subtarget->isTargetDarwin() && "automatic va_arg instruction only works on Darwin"' failed.
    

@zyedidia
Copy link
Contributor Author

Ah I see that is unfortunate for aarch64-none-elf. I'll just use the D runtime version I think. I can close this PR if you want to commit your fix separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants