void lift_builtins(World& world) { // This must be run first lift_pipeline(world); while (true) { Continuation* cur = nullptr; Scope::for_each(world, [&] (const Scope& scope) { if (cur) return; for (auto n : scope.f_cfg().post_order()) { if (n->continuation()->order() <= 1) continue; if (is_passed_to_accelerator(n->continuation(), false)) { cur = n->continuation(); break; } } }); if (!cur) break; static const int inline_threshold = 4; if (is_passed_to_intrinsic(cur, Intrinsic::Vectorize)) { Scope scope(cur); force_inline(scope, inline_threshold); } Scope scope(cur); // remove all continuations - they should be top-level functions and can thus be ignored std::vector<const Def*> defs; for (auto param : free_defs(scope)) { if (!param->isa_continuation()) { assert(param->order() == 0 && "creating a higher-order function"); defs.push_back(param); } } auto lifted = lift(scope, defs); for (auto use : cur->copy_uses()) { if (auto ucontinuation = use->isa_continuation()) { if (auto callee = ucontinuation->callee()->isa_continuation()) { if (callee->is_intrinsic()) { auto old_ops = ucontinuation->ops(); Array<const Def*> new_ops(old_ops.size() + defs.size()); std::copy(defs.begin(), defs.end(), std::copy(old_ops.begin(), old_ops.end(), new_ops.begin())); // old ops + former free defs assert(old_ops[use.index()] == cur); new_ops[use.index()] = world.global(lifted, false, lifted->debug()); // update to new lifted continuation // jump to new top-level dummy function with new args auto fn_type = world.fn_type(Array<const Type*>(new_ops.size()-1, [&] (auto i) { return new_ops[i+1]->type(); })); auto ncontinuation = world.continuation(fn_type, callee->cc(), callee->intrinsic(), callee->debug()); ucontinuation->jump(ncontinuation, new_ops.skip_front(), ucontinuation->jump_debug()); } } } } world.cleanup(); } }
ReturnValues<AnyCell>* llbase_call_with_current_continuation(World &world, TypedProcedureCell<ReturnValues<AnyCell>*, ProcedureCell*> *proc) { using dynamic::Continuation; using dynamic::EscapeProcedureCell; // This is the procedure we're calling alloc::StrongRef<TypedProcedureCell<ReturnValues<AnyCell>*, ProcedureCell*>> procRef(world, proc); // Create the escape procedure and its args // We build this first as there's no way to GC root a continuation at the moment. This also make sure the // continuation stays rooted in the resume path so we can access cont->passedValue(). Otherwise switching dynamic // states back to the original continuation could free the the continuation. alloc::StrongRef<EscapeProcedureCell> escapeRef(world, EscapeProcedureCell::createInstance(world, nullptr)); // Capture the current continuation Continuation *cont = Continuation::capture(world); if (ProperList<AnyCell> *passedValues = cont->takePassedValues()) { // We're the result of a continuation being invoked return passedValues; } else { // We're the original code flow path // Set the continuation on the escape proc - this will make the continuation reachable from GC escapeRef->setContinuation(cont); // Invoke the procedure passing in the escape proc // If it returns without invoking the escape proc we'll return through here return procRef->apply(world, escapeRef); } }
void invoke_continuation(Func& func, Future& future, Continuation& cont, boost::mpl::false_) { try { cont.set_data(func(std::move(future))); } catch (...) { cont.set_exception(boost::current_exception()); } }
void invoke_continuation(Func& func, Future && future, Continuation& cont, std::false_type) { try { cont.set_value(func(std::forward<Future>(future))); } catch (...) { cont.set_exception(std::current_exception()); } }
void invoke_continuation(Func& func, Future& future, Continuation& cont, boost::mpl::true_) { try { func(std::move(future)); cont.set_value(util::unused); } catch (...) { cont.set_exception(boost::current_exception()); } }
typename boost::enable_if< traits::is_future<typename util::result_of<Func(Future)>::type> >::type invoke_continuation(Func& func, Future& future, Continuation& cont) { try { typedef typename util::result_of<Func(Future)>::type inner_future; typedef typename shared_state_ptr_for<inner_future>::type inner_shared_state_ptr; // take by value, as the future may go away immediately inner_shared_state_ptr inner_state = future_access::get_shared_state(func(std::move(future))); if (inner_state.get() == 0) { HPX_THROW_EXCEPTION(no_state, "invoke_continuation", "the inner future has no valid shared state"); } // Bind an on_completed handler to this future which will transfer // its result to the new future. boost::intrusive_ptr<Continuation> cont_(&cont); inner_state->set_on_completed(util::bind( transfer_result<inner_future>(), inner_state, cont_)); } catch (...) { cont.set_exception(boost::current_exception()); } }
typename std::enable_if< traits::detail::is_unique_future< typename util::invoke_result<Func, Future>::type >::value >::type invoke_continuation(Func& func, Future && future, Continuation& cont) { try { typedef typename util::invoke_result<Func, Future>::type inner_future; typedef typename traits::detail::shared_state_ptr_for<inner_future>::type inner_shared_state_ptr; // take by value, as the future may go away immediately inner_shared_state_ptr inner_state = traits::detail::get_shared_state(func(std::forward<Future>(future))); typename inner_shared_state_ptr::element_type* ptr = inner_state.get(); if (ptr == nullptr) { HPX_THROW_EXCEPTION(no_state, "invoke_continuation", "the inner future has no valid shared state"); } // Bind an on_completed handler to this future which will transfer // its result to the new future. boost::intrusive_ptr<Continuation> cont_(&cont); ptr->execute_deferred(); ptr->set_on_completed( util::deferred_call(transfer_result<inner_future>(), std::move(inner_state), std::move(cont_))); } catch (...) { cont.set_exception(std::current_exception()); } }