anonymous namespace
| 481 | }; |
| 482 | } // anonymous namespace |
| 483 | MGB_IMPL_OPR_GRAD(JITExecutor) { |
| 484 | VarNodeArray grad_inputs; |
| 485 | for (auto input : opr.input()) |
| 486 | grad_inputs.push_back(input); |
| 487 | mgb_assert(out_grad[0]); |
| 488 | grad_inputs.push_back(opr.output(0)); |
| 489 | grad_inputs.push_back(out_grad[0]); |
| 490 | auto fwd_igraph_ptr = opr.internal_graph_ptr(); |
| 491 | auto output_ph = JITPlaceholder::make( |
| 492 | fwd_igraph_ptr->output(), fwd_igraph_ptr->placeholders().size()); |
| 493 | auto og_ph = JITPlaceholder::make( |
| 494 | out_grad[0], fwd_igraph_ptr->placeholders().size() + 1); |
| 495 | auto loss = opr::VirtualLoss::make({fwd_igraph_ptr->output()}, {og_ph}); |
| 496 | auto gx = cg::grad( |
| 497 | loss, fwd_igraph_ptr->placeholders()[wrt_idx]->output(0), false, false); |
| 498 | if (!gx.node()) { |
| 499 | return nullptr; |
| 500 | } |
| 501 | if (gx.node()->owner_opr()->same_type<opr::InvalidGrad>()) { |
| 502 | return opr::InvalidGrad::make(opr, wrt_idx); |
| 503 | } |
| 504 | // early return if grad expression is single node |
| 505 | for (size_t i = 0; i < fwd_igraph_ptr->placeholders().size(); ++i) { |
| 506 | if (gx.node() == fwd_igraph_ptr->placeholders()[i]->output(0)) { |
| 507 | return grad_inputs[i]; |
| 508 | } |
| 509 | } |
| 510 | if (gx.node() == og_ph.node()) { |
| 511 | return out_grad[0]; |
| 512 | } |
| 513 | if (gx.node() == fwd_igraph_ptr->output()) { |
| 514 | return opr.output(0); |
| 515 | } |
| 516 | if (auto imm = gopt::try_cast_as_op<opr::ImmutableTensor>(gx.node()->owner_opr())) { |
| 517 | HostTensorND hval{grad_inputs[0]->comp_node()}; |
| 518 | hval.copy_from(imm->value()).sync(); |
| 519 | return opr::ImmutableTensor::make(*imm->owner_graph(), hval).node(); |
| 520 | } |
| 521 | |
| 522 | // replace output var in internal graph with output placeholder, so |
| 523 | // we could forward opr.output(computeed by forward JITExecutor) into |
| 524 | // placeholder to avoid redundant computation |
| 525 | InternalGraphRewriter rewriter{gx.node()}; |
| 526 | rewriter.iter([&rewriter, &fwd_igraph_ptr, &output_ph](cg::OperatorNodeBase* opr) { |
| 527 | if (opr == fwd_igraph_ptr->output()->owner_opr()) { |
| 528 | rewriter.replace_var(opr->output(0), output_ph.node()); |
| 529 | return; |
| 530 | } |
| 531 | rewriter.auto_replace_outputs(opr); |
| 532 | }); |
| 533 | |
| 534 | auto expand_into_origin_graph = [&rewriter]( |
| 535 | cg::OperatorNodeBase* opr, |
| 536 | const VarNodeArray& grad_inputs) { |
| 537 | if (auto ph = gopt::try_cast_as_op<JITPlaceholder>(opr)) { |
| 538 | rewriter.replace_var(opr->output(0), grad_inputs.at(ph->input_id())); |
| 539 | return; |
| 540 | } |
nothing calls this directly
no test coverage detected