diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index 58ad3714e1b27..e46a99de90d04 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -1,8 +1,6 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license // Assumptions for pinning: -// * args need to be pinned -// * JL_ROOTING_ARGUMENT and JL_ROOTED_ARGUMENT will propagate pinning state as well. // * The checker may not consider alias for derived pointers in some cases. // * if f(x) returns a derived pointer from x, a = f(x); b = f(x); PTR_PIN(a); The checker will NOT find b as pinned. // * a = x->y; b = x->y; PTR_PIN(a); The checker will find b as pinned. @@ -106,8 +104,14 @@ class GCChecker : (P == Moved) ? "Moved" : "Error"); llvm::dbgs() << ","; - if (S == Rooted) - llvm::dbgs() << "(" << RootDepth << ")"; + if (S == Rooted) { + llvm::dbgs() << "Root("; + if (Root) { + Root->dump(); + llvm::dbgs() << ","; + } + llvm::dbgs() << RootDepth << ")"; + } } bool operator==(const ValueState &VS) const { @@ -169,6 +173,9 @@ class GCChecker } else if (parent.isPinned()) { // If parent is pinned, the child is not pinned. return getNotPinned(parent); + } else if (parent.isMoved()) { + // If parent is moved, the child is not pinned. + return getNotPinned(parent); } else { // For other cases, the children have the same state as the parent. return parent; @@ -194,19 +201,20 @@ class GCChecker const ParmVarDecl *PVD) { bool isFunctionSafepoint = !isFDAnnotatedNotSafepoint(FD); bool maybeUnrooted = declHasAnnotation(PVD, "julia_maybe_unrooted"); - bool maybeUnpinned = declHasAnnotation(PVD, "julia_maybe_unpinned"); - if (!isFunctionSafepoint || maybeUnrooted || maybeUnpinned) { + if (!isFunctionSafepoint || maybeUnrooted) { ValueState VS = getAllocated(); VS.PVD = PVD; VS.FD = FD; return VS; } bool require_tpin = declHasAnnotation(PVD, "julia_require_tpin"); + bool require_pin = declHasAnnotation(PVD, "julia_require_pin"); if (require_tpin) { return getRooted(nullptr, ValueState::TransitivelyPinned, -1); - } else { - // Assume arguments are pinned + } else if (require_pin) { return getRooted(nullptr, ValueState::Pinned, -1); + } else { + return getRooted(nullptr, ValueState::NotPinned, -1); } } }; @@ -339,6 +347,7 @@ class GCChecker void validateValue(const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message) const; void validateValueRootnessOnly(const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message) const; void validateValue(const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message, SourceRange range) const; + void validateValueRootnessOnly(const GCChecker::ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message, SourceRange range) const; int validateValueInner(const GCChecker::ValueState* VS) const; GCChecker::ValueState getRootedFromRegion(const MemRegion *Region, const PinState *PS, int Depth) const; template @@ -475,6 +484,15 @@ static const VarRegion *walk_back_to_global_VR(const MemRegion *Region) { #define FREED 1 #define MOVED 2 +void GCChecker::validateValueRootnessOnly(const ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message, SourceRange range) const { + int v = validateValueInner(VS); + if (v == FREED) { + GCChecker::report_value_error(C, Sym, (std::string(message) + " GCed").c_str(), range); + } else if (v == MOVED) { + // We don't care if it is moved + } +} + void GCChecker::validateValue(const ValueState* VS, CheckerContext &C, SymbolRef Sym, const char *message, SourceRange range) const { int v = validateValueInner(VS); if (v == FREED) { @@ -1133,10 +1151,10 @@ bool GCChecker::processPotentialSafepoint(const CallEvent &Call, // Symbolically move all unpinned values. GCValueMapTy AMap2 = State->get(); for (auto I = AMap2.begin(), E = AMap2.end(); I != E; ++I) { + logWithDump("- check Sym", I.getKey()); if (RetSym == I.getKey()) continue; if (I.getData().isNotPinned()) { - logWithDump("- move unpinned values, Sym", I.getKey()); logWithDump("- move unpinned values, VS", I.getData()); auto NewVS = ValueState::getMoved(I.getData()); State = State->set(I.getKey(), NewVS); @@ -1189,9 +1207,9 @@ bool GCChecker::processArgumentRooting(const CallEvent &Call, CheckerContext &C, const ValueState *CurrentVState = State->get(RootedSymbol); ValueState NewVState = *OldVState; // If the old state is pinned, the new state is not pinned. - if (OldVState->isPinned() && ((CurrentVState && !CurrentVState->isPinnedByAnyway()) || !CurrentVState)) { - NewVState = ValueState::getNotPinned(*OldVState); - } + // if (OldVState->isPinned() && ((CurrentVState && !CurrentVState->isPinnedByAnyway()) || !CurrentVState)) { + // NewVState = ValueState::getNotPinned(*OldVState); + // } logWithDump("- Rooted set to", NewVState); State = State->set(RootedSymbol, NewVState); return true; @@ -1627,23 +1645,16 @@ void GCChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { range); } } - if (ValState->isNotPinned()) { - bool MaybeUnpinned = false; - if (FD) { - if (idx < FD->getNumParams()) { - MaybeUnpinned = - declHasAnnotation(FD->getParamDecl(idx), "julia_maybe_unpinned"); - } - } - if (!MaybeUnpinned && isCalleeSafepoint) { - report_value_error(C, Sym, "Passing non-pinned value as argument to function that may GC", range); - } - } if (FD && idx < FD->getNumParams() && declHasAnnotation(FD->getParamDecl(idx), "julia_require_tpin")) { if (!ValState->isTransitivelyPinned()) { report_value_error(C, Sym, "Passing non-tpinned argument to function that requires a tpin argument."); } } + if (FD && idx < FD->getNumParams() && declHasAnnotation(FD->getParamDecl(idx), "julia_require_pin")) { + if (!ValState->isPinnedByAnyway()) { + report_value_error(C, Sym, "Passing non-pinned argument to function that requires a pin argument."); + } + } } } @@ -1673,12 +1684,20 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { PoppedRoots.push_back(I.getKey()); State = State->remove(I.getKey()); State = State->remove(I.getKey()); + logWithDump("- pop root", I.getKey()); } } + log("- Iterate value map"); GCValueMapTy VMap = State->get(); for (const MemRegion *R : PoppedRoots) { + logWithDump("-- check popped root", R); for (auto I = VMap.begin(), E = VMap.end(); I != E; ++I) { + logWithDump("--- check value", I.getKey()); + logWithDump("--- check state", I.getData()); + // FIXME: If this is a pop for TPin frame, we should remove TPin as well. + // For any region that is reachable from R, its pinning state should be reset. if (I.getData().isRootedBy(R)) { + logWithDump("--- no longer rooted", ValueState::getAllocated()); State = State->set(I.getKey(), ValueState::getAllocated()); } @@ -1707,11 +1726,17 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { return true; } const MemRegion *Region = MRV->getRegion(); - State = State->set(Region, RootState::getRoot(CurrentDepth)); + RootState RS = RootState::getRoot(CurrentDepth); + State = State->set(Region, RS); + logWithDump("- JL_GC_PUSH, Region", Region); + logWithDump("- JL_GC_PUSH, RS", RS); + PinState PS = PinState::getNoPin(-1); if (tpin) - State = State->set(Region, PinState::getTransitivePin(CurrentDepth)); + PS = PinState::getTransitivePin(CurrentDepth); else - State = State->set(Region, PinState::getPin(CurrentDepth)); + PS = PinState::getPin(CurrentDepth); + State = State->set(Region, PS); + logWithDump("- JL_GC_PUSH, PS", PS); // Now for the value SVal Value = State->getSVal(Region); SymbolRef Sym = Value.getAsSymbol(); @@ -1730,6 +1755,8 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { else VS = ValueState::getPinned(VS); State = State->set(Sym, VS); + logWithDump("- JL_GC_PUSH, Sym", Sym); + logWithDump("- JL_GC_PUSH, VS", VS); } CurrentDepth += 1; State = State->set(CurrentDepth); @@ -1788,6 +1815,7 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { } const ValueState *OldVS = C.getState()->get(Sym); + logWithDump("- PTR_PIN OldVS", OldVS); if (OldVS && OldVS->isMoved()) { report_error(C, "Attempt to PIN a value that is already moved."); return true; diff --git a/src/julia.h b/src/julia.h index 444ef468b95a6..f2b2db91df5dd 100644 --- a/src/julia.h +++ b/src/julia.h @@ -915,7 +915,6 @@ extern void _JL_GC_PUSHARGS(jl_value_t **, size_t) JL_NOTSAFEPOINT; extern void JL_GC_POP() JL_NOTSAFEPOINT; -#ifdef MMTK_GC extern void JL_GC_PUSH1_NO_TPIN(void *) JL_NOTSAFEPOINT; extern void JL_GC_PUSH2_NO_TPIN(void *, void *) JL_NOTSAFEPOINT; extern void JL_GC_PUSH3_NO_TPIN(void *, void *, void *) JL_NOTSAFEPOINT; @@ -931,8 +930,6 @@ extern void _JL_GC_PUSHARGS_NO_TPIN(jl_value_t **, size_t) JL_NOTSAFEPOINT; memset(rts_var, 0, sizeof(void*) * (n)); \ _JL_GC_PUSHARGS_NO_TPIN(rts_var, (n)); -#endif - #else #define JL_GC_PUSH1(arg1) \ @@ -976,9 +973,6 @@ extern void _JL_GC_PUSHARGS_NO_TPIN(jl_value_t **, size_t) JL_NOTSAFEPOINT; #define JL_GC_POP() (jl_pgcstack = jl_pgcstack->prev) -#endif - -#ifdef MMTK_GC // these are pinning roots: only the root object needs to be pinned as opposed to // the functions above which are transitively pinning #define JL_GC_PUSH1_NO_TPIN(arg1) \ @@ -1019,25 +1013,7 @@ extern void _JL_GC_PUSHARGS_NO_TPIN(jl_value_t **, size_t) JL_NOTSAFEPOINT; ((void**)rts_var)[-1] = jl_pgcstack; \ memset((void*)rts_var, 0, (n)*sizeof(jl_value_t*)); \ jl_pgcstack = (jl_gcframe_t*)&(((void**)rts_var)[-2]) -#else -// When not using MMTk, default to the stock functions -#define JL_GC_PUSH1_NO_TPIN(arg1) JL_GC_PUSH1(arg1) - -#define JL_GC_PUSH2_NO_TPIN(arg1, arg2) JL_GC_PUSH2(arg1, arg2) - -#define JL_GC_PUSH3_NO_TPIN(arg1, arg2, arg3) JL_GC_PUSH3(arg1, arg2, arg3) - -#define JL_GC_PUSH4_NO_TPIN(arg1, arg2, arg3, arg4) JL_GC_PUSH4(arg1, arg2, arg3, arg4) - -#define JL_GC_PUSH5_NO_TPIN(arg1, arg2, arg3, arg4, arg5) JL_GC_PUSH5(arg1, arg2, arg3, arg4, arg5) - -#define JL_GC_PUSH6_NO_TPIN(arg1, arg2, arg3, arg4, arg5, arg6) JL_GC_PUSH6(arg1, arg2, arg3, arg4, arg5, arg6) - -#define JL_GC_PUSH7_NO_TPIN(arg1, arg2, arg3, arg4, arg5, arg6, arg7) JL_GC_PUSH7(arg1, arg2, arg3, arg4, arg5, arg6, arg7) - -#define JL_GC_PUSH8_NO_TPIN(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) JL_GC_PUSH8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) -#define JL_GC_PUSHARGS_NO_TPIN(rts_var,n) JL_GC_PUSHARGS(rts_var,n) #endif JL_DLLEXPORT int jl_gc_enable(int on); @@ -1871,12 +1847,12 @@ JL_DLLEXPORT void JL_NORETURN jl_exceptionf(jl_datatype_t *ty, JL_DLLEXPORT void JL_NORETURN jl_too_few_args(const char *fname, int min); JL_DLLEXPORT void JL_NORETURN jl_too_many_args(const char *fname, int max); JL_DLLEXPORT void JL_NORETURN jl_type_error(const char *fname, - jl_value_t *expected JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED, - jl_value_t *got JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED); + jl_value_t *expected JL_MAYBE_UNROOTED, + jl_value_t *got JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname, const char *context, - jl_value_t *ty JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED, - jl_value_t *got JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED); + jl_value_t *ty JL_MAYBE_UNROOTED, + jl_value_t *got JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var); JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_sym_t *type_name, jl_sym_t *var); JL_DLLEXPORT void JL_NORETURN jl_atomic_error(char *str); diff --git a/src/rtutils.c b/src/rtutils.c index 2f63dc6e271d4..66a0c9e2bdef1 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -109,8 +109,8 @@ JL_DLLEXPORT void JL_NORETURN jl_too_many_args(const char *fname, int max) // with function name / location description, plus extra context JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname, const char *context, - jl_value_t *expected JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED, - jl_value_t *got JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED) + jl_value_t *expected JL_MAYBE_UNROOTED, + jl_value_t *got JL_MAYBE_UNROOTED) { jl_value_t *ctxt=NULL; JL_GC_PUSH3(&ctxt, &expected, &got); @@ -121,8 +121,8 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname, const char *co // with function name or description only JL_DLLEXPORT void JL_NORETURN jl_type_error(const char *fname, - jl_value_t *expected JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED, - jl_value_t *got JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED) + jl_value_t *expected JL_MAYBE_UNROOTED, + jl_value_t *got JL_MAYBE_UNROOTED) { jl_type_error_rt(fname, "", expected, got); } diff --git a/src/support/analyzer_annotations.h b/src/support/analyzer_annotations.h index cbf7cc63fdd00..266851c555326 100644 --- a/src/support/analyzer_annotations.h +++ b/src/support/analyzer_annotations.h @@ -13,7 +13,6 @@ #define JL_PROPAGATES_ROOT __attribute__((annotate("julia_propagates_root"))) #define JL_NOTSAFEPOINT __attribute__((annotate("julia_not_safepoint"))) #define JL_MAYBE_UNROOTED __attribute__((annotate("julia_maybe_unrooted"))) -#define JL_MAYBE_UNPINNED __attribute__((annotate("julia_maybe_unpinned"))) #define JL_GLOBALLY_ROOTED __attribute__((annotate("julia_globally_rooted"))) #define JL_GLOBALLY_PINNED __attribute__((annotate("julia_globally_pinned"))) #define JL_GLOBALLY_TPINNED __attribute__((annotate("julia_globally_tpinned"))) @@ -24,6 +23,7 @@ #define JL_ROOTS_TEMPORARILY __attribute__((annotate("julia_temporarily_roots"))) #define JL_REQUIRE_ROOTED_SLOT __attribute__((annotate("julia_require_rooted_slot"))) #define JL_REQUIRE_TPIN __attribute__((annotate("julia_require_tpin"))) +#define JL_REQUIRE_PIN __attribute__((annotate("julia_require_pin"))) #ifdef __cplusplus extern "C" { #endif @@ -38,7 +38,6 @@ extern "C" { #define JL_PROPAGATES_ROOT #define JL_NOTSAFEPOINT #define JL_MAYBE_UNROOTED -#define JL_MAYBE_UNPINNED // The runtime may mark any object that is reachable from a global root as globally rooted. // So JL_GLOBALLY_ROOTED does not need to an actual root. Thus we don't know anything // about pining state. @@ -54,6 +53,7 @@ extern "C" { #define JL_ROOTS_TEMPORARILY #define JL_REQUIRE_ROOTED_SLOT #define JL_REQUIRE_TPIN +#define JL_REQUIRE_PIN #define JL_GC_PROMISE_ROOTED(x) (void)(x) #define jl_may_leak(x) (void)(x) diff --git a/test/clangsa/MissingPinning.c b/test/clangsa/MissingPinning.c index 2fa0a814a2b94..8e524c3e371d4 100644 --- a/test/clangsa/MissingPinning.c +++ b/test/clangsa/MissingPinning.c @@ -6,33 +6,41 @@ #include "julia_internal.h" extern void look_at_value(jl_value_t *v); -extern void process_unrooted(jl_value_t *maybe_unrooted JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED); +extern void process_unrooted(jl_value_t *maybe_unrooted JL_MAYBE_UNROOTED); void unpinned_argument() { - jl_svec_t *val = jl_svec1(NULL); // expected-note{{Started tracking value here}} - JL_GC_PROMISE_ROOTED(val); // expected-note{{Value was rooted here}} - look_at_value((jl_value_t*) val); // expected-warning{{Passing non-pinned value as argument to function that may GC}} - // expected-note@-1{{Passing non-pinned value as argument to function that may GC}} + jl_svec_t *val = jl_svec1(NULL); + JL_GC_PROMISE_ROOTED(val); + look_at_value((jl_value_t*) val); } -int allow_unpinned() { +int unrooted_argument() { jl_svec_t *val = jl_svec1(NULL); process_unrooted((jl_value_t*)val); } -void pinned_argument() { +extern void process_pinned(jl_value_t *pinned JL_REQUIRE_PIN); + +void non_pinned_argument_for_require_pin() { + jl_svec_t *val = jl_svec1(NULL); // expected-note{{Started tracking value here}} + JL_GC_PROMISE_ROOTED(val); // expected-note{{Value was rooted here}} + process_pinned(val); // expected-warning{{Passing non-pinned argument to function that requires a pin argument}} + // expected-note@-1{{Passing non-pinned argument to function that requires a pin argument}} +} + +void pinned_argument_for_require_pin() { jl_svec_t *val = jl_svec1(NULL); JL_GC_PROMISE_ROOTED(val); PTR_PIN(val); - look_at_value((jl_value_t*) val); + process_pinned(val); PTR_UNPIN(val); } -void missing_pin_before_safepoint() { +void cannot_use_moved_value_for_arg() { jl_svec_t *val = jl_svec1(NULL); // expected-note{{Started tracking value here}} JL_GC_PROMISE_ROOTED(val); // expected-note{{Value was rooted here}} - jl_gc_safepoint(); - look_at_value((jl_value_t*) val); // expected-warning{{Argument value may have been moved}} + jl_gc_safepoint(); // expected-note{{Value was moved here}} + look_at_value((jl_value_t*) val); // expected-warning{{Argument value may have been moved}} // <<< here -- the value is used, and it is moved -- it is wrong. // expected-note@-1{{Argument value may have been moved}} } @@ -54,43 +62,41 @@ void proper_pin_before_safepoint() { PTR_UNPIN(val); } +extern void process_tpinned(jl_value_t *tpinned JL_REQUIRE_TPIN); + void push_tpin_value() { jl_svec_t *val = jl_svec1(NULL); JL_GC_PUSH1(&val); jl_gc_safepoint(); - look_at_value((jl_value_t*) val); + process_tpinned((jl_value_t*) val); JL_GC_POP(); } void push_no_tpin_value() { - jl_svec_t *val = jl_svec1(NULL); - JL_GC_PUSH1_NO_TPIN(&val); + jl_svec_t *val = jl_svec1(NULL); // expected-note{{Started tracking value here}} + JL_GC_PUSH1_NO_TPIN(&val); // expected-note{{GC frame changed here}} + // expected-note@-1{{Value was rooted here}} jl_gc_safepoint(); - look_at_value((jl_value_t*) val); + process_tpinned((jl_value_t*) val); // expected-warning{{Passing non-tpinned argument to function that requires a tpin argument}} + // expected-note@-1{{Passing non-tpinned argument to function that requires a tpin argument}} JL_GC_POP(); } void pointer_to_pointer(jl_value_t **v) { // *v is not pinned. - look_at_value(*v); // expected-warning{{Passing non-pinned value as argument to function that may GC}} - // expected-note@-1{{Passing non-pinned value as argument to function that may GC}} - // expected-note@-2{{Started tracking value here}} + look_at_value(*v); } void pointer_to_pointer2(jl_value_t* u, jl_value_t **v) { *v = u; - look_at_value(*v); // expected-warning{{Passing non-pinned value as argument to function that may GC}} - // expected-note@-1{{Passing non-pinned value as argument to function that may GC}} - // expected-note@+1{{Started tracking value here (root was inherited)}} + look_at_value(*v); } extern jl_value_t *first_array_elem(jl_array_t *a JL_PROPAGATES_ROOT); void root_propagation(jl_expr_t *expr) { - PTR_PIN(expr->args); jl_value_t *val = first_array_elem(expr->args); // expected-note{{Started tracking value here}} - PTR_UNPIN(expr->args); - jl_gc_safepoint(); + jl_gc_safepoint(); // expected-note{{Value was moved here}} look_at_value(val); // expected-warning{{Argument value may have been moved}} // expected-note@-1{{Argument value may have been moved}} } @@ -98,15 +104,11 @@ void root_propagation(jl_expr_t *expr) { void derive_ptr_alias(jl_method_instance_t *mi) { jl_value_t* a = mi->specTypes; jl_value_t* b = mi->specTypes; - PTR_PIN(a); look_at_value(b); - PTR_UNPIN(a); } void derive_ptr_alias2(jl_method_instance_t *mi) { - PTR_PIN(mi->specTypes); look_at_value(mi->specTypes); - PTR_UNPIN(mi->specTypes); } // Ignore this case for now. The checker conjures new syms for function return values. @@ -119,9 +121,7 @@ void derive_ptr_alias2(jl_method_instance_t *mi) { void mtable(jl_value_t *f) { jl_value_t* mtable = (jl_value_t*)jl_gf_mtable(f); - PTR_PIN(mtable); look_at_value(mtable); - PTR_UNPIN(mtable); } void pass_arg_to_non_safepoint(jl_tupletype_t *sigt) { @@ -132,7 +132,7 @@ void pass_arg_to_non_safepoint(jl_tupletype_t *sigt) { // So it is fine that the checker reports this as an error. void load_new_pointer_after_safepoint(jl_tupletype_t *t) { jl_value_t *a0 = jl_svecref(((jl_datatype_t*)(t))->parameters, 0);//expected-note{{Started tracking value here}} - jl_safepoint(); + jl_safepoint(); //expected-note{{Value was moved here}} jl_value_t *a1 = jl_svecref(((jl_datatype_t*)(t))->parameters, 1);//expected-warning{{Argument value may have been moved}} //expected-note@-1{{Argument value may have been moved}} } @@ -140,7 +140,7 @@ void load_new_pointer_after_safepoint(jl_tupletype_t *t) { void hoist_load_before_safepoint(jl_tupletype_t *t) { jl_svec_t* params = ((jl_datatype_t*)(t))->parameters; //expected-note{{Started tracking value here}} jl_value_t *a0 = jl_svecref(params, 0); - jl_safepoint(); + jl_safepoint(); //expected-note{{Value was moved here}} jl_value_t *a1 = jl_svecref(params, 1); //expected-warning{{Argument value may have been moved}} //expected-note@-1{{Argument value may have been moved}} } @@ -149,15 +149,13 @@ void hoist_load_before_safepoint(jl_tupletype_t *t) { void rebind_tpin(jl_method_instance_t *mi, size_t world) { jl_code_info_t *src = NULL; JL_GC_PUSH1(&src); + PTR_PIN(mi); jl_value_t *ci = jl_rettype_inferred(mi, world, world); + PTR_UNPIN(mi); jl_code_instance_t *codeinst = (ci == jl_nothing ? NULL : (jl_code_instance_t*)ci); if (codeinst) { - PTR_PIN(mi->def.method); - PTR_PIN(codeinst); src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); - PTR_UNPIN(codeinst); - PTR_UNPIN(mi->def.method); } JL_GC_POP(); } @@ -167,7 +165,7 @@ void rebind_tpin_simple1() { JL_GC_PUSH1(&t); jl_svec_t *v = jl_svec1(NULL); t = (jl_value_t*)v; - look_at_value(t); + process_tpinned(t); JL_GC_POP(); } @@ -176,7 +174,7 @@ void rebind_tpin_simple2() { JL_GC_PUSH1(&t); jl_svec_t *v = jl_svec1(NULL); t = (jl_value_t*)v; - look_at_value(v); + process_tpinned(v); JL_GC_POP(); } @@ -188,16 +186,29 @@ int transitive_closure(jl_value_t *v JL_REQUIRE_TPIN) { return 0; } -extern void look_at_tpin_value(jl_value_t *v JL_REQUIRE_TPIN); - int properly_tpin_arg(jl_value_t *v) { JL_GC_PUSH1(&v); - look_at_tpin_value(v); + process_tpinned(v); JL_GC_POP(); } int no_tpin_arg(jl_value_t *v) { - look_at_tpin_value(v); // expected-warning{{Passing non-tpinned argument to function that requires a tpin argument}} + process_tpinned(v); // expected-warning{{Passing non-tpinned argument to function that requires a tpin argument}} // expected-note@-1{{Passing non-tpinned argument to function that requires a tpin argument}} // expected-note@+1{{Started tracking value here (root was inherited)}} } + +jl_value_t *return_value_propagate(jl_value_t *t JL_PROPAGATES_ROOT); +void return_value_should_not_be_moved_propagated(jl_value_t *t) +{ + jl_value_t *ret = return_value_propagate(t); + // ret should not be moved at this point + PTR_PIN(ret); +} + +jl_value_t *return_value(); +void return_value_should_be_not_be_moved() { + jl_value_t *ret = return_value(); + // ret should not be moved at this point + PTR_PIN(ret); +} diff --git a/test/clangsa/MissingRoots.c b/test/clangsa/MissingRoots.c index 87d234096bee4..d34760bc6d024 100644 --- a/test/clangsa/MissingRoots.c +++ b/test/clangsa/MissingRoots.c @@ -1,12 +1,12 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license -// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CLANGSA_CXXFLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -Xclang -verify -x c %s +// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -Xclang -verify -x c %s #include "julia.h" #include "julia_internal.h" extern void look_at_value(jl_value_t *v); -extern void process_unrooted(jl_value_t *maybe_unrooted JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED); +extern void process_unrooted(jl_value_t *maybe_unrooted JL_MAYBE_UNROOTED); extern void jl_gc_safepoint(); void unrooted_argument() { @@ -181,18 +181,16 @@ void globally_rooted() { } extern jl_value_t *first_array_elem(jl_array_t *a JL_PROPAGATES_ROOT); - void root_propagation(jl_expr_t *expr) { - PTR_PIN(expr->args); jl_value_t *val = first_array_elem(expr->args); - PTR_UNPIN(expr->args); PTR_PIN(val); jl_gc_safepoint(); - look_at_value(val); PTR_UNPIN(val); + look_at_value(val); } void argument_propagation(jl_value_t *a) { + PTR_PIN(a); jl_svec_t *types = jl_svec2(NULL, NULL); JL_GC_PUSH1(&types); jl_value_t *val = jl_svecset(types, 0, jl_typeof(a)); @@ -200,19 +198,20 @@ void argument_propagation(jl_value_t *a) { look_at_value(val); jl_svecset(types, 1, jl_typeof(a)); JL_GC_POP(); + PTR_UNPIN(a); } // New value creation via [] void arg_array(jl_value_t **args) { + PTR_PIN(args[1]); jl_gc_safepoint(); jl_value_t *val = args[1]; - PTR_PIN(val); look_at_value(val); - PTR_UNPIN(val); jl_value_t *val2 = NULL; JL_GC_PUSH1(&val2); val2 = val; JL_GC_POP(); + PTR_UNPIN(args[1]); } // New value creation via -> @@ -246,7 +245,7 @@ void pushargs_as_args() JL_GC_POP(); } -static jl_typemap_entry_t *this_call_cache[10] JL_GLOBALLY_ROOTED JL_GLOBALLY_TPINNED; +static jl_typemap_entry_t *this_call_cache[10] JL_GLOBALLY_ROOTED; void global_array2() { jl_value_t *val = NULL; JL_GC_PUSH1(&val); @@ -267,9 +266,7 @@ void nonconst_loads(jl_svec_t *v) size_t i = jl_svec_len(v); jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(v); jl_method_instance_t *mi = data[i]; - PTR_PIN(mi->specTypes); look_at_value(mi->specTypes); - PTR_UNPIN(mi->specTypes); } void nonconst_loads2() @@ -294,6 +291,7 @@ void mtable(jl_value_t *f) { JL_GC_PUSH1(&val); val = mtable; JL_GC_POP(); + PTR_UNPIN(mtable); } void mtable2(jl_value_t **v) { @@ -304,18 +302,15 @@ void mtable2(jl_value_t **v) { } void tparam0(jl_value_t *atype) { - jl_value_t *param0 = jl_tparam0(atype); - PTR_PIN(param0); - look_at_value(param0); - PTR_UNPIN(param0); + look_at_value(jl_tparam0(atype)); } -extern jl_value_t *global_atype JL_GLOBALLY_ROOTED JL_GLOBALLY_TPINNED; +extern jl_value_t *global_atype JL_GLOBALLY_ROOTED; void tparam0_global() { look_at_value(jl_tparam0(global_atype)); } -static jl_value_t *some_global JL_GLOBALLY_ROOTED JL_GLOBALLY_PINNED; +static jl_value_t *some_global JL_GLOBALLY_ROOTED; void global_copy() { jl_value_t *local = NULL; jl_gc_safepoint(); @@ -342,6 +337,7 @@ void scopes() { jl_module_t *propagation(jl_module_t *m JL_PROPAGATES_ROOT); void module_member(jl_module_t *m) { + PTR_PIN(m); for(int i=(int)m->usings.len-1; i >= 0; --i) { jl_module_t *imp = propagation(m); PTR_PIN(imp); @@ -357,6 +353,7 @@ void module_member(jl_module_t *m) look_at_value((jl_value_t*)imp); JL_GC_POP(); } + PTR_UNPIN(m); } int type_type(jl_value_t *v) { @@ -371,7 +368,11 @@ void assoc_exact_broken(jl_value_t **args, size_t n, int8_t offs, size_t world) */ void assoc_exact_ok(jl_value_t *args1, jl_value_t **args, size_t n, int8_t offs, size_t world) { + PTR_PIN(args1); + PTR_PIN(args); jl_typemap_level_t *cache = jl_new_typemap_level(); + PTR_UNPIN(args1); + PTR_UNPIN(args); JL_GC_PUSH1(&cache); jl_typemap_assoc_exact(cache->any, args1, args, n, offs, world); JL_GC_POP();