Skip to content

Commit 834dc26

Browse files
vtjnashvchuravy
authored andcommitted
enable re-using external code in pkgimages (#48723)
* enable using external code in pkgimages This was unintentionally disabled (and incomplete) in the original PR for pkgimages. Co-authored-by: Valentin Churavy <[email protected]>
1 parent 9b73701 commit 834dc26

File tree

6 files changed

+80
-74
lines changed

6 files changed

+80
-74
lines changed

base/linking.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ function link_image_cmd(path, out)
162162
`$(ld()) $V $SHARED -o $out $WHOLE_ARCHIVE $path $NO_WHOLE_ARCHIVE $LIBDIR $PRIVATE_LIBDIR $SHLIBDIR $LIBS`
163163
end
164164

165-
function link_image(path, out, internal_stderr::IO = stderr, internal_stdout::IO = stdout)
166-
run(link_image_cmd(path, out), Base.DevNull(), stderr, stdout)
165+
function link_image(path, out, internal_stderr::IO=stderr, internal_stdout::IO=stdout)
166+
run(link_image_cmd(path, out), Base.DevNull(), internal_stderr, internal_stdout)
167167
end
168168

169169
end # module Linking

src/aotcompile.cpp

+3-21
Original file line numberDiff line numberDiff line change
@@ -369,34 +369,16 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm
369369
size_t offset = gvars.size();
370370
data->jl_external_to_llvm.resize(params.external_fns.size());
371371

372-
auto tbaa_const = tbaa_make_child_with_context(*ctxt.getContext(), "jtbaa_const", nullptr, true).first;
373372
for (auto &extern_fn : params.external_fns) {
374373
jl_code_instance_t *this_code = std::get<0>(extern_fn.first);
375374
bool specsig = std::get<1>(extern_fn.first);
376375
assert(specsig && "Error external_fns doesn't handle non-specsig yet");
377-
(void)specsig;
378-
Function *F = extern_fn.second;
379-
Module *M = F->getParent();
380-
381-
Type *T_funcp = F->getFunctionType()->getPointerTo();
382-
// Can't create a GC with type FunctionType. Alias also doesn't work
383-
GlobalVariable *GV = new GlobalVariable(*M, T_funcp, false,
384-
GlobalVariable::ExternalLinkage,
385-
Constant::getNullValue(T_funcp),
386-
F->getName());
387-
388-
389-
// Need to insert load instruction, thus we can't use replace all uses with
390-
replaceUsesWithLoad(*F, [GV](Instruction &) { return GV; }, tbaa_const);
391-
392-
assert(F->getNumUses() == 0); // declaration counts as use
393-
GV->takeName(F);
394-
F->eraseFromParent();
395-
376+
(void) specsig;
377+
GlobalVariable *F = extern_fn.second;
396378
size_t idx = gvars.size() - offset;
397379
assert(idx >= 0);
398380
data->jl_external_to_llvm.at(idx) = this_code;
399-
gvars.push_back(std::string(GV->getName()));
381+
gvars.push_back(std::string(F->getName()));
400382
}
401383

402384
// clones the contents of the module `m` to the shadow_output collector

src/codegen.cpp

+66-45
Original file line numberDiff line numberDiff line change
@@ -1542,7 +1542,7 @@ class jl_codectx_t {
15421542
jl_codegen_params_t &emission_context;
15431543
llvm::MapVector<jl_code_instance_t*, jl_codegen_call_target_t> call_targets;
15441544
std::map<void*, GlobalVariable*> &global_targets;
1545-
std::map<std::tuple<jl_code_instance_t*, bool>, Function*> &external_calls;
1545+
std::map<std::tuple<jl_code_instance_t*, bool>, GlobalVariable*> &external_calls;
15461546
Function *f = NULL;
15471547
// local var info. globals are not in here.
15481548
std::vector<jl_varinfo_t> slots;
@@ -1704,7 +1704,7 @@ static Value *get_current_task(jl_codectx_t &ctx);
17041704
static Value *get_current_ptls(jl_codectx_t &ctx);
17051705
static Value *get_last_age_field(jl_codectx_t &ctx);
17061706
static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block = true);
1707-
static CallInst *emit_jlcall(jl_codectx_t &ctx, Function *theFptr, Value *theF,
1707+
static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *theF,
17081708
const jl_cgval_t *args, size_t nargs, JuliaFunction *trampoline);
17091709
static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *theF,
17101710
const jl_cgval_t *args, size_t nargs, JuliaFunction *trampoline);
@@ -4039,14 +4039,14 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
40394039
}
40404040

40414041
// Returns ctx.types().T_prjlvalue
4042-
static CallInst *emit_jlcall(jl_codectx_t &ctx, Function *theFptr, Value *theF,
4042+
static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *theF,
40434043
const jl_cgval_t *argv, size_t nargs, JuliaFunction *trampoline)
40444044
{
40454045
++EmittedJLCalls;
40464046
Function *TheTrampoline = prepare_call(trampoline);
40474047
// emit arguments
40484048
SmallVector<Value*, 4> theArgs;
4049-
theArgs.push_back(theFptr);
4049+
theArgs.push_back(theFptr.getCallee());
40504050
if (theF)
40514051
theArgs.push_back(theF);
40524052
for (size_t i = 0; i < nargs; i++) {
@@ -4067,7 +4067,7 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *t
40674067
}
40684068

40694069

4070-
static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject,
4070+
static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal,
40714071
const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty)
40724072
{
40734073
++EmittedSpecfunCalls;
@@ -4143,7 +4143,22 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_
41434143
idx++;
41444144
}
41454145
assert(idx == nfargs);
4146-
CallInst *call = ctx.builder.CreateCall(returninfo.decl, ArrayRef<Value*>(&argvals[0], nfargs));
4146+
Value *callee = returninfo.decl;
4147+
if (fromexternal) {
4148+
std::string namep("p");
4149+
namep += returninfo.decl->getName();
4150+
GlobalVariable *GV = cast_or_null<GlobalVariable>(jl_Module->getNamedValue(namep));
4151+
if (GV == nullptr) {
4152+
GV = new GlobalVariable(*jl_Module, callee->getType(), false,
4153+
GlobalVariable::ExternalLinkage,
4154+
Constant::getNullValue(callee->getType()),
4155+
namep);
4156+
ctx.external_calls[std::make_tuple(fromexternal, true)] = GV;
4157+
}
4158+
jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const);
4159+
callee = ai.decorateInst(ctx.builder.CreateAlignedLoad(callee->getType(), GV, Align(sizeof(void*))));
4160+
}
4161+
CallInst *call = ctx.builder.CreateCall(cft, callee, ArrayRef<Value*>(&argvals[0], nfargs));
41474162
call->setAttributes(returninfo.decl->getAttributes());
41484163

41494164
jl_cgval_t retval;
@@ -4182,13 +4197,30 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_
41824197
return update_julia_type(ctx, retval, inferred_retty);
41834198
}
41844199

4185-
static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty, StringRef specFunctionObject,
4200+
static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal,
41864201
const jl_cgval_t *argv, size_t nargs, jl_value_t *inferred_retty)
41874202
{
4188-
auto theFptr = cast<Function>(
4189-
jl_Module->getOrInsertFunction(specFunctionObject, ctx.types().T_jlfunc).getCallee());
4190-
addRetAttr(theFptr, Attribute::NonNull);
4191-
Value *ret = emit_jlcall(ctx, theFptr, nullptr, argv, nargs, julia_call);
4203+
Value *theFptr;
4204+
if (fromexternal) {
4205+
std::string namep("p");
4206+
namep += specFunctionObject;
4207+
GlobalVariable *GV = cast_or_null<GlobalVariable>(jl_Module->getNamedValue(namep));
4208+
Type *pfunc = ctx.types().T_jlfunc->getPointerTo();
4209+
if (GV == nullptr) {
4210+
GV = new GlobalVariable(*jl_Module, pfunc, false,
4211+
GlobalVariable::ExternalLinkage,
4212+
Constant::getNullValue(pfunc),
4213+
namep);
4214+
ctx.external_calls[std::make_tuple(fromexternal, false)] = GV;
4215+
}
4216+
jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const);
4217+
theFptr = ai.decorateInst(ctx.builder.CreateAlignedLoad(pfunc, GV, Align(sizeof(void*))));
4218+
}
4219+
else {
4220+
theFptr = jl_Module->getOrInsertFunction(specFunctionObject, ctx.types().T_jlfunc).getCallee();
4221+
addRetAttr(cast<Function>(theFptr), Attribute::NonNull);
4222+
}
4223+
Value *ret = emit_jlcall(ctx, FunctionCallee(ctx.types().T_jlfunc, theFptr), nullptr, argv, nargs, julia_call);
41924224
return update_julia_type(ctx, mark_julia_type(ctx, ret, true, jlretty), inferred_retty);
41934225
}
41944226

@@ -4223,12 +4255,12 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const
42234255
FunctionType *ft = ctx.f->getFunctionType();
42244256
StringRef protoname = ctx.f->getName();
42254257
if (ft == ctx.types().T_jlfunc) {
4226-
result = emit_call_specfun_boxed(ctx, ctx.rettype, protoname, argv, nargs, rt);
4258+
result = emit_call_specfun_boxed(ctx, ctx.rettype, protoname, nullptr, argv, nargs, rt);
42274259
handled = true;
42284260
}
42294261
else if (ft != ctx.types().T_jlfuncparams) {
42304262
unsigned return_roots = 0;
4231-
result = emit_call_specfun_other(ctx, mi, ctx.rettype, protoname, argv, nargs, &cc, &return_roots, rt);
4263+
result = emit_call_specfun_other(ctx, mi, ctx.rettype, protoname, nullptr, argv, nargs, &cc, &return_roots, rt);
42324264
handled = true;
42334265
}
42344266
}
@@ -4248,16 +4280,17 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const
42484280
std::string name;
42494281
StringRef protoname;
42504282
bool need_to_emit = true;
4251-
bool cache_valid = ctx.use_cache;
4283+
bool cache_valid = ctx.use_cache || ctx.external_linkage;
42524284
bool external = false;
4253-
if (ctx.external_linkage) {
4254-
if (0 && jl_object_in_image((jl_value_t*)codeinst)) {
4255-
// Target is present in another pkgimage
4256-
cache_valid = true;
4257-
external = true;
4258-
}
4285+
4286+
// Check if we already queued this up
4287+
auto it = ctx.call_targets.find(codeinst);
4288+
if (need_to_emit && it != ctx.call_targets.end()) {
4289+
protoname = std::get<2>(it->second)->getName();
4290+
need_to_emit = cache_valid = false;
42594291
}
42604292

4293+
// Check if it is already compiled (either JIT or externally)
42614294
if (cache_valid) {
42624295
// optimization: emit the correct name immediately, if we know it
42634296
// TODO: use `emitted` map here too to try to consolidate names?
@@ -4270,32 +4303,30 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const
42704303
invoke = jl_atomic_load_relaxed(&codeinst->invoke);
42714304
if (specsig ? jl_atomic_load_relaxed(&codeinst->specsigflags) & 0b1 : invoke == jl_fptr_args_addr) {
42724305
protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst);
4273-
need_to_emit = false;
4306+
if (ctx.external_linkage) {
4307+
// TODO: Add !specsig support to aotcompile.cpp
4308+
// Check that the codeinst is containing native code
4309+
if (specsig && jl_atomic_load_relaxed(&codeinst->specsigflags) & 0b100) {
4310+
external = true;
4311+
need_to_emit = false;
4312+
}
4313+
}
4314+
else { // ctx.use_cache
4315+
need_to_emit = false;
4316+
}
42744317
}
42754318
}
42764319
}
4277-
auto it = ctx.call_targets.find(codeinst);
4278-
if (need_to_emit && it != ctx.call_targets.end()) {
4279-
protoname = std::get<2>(it->second)->getName();
4280-
need_to_emit = false;
4281-
}
42824320
if (need_to_emit) {
42834321
raw_string_ostream(name) << (specsig ? "j_" : "j1_") << name_from_method_instance(mi) << "_" << jl_atomic_fetch_add(&globalUniqueGeneratedNames, 1);
42844322
protoname = StringRef(name);
42854323
}
42864324
jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed;
42874325
unsigned return_roots = 0;
42884326
if (specsig)
4289-
result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, argv, nargs, &cc, &return_roots, rt);
4327+
result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt);
42904328
else
4291-
result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, argv, nargs, rt);
4292-
if (external) {
4293-
assert(!need_to_emit);
4294-
auto calledF = jl_Module->getFunction(protoname);
4295-
assert(calledF);
4296-
// TODO: Check if already present?
4297-
ctx.external_calls[std::make_tuple(codeinst, specsig)] = calledF;
4298-
}
4329+
result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, rt);
42994330
handled = true;
43004331
if (need_to_emit) {
43014332
Function *trampoline_decl = cast<Function>(jl_Module->getNamedValue(protoname));
@@ -5617,14 +5648,7 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Module *M, jl_cod
56175648
Function *theFunc;
56185649
Value *theFarg;
56195650
auto invoke = jl_atomic_load_relaxed(&codeinst->invoke);
5620-
56215651
bool cache_valid = params.cache;
5622-
if (params.external_linkage) {
5623-
if (0 && jl_object_in_image((jl_value_t*)codeinst)) {
5624-
// Target is present in another pkgimage
5625-
cache_valid = true;
5626-
}
5627-
}
56285652

56295653
if (cache_valid && invoke != NULL) {
56305654
StringRef theFptrName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)invoke, codeinst);
@@ -8537,9 +8561,6 @@ void jl_compile_workqueue(
85378561
bool preal_specsig = false;
85388562
auto invoke = jl_atomic_load_acquire(&codeinst->invoke);
85398563
bool cache_valid = params.cache;
8540-
if (params.external_linkage) {
8541-
cache_valid = 0 && jl_object_in_image((jl_value_t*)codeinst);
8542-
}
85438564
// WARNING: isspecsig is protected by the codegen-lock. If that lock is removed, then the isspecsig load needs to be properly atomically sequenced with this.
85448565
if (cache_valid && invoke != NULL) {
85458566
auto fptr = jl_atomic_load_relaxed(&codeinst->specptr.fptr);

src/jitlayers.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ typedef struct _jl_codegen_params_t {
190190
// outputs
191191
std::vector<std::pair<jl_code_instance_t*, jl_codegen_call_target_t>> workqueue;
192192
std::map<void*, GlobalVariable*> globals;
193-
std::map<std::tuple<jl_code_instance_t*,bool>, Function*> external_fns;
193+
std::map<std::tuple<jl_code_instance_t*,bool>, GlobalVariable*> external_fns;
194194
std::map<jl_datatype_t*, DIType*> ditypes;
195195
std::map<jl_datatype_t*, Type*> llvmtypes;
196196
DenseMap<Constant*, GlobalVariable*> mergedConstants;

src/julia.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,9 @@ typedef struct _jl_code_instance_t {
424424
jl_value_t *argescapes; // escape information of call arguments
425425

426426
// compilation state cache
427-
_Atomic(uint8_t) specsigflags; // & 0b1 == specptr is a specialized function signature for specTypes->rettype, &0b10 == invokeptr matches specptr
427+
_Atomic(uint8_t) specsigflags; // & 0b001 == specptr is a specialized function signature for specTypes->rettype
428+
// & 0b010 == invokeptr matches specptr
429+
// & 0b100 == From image
428430
_Atomic(uint8_t) precompile; // if set, this will be added to the output system image
429431
uint8_t relocatability; // nonzero if all roots are built into sysimg or tagged by module key
430432
_Atomic(jl_callptr_t) invoke; // jlcall entry point

src/staticdata.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ static void record_external_fns(jl_serializer_state *s, arraylist_t *external_fn
10771077
#ifndef JL_NDEBUG
10781078
for (size_t i = 0; i < external_fns->len; i++) {
10791079
jl_code_instance_t *ci = (jl_code_instance_t*)external_fns->items[i];
1080-
assert(jl_object_in_image((jl_value_t*)ci));
1080+
assert(jl_atomic_load_relaxed(&ci->specsigflags) & 0b100);
10811081
}
10821082
#endif
10831083
}
@@ -1889,7 +1889,7 @@ static void jl_update_all_fptrs(jl_serializer_state *s, jl_image_t *image)
18891889
void *fptr = (void*)(base + offset);
18901890
if (specfunc) {
18911891
codeinst->specptr.fptr = fptr;
1892-
codeinst->specsigflags = 0b11; // TODO: set only if confirmed to be true
1892+
codeinst->specsigflags = 0b111; // TODO: set only if confirmed to be true
18931893
}
18941894
else {
18951895
codeinst->invoke = (jl_callptr_t)fptr;
@@ -1913,11 +1913,12 @@ static uint32_t write_gvars(jl_serializer_state *s, arraylist_t *globals, arrayl
19131913
}
19141914
for (size_t i = 0; i < external_fns->len; i++) {
19151915
jl_code_instance_t *ci = (jl_code_instance_t*)external_fns->items[i];
1916+
assert(ci && (jl_atomic_load_relaxed(&ci->specsigflags) & 0b001));
19161917
uintptr_t item = backref_id(s, (void*)ci, s->link_ids_external_fnvars);
19171918
uintptr_t reloc = get_reloc_for_item(item, 0);
19181919
write_reloc_t(s->gvar_record, reloc);
19191920
}
1920-
return globals->len + 1;
1921+
return globals->len;
19211922
}
19221923

19231924
// Pointer relocation for native-code referenced global variables
@@ -1962,7 +1963,7 @@ static void jl_root_new_gvars(jl_serializer_state *s, jl_image_t *image, uint32_
19621963
v = (uintptr_t)jl_as_global_root((jl_value_t*)v);
19631964
} else {
19641965
jl_code_instance_t *codeinst = (jl_code_instance_t*) v;
1965-
assert(codeinst && (codeinst->specsigflags & 0b01));
1966+
assert(codeinst && (codeinst->specsigflags & 0b01) && codeinst->specptr.fptr);
19661967
v = (uintptr_t)codeinst->specptr.fptr;
19671968
}
19681969
*gv = v;

0 commit comments

Comments
 (0)