From a5fe945b4f61b9f213e9917cf0fede73031e33b4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 23 Mar 2022 13:20:13 -0400 Subject: [PATCH] ccall: add support for automatic llvmcall mangling (#44697) Sometimes the mangling changes by version, or is unclear, so we allow that to auto-upgrade here. Code from llvm-alloc-opt.cpp. Fix #44694 --- src/ccall.cpp | 39 +++++++++++++++++++++++++++++++-------- src/llvm-alloc-opt.cpp | 4 ++-- test/llvmcall2.jl | 24 ++++++++++++++++++++---- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index a7e8b0f4daa7c..bbcc1a3ba075f 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1927,14 +1927,37 @@ jl_cgval_t function_sig_t::emit_a_ccall( } else { assert(symarg.f_name != NULL); - const char* f_name = symarg.f_name; - bool f_extern = (strncmp(f_name, "extern ", 7) == 0); - if (f_extern) - f_name += 7; - llvmf = jl_Module->getOrInsertFunction(f_name, functype).getCallee(); - if (!f_extern && (!isa(llvmf) || - cast(llvmf)->getIntrinsicID() == - Intrinsic::not_intrinsic)) { + StringRef f_name(symarg.f_name); + bool f_extern = f_name.consume_front("extern "); + llvmf = NULL; + if (f_extern) { + llvmf = jl_Module->getOrInsertFunction(f_name, functype).getCallee(); + if (!isa(llvmf) || cast(llvmf)->isIntrinsic() || cast(llvmf)->getFunctionType() != functype) + llvmf = NULL; + } + else if (f_name.startswith("llvm.")) { + // compute and verify auto-mangling for intrinsic name + auto ID = Function::lookupIntrinsicID(f_name); + if (ID != Intrinsic::not_intrinsic) { + // Accumulate an array of overloaded types for the given intrinsic + // and compute the new name mangling schema + SmallVector overloadTys; + SmallVector Table; + getIntrinsicInfoTableEntries(ID, Table); + ArrayRef TableRef = Table; + auto res = Intrinsic::matchIntrinsicSignature(functype, TableRef, overloadTys); + if (res == Intrinsic::MatchIntrinsicTypes_Match) { + bool matchvararg = !Intrinsic::matchIntrinsicVarArg(functype->isVarArg(), TableRef); + if (matchvararg) { + Function *intrinsic = Intrinsic::getDeclaration(jl_Module, ID, overloadTys); + assert(intrinsic->getFunctionType() == functype); + if (intrinsic->getName() == f_name || Intrinsic::getBaseName(ID) == f_name) + llvmf = intrinsic; + } + } + } + } + if (llvmf == NULL) { emit_error(ctx, "llvmcall only supports intrinsic calls"); return jl_cgval_t(ctx.builder.getContext()); } diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index a4c1a2596b2ae..19b9660378c2c 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -517,8 +517,8 @@ void Optimizer::replaceIntrinsicUseWith(IntrinsicInst *call, Intrinsic::ID ID, auto res = Intrinsic::matchIntrinsicSignature(newfType, TableRef, overloadTys); assert(res == Intrinsic::MatchIntrinsicTypes_Match); (void)res; - bool matchvararg = Intrinsic::matchIntrinsicVarArg(newfType->isVarArg(), TableRef); - assert(!matchvararg); + bool matchvararg = !Intrinsic::matchIntrinsicVarArg(newfType->isVarArg(), TableRef); + assert(matchvararg); (void)matchvararg; } auto newF = Intrinsic::getDeclaration(call->getModule(), ID, overloadTys); diff --git a/test/llvmcall2.jl b/test/llvmcall2.jl index cfd20d210bfd7..8926b962a35c6 100644 --- a/test/llvmcall2.jl +++ b/test/llvmcall2.jl @@ -37,10 +37,26 @@ function ceilfloor(x::Float64) end @test ceilfloor(7.4) == 8.0 -# support for calling external functions -begin - f() = ccall("time", llvmcall, Cvoid, (Ptr{Cvoid},), C_NULL) - @test_throws ErrorException f() +let err = ErrorException("llvmcall only supports intrinsic calls") + # support for calling external functions + @test_throws err @eval ccall("time", llvmcall, Cvoid, (Ptr{Cvoid},), C_NULL) g() = ccall("extern time", llvmcall, Cvoid, (Ptr{Cvoid},), C_NULL) g() + @test_throws err @eval ccall("extern llvm.floor", llvmcall, Float64, (Float64,), 0.0) + + # support for mangling + @test (@eval ccall("llvm.floor.f64", llvmcall, Float64, (Float64,), 0.0)) === 0.0 + @test (@eval ccall("llvm.floor", llvmcall, Float64, (Float64,), 0.0), + ccall("llvm.floor", llvmcall, Float32, (Float32,), 0.0)) === (0.0, 0.0f0) + @test_throws err @eval ccall("llvm.floor.f64", llvmcall, Float32, (Float64,), 0.0) + @test_throws err @eval ccall("llvm.floor.f64", llvmcall, Float32, (Float32,), 0.0f0) + @test_throws err @eval ccall("llvm.floor.f64", llvmcall, Float64, (Float32,), 0.0f0) + @test_throws err @eval ccall("llvm.floor.f64", llvmcall, Float64, (Int,), 0) + @test_throws err @eval ccall("llvm.floor.f64", llvmcall, Int, (Int,), 0) + @test_throws err @eval ccall("llvm.floor", llvmcall, Float64, (Float32,), 0.0f0) + @test_throws err @eval ccall("llvm.floor", llvmcall, Float64, (Int,), 0) + @test_throws err @eval ccall("llvm.floor", llvmcall, Int, (Int,), 0) + + @test_throws err (@eval ccall("llvm.floor.f64", llvmcall, Float64, (Float64, Float64...,), 0.0)) === 0.0 + @test_throws err (@eval ccall("llvm.floor", llvmcall, Float64, (Float64, Float64...,), 0.0)) === 0.0 end