<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

//===- cubin_creator.cc -----------------------------------------*- C++ -*-===//
//
// This file implements the function to compile a TF kernel function to a cubin.
//
//===----------------------------------------------------------------------===//
#include "tensorflow/compiler/mlir/tools/kernel_gen/cubin_creator.h"

#include <string>
#include <utility>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/strings/escaping.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/GPU/GPUDialect.h"  // from @llvm-project
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"  // from @llvm-project
#include "mlir/Dialect/StandardOps/IR/Ops.h"  // from @llvm-project
#include "mlir/IR/Dialect.h"  // from @llvm-project
#include "mlir/IR/Function.h"  // from @llvm-project
#include "mlir/IR/Operation.h"  // from @llvm-project
#include "mlir/IR/StandardTypes.h"  // from @llvm-project
#include "mlir/IR/Value.h"  // from @llvm-project
#include "mlir/Parser.h"  // from @llvm-project
#include "mlir/Pass/Pass.h"  // from @llvm-project
#include "mlir/Pass/PassManager.h"  // from @llvm-project
#include "mlir/Target/NVVMIR.h"  // from @llvm-project
#include "mlir/Transforms/DialectConversion.h"  // from @llvm-project
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_ops.h"
#include "tensorflow/compiler/mlir/xla/ir/hlo_ops.h"
#include "tensorflow/compiler/mlir/xla/transforms/passes.h"
#include "tensorflow/compiler/mlir/xla/transforms/rewriters.h"
#include "tensorflow/compiler/xla/debug_options_flags.h"
#include "tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.h"
#include "tensorflow/compiler/xla/service/gpu/stream_executor_util.h"
#include "tensorflow/compiler/xla/service/gpu/target_constants.h"
#include "tensorflow/compiler/xla/service/mlir_gpu/kernel_lowering.h"
#include "tensorflow/core/platform/cuda_libdevice_path.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/path.h"
#if GOOGLE_CUDA
#include "tensorflow/stream_executor/gpu/asm_compiler.h"
#endif

namespace {
using tensorflow::Status;
using xla::InternalError;
using xla::StatusOr;

StatusOr<std::string> GetLibdeviceDir(
    const xla::HloModuleConfig& hlo_module_config) {
  for (const std::string& cuda_root : tensorflow::CandidateCudaRoots(
           hlo_module_config.debug_options().xla_gpu_cuda_data_dir())) {
    std::string libdevice_dir =
        tensorflow::io::JoinPath(cuda_root, "nvvm", "libdevice");
    VLOG(2) << "Looking for libdevice at " << libdevice_dir;
    if (tensorflow::Env::Default()->IsDirectory(libdevice_dir).ok()) {
      VLOG(2) << "Found libdevice dir " << libdevice_dir;
      return libdevice_dir;
    }
  }
  return InternalError(
      "Can't find libdevice directory ${CUDA_DIR}/nvvm/libdevice");
}

struct MaterializeBroadcastsPass
    : public mlir::PassWrapper<MaterializeBroadcastsPass, mlir::FunctionPass> {
  void runOnFunction() override {
    mlir::ConversionTarget conversionTarget(getContext());
    mlir::OwningRewritePatternList conversionPatterns;

    // Consider the xla_hlo dialect legal for tests.
    conversionTarget.addLegalDialect<mlir::xla_hlo::XlaHloDialect>();
    // The conversion uses helpers from the Standard dialect.
    conversionTarget.addLegalDialect<mlir::StandardOpsDialect>();

    mlir::xla_hlo::SetupMaterializeBroadcastsLegality(&getContext(),
                                                      &conversionTarget);
    mlir::xla_hlo::PopulateMaterializeBroadcastsPatterns(&getContext(),
                                                         &conversionPatterns);

    if (failed(applyPartialConversion(getFunction(), conversionTarget,
                                      conversionPatterns))) {
      return signalPassFailure();
    }
  }
};

struct UnfuseBatchNormPass
    : public mlir::PassWrapper<UnfuseBatchNormPass, mlir::FunctionPass> {
  void runOnFunction() override {
    mlir::OwningRewritePatternList patterns;
    mlir::xla_hlo::PopulateUnfuseBatchNormPatterns(&getContext(), &patterns);
    mlir::applyPatternsAndFoldGreedily(getOperation(), patterns);
  }
};

Status LowerTfOpToLhloWithDynamicShapes(mlir::ModuleOp module) {
  mlir::PassManager pm(module.getContext());
  auto enable_if_vlog_is_on = [](mlir::Pass* pass, mlir::Operation* op) {
    return VLOG_IS_ON(1);
  };
  pm.enableIRPrinting(/*shouldPrintBeforePass=*/{},
                      /*shouldPrintAfterPass=*/enable_if_vlog_is_on,
                      /*printModuleScope=*/false,
                      /*printAfterOnlyOnChange=*/false, llvm::dbgs());
  pm.addNestedPass<mlir::FuncOp>(mlir::xla_hlo::createLegalizeTFPass(false));
  pm.addNestedPass<mlir::FuncOp>(
      absl::make_unique<MaterializeBroadcastsPass>());
  pm.addNestedPass<mlir::FuncOp>(absl::make_unique<UnfuseBatchNormPass>());
  pm.addPass(mlir::xla_hlo::createLegalizeToLhloPass(
      /*results_escape_functions=*/true));
  pm.addNestedPass<mlir::FuncOp>(mlir::xla_lhlo::createLhloCopyRemovalPass());

  if (failed(pm.run(module))) {
    return InternalError("Lowering TF to LHLO failed.");
  }
  return Status::OK();
}

struct PropagateStaticKnowledge
    : public mlir::PassWrapper<PropagateStaticKnowledge,
                               mlir::OperationPass<mlir::LLVM::LLVMFuncOp>> {
  explicit PropagateStaticKnowledge(mlir::FunctionType type,
                                    llvm::ArrayRef<uint32_t> same_shape_)
      : func_type(type), same_shape(same_shape_) {}

  void runOnOperation() override {
    // We know due to tensorflow ABI that the offset is always 0 and that the
    // innermost stride is always 1. To make this visible to the compiler,
    // we insert constants into the code and replace usages accordingly.
    // We do not change the signature so that we keep a somewhat stable ABI
    // that is easy to undertand by tools.
    mlir::LLVM::LLVMFuncOp func = getOperation();

    // This only works if the function is local and we can rewrite it.
    if (func.isExternal()) return;

    mlir::OpBuilder b(func.getBody());
    // Steal the LLVM representation of the index type from the third argument.
    auto index_type = func.getArgument(3).getType();
    mlir::Value one = b.create<mlir::LLVM::ConstantOp>(
        func.getLoc(), index_type, b.getIntegerAttr(b.getIndexType(), 1));
    mlir::Value zero = b.create<mlir::LLVM::ConstantOp>(
        func.getLoc(), index_type, b.getIntegerAttr(b.getIndexType(), 0));
    uint32_t arg_pos = 0;
    std::vector<uint32_t> positions;
    // Collect the agument and return types of the surrounding function.
    auto arg_types = llvm::to_vector<4>(llvm::concat<const mlir::Type>(
        func_type.getInputs(), func_type.getResults()));
    for (mlir::Type arg_type : arg_types) {
      if (!arg_type.isa<mlir::MemRefType>()) {
        func.emitError() << "argument of surrounding func is not ranked memref";
        signalPassFailure();
        return;
      }
      positions.push_back(arg_pos);
      // Replace the offset with zero. Offset is argument number 3.
      func.getArgument(arg_pos + 2).replaceAllUsesWith(zero);
      // Forward over base_ptr, aligned_ptr, offset, size and stride arguments.
      arg_pos += 3 + arg_type.cast<mlir::MemRefType>().getRank() * 2;
      // Replace the last stride with constant 1.
      func.getArgument(arg_pos - 1).replaceAllUsesWith(one);
    }

    // If we have knowledge that some arguments have the same shape, we
    // can use that here. Simply replace usages of the shape parameters within
    // the function body to a single shape parameter.
    if (!same_shape.empty()) {
      auto first = same_shape.front();
      auto first_offset = positions.at(first);
      auto first_type = arg_types[first].cast<mlir::ShapedType>();
      uint32_t rank = first_type.getRank();
      for (auto same : same_shape.drop_front(1)) {
        uint32_t same_offset = positions.at(same);
        auto same_type = arg_types[same].cast<mlir::ShapedType>();
        if (same_type.getRank() != rank) {
          func.emitOpError() << "same shape constraints on arguments with "
                                "non-matching shapes: #"
                             << first << " and #" << same;
          signalPassFailure();
          continue;
        }

        for (uint32_t i = 0; i < 2 * rank; ++i) {
          // Replace uses for second arg data with first arg.
          auto same_arg = func.getArgument(same_offset + 3 + i);
          auto first_arg = func.getArgument(first_offset + 3 + i);
          same_arg.replaceAllUsesWith(first_arg);
        }
      }
    }
  }

  mlir::FunctionType func_type;
  llvm::ArrayRef<uint32_t> same_shape;
};

Status PropagateStaticShapeKnowledgeToKernel(
    mlir::ModuleOp module, llvm::ArrayRef<uint32_t> same_shape) {
  // Grab the original signature from the single function.
  auto func = *module.getBody()->op_begin<mlir::FuncOp>();

  mlir::PassManager pm(module.getContext());
  auto enable_if_vlog_is_on = [](mlir::Pass*, mlir::Operation*) {
    return VLOG_IS_ON(1);
  };
  pm.enableIRPrinting(/*shouldPrintBeforePass=*/{},
                      /*shouldPrintAfterPass=*/enable_if_vlog_is_on,
                      /*printModuleScope=*/false,
                      /*printAfterOnlyOnChange=*/false, llvm::dbgs());
  auto& kernel_pm = pm.nest<::mlir::gpu::GPUModuleOp>();
  kernel_pm.addNestedPass<mlir::LLVM::LLVMFuncOp>(
      absl::make_unique<PropagateStaticKnowledge>(func.getType(), same_shape));

  if (failed(pm.run(module))) {
    return InternalError("Static knowledge propagation failed.");
  }
  return Status::OK();
}

void RegisterDialects() {
  static bool init_once = []() {
    mlir::registerDialect<mlir::TF::TensorFlowDialect>();
    return true;
  }();
  (void)init_once;
}
}  // namespace

StatusOr<std::vector<uint8_t>> tensorflow::kernel_gen::GenerateCubinForTfCode(
    llvm::StringRef tf_code, std::pair<int32_t, int32_t> compute_capability,
    llvm::ArrayRef<uint32_t> tile_sizes, llvm::ArrayRef<uint32_t> same_shape,
    llvm::ArrayRef<uint32_t> unroll_factors) {
  RegisterDialects();
  mlir::MLIRContext context;
  mlir::OwningModuleRef module = mlir::parseSourceString(tf_code, &context);

  TF_RETURN_IF_ERROR(LowerTfOpToLhloWithDynamicShapes(module.get()));
  {
    xla::mlir_gpu::LowerLHLOToGPUOptions options;
    options.tile_sizes = tile_sizes;
    options.unroll_factors = unroll_factors;
    options.collapse_parallel_loops = false;
    TF_RETURN_IF_ERROR(xla::mlir_gpu::LowerLHLOToGPU(module.get(), options));
  }
  TF_RETURN_IF_ERROR(xla::mlir_gpu::LowerKernelBodiesToNVVM(module.get()));
  TF_RETURN_IF_ERROR(
      PropagateStaticShapeKnowledgeToKernel(module.get(), same_shape));

  mlir::OwningModuleRef kernel_module =
      xla::mlir_gpu::ExtractKernelModule(*module).ValueOrDie();
  auto llvmModule = mlir::translateModuleToNVVMIR(*kernel_module);
  if (!llvmModule) {
    return InternalError("Could not translate MLIR module to NVVM");
  }

  llvmModule->setModuleIdentifier("acme");
  llvmModule->setDataLayout(xla::gpu::nvptx::kDataLayout);

  xla::HloModuleConfig config;
  config.set_debug_options(xla::GetDebugOptionsFromFlags());

  TF_ASSIGN_OR_RETURN(std::string libdevice_dir, GetLibdeviceDir(config));
  TF_ASSIGN_OR_RETURN(std::string ptx, xla::gpu::nvptx::CompileToPtx(
                                           llvmModule.get(), compute_capability,
                                           config, libdevice_dir));
  VLOG(1) << ptx;

#if GOOGLE_CUDA
  return tensorflow::se::CompileGpuAsm(
      std::get<0>(compute_capability), std::get<1>(compute_capability),
      ptx.c_str(), xla::gpu::PtxOptsFromConfig(config));
#else
  return InternalError(
      "GOOGLE_CUDA not defined. Did you specify --config=cuda ?");
#endif
}
<commit_msg>Propagate noalias and alignment properties of TensorFlow ABI into kernels.<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

//===- cubin_creator.cc -----------------------------------------*- C++ -*-===//
//
// This file implements the function to compile a TF kernel function to a cubin.
//
//===----------------------------------------------------------------------===//
#include "tensorflow/compiler/mlir/tools/kernel_gen/cubin_creator.h"

#include <string>
#include <utility>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/strings/escaping.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/GPU/GPUDialect.h"  // from @llvm-project
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"  // from @llvm-project
#include "mlir/Dialect/StandardOps/IR/Ops.h"  // from @llvm-project
#include "mlir/IR/Dialect.h"  // from @llvm-project
#include "mlir/IR/Function.h"  // from @llvm-project
#include "mlir/IR/Operation.h"  // from @llvm-project
#include "mlir/IR/StandardTypes.h"  // from @llvm-project
#include "mlir/IR/Value.h"  // from @llvm-project
#include "mlir/Parser.h"  // from @llvm-project
#include "mlir/Pass/Pass.h"  // from @llvm-project
#include "mlir/Pass/PassManager.h"  // from @llvm-project
#include "mlir/Target/NVVMIR.h"  // from @llvm-project
#include "mlir/Transforms/DialectConversion.h"  // from @llvm-project
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_ops.h"
#include "tensorflow/compiler/mlir/xla/ir/hlo_ops.h"
#include "tensorflow/compiler/mlir/xla/transforms/passes.h"
#include "tensorflow/compiler/mlir/xla/transforms/rewriters.h"
#include "tensorflow/compiler/xla/debug_options_flags.h"
#include "tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.h"
#include "tensorflow/compiler/xla/service/gpu/stream_executor_util.h"
#include "tensorflow/compiler/xla/service/gpu/target_constants.h"
#include "tensorflow/compiler/xla/service/mlir_gpu/kernel_lowering.h"
#include "tensorflow/core/platform/cuda_libdevice_path.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/path.h"
#if GOOGLE_CUDA
#include "tensorflow/stream_executor/gpu/asm_compiler.h"
#endif

namespace {
using tensorflow::Status;
using xla::InternalError;
using xla::StatusOr;

StatusOr<std::string> GetLibdeviceDir(
    const xla::HloModuleConfig& hlo_module_config) {
  for (const std::string& cuda_root : tensorflow::CandidateCudaRoots(
           hlo_module_config.debug_options().xla_gpu_cuda_data_dir())) {
    std::string libdevice_dir =
        tensorflow::io::JoinPath(cuda_root, "nvvm", "libdevice");
    VLOG(2) << "Looking for libdevice at " << libdevice_dir;
    if (tensorflow::Env::Default()->IsDirectory(libdevice_dir).ok()) {
      VLOG(2) << "Found libdevice dir " << libdevice_dir;
      return libdevice_dir;
    }
  }
  return InternalError(
      "Can't find libdevice directory ${CUDA_DIR}/nvvm/libdevice");
}

struct MaterializeBroadcastsPass
    : public mlir::PassWrapper<MaterializeBroadcastsPass, mlir::FunctionPass> {
  void runOnFunction() override {
    mlir::ConversionTarget conversionTarget(getContext());
    mlir::OwningRewritePatternList conversionPatterns;

    // Consider the xla_hlo dialect legal for tests.
    conversionTarget.addLegalDialect<mlir::xla_hlo::XlaHloDialect>();
    // The conversion uses helpers from the Standard dialect.
    conversionTarget.addLegalDialect<mlir::StandardOpsDialect>();

    mlir::xla_hlo::SetupMaterializeBroadcastsLegality(&getContext(),
                                                      &conversionTarget);
    mlir::xla_hlo::PopulateMaterializeBroadcastsPatterns(&getContext(),
                                                         &conversionPatterns);

    if (failed(applyPartialConversion(getFunction(), conversionTarget,
                                      conversionPatterns))) {
      return signalPassFailure();
    }
  }
};

struct UnfuseBatchNormPass
    : public mlir::PassWrapper<UnfuseBatchNormPass, mlir::FunctionPass> {
  void runOnFunction() override {
    mlir::OwningRewritePatternList patterns;
    mlir::xla_hlo::PopulateUnfuseBatchNormPatterns(&getContext(), &patterns);
    mlir::applyPatternsAndFoldGreedily(getOperation(), patterns);
  }
};

Status LowerTfOpToLhloWithDynamicShapes(mlir::ModuleOp module) {
  mlir::PassManager pm(module.getContext());
  auto enable_if_vlog_is_on = [](mlir::Pass* pass, mlir::Operation* op) {
    return VLOG_IS_ON(1);
  };
  pm.enableIRPrinting(/*shouldPrintBeforePass=*/{},
                      /*shouldPrintAfterPass=*/enable_if_vlog_is_on,
                      /*printModuleScope=*/false,
                      /*printAfterOnlyOnChange=*/false, llvm::dbgs());
  pm.addNestedPass<mlir::FuncOp>(mlir::xla_hlo::createLegalizeTFPass(false));
  pm.addNestedPass<mlir::FuncOp>(
      absl::make_unique<MaterializeBroadcastsPass>());
  pm.addNestedPass<mlir::FuncOp>(absl::make_unique<UnfuseBatchNormPass>());
  pm.addPass(mlir::xla_hlo::createLegalizeToLhloPass(
      /*results_escape_functions=*/true));
  pm.addNestedPass<mlir::FuncOp>(mlir::xla_lhlo::createLhloCopyRemovalPass());

  if (failed(pm.run(module))) {
    return InternalError("Lowering TF to LHLO failed.");
  }
  return Status::OK();
}

struct PropagateTensorFlowABIKnowledge
    : public mlir::PassWrapper<PropagateTensorFlowABIKnowledge,
                               mlir::OperationPass<mlir::LLVM::LLVMFuncOp>> {
  explicit PropagateTensorFlowABIKnowledge(mlir::FunctionType type,
                                           llvm::ArrayRef<uint32_t> same_shape_)
      : func_type(type), same_shape(same_shape_) {}

  void runOnOperation() override {
    // We know due to tensorflow ABI that the offset is always 0 and that the
    // innermost stride is always 1. To make this visible to the compiler,
    // we insert constants into the code and replace usages accordingly.
    // We do not change the signature so that we keep a somewhat stable ABI
    // that is easy to undertand by tools.
    // We also know that tensorflow aligns all allocated pointers by 16, so
    // we pass this on. Furthermore, we know that arguments never alias. More
    // precicely, they may only alias (due to reuse) if the kernel does not
    // read from a position it previously has written to. We express this with
    // the noalias attribute.
    mlir::LLVM::LLVMFuncOp func = getOperation();

    // This only works if the function is local and we can rewrite it.
    if (func.isExternal()) return;

    mlir::OpBuilder b(func.getBody());
    // Steal the LLVM representation of the index type from the third argument.
    auto index_type = func.getArgument(3).getType();
    mlir::Value one = b.create<mlir::LLVM::ConstantOp>(
        func.getLoc(), index_type, b.getIntegerAttr(b.getIndexType(), 1));
    mlir::Value zero = b.create<mlir::LLVM::ConstantOp>(
        func.getLoc(), index_type, b.getIntegerAttr(b.getIndexType(), 0));
    uint32_t arg_pos = 0;
    std::vector<uint32_t> positions;
    // Collect the agument and return types of the surrounding function.
    auto arg_types = llvm::to_vector<4>(llvm::concat<const mlir::Type>(
        func_type.getInputs(), func_type.getResults()));
    for (mlir::Type arg_type : arg_types) {
      if (!arg_type.isa<mlir::MemRefType>()) {
        func.emitError() << "argument of surrounding func is not ranked memref";
        signalPassFailure();
        return;
      }
      positions.push_back(arg_pos);
      // Set alignment and aliasing on the pointers.
      func.setArgAttr(arg_pos + 1, "llvm.noalias", b.getBoolAttr(true));
      func.setArgAttr(arg_pos + 1, "llvm.align", b.getIndexAttr(16));
      // Replace the offset with zero. Offset is argument number 3.
      func.getArgument(arg_pos + 2).replaceAllUsesWith(zero);
      // Forward over base_ptr, aligned_ptr, offset, size and stride arguments.
      arg_pos += 3 + arg_type.cast<mlir::MemRefType>().getRank() * 2;
      // Replace the last stride with constant 1.
      func.getArgument(arg_pos - 1).replaceAllUsesWith(one);
    }

    // If we have knowledge that some arguments have the same shape, we
    // can use that here. Simply replace usages of the shape parameters within
    // the function body to a single shape parameter.
    if (!same_shape.empty()) {
      auto first = same_shape.front();
      auto first_offset = positions.at(first);
      auto first_type = arg_types[first].cast<mlir::ShapedType>();
      uint32_t rank = first_type.getRank();
      for (auto same : same_shape.drop_front(1)) {
        uint32_t same_offset = positions.at(same);
        auto same_type = arg_types[same].cast<mlir::ShapedType>();
        if (same_type.getRank() != rank) {
          func.emitOpError() << "same shape constraints on arguments with "
                                "non-matching shapes: #"
                             << first << " and #" << same;
          signalPassFailure();
          continue;
        }

        for (uint32_t i = 0; i < 2 * rank; ++i) {
          // Replace uses for second arg data with first arg.
          auto same_arg = func.getArgument(same_offset + 3 + i);
          auto first_arg = func.getArgument(first_offset + 3 + i);
          same_arg.replaceAllUsesWith(first_arg);
        }
      }
    }
  }

  mlir::FunctionType func_type;
  llvm::ArrayRef<uint32_t> same_shape;
};

Status PropagateTensorFlowABIKnowledgeToKernel(
    mlir::ModuleOp module, llvm::ArrayRef<uint32_t> same_shape) {
  // Grab the original signature from the single function.
  auto func = *module.getBody()->op_begin<mlir::FuncOp>();

  mlir::PassManager pm(module.getContext());
  auto enable_if_vlog_is_on = [](mlir::Pass*, mlir::Operation*) {
    return VLOG_IS_ON(1);
  };
  pm.enableIRPrinting(/*shouldPrintBeforePass=*/{},
                      /*shouldPrintAfterPass=*/enable_if_vlog_is_on,
                      /*printModuleScope=*/false,
                      /*printAfterOnlyOnChange=*/false, llvm::dbgs());
  auto& kernel_pm = pm.nest<::mlir::gpu::GPUModuleOp>();
  kernel_pm.addNestedPass<mlir::LLVM::LLVMFuncOp>(
      absl::make_unique<PropagateTensorFlowABIKnowledge>(func.getType(),
                                                         same_shape));

  if (failed(pm.run(module))) {
    return InternalError("Static knowledge propagation failed.");
  }
  return Status::OK();
}

void RegisterDialects() {
  static bool init_once = []() {
    mlir::registerDialect<mlir::TF::TensorFlowDialect>();
    return true;
  }();
  (void)init_once;
}
}  // namespace

StatusOr<std::vector<uint8_t>> tensorflow::kernel_gen::GenerateCubinForTfCode(
    llvm::StringRef tf_code, std::pair<int32_t, int32_t> compute_capability,
    llvm::ArrayRef<uint32_t> tile_sizes, llvm::ArrayRef<uint32_t> same_shape,
    llvm::ArrayRef<uint32_t> unroll_factors) {
  RegisterDialects();
  mlir::MLIRContext context;
  mlir::OwningModuleRef module = mlir::parseSourceString(tf_code, &context);

  TF_RETURN_IF_ERROR(LowerTfOpToLhloWithDynamicShapes(module.get()));
  {
    xla::mlir_gpu::LowerLHLOToGPUOptions options;
    options.tile_sizes = tile_sizes;
    options.unroll_factors = unroll_factors;
    options.collapse_parallel_loops = false;
    TF_RETURN_IF_ERROR(xla::mlir_gpu::LowerLHLOToGPU(module.get(), options));
  }
  TF_RETURN_IF_ERROR(xla::mlir_gpu::LowerKernelBodiesToNVVM(module.get()));
  TF_RETURN_IF_ERROR(
      PropagateTensorFlowABIKnowledgeToKernel(module.get(), same_shape));

  mlir::OwningModuleRef kernel_module =
      xla::mlir_gpu::ExtractKernelModule(*module).ValueOrDie();
  auto llvmModule = mlir::translateModuleToNVVMIR(*kernel_module);
  if (!llvmModule) {
    return InternalError("Could not translate MLIR module to NVVM");
  }

  llvmModule->setModuleIdentifier("acme");
  llvmModule->setDataLayout(xla::gpu::nvptx::kDataLayout);

  xla::HloModuleConfig config;
  config.set_debug_options(xla::GetDebugOptionsFromFlags());

  TF_ASSIGN_OR_RETURN(std::string libdevice_dir, GetLibdeviceDir(config));
  TF_ASSIGN_OR_RETURN(std::string ptx, xla::gpu::nvptx::CompileToPtx(
                                           llvmModule.get(), compute_capability,
                                           config, libdevice_dir));
  VLOG(1) << ptx;

#if GOOGLE_CUDA
  return tensorflow::se::CompileGpuAsm(
      std::get<0>(compute_capability), std::get<1>(compute_capability),
      ptx.c_str(), xla::gpu::PtxOptsFromConfig(config));
#else
  return InternalError(
      "GOOGLE_CUDA not defined. Did you specify --config=cuda ?");
#endif
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: MetaTContext.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 18:53:27 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    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.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _COM_SUN_STAR_XML_SAX_SAXPARSEEXCEPTION_HPP_
#include <com/sun/star/xml/sax/SAXParseException.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_SAXEXCEPTION_HPP_
#include <com/sun/star/xml/sax/SAXException.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_XDOCUMENTHANDLER_HPP_
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_XATTRIBUTELIST_HPP_
#include <com/sun/star/xml/sax/XAttributeList.hpp>
#endif
#ifndef _XMLOFF_NMSPMAP_HXX
#include "nmspmap.hxx"
#endif
#ifndef _XMLOFF_XMLTOKEN_HXX
#include "xmltoken.hxx"
#endif
#ifndef _XMLOFF_XMLNMSPE_HXX
#include "xmlnmspe.hxx"
#endif

#ifndef _XMLOFF_TRANSFOERMERBASE_HXX
#include "TransformerBase.hxx"
#endif
#ifndef _XMLOFF_MUTABLEATTRLIST_HXX
#include "MutableAttrList.hxx"
#endif

#ifndef _XMLOFF_METATCONTEXT_HXX
#include "MetaTContext.hxx"
#endif

using ::rtl::OUString;
using namespace ::xmloff::token;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::xml::sax;

XMLTokenEnum aMetaTokens[] =
{
    XML_GENERATOR,
    XML_TITLE,
    XML_DESCRIPTION,
    XML_SUBJECT,
    XML_INITIAL_CREATOR,
    XML_CREATION_DATE,
    XML_CREATOR,
    XML_DATE,
    XML_PRINTED_BY,
    XML_PRINT_DATE,
    XML_KEYWORD,
    XML_LANGUAGE,
    XML_EDITING_CYCLES,
    XML_EDITING_DURATION,
    XML_HYPERLINK_BEHAVIOUR,
    XML_AUTO_RELOAD,
    XML_TEMPLATE,
    XML_USER_DEFINED,
    XML_DOCUMENT_STATISTIC,
    XML_TOKEN_END
};

TYPEINIT1( XMLMetaTransformerContext, XMLTransformerContext );

XMLMetaTransformerContext::XMLMetaTransformerContext( XMLTransformerBase& rImp,
                                                const OUString& rQName ) :
    XMLTransformerContext( rImp, rQName )
{
}

XMLMetaTransformerContext::~XMLMetaTransformerContext()
{
}

XMLTransformerContext *XMLMetaTransformerContext::CreateChildContext(
            sal_uInt16 /*nPrefix*/,
            const OUString& rLocalName,
            const OUString& rQName,
            const Reference< XAttributeList >& )
{
    XMLPersTextContentTContext *pContext =
        new XMLPersTextContentTContext( GetTransformer(), rQName );
    XMLMetaContexts_Impl::value_type aVal( rLocalName, pContext );
    m_aContexts.insert( aVal );

    return pContext;
}

void XMLMetaTransformerContext::EndElement()
{
    // export everything in the correct order
    OUString aKeywordsQName;
    XMLTokenEnum *pToken = aMetaTokens;
    while( *pToken != XML_TOKEN_END )
    {
        const OUString& rToken = GetXMLToken( *pToken );
        XMLMetaContexts_Impl::const_iterator aIter =
            m_aContexts.find( rToken );
        if( aIter != m_aContexts.end() )
        {
            if( XML_KEYWORD == *pToken )
            {
                aKeywordsQName =
                    GetTransformer().GetNamespaceMap().GetQNameByKey(
                            XML_NAMESPACE_META, GetXMLToken(XML_KEYWORDS ) );

                Reference< XAttributeList > xAttrList =
                    new XMLMutableAttributeList;
                GetTransformer().GetDocHandler()->startElement( aKeywordsQName,
                                                            xAttrList );
            }

            // All elements may occur multiple times
            XMLMetaContexts_Impl::const_iterator aEndIter =
                m_aContexts.upper_bound( rToken );
            while( aIter != aEndIter )
            {
                (*aIter).second->Export();
                ++aIter;
            }

            if( XML_KEYWORD == *pToken )
                GetTransformer().GetDocHandler()->endElement( aKeywordsQName );
        }
        pToken++;
    }

    GetTransformer().GetDocHandler()->endElement( GetQName() );
}

void XMLMetaTransformerContext::Characters( const OUString& )
{
    // ignore them
}

<commit_msg>INTEGRATION: CWS pchfix02 (1.5.34); FILE MERGED 2006/09/01 18:00:17 kaib 1.5.34.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: MetaTContext.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 11:25:31 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    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.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmloff.hxx"

#ifndef _COM_SUN_STAR_XML_SAX_SAXPARSEEXCEPTION_HPP_
#include <com/sun/star/xml/sax/SAXParseException.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_SAXEXCEPTION_HPP_
#include <com/sun/star/xml/sax/SAXException.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_XDOCUMENTHANDLER_HPP_
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_XATTRIBUTELIST_HPP_
#include <com/sun/star/xml/sax/XAttributeList.hpp>
#endif
#ifndef _XMLOFF_NMSPMAP_HXX
#include "nmspmap.hxx"
#endif
#ifndef _XMLOFF_XMLTOKEN_HXX
#include "xmltoken.hxx"
#endif
#ifndef _XMLOFF_XMLNMSPE_HXX
#include "xmlnmspe.hxx"
#endif

#ifndef _XMLOFF_TRANSFOERMERBASE_HXX
#include "TransformerBase.hxx"
#endif
#ifndef _XMLOFF_MUTABLEATTRLIST_HXX
#include "MutableAttrList.hxx"
#endif

#ifndef _XMLOFF_METATCONTEXT_HXX
#include "MetaTContext.hxx"
#endif

using ::rtl::OUString;
using namespace ::xmloff::token;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::xml::sax;

XMLTokenEnum aMetaTokens[] =
{
    XML_GENERATOR,
    XML_TITLE,
    XML_DESCRIPTION,
    XML_SUBJECT,
    XML_INITIAL_CREATOR,
    XML_CREATION_DATE,
    XML_CREATOR,
    XML_DATE,
    XML_PRINTED_BY,
    XML_PRINT_DATE,
    XML_KEYWORD,
    XML_LANGUAGE,
    XML_EDITING_CYCLES,
    XML_EDITING_DURATION,
    XML_HYPERLINK_BEHAVIOUR,
    XML_AUTO_RELOAD,
    XML_TEMPLATE,
    XML_USER_DEFINED,
    XML_DOCUMENT_STATISTIC,
    XML_TOKEN_END
};

TYPEINIT1( XMLMetaTransformerContext, XMLTransformerContext );

XMLMetaTransformerContext::XMLMetaTransformerContext( XMLTransformerBase& rImp,
                                                const OUString& rQName ) :
    XMLTransformerContext( rImp, rQName )
{
}

XMLMetaTransformerContext::~XMLMetaTransformerContext()
{
}

XMLTransformerContext *XMLMetaTransformerContext::CreateChildContext(
            sal_uInt16 /*nPrefix*/,
            const OUString& rLocalName,
            const OUString& rQName,
            const Reference< XAttributeList >& )
{
    XMLPersTextContentTContext *pContext =
        new XMLPersTextContentTContext( GetTransformer(), rQName );
    XMLMetaContexts_Impl::value_type aVal( rLocalName, pContext );
    m_aContexts.insert( aVal );

    return pContext;
}

void XMLMetaTransformerContext::EndElement()
{
    // export everything in the correct order
    OUString aKeywordsQName;
    XMLTokenEnum *pToken = aMetaTokens;
    while( *pToken != XML_TOKEN_END )
    {
        const OUString& rToken = GetXMLToken( *pToken );
        XMLMetaContexts_Impl::const_iterator aIter =
            m_aContexts.find( rToken );
        if( aIter != m_aContexts.end() )
        {
            if( XML_KEYWORD == *pToken )
            {
                aKeywordsQName =
                    GetTransformer().GetNamespaceMap().GetQNameByKey(
                            XML_NAMESPACE_META, GetXMLToken(XML_KEYWORDS ) );

                Reference< XAttributeList > xAttrList =
                    new XMLMutableAttributeList;
                GetTransformer().GetDocHandler()->startElement( aKeywordsQName,
                                                            xAttrList );
            }

            // All elements may occur multiple times
            XMLMetaContexts_Impl::const_iterator aEndIter =
                m_aContexts.upper_bound( rToken );
            while( aIter != aEndIter )
            {
                (*aIter).second->Export();
                ++aIter;
            }

            if( XML_KEYWORD == *pToken )
                GetTransformer().GetDocHandler()->endElement( aKeywordsQName );
        }
        pToken++;
    }

    GetTransformer().GetDocHandler()->endElement( GetQName() );
}

void XMLMetaTransformerContext::Characters( const OUString& )
{
    // ignore them
}

<|endoftext|>


<commit_before>#pragma once
/**
	@file
	@brief operator
	@author MITSUNARI Shigeo(@herumi)
	@license modified new BSD license
	http://opensource.org/licenses/BSD-3-Clause
*/
#include <ios>
#include <cybozu/exception.hpp>

#ifdef _WIN32
	#ifndef MCL_FORCE_INLINE
		#define MCL_FORCE_INLINE __forceinline
	#endif
	#pragma warning(push)
	#pragma warning(disable : 4714)
#else
	#ifndef MCL_FORCE_INLINE
		#define MCL_FORCE_INLINE __attribute__((always_inline))
	#endif
#endif

namespace mcl { namespace ope {

template<class T>
struct Empty {};

/*
	T must have compare
*/
template<class T, class E = Empty<T> >
struct comparable : E {
	friend MCL_FORCE_INLINE bool operator<(const T& x, const T& y) { return T::compare(x, y) < 0; }
	friend MCL_FORCE_INLINE bool operator>=(const T& x, const T& y) { return !operator<(x, y); }

	friend MCL_FORCE_INLINE bool operator>(const T& x, const T& y) { return T::compare(x, y) > 0; }
	friend MCL_FORCE_INLINE bool operator<=(const T& x, const T& y) { return !operator>(x, y); }
	friend MCL_FORCE_INLINE bool operator==(const T& x, const T& y) { return T::compare(x, y) == 0; }
	friend MCL_FORCE_INLINE bool operator!=(const T& x, const T& y) { return !operator==(x, y); }
};

/*
	T must have add, sub
*/
template<class T, class E = Empty<T> >
struct addsub : E {
	template<class S> MCL_FORCE_INLINE T& operator+=(const S& rhs) { T::add(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> MCL_FORCE_INLINE T& operator-=(const S& rhs) { T::sub(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> friend MCL_FORCE_INLINE T operator+(const T& a, const S& b) { T c; T::add(c, a, b); return c; }
	template<class S> friend MCL_FORCE_INLINE T operator-(const T& a, const S& b) { T c; T::sub(c, a, b); return c; }
};

/*
	T must have mul
*/
template<class T, class E = Empty<T> >
struct mulable : E {
	template<class S> MCL_FORCE_INLINE T& operator*=(const S& rhs) { T::mul(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> friend MCL_FORCE_INLINE T operator*(const T& a, const S& b) { T c; T::mul(c, a, b); return c; }
};

/*
	T must have inv, mul
*/
template<class T, class E = Empty<T> >
struct invertible : E {
	MCL_FORCE_INLINE T& operator/=(const T& rhs) { T c; T::inv(c, rhs); T::mul(static_cast<T&>(*this), static_cast<const T&>(*this), c); return static_cast<T&>(*this); }
	friend MCL_FORCE_INLINE T operator/(const T& a, const T& b) { T c; T::inv(c, b); T::mul(c, c, a); return c; }
};

/*
	T must have neg
*/
template<class T, class E = Empty<T> >
struct hasNegative : E {
	MCL_FORCE_INLINE T operator-() const { T c; T::neg(c, static_cast<const T&>(*this)); return c; }
};

template<class T, class E = Empty<T> >
struct hasIO : E {
	friend inline std::ostream& operator<<(std::ostream& os, const T& self)
	{
		const std::ios_base::fmtflags f = os.flags();
		if (f & std::ios_base::oct) throw cybozu::Exception("fpT:operator<<:oct is not supported");
		const int base = (f & std::ios_base::hex) ? 16 : 10;
		const bool showBase = (f & std::ios_base::showbase) != 0;
		std::string str;
		self.getStr(str, base, showBase);
		return os << str;
	}
	friend inline std::istream& operator>>(std::istream& is, T& self)
	{
		const std::ios_base::fmtflags f = is.flags();
		if (f & std::ios_base::oct) throw cybozu::Exception("fpT:operator>>:oct is not supported");
		const int base = (f & std::ios_base::hex) ? 16 : 0;
		std::string str;
		is >> str;
		self.setStr(str, base);
		return is;
	}
};

} } // mcl::ope

#ifdef _WIN32
//	#pragma warning(pop)
#endif
<commit_msg>operator.hpp is removed<commit_after><|endoftext|>


<commit_before>#ifndef __PROCESS_HTTP_HPP__
#define __PROCESS_HTTP_HPP__

#include <string>

namespace process {
namespace http {

struct Request
{
  // TODO(benh): Add major/minor version.
  std::map<std::string, std::string> headers;
  std::string method;
  std::string path;
  std::string url;
  std::string fragment;
  std::string query;
  std::string body;
  bool keepAlive;
};

struct Response
{
  Response() : type(BODY) {}

  // TODO(benh): Add major/minor version.
  std::string status;
  std::map<std::string, std::string> headers;

  // TODO(benh): Make body a stream (channel) instead, and allow a
  // response to be returned without forcing the stream to be
  // finished.

  // Either provide a 'body' or an absolute 'path' to a file. If a
  // path is specified then we will attempt to perform a 'sendfile'
  // operation on the file. In either case you are expected to
  // properly specify the 'Content-Type' header, but the
  // 'Content-Length' header will be filled in for you if you specify
  // a path. Distinguish between the two using 'type' below.
  enum {
    BODY,
    PATH
  } type;

  std::string body;
  std::string path;
};

struct OK : Response
{
  OK() : Response()
  {
    status = "200 OK";
  }
};

struct BadRequest : Response
{
  BadRequest() : Response()
  {
    status = "400 Bad Request";
  }
};

struct NotFound : Response
{
  NotFound() : Response()
  {
    status = "404 Not Found";
  }
};

struct InternalServerError : Response
{
  InternalServerError() : Response()
  {
    status = "500 Internal Server Error";
  }
};

struct ServiceUnavailable : Response
{
  ServiceUnavailable() : Response()
  {
    status = "503 Service Unavailable";
  }
};

} // namespace http {
} // namespace process {

#endif // __PROCESS_HTTP_HPP__
<commit_msg>Added missing include.<commit_after>#ifndef __PROCESS_HTTP_HPP__
#define __PROCESS_HTTP_HPP__

#include <map>
#include <string>

namespace process {
namespace http {

struct Request
{
  // TODO(benh): Add major/minor version.
  std::map<std::string, std::string> headers;
  std::string method;
  std::string path;
  std::string url;
  std::string fragment;
  std::string query;
  std::string body;
  bool keepAlive;
};

struct Response
{
  Response() : type(BODY) {}

  // TODO(benh): Add major/minor version.
  std::string status;
  std::map<std::string, std::string> headers;

  // TODO(benh): Make body a stream (channel) instead, and allow a
  // response to be returned without forcing the stream to be
  // finished.

  // Either provide a 'body' or an absolute 'path' to a file. If a
  // path is specified then we will attempt to perform a 'sendfile'
  // operation on the file. In either case you are expected to
  // properly specify the 'Content-Type' header, but the
  // 'Content-Length' header will be filled in for you if you specify
  // a path. Distinguish between the two using 'type' below.
  enum {
    BODY,
    PATH
  } type;

  std::string body;
  std::string path;
};

struct OK : Response
{
  OK() : Response()
  {
    status = "200 OK";
  }
};

struct BadRequest : Response
{
  BadRequest() : Response()
  {
    status = "400 Bad Request";
  }
};

struct NotFound : Response
{
  NotFound() : Response()
  {
    status = "404 Not Found";
  }
};

struct InternalServerError : Response
{
  InternalServerError() : Response()
  {
    status = "500 Internal Server Error";
  }
};

struct ServiceUnavailable : Response
{
  ServiceUnavailable() : Response()
  {
    status = "503 Service Unavailable";
  }
};

} // namespace http {
} // namespace process {

#endif // __PROCESS_HTTP_HPP__
<|endoftext|>


<commit_before>#ifndef PROTON_TUPLE_HEADER
#define PROTON_TUPLE_HEADER

/** @file tuple.hpp
 *  @brief tuple support.
 *  Please include this header instead of \<tuple\>.
 */

#include <tuple>
#include <algorithm>
#include <iostream>
#include <initializer_list>
#include <algorithm>
#include <stdexcept>
#include <limits>

#include <proton/base.hpp>

namespace proton{

namespace detail{

// helper function to print a tuple of any size
template<typename T, std::size_t I>
struct output_tuple {
    static void output(std::ostream& s, T&& t)
    {
        output_tuple<T, I-1>::output(s,t);
        s << ", " << std::get<I-1>(t);
    }
};

template<typename T>
struct output_tuple<T, 1> {
    static void output(std::ostream& s, T&& t)
    {
        s << std::get<0>(t);
    }
};

template<typename T>
struct output_tuple<T, 0> {
    static void output(std::ostream& s, T&& t)
    {
    }
};

constexpr long fix_index(long i, long size)
{
    return (i>size)?
				size
			:(
				(i<0)?
					(i+size < 0 ?
						0
					:
						i+size
					)
				:
					i
			);
}

constexpr long sub_index(long i, long size)
{
    return (i>=size)?
				size-1
			:(
				(i<0)?
					(i+size < 0 ?
						0
					:
						i+size
					)
				:
					i
			);
}

constexpr long get_index(long i, long size)
{
    return (i>=size)?
				-1
			:(
				(i<0)?
					(i+size < 0 ?
						-1
					:
						i+size
					)
				:
					i
			);
}

template<long i, typename ...T>
struct at_index{
	static_assert(i>=0, "out of range");
	const std::tuple<T...>* p;
	typedef decltype(std::get<i>(*p)) type;
};

constexpr long fix_size(long begin, long end, long size)
{
	return fix_index(begin,size)>fix_index(end,size)?
				0
			:
				fix_index(end,size)-fix_index(begin,size);
}

template<typename T, size_t begin, size_t size>
struct sub{
private:
	static_assert(begin < std::tuple_size<T>::value, "out of range");

	std::tuple<typename std::tuple_element<begin, T>::type> *p;
	typedef typename sub<T, begin+1,
						(begin+size > std::tuple_size<T>::value ?
							(std::tuple_size<T>::value-begin-1)
							: (size-1))
						>::type next_types;
	next_types* q;

public:
	typedef decltype(std::tuple_cat(*p,*q)) type;
};

template<typename T, size_t begin>
struct sub<T, begin, 0>{
	typedef std::tuple<> type;
};

template<typename T, size_t begin>
struct sub<T,begin,1>{
	static_assert(begin < std::tuple_size<T>::value, "out of range");

	typedef std::tuple<typename std::tuple_element<begin,T>::type > type;
};

template<typename ...T>
struct len_t<std::tuple<T...> >{
    static size_t result(const std::tuple<T...>& x)
    {
        return sizeof...(T);
    }
};

} // ns detail

/** @addtogroup tuple
 * @{
 */

/** like x[index] in python
 */

template<long index, typename ...T>
typename detail::at_index<detail::get_index(index,sizeof...(T)),T...>::type
	at(const std::tuple<T...>& x)
{
	return std::get<detail::get_index(index,sizeof...(T))>(x);
}

/** get a slice of tuple x[begin:end] in python
 */
template<long begin, long end=std::numeric_limits<long>::max(), typename ...T>
typename detail::sub<std::tuple<T...>, detail::fix_index(begin, sizeof...(T)),
									   detail::fix_size(begin,end, sizeof...(T))>::type
	sub(const std::tuple<T...>& x)
{
	typedef typename detail::sub<std::tuple<T...>, detail::fix_index(begin, sizeof...(T)),
									   detail::fix_size(begin,end, sizeof...(T))>::type ret_t;
#ifdef __clang__
	return ret_t(*reinterpret_cast<const ret_t*>(&std::get<(detail::sub_index(begin, sizeof...(T)))>(x)));
#else
	#ifdef __GNUC__
		return ret_t(*reinterpret_cast<const ret_t*>(&std::get<(detail::sub_index(end-1, sizeof...(T)))>(x)));
	#else
		static_assert(0, "unknown compiler")
	#endif
#endif
}

/** general output for tuple.
 * @param s the output stream
 * @param x the tuple to be outputed
 * @return s
 */
template <typename ...T>
std::ostream& operator<<(std::ostream& s, const std::tuple<T...>& x)
{
    s << "(";
    detail::output_tuple<decltype(x), sizeof...(T)>::output(s,x);
    s << ")";
    return s;
}

template <typename ...T>
std::wostream& operator<<(std::wostream& s, const std::tuple<T...>& x)
{
    s << L"(";
    detail::output_tuple<decltype(x), sizeof...(T)>::output(s,x);
    s << L")";
    return s;
}

/** tuple + tuple
 */
template<typename T2, typename ...T1>
auto operator+(const std::tuple<T1...>& x, T2&& y) -> decltype(std::tuple_cat(x,y))
{
	return std::tuple_cat(x,y);
}

template<typename T2, typename ...T1>
auto operator+(std::tuple<T1...>&& x, T2&& y) -> decltype(std::tuple_cat(x,y))
{
	return std::tuple_cat(x,y);
}

/** eq to make_tuple
 */
template<typename ...T>
auto _t(T&& ...x) -> decltype(std::make_tuple(x...))
{
	return std::make_tuple(x...);
}

/** eq to forward_as_tuple
 */
template<typename ...T>
auto _f(T&& ...x) -> decltype(std::forward_as_tuple(x...))
{
	return std::forward_as_tuple(x...);
}

#if 0
/* vector_ * n
 */
template<typename T, typename A>
vector_<T,A> operator*(const std::vector<T,A>& s, size_t n)
{
    vector_<T,A> r;
    r.reserve(s.size()*n);
    for(size_t i=0; i<n; i++)
        r.extend(s);
    return r;
}

/* n * vector_
 */
template<typename T, typename A>
vector_<T,A> operator*(size_t n, const std::vector<T,A>& s)
{
    return s*n;
}
#endif

/**
 * @example tuple.cpp
 * @}
 */
}

#endif // PROTON_TUPLE_HEADER
<commit_msg>change at_index usage<commit_after>#ifndef PROTON_TUPLE_HEADER
#define PROTON_TUPLE_HEADER

/** @file tuple.hpp
 *  @brief tuple support.
 *  Please include this header instead of \<tuple\>.
 */

#include <tuple>
#include <algorithm>
#include <iostream>
#include <initializer_list>
#include <algorithm>
#include <stdexcept>
#include <limits>

#include <proton/base.hpp>

namespace proton{

namespace detail{

// helper function to print a tuple of any size
template<typename T, std::size_t I>
struct output_tuple {
    static void output(std::ostream& s, T&& t)
    {
        output_tuple<T, I-1>::output(s,t);
        s << ", " << std::get<I-1>(t);
    }
};

template<typename T>
struct output_tuple<T, 1> {
    static void output(std::ostream& s, T&& t)
    {
        s << std::get<0>(t);
    }
};

template<typename T>
struct output_tuple<T, 0> {
    static void output(std::ostream& s, T&& t)
    {
    }
};

constexpr long fix_index(long i, long size)
{
    return (i>size)?
				size
			:(
				(i<0)?
					(i+size < 0 ?
						0
					:
						i+size
					)
				:
					i
			);
}

constexpr long sub_index(long i, long size)
{
    return (i>=size)?
				size-1
			:(
				(i<0)?
					(i+size < 0 ?
						0
					:
						i+size
					)
				:
					i
			);
}

constexpr long get_index(long i, long size)
{
    return (i>=size)?
				-1
			:(
				(i<0)?
					(i+size < 0 ?
						-1
					:
						i+size
					)
				:
					i
			);
}

template<long i, typename ...T>
struct at_index{
	const std::tuple<T...>* p;
	typedef decltype(std::get<get_index(i,sizeof...(T))>(*p)) type;
};

constexpr long fix_size(long begin, long end, long size)
{
	return fix_index(begin,size)>fix_index(end,size)?
				0
			:
				fix_index(end,size)-fix_index(begin,size);
}

template<typename T, size_t begin, size_t size>
struct sub{
private:
	static_assert(begin < std::tuple_size<T>::value, "out of range");

	std::tuple<typename std::tuple_element<begin, T>::type> *p;
	typedef typename sub<T, begin+1,
						(begin+size > std::tuple_size<T>::value ?
							(std::tuple_size<T>::value-begin-1)
							: (size-1))
						>::type next_types;
	next_types* q;

public:
	typedef decltype(std::tuple_cat(*p,*q)) type;
};

template<typename T, size_t begin>
struct sub<T, begin, 0>{
	typedef std::tuple<> type;
};

template<typename T, size_t begin>
struct sub<T,begin,1>{
	static_assert(begin < std::tuple_size<T>::value, "out of range");

	typedef std::tuple<typename std::tuple_element<begin,T>::type > type;
};

template<typename ...T>
struct len_t<std::tuple<T...> >{
    static size_t result(const std::tuple<T...>& x)
    {
        return sizeof...(T);
    }
};

} // ns detail

/** @addtogroup tuple
 * @{
 */

/** like x[index] in python
 */

template<long index, typename ...T>
typename detail::at_index<index,T...>::type
	at(const std::tuple<T...>& x)
{
	return std::get<detail::get_index(index,sizeof...(T))>(x);
}

/** get a slice of tuple x[begin:end] in python
 */
template<long begin, long end=std::numeric_limits<long>::max(), typename ...T>
typename detail::sub<std::tuple<T...>, detail::fix_index(begin, sizeof...(T)),
									   detail::fix_size(begin,end, sizeof...(T))>::type
	sub(const std::tuple<T...>& x)
{
	typedef typename detail::sub<std::tuple<T...>, detail::fix_index(begin, sizeof...(T)),
									   detail::fix_size(begin,end, sizeof...(T))>::type ret_t;
#ifdef __clang__
	return ret_t(*reinterpret_cast<const ret_t*>(&std::get<(detail::sub_index(begin, sizeof...(T)))>(x)));
#else
	#ifdef __GNUC__
		return ret_t(*reinterpret_cast<const ret_t*>(&std::get<(detail::sub_index(end-1, sizeof...(T)))>(x)));
	#else
		static_assert(0, "unknown compiler")
	#endif
#endif
}

/** general output for tuple.
 * @param s the output stream
 * @param x the tuple to be outputed
 * @return s
 */
template <typename ...T>
std::ostream& operator<<(std::ostream& s, const std::tuple<T...>& x)
{
    s << "(";
    detail::output_tuple<decltype(x), sizeof...(T)>::output(s,x);
    s << ")";
    return s;
}

template <typename ...T>
std::wostream& operator<<(std::wostream& s, const std::tuple<T...>& x)
{
    s << L"(";
    detail::output_tuple<decltype(x), sizeof...(T)>::output(s,x);
    s << L")";
    return s;
}

/** tuple + tuple
 */
template<typename T2, typename ...T1>
auto operator+(const std::tuple<T1...>& x, T2&& y) -> decltype(std::tuple_cat(x,y))
{
	return std::tuple_cat(x,y);
}

template<typename T2, typename ...T1>
auto operator+(std::tuple<T1...>&& x, T2&& y) -> decltype(std::tuple_cat(x,y))
{
	return std::tuple_cat(x,y);
}

/** eq to make_tuple
 */
template<typename ...T>
auto _t(T&& ...x) -> decltype(std::make_tuple(x...))
{
	return std::make_tuple(x...);
}

/** eq to forward_as_tuple
 */
template<typename ...T>
auto _f(T&& ...x) -> decltype(std::forward_as_tuple(x...))
{
	return std::forward_as_tuple(x...);
}

#if 0
/* vector_ * n
 */
template<typename T, typename A>
vector_<T,A> operator*(const std::vector<T,A>& s, size_t n)
{
    vector_<T,A> r;
    r.reserve(s.size()*n);
    for(size_t i=0; i<n; i++)
        r.extend(s);
    return r;
}

/* n * vector_
 */
template<typename T, typename A>
vector_<T,A> operator*(size_t n, const std::vector<T,A>& s)
{
    return s*n;
}
#endif

/**
 * @example tuple.cpp
 * @}
 */
}

#endif // PROTON_TUPLE_HEADER
<|endoftext|>


<commit_before>#ifndef RENDERSYSTEM_H
#define RENDERSYSTEM_H

#include "isystem.hpp"
#include <unordered_map>
#include <string>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <vector>
#include <unordered_set>
#include <queue>
#include "spatialindexer.hpp"
#include "heap.hpp"
#include "text.hpp"

using namespace std;

class RenderComponent;
class MoveComponent;
class SizeComponent;
class FlagComponent;
class SpatialIndexer;

typedef unsigned int ID;

class CameraSystem;

struct TextureData {
	SDL_Texture* texture;
	unsigned int width;
	unsigned int height;
};

struct RenderData {
	ID id;
	RenderComponent* renderComponent;
	TextureData textureData;
	SDL_Rect cliprect;
	SDL_Rect target;

	inline bool operator< (const RenderData& rhs) const;
    inline bool operator> (const RenderData& rhs) const;
    inline bool operator<=(const RenderData& rhs) const;
    inline bool operator>=(const RenderData& rhs) const;
};

class RenderSystem : public ISystem {
 private:
	unordered_set<ID> ids;
	queue<ID> activeIds;
	queue<SpatialIndexer::Rect> previousDrawAreas;
	queue<Text> texts;
	CameraSystem* cameraSystem;

	static constexpr ushort SCREEN_WIDTH = 640;
	static constexpr ushort SCREEN_HEIGHT = 480;

	SDL_Renderer* renderer = nullptr;
	SDL_Window* window = nullptr;
	SDL_Texture* worldTexture = nullptr;
	SDL_Texture* fontTexture = nullptr;
	TTF_Font* font;
	unordered_map<string, TextureData> textureDatas;

	void renderArea(heap<RenderData>& pq, SpatialIndexer::Rect area);
	void renderTexts();

 public:
 	RenderSystem();
 	~RenderSystem();
	void add(ID id);
	void remove(ID id);
	void update();
	unsigned int count() const;
	void render(const RenderData& rd) const;
	const string getIdentifier() const;
	void calculateZIndex(ID id);
	void activateId(ID id);
	void setCameraSystem(CameraSystem* cameraSystem);
	void setImage(ID id, string path);
	void printText(const Text& text);

	//Forces a redraw within an area
	void inline constexpr renderArea(const SpatialIndexer::Rect& area) {
	    previousDrawAreas.push(area);
	}
	static constexpr ushort getScreenWidth() { return SCREEN_WIDTH; }
    static constexpr ushort getScreenHeight() { return SCREEN_HEIGHT; }

};

#endif //RENDERSYSTEM_H
<commit_msg>changed resolution to avoid artifacts when running in fullscreen<commit_after>#ifndef RENDERSYSTEM_H
#define RENDERSYSTEM_H

#include "isystem.hpp"
#include <unordered_map>
#include <string>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <vector>
#include <unordered_set>
#include <queue>
#include "spatialindexer.hpp"
#include "heap.hpp"
#include "text.hpp"

using namespace std;

class RenderComponent;
class MoveComponent;
class SizeComponent;
class FlagComponent;
class SpatialIndexer;

typedef unsigned int ID;

class CameraSystem;

struct TextureData {
	SDL_Texture* texture;
	unsigned int width;
	unsigned int height;
};

struct RenderData {
	ID id;
	RenderComponent* renderComponent;
	TextureData textureData;
	SDL_Rect cliprect;
	SDL_Rect target;

	inline bool operator< (const RenderData& rhs) const;
    inline bool operator> (const RenderData& rhs) const;
    inline bool operator<=(const RenderData& rhs) const;
    inline bool operator>=(const RenderData& rhs) const;
};

class RenderSystem : public ISystem {
 private:
	unordered_set<ID> ids;
	queue<ID> activeIds;
	queue<SpatialIndexer::Rect> previousDrawAreas;
	queue<Text> texts;
	CameraSystem* cameraSystem;

	static constexpr ushort SCREEN_WIDTH = 1920 / 4;
	static constexpr ushort SCREEN_HEIGHT = 1080 / 4;

	SDL_Renderer* renderer = nullptr;
	SDL_Window* window = nullptr;
	SDL_Texture* worldTexture = nullptr;
	SDL_Texture* fontTexture = nullptr;
	TTF_Font* font;
	unordered_map<string, TextureData> textureDatas;

	void renderArea(heap<RenderData>& pq, SpatialIndexer::Rect area);
	void renderTexts();

 public:
 	RenderSystem();
 	~RenderSystem();
	void add(ID id);
	void remove(ID id);
	void update();
	unsigned int count() const;
	void render(const RenderData& rd) const;
	const string getIdentifier() const;
	void calculateZIndex(ID id);
	void activateId(ID id);
	void setCameraSystem(CameraSystem* cameraSystem);
	void setImage(ID id, string path);
	void printText(const Text& text);

	//Forces a redraw within an area
	void inline constexpr renderArea(const SpatialIndexer::Rect& area) {
	    previousDrawAreas.push(area);
	}
	static constexpr ushort getScreenWidth() { return SCREEN_WIDTH; }
    static constexpr ushort getScreenHeight() { return SCREEN_HEIGHT; }

};

#endif //RENDERSYSTEM_H
<|endoftext|>


<commit_before>#pragma once

#include <type_traits>
#include <vector>
#include <initializer_list>
#include <cstddef>
#include <algorithm>
#include <iterator>

namespace helene
{

template <class T, std::size_t StackBufferSize = sizeof(std::vector<T>)>
class small_vector
{
    static_assert(std::is_trivial<T>::value,
                  "small_vector is only compatible with trivial types");

public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;
    typedef typename std::vector<T>::difference_type difference_type;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T* iterator;
    typedef const T* const_iterator;

private:
    static constexpr size_type at_least_size =
        std::max(StackBufferSize, sizeof(std::vector<T>));
    static constexpr size_type max_stack_size_ = at_least_size / sizeof(T);

public:
    small_vector() : size_()
    {
        new(&storage_) T[max_stack_size_];
    }

    small_vector(std::initializer_list<T> init) : size_(init.size())
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(init);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::copy(
                init.begin(), init.end(), reinterpret_cast<T*>(&storage_));
        }
    }

    small_vector(size_type count) : size_(count)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, T{});
        }
    }

    small_vector(size_type count, const T& value)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count, value);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, value);
        }
    }

    ~small_vector()
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();
        }
    }

public:
    void
    push_back(const T& value)
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else if(size_ == max_stack_size_)
        {
            T* buff = reinterpret_cast<T*>(&storage_);
            T temp[max_stack_size_];
            std::copy(buff, buff + size_, std::begin(temp));

            new(&storage_) std::vector<T>(std::begin(temp), std::end(temp));
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else
        {
            reinterpret_cast<T*>(&storage_)[size_] = value;
        }

        ++size_;
    }

    void
    pop_back()
    {
        if(size_ > max_stack_size_ + 1) // remain on heap
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            --size_;
        }
        else if(size_ == max_stack_size_ + 1) // transition to stack
        {
            // reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            // not necessary, trivially destructible

            std::vector<T> temp(
                std::move(*(reinterpret_cast<std::vector<T>*>(&storage_))));
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();

            new(&storage_) T[max_stack_size_];
            std::copy(
                temp.begin(), temp.end() - 1, reinterpret_cast<T*>(&storage_));
            --size_;
        }
        else // remain on stack
        {
            --size_;
        }
    }

    T& operator[](size_type n)
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->operator[](n);
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[n];
        }
    }

    const T& operator[](size_type n) const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->
            operator[](n);
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[n];
        }
    }

    T&
    front()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->front();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[0];
        }
    }

    const T&
    front() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->front();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[0];
        }
    }

    T&
    back()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->back();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[size_ - 1];
        }
    }

    const T&
    back() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->back();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[size_ - 1];
        }
    }

    iterator
    erase(iterator pos)
    {
        if(size_ > max_stack_size_ + 1)
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            typename std::vector<T>::iterator iter_pos =
                ref->begin() + (pos - ref->data());

            auto iter_out = ref->erase(iter_pos);
            --size_;

            // convert std::vector<T>::iterator to pointer
            difference_type diff = iter_out - ref->begin();

            return ref->data() + diff;
        }
        else if(size_ == max_stack_size_ + 1)
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            const auto diff = pos - ref->data();
            typename std::vector<T>::iterator iter_pos =
                ref->begin() + (pos - ref->data());

            auto iter_out = ref->erase(iter_pos);

            std::vector<T> temp(std::move(*ref));
            ref->~vector();

            new(&storage_) T[max_stack_size_];
            std::copy(
                temp.begin(), temp.end(), reinterpret_cast<T*>(&storage_));
            --size_;

            return reinterpret_cast<T*>(&storage_) + diff;
        }
        else
        {
            T* beg = reinterpret_cast<T*>(&storage_);
            std::copy(pos + 1, end(), pos);

            --size_;
            return pos;
        }
    }

    iterator
    erase(iterator first, iterator last)
    {
        const auto erase_size = std::distance(first, last);

        if(size_ > max_stack_size_ + erase_size)
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            auto first_iter = ref->begin() + (first - begin());
            auto last_iter = ref->begin() + (last - begin());

            auto iter_out = ref->erase(first_iter, last_iter);
            size_ -= erase_size;

            // convert back to simple pointer
            return begin() + (iter_out - ref->begin());
        }
        else if(size_ <= max_stack_size_)
        {
            T* buff_beg = reinterpret_cast<T*>(&storage_);
            if(last < end()) // if anything left past end of range to erase
            {
                std::copy(last, end(), first);
            }
            size_ -= erase_size;
            return first;
        }
        else
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            auto first_iter = ref->begin() + (first - begin());
            auto last_iter = ref->begin() + (last - begin());

            // carry out erase on vector
            auto iter_out = ref->erase(first_iter, last_iter);
            const auto out_diff = iter_out - ref->begin();

            std::vector<T> temp(std::move(*ref));
            ref->~vector();

            new(&storage_) T[max_stack_size_];

            std::copy(
                temp.begin(), temp.end(), reinterpret_cast<T*>(&storage_));

            size_ -= erase_size;

            return begin() + out_diff;
        }
    }

    T*
    data()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_);
        }
    }

    const T*
    data() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_);
        }
    }

    iterator
    begin()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_);
        }
    }

    iterator
    end()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data() + size_;
        }
        else
        {
            return reinterpret_cast<T*>(&storage_) + size_;
        }
    }

    const_iterator
    cbegin() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_);
        }
    }

    const_iterator
    cend() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data() +
                   size_;
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_) + size_;
        }
    }

    size_type
    size() const
    {
        return size_;
    }

    bool
    empty() const
    {
        return size_ == 0;
    }

    static constexpr size_type
    max_stack_size()
    {
        return max_stack_size_;
    }

    bool
    on_stack() const
    {
        return size_ <= max_stack_size_;
    }

private:
    std::aligned_storage_t<at_least_size> storage_;
    size_type size_;
};

} // namespace helene
<commit_msg>Rename return types to public member types. 	modified:   include/small_vector.hpp<commit_after>#pragma once

#include <type_traits>
#include <vector>
#include <initializer_list>
#include <cstddef>
#include <algorithm>
#include <iterator>

namespace helene
{

template <class T, std::size_t StackBufferSize = sizeof(std::vector<T>)>
class small_vector
{
    static_assert(std::is_trivial<T>::value,
                  "small_vector is only compatible with trivial types");

public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;
    typedef typename std::vector<T>::difference_type difference_type;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T* iterator;
    typedef const T* const_iterator;

private:
    static constexpr size_type at_least_size =
        std::max(StackBufferSize, sizeof(std::vector<T>));
    static constexpr size_type max_stack_size_ = at_least_size / sizeof(T);

public:
    small_vector() : size_()
    {
        new(&storage_) T[max_stack_size_];
    }

    small_vector(std::initializer_list<T> init) : size_(init.size())
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(init);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::copy(
                init.begin(), init.end(), reinterpret_cast<T*>(&storage_));
        }
    }

    small_vector(size_type count) : size_(count)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, T{});
        }
    }

    small_vector(size_type count, const T& value)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count, value);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, value);
        }
    }

    ~small_vector()
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();
        }
    }

public:
    void
    push_back(const T& value)
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else if(size_ == max_stack_size_)
        {
            T* buff = reinterpret_cast<T*>(&storage_);
            T temp[max_stack_size_];
            std::copy(buff, buff + size_, std::begin(temp));

            new(&storage_) std::vector<T>(std::begin(temp), std::end(temp));
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else
        {
            reinterpret_cast<T*>(&storage_)[size_] = value;
        }

        ++size_;
    }

    void
    pop_back()
    {
        if(size_ > max_stack_size_ + 1) // remain on heap
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            --size_;
        }
        else if(size_ == max_stack_size_ + 1) // transition to stack
        {
            // reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            // not necessary, trivially destructible

            std::vector<T> temp(
                std::move(*(reinterpret_cast<std::vector<T>*>(&storage_))));
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();

            new(&storage_) T[max_stack_size_];
            std::copy(
                temp.begin(), temp.end() - 1, reinterpret_cast<T*>(&storage_));
            --size_;
        }
        else // remain on stack
        {
            --size_;
        }
    }

    reference operator[](size_type n)
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->operator[](n);
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[n];
        }
    }

    const_reference operator[](size_type n) const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->
            operator[](n);
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[n];
        }
    }

    reference
    front()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->front();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[0];
        }
    }

    const_reference
    front() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->front();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[0];
        }
    }

    reference
    back()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->back();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[size_ - 1];
        }
    }

    const_reference
    back() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->back();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[size_ - 1];
        }
    }

    iterator
    erase(iterator pos)
    {
        if(size_ > max_stack_size_ + 1)
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            typename std::vector<T>::iterator iter_pos =
                ref->begin() + (pos - ref->data());

            auto iter_out = ref->erase(iter_pos);
            --size_;

            // convert std::vector<T>::iterator to pointer
            difference_type diff = iter_out - ref->begin();

            return ref->data() + diff;
        }
        else if(size_ == max_stack_size_ + 1)
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            const auto diff = pos - ref->data();
            typename std::vector<T>::iterator iter_pos =
                ref->begin() + (pos - ref->data());

            auto iter_out = ref->erase(iter_pos);

            std::vector<T> temp(std::move(*ref));
            ref->~vector();

            new(&storage_) T[max_stack_size_];
            std::copy(
                temp.begin(), temp.end(), reinterpret_cast<T*>(&storage_));
            --size_;

            return reinterpret_cast<T*>(&storage_) + diff;
        }
        else
        {
            T* beg = reinterpret_cast<T*>(&storage_);
            std::copy(pos + 1, end(), pos);

            --size_;
            return pos;
        }
    }

    iterator
    erase(iterator first, iterator last)
    {
        const auto erase_size = std::distance(first, last);

        if(size_ > max_stack_size_ + erase_size)
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            auto first_iter = ref->begin() + (first - begin());
            auto last_iter = ref->begin() + (last - begin());

            auto iter_out = ref->erase(first_iter, last_iter);
            size_ -= erase_size;

            // convert back to simple pointer
            return begin() + (iter_out - ref->begin());
        }
        else if(size_ <= max_stack_size_)
        {
            T* buff_beg = reinterpret_cast<T*>(&storage_);
            if(last < end()) // if anything left past end of range to erase
            {
                std::copy(last, end(), first);
            }
            size_ -= erase_size;
            return first;
        }
        else
        {
            std::vector<T>* ref = reinterpret_cast<std::vector<T>*>(&storage_);

            // convert to std::vector<T>::iterator
            auto first_iter = ref->begin() + (first - begin());
            auto last_iter = ref->begin() + (last - begin());

            // carry out erase on vector
            auto iter_out = ref->erase(first_iter, last_iter);
            const auto out_diff = iter_out - ref->begin();

            std::vector<T> temp(std::move(*ref));
            ref->~vector();

            new(&storage_) T[max_stack_size_];

            std::copy(
                temp.begin(), temp.end(), reinterpret_cast<T*>(&storage_));

            size_ -= erase_size;

            return begin() + out_diff;
        }
    }

    pointer
    data()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_);
        }
    }

    const_pointer
    data() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_);
        }
    }

    iterator
    begin()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_);
        }
    }

    iterator
    end()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data() + size_;
        }
        else
        {
            return reinterpret_cast<T*>(&storage_) + size_;
        }
    }

    const_iterator
    cbegin() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_);
        }
    }

    const_iterator
    cend() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data() +
                   size_;
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_) + size_;
        }
    }

    size_type
    size() const
    {
        return size_;
    }

    bool
    empty() const
    {
        return size_ == 0;
    }

    static constexpr size_type
    max_stack_size()
    {
        return max_stack_size_;
    }

    bool
    on_stack() const
    {
        return size_ <= max_stack_size_;
    }

private:
    std::aligned_storage_t<at_least_size> storage_;
    size_type size_;
};

} // namespace helene
<|endoftext|>


<commit_before>#pragma once

#include <type_traits>
#include <vector>
#include <initializer_list>
#include <cstddef>
#include <algorithm>
#include <iterator>

namespace helene
{

template <class T, std::size_t StackBufferSize = sizeof(std::vector<T>)>
class small_vector
{
    static_assert(std::is_trivial<T>::value,
                  "small_vector is only compatible with trivial types");

public:
    typedef typename std::vector<T>::size_type size_type;
    typedef typename std::vector<T>::difference_type difference_type;
    typedef T* iterator;
    typedef const T* const_iterator;

private:
    static constexpr size_type at_least_size =
        std::max(StackBufferSize, sizeof(std::vector<T>*));
    static constexpr size_type max_stack_size_ = at_least_size / sizeof(T);

public:
    small_vector() : size_()
    {
        new(&storage_) T[max_stack_size_];
    }

    small_vector(std::initializer_list<T> init) : size_(init.size())
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(init);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::copy(
                init.begin(), init.end(), reinterpret_cast<T*>(&storage_));
        }
    }

    small_vector(size_type count) : size_(count)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, T{});
        }
    }

    small_vector(size_type count, const T& value)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count, value);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, value);
        }
    }

    ~small_vector()
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();
        }
    }

public:
    void
    push_back(const T& value)
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else if(size_ == max_stack_size_)
        {
            T* buff = reinterpret_cast<T*>(&storage_);
            T temp[max_stack_size_];
            std::copy(buff, buff + size_, std::begin(temp));

            new(&storage_) std::vector<T>(std::begin(temp), std::end(temp));
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else
        {
            reinterpret_cast<T*>(&storage_)[size_] = value;
        }

        ++size_;
    }

    void
    pop_back()
    {
        if(size_ > max_stack_size_ + 1) // remain on heap
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            --size_;
        }
        else if(size_ == max_stack_size_ + 1) // transition to stack
        {
            // reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            // not necessary, trivially destructible

            std::vector<T> temp(
                std::move(*(reinterpret_cast<std::vector<T>*>(&storage_))));
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();

            new(&storage_) T[max_stack_size_];
            std::copy(
                temp.begin(), temp.end() - 1, reinterpret_cast<T*>(&storage_));
            --size_;
        }
        else // remain on stack
        {
            --size_;
        }
    }

    T& operator[](size_type n)
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->operator[](n);
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[n];
        }
    }

    const T& operator[](size_type n) const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->
            operator[](n);
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[n];
        }
    }

    iterator
    begin()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_);
        }
    }

    iterator
    end()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data() + size_;
        }
        else
        {
            return reinterpret_cast<T*>(&storage_) + size_;
        }
    }

    const_iterator
    cbegin() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_);
        }
    }

    const_iterator
    cend() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data() +
                   size_;
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_) + size_;
        }
    }

    size_type
    size() const
    {
        return size_;
    }

    bool
    empty() const
    {
        return size_ == 0;
    }

    static constexpr size_type
    max_stack_size()
    {
        return max_stack_size_;
    }

    bool
    on_stack() const
    {
        return size_ <= max_stack_size_;
    }

private:
    std::aligned_storage_t<at_least_size> storage_;
    size_type size_;
};

} // namespace helene
<commit_msg>Add member function 'data'. 	modified:   include/small_vector.hpp<commit_after>#pragma once

#include <type_traits>
#include <vector>
#include <initializer_list>
#include <cstddef>
#include <algorithm>
#include <iterator>

namespace helene
{

template <class T, std::size_t StackBufferSize = sizeof(std::vector<T>)>
class small_vector
{
    static_assert(std::is_trivial<T>::value,
                  "small_vector is only compatible with trivial types");

public:
    typedef typename std::vector<T>::size_type size_type;
    typedef typename std::vector<T>::difference_type difference_type;
    typedef T* iterator;
    typedef const T* const_iterator;

private:
    static constexpr size_type at_least_size =
        std::max(StackBufferSize, sizeof(std::vector<T>*));
    static constexpr size_type max_stack_size_ = at_least_size / sizeof(T);

public:
    small_vector() : size_()
    {
        new(&storage_) T[max_stack_size_];
    }

    small_vector(std::initializer_list<T> init) : size_(init.size())
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(init);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::copy(
                init.begin(), init.end(), reinterpret_cast<T*>(&storage_));
        }
    }

    small_vector(size_type count) : size_(count)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, T{});
        }
    }

    small_vector(size_type count, const T& value)
    {
        if(size_ > max_stack_size_)
        {
            new(&storage_) std::vector<T>(count, value);
        }
        else
        {
            new(&storage_) T[max_stack_size_];
            std::fill_n(reinterpret_cast<T*>(&storage_), size_, value);
        }
    }

    ~small_vector()
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();
        }
    }

public:
    void
    push_back(const T& value)
    {
        if(size_ > max_stack_size_)
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else if(size_ == max_stack_size_)
        {
            T* buff = reinterpret_cast<T*>(&storage_);
            T temp[max_stack_size_];
            std::copy(buff, buff + size_, std::begin(temp));

            new(&storage_) std::vector<T>(std::begin(temp), std::end(temp));
            reinterpret_cast<std::vector<T>*>(&storage_)->push_back(value);
        }
        else
        {
            reinterpret_cast<T*>(&storage_)[size_] = value;
        }

        ++size_;
    }

    void
    pop_back()
    {
        if(size_ > max_stack_size_ + 1) // remain on heap
        {
            reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            --size_;
        }
        else if(size_ == max_stack_size_ + 1) // transition to stack
        {
            // reinterpret_cast<std::vector<T>*>(&storage_)->pop_back();
            // not necessary, trivially destructible

            std::vector<T> temp(
                std::move(*(reinterpret_cast<std::vector<T>*>(&storage_))));
            reinterpret_cast<std::vector<T>*>(&storage_)->~vector();

            new(&storage_) T[max_stack_size_];
            std::copy(
                temp.begin(), temp.end() - 1, reinterpret_cast<T*>(&storage_));
            --size_;
        }
        else // remain on stack
        {
            --size_;
        }
    }

    T& operator[](size_type n)
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->operator[](n);
        }
        else
        {
            return reinterpret_cast<T*>(&storage_)[n];
        }
    }

    const T& operator[](size_type n) const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->
            operator[](n);
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_)[n];
        }
    }

    T*
    data()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_);
        }
    }

    const T*
    data() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_);
        }
    }

    iterator
    begin()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<T*>(&storage_);
        }
    }

    iterator
    end()
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<std::vector<T>*>(&storage_)->data() + size_;
        }
        else
        {
            return reinterpret_cast<T*>(&storage_) + size_;
        }
    }

    const_iterator
    cbegin() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data();
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_);
        }
    }

    const_iterator
    cend() const
    {
        if(size_ > max_stack_size_)
        {
            return reinterpret_cast<const std::vector<T>*>(&storage_)->data() +
                   size_;
        }
        else
        {
            return reinterpret_cast<const T*>(&storage_) + size_;
        }
    }

    size_type
    size() const
    {
        return size_;
    }

    bool
    empty() const
    {
        return size_ == 0;
    }

    static constexpr size_type
    max_stack_size()
    {
        return max_stack_size_;
    }

    bool
    on_stack() const
    {
        return size_ <= max_stack_size_;
    }

private:
    std::aligned_storage_t<at_least_size> storage_;
    size_type size_;
};

} // namespace helene
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2017 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/base/memory.h"

#include <sys/mman.h>
#include <unistd.h>

namespace xe {
namespace memory {

size_t page_size() { return getpagesize(); }
size_t allocation_granularity() { return page_size(); }

uint32_t ToPosixProtectFlags(PageAccess access) {
  switch (access) {
    case PageAccess::kNoAccess:
      return PROT_NONE;
    case PageAccess::kReadOnly:
      return PROT_READ;
    case PageAccess::kReadWrite:
      return PROT_READ | PROT_WRITE;
    case PageAccess::kExecuteReadWrite:
      return PROT_READ | PROT_WRITE | PROT_EXEC;
    default:
      assert_unhandled_case(access);
      return PROT_NONE;
  }
}

void* AllocFixed(void* base_address, size_t length,
                 AllocationType allocation_type, PageAccess access) {
  // mmap does not support reserve / commit, so ignore allocation_type.
  uint32_t prot = ToPosixProtectFlags(access);
  return mmap(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

bool DeallocFixed(void* base_address, size_t length,
                  DeallocationType deallocation_type) {
  return munmap(base_address, length) == 0;
}

bool Protect(void* base_address, size_t length, PageAccess access,
             PageAccess* out_old_access) {
  // Linux does not have a syscall to query memory permissions.
  assert_null(out_old_access);

  uint32_t prot = ToPosixProtectFlags(access);
  return mprotect(base_address, length, prot) == 0;
}

bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
  return false;
}

FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length,
                                          PageAccess access, bool commit) {
  return nullptr;
}

void CloseFileMappingHandle(FileMappingHandle handle) {}

void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
                  PageAccess access, size_t file_offset) {
  return nullptr;
}

bool UnmapFileView(FileMappingHandle handle, void* base_address,
                   size_t length) {
  return false;
}

}  // namespace memory
}  // namespace xe
<commit_msg>[Base] First-pass memory file mapping support<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2017 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/base/memory.h"
#include "xenia/base/string.h"

#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

namespace xe {
namespace memory {

size_t page_size() { return getpagesize(); }
size_t allocation_granularity() { return page_size(); }

uint32_t ToPosixProtectFlags(PageAccess access) {
  switch (access) {
    case PageAccess::kNoAccess:
      return PROT_NONE;
    case PageAccess::kReadOnly:
      return PROT_READ;
    case PageAccess::kReadWrite:
      return PROT_READ | PROT_WRITE;
    case PageAccess::kExecuteReadWrite:
      return PROT_READ | PROT_WRITE | PROT_EXEC;
    default:
      assert_unhandled_case(access);
      return PROT_NONE;
  }
}

void* AllocFixed(void* base_address, size_t length,
                 AllocationType allocation_type, PageAccess access) {
  // mmap does not support reserve / commit, so ignore allocation_type.
  uint32_t prot = ToPosixProtectFlags(access);
  return mmap(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

bool DeallocFixed(void* base_address, size_t length,
                  DeallocationType deallocation_type) {
  return munmap(base_address, length) == 0;
}

bool Protect(void* base_address, size_t length, PageAccess access,
             PageAccess* out_old_access) {
  // Linux does not have a syscall to query memory permissions.
  assert_null(out_old_access);

  uint32_t prot = ToPosixProtectFlags(access);
  return mprotect(base_address, length, prot) == 0;
}

bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
  return false;
}

FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length,
                                          PageAccess access, bool commit) {
  int oflag;
  switch (access) {
    case PageAccess::kNoAccess:
      oflag = 0;
      break;
    case PageAccess::kReadOnly:
      oflag = O_RDONLY;
      break;
    case PageAccess::kReadWrite:
      oflag = O_RDWR;
      break;
    default:
      assert_always();
      return nullptr;
  }

  oflag |= O_CREAT;
  int ret = shm_open(xe::to_string(path).c_str(), oflag, 0777);
  if (ret > 0) {
    ftruncate64(ret, length);
  }

  return ret <= 0 ? nullptr : reinterpret_cast<FileMappingHandle>(ret);
}

void CloseFileMappingHandle(FileMappingHandle handle) {
  close((intptr_t)handle);
}

void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
                  PageAccess access, size_t file_offset) {
  uint32_t prot = ToPosixProtectFlags(access);
  return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS,
                reinterpret_cast<intptr_t>(handle), file_offset);
}

bool UnmapFileView(FileMappingHandle handle, void* base_address,
                   size_t length) {
  return munmap(base_address, length) == 0;
}

}  // namespace memory
}  // namespace xe
<|endoftext|>