<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h"
#include "webrtc/voice_engine/test/auto_test/voe_standard_test.h"

class TestRtpObserver : public webrtc::VoERTPObserver {
 public:
  TestRtpObserver();
  virtual ~TestRtpObserver();
  virtual void OnIncomingCSRCChanged(int channel,
                                     unsigned int CSRC,
                                     bool added);
  virtual void OnIncomingSSRCChanged(int channel,
                                     unsigned int SSRC);
  void Reset();
 public:
  unsigned int ssrc_[2];
  unsigned int csrc_[2][2];  // Stores 2 CSRCs for each channel.
  bool added_[2][2];
  int size_[2];
};

TestRtpObserver::TestRtpObserver() {
  Reset();
}

TestRtpObserver::~TestRtpObserver() {
}

void TestRtpObserver::Reset() {
  for (int i = 0; i < 2; i++) {
    ssrc_[i] = 0;
    csrc_[i][0] = 0;
    csrc_[i][1] = 0;
    added_[i][0] = false;
    added_[i][1] = false;
    size_[i] = 0;
  }
}

void TestRtpObserver::OnIncomingCSRCChanged(int channel,
                                            unsigned int CSRC,
                                            bool added) {
  char msg[128];
  sprintf(msg, "=> OnIncomingCSRCChanged(channel=%d, CSRC=%u, added=%d)\n",
          channel, CSRC, added);
  TEST_LOG("%s", msg);

  if (channel > 1)
    return;  // Not enough memory.

  csrc_[channel][size_[channel]] = CSRC;
  added_[channel][size_[channel]] = added;

  size_[channel]++;
  if (size_[channel] == 2)
    size_[channel] = 0;
}

void TestRtpObserver::OnIncomingSSRCChanged(int channel,
                                            unsigned int SSRC) {
  char msg[128];
  sprintf(msg, "\n=> OnIncomingSSRCChanged(channel=%d, SSRC=%u)\n", channel,
          SSRC);
  TEST_LOG("%s", msg);

  ssrc_[channel] = SSRC;
}

class RtcpAppHandler : public webrtc::VoERTCPObserver {
 public:
  RtcpAppHandler() : length_in_bytes_(0), sub_type_(0), name_(0) {}
  void OnApplicationDataReceived(int channel,
                                 unsigned char sub_type,
                                 unsigned int name,
                                 const unsigned char* data,
                                 unsigned short length_in_bytes);
  void Reset();
  ~RtcpAppHandler() {}
  unsigned short length_in_bytes_;
  unsigned char data_[256];
  unsigned char sub_type_;
  unsigned int name_;
};

static const char* const RTCP_CNAME = "Whatever";

class RtpRtcpTest : public AfterStreamingFixture {
 protected:
  void SetUp() {
    // We need a second channel for this test, so set it up.
    second_channel_ = voe_base_->CreateChannel();
    EXPECT_GE(second_channel_, 0);

    transport_ = new LoopBackTransport(voe_network_);
    EXPECT_EQ(0, voe_network_->RegisterExternalTransport(second_channel_,
                                                         *transport_));

    EXPECT_EQ(0, voe_base_->StartReceive(second_channel_));
    EXPECT_EQ(0, voe_base_->StartPlayout(second_channel_));
    EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(second_channel_, 5678));
    EXPECT_EQ(0, voe_base_->StartSend(second_channel_));

    // We'll set up the RTCP CNAME and SSRC to something arbitrary here.
    voe_rtp_rtcp_->SetRTCP_CNAME(channel_, RTCP_CNAME);
  }

  void TearDown() {
    EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(second_channel_));
    voe_base_->DeleteChannel(second_channel_);
    delete transport_;
  }

  int second_channel_;
  LoopBackTransport* transport_;
};

void RtcpAppHandler::OnApplicationDataReceived(
    const int /*channel*/, unsigned char sub_type,
    unsigned int name, const unsigned char* data,
    unsigned short length_in_bytes) {
  length_in_bytes_ = length_in_bytes;
  memcpy(data_, &data[0], length_in_bytes);
  sub_type_ = sub_type;
  name_ = name;
}

void RtcpAppHandler::Reset() {
  length_in_bytes_ = 0;
  memset(data_, 0, sizeof(data_));
  sub_type_ = 0;
  name_ = 0;
}

TEST_F(RtpRtcpTest, RemoteRtcpCnameHasPropagatedToRemoteSide) {
  if (!FLAGS_include_timing_dependent_tests) {
    TEST_LOG("Skipping test - running in slow execution environment...\n");
    return;
  }

  // We need to sleep a bit here for the name to propagate. For instance,
  // 200 milliseconds is not enough, so we'll go with one second here.
  Sleep(1000);

  char char_buffer[256];
  voe_rtp_rtcp_->GetRemoteRTCP_CNAME(channel_, char_buffer);
  EXPECT_STREQ(RTCP_CNAME, char_buffer);
}

TEST_F(RtpRtcpTest, SSRCPropagatesCorrectly) {
  unsigned int local_ssrc = 1234;
  EXPECT_EQ(0, voe_base_->StopSend(channel_));
  EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, local_ssrc));
  EXPECT_EQ(0, voe_base_->StartSend(channel_));

  Sleep(1000);

  unsigned int ssrc;
  EXPECT_EQ(0, voe_rtp_rtcp_->GetLocalSSRC(channel_, ssrc));
  EXPECT_EQ(local_ssrc, ssrc);

  EXPECT_EQ(0, voe_rtp_rtcp_->GetRemoteSSRC(channel_, ssrc));
  EXPECT_EQ(local_ssrc, ssrc);
}

TEST_F(RtpRtcpTest, RtcpApplicationDefinedPacketsCanBeSentAndReceived) {
  RtcpAppHandler rtcp_app_handler;
  EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTCPObserver(
      channel_, rtcp_app_handler));

  // Send data aligned to 32 bytes.
  const char* data = "application-dependent data------";
  unsigned short data_length = strlen(data);
  unsigned int data_name = 0x41424344;  // 'ABCD' in ascii
  unsigned char data_subtype = 1;

  EXPECT_EQ(0, voe_rtp_rtcp_->SendApplicationDefinedRTCPPacket(
      channel_, data_subtype, data_name, data, data_length));

  // Ensure the RTP-RTCP process gets scheduled.
  Sleep(1000);

  // Ensure we received the data in the callback.
  ASSERT_EQ(data_length, rtcp_app_handler.length_in_bytes_);
  EXPECT_EQ(0, memcmp(data, rtcp_app_handler.data_, data_length));
  EXPECT_EQ(data_name, rtcp_app_handler.name_);
  EXPECT_EQ(data_subtype, rtcp_app_handler.sub_type_);

  EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTCPObserver(channel_));
}

TEST_F(RtpRtcpTest, DisabledRtcpObserverDoesNotReceiveData) {
  RtcpAppHandler rtcp_app_handler;
  EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTCPObserver(
      channel_, rtcp_app_handler));

  // Put observer in a known state before de-registering.
  rtcp_app_handler.Reset();

  EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTCPObserver(channel_));

  const char* data = "whatever";
  EXPECT_EQ(0, voe_rtp_rtcp_->SendApplicationDefinedRTCPPacket(
      channel_, 1, 0x41424344, data, strlen(data)));

  // Ensure the RTP-RTCP process gets scheduled.
  Sleep(1000);

  // Ensure we received no data.
  EXPECT_EQ(0u, rtcp_app_handler.name_);
  EXPECT_EQ(0u, rtcp_app_handler.sub_type_);
}

TEST_F(RtpRtcpTest, InsertExtraRTPPacketDealsWithInvalidArguments) {
  const char payload_data[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };

  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
      -1, 0, false, payload_data, 8)) <<
          "Should reject: invalid channel.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
      channel_, -1, false, payload_data, 8)) <<
          "Should reject: invalid payload type.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
      channel_, 128, false, payload_data, 8)) <<
          "Should reject: invalid payload type.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, 99, false, NULL, 8)) <<
            "Should reject: bad pointer.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, 99, false, payload_data, 1500 - 28 + 1)) <<
            "Should reject: invalid size.";
}

TEST_F(RtpRtcpTest, CanTransmitExtraRtpPacketsWithoutError) {
  const char payload_data[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };

  for (int i = 0; i < 128; ++i) {
    // Try both with and without the marker bit set
    EXPECT_EQ(0, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, i, false, payload_data, 8));
    EXPECT_EQ(0, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, i, true, payload_data, 8));
  }
}

// TODO(xians, phoglund): Re-enable when issue 372 is resolved.
TEST_F(RtpRtcpTest, DISABLED_CanCreateRtpDumpFilesWithoutError) {
  // Create two RTP dump files (3 seconds long). You can verify these after
  // the test using rtpplay or NetEqRTPplay if you like.
  std::string output_path = webrtc::test::OutputPath();
  std::string incoming_filename = output_path + "dump_in_3sec.rtp";
  std::string outgoing_filename = output_path + "dump_out_3sec.rtp";

  EXPECT_EQ(0, voe_rtp_rtcp_->StartRTPDump(
      channel_, incoming_filename.c_str(), webrtc::kRtpIncoming));
  EXPECT_EQ(0, voe_rtp_rtcp_->StartRTPDump(
      channel_, outgoing_filename.c_str(), webrtc::kRtpOutgoing));

  Sleep(3000);

  EXPECT_EQ(0, voe_rtp_rtcp_->StopRTPDump(channel_, webrtc::kRtpIncoming));
  EXPECT_EQ(0, voe_rtp_rtcp_->StopRTPDump(channel_, webrtc::kRtpOutgoing));
}

TEST_F(RtpRtcpTest, ObserverGetsNotifiedOnSsrcChange) {
  TestRtpObserver rtcp_observer;
  EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTPObserver(
      channel_, rtcp_observer));
  rtcp_observer.Reset();

  unsigned int new_ssrc = 7777;
  EXPECT_EQ(0, voe_base_->StopSend(channel_));
  EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, new_ssrc));
  EXPECT_EQ(0, voe_base_->StartSend(channel_));

  Sleep(500);

  // Verify we got the new SSRC.
  EXPECT_EQ(new_ssrc, rtcp_observer.ssrc_[0]);

  // Now try another SSRC.
  unsigned int newer_ssrc = 1717;
  EXPECT_EQ(0, voe_base_->StopSend(channel_));
  EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, newer_ssrc));
  EXPECT_EQ(0, voe_base_->StartSend(channel_));

  Sleep(500);

  EXPECT_EQ(newer_ssrc, rtcp_observer.ssrc_[0]);

  EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTPObserver(channel_));
}
<commit_msg>Disabled SsrcPropagatesCorrectly on Linux.<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h"
#include "webrtc/voice_engine/test/auto_test/voe_standard_test.h"

class TestRtpObserver : public webrtc::VoERTPObserver {
 public:
  TestRtpObserver();
  virtual ~TestRtpObserver();
  virtual void OnIncomingCSRCChanged(int channel,
                                     unsigned int CSRC,
                                     bool added);
  virtual void OnIncomingSSRCChanged(int channel,
                                     unsigned int SSRC);
  void Reset();
 public:
  unsigned int ssrc_[2];
  unsigned int csrc_[2][2];  // Stores 2 CSRCs for each channel.
  bool added_[2][2];
  int size_[2];
};

TestRtpObserver::TestRtpObserver() {
  Reset();
}

TestRtpObserver::~TestRtpObserver() {
}

void TestRtpObserver::Reset() {
  for (int i = 0; i < 2; i++) {
    ssrc_[i] = 0;
    csrc_[i][0] = 0;
    csrc_[i][1] = 0;
    added_[i][0] = false;
    added_[i][1] = false;
    size_[i] = 0;
  }
}

void TestRtpObserver::OnIncomingCSRCChanged(int channel,
                                            unsigned int CSRC,
                                            bool added) {
  char msg[128];
  sprintf(msg, "=> OnIncomingCSRCChanged(channel=%d, CSRC=%u, added=%d)\n",
          channel, CSRC, added);
  TEST_LOG("%s", msg);

  if (channel > 1)
    return;  // Not enough memory.

  csrc_[channel][size_[channel]] = CSRC;
  added_[channel][size_[channel]] = added;

  size_[channel]++;
  if (size_[channel] == 2)
    size_[channel] = 0;
}

void TestRtpObserver::OnIncomingSSRCChanged(int channel,
                                            unsigned int SSRC) {
  char msg[128];
  sprintf(msg, "\n=> OnIncomingSSRCChanged(channel=%d, SSRC=%u)\n", channel,
          SSRC);
  TEST_LOG("%s", msg);

  ssrc_[channel] = SSRC;
}

class RtcpAppHandler : public webrtc::VoERTCPObserver {
 public:
  RtcpAppHandler() : length_in_bytes_(0), sub_type_(0), name_(0) {}
  void OnApplicationDataReceived(int channel,
                                 unsigned char sub_type,
                                 unsigned int name,
                                 const unsigned char* data,
                                 unsigned short length_in_bytes);
  void Reset();
  ~RtcpAppHandler() {}
  unsigned short length_in_bytes_;
  unsigned char data_[256];
  unsigned char sub_type_;
  unsigned int name_;
};

static const char* const RTCP_CNAME = "Whatever";

class RtpRtcpTest : public AfterStreamingFixture {
 protected:
  void SetUp() {
    // We need a second channel for this test, so set it up.
    second_channel_ = voe_base_->CreateChannel();
    EXPECT_GE(second_channel_, 0);

    transport_ = new LoopBackTransport(voe_network_);
    EXPECT_EQ(0, voe_network_->RegisterExternalTransport(second_channel_,
                                                         *transport_));

    EXPECT_EQ(0, voe_base_->StartReceive(second_channel_));
    EXPECT_EQ(0, voe_base_->StartPlayout(second_channel_));
    EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(second_channel_, 5678));
    EXPECT_EQ(0, voe_base_->StartSend(second_channel_));

    // We'll set up the RTCP CNAME and SSRC to something arbitrary here.
    voe_rtp_rtcp_->SetRTCP_CNAME(channel_, RTCP_CNAME);
  }

  void TearDown() {
    EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(second_channel_));
    voe_base_->DeleteChannel(second_channel_);
    delete transport_;
  }

  int second_channel_;
  LoopBackTransport* transport_;
};

void RtcpAppHandler::OnApplicationDataReceived(
    const int /*channel*/, unsigned char sub_type,
    unsigned int name, const unsigned char* data,
    unsigned short length_in_bytes) {
  length_in_bytes_ = length_in_bytes;
  memcpy(data_, &data[0], length_in_bytes);
  sub_type_ = sub_type;
  name_ = name;
}

void RtcpAppHandler::Reset() {
  length_in_bytes_ = 0;
  memset(data_, 0, sizeof(data_));
  sub_type_ = 0;
  name_ = 0;
}

TEST_F(RtpRtcpTest, RemoteRtcpCnameHasPropagatedToRemoteSide) {
  if (!FLAGS_include_timing_dependent_tests) {
    TEST_LOG("Skipping test - running in slow execution environment...\n");
    return;
  }

  // We need to sleep a bit here for the name to propagate. For instance,
  // 200 milliseconds is not enough, so we'll go with one second here.
  Sleep(1000);

  char char_buffer[256];
  voe_rtp_rtcp_->GetRemoteRTCP_CNAME(channel_, char_buffer);
  EXPECT_STREQ(RTCP_CNAME, char_buffer);
}

// Flakily hangs on Linux. code.google.com/p/webrtc/issues/detail?id=2178.
TEST_F(RtpRtcpTest, DISABLED_ON_LINUX(SSRCPropagatesCorrectly)) {
  unsigned int local_ssrc = 1234;
  EXPECT_EQ(0, voe_base_->StopSend(channel_));
  EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, local_ssrc));
  EXPECT_EQ(0, voe_base_->StartSend(channel_));

  Sleep(1000);

  unsigned int ssrc;
  EXPECT_EQ(0, voe_rtp_rtcp_->GetLocalSSRC(channel_, ssrc));
  EXPECT_EQ(local_ssrc, ssrc);

  EXPECT_EQ(0, voe_rtp_rtcp_->GetRemoteSSRC(channel_, ssrc));
  EXPECT_EQ(local_ssrc, ssrc);
}

TEST_F(RtpRtcpTest, RtcpApplicationDefinedPacketsCanBeSentAndReceived) {
  RtcpAppHandler rtcp_app_handler;
  EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTCPObserver(
      channel_, rtcp_app_handler));

  // Send data aligned to 32 bytes.
  const char* data = "application-dependent data------";
  unsigned short data_length = strlen(data);
  unsigned int data_name = 0x41424344;  // 'ABCD' in ascii
  unsigned char data_subtype = 1;

  EXPECT_EQ(0, voe_rtp_rtcp_->SendApplicationDefinedRTCPPacket(
      channel_, data_subtype, data_name, data, data_length));

  // Ensure the RTP-RTCP process gets scheduled.
  Sleep(1000);

  // Ensure we received the data in the callback.
  ASSERT_EQ(data_length, rtcp_app_handler.length_in_bytes_);
  EXPECT_EQ(0, memcmp(data, rtcp_app_handler.data_, data_length));
  EXPECT_EQ(data_name, rtcp_app_handler.name_);
  EXPECT_EQ(data_subtype, rtcp_app_handler.sub_type_);

  EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTCPObserver(channel_));
}

TEST_F(RtpRtcpTest, DisabledRtcpObserverDoesNotReceiveData) {
  RtcpAppHandler rtcp_app_handler;
  EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTCPObserver(
      channel_, rtcp_app_handler));

  // Put observer in a known state before de-registering.
  rtcp_app_handler.Reset();

  EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTCPObserver(channel_));

  const char* data = "whatever";
  EXPECT_EQ(0, voe_rtp_rtcp_->SendApplicationDefinedRTCPPacket(
      channel_, 1, 0x41424344, data, strlen(data)));

  // Ensure the RTP-RTCP process gets scheduled.
  Sleep(1000);

  // Ensure we received no data.
  EXPECT_EQ(0u, rtcp_app_handler.name_);
  EXPECT_EQ(0u, rtcp_app_handler.sub_type_);
}

TEST_F(RtpRtcpTest, InsertExtraRTPPacketDealsWithInvalidArguments) {
  const char payload_data[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };

  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
      -1, 0, false, payload_data, 8)) <<
          "Should reject: invalid channel.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
      channel_, -1, false, payload_data, 8)) <<
          "Should reject: invalid payload type.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
      channel_, 128, false, payload_data, 8)) <<
          "Should reject: invalid payload type.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, 99, false, NULL, 8)) <<
            "Should reject: bad pointer.";
  EXPECT_EQ(-1, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, 99, false, payload_data, 1500 - 28 + 1)) <<
            "Should reject: invalid size.";
}

TEST_F(RtpRtcpTest, CanTransmitExtraRtpPacketsWithoutError) {
  const char payload_data[8] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };

  for (int i = 0; i < 128; ++i) {
    // Try both with and without the marker bit set
    EXPECT_EQ(0, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, i, false, payload_data, 8));
    EXPECT_EQ(0, voe_rtp_rtcp_->InsertExtraRTPPacket(
        channel_, i, true, payload_data, 8));
  }
}

// TODO(xians, phoglund): Re-enable when issue 372 is resolved.
TEST_F(RtpRtcpTest, DISABLED_CanCreateRtpDumpFilesWithoutError) {
  // Create two RTP dump files (3 seconds long). You can verify these after
  // the test using rtpplay or NetEqRTPplay if you like.
  std::string output_path = webrtc::test::OutputPath();
  std::string incoming_filename = output_path + "dump_in_3sec.rtp";
  std::string outgoing_filename = output_path + "dump_out_3sec.rtp";

  EXPECT_EQ(0, voe_rtp_rtcp_->StartRTPDump(
      channel_, incoming_filename.c_str(), webrtc::kRtpIncoming));
  EXPECT_EQ(0, voe_rtp_rtcp_->StartRTPDump(
      channel_, outgoing_filename.c_str(), webrtc::kRtpOutgoing));

  Sleep(3000);

  EXPECT_EQ(0, voe_rtp_rtcp_->StopRTPDump(channel_, webrtc::kRtpIncoming));
  EXPECT_EQ(0, voe_rtp_rtcp_->StopRTPDump(channel_, webrtc::kRtpOutgoing));
}

TEST_F(RtpRtcpTest, ObserverGetsNotifiedOnSsrcChange) {
  TestRtpObserver rtcp_observer;
  EXPECT_EQ(0, voe_rtp_rtcp_->RegisterRTPObserver(
      channel_, rtcp_observer));
  rtcp_observer.Reset();

  unsigned int new_ssrc = 7777;
  EXPECT_EQ(0, voe_base_->StopSend(channel_));
  EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, new_ssrc));
  EXPECT_EQ(0, voe_base_->StartSend(channel_));

  Sleep(500);

  // Verify we got the new SSRC.
  EXPECT_EQ(new_ssrc, rtcp_observer.ssrc_[0]);

  // Now try another SSRC.
  unsigned int newer_ssrc = 1717;
  EXPECT_EQ(0, voe_base_->StopSend(channel_));
  EXPECT_EQ(0, voe_rtp_rtcp_->SetLocalSSRC(channel_, newer_ssrc));
  EXPECT_EQ(0, voe_base_->StartSend(channel_));

  Sleep(500);

  EXPECT_EQ(newer_ssrc, rtcp_observer.ssrc_[0]);

  EXPECT_EQ(0, voe_rtp_rtcp_->DeRegisterRTPObserver(channel_));
}
<|endoftext|>


<commit_before>#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <bvs/bvs.h>
#include "BvsA.h"
#include <opencv2/core/core.hpp>
<<<<<<< HEAD
#include <iostream>
#include <dlfcn.h>
extern "C" {
void testtest()
{
LOG(1, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT");
}
}

void test2(){
void* dlib = dlopen(NULL, RTLD_NOW);
void* test = dlsym(dlib, "testtest");
if (test==NULL)
LOG(1, "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT");
}

static JavaVM *gJavaVM;
static jobject gInterfaceObject;
=======

static JavaVM *gJavaVM;
>>>>>>> b75d94cb38092f1fc527a9a1259af268a988afce
BVS::BVS* bvs;
jobject  javaObj;

void shutdownFunction()
{

	LOG(1,"daemon exit caused by bvs shutdown request!");

	//bvs->quit();

	alarm(1); // SIGALRM after 1 second so all framework/module threads have a chance to quit

}

cv::Mat* getNativeAddress()
{
	return javaMat;
}

void drawToAndroid()
{
/*
	if(!javaMat)
	{
		LOG(0,"ExampleCV no javaMat ADDR");
		javaMat=getNativeAddress();

	}
	*javaMat=img;

	*/
	//	LOG(0,"JNI drawToAndroid");

		int status;
		JNIEnv *env;
		bool isAttached =false;

		status = gJavaVM->GetEnv((void **) &env, JNI_VERSION_1_6);
	//	LOG(0,"STATUS: " << status);

		if(status < 0)
		{
	//		LOG(1, "callback_handler: failed to get JNI environment, assuming native thread");
			status = gJavaVM->AttachCurrentThread(&env, NULL);
			if(status <0)
			{
	//			LOG(1, "callback_handler: failed to attach current thread");
				return;
			}
		}

		jclass javaClass = env-> GetObjectClass(javaObj);
		if (javaClass == 0)
		{
			LOG(0,"FindClass error");
			return;
		}

		//LOG(0,"Setting methodID");
		//Method takes a cv::Mat and returns nothing, void
		//jmethodID javaMethodID= env->GetMethodID(javaClass, "drawToDisplay", "(Lorg/opencv/core/Mat;)V");
		//jmethodID javaMethodID= env->GetMethodID(javaClass, "drawToDisplay", "([Ljava/lang/Integer;)V");
		jmethodID javaMethodID= env->GetMethodID(javaClass, "drawToDisplay", "()V");
		if (javaMethodID == 0)
		{
			LOG(0,"GetMethodID error");
			return;
		}
//	LOG(0,"methodID set");
		isAttached=true;

		env->CallVoidMethod(javaObj, javaMethodID);

		if(isAttached)
		{
			gJavaVM->DetachCurrentThread();
		}

}

extern "C"
{

	jint JNI_OnLoad(JavaVM* vm, void*)
	{
		test2();
		gJavaVM=vm;
		JNIEnv *env;
		if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK)
			return -1;

		LOG(0,"JNI INIT");

		return JNI_VERSION_1_6;
	}

	JNIEXPORT void JNICALL Java_com_cvhci_bvsa_MainActivity_bvsAndroid(JNIEnv *env,	jobject  obj, jstring configFilePath,  jlong addrMat)
	{
		LOGD("Java_com_cvhci_bvsa_MainActivity_bvsAndroid enter");
		javaMat = (cv::Mat*)addrMat;
		LOG(1,"JNI bvsAndroid Mat Adress " << javaMat);
		javaObj = env->NewGlobalRef(obj);

		const char* configStrChars = env->GetStringUTFChars(configFilePath, (jboolean *)0);

		LOGD(configStrChars);

//		char* bvsconfig[] ={"v",const_cast<char*>(configStrChars)};
		const char* bvsconfig[] ={"v",configStrChars};

		bvs = new BVS::BVS(2,bvsconfig);
		bvs->loadModules();
		LOGD("connecting modules!");
		bvs->connectAllModules();
		LOG(2, "starting!");
		bvs->start();

		bvs->run();

	}

}

<commit_msg>android: merged BvsA.cpp<commit_after>#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <bvs/bvs.h>
#include "BvsA.h"
#include <opencv2/core/core.hpp>
static JavaVM *gJavaVM;
BVS::BVS* bvs;
jobject  javaObj;

void shutdownFunction()
{

	LOG(1,"daemon exit caused by bvs shutdown request!");

	//bvs->quit();

	alarm(1); // SIGALRM after 1 second so all framework/module threads have a chance to quit

}

cv::Mat* getNativeAddress()
{
	return javaMat;
}

void drawToAndroid()
{
/*
	if(!javaMat)
	{
		LOG(0,"ExampleCV no javaMat ADDR");
		javaMat=getNativeAddress();

	}
	*javaMat=img;

	*/
	//	LOG(0,"JNI drawToAndroid");

		int status;
		JNIEnv *env;
		bool isAttached =false;

		status = gJavaVM->GetEnv((void **) &env, JNI_VERSION_1_6);
	//	LOG(0,"STATUS: " << status);

		if(status < 0)
		{
	//		LOG(1, "callback_handler: failed to get JNI environment, assuming native thread");
			status = gJavaVM->AttachCurrentThread(&env, NULL);
			if(status <0)
			{
	//			LOG(1, "callback_handler: failed to attach current thread");
				return;
			}
		}

		jclass javaClass = env-> GetObjectClass(javaObj);
		if (javaClass == 0)
		{
			LOG(0,"FindClass error");
			return;
		}

		//LOG(0,"Setting methodID");
		//Method takes a cv::Mat and returns nothing, void
		//jmethodID javaMethodID= env->GetMethodID(javaClass, "drawToDisplay", "(Lorg/opencv/core/Mat;)V");
		//jmethodID javaMethodID= env->GetMethodID(javaClass, "drawToDisplay", "([Ljava/lang/Integer;)V");
		jmethodID javaMethodID= env->GetMethodID(javaClass, "drawToDisplay", "()V");
		if (javaMethodID == 0)
		{
			LOG(0,"GetMethodID error");
			return;
		}
//	LOG(0,"methodID set");
		isAttached=true;

		env->CallVoidMethod(javaObj, javaMethodID);

		if(isAttached)
		{
			gJavaVM->DetachCurrentThread();
		}

}

extern "C"
{

	jint JNI_OnLoad(JavaVM* vm, void*)
	{
		gJavaVM=vm;
		JNIEnv *env;
		if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK)
			return -1;

		LOG(0,"JNI INIT");

		return JNI_VERSION_1_6;
	}

	JNIEXPORT void JNICALL Java_com_cvhci_bvsa_MainActivity_bvsAndroid(JNIEnv *env,	jobject  obj, jstring configFilePath,  jlong addrMat)
	{
		LOGD("Java_com_cvhci_bvsa_MainActivity_bvsAndroid enter");
		javaMat = (cv::Mat*)addrMat;
		LOG(1,"JNI bvsAndroid Mat Adress " << javaMat);
		javaObj = env->NewGlobalRef(obj);

		const char* configStrChars = env->GetStringUTFChars(configFilePath, (jboolean *)0);

		LOGD(configStrChars);

//		char* bvsconfig[] ={"v",const_cast<char*>(configStrChars)};
		const char* bvsconfig[] ={"v",configStrChars};

		bvs = new BVS::BVS(2,bvsconfig);
		bvs->loadModules();
		LOGD("connecting modules!");
		bvs->connectAllModules();
		LOG(2, "starting!");
		bvs->start();

		bvs->run();

	}

}

<|endoftext|>


<commit_before>//===----- X86WinAllocaExpander.cpp - Expand WinAlloca pseudo instruction -===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a pass that expands WinAlloca pseudo-instructions.
//
// It performs a conservative analysis to determine whether each allocation
// falls within a region of the stack that is safe to use, or whether stack
// probes must be emitted.
//
//===----------------------------------------------------------------------===//

#include "X86.h"
#include "X86InstrBuilder.h"
#include "X86InstrInfo.h"
#include "X86MachineFunctionInfo.h"
#include "X86Subtarget.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {

class X86WinAllocaExpander : public MachineFunctionPass {
public:
  X86WinAllocaExpander() : MachineFunctionPass(ID) {}

  bool runOnMachineFunction(MachineFunction &MF) override;

private:
  /// Strategies for lowering a WinAlloca.
  enum Lowering { TouchAndSub, Sub, Probe };

  /// Deterministic-order map from WinAlloca instruction to desired lowering.
  typedef MapVector<MachineInstr*, Lowering> LoweringMap;

  /// Compute which lowering to use for each WinAlloca instruction.
  void computeLowerings(MachineFunction &MF, LoweringMap& Lowerings);

  /// Get the appropriate lowering based on current offset and amount.
  Lowering getLowering(int64_t CurrentOffset, int64_t AllocaAmount);

  /// Lower a WinAlloca instruction.
  void lower(MachineInstr* MI, Lowering L);

  MachineRegisterInfo *MRI;
  const X86Subtarget *STI;
  const TargetInstrInfo *TII;
  const X86RegisterInfo *TRI;
  unsigned StackPtr;
  unsigned SlotSize;
  int64_t StackProbeSize;
  bool NoStackArgProbe;

  StringRef getPassName() const override { return "X86 WinAlloca Expander"; }
  static char ID;
};

char X86WinAllocaExpander::ID = 0;

} // end anonymous namespace

FunctionPass *llvm::createX86WinAllocaExpander() {
  return new X86WinAllocaExpander();
}

/// Return the allocation amount for a WinAlloca instruction, or -1 if unknown.
static int64_t getWinAllocaAmount(MachineInstr *MI, MachineRegisterInfo *MRI) {
  assert(MI->getOpcode() == X86::WIN_ALLOCA_32 ||
         MI->getOpcode() == X86::WIN_ALLOCA_64);
  assert(MI->getOperand(0).isReg());

  unsigned AmountReg = MI->getOperand(0).getReg();
  MachineInstr *Def = MRI->getUniqueVRegDef(AmountReg);

  // Look through copies.
  while (Def && Def->isCopy() && Def->getOperand(1).isReg())
    Def = MRI->getUniqueVRegDef(Def->getOperand(1).getReg());

  if (!Def ||
      (Def->getOpcode() != X86::MOV32ri && Def->getOpcode() != X86::MOV64ri) ||
      !Def->getOperand(1).isImm())
    return -1;

  return Def->getOperand(1).getImm();
}

X86WinAllocaExpander::Lowering
X86WinAllocaExpander::getLowering(int64_t CurrentOffset,
                                  int64_t AllocaAmount) {
  // For a non-constant amount or a large amount, we have to probe.
  if (AllocaAmount < 0 || AllocaAmount > StackProbeSize)
    return Probe;

  // If it fits within the safe region of the stack, just subtract.
  if (CurrentOffset + AllocaAmount <= StackProbeSize)
    return Sub;

  // Otherwise, touch the current tip of the stack, then subtract.
  return TouchAndSub;
}

static bool isPushPop(const MachineInstr &MI) {
  switch (MI.getOpcode()) {
  case X86::PUSH32i8:
  case X86::PUSH32r:
  case X86::PUSH32rmm:
  case X86::PUSH32rmr:
  case X86::PUSHi32:
  case X86::PUSH64i8:
  case X86::PUSH64r:
  case X86::PUSH64rmm:
  case X86::PUSH64rmr:
  case X86::PUSH64i32:
  case X86::POP32r:
  case X86::POP64r:
    return true;
  default:
    return false;
  }
}

void X86WinAllocaExpander::computeLowerings(MachineFunction &MF,
                                            LoweringMap &Lowerings) {
  // Do a one-pass reverse post-order walk of the CFG to conservatively estimate
  // the offset between the stack pointer and the lowest touched part of the
  // stack, and use that to decide how to lower each WinAlloca instruction.

  // Initialize OutOffset[B], the stack offset at exit from B, to something big.
  DenseMap<MachineBasicBlock *, int64_t> OutOffset;
  for (MachineBasicBlock &MBB : MF)
    OutOffset[&MBB] = INT32_MAX;

  // Note: we don't know the offset at the start of the entry block since the
  // prologue hasn't been inserted yet, and how much that will adjust the stack
  // pointer depends on register spills, which have not been computed yet.

  // Compute the reverse post-order.
  ReversePostOrderTraversal<MachineFunction*> RPO(&MF);

  for (MachineBasicBlock *MBB : RPO) {
    int64_t Offset = -1;
    for (MachineBasicBlock *Pred : MBB->predecessors())
      Offset = std::max(Offset, OutOffset[Pred]);
    if (Offset == -1) Offset = INT32_MAX;

    for (MachineInstr &MI : *MBB) {
      if (MI.getOpcode() == X86::WIN_ALLOCA_32 ||
          MI.getOpcode() == X86::WIN_ALLOCA_64) {
        // A WinAlloca moves StackPtr, and potentially touches it.
        int64_t Amount = getWinAllocaAmount(&MI, MRI);
        Lowering L = getLowering(Offset, Amount);
        Lowerings[&MI] = L;
        switch (L) {
        case Sub:
          Offset += Amount;
          break;
        case TouchAndSub:
          Offset = Amount;
          break;
        case Probe:
          Offset = 0;
          break;
        }
      } else if (MI.isCall() || isPushPop(MI)) {
        // Calls, pushes and pops touch the tip of the stack.
        Offset = 0;
      } else if (MI.getOpcode() == X86::ADJCALLSTACKUP32 ||
                 MI.getOpcode() == X86::ADJCALLSTACKUP64) {
        Offset -= MI.getOperand(0).getImm();
      } else if (MI.getOpcode() == X86::ADJCALLSTACKDOWN32 ||
                 MI.getOpcode() == X86::ADJCALLSTACKDOWN64) {
        Offset += MI.getOperand(0).getImm();
      } else if (MI.modifiesRegister(StackPtr, TRI)) {
        // Any other modification of SP means we've lost track of it.
        Offset = INT32_MAX;
      }
    }

    OutOffset[MBB] = Offset;
  }
}

static unsigned getSubOpcode(bool Is64Bit, int64_t Amount) {
  if (Is64Bit)
    return isInt<8>(Amount) ? X86::SUB64ri8 : X86::SUB64ri32;
  return isInt<8>(Amount) ? X86::SUB32ri8 : X86::SUB32ri;
}

void X86WinAllocaExpander::lower(MachineInstr* MI, Lowering L) {
  DebugLoc DL = MI->getDebugLoc();
  MachineBasicBlock *MBB = MI->getParent();
  MachineBasicBlock::iterator I = *MI;

  int64_t Amount = getWinAllocaAmount(MI, MRI);
  if (Amount == 0) {
    MI->eraseFromParent();
    return;
  }

  // These two variables differ on x32, which is a 64-bit target with a
  // 32-bit alloca.
  bool Is64Bit = STI->is64Bit();
  bool Is64BitAlloca = MI->getOpcode() == X86::WIN_ALLOCA_64;
  assert(SlotSize == 4 || SlotSize == 8);

  switch (L) {
  case TouchAndSub: {
    assert(Amount >= SlotSize);

    // Use a push to touch the top of the stack.
    unsigned RegA = Is64Bit ? X86::RAX : X86::EAX;
    BuildMI(*MBB, I, DL, TII->get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
        .addReg(RegA, RegState::Undef);
    Amount -= SlotSize;
    if (!Amount)
      break;

    // Fall through to make any remaining adjustment.
    LLVM_FALLTHROUGH;
  }
  case Sub:
    assert(Amount > 0);
    if (Amount == SlotSize) {
      // Use push to save size.
      unsigned RegA = Is64Bit ? X86::RAX : X86::EAX;
      BuildMI(*MBB, I, DL, TII->get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
          .addReg(RegA, RegState::Undef);
    } else {
      // Sub.
      BuildMI(*MBB, I, DL,
              TII->get(getSubOpcode(Is64BitAlloca, Amount)), StackPtr)
          .addReg(StackPtr)
          .addImm(Amount);
    }
    break;
  case Probe:
    if (!NoStackArgProbe) {
      // The probe lowering expects the amount in RAX/EAX.
      unsigned RegA = Is64BitAlloca ? X86::RAX : X86::EAX;
      BuildMI(*MBB, MI, DL, TII->get(TargetOpcode::COPY), RegA)
          .addReg(MI->getOperand(0).getReg());

      // Do the probe.
      STI->getFrameLowering()->emitStackProbe(*MBB->getParent(), *MBB, MI, DL,
                                              /*InPrologue=*/false);
    } else {
      // Sub
      BuildMI(*MBB, I, DL,
              TII->get(Is64BitAlloca ? X86::SUB64rr : X86::SUB32rr), StackPtr)
          .addReg(StackPtr)
          .addReg(MI->getOperand(0).getReg());
    }
    break;
  }

  unsigned AmountReg = MI->getOperand(0).getReg();
  MI->eraseFromParent();

  // Delete the definition of AmountReg, possibly walking a chain of copies.
  for (;;) {
    if (!MRI->use_empty(AmountReg))
      break;
    MachineInstr *AmountDef = MRI->getUniqueVRegDef(AmountReg);
    if (!AmountDef)
      break;
    if (AmountDef->isCopy() && AmountDef->getOperand(1).isReg())
      AmountReg = AmountDef->getOperand(1).isReg();
    AmountDef->eraseFromParent();
    break;
  }
}

bool X86WinAllocaExpander::runOnMachineFunction(MachineFunction &MF) {
  if (!MF.getInfo<X86MachineFunctionInfo>()->hasWinAlloca())
    return false;

  MRI = &MF.getRegInfo();
  STI = &MF.getSubtarget<X86Subtarget>();
  TII = STI->getInstrInfo();
  TRI = STI->getRegisterInfo();
  StackPtr = TRI->getStackRegister();
  SlotSize = TRI->getSlotSize();

  StackProbeSize = 4096;
  if (MF.getFunction().hasFnAttribute("stack-probe-size")) {
    MF.getFunction()
        .getFnAttribute("stack-probe-size")
        .getValueAsString()
        .getAsInteger(0, StackProbeSize);
  }
  NoStackArgProbe = MF.getFunction().hasFnAttribute("no-stack-arg-probe");
  if (NoStackArgProbe)
    StackProbeSize = INT64_MAX;

  LoweringMap Lowerings;
  computeLowerings(MF, Lowerings);
  for (auto &P : Lowerings)
    lower(P.first, P.second);

  return true;
}
<commit_msg>X86WinAllocaExpander: Drop code looking through register copies (PR41786)<commit_after>//===----- X86WinAllocaExpander.cpp - Expand WinAlloca pseudo instruction -===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a pass that expands WinAlloca pseudo-instructions.
//
// It performs a conservative analysis to determine whether each allocation
// falls within a region of the stack that is safe to use, or whether stack
// probes must be emitted.
//
//===----------------------------------------------------------------------===//

#include "X86.h"
#include "X86InstrBuilder.h"
#include "X86InstrInfo.h"
#include "X86MachineFunctionInfo.h"
#include "X86Subtarget.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {

class X86WinAllocaExpander : public MachineFunctionPass {
public:
  X86WinAllocaExpander() : MachineFunctionPass(ID) {}

  bool runOnMachineFunction(MachineFunction &MF) override;

private:
  /// Strategies for lowering a WinAlloca.
  enum Lowering { TouchAndSub, Sub, Probe };

  /// Deterministic-order map from WinAlloca instruction to desired lowering.
  typedef MapVector<MachineInstr*, Lowering> LoweringMap;

  /// Compute which lowering to use for each WinAlloca instruction.
  void computeLowerings(MachineFunction &MF, LoweringMap& Lowerings);

  /// Get the appropriate lowering based on current offset and amount.
  Lowering getLowering(int64_t CurrentOffset, int64_t AllocaAmount);

  /// Lower a WinAlloca instruction.
  void lower(MachineInstr* MI, Lowering L);

  MachineRegisterInfo *MRI;
  const X86Subtarget *STI;
  const TargetInstrInfo *TII;
  const X86RegisterInfo *TRI;
  unsigned StackPtr;
  unsigned SlotSize;
  int64_t StackProbeSize;
  bool NoStackArgProbe;

  StringRef getPassName() const override { return "X86 WinAlloca Expander"; }
  static char ID;
};

char X86WinAllocaExpander::ID = 0;

} // end anonymous namespace

FunctionPass *llvm::createX86WinAllocaExpander() {
  return new X86WinAllocaExpander();
}

/// Return the allocation amount for a WinAlloca instruction, or -1 if unknown.
static int64_t getWinAllocaAmount(MachineInstr *MI, MachineRegisterInfo *MRI) {
  assert(MI->getOpcode() == X86::WIN_ALLOCA_32 ||
         MI->getOpcode() == X86::WIN_ALLOCA_64);
  assert(MI->getOperand(0).isReg());

  unsigned AmountReg = MI->getOperand(0).getReg();
  MachineInstr *Def = MRI->getUniqueVRegDef(AmountReg);

  if (!Def ||
      (Def->getOpcode() != X86::MOV32ri && Def->getOpcode() != X86::MOV64ri) ||
      !Def->getOperand(1).isImm())
    return -1;

  return Def->getOperand(1).getImm();
}

X86WinAllocaExpander::Lowering
X86WinAllocaExpander::getLowering(int64_t CurrentOffset,
                                  int64_t AllocaAmount) {
  // For a non-constant amount or a large amount, we have to probe.
  if (AllocaAmount < 0 || AllocaAmount > StackProbeSize)
    return Probe;

  // If it fits within the safe region of the stack, just subtract.
  if (CurrentOffset + AllocaAmount <= StackProbeSize)
    return Sub;

  // Otherwise, touch the current tip of the stack, then subtract.
  return TouchAndSub;
}

static bool isPushPop(const MachineInstr &MI) {
  switch (MI.getOpcode()) {
  case X86::PUSH32i8:
  case X86::PUSH32r:
  case X86::PUSH32rmm:
  case X86::PUSH32rmr:
  case X86::PUSHi32:
  case X86::PUSH64i8:
  case X86::PUSH64r:
  case X86::PUSH64rmm:
  case X86::PUSH64rmr:
  case X86::PUSH64i32:
  case X86::POP32r:
  case X86::POP64r:
    return true;
  default:
    return false;
  }
}

void X86WinAllocaExpander::computeLowerings(MachineFunction &MF,
                                            LoweringMap &Lowerings) {
  // Do a one-pass reverse post-order walk of the CFG to conservatively estimate
  // the offset between the stack pointer and the lowest touched part of the
  // stack, and use that to decide how to lower each WinAlloca instruction.

  // Initialize OutOffset[B], the stack offset at exit from B, to something big.
  DenseMap<MachineBasicBlock *, int64_t> OutOffset;
  for (MachineBasicBlock &MBB : MF)
    OutOffset[&MBB] = INT32_MAX;

  // Note: we don't know the offset at the start of the entry block since the
  // prologue hasn't been inserted yet, and how much that will adjust the stack
  // pointer depends on register spills, which have not been computed yet.

  // Compute the reverse post-order.
  ReversePostOrderTraversal<MachineFunction*> RPO(&MF);

  for (MachineBasicBlock *MBB : RPO) {
    int64_t Offset = -1;
    for (MachineBasicBlock *Pred : MBB->predecessors())
      Offset = std::max(Offset, OutOffset[Pred]);
    if (Offset == -1) Offset = INT32_MAX;

    for (MachineInstr &MI : *MBB) {
      if (MI.getOpcode() == X86::WIN_ALLOCA_32 ||
          MI.getOpcode() == X86::WIN_ALLOCA_64) {
        // A WinAlloca moves StackPtr, and potentially touches it.
        int64_t Amount = getWinAllocaAmount(&MI, MRI);
        Lowering L = getLowering(Offset, Amount);
        Lowerings[&MI] = L;
        switch (L) {
        case Sub:
          Offset += Amount;
          break;
        case TouchAndSub:
          Offset = Amount;
          break;
        case Probe:
          Offset = 0;
          break;
        }
      } else if (MI.isCall() || isPushPop(MI)) {
        // Calls, pushes and pops touch the tip of the stack.
        Offset = 0;
      } else if (MI.getOpcode() == X86::ADJCALLSTACKUP32 ||
                 MI.getOpcode() == X86::ADJCALLSTACKUP64) {
        Offset -= MI.getOperand(0).getImm();
      } else if (MI.getOpcode() == X86::ADJCALLSTACKDOWN32 ||
                 MI.getOpcode() == X86::ADJCALLSTACKDOWN64) {
        Offset += MI.getOperand(0).getImm();
      } else if (MI.modifiesRegister(StackPtr, TRI)) {
        // Any other modification of SP means we've lost track of it.
        Offset = INT32_MAX;
      }
    }

    OutOffset[MBB] = Offset;
  }
}

static unsigned getSubOpcode(bool Is64Bit, int64_t Amount) {
  if (Is64Bit)
    return isInt<8>(Amount) ? X86::SUB64ri8 : X86::SUB64ri32;
  return isInt<8>(Amount) ? X86::SUB32ri8 : X86::SUB32ri;
}

void X86WinAllocaExpander::lower(MachineInstr* MI, Lowering L) {
  DebugLoc DL = MI->getDebugLoc();
  MachineBasicBlock *MBB = MI->getParent();
  MachineBasicBlock::iterator I = *MI;

  int64_t Amount = getWinAllocaAmount(MI, MRI);
  if (Amount == 0) {
    MI->eraseFromParent();
    return;
  }

  // These two variables differ on x32, which is a 64-bit target with a
  // 32-bit alloca.
  bool Is64Bit = STI->is64Bit();
  bool Is64BitAlloca = MI->getOpcode() == X86::WIN_ALLOCA_64;
  assert(SlotSize == 4 || SlotSize == 8);

  switch (L) {
  case TouchAndSub: {
    assert(Amount >= SlotSize);

    // Use a push to touch the top of the stack.
    unsigned RegA = Is64Bit ? X86::RAX : X86::EAX;
    BuildMI(*MBB, I, DL, TII->get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
        .addReg(RegA, RegState::Undef);
    Amount -= SlotSize;
    if (!Amount)
      break;

    // Fall through to make any remaining adjustment.
    LLVM_FALLTHROUGH;
  }
  case Sub:
    assert(Amount > 0);
    if (Amount == SlotSize) {
      // Use push to save size.
      unsigned RegA = Is64Bit ? X86::RAX : X86::EAX;
      BuildMI(*MBB, I, DL, TII->get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
          .addReg(RegA, RegState::Undef);
    } else {
      // Sub.
      BuildMI(*MBB, I, DL,
              TII->get(getSubOpcode(Is64BitAlloca, Amount)), StackPtr)
          .addReg(StackPtr)
          .addImm(Amount);
    }
    break;
  case Probe:
    if (!NoStackArgProbe) {
      // The probe lowering expects the amount in RAX/EAX.
      unsigned RegA = Is64BitAlloca ? X86::RAX : X86::EAX;
      BuildMI(*MBB, MI, DL, TII->get(TargetOpcode::COPY), RegA)
          .addReg(MI->getOperand(0).getReg());

      // Do the probe.
      STI->getFrameLowering()->emitStackProbe(*MBB->getParent(), *MBB, MI, DL,
                                              /*InPrologue=*/false);
    } else {
      // Sub
      BuildMI(*MBB, I, DL,
              TII->get(Is64BitAlloca ? X86::SUB64rr : X86::SUB32rr), StackPtr)
          .addReg(StackPtr)
          .addReg(MI->getOperand(0).getReg());
    }
    break;
  }

  unsigned AmountReg = MI->getOperand(0).getReg();
  MI->eraseFromParent();

  // Delete the definition of AmountReg.
  if (MRI->use_empty(AmountReg))
    if (MachineInstr *AmountDef = MRI->getUniqueVRegDef(AmountReg))
      AmountDef->eraseFromParent();
}

bool X86WinAllocaExpander::runOnMachineFunction(MachineFunction &MF) {
  if (!MF.getInfo<X86MachineFunctionInfo>()->hasWinAlloca())
    return false;

  MRI = &MF.getRegInfo();
  STI = &MF.getSubtarget<X86Subtarget>();
  TII = STI->getInstrInfo();
  TRI = STI->getRegisterInfo();
  StackPtr = TRI->getStackRegister();
  SlotSize = TRI->getSlotSize();

  StackProbeSize = 4096;
  if (MF.getFunction().hasFnAttribute("stack-probe-size")) {
    MF.getFunction()
        .getFnAttribute("stack-probe-size")
        .getValueAsString()
        .getAsInteger(0, StackProbeSize);
  }
  NoStackArgProbe = MF.getFunction().hasFnAttribute("no-stack-arg-probe");
  if (NoStackArgProbe)
    StackProbeSize = INT64_MAX;

  LoweringMap Lowerings;
  computeLowerings(MF, Lowerings);
  for (auto &P : Lowerings)
    lower(P.first, P.second);

  return true;
}
<|endoftext|>


<commit_before>//===- InlineFunction.cpp - Code to perform function inlining -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements inlining of a function into a call site, resolving
// parameters and the return value as appropriate.
//
// FIXME: This pass should transform alloca instructions in the called function
// into alloca/dealloca pairs!  Or perhaps it should refuse to inline them!
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Constant.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Transforms/Utils/Local.h"
using namespace llvm;

bool llvm::InlineFunction(CallInst *CI) { return InlineFunction(CallSite(CI)); }
bool llvm::InlineFunction(InvokeInst *II) {return InlineFunction(CallSite(II));}

// InlineFunction - This function inlines the called function into the basic
// block of the caller.  This returns false if it is not possible to inline this
// call.  The program is still in a well defined state if this occurs though.
//
// Note that this only does one level of inlining.  For example, if the
// instruction 'call B' is inlined, and 'B' calls 'C', then the call to 'C' now
// exists in the instruction stream.  Similiarly this will inline a recursive
// function by one level.
//
bool llvm::InlineFunction(CallSite CS) {
  Instruction *TheCall = CS.getInstruction();
  assert(TheCall->getParent() && TheCall->getParent()->getParent() &&
         "Instruction not in function!");

  const Function *CalledFunc = CS.getCalledFunction();
  if (CalledFunc == 0 ||          // Can't inline external function or indirect
      CalledFunc->isExternal() || // call, or call to a vararg function!
      CalledFunc->getFunctionType()->isVarArg()) return false;

  BasicBlock *OrigBB = TheCall->getParent();
  Function *Caller = OrigBB->getParent();

  // Get an iterator to the last basic block in the function, which will have
  // the new function inlined after it.
  //
  Function::iterator LastBlock = &Caller->back();

  // Make sure to capture all of the return instructions from the cloned
  // function.
  std::vector<ReturnInst*> Returns;
  { // Scope to destroy ValueMap after cloning.
    // Calculate the vector of arguments to pass into the function cloner...
    std::map<const Value*, Value*> ValueMap;
    assert(std::distance(CalledFunc->abegin(), CalledFunc->aend()) ==
           std::distance(CS.arg_begin(), CS.arg_end()) &&
           "No varargs calls can be inlined!");

    CallSite::arg_iterator AI = CS.arg_begin();
    for (Function::const_aiterator I = CalledFunc->abegin(),
           E = CalledFunc->aend(); I != E; ++I, ++AI)
      ValueMap[I] = *AI;

    // Clone the entire body of the callee into the caller.
    CloneFunctionInto(Caller, CalledFunc, ValueMap, Returns, ".i");
  }

  // Remember the first block that is newly cloned over.
  Function::iterator FirstNewBlock = LastBlock; ++FirstNewBlock;

  // If there are any alloca instructions in the block that used to be the entry
  // block for the callee, move them to the entry block of the caller.  First
  // calculate which instruction they should be inserted before.  We insert the
  // instructions at the end of the current alloca list.
  //
  if (isa<AllocaInst>(FirstNewBlock->begin())) {
    BasicBlock::iterator InsertPoint = Caller->begin()->begin();
    while (isa<AllocaInst>(InsertPoint)) ++InsertPoint;

    for (BasicBlock::iterator I = FirstNewBlock->begin(),
           E = FirstNewBlock->end(); I != E; )
      if (AllocaInst *AI = dyn_cast<AllocaInst>(I++))
        if (isa<Constant>(AI->getArraySize())) {
          FirstNewBlock->getInstList().remove(AI);
          Caller->front().getInstList().insert(InsertPoint, AI);
        }
  }

  // If we are inlining for an invoke instruction, we must make sure to rewrite
  // any inlined 'unwind' instructions into branches to the invoke exception
  // destination, and call instructions into invoke instructions.
  if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) {
    BasicBlock *InvokeDest = II->getExceptionalDest();
    std::vector<Value*> InvokeDestPHIValues;

    // If there are PHI nodes in the exceptional destination block, we need to
    // keep track of which values came into them from this invoke, then remove
    // the entry for this block.
    for (BasicBlock::iterator I = InvokeDest->begin();
         PHINode *PN = dyn_cast<PHINode>(I); ++I)
      // Save the value to use for this edge...
      InvokeDestPHIValues.push_back(PN->getIncomingValueForBlock(OrigBB));

    for (Function::iterator BB = FirstNewBlock, E = Caller->end();
         BB != E; ++BB) {
      for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
        // We only need to check for function calls: inlined invoke instructions
        // require no special handling...
        if (CallInst *CI = dyn_cast<CallInst>(I)) {
          // Convert this function call into an invoke instruction...

          // First, split the basic block...
          BasicBlock *Split = BB->splitBasicBlock(CI, CI->getName()+".noexc");

          // Next, create the new invoke instruction, inserting it at the end
          // of the old basic block.
          InvokeInst *II =
            new InvokeInst(CI->getCalledValue(), Split, InvokeDest,
                           std::vector<Value*>(CI->op_begin()+1, CI->op_end()),
                           CI->getName(), BB->getTerminator());

          // Make sure that anything using the call now uses the invoke!
          CI->replaceAllUsesWith(II);

          // Delete the unconditional branch inserted by splitBasicBlock
          BB->getInstList().pop_back();
          Split->getInstList().pop_front();  // Delete the original call

          // Update any PHI nodes in the exceptional block to indicate that
          // there is now a new entry in them.
          unsigned i = 0;
          for (BasicBlock::iterator I = InvokeDest->begin();
               PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
            PN->addIncoming(InvokeDestPHIValues[i], BB);

          // This basic block is now complete, start scanning the next one.
          break;
        } else {
          ++I;
        }
      }

      if (UnwindInst *UI = dyn_cast<UnwindInst>(BB->getTerminator())) {
        // An UnwindInst requires special handling when it gets inlined into an
        // invoke site.  Once this happens, we know that the unwind would cause
        // a control transfer to the invoke exception destination, so we can
        // transform it into a direct branch to the exception destination.
        new BranchInst(InvokeDest, UI);

        // Delete the unwind instruction!
        UI->getParent()->getInstList().pop_back();

        // Update any PHI nodes in the exceptional block to indicate that
        // there is now a new entry in them.
        unsigned i = 0;
        for (BasicBlock::iterator I = InvokeDest->begin();
             PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
          PN->addIncoming(InvokeDestPHIValues[i], BB);
      }
    }

    // Now that everything is happy, we have one final detail.  The PHI nodes in
    // the exception destination block still have entries due to the original
    // invoke instruction.  Eliminate these entries (which might even delete the
    // PHI node) now.
    InvokeDest->removePredecessor(II->getParent());
  }

  // We want to clone the entire callee function into the hole between the
  // "starter" and "ender" blocks.  How we accomplish this depends on whether
  // this is an invoke instruction or a call instruction.
  BasicBlock *AfterCallBB;
  if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) {

    // Add an unconditional branch to make this look like the CallInst case...
    BranchInst *NewBr = new BranchInst(II->getNormalDest(), TheCall);

    // Split the basic block.  This guarantees that no PHI nodes will have to be
    // updated due to new incoming edges, and make the invoke case more
    // symmetric to the call case.
    AfterCallBB = OrigBB->splitBasicBlock(NewBr,
                                          CalledFunc->getName()+".entry");

    // Remove (unlink) the InvokeInst from the function...
    OrigBB->getInstList().remove(TheCall);

  } else {  // It's a call
    // If this is a call instruction, we need to split the basic block that the
    // call lives in.
    //
    AfterCallBB = OrigBB->splitBasicBlock(TheCall,
                                          CalledFunc->getName()+".entry");
    // Remove (unlink) the CallInst from the function...
    AfterCallBB->getInstList().remove(TheCall);
  }

  // Handle all of the return instructions that we just cloned in, and eliminate
  // any users of the original call/invoke instruction.
  if (Returns.size() > 1) {
    // The PHI node should go at the front of the new basic block to merge all
    // possible incoming values.
    //
    PHINode *PHI = 0;
    if (!TheCall->use_empty()) {
      PHI = new PHINode(CalledFunc->getReturnType(),
                        TheCall->getName(), AfterCallBB->begin());

      // Anything that used the result of the function call should now use the
      // PHI node as their operand.
      //
      TheCall->replaceAllUsesWith(PHI);
    }

    // Loop over all of the return instructions, turning them into unconditional
    // branches to the merge point now, and adding entries to the PHI node as
    // appropriate.
    for (unsigned i = 0, e = Returns.size(); i != e; ++i) {
      ReturnInst *RI = Returns[i];

      if (PHI) {
        assert(RI->getReturnValue() && "Ret should have value!");
        assert(RI->getReturnValue()->getType() == PHI->getType() &&
               "Ret value not consistent in function!");
        PHI->addIncoming(RI->getReturnValue(), RI->getParent());
      }

      // Add a branch to the merge point where the PHI node lives if it exists.
      new BranchInst(AfterCallBB, RI);

      // Delete the return instruction now
      RI->getParent()->getInstList().erase(RI);
    }

  } else if (!Returns.empty()) {
    // Otherwise, if there is exactly one return value, just replace anything
    // using the return value of the call with the computed value.
    if (!TheCall->use_empty())
      TheCall->replaceAllUsesWith(Returns[0]->getReturnValue());

    // Add a branch to the merge point where the PHI node lives if it exists.
    new BranchInst(AfterCallBB, Returns[0]);

    // Delete the return instruction now
    Returns[0]->getParent()->getInstList().erase(Returns[0]);
  }

  // Since we are now done with the Call/Invoke, we can delete it.
  delete TheCall;

  // Change the branch that used to go to AfterCallBB to branch to the first
  // basic block of the inlined function.
  //
  TerminatorInst *Br = OrigBB->getTerminator();
  assert(Br && Br->getOpcode() == Instruction::Br &&
	 "splitBasicBlock broken!");
  Br->setOperand(0, FirstNewBlock);

  // Now that the function is correct, make it a little bit nicer.  In
  // particular, move the basic blocks inserted from the end of the function
  // into the space made by splitting the source basic block.
  //
  Caller->getBasicBlockList().splice(AfterCallBB, Caller->getBasicBlockList(),
                                     FirstNewBlock, Caller->end());

  // We should always be able to fold the entry block of the function into the
  // single predecessor of the block...
  assert(cast<BranchInst>(Br)->isUnconditional() && "splitBasicBlock broken!");
  BasicBlock *CalleeEntry = cast<BranchInst>(Br)->getSuccessor(0);
  SimplifyCFG(CalleeEntry);

  // Okay, continue the CFG cleanup.  It's often the case that there is only a
  // single return instruction in the callee function.  If this is the case,
  // then we have an unconditional branch from the return block to the
  // 'AfterCallBB'.  Check for this case, and eliminate the branch is possible.
  SimplifyCFG(AfterCallBB);
  return true;
}
<commit_msg>Optimize the case where we are inlining a function that contains only one basic block, and that basic block ends with a return instruction.  In this case, we can just splice the cloned "body" of the function directly into the source basic block, avoiding a lot of rearrangement and splitBasicBlock's linear scan over the split block.  This speeds up the inliner on the testcase in PR209 from 2.3s to 1.7s, a 35% reduction.<commit_after>//===- InlineFunction.cpp - Code to perform function inlining -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements inlining of a function into a call site, resolving
// parameters and the return value as appropriate.
//
// FIXME: This pass should transform alloca instructions in the called function
// into alloca/dealloca pairs!  Or perhaps it should refuse to inline them!
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Constant.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Transforms/Utils/Local.h"
using namespace llvm;

bool llvm::InlineFunction(CallInst *CI) { return InlineFunction(CallSite(CI)); }
bool llvm::InlineFunction(InvokeInst *II) {return InlineFunction(CallSite(II));}

// InlineFunction - This function inlines the called function into the basic
// block of the caller.  This returns false if it is not possible to inline this
// call.  The program is still in a well defined state if this occurs though.
//
// Note that this only does one level of inlining.  For example, if the
// instruction 'call B' is inlined, and 'B' calls 'C', then the call to 'C' now
// exists in the instruction stream.  Similiarly this will inline a recursive
// function by one level.
//
bool llvm::InlineFunction(CallSite CS) {
  Instruction *TheCall = CS.getInstruction();
  assert(TheCall->getParent() && TheCall->getParent()->getParent() &&
         "Instruction not in function!");

  const Function *CalledFunc = CS.getCalledFunction();
  if (CalledFunc == 0 ||          // Can't inline external function or indirect
      CalledFunc->isExternal() || // call, or call to a vararg function!
      CalledFunc->getFunctionType()->isVarArg()) return false;

  BasicBlock *OrigBB = TheCall->getParent();
  Function *Caller = OrigBB->getParent();

  // Get an iterator to the last basic block in the function, which will have
  // the new function inlined after it.
  //
  Function::iterator LastBlock = &Caller->back();

  // Make sure to capture all of the return instructions from the cloned
  // function.
  std::vector<ReturnInst*> Returns;
  { // Scope to destroy ValueMap after cloning.
    // Calculate the vector of arguments to pass into the function cloner...
    std::map<const Value*, Value*> ValueMap;
    assert(std::distance(CalledFunc->abegin(), CalledFunc->aend()) ==
           std::distance(CS.arg_begin(), CS.arg_end()) &&
           "No varargs calls can be inlined!");

    CallSite::arg_iterator AI = CS.arg_begin();
    for (Function::const_aiterator I = CalledFunc->abegin(),
           E = CalledFunc->aend(); I != E; ++I, ++AI)
      ValueMap[I] = *AI;

    // Clone the entire body of the callee into the caller.
    CloneFunctionInto(Caller, CalledFunc, ValueMap, Returns, ".i");
  }

  // Remember the first block that is newly cloned over.
  Function::iterator FirstNewBlock = LastBlock; ++FirstNewBlock;

  // If there are any alloca instructions in the block that used to be the entry
  // block for the callee, move them to the entry block of the caller.  First
  // calculate which instruction they should be inserted before.  We insert the
  // instructions at the end of the current alloca list.
  //
  if (isa<AllocaInst>(FirstNewBlock->begin())) {
    BasicBlock::iterator InsertPoint = Caller->begin()->begin();
    while (isa<AllocaInst>(InsertPoint)) ++InsertPoint;

    for (BasicBlock::iterator I = FirstNewBlock->begin(),
           E = FirstNewBlock->end(); I != E; )
      if (AllocaInst *AI = dyn_cast<AllocaInst>(I++))
        if (isa<Constant>(AI->getArraySize())) {
          FirstNewBlock->getInstList().remove(AI);
          Caller->front().getInstList().insert(InsertPoint, AI);
        }
  }

  // If we are inlining for an invoke instruction, we must make sure to rewrite
  // any inlined 'unwind' instructions into branches to the invoke exception
  // destination, and call instructions into invoke instructions.
  if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) {
    BasicBlock *InvokeDest = II->getExceptionalDest();
    std::vector<Value*> InvokeDestPHIValues;

    // If there are PHI nodes in the exceptional destination block, we need to
    // keep track of which values came into them from this invoke, then remove
    // the entry for this block.
    for (BasicBlock::iterator I = InvokeDest->begin();
         PHINode *PN = dyn_cast<PHINode>(I); ++I)
      // Save the value to use for this edge...
      InvokeDestPHIValues.push_back(PN->getIncomingValueForBlock(OrigBB));

    for (Function::iterator BB = FirstNewBlock, E = Caller->end();
         BB != E; ++BB) {
      for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
        // We only need to check for function calls: inlined invoke instructions
        // require no special handling...
        if (CallInst *CI = dyn_cast<CallInst>(I)) {
          // Convert this function call into an invoke instruction...

          // First, split the basic block...
          BasicBlock *Split = BB->splitBasicBlock(CI, CI->getName()+".noexc");

          // Next, create the new invoke instruction, inserting it at the end
          // of the old basic block.
          InvokeInst *II =
            new InvokeInst(CI->getCalledValue(), Split, InvokeDest,
                           std::vector<Value*>(CI->op_begin()+1, CI->op_end()),
                           CI->getName(), BB->getTerminator());

          // Make sure that anything using the call now uses the invoke!
          CI->replaceAllUsesWith(II);

          // Delete the unconditional branch inserted by splitBasicBlock
          BB->getInstList().pop_back();
          Split->getInstList().pop_front();  // Delete the original call

          // Update any PHI nodes in the exceptional block to indicate that
          // there is now a new entry in them.
          unsigned i = 0;
          for (BasicBlock::iterator I = InvokeDest->begin();
               PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
            PN->addIncoming(InvokeDestPHIValues[i], BB);

          // This basic block is now complete, start scanning the next one.
          break;
        } else {
          ++I;
        }
      }

      if (UnwindInst *UI = dyn_cast<UnwindInst>(BB->getTerminator())) {
        // An UnwindInst requires special handling when it gets inlined into an
        // invoke site.  Once this happens, we know that the unwind would cause
        // a control transfer to the invoke exception destination, so we can
        // transform it into a direct branch to the exception destination.
        new BranchInst(InvokeDest, UI);

        // Delete the unwind instruction!
        UI->getParent()->getInstList().pop_back();

        // Update any PHI nodes in the exceptional block to indicate that
        // there is now a new entry in them.
        unsigned i = 0;
        for (BasicBlock::iterator I = InvokeDest->begin();
             PHINode *PN = dyn_cast<PHINode>(I); ++I, ++i)
          PN->addIncoming(InvokeDestPHIValues[i], BB);
      }
    }

    // Now that everything is happy, we have one final detail.  The PHI nodes in
    // the exception destination block still have entries due to the original
    // invoke instruction.  Eliminate these entries (which might even delete the
    // PHI node) now.
    InvokeDest->removePredecessor(II->getParent());
  }

  // If we cloned in _exactly one_ basic block, and if that block ends in a
  // return instruction, we splice the body of the inlined callee directly into
  // the calling basic block.
  if (Returns.size() == 1 && std::distance(FirstNewBlock, Caller->end()) == 1) {
    // Move all of the instructions right before the call.
    OrigBB->getInstList().splice(TheCall, FirstNewBlock->getInstList(),
                                 FirstNewBlock->begin(), FirstNewBlock->end());
    // Remove the cloned basic block.
    Caller->getBasicBlockList().pop_back();

    // If the call site was an invoke instruction, add a branch to the normal
    // destination.
    if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall))
      new BranchInst(II->getNormalDest(), TheCall);

    // If the return instruction returned a value, replace uses of the call with
    // uses of the returned value.
    if (!TheCall->use_empty())
      TheCall->replaceAllUsesWith(Returns[0]->getReturnValue());

    // Since we are now done with the Call/Invoke, we can delete it.
    TheCall->getParent()->getInstList().erase(TheCall);

    // Since we are now done with the return instruction, delete it also.
    Returns[0]->getParent()->getInstList().erase(Returns[0]);

    // We are now done with the inlining.
    return true;
  }

  // Otherwise, we have the normal case, of more than one block to inline or
  // multiple return sites.

  // We want to clone the entire callee function into the hole between the
  // "starter" and "ender" blocks.  How we accomplish this depends on whether
  // this is an invoke instruction or a call instruction.
  BasicBlock *AfterCallBB;
  if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) {

    // Add an unconditional branch to make this look like the CallInst case...
    BranchInst *NewBr = new BranchInst(II->getNormalDest(), TheCall);

    // Split the basic block.  This guarantees that no PHI nodes will have to be
    // updated due to new incoming edges, and make the invoke case more
    // symmetric to the call case.
    AfterCallBB = OrigBB->splitBasicBlock(NewBr,
                                          CalledFunc->getName()+".entry");

  } else {  // It's a call
    // If this is a call instruction, we need to split the basic block that
    // the call lives in.
    //
    AfterCallBB = OrigBB->splitBasicBlock(TheCall,
                                          CalledFunc->getName()+".entry");
  }

  // Change the branch that used to go to AfterCallBB to branch to the first
  // basic block of the inlined function.
  //
  TerminatorInst *Br = OrigBB->getTerminator();
  assert(Br && Br->getOpcode() == Instruction::Br &&
         "splitBasicBlock broken!");
  Br->setOperand(0, FirstNewBlock);

  // Now that the function is correct, make it a little bit nicer.  In
  // particular, move the basic blocks inserted from the end of the function
  // into the space made by splitting the source basic block.
  //
  Caller->getBasicBlockList().splice(AfterCallBB, Caller->getBasicBlockList(),
                                     FirstNewBlock, Caller->end());

  // Handle all of the return instructions that we just cloned in, and eliminate
  // any users of the original call/invoke instruction.
  if (Returns.size() > 1) {
    // The PHI node should go at the front of the new basic block to merge all
    // possible incoming values.
    //
    PHINode *PHI = 0;
    if (!TheCall->use_empty()) {
      PHI = new PHINode(CalledFunc->getReturnType(),
                        TheCall->getName(), AfterCallBB->begin());

      // Anything that used the result of the function call should now use the
      // PHI node as their operand.
      //
      TheCall->replaceAllUsesWith(PHI);
    }

    // Loop over all of the return instructions, turning them into unconditional
    // branches to the merge point now, and adding entries to the PHI node as
    // appropriate.
    for (unsigned i = 0, e = Returns.size(); i != e; ++i) {
      ReturnInst *RI = Returns[i];

      if (PHI) {
        assert(RI->getReturnValue() && "Ret should have value!");
        assert(RI->getReturnValue()->getType() == PHI->getType() &&
               "Ret value not consistent in function!");
        PHI->addIncoming(RI->getReturnValue(), RI->getParent());
      }

      // Add a branch to the merge point where the PHI node lives if it exists.
      new BranchInst(AfterCallBB, RI);

      // Delete the return instruction now
      RI->getParent()->getInstList().erase(RI);
    }

  } else if (!Returns.empty()) {
    // Otherwise, if there is exactly one return value, just replace anything
    // using the return value of the call with the computed value.
    if (!TheCall->use_empty())
      TheCall->replaceAllUsesWith(Returns[0]->getReturnValue());

    // Add a branch to the merge point where the PHI node lives if it exists.
    new BranchInst(AfterCallBB, Returns[0]);

    // Delete the return instruction now
    Returns[0]->getParent()->getInstList().erase(Returns[0]);
  }

  // Since we are now done with the Call/Invoke, we can delete it.
  TheCall->getParent()->getInstList().erase(TheCall);

  // We should always be able to fold the entry block of the function into the
  // single predecessor of the block...
  assert(cast<BranchInst>(Br)->isUnconditional() &&"splitBasicBlock broken!");
  BasicBlock *CalleeEntry = cast<BranchInst>(Br)->getSuccessor(0);
  SimplifyCFG(CalleeEntry);

  // Okay, continue the CFG cleanup.  It's often the case that there is only a
  // single return instruction in the callee function.  If this is the case,
  // then we have an unconditional branch from the return block to the
  // 'AfterCallBB'.  Check for this case, and eliminate the branch is possible.
  SimplifyCFG(AfterCallBB);

  return true;
}
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include <string>
#include <vector>

#include "State.hpp"
#include "MockupState.hpp"
#include "StateDirector.hpp"
#include "YarpStateDirector.hpp"
#include "RdUtils.hpp"

#include <yarp/os/Network.h>
#include <yarp/os/Bottle.h>
#include <yarp/os/RpcClient.h>
#include <yarp/os/RpcServer.h>

#include <yarp/os/Time.h>

using namespace rd;

//-- Class for the setup of the enviroment for all the tests
//----------------------------------------------------------------------------------------
//-- This opens the debug port for the FSM to send the messages to check states

class FSMTestEnvironment : public testing::Environment
{
    public:
        FSMTestEnvironment(int argc, char ** argv)
        {
            this->argc = argc;
            this->argv = argv;
        }

        virtual void SetUp()
        {
            //-- Init yarp network & server
            yarp::os::NetworkBase::setLocalMode(true);
            yarp::os::Network::init();
        }

        virtual void TearDown()
        {
            yarp::os::Network::fini();
        }

    private:
        int argc;
        char ** argv;

};

//-- Class for the setup of each test
//--------------------------------------------------------------------------------------
class FSMTest : public testing::Test
{
    public:
        virtual void SetUp()
        {
            ASSERT_TRUE(rpcClient.open(debug_port_name + "/rpc:c"));

            state1 = new MockupState(1);
            rpcServer1 = dynamic_cast<yarp::os::RpcServer *>(state1);
            yarp::os::PortReader *portReader1 = dynamic_cast<yarp::os::PortReader *>(state1);
            rpcServer1->setReader(*portReader1);
            ASSERT_TRUE(rpcServer1->open("/state1/rpc:s"));

            state2 = new MockupState(2);
            rpcServer2 = dynamic_cast<yarp::os::RpcServer *>(state2);
            yarp::os::PortReader *portReader2 = dynamic_cast<yarp::os::PortReader *>(state2);
            rpcServer2->setReader(*portReader2);
            ASSERT_TRUE(rpcServer2->open("/state2/rpc:s"));

            state3 = new MockupState(3);
            rpcServer3 = dynamic_cast<yarp::os::RpcServer *>(state3);
            yarp::os::PortReader *portReader3 = dynamic_cast<yarp::os::PortReader *>(state3);
            rpcServer3->setReader(*portReader3);
            ASSERT_TRUE(rpcServer3->open("/state3/rpc:s"));

            stateDirector1 = new YarpStateDirector(state1);
            stateDirector2 = new YarpStateDirector(state2);
            stateDirector3 = new YarpStateDirector(state3);

            nullStateDirector = new YarpStateDirector(State::getEndState());
        }

        virtual void TearDown()
        {
            //-- Disconnect states from yarp
            rpcServer1->interrupt();
            rpcServer1->close();

            rpcServer2->interrupt();
            rpcServer2->close();

            rpcServer3->interrupt();
            rpcServer3->close();

            rpcClient.interrupt();
            rpcClient.close();

            delete stateDirector1;
            delete stateDirector2;
            delete stateDirector3;
            delete nullStateDirector;

            stateDirector1 = NULL;
            stateDirector2 = NULL;
            stateDirector3 = NULL;
            nullStateDirector = NULL;
        }

    protected:
        State *state1, *state2, *state3;
        StateDirector *stateDirector1, *stateDirector2, *stateDirector3, *nullStateDirector;

        static const std::string debug_port_name;

        yarp::os::RpcClient rpcClient;
        yarp::os::RpcServer *rpcServer1, *rpcServer2, *rpcServer3;
};

const std::string FSMTest::debug_port_name = "/debug";

//--- Tests ------------------------------------------------------------------------------------------
TEST_F(FSMTest, StateMachineFlowIsCorrect)
{
    RD_INFO("Test Starts!\nAssigning transitions...\n");

    //-- Setup state machine
    stateDirector1->addTransition(stateDirector2, 2);
    stateDirector2->addTransition(stateDirector1, 1);
    stateDirector2->addTransition(stateDirector3, 3);
    stateDirector3->addTransition(stateDirector1, 1);

    //-- Start state machine
    ASSERT_TRUE(stateDirector1->Start());

    //-- Check that state 1 is active
    ASSERT_TRUE(stateDirector1->isActive());
    ASSERT_FALSE(stateDirector2->isActive());
    ASSERT_FALSE(stateDirector3->isActive());

    yarp::os::Bottle command, response;

    //-- Request current point in execution flow of state 1
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer1->getName()));
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through setup and loop
    int currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState1 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Send command to pass to state 2
    command.clear();
    command.addInt(2);
    rpcClient.write(command);

    //-- Wait until state 2 is initialized
    while (!stateDirector2->isActive()) {
        yarp::os::Time::delay(0.5);
    }

    //-- Request current point in execution flow of state 1
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through cleanup
    currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);

    //-- Check that state 2 is active
    ASSERT_FALSE(stateDirector1->isActive());
    ASSERT_TRUE(stateDirector2->isActive());
    ASSERT_FALSE(stateDirector3->isActive());

    //-- Request current point in execution flow of state 2
    ASSERT_TRUE(yarp::os::Network::disconnect(rpcClient.getName(), rpcServer1->getName(), false));
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer2->getName()));
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 2 passed through setup and loop
    int currentState2 = response.get(0).asInt();
    ASSERT_TRUE((currentState2 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState2 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Send command to pass to state 3
    command.clear();
    command.addInt(3);
    rpcClient.write(command);

    //-- Wait until state 3 is initialized
    while (!stateDirector3->isActive()) {
        yarp::os::Time::delay(0.5);
    }

    //-- Request current point in execution flow of state 2
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 2 passed through cleanup
    currentState2 = response.get(0).asInt();
    ASSERT_TRUE((currentState2 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);

    //-- Check that state 3 is active
    ASSERT_FALSE(stateDirector1->isActive());
    ASSERT_FALSE(stateDirector2->isActive());
    ASSERT_TRUE(stateDirector3->isActive());

    //-- Request current point in execution flow of state 3
    ASSERT_TRUE(yarp::os::Network::disconnect(rpcClient.getName(), rpcServer2->getName(), false));
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer3->getName()));
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 3 passed through setup and loop
    int currentState3 = response.get(0).asInt();
    ASSERT_TRUE((currentState3 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState3 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Stop state 3
    ASSERT_TRUE(stateDirector3->Stop());

    //-- Request current point in execution flow of state 3
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 3 passed through cleanup
    currentState3 = response.get(0).asInt();
    ASSERT_TRUE((currentState3 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);
}

TEST_F(FSMTest, StateMachineStopsAtNULL)
{
    RD_INFO("Test Starts!\nAssigning transitions...\n");

    //-- Setup state machine
    stateDirector1->addTransition(nullStateDirector, 2);

    //-- Start state machine
    ASSERT_TRUE(stateDirector1->Start());

    //-- Check that state 1 is active
    ASSERT_TRUE(stateDirector1->isActive());

    yarp::os::Bottle command, response;

    //-- Request current point in execution flow of state 1
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer1->getName()));
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through setup and loop
    int currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState1 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Send command to pass to state null (and therefore finish)
    command.clear();
    command.addInt(2);
    rpcClient.write(command);

    //-- Wait until state 1 is finalized
    while (stateDirector1->isActive()) {
        yarp::os::Time::delay(0.5);
    }

    //-- Request current point in execution flow of state 1
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through cleanup
    currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);

    //-- Check that state 1 is not active
    ASSERT_FALSE(stateDirector1->isActive());
}

//--- Main -------------------------------------------------------------------------------------------
int main(int argc, char **argv)
{
  testing::InitGoogleTest(&argc, argv);
  testing::Environment* env = testing::AddGlobalTestEnvironment(new FSMTestEnvironment(argc, argv));
  return RUN_ALL_TESTS();
}
<commit_msg>Take advantage of state-transition commands to perform less RPC calls<commit_after>#include "gtest/gtest.h"
#include <string>
#include <vector>

#include "State.hpp"
#include "MockupState.hpp"
#include "StateDirector.hpp"
#include "YarpStateDirector.hpp"
#include "RdUtils.hpp"

#include <yarp/os/Network.h>
#include <yarp/os/Bottle.h>
#include <yarp/os/RpcClient.h>
#include <yarp/os/RpcServer.h>

#include <yarp/os/Time.h>

using namespace rd;

//-- Class for the setup of the enviroment for all the tests
//----------------------------------------------------------------------------------------
//-- This opens the debug port for the FSM to send the messages to check states

class FSMTestEnvironment : public testing::Environment
{
    public:
        FSMTestEnvironment(int argc, char ** argv)
        {
            this->argc = argc;
            this->argv = argv;
        }

        virtual void SetUp()
        {
            //-- Init yarp network & server
            yarp::os::NetworkBase::setLocalMode(true);
            yarp::os::Network::init();
        }

        virtual void TearDown()
        {
            yarp::os::Network::fini();
        }

    private:
        int argc;
        char ** argv;

};

//-- Class for the setup of each test
//--------------------------------------------------------------------------------------
class FSMTest : public testing::Test
{
    public:
        virtual void SetUp()
        {
            ASSERT_TRUE(rpcClient.open(debug_port_name + "/rpc:c"));

            state1 = new MockupState(1);
            rpcServer1 = dynamic_cast<yarp::os::RpcServer *>(state1);
            yarp::os::PortReader *portReader1 = dynamic_cast<yarp::os::PortReader *>(state1);
            rpcServer1->setReader(*portReader1);
            ASSERT_TRUE(rpcServer1->open("/state1/rpc:s"));

            state2 = new MockupState(2);
            rpcServer2 = dynamic_cast<yarp::os::RpcServer *>(state2);
            yarp::os::PortReader *portReader2 = dynamic_cast<yarp::os::PortReader *>(state2);
            rpcServer2->setReader(*portReader2);
            ASSERT_TRUE(rpcServer2->open("/state2/rpc:s"));

            state3 = new MockupState(3);
            rpcServer3 = dynamic_cast<yarp::os::RpcServer *>(state3);
            yarp::os::PortReader *portReader3 = dynamic_cast<yarp::os::PortReader *>(state3);
            rpcServer3->setReader(*portReader3);
            ASSERT_TRUE(rpcServer3->open("/state3/rpc:s"));

            stateDirector1 = new YarpStateDirector(state1);
            stateDirector2 = new YarpStateDirector(state2);
            stateDirector3 = new YarpStateDirector(state3);

            nullStateDirector = new YarpStateDirector(State::getEndState());
        }

        virtual void TearDown()
        {
            //-- Disconnect states from yarp
            rpcServer1->interrupt();
            rpcServer1->close();

            rpcServer2->interrupt();
            rpcServer2->close();

            rpcServer3->interrupt();
            rpcServer3->close();

            rpcClient.interrupt();
            rpcClient.close();

            delete stateDirector1;
            delete stateDirector2;
            delete stateDirector3;
            delete nullStateDirector;

            stateDirector1 = NULL;
            stateDirector2 = NULL;
            stateDirector3 = NULL;
            nullStateDirector = NULL;
        }

    protected:
        State *state1, *state2, *state3;
        StateDirector *stateDirector1, *stateDirector2, *stateDirector3, *nullStateDirector;

        static const std::string debug_port_name;

        yarp::os::RpcClient rpcClient;
        yarp::os::RpcServer *rpcServer1, *rpcServer2, *rpcServer3;
};

const std::string FSMTest::debug_port_name = "/debug";

//--- Tests ------------------------------------------------------------------------------------------
TEST_F(FSMTest, StateMachineFlowIsCorrect)
{
    RD_INFO("Test Starts!\nAssigning transitions...\n");

    //-- Setup state machine
    stateDirector1->addTransition(stateDirector2, 2);
    stateDirector2->addTransition(stateDirector1, 1);
    stateDirector2->addTransition(stateDirector3, 3);
    stateDirector3->addTransition(stateDirector1, 1);

    //-- Start state machine
    ASSERT_TRUE(stateDirector1->Start());

    //-- Check that state 1 is active
    ASSERT_TRUE(stateDirector1->isActive());
    ASSERT_FALSE(stateDirector2->isActive());
    ASSERT_FALSE(stateDirector3->isActive());

    yarp::os::Bottle command, response;

    //-- Connect RpcClient with RpcServer for state 1
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer1->getName()));

    //-- Send command to pass to state 2
    command.addInt(2);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through setup and loop
    int currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState1 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Wait until state 2 is initialized
    while (!stateDirector2->isActive()) {
        yarp::os::Time::delay(0.5);
    }

    //-- Request current point in execution flow of state 1
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through cleanup
    currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);

    //-- Check that state 2 is active
    ASSERT_FALSE(stateDirector1->isActive());
    ASSERT_TRUE(stateDirector2->isActive());
    ASSERT_FALSE(stateDirector3->isActive());

    //-- Disconnect RpcClient from state 1, connect with RpcServer for state 2
    ASSERT_TRUE(yarp::os::Network::disconnect(rpcClient.getName(), rpcServer1->getName(), false));
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer2->getName()));

    //-- Send command to pass to state 3
    command.clear();
    command.addInt(3);
    rpcClient.write(command, response);

    //-- Check that state 2 passed through setup and loop
    int currentState2 = response.get(0).asInt();
    ASSERT_TRUE((currentState2 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState2 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Wait until state 3 is initialized
    while (!stateDirector3->isActive()) {
        yarp::os::Time::delay(0.5);
    }

    //-- Request current point in execution flow of state 2
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 2 passed through cleanup
    currentState2 = response.get(0).asInt();
    ASSERT_TRUE((currentState2 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);

    //-- Check that state 3 is active
    ASSERT_FALSE(stateDirector1->isActive());
    ASSERT_FALSE(stateDirector2->isActive());
    ASSERT_TRUE(stateDirector3->isActive());

    //-- Disconnect RpcClient from state 2, connect with RpcServer for state 3
    ASSERT_TRUE(yarp::os::Network::disconnect(rpcClient.getName(), rpcServer2->getName(), false));
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer3->getName()));

    //-- Request current point in execution flow of state 3
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 3 passed through setup and loop
    int currentState3 = response.get(0).asInt();
    ASSERT_TRUE((currentState3 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState3 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Stop state 3
    ASSERT_TRUE(stateDirector3->Stop());

    //-- Request current point in execution flow of state 3
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 3 passed through cleanup
    currentState3 = response.get(0).asInt();
    ASSERT_TRUE((currentState3 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);

}

TEST_F(FSMTest, StateMachineStopsAtNULL)
{
    RD_INFO("Test Starts!\nAssigning transitions...\n");

    //-- Setup state machine
    stateDirector1->addTransition(nullStateDirector, 2);

    //-- Start state machine
    ASSERT_TRUE(stateDirector1->Start());

    //-- Check that state 1 is active
    ASSERT_TRUE(stateDirector1->isActive());

    yarp::os::Bottle command, response;

    //-- Connect RpcClient with RpcServer for state 1
    ASSERT_TRUE(yarp::os::Network::connect(rpcClient.getName(), rpcServer1->getName()));

    //-- Send command to pass to state null (and therefore finish)
    command.clear();
    command.addInt(2);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through setup and loop
    int currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_SETUP) == MockupState::STATE_SETUP);
    ASSERT_TRUE((currentState1 & MockupState::STATE_LOOP) == MockupState::STATE_LOOP);

    //-- Wait until state 1 is finalized
    while (stateDirector1->isActive()) {
        yarp::os::Time::delay(0.5);
    }

    //-- Request current point in execution flow of state 1
    command.clear();
    command.addInt(MockupState::REQUEST_STATE);
    rpcClient.write(command, response);

    //-- Check that state 1 passed through cleanup
    currentState1 = response.get(0).asInt();
    ASSERT_TRUE((currentState1 & MockupState::STATE_CLEANUP) == MockupState::STATE_CLEANUP);

    //-- Check that state 1 is not active
    ASSERT_FALSE(stateDirector1->isActive());
}

//--- Main -------------------------------------------------------------------------------------------
int main(int argc, char **argv)
{
  testing::InitGoogleTest(&argc, argv);
  testing::Environment* env = testing::AddGlobalTestEnvironment(new FSMTestEnvironment(argc, argv));
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before><commit_msg>refactor AboutDialog to use RenderContext<commit_after><|endoftext|>