diff --git a/src/ccall.cpp b/src/ccall.cpp index 6ea7e2fa9b3b6..e1f93e5d553a2 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -813,18 +813,13 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar JL_TYPECHK(llvmcall, type, rt); JL_TYPECHK(llvmcall, type, at); - // Generate arguments - std::string arguments; - raw_string_ostream argstream(arguments); - jl_svec_t *tt = ((jl_datatype_t*)at)->parameters; - jl_value_t *rtt = rt; + // Determine argument types + // + // Semantics for arguments are as follows: + // If the argument type is immutable (including bitstype), we pass the loaded llvm value + // type. Otherwise we pass a pointer to a jl_value_t. + jl_svec_t *tt = ((jl_datatype_t *)at)->parameters; size_t nargt = jl_svec_len(tt); - - /* - * Semantics for arguments are as follows: - * If the argument type is immutable (including bitstype), we pass the loaded llvm value - * type. Otherwise we pass a pointer to a jl_value_t. - */ SmallVector argtypes; SmallVector argvals(nargt); for (size_t i = 0; i < nargt; ++i) { @@ -845,45 +840,87 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar argvals[i] = llvm_type_rewrite(ctx, v, t, issigned); } + // Determine return type + jl_value_t *rtt = rt; bool retboxed; Type *rettype = julia_type_to_llvm(ctx, rtt, &retboxed); // Make sure to find a unique name std::string ir_name; while (true) { - raw_string_ostream(ir_name) << (ctx.f->getName().str()) << "u" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); + raw_string_ostream(ir_name) + << (ctx.f->getName().str()) << "u" + << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); if (jl_Module->getFunction(ir_name) == NULL) break; } // generate a temporary module that contains our IR std::unique_ptr Mod; + Function *f; if (entry == NULL) { // we only have function IR, which we should put in a function - bool first = true; + // stringify arguments + std::string arguments; + raw_string_ostream argstream(arguments); for (SmallVector::iterator it = argtypes.begin(); it != argtypes.end(); ++it) { - if (!first) + if (it != argtypes.begin()) argstream << ","; - else - first = false; (*it)->print(argstream); argstream << " "; } + // stringify return type std::string rstring; raw_string_ostream rtypename(rstring); rettype->print(rtypename); - std::map localDecls; + // generate IR function definition std::string ir_string; raw_string_ostream ir_stream(ir_string); - ir_stream << "; Number of arguments: " << nargt << "\n" - << "define "< 0) + compat_argstream << ","; + jl_value_t *tti = jl_svecref(tt, i); + Type *t; + if (jl_is_cpointer_type(tti)) + t = ctx.types().T_size; + else + t = argtypes[i]; + t->print(compat_argstream); + compat_argstream << " "; + } + + std::string compat_rstring; + raw_string_ostream compat_rtypename(compat_rstring); + if (jl_is_cpointer_type(rtt)) + ctx.types().T_size->print(compat_rtypename); + else + rettype->print(compat_rtypename); + + std::string compat_ir_string; + raw_string_ostream compat_ir_stream(compat_ir_string); + compat_ir_stream << "define " << compat_rtypename.str() << " @\"" << ir_name + << "\"(" << compat_argstream.str() << ") {\n" + << jl_string_data(ir) << "\n}"; + + SMDiagnostic Err = SMDiagnostic(); + Mod = + parseAssemblyString(compat_ir_stream.str(), Err, ctx.builder.getContext()); + } + if (!Mod) { std::string message = "Failed to parse LLVM assembly: \n"; raw_string_ostream stream(message); @@ -893,7 +930,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar return jl_cgval_t(); } - Function *f = Mod->getFunction(ir_name); + f = Mod->getFunction(ir_name); f->addFnAttr(Attribute::AlwaysInline); } else { @@ -931,21 +968,88 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar Mod = std::move(ModuleOrErr.get()); } - Function *f = Mod->getFunction(jl_string_data(entry)); + f = Mod->getFunction(jl_string_data(entry)); if (!f) { emit_error(ctx, "Module IR does not contain specified entry function"); JL_GC_POP(); return jl_cgval_t(); } + assert(!f->isDeclaration()); f->setName(ir_name); + } - // verify the function type - assert(!f->isDeclaration()); - assert(f->getReturnType() == rettype); - int i = 0; - for (SmallVector::iterator it = argtypes.begin(); - it != argtypes.end(); ++it, ++i) - assert(*it == f->getFunctionType()->getParamType(i)); + // backwards compatibility: support for IR with integer pointers + bool mismatched_pointers = false; + for (size_t i = 0; i < nargt; ++i) { + jl_value_t *tti = jl_svecref(tt, i); + if (jl_is_cpointer_type(tti) && + !f->getFunctionType()->getParamType(i)->isPointerTy()) { + mismatched_pointers = true; + break; + } + } + if (mismatched_pointers) { + if (jl_options.depwarn) { + if (jl_options.depwarn == JL_OPTIONS_DEPWARN_ERROR) + jl_error("llvmcall with integer pointers is deprecated, " + "use an actual pointer type instead."); + jl_printf(JL_STDERR, + "WARNING: llvmcall with integer pointers is deprecated.\n" + "Use actual pointers instead, replacing i32 or i64 with i8* or ptr"); + if (jl_lineno != 0) + jl_printf(JL_STDERR, ", likely near %s:%d", jl_filename, jl_lineno); + jl_printf(JL_STDERR, "\n"); + } + + // wrap the function, performing the necesary pointer conversion + + Function *inner = f; + inner->setName(ir_name + ".inner"); + + FunctionType *wrapper_ft = FunctionType::get(rettype, argtypes, false); + Function *wrapper = + Function::Create(wrapper_ft, inner->getLinkage(), ir_name, *Mod); + + wrapper->copyAttributesFrom(inner); + inner->addFnAttr(Attribute::AlwaysInline); + + BasicBlock *entry = BasicBlock::Create(ctx.builder.getContext(), "", wrapper); + IRBuilder<> irbuilder(entry); + SmallVector wrapper_args; + for (size_t i = 0; i < nargt; ++i) { + jl_value_t *tti = jl_svecref(tt, i); + Value *v = wrapper->getArg(i); + if (jl_is_cpointer_type(tti)) + v = irbuilder.CreatePtrToInt(v, ctx.types().T_size); + wrapper_args.push_back(v); + } + Value *call = irbuilder.CreateCall(inner, wrapper_args); + // check if void + if (rettype->isVoidTy()) + irbuilder.CreateRetVoid(); + else { + if (jl_is_cpointer_type(rtt)) + call = irbuilder.CreateIntToPtr(call, ctx.types().T_ptr); + irbuilder.CreateRet(call); + } + + f = wrapper; + } + + // verify the function type + assert(f->getReturnType() == rettype); + int i = 0; + for (SmallVector::iterator it = argtypes.begin(); it != argtypes.end(); + ++it, ++i) { + if (*it != f->getFunctionType()->getParamType(i)) { + std::string message; + raw_string_ostream stream(message); + stream << "Malformed llvmcall: argument " << i + 1 << " type " + << *f->getFunctionType()->getParamType(i) + << " does not match expected argument type " << **it; + emit_error(ctx, stream.str()); + return jl_cgval_t(); + } } // copy module properties that should always match @@ -983,7 +1087,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar if (inst->getType() != rettype) { std::string message; raw_string_ostream stream(message); - stream << "llvmcall return type " << *inst->getType() + stream << "Malformed llvmcall: return type " << *inst->getType() << " does not match declared return type" << *rettype; emit_error(ctx, stream.str()); return jl_cgval_t();