Skip to content

Commit 52cb36e

Browse files
committed
Fix ZST handling in ccall.cpp
1 parent 7608cb0 commit 52cb36e

File tree

2 files changed

+57
-23
lines changed

2 files changed

+57
-23
lines changed

src/ccall.cpp

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,8 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
919919
jl_svec_t *tt = ((jl_datatype_t *)at)->parameters;
920920
size_t nargt = jl_svec_len(tt);
921921
SmallVector<llvm::Type*, 0> argtypes;
922-
SmallVector<Value *, 8> argvals(nargt);
922+
SmallVector<Value *, 8> argvals; //do not presize argvals, we will push_back only non-void arguments
923+
argvals.reserve(nargt);
923924
for (size_t i = 0; i < nargt; ++i) {
924925
jl_value_t *tti = jl_svecref(tt,i);
925926
bool toboxed;
@@ -933,9 +934,16 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
933934
jl_value_t *argi = args[4 + i];
934935
jl_cgval_t arg = emit_expr(ctx, argi);
935936

936-
Value *v = julia_to_native(ctx, t, toboxed, tti, NULL, arg, false, i);
937-
bool issigned = jl_signed_type && jl_subtype(tti, (jl_value_t*)jl_signed_type);
938-
argvals[i] = llvm_type_rewrite(ctx, v, t, issigned);
937+
Value *v = julia_to_native(ctx, t, toboxed, tti, NULL, arg, false, i); //1. If Void -> SKIP (do not push to argvals) 2. If empty struct -> push UndefValue 3. Normal case -> push value
938+
if (t->isVoidTy()) {
939+
continue;
940+
}
941+
if (v == NULL){
942+
argvals.push_back(UndefValue::get(t));
943+
} else{
944+
bool issigned = jl_signed_type && jl_subtype(tti, (jl_value_t*)jl_signed_type);
945+
argvals.push_back(llvm_type_rewrite(ctx, v, t, issigned));
946+
}
939947
}
940948

941949
// Determine return type
@@ -963,11 +971,11 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
963971
// stringify arguments
964972
std::string arguments;
965973
raw_string_ostream argstream(arguments);
974+
int printed_args = 0;
966975
for (SmallVector<Type *, 0>::iterator it = argtypes.begin(); it != argtypes.end(); ++it) {
967-
if (it != argtypes.begin())
968-
argstream << ",";
976+
if ((*it)-> isVoidTy()) continue;
977+
if (printed_args++ > 0) argstream << ", ";
969978
(*it)->print(argstream);
970-
argstream << " ";
971979
}
972980

973981
// stringify return type
@@ -991,17 +999,13 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
991999
if (!Mod) {
9921000
std::string compat_arguments;
9931001
raw_string_ostream compat_argstream(compat_arguments);
1002+
int printed_args = 0;
9941003
for (size_t i = 0; i < nargt; ++i) {
995-
if (i > 0)
996-
compat_argstream << ",";
9971004
jl_value_t *tti = jl_svecref(tt, i);
998-
Type *t;
999-
if (jl_is_cpointer_type(tti))
1000-
t = ctx.types().T_size;
1001-
else
1002-
t = argtypes[i];
1005+
Type *t = (jl_is_cpointer_type(tti)) ? ctx.types().T_size : argtypes[i];
1006+
if (t->isVoidTy()) continue;
1007+
if (printed_args++ > 0) compat_argstream << ", ";
10031008
t->print(compat_argstream);
1004-
compat_argstream << " ";
10051009
}
10061010

10071011
std::string compat_rstring;
@@ -1084,13 +1088,18 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
10841088

10851089
// backwards compatibility: support for IR with integer pointers
10861090
bool mismatched_pointers = false;
1091+
int arg_idx = 0;
10871092
for (size_t i = 0; i < nargt; ++i) {
10881093
jl_value_t *tti = jl_svecref(tt, i);
1094+
Type *t = argtypes[i];
1095+
if (t->isVoidTy())
1096+
continue;
10891097
if (jl_is_cpointer_type(tti) &&
1090-
!f->getFunctionType()->getParamType(i)->isPointerTy()) {
1098+
!f->getFunctionType()->getParamType(arg_idx)->isPointerTy()) {
10911099
mismatched_pointers = true;
10921100
break;
10931101
}
1102+
arg_idx++;
10941103
}
10951104
if (mismatched_pointers) {
10961105
if (jl_options.depwarn) {
@@ -1128,9 +1137,13 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
11281137
BasicBlock *entry = BasicBlock::Create(ctx.builder.getContext(), "", wrapper);
11291138
IRBuilder<> irbuilder(entry);
11301139
SmallVector<Value *, 0> wrapper_args;
1140+
arg_idx = 0;
11311141
for (size_t i = 0; i < nargt; ++i) {
11321142
jl_value_t *tti = jl_svecref(tt, i);
1133-
Value *v = wrapper->getArg(i);
1143+
Type *t = argtypes[i];
1144+
if (t->isVoidTy())
1145+
continue;
1146+
Value *v = wrapper->getArg(arg_idx++);
11341147
if (jl_is_cpointer_type(tti))
11351148
v = irbuilder.CreatePtrToInt(v, ctx.types().T_size);
11361149
wrapper_args.push_back(v);
@@ -1150,18 +1163,21 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
11501163

11511164
// verify the function type
11521165
assert(f->getReturnType() == rettype);
1153-
int i = 0;
1166+
arg_idx = 0;
11541167
for (SmallVector<Type *, 0>::iterator it = argtypes.begin(); it != argtypes.end();
1155-
++it, ++i) {
1156-
if (*it != f->getFunctionType()->getParamType(i)) {
1168+
++it) {
1169+
if ((*it)->isVoidTy())
1170+
continue;
1171+
if (*it != f->getFunctionType()->getParamType(arg_idx)) {
11571172
std::string message;
11581173
raw_string_ostream stream(message);
1159-
stream << "Malformed llvmcall: argument " << i + 1 << " type "
1160-
<< *f->getFunctionType()->getParamType(i)
1174+
stream << "Malformed llvmcall: argument " << arg_idx + 1 << " type "
1175+
<< *f->getFunctionType()->getParamType(arg_idx)
11611176
<< " does not match expected argument type " << **it;
11621177
emit_error(ctx, stream.str());
11631178
return jl_cgval_t();
11641179
}
1180+
arg_idx++;
11651181
}
11661182

11671183
// copy module properties that should always match
@@ -1183,7 +1199,12 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar
11831199
def->setLinkage(GlobalVariable::LinkOnceODRLinkage);
11841200

11851201
// generate a call
1186-
FunctionType *decl_typ = FunctionType::get(rettype, argtypes, def->isVarArg());
1202+
SmallVector<Type *, 0> filtered_argtypes;
1203+
for (auto t : argtypes) {
1204+
if (!t->isVoidTy())
1205+
filtered_argtypes.push_back(t);
1206+
}
1207+
FunctionType *decl_typ = FunctionType::get(rettype, filtered_argtypes, def->isVarArg());
11871208
Function *decl = Function::Create(decl_typ, def->getLinkage(), def->getAddressSpace(),
11881209
def->getName(), jl_Module);
11891210
decl->setAttributes(def->getAttributes());

test/llvmcall.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,16 @@ s = MyStruct()
218218
@test eltype(supertype(Core.LLVMPtr{UInt8,1})) <: UInt8
219219
@test s.kern == 0
220220
@test reinterpret(Int, s.ptr) == 0
221+
222+
# Issue #60063: llvmcall segfault/parser error with zero-sized argument (Nothing)
223+
module LLVMCallTest
224+
using Test
225+
let
226+
f(x::T) where T = Base.llvmcall("ret i8 %0", Int8, Tuple{T}, x)
227+
err = try f(nothing) catch e e end
228+
@test err isa ErrorException
229+
#verify the error is about the LLVM assembly failure and references the undefined symbol
230+
@test occursin("Failed to parse LLVM assembly", err.msg)
231+
@test occursin("%0", err.msg)
232+
end
233+
end #module LLVMCallTest

0 commit comments

Comments
 (0)