<commit_before>/*
 * This is part of the FL library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2014 Jan Issac (jan.issac@gmail.com)
 * Copyright (c) 2014 Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 * Max-Planck Institute for Intelligent Systems, AMD Lab
 * University of Southern California, CLMC Lab
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file traits_test.hpp
 * \date October 2014
 * \author Jan Issac (jan.issac@gmail.com)
 */

#include <gtest/gtest.h>

#include <Eigen/Dense>

#include <fl/util/traits.hpp>

TEST(TraitsTests, is_dynamic)
{
    EXPECT_TRUE(fl::IsDynamic<Eigen::Dynamic>());
    EXPECT_FALSE(fl::IsDynamic<10>());
}

TEST(TraitsTests, is_fixed)
{
    EXPECT_FALSE(fl::IsFixed<Eigen::Dynamic>());
    EXPECT_TRUE(fl::IsFixed<10>());
}

TEST(TraitsTests, DimensionOf_dynamic)
{
    EXPECT_EQ(fl::DimensionOf<Eigen::MatrixXd>(), 0);
    EXPECT_EQ(fl::DimensionOf<Eigen::VectorXd>(), 0);

    typedef Eigen::Matrix<double, 10, -1> PartiallyDynamicMatrix;
    EXPECT_EQ(fl::DimensionOf<PartiallyDynamicMatrix>(), 0);
}

TEST(TraitsTests, DimensionOf_fixed)
{
    EXPECT_EQ(fl::DimensionOf<Eigen::Matrix3d>(), 3);
    EXPECT_EQ(fl::DimensionOf<Eigen::Vector4d>(), 4);

    typedef Eigen::Matrix<double, 10, 10> FixedMatrix;
    EXPECT_EQ(fl::DimensionOf<FixedMatrix>(), 10);

    typedef Eigen::Matrix<double, 10, 1> FixedVector;
    EXPECT_EQ(fl::DimensionOf<FixedVector>(), 10);
}

//static constexpr size_t number_of_points(int dimension)
//{
//    return (dimension > Eigen::Dynamic) ? 2 * dimension + 1 : 0;
//}

//TEST(conststuff, constexpressions)
//{
//    enum
//    {
//        JointDimension = fl::JoinSizes<1, 2, 3, 4>::Size,
//        NumberOfPoints = number_of_points(fl::JoinSizes<1, 2, 3, 4>::Size)
//    };

//    std::cout << JointDimension << std::endl;
//    std::cout << NumberOfPoints << std::endl;
//}

<commit_msg>Some cleanup<commit_after>/*
 * This is part of the FL library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2014 Jan Issac (jan.issac@gmail.com)
 * Copyright (c) 2014 Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 * Max-Planck Institute for Intelligent Systems, AMD Lab
 * University of Southern California, CLMC Lab
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file traits_test.hpp
 * \date October 2014
 * \author Jan Issac (jan.issac@gmail.com)
 */

#include <gtest/gtest.h>

#include <Eigen/Dense>

#include <fl/util/math.hpp>
#include <fl/util/traits.hpp>

#include <cmath>

TEST(TraitsTests, is_dynamic)
{
    EXPECT_TRUE(fl::IsDynamic<Eigen::Dynamic>());
    EXPECT_FALSE(fl::IsDynamic<10>());
}

TEST(TraitsTests, is_fixed)
{
    EXPECT_FALSE(fl::IsFixed<Eigen::Dynamic>());
    EXPECT_TRUE(fl::IsFixed<10>());
}

TEST(TraitsTests, DimensionOf_dynamic)
{
    EXPECT_EQ(fl::DimensionOf<Eigen::MatrixXd>(), 0);
    EXPECT_EQ(fl::DimensionOf<Eigen::VectorXd>(), 0);

    typedef Eigen::Matrix<double, 10, -1> PartiallyDynamicMatrix;
    EXPECT_EQ(fl::DimensionOf<PartiallyDynamicMatrix>(), 0);
}

TEST(TraitsTests, DimensionOf_fixed)
{
    EXPECT_EQ(fl::DimensionOf<Eigen::Matrix3d>(), 3);
    EXPECT_EQ(fl::DimensionOf<Eigen::Vector4d>(), 4);

    typedef Eigen::Matrix<double, 10, 10> FixedMatrix;
    EXPECT_EQ(fl::DimensionOf<FixedMatrix>(), 10);

    typedef Eigen::Matrix<double, 10, 1> FixedVector;
    EXPECT_EQ(fl::DimensionOf<FixedVector>(), 10);
}

//static constexpr size_t number_of_points(int dimension)
//{
//    return (dimension > Eigen::Dynamic) ? 2 * dimension + 1 : 0;
//}

//TEST(conststuff, constexpressions)
//{
//    enum
//    {
//        JointDimension = fl::JoinSizes<1, 2, 3, 4>::Size,
//        NumberOfPoints = number_of_points(fl::JoinSizes<1, 2, 3, 4>::Size)
//    };

//    std::cout << JointDimension << std::endl;
//    std::cout << NumberOfPoints << std::endl;
//}

<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

/**
 * @file
 **/
#include <algorithm>
#include <vector>

#include "modules/planning/tasks/traffic_decider/destination.h"

#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/time/time.h"
#include "modules/map/proto/map_lane.pb.h"
#include "modules/planning/common/planning_gflags.h"
#include "modules/planning/common/planning_util.h"

namespace apollo {
namespace planning {

using apollo::common::Status;
using apollo::common::adapter::AdapterManager;
using apollo::common::time::Clock;
using apollo::hdmap::HDMapUtil;
using apollo::hdmap::LaneSegment;
using apollo::planning::util::GetPlanningStatus;

Destination::Destination(const TrafficRuleConfig& config)
    : TrafficRule(config) {}

Status Destination::ApplyRule(Frame* frame,
                              ReferenceLineInfo* const reference_line_info) {
  CHECK_NOTNULL(frame);
  CHECK_NOTNULL(reference_line_info);

  if (!frame->is_near_destination()) {
    return Status::OK();
  }

  const auto& routing =
      AdapterManager::GetRoutingResponse()->GetLatestObserved();

  common::SLPoint dest_sl;
  const auto& ref_line = reference_line_info->reference_line();
  const auto& routing_end = *routing.routing_request().waypoint().rbegin();
  ref_line.XYToSL({routing_end.pose().x(), routing_end.pose().y()}, &dest_sl);
  const auto& adc_sl = reference_line_info->AdcSlBoundary();
  const auto& dest = GetPlanningStatus()->destination();
  if (adc_sl.end_s() > dest_sl.s() && !dest.has_passed_destination()) {
    ADEBUG << "Destination at back, but we have not reached destination yet";
    return Status::OK();
  }

  BuildStopDecision(frame, reference_line_info);

  return Status::OK();
}

/**
 * @brief: build stop decision
 */
int Destination::BuildStopDecision(
    Frame* frame, ReferenceLineInfo* const reference_line_info) {
  CHECK_NOTNULL(frame);
  CHECK_NOTNULL(reference_line_info);

  const auto& routing =
      AdapterManager::GetRoutingResponse()->GetLatestObserved();
  if (routing.routing_request().waypoint_size() < 2) {
    AERROR << "routing_request has no end";
    return -1;
  }

  const auto& planning_state = GetPlanningStatus()->planning_state();
  const auto& routing_end = *routing.routing_request().waypoint().rbegin();
  double dest_lane_s =
      std::max(0.0, routing_end.s() - FLAGS_virtual_stop_wall_length -
                        config_.destination().stop_distance());

  if (planning_state.has_pull_over() &&
      planning_state.pull_over().has_status() &&
      planning_state.pull_over().status() == PullOverStatus::DISABLED) {
    Stop(frame, reference_line_info, routing_end.id(), dest_lane_s);
    ADEBUG << "destination: STOP at current lane. PULL-OVER disabled";
    return 0;
  }

  common::PointENU dest_point;
  if (CheckPullOver(reference_line_info, routing_end.id(), dest_lane_s,
                    &dest_point)) {
    if (planning_state.has_pull_over() &&
        planning_state.pull_over().in_pull_over()) {
      PullOver(nullptr);
      ADEBUG << "destination: continue PULL OVER";
    } else {
      PullOver(&dest_point);
      ADEBUG << "destination: PULL OVER";
    }
  } else {
    Stop(frame, reference_line_info, routing_end.id(), dest_lane_s);
    ADEBUG << "destination: STOP at current lane";
  }

  return 0;
}

/**
 * @brief: build on-lane stop decision upon arriving at destination
 */
int Destination::Stop(Frame* const frame,
                      ReferenceLineInfo* const reference_line_info,
                      const std::string lane_id, const double lane_s) {
  CHECK_NOTNULL(frame);
  CHECK_NOTNULL(reference_line_info);

  const auto& reference_line = reference_line_info->reference_line();

  // create virtual stop wall
  std::string stop_wall_id = FLAGS_destination_obstacle_id;
  auto* obstacle = frame->CreateStopObstacle(stop_wall_id, lane_id, lane_s);
  if (!obstacle) {
    AERROR << "Failed to create obstacle [" << stop_wall_id << "]";
    return -1;
  }

  PathObstacle* stop_wall = reference_line_info->AddObstacle(obstacle);
  if (!stop_wall) {
    AERROR << "Failed to create path_obstacle for: " << stop_wall_id;
    return -1;
  }

  // build stop decision
  const auto stop_wall_box = stop_wall->obstacle()->PerceptionBoundingBox();
  if (!reference_line.IsOnLane(stop_wall_box.center())) {
    ADEBUG << "destination point is not on lane";
    return 0;
  }
  auto stop_point = reference_line.GetReferencePoint(
      stop_wall->PerceptionSLBoundary().start_s() -
      config_.destination().stop_distance());

  ObjectDecisionType stop;
  auto stop_decision = stop.mutable_stop();
  stop_decision->set_reason_code(StopReasonCode::STOP_REASON_DESTINATION);
  stop_decision->set_distance_s(-config_.destination().stop_distance());
  stop_decision->set_stop_heading(stop_point.heading());
  stop_decision->mutable_stop_point()->set_x(stop_point.x());
  stop_decision->mutable_stop_point()->set_y(stop_point.y());
  stop_decision->mutable_stop_point()->set_z(0.0);

  auto* path_decision = reference_line_info->path_decision();
  path_decision->AddLongitudinalDecision(
      TrafficRuleConfig::RuleId_Name(config_.rule_id()), stop_wall->Id(), stop);

  return 0;
}

/**
 * @brief: check if adc will pull-over upon arriving destination
 */
bool Destination::CheckPullOver(ReferenceLineInfo* const reference_line_info,
                                const std::string& dest_lane_id,
                                const double dest_lane_s,
                                common::PointENU* dest_point) {
  CHECK_NOTNULL(reference_line_info);

  if (!config_.destination().enable_pull_over()) {
    return false;
  }

  const auto hdmap_ptr = HDMapUtil::BaseMapPtr();
  if (!hdmap_ptr) {
    AERROR << "Invalid HD Map.";
    return false;
  }
  const auto dest_lane = hdmap_ptr->GetLaneById(hdmap::MakeMapId(dest_lane_id));
  if (!dest_lane) {
    AERROR << "Failed to find lane[" << dest_lane_id << "]";
    return false;
  }

  // check if ChangeLane
  bool change_lane = false;
  const auto& segments = reference_line_info->Lanes();
  if (segments.NextAction() != routing::FORWARD) {
    change_lane = true;
  }

  // check dest OnRoad
  const auto& reference_line = reference_line_info->reference_line();
  *dest_point = dest_lane->GetSmoothPoint(dest_lane_s);
  if (!change_lane && !reference_line.IsOnLane(*dest_point)) {
    return false;
  }

  // check dest within pull_over_plan_distance
  common::SLPoint dest_sl;
  if (!reference_line.XYToSL({dest_point->x(), dest_point->y()}, &dest_sl)) {
    AERROR << "failed to project the dest point to the other reference line";
    return false;
  }
  double adc_front_edge_s = reference_line_info->AdcSlBoundary().end_s();
  double distance_to_dest = dest_sl.s() - adc_front_edge_s;
  ADEBUG << "adc_front_edge_s[" << adc_front_edge_s << "] distance_to_dest["
         << distance_to_dest << "] dest_lane[" << dest_lane_id
         << "] dest_lane_s[" << dest_lane_s << "]";

  if (distance_to_dest > config_.destination().pull_over_plan_distance()) {
    // to far, not sending pull-over yet
    return false;
  }

  // Disable pull-over for the rest route if ChangeLane and clost to dest
  if (change_lane) {
    auto* planning_state = GetPlanningStatus()->mutable_planning_state();
    planning_state->clear_pull_over();
    auto pull_over = planning_state->mutable_pull_over();
    pull_over->set_reason(PullOverStatus::DESTINATION);
    pull_over->set_status(PullOverStatus::DISABLED);
    pull_over->set_status_set_time(Clock::NowInSeconds());

    return false;
  }

  return true;
}

/**
 * @brief: build pull-over decision upon arriving at destination
 */
int Destination::PullOver(common::PointENU* const dest_point) {
  auto* planning_state = GetPlanningStatus()->mutable_planning_state();
  if (!planning_state->has_pull_over() ||
      !planning_state->pull_over().in_pull_over()) {
    planning_state->clear_pull_over();
    auto pull_over = planning_state->mutable_pull_over();
    pull_over->set_in_pull_over(true);
    pull_over->set_reason(PullOverStatus::DESTINATION);
    pull_over->set_status_set_time(Clock::NowInSeconds());

    if (dest_point) {
      pull_over->mutable_inlane_dest_point()->set_x(dest_point->x());
      pull_over->mutable_inlane_dest_point()->set_y(dest_point->y());
    }
  }

  return 0;
}

}  // namespace planning
}  // namespace apollo
<commit_msg>planning: relax destination stop<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

/**
 * @file
 **/
#include <algorithm>
#include <vector>

#include "modules/planning/tasks/traffic_decider/destination.h"

#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/time/time.h"
#include "modules/map/proto/map_lane.pb.h"
#include "modules/planning/common/planning_gflags.h"
#include "modules/planning/common/planning_util.h"

namespace apollo {
namespace planning {

using apollo::common::Status;
using apollo::common::adapter::AdapterManager;
using apollo::common::time::Clock;
using apollo::hdmap::HDMapUtil;
using apollo::hdmap::LaneSegment;
using apollo::planning::util::GetPlanningStatus;

Destination::Destination(const TrafficRuleConfig& config)
    : TrafficRule(config) {}

Status Destination::ApplyRule(Frame* frame,
                              ReferenceLineInfo* const reference_line_info) {
  CHECK_NOTNULL(frame);
  CHECK_NOTNULL(reference_line_info);

  if (!frame->is_near_destination()) {
    return Status::OK();
  }

  const auto& routing =
      AdapterManager::GetRoutingResponse()->GetLatestObserved();

  common::SLPoint dest_sl;
  const auto& ref_line = reference_line_info->reference_line();
  const auto& routing_end = *routing.routing_request().waypoint().rbegin();
  ref_line.XYToSL({routing_end.pose().x(), routing_end.pose().y()}, &dest_sl);
  const auto& adc_sl = reference_line_info->AdcSlBoundary();
  const auto& dest = GetPlanningStatus()->destination();
  if (adc_sl.start_s() > dest_sl.s() && !dest.has_passed_destination()) {
    ADEBUG << "Destination at back, but we have not reached destination yet";
    return Status::OK();
  }

  BuildStopDecision(frame, reference_line_info);

  return Status::OK();
}

/**
 * @brief: build stop decision
 */
int Destination::BuildStopDecision(
    Frame* frame, ReferenceLineInfo* const reference_line_info) {
  CHECK_NOTNULL(frame);
  CHECK_NOTNULL(reference_line_info);

  const auto& routing =
      AdapterManager::GetRoutingResponse()->GetLatestObserved();
  if (routing.routing_request().waypoint_size() < 2) {
    AERROR << "routing_request has no end";
    return -1;
  }

  const auto& planning_state = GetPlanningStatus()->planning_state();
  const auto& routing_end = *routing.routing_request().waypoint().rbegin();
  double dest_lane_s =
      std::max(0.0, routing_end.s() - FLAGS_virtual_stop_wall_length -
                        config_.destination().stop_distance());

  if (planning_state.has_pull_over() &&
      planning_state.pull_over().has_status() &&
      planning_state.pull_over().status() == PullOverStatus::DISABLED) {
    Stop(frame, reference_line_info, routing_end.id(), dest_lane_s);
    ADEBUG << "destination: STOP at current lane. PULL-OVER disabled";
    return 0;
  }

  common::PointENU dest_point;
  if (CheckPullOver(reference_line_info, routing_end.id(), dest_lane_s,
                    &dest_point)) {
    if (planning_state.has_pull_over() &&
        planning_state.pull_over().in_pull_over()) {
      PullOver(nullptr);
      ADEBUG << "destination: continue PULL OVER";
    } else {
      PullOver(&dest_point);
      ADEBUG << "destination: PULL OVER";
    }
  } else {
    Stop(frame, reference_line_info, routing_end.id(), dest_lane_s);
    ADEBUG << "destination: STOP at current lane";
  }

  return 0;
}

/**
 * @brief: build on-lane stop decision upon arriving at destination
 */
int Destination::Stop(Frame* const frame,
                      ReferenceLineInfo* const reference_line_info,
                      const std::string lane_id, const double lane_s) {
  CHECK_NOTNULL(frame);
  CHECK_NOTNULL(reference_line_info);

  const auto& reference_line = reference_line_info->reference_line();

  // create virtual stop wall
  std::string stop_wall_id = FLAGS_destination_obstacle_id;
  auto* obstacle = frame->CreateStopObstacle(stop_wall_id, lane_id, lane_s);
  if (!obstacle) {
    AERROR << "Failed to create obstacle [" << stop_wall_id << "]";
    return -1;
  }

  PathObstacle* stop_wall = reference_line_info->AddObstacle(obstacle);
  if (!stop_wall) {
    AERROR << "Failed to create path_obstacle for: " << stop_wall_id;
    return -1;
  }

  // build stop decision
  const auto stop_wall_box = stop_wall->obstacle()->PerceptionBoundingBox();
  if (!reference_line.IsOnLane(stop_wall_box.center())) {
    ADEBUG << "destination point is not on lane";
    return 0;
  }
  auto stop_point = reference_line.GetReferencePoint(
      stop_wall->PerceptionSLBoundary().start_s() -
      config_.destination().stop_distance());

  ObjectDecisionType stop;
  auto stop_decision = stop.mutable_stop();
  stop_decision->set_reason_code(StopReasonCode::STOP_REASON_DESTINATION);
  stop_decision->set_distance_s(-config_.destination().stop_distance());
  stop_decision->set_stop_heading(stop_point.heading());
  stop_decision->mutable_stop_point()->set_x(stop_point.x());
  stop_decision->mutable_stop_point()->set_y(stop_point.y());
  stop_decision->mutable_stop_point()->set_z(0.0);

  auto* path_decision = reference_line_info->path_decision();
  path_decision->AddLongitudinalDecision(
      TrafficRuleConfig::RuleId_Name(config_.rule_id()), stop_wall->Id(), stop);

  return 0;
}

/**
 * @brief: check if adc will pull-over upon arriving destination
 */
bool Destination::CheckPullOver(ReferenceLineInfo* const reference_line_info,
                                const std::string& dest_lane_id,
                                const double dest_lane_s,
                                common::PointENU* dest_point) {
  CHECK_NOTNULL(reference_line_info);

  if (!config_.destination().enable_pull_over()) {
    return false;
  }

  const auto hdmap_ptr = HDMapUtil::BaseMapPtr();
  if (!hdmap_ptr) {
    AERROR << "Invalid HD Map.";
    return false;
  }
  const auto dest_lane = hdmap_ptr->GetLaneById(hdmap::MakeMapId(dest_lane_id));
  if (!dest_lane) {
    AERROR << "Failed to find lane[" << dest_lane_id << "]";
    return false;
  }

  // check if ChangeLane
  bool change_lane = false;
  const auto& segments = reference_line_info->Lanes();
  if (segments.NextAction() != routing::FORWARD) {
    change_lane = true;
  }

  // check dest OnRoad
  const auto& reference_line = reference_line_info->reference_line();
  *dest_point = dest_lane->GetSmoothPoint(dest_lane_s);
  if (!change_lane && !reference_line.IsOnLane(*dest_point)) {
    return false;
  }

  // check dest within pull_over_plan_distance
  common::SLPoint dest_sl;
  if (!reference_line.XYToSL({dest_point->x(), dest_point->y()}, &dest_sl)) {
    AERROR << "failed to project the dest point to the other reference line";
    return false;
  }
  double adc_front_edge_s = reference_line_info->AdcSlBoundary().end_s();
  double distance_to_dest = dest_sl.s() - adc_front_edge_s;
  ADEBUG << "adc_front_edge_s[" << adc_front_edge_s << "] distance_to_dest["
         << distance_to_dest << "] dest_lane[" << dest_lane_id
         << "] dest_lane_s[" << dest_lane_s << "]";

  if (distance_to_dest > config_.destination().pull_over_plan_distance()) {
    // to far, not sending pull-over yet
    return false;
  }

  // Disable pull-over for the rest route if ChangeLane and clost to dest
  if (change_lane) {
    auto* planning_state = GetPlanningStatus()->mutable_planning_state();
    planning_state->clear_pull_over();
    auto pull_over = planning_state->mutable_pull_over();
    pull_over->set_reason(PullOverStatus::DESTINATION);
    pull_over->set_status(PullOverStatus::DISABLED);
    pull_over->set_status_set_time(Clock::NowInSeconds());

    return false;
  }

  return true;
}

/**
 * @brief: build pull-over decision upon arriving at destination
 */
int Destination::PullOver(common::PointENU* const dest_point) {
  auto* planning_state = GetPlanningStatus()->mutable_planning_state();
  if (!planning_state->has_pull_over() ||
      !planning_state->pull_over().in_pull_over()) {
    planning_state->clear_pull_over();
    auto pull_over = planning_state->mutable_pull_over();
    pull_over->set_in_pull_over(true);
    pull_over->set_reason(PullOverStatus::DESTINATION);
    pull_over->set_status_set_time(Clock::NowInSeconds());

    if (dest_point) {
      pull_over->mutable_inlane_dest_point()->set_x(dest_point->x());
      pull_over->mutable_inlane_dest_point()->set_y(dest_point->y());
    }
  }

  return 0;
}

}  // namespace planning
}  // namespace apollo
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2017-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "LiveRange.h"

#include <boost/pending/disjoint_sets.hpp>
#include <boost/property_map/property_map.hpp>

#include "ControlFlow.h"
#include "FixpointIterators.h"
#include "HashedAbstractEnvironment.h"
#include "HashedSetAbstractDomain.h"
#include "Transform.h"

namespace {

using namespace live_range;
using namespace std::placeholders;

/*
 * Type aliases for disjoint_sets
 */
using Rank = std::unordered_map<Def, size_t>;
using Parent = std::unordered_map<Def, Def>;
using RankPMap = boost::associative_property_map<Rank>;
using ParentPMap = boost::associative_property_map<Parent>;
using DefSets = boost::disjoint_sets<RankPMap, ParentPMap>;

/*
 * Allocates a unique symbolic register for every disjoint set of defs.
 */
class SymRegMapper {
  reg_t m_next_symreg {0};
  std::unordered_map<Def, reg_t> m_def_to_reg;
 public:
   reg_t make(Def def) {
     if (m_def_to_reg.find(def) == m_def_to_reg.end()) {
       m_def_to_reg[def] = m_next_symreg++;
     }
     return m_def_to_reg.at(def);
   }
   reg_t at(Def def) {
     return m_def_to_reg.at(def);
   }
   reg_t regs_size() const {
     return m_next_symreg;
   }
};

/*
 * Put all defs with a use in common into the same set.
 */
void unify_defs(const UDChains& chains, DefSets* def_sets) {
  for (const auto& chain : chains) {
    auto& defs = chain.second;
    auto it = defs.begin();
    Def first = *it;
    auto end = defs.end();
    for (++it; it != end; ++it) {
      def_sets->union_set(first, *it);
    }
  }
}

class DefsDomain final : public AbstractDomainReverseAdaptor<
                             HashedSetAbstractDomain<IRInstruction*>,
                             DefsDomain> {
 public:
  using AbstractDomainReverseAdaptor::AbstractDomainReverseAdaptor;

  explicit DefsDomain(IRInstruction* insn)
      : AbstractDomainReverseAdaptor(BaseDomain(insn)) {}

  size_t size() const {
    return unwrap().size();
  }

  const std::unordered_set<IRInstruction*>& elements() const {
    return unwrap().elements();
  }
};

class DefsEnvironment final : public AbstractDomainReverseAdaptor<
                                  HashedAbstractEnvironment<reg_t, DefsDomain>,
                                  DefsEnvironment> {
 public:
  using AbstractDomainReverseAdaptor::AbstractDomainReverseAdaptor;

  DefsDomain get(reg_t reg) {
    return unwrap().get(reg);
  }

  DefsEnvironment set(reg_t reg, const DefsDomain& value) {
    unwrap().set(reg, value);
    return *this;
  }
};

class ReachingDefsFixpointIterator final
    : public MonotonicFixpointIterator<Block*, DefsEnvironment> {
 public:
  using NodeId = Block*;

  ReachingDefsFixpointIterator(const ControlFlowGraph&, NodeId entry_block)
      : MonotonicFixpointIterator(entry_block,
                                  std::bind(&Block::succs, _1),
                                  std::bind(&Block::preds, _1)) {}

  void analyze_node(const NodeId& block,
                    DefsEnvironment* current_state) const override {
    for (const auto& mie : InstructionIterable(block)) {
      analyze_instruction(mie.insn, current_state);
    }
  }

  void analyze_instruction(const IRInstruction* insn,
                           DefsEnvironment* current_state) const {
    if (insn->dests_size()) {
      current_state->set(insn->dest(),
                         DefsDomain(const_cast<IRInstruction*>(insn)));
    }
  }

  DefsEnvironment analyze_edge(
      const NodeId& /* source_block */,
      const NodeId& /* target_block */,
      const DefsEnvironment& entry_state_at_source) const override {
    return entry_state_at_source;
  }
};

UDChains calculate_ud_chains(IRCode* code) {
  code->build_cfg();
  auto& cfg = code->cfg();
  always_assert(cfg.blocks().at(0)->id() == 0);
  ReachingDefsFixpointIterator fixpoint_iter(cfg, cfg.blocks().at(0));
  fixpoint_iter.run(DefsEnvironment());
  UDChains chains;
  for (Block* block : cfg.blocks()) {
    DefsEnvironment defs_in = fixpoint_iter.get_entry_state_at(block);
    for (const auto& mie : InstructionIterable(block)) {
      auto insn = mie.insn;
      for (size_t i = 0; i < insn->srcs_size(); ++i) {
        auto src = insn->src(i);
        Use use {insn, src};
        auto defs = defs_in.get(src);
        always_assert_log(defs.size() > 0,
                          "Found use without def when processing %s",
                          SHOW(insn));
        chains[use] = defs.elements();
      }
      fixpoint_iter.analyze_instruction(insn, &defs_in);
    }
  }
  return chains;
}

} // namespace

namespace live_range {

bool Use::operator==(const Use& that) const {
  return insn == that.insn && reg == that.reg;
}

void renumber_registers(IRCode* code) {
  auto chains = calculate_ud_chains(code);

  Rank rank;
  Parent parent;
  DefSets def_sets((RankPMap(rank)), (ParentPMap(parent)));
  for (const auto& mie : InstructionIterable(code)) {
    if (mie.insn->dests_size()) {
      def_sets.make_set(mie.insn);
    }
  }
  unify_defs(chains, &def_sets);
  SymRegMapper sym_reg_mapper;
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    if (insn->dests_size()) {
      auto sym_reg = sym_reg_mapper.make(def_sets.find_set(insn));
      insn->set_dest(sym_reg);
    }
  }
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    for (size_t i = 0; i < insn->srcs_size(); ++i) {
      auto& defs = chains.at(Use {insn, insn->src(i)});
      insn->set_src(i, sym_reg_mapper.at(def_sets.find_set(*defs.begin())));
    }
  }
  code->set_registers_size(sym_reg_mapper.regs_size());
}

} // namespace live_range
<commit_msg>A quick compiler error fix<commit_after>/**
 * Copyright (c) 2017-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "LiveRange.h"

#include <boost/pending/disjoint_sets.hpp>
#include <boost/property_map/property_map.hpp>

#include "ControlFlow.h"
#include "FixpointIterators.h"
#include "HashedAbstractEnvironment.h"
#include "HashedSetAbstractDomain.h"
#include "Transform.h"

namespace {

using namespace live_range;
using namespace std::placeholders;

/*
 * Type aliases for disjoint_sets
 */
using Rank = std::unordered_map<Def, size_t>;
using Parent = std::unordered_map<Def, Def>;
using RankPMap = boost::associative_property_map<Rank>;
using ParentPMap = boost::associative_property_map<Parent>;
using DefSets = boost::disjoint_sets<RankPMap, ParentPMap>;

/*
 * Allocates a unique symbolic register for every disjoint set of defs.
 */
class SymRegMapper {
  reg_t m_next_symreg {0};
  std::unordered_map<Def, reg_t> m_def_to_reg;
 public:
   reg_t make(Def def) {
     if (m_def_to_reg.find(def) == m_def_to_reg.end()) {
       m_def_to_reg[def] = m_next_symreg++;
     }
     return m_def_to_reg.at(def);
   }
   reg_t at(Def def) {
     return m_def_to_reg.at(def);
   }
   reg_t regs_size() const {
     return m_next_symreg;
   }
};

/*
 * Put all defs with a use in common into the same set.
 */
void unify_defs(const UDChains& chains, DefSets* def_sets) {
  for (const auto& chain : chains) {
    auto& defs = chain.second;
    auto it = defs.begin();
    Def first = *it;
    auto end = defs.end();
    for (++it; it != end; ++it) {
      def_sets->union_set(first, *it);
    }
  }
}

class DefsDomain final : public AbstractDomainReverseAdaptor<
                             HashedSetAbstractDomain<IRInstruction*>,
                             DefsDomain> {
 public:
  using AbstractDomainReverseAdaptor::AbstractDomainReverseAdaptor;

  // Some older compilers complain that the class is not default constructible.
  // We intended to use the default constructors of the base class (via using
  // AbstractDomainReverseAdaptor::AbstractDomainReverseAdaptor), but some
  // compilers fail to catch this. So we insert a redundant '= default'.
  DefsDomain() = default;
  explicit DefsDomain(IRInstruction* insn)
      : AbstractDomainReverseAdaptor(BaseDomain(insn)) {}

  size_t size() const {
    return unwrap().size();
  }

  const std::unordered_set<IRInstruction*>& elements() const {
    return unwrap().elements();
  }
};

class DefsEnvironment final : public AbstractDomainReverseAdaptor<
                                  HashedAbstractEnvironment<reg_t, DefsDomain>,
                                  DefsEnvironment> {
 public:
  using AbstractDomainReverseAdaptor::AbstractDomainReverseAdaptor;

  DefsDomain get(reg_t reg) {
    return unwrap().get(reg);
  }

  DefsEnvironment set(reg_t reg, const DefsDomain& value) {
    unwrap().set(reg, value);
    return *this;
  }
};

class ReachingDefsFixpointIterator final
    : public MonotonicFixpointIterator<Block*, DefsEnvironment> {
 public:
  using NodeId = Block*;

  ReachingDefsFixpointIterator(const ControlFlowGraph&, NodeId entry_block)
      : MonotonicFixpointIterator(entry_block,
                                  std::bind(&Block::succs, _1),
                                  std::bind(&Block::preds, _1)) {}

  void analyze_node(const NodeId& block,
                    DefsEnvironment* current_state) const override {
    for (const auto& mie : InstructionIterable(block)) {
      analyze_instruction(mie.insn, current_state);
    }
  }

  void analyze_instruction(const IRInstruction* insn,
                           DefsEnvironment* current_state) const {
    if (insn->dests_size()) {
      current_state->set(insn->dest(),
                         DefsDomain(const_cast<IRInstruction*>(insn)));
    }
  }

  DefsEnvironment analyze_edge(
      const NodeId& /* source_block */,
      const NodeId& /* target_block */,
      const DefsEnvironment& entry_state_at_source) const override {
    return entry_state_at_source;
  }
};

UDChains calculate_ud_chains(IRCode* code) {
  code->build_cfg();
  auto& cfg = code->cfg();
  always_assert(cfg.blocks().at(0)->id() == 0);
  ReachingDefsFixpointIterator fixpoint_iter(cfg, cfg.blocks().at(0));
  fixpoint_iter.run(DefsEnvironment());
  UDChains chains;
  for (Block* block : cfg.blocks()) {
    DefsEnvironment defs_in = fixpoint_iter.get_entry_state_at(block);
    for (const auto& mie : InstructionIterable(block)) {
      auto insn = mie.insn;
      for (size_t i = 0; i < insn->srcs_size(); ++i) {
        auto src = insn->src(i);
        Use use {insn, src};
        auto defs = defs_in.get(src);
        always_assert_log(defs.size() > 0,
                          "Found use without def when processing %s",
                          SHOW(insn));
        chains[use] = defs.elements();
      }
      fixpoint_iter.analyze_instruction(insn, &defs_in);
    }
  }
  return chains;
}

} // namespace

namespace live_range {

bool Use::operator==(const Use& that) const {
  return insn == that.insn && reg == that.reg;
}

void renumber_registers(IRCode* code) {
  auto chains = calculate_ud_chains(code);

  Rank rank;
  Parent parent;
  DefSets def_sets((RankPMap(rank)), (ParentPMap(parent)));
  for (const auto& mie : InstructionIterable(code)) {
    if (mie.insn->dests_size()) {
      def_sets.make_set(mie.insn);
    }
  }
  unify_defs(chains, &def_sets);
  SymRegMapper sym_reg_mapper;
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    if (insn->dests_size()) {
      auto sym_reg = sym_reg_mapper.make(def_sets.find_set(insn));
      insn->set_dest(sym_reg);
    }
  }
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    for (size_t i = 0; i < insn->srcs_size(); ++i) {
      auto& defs = chains.at(Use {insn, insn->src(i)});
      insn->set_src(i, sym_reg_mapper.at(def_sets.find_set(*defs.begin())));
    }
  }
  code->set_registers_size(sym_reg_mapper.regs_size());
}

} // namespace live_range
<|endoftext|>


<commit_before>// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/browser/api/electron_api_browser_window.h"

#include "content/browser/web_contents/web_contents_impl.h"  // nogncheck
#include "shell/browser/native_window_views.h"
#include "ui/aura/window.h"

namespace electron {

namespace api {

void BrowserWindow::UpdateDraggableRegions(
    const std::vector<mojom::DraggableRegionPtr>& regions) {
  if (window_->has_frame())
    return;

  if (&draggable_regions_ != &regions) {
    auto* view =
        static_cast<content::WebContentsImpl*>(web_contents())->GetView();
    if (view) {
      const gfx::NativeView nv = view->GetNativeView();
      auto const offset = nv->GetBoundsInRootWindow();
      auto snapped_regions = mojo::Clone(regions);
      for (auto& snapped_region : snapped_regions) {
        snapped_region->bounds.Offset(offset.x(), offset.y());
      }

      draggable_regions_ = mojo::Clone(snapped_regions);
    }
  }

  static_cast<NativeWindowViews*>(window_.get())
      ->UpdateDraggableRegions(draggable_regions_);
}

}  // namespace api

}  // namespace electron
<commit_msg>fix: ensure web_contents() is alive before grabbing view (#30571)<commit_after>// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/browser/api/electron_api_browser_window.h"

#include "content/browser/web_contents/web_contents_impl.h"  // nogncheck
#include "shell/browser/native_window_views.h"
#include "ui/aura/window.h"

namespace electron {

namespace api {

void BrowserWindow::UpdateDraggableRegions(
    const std::vector<mojom::DraggableRegionPtr>& regions) {
  if (window_->has_frame())
    return;

  if (&draggable_regions_ != &regions && web_contents()) {
    auto* view =
        static_cast<content::WebContentsImpl*>(web_contents())->GetView();
    if (view) {
      const gfx::NativeView nv = view->GetNativeView();
      auto const offset = nv->GetBoundsInRootWindow();
      auto snapped_regions = mojo::Clone(regions);
      for (auto& snapped_region : snapped_regions) {
        snapped_region->bounds.Offset(offset.x(), offset.y());
      }

      draggable_regions_ = mojo::Clone(snapped_regions);
    }
  }

  static_cast<NativeWindowViews*>(window_.get())
      ->UpdateDraggableRegions(draggable_regions_);
}

}  // namespace api

}  // namespace electron
<|endoftext|>


<commit_before>//===- KernelOutlining.cpp - Implementation of GPU kernel outling ---------===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// This file implements the GPU dialect kernel outlining pass.
//
//===----------------------------------------------------------------------===//

#include "mlir/Dialect/GPU/GPUDialect.h"
#include "mlir/Dialect/GPU/Passes.h"
#include "mlir/Dialect/StandardOps/Ops.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/Pass/Pass.h"

using namespace mlir;

template <typename OpTy>
static void createForAllDimensions(OpBuilder &builder, Location loc,
                                   SmallVectorImpl<Value *> &values) {
  for (StringRef dim : {"x", "y", "z"}) {
    Value *v = builder.create<OpTy>(loc, builder.getIndexType(),
                                    builder.getStringAttr(dim));
    values.push_back(v);
  }
}

// Add operations generating block/thread ids and gird/block dimensions at the
// beginning of `kernelFunc` and replace uses of the respective function args.
static void injectGpuIndexOperations(Location loc, FuncOp kernelFunc) {
  OpBuilder OpBuilder(kernelFunc.getBody());
  SmallVector<Value *, 12> indexOps;
  createForAllDimensions<gpu::BlockId>(OpBuilder, loc, indexOps);
  createForAllDimensions<gpu::ThreadId>(OpBuilder, loc, indexOps);
  createForAllDimensions<gpu::GridDim>(OpBuilder, loc, indexOps);
  createForAllDimensions<gpu::BlockDim>(OpBuilder, loc, indexOps);
  // Replace the leading 12 function args with the respective thread/block index
  // operations. Iterate backwards since args are erased and indices change.
  for (int i = 11; i >= 0; --i) {
    auto &firstBlock = kernelFunc.front();
    firstBlock.getArgument(i)->replaceAllUsesWith(indexOps[i]);
    firstBlock.eraseArgument(i);
  }
}

// Move all constant arguments of the given kernel function into the function,
// thereby reducing the number of kernel arguments.
static gpu::LaunchFuncOp inlineConstants(FuncOp kernelFunc,
                                         gpu::LaunchFuncOp launch) {
  OpBuilder kernelBuilder(kernelFunc.getBody());
  auto &firstBlock = kernelFunc.getBody().front();
  llvm::SmallVector<Value *, 8> newLaunchArgs;
  for (int i = launch.getNumKernelOperands() - 1; i >= 0; --i) {
    auto operandOp = launch.getKernelOperand(i)->getDefiningOp();
    auto constant = dyn_cast_or_null<ConstantOp>(operandOp);
    if (!constant) {
      newLaunchArgs.push_back(launch.getKernelOperand(i));
      continue;
    }
    auto newConstant = kernelBuilder.clone(*operandOp);
    firstBlock.getArgument(i)->replaceAllUsesWith(newConstant->getResult(0));
    firstBlock.eraseArgument(i);
  }
  if (newLaunchArgs.size() != launch.getNumKernelOperands()) {
    std::reverse(newLaunchArgs.begin(), newLaunchArgs.end());
    OpBuilder LaunchBuilder(launch);
    SmallVector<Type, 8> newArgumentTypes;
    newArgumentTypes.reserve(firstBlock.getNumArguments());
    for (auto value : firstBlock.getArguments()) {
      newArgumentTypes.push_back(value->getType());
    }
    kernelFunc.setType(LaunchBuilder.getFunctionType(newArgumentTypes, {}));
    auto newLaunch = LaunchBuilder.create<gpu::LaunchFuncOp>(
        launch.getLoc(), kernelFunc, launch.getGridSizeOperandValues(),
        launch.getBlockSizeOperandValues(), newLaunchArgs);
    launch.erase();
    return newLaunch;
  }
  return launch;
}

// Outline the `gpu.launch` operation body into a kernel function. Replace
// `gpu.return` operations by `std.return` in the generated functions.
static FuncOp outlineKernelFunc(gpu::LaunchOp launchOp) {
  Location loc = launchOp.getLoc();
  SmallVector<Type, 4> kernelOperandTypes(launchOp.getKernelOperandTypes());
  FunctionType type =
      FunctionType::get(kernelOperandTypes, {}, launchOp.getContext());
  std::string kernelFuncName =
      Twine(launchOp.getParentOfType<FuncOp>().getName(), "_kernel").str();
  FuncOp outlinedFunc = FuncOp::create(loc, kernelFuncName, type);
  outlinedFunc.getBody().takeBody(launchOp.getBody());
  Builder builder(launchOp.getContext());
  outlinedFunc.setAttr(gpu::GPUDialect::getKernelFuncAttrName(),
                       builder.getUnitAttr());
  injectGpuIndexOperations(loc, outlinedFunc);
  outlinedFunc.walk([](mlir::gpu::Return op) {
    OpBuilder replacer(op);
    replacer.create<ReturnOp>(op.getLoc());
    op.erase();
  });
  return outlinedFunc;
}

// Replace `gpu.launch` operations with an `gpu.launch_func` operation launching
// `kernelFunc`. The kernel func contains the body of the `gpu.launch` with
// constant region arguments inlined.
static void convertToLaunchFuncOp(gpu::LaunchOp &launchOp, FuncOp kernelFunc) {
  OpBuilder builder(launchOp);
  SmallVector<Value *, 4> kernelOperandValues(
      launchOp.getKernelOperandValues());
  auto launchFuncOp = builder.create<gpu::LaunchFuncOp>(
      launchOp.getLoc(), kernelFunc, launchOp.getGridSizeOperandValues(),
      launchOp.getBlockSizeOperandValues(), kernelOperandValues);
  inlineConstants(kernelFunc, launchFuncOp);
  launchOp.erase();
}

namespace {

class GpuKernelOutliningPass : public ModulePass<GpuKernelOutliningPass> {
public:
  void runOnModule() override {
    ModuleManager moduleManager(getModule());
    for (auto func : getModule().getOps<FuncOp>()) {
      func.walk([&](mlir::gpu::LaunchOp op) {
        FuncOp outlinedFunc = outlineKernelFunc(op);
        moduleManager.insert(outlinedFunc);
        convertToLaunchFuncOp(op, outlinedFunc);
      });
    }
  }
};

} // namespace

std::unique_ptr<ModulePassBase> mlir::createGpuKernelOutliningPass() {
  return std::make_unique<GpuKernelOutliningPass>();
}

static PassRegistration<GpuKernelOutliningPass>
    pass("gpu-kernel-outlining",
         "Outline gpu.launch bodies to kernel functions.");
<commit_msg>Addressing some late review comments on kernel inlining.<commit_after>//===- KernelOutlining.cpp - Implementation of GPU kernel outling ---------===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// This file implements the GPU dialect kernel outlining pass.
//
//===----------------------------------------------------------------------===//

#include "mlir/Dialect/GPU/GPUDialect.h"
#include "mlir/Dialect/GPU/Passes.h"
#include "mlir/Dialect/StandardOps/Ops.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/Pass/Pass.h"

using namespace mlir;

template <typename OpTy>
static void createForAllDimensions(OpBuilder &builder, Location loc,
                                   SmallVectorImpl<Value *> &values) {
  for (StringRef dim : {"x", "y", "z"}) {
    Value *v = builder.create<OpTy>(loc, builder.getIndexType(),
                                    builder.getStringAttr(dim));
    values.push_back(v);
  }
}

// Add operations generating block/thread ids and gird/block dimensions at the
// beginning of `kernelFunc` and replace uses of the respective function args.
static void injectGpuIndexOperations(Location loc, FuncOp kernelFunc) {
  OpBuilder OpBuilder(kernelFunc.getBody());
  SmallVector<Value *, 12> indexOps;
  createForAllDimensions<gpu::BlockId>(OpBuilder, loc, indexOps);
  createForAllDimensions<gpu::ThreadId>(OpBuilder, loc, indexOps);
  createForAllDimensions<gpu::GridDim>(OpBuilder, loc, indexOps);
  createForAllDimensions<gpu::BlockDim>(OpBuilder, loc, indexOps);
  // Replace the leading 12 function args with the respective thread/block index
  // operations. Iterate backwards since args are erased and indices change.
  for (int i = 11; i >= 0; --i) {
    auto &firstBlock = kernelFunc.front();
    firstBlock.getArgument(i)->replaceAllUsesWith(indexOps[i]);
    firstBlock.eraseArgument(i);
  }
}

// Move all constant arguments of the given kernel function into the function,
// thereby reducing the number of kernel arguments.
static gpu::LaunchFuncOp inlineConstants(FuncOp kernelFunc,
                                         gpu::LaunchFuncOp launch) {
  OpBuilder kernelBuilder(kernelFunc.getBody());
  auto &firstBlock = kernelFunc.getBody().front();
  llvm::SmallVector<Value *, 8> newLaunchArgs;
  for (int i = launch.getNumKernelOperands() - 1; i >= 0; --i) {
    auto operandOp = launch.getKernelOperand(i)->getDefiningOp();
    auto constant = dyn_cast_or_null<ConstantOp>(operandOp);
    if (!constant) {
      newLaunchArgs.push_back(launch.getKernelOperand(i));
      continue;
    }
    auto newConstant = kernelBuilder.clone(*operandOp);
    firstBlock.getArgument(i)->replaceAllUsesWith(newConstant->getResult(0));
    firstBlock.eraseArgument(i);
  }
  if (newLaunchArgs.size() == launch.getNumKernelOperands())
    return launch;

  std::reverse(newLaunchArgs.begin(), newLaunchArgs.end());
  OpBuilder LaunchBuilder(launch);
  SmallVector<Type, 8> newArgumentTypes;
  newArgumentTypes.reserve(firstBlock.getNumArguments());
  for (auto value : firstBlock.getArguments()) {
    newArgumentTypes.push_back(value->getType());
  }
  kernelFunc.setType(LaunchBuilder.getFunctionType(newArgumentTypes, {}));
  auto newLaunch = LaunchBuilder.create<gpu::LaunchFuncOp>(
      launch.getLoc(), kernelFunc, launch.getGridSizeOperandValues(),
      launch.getBlockSizeOperandValues(), newLaunchArgs);
  launch.erase();
  return newLaunch;
}

// Outline the `gpu.launch` operation body into a kernel function. Replace
// `gpu.return` operations by `std.return` in the generated functions.
static FuncOp outlineKernelFunc(gpu::LaunchOp launchOp) {
  Location loc = launchOp.getLoc();
  SmallVector<Type, 4> kernelOperandTypes(launchOp.getKernelOperandTypes());
  FunctionType type =
      FunctionType::get(kernelOperandTypes, {}, launchOp.getContext());
  std::string kernelFuncName =
      Twine(launchOp.getParentOfType<FuncOp>().getName(), "_kernel").str();
  FuncOp outlinedFunc = FuncOp::create(loc, kernelFuncName, type);
  outlinedFunc.getBody().takeBody(launchOp.getBody());
  Builder builder(launchOp.getContext());
  outlinedFunc.setAttr(gpu::GPUDialect::getKernelFuncAttrName(),
                       builder.getUnitAttr());
  injectGpuIndexOperations(loc, outlinedFunc);
  outlinedFunc.walk([](mlir::gpu::Return op) {
    OpBuilder replacer(op);
    replacer.create<ReturnOp>(op.getLoc());
    op.erase();
  });
  return outlinedFunc;
}

// Replace `gpu.launch` operations with an `gpu.launch_func` operation launching
// `kernelFunc`. The kernel func contains the body of the `gpu.launch` with
// constant region arguments inlined.
static void convertToLaunchFuncOp(gpu::LaunchOp &launchOp, FuncOp kernelFunc) {
  OpBuilder builder(launchOp);
  SmallVector<Value *, 4> kernelOperandValues(
      launchOp.getKernelOperandValues());
  auto launchFuncOp = builder.create<gpu::LaunchFuncOp>(
      launchOp.getLoc(), kernelFunc, launchOp.getGridSizeOperandValues(),
      launchOp.getBlockSizeOperandValues(), kernelOperandValues);
  inlineConstants(kernelFunc, launchFuncOp);
  launchOp.erase();
}

namespace {

class GpuKernelOutliningPass : public ModulePass<GpuKernelOutliningPass> {
public:
  void runOnModule() override {
    ModuleManager moduleManager(getModule());
    for (auto func : getModule().getOps<FuncOp>()) {
      func.walk([&](mlir::gpu::LaunchOp op) {
        FuncOp outlinedFunc = outlineKernelFunc(op);
        moduleManager.insert(outlinedFunc);
        convertToLaunchFuncOp(op, outlinedFunc);
      });
    }
  }
};

} // namespace

std::unique_ptr<ModulePassBase> mlir::createGpuKernelOutliningPass() {
  return std::make_unique<GpuKernelOutliningPass>();
}

static PassRegistration<GpuKernelOutliningPass>
    pass("gpu-kernel-outlining",
         "Outline gpu.launch bodies to kernel functions.");
<|endoftext|>


<commit_before>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#include "common.h"
#include "eventpipebuffermanager.h"
#include "eventpipeeventinstance.h"
#include "sampleprofiler.h"
#include "hosting.h"
#include "threadsuspend.h"

#ifdef FEATURE_PERFTRACING

#ifndef FEATURE_PAL
#include <mmsystem.h>
#endif //FEATURE_PAL

const unsigned long NUM_NANOSECONDS_IN_1_MS = 1000000;

Volatile<BOOL> SampleProfiler::s_profilingEnabled = false;
Thread* SampleProfiler::s_pSamplingThread = NULL;
const WCHAR* SampleProfiler::s_providerName = W("Microsoft-DotNETCore-SampleProfiler");
EventPipeProvider* SampleProfiler::s_pEventPipeProvider = NULL;
EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL;
BYTE* SampleProfiler::s_pPayloadExternal = NULL;
BYTE* SampleProfiler::s_pPayloadManaged = NULL;
CLREventStatic SampleProfiler::s_threadShutdownEvent;
unsigned long SampleProfiler::s_samplingRateInNs = NUM_NANOSECONDS_IN_1_MS; // 1ms
bool SampleProfiler::s_timePeriodIsSet = FALSE;

#ifndef FEATURE_PAL
PVOID SampleProfiler::s_timeBeginPeriodFn = NULL;
PVOID SampleProfiler::s_timeEndPeriodFn = NULL;
HINSTANCE SampleProfiler::s_hMultimediaLib = NULL;

typedef MMRESULT (WINAPI *TimePeriodFnPtr) (UINT uPeriod);
#endif //FEATURE_PAL

void SampleProfiler::Enable(EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;
        PRECONDITION(s_pSamplingThread == NULL);
        // Synchronization of multiple callers occurs in EventPipe::Enable.
        PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
    }
    CONTRACTL_END;

    LoadDependencies();

    if(s_pEventPipeProvider == NULL)
    {
        s_pEventPipeProvider = EventPipe::CreateProvider(SL(s_providerName), nullptr, nullptr, pEventPipeProviderCallbackDataQueue);
        s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent(
            0, /* eventID */
            0, /* keywords */
            0, /* eventVersion */
            EventPipeEventLevel::Informational,
            false /* NeedStack */);
    }

    // Check to see if the sample profiler event is enabled.  If it is not, do not spin up the sampling thread.
    if(!s_pThreadTimeEvent->IsEnabled())
    {
        return;
    }

    if(s_pPayloadExternal == NULL)
    {
        s_pPayloadExternal = new BYTE[sizeof(unsigned int)];
        *((unsigned int *)s_pPayloadExternal) = static_cast<unsigned int>(SampleProfilerSampleType::External);

        s_pPayloadManaged = new BYTE[sizeof(unsigned int)];
        *((unsigned int *)s_pPayloadManaged) = static_cast<unsigned int>(SampleProfilerSampleType::Managed);
    }

    s_profilingEnabled = true;
    s_pSamplingThread = SetupUnstartedThread();
    if(s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL))
    {
        // Start the sampling thread.
        s_pSamplingThread->SetBackground(TRUE);
        s_pSamplingThread->StartThread();
    }
    else
    {
        _ASSERT(!"Unable to create sample profiler thread.");
    }

    s_threadShutdownEvent.CreateManualEvent(FALSE);

    SetTimeGranularity();
}

void SampleProfiler::Disable()
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;
        // Synchronization of multiple callers occurs in EventPipe::Disable.
        PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
    }
    CONTRACTL_END;

    // Bail early if profiling is not enabled.
    if(!s_profilingEnabled)
    {
        return;
    }

    // Reset the event before shutdown.
    s_threadShutdownEvent.Reset();

    // The sampling thread will watch this value and exit
    // when profiling is disabled.
    s_profilingEnabled = false;

    // Wait for the sampling thread to clean itself up.
    s_threadShutdownEvent.Wait(INFINITE, FALSE /* bAlertable */);
    s_threadShutdownEvent.CloseEvent();

    if(s_timePeriodIsSet)
    {
        ResetTimeGranularity();
    }
    UnloadDependencies();
}

void SampleProfiler::SetSamplingRate(unsigned long nanoseconds)
{
    LIMITED_METHOD_CONTRACT;

    // If the time period setting was modified by us,
    // make sure to change it back before changing our period
    // and losing track of what we set it to
    if(s_timePeriodIsSet)
    {
        ResetTimeGranularity();
    }

    s_samplingRateInNs = nanoseconds;

    if(!s_timePeriodIsSet)
    {
        SetTimeGranularity();
    }
}

DWORD WINAPI SampleProfiler::ThreadProc(void *args)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        PRECONDITION(s_pSamplingThread != NULL);
    }
    CONTRACTL_END;

    // Complete thread initialization and start the profiling loop.
    if(s_pSamplingThread->HasStarted())
    {
        // Switch to pre-emptive mode so that this thread doesn't starve the GC.
        GCX_PREEMP();

        while(s_profilingEnabled)
        {
            // Check to see if we can suspend managed execution.
            if(ThreadSuspend::SysIsSuspendInProgress() || (ThreadSuspend::GetSuspensionThread() != 0))
            {
                // Skip the current sample.
                PlatformSleep(s_samplingRateInNs);
                continue;
            }

            // Actually suspend managed execution.
            ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_OTHER);

            // Walk all managed threads and capture stacks.
            WalkManagedThreads();

            // Resume managed execution.
            ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceeded */);

            // Wait until it's time to sample again.
            PlatformSleep(s_samplingRateInNs);
        }
    }

    // Destroy the sampling thread when it is done running.
    DestroyThread(s_pSamplingThread);
    s_pSamplingThread = NULL;

    // Signal Disable() that the thread has been destroyed.
    s_threadShutdownEvent.Set();

    return S_OK;
}

// The thread store lock must already be held by the thread before this function
// is called.  ThreadSuspend::SuspendEE acquires the thread store lock.
void SampleProfiler::WalkManagedThreads()
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

    Thread *pTargetThread = NULL;

    // Iterate over all managed threads.
    // Assumes that the ThreadStoreLock is held because we've suspended all threads.
    while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL)
    {
        StackContents stackContents;

        // Walk the stack and write it out as an event.
        if(EventPipe::WalkManagedStackForThread(pTargetThread, stackContents) && !stackContents.IsEmpty())
        {
            // Set the payload.  If the GC mode on suspension > 0, then the thread was in cooperative mode.
            // Even though there are some cases where this is not managed code, we assume it is managed code here.
            // If the GC mode on suspension == 0 then the thread was in preemptive mode, which we qualify as external here.
            BYTE *pPayload = s_pPayloadExternal;
            if(pTargetThread->GetGCModeOnSuspension())
            {
                pPayload = s_pPayloadManaged;
            }

            // Write the sample.
            EventPipe::WriteSampleProfileEvent(s_pSamplingThread, s_pThreadTimeEvent, pTargetThread, stackContents, pPayload, c_payloadSize);
        }

        // Reset the GC mode.
        pTargetThread->ClearGCModeOnSuspension();
    }
}

void SampleProfiler::PlatformSleep(unsigned long nanoseconds)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifdef FEATURE_PAL
    PAL_nanosleep(nanoseconds);
#else //FEATURE_PAL
    ClrSleepEx(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS, FALSE);
#endif //FEATURE_PAL
}

void SampleProfiler::SetTimeGranularity()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    // Attempt to set the systems minimum timer period to the sampling rate
    // If the sampling rate is lower than the current system setting (16ms by default),
    // this will cause the OS to wake more often for scheduling descsion, allowing us to take samples
    // Note that is effects a system-wide setting and when set low will increase the amount of time
    // the OS is on-CPU, decreasing overall system performance and increasing power consumption
    if(s_timeBeginPeriodFn != NULL)
    {
        if(((TimePeriodFnPtr) s_timeBeginPeriodFn)(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS) == TIMERR_NOERROR)
        {
            s_timePeriodIsSet = TRUE;
        }
    }
#endif //FEATURE_PAL
}

void SampleProfiler::ResetTimeGranularity()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    // End the modifications we had to the timer period in Enable
    if(s_timeEndPeriodFn != NULL)
    {
        if(((TimePeriodFnPtr) s_timeEndPeriodFn)(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS) == TIMERR_NOERROR)
        {
            s_timePeriodIsSet = FALSE;
        }
    }
#endif //FEATURE_PAL
}

bool SampleProfiler::LoadDependencies()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    s_hMultimediaLib = WszLoadLibrary(W("winmm.dll"));

    if (s_hMultimediaLib != NULL)
    {
        s_timeBeginPeriodFn = (PVOID) GetProcAddress(s_hMultimediaLib, "timeBeginPeriod");
        s_timeEndPeriodFn = (PVOID) GetProcAddress(s_hMultimediaLib, "timeEndPeriod");
    }

    return s_hMultimediaLib != NULL && s_timeBeginPeriodFn != NULL && s_timeEndPeriodFn != NULL;
#else
    return FALSE;
#endif //FEATURE_PAL
}

void SampleProfiler::UnloadDependencies()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    if (s_hMultimediaLib != NULL)
    {
        FreeLibrary(s_hMultimediaLib);
        s_hMultimediaLib = NULL;
        s_timeBeginPeriodFn = NULL;
        s_timeEndPeriodFn = NULL;
    }
#endif //FEATURE_PAL
}

#endif // FEATURE_PERFTRACING
<commit_msg>Handle the case when the SamplingProfiler thread is terminated prematurely<commit_after>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#include "common.h"
#include "eventpipebuffermanager.h"
#include "eventpipeeventinstance.h"
#include "sampleprofiler.h"
#include "hosting.h"
#include "threadsuspend.h"

#ifdef FEATURE_PERFTRACING

#ifndef FEATURE_PAL
#include <mmsystem.h>
#endif //FEATURE_PAL

const unsigned long NUM_NANOSECONDS_IN_1_MS = 1000000;

Volatile<BOOL> SampleProfiler::s_profilingEnabled = false;
Thread* SampleProfiler::s_pSamplingThread = NULL;
const WCHAR* SampleProfiler::s_providerName = W("Microsoft-DotNETCore-SampleProfiler");
EventPipeProvider* SampleProfiler::s_pEventPipeProvider = NULL;
EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL;
BYTE* SampleProfiler::s_pPayloadExternal = NULL;
BYTE* SampleProfiler::s_pPayloadManaged = NULL;
CLREventStatic SampleProfiler::s_threadShutdownEvent;
unsigned long SampleProfiler::s_samplingRateInNs = NUM_NANOSECONDS_IN_1_MS; // 1ms
bool SampleProfiler::s_timePeriodIsSet = FALSE;

#ifndef FEATURE_PAL
PVOID SampleProfiler::s_timeBeginPeriodFn = NULL;
PVOID SampleProfiler::s_timeEndPeriodFn = NULL;
HINSTANCE SampleProfiler::s_hMultimediaLib = NULL;

typedef MMRESULT (WINAPI *TimePeriodFnPtr) (UINT uPeriod);
#endif //FEATURE_PAL

void SampleProfiler::Enable(EventPipeProviderCallbackDataQueue* pEventPipeProviderCallbackDataQueue)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;
        PRECONDITION(s_pSamplingThread == NULL);
        // Synchronization of multiple callers occurs in EventPipe::Enable.
        PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
    }
    CONTRACTL_END;

    LoadDependencies();

    if(s_pEventPipeProvider == NULL)
    {
        s_pEventPipeProvider = EventPipe::CreateProvider(SL(s_providerName), nullptr, nullptr, pEventPipeProviderCallbackDataQueue);
        s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent(
            0, /* eventID */
            0, /* keywords */
            0, /* eventVersion */
            EventPipeEventLevel::Informational,
            false /* NeedStack */);
    }

    // Check to see if the sample profiler event is enabled.  If it is not, do not spin up the sampling thread.
    if(!s_pThreadTimeEvent->IsEnabled())
    {
        return;
    }

    if(s_pPayloadExternal == NULL)
    {
        s_pPayloadExternal = new BYTE[sizeof(unsigned int)];
        *((unsigned int *)s_pPayloadExternal) = static_cast<unsigned int>(SampleProfilerSampleType::External);

        s_pPayloadManaged = new BYTE[sizeof(unsigned int)];
        *((unsigned int *)s_pPayloadManaged) = static_cast<unsigned int>(SampleProfilerSampleType::Managed);
    }

    s_profilingEnabled = true;
    s_pSamplingThread = SetupUnstartedThread();
    if(s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL))
    {
        // Start the sampling thread.
        s_pSamplingThread->SetBackground(TRUE);
        s_pSamplingThread->StartThread();
    }
    else
    {
        _ASSERT(!"Unable to create sample profiler thread.");
    }

    s_threadShutdownEvent.CreateManualEvent(FALSE);

    SetTimeGranularity();
}

void SampleProfiler::Disable()
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;
        // Synchronization of multiple callers occurs in EventPipe::Disable.
        PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
    }
    CONTRACTL_END;

    // Bail early if profiling is not enabled.
    if(!s_profilingEnabled)
    {
        return;
    }

    // If g_fProcessDetach is true, the sampling profiler thread probably got
    // ripped because someone called ExitProcess(). This check is an attempt
    // to recognize that case and avoid waiting for the (already dead) sampling
    // profiler thread to tell us it is terminated.
    if (!g_fProcessDetach)
    {
        // Reset the event before shutdown.
        s_threadShutdownEvent.Reset();

        // The sampling thread will watch this value and exit
        // when profiling is disabled.
        s_profilingEnabled = false;

        // Wait for the sampling thread to clean itself up.
        s_threadShutdownEvent.Wait(INFINITE, FALSE /* bAlertable */);
        s_threadShutdownEvent.CloseEvent();
    }

    if(s_timePeriodIsSet)
    {
        ResetTimeGranularity();
    }
    UnloadDependencies();
}

void SampleProfiler::SetSamplingRate(unsigned long nanoseconds)
{
    LIMITED_METHOD_CONTRACT;

    // If the time period setting was modified by us,
    // make sure to change it back before changing our period
    // and losing track of what we set it to
    if(s_timePeriodIsSet)
    {
        ResetTimeGranularity();
    }

    s_samplingRateInNs = nanoseconds;

    if(!s_timePeriodIsSet)
    {
        SetTimeGranularity();
    }
}

DWORD WINAPI SampleProfiler::ThreadProc(void *args)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        PRECONDITION(s_pSamplingThread != NULL);
    }
    CONTRACTL_END;

    // Complete thread initialization and start the profiling loop.
    if(s_pSamplingThread->HasStarted())
    {
        // Switch to pre-emptive mode so that this thread doesn't starve the GC.
        GCX_PREEMP();

        while(s_profilingEnabled)
        {
            // Check to see if we can suspend managed execution.
            if(ThreadSuspend::SysIsSuspendInProgress() || (ThreadSuspend::GetSuspensionThread() != 0))
            {
                // Skip the current sample.
                PlatformSleep(s_samplingRateInNs);
                continue;
            }

            // Actually suspend managed execution.
            ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_OTHER);

            // Walk all managed threads and capture stacks.
            WalkManagedThreads();

            // Resume managed execution.
            ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceeded */);

            // Wait until it's time to sample again.
            PlatformSleep(s_samplingRateInNs);
        }
    }

    // Destroy the sampling thread when it is done running.
    DestroyThread(s_pSamplingThread);
    s_pSamplingThread = NULL;

    // Signal Disable() that the thread has been destroyed.
    s_threadShutdownEvent.Set();

    return S_OK;
}

// The thread store lock must already be held by the thread before this function
// is called.  ThreadSuspend::SuspendEE acquires the thread store lock.
void SampleProfiler::WalkManagedThreads()
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
    }
    CONTRACTL_END;

    Thread *pTargetThread = NULL;

    // Iterate over all managed threads.
    // Assumes that the ThreadStoreLock is held because we've suspended all threads.
    while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL)
    {
        StackContents stackContents;

        // Walk the stack and write it out as an event.
        if(EventPipe::WalkManagedStackForThread(pTargetThread, stackContents) && !stackContents.IsEmpty())
        {
            // Set the payload.  If the GC mode on suspension > 0, then the thread was in cooperative mode.
            // Even though there are some cases where this is not managed code, we assume it is managed code here.
            // If the GC mode on suspension == 0 then the thread was in preemptive mode, which we qualify as external here.
            BYTE *pPayload = s_pPayloadExternal;
            if(pTargetThread->GetGCModeOnSuspension())
            {
                pPayload = s_pPayloadManaged;
            }

            // Write the sample.
            EventPipe::WriteSampleProfileEvent(s_pSamplingThread, s_pThreadTimeEvent, pTargetThread, stackContents, pPayload, c_payloadSize);
        }

        // Reset the GC mode.
        pTargetThread->ClearGCModeOnSuspension();
    }
}

void SampleProfiler::PlatformSleep(unsigned long nanoseconds)
{
    CONTRACTL
    {
        NOTHROW;
        GC_TRIGGERS;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifdef FEATURE_PAL
    PAL_nanosleep(nanoseconds);
#else //FEATURE_PAL
    ClrSleepEx(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS, FALSE);
#endif //FEATURE_PAL
}

void SampleProfiler::SetTimeGranularity()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    // Attempt to set the systems minimum timer period to the sampling rate
    // If the sampling rate is lower than the current system setting (16ms by default),
    // this will cause the OS to wake more often for scheduling descsion, allowing us to take samples
    // Note that is effects a system-wide setting and when set low will increase the amount of time
    // the OS is on-CPU, decreasing overall system performance and increasing power consumption
    if(s_timeBeginPeriodFn != NULL)
    {
        if(((TimePeriodFnPtr) s_timeBeginPeriodFn)(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS) == TIMERR_NOERROR)
        {
            s_timePeriodIsSet = TRUE;
        }
    }
#endif //FEATURE_PAL
}

void SampleProfiler::ResetTimeGranularity()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    // End the modifications we had to the timer period in Enable
    if(s_timeEndPeriodFn != NULL)
    {
        if(((TimePeriodFnPtr) s_timeEndPeriodFn)(s_samplingRateInNs / NUM_NANOSECONDS_IN_1_MS) == TIMERR_NOERROR)
        {
            s_timePeriodIsSet = FALSE;
        }
    }
#endif //FEATURE_PAL
}

bool SampleProfiler::LoadDependencies()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    s_hMultimediaLib = WszLoadLibrary(W("winmm.dll"));

    if (s_hMultimediaLib != NULL)
    {
        s_timeBeginPeriodFn = (PVOID) GetProcAddress(s_hMultimediaLib, "timeBeginPeriod");
        s_timeEndPeriodFn = (PVOID) GetProcAddress(s_hMultimediaLib, "timeEndPeriod");
    }

    return s_hMultimediaLib != NULL && s_timeBeginPeriodFn != NULL && s_timeEndPeriodFn != NULL;
#else
    return FALSE;
#endif //FEATURE_PAL
}

void SampleProfiler::UnloadDependencies()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

#ifndef FEATURE_PAL
    if (s_hMultimediaLib != NULL)
    {
        FreeLibrary(s_hMultimediaLib);
        s_hMultimediaLib = NULL;
        s_timeBeginPeriodFn = NULL;
        s_timeEndPeriodFn = NULL;
    }
#endif //FEATURE_PAL
}

#endif // FEATURE_PERFTRACING
<|endoftext|>


<commit_before>/*
 *	dac≈
 *	Jamoma Multicore external object for Max
 *	Copyright © 2008 by Timothy Place
 *
 *	License: This code is licensed under the terms of the GNU LGPL
 *	http://www.gnu.org/licenses/lgpl.html
 */

#include "maxMulticore.h"

// Data Structure for this object
struct Dac {
    t_object				obj;
	TTMulticoreObjectPtr	multicoreObject;
};
typedef Dac* DacPtr;

// Prototypes for methods
DacPtr	DacNew(SymbolPtr msg, AtomCount argc, AtomPtr argv);
void	DacFree(DacPtr self);
void	DacAssist(DacPtr self, void* b, long msg, long arg, char* dst);
TTErr	DacReset(DacPtr self);
TTErr	DacConnect(DacPtr self, TTMulticoreObjectPtr audioSourceObject, long sourceOutletNumber);
TTErr	DacStart(DacPtr self);
TTErr	DacStop(DacPtr self);
// Prototypes for attribute accessors
MaxErr	DacSetSampleRate(DacPtr self, void* attr, AtomCount argc, AtomPtr argv);
MaxErr	DacGetSampleRate(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv);
MaxErr	DacSetVectorSize(DacPtr self, void* attr, AtomCount argc, AtomPtr argv);
MaxErr	DacGetVectorSize(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv);

// Globals
static ClassPtr sDacClass;

/************************************************************************************/
// Main() Function

int main(void)
{
	ClassPtr c;

	TTMulticoreInit();
	common_symbols_init();

	c = class_new("jcom.dac≈", (method)DacNew, (method)DacFree, sizeof(Dac), (method)0L, A_GIMME, 0);

	class_addmethod(c, (method)DacStart,		"start",		0);
	class_addmethod(c, (method)DacStop,		"stop",			0);
	//class_addmethod(c, (method)DacNotify,		"notify",		A_CANT, 0);
	class_addmethod(c, (method)DacReset,		"multicore.reset",	A_CANT, 0);
	class_addmethod(c, (method)DacConnect,		"multicore.connect",	A_OBJ, A_LONG, 0);
	class_addmethod(c, (method)DacAssist,		"assist",		A_CANT, 0);
	class_addmethod(c, (method)object_obex_dumpout,	"dumpout",		A_CANT, 0);

	CLASS_ATTR_LONG(c,		"sampleRate",	0,		Dac,	obj);
	CLASS_ATTR_ACCESSORS(c,	"sampleRate",	DacGetSampleRate,	DacSetSampleRate);

	CLASS_ATTR_LONG(c,		"vectorSize",	0,		Dac,	obj);
	CLASS_ATTR_ACCESSORS(c,	"vectorSize",	DacGetVectorSize,	DacSetVectorSize);

	class_register(_sym_box, c);
	sDacClass = c;
	return 0;
}

/************************************************************************************/
// Life Cycle

DacPtr DacNew(SymbolPtr msg, AtomCount argc, AtomPtr argv)
{
    DacPtr		self = DacPtr(object_alloc(sDacClass));
	TTValue		v;
	TTErr		err;

    if (self) {
		v.setSize(2);
		v.set(0, TT("multicore.output"));
		v.set(1, 2);
		err = TTObjectInstantiate(TT("multicore.object"), (TTObjectPtr*)&self->multicoreObject, v);

		v = TTPtr(self->multicoreObject);
		//self->multicoreObject->mUnitGenerator->sendMessage(TT("setOwner"), v);

		attr_args_process(self, argc, argv);
		object_obex_store((void*)self, _sym_dumpout, (object*)outlet_new(self, NULL));
	}
	return self;
}

void DacFree(DacPtr self)
{
	TTObjectRelease((TTObjectPtr*)&self->multicoreObject);
}

/************************************************************************************/
// Methods bound to input/inlets

// Method for Assistance Messages
void DacAssist(DacPtr self, void* b, long msg, long arg, char* dst)
{
	if (msg==1)				// Inlets
		strcpy(dst, "multichannel audio connection and control messages");
	else if (msg==2)		// Outlets
		strcpy(dst, "dumpout");
}

TTErr DacReset(DacPtr self)
{
	return self->multicoreObject->reset();
}

TTErr DacConnect(DacPtr self, TTMulticoreObjectPtr audioSourceObject, long sourceOutletNumber)
{
	return self->multicoreObject->connect(audioSourceObject, sourceOutletNumber);
}

TTErr DacStart(DacPtr self)
{
	MaxErr		err;
	ObjectPtr	patcher = NULL;
	ObjectPtr	box = NULL;
	ObjectPtr	o = NULL;
	method		multicoreSetupMethod = NULL;
	long		vectorSize;

	self->multicoreObject->mUnitGenerator->getAttributeValue(TT("vectorSize"), vectorSize);

	err = object_obex_lookup(self, gensym("#P"), &patcher);
	box = jpatcher_get_firstobject(patcher);
	while (box) {
		o = jbox_get_object(box);
		multicoreSetupMethod = zgetfn(o, gensym("multicore.reset"));
		if (multicoreSetupMethod)
			err = (MaxErr)multicoreSetupMethod(o, vectorSize);
		box = jbox_get_nextobject(box);
	}
	box = jpatcher_get_firstobject(patcher);
	while (box) {
		o = jbox_get_object(box);
		multicoreSetupMethod = zgetfn(o, gensym("multicore.setup"));
		if (multicoreSetupMethod)
			err = (MaxErr)multicoreSetupMethod(o);
		box = jbox_get_nextobject(box);
	}

	TTMulticoreInitData	initData;
	initData.vectorSize = vectorSize;
	self->multicoreObject->init(initData);

	return self->multicoreObject->mUnitGenerator->sendMessage(TT("start"));
}

TTErr DacStop(DacPtr self)
{
	return self->multicoreObject->mUnitGenerator->sendMessage(TT("stop"));
}

MaxErr DacSetSampleRate(DacPtr self, void* attr, AtomCount argc, AtomPtr argv)
{
	if (argc) {
		TTUInt32 sr = atom_getlong(argv);
		self->multicoreObject->mUnitGenerator->setAttributeValue(TT("sampleRate"), sr);
	}
	return MAX_ERR_NONE;
}

MaxErr DacGetSampleRate(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv)
{
	long sr;

	self->multicoreObject->mUnitGenerator->getAttributeValue(TT("sampleRate"), sr);

	*argc = 1;
	if (!(*argv)) // otherwise use memory passed in
		*argv = (t_atom *)sysmem_newptr(sizeof(t_atom));
	atom_setlong(*argv, sr);
	return MAX_ERR_NONE;
}

MaxErr DacSetVectorSize(DacPtr self, void* attr, AtomCount argc, AtomPtr argv)
{
	if (argc) {
		TTUInt32 vs = atom_getlong(argv);
		self->multicoreObject->mUnitGenerator->setAttributeValue(TT("vectorSize"), vs);
	}
	return MAX_ERR_NONE;
}

MaxErr DacGetVectorSize(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv)
{
	long vs;

	self->multicoreObject->mUnitGenerator->getAttributeValue(TT("vectorSize"), vs);

	*argc = 1;
	if (!(*argv)) // otherwise use memory passed in
		*argv = (t_atom *)sysmem_newptr(sizeof(t_atom));
	atom_setlong(*argv, vs);
	return MAX_ERR_NONE;
}

<commit_msg>dac≈: now iterates through subpatchers when setting up the graph.<commit_after>/*
 *	dac≈
 *	Jamoma Multicore external object for Max
 *	Copyright © 2008 by Timothy Place
 *
 *	License: This code is licensed under the terms of the GNU LGPL
 *	http://www.gnu.org/licenses/lgpl.html
 */

#include "maxMulticore.h"

// Data Structure for this object
struct Dac {
    t_object				obj;
	TTMulticoreObjectPtr	multicoreObject;
};
typedef Dac* DacPtr;

// Prototypes for methods
DacPtr	DacNew(SymbolPtr msg, AtomCount argc, AtomPtr argv);
void	DacFree(DacPtr self);
void	DacAssist(DacPtr self, void* b, long msg, long arg, char* dst);
TTErr	DacReset(DacPtr self);
TTErr	DacConnect(DacPtr self, TTMulticoreObjectPtr audioSourceObject, long sourceOutletNumber);
TTErr	DacStart(DacPtr self);
TTErr	DacStop(DacPtr self);
// Prototypes for attribute accessors
MaxErr	DacSetSampleRate(DacPtr self, void* attr, AtomCount argc, AtomPtr argv);
MaxErr	DacGetSampleRate(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv);
MaxErr	DacSetVectorSize(DacPtr self, void* attr, AtomCount argc, AtomPtr argv);
MaxErr	DacGetVectorSize(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv);

// Globals
static ClassPtr sDacClass;

/************************************************************************************/
// Main() Function

int main(void)
{
	ClassPtr c;

	TTMulticoreInit();
	common_symbols_init();

	c = class_new("jcom.dac≈", (method)DacNew, (method)DacFree, sizeof(Dac), (method)0L, A_GIMME, 0);

	class_addmethod(c, (method)DacStart,		"start",		0);
	class_addmethod(c, (method)DacStop,		"stop",			0);
	//class_addmethod(c, (method)DacNotify,		"notify",		A_CANT, 0);
	class_addmethod(c, (method)DacReset,		"multicore.reset",	A_CANT, 0);
	class_addmethod(c, (method)DacConnect,		"multicore.connect",	A_OBJ, A_LONG, 0);
	class_addmethod(c, (method)DacAssist,		"assist",		A_CANT, 0);
	class_addmethod(c, (method)object_obex_dumpout,	"dumpout",		A_CANT, 0);

	CLASS_ATTR_LONG(c,		"sampleRate",	0,		Dac,	obj);
	CLASS_ATTR_ACCESSORS(c,	"sampleRate",	DacGetSampleRate,	DacSetSampleRate);

	CLASS_ATTR_LONG(c,		"vectorSize",	0,		Dac,	obj);
	CLASS_ATTR_ACCESSORS(c,	"vectorSize",	DacGetVectorSize,	DacSetVectorSize);

	class_register(_sym_box, c);
	sDacClass = c;
	return 0;
}

/************************************************************************************/
// Life Cycle

DacPtr DacNew(SymbolPtr msg, AtomCount argc, AtomPtr argv)
{
    DacPtr		self = DacPtr(object_alloc(sDacClass));
	TTValue		v;
	TTErr		err;

    if (self) {
		v.setSize(2);
		v.set(0, TT("multicore.output"));
		v.set(1, 2);
		err = TTObjectInstantiate(TT("multicore.object"), (TTObjectPtr*)&self->multicoreObject, v);

		v = TTPtr(self->multicoreObject);
		//self->multicoreObject->mUnitGenerator->sendMessage(TT("setOwner"), v);

		attr_args_process(self, argc, argv);
		object_obex_store((void*)self, _sym_dumpout, (object*)outlet_new(self, NULL));
	}
	return self;
}

void DacFree(DacPtr self)
{
	TTObjectRelease((TTObjectPtr*)&self->multicoreObject);
}

/************************************************************************************/
// Methods bound to input/inlets

// Method for Assistance Messages
void DacAssist(DacPtr self, void* b, long msg, long arg, char* dst)
{
	if (msg==1)				// Inlets
		strcpy(dst, "multichannel audio connection and control messages");
	else if (msg==2)		// Outlets
		strcpy(dst, "dumpout");
}

TTErr DacReset(DacPtr self)
{
	return self->multicoreObject->reset();
}

TTErr DacConnect(DacPtr self, TTMulticoreObjectPtr audioSourceObject, long sourceOutletNumber)
{
	return self->multicoreObject->connect(audioSourceObject, sourceOutletNumber);
}

void DacIterateResetCallback(DacPtr self, ObjectPtr obj)
{
	TTUInt32	vectorSize;
	method		multicoreResetMethod = zgetfn(obj, gensym("multicore.reset"));

	if (multicoreResetMethod) {
		self->multicoreObject->mUnitGenerator->getAttributeValue(TT("vectorSize"), vectorSize);
		multicoreResetMethod(obj, vectorSize);
	}
}

void DacIterateSetupCallback(DacPtr self, ObjectPtr obj)
{
	method multicoreSetupMethod = zgetfn(obj, gensym("multicore.setup"));

	if (multicoreSetupMethod)
		multicoreSetupMethod(obj);
}

TTErr DacStart(DacPtr self)
{
	MaxErr		err;
	ObjectPtr	patcher = NULL;
	long		vectorSize;
	long		result = 0;

	self->multicoreObject->mUnitGenerator->getAttributeValue(TT("vectorSize"), vectorSize);

	err = object_obex_lookup(self, gensym("#P"), &patcher);
	object_method(patcher, gensym("iterate"), (method)DacIterateResetCallback, self, PI_DEEP, &result);
	object_method(patcher, gensym("iterate"), (method)DacIterateSetupCallback, self, PI_DEEP, &result);

	TTMulticoreInitData	initData;
	initData.vectorSize = vectorSize;
	self->multicoreObject->init(initData);

	return self->multicoreObject->mUnitGenerator->sendMessage(TT("start"));
}

TTErr DacStop(DacPtr self)
{
	return self->multicoreObject->mUnitGenerator->sendMessage(TT("stop"));
}

MaxErr DacSetSampleRate(DacPtr self, void* attr, AtomCount argc, AtomPtr argv)
{
	if (argc) {
		TTUInt32 sr = atom_getlong(argv);
		self->multicoreObject->mUnitGenerator->setAttributeValue(TT("sampleRate"), sr);
	}
	return MAX_ERR_NONE;
}

MaxErr DacGetSampleRate(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv)
{
	long sr;

	self->multicoreObject->mUnitGenerator->getAttributeValue(TT("sampleRate"), sr);

	*argc = 1;
	if (!(*argv)) // otherwise use memory passed in
		*argv = (t_atom *)sysmem_newptr(sizeof(t_atom));
	atom_setlong(*argv, sr);
	return MAX_ERR_NONE;
}

MaxErr DacSetVectorSize(DacPtr self, void* attr, AtomCount argc, AtomPtr argv)
{
	if (argc) {
		TTUInt32 vs = atom_getlong(argv);
		self->multicoreObject->mUnitGenerator->setAttributeValue(TT("vectorSize"), vs);
	}
	return MAX_ERR_NONE;
}

MaxErr DacGetVectorSize(DacPtr self, void* attr, AtomCount* argc, AtomPtr* argv)
{
	long vs;

	self->multicoreObject->mUnitGenerator->getAttributeValue(TT("vectorSize"), vs);

	*argc = 1;
	if (!(*argv)) // otherwise use memory passed in
		*argv = (t_atom *)sysmem_newptr(sizeof(t_atom));
	atom_setlong(*argv, vs);
	return MAX_ERR_NONE;
}

<|endoftext|>