<commit_before>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of meegotouch-controlpanelapplets.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "ut_batteryapplet.h"
#include "batteryapplet.h"
#include "batterywidget.h"

#include <dcpwidgettypes.h>
#include <MApplication>
#include <MAction>
#include <QVector>

#define DEBUG
#include "../../src/debug.h"

/******************************************************************************
 * Ut_BatteryApplet implementation.
 */
void
Ut_BatteryApplet::init()
{
}

void
Ut_BatteryApplet::cleanup()
{
}

static int argc = 1;
static char *app_name = (char*) "./Ut_BatteryApplet";

void
Ut_BatteryApplet::initTestCase()
{
    m_App = new MApplication (argc, &app_name);
    m_Applet = new BatteryApplet;

    QVERIFY (!m_Applet->m_MainWidget);
    m_Applet->init ();

    /*
     * Testing if the widget is not created yet.
     */
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_BatteryApplet::cleanupTestCase()
{
    delete m_Applet;
    m_App->deleteLater ();
}

void
Ut_BatteryApplet::testTitle ()
{
    QString title = m_Applet->title();
    QVERIFY (!title.isEmpty());
}

void
Ut_BatteryApplet::testConstructWidget ()
{
    DcpWidget *widget;
    bool       backAccepted;

    /*
     * Testing if the applet creates a widget the first time.
     */
    widget = m_Applet->constructWidget (0);
    QVERIFY (widget);
    QVERIFY (m_Applet->m_MainWidget == widget);

    /*
     * Testing if the widget accepts the back. Our applets always accept back.
     */
    backAccepted = widget->back();
    QVERIFY (backAccepted);

    /*
     * Testing if the applet knows about the destruction of the widget.
     */
    delete widget;
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_BatteryApplet::testMenuItems ()
{
    QVector<MAction*> items = m_Applet->viewMenuItems ();

    SYS_DEBUG ("*** menu has %d items", items.size());
    /*
     * Seems that someone changed the code, we have no menu items.
     */
    QVERIFY (items.size() == 0);
}

QTEST_APPLESS_MAIN(Ut_BatteryApplet)

<commit_msg>Changes: oooupps... build fix<commit_after>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of meegotouch-controlpanelapplets.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "ut_batteryapplet.h"
#include "batteryapplet.h"
#include "batterywidget.h"

#include <dcpwidgettypes.h>
#include <dcpstylablewidget.h>
#include <MApplication>
#include <MAction>
#include <QVector>

#define DEBUG
#include "../../src/debug.h"

/******************************************************************************
 * Ut_BatteryApplet implementation.
 */
void
Ut_BatteryApplet::init()
{
}

void
Ut_BatteryApplet::cleanup()
{
}

static int argc = 1;
static char *app_name = (char*) "./Ut_BatteryApplet";

void
Ut_BatteryApplet::initTestCase()
{
    m_App = new MApplication (argc, &app_name);
    m_Applet = new BatteryApplet;

    QVERIFY (!m_Applet->m_MainWidget);
    m_Applet->init ();

    /*
     * Testing if the widget is not created yet.
     */
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_BatteryApplet::cleanupTestCase()
{
    delete m_Applet;
    m_App->deleteLater ();
}

void
Ut_BatteryApplet::testTitle ()
{
    QString title = m_Applet->title();
    QVERIFY (!title.isEmpty());
}

void
Ut_BatteryApplet::testConstructWidget ()
{
    DcpStylableWidget *widget;
    bool       backAccepted;

    /*
     * Testing if the applet creates a widget the first time.
     */
    widget = m_Applet->constructStylableWidget (0);
    QVERIFY (widget);
    QVERIFY (m_Applet->m_MainWidget == widget);

    /*
     * Testing if the widget accepts the back. Our applets always accept back.
     */
    backAccepted = widget->back();
    QVERIFY (backAccepted);

    /*
     * Testing if the applet knows about the destruction of the widget.
     */
    delete widget;
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_BatteryApplet::testMenuItems ()
{
    QVector<MAction*> items = m_Applet->viewMenuItems ();

    SYS_DEBUG ("*** menu has %d items", items.size());
    /*
     * Seems that someone changed the code, we have no menu items.
     */
    QVERIFY (items.size() == 0);
}

QTEST_APPLESS_MAIN(Ut_BatteryApplet)

<|endoftext|>


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

#include "absl/strings/str_split.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "asylo/client.h"
#include "asylo/grpc/util/enclave_server.pb.h"
#include "asylo/util/logging.h"
#include "gflags/gflags.h"

#include "oak/proto/oak.pb.h"

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

DEFINE_string(enclave_path, "", "Path to enclave to load");

int main(int argc, char *argv[]) {
  // Setup.
  ::google::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/true);

  // Initialise enclave.
  asylo::EnclaveManager::Configure(asylo::EnclaveManagerOptions());
  auto manager_result = asylo::EnclaveManager::Instance();
  if (!manager_result.ok()) {
    LOG(QFATAL) << "EnclaveManager unavailable: " << manager_result.status();
  }
  asylo::EnclaveManager *manager = manager_result.ValueOrDie();
  std::cout << "Loading " << FLAGS_enclave_path << std::endl;
  asylo::SimLoader loader(FLAGS_enclave_path, /*debug=*/true);

  asylo::EnclaveConfig config;
  asylo::ServerConfig *server_config = config.MutableExtension(asylo::server_input_config);
  server_config->set_host("0.0.0.0");
  server_config->set_port(8888);
  asylo::Status status = manager->LoadEnclave("oak_enclave", loader, config);
  if (!status.ok()) {
    LOG(QFATAL) << "Load " << FLAGS_enclave_path << " failed: " << status;
  }

  asylo::EnclaveClient *client = manager->GetClient("oak_enclave");

  // Initialisation.
  {
    LOG(INFO) << "Initialising enclave";
    asylo::EnclaveInput input;
    asylo::EnclaveOutput output;
    status = client->EnterAndRun(input, &output);
    if (!status.ok()) {
      LOG(QFATAL) << "EnterAndRun failed: " << status;
    }
    LOG(INFO) << "Enclave initialised";
  }

  // Wait.
  {
    absl::Notification server_timeout;
    server_timeout.WaitForNotificationWithTimeout(absl::Seconds(300));
  }

  // Finalisation.
  {
    LOG(INFO) << "Destroying enclave";
    asylo::EnclaveFinal final_input;
    status = manager->DestroyEnclave(client, final_input);
    if (!status.ok()) {
      LOG(QFATAL) << "Destroy " << FLAGS_enclave_path << " failed: " << status;
    }
    LOG(INFO) << "Enclave destroyed";
  }

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

#include "absl/strings/str_split.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "asylo/client.h"
#include "asylo/grpc/util/enclave_server.pb.h"
#include "asylo/util/logging.h"
#include "gflags/gflags.h"

#include "oak/proto/oak.pb.h"

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

DEFINE_string(enclave_path, "", "Path to enclave to load");

int main(int argc, char *argv[]) {
  // Setup.
  ::google::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/true);

  // Initialise enclave.
  asylo::EnclaveManager::Configure(asylo::EnclaveManagerOptions());
  auto manager_result = asylo::EnclaveManager::Instance();
  if (!manager_result.ok()) {
    LOG(QFATAL) << "EnclaveManager unavailable: " << manager_result.status();
  }
  asylo::EnclaveManager *manager = manager_result.ValueOrDie();
  std::cout << "Loading " << FLAGS_enclave_path << std::endl;
  asylo::SimLoader loader(FLAGS_enclave_path, /*debug=*/true);

  // Loading.
  {
    asylo::EnclaveConfig config;
    asylo::ServerConfig *server_config = config.MutableExtension(asylo::server_input_config);
    server_config->set_host("0.0.0.0");
    server_config->set_port(8888);
    asylo::Status status = manager->LoadEnclave("oak_enclave", loader, config);
    if (!status.ok()) {
      LOG(QFATAL) << "Load " << FLAGS_enclave_path << " failed: " << status;
    }
  }

  asylo::EnclaveClient *client = manager->GetClient("oak_enclave");

  // Initialisation.
  // This does not actually do anything interesting at the moment.
  {
    LOG(INFO) << "Initialising enclave";
    asylo::EnclaveInput input;
    asylo::EnclaveOutput output;
    asylo::Status status = client->EnterAndRun(input, &output);
    if (!status.ok()) {
      LOG(QFATAL) << "EnterAndRun failed: " << status;
    }
    LOG(INFO) << "Enclave initialised";
  }

  // Wait.
  {
    absl::Notification server_timeout;
    server_timeout.WaitForNotificationWithTimeout(absl::Seconds(300));
  }

  // Finalisation.
  {
    LOG(INFO) << "Destroying enclave";
    asylo::EnclaveFinal final_input;
    asylo::Status status = manager->DestroyEnclave(client, final_input);
    if (!status.ok()) {
      LOG(QFATAL) << "Destroy " << FLAGS_enclave_path << " failed: " << status;
    }
    LOG(INFO) << "Enclave destroyed";
  }

  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <thrift/lib/cpp2/async/ProtectionHandler.h>
#include <thrift/lib/cpp/transport/TTransportException.h>

namespace apache { namespace thrift {

void ProtectionHandler::read(Context* ctx, folly::IOBufQueue& q) {
  if (&inputQueue_ != &q) {
    // If not the same queue
    inputQueue_.append(q);
  }

  auto e = folly::try_and_catch<std::exception>([&]() {
      while (!closing_) {
        if (protectionState_ == ProtectionState::INVALID) {
          throw transport::TTransportException("protection state is invalid");
        }

        if (protectionState_ == ProtectionState::INPROGRESS) {
          // security is still doing stuff, let's return blank.
          break;
        }

        if (protectionState_ != ProtectionState::VALID) {
          // not an encrypted message, so pass-through
          ctx->fireRead(inputQueue_);
          break;
        }

        assert(saslEndpoint_ != nullptr);
        size_t remaining = 0;

        if (!inputQueue_.front() || inputQueue_.front()->empty()) {
          break;
        }

        // decrypt
        std::unique_ptr<folly::IOBuf> unwrapped =
          saslEndpoint_->unwrap(&inputQueue_, &remaining);
        assert(bool(unwrapped) ^ (remaining > 0));   // 1 and only 1 should be true
        if (unwrapped) {
          queue_.append(std::move(unwrapped));

          ctx->fireRead(queue_);
        } else {
          break;
        }
      }
    });
  if (e) {
    VLOG(5) << "Exception in ProtectionHandler::read(): " << e.what();
    ctx->fireReadException(std::move(e));
  }
  // Give ownership back to the main queue if we're not in the inprogress
  // state
  if (&inputQueue_ != &q &&
      protectionState_ != ProtectionState::INPROGRESS) {
    q.append(inputQueue_);
  }
}

folly::Future<void> ProtectionHandler::write(
  Context* ctx,
  std::unique_ptr<folly::IOBuf> buf) {
  if (protectionState_ == ProtectionState::VALID) {
    assert(saslEndpoint_);
    buf = saslEndpoint_->wrap(std::move(buf));
  }
  return ctx->fireWrite(std::move(buf));
}

void ProtectionHandler::protectionStateChanged() {
  // We only want to do this callback in the case where we're switching
  // to a valid protection state.
  if (getContext() && !inputQueue_.empty() &&
      protectionState_ == ProtectionState::VALID) {
    read(getContext(), inputQueue_);
  }
}

folly::Future<void> ProtectionHandler::close(Context* ctx) {
  closing_ = true;
  return ctx->fireClose();
}

}}
<commit_msg>ThriftSecurity: VLOG(5) -> LOG(ERROR)<commit_after>/*
 * Copyright 2015 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <thrift/lib/cpp2/async/ProtectionHandler.h>
#include <thrift/lib/cpp/transport/TTransportException.h>

namespace apache { namespace thrift {

void ProtectionHandler::read(Context* ctx, folly::IOBufQueue& q) {
  if (&inputQueue_ != &q) {
    // If not the same queue
    inputQueue_.append(q);
  }

  auto e = folly::try_and_catch<std::exception>([&]() {
      while (!closing_) {
        if (protectionState_ == ProtectionState::INVALID) {
          throw transport::TTransportException("protection state is invalid");
        }

        if (protectionState_ == ProtectionState::INPROGRESS) {
          // security is still doing stuff, let's return blank.
          break;
        }

        if (protectionState_ != ProtectionState::VALID) {
          // not an encrypted message, so pass-through
          ctx->fireRead(inputQueue_);
          break;
        }

        assert(saslEndpoint_ != nullptr);
        size_t remaining = 0;

        if (!inputQueue_.front() || inputQueue_.front()->empty()) {
          break;
        }

        // decrypt
        std::unique_ptr<folly::IOBuf> unwrapped =
          saslEndpoint_->unwrap(&inputQueue_, &remaining);
        assert(bool(unwrapped) ^ (remaining > 0));   // 1 and only 1 should be true
        if (unwrapped) {
          queue_.append(std::move(unwrapped));

          ctx->fireRead(queue_);
        } else {
          break;
        }
      }
    });
  if (e) {
    LOG(ERROR) << "Exception in ProtectionHandler::read(): " << e.what();
    ctx->fireReadException(std::move(e));
  }
  // Give ownership back to the main queue if we're not in the inprogress
  // state
  if (&inputQueue_ != &q &&
      protectionState_ != ProtectionState::INPROGRESS) {
    q.append(inputQueue_);
  }
}

folly::Future<void> ProtectionHandler::write(
  Context* ctx,
  std::unique_ptr<folly::IOBuf> buf) {
  if (protectionState_ == ProtectionState::VALID) {
    assert(saslEndpoint_);
    buf = saslEndpoint_->wrap(std::move(buf));
  }
  return ctx->fireWrite(std::move(buf));
}

void ProtectionHandler::protectionStateChanged() {
  // We only want to do this callback in the case where we're switching
  // to a valid protection state.
  if (getContext() && !inputQueue_.empty() &&
      protectionState_ == ProtectionState::VALID) {
    read(getContext(), inputQueue_);
  }
}

folly::Future<void> ProtectionHandler::close(Context* ctx) {
  closing_ = true;
  return ctx->fireClose();
}

}}
<|endoftext|>


<commit_before>// BUG: this whole thing depends on the specifics of how the clang version I
// am using emits llvm bitcode for the hacky RMC protocol.
// We rely on how basic blocks get named, on the labels forcing things
// into their own basic blocks, and probably will rely on this block
// having one predecessor and one successor. We could probably even
// force those to be empty without too much work by adding more labels...

#include <llvm/Pass.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/InstIterator.h>

#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
#include <llvm/Transforms/Utils/UnifyFunctionExitNodes.h>

#include <llvm/ADT/ArrayRef.h>

#include <llvm/Support/raw_ostream.h>

#include <ostream>
#include <fstream>
#include <iostream>
#include <map>
#include <set>

using namespace llvm;

namespace {
#if 0 // I hate you, emacs. Is there a better way to work around this?
}
#endif

/* To do bogus data dependencies, we will emit inline assembly
  instructions. This is sort of tasteless; we should add an intrinsic;
  but it means we don't need to modify llvm. This is what the
  instruction should look like (probably the constraints aren't
  right?):

  %2 = call i32 asm sideeffect "eor $0, $0;", "=r,0"(i32 %1) #2, !srcloc !11

  Also for ctrlisb we do:
  call void asm sideeffect "cmp $0, $0;beq 1f;1: isb", "r,~{memory}"(i32 %1) #2, !srcloc !4

  And for dmb:
  call void asm sideeffect "dmb", "~{memory}"() #2, !srcloc !5

  We can probably do a bogus inline asm on x86 to prevent reordering:
  %2 = call i32 asm sideeffect "", "=r,r,0,~{dirflag},~{fpsr},~{flags}"(i32 %1, i32 %0) #3, !srcloc !9

  What about this makes the right value come out:
    =r specifies an output parameter and the 0 says that that input parameter
    is paired with param 0.

  Hm. Does this *actually* work?? What does "sideeffect" actually mean
  for asm and do we need it.

  --
  Should we emit dmb directly, or try to use llvm's fences?
*/

//// Some auxillary data structures
enum RMCEdgeType {
  VisbilityEdge,
  ExecutionEdge
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdgeType& t) {
  switch (t) {
    case VisbilityEdge:
      os << "v";
      break;
    case ExecutionEdge:
      os << "x";
      break;
    default:
      os << "?";
      break;
  }
  return os;
}

// Info about an RMC edge
struct RMCEdge {
  RMCEdgeType edgeType;
  BasicBlock *src;
  BasicBlock *dst;

  bool operator<(const RMCEdge& rhs) const {
    return std::tie(edgeType, src, dst)
      < std::tie(rhs.edgeType, rhs.src, rhs.dst);
  }

  void print(raw_ostream &os) const {
    // substr(5) is to drop "_rmc_" from the front
    os << src->getName().substr(5) << " -" << edgeType <<
      "-> " << dst->getName().substr(5);
  }
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdge& e) {
  e.print(os);
  return os;
}

///////////////////////////////////////////////////////////////////////////
// Information for a node in the RMC graph.
enum ActionType {
  ActionComplex,
  ActionSimpleRead,
  ActionSimpleWrites, // needs to be paired with a dep
  ActionSimpleRMW
};
struct Action {
  Action(BasicBlock *p_bb) :
    bb(p_bb),
    type(ActionComplex),
    stores(0), loads(0), RMWs(0), calls(0), soleLoad(nullptr) {}
  void operator=(const Action &) LLVM_DELETED_FUNCTION;
  Action(const Action &) LLVM_DELETED_FUNCTION;
  Action(Action &&) = default; // move constructor!

  BasicBlock *bb;

  // Some basic info about what sort of instructions live in the action
  ActionType type;
  int stores;
  int loads;
  int RMWs;
  int calls;
  LoadInst *soleLoad;

  // Edges in the graph.
  // XXX: Would we be better off storing this some other way?
  // a <ptr, type> pair?
  // And should we store v edges in x
  SmallPtrSet<Action *, 2> execEdges;
  SmallPtrSet<Action *, 2> visEdges;
};

enum CutType {
  CutNone,
  CutCtrlIsync, // needs to be paired with a dep
  CutLwsync,
  CutSync
};
struct EdgeCut {
  EdgeCut() : type(CutNone), front(false), read(nullptr) {}
  CutType type;
  bool front;
  Value *read;
};

///////////////////////////////////////////////////////////////////////////
// Code to find all simple paths between two basic blocks.
// Could generalize more to graphs if we wanted, but I don't right
// now.
typedef std::vector<BasicBlock *> Path;
typedef SmallVector<Path, 2> PathList;
typedef SmallPtrSet<BasicBlock *, 8> GreySet;

PathList findAllSimplePaths_(GreySet *grey, BasicBlock *src, BasicBlock *dst) {
  PathList paths;
  if (grey->count(src)) return paths;
  if (src == dst) {
    Path path;
    path.push_back(dst);
    paths.push_back(std::move(path));
    return paths;
  }

  grey->insert(src);

  // Go search all the successors
  for (auto i = succ_begin(src), e = succ_end(src); i != e; i++) {
    PathList subpaths = findAllSimplePaths_(grey, *i, dst);
    std::move(subpaths.begin(), subpaths.end(), back_inserter(paths));
  }

  // Add our step to all of the vectors
  for (auto & path : paths) {
    path.push_back(src);
  }

  // Remove it from the set of things we've seen. We might come
  // through here again.
  // We can't really do any sort of memoization, since in a cyclic
  // graph the possible simple paths depend not just on what node we
  // are on, but our previous path (to avoid looping).
  grey->erase(src);

  return paths;
}

PathList findAllSimplePaths(BasicBlock *src, BasicBlock *dst) {
  GreySet grey;
  return findAllSimplePaths_(&grey, src, dst);
}

void dumpPaths(const PathList &paths) {
  for (auto & path : paths) {
    for (auto block : path) {
      errs() << block->getName() << " <- ";
    }
    errs() << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////
// XXX: This is the wrong way to do this!
bool targetARM = true;
bool targetx86 = false;

// Some llvm nonsense. I should probably find a way to clean this up.
// do we put ~{dirflag},~{fpsr},~{flags} for the x86 ones? don't think so.
Instruction *makeSync(Value *dummy) {
  LLVMContext &C = dummy->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "msync", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "sync");
}
Instruction *makeLwsync(Value *dummy) {
  LLVMContext &C = dummy->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "lwsync");
}
Instruction *makeCtrlIsync(Value *v) {
  LLVMContext &C = v->getContext();
  FunctionType *f_ty =
    FunctionType::get(FunctionType::getVoidTy(C), v->getType(), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "cmp $0, $0;beq 1f;1: isb", "r,~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "", "r,~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "ctrlisync");
}
// We also need to add a thing for fake data deps, which is more annoying.

///////////////////////////////////////////////////////////////////////////
//// Actual code for the pass

class RealizeRMC : public FunctionPass {
private:
  std::vector<Action> actions_;
  DenseMap<BasicBlock *, Action *> bb2action_;
  DenseMap<BasicBlock *, EdgeCut> cuts_;

public:
  static char ID;
  RealizeRMC() : FunctionPass(ID) {

  }
  ~RealizeRMC() { }
  std::vector<RMCEdge> findEdges(Function &F);
  void buildGraph(std::vector<RMCEdge> &edges, Function &F);
  virtual bool runOnFunction(Function &F);

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequiredID(BreakCriticalEdgesID);
    AU.addRequired<UnifyFunctionExitNodes>();
    AU.setPreservesCFG();
  }

  // Clear our data structures to save memory, make things clean for
  // future runs.
  void clear() {
    actions_.clear();
    bb2action_.clear();
    cuts_.clear();
  }
};

bool nameMatches(StringRef blockName, StringRef target) {
  StringRef name = "_rmc_" + target.str() + "_";
  if (!blockName.startswith(name)) return false;
  // Now make sure the rest is an int
  APInt dummy;
  return !blockName.drop_front(name.size()).getAsInteger(10, dummy);
}

StringRef getStringArg(Value *v) {
  const Value *g = cast<Constant>(v)->stripPointerCasts();
  const Constant *ptr = cast<GlobalVariable>(g)->getInitializer();
  StringRef str = cast<ConstantDataSequential>(ptr)->getAsString();
  // Drop the '\0' at the end
  return str.drop_back();
}

std::vector<RMCEdge> RealizeRMC::findEdges(Function &F) {
  std::vector<RMCEdge> edges;

  for (inst_iterator is = inst_begin(F), ie = inst_end(F); is != ie;) {
    // Grab the instruction and advance the iterator at the start, since
    // we might delete the instruction.
    Instruction *i = &*is;
    is++;

    CallInst *load = dyn_cast<CallInst>(i);
    if (!load) continue;
    Function *target = load->getCalledFunction();
    // We look for calls to the bogus function
    // __rmc_edge_register, pull out the information about them,
    // and delete the calls.
    if (!target || target->getName() != "__rmc_edge_register") continue;

    // Pull out what the operands have to be.
    // We just assert if something is wrong, which is not great UX.
    bool isVisibility = cast<ConstantInt>(load->getOperand(0))
      ->getValue().getBoolValue();
    RMCEdgeType edgeType = isVisibility ? VisbilityEdge : ExecutionEdge;
    StringRef srcName = getStringArg(load->getOperand(1));
    StringRef dstName = getStringArg(load->getOperand(2));

    // Since multiple blocks can have the same tag, we search for
    // them by name.
    // We could make this more efficient by building maps but I don't think
    // it is going to matter.
    for (auto & src : F) {
      if (!nameMatches(src.getName(), srcName)) continue;
      for (auto & dst : F) {
        if (!nameMatches(dst.getName(), dstName)) continue;

        edges.push_back({edgeType, &src, &dst});
      }
    }

    // Delete the bogus call.
    i->eraseFromParent();
  }

  return edges;
}

void analyzeAction(Action &info) {
  LoadInst *soleLoad = nullptr;
  for (auto & i : *info.bb) {
    if (LoadInst *load = dyn_cast<LoadInst>(&i)) {
      info.loads++;
      soleLoad = load;
    } else if (isa<StoreInst>(i)) {
      info.stores++;
    // What else counts as a call? I'm counting fences I guess.
    } else if (isa<CallInst>(i) || isa<FenceInst>(i)) {
      info.calls++;
    } else if (isa<AtomicCmpXchgInst>(i) || isa<AtomicRMWInst>(i)) {
      info.RMWs++;
    }
  }
  // Try to characterize what this action does.
  // These categories might not be the best.
  if (info.loads == 1 && info.stores+info.calls+info.RMWs == 0) {
    info.soleLoad = soleLoad;
    info.type = ActionSimpleRead;
  } else if (info.stores >= 1 && info.loads+info.calls+info.RMWs == 0) {
    info.type = ActionSimpleWrites;
  } else if (info.RMWs == 1 && info.stores+info.loads+info.calls == 0) {
    info.type = ActionSimpleRMW;
  } else {
    info.type = ActionComplex;
  }
}

void RealizeRMC::buildGraph(std::vector<RMCEdge> &edges, Function &F) {
  // First, collect all the basic blocks with edges attached to them
  SmallPtrSet<BasicBlock *, 8> basicBlocks;
  for (auto & edge : edges) {
    basicBlocks.insert(edge.src);
    basicBlocks.insert(edge.dst);
  }

  // Now, make the vector of actions and a mapping from BasicBlock *.
  actions_.reserve(basicBlocks.size());
  for (auto bb : basicBlocks) {
    actions_.emplace_back(bb);
    bb2action_[bb] = &actions_.back();
  }

  // Analyze the instructions in actions to see what they do.
  for (auto & action : actions_) {
    analyzeAction(action);
  }

  // Build our list of edges into a more explicit graph
  for (auto & edge : edges) {
    Action *src = bb2action_[edge.src];
    Action *dst = bb2action_[edge.dst];
    if (edge.edgeType == VisbilityEdge) {
      src->visEdges.insert(dst);
    } else {
      src->execEdges.insert(dst);
    }
  }
}

bool RealizeRMC::runOnFunction(Function &F) {

  auto edges = findEdges(F);
  if (edges.empty()) return false;

  for (auto & edge : edges) {
    errs() << "Found an edge: " << edge << "\n";
  }

  //UnifyFunctionExitNodes &EN = getAnalysis<UnifyFunctionExitNodes>();
  buildGraph(edges, F);

  clear();
  return true;
}

char RealizeRMC::ID = 0;
RegisterPass<RealizeRMC> X("realize-rmc", "Compile RMC annotations");
}
<commit_msg>A basic implementation of RMC by just emitting a lot of lwsyncs.<commit_after>// BUG: this whole thing depends on the specifics of how the clang version I
// am using emits llvm bitcode for the hacky RMC protocol.
// We rely on how basic blocks get named, on the labels forcing things
// into their own basic blocks, and probably will rely on this block
// having one predecessor and one successor. We could probably even
// force those to be empty without too much work by adding more labels...

#include <llvm/Pass.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/InstIterator.h>

#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
#include <llvm/Transforms/Utils/UnifyFunctionExitNodes.h>

#include <llvm/ADT/ArrayRef.h>

#include <llvm/Support/raw_ostream.h>

#include <ostream>
#include <fstream>
#include <iostream>
#include <map>
#include <set>

using namespace llvm;

namespace {
#if 0 // I hate you, emacs. Is there a better way to work around this?
}
#endif

/* To do bogus data dependencies, we will emit inline assembly
  instructions. This is sort of tasteless; we should add an intrinsic;
  but it means we don't need to modify llvm. This is what the
  instruction should look like (probably the constraints aren't
  right?):

  %2 = call i32 asm sideeffect "eor $0, $0;", "=r,0"(i32 %1) #2, !srcloc !11

  Also for ctrlisb we do:
  call void asm sideeffect "cmp $0, $0;beq 1f;1: isb", "r,~{memory}"(i32 %1) #2, !srcloc !4

  And for dmb:
  call void asm sideeffect "dmb", "~{memory}"() #2, !srcloc !5

  We can probably do a bogus inline asm on x86 to prevent reordering:
  %2 = call i32 asm sideeffect "", "=r,r,0,~{dirflag},~{fpsr},~{flags}"(i32 %1, i32 %0) #3, !srcloc !9

  What about this makes the right value come out:
    =r specifies an output parameter and the 0 says that that input parameter
    is paired with param 0.

  Hm. Does this *actually* work?? What does "sideeffect" actually mean
  for asm and do we need it.

  --
  Should we emit dmb directly, or try to use llvm's fences?
*/

//// Some auxillary data structures
enum RMCEdgeType {
  VisbilityEdge,
  ExecutionEdge
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdgeType& t) {
  switch (t) {
    case VisbilityEdge:
      os << "v";
      break;
    case ExecutionEdge:
      os << "x";
      break;
    default:
      os << "?";
      break;
  }
  return os;
}

// Info about an RMC edge
struct RMCEdge {
  RMCEdgeType edgeType;
  BasicBlock *src;
  BasicBlock *dst;

  bool operator<(const RMCEdge& rhs) const {
    return std::tie(edgeType, src, dst)
      < std::tie(rhs.edgeType, rhs.src, rhs.dst);
  }

  void print(raw_ostream &os) const {
    // substr(5) is to drop "_rmc_" from the front
    os << src->getName().substr(5) << " -" << edgeType <<
      "-> " << dst->getName().substr(5);
  }
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdge& e) {
  e.print(os);
  return os;
}

///////////////////////////////////////////////////////////////////////////
// Information for a node in the RMC graph.
enum ActionType {
  ActionComplex,
  ActionSimpleRead,
  ActionSimpleWrites, // needs to be paired with a dep
  ActionSimpleRMW
};
struct Action {
  Action(BasicBlock *p_bb) :
    bb(p_bb),
    type(ActionComplex),
    stores(0), loads(0), RMWs(0), calls(0), soleLoad(nullptr) {}
  void operator=(const Action &) LLVM_DELETED_FUNCTION;
  Action(const Action &) LLVM_DELETED_FUNCTION;
  Action(Action &&) = default; // move constructor!

  BasicBlock *bb;

  // Some basic info about what sort of instructions live in the action
  ActionType type;
  int stores;
  int loads;
  int RMWs;
  int calls;
  LoadInst *soleLoad;

  // Edges in the graph.
  // XXX: Would we be better off storing this some other way?
  // a <ptr, type> pair?
  // And should we store v edges in x
  SmallPtrSet<Action *, 2> execEdges;
  SmallPtrSet<Action *, 2> visEdges;
};

enum CutType {
  CutNone,
  CutCtrlIsync, // needs to be paired with a dep
  CutLwsync,
  CutSync
};
struct EdgeCut {
  EdgeCut() : type(CutNone), front(false), read(nullptr) {}
  EdgeCut(CutType ptype, bool pfront, Value *pread = nullptr)
    : type(ptype), front(pfront), read(pread) {}
  CutType type;
  bool front;
  Value *read;
};

///////////////////////////////////////////////////////////////////////////
// Code to find all simple paths between two basic blocks.
// Could generalize more to graphs if we wanted, but I don't right
// now.
typedef std::vector<BasicBlock *> Path;
typedef SmallVector<Path, 2> PathList;
typedef SmallPtrSet<BasicBlock *, 8> GreySet;

PathList findAllSimplePaths_(GreySet *grey, BasicBlock *src, BasicBlock *dst) {
  PathList paths;
  if (grey->count(src)) return paths;
  if (src == dst) {
    Path path;
    path.push_back(dst);
    paths.push_back(std::move(path));
    return paths;
  }

  grey->insert(src);

  // Go search all the successors
  for (auto i = succ_begin(src), e = succ_end(src); i != e; i++) {
    PathList subpaths = findAllSimplePaths_(grey, *i, dst);
    std::move(subpaths.begin(), subpaths.end(), back_inserter(paths));
  }

  // Add our step to all of the vectors
  for (auto & path : paths) {
    path.push_back(src);
  }

  // Remove it from the set of things we've seen. We might come
  // through here again.
  // We can't really do any sort of memoization, since in a cyclic
  // graph the possible simple paths depend not just on what node we
  // are on, but our previous path (to avoid looping).
  grey->erase(src);

  return paths;
}

PathList findAllSimplePaths(BasicBlock *src, BasicBlock *dst) {
  GreySet grey;
  return findAllSimplePaths_(&grey, src, dst);
}

void dumpPaths(const PathList &paths) {
  for (auto & path : paths) {
    for (auto block : path) {
      errs() << block->getName() << " <- ";
    }
    errs() << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////
// XXX: This is the wrong way to do this!
bool targetARM = true;
bool targetx86 = false;

// Some llvm nonsense. I should probably find a way to clean this up.
// do we put ~{dirflag},~{fpsr},~{flags} for the x86 ones? don't think so.
Instruction *makeSync(Instruction *to_precede) {
  LLVMContext &C = to_precede->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb #sync", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "msync #sync", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "sync", to_precede);
}
Instruction *makeLwsync(Instruction *to_precede) {
  LLVMContext &C = to_precede->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb #lwsync", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "#lwsync", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "", to_precede);
}
Instruction *makeCtrlIsync(Value *v, Instruction *to_precede) {
  LLVMContext &C = v->getContext();
  FunctionType *f_ty =
    FunctionType::get(FunctionType::getVoidTy(C), v->getType(), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "cmp $0, $0;beq 1f;1: isb #ctrlisync",
                       "r,~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "#ctrlisync", "r,~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "", to_precede);
}
// We also need to add a thing for fake data deps, which is more annoying.

///////////////////////////////////////////////////////////////////////////
//// Actual code for the pass

class RealizeRMC : public FunctionPass {
private:
  std::vector<Action> actions_;
  DenseMap<BasicBlock *, Action *> bb2action_;
  DenseMap<BasicBlock *, EdgeCut> cuts_;

  std::vector<RMCEdge> findEdges(Function &F);
  void buildGraph(Function &F, std::vector<RMCEdge> &edges);
  void cutEdges(Function &F, std::vector<RMCEdge> &edges);
  void cutEdge(Function &F, Action *src, Action *dst, RMCEdgeType type);

  // Clear our data structures to save memory, make things clean for
  // future runs.
  void clear() {
    actions_.clear();
    bb2action_.clear();
    cuts_.clear();
  }

public:
  static char ID;
  RealizeRMC() : FunctionPass(ID) {

  }
  ~RealizeRMC() { }
  virtual bool runOnFunction(Function &F);

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequiredID(BreakCriticalEdgesID);
    AU.addRequired<UnifyFunctionExitNodes>();
    AU.setPreservesCFG();
  }

};

bool nameMatches(StringRef blockName, StringRef target) {
  StringRef name = "_rmc_" + target.str() + "_";
  if (!blockName.startswith(name)) return false;
  // Now make sure the rest is an int
  APInt dummy;
  return !blockName.drop_front(name.size()).getAsInteger(10, dummy);
}

StringRef getStringArg(Value *v) {
  const Value *g = cast<Constant>(v)->stripPointerCasts();
  const Constant *ptr = cast<GlobalVariable>(g)->getInitializer();
  StringRef str = cast<ConstantDataSequential>(ptr)->getAsString();
  // Drop the '\0' at the end
  return str.drop_back();
}

std::vector<RMCEdge> RealizeRMC::findEdges(Function &F) {
  std::vector<RMCEdge> edges;

  for (inst_iterator is = inst_begin(F), ie = inst_end(F); is != ie;) {
    // Grab the instruction and advance the iterator at the start, since
    // we might delete the instruction.
    Instruction *i = &*is;
    is++;

    CallInst *load = dyn_cast<CallInst>(i);
    if (!load) continue;
    Function *target = load->getCalledFunction();
    // We look for calls to the bogus function
    // __rmc_edge_register, pull out the information about them,
    // and delete the calls.
    if (!target || target->getName() != "__rmc_edge_register") continue;

    // Pull out what the operands have to be.
    // We just assert if something is wrong, which is not great UX.
    bool isVisibility = cast<ConstantInt>(load->getOperand(0))
      ->getValue().getBoolValue();
    RMCEdgeType edgeType = isVisibility ? VisbilityEdge : ExecutionEdge;
    StringRef srcName = getStringArg(load->getOperand(1));
    StringRef dstName = getStringArg(load->getOperand(2));

    // Since multiple blocks can have the same tag, we search for
    // them by name.
    // We could make this more efficient by building maps but I don't think
    // it is going to matter.
    for (auto & src : F) {
      if (!nameMatches(src.getName(), srcName)) continue;
      for (auto & dst : F) {
        if (!nameMatches(dst.getName(), dstName)) continue;

        edges.push_back({edgeType, &src, &dst});
      }
    }

    // Delete the bogus call.
    i->eraseFromParent();
  }

  return edges;
}

void analyzeAction(Action &info) {
  LoadInst *soleLoad = nullptr;
  for (auto & i : *info.bb) {
    if (LoadInst *load = dyn_cast<LoadInst>(&i)) {
      info.loads++;
      soleLoad = load;
    } else if (isa<StoreInst>(i)) {
      info.stores++;
    // What else counts as a call? I'm counting fences I guess.
    } else if (isa<CallInst>(i) || isa<FenceInst>(i)) {
      info.calls++;
    } else if (isa<AtomicCmpXchgInst>(i) || isa<AtomicRMWInst>(i)) {
      info.RMWs++;
    }
  }
  // Try to characterize what this action does.
  // These categories might not be the best.
  if (info.loads == 1 && info.stores+info.calls+info.RMWs == 0) {
    info.soleLoad = soleLoad;
    info.type = ActionSimpleRead;
  } else if (info.stores >= 1 && info.loads+info.calls+info.RMWs == 0) {
    info.type = ActionSimpleWrites;
  } else if (info.RMWs == 1 && info.stores+info.loads+info.calls == 0) {
    info.type = ActionSimpleRMW;
  } else {
    info.type = ActionComplex;
  }
}

void RealizeRMC::buildGraph(Function &F, std::vector<RMCEdge> &edges) {
  // First, collect all the basic blocks with edges attached to them
  SmallPtrSet<BasicBlock *, 8> basicBlocks;
  for (auto & edge : edges) {
    basicBlocks.insert(edge.src);
    basicBlocks.insert(edge.dst);
  }

  // Now, make the vector of actions and a mapping from BasicBlock *.
  actions_.reserve(basicBlocks.size());
  for (auto bb : basicBlocks) {
    actions_.emplace_back(bb);
    bb2action_[bb] = &actions_.back();
  }

  // Analyze the instructions in actions to see what they do.
  for (auto & action : actions_) {
    analyzeAction(action);
  }

  // Build our list of edges into a more explicit graph
  for (auto & edge : edges) {
    Action *src = bb2action_[edge.src];
    Action *dst = bb2action_[edge.dst];
    if (edge.edgeType == VisbilityEdge) {
      src->visEdges.insert(dst);
    } else {
      src->execEdges.insert(dst);
    }
  }
}

void RealizeRMC::cutEdge(Function &F,
                         Action *src, Action *dst, RMCEdgeType type) {

  // As a first pass, we just insert lwsyncs at the start of the destination.
  BasicBlock *bb = dst->bb;
  makeLwsync(&bb->front());
  cuts_[bb] = EdgeCut(CutLwsync, true);
}

void RealizeRMC::cutEdges(Function &F, std::vector<RMCEdge> &edges) {
  // Maybe we should actually use the graph structure we built?
  for (auto & edge : edges) {
    cutEdge(F, bb2action_[edge.src], bb2action_[edge.dst], edge.edgeType);
  }
}

bool RealizeRMC::runOnFunction(Function &F) {

  auto edges = findEdges(F);
  if (edges.empty()) return false;

  for (auto & edge : edges) {
    errs() << "Found an edge: " << edge << "\n";
  }

  //UnifyFunctionExitNodes &EN = getAnalysis<UnifyFunctionExitNodes>();
  buildGraph(F, edges);
  cutEdges(F, edges);

  clear();
  return true;
}

char RealizeRMC::ID = 0;
RegisterPass<RealizeRMC> X("realize-rmc", "Compile RMC annotations");
}
<|endoftext|>


<commit_before>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#pragma once
#include"ork/ork.hpp"

namespace ork {

enum class align {
	  left
	, right
	, center
};

enum class border {
	  none
	, inside
	, outside
	, both
};

namespace html {

//Base class for html stuff
struct exportable {
public:
	bstring style = BORK("");
	bool col_span = false;//Used only if this is inside a table
public:
	virtual ~exportable() {}
	exportable() {}
	exportable(const bstring&style_) :style(style_) {}
	exportable(const bstring&style_, const bool span) :style(style_), col_span(span) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const = 0;
};

struct pair : public exportable {
public:
	bstring key;
	bstring value;
public:
	pair(const bstring&key_, const bstring&val) :key(key_), value(val) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
	static bstring export_html(const bstring&key, const bstring&val);
};

struct string : public exportable {
public:
	bstring text = BORK("");
public:
	string() {}
	string(const bstring&style, const bstring&text_) :exportable(style), text(text_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct heading : public string {
public:
	unsigned level = 1;
public:
	heading(const bstring&style, const bstring&text, const unsigned level_) :string(style, text), level(level_) {}
	heading(const bstring&style, const bstring&text, const unsigned level_, const unsigned pct_width) :string(style, text), level(level_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct page_break : public exportable {
public:
public:
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct padding {
public:
	int top = 0;
	int right = 0;
	int bottom = 0;
	int left = 0;
public:
	padding() {}
	padding(const int t, const int r, const int b, const int l) :top(t), right(r), bottom(b), left(l) {}
public:
	bstring export_html()const;
};

struct image : public exportable {
public:
	bstring source = BORK("");
	bstring alt_text = BORK("");
	unsigned width = 0;
	unsigned height = 0;
public:
	image() {}
	image(const bstring&src, const bstring&alt, const bstring&image_style, const unsigned w, const unsigned h) :exportable(image_style), source(src), alt_text(alt), width(w), height(h) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct style : public exportable {
public:
	static const unsigned bold = 0x1<<0;
	static const unsigned serif = 0x1<<1;
	static const unsigned overline = 0x1 << 2;
	static const unsigned thruline = 0x1 << 3;
	static const unsigned underline = 0x1 << 4;
	static const unsigned italic = 0x1 << 5;
public:
	bstring name = BORK("");
	int size_pct = 100;
	int width = 0;
	unsigned flags = 0;
	bstring color = BORK("000000");
	bstring background = BORK("");
	padding pad;
public:
	style(const bstring&name_, const int size_pct_, const int width_, const unsigned flags_);
	style(const bstring&name_, const int size_pct_, const int width_, const unsigned flags_, const bstring&color_, const bstring&background_, const padding&pad_);
public:
	bool decorated()const;
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct image_style : public exportable {
public:
	bstring name = BORK("");
	align alignment = align::left;
	padding pad;
public:
	image_style(const bstring&name_, const align a, const padding&pad_) : name(name_), alignment(a), pad(pad_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct div_style : public exportable {
public:
	bstring name = BORK("");
	unsigned width_px = 0;
	align alignment = align::left;
	bstring back_color = BORK("");
public:
	div_style(const bstring&name_, const unsigned width_px_, const align a, const bstring&color);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct table_style : public exportable {
public:
	bstring name = BORK("");
	unsigned border_width = 0;
	border borders;
	bool only_bottom = false;
	bstring color = BORK("000000");
	unsigned width_pct = 0;
	unsigned height_px = 0;
	unsigned padding_px = 0;
public:
	table_style(
		  const bstring&name_
		, const unsigned border_width_
		, const border bord
		, const bstring&color_
		, const unsigned width_pct_
		, const unsigned height_px_
		, const unsigned padding_px_
	);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct line_style : public exportable {
public:
	bstring name = BORK("");
	unsigned width_pct = 0;
	align alignment = align::left;
	bstring color = BORK("000000");
	unsigned top_pad = 0;
	unsigned bottom_pad = 0;
	unsigned height_px = 0;
public:
	line_style(const bstring&name_, const unsigned width, const align a, const bstring&color_, const unsigned top_px, const unsigned bottom_px, const unsigned height_px_);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct style_set : public exportable {
public:
	std::vectorstyles;
	std::vector<image_style>image_styles;
	std::vector<div_style>div_styles;
	std::vector<table_style>table_styles;
	std::vector<line_style>line_styles;
public:
	style_set() {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct header : public exportable {
public:
	bstring author = BORK("");
	bstring description = BORK("");
	bstring encoding = BORK("UTF-8");
	bstring title = BORK("");
	style_set styles;
public:
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct line : public exportable {
public:
	//unsigned width_px = 80;
	//bool align_left = false;
public:
	line(const bstring&line_style_/*, const unsigned width, const bool left*/) :exportable(line_style_)/*, width_px(width), align_left(left)*/ {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct label : public exportable {
public:
	bstring text;
public:
	label(const bstring&style, const bstring&text_) :exportable(style, true), text(text_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct table_element {
	static void export_html(const exportable*element, const size_t remain_col, std::ostream&out);
};

struct table : public exportable {
public:
	typedef std::unique_ptr<exportable>ptr_type;
	typedef std::vector<ptr_type>row_type;
public:
	unsigned padding = 0;
	unsigned spacing = 0;
protected:
	bstring _table_style = BORK("");
	size_t _num_column;
	std::vector<row_type>_rows;
public:
	table(const bstring&style, const unsigned num_column) :_table_style(style), _num_column(num_column) {}
	table(const bstring&style, const unsigned num_column, const unsigned pad, const unsigned space) :_table_style(style), _num_column(num_column), padding(pad), spacing(space) {}
public:
	size_t num_column()const {
		return _num_column;
	}
	void insert_row();
	void insert_label(const bstring&style, const bstring&text);
	row_type&at(const size_t index);
	const row_type&at(const size_t index)const;
	row_type&back();
	const row_type&back()const;
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct div : public exportable {
public:
	typedef std::unique_ptr<exportable>ptr_type;
public:
	std::vector<ptr_type>elements;
public:
	div(const bstring&div_style) :exportable(div_style) {}
	ORK_MOVE_ONLY_1(div, elements);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct body : public exportable {
public:
	std::vector
divs;
public:
	body() {}
	ORK_MOVE_ONLY_1(body, divs);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct document : public exportable {
public:
	header head;
	html::body body;
public:
	document() {}
	document(const header&head_, html::body&&body_) :head(head_), body(std::move(body_)) {}
	ORK_MOVE_ONLY_2(document, head, body);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

}//namespace html
}//namespace ork
<commit_msg>Marked html types for export<commit_after>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#pragma once
#include"ork/ork.hpp"

#if ORK_MSC
#pragma warning(push)
#pragma warning(disable : 4251) //DLL interface for X needs DLL interface for Y
#endif

namespace ork {

enum class align {
	  left
	, right
	, center
};

enum class border {
	  none
	, inside
	, outside
	, both
};

namespace html {

//Base class for html stuff
struct ORK_ORK_API exportable {
public:
	bstring style = BORK("");
	bool col_span = false;//Used only if this is inside a table
public:
	virtual ~exportable() {}
	exportable() {}
	explicit exportable(const bstring&style_) :style(style_) {}
	exportable(const bstring&style_, const bool span) :style(style_), col_span(span) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const = 0;
};

struct ORK_ORK_API pair : public exportable {
public:
	bstring key;
	bstring value;
public:
	pair(const bstring&key_, const bstring&val) :key(key_), value(val) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
	static bstring export_html(const bstring&key, const bstring&val);
};

struct ORK_ORK_API string : public exportable {
public:
	bstring text = BORK("");
public:
	string() {}
	string(const bstring&style, const bstring&text_) :exportable(style), text(text_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API heading : public string {
public:
	unsigned level = 1;
public:
	heading(const bstring&style, const bstring&text, const unsigned level_) :string(style, text), level(level_) {}
	heading(const bstring&style, const bstring&text, const unsigned level_, const unsigned pct_width) :string(style, text), level(level_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API page_break : public exportable {
public:
public:
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API padding {
public:
	int top = 0;
	int right = 0;
	int bottom = 0;
	int left = 0;
public:
	padding() {}
	padding(const int t, const int r, const int b, const int l) :top(t), right(r), bottom(b), left(l) {}
public:
	bstring export_html()const;
};

struct ORK_ORK_API image : public exportable {
public:
	bstring source = BORK("");
	bstring alt_text = BORK("");
	unsigned width = 0;
	unsigned height = 0;
public:
	image() {}
	image(const bstring&src, const bstring&alt, const bstring&image_style, const unsigned w, const unsigned h) :exportable(image_style), source(src), alt_text(alt), width(w), height(h) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API style : public exportable {
public:
	static const unsigned bold = 0x1<<0;
	static const unsigned serif = 0x1<<1;
	static const unsigned overline = 0x1 << 2;
	static const unsigned thruline = 0x1 << 3;
	static const unsigned underline = 0x1 << 4;
	static const unsigned italic = 0x1 << 5;
public:
	bstring name = BORK("");
	int size_pct = 100;
	int width = 0;
	unsigned flags = 0;
	bstring color = BORK("000000");
	bstring background = BORK("");
	padding pad;
public:
	style(const bstring&name_, const int size_pct_, const int width_, const unsigned flags_);
	style(const bstring&name_, const int size_pct_, const int width_, const unsigned flags_, const bstring&color_, const bstring&background_, const padding&pad_);
public:
	bool decorated()const;
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API image_style : public exportable {
public:
	bstring name = BORK("");
	align alignment = align::left;
	padding pad;
public:
	image_style(const bstring&name_, const align a, const padding&pad_) : name(name_), alignment(a), pad(pad_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API div_style : public exportable {
public:
	bstring name = BORK("");
	unsigned width_px = 0;
	align alignment = align::left;
	bstring back_color = BORK("");
public:
	div_style(const bstring&name_, const unsigned width_px_, const align a, const bstring&color);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API table_style : public exportable {
public:
	bstring name = BORK("");
	unsigned border_width = 0;
	border borders;
	bool only_bottom = false;
	bstring color = BORK("000000");
	unsigned width_pct = 0;
	unsigned height_px = 0;
	unsigned padding_px = 0;
public:
	table_style(
		  const bstring&name_
		, const unsigned border_width_
		, const border bord
		, const bstring&color_
		, const unsigned width_pct_
		, const unsigned height_px_
		, const unsigned padding_px_
	);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API line_style : public exportable {
public:
	bstring name = BORK("");
	unsigned width_pct = 0;
	align alignment = align::left;
	bstring color = BORK("000000");
	unsigned top_pad = 0;
	unsigned bottom_pad = 0;
	unsigned height_px = 0;
public:
	line_style(const bstring&name_, const unsigned width, const align a, const bstring&color_, const unsigned top_px, const unsigned bottom_px, const unsigned height_px_);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API style_set : public exportable {
public:
	std::vectorstyles;
	std::vector<image_style>image_styles;
	std::vector<div_style>div_styles;
	std::vector<table_style>table_styles;
	std::vector<line_style>line_styles;
public:
	style_set() {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API header : public exportable {
public:
	bstring author = BORK("");
	bstring description = BORK("");
	bstring encoding = BORK("UTF-8");
	bstring title = BORK("");
	style_set styles;
public:
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API line : public exportable {
public:
	//unsigned width_px = 80;
	//bool align_left = false;
public:
	line(const bstring&line_style_/*, const unsigned width, const bool left*/) :exportable(line_style_)/*, width_px(width), align_left(left)*/ {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API label : public exportable {
public:
	bstring text;
public:
	label(const bstring&style, const bstring&text_) :exportable(style, true), text(text_) {}
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API table_element {
	static void export_html(const exportable*element, const size_t remain_col, std::ostream&out);
};

struct ORK_ORK_API table : public exportable {
public:
	typedef std::unique_ptr<exportable>ptr_type;
	typedef std::vector<ptr_type>row_type;
public:
	unsigned padding = 0;
	unsigned spacing = 0;
protected:
	bstring _table_style = BORK("");
	size_t _num_column;
	std::vector<row_type>_rows;
public:
	table(const bstring&style, const unsigned num_column) :_table_style(style), _num_column(num_column) {}
	table(const bstring&style, const unsigned num_column, const unsigned pad, const unsigned space) :_table_style(style), _num_column(num_column), padding(pad), spacing(space) {}
	ORK_MOVE_ONLY_5(table, padding, spacing, _table_style, _num_column, _rows);
public:
	size_t num_column()const {
		return _num_column;
	}
	void insert_row();
	void insert_label(const bstring&style, const bstring&text);
	row_type&at(const size_t index);
	const row_type&at(const size_t index)const;
	row_type&back();
	const row_type&back()const;
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API div : public exportable {
public:
	typedef std::unique_ptr<exportable>ptr_type;
public:
	std::vector<ptr_type>elements;
public:
	div(const bstring&div_style) :exportable(div_style) {}
	ORK_MOVE_ONLY_1(div, elements);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API body : public exportable {
public:
	std::vector
divs;
public:
	body() {}
	ORK_MOVE_ONLY_1(body, divs);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

struct ORK_ORK_API document : public exportable {
public:
	header head;
	html::body body;
public:
	document() {}
	document(const header&head_, html::body&&body_) :head(head_), body(std::move(body_)) {}
	ORK_MOVE_ONLY_2(document, head, body);
public:
	virtual std::ostream& export_html(std::ostream&out)const override;
};

}//namespace html
}//namespace ork

#if ORK_MSC
#pragma warning(pop)
#endif
<|endoftext|>


<commit_before>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#include "TestingEnvironment.hpp"
#include "ShaderMacroHelper.hpp"

#include "Vulkan/TestingEnvironmentVk.hpp"

#include "gtest/gtest.h"

using namespace Diligent;
using namespace Diligent::Testing;

namespace Diligent
{

TEST(WaveOpTest, CompileShader_HLSL)
{
    auto* const pEnv    = TestingEnvironment::GetInstance();
    auto* const pDevice = pEnv->GetDevice();

    if (!pDevice->GetDeviceCaps().Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }
    if (!pEnv->HasDXCompiler())
    {
        GTEST_SKIP() << "HLSL source code with wave operations can be compiled only by DXC";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetDeviceProperties().WaveOp;

    auto WaveOpFeatures = WaveOpProps.Features;

    Uint32 DxcMajorVer = 0;
    Uint32 DxcMinorVer = 0;
    pEnv->GetDXCompilerVersion(DxcMajorVer, DxcMinorVer);
    if (!(DxcMajorVer >= 2 || DxcMajorVer >= 1 && DxcMinorVer >= 5))
    {
        // There is a bug in older versions of DXC that causes the following error:
        //      opcode 'QuadReadAcross' should only be used in 'Pixel Shader'
        WaveOpFeatures &= ~WAVE_FEATURE_QUAD;
    }

    ASSERT_NE(WaveOpFeatures, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpFeatures & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    ShaderMacroHelper Macros;
    Macros.AddShaderMacro("SUBGROUP_SIZE", WaveOpProps.MinSize);

    // clang-format off
    Macros.AddShaderMacro("WAVE_FEATURE_BASIC",      (WaveOpFeatures & WAVE_FEATURE_BASIC)      != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_VOTE",       (WaveOpFeatures & WAVE_FEATURE_VOTE)       != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_ARITHMETIC", (WaveOpFeatures & WAVE_FEATURE_ARITHMETIC) != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_BALLOUT",    (WaveOpFeatures & WAVE_FEATURE_BALLOUT)    != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_QUAD",       (WaveOpFeatures & WAVE_FEATURE_QUAD)       != 0);
    // clang-format on

    static const char Source[] = R"(
RWByteAddressBuffer g_RWBuffer;

[numthreads(SUBGROUP_SIZE, 1, 1)]
void main(uint DTid : SV_DispatchThreadID)
{
    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = WaveGetLaneCount();
        uint  lineIndex = WaveGetLaneIndex();
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (WaveActiveAllTrue(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = WaveActiveSum(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = WaveActiveCountBits((DTid & 2) == 0);
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = QuadReadAcrossDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_RWBuffer.Store(DTid, Accum);
}
)";

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_HLSL;
    ShaderCI.ShaderCompiler  = SHADER_COMPILER_DXC;
    ShaderCI.HLSLVersion     = {6, 0};
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source;
    ShaderCI.Macros          = Macros;

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

TEST(WaveOpTest, CompileShader_GLSL)
{
    auto* const pEnv    = TestingEnvironment::GetInstance();
    auto* const pDevice = pEnv->GetDevice();
    const auto& Caps    = pDevice->GetDeviceCaps();

    if (!Caps.IsVulkanDevice() && !Caps.IsGLDevice())
    {
        GTEST_SKIP();
    }
    if (!Caps.Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetDeviceProperties().WaveOp;

    ASSERT_NE(WaveOpProps.Features, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.Features & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    std::stringstream ShaderSourceStream;
    ShaderSourceStream << "#version 450\n\n";
    ShaderSourceStream << "#define SUBGROUP_SIZE " << WaveOpProps.MinSize << "\n";

    // clang-format off
    ShaderSourceStream << "#define WAVE_FEATURE_BASIC "            << int{(WaveOpProps.Features & WAVE_FEATURE_BASIC)            != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_VOTE "             << int{(WaveOpProps.Features & WAVE_FEATURE_VOTE)             != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_ARITHMETIC "       << int{(WaveOpProps.Features & WAVE_FEATURE_ARITHMETIC)       != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_BALLOUT "          << int{(WaveOpProps.Features & WAVE_FEATURE_BALLOUT)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE "          << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE_RELATIVE " << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE_RELATIVE) != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_CLUSTERED "        << int{(WaveOpProps.Features & WAVE_FEATURE_CLUSTERED)        != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_QUAD "             << int{(WaveOpProps.Features & WAVE_FEATURE_QUAD)             != 0} << "\n";
    // clang-format on

    static const char ShaderBody[] = R"(
#if WAVE_FEATURE_BASIC
#    extension GL_KHR_shader_subgroup_basic: enable
#endif
#if WAVE_FEATURE_VOTE
#    extension GL_KHR_shader_subgroup_vote: enable
#endif
#if WAVE_FEATURE_BALLOUT
#    extension GL_KHR_shader_subgroup_ballot: enable
#endif
#if WAVE_FEATURE_ARITHMETIC
#    extension GL_KHR_shader_subgroup_arithmetic: enable
#endif
#if WAVE_FEATURE_SHUFFLE
#    extension GL_KHR_shader_subgroup_shuffle: enable
#endif
#if WAVE_FEATURE_SHUFFLE_RELATIVE
#    extension GL_KHR_shader_subgroup_shuffle_relative: enable
#endif
#if WAVE_FEATURE_CLUSTERED
#    extension GL_KHR_shader_subgroup_clustered: enable
#endif
#if WAVE_FEATURE_QUAD
#    extension GL_KHR_shader_subgroup_quad: enable
#endif

layout(local_size_x = SUBGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;

layout(std140) writeonly buffer WBuffer
{
    uint g_WBuffer[];
};

void main()
{
    const uint DTid = gl_LocalInvocationID.x;

    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = gl_SubgroupSize;
        uint  lineIndex = gl_SubgroupInvocationID;
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (subgroupAll(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = subgroupAdd(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = subgroupBallotExclusiveBitCount(subgroupBallot((DTid & 1) == 0));
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE
    {
        vec4 temp      = vec4(float(DTid));
        vec4 blendWith = subgroupShuffle(temp, (DTid + 5) & 7);
        Accum += (dot(temp, temp) < 0.0 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE_RELATIVE
    {
        vec4 temp = vec4(float(DTid));
        for (uint i = 2; i < gl_SubgroupSize; i *= 2)
        {
            vec4 other = subgroupShuffleUp(temp, i);

            if (i <= gl_SubgroupInvocationID)
                temp = temp * other;
        }
        Accum += (dot(temp, temp) > 0.5 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_CLUSTERED
    {
        uint maxId = subgroupClusteredMax(DTid, 4*4);
        Accum += (maxId+1 == SUBGROUP_SIZE ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = subgroupQuadSwapDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_WBuffer[DTid] = Accum;
}
)";
    ShaderSourceStream << ShaderBody;
    const String Source = ShaderSourceStream.str();

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM;
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source.c_str();

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

} // namespace Diligent
<commit_msg>Fixed clang warnings in WaveOpTest<commit_after>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#include "TestingEnvironment.hpp"
#include "ShaderMacroHelper.hpp"

#include "Vulkan/TestingEnvironmentVk.hpp"

#include "gtest/gtest.h"

using namespace Diligent;
using namespace Diligent::Testing;

namespace Diligent
{

TEST(WaveOpTest, CompileShader_HLSL)
{
    auto* const pEnv    = TestingEnvironment::GetInstance();
    auto* const pDevice = pEnv->GetDevice();

    if (!pDevice->GetDeviceCaps().Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }
    if (!pEnv->HasDXCompiler())
    {
        GTEST_SKIP() << "HLSL source code with wave operations can be compiled only by DXC";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetDeviceProperties().WaveOp;

    auto WaveOpFeatures = WaveOpProps.Features;

    Uint32 DxcMajorVer = 0;
    Uint32 DxcMinorVer = 0;
    pEnv->GetDXCompilerVersion(DxcMajorVer, DxcMinorVer);
    if (!(DxcMajorVer >= 2 || (DxcMajorVer == 1 && DxcMinorVer >= 5)))
    {
        // There is a bug in older versions of DXC that causes the following error:
        //      opcode 'QuadReadAcross' should only be used in 'Pixel Shader'
        WaveOpFeatures &= ~WAVE_FEATURE_QUAD;
    }

    ASSERT_NE(WaveOpFeatures, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpFeatures & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    ShaderMacroHelper Macros;
    Macros.AddShaderMacro("SUBGROUP_SIZE", WaveOpProps.MinSize);

    // clang-format off
    Macros.AddShaderMacro("WAVE_FEATURE_BASIC",      (WaveOpFeatures & WAVE_FEATURE_BASIC)      != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_VOTE",       (WaveOpFeatures & WAVE_FEATURE_VOTE)       != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_ARITHMETIC", (WaveOpFeatures & WAVE_FEATURE_ARITHMETIC) != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_BALLOUT",    (WaveOpFeatures & WAVE_FEATURE_BALLOUT)    != 0);
    Macros.AddShaderMacro("WAVE_FEATURE_QUAD",       (WaveOpFeatures & WAVE_FEATURE_QUAD)       != 0);
    // clang-format on

    static const char Source[] = R"(
RWByteAddressBuffer g_RWBuffer;

[numthreads(SUBGROUP_SIZE, 1, 1)]
void main(uint DTid : SV_DispatchThreadID)
{
    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = WaveGetLaneCount();
        uint  lineIndex = WaveGetLaneIndex();
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (WaveActiveAllTrue(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = WaveActiveSum(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = WaveActiveCountBits((DTid & 2) == 0);
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = QuadReadAcrossDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_RWBuffer.Store(DTid, Accum);
}
)";

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_HLSL;
    ShaderCI.ShaderCompiler  = SHADER_COMPILER_DXC;
    ShaderCI.HLSLVersion     = {6, 0};
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source;
    ShaderCI.Macros          = Macros;

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

TEST(WaveOpTest, CompileShader_GLSL)
{
    auto* const pEnv    = TestingEnvironment::GetInstance();
    auto* const pDevice = pEnv->GetDevice();
    const auto& Caps    = pDevice->GetDeviceCaps();

    if (!Caps.IsVulkanDevice() && !Caps.IsGLDevice())
    {
        GTEST_SKIP();
    }
    if (!Caps.Features.WaveOp)
    {
        GTEST_SKIP() << "Wave operations are not supported by this device";
    }

    TestingEnvironment::ScopedReset EnvironmentAutoReset;

    const auto& WaveOpProps = pDevice->GetDeviceProperties().WaveOp;

    ASSERT_NE(WaveOpProps.Features, WAVE_FEATURE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.Features & WAVE_FEATURE_BASIC) != 0);

    ASSERT_NE(WaveOpProps.SupportedStages, SHADER_TYPE_UNKNOWN);
    ASSERT_TRUE((WaveOpProps.SupportedStages & SHADER_TYPE_COMPUTE) != 0);

    ASSERT_GT(WaveOpProps.MinSize, 0u);
    ASSERT_GE(WaveOpProps.MaxSize, WaveOpProps.MinSize);

    std::stringstream ShaderSourceStream;
    ShaderSourceStream << "#version 450\n\n";
    ShaderSourceStream << "#define SUBGROUP_SIZE " << WaveOpProps.MinSize << "\n";

    // clang-format off
    ShaderSourceStream << "#define WAVE_FEATURE_BASIC "            << int{(WaveOpProps.Features & WAVE_FEATURE_BASIC)            != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_VOTE "             << int{(WaveOpProps.Features & WAVE_FEATURE_VOTE)             != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_ARITHMETIC "       << int{(WaveOpProps.Features & WAVE_FEATURE_ARITHMETIC)       != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_BALLOUT "          << int{(WaveOpProps.Features & WAVE_FEATURE_BALLOUT)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE "          << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE)          != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_SHUFFLE_RELATIVE " << int{(WaveOpProps.Features & WAVE_FEATURE_SHUFFLE_RELATIVE) != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_CLUSTERED "        << int{(WaveOpProps.Features & WAVE_FEATURE_CLUSTERED)        != 0} << "\n";
    ShaderSourceStream << "#define WAVE_FEATURE_QUAD "             << int{(WaveOpProps.Features & WAVE_FEATURE_QUAD)             != 0} << "\n";
    // clang-format on

    static const char ShaderBody[] = R"(
#if WAVE_FEATURE_BASIC
#    extension GL_KHR_shader_subgroup_basic: enable
#endif
#if WAVE_FEATURE_VOTE
#    extension GL_KHR_shader_subgroup_vote: enable
#endif
#if WAVE_FEATURE_BALLOUT
#    extension GL_KHR_shader_subgroup_ballot: enable
#endif
#if WAVE_FEATURE_ARITHMETIC
#    extension GL_KHR_shader_subgroup_arithmetic: enable
#endif
#if WAVE_FEATURE_SHUFFLE
#    extension GL_KHR_shader_subgroup_shuffle: enable
#endif
#if WAVE_FEATURE_SHUFFLE_RELATIVE
#    extension GL_KHR_shader_subgroup_shuffle_relative: enable
#endif
#if WAVE_FEATURE_CLUSTERED
#    extension GL_KHR_shader_subgroup_clustered: enable
#endif
#if WAVE_FEATURE_QUAD
#    extension GL_KHR_shader_subgroup_quad: enable
#endif

layout(local_size_x = SUBGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;

layout(std140) writeonly buffer WBuffer
{
    uint g_WBuffer[];
};

void main()
{
    const uint DTid = gl_LocalInvocationID.x;

    uint Accum = 0;
    #if WAVE_FEATURE_BASIC
    {
        uint  laneCount = gl_SubgroupSize;
        uint  lineIndex = gl_SubgroupInvocationID;
        Accum += (lineIndex % laneCount);
    }
    #endif
    #if WAVE_FEATURE_VOTE
    {
        if (subgroupAll(Accum > 0xFFFF))
            Accum += 1;
    }
    #endif
    #if WAVE_FEATURE_ARITHMETIC
    {
        uint sum = subgroupAdd(DTid);
        Accum += (sum & 1);
    }
    #endif
    #if WAVE_FEATURE_BALLOUT
    {
        uint count = subgroupBallotExclusiveBitCount(subgroupBallot((DTid & 1) == 0));
        Accum += (count & 1);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE
    {
        vec4 temp      = vec4(float(DTid));
        vec4 blendWith = subgroupShuffle(temp, (DTid + 5) & 7);
        Accum += (dot(temp, temp) < 0.0 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_SHUFFLE_RELATIVE
    {
        vec4 temp = vec4(float(DTid));
        for (uint i = 2; i < gl_SubgroupSize; i *= 2)
        {
            vec4 other = subgroupShuffleUp(temp, i);

            if (i <= gl_SubgroupInvocationID)
                temp = temp * other;
        }
        Accum += (dot(temp, temp) > 0.5 ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_CLUSTERED
    {
        uint maxId = subgroupClusteredMax(DTid, 4*4);
        Accum += (maxId+1 == SUBGROUP_SIZE ? 1 : 0);
    }
    #endif
    #if WAVE_FEATURE_QUAD
    {
        uint diag = subgroupQuadSwapDiagonal(DTid);
        Accum += (diag & 1);
    }
    #endif

    g_WBuffer[DTid] = Accum;
}
)";
    ShaderSourceStream << ShaderBody;
    const String Source = ShaderSourceStream.str();

    ShaderCreateInfo ShaderCI;
    ShaderCI.SourceLanguage  = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM;
    ShaderCI.Desc.ShaderType = SHADER_TYPE_COMPUTE;
    ShaderCI.Desc.Name       = "Wave op test - CS";
    ShaderCI.EntryPoint      = "main";
    ShaderCI.Source          = Source.c_str();

    RefCntAutoPtr<IShader> pCS;
    pDevice->CreateShader(ShaderCI, &pCS);
    ASSERT_NE(pCS, nullptr);

    ComputePipelineStateCreateInfo PSOCreateInfo;
    PSOCreateInfo.PSODesc.Name = "Wave op test";
    PSOCreateInfo.pCS          = pCS;

    RefCntAutoPtr<IPipelineState> pPSO;
    pDevice->CreateComputePipelineState(PSOCreateInfo, &pPSO);
    ASSERT_NE(pPSO, nullptr);
}

} // namespace Diligent
<|endoftext|>


<commit_before>#include <stdlib.h>
#include "aes.h"

namespace i2p
{
namespace crypto
{

#ifdef __x86_64__

	ECBCryptoAESNI::ECBCryptoAESNI ()
	{
		m_KeySchedule = m_UnalignedBuffer;
		uint8_t rem = ((uint64_t)m_KeySchedule) & 0x0f;
		if (rem)
			m_KeySchedule += (16 - rem);
	}

	#define KeyExpansion256 \
		"pshufd	$0xff, %%xmm2, %%xmm2 \n" \
		"movaps	%%xmm1, %%xmm4 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm1 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm1 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm1 \n" \
		"pxor %%xmm2, %%xmm1 \n" \
		"movups	%%xmm1, (%%rcx) \n" \
		"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
		"pshufd	$0xaa, %%xmm4, %%xmm2 \n" \
		"movaps	%%xmm3, %%xmm4 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm3 \n" \
	    "pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm3 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm3 \n" \
		"pxor %%xmm2, %%xmm3 \n" \
		"movups	%%xmm3, 16(%%rcx) \n" \
		"add $32, %%rcx \n"

	void ECBCryptoAESNI::ExpandKey (const uint8_t * key)
	{
		__asm__
		(
			"movups (%%rsi), %%xmm1 \n"
			"movups 16(%%rsi), %%xmm3 \n"
			"movups %%xmm1, (%%rdi) \n"
			"movups %%xmm3, 16(%%rdi) \n"
			"lea 32(%%rdi), %%rcx \n"
			"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
			// key expansion final
			"pshufd	$0xff, %%xmm2, %%xmm2 \n"
			"movaps	%%xmm1, %%xmm4 \n"
			"pslldq	$4, %%xmm4 \n"
			"pxor %%xmm4, %%xmm1 \n"
			"pslldq	$4, %%xmm4 \n"
			"pxor %%xmm4, %%xmm1 \n"
			"pslldq	$4, %%xmm4 \n"
			"pxor %%xmm4, %%xmm1 \n"
			"pxor %%xmm2, %%xmm1 \n"
			"movups	%%xmm1, (%%rcx) \n"
			: // output
			: "S" (key), "D" (m_KeySchedule) // input
			: "%rcx" // clogged
		);
	}

	void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out)
	{
		__asm__
		(
			"movups	(%%rsi), %%xmm0 \n"
			"pxor (%%rdx), %%xmm0 \n"
			"aesenc	16(%%rdx), %%xmm0 \n"
			"aesenc	32(%%rdx), %%xmm0 \n"
			"aesenc	48(%%rdx), %%xmm0 \n"
			"aesenc	64(%%rdx), %%xmm0 \n"
			"aesenc	80(%%rdx), %%xmm0 \n"
			"aesenc	96(%%rdx), %%xmm0 \n"
			"aesenc	112(%%rdx), %%xmm0 \n"
			"aesenc	128(%%rdx), %%xmm0 \n"
			"aesenc	144(%%rdx), %%xmm0 \n"
			"aesenc	160(%%rdx), %%xmm0 \n"
			"aesenc	176(%%rdx), %%xmm0 \n"
			"aesenc	192(%%rdx), %%xmm0 \n"
			"aesenc	208(%%rdx), %%xmm0 \n"
			"aesenclast	224(%%rdx), %%xmm0 \n"
			"movups	%%xmm0, (%%rdi) \n"
			: : "d" (m_KeySchedule), "S" (in), "D" (out)
		);
	}

	void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out)
	{
		__asm__
		(
			"movups	(%%rsi), %%xmm0 \n"
			"pxor 224(%%rdx), %%xmm0 \n"
			"aesdec	208(%%rdx), %%xmm0 \n"
			"aesdec	192(%%rdx), %%xmm0 \n"
			"aesdec	176(%%rdx), %%xmm0 \n"
			"aesdec	160(%%rdx), %%xmm0 \n"
			"aesdec	144(%%rdx), %%xmm0 \n"
			"aesdec	128(%%rdx), %%xmm0 \n"
			"aesdec	112(%%rdx), %%xmm0 \n"
			"aesdec	96(%%rdx), %%xmm0 \n"
			"aesdec	80(%%rdx), %%xmm0 \n"
			"aesdec	64(%%rdx), %%xmm0 \n"
			"aesdec	48(%%rdx), %%xmm0 \n"
			"aesdec	32(%%rdx), %%xmm0 \n"
			"aesdec	16(%%rdx), %%xmm0 \n"
			"aesdeclast (%%rdx), %%xmm0 \n"
			"movups	%%xmm0, (%%rdi) \n"
			: : "d" (m_KeySchedule), "S" (in), "D" (out)
		);
	}

	#define CallAESIMC(offset) \
		"movups "#offset"(%%rdx), %%xmm0 \n"	\
		"aesimc %%xmm0, %%xmm0 \n" \
		"movups %%xmm0, "#offset"(%%rdx) \n"

	void ECBDecryptionAESNI::SetKey (const uint8_t * key)
	{
		ExpandKey (key); // expand encryption key first
		// then  invert it using aesimc
		__asm__
		(
			CallAESIMC(16)
			CallAESIMC(32)
			CallAESIMC(48)
			CallAESIMC(64)
			CallAESIMC(80)
			CallAESIMC(96)
			CallAESIMC(112)
			CallAESIMC(128)
			CallAESIMC(144)
			CallAESIMC(160)
			CallAESIMC(176)
			CallAESIMC(192)
			CallAESIMC(208)
			: : "d" (m_KeySchedule)
		);
	}

#endif

	void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
	{
		for (int i = 0; i < numBlocks; i++)
		{
			m_LastBlock ^= in[i];
			m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock);
			out[i] = m_LastBlock;
		}
	}

	bool CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
	{
		div_t d = div (len, 16);
		if (d.rem) return false; // len is not multipple of 16
		Encrypt (d.quot, (const ChipherBlock *)in, (ChipherBlock *)out);
		return true;
	}

	void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
	{
		for (int i = 0; i < numBlocks; i++)
		{
			ChipherBlock tmp = in[i];
			m_ECBDecryption.Decrypt (in + i, out + i);
			out[i] ^= m_IV;
			m_IV = tmp;
		}
	}

	bool CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
	{
		div_t d = div (len, 16);
		if (d.rem) return false; // len is not multipple of 16
		Decrypt (d.quot, (const ChipherBlock *)in, (ChipherBlock *)out);
		return true;
	}
}
}

<commit_msg>eliminate hardcoded registers<commit_after>#include <stdlib.h>
#include "aes.h"

namespace i2p
{
namespace crypto
{

#ifdef __x86_64__

	ECBCryptoAESNI::ECBCryptoAESNI ()
	{
		m_KeySchedule = m_UnalignedBuffer;
		uint8_t rem = ((uint64_t)m_KeySchedule) & 0x0f;
		if (rem)
			m_KeySchedule += (16 - rem);
	}

	#define KeyExpansion256 \
		"pshufd	$0xff, %%xmm2, %%xmm2 \n" \
		"movaps	%%xmm1, %%xmm4 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm1 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm1 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm1 \n" \
		"pxor %%xmm2, %%xmm1 \n" \
		"movaps	%%xmm1, (%%rcx) \n" \
		"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
		"pshufd	$0xaa, %%xmm4, %%xmm2 \n" \
		"movaps	%%xmm3, %%xmm4 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm3 \n" \
	    "pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm3 \n" \
		"pslldq	$4, %%xmm4 \n" \
		"pxor %%xmm4, %%xmm3 \n" \
		"pxor %%xmm2, %%xmm3 \n" \
		"movaps	%%xmm3, 16(%%rcx) \n" \
		"add $32, %%rcx \n"

	void ECBCryptoAESNI::ExpandKey (const uint8_t * key)
	{
		__asm__
		(
			"movups (%[key]), %%xmm1 \n"
			"movups 16(%[key]), %%xmm3 \n"
			"movaps %%xmm1, (%[shed]) \n"
			"movaps %%xmm3, 16(%[shed]) \n"
			"lea 32(%[shed]), %%rcx \n"
			"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
			KeyExpansion256
			"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
			// key expansion final
			"pshufd	$0xff, %%xmm2, %%xmm2 \n"
			"movaps	%%xmm1, %%xmm4 \n"
			"pslldq	$4, %%xmm4 \n"
			"pxor %%xmm4, %%xmm1 \n"
			"pslldq	$4, %%xmm4 \n"
			"pxor %%xmm4, %%xmm1 \n"
			"pslldq	$4, %%xmm4 \n"
			"pxor %%xmm4, %%xmm1 \n"
			"pxor %%xmm2, %%xmm1 \n"
			"movups	%%xmm1, (%%rcx) \n"
			: // output
			: [key]"r"(key), [shed]"r"(m_KeySchedule) // input
			: "%rcx", "%xmm1", "%xmm2", "%xmm3", "%xmm4" // clogged
		);
	}

	void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out)
	{
		__asm__
		(
			"movups	(%[in]), %%xmm0 \n"
			"pxor (%[shed]), %%xmm0 \n"
			"aesenc	16(%[shed]), %%xmm0 \n"
			"aesenc	32(%[shed]), %%xmm0 \n"
			"aesenc	48(%[shed]), %%xmm0 \n"
			"aesenc	64(%[shed]), %%xmm0 \n"
			"aesenc	80(%[shed]), %%xmm0 \n"
			"aesenc	96(%[shed]), %%xmm0 \n"
			"aesenc	112(%[shed]), %%xmm0 \n"
			"aesenc	128(%[shed]), %%xmm0 \n"
			"aesenc	144(%[shed]), %%xmm0 \n"
			"aesenc	160(%[shed]), %%xmm0 \n"
			"aesenc	176(%[shed]), %%xmm0 \n"
			"aesenc	192(%[shed]), %%xmm0 \n"
			"aesenc	208(%[shed]), %%xmm0 \n"
			"aesenclast	224(%[shed]), %%xmm0 \n"
			"movups	%%xmm0, (%[out]) \n"
			: : [shed]"r"(m_KeySchedule), [in]"r"(in), [out]"r"(out) : "%xmm0"
		);
	}

	void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out)
	{
		__asm__
		(
			"movups	(%[in]), %%xmm0 \n"
			"pxor 224(%[shed]), %%xmm0 \n"
			"aesdec	208(%[shed]), %%xmm0 \n"
			"aesdec	192(%[shed]), %%xmm0 \n"
			"aesdec	176(%[shed]), %%xmm0 \n"
			"aesdec	160(%[shed]), %%xmm0 \n"
			"aesdec	144(%[shed]), %%xmm0 \n"
			"aesdec	128(%[shed]), %%xmm0 \n"
			"aesdec	112(%[shed]), %%xmm0 \n"
			"aesdec	96(%[shed]), %%xmm0 \n"
			"aesdec	80(%[shed]), %%xmm0 \n"
			"aesdec	64(%[shed]), %%xmm0 \n"
			"aesdec	48(%[shed]), %%xmm0 \n"
			"aesdec	32(%[shed]), %%xmm0 \n"
			"aesdec	16(%[shed]), %%xmm0 \n"
			"aesdeclast (%[shed]), %%xmm0 \n"
			"movups	%%xmm0, (%[out]) \n"
			: : [shed]"r"(m_KeySchedule), [in]"r"(in), [out]"r"(out) : "%xmm0"
		);
	}

	#define CallAESIMC(offset) \
		"movaps "#offset"(%[shed]), %%xmm0 \n"	\
		"aesimc %%xmm0, %%xmm0 \n" \
		"movaps %%xmm0, "#offset"(%[shed]) \n"

	void ECBDecryptionAESNI::SetKey (const uint8_t * key)
	{
		ExpandKey (key); // expand encryption key first
		// then  invert it using aesimc
		__asm__
		(
			CallAESIMC(16)
			CallAESIMC(32)
			CallAESIMC(48)
			CallAESIMC(64)
			CallAESIMC(80)
			CallAESIMC(96)
			CallAESIMC(112)
			CallAESIMC(128)
			CallAESIMC(144)
			CallAESIMC(160)
			CallAESIMC(176)
			CallAESIMC(192)
			CallAESIMC(208)
			: : [shed]"r"(m_KeySchedule) : "%xmm0"
		);
	}

#endif

	void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
	{
		for (int i = 0; i < numBlocks; i++)
		{
			m_LastBlock ^= in[i];
			m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock);
			out[i] = m_LastBlock;
		}
	}

	bool CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
	{
		div_t d = div (len, 16);
		if (d.rem) return false; // len is not multipple of 16
		Encrypt (d.quot, (const ChipherBlock *)in, (ChipherBlock *)out);
		return true;
	}

	void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
	{
		for (int i = 0; i < numBlocks; i++)
		{
			ChipherBlock tmp = in[i];
			m_ECBDecryption.Decrypt (in + i, out + i);
			out[i] ^= m_IV;
			m_IV = tmp;
		}
	}

	bool CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
	{
		div_t d = div (len, 16);
		if (d.rem) return false; // len is not multipple of 16
		Decrypt (d.quot, (const ChipherBlock *)in, (ChipherBlock *)out);
		return true;
	}
}
}

<|endoftext|>