Repository: JuliaLang/julia
File: src/ccall.cpp (C++)

Code before:
            else
                llvmf = emit_plt(ctx, functype, attributes, cc, symarg.f_lib, symarg.f_name);
        }
        else {
            void *symaddr;
            void *libsym = jl_get_library_(symarg.f_lib, 0);
            int symbol_found = jl_dlsym(libsym, symarg.f_name, &symaddr, 0);
            if (!libsym || !symbol_found) {
                ++DeferredCCallLookups;
                // either the library or the symbol could not be found, place a runtime
                // lookup here instead.
                llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, ctx.f);
            } else {
                ++LiteralCCalls;
                // since we aren't saving this code, there's no sense in
                // putting anything complicated here: just JIT the function address
                llvmf = literal_static_pointer_val(symaddr, funcptype);
            }
        }
    }

    Value *last_gc_state = NULL;
    Value *ptls = get_current_ptls(ctx); // Do we need to reload this after the ccall?

    if (gc_safe)
        last_gc_state = emit_gc_safe_enter(ctx.builder, ctx.types().T_size, ptls, false);
    OperandBundleDef OpBundle("jl_roots", gc_uses);
    // the actual call
    CallInst *ret = ctx.builder.CreateCall(functype, llvmf,
            argvals,
            ArrayRef<OperandBundleDef>(&OpBundle, gc_uses.empty() ? 0 : 1));
    ((CallInst*)ret)->setAttributes(attributes);

    if (cc != CallingConv::C)
        ((CallInst*)ret)->setCallingConv(cc);
    if (!sret)
        result = ret; // no need to update sretty here because we know !sret
    if (0) { // Enable this to turn on SSPREQ (-fstack-protector) on the function containing this ccall
        ctx.f->addFnAttr(Attribute::StackProtectReq);
    }

    if (gc_safe)
        emit_gc_safe_leave(ctx.builder, ctx.types().T_size, ptls, last_gc_state, false);

    if (rt == jl_bottom_type) {
        CreateTrap(ctx.builder);
        return jl_cgval_t();
    }

    // Finally we need to box the result into julia type
    // However, if we have already created a box for the return

Review comment (suggestion):
Since the OpBundle mutates gc-unsafe state locally, I think we actually will need to insert these later (after the gc-late-lowering pass) or at least teach that pass to accurately detect gc-safe/unsafe region transitions and move those region boundaries to inside of the runtime-interop code.

Code after:
    if (llvmcall) {
        ++EmittedLLVMCalls;
        if (symarg.jl_ptr != NULL) {
            emit_error(ctx, "llvmcall doesn't support dynamic pointers");
            return jl_cgval_t();
        }
        else if (symarg.fptr != NULL) {
            emit_error(ctx, "llvmcall doesn't support static pointers");
            return jl_cgval_t();
        }
        else if (symarg.f_lib != NULL) {
            emit_error(ctx, "llvmcall doesn't support dynamic libraries");
            return jl_cgval_t();
        }
        else {
            assert(symarg.f_name != NULL);
            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<Function>(llvmf) || cast<Function>(llvmf)->isIntrinsic() || cast<Function>(llvmf)->getFunctionType() != functype)
                    llvmf = NULL;
            }
            else if (f_name.starts_with("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<Type*, 4> overloadTys;
                    SmallVector<Intrinsic::IITDescriptor, 8> Table;
                    getIntrinsicInfoTableEntries(ID, Table);
                    ArrayRef<Intrinsic::IITDescriptor> 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();
            }
        }